@toa.io/norm 1.0.0-alpha.283 → 1.0.0-alpha.286

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 (67) hide show
  1. package/package.json +5 -5
  2. package/src/.component/.expand/bridge.js +1 -1
  3. package/src/.component/.expand/entity.js +1 -1
  4. package/src/.component/.expand/events.js +1 -1
  5. package/src/.component/.expand/extensions.js +2 -1
  6. package/src/.component/.expand/operations.js +1 -1
  7. package/src/.component/.expand/properties.js +1 -1
  8. package/src/.component/.expand/receivers.js +3 -2
  9. package/src/.component/.expand/version.js +11 -13
  10. package/src/.component/.normalize/entity.js +7 -0
  11. package/src/.component/.normalize/events.js +4 -1
  12. package/src/.component/.normalize/index.js +1 -0
  13. package/src/.component/.normalize/operations.js +9 -1
  14. package/src/.component/.normalize/receivers.js +4 -1
  15. package/src/.component/collapse.js +14 -6
  16. package/src/.component/defaults.js +3 -4
  17. package/src/.component/dependencies.js +7 -4
  18. package/src/.component/dereference.js +14 -13
  19. package/src/.component/expand.js +11 -2
  20. package/src/.component/extensions.js +2 -1
  21. package/src/.component/index.js +1 -0
  22. package/src/.component/merge.js +3 -5
  23. package/src/.component/migrations.js +57 -0
  24. package/src/.component/normalize.js +9 -1
  25. package/src/.component/schema.yaml +32 -39
  26. package/src/.component/validate.js +23 -3
  27. package/src/.context/.dependencies/connectors.js +3 -3
  28. package/src/.context/.dependencies/describe.js +4 -1
  29. package/src/.context/.dependencies/extensions.js +18 -13
  30. package/src/.context/.dependencies/load.js +6 -4
  31. package/src/.context/.dependencies/resolve.js +1 -1
  32. package/src/.context/dereference.js +3 -1
  33. package/src/.context/normalize.js +5 -0
  34. package/src/.context/schema.yaml +6 -4
  35. package/src/component.js +14 -5
  36. package/src/context.js +9 -5
  37. package/src/entity.js +15 -0
  38. package/src/index.js +1 -0
  39. package/src/shortcuts.js +1 -1
  40. package/test/component/collapse.fixtures.js +28 -34
  41. package/test/component/collapse.test.js +21 -0
  42. package/test/component/dependencies.test.js +6 -6
  43. package/test/component/dereference.fixtures.js +13 -17
  44. package/test/component/dereference.test.js +12 -3
  45. package/test/component/dummies/migrations/conflicting/migrations/0001-one.yaml +2 -0
  46. package/test/component/dummies/migrations/conflicting/migrations/0001-one.yml +2 -0
  47. package/test/component/dummies/migrations/malformed/migrations/0001-one.yaml +1 -0
  48. package/test/component/dummies/migrations/ordered/migrations/0001-first.yml +4 -0
  49. package/test/component/dummies/migrations/ordered/migrations/0002-second.yaml +4 -0
  50. package/test/component/dummies/migrations/ordered/migrations/0010-third.json +1 -0
  51. package/test/component/dummies/migrations/stateless/migrations/0001-one.yaml +2 -0
  52. package/test/component/migrations.test.js +62 -0
  53. package/test/component/normalize.test.js +51 -13
  54. package/test/component/validate.fixtures.js +14 -8
  55. package/test/component/validate.test.js +113 -59
  56. package/test/context/complete.test.js +5 -1
  57. package/test/context/dereference.test.js +3 -2
  58. package/test/context/expand.test.js +4 -2
  59. package/test/context/normalize.test.js +14 -1
  60. package/test/context/validate.test.js +40 -20
  61. package/test/shortcuts.test.js +2 -1
  62. package/types/component.d.ts +15 -3
  63. package/types/context/declaration.d.ts +1 -1
  64. package/types/context.d.ts +10 -9
  65. package/types/entity.d.ts +7 -0
  66. package/types/index.d.ts +1 -0
  67. package/CHANGELOG.md +0 -195
