@toa.io/extensions.configuration 1.0.0-alpha.313 → 1.0.0-alpha.315

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 (50) hide show
  1. package/components/configuration.values/manifest.toa.yaml +48 -0
  2. package/components/configuration.values/operations/create.js +2 -7
  3. package/components/configuration.values/operations/get.js +4 -2
  4. package/components/configuration.values/operations/lib/errors.js +7 -0
  5. package/components/configuration.values/operations/lib/resolve.js +10 -7
  6. package/components/configuration.values/operations/list.js +9 -2
  7. package/components/configuration.values/operations/reset.js +20 -0
  8. package/components/configuration.values/source/create.ts +1 -8
  9. package/components/configuration.values/source/get.ts +5 -1
  10. package/components/configuration.values/source/lib/errors.ts +7 -0
  11. package/components/configuration.values/source/lib/resolve.ts +12 -8
  12. package/components/configuration.values/source/list.ts +10 -1
  13. package/components/configuration.values/source/reset.ts +38 -0
  14. package/components/configuration.values/types/toa.d.ts +24 -11
  15. package/package.json +4 -4
  16. package/readme.md +37 -22
  17. package/transpiled/Aspect.d.ts +1 -0
  18. package/transpiled/Aspect.js +14 -3
  19. package/transpiled/Aspect.js.map +1 -1
  20. package/transpiled/Client.d.ts +12 -2
  21. package/transpiled/Client.js +10 -4
  22. package/transpiled/Client.js.map +1 -1
  23. package/ui/dist/_app/immutable/assets/0.p4KMpIb5.css +2 -0
  24. package/ui/dist/_app/immutable/chunks/B8XvKDKK.js +3 -0
  25. package/ui/dist/_app/immutable/chunks/BbdD8cVe.js +6 -0
  26. package/ui/dist/_app/immutable/chunks/BgP_g0Vq.js +50 -0
  27. package/ui/dist/_app/immutable/chunks/BgeYVt0O.js +8 -0
  28. package/ui/dist/_app/immutable/chunks/{D2Pwjbel.js → CrAWEwWl.js} +1 -1
  29. package/ui/dist/_app/immutable/chunks/{N2QYE_-W.js → XpAw51oI.js} +1 -1
  30. package/ui/dist/_app/immutable/entry/app.s9-wduk8.js +2 -0
  31. package/ui/dist/_app/immutable/entry/start.Zw5Ep8Bk.js +1 -0
  32. package/ui/dist/_app/immutable/nodes/0.DkkBq6e1.js +1 -0
  33. package/ui/dist/_app/immutable/nodes/1.CVG_C4Sr.js +1 -0
  34. package/ui/dist/_app/immutable/nodes/2.DzcEonXR.js +1 -0
  35. package/ui/dist/_app/immutable/nodes/3.B8P9lr3Z.js +1 -0
  36. package/ui/dist/_app/immutable/nodes/4.DPXR3wrG.js +1 -0
  37. package/ui/dist/_app/version.json +1 -1
  38. package/ui/dist/index.html +12 -12
  39. package/ui/dist/_app/immutable/assets/0.BGFdDICE.css +0 -2
  40. package/ui/dist/_app/immutable/chunks/BChBfZBo.js +0 -6
  41. package/ui/dist/_app/immutable/chunks/CcD_ISxM.js +0 -3
  42. package/ui/dist/_app/immutable/chunks/DucMSfU2.js +0 -8
  43. package/ui/dist/_app/immutable/chunks/tCi1Fi-q.js +0 -50
  44. package/ui/dist/_app/immutable/entry/app.BTseTozs.js +0 -2
  45. package/ui/dist/_app/immutable/entry/start.Te91gu4w.js +0 -1
  46. package/ui/dist/_app/immutable/nodes/0.BFLPfNMb.js +0 -1
  47. package/ui/dist/_app/immutable/nodes/1.BwA1zIJg.js +0 -1
  48. package/ui/dist/_app/immutable/nodes/2.BG0rgRs0.js +0 -1
  49. package/ui/dist/_app/immutable/nodes/3.kqEdbNlI.js +0 -1
  50. package/ui/dist/_app/immutable/nodes/4.1G5zNhKM.js +0 -1
@@ -6,6 +6,10 @@ name: values
6
6
  # What every component's epoch, schema and defaults are, the service is told on
7
7
  # deployment (`TOA_CONFIGURATION_VALUES`). A created object is published as the
8
8
  # prototype's `created` event.
9
+ #
10
+ # A reset is an object too: it holds the deployed defaults and their revision, so the event
11
+ # carries the value a running component is to take. While it is the latest, what is served is
12
+ # the defaults deployed now, which a later deployment may have changed.
9
13
 
10
14
  entity:
11
15
  properties:
@@ -17,6 +21,10 @@ entity:
17
21
  type: object
18
22
  originator:
19
23
  type: string
24
+ # of the defaults a reset holds; a created object has none
25
+ revision:
26
+ type: string
27
+ nullable: true
20
28
  required:
21
29
  - component
22
30
  - epoch
@@ -47,9 +55,17 @@ operations:
47
55
  type: object
48
56
  epoch:
49
57
  type: string
58
+ created:
59
+ type: integer
60
+ # of the deployed defaults when they are what is served; null for a created object
61
+ revision:
62
+ type: string
63
+ nullable: true
50
64
  required:
51
65
  - configuration
52
66
  - epoch
67
+ - created
68
+ - revision
53
69
  # the same for many at once, as the aspects of one process ask
54
70
  fetch:
55
71
  query: false
