@doxsync/core 0.1.0-alpha.1 → 0.1.0-alpha.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/README.md CHANGED
@@ -6,18 +6,19 @@ state. JavaScript provides loading and a small synchronous API.
6
6
 
7
7
  ## Build from this repository
8
8
 
9
- Install Rust, Node.js 22+, and `wasm-pack`, then run:
9
+ Install Rust, Node.js 22.12+ (or 24+), and `wasm-pack`, then run:
10
10
 
11
11
  ```sh
12
12
  rustup target add wasm32-unknown-unknown
13
13
  cd doxsync-js
14
+ npm ci
14
15
  npm run build
15
16
  node examples/node.mjs
16
17
  ```
17
18
 
18
19
  `npm run build` runs `wasm-pack` in release mode with the locked Cargo
19
- dependencies and the `wasm` feature, then copies the JS wrapper and declarations
20
- into `dist/`. There are no npm runtime dependencies. To create a local npm
20
+ dependencies and the `wasm` feature, then builds the TypeScript wrapper with Vite in library mode.
21
+ `vite-plugin-dts` generates type declarations in `dist/`. There are no npm runtime dependencies. To create a local npm
21
22
  tarball, run `npm pack`; it builds first and includes the WASM artifact.
22
23
  Installing that tarball does not require Rust or wasm-pack.
23
24
 
@@ -37,10 +38,10 @@ also generate the JS glue required to load it.
37
38
  After installing the package from the local tarball:
38
39
 
39
40
  ```js
40
- import { init, Producer, Consumer } from "@doxsync/core";
41
+ import { init, Producer, Consumer, supportedProtocols } from "@doxsync/core";
41
42
 
42
43
  await init();
43
- const producer = new Producer({ count: 1n, ratio: 0.5 }, [1]);
44
+ const producer = new Producer({ count: 1n, ratio: 0.5 }, supportedProtocols());
44
45
  const consumer = new Consumer();
45
46
 
46
47
  try {
@@ -77,7 +78,8 @@ provide garbage-collection cleanup, but its timing is not deterministic.
77
78
 
78
79
  ## WASM initialization
79
80
 
80
- Call and await `init()` before creating either class. Initialization is the
81
+ Call and await `init()` before creating either class or calling
82
+ `supportedProtocols()`. Initialization is the
81
83
  only asynchronous operation. Concurrent calls share the first call's promise
82
84
  and source; successful initialization is reused, and failed initialization
83
85
  can be retried.
@@ -151,12 +153,24 @@ try {
151
153
  }
152
154
  ```
153
155
 
154
- The JS package exposes only `init`, `Producer`, and `Consumer` at runtime.
156
+ The JS package exposes `init`, `supportedProtocols`, `Producer`, and `Consumer`
157
+ at runtime.
155
158
  Rust's lower-level Document, Value, and Message APIs remain internal to this
156
159
  binding, and the wire protocol is shared with native Rust.
157
160
 
158
161
  ### Protocol negotiation
159
162
 
163
+ Call `supportedProtocols()` after `await init()` to get all protocol versions
164
+ supported by the local build, currently `[1]`. Each call returns an independent
165
+ ordinary `number[]`; modifying it does not affect subsequent calls or the engine.
166
+ No producer or consumer instance is needed. Rust callers can use
167
+ `doxsync::supported_protocols() -> &'static [u32]` without initialization.
168
+
169
+ For peers running in separate processes, have the consumer advertise its list
170
+ and pass that received list to the producer. The examples above use the local
171
+ list because both peers share the same build. This query reports supported
172
+ versions, not the version negotiated for an individual stream.
173
+
160
174
  The second `Producer` constructor argument is required: pass an array of protocol
161
175
  versions supported by the consumer, such as `[1]`. Versions must be integers in
162
176
  `[0, 2^32 - 1]`. The producer selects the highest version supported by both peers;
