@syncular/server 0.0.1-100

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 (225) hide show
  1. package/dist/blobs/adapters/database.d.ts +83 -0
  2. package/dist/blobs/adapters/database.d.ts.map +1 -0
  3. package/dist/blobs/adapters/database.js +202 -0
  4. package/dist/blobs/adapters/database.js.map +1 -0
  5. package/dist/blobs/adapters/s3.d.ts +82 -0
  6. package/dist/blobs/adapters/s3.d.ts.map +1 -0
  7. package/dist/blobs/adapters/s3.js +170 -0
  8. package/dist/blobs/adapters/s3.js.map +1 -0
  9. package/dist/blobs/index.d.ts +9 -0
  10. package/dist/blobs/index.d.ts.map +1 -0
  11. package/dist/blobs/index.js +9 -0
  12. package/dist/blobs/index.js.map +1 -0
  13. package/dist/blobs/manager.d.ts +195 -0
  14. package/dist/blobs/manager.d.ts.map +1 -0
  15. package/dist/blobs/manager.js +440 -0
  16. package/dist/blobs/manager.js.map +1 -0
  17. package/dist/blobs/migrate.d.ts +27 -0
  18. package/dist/blobs/migrate.d.ts.map +1 -0
  19. package/dist/blobs/migrate.js +119 -0
  20. package/dist/blobs/migrate.js.map +1 -0
  21. package/dist/blobs/types.d.ts +54 -0
  22. package/dist/blobs/types.d.ts.map +1 -0
  23. package/dist/blobs/types.js +5 -0
  24. package/dist/blobs/types.js.map +1 -0
  25. package/dist/clients.d.ts +14 -0
  26. package/dist/clients.d.ts.map +1 -0
  27. package/dist/clients.js +7 -0
  28. package/dist/clients.js.map +1 -0
  29. package/dist/compaction.d.ts +27 -0
  30. package/dist/compaction.d.ts.map +1 -0
  31. package/dist/compaction.js +49 -0
  32. package/dist/compaction.js.map +1 -0
  33. package/dist/dialect/base.d.ts +83 -0
  34. package/dist/dialect/base.d.ts.map +1 -0
  35. package/dist/dialect/base.js +144 -0
  36. package/dist/dialect/base.js.map +1 -0
  37. package/dist/dialect/helpers.d.ts +10 -0
  38. package/dist/dialect/helpers.d.ts.map +1 -0
  39. package/dist/dialect/helpers.js +59 -0
  40. package/dist/dialect/helpers.js.map +1 -0
  41. package/dist/dialect/index.d.ts +7 -0
  42. package/dist/dialect/index.d.ts.map +1 -0
  43. package/dist/dialect/index.js +7 -0
  44. package/dist/dialect/index.js.map +1 -0
  45. package/dist/dialect/types.d.ts +149 -0
  46. package/dist/dialect/types.d.ts.map +1 -0
  47. package/dist/dialect/types.js +8 -0
  48. package/dist/dialect/types.js.map +1 -0
  49. package/dist/helpers/conflict.d.ts +52 -0
  50. package/dist/helpers/conflict.d.ts.map +1 -0
  51. package/dist/helpers/conflict.js +49 -0
  52. package/dist/helpers/conflict.js.map +1 -0
  53. package/dist/helpers/emitted-change.d.ts +56 -0
  54. package/dist/helpers/emitted-change.d.ts.map +1 -0
  55. package/dist/helpers/emitted-change.js +46 -0
  56. package/dist/helpers/emitted-change.js.map +1 -0
  57. package/dist/helpers/index.d.ts +10 -0
  58. package/dist/helpers/index.d.ts.map +1 -0
  59. package/dist/helpers/index.js +10 -0
  60. package/dist/helpers/index.js.map +1 -0
  61. package/dist/helpers/paginate.d.ts +49 -0
  62. package/dist/helpers/paginate.d.ts.map +1 -0
  63. package/dist/helpers/paginate.js +54 -0
  64. package/dist/helpers/paginate.js.map +1 -0
  65. package/dist/helpers/scope-strings.d.ts +74 -0
  66. package/dist/helpers/scope-strings.d.ts.map +1 -0
  67. package/dist/helpers/scope-strings.js +82 -0
  68. package/dist/helpers/scope-strings.js.map +1 -0
  69. package/dist/index.d.ts +28 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +27 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/migrate.d.ts +14 -0
  74. package/dist/migrate.d.ts.map +1 -0
  75. package/dist/migrate.js +13 -0
  76. package/dist/migrate.js.map +1 -0
  77. package/dist/proxy/handler.d.ts +42 -0
  78. package/dist/proxy/handler.d.ts.map +1 -0
  79. package/dist/proxy/handler.js +102 -0
  80. package/dist/proxy/handler.js.map +1 -0
  81. package/dist/proxy/index.d.ts +9 -0
  82. package/dist/proxy/index.d.ts.map +1 -0
  83. package/dist/proxy/index.js +14 -0
  84. package/dist/proxy/index.js.map +1 -0
  85. package/dist/proxy/mutation-detector.d.ts +35 -0
  86. package/dist/proxy/mutation-detector.d.ts.map +1 -0
  87. package/dist/proxy/mutation-detector.js +246 -0
  88. package/dist/proxy/mutation-detector.js.map +1 -0
  89. package/dist/proxy/oplog.d.ts +30 -0
  90. package/dist/proxy/oplog.d.ts.map +1 -0
  91. package/dist/proxy/oplog.js +110 -0
  92. package/dist/proxy/oplog.js.map +1 -0
  93. package/dist/proxy/registry.d.ts +35 -0
  94. package/dist/proxy/registry.d.ts.map +1 -0
  95. package/dist/proxy/registry.js +49 -0
  96. package/dist/proxy/registry.js.map +1 -0
  97. package/dist/proxy/types.d.ts +44 -0
  98. package/dist/proxy/types.d.ts.map +1 -0
  99. package/dist/proxy/types.js +7 -0
  100. package/dist/proxy/types.js.map +1 -0
  101. package/dist/prune.d.ts +37 -0
  102. package/dist/prune.d.ts.map +1 -0
  103. package/dist/prune.js +112 -0
  104. package/dist/prune.js.map +1 -0
  105. package/dist/pull.d.ts +31 -0
  106. package/dist/pull.d.ts.map +1 -0
  107. package/dist/pull.js +608 -0
  108. package/dist/pull.js.map +1 -0
  109. package/dist/push.d.ts +33 -0
  110. package/dist/push.d.ts.map +1 -0
  111. package/dist/push.js +412 -0
  112. package/dist/push.js.map +1 -0
  113. package/dist/realtime/in-memory.d.ts +13 -0
  114. package/dist/realtime/in-memory.d.ts.map +1 -0
  115. package/dist/realtime/in-memory.js +28 -0
  116. package/dist/realtime/in-memory.js.map +1 -0
  117. package/dist/realtime/index.d.ts +3 -0
  118. package/dist/realtime/index.d.ts.map +1 -0
  119. package/dist/realtime/index.js +2 -0
  120. package/dist/realtime/index.js.map +1 -0
  121. package/dist/realtime/types.d.ts +50 -0
  122. package/dist/realtime/types.d.ts.map +1 -0
  123. package/dist/realtime/types.js +7 -0
  124. package/dist/realtime/types.js.map +1 -0
  125. package/dist/schema.d.ts +164 -0
  126. package/dist/schema.d.ts.map +1 -0
  127. package/dist/schema.js +10 -0
  128. package/dist/schema.js.map +1 -0
  129. package/dist/shapes/create-handler.d.ts +119 -0
  130. package/dist/shapes/create-handler.d.ts.map +1 -0
  131. package/dist/shapes/create-handler.js +327 -0
  132. package/dist/shapes/create-handler.js.map +1 -0
  133. package/dist/shapes/index.d.ts +4 -0
  134. package/dist/shapes/index.d.ts.map +1 -0
  135. package/dist/shapes/index.js +4 -0
  136. package/dist/shapes/index.js.map +1 -0
  137. package/dist/shapes/registry.d.ts +20 -0
  138. package/dist/shapes/registry.d.ts.map +1 -0
  139. package/dist/shapes/registry.js +88 -0
  140. package/dist/shapes/registry.js.map +1 -0
  141. package/dist/shapes/types.d.ts +204 -0
  142. package/dist/shapes/types.d.ts.map +1 -0
  143. package/dist/shapes/types.js +2 -0
  144. package/dist/shapes/types.js.map +1 -0
  145. package/dist/snapshot-chunks/adapters/s3.d.ts +74 -0
  146. package/dist/snapshot-chunks/adapters/s3.d.ts.map +1 -0
  147. package/dist/snapshot-chunks/adapters/s3.js +50 -0
  148. package/dist/snapshot-chunks/adapters/s3.js.map +1 -0
  149. package/dist/snapshot-chunks/db-metadata.d.ts +38 -0
  150. package/dist/snapshot-chunks/db-metadata.d.ts.map +1 -0
  151. package/dist/snapshot-chunks/db-metadata.js +324 -0
  152. package/dist/snapshot-chunks/db-metadata.js.map +1 -0
  153. package/dist/snapshot-chunks/index.d.ts +9 -0
  154. package/dist/snapshot-chunks/index.d.ts.map +1 -0
  155. package/dist/snapshot-chunks/index.js +9 -0
  156. package/dist/snapshot-chunks/index.js.map +1 -0
  157. package/dist/snapshot-chunks/types.d.ts +78 -0
  158. package/dist/snapshot-chunks/types.d.ts.map +1 -0
  159. package/dist/snapshot-chunks/types.js +8 -0
  160. package/dist/snapshot-chunks/types.js.map +1 -0
  161. package/dist/snapshot-chunks.d.ts +60 -0
  162. package/dist/snapshot-chunks.d.ts.map +1 -0
  163. package/dist/snapshot-chunks.js +223 -0
  164. package/dist/snapshot-chunks.js.map +1 -0
  165. package/dist/stats.d.ts +19 -0
  166. package/dist/stats.d.ts.map +1 -0
  167. package/dist/stats.js +57 -0
  168. package/dist/stats.js.map +1 -0
  169. package/dist/subscriptions/index.d.ts +2 -0
  170. package/dist/subscriptions/index.d.ts.map +1 -0
  171. package/dist/subscriptions/index.js +2 -0
  172. package/dist/subscriptions/index.js.map +1 -0
  173. package/dist/subscriptions/resolve.d.ts +35 -0
  174. package/dist/subscriptions/resolve.d.ts.map +1 -0
  175. package/dist/subscriptions/resolve.js +134 -0
  176. package/dist/subscriptions/resolve.js.map +1 -0
  177. package/package.json +80 -0
  178. package/src/blobs/adapters/database.test.ts +67 -0
  179. package/src/blobs/adapters/database.ts +315 -0
  180. package/src/blobs/adapters/s3.ts +271 -0
  181. package/src/blobs/index.ts +9 -0
  182. package/src/blobs/manager.ts +600 -0
  183. package/src/blobs/migrate.ts +150 -0
  184. package/src/blobs/types.ts +70 -0
  185. package/src/clients.ts +21 -0
  186. package/src/compaction.ts +77 -0
  187. package/src/dialect/base.ts +292 -0
  188. package/src/dialect/helpers.ts +61 -0
  189. package/src/dialect/index.ts +7 -0
  190. package/src/dialect/types.ts +197 -0
  191. package/src/helpers/conflict.ts +64 -0
  192. package/src/helpers/emitted-change.ts +69 -0
  193. package/src/helpers/index.ts +10 -0
  194. package/src/helpers/paginate.ts +82 -0
  195. package/src/helpers/scope-strings.ts +101 -0
  196. package/src/index.ts +28 -0
  197. package/src/migrate.ts +20 -0
  198. package/src/proxy/handler.test.ts +120 -0
  199. package/src/proxy/handler.ts +159 -0
  200. package/src/proxy/index.ts +18 -0
  201. package/src/proxy/mutation-detector.test.ts +71 -0
  202. package/src/proxy/mutation-detector.ts +281 -0
  203. package/src/proxy/oplog.ts +146 -0
  204. package/src/proxy/registry.ts +56 -0
  205. package/src/proxy/types.ts +46 -0
  206. package/src/prune.ts +200 -0
  207. package/src/pull.ts +858 -0
  208. package/src/push.ts +583 -0
  209. package/src/realtime/in-memory.ts +33 -0
  210. package/src/realtime/index.ts +5 -0
  211. package/src/realtime/types.ts +55 -0
  212. package/src/schema.ts +172 -0
  213. package/src/shapes/create-handler.ts +590 -0
  214. package/src/shapes/index.ts +3 -0
  215. package/src/shapes/registry.ts +109 -0
  216. package/src/shapes/types.ts +267 -0
  217. package/src/snapshot-chunks/adapters/s3.ts +68 -0
  218. package/src/snapshot-chunks/db-metadata.test.ts +100 -0
  219. package/src/snapshot-chunks/db-metadata.ts +466 -0
  220. package/src/snapshot-chunks/index.ts +9 -0
  221. package/src/snapshot-chunks/types.ts +103 -0
  222. package/src/snapshot-chunks.ts +329 -0
  223. package/src/stats.ts +104 -0
  224. package/src/subscriptions/index.ts +1 -0
  225. package/src/subscriptions/resolve.ts +185 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @syncular/server - Scope string utilities
