@toa.io/norm 1.0.0-alpha.3 → 1.0.0-alpha.31

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/norm",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.31",
4
4
  "description": "Toa declarations normalization and validation",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -20,11 +20,11 @@
20
20
  "test": "echo \"Error: run tests from root\" && exit 1"
21
21
  },
22
22
  "dependencies": {
23
- "@toa.io/core": "1.0.0-alpha.3",
24
- "@toa.io/generic": "1.0.0-alpha.3",
25
- "@toa.io/schema": "1.0.0-alpha.3",
26
- "@toa.io/schemas": "1.0.0-alpha.3",
27
- "@toa.io/yaml": "1.0.0-alpha.3"
23
+ "@toa.io/core": "1.0.0-alpha.31",
24
+ "@toa.io/generic": "1.0.0-alpha.31",
25
+ "@toa.io/schema": "1.0.0-alpha.31",
26
+ "@toa.io/schemas": "1.0.0-alpha.31",
27
+ "@toa.io/yaml": "1.0.0-alpha.31"
28
28
  },
29
- "gitHead": "e36ac7871fc14d15863aaf8f9bbdeace8bdfa9f0"
29
+ "gitHead": "91839a488176853a0731f49abc70dc7103ed7cca"
30
30
  }
@@ -7,6 +7,7 @@ const { extensions } = require('./extensions')
7
7
  const { operations } = require('./operations')
8
8
  const { properties } = require('./properties')
9
9
  const { receivers } = require('./receivers')
10
+ const { version } = require('./version')
10
11
 
11
12
  exports.bridge = bridge
12
13
  exports.entity = entity
@@ -15,3 +16,4 @@ exports.extensions = extensions
15
16
  exports.operations = operations
16
17
  exports.properties = properties
17
18
  exports.receivers = receivers
19
+ exports.version = version
@@ -0,0 +1,45 @@
1
+ 'use strict'
2
+
3
+ const { join } = require('node:path')
4
+ const { createHash } = require('node:crypto')
5
+ const fs = require('node:fs/promises')
6
+ const { createReadStream } = require('node:fs')
7
+ const { once } = require('node:events')
8
+
9
+ async function version (manifest) {
10
+ manifest.version ??= await hash(manifest.path)
11
+ }
12
+
13
+ async function hash (path) {
14
+ const hash = await hashd(path)
15
+
16
+ return hash.digest('hex').slice(0, 8)
17
+ }
18
+
19
+ /**
20
+ * @param {string} path
21
+ * @param {import('node:crypto').Hash} hash
22
+ */
23
+ async function hashd (path, hash = createHash('sha256')) {
24
+ const stat = await fs.stat(path)
25
+
26
+ if (stat.isFile()) {
27
+ const stream = createReadStream(path)
28
+
29
+ stream.pipe(hash, { end: false })
30
+
31
+ return await once(stream, 'end')
32
+ }
33
+
34
+ if (stat.isDirectory()) {
35
+ const entries = await fs.opendir(path)
36
+
37
+ for await (const entry of entries) {
38
+ await hashd(join(path, entry.name), hash)
39
+ }
40
+
41
+ return hash
42
+ }
43
+ }
44
+
45
+ exports.version = version
@@ -4,8 +4,11 @@ const { hash } = require('@toa.io/generic')
4
4
  const assert = require('node:assert')
5
5
 
6
6
  // these defaults are required before validation
