reflectdb 0.1.0 → 0.1.2

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.
Files changed (61) hide show
  1. package/README.md +500 -40
  2. package/dist/cjs/client/index.cjs +32 -17
  3. package/dist/cjs/client/index.d.cts +18 -3
  4. package/dist/cjs/client/storage/indexeddb.d.cts +1 -1
  5. package/dist/cjs/core/index.cjs +28 -28
  6. package/dist/cjs/core/index.d.cts +21 -9
  7. package/dist/cjs/react/index.cjs +48 -33
  8. package/dist/cjs/react/index.d.cts +23 -5
  9. package/dist/cjs/server/drizzle.cjs +2 -2
  10. package/dist/cjs/server/drizzle.d.cts +1 -1
  11. package/dist/cjs/server/ephemeral/index.cjs +180 -0
  12. package/dist/cjs/server/ephemeral/index.d.cts +102 -0
  13. package/dist/cjs/server/ephemeral/redis.cjs +226 -0
  14. package/dist/cjs/server/ephemeral/redis.d.cts +136 -0
  15. package/dist/cjs/server/index.cjs +238 -87
  16. package/dist/cjs/server/index.d.cts +196 -5
  17. package/dist/cjs/svelte/index.cjs +34 -19
  18. package/dist/cjs/svelte/index.d.cts +18 -3
  19. package/dist/cjs/transport/bun-ws.cjs +9 -9
  20. package/dist/cjs/transport/bun-ws.d.cts +1 -1
  21. package/dist/cjs/transport/polling.cjs +11 -11
  22. package/dist/cjs/transport/polling.d.cts +1 -1
  23. package/dist/cjs/transport/sse.cjs +11 -11
  24. package/dist/cjs/transport/sse.d.cts +1 -1
  25. package/dist/cjs/transport/ws.cjs +11 -11
  26. package/dist/cjs/transport/ws.d.cts +1 -1
  27. package/dist/cjs/vanilla/index.cjs +34 -19
  28. package/dist/cjs/vanilla/index.d.cts +18 -3
  29. package/dist/client/index.d.ts +18 -3
  30. package/dist/client/index.js +7 -7
  31. package/dist/client/storage/indexeddb.d.ts +1 -1
  32. package/dist/client/storage/indexeddb.js +1 -1
  33. package/dist/core/index.d.ts +21 -9
  34. package/dist/core/index.js +20 -20
  35. package/dist/react/index.d.ts +23 -5
  36. package/dist/react/index.js +23 -23
  37. package/dist/server/drizzle.d.ts +1 -1
  38. package/dist/server/drizzle.js +3 -3
  39. package/dist/server/ephemeral/index.d.ts +102 -0
  40. package/dist/server/ephemeral/index.js +9 -0
  41. package/dist/server/ephemeral/redis.d.ts +136 -0
  42. package/dist/server/ephemeral/redis.js +186 -0
  43. package/dist/server/index.d.ts +196 -5
  44. package/dist/server/index.js +184 -163
  45. package/dist/shared/{esm-ytrd3hbq.js → esm-8qbr4y0d.js} +18 -3
  46. package/dist/shared/{esm-wkwx6bd9.js → esm-ck88h30s.js} +10 -10
  47. package/dist/shared/esm-g0marxk7.js +134 -0
  48. package/dist/svelte/index.d.ts +18 -3
  49. package/dist/svelte/index.js +9 -9
  50. package/dist/transport/bun-ws.d.ts +1 -1
  51. package/dist/transport/bun-ws.js +1 -1
  52. package/dist/transport/polling.d.ts +1 -1
  53. package/dist/transport/polling.js +3 -3
  54. package/dist/transport/sse.d.ts +1 -1
  55. package/dist/transport/sse.js +3 -3
  56. package/dist/transport/ws.d.ts +1 -1
  57. package/dist/transport/ws.js +3 -3
  58. package/dist/vanilla/index.d.ts +18 -3
  59. package/dist/vanilla/index.js +9 -9
  60. package/package.json +66 -44
  61. /package/dist/shared/{esm-b7xs9cde.js → esm-k7kedp3y.js} +0 -0
@@ -41,16 +41,16 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
41
41
  // src/core/types.ts
42
42
  var exports_types = {};
