@dxos/echo 0.8.4-main.03d5cd7b56 → 0.8.4-main.05e74ebcff

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.
Files changed (95) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/neutral/Database.mjs +1 -1
  4. package/dist/lib/neutral/Entity.mjs +6 -6
  5. package/dist/lib/neutral/Feed.mjs +14 -12
  6. package/dist/lib/neutral/Filter.mjs +9 -7
  7. package/dist/lib/neutral/JsonSchema.mjs +4 -4
  8. package/dist/lib/neutral/Migration.mjs +6 -6
  9. package/dist/lib/neutral/Obj.mjs +8 -8
  10. package/dist/lib/neutral/Query.mjs +12 -12
  11. package/dist/lib/neutral/Ref.mjs +6 -4
  12. package/dist/lib/neutral/Relation.mjs +9 -9
  13. package/dist/lib/neutral/Tag.mjs +10 -10
  14. package/dist/lib/neutral/Type.mjs +6 -6
  15. package/dist/lib/neutral/{chunk-6VIJV543.mjs → chunk-APHSOTIX.mjs} +2 -2
  16. package/dist/lib/neutral/{chunk-RF7ZBZ4A.mjs → chunk-APJKDGFL.mjs} +16 -5
  17. package/dist/lib/neutral/chunk-APJKDGFL.mjs.map +7 -0
  18. package/dist/lib/neutral/{chunk-MPAI2MHO.mjs → chunk-BMB7IHGB.mjs} +2 -2
  19. package/dist/lib/neutral/{chunk-SUZMWP3Y.mjs → chunk-FIWO2FZK.mjs} +1 -1
  20. package/dist/lib/neutral/chunk-FIWO2FZK.mjs.map +7 -0
  21. package/dist/lib/neutral/{chunk-SJKBWMJY.mjs → chunk-G54OX4IX.mjs} +34 -18
  22. package/dist/lib/neutral/chunk-G54OX4IX.mjs.map +7 -0
  23. package/dist/lib/neutral/{chunk-FHYIM4RD.mjs → chunk-I2DARWPX.mjs} +1 -1
  24. package/dist/lib/neutral/chunk-I2DARWPX.mjs.map +7 -0
  25. package/dist/lib/neutral/{chunk-SW5CUSBY.mjs → chunk-J54QMAKF.mjs} +5 -4
  26. package/dist/lib/neutral/{chunk-SW5CUSBY.mjs.map → chunk-J54QMAKF.mjs.map} +3 -3
  27. package/dist/lib/neutral/{chunk-QXIANHKU.mjs → chunk-MGSQGHOD.mjs} +8 -8
  28. package/dist/lib/neutral/chunk-MGSQGHOD.mjs.map +7 -0
  29. package/dist/lib/neutral/{chunk-O5LRY6CO.mjs → chunk-MLS7U7AT.mjs} +2 -2
  30. package/dist/lib/neutral/{chunk-VYAGNFSJ.mjs → chunk-N7VOEPSV.mjs} +6 -3
  31. package/dist/lib/neutral/{chunk-VYAGNFSJ.mjs.map → chunk-N7VOEPSV.mjs.map} +3 -3
  32. package/dist/lib/neutral/{chunk-FZHVQEHN.mjs → chunk-PSZBLH53.mjs} +2 -2
  33. package/dist/lib/neutral/{chunk-WRVRDZDA.mjs → chunk-PT37DG2F.mjs} +4 -4
  34. package/dist/lib/neutral/{chunk-HPNQTEEQ.mjs → chunk-Q2KKKJSV.mjs} +3 -3
  35. package/dist/lib/neutral/{chunk-VGKLHHRT.mjs → chunk-Q7ZL2P5H.mjs} +18 -16
  36. package/dist/lib/neutral/{chunk-VGKLHHRT.mjs.map → chunk-Q7ZL2P5H.mjs.map} +3 -3
  37. package/dist/lib/neutral/{chunk-QGMIH2SN.mjs → chunk-QRZ2I3ZM.mjs} +2 -2
  38. package/dist/lib/neutral/{chunk-LVGOVFDV.mjs → chunk-SCPFDS2E.mjs} +2 -2
  39. package/dist/lib/neutral/{chunk-S7IMFVTB.mjs → chunk-ZFACXBY6.mjs} +19 -15
  40. package/dist/lib/neutral/chunk-ZFACXBY6.mjs.map +7 -0
  41. package/dist/lib/neutral/index.mjs +17 -17
  42. package/dist/lib/neutral/internal/index.mjs +5 -5
  43. package/dist/lib/neutral/meta.json +1 -1
  44. package/dist/lib/neutral/testing/index.mjs +17 -17
  45. package/dist/types/src/Database.d.ts +1 -1
  46. package/dist/types/src/Feed.d.ts +36 -12
  47. package/dist/types/src/Feed.d.ts.map +1 -1
  48. package/dist/types/src/Filter.d.ts +21 -0
  49. package/dist/types/src/Filter.d.ts.map +1 -1
  50. package/dist/types/src/Hypergraph.d.ts +3 -3
  51. package/dist/types/src/Hypergraph.d.ts.map +1 -1
  52. package/dist/types/src/Migration.d.ts +15 -3
  53. package/dist/types/src/Migration.d.ts.map +1 -1
  54. package/dist/types/src/Obj.d.ts +1 -1
  55. package/dist/types/src/Obj.d.ts.map +1 -1
  56. package/dist/types/src/Query.d.ts.map +1 -1
  57. package/dist/types/src/Ref.d.ts +1 -0
  58. package/dist/types/src/Ref.d.ts.map +1 -1
  59. package/dist/types/src/internal/Entity/model.d.ts +2 -0
  60. package/dist/types/src/internal/Entity/model.d.ts.map +1 -1
  61. package/dist/types/src/internal/Obj/ids.d.ts +1 -1
  62. package/dist/types/src/internal/Query.d.ts.map +1 -1
  63. package/dist/types/src/internal/common/types/meta.d.ts +10 -0
  64. package/dist/types/src/internal/common/types/meta.d.ts.map +1 -1
  65. package/dist/types/tsconfig.tsbuildinfo +1 -1
  66. package/package.json +14 -14
  67. package/src/Database.ts +1 -1
  68. package/src/Feed.ts +55 -24
  69. package/src/Filter.ts +31 -0
  70. package/src/Hypergraph.ts +3 -3
  71. package/src/Migration.ts +19 -7
  72. package/src/Obj.ts +1 -0
  73. package/src/Query.test.ts +16 -16
  74. package/src/Query.ts +12 -7
  75. package/src/Ref.ts +2 -0
  76. package/src/internal/Entity/model.ts +2 -0
  77. package/src/internal/Obj/ids.ts +1 -1
  78. package/src/internal/Obj/json-serializer.ts +9 -9
  79. package/src/internal/Query.ts +23 -4
  80. package/src/internal/common/proxy/reactive.test.ts +1 -1
  81. package/src/internal/common/types/meta.ts +12 -0
  82. package/dist/lib/neutral/chunk-FHYIM4RD.mjs.map +0 -7
  83. package/dist/lib/neutral/chunk-QXIANHKU.mjs.map +0 -7
  84. package/dist/lib/neutral/chunk-RF7ZBZ4A.mjs.map +0 -7
  85. package/dist/lib/neutral/chunk-S7IMFVTB.mjs.map +0 -7
  86. package/dist/lib/neutral/chunk-SJKBWMJY.mjs.map +0 -7
  87. package/dist/lib/neutral/chunk-SUZMWP3Y.mjs.map +0 -7
  88. /package/dist/lib/neutral/{chunk-6VIJV543.mjs.map → chunk-APHSOTIX.mjs.map} +0 -0
  89. /package/dist/lib/neutral/{chunk-MPAI2MHO.mjs.map → chunk-BMB7IHGB.mjs.map} +0 -0
  90. /package/dist/lib/neutral/{chunk-O5LRY6CO.mjs.map → chunk-MLS7U7AT.mjs.map} +0 -0
  91. /package/dist/lib/neutral/{chunk-FZHVQEHN.mjs.map → chunk-PSZBLH53.mjs.map} +0 -0
  92. /package/dist/lib/neutral/{chunk-WRVRDZDA.mjs.map → chunk-PT37DG2F.mjs.map} +0 -0
  93. /package/dist/lib/neutral/{chunk-HPNQTEEQ.mjs.map → chunk-Q2KKKJSV.mjs.map} +0 -0
  94. /package/dist/lib/neutral/{chunk-QGMIH2SN.mjs.map → chunk-QRZ2I3ZM.mjs.map} +0 -0
  95. /package/dist/lib/neutral/{chunk-LVGOVFDV.mjs.map → chunk-SCPFDS2E.mjs.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo",
3
- "version": "0.8.4-main.03d5cd7b56",
3
+ "version": "0.8.4-main.05e74ebcff",
4
4
  "description": "ECHO API",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -8,7 +8,7 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/dxos/dxos"
10
10
  },