@@ -104,11 +120,18 @@ operations:
104
120
  type: object
105
121
  configuration:
106
122
  type: object
123
+ created:
124
+ type: integer
125
+ revision:
126
+ type: string
127
+ nullable: true
107
128
  required:
108
129
  - component
109
130
  - epoch
110
131
  - schema
111
132
  - configuration
133
+ - created
134
+ - revision
112
135
  create:
113
136
  query: false
114
137
  input:
@@ -132,6 +155,26 @@ operations:
132
155
  errors:
133
156
  - UNKNOWN_COMPONENT
134
157
  - INVALID_CONFIGURATION
158
+ # back to the deployed defaults, as a new object like any created one
159
+ reset:
160
+ query: false
161
+ input:
162
+ type: object
163
+ properties:
164
+ component:
165
+ type: string
166
+ originator:
167
+ type: object
168
+ properties:
169
+ id:
170
+ type: string
171
+ required:
172
+ - id
173
+ required:
174
+ - component
175
+ - originator
176
+ errors:
177
+ - UNKNOWN_COMPONENT
135
178
 
136
179
  exposition:
137
180
  isolated: true
@@ -153,3 +196,8 @@ exposition:
153
196
  role: system:configuration:create
154
197
  delegate: originator
155
198
  endpoint: create
199
+ DELETE:
200
+ auth:rule:
201
+ role: system:configuration:create
202
+ delegate: originator
203
+ endpoint: reset
@@ -1,6 +1,7 @@
1
1
  import * as schemas from '@toa.io/schemas';
2
2
  import { assertSecrets } from '@toa.io/definitions/extensions.configuration';
3
3
  import { entry } from './lib/map.js';
