meridian-sdk 0.2.1 → 0.2.2
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/biome.json +4 -0
- package/dist/auth/token.d.ts +0 -19
- package/dist/auth/token.d.ts.map +1 -1
- package/dist/auth/token.js +6 -31
- package/dist/auth/token.js.map +1 -1
- package/dist/client.d.ts +139 -23
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +165 -52
- package/dist/client.js.map +1 -1
- package/dist/codec.d.ts +7 -35
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +13 -65
- package/dist/codec.js.map +1 -1
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +7 -0
- package/dist/constants.js.map +1 -0
- package/dist/crdt/gcounter.d.ts +18 -9
- package/dist/crdt/gcounter.d.ts.map +1 -1
- package/dist/crdt/gcounter.js +16 -13
- package/dist/crdt/gcounter.js.map +1 -1
- package/dist/crdt/lwwregister.d.ts +24 -11
- package/dist/crdt/lwwregister.d.ts.map +1 -1
- package/dist/crdt/lwwregister.js +25 -19
- package/dist/crdt/lwwregister.js.map +1 -1
- package/dist/crdt/orset.d.ts +25 -13
- package/dist/crdt/orset.d.ts.map +1 -1
- package/dist/crdt/orset.js +31 -23
- package/dist/crdt/orset.js.map +1 -1
- package/dist/crdt/pncounter.d.ts +22 -4
- package/dist/crdt/pncounter.d.ts.map +1 -1
- package/dist/crdt/pncounter.js +28 -14
- package/dist/crdt/pncounter.js.map +1 -1
- package/dist/crdt/presence.d.ts +33 -13
- package/dist/crdt/presence.d.ts.map +1 -1
- package/dist/crdt/presence.js +36 -20
- package/dist/crdt/presence.js.map +1 -1
- package/dist/errors.d.ts +0 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +0 -16
- package/dist/errors.js.map +1 -1
- package/dist/schema.d.ts +3 -9
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +3 -34
- package/dist/schema.js.map +1 -1
- package/dist/sync/clock.d.ts +1 -20
- package/dist/sync/clock.d.ts.map +1 -1
- package/dist/sync/clock.js +20 -46
- package/dist/sync/clock.js.map +1 -1
- package/dist/sync/delta.d.ts +5 -22
- package/dist/sync/delta.d.ts.map +1 -1
- package/dist/sync/delta.js +18 -26
- package/dist/sync/delta.js.map +1 -1
- package/dist/transport/http.d.ts +1 -14
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +3 -21
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/websocket.d.ts +0 -27
- package/dist/transport/websocket.d.ts.map +1 -1
- package/dist/transport/websocket.js +9 -37
- package/dist/transport/websocket.js.map +1 -1
- package/dist/utils/to-hex.d.ts +2 -0
- package/dist/utils/to-hex.d.ts.map +1 -0
- package/dist/utils/to-hex.js +2 -0
- package/dist/utils/to-hex.js.map +1 -0
- package/package.json +6 -3
- package/src/auth/token.ts +6 -34
- package/src/client.ts +165 -65
- package/src/codec.ts +13 -71
- package/src/constants.ts +6 -0
- package/src/crdt/gcounter.ts +18 -20
- package/src/crdt/lwwregister.ts +27 -26
- package/src/crdt/orset.ts +32 -29
- package/src/crdt/pncounter.ts +30 -21
- package/src/crdt/presence.ts +37 -26
- package/src/errors.ts +0 -21
- package/src/schema.ts +3 -44
- package/src/sync/clock.ts +18 -50
- package/src/sync/delta.ts +20 -58
- package/src/transport/http.ts +3 -33
- package/src/transport/websocket.ts +15 -52
- package/src/utils/to-hex.ts +1 -0
- package/test/integration.test.ts +2 -3
- package/test/sync.test.ts +1 -2
package/dist/codec.d.ts
CHANGED
|
@@ -1,40 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Msgpack codec — wraps msgpackr with Meridian-specific conventions.
|
|
3
|
-
*
|
|
4
|
-
* The server uses rmp-serde with `to_vec_named` (named fields) so enum
|
|
5
|
-
* variants are encoded as `{ "VariantName": { ...fields } }` maps.
|
|
6
|
-
* msgpackr handles this transparently in JS land.
|
|
7
|
-
*/
|
|
8
1
|
import { Effect } from "effect";
|
|
9
2
|
import { CodecError } from "./errors.js";
|
|
10
3
|
import { ServerMsg } from "./schema.js";
|
|
11
4
|
import type { ClientMsg, VectorClock } from "./schema.js";
|
|
12
|
-
|
|
13
|
-
export declare
|
|
14
|
-
|
|
15
|
-
export declare
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* `{ Subscribe: { crdt_id: "foo" } }` → msgpack map with one key.
|
|
20
|
-
*/
|
|
21
|
-
export declare function encodeClientMsg(msg: ClientMsg): Uint8Array;
|
|
22
|
-
/**
|
|
23
|
-
* Decode a binary WebSocket frame into a ServerMsg.
|
|
24
|
-
* Returns Effect<ServerMsg, CodecError>.
|
|
25
|
-
*/
|
|
26
|
-
export declare function decodeServerMsg(bytes: Uint8Array): Effect.Effect<ServerMsg, CodecError>;
|
|
27
|
-
/**
|
|
28
|
-
* Convert a UUID string ("xxxxxxxx-xxxx-...") to a 16-byte Uint8Array.
|
|
29
|
-
* Rust's `uuid::Uuid` is serialized by rmp-serde as raw bytes (bin type).
|
|
30
|
-
*/
|
|
31
|
-
export declare function uuidToBytes(uuid: string): Uint8Array;
|
|
32
|
-
/**
|
|
33
|
-
* Encode a wall-clock timestamp (ms) as BigInt for msgpack u64 encoding.
|
|
34
|
-
* msgpackr encodes JS `number` as float64 for large values; Rust u64 fields
|
|
35
|
-
* require an integer encoding — BigInt forces msgpackr to use uint64.
|
|
36
|
-
*/
|
|
37
|
-
export declare function wallMsToBigInt(ms: number): bigint;
|
|
38
|
-
export declare function encodeVectorClock(vc: VectorClock): Uint8Array;
|
|
39
|
-
export declare function decodeVectorClock(bytes: Uint8Array): Effect.Effect<VectorClock, CodecError>;
|
|
5
|
+
export declare const encode: (value: unknown) => Uint8Array;
|
|
6
|
+
export declare const decode: (bytes: Uint8Array) => unknown;
|
|
7
|
+
export declare const encodeClientMsg: (msg: ClientMsg) => Uint8Array;
|
|
8
|
+
export declare const decodeServerMsg: (bytes: Uint8Array) => Effect.Effect<ServerMsg, CodecError>;
|
|
9
|
+
export declare const uuidToBytes: (uuid: string) => Uint8Array;
|
|
10
|
+
export declare const encodeVectorClock: (vc: VectorClock) => Uint8Array;
|
|
11
|
+
export declare const decodeVectorClock: (bytes: Uint8Array) => Effect.Effect<VectorClock, CodecError>;
|
|
40
12
|
//# sourceMappingURL=codec.d.ts.map
|
package/dist/codec.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1D,eAAO,MAAM,MAAM,GAAI,OAAO,OAAO,KAAG,UAAkC,CAAC;AAE3E,eAAO,MAAM,MAAM,GAAI,OAAO,UAAU,KAAG,OAA+B,CAAC;AAE3E,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,KAAG,UAAyB,CAAC;AAE3E,eAAO,MAAM,eAAe,GAAI,OAAO,UAAU,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,CAgBtF,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,UAO1C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,IAAI,WAAW,KAAG,UAAqC,CAAC;AAE1F,eAAO,MAAM,iBAAiB,GAAI,OAAO,UAAU,KAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,UAAU,CAkB1F,CAAC"}
|
package/dist/codec.js
CHANGED
|
@@ -1,88 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
* Msgpack codec — wraps msgpackr with Meridian-specific conventions.
|
|
3
|
-
*
|
|
4
|
-
* The server uses rmp-serde with `to_vec_named` (named fields) so enum
|
|
5
|
-
* variants are encoded as `{ "VariantName": { ...fields } }` maps.
|
|
6
|
-
* msgpackr handles this transparently in JS land.
|
|
7
|
-
*/
|
|
8
|
-
import { pack, unpack } from "msgpackr";
|
|
1
|
+
import { decode as msgpackDecode, encode as msgpackEncode } from "@msgpack/msgpack";
|
|
9
2
|
import { Effect, Schema } from "effect";
|
|
10
3
|
import { CodecError } from "./errors.js";
|
|
11
4
|
import { ServerMsg } from "./schema.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export function encode(value) {
|
|
17
|
-
return pack(value);
|
|
18
|
-
}
|
|
19
|
-
/** Decode msgpack bytes to a plain JS value (unsafe — no schema validation). */
|
|
20
|
-
export function decode(bytes) {
|
|
21
|
-
return unpack(bytes);
|
|
22
|
-
}
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
// Typed helpers for WebSocket frames
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
/**
|
|
27
|
-
* Encode a ClientMsg to a binary WebSocket frame.
|
|
28
|
-
* The msgpack encoding matches rmp-serde's "named" enum format:
|
|
29
|
-
* `{ Subscribe: { crdt_id: "foo" } }` → msgpack map with one key.
|
|
30
|
-
*/
|
|
31
|
-
export function encodeClientMsg(msg) {
|
|
32
|
-
return encode(msg);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Decode a binary WebSocket frame into a ServerMsg.
|
|
36
|
-
* Returns Effect<ServerMsg, CodecError>.
|
|
37
|
-
*/
|
|
38
|
-
export function decodeServerMsg(bytes) {
|
|
5
|
+
export const encode = (value) => msgpackEncode(value);
|
|
6
|
+
export const decode = (bytes) => msgpackDecode(bytes);
|
|
7
|
+
export const encodeClientMsg = (msg) => encode(msg);
|
|
8
|
+
export const decodeServerMsg = (bytes) => {
|
|
39
9
|
let raw;
|
|
40
10
|
try {
|
|
41
|
-
raw =
|
|
11
|
+
raw = decode(bytes);
|
|
42
12
|
}
|
|
43
13
|
catch {
|
|
44
14
|
return Effect.fail(new CodecError({ message: "msgpack decode failed", raw: bytes }));
|
|
45
15
|
}
|
|
46
|
-
// Schema.decodeUnknown accepts `unknown` input — correct for runtime decode boundaries
|
|
47
16
|
return Schema.decodeUnknown(ServerMsg)(raw).pipe(Effect.mapError((parseError) => new CodecError({
|
|
48
17
|
message: `ServerMsg schema validation failed: ${parseError.message}`,
|
|
49
18
|
raw: bytes,
|
|
50
19
|
})));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Wire type helpers
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
/**
|
|
56
|
-
* Convert a UUID string ("xxxxxxxx-xxxx-...") to a 16-byte Uint8Array.
|
|
57
|
-
* Rust's `uuid::Uuid` is serialized by rmp-serde as raw bytes (bin type).
|
|
58
|
-
*/
|
|
59
|
-
export function uuidToBytes(uuid) {
|
|
20
|
+
};
|
|
21
|
+
export const uuidToBytes = (uuid) => {
|
|
60
22
|
const hex = uuid.replace(/-/g, "");
|
|
61
23
|
const bytes = new Uint8Array(16);
|
|
62
24
|
for (let i = 0; i < 16; i++) {
|
|
63
25
|
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
64
26
|
}
|
|
65
27
|
return bytes;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* msgpackr encodes JS `number` as float64 for large values; Rust u64 fields
|
|
70
|
-
* require an integer encoding — BigInt forces msgpackr to use uint64.
|
|
71
|
-
*/
|
|
72
|
-
export function wallMsToBigInt(ms) {
|
|
73
|
-
return BigInt(ms);
|
|
74
|
-
}
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// VectorClock <-> msgpack bytes
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
export function encodeVectorClock(vc) {
|
|
79
|
-
// Server expects { entries: { "client_id": version, ... } }
|
|
80
|
-
return encode({ entries: vc });
|
|
81
|
-
}
|
|
82
|
-
export function decodeVectorClock(bytes) {
|
|
28
|
+
};
|
|
29
|
+
export const encodeVectorClock = (vc) => encode({ entries: vc });
|
|
30
|
+
export const decodeVectorClock = (bytes) => {
|
|
83
31
|
let raw;
|
|
84
32
|
try {
|
|
85
|
-
raw =
|
|
33
|
+
raw = decode(bytes);
|
|
86
34
|
}
|
|
87
35
|
catch {
|
|
88
36
|
return Effect.fail(new CodecError({ message: "VectorClock msgpack decode failed", raw: bytes }));
|
|
@@ -91,5 +39,5 @@ export function decodeVectorClock(bytes) {
|
|
|
91
39
|
? raw.entries
|
|
92
40
|
: {};
|
|
93
41
|
return Schema.decodeUnknown(Schema.Record({ key: Schema.String, value: Schema.Number }))(entries ?? {}).pipe(Effect.mapError((e) => new CodecError({ message: `VectorClock schema validation failed: ${e.message}`, raw: bytes })));
|
|
94
|
-
}
|
|
42
|
+
};
|
|
95
43
|
//# sourceMappingURL=codec.js.map
|
package/dist/codec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const BACKOFF_INITIAL_MS = 100;
|
|
2
|
+
export declare const BACKOFF_MAX_MS = 30000;
|
|
3
|
+
export declare const BACKOFF_MULTIPLIER = 2;
|
|
4
|
+
export declare const JITTER_MULTIPLIER = 0.2;
|
|
5
|
+
export declare const DEFAULT_TIMEOUT_MS = 5000;
|
|
6
|
+
export declare const TOKEN_SKEW_MS = 5000;
|
|
7
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,cAAc,QAAS,CAAC;AACrC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AACxC,eAAO,MAAM,aAAa,OAAQ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const BACKOFF_INITIAL_MS = 100;
|
|
2
|
+
export const BACKOFF_MAX_MS = 30_000;
|
|
3
|
+
export const BACKOFF_MULTIPLIER = 2;
|
|
4
|
+
export const JITTER_MULTIPLIER = 0.2;
|
|
5
|
+
export const DEFAULT_TIMEOUT_MS = 5_000;
|
|
6
|
+
export const TOKEN_SKEW_MS = 5_000;
|
|
7
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AACrC,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACxC,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC"}
|
package/dist/crdt/gcounter.d.ts
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GCounter handle — increment-only counter.
|
|
3
|
-
*
|
|
4
|
-
* Local state is kept as a sparse map `{ client_id → count }`.
|
|
5
|
-
* `value()` returns the sum. Deltas are merged on incoming ServerMsg.Delta.
|
|
6
|
-
*/
|
|
7
1
|
import type { WsTransport } from "../transport/websocket.js";
|
|
8
2
|
import type { GCounterDelta } from "../sync/delta.js";
|
|
9
3
|
export interface GCounterState {
|
|
10
4
|
counts: Record<string, number>;
|
|
11
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Low-level handle for a grow-only counter (GCounter) CRDT.
|
|
8
|
+
*
|
|
9
|
+
* Obtained via `MeridianClient.gcounter()`. Prefer the `useGCounter` React hook
|
|
10
|
+
* for component-level usage; use this handle directly in non-React environments.
|
|
11
|
+
*/
|
|
12
12
|
export declare class GCounterHandle {
|
|
13
13
|
private state;
|
|
14
14
|
private readonly clientId;
|
|
15
15
|
private readonly crdtId;
|
|
16
16
|
private readonly ns;
|
|
17
17
|
private readonly transport;
|
|
18
|
-
/** Emits on every state change. */
|
|
19
18
|
private readonly listeners;
|
|
20
19
|
constructor(opts: {
|
|
21
20
|
ns: string;
|
|
@@ -24,11 +23,21 @@ export declare class GCounterHandle {
|
|
|
24
23
|
transport: WsTransport;
|
|
25
24
|
initial?: GCounterState;
|
|
26
25
|
});
|
|
26
|
+
/** Returns the current counter value (sum of all client contributions). */
|
|
27
27
|
value(): number;
|
|
28
|
+
/** Returns the raw per-client contribution map, keyed by client id string. */
|
|
28
29
|
counts(): Readonly<Record<string, number>>;
|
|
29
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Registers a listener that is called whenever the counter value changes.
|
|
32
|
+
*
|
|
33
|
+
* @returns An unsubscribe function — call it to stop receiving updates.
|
|
34
|
+
*/
|
|
30
35
|
onChange(listener: (value: number) => void): () => void;
|
|
31
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Increments the counter by `amount` (default `1`) and broadcasts the delta.
|
|
38
|
+
*
|
|
39
|
+
* @throws {RangeError} If `amount` is not greater than zero.
|
|
40
|
+
*/
|
|
32
41
|
increment(amount?: number): void;
|
|
33
42
|
applyDelta(delta: GCounterDelta): void;
|
|
34
43
|
private emit;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcounter.d.ts","sourceRoot":"","sources":["../../src/crdt/gcounter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gcounter.d.ts","sourceRoot":"","sources":["../../src/crdt/gcounter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IAExC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;gBAEpD,IAAI,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,WAAW,CAAC;QACvB,OAAO,CAAC,EAAE,aAAa,CAAC;KACzB;IAQD,2EAA2E;IAC3E,KAAK,IAAI,MAAM;IAIf,8EAA8E;IAC9E,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAI1C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAKvD;;;;OAIG;IACH,SAAS,CAAC,MAAM,GAAE,MAAU,GAAG,IAAI;IAkBnC,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAYtC,OAAO,CAAC,IAAI;CAMb"}
|
package/dist/crdt/gcounter.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
+
import { encode } from "../codec.js";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Low-level handle for a grow-only counter (GCounter) CRDT.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Obtained via `MeridianClient.gcounter()`. Prefer the `useGCounter` React hook
|
|
6
|
+
* for component-level usage; use this handle directly in non-React environments.
|
|
6
7
|
*/
|
|
7
|
-
import { encode } from "../codec.js";
|
|
8
8
|
export class GCounterHandle {
|
|
9
9
|
state;
|
|
10
10
|
clientId;
|
|
11
11
|
crdtId;
|
|
12
12
|
ns;
|
|
13
13
|
transport;
|
|
14
|
-
/** Emits on every state change. */
|
|
15
14
|
listeners = new Set();
|
|
16
15
|
constructor(opts) {
|
|
17
16
|
this.ns = opts.ns;
|
|
@@ -20,28 +19,34 @@ export class GCounterHandle {
|
|
|
20
19
|
this.transport = opts.transport;
|
|
21
20
|
this.state = opts.initial ?? { counts: {} };
|
|
22
21
|
}
|
|
23
|
-
|
|
22
|
+
/** Returns the current counter value (sum of all client contributions). */
|
|
24
23
|
value() {
|
|
25
24
|
return Object.values(this.state.counts).reduce((a, b) => a + b, 0);
|
|
26
25
|
}
|
|
26
|
+
/** Returns the raw per-client contribution map, keyed by client id string. */
|
|
27
27
|
counts() {
|
|
28
28
|
return this.state.counts;
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Registers a listener that is called whenever the counter value changes.
|
|
32
|
+
*
|
|
33
|
+
* @returns An unsubscribe function — call it to stop receiving updates.
|
|
34
|
+
*/
|
|
31
35
|
onChange(listener) {
|
|
32
36
|
this.listeners.add(listener);
|
|
33
37
|
return () => { this.listeners.delete(listener); };
|
|
34
38
|
}
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Increments the counter by `amount` (default `1`) and broadcasts the delta.
|
|
41
|
+
*
|
|
42
|
+
* @throws {RangeError} If `amount` is not greater than zero.
|
|
43
|
+
*/
|
|
37
44
|
increment(amount = 1) {
|
|
38
45
|
if (amount <= 0)
|
|
39
46
|
throw new RangeError("GCounter: increment amount must be > 0");
|
|
40
|
-
// Optimistic local update
|
|
41
47
|
const key = String(this.clientId);
|
|
42
48
|
this.state.counts[key] = (this.state.counts[key] ?? 0) + amount;
|
|
43
49
|
this.emit();
|
|
44
|
-
// Send Op to server
|
|
45
50
|
const op = encode({
|
|
46
51
|
GCounter: {
|
|
47
52
|
client_id: this.clientId,
|
|
@@ -52,7 +57,6 @@ export class GCounterHandle {
|
|
|
52
57
|
Op: { crdt_id: this.crdtId, op_bytes: op },
|
|
53
58
|
});
|
|
54
59
|
}
|
|
55
|
-
// ---- Delta application (called by MeridianClient on incoming Delta) ----
|
|
56
60
|
applyDelta(delta) {
|
|
57
61
|
let changed = false;
|
|
58
62
|
for (const [id, count] of Object.entries(delta.counters)) {
|
|
@@ -65,7 +69,6 @@ export class GCounterHandle {
|
|
|
65
69
|
if (changed)
|
|
66
70
|
this.emit();
|
|
67
71
|
}
|
|
68
|
-
// ---- Internal ----
|
|
69
72
|
emit() {
|
|
70
73
|
const v = this.value();
|
|
71
74
|
for (const listener of this.listeners) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcounter.js","sourceRoot":"","sources":["../../src/crdt/gcounter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gcounter.js","sourceRoot":"","sources":["../../src/crdt/gcounter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LWW Register handle — Last-Write-Wins single-value cell.
|
|
3
|
-
*
|
|
4
|
-
* The winning write is determined by HLC (highest wall_ms wins),
|
|
5
|
-
* tie-broken by author (higher client_id wins). The client sends its
|
|
6
|
-
* local wall clock as HLC wall_ms; the server enforces ±30s drift limit.
|
|
7
|
-
*
|
|
8
|
-
* Pass a `schema` to get runtime validation of incoming deltas:
|
|
9
|
-
* client.lwwregister("id", Schema.Struct({ x: Schema.Number }))
|
|
10
|
-
*/
|
|
11
1
|
import { Schema } from "effect";
|
|
12
2
|
import type { WsTransport } from "../transport/websocket.js";
|
|
13
3
|
import type { LwwDelta, LwwEntry } from "../sync/delta.js";
|
|
4
|
+
/**
|
|
5
|
+
* Low-level handle for a Last-Write-Wins register (LWW-Register) CRDT.
|
|
6
|
+
*
|
|
7
|
+
* Obtained via `MeridianClient.lwwregister()`. Prefer the `useLwwRegister` React
|
|
8
|
+
* hook for component-level usage; use this handle directly in non-React environments.
|
|
9
|
+
*/
|
|
14
10
|
export declare class LwwRegisterHandle<T> {
|
|
15
11
|
private entry;
|
|
16
12
|
private readonly crdtId;
|
|
@@ -26,13 +22,30 @@ export declare class LwwRegisterHandle<T> {
|
|
|
26
22
|
schema?: Schema.Schema<T>;
|
|
27
23
|
initial?: LwwEntry | null;
|
|
28
24
|
});
|
|
25
|
+
/** Returns the current register value, or `null` if no value has been written yet. */
|
|
29
26
|
value(): T | null;
|
|
30
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Returns metadata about the winning entry, or `null` if the register is empty.
|
|
29
|
+
*
|
|
30
|
+
* `updatedAtMs` is the wall-clock timestamp of the write; `author` is the
|
|
31
|
+
* numeric client id of the writer.
|
|
32
|
+
*/
|
|
31
33
|
meta(): {
|
|
32
34
|
updatedAtMs: number;
|
|
33
35
|
author: number;
|
|
34
36
|
} | null;
|
|
37
|
+
/**
|
|
38
|
+
* Registers a listener that is called whenever the register value changes.
|
|
39
|
+
*
|
|
40
|
+
* @returns An unsubscribe function — call it to stop receiving updates.
|
|
41
|
+
*/
|
|
35
42
|
onChange(listener: (value: T | null) => void): () => void;
|
|
43
|
+
/**
|
|
44
|
+
* Writes `value` to the register and broadcasts the operation.
|
|
45
|
+
*
|
|
46
|
+
* The write is stamped with the current wall-clock time. If a concurrent
|
|
47
|
+
* write from another client has a later timestamp it will win over this one.
|
|
48
|
+
*/
|
|
36
49
|
set(value: T): void;
|
|
37
50
|
applyDelta(delta: LwwDelta): void;
|
|
38
51
|
private decode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lwwregister.d.ts","sourceRoot":"","sources":["../../src/crdt/lwwregister.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"lwwregister.d.ts","sourceRoot":"","sources":["../../src/crdt/lwwregister.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE3D;;;;;GAKG;AACH,qBAAa,iBAAiB,CAAC,CAAC;IAC9B,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;gBAEtD,IAAI,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,WAAW,CAAC;QACvB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1B,OAAO,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;KAC3B;IAQD,sFAAsF;IACtF,KAAK,IAAI,CAAC,GAAG,IAAI;IAKjB;;;;;OAKG;IACH,IAAI,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAKtD;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI;IAKzD;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAkBnB,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAQjC,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,IAAI;CAIb"}
|
package/dist/crdt/lwwregister.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { encode } from "../codec.js";
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* The winning write is determined by HLC (highest wall_ms wins),
|
|
5
|
-
* tie-broken by author (higher client_id wins). The client sends its
|
|
6
|
-
* local wall clock as HLC wall_ms; the server enforces ±30s drift limit.
|
|
4
|
+
* Low-level handle for a Last-Write-Wins register (LWW-Register) CRDT.
|
|
7
5
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Obtained via `MeridianClient.lwwregister()`. Prefer the `useLwwRegister` React
|
|
7
|
+
* hook for component-level usage; use this handle directly in non-React environments.
|
|
10
8
|
*/
|
|
11
|
-
import { Schema } from "effect";
|
|
12
|
-
import { encode, wallMsToBigInt } from "../codec.js";
|
|
13
9
|
export class LwwRegisterHandle {
|
|
14
10
|
entry = null;
|
|
15
11
|
crdtId;
|
|
@@ -24,23 +20,38 @@ export class LwwRegisterHandle {
|
|
|
24
20
|
this.schema = opts.schema ?? null;
|
|
25
21
|
this.entry = opts.initial ?? null;
|
|
26
22
|
}
|
|
27
|
-
|
|
23
|
+
/** Returns the current register value, or `null` if no value has been written yet. */
|
|
28
24
|
value() {
|
|
29
25
|
if (this.entry === null)
|
|
30
26
|
return null;
|
|
31
27
|
return this.decode(this.entry.value);
|
|
32
28
|
}
|
|
33
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Returns metadata about the winning entry, or `null` if the register is empty.
|
|
31
|
+
*
|
|
32
|
+
* `updatedAtMs` is the wall-clock timestamp of the write; `author` is the
|
|
33
|
+
* numeric client id of the writer.
|
|
34
|
+
*/
|
|
34
35
|
meta() {
|
|
35
36
|
if (this.entry === null)
|
|
36
37
|
return null;
|
|
37
38
|
return { updatedAtMs: Number(this.entry.hlc.wall_ms), author: Number(this.entry.author) };
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Registers a listener that is called whenever the register value changes.
|
|
42
|
+
*
|
|
43
|
+
* @returns An unsubscribe function — call it to stop receiving updates.
|
|
44
|
+
*/
|
|
39
45
|
onChange(listener) {
|
|
40
46
|
this.listeners.add(listener);
|
|
41
47
|
return () => { this.listeners.delete(listener); };
|
|
42
48
|
}
|
|
43
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Writes `value` to the register and broadcasts the operation.
|
|
51
|
+
*
|
|
52
|
+
* The write is stamped with the current wall-clock time. If a concurrent
|
|
53
|
+
* write from another client has a later timestamp it will win over this one.
|
|
54
|
+
*/
|
|
44
55
|
set(value) {
|
|
45
56
|
const wallMs = Date.now();
|
|
46
57
|
const hlc = { wall_ms: wallMs, logical: 0, node_id: this.clientId };
|
|
@@ -49,17 +60,13 @@ export class LwwRegisterHandle {
|
|
|
49
60
|
this.entry = newEntry;
|
|
50
61
|
this.emit();
|
|
51
62
|
}
|
|
52
|
-
// wall_ms must be BigInt — msgpackr encodes large JS numbers as float64,
|
|
53
|
-
// but Rust expects u64 integer encoding
|
|
54
|
-
const wireHlc = { wall_ms: wallMsToBigInt(wallMs), logical: 0, node_id: this.clientId };
|
|
55
63
|
this.transport.send({
|
|
56
64
|
Op: {
|
|
57
65
|
crdt_id: this.crdtId,
|
|
58
|
-
op_bytes: encode({ LwwRegister: { value, hlc:
|
|
66
|
+
op_bytes: encode({ LwwRegister: { value, hlc: { wall_ms: wallMs, logical: 0, node_id: this.clientId }, author: this.clientId } }),
|
|
59
67
|
},
|
|
60
68
|
});
|
|
61
69
|
}
|
|
62
|
-
// ---- Delta application ----
|
|
63
70
|
applyDelta(delta) {
|
|
64
71
|
if (delta.entry === null)
|
|
65
72
|
return;
|
|
@@ -68,12 +75,11 @@ export class LwwRegisterHandle {
|
|
|
68
75
|
this.emit();
|
|
69
76
|
}
|
|
70
77
|
}
|
|
71
|
-
// ---- Internal ----
|
|
72
78
|
decode(raw) {
|
|
73
79
|
if (this.schema !== null) {
|
|
74
80
|
return Schema.decodeUnknownSync(this.schema)(raw);
|
|
75
81
|
}
|
|
76
|
-
// No schema provided — T defaults to unknown, cast is the caller's responsibility
|
|
82
|
+
// HACK: No schema provided — T defaults to unknown, cast is the caller's responsibility.
|
|
77
83
|
return raw;
|
|
78
84
|
}
|
|
79
85
|
entryWins(candidate, existing) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lwwregister.js","sourceRoot":"","sources":["../../src/crdt/lwwregister.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"lwwregister.js","sourceRoot":"","sources":["../../src/crdt/lwwregister.ts"],"names":[],"mappings":"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"}
|
package/dist/crdt/orset.d.ts
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ORSet handle — add-wins observed-remove set.
|
|
3
|
-
*
|
|
4
|
-
* Each element has a set of add-tags (UUIDs). Remove only removes tags
|
|
5
|
-
* known at remove time — a concurrent add with a new tag survives.
|
|
6
|
-
*
|
|
7
|
-
* Elements are serialized as JSON for the wire (serde_json::Value).
|
|
8
|
-
*
|
|
9
|
-
* Pass a `schema` to get runtime validation of elements deserialized from deltas:
|
|
10
|
-
* client.orset("id", Schema.Struct({ id: Schema.String }))
|
|
11
|
-
*/
|
|
12
1
|
import { Schema } from "effect";
|
|
13
2
|
import type { WsTransport } from "../transport/websocket.js";
|
|
14
3
|
import type { ORSetDelta } from "../sync/delta.js";
|
|
4
|
+
/**
|
|
5
|
+
* Low-level handle for an Observed-Remove Set (OR-Set) CRDT.
|
|
6
|
+
*
|
|
7
|
+
* Obtained via `MeridianClient.orset()`. Prefer the `useORSet` React hook for
|
|
8
|
+
* component-level usage; use this handle directly in non-React environments.
|
|
9
|
+
*/
|
|
15
10
|
export declare class ORSetHandle<T> {
|
|
16
|
-
/** element (JSON-stringified) → Set of live add-tags */
|
|
17
11
|
private readonly tags;
|
|
18
12
|
private readonly crdtId;
|
|
19
13
|
private readonly clientId;
|
|
@@ -27,11 +21,29 @@ export declare class ORSetHandle<T> {
|
|
|
27
21
|
transport: WsTransport;
|
|
28
22
|
schema?: Schema.Schema<T>;
|
|
29
23
|
});
|
|
30
|
-
/** Returns
|
|
24
|
+
/** Returns the current set elements as an array, decoded via the optional schema. */
|
|
31
25
|
elements(): T[];
|
|
26
|
+
/** Returns `true` if the set currently contains `element`. */
|
|
32
27
|
has(element: T): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Registers a listener that is called whenever the set contents change.
|
|
30
|
+
*
|
|
31
|
+
* @returns An unsubscribe function — call it to stop receiving updates.
|
|
32
|
+
*/
|
|
33
33
|
onChange(listener: (elements: T[]) => void): () => void;
|
|
34
|
+
/**
|
|
35
|
+
* Adds `element` to the set and broadcasts the operation.
|
|
36
|
+
*
|
|
37
|
+
* Each call generates a unique tag so concurrent adds of the same value
|
|
38
|
+
* are treated as distinct entries.
|
|
39
|
+
*/
|
|
34
40
|
add(element: T): void;
|
|
41
|
+
/**
|
|
42
|
+
* Removes `element` from the set and broadcasts the operation.
|
|
43
|
+
*
|
|
44
|
+
* Only the tags observed locally at the time of this call are removed;
|
|
45
|
+
* concurrently added copies on other clients are left intact.
|
|
46
|
+
*/
|
|
35
47
|
remove(element: T): void;
|
|
36
48
|
applyDelta(delta: ORSetDelta): void;
|
|
37
49
|
private decode;
|
package/dist/crdt/orset.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orset.d.ts","sourceRoot":"","sources":["../../src/crdt/orset.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"orset.d.ts","sourceRoot":"","sources":["../../src/crdt/orset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD;;;;;GAKG;AACH,qBAAa,WAAW,CAAC,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkC;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;gBAEpD,IAAI,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,WAAW,CAAC;QACvB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;KAC3B;IAOD,qFAAqF;IACrF,QAAQ,IAAI,CAAC,EAAE;IAMf,8DAA8D;IAC9D,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO;IAKxB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI;IAKvD;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI;IAiBrB;;;;;OAKG;IACH,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI;IAgBxB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAyBnC,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,IAAI;CAIb"}
|