@dxos/echo 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/Annotation.mjs +3 -0
- package/dist/lib/Blob.mjs +160 -0
- package/dist/lib/Blob.mjs.map +1 -0
- package/dist/lib/Database.mjs +2 -0
- package/dist/lib/Entity.mjs +231 -0
- package/dist/lib/Entity.mjs.map +1 -0
- package/dist/lib/Err.mjs +105 -0
- package/dist/lib/Err.mjs.map +1 -0
- package/dist/lib/Feed.mjs +338 -0
- package/dist/lib/Feed.mjs.map +1 -0
- package/dist/lib/Filter.mjs +380 -0
- package/dist/lib/Filter.mjs.map +1 -0
- package/dist/lib/Format.mjs +47 -0
- package/dist/lib/Format.mjs.map +1 -0
- package/dist/lib/JsonSchema.mjs +21 -0
- package/dist/lib/JsonSchema.mjs.map +1 -0
- package/dist/lib/Key.mjs +15 -0
- package/dist/lib/Key.mjs.map +1 -0
- package/dist/lib/Migration.mjs +38 -0
- package/dist/lib/Migration.mjs.map +1 -0
- package/dist/lib/Obj.mjs +660 -0
- package/dist/lib/Obj.mjs.map +1 -0
- package/dist/lib/Order.mjs +65 -0
- package/dist/lib/Order.mjs.map +1 -0
- package/dist/lib/Query.mjs +336 -0
- package/dist/lib/Query.mjs.map +1 -0
- package/dist/lib/QueryResult.mjs +7 -0
- package/dist/lib/QueryResult.mjs.map +1 -0
- package/dist/lib/Ref.mjs +44 -0
- package/dist/lib/Ref.mjs.map +1 -0
- package/dist/lib/Registry.mjs +35 -0
- package/dist/lib/Registry.mjs.map +1 -0
- package/dist/lib/Relation.mjs +397 -0
- package/dist/lib/Relation.mjs.map +1 -0
- package/dist/lib/Scope.mjs +51 -0
- package/dist/lib/Scope.mjs.map +1 -0
- package/dist/lib/Tag.mjs +70 -0
- package/dist/lib/Tag.mjs.map +1 -0
- package/dist/lib/Text.mjs +82 -0
- package/dist/lib/Text.mjs.map +1 -0
- package/dist/lib/Type.mjs +371 -0
- package/dist/lib/Type.mjs.map +1 -0
- package/dist/lib/chunk-Annotation.mjs +221 -0
- package/dist/lib/chunk-Annotation.mjs.map +1 -0
- package/dist/lib/chunk-Database.mjs +918 -0
- package/dist/lib/chunk-Database.mjs.map +1 -0
- package/dist/lib/chunk-atom-snapshot.mjs +249 -0
- package/dist/lib/chunk-atom-snapshot.mjs.map +1 -0
- package/dist/lib/chunk-internal.mjs +2775 -0
- package/dist/lib/chunk-internal.mjs.map +1 -0
- package/dist/lib/chunk-json-schema.mjs +660 -0
- package/dist/lib/chunk-json-schema.mjs.map +1 -0
- package/dist/lib/chunk-number.mjs +164 -0
- package/dist/lib/chunk-number.mjs.map +1 -0
- package/dist/lib/chunk-ref-array.mjs +31 -0
- package/dist/lib/chunk-ref-array.mjs.map +1 -0
- package/dist/lib/chunk-ref.mjs +590 -0
- package/dist/lib/chunk-ref.mjs.map +1 -0
- package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
- package/dist/lib/chunk-select.mjs +186 -0
- package/dist/lib/chunk-select.mjs.map +1 -0
- package/dist/lib/chunk-src.mjs +234 -0
- package/dist/lib/chunk-src.mjs.map +1 -0
- package/dist/lib/chunk-util.mjs +140 -0
- package/dist/lib/chunk-util.mjs.map +1 -0
- package/dist/lib/index.mjs +24 -0
- package/dist/lib/internal.mjs +10 -0
- package/dist/lib/testing.mjs +346 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/types/src/Aggregate.d.ts +86 -0
- package/dist/types/src/Aggregate.d.ts.map +1 -0
- package/dist/types/src/Blob.d.ts +153 -0
- package/dist/types/src/Blob.d.ts.map +1 -0
- package/dist/types/src/Blob.test.d.ts +2 -0
- package/dist/types/src/Blob.test.d.ts.map +1 -0
- package/dist/types/src/Database.d.ts +153 -4
- package/dist/types/src/Database.d.ts.map +1 -1
- package/dist/types/src/Err.d.ts +116 -0
- package/dist/types/src/Err.d.ts.map +1 -1
- package/dist/types/src/Feed.d.ts +116 -28
- package/dist/types/src/Feed.d.ts.map +1 -1
- package/dist/types/src/Feed.test.d.ts +2 -0
- package/dist/types/src/Feed.test.d.ts.map +1 -0
- package/dist/types/src/Filter.d.ts +14 -6
- package/dist/types/src/Filter.d.ts.map +1 -1
- package/dist/types/src/Hypergraph.d.ts +18 -0
- package/dist/types/src/Hypergraph.d.ts.map +1 -1
- package/dist/types/src/Json.d.ts.map +1 -1
- package/dist/types/src/Obj.d.ts +14 -1
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Order.d.ts +9 -3
- package/dist/types/src/Order.d.ts.map +1 -1
- package/dist/types/src/Query.d.ts +107 -26
- package/dist/types/src/Query.d.ts.map +1 -1
- package/dist/types/src/Relation.d.ts +6 -0
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/Type.d.ts.map +1 -1
- package/dist/types/src/View.d.ts +1 -1
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/api.d.ts +6 -0
- package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/model.d.ts +3 -2
- package/dist/types/src/internal/Entity/model.d.ts.map +1 -1
- package/dist/types/src/internal/Filter/match.d.ts.map +1 -1
- package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts +34 -8
- package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts.map +1 -1
- package/dist/types/src/internal/JsonSchema/json-schema.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/atoms.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
- package/dist/types/src/internal/Query/pretty.d.ts.map +1 -1
- package/dist/types/src/internal/Query/query-result-effect.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/make-object.d.ts +13 -0
- package/dist/types/src/internal/common/proxy/make-object.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/reactive.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/symbols.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/typed-handler.d.ts +12 -0
- package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
- package/dist/types/src/internal/common/types/index.d.ts +1 -0
- package/dist/types/src/internal/common/types/index.d.ts.map +1 -1
- package/dist/types/src/internal/common/types/model-symbols.d.ts +5 -0
- package/dist/types/src/internal/common/types/model-symbols.d.ts.map +1 -1
- package/dist/types/src/internal/common/types/projection.d.ts +32 -0
- package/dist/types/src/internal/common/types/projection.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +40 -35
- package/src/Aggregate.ts +105 -0
- package/src/Blob.test.ts +45 -0
- package/src/Blob.ts +205 -0
- package/src/Database.ts +207 -17
- package/src/Err.ts +36 -0
- package/src/Feed.test.ts +188 -0
- package/src/Feed.ts +238 -62
- package/src/Filter.ts +28 -10
- package/src/Hypergraph.ts +18 -0
- package/src/Json.ts +1 -0
- package/src/Obj.test.ts +2 -2
- package/src/Obj.ts +21 -1
- package/src/Order.ts +16 -9
- package/src/Query.test.ts +319 -2
- package/src/Query.ts +165 -51
- package/src/Relation.ts +14 -0
- package/src/Type.ts +5 -6
- package/src/index.ts +2 -0
- package/src/internal/Entity/api.ts +11 -1
- package/src/internal/Entity/model.ts +3 -0
- package/src/internal/Filter/match.ts +30 -1
- package/src/internal/JsonSchema/json-schema-type.ts +14 -2
- package/src/internal/JsonSchema/json-schema.test.ts +28 -1
- package/src/internal/JsonSchema/json-schema.ts +33 -6
- package/src/internal/Obj/atoms.ts +4 -2
- package/src/internal/Obj/json-serializer.test.ts +162 -2
- package/src/internal/Obj/json-serializer.ts +6 -0
- package/src/internal/Query/pretty.ts +19 -1
- package/src/internal/Query/query-result-effect.ts +2 -4
- package/src/internal/Ref/atoms.ts +1 -1
- package/src/internal/common/proxy/change-context.ts +1 -1
- package/src/internal/common/proxy/make-object.ts +42 -3
- package/src/internal/common/proxy/reactive.ts +5 -0
- package/src/internal/common/proxy/symbols.ts +1 -0
- package/src/internal/common/proxy/typed-handler.ts +71 -29
- package/src/internal/common/types/index.ts +1 -0
- package/src/internal/common/types/model-symbols.ts +6 -0
- package/src/internal/common/types/projection.ts +45 -0
- package/src/testing/test-data.ts +14 -14
- package/dist/lib/neutral/Annotation.mjs +0 -90
- package/dist/lib/neutral/Annotation.mjs.map +0 -7
- package/dist/lib/neutral/Database.mjs +0 -42
- package/dist/lib/neutral/Database.mjs.map +0 -7
- package/dist/lib/neutral/Entity.mjs +0 -76
- package/dist/lib/neutral/Entity.mjs.map +0 -7
- package/dist/lib/neutral/Err.mjs +0 -16
- package/dist/lib/neutral/Err.mjs.map +0 -7
- package/dist/lib/neutral/Feed.mjs +0 -50
- package/dist/lib/neutral/Feed.mjs.map +0 -7
- package/dist/lib/neutral/Filter.mjs +0 -72
- package/dist/lib/neutral/Filter.mjs.map +0 -7
- package/dist/lib/neutral/Format.mjs +0 -82
- package/dist/lib/neutral/Format.mjs.map +0 -7
- package/dist/lib/neutral/JsonSchema.mjs +0 -18
- package/dist/lib/neutral/JsonSchema.mjs.map +0 -7
- package/dist/lib/neutral/Key.mjs +0 -16
- package/dist/lib/neutral/Key.mjs.map +0 -7
- package/dist/lib/neutral/Migration.mjs +0 -18
- package/dist/lib/neutral/Migration.mjs.map +0 -7
- package/dist/lib/neutral/Obj.mjs +0 -128
- package/dist/lib/neutral/Obj.mjs.map +0 -7
- package/dist/lib/neutral/Order.mjs +0 -16
- package/dist/lib/neutral/Order.mjs.map +0 -7
- package/dist/lib/neutral/Query.mjs +0 -38
- package/dist/lib/neutral/Query.mjs.map +0 -7
- package/dist/lib/neutral/QueryResult.mjs +0 -2
- package/dist/lib/neutral/QueryResult.mjs.map +0 -7
- package/dist/lib/neutral/Ref.mjs +0 -25
- package/dist/lib/neutral/Ref.mjs.map +0 -7
- package/dist/lib/neutral/Registry.mjs +0 -14
- package/dist/lib/neutral/Registry.mjs.map +0 -7
- package/dist/lib/neutral/Relation.mjs +0 -97
- package/dist/lib/neutral/Relation.mjs.map +0 -7
- package/dist/lib/neutral/Scope.mjs +0 -12
- package/dist/lib/neutral/Scope.mjs.map +0 -7
- package/dist/lib/neutral/Tag.mjs +0 -32
- package/dist/lib/neutral/Tag.mjs.map +0 -7
- package/dist/lib/neutral/Text.mjs +0 -25
- package/dist/lib/neutral/Text.mjs.map +0 -7
- package/dist/lib/neutral/Type.mjs +0 -65
- package/dist/lib/neutral/Type.mjs.map +0 -7
- package/dist/lib/neutral/chunk-2HKGAQM4.mjs +0 -1394
- package/dist/lib/neutral/chunk-2HKGAQM4.mjs.map +0 -7
- package/dist/lib/neutral/chunk-2ZHECXUR.mjs +0 -95
- package/dist/lib/neutral/chunk-2ZHECXUR.mjs.map +0 -7
- package/dist/lib/neutral/chunk-3KTE2Y7Q.mjs +0 -336
- package/dist/lib/neutral/chunk-3KTE2Y7Q.mjs.map +0 -7
- package/dist/lib/neutral/chunk-3TN2VMI4.mjs +0 -714
- package/dist/lib/neutral/chunk-3TN2VMI4.mjs.map +0 -7
- package/dist/lib/neutral/chunk-42L4HJ3Z.mjs +0 -104
- package/dist/lib/neutral/chunk-42L4HJ3Z.mjs.map +0 -7
- package/dist/lib/neutral/chunk-4A2NWKRJ.mjs +0 -747
- package/dist/lib/neutral/chunk-4A2NWKRJ.mjs.map +0 -7
- package/dist/lib/neutral/chunk-7LOUAPYZ.mjs +0 -25
- package/dist/lib/neutral/chunk-7LOUAPYZ.mjs.map +0 -7
- package/dist/lib/neutral/chunk-ACFAOTKJ.mjs +0 -1531
- package/dist/lib/neutral/chunk-ACFAOTKJ.mjs.map +0 -7
- package/dist/lib/neutral/chunk-BEFR7LEA.mjs +0 -170
- package/dist/lib/neutral/chunk-BEFR7LEA.mjs.map +0 -7
- package/dist/lib/neutral/chunk-BXIKXT7Q.mjs +0 -89
- package/dist/lib/neutral/chunk-BXIKXT7Q.mjs.map +0 -7
- package/dist/lib/neutral/chunk-C57XMMBY.mjs +0 -206
- package/dist/lib/neutral/chunk-C57XMMBY.mjs.map +0 -7
- package/dist/lib/neutral/chunk-C62JYFAD.mjs +0 -183
- package/dist/lib/neutral/chunk-C62JYFAD.mjs.map +0 -7
- package/dist/lib/neutral/chunk-FSGX2R62.mjs +0 -48
- package/dist/lib/neutral/chunk-FSGX2R62.mjs.map +0 -7
- package/dist/lib/neutral/chunk-HBXT3GSB.mjs +0 -459
- package/dist/lib/neutral/chunk-HBXT3GSB.mjs.map +0 -7
- package/dist/lib/neutral/chunk-IQR45HAD.mjs +0 -31
- package/dist/lib/neutral/chunk-IQR45HAD.mjs.map +0 -7
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/neutral/chunk-KCBW4BR6.mjs +0 -424
- package/dist/lib/neutral/chunk-KCBW4BR6.mjs.map +0 -7
- package/dist/lib/neutral/chunk-L3E5IXAE.mjs +0 -575
- package/dist/lib/neutral/chunk-L3E5IXAE.mjs.map +0 -7
- package/dist/lib/neutral/chunk-PRC4FEKJ.mjs +0 -56
- package/dist/lib/neutral/chunk-PRC4FEKJ.mjs.map +0 -7
- package/dist/lib/neutral/chunk-SCT6FFCR.mjs +0 -351
- package/dist/lib/neutral/chunk-SCT6FFCR.mjs.map +0 -7
- package/dist/lib/neutral/chunk-TOCVVFK2.mjs +0 -75
- package/dist/lib/neutral/chunk-TOCVVFK2.mjs.map +0 -7
- package/dist/lib/neutral/chunk-U53LSQQA.mjs +0 -41
- package/dist/lib/neutral/chunk-U53LSQQA.mjs.map +0 -7
- package/dist/lib/neutral/chunk-UGFFV2FR.mjs +0 -27
- package/dist/lib/neutral/chunk-UGFFV2FR.mjs.map +0 -7
- package/dist/lib/neutral/chunk-UWLP4RFM.mjs +0 -7
- package/dist/lib/neutral/chunk-UWLP4RFM.mjs.map +0 -7
- package/dist/lib/neutral/chunk-UZHCQ6NI.mjs +0 -283
- package/dist/lib/neutral/chunk-UZHCQ6NI.mjs.map +0 -7
- package/dist/lib/neutral/chunk-WISOH2XH.mjs +0 -36
- package/dist/lib/neutral/chunk-WISOH2XH.mjs.map +0 -7
- package/dist/lib/neutral/chunk-WKIT2YY5.mjs +0 -34
- package/dist/lib/neutral/chunk-WKIT2YY5.mjs.map +0 -7
- package/dist/lib/neutral/chunk-XB6ISRAO.mjs +0 -271
- package/dist/lib/neutral/chunk-XB6ISRAO.mjs.map +0 -7
- package/dist/lib/neutral/chunk-XXL42WCB.mjs +0 -84
- package/dist/lib/neutral/chunk-XXL42WCB.mjs.map +0 -7
- package/dist/lib/neutral/chunk-XXWK4J53.mjs +0 -117
- package/dist/lib/neutral/chunk-XXWK4J53.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -115
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/internal/index.mjs +0 -581
- package/dist/lib/neutral/internal/index.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/testing/index.mjs +0 -477
- package/dist/lib/neutral/testing/index.mjs.map +0 -7
package/src/Query.ts
CHANGED
|
@@ -8,12 +8,13 @@ import type * as EffectArray from 'effect/Array';
|
|
|
8
8
|
import type * as Schema from 'effect/Schema';
|
|
9
9
|
|
|
10
10
|
import { type QueryAST } from '@dxos/echo-protocol';
|
|
11
|
-
import { type URI } from '@dxos/keys';
|
|
11
|
+
import { EID, type URI } from '@dxos/keys';
|
|
12
12
|
|
|
13
|
+
import type * as Aggregate from './Aggregate';
|
|
13
14
|
import type * as Collection from './Collection';
|
|
14
15
|
import * as Database from './Database';
|
|
15
16
|
import type * as Dataset from './Dataset';
|
|
16
|
-
import * as Feed from './Feed';
|
|
17
|
+
import type * as Feed from './Feed';
|
|
17
18
|
import * as Filter from './Filter';
|
|
18
19
|
import * as internal from './internal';
|
|
19
20
|
import * as Obj from './Obj';
|
|
@@ -45,27 +46,49 @@ type ReferenceTraversalTarget<P> = P extends Ref.Unknown
|
|
|
45
46
|
? Ref.Target<RefArrayElement<P>>
|
|
46
47
|
: never;
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Phantom brand on the flat row produced by {@link Query.aggregate}. Present only at the type level
|
|
51
|
+
* (never at runtime), it lets hooks like `useQuery`/`usePagination` distinguish an aggregate-row
|
|
52
|
+
* query from an entity query and avoid wrapping the row in `Entity.Entity`. The brand is a required
|
|
53
|
+
* property so `T extends AggregateResult` discriminates — an optional one would be satisfied by any
|
|
54
|
+
* type. Consumers never read it.
|
|
55
|
+
*/
|
|
56
|
+
export interface AggregateResult {
|
|
57
|
+
readonly '~@dxos/echo/Query.AggregateResult': true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const QueryTypeId = '~@dxos/echo/Query' as const;
|
|
61
|
+
export type QueryTypeId = typeof QueryTypeId;
|
|
62
|
+
|
|
48
63
|
// TODO(burdon): Narrow T to Entity.Unknown?
|
|
49
64
|
export interface Query<T> {
|
|
50
|
-
|
|
51
|
-
'~Query': { value: T };
|
|
65
|
+
readonly [QueryTypeId]: { value: T };
|
|
52
66
|
|
|
53
|
-
|
|
67
|
+
ast: QueryAST.Query;
|
|
54
68
|
|
|
55
69
|
/**
|
|
56
70
|
* Filter the current selection based on a filter.
|
|
57
71
|
* @param filter - Filter to select the objects.
|
|
58
72
|
* @returns Query for the selected objects.
|
|
59
73
|
*/
|
|
60
|
-
|
|
61
|
-
|
|
74
|
+
select(filter: Filter.Filter<T>): Query<T>;
|
|
75
|
+
select(props: Filter.Props<T>): Query<T>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Project the query's results to a single scalar property, for use as a membership set in
|
|
79
|
+
* `Filter.in(query.project('col'))` — an uncorrelated `col IN (SELECT property FROM ...)`
|
|
80
|
+
* semi-join. The subquery is resolved once at execution time. A projection is a terminal
|
|
81
|
+
* value, not a `Query`, so it cannot be chained further.
|
|
82
|
+
* @param property - Property path to project.
|
|
83
|
+
*/
|
|
84
|
+
project<K extends RefPropKey<T>>(property: K): Projection<T[K]>;
|
|
62
85
|
|
|
63
86
|
/**
|
|
64
87
|
* Traverse an outgoing reference.
|
|
65
88
|
* @param key - Property path inside T that is a reference or optional reference.
|
|
66
89
|
* @returns Query for the target of the reference.
|
|
67
90
|
*/
|
|
68
|
-
|
|
91
|
+
reference<K extends RefPropKey<T>>(key: K): Query<ReferenceTraversalTarget<T[K]>>;
|
|
69
92
|
|
|
70
93
|
/**
|
|
71
94
|
* Find objects referencing this object.
|
|
@@ -75,12 +98,12 @@ export interface Query<T> {
|
|
|
75
98
|
*/
|
|
76
99
|
// TODO(dmaretskyi): any way to enforce `Ref.Target<Schema.Schema.Type<S>[key]> == T`?
|
|
77
100
|
// TODO(dmaretskyi): Ability to go through arrays of references.
|
|
78
|
-
|
|
101
|
+
referencedBy<S extends Type$.AnyEntity>(
|
|
79
102
|
target: S | URI.URI,
|
|
80
103
|
key: RefPropKey<Type$.InstanceType<S>>,
|
|
81
104
|
): Query<Type$.InstanceType<S>>;
|
|
82
|
-
|
|
83
|
-
|
|
105
|
+
referencedBy<S extends Type$.AnyEntity>(target: S | URI.URI): Query<Type$.InstanceType<S>>;
|
|
106
|
+
referencedBy(): Query<any>;
|
|
84
107
|
|
|
85
108
|
/**
|
|
86
109
|
* Find relations where this object is the source.
|
|
@@ -88,7 +111,7 @@ export interface Query<T> {
|
|
|
88
111
|
* @param relation - Schema of the relation.
|
|
89
112
|
* @param predicates - Predicates to filter the relation objects.
|
|
90
113
|
*/
|
|
91
|
-
|
|
114
|
+
sourceOf<R extends Type$.AnyRelation>(
|
|
92
115
|
relation?: R | URI.URI,
|
|
93
116
|
predicates?: Filter.Props<Type$.InstanceType<R>>,
|
|
94
117
|
): Query<Type$.InstanceType<R>>;
|
|
@@ -99,7 +122,7 @@ export interface Query<T> {
|
|
|
99
122
|
* @param relation - Type entity of the relation.
|
|
100
123
|
* @param predicates - Predicates to filter the relation objects.
|
|
101
124
|
*/
|
|
102
|
-
|
|
125
|
+
targetOf<R extends Type$.AnyRelation>(
|
|
103
126
|
relation?: R | URI.URI,
|
|
104
127
|
predicates?: Filter.Props<Type$.InstanceType<R>>,
|
|
105
128
|
): Query<Type$.InstanceType<R>>;
|
|
@@ -108,40 +131,86 @@ export interface Query<T> {
|
|
|
108
131
|
* For a query for relations, get the source objects.
|
|
109
132
|
* @returns Query for the source objects.
|
|
110
133
|
*/
|
|
111
|
-
|
|
134
|
+
source(): Query<Relation.SourceOf<T>>;
|
|
112
135
|
|
|
113
136
|
/**
|
|
114
137
|
* For a query for relations, get the target objects.
|
|
115
138
|
* @returns Query for the target objects.
|
|
116
139
|
*/
|
|
117
|
-
|
|
140
|
+
target(): Query<Relation.TargetOf<T>>;
|
|
118
141
|
|
|
119
142
|
/**
|
|
120
143
|
* Get the parent object of the current selection.
|
|
121
144
|
* @returns Query for the parent objects.
|
|
122
145
|
*/
|
|
123
|
-
|
|
146
|
+
parent(): Query<any>;
|
|
124
147
|
|
|
125
148
|
/**
|
|
126
149
|
* Get all child objects of the current selection.
|
|
127
150
|
* @returns Query for the child objects.
|
|
128
151
|
*/
|
|
129
|
-
|
|
152
|
+
children(): Query<any>;
|
|
130
153
|
|
|
131
154
|
/**
|
|
132
155
|
* Order the query results.
|
|
133
156
|
* Orders are specified in priority order. The first order will be applied first, etc.
|
|
157
|
+
*
|
|
158
|
+
* `Order.property` orders by the current result shape's fields, so it works both before and after
|
|
159
|
+
* an {@link aggregate}: before, by member (row) properties; after, by the flat record's fields
|
|
160
|
+
* (any group or aggregate field — e.g. `Order.property('lastMessageAt')` reorders the groups by
|
|
161
|
+
* that aggregate).
|
|
134
162
|
* @param order - Order to sort the results.
|
|
135
163
|
* @returns Query for the ordered results.
|
|
136
164
|
*/
|
|
137
|
-
|
|
165
|
+
orderBy(...order: EffectArray.NonEmptyArray<Order.Order<T>>): Query<T>;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Aggregate the query results into flat records. {@link Aggregate.group} entries partition the
|
|
169
|
+
* results into contiguous groups (one record each), keyed by the record field the group is named
|
|
170
|
+
* after; with no `group` entries the entire input aggregates into a single record. Each declared
|
|
171
|
+
* aggregate becomes a top-level field and can be ordered by with a following {@link orderBy} using
|
|
172
|
+
* {@link Order.property}.
|
|
173
|
+
*
|
|
174
|
+
* Groups are ordered by the first occurrence of their key in the incoming stream, so a preceding
|
|
175
|
+
* `orderBy` controls group order in the absence of a following one. {@link Aggregate.items}'s own
|
|
176
|
+
* `order` option is a separate, explicit per-group member ordering — pass it there rather than
|
|
177
|
+
* relying on a preceding `orderBy`, whose only well-defined job once `aggregate` follows is
|
|
178
|
+
* establishing initial group order. For example, message threads ordered by their most recent
|
|
179
|
+
* message, each retaining up to 20 members newest-first:
|
|
180
|
+
*
|
|
181
|
+
* ```ts
|
|
182
|
+
* Query.type(Message)
|
|
183
|
+
* .aggregate({
|
|
184
|
+
* threadId: Aggregate.group('threadId'),
|
|
185
|
+
* lastMessageAt: Aggregate.max('created'),
|
|
186
|
+
* items: Aggregate.items({ limit: 20, order: [Order.property('created', 'desc')] }),
|
|
187
|
+
* })
|
|
188
|
+
* .orderBy(Order.property('lastMessageAt', 'desc'));
|
|
189
|
+
* ```
|
|
190
|
+
*
|
|
191
|
+
* Must be the last data-selecting clause in the chain — only `from`/`options`/`orderBy`/`limit`/
|
|
192
|
+
* `skip` may follow.
|
|
193
|
+
* @param aggregates - Record of aggregate declarations keyed by result field name.
|
|
194
|
+
* @returns Query whose flat result records carry the named aggregates as fields.
|
|
195
|
+
*/
|
|
196
|
+
aggregate<const A extends Record<string, Aggregate.Aggregate<T, any>>>(
|
|
197
|
+
aggregates: A,
|
|
198
|
+
): Query<Aggregate.AggregationResult<A>>;
|
|
138
199
|
|
|
139
200
|
/**
|
|
140
201
|
* Limit the number of results.
|
|
141
202
|
* @param limit - Maximum number of results to return.
|
|
142
203
|
* @returns Query for the limited results.
|
|
143
204
|
*/
|
|
144
|
-
|
|
205
|
+
limit(limit: number): Query<T>;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Skip a number of results (offset). Combined with `orderBy` and `limit`, expresses a windowed
|
|
209
|
+
* (paginated) read.
|
|
210
|
+
* @param skip - Number of leading results to skip.
|
|
211
|
+
* @returns Query for the remaining results.
|
|
212
|
+
*/
|
|
213
|
+
skip(skip: number): Query<T>;
|
|
145
214
|
|
|
146
215
|
/**
|
|
147
216
|
* Query from selected databases only.
|
|
@@ -154,7 +223,7 @@ export interface Query<T> {
|
|
|
154
223
|
*
|
|
155
224
|
* @param options.includeFeeds [false] - Whether to include feeds in the query. Default is to query from automerge documents only.
|
|
156
225
|
*/
|
|
157
|
-
|
|
226
|
+
from(database: Database.Database | Database.Database[], options?: { includeFeeds?: boolean }): Query<T>;
|
|
158
227
|
|
|
159
228
|
/**
|
|
160
229
|
* Query from selected feeds only.
|
|
@@ -166,7 +235,7 @@ export interface Query<T> {
|
|
|
166
235
|
* ```
|
|
167
236
|
*
|
|
168
237
|
*/
|
|
169
|
-
|
|
238
|
+
from(feeds: Feed.Feed | Feed.Feed[]): Query<T>;
|
|
170
239
|
|
|
171
240
|
/**
|
|
172
241
|
* Query from all accessible spaces.
|
|
@@ -179,7 +248,7 @@ export interface Query<T> {
|
|
|
179
248
|
*
|
|
180
249
|
* @param options.includeFeeds [false] - Whether to include feeds in the query. Default is to query from automerge documents only.
|
|
181
250
|
*/
|
|
182
|
-
|
|
251
|
+
from(allSpaces: 'all-accessible-spaces', options?: { includeFeeds?: boolean }): Query<T>;
|
|
183
252
|
|
|
184
253
|
/**
|
|
185
254
|
* Query from a dataset.
|
|
@@ -191,7 +260,7 @@ export interface Query<T> {
|
|
|
191
260
|
* Query.type(Person).from(feed);
|
|
192
261
|
* ```
|
|
193
262
|
*/
|
|
194
|
-
|
|
263
|
+
from(dataset: Dataset.Dataset): Query<T>;
|
|
195
264
|
|
|
196
265
|
/**
|
|
197
266
|
* Query from the results of another query.
|
|
@@ -202,7 +271,7 @@ export interface Query<T> {
|
|
|
202
271
|
* Query.select(Filter.props({ foo: 'foo' })).from(Query.select(Filter.type(Contact)).reference('org'));
|
|
203
272
|
* ```
|
|
204
273
|
*/
|
|
205
|
-
|
|
274
|
+
from(query: Any): Query<T>;
|
|
206
275
|
|
|
207
276
|
/**
|
|
208
277
|
* Query from one or more raw scopes.
|
|
@@ -213,36 +282,48 @@ export interface Query<T> {
|
|
|
213
282
|
* Query.select(Filter.type(Type.Type)).from(Scope.space(), Scope.registry());
|
|
214
283
|
* ```
|
|
215
284
|
*/
|
|
216
|
-
|
|
285
|
+
from(...scopes: QueryAST.Scope[]): Query<T>;
|
|
217
286
|
|
|
218
287
|
/**
|
|
219
288
|
* Query from a raw scope or array of scopes.
|
|
220
289
|
*/
|
|
221
|
-
|
|
290
|
+
from(scope: QueryAST.Scope | QueryAST.Scope[]): Query<T>;
|
|
222
291
|
|
|
223
292
|
/**
|
|
224
293
|
* Add options to a query.
|
|
225
294
|
*/
|
|
226
|
-
|
|
295
|
+
options(options: QueryAST.QueryOptions): Query<T>;
|
|
227
296
|
|
|
228
297
|
/**
|
|
229
298
|
* Attach a diagnostic label for logs and tooling (execution semantics unchanged).
|
|
230
299
|
*/
|
|
231
|
-
|
|
300
|
+
debugLabel(label: string): Query<T>;
|
|
232
301
|
}
|
|
233
302
|
|
|
234
303
|
export type Any = Query<any>;
|
|
235
304
|
|
|
236
305
|
export type Type<Q extends Any> = Q extends Query<infer T> ? T : never;
|
|
237
306
|
|
|
307
|
+
/**
|
|
308
|
+
* A query projected to a single scalar property (see {@link Query.project}).
|
|
309
|
+
*/
|
|
310
|
+
export type Projection<V = unknown> = internal.Projection<V>;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Brand key for {@link Projection}. Re-exported (like {@link QueryTypeId}) so the sandboxed
|
|
314
|
+
* `query-lite` mirror can declare its own local constant with the same string literal and
|
|
315
|
+
* construct structurally-compatible projections without importing this module's runtime.
|
|
316
|
+
*/
|
|
317
|
+
export type ProjectionTypeId = internal.ProjectionTypeId;
|
|
318
|
+
|
|
238
319
|
class QueryClass implements Any {
|
|
239
|
-
private static 'variance': Any[
|
|
320
|
+
private static 'variance': Any[QueryTypeId] = {} as Any[QueryTypeId];
|
|
240
321
|
|
|
241
|
-
|
|
322
|
+
constructor(public readonly ast: QueryAST.Query) {}
|
|
242
323
|
|
|
243
|
-
|
|
324
|
+
[QueryTypeId] = QueryClass.variance;
|
|
244
325
|
|
|
245
|
-
|
|
326
|
+
select(filter: Filter.Any | Filter.Props<any>): Any {
|
|
246
327
|
if (Filter.is(filter)) {
|
|
247
328
|
return new QueryClass({
|
|
248
329
|
type: 'filter',
|
|
@@ -258,7 +339,11 @@ class QueryClass implements Any {
|
|
|
258
339
|
}
|
|
259
340
|
}
|
|
260
341
|
|
|
261
|
-
|
|
342
|
+
project(property: string): Projection<any> {
|
|
343
|
+
return internal.makeProjection(this.ast, property);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
reference(key: string): Any {
|
|
262
347
|
return new QueryClass({
|
|
263
348
|
type: 'reference-traversal',
|
|
264
349
|
anchor: this.ast,
|
|
@@ -266,7 +351,7 @@ class QueryClass implements Any {
|
|
|
266
351
|
});
|
|
267
352
|
}
|
|
268
353
|
|
|
269
|
-
|
|
354
|
+
referencedBy(target?: Type$.AnyEntity | URI.URI, key?: string): Any {
|
|
270
355
|
const uri = target !== undefined ? internal.getTypeURIFromSpecifier(target) : null;
|
|
271
356
|
return new QueryClass({
|
|
272
357
|
type: 'incoming-references',
|
|
@@ -276,7 +361,7 @@ class QueryClass implements Any {
|
|
|
276
361
|
});
|
|
277
362
|
}
|
|
278
363
|
|
|
279
|
-
|
|
364
|
+
sourceOf(relation?: Type$.AnyRelation | URI.URI, predicates?: Filter.Props<unknown> | undefined): Any {
|
|
280
365
|
return new QueryClass({
|
|
281
366
|
type: 'relation',
|
|
282
367
|
anchor: this.ast,
|
|
@@ -285,7 +370,7 @@ class QueryClass implements Any {
|
|
|
285
370
|
});
|
|
286
371
|
}
|
|
287
372
|
|
|
288
|
-
|
|
373
|
+
targetOf(relation?: Type$.AnyRelation | URI.URI, predicates?: Filter.Props<unknown> | undefined): Any {
|
|
289
374
|
return new QueryClass({
|
|
290
375
|
type: 'relation',
|
|
291
376
|
anchor: this.ast,
|
|
@@ -294,7 +379,7 @@ class QueryClass implements Any {
|
|
|
294
379
|
});
|
|
295
380
|
}
|
|
296
381
|
|
|
297
|
-
|
|
382
|
+
source(): Any {
|
|
298
383
|
return new QueryClass({
|
|
299
384
|
type: 'relation-traversal',
|
|
300
385
|
anchor: this.ast,
|
|
@@ -302,7 +387,7 @@ class QueryClass implements Any {
|
|
|
302
387
|
});
|
|
303
388
|
}
|
|
304
389
|
|
|
305
|
-
|
|
390
|
+
target(): Any {
|
|
306
391
|
return new QueryClass({
|
|
307
392
|
type: 'relation-traversal',
|
|
308
393
|
anchor: this.ast,
|
|
@@ -310,7 +395,7 @@ class QueryClass implements Any {
|
|
|
310
395
|
});
|
|
311
396
|
}
|
|
312
397
|
|
|
313
|
-
|
|
398
|
+
parent(): Any {
|
|
314
399
|
return new QueryClass({
|
|
315
400
|
type: 'hierarchy-traversal',
|
|
316
401
|
anchor: this.ast,
|
|
@@ -318,7 +403,7 @@ class QueryClass implements Any {
|
|
|
318
403
|
});
|
|
319
404
|
}
|
|
320
405
|
|
|
321
|
-
|
|
406
|
+
children(): Any {
|
|
322
407
|
return new QueryClass({
|
|
323
408
|
type: 'hierarchy-traversal',
|
|
324
409
|
anchor: this.ast,
|
|
@@ -326,7 +411,7 @@ class QueryClass implements Any {
|
|
|
326
411
|
});
|
|
327
412
|
}
|
|
328
413
|
|
|
329
|
-
|
|
414
|
+
orderBy(...order: Order.Any[]): Any {
|
|
330
415
|
return new QueryClass({
|
|
331
416
|
type: 'order',
|
|
332
417
|
query: this.ast,
|
|
@@ -334,7 +419,15 @@ class QueryClass implements Any {
|
|
|
334
419
|
});
|
|
335
420
|
}
|
|
336
421
|
|
|
337
|
-
|
|
422
|
+
aggregate(aggregates: Record<string, Aggregate.Any>): Any {
|
|
423
|
+
return new QueryClass({
|
|
424
|
+
type: 'aggregate',
|
|
425
|
+
query: this.ast,
|
|
426
|
+
aggregates: Object.entries(aggregates).map(([name, aggregate]) => ({ name, ...aggregate.spec })),
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
limit(limit: number): Any {
|
|
338
431
|
return new QueryClass({
|
|
339
432
|
type: 'limit',
|
|
340
433
|
query: this.ast,
|
|
@@ -342,7 +435,15 @@ class QueryClass implements Any {
|
|
|
342
435
|
});
|
|
343
436
|
}
|
|
344
437
|
|
|
345
|
-
|
|
438
|
+
skip(skip: number): Any {
|
|
439
|
+
return new QueryClass({
|
|
440
|
+
type: 'skip',
|
|
441
|
+
query: this.ast,
|
|
442
|
+
skip,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
from(
|
|
346
447
|
...args:
|
|
347
448
|
| [
|
|
348
449
|
(
|
|
@@ -451,11 +552,13 @@ class QueryClass implements Any {
|
|
|
451
552
|
if (typename === 'org.dxos.type.collection') {
|
|
452
553
|
throw new Error('Query.from(collection) is not yet supported.');
|
|
453
554
|
}
|
|
454
|
-
// Validate that the items are
|
|
555
|
+
// Validate that the items are feed objects. Checked by typename rather than schema instanceof
|
|
556
|
+
// to keep this module free of a runtime dependency on the Feed module (avoids an import cycle).
|
|
455
557
|
for (const item of items) {
|
|
456
|
-
|
|
558
|
+
const itemTypename = Obj.getTypename(item as Obj.Unknown);
|
|
559
|
+
if (itemTypename !== 'org.dxos.type.feed') {
|
|
457
560
|
throw new TypeError(
|
|
458
|
-
`Query.from() expects Feed objects (org.dxos.type.feed), but received an object with typename '${
|
|
561
|
+
`Query.from() expects Feed objects (org.dxos.type.feed), but received an object with typename '${itemTypename ?? 'unknown'}'.`,
|
|
459
562
|
);
|
|
460
563
|
}
|
|
461
564
|
}
|
|
@@ -463,10 +566,11 @@ class QueryClass implements Any {
|
|
|
463
566
|
|
|
464
567
|
const feedItems = items as Feed.Feed[];
|
|
465
568
|
const feedScopes = feedItems.map((feed) => {
|
|
466
|
-
|
|
569
|
+
// Inlined Feed.getFeedUri to avoid a runtime import cycle with the Feed module.
|
|
570
|
+
const uri = EID.tryParse(Obj.getURI(feed));
|
|
467
571
|
if (!uri) {
|
|
468
572
|
throw new TypeError(
|
|
469
|
-
`Query.from() expects persisted Feed objects with a
|
|
573
|
+
`Query.from() expects persisted Feed objects with a feed URI; got feed without a space (id=${Obj.getURI(feed)}).`,
|
|
470
574
|
);
|
|
471
575
|
}
|
|
472
576
|
return { _tag: 'feed' as const, feedUri: String(uri) };
|
|
@@ -478,7 +582,7 @@ class QueryClass implements Any {
|
|
|
478
582
|
});
|
|
479
583
|
}
|
|
480
584
|
|
|
481
|
-
|
|
585
|
+
options(options: QueryAST.QueryOptions): Any {
|
|
482
586
|
return new QueryClass({
|
|
483
587
|
type: 'options',
|
|
484
588
|
query: this.ast,
|
|
@@ -486,7 +590,7 @@ class QueryClass implements Any {
|
|
|
486
590
|
});
|
|
487
591
|
}
|
|
488
592
|
|
|
489
|
-
|
|
593
|
+
debugLabel(label: string): Any {
|
|
490
594
|
if (this.ast.type === 'options') {
|
|
491
595
|
return new QueryClass({
|
|
492
596
|
type: 'options',
|
|
@@ -503,7 +607,7 @@ class QueryClass implements Any {
|
|
|
503
607
|
}
|
|
504
608
|
|
|
505
609
|
export const is = (value: unknown): value is Any => {
|
|
506
|
-
return typeof value === 'object' && value !== null &&
|
|
610
|
+
return typeof value === 'object' && value !== null && QueryTypeId in value;
|
|
507
611
|
};
|
|
508
612
|
|
|
509
613
|
/** Construct a query from an ast. */
|
|
@@ -550,6 +654,16 @@ export const type: {
|
|
|
550
654
|
});
|
|
551
655
|
};
|
|
552
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Project a query's results to a single scalar property, for use as a membership set in
|
|
659
|
+
* `Filter.in(Query.project(query, 'col'))`.
|
|
660
|
+
* @param query - Query to project.
|
|
661
|
+
* @param property - Property path to project.
|
|
662
|
+
* @returns Projection for use in `Filter.in`.
|
|
663
|
+
*/
|
|
664
|
+
export const project = <T, K extends RefPropKey<T>>(query: Query<T>, property: K): Projection<T[K]> =>
|
|
665
|
+
internal.makeProjection<T[K]>(query.ast, property);
|
|
666
|
+
|
|
553
667
|
/**
|
|
554
668
|
* Combine results of multiple queries.
|
|
555
669
|
* @param queries - Queries to combine.
|
package/src/Relation.ts
CHANGED
|
@@ -230,6 +230,20 @@ export const isSnapshot = (value: unknown): value is Snapshot => {
|
|
|
230
230
|
return (value as any)[Entity.SnapshotKindId] === internal.EntityKind.Relation;
|
|
231
231
|
};
|
|
232
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Sets a relation as the parent of an object: the object cascade-deletes with the relation and is
|
|
235
|
+
* persisted transitively when the relation is added. The delete-cascade resolves the parent by id
|
|
236
|
+
* regardless of kind. The object-parent counterpart is {@link Obj.setParent}.
|
|
237
|
+
*/
|
|
238
|
+
export const setParent = (entity: Obj.Unknown, parent: Unknown): Obj.Unknown => {
|
|
239
|
+
assertArgument(Obj.isObject(entity), 'Expected an object');
|
|
240
|
+
assertArgument(isRelation(parent), 'Expected a relation');
|
|
241
|
+
assumeType<internal.InternalObjectProps>(entity);
|
|
242
|
+
assumeType<internal.InternalObjectProps>(parent);
|
|
243
|
+
entity[internal.ParentId] = parent;
|
|
244
|
+
return entity;
|
|
245
|
+
};
|
|
246
|
+
|
|
233
247
|
/**
|
|
234
248
|
* @returns Relation source URI.
|
|
235
249
|
* Accepts both reactive relations and snapshots.
|
package/src/Type.ts
CHANGED
|
@@ -505,7 +505,7 @@ export const getVersion = (input: AnyEntity): string => {
|
|
|
505
505
|
};
|
|
506
506
|
|
|
507
507
|
/**
|
|
508
|
-
* Strip URI prefixes (`dxn:`, `echo:/`, `echo
|
|
508
|
+
* Strip URI prefixes (`dxn:`, `echo:/`, `echo://`, `echo:///`) from a typename string.
|
|
509
509
|
* Typename is a bare identifier — callers reading from meta or from a
|
|
510
510
|
* caller-supplied seed value shouldn't propagate URI prefixes downstream.
|
|
511
511
|
*/
|
|
@@ -513,11 +513,10 @@ const stripTypenamePrefix = (value: string): string => {
|
|
|
513
513
|
if (value.startsWith('dxn:')) {
|
|
514
514
|
return value.slice('dxn:'.length);
|
|
515
515
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
return value.slice('echo:/'.length);
|
|
516
|
+
// Strip the `echo:` scheme along with any leading slashes so every local form
|
|
517
|
+
// (`echo:/<id>`, `echo:///<id>`) and the qualified `echo://<space>/<id>` collapse consistently.
|
|
518
|
+
if (value.startsWith('echo:')) {
|
|
519
|
+
return value.slice('echo:'.length).replace(/^\/+/, '');
|
|
521
520
|
}
|
|
522
521
|
return value;
|
|
523
522
|
};
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
export { QueryAST } from '@dxos/echo-protocol';
|
|
6
6
|
export { DXN, EID, URI } from '@dxos/keys';
|
|
7
7
|
|
|
8
|
+
export * as Aggregate from './Aggregate';
|
|
8
9
|
export * as Annotation from './Annotation';
|
|
10
|
+
export * as Blob from './Blob';
|
|
9
11
|
export * as Database from './Database';
|
|
10
12
|
export * as Entity from './Entity';
|
|
11
13
|
export * as Err from './Err';
|
|
@@ -8,7 +8,7 @@ import { assumeType } from '@dxos/util';
|
|
|
8
8
|
|
|
9
9
|
import type { AnyEntity } from '../common/types';
|
|
10
10
|
import { MetaId } from '../common/types/model-symbols';
|
|
11
|
-
import { type InternalObjectProps, ObjectDatabaseId } from './model';
|
|
11
|
+
import { type InternalObjectProps, ObjectBranchId, ObjectDatabaseId } from './model';
|
|
12
12
|
import { getObjectEchoUri } from './util';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -75,3 +75,13 @@ export const getDatabase = (entity: AnyEntity): any | undefined => {
|
|
|
75
75
|
assumeType<InternalObjectProps>(entity);
|
|
76
76
|
return entity[ObjectDatabaseId];
|
|
77
77
|
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get the branch this entity instance is bound to (`'main'` for the canonical object, or the branch
|
|
81
|
+
* of a `db.branch()` independent instance). Reads the branch off the instance, so two instances of the
|
|
82
|
+
* same object id on different branches report their own branch.
|
|
83
|
+
*/
|
|
84
|
+
export const getBranch = (entity: AnyEntity): string => {
|
|
85
|
+
assumeType<InternalObjectProps>(entity);
|
|
86
|
+
return entity[ObjectBranchId] ?? 'main';
|
|
87
|
+
};
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type ATTR_TYPE,
|
|
21
21
|
EntityKind,
|
|
22
22
|
KindId,
|
|
23
|
+
ObjectBranchId,
|
|
23
24
|
ObjectDatabaseId,
|
|
24
25
|
ObjectDeletedId,
|
|
25
26
|
ObjectVersionId,
|
|
@@ -40,6 +41,7 @@ export {
|
|
|
40
41
|
ATTR_DELETED,
|
|
41
42
|
ATTR_SELF_URI,
|
|
42
43
|
ATTR_SELF_URI_LEGACY,
|
|
44
|
+
ObjectBranchId,
|
|
43
45
|
ObjectDatabaseId,
|
|
44
46
|
ObjectDeletedId,
|
|
45
47
|
ObjectVersionId,
|
|
@@ -60,6 +62,7 @@ export interface InternalObjectProps {
|
|
|
60
62
|
readonly [MetaId]?: EntityMeta;
|
|
61
63
|
[ParentId]?: InternalObjectProps;
|
|
62
64
|
readonly [ObjectDatabaseId]?: Database.Database;
|
|
65
|
+
readonly [ObjectBranchId]?: string;
|
|
63
66
|
readonly [ObjectDeletedId]?: boolean;
|
|
64
67
|
readonly [ObjectVersionId]?: Version;
|
|
65
68
|
readonly [RelationSourceDXNId]?: EID.EID;
|
|
@@ -117,6 +117,28 @@ const structuralMatch = (filterObj: any, targetObj: any, strict = true): boolean
|
|
|
117
117
|
});
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
/** Normalizes an encoded reference to its URI string so ref-valued properties compare by identity. */
|
|
121
|
+
const _normalizeInValue = (value: unknown): unknown =>
|
|
122
|
+
isEncodedReference(value) ? EncodedReference.toURI(value) : value;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Memoizes a `Set` per `values` array so `Filter.in` membership is O(1) rather than
|
|
126
|
+
* `Array.includes`'s O(n) — this matters most for large sets projected from a subquery
|
|
127
|
+
* (`Filter.in(query.project(...))`, resolved to a literal `in` by the query executor), but
|
|
128
|
+
* also speeds up ordinary literal-list `Filter.in`. Keyed by array identity: the planner and
|
|
129
|
+
* executor never mutate a filter's `values` array in place, so the cache stays valid for the
|
|
130
|
+
* array's lifetime.
|
|
131
|
+
*/
|
|
132
|
+
const _inSetCache = new WeakMap<readonly unknown[], Set<unknown>>();
|
|
133
|
+
const _getInSet = (values: readonly unknown[]): Set<unknown> => {
|
|
134
|
+
let set = _inSetCache.get(values);
|
|
135
|
+
if (!set) {
|
|
136
|
+
set = new Set(values.map(_normalizeInValue));
|
|
137
|
+
_inSetCache.set(values, set);
|
|
138
|
+
}
|
|
139
|
+
return set;
|
|
140
|
+
};
|
|
141
|
+
|
|
120
142
|
export const filterMatchValue = (filter: QueryAST.Filter, value: unknown): boolean => {
|
|
121
143
|
switch (filter.type) {
|
|
122
144
|
case 'compare': {
|
|
@@ -161,7 +183,10 @@ export const filterMatchValue = (filter: QueryAST.Filter, value: unknown): boole
|
|
|
161
183
|
return true;
|
|
162
184
|
}
|
|
163
185
|
case 'in': {
|
|
164
|
-
return filter.values.
|
|
186
|
+
return _getInSet(filter.values).has(_normalizeInValue(value));
|
|
187
|
+
}
|
|
188
|
+
case 'in-query': {
|
|
189
|
+
throw new Error('in-query filters must be resolved to a literal `in` by the query executor before matching.');
|
|
165
190
|
}
|
|
166
191
|
case 'contains': {
|
|
167
192
|
if (!Array.isArray(value)) {
|
|
@@ -266,6 +291,10 @@ export const filterMatchEntity = (filter: QueryAST.Filter, entity: AnyEntity): b
|
|
|
266
291
|
throw new Error('child-of filters must be handled at the executor level, not in-memory matching.');
|
|
267
292
|
}
|
|
268
293
|
|
|
294
|
+
case 'in-query': {
|
|
295
|
+
throw new Error('in-query filters must be resolved to a literal `in` by the query executor before matching.');
|
|
296
|
+
}
|
|
297
|
+
|
|
269
298
|
case 'not': {
|
|
270
299
|
return !filterMatchEntity(filter.filter, entity);
|
|
271
300
|
}
|
|
@@ -38,9 +38,21 @@ export const JsonSchemaEchoAnnotations = Schema.Struct({
|
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Generator function for this schema.
|
|
41
|
-
* Mapped from {@link GeneratorAnnotationId}
|
|
41
|
+
* Mapped from {@link GeneratorAnnotationId}; mirrors `GeneratorAnnotationValue`, whose object
|
|
42
|
+
* form (`{ generator, args }`) is used by schemas like `Task` — a narrower contract here makes
|
|
43
|
+
* those schemas unserializable (`Operation.serialize` fails on any operation referencing them).
|
|
42
44
|
*/
|
|
43
|
-
generator: Schema.optional(
|
|
45
|
+
generator: Schema.optional(
|
|
46
|
+
Schema.Union(
|
|
47
|
+
Schema.String,
|
|
48
|
+
Schema.Tuple(Schema.String, Schema.Number),
|
|
49
|
+
Schema.Struct({
|
|
50
|
+
generator: Schema.String,
|
|
51
|
+
args: Schema.optional(Schema.Array(Schema.Any)),
|
|
52
|
+
probability: Schema.optional(Schema.Number),
|
|
53
|
+
}),
|
|
54
|
+
),
|
|
55
|
+
),
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
58
|
* {@link PropertyMeta} annotations get serialized here.
|