4
+ import { UnknownComponentError } from './lib/errors.js';
4
5
  export async function transition(input, object) {
5
6
  const known = entry(input.component);
6
7
  if (known === undefined)
@@ -20,13 +21,7 @@ export async function transition(input, object) {
20
21
  object.originator = input.originator.id;
21
22
  return object;
22
23
  }
23
- class UnknownComponentError extends Error {
24
- code = 'UNKNOWN_COMPONENT';
25
- constructor(component) {
26
- super(`Component '${component}' is not configured`);
27
- }
28
- }
29
24
  class InvalidConfigurationError extends Error {
30
25
  code = 'INVALID_CONFIGURATION';
31
26
  }
32
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc291cmNlL2NyZWF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGlCQUFpQixDQUFBO0FBQzFDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSw4Q0FBOEMsQ0FBQTtBQUM1RSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBR3BDLE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUFDLEtBQVksRUFBRSxNQUFjO0lBQzNELE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFcEMsSUFBSSxLQUFLLEtBQUssU0FBUztRQUFFLE9BQU8sSUFBSSxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFMUUsTUFBTSxhQUFhLEdBQUcsZUFBZSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQTtJQUUxRCxNQUFNLE1BQU0sR0FBZ0IsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUE7SUFFeEQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsQ0FBQTtRQUM5QixhQUFhLENBQUMsS0FBSyxDQUFDLE1BQU0sRUFBRSxhQUFhLENBQUMsQ0FBQTtJQUM1QyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sSUFBSSx5QkFBeUIsQ0FBRSxLQUFlLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDaEUsQ0FBQztJQUVELE1BQU0sQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQTtJQUNsQyxNQUFNLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUE7SUFDMUIsTUFBTSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUE7SUFDcEMsTUFBTSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQTtJQUV2QyxPQUFPLE1BQU0sQ0FBQTtBQUNmLENBQUM7QUFFRCxNQUFNLHFCQUFzQixTQUFRLEtBQUs7SUFDdkIsSUFBSSxHQUFHLG1CQUFtQixDQUFBO0lBRTFDLFlBQW1CLFNBQWlCO1FBQ2xDLEtBQUssQ0FBQyxjQUFjLFNBQVMscUJBQXFCLENBQUMsQ0FBQTtJQUNyRCxDQUFDO0NBQ0Y7QUFFRCxNQUFNLHlCQUEwQixTQUFRLEtBQUs7SUFDM0IsSUFBSSxHQUFHLHVCQUF1QixDQUFBO0NBQy9DIn0=
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc291cmNlL2NyZWF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGlCQUFpQixDQUFBO0FBQzFDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSw4Q0FBOEMsQ0FBQTtBQUM1RSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBR3ZELE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUFDLEtBQVksRUFBRSxNQUFjO0lBQzNELE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFcEMsSUFBSSxLQUFLLEtBQUssU0FBUztRQUFFLE9BQU8sSUFBSSxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFMUUsTUFBTSxhQUFhLEdBQUcsZUFBZSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQTtJQUUxRCxNQUFNLE1BQU0sR0FBZ0IsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUE7SUFFeEQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsQ0FBQTtRQUM5QixhQUFhLENBQUMsS0FBSyxDQUFDLE1BQU0sRUFBRSxhQUFhLENBQUMsQ0FBQTtJQUM1QyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sSUFBSSx5QkFBeUIsQ0FBRSxLQUFlLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDaEUsQ0FBQztJQUVELE1BQU0sQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQTtJQUNsQyxNQUFNLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUE7SUFDMUIsTUFBTSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUE7SUFDcEMsTUFBTSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQTtJQUV2QyxPQUFPLE1BQU0sQ0FBQTtBQUNmLENBQUM7QUFFRCxNQUFNLHlCQUEwQixTQUFRLEtBQUs7SUFDM0IsSUFBSSxHQUFHLHVCQUF1QixDQUFBO0NBQy9DIn0=
@@ -13,7 +13,9 @@ export async function computation(input, context) {
13
13
  return {
14
14
  configuration: value.configuration,
15
15
  schema: known?.schema,
16
- epoch: input.epoch ?? known.epoch
16
+ epoch: input.epoch ?? known.epoch,
17
+ created: value.created,
18
+ revision: value.revision
17
19
  };
18
20
  }
19
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc291cmNlL2dldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxPQUFPLEVBQWdCLE1BQU0sa0JBQWtCLENBQUE7QUFFeEQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsV0FBVyxDQUFDLEtBQVksRUFBRSxPQUFnQjtJQUM5RCxNQUFNLEtBQUssR0FBRyxNQUFNLE9BQU8sQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUE7SUFFbEUsSUFBSSxLQUFLLEtBQUssSUFBSTtRQUFFLE9BQU8sSUFBSSxDQUFBO0lBRS9CLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFcEMsT0FBTztRQUNMLGFBQWEsRUFBRSxLQUFLLENBQUMsYUFBYTtRQUNsQyxNQUFNLEVBQUUsS0FBSyxFQUFFLE1BQU07UUFDckIsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLLElBQUksS0FBTSxDQUFDLEtBQUs7S0FDbkMsQ0FBQTtBQUNILENBQUMifQ==
21
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc291cmNlL2dldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxPQUFPLEVBQWdCLE1BQU0sa0JBQWtCLENBQUE7QUFFeEQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsV0FBVyxDQUFDLEtBQVksRUFBRSxPQUFnQjtJQUM5RCxNQUFNLEtBQUssR0FBRyxNQUFNLE9BQU8sQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUE7SUFFbEUsSUFBSSxLQUFLLEtBQUssSUFBSTtRQUFFLE9BQU8sSUFBSSxDQUFBO0lBRS9CLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFcEMsT0FBTztRQUNMLGFBQWEsRUFBRSxLQUFLLENBQUMsYUFBYTtRQUNsQyxNQUFNLEVBQUUsS0FBSyxFQUFFLE1BQU07UUFDckIsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLLElBQUksS0FBTSxDQUFDLEtBQUs7UUFDbEMsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPO1FBQ3RCLFFBQVEsRUFBRSxLQUFLLENBQUMsUUFBUTtLQUN6QixDQUFBO0FBQ0gsQ0FBQyJ9
@@ -0,0 +1,7 @@
1
+ export class UnknownComponentError extends Error {
2
+ code = 'UNKNOWN_COMPONENT';
3
+ constructor(component) {
4
+ super(`Component '${component}' is not configured`);
5
+ }
6
+ }
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL2xpYi9lcnJvcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxPQUFPLHFCQUFzQixTQUFRLEtBQUs7SUFDOUIsSUFBSSxHQUFHLG1CQUFtQixDQUFBO0lBRTFDLFlBQW1CLFNBQWlCO1FBQ2xDLEtBQUssQ0FBQyxjQUFjLFNBQVMscUJBQXFCLENBQUMsQ0FBQTtJQUNyRCxDQUFDO0NBQ0YifQ==
@@ -2,7 +2,8 @@ import { revision } from '@toa.io/definitions/extensions.configuration';
2
2
  import { entry } from './map.js';
3
3
  /**
4
4
  * The latest configuration created for the component and the epoch; the deployed
5
- * defaults when none was; `null` when the epoch is not the one deployed.
5
+ * defaults when none was, or when the latest is a reset; `null` when the epoch is not the one
6
+ * deployed.
6
7
  */
7
8
  export async function resolve(context, component, epoch) {
8
9
  const known = entry(component);
@@ -16,19 +17,21 @@ export async function resolve(context, component, epoch) {
16
17
  limit: 1
17
18
  };
18
19
  const objects = await context.local.enumerate({ query });
19
- if (objects.length > 0)
20
+ const latest = objects[0];
21
+ if (latest !== undefined && (latest.revision ?? null) === null)
20
22
  return {
21
- configuration: objects[0].configuration,
22
- created: objects[0].CREATED,
23
+ configuration: latest.configuration,
24
+ created: latest.CREATED,
23
25
  revision: null
24
26
  };
25
- // what a component was deployed with tells this deployment's defaults from another's
27
+ // a reset stored the defaults of its deployment; those of this one are what it means
26
28
  if (known !== undefined && known.epoch === epoch)
27
29
  return {
28
30
  configuration: known.defaults ?? {},
29
- created: 0,
31
+ created: latest === undefined ? 0 : latest.CREATED,
32
+ // what a component was deployed with tells this deployment's defaults from another's
30
33
  revision: revision(known.defaults)
31
34
  };
32
35
  return null;
33
36
  }
34
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzb2x2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9saWIvcmVzb2x2ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sOENBQThDLENBQUE7QUFDdkUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUVoQzs7O0dBR0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLE9BQU8sQ0FDM0IsT0FBZ0IsRUFDaEIsU0FBaUIsRUFDakIsS0FBYztJQUVkLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQTtJQUU5QixLQUFLLEtBQUssS0FBSyxFQUFFLEtBQUssQ0FBQTtJQUV0QixJQUFJLEtBQUssS0FBSyxTQUFTO1FBQUUsT0FBTyxJQUFJLENBQUE7SUFFcEMsd0ZBQXdGO0lBQ3hGLE1BQU0sS0FBSyxHQUFVO1FBQ25CLFFBQVEsRUFBRSxlQUFlLFNBQVMsYUFBYSxLQUFLLEdBQUc7UUFDdkQsSUFBSSxFQUFFLENBQUMsY0FBYyxDQUFDO1FBQ3RCLEtBQUssRUFBRSxDQUFDO0tBQ1QsQ0FBQTtJQUVELE1BQU0sT0FBTyxHQUFHLE1BQU0sT0FBTyxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFBO0lBRXhELElBQUksT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDO1FBQ3BCLE9BQU87WUFDTCxhQUFhLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLGFBQWE7WUFDdkMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPO1lBQzNCLFFBQVEsRUFBRSxJQUFJO1NBQ2YsQ0FBQTtJQUVILHFGQUFxRjtJQUNyRixJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxDQUFDLEtBQUssS0FBSyxLQUFLO1FBQzlDLE9BQU87WUFDTCxhQUFhLEVBQUUsS0FBSyxDQUFDLFFBQVEsSUFBSSxFQUFFO1lBQ25DLE9BQU8sRUFBRSxDQUFDO1lBQ1YsUUFBUSxFQUFFLFFBQVEsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDO1NBQ25DLENBQUE7SUFFSCxPQUFPLElBQUksQ0FBQTtBQUNiLENBQUMifQ==
37
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzb2x2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9saWIvcmVzb2x2ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sOENBQThDLENBQUE7QUFDdkUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUVoQzs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxPQUFPLENBQzNCLE9BQWdCLEVBQ2hCLFNBQWlCLEVBQ2pCLEtBQWM7SUFFZCxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFOUIsS0FBSyxLQUFLLEtBQUssRUFBRSxLQUFLLENBQUE7SUFFdEIsSUFBSSxLQUFLLEtBQUssU0FBUztRQUFFLE9BQU8sSUFBSSxDQUFBO0lBRXBDLHdGQUF3RjtJQUN4RixNQUFNLEtBQUssR0FBVTtRQUNuQixRQUFRLEVBQUUsZUFBZSxTQUFTLGFBQWEsS0FBSyxHQUFHO1FBQ3ZELElBQUksRUFBRSxDQUFDLGNBQWMsQ0FBQztRQUN0QixLQUFLLEVBQUUsQ0FBQztLQUNULENBQUE7SUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLE9BQU8sQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQTtJQUN4RCxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFFekIsSUFBSSxNQUFNLEtBQUssU0FBUyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsS0FBSyxJQUFJO1FBQzVELE9BQU87WUFDTCxhQUFhLEVBQUUsTUFBTSxDQUFDLGFBQWE7WUFDbkMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPO1lBQ3ZCLFFBQVEsRUFBRSxJQUFJO1NBQ2YsQ0FBQTtJQUVILHFGQUFxRjtJQUNyRixJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxDQUFDLEtBQUssS0FBSyxLQUFLO1FBQzlDLE9BQU87WUFDTCxhQUFhLEVBQUUsS0FBSyxDQUFDLFFBQVEsSUFBSSxFQUFFO1lBQ25DLE9BQU8sRUFBRSxNQUFNLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPO1lBQ2xELHFGQUFxRjtZQUNyRixRQUFRLEVBQUUsUUFBUSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUM7U0FDbkMsQ0FBQTtJQUVILE9BQU8sSUFBSSxDQUFBO0FBQ2IsQ0FBQyJ9
@@ -5,7 +5,14 @@ export async function computation(_, context) {
5
5
  return await Promise.all(components().map(async (component) => {
6
6
  const { epoch, schema } = entry(component);
7
7
  const value = await resolve(context, component, epoch);
8
- return { component, epoch, schema, configuration: value.configuration };
8
+ return {
9
+ component,
10
+ epoch,
11
+ schema,
12
+ configuration: value.configuration,
13
+ created: value.created,
14
+ revision: value.revision
15
+ };
9
16
  }));
10
17
  }
11
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NvdXJjZS9saXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQ2hELE9BQU8sRUFBRSxPQUFPLEVBQWdCLE1BQU0sa0JBQWtCLENBQUE7QUFFeEQsc0ZBQXNGO0FBQ3RGLE1BQU0sQ0FBQyxLQUFLLFVBQVUsV0FBVyxDQUFDLENBQU8sRUFBRSxPQUFnQjtJQUN6RCxPQUFPLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FDdEIsVUFBVSxFQUFFLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxTQUFTLEVBQUUsRUFBRTtRQUNuQyxNQUFNLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLEtBQUssQ0FBQyxTQUFTLENBQUUsQ0FBQTtRQUMzQyxNQUFNLEtBQUssR0FBRyxNQUFNLE9BQU8sQ0FBQyxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssQ0FBQyxDQUFBO1FBRXRELE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxhQUFhLEVBQUUsS0FBTSxDQUFDLGFBQWEsRUFBRSxDQUFBO0lBQzFFLENBQUMsQ0FBQyxDQUNILENBQUE7QUFDSCxDQUFDIn0=
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NvdXJjZS9saXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQ2hELE9BQU8sRUFBRSxPQUFPLEVBQWdCLE1BQU0sa0JBQWtCLENBQUE7QUFFeEQsc0ZBQXNGO0FBQ3RGLE1BQU0sQ0FBQyxLQUFLLFVBQVUsV0FBVyxDQUFDLENBQU8sRUFBRSxPQUFnQjtJQUN6RCxPQUFPLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FDdEIsVUFBVSxFQUFFLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxTQUFTLEVBQUUsRUFBRTtRQUNuQyxNQUFNLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLEtBQUssQ0FBQyxTQUFTLENBQUUsQ0FBQTtRQUMzQyxNQUFNLEtBQUssR0FBRyxNQUFNLE9BQU8sQ0FBQyxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssQ0FBQyxDQUFBO1FBRXRELE9BQU87WUFDTCxTQUFTO1lBQ1QsS0FBSztZQUNMLE1BQU07WUFDTixhQUFhLEVBQUUsS0FBTSxDQUFDLGFBQWE7WUFDbkMsT0FBTyxFQUFFLEtBQU0sQ0FBQyxPQUFPO1lBQ3ZCLFFBQVEsRUFBRSxLQUFNLENBQUMsUUFBUTtTQUMxQixDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQ0gsQ0FBQTtBQUNILENBQUMifQ==
@@ -0,0 +1,20 @@
1
+ import { revision } from '@toa.io/definitions/extensions.configuration';
2
+ import { entry } from './lib/map.js';
3
+ import { UnknownComponentError } from './lib/errors.js';
4
+ /**
5
+ * The deployed defaults, as a new object. The revision tells it from a created one: it is
6
+ * what a component checks the defaults against, and what makes `resolve` serve the defaults
7
+ * deployed now rather than those stored.
8
+ */
9
+ export async function transition(input, object) {
10
+ const known = entry(input.component);
11
+ if (known === undefined)
12
+ return new UnknownComponentError(input.component);
13
+ object.component = input.component;
14
+ object.epoch = known.epoch;
15
+ object.configuration = structuredClone(known.defaults ?? {});
16
+ object.revision = revision(known.defaults);
17
+ object.originator = input.originator.id;
18
+ return object;
19
+ }
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2UvcmVzZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDhDQUE4QyxDQUFBO0FBQ3ZFLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxjQUFjLENBQUE7QUFDcEMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFFdkQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUFDLEtBQVksRUFBRSxNQUFjO0lBQzNELE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFcEMsSUFBSSxLQUFLLEtBQUssU0FBUztRQUFFLE9BQU8sSUFBSSxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUE7SUFFMUUsTUFBTSxDQUFDLFNBQVMsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFBO0lBQ2xDLE1BQU0sQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQTtJQUMxQixNQUFNLENBQUMsYUFBYSxHQUFHLGVBQWUsQ0FBQyxLQUFLLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0lBQzVELE1BQU0sQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUMxQyxNQUFNLENBQUMsVUFBVSxHQUFHLEtBQUssQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFBO0lBRXZDLE9BQU8sTUFBTSxDQUFBO0FBQ2YsQ0FBQyJ9
@@ -1,6 +1,7 @@
1
1
  import * as schemas from '@toa.io/schemas'
2
2
  import { assertSecrets } from '@toa.io/definitions/extensions.configuration'
3
3
  import { entry } from './lib/map.ts'
4
+ import { UnknownComponentError } from './lib/errors.ts'
4
5
  import type { Schema } from '@toa.io/schemas'
5
6
 
6
7
  export async function transition(input: Input, object: Entity): Promise<Entity | Error> {
@@ -27,14 +28,6 @@ export async function transition(input: Input, object: Entity): Promise<Entity |
27
28
  return object
28
29
  }
29
30
 
30
- class UnknownComponentError extends Error {
31
- public readonly code = 'UNKNOWN_COMPONENT'
32
-
33
- public constructor(component: string) {
34
- super(`Component '${component}' is not configured`)
35
- }
36
- }
37
-
38
31
  class InvalidConfigurationError extends Error {
39
32
  public readonly code = 'INVALID_CONFIGURATION'
40
33
  }
@@ -16,7 +16,9 @@ export async function computation(input: Input, context: Context): Promise<Item
16
16
  return {
17
17
  configuration: value.configuration,
18
18
  schema: known?.schema,
19
- epoch: input.epoch ?? known!.epoch
19
+ epoch: input.epoch ?? known!.epoch,
20
+ created: value.created,
21
+ revision: value.revision
20
22
  }
21
23
  }
