@toa.io/norm 1.0.0-alpha.302 → 1.0.0-alpha.303

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 CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-alpha.303](https://github.com/toa-io/toa/compare/v1.0.0-alpha.302...v1.0.0-alpha.303) (2026-09-12)
7
+
8
+ ### Features
9
+
10
+ * **operations:** a composition states how many replicas it runs ([4466f5a](https://github.com/toa-io/toa/commit/4466f5abcc22c6dceb2994a0143b6bf9d5201d9d))
11
+ * **telemetry:** let a component declare its own metrics ([05aa3c7](https://github.com/toa-io/toa/commit/05aa3c72cd6a160e1cef99df6176a7f627075bc0))
12
+
13
+
6
14
  # [1.0.0-alpha.302](https://github.com/toa-io/toa/compare/v1.0.0-alpha.301...v1.0.0-alpha.302) (2026-09-11)
7
15
 
8
16
  **Note:** Version bump only for package @toa.io/norm
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/norm",
3
- "version": "1.0.0-alpha.302",
3
+ "version": "1.0.0-alpha.303",
4
4
  "type": "module",
5
5
  "description": "Toa declarations normalization and validation",
6
6
  "author": "temich <tema.gurtovoy@gmail.com>",
@@ -21,12 +21,12 @@
21
21
  "test": "echo \"Error: run tests from root\" && exit 1"
22
22
  },
23
23
  "dependencies": {
24
- "@toa.io/core": "1.0.0-alpha.302",
25
- "@toa.io/definitions": "1.0.0-alpha.302",
24
+ "@toa.io/core": "1.0.0-alpha.303",
25
+ "@toa.io/definitions": "1.0.0-alpha.303",
26
26
  "@toa.io/generic": "1.0.0-alpha.301",
27
27
  "@toa.io/prototype": "1.0.0-alpha.301",
28
28
  "@toa.io/schemas": "1.0.0-alpha.301",
29
29
  "fast-glob": "3.3.3"
30
30
  },
31
- "gitHead": "3cbbca55a61711db78f98b13e50cf4d1afaf5c08"
31
+ "gitHead": "b8e9dd89f6d49756944687a95dfc786f12947286"
32
32
  }
@@ -13,5 +13,6 @@ const SHORTCUTS = {
13
13
  stash: '@toa.io/extensions.stash',
14
14
  storages: '@toa.io/extensions.storages',
15
15
  cadence: '@toa.io/extensions.cadence',
16
- introspection: '@toa.io/extensions.introspection'
16
+ introspection: '@toa.io/extensions.introspection',
17
+ telemetry: '@toa.io/extensions.telemetry'
17
18
  }
@@ -77,6 +77,9 @@ properties:
77
77
  minLength: 1
78
78
  resources:
79
79
  $ref: '#/definitions/resources'
80
+ replicas:
81
+ type: integer
82
+ minimum: 1
80
83
  required: [name, components]
81
84
  evicted:
82
85
  description: >
@@ -81,15 +81,15 @@ describe('entity', () => {
81
81
 
82
82
  assert.deepStrictEqual(manifest.entity.properties.settled.format, 'date-time')
83
83
  })
84
-
85
-
86
84
  })
87
85
 
88
86
  describe('extensions', () => {
89
87
  it('should add predefined extensions', async () => {
90
88
  await extensions(manifest)
91
89
 
92
- assert.strictEqual(manifest.extensions['@toa.io/extensions.telemetry'], null)
90
+ // telemetry normalizes its declaration, so a component that declares nothing has an
91
+ // empty one rather than none; fetch has no manifest of its own to normalize
92
+ assert.deepStrictEqual(manifest.extensions['@toa.io/extensions.telemetry'], {})
93
93
  assert.strictEqual(manifest.extensions['@toa.io/extensions.fetch'], null)
94
94
  })
95
95
 
@@ -99,7 +99,7 @@ describe('extensions', () => {
99
99
  await extensions(manifest)
100
100
 
101
101
  assert.deepStrictEqual(manifest.extensions, {
102
- '@toa.io/extensions.telemetry': null,
102
+ '@toa.io/extensions.telemetry': {},
103
103
  '@toa.io/extensions.fetch': null,
104
104
  '@toa.io/extensions.introspection': {}
105
105
  })
@@ -125,6 +125,21 @@ describe('compositions', () => {
125
125
  (error) => /must NOT have additional properties/.test(error.message)
126
126
  )
127
127
  })
128
+
129
+ it('should allow replicas of one', () => {
130
+ context.compositions[0].replicas = 1
131
+
132
+ assert.doesNotThrow(() => validate(context))
133
+ })
134
+
135
+ it('should refuse replicas below one', () => {
136
+ context.compositions[0].replicas = 0
137
+
138
+ assert.throws(
139
+ () => validate(context),
140
+ (error) => /must be >= 1/.test(error.message)
141
+ )
142
+ })
128
143
  })
129
144
 
130
145
  describe('evicted', () => {
@@ -5,6 +5,7 @@ interface Composition {
5
5
  name: string
6
6
  components: string[]
7
7
  services?: string[]
8
+ replicas?: number
8
9
  }
9
10
 
10
11
  /** What Toa does not deploy for a context, whatever else names it: still part of it, deployed by other means. */
@@ -23,6 +23,7 @@ interface Composition {
23
23
  name: string
24
24
  components: Manifest[]
25
25
  services?: string[]
26
+ replicas?: number
26
27
  /** what a deploy installs for the services this composition runs, by package name */
27
28
  packages?: Record<string, string>
28
29
  }