@irtio/schema 0.5.1 → 0.6.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 +94 -1
- package/dist/index.js +220 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -159,6 +159,46 @@ declare function compilePhysics(at: string, kind: 'entity' | 'singleton', fields
|
|
|
159
159
|
declare function canonicalPhysics(p: PhysicsDesc): Record<string, unknown>;
|
|
160
160
|
/** Reads a canonical physics fragment back (`schemaFromCanonical`). */
|
|
161
161
|
declare function physicsFromCanonical(at: string, raw: unknown): EntityPhysics;
|
|
162
|
+
/**
|
|
163
|
+
* One planar body's state, in the vocabulary a 2D engine speaks. This is the only place the
|
|
164
|
+
* mapping from two dimensions onto the thirteen 3D channels is written down, and it lives in the
|
|
165
|
+
* schema package precisely because two sides have to agree about it: the room's server-side world
|
|
166
|
+
* writes through it, and anything reading those channels back (a renderer deriving the angle, a
|
|
167
|
+
* client-side world) reverses it. A second copy of these six lines somewhere else would be a
|
|
168
|
+
* silent way for the two to disagree by a sign.
|
|
169
|
+
*
|
|
170
|
+
* `angle` rides the quaternion about Z, which is what a rotation in the xy plane *is*:
|
|
171
|
+
* `qz = sin(a/2)`, `qw = cos(a/2)`, and back out with `2 * atan2(qz, qw)`. Channels a plane has no
|
|
172
|
+
* use for (`z`, `qx`, `qy`, `vz`, `wx`, `wy`) read as constant zero rather than being omitted, so a
|
|
173
|
+
* schema written for one engine still decodes under the other.
|
|
174
|
+
*/
|
|
175
|
+
interface Body2dState {
|
|
176
|
+
readonly x: number;
|
|
177
|
+
readonly y: number;
|
|
178
|
+
/** Radians, counter-clockwise. */
|
|
179
|
+
readonly angle: number;
|
|
180
|
+
readonly vx: number;
|
|
181
|
+
readonly vy: number;
|
|
182
|
+
/** Radians per second about Z. */
|
|
183
|
+
readonly angularVelocity: number;
|
|
184
|
+
}
|
|
185
|
+
/** The value a planar body puts on one 3D channel. */
|
|
186
|
+
declare function channelOf2d(channel: PhysicsBodyChannel, b: Body2dState): number;
|
|
187
|
+
/** Reads the angle back out of the two quaternion channels a plane uses. */
|
|
188
|
+
declare function angleFrom2d(qz: number, qw: number): number;
|
|
189
|
+
/**
|
|
190
|
+
* The reverse of {@link channelOf2d}: folds one channel's value into a mutable planar state.
|
|
191
|
+
* `qz`/`qw` are accumulated and the angle recomputed, so either order of arrival works.
|
|
192
|
+
*/
|
|
193
|
+
declare function applyChannel2d(channel: PhysicsBodyChannel, value: number, target: {
|
|
194
|
+
x: number;
|
|
195
|
+
y: number;
|
|
196
|
+
qz: number;
|
|
197
|
+
qw: number;
|
|
198
|
+
vx: number;
|
|
199
|
+
vy: number;
|
|
200
|
+
wz: number;
|
|
201
|
+
}): void;
|
|
162
202
|
|
|
163
203
|
/**
|
|
164
204
|
* `entity`, `singleton`, `server`, `client`, `defineSchema`, and the compiled schema
|
|
@@ -774,6 +814,59 @@ declare function applyDelta(schema: AnySchema, state: PlainState, delta: Delta):
|
|
|
774
814
|
/** Field-level diff between two plain states: what would have to be sent to turn `from` into `to`. */
|
|
775
815
|
declare function computeDirty(schema: AnySchema, from: PlainState, to: PlainState): DirtySet;
|
|
776
816
|
|
|
817
|
+
/**
|
|
818
|
+
* The measurement walker: a reader's-eye pass over *encoded* delta and snapshot bytes that
|
|
819
|
+
* reports where each byte went, keyed by collection and field.
|
|
820
|
+
*
|
|
821
|
+
* This is deliberately a second reader of the wire format rather than an instrumented writer.
|
|
822
|
+
* The encoders and `ValueSink` are untouched, so the hot path stays byte-identical and
|
|
823
|
+
* cost-identical whether or not anything is profiling; and because the walker reads exactly what
|
|
824
|
+
* `decodeDeltaFrom`/`decodeSnapshot` read, it works on the server after an encode and on the
|
|
825
|
+
* client before a decode, from the same code.
|
|
826
|
+
*
|
|
827
|
+
* The copy is kept honest by conservation: the spans it reports partition the bytes it consumed,
|
|
828
|
+
* with nothing counted twice and nothing left over (`walk.test.ts` proves it against
|
|
829
|
+
* `encodeDelta(...).length` over random schemas). If the format changes here without changing
|
|
830
|
+
* `codec.ts`, or the other way round, that property test fails.
|
|
831
|
+
*
|
|
832
|
+
* Nothing here allocates a decoded value: it skips values by width, so a profiled room pays for
|
|
833
|
+
* a second pass over the bytes and not for a second decode.
|
|
834
|
+
*/
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Where the bytes went. Every callback reports a byte count, and for one walk the counts sum to
|
|
838
|
+
* the number of bytes the walk consumed.
|
|
839
|
+
*/
|
|
840
|
+
interface WalkEvents {
|
|
841
|
+
/**
|
|
842
|
+
* Framing that belongs to no single field: `tick`, `hash8`, the dirty-collection count, and
|
|
843
|
+
* each collection's index and op count (snapshot: the header and each entity count).
|
|
844
|
+
*/
|
|
845
|
+
header(bytes: number): void;
|
|
846
|
+
/**
|
|
847
|
+
* One op's own framing: the tag byte, the id string, the owner string (add), the dirty mask
|
|
848
|
+
* and the owner string when the owner bit is set (update). Always called before that op's
|
|
849
|
+
* fields, so a consumer can route a whole op somewhere else by latching on this call.
|
|
850
|
+
*
|
|
851
|
+
* A snapshot's records report as `add` ops whose framing is the id and owner strings; a
|
|
852
|
+
* snapshot singleton has no op framing at all, only fields.
|
|
853
|
+
*/
|
|
854
|
+
op(collection: CollectionDesc, kind: 'add' | 'update' | 'remove', id: string, bytes: number): void;
|
|
855
|
+
/** One field value, nested struct masks and presence bytes included. */
|
|
856
|
+
field(collection: CollectionDesc, field: FieldDesc, bytes: number): void;
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Walks one delta body, reporting spans through `events`. Accepts raw bytes or a positioned
|
|
860
|
+
* reader, which it leaves at the first byte after the body exactly as `decodeDeltaFrom` does
|
|
861
|
+
* (the `CORRECT` frame's tick suffix is the caller's). Returns the number of bytes consumed.
|
|
862
|
+
*/
|
|
863
|
+
declare function walkDelta(schema: AnySchema, bytes: Uint8Array | ByteReader, events: WalkEvents): number;
|
|
864
|
+
/**
|
|
865
|
+
* Walks one snapshot body, reporting spans through `events`. Entity records report as `add` ops;
|
|
866
|
+
* a singleton reports only its fields. Returns the number of bytes consumed.
|
|
867
|
+
*/
|
|
868
|
+
declare function walkSnapshot(schema: AnySchema, bytes: Uint8Array | ByteReader, events: WalkEvents): number;
|
|
869
|
+
|
|
777
870
|
/**
|
|
778
871
|
* `track()` — a proxy tree over a plain state that records every write into a `DirtySet`.
|
|
779
872
|
*
|
|
@@ -824,4 +917,4 @@ interface SchemaChange {
|
|
|
824
917
|
/** Compares two compiled schemas and returns every classified change, sorted by path then code. */
|
|
825
918
|
declare function diffSchemas(oldSchema: AnySchema, newSchema: AnySchema): SchemaChange[];
|
|
826
919
|
|
|
827
|
-
export { type AddOptions, type AnyDef, type AnyRpc, type AnySchema, type AnyType, type BodyFieldsOf, type BroadcastProxy, ByteReader, ByteWriter, CANONICAL_VERSION, type ClientCallProxy, type ClientImplementations, type ClientRpcs, type ClientState, type Collection, type CollectionDesc, type CollectionDirty, type DecodedSnapshot, type DeepReadonly, type DeepWritable, type DefMap, type Delta, type DeltaCollection, type DeltaOp, type DirtySet, EntityCollection, type EntityDef, type EntityOptions, type EntityPhysics, type FieldDesc, type FieldMask, type Fields, type FromCanonicalOptions, type GridDesc, type GridOptions, type Header, ID_MAX_BYTES, type Implementations, type Infer, type InferFields, type InitFields, type InitOf, type InstanceOf, type Kind, type OwnableKeys, type Owned, PHYSICS_BODY_CHANNELS, type PhysicsBodyChannel, type PhysicsBodyMap, type PhysicsDesc, type PhysicsKeys, type PlainState, RESERVED_COLLECTION_NAMES, RESERVED_RPC_NAMES, type ReadonlyBodyFields, type ReadonlyCollection, type RecordDirty, type RoleOf, type RpcDef, type RpcDesc, type RpcDirection, type RpcMap, type RpcParams, type RpcReturns, type RpcSpec, SERVER_OWNER, SINGLETON_ID, type Schema, type SchemaChange, type SchemaDefs, type SchemaIssue, type SchemaOptions, type SchemaRoles, type SchemaRpc, type ServerCallProxy, type ServerOwnedKeys, type ServerRpcs, type SingletonDef, type SnapshotOptions, type State, type Tracked, type Type, type TypeDesc, type ValueSink, type Visibility, type VisibleKeys, applyDelta, bool, bytesEqual, canonicalPhysics, canonicalType, client, cloneValue, collectionDirty, compilePhysics, computeDirty, createDirtySet, createFieldMask, createState, decodeDelta, decodeDeltaFrom, decodeFields, decodeSnapshot, defaultRecord, defaultValue, defineSchema, describeType, diffSchemas, encodeDelta, encodeFields, encodeSnapshot, entity, enumOf, estimateSize, f32, f64, filterDirty, frozenProxy, i32, isDirtyEmpty, isWhole, list, markAdd, markField, markOwner, markPath, markRemove, mergeDirty, mergeMask, normalizeRecord, normalizeValue, physicsFromCanonical, readValue, ref, schemaFromCanonical, server, sha256, sha256Hex, singleton, stableStringify, str, struct, toHex, track, u16, u32, u8, validateForDeploy, validateValue, writeValue };
|
|
920
|
+
export { type AddOptions, type AnyDef, type AnyRpc, type AnySchema, type AnyType, type Body2dState, type BodyFieldsOf, type BroadcastProxy, ByteReader, ByteWriter, CANONICAL_VERSION, type ClientCallProxy, type ClientImplementations, type ClientRpcs, type ClientState, type Collection, type CollectionDesc, type CollectionDirty, type DecodedSnapshot, type DeepReadonly, type DeepWritable, type DefMap, type Delta, type DeltaCollection, type DeltaOp, type DirtySet, EntityCollection, type EntityDef, type EntityOptions, type EntityPhysics, type FieldDesc, type FieldMask, type Fields, type FromCanonicalOptions, type GridDesc, type GridOptions, type Header, ID_MAX_BYTES, type Implementations, type Infer, type InferFields, type InitFields, type InitOf, type InstanceOf, type Kind, type OwnableKeys, type Owned, PHYSICS_BODY_CHANNELS, type PhysicsBodyChannel, type PhysicsBodyMap, type PhysicsDesc, type PhysicsKeys, type PlainState, RESERVED_COLLECTION_NAMES, RESERVED_RPC_NAMES, type ReadonlyBodyFields, type ReadonlyCollection, type RecordDirty, type RoleOf, type RpcDef, type RpcDesc, type RpcDirection, type RpcMap, type RpcParams, type RpcReturns, type RpcSpec, SERVER_OWNER, SINGLETON_ID, type Schema, type SchemaChange, type SchemaDefs, type SchemaIssue, type SchemaOptions, type SchemaRoles, type SchemaRpc, type ServerCallProxy, type ServerOwnedKeys, type ServerRpcs, type SingletonDef, type SnapshotOptions, type State, type Tracked, type Type, type TypeDesc, type ValueSink, type Visibility, type VisibleKeys, type WalkEvents, angleFrom2d, applyChannel2d, applyDelta, bool, bytesEqual, canonicalPhysics, canonicalType, channelOf2d, client, cloneValue, collectionDirty, compilePhysics, computeDirty, createDirtySet, createFieldMask, createState, decodeDelta, decodeDeltaFrom, decodeFields, decodeSnapshot, defaultRecord, defaultValue, defineSchema, describeType, diffSchemas, encodeDelta, encodeFields, encodeSnapshot, entity, enumOf, estimateSize, f32, f64, filterDirty, frozenProxy, i32, isDirtyEmpty, isWhole, list, markAdd, markField, markOwner, markPath, markRemove, mergeDirty, mergeMask, normalizeRecord, normalizeValue, physicsFromCanonical, readValue, ref, schemaFromCanonical, server, sha256, sha256Hex, singleton, stableStringify, str, struct, toHex, track, u16, u32, u8, validateForDeploy, validateValue, walkDelta, walkSnapshot, writeValue };
|
package/dist/index.js
CHANGED
|
@@ -318,6 +318,56 @@ function physicsFromCanonical(at, raw) {
|
|
|
318
318
|
}
|
|
319
319
|
return { body, intents };
|
|
320
320
|
}
|
|
321
|
+
function channelOf2d(channel, b) {
|
|
322
|
+
switch (channel) {
|
|
323
|
+
case "x":
|
|
324
|
+
return b.x;
|
|
325
|
+
case "y":
|
|
326
|
+
return b.y;
|
|
327
|
+
case "qz":
|
|
328
|
+
return Math.sin(b.angle / 2);
|
|
329
|
+
case "qw":
|
|
330
|
+
return Math.cos(b.angle / 2);
|
|
331
|
+
case "vx":
|
|
332
|
+
return b.vx;
|
|
333
|
+
case "vy":
|
|
334
|
+
return b.vy;
|
|
335
|
+
case "wz":
|
|
336
|
+
return b.angularVelocity;
|
|
337
|
+
default:
|
|
338
|
+
return 0;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function angleFrom2d(qz, qw) {
|
|
342
|
+
return 2 * Math.atan2(qz, qw);
|
|
343
|
+
}
|
|
344
|
+
function applyChannel2d(channel, value, target) {
|
|
345
|
+
switch (channel) {
|
|
346
|
+
case "x":
|
|
347
|
+
target.x = value;
|
|
348
|
+
return;
|
|
349
|
+
case "y":
|
|
350
|
+
target.y = value;
|
|
351
|
+
return;
|
|
352
|
+
case "qz":
|
|
353
|
+
target.qz = value;
|
|
354
|
+
return;
|
|
355
|
+
case "qw":
|
|
356
|
+
target.qw = value;
|
|
357
|
+
return;
|
|
358
|
+
case "vx":
|
|
359
|
+
target.vx = value;
|
|
360
|
+
return;
|
|
361
|
+
case "vy":
|
|
362
|
+
target.vy = value;
|
|
363
|
+
return;
|
|
364
|
+
case "wz":
|
|
365
|
+
target.wz = value;
|
|
366
|
+
return;
|
|
367
|
+
default:
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
321
371
|
|
|
322
372
|
// src/sha256.ts
|
|
323
373
|
var K = new Uint32Array([
|
|
@@ -1901,6 +1951,147 @@ function computeDirty(schema, from, to) {
|
|
|
1901
1951
|
return dirty;
|
|
1902
1952
|
}
|
|
1903
1953
|
|
|
1954
|
+
// src/walk.ts
|
|
1955
|
+
function skip(r, n) {
|
|
1956
|
+
if (n < 0 || r.remaining < n) throw new Error("unexpected end of buffer");
|
|
1957
|
+
r.pos += n;
|
|
1958
|
+
}
|
|
1959
|
+
function structNames2(d) {
|
|
1960
|
+
return Object.keys(d.fields);
|
|
1961
|
+
}
|
|
1962
|
+
function skipValue(r, desc) {
|
|
1963
|
+
if (desc.opt && r.u8() === 0) return;
|
|
1964
|
+
switch (desc.kind) {
|
|
1965
|
+
case "bool":
|
|
1966
|
+
case "u8":
|
|
1967
|
+
case "enum":
|
|
1968
|
+
skip(r, 1);
|
|
1969
|
+
return;
|
|
1970
|
+
case "u16":
|
|
1971
|
+
skip(r, 2);
|
|
1972
|
+
return;
|
|
1973
|
+
case "u32":
|
|
1974
|
+
case "i32":
|
|
1975
|
+
case "f32":
|
|
1976
|
+
skip(r, 4);
|
|
1977
|
+
return;
|
|
1978
|
+
case "f64":
|
|
1979
|
+
skip(r, 8);
|
|
1980
|
+
return;
|
|
1981
|
+
case "str":
|
|
1982
|
+
case "ref":
|
|
1983
|
+
skip(r, r.varint());
|
|
1984
|
+
return;
|
|
1985
|
+
case "list": {
|
|
1986
|
+
const n = r.varint();
|
|
1987
|
+
for (let i = 0; i < n; i++) skipValue(r, desc.item);
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1990
|
+
case "struct": {
|
|
1991
|
+
for (const fd of Object.values(desc.fields)) skipValue(r, fd);
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
function readBit2(mask, i) {
|
|
1997
|
+
return ((mask[i >> 3] ?? 0) & 1 << (i & 7)) !== 0;
|
|
1998
|
+
}
|
|
1999
|
+
function skipDirtyField(r, desc) {
|
|
2000
|
+
if (desc.kind !== "struct") {
|
|
2001
|
+
skipValue(r, desc);
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
if (desc.opt && r.u8() === 0) return;
|
|
2005
|
+
const names = structNames2(desc);
|
|
2006
|
+
const maskBytes = r.bytes(names.length + 7 >> 3);
|
|
2007
|
+
for (let i = 0; i < names.length; i++) {
|
|
2008
|
+
if (!readBit2(maskBytes, i)) continue;
|
|
2009
|
+
skipDirtyField(r, desc.fields[names[i]]);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
function readerFor(bytes) {
|
|
2013
|
+
return bytes instanceof ByteReader ? bytes : new ByteReader(bytes);
|
|
2014
|
+
}
|
|
2015
|
+
function walkDelta(schema, bytes, events) {
|
|
2016
|
+
const r = readerFor(bytes);
|
|
2017
|
+
const from = r.pos;
|
|
2018
|
+
let at = r.pos;
|
|
2019
|
+
skip(r, 12);
|
|
2020
|
+
const count = r.varint();
|
|
2021
|
+
events.header(r.pos - at);
|
|
2022
|
+
for (let ci = 0; ci < count; ci++) {
|
|
2023
|
+
at = r.pos;
|
|
2024
|
+
const index = r.varint();
|
|
2025
|
+
const c = schema.collections[index];
|
|
2026
|
+
if (!c) throw new Error(`delta references unknown collection index ${index}`);
|
|
2027
|
+
const opCount = r.varint();
|
|
2028
|
+
events.header(r.pos - at);
|
|
2029
|
+
for (let oi = 0; oi < opCount; oi++) {
|
|
2030
|
+
at = r.pos;
|
|
2031
|
+
const tag = r.u8();
|
|
2032
|
+
const id = r.str();
|
|
2033
|
+
if (tag === 2) {
|
|
2034
|
+
events.op(c, "remove", id, r.pos - at);
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
if (tag === 0) {
|
|
2038
|
+
skip(r, r.varint());
|
|
2039
|
+
events.op(c, "add", id, r.pos - at);
|
|
2040
|
+
for (const f of c.fields) {
|
|
2041
|
+
const start = r.pos;
|
|
2042
|
+
skipValue(r, f.type);
|
|
2043
|
+
events.field(c, f, r.pos - start);
|
|
2044
|
+
}
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
2047
|
+
if (tag !== 1) throw new Error(`unknown delta op byte ${tag} in ${c.name}`);
|
|
2048
|
+
const n = c.fields.length;
|
|
2049
|
+
const maskBytes = r.bytes(n + 1 + 7 >> 3).slice();
|
|
2050
|
+
if (readBit2(maskBytes, n)) skip(r, r.varint());
|
|
2051
|
+
events.op(c, "update", id, r.pos - at);
|
|
2052
|
+
for (let i = 0; i < n; i++) {
|
|
2053
|
+
if (!readBit2(maskBytes, i)) continue;
|
|
2054
|
+
const f = c.fields[i];
|
|
2055
|
+
const start = r.pos;
|
|
2056
|
+
skipDirtyField(r, f.type);
|
|
2057
|
+
events.field(c, f, r.pos - start);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
return r.pos - from;
|
|
2062
|
+
}
|
|
2063
|
+
function walkSnapshot(schema, bytes, events) {
|
|
2064
|
+
const r = readerFor(bytes);
|
|
2065
|
+
const from = r.pos;
|
|
2066
|
+
skip(r, 12);
|
|
2067
|
+
events.header(12);
|
|
2068
|
+
for (const c of schema.collections) {
|
|
2069
|
+
if (c.kind === "entity") {
|
|
2070
|
+
let at = r.pos;
|
|
2071
|
+
const n = r.varint();
|
|
2072
|
+
events.header(r.pos - at);
|
|
2073
|
+
for (let i = 0; i < n; i++) {
|
|
2074
|
+
at = r.pos;
|
|
2075
|
+
const id = r.str();
|
|
2076
|
+
skip(r, r.varint());
|
|
2077
|
+
events.op(c, "add", id, r.pos - at);
|
|
2078
|
+
for (const f of c.fields) {
|
|
2079
|
+
const start = r.pos;
|
|
2080
|
+
skipValue(r, f.type);
|
|
2081
|
+
events.field(c, f, r.pos - start);
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
} else {
|
|
2085
|
+
for (const f of c.fields) {
|
|
2086
|
+
const start = r.pos;
|
|
2087
|
+
skipValue(r, f.type);
|
|
2088
|
+
events.field(c, f, r.pos - start);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
return r.pos - from;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
1904
2095
|
// src/track.ts
|
|
1905
2096
|
var fieldTables = /* @__PURE__ */ new WeakMap();
|
|
1906
2097
|
function structFields(d) {
|
|
@@ -2388,6 +2579,14 @@ function diffCollections(oldCollections, newCollections, changes) {
|
|
|
2388
2579
|
message: `${oldC.name}: visible roles changed from [${(oldC.roles ?? []).join(", ")}] to [${(newC.roles ?? []).join(", ")}] \u2014 changes who sees this collection`
|
|
2389
2580
|
});
|
|
2390
2581
|
}
|
|
2582
|
+
if (oldC.index !== newC.index) {
|
|
2583
|
+
changes.push({
|
|
2584
|
+
kind: "breaking",
|
|
2585
|
+
code: `${oldC.kind}.reordered`,
|
|
2586
|
+
path: oldC.name,
|
|
2587
|
+
message: `${oldC.name}: wire index changed (${oldC.index} -> ${newC.index}) \u2014 collections are addressed positionally, so every delta and snapshot for this collection would be misread (breaking)`
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2391
2590
|
if (stableStringify(oldC.grid) !== stableStringify(newC.grid)) {
|
|
2392
2591
|
changes.push({
|
|
2393
2592
|
kind: "breaking",
|
|
@@ -2422,9 +2621,25 @@ function diffRpcs(oldRpcs, newRpcs, changes) {
|
|
|
2422
2621
|
});
|
|
2423
2622
|
}
|
|
2424
2623
|
}
|
|
2624
|
+
if (oldRpcs.length !== newRpcs.length) {
|
|
2625
|
+
changes.push({
|
|
2626
|
+
kind: "breaking",
|
|
2627
|
+
code: "rpc.builtin_reordered",
|
|
2628
|
+
path: "rpc",
|
|
2629
|
+
message: `rpc: the number of RPCs changed from ${oldRpcs.length} to ${newRpcs.length}, which shifts the ids of irtio's built-in RPCs (they are numbered after yours) \u2014 connected clients would call the wrong one (breaking)`
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2425
2632
|
for (const oldR of oldRpcs) {
|
|
2426
2633
|
const newR = newByName.get(oldR.name);
|
|
2427
2634
|
if (!newR) continue;
|
|
2635
|
+
if (oldR.index !== newR.index) {
|
|
2636
|
+
changes.push({
|
|
2637
|
+
kind: "breaking",
|
|
2638
|
+
code: "rpc.reordered",
|
|
2639
|
+
path: `rpc.${oldR.name}`,
|
|
2640
|
+
message: `rpc ${oldR.name}: wire id changed (${oldR.index} -> ${newR.index}) \u2014 RPCs are addressed by a positional id, so calls would reach the wrong handler (breaking)`
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2428
2643
|
if (oldR.direction !== newR.direction) {
|
|
2429
2644
|
changes.push({
|
|
2430
2645
|
kind: "breaking",
|
|
@@ -2682,11 +2897,14 @@ export {
|
|
|
2682
2897
|
RESERVED_RPC_NAMES,
|
|
2683
2898
|
SERVER_OWNER,
|
|
2684
2899
|
SINGLETON_ID,
|
|
2900
|
+
angleFrom2d,
|
|
2901
|
+
applyChannel2d,
|
|
2685
2902
|
applyDelta,
|
|
2686
2903
|
bool,
|
|
2687
2904
|
bytesEqual,
|
|
2688
2905
|
canonicalPhysics,
|
|
2689
2906
|
canonicalType,
|
|
2907
|
+
channelOf2d,
|
|
2690
2908
|
client,
|
|
2691
2909
|
cloneValue,
|
|
2692
2910
|
collectionDirty,
|
|
@@ -2745,5 +2963,7 @@ export {
|
|
|
2745
2963
|
u8,
|
|
2746
2964
|
validateForDeploy,
|
|
2747
2965
|
validateValue,
|
|
2966
|
+
walkDelta,
|
|
2967
|
+
walkSnapshot,
|
|
2748
2968
|
writeValue
|
|
2749
2969
|
};
|