@luckystack/sync 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/CLAUDE.md +4 -3
- package/README.md +5 -7
- package/docs/callback-registration.md +4 -4
- package/docs/error-states.md +6 -6
- package/docs/server-vs-client-handlers.md +2 -2
- package/docs/streaming.md +1 -1
- package/docs/sync-request.md +2 -2
- package/docs/version-policy.md +2 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.4] - 2026-08-17
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Corrected dependency, generated-typing, error-tracker, and transport-span documentation, including the current root no-cast rule and HTTP-only automatic sync request span.
|
|
15
|
+
|
|
10
16
|
## [0.8.1] - 2026-07-27
|
|
11
17
|
|
|
12
18
|
### Fixed
|
package/CLAUDE.md
CHANGED
|
@@ -106,11 +106,12 @@ None directly. Inherits socket transport config from `@luckystack/server` and Re
|
|
|
106
106
|
## Peer dependencies
|
|
107
107
|
|
|
108
108
|
- **Required**: `@luckystack/core`, `@luckystack/error-tracking`. **`@luckystack/login` is NOT a dependency** (0.2.0 decoupling) — sessions resolve through core's `readSession` / session-provider registry (`handleSyncRequest` imports `readSession` from `@luckystack/core`), with login as the default *provider* (optional package), not a hard runtime dep.
|
|
109
|
-
- **
|
|
110
|
-
- `@prisma/client@^6.19.0` (transitively required via `@luckystack/core`)
|
|
111
|
-
- `react@^19.2.0` (only the `/client` subpath)
|
|
109
|
+
- **Required peers**:
|
|
112
110
|
- `socket.io@^4.8.0` (server entry)
|
|
113
111
|
- `socket.io-client@^4.8.0` (client entry)
|
|
112
|
+
- **Optional peers**:
|
|
113
|
+
- `@prisma/client@^6.19.0` (only for the default Prisma-backed session/user provider)
|
|
114
|
+
- `react@^19.2.0` (only the `/client` subpath)
|
|
114
115
|
- Redis adapter (required for cross-instance fanout) wired by `@luckystack/server`; see `/docs/ARCHITECTURE_SOCKET.md`.
|
|
115
116
|
|
|
116
117
|
## Related
|
package/README.md
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @luckystack/sync
|
|
8
|
+
npm install @luckystack/sync socket.io socket.io-client
|
|
9
|
+
# Optional React client helpers: npm install react
|
|
9
10
|
```
|
|
10
11
|
|
|
11
12
|
## Quickstart
|
|
@@ -143,12 +144,9 @@ Client entry (`@luckystack/sync/client`):
|
|
|
143
144
|
|
|
144
145
|
## Dependencies
|
|
145
146
|
|
|
146
|
-
- Runtime: `@luckystack/core`, `@luckystack/
|
|
147
|
-
-
|
|
148
|
-
|
|
149
|
-
- `react@^19.2.0` (`/client` entry only)
|
|
150
|
-
- `socket.io@^4.8.0`
|
|
151
|
-
- `socket.io-client@^4.8.0`
|
|
147
|
+
- Runtime: `@luckystack/core`, `@luckystack/error-tracking` (`@luckystack/login` is an optional session provider, not a runtime dependency)
|
|
148
|
+
- Required peers: `socket.io@^4.8.0`, `socket.io-client@^4.8.0`
|
|
149
|
+
- Optional peers: `@prisma/client@^6.19.0` (only for the default Prisma-backed session/user provider), `react@^19.2.0` (`/client` entry only).
|
|
152
150
|
|
|
153
151
|
## License
|
|
154
152
|
|
|
@@ -66,7 +66,7 @@ upsertSyncEventCallback<F, V>(params: {
|
|
|
66
66
|
}): () => void // returns unsubscribe function
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
### Typed payloads (rule
|
|
69
|
+
### Typed payloads (root contract rule 21, no casts)
|
|
70
70
|
|
|
71
71
|
Both `clientOutput` and `serverOutput` are typed via the generated `SyncTypeMap`. The framework's discriminated unions surface every possible status:
|
|
72
72
|
|
|
@@ -83,7 +83,7 @@ upsertSyncEventCallback({
|
|
|
83
83
|
});
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
Never cast these to `unknown` or `any` (root `CLAUDE.md` rule
|
|
86
|
+
Never cast these to `unknown` or `any` (root `CLAUDE.md` rule 21). If inference fails, run `npm run generateArtifacts`; if generated output is wrong, fix the generator rather than casting around it.
|
|
87
87
|
|
|
88
88
|
### Validation rules
|
|
89
89
|
|
|
@@ -199,7 +199,7 @@ Idempotent: if `initSyncRequest` is called again later, the previous handlers ar
|
|
|
199
199
|
|
|
200
200
|
## 6. Why callbacks must never cast `serverOutput` / `clientOutput`
|
|
201
201
|
|
|
202
|
-
Rule
|
|
202
|
+
Rule 21 in the repository-root `CLAUDE.md`: generated types are mandatory; no `unknown` / `any` casts on typed transports.
|
|
203
203
|
|
|
204
204
|
Both `upsertSyncEventCallback` and `upsertSyncEventStreamCallback` give you fully typed payloads via the generated `SyncTypeMap`. The framework's discriminated unions handle the status branching cleanly:
|
|
205
205
|
|
|
@@ -253,5 +253,5 @@ When inference fails:
|
|
|
253
253
|
- Originator API: [`./sync-request.md`](./sync-request.md)
|
|
254
254
|
- Stream payload shapes: [`./streaming.md`](./streaming.md)
|
|
255
255
|
- Server / client handler contracts (what produces the payloads): [`./server-vs-client-handlers.md`](./server-vs-client-handlers.md)
|
|
256
|
-
- Type-generation contract (rule
|
|
256
|
+
- Type-generation contract (rule 21): repository-root [`CLAUDE.md`](../../../CLAUDE.md)
|
|
257
257
|
- `SocketStatusProvider` wiring: `src/_providers/SocketStatusProvider.tsx` (installer side)
|
package/docs/error-states.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# error-states
|
|
2
2
|
|
|
3
|
-
> Authoritative catalog of error codes emitted by `syncRequest` (client) and `handleSyncRequest` / `handleHttpSyncRequest` (server). Documents HTTP-status mapping, locale-aware message normalization, `errorParams` interpolation slots, the `rateLimitExceeded` hook payload, and
|
|
3
|
+
> Authoritative catalog of error codes emitted by `syncRequest` (client) and `handleSyncRequest` / `handleHttpSyncRequest` (server). Documents HTTP-status mapping, locale-aware message normalization, `errorParams` interpolation slots, the `rateLimitExceeded` hook payload, and registered error-tracker capture behavior.
|
|
4
4
|
|
|
5
5
|
For the originator response envelope shape see [`./sync-request.md`](./sync-request.md). For the full lifecycle these errors slot into see [`./server-vs-client-handlers.md`](./server-vs-client-handlers.md).
|
|
6
6
|
|
|
@@ -167,14 +167,14 @@ Recipients (per-recipient errors during fanout) use **their own** headers, not t
|
|
|
167
167
|
|
|
168
168
|
---
|
|
169
169
|
|
|
170
|
-
## 9.
|
|
170
|
+
## 9. Error-tracker capture via `tryCatch`
|
|
171
171
|
|
|
172
172
|
Every `_server` and `_client` execution is wrapped in `tryCatch(..., undefined, { handler, sync, stage, userId, ... })`. The fourth argument is the breadcrumb context that gets attached when `tryCatch` captures an exception.
|
|
173
173
|
|
|
174
174
|
Captured automatically:
|
|
175
175
|
|
|
176
|
-
- `_server` thrown errors ->
|
|
177
|
-
- `_client` thrown errors ->
|
|
176
|
+
- `_server` thrown errors -> a breadcrumb/event for the registered tracker(s): `{ handler: 'handleSyncRequest', sync: <route>, stage: 'server', userId, receiver, transport: 'socket' | 'http' }`.
|
|
177
|
+
- `_client` thrown errors -> a breadcrumb/event for the registered tracker(s): `{ handler: 'handleSyncRequest', sync: <route>, stage: 'client', sourceUserId, targetToken, receiver, transport }`.
|
|
178
178
|
|
|
179
179
|
NOT captured (returned as `status: 'error'` envelopes only):
|
|
180
180
|
|
|
@@ -183,7 +183,7 @@ NOT captured (returned as `status: 'error'` envelopes only):
|
|
|
183
183
|
- Rate-limit rejects — these are noise.
|
|
184
184
|
- `_server` / `_client` returning `{ status: 'error' }` voluntarily — the handler made a domain decision; nothing threw.
|
|
185
185
|
|
|
186
|
-
If you need to
|
|
186
|
+
If you need to capture a voluntary error (e.g. "this code path should never trigger in production"), do it explicitly inside the handler via `@luckystack/error-tracking`'s `captureException`; it fans out to the registered tracker(s).
|
|
187
187
|
|
|
188
188
|
---
|
|
189
189
|
|
|
@@ -235,7 +235,7 @@ These are dev hints, not production behavior. Toggle both to `false` for product
|
|
|
235
235
|
| Request reached server but auth rejected | `auth.required`, `auth.forbidden` | `_server`'s `auth` export, [`./server-vs-client-handlers.md`](./server-vs-client-handlers.md) |
|
|
236
236
|
| 429 rate-limit responses | `sync.rateLimitExceeded` | `projectConfig.rateLimiting` |
|
|
237
237
|
| Server-side schema fail | `sync.invalidInputType` | `_server`'s `SyncParams.clientInput` interface + generated Zod schema |
|
|
238
|
-
| Handler threw | `sync.serverExecutionFailed` / `sync.clientExecutionFailed` |
|
|
238
|
+
| Handler threw | `sync.serverExecutionFailed` / `sync.clientExecutionFailed` | Registered error-tracker capture |
|
|
239
239
|
| Domain logic rejected | `<route-supplied errorCode>` | The `_server` or `_client` file's return path |
|
|
240
240
|
| Empty room | `sync.noReceiversFound` | `receiver` argument; check room membership |
|
|
241
241
|
| Offline + caller didn't await | `offline.queueFull` | Bump `offlineQueue.maxSize` or switch `dropPolicy` |
|
|
@@ -56,7 +56,7 @@ Key rules:
|
|
|
56
56
|
- **Streaming primitives are received as params.** See [`./streaming.md`](./streaming.md) for the audience matrix.
|
|
57
57
|
- **Optional `export const validation`** toggles runtime input validation, exactly like the API handler. `'strict'` (default, or omit the export) validates `clientInput` against the generated Zod schema; `'relaxed'` or `{ input: 'skip' }` skips validation entirely — use it for routes whose payload shape can't be modelled in TypeScript (third-party webhook fan-in, dynamic blobs). Honored identically by both the Socket.io and HTTP/SSE transports.
|
|
58
58
|
- **Optional `export const errorFormatter`** overrides the error envelope for this route only (falls back to the global `registerErrorFormatter`, then the framework default). Both transports honor it.
|
|
59
|
-
- **Never `try/catch` manually.** Wrap async ops in `functions.tryCatch` (or the destructured `tryCatch` from `functions`) — the framework already wraps the whole `main()` in its own `tryCatch` for
|
|
59
|
+
- **Never `try/catch` manually.** Wrap async ops in `functions.tryCatch` (or the destructured `tryCatch` from `functions`) — the framework already wraps the whole `main()` in its own `tryCatch` for registered error-tracker capture, but per-operation `tryCatch` lets you surface targeted error codes.
|
|
60
60
|
|
|
61
61
|
---
|
|
62
62
|
|
|
@@ -146,7 +146,7 @@ incoming msg (Socket.io 'sync' event)
|
|
|
146
146
|
2. parseTransportRouteName(name) -> routing.invalidServiceRouteName
|
|
147
147
|
3. validate cb -> sync.invalidCallback
|
|
148
148
|
4. validate receiver -> sync.missingReceiver
|
|
149
|
-
5. getSession(token) ->
|
|
149
|
+
5. getSession(token) -> propagate identity to registered error trackers
|
|
150
150
|
6. getRuntimeSyncMaps()
|
|
151
151
|
no _server AND no _client -> sync.notFound
|
|
152
152
|
|
|
package/docs/streaming.md
CHANGED
|
@@ -238,7 +238,7 @@ io.to(receiver).emit(socketEventNames.sync, frame);
|
|
|
238
238
|
|
|
239
239
|
### Stream-hook fire-and-forget
|
|
240
240
|
|
|
241
|
-
`preSyncStream` and `postSyncStream` hooks dispatch via `void dispatchHook(...)` — chunks never `await` the hook. Hook errors are swallowed by the hook dispatcher's own `tryCatch`. This matters because a slow
|
|
241
|
+
`preSyncStream` and `postSyncStream` hooks dispatch via `void dispatchHook(...)` — chunks never `await` the hook. Hook errors are swallowed by the hook dispatcher's own `tryCatch`. This matters because a slow error-tracker breadcrumb (for example) must not delay chunk delivery to the user.
|
|
242
242
|
|
|
243
243
|
### Chunk-index counter
|
|
244
244
|
|
package/docs/sync-request.md
CHANGED
|
@@ -40,7 +40,7 @@ This is enforced through generated `SyncTypeMap` entries — no runtime check fi
|
|
|
40
40
|
- `src/{page}/_sync/{name}_server_v{N}.ts` -> `'{page}/{name}'`
|
|
41
41
|
- `src/_sync/{name}_server_v{N}.ts` (root-level) -> `'system/{name}'`
|
|
42
42
|
|
|
43
|
-
Always pass the literal string. Any narrowing layer (`as any`, `as SyncFullName`, manual cast helpers) breaks inference and **violates rule
|
|
43
|
+
Always pass the literal string. Any narrowing layer (`as any`, `as SyncFullName`, manual cast helpers) breaks inference and **violates rule 21** in the repository-root `CLAUDE.md` (no `unknown`/`any` casts on typed transports).
|
|
44
44
|
|
|
45
45
|
### `version: V extends VersionsForFullName<F>`
|
|
46
46
|
|
|
@@ -222,7 +222,7 @@ When the Socket.io transport is unavailable (corporate firewalls, mobile carrier
|
|
|
222
222
|
|---|---|---|
|
|
223
223
|
| Originator chunks (`stream(payload)`) | `socket.emit(progressEventName, payload)` | `stream?.(payload)` injected callback → SSE writer |
|
|
224
224
|
| Recipients (`broadcastStream`, `streamTo`) | Socket.io fanout | Socket.io fanout (recipients still on sockets) |
|
|
225
|
-
|
|
|
225
|
+
| Auto-instrumented error-tracker span | none | `'sync.request.http'` |
|
|
226
226
|
| Final ack | `socket.emit(buildSyncResponseEventName(responseIndex), envelope)` | HTTP response body (`HttpSyncResponse`) |
|
|
227
227
|
|
|
228
228
|
Signature:
|
package/docs/version-policy.md
CHANGED
|
@@ -331,7 +331,7 @@ Allowed by the framework (it just maps `v3` to its own entry), but confusing for
|
|
|
331
331
|
await syncRequest({ name: 'board/moveCard' as any, version: someString as any, ... });
|
|
332
332
|
```
|
|
333
333
|
|
|
334
|
-
This violates rule
|
|
334
|
+
This violates repository contract rule 21 (no `as any` / `as unknown` on typed transports). If you don't know the version statically, the design needs to change — versions are an authoring-time decision, not a runtime one.
|
|
335
335
|
|
|
336
336
|
---
|
|
337
337
|
|
|
@@ -359,4 +359,4 @@ This violates rule 16 (no `as any` / `as unknown` on typed transports). If you d
|
|
|
359
359
|
- Recipient subscription: [`./callback-registration.md`](./callback-registration.md)
|
|
360
360
|
- Error contract (including `sync.invalidInputType` from regenerated Zod schemas): [`./error-states.md`](./error-states.md)
|
|
361
361
|
- Type-map regeneration: `npm run ai:index` (`@luckystack/devkit`)
|
|
362
|
-
- Sync type contract: rule
|
|
362
|
+
- Sync type contract: rule 21 in repository-root [`CLAUDE.md`](../../../CLAUDE.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luckystack/sync",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"test": "vitest run"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@luckystack/core": "^0.8.
|
|
64
|
-
"@luckystack/error-tracking": "^0.8.
|
|
63
|
+
"@luckystack/core": "^0.8.4",
|
|
64
|
+
"@luckystack/error-tracking": "^0.8.4"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"@prisma/client": "^6.19.0",
|