22
24
 
@@ -29,4 +31,6 @@ interface Item {
29
31
  configuration: object
30
32
  schema?: object
31
33
  epoch: string
34
+ created: number
35
+ revision: string | null
32
36
  }
@@ -0,0 +1,7 @@
1
+ export class UnknownComponentError extends Error {
2
+ public readonly code = 'UNKNOWN_COMPONENT'
3
+
4
+ public constructor(component: string) {
5
+ super(`Component '${component}' is not configured`)
6
+ }
7
+ }
@@ -3,7 +3,8 @@ import { entry } from './map.ts'
3
3
 
4
4
  /**
5
5
  * The latest configuration created for the component and the epoch; the deployed
6
- * defaults when none was; `null` when the epoch is not the one deployed.
6
+ * defaults when none was, or when the latest is a reset; `null` when the epoch is not the one
7
+ * deployed.
7
8
  */
8
9
  export async function resolve(
9
10
  context: Context,
@@ -24,19 +25,21 @@ export async function resolve(
24
25
  }
25
26
 
26
27
  const objects = await context.local.enumerate({ query })
28
+ const latest = objects[0]
27
29
 
28
- if (objects.length > 0)
30
+ if (latest !== undefined && (latest.revision ?? null) === null)
29
31
  return {
30
- configuration: objects[0].configuration,
31
- created: objects[0].CREATED,
32
+ configuration: latest.configuration,
33
+ created: latest.CREATED,
32
34
  revision: null
33
35
  }
34
36
 
35
- // what a component was deployed with tells this deployment's defaults from another's
37
+ // a reset stored the defaults of its deployment; those of this one are what it means
36
38
  if (known !== undefined && known.epoch === epoch)
37
39
  return {
38
40
  configuration: known.defaults ?? {},
39
- created: 0,
41
+ created: latest === undefined ? 0 : latest.CREATED,
42
+ // what a component was deployed with tells this deployment's defaults from another's
40
43
  revision: revision(known.defaults)
41
44
  }
42
45
 
@@ -44,8 +47,8 @@ export async function resolve(
44
47
  }
45
48
 
46
49
  /**
47
- * A configuration and when it was created; `0` for the deployed defaults, which alone have a
48
- * revision.
50
+ * A configuration and when it was created: `0` for the deployed defaults, unless a reset
51
+ * brought them back. The defaults alone have a revision.
49
52
  */
50
53
  export interface Value {
51
54
  configuration: object
@@ -67,5 +70,6 @@ interface Query {
67
70
 
68
71
  interface Stored {
69
72
  configuration: object
73
+ revision?: string | null
70
74
  CREATED: number
71
75
  }
@@ -8,7 +8,14 @@ export async function computation(_: null, context: Context): Promise<Item[]> {
8
8
  const { epoch, schema } = entry(component)!
9
9
  const value = await resolve(context, component, epoch)
10
10
 
11
- return { component, epoch, schema, configuration: value!.configuration }
11
+ return {
12
+ component,
13
+ epoch,
14
+ schema,
15
+ configuration: value!.configuration,
16
+ created: value!.created,
17
+ revision: value!.revision
18
+ }
12
19
  })
13
20
  )
14
21
  }
