@toa.io/extensions.configuration 1.0.0-alpha.28 → 1.0.0-alpha.282
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 +347 -0
- package/components/configuration.values/manifest.toa.yaml +158 -0
- package/components/configuration.values/operations/create.d.ts +16 -0
- package/components/configuration.values/operations/create.js +31 -0
- package/components/configuration.values/operations/create.js.map +1 -0
- package/components/configuration.values/operations/fetch.d.ts +11 -0
- package/components/configuration.values/operations/fetch.js +13 -0
- package/components/configuration.values/operations/fetch.js.map +1 -0
- package/components/configuration.values/operations/get.d.ts +17 -0
- package/components/configuration.values/operations/get.js +19 -0
- package/components/configuration.values/operations/get.js.map +1 -0
- package/components/configuration.values/operations/lib/map.d.ts +11 -0
- package/components/configuration.values/operations/lib/map.js +21 -0
- package/components/configuration.values/operations/lib/map.js.map +1 -0
- package/components/configuration.values/operations/lib/map.test.d.ts +1 -0
- package/components/configuration.values/operations/lib/map.test.js +26 -0
- package/components/configuration.values/operations/lib/map.test.js.map +1 -0
- package/components/configuration.values/operations/lib/resolve.d.ts +27 -0
- package/components/configuration.values/operations/lib/resolve.js +24 -0
- package/components/configuration.values/operations/lib/resolve.js.map +1 -0
- package/components/configuration.values/operations/list.d.ts +10 -0
- package/components/configuration.values/operations/list.js +11 -0
- package/components/configuration.values/operations/list.js.map +1 -0
- package/components/configuration.values/source/create.ts +56 -0
- package/components/configuration.values/source/fetch.ts +24 -0
- package/components/configuration.values/source/get.ts +33 -0
- package/components/configuration.values/source/lib/map.test.ts +37 -0
- package/components/configuration.values/source/lib/map.ts +34 -0
- package/components/configuration.values/source/lib/resolve.ts +54 -0
- package/components/configuration.values/source/list.ts +19 -0
- package/components/configuration.values/tsconfig.json +10 -0
- package/components/configuration.values/tsconfig.tsbuildinfo +1 -0
- package/cucumber.mjs +5 -0
- package/features/site/_app/immutable/asset.js +1 -0
- package/features/site/favicon.ico +0 -0
- package/features/site/index.html +6 -0
- package/features/steps/UI.ts +91 -0
- package/features/steps/tsconfig.json +9 -0
- package/features/ui.feature +93 -0
- package/package.json +11 -10
- package/readme.md +160 -47
- package/schemas/annotation.cos.yaml +29 -1
- package/schemas/manifest.cos.yaml +9 -2
- package/source/Aspect.test.ts +89 -9
- package/source/Aspect.ts +64 -5
- package/source/Client.test.ts +108 -0
- package/source/Client.ts +258 -0
- package/source/Composition.ts +57 -0
- package/source/Factory.ts +28 -6
- package/source/Secret.test.ts +32 -0
- package/source/Secret.ts +29 -0
- package/source/UI.ts +212 -0
- package/source/configuration.test.ts +96 -40
- package/source/configuration.ts +30 -11
- package/source/const.ts +17 -0
- package/source/deployment.test.ts +78 -12
- package/source/deployment.ts +75 -27
- package/source/epoch.test.ts +29 -0
- package/source/epoch.ts +21 -0
- package/source/index.ts +4 -3
- package/source/manifest.test.ts +8 -5
- package/source/manifest.ts +2 -4
- package/source/schemas.ts +1 -1
- package/transpiled/Aspect.d.ts +18 -4
- package/transpiled/Aspect.js +50 -9
- package/transpiled/Aspect.js.map +1 -1
- package/transpiled/Aspect.test.js +63 -12
- package/transpiled/Aspect.test.js.map +1 -1
- package/transpiled/Client.d.ts +61 -0
- package/transpiled/Client.js +169 -0
- package/transpiled/Client.js.map +1 -0
- package/transpiled/Client.test.d.ts +1 -0
- package/transpiled/Client.test.js +78 -0
- package/transpiled/Client.test.js.map +1 -0
- package/transpiled/Composition.d.ts +14 -0
- package/transpiled/Composition.js +38 -0
- package/transpiled/Composition.js.map +1 -0
- package/transpiled/Factory.d.ts +8 -2
- package/transpiled/Factory.js +20 -9
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Secret.d.ts +12 -0
- package/transpiled/Secret.js +22 -0
- package/transpiled/Secret.js.map +1 -0
- package/transpiled/Secret.test.d.ts +1 -0
- package/transpiled/Secret.test.js +26 -0
- package/transpiled/Secret.test.js.map +1 -0
- package/transpiled/UI.d.ts +25 -0
- package/transpiled/UI.js +172 -0
- package/transpiled/UI.js.map +1 -0
- package/transpiled/configuration.d.ts +7 -2
- package/transpiled/configuration.js +30 -41
- package/transpiled/configuration.js.map +1 -1
- package/transpiled/configuration.test.js +90 -43
- package/transpiled/configuration.test.js.map +1 -1
- package/transpiled/const.d.ts +11 -0
- package/transpiled/const.js +11 -0
- package/transpiled/const.js.map +1 -0
- package/transpiled/deployment.d.ts +9 -3
- package/transpiled/deployment.js +62 -57
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/deployment.test.js +61 -13
- package/transpiled/deployment.test.js.map +1 -1
- package/transpiled/epoch.d.ts +2 -0
- package/transpiled/epoch.js +17 -0
- package/transpiled/epoch.js.map +1 -0
- package/transpiled/epoch.test.d.ts +1 -0
- package/transpiled/epoch.test.js +22 -0
- package/transpiled/epoch.test.js.map +1 -0
- package/transpiled/index.d.ts +4 -3
- package/transpiled/index.js +4 -9
- package/transpiled/index.js.map +1 -1
- package/transpiled/manifest.d.ts +1 -1
- package/transpiled/manifest.js +2 -31
- package/transpiled/manifest.js.map +1 -1
- package/transpiled/manifest.test.js +9 -9
- package/transpiled/manifest.test.js.map +1 -1
- package/transpiled/schemas.js +5 -31
- package/transpiled/schemas.js.map +1 -1
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -0
- package/ui/dist/_app/env.js +1 -0
- package/ui/dist/_app/immutable/assets/0.CNS8Cqhc.css +2 -0
- package/ui/dist/_app/immutable/assets/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-cyrillic-wght-normal.DqGufNeO.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-greek-ext-wght-normal.DlzME5K_.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-greek-wght-normal.CkhJZR-_.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-latin-ext-wght-normal.DO1Apj_S.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-latin-wght-normal.Dx4kXJAl.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-vietnamese-wght-normal.CBcvBZtf.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/ui.BghF56l_.css +1 -0
- package/ui/dist/_app/immutable/chunks/BI4iTnyC.js +1 -0
- package/ui/dist/_app/immutable/chunks/Bjy-W4x2.js +81 -0
- package/ui/dist/_app/immutable/chunks/CcD_ISxM.js +3 -0
- package/ui/dist/_app/immutable/chunks/CcWYApmX.js +6 -0
- package/ui/dist/_app/immutable/chunks/DNNahQnI.js +8 -0
- package/ui/dist/_app/immutable/chunks/XPGIBZyO.js +1 -0
- package/ui/dist/_app/immutable/chunks/_OCUOSf_.js +50 -0
- package/ui/dist/_app/immutable/chunks/xihTtKlq.js +1 -0
- package/ui/dist/_app/immutable/entry/app.Dm0NVzuV.js +2 -0
- package/ui/dist/_app/immutable/entry/start.DWjxVbPv.js +1 -0
- package/ui/dist/_app/immutable/nodes/0.C1zAG1m5.js +1 -0
- package/ui/dist/_app/immutable/nodes/1.QblBNjka.js +1 -0
- package/ui/dist/_app/immutable/nodes/2.CyVYZW9A.js +1 -0
- package/ui/dist/_app/immutable/nodes/3.DJ58VY-o.js +1 -0
- package/ui/dist/_app/immutable/nodes/4.CZA2PMJQ.js +1 -0
- package/ui/dist/_app/version.json +1 -0
- package/ui/dist/apple-touch-icon.png +0 -0
- package/ui/dist/favicon-96x96.png +0 -0
- package/ui/dist/favicon.ico +0 -0
- package/ui/dist/index.html +52 -0
- package/ui/dist/robots.txt +3 -0
- package/transpiled/tsconfig.tsbuildinfo +0 -1
package/source/deployment.ts
CHANGED
|
@@ -1,35 +1,64 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
1
2
|
import assert from 'node:assert'
|
|
2
|
-
import { type Dependency, type Variable, type Variables } from '@toa.io/operations'
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import * as validators from './schemas'
|
|
7
|
-
import type {
|
|
3
|
+
import { type Dependency, type Resources, type Service, type Variable, type Variables } from '@toa.io/operations'
|
|
4
|
+
import { components } from './Composition.js'
|
|
5
|
+
import { EVENT, PREFIX, SECRET_RX, UI_PATH, UI_PORT, VALUES } from './const.js'
|
|
6
|
+
import { epoch } from './epoch.js'
|
|
7
|
+
import * as validators from './schemas.js'
|
|
8
|
+
import type { Manifest } from './manifest.js'
|
|
8
9
|
import type { context } from '@toa.io/norm'
|
|
9
10
|
|
|
10
11
|
export function deployment (instances: Instance[], annotation: Annotation = {}): Dependency {
|
|
11
|
-
|
|
12
|
+
const { resources, values } = split(annotation)
|
|
13
|
+
|
|
14
|
+
annotation = prepare(values, instances)
|
|
12
15
|
|
|
13
16
|
const variables: Variables = {}
|
|
14
17
|
|
|
15
18
|
for (const instance of instances) {
|
|
16
19
|
const values = annotation[instance.locator.id]
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
if (values === undefined)
|
|
22
|
+
continue
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
const secrets = createSecrets(values)
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
if (secrets.length > 0)
|
|
27
|
+
variables[instance.locator.label] = secrets
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const service: Service = {
|
|
31
|
+
group: 'configuration',
|
|
32
|
+
name: 'values',
|
|
33
|
+
version: JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version,
|
|
34
|
+
components: components().labels,
|
|
35
|
+
resources,
|
|
36
|
+
// the service that holds the values also serves the page that reads them
|
|
37
|
+
port: UI_PORT,
|
|
38
|
+
ingress: { path: UI_PATH },
|
|
39
|
+
variables: [{
|
|
40
|
+
name: VALUES,
|
|
41
|
+
value: JSON.stringify(describe(instances, annotation))
|
|
25
42
|
}]
|
|
43
|
+
}
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
return { services: [service], variables, events: [EVENT] }
|
|
46
|
+
}
|
|
28
47
|
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
/** What the values service is given: the epoch, the schema and the defaults of every component. */
|
|
49
|
+
export function describe (instances: Instance[], annotation: Annotation = {}): Values {
|
|
50
|
+
annotation = prepare(split(annotation).values, instances)
|
|
51
|
+
|
|
52
|
+
const values: Values = {}
|
|
53
|
+
|
|
54
|
+
for (const { locator, manifest } of instances)
|
|
55
|
+
values[locator.id] = {
|
|
56
|
+
epoch: epoch(manifest.schema),
|
|
57
|
+
schema: manifest.schema,
|
|
58
|
+
defaults: annotation[locator.id] ?? manifest.defaults
|
|
59
|
+
}
|
|
31
60
|
|
|
32
|
-
return
|
|
61
|
+
return values
|
|
33
62
|
}
|
|
34
63
|
|
|
35
64
|
function createSecrets (values: object): Variable[] {
|
|
@@ -61,26 +90,45 @@ function createSecrets (values: object): Variable[] {
|
|
|
61
90
|
return secrets
|
|
62
91
|
}
|
|
63
92
|
|
|
64
|
-
|
|
93
|
+
/**
|
|
94
|
+
* The service's own resources, and the component values that are the rest of the annotation.
|
|
95
|
+
*
|
|
96
|
+
* Every key here names a component, so the one option the service has of its own needs a
|
|
97
|
+
* name that cannot be mistaken for one. `resources` is reserved: a component actually
|
|
98
|
+
* called that is written with its namespace, `default.resources`, which is what an id is
|
|
99
|
+
* anyway — the bare form is the shorthand.
|
|
100
|
+
*/
|
|
101
|
+
function split (annotation: Annotation): { resources?: Resources, values: Annotation } {
|
|
65
102
|
validators.annotation.validate(annotation)
|
|
66
103
|
|
|
104
|
+
const { resources, ...values } = annotation as Annotation & { resources?: Resources }
|
|
105
|
+
|
|
106
|
+
return { resources, values }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Validated, keyed by full component ids, and checked against the components that ask. */
|
|
110
|
+
function prepare (annotation: Annotation, instances: Instance[]): Annotation {
|
|
111
|
+
const normalized: Annotation = {}
|
|
67
112
|
const requested = instances.map((instance) => instance.locator.id)
|
|
68
113
|
|
|
69
|
-
for (const
|
|
114
|
+
for (const [key, values] of Object.entries(annotation)) {
|
|
115
|
+
const id = key.includes('.') ? key : 'default.' + key
|
|
116
|
+
|
|
70
117
|
assert.ok(requested.includes(id),
|
|
71
118
|
`Component '${id}' does not request configuration or does not exist.`)
|
|
72
|
-
}
|
|
73
119
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const configuration = overwrite(defaults, values)
|
|
77
|
-
const schema: Schema<any> = schemas.schema(instace.manifest.schema)
|
|
120
|
+
normalized[id] = values
|
|
121
|
+
}
|
|
78
122
|
|
|
79
|
-
|
|
123
|
+
return normalized
|
|
80
124
|
}
|
|
81
125
|
|
|
82
|
-
export const SECRET_RX = /^\$(?<variable>[A-Z0-9_]{1,32})$/
|
|
83
|
-
export const PREFIX = 'TOA_CONFIGURATION_'
|
|
84
|
-
|
|
85
126
|
export type Annotation = Record<string, object>
|
|
86
127
|
export type Instance = context.Dependency<Manifest>
|
|
128
|
+
export type Values = Record<string, Entry>
|
|
129
|
+
|
|
130
|
+
export interface Entry {
|
|
131
|
+
epoch: string
|
|
132
|
+
schema: object
|
|
133
|
+
defaults?: object
|
|
134
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { epoch } from './epoch.js'
|
|
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 = { type: 'object', properties: { foo: { type: 'string', default: 'x' }, bar: { type: 'number' } } }
|
|
12
|
+
const b = { properties: { bar: { type: 'number' }, foo: { default: 'x', type: 'string' } }, type: 'object' }
|
|
13
|
+
|
|
14
|
+
assert.deepStrictEqual(epoch(a), epoch(b))
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('should depend on values', () => {
|
|
18
|
+
const a = { type: 'object', properties: { foo: { type: 'string' } } }
|
|
19
|
+
const b = { type: 'object', properties: { foo: { type: 'number' } } }
|
|
20
|
+
|
|
21
|
+
assert.notDeepStrictEqual(epoch(a), epoch(b))
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should keep array order', () => {
|
|
25
|
+
const a = { enum: [1, 2] }
|
|
26
|
+
const b = { enum: [2, 1] }
|
|
27
|
+
|
|
28
|
+
assert.notDeepStrictEqual(epoch(a), epoch(b))
|
|
29
|
+
})
|
package/source/epoch.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
/** The same for the same schema, whatever the key order it was written in. */
|
|
4
|
+
export function epoch (schema: object): string {
|
|
5
|
+
return createHash('sha256').update(canonical(schema)).digest('hex')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function canonical (value: unknown): string {
|
|
9
|
+
if (Array.isArray(value))
|
|
10
|
+
return '[' + value.map(canonical).join(',') + ']'
|
|
11
|
+
|
|
12
|
+
if (value !== null && typeof value === 'object') {
|
|
13
|
+
const object = value as Record<string, unknown>
|
|
14
|
+
const keys = Object.keys(object).filter((key) => object[key] !== undefined).sort()
|
|
15
|
+
const entries = keys.map((key) => JSON.stringify(key) + ':' + canonical(object[key]))
|
|
16
|
+
|
|
17
|
+
return '{' + entries.join(',') + '}'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return JSON.stringify(value) ?? 'null'
|
|
21
|
+
}
|
package/source/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { manifest } from './manifest'
|
|
2
|
-
export { deployment } from './deployment'
|
|
3
|
-
export {
|
|
1
|
+
export { manifest } from './manifest.js'
|
|
2
|
+
export { deployment, describe } from './deployment.js'
|
|
3
|
+
export { components } from './Composition.js'
|
|
4
|
+
export { Factory } from './Factory.js'
|
package/source/manifest.test.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { type Manifest, manifest } from './manifest.js'
|
|
2
5
|
|
|
3
6
|
it('should validate', async () => {
|
|
4
7
|
const additional = { schema: {}, foo: 'bar' } as unknown as Manifest
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
assert.throws(() => {
|
|
7
10
|
manifest(additional)
|
|
8
|
-
})
|
|
11
|
+
}, (error: any) => /not expected/.test(error.message))
|
|
9
12
|
|
|
10
13
|
const wrongType = { schema: 'not ok' } as unknown as Manifest
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
assert.throws(() => {
|
|
13
16
|
manifest(wrongType)
|
|
14
|
-
}).
|
|
17
|
+
}, (error: any) => /object/.test(error.message))
|
|
15
18
|
})
|
package/source/manifest.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import * as schemas from './schemas'
|
|
2
|
-
import { type Node } from './configuration'
|
|
1
|
+
import * as schemas from './schemas.js'
|
|
2
|
+
import { type Node } from './configuration.js'
|
|
3
3
|
|
|
4
4
|
export function manifest (manifest: Manifest): Manifest {
|
|
5
|
-
if (manifest.schema === undefined) manifest = { schema: manifest }
|
|
6
|
-
|
|
7
5
|
schemas.manifest.validate(manifest)
|
|
8
6
|
|
|
9
7
|
return manifest
|
package/source/schemas.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { resolve } from 'node:path'
|
|
|
2
2
|
import * as schemas from '@toa.io/schemas'
|
|
3
3
|
import type { Schema } from '@toa.io/schemas'
|
|
4
4
|
|
|
5
|
-
const path = resolve(
|
|
5
|
+
const path = resolve(import.meta.dirname, '../schemas')
|
|
6
6
|
const namespace = schemas.namespace(path)
|
|
7
7
|
|
|
8
8
|
export const manifest: Schema<any> = namespace.schema('manifest')
|
package/transpiled/Aspect.d.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
import { Connector, type extensions } from '@toa.io/core';
|
|
1
|
+
import { Connector, type Locator, type extensions } from '@toa.io/core';
|
|
2
|
+
import type { Client } from './Client.js';
|
|
3
|
+
import type { Manifest } from './manifest.js';
|
|
2
4
|
export declare class Aspect extends Connector implements extensions.Aspect {
|
|
3
5
|
readonly name = "configuration";
|
|
4
|
-
private readonly
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
private readonly locator;
|
|
7
|
+
private readonly manifest;
|
|
8
|
+
private readonly client;
|
|
9
|
+
private readonly epoch;
|
|
10
|
+
private value;
|
|
11
|
+
private created;
|
|
12
|
+
/**
|
|
13
|
+
* Without a client the value is local: the variable, the defaults and the schema.
|
|
14
|
+
* With one, the value is what the service holds, and it follows the service.
|
|
15
|
+
*/
|
|
16
|
+
constructor(locator: Locator, manifest: Manifest, client: Client | null);
|
|
17
|
+
invoke(path?: string[]): any;
|
|
18
|
+
protected open(): Promise<void>;
|
|
19
|
+
protected close(): Promise<void>;
|
|
20
|
+
private readonly listener;
|
|
7
21
|
}
|
package/transpiled/Aspect.js
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Aspect extends
|
|
1
|
+
import { console } from 'openspan';
|
|
2
|
+
import { Connector } from '@toa.io/core';
|
|
3
|
+
import { fit, local } from './configuration.js';
|
|
4
|
+
import { epoch } from './epoch.js';
|
|
5
|
+
export class Aspect extends Connector {
|
|
6
6
|
name = 'configuration';
|
|
7
|
-
|
|
8
|
-
|
|
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.
|
|
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,33 @@ 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', { component: this.locator.id, epoch: this.epoch, created });
|
|
43
|
+
}
|
|
44
|
+
async close() {
|
|
45
|
+
this.client?.unsubscribe(this.locator.id, this.epoch, this.listener);
|
|
46
|
+
}
|
|
47
|
+
listener = ({ configuration, created }) => {
|
|
48
|
+
// deliveries may repeat or cross: only what is newer than the held value replaces it
|
|
49
|
+
if (created <= this.created)
|
|
50
|
+
return;
|
|
51
|
+
try {
|
|
52
|
+
this.value = fit(configuration, this.manifest);
|
|
53
|
+
this.created = created;
|
|
54
|
+
console.info('Configuration updated', { component: this.locator.id, epoch: this.epoch, created });
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
// the service validated it against the schema of its epoch, so the two schemas differ
|
|
58
|
+
console.error('Configuration value does not match the schema', { component: this.locator.id, epoch: this.epoch, error });
|
|
59
|
+
}
|
|
60
|
+
};
|
|
19
61
|
}
|
|
20
|
-
exports.Aspect = Aspect;
|
|
21
62
|
//# sourceMappingURL=Aspect.js.map
|
package/transpiled/Aspect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Aspect.js","sourceRoot":"","sources":["../source/Aspect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Aspect.js","sourceRoot":"","sources":["../source/Aspect.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { it, beforeEach, afterEach, mock } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
+
import { Connector, Locator } from '@toa.io/core';
|
|
5
|
+
import { generate } from 'randomstring';
|
|
6
|
+
import { Aspect } from './Aspect.js';
|
|
7
|
+
class Fake extends Connector {
|
|
8
|
+
fetch = mock.fn(async () => ({ configuration: { foo: 'served' }, created: 5 }));
|
|
9
|
+
subscribe = mock.fn();
|
|
10
|
+
unsubscribe = mock.fn();
|
|
11
|
+
}
|
|
12
|
+
const manifest = {
|
|
13
|
+
schema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
foo: { type: 'string' },
|
|
17
|
+
bar: { type: 'object', properties: { baz: { type: 'string' } }, default: { baz: 'quux' } }
|
|
9
18
|
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
let locator;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
locator = new Locator(generate(), generate());
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
delete process.env['TOA_CONFIGURATION_' + locator.uppercase];
|
|
27
|
+
});
|
|
28
|
+
it('should be named', async () => {
|
|
29
|
+
assert.deepStrictEqual(new Aspect(locator, manifest, null).name, 'configuration');
|
|
30
|
+
});
|
|
31
|
+
it('should resolve locally without a client', async () => {
|
|
32
|
+
process.env['TOA_CONFIGURATION_' + locator.uppercase] = JSON.stringify({ foo: 'local' });
|
|
33
|
+
const aspect = new Aspect(locator, manifest, null);
|
|
34
|
+
await aspect.connect();
|
|
35
|
+
assert.deepStrictEqual(aspect.invoke(), { foo: 'local', bar: { baz: 'quux' } });
|
|
36
|
+
assert.deepStrictEqual(aspect.invoke(['foo']), 'local');
|
|
37
|
+
assert.deepStrictEqual(aspect.invoke(['bar', 'baz']), 'quux');
|
|
38
|
+
});
|
|
39
|
+
it('should fetch from the client and follow it', async () => {
|
|
40
|
+
const client = new Fake();
|
|
41
|
+
const aspect = new Aspect(locator, manifest, client);
|
|
42
|
+
await aspect.connect();
|
|
43
|
+
assert.deepStrictEqual(client.connected, true);
|
|
44
|
+
assert.strictEqual(client.fetch.mock.callCount(), 1);
|
|
45
|
+
const [component, epoch] = client.fetch.mock.calls[0].arguments;
|
|
46
|
+
assert.deepStrictEqual(component, locator.id);
|
|
47
|
+
assert.match(epoch, /^[a-f0-9]{64}$/);
|
|
48
|
+
assert.deepStrictEqual(aspect.invoke(), { foo: 'served', bar: { baz: 'quux' } });
|
|
49
|
+
assert.ok(client.subscribe.mock.calls.some((call) => call.arguments.length === 3 && isDeepStrictEqual(call.arguments[0], component) && isDeepStrictEqual(call.arguments[1], epoch) && typeof call.arguments[2] === 'function'));
|
|
50
|
+
const listener = client.subscribe.mock.calls[0].arguments[2];
|
|
51
|
+
listener({ configuration: { foo: 'updated' }, created: 6 });
|
|
52
|
+
assert.deepStrictEqual(aspect.invoke(['foo']), 'updated');
|
|
53
|
+
// what is not newer than the held value is left alone
|
|
54
|
+
listener({ configuration: { foo: 'stale' }, created: 6 });
|
|
55
|
+
listener({ configuration: { foo: 'older' }, created: 4 });
|
|
56
|
+
assert.deepStrictEqual(aspect.invoke(['foo']), 'updated');
|
|
57
|
+
// a value that does not fit keeps the previous one
|
|
58
|
+
listener({ configuration: { foo: { nested: true } }, created: 7 });
|
|
59
|
+
assert.deepStrictEqual(aspect.invoke(['foo']), 'updated');
|
|
60
|
+
// and the one after it still applies
|
|
61
|
+
listener({ configuration: { foo: 'latest' }, created: 8 });
|
|
62
|
+
assert.deepStrictEqual(aspect.invoke(['foo']), 'latest');
|
|
63
|
+
await aspect.disconnect();
|
|
64
|
+
assert.ok(client.unsubscribe.mock.calls.some((call) => call.arguments.length === 3 && isDeepStrictEqual(call.arguments[0], component) && isDeepStrictEqual(call.arguments[1], epoch) && isDeepStrictEqual(call.arguments[2], listener)));
|
|
14
65
|
});
|
|
15
66
|
//# sourceMappingURL=Aspect.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Aspect.test.js","sourceRoot":"","sources":["../source/Aspect.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Aspect.test.js","sourceRoot":"","sources":["../source/Aspect.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -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;
|