@dxos/echo 0.8.2-staging.7ac8446 → 0.8.3-main.672df60

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 (46) hide show
  1. package/README.md +120 -0
  2. package/dist/lib/browser/index.mjs +274 -32
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/node/index.cjs +276 -28
  6. package/dist/lib/node/index.cjs.map +4 -4
  7. package/dist/lib/node/meta.json +1 -1
  8. package/dist/lib/node-esm/index.mjs +274 -32
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/types/src/Obj.d.ts +61 -0
  12. package/dist/types/src/Obj.d.ts.map +1 -0
  13. package/dist/types/src/Ref.d.ts +15 -0
  14. package/dist/types/src/Ref.d.ts.map +1 -0
  15. package/dist/types/src/Relation.d.ts +32 -0
  16. package/dist/types/src/Relation.d.ts.map +1 -0
  17. package/dist/types/src/Type.d.ts +80 -42
  18. package/dist/types/src/Type.d.ts.map +1 -1
  19. package/dist/types/src/{Database.d.ts → experimental/database.d.ts} +1 -1
  20. package/dist/types/src/experimental/database.d.ts.map +1 -0
  21. package/dist/types/src/experimental/index.d.ts +1 -0
  22. package/dist/types/src/experimental/index.d.ts.map +1 -0
  23. package/dist/types/src/{Queue.d.ts → experimental/queue.d.ts} +1 -1
  24. package/dist/types/src/experimental/queue.d.ts.map +1 -0
  25. package/dist/types/src/{Space.d.ts → experimental/space.d.ts} +1 -1
  26. package/dist/types/src/experimental/space.d.ts.map +1 -0
  27. package/dist/types/src/index.d.ts +5 -5
  28. package/dist/types/src/index.d.ts.map +1 -1
  29. package/dist/types/src/{api.test.d.ts.map → test/api.test.d.ts.map} +1 -1
  30. package/dist/types/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +14 -15
  32. package/src/Obj.ts +120 -0
  33. package/src/Ref.ts +26 -0
  34. package/src/Relation.ts +74 -0
  35. package/src/Type.ts +103 -75
  36. package/src/{Database.ts → experimental/database.ts} +1 -1
  37. package/src/experimental/index.ts +7 -0
  38. package/src/{Queue.ts → experimental/queue.ts} +1 -1
  39. package/src/index.ts +6 -7
  40. package/src/test/api.test.ts +128 -0
  41. package/dist/types/src/Database.d.ts.map +0 -1
  42. package/dist/types/src/Queue.d.ts.map +0 -1
  43. package/dist/types/src/Space.d.ts.map +0 -1
  44. package/src/api.test.ts +0 -94
  45. /package/dist/types/src/{api.test.d.ts → test/api.test.d.ts} +0 -0
  46. /package/src/{Space.ts → experimental/space.ts} +0 -0
package/src/Type.ts CHANGED
@@ -4,96 +4,124 @@
4
4
 
5
5
  import { type Schema } from 'effect';
6
6
 
7
- import {
8
- type BaseSchema,
9
- type EchoSchema,
10
- type Expando as Expando$,
11
- type ImmutableSchema,
12
- type JsonSchemaType,
13
- type TypeMeta,
14
- EchoObject,
15
- EntityKind,
16
- ObjectId,
17
- Ref as Ref$,
18
- getTypeAnnotation,
19
- getSchema,
20
- getSchemaDXN,
21
- getSchemaTypename,
22
- getSchemaVersion,
23
- isInstanceOf,
24
- } from '@dxos/echo-schema';
25
- import { create as create$, makeRef } from '@dxos/live-object';
7
+ import type { EncodedReference } from '@dxos/echo-protocol';
8
+ import * as EchoSchema from '@dxos/echo-schema';
9
+ import { invariant } from '@dxos/invariant';
10
+ import type * as Keys from '@dxos/keys';
26
11
 
27
- // NOTES:
28
- // - New Echo package and namespaces allow for incremental migration; vastly simplifies imports.
29
- // - Split into separate ECHO namespaces: Database, Space, Type, Query, Queue.
30
- // - Example; import { Database, Type, Query, Queue } from '@dxos/echo';
31
- // - Use `declare namespace` for types (no code is generated). See Effect pattern, where Schema is a namespace, interface, and function.
32
- // - Test with @dxos/schema/testing types.
33
- // - Define user (Composer) types in namespace (e.g., of plugin) and drop Type suffix; remove all deprecated Braneframe types.
12
+ /**
13
+ * ECHO schema.
14
+ */
15
+ export type Schema = EchoSchema.EchoSchema;
34
16
 