7
- const defaults = (manifest) => {
8
- if (manifest.name === undefined) manifest.name = protoName(manifest)
7
+ const defaults = (manifest, proto) => {
8
+ if (manifest.name === undefined)
9
+ if (proto) manifest.name = protoName(manifest)
10
+ else nameAfterDir(manifest)
11
+
9
12
  if (manifest.bindings === undefined) manifest.bindings = ['@toa.io/bindings.amqp']
10
13
  if (manifest.bridge === undefined) manifest.bridge = '@toa.io/bridges.node'
11
14
 
@@ -18,25 +21,19 @@ const defaults = (manifest) => {
18
21
  }
19
22
 
20
23
  if (manifest.prototype === undefined) manifest.prototype = '@toa.io/prototype'
21
-
22
- // TODO: bridge.version()
23
-
24
- if (manifest.version === undefined) {
25
- const bridge = require(manifest.bridge)
26
-
27
- if ('version' in bridge)
28
- manifest.version = bridge.version(manifest)
29
- }
30
-
31
- if (manifest.version === undefined) {
32
- console.warn(`Component '${manifest.namespace ? manifest.namespace + '.' : ''}${manifest.name}' has no version`)
33
-
34
- manifest.version = Math.random().toString(36).slice(2)
35
- }
36
24
  }
37
25
 
38
26
  function protoName (manifest) {
39
27
  return 'proto' + hash(manifest.path)
40
28
  }
41
29
 
30
+ function nameAfterDir (manifest) {
31
+ const parts = manifest.path.split('/')
32
+ const dirname = parts[parts.length - 1]
33
+ const [name, namespace] = dirname.split('.').reverse()
34
+
35
+ manifest.name = name
36
+ manifest.namespace = namespace
37
+ }
38
+
42
39
  exports.defaults = defaults
@@ -7,10 +7,11 @@ const {
7
7
  events,
8
8
  receivers,
9
9
  extensions,
10
- properties
10
+ properties,
11
+ version
11
12
  } = require('./.expand')
12
13
 
13
- const expand = (manifest) => {
14
+ async function expand (manifest) {
14
15
  entity(manifest)
15
16
  bridge(manifest)
16
17
  operations(manifest)
@@ -18,6 +19,8 @@ const expand = (manifest) => {
18
19
  receivers(manifest)
19
20
  properties(manifest)
20
21
  extensions(manifest)
22
+
23
+ await version(manifest)
21
24
  }
22
25
 
23
26
  exports.expand = expand
@@ -79,8 +79,24 @@ properties:
79
79
  propertyNames:
80
80
  oneOf:
81
81
  - $ref: '#/definitions/name'
82
- - enum: [_version]
83
- dependent:
82
+ - enum: [_version, _created, _updated, _deleted]
83
+ unique:
84
+ type: object
85
+ patternProperties:
86
+ '.*':
87
+ type: array
88
+ items:
89
+ type: string
90
+ index:
91
+ type: object
92
+ patternProperties:
93
+ '.*':
94
+ type: object
95
+ patternProperties:
96
+ '.*':
97
+ type: string
98
+ enum: [asc, desc, hash]
99
+ associated:
84
100
  type: boolean
85
101
  default: false
86
102
  custom:
@@ -244,6 +260,8 @@ properties:
244
260
  adaptive:
245
261
  type: boolean
246
262
  default: false
263
+ arguments:
264
+ type: array
247
265
  required: [operation]
248
266
  additionalProperties: false
249
267
 
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { resolve } = require('node:path')
4
+
3
5
  const connectors = (context, extracted) => {
4
6
  const connectors = {}
5
7
 
@@ -10,11 +12,14 @@ const connectors = (context, extracted) => {
10
12
 
11
13
  for (const component of components) {
12
14
  if (component.entity !== undefined) {
13
- if (connectors[component.entity.storage] === undefined) {
14
- connectors[component.entity.storage] = []
15
+ let storage = component.entity.storage
16
+
17
+ if (storage[0] === '.') {
18
+ storage = resolve(component.path, storage)
15
19
  }
16
20
 
17
- connectors[component.entity.storage].push(component)
21
+ connectors[storage] ??= []
22
+ connectors[storage].push(component)
18
23
  }
19
24
 
20
25
  const bindings = new Set()
@@ -11,6 +11,7 @@ properties:
11
11
  type: string
12
12
  packages:
13
13
  type: string
14
+ default: components/*
14
15
  build:
15
16
  type: object
16
17
  properties:
@@ -64,5 +65,5 @@ properties:
64
65
  required: [name, components]
65
66
  annotations:
66
67
  type: object
67
- required: [runtime, name, packages, registry]
68
+ required: [runtime, name, registry]
68
69
  additionalProperties: false
package/src/component.js CHANGED
@@ -20,7 +20,7 @@ const {
20
20
  const component = async (path) => {
21
21
  const manifest = await load(path)
22
22
 
23
- normalize(manifest)
23
+ normalize(manifest, path)
24
24
  validate(manifest)
25
25
  extensions(manifest)
26
26
 
@@ -29,7 +29,7 @@ const component = async (path) => {
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)
@@ -37,13 +37,13 @@ const load = async (path, base) => {
37
37
 
38
38
  manifest.path = path
39
39
 
40
- defaults(manifest)
41
- expand(manifest)
40
+ defaults(manifest, proto)
41
+ await expand(manifest)
42
42
 
43
43
  await merge(path, manifest)
44
44
 
45
45
  if (manifest.prototype !== null) {
46
- const prototype = await load(manifest.prototype, path)
46
+ const prototype = await load(manifest.prototype, path, true)
47
47
 
48
48
  collapse(manifest, prototype)
49
49
  }
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const source = {
4
+ path: '',
4
5
  entity: {
5
6
  schema: {
6
7
  properties: {
@@ -112,15 +112,18 @@ describe('entity', () => {
112
112
  })
113
113
 
114
114
  it('should allow default id', () => {
115
- manifest.entity.schema.properties.id = { type: 'string', pattern: '^[a-fA-F0-9]+$' }
115
+ manifest.entity.schema.properties.id = {
116
+ type: 'string',
117
+ pattern: '^[a-fA-F0-9]+$'
118
+ }
116
119
  expect(() => validate(manifest)).not.toThrow()
117
120
  })
118
121
  })
119
122
 
120
- describe('dependent', () => {
123
+ describe('associated', () => {
121
124
  it('should provide default', () => {
122
125
  expect(() => validate(manifest)).not.toThrow()
123
- expect(manifest.entity.dependent).toBe(false)
126
+ expect(manifest.entity.associated).toBe(false)
124
127
  })
125
128
  })
126
129
  })
@@ -72,9 +72,10 @@ it('should require name as label', () => {
72
72
  expect(() => validate(context)).not.toThrow(/pattern/)
73
73
  })
74
74
 
75
- it('should require packages location', () => {
75
+ it('should set default packages location', () => {
76
76
  delete context.packages
77
- expect(() => validate(context)).toThrow(/required/)
77
+ expect(() => validate(context)).not.toThrow()
78
+ expect(context.packages).toBe('components/*')
78
79
  })
79
80
 
80
81
  it('should require registry url', () => {
@@ -37,7 +37,7 @@ type Receiver = {
37
37
  type Entity = {
38
38
  schema: Object
39
39
  storage?: string
40
- dependent?: boolean
40
+ associated?: boolean
41
41
  }
42
42
 
43
43
  type Declaration = {