@irtio/schema 4.7.1 → 4.8.0
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/dist/index.d.ts +124 -10
- package/dist/index.js +708 -62
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -13,7 +13,8 @@ var TypeImpl = class _TypeImpl {
|
|
|
13
13
|
}
|
|
14
14
|
const err = validateValue(this.desc, value);
|
|
15
15
|
if (err) throw new Error(`invalid default for ${describeType(this.desc)}: ${err}`);
|
|
16
|
-
|
|
16
|
+
const stored = value instanceof Uint8Array ? copyBytes(value) : value;
|
|
17
|
+
return new _TypeImpl({ ...this.desc, hasDefault: true, default: stored });
|
|
17
18
|
}
|
|
18
19
|
};
|
|
19
20
|
function make(desc) {
|
|
@@ -39,8 +40,32 @@ function enumOf(...values) {
|
|
|
39
40
|
function ref(entity2) {
|
|
40
41
|
return make({ kind: "ref", entity: entity2 });
|
|
41
42
|
}
|
|
43
|
+
var BYTES_MAX = 65535;
|
|
44
|
+
function bytes(n) {
|
|
45
|
+
if (!Number.isInteger(n) || n < 1 || n > BYTES_MAX) {
|
|
46
|
+
throw new Error(`bytes(n): n must be an integer from 1 to ${BYTES_MAX}, got ${String(n)}`);
|
|
47
|
+
}
|
|
48
|
+
return make({ kind: "bytes", n });
|
|
49
|
+
}
|
|
50
|
+
function containsBytes(d) {
|
|
51
|
+
switch (d.kind) {
|
|
52
|
+
case "bytes":
|
|
53
|
+
return true;
|
|
54
|
+
case "list":
|
|
55
|
+
return containsBytes(d.item);
|
|
56
|
+
case "struct":
|
|
57
|
+
return Object.values(d.fields).some(containsBytes);
|
|
58
|
+
default:
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
42
62
|
function list(item, max) {
|
|
43
63
|
assertMax(max, "list");
|
|
64
|
+
if (containsBytes(item.desc)) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
"list items may not contain bytes(n) \u2014 a list is whole-replace; model chunks as an entity collection"
|
|
67
|
+
);
|
|
68
|
+
}
|
|
44
69
|
return make({ kind: "list", item: item.desc, max });
|
|
45
70
|
}
|
|
46
71
|
function struct(fields) {
|
|
@@ -73,6 +98,9 @@ function describeType(d) {
|
|
|
73
98
|
case "ref":
|
|
74
99
|
s = `ref(${JSON.stringify(d.entity)})`;
|
|
75
100
|
break;
|
|
101
|
+
case "bytes":
|
|
102
|
+
s = `bytes(${d.n})`;
|
|
103
|
+
break;
|
|
76
104
|
case "list":
|
|
77
105
|
s = `list(${describeType(d.item)}, ${d.max})`;
|
|
78
106
|
break;
|
|
@@ -123,6 +151,8 @@ function validateValue(d, v, path = "") {
|
|
|
123
151
|
}
|
|
124
152
|
case "enum":
|
|
125
153
|
return typeof v === "string" && d.values.includes(v) ? null : `${at} must be one of ${d.values.join("|")}`;
|
|
154
|
+
case "bytes":
|
|
155
|
+
return v instanceof Uint8Array && v.length === d.n ? null : `${at} must be a Uint8Array of length ${d.n} (got ${describeBytesValue(v)})`;
|
|
126
156
|
case "list": {
|
|
127
157
|
if (!Array.isArray(v)) return `${at} must be an array`;
|
|
128
158
|
if (v.length > d.max) return `${at} exceeds max length ${d.max} (${v.length})`;
|
|
@@ -160,6 +190,8 @@ function defaultValue(d) {
|
|
|
160
190
|
return "";
|
|
161
191
|
case "enum":
|
|
162
192
|
return d.values[0];
|
|
193
|
+
case "bytes":
|
|
194
|
+
return new Uint8Array(d.n);
|
|
163
195
|
case "list":
|
|
164
196
|
return [];
|
|
165
197
|
case "struct": {
|
|
@@ -170,6 +202,7 @@ function defaultValue(d) {
|
|
|
170
202
|
}
|
|
171
203
|
}
|
|
172
204
|
function cloneValue(v) {
|
|
205
|
+
if (v instanceof Uint8Array) return copyBytes(v);
|
|
173
206
|
if (Array.isArray(v)) return v.map(cloneValue);
|
|
174
207
|
if (v !== null && typeof v === "object") {
|
|
175
208
|
const o = {};
|
|
@@ -199,6 +232,11 @@ function normalizeValue(d, v, path = "") {
|
|
|
199
232
|
if (v.length > d.max) throw new Error(`${at} exceeds max length ${d.max} (${v.length})`);
|
|
200
233
|
return v.map((x, i) => normalizeValue(d.item, x, `${at}[${i}]`));
|
|
201
234
|
}
|
|
235
|
+
case "bytes": {
|
|
236
|
+
const err = validateValue(d, v, path);
|
|
237
|
+
if (err) throw new Error(err);
|
|
238
|
+
return copyBytes(v);
|
|
239
|
+
}
|
|
202
240
|
case "struct": {
|
|
203
241
|
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
204
242
|
throw new Error(`${at} must be an object`);
|
|
@@ -215,6 +253,83 @@ function normalizeValue(d, v, path = "") {
|
|
|
215
253
|
}
|
|
216
254
|
}
|
|
217
255
|
}
|
|
256
|
+
function copyBytes(v) {
|
|
257
|
+
return ArrayBuffer.isView(v) ? new Uint8Array(v) : v.slice();
|
|
258
|
+
}
|
|
259
|
+
function describeBytesValue(v) {
|
|
260
|
+
if (v instanceof Uint8Array) return `a Uint8Array of length ${v.length}`;
|
|
261
|
+
if (Array.isArray(v)) return `an array of length ${v.length}`;
|
|
262
|
+
if (v === null) return "null";
|
|
263
|
+
if (ArrayBuffer.isView(v)) return `a ${v.constructor.name}`;
|
|
264
|
+
return typeof v;
|
|
265
|
+
}
|
|
266
|
+
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
267
|
+
function bytesToBase64(b) {
|
|
268
|
+
let out = "";
|
|
269
|
+
let i = 0;
|
|
270
|
+
for (; i + 2 < b.length; i += 3) {
|
|
271
|
+
const x = b[i] << 16 | b[i + 1] << 8 | b[i + 2];
|
|
272
|
+
out += B64[x >> 18] + B64[x >> 12 & 63] + B64[x >> 6 & 63] + B64[x & 63];
|
|
273
|
+
}
|
|
274
|
+
const rest = b.length - i;
|
|
275
|
+
if (rest === 1) {
|
|
276
|
+
const x = b[i] << 16;
|
|
277
|
+
out += `${B64[x >> 18]}${B64[x >> 12 & 63]}==`;
|
|
278
|
+
} else if (rest === 2) {
|
|
279
|
+
const x = b[i] << 16 | b[i + 1] << 8;
|
|
280
|
+
out += `${B64[x >> 18]}${B64[x >> 12 & 63]}${B64[x >> 6 & 63]}=`;
|
|
281
|
+
}
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
function base64ToBytes(s) {
|
|
285
|
+
if (s.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(s)) {
|
|
286
|
+
throw new Error("invalid base64");
|
|
287
|
+
}
|
|
288
|
+
const pad = s.endsWith("==") ? 2 : s.endsWith("=") ? 1 : 0;
|
|
289
|
+
const out = new Uint8Array(s.length / 4 * 3 - pad);
|
|
290
|
+
let o = 0;
|
|
291
|
+
for (let i = 0; i < s.length; i += 4) {
|
|
292
|
+
const x = B64.indexOf(s[i]) << 18 | B64.indexOf(s[i + 1]) << 12 | (s[i + 2] === "=" ? 0 : B64.indexOf(s[i + 2])) << 6 | (s[i + 3] === "=" ? 0 : B64.indexOf(s[i + 3]));
|
|
293
|
+
out[o++] = x >> 16;
|
|
294
|
+
if (o < out.length) out[o++] = x >> 8 & 255;
|
|
295
|
+
if (o < out.length) out[o++] = x & 255;
|
|
296
|
+
}
|
|
297
|
+
return out;
|
|
298
|
+
}
|
|
299
|
+
function jsonDefault(d, v) {
|
|
300
|
+
if (v === void 0 || v === null) return v;
|
|
301
|
+
switch (d.kind) {
|
|
302
|
+
case "bytes":
|
|
303
|
+
return v instanceof Uint8Array ? bytesToBase64(v) : v;
|
|
304
|
+
case "struct": {
|
|
305
|
+
const o = {};
|
|
306
|
+
for (const [k, fd] of Object.entries(d.fields)) {
|
|
307
|
+
o[k] = jsonDefault(fd, v[k]);
|
|
308
|
+
}
|
|
309
|
+
return o;
|
|
310
|
+
}
|
|
311
|
+
default:
|
|
312
|
+
return v;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function defaultFromJson(d, v) {
|
|
316
|
+
if (v === void 0 || v === null) return v;
|
|
317
|
+
switch (d.kind) {
|
|
318
|
+
case "bytes":
|
|
319
|
+
return typeof v === "string" ? base64ToBytes(v) : v;
|
|
320
|
+
case "struct": {
|
|
321
|
+
if (typeof v !== "object") return v;
|
|
322
|
+
const o = {};
|
|
323
|
+
for (const [k, fd] of Object.entries(d.fields)) {
|
|
324
|
+
const x = v[k];
|
|
325
|
+
if (x !== void 0) o[k] = defaultFromJson(fd, x);
|
|
326
|
+
}
|
|
327
|
+
return o;
|
|
328
|
+
}
|
|
329
|
+
default:
|
|
330
|
+
return v;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
218
333
|
|
|
219
334
|
// src/physics.ts
|
|
220
335
|
var PHYSICS_BODY_CHANNELS = [
|
|
@@ -647,9 +762,9 @@ function sha256(data) {
|
|
|
647
762
|
function sha256Hex(data) {
|
|
648
763
|
return toHex(sha256(data));
|
|
649
764
|
}
|
|
650
|
-
function toHex(
|
|
765
|
+
function toHex(bytes2) {
|
|
651
766
|
let s = "";
|
|
652
|
-
for (let i = 0; i <
|
|
767
|
+
for (let i = 0; i < bytes2.length; i++) s += bytes2[i].toString(16).padStart(2, "0");
|
|
653
768
|
return s;
|
|
654
769
|
}
|
|
655
770
|
|
|
@@ -715,13 +830,13 @@ function maxEncodedBytes(fields, names) {
|
|
|
715
830
|
return total;
|
|
716
831
|
}
|
|
717
832
|
function varintBytes(max) {
|
|
718
|
-
let
|
|
833
|
+
let bytes2 = 1;
|
|
719
834
|
let v = Math.max(0, Math.floor(max));
|
|
720
835
|
while (v > 127) {
|
|
721
836
|
v = Math.floor(v / 128);
|
|
722
|
-
|
|
837
|
+
bytes2++;
|
|
723
838
|
}
|
|
724
|
-
return
|
|
839
|
+
return bytes2;
|
|
725
840
|
}
|
|
726
841
|
function worstCase(d) {
|
|
727
842
|
const presence = d.opt ? 1 : 0;
|
|
@@ -742,6 +857,8 @@ function worstCase(d) {
|
|
|
742
857
|
return presence + varintBytes(d.max) + d.max;
|
|
743
858
|
case "ref":
|
|
744
859
|
return presence + varintBytes(ID_MAX_BYTES) + ID_MAX_BYTES;
|
|
860
|
+
case "bytes":
|
|
861
|
+
return presence + d.n;
|
|
745
862
|
case "list":
|
|
746
863
|
return presence + varintBytes(d.max) + d.max * worstCase(d.item);
|
|
747
864
|
case "struct": {
|
|
@@ -778,12 +895,17 @@ function compilePersist(name, fields, perPlayer, declared) {
|
|
|
778
895
|
`${name}: persist names ${field}, an entity ref \u2014 a ref is an id inside one room and means nothing in the next one`
|
|
779
896
|
);
|
|
780
897
|
}
|
|
898
|
+
if (containsBytes(known.get(field).type)) {
|
|
899
|
+
throw new Error(
|
|
900
|
+
`${name}: persist names ${field}, a bytes field \u2014 the persistence blob is JSON and does not carry binary`
|
|
901
|
+
);
|
|
902
|
+
}
|
|
781
903
|
out.push(field);
|
|
782
904
|
}
|
|
783
|
-
const
|
|
784
|
-
if (
|
|
905
|
+
const bytes2 = maxEncodedBytes(fields, out);
|
|
906
|
+
if (bytes2 > PERSIST_BLOB_BYTES) {
|
|
785
907
|
throw new Error(
|
|
786
|
-
`${name}: persist fields can encode up to ${
|
|
908
|
+
`${name}: persist fields can encode up to ${bytes2} bytes, over the 4 KB persistence cap`
|
|
787
909
|
);
|
|
788
910
|
}
|
|
789
911
|
return out.sort();
|
|
@@ -848,6 +970,7 @@ function defineSchema(defs, options = {}) {
|
|
|
848
970
|
const ownership = o.ownership || perPlayer ? compileOwnership(name, def.kind, fields, o.ownership ?? {}, perPlayer) : void 0;
|
|
849
971
|
const persist = compilePersist(name, fields, perPlayer, o.persist);
|
|
850
972
|
const serverFields = compileServerFields(name, def.kind, fields, o);
|
|
973
|
+
checkBytesWriters(name, def.kind, fields, o, serverFields);
|
|
851
974
|
return {
|
|
852
975
|
name,
|
|
853
976
|
index,
|
|
@@ -988,6 +1111,15 @@ function defineSchema(defs, options = {}) {
|
|
|
988
1111
|
hash8: hashBytes.slice(0, 8)
|
|
989
1112
|
};
|
|
990
1113
|
}
|
|
1114
|
+
function checkBytesWriters(name, kind, fields, o, serverFields) {
|
|
1115
|
+
if (kind !== "entity" || o.serverOwned === true) return;
|
|
1116
|
+
for (const f of fields) {
|
|
1117
|
+
if (!containsBytes(f.type) || serverFields?.has(f.name)) continue;
|
|
1118
|
+
throw new Error(
|
|
1119
|
+
`${name}.${f.name}: bytes(n) fields are server-written only in this release \u2014 declare the collection serverOwned: true or list ${JSON.stringify(f.name)} in serverFields`
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
991
1123
|
function compileFields(fields) {
|
|
992
1124
|
return Object.entries(fields).map(([name, t], index) => ({ name, index, type: t.desc }));
|
|
993
1125
|
}
|
|
@@ -1011,7 +1143,7 @@ var CANONICAL_VERSION = 1;
|
|
|
1011
1143
|
function canonicalType(d) {
|
|
1012
1144
|
const o = { kind: d.kind };
|
|
1013
1145
|
if (d.opt) o.opt = true;
|
|
1014
|
-
if (d.hasDefault) o.default = d.default;
|
|
1146
|
+
if (d.hasDefault) o.default = containsBytes(d) ? jsonDefault(d, d.default) : d.default;
|
|
1015
1147
|
switch (d.kind) {
|
|
1016
1148
|
case "str":
|
|
1017
1149
|
o.max = d.max;
|
|
@@ -1022,6 +1154,9 @@ function canonicalType(d) {
|
|
|
1022
1154
|
case "ref":
|
|
1023
1155
|
o.entity = d.entity;
|
|
1024
1156
|
break;
|
|
1157
|
+
case "bytes":
|
|
1158
|
+
o.n = d.n;
|
|
1159
|
+
break;
|
|
1025
1160
|
case "list":
|
|
1026
1161
|
o.item = canonicalType(d.item);
|
|
1027
1162
|
o.max = d.max;
|
|
@@ -1332,6 +1467,9 @@ function typeOf(t, at) {
|
|
|
1332
1467
|
case "ref":
|
|
1333
1468
|
base = ref(stringAt(t, "entity"));
|
|
1334
1469
|
break;
|
|
1470
|
+
case "bytes":
|
|
1471
|
+
base = bytes(numberAt(t, "n"));
|
|
1472
|
+
break;
|
|
1335
1473
|
case "list":
|
|
1336
1474
|
base = list(typeOf(objectAt(t, "item"), `${at}[]`), numberAt(t, "max"));
|
|
1337
1475
|
break;
|
|
@@ -1342,7 +1480,10 @@ function typeOf(t, at) {
|
|
|
1342
1480
|
throw new Error(`${at}: unknown type kind ${JSON.stringify(kind)}`);
|
|
1343
1481
|
}
|
|
1344
1482
|
if (t.opt === true) base = base.opt;
|
|
1345
|
-
if (Object.hasOwn(t, "default"))
|
|
1483
|
+
if (Object.hasOwn(t, "default")) {
|
|
1484
|
+
const value = containsBytes(base.desc) ? defaultFromJson(base.desc, t.default) : t.default;
|
|
1485
|
+
base = base.default(value);
|
|
1486
|
+
}
|
|
1346
1487
|
return base;
|
|
1347
1488
|
}
|
|
1348
1489
|
function entityOptions(e, name) {
|
|
@@ -1565,6 +1706,7 @@ function createState(schema, init) {
|
|
|
1565
1706
|
}
|
|
1566
1707
|
|
|
1567
1708
|
// src/dirty.ts
|
|
1709
|
+
var MAX_BYTE_RANGES = 16;
|
|
1568
1710
|
function createDirtySet() {
|
|
1569
1711
|
return /* @__PURE__ */ new Map();
|
|
1570
1712
|
}
|
|
@@ -1610,6 +1752,87 @@ function markPath(mask, path) {
|
|
|
1610
1752
|
const last = path[path.length - 1];
|
|
1611
1753
|
m.fields.add(last);
|
|
1612
1754
|
m.nested.delete(last);
|
|
1755
|
+
m.ranges?.delete(last);
|
|
1756
|
+
}
|
|
1757
|
+
function markBytes(dirty, collection, id, path, start, end, n) {
|
|
1758
|
+
const r = recordDirty(dirty, collection, id);
|
|
1759
|
+
if (r) markBytesPath(r.mask, path, start, end, n);
|
|
1760
|
+
}
|
|
1761
|
+
function markBytesPath(mask, path, start, end, n) {
|
|
1762
|
+
if (path.length === 0 || end <= start) return;
|
|
1763
|
+
let m = mask;
|
|
1764
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
1765
|
+
const idx = path[i];
|
|
1766
|
+
if (isWhole(m, idx)) return;
|
|
1767
|
+
m.fields.add(idx);
|
|
1768
|
+
let nm = m.nested.get(idx);
|
|
1769
|
+
if (!nm) {
|
|
1770
|
+
nm = createFieldMask();
|
|
1771
|
+
m.nested.set(idx, nm);
|
|
1772
|
+
}
|
|
1773
|
+
m = nm;
|
|
1774
|
+
}
|
|
1775
|
+
const last = path[path.length - 1];
|
|
1776
|
+
if (m.fields.has(last) && !m.ranges?.has(last)) return;
|
|
1777
|
+
m.fields.add(last);
|
|
1778
|
+
const ranges = m.ranges ??= /* @__PURE__ */ new Map();
|
|
1779
|
+
const merged = unionRanges(ranges.get(last) ?? [], [{ start, end }]);
|
|
1780
|
+
const settled = settleRanges(merged, n);
|
|
1781
|
+
if (settled) ranges.set(last, settled);
|
|
1782
|
+
else ranges.delete(last);
|
|
1783
|
+
}
|
|
1784
|
+
function varintSize(v) {
|
|
1785
|
+
let n = 1;
|
|
1786
|
+
let x = v;
|
|
1787
|
+
while (x >= 128) {
|
|
1788
|
+
x = Math.floor(x / 128);
|
|
1789
|
+
n++;
|
|
1790
|
+
}
|
|
1791
|
+
return n;
|
|
1792
|
+
}
|
|
1793
|
+
function rangesPatchBytes(ranges) {
|
|
1794
|
+
let total = varintSize(ranges.length);
|
|
1795
|
+
for (const r of ranges) {
|
|
1796
|
+
const len = r.end - r.start;
|
|
1797
|
+
total += varintSize(r.start) + varintSize(len) + len;
|
|
1798
|
+
}
|
|
1799
|
+
return total;
|
|
1800
|
+
}
|
|
1801
|
+
function settleRanges(ranges, n) {
|
|
1802
|
+
if (ranges.length === 0 || ranges.length > MAX_BYTE_RANGES) return void 0;
|
|
1803
|
+
if (n !== void 0 && rangesPatchBytes(ranges) >= n) return void 0;
|
|
1804
|
+
return ranges;
|
|
1805
|
+
}
|
|
1806
|
+
function unionRanges(a, b) {
|
|
1807
|
+
const all = [...a, ...b].sort((x, y) => x.start - y.start);
|
|
1808
|
+
const out = [];
|
|
1809
|
+
for (const r of all) {
|
|
1810
|
+
const last = out[out.length - 1];
|
|
1811
|
+
if (last && last.end >= r.start) {
|
|
1812
|
+
if (r.end > last.end) out[out.length - 1] = { start: last.start, end: r.end };
|
|
1813
|
+
} else {
|
|
1814
|
+
out.push({ start: r.start, end: r.end });
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
return out;
|
|
1818
|
+
}
|
|
1819
|
+
function diffByteRanges(a, b, gap = 4) {
|
|
1820
|
+
const out = [];
|
|
1821
|
+
const n = Math.min(a.length, b.length);
|
|
1822
|
+
let i = 0;
|
|
1823
|
+
while (i < n) {
|
|
1824
|
+
if (a[i] === b[i]) {
|
|
1825
|
+
i++;
|
|
1826
|
+
continue;
|
|
1827
|
+
}
|
|
1828
|
+
let end = i + 1;
|
|
1829
|
+
while (end < n && a[end] !== b[end]) end++;
|
|
1830
|
+
const last = out[out.length - 1];
|
|
1831
|
+
if (last && i - last.end < gap) out[out.length - 1] = { start: last.start, end };
|
|
1832
|
+
else out.push({ start: i, end });
|
|
1833
|
+
i = end;
|
|
1834
|
+
}
|
|
1835
|
+
return out;
|
|
1613
1836
|
}
|
|
1614
1837
|
function markOwner(dirty, collection, id) {
|
|
1615
1838
|
const r = recordDirty(dirty, collection, id);
|
|
@@ -1634,7 +1857,7 @@ function isDirtyEmpty(dirty) {
|
|
|
1634
1857
|
return true;
|
|
1635
1858
|
}
|
|
1636
1859
|
function isWhole(mask, idx) {
|
|
1637
|
-
return mask.fields.has(idx) && !mask.nested.has(idx);
|
|
1860
|
+
return mask.fields.has(idx) && !mask.nested.has(idx) && !mask.ranges?.has(idx);
|
|
1638
1861
|
}
|
|
1639
1862
|
function mergeDirty(dst, src) {
|
|
1640
1863
|
for (const [name, sc] of src) {
|
|
@@ -1651,9 +1874,18 @@ function mergeDirty(dst, src) {
|
|
|
1651
1874
|
function mergeMask(dst, src) {
|
|
1652
1875
|
for (const idx of src.fields) {
|
|
1653
1876
|
const n = src.nested.get(idx);
|
|
1654
|
-
|
|
1877
|
+
const sr = src.ranges?.get(idx);
|
|
1878
|
+
if (sr) {
|
|
1879
|
+
if (dst.fields.has(idx) && !dst.ranges?.has(idx)) continue;
|
|
1880
|
+
dst.fields.add(idx);
|
|
1881
|
+
const ranges = dst.ranges ??= /* @__PURE__ */ new Map();
|
|
1882
|
+
const settled = settleRanges(unionRanges(ranges.get(idx) ?? [], sr));
|
|
1883
|
+
if (settled) ranges.set(idx, settled);
|
|
1884
|
+
else ranges.delete(idx);
|
|
1885
|
+
} else if (!n) {
|
|
1655
1886
|
dst.fields.add(idx);
|
|
1656
1887
|
dst.nested.delete(idx);
|
|
1888
|
+
dst.ranges?.delete(idx);
|
|
1657
1889
|
} else if (!isWhole(dst, idx)) {
|
|
1658
1890
|
dst.fields.add(idx);
|
|
1659
1891
|
let dn = dst.nested.get(idx);
|
|
@@ -1773,11 +2005,11 @@ var ByteWriter = class {
|
|
|
1773
2005
|
return this.buf.slice(0, this.pos);
|
|
1774
2006
|
}
|
|
1775
2007
|
};
|
|
1776
|
-
function utf82(
|
|
2008
|
+
function utf82(bytes2) {
|
|
1777
2009
|
try {
|
|
1778
|
-
return decoder.decode(
|
|
2010
|
+
return decoder.decode(bytes2);
|
|
1779
2011
|
} catch {
|
|
1780
|
-
throw new Error(`invalid UTF-8 in string (${
|
|
2012
|
+
throw new Error(`invalid UTF-8 in string (${bytes2.length} bytes)`);
|
|
1781
2013
|
}
|
|
1782
2014
|
}
|
|
1783
2015
|
var ByteReader = class {
|
|
@@ -1889,7 +2121,7 @@ function bytesEqual(a, b) {
|
|
|
1889
2121
|
}
|
|
1890
2122
|
|
|
1891
2123
|
// src/codec.ts
|
|
1892
|
-
function
|
|
2124
|
+
function varintSize2(v) {
|
|
1893
2125
|
let n = 1;
|
|
1894
2126
|
let x = v;
|
|
1895
2127
|
while (x >= 128) {
|
|
@@ -1924,14 +2156,14 @@ var CountingSink = class {
|
|
|
1924
2156
|
varint(v) {
|
|
1925
2157
|
if (!Number.isInteger(v) || v < 0 || v > 4294967295)
|
|
1926
2158
|
throw new Error(`varint out of range: ${v}`);
|
|
1927
|
-
this.length +=
|
|
2159
|
+
this.length += varintSize2(v);
|
|
1928
2160
|
}
|
|
1929
2161
|
bytes(b) {
|
|
1930
2162
|
this.length += b.length;
|
|
1931
2163
|
}
|
|
1932
2164
|
str(s) {
|
|
1933
2165
|
const n = utf8Length(s);
|
|
1934
|
-
this.length +=
|
|
2166
|
+
this.length += varintSize2(n) + n;
|
|
1935
2167
|
return n;
|
|
1936
2168
|
}
|
|
1937
2169
|
};
|
|
@@ -2000,6 +2232,9 @@ function writeValue(w, desc, v, path = "value") {
|
|
|
2000
2232
|
w.u8(i);
|
|
2001
2233
|
return;
|
|
2002
2234
|
}
|
|
2235
|
+
case "bytes":
|
|
2236
|
+
w.bytes(checkBytes(value, desc.n, path));
|
|
2237
|
+
return;
|
|
2003
2238
|
case "list": {
|
|
2004
2239
|
if (!Array.isArray(value)) fail(path, "must be an array");
|
|
2005
2240
|
if (value.length > desc.max) fail(path, `exceeds max length ${desc.max} (${value.length})`);
|
|
@@ -2016,6 +2251,14 @@ function writeValue(w, desc, v, path = "value") {
|
|
|
2016
2251
|
}
|
|
2017
2252
|
}
|
|
2018
2253
|
}
|
|
2254
|
+
function checkBytes(value, n, path) {
|
|
2255
|
+
if (!(value instanceof Uint8Array) || value.length !== n) {
|
|
2256
|
+
fail(path, `must be a Uint8Array of length ${n} (got ${describeBytesValue(value)})`);
|
|
2257
|
+
}
|
|
2258
|
+
return value;
|
|
2259
|
+
}
|
|
2260
|
+
var BYTES_WHOLE = 0;
|
|
2261
|
+
var BYTES_PATCH = 1;
|
|
2019
2262
|
function warnNonFinite(path, value) {
|
|
2020
2263
|
console.warn(
|
|
2021
2264
|
`irtio: snapshot field ${path} held ${value}; restored as the field's default. The value predates the finiteness checks on write and encode.`
|
|
@@ -2054,6 +2297,8 @@ function readValue(r, desc, opts, path = "value") {
|
|
|
2054
2297
|
throw new Error(`unknown enum index ${i} (${desc.values.length} values)`);
|
|
2055
2298
|
return v;
|
|
2056
2299
|
}
|
|
2300
|
+
case "bytes":
|
|
2301
|
+
return copyBytes(r.bytes(desc.n));
|
|
2057
2302
|
case "list": {
|
|
2058
2303
|
const n = r.varint();
|
|
2059
2304
|
if (n > desc.max) throw new Error(`list exceeds max length ${desc.max} (${n})`);
|
|
@@ -2085,8 +2330,8 @@ function encodeFields(fields, value) {
|
|
|
2085
2330
|
writeRecord(w, fields, value, "fields");
|
|
2086
2331
|
return w.finish();
|
|
2087
2332
|
}
|
|
2088
|
-
function decodeFields(fields,
|
|
2089
|
-
const r =
|
|
2333
|
+
function decodeFields(fields, bytes2) {
|
|
2334
|
+
const r = bytes2 instanceof ByteReader ? bytes2 : new ByteReader(bytes2);
|
|
2090
2335
|
return readRecord(r, fields);
|
|
2091
2336
|
}
|
|
2092
2337
|
function entityOf(state, name) {
|
|
@@ -2137,8 +2382,8 @@ function encodeSnapshot(schema, state, header, options) {
|
|
|
2137
2382
|
writeSnapshot(w, schema, state, header.tick, options);
|
|
2138
2383
|
return w.finish();
|
|
2139
2384
|
}
|
|
2140
|
-
function decodeSnapshot(schema,
|
|
2141
|
-
const r = new ByteReader(
|
|
2385
|
+
function decodeSnapshot(schema, bytes2, options = {}) {
|
|
2386
|
+
const r = new ByteReader(bytes2);
|
|
2142
2387
|
const opts = { onNonFinite: options.onNonFinite ?? warnNonFinite };
|
|
2143
2388
|
const tick = r.u32();
|
|
2144
2389
|
const hash8 = r.bytes(8).slice();
|
|
@@ -2182,14 +2427,18 @@ function planCollection(c, state, dirty) {
|
|
|
2182
2427
|
return ops.length > 0 ? ops : void 0;
|
|
2183
2428
|
}
|
|
2184
2429
|
function maskBytesFor(indices, bits) {
|
|
2185
|
-
const
|
|
2186
|
-
for (const i of indices)
|
|
2187
|
-
return
|
|
2430
|
+
const bytes2 = new Uint8Array(bits + 7 >> 3);
|
|
2431
|
+
for (const i of indices) bytes2[i >> 3] |= 1 << (i & 7);
|
|
2432
|
+
return bytes2;
|
|
2188
2433
|
}
|
|
2189
2434
|
function sortedIndices(mask, limit) {
|
|
2190
2435
|
return [...mask.fields].filter((i) => i >= 0 && i < limit).sort((a, b) => a - b);
|
|
2191
2436
|
}
|
|
2192
|
-
function writeDirtyField(w, desc, v, nested, path) {
|
|
2437
|
+
function writeDirtyField(w, desc, v, nested, ranges, path) {
|
|
2438
|
+
if (desc.kind === "bytes") {
|
|
2439
|
+
writeDirtyBytes(w, desc, v, ranges, path);
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2193
2442
|
if (desc.kind !== "struct") {
|
|
2194
2443
|
writeValue(w, desc, v, path);
|
|
2195
2444
|
return;
|
|
@@ -2214,8 +2463,41 @@ function writeDirtyField(w, desc, v, nested, path) {
|
|
|
2214
2463
|
for (const i of indices) {
|
|
2215
2464
|
const name = names[i];
|
|
2216
2465
|
const fd = desc.fields[name];
|
|
2217
|
-
writeDirtyField(
|
|
2466
|
+
writeDirtyField(
|
|
2467
|
+
w,
|
|
2468
|
+
fd,
|
|
2469
|
+
o[name],
|
|
2470
|
+
nested?.nested.get(i),
|
|
2471
|
+
nested?.ranges?.get(i),
|
|
2472
|
+
`${path}.${name}`
|
|
2473
|
+
);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
function writeDirtyBytes(w, desc, v, ranges, path) {
|
|
2477
|
+
let value = v;
|
|
2478
|
+
if (value === void 0 && desc.hasDefault && desc.default !== void 0) value = desc.default;
|
|
2479
|
+
if (desc.opt) {
|
|
2480
|
+
if (value === void 0) {
|
|
2481
|
+
w.u8(0);
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
w.u8(1);
|
|
2485
|
+
} else if (value === void 0) {
|
|
2486
|
+
fail(path, "is required");
|
|
2487
|
+
}
|
|
2488
|
+
const b = checkBytes(value, desc.n, path);
|
|
2489
|
+
if (ranges && ranges.length > 0 && ranges.length <= MAX_BYTE_RANGES && rangesPatchBytes(ranges) < desc.n) {
|
|
2490
|
+
w.u8(BYTES_PATCH);
|
|
2491
|
+
w.varint(ranges.length);
|
|
2492
|
+
for (const r of ranges) {
|
|
2493
|
+
w.varint(r.start);
|
|
2494
|
+
w.varint(r.end - r.start);
|
|
2495
|
+
w.bytes(b.subarray(r.start, r.end));
|
|
2496
|
+
}
|
|
2497
|
+
return;
|
|
2218
2498
|
}
|
|
2499
|
+
w.u8(BYTES_WHOLE);
|
|
2500
|
+
w.bytes(b);
|
|
2219
2501
|
}
|
|
2220
2502
|
function writeDelta(w, schema, state, dirty, tick) {
|
|
2221
2503
|
w.u32(tick);
|
|
@@ -2258,6 +2540,7 @@ function writeDelta(w, schema, state, dirty, tick) {
|
|
|
2258
2540
|
f.type,
|
|
2259
2541
|
value[f.name],
|
|
2260
2542
|
op.mask.nested.get(i),
|
|
2543
|
+
op.mask.ranges?.get(i),
|
|
2261
2544
|
`${c.name}[${op.id}].${f.name}`
|
|
2262
2545
|
);
|
|
2263
2546
|
}
|
|
@@ -2278,6 +2561,7 @@ function readBit(mask, i) {
|
|
|
2278
2561
|
return ((mask[i >> 3] ?? 0) & 1 << (i & 7)) !== 0;
|
|
2279
2562
|
}
|
|
2280
2563
|
function readDirtyField(r, desc) {
|
|
2564
|
+
if (desc.kind === "bytes") return readDirtyBytes(r, desc);
|
|
2281
2565
|
if (desc.kind !== "struct") return { value: readValue(r, desc) };
|
|
2282
2566
|
if (desc.opt && r.u8() === 0) return { value: void 0 };
|
|
2283
2567
|
const names = structNames(desc);
|
|
@@ -2292,41 +2576,78 @@ function readDirtyField(r, desc) {
|
|
|
2292
2576
|
out[name] = got.value;
|
|
2293
2577
|
mask.fields.add(i);
|
|
2294
2578
|
if (got.nested) mask.nested.set(i, got.nested);
|
|
2579
|
+
if (got.ranges) (mask.ranges ??= /* @__PURE__ */ new Map()).set(i, got.ranges);
|
|
2295
2580
|
}
|
|
2296
2581
|
return { value: out, nested: mask };
|
|
2297
2582
|
}
|
|
2298
|
-
function
|
|
2299
|
-
|
|
2583
|
+
function readDirtyBytes(r, desc) {
|
|
2584
|
+
if (desc.opt && r.u8() === 0) return { value: void 0 };
|
|
2585
|
+
const tag = r.u8();
|
|
2586
|
+
if (tag === BYTES_WHOLE) return { value: copyBytes(r.bytes(desc.n)) };
|
|
2587
|
+
if (tag !== BYTES_PATCH) throw new Error(`unknown bytes tag ${tag}`);
|
|
2588
|
+
const count = r.varint();
|
|
2589
|
+
if (count < 1 || count > MAX_BYTE_RANGES) {
|
|
2590
|
+
throw new Error(`bytes patch has ${count} spans (1..${MAX_BYTE_RANGES} allowed)`);
|
|
2591
|
+
}
|
|
2592
|
+
const spans = [];
|
|
2593
|
+
const ranges = [];
|
|
2594
|
+
let floor = 0;
|
|
2595
|
+
for (let i = 0; i < count; i++) {
|
|
2596
|
+
const offset = r.varint();
|
|
2597
|
+
const length = r.varint();
|
|
2598
|
+
if (length < 1) throw new Error("bytes patch span has length 0");
|
|
2599
|
+
if (offset < floor || offset + length > desc.n) {
|
|
2600
|
+
throw new Error(
|
|
2601
|
+
`bytes patch span [${offset}, ${offset + length}) is out of order or outside bytes(${desc.n})`
|
|
2602
|
+
);
|
|
2603
|
+
}
|
|
2604
|
+
spans.push({ offset, bytes: copyBytes(r.bytes(length)) });
|
|
2605
|
+
ranges.push({ start: offset, end: offset + length });
|
|
2606
|
+
floor = offset + length;
|
|
2607
|
+
}
|
|
2608
|
+
const patch = { spans };
|
|
2609
|
+
return { value: patch, ranges };
|
|
2610
|
+
}
|
|
2611
|
+
function decodeDelta(schema, bytes2, options) {
|
|
2612
|
+
return decodeDeltaFrom(schema, new ByteReader(bytes2), options);
|
|
2300
2613
|
}
|
|
2301
|
-
function decodeDeltaFrom(schema, r) {
|
|
2614
|
+
function decodeDeltaFrom(schema, r, options = {}) {
|
|
2302
2615
|
const tick = r.u32();
|
|
2303
2616
|
const hash8 = r.bytes(8).slice();
|
|
2304
2617
|
const collections = [];
|
|
2305
2618
|
const count = r.varint();
|
|
2619
|
+
const known = schema.collections.length;
|
|
2620
|
+
if (count > known) throw new Error(`delta names ${count} collections; the schema has ${known}`);
|
|
2621
|
+
let total = 0;
|
|
2306
2622
|
for (let ci = 0; ci < count; ci++) {
|
|
2307
2623
|
const index = r.varint();
|
|
2308
2624
|
const c = schema.collections[index];
|
|
2309
2625
|
if (!c) throw new Error(`delta references unknown collection index ${index}`);
|
|
2310
2626
|
const opCount = r.varint();
|
|
2627
|
+
if (opCount > r.remaining)
|
|
2628
|
+
throw new Error(`delta claims ${opCount} ops in ${c.name} with ${r.remaining} bytes left`);
|
|
2629
|
+
total += opCount;
|
|
2630
|
+
if (options.maxOps !== void 0 && total > options.maxOps)
|
|
2631
|
+
throw new Error(`delta carries ${total} ops; the limit is ${options.maxOps}`);
|
|
2311
2632
|
const ops = [];
|
|
2312
2633
|
for (let oi = 0; oi < opCount; oi++) {
|
|
2313
2634
|
const op = r.u8();
|
|
2314
2635
|
if (op === OP_REMOVE) {
|
|
2315
|
-
ops.push({ op: "remove", id: r.str() });
|
|
2636
|
+
ops.push({ op: "remove", id: r.str(ID_MAX_BYTES) });
|
|
2316
2637
|
continue;
|
|
2317
2638
|
}
|
|
2318
2639
|
if (op === OP_ADD) {
|
|
2319
|
-
const id2 = r.str();
|
|
2320
|
-
const owner2 = r.str();
|
|
2640
|
+
const id2 = r.str(ID_MAX_BYTES);
|
|
2641
|
+
const owner2 = r.str(ID_MAX_BYTES);
|
|
2321
2642
|
ops.push({ op: "add", id: id2, owner: owner2, value: readRecord(r, c.fields) });
|
|
2322
2643
|
continue;
|
|
2323
2644
|
}
|
|
2324
2645
|
if (op !== OP_UPDATE) throw new Error(`unknown delta op byte ${op} in ${c.name}`);
|
|
2325
|
-
const id = r.str();
|
|
2646
|
+
const id = r.str(ID_MAX_BYTES);
|
|
2326
2647
|
const n = c.fields.length;
|
|
2327
2648
|
const maskBytes = r.bytes(n + 1 + 7 >> 3);
|
|
2328
2649
|
const ownerDirty = readBit(maskBytes, n);
|
|
2329
|
-
const owner = ownerDirty ? r.str() : void 0;
|
|
2650
|
+
const owner = ownerDirty ? r.str(ID_MAX_BYTES) : void 0;
|
|
2330
2651
|
const mask = createFieldMask();
|
|
2331
2652
|
const patch = {};
|
|
2332
2653
|
for (let i = 0; i < n; i++) {
|
|
@@ -2336,6 +2657,7 @@ function decodeDeltaFrom(schema, r) {
|
|
|
2336
2657
|
patch[f.name] = got.value;
|
|
2337
2658
|
mask.fields.add(i);
|
|
2338
2659
|
if (got.nested) mask.nested.set(i, got.nested);
|
|
2660
|
+
if (got.ranges) (mask.ranges ??= /* @__PURE__ */ new Map()).set(i, got.ranges);
|
|
2339
2661
|
}
|
|
2340
2662
|
ops.push(
|
|
2341
2663
|
owner === void 0 ? { op: "update", id, mask, patch } : { op: "update", id, owner, mask, patch }
|
|
@@ -2366,9 +2688,23 @@ function applyPatch(fieldTypes, target, patch, mask) {
|
|
|
2366
2688
|
continue;
|
|
2367
2689
|
}
|
|
2368
2690
|
}
|
|
2691
|
+
if (mask.ranges?.has(i) && f.type.kind === "bytes" && isBytesPatch(value)) {
|
|
2692
|
+
const current = target[f.name];
|
|
2693
|
+
if (current instanceof Uint8Array && current.length === f.type.n) {
|
|
2694
|
+
for (const span of value.spans) current.set(span.bytes, span.offset);
|
|
2695
|
+
continue;
|
|
2696
|
+
}
|
|
2697
|
+
const fresh = new Uint8Array(f.type.n);
|
|
2698
|
+
for (const span of value.spans) fresh.set(span.bytes, span.offset);
|
|
2699
|
+
target[f.name] = fresh;
|
|
2700
|
+
continue;
|
|
2701
|
+
}
|
|
2369
2702
|
target[f.name] = value;
|
|
2370
2703
|
}
|
|
2371
2704
|
}
|
|
2705
|
+
function isBytesPatch(v) {
|
|
2706
|
+
return typeof v === "object" && v !== null && Array.isArray(v.spans);
|
|
2707
|
+
}
|
|
2372
2708
|
function applyDelta(schema, state, delta) {
|
|
2373
2709
|
for (const dc of delta.collections) {
|
|
2374
2710
|
const c = schema.collection(dc.name);
|
|
@@ -2397,6 +2733,9 @@ function applyDelta(schema, state, delta) {
|
|
|
2397
2733
|
}
|
|
2398
2734
|
function valueEquals(a, b) {
|
|
2399
2735
|
if (Object.is(a, b)) return true;
|
|
2736
|
+
if (a instanceof Uint8Array || b instanceof Uint8Array) {
|
|
2737
|
+
return a instanceof Uint8Array && b instanceof Uint8Array && bytesEqual(a, b);
|
|
2738
|
+
}
|
|
2400
2739
|
if (Array.isArray(a) || Array.isArray(b)) {
|
|
2401
2740
|
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
2402
2741
|
for (let i = 0; i < a.length; i++) if (!valueEquals(a[i], b[i])) return false;
|
|
@@ -2422,6 +2761,14 @@ function diffField(desc, a, b, mask, idx) {
|
|
|
2422
2761
|
mask.nested.set(idx, nested);
|
|
2423
2762
|
return;
|
|
2424
2763
|
}
|
|
2764
|
+
if (desc.kind === "bytes" && a instanceof Uint8Array && b instanceof Uint8Array && a.length === desc.n && b.length === desc.n) {
|
|
2765
|
+
const found = diffByteRanges(a, b);
|
|
2766
|
+
if (found.length === 0) return;
|
|
2767
|
+
mask.fields.add(idx);
|
|
2768
|
+
const settled = settleRanges(found, desc.n);
|
|
2769
|
+
if (settled) (mask.ranges ??= /* @__PURE__ */ new Map()).set(idx, settled);
|
|
2770
|
+
return;
|
|
2771
|
+
}
|
|
2425
2772
|
if (!valueEquals(a, b)) mask.fields.add(idx);
|
|
2426
2773
|
}
|
|
2427
2774
|
function diffRecord(c, a, b, mask) {
|
|
@@ -2491,6 +2838,9 @@ function skipValue(r, desc) {
|
|
|
2491
2838
|
case "ref":
|
|
2492
2839
|
skip(r, r.varint());
|
|
2493
2840
|
return;
|
|
2841
|
+
case "bytes":
|
|
2842
|
+
skip(r, desc.n);
|
|
2843
|
+
return;
|
|
2494
2844
|
case "list": {
|
|
2495
2845
|
const n = r.varint();
|
|
2496
2846
|
if (n > desc.max) throw new Error(`list exceeds max length ${desc.max} (${n})`);
|
|
@@ -2507,6 +2857,33 @@ function readBit2(mask, i) {
|
|
|
2507
2857
|
return ((mask[i >> 3] ?? 0) & 1 << (i & 7)) !== 0;
|
|
2508
2858
|
}
|
|
2509
2859
|
function skipDirtyField(r, desc) {
|
|
2860
|
+
if (desc.kind === "bytes") {
|
|
2861
|
+
if (desc.opt && r.u8() === 0) return;
|
|
2862
|
+
const tag = r.u8();
|
|
2863
|
+
if (tag === 0) {
|
|
2864
|
+
skip(r, desc.n);
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
if (tag !== 1) throw new Error(`unknown bytes tag ${tag}`);
|
|
2868
|
+
const count = r.varint();
|
|
2869
|
+
if (count < 1 || count > MAX_BYTE_RANGES) {
|
|
2870
|
+
throw new Error(`bytes patch has ${count} spans (1..${MAX_BYTE_RANGES} allowed)`);
|
|
2871
|
+
}
|
|
2872
|
+
let floor = 0;
|
|
2873
|
+
for (let i = 0; i < count; i++) {
|
|
2874
|
+
const offset = r.varint();
|
|
2875
|
+
const length = r.varint();
|
|
2876
|
+
if (length < 1) throw new Error("bytes patch span has length 0");
|
|
2877
|
+
if (offset < floor || offset + length > desc.n) {
|
|
2878
|
+
throw new Error(
|
|
2879
|
+
`bytes patch span [${offset}, ${offset + length}) is out of order or outside bytes(${desc.n})`
|
|
2880
|
+
);
|
|
2881
|
+
}
|
|
2882
|
+
skip(r, length);
|
|
2883
|
+
floor = offset + length;
|
|
2884
|
+
}
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2510
2887
|
if (desc.kind !== "struct") {
|
|
2511
2888
|
skipValue(r, desc);
|
|
2512
2889
|
return;
|
|
@@ -2519,11 +2896,11 @@ function skipDirtyField(r, desc) {
|
|
|
2519
2896
|
skipDirtyField(r, desc.fields[names[i]]);
|
|
2520
2897
|
}
|
|
2521
2898
|
}
|
|
2522
|
-
function readerFor(
|
|
2523
|
-
return
|
|
2899
|
+
function readerFor(bytes2) {
|
|
2900
|
+
return bytes2 instanceof ByteReader ? bytes2 : new ByteReader(bytes2);
|
|
2524
2901
|
}
|
|
2525
|
-
function walkDelta(schema,
|
|
2526
|
-
const r = readerFor(
|
|
2902
|
+
function walkDelta(schema, bytes2, events) {
|
|
2903
|
+
const r = readerFor(bytes2);
|
|
2527
2904
|
const from = r.pos;
|
|
2528
2905
|
let at = r.pos;
|
|
2529
2906
|
skip(r, 12);
|
|
@@ -2570,8 +2947,8 @@ function walkDelta(schema, bytes, events) {
|
|
|
2570
2947
|
}
|
|
2571
2948
|
return r.pos - from;
|
|
2572
2949
|
}
|
|
2573
|
-
function walkSnapshot(schema,
|
|
2574
|
-
const r = readerFor(
|
|
2950
|
+
function walkSnapshot(schema, bytes2, events) {
|
|
2951
|
+
const r = readerFor(bytes2);
|
|
2575
2952
|
const from = r.pos;
|
|
2576
2953
|
skip(r, 12);
|
|
2577
2954
|
events.header(12);
|
|
@@ -2631,8 +3008,9 @@ function mark(root, path) {
|
|
|
2631
3008
|
}
|
|
2632
3009
|
function assertLive(root) {
|
|
2633
3010
|
if (!root.live) {
|
|
3011
|
+
const id = JSON.stringify(root.id);
|
|
2634
3012
|
throw new Error(
|
|
2635
|
-
`${root.collection}: instance ${
|
|
3013
|
+
root.reason === "replaced" ? `${root.collection}: instance ${id} was replaced (re-added) since this handle was taken; re-read it with .get(${id})` : `${root.collection}: instance ${id} was removed; its proxy is stale`
|
|
2636
3014
|
);
|
|
2637
3015
|
}
|
|
2638
3016
|
}
|
|
@@ -2653,6 +3031,7 @@ function objHandler(node) {
|
|
|
2653
3031
|
return structProxy(node, f, v);
|
|
2654
3032
|
}
|
|
2655
3033
|
if (f.type.kind === "list") return listProxy(node, f, v);
|
|
3034
|
+
if (f.type.kind === "bytes") return bytesProxy(node, f, v);
|
|
2656
3035
|
return v;
|
|
2657
3036
|
},
|
|
2658
3037
|
set(target, prop, value) {
|
|
@@ -2665,6 +3044,16 @@ function objHandler(node) {
|
|
|
2665
3044
|
const at = `${node.label}.${f.name}`;
|
|
2666
3045
|
const next = value === void 0 && f.type.opt ? void 0 : normalizeValue(f.type, value, at);
|
|
2667
3046
|
if (valueEquals(target[f.name], next)) return true;
|
|
3047
|
+
const current = target[f.name];
|
|
3048
|
+
if (f.type.kind === "bytes" && !node.whole && current instanceof Uint8Array && next instanceof Uint8Array) {
|
|
3049
|
+
const path = childPath(node.path, node.whole, f.index);
|
|
3050
|
+
const root = node.root;
|
|
3051
|
+
for (const r of diffByteRanges(current, next)) {
|
|
3052
|
+
markBytes(root.ctx.dirty, root.collection, root.id, path, r.start, r.end, f.type.n);
|
|
3053
|
+
}
|
|
3054
|
+
current.set(next);
|
|
3055
|
+
return true;
|
|
3056
|
+
}
|
|
2668
3057
|
target[f.name] = next;
|
|
2669
3058
|
mark(node.root, childPath(node.path, node.whole, f.index));
|
|
2670
3059
|
return true;
|
|
@@ -2832,6 +3221,177 @@ function listElemProxy(parent, prop, value) {
|
|
|
2832
3221
|
label: `${parent.label}[${prop}]`
|
|
2833
3222
|
});
|
|
2834
3223
|
}
|
|
3224
|
+
var BYTES_MUTATORS = /* @__PURE__ */ new Set(["set", "fill", "copyWithin", "sort", "reverse"]);
|
|
3225
|
+
function isNumericProp(prop) {
|
|
3226
|
+
return prop === "-0" || String(Number(prop)) === prop;
|
|
3227
|
+
}
|
|
3228
|
+
function relIndex(x, len, dflt) {
|
|
3229
|
+
if (x === void 0) return dflt;
|
|
3230
|
+
const v = Math.trunc(Number(x)) || 0;
|
|
3231
|
+
return v < 0 ? Math.max(len + v, 0) : Math.min(v, len);
|
|
3232
|
+
}
|
|
3233
|
+
function checkByte(label, i, v) {
|
|
3234
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < 0 || v > 255) {
|
|
3235
|
+
throw new Error(`${label}[${i}]: must be an integer 0..255 (got ${String(v)})`);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
function reportDiff(t, before, start, hooks) {
|
|
3239
|
+
let first = -1;
|
|
3240
|
+
let last = -1;
|
|
3241
|
+
for (let i = 0; i < before.length; i++) {
|
|
3242
|
+
if (before[i] !== t[start + i]) {
|
|
3243
|
+
if (first < 0) first = i;
|
|
3244
|
+
last = i;
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
if (first >= 0) hooks.changed(start + first, start + last + 1);
|
|
3248
|
+
}
|
|
3249
|
+
function bytesView(target, label, hooks) {
|
|
3250
|
+
const n = target.length;
|
|
3251
|
+
const bound = /* @__PURE__ */ new Map();
|
|
3252
|
+
const mutator = (name) => {
|
|
3253
|
+
const t = target;
|
|
3254
|
+
switch (name) {
|
|
3255
|
+
case "set":
|
|
3256
|
+
return (src, offset) => {
|
|
3257
|
+
if (!hooks.begin("set")) return void 0;
|
|
3258
|
+
const source = src;
|
|
3259
|
+
const len = Number(source?.length ?? 0) || 0;
|
|
3260
|
+
const off = Math.trunc(Number(offset ?? 0)) || 0;
|
|
3261
|
+
if (off < 0 || off + len > n) {
|
|
3262
|
+
throw new Error(
|
|
3263
|
+
`${label}.set: a source of length ${len} at offset ${off} overflows bytes(${n})`
|
|
3264
|
+
);
|
|
3265
|
+
}
|
|
3266
|
+
if (!(src instanceof Uint8Array) && !(src instanceof Uint8ClampedArray)) {
|
|
3267
|
+
for (let i = 0; i < len; i++) checkByte(label, off + i, source[i]);
|
|
3268
|
+
}
|
|
3269
|
+
const before = t.slice(off, off + len);
|
|
3270
|
+
try {
|
|
3271
|
+
t.set(src, off);
|
|
3272
|
+
} catch (e) {
|
|
3273
|
+
if (e instanceof RangeError) {
|
|
3274
|
+
throw new Error(`${label}.set: ${e.message} (bytes(${n}))`);
|
|
3275
|
+
}
|
|
3276
|
+
throw e;
|
|
3277
|
+
}
|
|
3278
|
+
reportDiff(t, before, off, hooks);
|
|
3279
|
+
return void 0;
|
|
3280
|
+
};
|
|
3281
|
+
case "fill":
|
|
3282
|
+
return (v, start, end) => {
|
|
3283
|
+
if (!hooks.begin("fill")) return proxy;
|
|
3284
|
+
checkByte(label, "fill", v);
|
|
3285
|
+
const s = relIndex(start, n, 0);
|
|
3286
|
+
const e = relIndex(end, n, n);
|
|
3287
|
+
if (e > s) {
|
|
3288
|
+
const before = t.slice(s, e);
|
|
3289
|
+
t.fill(v, s, e);
|
|
3290
|
+
reportDiff(t, before, s, hooks);
|
|
3291
|
+
}
|
|
3292
|
+
return proxy;
|
|
3293
|
+
};
|
|
3294
|
+
case "copyWithin":
|
|
3295
|
+
return (to, start, end) => {
|
|
3296
|
+
if (!hooks.begin("copyWithin")) return proxy;
|
|
3297
|
+
const dst = relIndex(to, n, 0);
|
|
3298
|
+
const from = relIndex(start, n, 0);
|
|
3299
|
+
const fin = relIndex(end, n, n);
|
|
3300
|
+
const count = Math.min(fin - from, n - dst);
|
|
3301
|
+
if (count > 0) {
|
|
3302
|
+
const before = t.slice(dst, dst + count);
|
|
3303
|
+
t.copyWithin(dst, from, fin);
|
|
3304
|
+
reportDiff(t, before, dst, hooks);
|
|
3305
|
+
}
|
|
3306
|
+
return proxy;
|
|
3307
|
+
};
|
|
3308
|
+
default:
|
|
3309
|
+
return (...args) => {
|
|
3310
|
+
if (!hooks.begin(name)) return proxy;
|
|
3311
|
+
const before = t.slice();
|
|
3312
|
+
Uint8Array.prototype[name].apply(t, args);
|
|
3313
|
+
for (let i = 0; i < n; i++) {
|
|
3314
|
+
if (before[i] !== t[i]) {
|
|
3315
|
+
hooks.changed(0, n);
|
|
3316
|
+
break;
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
return proxy;
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
};
|
|
3323
|
+
const selfValue = () => proxy;
|
|
3324
|
+
const proxy = new Proxy(target, {
|
|
3325
|
+
get(t, prop) {
|
|
3326
|
+
if (typeof prop === "string") {
|
|
3327
|
+
if (isIndexProp(prop)) return t[prop];
|
|
3328
|
+
if (BYTES_MUTATORS.has(prop)) {
|
|
3329
|
+
let fn = bound.get(prop);
|
|
3330
|
+
if (!fn) {
|
|
3331
|
+
fn = mutator(prop);
|
|
3332
|
+
bound.set(prop, fn);
|
|
3333
|
+
}
|
|
3334
|
+
return fn;
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
if (prop === "valueOf") return selfValue;
|
|
3338
|
+
const v = Reflect.get(t, prop, t);
|
|
3339
|
+
if (typeof v === "function" && prop !== "constructor") {
|
|
3340
|
+
let fn = bound.get(prop);
|
|
3341
|
+
if (!fn) {
|
|
3342
|
+
fn = v.bind(t);
|
|
3343
|
+
bound.set(prop, fn);
|
|
3344
|
+
}
|
|
3345
|
+
return fn;
|
|
3346
|
+
}
|
|
3347
|
+
return v;
|
|
3348
|
+
},
|
|
3349
|
+
set(t, prop, value) {
|
|
3350
|
+
if (typeof prop === "string" && isNumericProp(prop)) {
|
|
3351
|
+
if (!hooks.begin(prop)) return true;
|
|
3352
|
+
const i = Number(prop);
|
|
3353
|
+
if (!Number.isInteger(i) || i < 0 || i >= n || prop === "-0") {
|
|
3354
|
+
throw new Error(`${label}[${prop}]: index out of range for bytes(${n})`);
|
|
3355
|
+
}
|
|
3356
|
+
checkByte(label, i, value);
|
|
3357
|
+
if (t[i] !== value) {
|
|
3358
|
+
t[i] = value;
|
|
3359
|
+
hooks.changed(i, i + 1);
|
|
3360
|
+
}
|
|
3361
|
+
return true;
|
|
3362
|
+
}
|
|
3363
|
+
if (!hooks.begin(String(prop))) return true;
|
|
3364
|
+
throw new Error(`${label}: cannot set ${String(prop)} on a bytes(${n}) field`);
|
|
3365
|
+
},
|
|
3366
|
+
deleteProperty(_t, prop) {
|
|
3367
|
+
if (!hooks.begin(String(prop))) return true;
|
|
3368
|
+
throw new Error(`${label}: cannot delete ${String(prop)} from a bytes(${n}) field`);
|
|
3369
|
+
},
|
|
3370
|
+
defineProperty(_t, prop) {
|
|
3371
|
+
if (!hooks.begin(String(prop))) return true;
|
|
3372
|
+
throw new Error(`${label}: cannot defineProperty ${String(prop)} on a bytes(${n}) field`);
|
|
3373
|
+
}
|
|
3374
|
+
});
|
|
3375
|
+
return proxy;
|
|
3376
|
+
}
|
|
3377
|
+
function bytesProxy(parent, f, value) {
|
|
3378
|
+
const root = parent.root;
|
|
3379
|
+
const cached = root.ctx.proxies.get(value);
|
|
3380
|
+
if (cached) return cached;
|
|
3381
|
+
const path = childPath(parent.path, parent.whole, f.index);
|
|
3382
|
+
const n = f.type.n;
|
|
3383
|
+
const p = bytesView(value, `${parent.label}.${f.name}`, {
|
|
3384
|
+
begin() {
|
|
3385
|
+
assertLive(root);
|
|
3386
|
+
return true;
|
|
3387
|
+
},
|
|
3388
|
+
// `whole` (a bytes field inside a list element) cannot happen today — `list()` refuses bytes
|
|
3389
|
+
// items — but if it ever can, the list field is what has to be marked.
|
|
3390
|
+
changed: parent.whole ? () => mark(root, path) : (start, end) => markBytes(root.ctx.dirty, root.collection, root.id, path, start, end, n)
|
|
3391
|
+
});
|
|
3392
|
+
root.ctx.proxies.set(value, p);
|
|
3393
|
+
return p;
|
|
3394
|
+
}
|
|
2835
3395
|
var TrackedCollection = class {
|
|
2836
3396
|
constructor(ctx, desc, inner) {
|
|
2837
3397
|
this.ctx = ctx;
|
|
@@ -2842,10 +3402,11 @@ var TrackedCollection = class {
|
|
|
2842
3402
|
desc;
|
|
2843
3403
|
inner;
|
|
2844
3404
|
roots = /* @__PURE__ */ new Map();
|
|
2845
|
-
invalidate(id) {
|
|
3405
|
+
invalidate(id, reason) {
|
|
2846
3406
|
const root = this.roots.get(id);
|
|
2847
3407
|
if (root) {
|
|
2848
3408
|
root.live = false;
|
|
3409
|
+
root.reason = reason;
|
|
2849
3410
|
this.roots.delete(id);
|
|
2850
3411
|
}
|
|
2851
3412
|
}
|
|
@@ -2854,7 +3415,10 @@ var TrackedCollection = class {
|
|
|
2854
3415
|
if (cached) return cached;
|
|
2855
3416
|
let root = this.roots.get(id);
|
|
2856
3417
|
if (!root || root.target !== value) {
|
|
2857
|
-
if (root)
|
|
3418
|
+
if (root) {
|
|
3419
|
+
root.live = false;
|
|
3420
|
+
root.reason = "replaced";
|
|
3421
|
+
}
|
|
2858
3422
|
root = { ctx: this.ctx, collection: this.desc.name, id, target: value, live: true };
|
|
2859
3423
|
this.roots.set(id, root);
|
|
2860
3424
|
}
|
|
@@ -2868,7 +3432,7 @@ var TrackedCollection = class {
|
|
|
2868
3432
|
});
|
|
2869
3433
|
}
|
|
2870
3434
|
add(id, values, options) {
|
|
2871
|
-
this.invalidate(id);
|
|
3435
|
+
this.invalidate(id, this.inner.has(id) ? "replaced" : "removed");
|
|
2872
3436
|
const value = this.inner.add(id, values, options);
|
|
2873
3437
|
markAdd(this.ctx.dirty, this.desc.name, id);
|
|
2874
3438
|
return this.proxyFor(id, value);
|
|
@@ -2876,7 +3440,7 @@ var TrackedCollection = class {
|
|
|
2876
3440
|
remove(id) {
|
|
2877
3441
|
const had = this.inner.remove(id);
|
|
2878
3442
|
if (had) {
|
|
2879
|
-
this.invalidate(id);
|
|
3443
|
+
this.invalidate(id, "removed");
|
|
2880
3444
|
markRemove(this.ctx.dirty, this.desc.name, id);
|
|
2881
3445
|
}
|
|
2882
3446
|
return had;
|
|
@@ -3001,6 +3565,18 @@ function frozenProxy(value, ownershipHint) {
|
|
|
3001
3565
|
function freeze(target, hint, prefix, warned, cache) {
|
|
3002
3566
|
const cached = cache.get(target);
|
|
3003
3567
|
if (cached) return cached;
|
|
3568
|
+
if (target instanceof Uint8Array) {
|
|
3569
|
+
const view = bytesView(target, prefix, {
|
|
3570
|
+
begin(member) {
|
|
3571
|
+
warnFrozen(hint, prefix, member, warned);
|
|
3572
|
+
return false;
|
|
3573
|
+
},
|
|
3574
|
+
changed() {
|
|
3575
|
+
}
|
|
3576
|
+
});
|
|
3577
|
+
cache.set(target, view);
|
|
3578
|
+
return view;
|
|
3579
|
+
}
|
|
3004
3580
|
const p = new Proxy(target, {
|
|
3005
3581
|
get(t, prop, receiver) {
|
|
3006
3582
|
const v = Reflect.get(t, prop, receiver);
|
|
@@ -3035,17 +3611,34 @@ function warnFrozen(hint, prefix, prop, warned) {
|
|
|
3035
3611
|
// src/diff.ts
|
|
3036
3612
|
function diffSchemas(oldSchema, newSchema) {
|
|
3037
3613
|
const changes = [];
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
changes
|
|
3614
|
+
const section = (scope, diff) => {
|
|
3615
|
+
const out = [];
|
|
3616
|
+
diff(out);
|
|
3617
|
+
for (const c of out) changes.push({ ...c, scope });
|
|
3618
|
+
};
|
|
3619
|
+
section(
|
|
3620
|
+
"collection",
|
|
3621
|
+
(out) => diffCollections(
|
|
3622
|
+
oldSchema.collections,
|
|
3623
|
+
newSchema.collections,
|
|
3624
|
+
out
|
|
3625
|
+
)
|
|
3042
3626
|
);
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3627
|
+
section(
|
|
3628
|
+
"rpc",
|
|
3629
|
+
(out) => diffRpcs(oldSchema.rpcs, newSchema.rpcs, out)
|
|
3630
|
+
);
|
|
3631
|
+
section(
|
|
3632
|
+
"roles",
|
|
3633
|
+
(out) => diffRoles(oldSchema.roles, newSchema.roles, out)
|
|
3634
|
+
);
|
|
3635
|
+
section(
|
|
3636
|
+
"message",
|
|
3637
|
+
(out) => diffMessages(
|
|
3638
|
+
oldSchema.messages ?? [],
|
|
3639
|
+
newSchema.messages ?? [],
|
|
3640
|
+
out
|
|
3641
|
+
)
|
|
3049
3642
|
);
|
|
3050
3643
|
changes.sort(
|
|
3051
3644
|
(a, b) => a.path === b.path ? a.code.localeCompare(b.code) : a.path.localeCompare(b.path)
|
|
@@ -3054,9 +3647,7 @@ function diffSchemas(oldSchema, newSchema) {
|
|
|
3054
3647
|
}
|
|
3055
3648
|
function breakingIsWireOnly(changes) {
|
|
3056
3649
|
const breaking = changes.filter((c) => c.kind === "breaking");
|
|
3057
|
-
return breaking.length > 0 && breaking.every(
|
|
3058
|
-
(c) => c.path === "rpc" || c.path.startsWith("rpc.") || c.path.startsWith("message.")
|
|
3059
|
-
);
|
|
3650
|
+
return breaking.length > 0 && breaking.every((c) => c.scope === "rpc" || c.scope === "message");
|
|
3060
3651
|
}
|
|
3061
3652
|
function diffCollections(oldCollections, newCollections, changes) {
|
|
3062
3653
|
const oldByName = new Map(oldCollections.map((c) => [c.name, c]));
|
|
@@ -3250,6 +3841,24 @@ function diffRoles(oldRoles, newRoles, changes) {
|
|
|
3250
3841
|
}
|
|
3251
3842
|
}
|
|
3252
3843
|
}
|
|
3844
|
+
function defaultHint(t) {
|
|
3845
|
+
switch (t.kind) {
|
|
3846
|
+
case "bool":
|
|
3847
|
+
return "add `.default(false)`, or make it .opt";
|
|
3848
|
+
case "str":
|
|
3849
|
+
return "add `.default('')`, or make it .opt";
|
|
3850
|
+
case "enum":
|
|
3851
|
+
return `add \`.default(${JSON.stringify(t.values[0])})\`, or make it .opt`;
|
|
3852
|
+
case "list":
|
|
3853
|
+
return "add `.default([])`, or make it .opt";
|
|
3854
|
+
case "struct":
|
|
3855
|
+
return "make it .opt, or add `.default({ ... })` with every field filled";
|
|
3856
|
+
case "ref":
|
|
3857
|
+
return "make it .opt (a ref names an instance; there is no default worth inventing)";
|
|
3858
|
+
default:
|
|
3859
|
+
return `add \`.default(0)\` to the ${t.kind}, or make it .opt`;
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3253
3862
|
function diffFields(oldFields, newFields, basePath, changes) {
|
|
3254
3863
|
const oldByName = new Map(oldFields.map((f) => [f.name, f]));
|
|
3255
3864
|
const newByName = new Map(newFields.map((f) => [f.name, f]));
|
|
@@ -3270,7 +3879,7 @@ function diffFields(oldFields, newFields, basePath, changes) {
|
|
|
3270
3879
|
kind: additive ? "additive" : "breaking",
|
|
3271
3880
|
code: "field.added",
|
|
3272
3881
|
path: `${basePath}.${name}`,
|
|
3273
|
-
message: additive ? `${basePath}.${name}: new field added (has a default/opt, safe for existing snapshots)` : `${basePath}.${name}: new required field without a default (breaking) \u2014
|
|
3882
|
+
message: additive ? `${basePath}.${name}: new field added (has a default/opt, safe for existing snapshots)` : `${basePath}.${name}: new required field without a default (breaking) \u2014 ${defaultHint(f.type)}`
|
|
3274
3883
|
});
|
|
3275
3884
|
}
|
|
3276
3885
|
if (removedNames.length === 1 && addedNames.length === 1) {
|
|
@@ -3347,6 +3956,18 @@ function diffType(oldT, newT, path, changes) {
|
|
|
3347
3956
|
case "enum":
|
|
3348
3957
|
diffEnum(oldT.values, newT.values, path, changes);
|
|
3349
3958
|
break;
|
|
3959
|
+
case "bytes": {
|
|
3960
|
+
const newN = newT.n;
|
|
3961
|
+
if (oldT.n !== newN) {
|
|
3962
|
+
changes.push({
|
|
3963
|
+
kind: "breaking",
|
|
3964
|
+
code: "field.bytes_length_changed",
|
|
3965
|
+
path,
|
|
3966
|
+
message: `${path}: bytes length changed from ${oldT.n} to ${newN} \u2014 the length is implied by the schema, so every stored snapshot would be misread (breaking)`
|
|
3967
|
+
});
|
|
3968
|
+
}
|
|
3969
|
+
break;
|
|
3970
|
+
}
|
|
3350
3971
|
case "list": {
|
|
3351
3972
|
const newList = newT;
|
|
3352
3973
|
diffMax(oldT.max, newList.max, path, changes);
|
|
@@ -3413,7 +4034,7 @@ function diffDefault(oldT, newT, path, changes) {
|
|
|
3413
4034
|
path,
|
|
3414
4035
|
message: `${path}: .default(...) removed; existing snapshots already have values, so this is safe`
|
|
3415
4036
|
});
|
|
3416
|
-
} else if (oldT.hasDefault && newT.hasDefault &&
|
|
4037
|
+
} else if (oldT.hasDefault && newT.hasDefault && !defaultsEqual(oldT, newT)) {
|
|
3417
4038
|
changes.push({
|
|
3418
4039
|
kind: "additive",
|
|
3419
4040
|
code: "field.default_changed",
|
|
@@ -3422,6 +4043,15 @@ function diffDefault(oldT, newT, path, changes) {
|
|
|
3422
4043
|
});
|
|
3423
4044
|
}
|
|
3424
4045
|
}
|
|
4046
|
+
function defaultsEqual(oldT, newT) {
|
|
4047
|
+
if (oldT.default instanceof Uint8Array && newT.default instanceof Uint8Array) {
|
|
4048
|
+
return bytesEqual(oldT.default, newT.default);
|
|
4049
|
+
}
|
|
4050
|
+
if (containsBytes(oldT) || containsBytes(newT)) {
|
|
4051
|
+
return stableStringify(jsonDefault(oldT, oldT.default)) === stableStringify(jsonDefault(newT, newT.default));
|
|
4052
|
+
}
|
|
4053
|
+
return stableStringify(oldT.default) === stableStringify(newT.default);
|
|
4054
|
+
}
|
|
3425
4055
|
function structFieldDescs(fields) {
|
|
3426
4056
|
return Object.entries(fields).map(([name, type], index) => ({ name, index, type }));
|
|
3427
4057
|
}
|
|
@@ -3448,6 +4078,9 @@ function typeSignature(d) {
|
|
|
3448
4078
|
case "ref":
|
|
3449
4079
|
o.entity = d.entity;
|
|
3450
4080
|
break;
|
|
4081
|
+
case "bytes":
|
|
4082
|
+
o.n = d.n;
|
|
4083
|
+
break;
|
|
3451
4084
|
case "list":
|
|
3452
4085
|
o.max = d.max;
|
|
3453
4086
|
o.item = typeSignature(d.item);
|
|
@@ -3461,11 +4094,13 @@ function typeSignature(d) {
|
|
|
3461
4094
|
return stableStringify(o);
|
|
3462
4095
|
}
|
|
3463
4096
|
export {
|
|
4097
|
+
BYTES_MAX,
|
|
3464
4098
|
ByteReader,
|
|
3465
4099
|
ByteWriter,
|
|
3466
4100
|
CANONICAL_VERSION,
|
|
3467
4101
|
EntityCollection,
|
|
3468
4102
|
ID_MAX_BYTES,
|
|
4103
|
+
MAX_BYTE_RANGES,
|
|
3469
4104
|
MAX_MESSAGES,
|
|
3470
4105
|
MAX_USER_RPCS,
|
|
3471
4106
|
PERSIST_BLOB_BYTES,
|
|
@@ -3478,9 +4113,12 @@ export {
|
|
|
3478
4113
|
angleFrom2d,
|
|
3479
4114
|
applyChannel2d,
|
|
3480
4115
|
applyDelta,
|
|
4116
|
+
base64ToBytes,
|
|
3481
4117
|
bool,
|
|
3482
4118
|
breakingIsWireOnly,
|
|
4119
|
+
bytes,
|
|
3483
4120
|
bytesEqual,
|
|
4121
|
+
bytesToBase64,
|
|
3484
4122
|
canonicalOwnership,
|
|
3485
4123
|
canonicalPhysics,
|
|
3486
4124
|
canonicalType,
|
|
@@ -3491,6 +4129,8 @@ export {
|
|
|
3491
4129
|
compileOwnership,
|
|
3492
4130
|
compilePhysics,
|
|
3493
4131
|
computeDirty,
|
|
4132
|
+
containsBytes,
|
|
4133
|
+
copyBytes,
|
|
3494
4134
|
createDirtySet,
|
|
3495
4135
|
createFieldMask,
|
|
3496
4136
|
createState,
|
|
@@ -3502,6 +4142,7 @@ export {
|
|
|
3502
4142
|
defaultValue,
|
|
3503
4143
|
defineSchema,
|
|
3504
4144
|
describeType,
|
|
4145
|
+
diffByteRanges,
|
|
3505
4146
|
diffSchemas,
|
|
3506
4147
|
encodeDelta,
|
|
3507
4148
|
encodeFields,
|
|
@@ -3518,6 +4159,8 @@ export {
|
|
|
3518
4159
|
isWhole,
|
|
3519
4160
|
list,
|
|
3520
4161
|
markAdd,
|
|
4162
|
+
markBytes,
|
|
4163
|
+
markBytesPath,
|
|
3521
4164
|
markField,
|
|
3522
4165
|
markOwner,
|
|
3523
4166
|
markPath,
|
|
@@ -3530,11 +4173,13 @@ export {
|
|
|
3530
4173
|
ownershipFromCanonical,
|
|
3531
4174
|
parseHold,
|
|
3532
4175
|
physicsFromCanonical,
|
|
4176
|
+
rangesPatchBytes,
|
|
3533
4177
|
readValue,
|
|
3534
4178
|
reconcileCollection,
|
|
3535
4179
|
ref,
|
|
3536
4180
|
schemaFromCanonical,
|
|
3537
4181
|
server,
|
|
4182
|
+
settleRanges,
|
|
3538
4183
|
sha256,
|
|
3539
4184
|
sha256Hex,
|
|
3540
4185
|
singleton,
|
|
@@ -3546,6 +4191,7 @@ export {
|
|
|
3546
4191
|
u16,
|
|
3547
4192
|
u32,
|
|
3548
4193
|
u8,
|
|
4194
|
+
unionRanges,
|
|
3549
4195
|
utf8Length,
|
|
3550
4196
|
validateForDeploy,
|
|
3551
4197
|
validateValue,
|