feat: add @karnak19/pbkit-realtime plugin for typed realtime subscriptions - #27
Conversation
|
/oc review |
PR Review:
|
|
/oc review |
PR Review:
|
|
/oc review |
PR Review:
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ebbeec7 to
10f7cc4
Compare
There was a problem hiding this comment.
Review of @karnak19/pbkit-realtime plugin. The implementation is clean, tests are thorough, and the pascalCase extraction into core is a good improvement. Two issues to address:
- Return type mismatch (
generate.ts:16): Generated unsubscribe is typed() => voidbut PocketBase SDK'sUnsubscribeFuncis() => Promise<void>. Consumers won't know toawaitthe unsubscribe, risking unhandled rejections. - Missing
peerDependencies(package.json): Sibling plugins (pbkit-tanstack,pbkit-zod) declare runtime peer deps; this package omitspocketbase. - Missing changeset: This PR adds a new published package and modifies existing packages (
@karnak19/pbkitexportspascalCase, tanstack/zod import changes). A.changeset/*.mdentry is needed for proper versioning.
Publish @karnak19/pbkit-realtime at 0.1.0 and bump @karnak19/pbkit (minor) so the pascalCase export the plugin imports is available in a published version. Set realtime version to 0.0.0 so the minor changeset lands 0.1.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Two issues carried over from the previous review round — both still present in the current diff:
- Return type mismatch (
generate.ts:16): Generated unsubscribe function is typed() => voidbut PocketBase SDK returns() => Promise<void>, risking unhandled rejections. (Correctness) - Missing
pocketbasepeerDependency (package.json:39): Sibling plugins declare runtime deps of generated code as peerDependencies; this package does not. (Package hygiene)
| lines.push(`export async function ${fnName}(`); | ||
| lines.push(` callback: (event: RealtimeEvent<${recordType}>) => void,`); | ||
| lines.push(` options?: { filter?: string; id?: string },`); | ||
| lines.push(`): Promise<() => void> {`); |
There was a problem hiding this comment.
Problem: The generated return type Promise<() => void> doesn't match the PocketBase SDK's actual return type. RecordService.subscribe() returns Promise<UnsubscribeFunc> where UnsubscribeFunc = () => Promise<void> (per the SDK source). The unsubscribe function is async — it calls unsubscribeByTopicAndListener which awaits submitSubscriptions().
Impact: TypeScript allows () => Promise<void> to be assigned to () => void (void return means "value ignored"), so the code compiles. But consumers see unsub() as synchronous and won't await it. If the unsubscribe network request fails, the resulting promise rejection is unhandled. The docs also demonstrate unsub() without await, reinforcing the wrong pattern.
Fix: Change the generated return type and update the docs/README accordingly:
lines.push(`): Promise<() => Promise<void>> {`);Then in the docs and README, show await unsub() instead of unsub(). The React example's cleanup type should also change to (() => Promise<void>) | undefined.
There was a problem hiding this comment.
Fixed in 19adf11. Generated helpers now return Promise<() => Promise<void>> (PocketBase UnsubscribeFunc). Updated the snapshot, the Promise<() => void> assertion, and the docs/README/SKILL to await unsub(); the React example cleanup type is now (() => Promise<void>) | undefined.
| "access": "public" | ||
| }, | ||
| "license": "MIT", | ||
| "dependencies": { |
There was a problem hiding this comment.
Problem: This package doesn't declare pocketbase as a peerDependency, even though the generated realtime.gen.ts code depends on it at runtime (via client.gen.ts which imports from pocketbase).
Impact: Both sibling plugins follow the peerDependency pattern — pbkit-tanstack declares @tanstack/query-core and pbkit-zod declares zod. Without it, npm install @karnak19/pbkit-realtime won't warn users if pocketbase is missing from their project. While pocketbase is typically already present (required by the core pbkit client), explicit declaration is the correct package hygiene.
Fix: Add a peerDependencies block consistent with the sibling packages:
"peerDependencies": {
"pocketbase": ">=0.21.0"
}
``` <!-- ai-pr-review:inline -->There was a problem hiding this comment.
Fixed in 19adf11. Added "peerDependencies": { "pocketbase": ">=0.21.0" }, matching the pbkit-zod (zod) / pbkit-tanstack (@tanstack/query-core) pattern.
- Generated subscribe helpers now return Promise<() => Promise<void>>, matching PocketBase's UnsubscribeFunc (unsubscribe is async). Update snapshot, tests, docs, README, and SKILL to await unsub(). - Declare pocketbase as a peerDependency, consistent with pbkit-zod and pbkit-tanstack. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Both previously raised issues (return type mismatch → |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>



Summary
Closes #23
Adds
@karnak19/pbkit-realtime— a new plugin that generates typed realtime subscription helpers from the PocketBase schema, using the built-in SSE system.What it generates
realtime.gen.tswith:RealtimeActiontype ("create" | "update" | "delete")RealtimeEvent<T>interfacesubscribeTo{Collection}(callback, options?)per non-excluded collectionRealtimeEvent<{Collection}Record>filter(PocketBase filter string) andid(specific record)() => Promise<void>collectionsconfig (isCollectionExcluded)Files changed
packages/pbkit-realtime/— plugin implementation + 17 testsapps/docs/src/content/docs/plugins/realtime.md— full plugin pageskills/pbkit/SKILL.md— updated with realtime section, tags, agent workflowapps/playground/pbkit.config.ts— addedrealtimePluginbun.lock— workspace registrationRelated
@karnak19/pbkit-tanstack-realtime(follow-up, not included here)