11
- "license": "MIT",
11
+ "license": "FSL-1.1-Apache-2.0",
12
12
  "author": "info@dxos.org",
13
13
  "sideEffects": false,
14
14
  "type": "module",
@@ -135,18 +135,18 @@
135
135
  ],
136
136
  "dependencies": {
137
137
  "effect": "3.20.0",
138
- "@dxos/context": "0.8.4-main.03d5cd7b56",
139
- "@dxos/debug": "0.8.4-main.03d5cd7b56",
140
- "@dxos/errors": "0.8.4-main.03d5cd7b56",
141
- "@dxos/echo-protocol": "0.8.4-main.03d5cd7b56",
142
- "@dxos/effect": "0.8.4-main.03d5cd7b56",
143
- "@dxos/invariant": "0.8.4-main.03d5cd7b56",
144
- "@dxos/async": "0.8.4-main.03d5cd7b56",
145
- "@dxos/keys": "0.8.4-main.03d5cd7b56",
146
- "@dxos/log": "0.8.4-main.03d5cd7b56",
147
- "@dxos/protocols": "0.8.4-main.03d5cd7b56",
148
- "@dxos/node-std": "0.8.4-main.03d5cd7b56",
149
- "@dxos/util": "0.8.4-main.03d5cd7b56"
138
+ "@dxos/async": "0.8.4-main.05e74ebcff",
139
+ "@dxos/context": "0.8.4-main.05e74ebcff",
140
+ "@dxos/debug": "0.8.4-main.05e74ebcff",
141
+ "@dxos/echo-protocol": "0.8.4-main.05e74ebcff",
142
+ "@dxos/errors": "0.8.4-main.05e74ebcff",
143
+ "@dxos/invariant": "0.8.4-main.05e74ebcff",
144
+ "@dxos/keys": "0.8.4-main.05e74ebcff",
145
+ "@dxos/log": "0.8.4-main.05e74ebcff",
146
+ "@dxos/node-std": "0.8.4-main.05e74ebcff",
147
+ "@dxos/protocols": "0.8.4-main.05e74ebcff",
148
+ "@dxos/util": "0.8.4-main.05e74ebcff",
149
+ "@dxos/effect": "0.8.4-main.05e74ebcff"
150
150
  },
151
151
  "publishConfig": {
152
152
  "access": "public"
package/src/Database.ts CHANGED
@@ -38,7 +38,7 @@ export interface QueryFn {
38
38
  }
39
39
 
40
40
  /**
41
- * Common interface for Database and Queue.
41
+ * Common interface for Database, Feed, and Hypergraph.
42
42
  */
43
43
  export interface Queryable {
44
44
  query: QueryFn;
package/src/Feed.ts CHANGED
@@ -10,7 +10,7 @@ import * as Layer from 'effect/Layer';
10
10
  import type * as Option from 'effect/Option';
11
11
  import * as Schema from 'effect/Schema';
12
12
 
13
- import { DXN, type ObjectId } from '@dxos/keys';
13
+ import { DXN } from '@dxos/keys';
14
14
 
15
15
  import * as Annotation from './Annotation';
16
16
  import type * as Entity from './Entity';
@@ -80,6 +80,16 @@ export interface RetentionOptions {
80
80
  cursor?: string;
81
81
  }
82
82
 
83
+ /**
84
+ * Sync options for a feed.
85
+ */
86
+ export interface SyncOptions {
87
+ /** Push local changes to the server. Defaults to true. */
88
+ shouldPush?: boolean;
89
+ /** Pull remote changes from the server. Defaults to true. */
90
+ shouldPull?: boolean;
91
+ }
92
+
83
93
  //
84
94
  // Factory
85
95
  //
@@ -109,27 +119,6 @@ export const getQueueDxn = (feed: Feed): DXN | undefined => {
109
119
  return new DXN(DXN.kind.QUEUE, [feed.namespace ?? 'data', self.spaceId, self.echoId]);
110
120
  };
111
121
 
112
- /**
113
- * Creates a Feed object from a queue DXN, inferring the feed's id and namespace from the DXN parts.
114
- *
115
- * The resulting Feed, when added to the same space as the queue, will have a queue DXN
116
- * equal to the input (see `Feed.getQueueDxn`). Useful when migrating `Ref(Queue)` fields to
117
- * `Ref(Feed.Feed)`.
118
- *
119
- * @remarks Unsafe because the caller must ensure the queue DXN's space matches the database
120
- * the feed is added to; the feed id is set from the queue id, bypassing id generation.
121
- */
122
- export const unsafeFromQueueDXN = (queueDxn: DXN): Feed => {
123
- const parts = queueDxn.asQueueDXN();
124
- if (!parts) {
125
- throw new Error(`Expected a queue DXN, got: ${queueDxn.toString()}`);
126
- }
127
- return Obj.make(Feed, {
128
- id: parts.queueId as ObjectId,
129
- namespace: parts.subspaceTag === 'trace' ? 'trace' : undefined,
130
- });
131
- };
132
-
133
122
  //
134
123
  // Service
135
124
  //
@@ -160,6 +149,11 @@ export class FeedService extends Context.Tag('@dxos/echo/Feed/FeedService')<
160
149
  <Q extends Query.Any>(feed: Feed, query: Q): QueryResult.QueryResult<Query.Type<Q>>;
161
150
  <F extends Filter.Any>(feed: Feed, filter: F): QueryResult.QueryResult<Filter.Type<F>>;
162
151
  };
152
+
153
+ /**
154
+ * Syncs the feed with the server.
155
+ */
156
+ sync(feed: Feed, options?: SyncOptions): Promise<void>;
163
157
  }
