@stratasync/server 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +188 -0
  2. package/dist/bootstrap/bootstrap-service.d.ts +41 -0
  3. package/dist/bootstrap/bootstrap-service.d.ts.map +1 -0
  4. package/dist/bootstrap/bootstrap-service.js +411 -0
  5. package/dist/bootstrap/bootstrap-service.js.map +1 -0
  6. package/dist/config.d.ts +124 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +10 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/create-sync-server.d.ts +5 -0
  11. package/dist/create-sync-server.d.ts.map +1 -0
  12. package/dist/create-sync-server.js +96 -0
  13. package/dist/create-sync-server.js.map +1 -0
  14. package/dist/dao/sync-dao.d.ts +64 -0
  15. package/dist/dao/sync-dao.d.ts.map +1 -0
  16. package/dist/dao/sync-dao.js +137 -0
  17. package/dist/dao/sync-dao.js.map +1 -0
  18. package/dist/db.d.ts +37 -0
  19. package/dist/db.d.ts.map +1 -0
  20. package/dist/db.js +2 -0
  21. package/dist/db.js.map +1 -0
  22. package/dist/delta/delta-publisher.d.ts +52 -0
  23. package/dist/delta/delta-publisher.d.ts.map +1 -0
  24. package/dist/delta/delta-publisher.js +217 -0
  25. package/dist/delta/delta-publisher.js.map +1 -0
  26. package/dist/delta/delta-service.d.ts +13 -0
  27. package/dist/delta/delta-service.d.ts.map +1 -0
  28. package/dist/delta/delta-service.js +36 -0
  29. package/dist/delta/delta-service.js.map +1 -0
  30. package/dist/fastify/index.d.ts +7 -0
  31. package/dist/fastify/index.d.ts.map +1 -0
  32. package/dist/fastify/index.js +5 -0
  33. package/dist/fastify/index.js.map +1 -0
  34. package/dist/fastify/middleware.d.ts +16 -0
  35. package/dist/fastify/middleware.d.ts.map +1 -0
  36. package/dist/fastify/middleware.js +101 -0
  37. package/dist/fastify/middleware.js.map +1 -0
  38. package/dist/fastify/routes.d.ts +17 -0
  39. package/dist/fastify/routes.d.ts.map +1 -0
  40. package/dist/fastify/routes.js +150 -0
  41. package/dist/fastify/routes.js.map +1 -0
  42. package/dist/fastify/validation.d.ts +59 -0
  43. package/dist/fastify/validation.d.ts.map +1 -0
  44. package/dist/fastify/validation.js +79 -0
  45. package/dist/fastify/validation.js.map +1 -0
  46. package/dist/index.d.ts +22 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +22 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/mutate/archive-mutation.d.ts +10 -0
  51. package/dist/mutate/archive-mutation.d.ts.map +1 -0
  52. package/dist/mutate/archive-mutation.js +14 -0
  53. package/dist/mutate/archive-mutation.js.map +1 -0
  54. package/dist/mutate/field-codecs.d.ts +15 -0
  55. package/dist/mutate/field-codecs.d.ts.map +1 -0
  56. package/dist/mutate/field-codecs.js +103 -0
  57. package/dist/mutate/field-codecs.js.map +1 -0
  58. package/dist/mutate/model-handlers.d.ts +32 -0
  59. package/dist/mutate/model-handlers.d.ts.map +1 -0
  60. package/dist/mutate/model-handlers.js +131 -0
  61. package/dist/mutate/model-handlers.js.map +1 -0
  62. package/dist/mutate/mutate-service.d.ts +30 -0
  63. package/dist/mutate/mutate-service.d.ts.map +1 -0
  64. package/dist/mutate/mutate-service.js +326 -0
  65. package/dist/mutate/mutate-service.js.map +1 -0
  66. package/dist/types.d.ts +101 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +29 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/utils/composite-ids.d.ts +14 -0
  71. package/dist/utils/composite-ids.d.ts.map +1 -0
  72. package/dist/utils/composite-ids.js +15 -0
  73. package/dist/utils/composite-ids.js.map +1 -0
  74. package/dist/utils/dates.d.ts +8 -0
  75. package/dist/utils/dates.d.ts.map +1 -0
  76. package/dist/utils/dates.js +101 -0
  77. package/dist/utils/dates.js.map +1 -0
  78. package/dist/utils/sync-scope.d.ts +4 -0
  79. package/dist/utils/sync-scope.d.ts.map +1 -0
  80. package/dist/utils/sync-scope.js +17 -0
  81. package/dist/utils/sync-scope.js.map +1 -0
  82. package/dist/utils/sync-utils.d.ts +27 -0
  83. package/dist/utils/sync-utils.d.ts.map +1 -0
  84. package/dist/utils/sync-utils.js +94 -0
  85. package/dist/utils/sync-utils.js.map +1 -0
  86. package/dist/websocket/sync-websocket.d.ts +14 -0
  87. package/dist/websocket/sync-websocket.d.ts.map +1 -0
  88. package/dist/websocket/sync-websocket.js +326 -0
  89. package/dist/websocket/sync-websocket.js.map +1 -0
  90. package/package.json +70 -0
