@toa.io/norm 1.0.0-alpha.29 → 1.0.0-alpha.291
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/CHANGELOG.md +35 -0
- package/package.json +8 -7
- package/src/.component/.expand/bridge.js +2 -6
- package/src/.component/.expand/entity.js +2 -9
- package/src/.component/.expand/events.js +2 -6
- package/src/.component/.expand/extensions.js +5 -8
- package/src/.component/.expand/index.js +8 -19
- package/src/.component/.expand/operations.js +2 -9
- package/src/.component/.expand/properties.js +2 -6
- package/src/.component/.expand/receivers.js +4 -7
- package/src/.component/.expand/version.js +38 -28
- package/src/.component/.normalize/entity.js +7 -0
- package/src/.component/.normalize/events.js +15 -5
- package/src/.component/.normalize/index.js +4 -9
- package/src/.component/.normalize/operations.js +10 -6
- package/src/.component/.normalize/receivers.js +4 -5
- package/src/.component/collapse.js +60 -9
- package/src/.component/defaults.js +16 -10
- package/src/.component/dependencies.js +14 -10
- package/src/.component/dereference.js +20 -18
- package/src/.component/expand.js +3 -7
- package/src/.component/extensions.js +15 -8
- package/src/.component/index.js +10 -21
- package/src/.component/merge.js +24 -13
- package/src/.component/migrations.js +57 -0
- package/src/.component/normalize.js +10 -6
- package/src/.component/schema.yaml +108 -71
- package/src/.component/validate.js +36 -17
- package/src/.context/.dependencies/connectors.js +5 -9
- package/src/.context/.dependencies/describe.js +4 -5
- package/src/.context/.dependencies/extensions.js +62 -20
- package/src/.context/.dependencies/index.js +3 -9
- package/src/.context/.dependencies/load.js +17 -14
- package/src/.context/.dependencies/resolve.js +23 -6
- package/src/.context/complete.js +1 -5
- package/src/.context/dependencies.js +2 -6
- package/src/.context/dereference.js +11 -6
- package/src/.context/expand.js +8 -6
- package/src/.context/index.js +6 -15
- package/src/.context/normalize.js +7 -5
- package/src/.context/schema.yaml +132 -11
- package/src/.context/validate.js +8 -14
- package/src/component.js +36 -17
- package/src/context.js +26 -14
- package/src/entity.js +15 -0
- package/src/index.js +4 -9
- package/src/shortcuts.js +8 -13
- package/test/component/collapse.fixtures.js +30 -39
- package/test/component/collapse.test.js +90 -10
- package/test/component/dependencies.test.js +16 -14
- package/test/component/dereference.fixtures.js +15 -24
- package/test/component/dereference.test.js +18 -8
- package/test/component/dummies/extension/index.js +1 -5
- package/test/component/dummies/migrations/conflicting/migrations/0001-one.yaml +2 -0
- package/test/component/dummies/migrations/conflicting/migrations/0001-one.yml +2 -0
- package/test/component/dummies/migrations/malformed/migrations/0001-one.yaml +1 -0
- package/test/component/dummies/migrations/ordered/migrations/0001-first.yml +4 -0
- package/test/component/dummies/migrations/ordered/migrations/0002-second.yaml +4 -0
- package/test/component/dummies/migrations/ordered/migrations/0010-third.json +1 -0
- package/test/component/dummies/migrations/stateless/migrations/0001-one.yaml +2 -0
- package/test/component/expand.fixtures.js +13 -13
- package/test/component/expand.test.js +25 -10
- package/test/component/migrations.test.js +62 -0
- package/test/component/normalize.fixtures.js +3 -7
- package/test/component/normalize.test.js +86 -28
- package/test/component/validate.fixtures.js +15 -13
- package/test/component/validate.test.js +162 -109
- package/test/context/complete.fixtures.js +2 -8
- package/test/context/complete.test.js +14 -8
- package/test/context/dependencies.load.test.js +13 -12
- package/test/context/dereference.fixtures.js +3 -9
- package/test/context/dereference.test.js +18 -7
- package/test/context/expand.fixtures.js +1 -5
- package/test/context/expand.test.js +36 -8
- package/test/context/normalize.fixtures.js +2 -7
- package/test/context/normalize.test.js +33 -9
- package/test/context/validate.fixtures.js +1 -6
- package/test/context/validate.test.js +81 -37
- package/test/shortcuts.fixtures.js +4 -9
- package/test/shortcuts.test.js +17 -15
- package/types/component.d.ts +17 -3
- package/types/context/declaration.d.ts +26 -3
- package/types/context.d.ts +15 -13
- package/types/entity.d.ts +7 -0
- package/types/index.d.ts +4 -3
package/src/.context/validate.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { yaml } from '@toa.io/generic'
|
|
5
|
+
import * as schemas from '@toa.io/schemas'
|
|
4
6
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
+
const path = resolve(import.meta.dirname, 'schema.yaml')
|
|
8
|
+
const object = yaml.load(readFileSync(path, 'utf8'))
|
|
9
|
+
const schema = schemas.schema(object)
|
|
7
10
|
|
|
8
|
-
const
|
|
9
|
-
const object = load.sync(path)
|
|
10
|
-
const schema = new Schema(object)
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @param {toa.norm.context.Declaration} context
|
|
14
|
-
*/
|
|
15
|
-
const validate = (context) => {
|
|
11
|
+
export const validate = (context) => {
|
|
16
12
|
schema.validate(context)
|
|
17
13
|
}
|
|
18
|
-
|
|
19
|
-
exports.validate = validate
|
package/src/component.js
CHANGED
|
@@ -1,49 +1,57 @@
|
|
|
1
|
-
|
|
1
|
+
import { join } from 'node:path'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { readFile } from 'node:fs/promises'
|
|
4
|
+
import { yaml as jsyaml } from '@toa.io/generic'
|
|
5
|
+
import { find } from '@toa.io/generic'
|
|
6
|
+
import { Locator } from '@toa.io/core'
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
const { directory: { find } } = require('@toa.io/filesystem')
|
|
7
|
-
const { Locator } = require('@toa.io/core')
|
|
8
|
-
|
|
9
|
-
const {
|
|
8
|
+
import {
|
|
10
9
|
expand,
|
|
11
10
|
merge,
|
|
11
|
+
migrations,
|
|
12
12
|
validate,
|
|
13
13
|
collapse,
|
|
14
14
|
dereference,
|
|
15
15
|
defaults,
|
|
16
16
|
normalize,
|
|
17
17
|
extensions
|
|
18
|
-
}
|
|
18
|
+
} from './.component/index.js'
|
|
19
19
|
|
|
20
|
-
const component = async (path) => {
|
|
20
|
+
export const component = async (path) => {
|
|
21
21
|
const manifest = await load(path)
|
|
22
22
|
|
|
23
|
-
normalize(manifest)
|
|
24
|
-
validate(manifest)
|
|
25
|
-
extensions(manifest)
|
|
23
|
+
await normalize(manifest, path)
|
|
24
|
+
await validate(manifest)
|
|
25
|
+
await extensions(manifest)
|
|
26
26
|
|
|
27
27
|
manifest.locator = new Locator(manifest.name, manifest.namespace)
|
|
28
28
|
|
|
29
29
|
return manifest
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const load = async (path, base) => {
|
|
32
|
+
const load = async (path, base, proto = false) => {
|
|
33
33
|
if (base !== undefined) path = find(path, base, MANIFEST)
|
|
34
34
|
|
|
35
35
|
const file = join(path, MANIFEST)
|
|
36
|
-
const manifest =
|
|
36
|
+
const manifest = (await read(file)) ?? {}
|
|
37
37
|
|
|
38
38
|
manifest.path = path
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
const anonymous = manifest.name === undefined
|
|
41
|
+
|
|
42
|
+
defaults(manifest, proto)
|
|
41
43
|
await expand(manifest)
|
|
42
44
|
|
|
43
45
|
await merge(path, manifest)
|
|
46
|
+
await migrations(path, manifest)
|
|
47
|
+
|
|
48
|
+
// an inherited migration is recorded under the prototype's name, and a generated one is not
|
|
49
|
+
// the same on the next machine
|
|
50
|
+
if (proto && anonymous && manifest.entity?.migrations !== undefined)
|
|
51
|
+
throw new Error(`Prototype at '${path}' declares migrations, so it has to be named`)
|
|
44
52
|
|
|
45
53
|
if (manifest.prototype !== null) {
|
|
46
|
-
const prototype = await load(manifest.prototype, path)
|
|
54
|
+
const prototype = await load(manifest.prototype, path, true)
|
|
47
55
|
|
|
48
56
|
collapse(manifest, prototype)
|
|
49
57
|
}
|
|
@@ -56,4 +64,15 @@ const load = async (path, base) => {
|
|
|
56
64
|
|
|
57
65
|
const MANIFEST = 'manifest.toa.yaml'
|
|
58
66
|
|
|
59
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Reads a YAML file, resolving anchors into distinct objects so that
|
|
69
|
+
* mutating one node cannot reach another.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} path
|
|
72
|
+
* @return {Promise<object>}
|
|
73
|
+
*/
|
|
74
|
+
async function read(path) {
|
|
75
|
+
const object = jsyaml.load(await readFile(path, 'utf8'))
|
|
76
|
+
|
|
77
|
+
return jsyaml.load(jsyaml.dump(object, { noRefs: true, lineWidth: -1 }))
|
|
78
|
+
}
|
package/src/context.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import { convolve, environment as variables } from '@toa.io/generic'
|
|
3
|
+
import glob from 'fast-glob'
|
|
4
|
+
import { readFile } from 'node:fs/promises'
|
|
5
|
+
import { yaml as jsyaml } from '@toa.io/generic'
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
const { convolve } = require('@toa.io/generic')
|
|
5
|
-
const { directory: { glob } } = require('@toa.io/filesystem')
|
|
6
|
-
const { load } = require('@toa.io/yaml')
|
|
7
|
+
import { component } from './component.js'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const {
|
|
9
|
+
import {
|
|
11
10
|
dependencies,
|
|
12
11
|
normalize,
|
|
13
12
|
complete,
|
|
14
13
|
dereference,
|
|
15
14
|
expand,
|
|
16
15
|
validate
|
|
17
|
-
}
|
|
16
|
+
} from './.context/index.js'
|
|
18
17
|
|
|
19
|
-
const context = async (root, environment =
|
|
18
|
+
export const context = async (root, environment = variables.get('TOA_ENV')) => {
|
|
20
19
|
const path = resolve(root, CONTEXT)
|
|
21
|
-
const context = /** @type {toa.norm.Context} */ await
|
|
22
|
-
const pattern = resolve(root, context.packages)
|
|
20
|
+
const context = /** @type {toa.norm.Context} */ await read(path)
|
|
23
21
|
|
|
24
22
|
context.environment = environment
|
|
25
23
|
|
|
@@ -29,7 +27,7 @@ const context = async (root, environment = process.env.TOA_ENV) => {
|
|
|
29
27
|
|
|
30
28
|
validate(context)
|
|
31
29
|
|
|
32
|
-
const paths = await glob(
|
|
30
|
+
const paths = await glob(resolve(root, COMPONENTS), GLOB)
|
|
33
31
|
|
|
34
32
|
context.components = await Promise.all(paths.map(component))
|
|
35
33
|
context.dependencies = await dependencies(context)
|
|
@@ -41,5 +39,19 @@ const context = async (root, environment = process.env.TOA_ENV) => {
|
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
const CONTEXT = 'context.toa.yaml'
|
|
42
|
+
const COMPONENTS = 'components/*'
|
|
43
|
+
|
|
44
|
+
const GLOB = { onlyDirectories: true, absolute: true }
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Reads a YAML file, resolving anchors into distinct objects so that
|
|
48
|
+
* mutating one node cannot reach another.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} path
|
|
51
|
+
* @return {Promise<object>}
|
|
52
|
+
*/
|
|
53
|
+
async function read(path) {
|
|
54
|
+
const object = jsyaml.load(await readFile(path, 'utf8'))
|
|
55
|
+
|
|
56
|
+
return jsyaml.load(jsyaml.dump(object, { noRefs: true, lineWidth: -1 }))
|
|
57
|
+
}
|
package/src/entity.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a component declares about its records is a property map and a list of the properties a
|
|
3
|
+
* whole one has. A validator wants a schema, so it is assembled here rather than declared: one
|
|
4
|
+
* that a stored record must fit, and one for a changeset, which is whatever subset of the same
|
|
5
|
+
* properties an assignment writes.
|
|
6
|
+
*/
|
|
7
|
+
export const schema = (entity) =>
|
|
8
|
+
entity.required === undefined
|
|
9
|
+
? changeset(entity)
|
|
10
|
+
: { type: 'object', properties: entity.properties, required: entity.required }
|
|
11
|
+
|
|
12
|
+
export const changeset = (entity) => ({
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: entity.properties
|
|
15
|
+
})
|
package/src/index.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
exports.shortcuts = require('./shortcuts')
|
|
7
|
-
|
|
8
|
-
exports.context = context
|
|
9
|
-
exports.component = component
|
|
1
|
+
export { context } from './context.js'
|
|
2
|
+
export { component } from './component.js'
|
|
3
|
+
export * as shortcuts from './shortcuts.js'
|
|
4
|
+
export * as entity from './entity.js'
|
package/src/shortcuts.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { empty, merge } = require('@toa.io/generic')
|
|
1
|
+
import { empty, merge } from '@toa.io/generic'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Resolves shortcuts
|
|
@@ -8,7 +6,7 @@ const { empty, merge } = require('@toa.io/generic')
|
|
|
8
6
|
* @param {string} token
|
|
9
7
|
* @returns {string}
|
|
10
8
|
*/
|
|
11
|
-
const resolve = (token) => {
|
|
9
|
+
export const resolve = (token) => {
|
|
12
10
|
if (token in SHORTCUTS) return SHORTCUTS[token]
|
|
13
11
|
else return token
|
|
14
12
|
}
|
|
@@ -20,7 +18,7 @@ const resolve = (token) => {
|
|
|
20
18
|
* @param {Object} object
|
|
21
19
|
* @param {string} [group]
|
|
22
20
|
*/
|
|
23
|
-
const recognize = (shortcuts, object, group) => {
|
|
21
|
+
export const recognize = (shortcuts, object, group) => {
|
|
24
22
|
if (object === undefined) return
|
|
25
23
|
|
|
26
24
|
const target = group === undefined ? object : {}
|
|
@@ -38,21 +36,18 @@ const recognize = (shortcuts, object, group) => {
|
|
|
38
36
|
if (group !== undefined && !empty(target)) object[group] = merge(object[group], target)
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
const SHORTCUTS = {
|
|
39
|
+
export const SHORTCUTS = {
|
|
42
40
|
amqp: '@toa.io/bindings.amqp',
|
|
43
41
|
node: '@toa.io/bridges.node',
|
|
44
42
|
bash: '@toa.io/bridges.bash',
|
|
45
43
|
mongodb: '@toa.io/storages.mongodb',
|
|
46
|
-
sql: '@toa.io/storages.sql',
|
|
47
44
|
queues: '@toa.io/storages.queues',
|
|
48
45
|
exposition: '@toa.io/extensions.exposition',
|
|
49
46
|
realtime: '@toa.io/extensions.realtime',
|
|
50
47
|
configuration: '@toa.io/extensions.configuration',
|
|
51
|
-
origins: '@toa.io/extensions.origins',
|
|
52
48
|
stash: '@toa.io/extensions.stash',
|
|
53
|
-
storages: '@toa.io/extensions.storages'
|
|
49
|
+
storages: '@toa.io/extensions.storages',
|
|
50
|
+
telemetry: '@toa.io/extensions.telemetry',
|
|
51
|
+
cadence: '@toa.io/extensions.cadence',
|
|
52
|
+
introspection: '@toa.io/extensions.introspection'
|
|
54
53
|
}
|
|
55
|
-
|
|
56
|
-
exports.recognize = recognize
|
|
57
|
-
exports.resolve = resolve
|
|
58
|
-
exports.SHORTCUTS = SHORTCUTS
|
|
@@ -1,55 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { generate } = require('randomstring')
|
|
1
|
+
import { generate } from 'randomstring'
|
|
4
2
|
|
|
5
3
|
const entity = {
|
|
6
4
|
manifest: {
|
|
7
5
|
entity: {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
type: 'string'
|
|
12
|
-
},
|
|
13
|
-
bar: {
|
|
14
|
-
type: 'integer'
|
|
15
|
-
}
|
|
6
|
+
properties: {
|
|
7
|
+
foo: {
|
|
8
|
+
type: 'string'
|
|
16
9
|
},
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
bar: {
|
|
11
|
+
type: 'integer'
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
required: ['foo']
|
|
19
15
|
}
|
|
20
16
|
},
|
|
21
17
|
prototype: {
|
|
22
18
|
entity: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
default: 'ok'
|
|
27
|
-
},
|
|
28
|
-
baz: {
|
|
29
|
-
type: 'boolean'
|
|
30
|
-
}
|
|
19
|
+
properties: {
|
|
20
|
+
foo: {
|
|
21
|
+
minLength: 1
|
|
31
22
|
},
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
baz: {
|
|
24
|
+
type: 'boolean'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
required: ['baz']
|
|
34
28
|
}
|
|
35
29
|
},
|
|
36
30
|
result: {
|
|
37
31
|
entity: {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
default: 'ok'
|
|
43
|
-
},
|
|
44
|
-
bar: {
|
|
45
|
-
type: 'integer'
|
|
46
|
-
},
|
|
47
|
-
baz: {
|
|
48
|
-
type: 'boolean'
|
|
49
|
-
}
|
|
32
|
+
properties: {
|
|
33
|
+
foo: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
minLength: 1
|
|
50
36
|
},
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
bar: {
|
|
38
|
+
type: 'integer'
|
|
39
|
+
},
|
|
40
|
+
baz: {
|
|
41
|
+
type: 'boolean'
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
required: ['foo', 'baz']
|
|
53
45
|
}
|
|
54
46
|
}
|
|
55
47
|
}
|
|
@@ -88,7 +80,6 @@ const operations = {
|
|
|
88
80
|
result: {
|
|
89
81
|
prototype: {
|
|
90
82
|
prototype: null,
|
|
91
|
-
path: expect.any(String),
|
|
92
83
|
operations: {
|
|
93
84
|
add: {
|
|
94
85
|
bridge: 'a'
|
|
@@ -131,4 +122,4 @@ const remotes = {
|
|
|
131
122
|
}
|
|
132
123
|
}
|
|
133
124
|
|
|
134
|
-
|
|
125
|
+
export const samples = { entity, operations, remotes }
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import clone from 'clone-deep'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import * as fixtures from './collapse.fixtures.js'
|
|
7
|
+
import { collapse } from '../../src/.component/index.js'
|
|
7
8
|
|
|
8
9
|
let samples
|
|
9
10
|
|
|
@@ -18,7 +19,7 @@ it('should ignore locator', () => {
|
|
|
18
19
|
|
|
19
20
|
collapse(manifest, prototype)
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
assert.deepStrictEqual(manifest, source)
|
|
22
23
|
})
|
|
23
24
|
|
|
24
25
|
it('should remove prototype property', () => {
|
|
@@ -26,7 +27,7 @@ it('should remove prototype property', () => {
|
|
|
26
27
|
|
|
27
28
|
collapse(manifest, {})
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
assert.deepStrictEqual(manifest, {})
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
describe('entity', () => {
|
|
@@ -34,7 +35,80 @@ describe('entity', () => {
|
|
|
34
35
|
const manifest = clone(samples.entity.manifest)
|
|
35
36
|
|
|
36
37
|
collapse(manifest, samples.entity.prototype)
|
|
37
|
-
|
|
38
|
+
assert.deepStrictEqual(manifest, samples.entity.result)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should let a component state its own id', () => {
|
|
42
|
+
const prototype = { entity: { properties: { id: { type: 'string' }, VERSION: {} } } }
|
|
43
|
+
const manifest = { name: 'pot', entity: { properties: { id: { type: 'number' } } } }
|
|
44
|
+
|
|
45
|
+
collapse(manifest, prototype)
|
|
46
|
+
|
|
47
|
+
assert.strictEqual(manifest.entity.custom, true)
|
|
48
|
+
assert.deepStrictEqual(manifest.entity.properties.id, { type: 'number' })
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should refuse a component that states a property the runtime writes', () => {
|
|
52
|
+
const prototype = { entity: { properties: { DELETED: { type: 'integer' } } } }
|
|
53
|
+
const manifest = {
|
|
54
|
+
name: 'pot',
|
|
55
|
+
entity: { properties: { DELETED: { type: 'integer' } } }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
assert.throws(
|
|
59
|
+
() => collapse(manifest, prototype),
|
|
60
|
+
/System property 'DELETED' cannot be overridden/
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should inherit migrations ahead of its own, under the prototype name', () => {
|
|
65
|
+
const manifest = { entity: { migrations: [{ id: '0002', steps: [] }] } }
|
|
66
|
+
const prototype = {
|
|
67
|
+
name: 'base',
|
|
68
|
+
entity: { migrations: [{ id: '0001', steps: [] }] }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
collapse(manifest, prototype)
|
|
72
|
+
|
|
73
|
+
assert.deepStrictEqual(manifest.entity.migrations, [
|
|
74
|
+
{ id: 'base:0001', steps: [], prototype: 'base' },
|
|
75
|
+
{ id: '0002', steps: [] }
|
|
76
|
+
])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should pass on what the prototype inherited as it is', () => {
|
|
80
|
+
const manifest = { entity: { migrations: [{ id: '0002', steps: [] }] } }
|
|
81
|
+
const prototype = {
|
|
82
|
+
name: 'base',
|
|
83
|
+
entity: {
|
|
84
|
+
migrations: [
|
|
85
|
+
{ id: 'system:0001', steps: [], prototype: 'system' },
|
|
86
|
+
{ id: '0001', steps: [] }
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
collapse(manifest, prototype)
|
|
92
|
+
|
|
93
|
+
assert.deepStrictEqual(
|
|
94
|
+
manifest.entity.migrations.map(({ id }) => id),
|
|
95
|
+
['system:0001', 'base:0001', '0002']
|
|
96
|
+
)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('should inherit migrations into a component that declares none', () => {
|
|
100
|
+
const manifest = {}
|
|
101
|
+
const prototype = {
|
|
102
|
+
name: 'base',
|
|
103
|
+
entity: { migrations: [{ id: '0001', steps: [] }] }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
collapse(manifest, prototype)
|
|
107
|
+
|
|
108
|
+
assert.deepStrictEqual(
|
|
109
|
+
manifest.entity.migrations.map(({ id }) => id),
|
|
110
|
+
['base:0001']
|
|
111
|
+
)
|
|
38
112
|
})
|
|
39
113
|
})
|
|
40
114
|
|
|
@@ -44,12 +118,12 @@ it('should ignore bindings', () => {
|
|
|
44
118
|
const manifest = clone(source)
|
|
45
119
|
|
|
46
120
|
collapse(manifest, prototype)
|
|
47
|
-
|
|
121
|
+
assert.deepStrictEqual(manifest, source)
|
|
48
122
|
|
|
49
123
|
delete manifest.bindings
|
|
50
124
|
|
|
51
125
|
collapse(manifest, prototype)
|
|
52
|
-
|
|
126
|
+
assert.deepStrictEqual(manifest, {})
|
|
53
127
|
})
|
|
54
128
|
|
|
55
129
|
it('should merge operations', () => {
|
|
@@ -57,5 +131,11 @@ it('should merge operations', () => {
|
|
|
57
131
|
const prototype = clone(samples.operations.prototype)
|
|
58
132
|
|
|
59
133
|
collapse(manifest, prototype, '/somewhere')
|
|
60
|
-
|
|
134
|
+
|
|
135
|
+
// the prototype's path is generated, so it is checked apart from the shape
|
|
136
|
+
assert.strictEqual(typeof manifest.prototype.path, 'string')
|
|
137
|
+
|
|
138
|
+
const { path, ...rest } = manifest.prototype
|
|
139
|
+
|
|
140
|
+
assert.deepStrictEqual({ ...manifest, prototype: rest }, samples.operations.result)
|
|
61
141
|
})
|
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
import { resolve } from 'node:path'
|
|
5
|
+
import { dependencies } from '../../src/.component/index.js'
|
|
5
6
|
|
|
6
|
-
const NORM = resolve(
|
|
7
|
+
const NORM = resolve(import.meta.dirname, '../../')
|
|
7
8
|
|
|
8
9
|
it('should be', async () => {
|
|
9
|
-
|
|
10
|
+
assert.ok(dependencies instanceof Function)
|
|
10
11
|
})
|
|
11
12
|
|
|
12
13
|
/** @type {toa.norm.Component} */
|
|
13
14
|
let component
|
|
14
15
|
|
|
15
16
|
beforeEach(() => {
|
|
16
|
-
component = /** @type {toa.norm.Component} */ { path:
|
|
17
|
+
component = /** @type {toa.norm.Component} */ { path: import.meta.dirname }
|
|
17
18
|
})
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
for (const [_, reference] of [
|
|
20
21
|
['package id', '@toa.io/norm'],
|
|
21
22
|
['relative path', '../../']
|
|
22
|
-
])
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
])
|
|
24
|
+
describe(`${_}`, () => {
|
|
25
|
+
it('should resolve storage', async () => {
|
|
26
|
+
component.entity = { storage: reference, schema: {} }
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
dependencies(component)
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
assert.deepStrictEqual(component.entity.storage, NORM)
|
|
31
|
+
})
|
|
29
32
|
})
|
|
30
|
-
})
|
|
31
33
|
|
|
32
34
|
it('should resolve toa packages', async () => {
|
|
33
35
|
component.entity = { storage: '@toa.io/core', schema: {} }
|
|
34
36
|
|
|
35
37
|
dependencies(component)
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
assert.notStrictEqual(component.entity.storage, undefined)
|
|
38
40
|
})
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const source = {
|
|
1
|
+
export const source = {
|
|
4
2
|
entity: {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
default: '.foo'
|
|
13
|
-
}
|
|
3
|
+
properties: {
|
|
4
|
+
foo: {
|
|
5
|
+
type: 'string'
|
|
6
|
+
},
|
|
7
|
+
bar: {
|
|
8
|
+
type: 'string',
|
|
9
|
+
default: '.foo'
|
|
14
10
|
}
|
|
15
11
|
}
|
|
16
12
|
},
|
|
@@ -66,16 +62,14 @@ const source = {
|
|
|
66
62
|
}
|
|
67
63
|
}
|
|
68
64
|
|
|
69
|
-
const target = {
|
|
65
|
+
export const target = {
|
|
70
66
|
entity: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
type: 'string'
|
|
78
|
-
}
|
|
67
|
+
properties: {
|
|
68
|
+
foo: {
|
|
69
|
+
type: 'string'
|
|
70
|
+
},
|
|
71
|
+
bar: {
|
|
72
|
+
type: 'string'
|
|
79
73
|
}
|
|
80
74
|
}
|
|
81
75
|
},
|
|
@@ -160,6 +154,3 @@ const target = {
|
|
|
160
154
|
}
|
|
161
155
|
}
|
|
162
156
|
}
|
|
163
|
-
|
|
164
|
-
exports.source = source
|
|
165
|
-
exports.target = target
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { it, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import clone from 'clone-deep'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import { dereference } from '../../src/.component/index.js'
|
|
7
|
+
import * as fixtures from './dereference.fixtures.js'
|
|
7
8
|
|
|
8
9
|
let source
|
|
9
10
|
|
|
@@ -14,18 +15,27 @@ beforeEach(() => {
|
|
|
14
15
|
it('should dereference', () => {
|
|
15
16
|
dereference(source)
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
assert.deepStrictEqual(source, fixtures.target)
|
|
18
19
|
})
|
|
19
20
|
|
|
20
21
|
it('should throw on invalid schema reference', () => {
|
|
21
22
|
source.operations.transit.output.properties.baz = { type: 'string', default: '.' }
|
|
22
|
-
|
|
23
|
+
assert.throws(
|
|
24
|
+
() => dereference(source),
|
|
25
|
+
(error) => /is not defined/.test(error.message)
|
|
26
|
+
)
|
|
23
27
|
|
|
24
28
|
source.operations.transit.output.properties.baz = { type: 'string', default: '.baz' }
|
|
25
|
-
|
|
29
|
+
assert.throws(
|
|
30
|
+
() => dereference(source),
|
|
31
|
+
(error) => /is not defined/.test(error.message)
|
|
32
|
+
)
|
|
26
33
|
})
|
|
27
34
|
|
|
28
35
|
it('should throw on invalid forwarding', () => {
|
|
29
36
|
source.operations.create.forward = 'foo'
|
|
30
|
-
|
|
37
|
+
assert.throws(
|
|
38
|
+
() => dereference(source),
|
|
39
|
+
(error) => /is not defined/.test(error.message)
|
|
40
|
+
)
|
|
31
41
|
})
|