164
158
  >() {}
165
159
 
@@ -187,8 +181,30 @@ export const notAvailable: Layer.Layer<FeedService> = Layer.succeed(FeedService,
187
181
  query: () => {
188
182
  throw new Error('Feed.FeedService not available');
189
183
  },
184
+ sync: () => {
185
+ throw new Error('Feed.FeedService not available');
186
+ },
190
187
  } as Context.Tag.Service<FeedService>);
191
188
 
189
+ //
190
+ // Context (per-call) service
191
+ //
192
+
193
+ /**
194
+ * Effect service exposing a single `Feed.Feed` as the "current" feed for a call site.
195
+ *
196
+ * @deprecated Prefer threading a `Feed.Feed` explicitly through function signatures
197
+ * over hiding it behind a context service.
198
+ */
199
+ export class ContextFeedService extends Context.Tag('@dxos/echo/Feed/ContextFeedService')<
200
+ ContextFeedService,
201
+ {
202
+ readonly feed: Feed;
203
+ }
204
+ >() {
205
+ static layer = (feed: Feed): Layer.Layer<ContextFeedService> => Layer.succeed(ContextFeedService, { feed });
206
+ }
207
+
192
208
  //
193
209
  // Operations
194
210
  //
@@ -262,6 +278,21 @@ export const runQuery: {
262
278
  } = (feed: Feed, queryOrFilter: Query.Any | Filter.Any) =>
263
279
  query(feed, queryOrFilter as any).pipe(Effect.flatMap((queryResult) => Effect.promise(() => queryResult.run())));
264
280
 
281
+ /**
282
+ * Syncs the feed with the server.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * yield* Feed.sync(feed);
287
+ * yield* Feed.sync(feed, { shouldPush: false });
288
+ * ```
289
+ */
290
+ export const sync = (feed: Feed, options?: SyncOptions): Effect.Effect<void, never, FeedService> =>
291
+ Effect.gen(function* () {
292
+ const service = yield* FeedService;
293
+ yield* Effect.promise(() => service.sync(feed, options));
294
+ });
295
+
265
296
  /**
266
297
  * Creates a cursor for iterating over feed items.
267
298
  * Currently stubbed — cursor operations are not yet implemented.
@@ -292,13 +323,13 @@ export const nextOption = <T = Obj.Snapshot>(_cursor: Cursor<T>): Effect.Effect<
292
323
 
293
324
  /**
294
325
  * Sets the local retention policy for a feed.
295
- * Currently stubbed — queues do not yet support retention.
326
+ * Currently stubbed — feeds do not yet support retention.
296
327
  *
297
328
  * @example
298
329
  * ```ts
299
330
  * yield* Feed.setRetention(feed, { count: 1000 });
300
331
  * ```
301
332
  */
302
- // TODO(feed): Implement when queue retention is supported.
333
+ // TODO(dmaretskyi): Implement when feed retention is supported.
303
334
  export const setRetention = (_feed: Feed, _options: RetentionOptions): Effect.Effect<void, never, FeedService> =>
304
335
  Effect.void;
package/src/Filter.ts CHANGED
@@ -158,6 +158,37 @@ export const tag = (tag: string): Any => {
158
158
  });
159
159
  };
160
160
 
161
+ /**
162
+ * Options for {@link key} filter.
163
+ */
164
+ export type KeyFilterOptions = {
165
+ /**
166
+ * Optional semver range expression (e.g. `^1.2.3`, `~2.0.0`, `>=1.0.0 <2.0.0`).
167
+ * Matches the object's meta `version` field against the range.
168
+ * If omitted, matches any version (including objects with no version).
169
+ */
170
+ version?: string;
171
+ };
172
+
173
+ /**
174
+ * Filter by registry key stored in object meta.
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * Filter.key('org.example.type.foo');
179
+ * Filter.key('org.example.type.foo', { version: '^1.2.3' });
180
+ * ```
181
+ */
182
+ export const key = (key: string, options?: KeyFilterOptions): Any => {
183
+ return new FilterClass({
184
+ type: 'object',
185
+ typename: null,
186
+ props: {},
187
+ metaKey: key,
188
+ metaVersion: options?.version,
189
+ });
190
+ };
191
+
161
192
  /**
162
193
  * Filter by properties.
163
194
  */
package/src/Hypergraph.ts CHANGED
@@ -22,10 +22,10 @@ export interface RefResolutionContext {
22
22
  space?: Key.SpaceId;
23
23
 
24
24
  /**
25
- * Queue that the resolution is happening from.
26
- * This queue will be searched first, and then the space it belongs to.
25
+ * Feed that the resolution is happening from (as the underlying queue DXN).
26
+ * This feed will be searched first, and then the space it belongs to.
27
27
  */
28
- queue?: DXN;
28
+ feed?: DXN;
29
29
  }
30
30
 
