@toa.io/extensions.configuration 1.0.0-alpha.29 → 1.0.0-alpha.292

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 (129) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/components/configuration.values/manifest.toa.yaml +151 -0
  3. package/components/configuration.values/migrations/0002-indexes.yaml +7 -0
  4. package/components/configuration.values/operations/create.js +30 -0
  5. package/components/configuration.values/operations/fetch.js +13 -0
  6. package/components/configuration.values/operations/get.js +19 -0
  7. package/components/configuration.values/operations/lib/map.js +22 -0
  8. package/components/configuration.values/operations/lib/resolve.js +24 -0
  9. package/components/configuration.values/operations/list.js +11 -0
  10. package/components/configuration.values/source/create.ts +54 -0
  11. package/components/configuration.values/source/fetch.ts +26 -0
  12. package/components/configuration.values/source/get.ts +32 -0
  13. package/components/configuration.values/source/lib/map.test.ts +42 -0
  14. package/components/configuration.values/source/lib/map.ts +36 -0
  15. package/components/configuration.values/source/lib/resolve.ts +57 -0
  16. package/components/configuration.values/source/list.ts +21 -0
  17. package/components/configuration.values/tsconfig.json +10 -0
  18. package/components/configuration.values/tsconfig.tsbuildinfo +1 -0
  19. package/components/configuration.values/types/index.d.ts +3 -0
  20. package/components/configuration.values/types/toa.d.ts +68 -0
  21. package/cucumber.mjs +8 -0
  22. package/features/site/_app/immutable/asset.js +1 -0
  23. package/features/site/favicon.ico +0 -0
  24. package/features/site/index.html +10 -0
  25. package/features/steps/UI.ts +98 -0
  26. package/features/steps/tsconfig.json +9 -0
  27. package/features/ui.feature +93 -0
  28. package/notes/consistency.md +10 -9
  29. package/package.json +12 -10
  30. package/readme.md +165 -51
  31. package/source/Aspect.test.ts +114 -10
  32. package/source/Aspect.ts +76 -8
  33. package/source/Client.test.ts +125 -0
  34. package/source/Client.ts +251 -0
  35. package/source/Composition.ts +57 -0
  36. package/source/Factory.ts +29 -7
  37. package/source/Secret.test.ts +32 -0
  38. package/source/Secret.ts +32 -0
  39. package/source/UI.ts +213 -0
  40. package/source/configuration.test.ts +105 -43
  41. package/source/configuration.ts +36 -21
  42. package/source/epoch.test.ts +35 -0
  43. package/source/index.ts +5 -3
  44. package/transpiled/Aspect.d.ts +19 -4
  45. package/transpiled/Aspect.js +62 -9
  46. package/transpiled/Aspect.js.map +1 -1
  47. package/transpiled/Client.d.ts +61 -0
  48. package/transpiled/Client.js +175 -0
  49. package/transpiled/Client.js.map +1 -0
  50. package/transpiled/Composition.d.ts +14 -0
  51. package/transpiled/Composition.js +38 -0
  52. package/transpiled/Composition.js.map +1 -0
  53. package/transpiled/Factory.d.ts +9 -2
  54. package/transpiled/Factory.js +20 -9
  55. package/transpiled/Factory.js.map +1 -1
  56. package/transpiled/Secret.d.ts +15 -0
  57. package/transpiled/Secret.js +26 -0
  58. package/transpiled/Secret.js.map +1 -0
  59. package/transpiled/UI.d.ts +25 -0
  60. package/transpiled/UI.js +173 -0
  61. package/transpiled/UI.js.map +1 -0
  62. package/transpiled/configuration.d.ts +7 -2
  63. package/transpiled/configuration.js +32 -43
  64. package/transpiled/configuration.js.map +1 -1
  65. package/transpiled/index.d.ts +3 -3
  66. package/transpiled/index.js +4 -9
  67. package/transpiled/index.js.map +1 -1
  68. package/tsconfig.json +5 -1
  69. package/tsconfig.tsbuildinfo +1 -0
  70. package/ui/dist/_app/env.js +1 -0
  71. package/ui/dist/_app/immutable/assets/0.CNS8Cqhc.css +2 -0
  72. package/ui/dist/_app/immutable/assets/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2 +0 -0
  73. package/ui/dist/_app/immutable/assets/inter-cyrillic-wght-normal.DqGufNeO.woff2 +0 -0
  74. package/ui/dist/_app/immutable/assets/inter-greek-ext-wght-normal.DlzME5K_.woff2 +0 -0
  75. package/ui/dist/_app/immutable/assets/inter-greek-wght-normal.CkhJZR-_.woff2 +0 -0
  76. package/ui/dist/_app/immutable/assets/inter-latin-ext-wght-normal.DO1Apj_S.woff2 +0 -0
  77. package/ui/dist/_app/immutable/assets/inter-latin-wght-normal.Dx4kXJAl.woff2 +0 -0
  78. package/ui/dist/_app/immutable/assets/inter-vietnamese-wght-normal.CBcvBZtf.woff2 +0 -0
  79. package/ui/dist/_app/immutable/assets/ui.BghF56l_.css +1 -0
  80. package/ui/dist/_app/immutable/chunks/-d-dxcbq.js +8 -0
  81. package/ui/dist/_app/immutable/chunks/64H_hMtE.js +50 -0
  82. package/ui/dist/_app/immutable/chunks/BeorVtKF.js +6 -0
  83. package/ui/dist/_app/immutable/chunks/Bjy-W4x2.js +81 -0
  84. package/ui/dist/_app/immutable/chunks/C1Pl1PSE.js +1 -0
  85. package/ui/dist/_app/immutable/chunks/CcD_ISxM.js +3 -0
  86. package/ui/dist/_app/immutable/chunks/DzmF9ykx.js +1 -0
  87. package/ui/dist/_app/immutable/chunks/xihTtKlq.js +1 -0
  88. package/ui/dist/_app/immutable/entry/app.D5YrjA4l.js +2 -0
  89. package/ui/dist/_app/immutable/entry/start.CKkVSyXd.js +1 -0
  90. package/ui/dist/_app/immutable/nodes/0.Cm3Z3YNE.js +1 -0
  91. package/ui/dist/_app/immutable/nodes/1.e2aDXaPj.js +1 -0
  92. package/ui/dist/_app/immutable/nodes/2.Blh7Ex_K.js +1 -0
  93. package/ui/dist/_app/immutable/nodes/3.61NbGuZc.js +1 -0
  94. package/ui/dist/_app/immutable/nodes/4.B6OdRyVa.js +1 -0
  95. package/ui/dist/_app/version.json +1 -0
  96. package/ui/dist/apple-touch-icon.png +0 -0
  97. package/ui/dist/favicon-96x96.png +0 -0
  98. package/ui/dist/favicon.ico +0 -0
  99. package/ui/dist/index.html +57 -0
  100. package/ui/dist/robots.txt +3 -0
  101. package/schemas/annotation.cos.yaml +0 -1
  102. package/schemas/manifest.cos.yaml +0 -2
  103. package/source/deployment.test.ts +0 -21
  104. package/source/deployment.ts +0 -86
  105. package/source/manifest.test.ts +0 -15
  106. package/source/manifest.ts +0 -15
  107. package/source/schemas.ts +0 -9
  108. package/transpiled/Aspect.test.d.ts +0 -1
  109. package/transpiled/Aspect.test.js +0 -15
  110. package/transpiled/Aspect.test.js.map +0 -1
  111. package/transpiled/configuration.test.d.ts +0 -1
  112. package/transpiled/configuration.test.js +0 -64
  113. package/transpiled/configuration.test.js.map +0 -1
  114. package/transpiled/deployment.d.ts +0 -8
  115. package/transpiled/deployment.js +0 -88
  116. package/transpiled/deployment.js.map +0 -1
  117. package/transpiled/deployment.test.d.ts +0 -1
  118. package/transpiled/deployment.test.js +0 -18
  119. package/transpiled/deployment.test.js.map +0 -1
  120. package/transpiled/manifest.d.ts +0 -6
  121. package/transpiled/manifest.js +0 -35
  122. package/transpiled/manifest.js.map +0 -1
  123. package/transpiled/manifest.test.d.ts +0 -1
  124. package/transpiled/manifest.test.js +0 -14
  125. package/transpiled/manifest.test.js.map +0 -1
  126. package/transpiled/schemas.d.ts +0 -3
  127. package/transpiled/schemas.js +0 -33
  128. package/transpiled/schemas.js.map +0 -1
  129. package/transpiled/tsconfig.tsbuildinfo +0 -1
