@dxos/echo-protocol 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.13.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/lib/index.mjs +143 -32
- package/dist/lib/index.mjs.map +1 -1
- package/dist/types/src/document-structure.d.ts +45 -4
- package/dist/types/src/document-structure.d.ts.map +1 -1
- package/dist/types/src/echo-feed-codec.d.ts +36 -4
- package/dist/types/src/echo-feed-codec.d.ts.map +1 -1
- package/dist/types/src/edge-peer.d.ts +2 -2
- package/dist/types/src/edge-peer.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +10 -11
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +100 -8
- package/dist/types/src/query/ast.d.ts.map +1 -1
- package/dist/types/src/query/index.d.ts +1 -1
- package/dist/types/src/query/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/document-structure.ts +59 -5
- package/src/echo-feed-codec.ts +94 -28
- package/src/edge-peer.ts +4 -8
- package/src/foreign-key.ts +1 -1
- package/src/index.ts +10 -11
- package/src/query/ast.ts +77 -1
- package/src/query/index.ts +1 -1
- package/dist/types/src/blob.d.ts +0 -40
- package/dist/types/src/blob.d.ts.map +0 -1
- package/src/blob.ts +0 -36
package/dist/lib/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { ComplexMap, compositeKey, visitValues } from "@dxos/util";
|
|
|
4
4
|
import { subtleCrypto } from "@dxos/crypto/subtle";
|
|
5
5
|
import { EdgeService, FeedProtocol } from "@dxos/protocols";
|
|
6
6
|
import * as Schema from "effect/Schema";
|
|
7
|
-
import
|
|
7
|
+
import * as SchemaAST from "@dxos/effect/SchemaAST";
|
|
8
8
|
import * as Match from "effect/Match";
|
|
9
9
|
//#region \0rolldown/runtime.js
|
|
10
10
|
var __defProp = Object.defineProperty;
|
|
@@ -151,6 +151,15 @@ var EntityStructure = Object.freeze({
|
|
|
151
151
|
isDeleted: (object) => {
|
|
152
152
|
return object.system?.deleted ?? false;
|
|
153
153
|
},
|
|
154
|
+
getConvergenceKey: (object) => {
|
|
155
|
+
return object.meta?.convergenceKey;
|
|
156
|
+
},
|
|
157
|
+
getMergedInto: (object) => {
|
|
158
|
+
return object.system?.mergedInto;
|
|
159
|
+
},
|
|
160
|
+
getMergedFrom: (object) => {
|
|
161
|
+
return object.system?.mergedFrom ?? [];
|
|
162
|
+
},
|
|
154
163
|
getRelationSource: (object) => {
|
|
155
164
|
return object.system?.source;
|
|
156
165
|
},
|
|
@@ -224,25 +233,26 @@ var DATA_NAMESPACE = "data";
|
|
|
224
233
|
//#endregion
|
|
225
234
|
//#region src/edge-peer.ts
|
|
226
235
|
/**
|
|
227
|
-
* Returns true if the given peerId belongs to
|
|
236
|
+
* Returns true if the given peerId belongs to the EDGE Subduction replicator.
|
|
228
237
|
*
|
|
229
238
|
* When `spaceId` is provided, the match is scoped to that space (peerId must start with
|
|
230
239
|
* `<service>:<spaceId>`). When omitted, only the leading service segment is checked
|
|
231
240
|
* (peerId must start with `<service>:`), which is useful when the caller doesn't have a
|
|
232
|
-
* spaceId on hand or wants to match
|
|
241
|
+
* spaceId on hand or wants to match the edge replicator regardless of space.
|
|
233
242
|
*/
|
|
234
243
|
var isEdgePeerId = (peerId, spaceId) => {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
return peerId.startsWith(automergePrefix) || peerId.startsWith(subductionPrefix);
|
|
244
|
+
const prefix = spaceId !== void 0 ? compositeKey(EdgeService.SUBDUCTION_REPLICATOR, spaceId) : `${EdgeService.SUBDUCTION_REPLICATOR}:`;
|
|
245
|
+
return peerId.startsWith(prefix);
|
|
238
246
|
};
|
|
239
247
|
//#endregion
|
|
240
248
|
//#region src/echo-feed-codec.ts
|
|
241
249
|
/** Property name for meta when object is serialized to JSON. Matches @dxos/echo/internal ATTR_META. */
|
|
242
250
|
var ATTR_META = "@meta";
|
|
251
|
+
/** Meta keys a store stamps on decode and strips on encode: they describe the block, not the object. */
|
|
252
|
+
var FEED_KEY_SOURCES = [FeedProtocol.KEY_QUEUE_POSITION, FeedProtocol.KEY_FEED_BLOCK];
|
|
243
253
|
/**
|
|
244
254
|
* Codec for ECHO objects in feed block payload: JSON object ↔ UTF-8 bytes.
|
|
245
|
-
* Encodes with
|
|
255
|
+
* Encodes with the block's own keys stripped; decodes with them stamped into `@meta`.
|
|
246
256
|
*/
|
|
247
257
|
var EchoFeedCodec = class EchoFeedCodec {
|
|
248
258
|
static #encoder = new TextEncoder();
|
|
@@ -253,8 +263,7 @@ var EchoFeedCodec = class EchoFeedCodec {
|
|
|
253
263
|
* last-write-wins merge at the index (see EntityMetaIndex.update).
|
|
254
264
|
*/
|
|
255
265
|
static encode(value) {
|
|
256
|
-
|
|
257
|
-
return EchoFeedCodec.#encoder.encode(JSON.stringify(prepared));
|
|
266
|
+
return EchoFeedCodec.#encoder.encode(JSON.stringify(EchoFeedCodec.stripFeedKeys(value)));
|
|
258
267
|
}
|
|
259
268
|
/**
|
|
260
269
|
* Decodes feed block bytes to a JSON value.
|
|
@@ -262,33 +271,77 @@ var EchoFeedCodec = class EchoFeedCodec {
|
|
|
262
271
|
*/
|
|
263
272
|
static decode(data, position) {
|
|
264
273
|
const decoded = JSON.parse(EchoFeedCodec.#decoder.decode(data));
|
|
265
|
-
if (position !== void 0 && typeof decoded === "object" && decoded !== null) EchoFeedCodec.#
|
|
274
|
+
if (position !== void 0 && typeof decoded === "object" && decoded !== null) EchoFeedCodec.#setKey(decoded, FeedProtocol.KEY_QUEUE_POSITION, position.toString());
|
|
266
275
|
return decoded;
|
|
267
276
|
}
|
|
268
277
|
/**
|
|
269
|
-
*
|
|
270
|
-
*
|
|
278
|
+
* Decodes a stored block, stamping its identity (and position, once it has one) into `@meta` so a
|
|
279
|
+
* reader can tell blocks apart and order them without comparing their content.
|
|
271
280
|
*/
|
|
272
|
-
static
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (meta?.keys?.some((key) => key.source === FeedProtocol.KEY_QUEUE_POSITION)) meta.keys = meta.keys.filter((key) => key.source !== FeedProtocol.KEY_QUEUE_POSITION);
|
|
277
|
-
return obj;
|
|
281
|
+
static decodeBlock(block) {
|
|
282
|
+
const decoded = EchoFeedCodec.decode(block.data, block.position ?? void 0);
|
|
283
|
+
if (typeof decoded === "object" && decoded !== null) EchoFeedCodec.#setKey(decoded, FeedProtocol.KEY_FEED_BLOCK, EchoFeedCodec.blockId(block.actorId, block.sequence));
|
|
284
|
+
return decoded;
|
|
278
285
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
i--;
|
|
286
|
-
}
|
|
287
|
-
keys.push({
|
|
288
|
-
source: FeedProtocol.KEY_QUEUE_POSITION,
|
|
289
|
-
id: position.toString()
|
|
290
|
-
});
|
|
286
|
+
/**
|
|
287
|
+
* The id stamped for a block. The sequence comes first: it is always digits, so the first separator
|
|
288
|
+
* splits the id even when the actor id contains one.
|
|
289
|
+
*/
|
|
290
|
+
static blockId(actorId, sequence) {
|
|
291
|
+
return `${sequence}@${actorId}`;
|
|
291
292
|
}
|
|
293
|
+
/** Reads the block reference {@link decodeBlock} (or {@link decode} with a position) stamped. */
|
|
294
|
+
static blockOf(value) {
|
|
295
|
+
return EchoFeedCodec.blockOfKeys(metaKeysOf(value));
|
|
296
|
+
}
|
|
297
|
+
/** Reads the block reference from an object's foreign keys. */
|
|
298
|
+
static blockOfKeys(keys) {
|
|
299
|
+
const ref = {};
|
|
300
|
+
for (const key of keys) if (key.source === FeedProtocol.KEY_QUEUE_POSITION) {
|
|
301
|
+
const position = Number(key.id);
|
|
302
|
+
if (Number.isFinite(position)) ref.position = position;
|
|
303
|
+
} else if (key.source === FeedProtocol.KEY_FEED_BLOCK) Object.assign(ref, parseBlockId(key.id));
|
|
304
|
+
return ref;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Returns `value` without the keys a store stamps on decode, so re-appending a decoded object does
|
|
308
|
+
* not carry its old block's identity into the new block. Copies only the containers it changes.
|
|
309
|
+
*/
|
|
310
|
+
static stripFeedKeys(value) {
|
|
311
|
+
const meta = value[ATTR_META];
|
|
312
|
+
const keys = metaKeysOf(value);
|
|
313
|
+
if (typeof meta !== "object" || meta === null || !keys.some((key) => FEED_KEY_SOURCES.includes(key.source))) return value;
|
|
314
|
+
return {
|
|
315
|
+
...value,
|
|
316
|
+
[ATTR_META]: {
|
|
317
|
+
...meta,
|
|
318
|
+
keys: keys.filter((key) => !FEED_KEY_SOURCES.includes(key.source))
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
static #setKey(obj, source, id) {
|
|
323
|
+
const meta = obj[ATTR_META];
|
|
324
|
+
obj[ATTR_META] = {
|
|
325
|
+
...typeof meta === "object" && meta !== null ? meta : {},
|
|
326
|
+
keys: [...metaKeysOf(obj).filter((key) => key.source !== source), {
|
|
327
|
+
source,
|
|
328
|
+
id
|
|
329
|
+
}]
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
var metaKeysOf = (value) => {
|
|
334
|
+
const meta = value[ATTR_META];
|
|
335
|
+
const keys = typeof meta === "object" && meta !== null && "keys" in meta ? meta.keys : void 0;
|
|
336
|
+
return Array.isArray(keys) ? keys : [];
|
|
337
|
+
};
|
|
338
|
+
var parseBlockId = (id) => {
|
|
339
|
+
const separator = id.indexOf("@");
|
|
340
|
+
const sequence = Number(id.slice(0, separator));
|
|
341
|
+
return separator > 0 && Number.isInteger(sequence) ? {
|
|
342
|
+
sequence,
|
|
343
|
+
actorId: id.slice(separator + 1)
|
|
344
|
+
} : {};
|
|
292
345
|
};
|
|
293
346
|
/**
|
|
294
347
|
* Foreign-key source for the global position a feed block was assigned.
|
|
@@ -315,6 +368,8 @@ var ast_exports = /* @__PURE__ */ __exportAll({
|
|
|
315
368
|
FeedScope: () => FeedScope,
|
|
316
369
|
Filter: () => Filter,
|
|
317
370
|
FilterAnd: () => FilterAnd,
|
|
371
|
+
FilterAnnotation: () => FilterAnnotation,
|
|
372
|
+
FilterChanges: () => FilterChanges,
|
|
318
373
|
FilterChildOf: () => FilterChildOf,
|
|
319
374
|
FilterCompare: () => FilterCompare,
|
|
320
375
|
FilterContains: () => FilterContains,
|
|
@@ -322,6 +377,7 @@ var ast_exports = /* @__PURE__ */ __exportAll({
|
|
|
322
377
|
FilterHasParent: () => FilterHasParent,
|
|
323
378
|
FilterIn: () => FilterIn,
|
|
324
379
|
FilterInQuery: () => FilterInQuery,
|
|
380
|
+
FilterMnemonic: () => FilterMnemonic,
|
|
325
381
|
FilterNot: () => FilterNot,
|
|
326
382
|
FilterObject: () => FilterObject,
|
|
327
383
|
FilterOr: () => FilterOr,
|
|
@@ -354,6 +410,7 @@ var ast_exports = /* @__PURE__ */ __exportAll({
|
|
|
354
410
|
Scope: () => Scope,
|
|
355
411
|
SpaceScope: () => SpaceScope,
|
|
356
412
|
fold: () => fold,
|
|
413
|
+
isGroupKeyAggregate: () => isGroupKeyAggregate,
|
|
357
414
|
map: () => map,
|
|
358
415
|
visit: () => visit
|
|
359
416
|
});
|
|
@@ -414,6 +471,15 @@ var FilterTag = Schema.Struct({
|
|
|
414
471
|
type: Schema.Literal("tag"),
|
|
415
472
|
tag: Schema.String
|
|
416
473
|
});
|
|
474
|
+
var FilterAnnotation = Schema.Struct({
|
|
475
|
+
type: Schema.Literal("annotation"),
|
|
476
|
+
key: Schema.String,
|
|
477
|
+
value: Schema.optional(Schema.Union([
|
|
478
|
+
Schema.String,
|
|
479
|
+
Schema.Number,
|
|
480
|
+
Schema.Boolean
|
|
481
|
+
]))
|
|
482
|
+
});
|
|
417
483
|
var FilterRange = Schema.Struct({
|
|
418
484
|
type: Schema.Literal("range"),
|
|
419
485
|
from: Schema.Any,
|
|
@@ -466,6 +532,15 @@ var FilterHasParent = Schema.Struct({
|
|
|
466
532
|
/** True matches objects with a parent; false matches unparented (root) objects. */
|
|
467
533
|
value: Schema.Boolean
|
|
468
534
|
});
|
|
535
|
+
var FilterMnemonic = Schema.Struct({
|
|
536
|
+
type: Schema.Literal("mnemonic"),
|
|
537
|
+
/** Normalized (uppercase) mnemonic. */
|
|
538
|
+
mnemonic: Schema.String
|
|
539
|
+
});
|
|
540
|
+
var FilterChanges = Schema.Struct({
|
|
541
|
+
type: Schema.Literal("changes"),
|
|
542
|
+
targets: Schema.optional(Schema.Array(EID.Schema))
|
|
543
|
+
});
|
|
469
544
|
/**
|
|
470
545
|
* Union of filters.
|
|
471
546
|
*/
|
|
@@ -476,12 +551,15 @@ var Filter = Schema.Union([
|
|
|
476
551
|
FilterInQuery,
|
|
477
552
|
FilterContains,
|
|
478
553
|
FilterTag,
|
|
554
|
+
FilterAnnotation,
|
|
479
555
|
FilterRange,
|
|
480
556
|
FilterTimestamp,
|
|
481
557
|
FilterFeedCursor,
|
|
482
558
|
FilterTextSearch,
|
|
483
559
|
FilterChildOf,
|
|
484
560
|
FilterHasParent,
|
|
561
|
+
FilterMnemonic,
|
|
562
|
+
FilterChanges,
|
|
485
563
|
FilterNot,
|
|
486
564
|
FilterAnd,
|
|
487
565
|
FilterOr
|
|
@@ -607,6 +685,11 @@ var QuerySkipClause = Schema.Struct({
|
|
|
607
685
|
* ordering, independent of any `orderBy` clause elsewhere in the query (which orders the whole
|
|
608
686
|
* input stream / the resulting groups, not this aggregate's member selection).
|
|
609
687
|
* - `count` yields the member count. Opt-in — a row carries no count otherwise.
|
|
688
|
+
* - `type` partitions members by their type URI; the field carries the URI string.
|
|
689
|
+
* - `timestamp` partitions members by the UTC hour or day a system timestamp falls in; the field
|
|
690
|
+
* carries the start of that interval in unix ms.
|
|
691
|
+
* - `time` is `timestamp` over a unix-ms member `property` instead of a system timestamp.
|
|
692
|
+
* - `sum` adds a numeric member `property`; non-numeric values count as 0.
|
|
610
693
|
*/
|
|
611
694
|
var GroupAggregateGroup_ = Schema.Struct({
|
|
612
695
|
name: Schema.String,
|
|
@@ -639,13 +722,41 @@ var GroupAggregateCount_ = Schema.Struct({
|
|
|
639
722
|
name: Schema.String,
|
|
640
723
|
kind: Schema.Literal("count")
|
|
641
724
|
});
|
|
642
|
-
var
|
|
725
|
+
var GroupAggregateType_ = Schema.Struct({
|
|
726
|
+
name: Schema.String,
|
|
727
|
+
kind: Schema.Literal("type")
|
|
728
|
+
});
|
|
729
|
+
var GroupAggregateTimestamp_ = Schema.Struct({
|
|
730
|
+
name: Schema.String,
|
|
731
|
+
kind: Schema.Literal("timestamp"),
|
|
732
|
+
field: Schema.Literals(["createdAt", "updatedAt"]),
|
|
733
|
+
unit: Schema.Literals(["hour", "day"])
|
|
734
|
+
});
|
|
735
|
+
var GroupAggregateTime_ = Schema.Struct({
|
|
736
|
+
name: Schema.String,
|
|
737
|
+
kind: Schema.Literal("time"),
|
|
738
|
+
property: Schema.String,
|
|
739
|
+
unit: Schema.Literals(["hour", "day"])
|
|
740
|
+
});
|
|
741
|
+
var GroupAggregateSum_ = Schema.Struct({
|
|
742
|
+
name: Schema.String,
|
|
743
|
+
kind: Schema.Literal("sum"),
|
|
744
|
+
property: Schema.String
|
|
745
|
+
});
|
|
746
|
+
var GroupAggregate_ = Schema.Union([
|
|
643
747
|
GroupAggregateGroup_,
|
|
644
748
|
GroupAggregateMax_,
|
|
645
749
|
GroupAggregateMin_,
|
|
646
750
|
GroupAggregateItems_,
|
|
647
|
-
GroupAggregateCount_
|
|
751
|
+
GroupAggregateCount_,
|
|
752
|
+
GroupAggregateType_,
|
|
753
|
+
GroupAggregateTimestamp_,
|
|
754
|
+
GroupAggregateTime_,
|
|
755
|
+
GroupAggregateSum_
|
|
648
756
|
]);
|
|
757
|
+
/** Aggregate kinds that contribute a component to the group key. */
|
|
758
|
+
var isGroupKeyAggregate = (aggregate) => aggregate.kind === "group" || aggregate.kind === "type" || aggregate.kind === "timestamp" || aggregate.kind === "time";
|
|
759
|
+
var GroupAggregate = GroupAggregate_;
|
|
649
760
|
var QueryAggregateClause = Schema.Struct({
|
|
650
761
|
type: Schema.Literal("aggregate"),
|
|
651
762
|
query: Schema.suspend(() => Query),
|