@rljson/rljson 0.0.74 → 0.0.76
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.architecture.md +365 -0
- package/README.md +11 -1
- package/README.public.md +397 -4
- package/README.trouble.md +4 -0
- package/dist/README.architecture.md +365 -0
- package/dist/README.md +11 -1
- package/dist/README.public.md +397 -4
- package/dist/README.trouble.md +4 -0
- package/dist/content/tree.d.ts +2 -1
- package/dist/index.d.ts +6 -0
- package/dist/insertHistory/insertHistory.d.ts +3 -1
- package/dist/rljson.js +98 -1
- package/dist/sync/ack-payload.d.ts +25 -0
- package/dist/sync/client-id.d.ts +27 -0
- package/dist/sync/connector-payload.d.ts +44 -0
- package/dist/sync/gap-fill.d.ts +41 -0
- package/dist/sync/sync-config.d.ts +70 -0
- package/dist/sync/sync-events.d.ts +32 -0
- package/package.json +11 -11
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { InsertHistoryTimeId } from '../insertHistory/insertHistory.ts';
|
|
2
|
+
import { ClientId } from './client-id.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Wire-protocol payload transmitted between Connector and Server.
|
|
5
|
+
*
|
|
6
|
+
* The two required fields (`o`, `r`) provide backward-compatible
|
|
7
|
+
* self-echo filtering and ref identification. All other fields are
|
|
8
|
+
* optional and activate only when the corresponding `SyncConfig`
|
|
9
|
+
* flags are set.
|
|
10
|
+
*
|
|
11
|
+
* | Field | Concept | Purpose |
|
|
12
|
+
* |---------|------------------------|----------------------------------------|
|
|
13
|
+
* | `r` | existing | The ref being announced |
|
|
14
|
+
* | `o` | existing | Ephemeral origin for self-echo filter |
|
|
15
|
+
* | `c` | Client identity | Stable client id across reconnections |
|
|
16
|
+
* | `t` | Client identity | Client-side wall-clock timestamp (ms) |
|
|
17
|
+
* | `seq` | Predecessor chain | Monotonic counter per (client, route) |
|
|
18
|
+
* | `p` | Predecessor chain | Causal predecessor timeIds |
|
|
19
|
+
* | `cksum` | Acknowledgment | Content checksum for ACK verification |
|
|
20
|
+
*/
|
|
21
|
+
export type ConnectorPayload = {
|
|
22
|
+
/** The ref (InsertHistoryTimeId) being announced. */
|
|
23
|
+
r: string;
|
|
24
|
+
/** Ephemeral origin of the sending Connector (for self-echo filtering). */
|
|
25
|
+
o: string;
|
|
26
|
+
/** Stable client identity (survives reconnections). */
|
|
27
|
+
c?: ClientId;
|
|
28
|
+
/** Client-side wall-clock timestamp in milliseconds since epoch. */
|
|
29
|
+
t?: number;
|
|
30
|
+
/** Monotonic sequence number per (client, route) pair. */
|
|
31
|
+
seq?: number;
|
|
32
|
+
/** Causal predecessor InsertHistory timeIds. */
|
|
33
|
+
p?: InsertHistoryTimeId[];
|
|
34
|
+
/** Content checksum of the referenced data, for ACK verification. */
|
|
35
|
+
cksum?: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Returns a minimal example ConnectorPayload (backward-compatible format).
|
|
39
|
+
*/
|
|
40
|
+
export declare const connectorPayloadExample: () => ConnectorPayload;
|
|
41
|
+
/**
|
|
42
|
+
* Returns a fully-populated example ConnectorPayload with all optional fields.
|
|
43
|
+
*/
|
|
44
|
+
export declare const connectorPayloadFullExample: () => ConnectorPayload;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { InsertHistoryTimeId } from '../insertHistory/insertHistory.ts';
|
|
2
|
+
import { ConnectorPayload } from './connector-payload.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Client → Server request for missing refs on a specific route.
|
|
5
|
+
*
|
|
6
|
+
* Sent on the `${route}:gapfill:req` event when a Connector detects
|
|
7
|
+
* a gap in the sequence numbers it has received from a peer.
|
|
8
|
+
*/
|
|
9
|
+
export type GapFillRequest = {
|
|
10
|
+
/** The route for which refs are missing. */
|
|
11
|
+
route: string;
|
|
12
|
+
/** The last sequence number the client has successfully processed. */
|
|
13
|
+
afterSeq: number;
|
|
14
|
+
/**
|
|
15
|
+
* Alternative / additional anchor: the last known InsertHistory timeId.
|
|
16
|
+
* The server may use this to determine the starting point if sequence
|
|
17
|
+
* numbers are unavailable.
|
|
18
|
+
*/
|
|
19
|
+
afterTimeId?: InsertHistoryTimeId;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Server → Client response containing the missing refs.
|
|
23
|
+
*
|
|
24
|
+
* Sent on the `${route}:gapfill:res` event in response to a
|
|
25
|
+
* `GapFillRequest`. The `refs` array is ordered chronologically
|
|
26
|
+
* (oldest first).
|
|
27
|
+
*/
|
|
28
|
+
export type GapFillResponse = {
|
|
29
|
+
/** The route this response corresponds to. */
|
|
30
|
+
route: string;
|
|
31
|
+
/** Ordered list of missing payloads (oldest first). */
|
|
32
|
+
refs: ConnectorPayload[];
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Returns an example GapFillRequest.
|
|
36
|
+
*/
|
|
37
|
+
export declare const gapFillRequestExample: () => GapFillRequest;
|
|
38
|
+
/**
|
|
39
|
+
* Returns an example GapFillResponse with two missing refs.
|
|
40
|
+
*/
|
|
41
|
+
export declare const gapFillResponseExample: () => GapFillResponse;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature flags for hardened sync behavior.
|
|
3
|
+
*
|
|
4
|
+
* All flags default to `false` / `undefined`, so existing code that
|
|
5
|
+
* does not set a `SyncConfig` continues to work exactly as before.
|
|
6
|
+
*
|
|
7
|
+
* | Flag | Concept | Effect when enabled |
|
|
8
|
+
* |-------------------------|----------------------|----------------------------------------|
|
|
9
|
+
* | `causalOrdering` | Predecessor chain | Attach `seq` + `p` to payloads; |
|
|
10
|
+
* | | | detect gaps; request gap-fill |
|
|
11
|
+
* | `requireAck` | Acknowledgment | Wait for server ACK after send |
|
|
12
|
+
* | `ackTimeoutMs` | Acknowledgment | Timeout before treating ACK as failed |
|
|
13
|
+
* | `includeClientIdentity` | Client identity | Attach `c` + `t` to payloads |
|
|
14
|
+
* | `maxDedupSetSize` | Memory management | Cap dedup sets; default 10 000 |
|
|
15
|
+
*/
|
|
16
|
+
export type SyncConfig = {
|
|
17
|
+
/**
|
|
18
|
+
* When `true`, the Connector attaches a monotonic `seq` number and
|
|
19
|
+
* causal predecessor timeIds (`p`) to every outgoing payload. Receiving
|
|
20
|
+
* Connectors detect sequence gaps and request gap-fill from the server.
|
|
21
|
+
*/
|
|
22
|
+
causalOrdering?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* When `true`, the Connector awaits a server-side `AckPayload` after
|
|
25
|
+
* sending a ref. The `sendWithAck()` method becomes available.
|
|
26
|
+
*/
|
|
27
|
+
requireAck?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Timeout in milliseconds for awaiting an ACK from the server.
|
|
30
|
+
* Only meaningful when `requireAck` is `true`.
|
|
31
|
+
* Defaults to `10_000` (10 seconds) when not specified.
|
|
32
|
+
*/
|
|
33
|
+
ackTimeoutMs?: number;
|
|
34
|
+
/**
|
|
35
|
+
* When `true`, the Connector attaches the stable `ClientId` (`c`) and
|
|
36
|
+
* a wall-clock timestamp (`t`) to every outgoing payload.
|
|
37
|
+
*/
|
|
38
|
+
includeClientIdentity?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Maximum number of entries per dedup set generation before rotation.
|
|
41
|
+
* The Connector uses two-generation eviction: when the current generation
|
|
42
|
+
* exceeds this limit, it becomes the previous generation and a new empty
|
|
43
|
+
* set is created. Lookups check both generations.
|
|
44
|
+
* Defaults to `10_000` when not specified.
|
|
45
|
+
*/
|
|
46
|
+
maxDedupSetSize?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Interval in milliseconds for the server to broadcast its latest ref
|
|
49
|
+
* to all connected clients as a heartbeat. This acts as a fallback
|
|
50
|
+
* in case the initial bootstrap message on connect was missed.
|
|
51
|
+
*
|
|
52
|
+
* When `undefined` or `0`, no periodic heartbeat is sent — only the
|
|
53
|
+
* immediate bootstrap on `addSocket()` is active.
|
|
54
|
+
*
|
|
55
|
+
* Recommended value: `30_000` (30 seconds) or higher.
|
|
56
|
+
*/
|
|
57
|
+
bootstrapHeartbeatMs?: number;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Default SyncConfig — all features disabled (backward-compatible mode).
|
|
61
|
+
*/
|
|
62
|
+
export declare const syncConfigDefault: () => SyncConfig;
|
|
63
|
+
/**
|
|
64
|
+
* Returns an example SyncConfig with all features enabled.
|
|
65
|
+
*/
|
|
66
|
+
export declare const syncConfigFullExample: () => SyncConfig;
|
|
67
|
+
/**
|
|
68
|
+
* Returns an example SyncConfig with only causal ordering enabled.
|
|
69
|
+
*/
|
|
70
|
+
export declare const syncConfigCausalOnlyExample: () => SyncConfig;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The set of socket event names used by the sync protocol for a given route.
|
|
3
|
+
* Using this helper avoids hard-coding event name strings across layers
|
|
4
|
+
* and prevents typos.
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const events = syncEvents('/sharedTree');
|
|
8
|
+
* socket.on(events.ref, handleRef);
|
|
9
|
+
* socket.on(events.ack, handleAck);
|
|
10
|
+
* socket.emit(events.gapFillReq, request);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export type SyncEventNames = {
|
|
14
|
+
/** Bidirectional: ref broadcast (existing event). */
|
|
15
|
+
ref: string;
|
|
16
|
+
/** Server → Client: delivery acknowledgment. */
|
|
17
|
+
ack: string;
|
|
18
|
+
/** Client → Server: individual client ACK of a received ref. */
|
|
19
|
+
ackClient: string;
|
|
20
|
+
/** Client → Server: request missing refs. */
|
|
21
|
+
gapFillReq: string;
|
|
22
|
+
/** Server → Client: supply missing refs. */
|
|
23
|
+
gapFillRes: string;
|
|
24
|
+
/** Server → Client: bootstrap latest ref on connect / heartbeat. */
|
|
25
|
+
bootstrap: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Creates typed, route-specific socket event names for the sync protocol.
|
|
29
|
+
* @param route - The route string (e.g. `"/sharedTree"`)
|
|
30
|
+
* @returns An object with all sync event names derived from the route
|
|
31
|
+
*/
|
|
32
|
+
export declare const syncEvents: (route: string) => SyncEventNames;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rljson/rljson",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.76",
|
|
4
4
|
"description": "The RLJSON data format specification",
|
|
5
5
|
"homepage": "https://github.com/rljson/rljson",
|
|
6
6
|
"bugs": "https://github.com/rljson/rljson/issues",
|
|
@@ -20,24 +20,24 @@
|
|
|
20
20
|
],
|
|
21
21
|
"type": "module",
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^25.
|
|
24
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
25
|
-
"@typescript-eslint/parser": "^8.
|
|
26
|
-
"@vitest/coverage-v8": "^4.0.
|
|
23
|
+
"@types/node": "^25.2.3",
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
25
|
+
"@typescript-eslint/parser": "^8.55.0",
|
|
26
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
27
27
|
"cross-env": "^10.1.0",
|
|
28
28
|
"eslint": "^9.39.2",
|
|
29
|
-
"eslint-plugin-jsdoc": "^62.
|
|
29
|
+
"eslint-plugin-jsdoc": "^62.5.0",
|
|
30
30
|
"eslint-plugin-tsdoc": "^0.5.0",
|
|
31
|
-
"globals": "^17.
|
|
31
|
+
"globals": "^17.3.0",
|
|
32
32
|
"jsdoc": "^4.0.5",
|
|
33
|
-
"read-pkg": "^10.
|
|
33
|
+
"read-pkg": "^10.1.0",
|
|
34
34
|
"typescript": "~5.9.3",
|
|
35
|
-
"typescript-eslint": "^8.
|
|
35
|
+
"typescript-eslint": "^8.55.0",
|
|
36
36
|
"vite": "^7.3.1",
|
|
37
37
|
"vite-node": "^5.3.0",
|
|
38
38
|
"vite-plugin-dts": "^4.5.4",
|
|
39
|
-
"vite-tsconfig-paths": "^6.0
|
|
40
|
-
"vitest": "^4.0.
|
|
39
|
+
"vite-tsconfig-paths": "^6.1.0",
|
|
40
|
+
"vitest": "^4.0.18",
|
|
41
41
|
"vitest-dom": "^0.1.1"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|