@toa.io/extensions.configuration 1.0.0-alpha.29 → 1.0.0-alpha.291
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 +35 -0
- package/components/configuration.values/manifest.toa.yaml +151 -0
- package/components/configuration.values/migrations/0002-indexes.yaml +7 -0
- package/components/configuration.values/operations/create.js +30 -0
- package/components/configuration.values/operations/fetch.js +13 -0
- package/components/configuration.values/operations/get.js +19 -0
- package/components/configuration.values/operations/lib/map.js +22 -0
- package/components/configuration.values/operations/lib/resolve.js +24 -0
- package/components/configuration.values/operations/list.js +11 -0
- package/components/configuration.values/source/create.ts +54 -0
- package/components/configuration.values/source/fetch.ts +26 -0
- package/components/configuration.values/source/get.ts +32 -0
- package/components/configuration.values/source/lib/map.test.ts +42 -0
- package/components/configuration.values/source/lib/map.ts +36 -0
- package/components/configuration.values/source/lib/resolve.ts +57 -0
- package/components/configuration.values/source/list.ts +21 -0
- package/components/configuration.values/tsconfig.json +10 -0
- package/components/configuration.values/tsconfig.tsbuildinfo +1 -0
- package/components/configuration.values/types/index.d.ts +3 -0
- package/components/configuration.values/types/toa.d.ts +68 -0
- package/cucumber.mjs +8 -0
- package/features/site/_app/immutable/asset.js +1 -0
- package/features/site/favicon.ico +0 -0
- package/features/site/index.html +10 -0
- package/features/steps/UI.ts +98 -0
- package/features/steps/tsconfig.json +9 -0
- package/features/ui.feature +93 -0
- package/notes/consistency.md +10 -9
- package/package.json +11 -10
- package/readme.md +165 -51
- package/schemas/annotation.cos.yaml +29 -1
- package/schemas/manifest.cos.yaml +9 -2
- package/source/Aspect.test.ts +114 -10
- package/source/Aspect.ts +76 -8
- package/source/Client.test.ts +125 -0
- package/source/Client.ts +251 -0
- package/source/Composition.ts +57 -0
- package/source/Factory.ts +29 -7
- package/source/Secret.test.ts +32 -0
- package/source/Secret.ts +32 -0
- package/source/UI.ts +213 -0
- package/source/configuration.test.ts +105 -43
- package/source/configuration.ts +36 -21
- package/source/const.ts +30 -0
- package/source/context.ts +13 -0
- package/source/deployment.test.ts +103 -12
- package/source/deployment.ts +105 -31
- package/source/epoch.test.ts +35 -0
- package/source/epoch.ts +22 -0
- package/source/index.ts +10 -3
- package/source/manifest.test.ts +16 -7
- package/source/manifest.ts +3 -5
- package/source/schemas.ts +1 -1
- package/transpiled/Aspect.d.ts +19 -4
- package/transpiled/Aspect.js +62 -9
- package/transpiled/Aspect.js.map +1 -1
- package/transpiled/Client.d.ts +61 -0
- package/transpiled/Client.js +175 -0
- package/transpiled/Client.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 +9 -2
- package/transpiled/Factory.js +20 -9
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Secret.d.ts +15 -0
- package/transpiled/Secret.js +26 -0
- package/transpiled/Secret.js.map +1 -0
- package/transpiled/UI.d.ts +25 -0
- package/transpiled/UI.js +173 -0
- package/transpiled/UI.js.map +1 -0
- package/transpiled/configuration.d.ts +7 -2
- package/transpiled/configuration.js +32 -43
- package/transpiled/configuration.js.map +1 -1
- package/transpiled/const.d.ts +18 -0
- package/transpiled/const.js +22 -0
- package/transpiled/const.js.map +1 -0
- package/transpiled/context.d.ts +4 -0
- package/transpiled/context.js +9 -0
- package/transpiled/context.js.map +1 -0
- package/transpiled/deployment.d.ts +12 -3
- package/transpiled/deployment.js +76 -57
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/epoch.d.ts +2 -0
- package/transpiled/epoch.js +19 -0
- package/transpiled/epoch.js.map +1 -0
- package/transpiled/index.d.ts +6 -3
- package/transpiled/index.js +8 -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/schemas.js +5 -31
- package/transpiled/schemas.js.map +1 -1
- package/tsconfig.json +5 -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/3Kbn_1Vv.js +1 -0
- package/ui/dist/_app/immutable/chunks/BV5cDXGB.js +50 -0
- package/ui/dist/_app/immutable/chunks/B_CH3nFk.js +1 -0
- package/ui/dist/_app/immutable/chunks/Bjy-W4x2.js +81 -0
- package/ui/dist/_app/immutable/chunks/CNZAv2Es.js +8 -0
- package/ui/dist/_app/immutable/chunks/CcD_ISxM.js +3 -0
- package/ui/dist/_app/immutable/chunks/vl8APhvf.js +6 -0
- package/ui/dist/_app/immutable/chunks/xihTtKlq.js +1 -0
- package/ui/dist/_app/immutable/entry/app.CK-db-gz.js +2 -0
- package/ui/dist/_app/immutable/entry/start.Bs7xr9Ne.js +1 -0
- package/ui/dist/_app/immutable/nodes/0.vPwVABRs.js +1 -0
- package/ui/dist/_app/immutable/nodes/1.DS63PME5.js +1 -0
- package/ui/dist/_app/immutable/nodes/2.KRpWYBfz.js +1 -0
- package/ui/dist/_app/immutable/nodes/3.DCxORePS.js +1 -0
- package/ui/dist/_app/immutable/nodes/4.D8lluH3u.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 +57 -0
- package/ui/dist/robots.txt +3 -0
- package/transpiled/Aspect.test.d.ts +0 -1
- package/transpiled/Aspect.test.js +0 -15
- package/transpiled/Aspect.test.js.map +0 -1
- package/transpiled/configuration.test.d.ts +0 -1
- package/transpiled/configuration.test.js +0 -64
- package/transpiled/configuration.test.js.map +0 -1
- package/transpiled/deployment.test.d.ts +0 -1
- package/transpiled/deployment.test.js +0 -18
- package/transpiled/deployment.test.js.map +0 -1
- package/transpiled/manifest.test.d.ts +0 -1
- package/transpiled/manifest.test.js +0 -14
- package/transpiled/manifest.test.js.map +0 -1
- package/transpiled/tsconfig.tsbuildinfo +0 -1
package/transpiled/UI.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as http from 'node:http';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { Connector } from '@toa.io/core';
|
|
5
|
+
import { console } from 'openspan';
|
|
6
|
+
import { UI_PATH } from './const.js';
|
|
7
|
+
/**
|
|
8
|
+
* Serves the configuration UI: the directory `ui` builds, and nothing else.
|
|
9
|
+
*
|
|
10
|
+
* The page is a single-page application whose mount path is baked into the bundle,
|
|
11
|
+
* so this server routes relative to `UI_PATH` — which is also what the ingress
|
|
12
|
+
* forwards. `/configuration/*` is left alone: that is the component's own API.
|
|
13
|
+
*/
|
|
14
|
+
export class UI extends Connector {
|
|
15
|
+
server = http.createServer();
|
|
16
|
+
port;
|
|
17
|
+
root;
|
|
18
|
+
constructor(port, root = SITE) {
|
|
19
|
+
super();
|
|
20
|
+
this.port = port;
|
|
21
|
+
this.root = root;
|
|
22
|
+
this.server.on('request', (request, response) => {
|
|
23
|
+
this.listen(request, response);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async open() {
|
|
27
|
+
/*
|
|
28
|
+
* A taken port is a real error: uniqueness across services is settled at export
|
|
29
|
+
* time, so nothing here has to negotiate for one.
|
|
30
|
+
*/
|
|
31
|
+
await new Promise((resolve, reject) => {
|
|
32
|
+
const failed = (error) => {
|
|
33
|
+
this.server.off('listening', listening);
|
|
34
|
+
reject(error);
|
|
35
|
+
};
|
|
36
|
+
const listening = () => {
|
|
37
|
+
this.server.off('error', failed);
|
|
38
|
+
resolve();
|
|
39
|
+
};
|
|
40
|
+
this.server.once('error', failed);
|
|
41
|
+
this.server.once('listening', listening);
|
|
42
|
+
this.server.listen(this.port);
|
|
43
|
+
});
|
|
44
|
+
if (!isFile(path.join(this.root, 'index.html')))
|
|
45
|
+
console.warn('Configuration UI is not built', { root: this.root });
|
|
46
|
+
console.info('Configuration UI started', { port: this.port, path: UI_PATH + '/' });
|
|
47
|
+
}
|
|
48
|
+
async close() {
|
|
49
|
+
const closed = new Promise((resolve) => this.server.once('close', () => {
|
|
50
|
+
resolve();
|
|
51
|
+
}));
|
|
52
|
+
this.server.close();
|
|
53
|
+
this.server.closeAllConnections();
|
|
54
|
+
await closed;
|
|
55
|
+
}
|
|
56
|
+
listen(request, response) {
|
|
57
|
+
void this.respond(request, response).catch((error) => {
|
|
58
|
+
console.error('Configuration UI failure', { message: error.message });
|
|
59
|
+
if (!response.writableEnded)
|
|
60
|
+
response.writeHead(500).end();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async respond(request, response) {
|
|
64
|
+
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
65
|
+
response.writeHead(405, { allow: 'GET, HEAD' }).end();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const pathname = decode(request.url ?? '/');
|
|
69
|
+
if (pathname === null) {
|
|
70
|
+
response.writeHead(400).end();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// the port is the explorer's alone, so the root is the way in
|
|
74
|
+
if (pathname === '/') {
|
|
75
|
+
response.writeHead(302, { location: UI_PATH + '/' }).end();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const file = this.resolve(pathname);
|
|
79
|
+
if (file === null)
|
|
80
|
+
response.writeHead(404).end();
|
|
81
|
+
else
|
|
82
|
+
await this.send(file, request, response);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The file a request lands on, or `null` when nothing does. A path that exists is
|
|
86
|
+
* served as it is; anything else that could be a route falls back to the page,
|
|
87
|
+
* because the client router — not this server — knows what routes there are.
|
|
88
|
+
*/
|
|
89
|
+
resolve(pathname) {
|
|
90
|
+
if (!pathname.startsWith(UI_PATH))
|
|
91
|
+
return null;
|
|
92
|
+
const relative = pathname.slice(UI_PATH.length);
|
|
93
|
+
if (relative !== '' && !relative.startsWith('/'))
|
|
94
|
+
return null;
|
|
95
|
+
const file = path.join(this.root, relative);
|
|
96
|
+
if (file !== this.root && !file.startsWith(this.root + path.sep))
|
|
97
|
+
return null;
|
|
98
|
+
if (isFile(file))
|
|
99
|
+
return file;
|
|
100
|
+
/*
|
|
101
|
+
* A missing asset is missing, but a route can look like one: `identity.passkeys` is a
|
|
102
|
+
* component, not a file with an extension. What this server would have served is what
|
|
103
|
+
* it knows how to serve, so anything else is a route and falls back to the page — as
|
|
104
|
+
* does anything ending in a slash, which is no name for a file at all.
|
|
105
|
+
*/
|
|
106
|
+
const asset = !relative.endsWith('/') && path.extname(relative) in TYPES;
|
|
107
|
+
return asset ? null : path.join(this.root, 'index.html');
|
|
108
|
+
}
|
|
109
|
+
async send(file, request, response) {
|
|
110
|
+
const stats = await fs.promises.stat(file).catch(() => null);
|
|
111
|
+
if (stats === null) {
|
|
112
|
+
response
|
|
113
|
+
.writeHead(503, { 'content-type': 'text/plain' })
|
|
114
|
+
.end('The configuration UI is not built. Run `npm run build:ui`.\n');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
response.writeHead(200, {
|
|
118
|
+
'content-type': TYPES[path.extname(file)] ?? 'application/octet-stream',
|
|
119
|
+
'content-length': stats.size,
|
|
120
|
+
'cache-control': caching(path.relative(this.root, file))
|
|
121
|
+
});
|
|
122
|
+
if (request.method === 'HEAD')
|
|
123
|
+
response.end();
|
|
124
|
+
else
|
|
125
|
+
fs.createReadStream(file).pipe(response);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function decode(url) {
|
|
129
|
+
try {
|
|
130
|
+
return decodeURIComponent(url.split('?')[0]);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function isFile(file) {
|
|
137
|
+
return fs.existsSync(file) && fs.statSync(file).isFile();
|
|
138
|
+
}
|
|
139
|
+
/** Where `npm run build:ui` puts the page, from both `source` and `transpiled`. */
|
|
140
|
+
const SITE = path.resolve(import.meta.dirname, '..', 'ui', 'dist');
|
|
141
|
+
/** Assets under this prefix carry their build hash in the name. */
|
|
142
|
+
const IMMUTABLE = path.join('_app', 'immutable');
|
|
143
|
+
const FOREVER = 'public, max-age=31536000, immutable';
|
|
144
|
+
/** The icons: named without a hash, so they are re-read, but rarely. */
|
|
145
|
+
const ICONS = new Set(['favicon.ico', 'favicon-96x96.png', 'apple-touch-icon.png']);
|
|
146
|
+
const DAY = 'public, max-age=86400';
|
|
147
|
+
/**
|
|
148
|
+
* How long what is served may be held. The page is never cached — it names the assets,
|
|
149
|
+
* and their names carry the build. An icon is named for what it is rather than for its
|
|
150
|
+
* content, so it is asked about again, but not on every page load.
|
|
151
|
+
*/
|
|
152
|
+
function caching(relative) {
|
|
153
|
+
if (relative.startsWith(IMMUTABLE))
|
|
154
|
+
return FOREVER;
|
|
155
|
+
return ICONS.has(relative) ? DAY : 'no-cache';
|
|
156
|
+
}
|
|
157
|
+
const TYPES = {
|
|
158
|
+
'.css': 'text/css; charset=utf-8',
|
|
159
|
+
'.html': 'text/html; charset=utf-8',
|
|
160
|
+
'.ico': 'image/x-icon',
|
|
161
|
+
'.jpg': 'image/jpeg',
|
|
162
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
163
|
+
'.json': 'application/json; charset=utf-8',
|
|
164
|
+
'.map': 'application/json; charset=utf-8',
|
|
165
|
+
'.png': 'image/png',
|
|
166
|
+
'.svg': 'image/svg+xml',
|
|
167
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
168
|
+
'.webmanifest': 'application/manifest+json',
|
|
169
|
+
'.webp': 'image/webp',
|
|
170
|
+
'.woff': 'font/woff',
|
|
171
|
+
'.woff2': 'font/woff2'
|
|
172
|
+
};
|
|
173
|
+
//# sourceMappingURL=UI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UI.js","sourceRoot":"","sources":["../source/UI.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { type Locator } from '@toa.io/core';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import type { Manifest } from './manifest.js';
|
|
3
|
+
/** The variable is set, so the values service is not consulted. */
|
|
4
|
+
export declare function overridden(locator: Locator): boolean;
|
|
5
|
+
/** The variable, the manifest defaults, then the schema. */
|
|
6
|
+
export declare function local(locator: Locator, manifest: Manifest): Node;
|
|
7
|
+
/** A copy of the value with the schema applied and the secrets substituted. */
|
|
8
|
+
export declare function fit(raw: object, manifest: Manifest): Node;
|
|
4
9
|
export type Node = Record<string, unknown>;
|
|
@@ -1,49 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.get = void 0;
|
|
27
|
-
const generic_1 = require("@toa.io/generic");
|
|
28
|
-
const schemas = __importStar(require("@toa.io/schemas"));
|
|
29
|
-
const deployment_1 = require("./deployment");
|
|
30
|
-
function get(locator, manifest) {
|
|
31
|
-
const values = getConfiguration(locator.uppercase);
|
|
32
|
-
substituteSecrets(values);
|
|
1
|
+
import { add, environment } from '@toa.io/generic';
|
|
2
|
+
import * as schemas from '@toa.io/schemas';
|
|
3
|
+
import { PREFIX, SECRET_RX } from './const.js';
|
|
4
|
+
import { Secret } from './Secret.js';
|
|
5
|
+
/** The variable is set, so the values service is not consulted. */
|
|
6
|
+
export function overridden(locator) {
|
|
7
|
+
return environment.get(PREFIX + locator.uppercase) !== undefined;
|
|
8
|
+
}
|
|
9
|
+
/** The variable, the manifest defaults, then the schema. */
|
|
10
|
+
export function local(locator, manifest) {
|
|
11
|
+
const values = read(locator.uppercase);
|
|
33
12
|
if (manifest.defaults !== undefined)
|
|
34
|
-
|
|
13
|
+
add(values, manifest.defaults);
|
|
14
|
+
return fit(values, manifest);
|
|
15
|
+
}
|
|
16
|
+
/** A copy of the value with the schema applied and the secrets substituted. */
|
|
17
|
+
export function fit(raw, manifest) {
|
|
18
|
+
// a copy of the caller's, and one of this realm: what came over the wire is JSON anyway
|
|
19
|
+
const values = JSON.parse(JSON.stringify(raw));
|
|
20
|
+
// the schema sees the references, which are the strings it declares
|
|
21
|
+
validate(values, manifest);
|
|
22
|
+
substituteSecrets(values);
|
|
23
|
+
return values;
|
|
24
|
+
}
|
|
25
|
+
function validate(values, manifest) {
|
|
35
26
|
const schema = schemas.schema(manifest.schema);
|
|
36
27
|
schema.validate(values);
|
|
37
|
-
return values;
|
|
38
28
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const string = process.env[variable];
|
|
29
|
+
function read(suffix) {
|
|
30
|
+
const variable = PREFIX + suffix;
|
|
31
|
+
const string = environment.get(variable);
|
|
43
32
|
if (string === undefined)
|
|
44
33
|
return {};
|
|
45
34
|
else
|
|
46
|
-
return
|
|
35
|
+
return JSON.parse(string);
|
|
47
36
|
}
|
|
48
37
|
function substituteSecrets(configuration) {
|
|
49
38
|
for (const [key, value] of Object.entries(configuration)) {
|
|
@@ -51,16 +40,16 @@ function substituteSecrets(configuration) {
|
|
|
51
40
|
substituteSecrets(value);
|
|
52
41
|
if (typeof value !== 'string')
|
|
53
42
|
continue;
|
|
54
|
-
const match = value.match(
|
|
43
|
+
const match = value.match(SECRET_RX);
|
|
55
44
|
if (match === null)
|
|
56
45
|
continue;
|
|
57
46
|
const name = match.groups?.variable;
|
|
58
|
-
configuration[key] = getSecret(name);
|
|
47
|
+
configuration[key] = new Secret(getSecret(name));
|
|
59
48
|
}
|
|
60
49
|
}
|
|
61
50
|
function getSecret(name) {
|
|
62
|
-
const variable =
|
|
63
|
-
const value =
|
|
51
|
+
const variable = PREFIX + '_' + name;
|
|
52
|
+
const value = environment.get(variable);
|
|
64
53
|
if (value === undefined)
|
|
65
54
|
throw new Error(`${variable} is not set.`);
|
|
66
55
|
return value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../source/configuration.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../source/configuration.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAIpC,mEAAmE;AACnE,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,OAAO,WAAW,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,SAAS,CAAA;AAClE,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,KAAK,CAAC,OAAgB,EAAE,QAAkB;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAEtC,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAEnE,OAAO,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAC9B,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,QAAkB;IACjD,wFAAwF;IACxF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAS,CAAA;IAEtD,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC1B,iBAAiB,CAAC,MAAM,CAAC,CAAA;IAEzB,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,MAAY,EAAE,QAAkB;IAChD,MAAM,MAAM,GAAgB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAE3D,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AACzB,CAAC;AAED,SAAS,IAAI,CAAC,MAAc;IAC1B,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;IAChC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAExC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;AAChC,CAAC;AAED,SAAS,iBAAiB,CAAC,aAAmB;IAC5C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,iBAAiB,CAAC,KAAa,CAAC,CAAA;QAEjF,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAQ;QAEvC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAEpC,IAAI,KAAK,KAAK,IAAI;YAAE,SAAQ;QAE5B,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAA;QAEnC,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,IAAK,CAAC,CAAC,CAAA;IACnD,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,QAAQ,GAAG,MAAM,GAAG,GAAG,GAAG,IAAI,CAAA;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAEvC,IAAI,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,cAAc,CAAC,CAAA;IAEnE,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Source } from '@toa.io/core/types';
|
|
2
|
+
/** Per-component variables: the local override and the secrets. */
|
|
3
|
+
export declare const PREFIX = "TOA_CONFIGURATION_";
|
|
4
|
+
/** The map of every configured component, on the values service. */
|
|
5
|
+
export declare const VALUES = "TOA_CONFIGURATION_VALUES";
|
|
6
|
+
export declare const SECRET_RX: RegExp;
|
|
7
|
+
/** Where the UI is mounted; `/configuration/*` belongs to the component's own API. */
|
|
8
|
+
export declare const UI_PATH = "/.configuration";
|
|
9
|
+
export declare const UI_PORT = 8003;
|
|
10
|
+
/**
|
|
11
|
+
* The port the UI is served on. `UI_PORT` is what a deployment publishes and what the chart
|
|
12
|
+
* renders; the variable is for a machine that runs more than one Toa process — a Toa checkout
|
|
13
|
+
* beside an application, or two applications — where one of them has to give it up.
|
|
14
|
+
*/
|
|
15
|
+
export declare const UI_PORT_ENV = "TOA_CONFIGURATION_UI_PORT";
|
|
16
|
+
export declare function uiPort(): number;
|
|
17
|
+
export declare const EVENT = "configuration.values.created";
|
|
18
|
+
export declare const SOURCE: Source;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { environment } from '@toa.io/generic';
|
|
2
|
+
/** Per-component variables: the local override and the secrets. */
|
|
3
|
+
export const PREFIX = 'TOA_CONFIGURATION_';
|
|
4
|
+
/** The map of every configured component, on the values service. */
|
|
5
|
+
export const VALUES = 'TOA_CONFIGURATION_VALUES';
|
|
6
|
+
export const SECRET_RX = /^\$(?<variable>[A-Z0-9_]{1,32})$/;
|
|
7
|
+
/** Where the UI is mounted; `/configuration/*` belongs to the component's own API. */
|
|
8
|
+
export const UI_PATH = '/.configuration';
|
|
9
|
+
export const UI_PORT = 8003;
|
|
10
|
+
/**
|
|
11
|
+
* The port the UI is served on. `UI_PORT` is what a deployment publishes and what the chart
|
|
12
|
+
* renders; the variable is for a machine that runs more than one Toa process — a Toa checkout
|
|
13
|
+
* beside an application, or two applications — where one of them has to give it up.
|
|
14
|
+
*/
|
|
15
|
+
export const UI_PORT_ENV = 'TOA_CONFIGURATION_UI_PORT';
|
|
16
|
+
export function uiPort() {
|
|
17
|
+
const value = environment.get(UI_PORT_ENV);
|
|
18
|
+
return value === undefined ? UI_PORT : Number(value);
|
|
19
|
+
}
|
|
20
|
+
export const EVENT = 'configuration.values.created';
|
|
21
|
+
export const SOURCE = { service: 'configuration' };
|
|
22
|
+
//# sourceMappingURL=const.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"const.js","sourceRoot":"","sources":["../source/const.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,mEAAmE;AACnE,MAAM,CAAC,MAAM,MAAM,GAAG,oBAAoB,CAAA;AAE1C,oEAAoE;AACpE,MAAM,CAAC,MAAM,MAAM,GAAG,0BAA0B,CAAA;AAEhD,MAAM,CAAC,MAAM,SAAS,GAAG,kCAAkC,CAAA;AAE3D,sFAAsF;AACtF,MAAM,CAAC,MAAM,OAAO,GAAG,iBAAiB,CAAA;AACxC,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAA;AAE3B;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,2BAA2B,CAAA;AACtD,MAAM,UAAU,MAAM;IACpB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAE1C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,8BAA8B,CAAA;AAEnD,MAAM,CAAC,MAAM,MAAM,GAAW,EAAE,OAAO,EAAE,eAAe,EAAE,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** What this extension puts on the context of a component that declares it. */
|
|
2
|
+
export function context(declaration) {
|
|
3
|
+
const contribution = { name: 'configuration', schema: declaration.schema };
|
|
4
|
+
// a value the schema declares a secret is one on the context, not a string
|
|
5
|
+
if (JSON.stringify(declaration.schema).includes('"secret"'))
|
|
6
|
+
contribution.imports = { '@toa.io/extensions.configuration': ['Secret'] };
|
|
7
|
+
return contribution;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../source/context.ts"],"names":[],"mappings":"AAGA,+EAA+E;AAC/E,MAAM,UAAU,OAAO,CAAC,WAAqB;IAC3C,MAAM,YAAY,GAAiB,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAA;IAExF,2EAA2E;IAC3E,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;QACzD,YAAY,CAAC,OAAO,GAAG,EAAE,kCAAkC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAA;IAE3E,OAAO,YAAY,CAAA;AACrB,CAAC"}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { type Dependency } from '@toa.io/operations';
|
|
2
|
-
import {
|
|
2
|
+
import type { Manifest } from './manifest.js';
|
|
3
3
|
import type { context } from '@toa.io/norm';
|
|
4
|
+
/** Where Toa's release publishes this service's image. An application takes it
|
|
5
|
+
* instead of building one when its context says `registry.services: published`. */
|
|
6
|
+
export declare const image = "ghcr.io/toa-io/extension-configuration-values";
|
|
4
7
|
export declare function deployment(instances: Instance[], annotation?: Annotation): Dependency;
|
|
5
|
-
|
|
6
|
-
export declare
|
|
8
|
+
/** What the values service is given: the epoch, the schema and the defaults of every component. */
|
|
9
|
+
export declare function describe(instances: Instance[], annotation?: Annotation): Values;
|
|
7
10
|
export type Annotation = Record<string, object>;
|
|
8
11
|
export type Instance = context.Dependency<Manifest>;
|
|
12
|
+
export type Values = Record<string, Entry>;
|
|
13
|
+
export interface Entry {
|
|
14
|
+
epoch: string;
|
|
15
|
+
schema: object;
|
|
16
|
+
defaults?: object;
|
|
17
|
+
}
|
package/transpiled/deployment.js
CHANGED
|
@@ -1,54 +1,63 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.PREFIX = exports.SECRET_RX = exports.deployment = void 0;
|
|
30
|
-
const node_assert_1 = __importDefault(require("node:assert"));
|
|
31
|
-
const schemas = __importStar(require("@toa.io/schemas"));
|
|
32
|
-
const generic_1 = require("@toa.io/generic");
|
|
33
|
-
const validators = __importStar(require("./schemas"));
|
|
34
|
-
function deployment(instances, annotation = {}) {
|
|
35
|
-
validate(annotation, instances);
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { add } from '@toa.io/generic';
|
|
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
|
+
/** Where Toa's release publishes this service's image. An application takes it
|
|
9
|
+
* instead of building one when its context says `registry.services: published`. */
|
|
10
|
+
export const image = 'ghcr.io/toa-io/extension-configuration-values';
|
|
11
|
+
export function deployment(instances, annotation = {}) {
|
|
12
|
+
const { resources, values } = split(annotation);
|
|
13
|
+
annotation = prepare(values, instances);
|
|
36
14
|
const variables = {};
|
|
37
15
|
for (const instance of instances) {
|
|
38
16
|
const values = annotation[instance.locator.id];
|
|
39
|
-
validateInstance(instance, values);
|
|
40
17
|
if (values === undefined)
|
|
41
18
|
continue;
|
|
42
|
-
variables[instance.locator.label] = [{
|
|
43
|
-
name: exports.PREFIX + instance.locator.uppercase,
|
|
44
|
-
value: (0, generic_1.encode)(values)
|
|
45
|
-
}];
|
|
46
19
|
const secrets = createSecrets(values);
|
|
47
|
-
|
|
20
|
+
if (secrets.length > 0)
|
|
21
|
+
variables[instance.locator.label] = secrets;
|
|
22
|
+
}
|
|
23
|
+
const service = {
|
|
24
|
+
group: 'configuration',
|
|
25
|
+
name: 'values',
|
|
26
|
+
image,
|
|
27
|
+
version: JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
28
|
+
.version,
|
|
29
|
+
components: components().labels,
|
|
30
|
+
resources,
|
|
31
|
+
// the service that holds the values also serves the page that reads them
|
|
32
|
+
port: UI_PORT,
|
|
33
|
+
ingress: { path: UI_PATH },
|
|
34
|
+
variables: [
|
|
35
|
+
{
|
|
36
|
+
name: VALUES,
|
|
37
|
+
value: JSON.stringify(describe(instances, annotation))
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
};
|
|
41
|
+
return { services: [service], variables, events: [EVENT] };
|
|
42
|
+
}
|
|
43
|
+
/** What the values service is given: the epoch, the schema and the defaults of every component. */
|
|
44
|
+
export function describe(instances, annotation = {}) {
|
|
45
|
+
annotation = prepare(split(annotation).values, instances);
|
|
46
|
+
const values = {};
|
|
47
|
+
for (const { locator, manifest } of instances) {
|
|
48
|
+
const deployed = annotation[locator.id];
|
|
49
|
+
values[locator.id] = {
|
|
50
|
+
epoch: epoch(manifest.schema),
|
|
51
|
+
schema: manifest.schema,
|
|
52
|
+
// what the Context says stands over what the manifest declares, value by value: a
|
|
53
|
+
// deployment that names one of them does not thereby unset the rest
|
|
54
|
+
defaults: deployed === undefined
|
|
55
|
+
? manifest.defaults
|
|
56
|
+
: add(structuredClone(deployed), manifest.defaults ?? {})
|
|
57
|
+
};
|
|
48
58
|
}
|
|
49
|
-
return
|
|
59
|
+
return values;
|
|
50
60
|
}
|
|
51
|
-
exports.deployment = deployment;
|
|
52
61
|
function createSecrets(values) {
|
|
53
62
|
const secrets = [];
|
|
54
63
|
for (const value of Object.values(values)) {
|
|
@@ -56,13 +65,13 @@ function createSecrets(values) {
|
|
|
56
65
|
secrets.push(...createSecrets(value));
|
|
57
66
|
if (typeof value !== 'string')
|
|
58
67
|
continue;
|
|
59
|
-
const match = value.match(
|
|
68
|
+
const match = value.match(SECRET_RX);
|
|
60
69
|
if (match === null)
|
|
61
70
|
continue;
|
|
62
71
|
const name = match.groups?.variable;
|
|
63
|
-
|
|
72
|
+
assert.ok(name !== undefined);
|
|
64
73
|
secrets.push({
|
|
65
|
-
name:
|
|
74
|
+
name: PREFIX + '_' + name,
|
|
66
75
|
secret: {
|
|
67
76
|
name: 'toa-configuration',
|
|
68
77
|
key: name
|
|
@@ -71,18 +80,28 @@ function createSecrets(values) {
|
|
|
71
80
|
}
|
|
72
81
|
return secrets;
|
|
73
82
|
}
|
|
74
|
-
|
|
83
|
+
/**
|
|
84
|
+
* The service's own resources, and the component values that are the rest of the annotation.
|
|
85
|
+
*
|
|
86
|
+
* Every key here names a component, so the one option the service has of its own needs a
|
|
87
|
+
* name that cannot be mistaken for one. `resources` is reserved: a component actually
|
|
88
|
+
* called that is written with its namespace, `default.resources`, which is what an id is
|
|
89
|
+
* anyway — the bare form is the shorthand.
|
|
90
|
+
*/
|
|
91
|
+
function split(annotation) {
|
|
75
92
|
validators.annotation.validate(annotation);
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
node_assert_1.default.ok(requested.includes(id), `Component '${id}' does not request configuration or does not exist.`);
|
|
93
|
+
const { resources, ...values } = annotation;
|
|
94
|
+
return { resources, values };
|
|
79
95
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
96
|
+
/** Validated, keyed by full component ids, and checked against the components that ask. */
|
|
97
|
+
function prepare(annotation, instances) {
|
|
98
|
+
const normalized = {};
|
|
99
|
+
const requested = instances.map((instance) => instance.locator.id);
|
|
100
|
+
for (const [key, values] of Object.entries(annotation)) {
|
|
101
|
+
const id = key.includes('.') ? key : 'default.' + key;
|
|
102
|
+
assert.ok(requested.includes(id), `Component '${id}' does not request configuration or does not exist.`);
|
|
103
|
+
normalized[id] = values;
|
|
104
|
+
}
|
|
105
|
+
return normalized;
|
|
85
106
|
}
|
|
86
|
-
exports.SECRET_RX = /^\$(?<variable>[A-Z0-9_]{1,32})$/;
|
|
87
|
-
exports.PREFIX = 'TOA_CONFIGURATION_';
|
|
88
107
|
//# sourceMappingURL=deployment.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment.js","sourceRoot":"","sources":["../source/deployment.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deployment.js","sourceRoot":"","sources":["../source/deployment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/** The same for the same schema, whatever the key order it was written in. */
|
|
3
|
+
export function epoch(schema) {
|
|
4
|
+
return createHash('sha256').update(canonical(schema)).digest('hex');
|
|
5
|
+
}
|
|
6
|
+
function canonical(value) {
|
|
7
|
+
if (Array.isArray(value))
|
|
8
|
+
return '[' + value.map(canonical).join(',') + ']';
|
|
9
|
+
if (value !== null && typeof value === 'object') {
|
|
10
|
+
const object = value;
|
|
11
|
+
const keys = Object.keys(object)
|
|
12
|
+
.filter((key) => object[key] !== undefined)
|
|
13
|
+
.sort();
|
|
14
|
+
const entries = keys.map((key) => JSON.stringify(key) + ':' + canonical(object[key]));
|
|
15
|
+
return '{' + entries.join(',') + '}';
|
|
16
|
+
}
|
|
17
|
+
return JSON.stringify(value) ?? 'null';
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=epoch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epoch.js","sourceRoot":"","sources":["../source/epoch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,8EAA8E;AAC9E,MAAM,UAAU,KAAK,CAAC,MAAc;IAClC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACrE,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IAE3E,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,KAAgC,CAAA;QAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;aAC7B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;aAC1C,IAAI,EAAE,CAAA;QACT,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QAErF,OAAO,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IACtC,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAA;AACxC,CAAC"}
|
package/transpiled/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export { manifest } from './manifest';
|
|
2
|
-
export { deployment } from './deployment';
|
|
3
|
-
export {
|
|
1
|
+
export { manifest } from './manifest.js';
|
|
2
|
+
export { deployment, describe, image } from './deployment.js';
|
|
3
|
+
export { components } from './Composition.js';
|
|
4
|
+
export { Factory } from './Factory.js';
|
|
5
|
+
export { Secret } from './Secret.js';
|
|
6
|
+
export { context } from './context.js';
|