@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,17 @@
1
+ export const resolveRequestedSyncGroups = (authorizedGroups, requestedGroups) => {
2
+ if (!requestedGroups || requestedGroups.length === 0) {
3
+ return [...authorizedGroups];
4
+ }
5
+ const allowedGroups = new Set(authorizedGroups);
6
+ return requestedGroups.filter((group) => allowedGroups.has(group));
7
+ };
8
+ export const resolvePublishedDeltaGroups = (groupId, fallbackGroups) => {
9
+ if (groupId) {
10
+ return [groupId];
11
+ }
12
+ return [...fallbackGroups];
13
+ };
14
+ export const dedupeSyncGroups = (groups) => [
15
+ ...new Set(groups),
16
+ ];
17
+ //# sourceMappingURL=sync-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-scope.js","sourceRoot":"","sources":["../../src/utils/sync-scope.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,gBAA0B,EAC1B,eAA0B,EAChB,EAAE;IACZ,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,CAAC,GAAG,gBAAgB,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAChD,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AACrE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,OAAkC,EAClC,cAAwB,EACd,EAAE;IACZ,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC;IAED,OAAO,CAAC,GAAG,cAAc,CAAC,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAgB,EAAY,EAAE,CAAC;IAC9D,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC;CACnB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { AnyPgColumn, AnyPgTable } from "drizzle-orm/pg-core";
2
+ import type { SerializedSyncActionOutput, SyncActionOutput } from "../types.js";
3
+ /**
4
+ * Gets a typed column reference from a Drizzle table by column name.
5
+ */
6
+ export declare const getColumn: (table: AnyPgTable, columnName: string) => AnyPgColumn;
7
+ interface RawSyncAction {
8
+ id: bigint;
9
+ model: string;
10
+ modelId: string;
11
+ action: string;
12
+ data: unknown;
13
+ groupId: string | null;
14
+ clientTxId: string | null;
15
+ clientId: string | null;
16
+ createdAt: Date;
17
+ }
18
+ export declare const serializeSyncId: (syncId: bigint) => string;
19
+ export declare const parseSyncIdString: (syncId: string) => bigint;
20
+ /**
21
+ * Converts a raw database sync action to the output format.
22
+ */
23
+ export declare const toSyncActionOutput: (action: RawSyncAction) => SyncActionOutput;
24
+ export declare const serializeSyncActionOutput: (action: SyncActionOutput) => SerializedSyncActionOutput;
25
+ export declare const parseSyncActionOutput: (raw: unknown) => SyncActionOutput;
26
+ export {};
27
+ //# sourceMappingURL=sync-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-utils.d.ts","sourceRoot":"","sources":["../../src/utils/sync-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIhF;;GAEG;AACH,eAAO,MAAM,SAAS,GACpB,OAAO,UAAU,EACjB,YAAY,MAAM,KACjB,WAUF,CAAC;AAEF,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;CACjB;AAKD,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAA2B,CAAC;AAE7E,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,MAKlD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,aAAa,KACpB,gBAUD,CAAC;AAEH,eAAO,MAAM,yBAAyB,GACpC,QAAQ,gBAAgB,KACvB,0BAUD,CAAC;AAEH,eAAO,MAAM,qBAAqB,GAAI,KAAK,OAAO,KAAG,gBAqDpD,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { getTableColumns } from "drizzle-orm";
2
+ const NON_NEGATIVE_INTEGER_REGEX = /^\d+$/;
3
+ /**
4
+ * Gets a typed column reference from a Drizzle table by column name.
5
+ */
6
+ export const getColumn = (table, columnName) => {
7
+ const columnMap = getTableColumns(table);
8
+ const column = columnMap[columnName];
9
+ if (!column) {
10
+ throw new Error(`Column ${columnName} not found on table ${table._.name}`);
11
+ }
12
+ return column;
13
+ };
14
+ const isRecord = (value) => typeof value === "object" && value !== null;
15
+ export const serializeSyncId = (syncId) => syncId.toString();
16
+ export const parseSyncIdString = (syncId) => {
17
+ if (!NON_NEGATIVE_INTEGER_REGEX.test(syncId)) {
18
+ throw new Error(`Invalid syncId: ${syncId}`);
19
+ }
20
+ return BigInt(syncId);
21
+ };
22
+ /**
23
+ * Converts a raw database sync action to the output format.
24
+ */
25
+ export const toSyncActionOutput = (action) => ({
26
+ action: action.action,
27
+ clientId: action.clientId ?? undefined,
28
+ clientTxId: action.clientTxId ?? undefined,
29
+ createdAt: action.createdAt,
30
+ data: action.data ?? {},
31
+ groupId: action.groupId ?? undefined,
32
+ modelId: action.modelId,
33
+ modelName: action.model,
34
+ syncId: serializeSyncId(action.id),
35
+ });
36
+ export const serializeSyncActionOutput = (action) => ({
37
+ action: action.action,
38
+ clientId: action.clientId,
39
+ clientTxId: action.clientTxId,
40
+ createdAt: action.createdAt.toISOString(),
41
+ data: action.data,
42
+ groupId: action.groupId,
43
+ modelId: action.modelId,
44
+ modelName: action.modelName,
45
+ syncId: action.syncId,
46
+ });
47
+ export const parseSyncActionOutput = (raw) => {
48
+ if (!isRecord(raw)) {
49
+ throw new Error("Sync action must be an object");
50
+ }
51
+ const { action, createdAt, data, modelId, modelName, syncId } = raw;
52
+ if (typeof action !== "string") {
53
+ throw new TypeError("Sync action action must be a string");
54
+ }
55
+ if (typeof createdAt !== "string") {
56
+ throw new TypeError("Sync action createdAt must be a string");
57
+ }
58
+ if (!isRecord(data)) {
59
+ throw new Error("Sync action data must be an object");
60
+ }
61
+ if (typeof modelId !== "string") {
62
+ throw new TypeError("Sync action modelId must be a string");
63
+ }
64
+ if (typeof modelName !== "string") {
65
+ throw new TypeError("Sync action modelName must be a string");
66
+ }
67
+ if (typeof syncId !== "string") {
68
+ throw new TypeError("Sync action syncId must be a string");
69
+ }
70
+ parseSyncIdString(syncId);
71
+ const createdAtDate = new Date(createdAt);
72
+ if (Number.isNaN(createdAtDate.getTime())) {
73
+ throw new TypeError("Sync action createdAt must be a valid date");
74
+ }
75
+ const parsed = {
76
+ action,
77
+ createdAt: createdAtDate,
78
+ data,
79
+ modelId,
80
+ modelName,
81
+ syncId,
82
+ };
83
+ if (typeof raw.groupId === "string") {
84
+ parsed.groupId = raw.groupId;
85
+ }
86
+ if (typeof raw.clientTxId === "string") {
87
+ parsed.clientTxId = raw.clientTxId;
88
+ }
89
+ if (typeof raw.clientId === "string") {
90
+ parsed.clientId = raw.clientId;
91
+ }
92
+ return parsed;
93
+ };
94
+ //# sourceMappingURL=sync-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-utils.js","sourceRoot":"","sources":["../../src/utils/sync-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type { SyncAuthConfig, SyncLogger, WebSocketHooks } from "../config.js";
3
+ import type { SyncDao } from "../dao/sync-dao.js";
4
+ import type { DeltaSubscriberLike } from "../delta/delta-publisher.js";
5
+ interface RegisterWebSocketOptions {
6
+ auth: SyncAuthConfig;
7
+ syncDao: SyncDao;
8
+ deltaSubscriber?: DeltaSubscriberLike;
9
+ hooks?: WebSocketHooks;
10
+ logger?: SyncLogger;
11
+ }
12
+ export declare const registerSyncWebsocket: (server: FastifyInstance, options: RegisterWebSocketOptions) => void;
13
+ export {};
14
+ //# sourceMappingURL=sync-websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-websocket.d.ts","sourceRoot":"","sources":["../../src/websocket/sync-websocket.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/C,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AA0SvE,UAAU,wBAAwB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,eAAO,MAAM,qBAAqB,GAChC,QAAQ,eAAe,EACvB,SAAS,wBAAwB,KAChC,IAwQF,CAAC"}
@@ -0,0 +1,326 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { noopLogger } from "../config.js";
3
+ import { safeJsonStringify } from "../delta/delta-publisher.js";
4
+ import { dedupeSyncGroups, resolveRequestedSyncGroups, } from "../utils/sync-scope.js";
5
+ import { parseSyncIdString, serializeSyncId, toSyncActionOutput, } from "../utils/sync-utils.js";
6
+ const HEARTBEAT_INTERVAL_MS = 30_000;
7
+ const NON_NEGATIVE_INTEGER_REGEX = /^\d+$/;
8
+ const hasGroupOverlap = (clientGroups, deltaGroups) => {
9
+ if (deltaGroups.length === 0) {
10
+ return true;
11
+ }
12
+ return clientGroups.some((group) => deltaGroups.includes(group));
13
+ };
14
+ const isRecord = (value) => typeof value === "object" && value !== null;
15
+ const isNonNegativeIntegerString = (value) => NON_NEGATIVE_INTEGER_REGEX.test(value);
16
+ const isSubscribeMessage = (msg) => {
17
+ if (typeof msg !== "object" || msg === null) {
18
+ return false;
19
+ }
20
+ const record = msg;
21
+ if (record.type !== "subscribe") {
22
+ return false;
23
+ }
24
+ if (typeof record.token !== "string") {
25
+ return false;
26
+ }
27
+ const { afterSyncId } = record;
28
+ const { groups } = record;
29
+ const groupsValid = groups === undefined ||
30
+ (Array.isArray(groups) &&
31
+ groups.every((group) => typeof group === "string"));
32
+ if (!groupsValid) {
33
+ return false;
34
+ }
35
+ return (afterSyncId === undefined ||
36
+ (typeof afterSyncId === "string" && isNonNegativeIntegerString(afterSyncId)));
37
+ };
38
+ const compareSyncActionOutput = (left, right) => {
39
+ const leftId = parseSyncIdString(left.syncId);
40
+ const rightId = parseSyncIdString(right.syncId);
41
+ if (leftId === rightId) {
42
+ return 0;
43
+ }
44
+ return leftId < rightId ? -1 : 1;
45
+ };
46
+ const unsubscribeClient = (state) => {
47
+ if (!state.unsubscribe) {
48
+ return;
49
+ }
50
+ state.unsubscribe();
51
+ state.unsubscribe = null;
52
+ };
53
+ const resetClientAuthState = (state) => {
54
+ state.authenticated = false;
55
+ state.userId = null;
56
+ state.groups = [];
57
+ state.afterSyncId = "0";
58
+ state.replaying = false;
59
+ state.bufferedActions = [];
60
+ };
61
+ const setSubscribedState = (state, userId, groups, afterSyncId) => {
62
+ state.userId = userId;
63
+ state.groups = groups;
64
+ state.authenticated = true;
65
+ state.afterSyncId = afterSyncId;
66
+ state.replaying = true;
67
+ state.bufferedActions = [];
68
+ };
69
+ const resolveSubscribeGroups = (authorizedGroups, requestedGroups) => {
70
+ const dedupedAuthorizedGroups = dedupeSyncGroups(authorizedGroups);
71
+ const dedupedRequestedGroups = requestedGroups
72
+ ? dedupeSyncGroups(requestedGroups)
73
+ : undefined;
74
+ const allowedGroups = new Set(dedupedAuthorizedGroups);
75
+ const groups = resolveRequestedSyncGroups(dedupedAuthorizedGroups, dedupedRequestedGroups);
76
+ const rejectedGroups = dedupedRequestedGroups
77
+ ? dedupedRequestedGroups.filter((group) => !allowedGroups.has(group))
78
+ : [];
79
+ return { groups, rejectedGroups };
80
+ };
81
+ const sendSubscribedMessage = (ws, state) => {
82
+ ws.send(JSON.stringify({
83
+ afterSyncId: state.afterSyncId,
84
+ groups: state.groups,
85
+ type: "subscribed",
86
+ }));
87
+ };
88
+ const bufferOrSendLiveDelta = (ws, state, action, groups, sendDeltaAction) => {
89
+ if (!hasGroupOverlap(state.groups, groups)) {
90
+ return;
91
+ }
92
+ const { syncId } = action;
93
+ if (parseSyncIdString(syncId) <= parseSyncIdString(state.afterSyncId)) {
94
+ return;
95
+ }
96
+ if (state.replaying) {
97
+ state.bufferedActions.push({ action, groups });
98
+ return;
99
+ }
100
+ sendDeltaAction(ws, state, action);
101
+ };
102
+ const installDeltaSubscription = (ws, state, deltaSubscriber, sendDeltaAction) => {
103
+ if (!deltaSubscriber) {
104
+ return;
105
+ }
106
+ state.unsubscribe = deltaSubscriber.onDelta((action, groups) => {
107
+ bufferOrSendLiveDelta(ws, state, action, groups, sendDeltaAction);
108
+ });
109
+ };
110
+ const replaySyncActions = async (syncDao, ws, state, sendDeltaAction) => {
111
+ let replayCursor = state.afterSyncId;
112
+ while (true) {
113
+ const actions = await syncDao.getSyncActions(parseSyncIdString(replayCursor), state.groups, 1000);
114
+ if (actions.length === 0) {
115
+ break;
116
+ }
117
+ for (const action of actions) {
118
+ sendDeltaAction(ws, state, toSyncActionOutput(action));
119
+ }
120
+ const lastAction = actions.at(-1);
121
+ if (!lastAction || actions.length < 1000) {
122
+ break;
123
+ }
124
+ replayCursor = serializeSyncId(lastAction.id);
125
+ }
126
+ };
127
+ const flushBufferedActions = (ws, state, sendDeltaAction) => {
128
+ state.replaying = false;
129
+ state.bufferedActions.sort((left, right) => compareSyncActionOutput(left.action, right.action));
130
+ for (const entry of state.bufferedActions) {
131
+ if (!hasGroupOverlap(state.groups, entry.groups)) {
132
+ continue;
133
+ }
134
+ sendDeltaAction(ws, state, entry.action);
135
+ }
136
+ state.bufferedActions = [];
137
+ };
138
+ export const registerSyncWebsocket = (server, options) => {
139
+ const { auth, deltaSubscriber, hooks, logger = noopLogger, syncDao, } = options;
140
+ // Use route registration that is compatible with @fastify/websocket
141
+ // The `websocket: true` option and handler signature come from the plugin
142
+ server.get("/sync/ws", {
143
+ preValidation: async (request, reply) => {
144
+ const token = request.query?.token;
145
+ if (!token) {
146
+ reply.code(401).send({ error: "Token required" });
147
+ return;
148
+ }
149
+ const payload = await auth.verifyToken(token);
150
+ if (!payload) {
151
+ reply.code(401).send({ error: "Invalid token" });
152
+ }
153
+ },
154
+ websocket: true,
155
+ }, (socket) => {
156
+ const connectionId = randomUUID();
157
+ const clientState = {
158
+ afterSyncId: "0",
159
+ authenticated: false,
160
+ bufferedActions: [],
161
+ groups: [],
162
+ replaying: false,
163
+ subscribePromise: null,
164
+ unsubscribe: null,
165
+ userId: null,
166
+ };
167
+ const heartbeatInterval = setInterval(() => {
168
+ if (socket.readyState === socket.OPEN) {
169
+ socket.ping();
170
+ }
171
+ }, HEARTBEAT_INTERVAL_MS);
172
+ const getConnectionContext = () => ({
173
+ connId: connectionId,
174
+ groups: clientState.groups,
175
+ userId: clientState.userId ?? `anonymous-${connectionId}`,
176
+ });
177
+ const sendDeltaAction = (ws, state, action) => {
178
+ const { syncId } = action;
179
+ if (parseSyncIdString(syncId) <= parseSyncIdString(state.afterSyncId)) {
180
+ return;
181
+ }
182
+ state.afterSyncId = syncId;
183
+ const deltaMessage = {
184
+ packet: {
185
+ actions: [
186
+ {
187
+ action: action.action,
188
+ clientId: action.clientId,
189
+ clientTxId: action.clientTxId,
190
+ createdAt: action.createdAt.toISOString(),
191
+ data: action.data,
192
+ groupId: action.groupId,
193
+ modelId: action.modelId,
194
+ modelName: action.modelName,
195
+ syncId,
196
+ },
197
+ ],
198
+ lastSyncId: syncId,
199
+ },
200
+ type: "delta",
201
+ };
202
+ if (ws.readyState === ws.OPEN) {
203
+ ws.send(safeJsonStringify(deltaMessage));
204
+ }
205
+ };
206
+ const handleSubscribe = async (ws, state, msg) => {
207
+ const previousContext = getConnectionContext();
208
+ unsubscribeClient(state);
209
+ resetClientAuthState(state);
210
+ if (hooks?.onSubscribe) {
211
+ await hooks.onSubscribe(ws, getConnectionContext(), previousContext);
212
+ }
213
+ const payload = await auth.verifyToken(msg.token);
214
+ if (!payload) {
215
+ ws.send(JSON.stringify({ message: "Invalid token", type: "error" }));
216
+ return;
217
+ }
218
+ const dbGroups = await syncDao.getUserGroups(payload.userId);
219
+ const authorizedGroups = dedupeSyncGroups([
220
+ ...dbGroups,
221
+ payload.userId,
222
+ ]);
223
+ const { groups, rejectedGroups } = resolveSubscribeGroups(authorizedGroups, msg.groups);
224
+ if (rejectedGroups.length > 0) {
225
+ logger.warn({
226
+ authorizedGroups,
227
+ requestedGroups: rejectedGroups,
228
+ userId: payload.userId,
229
+ }, "Rejected unauthorized WebSocket sync groups");
230
+ }
231
+ setSubscribedState(state, payload.userId, groups, msg.afterSyncId ?? "0");
232
+ installDeltaSubscription(ws, state, deltaSubscriber, sendDeltaAction);
233
+ sendSubscribedMessage(ws, state);
234
+ try {
235
+ await replaySyncActions(syncDao, ws, state, sendDeltaAction);
236
+ flushBufferedActions(ws, state, sendDeltaAction);
237
+ }
238
+ catch (replayError) {
239
+ state.replaying = false;
240
+ state.bufferedActions = [];
241
+ throw replayError;
242
+ }
243
+ };
244
+ const handleSubscribeMessage = async (ws, state, msg) => {
245
+ if (state.subscribePromise) {
246
+ await state.subscribePromise;
247
+ }
248
+ const subscribePromise = handleSubscribe(ws, state, msg);
249
+ state.subscribePromise = subscribePromise;
250
+ try {
251
+ await subscribePromise;
252
+ }
253
+ finally {
254
+ if (state.subscribePromise === subscribePromise) {
255
+ state.subscribePromise = null;
256
+ }
257
+ }
258
+ };
259
+ const maybeHandleSubscribeFrame = async (message) => {
260
+ if (isSubscribeMessage(message)) {
261
+ await handleSubscribeMessage(socket, clientState, message);
262
+ return true;
263
+ }
264
+ if (message.type === "subscribe") {
265
+ socket.send(JSON.stringify({
266
+ message: "Authentication required",
267
+ type: "error",
268
+ }));
269
+ return true;
270
+ }
271
+ return false;
272
+ };
273
+ const maybeHandleHookMessage = async (message) => {
274
+ if (!hooks?.onMessage) {
275
+ return false;
276
+ }
277
+ if (!clientState.authenticated) {
278
+ return false;
279
+ }
280
+ return await hooks.onMessage(socket, message, getConnectionContext());
281
+ };
282
+ socket.on("message", async (data) => {
283
+ try {
284
+ const message = JSON.parse(data.toString());
285
+ if (!isRecord(message)) {
286
+ return;
287
+ }
288
+ if (await maybeHandleSubscribeFrame(message)) {
289
+ return;
290
+ }
291
+ if (clientState.subscribePromise) {
292
+ await clientState.subscribePromise;
293
+ }
294
+ await maybeHandleHookMessage(message);
295
+ }
296
+ catch (error) {
297
+ logger.warn({ error }, "WebSocket message handling error");
298
+ }
299
+ });
300
+ const safeRunOnClose = async () => {
301
+ if (!hooks?.onClose) {
302
+ return;
303
+ }
304
+ try {
305
+ await hooks.onClose(socket, getConnectionContext());
306
+ }
307
+ catch {
308
+ // Silently ignore cleanup errors during disconnect
309
+ }
310
+ };
311
+ const cleanupWebSocket = () => {
312
+ clearInterval(heartbeatInterval);
313
+ if (clientState.unsubscribe) {
314
+ clientState.unsubscribe();
315
+ clientState.unsubscribe = null;
316
+ }
317
+ safeRunOnClose();
318
+ };
319
+ socket.on("close", cleanupWebSocket);
320
+ socket.on("error", (error) => {
321
+ logger.warn({ error }, "WebSocket error");
322
+ cleanupWebSocket();
323
+ });
324
+ });
325
+ };
326
+ //# sourceMappingURL=sync-websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-websocket.js","sourceRoot":"","sources":["../../src/websocket/sync-websocket.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@stratasync/server",
3
+ "version": "0.2.0",
4
+ "files": [
5
+ "dist"
6
+ ],
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ },
15
+ "./fastify": {
16
+ "types": "./dist/fastify/index.d.ts",
17
+ "import": "./dist/fastify/index.js"
18
+ }
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "dev": "tsc --watch",
26
+ "lint": "oxlint .",
27
+ "lint:fix": "oxlint --fix .",
28
+ "format": "oxfmt --write .",
29
+ "format:check": "oxfmt --check .",
30
+ "check-types": "tsc --noEmit",
31
+ "test": "vitest run"
32
+ },
33
+ "dependencies": {
34
+ "uuid": "^13.0.0",
35
+ "zod": "^4.3.6"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^25.5.0",
39
+ "@types/ws": "^8.18.1",
40
+ "fastify": "^5.8.2",
41
+ "lefthook": "^2.1.4",
42
+ "oxfmt": "^0.41.0",
43
+ "oxlint": "^1.56.0",
44
+ "redis": "^5.11.0",
45
+ "typescript": "^5.9.3",
46
+ "ultracite": "^7.3.2",
47
+ "vitest": "^4.1.0",
48
+ "ws": "^8.19.0"
49
+ },
50
+ "peerDependencies": {
51
+ "@fastify/websocket": ">=11.0.0",
52
+ "drizzle-orm": ">=1.0.0-beta.1",
53
+ "fastify": ">=5.0.0",
54
+ "redis": ">=5.0.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "fastify": {
58
+ "optional": true
59
+ },
60
+ "redis": {
61
+ "optional": true
62
+ },
63
+ "@fastify/websocket": {
64
+ "optional": true
65
+ },
66
+ "drizzle-orm": {
67
+ "optional": false
68
+ }
69
+ }
70
+ }