@dxos/echo-protocol 0.10.0 → 0.11.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 +722 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/types/src/blob.d.ts +40 -0
- package/dist/types/src/blob.d.ts.map +1 -0
- package/dist/types/src/document-structure.d.ts +72 -4
- package/dist/types/src/document-structure.d.ts.map +1 -1
- package/dist/types/src/echo-feed-codec.d.ts +13 -1
- package/dist/types/src/echo-feed-codec.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +191 -23
- package/dist/types/src/query/ast.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/blob.ts +36 -0
- package/src/document-structure.ts +104 -4
- package/src/echo-feed-codec.ts +15 -3
- package/src/index.ts +1 -0
- package/src/query/ast.ts +101 -1
- package/dist/lib/neutral/index.mjs +0 -721
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
import { assertArgument, invariant } from "@dxos/invariant";
|
|
2
|
+
import { EID, EntityId, PublicKey, SpaceId, URI } from "@dxos/keys";
|
|
3
|
+
import { ComplexMap, compositeKey, visitValues } from "@dxos/util";
|
|
4
|
+
import { subtleCrypto } from "@dxos/crypto";
|
|
5
|
+
import { EdgeService, FeedProtocol } from "@dxos/protocols";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import * as SchemaAST from "effect/SchemaAST";
|
|
8
|
+
import * as Match from "effect/Match";
|
|
9
|
+
//#region \0rolldown/runtime.js
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __exportAll = (all, no_symbols) => {
|
|
12
|
+
let target = {};
|
|
13
|
+
for (var name in all) __defProp(target, name, {
|
|
14
|
+
get: all[name],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
18
|
+
return target;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/reference.ts
|
|
22
|
+
var REFERENCE_TYPE_TAG = "dxos.echo.model.document.Reference";
|
|
23
|
+
var isEncodedReference = (value) => typeof value === "object" && value !== null && Object.keys(value).length === 1 && typeof value["/"] === "string";
|
|
24
|
+
var EncodedReference = Object.freeze({
|
|
25
|
+
isEncodedReference,
|
|
26
|
+
/**
|
|
27
|
+
* Returns the opaque URI stored in the encoded reference (any scheme: `echo:` or `dxn:`).
|
|
28
|
+
* Consumers can narrow with `EID.isEID(uri)` / `DXN.isDXN(uri)`.
|
|
29
|
+
*/
|
|
30
|
+
toURI: (value) => {
|
|
31
|
+
assertArgument(isEncodedReference(value), "value", "invalid reference");
|
|
32
|
+
return value["/"];
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Creates an encoded reference from an opaque URI.
|
|
36
|
+
*/
|
|
37
|
+
fromURI: (uri) => {
|
|
38
|
+
return { "/": uri };
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/space-id.ts
|
|
43
|
+
var SPACE_IDS_CACHE = new ComplexMap(PublicKey.hash);
|
|
44
|
+
/**
|
|
45
|
+
* Space keys are generated by creating a keypair, and then taking the first 20 bytes of the SHA-256 hash of the public key and encoding them to multibase RFC4648 base-32 format (prefixed with B, see Multibase Table).
|
|
46
|
+
* Inspired by how ethereum addresses are derived.
|
|
47
|
+
*/
|
|
48
|
+
var createIdFromSpaceKey = async (spaceKey) => {
|
|
49
|
+
const cachedValue = SPACE_IDS_CACHE.get(spaceKey);
|
|
50
|
+
if (cachedValue !== void 0) return cachedValue;
|
|
51
|
+
const digest = await subtleCrypto.digest("SHA-256", spaceKey.asUint8Array());
|
|
52
|
+
const bytes = new Uint8Array(digest).slice(0, SpaceId.byteLength);
|
|
53
|
+
const spaceId = SpaceId.encode(bytes);
|
|
54
|
+
SPACE_IDS_CACHE.set(spaceKey, spaceId);
|
|
55
|
+
return spaceId;
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/document-structure.ts
|
|
59
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/core/echo/echo-protocol/src/document-structure.ts";
|
|
60
|
+
var DatabaseDirectory = Object.freeze({
|
|
61
|
+
/**
|
|
62
|
+
* @returns ID of the space that owns the document.
|
|
63
|
+
* Coalesces `access.spaceId` with the deprecated space key fields (`access.spaceKey`,
|
|
64
|
+
* `experimental_spaceKey`), deriving the id from the key for documents that predate `spaceId`.
|
|
65
|
+
*/
|
|
66
|
+
getSpaceId: async (doc) => {
|
|
67
|
+
if (doc.access?.spaceId != null) {
|
|
68
|
+
invariant(SpaceId.isValid(doc.access.spaceId), "Invalid space ID", {
|
|
69
|
+
"~LogMeta": "~LogMeta",
|
|
70
|
+
F: __dxlog_file,
|
|
71
|
+
L: 111,
|
|
72
|
+
S: void 0,
|
|
73
|
+
A: ["SpaceId.isValid(doc.access.spaceId)", "'Invalid space ID'"]
|
|
74
|
+
});
|
|
75
|
+
return doc.access.spaceId;
|
|
76
|
+
}
|
|
77
|
+
const spaceKeyHex = DatabaseDirectory.getSpaceKey(doc);
|
|
78
|
+
if (spaceKeyHex == null) return null;
|
|
79
|
+
return createIdFromSpaceKey(PublicKey.fromHex(spaceKeyHex));
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* @deprecated Use {@link DatabaseDirectory.getSpaceId}. Only paths that require the space
|
|
83
|
+
* public key (which cannot be derived from the space id) should read the key.
|
|
84
|
+
*
|
|
85
|
+
* @returns Space key in hex of the space that owns the document. In hex format. Without 0x prefix.
|
|
86
|
+
*/
|
|
87
|
+
getSpaceKey: (doc) => {
|
|
88
|
+
const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
|
|
89
|
+
if (rawSpaceKey == null) return null;
|
|
90
|
+
const rawKey = String(rawSpaceKey);
|
|
91
|
+
invariant(!rawKey.startsWith("0x"), "Space key must not start with 0x", {
|
|
92
|
+
"~LogMeta": "~LogMeta",
|
|
93
|
+
F: __dxlog_file,
|
|
94
|
+
L: 137,
|
|
95
|
+
S: void 0,
|
|
96
|
+
A: ["!rawKey.startsWith('0x')", "'Space key must not start with 0x'"]
|
|
97
|
+
});
|
|
98
|
+
return rawKey;
|
|
99
|
+
},
|
|
100
|
+
getInlineObject: (doc, id) => {
|
|
101
|
+
return doc.objects?.[id];
|
|
102
|
+
},
|
|
103
|
+
getLink: (doc, id) => {
|
|
104
|
+
return doc.links?.[id]?.toString();
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* @returns The branch registry for a subtree-root object, or undefined if it has no branches.
|
|
108
|
+
*/
|
|
109
|
+
getBranches: (doc, rootObjectId) => {
|
|
110
|
+
return doc.branches?.[rootObjectId];
|
|
111
|
+
},
|
|
112
|
+
/**
|
|
113
|
+
* @returns All branch document urls referenced anywhere in the registry. Used by the host to
|
|
114
|
+
* decide which documents to replicate (branch docs are NOT object links).
|
|
115
|
+
*/
|
|
116
|
+
getAllBranchDocUrls: (doc) => {
|
|
117
|
+
const urls = [];
|
|
118
|
+
for (const byName of Object.values(doc.branches ?? {})) for (const record of Object.values(byName)) for (const url of Object.values(record.members ?? {})) urls.push(url.toString());
|
|
119
|
+
return urls;
|
|
120
|
+
},
|
|
121
|
+
make: ({ spaceId, spaceKey, objects, links }) => ({
|
|
122
|
+
access: {
|
|
123
|
+
...spaceId != null ? { spaceId } : {},
|
|
124
|
+
...spaceKey != null ? { spaceKey } : {}
|
|
125
|
+
},
|
|
126
|
+
objects: objects ?? {},
|
|
127
|
+
links: links ?? {}
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
var EntityStructure = Object.freeze({
|
|
131
|
+
/**
|
|
132
|
+
* @throws On invalid object structure.
|
|
133
|
+
*/
|
|
134
|
+
getTypeReference: (object) => {
|
|
135
|
+
return object.system?.type;
|
|
136
|
+
},
|
|
137
|
+
/**
|
|
138
|
+
* @throws On invalid object structure.
|
|
139
|
+
*/
|
|
140
|
+
getEntityKind: (object) => {
|
|
141
|
+
const kind = object.system?.kind ?? "object";
|
|
142
|
+
invariant(kind === "object" || kind === "relation" || kind === "type", "Invalid kind", {
|
|
143
|
+
"~LogMeta": "~LogMeta",
|
|
144
|
+
F: __dxlog_file,
|
|
145
|
+
L: 224,
|
|
146
|
+
S: void 0,
|
|
147
|
+
A: ["kind === 'object' || kind === 'relation' || kind === 'type'", "'Invalid kind'"]
|
|
148
|
+
});
|
|
149
|
+
return kind;
|
|
150
|
+
},
|
|
151
|
+
isDeleted: (object) => {
|
|
152
|
+
return object.system?.deleted ?? false;
|
|
153
|
+
},
|
|
154
|
+
getRelationSource: (object) => {
|
|
155
|
+
return object.system?.source;
|
|
156
|
+
},
|
|
157
|
+
getRelationTarget: (object) => {
|
|
158
|
+
return object.system?.target;
|
|
159
|
+
},
|
|
160
|
+
getParent: (object) => {
|
|
161
|
+
return object.system?.parent;
|
|
162
|
+
},
|
|
163
|
+
/**
|
|
164
|
+
* @returns All references in the data section of the object.
|
|
165
|
+
*/
|
|
166
|
+
getAllOutgoingReferences: (object) => {
|
|
167
|
+
const references = [];
|
|
168
|
+
const visit = (path, value) => {
|
|
169
|
+
if (isEncodedReference(value)) references.push({
|
|
170
|
+
path,
|
|
171
|
+
reference: value
|
|
172
|
+
});
|
|
173
|
+
else visitValues(value, (value, key) => visit([...path, String(key)], value));
|
|
174
|
+
};
|
|
175
|
+
visitValues(object.data, (value, key) => visit([String(key)], value));
|
|
176
|
+
return references;
|
|
177
|
+
},
|
|
178
|
+
getTags: (object) => {
|
|
179
|
+
return object.meta.tags ?? [];
|
|
180
|
+
},
|
|
181
|
+
makeObject: ({ type, data, keys }) => {
|
|
182
|
+
return {
|
|
183
|
+
system: {
|
|
184
|
+
kind: "object",
|
|
185
|
+
type: { "/": type }
|
|
186
|
+
},
|
|
187
|
+
meta: { keys: keys ?? [] },
|
|
188
|
+
data: data ?? {}
|
|
189
|
+
};
|
|
190
|
+
},
|
|
191
|
+
makeRelation: ({ type, source, target, deleted, keys, data }) => {
|
|
192
|
+
return {
|
|
193
|
+
system: {
|
|
194
|
+
kind: "relation",
|
|
195
|
+
type: { "/": type },
|
|
196
|
+
source,
|
|
197
|
+
target,
|
|
198
|
+
deleted: deleted ?? false
|
|
199
|
+
},
|
|
200
|
+
meta: { keys: keys ?? [] },
|
|
201
|
+
data: data ?? {}
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
makeType: ({ type, keys, data }) => {
|
|
205
|
+
return {
|
|
206
|
+
system: {
|
|
207
|
+
kind: "type",
|
|
208
|
+
type: { "/": type }
|
|
209
|
+
},
|
|
210
|
+
meta: { keys: keys ?? [] },
|
|
211
|
+
data: data ?? {}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
/**
|
|
216
|
+
* Id property name.
|
|
217
|
+
*/
|
|
218
|
+
var PROPERTY_ID = "id";
|
|
219
|
+
/**
|
|
220
|
+
* Data namespace.
|
|
221
|
+
* The key on {@link EntityStructure} that contains the user-defined data.
|
|
222
|
+
*/
|
|
223
|
+
var DATA_NAMESPACE = "data";
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/edge-peer.ts
|
|
226
|
+
/**
|
|
227
|
+
* Returns true if the given peerId belongs to an EDGE replicator (Automerge or Subduction).
|
|
228
|
+
*
|
|
229
|
+
* When `spaceId` is provided, the match is scoped to that space (peerId must start with
|
|
230
|
+
* `<service>:<spaceId>`). When omitted, only the leading service segment is checked
|
|
231
|
+
* (peerId must start with `<service>:`), which is useful when the caller doesn't have a
|
|
232
|
+
* spaceId on hand or wants to match any edge replicator regardless of space.
|
|
233
|
+
*/
|
|
234
|
+
var isEdgePeerId = (peerId, spaceId) => {
|
|
235
|
+
const automergePrefix = spaceId !== void 0 ? compositeKey(EdgeService.AUTOMERGE_REPLICATOR, spaceId) : `${EdgeService.AUTOMERGE_REPLICATOR}:`;
|
|
236
|
+
const subductionPrefix = spaceId !== void 0 ? compositeKey(EdgeService.SUBDUCTION_REPLICATOR, spaceId) : `${EdgeService.SUBDUCTION_REPLICATOR}:`;
|
|
237
|
+
return peerId.startsWith(automergePrefix) || peerId.startsWith(subductionPrefix);
|
|
238
|
+
};
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/echo-feed-codec.ts
|
|
241
|
+
/** Property name for meta when object is serialized to JSON. Matches @dxos/echo/internal ATTR_META. */
|
|
242
|
+
var ATTR_META = "@meta";
|
|
243
|
+
/**
|
|
244
|
+
* Codec for ECHO objects in feed block payload: JSON object ↔ UTF-8 bytes.
|
|
245
|
+
* Encodes with queue position stripped; decodes with optional position injection.
|
|
246
|
+
*/
|
|
247
|
+
var EchoFeedCodec = class EchoFeedCodec {
|
|
248
|
+
static #encoder = new TextEncoder();
|
|
249
|
+
static #decoder = new TextDecoder();
|
|
250
|
+
/**
|
|
251
|
+
* Feed blocks are always whole-object snapshots; the index collapses entries by id to the latest
|
|
252
|
+
* block. TODO(wittjosiah): Follow-up — a partial-object update block format with field-level
|
|
253
|
+
* last-write-wins merge at the index (see EntityMetaIndex.update).
|
|
254
|
+
*/
|
|
255
|
+
static encode(value) {
|
|
256
|
+
const prepared = EchoFeedCodec.stripQueuePosition(value);
|
|
257
|
+
return EchoFeedCodec.#encoder.encode(JSON.stringify(prepared));
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Decodes feed block bytes to a JSON value.
|
|
261
|
+
* If position is provided, injects queue position into the decoded object's metadata.
|
|
262
|
+
*/
|
|
263
|
+
static decode(data, position) {
|
|
264
|
+
const decoded = JSON.parse(EchoFeedCodec.#decoder.decode(data));
|
|
265
|
+
if (position !== void 0 && typeof decoded === "object" && decoded !== null) EchoFeedCodec.#setQueuePosition(decoded, position);
|
|
266
|
+
return decoded;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Strips the queue-position foreign key from an object's metadata, producing a canonical form
|
|
270
|
+
* comparable across a local snapshot and an inbound feed block (positions differ per-append).
|
|
271
|
+
*/
|
|
272
|
+
static stripQueuePosition(value) {
|
|
273
|
+
if (typeof value !== "object" || value === null) return value;
|
|
274
|
+
const obj = structuredClone(value);
|
|
275
|
+
const meta = obj[ATTR_META];
|
|
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;
|
|
278
|
+
}
|
|
279
|
+
static #setQueuePosition(obj, position) {
|
|
280
|
+
obj[ATTR_META] ??= { keys: [] };
|
|
281
|
+
obj[ATTR_META].keys ??= [];
|
|
282
|
+
const keys = obj[ATTR_META].keys;
|
|
283
|
+
for (let i = 0; i < keys.length; i++) if (keys[i].source === FeedProtocol.KEY_QUEUE_POSITION) {
|
|
284
|
+
keys.splice(i, 1);
|
|
285
|
+
i--;
|
|
286
|
+
}
|
|
287
|
+
keys.push({
|
|
288
|
+
source: FeedProtocol.KEY_QUEUE_POSITION,
|
|
289
|
+
id: position.toString()
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
/**
|
|
294
|
+
* Foreign-key source for the global position a feed block was assigned.
|
|
295
|
+
* Re-exported so `@dxos/echo` can read it without depending on `@dxos/protocols`.
|
|
296
|
+
*/
|
|
297
|
+
var KEY_QUEUE_POSITION = FeedProtocol.KEY_QUEUE_POSITION;
|
|
298
|
+
/**
|
|
299
|
+
* Reference to an object in a foreign database.
|
|
300
|
+
*/
|
|
301
|
+
var ForeignKey = Schema.Struct({
|
|
302
|
+
/**
|
|
303
|
+
* Name of the foreign database/system.
|
|
304
|
+
* E.g., `github.com`.
|
|
305
|
+
*/
|
|
306
|
+
source: Schema.String,
|
|
307
|
+
/**
|
|
308
|
+
* Id within the foreign database.
|
|
309
|
+
*/
|
|
310
|
+
id: Schema.String.annotations({ [SchemaAST.IdentifierAnnotationId]: "false" })
|
|
311
|
+
});
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/query/ast.ts
|
|
314
|
+
var ast_exports = /* @__PURE__ */ __exportAll({
|
|
315
|
+
FeedScope: () => FeedScope,
|
|
316
|
+
Filter: () => Filter,
|
|
317
|
+
FilterAnd: () => FilterAnd,
|
|
318
|
+
FilterChildOf: () => FilterChildOf,
|
|
319
|
+
FilterCompare: () => FilterCompare,
|
|
320
|
+
FilterContains: () => FilterContains,
|
|
321
|
+
FilterIn: () => FilterIn,
|
|
322
|
+
FilterInQuery: () => FilterInQuery,
|
|
323
|
+
FilterNot: () => FilterNot,
|
|
324
|
+
FilterObject: () => FilterObject,
|
|
325
|
+
FilterOr: () => FilterOr,
|
|
326
|
+
FilterRange: () => FilterRange,
|
|
327
|
+
FilterTag: () => FilterTag,
|
|
328
|
+
FilterTextSearch: () => FilterTextSearch,
|
|
329
|
+
FilterTimestamp: () => FilterTimestamp,
|
|
330
|
+
GroupAggregate: () => GroupAggregate,
|
|
331
|
+
Order: () => Order,
|
|
332
|
+
OrderDirection: () => OrderDirection,
|
|
333
|
+
Query: () => Query,
|
|
334
|
+
QueryAggregateClause: () => QueryAggregateClause,
|
|
335
|
+
QueryFilterClause: () => QueryFilterClause,
|
|
336
|
+
QueryFromClause: () => QueryFromClause,
|
|
337
|
+
QueryFromClause_: () => QueryFromClause_,
|
|
338
|
+
QueryHierarchyTraversalClause: () => QueryHierarchyTraversalClause,
|
|
339
|
+
QueryIncomingReferencesClause: () => QueryIncomingReferencesClause,
|
|
340
|
+
QueryLimitClause: () => QueryLimitClause,
|
|
341
|
+
QueryOptions: () => QueryOptions,
|
|
342
|
+
QueryOptionsClause: () => QueryOptionsClause,
|
|
343
|
+
QueryOrderClause: () => QueryOrderClause,
|
|
344
|
+
QueryReferenceTraversalClause: () => QueryReferenceTraversalClause,
|
|
345
|
+
QueryRelationClause: () => QueryRelationClause,
|
|
346
|
+
QueryRelationTraversalClause: () => QueryRelationTraversalClause,
|
|
347
|
+
QuerySelectClause: () => QuerySelectClause,
|
|
348
|
+
QuerySetDifferenceClause: () => QuerySetDifferenceClause,
|
|
349
|
+
QuerySkipClause: () => QuerySkipClause,
|
|
350
|
+
QueryUnionClause: () => QueryUnionClause,
|
|
351
|
+
RegistryScope: () => RegistryScope,
|
|
352
|
+
Scope: () => Scope,
|
|
353
|
+
SpaceScope: () => SpaceScope,
|
|
354
|
+
fold: () => fold,
|
|
355
|
+
map: () => map,
|
|
356
|
+
visit: () => visit
|
|
357
|
+
});
|
|
358
|
+
var TypenameSpecifier = Schema.Union(URI.Schema, Schema.Null);
|
|
359
|
+
var FilterObject = Schema.Struct({
|
|
360
|
+
type: Schema.Literal("object"),
|
|
361
|
+
typename: TypenameSpecifier,
|
|
362
|
+
id: Schema.optional(Schema.Array(EntityId)),
|
|
363
|
+
/**
|
|
364
|
+
* Filter by property.
|
|
365
|
+
* Must not include object ID.
|
|
366
|
+
*/
|
|
367
|
+
props: Schema.Record({
|
|
368
|
+
key: Schema.String.annotations({ description: "Property name" }),
|
|
369
|
+
value: Schema.suspend(() => Filter)
|
|
370
|
+
}),
|
|
371
|
+
/**
|
|
372
|
+
* Objects that have any of the given foreign keys.
|
|
373
|
+
*/
|
|
374
|
+
foreignKeys: Schema.optional(Schema.Array(ForeignKey)),
|
|
375
|
+
/**
|
|
376
|
+
* Match objects whose meta `key` equals this fully-qualified registry key (FQN format).
|
|
377
|
+
*/
|
|
378
|
+
metaKey: Schema.optional(Schema.String),
|
|
379
|
+
/**
|
|
380
|
+
* Semver range matched against the object's meta `version`.
|
|
381
|
+
* Only consulted when {@link metaKey} is set. Objects with no `version` do not satisfy a version-constrained filter.
|
|
382
|
+
*/
|
|
383
|
+
metaVersion: Schema.optional(Schema.String)
|
|
384
|
+
});
|
|
385
|
+
var FilterCompare = Schema.Struct({
|
|
386
|
+
type: Schema.Literal("compare"),
|
|
387
|
+
operator: Schema.Literal("eq", "neq", "gt", "gte", "lt", "lte"),
|
|
388
|
+
value: Schema.Unknown
|
|
389
|
+
});
|
|
390
|
+
var FilterIn = Schema.Struct({
|
|
391
|
+
type: Schema.Literal("in"),
|
|
392
|
+
values: Schema.Array(Schema.Any)
|
|
393
|
+
});
|
|
394
|
+
var FilterInQuery = Schema.Struct({
|
|
395
|
+
type: Schema.Literal("in-query"),
|
|
396
|
+
subquery: Schema.suspend(() => Query),
|
|
397
|
+
property: Schema.String
|
|
398
|
+
});
|
|
399
|
+
/**
|
|
400
|
+
* Predicate for an array property to contain the provided value.
|
|
401
|
+
* Nested objects are matched using strict structural matching.
|
|
402
|
+
*/
|
|
403
|
+
var FilterContains = Schema.Struct({
|
|
404
|
+
type: Schema.Literal("contains"),
|
|
405
|
+
value: Schema.Any
|
|
406
|
+
});
|
|
407
|
+
var FilterTag = Schema.Struct({
|
|
408
|
+
type: Schema.Literal("tag"),
|
|
409
|
+
tag: Schema.String
|
|
410
|
+
});
|
|
411
|
+
var FilterRange = Schema.Struct({
|
|
412
|
+
type: Schema.Literal("range"),
|
|
413
|
+
from: Schema.Any,
|
|
414
|
+
to: Schema.Any
|
|
415
|
+
});
|
|
416
|
+
var FilterTimestamp = Schema.Struct({
|
|
417
|
+
type: Schema.Literal("timestamp"),
|
|
418
|
+
field: Schema.Literal("createdAt", "updatedAt"),
|
|
419
|
+
operator: Schema.Literal("gt", "gte", "lt", "lte"),
|
|
420
|
+
value: Schema.Number
|
|
421
|
+
});
|
|
422
|
+
var FilterTextSearch = Schema.Struct({
|
|
423
|
+
type: Schema.Literal("text-search"),
|
|
424
|
+
text: Schema.String,
|
|
425
|
+
searchKind: Schema.optional(Schema.Literal("full-text", "vector"))
|
|
426
|
+
});
|
|
427
|
+
var FilterNot = Schema.Struct({
|
|
428
|
+
type: Schema.Literal("not"),
|
|
429
|
+
filter: Schema.suspend(() => Filter)
|
|
430
|
+
});
|
|
431
|
+
var FilterAnd = Schema.Struct({
|
|
432
|
+
type: Schema.Literal("and"),
|
|
433
|
+
filters: Schema.Array(Schema.suspend(() => Filter))
|
|
434
|
+
});
|
|
435
|
+
var FilterOr = Schema.Struct({
|
|
436
|
+
type: Schema.Literal("or"),
|
|
437
|
+
filters: Schema.Array(Schema.suspend(() => Filter))
|
|
438
|
+
});
|
|
439
|
+
var FilterChildOf = Schema.Struct({
|
|
440
|
+
type: Schema.Literal("child-of"),
|
|
441
|
+
/** Parent DXNs to match children of. */
|
|
442
|
+
parents: Schema.Array(EID.Schema),
|
|
443
|
+
/** Whether to match transitively (grandchildren, etc.). Defaults to true. */
|
|
444
|
+
transitive: Schema.Boolean
|
|
445
|
+
});
|
|
446
|
+
/**
|
|
447
|
+
* Union of filters.
|
|
448
|
+
*/
|
|
449
|
+
var Filter = Schema.Union(FilterObject, FilterCompare, FilterIn, FilterInQuery, FilterContains, FilterTag, FilterRange, FilterTimestamp, FilterTextSearch, FilterChildOf, FilterNot, FilterAnd, FilterOr).annotations({ identifier: "org.dxos.schema.filter" });
|
|
450
|
+
var QuerySelectClause = Schema.Struct({
|
|
451
|
+
type: Schema.Literal("select"),
|
|
452
|
+
filter: Schema.suspend(() => Filter)
|
|
453
|
+
});
|
|
454
|
+
var QueryFilterClause = Schema.Struct({
|
|
455
|
+
type: Schema.Literal("filter"),
|
|
456
|
+
selection: Schema.suspend(() => Query),
|
|
457
|
+
filter: Schema.suspend(() => Filter)
|
|
458
|
+
});
|
|
459
|
+
var QueryReferenceTraversalClause = Schema.Struct({
|
|
460
|
+
type: Schema.Literal("reference-traversal"),
|
|
461
|
+
anchor: Schema.suspend(() => Query),
|
|
462
|
+
property: Schema.String
|
|
463
|
+
});
|
|
464
|
+
var QueryIncomingReferencesClause = Schema.Struct({
|
|
465
|
+
type: Schema.Literal("incoming-references"),
|
|
466
|
+
anchor: Schema.suspend(() => Query),
|
|
467
|
+
/**
|
|
468
|
+
* Property path where the reference is located.
|
|
469
|
+
* If null, matches references from any property.
|
|
470
|
+
*/
|
|
471
|
+
property: Schema.NullOr(Schema.String),
|
|
472
|
+
typename: TypenameSpecifier
|
|
473
|
+
});
|
|
474
|
+
var QueryRelationClause = Schema.Struct({
|
|
475
|
+
type: Schema.Literal("relation"),
|
|
476
|
+
anchor: Schema.suspend(() => Query),
|
|
477
|
+
/**
|
|
478
|
+
* outgoing: anchor is the source of the relation.
|
|
479
|
+
* incoming: anchor is the target of the relation.
|
|
480
|
+
* both: anchor is either the source or target of the relation.
|
|
481
|
+
*/
|
|
482
|
+
direction: Schema.Literal("outgoing", "incoming", "both"),
|
|
483
|
+
filter: Schema.optional(Schema.suspend(() => Filter))
|
|
484
|
+
});
|
|
485
|
+
var QueryRelationTraversalClause = Schema.Struct({
|
|
486
|
+
type: Schema.Literal("relation-traversal"),
|
|
487
|
+
anchor: Schema.suspend(() => Query),
|
|
488
|
+
direction: Schema.Literal("source", "target", "both")
|
|
489
|
+
});
|
|
490
|
+
var QueryHierarchyTraversalClause = Schema.Struct({
|
|
491
|
+
type: Schema.Literal("hierarchy-traversal"),
|
|
492
|
+
anchor: Schema.suspend(() => Query),
|
|
493
|
+
/**
|
|
494
|
+
* to-parent: traverse from child to parent.
|
|
495
|
+
* to-children: traverse from parent to children.
|
|
496
|
+
*/
|
|
497
|
+
direction: Schema.Literal("to-parent", "to-children")
|
|
498
|
+
});
|
|
499
|
+
var QueryUnionClause = Schema.Struct({
|
|
500
|
+
type: Schema.Literal("union"),
|
|
501
|
+
queries: Schema.Array(Schema.suspend(() => Query))
|
|
502
|
+
});
|
|
503
|
+
var QuerySetDifferenceClause = Schema.Struct({
|
|
504
|
+
type: Schema.Literal("set-difference"),
|
|
505
|
+
source: Schema.suspend(() => Query),
|
|
506
|
+
exclude: Schema.suspend(() => Query)
|
|
507
|
+
});
|
|
508
|
+
var OrderDirection = Schema.Literal("asc", "desc");
|
|
509
|
+
var Order = Schema.Union(Schema.Struct({
|
|
510
|
+
kind: Schema.Literal("natural"),
|
|
511
|
+
direction: OrderDirection
|
|
512
|
+
}), Schema.Struct({
|
|
513
|
+
kind: Schema.Literal("property"),
|
|
514
|
+
property: Schema.String,
|
|
515
|
+
direction: OrderDirection
|
|
516
|
+
}), Schema.Struct({
|
|
517
|
+
kind: Schema.Literal("rank"),
|
|
518
|
+
direction: OrderDirection
|
|
519
|
+
}), Schema.Struct({
|
|
520
|
+
kind: Schema.Literal("timestamp"),
|
|
521
|
+
field: Schema.Literal("createdAt", "updatedAt"),
|
|
522
|
+
direction: OrderDirection
|
|
523
|
+
}));
|
|
524
|
+
var QueryOrderClause = Schema.Struct({
|
|
525
|
+
type: Schema.Literal("order"),
|
|
526
|
+
query: Schema.suspend(() => Query),
|
|
527
|
+
order: Schema.Array(Order)
|
|
528
|
+
});
|
|
529
|
+
var QueryOptionsClause = Schema.Struct({
|
|
530
|
+
type: Schema.Literal("options"),
|
|
531
|
+
query: Schema.suspend(() => Query),
|
|
532
|
+
options: Schema.suspend(() => QueryOptions)
|
|
533
|
+
});
|
|
534
|
+
var QueryLimitClause = Schema.Struct({
|
|
535
|
+
type: Schema.Literal("limit"),
|
|
536
|
+
query: Schema.suspend(() => Query),
|
|
537
|
+
limit: Schema.Number
|
|
538
|
+
});
|
|
539
|
+
var QuerySkipClause = Schema.Struct({
|
|
540
|
+
type: Schema.Literal("skip"),
|
|
541
|
+
query: Schema.suspend(() => Query),
|
|
542
|
+
skip: Schema.Number
|
|
543
|
+
});
|
|
544
|
+
/**
|
|
545
|
+
* A named aggregate computed per group over its members, exposed as a top-level field on the flat
|
|
546
|
+
* result record (`row[name]`) and orderable via a following `orderBy(Order.property(name))`. A
|
|
547
|
+
* tagged union per kind — `property`/`limit`/`order` are present exactly when the kind uses them,
|
|
548
|
+
* so read sites narrow by `kind` instead of guarding an unused optional field.
|
|
549
|
+
* - `group` partitions members by a scalar `property`; its coerced key value is the field's value.
|
|
550
|
+
* Composite keys are formed from multiple `group` entries. A query with no `group` entries
|
|
551
|
+
* aggregates its entire input into a single row.
|
|
552
|
+
* - `max`/`min` reduce a scalar member `property`.
|
|
553
|
+
* - `items` collects the group's members, optionally ordered by `order` and capped to `limit`.
|
|
554
|
+
* Opt-in — a row carries no members otherwise. `order` is this aggregate's own per-group
|
|
555
|
+
* ordering, independent of any `orderBy` clause elsewhere in the query (which orders the whole
|
|
556
|
+
* input stream / the resulting groups, not this aggregate's member selection).
|
|
557
|
+
* - `count` yields the member count. Opt-in — a row carries no count otherwise.
|
|
558
|
+
*/
|
|
559
|
+
var GroupAggregateGroup_ = Schema.Struct({
|
|
560
|
+
name: Schema.String,
|
|
561
|
+
kind: Schema.Literal("group"),
|
|
562
|
+
property: Schema.String
|
|
563
|
+
});
|
|
564
|
+
var GroupAggregateMax_ = Schema.Struct({
|
|
565
|
+
name: Schema.String,
|
|
566
|
+
kind: Schema.Literal("max"),
|
|
567
|
+
property: Schema.String
|
|
568
|
+
});
|
|
569
|
+
var GroupAggregateMin_ = Schema.Struct({
|
|
570
|
+
name: Schema.String,
|
|
571
|
+
kind: Schema.Literal("min"),
|
|
572
|
+
property: Schema.String
|
|
573
|
+
});
|
|
574
|
+
var GroupAggregateItems_ = Schema.Struct({
|
|
575
|
+
name: Schema.String,
|
|
576
|
+
kind: Schema.Literal("items"),
|
|
577
|
+
limit: Schema.optional(Schema.Number),
|
|
578
|
+
order: Schema.optional(Schema.Array(Order))
|
|
579
|
+
});
|
|
580
|
+
var GroupAggregateCount_ = Schema.Struct({
|
|
581
|
+
name: Schema.String,
|
|
582
|
+
kind: Schema.Literal("count")
|
|
583
|
+
});
|
|
584
|
+
var GroupAggregate = Schema.Union(GroupAggregateGroup_, GroupAggregateMax_, GroupAggregateMin_, GroupAggregateItems_, GroupAggregateCount_);
|
|
585
|
+
var QueryAggregateClause = Schema.Struct({
|
|
586
|
+
type: Schema.Literal("aggregate"),
|
|
587
|
+
query: Schema.suspend(() => Query),
|
|
588
|
+
aggregates: Schema.Array(GroupAggregate)
|
|
589
|
+
});
|
|
590
|
+
var QueryFromClause_ = Schema.Struct({
|
|
591
|
+
type: Schema.Literal("from"),
|
|
592
|
+
query: Schema.suspend(() => Query),
|
|
593
|
+
from: Schema.Union(Schema.TaggedStruct("scope", { scopes: Schema.Array(Schema.suspend(() => Scope)) }), Schema.TaggedStruct("query", { query: Schema.suspend(() => Query) }))
|
|
594
|
+
});
|
|
595
|
+
var QueryFromClause = QueryFromClause_;
|
|
596
|
+
var Query = Schema.Union(QuerySelectClause, QueryFilterClause, QueryReferenceTraversalClause, QueryIncomingReferencesClause, QueryRelationClause, QueryRelationTraversalClause, QueryHierarchyTraversalClause, QueryUnionClause, QuerySetDifferenceClause, QueryOrderClause, QueryOptionsClause, QueryLimitClause, QuerySkipClause, QueryAggregateClause, QueryFromClause).annotations({ identifier: "org.dxos.schema.query" });
|
|
597
|
+
var QueryOptions = Schema.Struct({
|
|
598
|
+
/**
|
|
599
|
+
* Nested select statements will use this option to filter deleted objects.
|
|
600
|
+
*/
|
|
601
|
+
deleted: Schema.optional(Schema.Literal("include", "exclude", "only")),
|
|
602
|
+
/**
|
|
603
|
+
* Diagnostics-only label for logs / tooling (not used by execution semantics).
|
|
604
|
+
*/
|
|
605
|
+
debugLabel: Schema.optional(Schema.String)
|
|
606
|
+
});
|
|
607
|
+
/**
|
|
608
|
+
* Selects from a space (automerge documents).
|
|
609
|
+
* When `spaceId` is omitted, targets the owning space — i.e. the space of whichever
|
|
610
|
+
* database executes the query. This lets callers reference "this space" without
|
|
611
|
+
* having to look up its id.
|
|
612
|
+
* When `includeAllFeeds` is true, also selects from all feeds belonging to that space.
|
|
613
|
+
*/
|
|
614
|
+
var SpaceScope = Schema.TaggedStruct("space", {
|
|
615
|
+
spaceId: Schema.optional(Schema.String),
|
|
616
|
+
includeAllFeeds: Schema.optional(Schema.Boolean)
|
|
617
|
+
});
|
|
618
|
+
/**
|
|
619
|
+
* Selects from a specific feed (by its underlying queue DXN).
|
|
620
|
+
*/
|
|
621
|
+
var FeedScope = Schema.TaggedStruct("feed", { feedUri: Schema.String });
|
|
622
|
+
/**
|
|
623
|
+
* Selects from a code-shipped object registry.
|
|
624
|
+
*
|
|
625
|
+
* - `'local'` — the in-process registry attached to the hypergraph.
|
|
626
|
+
* - `'remote'` — a future remote registry service (not yet implemented).
|
|
627
|
+
*
|
|
628
|
+
* To include both, add two separate `RegistryScope` entries to the `scopes` array.
|
|
629
|
+
*/
|
|
630
|
+
var RegistryScope = Schema.TaggedStruct("registry", { location: Schema.Literal("local", "remote") });
|
|
631
|
+
/**
|
|
632
|
+
* Specifies the scope of the data to query from.
|
|
633
|
+
* A `from` clause may carry multiple scopes; results are unioned across them.
|
|
634
|
+
*/
|
|
635
|
+
var Scope = Schema.Union(SpaceScope, FeedScope, RegistryScope);
|
|
636
|
+
var visit = (query, visitor) => {
|
|
637
|
+
visitor(query);
|
|
638
|
+
Match.value(query).pipe(Match.when({ type: "filter" }, ({ selection }) => visit(selection, visitor)), Match.when({ type: "reference-traversal" }, ({ anchor }) => visit(anchor, visitor)), Match.when({ type: "incoming-references" }, ({ anchor }) => visit(anchor, visitor)), Match.when({ type: "relation" }, ({ anchor }) => visit(anchor, visitor)), Match.when({ type: "options" }, ({ query }) => visit(query, visitor)), Match.when({ type: "relation-traversal" }, ({ anchor }) => visit(anchor, visitor)), Match.when({ type: "hierarchy-traversal" }, ({ anchor }) => visit(anchor, visitor)), Match.when({ type: "union" }, ({ queries }) => queries.forEach((q) => visit(q, visitor))), Match.when({ type: "set-difference" }, ({ source, exclude }) => {
|
|
639
|
+
visit(source, visitor);
|
|
640
|
+
visit(exclude, visitor);
|
|
641
|
+
}), Match.when({ type: "order" }, ({ query }) => visit(query, visitor)), Match.when({ type: "limit" }, ({ query }) => visit(query, visitor)), Match.when({ type: "skip" }, ({ query }) => visit(query, visitor)), Match.when({ type: "aggregate" }, ({ query }) => visit(query, visitor)), Match.when({ type: "from" }, (node) => {
|
|
642
|
+
visit(node.query, visitor);
|
|
643
|
+
if (node.from._tag === "query") visit(node.from.query, visitor);
|
|
644
|
+
}), Match.when({ type: "select" }, () => {}), Match.exhaustive);
|
|
645
|
+
};
|
|
646
|
+
/**
|
|
647
|
+
* Recursively transforms a query tree bottom-up.
|
|
648
|
+
* The mapper receives each node with its children already transformed.
|
|
649
|
+
*/
|
|
650
|
+
var map = (query, mapper) => {
|
|
651
|
+
return mapper(Match.value(query).pipe(Match.when({ type: "filter" }, (node) => ({
|
|
652
|
+
...node,
|
|
653
|
+
selection: map(node.selection, mapper)
|
|
654
|
+
})), Match.when({ type: "reference-traversal" }, (node) => ({
|
|
655
|
+
...node,
|
|
656
|
+
anchor: map(node.anchor, mapper)
|
|
657
|
+
})), Match.when({ type: "incoming-references" }, (node) => ({
|
|
658
|
+
...node,
|
|
659
|
+
anchor: map(node.anchor, mapper)
|
|
660
|
+
})), Match.when({ type: "relation" }, (node) => ({
|
|
661
|
+
...node,
|
|
662
|
+
anchor: map(node.anchor, mapper)
|
|
663
|
+
})), Match.when({ type: "relation-traversal" }, (node) => ({
|
|
664
|
+
...node,
|
|
665
|
+
anchor: map(node.anchor, mapper)
|
|
666
|
+
})), Match.when({ type: "hierarchy-traversal" }, (node) => ({
|
|
667
|
+
...node,
|
|
668
|
+
anchor: map(node.anchor, mapper)
|
|
669
|
+
})), Match.when({ type: "options" }, (node) => ({
|
|
670
|
+
...node,
|
|
671
|
+
query: map(node.query, mapper)
|
|
672
|
+
})), Match.when({ type: "order" }, (node) => ({
|
|
673
|
+
...node,
|
|
674
|
+
query: map(node.query, mapper)
|
|
675
|
+
})), Match.when({ type: "limit" }, (node) => ({
|
|
676
|
+
...node,
|
|
677
|
+
query: map(node.query, mapper)
|
|
678
|
+
})), Match.when({ type: "skip" }, (node) => ({
|
|
679
|
+
...node,
|
|
680
|
+
query: map(node.query, mapper)
|
|
681
|
+
})), Match.when({ type: "aggregate" }, (node) => ({
|
|
682
|
+
...node,
|
|
683
|
+
query: map(node.query, mapper)
|
|
684
|
+
})), Match.when({ type: "from" }, (node) => ({
|
|
685
|
+
...node,
|
|
686
|
+
query: map(node.query, mapper),
|
|
687
|
+
...node.from._tag === "query" ? { from: {
|
|
688
|
+
_tag: "query",
|
|
689
|
+
query: map(node.from.query, mapper)
|
|
690
|
+
} } : {}
|
|
691
|
+
})), Match.when({ type: "union" }, (node) => ({
|
|
692
|
+
...node,
|
|
693
|
+
queries: node.queries.map((q) => map(q, mapper))
|
|
694
|
+
})), Match.when({ type: "set-difference" }, (node) => ({
|
|
695
|
+
...node,
|
|
696
|
+
source: map(node.source, mapper),
|
|
697
|
+
exclude: map(node.exclude, mapper)
|
|
698
|
+
})), Match.when({ type: "select" }, (node) => node), Match.exhaustive));
|
|
699
|
+
};
|
|
700
|
+
var fold = (query, reducer) => {
|
|
701
|
+
return Match.value(query).pipe(Match.withReturnType(), Match.when({ type: "filter" }, ({ selection }) => fold(selection, reducer)), Match.when({ type: "reference-traversal" }, ({ anchor }) => fold(anchor, reducer)), Match.when({ type: "incoming-references" }, ({ anchor }) => fold(anchor, reducer)), Match.when({ type: "relation" }, ({ anchor }) => fold(anchor, reducer)), Match.when({ type: "options" }, ({ query }) => fold(query, reducer)), Match.when({ type: "relation-traversal" }, ({ anchor }) => fold(anchor, reducer)), Match.when({ type: "hierarchy-traversal" }, ({ anchor }) => fold(anchor, reducer)), Match.when({ type: "union" }, ({ queries }) => queries.flatMap((q) => fold(q, reducer))), Match.when({ type: "set-difference" }, ({ source, exclude }) => fold(source, reducer).concat(fold(exclude, reducer))), Match.when({ type: "order" }, ({ query }) => fold(query, reducer)), Match.when({ type: "limit" }, ({ query }) => fold(query, reducer)), Match.when({ type: "skip" }, ({ query }) => fold(query, reducer)), Match.when({ type: "aggregate" }, ({ query }) => fold(query, reducer)), Match.when({ type: "from" }, (node) => {
|
|
702
|
+
const results = fold(node.query, reducer);
|
|
703
|
+
if (node.from._tag === "query") return results.concat(fold(node.from.query, reducer));
|
|
704
|
+
return results;
|
|
705
|
+
}), Match.when({ type: "select" }, () => []), Match.exhaustive);
|
|
706
|
+
};
|
|
707
|
+
//#endregion
|
|
708
|
+
//#region src/space-doc-version.ts
|
|
709
|
+
var SpaceDocVersion = Object.freeze({
|
|
710
|
+
/**
|
|
711
|
+
* For the documents created before the versioning was introduced.
|
|
712
|
+
*/
|
|
713
|
+
LEGACY: 0,
|
|
714
|
+
/**
|
|
715
|
+
* Current version.
|
|
716
|
+
*/
|
|
717
|
+
CURRENT: 1
|
|
718
|
+
});
|
|
719
|
+
//#endregion
|
|
720
|
+
export { DATA_NAMESPACE, DatabaseDirectory, EchoFeedCodec, EncodedReference, EntityStructure, ForeignKey, KEY_QUEUE_POSITION, PROPERTY_ID, ast_exports as QueryAST, REFERENCE_TYPE_TAG, SpaceDocVersion, createIdFromSpaceKey, isEdgePeerId, isEncodedReference };
|
|
721
|
+
|
|
722
|
+
//# sourceMappingURL=index.mjs.map
|