@@ -19,12 +19,18 @@ beforeEach(() => {
19
19
  describe('runtime', () => {
20
20
  it('should require', () => {
21
21
  delete context.runtime
22
- assert.throws(() => validate(context), (error) => /required/.test(error.message))
22
+ assert.throws(
23
+ () => validate(context),
24
+ (error) => /required/.test(error.message)
25
+ )
23
26
  })
24
27
 
25
28
  it('should require registry to match uri format', () => {
26
29
  context.runtime.registry = 'not-a-uri'
27
- assert.throws(() => validate(context), (error) => /must match format/.test(error.message))
30
+ assert.throws(
31
+ () => validate(context),
32
+ (error) => /must match format/.test(error.message)
33
+ )
28
34
 
29
35
  context.runtime.registry = 'http://localhost'
30
36
  assert.doesNotThrow(() => validate(context))
@@ -32,7 +38,10 @@ describe('runtime', () => {
32
38
 
33
39
  it('should require proxy to match uri format', () => {
34
40
  context.runtime.proxy = 'not-a-uri'
35
- assert.throws(() => validate(context), (error) => /must match format/.test(error.message))
41
+ assert.throws(
42
+ () => validate(context),
43
+ (error) => /must match format/.test(error.message)
44
+ )
36
45
 
37
46
  context.runtime.proxy = 'http://localhost'
38
47
  assert.doesNotThrow(() => validate(context))
@@ -43,25 +52,25 @@ describe('registry', () => {
43
52
  it('should require', () => {
44
53
  delete context.registry
45
54
 
46
- assert.throws(() => validate(context), (error) => /required property 'registry'/.test(error.message))
47
- })
48
-
49
- it('should set default platforms', () => {
50
- delete context.registry.platforms
51
-
52
- validate(context)
53
-
54
- assert.ok(context.registry.platforms instanceof Array)
55
- assert.deepStrictEqual(context.registry.platforms, ['linux/amd64', 'linux/arm/v7', 'linux/arm64'])
55
+ assert.throws(
56
+ () => validate(context),
57
+ (error) => /required property 'registry'/.test(error.message)
58
+ )
56
59
  })
57
60
  })
58
61
 
59
62
  it('should require name as label', () => {
60
63
  delete context.name
61
- assert.throws(() => validate(context), (error) => /required/.test(error.message))
64
+ assert.throws(
65
+ () => validate(context),
66
+ (error) => /required/.test(error.message)
67
+ )
62
68
 
63
69
  context.name = 'foo bar'
64
- assert.throws(() => validate(context), (error) => /pattern/.test(error.message))
70
+ assert.throws(
71
+ () => validate(context),
72
+ (error) => /pattern/.test(error.message)
73
+ )
65
74
 
66
75
  context.name = 'foo-bar'
67
76
  assert.doesNotThrow(() => validate(context))
@@ -69,7 +78,10 @@ it('should require name as label', () => {
69
78
 
70
79
  it('should require registry url', () => {
71
80
  delete context.registry
72
- assert.throws(() => validate(context), (error) => /required/.test(error.message))
81
+ assert.throws(
82
+ () => validate(context),
83
+ (error) => /required/.test(error.message)
84
+ )
73
85
  })
74
86
 
75
87
  it('should allow mono replicas and resources', () => {
@@ -93,16 +105,24 @@ describe('compositions', () => {
93
105
 
94
106
  it('should require services to be a non-empty array of strings', () => {
95
107
  context.compositions[0].services = []
96
- assert.throws(() => validate(context), (error) => /fewer than 1 items/.test(error.message))
108
+ assert.throws(
109
+ () => validate(context),
110
+ (error) => /fewer than 1 items/.test(error.message)
111
+ )
97
112
 
98
113
  context.compositions[0].services = 'exposition'
99
- assert.throws(() => validate(context), (error) => /must be array/.test(error.message))
114
+ assert.throws(
115
+ () => validate(context),
116
+ (error) => /must be array/.test(error.message)
117
+ )
100
118
  })
101
119
 
102
120
  it('should reject an unknown property', () => {
103
121
  context.compositions[0].compoments = ['a.b']
104
122
 
105
- assert.throws(() => validate(context),
106
- (error) => /Property compoments is not expected to be here/.test(error.message))
123
+ assert.throws(
124
+ () => validate(context),
125
+ (error) => /Property compoments is not expected to be here/.test(error.message)
126
+ )
107
127
  })
108
128
  })
@@ -55,7 +55,8 @@ describe('recognize', () => {
55
55
 
56
56
  assert.deepStrictEqual(object[group], known)
57
57
 
58
- for (const alias of Object.keys(fixtures.SHORTCUTS)) assert.strictEqual(object[alias], undefined)
58
+ for (const alias of Object.keys(fixtures.SHORTCUTS))
59
+ assert.strictEqual(object[alias], undefined)
59
60
  })
60
61
 
61
62
  it('should not overwrite group', () => {
@@ -1,4 +1,5 @@
1
- import type { Locator, operations } from '@toa.io/core'
1
+ import type { Locator } from '@toa.io/core'
2
+ import type { operations } from '@toa.io/core/types'
2
3
 
3
4
  type Map = {
4
5
  [id: string]: Component
@@ -8,6 +9,7 @@ export type Operation = {
8
9
  type: operations.type
9
10
  scope?: operations.scope
10
11
  bindings?: string[]
12
+ description?: string
11
13
  input?: any
12
14
  output?: any
13
15
  error?: any
@@ -34,10 +36,20 @@ type Receiver = {
34
36
  source?: string
35
37
  }
36
38
 
37
- type Entity = {
38
- schema: Object
39
+ type Migration = {
40
+ id: string
41
+ steps: unknown[]
42
+ }
43
+
44
+ export type Entity = {
45
+ properties: Record<string, Object>
46
+ required?: string[]
47
+ /** what a record holds before anything is written to it */
48
+ blank?: Record<string, unknown>
39
49
  storage?: string
40
50
  associated?: boolean
51
+ custom?: boolean
52
+ migrations?: Migration[]
41
53
  }
42
54
 
43
55
  type Declaration = {
@@ -2,7 +2,7 @@ import { Registry, Runtime } from '../context.js'
2
2
 
3
3
  /** A composition as declared, before its members are resolved. */
4
4
  interface Composition {
5
- name: string,
5
+ name: string
6
6
  components: string[]
7
7
  services?: string[]
8
8
  }
@@ -2,35 +2,36 @@ import { Manifest } from './component.js'
2
2
  import { Locator } from '@toa.io/core'
3
3
  import type { Declaration } from './context/declaration.js'
4
4
 
5
- interface Runtime{
5
+ interface Runtime {
6
6
  version: string
7
7
  registry?: string
8
8
  proxy?: string
9
9
  }
10
10
 
11
- interface Registry{
11
+ interface Registry {
12
12
  base?: string
13
13
  platforms?: string[] | null
14
+ services?: 'build' | 'published'
14
15
  build?: {
15
16
  arguments?: string[]
16
17
  run?: string
17
- },
18
+ }
18
19
  credentials: string
19
20
  }
20
21
 
21
- interface Composition{
22
- name: string,
22
+ interface Composition {
23
+ name: string
23
24
  components: Manifest[]
24
25
  services?: string[]
25
26
  }
26
27
 
27
- export interface Dependency<T = undefined>{
28
+ export interface Dependency<T = undefined> {
28
29
  locator: Locator
29
- manifest: T,
30
+ manifest: T
30
31
  component: Manifest
31
32
  }
32
33
 
33
- interface Context extends Declaration{
34
+ interface Context extends Declaration {
34
35
  runtime?: Runtime
35
36
  environment?: string
36
37
  registry?: Registry
@@ -39,4 +40,4 @@ interface Context extends Declaration{
39
40
  dependencies?: Record<string, Dependency[]>
40
41
  }
41
42
 
42
- export function context (path: string, environment?: string): Promise<Context>
43
+ export function context(path: string, environment?: string): Promise<Context>
@@ -0,0 +1,7 @@
1
+ import type { Entity } from './component.js'
2
+
3
+ /** The schema a stored record must fit. */
4
+ export function schema(entity: Entity): object
5
+
6
+ /** The schema a changeset must fit: the same properties, none of them required. */
7
+ export function changeset(entity: Entity): object
package/types/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export * as context from './context.js'
2
2
  export * as component from './component.js'
3
3
 
4
4
  export { Manifest } from './component.js'
5
+ export * as entity from './entity.js'
package/CHANGELOG.md DELETED
@@ -1,195 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- # [1.0.0-alpha.283](https://github.com/toa-io/toa/compare/v1.0.0-alpha.282...v1.0.0-alpha.283) (2026-09-04)
7
-
8
- ### Bug Fixes
9
-
10
- * **exposition:** declare the errors the built-in components return ([27200c3](https://github.com/toa-io/toa/commit/27200c348ab09068d58c52c3e83208cb29cd7290))
11
-
12
- ### Features
13
-
14
- * **operations:** run extension services inside a composition ([154aced](https://github.com/toa-io/toa/commit/154aced047228a250ac3bab353e09432fcc5a34d))
15
-
16
-
17
- # [1.0.0-alpha.282](https://github.com/toa-io/toa/compare/v1.0.0-alpha.281...v1.0.0-alpha.282) (2026-09-03)
18
-
19
- **Note:** Version bump only for package @toa.io/norm
20
-
21
-
22
-
23
-
24
-
25
- # [1.0.0-alpha.278](https://github.com/toa-io/toa/compare/v1.0.0-alpha.277...v1.0.0-alpha.278) (2026-09-03)
26
-
27
- **Note:** Version bump only for package @toa.io/norm
28
-
29
-
30
-
31
-
32
-
33
- # [1.0.0-alpha.277](https://github.com/toa-io/toa/compare/v1.0.0-alpha.276...v1.0.0-alpha.277) (2026-09-03)
34
-
35
- ### Bug Fixes
36
-
37
- * read a manifest the way js-yaml read one before ([fcea1b2](https://github.com/toa-io/toa/commit/fcea1b21996d6efa6c3198acbfc9f70f1ce0c35c))
38
- * settle what the module loader made asynchronous ([d4ab9df](https://github.com/toa-io/toa/commit/d4ab9dfb09e382da311dba08f52214e7645f4a6c))
39
- * what the loader resolves, and what it will not ([1b8a02f](https://github.com/toa-io/toa/commit/1b8a02ff0a5e0f18ee62e3f71bc81116f86f9af1))
40
-
41
-
42
- # [1.0.0-alpha.274](https://github.com/toa-io/toa/compare/v1.0.0-alpha.273...v1.0.0-alpha.274) (2026-09-02)
43
-
44
- **Note:** Version bump only for package @toa.io/norm
45
-
46
-
47
-
48
-
49
-
50
- # [1.0.0-alpha.273](https://github.com/toa-io/toa/compare/v1.0.0-alpha.272...v1.0.0-alpha.273) (2026-09-02)
51
-
52
- **Note:** Version bump only for package @toa.io/norm
53
-
54
-
55
-
56
-
57
-
58
- # [1.0.0-alpha.272](https://github.com/toa-io/toa/compare/v1.0.0-alpha.271...v1.0.0-alpha.272) (2026-09-01)
59
-
60
-
61
- ### Bug Fixes
62
-
63
- * **atomicity:** honour a cluster, and let it be unreachable ([c475d66](https://github.com/toa-io/toa/commit/c475d663ddf7329d86da517ccceee4cb03280e95))
64
-
65
-
66
- * feat(atomicity)!: take a quorum of independent servers ([862562f](https://github.com/toa-io/toa/commit/862562f24d77ec00127dbbd88d43802b208c643b))
67
- * refactor(atomicity)!: take one Redis, not a cluster ([b0a4770](https://github.com/toa-io/toa/commit/b0a4770dff5dfe8fee4b9ae637a4dd297454fcd4))
68
- * refactor(atomicity)!: take the lock manager from the stash ([4fd91eb](https://github.com/toa-io/toa/commit/4fd91eb2fdb68f7b87aaf16d182794bbc567ba57))
69
- * refactor(core)!: pump the outbox in one cycle ([bf590fe](https://github.com/toa-io/toa/commit/bf590fe8ed59c701b5fd1a91ba4874685b79242f))
70
- * refactor(atomicity)!: rename the connector and free it of the outbox ([21f1a41](https://github.com/toa-io/toa/commit/21f1a41aceafcfd35c7620014062fe46b54afa95))
71
- * feat(core)!: commit events with the state that produced them ([1eb68cc](https://github.com/toa-io/toa/commit/1eb68cc435dbfa03faa16009fceb866693d22e1a)), closes [#20](https://github.com/toa-io/toa/issues/20)
72
-
73
-
74
- ### Features
75
-
76
- * **atomicity:** make the interval a setting, and stop repeating n-and-i ([5c3a6d1](https://github.com/toa-io/toa/commit/5c3a6d1533751a21b7eb7d9c737f1a270bf5a5ae))
77
-
78
-
79
- ### BREAKING CHANGES
80
-
81
- * `atomicity.redis` accepts a list again, of independent servers rather
82
- than cluster nodes, and refuses an even number of them.
83
-
84
- Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
85
- * `atomicity.redis` is a string. A list is refused at export.
86
-
87
- Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
88
- * `context.stash.lock` is gone; lock through `context.atom` instead.
89
- A stash pointer resolving to several addresses now uses the first.
90
-
91
- Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
92
- * `Storage.outbox.pending` takes a fourth argument, the id to
93
- continue from.
94
-
95
- Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
96
- * `@toa.io/partitions.redis` is now `@toa.io/atomicity`, and its
97
- `lanes(total)` is `slots(total)`. Redis is declared as `atomicity` in the context
98
- rather than `outbox.redis`, and read from `TOA_ATOMICITY_REDIS`.
99
- `TOA_OUTBOX_PARTITION_INTERVAL` is `TOA_ATOMICITY_INTERVAL`.
100
-
101
- Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
102
- * `event.changeset` is removed; use `origin` and `state`. `State`
103
- takes an `Outbox` in place of an `Emission`. `difference` is dropped from
104
- `@toa.io/generic` along with its last caller.
105
-
106
- Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
107
-
108
-
109
-
110
-
111
-
112
- # [1.0.0-alpha.270](https://github.com/toa-io/toa/compare/v1.0.0-alpha.269...v1.0.0-alpha.270) (2026-08-31)
113
-
114
-
115
- ### Bug Fixes
116
-
117
- * **norm:** resolve annotations keyed by a dependency id ([e2860cd](https://github.com/toa-io/toa/commit/e2860cd2cc93dd3a135c092db1d943f80648ee56))
118
- * **norm:** return the hash after hashing a file ([35058bd](https://github.com/toa-io/toa/commit/35058bdaae19f367197ea5b800742349e507ab11))
119
-
120
-
121
- ### Performance Improvements
122
-
123
- * **boot:** stop paying for what a composition does not need to start ([1619c27](https://github.com/toa-io/toa/commit/1619c2743072f4706939ce72f3074e615d26a91e))
124
-
125
-
126
-
127
-
128
-
129
- # [1.0.0-alpha.266](https://github.com/toa-io/toa/compare/v1.0.0-alpha.265...v1.0.0-alpha.266) (2026-08-29)
130
-
131
- **Note:** Version bump only for package @toa.io/norm
132
-
133
-
134
-
135
-
136
-
137
- # [1.0.0-alpha.265](https://github.com/toa-io/toa/compare/v1.0.0-alpha.264...v1.0.0-alpha.265) (2026-08-29)
138
-
139
- **Note:** Version bump only for package @toa.io/norm
140
-
141
-
142
-
143
-
144
-
145
- # [1.0.0-alpha.264](https://github.com/toa-io/toa/compare/v1.0.0-alpha.263...v1.0.0-alpha.264) (2026-08-29)
146
-
147
- **Note:** Version bump only for package @toa.io/norm
148
-
149
-
150
-
151
-
152
-
153
- # [1.0.0-alpha.263](https://github.com/toa-io/toa/compare/v1.0.0-alpha.262...v1.0.0-alpha.263) (2026-08-29)
154
-
155
- **Note:** Version bump only for package @toa.io/norm
156
-
157
-
158
-
159
-
160
-
161
- # [1.0.0-alpha.262](https://github.com/toa-io/toa/compare/v1.0.0-alpha.261...v1.0.0-alpha.262) (2026-08-28)
162
-
163
-
164
- ### Features
165
-
166
- * **introspection:** add the introspection extension ([a07ed86](https://github.com/toa-io/toa/commit/a07ed8651404258d544f3b6d8236e58cf4da09ac))
167
- * **operations:** let a service claim a path prefix on the host ([02677d1](https://github.com/toa-io/toa/commit/02677d17fcd4222b07c3be458d93bcc496a016a8))
168
-
169
-
170
-
171
-
172
-
173
- # [1.0.0-alpha.259](https://github.com/toa-io/toa/compare/v1.0.0-alpha.258...v1.0.0-alpha.259) (2026-08-24)
174
-
175
- **Note:** Version bump only for package @toa.io/norm
176
-
177
-
178
-
179
-
180
-
181
- # [1.0.0-alpha.257](https://github.com/toa-io/toa/compare/v1.0.0-alpha.256...v1.0.0-alpha.257) (2026-08-24)
182
-
183
- **Note:** Version bump only for package @toa.io/norm
184
-
185
-
186
-
187
-
188
-
189
- # [1.0.0-alpha.256](https://github.com/toa-io/toa/compare/v1.0.0-alpha.255...v1.0.0-alpha.256) (2026-08-23)
190
-
191
-
192
- ### Features
193
-
194
- * add toa mono and discover components at components/* ([787a111](https://github.com/toa-io/toa/commit/787a111c8f83e56ebc734801c97106c585d034ea))
195
- * deploy a single mono image with toa deploy --mono ([a7f14f9](https://github.com/toa-io/toa/commit/a7f14f9877a280e9c74d16c195028b35d74fb15a))