@syncular/server 0.0.1-98 → 0.0.2-126

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 (106) hide show
  1. package/README.md +25 -0
  2. package/dist/blobs/adapters/filesystem.d.ts +31 -0
  3. package/dist/blobs/adapters/filesystem.d.ts.map +1 -0
  4. package/dist/blobs/adapters/filesystem.js +140 -0
  5. package/dist/blobs/adapters/filesystem.js.map +1 -0
  6. package/dist/blobs/adapters/s3.d.ts +3 -2
  7. package/dist/blobs/adapters/s3.d.ts.map +1 -1
  8. package/dist/blobs/adapters/s3.js +49 -0
  9. package/dist/blobs/adapters/s3.js.map +1 -1
  10. package/dist/blobs/index.d.ts +1 -0
  11. package/dist/blobs/index.d.ts.map +1 -1
  12. package/dist/blobs/index.js +1 -0
  13. package/dist/blobs/index.js.map +1 -1
  14. package/dist/compaction.d.ts +1 -1
  15. package/dist/compaction.js +1 -1
  16. package/dist/{shapes → handlers}/create-handler.d.ts +18 -5
  17. package/dist/handlers/create-handler.d.ts.map +1 -0
  18. package/dist/{shapes → handlers}/create-handler.js +54 -17
  19. package/dist/handlers/create-handler.js.map +1 -0
  20. package/dist/handlers/index.d.ts.map +1 -0
  21. package/dist/handlers/index.js.map +1 -0
  22. package/dist/handlers/registry.d.ts.map +1 -0
  23. package/dist/handlers/registry.js.map +1 -0
  24. package/dist/{shapes → handlers}/types.d.ts +7 -7
  25. package/dist/{shapes → handlers}/types.d.ts.map +1 -1
  26. package/dist/{shapes → handlers}/types.js.map +1 -1
  27. package/dist/helpers/conflict.d.ts +1 -1
  28. package/dist/helpers/conflict.d.ts.map +1 -1
  29. package/dist/helpers/emitted-change.d.ts +1 -1
  30. package/dist/helpers/emitted-change.d.ts.map +1 -1
  31. package/dist/index.d.ts +2 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/notify.d.ts +47 -0
  36. package/dist/notify.d.ts.map +1 -0
  37. package/dist/notify.js +85 -0
  38. package/dist/notify.js.map +1 -0
  39. package/dist/proxy/handler.d.ts +1 -1
  40. package/dist/proxy/handler.d.ts.map +1 -1
  41. package/dist/proxy/handler.js +7 -7
  42. package/dist/proxy/handler.js.map +1 -1
  43. package/dist/proxy/oplog.d.ts +1 -1
  44. package/dist/proxy/oplog.d.ts.map +1 -1
  45. package/dist/proxy/oplog.js +6 -6
  46. package/dist/proxy/oplog.js.map +1 -1
  47. package/dist/pull.d.ts +2 -2
  48. package/dist/pull.d.ts.map +1 -1
  49. package/dist/pull.js +48 -9
  50. package/dist/pull.js.map +1 -1
  51. package/dist/push.d.ts +2 -2
  52. package/dist/push.d.ts.map +1 -1
  53. package/dist/push.js +1 -1
  54. package/dist/push.js.map +1 -1
  55. package/dist/snapshot-chunks/db-metadata.d.ts.map +1 -1
  56. package/dist/snapshot-chunks/db-metadata.js +14 -3
  57. package/dist/snapshot-chunks/db-metadata.js.map +1 -1
  58. package/dist/snapshot-chunks/index.d.ts +0 -1
  59. package/dist/snapshot-chunks/index.d.ts.map +1 -1
  60. package/dist/snapshot-chunks/index.js +0 -1
  61. package/dist/snapshot-chunks/index.js.map +1 -1
  62. package/dist/subscriptions/resolve.d.ts +6 -6
  63. package/dist/subscriptions/resolve.d.ts.map +1 -1
  64. package/dist/subscriptions/resolve.js +53 -14
  65. package/dist/subscriptions/resolve.js.map +1 -1
  66. package/package.json +2 -2
  67. package/src/blobs/adapters/filesystem.test.ts +132 -0
  68. package/src/blobs/adapters/filesystem.ts +189 -0
  69. package/src/blobs/adapters/s3.test.ts +522 -0
  70. package/src/blobs/adapters/s3.ts +55 -2
  71. package/src/blobs/index.ts +1 -0
  72. package/src/compaction.ts +1 -1
  73. package/src/{shapes → handlers}/create-handler.ts +111 -21
  74. package/src/{shapes → handlers}/types.ts +10 -7
  75. package/src/helpers/conflict.ts +1 -1
  76. package/src/helpers/emitted-change.ts +1 -1
  77. package/src/index.ts +2 -1
  78. package/src/notify.test.ts +516 -0
  79. package/src/notify.ts +131 -0
  80. package/src/proxy/handler.test.ts +3 -3
  81. package/src/proxy/handler.ts +8 -8
  82. package/src/proxy/oplog.ts +7 -7
  83. package/src/pull.ts +66 -12
  84. package/src/push.ts +3 -3
  85. package/src/snapshot-chunks/db-metadata.test.ts +69 -0
  86. package/src/snapshot-chunks/db-metadata.ts +14 -3
  87. package/src/snapshot-chunks/index.ts +0 -1
  88. package/src/subscriptions/resolve.ts +73 -18
  89. package/dist/shapes/create-handler.d.ts.map +0 -1
  90. package/dist/shapes/create-handler.js.map +0 -1
  91. package/dist/shapes/index.d.ts.map +0 -1
  92. package/dist/shapes/index.js.map +0 -1
  93. package/dist/shapes/registry.d.ts.map +0 -1
  94. package/dist/shapes/registry.js.map +0 -1
  95. package/dist/snapshot-chunks/adapters/s3.d.ts +0 -74
  96. package/dist/snapshot-chunks/adapters/s3.d.ts.map +0 -1
  97. package/dist/snapshot-chunks/adapters/s3.js +0 -50
  98. package/dist/snapshot-chunks/adapters/s3.js.map +0 -1
  99. package/src/snapshot-chunks/adapters/s3.ts +0 -68
  100. /package/dist/{shapes → handlers}/index.d.ts +0 -0
  101. /package/dist/{shapes → handlers}/index.js +0 -0
  102. /package/dist/{shapes → handlers}/registry.d.ts +0 -0
  103. /package/dist/{shapes → handlers}/registry.js +0 -0
  104. /package/dist/{shapes → handlers}/types.js +0 -0
  105. /package/src/{shapes → handlers}/index.ts +0 -0
  106. /package/src/{shapes → handlers}/registry.ts +0 -0