3
+ *
4
+ * Helpers for creating and parsing scope strings.
5
+ * Scope strings identify partitions of data for sync subscriptions.
6
+ */
7
+ /**
8
+ * Create a scope string from a prefix and values.
9
+ *
10
+ * Scope strings use colon separators: `prefix:value1:value2`
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // Simple scope string
15
+ * createScopeKey('user', 'alice')
16
+ * // => 'user:alice'
17
+ *
18
+ * // Multi-value scope string
19
+ * createScopeKey('user', 'alice', 'project', 'proj-1')
20
+ * // => 'user:alice:project:proj-1'
21
+ * ```
22
+ */
23
+ export function createScopeKey(prefix, ...values) {
24
+ return [prefix, ...values].join(':');
25
+ }
26
+ /**
27
+ * Parse a scope string into its prefix and values.
28
+ *
29
+ * Returns null if the key is invalid or doesn't match the expected prefix.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // Parse any scope string
34
+ * parseScopeKey('user:alice')
35
+ * // => { prefix: 'user', values: ['alice'] }
36
+ *
37
+ * // Parse with expected prefix
38
+ * parseScopeKey('user:alice', 'user')
39
+ * // => { prefix: 'user', values: ['alice'] }
40
+ *
41
+ * // Returns null if prefix doesn't match
42
+ * parseScopeKey('user:alice', 'project')
43
+ * // => null
44
+ *
45
+ * // Multi-value scope string
46
+ * parseScopeKey('user:alice:project:proj-1')
47
+ * // => { prefix: 'user', values: ['alice', 'project', 'proj-1'] }
48
+ * ```
49
+ */
50
+ export function parseScopeKey(key, expectedPrefix) {
51
+ const parts = key.split(':');
52
+ if (parts.length < 1)
53
+ return null;
54
+ const [prefix, ...values] = parts;
55
+ if (!prefix)
56
+ return null;
57
+ // Check expected prefix if provided
58
+ if (expectedPrefix && prefix !== expectedPrefix)
59
+ return null;
60
+ return { prefix, values };
61
+ }
62
+ /**
63
+ * Extract a specific value from a scope string by index.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // Get first value after prefix
68
+ * getScopeKeyValue('user:alice:project:proj-1', 0)
69
+ * // => 'alice'
70
+ *
71
+ * // Get second value
72
+ * getScopeKeyValue('user:alice:project:proj-1', 2)
73
+ * // => 'proj-1'
74
+ * ```
75
+ */
76
+ export function getScopeKeyValue(key, valueIndex) {
77
+ const parsed = parseScopeKey(key);
78
+ if (!parsed)
79
+ return null;
80
+ return parsed.values[valueIndex] ?? null;
81
+ }
82
+ //# sourceMappingURL=scope-strings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-strings.js","sourceRoot":"","sources":["../../src/helpers/scope-strings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,GAAG,MAAgB,EAAU;IAC1E,OAAO,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAAA,CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAW,EACX,cAAuB,EACA;IACvB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAElC,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC;IAClC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,oCAAoC;IACpC,IAAI,cAAc,IAAI,MAAM,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC;IAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAAA,CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,UAAkB,EACH;IACf,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;AAAA,CAC1C"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @syncular/server - Server-side sync infrastructure
3
+ *
4
+ * Commit-log based sync with:
5
+ * - commit log + change log
6
+ * - scopes + subscriptions (partial sync + auth)
7
+ * - commit-level idempotency
8
+ * - blob/media storage
9
+ */
10
+ export * from '@syncular/core';
11
+ export * from './blobs';
12
+ export * from './clients';
13
+ export * from './compaction';
14
+ export * from './dialect';
15
+ export * from './helpers';
16
+ export * from './migrate';
17
+ export * from './proxy';
18
+ export * from './prune';
19
+ export * from './pull';
20
+ export * from './push';
21
+ export * from './realtime';
22
+ export * from './schema';
23
+ export * from './shapes';
24
+ export * from './snapshot-chunks';
25
+ export type { SnapshotChunkStorage } from './snapshot-chunks/types';
26
+ export * from './stats';
27
+ export * from './subscriptions';
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,cAAc,gBAAgB,CAAC;AAE/B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,YAAY,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @syncular/server - Server-side sync infrastructure
3
+ *
4
+ * Commit-log based sync with:
5
+ * - commit log + change log
6
+ * - scopes + subscriptions (partial sync + auth)
7
+ * - commit-level idempotency
8
+ * - blob/media storage
9
+ */
10
+ export * from '@syncular/core';
11
+ export * from './blobs/index.js';
12
+ export * from './clients.js';
13
+ export * from './compaction.js';
14
+ export * from './dialect/index.js';
15
+ export * from './helpers/index.js';
16
+ export * from './migrate.js';
17
+ export * from './proxy/index.js';
18
+ export * from './prune.js';
19
+ export * from './pull.js';
20
+ export * from './push.js';
21
+ export * from './realtime/index.js';
22
+ export * from './schema.js';
23
+ export * from './shapes/index.js';
24
+ export * from './snapshot-chunks.js';
25
+ export * from './stats.js';
26
+ export * from './subscriptions/index.js';
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,cAAc,gBAAgB,CAAC;AAE/B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAElC,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @syncular/server - Schema setup
3
+ */
4
+ import type { Kysely } from 'kysely';
5
+ import type { ServerSyncDialect } from './dialect/types';
6
+ import type { SyncCoreDb } from './schema';
7
+ /**
8
+ * Ensures the sync schema exists in the database.
9
+ * Safe to call multiple times (idempotent).
10
+ *
11
+ * @typeParam DB - Your database type that extends SyncCoreDb
12
+ */
13
+ export declare function ensureSyncSchema<DB extends SyncCoreDb>(db: Kysely<DB>, dialect: ServerSyncDialect): Promise<void>;
14
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,SAAS,UAAU,EAC1D,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAEf"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @syncular/server - Schema setup
3
+ */
4
+ /**
5
+ * Ensures the sync schema exists in the database.
6
+ * Safe to call multiple times (idempotent).
7
+ *
8
+ * @typeParam DB - Your database type that extends SyncCoreDb
9
+ */
10
+ export async function ensureSyncSchema(db, dialect) {
11
+ await dialect.ensureSyncSchema(db);
12
+ }
13
+ //# sourceMappingURL=migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,EAAc,EACd,OAA0B,EACX;IACf,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;AAAA,CACpC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @syncular/server - Proxy Query Handler
3
+ *
4
+ * Executes proxied queries with automatic oplog generation for mutations.
5
+ */
6
+ import type { Kysely } from 'kysely';
7
+ import type { ServerSyncDialect } from '../dialect/types';
8
+ import type { SyncCoreDb } from '../schema';
9
+ import type { ProxyTableRegistry } from './registry';
10
+ import type { ProxyQueryContext } from './types';
11
+ export interface ExecuteProxyQueryArgs<DB extends SyncCoreDb = SyncCoreDb> {
12
+ /** Database connection or transaction */
13
+ db: Kysely<DB>;
14
+ /** Server sync dialect */
15
+ dialect: ServerSyncDialect;
16
+ /** Proxy table registry for oplog generation */
17
+ shapes: ProxyTableRegistry;
18
+ /** Query context (actor/client IDs) */
19
+ ctx: ProxyQueryContext;
20
+ /** SQL query string */
21
+ sqlQuery: string;
22
+ /** Query parameters */
23
+ parameters: readonly unknown[];
24
+ }
25
+ export interface ExecuteProxyQueryResult {
26
+ /** Query result rows (for SELECT or RETURNING) */
27
+ rows?: unknown[];
28
+ /** Number of affected rows (for mutations) */
29
+ rowCount?: number;
30
+ /** Commit sequence if oplog was created */
31
+ commitSeq?: number;
32
+ /** Affected tables if oplog was created */
33
+ affectedTables?: string[];
34
+ }
35
+ /**
36
+ * Execute a proxied query with automatic oplog generation for mutations.
37
+ *
38
+ * - Read queries: Execute directly and return rows
39
+ * - Mutations: Append RETURNING *, execute, create oplog entries
40
+ */
41
+ export declare function executeProxyQuery<DB extends SyncCoreDb>(args: ExecuteProxyQueryArgs<DB>): Promise<ExecuteProxyQueryResult>;
42
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/proxy/handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAQ5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,MAAM,WAAW,qBAAqB,CAAC,EAAE,SAAS,UAAU,GAAG,UAAU;IACvE,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACf,0BAA0B;IAC1B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,gDAAgD;IAChD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,uCAAuC;IACvC,GAAG,EAAE,iBAAiB,CAAC;IACvB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,uBAAuB;IACvB,UAAU,EAAE,SAAS,OAAO,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,kDAAkD;IAClD,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAiDD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,SAAS,UAAU,EAC3D,IAAI,EAAE,qBAAqB,CAAC,EAAE,CAAC,GAC9B,OAAO,CAAC,uBAAuB,CAAC,CAyDlC"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @syncular/server - Proxy Query Handler
3
+ *
4
+ * Executes proxied queries with automatic oplog generation for mutations.
5
+ */
6
+ import { sql } from 'kysely';
7
+ import { appendReturning, detectMutation, hasReturningClause, hasReturningWildcard, } from './mutation-detector.js';
8
+ import { createOplogEntries } from './oplog.js';
9
+ /**
10
+ * Build a raw SQL query with parameters using Kysely's sql helper.
11
+ *
12
+ * This converts parameterized SQL (using $1, $2, etc.) to Kysely's format.
13
+ */
14
+ function buildRawQuery(sqlQuery, parameters) {
15
+ // If no parameters, just use sql.raw
16
+ if (parameters.length === 0) {
17
+ return sql.raw(sqlQuery);
18
+ }
19
+ // Parse the SQL and split by parameter placeholders ($1, $2, etc.)
20
+ // Then use sql.join to build the query with proper parameter binding
21
+ const parts = [];
22
+ let lastIndex = 0;
23
+ const paramRegex = /\$(\d+)/g;
24
+ let match;
25
+ while ((match = paramRegex.exec(sqlQuery)) !== null) {
26
+ // Add the SQL before this parameter
27
+ if (match.index > lastIndex) {
28
+ parts.push(sql.raw(sqlQuery.slice(lastIndex, match.index)));
29
+ }
30
+ // Add the parameter value (1-indexed in SQL, 0-indexed in array)
31
+ const paramIndex = Number.parseInt(match[1], 10) - 1;
32
+ if (paramIndex >= 0 && paramIndex < parameters.length) {
33
+ // Use sql.value to create a proper parameter binding
34
+ parts.push(sql.val(parameters[paramIndex]));
35
+ }
36
+ else {
37
+ // Keep the original placeholder if out of bounds (shouldn't happen)
38
+ parts.push(sql.raw(match[0]));
39
+ }
40
+ lastIndex = match.index + match[0].length;
41
+ }
42
+ // Add remaining SQL after last parameter
43
+ if (lastIndex < sqlQuery.length) {
44
+ parts.push(sql.raw(sqlQuery.slice(lastIndex)));
45
+ }
46
+ // Join all parts together
47
+ return sql.join(parts, sql.raw(''));
48
+ }
49
+ /**
50
+ * Execute a proxied query with automatic oplog generation for mutations.
51
+ *
52
+ * - Read queries: Execute directly and return rows
53
+ * - Mutations: Append RETURNING *, execute, create oplog entries
54
+ */
55
+ export async function executeProxyQuery(args) {
56
+ const { db, dialect, shapes, ctx, sqlQuery, parameters } = args;
57
+ const mutation = detectMutation(sqlQuery);
58
+ if (!mutation) {
59
+ // Read query - execute directly
60
+ const result = await buildRawQuery(sqlQuery, parameters).execute(db);
61
+ return { rows: result.rows };
62
+ }
63
+ // Check if this table has a registered shape
64
+ const shape = shapes.get(mutation.tableName);
65
+ if (!shape) {
66
+ // No shape registered - execute without oplog
67
+ // This allows proxy operations on non-synced tables
68
+ const result = await buildRawQuery(sqlQuery, parameters).execute(db);
69
+ return {
70
+ rows: result.rows,
71
+ rowCount: Number(result.numAffectedRows ?? 0),
72
+ };
73
+ }
74
+ // Mutation with registered shape - append RETURNING * and create oplog
75
+ const hasReturning = hasReturningClause(sqlQuery);
76
+ if (hasReturning && !hasReturningWildcard(sqlQuery)) {
77
+ throw new Error(`Proxy mutation on synced table "${mutation.tableName}" must use RETURNING * (or omit RETURNING)`);
78
+ }
79
+ const finalSql = hasReturning ? sqlQuery : appendReturning(sqlQuery);
80
+ const result = await buildRawQuery(finalSql, parameters).execute(db);
81
+ const affectedRows = result.rows;
82
+ if (affectedRows.length === 0) {
83
+ return { rowCount: 0 };
84
+ }
85
+ // Create oplog entries
86
+ const { commitSeq, affectedTables } = await createOplogEntries({
87
+ trx: db,
88
+ dialect,
89
+ actorId: ctx.actorId,
90
+ clientId: ctx.clientId,
91
+ partitionId: ctx.partitionId,
92
+ shape,
93
+ operation: mutation.operation,
94
+ rows: affectedRows,
95
+ });
96
+ return {
97
+ rowCount: affectedRows.length,
98
+ commitSeq,
99
+ affectedTables,
100
+ };
101
+ }
102
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/proxy/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @syncular/server - Proxy Exports
3
+ *
4
+ * Server-side proxy functionality for database access.
5
+ */
6
+ export { type ExecuteProxyQueryArgs, type ExecuteProxyQueryResult, executeProxyQuery, } from './handler';
7
+ export { type DetectedMutation, detectMutation } from './mutation-detector';
8
+ export { ProxyTableRegistry } from './registry';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,iBAAiB,GAClB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,KAAK,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @syncular/server - Proxy Exports
3
+ *
4
+ * Server-side proxy functionality for database access.
5
+ */
6
+ // Query execution
7
+ export { executeProxyQuery, } from './handler.js';
8
+ // Mutation detection
9
+ export { detectMutation } from './mutation-detector.js';
10
+ // Oplog creation
11
+ // Registry
12
+ export { ProxyTableRegistry } from './registry.js';
13
+ // Types
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,kBAAkB;AAClB,OAAO,EAGL,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,qBAAqB;AACrB,OAAO,EAAyB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC5E,iBAAiB;AACjB,WAAW;AACX,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,QAAQ"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @syncular/server - Mutation Detector
3
+ *
4
+ * Detects whether a SQL query is a mutation (INSERT/UPDATE/DELETE).
5
+ */
6
+ import type { SyncOp } from '@syncular/core';
7
+ export interface DetectedMutation {
8
+ /** Operation type */
9
+ operation: SyncOp;
10
+ /** Table name being modified */
11
+ tableName: string;
12
+ }
13
+ /**
14
+ * Detect if a SQL query is a mutation and extract table info.
15
+ *
16
+ * @param sql - The SQL query string
17
+ * @returns Mutation info if detected, null for read queries
18
+ */
19
+ export declare function detectMutation(sql: string): DetectedMutation | null;
20
+ /**
21
+ * Check if SQL already has a RETURNING clause.
22
+ */
23
+ export declare function hasReturningClause(sql: string): boolean;
24
+ /**
25
+ * Check if SQL has a wildcard RETURNING clause (RETURNING * or alias.*).
26
+ */
27
+ export declare function hasReturningWildcard(sql: string): boolean;
28
+ /**
29
+ * Append RETURNING * to a mutation query if not already present.
30
+ *
31
+ * @param sql - The SQL query string
32
+ * @returns Modified SQL with RETURNING *
33
+ */
34
+ export declare function appendReturning(sql: string): string;
35
+ //# sourceMappingURL=mutation-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-detector.d.ts","sourceRoot":"","sources":["../../src/proxy/mutation-detector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB;AAyMD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CA2BnE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIzD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQnD"}
@@ -0,0 +1,246 @@
1
+ /**
2
+ * @syncular/server - Mutation Detector
3
+ *
4
+ * Detects whether a SQL query is a mutation (INSERT/UPDATE/DELETE).
5
+ */
6
+ function isWordStart(ch) {
7
+ return /[A-Za-z_]/.test(ch);
8
+ }
9
+ function isWordPart(ch) {
10
+ return /[A-Za-z0-9_$]/.test(ch);
11
+ }
12
+ function skipLeadingNoise(sql) {
13
+ let index = 0;
14
+ while (index < sql.length) {
15
+ while (index < sql.length && /[\s;]/.test(sql[index])) {
16
+ index += 1;
17
+ }
18
+ if (sql.startsWith('--', index)) {
19
+ index += 2;
20
+ while (index < sql.length && sql[index] !== '\n') {
21
+ index += 1;
22
+ }
23
+ continue;
24
+ }
25
+ if (sql.startsWith('/*', index)) {
26
+ const end = sql.indexOf('*/', index + 2);
27
+ if (end === -1)
28
+ return '';
29
+ index = end + 2;
30
+ continue;
31
+ }
32
+ break;
33
+ }
34
+ return sql.slice(index);
35
+ }
36
+ function extractMainStatement(sql) {
37
+ const normalized = skipLeadingNoise(sql);
38
+ if (!normalized.toLowerCase().startsWith('with')) {
39
+ return normalized;
40
+ }
41
+ const lower = normalized.toLowerCase();
42
+ const rootKeywords = new Set(['insert', 'update', 'delete', 'select']);
43
+ let index = 0;
44
+ let depth = 0;
45
+ let inSingleQuote = false;
46
+ let inDoubleQuote = false;
47
+ let inLineComment = false;
48
+ let inBlockComment = false;
49
+ while (index < normalized.length) {
50
+ const ch = normalized[index];
51
+ const next = normalized[index + 1];
52
+ if (inLineComment) {
53
+ if (ch === '\n')
54
+ inLineComment = false;
55
+ index += 1;
56
+ continue;
57
+ }
58
+ if (inBlockComment) {
59
+ if (ch === '*' && next === '/') {
60
+ inBlockComment = false;
61
+ index += 2;
62
+ continue;
63
+ }
64
+ index += 1;
65
+ continue;
66
+ }
67
+ if (inSingleQuote) {
68
+ if (ch === "'" && next === "'") {
69
+ index += 2;
70
+ continue;
71
+ }
72
+ if (ch === "'")
73
+ inSingleQuote = false;
74
+ index += 1;
75
+ continue;
76
+ }
77
+ if (inDoubleQuote) {
78
+ if (ch === '"' && next === '"') {
79
+ index += 2;
80
+ continue;
81
+ }
82
+ if (ch === '"')
83
+ inDoubleQuote = false;
84
+ index += 1;
85
+ continue;
86
+ }
87
+ if (ch === '-' && next === '-') {
88
+ inLineComment = true;
89
+ index += 2;
90
+ continue;
91
+ }
92
+ if (ch === '/' && next === '*') {
93
+ inBlockComment = true;
94
+ index += 2;
95
+ continue;
96
+ }
97
+ if (ch === "'") {
98
+ inSingleQuote = true;
99
+ index += 1;
100
+ continue;
101
+ }
102
+ if (ch === '"') {
103
+ inDoubleQuote = true;
104
+ index += 1;
105
+ continue;
106
+ }
107
+ if (ch === '(') {
108
+ depth += 1;
109
+ index += 1;
110
+ continue;
111
+ }
112
+ if (ch === ')') {
113
+ if (depth > 0)
114
+ depth -= 1;
115
+ index += 1;
116
+ continue;
117
+ }
118
+ if (depth === 0 && isWordStart(ch)) {
119
+ const tokenStart = index;
120
+ index += 1;
121
+ while (index < normalized.length && isWordPart(normalized[index])) {
122
+ index += 1;
123
+ }
124
+ const token = lower.slice(tokenStart, index);
125
+ if (token !== 'with' &&
126
+ token !== 'recursive' &&
127
+ rootKeywords.has(token)) {
128
+ return normalized.slice(tokenStart);
129
+ }
130
+ continue;
131
+ }
132
+ index += 1;
133
+ }
134
+ return normalized;
135
+ }
136
+ function parseIdentifier(input, startIndex) {
137
+ let index = startIndex;
138
+ while (index < input.length && /\s/.test(input[index])) {
139
+ index += 1;
140
+ }
141
+ if (index >= input.length)
142
+ return null;
143
+ if (input[index] === '"') {
144
+ index += 1;
145
+ let value = '';
146
+ while (index < input.length) {
147
+ const ch = input[index];
148
+ if (ch === '"' && input[index + 1] === '"') {
149
+ value += '"';
150
+ index += 2;
151
+ continue;
152
+ }
153
+ if (ch === '"') {
154
+ index += 1;
155
+ return { name: value, nextIndex: index };
156
+ }
157
+ value += ch;
158
+ index += 1;
159
+ }
160
+ return null;
161
+ }
162
+ if (!isWordStart(input[index]))
163
+ return null;
164
+ const first = index;
165
+ index += 1;
166
+ while (index < input.length && isWordPart(input[index])) {
167
+ index += 1;
168
+ }
169
+ return { name: input.slice(first, index), nextIndex: index };
170
+ }
171
+ function parseTargetTable(input) {
172
+ const first = parseIdentifier(input, 0);
173
+ if (!first)
174
+ return null;
175
+ let index = first.nextIndex;
176
+ while (index < input.length && /\s/.test(input[index])) {
177
+ index += 1;
178
+ }
179
+ if (input[index] !== '.') {
180
+ return first.name;
181
+ }
182
+ const second = parseIdentifier(input, index + 1);
183
+ if (!second)
184
+ return null;
185
+ return second.name;
186
+ }
187
+ /**
188
+ * Detect if a SQL query is a mutation and extract table info.
189
+ *
190
+ * @param sql - The SQL query string
191
+ * @returns Mutation info if detected, null for read queries
192
+ */
193
+ export function detectMutation(sql) {
194
+ const statement = extractMainStatement(sql).trimStart();
195
+ const lower = statement.toLowerCase();
196
+ if (lower.startsWith('insert')) {
197
+ const tableName = parseTargetTable(statement.replace(/^insert\s+into\s+/i, ''));
198
+ if (!tableName)
199
+ return null;
200
+ return { operation: 'upsert', tableName };
201
+ }
202
+ if (lower.startsWith('update')) {
203
+ const tableName = parseTargetTable(statement.replace(/^update\s+/i, ''));
204
+ if (!tableName)
205
+ return null;
206
+ return { operation: 'upsert', tableName };
207
+ }
208
+ if (lower.startsWith('delete')) {
209
+ const tableName = parseTargetTable(statement.replace(/^delete\s+from\s+/i, ''));
210
+ if (!tableName)
211
+ return null;
212
+ return { operation: 'delete', tableName };
213
+ }
214
+ return null;
215
+ }
216
+ /**
217
+ * Check if SQL already has a RETURNING clause.
218
+ */
219
+ export function hasReturningClause(sql) {
220
+ // Simple check - look for RETURNING keyword not in a string
221
+ return /\bRETURNING\b/i.test(sql);
222
+ }
223
+ /**
224
+ * Check if SQL has a wildcard RETURNING clause (RETURNING * or alias.*).
225
+ */
226
+ export function hasReturningWildcard(sql) {
227
+ const match = sql.match(/\bRETURNING\b([\s\S]*)$/i);
228
+ if (!match)
229
+ return false;
230
+ return /(^|,)\s*(?:[A-Za-z_][A-Za-z0-9_$]*\.)?\*/i.test(match[1]);
231
+ }
232
+ /**
233
+ * Append RETURNING * to a mutation query if not already present.
234
+ *
235
+ * @param sql - The SQL query string
236
+ * @returns Modified SQL with RETURNING *
237
+ */
238
+ export function appendReturning(sql) {
239
+ if (hasReturningClause(sql)) {
240
+ return sql;
241
+ }
242
+ // Remove trailing semicolon if present
243
+ const trimmed = sql.trim().replace(/;\s*$/, '');
244
+ return `${trimmed} RETURNING *`;
245
+ }
246
+ //# sourceMappingURL=mutation-detector.js.map