@dxos/echo-generator 0.4.10-main.b39148d → 0.4.10-main.c32f430

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/src/data.ts CHANGED
@@ -5,7 +5,11 @@
5
5
  // TODO(burdon): Reconcile with @dxos/plugin-debug, @dxos/aurora/testing.
6
6
  // TODO(burdon): Bug when adding stale objects to space (e.g., static objects already added in previous story invocation).
7
7
 
8
- import { Schema, type Space, TextObject } from '@dxos/client/echo';
8
+ import * as S from '@effect/schema/Schema';
9
+
10
+ import { type Space } from '@dxos/client/echo';
11
+ import * as E from '@dxos/echo-schema';
12
+ import { DynamicEchoSchema, effectToJsonSchema, StoredEchoSchema } from '@dxos/echo-schema';
9
13
  import { faker } from '@dxos/random';
10
14
 
11
15
  import { SpaceObjectGenerator, TestObjectGenerator } from './generator';
@@ -22,111 +26,46 @@ export enum TestSchemaType {
22
26
  project = 'example.com/schema/project',
23
27
  }
24
28
 
25
- export const testSchemas = (): TestSchemaMap<TestSchemaType> => {
26
- const document = new Schema({
27
- typename: TestSchemaType.document,
28
- props: [
29
- {
30
- id: 'title',
31
- type: Schema.PropType.STRING,
32
- description: 'title of the document',
33
- },
34
- {
35
- id: 'content',
36
- type: Schema.PropType.STRING, // TODO(burdon): Rich text.
37
- },
38
- ],
29
+ const createDynamicSchema = (typename: string, fields: S.Struct.Fields): DynamicEchoSchema => {
30
+ const typeSchema = S.partial(S.struct(fields)).pipe(E.echoObject(typename, '1.0.0'));
31
+ return new DynamicEchoSchema(
32
+ E.object(StoredEchoSchema, {
33
+ typename,
34
+ version: '1.0.0',
35
+ jsonSchema: effectToJsonSchema(typeSchema),
36
+ }),
37
+ );
38
+ };
39
+
40
+ const testSchemas = (): TestSchemaMap<TestSchemaType> => {
41
+ const document = createDynamicSchema(TestSchemaType.document, {
42
+ title: S.string.pipe(S.description('title of the document')),
43
+ content: S.string,
39
44
  });
40
45
 
41
- const organization = new Schema({
42
- typename: TestSchemaType.organization,
43
- props: [
44
- {
45
- id: 'name',
46
- type: Schema.PropType.STRING,
47
- description: 'name of the company or organization',
48
- },
49
- {
50
- id: 'website',
51
- type: Schema.PropType.STRING,
52
- description: 'public website URL',
53
- },
54
- {
55
- id: 'description',
56
- type: Schema.PropType.STRING,
57
- description: 'short summary of the company',
58
- },
59
- ],
46
+ const organization = createDynamicSchema(TestSchemaType.organization, {
47
+ name: S.string.pipe(S.description('name of the company or organization')),
48
+ website: S.string.pipe(S.description('public website URL')),
49
+ description: S.string.pipe(S.description('short summary of the company')),
60
50
  });
61
51
 
62
- const contact = new Schema({
63
- typename: TestSchemaType.contact,
64
- props: [
65
- {
66
- id: 'name',
67
- type: Schema.PropType.STRING,
68
- description: 'name of the person',
69
- },
70
- // TODO(burdon): Support multiple tagged properties.
71
- {
72
- id: 'email',
73
- type: Schema.PropType.STRING,
74
- },
75
- {
76
- id: 'org',
77
- type: Schema.PropType.REF,
78
- ref: organization,
79
- },
80
- // TODO(burdon): Convert to object with nested props.
81
- {
82
- id: 'lat',
83
- type: Schema.PropType.NUMBER,
84
- },
85
- {
86
- id: 'lng',
87
- type: Schema.PropType.NUMBER,
88
- },
89
- ],
52
+ const contact = createDynamicSchema(TestSchemaType.contact, {
53
+ name: S.string.pipe(S.description('name of the person')),
54
+ email: S.string,
55
+ org: E.ref(organization),
56
+ lat: S.number,
57
+ lng: S.number,
90
58
  });
91
59
 
92
- const project = new Schema({
93
- typename: TestSchemaType.project,
94
- props: [
95
- {
96
- id: 'name',
97
- type: Schema.PropType.STRING,
98
- description: 'name of the project',
99
- },
100
- {
101
- id: 'description',
102
- type: Schema.PropType.STRING, // TODO(burdon): Text.
103
- },
104
- {
105
- id: 'website',
106
- type: Schema.PropType.STRING,
107
- },
108
- {
109
- id: 'repo',
110
- type: Schema.PropType.STRING,
111
- },
112
- {
113
- id: 'status',
114
- type: Schema.PropType.STRING,
115
- },
116
- {
117
- id: 'priority',
118
- type: Schema.PropType.NUMBER,
119
- },
120
- {
121
- id: 'active',
122
- type: Schema.PropType.BOOLEAN,
123
- },
124
- {
125
- id: 'org',
126
- type: Schema.PropType.REF,
127
- ref: organization,
128
- },
129
- ],
60
+ const project = createDynamicSchema(TestSchemaType.project, {
61
+ name: S.string.pipe(S.description('name of the project')),
62
+ description: S.string,
63
+ website: S.string,
64
+ repo: S.string,
65
+ status: S.string,
66
+ priority: S.number,
67
+ active: S.boolean,
68
+ org: E.ref(organization),
130
69
  });
131
70
 
132
71
  return {
@@ -137,7 +76,7 @@ export const testSchemas = (): TestSchemaMap<TestSchemaType> => {
137
76
  };
138
77
  };
139
78
 
140
- export const testObjectGenerators: TestGeneratorMap<TestSchemaType> = {
79
+ const testObjectGenerators: TestGeneratorMap<TestSchemaType> = {
141
80
  [TestSchemaType.document]: () => ({
142
81
  title: faker.lorem.sentence(3),
143
82
  content: faker.lorem.sentences({ min: 1, max: faker.number.int({ min: 1, max: 3 }) }),
@@ -146,7 +85,7 @@ export const testObjectGenerators: TestGeneratorMap<TestSchemaType> = {
146
85
  [TestSchemaType.organization]: () => ({
147
86
  name: faker.company.name(),
148
87
  website: faker.datatype.boolean({ probability: 0.3 }) ? faker.internet.url() : undefined,
149
- description: new TextObject(faker.lorem.sentences()),
88
+ description: faker.lorem.sentences(),
150
89
  }),
151
90
 
152
91
  [TestSchemaType.contact]: (provider) => {
@@ -4,10 +4,11 @@
4
4
 
5
5
  import { expect } from 'chai';
6
6
 
7
- import { Client } from '@dxos/client';
7
+ import { Client, Config } from '@dxos/client';
8
8
  import { debug } from '@dxos/client/echo';
9
+ import * as E from '@dxos/echo-schema';
9
10
  import { faker } from '@dxos/random';
10
- import { describe, test } from '@dxos/test';
11
+ import { afterTest, describe, test } from '@dxos/test';
11
12
 
12
13
  import { createSpaceObjectGenerator, createTestObjectGenerator, TestSchemaType } from './data';
13
14
 
@@ -23,30 +24,22 @@ describe('TestObjectGenerator', () => {
23
24
  });
24
25
 
25
26
  test('with space', async () => {
26
- const client = new Client();
27
- await client.initialize();
28
- await client.halo.createIdentity();
29
- const space = await client.spaces.create();
27
+ const { space } = await setupTest();
30
28
 
31
29
  const generator = createSpaceObjectGenerator(space);
32
30
  generator.addSchemas();
33
31
 
34
32
  // Create org object.
35
33
  const organization = generator.createObject({ types: [TestSchemaType.organization] });
36
- expect(organization.__schema).to.exist;
34
+ expect(E.typeOf(organization)).to.exist;
37
35
 
38
36
  // Expect at least one person object with a linked org reference.
39
37
  const objects = generator.createObjects({ [TestSchemaType.contact]: 10 });
40
38
  expect(objects.some((object) => object.org === organization)).to.be.true;
41
-
42
- await client.destroy();
43
39
  });
44
40
 
45
- test.only('idempotence', async () => {
46
- const client = new Client();
47
- await client.initialize();
48
- await client.halo.createIdentity();
49
- const space = await client.spaces.create();
41
+ test('idempotence', async () => {
42
+ const { space } = await setupTest();
50
43
 
51
44
  const schemaId: string[] = [];
52
45
 
@@ -54,22 +47,30 @@ describe('TestObjectGenerator', () => {
54
47
  const generator = createSpaceObjectGenerator(space);
55
48
  generator.addSchemas();
56
49
  const organization = generator.createObject({ types: [TestSchemaType.organization] });
57
- schemaId.push(organization.__schema!.id);
50
+ schemaId.push(E.typeOf(organization)!.itemId);
58
51
  }
59
52
 
60
53
  {
61
54
  const generator = createSpaceObjectGenerator(space);
62
55
  generator.addSchemas();
63
56
  const organization = generator.createObject({ types: [TestSchemaType.organization] });
64
- schemaId.push(organization.__schema!.id);
57
+ schemaId.push(E.typeOf(organization)!.itemId);
65
58
  }
66
59
 
60
+ expect(schemaId[0]).not.to.be.undefined;
67
61
  expect(schemaId[0]).to.eq(schemaId[1]);
68
62
 
69
63
  {
70
64
  console.log(space.db.objects.map((object) => object[debug]));
71
65
  }
72
-
73
- await client.destroy();
74
66
  });
67
+
68
+ const setupTest = async () => {
69
+ const client = new Client({ config: new Config({ runtime: { client: { useReactiveObjectApi: true } } }) });
70
+ await client.initialize();
71
+ afterTest(async () => await client.destroy());
72
+ await client.halo.createIdentity();
73
+ const space = await client.spaces.create();
74
+ return { client, space };
75
+ };
75
76
  });
package/src/generator.ts CHANGED
@@ -2,7 +2,9 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Expando, type TypedObject, Schema, type Space } from '@dxos/client/echo';
5
+ import { type Space } from '@dxos/client/echo';
6
+ import { type DynamicEchoSchema, Filter, type ReactiveObject } from '@dxos/echo-schema';
7
+ import * as E from '@dxos/echo-schema';
6
8
  import { faker } from '@dxos/random';
7
9
 
8
10
  import { type TestSchemaType } from './data';
@@ -20,30 +22,30 @@ export class TestObjectGenerator<T extends string = TestSchemaType> {
20
22
  private readonly _provider?: TestObjectProvider<T>
21
23
  ) {}
22
24
 
23
- get schemas(): Schema[] {
25
+ get schemas(): DynamicEchoSchema[] {
24
26
  return Object.values(this._schemas);
25
27
  }
26
28
 
27
- getSchema(type: T) {
29
+ getSchema(type: T): DynamicEchoSchema | undefined {
28
30
  return this.schemas.find((schema) => schema.typename === type);
29
31
  }
30
32
 
31
- protected setSchema(type: T, schema: Schema) {
33
+ protected setSchema(type: T, schema: DynamicEchoSchema) {
32
34
  this._schemas[type] = schema;
33
35
  }
34
36
 
35
37
  // TODO(burdon): Runtime type check via: https://github.com/Effect-TS/schema (or zod).
36
- createObject({ types }: { types?: T[] } = {}): TypedObject {
38
+ createObject({ types }: { types?: T[] } = {}): ReactiveObject<any> {
37
39
  const type = faker.helpers.arrayElement(types ?? (Object.keys(this._schemas) as T[]));
38
40
  const data = this._generators[type](this._provider);
39
41
  const schema = this.getSchema(type);
40
- return new Expando(data, { schema });
42
+ return schema ? E.object(schema, data) : E.object(data);
41
43
  }
42
44
 
43
45
  // TODO(burdon): Create batch.
44
46
  // TODO(burdon): Based on dependencies (e.g., organization before contact).
45
- createObjects(map: Partial<Record<T, number>>): Expando[] {
46
- const objects: Expando[] = [];
47
+ createObjects(map: Partial<Record<T, number>>): ReactiveObject<any>[] {
48
+ const objects: ReactiveObject<any>[] = [];
47
49
  Object.entries<number>(map as any).forEach(([type, count]) => {
48
50
  range(() => objects.push(this.createObject({ types: [type as T] })), count);
49
51
  });
@@ -62,36 +64,38 @@ export class SpaceObjectGenerator<T extends string> extends TestObjectGenerator<
62
64
  generators: TestGeneratorMap<T>,
63
65
  ) {
64
66
  super(schemaMap, generators, (type: T) => {
65
- const { objects } = this._space.db.query((object: TypedObject) => {
66
- return object.__schema?.id === this.getSchema(type)?.id;
67
- });
68
-
69
- return objects;
67
+ const schema = this.getSchema(type);
68
+ return (schema && this._space.db.query(Filter.schema(schema)).objects) ?? [];
70
69
  });
71
70
 
72
71
  // TODO(burdon): Map initially are objects that have not been added to the space.
73
72
  // Merge existing schema in space with defaults.
74
- const { objects } = this._space.db.query(Schema.filter());
75
- Object.keys(schemaMap).forEach((type) => {
76
- const schema = objects.find((object) => object.typename === type);
77
- if (schema) {
78
- this.setSchema(type as T, schema);
73
+ const schemas = this._space.db.schemaRegistry.getAll();
74
+ Object.entries<DynamicEchoSchema>(schemaMap).forEach(([type, dynamicSchema]) => {
75
+ let schema = schemas.find((object) => object.typename === type);
76
+ if (schema == null) {
77
+ schema = this._space.db.schemaRegistry.add(dynamicSchema.schema);
79
78
  }
79
+ this.setSchema(type as T, schema);
80
80
  });
81
81
  }
82
82
 
83
83
  addSchemas() {
84
- const { objects } = this._space.db.query(Schema.filter());
84
+ const result: DynamicEchoSchema[] = [];
85
+ const dbSchemas = this._space.db.schemaRegistry.getAll();
85
86
  this.schemas.forEach((schema) => {
86
- if (!objects.find((object) => object.typename === schema.typename)) {
87
- this._space.db.add(schema);
87
+ const existing = dbSchemas.find((object) => object.typename === schema.typename);
88
+ if (existing == null) {
89
+ result.push(this._space.db.schemaRegistry.add(schema.schema));
90
+ } else {
91
+ result.push(existing);
88
92
  }
89
93
  });
90
94
 
91
- return this.schemas;
95
+ return result;
92
96
  }
93
97
 
94
- override createObject({ types }: { types?: T[] } = {}): Expando {
98
+ override createObject({ types }: { types?: T[] } = {}): ReactiveObject<any> {
95
99
  return this._space.db.add(super.createObject({ types }));
96
100
  }
97
101
  }
package/src/types.ts CHANGED
@@ -2,12 +2,12 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type Schema, type Expando } from '@dxos/client/echo';
5
+ import { type DynamicEchoSchema, type ReactiveObject } from '@dxos/echo-schema';
6
6
 
7
7
  export type TestObject = { id: string } & Record<string, any>;
8
8
 
9
- export type TestSchemaMap<T extends string> = Record<T, Schema>;
9
+ export type TestSchemaMap<T extends string> = Record<T, DynamicEchoSchema>;
10
10
 
11
11
  export type TestGeneratorMap<T extends string> = Record<T, (provider: TestObjectProvider<T> | undefined) => any>;
12
12
 
13
- export type TestObjectProvider<T extends string> = (type: T) => Expando[];
13
+ export type TestObjectProvider<T extends string> = (type: T) => ReactiveObject<any>[];