43
43
  __export(exports_types, {
44
- reasonFromError: () => reasonFromError,
45
- isErrorReason: () => isErrorReason,
46
- TransportSendError: () => TransportSendError,
47
- TOMBSTONE_RETENTION_MS: () => TOMBSTONE_RETENTION_MS,
48
- SUPPORTED_PROTOCOL_VERSIONS: () => SUPPORTED_PROTOCOL_VERSIONS,
49
- SERVER_TOMBSTONE_RETENTION_MS: () => SERVER_TOMBSTONE_RETENTION_MS,
50
- PROTOCOL_VERSION: () => PROTOCOL_VERSION,
51
- MutationError: () => MutationError,
44
+ MAX_BATCH_SIZE: () => MAX_BATCH_SIZE,
52
45
  MAX_CLOCK_DRIFT_MS: () => MAX_CLOCK_DRIFT_MS,
53
- MAX_BATCH_SIZE: () => MAX_BATCH_SIZE
46
+ MutationError: () => MutationError,
47
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
48
+ SERVER_TOMBSTONE_RETENTION_MS: () => SERVER_TOMBSTONE_RETENTION_MS,
49
+ SUPPORTED_PROTOCOL_VERSIONS: () => SUPPORTED_PROTOCOL_VERSIONS,
50
+ TOMBSTONE_RETENTION_MS: () => TOMBSTONE_RETENTION_MS,
51
+ TransportSendError: () => TransportSendError,
52
+ isErrorReason: () => isErrorReason,
53
+ reasonFromError: () => reasonFromError
54
54
  });