@@ -18,4 +25,6 @@ interface Item {
18
25
  epoch: string
19
26
  schema: object
20
27
  configuration: object
28
+ created: number
29
+ revision: string | null
21
30
  }
@@ -0,0 +1,38 @@
1
+ import { revision } from '@toa.io/definitions/extensions.configuration'
2
+ import { entry } from './lib/map.ts'
3
+ import { UnknownComponentError } from './lib/errors.ts'
4
+
5
+ /**
6
+ * The deployed defaults, as a new object. The revision tells it from a created one: it is
7
+ * what a component checks the defaults against, and what makes `resolve` serve the defaults
8
+ * deployed now rather than those stored.
9
+ */
10
+ export async function transition(input: Input, object: Entity): Promise<Entity | Error> {
11
+ const known = entry(input.component)
12
+
13
+ if (known === undefined) return new UnknownComponentError(input.component)
14
+
15
+ object.component = input.component
16
+ object.epoch = known.epoch
17
+ object.configuration = structuredClone(known.defaults ?? {})
18
+ object.revision = revision(known.defaults)
19
+ object.originator = input.originator.id
20
+
21
+ return object
22
+ }
23
+
24
+ interface Input {
25
+ component: string
26
+ originator: {
27
+ id: string
28
+ }
29
+ }
30
+
31
+ interface Entity {
32
+ id: string
33
+ component: string
34
+ epoch: string
35
+ configuration: object
36
+ revision: string
37
+ originator: string
38
+ }
@@ -1,7 +1,7 @@
1
1
  // Written by `toa types`. Every run rewrites it.
