@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.
- package/README.md +188 -0
- package/dist/bootstrap/bootstrap-service.d.ts +41 -0
- package/dist/bootstrap/bootstrap-service.d.ts.map +1 -0
- package/dist/bootstrap/bootstrap-service.js +411 -0
- package/dist/bootstrap/bootstrap-service.js.map +1 -0
- package/dist/config.d.ts +124 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -0
- package/dist/create-sync-server.d.ts +5 -0
- package/dist/create-sync-server.d.ts.map +1 -0
- package/dist/create-sync-server.js +96 -0
- package/dist/create-sync-server.js.map +1 -0
- package/dist/dao/sync-dao.d.ts +64 -0
- package/dist/dao/sync-dao.d.ts.map +1 -0
- package/dist/dao/sync-dao.js +137 -0
- package/dist/dao/sync-dao.js.map +1 -0
- package/dist/db.d.ts +37 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +2 -0
- package/dist/db.js.map +1 -0
- package/dist/delta/delta-publisher.d.ts +52 -0
- package/dist/delta/delta-publisher.d.ts.map +1 -0
- package/dist/delta/delta-publisher.js +217 -0
- package/dist/delta/delta-publisher.js.map +1 -0
- package/dist/delta/delta-service.d.ts +13 -0
- package/dist/delta/delta-service.d.ts.map +1 -0
- package/dist/delta/delta-service.js +36 -0
- package/dist/delta/delta-service.js.map +1 -0
- package/dist/fastify/index.d.ts +7 -0
- package/dist/fastify/index.d.ts.map +1 -0
- package/dist/fastify/index.js +5 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/fastify/middleware.d.ts +16 -0
- package/dist/fastify/middleware.d.ts.map +1 -0
- package/dist/fastify/middleware.js +101 -0
- package/dist/fastify/middleware.js.map +1 -0
- package/dist/fastify/routes.d.ts +17 -0
- package/dist/fastify/routes.d.ts.map +1 -0
- package/dist/fastify/routes.js +150 -0
- package/dist/fastify/routes.js.map +1 -0
- package/dist/fastify/validation.d.ts +59 -0
- package/dist/fastify/validation.d.ts.map +1 -0
- package/dist/fastify/validation.js +79 -0
- package/dist/fastify/validation.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/mutate/archive-mutation.d.ts +10 -0
- package/dist/mutate/archive-mutation.d.ts.map +1 -0
- package/dist/mutate/archive-mutation.js +14 -0
- package/dist/mutate/archive-mutation.js.map +1 -0
- package/dist/mutate/field-codecs.d.ts +15 -0
- package/dist/mutate/field-codecs.d.ts.map +1 -0
- package/dist/mutate/field-codecs.js +103 -0
- package/dist/mutate/field-codecs.js.map +1 -0
- package/dist/mutate/model-handlers.d.ts +32 -0
- package/dist/mutate/model-handlers.d.ts.map +1 -0
- package/dist/mutate/model-handlers.js +131 -0
- package/dist/mutate/model-handlers.js.map +1 -0
- package/dist/mutate/mutate-service.d.ts +30 -0
- package/dist/mutate/mutate-service.d.ts.map +1 -0
- package/dist/mutate/mutate-service.js +326 -0
- package/dist/mutate/mutate-service.js.map +1 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +29 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/composite-ids.d.ts +14 -0
- package/dist/utils/composite-ids.d.ts.map +1 -0
- package/dist/utils/composite-ids.js +15 -0
- package/dist/utils/composite-ids.js.map +1 -0
- package/dist/utils/dates.d.ts +8 -0
- package/dist/utils/dates.d.ts.map +1 -0
- package/dist/utils/dates.js +101 -0
- package/dist/utils/dates.js.map +1 -0
- package/dist/utils/sync-scope.d.ts +4 -0
- package/dist/utils/sync-scope.d.ts.map +1 -0
- package/dist/utils/sync-scope.js +17 -0
- package/dist/utils/sync-scope.js.map +1 -0
- package/dist/utils/sync-utils.d.ts +27 -0
- package/dist/utils/sync-utils.d.ts.map +1 -0
- package/dist/utils/sync-utils.js +94 -0
- package/dist/utils/sync-utils.js.map +1 -0
- package/dist/websocket/sync-websocket.d.ts +14 -0
- package/dist/websocket/sync-websocket.d.ts.map +1 -0
- package/dist/websocket/sync-websocket.js +326 -0
- package/dist/websocket/sync-websocket.js.map +1 -0
- 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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"}
|