package/dist/index.d.ts CHANGED
@@ -1,64 +1,10 @@
1
- /** Numbers are floats; bigint values are integers in [-2^64, 2^64 - 1]. */
2
- export type SyncValue =
3
- | null
4
- | boolean
5
- | string
6
- | number
7
- | bigint
8
- | Uint8Array
9
- | SyncValue[]
10
- | { [key: string]: SyncValue };
11
-
12
- export type InitSource =
13
- | string
14
- | URL
15
- | ArrayBuffer
16
- | Uint8Array
17
- | WebAssembly.Module;
18
-
19
- /** Rust errors thrown by synchronization and value conversion. */
20
- export interface DoxsyncError extends Error {
21
- kind: "Internal" | "InvalidData" | "UnexpectedType";
22
- }
23
-
1
+ import { InitSource } from './types.js';
2
+ export type { SyncValue, InitSource, DoxsyncError } from './types.js';
3
+ export { Producer, Consumer, supportedProtocols } from './runtime.js';
24
4
  /**
25
- * Initialize once before constructing Producer or Consumer.
5
+ * Initialize once before constructing Producer or Consumer or calling supportedProtocols.
26
6
  * Defaults to the packaged WASM. Concurrent calls share the first call's source
27
7
  * and promise; failures can be retried. Later successful calls are no-ops.
28
8
  * Strings are URLs, not filesystem paths; Node also accepts file: URLs.
29
9
  */
