@doxsync/core 0.1.0-alpha.2 → 0.1.0-alpha.3
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 +110 -8
- package/dist/decimal.d.ts +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -3
- package/dist/node.d.ts +1 -0
- package/dist/node.js +7 -6
- package/dist/runtime-BePF61JJ.js +72 -0
- package/dist/runtime.d.ts +8 -4
- package/dist/types.d.ts +5 -2
- package/dist/wasm/doxsync.d.ts +4 -4
- package/dist/wasm/doxsync.js +10 -4
- package/dist/wasm/doxsync_bg.wasm +0 -0
- package/dist/wasm/doxsync_bg.wasm.d.ts +2 -2
- package/examples/browser.html +3 -0
- package/examples/browser.js +10 -1
- package/examples/node.mjs +4 -3
- package/package.json +4 -1
- package/dist/runtime-CJqt5vND.js +0 -55
package/README.md
CHANGED
|
@@ -18,7 +18,8 @@ node examples/node.mjs
|
|
|
18
18
|
|
|
19
19
|
`npm run build` runs `wasm-pack` in release mode with the locked Cargo
|
|
20
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/`.
|
|
21
|
+
`vite-plugin-dts` generates type declarations in `dist/`. The runtime dependency
|
|
22
|
+
`decimal.js` supplies exact JS decimal values. To create a local npm
|
|
22
23
|
tarball, run `npm pack`; it builds first and includes the WASM artifact.
|
|
23
24
|
Installing that tarball does not require Rust or wasm-pack.
|
|
24
25
|
|
|
@@ -86,6 +87,8 @@ can be retried.
|
|
|
86
87
|
|
|
87
88
|
- Browsers fetch the WASM next to the generated JS. Serve `dist/` over HTTP;
|
|
88
89
|
retain the `dist/wasm/` directory and serve `.wasm` as `application/wasm`.
|
|
90
|
+
When loading without a bundler, provide an import map for `decimal.js`;
|
|
91
|
+
`examples/browser.html` maps it to the local npm installation.
|
|
89
92
|
- Node.js uses the package's conditional export to read the same packaged
|
|
90
93
|
WASM from disk. Browser entry points contain no Node.js imports.
|
|
91
94
|
- `init(source)` also accepts a URL string, `URL`, `ArrayBuffer`, `Uint8Array`,
|
|
@@ -119,6 +122,7 @@ CommonJS entry point or polyfills are supplied.
|
|
|
119
122
|
| `null`, `boolean`, `string` | Null, Bool, UTF-8 text |
|
|
120
123
|
| `number` | Float, including `-0`, NaN, and infinities |
|
|
121
124
|
| `bigint` | Int in `[-2^64, 2^64 - 1]` |
|
|
125
|
+
| `Decimal` from `decimal.js` | Exact Decimal with a 96-bit coefficient and scale 0–28 |
|
|
122
126
|
| `Uint8Array` (including Node Buffer) | Binary string, copied |
|
|
123
127
|
| Dense arrays | Arrays, copied recursively |
|
|
124
128
|
| Plain or null-prototype objects | Maps with string keys |
|
|
@@ -137,8 +141,8 @@ Unsupported values are rejected: `undefined`, functions, symbols (including
|
|
|
137
141
|
enumerable symbol map keys), sparse arrays, cyclic references, accessor
|
|
138
142
|
properties used as document values, out-of-range integers, unpaired UTF-16
|
|
139
143
|
surrogates in strings or keys, and objects such as Date, Map, Set, or class
|
|
140
|
-
instances. Use values from the same JS realm as the library;
|
|
141
|
-
and proxies are not supported as a document model.
|
|
144
|
+
instances other than Decimal. Use values from the same JS realm as the library;
|
|
145
|
+
custom prototypes and proxies are not supported as a document model.
|
|
142
146
|
|
|
143
147
|
Rust and conversion errors are JS `Error` instances with `kind` equal to
|
|
144
148
|
`Internal`, `InvalidData`, or `UnexpectedType`. Initialization, lifecycle,
|
|
@@ -153,15 +157,58 @@ try {
|
|
|
153
157
|
}
|
|
154
158
|
```
|
|
155
159
|
|
|
156
|
-
The JS package exposes `init`, `supportedProtocols`, `Producer`, and `
|
|
160
|
+
The JS package exposes `init`, `supportedProtocols`, `Producer`, `Consumer`, and `Decimal`
|
|
157
161
|
at runtime.
|
|
158
162
|
Rust's lower-level Document, Value, and Message APIs remain internal to this
|
|
159
163
|
binding, and the wire protocol is shared with native Rust.
|
|
160
164
|
|
|
165
|
+
### Decimal values
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
import { init, Producer, Consumer, Decimal, supportedProtocols } from "@doxsync/core";
|
|
169
|
+
|
|
170
|
+
await init();
|
|
171
|
+
const producer = new Producer({ price: new Decimal("12.34") }, supportedProtocols());
|
|
172
|
+
const consumer = new Consumer();
|
|
173
|
+
try {
|
|
174
|
+
consumer.consumeDiff(producer.produceDiff());
|
|
175
|
+
console.log(consumer.document().price.toFixed(2)); // "12.34"
|
|
176
|
+
} finally {
|
|
177
|
+
producer.free();
|
|
178
|
+
consumer.free();
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Pass decimal strings to the Decimal constructor to avoid introducing binary
|
|
183
|
+
floating-point rounding before synchronization. Instances imported directly from
|
|
184
|
+
`decimal.js` or created by `Decimal.clone()` are also accepted. Values must be
|
|
185
|
+
finite and exactly representable using a sign and a 96-bit unsigned coefficient
|
|
186
|
+
divided by `10^scale`, with scale from 0 through 28. Invalid values throw `InvalidData`;
|
|
187
|
+
conversion never silently rounds. Input `number` values remain Float values.
|
|
188
|
+
|
|
189
|
+
Protocol 2 returns decimal.js instances, constructed independently of the user's
|
|
190
|
+
global decimal.js configuration with 29 significant digits of arithmetic
|
|
191
|
+
precision and an exponent range of -100 through 100. Their initial values are
|
|
192
|
+
exact. Arithmetic follows decimal.js precision rules; synchronization does not
|
|
193
|
+
implement arithmetic or recover precision already lost by the caller.
|
|
194
|
+
|
|
195
|
+
Rust preserves scale: `Decimal::from_str_exact("1.20")` remains distinct from
|
|
196
|
+
`Decimal::from_str_exact("1.2")` when wrapped with `Value::decimal`. It also
|
|
197
|
+
provides `as_decimal`, `as_decimal_and_modify`, and `value!` conversions for owned
|
|
198
|
+
or borrowed Decimal values. Negative zero is normalized without changing scale.
|
|
199
|
+
JS decimal.js instances do not retain trailing zeros: a Rust value of `1.20`
|
|
200
|
+
read through `document()` and sent back through JS may become `1.2`.
|
|
201
|
+
|
|
202
|
+
With a protocol 1 producer (`new Producer(value, [1])`), decimals arrive as
|
|
203
|
+
ordinary strings. Rust preserves the source scale in those strings; JS can only
|
|
204
|
+
send the scale still present in its decimal.js value. Strings are never
|
|
205
|
+
automatically parsed as decimals. Switching between a Decimal and its identical
|
|
206
|
+
fallback string produces no change under protocol 1.
|
|
207
|
+
|
|
161
208
|
### Protocol negotiation
|
|
162
209
|
|
|
163
210
|
Call `supportedProtocols()` after `await init()` to get all protocol versions
|
|
164
|
-
supported by the local build, currently `[1]`. Each call returns an independent
|
|
211
|
+
supported by the local build, currently `[1, 2]`. Each call returns an independent
|
|
165
212
|
ordinary `number[]`; modifying it does not affect subsequent calls or the engine.
|
|
166
213
|
No producer or consumer instance is needed. Rust callers can use
|
|
167
214
|
`doxsync::supported_protocols() -> &'static [u32]` without initialization.
|
|
@@ -174,20 +221,47 @@ versions, not the version negotiated for an individual stream.
|
|
|
174
221
|
The second `Producer` constructor argument is required: pass an array of protocol
|
|
175
222
|
versions supported by the consumer, such as `[1]`. Versions must be integers in
|
|
176
223
|
`[0, 2^32 - 1]`. The producer selects the highest version supported by both peers;
|
|
177
|
-
order and duplicates do not matter.
|
|
224
|
+
order and duplicates do not matter. Versions `1` and `2` are implemented.
|
|
178
225
|
Empty or incompatible lists, omitted arguments, and invalid values throw an error
|
|
179
226
|
with `kind: "InvalidData"`.
|
|
180
227
|
|
|
181
228
|
The first packed message starts its metadata with protocol instruction `2` followed
|
|
182
|
-
by the selected version
|
|
229
|
+
by the selected version; both are unsigned variable-length integers.
|
|
183
230
|
Later messages omit this declaration and retain the same protocol for the stream.
|
|
184
231
|
The consumer rejects first messages without a leading protocol declaration,
|
|
185
232
|
unsupported versions, and repeated declarations. Legacy messages without version
|
|
186
|
-
metadata are
|
|
233
|
+
metadata are not accepted. Existing protocol 1 peers can continue to receive
|
|
234
|
+
updates, with decimals represented as strings.
|
|
187
235
|
|
|
188
236
|
Failed encoding or consumption preserves protocol initialization state as well as
|
|
189
237
|
pool state, allowing retries. Keep and deliver packed messages in order as usual.
|
|
190
238
|
|
|
239
|
+
### Decimal wire encoding
|
|
240
|
+
|
|
241
|
+
Protocol 2 represents `sign × coefficient × 10^-scale`. The first byte's high
|
|
242
|
+
nibble is `0x9` for nonnegative decimals or `0xA` for negative decimals; `0x6`
|
|
243
|
+
remains reserved. Its low nibble directly contains scales 0–14. A low nibble of
|
|
244
|
+
`0xF` is followed by one byte containing the complete scale, 15–28.
|
|
245
|
+
|
|
246
|
+
The absolute coefficient follows as minimal unsigned LEB128: seven data bits
|
|
247
|
+
per byte, least significant group first, with the high bit indicating continuation.
|
|
248
|
+
Zero uses one `00` byte. There is no ZigZag transform or negative offset.
|
|
249
|
+
Coefficients occupy 1–14 bytes and must fit in 96 bits. The fourteenth byte
|
|
250
|
+
must have its continuation bit clear and contain at most `0x1F`.
|
|
251
|
+
|
|
252
|
+
| Value | Hex bytes (excluding the message header) |
|
|
253
|
+
| --- | --- |
|
|
254
|
+
| `0.01` | `92 01` |
|
|
255
|
+
| `1.20` | `92 78` |
|
|
256
|
+
| `12.34` | `92 D2 09` |
|
|
257
|
+
| `-12.34` | `A2 D2 09` |
|
|
258
|
+
| `0.00` | `92 00` |
|
|
259
|
+
| `0.000000000000001` | `9F 0F 01` |
|
|
260
|
+
|
|
261
|
+
The maximum value encoding length is 16 bytes. Decoders reject negative zero,
|
|
262
|
+
non-minimal coefficients, invalid scales, overflow, truncation, and Decimal
|
|
263
|
+
tags in protocol 1. Existing Int encoding is unchanged.
|
|
264
|
+
|
|
191
265
|
### Path key encoding
|
|
192
266
|
|
|
193
267
|
Protocol v1 path definitions use the low two bits of each segment's unsigned
|
|
@@ -205,3 +279,31 @@ through message encoding and are resolved when the consumer reads the definition
|
|
|
205
279
|
|
|
206
280
|
This extends v1 directly. Both peers must be upgraded together: older decoders
|
|
207
281
|
reject the new `0b10` path segment tag.
|
|
282
|
+
|
|
283
|
+
### Compact floating-point values
|
|
284
|
+
|
|
285
|
+
Protocol 2 encodes numbers as binary16, binary32, or
|
|
286
|
+
binary64, choosing the shortest representation that preserves the original
|
|
287
|
+
binary64 bits. Decoded values remain JavaScript numbers. Signed zero is
|
|
288
|
+
preserved; NaNs retain binary64 encoding. Protocol 1 continues to use
|
|
289
|
+
binary64 for every number.
|
|
290
|
+
|
|
291
|
+
### Binary value reuse
|
|
292
|
+
|
|
293
|
+
Protocol 2 pools binary values by content, so repeated `Uint8Array` values can
|
|
294
|
+
share a compact reference within a message and across subsequent messages.
|
|
295
|
+
Producers admit payloads from 16 bytes through 64 KiB, using up to 256 slots
|
|
296
|
+
and at most 1 MiB of definitions per message. Values that cannot be admitted
|
|
297
|
+
remain inline. The first definition has an overhead, so pooling does not make
|
|
298
|
+
every message smaller.
|
|
299
|
+
|
|
300
|
+
The Rust pool shares immutable payload storage. Each `document()` conversion
|
|
301
|
+
still returns independent `Uint8Array` copies: modifying one field cannot change
|
|
302
|
+
another field, the consumer's document, or a later message.
|
|
303
|
+
|
|
304
|
+
This extends unreleased protocol 2 without adding a version. New consumers accept
|
|
305
|
+
earlier v2 messages, but earlier v2 consumers cannot read binary pool definitions
|
|
306
|
+
or references. Upgrade both v2 peers together; explicitly select `[1]` when
|
|
307
|
+
communicating with an implementation that lacks this extension. Version
|
|
308
|
+
negotiation alone cannot distinguish these v2 builds. Protocol 1 continues to
|
|
309
|
+
encode binary values inline.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InitSource } from './types.js';
|
|
2
2
|
export type { SyncValue, InitSource, DoxsyncError } from './types.js';
|
|
3
|
+
export { Decimal } from 'decimal.js';
|
|
3
4
|
export { Producer, Consumer, supportedProtocols } from './runtime.js';
|
|
4
5
|
/**
|
|
5
6
|
* Initialize once before constructing Producer or Consumer or calling supportedProtocols.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { i as e, n as t, r as n, t as r } from "./runtime-
|
|
1
|
+
import { i as e, n as t, r as n, t as r } from "./runtime-BePF61JJ.js";
|
|
2
|
+
import { Decimal as i } from "decimal.js";
|
|
2
3
|
//#region src/index.ts
|
|
3
|
-
function
|
|
4
|
+
function a(e) {
|
|
4
5
|
return n(() => e === void 0 ? new URL(
|
|
5
6
|
/* @vite-ignore */
|
|
6
7
|
"./wasm/doxsync_bg.wasm",
|
|
@@ -8,4 +9,4 @@ function i(e) {
|
|
|
8
9
|
) : e);
|
|
9
10
|
}
|
|
10
11
|
//#endregion
|
|
11
|
-
export { r as Consumer, t as Producer,
|
|
12
|
+
export { r as Consumer, i as Decimal, t as Producer, a as init, e as supportedProtocols };
|
package/dist/node.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InitSource } from './types.js';
|
|
2
2
|
export type { SyncValue, InitSource, DoxsyncError } from './types.js';
|
|
3
|
+
export { Decimal } from 'decimal.js';
|
|
3
4
|
export { Producer, Consumer, supportedProtocols } from './runtime.js';
|
|
4
5
|
/**
|
|
5
6
|
* Initialize once before constructing Producer or Consumer or calling supportedProtocols.
|
package/dist/node.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { i as e, n as t, r as n, t as r } from "./runtime-
|
|
2
|
-
import {
|
|
1
|
+
import { i as e, n as t, r as n, t as r } from "./runtime-BePF61JJ.js";
|
|
2
|
+
import { Decimal as i } from "decimal.js";
|
|
3
|
+
import { readFile as a } from "node:fs/promises";
|
|
3
4
|
//#region src/node.ts
|
|
4
|
-
function
|
|
5
|
-
return n(() => e === void 0 ?
|
|
5
|
+
function o(e) {
|
|
6
|
+
return n(() => e === void 0 ? a(new URL(
|
|
6
7
|
/* @vite-ignore */
|
|
7
8
|
"./wasm/doxsync_bg.wasm",
|
|
8
9
|
import.meta.url
|
|
9
|
-
)) : e instanceof URL && e.protocol === "file:" ?
|
|
10
|
+
)) : e instanceof URL && e.protocol === "file:" ? a(e) : typeof e == "string" && e.startsWith("file:") ? a(new URL(e)) : e);
|
|
10
11
|
}
|
|
11
12
|
//#endregion
|
|
12
|
-
export { r as Consumer, t as Producer,
|
|
13
|
+
export { r as Consumer, i as Decimal, t as Producer, o as init, e as supportedProtocols };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Decimal as e } from "decimal.js";
|
|
2
|
+
import t, { Consumer as n, Producer as r, supportedProtocols as i } from "./wasm/doxsync.js";
|
|
3
|
+
//#region src/decimal.ts
|
|
4
|
+
var a = e.clone({
|
|
5
|
+
precision: 29,
|
|
6
|
+
minE: -100,
|
|
7
|
+
maxE: 100
|
|
8
|
+
});
|
|
9
|
+
function o(t) {
|
|
10
|
+
if (e.isDecimal(t)) {
|
|
11
|
+
if (!t.isFinite() || t.decimalPlaces() > 28 || t.e > 28) throw Object.assign(/* @__PURE__ */ Error("Decimal outside the supported coefficient or scale range"), { kind: "InvalidData" });
|
|
12
|
+
return t.toFixed();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function s(e) {
|
|
16
|
+
return new a(e);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/runtime.ts
|
|
20
|
+
var c, l = !1;
|
|
21
|
+
function u(e) {
|
|
22
|
+
return c ||= Promise.resolve().then(e).then((e) => t({ module_or_path: e })).then(() => {
|
|
23
|
+
l = !0;
|
|
24
|
+
}).catch((e) => {
|
|
25
|
+
throw c = void 0, e;
|
|
26
|
+
}), c;
|
|
27
|
+
}
|
|
28
|
+
function d() {
|
|
29
|
+
if (!l) throw Error("Call and await init() before using the doxsync API");
|
|
30
|
+
}
|
|
31
|
+
function f() {
|
|
32
|
+
return d(), i();
|
|
33
|
+
}
|
|
34
|
+
var p = class {
|
|
35
|
+
#e;
|
|
36
|
+
constructor(e, t) {
|
|
37
|
+
d(), this.#e = new r(e, t, o);
|
|
38
|
+
}
|
|
39
|
+
#t() {
|
|
40
|
+
if (!this.#e) throw Error("Producer has been freed");
|
|
41
|
+
return this.#e;
|
|
42
|
+
}
|
|
43
|
+
replace(e) {
|
|
44
|
+
this.#t().replace(e);
|
|
45
|
+
}
|
|
46
|
+
produceDiff() {
|
|
47
|
+
return this.#t().produceDiff();
|
|
48
|
+
}
|
|
49
|
+
free() {
|
|
50
|
+
this.#e &&= (this.#e.free(), void 0);
|
|
51
|
+
}
|
|
52
|
+
}, m = class {
|
|
53
|
+
#e;
|
|
54
|
+
constructor() {
|
|
55
|
+
d(), this.#e = new n();
|
|
56
|
+
}
|
|
57
|
+
#t() {
|
|
58
|
+
if (!this.#e) throw Error("Consumer has been freed");
|
|
59
|
+
return this.#e;
|
|
60
|
+
}
|
|
61
|
+
consumeDiff(e) {
|
|
62
|
+
this.#t().consumeDiff(e);
|
|
63
|
+
}
|
|
64
|
+
document() {
|
|
65
|
+
return this.#t().document(s);
|
|
66
|
+
}
|
|
67
|
+
free() {
|
|
68
|
+
this.#e &&= (this.#e.free(), void 0);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { f as i, p as n, u as r, m as t };
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InitSource, SyncValue } from './types.js';
|
|
2
2
|
export declare function initialize(loadSource: () => InitSource | Promise<InitSource>): Promise<void>;
|
|
3
3
|
/**
|
|
4
|
-
* Returns all protocol versions supported by this build, currently [1].
|
|
4
|
+
* Returns all protocol versions supported by this build, currently [1, 2].
|
|
5
5
|
* Requires await init(). Each call returns an independent ordinary array.
|
|
6
6
|
* Consumers can advertise this list to producers for protocol negotiation.
|
|
7
7
|
*/
|
|
@@ -11,8 +11,10 @@ export declare class Producer {
|
|
|
11
11
|
/**
|
|
12
12
|
* Copies the value into WASM and selects the highest common protocol version.
|
|
13
13
|
* protocols lists the consumer's supported versions as u32 integers.
|
|
14
|
-
*
|
|
14
|
+
* Versions 1 and 2 are supported. Missing, invalid, empty, or incompatible
|
|
15
15
|
* lists throw InvalidData errors. Order and duplicates do not matter.
|
|
16
|
+
* Protocol 1 transmits Decimal values as strings; protocol 2 preserves the type.
|
|
17
|
+
* Protocol 2 also pools binary values; both peers must support that extension.
|
|
16
18
|
*/
|
|
17
19
|
constructor(value: SyncValue, protocols: readonly number[]);
|
|
18
20
|
/** Replaces the document after validating and copying the entire value. */
|
|
@@ -29,11 +31,13 @@ export declare class Consumer {
|
|
|
29
31
|
#private;
|
|
30
32
|
constructor();
|
|
31
33
|
/**
|
|
32
|
-
* Applies one complete message. The first message must declare protocol 1.
|
|
34
|
+
* Applies one complete message. The first message must declare protocol 1 or 2.
|
|
33
35
|
* Failures leave the document, pools, and protocol state intact.
|
|
34
36
|
*/
|
|
35
37
|
consumeDiff(bytes: Uint8Array): void;
|
|
36
|
-
/** Returns an independent copy, or undefined before the first snapshot.
|
|
38
|
+
/** Returns an independent copy, or undefined before the first snapshot.
|
|
39
|
+
* Protocol 2 decimals become decimal.js instances; their original scale is lost.
|
|
40
|
+
*/
|
|
37
41
|
document(): SyncValue | undefined;
|
|
38
42
|
/** Release WASM resources. Safe to call repeatedly. */
|
|
39
43
|
free(): void;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { Decimal } from 'decimal.js';
|
|
2
|
+
/** Numbers are floats; bigint values are integers in [-2^64, 2^64 - 1].
|
|
3
|
+
* Decimal instances preserve numeric value, but not trailing zeros.
|
|
4
|
+
*/
|
|
5
|
+
export type SyncValue = null | boolean | string | number | bigint | Decimal | Uint8Array | SyncValue[] | {
|
|
3
6
|
[key: string]: SyncValue;
|
|
4
7
|
};
|
|
5
8
|
export type InitSource = string | URL | ArrayBuffer | Uint8Array | WebAssembly.Module;
|
package/dist/wasm/doxsync.d.ts
CHANGED
|
@@ -5,14 +5,14 @@ export class Consumer {
|
|
|
5
5
|
free(): void;
|
|
6
6
|
[Symbol.dispose](): void;
|
|
7
7
|
consumeDiff(bytes: any): void;
|
|
8
|
-
document(): any;
|
|
8
|
+
document(decimal_from_string: Function): any;
|
|
9
9
|
constructor();
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export class Producer {
|
|
13
13
|
free(): void;
|
|
14
14
|
[Symbol.dispose](): void;
|
|
15
|
-
constructor(value: any, protocols: any);
|
|
15
|
+
constructor(value: any, protocols: any, decimal_to_string: Function);
|
|
16
16
|
produceDiff(): any;
|
|
17
17
|
replace(value: any): void;
|
|
18
18
|
}
|
|
@@ -26,9 +26,9 @@ export interface InitOutput {
|
|
|
26
26
|
readonly __wbg_consumer_free: (a: number, b: number) => void;
|
|
27
27
|
readonly __wbg_producer_free: (a: number, b: number) => void;
|
|
28
28
|
readonly consumer_consumeDiff: (a: number, b: any) => [number, number];
|
|
29
|
-
readonly consumer_document: (a: number) => [number, number, number];
|
|
29
|
+
readonly consumer_document: (a: number, b: any) => [number, number, number];
|
|
30
30
|
readonly consumer_new: () => number;
|
|
31
|
-
readonly producer_new: (a: any, b: any) => [number, number, number];
|
|
31
|
+
readonly producer_new: (a: any, b: any, c: any) => [number, number, number];
|
|
32
32
|
readonly producer_produceDiff: (a: number) => [number, number, number];
|
|
33
33
|
readonly producer_replace: (a: number, b: any) => [number, number];
|
|
34
34
|
readonly supportedProtocols: () => any;
|
package/dist/wasm/doxsync.js
CHANGED
|
@@ -21,10 +21,11 @@ export class Consumer {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
+
* @param {Function} decimal_from_string
|
|
24
25
|
* @returns {any}
|
|
25
26
|
*/
|
|
26
|
-
document() {
|
|
27
|
-
const ret = wasm.consumer_document(this.__wbg_ptr);
|
|
27
|
+
document(decimal_from_string) {
|
|
28
|
+
const ret = wasm.consumer_document(this.__wbg_ptr, decimal_from_string);
|
|
28
29
|
if (ret[2]) {
|
|
29
30
|
throw takeFromExternrefTable0(ret[1]);
|
|
30
31
|
}
|
|
@@ -53,9 +54,10 @@ export class Producer {
|
|
|
53
54
|
/**
|
|
54
55
|
* @param {any} value
|
|
55
56
|
* @param {any} protocols
|
|
57
|
+
* @param {Function} decimal_to_string
|
|
56
58
|
*/
|
|
57
|
-
constructor(value, protocols) {
|
|
58
|
-
const ret = wasm.producer_new(value, protocols);
|
|
59
|
+
constructor(value, protocols, decimal_to_string) {
|
|
60
|
+
const ret = wasm.producer_new(value, protocols, decimal_to_string);
|
|
59
61
|
if (ret[2]) {
|
|
60
62
|
throw takeFromExternrefTable0(ret[1]);
|
|
61
63
|
}
|
|
@@ -156,6 +158,10 @@ function __wbg_get_imports() {
|
|
|
156
158
|
const ret = arg0.add(arg1);
|
|
157
159
|
return ret;
|
|
158
160
|
},
|
|
161
|
+
__wbg_call_6bcf8d3e20937e46: function() { return handleError(function (arg0, arg1, arg2) {
|
|
162
|
+
const ret = arg0.call(arg1, arg2);
|
|
163
|
+
return ret;
|
|
164
|
+
}, arguments); },
|
|
159
165
|
__wbg_charCodeAt_90635349fbda9f5f: function(arg0, arg1) {
|
|
160
166
|
const ret = arg0.charCodeAt(arg1 >>> 0);
|
|
161
167
|
return ret;
|
|
Binary file
|
|
@@ -4,9 +4,9 @@ export const memory: WebAssembly.Memory;
|
|
|
4
4
|
export const __wbg_consumer_free: (a: number, b: number) => void;
|
|
5
5
|
export const __wbg_producer_free: (a: number, b: number) => void;
|
|
6
6
|
export const consumer_consumeDiff: (a: number, b: any) => [number, number];
|
|
7
|
-
export const consumer_document: (a: number) => [number, number, number];
|
|
7
|
+
export const consumer_document: (a: number, b: any) => [number, number, number];
|
|
8
8
|
export const consumer_new: () => number;
|
|
9
|
-
export const producer_new: (a: any, b: any) => [number, number, number];
|
|
9
|
+
export const producer_new: (a: any, b: any, c: 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
12
|
export const supportedProtocols: () => any;
|
package/examples/browser.html
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>doxsync · Sync playground</title>
|
|
7
7
|
<link rel="stylesheet" href="./browser.css">
|
|
8
|
+
<script type="importmap">
|
|
9
|
+
{ "imports": { "decimal.js": "../node_modules/decimal.js/decimal.mjs" } }
|
|
10
|
+
</script>
|
|
8
11
|
<script type="module" src="./browser.js"></script>
|
|
9
12
|
</head>
|
|
10
13
|
<body>
|
package/examples/browser.js
CHANGED
|
@@ -11,6 +11,7 @@ let loading = false;
|
|
|
11
11
|
const initialDocument = () => ({
|
|
12
12
|
title: "doxsync playground",
|
|
13
13
|
visitors: 12n,
|
|
14
|
+
price: new bindings.Decimal("12.34"),
|
|
14
15
|
online: true,
|
|
15
16
|
settings: { theme: "light", volume: 60 },
|
|
16
17
|
tags: ["rust", "wasm"],
|
|
@@ -28,6 +29,7 @@ const delayValue = () => {
|
|
|
28
29
|
// suffix preserves bigint's type instead of presenting it as a JSON string.
|
|
29
30
|
function format(value, depth = 0) {
|
|
30
31
|
if (typeof value === "bigint") return `${value}n`;
|
|
32
|
+
if (bindings.Decimal.isDecimal(value)) return `Decimal("${value.toFixed()}")`;
|
|
31
33
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
32
34
|
const array = Array.isArray(value);
|
|
33
35
|
const entries = array ? value : Object.keys(value).sort();
|
|
@@ -219,6 +221,7 @@ function send(summary) {
|
|
|
219
221
|
}
|
|
220
222
|
|
|
221
223
|
const mutations = [
|
|
224
|
+
(doc) => { doc.price = doc.price.plus("0.01"); return `price → ${doc.price.toFixed(2)}`; },
|
|
222
225
|
(doc) => { const delta = BigInt(number(1, 5)); doc.visitors += delta; return `visitors increased by ${delta}`; },
|
|
223
226
|
(doc) => { doc.online = !doc.online; return `online → ${doc.online}`; },
|
|
224
227
|
(doc) => { doc.settings.theme = pick(["light", "dark", "system"].filter((value) => value !== doc.settings.theme)); return `settings.theme → ${doc.settings.theme}`; },
|
|
@@ -246,7 +249,13 @@ function mutate() {
|
|
|
246
249
|
try {
|
|
247
250
|
// Producer owns a copy. Keep this display model in step with successfully
|
|
248
251
|
// accepted replacements; no JS implementation of the diff algorithm.
|
|
249
|
-
|
|
252
|
+
// Copy this demo's containers explicitly: structuredClone cannot copy Decimal.
|
|
253
|
+
const next = {
|
|
254
|
+
...state.document,
|
|
255
|
+
settings: { ...state.document.settings },
|
|
256
|
+
tags: [...state.document.tags],
|
|
257
|
+
price: new bindings.Decimal(state.document.price),
|
|
258
|
+
};
|
|
250
259
|
const summary = pick(mutations)(next);
|
|
251
260
|
state.producer.replace(next);
|
|
252
261
|
state.document = next;
|
package/examples/node.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { init, Producer, Consumer, supportedProtocols } from "@doxsync/core";
|
|
1
|
+
import { init, Producer, Consumer, Decimal, supportedProtocols } from "@doxsync/core";
|
|
2
2
|
|
|
3
3
|
await init();
|
|
4
|
-
const producer = new Producer({ count: 1n, data: new Uint8Array([1, 2, 3]) }, supportedProtocols());
|
|
4
|
+
const producer = new Producer({ count: 1n, price: new Decimal("12.34"), data: new Uint8Array([1, 2, 3]) }, supportedProtocols());
|
|
5
5
|
const consumer = new Consumer();
|
|
6
6
|
|
|
7
7
|
try {
|
|
8
8
|
consumer.consumeDiff(producer.produceDiff());
|
|
9
9
|
console.log("Snapshot:", consumer.document());
|
|
10
10
|
|
|
11
|
-
producer.replace({ count: 2n, data: new Uint8Array([1, 2, 3]) });
|
|
11
|
+
producer.replace({ count: 2n, price: new Decimal("12.35"), data: new Uint8Array([1, 2, 3]) });
|
|
12
12
|
const message = producer.produceDiff();
|
|
13
13
|
if (message !== undefined) consumer.consumeDiff(message);
|
|
14
14
|
console.log("Updated:", consumer.document());
|
|
15
|
+
console.log("Exact price:", consumer.document().price.toFixed(2));
|
|
15
16
|
console.log("Unchanged:", producer.produceDiff());
|
|
16
17
|
} finally {
|
|
17
18
|
producer.free();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doxsync/core",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"description": "JavaScript bindings for the doxsync Rust synchronization engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -37,5 +37,8 @@
|
|
|
37
37
|
"typescript": "^6.0.3",
|
|
38
38
|
"vite": "^8.3.0",
|
|
39
39
|
"vite-plugin-dts": "^5.1.1"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"decimal.js": "^10.6.0"
|
|
40
43
|
}
|
|
41
44
|
}
|
package/dist/runtime-CJqt5vND.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
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 };
|