@@ -1,85 +1,147 @@
1
- import { encode } from '@toa.io/generic'
1
+ import { describe, it, beforeEach, afterEach } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
2
4
  import { Locator } from '@toa.io/core'
3
5
  import { generate } from 'randomstring'
4
- import { get } from './configuration'
5
- import { type Manifest } from './manifest'
6
+ import { fit, local, overridden } from './configuration.js'
7
+ import { Secret } from './Secret.js'
8
+ import { type Manifest } from '@toa.io/definitions/extensions.configuration'
6
9
 
7
10
  let locator: Locator
8
11
  let manifest: Manifest
9
12
 
10
13
  beforeEach(() => {
11
14
  locator = new Locator(generate(), generate())
12
- manifest = { schema: { foo: 'string' } }
15
+ manifest = {
16
+ schema: {
17
+ type: 'object',
18
+ properties: { foo: { type: 'string' } }
19
+ }
20
+ }
13
21
  })
14
22
 
15
23
  afterEach(() => {
16
- for (const name of used)
17
- delete process.env[name]
24
+ for (const name of used) delete process.env[name]
18
25
 
19
26
  used = []
20
27
  })
21
28
 
22
- it('should read value', async () => {
23
- manifest.schema = { foo: 'string' }
29
+ describe('overridden', () => {
30
+ it('should be false without the variable', async () => {
31
+ assert.deepStrictEqual(overridden(locator), false)
32
+ })
24
33
 
25
- const value: object = { foo: generate() }
34
+ it('should be true with the variable', async () => {
35
+ set({})
26
36
 
27
- set(value)
37
+ assert.deepStrictEqual(overridden(locator), true)
38
+ })
39
+ })
28
40
 
29
- const result = get(locator, manifest)
41
+ describe('local', () => {
42
+ it('should read value', async () => {
43
+ const value: object = { foo: generate() }
30
44
 
31
- expect(result).toStrictEqual(value)
32
- })
45
+ set(value)
33
46
 
34
- it('should return empty object if no value set', async () => {
35
- expect(get(locator, manifest)).toStrictEqual({})
36
- })
47
+ assert.deepStrictEqual(local(locator, manifest), value)
48
+ })
37
49
 
38
- it('should substitute secrets', async () => {
39
- const value: object = { foo: '$BAR' }
50
+ it('should return empty object if no value set', async () => {
51
+ assert.deepStrictEqual(local(locator, manifest), {})
52
+ })
40
53
 
41
- set(value)
42
- set('bar', '_BAR')
54
+ it('should substitute secrets', async () => {
55
+ set({ foo: '$BAR' })
56
+ set('bar', '_BAR')
43
57
 
44
- const result = get(locator, manifest)
58
+ const { foo } = local(locator, manifest)
45
59
 
46
- expect(result).toStrictEqual({ foo: 'bar' })
47
- })
60
+ assert.ok(foo instanceof Secret)
61
+ assert.deepStrictEqual((foo as Secret).unwrap(), 'bar')
62
+ assert.deepStrictEqual(String(foo), '<REDACTED>')
63
+ })
48
64
 
49
- it('should use defaults', async () => {
50
- manifest.schema = { foo: 'string', bar: ['number'], 'baz?': 'string' }
51
- manifest.defaults = { foo: 'bar', bar: [1] }
65
+ it('should substitute secrets in defaults', async () => {
66
+ manifest.defaults = { foo: '$BAR' }
52
67
 
53
- const values = { bar: [2], baz: 'foo' }
68
+ set('bar', '_BAR')
54
69
 
55
- set(values)
70
+ assert.deepStrictEqual((local(locator, manifest).foo as Secret).unwrap(), 'bar')
71
+ })
72
+
73
+ it('should use defaults', async () => {
74
+ manifest.schema = {
75
+ type: 'object',
76
+ properties: {
77
+ foo: { type: 'string' },
78
+ bar: { type: 'array', items: { type: 'number' } },
79
+ baz: { type: 'string' }
80
+ },
81
+ required: ['foo', 'bar']
82
+ }
83
+ manifest.defaults = { foo: 'bar', bar: [1] }
84
+
85
+ set({ bar: [2], baz: 'foo' })
86
+
87
+ assert.deepStrictEqual(local(locator, manifest), { foo: 'bar', bar: [2], baz: 'foo' })
88
+ })
89
+
90
+ it('should validate', async () => {
91
+ manifest.schema = {
92
+ type: 'object',
93
+ properties: {
94
+ foo: { type: 'string' },
95
+ bar: { type: 'number' }
96
+ }
97
+ }
98
+
99
+ set({ bar: 5 })
56
100
 
57
- const result = get(locator, manifest)
101
+ assert.deepStrictEqual(local(locator, manifest), { bar: 5 })
58
102
 
59
- expect(result).toStrictEqual({
60
- foo: 'bar',
61
- bar: [2],
62
- baz: 'foo'
103
+ set({ bar: 'five' })
104
+
105
+ assert.throws(
106
+ () => local(locator, manifest),
107
+ (error: Error) => /must be number/.test(error.message)
108
+ )
63
109
  })
64
110
  })
65
111
 
66
- it('should validate', async () => {
67
- manifest.schema = { foo: 'hello', bar: 'number' }
112
+ describe('fit', () => {
113
+ it('should substitute secrets and apply the schema', async () => {
114
+ manifest.schema = {
115
+ type: 'object',
116
+ properties: {
117
+ foo: { type: 'string' },
118
+ bar: { type: 'number' }
119
+ }
120
+ }
121
+
122
+ set('secret', '_FOO')
68
123
 
69
- const values = { bar: 5 }
124
+ const raw = { foo: '$FOO', bar: 1 }
125
+ const values = fit(raw, manifest)
70
126
 
71
- set(values)
127
+ assert.deepStrictEqual((values.foo as Secret).unwrap(), 'secret')
128
+ assert.deepStrictEqual(values.bar, 1)
129
+ assert.deepStrictEqual(raw, { foo: '$FOO', bar: 1 }) // untouched
130
+ })
72
131
 
73
- const result = get(locator, manifest)
132
+ it('should not apply the manifest defaults', async () => {
133
+ manifest.defaults = { foo: 'hello' }
134
+
135
+ assert.deepStrictEqual(fit({}, manifest), {})
136
+ })
74
137
 
75
- expect(result).toStrictEqual({
76
- foo: 'hello',
77
- bar: 5
138
+ it('should throw on a value not fitting the schema', async () => {
139
+ assert.throws(() => fit({ foo: { nested: true } }, manifest))
78
140
  })
79
141
  })
80
142
 
81
- function set (value: object | string, key = locator.uppercase): void {
82
- const string = typeof value === 'string' ? value : encode(value)
143
+ function set(value: object | string, key = locator.uppercase): void {
144
+ const string = typeof value === 'string' ? value : JSON.stringify(value)
83
145
  const name = 'TOA_CONFIGURATION_' + key
84
146
 
85
147
  process.env[name] = string
@@ -1,39 +1,54 @@
1
1
  import { type Locator } from '@toa.io/core'
2
- import { decode, add } from '@toa.io/generic'
2
+ import { add, environment } from '@toa.io/generic'
3
3
  import * as schemas from '@toa.io/schemas'
4
- import { PREFIX, SECRET_RX } from './deployment'
5
- import { type Manifest } from './manifest'
4
+ import { PREFIX, SECRET_RX } from '@toa.io/definitions/extensions.configuration'
5
+ import { Secret } from './Secret.js'
6
6
  import type { Schema } from '@toa.io/schemas'
7
+ import type { Manifest } from '@toa.io/definitions/extensions.configuration'
7
8
 
8
- export function get (locator: Locator, manifest: Manifest): Node {
9
- const values = getConfiguration(locator.uppercase)
9
+ /** The variable is set, so the values service is not consulted. */
10
+ export function overridden(locator: Locator): boolean {
11
+ return environment.get(PREFIX + locator.uppercase) !== undefined
12
+ }
13
+
14
+ /** The variable, the manifest defaults, then the schema. */
15
+ export function local(locator: Locator, manifest: Manifest): Node {
16
+ const values = read(locator.uppercase)
17
+
18
+ if (manifest.defaults !== undefined) add(values, manifest.defaults)
19
+
20
+ return fit(values, manifest)
21
+ }
10
22
 
23
+ /** A copy of the value with the schema applied and the secrets substituted. */
24
+ export function fit(raw: object, manifest: Manifest): Node {
25
+ // a copy of the caller's, and one of this realm: what came over the wire is JSON anyway
26
+ const values = JSON.parse(JSON.stringify(raw)) as Node
27
+
28
+ // the schema sees the references, which are the strings it declares
29
+ validate(values, manifest)
11
30
  substituteSecrets(values)
12
31
 
13
- if (manifest.defaults !== undefined)
14
- add(values, manifest.defaults)
32
+ return values
33
+ }
15
34
 
35
+ function validate(values: Node, manifest: Manifest): void {
16
36
  const schema: Schema<any> = schemas.schema(manifest.schema)
17
37
 
18
38
  schema.validate(values)
19
-
20
- return values
21
39
  }
22
40
 
23
- function getConfiguration (suffix: string): Node {
41
+ function read(suffix: string): Node {
24
42
  const variable = PREFIX + suffix
25
- const string = process.env[variable]
43
+ const string = environment.get(variable)
26
44
 
27
- if (string === undefined)
28
- return {}
29
- else
30
- return decode(string)
45
+ if (string === undefined) return {}
46
+ else return JSON.parse(string)
31
47
  }
32
48
 
33
- function substituteSecrets (configuration: Node): void {
49
+ function substituteSecrets(configuration: Node): void {
34
50
  for (const [key, value] of Object.entries(configuration)) {
35
- if (typeof value === 'object' && value !== null)
36
- substituteSecrets(value as Node)
51
+ if (typeof value === 'object' && value !== null) substituteSecrets(value as Node)
37
52
 
38
53
  if (typeof value !== 'string') continue
39
54
 
@@ -43,13 +58,13 @@ function substituteSecrets (configuration: Node): void {
43
58
 
44
59
  const name = match.groups?.variable
45
60
 
46
- configuration[key] = getSecret(name!)
61
+ configuration[key] = new Secret(getSecret(name!))
47
62
  }
48
63
  }
49
64
 
50
- function getSecret (name: string): string {
65
+ function getSecret(name: string): string {
51
66
  const variable = PREFIX + '_' + name
52
- const value = process.env[variable]
67
+ const value = environment.get(variable)
53
68
 
54
69
  if (value === undefined) throw new Error(`${variable} is not set.`)
55
70
 
@@ -0,0 +1,35 @@
1
+ import { it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { epoch } from '@toa.io/definitions/extensions.configuration'
5
+
6
+ it('should be a sha256 hex', () => {
7
+ assert.match(epoch({ type: 'object' }), /^[a-f0-9]{64}$/)
8
+ })
9
+
10
+ it('should not depend on key order', () => {
11
+ const a = {
12
+ type: 'object',
13
+ properties: { foo: { type: 'string', default: 'x' }, bar: { type: 'number' } }
14
+ }
15
+ const b = {
16
+ properties: { bar: { type: 'number' }, foo: { default: 'x', type: 'string' } },
17
+ type: 'object'
18
+ }
19
+
20
+ assert.deepStrictEqual(epoch(a), epoch(b))
21
+ })
22
+
23
+ it('should depend on values', () => {
24
+ const a = { type: 'object', properties: { foo: { type: 'string' } } }
25
+ const b = { type: 'object', properties: { foo: { type: 'number' } } }
26
+
27
+ assert.notDeepStrictEqual(epoch(a), epoch(b))
28
+ })
29
+
30
+ it('should keep array order', () => {
31
+ const a = { enum: [1, 2] }
32
+ const b = { enum: [2, 1] }
33
+
34
+ assert.notDeepStrictEqual(epoch(a), epoch(b))
35
+ })
package/source/index.ts CHANGED
@@ -1,3 +1,5 @@
1
- export { manifest } from './manifest'
2
- export { deployment } from './deployment'
3
- export { Factory } from './Factory'
1
+ export { components } from './Composition.js'
2
+ export { Factory } from './Factory.js'
3
+
4
+ // what a value declared `format: secret` is on a component's context
5
+ export { Secret } from './Secret.js'
@@ -1,7 +1,22 @@
1
- import { Connector, type extensions } from '@toa.io/core';
1
+ import { Connector, type Locator } from '@toa.io/core';
2
+ import type { extensions } from '@toa.io/core/types';
3
+ import type { Client } from './Client.js';
4
+ import type { Manifest } from '@toa.io/definitions/extensions.configuration';
2
5
  export declare class Aspect extends Connector implements extensions.Aspect {
3
6
  readonly name = "configuration";
4
- private readonly value;
5
- constructor(value: object);
6
- invoke(path: string[]): any;
7
+ private readonly locator;
8
+ private readonly manifest;
9
+ private readonly client;
10
+ private readonly epoch;
11
+ private value;
12
+ private created;
13
+ /**
14
+ * Without a client the value is local: the variable, the defaults and the schema.
15
+ * With one, the value is what the service holds, and it follows the service.
16
+ */
17
+ constructor(locator: Locator, manifest: Manifest, client: Client | null);
18
+ invoke(path?: string[]): any;
19
+ protected open(): Promise<void>;
20
+ protected close(): Promise<void>;
21
+ private readonly listener;
7
22
  }
@@ -1,13 +1,27 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Aspect = void 0;
4
- const core_1 = require("@toa.io/core");
5
- class Aspect extends core_1.Connector {
1
+ import { console } from 'openspan';
2
+ import { Connector } from '@toa.io/core';
3
+ import { fit, local } from './configuration.js';
4
+ import { epoch } from '@toa.io/definitions/extensions.configuration';
5
+ export class Aspect extends Connector {
6
6
  name = 'configuration';
7
- value;
8
- constructor(value) {
7
+ locator;
8
+ manifest;
9
+ client;
10
+ epoch;
11
+ value = {};
12
+ created = 0;
13
+ /**
14
+ * Without a client the value is local: the variable, the defaults and the schema.
15
+ * With one, the value is what the service holds, and it follows the service.
16
+ */
17
+ constructor(locator, manifest, client) {
9
18
  super();
10
- this.value = value;
19
+ this.locator = locator;
20
+ this.manifest = manifest;
21
+ this.client = client;
22
+ this.epoch = epoch(manifest.schema);
23
+ if (client !== null)
24
+ this.depends(client);
11
25
  }
12
26
  invoke(path) {
13
27
  let cursor = this.value;
@@ -16,6 +30,45 @@ class Aspect extends core_1.Connector {
16
30
  cursor = cursor[segment];
17
31
  return cursor;
18
32
  }
33
+ async open() {
34
+ if (this.client === null) {
35
+ this.value = local(this.locator, this.manifest);
36
+ return;
37
+ }
38
+ const { configuration, created } = await this.client.fetch(this.locator.id, this.epoch);
39
+ this.value = fit(configuration, this.manifest);
40
+ this.created = created;
41
+ this.client.subscribe(this.locator.id, this.epoch, this.listener);
42
+ console.info('Configuration resolved', {
43
+ component: this.locator.id,
44
+ epoch: this.epoch,
45
+ created
46
+ });
47
+ }
48
+ async close() {
49
+ this.client?.unsubscribe(this.locator.id, this.epoch, this.listener);
50
+ }
51
+ listener = ({ configuration, created }) => {
52
+ // deliveries may repeat or cross: only what is newer than the held value replaces it
53
+ if (created <= this.created)
54
+ return;
55
+ try {
56
+ this.value = fit(configuration, this.manifest);
57
+ this.created = created;
58
+ console.info('Configuration updated', {
59
+ component: this.locator.id,
60
+ epoch: this.epoch,
61
+ created
62
+ });
63
+ }
64
+ catch (error) {
65
+ // the service validated it against the schema of its epoch, so the two schemas differ
66
+ console.error('Configuration value does not match the schema', {
67
+ component: this.locator.id,
68
+ epoch: this.epoch,
69
+ error
70
+ });
71
+ }
72
+ };
19
73
  }
20
- exports.Aspect = Aspect;
21
74
  //# sourceMappingURL=Aspect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Aspect.js","sourceRoot":"","sources":["../source/Aspect.ts"],"names":[],"mappings":";;;AAAA,uCAAyD;AAEzD,MAAa,MAAO,SAAQ,gBAAS;IACnB,IAAI,GAAG,eAAe,CAAA;IAErB,KAAK,CAAQ;IAE9B,YAAoB,KAAa;QAC/B,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAEM,MAAM,CAAE,IAAc;QAC3B,IAAI,MAAM,GAAQ,IAAI,CAAC,KAAK,CAAA;QAE5B,IAAI,IAAI,KAAK,SAAS;YACpB,KAAK,MAAM,OAAO,IAAI,IAAI;gBACxB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;QAE5B,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AApBD,wBAoBC"}
1
+ {"version":3,"file":"Aspect.js","sourceRoot":"","sources":["../source/Aspect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,OAAO,EAAE,SAAS,EAAgB,MAAM,cAAc,CAAA;AAEtD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAa,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,8CAA8C,CAAA;AAIpE,MAAM,OAAO,MAAO,SAAQ,SAAS;IACnB,IAAI,GAAG,eAAe,CAAA;IAErB,OAAO,CAAS;IAChB,QAAQ,CAAU;IAClB,MAAM,CAAe;IACrB,KAAK,CAAQ;IACtB,KAAK,GAAS,EAAE,CAAA;IAChB,OAAO,GAAG,CAAC,CAAA;IAEnB;;;OAGG;IACH,YAAmB,OAAgB,EAAE,QAAkB,EAAE,MAAqB;QAC5E,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;QAEnC,IAAI,MAAM,KAAK,IAAI;YAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3C,CAAC;IAEM,MAAM,CAAC,IAAe;QAC3B,IAAI,MAAM,GAAQ,IAAI,CAAC,KAAK,CAAA;QAE5B,IAAI,IAAI,KAAK,SAAS;YAAE,KAAK,MAAM,OAAO,IAAI,IAAI;gBAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;QAE5E,OAAO,MAAM,CAAA;IACf,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YAE/C,OAAM;QACR,CAAC;QAED,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CACxD,IAAI,CAAC,OAAO,CAAC,EAAE,EACf,IAAI,CAAC,KAAK,CACX,CAAA;QAED,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC9C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAEjE,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACrC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;YAC1B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAEkB,KAAK,CAAC,KAAK;QAC5B,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IACtE,CAAC;IAEgB,QAAQ,GAAG,CAAC,EAAE,aAAa,EAAE,OAAO,EAAS,EAAQ,EAAE;QACtE,qFAAqF;QACrF,IAAI,OAAO,IAAI,IAAI,CAAC,OAAO;YAAE,OAAM;QAEnC,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YAC9C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;YAEtB,OAAO,CAAC,IAAI,CAAC,uBAAuB,EAAE;gBACpC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;gBAC1B,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,OAAO;aACR,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sFAAsF;YACtF,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE;gBAC7D,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;gBAC1B,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK;aACN,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CAAA;CACF"}
@@ -0,0 +1,61 @@
1
+ import { Connector } from '@toa.io/core';
2
+ import type { Host } from './Factory.js';
3
+ /**
4
+ * One per process: one remote to the values service and one subscription to its events,
5
+ * shared by every Aspect. What the Aspects ask for is collected and sent as one call;
6
+ * what the service creates afterwards is handed to whoever subscribed.
7
+ */
8
+ export declare class Client extends Connector {
9
+ /** Disconnected once, a connector keeps what it depended on, so a gone client is not reused. */
10
+ disposed: boolean;
11
+ private readonly host;
12
+ private readonly options;
13
+ private readonly pending;
14
+ private readonly listeners;
15
+ private remote;
16
+ private timer;
17
+ private flushing;
18
+ private fresh;
19
+ private round;
20
+ constructor(host: Host, options?: Partial<Options>);
21
+ /** The configuration of a component for an epoch, once the service has one. */
22
+ fetch(component: string, epoch: string): Promise<Value>;
23
+ subscribe(component: string, epoch: string, listener: Listener): void;
24
+ unsubscribe(component: string, epoch: string, listener: Listener): void;
25
+ protected open(): Promise<void>;
26
+ protected close(): Promise<void>;
27
+ protected dispose(): Promise<void>;
28
+ private schedule;
29
+ private flush;
30
+ /** Those the service has served are told; the rest stay for the next round. */
31
+ private settle;
32
+ private report;
33
+ private backoff;
34
+ /** A created object goes to the subscribers of its component and epoch, as it is. */
35
+ private deliver;
36
+ }
37
+ export interface Options {
38
+ base: number;
39
+ max: number;
40
+ /** Every n-th round of waiting is reported. */
41
+ warn: number;
42
+ }
43
+ /** A configuration and when it was created; `0` for the deployed defaults. */
44
+ export interface Value {
45
+ configuration: object;
46
+ created: number;
47
+ }
48
+ export interface Fetched {
49
+ component: string;
50
+ epoch: string;
51
+ configuration: object | null;
52
+ created: number;
53
+ }
54
+ /** The `configuration.values.created` payload: the object as stored. */
55
+ export interface Created {
56
+ component: string;
57
+ epoch: string;
58
+ configuration: object;
59
+ CREATED: number;
60
+ }
61
+ export type Listener = (value: Value) => void;