35
- export type { TypeMeta as Meta, JsonSchemaType as JsonSchema };
36
- export {
37
- EntityKind as Kind,
38
- ObjectId,
39
- getTypeAnnotation as getMeta,
40
- getSchema,
41
- getSchemaDXN as getDXN,
42
- getSchemaTypename as getTypename,
43
- getSchemaVersion as getVersion,
44
- isInstanceOf as instanceOf,
45
- };
17
+ /**
18
+ * EchoObject schema.
19
+ */
20
+ export const Obj = EchoSchema.EchoObject;
21
+
22
+ /**
23
+ * EchoRelation schema.
24
+ */
25
+ export const Relation = EchoSchema.EchoRelation;
46
26
 
47
27
  /**
48
- * Type API.
49
- *
50
- * @category api namespace
51
- * @since 0.9.0
28
+ * Ref schema.
52
29
  */
53
- export declare namespace Type {
30
+ export const Ref: <S extends Obj.Any>(schema: S) => EchoSchema.Ref$<Schema.Schema.Type<S>> = EchoSchema.Ref;
31
+
32
+ export namespace Obj {
33
+ /**
34
+ * Type that represents an arbitrary schema type of an object.
35
+ * NOTE: This is not an instance type.
36
+ */
37
+ // TODO(dmaretskyi): If schema was covariant, we could specify props in here, like `id: ObjectId`.
38
+ export type Any = Schema.Schema.AnyNoContext;
39
+ }
40
+
41
+ export namespace Relation {
42
+ /**
43
+ * Type that represents an arbitrary schema type of a relation.
44
+ * NOTE: This is not an instance type.
45
+ */
46
+ // TODO(dmaretskyi): If schema was covariant, we could specify props in here, like `id: ObjectId`.
47
+ export type Any = Schema.Schema.AnyNoContext;
48
+
54
49
  /**
55
- * A schema that can be extended with arbitrary properties.
50
+ * Get relation target type.
56
51
  */
57
- export type Expando = Expando$;
52
+ export type Target<A> = A extends EchoSchema.RelationSourceTargetRefs<infer T, infer _S> ? T : never;
58
53
 
59
- export type Abstract<T = any> = BaseSchema<T>;
60
- export type ImmutableType<T> = ImmutableSchema<T>;
61
- export type MutableType<T> = EchoSchema<T>;
54
+ /**
55
+ * Get relation source type.
56
+ */
57
+ export type Source<A> = A extends EchoSchema.RelationSourceTargetRefs<infer _T, infer S> ? S : never;
62
58
  }
63
59
 
64
- //
65
- // Constructors
66
- //
60
+ export namespace Ref {
61
+ /**
62
+ * Type that represents an arbitrary schema type of a reference.
63
+ * NOTE: This is not an instance type.
64
+ */
65
+ export type Any = Schema.Schema<EchoSchema.Ref<any>, EncodedReference>;
66
+ }
67
67
 
68
- export const ref = makeRef;
69
- export const create = create$;
68
+ /**
69
+ * Gets the full DXN of the schema.
70
+ * Will include the version if it's a `type` DXN.
71
+ * @example "dxn:example.com/type/Person:0.1.0"
72
+ * @example "dxn:echo:SSSSSSSSSS:XXXXXXXXXXXXX"
73
+ */
74
+ export const getDXN = (schema: Obj.Any | Relation.Any): Keys.DXN | undefined => {
75
+ return EchoSchema.getSchemaDXN(schema);
76
+ };
70
77
 
71
- //
72
- // Combinators
73
- //
78
+ /**
79
+ * @param schema - Schema to get the typename from.
80
+ * @returns The typename of the schema. Example: `example.com/type/Person`.
81
+ */
82
+ export const getTypename = (schema: Obj.Any | Relation.Any): string => {
83
+ const typename = EchoSchema.getSchemaTypename(schema);
84
+ invariant(typeof typename === 'string' && !typename.startsWith('dxn:'), 'Invalid typename');
85
+ return typename;
86
+ };
74
87
 
75
88
  /**
76
- * Defines an ECHO type.
77
- *
78
- * @example
79
- * ```ts
80
- * const Org = S.Struct({
81
- * name: S.String,
82
- * }).pipe(Type.def({ typename: 'example.com/type/Org', version: '1.0.0' }));
83
- * ```
89
+ * Gets the version of the schema.
90
+ * @example 0.1.0
84
91
  */
85
- export const def = (meta: TypeMeta) => EchoObject(meta);
92
+ export const getVersion = (schema: Obj.Any | Relation.Any): string => {
93
+ const version = EchoSchema.getSchemaVersion(schema);
94
+ invariant(typeof version === 'string' && version.match(/^\d+\.\d+\.\d+$/), 'Invalid version');
95
+ return version;
96
+ };
86
97
 
87
98
  /**
88
- * Defines a reference to an ECHO object.
89
- *
90
- * @example
91
- * ```ts
92
- * import { Type } from '@dxos/echo';
93
- * const Contact = S.Struct({
94
- * name: S.String,
95
- * employer: Type.Ref(Org),
96
- * }).pipe(Type.def({ typename: 'example.com/type/Contact', version: '1.0.0' }));
97
- * ```
99
+ * ECHO type metadata.
98
100
  */
99
- export const Ref = <S extends Schema.Schema.AnyNoContext>(self: S) => Ref$<Schema.Schema.Type<S>>(self);
101
+ export type Meta = EchoSchema.TypeAnnotation;
102
+
103
+ /**
104
+ * Gets the meta data of the schema.
105
+ */
106
+ export const getMeta = (schema: Obj.Any | Relation.Any): Meta | undefined => {
107
+ return EchoSchema.getTypeAnnotation(schema);
108
+ };
109
+
110
+ export { EntityKind as Kind } from '@dxos/echo-schema';
111
+
112
+ /**
113
+ * @returns True if the schema is mutable.
114
+ */
115
+ export const isMutable = (schema: Obj.Any | Relation.Any): boolean => {
116
+ return EchoSchema.isMutable(schema);
117
+ };
118
+
119
+ export { SpaceId, ObjectId, DXN } from '@dxos/keys';
120
+
121
+ export {
122
+ //
123
+ Expando,
124
+ JsonSchemaType as JsonSchema,
125
+ toJsonSchema,
126
+ Format,
127
+ } from '@dxos/echo-schema';
@@ -8,4 +8,4 @@
8
8
  * @category api namespace
9
9
  * @since 0.9.0
10
10
  */
11
- export declare namespace Database {}
11
+ export namespace Database {}
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ // export type { Database } from './database';
6
+ // export type { Queue } from './queue';
7
+ // export type { Space } from './space';
@@ -8,4 +8,4 @@
8
8
  * @category api namespace
9
9
  * @since 0.9.0
10
10
  */
11
- export declare namespace Queue {}
11
+ export namespace Queue {}
package/src/index.ts CHANGED
@@ -2,11 +2,10 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { DXN } from '@dxos/keys';
6
-
7
- export { DXN };
8
-
9
- export * as Database from './Database';
10
- export * as Queue from './Queue';
11
- export * as Space from './Space';
12
5
  export * as Type from './Type';
6
+ export * as Obj from './Obj';
7
+ export * as Relation from './Relation';
8
+ export * as Ref from './Ref';
9
+
10
+ export { type Live } from '@dxos/live-object';
11
+ export { Filter, Query } from '@dxos/echo-schema';
@@ -0,0 +1,128 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Schema } from 'effect';
6
+ import { describe, test } from 'vitest';
7
+
8
+ import { raise } from '@dxos/debug';
9
+ import { FormatEnum, FormatAnnotation } from '@dxos/echo-schema';
10
+
11
+ import { Obj, Ref, Type, type Live } from '../index';
12
+
13
+ namespace Testing {
14
+ export const Organization = Schema.Struct({
15
+ id: Type.ObjectId,
16
+ name: Schema.String,
17
+ }).pipe(
18
+ Type.Obj({
19
+ typename: 'example.com/type/Organization',
20
+ version: '0.1.0',
21
+ }),
22
+ );
23
+
24
+ export interface Organization extends Schema.Schema.Type<typeof Organization> {}
25
+
26
+ export const Person = Schema.Struct({
27
+ name: Schema.String,
28
+ dob: Schema.optional(Schema.String),
29
+ email: Schema.optional(Schema.String.pipe(FormatAnnotation.set(FormatEnum.Email))),
30
+ organization: Schema.optional(Type.Ref(Organization)),
31
+ }).pipe(
32
+ Type.Obj({
33
+ typename: 'example.com/type/Person',
34
+ version: '0.1.0',
35
+ }),
36
+ );
37
+
38
+ export interface Person extends Schema.Schema.Type<typeof Person> {}
39
+
40
+ // export const WorksFor = S.Struct({
41
+ // id: Type.ObjectId,
42
+ // since: S.String,
43
+ // jobTitle: S.String,
44
+ // ...Range({ from, to }),
45
+ // ...Provenance({ source: 'duckduckgo.com', confidence: 0.9 }), // keys
46
+ // ...Relation.make({ source: Contact, target: Organization }),
47
+ // }).pipe(
48
+ // Type.Relation({
49
+ // typename: 'example.com/relation/WorksFor',
50
+ // version: '0.1.0',
51
+ // }),
52
+ // );
53
+
54
+ // {
55
+ // const contact = db.add(create(Contact, { name: 'Test' }));
56
+ // const organization = db.add(create(Organization, { name: 'DXOS' }));
57
+ // db.add(create(WorksFor, { source: contact, target: organization }));
58
+ // }
59
+
60
+ export const WorksFor = Schema.Struct({
61
+ // id: Type.ObjectId,
62
+ role: Schema.String,
63
+ }).pipe(
64
+ // Type.Relation
65
+ Type.Obj({
66
+ typename: 'example.com/type/WorksFor',
67
+ version: '0.1.0',
68
+ // source: Person,
69
+ // target: Organization,
70
+ }),
71
+ );
72
+
73
+ export interface WorksFor extends Schema.Schema.Type<typeof WorksFor> {}
74
+
75
+ // TODO(burdon): Fix (Type.Obj currently removes TypeLiteral that implements the `make` function).
76
+ // Property 'make' does not exist on type 'EchoObjectSchema<Struct<{ timestamp: PropertySignature<":", string, never, ":", string, true, never>; }>>'.ts(2339)
77
+ export const MessageStruct = Schema.Struct({
78
+ // TODO(burdon): Support S.Date; Custom Timestamp (with defaults).
79
+ // TODO(burdon): Support defaults (update create and create).
80
+ timestamp: Schema.String.pipe(
81
+ Schema.propertySignature,
82
+ Schema.withConstructorDefault(() => new Date().toISOString()),
83
+ ),
84
+ });
85
+
86
+ export const Message = MessageStruct.pipe(
87
+ Type.Obj({
88
+ typename: 'example.com/type/Message',
89
+ version: '0.1.0',
90
+ }),
91
+ );
92
+
93
+ export interface Message extends Schema.Schema.Type<typeof Message> {}
94
+ }
95
+
96
+ describe('Experimental API review', () => {
97
+ test('type checks', ({ expect }) => {
98
+ const contact = Obj.make(Testing.Person, { name: 'Test' });
99
+ const type: Schema.Schema<Testing.Person> = Obj.getSchema(contact) ?? raise(new Error('No schema found'));
100
+
101
+ expect(Type.getDXN(type)?.typename).to.eq(Testing.Person.typename);
102
+ expect(Type.getTypename(type)).to.eq('example.com/type/Person');
103
+ expect(Type.getVersion(type)).to.eq('0.1.0');
104
+ expect(Type.getMeta(type)).to.deep.eq({
105
+ kind: Type.Kind.Object,
106
+ typename: 'example.com/type/Person',
107
+ version: '0.1.0',
108
+ });
109
+ });
110
+
111
+ test('instance checks', ({ expect }) => {
112
+ const organization: Live<Testing.Organization> = Obj.make(Testing.Organization, { name: 'DXOS' });
113
+ const contact: Live<Testing.Person> = Obj.make(Testing.Person, {
114
+ name: 'Test',
115
+ organization: Ref.make(organization),
116
+ });
117
+
118
+ expect(Schema.is(Testing.Person)(contact)).to.be.true;
119
+ expect(Testing.Person.instanceOf(contact)).to.be.true;
120
+ expect(Obj.instanceOf(Testing.Person)(contact)).to.be.true;
121
+ expect(Obj.instanceOf(Testing.Organization)(organization)).to.be.true;
122
+ });
123
+
124
+ test('default props', ({ expect }) => {
125
+ const message = Obj.make(Testing.Message, Testing.MessageStruct.make({}));
126
+ expect(message.timestamp).to.exist;
127
+ });
128
+ });
@@ -1 +0,0 @@
1
- {"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../../src/Database.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,WAAW,QAAQ,CAAC,GAAE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Queue.d.ts","sourceRoot":"","sources":["../../../src/Queue.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,WAAW,KAAK,CAAC,GAAE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Space.d.ts","sourceRoot":"","sources":["../../../src/Space.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,WAAW,KAAK,CAAC,GAAE"}
package/src/api.test.ts DELETED
@@ -1,94 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { Schema as S } from 'effect';
6
- import { describe, test } from 'vitest';
7
-
8
- import { raise } from '@dxos/debug';
9
- import { FormatEnum, FormatAnnotation } from '@dxos/echo-schema';
10
-
11
- // Deliberately testing top-level import as if external consumer for @dxos/echo.
12
- import { Type } from '.';
13
-
14
- namespace Testing {
15
- export const Org = S.Struct({
16
- id: Type.ObjectId,
17
- name: S.String,
18
- }).pipe(
19
- Type.def({
20
- typename: 'example.com/type/Org',
21
- version: '0.1.0',
22
- }),
23
- );
24
-
25
- export interface Org extends S.Schema.Type<typeof Org> {}
26
-
27
- export const Contact = S.Struct({
28
- name: S.String,
29
- dob: S.optional(S.String),
30
- email: S.optional(S.String.pipe(FormatAnnotation.set(FormatEnum.Email))),
31
- org: S.optional(Type.Ref(Org)),
32
- }).pipe(
33
- Type.def({
34
- typename: 'example.com/type/Contact',
35
- version: '0.1.0',
36
- }),
37
- );
38
-
39
- export interface Contact extends S.Schema.Type<typeof Contact> {}
40
-
41
- export const Message = S.Struct({
42
- // TODO(burdon): Support S.Date; Custom Timestamp (with defaults).
43
- // TODO(burdon): Support defaults (update create and createStatic).
44
- timestamp: S.String.pipe(
45
- S.propertySignature,
46
- S.withConstructorDefault(() => new Date().toISOString()),
47
- ),
48
- });
49
-
50
- // TODO(burdon): Fix (Type.def currently removes TypeLiteral that implements the `make` function)..
51
- // }).pipe(
52
- // Type.def({
53
- // typename: 'example.com/type/Message',
54
- // version: '0.1.0',
55
- // }),
56
- // );
57
-
58
- export interface Message extends S.Schema.Type<typeof Message> {}
59
- }
60
-
61
- describe('Experimental API review', () => {
62
- test('type checks', ({ expect }) => {
63
- const contact = Type.create(Testing.Contact, { name: 'Test' });
64
- const type: S.Schema<Testing.Contact> = Type.getSchema(contact) ?? raise(new Error('No schema found'));
65
-
66
- expect(Type.getDXN(type)?.typename).to.eq(Testing.Contact.typename);
67
- expect(Type.getTypename(type)).to.eq('example.com/type/Contact');
68
- expect(Type.getVersion(type)).to.eq('0.1.0');
69
- expect(Type.getMeta(type)).to.deep.eq({
70
- kind: Type.Kind.Object,
71
- typename: 'example.com/type/Contact',
72
- version: '0.1.0',
73
- });
74
- });
75
-
76
- test('instance checks', ({ expect }) => {
77
- // TODO(burdon): Implement.
78
- // const org = Org.create({ name: 'DXOS' });
79
- const org = Type.create(Testing.Org, { name: 'DXOS' });
80
- const contact = Type.create(Testing.Contact, { name: 'Test', org: Type.ref(org) });
81
-
82
- expect(S.is(Testing.Contact)(contact)).to.be.true;
83
- expect(Testing.Contact.instanceOf(contact)).to.be.true;
84
- expect(Type.instanceOf(Testing.Contact, contact)).to.be.true;
85
- expect(Type.instanceOf(Testing.Org, contact.org?.target)).to.be.true;
86
- });
87
-
88
- test('default props', ({ expect }) => {
89
- // TODO(burdon): Doesn't work after pipe(Type.def).
90
- // Property 'make' does not exist on type 'EchoObjectSchema<Struct<{ timestamp: PropertySignature<":", string, never, ":", string, true, never>; }>>'.ts(2339)
91
- const message = Type.create(Testing.Message, Testing.Message.make({}));
92
- expect(message.timestamp).to.exist;
93
- });
94
- });
File without changes