31
31
  export interface RefResolverOptions {
package/src/Migration.ts CHANGED
@@ -10,19 +10,31 @@ import { type DXN } from '@dxos/keys';
10
10
 
11
11
  import type * as Database from './Database';
12
12
  import type * as Entity from './Entity';
13
- import { getSchemaDXN } from './internal';
13
+ import { MetaId, type ObjectMeta, getSchemaDXN } from './internal';
14
+
15
+ /**
16
+ * Result returned by a migration's `transform` callback.
17
+ * The data shape matches the target schema; the optional `[Obj.Meta]` symbol key lets the
18
+ * transform update the object's meta (e.g. `key` / `version`) atomically with the data swap.
19
+ */
20
+ export type TransformResult<To extends Schema.Schema.AnyNoContext> = Omit<
21
+ Schema.Schema.Type<To>,
22
+ 'id' | Entity.KindId
23
+ > & {
24
+ [MetaId]?: Partial<ObjectMeta>;
25
+ };
14
26
 
15
27
  type DefineObjectMigrationOptions<From extends Schema.Schema.AnyNoContext, To extends Schema.Schema.AnyNoContext> = {
16
28
  from: From;
17
29
  to: To;
18
30
  /**
19
31
  * Pure function that converts the old object data to the new object data.
32
+ *
33
+ * The returned object may include an optional `[Obj.Meta]` entry to update the object's meta
34
+ * (e.g. registry `key` / `version`) atomically with the data swap.
20
35
  */
21
36
  // TODO(dmaretskyi): `id` should not be a part of the schema.
22
- transform: (
23
- from: Schema.Schema.Type<From>,
24
- context: ObjectMigrationContext,
25
- ) => Promise<Omit<Schema.Schema.Type<To>, 'id' | Entity.KindId>>;
37
+ transform: (from: Schema.Schema.Type<From>, context: ObjectMigrationContext) => Promise<TransformResult<To>>;
26
38
 
27
39
  /**
28
40
  * Callback that is called after the object is migrated. Called for every object that is migrated.
@@ -30,7 +42,7 @@ type DefineObjectMigrationOptions<From extends Schema.Schema.AnyNoContext, To ex
30
42
  * NOTE: Database mutations performed in this callback are not guaranteed to be idempotent.
31
43
  * If multiple peers run the migration separately, the effects may be applied multiple times.
32
44
  */
33
- onMigration: (params: OnMigrateProps<From, To>) => Promise<void>;
45
+ onMigration?: (params: OnMigrateProps<From, To>) => Promise<void>;
34
46
  };
35
47
 
36
48
  /**
@@ -55,7 +67,7 @@ export type ObjectMigration = {
55
67
  fromSchema: Schema.Schema.AnyNoContext;
56
68
  toSchema: Schema.Schema.AnyNoContext;
57
69
  transform: (from: unknown, context: ObjectMigrationContext) => Promise<unknown>;
58
- onMigration: (params: OnMigrateProps<any, any>) => Promise<void>;
70
+ onMigration?: (params: OnMigrateProps<any, any>) => Promise<void>;
59
71
  };
60
72
 
61
73
  /**
package/src/Obj.ts CHANGED
@@ -664,6 +664,7 @@ export const setParent = (entity: Unknown, parent: Any | undefined) => {
664
664
  assumeType<internal.InternalObjectProps>(entity);
665
665
  assumeType<internal.InternalObjectProps | undefined>(parent);
666
666
  entity[internal.ParentId] = parent;
667
+ return entity;
667
668
  };
668
669
 
669
670
  interface UpdateFromOptions<T> {
package/src/Query.test.ts CHANGED
@@ -523,7 +523,7 @@ describe('query api', () => {
523
523
  "from": {
524
524
  "_tag": "scope",
525
525
  "scope": {
526
- "allQueuesFromSpaces": true,
526
+ "allFeedsFromSpaces": true,
527
527
  },
528
528
  },
529
529
  "query": {
@@ -563,17 +563,17 @@ describe('query api', () => {
563
563
  test('Query.type(...).from(feed) sets queue scope', async () => {
564
564
  const spaceId = SpaceId.random();
565
565
  const feedId = ObjectId.random();
566
- const feedDxn = DXN.parse(`dxn:echo:${spaceId}:${feedId}`);
566
+ const feedDXN = DXN.parse(`dxn:echo:${spaceId}:${feedId}`);
567
567
  const feed = (await Obj.fromJSON(
568
568
  {
569
569
  '@type': 'dxn:type:org.dxos.type.feed:0.1.0',
570
570
  id: feedId,
571
571
  name: 'test-feed',
572
572
  },
573
- { dxn: feedDxn },
573
+ { dxn: feedDXN },
574
574
  )) as Feed.Feed;
575
575
 
576
- const expectedQueueDxn = new DXN(DXN.kind.QUEUE, ['data', spaceId, feedId]);
576
+ const expectedQueueDXN = new DXN(DXN.kind.QUEUE, ['data', spaceId, feedId]);
577
577
 
578
578
  const query = Query.type(TestSchema.Person).from(feed);
579
579
  Schema.validateSync(QueryAST.Query)(query.ast);
@@ -582,7 +582,7 @@ describe('query api', () => {
582
582
  from: {
583
583
  _tag: 'scope',
584
584
  scope: {
585
- queues: [expectedQueueDxn.toString()],
585
+ feeds: [expectedQueueDXN.toString()],
586
586
  },
587
587
  },
588
588
  query: {
@@ -686,13 +686,13 @@ describe('query api', () => {
686
686
 
687
687
  describe('Filter.childOf', () => {
688
688
  test('childOf with Ref', () => {
689
- const parentDxn = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
690
- const parentRef = Ref.fromDXN(parentDxn);
689
+ const parentDXN = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
690
+ const parentRef = Ref.fromDXN(parentDXN);
691
691
  const filter = Filter.childOf(parentRef);
692
692
 
693
693
  expect(filter.ast).toMatchObject({
694
694
  type: 'child-of',
695
- parents: [parentDxn.toString()],
695
+ parents: [parentDXN.toString()],
696
696
  transitive: true,
697
697
  });
698
698
  Schema.validateSync(QueryAST.Filter)(filter.ast);
@@ -736,15 +736,15 @@ describe('query api', () => {
736
736
  });
737
737
 
738
738
  test('childOf in select query', () => {
739
- const parentDxn = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
740
- const parentRef = Ref.fromDXN(parentDxn);
739
+ const parentDXN = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
740
+ const parentRef = Ref.fromDXN(parentDXN);
741
741
  const query = Query.select(Filter.childOf(parentRef));
742
742
 
743
743
  expect(query.ast).toMatchObject({
744
744
  type: 'select',
745
745
  filter: {
746
746
  type: 'child-of',
747
- parents: [parentDxn.toString()],
747
+ parents: [parentDXN.toString()],
748
748
  transitive: true,
749
749
  },
750
750
  });
@@ -752,8 +752,8 @@ describe('query api', () => {
752
752
  });
753
753
 
754
754
  test('childOf combined with type filter', () => {
755
- const parentDxn = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
756
- const parentRef = Ref.fromDXN(parentDxn);
755
+ const parentDXN = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
756
+ const parentRef = Ref.fromDXN(parentDXN);
757
757
  const query = Query.select(Filter.and(Filter.type(TestSchema.Person), Filter.childOf(parentRef)));
758
758
 
759
759
  Schema.validateSync(QueryAST.Query)(query.ast);
@@ -763,7 +763,7 @@ describe('query api', () => {
763
763
  type: 'and',
764
764
  filters: [
765
765
  { type: 'object', typename: 'dxn:type:com.example.type.person:0.1.0' },
766
- { type: 'child-of', parents: [parentDxn.toString()], transitive: true },
766
+ { type: 'child-of', parents: [parentDXN.toString()], transitive: true },
767
767
  ],
768
768
  },
769
769
  });
@@ -779,8 +779,8 @@ describe('query api', () => {
779
779
 
780
780
  test('childOf with mixed objects and Refs', () => {
781
781
  const parent = Obj.make(TestSchema.Person, { name: 'Parent' });
782
- const refDxn = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
783
- const parentRef = Ref.fromDXN(refDxn);
782
+ const refDXN = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
783
+ const parentRef = Ref.fromDXN(refDXN);
784
784
  const filter = Filter.childOf([parent, parentRef]);
785
785
 
786
786
  expect(filter.ast).toMatchObject({
package/src/Query.ts CHANGED
@@ -359,7 +359,7 @@ class QueryClass implements Any {
359
359
  from: {
360
360
  _tag: 'scope',
361
361
  scope: {
362
- ...(options?.includeFeeds ? { allQueuesFromSpaces: true } : {}),
362
+ ...(options?.includeFeeds ? { allFeedsFromSpaces: true } : {}),
363
363
  },
364
364
  },
365
365
  });
@@ -384,7 +384,7 @@ class QueryClass implements Any {
384
384
  _tag: 'scope',
385
385
  scope: {
386
386
  spaceIds: databases.map((db) => db.spaceId),
387
- ...(options?.includeFeeds ? { allQueuesFromSpaces: true } : {}),
387
+ ...(options?.includeFeeds ? { allFeedsFromSpaces: true } : {}),
388
388
  },
389
389
  },
390
390
  });
@@ -410,10 +410,15 @@ class QueryClass implements Any {
410
410
  }
411
411
  }
412
412
 
413
- const feeds = items as Feed.Feed[];
414
- const queueDxns = feeds.flatMap((feed) => {
413
+ const feedItems = items as Feed.Feed[];
414
+ const feedDXNs = feedItems.map((feed) => {
415
415
  const dxn = Feed.getQueueDxn(feed);
416
- return dxn ? [dxn.toString()] : [];
416
+ if (!dxn) {
417
+ throw new TypeError(
418
+ `Query.from() expects persisted Feed objects with a queue DXN; got feed without a space (id=${Obj.getDXN(feed).toString()}).`,
419
+ );
420
+ }
421
+ return dxn.toString();
417
422
  });
418
423
  return new QueryClass({
419
424
  type: 'from',
@@ -421,7 +426,7 @@ class QueryClass implements Any {
421
426
  from: {
422
427
  _tag: 'scope',
423
428
  scope: {
424
- queues: queueDxns,
429
+ feeds: feedDXNs,
425
430
  },
426
431
  },
427
432
  });
@@ -551,7 +556,7 @@ export const from = (
551
556
  return wrapper.from(source as any, options);
552
557
  };
553
558
 
554
- const SCOPE_KEYS = new Set(['spaceIds', 'queues', 'allQueuesFromSpaces']);
559
+ const SCOPE_KEYS = new Set(['spaceIds', 'feeds', 'allFeedsFromSpaces']);
555
560
 
556
561
  /** Detect a raw Scope object (plain object with only Scope-valid keys). */
557
562
  const _isScope = (value: unknown): value is QueryAST.Scope => {
package/src/Ref.ts CHANGED
@@ -85,6 +85,8 @@ export const make = refInternal.Ref.make;
85
85
  // TODO(dmaretskyi): Consider just allowing `make` to accept DXN.
86
86
  export const fromDXN = refInternal.Ref.fromDXN;
87
87
 
88
+ export const hasObjectId = refInternal.Ref.hasObjectId;
89
+
88
90
  // TODO(wittjosiah): Factor out?
89
91
  export const isRefType = (ast: SchemaAST.AST): boolean => {
90
92
  return SchemaAST.getAnnotation<JsonSchema.JsonSchema>(ast, SchemaAST.JSONSchemaAnnotationId).pipe(
@@ -66,6 +66,8 @@ export interface InternalObjectProps {
66
66
  export interface ObjectMetaJSON {
67
67
  keys: ForeignKey[];
68
68
  tags?: string[];
69
+ key?: string;
70
+ version?: string;
69
71
  }
70
72
 
71
73
  /**
@@ -5,7 +5,7 @@
5
5
  import { DXN, ObjectId, QueueSubspaceTags, SpaceId } from '@dxos/keys';
6
6
 
7
7
  /**
8
- * @deprecated Use `db.queues.create()`
8
+ * @deprecated Use `Feed.make(...)` + `db.add(feed)` then `Feed.getQueueDxn(feed)`.
9
9
  */
10
10
  // TODO(burdon): Move to @dxos/keys.
11
11
  export const createQueueDXN = (spaceId = SpaceId.random(), queueId = ObjectId.random()) =>
@@ -116,15 +116,15 @@ export const objectFromJSON = async (
116
116
  const isRelation =
117
117
  typeof jsonData[ATTR_RELATION_SOURCE] === 'string' || typeof jsonData[ATTR_RELATION_TARGET] === 'string';
118
118
  if (isRelation) {
119
- const sourceDxn: DXN = DXN.parse(jsonData[ATTR_RELATION_SOURCE] ?? raise(new TypeError('Missing relation source')));
120
- const targetDxn: DXN = DXN.parse(jsonData[ATTR_RELATION_TARGET] ?? raise(new TypeError('Missing relation target')));
119
+ const sourceDXN: DXN = DXN.parse(jsonData[ATTR_RELATION_SOURCE] ?? raise(new TypeError('Missing relation source')));
120
+ const targetDXN: DXN = DXN.parse(jsonData[ATTR_RELATION_TARGET] ?? raise(new TypeError('Missing relation target')));
121
121
 
122
- const source = (await refResolver?.resolve(sourceDxn)) as AnyEntity | undefined;
123
- const target = (await refResolver?.resolve(targetDxn)) as AnyEntity | undefined;
122
+ const source = (await refResolver?.resolve(sourceDXN)) as AnyEntity | undefined;
123
+ const target = (await refResolver?.resolve(targetDXN)) as AnyEntity | undefined;
124
124
 
125
125
  defineHiddenProperty(obj, KindId, EntityKind.Relation);
126
- defineHiddenProperty(obj, RelationSourceDXNId, sourceDxn);
127
- defineHiddenProperty(obj, RelationTargetDXNId, targetDxn);
126
+ defineHiddenProperty(obj, RelationSourceDXNId, sourceDXN);
127
+ defineHiddenProperty(obj, RelationTargetDXNId, targetDXN);
128
128
  defineHiddenProperty(obj, RelationSourceId, source);
129
129
  defineHiddenProperty(obj, RelationTargetId, target);
130
130
  } else {
@@ -142,8 +142,8 @@ export const objectFromJSON = async (
142
142
  }
143
143
 
144
144
  if (jsonData[ATTR_PARENT]) {
145
- const parentDxn = DXN.parse(jsonData[ATTR_PARENT]);
146
- const resolvedParent = (await refResolver?.resolve(parentDxn)) as Obj.Unknown | undefined;
145
+ const parentDXN = DXN.parse(jsonData[ATTR_PARENT]);
146
+ const resolvedParent = (await refResolver?.resolve(parentDXN)) as Obj.Unknown | undefined;
147
147
  defineHiddenProperty(obj, ParentId, resolvedParent);
148
148
  } else if (parent) {
149
149
  defineHiddenProperty(obj, ParentId, parent);
@@ -184,7 +184,7 @@ const stripInternalJsonKeys = (jsonData: unknown) => {
184
184
  [ATTR_TYPE]: _type,
185
185
  [ATTR_META]: _meta,
186
186
  [ATTR_DELETED]: _deleted,
187
- [ATTR_SELF_DXN]: _selfDxn,
187
+ [ATTR_SELF_DXN]: _selfDXN,
188
188
  [ATTR_RELATION_SOURCE]: _relationSource,
189
189
  [ATTR_RELATION_TARGET]: _relationTarget,
190
190
  ...props
@@ -10,6 +10,18 @@ import { type QueryAST } from '@dxos/echo-protocol';
10
10
  export const prettyFilter = (filter: QueryAST.Filter): string => {
11
11
  switch (filter.type) {
12
12
  case 'object': {
13
+ // A type-less object filter with only a meta-key constraint is `Filter.key(...)`.
14
+ if (
15
+ filter.typename === null &&
16
+ (filter.id === undefined || filter.id.length === 0) &&
17
+ Object.keys(filter.props).length === 0 &&
18
+ (filter.foreignKeys === undefined || filter.foreignKeys.length === 0) &&
19
+ filter.metaKey !== undefined
20
+ ) {
21
+ return filter.metaVersion !== undefined
22
+ ? `Filter.key(${JSON.stringify(filter.metaKey)}, { version: ${JSON.stringify(filter.metaVersion)} })`
23
+ : `Filter.key(${JSON.stringify(filter.metaKey)})`;
24
+ }
13
25
  const parts: string[] = [];
14
26
  if (filter.typename !== null) {
15
27
  parts.push(String(filter.typename));
@@ -25,6 +37,13 @@ export const prettyFilter = (filter: QueryAST.Filter): string => {
25
37
  if (filter.foreignKeys !== undefined && filter.foreignKeys.length > 0) {
26
38
  parts.push(`foreignKeys: [${filter.foreignKeys.map((fk) => JSON.stringify(fk)).join(', ')}]`);
27
39
  }
40
+ if (filter.metaKey !== undefined) {
41
+ parts.push(
42
+ filter.metaVersion !== undefined
43
+ ? `metaKey: ${JSON.stringify(filter.metaKey)} (${filter.metaVersion})`
44
+ : `metaKey: ${JSON.stringify(filter.metaKey)}`,
45
+ );
46
+ }
28
47
  return parts.length > 0 ? `Filter.type(${parts.join(', ')})` : 'Filter.everything()';
29
48
  }
30
49
  case 'compare':
@@ -121,11 +140,11 @@ export const prettyQuery = (query: QueryAST.Query): string => {
121
140
  if (scope.spaceIds !== undefined) {
122
141
  parts.push(`spaceIds: [${scope.spaceIds.map((s) => JSON.stringify(s)).join(', ')}]`);
123
142
  }
124
- if (scope.queues !== undefined) {
125
- parts.push(`queues: [${scope.queues.map(String).join(', ')}]`);
143
+ if (scope.feeds !== undefined) {
144
+ parts.push(`feeds: [${scope.feeds.map(String).join(', ')}]`);
126
145
  }
127
- if (scope.allQueuesFromSpaces !== undefined) {
128
- parts.push(`allQueuesFromSpaces: ${scope.allQueuesFromSpaces}`);
146
+ if (scope.allFeedsFromSpaces !== undefined) {
147
+ parts.push(`allFeedsFromSpaces: ${scope.allFeedsFromSpaces}`);
129
148
  }
130
149
  return `${prettyQuery(query.query)}.from({ ${parts.join(', ')} })`;
131
150
  }
@@ -28,7 +28,7 @@ describe('Obj.subscribe', () => {
28
28
  expect(calls).toBe(seen);
29
29
  });
30
30
 
31
- // Regression: queue-stored typed objects (e.g. ContextBinding) and other Obj-shaped values
31
+ // Regression: queue-stored typed objects (e.g. AiContext.Binding) and other Obj-shaped values
32
32
  // satisfy `Obj.isObject` (KindId is set) but are not wrapped in a reactive Proxy. Earlier
33
33
  // versions invariant'd inside `getProxyTarget` when an atom body did `Obj.subscribe(obj)` on
34
34
  // such an input. The contract is "no-op for non-reactive inputs"; verify the function
@@ -38,6 +38,18 @@ export const ObjectMetaSchema = Schema.Struct({
38
38
  // Defaulting to an empty array is possible but requires a bit more work.
39
39
  // TODO(dmaretskyi): In automerge this should be a map of { [tag]: boolean } for uniqueness and conflict resolution.
40
40
  tags: Schema.optional(Schema.Array(Schema.String)),
41
+
42
+ /**
43
+ * Fully-qualified registry key for the object (FQN format, e.g. `org.example.type.foo`).
44
+ * Identifies the canonical registry entry the object instance was created from.
45
+ */
46
+ key: Schema.optional(Schema.String),
47
+
48
+ /**
49
+ * Semantic version of the registry entry the object was created from.
50
+ * Must be a valid semver string (e.g. `1.2.3`).
51
+ */
52
+ version: Schema.optional(Schema.String),
41
53
  });
42
54
 
43
55
  export type ObjectMeta = Schema.Schema.Type<typeof ObjectMetaSchema>;
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/Database.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport * as Schema from 'effect/Schema';\nimport type * as Types from 'effect/Types';\n\nimport { promiseWithCauseCapture } from '@dxos/effect';\nimport { invariant } from '@dxos/invariant';\nimport { DXN, type SpaceId } from '@dxos/keys';\n\nimport type * as Entity from './Entity';\nimport * as Err from './Err';\nimport type * as Filter from './Filter';\nimport type * as Hypergraph from './Hypergraph';\nimport { isInstanceOf } from './internal/Annotation';\nimport { type AnyProperties } from './internal/common/types';\nimport type { Ref } from './internal/Ref/ref';\nimport type * as Obj from './Obj';\nimport type * as Query from './Query';\nimport type * as QueryResult from './QueryResult';\nimport type * as SchemaRegistry from './SchemaRegistry';\nimport type * as Type from './Type';\n\n/**\n * `query` API function declaration.\n */\n// TODO(burdon): Reconcile Query and Filter (should only have one root type).\nexport interface QueryFn {\n <Q extends Query.Any>(query: Q): QueryResult.QueryResult<Query.Type<Q>>;\n <F extends Filter.Any>(filter: F): QueryResult.QueryResult<Filter.Type<F>>;\n}\n\n/**\n * Common interface for Database and Queue.\n */\nexport interface Queryable {\n query: QueryFn;\n}\n\nexport type GetObjectByIdOptions = {\n deleted?: boolean;\n};\n\nexport type ObjectPlacement = 'root-doc' | 'linked-doc';\n\nexport type AddOptions = {\n /**\n * Where to place the object in the Automerge document tree.\n * Root document is always loaded with the space.\n * Linked documents are loaded lazily.\n * Placing large number of objects in the root document may slow down the initial load.\n *\n * @default 'linked-doc'\n */\n placeIn?: ObjectPlacement;\n};\n\nexport type FlushOptions = {\n /**\n * Write any pending changes to disk.\n * @default true\n */\n disk?: boolean;\n\n /**\n * Wait for pending index updates.\n * @default true\n */\n indexes?: boolean;\n\n /**\n * Flush pending updates to objects and queries.\n * @default false\n */\n updates?: boolean;\n};\n\n/**\n * Identifier denoting an ECHO Database.\n */\nexport const TypeId = Symbol.for('@dxos/echo/Database');\nexport type TypeId = typeof TypeId;\n\n/**\n * ECHO Database interface.\n */\nexport interface Database extends Queryable {\n readonly [TypeId]: TypeId;\n\n get spaceId(): SpaceId;\n\n // TODO(burdon): Can we move this into graph?\n get schemaRegistry(): SchemaRegistry.SchemaRegistry;\n\n get graph(): Hypergraph.Hypergraph;\n\n toJSON(): object;\n\n /**\n * Return object by local ID.\n */\n getObjectById<T extends Obj.Unknown = Obj.OfShape<AnyProperties>>(\n id: string,\n opts?: GetObjectByIdOptions,\n ): T | undefined;\n\n /**\n * Query objects.\n */\n query: QueryFn;\n\n /**\n * Creates a reference to an existing object in the database.\n *\n * NOTE: The reference may be dangling if the object is not present in the database.\n * NOTE: Difference from `Ref.fromDXN`\n * `Ref.fromDXN(dxn)` returns an unhydrated reference. The `.load` and `.target` APIs will not work.\n * `db.makeRef(dxn)` is preferable in cases with access to the database.\n */\n makeRef<T extends Entity.Unknown = Entity.Unknown>(dxn: DXN): Ref<T>;\n\n /**\n * Adds object to the database.\n */\n add<T extends Entity.Unknown = Entity.Unknown>(obj: T, opts?: AddOptions): T;\n\n /**\n * Removes object from the database.\n */\n // TODO(burdon): Return true if removed (currently throws if not present).\n remove(obj: Entity.Unknown): void;\n\n /**\n * Wait for all pending changes to be saved to disk.\n * Optionaly waits for changes to be propagated to indexes and event handlers.\n */\n flush(opts?: FlushOptions): Promise<void>;\n}\n\nexport const isDatabase = (obj: unknown): obj is Database => {\n return obj ? typeof obj === 'object' && TypeId in obj && obj[TypeId] === TypeId : false;\n};\n\nexport const Database: Schema.Schema<Database> = Schema.Any.pipe(Schema.filter((space) => isDatabase(space)));\n\n/**\n * Effect service tag for Database dependency injection.\n */\nexport class Service extends Context.Tag('@dxos/echo/Database/Service')<\n Service,\n {\n readonly db: Database;\n }\n>() {}\n\n/**\n * Layer that provides a Database service that throws when accessed.\n * Useful as a default layer when no database is available.\n */\nexport const notAvailable = Layer.succeed(Service, {\n get db(): Database {\n throw new Error('Database not available');\n },\n});\n\n/**\n * Creates a Database service instance from a Database.\n */\nexport const makeService = (db: Database): Context.Tag.Service<Service> => {\n return {\n get db() {\n return db;\n },\n };\n};\n\n/**\n * Creates a Layer that provides the Database service.\n */\nexport const layer = (db: Database): Layer.Layer<Service> => {\n return Layer.succeed(Service, makeService(db));\n};\n\n/**\n * Returns the space ID of the database.\n */\nexport const spaceId = Effect.gen(function* () {\n const { db } = yield* Service;\n return db.spaceId;\n});\n\n/**\n * Resolves an object by its DXN.\n */\nexport const resolve: {\n // No type check.\n (ref: DXN | Ref<any>): Effect.Effect<Entity.Unknown, never, Service>;\n // Check matches schema.\n <S extends Type.AnyEntity>(\n ref: DXN | Ref<any>,\n schema: S,\n ): Effect.Effect<Schema.Schema.Type<S>, Err.ObjectNotFoundError, Service>;\n} = (<S extends Type.AnyEntity>(\n ref: DXN | Ref<any>,\n schema?: S,\n): Effect.Effect<Schema.Schema.Type<S>, Err.ObjectNotFoundError, Service> =>\n Effect.gen(function* () {\n const { db } = yield* Service;\n const dxn = ref instanceof DXN ? ref : ref.dxn;\n const object = yield* promiseWithCauseCapture(() =>\n db.graph\n .createRefResolver({\n context: {\n space: db.spaceId,\n },\n })\n .resolve(dxn),\n );\n\n if (!object) {\n return yield* Effect.fail(new Err.ObjectNotFoundError(dxn));\n }\n invariant(!schema || isInstanceOf(schema, object), 'Object type mismatch.');\n return object as any;\n }).pipe(Effect.withSpan('Database.resolve'))) as any;\n\n/**\n * Loads an object reference.\n */\nexport const load: <T>(ref: Ref<T>) => Effect.Effect<T, Err.ObjectNotFoundError, never> = Effect.fn('Database.load')(\n function* (ref) {\n const object = yield* promiseWithCauseCapture(() => ref.tryLoad());\n if (!object) {\n return yield* Effect.fail(new Err.ObjectNotFoundError(ref.dxn));\n }\n return object;\n },\n);\n\n/**\n * Loads an object reference option.\n */\n// TODO(dmaretskyi): Do we need this -- you can just use `Effect.catchTag` in calling code instead.\nexport const loadOption: <T>(ref: Ref<T>) => Effect.Effect<Option.Option<T>, never, never> = Effect.fn(\n 'Database.loadOption',\n)(function* (ref) {\n const object = yield* load(ref).pipe(Effect.catchTag('ObjectNotFoundError', () => Effect.succeed(undefined)));\n\n return Option.fromNullable(object);\n});\n\n/**\n * Adds an object to the database.\n * @see {@link Database.add}\n */\nexport const add = <T extends Entity.Unknown>(obj: T): Effect.Effect<T, never, Service> =>\n Service.pipe(Effect.map(({ db }) => db.add(obj))).pipe(Effect.withSpan('Database.add'));\n\n/**\n * Removes an object from the database.\n * @see {@link Database.remove}\n */\nexport const remove = <T extends Entity.Unknown>(obj: T): Effect.Effect<void, never, Service> =>\n Service.pipe(Effect.map(({ db }) => db.remove(obj))).pipe(Effect.withSpan('Database.remove'));\n\n/**\n * Flushes pending changes to disk.\n * @see {@link Database.flush}\n */\nexport const flush = (opts?: FlushOptions) =>\n Service.pipe(Effect.flatMap(({ db }) => promiseWithCauseCapture(() => db.flush(opts)))).pipe(\n Effect.withSpan('Database.flush'),\n );\n\n/**\n * Creates a `QueryResult` object that can be subscribed to.\n */\nexport const query: {\n <Q extends Query.Any>(query: Q): Effect.Effect<QueryResult.QueryResult<Query.Type<Q>>, never, Service>;\n <F extends Filter.Any>(filter: F): Effect.Effect<QueryResult.QueryResult<Filter.Type<F>>, never, Service>;\n} = (queryOrFilter: Query.Any | Filter.Any) =>\n Service.pipe(\n Effect.map(({ db }) => db.query(queryOrFilter as any) as QueryResult.QueryResult<any>),\n Effect.withSpan('Database.query'),\n );\n\n/**\n * Executes the query once and returns the results.\n */\nexport const runQuery: {\n <Q extends Query.Any>(query: Q): Effect.Effect<Query.Type<Q>[], never, Service>;\n <F extends Filter.Any>(filter: F): Effect.Effect<Filter.Type<F>[], never, Service>;\n} = (queryOrFilter: Query.Any | Filter.Any) =>\n query(queryOrFilter as any).pipe(\n Effect.flatMap((queryResult) => promiseWithCauseCapture(() => queryResult.run())),\n Effect.withSpan('Database.runQuery'),\n );\n\n/**\n * Executes the query once and returns the first result as or None.\n */\nexport const runQueryFirst: {\n <Q extends Query.Any>(query: Q): Effect.Effect<Option.Option<Query.Type<Q>>, never, Service>;\n <F extends Filter.Any>(filter: F): Effect.Effect<Option.Option<Filter.Type<F>>, never, Service>;\n} = (queryOrFilter: Query.Any | Filter.Any) =>\n query(queryOrFilter as any).pipe(\n Effect.flatMap((queryResult) =>\n promiseWithCauseCapture(async () => Option.fromNullable(await queryResult.firstOrUndefined())),\n ),\n Effect.withSpan('Database.runQueryFirst'),\n );\n\n/**\n * Persists schemas in the database so they replicate to other clients.\n * @see {@link SchemaRegistry.SchemaRegistry.register}\n */\nexport const registerSchema = (\n input: SchemaRegistry.RegisterSchemaInput[],\n): Effect.Effect<Type.RuntimeType[], never, Service> =>\n Service.pipe(\n Effect.flatMap(({ db }) => promiseWithCauseCapture(() => db.schemaRegistry.register(input))),\n Effect.withSpan('Database.registerSchema'),\n );\n\n/**\n * Creates a schema query result that can be subscribed to.\n */\n// TODO(dmaretskyi): Change API to `yield* Database.querySchema(...).first` and `yield* Database.querySchema(...).schema`.\nexport const schemaQuery = <Q extends Types.NoExcessProperties<SchemaRegistry.Query, Q>>(\n schemaQueryOptions?: Q & SchemaRegistry.Query,\n): Effect.Effect<QueryResult.QueryResult<SchemaRegistry.ExtractQueryResult<Q>>, never, Service> =>\n Service.pipe(\n Effect.map(({ db }) => db.schemaRegistry.query(schemaQueryOptions)),\n Effect.withSpan('Database.schemaQuery'),\n );\n\n/**\n * Executes a schema query once and returns the results.\n */\nexport const runSchemaQuery = <Q extends Types.NoExcessProperties<SchemaRegistry.Query, Q>>(\n schemaQueryOptions?: Q & SchemaRegistry.Query,\n): Effect.Effect<SchemaRegistry.ExtractQueryResult<Q>[], never, Service> =>\n schemaQuery(schemaQueryOptions).pipe(\n Effect.flatMap((queryResult) => promiseWithCauseCapture(() => queryResult.run())),\n );\n"],
5
- "mappings": ";;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;AAMA,YAAYA,aAAa;AACzB,YAAYC,YAAY;AACxB,YAAYC,WAAW;AACvB,YAAYC,YAAY;AACxB,YAAYC,YAAY;AAGxB,SAASC,+BAA+B;AACxC,SAASC,iBAAiB;AAC1B,SAASC,WAAyB;AAqElC,IAAA,eAAA;AA+DSC,IAAM,SAAOA,uBAAQ,IAAA,qBAAsBA;AAClD,IAAA,aAAA,CAAA,QAAA;AAEF,SAAO,MAAMC,OAAoCC,QAAOC,YAASD,UAAc,OAACE,IAAUC,MAAAA,MAAWD,SAAS;AAE9G;;AAUA,IAAA,UAAA,cAAA,YAAA,6BAAA,EAAA,EAAA;;AAMI,IAAM,eAAU,cAAA,SAAA;EAClB,IAAA,KAAA;AACC,UAAA,IAAA,MAAA,wBAAA;EAEH;;AAKI,IAAIE,cAAK,CAAA,OAAA;;IAET,IAAA,KAAA;AACF,aAAA;IACA;EAEF;;AAKE,IAAA,QAAA,CAAA,OAAA;AAEF,SAAA,cAAA,SAAA,YAAA,EAAA,CAAA;;AAKSA,IAAGC,UAAO,WAAA,aAAA;AAChB,QAAA,EAAA,GAAA,IAAA,OAAA;AAEH,SAAA,GAAA;;AAiBI,IAAMC,UAAMC,CAAAA,KAAAA,WAA2BA,WAAID,aAAG;AAC9C,QAAME,EAAAA,GAAAA,IAAS,OAAOC;cAGhBC,eAAS,MAAA,MAAA,IAAA;iBACPR,OAAUG,wBAAO,MAAA,GAAA,MAAA,kBAAA;IACnB,SAAA;MAEDM,OAAQL,GAAAA;IAGRE;EACH,CAAA,EAAA,QAAO,GAAOI,CAAAA;AAChB,MAAA,CAAA,QAAA;AACAC,WAAWC,OAAUC,YAAAA,IAAaD,oBAAiB,GAAA,CAAA;EACnD;AACCE,YAAKJ,CAAOK,UAAS,aAAA,QAA6B,MAAA,GAAA,yBAAA,EAAA,YAAA,YAAA,GAAA,cAAA,GAAA,IAAA,GAAA,MAAA,GAAA,CAAA,2CAAA,yBAAA,EAAA,CAAA;AAEvD,SAAA;;AAMST,IAAQ,OAAA,UAAA,eAAA,EAAA,WAAA,KAAA;QACX,SAAO,OAAOI,wBAAoBM,MAAAA,IAAAA,QAAwBZ,CAAAA;AAC5D,MAAA,CAAA,QAAA;AACA,WAAOE,OAAAA,YAAAA,IAAAA,oBAAAA,IAAAA,GAAAA,CAAAA;EAET;AAEF,SAAA;;AASE,IAAOW,aAAoBX,UAAAA,qBAAAA,EAAAA,WAAAA,KAAAA;AAC1B,QAAA,SAAA,OAAA,KAAA,GAAA,EAAA,KAAA,gBAAA,uBAAA,MAAA,eAAA,MAAA,CAAA,CAAA;AAEH,SAAA,oBAAA,MAAA;;;;;;;;;;;",
6
- "names": ["Context", "Effect", "Layer", "Option", "Schema", "promiseWithCauseCapture", "invariant", "DXN", "obj", "Database", "Schema", "Any", "space", "isDatabase", "db", "spaceId", "dxn", "ref", "object", "promiseWithCauseCapture", "context", "resolve", "Effect", "invariant", "schema", "isInstanceOf", "pipe", "withSpan", "ObjectNotFoundError", "Option"]
7
- }