2
2
  // What a manifest does not state belongs in a file of your own.
3
3
 
4
- import type { Options, Query, RemoteError } from '@toa.io/core/types'
4
+ import type { Query, RemoteError } from '@toa.io/core/types'
5
5
  import type { Readable } from 'node:stream'
6
6
 
7
7
  export interface Entity {
@@ -9,6 +9,7 @@ export interface Entity {
9
9
  epoch: string
10
10
  configuration: Record<string, unknown>
11
11
  originator: string
12
+ revision?: string | null
12
13
  id: string
13
14
  VERSION: number
14
15
  CREATED: number
@@ -26,6 +27,8 @@ export type GetOutput = {
26
27
  configuration: Record<string, unknown>
27
28
  schema?: Record<string, unknown>
28
29
  epoch: string
30
+ created: number
31
+ revision: string | null
29
32
  } | null
30
33
 
31
34
  export type FetchInput = Array<{
@@ -46,6 +49,8 @@ export type ListOutput = Array<{
46
49
  epoch: string
47
50
  schema: Record<string, unknown>
48
51
  configuration: Record<string, unknown>
52
+ created: number
53
+ revision: string | null
49
54
  }>
50
55
 
51
56
  export type CreateInput = {
@@ -56,15 +61,23 @@ export type CreateInput = {
56
61
  }
57
62
  }
58
63
 
64
+ export type ResetInput = {
65
+ component: string
66
+ originator: {
67
+ id: string
68
+ }
69
+ }
70
+
59
71
  export interface Component {
60
- get: (request: { input: GetInput, task?: boolean }, options?: Options) => Promise<GetOutput>
61
- fetch: (request: { input: FetchInput, task?: boolean }, options?: Options) => Promise<FetchOutput>
62
- list: (request: { input?: null, task?: boolean }, options?: Options) => Promise<ListOutput>
63
- create: (request: { input: CreateInput, task?: boolean }, options?: Options) => Promise<unknown | RemoteError<"UNKNOWN_COMPONENT" | "INVALID_CONFIGURATION">>
64
- assign: (request: { input?: null, query?: Query<Entity>, task?: boolean }, options?: Options) => Promise<Entity>
65
- ensure: (request: { input?: null, query?: Query<Entity>, task?: boolean }, options?: Options) => Promise<Entity>
66
- enumerate: (request: { input?: null, query?: Query<Entity>, task?: boolean }, options?: Options) => Promise<Entity[]>
67
- observe: (request: { input?: null, query?: Query<Entity>, task?: boolean }, options?: Options) => Promise<Entity | null>
68
- stream: (request: { input?: null, query?: Query<Entity>, task?: boolean }, options?: Options) => Promise<Readable>
69
- terminate: (request: { input?: null, query?: Query<Entity>, task?: boolean }, options?: Options) => Promise<Entity>
72
+ get: (request: { input: GetInput, task?: boolean }) => Promise<GetOutput>
73
+ fetch: (request: { input: FetchInput, task?: boolean }) => Promise<FetchOutput>
74
+ list: (request: { input?: null, task?: boolean }) => Promise<ListOutput>
75
+ create: (request: { input: CreateInput, task?: boolean }) => Promise<unknown | RemoteError<"UNKNOWN_COMPONENT" | "INVALID_CONFIGURATION">>
76
+ reset: (request: { input: ResetInput, task?: boolean }) => Promise<unknown | RemoteError<"UNKNOWN_COMPONENT">>
77
+ assign: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity>
78
+ ensure: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity>
79
+ enumerate: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity[]>
80
+ observe: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity | null>
81
+ stream: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Readable>
82
+ terminate: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity>
70
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.configuration",
3
- "version": "1.0.0-alpha.313",
3
+ "version": "1.0.0-alpha.315",
4
4
  "type": "module",
5
5
  "description": "Toa Configuration",
6
6
  "author": "temich <tema.gurtovoy@gmail.com>",
@@ -32,10 +32,10 @@
32
32
  "prepack": "npm run build:ui",
33
33
  "features": "TSX_TSCONFIG_PATH=features/steps/tsconfig.json NODE_OPTIONS=--import=tsx cucumber-js"
34
34
  },
35
- "gitHead": "7da3b2627ceeefd1fa19d5c8851be88d928523b6",
35
+ "gitHead": "d88ed286bf44bf95b5f3d9db6e7d4621261dbb99",
36
36
  "dependencies": {
37
- "@toa.io/core": "1.0.0-alpha.312",
38
- "@toa.io/definitions": "1.0.0-alpha.313",
37
+ "@toa.io/core": "1.0.0-alpha.315",
38
+ "@toa.io/definitions": "1.0.0-alpha.315",
39
39
  "@toa.io/generic": "1.0.0-alpha.311",
40
40
  "@toa.io/schemas": "1.0.0-alpha.311",
41
41
  "openspan": "1.0.0-alpha.305"
package/readme.md CHANGED
@@ -233,50 +233,64 @@ created one. Each object records its `originator`.
233
233
 
234
234
  The configuration of a component for an epoch is:
235
235
 
236
- 1. The latest object created for the component and the epoch;
236
+ 1. The latest object created for the component and the epoch, unless it is a reset;
237
237
  2. Otherwise, the deployed defaults, if the epoch is the deployed one;
238
238
  3. Otherwise, none.
239
239
 
240
+ A reset brings a component back to its deployed defaults and keeps it following them: a later
241
+ deployment that changes the defaults changes what is served after a reset.
242
+
240
243
  ### Operations
241
244
 
242
245
  - `get({ component, epoch? })`: the configuration with the schema it is checked against, as
243
- `{ configuration, schema, epoch }`, or `null` when there is none. The epoch is the deployed
244
- one when omitted; an epoch the deployment does not know has no `schema`.
246
+ `{ configuration, schema, epoch, created, revision }`, or `null` when there is none. The
247
+ epoch is the deployed one when omitted; an epoch the deployment does not know has no
248
+ `schema`. `created` is `0` for the deployed defaults, unless a reset brought them back.
249
+ `revision` is that of the deployed defaults when they are what is served, and `null` for a
250
+ created object.
245
251
  - `fetch([{ component, epoch }])`: the same for several pairs at once, as
246
- `[{ component, epoch, configuration, created, revision }]`. `revision` is that of the deployed
247
- defaults when they are what is served, and `null` for a created object.
252
+ `[{ component, epoch, configuration, created, revision }]`.
248
253
  - `list()`: every component's configuration for its deployed epoch, by component name, as
249
- `[{ component, epoch, schema, configuration }]`.
254
+ `[{ component, epoch, schema, configuration, created, revision }]`.
250
255
  - `create({ component, configuration, originator })`: a new object for the component's
251
256
  deployed epoch. The configuration must satisfy the schema. Errors: `UNKNOWN_COMPONENT`,
252
257
  `INVALID_CONFIGURATION`.
258
+ - `reset({ component, originator })`: a new object for the component's deployed epoch, holding
259
+ its deployed defaults and their `revision`. Errors: `UNKNOWN_COMPONENT`.
253
260
 
254
- Creating a configuration publishes the `configuration.values.created` event with the
255
- object as stored.
261
+ Creating and resetting publish the `configuration.values.created` event with the object as
262
+ stored. The object of a reset carries a `revision`; a created one does not.
256
263
 
257
264
  ### Resources
258
265
 
259
- | Method | Path | Role |
260
- | ------ | ----------------------------------- | ----------------------------- |
261
- | `GET` | `/configuration/values/` | `system:configuration:get` |
262
- | `GET` | `/configuration/values/:component/` | `system:configuration:get` |
263
- | `POST` | `/configuration/values/:component/` | `system:configuration:create` |
266
+ | Method | Path | Role |
267
+ | -------- | ----------------------------------- | ----------------------------- |
268
+ | `GET` | `/configuration/values/` | `system:configuration:get` |
269
+ | `GET` | `/configuration/values/:component/` | `system:configuration:get` |
270
+ | `POST` | `/configuration/values/:component/` | `system:configuration:create` |
271
+ | `DELETE` | `/configuration/values/:component/` | `system:configuration:create` |
264
272
 
265
273
  `GET /configuration/values/` lists every component's configuration for its deployed epoch, by
266
- component name, as `[{ component, epoch, schema, configuration }]`.
274
+ component name, as `[{ component, epoch, schema, configuration, created, revision }]`.
267
275
 
268
- `GET /configuration/values/:component/` returns `{ configuration, schema, epoch }` for the
269
- deployed epoch, `404` when there is none.
276
+ `GET /configuration/values/:component/` returns
277
+ `{ configuration, schema, epoch, created, revision }` for the deployed epoch, `404` when
278
+ there is none.
270
279
 
271
280
  `POST` takes `{ configuration }`, records the Identity as the `originator`, and returns
272
281
  `{ id, epoch }`. A configuration not satisfying the schema, or an unknown component, is
273
282
  `422`.
274
283
 
284
+ `DELETE` resets the component's configuration to its deployed defaults, records the Identity as
285
+ the `originator`, and answers `204`. An unknown component is `422`.
286
+
275
287
  ## UI
276
288
 
277
- The values service serves a page listing the configured components and creating
278
- configurations, mounted at `/.configuration` on port `8003`. Reading it needs the
279
- `system:configuration:get` role, creating needs `system:configuration:create`.
289
+ The values service serves a page listing the configured components, creating configurations and
290
+ resetting them, mounted at `/.configuration` on port `8003`. Reading it needs the
291
+ `system:configuration:get` role, creating and resetting need `system:configuration:create`.
292
+ A created configuration is marked on the list. Reset is offered only for those: the deployed
293
+ defaults have nothing to bring back.
280
294
 
281
295
  The page is always published: unlike the introspection annotation, the configuration
282
296
  annotation is the per-component values map and has nowhere to carry a switch.
@@ -297,12 +311,13 @@ On start, a component requests its configuration for its epoch from the values s
297
311
  waits until there is one, reporting every fifth attempt. The schema is applied, and secrets
298
312
  are substituted. What is served is what was stored, whole: a `default` written into the schema
299
313
  fills nothing, so a value every component is to have is declared in `defaults`. After a
300
- configuration is created, the running component receives the new object and takes it when its
301
- `CREATED` is later than that of the value it holds.
314
+ configuration is created or reset, the running component receives the new object and takes it
315
+ when its `CREATED` is later than that of the value it holds.
302
316
 
303
317
  Deployed defaults are taken only from a values service of the component's own
304
318
  [revision](#revision). One of another — a values service of the previous deployment, still serving
305
- while it is replaced — is refused and asked again, and the refusal is logged as
319
+ while it is replaced — is refused and asked again, at start and when a running component
320
+ receives a reset, and the refusal is logged as
306
321
  `Configuration of another revision refused` with the `component`, its `epoch`, the `expected`
307
322
  revision and the `received` one, on the first refusal and every fifth. A created configuration is
308
323
  taken from any. A component deployed without a revision, one started by `toa compose` or evicted
@@ -8,6 +8,7 @@ export declare class Aspect extends Connector implements extensions.Aspect {
8
8
  private readonly manifest;
9
9
  private readonly client;
10
10
  private readonly epoch;
11
+ private readonly revision;
11
12
  private value;
12
13
  private created;
13
14
  /**
@@ -8,6 +8,7 @@ export class Aspect extends Connector {
8
8
  manifest;
9
9
  client;
10
10
  epoch;
11
+ revision;
11
12
  value = {};
12
13
  created = 0;
13
14
  /**
@@ -20,6 +21,7 @@ export class Aspect extends Connector {
20
21
  this.manifest = manifest;
21
22
  this.client = client;
22
23
  this.epoch = epoch(manifest.schema);
24
+ this.revision = revision(locator);
23
25
  if (client !== null)
24
26
  this.depends(client);
25
27
  }
@@ -35,7 +37,7 @@ export class Aspect extends Connector {
35
37
  this.value = local(this.locator, this.manifest);
36
38
  return;
37
39
  }
38
- const { configuration, created } = await this.client.fetch(this.locator.id, this.epoch, revision(this.locator));
40
+ const { configuration, created } = await this.client.fetch(this.locator.id, this.epoch, this.revision);
39
41
  this.value = fit(configuration, this.manifest);
40
42
  this.created = created;
41
43
  this.client.subscribe(this.locator.id, this.epoch, this.listener);
@@ -48,10 +50,19 @@ export class Aspect extends Connector {
48
50
  async close() {
49
51
  this.client?.unsubscribe(this.locator.id, this.epoch, this.listener);
50
52
  }
51
- listener = ({ configuration, created }) => {
53
+ listener = (value) => {
52
54
  // deliveries may repeat or cross: only what is newer than the held value replaces it
53
- if (created <= this.created)
55
+ if (value.created <= this.created)
54
56
  return;
57
+ // a reset made by a values service of another deployment holds that deployment's defaults,
58
+ // so they are asked for as on start, where a refusal is reported
59
+ if (this.revision !== undefined &&
60
+ value.revision !== null &&
61
+ value.revision !== this.revision) {
62
+ void this.client.fetch(this.locator.id, this.epoch, this.revision).then(this.listener);
63
+ return;
64
+ }
65
+ const { configuration, created } = value;
55
66
  try {
56
67
  this.value = fit(configuration, this.manifest);
57
68
  this.created = created;