@dxos/echo-protocol 0.8.4-main.ead640a → 0.8.4-main.effb148878
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/{browser → neutral}/index.mjs +306 -198
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/types/src/document-structure.d.ts +40 -9
- package/dist/types/src/document-structure.d.ts.map +1 -1
- package/dist/types/src/echo-feed-codec.d.ts +17 -0
- package/dist/types/src/echo-feed-codec.d.ts.map +1 -0
- package/dist/types/src/edge-peer.d.ts +11 -0
- package/dist/types/src/edge-peer.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +5 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +150 -36
- package/dist/types/src/query/ast.d.ts.map +1 -1
- package/dist/types/src/reference.d.ts +11 -64
- package/dist/types/src/reference.d.ts.map +1 -1
- package/dist/types/src/space-id.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -15
- package/src/document-structure.ts +55 -10
- package/src/echo-feed-codec.ts +67 -0
- package/src/edge-peer.ts +27 -0
- package/src/index.ts +5 -3
- package/src/query/ast.ts +201 -15
- package/src/reference.ts +12 -157
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -613
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
package/package.json
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-protocol",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.effb148878",
|
|
4
4
|
"description": "Core ECHO APIs.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
-
"repository":
|
|
8
|
-
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
9
12
|
"author": "DXOS.org",
|
|
10
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
11
14
|
"type": "module",
|
|
12
15
|
"exports": {
|
|
13
16
|
".": {
|
|
14
17
|
"source": "./src/index.ts",
|
|
15
18
|
"types": "./dist/types/src/index.d.ts",
|
|
16
|
-
"
|
|
17
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
19
|
+
"default": "./dist/lib/neutral/index.mjs"
|
|
18
20
|
}
|
|
19
21
|
},
|
|
20
22
|
"types": "dist/types/src/index.d.ts",
|
|
21
|
-
"typesVersions": {
|
|
22
|
-
"*": {}
|
|
23
|
-
},
|
|
24
23
|
"files": [
|
|
25
24
|
"dist",
|
|
26
25
|
"src"
|
|
27
26
|
],
|
|
28
27
|
"dependencies": {
|
|
29
|
-
"effect": "3.
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
28
|
+
"effect": "3.20.0",
|
|
29
|
+
"@dxos/invariant": "0.8.4-main.effb148878",
|
|
30
|
+
"@dxos/util": "0.8.4-main.effb148878",
|
|
31
|
+
"@dxos/protocols": "0.8.4-main.effb148878",
|
|
32
|
+
"@dxos/crypto": "0.8.4-main.effb148878",
|
|
33
|
+
"@dxos/keys": "0.8.4-main.effb148878"
|
|
35
34
|
},
|
|
36
35
|
"publishConfig": {
|
|
37
36
|
"access": "public"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { invariant } from '@dxos/invariant';
|
|
6
|
-
import type {
|
|
6
|
+
import type { ObjectId, URI } from '@dxos/keys';
|
|
7
7
|
import { visitValues } from '@dxos/util';
|
|
8
8
|
|
|
9
9
|
import { type RawString } from './automerge';
|
|
@@ -41,7 +41,7 @@ export interface DatabaseDirectory {
|
|
|
41
41
|
* Object id points to an automerge doc url where the object is embedded.
|
|
42
42
|
*/
|
|
43
43
|
links?: {
|
|
44
|
-
[
|
|
44
|
+
[echoUri: string]: string | RawString;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -119,9 +119,9 @@ export const ObjectStructure = Object.freeze({
|
|
|
119
119
|
/**
|
|
120
120
|
* @throws On invalid object structure.
|
|
121
121
|
*/
|
|
122
|
-
getEntityKind: (object: ObjectStructure): 'object' | 'relation' => {
|
|
122
|
+
getEntityKind: (object: ObjectStructure): 'object' | 'relation' | 'type' => {
|
|
123
123
|
const kind = object.system?.kind ?? 'object';
|
|
124
|
-
invariant(kind === 'object' || kind === 'relation', 'Invalid kind');
|
|
124
|
+
invariant(kind === 'object' || kind === 'relation' || kind === 'type', 'Invalid kind');
|
|
125
125
|
return kind;
|
|
126
126
|
},
|
|
127
127
|
|
|
@@ -137,6 +137,10 @@ export const ObjectStructure = Object.freeze({
|
|
|
137
137
|
return object.system?.target;
|
|
138
138
|
},
|
|
139
139
|
|
|
140
|
+
getParent: (object: ObjectStructure): EncodedReference | undefined => {
|
|
141
|
+
return object.system?.parent;
|
|
142
|
+
},
|
|
143
|
+
|
|
140
144
|
/**
|
|
141
145
|
* @returns All references in the data section of the object.
|
|
142
146
|
*/
|
|
@@ -162,7 +166,7 @@ export const ObjectStructure = Object.freeze({
|
|
|
162
166
|
data,
|
|
163
167
|
keys,
|
|
164
168
|
}: {
|
|
165
|
-
type:
|
|
169
|
+
type: URI.URI;
|
|
166
170
|
deleted?: boolean;
|
|
167
171
|
keys?: ForeignKey[];
|
|
168
172
|
data?: unknown;
|
|
@@ -187,7 +191,7 @@ export const ObjectStructure = Object.freeze({
|
|
|
187
191
|
keys,
|
|
188
192
|
data,
|
|
189
193
|
}: {
|
|
190
|
-
type:
|
|
194
|
+
type: URI.URI;
|
|
191
195
|
source: EncodedReference;
|
|
192
196
|
target: EncodedReference;
|
|
193
197
|
deleted?: boolean;
|
|
@@ -208,6 +212,19 @@ export const ObjectStructure = Object.freeze({
|
|
|
208
212
|
data: data ?? {},
|
|
209
213
|
};
|
|
210
214
|
},
|
|
215
|
+
|
|
216
|
+
makeType: ({ type, keys, data }: { type: URI.URI; keys?: ForeignKey[]; data?: unknown }): ObjectStructure => {
|
|
217
|
+
return {
|
|
218
|
+
system: {
|
|
219
|
+
kind: 'type',
|
|
220
|
+
type: { '/': type },
|
|
221
|
+
},
|
|
222
|
+
meta: {
|
|
223
|
+
keys: keys ?? [],
|
|
224
|
+
},
|
|
225
|
+
data: data ?? {},
|
|
226
|
+
};
|
|
227
|
+
},
|
|
211
228
|
});
|
|
212
229
|
|
|
213
230
|
/**
|
|
@@ -226,6 +243,18 @@ export type ObjectMeta = {
|
|
|
226
243
|
* NOTE: Optional for backwards compatibilty.
|
|
227
244
|
*/
|
|
228
245
|
tags?: string[];
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Fully-qualified registry key for the object (FQN format, e.g. `org.example.type.foo`).
|
|
249
|
+
* Identifies the canonical registry entry the object instance was created from.
|
|
250
|
+
*/
|
|
251
|
+
key?: string;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Semantic version of the registry entry the object was created from.
|
|
255
|
+
* Must be a valid semver string (e.g. `1.2.3`).
|
|
256
|
+
*/
|
|
257
|
+
version?: string;
|
|
229
258
|
};
|
|
230
259
|
|
|
231
260
|
/**
|
|
@@ -234,12 +263,22 @@ export type ObjectMeta = {
|
|
|
234
263
|
*/
|
|
235
264
|
export type ObjectSystem = {
|
|
236
265
|
/**
|
|
237
|
-
* Entity kind.
|
|
266
|
+
* Entity kind. `'type'` covers persisted ECHO type definitions (instances of
|
|
267
|
+
* the `Type.Type` meta-schema); `'object'` / `'relation'` cover regular ECHO
|
|
268
|
+
* instances.
|
|
238
269
|
*/
|
|
239
|
-
kind?: 'object' | 'relation';
|
|
270
|
+
kind?: 'object' | 'relation' | 'type';
|
|
240
271
|
|
|
241
272
|
/**
|
|
242
|
-
* Object reference ('protobuf' protocol) type
|
|
273
|
+
* Object reference ('protobuf' protocol) type — DXN of the schema this
|
|
274
|
+
* entity instantiates.
|
|
275
|
+
*
|
|
276
|
+
* - For `kind === 'object'` / `'relation'` instances, this is the URI of the
|
|
277
|
+
* user-defined schema the entity was created from (e.g. `dxn:type:org.example.Person:1.0.0`).
|
|
278
|
+
* - For `kind === 'type'` entities (persisted Type.Type meta-instances) this
|
|
279
|
+
* is always the URI of the `TypeSchema` meta-schema itself
|
|
280
|
+
* (`dxn:org.dxos.type.schema:0.1.0`). The kind that the meta-instance
|
|
281
|
+
* _describes_ (object/relation/type) lives in `data.jsonSchema.entityKind`.
|
|
243
282
|
*/
|
|
244
283
|
type?: EncodedReference;
|
|
245
284
|
|
|
@@ -248,13 +287,19 @@ export type ObjectSystem = {
|
|
|
248
287
|
*/
|
|
249
288
|
deleted?: boolean;
|
|
250
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Object parent.
|
|
292
|
+
* Objects with no parent are at the top level of the object hierarchy in the space.
|
|
293
|
+
*/
|
|
294
|
+
parent?: EncodedReference;
|
|
295
|
+
|
|
251
296
|
/**
|
|
252
297
|
* Only for relations.
|
|
253
298
|
*/
|
|
254
299
|
source?: EncodedReference;
|
|
255
300
|
|
|
256
301
|
/**
|
|
257
|
-
* Only for relations.
|
|
302
|
+
* Only for relations.
|
|
258
303
|
*/
|
|
259
304
|
target?: EncodedReference;
|
|
260
305
|
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { FeedProtocol } from '@dxos/protocols';
|
|
6
|
+
|
|
7
|
+
import type { ForeignKey } from './foreign-key';
|
|
8
|
+
|
|
9
|
+
/** Property name for meta when object is serialized to JSON. Matches @dxos/echo/internal ATTR_META. */
|
|
10
|
+
const ATTR_META = '@meta';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Codec for ECHO objects in feed block payload: JSON object ↔ UTF-8 bytes.
|
|
14
|
+
* Encodes with queue position stripped; decodes with optional position injection.
|
|
15
|
+
*/
|
|
16
|
+
export class EchoFeedCodec {
|
|
17
|
+
static readonly #encoder = new TextEncoder();
|
|
18
|
+
static readonly #decoder = new TextDecoder();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Prepares a value for feed storage (strips queue position from metadata) and encodes to bytes.
|
|
22
|
+
*/
|
|
23
|
+
static encode(value: Record<string, unknown>): Uint8Array {
|
|
24
|
+
const prepared = EchoFeedCodec.#stripQueuePosition(value);
|
|
25
|
+
return EchoFeedCodec.#encoder.encode(JSON.stringify(prepared));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Decodes feed block bytes to a JSON value.
|
|
30
|
+
* If position is provided, injects queue position into the decoded object's metadata.
|
|
31
|
+
*/
|
|
32
|
+
static decode(data: Uint8Array, position?: number): Record<string, unknown> {
|
|
33
|
+
const decoded = JSON.parse(EchoFeedCodec.#decoder.decode(data));
|
|
34
|
+
if (position !== undefined && typeof decoded === 'object' && decoded !== null) {
|
|
35
|
+
EchoFeedCodec.#setQueuePosition(decoded, position);
|
|
36
|
+
}
|
|
37
|
+
return decoded;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static #stripQueuePosition(value: Record<string, unknown>): Record<string, unknown> {
|
|
41
|
+
if (typeof value !== 'object' || value === null) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
const obj = structuredClone(value);
|
|
45
|
+
const meta = obj[ATTR_META] as { keys?: ForeignKey[] } | undefined;
|
|
46
|
+
if (meta?.keys?.some((key: ForeignKey) => key.source === FeedProtocol.KEY_QUEUE_POSITION)) {
|
|
47
|
+
meta.keys = meta.keys.filter((key: ForeignKey) => key.source !== FeedProtocol.KEY_QUEUE_POSITION);
|
|
48
|
+
}
|
|
49
|
+
return obj;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static #setQueuePosition(obj: Record<string, any>, position: number): void {
|
|
53
|
+
obj[ATTR_META] ??= { keys: [] };
|
|
54
|
+
obj[ATTR_META]!.keys ??= [];
|
|
55
|
+
const keys = obj[ATTR_META]!.keys!;
|
|
56
|
+
for (let i = 0; i < keys.length; i++) {
|
|
57
|
+
if (keys[i].source === FeedProtocol.KEY_QUEUE_POSITION) {
|
|
58
|
+
keys.splice(i, 1);
|
|
59
|
+
i--;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
keys.push({
|
|
63
|
+
source: FeedProtocol.KEY_QUEUE_POSITION,
|
|
64
|
+
id: position.toString(),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/edge-peer.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type SpaceId } from '@dxos/keys';
|
|
6
|
+
import { EdgeService } from '@dxos/protocols';
|
|
7
|
+
import { compositeKey } from '@dxos/util';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns true if the given peerId belongs to an EDGE replicator (Automerge or Subduction).
|
|
11
|
+
*
|
|
12
|
+
* When `spaceId` is provided, the match is scoped to that space (peerId must start with
|
|
13
|
+
* `<service>:<spaceId>`). When omitted, only the leading service segment is checked
|
|
14
|
+
* (peerId must start with `<service>:`), which is useful when the caller doesn't have a
|
|
15
|
+
* spaceId on hand or wants to match any edge replicator regardless of space.
|
|
16
|
+
*/
|
|
17
|
+
export const isEdgePeerId = (peerId: string, spaceId?: SpaceId): boolean => {
|
|
18
|
+
const automergePrefix =
|
|
19
|
+
spaceId !== undefined
|
|
20
|
+
? compositeKey(EdgeService.AUTOMERGE_REPLICATOR, spaceId)
|
|
21
|
+
: `${EdgeService.AUTOMERGE_REPLICATOR}:`;
|
|
22
|
+
const subductionPrefix =
|
|
23
|
+
spaceId !== undefined
|
|
24
|
+
? compositeKey(EdgeService.SUBDUCTION_REPLICATOR, spaceId)
|
|
25
|
+
: `${EdgeService.SUBDUCTION_REPLICATOR}:`;
|
|
26
|
+
return peerId.startsWith(automergePrefix) || peerId.startsWith(subductionPrefix);
|
|
27
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export type * from './collection-sync';
|
|
5
6
|
export * from './document-structure';
|
|
7
|
+
export * from './edge-peer';
|
|
8
|
+
export * from './echo-feed-codec';
|
|
9
|
+
export * from './foreign-key';
|
|
10
|
+
export * from './query';
|
|
6
11
|
export * from './reference';
|
|
7
12
|
export * from './space-doc-version';
|
|
8
|
-
export type * from './collection-sync';
|
|
9
13
|
export * from './space-id';
|
|
10
|
-
export * from './foreign-key';
|
|
11
|
-
export * from './query';
|