@@ -0,0 +1,217 @@
1
+ import { noopLogger } from "../config.js";
2
+ import { parseSyncActionOutput, serializeSyncActionOutput, } from "../utils/sync-utils.js";
3
+ const SYNC_DELTA_CHANNEL = "sync:deltas";
4
+ const isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
5
+ const parseDeltaMessage = (raw) => {
6
+ if (typeof raw !== "object" || raw === null) {
7
+ throw new Error("Delta message must be an object");
8
+ }
9
+ const record = raw;
10
+ if (!isStringArray(record.groups)) {
11
+ throw new Error("Delta message groups must be a string array");
12
+ }
13
+ const parsed = {
14
+ action: parseSyncActionOutput(record.action),
15
+ groups: record.groups,
16
+ };
17
+ if (typeof record.sourceId === "string") {
18
+ return { ...parsed, sourceId: record.sourceId };
19
+ }
20
+ return parsed;
21
+ };
22
+ const jsonReplacer = (_, value) => {
23
+ if (typeof value === "bigint") {
24
+ return value.toString();
25
+ }
26
+ return value;
27
+ };
28
+ export const safeJsonStringify = (value) => JSON.stringify(value, jsonReplacer);
29
+ /**
30
+ * Publisher for broadcasting sync deltas via Redis pub/sub
31
+ */
32
+ export class DeltaPublisher {
33
+ redis;
34
+ sourceId;
35
+ constructor(redis, sourceId) {
36
+ this.redis = redis;
37
+ this.sourceId = sourceId;
38
+ }
39
+ async publish(action, groups) {
40
+ const message = {
41
+ action: serializeSyncActionOutput(action),
42
+ groups,
43
+ sourceId: this.sourceId,
44
+ };
45
+ await this.redis.publish(SYNC_DELTA_CHANNEL, safeJsonStringify(message));
46
+ }
47
+ async publishMany(actions, groups) {
48
+ for (const action of actions) {
49
+ await this.publish(action, groups);
50
+ }
51
+ }
52
+ }
53
+ /**
54
+ * Subscriber for receiving sync deltas via Redis pub/sub
55
+ */
56
+ export class DeltaSubscriber {
57
+ callbacks = new Set();
58
+ redis;
59
+ subscriberRedis = null;
60
+ sourceId;
61
+ constructor(redis, sourceId) {
62
+ this.redis = redis;
63
+ this.sourceId = sourceId;
64
+ }
65
+ async start() {
66
+ this.subscriberRedis = this.redis.duplicate();
67
+ await this.subscriberRedis.connect();
68
+ await this.subscriberRedis.subscribe(SYNC_DELTA_CHANNEL, (message) => {
69
+ try {
70
+ const delta = parseDeltaMessage(JSON.parse(message));
71
+ if (this.sourceId && delta.sourceId === this.sourceId) {
72
+ return;
73
+ }
74
+ this.notifyCallbacks(delta.action, delta.groups);
75
+ }
76
+ catch {
77
+ // Invalid message, ignore
78
+ }
79
+ });
80
+ }
81
+ async stop() {
82
+ if (this.subscriberRedis) {
83
+ await this.subscriberRedis.unsubscribe(SYNC_DELTA_CHANNEL);
84
+ await this.subscriberRedis.quit();
85
+ this.subscriberRedis = null;
86
+ }
87
+ }
88
+ // oxlint-disable-next-line prefer-await-to-callbacks -- event listener registration
89
+ onDelta(callback) {
90
+ this.callbacks.add(callback);
91
+ return () => {
92
+ this.callbacks.delete(callback);
93
+ };
94
+ }
95
+ notifyCallbacks(action, groups) {
96
+ for (const callback of this.callbacks) {
97
+ try {
98
+ // oxlint-disable-next-line prefer-await-to-callbacks -- event listener registration
99
+ callback(action, groups);
100
+ }
101
+ catch {
102
+ // Callback error, continue
103
+ }
104
+ }
105
+ }
106
+ }
107
+ class InMemoryDeltaBus {
108
+ callbacks = new Set();
109
+ publish(action, groups) {
110
+ for (const callback of this.callbacks) {
111
+ try {
112
+ // oxlint-disable-next-line prefer-await-to-callbacks -- event listener registration
113
+ callback(action, groups);
114
+ }
115
+ catch {
116
+ // Callback error, continue
117
+ }
118
+ }
119
+ }
120
+ // oxlint-disable-next-line prefer-await-to-callbacks -- event listener registration
121
+ onDelta(callback) {
122
+ this.callbacks.add(callback);
123
+ return () => {
124
+ this.callbacks.delete(callback);
125
+ };
126
+ }
127
+ }
128
+ class InMemoryDeltaPublisher {
129
+ bus;
130
+ constructor(bus) {
131
+ this.bus = bus;
132
+ }
133
+ publish(action, groups) {
134
+ this.bus.publish(action, groups);
135
+ return Promise.resolve();
136
+ }
137
+ async publishMany(actions, groups) {
138
+ for (const action of actions) {
139
+ await this.publish(action, groups);
140
+ }
141
+ }
142
+ }
143
+ class InMemoryDeltaSubscriber {
144
+ bus;
145
+ constructor(bus) {
146
+ this.bus = bus;
147
+ }
148
+ // oxlint-disable-next-line class-methods-use-this -- required by DeltaSubscriberLike interface
149
+ start() {
150
+ return Promise.resolve();
151
+ }
152
+ // oxlint-disable-next-line class-methods-use-this -- required by DeltaSubscriberLike interface
153
+ stop() {
154
+ return Promise.resolve();
155
+ }
156
+ // oxlint-disable-next-line prefer-await-to-callbacks -- event listener registration
157
+ onDelta(callback) {
158
+ return this.bus.onDelta(callback);
159
+ }
160
+ }
161
+ const normalizeError = (error) => {
162
+ if (error instanceof Error) {
163
+ return error;
164
+ }
165
+ return new Error(String(error));
166
+ };
167
+ const formatError = (error) => {
168
+ const err = normalizeError(error);
169
+ return {
170
+ message: err.message,
171
+ name: err.name,
172
+ stack: err.stack,
173
+ };
174
+ };
175
+ class CompositeDeltaPublisher {
176
+ publishers;
177
+ logger;
178
+ constructor(publishers, logger) {
179
+ this.publishers = publishers;
180
+ this.logger = logger;
181
+ }
182
+ async publish(action, groups) {
183
+ let successCount = 0;
184
+ const errors = [];
185
+ for (const publisher of this.publishers) {
186
+ try {
187
+ await publisher.publish(action, groups);
188
+ successCount += 1;
189
+ }
190
+ catch (error) {
191
+ errors.push(error);
192
+ }
193
+ }
194
+ if (errors.length > 0) {
195
+ this.logger.warn({
196
+ error: formatError(errors[0]),
197
+ event: "sync.delta.publish_partial_failure",
198
+ failureCount: errors.length,
199
+ }, "Delta publish failed for one or more publishers");
200
+ }
201
+ if (successCount === 0) {
202
+ throw normalizeError(errors[0] ?? new Error("Delta publish failed"));
203
+ }
204
+ }
205
+ async publishMany(actions, groups) {
206
+ for (const action of actions) {
207
+ await this.publish(action, groups);
208
+ }
209
+ }
210
+ }
211
+ export const createDeltaPublisher = (redis, sourceId) => new DeltaPublisher(redis, sourceId);
212
+ export const createDeltaSubscriber = (redis, sourceId) => new DeltaSubscriber(redis, sourceId);
213
+ export const createInMemoryDeltaBus = () => new InMemoryDeltaBus();
214
+ export const createInMemoryDeltaPublisher = (bus) => new InMemoryDeltaPublisher(bus);
215
+ export const createInMemoryDeltaSubscriber = (bus) => new InMemoryDeltaSubscriber(bus);
216
+ export const createCompositeDeltaPublisher = (publishers, logger = noopLogger) => new CompositeDeltaPublisher(publishers, logger);
217
+ //# sourceMappingURL=delta-publisher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta-publisher.js","sourceRoot":"","sources":["../../src/delta/delta-publisher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { SyncLogger } from "../config.js";
2
+ import type { SyncDao } from "../dao/sync-dao.js";
3
+ import type { DeltaPacket, SyncUserContext } from "../types.js";
4
+ /**
5
+ * Service for fetching sync deltas.
6
+ */
7
+ export declare class DeltaService {
8
+ private readonly dao;
9
+ private readonly logger;
10
+ constructor(dao: SyncDao, logger?: SyncLogger);
11
+ fetchDeltas(context: SyncUserContext, afterSyncId: bigint, limit: number): Promise<DeltaPacket>;
12
+ }
13
+ //# sourceMappingURL=delta-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta-service.d.ts","sourceRoot":"","sources":["../../src/delta/delta-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGhE;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAU;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;gBAExB,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAuB;IAKnD,WAAW,CACf,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,CAAC;CAoDxB"}
@@ -0,0 +1,36 @@
1
+ import { noopLogger } from "../config.js";
2
+ import { serializeSyncId, toSyncActionOutput } from "../utils/sync-utils.js";
3
+ /**
4
+ * Service for fetching sync deltas.
5
+ */
6
+ export class DeltaService {
7
+ dao;
8
+ logger;
9
+ constructor(dao, logger = noopLogger) {
10
+ this.dao = dao;
11
+ this.logger = logger;
12
+ }
13
+ async fetchDeltas(context, afterSyncId, limit) {
14
+ this.logger.debug({ afterSyncId: String(afterSyncId), limit, userId: context.userId }, "Fetching deltas");
15
+ const actions = await this.dao.getSyncActions(afterSyncId, context.groups, limit + 1);
16
+ const hasMore = actions.length > limit;
17
+ const resultActions = hasMore ? actions.slice(0, limit) : actions;
18
+ const lastAction = resultActions.at(-1);
19
+ const lastSyncId = lastAction
20
+ ? lastAction.id
21
+ : afterSyncId;
22
+ const outputActions = resultActions.map((action) => toSyncActionOutput(action));
23
+ this.logger.debug({
24
+ actionsCount: outputActions.length,
25
+ hasMore,
26
+ lastSyncId: serializeSyncId(lastSyncId),
27
+ userId: context.userId,
28
+ }, "Deltas fetched");
29
+ return {
30
+ actions: outputActions,
31
+ hasMore,
32
+ lastSyncId: serializeSyncId(lastSyncId),
33
+ };
34
+ }
35
+ }
36
+ //# sourceMappingURL=delta-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta-service.js","sourceRoot":"","sources":["../../src/delta/delta-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE7E;;GAEG;AACH,MAAM,OAAO,YAAY;IACN,GAAG,CAAU;IACb,MAAM,CAAa;IAEpC,YAAY,GAAY,EAAE,SAAqB,UAAU;QACvD,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,WAAW,CACf,OAAwB,EACxB,WAAmB,EACnB,KAAa;QAEb,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EACnE,iBAAiB,CAClB,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,cAAc,CAC3C,WAAW,EACX,OAAO,CAAC,MAAM,EACd,KAAK,GAAG,CAAC,CACV,CAAC;QAEF,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;QACvC,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAElE,MAAM,UAAU,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,UAAU;YAC3B,CAAC,CAAE,UAA6B,CAAC,EAAE;YACnC,CAAC,CAAC,WAAW,CAAC;QAEhB,MAAM,aAAa,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACjD,kBAAkB,CAChB,MAUC,CACF,CACF,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,KAAK,CACf;YACE,YAAY,EAAE,aAAa,CAAC,MAAM;YAClC,OAAO;YACP,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC;YACvC,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,EACD,gBAAgB,CACjB,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,aAAa;YACtB,OAAO;YACP,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC;SACxC,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ export { createSyncAuthMiddleware, getSyncUser, validateBody, validateQuery, } from "./middleware.js";
2
+ export type { SyncAuthenticatedRequest } from "./middleware.js";
3
+ export { registerSyncRoutes } from "./routes.js";
4
+ export { BatchLoadBodySchema, BootstrapQuerySchema, DeltaQuerySchema, MutateBodySchema, } from "./validation.js";
5
+ export type { BatchLoadBody, BootstrapQuery, DeltaQuery, MutateBody, } from "./validation.js";
6
+ export { registerSyncWebsocket } from "../websocket/sync-websocket.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fastify/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,WAAW,EACX,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,aAAa,EACb,cAAc,EACd,UAAU,EACV,UAAU,GACX,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createSyncAuthMiddleware, getSyncUser, validateBody, validateQuery, } from "./middleware.js";
2
+ export { registerSyncRoutes } from "./routes.js";
3
+ export { BatchLoadBodySchema, BootstrapQuerySchema, DeltaQuerySchema, MutateBodySchema, } from "./validation.js";
4
+ export { registerSyncWebsocket } from "../websocket/sync-websocket.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fastify/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,WAAW,EACX,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { FastifyReply, FastifyRequest } from "fastify";
2
+ import type { ZodType } from "zod";
3
+ import type { SyncAuthConfig, SyncLogger } from "../config.js";
4
+ import type { SyncDao } from "../dao/sync-dao.js";
5
+ import type { SyncUserContext } from "../types.js";
6
+ export interface SyncAuthenticatedRequest extends FastifyRequest {
7
+ syncUser: SyncUserContext;
8
+ }
9
+ type SyncAuthMiddleware = (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
10
+ export declare const createSyncAuthMiddleware: (auth: SyncAuthConfig, syncDao: SyncDao, logger?: SyncLogger) => SyncAuthMiddleware;
11
+ export declare const getSyncUser: (request: FastifyRequest) => SyncUserContext;
12
+ type PreHandlerHook = (request: FastifyRequest, reply: FastifyReply, done: (err?: Error) => void) => void;
13
+ export declare const validateBody: <T>(schema: ZodType<T>) => PreHandlerHook;
14
+ export declare const validateQuery: <T>(schema: ZodType<T>) => PreHandlerHook;
15
+ export {};
16
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/fastify/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEnC,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC9D,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,KAAK,kBAAkB,GAAG,CACxB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,KAChB,OAAO,CAAC,IAAI,CAAC,CAAC;AAanB,eAAO,MAAM,wBAAwB,GACnC,MAAM,cAAc,EACpB,SAAS,OAAO,EAChB,SAAQ,UAAuB,KAC9B,kBAwDA,CAAC;AAEJ,eAAO,MAAM,WAAW,GAAI,SAAS,cAAc,KAAG,eAMrD,CAAC;AAgDF,KAAK,cAAc,GAAG,CACpB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,KACxB,IAAI,CAAC;AAEV,eAAO,MAAM,YAAY,GACtB,CAAC,EAAE,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAG,cAQxB,CAAC;AAEJ,eAAO,MAAM,aAAa,GACvB,CAAC,EAAE,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAG,cAaxB,CAAC"}
@@ -0,0 +1,101 @@
1
+ import { z } from "zod";
2
+ import { noopLogger } from "../config.js";
3
+ const extractBearerToken = (authHeader) => {
4
+ if (!authHeader) {
5
+ return null;
6
+ }
7
+ const parts = authHeader.trim().split(" ");
8
+ if (parts.length !== 2 || parts[0] !== "Bearer") {
9
+ return null;
10
+ }
11
+ return parts[1]?.trim() ?? null;
12
+ };
13
+ export const createSyncAuthMiddleware = (auth, syncDao, logger = noopLogger) => async function syncAuthMiddleware(request, reply) {
14
+ logger.debug({ url: request.url }, "Sync auth middleware started");
15
+ const authHeader = request.headers.authorization ?? null;
16
+ if (!authHeader) {
17
+ logger.debug({ url: request.url }, "Sync auth rejected: no auth header");
18
+ reply.code(401).send({ error: "Authorization header required" });
19
+ return;
20
+ }
21
+ const token = extractBearerToken(authHeader);
22
+ if (!token) {
23
+ logger.debug({ url: request.url }, "Sync auth rejected: invalid format");
24
+ reply.code(401).send({ error: "Invalid authorization format" });
25
+ return;
26
+ }
27
+ try {
28
+ const authPayload = await auth.verifyToken(token);
29
+ if (!authPayload) {
30
+ reply.code(401).send({ error: "Invalid token" });
31
+ return;
32
+ }
33
+ const { userId } = authPayload;
34
+ const groupsFromDb = await syncDao.getUserGroups(userId);
35
+ const groups = [...new Set([...groupsFromDb, userId])];
36
+ const syncUser = {
37
+ email: authPayload.email,
38
+ groups,
39
+ name: authPayload.name,
40
+ userId,
41
+ };
42
+ request.syncUser = syncUser;
43
+ logger.debug({ userId: syncUser.userId }, "Sync auth middleware complete");
44
+ }
45
+ catch (error) {
46
+ logger.warn({ error }, "Sync auth failed");
47
+ if (error instanceof Error && error.message.includes("expired")) {
48
+ reply.code(401).send({ error: "Token expired" });
49
+ return;
50
+ }
51
+ reply.code(401).send({ error: "Invalid token" });
52
+ }
53
+ };
54
+ export const getSyncUser = (request) => {
55
+ const syncRequest = request;
56
+ if (!syncRequest.syncUser) {
57
+ throw new Error("Sync user context not found");
58
+ }
59
+ return syncRequest.syncUser;
60
+ };
61
+ const formatZodError = (error) => {
62
+ const flattened = z.flattenError(error);
63
+ const errors = [];
64
+ for (const message of flattened.formErrors) {
65
+ errors.push({ field: "root", message });
66
+ }
67
+ const fieldErrors = flattened.fieldErrors;
68
+ for (const [field, messages] of Object.entries(fieldErrors)) {
69
+ if (Array.isArray(messages)) {
70
+ for (const message of messages) {
71
+ errors.push({ field, message });
72
+ }
73
+ }
74
+ }
75
+ return errors;
76
+ };
77
+ const sendValidationError = (request, reply, errors, source) => {
78
+ reply.code(400).send({
79
+ details: errors,
80
+ error: "Validation failed",
81
+ requestId: request.id,
82
+ source,
83
+ });
84
+ };
85
+ export const validateBody = (schema) => (request, reply, done) => {
86
+ const result = schema.safeParse(request.body);
87
+ if (!result.success) {
88
+ sendValidationError(request, reply, formatZodError(result.error), "body");
89
+ return;
90
+ }
91
+ done();
92
+ };
93
+ export const validateQuery = (schema) => (request, reply, done) => {
94
+ const result = schema.safeParse(request.query);
95
+ if (!result.success) {
96
+ sendValidationError(request, reply, formatZodError(result.error), "query");
97
+ return;
98
+ }
99
+ done();
100
+ };
101
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/fastify/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
2
+ import type { BootstrapService } from "../bootstrap/bootstrap-service.js";
3
+ import type { SyncLogger } from "../config.js";
4
+ import type { DeltaPublisherLike } from "../delta/delta-publisher.js";
5
+ import type { DeltaService } from "../delta/delta-service.js";
6
+ import { MutateService } from "../mutate/mutate-service.js";
7
+ interface RegisterRoutesOptions {
8
+ bootstrapService: BootstrapService;
9
+ deltaService: DeltaService;
10
+ mutateService: MutateService;
11
+ deltaPublisher?: DeltaPublisherLike;
12
+ authMiddleware: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
13
+ logger?: SyncLogger;
14
+ }
15
+ export declare const registerSyncRoutes: (server: FastifyInstance, options: RegisterRoutesOptions) => void;
16
+ export {};
17
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/fastify/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAwB5D,UAAU,qBAAqB;IAC7B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC,cAAc,EAAE,CACd,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,KAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,eAAe,EACvB,SAAS,qBAAqB,KAC7B,IA8NF,CAAC"}
@@ -0,0 +1,150 @@
1
+ import { noopLogger } from "../config.js";
2
+ import { MutateService } from "../mutate/mutate-service.js";
3
+ import { resolvePublishedDeltaGroups, resolveRequestedSyncGroups, } from "../utils/sync-scope.js";
4
+ import { parseSyncIdString } from "../utils/sync-utils.js";
5
+ import { getSyncUser, validateBody, validateQuery } from "./middleware.js";
6
+ import { BatchLoadBodySchema, BootstrapQuerySchema, DeltaQuerySchema, MutateBodySchema, } from "./validation.js";
7
+ const DEFAULT_LIMIT = 1000;
8
+ const MAX_LIMIT = 5000;
9
+ export const registerSyncRoutes = (server, options) => {
10
+ const { authMiddleware, bootstrapService, deltaPublisher, deltaService, logger = noopLogger, mutateService, } = options;
11
+ // GET /sync/bootstrap
12
+ server.get("/sync/bootstrap", { preHandler: [validateQuery(BootstrapQuerySchema), authMiddleware] }, async (request, reply) => {
13
+ const syncUser = getSyncUser(request);
14
+ const { query } = request;
15
+ const onlyModels = query.onlyModels?.split(",").filter(Boolean);
16
+ const requestedGroups = query.syncGroups?.split(",").filter(Boolean);
17
+ const syncGroups = resolveRequestedSyncGroups(syncUser.groups, requestedGroups);
18
+ const { schemaHash } = query;
19
+ const origin = request.headers.origin || "*";
20
+ reply.raw.writeHead(200, {
21
+ "Access-Control-Allow-Credentials": "true",
22
+ "Access-Control-Allow-Origin": origin,
23
+ "Cache-Control": "no-cache",
24
+ Connection: "keep-alive",
25
+ "Content-Type": "application/x-ndjson",
26
+ "Transfer-Encoding": "chunked",
27
+ });
28
+ try {
29
+ for await (const line of bootstrapService.generateBootstrapNdjson(syncUser, {
30
+ groups: syncGroups,
31
+ models: onlyModels,
32
+ schemaHash: schemaHash ?? "",
33
+ })) {
34
+ reply.raw.write(`${line}\n`);
35
+ }
36
+ }
37
+ catch (error) {
38
+ reply.raw.write(`${JSON.stringify({
39
+ message: error instanceof Error ? error.message : "Unknown error",
40
+ type: "error",
41
+ })}\n`);
42
+ }
43
+ reply.raw.end();
44
+ });
45
+ // POST /sync/batch
46
+ server.post("/sync/batch", { preHandler: [validateBody(BatchLoadBodySchema), authMiddleware] }, async (request, reply) => {
47
+ const syncUser = getSyncUser(request);
48
+ const { firstSyncId, requests } = request.body;
49
+ const origin = request.headers.origin || "*";
50
+ reply.raw.writeHead(200, {
51
+ "Access-Control-Allow-Credentials": "true",
52
+ "Access-Control-Allow-Origin": origin,
53
+ "Cache-Control": "no-cache",
54
+ "Content-Type": "application/x-ndjson",
55
+ "Transfer-Encoding": "chunked",
56
+ });
57
+ try {
58
+ for await (const line of bootstrapService.batchLoadNdjson(syncUser, requests, firstSyncId)) {
59
+ reply.raw.write(`${line}\n`);
60
+ }
61
+ }
62
+ catch (error) {
63
+ reply.raw.write(`${JSON.stringify({
64
+ message: error instanceof Error ? error.message : "Unknown error",
65
+ type: "error",
66
+ })}\n`);
67
+ }
68
+ reply.raw.end();
69
+ });
70
+ // GET /sync/deltas
71
+ server.get("/sync/deltas", { preHandler: [validateQuery(DeltaQuerySchema), authMiddleware] }, async (request, reply) => {
72
+ const syncUser = getSyncUser(request);
73
+ const syncGroups = resolveRequestedSyncGroups(syncUser.groups, request.query.syncGroups?.split(",").filter(Boolean));
74
+ const afterSyncId = request.query.after
75
+ ? parseSyncIdString(request.query.after)
76
+ : 0n;
77
+ let limit = request.query.limit
78
+ ? Number.parseInt(request.query.limit, 10)
79
+ : DEFAULT_LIMIT;
80
+ if (Number.isNaN(limit) || limit < 1) {
81
+ limit = DEFAULT_LIMIT;
82
+ }
83
+ else if (limit > MAX_LIMIT) {
84
+ limit = MAX_LIMIT;
85
+ }
86
+ const packet = await deltaService.fetchDeltas({
87
+ ...syncUser,
88
+ groups: syncGroups,
89
+ }, afterSyncId, limit);
90
+ return reply.send({
91
+ actions: packet.actions.map((action) => ({
92
+ action: action.action,
93
+ clientId: action.clientId,
94
+ clientTxId: action.clientTxId,
95
+ createdAt: action.createdAt.toISOString(),
96
+ data: action.data,
97
+ groupId: action.groupId,
98
+ modelId: action.modelId,
99
+ modelName: action.modelName,
100
+ syncId: action.syncId,
101
+ })),
102
+ hasMore: packet.hasMore,
103
+ lastSyncId: packet.lastSyncId,
104
+ });
105
+ });
106
+ // POST /sync/mutate
107
+ server.post("/sync/mutate", { preHandler: [validateBody(MutateBodySchema), authMiddleware] }, async (request, reply) => {
108
+ const syncUser = getSyncUser(request);
109
+ const { batchId, transactions } = request.body;
110
+ const transactionSummaries = transactions.map((tx) => ({
111
+ action: tx.action,
112
+ clientId: tx.clientId,
113
+ clientTxId: tx.clientTxId,
114
+ modelId: tx.modelId,
115
+ modelName: tx.modelName,
116
+ payloadKeys: Object.keys(tx.payload ?? {}),
117
+ }));
118
+ logger.debug({
119
+ batchId,
120
+ transactionCount: transactions.length,
121
+ transactions: transactionSummaries,
122
+ }, "Sync mutate request received");
123
+ for (const tx of transactions) {
124
+ const errors = MutateService.validateTransaction(tx);
125
+ if (errors.length > 0) {
126
+ return reply.code(400).send({
127
+ clientTxId: tx.clientTxId,
128
+ details: errors,
129
+ error: "Invalid transaction",
130
+ });
131
+ }
132
+ }
133
+ const input = { batchId, transactions };
134
+ const result = await mutateService.mutate(syncUser, input, (action) => {
135
+ if (deltaPublisher) {
136
+ const groups = resolvePublishedDeltaGroups(action.groupId, syncUser.groups);
137
+ deltaPublisher.publish(action, groups).catch((error) => {
138
+ const formattedError = error instanceof Error ? error : new Error(String(error));
139
+ logger.error({ err: formattedError }, "Failed to publish delta");
140
+ });
141
+ }
142
+ });
143
+ return reply.send({
144
+ lastSyncId: result.lastSyncId,
145
+ results: result.results,
146
+ success: result.success,
147
+ });
148
+ });
149
+ };
150
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/fastify/routes.ts"],"names":[],"mappings":"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"}