@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
package/dist/push.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { type SyncChange, type SyncPushRequest, type SyncPushResponse } from '@syncular/core';
2
+ import type { Kysely } from 'kysely';
3
+ import type { ServerSyncDialect } from './dialect/types';
4
+ import type { SyncCoreDb } from './schema';
5
+ import type { TableRegistry } from './shapes/registry';
6
+ export interface PushCommitResult {
7
+ response: SyncPushResponse;
8
+ /**
9
+ * Distinct tables affected by this commit.
10
+ * Empty for rejected commits and for commits that emit no changes.
11
+ */
12
+ affectedTables: string[];
13
+ /**
14
+ * Scope keys derived from emitted changes (e.g. "org:abc", "team:xyz").
15
+ * Computed in-transaction so callers don't need an extra DB query.
16
+ * Empty for rejected/cached commits.
17
+ */
18
+ scopeKeys: string[];
19
+ /**
20
+ * Changes emitted by this commit. Available for WS data delivery.
21
+ * Empty for rejected/cached commits.
22
+ */
23
+ emittedChanges: SyncChange[];
24
+ }
25
+ export declare function pushCommit<DB extends SyncCoreDb>(args: {
26
+ db: Kysely<DB>;
27
+ dialect: ServerSyncDialect;
28
+ shapes: TableRegistry<DB>;
29
+ actorId: string;
30
+ partitionId?: string;
31
+ request: SyncPushRequest;
32
+ }): Promise<PushCommitResult>;
33
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAEtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAEV,MAAM,EAIP,MAAM,QAAQ,CAAC;AAEhB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKvD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB;;;;OAIG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;OAGG;IACH,cAAc,EAAE,UAAU,EAAE,CAAC;CAC9B;AA6FD,wBAAsB,UAAU,CAAC,EAAE,SAAS,UAAU,EAAE,IAAI,EAAE;IAC5D,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,eAAe,CAAC;CAC1B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwb5B"}
package/dist/push.js ADDED
@@ -0,0 +1,412 @@
1
+ import { captureSyncException, countSyncMetric, distributionSyncMetric, startSyncSpan, } from '@syncular/core';
2
+ import { sql } from 'kysely';
3
+ class RejectCommitError extends Error {
4
+ response;
5
+ constructor(response) {
6
+ super('REJECT_COMMIT');
7
+ this.response = response;
8
+ this.name = 'RejectCommitError';
9
+ }
10
+ }
11
+ async function readCommitAffectedTables(db, dialect, commitSeq, partitionId) {
12
+ try {
13
+ const commitsQ = db.selectFrom('sync_commits');
14
+ const row = await commitsQ
15
+ .selectAll()
16
+ .where(sql `commit_seq = ${commitSeq}`)
17
+ .where(sql `partition_id = ${partitionId}`)
18
+ .executeTakeFirst();
19
+ const raw = row?.affected_tables;
20
+ return dialect.dbToArray(raw);
21
+ }
22
+ catch {
23
+ // ignore and fall back to scanning changes (best-effort)
24
+ }
25
+ // Fallback: read from changes using dialect-specific implementation
26
+ return dialect.readAffectedTablesFromChanges(db, commitSeq, { partitionId });
27
+ }
28
+ function scopeKeysFromEmitted(emitted) {
29
+ const keys = new Set();
30
+ for (const c of emitted) {
31
+ for (const [key, value] of Object.entries(c.scopes)) {
32
+ if (!value)
33
+ continue;
34
+ const prefix = key.replace(/_id$/, '');
35
+ keys.add(`${prefix}:${value}`);
36
+ }
37
+ }
38
+ return Array.from(keys);
39
+ }
40
+ function recordPushMetrics(args) {
41
+ const { status, durationMs, operationCount, emittedChangeCount, affectedTableCount, } = args;
42
+ countSyncMetric('sync.server.push.requests', 1, {
43
+ attributes: { status },
44
+ });
45
+ countSyncMetric('sync.server.push.operations', operationCount, {
46
+ attributes: { status },
47
+ });
48
+ distributionSyncMetric('sync.server.push.duration_ms', durationMs, {
49
+ unit: 'millisecond',
50
+ attributes: { status },
51
+ });
52
+ distributionSyncMetric('sync.server.push.emitted_changes', emittedChangeCount, {
53
+ attributes: { status },
54
+ });
55
+ distributionSyncMetric('sync.server.push.affected_tables', affectedTableCount, {
56
+ attributes: { status },
57
+ });
58
+ }
59
+ export async function pushCommit(args) {
60
+ const { request, dialect } = args;
61
+ const db = args.db;
62
+ const partitionId = args.partitionId ?? 'default';
63
+ const requestedOps = Array.isArray(request.operations)
64
+ ? request.operations
65
+ : [];
66
+ const operationCount = requestedOps.length;
67
+ const startedAtMs = Date.now();
68
+ return startSyncSpan({
69
+ name: 'sync.server.push',
70
+ op: 'sync.push',
71
+ attributes: {
72
+ operation_count: operationCount,
73
+ },
74
+ }, async (span) => {
75
+ const finalizeResult = (result) => {
76
+ const durationMs = Math.max(0, Date.now() - startedAtMs);
77
+ const status = result.response.status;
78
+ span.setAttribute('status', status);
79
+ span.setAttribute('duration_ms', durationMs);
80
+ span.setAttribute('emitted_change_count', result.emittedChanges.length);
81
+ span.setAttribute('affected_table_count', result.affectedTables.length);
82
+ span.setStatus('ok');
83
+ recordPushMetrics({
84
+ status,
85
+ durationMs,
86
+ operationCount,
87
+ emittedChangeCount: result.emittedChanges.length,
88
+ affectedTableCount: result.affectedTables.length,
89
+ });
90
+ return result;
91
+ };
92
+ try {
93
+ if (!request.clientId || !request.clientCommitId) {
94
+ return finalizeResult({
95
+ response: {
96
+ ok: true,
97
+ status: 'rejected',
98
+ results: [
99
+ {
100
+ opIndex: 0,
101
+ status: 'error',
102
+ error: 'INVALID_REQUEST',
103
+ code: 'INVALID_REQUEST',
104
+ retriable: false,
105
+ },
106
+ ],
107
+ },
108
+ affectedTables: [],
109
+ scopeKeys: [],
110
+ emittedChanges: [],
111
+ });
112
+ }
113
+ const ops = request.operations ?? [];
114
+ if (!Array.isArray(ops) || ops.length === 0) {
115
+ return finalizeResult({
116
+ response: {
117
+ ok: true,
118
+ status: 'rejected',
119
+ results: [
120
+ {
121
+ opIndex: 0,
122
+ status: 'error',
123
+ error: 'EMPTY_COMMIT',
124
+ code: 'EMPTY_COMMIT',
125
+ retriable: false,
126
+ },
127
+ ],
128
+ },
129
+ affectedTables: [],
130
+ scopeKeys: [],
131
+ emittedChanges: [],
132
+ });
133
+ }
134
+ return finalizeResult(await dialect.executeInTransaction(db, async (trx) => {
135
+ const syncTrx = trx;
136
+ // Clean up any stale commit row with null result_json.
137
+ // This can happen when a previous push inserted the commit row but crashed
138
+ // before writing the result (e.g., on D1 without transaction support).
139
+ await syncTrx
140
+ .deleteFrom('sync_commits')
141
+ .where('partition_id', '=', partitionId)
142
+ .where('client_id', '=', request.clientId)
143
+ .where('client_commit_id', '=', request.clientCommitId)
144
+ .where('result_json', 'is', null)
145
+ .execute();
146
+ // Insert commit row (idempotency key)
147
+ const commitRow = {
148
+ partition_id: partitionId,
149
+ actor_id: args.actorId,
150
+ client_id: request.clientId,
151
+ client_commit_id: request.clientCommitId,
152
+ meta: null,
153
+ result_json: null,
154
+ };
155
+ const insertResult = await syncTrx
156
+ .insertInto('sync_commits')
157
+ .values(commitRow)
158
+ .onConflict((oc) => oc
159
+ .columns(['partition_id', 'client_id', 'client_commit_id'])
160
+ .doNothing())
161
+ .executeTakeFirstOrThrow();
162
+ const insertedRows = Number(insertResult.numInsertedOrUpdatedRows ?? 0);
163
+ if (insertedRows === 0) {
164
+ // Existing commit: return cached response (applied or rejected)
165
+ // Use forUpdate() for row locking on databases that support it
166
+ let query = syncTrx.selectFrom('sync_commits')
167
+ .selectAll()
168
+ .where('partition_id', '=', partitionId)
169
+ .where('client_id', '=', request.clientId)
170
+ .where('client_commit_id', '=', request.clientCommitId);
171
+ if (dialect.supportsForUpdate) {
172
+ query = query.forUpdate();
173
+ }
174
+ const existing = await query.executeTakeFirstOrThrow();
175
+ const cached = existing.result_json;
176
+ if (!cached || cached.ok !== true) {
177
+ return {
178
+ response: {
179
+ ok: true,
180
+ status: 'rejected',
181
+ results: [
182
+ {
183
+ opIndex: 0,
184
+ status: 'error',
185
+ error: 'IDEMPOTENCY_CACHE_MISS',
186
+ code: 'INTERNAL',
187
+ retriable: true,
188
+ },
189
+ ],
190
+ },
191
+ affectedTables: [],
192
+ scopeKeys: [],
193
+ emittedChanges: [],
194
+ };
195
+ }
196
+ const base = {
197
+ ...cached,
198
+ commitSeq: Number(existing.commit_seq),
199
+ };
200
+ if (cached.status === 'applied') {
201
+ const tablesFromDb = dialect.dbToArray(existing.affected_tables);
202
+ return {
203
+ response: { ...base, status: 'cached' },
204
+ affectedTables: tablesFromDb.length > 0
205
+ ? tablesFromDb
206
+ : await readCommitAffectedTables(trx, dialect, Number(existing.commit_seq), partitionId),
207
+ scopeKeys: [],
208
+ emittedChanges: [],
209
+ };
210
+ }
211
+ return {
212
+ response: base,
213
+ affectedTables: [],
214
+ scopeKeys: [],
215
+ emittedChanges: [],
216
+ };
217
+ }
218
+ const insertedCommit = await syncTrx.selectFrom('sync_commits')
219
+ .selectAll()
220
+ .where('partition_id', '=', partitionId)
221
+ .where('client_id', '=', request.clientId)
222
+ .where('client_commit_id', '=', request.clientCommitId)
223
+ .executeTakeFirstOrThrow();
224
+ const commitSeq = Number(insertedCommit.commit_seq);
225
+ const commitId = `${request.clientId}:${request.clientCommitId}`;
226
+ const savepointName = 'sync_apply';
227
+ const useSavepoints = dialect.supportsSavepoints;
228
+ let savepointCreated = false;
229
+ try {
230
+ // Apply the commit under a savepoint so we can roll back app writes on conflict
231
+ // while still persisting the commit-level cached response.
232
+ if (useSavepoints) {
233
+ await sql.raw(`SAVEPOINT ${savepointName}`).execute(trx);
234
+ savepointCreated = true;
235
+ }
236
+ const allEmitted = [];
237
+ const results = [];
238
+ const affectedTablesSet = new Set();
239
+ for (let i = 0; i < ops.length; i++) {
240
+ const op = ops[i];
241
+ const handler = args.shapes.getOrThrow(op.table);
242
+ const applied = await handler.applyOperation({
243
+ db: trx,
244
+ trx,
245
+ actorId: args.actorId,
246
+ clientId: request.clientId,
247
+ commitId,
248
+ schemaVersion: request.schemaVersion,
249
+ }, op, i);
250
+ if (applied.result.status !== 'applied') {
251
+ results.push(applied.result);
252
+ throw new RejectCommitError({
253
+ ok: true,
254
+ status: 'rejected',
255
+ commitSeq,
256
+ results,
257
+ });
258
+ }
259
+ // Framework-level enforcement: emitted changes must have scopes
260
+ for (const c of applied.emittedChanges ?? []) {
261
+ const scopes = c?.scopes;
262
+ if (!scopes || typeof scopes !== 'object') {
263
+ results.push({
264
+ opIndex: i,
265
+ status: 'error',
266
+ error: 'MISSING_SCOPES',
267
+ code: 'INVALID_SCOPE',
268
+ retriable: false,
269
+ });
270
+ throw new RejectCommitError({
271
+ ok: true,
272
+ status: 'rejected',
273
+ commitSeq,
274
+ results,
275
+ });
276
+ }
277
+ }
278
+ results.push(applied.result);
279
+ allEmitted.push(...applied.emittedChanges);
280
+ for (const c of applied.emittedChanges) {
281
+ affectedTablesSet.add(c.table);
282
+ }
283
+ }
284
+ if (allEmitted.length > 0) {
285
+ const changeRows = allEmitted.map((c) => ({
286
+ partition_id: partitionId,
287
+ commit_seq: commitSeq,
288
+ table: c.table,
289
+ row_id: c.row_id,
290
+ op: c.op,
291
+ row_json: c.row_json,
292
+ row_version: c.row_version,
293
+ scopes: dialect.scopesToDb(c.scopes),
294
+ }));
295
+ await syncTrx
296
+ .insertInto('sync_changes')
297
+ .values(changeRows)
298
+ .execute();
299
+ }
300
+ const appliedResponse = {
301
+ ok: true,
302
+ status: 'applied',
303
+ commitSeq,
304
+ results,
305
+ };
306
+ const affectedTables = Array.from(affectedTablesSet).sort();
307
+ const appliedCommitUpdate = {
308
+ result_json: appliedResponse,
309
+ change_count: allEmitted.length,
310
+ affected_tables: affectedTables,
311
+ };
312
+ await syncTrx
313
+ .updateTable('sync_commits')
314
+ .set(appliedCommitUpdate)
315
+ .where('commit_seq', '=', commitSeq)
316
+ .execute();
317
+ // Insert table commits for subscription filtering
318
+ if (affectedTables.length > 0) {
319
+ const tableCommits = affectedTables.map((table) => ({
320
+ partition_id: partitionId,
321
+ table,
322
+ commit_seq: commitSeq,
323
+ }));
324
+ await syncTrx
325
+ .insertInto('sync_table_commits')
326
+ .values(tableCommits)
327
+ .onConflict((oc) => oc
328
+ .columns(['partition_id', 'table', 'commit_seq'])
329
+ .doNothing())
330
+ .execute();
331
+ }
332
+ if (useSavepoints) {
333
+ await sql
334
+ .raw(`RELEASE SAVEPOINT ${savepointName}`)
335
+ .execute(trx);
336
+ }
337
+ return {
338
+ response: appliedResponse,
339
+ affectedTables,
340
+ scopeKeys: scopeKeysFromEmitted(allEmitted),
341
+ emittedChanges: allEmitted.map((c) => ({
342
+ table: c.table,
343
+ row_id: c.row_id,
344
+ op: c.op,
345
+ row_json: c.row_json,
346
+ row_version: c.row_version,
347
+ scopes: c.scopes,
348
+ })),
349
+ };
350
+ }
351
+ catch (err) {
352
+ // Roll back app writes but keep the commit row.
353
+ if (savepointCreated) {
354
+ try {
355
+ await sql
356
+ .raw(`ROLLBACK TO SAVEPOINT ${savepointName}`)
357
+ .execute(trx);
358
+ await sql
359
+ .raw(`RELEASE SAVEPOINT ${savepointName}`)
360
+ .execute(trx);
361
+ }
362
+ catch (savepointErr) {
363
+ // If savepoint rollback fails, the transaction may be in an
364
+ // inconsistent state. Log and rethrow to fail the entire commit
365
+ // rather than risk data corruption.
366
+ console.error('[pushCommit] Savepoint rollback failed:', savepointErr);
367
+ throw savepointErr;
368
+ }
369
+ }
370
+ if (!(err instanceof RejectCommitError))
371
+ throw err;
372
+ const rejectedCommitUpdate = {
373
+ result_json: err.response,
374
+ change_count: 0,
375
+ affected_tables: [],
376
+ };
377
+ // Persist the rejected response for commit-level idempotency.
378
+ await syncTrx
379
+ .updateTable('sync_commits')
380
+ .set(rejectedCommitUpdate)
381
+ .where('commit_seq', '=', commitSeq)
382
+ .execute();
383
+ return {
384
+ response: err.response,
385
+ affectedTables: [],
386
+ scopeKeys: [],
387
+ emittedChanges: [],
388
+ };
389
+ }
390
+ }));
391
+ }
392
+ catch (error) {
393
+ const durationMs = Math.max(0, Date.now() - startedAtMs);
394
+ span.setAttribute('status', 'error');
395
+ span.setAttribute('duration_ms', durationMs);
396
+ span.setStatus('error');
397
+ recordPushMetrics({
398
+ status: 'error',
399
+ durationMs,
400
+ operationCount,
401
+ emittedChangeCount: 0,
402
+ affectedTableCount: 0,
403
+ });
404
+ captureSyncException(error, {
405
+ event: 'sync.server.push',
406
+ operationCount,
407
+ });
408
+ throw error;
409
+ }
410
+ });
411
+ }
412
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { SyncRealtimeBroadcaster, SyncRealtimeEvent } from './types';
2
+ /**
3
+ * In-memory broadcaster for tests and single-process setups.
4
+ *
5
+ * This simulates multi-instance fanout when shared across "instances" in a test.
6
+ */
7
+ export declare class InMemorySyncRealtimeBroadcaster implements SyncRealtimeBroadcaster {
8
+ private handlers;
9
+ subscribe(handler: (event: SyncRealtimeEvent) => void): () => void;
10
+ publish(event: SyncRealtimeEvent): Promise<void>;
11
+ close(): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=in-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory.d.ts","sourceRoot":"","sources":["../../src/realtime/in-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE1E;;;;GAIG;AACH,qBAAa,+BACX,YAAW,uBAAuB;IAElC,OAAO,CAAC,QAAQ,CAAiD;IAEjE,SAAS,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,CAKjE;IAEK,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrD;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;CACF"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * In-memory broadcaster for tests and single-process setups.
3
+ *
4
+ * This simulates multi-instance fanout when shared across "instances" in a test.
5
+ */
6
+ export class InMemorySyncRealtimeBroadcaster {
7
+ handlers = new Set();
8
+ subscribe(handler) {
9
+ this.handlers.add(handler);
10
+ return () => {
11
+ this.handlers.delete(handler);
12
+ };
13
+ }
14
+ async publish(event) {
15
+ for (const handler of this.handlers) {
16
+ try {
17
+ handler(event);
18
+ }
19
+ catch {
20
+ // ignore individual handler errors; realtime is best-effort
21
+ }
22
+ }
23
+ }
24
+ async close() {
25
+ this.handlers.clear();
26
+ }
27
+ }
28
+ //# sourceMappingURL=in-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory.js","sourceRoot":"","sources":["../../src/realtime/in-memory.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,OAAO,+BAA+B;IAGlC,QAAQ,GAAG,IAAI,GAAG,EAAsC,CAAC;IAEjE,SAAS,CAAC,OAA2C,EAAc;QACjE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,GAAG,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAAA,CAC/B,CAAC;IAAA,CACH;IAED,KAAK,CAAC,OAAO,CAAC,KAAwB,EAAiB;QACrD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC;gBACH,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;YAAC,MAAM,CAAC;gBACP,4DAA4D;YAC9D,CAAC;QACH,CAAC;IAAA,CACF;IAED,KAAK,CAAC,KAAK,GAAkB;QAC3B,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAAA,CACvB;CACF"}
@@ -0,0 +1,3 @@
1
+ export { InMemorySyncRealtimeBroadcaster } from './in-memory';
2
+ export type { SyncRealtimeBroadcaster, SyncRealtimeEvent, } from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAC9D,YAAY,EACV,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { InMemorySyncRealtimeBroadcaster } from './in-memory.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @syncular/server - Realtime broadcaster types
3
+ *
4
+ * Realtime is a best-effort "wake up" mechanism. Correctness always comes from pull + cursors.
5
+ */
6
+ export interface SyncRealtimeCommitEvent {
7
+ type: 'commit';
8
+ commitSeq: number;
9
+ /** Logical partition key (tenant / demo / workspace). */
10
+ partitionId?: string;
11
+ /**
12
+ * Optional scopes affected by the commit.
13
+ * Broadcasters may omit this to keep payloads small (listeners can look up in DB).
14
+ */
15
+ scopeKeys?: string[];
16
+ /** Optional instance id to suppress echo on the originating instance. */
17
+ sourceInstanceId?: string;
18
+ }
19
+ /**
20
+ * Presence event for tracking which clients are connected to which scopes.
21
+ * Used for collaborative features like "who's online" indicators.
22
+ */
23
+ export interface SyncRealtimePresenceEvent {
24
+ type: 'presence';
25
+ /** The action that occurred */
26
+ action: 'join' | 'leave' | 'update';
27
+ /** The scope key this presence event relates to */
28
+ scopeKey: string;
29
+ /** Client/device identifier */
30
+ clientId: string;
31
+ /** Actor/user identifier */
32
+ actorId: string;
33
+ /** Optional metadata (e.g., entity being viewed/edited) */
34
+ metadata?: Record<string, unknown>;
35
+ /** Optional instance id to suppress echo on the originating instance. */
36
+ sourceInstanceId?: string;
37
+ }
38
+ export type SyncRealtimeEvent = SyncRealtimeCommitEvent | SyncRealtimePresenceEvent;
39
+ export interface SyncRealtimeBroadcaster {
40
+ /** Publish an event to other app instances. */
41
+ publish(event: SyncRealtimeEvent): Promise<void>;
42
+ /**
43
+ * Subscribe to events from other app instances.
44
+ * Returns an unsubscribe function.
45
+ */
46
+ subscribe(handler: (event: SyncRealtimeEvent) => void): () => void;
47
+ /** Close underlying resources (best-effort). */
48
+ close(): Promise<void>;
49
+ }
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/realtime/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,UAAU,CAAC;IACjB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpC,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,iBAAiB,GACzB,uBAAuB,GACvB,yBAAyB,CAAC;AAE9B,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACnE,gDAAgD;IAChD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @syncular/server - Realtime broadcaster types
3
+ *
4
+ * Realtime is a best-effort "wake up" mechanism. Correctness always comes from pull + cursors.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/realtime/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}