55
55
  function isErrorReason(value) {
56
56
  return typeof value === "string" && VALID_ERROR_REASONS.has(value);
@@ -107,12 +107,12 @@ var init_types = __esm(() => {
107
107
  // src/client/index.ts
108
108
  var exports_client = {};
109
109
  __export(exports_client, {
110
- pushSafely: () => pushSafely,
111
- createSyncClient: () => createSyncClient,
112
- createOpCreator: () => createOpCreator,
113
- createMemoryStorage: () => createMemoryStorage,
110
+ ClientStore: () => ClientStore,
114
111
  SyncClient: () => SyncClient,
115
- ClientStore: () => ClientStore
112
+ createMemoryStorage: () => createMemoryStorage,
113
+ createOpCreator: () => createOpCreator,
114
+ createSyncClient: () => createSyncClient,
115
+ pushSafely: () => pushSafely
116
116
  });
117
117
  module.exports = __toCommonJS(exports_client);
118
118
 
@@ -414,7 +414,7 @@ class ClientStore {
414
414
  if (existing?.serverHlc && compareHlc(hlc, existing.serverHlc) <= 0) {
415
415
  return;
416
416
  }
417
- this.setRow(table, rowId, payload ?? {}, colClocks ?? { _row: hlc }, hlc);
417
+ this.setRow(table, rowId, this.withPk(table, rowId, payload ?? {}), colClocks ?? { _row: hlc }, hlc);
418
418
  return;
419
419
  }
420
420
  const incomingClocks = colClocks ?? {};
@@ -435,7 +435,13 @@ class ClientStore {
435
435
  }
436
436
  const newRowHlc = existing.serverHlc && compareHlc(existing.serverHlc, hlc) >= 0 ? existing.serverHlc : hlc;
437
437
  mergedClocks._row = newRowHlc;
438
- this.setRow(table, rowId, mergedData, mergedClocks, newRowHlc);
438
+ this.setRow(table, rowId, this.withPk(table, rowId, mergedData), mergedClocks, newRowHlc);
439
+ }
440
+ withPk(table, rowId, data) {
441
+ const pk = this.tableMeta.get(table)?.pk ?? "id";
442
+ if (data[pk] !== undefined)
443
+ return data;
444
+ return { ...data, [pk]: rowId };
439
445
  }
440
446
  revertOp(opId, serverRow) {
441
447
  const pending = this.pendingOps.find((p) => p.op.id === opId);
@@ -480,10 +486,19 @@ class ClientStore {
480
486
  delete payload[field];
481
487
  }
482
488
  }
489
+ if (op.op === "insert" && payload) {
490
+ payload = this.withPk(op.table, op.rowId, payload);
491
+ }
483
492
  if (op.op === "delete") {
484
493
  this.setRow(op.table, op.rowId, null, { _row: op.hlc }, op.hlc);
485
494
  } else if (op.op === "insert") {
486
- this.setRow(op.table, op.rowId, payload ?? {}, { _row: op.hlc }, op.hlc);
495
+ const existing = this.getRow(op.table, op.rowId);
496
+ if (existing?.data) {
497
+ const merged = { ...existing.data, ...payload };
498
+ this.setRow(op.table, op.rowId, merged, existing.colClocks, existing.serverHlc);
499
+ } else {
500
+ this.setRow(op.table, op.rowId, payload ?? {}, { _row: op.hlc }, op.hlc);
501
+ }
487
502
  } else {
488
503
  const existing = this.getRow(op.table, op.rowId);
489
504
  if (existing?.data) {
@@ -285,6 +285,17 @@ declare class ClientStore {
285
285
  clearTable(table: string): void;
286
286
  applySnapshot(table: string, rows: Record<string, unknown>[], colClocks: Record<string, Record<string, string>>, append?: boolean, pk?: string): void;
287
287
  applyDelta(table: string, op: string, rowId: string, payload: Record<string, unknown> | null, hlc: string, colClocks?: Record<string, string>): void;
288
+ /**
289
+ * Guarantee a materialized row carries its own primary key.
290
+ *
291
+ * A delta's payload is not always a whole row: eager broadcasts forward the
292
+ * writer's payload verbatim, and the typed API omits the pk from what a
293
+ * client may write — so an eagerly-broadcast insert describes a row with no
294
+ * id in it. Rows are addressed by `rowId` everywhere in the protocol, so the
295
+ * key is never in doubt; only the materialized object was missing it, and
296
+ * `rows.map(r => r.id)` came back undefined.
297
+ */
298
+ private withPk;
288
299
  revertOp(opId: string, serverRow: Record<string, unknown> | null | undefined): void;
289
300
  setTableMeta(meta: Record<string, {
290
301
  serverSet: string[];
@@ -475,11 +486,15 @@ interface DrizzleTableLike {
475
486
  /**
476
487
  * Value supplied per serverSet field in the schema's object form.
477
488
  * Either a static value or a function that receives the request context.
489
+ *
490
+ * The static arm is enumerated rather than written as `unknown`: a union with
491
+ * `unknown` collapses to `unknown`, which strips the contextual type from the
492
+ * callback arm and makes every `(ctx) => …` an implicit `any` under `strict`.
478
493
  */
479
- type ServerSetSchemaValue = unknown | ((ctx: {
494
+ type ServerSetSchemaValue = ((ctx: {
480
495
  auth: unknown;
481
496
  params: unknown;
482
- }) => unknown);
497
+ }) => unknown) | string | number | bigint | boolean | symbol | null | undefined | Date | readonly unknown[] | Record<string, unknown>;
483
498
  /**
484
499
  * Schema-side `serverSet` declaration. Two shapes:
485
500
  * - `string[]` — keys only; values are supplied at `implement(...)` time.
@@ -626,4 +641,4 @@ interface TypedSyncClient<TQueries extends SyncQueryMap> {
626
641
  subscribeEphemeral(key: string, listener: (event: EphemeralEvent) => void): () => void;
627
642
  }
628
643
  declare function createSyncClient<TQueries extends SyncQueryMap>(config: TypedSyncClientConfig<TQueries>): TypedSyncClient<TQueries>;
629
- export { pushSafely, createSyncClient, createOpCreator, createMemoryStorage, TypedSyncClientConfig, TypedSyncClient, SyncOptions, SyncClientState, SyncClientConfig, SyncClient, PendingOp, OpCreator, LocalRow, ClientStore, ClientStorageAdapter };
644
+ export { ClientStorageAdapter, ClientStore, LocalRow, OpCreator, PendingOp, SyncClient, SyncClientConfig, SyncClientState, SyncOptions, TypedSyncClient, TypedSyncClientConfig, createMemoryStorage, createOpCreator, createSyncClient, pushSafely };
@@ -82,4 +82,4 @@ declare function createIndexedDBStorage(config: {
82
82
  configVersion: number;
83
83
  }, storage: ClientStorageAdapter) => void | Promise<void>;
84
84
  }): ClientStorageAdapter;
85
- export { createIndexedDBStorage, IndexedDBMigrationContext };
85
+ export { IndexedDBMigrationContext, createIndexedDBStorage };
@@ -41,16 +41,16 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
41
41
  // src/core/types.ts
42
42
  var exports_types = {};
43
43
  __export(exports_types, {
44
- reasonFromError: () => reasonFromError,
45
- isErrorReason: () => isErrorReason,
46
- TransportSendError: () => TransportSendError,
47
- TOMBSTONE_RETENTION_MS: () => TOMBSTONE_RETENTION_MS,
48
- SUPPORTED_PROTOCOL_VERSIONS: () => SUPPORTED_PROTOCOL_VERSIONS,
49
- SERVER_TOMBSTONE_RETENTION_MS: () => SERVER_TOMBSTONE_RETENTION_MS,
50
- PROTOCOL_VERSION: () => PROTOCOL_VERSION,
51
- MutationError: () => MutationError,
44
+ MAX_BATCH_SIZE: () => MAX_BATCH_SIZE,
52
45
  MAX_CLOCK_DRIFT_MS: () => MAX_CLOCK_DRIFT_MS,
53
- MAX_BATCH_SIZE: () => MAX_BATCH_SIZE
46
+ MutationError: () => MutationError,
47
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
48
+ SERVER_TOMBSTONE_RETENTION_MS: () => SERVER_TOMBSTONE_RETENTION_MS,
49
+ SUPPORTED_PROTOCOL_VERSIONS: () => SUPPORTED_PROTOCOL_VERSIONS,
50
+ TOMBSTONE_RETENTION_MS: () => TOMBSTONE_RETENTION_MS,
51
+ TransportSendError: () => TransportSendError,
52
+ isErrorReason: () => isErrorReason,
53
+ reasonFromError: () => reasonFromError
54
54
  });
55
55
  function isErrorReason(value) {
56
56
  return typeof value === "string" && VALID_ERROR_REASONS.has(value);
@@ -107,26 +107,26 @@ var init_types = __esm(() => {
107
107
  // src/core/index.ts
108
108
  var exports_core = {};
109
109
  __export(exports_core, {
110
- view: () => view,
111
- unpackHlc: () => unpackHlc,
112
- t: () => t,
113
- sendHlc: () => sendHlc,
114
- receiveHlc: () => receiveHlc,
115
- reasonFromError: () => reasonFromError,
116
- presence: () => presence,
117
- packHlc: () => packHlc,
118
- isErrorReason: () => isErrorReason,
119
- defineSyncQueries: () => defineSyncQueries,
120
- createHlc: () => createHlc,
121
- compareHlc: () => compareHlc,
122
- TransportSendError: () => TransportSendError,
123
- TOMBSTONE_RETENTION_MS: () => TOMBSTONE_RETENTION_MS,
124
- SUPPORTED_PROTOCOL_VERSIONS: () => SUPPORTED_PROTOCOL_VERSIONS,
125
- SERVER_TOMBSTONE_RETENTION_MS: () => SERVER_TOMBSTONE_RETENTION_MS,
126
- PROTOCOL_VERSION: () => PROTOCOL_VERSION,
127
- MutationError: () => MutationError,
110
+ MAX_BATCH_SIZE: () => MAX_BATCH_SIZE,
128
111
  MAX_CLOCK_DRIFT_MS: () => MAX_CLOCK_DRIFT_MS,
129
- MAX_BATCH_SIZE: () => MAX_BATCH_SIZE
112
+ MutationError: () => MutationError,
113
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
114
+ SERVER_TOMBSTONE_RETENTION_MS: () => SERVER_TOMBSTONE_RETENTION_MS,
115
+ SUPPORTED_PROTOCOL_VERSIONS: () => SUPPORTED_PROTOCOL_VERSIONS,
116
+ TOMBSTONE_RETENTION_MS: () => TOMBSTONE_RETENTION_MS,
117
+ TransportSendError: () => TransportSendError,
118
+ compareHlc: () => compareHlc,
119
+ createHlc: () => createHlc,
120
+ defineSyncQueries: () => defineSyncQueries,
121
+ isErrorReason: () => isErrorReason,
122
+ packHlc: () => packHlc,
123
+ presence: () => presence,
124
+ reasonFromError: () => reasonFromError,
125
+ receiveHlc: () => receiveHlc,
126
+ sendHlc: () => sendHlc,
127
+ t: () => t,
128
+ unpackHlc: () => unpackHlc,
129
+ view: () => view
130
130
  });
131
131
  module.exports = __toCommonJS(exports_core);
132
132
 
@@ -321,11 +321,15 @@ interface DrizzleTableLike {
321
321
  /**
322
322
  * Value supplied per serverSet field in the schema's object form.
323
323
  * Either a static value or a function that receives the request context.
324
+ *
325
+ * The static arm is enumerated rather than written as `unknown`: a union with
326
+ * `unknown` collapses to `unknown`, which strips the contextual type from the
327
+ * callback arm and makes every `(ctx) => …` an implicit `any` under `strict`.
324
328
  */
325
- type ServerSetSchemaValue = unknown | ((ctx: {
329
+ type ServerSetSchemaValue = ((ctx: {
326
330
  auth: unknown;
327
331
  params: unknown;
328
- }) => unknown);
332
+ }) => unknown) | string | number | bigint | boolean | symbol | null | undefined | Date | readonly unknown[] | Record<string, unknown>;
329
333
  /**
330
334
  * Schema-side `serverSet` declaration. Two shapes:
331
335
  * - `string[]` — keys only; values are supplied at `implement(...)` time.
@@ -370,8 +374,8 @@ declare function view<
370
374
  deps?: string[];
371
375
  tables?: string[];
372
376
  }): SyncViewDef & {
373
- row?: TRow;
374
- params?: TParams;
377
+ row: TRow;
378
+ params: TParams;
375
379
  };
376
380
  /**
377
381
  * Typed ephemeral channel. Sugar over the runtime `useEphemeral` hook —
@@ -392,8 +396,8 @@ declare function presence<
392
396
  params?: TParams;
393
397
  ttlMs?: number;
394
398
  }): SyncPresenceDef & {
395
- state?: TState;
396
- params?: TParams;
399
+ state: TState;
400
+ params: TParams;
397
401
  };
398
402
  type SyncQueryEntry = SyncQueryDef | SyncViewDef | SyncPresenceDef;
399
403
  type SyncQueryMap = Record<string, SyncQueryEntry>;
@@ -403,12 +407,20 @@ type ColumnOf<TDef> = TDef extends {
403
407
  } ? keyof TDef["table"]["$inferSelect"] & string : TDef extends {
404
408
  row: infer R extends Record<string, unknown>;
405
409
  } ? keyof R & string : string;
406
- /** Constrain serverSet and readonly to valid column names. Accepts either array or object form for serverSet. View/presence entries don't have these fields, so the constraint is empty. */
410
+ /**
411
+ * Constrain serverSet and readonly to valid column names. Accepts either array
412
+ * or object form for serverSet.
413
+ *
414
+ * View/presence entries carry neither field, so they get `unknown` — the
415
+ * identity of intersection. `Record<string, never>` reads like "no constraint"
416
+ * but means "an object with no properties", which every view and presence entry
417
+ * fails, making both helpers unusable inside `defineSyncQueries`.
418
+ */
407
419
  type FieldsConstraint<TDef> = TDef extends {
408
420
  __view: true;
409
421
  } | {
410
422
  __presence: true;
411
- } ? Record<string, never> : {
423
+ } ? unknown : {
412
424
  serverSet?: readonly ColumnOf<TDef>[] | Readonly<Partial<Record<ColumnOf<TDef>, ServerSetSchemaValue>>>;
413
425
  readonly?: readonly ColumnOf<TDef>[];
414
426
  };
@@ -470,4 +482,4 @@ type InferWritableRow<
470
482
  } ? never : Omit<InferRow<TQueries, K>, (TQueries[K] extends {
471
483
  readonly: readonly (infer R extends string)[];
472
484
  } ? R : never) | InferServerSetKeys<TQueries[K]> | PkOf<TQueries[K]>>;
473
- export { view, unpackHlc, t, sendHlc, receiveHlc, reasonFromError, presence, packHlc, isErrorReason, defineSyncQueries, createHlc, compareHlc, UnsyncMessage, TransportSendError, TableMeta, TOMBSTONE_RETENTION_MS, SyncViewDef, SyncQueryMap, SyncQueryEntry, SyncQueryDef, SyncPresenceDef, SyncOp, SyncMessage, SyncError, SyncDeclareMessage, SnapshotMessage, ShapeConfig, ShapeChangedMessage, ServerTransport, ServerMessage, SUPPORTED_PROTOCOL_VERSIONS, SERVER_TOMBSTONE_RETENTION_MS, ResumeRejectedMessage, ResumeMessage, ResumeCompleteMessage, RequiresParams, RejectMessage, ReauthMessage, RateLimitConfig, PROTOCOL_VERSION, OpsMessage, OpType, OpStatus, MutationError, MAX_CLOCK_DRIFT_MS, MAX_BATCH_SIZE, LoadMoreMessage, InferWritableRow, InferState, InferRow, InferParams, HelloRejectMessage, HelloMessage, HelloAckMessage, HLC, ErrorReason, EphemeralMessage, EphemeralEvent, DrizzleTableLike, DisconnectMessage, Dialect, DeltaMessage, CustomConflictPolicy, CountChangedMessage, ConflictResolver, ConflictPolicy, CompactionConfig, ClientTransport, ClientOp, ClientMessage, BootstrapMessage, BootstrapCompleteMessage, AuthMessage, AuthContext, AckMessage };
485
+ export { AckMessage, AuthContext, AuthMessage, BootstrapCompleteMessage, BootstrapMessage, ClientMessage, ClientOp, ClientTransport, CompactionConfig, ConflictPolicy, ConflictResolver, CountChangedMessage, CustomConflictPolicy, DeltaMessage, Dialect, DisconnectMessage, DrizzleTableLike, EphemeralEvent, EphemeralMessage, ErrorReason, HLC, HelloAckMessage, HelloMessage, HelloRejectMessage, InferParams, InferRow, InferState, InferWritableRow, LoadMoreMessage, MAX_BATCH_SIZE, MAX_CLOCK_DRIFT_MS, MutationError, OpStatus, OpType, OpsMessage, PROTOCOL_VERSION, RateLimitConfig, ReauthMessage, RejectMessage, RequiresParams, ResumeCompleteMessage, ResumeMessage, ResumeRejectedMessage, SERVER_TOMBSTONE_RETENTION_MS, SUPPORTED_PROTOCOL_VERSIONS, ServerMessage, ServerTransport, ShapeChangedMessage, ShapeConfig, SnapshotMessage, SyncDeclareMessage, SyncError, SyncMessage, SyncOp, SyncPresenceDef, SyncQueryDef, SyncQueryEntry, SyncQueryMap, SyncViewDef, TOMBSTONE_RETENTION_MS, TableMeta, TransportSendError, UnsyncMessage, compareHlc, createHlc, defineSyncQueries, isErrorReason, packHlc, presence, reasonFromError, receiveHlc, sendHlc, t, unpackHlc, view };
@@ -41,16 +41,16 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
41
41
  // src/core/types.ts
42
42
  var exports_types = {};
43
43
  __export(exports_types, {
44
- reasonFromError: () => reasonFromError,
45
- isErrorReason: () => isErrorReason,
46
- TransportSendError: () => TransportSendError,
47
- TOMBSTONE_RETENTION_MS: () => TOMBSTONE_RETENTION_MS,
48
- SUPPORTED_PROTOCOL_VERSIONS: () => SUPPORTED_PROTOCOL_VERSIONS,
49
- SERVER_TOMBSTONE_RETENTION_MS: () => SERVER_TOMBSTONE_RETENTION_MS,
50
- PROTOCOL_VERSION: () => PROTOCOL_VERSION,
51
- MutationError: () => MutationError,
44
+ MAX_BATCH_SIZE: () => MAX_BATCH_SIZE,
52
45
  MAX_CLOCK_DRIFT_MS: () => MAX_CLOCK_DRIFT_MS,
53
- MAX_BATCH_SIZE: () => MAX_BATCH_SIZE
46
+ MutationError: () => MutationError,
47
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
48
+ SERVER_TOMBSTONE_RETENTION_MS: () => SERVER_TOMBSTONE_RETENTION_MS,
49
+ SUPPORTED_PROTOCOL_VERSIONS: () => SUPPORTED_PROTOCOL_VERSIONS,
50
+ TOMBSTONE_RETENTION_MS: () => TOMBSTONE_RETENTION_MS,
51
+ TransportSendError: () => TransportSendError,
52
+ isErrorReason: () => isErrorReason,
53
+ reasonFromError: () => reasonFromError
54
54
  });
55
55
  function isErrorReason(value) {
56
56
  return typeof value === "string" && VALID_ERROR_REASONS.has(value);
@@ -107,18 +107,18 @@ var init_types = __esm(() => {
107
107
  // src/react/index.ts
108
108
  var exports_react = {};
109
109
  __export(exports_react, {
110
- useTotalCount: () => useTotalCount,
111
- useSyncStatus: () => useSyncStatus,
112
- useSyncClient: () => useSyncClient,
113
- useSync: () => useSync,
114
- useRow: () => useRow,
115
- usePendingCount: () => usePendingCount,
116
- useLoadMore: () => useLoadMore,
117
- useEphemeral: () => useEphemeral,
118
- derivePresenceKey: () => derivePresenceKey,
119
- createSyncReact: () => createSyncReact,
110
+ SyncContext: () => SyncContext,
120
111
  SyncProvider: () => SyncProvider,
121
- SyncContext: () => SyncContext
112
+ createSyncReact: () => createSyncReact,
113
+ derivePresenceKey: () => derivePresenceKey,
114
+ useEphemeral: () => useEphemeral,
115
+ useLoadMore: () => useLoadMore,
116
+ usePendingCount: () => usePendingCount,
117
+ useRow: () => useRow,
118
+ useSync: () => useSync,
119
+ useSyncClient: () => useSyncClient,
120
+ useSyncStatus: () => useSyncStatus,
121
+ useTotalCount: () => useTotalCount
122
122
  });
123
123
  module.exports = __toCommonJS(exports_react);
124
124
 
@@ -427,7 +427,7 @@ class ClientStore {
427
427
  if (existing?.serverHlc && compareHlc(hlc, existing.serverHlc) <= 0) {
428
428
  return;
429
429
  }
430
- this.setRow(table, rowId, payload ?? {}, colClocks ?? { _row: hlc }, hlc);
430
+ this.setRow(table, rowId, this.withPk(table, rowId, payload ?? {}), colClocks ?? { _row: hlc }, hlc);
431
431
  return;
432
432
  }
433
433
  const incomingClocks = colClocks ?? {};
@@ -448,7 +448,13 @@ class ClientStore {
448
448
  }
449
449
  const newRowHlc = existing.serverHlc && compareHlc(existing.serverHlc, hlc) >= 0 ? existing.serverHlc : hlc;
450
450
  mergedClocks._row = newRowHlc;
451
- this.setRow(table, rowId, mergedData, mergedClocks, newRowHlc);
451
+ this.setRow(table, rowId, this.withPk(table, rowId, mergedData), mergedClocks, newRowHlc);
452
+ }
453
+ withPk(table, rowId, data) {
454
+ const pk = this.tableMeta.get(table)?.pk ?? "id";
455
+ if (data[pk] !== undefined)
456
+ return data;
457
+ return { ...data, [pk]: rowId };
452
458
  }
453
459
  revertOp(opId, serverRow) {
454
460
  const pending = this.pendingOps.find((p) => p.op.id === opId);
@@ -493,10 +499,19 @@ class ClientStore {
493
499
  delete payload[field];
494
500
  }
495
501
  }
502
+ if (op.op === "insert" && payload) {
503
+ payload = this.withPk(op.table, op.rowId, payload);
504
+ }
496
505
  if (op.op === "delete") {
497
506
  this.setRow(op.table, op.rowId, null, { _row: op.hlc }, op.hlc);
498
507
  } else if (op.op === "insert") {
499
- this.setRow(op.table, op.rowId, payload ?? {}, { _row: op.hlc }, op.hlc);
508
+ const existing = this.getRow(op.table, op.rowId);
509
+ if (existing?.data) {
510
+ const merged = { ...existing.data, ...payload };
511
+ this.setRow(op.table, op.rowId, merged, existing.colClocks, existing.serverHlc);
512
+ } else {
513
+ this.setRow(op.table, op.rowId, payload ?? {}, { _row: op.hlc }, op.hlc);
514
+ }
500
515
  } else {
501
516
  const existing = this.getRow(op.table, op.rowId);
502
517
  if (existing?.data) {
@@ -1176,7 +1191,7 @@ function pushSafely(client) {
1176
1191
  }
1177
1192
 
1178
1193
  // src/react/context.tsx
1179
- var jsx_dev_runtime = require("react/jsx-dev-runtime");
1194
+ var jsx_runtime = require("react/jsx-runtime");
1180
1195
  var SyncContext = import_react.createContext(null);
1181
1196
  function createBrowserWsTransport(url) {
1182
1197
  let ws = null;
@@ -1292,10 +1307,10 @@ function SyncProvider({
1292
1307
  }, [url, clientId, storage, managedTables]);
1293
1308
  if (!ctx)
1294
1309
  return null;
1295
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(SyncContext.Provider, {
1310
+ return /* @__PURE__ */ jsx_runtime.jsx(SyncContext.Provider, {
1296
1311
  value: ctx,
1297
1312
  children
1298
- }, undefined, false, undefined, this);
1313
+ });
1299
1314
  }
1300
1315
  function useSyncClient() {
1301
1316
  const ctx = import_react.useContext(SyncContext);
@@ -1361,21 +1376,21 @@ function useEphemeral(config) {
1361
1376
  const timersRef = import_react2.useRef(new Map);
1362
1377
  import_react2.useEffect(() => {
1363
1378
  const unsub = client.subscribeEphemeral(config.key, (event) => {
1364
- setEvents((prev) => ({ ...prev, [event.userId]: event.data }));
1379
+ setEvents((prev) => ({ ...prev, [event.clientId]: event.data }));
1365
1380
  if (config.ttlMs) {
1366
- const existing = timersRef.current.get(event.userId);
1381
+ const existing = timersRef.current.get(event.clientId);
1367
1382
  if (existing) {
1368
1383
  clearTimeout(existing);
1369
1384
  }
1370
1385
  const timer = setTimeout(() => {
1371
1386
  setEvents((prev) => {
1372
1387
  const next = { ...prev };
1373
- delete next[event.userId];
1388
+ delete next[event.clientId];
1374
1389
  return next;
1375
1390
  });
1376
- timersRef.current.delete(event.userId);
1391
+ timersRef.current.delete(event.clientId);
1377
1392
  }, config.ttlMs);
1378
- timersRef.current.set(event.userId, timer);
1393
+ timersRef.current.set(event.clientId, timer);
1379
1394
  }
1380
1395
  });
1381
1396
  return () => {
@@ -1492,8 +1507,8 @@ function createSyncReact(queries) {
1492
1507
  userId,
1493
1508
  ttlMs
1494
1509
  });
1495
- const peers = import_react3.useMemo(() => Object.entries(events).map(([uid, state]) => ({
1496
- userId: uid,
1510
+ const peers = import_react3.useMemo(() => Object.entries(events).map(([peerId, state]) => ({
1511
+ userId: peerId,
1497
1512
  state
1498
1513
  })), [events]);
1499
1514
  return { peers, set: broadcast };
@@ -268,6 +268,17 @@ declare class ClientStore {
268
268
  clearTable(table: string): void;
269
269
  applySnapshot(table: string, rows: Record<string, unknown>[], colClocks: Record<string, Record<string, string>>, append?: boolean, pk?: string): void;
270
270
  applyDelta(table: string, op: string, rowId: string, payload: Record<string, unknown> | null, hlc: string, colClocks?: Record<string, string>): void;
271
+ /**
272
+ * Guarantee a materialized row carries its own primary key.
273
+ *
274
+ * A delta's payload is not always a whole row: eager broadcasts forward the
275
+ * writer's payload verbatim, and the typed API omits the pk from what a
276
+ * client may write — so an eagerly-broadcast insert describes a row with no
277
+ * id in it. Rows are addressed by `rowId` everywhere in the protocol, so the
278
+ * key is never in doubt; only the materialized object was missing it, and
279
+ * `rows.map(r => r.id)` came back undefined.
280
+ */
281
+ private withPk;
271
282
  revertOp(opId: string, serverRow: Record<string, unknown> | null | undefined): void;
272
283
  setTableMeta(meta: Record<string, {
273
284
  serverSet: string[];
@@ -531,6 +542,9 @@ declare function usePendingCount2(): number;
531
542
  *
532
543
  * ```tsx
533
544
  * function CursorTracker({ userId }: { userId: string }) {
545
+ * `events` is keyed by the peer's `clientId`, so two tabs from one account
546
+ * appear as two peers.
547
+ *
534
548
  * const { events, broadcast } = useEphemeral({
535
549
  * key: "cursor",
536
550
  * userId,
@@ -546,8 +560,8 @@ declare function usePendingCount2(): number;
546
560
  *
547
561
  * return (
548
562
  * <div>
549
- * {Object.entries(events).map(([uid, data]) => (
550
- * <div key={uid}>User {uid}: {JSON.stringify(data)}</div>
563
+ * {Object.entries(events).map(([peerId, data]) => (
564
+ * <div key={peerId}>Peer {peerId}: {JSON.stringify(data)}</div>
551
565
  * ))}
552
566
  * </div>
553
567
  * );
@@ -579,11 +593,15 @@ interface DrizzleTableLike {
579
593
  /**
580
594
  * Value supplied per serverSet field in the schema's object form.
581
595
  * Either a static value or a function that receives the request context.
596
+ *
597
+ * The static arm is enumerated rather than written as `unknown`: a union with
598
+ * `unknown` collapses to `unknown`, which strips the contextual type from the
599
+ * callback arm and makes every `(ctx) => …` an implicit `any` under `strict`.
582
600
  */
583
- type ServerSetSchemaValue = unknown | ((ctx: {
601
+ type ServerSetSchemaValue = ((ctx: {
584
602
  auth: unknown;
585
603
  params: unknown;
586
- }) => unknown);
604
+ }) => unknown) | string | number | bigint | boolean | symbol | null | undefined | Date | readonly unknown[] | Record<string, unknown>;
587
605
  /**
588
606
  * Schema-side `serverSet` declaration. Two shapes:
589
607
  * - `string[]` — keys only; values are supplied at `implement(...)` time.
@@ -745,4 +763,4 @@ type UseSyncOptions<
745
763
  window?: number;
746
764
  };
747
765
  declare function createSyncReact<TQueries extends SyncQueryMap>(queries?: TQueries): SyncReactHooks<TQueries>;
748
- export { useTotalCount2 as useTotalCount, useSyncStatus2 as useSyncStatus, useSyncClient, useSync2 as useSync, useRow2 as useRow, usePendingCount2 as usePendingCount, useLoadMore2 as useLoadMore, useEphemeral2 as useEphemeral, derivePresenceKey, createSyncReact, SyncProviderProps, SyncProvider2 as SyncProvider, SyncContext };
766
+ export { SyncContext, SyncProvider2 as SyncProvider, SyncProviderProps, createSyncReact, derivePresenceKey, useEphemeral2 as useEphemeral, useLoadMore2 as useLoadMore, usePendingCount2 as usePendingCount, useRow2 as useRow, useSync2 as useSync, useSyncClient, useSyncStatus2 as useSyncStatus, useTotalCount2 as useTotalCount };
@@ -41,8 +41,8 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
41
41
  // src/server/drizzle.ts
42
42
  var exports_drizzle = {};
43
43
  __export(exports_drizzle, {
44
- drizzleTxAtomic: () => drizzleTxAtomic,
45
- drizzleTable: () => drizzleTable
44
+ drizzleTable: () => drizzleTable,
45
+ drizzleTxAtomic: () => drizzleTxAtomic
46
46
  });
47
47
  module.exports = __toCommonJS(exports_drizzle);
48
48
  var import_node_module2 = require("node:module");
@@ -230,4 +230,4 @@ declare function drizzleTable<
230
230
  * has a single source of truth.
231
231
  */
232
232
  declare const drizzleTxAtomic: TxAtomicAdapter;
233
- export { drizzleTxAtomic, drizzleTable, DrizzleTableOpts };
233
+ export { DrizzleTableOpts, drizzleTable, drizzleTxAtomic };