@dxos/echo-generator 0.3.3-main.1817c8a → 0.3.3-main.22971a3
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/browser/index.mjs +170 -102
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +175 -103
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/data.d.ts +11 -0
- package/dist/types/src/data.d.ts.map +1 -0
- package/dist/types/src/generator.d.ts +25 -36
- package/dist/types/src/generator.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/types.d.ts +8 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +2 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/data.ts +130 -0
- package/src/generator.test.ts +31 -4
- package/src/generator.ts +42 -123
- package/src/index.ts +2 -0
- package/src/types.ts +13 -0
- package/src/util.ts +9 -0
package/src/data.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// TODO(burdon): Reconcile with @dxos/plugin-debug, @dxos/aurora/testing.
|
|
6
|
+
// TODO(burdon): Bug when adding stale objects to space (e.g., static objects already added in previous story invocation).
|
|
7
|
+
|
|
8
|
+
import { faker } from '@faker-js/faker';
|
|
9
|
+
|
|
10
|
+
import { Schema, type Space, Text } from '@dxos/client/echo';
|
|
11
|
+
|
|
12
|
+
import { SpaceObjectGenerator, TestObjectGenerator } from './generator';
|
|
13
|
+
import { type TestGeneratorMap, type TestSchemaMap } from './types';
|
|
14
|
+
|
|
15
|
+
// TODO(burdon): Handle restricted values.
|
|
16
|
+
export const Status = ['pending', 'active', 'done'];
|
|
17
|
+
export const Priority = [1, 2, 3, 4, 5];
|
|
18
|
+
|
|
19
|
+
export type TestSchemaType = 'document' | 'organization' | 'person' | 'project';
|
|
20
|
+
|
|
21
|
+
export const testSchemas = (): TestSchemaMap<TestSchemaType> => {
|
|
22
|
+
const document = new Schema({
|
|
23
|
+
props: [
|
|
24
|
+
{
|
|
25
|
+
id: 'name',
|
|
26
|
+
type: Schema.PropType.STRING,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'content',
|
|
30
|
+
type: Schema.PropType.STRING, // TODO(burdon): Rich text.
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const organization = new Schema({
|
|
36
|
+
props: [
|
|
37
|
+
// TODO(burdon): Add metadata for default label.
|
|
38
|
+
{
|
|
39
|
+
id: 'name',
|
|
40
|
+
type: Schema.PropType.STRING,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'website',
|
|
44
|
+
type: Schema.PropType.STRING,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'active',
|
|
48
|
+
type: Schema.PropType.BOOLEAN,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const person = new Schema({
|
|
54
|
+
props: [
|
|
55
|
+
{
|
|
56
|
+
id: 'name',
|
|
57
|
+
type: Schema.PropType.STRING,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'email',
|
|
61
|
+
type: Schema.PropType.STRING,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'org',
|
|
65
|
+
type: Schema.PropType.REF,
|
|
66
|
+
ref: organization,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const project = new Schema({
|
|
72
|
+
props: [
|
|
73
|
+
{
|
|
74
|
+
id: 'title',
|
|
75
|
+
type: Schema.PropType.STRING,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'repo',
|
|
79
|
+
type: Schema.PropType.STRING,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 'status',
|
|
83
|
+
type: Schema.PropType.STRING,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'priority',
|
|
87
|
+
type: Schema.PropType.NUMBER,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return { document, organization, person, project };
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const testObjectGenerators: TestGeneratorMap<TestSchemaType> = {
|
|
96
|
+
document: () => ({
|
|
97
|
+
title: faker.lorem.sentence(3),
|
|
98
|
+
content: faker.lorem.sentences({ min: 1, max: faker.number.int({ min: 1, max: 3 }) }),
|
|
99
|
+
}),
|
|
100
|
+
|
|
101
|
+
organization: () => ({
|
|
102
|
+
name: faker.company.name(),
|
|
103
|
+
website: faker.datatype.boolean({ probability: 0.3 }) ? faker.internet.url() : undefined,
|
|
104
|
+
description: new Text(faker.lorem.sentences()),
|
|
105
|
+
}),
|
|
106
|
+
|
|
107
|
+
person: (provider) => {
|
|
108
|
+
const organizations = provider?.('organization');
|
|
109
|
+
return {
|
|
110
|
+
name: faker.person.fullName(),
|
|
111
|
+
email: faker.datatype.boolean({ probability: 0.5 }) ? faker.internet.email() : undefined,
|
|
112
|
+
org:
|
|
113
|
+
organizations?.length && faker.datatype.boolean({ probability: 0.3 })
|
|
114
|
+
? faker.helpers.arrayElement(organizations)
|
|
115
|
+
: undefined,
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
project: () => ({
|
|
120
|
+
title: faker.commerce.productName(),
|
|
121
|
+
repo: faker.datatype.boolean({ probability: 0.3 }) ? faker.internet.url() : undefined,
|
|
122
|
+
status: faker.helpers.arrayElement(Status),
|
|
123
|
+
priority: faker.helpers.arrayElement(Priority),
|
|
124
|
+
}),
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const createTestObjectGenerator = () => new TestObjectGenerator(testSchemas(), testObjectGenerators);
|
|
128
|
+
|
|
129
|
+
export const createSpaceObjectGenerator = (space: Space) =>
|
|
130
|
+
new SpaceObjectGenerator(space, testSchemas(), testObjectGenerators);
|
package/src/generator.test.ts
CHANGED
|
@@ -2,16 +2,43 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { faker } from '@faker-js/faker';
|
|
5
6
|
import { expect } from 'chai';
|
|
6
7
|
|
|
8
|
+
import { Client } from '@dxos/client';
|
|
7
9
|
import { describe, test } from '@dxos/test';
|
|
8
10
|
|
|
9
|
-
import {
|
|
11
|
+
import { createSpaceObjectGenerator, createTestObjectGenerator } from './data';
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
faker.seed(3);
|
|
14
|
+
|
|
15
|
+
describe('TestObjectGenerator', () => {
|
|
12
16
|
test('basic', () => {
|
|
13
|
-
const generator =
|
|
14
|
-
|
|
17
|
+
const generator = createTestObjectGenerator();
|
|
18
|
+
|
|
19
|
+
// Create raw object.
|
|
20
|
+
const object = generator.createObject({ types: ['person'] });
|
|
15
21
|
expect(object).to.exist;
|
|
16
22
|
});
|
|
23
|
+
|
|
24
|
+
test('with space', async () => {
|
|
25
|
+
const client = new Client();
|
|
26
|
+
await client.initialize();
|
|
27
|
+
await client.halo.createIdentity();
|
|
28
|
+
const space = await client.spaces.create();
|
|
29
|
+
const generator = createSpaceObjectGenerator(space);
|
|
30
|
+
|
|
31
|
+
// Add schemas.
|
|
32
|
+
generator.addSchemas();
|
|
33
|
+
|
|
34
|
+
// Create org object.
|
|
35
|
+
const organization = generator.createObject({ types: ['organization'] });
|
|
36
|
+
expect(organization.__schema).to.exist;
|
|
37
|
+
|
|
38
|
+
// Expect at least one person object with a linked org reference.
|
|
39
|
+
const objects = generator.createObjects({ types: ['person'], count: 10 });
|
|
40
|
+
expect(objects.some((object) => object.org === organization)).to.be.true;
|
|
41
|
+
|
|
42
|
+
await client.destroy();
|
|
43
|
+
});
|
|
17
44
|
});
|
package/src/generator.ts
CHANGED
|
@@ -7,140 +7,59 @@
|
|
|
7
7
|
|
|
8
8
|
import { faker } from '@faker-js/faker';
|
|
9
9
|
|
|
10
|
-
import { Expando, type
|
|
10
|
+
import { Expando, type Schema, type Space } from '@dxos/client/echo';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Array.from({ length })
|
|
15
|
-
.map((_, i) => fn(i))
|
|
16
|
-
.filter(Boolean) as T[];
|
|
17
|
-
|
|
18
|
-
// TODO(burdon): Commit to using ECHO to generate all test data? Or convert from raw data?
|
|
19
|
-
export type TestItem = { id: string; type: string } & Record<string, any>;
|
|
20
|
-
|
|
21
|
-
type ObjectDataGenerator = {
|
|
22
|
-
createSchema?: () => SchemaType;
|
|
23
|
-
createData: () => any;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
type ObjectFactory<T extends TypedObject> = {
|
|
27
|
-
schema?: SchemaType; // TODO(burdon): Support both typed and expando schema.
|
|
28
|
-
createObject: () => T;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
type ObjectFactoryMap = { [type: string]: ObjectFactory<any> };
|
|
32
|
-
|
|
33
|
-
const createFactory = ({ createSchema, createData }: ObjectDataGenerator) => {
|
|
34
|
-
const schema = createSchema?.();
|
|
35
|
-
return {
|
|
36
|
-
schema,
|
|
37
|
-
createObject: () => new Expando(createData(), { schema }),
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// TODO(burdon): Handle restricted values.
|
|
42
|
-
export const Status = ['pending', 'active', 'done'];
|
|
43
|
-
export const Priority = [1, 2, 3, 4, 5];
|
|
44
|
-
|
|
45
|
-
export const defaultGenerators: { [type: string]: ObjectDataGenerator } = {
|
|
46
|
-
document: {
|
|
47
|
-
createSchema: () =>
|
|
48
|
-
new SchemaType({
|
|
49
|
-
props: [
|
|
50
|
-
{
|
|
51
|
-
id: 'title',
|
|
52
|
-
type: SchemaType.PropType.STRING,
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
id: 'body',
|
|
56
|
-
type: SchemaType.PropType.STRING,
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
}),
|
|
60
|
-
createData: () => ({
|
|
61
|
-
title: faker.lorem.sentence(3),
|
|
62
|
-
body: faker.lorem.sentences({ min: 1, max: faker.number.int({ min: 1, max: 3 }) }),
|
|
63
|
-
}),
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
// TODO(burdon): Configure images.
|
|
67
|
-
image: {
|
|
68
|
-
createSchema: () =>
|
|
69
|
-
new SchemaType({
|
|
70
|
-
props: [
|
|
71
|
-
{
|
|
72
|
-
id: 'title',
|
|
73
|
-
type: SchemaType.PropType.STRING,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
id: 'body',
|
|
77
|
-
type: SchemaType.PropType.STRING,
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
}),
|
|
81
|
-
createData: () => ({
|
|
82
|
-
title: faker.lorem.sentence(3),
|
|
83
|
-
body: faker.datatype.boolean() ? faker.lorem.sentences() : undefined,
|
|
84
|
-
}),
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
project: {
|
|
88
|
-
createSchema: () =>
|
|
89
|
-
new SchemaType({
|
|
90
|
-
props: [
|
|
91
|
-
{
|
|
92
|
-
id: 'title',
|
|
93
|
-
type: SchemaType.PropType.STRING,
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
id: 'repo',
|
|
97
|
-
type: SchemaType.PropType.STRING,
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
id: 'status',
|
|
101
|
-
type: SchemaType.PropType.STRING,
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
id: 'priority',
|
|
105
|
-
type: SchemaType.PropType.NUMBER,
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
}),
|
|
109
|
-
createData: () => ({
|
|
110
|
-
title: faker.commerce.productName(),
|
|
111
|
-
repo: faker.datatype.boolean({ probability: 0.3 }) ? faker.internet.url() : undefined,
|
|
112
|
-
status: faker.helpers.arrayElement(Status),
|
|
113
|
-
priority: faker.helpers.arrayElement(Priority),
|
|
114
|
-
}),
|
|
115
|
-
},
|
|
116
|
-
};
|
|
12
|
+
import { type TestGeneratorMap, type TestObjectProvider, type TestSchemaMap } from './types';
|
|
13
|
+
import { range } from './util';
|
|
117
14
|
|
|
118
15
|
/**
|
|
119
16
|
* Typed object generator.
|
|
120
17
|
*/
|
|
121
|
-
export class TestObjectGenerator {
|
|
122
|
-
|
|
18
|
+
export class TestObjectGenerator<T extends string> {
|
|
19
|
+
// prettier-ignore
|
|
20
|
+
constructor(
|
|
21
|
+
public readonly schema: TestSchemaMap<T>,
|
|
22
|
+
private readonly _generators: TestGeneratorMap<T>,
|
|
23
|
+
private readonly _provider?: TestObjectProvider<T>
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
get schemas(): Schema[] {
|
|
27
|
+
return Object.values(this.schema);
|
|
28
|
+
}
|
|
123
29
|
|
|
124
|
-
|
|
125
|
-
this.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return acc;
|
|
130
|
-
}, {});
|
|
30
|
+
createObject({ types }: { types?: T[] } = {}): Expando {
|
|
31
|
+
const type = faker.helpers.arrayElement(types ?? (Object.keys(this.schema) as T[]));
|
|
32
|
+
const factory = this._generators[type];
|
|
33
|
+
const data = factory(this._provider);
|
|
34
|
+
return new Expando(data, { schema: this.schema[type] });
|
|
131
35
|
}
|
|
132
36
|
|
|
133
|
-
|
|
134
|
-
|
|
37
|
+
// TODO(burdon): Create batch.
|
|
38
|
+
createObjects({ types, count }: { types?: T[]; count: number }): Expando[] {
|
|
39
|
+
return range(() => this.createObject({ types }), count);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Typed object generator for a space.
|
|
45
|
+
*/
|
|
46
|
+
export class SpaceObjectGenerator<T extends string> extends TestObjectGenerator<T> {
|
|
47
|
+
constructor(private readonly _space: Space, schema: TestSchemaMap<T>, generators: TestGeneratorMap<T>) {
|
|
48
|
+
super(schema, generators, (type: T) => {
|
|
49
|
+
// TODO(burdon): Query by schema.
|
|
50
|
+
const { objects } = this._space.db.query((object) => {
|
|
51
|
+
return object.__schema === schema[type];
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return objects;
|
|
55
|
+
});
|
|
135
56
|
}
|
|
136
57
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const factory = this.factories[type];
|
|
140
|
-
return factory?.createObject();
|
|
58
|
+
addSchemas() {
|
|
59
|
+
this.schemas.forEach((schema) => this._space.db.add(schema));
|
|
141
60
|
}
|
|
142
61
|
|
|
143
|
-
|
|
144
|
-
return
|
|
62
|
+
override createObject({ types }: { types?: T[] } = {}): Expando {
|
|
63
|
+
return this._space.db.add(super.createObject({ types }));
|
|
145
64
|
}
|
|
146
65
|
}
|
package/src/index.ts
CHANGED
package/src/types.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Schema, type Expando } from '@dxos/client/echo';
|
|
6
|
+
|
|
7
|
+
export type TestObject = { id: string } & Record<string, any>;
|
|
8
|
+
|
|
9
|
+
export type TestSchemaMap<T extends string> = Record<T, Schema>;
|
|
10
|
+
|
|
11
|
+
export type TestObjectProvider<T extends string> = (type: T) => Expando[];
|
|
12
|
+
|
|
13
|
+
export type TestGeneratorMap<T extends string> = Record<T, (provider: TestObjectProvider<T> | undefined) => any>;
|