@toa.io/boot 1.0.0-alpha.296 → 1.0.0-alpha.298
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 +16 -0
- package/package.json +4 -4
- package/src/manifest.js +56 -7
- package/src/manifest.test.js +39 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.0.0-alpha.298](https://github.com/toa-io/toa/compare/v1.0.0-alpha.297...v1.0.0-alpha.298) (2026-09-08)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @toa.io/boot
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [1.0.0-alpha.297](https://github.com/toa-io/toa/compare/v1.0.0-alpha.296...v1.0.0-alpha.297) (2026-09-08)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @toa.io/boot
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
# [1.0.0-alpha.296](https://github.com/toa-io/toa/compare/v1.0.0-alpha.295...v1.0.0-alpha.296) (2026-09-08)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @toa.io/boot
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/boot",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.298",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Toa Boot",
|
|
6
6
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@toa.io/atomicity": "1.0.0-alpha.293",
|
|
25
25
|
"@toa.io/core": "1.0.0-alpha.293",
|
|
26
|
-
"@toa.io/definitions": "1.0.0-alpha.
|
|
26
|
+
"@toa.io/definitions": "1.0.0-alpha.298",
|
|
27
27
|
"@toa.io/generic": "1.0.0-alpha.292",
|
|
28
|
-
"@toa.io/norm": "1.0.0-alpha.
|
|
28
|
+
"@toa.io/norm": "1.0.0-alpha.298",
|
|
29
29
|
"@toa.io/schemas": "1.0.0-alpha.293",
|
|
30
30
|
"clone-deep": "4.0.1",
|
|
31
31
|
"dotenv": "17.4.2",
|
|
32
32
|
"openspan": "1.0.0-alpha.293"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "334fad8726adfa7507462c33b509ce7db9db853f"
|
|
35
35
|
}
|
package/src/manifest.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import clone from 'clone-deep'
|
|
2
|
-
import { existsSync } from 'node:fs'
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
3
3
|
import { readFile } from 'node:fs/promises'
|
|
4
|
-
import { basename, join } from 'node:path'
|
|
4
|
+
import { basename, dirname, join } from 'node:path'
|
|
5
5
|
import { merge } from '@toa.io/generic'
|
|
6
|
-
import { component as load, revive, NORMALIZED } from '@toa.io/norm'
|
|
6
|
+
import { component as load, definition, revive, NORMALIZED } from '@toa.io/norm'
|
|
7
7
|
import { Locator } from '@toa.io/core'
|
|
8
8
|
|
|
9
9
|
import { span } from './span.js'
|
|
@@ -33,17 +33,66 @@ export const manifest = async (path, options = {}) => {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* A manifest already normalised, where there is one, and the sources read where there is not.
|
|
37
|
+
*
|
|
38
|
+
* A component of an application's own comes with the manifest its build wrote beside it. A
|
|
39
|
+
* component an extension ships comes with none — its package is published once, long before
|
|
40
|
+
* any image is built — but `@toa.io/definitions` carries the digest of those manifests, which
|
|
41
|
+
* a deploy has read for as long as it has been unable to install the extension. A service runs
|
|
42
|
+
* them from the package, so it reads the same digest rather than reading their sources again.
|
|
43
|
+
*
|
|
44
|
+
* Only a workspace normalises, then, which is the one place a source can have changed since.
|
|
38
45
|
*
|
|
39
46
|
* @param {string} path
|
|
40
47
|
*/
|
|
41
48
|
async function read(path) {
|
|
42
49
|
const file = join(path, NORMALIZED)
|
|
43
50
|
|
|
44
|
-
if (
|
|
51
|
+
if (existsSync(file)) return revive(JSON.parse(await readFile(file, 'utf8')), path)
|
|
45
52
|
|
|
46
|
-
return
|
|
53
|
+
return (await digested(path)) ?? (await load(path))
|
|
47
54
|
}
|
|
48
55
|
|
|
56
|
+
/**
|
|
57
|
+
* What the digest holds for a component of an extension's own, by the directory it is in.
|
|
58
|
+
*
|
|
59
|
+
* An extension ships its components in `components/` beside its manifest and nowhere else, so
|
|
60
|
+
* that is the whole of what is looked for: anything else — an application's own component, a
|
|
61
|
+
* fixture in a workspace — is read from its sources.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} path
|
|
64
|
+
* @returns {Promise<object | undefined>}
|
|
65
|
+
*/
|
|
66
|
+
async function digested(path) {
|
|
67
|
+
const directory = dirname(path)
|
|
68
|
+
|
|
69
|
+
if (basename(directory) !== COMPONENTS) return undefined
|
|
70
|
+
|
|
71
|
+
const root = dirname(directory)
|
|
72
|
+
const manifest = join(root, MANIFEST)
|
|
73
|
+
|
|
74
|
+
if (!existsSync(manifest)) return undefined
|
|
75
|
+
|
|
76
|
+
const { name } = JSON.parse(readFileSync(manifest, 'utf8'))
|
|
77
|
+
|
|
78
|
+
// the digest is of what Toa ships, and a package it does not define has none
|
|
79
|
+
if (name === undefined || !name.startsWith(SCOPE)) return undefined
|
|
80
|
+
|
|
81
|
+
const { module } = await definition(name)
|
|
82
|
+
const { labels, manifests } = module.components?.() ?? {}
|
|
83
|
+
|
|
84
|
+
if (manifests === undefined) return undefined
|
|
85
|
+
|
|
86
|
+
// a chart names a component's workload the way the digest labels it
|
|
87
|
+
const index = labels.indexOf(basename(path).replace('.', '-'))
|
|
88
|
+
|
|
89
|
+
return index === -1 ? undefined : revive(manifests[index], path)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const SCOPE = '@toa.io/'
|
|
93
|
+
|
|
94
|
+
const COMPONENTS = 'components'
|
|
95
|
+
|
|
96
|
+
const MANIFEST = 'package.json'
|
|
97
|
+
|
|
49
98
|
const DEFAULTS = {}
|
package/src/manifest.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { it, mock } from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
3
|
|
|
4
|
-
import { mkdtemp, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { mkdtemp, mkdir, writeFile } from 'node:fs/promises'
|
|
5
5
|
import { join } from 'node:path'
|
|
6
6
|
import { tmpdir } from 'node:os'
|
|
7
7
|
import { generate } from 'randomstring'
|
|
@@ -10,6 +10,8 @@ const NORMALIZED = 'manifest.toa.json'
|
|
|
10
10
|
|
|
11
11
|
let normalised = 0
|
|
12
12
|
|
|
13
|
+
const digest = { labels: ['identity-basic'], manifests: [{ name: 'basic', namespace: 'identity' }] }
|
|
14
|
+
|
|
13
15
|
mock.module('@toa.io/norm', {
|
|
14
16
|
namedExports: {
|
|
15
17
|
component: () => {
|
|
@@ -17,7 +19,9 @@ mock.module('@toa.io/norm', {
|
|
|
17
19
|
|
|
18
20
|
return mockComponent()
|
|
19
21
|
},
|
|
20
|
-
definition: async () => ({
|
|
22
|
+
definition: async (reference) => ({
|
|
23
|
+
module: reference === '@toa.io/extensions.exposition' ? { components: () => digest } : {}
|
|
24
|
+
}),
|
|
21
25
|
revive: (declared, path) => ({ ...declared, path }),
|
|
22
26
|
NORMALIZED
|
|
23
27
|
}
|
|
@@ -58,6 +62,39 @@ it('should read what the build wrote, and not normalize', async () => {
|
|
|
58
62
|
assert.strictEqual(read.locator.id, 'todos.tasks')
|
|
59
63
|
})
|
|
60
64
|
|
|
65
|
+
it('should read the digest for a component an extension ships, and not normalize', async () => {
|
|
66
|
+
const root = await mkdtemp(join(tmpdir(), 'toa-manifest-test'))
|
|
67
|
+
const component = join(root, 'components', 'identity.basic')
|
|
68
|
+
|
|
69
|
+
await mkdir(component, { recursive: true })
|
|
70
|
+
await writeFile(
|
|
71
|
+
join(root, 'package.json'),
|
|
72
|
+
JSON.stringify({ name: '@toa.io/extensions.exposition' })
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const before = normalised
|
|
76
|
+
const read = await manifest(component)
|
|
77
|
+
|
|
78
|
+
assert.strictEqual(normalised, before, 'the component was normalized')
|
|
79
|
+
assert.strictEqual(read.path, component)
|
|
80
|
+
assert.strictEqual(read.locator.id, 'identity.basic')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should normalize a component that only looks like one an extension ships', async () => {
|
|
84
|
+
const root = await mkdtemp(join(tmpdir(), 'toa-manifest-test'))
|
|
85
|
+
const component = join(root, 'components', 'identity.basic')
|
|
86
|
+
|
|
87
|
+
await mkdir(component, { recursive: true })
|
|
88
|
+
// an application's own package, which the digest knows nothing about
|
|
89
|
+
await writeFile(join(root, 'package.json'), JSON.stringify({ name: 'acme' }))
|
|
90
|
+
|
|
91
|
+
const before = normalised
|
|
92
|
+
|
|
93
|
+
await manifest(component)
|
|
94
|
+
|
|
95
|
+
assert.strictEqual(normalised, before + 1)
|
|
96
|
+
})
|
|
97
|
+
|
|
61
98
|
function mockComponent() {
|
|
62
99
|
return { name: generate(), namespace: generate() }
|
|
63
100
|
}
|