@@ -5,11 +5,20 @@
5
5
  import type {
6
6
  ScopePattern,
7
7
  ScopeValues,
8
+ ScopeValuesFromPatterns,
8
9
  ScopeDefinition as SimpleScopeDefinition,
9
10
  StoredScopes,
10
11
  SyncOperation,
11
12
  } from '@syncular/core';
12
- import { extractScopeVars, normalizeScopes } from '@syncular/core';
13
+ import {
14
+ applyCodecsFromDbRow,
15
+ applyCodecsToDbRow,
16
+ type ColumnCodecDialect,
17
+ type ColumnCodecSource,
18
+ extractScopeVars,
19
+ normalizeScopes,
20
+ toTableColumnCodecs,
21
+ } from '@syncular/core';
13
22
  import type {
14
23
  DeleteQueryBuilder,
15
24
  DeleteResult,
@@ -69,6 +78,8 @@ export interface CreateServerHandlerOptions<
69
78
  ServerDB extends SyncCoreDb,
70
79
  ClientDB,
71
80
  TableName extends keyof ServerDB & keyof ClientDB & string,
81
+ ScopeDefs extends
82
+ readonly SimpleScopeDefinition[] = readonly SimpleScopeDefinition[],
72
83
  > {
73
84
  /** Table name in the database */
74
85
  table: TableName;
@@ -88,7 +99,7 @@ export interface CreateServerHandlerOptions<
88
99
  * ]
89
100
  * ```
90
101
  */
91
- scopes: SimpleScopeDefinition[];
102
+ scopes: ScopeDefs;
92
103
 
93
104
  /** Primary key column name (default: 'id') */
94
105
  primaryKey?: string;
@@ -112,7 +123,9 @@ export interface CreateServerHandlerOptions<
112
123
  * project_id: await getProjectsForUser(ctx.db, ctx.actorId),
113
124
  * })
114
125
  */
115
- resolveScopes: (ctx: ServerContext<ServerDB>) => Promise<ScopeValues>;
126
+ resolveScopes: (
127
+ ctx: ServerContext<ServerDB>
128
+ ) => Promise<ScopeValuesFromPatterns<ScopeDefs>>;
116
129
 
117
130
  /**
118
131
  * Transform inbound row from client to server format.
@@ -130,6 +143,20 @@ export interface CreateServerHandlerOptions<
130
143
  row: Selectable<ServerDB[TableName]>
131
144
  ) => ClientDB[TableName];
132
145
 
146
+ /**
147
+ * Optional column codec resolver.
148
+ * Receives `{ table, column, sqlType?, dialect? }` and returns a codec.
149
+ * Only used by default snapshot/apply paths when the corresponding
150
+ * transform hook is not provided.
151
+ */
152
+ columnCodecs?: ColumnCodecSource;
153
+
154
+ /**
155
+ * Dialect used for codec dialect overrides.
156
+ * Default: 'sqlite'
157
+ */
158
+ codecDialect?: ColumnCodecDialect;
159
+
133
160
  /**
134
161
  * Authorize an operation before applying.
135
162
  * Return true to allow, or an error object to reject.
@@ -186,8 +213,10 @@ export function createServerHandler<
186
213
  ServerDB extends SyncCoreDb,
187
214
  ClientDB,
188
215
  TableName extends keyof ServerDB & keyof ClientDB & string,
216
+ ScopeDefs extends
217
+ readonly SimpleScopeDefinition[] = readonly SimpleScopeDefinition[],
189
218
  >(
190
- options: CreateServerHandlerOptions<ServerDB, ClientDB, TableName>
219
+ options: CreateServerHandlerOptions<ServerDB, ClientDB, TableName, ScopeDefs>
191
220
  ): ServerTableHandler<ServerDB> {
192
221
  type OverloadParameters<T> = T extends (...args: infer A) => unknown
193
222
  ? A
@@ -210,9 +239,25 @@ export function createServerHandler<
210
239
  resolveScopes,
211
240
  transformInbound,
212
241
  transformOutbound,
242
+ columnCodecs,
243
+ codecDialect = 'sqlite',
213
244
  authorize,
214
245
  extractScopes: customExtractScopes,
215
246
  } = options;
247
+ const codecCache = new Map<string, ReturnType<typeof toTableColumnCodecs>>();
248
+ const resolveTableCodecs = (row: Record<string, unknown>) => {
249
+ if (!columnCodecs) return {};
250
+ const columns = Object.keys(row);
251
+ if (columns.length === 0) return {};
252
+ const cacheKey = columns.slice().sort().join('\u0000');
253
+ const cached = codecCache.get(cacheKey);
254
+ if (cached) return cached;
255
+ const resolved = toTableColumnCodecs(table, columnCodecs, columns, {
256
+ dialect: codecDialect,
257
+ });
258
+ codecCache.set(cacheKey, resolved);
259
+ return resolved;
260
+ };
216
261
 
217
262
  // Normalize scopes to pattern map and extract patterns/columns
218
263
  const scopeColumnMap = normalizeScopes(scopeDefs);
@@ -252,6 +297,53 @@ export function createServerHandler<
252
297
 
253
298
  const extractScopesImpl = customExtractScopes ?? defaultExtractScopes;
254
299
 
300
+ const resolveScopesImpl = async (
301
+ ctx: ServerContext<ServerDB>
302
+ ): Promise<ScopeValues> => {
303
+ const resolved = await resolveScopes(ctx);
304
+ const normalized: ScopeValues = {};
305
+ for (const [scopeKey, scopeValue] of Object.entries(resolved)) {
306
+ if (typeof scopeValue === 'string' || Array.isArray(scopeValue)) {
307
+ normalized[scopeKey] = scopeValue;
308
+ }
309
+ }
310
+ return normalized;
311
+ };
312
+
313
+ const applyOutboundTransform = (
314
+ row: Selectable<ServerDB[TableName]>
315
+ ): ClientDB[TableName] => {
316
+ if (transformOutbound) {
317
+ return transformOutbound(row);
318
+ }
319
+
320
+ const recordRow = row as Record<string, unknown>;
321
+ const transformed = applyCodecsFromDbRow(
322
+ recordRow,
323
+ resolveTableCodecs(recordRow),
324
+ codecDialect
325
+ );
326
+ return transformed as ClientDB[TableName];
327
+ };
328
+
329
+ const applyInboundTransform = (
330
+ row: Record<string, unknown>,
331
+ schemaVersion: number | undefined
332
+ ): Updateable<ServerDB[TableName]> => {
333
+ if (transformInbound) {
334
+ return transformInbound(row as ClientDB[TableName], {
335
+ schemaVersion,
336
+ });
337
+ }
338
+
339
+ const transformed = applyCodecsToDbRow(
340
+ row,
341
+ resolveTableCodecs(row),
342
+ codecDialect
343
+ );
344
+ return transformed as Updateable<ServerDB[TableName]>;
345
+ };
346
+
255
347
  // Default snapshot implementation
256
348
  const defaultSnapshot = async (
257
349
  ctx: ServerSnapshotContext<ServerDB>,
@@ -308,11 +400,9 @@ export function createServerHandler<
308
400
  : null;
309
401
 
310
402
  // Transform outbound if provided
311
- const outputRows = transformOutbound
312
- ? pageRows.map((r) =>
313
- transformOutbound(r as Selectable<ServerDB[TableName]>)
314
- )
315
- : pageRows;
403
+ const outputRows = pageRows.map((r) =>
404
+ applyOutboundTransform(r as Selectable<ServerDB[TableName]>)
405
+ );
316
406
 
317
407
  return {
318
408
  rows: outputRows,
@@ -408,11 +498,11 @@ export function createServerHandler<
408
498
 
409
499
  // Handle upsert
410
500
  const rawPayload = op.payload ?? {};
411
- const payload = transformInbound
412
- ? transformInbound(rawPayload as ClientDB[TableName], {
413
- schemaVersion: ctx.schemaVersion,
414
- })
415
- : (rawPayload as Updateable<ServerDB[TableName]>);
501
+ const payloadRecord =
502
+ rawPayload !== null && typeof rawPayload === 'object'
503
+ ? (rawPayload as Record<string, unknown>)
504
+ : {};
505
+ const payload = applyInboundTransform(payloadRecord, ctx.schemaVersion);
416
506
 
417
507
  // Check for existing row
418
508
  const existing = await (
@@ -441,9 +531,9 @@ export function createServerHandler<
441
531
  status: 'conflict',
442
532
  message: `Version conflict: server=${existingVersion}, base=${op.base_version}`,
443
533
  server_version: existingVersion,
444
- server_row: transformOutbound
445
- ? transformOutbound(existing as Selectable<ServerDB[TableName]>)
446
- : existing,
534
+ server_row: applyOutboundTransform(
535
+ existing as Selectable<ServerDB[TableName]>
536
+ ),
447
537
  },
448
538
  emittedChanges: [],
449
539
  };
@@ -558,9 +648,9 @@ export function createServerHandler<
558
648
  const scopes = extractScopesImpl(updatedRow);
559
649
 
560
650
  // Transform outbound for emitted change
561
- const rowJson = transformOutbound
562
- ? transformOutbound(updated as Selectable<ServerDB[TableName]>)
563
- : updated;
651
+ const rowJson = applyOutboundTransform(
652
+ updated as Selectable<ServerDB[TableName]>
653
+ );
564
654
 
565
655
  const emitted: EmittedChange = {
566
656
  table,
@@ -582,7 +672,7 @@ export function createServerHandler<
582
672
  scopePatterns,
583
673
  dependsOn,
584
674
  snapshotChunkTtlMs,
585
- resolveScopes,
675
+ resolveScopes: resolveScopesImpl,
586
676
  extractScopes: extractScopesImpl,
587
677
  snapshot: options.snapshot ?? defaultSnapshot,
588
678
  applyOperation: options.applyOperation ?? defaultApplyOperation,
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  ScopePattern,
3
3
  ScopeValues,
4
+ ScopeValuesForKeys,
4
5
  StoredScopes,
5
6
  SyncOp,
6
7
  SyncOperation,
@@ -50,12 +51,14 @@ export interface ServerContext<DB extends SyncCoreDb = SyncCoreDb> {
50
51
  /**
51
52
  * Context passed to snapshot method.
52
53
  */
53
- export interface ServerSnapshotContext<DB extends SyncCoreDb = SyncCoreDb>
54
- extends ServerContext<DB> {
54
+ export interface ServerSnapshotContext<
55
+ DB extends SyncCoreDb = SyncCoreDb,
56
+ ScopeKeys extends string = string,
57
+ > extends ServerContext<DB> {
55
58
  /** Database executor for the snapshot */
56
59
  db: DbExecutor<DB>;
57
60
  /** Effective scope values for this subscription */
58
- scopeValues: ScopeValues;
61
+ scopeValues: ScopeValuesForKeys<ScopeKeys>;
59
62
  /** Pagination cursor (row_id for keyset pagination) */
60
63
  cursor: string | null;
61
64
  /** Max rows to return */
@@ -120,9 +123,9 @@ interface ServerScopeConfig {
120
123
  }
121
124
 
122
125
  /**
123
- * Server shape options - configuration for a table's sync behavior.
126
+ * Server handler options - configuration for a table's sync behavior.
124
127
  */
125
- export interface ServerShapeOptions<
128
+ export interface ServerHandlerOptions<
126
129
  DB extends SyncCoreDb = SyncCoreDb,
127
130
  Scopes extends Record<ScopePattern, Record<string, string>> = Record<
128
131
  ScopePattern,
@@ -132,7 +135,7 @@ export interface ServerShapeOptions<
132
135
  Params extends ZodSchema = ZodSchema,
133
136
  > {
134
137
  /**
135
- * Scope patterns this shape uses.
138
+ * Scope patterns this handler uses.
136
139
  * Array of pattern keys from SharedScopes.
137
140
  */
138
141
  scopes: (keyof Scopes)[];
@@ -225,7 +228,7 @@ export interface ServerTableHandler<DB extends SyncCoreDb = SyncCoreDb> {
225
228
  /** Table name */
226
229
  table: string;
227
230
 
228
- /** Scope patterns used by this shape */
231
+ /** Scope patterns used by this handler */
229
232
  scopePatterns: ScopePattern[];
230
233
 
231
234
  /**
@@ -4,7 +4,7 @@
4
4
  * Helper for building conflict results in server table handlers.
5
5
  */
6
6
 
7
- import type { ApplyOperationResult } from '../shapes/types';
7
+ import type { ApplyOperationResult } from '../handlers/types';
8
8
 
9
9
  export interface BuildConflictResultArgs {
10
10
  /** Index of the operation in the batch */
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { StoredScopes } from '@syncular/core';
8
- import type { EmittedChange } from '../shapes/types';
8
+ import type { EmittedChange } from '../handlers/types';
9
9
 
10
10
  export interface CreateEmittedChangeArgs {
11
11
  /** Table name */
package/src/index.ts CHANGED
@@ -13,15 +13,16 @@ export * from './blobs';
13
13
  export * from './clients';
14
14
  export * from './compaction';
15
15
  export * from './dialect';
16
+ export * from './handlers';
16
17
  export * from './helpers';
17
18
  export * from './migrate';
19
+ export * from './notify';
18
20
  export * from './proxy';
19
21
  export * from './prune';
20
22
  export * from './pull';
21
23
  export * from './push';
22
24
  export * from './realtime';
23
25
  export * from './schema';
24
- export * from './shapes';
25
26
  export * from './snapshot-chunks';
26
27
  export type { SnapshotChunkStorage } from './snapshot-chunks/types';
27
28
  export * from './stats';