@pylonsync/sync 0.3.189 → 0.3.192
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/package.json +1 -1
- package/src/ids.ts +60 -0
- package/src/index.ts +165 -716
- package/src/local-store.ts +309 -0
- package/src/mutation-queue.ts +153 -0
- package/src/transport.ts +205 -0
- package/src/types.ts +136 -0
package/package.json
CHANGED
package/src/ids.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Pylon-compatible id generation.
|
|
3
|
+
//
|
|
4
|
+
// IDs are lex-sortable: 32-char hex timestamp (BigInt nanoseconds)
|
|
5
|
+
// followed by 8-char hex counter. That contract is what the cursor
|
|
6
|
+
// pagination relies on — `WHERE id > '<after>'` returns rows in the
|
|
7
|
+
// same order across pages, and a 39-char id never sorts ahead of a
|
|
8
|
+
// 40-char id (which would corrupt pagination at the width boundary).
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
import type { Storage } from "./storage";
|
|
12
|
+
|
|
13
|
+
let idCounter = 0;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Mint a Pylon-shaped id (40 hex chars). Used by `SyncEngine.insert`
|
|
17
|
+
* so the optimistic ghost and the canonical row share the exact same
|
|
18
|
+
* id — without this, the server-minted id would replace a `_pending_*`
|
|
19
|
+
* placeholder and the local replica would briefly carry both.
|
|
20
|
+
*/
|
|
21
|
+
export function generateId(): string {
|
|
22
|
+
// BigInt to dodge the 2^53 ceiling — `Date.now() * 1_000_000` busts
|
|
23
|
+
// Number.MAX_SAFE_INTEGER for any timestamp past 1973. Hex output is
|
|
24
|
+
// padded to 32 chars so the lex sort behaves at width boundaries.
|
|
25
|
+
const nanos = BigInt(Date.now()) * 1_000_000n;
|
|
26
|
+
const seq = idCounter++ >>> 0;
|
|
27
|
+
return (
|
|
28
|
+
nanos.toString(16).padStart(32, "0") + seq.toString(16).padStart(8, "0")
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Stable client id persisted in storage. Lets the server correlate
|
|
34
|
+
* retries (even when op_id is absent) and attach per-client
|
|
35
|
+
* diagnostics / rate limits.
|
|
36
|
+
*/
|
|
37
|
+
export function generateClientId(storage: Storage): string {
|
|
38
|
+
const key = "pylon:client_id";
|
|
39
|
+
const existing = storage.get(key);
|
|
40
|
+
if (existing) return existing;
|
|
41
|
+
const fresh = newUuidLike();
|
|
42
|
+
storage.set(key, fresh);
|
|
43
|
+
return fresh;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function newUuidLike(): string {
|
|
47
|
+
try {
|
|
48
|
+
if (
|
|
49
|
+
typeof crypto !== "undefined" &&
|
|
50
|
+
typeof crypto.randomUUID === "function"
|
|
51
|
+
) {
|
|
52
|
+
return crypto.randomUUID();
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
/* fall through */
|
|
56
|
+
}
|
|
57
|
+
const rand = Math.random().toString(36).slice(2, 10);
|
|
58
|
+
const t = Date.now().toString(36);
|
|
59
|
+
return `cl_${t}_${rand}`;
|
|
60
|
+
}
|