@dxos/echo 0.8.4-main.9be5663bfe → 0.8.4-main.abd8ff62ef
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/neutral/Annotation.mjs +3 -3
- package/dist/lib/neutral/Database.mjs +6 -4
- package/dist/lib/neutral/Entity.mjs +16 -14
- package/dist/lib/neutral/Err.mjs +1 -1
- package/dist/lib/neutral/Extension.mjs +1 -1
- package/dist/lib/neutral/Feed.mjs +19 -17
- package/dist/lib/neutral/Filter.mjs +11 -11
- package/dist/lib/neutral/Format.mjs +3 -3
- package/dist/lib/neutral/JsonSchema.mjs +8 -8
- package/dist/lib/neutral/Key.mjs +1 -1
- package/dist/lib/neutral/Migration.mjs +17 -0
- package/dist/lib/neutral/Migration.mjs.map +7 -0
- package/dist/lib/neutral/Obj.mjs +14 -14
- package/dist/lib/neutral/Order.mjs +1 -1
- package/dist/lib/neutral/Query.mjs +17 -17
- package/dist/lib/neutral/QueryResult.mjs +1 -1
- package/dist/lib/neutral/Ref.mjs +7 -7
- package/dist/lib/neutral/Relation.mjs +15 -15
- package/dist/lib/neutral/SchemaRegistry.mjs +1 -1
- package/dist/lib/neutral/Tag.mjs +14 -14
- package/dist/lib/neutral/Type.mjs +10 -10
- package/dist/lib/neutral/{chunk-7SQD3FRZ.mjs → chunk-2T22UGGN.mjs} +59 -12
- package/dist/lib/neutral/chunk-2T22UGGN.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-GZQTCRJB.mjs → chunk-44HT3MEC.mjs} +2 -2
- package/dist/lib/neutral/{chunk-WVLOCXB5.mjs → chunk-6VC3FI5E.mjs} +12 -8
- package/dist/lib/neutral/chunk-6VC3FI5E.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-HBJ7JT5A.mjs → chunk-7JFW72MX.mjs} +17 -5
- package/dist/lib/neutral/chunk-7JFW72MX.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-ANHVGJI4.mjs → chunk-7RVZT53K.mjs} +1 -1
- package/dist/lib/neutral/{chunk-BNCCGLJN.mjs → chunk-BICZKPQG.mjs} +1 -1
- package/dist/lib/neutral/chunk-CIWZ5MHQ.mjs +36 -0
- package/dist/lib/neutral/chunk-CIWZ5MHQ.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-OLFCVPOO.mjs → chunk-DUNXPKAC.mjs} +4 -4
- package/dist/lib/neutral/{chunk-R72KFH2X.mjs → chunk-FAW7PJRO.mjs} +2 -2
- package/dist/lib/neutral/{chunk-E5PBQJWV.mjs → chunk-FAYW32CW.mjs} +2 -2
- package/dist/lib/neutral/{chunk-YS6Q3XAD.mjs → chunk-GWFFC34K.mjs} +1 -1
- package/dist/lib/neutral/{chunk-YS6Q3XAD.mjs.map → chunk-GWFFC34K.mjs.map} +1 -1
- package/dist/lib/neutral/{chunk-T2JOLN37.mjs → chunk-I2MFJ76N.mjs} +6 -6
- package/dist/lib/neutral/chunk-I2MFJ76N.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-6URFBQJH.mjs → chunk-JALF2CVV.mjs} +5 -21
- package/dist/lib/neutral/chunk-JALF2CVV.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-EBVB5NOH.mjs → chunk-KQUQZ3CB.mjs} +15 -20
- package/dist/lib/neutral/chunk-KQUQZ3CB.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-ZGVZNBBJ.mjs → chunk-LOTZLYHB.mjs} +17 -12
- package/dist/lib/neutral/chunk-LOTZLYHB.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-TBKX6JQO.mjs → chunk-N4B7FHQT.mjs} +1 -1
- package/dist/lib/neutral/{chunk-UPWIIW2V.mjs → chunk-NKXEKBP5.mjs} +6 -22
- package/dist/lib/neutral/{chunk-UPWIIW2V.mjs.map → chunk-NKXEKBP5.mjs.map} +2 -2
- package/dist/lib/neutral/{chunk-YSLSJ7QS.mjs → chunk-NSMLBSFS.mjs} +17 -45
- package/dist/lib/neutral/chunk-NSMLBSFS.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-ZIXGDU6F.mjs → chunk-QBIGOSRF.mjs} +2 -2
- package/dist/lib/neutral/{chunk-FNEFSO2C.mjs → chunk-QBLYZ4IV.mjs} +12 -65
- package/dist/lib/neutral/{chunk-FNEFSO2C.mjs.map → chunk-QBLYZ4IV.mjs.map} +2 -2
- package/dist/lib/neutral/{chunk-5VKHCUDA.mjs → chunk-QEVM3JUP.mjs} +26 -7
- package/dist/lib/neutral/chunk-QEVM3JUP.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-QWAOTFCY.mjs → chunk-REP7WWAQ.mjs} +16 -66
- package/dist/lib/neutral/chunk-REP7WWAQ.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-DQYLD2RB.mjs → chunk-TRPZU2HV.mjs} +2 -2
- package/dist/lib/neutral/{chunk-UI6MWK5W.mjs → chunk-TTCSATUD.mjs} +1 -1
- package/dist/lib/neutral/{chunk-46QNGNTY.mjs → chunk-TW76K7H5.mjs} +3 -3
- package/dist/lib/neutral/{chunk-FW7UJX25.mjs → chunk-UYJYDSD7.mjs} +67 -465
- package/dist/lib/neutral/chunk-UYJYDSD7.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-OMUPQMLR.mjs → chunk-V72DY6LU.mjs} +1 -1
- package/dist/lib/neutral/{chunk-UBEZSGXY.mjs → chunk-ZISMEVKD.mjs} +1 -1
- package/dist/lib/neutral/{chunk-UBEZSGXY.mjs.map → chunk-ZISMEVKD.mjs.map} +2 -2
- package/dist/lib/neutral/index.mjs +33 -27
- package/dist/lib/neutral/internal/index.mjs +9 -9
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/lib/neutral/testing/index.mjs +28 -27
- package/dist/lib/neutral/testing/index.mjs.map +1 -1
- package/dist/types/src/Collection.d.ts.map +1 -1
- package/dist/types/src/Database.d.ts +5 -0
- package/dist/types/src/Database.d.ts.map +1 -1
- package/dist/types/src/Dataset.d.ts +1 -1
- package/dist/types/src/Entity.d.ts +15 -9
- package/dist/types/src/Entity.d.ts.map +1 -1
- package/dist/types/src/Err.d.ts +18 -18
- package/dist/types/src/Err.d.ts.map +1 -1
- package/dist/types/src/Extension.d.ts +4 -4
- package/dist/types/src/Extension.d.ts.map +1 -1
- package/dist/types/src/Feed.d.ts +12 -1
- package/dist/types/src/Feed.d.ts.map +1 -1
- package/dist/types/src/Filter.d.ts +5 -3
- package/dist/types/src/Filter.d.ts.map +1 -1
- package/dist/types/src/Json.d.ts +33 -0
- package/dist/types/src/Json.d.ts.map +1 -0
- package/dist/types/src/Json.test.d.ts +2 -0
- package/dist/types/src/Json.test.d.ts.map +1 -0
- package/dist/types/src/JsonSchema.d.ts +1 -1
- package/dist/types/src/Migration.d.ts +57 -0
- package/dist/types/src/Migration.d.ts.map +1 -0
- package/dist/types/src/Obj.d.ts +22 -21
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Order.d.ts.map +1 -1
- package/dist/types/src/Query.d.ts +5 -1
- package/dist/types/src/Query.d.ts.map +1 -1
- package/dist/types/src/Ref.d.ts.map +1 -1
- package/dist/types/src/Relation.d.ts +15 -15
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/Tag.d.ts +2 -2
- package/dist/types/src/Tag.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/View.d.ts.map +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/Annotation/annotations.d.ts +2 -2
- package/dist/types/src/internal/Annotation/annotations.d.ts.map +1 -1
- package/dist/types/src/internal/Annotation/sorting.d.ts.map +1 -1
- package/dist/types/src/internal/Annotation/util.d.ts +1 -1
- package/dist/types/src/internal/Annotation/util.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/relation.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/version.d.ts.map +1 -1
- package/dist/types/src/internal/Format/date.d.ts.map +1 -1
- package/dist/types/src/internal/Format/format.d.ts.map +1 -1
- package/dist/types/src/internal/Format/number.d.ts.map +1 -1
- package/dist/types/src/internal/Format/object.d.ts.map +1 -1
- package/dist/types/src/internal/Format/types.d.ts.map +1 -1
- package/dist/types/src/internal/JsonSchema/json-schema-normalize.d.ts.map +1 -1
- package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts +28 -28
- package/dist/types/src/internal/JsonSchema/json-schema-type.d.ts.map +1 -1
- package/dist/types/src/internal/JsonSchema/json-schema.d.ts +1 -1
- package/dist/types/src/internal/JsonSchema/json-schema.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/clone.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/common.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/create-object.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/deleted.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/ids.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/set-value.d.ts +1 -1
- package/dist/types/src/internal/Obj/set-value.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/snapshot.d.ts.map +1 -1
- package/dist/types/src/internal/Query.d.ts.map +1 -1
- package/dist/types/src/internal/Ref/ref.d.ts +13 -0
- package/dist/types/src/internal/Ref/ref.d.ts.map +1 -1
- package/dist/types/src/internal/Type/compose.d.ts.map +1 -1
- package/dist/types/src/internal/Type/echo-schema.d.ts +1 -1
- package/dist/types/src/internal/Type/echo-schema.d.ts.map +1 -1
- package/dist/types/src/internal/Type/manipulation.d.ts.map +1 -1
- package/dist/types/src/internal/common/api/meta.d.ts +3 -3
- package/dist/types/src/internal/common/api/meta.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/change-context.d.ts +1 -1
- package/dist/types/src/internal/common/proxy/change-context.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/define-hidden-property.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/errors.d.ts +1 -1
- package/dist/types/src/internal/common/proxy/errors.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/event-batch.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/json-serializer.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/ownership.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/proxy-utils.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/reactive-array.d.ts +1 -1
- package/dist/types/src/internal/common/proxy/reactive.d.ts +1 -1
- package/dist/types/src/internal/common/proxy/reactive.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/reactive.test.d.ts +2 -0
- package/dist/types/src/internal/common/proxy/reactive.test.d.ts.map +1 -0
- package/dist/types/src/internal/common/proxy/schema-validator.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
- package/dist/types/src/internal/common/types/base.d.ts.map +1 -1
- package/dist/types/src/internal/common/types/entity.d.ts +3 -3
- package/dist/types/src/internal/common/types/meta.d.ts.map +1 -1
- package/dist/types/src/internal/common/types/version.d.ts +1 -1
- package/dist/types/src/testing/test-data.d.ts.map +1 -1
- package/dist/types/src/testing/test-schema.d.ts +53 -53
- package/dist/types/src/testing/test-schema.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -13
- package/src/Collection.ts +1 -1
- package/src/Database.ts +35 -13
- package/src/Entity.ts +16 -9
- package/src/Extension.ts +3 -3
- package/src/Feed.ts +22 -1
- package/src/Filter.ts +9 -5
- package/src/Json.test.ts +175 -0
- package/src/Json.ts +102 -0
- package/src/Migration.ts +94 -0
- package/src/Obj.test.ts +12 -12
- package/src/Obj.ts +27 -24
- package/src/Query.test.ts +44 -11
- package/src/Query.ts +20 -0
- package/src/Relation.ts +21 -17
- package/src/index.ts +3 -0
- package/src/internal/Annotation/annotations.ts +5 -6
- package/src/internal/Obj/json-serializer.test.ts +1 -1
- package/src/internal/Obj/set-value.test.ts +15 -15
- package/src/internal/Obj/set-value.ts +1 -1
- package/src/internal/Query.ts +3 -0
- package/src/internal/Ref/ref.ts +17 -0
- package/src/internal/Type/echo-schema.ts +1 -1
- package/src/internal/common/README.md +1 -1
- package/src/internal/common/api/meta.ts +3 -3
- package/src/internal/common/proxy/change-context.ts +1 -1
- package/src/internal/common/proxy/change.test.ts +59 -59
- package/src/internal/common/proxy/errors.ts +2 -2
- package/src/internal/common/proxy/reactive-array.ts +1 -1
- package/src/internal/common/proxy/reactive.test.ts +54 -0
- package/src/internal/common/proxy/reactive.ts +11 -2
- package/src/internal/common/proxy/typed-handler.ts +7 -7
- package/src/internal/common/proxy/typed-object.test.ts +1 -1
- package/dist/lib/neutral/chunk-5VKHCUDA.mjs.map +0 -7
- package/dist/lib/neutral/chunk-6URFBQJH.mjs.map +0 -7
- package/dist/lib/neutral/chunk-7SQD3FRZ.mjs.map +0 -7
- package/dist/lib/neutral/chunk-EBVB5NOH.mjs.map +0 -7
- package/dist/lib/neutral/chunk-FW7UJX25.mjs.map +0 -7
- package/dist/lib/neutral/chunk-HBJ7JT5A.mjs.map +0 -7
- package/dist/lib/neutral/chunk-QWAOTFCY.mjs.map +0 -7
- package/dist/lib/neutral/chunk-T2JOLN37.mjs.map +0 -7
- package/dist/lib/neutral/chunk-WVLOCXB5.mjs.map +0 -7
- package/dist/lib/neutral/chunk-YSLSJ7QS.mjs.map +0 -7
- package/dist/lib/neutral/chunk-ZGVZNBBJ.mjs.map +0 -7
- /package/dist/lib/neutral/{chunk-GZQTCRJB.mjs.map → chunk-44HT3MEC.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-ANHVGJI4.mjs.map → chunk-7RVZT53K.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-BNCCGLJN.mjs.map → chunk-BICZKPQG.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-OLFCVPOO.mjs.map → chunk-DUNXPKAC.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-R72KFH2X.mjs.map → chunk-FAW7PJRO.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-E5PBQJWV.mjs.map → chunk-FAYW32CW.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-TBKX6JQO.mjs.map → chunk-N4B7FHQT.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-ZIXGDU6F.mjs.map → chunk-QBIGOSRF.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-DQYLD2RB.mjs.map → chunk-TRPZU2HV.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-UI6MWK5W.mjs.map → chunk-TTCSATUD.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-46QNGNTY.mjs.map → chunk-TW76K7H5.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-OMUPQMLR.mjs.map → chunk-V72DY6LU.mjs.map} +0 -0
package/src/Migration.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @import-as-namespace
|
|
6
|
+
|
|
7
|
+
import type * as Schema from 'effect/Schema';
|
|
8
|
+
|
|
9
|
+
import { type DXN } from '@dxos/keys';
|
|
10
|
+
|
|
11
|
+
import type * as Database from './Database';
|
|
12
|
+
import type * as Entity from './Entity';
|
|
13
|
+
import { getSchemaDXN } from './internal';
|
|
14
|
+
|
|
15
|
+
type DefineObjectMigrationOptions<From extends Schema.Schema.AnyNoContext, To extends Schema.Schema.AnyNoContext> = {
|
|
16
|
+
from: From;
|
|
17
|
+
to: To;
|
|
18
|
+
/**
|
|
19
|
+
* Pure function that converts the old object data to the new object data.
|
|
20
|
+
*/
|
|
21
|
+
// 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>>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Callback that is called after the object is migrated. Called for every object that is migrated.
|
|
29
|
+
*
|
|
30
|
+
* NOTE: Database mutations performed in this callback are not guaranteed to be idempotent.
|
|
31
|
+
* If multiple peers run the migration separately, the effects may be applied multiple times.
|
|
32
|
+
*/
|
|
33
|
+
onMigration: (params: OnMigrateProps<From, To>) => Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Context passed to object migration callbacks.
|
|
38
|
+
*/
|
|
39
|
+
export type ObjectMigrationContext = {
|
|
40
|
+
db: Database.Database;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type OnMigrateProps<From extends Schema.Schema.AnyNoContext, To extends Schema.Schema.AnyNoContext> = {
|
|
44
|
+
before: Schema.Schema.Type<From>;
|
|
45
|
+
object: Schema.Schema.Type<To>;
|
|
46
|
+
db: Database.Database;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Definition of a migration from one object schema version to another.
|
|
51
|
+
*/
|
|
52
|
+
export type ObjectMigration = {
|
|
53
|
+
fromType: DXN;
|
|
54
|
+
toType: DXN;
|
|
55
|
+
fromSchema: Schema.Schema.AnyNoContext;
|
|
56
|
+
toSchema: Schema.Schema.AnyNoContext;
|
|
57
|
+
transform: (from: unknown, context: ObjectMigrationContext) => Promise<unknown>;
|
|
58
|
+
onMigration: (params: OnMigrateProps<any, any>) => Promise<void>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Define a migration between two object schemas.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const migration = Migration.define({
|
|
67
|
+
* from: ContactV1,
|
|
68
|
+
* to: ContactV2,
|
|
69
|
+
* transform: async (from) => ({ name: `${from.firstName} ${from.lastName}` }),
|
|
70
|
+
* onMigration: async () => {},
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export const define = <From extends Schema.Schema.AnyNoContext, To extends Schema.Schema.AnyNoContext>(
|
|
75
|
+
options: DefineObjectMigrationOptions<From, To>,
|
|
76
|
+
): ObjectMigration => {
|
|
77
|
+
const fromType = getSchemaDXN(options.from);
|
|
78
|
+
if (!fromType) {
|
|
79
|
+
throw new Error('Invalid from schema');
|
|
80
|
+
}
|
|
81
|
+
const toType = getSchemaDXN(options.to);
|
|
82
|
+
if (!toType) {
|
|
83
|
+
throw new Error('Invalid to schema');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
fromType,
|
|
88
|
+
toType,
|
|
89
|
+
fromSchema: options.from,
|
|
90
|
+
toSchema: options.to,
|
|
91
|
+
transform: options.transform as any,
|
|
92
|
+
onMigration: options.onMigration as any,
|
|
93
|
+
};
|
|
94
|
+
};
|
package/src/Obj.test.ts
CHANGED
|
@@ -117,7 +117,7 @@ describe('Obj', () => {
|
|
|
117
117
|
|
|
118
118
|
test('returns keys from reactive object and snapshot', ({ expect }) => {
|
|
119
119
|
const obj = Obj.make(TestSchema.Person, { name: 'Test' });
|
|
120
|
-
Obj.
|
|
120
|
+
Obj.update(obj, (obj) => {
|
|
121
121
|
const meta = Obj.getMeta(obj);
|
|
122
122
|
meta.keys.push({ source: SOURCE, id: 'key-1' });
|
|
123
123
|
meta.keys.push({ source: SOURCE, id: 'key-2' });
|
|
@@ -166,7 +166,7 @@ describe('Obj', () => {
|
|
|
166
166
|
|
|
167
167
|
const cloned = Obj.clone(original);
|
|
168
168
|
|
|
169
|
-
Obj.
|
|
169
|
+
Obj.update(original, (original) => {
|
|
170
170
|
original.name = 'Bob';
|
|
171
171
|
});
|
|
172
172
|
|
|
@@ -206,7 +206,7 @@ describe('Obj', () => {
|
|
|
206
206
|
expect(cloned.employer?.target).toBe(person.employer?.target);
|
|
207
207
|
|
|
208
208
|
// Modifying the referenced object affects both
|
|
209
|
-
Obj.
|
|
209
|
+
Obj.update(employer, (employer) => {
|
|
210
210
|
employer.name = 'Updated DXOS';
|
|
211
211
|
});
|
|
212
212
|
|
|
@@ -234,7 +234,7 @@ describe('Obj', () => {
|
|
|
234
234
|
expect(cloned.employer?.target?.name).toBe(employer.name);
|
|
235
235
|
|
|
236
236
|
// Modifying the original referenced object does not affect the clone
|
|
237
|
-
Obj.
|
|
237
|
+
Obj.update(employer, (employer) => {
|
|
238
238
|
employer.name = 'Updated DXOS';
|
|
239
239
|
});
|
|
240
240
|
|
|
@@ -333,7 +333,7 @@ describe('Obj', () => {
|
|
|
333
333
|
expect(cloned.address?.coordinates.lng).toBe(-122.4194);
|
|
334
334
|
|
|
335
335
|
// Modifying nested properties should be independent
|
|
336
|
-
Obj.
|
|
336
|
+
Obj.update(person, (person) => {
|
|
337
337
|
person.address!.city = 'New York';
|
|
338
338
|
});
|
|
339
339
|
|
|
@@ -366,7 +366,7 @@ describe('Obj', () => {
|
|
|
366
366
|
expect(cloned.tasks?.[2]?.target).not.toBe(task3);
|
|
367
367
|
|
|
368
368
|
// Modifying original tasks should not affect cloned ones
|
|
369
|
-
Obj.
|
|
369
|
+
Obj.update(task1, (task1) => {
|
|
370
370
|
task1.title = 'Updated Task 1';
|
|
371
371
|
});
|
|
372
372
|
|
|
@@ -449,7 +449,7 @@ describe('Obj', () => {
|
|
|
449
449
|
test('returns false when values already match', () => {
|
|
450
450
|
const target = Obj.make(TestSchema.Organization, { name: 'Acme', properties: { region: 'EU' } });
|
|
451
451
|
const source = Obj.make(TestSchema.Organization, { name: 'Acme', properties: { region: 'EU' } });
|
|
452
|
-
Obj.
|
|
452
|
+
Obj.update(target, (target) => {
|
|
453
453
|
expect(Obj.updateFrom(target, source)).toBe(false);
|
|
454
454
|
});
|
|
455
455
|
});
|
|
@@ -463,7 +463,7 @@ describe('Obj', () => {
|
|
|
463
463
|
name: 'New',
|
|
464
464
|
properties: { a: '2', b: '3' },
|
|
465
465
|
});
|
|
466
|
-
Obj.
|
|
466
|
+
Obj.update(target, (target) => {
|
|
467
467
|
expect(Obj.updateFrom(target, source)).toBe(true);
|
|
468
468
|
});
|
|
469
469
|
expect(target.name).toBe('New');
|
|
@@ -487,7 +487,7 @@ describe('Obj', () => {
|
|
|
487
487
|
employer: Ref.make(orgB),
|
|
488
488
|
address: { city: 'Portland', state: 'OR', zip: '97201', coordinates: { lat: 45.5, lng: -122.6 } },
|
|
489
489
|
});
|
|
490
|
-
Obj.
|
|
490
|
+
Obj.update(target, (target) => {
|
|
491
491
|
expect(Obj.updateFrom(target, source)).toBe(true);
|
|
492
492
|
});
|
|
493
493
|
expect(target.employer?.dxn.toString()).toBe(Ref.make(orgB).dxn.toString());
|
|
@@ -510,7 +510,7 @@ describe('Obj', () => {
|
|
|
510
510
|
email: 'bob@x.test',
|
|
511
511
|
tasks: [Ref.make(t1), Ref.make(t3)],
|
|
512
512
|
});
|
|
513
|
-
Obj.
|
|
513
|
+
Obj.update(target, (target) => {
|
|
514
514
|
expect(Obj.updateFrom(target, source)).toBe(true);
|
|
515
515
|
});
|
|
516
516
|
expect(target.tasks?.map((r) => r.dxn.toString())).toEqual(source.tasks?.map((r) => r.dxn.toString()));
|
|
@@ -519,7 +519,7 @@ describe('Obj', () => {
|
|
|
519
519
|
test('respects include option', () => {
|
|
520
520
|
const target = Obj.make(TestSchema.Organization, { name: 'Keep', properties: { x: '1' } });
|
|
521
521
|
const source = Obj.make(TestSchema.Organization, { name: 'Drop', properties: { x: '2' } });
|
|
522
|
-
Obj.
|
|
522
|
+
Obj.update(target, (target) => {
|
|
523
523
|
expect(Obj.updateFrom(target, source, { include: ['properties'] })).toBe(true);
|
|
524
524
|
});
|
|
525
525
|
expect(target.name).toBe('Keep');
|
|
@@ -529,7 +529,7 @@ describe('Obj', () => {
|
|
|
529
529
|
test('respects exclude option', () => {
|
|
530
530
|
const target = Obj.make(TestSchema.Organization, { name: 'Old', properties: { x: '1' } });
|
|
531
531
|
const source = Obj.make(TestSchema.Organization, { name: 'New', properties: { x: '2' } });
|
|
532
|
-
Obj.
|
|
532
|
+
Obj.update(target, (target) => {
|
|
533
533
|
expect(Obj.updateFrom(target, source, { exclude: ['name'] })).toBe(true);
|
|
534
534
|
});
|
|
535
535
|
expect(target.name).toBe('Old');
|
package/src/Obj.ts
CHANGED
|
@@ -13,7 +13,7 @@ import * as Utils from 'effect/Utils';
|
|
|
13
13
|
|
|
14
14
|
import type { ForeignKey } from '@dxos/echo-protocol';
|
|
15
15
|
import { createJsonPath } from '@dxos/effect';
|
|
16
|
-
import { assertArgument } from '@dxos/invariant';
|
|
16
|
+
import { assertArgument, invariant } from '@dxos/invariant';
|
|
17
17
|
import { type DXN, ObjectId } from '@dxos/keys';
|
|
18
18
|
import { assumeType, deepMapValues } from '@dxos/util';
|
|
19
19
|
|
|
@@ -304,7 +304,7 @@ export const clone: <T extends Unknown>(obj: T, opts?: CloneOptions) => T = objI
|
|
|
304
304
|
|
|
305
305
|
/**
|
|
306
306
|
* Makes all properties mutable recursively.
|
|
307
|
-
* Used to provide a mutable view of an object within `Obj.
|
|
307
|
+
* Used to provide a mutable view of an object within `Obj.update`.
|
|
308
308
|
*/
|
|
309
309
|
export type Mutable<T> = internal.Mutable<T>;
|
|
310
310
|
|
|
@@ -312,33 +312,33 @@ export type Mutable<T> = internal.Mutable<T>;
|
|
|
312
312
|
* Perform mutations on an echo object within a controlled context.
|
|
313
313
|
*
|
|
314
314
|
* All mutations within the callback are batched and trigger a single notification
|
|
315
|
-
* when the callback completes. Direct mutations outside of `Obj.
|
|
315
|
+
* when the callback completes. Direct mutations outside of `Obj.update` will throw
|
|
316
316
|
* an error for echo objects.
|
|
317
317
|
*
|
|
318
318
|
* This function also works with nested objects within echo objects (e.g., Template structs)
|
|
319
319
|
* that are reactive at runtime.
|
|
320
320
|
*
|
|
321
|
-
* @param obj - The echo object to mutate. Use `Relation.
|
|
321
|
+
* @param obj - The echo object to mutate. Use `Relation.update` for relations.
|
|
322
322
|
* @param callback - The callback that performs mutations on the object.
|
|
323
323
|
*
|
|
324
324
|
* @example
|
|
325
325
|
* ```ts
|
|
326
326
|
* const person = Obj.make(Person, { name: 'John', age: 25 });
|
|
327
327
|
*
|
|
328
|
-
* // Mutate within Obj.
|
|
329
|
-
* Obj.
|
|
328
|
+
* // Mutate within Obj.update
|
|
329
|
+
* Obj.update(person, (obj) => {
|
|
330
330
|
* obj.name = 'Jane';
|
|
331
331
|
* obj.age = 30;
|
|
332
332
|
* });
|
|
333
333
|
* // ONE notification fires here
|
|
334
334
|
*
|
|
335
335
|
* // Direct mutation throws
|
|
336
|
-
* person.name = 'Bob'; // Error: Cannot modify outside Obj.
|
|
336
|
+
* person.name = 'Bob'; // Error: Cannot modify outside Obj.update()
|
|
337
337
|
* ```
|
|
338
338
|
*
|
|
339
|
-
* Note: Only accepts objects. Use `Relation.
|
|
339
|
+
* Note: Only accepts objects. Use `Relation.update` for relations.
|
|
340
340
|
*/
|
|
341
|
-
export const
|
|
341
|
+
export const update = <T extends Unknown>(obj: T, callback: internal.ChangeCallback<T>): void => {
|
|
342
342
|
internal.change(obj, callback);
|
|
343
343
|
};
|
|
344
344
|
|
|
@@ -373,7 +373,7 @@ export const getValue = (obj: Unknown | Snapshot, path: readonly (string | numbe
|
|
|
373
373
|
* whether to initialize nested data as an empty object or array.
|
|
374
374
|
*
|
|
375
375
|
* Similar to lodash.set and setDeep from @dxos/util, but schema-aware.
|
|
376
|
-
* Must be called within an `Obj.
|
|
376
|
+
* Must be called within an `Obj.update` callback.
|
|
377
377
|
*
|
|
378
378
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
379
379
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -387,7 +387,7 @@ export const getValue = (obj: Unknown | Snapshot, path: readonly (string | numbe
|
|
|
387
387
|
* ```ts
|
|
388
388
|
* const person = Obj.make(Person, { name: 'John' });
|
|
389
389
|
* // Person schema has: addresses: Schema.Array(Address)
|
|
390
|
-
* Obj.
|
|
390
|
+
* Obj.update(person, (obj) => {
|
|
391
391
|
* Obj.setValue(obj, ['addresses', 0, 'street'], '123 Main St');
|
|
392
392
|
* });
|
|
393
393
|
* // Creates: person.addresses = [{ street: '123 Main St' }]
|
|
@@ -470,16 +470,19 @@ export const getDXN = (entity: Unknown | Snapshot): DXN => {
|
|
|
470
470
|
/**
|
|
471
471
|
* @returns The DXN of the object's type.
|
|
472
472
|
* @example dxn:com.example.type.person:1.0.0
|
|
473
|
+
* @throws If the object is missing its type (corrupted object).
|
|
473
474
|
*/
|
|
474
|
-
|
|
475
|
-
|
|
475
|
+
export const getTypeDXN = (obj: Unknown | Snapshot): DXN => {
|
|
476
|
+
const type = internal.getTypeDXN(obj);
|
|
477
|
+
invariant(type != null, 'Corrupted object: missing type.');
|
|
478
|
+
return type;
|
|
479
|
+
};
|
|
476
480
|
|
|
477
481
|
/**
|
|
478
482
|
* Get the schema of the object.
|
|
479
483
|
* Returns the branded ECHO schema used to create the object.
|
|
480
484
|
*/
|
|
481
|
-
|
|
482
|
-
export const getSchema: (obj: unknown | undefined) => Type.AnyEntity | undefined = internal.getSchema as any;
|
|
485
|
+
export const getSchema: (obj: Unknown | Snapshot) => Type.AnyEntity | undefined = internal.getSchema as any;
|
|
483
486
|
|
|
484
487
|
/**
|
|
485
488
|
* @returns The typename of the object's type.
|
|
@@ -517,7 +520,7 @@ export const Meta = internal.MetaId;
|
|
|
517
520
|
export type ReadonlyMeta = internal.ReadonlyMeta;
|
|
518
521
|
|
|
519
522
|
/**
|
|
520
|
-
* Mutable meta type returned by `Obj.getMeta` inside an `Obj.
|
|
523
|
+
* Mutable meta type returned by `Obj.getMeta` inside an `Obj.update` callback.
|
|
521
524
|
*/
|
|
522
525
|
export type Meta = internal.Meta;
|
|
523
526
|
|
|
@@ -525,7 +528,7 @@ export type Meta = internal.Meta;
|
|
|
525
528
|
// TODO(dmaretskyi): Allow returning undefined.
|
|
526
529
|
/**
|
|
527
530
|
* Get the metadata for an object.
|
|
528
|
-
* Returns mutable meta when passed a mutable object (inside `Obj.
|
|
531
|
+
* Returns mutable meta when passed a mutable object (inside `Obj.update` callback).
|
|
529
532
|
* Returns read-only meta when passed a regular object or snapshot.
|
|
530
533
|
*
|
|
531
534
|
* @example
|
|
@@ -534,7 +537,7 @@ export type Meta = internal.Meta;
|
|
|
534
537
|
* const meta = Obj.getMeta(person); // ReadonlyMeta
|
|
535
538
|
*
|
|
536
539
|
* // Mutable access inside change callback
|
|
537
|
-
* Obj.
|
|
540
|
+
* Obj.update(person, (obj) => {
|
|
538
541
|
* const meta = Obj.getMeta(obj); // ObjectMeta (mutable)
|
|
539
542
|
* meta.tags ??= [];
|
|
540
543
|
* meta.tags.push('important');
|
|
@@ -559,7 +562,7 @@ export const getKeys: {
|
|
|
559
562
|
|
|
560
563
|
/**
|
|
561
564
|
* Delete all keys from the object for the specified source.
|
|
562
|
-
* Must be called within an `Obj.
|
|
565
|
+
* Must be called within an `Obj.update` callback.
|
|
563
566
|
*
|
|
564
567
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
565
568
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -568,7 +571,7 @@ export const deleteKeys = (entity: Mutable<Unknown>, source: string): void => in
|
|
|
568
571
|
|
|
569
572
|
/**
|
|
570
573
|
* Add a tag to the object.
|
|
571
|
-
* Must be called within an `Obj.
|
|
574
|
+
* Must be called within an `Obj.update` callback.
|
|
572
575
|
*
|
|
573
576
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
574
577
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -577,7 +580,7 @@ export const addTag = (entity: Mutable<Unknown>, tag: string): void => internal.
|
|
|
577
580
|
|
|
578
581
|
/**
|
|
579
582
|
* Remove a tag from the object.
|
|
580
|
-
* Must be called within an `Obj.
|
|
583
|
+
* Must be called within an `Obj.update` callback.
|
|
581
584
|
*
|
|
582
585
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
583
586
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -603,7 +606,7 @@ export const getLabel = (entity: Unknown | Snapshot): string | undefined => inte
|
|
|
603
606
|
|
|
604
607
|
/**
|
|
605
608
|
* Set the label of the object.
|
|
606
|
-
* Must be called within an `Obj.
|
|
609
|
+
* Must be called within an `Obj.update` callback.
|
|
607
610
|
*
|
|
608
611
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
609
612
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -618,7 +621,7 @@ export const getDescription = (entity: Unknown | Snapshot): string | undefined =
|
|
|
618
621
|
|
|
619
622
|
/**
|
|
620
623
|
* Set the description of the object.
|
|
621
|
-
* Must be called within an `Obj.
|
|
624
|
+
* Must be called within an `Obj.update` callback.
|
|
622
625
|
*
|
|
623
626
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
624
627
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -741,7 +744,7 @@ const prepareAssignValue = (value: unknown): unknown =>
|
|
|
741
744
|
* References are compared by target DXN; other values use Effect `Equal.equals` inside a structural region,
|
|
742
745
|
* with recursive comparison for arrays and plain object-shaped property bags (excluding `id`).
|
|
743
746
|
*
|
|
744
|
-
* Must be called within an `Obj.
|
|
747
|
+
* Must be called within an `Obj.update` callback.
|
|
745
748
|
*
|
|
746
749
|
* @returns Whether any property was updated.
|
|
747
750
|
*/
|
package/src/Query.test.ts
CHANGED
|
@@ -617,6 +617,22 @@ describe('query api', () => {
|
|
|
617
617
|
);
|
|
618
618
|
});
|
|
619
619
|
|
|
620
|
+
test('Query.pretty surfaces debugLabel from options', () => {
|
|
621
|
+
const query = Query.select(Filter.type(TestSchema.Person)).debugLabel('my-label');
|
|
622
|
+
expect(Query.pretty(query)).toContain('debugLabel');
|
|
623
|
+
expect(Query.pretty(query)).toContain('"my-label"');
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test('Query.debugLabel merges onto existing options clause', () => {
|
|
627
|
+
const query = Query.select(Filter.type(TestSchema.Person))
|
|
628
|
+
.options({ deleted: 'exclude' })
|
|
629
|
+
.debugLabel('timer-probe');
|
|
630
|
+
const pretty = Query.pretty(query);
|
|
631
|
+
expect(pretty).toContain('deleted');
|
|
632
|
+
expect(pretty).toContain('debugLabel');
|
|
633
|
+
expect(pretty).toContain('"timer-probe"');
|
|
634
|
+
});
|
|
635
|
+
|
|
620
636
|
test('Query.pretty returns human-readable query string', () => {
|
|
621
637
|
const query = Query.select(Filter.type(TestSchema.Person, { name: 'Fred' }));
|
|
622
638
|
const pretty = Query.pretty(query);
|
|
@@ -669,9 +685,10 @@ describe('query api', () => {
|
|
|
669
685
|
});
|
|
670
686
|
|
|
671
687
|
describe('Filter.childOf', () => {
|
|
672
|
-
test('childOf with
|
|
688
|
+
test('childOf with Ref', () => {
|
|
673
689
|
const parentDxn = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
|
|
674
|
-
const
|
|
690
|
+
const parentRef = Ref.fromDXN(parentDxn);
|
|
691
|
+
const filter = Filter.childOf(parentRef);
|
|
675
692
|
|
|
676
693
|
expect(filter.ast).toMatchObject({
|
|
677
694
|
type: 'child-of',
|
|
@@ -693,10 +710,10 @@ describe('query api', () => {
|
|
|
693
710
|
Schema.validateSync(QueryAST.Filter)(filter.ast);
|
|
694
711
|
});
|
|
695
712
|
|
|
696
|
-
test('childOf with array of
|
|
713
|
+
test('childOf with array of Refs', () => {
|
|
697
714
|
const dxn1 = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
|
|
698
715
|
const dxn2 = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
|
|
699
|
-
const filter = Filter.childOf([dxn1, dxn2]);
|
|
716
|
+
const filter = Filter.childOf([Ref.fromDXN(dxn1), Ref.fromDXN(dxn2)]);
|
|
700
717
|
|
|
701
718
|
expect(filter.ast).toMatchObject({
|
|
702
719
|
type: 'child-of',
|
|
@@ -707,12 +724,12 @@ describe('query api', () => {
|
|
|
707
724
|
});
|
|
708
725
|
|
|
709
726
|
test('childOf with transitive=false', () => {
|
|
710
|
-
const
|
|
711
|
-
const filter = Filter.childOf(
|
|
727
|
+
const parentRef = Ref.fromDXN(DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random()));
|
|
728
|
+
const filter = Filter.childOf(parentRef, { transitive: false });
|
|
712
729
|
|
|
713
730
|
expect(filter.ast).toMatchObject({
|
|
714
731
|
type: 'child-of',
|
|
715
|
-
parents: [
|
|
732
|
+
parents: [parentRef.dxn.toString()],
|
|
716
733
|
transitive: false,
|
|
717
734
|
});
|
|
718
735
|
Schema.validateSync(QueryAST.Filter)(filter.ast);
|
|
@@ -720,7 +737,8 @@ describe('query api', () => {
|
|
|
720
737
|
|
|
721
738
|
test('childOf in select query', () => {
|
|
722
739
|
const parentDxn = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
|
|
723
|
-
const
|
|
740
|
+
const parentRef = Ref.fromDXN(parentDxn);
|
|
741
|
+
const query = Query.select(Filter.childOf(parentRef));
|
|
724
742
|
|
|
725
743
|
expect(query.ast).toMatchObject({
|
|
726
744
|
type: 'select',
|
|
@@ -735,7 +753,8 @@ describe('query api', () => {
|
|
|
735
753
|
|
|
736
754
|
test('childOf combined with type filter', () => {
|
|
737
755
|
const parentDxn = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
|
|
738
|
-
const
|
|
756
|
+
const parentRef = Ref.fromDXN(parentDxn);
|
|
757
|
+
const query = Query.select(Filter.and(Filter.type(TestSchema.Person), Filter.childOf(parentRef)));
|
|
739
758
|
|
|
740
759
|
Schema.validateSync(QueryAST.Query)(query.ast);
|
|
741
760
|
expect(query.ast).toMatchObject({
|
|
@@ -751,12 +770,26 @@ describe('query api', () => {
|
|
|
751
770
|
});
|
|
752
771
|
|
|
753
772
|
test('childOf pretty-prints correctly', () => {
|
|
754
|
-
const
|
|
755
|
-
const filter = Filter.childOf(
|
|
773
|
+
const parentRef = Ref.fromDXN(DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random()));
|
|
774
|
+
const filter = Filter.childOf(parentRef);
|
|
756
775
|
const pretty = Filter.pretty(filter);
|
|
757
776
|
expect(pretty).toContain('Filter.childOf');
|
|
758
777
|
expect(pretty).toContain('transitive: true');
|
|
759
778
|
});
|
|
779
|
+
|
|
780
|
+
test('childOf with mixed objects and Refs', () => {
|
|
781
|
+
const parent = Obj.make(TestSchema.Person, { name: 'Parent' });
|
|
782
|
+
const refDxn = DXN.fromSpaceAndObjectId(SpaceId.random(), ObjectId.random());
|
|
783
|
+
const parentRef = Ref.fromDXN(refDxn);
|
|
784
|
+
const filter = Filter.childOf([parent, parentRef]);
|
|
785
|
+
|
|
786
|
+
expect(filter.ast).toMatchObject({
|
|
787
|
+
type: 'child-of',
|
|
788
|
+
transitive: true,
|
|
789
|
+
});
|
|
790
|
+
expect(filter.ast.type === 'child-of' && filter.ast.parents.length).toBe(2);
|
|
791
|
+
Schema.validateSync(QueryAST.Filter)(filter.ast);
|
|
792
|
+
});
|
|
760
793
|
});
|
|
761
794
|
|
|
762
795
|
describe('Filter', () => {
|
package/src/Query.ts
CHANGED
|
@@ -207,6 +207,11 @@ export interface Query<T> {
|
|
|
207
207
|
* Add options to a query.
|
|
208
208
|
*/
|
|
209
209
|
'options'(options: QueryAST.QueryOptions): Query<T>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Attach a diagnostic label for logs and tooling (execution semantics unchanged).
|
|
213
|
+
*/
|
|
214
|
+
'debugLabel'(label: string): Query<T>;
|
|
210
215
|
}
|
|
211
216
|
|
|
212
217
|
export type Any = Query<any>;
|
|
@@ -429,6 +434,21 @@ class QueryClass implements Any {
|
|
|
429
434
|
options,
|
|
430
435
|
});
|
|
431
436
|
}
|
|
437
|
+
|
|
438
|
+
debugLabel(label: string): Any {
|
|
439
|
+
if (this.ast.type === 'options') {
|
|
440
|
+
return new QueryClass({
|
|
441
|
+
type: 'options',
|
|
442
|
+
query: this.ast.query,
|
|
443
|
+
options: { ...this.ast.options, debugLabel: label },
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return new QueryClass({
|
|
447
|
+
type: 'options',
|
|
448
|
+
query: this.ast,
|
|
449
|
+
options: { debugLabel: label },
|
|
450
|
+
});
|
|
451
|
+
}
|
|
432
452
|
}
|
|
433
453
|
|
|
434
454
|
export const is = (value: unknown): value is Any => {
|
package/src/Relation.ts
CHANGED
|
@@ -237,7 +237,9 @@ export const getSource = <T extends Unknown | Snapshot>(relation: T): SourceOf<T
|
|
|
237
237
|
assertArgument(isRelation(relation), 'Expected a relation');
|
|
238
238
|
assumeType<internal.InternalObjectProps>(relation);
|
|
239
239
|
const obj = (relation as internal.InternalObjectProps)[internal.RelationSourceId];
|
|
240
|
-
|
|
240
|
+
if (obj === undefined) {
|
|
241
|
+
throw new Error(`Relation source could not be resolved.`);
|
|
242
|
+
}
|
|
241
243
|
return obj as SourceOf<T>;
|
|
242
244
|
};
|
|
243
245
|
|
|
@@ -250,7 +252,9 @@ export const getTarget = <T extends Unknown | Snapshot>(relation: T): TargetOf<T
|
|
|
250
252
|
assertArgument(isRelation(relation), 'Expected a relation');
|
|
251
253
|
assumeType<internal.InternalObjectProps>(relation);
|
|
252
254
|
const obj = (relation as internal.InternalObjectProps)[internal.RelationTargetId];
|
|
253
|
-
|
|
255
|
+
if (obj === undefined) {
|
|
256
|
+
throw new Error(`Relation target could not be resolved.`);
|
|
257
|
+
}
|
|
254
258
|
return obj as TargetOf<T>;
|
|
255
259
|
};
|
|
256
260
|
|
|
@@ -260,7 +264,7 @@ export const getTarget = <T extends Unknown | Snapshot>(relation: T): TargetOf<T
|
|
|
260
264
|
|
|
261
265
|
/**
|
|
262
266
|
* Makes all properties mutable recursively.
|
|
263
|
-
* Used to provide a mutable view of a relation within `Relation.
|
|
267
|
+
* Used to provide a mutable view of a relation within `Relation.update`.
|
|
264
268
|
*/
|
|
265
269
|
export type Mutable<T> = internal.Mutable<T>;
|
|
266
270
|
|
|
@@ -268,10 +272,10 @@ export type Mutable<T> = internal.Mutable<T>;
|
|
|
268
272
|
* Perform mutations on an echo relation within a controlled context.
|
|
269
273
|
*
|
|
270
274
|
* All mutations within the callback are batched and trigger a single notification
|
|
271
|
-
* when the callback completes. Direct mutations outside of `Relation.
|
|
275
|
+
* when the callback completes. Direct mutations outside of `Relation.update` will throw
|
|
272
276
|
* an error for echo relations.
|
|
273
277
|
*
|
|
274
|
-
* @param relation - The echo relation to mutate. Use `Obj.
|
|
278
|
+
* @param relation - The echo relation to mutate. Use `Obj.update` for objects.
|
|
275
279
|
* @param callback - The callback that performs mutations on the relation.
|
|
276
280
|
*
|
|
277
281
|
* @example
|
|
@@ -282,15 +286,15 @@ export type Mutable<T> = internal.Mutable<T>;
|
|
|
282
286
|
* role: 'Engineer',
|
|
283
287
|
* });
|
|
284
288
|
*
|
|
285
|
-
* // Mutate within Relation.
|
|
286
|
-
* Relation.
|
|
289
|
+
* // Mutate within Relation.update
|
|
290
|
+
* Relation.update(worksFor, (obj) => {
|
|
287
291
|
* obj.role = 'Senior Engineer';
|
|
288
292
|
* });
|
|
289
293
|
* ```
|
|
290
294
|
*
|
|
291
|
-
* Note: Only accepts relations. Use `Obj.
|
|
295
|
+
* Note: Only accepts relations. Use `Obj.update` for objects.
|
|
292
296
|
*/
|
|
293
|
-
export const
|
|
297
|
+
export const update = <T extends Unknown>(relation: T, callback: internal.ChangeCallback<T>): void => {
|
|
294
298
|
internal.change(relation, callback);
|
|
295
299
|
};
|
|
296
300
|
|
|
@@ -333,7 +337,7 @@ export const getValue = (rel: Unknown | Snapshot, path: readonly (string | numbe
|
|
|
333
337
|
|
|
334
338
|
/**
|
|
335
339
|
* Set a deeply nested property on a relation.
|
|
336
|
-
* Must be called within a `Relation.
|
|
340
|
+
* Must be called within a `Relation.update` callback.
|
|
337
341
|
*
|
|
338
342
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
339
343
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -395,13 +399,13 @@ export const Meta = internal.MetaId;
|
|
|
395
399
|
export type ReadonlyMeta = internal.ReadonlyMeta;
|
|
396
400
|
|
|
397
401
|
/**
|
|
398
|
-
* Mutable meta type returned by `Relation.getMeta` inside a `Relation.
|
|
402
|
+
* Mutable meta type returned by `Relation.getMeta` inside a `Relation.update` callback.
|
|
399
403
|
*/
|
|
400
404
|
export type Meta = internal.Meta;
|
|
401
405
|
|
|
402
406
|
/**
|
|
403
407
|
* Get the metadata for a relation.
|
|
404
|
-
* Returns mutable meta when passed a mutable relation (inside `Relation.
|
|
408
|
+
* Returns mutable meta when passed a mutable relation (inside `Relation.update` callback).
|
|
405
409
|
* Returns read-only meta when passed a regular relation or snapshot.
|
|
406
410
|
*/
|
|
407
411
|
// TODO(wittjosiah): When passed a Snapshot, should return a snapshot of meta, not the live meta proxy.
|
|
@@ -419,7 +423,7 @@ export const getKeys = (entity: Unknown | Snapshot, source: string): ForeignKey[
|
|
|
419
423
|
|
|
420
424
|
/**
|
|
421
425
|
* Delete all keys from the relation for the specified source.
|
|
422
|
-
* Must be called within a `Relation.
|
|
426
|
+
* Must be called within a `Relation.update` callback.
|
|
423
427
|
*
|
|
424
428
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
425
429
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -428,7 +432,7 @@ export const deleteKeys = (entity: Mutable<Unknown>, source: string): void => in
|
|
|
428
432
|
|
|
429
433
|
/**
|
|
430
434
|
* Add a tag to the relation.
|
|
431
|
-
* Must be called within a `Relation.
|
|
435
|
+
* Must be called within a `Relation.update` callback.
|
|
432
436
|
*
|
|
433
437
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
434
438
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -437,7 +441,7 @@ export const addTag = (entity: Mutable<Unknown>, tag: string): void => internal.
|
|
|
437
441
|
|
|
438
442
|
/**
|
|
439
443
|
* Remove a tag from the relation.
|
|
440
|
-
* Must be called within a `Relation.
|
|
444
|
+
* Must be called within a `Relation.update` callback.
|
|
441
445
|
*
|
|
442
446
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
443
447
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -462,7 +466,7 @@ export const getLabel = (entity: Unknown | Snapshot): string | undefined => inte
|
|
|
462
466
|
|
|
463
467
|
/**
|
|
464
468
|
* Set the label of the relation.
|
|
465
|
-
* Must be called within a `Relation.
|
|
469
|
+
* Must be called within a `Relation.update` callback.
|
|
466
470
|
*
|
|
467
471
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
468
472
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
|
@@ -477,7 +481,7 @@ export const getDescription = (entity: Unknown | Snapshot): string | undefined =
|
|
|
477
481
|
|
|
478
482
|
/**
|
|
479
483
|
* Set the description of the relation.
|
|
480
|
-
* Must be called within a `Relation.
|
|
484
|
+
* Must be called within a `Relation.update` callback.
|
|
481
485
|
*
|
|
482
486
|
* NOTE: TypeScript's structural typing allows readonly objects to be passed to `Mutable<T>`
|
|
483
487
|
* parameters, so there is no compile-time error. Enforcement is runtime-only.
|
package/src/index.ts
CHANGED
|
@@ -9,13 +9,16 @@ export { DXN } from '@dxos/keys';
|
|
|
9
9
|
export * as Annotation from './Annotation';
|
|
10
10
|
export * as Database from './Database';
|
|
11
11
|
export * as Entity from './Entity';
|
|
12
|
+
// TODO(burdon): Rename to Error (less problematic than Obj/Object).
|
|
12
13
|
export * as Err from './Err';
|
|
13
14
|
export * as Feed from './Feed';
|
|
14
15
|
export * as Filter from './Filter';
|
|
15
16
|
export * as Format from './Format';
|
|
16
17
|
export * as Hypergraph from './Hypergraph';
|
|
18
|
+
export * as Json from './Json';
|
|
17
19
|
export * as JsonSchema from './JsonSchema';
|
|
18
20
|
export * as Key from './Key';
|
|
21
|
+
export * as Migration from './Migration';
|
|
19
22
|
export * as Obj from './Obj';
|
|
20
23
|
export * as Order from './Order';
|
|
21
24
|
export * as Query from './Query';
|