@pylonsync/sync 0.3.188 → 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 CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.188",
6
+ "version": "0.3.192",
7
7
  "type": "module",
8
8
  "main": "src/index.ts",
9
9
  "types": "src/index.ts",
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
+ }