30
- export function init(source?: InitSource): Promise<void>;
31
-
32
- export class Producer {
33
- #private;
34
- /**
35
- * Copies the value into WASM and selects the highest common protocol version.
36
- * protocols lists the consumer's supported versions as u32 integers.
37
- * Currently only 1 is supported. Missing, invalid, empty, or incompatible
38
- * lists throw InvalidData errors. Order and duplicates do not matter.
39
- */
40
- constructor(value: SyncValue, protocols: readonly number[]);
41
- /** Replaces the document after validating and copying the entire value. */
42
- replace(value: SyncValue): void;
43
- /**
44
- * Returns a snapshot with protocol metadata on the first call, then changes.
45
- * Returns undefined if unchanged. Retain each message until delivered in order.
46
- */
47
- produceDiff(): Uint8Array | undefined;
48
- /** Release WASM resources. Safe to call repeatedly. */
49
- free(): void;
50
- }
51
-
52
- export class Consumer {
53
- #private;
54
- constructor();
55
- /**
56
- * Applies one complete message. The first message must declare protocol 1.
57
- * Failures leave the document, pools, and protocol state intact.
58
- */
59
- consumeDiff(bytes: Uint8Array): void;
60
- /** Returns an independent copy, or undefined before the first snapshot. */
61
- document(): SyncValue | undefined;
62
- /** Release WASM resources. Safe to call repeatedly. */
63
- free(): void;
64
- }
10
+ export declare function init(source?: InitSource): Promise<void>;
package/dist/index.js CHANGED
@@ -1,9 +1,11 @@
1
- import { initialize } from "./runtime.js";
2
-
3
- export { Producer, Consumer } from "./runtime.js";
4
-
5
- export function init(source) {
6
- return initialize(() => source === undefined
7
- ? new URL("./wasm/doxsync_bg.wasm", import.meta.url)
8
- : source);
1
+ import { i as e, n as t, r as n, t as r } from "./runtime-CJqt5vND.js";
2
+ //#region src/index.ts
3
+ function i(e) {
4
+ return n(() => e === void 0 ? new URL(
5
+ /* @vite-ignore */
6
+ "./wasm/doxsync_bg.wasm",
7
+ import.meta.url
8
+ ) : e);
9
9
  }
10
+ //#endregion
11
+ export { r as Consumer, t as Producer, i as init, e as supportedProtocols };
package/dist/node.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { InitSource } from './types.js';
2
+ export type { SyncValue, InitSource, DoxsyncError } from './types.js';
3
+ export { Producer, Consumer, supportedProtocols } from './runtime.js';
4
+ /**
5
+ * Initialize once before constructing Producer or Consumer or calling supportedProtocols.
6
+ * Defaults to the packaged WASM. Concurrent calls share the first call's source
7
+ * and promise; failures can be retried. Later successful calls are no-ops.
8
+ * Strings are URLs, not filesystem paths; Node also accepts file: URLs.
9
+ */
10
+ export declare function init(source?: InitSource): Promise<void>;
package/dist/node.js CHANGED
@@ -1,19 +1,12 @@
1
- import { readFile } from "node:fs/promises";
2
- import { initialize } from "./runtime.js";
3
-
4
- export { Producer, Consumer } from "./runtime.js";
5
-
6
- export function init(source) {
7
- return initialize(() => {
8
- if (source === undefined) {
9
- return readFile(new URL("./wasm/doxsync_bg.wasm", import.meta.url));
10
- }
11
- if (source instanceof URL && source.protocol === "file:") {
12
- return readFile(source);
13
- }
14
- if (typeof source === "string" && source.startsWith("file:")) {
15
- return readFile(new URL(source));
16
- }
17
- return source;
18
- });
1
+ import { i as e, n as t, r as n, t as r } from "./runtime-CJqt5vND.js";
2
+ import { readFile as i } from "node:fs/promises";
3
+ //#region src/node.ts
4
+ function a(e) {
5
+ return n(() => e === void 0 ? i(new URL(
6
+ /* @vite-ignore */
7
+ "./wasm/doxsync_bg.wasm",
8
+ import.meta.url
9
+ )) : e instanceof URL && e.protocol === "file:" ? i(e) : typeof e == "string" && e.startsWith("file:") ? i(new URL(e)) : e);
19
10
  }
11
+ //#endregion
12
+ export { r as Consumer, t as Producer, a as init, e as supportedProtocols };
@@ -0,0 +1,55 @@
1
+ import e, { Consumer as t, Producer as n, supportedProtocols as r } from "./wasm/doxsync.js";
2
+ //#region src/runtime.ts
3
+ var i, a = !1;
4
+ function o(t) {
5
+ return i ||= Promise.resolve().then(t).then((t) => e({ module_or_path: t })).then(() => {
6
+ a = !0;
7
+ }).catch((e) => {
8
+ throw i = void 0, e;
9
+ }), i;
10
+ }
11
+ function s() {
12
+ if (!a) throw Error("Call and await init() before using the doxsync API");
13
+ }
14
+ function c() {
15
+ return s(), r();
16
+ }
17
+ var l = class {
18
+ #e;
19
+ constructor(e, t) {
20
+ s(), this.#e = new n(e, t);
21
+ }
22
+ #t() {
23
+ if (!this.#e) throw Error("Producer has been freed");
24
+ return this.#e;
25
+ }
26
+ replace(e) {
27
+ this.#t().replace(e);
28
+ }
29
+ produceDiff() {
30
+ return this.#t().produceDiff();
31
+ }
32
+ free() {
33
+ this.#e &&= (this.#e.free(), void 0);
34
+ }
35
+ }, u = class {
36
+ #e;
37
+ constructor() {
38
+ s(), this.#e = new t();
39
+ }
40
+ #t() {
41
+ if (!this.#e) throw Error("Consumer has been freed");
42
+ return this.#e;
43
+ }
44
+ consumeDiff(e) {
45
+ this.#t().consumeDiff(e);
46
+ }
47
+ document() {
48
+ return this.#t().document();
49
+ }
50
+ free() {
51
+ this.#e &&= (this.#e.free(), void 0);
52
+ }
53
+ };
54
+ //#endregion
55
+ export { c as i, l as n, o as r, u as t };
@@ -0,0 +1,40 @@
1
+ import { InitSource, SyncValue } from './types.js';
2
+ export declare function initialize(loadSource: () => InitSource | Promise<InitSource>): Promise<void>;
3
+ /**
4
+ * Returns all protocol versions supported by this build, currently [1].
5
+ * Requires await init(). Each call returns an independent ordinary array.
6
+ * Consumers can advertise this list to producers for protocol negotiation.
7
+ */
8
+ export declare function supportedProtocols(): number[];
9
+ export declare class Producer {
10
+ #private;
11
+ /**
12
+ * Copies the value into WASM and selects the highest common protocol version.
13
+ * protocols lists the consumer's supported versions as u32 integers.
14
+ * Currently only 1 is supported. Missing, invalid, empty, or incompatible
15
+ * lists throw InvalidData errors. Order and duplicates do not matter.
16
+ */
17
+ constructor(value: SyncValue, protocols: readonly number[]);
18
+ /** Replaces the document after validating and copying the entire value. */
19
+ replace(value: SyncValue): void;
20
+ /**
21
+ * Returns a snapshot with protocol metadata on the first call, then changes.
22
+ * Returns undefined if unchanged. Retain each message until delivered in order.
23
+ */
24
+ produceDiff(): Uint8Array | undefined;
25
+ /** Release WASM resources. Safe to call repeatedly. */
26
+ free(): void;
27
+ }
28
+ export declare class Consumer {
29
+ #private;
30
+ constructor();
31
+ /**
32
+ * Applies one complete message. The first message must declare protocol 1.
33
+ * Failures leave the document, pools, and protocol state intact.
34
+ */
35
+ consumeDiff(bytes: Uint8Array): void;
36
+ /** Returns an independent copy, or undefined before the first snapshot. */
37
+ document(): SyncValue | undefined;
38
+ /** Release WASM resources. Safe to call repeatedly. */
39
+ free(): void;
40
+ }
@@ -0,0 +1,9 @@
1
+ /** Numbers are floats; bigint values are integers in [-2^64, 2^64 - 1]. */
2
+ export type SyncValue = null | boolean | string | number | bigint | Uint8Array | SyncValue[] | {
3
+ [key: string]: SyncValue;
4
+ };
5
+ export type InitSource = string | URL | ArrayBuffer | Uint8Array | WebAssembly.Module;
6
+ /** Rust errors thrown by synchronization and value conversion. */
7
+ export interface DoxsyncError extends Error {
8
+ kind: "Internal" | "InvalidData" | "UnexpectedType";
9
+ }
@@ -17,6 +17,8 @@ export class Producer {
17
17
  replace(value: any): void;
18
18
  }
19
19
 
20
+ export function supportedProtocols(): Array<any>;
21
+
20
22
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
21
23
 
22
24
  export interface InitOutput {
@@ -29,6 +31,7 @@ export interface InitOutput {
29
31
  readonly producer_new: (a: any, b: any) => [number, number, number];
30
32
  readonly producer_produceDiff: (a: number) => [number, number, number];
31
33
  readonly producer_replace: (a: number, b: any) => [number, number];
34
+ readonly supportedProtocols: () => any;
32
35
  readonly __wbindgen_malloc: (a: number, b: number) => number;
33
36
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
34
37
  readonly __wbindgen_exn_store: (a: number) => void;
@@ -84,6 +84,14 @@ export class Producer {
84
84
  }
85
85
  }
86
86
  if (Symbol.dispose) Producer.prototype[Symbol.dispose] = Producer.prototype.free;
87
+
88
+ /**
89
+ * @returns {Array<any>}
90
+ */
91
+ export function supportedProtocols() {
92
+ const ret = wasm.supportedProtocols();
93
+ return ret;
94
+ }
87
95
  function __wbg_get_imports() {
88
96
  const import0 = {
89
97
  __proto__: null,
Binary file
@@ -9,6 +9,7 @@ export const consumer_new: () => number;
9
9
  export const producer_new: (a: any, b: any) => [number, number, number];
10
10
  export const producer_produceDiff: (a: number) => [number, number, number];
11
11
  export const producer_replace: (a: number, b: any) => [number, number];
12
+ export const supportedProtocols: () => any;
12
13
  export const __wbindgen_malloc: (a: number, b: number) => number;
13
14
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
14
15
  export const __wbindgen_exn_store: (a: number) => void;
@@ -293,7 +293,7 @@ function reset() {
293
293
  dispose();
294
294
  const document = initialDocument();
295
295
  state = {
296
- document, producer: new bindings.Producer(document, [1]), consumer: new bindings.Consumer(),
296
+ document, producer: new bindings.Producer(document, bindings.supportedProtocols()), consumer: new bindings.Consumer(),
297
297
  producerText: format(document), consumerText: "", queue: [], history: [],
298
298
  selected: undefined, sent: 0, totalBytes: 0, unchanged: 0,
299
299
  running: true, failed: false,
package/examples/node.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { init, Producer, Consumer } from "@doxsync/core";
1
+ import { init, Producer, Consumer, supportedProtocols } from "@doxsync/core";
2
2
 
3
3
  await init();
4
- const producer = new Producer({ count: 1n, data: new Uint8Array([1, 2, 3]) }, [1]);
4
+ const producer = new Producer({ count: 1n, data: new Uint8Array([1, 2, 3]) }, supportedProtocols());
5
5
  const consumer = new Consumer();
6
6
 
7
7
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doxsync/core",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "JavaScript bindings for the doxsync Rust synchronization engine",
5
5
  "type": "module",
6
6
  "engines": {
@@ -23,7 +23,7 @@
23
23
  "LICENSE-APACHE"
24
24
  ],
25
25
  "scripts": {
26
- "build": "node scripts/build.mjs",
26
+ "build": "node scripts/build-wasm.mjs && vite build",
27
27
  "prepack": "npm run build"
28
28
  },
29
29
  "license": "MIT OR Apache-2.0",
@@ -31,5 +31,11 @@
31
31
  "type": "git",
32
32
  "url": "git+https://github.com/PeterlitsZo/doxsync.git",
33
33
  "directory": "doxsync-js"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22.20.4",
37
+ "typescript": "^6.0.3",
38
+ "vite": "^8.3.0",
39
+ "vite-plugin-dts": "^5.1.1"
34
40
  }
35
41
  }
package/dist/runtime.js DELETED
@@ -1,85 +0,0 @@
1
- import initWasm, {
2
- Producer as WasmProducer,
3
- Consumer as WasmConsumer,
4
- } from "./wasm/doxsync.js";
5
-
6
- let initialization;
7
- let initialized = false;
8
-
9
- export function initialize(loadSource) {
10
- if (!initialization) {
11
- initialization = Promise.resolve()
12
- .then(loadSource)
13
- .then((source) => initWasm({ module_or_path: source }))
14
- .then(() => { initialized = true; })
15
- .catch((error) => {
16
- initialization = undefined;
17
- throw error;
18
- });
19
- }
20
- return initialization;
21
- }
22
-
23
- function requireInitialized() {
24
- if (!initialized) {
25
- throw new Error("Call and await init() before creating a doxsync object");
26
- }
27
- }
28
-
29
- export class Producer {
30
- #handle;
31
-
32
- constructor(value, protocols) {
33
- requireInitialized();
34
- this.#handle = new WasmProducer(value, protocols);
35
- }
36
-
37
- #live() {
38
- if (!this.#handle) throw new Error("Producer has been freed");
39
- return this.#handle;
40
- }
41
-
42
- replace(value) {
43
- this.#live().replace(value);
44
- }
45
-
46
- produceDiff() {
47
- return this.#live().produceDiff();
48
- }
49
-
50
- free() {
51
- if (this.#handle) {
52
- this.#handle.free();
53
- this.#handle = undefined;
54
- }
55
- }
56
- }
57
-
58
- export class Consumer {
59
- #handle;
60
-
61
- constructor() {
62
- requireInitialized();
63
- this.#handle = new WasmConsumer();
64
- }
65
-
66
- #live() {
67
- if (!this.#handle) throw new Error("Consumer has been freed");
68
- return this.#handle;
69
- }
70
-
71
- consumeDiff(bytes) {
72
- this.#live().consumeDiff(bytes);
73
- }
74
-
75
- document() {
76
- return this.#live().document();
77
- }
78
-
79
- free() {
80
- if (this.#handle) {
81
- this.#handle.free();
82
- this.#handle = undefined;
83
- }
84
- }
85
- }