@rljson/rljson 0.0.75 → 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.
@@ -1,7 +1,400 @@
1
- # @rljson/
1
+ <!--
2
+ @license
3
+ Copyright (c) 2025 Rljson
2
4
 
3
- Todo: Add description here
5
+ Use of this source code is governed by terms that can be
6
+ found in the LICENSE file in the root of this package.
7
+ -->
4
8
 
5
- ## Example
9
+ # @rljson/rljson
6
10
 
7
- [src/example.ts](src/example.ts)
11
+ Core types, validation, and sync protocol for the RLJSON data format.
12
+
13
+ ## Table of Contents <!-- omit in toc -->
14
+
15
+ - [Installation](#installation)
16
+ - [Overview](#overview)
17
+ - [Data Model Types](#data-model-types)
18
+ - [Components](#components)
19
+ - [SliceIds](#sliceids)
20
+ - [Layers](#layers)
21
+ - [Cakes](#cakes)
22
+ - [Buffets](#buffets)
23
+ - [Trees](#trees)
24
+ - [Schema System](#schema-system)
25
+ - [TableCfg](#tablecfg)
26
+ - [Validation](#validation)
27
+ - [Edit Protocol](#edit-protocol)
28
+ - [Insert](#insert)
29
+ - [InsertHistory](#inserthistory)
30
+ - [Edits, MultiEdits, EditHistory](#edits-multiedits-edithistory)
31
+ - [Routing](#routing)
32
+ - [Sync Protocol](#sync-protocol)
33
+ - [ConnectorPayload](#connectorpayload)
34
+ - [AckPayload](#ackpayload)
35
+ - [GapFill](#gapfill)
36
+ - [SyncConfig](#syncconfig)
37
+ - [SyncEventNames](#synceventnames)
38
+ - [ClientId](#clientid)
39
+ - [Utilities](#utilities)
40
+ - [TimeId](#timeid)
41
+ - [RemoveDuplicates](#removeduplicates)
42
+ - [Ecosystem](#ecosystem)
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pnpm add @rljson/rljson
48
+ ```
49
+
50
+ ## Overview
51
+
52
+ `@rljson/rljson` is the foundational types package for the RLJSON ecosystem. It
53
+ defines the data format specification — a JSON-based, relational, normalized,
54
+ deeply-hashed exchange format designed for efficient synchronization of large
55
+ datasets.
56
+
57
+ ```typescript
58
+ import {
59
+ Rljson,
60
+ Route,
61
+ ConnectorPayload,
62
+ SyncConfig,
63
+ timeId,
64
+ } from '@rljson/rljson';
65
+ ```
66
+
67
+ ## Data Model Types
68
+
69
+ ### Components
70
+
71
+ The fundamental data unit. A `ComponentsTable` contains key-value rows with an
72
+ auto-generated `_hash` serving as the primary key.
73
+
74
+ ```typescript
75
+ import { ComponentsTable } from '@rljson/rljson';
76
+
77
+ const ingredients: ComponentsTable = {
78
+ _type: 'components',
79
+ _data: [
80
+ { id: 'flour', amountUnit: 'g', _hash: 'A5d...' },
81
+ { id: 'sugar', amountUnit: 'g', _hash: 'B7f...' },
82
+ ],
83
+ _hash: 't5o...',
84
+ };
85
+ ```
86
+
87
+ ### SliceIds
88
+
89
+ For efficient management of large layers, slice IDs are separated from their
90
+ data. This allows fetching IDs first and retrieving details on demand.
91
+
92
+ ```typescript
93
+ import { SliceIds, SliceIdsTable } from '@rljson/rljson';
94
+
95
+ const slices: SliceIdsTable = {
96
+ _type: 'sliceIds',
97
+ _data: [
98
+ { add: ['slice0', 'slice1'], remove: [], _hash: 'wyY...' },
99
+ ],
100
+ _hash: 'cnG...',
101
+ };
102
+ ```
103
+
104
+ Derived sets can modify an existing set using `base`, `add`, and `remove`.
105
+
106
+ ### Layers
107
+
108
+ Layers assign components to slices.
109
+
110
+ ```typescript
111
+ import { Layer, LayersTable } from '@rljson/rljson';
112
+ ```
113
+
114
+ A layer references a `componentsTable` and a `sliceIdsTable`, then maps each
115
+ slice ID to a component hash in its `add` block.
116
+
117
+ ### Cakes
118
+
119
+ A `Cake` is a stack of layers sharing the same slice IDs. Each layer assigns
120
+ different component values to the shared slices.
121
+
122
+ ```typescript
123
+ import { Cake, CakesTable } from '@rljson/rljson';
124
+ ```
125
+
126
+ ### Buffets
127
+
128
+ A `Buffet` is a heterogeneous collection of related items — references to rows
129
+ in any other table (cakes, layers, components, etc.).
130
+
131
+ ```typescript
132
+ import { Buffet, BuffetsTable } from '@rljson/rljson';
133
+ ```
134
+
135
+ ### Trees
136
+
137
+ Hierarchical tree structures with content-addressed nodes. Each `Tree` has an
138
+ optional `id` (unique among siblings), an `isParent` flag, a `meta` field
139
+ (`Json | null`), and a `children` array of child hashes (or `null` for leaves).
140
+
141
+ ```typescript
142
+ import { Tree, TreesTable, treeFromObject } from '@rljson/rljson';
143
+
144
+ // Convert a plain object into hashed tree nodes
145
+ const nodes = treeFromObject({ src: { 'index.ts': 'console.log("hello")' } });
146
+ ```
147
+
148
+ ## Schema System
149
+
150
+ ### TableCfg
151
+
152
+ `TableCfg` defines the schema for any table — its key, content type, column
153
+ definitions, and metadata flags.
154
+
155
+ ```typescript
156
+ import { TableCfg, ColumnCfg, throwOnInvalidTableCfg } from '@rljson/rljson';
157
+
158
+ const cfg: TableCfg = {
159
+ key: 'ingredients',
160
+ type: 'components',
161
+ columns: [
162
+ { key: '_hash', type: 'string', titleLong: 'Hash', titleShort: 'Hash' },
163
+ { key: 'name', type: 'string', titleLong: 'Name', titleShort: 'Name' },
164
+ ],
165
+ isHead: false,
166
+ isRoot: false,
167
+ isShared: false,
168
+ };
169
+
170
+ throwOnInvalidTableCfg(cfg); // throws on invalid config
171
+ ```
172
+
173
+ Column types: `string`, `number`, `boolean`, `json`, `jsonArray`.
174
+
175
+ Columns can reference other tables via the `ref` property on `ColumnCfgWithRef`.
176
+
177
+ ### Validation
178
+
179
+ The `Validate` class coordinates multiple validators to check entire RLJSON
180
+ objects — naming conventions, hash integrity, reference validity, tree
181
+ structure, layer/cake/buffet consistency.
182
+
183
+ ```typescript
184
+ import { Validate, BaseValidator } from '@rljson/rljson';
185
+
186
+ const validate = new Validate();
187
+ validate.addValidator(new BaseValidator());
188
+ const errors = await validate.run(myRljsonData);
189
+ // errors: { base: { hasErrors: false } }
190
+ ```
191
+
192
+ The `BaseValidator` checks all core rules. Custom validators can be added by
193
+ implementing the `Validator` interface.
194
+
195
+ ## Edit Protocol
196
+
197
+ ### Insert
198
+
199
+ An `Insert` describes a data modification operation with a command (`add`),
200
+ a value, a route, and an optional origin.
201
+
202
+ ```typescript
203
+ import { Insert, validateInsert } from '@rljson/rljson';
204
+
205
+ const insert: Insert<any> = {
206
+ route: '/ingredients',
207
+ command: 'add',
208
+ value: { name: 'butter', amountUnit: 'g' },
209
+ };
210
+
211
+ const errors = validateInsert(insert);
212
+ ```
213
+
214
+ ### InsertHistory
215
+
216
+ `InsertHistoryRow` tracks each insert operation with a unique `timeId`, the
217
+ `route` that was modified, and optional `origin`, `previous` (causal
218
+ predecessors), and `clientTimestamp`.
219
+
220
+ ```typescript
221
+ import { InsertHistoryRow, InsertHistoryTimeId } from '@rljson/rljson';
222
+
223
+ const row: InsertHistoryRow<'ingredients'> = {
224
+ ingredientsRef: 'A5d...',
225
+ timeId: '1700000000000:AbCd',
226
+ route: '/ingredients',
227
+ origin: 'client_ExAmPlE12345',
228
+ previous: ['1699999999999:ZzZz'],
229
+ clientTimestamp: 1700000000000,
230
+ };
231
+ ```
232
+
233
+ ### Edits, MultiEdits, EditHistory
234
+
235
+ - **Edit**: A named action with a type and data payload (`EditAction`)
236
+ - **MultiEdit**: Chains edits into a linked list via `previous` ref
237
+ - **EditHistory**: Tracks the full chain of multi-edits with `timeId` timestamps
238
+
239
+ ## Routing
240
+
241
+ The `Route` class parses and builds hierarchical data paths used throughout
242
+ the RLJSON ecosystem.
243
+
244
+ ```typescript
245
+ import { Route } from '@rljson/rljson';
246
+
247
+ // Parse a flat route string
248
+ const route = Route.fromFlat('/ingredients@A5d.../nutritionalValues');
249
+
250
+ // Navigate route segments
251
+ route.top; // first segment: { tableKey: 'ingredients', ... }
252
+ route.root; // last segment: { tableKey: 'nutritionalValues' }
253
+ route.segment(0); // segment by index
254
+ route.flat; // serialized string
255
+ ```
256
+
257
+ Routes support references (`@hash`), slice IDs (`(id1,id2)`), and
258
+ InsertHistory refs (`@timestamp:unique`).
259
+
260
+ ## Sync Protocol
261
+
262
+ The `sync/` module defines wire-protocol types for the messaging hardening
263
+ system used by `@rljson/db` (Connector) and `@rljson/server`.
264
+
265
+ ### ConnectorPayload
266
+
267
+ The payload transmitted between Connector and Server on the wire.
268
+
269
+ ```typescript
270
+ import { ConnectorPayload } from '@rljson/rljson';
271
+
272
+ // Minimal (backward-compatible)
273
+ const legacy: ConnectorPayload = { o: 'origin', r: 'ref' };
274
+
275
+ // Fully enriched
276
+ const enriched: ConnectorPayload = {
277
+ o: 'origin', // ephemeral origin (self-echo filter)
278
+ r: 'ref', // the ref being announced
279
+ c: 'client_abc123...', // stable client identity
280
+ t: Date.now(), // client-side timestamp
281
+ seq: 42, // monotonic sequence number
282
+ p: ['prev-timeId'], // causal predecessors
283
+ cksum: 'sha256:...', // content checksum
284
+ };
285
+ ```
286
+
287
+ ### AckPayload
288
+
289
+ Server → Client acknowledgment that all (or some) receivers got a ref.
290
+
291
+ ```typescript
292
+ import { AckPayload } from '@rljson/rljson';
293
+
294
+ const ack: AckPayload = {
295
+ r: 'ref',
296
+ ok: true,
297
+ receivedBy: 3,
298
+ totalClients: 3,
299
+ };
300
+ ```
301
+
302
+ ### GapFill
303
+
304
+ Types for requesting and receiving missing refs when a sequence gap is
305
+ detected.
306
+
307
+ ```typescript
308
+ import { GapFillRequest, GapFillResponse } from '@rljson/rljson';
309
+
310
+ const request: GapFillRequest = {
311
+ route: '/sharedTree',
312
+ afterSeq: 5,
313
+ };
314
+ ```
315
+
316
+ ### SyncConfig
317
+
318
+ Feature flags to opt into hardened sync behavior. All flags are optional and
319
+ default to off.
320
+
321
+ ```typescript
322
+ import { SyncConfig } from '@rljson/rljson';
323
+
324
+ const config: SyncConfig = {
325
+ causalOrdering: true, // track predecessors + detect gaps
326
+ requireAck: true, // wait for server ACK after send
327
+ ackTimeoutMs: 5_000, // ACK timeout
328
+ includeClientIdentity: true, // attach clientId + timestamp
329
+ maxDedupSetSize: 10_000, // max refs per dedup generation (default: 10 000)
330
+ bootstrapHeartbeatMs: 30_000, // periodic bootstrap heartbeat interval (optional)
331
+ };
332
+ ```
333
+
334
+ ### SyncEventNames
335
+
336
+ Helper to generate typed, route-specific socket event names.
337
+
338
+ ```typescript
339
+ import { syncEvents } from '@rljson/rljson';
340
+
341
+ const events = syncEvents('/sharedTree');
342
+ // events.ref → '/sharedTree'
343
+ // events.ack → '/sharedTree:ack'
344
+ // events.ackClient → '/sharedTree:ack:client'
345
+ // events.gapFillReq → '/sharedTree:gapfill:req'
346
+ // events.gapFillRes → '/sharedTree:gapfill:res'
347
+ // events.bootstrap → '/sharedTree:bootstrap'
348
+ ```
349
+
350
+ ### ClientId
351
+
352
+ A stable client identity that persists across reconnections (unlike the
353
+ ephemeral Connector origin).
354
+
355
+ ```typescript
356
+ import { clientId, isClientId } from '@rljson/rljson';
357
+
358
+ const id = clientId(); // 'client_V1StGXR8_Z5j'
359
+ isClientId(id); // true
360
+ isClientId('not-a-client-id'); // false
361
+ ```
362
+
363
+ ## Utilities
364
+
365
+ ### TimeId
366
+
367
+ A unique, time-based identifier in the format `"timestamp:xxxx"`.
368
+
369
+ ```typescript
370
+ import { timeId, isTimeId, getTimeIdTimestamp } from '@rljson/rljson';
371
+
372
+ const id = timeId(); // '1700000000000:AbCd'
373
+ isTimeId(id); // true
374
+ getTimeIdTimestamp(id); // 1700000000000
375
+ ```
376
+
377
+ ### RemoveDuplicates
378
+
379
+ Deduplicates rows (by `_hash`) across all tables in an Rljson object.
380
+
381
+ ```typescript
382
+ import { removeDuplicates } from '@rljson/rljson';
383
+
384
+ const deduped = removeDuplicates(myRljsonData);
385
+ ```
386
+
387
+ ## Ecosystem
388
+
389
+ `@rljson/rljson` is the foundational layer of the RLJSON ecosystem:
390
+
391
+ | Package | Purpose |
392
+ | ------------------ | ----------------------------------- |
393
+ | `@rljson/rljson` | Core types & validation (this) |
394
+ | `@rljson/hash` | Deep hashing for RLJSON data |
395
+ | `@rljson/json` | JSON type definitions |
396
+ | `@rljson/io` | Data transport (Io, Socket, IoPeer) |
397
+ | `@rljson/bs` | Blob storage (Bs, BsPeer) |
398
+ | `@rljson/db` | Database pipeline (Db, Connector) |
399
+ | `@rljson/server` | Server relay & Client setup |
400
+ | `@rljson/fs-agent` | Filesystem ↔ database sync |
@@ -1,5 +1,9 @@
1
1
  <!--
2
+ @license
3
+ Copyright (c) 2025 Rljson
2
4
 
5
+ Use of this source code is governed by terms that can be
6
+ found in the LICENSE file in the root of this package.
3
7
  -->
4
8
 
5
9
  # Trouble shooting
package/dist/index.d.ts CHANGED
@@ -16,6 +16,12 @@ export * from './insert/insert.ts';
16
16
  export * from './insertHistory/insertHistory.ts';
17
17
  export * from './rljson.ts';
18
18
  export * from './route/route.ts';
19
+ export * from './sync/ack-payload.ts';
20
+ export * from './sync/client-id.ts';
21
+ export * from './sync/connector-payload.ts';
22
+ export * from './sync/gap-fill.ts';
23
+ export * from './sync/sync-config.ts';
24
+ export * from './sync/sync-events.ts';
19
25
  export * from './tools/remove-duplicates.ts';
20
26
  export * from './tools/time-id.ts';
21
27
  export * from './typedefs.ts';
@@ -1,6 +1,7 @@
1
1
  import { TableCfg } from '../content/table-cfg.ts';
2
2
  import { RljsonTable } from '../rljson.ts';
3
3
  import { RouteRef } from '../route/route.ts';
4
+ import { ClientId } from '../sync/client-id.ts';
4
5
  import { Ref } from '../typedefs.ts';
5
6
  export type InsertHistoryTimeId = string;
6
7
  export type InsertHistoryRow<Str extends string> = {
@@ -8,8 +9,9 @@ export type InsertHistoryRow<Str extends string> = {
8
9
  } & {
9
10
  timeId: InsertHistoryTimeId;
10
11
  route: RouteRef;
11
- origin?: Ref;
12
+ origin?: ClientId | Ref;
12
13
  previous?: InsertHistoryTimeId[];
14
+ clientTimestamp?: number;
13
15
  };
14
16
  export type InsertHistoryTable<Str extends string> = RljsonTable<InsertHistoryRow<Str>, 'insertHistory'>;
15
17
  /**
package/dist/rljson.js CHANGED
@@ -1926,6 +1926,12 @@ const createInsertHistoryTableCfg = (tableCfg) => ({
1926
1926
  type: "jsonArray",
1927
1927
  titleLong: "Previous",
1928
1928
  titleShort: "Previous"
1929
+ },
1930
+ {
1931
+ key: "clientTimestamp",
1932
+ type: "number",
1933
+ titleLong: "Client Timestamp",
1934
+ titleShort: "Timestamp"
1929
1935
  }
1930
1936
  ],
1931
1937
  isHead: false,
@@ -1972,6 +1978,71 @@ const iterateTables = async (rljson, callback) => {
1972
1978
  throw errors;
1973
1979
  }
1974
1980
  };
1981
+ const ackPayloadExample = () => ({
1982
+ r: "1700000000001:EfGh",
1983
+ ok: true,
1984
+ receivedBy: 3,
1985
+ totalClients: 3
1986
+ });
1987
+ const ackPayloadPartialExample = () => ({
1988
+ r: "1700000000001:EfGh",
1989
+ ok: false,
1990
+ receivedBy: 1,
1991
+ totalClients: 3
1992
+ });
1993
+ const clientId = () => {
1994
+ return "client_" + nanoid(12);
1995
+ };
1996
+ const isClientId = (id) => {
1997
+ if (!id.startsWith("client_")) return false;
1998
+ const suffix = id.slice("client_".length);
1999
+ return suffix.length === 12;
2000
+ };
2001
+ const clientIdExample = () => "client_ExAmPlE12345";
2002
+ const connectorPayloadExample = () => ({
2003
+ o: "1700000000000:AbCd",
2004
+ r: "1700000000001:EfGh"
2005
+ });
2006
+ const connectorPayloadFullExample = () => ({
2007
+ o: "1700000000000:AbCd",
2008
+ r: "1700000000001:EfGh",
2009
+ c: "client_ExAmPlE12345",
2010
+ t: 1700000000001,
2011
+ seq: 42,
2012
+ p: ["1700000000000:XyZw"],
2013
+ cksum: "sha256:abc123def456"
2014
+ });
2015
+ const gapFillRequestExample = () => ({
2016
+ route: "/sharedTree",
2017
+ afterSeq: 5,
2018
+ afterTimeId: "1700000000000:AbCd"
2019
+ });
2020
+ const gapFillResponseExample = () => ({
2021
+ route: "/sharedTree",
2022
+ refs: [
2023
+ { o: "1700000000000:AbCd", r: "1700000000006:MnOp", seq: 6 },
2024
+ { o: "1700000000000:AbCd", r: "1700000000007:QrSt", seq: 7 }
2025
+ ]
2026
+ });
2027
+ const syncConfigDefault = () => ({});
2028
+ const syncConfigFullExample = () => ({
2029
+ causalOrdering: true,
2030
+ requireAck: true,
2031
+ ackTimeoutMs: 5e3,
2032
+ includeClientIdentity: true,
2033
+ maxDedupSetSize: 1e4
2034
+ });
2035
+ const syncConfigCausalOnlyExample = () => ({
2036
+ causalOrdering: true
2037
+ });
2038
+ const syncEvents = (route) => ({
2039
+ ref: route,
2040
+ ack: `${route}:ack`,
2041
+ ackClient: `${route}:ack:client`,
2042
+ gapFillReq: `${route}:gapfill:req`,
2043
+ gapFillRes: `${route}:gapfill:res`,
2044
+ bootstrap: `${route}:bootstrap`
2045
+ });
1975
2046
  const removeDuplicates = (rljson) => {
1976
2047
  const result = {};
1977
2048
  for (const key in rljson) {
@@ -2971,8 +3042,14 @@ export {
2971
3042
  InsertValidator,
2972
3043
  Route,
2973
3044
  Validate,
3045
+ ackPayloadExample,
3046
+ ackPayloadPartialExample,
2974
3047
  addColumnsToTableCfg,
2975
3048
  bakeryExample,
3049
+ clientId,
3050
+ clientIdExample,
3051
+ connectorPayloadExample,
3052
+ connectorPayloadFullExample,
2976
3053
  contentTypes,
2977
3054
  createCakeTableCfg,
2978
3055
  createEditHistoryTableCfg,
@@ -2995,8 +3072,11 @@ export {
2995
3072
  exampleTableCfgTable,
2996
3073
  exampleTreesTable,
2997
3074
  exampleTypedefs,
3075
+ gapFillRequestExample,
3076
+ gapFillResponseExample,
2998
3077
  getTimeIdTimestamp,
2999
3078
  getTimeIdUniquePart,
3079
+ isClientId,
3000
3080
  isTimeId,
3001
3081
  isValidFieldName,
3002
3082
  iterateTables,
@@ -3007,6 +3087,10 @@ export {
3007
3087
  routeRefSeperator,
3008
3088
  routeSliceIdIndicators,
3009
3089
  routeSliceIdSeperator,
3090
+ syncConfigCausalOnlyExample,
3091
+ syncConfigDefault,
3092
+ syncConfigFullExample,
3093
+ syncEvents,
3010
3094
  throwOnInvalidTableCfg,
3011
3095
  timeId,
3012
3096
  treeFromObject,
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Server → Client acknowledgment that all (or some) receivers
3
+ * successfully received and processed a given ref.
4
+ *
5
+ * Sent on the `${route}:ack` event after the server has collected
6
+ * individual client ACKs (or after a timeout).
7
+ */
8
+ export type AckPayload = {
9
+ /** The ref being acknowledged. */
10
+ r: string;
11
+ /** `true` if all connected clients confirmed receipt; `false` on timeout / partial. */
12
+ ok: boolean;
13
+ /** Number of clients that confirmed receipt. */
14
+ receivedBy?: number;
15
+ /** Total number of receiver clients at the time of broadcast. */
16
+ totalClients?: number;
17
+ };
18
+ /**
19
+ * Returns an example AckPayload where all clients confirmed receipt.
20
+ */
21
+ export declare const ackPayloadExample: () => AckPayload;
22
+ /**
23
+ * Returns an example AckPayload for a partial / timed-out acknowledgment.
24
+ */
25
+ export declare const ackPayloadPartialExample: () => AckPayload;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A stable client identity that persists across reconnections.
3
+ *
4
+ * Unlike a Connector's ephemeral `origin` (which changes on every
5
+ * instantiation), a `ClientId` should be generated once and stored
6
+ * (e.g. in local storage) so that it can be reused across sessions.
7
+ */
8
+ export type ClientId = string;
9
+ /**
10
+ * Generates a new ClientId.
11
+ * A ClientId is a 12-character nanoid, prefixed with `"client_"` for
12
+ * easy visual identification in logs and debugging.
13
+ * @returns A new ClientId string (e.g. `"client_V1StGXR8_Z5j"`)
14
+ */
15
+ export declare const clientId: () => ClientId;
16
+ /**
17
+ * Checks whether a given string is a valid ClientId.
18
+ * A valid ClientId starts with `"client_"` followed by exactly 12
19
+ * characters.
20
+ * @param id - The string to check
21
+ * @returns `true` if the string matches the ClientId format
22
+ */
23
+ export declare const isClientId: (id: string) => boolean;
24
+ /**
25
+ * Returns an example ClientId for documentation and testing.
26
+ */
27
+ export declare const clientIdExample: () => ClientId;
@@ -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;