@luckystack/sync 0.7.6 → 0.8.0

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 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.0] - 2026-07-27
11
+
12
+ ### Added
13
+
14
+ - `syncRequest` can invoke owning services through HTTP/SSE in routed transport mode while callbacks, rooms and fanout continue over the single Socket.io connection.
15
+
10
16
  ## [0.1.0]
11
17
 
12
18
  ### Added
package/CLAUDE.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## What this package does
6
6
 
7
- Real-time sync transport for LuckyStack. Provides type-safe, room-based fanout over Socket.io with an HTTP/SSE fallback. Each sync event is a file-based route with a mandatory `_server_v{N}.ts` (runs once, validates, produces `serverOutput`) and an optional `_client_v{N}.ts` (runs once per recipient socket for per-target filtering or auth). Streaming primitives (`stream`, `broadcastStream`, `streamTo`) support live LLM tokens, collab-editor diffs, and per-recipient progress. An offline queue with configurable drop policy keeps optimistic sends from being lost when the socket reconnects.
7
+ Real-time sync transport for LuckyStack. Provides type-safe, room-based fanout over Socket.io with configurable socket or routed HTTP/SSE invocation. Each sync event is a file-based route with a mandatory `_server_v{N}.ts` (runs once, validates, produces `serverOutput`) and an optional `_client_v{N}.ts` (runs once per recipient socket for per-target filtering or auth). Streaming primitives (`stream`, `broadcastStream`, `streamTo`) support live LLM tokens, collab-editor diffs, and per-recipient progress. An offline queue with configurable drop policy keeps optimistic sends from being lost when the socket reconnects.
8
8
 
9
9
  ## When to USE this package
10
10
 
@@ -38,7 +38,7 @@ Real-time sync transport for LuckyStack. Provides type-safe, room-based fanout o
38
38
 
39
39
  | Export | One-liner | Deep doc |
40
40
  |---|---|---|
41
- | `syncRequest({ name, version, data?, receiver, ignoreSelf?, onStream?, offlineDropPolicy? })` | Fire a typed sync event into a room; resolves with the server result envelope. | → `docs/sync-request.md` |
41
+ | `syncRequest({ name, version, data?, receiver, ignoreSelf?, onStream?, offlineDropPolicy? })` | Fire a typed sync event using `transport.invocation`; routed mode uses HTTP/SSE while recipient callbacks stay on the one socket. | → `docs/sync-request.md` |
42
42
  | `useSyncEvents()` | React hook returning `{ upsertSyncEventCallback, upsertSyncEventStreamCallback }` scoped to the component lifetime. | → `docs/callback-registration.md` |
43
43
  | `useSyncEventTrigger()` | React hook returning `{ triggerSyncEvent, triggerSyncStreamEvent }` for manually invoking registered callbacks (testing / local echo). | → `docs/callback-registration.md` |
44
44
  | `initSyncRequest({ setSocketStatus, sessionRef })` | One-time wiring of socket lifecycle handlers (connect/disconnect/reconnectAttempt/userAfk/userBack/connectError) into the socket-status provider. | → `docs/callback-registration.md` |
@@ -74,7 +74,9 @@ Real-time sync transport for LuckyStack. Provides type-safe, room-based fanout o
74
74
 
75
75
  ## Config keys
76
76
 
77
- ### `registerProjectConfig({ sync, offlineQueue })`
77
+ ### `registerProjectConfig({ transport, sync, offlineQueue })`
78
+
79
+ - `transport.invocation` — `'socket'` (backwards-compatible default) or `'routed-http'`. Routed mode invokes the owning service through the router; Socket.io remains connected for fanout callbacks, rooms and presence.
78
80
 
79
81
  - `sync.streamThrottle.flushAtChars` — char threshold before a buffered throttle flushes. Default `32`.
80
82
  - `sync.streamThrottle.flushEveryMs` — timer-based flush interval; `false` disables the timer. Default `50`.
package/dist/client.js CHANGED
@@ -7,18 +7,26 @@ import {
7
7
  socket,
8
8
  waitForSocket,
9
9
  enqueueSyncRequest,
10
+ removeSyncQueueItem,
10
11
  isOnline,
11
12
  normalizeErrorResponseCore,
12
13
  parseServiceRouteName,
13
14
  buildSyncProgressEventName,
14
15
  buildSyncResponseEventName,
15
- socketEventNames
16
+ socketEventNames,
17
+ invokeRoutedHttp
16
18
  } from "@luckystack/core/client";
17
19
  import { useCallback, useEffect, useRef } from "react";
18
20
  var shouldLogDev = () => getProjectConfig().logging.devLogs;
19
21
  var shouldNotifyDev = () => getProjectConfig().logging.devNotifications;
20
22
  var shouldLogSocketStatus = () => getProjectConfig().logging.socketStatus;
21
23
  var shouldLogStream = () => getProjectConfig().logging.stream;
24
+ var routedSyncErrorCode = (kind) => {
25
+ if (kind === "timeout") return "sync.requestTimeout";
26
+ if (kind === "aborted") return "request.aborted";
27
+ if (kind === "invalid-response") return "sync.invalidServerResponse";
28
+ return "sync.ioUnavailable";
29
+ };
22
30
  var syncEvents = {};
23
31
  var syncStreamEvents = {};
24
32
  var noop = () => null;
@@ -220,9 +228,73 @@ var syncRequestInternal = (params) => {
220
228
  response: { status: "error", errorCode: "offline.queueFull" },
221
229
  fallbackErrorCode: "offline.queueFull"
222
230
  }));
231
+ } else if (externalSignal && queueId) {
232
+ const capturedQueueId = queueId;
233
+ externalSignal.addEventListener("abort", () => {
234
+ removeSyncQueueItem(capturedQueueId);
235
+ resolve(normalizeSyncError({
236
+ response: { status: "error", errorCode: "request.aborted" },
237
+ fallbackErrorCode: "request.aborted"
238
+ }));
239
+ }, { once: true });
223
240
  }
224
241
  return;
225
242
  }
243
+ if (getProjectConfig().transport.invocation === "routed-http") {
244
+ void (async () => {
245
+ const routed = await invokeRoutedHttp({
246
+ path: `/sync/${sanitizedName}/${resolvedVersion}`,
247
+ method: "POST",
248
+ data: {
249
+ data,
250
+ receiver: normalizedReceiver,
251
+ ignoreSelf: ignoreSelf ?? false
252
+ },
253
+ stream: onStream,
254
+ signals: [externalSignal],
255
+ timeoutMs: getProjectConfig().sync.requestTimeoutMs
256
+ });
257
+ if (queueId) removeSyncQueueItem(queueId);
258
+ if (routed.kind !== "response") {
259
+ const errorCode = routedSyncErrorCode(routed.kind);
260
+ resolve(normalizeSyncError({
261
+ response: {
262
+ status: "error",
263
+ errorCode,
264
+ httpStatus: routed.kind === "timeout" ? 504 : void 0
265
+ },
266
+ fallbackErrorCode: errorCode
267
+ }));
268
+ return;
269
+ }
270
+ const responseData = routed.response;
271
+ if (responseData.status === "error") {
272
+ const normalizedError = normalizeSyncError({
273
+ response: responseData,
274
+ fallbackErrorCode: "sync.failedRequest"
275
+ });
276
+ if (shouldNotifyDev()) {
277
+ notify.error({ key: normalizedError.errorCode, params: normalizedError.errorParams });
278
+ }
279
+ resolve(normalizedError);
280
+ return;
281
+ }
282
+ if (responseData.status !== "success") {
283
+ resolve(normalizeSyncError({
284
+ response: responseData,
285
+ fallbackErrorCode: "sync.invalidServerResponse"
286
+ }));
287
+ return;
288
+ }
289
+ const result = responseData.result && typeof responseData.result === "object" ? responseData.result : {};
290
+ resolve({
291
+ status: "success",
292
+ message: typeof responseData.message === "string" && responseData.message.trim().length > 0 ? responseData.message : `sync ${sanitizedName} success`,
293
+ result
294
+ });
295
+ })();
296
+ return;
297
+ }
226
298
  const tempIndex = incrementResponseIndex();
227
299
  let cleanupProgressListener = null;
