@nsky/sync 0.1.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 +280 -0
- package/dist/core/index.cjs +277 -0
- package/dist/core/index.d.cts +2 -0
- package/dist/core/index.d.mts +2 -0
- package/dist/core/index.mjs +272 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/http/index.cjs +184 -0
- package/dist/http/index.d.cts +37 -0
- package/dist/http/index.d.cts.map +1 -0
- package/dist/http/index.d.mts +37 -0
- package/dist/http/index.d.mts.map +1 -0
- package/dist/http/index.mjs +184 -0
- package/dist/http/index.mjs.map +1 -0
- package/dist/index.cjs +23 -0
- package/dist/index.d.cts +56 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +56 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +5 -0
- package/dist/index2.d.cts +168 -0
- package/dist/index2.d.cts.map +1 -0
- package/dist/index2.d.mts +168 -0
- package/dist/index2.d.mts.map +1 -0
- package/dist/index3.d.cts +5 -0
- package/dist/index3.d.mts +5 -0
- package/dist/sql/index.cjs +149 -0
- package/dist/sql/index.d.cts +57 -0
- package/dist/sql/index.d.cts.map +1 -0
- package/dist/sql/index.d.mts +57 -0
- package/dist/sql/index.d.mts.map +1 -0
- package/dist/sql/index.mjs +126 -0
- package/dist/sql/index.mjs.map +1 -0
- package/dist/utils/index.cjs +148 -0
- package/dist/utils/index.d.cts +2 -0
- package/dist/utils/index.d.mts +2 -0
- package/dist/utils/index.mjs +143 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { ExponentialBackoffPolicy, retryOperation } from "../utils/index.mjs";
|
|
2
|
+
import { createSyncError, normalizeSyncError } from "../core/index.mjs";
|
|
3
|
+
//#region src/http/fetch-transport.ts
|
|
4
|
+
var FetchSyncTransport = class {
|
|
5
|
+
type = "http";
|
|
6
|
+
#baseUrl;
|
|
7
|
+
#fetch;
|
|
8
|
+
#headers;
|
|
9
|
+
#retryPolicy;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.#baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
12
|
+
this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
13
|
+
this.#headers = options.headers ?? {};
|
|
14
|
+
this.#retryPolicy = options.retryPolicy ?? new ExponentialBackoffPolicy();
|
|
15
|
+
}
|
|
16
|
+
async push(payload) {
|
|
17
|
+
await retryOperation(async () => {
|
|
18
|
+
await ensureOk(await this.#fetch(`${this.#baseUrl}/push`, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: this.#createHeaders(),
|
|
21
|
+
body: JSON.stringify(payload)
|
|
22
|
+
}));
|
|
23
|
+
}, {
|
|
24
|
+
policy: this.#retryPolicy,
|
|
25
|
+
normalizeError: normalizeSyncError
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async pull(cursor, limit) {
|
|
29
|
+
return await retryOperation(async () => {
|
|
30
|
+
const url = new URL(`${this.#baseUrl}/pull`);
|
|
31
|
+
if (cursor !== null && cursor.length > 0) url.searchParams.set("cursor", cursor);
|
|
32
|
+
if (limit !== void 0) url.searchParams.set("limit", limit.toString());
|
|
33
|
+
const response = await this.#fetch(url.toString(), {
|
|
34
|
+
method: "GET",
|
|
35
|
+
headers: this.#createHeaders()
|
|
36
|
+
});
|
|
37
|
+
await ensureOk(response);
|
|
38
|
+
return await response.json();
|
|
39
|
+
}, {
|
|
40
|
+
policy: this.#retryPolicy,
|
|
41
|
+
normalizeError: normalizeSyncError
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
#createHeaders() {
|
|
45
|
+
return {
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
...this.#headers
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
async function ensureOk(response) {
|
|
52
|
+
if (response.ok) return;
|
|
53
|
+
throw createSyncError(codeFromStatus(response.status), `Sync HTTP request failed with status ${response.status}.`, await response.text());
|
|
54
|
+
}
|
|
55
|
+
function codeFromStatus(status) {
|
|
56
|
+
if (status === 401 || status === 403) return "AUTH_FAILED";
|
|
57
|
+
if (status === 410) return "CURSOR_EXPIRED";
|
|
58
|
+
if (status >= 500) return "SERVER_ERROR";
|
|
59
|
+
return "TRANSPORT_ERROR";
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/http/websocket-transport.ts
|
|
63
|
+
var WebSocketSyncTransport = class {
|
|
64
|
+
type = "websocket";
|
|
65
|
+
#url;
|
|
66
|
+
#WebSocket;
|
|
67
|
+
#requestTimeoutMs;
|
|
68
|
+
#retryPolicy;
|
|
69
|
+
#subscribers = /* @__PURE__ */ new Set();
|
|
70
|
+
#pending = /* @__PURE__ */ new Map();
|
|
71
|
+
#socket = null;
|
|
72
|
+
constructor(options) {
|
|
73
|
+
this.#url = options.url;
|
|
74
|
+
this.#WebSocket = options.WebSocket ?? globalThis.WebSocket;
|
|
75
|
+
this.#requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
|
|
76
|
+
this.#retryPolicy = options.retryPolicy ?? new ExponentialBackoffPolicy();
|
|
77
|
+
}
|
|
78
|
+
async push(payload) {
|
|
79
|
+
await retryOperation(async () => {
|
|
80
|
+
if ((await this.#request({
|
|
81
|
+
type: "push",
|
|
82
|
+
payload
|
|
83
|
+
})).type !== "pushResult") throw createSyncError("SERIALIZATION_ERROR", "Unexpected WebSocket push response.");
|
|
84
|
+
}, {
|
|
85
|
+
policy: this.#retryPolicy,
|
|
86
|
+
normalizeError: normalizeSyncError
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async pull(cursor, limit) {
|
|
90
|
+
return await retryOperation(async () => {
|
|
91
|
+
const message = await this.#request({
|
|
92
|
+
type: "pull",
|
|
93
|
+
cursor,
|
|
94
|
+
limit
|
|
95
|
+
});
|
|
96
|
+
if (message.type !== "pullResult") throw createSyncError("SERIALIZATION_ERROR", "Unexpected WebSocket pull response.");
|
|
97
|
+
return message.result;
|
|
98
|
+
}, {
|
|
99
|
+
policy: this.#retryPolicy,
|
|
100
|
+
normalizeError: normalizeSyncError
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
subscribe(onChanges) {
|
|
104
|
+
this.#subscribers.add(onChanges);
|
|
105
|
+
this.#connect();
|
|
106
|
+
return () => {
|
|
107
|
+
this.#subscribers.delete(onChanges);
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
close() {
|
|
111
|
+
this.#socket?.close();
|
|
112
|
+
this.#socket = null;
|
|
113
|
+
for (const pending of this.#pending.values()) {
|
|
114
|
+
clearTimeout(pending.timeout);
|
|
115
|
+
pending.reject(createSyncError("TRANSPORT_ERROR", "WebSocket transport closed."));
|
|
116
|
+
}
|
|
117
|
+
this.#pending.clear();
|
|
118
|
+
}
|
|
119
|
+
async #request(payload) {
|
|
120
|
+
const socket = await this.#connect();
|
|
121
|
+
const id = crypto.randomUUID();
|
|
122
|
+
const message = JSON.stringify({
|
|
123
|
+
id,
|
|
124
|
+
...payload
|
|
125
|
+
});
|
|
126
|
+
return await new Promise((resolve, reject) => {
|
|
127
|
+
const timeout = setTimeout(() => {
|
|
128
|
+
this.#pending.delete(id);
|
|
129
|
+
reject(createSyncError("NETWORK_UNAVAILABLE", "WebSocket sync request timed out."));
|
|
130
|
+
}, this.#requestTimeoutMs);
|
|
131
|
+
this.#pending.set(id, {
|
|
132
|
+
resolve,
|
|
133
|
+
reject,
|
|
134
|
+
timeout
|
|
135
|
+
});
|
|
136
|
+
socket.send(message);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async #connect() {
|
|
140
|
+
if (this.#socket?.readyState === this.#WebSocket.OPEN) return this.#socket;
|
|
141
|
+
const socket = new this.#WebSocket(this.#url);
|
|
142
|
+
this.#socket = socket;
|
|
143
|
+
socket.addEventListener("message", (event) => this.#handleMessage(event));
|
|
144
|
+
if (socket.readyState === this.#WebSocket.OPEN) return socket;
|
|
145
|
+
return await new Promise((resolve, reject) => {
|
|
146
|
+
const handleOpen = () => {
|
|
147
|
+
cleanup();
|
|
148
|
+
resolve(socket);
|
|
149
|
+
};
|
|
150
|
+
const handleError = () => {
|
|
151
|
+
cleanup();
|
|
152
|
+
reject(createSyncError("NETWORK_UNAVAILABLE", "WebSocket connection failed."));
|
|
153
|
+
};
|
|
154
|
+
const cleanup = () => {
|
|
155
|
+
socket.removeEventListener("open", handleOpen);
|
|
156
|
+
socket.removeEventListener("error", handleError);
|
|
157
|
+
};
|
|
158
|
+
socket.addEventListener("open", handleOpen);
|
|
159
|
+
socket.addEventListener("error", handleError);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
#handleMessage(event) {
|
|
163
|
+
const rawData = "data" in event ? event.data : void 0;
|
|
164
|
+
if (typeof rawData !== "string") return;
|
|
165
|
+
const message = JSON.parse(rawData);
|
|
166
|
+
if (message.type === "changes") {
|
|
167
|
+
for (const subscriber of this.#subscribers) subscriber(message.cursor);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const pending = this.#pending.get(message.id);
|
|
171
|
+
if (pending === void 0) return;
|
|
172
|
+
clearTimeout(pending.timeout);
|
|
173
|
+
this.#pending.delete(message.id);
|
|
174
|
+
if ("error" in message && message.error !== void 0) {
|
|
175
|
+
pending.reject(createSyncError("TRANSPORT_ERROR", message.error.message ?? "WebSocket sync request failed.", message.error));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
pending.resolve(message);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
//#endregion
|
|
182
|
+
export { FetchSyncTransport, WebSocketSyncTransport };
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["#baseUrl","#fetch","#headers","#retryPolicy","#createHeaders","#url","#WebSocket","#requestTimeoutMs","#retryPolicy","#subscribers","#pending","#request","#connect","#socket","#handleMessage"],"sources":["../../src/http/fetch-transport.ts","../../src/http/websocket-transport.ts"],"sourcesContent":["import { createSyncError, type PullResult, type PushPayload, type SyncTransport } from \"../core\";\nimport type { SyncErrorCode } from \"../core\";\nimport { ExponentialBackoffPolicy, retryOperation, type RetryPolicy } from \"../utils\";\nimport { normalizeSyncError } from \"../core\";\n\nexport interface FetchSyncTransportOptions {\n readonly baseUrl: string;\n readonly fetch?: typeof fetch;\n readonly headers?: Record<string, string>;\n readonly retryPolicy?: RetryPolicy;\n}\n\nexport class FetchSyncTransport implements SyncTransport {\n readonly type = \"http\";\n readonly #baseUrl: string;\n readonly #fetch: typeof fetch;\n readonly #headers: Record<string, string>;\n readonly #retryPolicy: RetryPolicy;\n\n constructor(options: FetchSyncTransportOptions) {\n this.#baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.#headers = options.headers ?? {};\n this.#retryPolicy = options.retryPolicy ?? new ExponentialBackoffPolicy();\n }\n\n async push(payload: PushPayload): Promise<void> {\n await retryOperation(\n async () => {\n const response = await this.#fetch(`${this.#baseUrl}/push`, {\n method: \"POST\",\n headers: this.#createHeaders(),\n body: JSON.stringify(payload),\n });\n\n await ensureOk(response);\n },\n { policy: this.#retryPolicy, normalizeError: normalizeSyncError },\n );\n }\n\n async pull(cursor: string | null, limit?: number): Promise<PullResult> {\n return await retryOperation(\n async () => {\n const url = new URL(`${this.#baseUrl}/pull`);\n\n if (cursor !== null && cursor.length > 0) {\n url.searchParams.set(\"cursor\", cursor);\n }\n\n if (limit !== undefined) {\n url.searchParams.set(\"limit\", limit.toString());\n }\n\n const response = await this.#fetch(url.toString(), {\n method: \"GET\",\n headers: this.#createHeaders(),\n });\n\n await ensureOk(response);\n return (await response.json()) as PullResult;\n },\n { policy: this.#retryPolicy, normalizeError: normalizeSyncError },\n );\n }\n\n #createHeaders(): Record<string, string> {\n return {\n \"Content-Type\": \"application/json\",\n ...this.#headers,\n };\n }\n}\n\nasync function ensureOk(response: Response): Promise<void> {\n if (response.ok) {\n return;\n }\n\n throw createSyncError(\n codeFromStatus(response.status),\n `Sync HTTP request failed with status ${response.status}.`,\n await response.text(),\n );\n}\n\nfunction codeFromStatus(status: number): SyncErrorCode {\n if (status === 401 || status === 403) {\n return \"AUTH_FAILED\";\n }\n\n if (status === 410) {\n return \"CURSOR_EXPIRED\";\n }\n\n if (status >= 500) {\n return \"SERVER_ERROR\";\n }\n\n return \"TRANSPORT_ERROR\";\n}\n","import { createSyncError, normalizeSyncError, type PullResult, type PushPayload } from \"../core\";\nimport type { SyncTransport } from \"../core\";\nimport { ExponentialBackoffPolicy, retryOperation, type RetryPolicy } from \"../utils\";\n\nexport interface WebSocketSyncTransportOptions {\n readonly url: string;\n readonly WebSocket?: typeof globalThis.WebSocket;\n readonly requestTimeoutMs?: number;\n readonly retryPolicy?: RetryPolicy;\n}\n\ntype ServerMessage =\n | { readonly id: string; readonly type: \"pushResult\"; readonly error?: ErrorPayload }\n | {\n readonly id: string;\n readonly type: \"pullResult\";\n readonly result: PullResult;\n readonly error?: ErrorPayload;\n }\n | { readonly type: \"changes\"; readonly cursor: string };\n\ninterface ErrorPayload {\n readonly code?: string;\n readonly message?: string;\n}\n\nexport class WebSocketSyncTransport implements SyncTransport {\n readonly type = \"websocket\";\n readonly #url: string;\n readonly #WebSocket: typeof globalThis.WebSocket;\n readonly #requestTimeoutMs: number;\n readonly #retryPolicy: RetryPolicy;\n readonly #subscribers = new Set<(cursor: string) => void>();\n readonly #pending = new Map<\n string,\n {\n readonly resolve: (message: ServerMessage) => void;\n readonly reject: (error: unknown) => void;\n readonly timeout: ReturnType<typeof setTimeout>;\n }\n >();\n #socket: WebSocket | null = null;\n\n constructor(options: WebSocketSyncTransportOptions) {\n this.#url = options.url;\n this.#WebSocket = options.WebSocket ?? globalThis.WebSocket;\n this.#requestTimeoutMs = options.requestTimeoutMs ?? 30_000;\n this.#retryPolicy = options.retryPolicy ?? new ExponentialBackoffPolicy();\n }\n\n async push(payload: PushPayload): Promise<void> {\n await retryOperation(\n async () => {\n const message = await this.#request({ type: \"push\", payload });\n if (message.type !== \"pushResult\") {\n throw createSyncError(\"SERIALIZATION_ERROR\", \"Unexpected WebSocket push response.\");\n }\n },\n { policy: this.#retryPolicy, normalizeError: normalizeSyncError },\n );\n }\n\n async pull(cursor: string | null, limit?: number): Promise<PullResult> {\n return await retryOperation(\n async () => {\n const message = await this.#request({ type: \"pull\", cursor, limit });\n if (message.type !== \"pullResult\") {\n throw createSyncError(\"SERIALIZATION_ERROR\", \"Unexpected WebSocket pull response.\");\n }\n return message.result;\n },\n { policy: this.#retryPolicy, normalizeError: normalizeSyncError },\n );\n }\n\n subscribe(onChanges: (cursor: string) => void): () => void {\n this.#subscribers.add(onChanges);\n void this.#connect();\n return () => {\n this.#subscribers.delete(onChanges);\n };\n }\n\n close(): void {\n this.#socket?.close();\n this.#socket = null;\n for (const pending of this.#pending.values()) {\n clearTimeout(pending.timeout);\n pending.reject(createSyncError(\"TRANSPORT_ERROR\", \"WebSocket transport closed.\"));\n }\n this.#pending.clear();\n }\n\n async #request(payload: Record<string, unknown>): Promise<ServerMessage> {\n const socket = await this.#connect();\n const id = crypto.randomUUID();\n const message = JSON.stringify({ id, ...payload });\n\n return await new Promise<ServerMessage>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.#pending.delete(id);\n reject(createSyncError(\"NETWORK_UNAVAILABLE\", \"WebSocket sync request timed out.\"));\n }, this.#requestTimeoutMs);\n\n this.#pending.set(id, { resolve, reject, timeout });\n socket.send(message);\n });\n }\n\n async #connect(): Promise<WebSocket> {\n if (this.#socket?.readyState === this.#WebSocket.OPEN) {\n return this.#socket;\n }\n\n const socket = new this.#WebSocket(this.#url);\n this.#socket = socket;\n socket.addEventListener(\"message\", (event) => this.#handleMessage(event));\n\n if (socket.readyState === this.#WebSocket.OPEN) {\n return socket;\n }\n\n return await new Promise<WebSocket>((resolve, reject) => {\n const handleOpen = (): void => {\n cleanup();\n resolve(socket);\n };\n const handleError = (): void => {\n cleanup();\n reject(createSyncError(\"NETWORK_UNAVAILABLE\", \"WebSocket connection failed.\"));\n };\n const cleanup = (): void => {\n socket.removeEventListener(\"open\", handleOpen);\n socket.removeEventListener(\"error\", handleError);\n };\n\n socket.addEventListener(\"open\", handleOpen);\n socket.addEventListener(\"error\", handleError);\n });\n }\n\n #handleMessage(event: Event): void {\n const rawData = \"data\" in event ? event.data : undefined;\n if (typeof rawData !== \"string\") {\n return;\n }\n\n const message = JSON.parse(rawData) as ServerMessage;\n if (message.type === \"changes\") {\n for (const subscriber of this.#subscribers) {\n subscriber(message.cursor);\n }\n return;\n }\n\n const pending = this.#pending.get(message.id);\n if (pending === undefined) {\n return;\n }\n\n clearTimeout(pending.timeout);\n this.#pending.delete(message.id);\n\n if (\"error\" in message && message.error !== undefined) {\n pending.reject(\n createSyncError(\n \"TRANSPORT_ERROR\",\n message.error.message ?? \"WebSocket sync request failed.\",\n message.error,\n ),\n );\n return;\n }\n\n pending.resolve(message);\n }\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_utils_index = require("./utils/index.cjs");
|
|
3
|
+
const require_core_index = require("./core/index.cjs");
|
|
4
|
+
const require_http_index = require("./http/index.cjs");
|
|
5
|
+
const require_sql_index = require("./sql/index.cjs");
|
|
6
|
+
exports.DefaultSyncScheduler = require_core_index.DefaultSyncScheduler;
|
|
7
|
+
exports.DexieDatabaseAdapter = require_sql_index.DexieDatabaseAdapter;
|
|
8
|
+
exports.ExponentialBackoffPolicy = require_utils_index.ExponentialBackoffPolicy;
|
|
9
|
+
exports.FetchSyncTransport = require_http_index.FetchSyncTransport;
|
|
10
|
+
exports.HLCClock = require_utils_index.HLCClock;
|
|
11
|
+
exports.LWWResolver = require_core_index.LWWResolver;
|
|
12
|
+
exports.MemoryDatabaseAdapter = require_sql_index.MemoryDatabaseAdapter;
|
|
13
|
+
exports.SyncEngine = require_core_index.SyncEngine;
|
|
14
|
+
exports.TypedSyncEventBus = require_core_index.TypedSyncEventBus;
|
|
15
|
+
exports.WebSocketSyncTransport = require_http_index.WebSocketSyncTransport;
|
|
16
|
+
exports.compareHLC = require_utils_index.compareHLC;
|
|
17
|
+
exports.createChangeId = require_utils_index.createChangeId;
|
|
18
|
+
exports.createDeviceId = require_utils_index.createDeviceId;
|
|
19
|
+
exports.createSyncError = require_core_index.createSyncError;
|
|
20
|
+
exports.isSyncError = require_core_index.isSyncError;
|
|
21
|
+
exports.normalizeSyncError = require_core_index.normalizeSyncError;
|
|
22
|
+
exports.retryOperation = require_utils_index.retryOperation;
|
|
23
|
+
exports.sleep = require_utils_index.sleep;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/utils/hlc.d.ts
|
|
2
|
+
type HLCTimestamp = string;
|
|
3
|
+
declare class HLCClock {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(nodeId: string, now?: () => number);
|
|
6
|
+
now(): HLCTimestamp;
|
|
7
|
+
merge(remoteTimestamp: HLCTimestamp): HLCTimestamp;
|
|
8
|
+
}
|
|
9
|
+
declare function compareHLC(left: HLCTimestamp, right: HLCTimestamp): number;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/utils/id.d.ts
|
|
12
|
+
declare function createDeviceId(): string;
|
|
13
|
+
declare function createChangeId(): string;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/utils/retry.d.ts
|
|
16
|
+
interface RetryContext {
|
|
17
|
+
readonly attempt: number;
|
|
18
|
+
readonly lastError: {
|
|
19
|
+
readonly code: string;
|
|
20
|
+
readonly message: string;
|
|
21
|
+
readonly occurredAt: number;
|
|
22
|
+
};
|
|
23
|
+
readonly firstFailedAt: number;
|
|
24
|
+
}
|
|
25
|
+
type RetryDecision = {
|
|
26
|
+
readonly action: "retry";
|
|
27
|
+
readonly delayMs: number;
|
|
28
|
+
} | {
|
|
29
|
+
readonly action: "abort";
|
|
30
|
+
};
|
|
31
|
+
interface RetryPolicy {
|
|
32
|
+
decide(context: RetryContext): RetryDecision;
|
|
33
|
+
}
|
|
34
|
+
interface RetryOperationOptions {
|
|
35
|
+
readonly policy: RetryPolicy;
|
|
36
|
+
readonly normalizeError: (error: unknown) => RetryContext["lastError"];
|
|
37
|
+
readonly sleep?: (delayMs: number) => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
interface ExponentialBackoffOptions {
|
|
40
|
+
readonly maxAttempts?: number;
|
|
41
|
+
readonly baseDelayMs?: number;
|
|
42
|
+
readonly maxDelayMs?: number;
|
|
43
|
+
readonly retryableCodes?: ReadonlySet<string>;
|
|
44
|
+
}
|
|
45
|
+
declare class ExponentialBackoffPolicy implements RetryPolicy {
|
|
46
|
+
#private;
|
|
47
|
+
constructor(options?: ExponentialBackoffOptions);
|
|
48
|
+
decide(context: RetryContext): RetryDecision;
|
|
49
|
+
}
|
|
50
|
+
declare function retryOperation<T>(operation: () => Promise<T>, options: RetryOperationOptions): Promise<T>;
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/utils/sleep.d.ts
|
|
53
|
+
declare function sleep(delayMs: number): Promise<void>;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { RetryDecision as a, retryOperation as c, HLCClock as d, HLCTimestamp as f, RetryContext as i, createChangeId as l, ExponentialBackoffOptions as n, RetryOperationOptions as o, compareHLC as p, ExponentialBackoffPolicy as r, RetryPolicy as s, sleep as t, createDeviceId as u };
|
|
56
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/utils/hlc.ts","../src/utils/id.ts","../src/utils/retry.ts","../src/utils/sleep.ts"],"mappings":";KAAY,YAAA;AAAA,cAQC,QAAA;EAAA;cAMC,MAAA,UAAgB,GAAA;EAS5B,GAAA,IAAO,YAAA;EAiBP,KAAA,CAAM,eAAA,EAAiB,YAAA,GAAe,YAAA;AAAA;AAAA,iBAyBxB,UAAA,CAAW,IAAA,EAAM,YAAA,EAAc,KAAA,EAAO,YAAY;;;iBCjElD,cAAA;AAAA,iBAIA,cAAA;;;UCJC,YAAA;EAAA,SACN,OAAA;EAAA,SACA,SAAA;IAAA,SACE,IAAA;IAAA,SACA,OAAA;IAAA,SACA,UAAA;EAAA;EAAA,SAEF,aAAA;AAAA;AAAA,KAGC,aAAA;EAAA,SACG,MAAA;EAAA,SAA0B,OAAA;AAAA;EAAA,SAC1B,MAAA;AAAA;AAAA,UAEE,WAAA;EACf,MAAA,CAAO,OAAA,EAAS,YAAA,GAAe,aAAa;AAAA;AAAA,UAG7B,qBAAA;EAAA,SACN,MAAA,EAAQ,WAAA;EAAA,SACR,cAAA,GAAiB,KAAA,cAAmB,YAAA;EAAA,SACpC,KAAA,IAAS,OAAA,aAAoB,OAAA;AAAA;AAAA,UAGvB,yBAAA;EAAA,SACN,WAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;EAAA,SACA,cAAA,GAAiB,WAAW;AAAA;AAAA,cAG1B,wBAAA,YAAoC,WAAA;EAAA;cAMnC,OAAA,GAAS,yBAAA;EASrB,MAAA,CAAO,OAAA,EAAS,YAAA,GAAe,aAAA;AAAA;AAAA,iBAkBX,cAAA,IACpB,SAAA,QAAiB,OAAA,CAAQ,CAAA,GACzB,OAAA,EAAS,qBAAA,GACR,OAAA,CAAQ,CAAA;;;iBCnEK,KAAA,CAAM,OAAA,WAAkB,OAAO"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/utils/hlc.d.ts
|
|
2
|
+
type HLCTimestamp = string;
|
|
3
|
+
declare class HLCClock {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(nodeId: string, now?: () => number);
|
|
6
|
+
now(): HLCTimestamp;
|
|
7
|
+
merge(remoteTimestamp: HLCTimestamp): HLCTimestamp;
|
|
8
|
+
}
|
|
9
|
+
declare function compareHLC(left: HLCTimestamp, right: HLCTimestamp): number;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/utils/id.d.ts
|
|
12
|
+
declare function createDeviceId(): string;
|
|
13
|
+
declare function createChangeId(): string;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/utils/retry.d.ts
|
|
16
|
+
interface RetryContext {
|
|
17
|
+
readonly attempt: number;
|
|
18
|
+
readonly lastError: {
|
|
19
|
+
readonly code: string;
|
|
20
|
+
readonly message: string;
|
|
21
|
+
readonly occurredAt: number;
|
|
22
|
+
};
|
|
23
|
+
readonly firstFailedAt: number;
|
|
24
|
+
}
|
|
25
|
+
type RetryDecision = {
|
|
26
|
+
readonly action: "retry";
|
|
27
|
+
readonly delayMs: number;
|
|
28
|
+
} | {
|
|
29
|
+
readonly action: "abort";
|
|
30
|
+
};
|
|
31
|
+
interface RetryPolicy {
|
|
32
|
+
decide(context: RetryContext): RetryDecision;
|
|
33
|
+
}
|
|
34
|
+
interface RetryOperationOptions {
|
|
35
|
+
readonly policy: RetryPolicy;
|
|
36
|
+
readonly normalizeError: (error: unknown) => RetryContext["lastError"];
|
|
37
|
+
readonly sleep?: (delayMs: number) => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
interface ExponentialBackoffOptions {
|
|
40
|
+
readonly maxAttempts?: number;
|
|
41
|
+
readonly baseDelayMs?: number;
|
|
42
|
+
readonly maxDelayMs?: number;
|
|
43
|
+
readonly retryableCodes?: ReadonlySet<string>;
|
|
44
|
+
}
|
|
45
|
+
declare class ExponentialBackoffPolicy implements RetryPolicy {
|
|
46
|
+
#private;
|
|
47
|
+
constructor(options?: ExponentialBackoffOptions);
|
|
48
|
+
decide(context: RetryContext): RetryDecision;
|
|
49
|
+
}
|
|
50
|
+
declare function retryOperation<T>(operation: () => Promise<T>, options: RetryOperationOptions): Promise<T>;
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/utils/sleep.d.ts
|
|
53
|
+
declare function sleep(delayMs: number): Promise<void>;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { RetryDecision as a, retryOperation as c, HLCClock as d, HLCTimestamp as f, RetryContext as i, createChangeId as l, ExponentialBackoffOptions as n, RetryOperationOptions as o, compareHLC as p, ExponentialBackoffPolicy as r, RetryPolicy as s, sleep as t, createDeviceId as u };
|
|
56
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/utils/hlc.ts","../src/utils/id.ts","../src/utils/retry.ts","../src/utils/sleep.ts"],"mappings":";KAAY,YAAA;AAAA,cAQC,QAAA;EAAA;cAMC,MAAA,UAAgB,GAAA;EAS5B,GAAA,IAAO,YAAA;EAiBP,KAAA,CAAM,eAAA,EAAiB,YAAA,GAAe,YAAA;AAAA;AAAA,iBAyBxB,UAAA,CAAW,IAAA,EAAM,YAAA,EAAc,KAAA,EAAO,YAAY;;;iBCjElD,cAAA;AAAA,iBAIA,cAAA;;;UCJC,YAAA;EAAA,SACN,OAAA;EAAA,SACA,SAAA;IAAA,SACE,IAAA;IAAA,SACA,OAAA;IAAA,SACA,UAAA;EAAA;EAAA,SAEF,aAAA;AAAA;AAAA,KAGC,aAAA;EAAA,SACG,MAAA;EAAA,SAA0B,OAAA;AAAA;EAAA,SAC1B,MAAA;AAAA;AAAA,UAEE,WAAA;EACf,MAAA,CAAO,OAAA,EAAS,YAAA,GAAe,aAAa;AAAA;AAAA,UAG7B,qBAAA;EAAA,SACN,MAAA,EAAQ,WAAA;EAAA,SACR,cAAA,GAAiB,KAAA,cAAmB,YAAA;EAAA,SACpC,KAAA,IAAS,OAAA,aAAoB,OAAA;AAAA;AAAA,UAGvB,yBAAA;EAAA,SACN,WAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;EAAA,SACA,cAAA,GAAiB,WAAW;AAAA;AAAA,cAG1B,wBAAA,YAAoC,WAAA;EAAA;cAMnC,OAAA,GAAS,yBAAA;EASrB,MAAA,CAAO,OAAA,EAAS,YAAA,GAAe,aAAA;AAAA;AAAA,iBAkBX,cAAA,IACpB,SAAA,QAAiB,OAAA,CAAQ,CAAA,GACzB,OAAA,EAAS,qBAAA,GACR,OAAA,CAAQ,CAAA;;;iBCnEK,KAAA,CAAM,OAAA,WAAkB,OAAO"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ExponentialBackoffPolicy, HLCClock, compareHLC, createChangeId, createDeviceId, retryOperation, sleep } from "./utils/index.mjs";
|
|
2
|
+
import { DefaultSyncScheduler, LWWResolver, SyncEngine, TypedSyncEventBus, createSyncError, isSyncError, normalizeSyncError } from "./core/index.mjs";
|
|
3
|
+
import { FetchSyncTransport, WebSocketSyncTransport } from "./http/index.mjs";
|
|
4
|
+
import { DexieDatabaseAdapter, MemoryDatabaseAdapter } from "./sql/index.mjs";
|
|
5
|
+
export { DefaultSyncScheduler, DexieDatabaseAdapter, ExponentialBackoffPolicy, FetchSyncTransport, HLCClock, LWWResolver, MemoryDatabaseAdapter, SyncEngine, TypedSyncEventBus, WebSocketSyncTransport, compareHLC, createChangeId, createDeviceId, createSyncError, isSyncError, normalizeSyncError, retryOperation, sleep };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { f as HLCTimestamp, s as RetryPolicy } from "./index.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/core/types.d.ts
|
|
4
|
+
type DeviceId = string;
|
|
5
|
+
type ChangeAction = "insert" | "update" | "delete";
|
|
6
|
+
interface Change<TPayload = unknown> {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly table: string;
|
|
9
|
+
readonly entityId: string;
|
|
10
|
+
readonly action: ChangeAction;
|
|
11
|
+
readonly payload: TPayload;
|
|
12
|
+
readonly version: HLCTimestamp;
|
|
13
|
+
readonly deviceId: DeviceId;
|
|
14
|
+
readonly createdAt: number;
|
|
15
|
+
}
|
|
16
|
+
interface SyncCursor {
|
|
17
|
+
readonly value: string;
|
|
18
|
+
readonly lastSyncedAt: number;
|
|
19
|
+
}
|
|
20
|
+
interface DatabaseAdapter {
|
|
21
|
+
runInTransaction<T>(workload: () => Promise<T>): Promise<T>;
|
|
22
|
+
getLocalCursor(): Promise<SyncCursor | null>;
|
|
23
|
+
saveLocalCursor(cursor: SyncCursor): Promise<void>;
|
|
24
|
+
getPendingChanges(limit?: number): Promise<Change[]>;
|
|
25
|
+
markAsSynced(changeIds: string[]): Promise<void>;
|
|
26
|
+
applyRemoteChanges(changes: Change[]): Promise<void>;
|
|
27
|
+
clearTombstones(beforeTimestamp: number): Promise<number>;
|
|
28
|
+
}
|
|
29
|
+
interface PushPayload {
|
|
30
|
+
readonly changes: ReadonlyArray<Change>;
|
|
31
|
+
readonly deviceId: DeviceId;
|
|
32
|
+
}
|
|
33
|
+
interface PullResult {
|
|
34
|
+
readonly cursor: SyncCursor;
|
|
35
|
+
readonly changes: ReadonlyArray<Change>;
|
|
36
|
+
readonly hasMore: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface SyncTransport {
|
|
39
|
+
readonly type: "http" | "websocket" | "webrtc" | "grpc";
|
|
40
|
+
push(changes: PushPayload): Promise<void>;
|
|
41
|
+
pull(cursor: string | null, limit?: number): Promise<PullResult>;
|
|
42
|
+
subscribe?: (onChanges: (cursor: string) => void) => () => void;
|
|
43
|
+
}
|
|
44
|
+
type SyncStatus = "idle" | "uploading" | "downloading" | "applying" | "error";
|
|
45
|
+
type SyncErrorCode = "NETWORK_UNAVAILABLE" | "TRANSPORT_ERROR" | "AUTH_FAILED" | "SERVER_ERROR" | "CURSOR_EXPIRED" | "CONFLICT_UNRESOLVABLE" | "DB_WRITE_FAILED" | "SERIALIZATION_ERROR" | "MAX_RETRY_EXCEEDED" | "UNKNOWN";
|
|
46
|
+
interface SyncError {
|
|
47
|
+
readonly code: SyncErrorCode;
|
|
48
|
+
readonly message: string;
|
|
49
|
+
readonly cause?: unknown;
|
|
50
|
+
readonly occurredAt: number;
|
|
51
|
+
}
|
|
52
|
+
interface SyncResult {
|
|
53
|
+
readonly success: boolean;
|
|
54
|
+
readonly uploadedCount: number;
|
|
55
|
+
readonly downloadedCount: number;
|
|
56
|
+
readonly durationMs: number;
|
|
57
|
+
readonly error?: SyncError;
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/core/conflict.d.ts
|
|
61
|
+
interface ConflictContext {
|
|
62
|
+
readonly local: Change;
|
|
63
|
+
readonly remote: Change;
|
|
64
|
+
readonly deviceId: DeviceId;
|
|
65
|
+
}
|
|
66
|
+
interface ConflictResolution {
|
|
67
|
+
readonly winner: Change;
|
|
68
|
+
readonly strategy: string;
|
|
69
|
+
}
|
|
70
|
+
interface ConflictResolver {
|
|
71
|
+
resolve(context: ConflictContext): ConflictResolution;
|
|
72
|
+
}
|
|
73
|
+
declare class LWWResolver implements ConflictResolver {
|
|
74
|
+
resolve(context: ConflictContext): ConflictResolution;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/core/event-bus.d.ts
|
|
78
|
+
interface SyncEventMap {
|
|
79
|
+
readonly syncStart: void;
|
|
80
|
+
readonly syncFinish: SyncResult;
|
|
81
|
+
readonly statusChange: SyncStatus;
|
|
82
|
+
readonly uploadComplete: {
|
|
83
|
+
readonly uploadedCount: number;
|
|
84
|
+
};
|
|
85
|
+
readonly downloadComplete: {
|
|
86
|
+
readonly downloadedCount: number;
|
|
87
|
+
};
|
|
88
|
+
readonly conflictResolved: ConflictResolution;
|
|
89
|
+
readonly error: SyncError;
|
|
90
|
+
readonly deadLetter: {
|
|
91
|
+
readonly changes: ReadonlyArray<Change>;
|
|
92
|
+
readonly error: SyncError;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
type SyncEventListener<K extends keyof SyncEventMap> = SyncEventMap[K] extends void ? () => void : (payload: SyncEventMap[K]) => void;
|
|
96
|
+
interface SyncEventBus {
|
|
97
|
+
on<K extends keyof SyncEventMap>(event: K, listener: SyncEventListener<K>): () => void;
|
|
98
|
+
once<K extends keyof SyncEventMap>(event: K, listener: SyncEventListener<K>): void;
|
|
99
|
+
off<K extends keyof SyncEventMap>(event: K, listener: SyncEventListener<K>): void;
|
|
100
|
+
}
|
|
101
|
+
declare class TypedSyncEventBus implements SyncEventBus {
|
|
102
|
+
#private;
|
|
103
|
+
on<K extends keyof SyncEventMap>(event: K, listener: SyncEventListener<K>): () => void;
|
|
104
|
+
once<K extends keyof SyncEventMap>(event: K, listener: SyncEventListener<K>): void;
|
|
105
|
+
off<K extends keyof SyncEventMap>(event: K, listener: SyncEventListener<K>): void;
|
|
106
|
+
emit<K extends keyof SyncEventMap>(event: K, ...args: SyncEventMap[K] extends void ? [] : [SyncEventMap[K]]): void;
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/core/engine.d.ts
|
|
110
|
+
interface SyncEngineOptions {
|
|
111
|
+
readonly database: DatabaseAdapter;
|
|
112
|
+
readonly transport: SyncTransport;
|
|
113
|
+
readonly resolver?: ConflictResolver;
|
|
114
|
+
readonly deviceId: DeviceId;
|
|
115
|
+
readonly batchSize?: number;
|
|
116
|
+
}
|
|
117
|
+
declare class SyncEngine {
|
|
118
|
+
#private;
|
|
119
|
+
readonly resolver: ConflictResolver;
|
|
120
|
+
readonly events: TypedSyncEventBus;
|
|
121
|
+
constructor(options: SyncEngineOptions);
|
|
122
|
+
get status(): SyncStatus;
|
|
123
|
+
sync(): Promise<SyncResult>;
|
|
124
|
+
dispose(): Promise<void>;
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/core/errors.d.ts
|
|
128
|
+
declare function createSyncError(code: SyncErrorCode, message: string, cause?: unknown): SyncError;
|
|
129
|
+
declare function isSyncError(error: unknown): error is SyncError;
|
|
130
|
+
declare function normalizeSyncError(error: unknown): SyncError;
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/core/scheduler.d.ts
|
|
133
|
+
interface SyncSchedulerEngine {
|
|
134
|
+
sync(): Promise<SyncResult>;
|
|
135
|
+
}
|
|
136
|
+
interface SyncSchedulerDatabase {
|
|
137
|
+
clearTombstones(beforeTimestamp: number): Promise<number>;
|
|
138
|
+
}
|
|
139
|
+
interface SchedulerOptions {
|
|
140
|
+
readonly intervalMs?: number;
|
|
141
|
+
readonly retryPolicy?: RetryPolicy;
|
|
142
|
+
readonly syncOnNetworkRecover?: boolean;
|
|
143
|
+
readonly syncOnForeground?: boolean;
|
|
144
|
+
readonly tombstoneCleanupIntervalMs?: number;
|
|
145
|
+
readonly tombstoneMaxAgeMs?: number;
|
|
146
|
+
}
|
|
147
|
+
interface DefaultSyncSchedulerOptions {
|
|
148
|
+
readonly engine: SyncSchedulerEngine;
|
|
149
|
+
readonly database?: SyncSchedulerDatabase | Pick<DatabaseAdapter, "clearTombstones">;
|
|
150
|
+
readonly retryPolicy?: RetryPolicy;
|
|
151
|
+
readonly window?: Pick<Window, "addEventListener" | "removeEventListener">;
|
|
152
|
+
readonly document?: Pick<Document, "addEventListener" | "removeEventListener" | "visibilityState">;
|
|
153
|
+
}
|
|
154
|
+
interface SyncScheduler {
|
|
155
|
+
start(options?: SchedulerOptions): void;
|
|
156
|
+
stop(): void;
|
|
157
|
+
triggerSync(): Promise<SyncResult>;
|
|
158
|
+
}
|
|
159
|
+
declare class DefaultSyncScheduler implements SyncScheduler {
|
|
160
|
+
#private;
|
|
161
|
+
constructor(options: DefaultSyncSchedulerOptions);
|
|
162
|
+
start(options?: SchedulerOptions): void;
|
|
163
|
+
stop(): void;
|
|
164
|
+
triggerSync(): Promise<SyncResult>;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
export { SyncStatus as A, DeviceId as C, SyncError as D, SyncCursor as E, SyncErrorCode as O, DatabaseAdapter as S, PushPayload as T, ConflictResolution as _, SyncSchedulerDatabase as a, Change as b, isSyncError as c, SyncEngineOptions as d, SyncEventBus as f, ConflictContext as g, TypedSyncEventBus as h, SyncScheduler as i, SyncTransport as j, SyncResult as k, normalizeSyncError as l, SyncEventMap as m, DefaultSyncSchedulerOptions as n, SyncSchedulerEngine as o, SyncEventListener as p, SchedulerOptions as r, createSyncError as s, DefaultSyncScheduler as t, SyncEngine as u, ConflictResolver as v, PullResult as w, ChangeAction as x, LWWResolver as y };
|
|
168
|
+
//# sourceMappingURL=index2.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index2.d.cts","names":[],"sources":["../src/core/types.ts","../src/core/conflict.ts","../src/core/event-bus.ts","../src/core/engine.ts","../src/core/errors.ts","../src/core/scheduler.ts"],"mappings":"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"}
|