228
300
  let responseTimeout = null;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/syncRequest.ts"],"sourcesContent":["//? All cross-package imports go through `@luckystack/core/client` (the\n//? browser-safe subpath) — never the main `@luckystack/core` barrel which\n//? re-exports server-only modules (paths.ts, db.ts, redis.ts) that must\n//? not enter a Vite client bundle.\nimport type {\n BaseSessionLayout as SessionLayout,\n StreamPayload,\n SyncTypeMap,\n statusContent,\n} from \"@luckystack/core/client\";\nimport {\n getLogger,\n getProjectConfig,\n notify,\n incrementResponseIndex,\n socket,\n waitForSocket,\n enqueueSyncRequest,\n isOnline,\n normalizeErrorResponseCore,\n parseServiceRouteName,\n buildSyncProgressEventName,\n buildSyncResponseEventName,\n socketEventNames,\n} from \"@luckystack/core/client\";\nimport { Dispatch, RefObject, SetStateAction, useCallback, useEffect, useRef } from \"react\";\nimport { Socket } from \"socket.io-client\";\n\nexport type SyncRequestStreamEvent<T extends StreamPayload = StreamPayload> = T;\n\nexport type SyncRouteStreamEvent<T extends StreamPayload = StreamPayload> = T;\n\ntype SyncRequestStreamCallback = (event: SyncRequestStreamEvent) => void;\ntype SyncEventStreamCallback = (params: { stream: SyncRouteStreamEvent }) => void;\n\nconst shouldLogDev = () => getProjectConfig().logging.devLogs;\nconst shouldNotifyDev = () => getProjectConfig().logging.devNotifications;\nconst shouldLogSocketStatus = () => getProjectConfig().logging.socketStatus;\nconst shouldLogStream = () => getProjectConfig().logging.stream;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Type Helpers for Sync Requests\n// ═══════════════════════════════════════════════════════════════════════════════\n\n// Check if data input is required (i.e., T does NOT allow empty object)\n// Unions like {a:1} | {b:1} do NOT allow {}, so data will be required\ntype DataRequired<T> = Record<string, never> extends T ? false : true;\n\ntype UnionToIntersection<U> =\n (U extends unknown ? (arg: U) => void : never) extends ((arg: infer I) => void)\n ? I\n : never;\n\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Global Sync Params\n// ═══════════════════════════════════════════════════════════════════════════════\n\n// All possible sync names across all pages\ntype SyncRouteRecord = UnionToIntersection<{\n [P in keyof SyncTypeMap]: {\n [N in keyof SyncTypeMap[P] as P extends 'root'\n ? `system/${Extract<N, string>}`\n : `${Extract<P, string>}/${Extract<N, string>}`]: SyncTypeMap[P][N]\n }\n}[keyof SyncTypeMap]>;\n\ntype SyncFullName = Extract<keyof SyncRouteRecord, string>;\ntype VersionsForFullName<F extends SyncFullName> = Extract<keyof SyncRouteRecord[F], string>;\n\ntype ClientInputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientInput: infer I }\n ? I\n : never;\n\ntype ServerOutputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { serverOutput: infer O }\n ? O\n : never;\n\ntype ClientOutputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientOutput: infer O }\n ? O\n : never;\n\ntype ServerStreamForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { serverStream: infer O }\n ? O\n : never;\n\ntype ClientStreamForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientStream: infer O }\n ? O\n : never;\n\ntype SyncRequestStreamCallbackForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\n [ServerStreamForFullName<F, V>] extends [never]\n ? never\n : (event: SyncRequestStreamEvent<Prettify<ServerStreamForFullName<F, V> extends StreamPayload ? ServerStreamForFullName<F, V> : StreamPayload>>) => void;\n\n//? Recipients see chunks from THREE sources, all on the same wire channel:\n//? 1. `_client_v{n}.ts`'s `stream(...)` (per-recipient, runs after _server)\n//? 2. `_server_v{n}.ts`'s `broadcastStream(...)` (room-wide fan-out)\n//? 3. `_server_v{n}.ts`'s `streamTo(tokens, ...)` (selective fan-out)\n//? All three flow into `upsertSyncEventCallback`'s `stream` argument. Both\n//? serverStream and clientStream are folded into the union so the callback\n//? sees every shape the route can emit. If neither side ever streams, the\n//? callback type collapses to never (compile error to register one).\ntype CombinedRouteStream<F extends SyncFullName, V extends VersionsForFullName<F>> =\n | (ClientStreamForFullName<F, V> extends never ? never : ClientStreamForFullName<F, V>)\n | (ServerStreamForFullName<F, V> extends never ? never : ServerStreamForFullName<F, V>);\n\ntype SyncRouteStreamCallbackForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\n [CombinedRouteStream<F, V>] extends [never]\n ? never\n : (params: { stream: SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>> }) => void;\n\ntype SyncParamsForFullName<\n F extends SyncFullName,\n V extends VersionsForFullName<F>\n> = DataRequired<ClientInputForFullName<F, V>> extends true\n ? {\n name: F;\n version: V;\n data: ClientInputForFullName<F, V>;\n receiver: string;\n ignoreSelf?: boolean;\n onStream?: SyncRequestStreamCallbackForFullName<F, V>;\n /**\n * Per-request override of `projectConfig.offlineQueue.dropPolicy`. Lets a\n * specific sync (\"editor cursor move\") pick `'drop-oldest'` while the\n * app default stays `'reject'` for safer sends. When omitted, falls back\n * to the global config.\n */\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\n /**\n * Optional AbortSignal. When aborted the client emits `syncCancel { cb }`\n * to the server and resolves locally with\n * `{ status: 'error', errorCode: 'request.aborted' }`.\n */\n signal?: AbortSignal;\n }\n : {\n name: F;\n version: V;\n data?: ClientInputForFullName<F, V>;\n receiver: string;\n ignoreSelf?: boolean;\n onStream?: SyncRequestStreamCallbackForFullName<F, V>;\n /** Per-request override (see typed branch). */\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\n /** Optional AbortSignal — see typed branch. */\n signal?: AbortSignal;\n };\n\ninterface RuntimeSyncParams {\n name?: string;\n version?: string;\n data?: unknown;\n receiver?: string;\n ignoreSelf?: boolean;\n onStream?: SyncRequestStreamCallback;\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\n signal?: AbortSignal;\n}\n\ninterface SyncErrorParam { key: string; value: string | number | boolean };\n\ninterface SyncResponseError {\n status: 'error';\n message: string;\n errorCode: string;\n errorParams?: SyncErrorParam[];\n httpStatus?: number;\n}\n\ninterface SyncAckResponse {\n status?: 'success' | 'error';\n message?: string;\n result?: unknown;\n errorCode?: string;\n errorParams?: SyncErrorParam[];\n httpStatus?: number;\n}\n\ntype SyncResultForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\n [ServerOutputForFullName<F, V>] extends [never]\n ? Record<string, never>\n : ServerOutputForFullName<F, V>;\n\ntype SyncRequestResponseForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\n | SyncResponseError\n | {\n status: 'success';\n message: string;\n result: SyncResultForFullName<F, V>;\n };\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Sync Event Callbacks Registry\n// ═══════════════════════════════════════════════════════════════════════════════\n\ntype SyncEventCallback = (params: { clientOutput: unknown; serverOutput: unknown }) => void;\nconst syncEvents: Partial<Record<string, SyncEventCallback[]>> = {};\nconst syncStreamEvents: Partial<Record<string, SyncEventStreamCallback[]>> = {};\nconst noop = () => null;\n\ninterface SyncLifecycleHandlers {\n connect: () => void;\n disconnect: () => void;\n reconnectAttempt: (attempt: number) => void;\n userAfk: (payload: { userId: string; endTime?: number }) => void;\n userBack: (payload: { userId: string }) => void;\n connectError: (err: { message: string }) => void;\n}\n\nlet activeLifecycleHandlers: SyncLifecycleHandlers | null = null;\n\nconst canSendNow = (socketInstance: Socket) => {\n if (!socketInstance.connected) return false;\n return isOnline();\n};\n\nconst createQueueId = () => {\n return `${String(Date.now())}-${String(Math.random())}`;\n};\n\nconst getCallbacksForRoute = (route: string): SyncEventCallback[] => {\n syncEvents[route] ??= [];\n return syncEvents[route];\n};\n\nconst getStreamCallbacksForRoute = (route: string): SyncEventStreamCallback[] => {\n syncStreamEvents[route] ??= [];\n return syncStreamEvents[route];\n};\n\nconst triggerSyncCallbacks = (name: string, clientOutput: unknown, serverOutput: unknown) => {\n const callbacks = syncEvents[name] ?? [];\n if (callbacks.length === 0) {\n //? Streaming-only routes legitimately register ONLY a stream callback —\n //? the final success envelope is implicit in the stream completing. Only\n //? warn when NEITHER callback type is registered for the route.\n const streamCallbacks = syncStreamEvents[name] ?? [];\n if (streamCallbacks.length === 0 && shouldLogDev()) {\n getLogger().warn(`Sync event ${name} has no registered callback on this page`);\n }\n return;\n }\n\n for (const callback of callbacks) {\n callback({ clientOutput, serverOutput });\n }\n};\n\nconst triggerSyncStreamCallbacks = (name: string, stream: SyncRouteStreamEvent) => {\n const callbacks = syncStreamEvents[name] ?? [];\n if (callbacks.length === 0) {\n return;\n }\n\n for (const callback of callbacks) {\n callback({ stream });\n }\n};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// syncRequest Function Overloads\n// ═══════════════════════════════════════════════════════════════════════════════\n\nconst normalizeSyncError = ({\n response,\n fallbackErrorCode,\n}: {\n response: SyncAckResponse;\n fallbackErrorCode: string;\n}): SyncResponseError => {\n const normalized = normalizeErrorResponseCore({\n response,\n fallbackErrorCode,\n resolveMessage: ({ errorCode }) => {\n if (typeof response.message === 'string' && response.message.trim().length > 0) {\n return response.message;\n }\n\n return errorCode;\n },\n });\n\n return {\n status: 'error',\n message: normalized.message,\n errorCode: normalized.errorCode,\n errorParams: normalized.errorParams,\n httpStatus: normalized.httpStatus,\n };\n};\n\ntype SyncRequestParamsWithOptions<F extends SyncFullName, V extends VersionsForFullName<F>> =\n SyncParamsForFullName<F, V>;\n\n//? Outcome of the pre-flight validation phase of a sync send. Either a\n//? normalized error envelope to resolve the request with, or the sanitized\n//? values the send phase needs. Extracted from `syncRequestInternal` so the\n//? \"validate first, execute second\" split is explicit (the orchestrator stays\n//? a thin promise wrapper around prepare → run).\ntype PreparedSyncRequest =\n | { ok: false; error: SyncResponseError }\n | { ok: true; sanitizedName: string; version: string; data: object; normalizedReceiver: string };\n\n//? Pure-ish validation chain (logs + dev-notifies on failure, same as the\n//? previous inlined block). Async because the socket-readiness gate\n//? (`waitForSocket`) is awaited last, after the cheap structural checks. Each\n//? failure path returns the SAME normalized error envelope the inlined code\n//? resolved with — behaviour-identical.\nconst prepareSyncRequest = async ({\n name,\n version,\n receiver,\n payloadData,\n}: {\n name: unknown;\n version: unknown;\n receiver: unknown;\n payloadData: unknown;\n}): Promise<PreparedSyncRequest> => {\n if (!name || typeof name !== \"string\") {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid name for syncRequest\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidName' });\n }\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.invalidName' },\n fallbackErrorCode: 'sync.invalidName',\n }) };\n }\n\n const parsedRoute = parseServiceRouteName(name);\n if (parsedRoute.status === 'error') {\n if (shouldLogDev()) {\n getLogger().error(`[syncRequest] Invalid service route name '${name}'`, undefined, { reason: parsedRoute.reason });\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'routing.invalidServiceRouteName' });\n }\n return { ok: false, error: normalizeSyncError({\n response: {\n status: 'error',\n errorCode: 'routing.invalidServiceRouteName',\n errorParams: [{ key: 'name', value: name }],\n },\n fallbackErrorCode: 'routing.invalidServiceRouteName',\n }) };\n }\n\n const sanitizedName = parsedRoute.normalizedRouteName;\n\n const data = payloadData && typeof payloadData === \"object\" ? payloadData : {};\n\n if (!version || typeof version !== 'string') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid version for syncRequest\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidVersion' });\n }\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.invalidVersion' },\n fallbackErrorCode: 'sync.invalidVersion',\n }) };\n }\n\n const normalizedReceiver = typeof receiver === 'string' ? receiver.trim() : '';\n\n if (!normalizedReceiver) {\n if (shouldLogDev()) {\n getLogger().error(\"You need to provide a receiver for syncRequest, this can be either 'all' to trigger all sockets which we do not recommend or it can be any value such as a code e.g 'Ag2cg4'. this works together with the joinRoom and leaveRoom function\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.missingReceiver' });\n }\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.missingReceiver' },\n fallbackErrorCode: 'sync.missingReceiver',\n }) };\n }\n\n if (!await waitForSocket()) {\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.ioUnavailable' },\n fallbackErrorCode: 'sync.ioUnavailable',\n }) };\n }\n if (!socket) {\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.ioUnavailable' },\n fallbackErrorCode: 'sync.ioUnavailable',\n }) };\n }\n\n return { ok: true, sanitizedName, version, data, normalizedReceiver };\n};\n\nconst syncRequestInternal = <F extends SyncFullName, V extends VersionsForFullName<F>>(\n params: SyncRequestParamsWithOptions<F, V>\n): Promise<Prettify<SyncRequestResponseForFullName<F, V>>> => {\n const runtimeParams = params as RuntimeSyncParams;\n const { name, version, receiver, ignoreSelf, onStream, offlineDropPolicy, signal: externalSignal } = runtimeParams;\n const payloadData = runtimeParams.data;\n\n type RequestOutput = Prettify<SyncRequestResponseForFullName<F, V>>;\n\n return new Promise<RequestOutput>((resolve) => {\n void (async () => {\n //? B1 — if the consumer-supplied signal is already aborted at call\n //? time, short-circuit before we even touch the socket.\n if (externalSignal?.aborted) {\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'request.aborted' },\n fallbackErrorCode: 'request.aborted',\n }));\n return;\n }\n\n const prepared = await prepareSyncRequest({ name, version, receiver, payloadData });\n if (!prepared.ok) {\n resolve(prepared.error);\n return;\n }\n const { sanitizedName, version: resolvedVersion, data, normalizedReceiver } = prepared;\n\n //? Re-narrow `socket` for the closures below. `prepareSyncRequest`\n //? already guaranteed it is non-null (returns an `ioUnavailable` error\n //? otherwise), but that guard lives in another function so TS can't\n //? carry the narrowing across the call boundary.\n const activeSocket = socket;\n if (!activeSocket) {\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.ioUnavailable' },\n fallbackErrorCode: 'sync.ioUnavailable',\n }));\n return;\n }\n\n const fullName = `sync/${sanitizedName}/${resolvedVersion}`;\n let queueId: string | null = null;\n\n const runRequest = (socketInstance: Socket) => {\n if (!canSendNow(socketInstance)) {\n queueId ??= createQueueId();\n //? For the immediate reject-new path (`drop-newest` / `reject`\n //? policy) THIS item's `onDrop` fires SYNCHRONOUSLY inside\n //? `enqueueSyncRequest`. We keep surfacing that as the historical\n //? `offline.queueFull` via the `!enqueued` branch, so suppress the\n //? `onDrop` resolve during the synchronous enqueue window and re-arm\n //? it only if the item was actually queued (so a LATER async eviction\n //? — drop-oldest by a future enqueue, or age expiry — still settles).\n let suppressOnDrop = true;\n //? `enqueueSyncRequest` returns `false` when the offline queue is\n //? full and `dropPolicy: 'reject'` is active. Resolve with a\n //? normalized error envelope so the caller can branch instead of\n //? awaiting forever.\n const enqueued = enqueueSyncRequest({\n id: queueId,\n key: fullName,\n run: (s) => {\n runRequest(s);\n },\n createdAt: Date.now(),\n dropPolicy: offlineDropPolicy,\n //? SYNC-09 — settle the awaiting caller when this QUEUED request is\n //? later EVICTED (drop-oldest by a future enqueue / age expiry)\n //? instead of ever running. Without it the promise would hang\n //? forever, since `run` is the only thing that could otherwise\n //? resolve it. The default `offlineQueue.dropPolicy` is\n //? `'drop-oldest'`, so async eviction is the NORMAL overflow path\n //? for high-churn syncs (cursor moves), not an edge case.\n onDrop: (reason) => {\n if (suppressOnDrop) return;\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'offline.dropped', errorParams: [{ key: 'reason', value: reason }] },\n fallbackErrorCode: 'offline.dropped',\n }));\n },\n });\n //? Past the synchronous enqueue window: if the item is sitting in the\n //? queue, a future eviction should settle via `onDrop`.\n suppressOnDrop = false;\n if (!enqueued) {\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'offline.queueFull' },\n fallbackErrorCode: 'offline.queueFull',\n }));\n }\n return;\n }\n\n const tempIndex = incrementResponseIndex();\n\n let cleanupProgressListener: (() => void) | null = null;\n //? CORE-06 — ack-timeout. Armed right after the request is emitted so a\n //? lost acknowledgement (server restart/crash between emit and reply)\n //? settles the promise with `sync.requestTimeout` (504) instead of\n //? hanging the awaiting caller forever. Mirrors `apiRequest`'s\n //? `api.requestTimeoutMs` wiring.\n let responseTimeout: ReturnType<typeof setTimeout> | null = null;\n const clearResponseTimeout = () => {\n if (responseTimeout) {\n clearTimeout(responseTimeout);\n responseTimeout = null;\n }\n };\n\n if (shouldLogDev()) {\n getLogger().debug(`Client Sync Request(${String(tempIndex)})`, { syncName: sanitizedName, data, receiver: normalizedReceiver, ignoreSelf });\n }\n\n //? S13 — server-issued cancel id. The server emits a `{ __cancelId }`\n //? handshake frame on the progress channel before any work runs; we\n //? capture it so an abort can target THIS exact request (not a callback\n //? name reused across concurrent same-route requests, which corrupted\n //? the server-side abort registry). The progress listener is ALWAYS\n //? registered now — even without `onStream` — so the handshake is caught\n //? on non-streaming routes too. The reserved `__cancelId` frame is\n //? filtered out of `onStream` delivery.\n let serverCancelId: string | null = null;\n const progressEventName = buildSyncProgressEventName(tempIndex);\n const progressListener = (streamPayload: SyncRequestStreamEvent & { __cancelId?: string }) => {\n if (typeof streamPayload.__cancelId === 'string') {\n serverCancelId = streamPayload.__cancelId;\n return;\n }\n\n if (typeof onStream !== 'function') return;\n\n if (shouldLogStream()) {\n getLogger().debug(`Server Sync Stream(${String(tempIndex)})`, { syncName: sanitizedName, streamPayload });\n }\n\n onStream(streamPayload);\n };\n\n socketInstance.on(progressEventName, progressListener);\n cleanupProgressListener = () => {\n socketInstance.off(progressEventName, progressListener);\n };\n\n //? Fallback cancel key for the narrow window before the `__cancelId`\n //? handshake frame arrives (abort fired immediately on a slow link).\n //? Best-effort only — once the handshake lands `serverCancelId` is the\n //? authoritative, per-request-unique key the server registered under.\n const syncCb = `${sanitizedName}/${resolvedVersion}`;\n socketInstance.emit(socketEventNames.sync, { name: fullName, data, cb: syncCb, receiver: normalizedReceiver, responseIndex: tempIndex, ignoreSelf });\n\n //? B1 / S13 — bridge the consumer's AbortSignal to the server. When the\n //? signal fires we emit `syncCancel { cb: <serverCancelId> }` so the\n //? server-side handler stops emitting new chunks. We also resolve\n //? locally with `request.aborted` so the awaiting caller settles.\n const responseEventName = buildSyncResponseEventName(tempIndex);\n //? Forward-declared so the abort/timeout paths can proactively `off()`\n //? the lingering ack listener instead of waiting for a reply that was\n //? cancelled / will never arrive.\n let cleanupResponseListener: (() => void) | null = null;\n\n let cleanupExternalAbort: (() => void) | null = null;\n if (externalSignal) {\n const externalAbortHandler = () => {\n clearResponseTimeout();\n socketInstance.emit(socketEventNames.syncCancel, { cb: serverCancelId ?? syncCb });\n cleanupProgressListener?.();\n cleanupProgressListener = null;\n cleanupResponseListener?.();\n cleanupResponseListener = null;\n cleanupExternalAbort?.();\n cleanupExternalAbort = null;\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'request.aborted' },\n fallbackErrorCode: 'request.aborted',\n }));\n };\n externalSignal.addEventListener('abort', externalAbortHandler);\n cleanupExternalAbort = () => {\n externalSignal.removeEventListener('abort', externalAbortHandler);\n };\n }\n\n //? Arm the ack-timeout. `sync.requestTimeoutMs` (default 30000) may be\n //? `false` to disable. On expiry we tear down every listener and settle\n //? with a 504 envelope so the awaiting caller never hangs.\n const effectiveTimeoutMs = getProjectConfig().sync.requestTimeoutMs;\n if (typeof effectiveTimeoutMs === 'number' && effectiveTimeoutMs > 0) {\n responseTimeout = setTimeout(() => {\n responseTimeout = null;\n //? SYNC — on ack-timeout, tell the server to STOP the in-flight work\n //? before tearing down locally. Previously the timeout only cleaned up\n //? the client listeners + settled 504, leaving the `_server` handler\n //? (and any fanout/streaming) running to completion uncancelled. The\n //? `__cancelId` handshake has long since arrived by the timeout window\n //? (the server emits it immediately), so `serverCancelId` is the\n //? authoritative key here; `syncCb` is only the sub-RTT fallback.\n socketInstance.emit(socketEventNames.syncCancel, { cb: serverCancelId ?? syncCb });\n cleanupProgressListener?.();\n cleanupProgressListener = null;\n cleanupResponseListener?.();\n cleanupResponseListener = null;\n cleanupExternalAbort?.();\n cleanupExternalAbort = null;\n if (shouldLogDev()) {\n getLogger().error(`Sync ${sanitizedName} timed out`, undefined, { responseIndex: tempIndex });\n }\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.requestTimeout', httpStatus: 504 },\n fallbackErrorCode: 'sync.requestTimeout',\n }));\n }, effectiveTimeoutMs);\n }\n\n const responseListener = (responseData: SyncAckResponse) => {\n clearResponseTimeout();\n cleanupProgressListener?.();\n cleanupProgressListener = null;\n cleanupResponseListener = null;\n cleanupExternalAbort?.();\n cleanupExternalAbort = null;\n\n if (responseData.status === \"error\") {\n const normalizedError = normalizeSyncError({\n response: responseData,\n fallbackErrorCode: 'sync.failedRequest',\n });\n\n if (shouldLogDev()) {\n getLogger().error(`Sync ${sanitizedName} failed`, undefined, { message: normalizedError.message });\n }\n if (shouldNotifyDev()) {\n notify.error({\n key: 'sync.failedRequest',\n params: [\n { key: 'name', value: sanitizedName },\n { key: 'message', value: normalizedError.message },\n ],\n });\n }\n resolve(normalizedError);\n return;\n }\n\n if (responseData.status !== 'success') {\n resolve(normalizeSyncError({\n response: responseData,\n fallbackErrorCode: 'sync.invalidServerResponse',\n }));\n return;\n }\n\n const result = responseData.result && typeof responseData.result === 'object'\n ? responseData.result\n : {};\n\n //? `RequestOutput.result` is a generic `Record<string, never>` shape\n //? that the runtime response can't structurally satisfy from the\n //? untyped `responseData.result` (server-side typing lives on the\n //? other side of the socket). The cast is the documented socket\n //? response boundary.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- socket response boundary\n resolve({\n status: 'success',\n message: typeof responseData.message === 'string' && responseData.message.trim().length > 0\n ? responseData.message\n : `sync ${sanitizedName} success`,\n result,\n } as RequestOutput);\n };\n\n socketInstance.once(responseEventName, responseListener);\n cleanupResponseListener = () => {\n socketInstance.off(responseEventName, responseListener);\n };\n };\n\n runRequest(activeSocket);\n })();\n });\n};\n\nexport function syncRequest<F extends SyncFullName, V extends VersionsForFullName<F>>(\n params: SyncRequestParamsWithOptions<F, V>\n): Promise<Prettify<SyncRequestResponseForFullName<F, V>>> {\n return syncRequestInternal(params);\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// useSyncEvents Hook - Type-Safe Event Registration\n// ═══════════════════════════════════════════════════════════════════════════════\n\n//? Hoisted to module scope so the emitted `.d.ts` for `useSyncEvents` can\n//? reference them. Local interface declarations inside an exported function\n//? trigger TS4025 (\"has or is using private name\") under `declaration: true`.\ninterface TypedCallbackParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\n clientOutput: ClientOutputForFullName<F, V>;\n serverOutput: ServerOutputForFullName<F, V>;\n}\n\ninterface UpsertParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\n name: F;\n version: V;\n callback: (params: TypedCallbackParams<F, V>) => void;\n}\n\ninterface UpsertStreamParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\n name: F;\n version: V;\n callback: SyncRouteStreamCallbackForFullName<F, V>;\n}\n\nexport const useSyncEvents = () => {\n const localRegistryRef = useRef<Map<string, SyncEventCallback>>(new Map());\n const localStreamRegistryRef = useRef<Map<string, SyncEventStreamCallback>>(new Map());\n\n const upsertSyncEventCallback = useCallback(<F extends SyncFullName, V extends VersionsForFullName<F>>(\n params: UpsertParams<F, V>\n ): (() => void) => {\n\n if (typeof params.version !== 'string') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid version for upsertSyncEventCallback\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidVersion' });\n }\n return noop;\n }\n\n if (typeof params.callback !== 'function') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid callback for upsertSyncEventCallback\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidCallback' });\n }\n return noop;\n }\n\n const routeName = params.name;\n const parsedRoute = parseServiceRouteName(routeName);\n if (parsedRoute.status === 'error') {\n if (shouldLogDev()) {\n getLogger().error(`Invalid name for upsertSyncEventCallback`, undefined, { routeName, reason: parsedRoute.reason });\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'routing.invalidServiceRouteName' });\n }\n return noop;\n }\n const sanitizedName = parsedRoute.normalizedRouteName;\n\n const routeVersion = params.version;\n const fullName = `sync/${sanitizedName}/${routeVersion}`;\n const callback: SyncEventCallback = ({ clientOutput, serverOutput }) => {\n params.callback({\n clientOutput: clientOutput as ClientOutputForFullName<F, V>,\n serverOutput: serverOutput as ServerOutputForFullName<F, V>,\n });\n };\n const callbacks = getCallbacksForRoute(fullName);\n\n const previousForRoute = localRegistryRef.current.get(fullName);\n if (previousForRoute) {\n syncEvents[fullName] = callbacks.filter((cb) => cb !== previousForRoute);\n }\n\n const nextCallbacks = getCallbacksForRoute(fullName);\n // Multiple components can intentionally subscribe to the same sync event.\n // Only warn when the exact same callback is registered twice.\n if (nextCallbacks.includes(callback)) {\n if (shouldLogDev()) {\n getLogger().warn(`[SyncEvents] Duplicate callback registration was ignored`, { fullName });\n }\n\n localRegistryRef.current.set(fullName, callback);\n\n return () => {\n const current = getCallbacksForRoute(fullName);\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\n\n if (localRegistryRef.current.get(fullName) === callback) {\n localRegistryRef.current.delete(fullName);\n }\n };\n }\n\n nextCallbacks.push(callback);\n syncEvents[fullName] = nextCallbacks;\n localRegistryRef.current.set(fullName, callback);\n\n return () => {\n const current = getCallbacksForRoute(fullName);\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\n\n if (localRegistryRef.current.get(fullName) === callback) {\n localRegistryRef.current.delete(fullName);\n }\n };\n }, []);\n\n const upsertSyncEventStreamCallback = useCallback(<F extends SyncFullName, V extends VersionsForFullName<F>>(\n params: UpsertStreamParams<F, V>\n ): (() => void) => {\n\n if (typeof params.version !== 'string') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid version for upsertSyncEventStreamCallback\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidVersion' });\n }\n return noop;\n }\n\n if (typeof params.callback !== 'function') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid callback for upsertSyncEventStreamCallback\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidCallback' });\n }\n return noop;\n }\n\n const routeName = params.name;\n const parsedRoute = parseServiceRouteName(routeName);\n if (parsedRoute.status === 'error') {\n if (shouldLogDev()) {\n getLogger().error(`Invalid name for upsertSyncEventStreamCallback`, undefined, { routeName, reason: parsedRoute.reason });\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'routing.invalidServiceRouteName' });\n }\n return noop;\n }\n const sanitizedName = parsedRoute.normalizedRouteName;\n\n const routeVersion = params.version;\n const fullName = `sync/${sanitizedName}/${routeVersion}`;\n //? Combined union: both `serverStream` (from `broadcastStream` /\n //? `streamTo`) and `clientStream` (from `_client_v{n}.ts`) flow through\n //? this callback. The type matches `SyncRouteStreamCallbackForFullName`.\n const typedCallback = params.callback as (params: {\n stream: SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>>;\n }) => void;\n const callback: SyncEventStreamCallback = ({ stream }) => {\n typedCallback({\n stream: stream as SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>>,\n });\n };\n const callbacks = getStreamCallbacksForRoute(fullName);\n\n const previousForRoute = localStreamRegistryRef.current.get(fullName);\n if (previousForRoute) {\n syncStreamEvents[fullName] = callbacks.filter((cb) => cb !== previousForRoute);\n }\n\n const nextCallbacks = getStreamCallbacksForRoute(fullName);\n if (nextCallbacks.includes(callback)) {\n if (shouldLogDev()) {\n getLogger().warn(`[SyncEvents] Duplicate stream callback registration was ignored`, { fullName });\n }\n\n localStreamRegistryRef.current.set(fullName, callback);\n\n return () => {\n const current = getStreamCallbacksForRoute(fullName);\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\n\n if (localStreamRegistryRef.current.get(fullName) === callback) {\n localStreamRegistryRef.current.delete(fullName);\n }\n };\n }\n\n nextCallbacks.push(callback);\n syncStreamEvents[fullName] = nextCallbacks;\n localStreamRegistryRef.current.set(fullName, callback);\n\n return () => {\n const current = getStreamCallbacksForRoute(fullName);\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\n\n if (localStreamRegistryRef.current.get(fullName) === callback) {\n localStreamRegistryRef.current.delete(fullName);\n }\n };\n }, []);\n\n useEffect(() => {\n const localRegistry = localRegistryRef.current;\n const localStreamRegistry = localStreamRegistryRef.current;\n\n return () => {\n for (const [fullName, callback] of localRegistry.entries()) {\n const current = getCallbacksForRoute(fullName);\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\n }\n\n for (const [fullName, callback] of localStreamRegistry.entries()) {\n const current = getStreamCallbacksForRoute(fullName);\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\n }\n\n localRegistry.clear();\n localStreamRegistry.clear();\n };\n }, []);\n\n return { upsertSyncEventCallback, upsertSyncEventStreamCallback };\n}\n\nexport const useSyncEventTrigger = () => {\n const triggerSyncEvent = useCallback((name: string, clientOutput: unknown = {}, serverOutput: unknown = {}) => {\n triggerSyncCallbacks(name, clientOutput, serverOutput);\n }, []);\n\n const triggerSyncStreamEvent = useCallback((name: string, stream: SyncRouteStreamEvent) => {\n triggerSyncStreamCallbacks(name, stream);\n }, []);\n\n return { triggerSyncEvent, triggerSyncStreamEvent }\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Sync Receive Bridge — attachSyncReceiver\n// ═══════════════════════════════════════════════════════════════════════════════\n\n//? Shape of the `sync` socket event the server emits to clients. Mirrors the\n//? server's emit payload; extra keys are stream chunk fields.\ninterface SyncEventPayload {\n cb?: string;\n clientOutput?: unknown;\n serverOutput?: unknown;\n message?: string;\n status?: 'success' | 'error' | 'stream';\n fullName?: string;\n errorCode?: string;\n errorParams?: { key: string; value: string | number | boolean }[];\n httpStatus?: number;\n [key: string]: unknown;\n}\n\nconst normalizeSyncRouteKey = (value: string): string => {\n const sanitized = value.replaceAll(/^\\/+|\\/+$/g, '');\n if (sanitized.length === 0) return '';\n if (sanitized.startsWith('sync/')) return sanitized;\n return `sync/${sanitized}`;\n};\n\nconst getSyncRouteKeys = ({ fullName, cb }: { fullName?: string; cb?: string }): string[] => {\n const keys = new Set<string>();\n if (typeof fullName === 'string') {\n const normalized = normalizeSyncRouteKey(fullName);\n if (normalized) keys.add(normalized);\n }\n if (typeof cb === 'string') {\n const normalized = normalizeSyncRouteKey(cb);\n if (normalized) keys.add(normalized);\n }\n return [...keys];\n};\n\n//? Per-socket idempotency guard — attaching twice to the same socket would\n//? double-dispatch every sync event.\nconst attachedSyncSockets = new WeakSet<Socket>();\n\n//? Wire the incoming `sync` socket listener that routes server fan-out into the\n//? registered `upsertSyncEventCallback` / `upsertSyncEventStreamCallback`\n//? subscribers. Previously this logic was inlined in the consumer's\n//? `socketInitializer.ts`; relocating it here makes `@luckystack/sync` own its\n//? own receive path, so a consumer only has to dynamic-import this and call it\n//? (the add-later sync wiring is then a single line, not a copy-paste block).\nexport const attachSyncReceiver = (socketInstance: Socket): void => {\n if (attachedSyncSockets.has(socketInstance)) return;\n attachedSyncSockets.add(socketInstance);\n\n socketInstance.on(socketEventNames.sync, (payload: SyncEventPayload) => {\n const { cb, clientOutput, serverOutput, message, status, fullName, errorCode, errorParams } = payload;\n if (shouldLogDev()) {\n getLogger().debug('Server Sync Response', { payload });\n }\n\n const routeKeys = getSyncRouteKeys({ fullName, cb });\n\n if (status === 'stream') {\n if (routeKeys.length === 0) return;\n\n //? SYNC-N7 — strip all reserved envelope keys from the forwarded stream\n //? payload so a consumer handler that accidentally emits `errorCode`,\n //? `errorParams`, `httpStatus`, or `message` in a stream chunk doesn't\n //? pollute the client callback's stream argument with error-envelope fields.\n //? `status`, `fullName`, `cb` were already stripped; the others are added here.\n const streamPayload = { ...payload };\n delete streamPayload.status;\n delete streamPayload.fullName;\n delete streamPayload.cb;\n delete streamPayload.errorCode;\n delete streamPayload.errorParams;\n delete streamPayload.httpStatus;\n delete streamPayload.message;\n\n if (shouldLogStream()) {\n getLogger().debug('Server Sync Stream', { routeKeys, streamPayload });\n }\n\n for (const routeKey of routeKeys) {\n triggerSyncStreamCallbacks(routeKey, streamPayload);\n }\n return;\n }\n\n if (status === 'error') {\n if (errorCode === 'sync.ignore' || message === 'sync.ignore') return;\n if (shouldNotifyDev()) {\n if (errorCode) {\n notify.error({ key: errorCode, params: errorParams });\n } else if (message) {\n notify.error({ key: message });\n }\n }\n return;\n }\n\n if (routeKeys.length === 0) {\n const errorMessage = `Sync response is missing fullName for cb '${cb ?? 'unknown'}'.`;\n if (shouldLogDev()) {\n getLogger().error(errorMessage);\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidRequestFormat' });\n }\n //? SYNC-10 — degrade to a logged warning instead of throwing. This runs\n //? inside the `socket.on(sync)` listener; a `throw` here is an uncaught\n //? exception on a server-controlled (malformed) frame that can kill other\n //? listeners bound to the same emit. The error is already logged + notified\n //? above, so `return` loses no information.\n return;\n }\n\n for (const routeKey of routeKeys) {\n triggerSyncCallbacks(routeKey, clientOutput, serverOutput);\n }\n });\n};\n\ntype SocketStatusSetter = Dispatch<\n SetStateAction<{\n self: statusContent;\n [userId: string]: statusContent;\n }>\n>;\n\nconst buildConnectHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => () => {\n if (shouldLogSocketStatus()) getLogger().info(\"Connected to server\");\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"CONNECTED\" } }));\n};\n\nconst buildDisconnectHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => () => {\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"DISCONNECTED\" } }));\n if (shouldLogSocketStatus()) getLogger().info(\"Disconnected, trying to reconnect...\");\n};\n\nconst buildReconnectAttemptHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => (attempt: number) => {\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"RECONNECTING\", reconnectAttempt: attempt } }));\n if (shouldLogSocketStatus()) getLogger().info(`Reconnecting attempt ${String(attempt)}...`);\n};\n\nconst buildUserAfkHandler = ({\n setSocketStatus, sessionRef,\n}: {\n setSocketStatus: SocketStatusSetter;\n sessionRef: RefObject<SessionLayout | null>;\n}) => ({ userId, endTime }: { userId: string; endTime?: number }) => {\n if (sessionRef.current !== null && userId === sessionRef.current.id) {\n setSocketStatus(prev => ({\n ...prev,\n self: { status: \"DISCONNECTED\", reconnectAttempt: undefined, endTime },\n }));\n } else {\n setSocketStatus(prev => ({\n ...prev,\n [userId]: { status: \"DISCONNECTED\", endTime },\n }));\n }\n};\n\nconst buildUserBackHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => ({ userId }: { userId: string }) => {\n if (shouldLogSocketStatus()) getLogger().debug(\"userBack\", { userId });\n setSocketStatus(prev => ({\n ...prev,\n [userId]: { status: \"CONNECTED\", endTime: undefined },\n }));\n};\n\nconst buildConnectErrorHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => (err: { message: string }) => {\n if (shouldLogSocketStatus()) getLogger().debug(\"connect_error\", { err });\n setSocketStatus(prev => ({\n ...prev,\n self: { ...prev.self, status: \"DISCONNECTED\", reconnectAttempt: undefined },\n }));\n if (shouldLogDev()) getLogger().error(`Connection error`, err);\n if (shouldNotifyDev()) notify.error({ key: 'common.connectionError' });\n};\n\n//? SYNC-N6 — `initSyncRequest` owns the canonical lifecycle listeners\n//? (`connect`, `disconnect`, `reconnect_attempt`, `userAfk`, `userBack`,\n//? `connect_error`). A consumer's `socketInitializer.ts` that also registers\n//? `disconnect`/`reconnect_attempt` on the same socket instance creates\n//? duplicate handlers — both fire on every event. Since `initSyncRequest` is\n//? idempotent (tears down the previous set before re-binding), consumer code\n//? should delegate lifecycle status updates here and avoid re-registering\n//? those events directly. Duplicate `console.log` in the consumer is harmless\n//? but produces double log lines.\nexport const initSyncRequest = async ({\n setSocketStatus,\n sessionRef\n}: {\n setSocketStatus: SocketStatusSetter;\n sessionRef: RefObject<SessionLayout | null> | null;\n}) => {\n\n if (!await waitForSocket()) { return; }\n if (!socket) { return; }\n if (!sessionRef) { return; }\n\n if (activeLifecycleHandlers) {\n socket.off(socketEventNames.connect, activeLifecycleHandlers.connect);\n socket.off(socketEventNames.disconnect, activeLifecycleHandlers.disconnect);\n socket.off(socketEventNames.reconnectAttempt, activeLifecycleHandlers.reconnectAttempt);\n socket.off(socketEventNames.userAfk, activeLifecycleHandlers.userAfk);\n socket.off(socketEventNames.userBack, activeLifecycleHandlers.userBack);\n socket.off(socketEventNames.connectError, activeLifecycleHandlers.connectError);\n }\n\n const connect = buildConnectHandler({ setSocketStatus });\n const disconnect = buildDisconnectHandler({ setSocketStatus });\n const reconnectAttempt = buildReconnectAttemptHandler({ setSocketStatus });\n const userAfk = buildUserAfkHandler({ setSocketStatus, sessionRef });\n const userBack = buildUserBackHandler({ setSocketStatus });\n const connectError = buildConnectErrorHandler({ setSocketStatus });\n\n activeLifecycleHandlers = {\n connect,\n disconnect,\n reconnectAttempt,\n userAfk,\n userBack,\n connectError,\n };\n\n socket.on(socketEventNames.connect, connect);\n socket.on(socketEventNames.disconnect, disconnect);\n socket.on(socketEventNames.reconnectAttempt, reconnectAttempt);\n socket.on(socketEventNames.userAfk, userAfk);\n socket.on(socketEventNames.userBack, userBack);\n socket.on(socketEventNames.connectError, connectError);\n\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/syncRequest.ts"],"sourcesContent":["//? All cross-package imports go through `@luckystack/core/client` (the\n//? browser-safe subpath) — never the main `@luckystack/core` barrel which\n//? re-exports server-only modules (paths.ts, db.ts, redis.ts) that must\n//? not enter a Vite client bundle.\nimport type {\n BaseSessionLayout as SessionLayout,\n StreamPayload,\n SyncTypeMap,\n statusContent,\n} from \"@luckystack/core/client\";\nimport {\n getLogger,\n getProjectConfig,\n notify,\n incrementResponseIndex,\n socket,\n waitForSocket,\n enqueueSyncRequest,\n removeSyncQueueItem,\n isOnline,\n normalizeErrorResponseCore,\n parseServiceRouteName,\n buildSyncProgressEventName,\n buildSyncResponseEventName,\n socketEventNames,\n invokeRoutedHttp,\n} from \"@luckystack/core/client\";\nimport { Dispatch, RefObject, SetStateAction, useCallback, useEffect, useRef } from \"react\";\nimport { Socket } from \"socket.io-client\";\n\nexport type SyncRequestStreamEvent<T extends StreamPayload = StreamPayload> = T;\n\nexport type SyncRouteStreamEvent<T extends StreamPayload = StreamPayload> = T;\n\ntype SyncRequestStreamCallback = (event: SyncRequestStreamEvent) => void;\ntype SyncEventStreamCallback = (params: { stream: SyncRouteStreamEvent }) => void;\n\nconst shouldLogDev = () => getProjectConfig().logging.devLogs;\nconst shouldNotifyDev = () => getProjectConfig().logging.devNotifications;\nconst shouldLogSocketStatus = () => getProjectConfig().logging.socketStatus;\nconst shouldLogStream = () => getProjectConfig().logging.stream;\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Type Helpers for Sync Requests\n// ═══════════════════════════════════════════════════════════════════════════════\n\n// Check if data input is required (i.e., T does NOT allow empty object)\n// Unions like {a:1} | {b:1} do NOT allow {}, so data will be required\ntype DataRequired<T> = Record<string, never> extends T ? false : true;\n\ntype UnionToIntersection<U> =\n (U extends unknown ? (arg: U) => void : never) extends ((arg: infer I) => void)\n ? I\n : never;\n\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Global Sync Params\n// ═══════════════════════════════════════════════════════════════════════════════\n\n// All possible sync names across all pages\ntype SyncRouteRecord = UnionToIntersection<{\n [P in keyof SyncTypeMap]: {\n [N in keyof SyncTypeMap[P] as P extends 'root'\n ? `system/${Extract<N, string>}`\n : `${Extract<P, string>}/${Extract<N, string>}`]: SyncTypeMap[P][N]\n }\n}[keyof SyncTypeMap]>;\n\ntype SyncFullName = Extract<keyof SyncRouteRecord, string>;\ntype VersionsForFullName<F extends SyncFullName> = Extract<keyof SyncRouteRecord[F], string>;\n\ntype ClientInputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientInput: infer I }\n ? I\n : never;\n\ntype ServerOutputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { serverOutput: infer O }\n ? O\n : never;\n\ntype ClientOutputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientOutput: infer O }\n ? O\n : never;\n\ntype ServerStreamForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { serverStream: infer O }\n ? O\n : never;\n\ntype ClientStreamForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientStream: infer O }\n ? O\n : never;\n\ntype SyncRequestStreamCallbackForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\n [ServerStreamForFullName<F, V>] extends [never]\n ? never\n : (event: SyncRequestStreamEvent<Prettify<ServerStreamForFullName<F, V> extends StreamPayload ? ServerStreamForFullName<F, V> : StreamPayload>>) => void;\n\n//? Recipients see chunks from THREE sources, all on the same wire channel:\n//? 1. `_client_v{n}.ts`'s `stream(...)` (per-recipient, runs after _server)\n//? 2. `_server_v{n}.ts`'s `broadcastStream(...)` (room-wide fan-out)\n//? 3. `_server_v{n}.ts`'s `streamTo(tokens, ...)` (selective fan-out)\n//? All three flow into `upsertSyncEventCallback`'s `stream` argument. Both\n//? serverStream and clientStream are folded into the union so the callback\n//? sees every shape the route can emit. If neither side ever streams, the\n//? callback type collapses to never (compile error to register one).\ntype CombinedRouteStream<F extends SyncFullName, V extends VersionsForFullName<F>> =\n | (ClientStreamForFullName<F, V> extends never ? never : ClientStreamForFullName<F, V>)\n | (ServerStreamForFullName<F, V> extends never ? never : ServerStreamForFullName<F, V>);\n\ntype SyncRouteStreamCallbackForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\n [CombinedRouteStream<F, V>] extends [never]\n ? never\n : (params: { stream: SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>> }) => void;\n\ntype SyncParamsForFullName<\n F extends SyncFullName,\n V extends VersionsForFullName<F>\n> = DataRequired<ClientInputForFullName<F, V>> extends true\n ? {\n name: F;\n version: V;\n data: ClientInputForFullName<F, V>;\n receiver: string;\n ignoreSelf?: boolean;\n onStream?: SyncRequestStreamCallbackForFullName<F, V>;\n /**\n * Per-request override of `projectConfig.offlineQueue.dropPolicy`. Lets a\n * specific sync (\"editor cursor move\") pick `'drop-oldest'` while the\n * app default stays `'reject'` for safer sends. When omitted, falls back\n * to the global config.\n */\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\n /**\n * Optional AbortSignal. When aborted the client emits `syncCancel { cb }`\n * to the server and resolves locally with\n * `{ status: 'error', errorCode: 'request.aborted' }`.\n */\n signal?: AbortSignal;\n }\n : {\n name: F;\n version: V;\n data?: ClientInputForFullName<F, V>;\n receiver: string;\n ignoreSelf?: boolean;\n onStream?: SyncRequestStreamCallbackForFullName<F, V>;\n /** Per-request override (see typed branch). */\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\n /** Optional AbortSignal — see typed branch. */\n signal?: AbortSignal;\n };\n\ninterface RuntimeSyncParams {\n name?: string;\n version?: string;\n data?: unknown;\n receiver?: string;\n ignoreSelf?: boolean;\n onStream?: SyncRequestStreamCallback;\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\n signal?: AbortSignal;\n}\n\ninterface SyncErrorParam { key: string; value: string | number | boolean };\n\ninterface SyncResponseError {\n status: 'error';\n message: string;\n errorCode: string;\n errorParams?: SyncErrorParam[];\n httpStatus?: number;\n}\n\ntype RoutedSyncFailureKind = 'timeout' | 'aborted' | 'network-error' | 'invalid-response';\n\nconst routedSyncErrorCode = (kind: RoutedSyncFailureKind): string => {\n if (kind === 'timeout') return 'sync.requestTimeout';\n if (kind === 'aborted') return 'request.aborted';\n if (kind === 'invalid-response') return 'sync.invalidServerResponse';\n return 'sync.ioUnavailable';\n};\n\ninterface SyncAckResponse {\n status?: 'success' | 'error';\n message?: string;\n result?: unknown;\n errorCode?: string;\n errorParams?: SyncErrorParam[];\n httpStatus?: number;\n}\n\ntype SyncResultForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\n [ServerOutputForFullName<F, V>] extends [never]\n ? Record<string, never>\n : ServerOutputForFullName<F, V>;\n\ntype SyncRequestResponseForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\n | SyncResponseError\n | {\n status: 'success';\n message: string;\n result: SyncResultForFullName<F, V>;\n };\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Sync Event Callbacks Registry\n// ═══════════════════════════════════════════════════════════════════════════════\n\ntype SyncEventCallback = (params: { clientOutput: unknown; serverOutput: unknown }) => void;\nconst syncEvents: Partial<Record<string, SyncEventCallback[]>> = {};\nconst syncStreamEvents: Partial<Record<string, SyncEventStreamCallback[]>> = {};\nconst noop = () => null;\n\ninterface SyncLifecycleHandlers {\n connect: () => void;\n disconnect: () => void;\n reconnectAttempt: (attempt: number) => void;\n userAfk: (payload: { userId: string; endTime?: number }) => void;\n userBack: (payload: { userId: string }) => void;\n connectError: (err: { message: string }) => void;\n}\n\nlet activeLifecycleHandlers: SyncLifecycleHandlers | null = null;\n\nconst canSendNow = (socketInstance: Socket) => {\n if (!socketInstance.connected) return false;\n return isOnline();\n};\n\nconst createQueueId = () => {\n return `${String(Date.now())}-${String(Math.random())}`;\n};\n\nconst getCallbacksForRoute = (route: string): SyncEventCallback[] => {\n syncEvents[route] ??= [];\n return syncEvents[route];\n};\n\nconst getStreamCallbacksForRoute = (route: string): SyncEventStreamCallback[] => {\n syncStreamEvents[route] ??= [];\n return syncStreamEvents[route];\n};\n\nconst triggerSyncCallbacks = (name: string, clientOutput: unknown, serverOutput: unknown) => {\n const callbacks = syncEvents[name] ?? [];\n if (callbacks.length === 0) {\n //? Streaming-only routes legitimately register ONLY a stream callback —\n //? the final success envelope is implicit in the stream completing. Only\n //? warn when NEITHER callback type is registered for the route.\n const streamCallbacks = syncStreamEvents[name] ?? [];\n if (streamCallbacks.length === 0 && shouldLogDev()) {\n getLogger().warn(`Sync event ${name} has no registered callback on this page`);\n }\n return;\n }\n\n for (const callback of callbacks) {\n callback({ clientOutput, serverOutput });\n }\n};\n\nconst triggerSyncStreamCallbacks = (name: string, stream: SyncRouteStreamEvent) => {\n const callbacks = syncStreamEvents[name] ?? [];\n if (callbacks.length === 0) {\n return;\n }\n\n for (const callback of callbacks) {\n callback({ stream });\n }\n};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// syncRequest Function Overloads\n// ═══════════════════════════════════════════════════════════════════════════════\n\nconst normalizeSyncError = ({\n response,\n fallbackErrorCode,\n}: {\n response: SyncAckResponse;\n fallbackErrorCode: string;\n}): SyncResponseError => {\n const normalized = normalizeErrorResponseCore({\n response,\n fallbackErrorCode,\n resolveMessage: ({ errorCode }) => {\n if (typeof response.message === 'string' && response.message.trim().length > 0) {\n return response.message;\n }\n\n return errorCode;\n },\n });\n\n return {\n status: 'error',\n message: normalized.message,\n errorCode: normalized.errorCode,\n errorParams: normalized.errorParams,\n httpStatus: normalized.httpStatus,\n };\n};\n\ntype SyncRequestParamsWithOptions<F extends SyncFullName, V extends VersionsForFullName<F>> =\n SyncParamsForFullName<F, V>;\n\n//? Outcome of the pre-flight validation phase of a sync send. Either a\n//? normalized error envelope to resolve the request with, or the sanitized\n//? values the send phase needs. Extracted from `syncRequestInternal` so the\n//? \"validate first, execute second\" split is explicit (the orchestrator stays\n//? a thin promise wrapper around prepare → run).\ntype PreparedSyncRequest =\n | { ok: false; error: SyncResponseError }\n | { ok: true; sanitizedName: string; version: string; data: object; normalizedReceiver: string };\n\n//? Pure-ish validation chain (logs + dev-notifies on failure, same as the\n//? previous inlined block). Async because the socket-readiness gate\n//? (`waitForSocket`) is awaited last, after the cheap structural checks. Each\n//? failure path returns the SAME normalized error envelope the inlined code\n//? resolved with — behaviour-identical.\nconst prepareSyncRequest = async ({\n name,\n version,\n receiver,\n payloadData,\n}: {\n name: unknown;\n version: unknown;\n receiver: unknown;\n payloadData: unknown;\n}): Promise<PreparedSyncRequest> => {\n if (!name || typeof name !== \"string\") {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid name for syncRequest\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidName' });\n }\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.invalidName' },\n fallbackErrorCode: 'sync.invalidName',\n }) };\n }\n\n const parsedRoute = parseServiceRouteName(name);\n if (parsedRoute.status === 'error') {\n if (shouldLogDev()) {\n getLogger().error(`[syncRequest] Invalid service route name '${name}'`, undefined, { reason: parsedRoute.reason });\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'routing.invalidServiceRouteName' });\n }\n return { ok: false, error: normalizeSyncError({\n response: {\n status: 'error',\n errorCode: 'routing.invalidServiceRouteName',\n errorParams: [{ key: 'name', value: name }],\n },\n fallbackErrorCode: 'routing.invalidServiceRouteName',\n }) };\n }\n\n const sanitizedName = parsedRoute.normalizedRouteName;\n\n const data = payloadData && typeof payloadData === \"object\" ? payloadData : {};\n\n if (!version || typeof version !== 'string') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid version for syncRequest\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidVersion' });\n }\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.invalidVersion' },\n fallbackErrorCode: 'sync.invalidVersion',\n }) };\n }\n\n const normalizedReceiver = typeof receiver === 'string' ? receiver.trim() : '';\n\n if (!normalizedReceiver) {\n if (shouldLogDev()) {\n getLogger().error(\"You need to provide a receiver for syncRequest, this can be either 'all' to trigger all sockets which we do not recommend or it can be any value such as a code e.g 'Ag2cg4'. this works together with the joinRoom and leaveRoom function\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.missingReceiver' });\n }\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.missingReceiver' },\n fallbackErrorCode: 'sync.missingReceiver',\n }) };\n }\n\n if (!await waitForSocket()) {\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.ioUnavailable' },\n fallbackErrorCode: 'sync.ioUnavailable',\n }) };\n }\n if (!socket) {\n return { ok: false, error: normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.ioUnavailable' },\n fallbackErrorCode: 'sync.ioUnavailable',\n }) };\n }\n\n return { ok: true, sanitizedName, version, data, normalizedReceiver };\n};\n\nconst syncRequestInternal = <F extends SyncFullName, V extends VersionsForFullName<F>>(\n params: SyncRequestParamsWithOptions<F, V>\n): Promise<Prettify<SyncRequestResponseForFullName<F, V>>> => {\n const runtimeParams = params as RuntimeSyncParams;\n const { name, version, receiver, ignoreSelf, onStream, offlineDropPolicy, signal: externalSignal } = runtimeParams;\n const payloadData = runtimeParams.data;\n\n type RequestOutput = Prettify<SyncRequestResponseForFullName<F, V>>;\n\n return new Promise<RequestOutput>((resolve) => {\n void (async () => {\n //? B1 — if the consumer-supplied signal is already aborted at call\n //? time, short-circuit before we even touch the socket.\n if (externalSignal?.aborted) {\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'request.aborted' },\n fallbackErrorCode: 'request.aborted',\n }));\n return;\n }\n\n const prepared = await prepareSyncRequest({ name, version, receiver, payloadData });\n if (!prepared.ok) {\n resolve(prepared.error);\n return;\n }\n const { sanitizedName, version: resolvedVersion, data, normalizedReceiver } = prepared;\n\n //? Re-narrow `socket` for the closures below. `prepareSyncRequest`\n //? already guaranteed it is non-null (returns an `ioUnavailable` error\n //? otherwise), but that guard lives in another function so TS can't\n //? carry the narrowing across the call boundary.\n const activeSocket = socket;\n if (!activeSocket) {\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.ioUnavailable' },\n fallbackErrorCode: 'sync.ioUnavailable',\n }));\n return;\n }\n\n const fullName = `sync/${sanitizedName}/${resolvedVersion}`;\n let queueId: string | null = null;\n\n const runRequest = (socketInstance: Socket) => {\n if (!canSendNow(socketInstance)) {\n queueId ??= createQueueId();\n //? For the immediate reject-new path (`drop-newest` / `reject`\n //? policy) THIS item's `onDrop` fires SYNCHRONOUSLY inside\n //? `enqueueSyncRequest`. We keep surfacing that as the historical\n //? `offline.queueFull` via the `!enqueued` branch, so suppress the\n //? `onDrop` resolve during the synchronous enqueue window and re-arm\n //? it only if the item was actually queued (so a LATER async eviction\n //? — drop-oldest by a future enqueue, or age expiry — still settles).\n let suppressOnDrop = true;\n //? `enqueueSyncRequest` returns `false` when the offline queue is\n //? full and `dropPolicy: 'reject'` is active. Resolve with a\n //? normalized error envelope so the caller can branch instead of\n //? awaiting forever.\n const enqueued = enqueueSyncRequest({\n id: queueId,\n key: fullName,\n run: (s) => {\n runRequest(s);\n },\n createdAt: Date.now(),\n dropPolicy: offlineDropPolicy,\n //? SYNC-09 — settle the awaiting caller when this QUEUED request is\n //? later EVICTED (drop-oldest by a future enqueue / age expiry)\n //? instead of ever running. Without it the promise would hang\n //? forever, since `run` is the only thing that could otherwise\n //? resolve it. The default `offlineQueue.dropPolicy` is\n //? `'drop-oldest'`, so async eviction is the NORMAL overflow path\n //? for high-churn syncs (cursor moves), not an edge case.\n onDrop: (reason) => {\n if (suppressOnDrop) return;\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'offline.dropped', errorParams: [{ key: 'reason', value: reason }] },\n fallbackErrorCode: 'offline.dropped',\n }));\n },\n });\n //? Past the synchronous enqueue window: if the item is sitting in the\n //? queue, a future eviction should settle via `onDrop`.\n suppressOnDrop = false;\n if (!enqueued) {\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'offline.queueFull' },\n fallbackErrorCode: 'offline.queueFull',\n }));\n } else if (externalSignal && queueId) {\n const capturedQueueId = queueId;\n externalSignal.addEventListener('abort', () => {\n removeSyncQueueItem(capturedQueueId);\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'request.aborted' },\n fallbackErrorCode: 'request.aborted',\n }));\n }, { once: true });\n }\n return;\n }\n\n if (getProjectConfig().transport.invocation === 'routed-http') {\n void (async () => {\n const routed = await invokeRoutedHttp<SyncAckResponse, SyncRequestStreamEvent>({\n path: `/sync/${sanitizedName}/${resolvedVersion}`,\n method: 'POST',\n data: {\n data,\n receiver: normalizedReceiver,\n ignoreSelf: ignoreSelf ?? false,\n },\n stream: onStream,\n signals: [externalSignal],\n timeoutMs: getProjectConfig().sync.requestTimeoutMs,\n });\n if (queueId) removeSyncQueueItem(queueId);\n\n if (routed.kind !== 'response') {\n const errorCode = routedSyncErrorCode(routed.kind);\n resolve(normalizeSyncError({\n response: {\n status: 'error',\n errorCode,\n httpStatus: routed.kind === 'timeout' ? 504 : undefined,\n },\n fallbackErrorCode: errorCode,\n }));\n return;\n }\n\n const responseData = routed.response;\n if (responseData.status === 'error') {\n const normalizedError = normalizeSyncError({\n response: responseData,\n fallbackErrorCode: 'sync.failedRequest',\n });\n if (shouldNotifyDev()) {\n notify.error({ key: normalizedError.errorCode, params: normalizedError.errorParams });\n }\n resolve(normalizedError);\n return;\n }\n if (responseData.status !== 'success') {\n resolve(normalizeSyncError({\n response: responseData,\n fallbackErrorCode: 'sync.invalidServerResponse',\n }));\n return;\n }\n\n const result = responseData.result && typeof responseData.result === 'object'\n ? responseData.result\n : {};\n //? HTTP response boundary: generated route typing supplies the\n //? precise result shape, while JSON parsing necessarily starts at unknown.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- routed response boundary\n resolve({\n status: 'success',\n message: typeof responseData.message === 'string' && responseData.message.trim().length > 0\n ? responseData.message\n : `sync ${sanitizedName} success`,\n result,\n } as RequestOutput);\n })();\n return;\n }\n\n const tempIndex = incrementResponseIndex();\n\n let cleanupProgressListener: (() => void) | null = null;\n //? CORE-06 — ack-timeout. Armed right after the request is emitted so a\n //? lost acknowledgement (server restart/crash between emit and reply)\n //? settles the promise with `sync.requestTimeout` (504) instead of\n //? hanging the awaiting caller forever. Mirrors `apiRequest`'s\n //? `api.requestTimeoutMs` wiring.\n let responseTimeout: ReturnType<typeof setTimeout> | null = null;\n const clearResponseTimeout = () => {\n if (responseTimeout) {\n clearTimeout(responseTimeout);\n responseTimeout = null;\n }\n };\n\n if (shouldLogDev()) {\n getLogger().debug(`Client Sync Request(${String(tempIndex)})`, { syncName: sanitizedName, data, receiver: normalizedReceiver, ignoreSelf });\n }\n\n //? S13 — server-issued cancel id. The server emits a `{ __cancelId }`\n //? handshake frame on the progress channel before any work runs; we\n //? capture it so an abort can target THIS exact request (not a callback\n //? name reused across concurrent same-route requests, which corrupted\n //? the server-side abort registry). The progress listener is ALWAYS\n //? registered now — even without `onStream` — so the handshake is caught\n //? on non-streaming routes too. The reserved `__cancelId` frame is\n //? filtered out of `onStream` delivery.\n let serverCancelId: string | null = null;\n const progressEventName = buildSyncProgressEventName(tempIndex);\n const progressListener = (streamPayload: SyncRequestStreamEvent & { __cancelId?: string }) => {\n if (typeof streamPayload.__cancelId === 'string') {\n serverCancelId = streamPayload.__cancelId;\n return;\n }\n\n if (typeof onStream !== 'function') return;\n\n if (shouldLogStream()) {\n getLogger().debug(`Server Sync Stream(${String(tempIndex)})`, { syncName: sanitizedName, streamPayload });\n }\n\n onStream(streamPayload);\n };\n\n socketInstance.on(progressEventName, progressListener);\n cleanupProgressListener = () => {\n socketInstance.off(progressEventName, progressListener);\n };\n\n //? Fallback cancel key for the narrow window before the `__cancelId`\n //? handshake frame arrives (abort fired immediately on a slow link).\n //? Best-effort only — once the handshake lands `serverCancelId` is the\n //? authoritative, per-request-unique key the server registered under.\n const syncCb = `${sanitizedName}/${resolvedVersion}`;\n socketInstance.emit(socketEventNames.sync, { name: fullName, data, cb: syncCb, receiver: normalizedReceiver, responseIndex: tempIndex, ignoreSelf });\n\n //? B1 / S13 — bridge the consumer's AbortSignal to the server. When the\n //? signal fires we emit `syncCancel { cb: <serverCancelId> }` so the\n //? server-side handler stops emitting new chunks. We also resolve\n //? locally with `request.aborted` so the awaiting caller settles.\n const responseEventName = buildSyncResponseEventName(tempIndex);\n //? Forward-declared so the abort/timeout paths can proactively `off()`\n //? the lingering ack listener instead of waiting for a reply that was\n //? cancelled / will never arrive.\n let cleanupResponseListener: (() => void) | null = null;\n\n let cleanupExternalAbort: (() => void) | null = null;\n if (externalSignal) {\n const externalAbortHandler = () => {\n clearResponseTimeout();\n socketInstance.emit(socketEventNames.syncCancel, { cb: serverCancelId ?? syncCb });\n cleanupProgressListener?.();\n cleanupProgressListener = null;\n cleanupResponseListener?.();\n cleanupResponseListener = null;\n cleanupExternalAbort?.();\n cleanupExternalAbort = null;\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'request.aborted' },\n fallbackErrorCode: 'request.aborted',\n }));\n };\n externalSignal.addEventListener('abort', externalAbortHandler);\n cleanupExternalAbort = () => {\n externalSignal.removeEventListener('abort', externalAbortHandler);\n };\n }\n\n //? Arm the ack-timeout. `sync.requestTimeoutMs` (default 30000) may be\n //? `false` to disable. On expiry we tear down every listener and settle\n //? with a 504 envelope so the awaiting caller never hangs.\n const effectiveTimeoutMs = getProjectConfig().sync.requestTimeoutMs;\n if (typeof effectiveTimeoutMs === 'number' && effectiveTimeoutMs > 0) {\n responseTimeout = setTimeout(() => {\n responseTimeout = null;\n //? SYNC — on ack-timeout, tell the server to STOP the in-flight work\n //? before tearing down locally. Previously the timeout only cleaned up\n //? the client listeners + settled 504, leaving the `_server` handler\n //? (and any fanout/streaming) running to completion uncancelled. The\n //? `__cancelId` handshake has long since arrived by the timeout window\n //? (the server emits it immediately), so `serverCancelId` is the\n //? authoritative key here; `syncCb` is only the sub-RTT fallback.\n socketInstance.emit(socketEventNames.syncCancel, { cb: serverCancelId ?? syncCb });\n cleanupProgressListener?.();\n cleanupProgressListener = null;\n cleanupResponseListener?.();\n cleanupResponseListener = null;\n cleanupExternalAbort?.();\n cleanupExternalAbort = null;\n if (shouldLogDev()) {\n getLogger().error(`Sync ${sanitizedName} timed out`, undefined, { responseIndex: tempIndex });\n }\n resolve(normalizeSyncError({\n response: { status: 'error', errorCode: 'sync.requestTimeout', httpStatus: 504 },\n fallbackErrorCode: 'sync.requestTimeout',\n }));\n }, effectiveTimeoutMs);\n }\n\n const responseListener = (responseData: SyncAckResponse) => {\n clearResponseTimeout();\n cleanupProgressListener?.();\n cleanupProgressListener = null;\n cleanupResponseListener = null;\n cleanupExternalAbort?.();\n cleanupExternalAbort = null;\n\n if (responseData.status === \"error\") {\n const normalizedError = normalizeSyncError({\n response: responseData,\n fallbackErrorCode: 'sync.failedRequest',\n });\n\n if (shouldLogDev()) {\n getLogger().error(`Sync ${sanitizedName} failed`, undefined, { message: normalizedError.message });\n }\n if (shouldNotifyDev()) {\n notify.error({\n key: 'sync.failedRequest',\n params: [\n { key: 'name', value: sanitizedName },\n { key: 'message', value: normalizedError.message },\n ],\n });\n }\n resolve(normalizedError);\n return;\n }\n\n if (responseData.status !== 'success') {\n resolve(normalizeSyncError({\n response: responseData,\n fallbackErrorCode: 'sync.invalidServerResponse',\n }));\n return;\n }\n\n const result = responseData.result && typeof responseData.result === 'object'\n ? responseData.result\n : {};\n\n //? `RequestOutput.result` is a generic `Record<string, never>` shape\n //? that the runtime response can't structurally satisfy from the\n //? untyped `responseData.result` (server-side typing lives on the\n //? other side of the socket). The cast is the documented socket\n //? response boundary.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- socket response boundary\n resolve({\n status: 'success',\n message: typeof responseData.message === 'string' && responseData.message.trim().length > 0\n ? responseData.message\n : `sync ${sanitizedName} success`,\n result,\n } as RequestOutput);\n };\n\n socketInstance.once(responseEventName, responseListener);\n cleanupResponseListener = () => {\n socketInstance.off(responseEventName, responseListener);\n };\n };\n\n runRequest(activeSocket);\n })();\n });\n};\n\nexport function syncRequest<F extends SyncFullName, V extends VersionsForFullName<F>>(\n params: SyncRequestParamsWithOptions<F, V>\n): Promise<Prettify<SyncRequestResponseForFullName<F, V>>> {\n return syncRequestInternal(params);\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// useSyncEvents Hook - Type-Safe Event Registration\n// ═══════════════════════════════════════════════════════════════════════════════\n\n//? Hoisted to module scope so the emitted `.d.ts` for `useSyncEvents` can\n//? reference them. Local interface declarations inside an exported function\n//? trigger TS4025 (\"has or is using private name\") under `declaration: true`.\ninterface TypedCallbackParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\n clientOutput: ClientOutputForFullName<F, V>;\n serverOutput: ServerOutputForFullName<F, V>;\n}\n\ninterface UpsertParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\n name: F;\n version: V;\n callback: (params: TypedCallbackParams<F, V>) => void;\n}\n\ninterface UpsertStreamParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\n name: F;\n version: V;\n callback: SyncRouteStreamCallbackForFullName<F, V>;\n}\n\nexport const useSyncEvents = () => {\n const localRegistryRef = useRef<Map<string, SyncEventCallback>>(new Map());\n const localStreamRegistryRef = useRef<Map<string, SyncEventStreamCallback>>(new Map());\n\n const upsertSyncEventCallback = useCallback(<F extends SyncFullName, V extends VersionsForFullName<F>>(\n params: UpsertParams<F, V>\n ): (() => void) => {\n\n if (typeof params.version !== 'string') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid version for upsertSyncEventCallback\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidVersion' });\n }\n return noop;\n }\n\n if (typeof params.callback !== 'function') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid callback for upsertSyncEventCallback\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidCallback' });\n }\n return noop;\n }\n\n const routeName = params.name;\n const parsedRoute = parseServiceRouteName(routeName);\n if (parsedRoute.status === 'error') {\n if (shouldLogDev()) {\n getLogger().error(`Invalid name for upsertSyncEventCallback`, undefined, { routeName, reason: parsedRoute.reason });\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'routing.invalidServiceRouteName' });\n }\n return noop;\n }\n const sanitizedName = parsedRoute.normalizedRouteName;\n\n const routeVersion = params.version;\n const fullName = `sync/${sanitizedName}/${routeVersion}`;\n const callback: SyncEventCallback = ({ clientOutput, serverOutput }) => {\n params.callback({\n clientOutput: clientOutput as ClientOutputForFullName<F, V>,\n serverOutput: serverOutput as ServerOutputForFullName<F, V>,\n });\n };\n const callbacks = getCallbacksForRoute(fullName);\n\n const previousForRoute = localRegistryRef.current.get(fullName);\n if (previousForRoute) {\n syncEvents[fullName] = callbacks.filter((cb) => cb !== previousForRoute);\n }\n\n const nextCallbacks = getCallbacksForRoute(fullName);\n // Multiple components can intentionally subscribe to the same sync event.\n // Only warn when the exact same callback is registered twice.\n if (nextCallbacks.includes(callback)) {\n if (shouldLogDev()) {\n getLogger().warn(`[SyncEvents] Duplicate callback registration was ignored`, { fullName });\n }\n\n localRegistryRef.current.set(fullName, callback);\n\n return () => {\n const current = getCallbacksForRoute(fullName);\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\n\n if (localRegistryRef.current.get(fullName) === callback) {\n localRegistryRef.current.delete(fullName);\n }\n };\n }\n\n nextCallbacks.push(callback);\n syncEvents[fullName] = nextCallbacks;\n localRegistryRef.current.set(fullName, callback);\n\n return () => {\n const current = getCallbacksForRoute(fullName);\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\n\n if (localRegistryRef.current.get(fullName) === callback) {\n localRegistryRef.current.delete(fullName);\n }\n };\n }, []);\n\n const upsertSyncEventStreamCallback = useCallback(<F extends SyncFullName, V extends VersionsForFullName<F>>(\n params: UpsertStreamParams<F, V>\n ): (() => void) => {\n\n if (typeof params.version !== 'string') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid version for upsertSyncEventStreamCallback\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidVersion' });\n }\n return noop;\n }\n\n if (typeof params.callback !== 'function') {\n if (shouldLogDev()) {\n getLogger().error(\"Invalid callback for upsertSyncEventStreamCallback\");\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidCallback' });\n }\n return noop;\n }\n\n const routeName = params.name;\n const parsedRoute = parseServiceRouteName(routeName);\n if (parsedRoute.status === 'error') {\n if (shouldLogDev()) {\n getLogger().error(`Invalid name for upsertSyncEventStreamCallback`, undefined, { routeName, reason: parsedRoute.reason });\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'routing.invalidServiceRouteName' });\n }\n return noop;\n }\n const sanitizedName = parsedRoute.normalizedRouteName;\n\n const routeVersion = params.version;\n const fullName = `sync/${sanitizedName}/${routeVersion}`;\n //? Combined union: both `serverStream` (from `broadcastStream` /\n //? `streamTo`) and `clientStream` (from `_client_v{n}.ts`) flow through\n //? this callback. The type matches `SyncRouteStreamCallbackForFullName`.\n const typedCallback = params.callback as (params: {\n stream: SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>>;\n }) => void;\n const callback: SyncEventStreamCallback = ({ stream }) => {\n typedCallback({\n stream: stream as SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>>,\n });\n };\n const callbacks = getStreamCallbacksForRoute(fullName);\n\n const previousForRoute = localStreamRegistryRef.current.get(fullName);\n if (previousForRoute) {\n syncStreamEvents[fullName] = callbacks.filter((cb) => cb !== previousForRoute);\n }\n\n const nextCallbacks = getStreamCallbacksForRoute(fullName);\n if (nextCallbacks.includes(callback)) {\n if (shouldLogDev()) {\n getLogger().warn(`[SyncEvents] Duplicate stream callback registration was ignored`, { fullName });\n }\n\n localStreamRegistryRef.current.set(fullName, callback);\n\n return () => {\n const current = getStreamCallbacksForRoute(fullName);\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\n\n if (localStreamRegistryRef.current.get(fullName) === callback) {\n localStreamRegistryRef.current.delete(fullName);\n }\n };\n }\n\n nextCallbacks.push(callback);\n syncStreamEvents[fullName] = nextCallbacks;\n localStreamRegistryRef.current.set(fullName, callback);\n\n return () => {\n const current = getStreamCallbacksForRoute(fullName);\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\n\n if (localStreamRegistryRef.current.get(fullName) === callback) {\n localStreamRegistryRef.current.delete(fullName);\n }\n };\n }, []);\n\n useEffect(() => {\n const localRegistry = localRegistryRef.current;\n const localStreamRegistry = localStreamRegistryRef.current;\n\n return () => {\n for (const [fullName, callback] of localRegistry.entries()) {\n const current = getCallbacksForRoute(fullName);\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\n }\n\n for (const [fullName, callback] of localStreamRegistry.entries()) {\n const current = getStreamCallbacksForRoute(fullName);\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\n }\n\n localRegistry.clear();\n localStreamRegistry.clear();\n };\n }, []);\n\n return { upsertSyncEventCallback, upsertSyncEventStreamCallback };\n}\n\nexport const useSyncEventTrigger = () => {\n const triggerSyncEvent = useCallback((name: string, clientOutput: unknown = {}, serverOutput: unknown = {}) => {\n triggerSyncCallbacks(name, clientOutput, serverOutput);\n }, []);\n\n const triggerSyncStreamEvent = useCallback((name: string, stream: SyncRouteStreamEvent) => {\n triggerSyncStreamCallbacks(name, stream);\n }, []);\n\n return { triggerSyncEvent, triggerSyncStreamEvent }\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Sync Receive Bridge — attachSyncReceiver\n// ═══════════════════════════════════════════════════════════════════════════════\n\n//? Shape of the `sync` socket event the server emits to clients. Mirrors the\n//? server's emit payload; extra keys are stream chunk fields.\ninterface SyncEventPayload {\n cb?: string;\n clientOutput?: unknown;\n serverOutput?: unknown;\n message?: string;\n status?: 'success' | 'error' | 'stream';\n fullName?: string;\n errorCode?: string;\n errorParams?: { key: string; value: string | number | boolean }[];\n httpStatus?: number;\n [key: string]: unknown;\n}\n\nconst normalizeSyncRouteKey = (value: string): string => {\n const sanitized = value.replaceAll(/^\\/+|\\/+$/g, '');\n if (sanitized.length === 0) return '';\n if (sanitized.startsWith('sync/')) return sanitized;\n return `sync/${sanitized}`;\n};\n\nconst getSyncRouteKeys = ({ fullName, cb }: { fullName?: string; cb?: string }): string[] => {\n const keys = new Set<string>();\n if (typeof fullName === 'string') {\n const normalized = normalizeSyncRouteKey(fullName);\n if (normalized) keys.add(normalized);\n }\n if (typeof cb === 'string') {\n const normalized = normalizeSyncRouteKey(cb);\n if (normalized) keys.add(normalized);\n }\n return [...keys];\n};\n\n//? Per-socket idempotency guard — attaching twice to the same socket would\n//? double-dispatch every sync event.\nconst attachedSyncSockets = new WeakSet<Socket>();\n\n//? Wire the incoming `sync` socket listener that routes server fan-out into the\n//? registered `upsertSyncEventCallback` / `upsertSyncEventStreamCallback`\n//? subscribers. Previously this logic was inlined in the consumer's\n//? `socketInitializer.ts`; relocating it here makes `@luckystack/sync` own its\n//? own receive path, so a consumer only has to dynamic-import this and call it\n//? (the add-later sync wiring is then a single line, not a copy-paste block).\nexport const attachSyncReceiver = (socketInstance: Socket): void => {\n if (attachedSyncSockets.has(socketInstance)) return;\n attachedSyncSockets.add(socketInstance);\n\n socketInstance.on(socketEventNames.sync, (payload: SyncEventPayload) => {\n const { cb, clientOutput, serverOutput, message, status, fullName, errorCode, errorParams } = payload;\n if (shouldLogDev()) {\n getLogger().debug('Server Sync Response', { payload });\n }\n\n const routeKeys = getSyncRouteKeys({ fullName, cb });\n\n if (status === 'stream') {\n if (routeKeys.length === 0) return;\n\n //? SYNC-N7 — strip all reserved envelope keys from the forwarded stream\n //? payload so a consumer handler that accidentally emits `errorCode`,\n //? `errorParams`, `httpStatus`, or `message` in a stream chunk doesn't\n //? pollute the client callback's stream argument with error-envelope fields.\n //? `status`, `fullName`, `cb` were already stripped; the others are added here.\n const streamPayload = { ...payload };\n delete streamPayload.status;\n delete streamPayload.fullName;\n delete streamPayload.cb;\n delete streamPayload.errorCode;\n delete streamPayload.errorParams;\n delete streamPayload.httpStatus;\n delete streamPayload.message;\n\n if (shouldLogStream()) {\n getLogger().debug('Server Sync Stream', { routeKeys, streamPayload });\n }\n\n for (const routeKey of routeKeys) {\n triggerSyncStreamCallbacks(routeKey, streamPayload);\n }\n return;\n }\n\n if (status === 'error') {\n if (errorCode === 'sync.ignore' || message === 'sync.ignore') return;\n if (shouldNotifyDev()) {\n if (errorCode) {\n notify.error({ key: errorCode, params: errorParams });\n } else if (message) {\n notify.error({ key: message });\n }\n }\n return;\n }\n\n if (routeKeys.length === 0) {\n const errorMessage = `Sync response is missing fullName for cb '${cb ?? 'unknown'}'.`;\n if (shouldLogDev()) {\n getLogger().error(errorMessage);\n }\n if (shouldNotifyDev()) {\n notify.error({ key: 'sync.invalidRequestFormat' });\n }\n //? SYNC-10 — degrade to a logged warning instead of throwing. This runs\n //? inside the `socket.on(sync)` listener; a `throw` here is an uncaught\n //? exception on a server-controlled (malformed) frame that can kill other\n //? listeners bound to the same emit. The error is already logged + notified\n //? above, so `return` loses no information.\n return;\n }\n\n for (const routeKey of routeKeys) {\n triggerSyncCallbacks(routeKey, clientOutput, serverOutput);\n }\n });\n};\n\ntype SocketStatusSetter = Dispatch<\n SetStateAction<{\n self: statusContent;\n [userId: string]: statusContent;\n }>\n>;\n\nconst buildConnectHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => () => {\n if (shouldLogSocketStatus()) getLogger().info(\"Connected to server\");\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"CONNECTED\" } }));\n};\n\nconst buildDisconnectHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => () => {\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"DISCONNECTED\" } }));\n if (shouldLogSocketStatus()) getLogger().info(\"Disconnected, trying to reconnect...\");\n};\n\nconst buildReconnectAttemptHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => (attempt: number) => {\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"RECONNECTING\", reconnectAttempt: attempt } }));\n if (shouldLogSocketStatus()) getLogger().info(`Reconnecting attempt ${String(attempt)}...`);\n};\n\nconst buildUserAfkHandler = ({\n setSocketStatus, sessionRef,\n}: {\n setSocketStatus: SocketStatusSetter;\n sessionRef: RefObject<SessionLayout | null>;\n}) => ({ userId, endTime }: { userId: string; endTime?: number }) => {\n if (sessionRef.current !== null && userId === sessionRef.current.id) {\n setSocketStatus(prev => ({\n ...prev,\n self: { status: \"DISCONNECTED\", reconnectAttempt: undefined, endTime },\n }));\n } else {\n setSocketStatus(prev => ({\n ...prev,\n [userId]: { status: \"DISCONNECTED\", endTime },\n }));\n }\n};\n\nconst buildUserBackHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => ({ userId }: { userId: string }) => {\n if (shouldLogSocketStatus()) getLogger().debug(\"userBack\", { userId });\n setSocketStatus(prev => ({\n ...prev,\n [userId]: { status: \"CONNECTED\", endTime: undefined },\n }));\n};\n\nconst buildConnectErrorHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => (err: { message: string }) => {\n if (shouldLogSocketStatus()) getLogger().debug(\"connect_error\", { err });\n setSocketStatus(prev => ({\n ...prev,\n self: { ...prev.self, status: \"DISCONNECTED\", reconnectAttempt: undefined },\n }));\n if (shouldLogDev()) getLogger().error(`Connection error`, err);\n if (shouldNotifyDev()) notify.error({ key: 'common.connectionError' });\n};\n\n//? SYNC-N6 — `initSyncRequest` owns the canonical lifecycle listeners\n//? (`connect`, `disconnect`, `reconnect_attempt`, `userAfk`, `userBack`,\n//? `connect_error`). A consumer's `socketInitializer.ts` that also registers\n//? `disconnect`/`reconnect_attempt` on the same socket instance creates\n//? duplicate handlers — both fire on every event. Since `initSyncRequest` is\n//? idempotent (tears down the previous set before re-binding), consumer code\n//? should delegate lifecycle status updates here and avoid re-registering\n//? those events directly. Duplicate `console.log` in the consumer is harmless\n//? but produces double log lines.\nexport const initSyncRequest = async ({\n setSocketStatus,\n sessionRef\n}: {\n setSocketStatus: SocketStatusSetter;\n sessionRef: RefObject<SessionLayout | null> | null;\n}) => {\n\n if (!await waitForSocket()) { return; }\n if (!socket) { return; }\n if (!sessionRef) { return; }\n\n if (activeLifecycleHandlers) {\n socket.off(socketEventNames.connect, activeLifecycleHandlers.connect);\n socket.off(socketEventNames.disconnect, activeLifecycleHandlers.disconnect);\n socket.off(socketEventNames.reconnectAttempt, activeLifecycleHandlers.reconnectAttempt);\n socket.off(socketEventNames.userAfk, activeLifecycleHandlers.userAfk);\n socket.off(socketEventNames.userBack, activeLifecycleHandlers.userBack);\n socket.off(socketEventNames.connectError, activeLifecycleHandlers.connectError);\n }\n\n const connect = buildConnectHandler({ setSocketStatus });\n const disconnect = buildDisconnectHandler({ setSocketStatus });\n const reconnectAttempt = buildReconnectAttemptHandler({ setSocketStatus });\n const userAfk = buildUserAfkHandler({ setSocketStatus, sessionRef });\n const userBack = buildUserBackHandler({ setSocketStatus });\n const connectError = buildConnectErrorHandler({ setSocketStatus });\n\n activeLifecycleHandlers = {\n connect,\n disconnect,\n reconnectAttempt,\n userAfk,\n userBack,\n connectError,\n };\n\n socket.on(socketEventNames.connect, connect);\n socket.on(socketEventNames.disconnect, disconnect);\n socket.on(socketEventNames.reconnectAttempt, reconnectAttempt);\n socket.on(socketEventNames.userAfk, userAfk);\n socket.on(socketEventNames.userBack, userBack);\n socket.on(socketEventNames.connectError, connectError);\n\n}"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luckystack/sync",
3
- "version": "0.7.6",
3
+ "version": "0.8.0",
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.7.6",
64
- "@luckystack/error-tracking": "^0.7.6"
63
+ "@luckystack/core": "^0.8.0",
64
+ "@luckystack/error-tracking": "^0.8.0"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@prisma/client": "^6.19.0",