@arki/dot 0.3.0 → 0.4.0
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/README.md +86 -86
- package/dist/cli/discover.js +2 -2
- package/dist/cli/discover.js.map +1 -1
- package/dist/cli/error-codes.d.ts +8 -0
- package/dist/cli/error-codes.d.ts.map +1 -1
- package/dist/cli/error-codes.js +12 -0
- package/dist/cli/error-codes.js.map +1 -1
- package/dist/cli/index.d.ts +9 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +54 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/new.d.ts +1 -1
- package/dist/cli/new.js +1 -1
- package/dist/cli/render-doctor.d.ts +1 -1
- package/dist/cli/render-doctor.js +23 -23
- package/dist/cli/render-doctor.js.map +1 -1
- package/dist/cli/render-explain.d.ts.map +1 -1
- package/dist/cli/render-explain.js +33 -19
- package/dist/cli/render-explain.js.map +1 -1
- package/dist/cli/render-graph.d.ts +3 -3
- package/dist/cli/render-graph.js +12 -12
- package/dist/cli/render-graph.js.map +1 -1
- package/dist/cli/render-projection.d.ts +29 -0
- package/dist/cli/render-projection.d.ts.map +1 -0
- package/dist/cli/render-projection.js +202 -0
- package/dist/cli/render-projection.js.map +1 -0
- package/dist/define-app.d.ts +49 -17
- package/dist/define-app.d.ts.map +1 -1
- package/dist/define-app.js +16 -9
- package/dist/define-app.js.map +1 -1
- package/dist/diagnostics.d.ts +19 -15
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +2 -2
- package/dist/index.d.ts +15 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/init-pips.d.ts +45 -0
- package/dist/init-pips.d.ts.map +1 -0
- package/dist/init-pips.js +49 -0
- package/dist/init-pips.js.map +1 -0
- package/dist/init-plugins.d.ts +45 -0
- package/dist/init-plugins.d.ts.map +1 -0
- package/dist/init-plugins.js +49 -0
- package/dist/init-plugins.js.map +1 -0
- package/dist/kernel/app-instance.d.ts +5 -5
- package/dist/kernel/app-instance.d.ts.map +1 -1
- package/dist/kernel/app-instance.js +375 -244
- package/dist/kernel/app-instance.js.map +1 -1
- package/dist/kernel/otel.d.ts +19 -19
- package/dist/kernel/otel.d.ts.map +1 -1
- package/dist/kernel/otel.js +22 -22
- package/dist/kernel/otel.js.map +1 -1
- package/dist/lifecycle-observer.d.ts +10 -10
- package/dist/lifecycle-observer.d.ts.map +1 -1
- package/dist/lifecycle-observer.js +1 -1
- package/dist/lifecycle.d.ts +18 -14
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +13 -9
- package/dist/lifecycle.js.map +1 -1
- package/dist/manifest.d.ts +70 -48
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +74 -5
- package/dist/manifest.js.map +1 -1
- package/dist/pip-contract.d.ts +29 -21
- package/dist/pip-contract.d.ts.map +1 -1
- package/dist/pip-contract.js +1 -0
- package/dist/pip-contract.js.map +1 -1
- package/dist/pip.d.ts +7 -4
- package/dist/pip.d.ts.map +1 -1
- package/dist/pip.js +2 -0
- package/dist/pip.js.map +1 -1
- package/dist/plugin-contract.d.ts +335 -143
- package/dist/plugin-contract.d.ts.map +1 -1
- package/dist/plugin-contract.js +201 -36
- package/dist/plugin-contract.js.map +1 -1
- package/dist/plugin.d.ts +18 -15
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +11 -11
- package/dist/plugin.js.map +1 -1
- package/dist/test-harness.d.ts +31 -31
- package/dist/test-harness.d.ts.map +1 -1
- package/dist/test-harness.js +29 -28
- package/dist/test-harness.js.map +1 -1
- package/dist/timeline.d.ts +2 -2
- package/dist/timeline.js +5 -5
- package/dist/timeline.js.map +1 -1
- package/package.json +9 -9
- package/src/cli/discover.ts +2 -2
- package/src/cli/error-codes.ts +12 -0
- package/src/cli/index.ts +76 -29
- package/src/cli/new.ts +1 -1
- package/src/cli/render-doctor.ts +21 -21
- package/src/cli/render-explain.ts +37 -19
- package/src/cli/render-graph.ts +12 -12
- package/src/cli/render-projection.ts +239 -0
- package/src/define-app.ts +85 -22
- package/src/diagnostics.ts +19 -15
- package/src/index.ts +37 -20
- package/src/init-plugins.ts +142 -0
- package/src/kernel/app-instance.ts +428 -282
- package/src/kernel/otel.ts +27 -27
- package/src/lifecycle-observer.ts +10 -10
- package/src/lifecycle.ts +19 -15
- package/src/manifest.ts +128 -50
- package/src/{pip-contract.ts → plugin-contract.ts} +73 -62
- package/src/{pip.ts → plugin.ts} +37 -18
- package/src/test-harness.ts +52 -51
- package/src/timeline.ts +6 -6
- package/templates/app-minimal/AGENTS.md.tmpl +9 -9
- package/templates/app-minimal/README.md.tmpl +1 -1
- package/templates/app-minimal/tests/boot.test.ts.tmpl +1 -1
- package/src/cli/render-openapi.ts +0 -147
package/dist/test-harness.js
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Test harness for unit-testing DOT
|
|
2
|
+
* Test harness for unit-testing DOT plugins.
|
|
3
3
|
*
|
|
4
|
-
* Provides `testApp` — a convenience wrapper that lets
|
|
4
|
+
* Provides `testApp` — a convenience wrapper that lets plugin authors verify
|
|
5
5
|
* lifecycle behaviour, registration, and service publishing without dragging
|
|
6
6
|
* in concrete framework dependencies.
|
|
7
7
|
*
|
|
8
|
-
* NOTE: `testApp` takes an erased
|
|
8
|
+
* NOTE: `testApp` takes an erased plugin array, so the compile-time wiring
|
|
9
9
|
* guard does not apply here — the kernel's runtime validation (unsatisfied
|
|
10
10
|
* needs, collisions) still does. Use `defineApp(...).use(...)` chains in
|
|
11
11
|
* tests that should exercise the type-level guard.
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
|
-
* import { testApp,
|
|
14
|
+
* import { testApp, plugin } from '@arki/dot';
|
|
15
15
|
*
|
|
16
|
-
* const
|
|
16
|
+
* const myPlugin = plugin({
|
|
17
17
|
* name: 'counter',
|
|
18
18
|
* boot: () => ({ counter: { value: 0 } }),
|
|
19
19
|
* });
|
|
20
20
|
*
|
|
21
21
|
* it('publishes a counter service', async () => {
|
|
22
|
-
* const app = await bootTestApp<{ counter: { value: number } }>([
|
|
22
|
+
* const app = await bootTestApp<{ counter: { value: number } }>([myPlugin]);
|
|
23
23
|
* expect(app.services.counter.value).toBe(0);
|
|
24
24
|
* await app.dispose();
|
|
25
25
|
* });
|
|
26
26
|
*/
|
|
27
27
|
import { defineApp } from './define-app.js';
|
|
28
28
|
/**
|
|
29
|
-
* Build a DOT app builder pre-populated with the given
|
|
29
|
+
* Build a DOT app builder pre-populated with the given plugins, ready to
|
|
30
30
|
* `.configure()`, `.boot()` or `.start()` from a test.
|
|
31
31
|
*/
|
|
32
|
-
export function testApp(
|
|
32
|
+
export function testApp(plugins = [], options = {}) {
|
|
33
33
|
let builder = defineApp(options.name ?? 'test-app', { config: options.config });
|
|
34
|
-
for (const p of
|
|
34
|
+
for (const p of plugins) {
|
|
35
35
|
builder = builder.use(p);
|
|
36
36
|
}
|
|
37
37
|
return builder;
|
|
@@ -40,22 +40,22 @@ export function testApp(pips = [], options = {}) {
|
|
|
40
40
|
* Convenience: build, boot, return the running app. Caller is responsible for
|
|
41
41
|
* calling `app.dispose()` when finished.
|
|
42
42
|
*/
|
|
43
|
-
export async function bootTestApp(
|
|
44
|
-
return testApp(
|
|
43
|
+
export async function bootTestApp(plugins = [], options = {}) {
|
|
44
|
+
return testApp(plugins, options).boot();
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
|
-
* Unit-test a single
|
|
47
|
+
* Unit-test a single plugin with **typed overrides** — the compile-checked
|
|
48
48
|
* counterpart to {@link testApp}'s erased arrays.
|
|
49
49
|
*
|
|
50
|
-
* Each `.provide()` satisfies one of the
|
|
51
|
-
* provider
|
|
50
|
+
* Each `.provide()` satisfies one of the plugin's needs directly (no real
|
|
51
|
+
* provider plugin, no dependency chain) and removes it from the builder's
|
|
52
52
|
* remaining-needs type. `boot()` only compiles once every need is covered,
|
|
53
53
|
* and a fake of the wrong shape fails at the `.provide()` call site:
|
|
54
54
|
*
|
|
55
55
|
* ```ts
|
|
56
|
-
* import {
|
|
56
|
+
* import { testPlugin } from '@arki/dot/test-harness';
|
|
57
57
|
*
|
|
58
|
-
* const app = await
|
|
58
|
+
* const app = await testPlugin(catalog)
|
|
59
59
|
* .provide(Db, fakeDb) // token need
|
|
60
60
|
* .provide('cache', fakeKv) // anonymous need — wire key is the alias
|
|
61
61
|
* .boot();
|
|
@@ -67,41 +67,42 @@ export async function bootTestApp(pips = [], options = {}) {
|
|
|
67
67
|
* `service.lazy<T>()` needs accept either a plain `T` fake (the kernel
|
|
68
68
|
* lifts it) or a `Lazy<T>` handle (`lazyOf(value)` is handy here).
|
|
69
69
|
* Lifecycle semantics are the real kernel's — the fakes are published by a
|
|
70
|
-
* synthetic first
|
|
70
|
+
* synthetic first plugin, so reverse-order teardown and lazy auto-dispose
|
|
71
71
|
* behave exactly as in production.
|
|
72
72
|
*/
|
|
73
|
-
export function
|
|
73
|
+
export function testPlugin(plugin, options = {}) {
|
|
74
74
|
// Erasure seam — same boundary as `makeBuilder` in define-app.ts: the
|
|
75
75
|
// runtime impl is untyped, the type parameters do all the guarding.
|
|
76
|
-
return
|
|
76
|
+
return makeTestPluginBuilder({ plugin: plugin, fakes: {}, options });
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
|
-
* Erased implementation behind {@link
|
|
79
|
+
* Erased implementation behind {@link testPlugin}. Mirrors `makeBuilder` in
|
|
80
80
|
* `define-app.ts`: the runtime is untyped, the single cast at the return
|
|
81
81
|
* is the seam, and the type parameters do all the guarding.
|
|
82
82
|
*/
|
|
83
|
-
function
|
|
83
|
+
function makeTestPluginBuilder(state) {
|
|
84
84
|
const bootApp = async () => {
|
|
85
|
-
const
|
|
85
|
+
const plugins = [];
|
|
86
86
|
if (Object.keys(state.fakes).length > 0) {
|
|
87
87
|
const fakes = { ...state.fakes };
|
|
88
|
-
|
|
89
|
-
name: `test:fakes(${state.
|
|
88
|
+
plugins.push({
|
|
89
|
+
name: `test:fakes(${state.plugin.name})`,
|
|
90
90
|
needs: {},
|
|
91
|
+
actions: [],
|
|
91
92
|
renames: {},
|
|
92
93
|
hooks: { boot: () => fakes },
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
|
-
|
|
96
|
-
return testApp(
|
|
97
|
-
name: state.options.name ?? `test:${state.
|
|
96
|
+
plugins.push(state.plugin);
|
|
97
|
+
return testApp(plugins, {
|
|
98
|
+
name: state.options.name ?? `test:${state.plugin.name}`,
|
|
98
99
|
...(state.options.config === undefined ? {} : { config: state.options.config }),
|
|
99
100
|
}).boot();
|
|
100
101
|
};
|
|
101
102
|
const impl = {
|
|
102
103
|
provide(tokenOrKey, value) {
|
|
103
104
|
const key = typeof tokenOrKey === 'string' ? tokenOrKey : tokenOrKey.key;
|
|
104
|
-
return
|
|
105
|
+
return makeTestPluginBuilder({ ...state, fakes: { ...state.fakes, [key]: value } });
|
|
105
106
|
},
|
|
106
107
|
async boot(..._guard) {
|
|
107
108
|
return bootApp();
|
package/dist/test-harness.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-harness.js","sourceRoot":"","sources":["../src/test-harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAkB5C;;;GAGG;AACH,MAAM,UAAU,OAAO,CACrB,
|
|
1
|
+
{"version":3,"file":"test-harness.js","sourceRoot":"","sources":["../src/test-harness.ts"],"names":[],"mappings":"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"}
|
package/dist/timeline.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ASCII waterfall renderer for DOT lifecycle diagnostics.
|
|
3
3
|
*
|
|
4
|
-
* Given a `DotDiagnosticsSnapshot`, builds a compact per-phase, per-
|
|
4
|
+
* Given a `DotDiagnosticsSnapshot`, builds a compact per-phase, per-plugin
|
|
5
5
|
* duration chart suitable for printing in a terminal or embedding in a
|
|
6
6
|
* `dot doctor` text report. No colour, no Unicode beyond `█` so the
|
|
7
7
|
* output stays clean in log aggregators and CI.
|
|
@@ -29,7 +29,7 @@ export type RenderTimelineOptions = {
|
|
|
29
29
|
/** Maximum bar width in characters (default `50`). */
|
|
30
30
|
readonly barWidth?: number;
|
|
31
31
|
/**
|
|
32
|
-
* When `true`, also emit any
|
|
32
|
+
* When `true`, also emit any plugin's diagnostic `issues[]` underneath
|
|
33
33
|
* the bar so failure reasons are visible in the timeline view.
|
|
34
34
|
* Default `true`.
|
|
35
35
|
*/
|
package/dist/timeline.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ASCII waterfall renderer for DOT lifecycle diagnostics.
|
|
3
3
|
*
|
|
4
|
-
* Given a `DotDiagnosticsSnapshot`, builds a compact per-phase, per-
|
|
4
|
+
* Given a `DotDiagnosticsSnapshot`, builds a compact per-phase, per-plugin
|
|
5
5
|
* duration chart suitable for printing in a terminal or embedding in a
|
|
6
6
|
* `dot doctor` text report. No colour, no Unicode beyond `█` so the
|
|
7
7
|
* output stays clean in log aggregators and CI.
|
|
@@ -60,8 +60,8 @@ export function renderTimeline(snapshot, opts = {}) {
|
|
|
60
60
|
const title = `Timeline: ${snapshot.app.name} (state=${snapshot.app.state})`;
|
|
61
61
|
lines.push(title);
|
|
62
62
|
lines.push('─'.repeat(title.length));
|
|
63
|
-
// Layout: 2-space indent, 10-char
|
|
64
|
-
const
|
|
63
|
+
// Layout: 2-space indent, 10-char plugin column, 8-char duration column, then bar.
|
|
64
|
+
const PLUGIN_COL = 12;
|
|
65
65
|
const DUR_COL = 8;
|
|
66
66
|
const indent = ' ';
|
|
67
67
|
let anyRendered = false;
|
|
@@ -77,9 +77,9 @@ export function renderTimeline(snapshot, opts = {}) {
|
|
|
77
77
|
const durationMs = entry.durationMs ?? 0;
|
|
78
78
|
const bar = makeBar(durationMs, maxDuration, barWidth);
|
|
79
79
|
const failedMark = entry.state === 'failed' ? ' ✗' : '';
|
|
80
|
-
const
|
|
80
|
+
const pluginCol = entry.plugin.padEnd(PLUGIN_COL);
|
|
81
81
|
const durCol = formatDuration(durationMs).padStart(DUR_COL);
|
|
82
|
-
lines.push(`${indent}${
|
|
82
|
+
lines.push(`${indent}${pluginCol}${durCol} ${bar}${failedMark}`);
|
|
83
83
|
if (showIssues && entry.issues.length > 0) {
|
|
84
84
|
for (const issue of entry.issues) {
|
|
85
85
|
lines.push(`${indent} └─ ${issue.code}: ${issue.message}`);
|
package/dist/timeline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAKH,MAAM,WAAW,GAAgC,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAEnG,MAAM,QAAQ,GAAG,GAAG,CAAC;AACrB,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAa9B;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgC,EAAE,OAA8B,EAAE;IAC/F,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,iBAAiB,CAAC;IACpD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;IAE3C,sEAAsE;IACtE,sEAAsE;IACtE,qEAAqE;IACrE,yDAAyD;IACzD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2C,CAAC;IACnE,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,MAAM;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,0EAA0E;IAC1E,oEAAoE;IACpE,uDAAuD;IACvD,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,WAAW;YAAE,WAAW,GAAG,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,aAAa,QAAQ,CAAC,GAAG,CAAC,IAAI,WAAW,QAAQ,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC;IAC7E,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAErC,
|
|
1
|
+
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arki/dot",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript-first application composition framework for the ARKI package family — lifecycle-aware
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "TypeScript-first application composition framework for the ARKI package family — lifecycle-aware plugins, deterministic boot order, manifest/diagnostics envelopes, and an agent-native CLI. A DOT app is the sum of its plugins.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"arki",
|
|
16
16
|
"typescript",
|
|
17
17
|
"framework",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"plugin",
|
|
19
|
+
"plugins",
|
|
20
20
|
"lifecycle",
|
|
21
21
|
"diagnostics",
|
|
22
22
|
"manifest",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"import": "./src/index.ts",
|
|
32
32
|
"default": "./dist/index.js"
|
|
33
33
|
},
|
|
34
|
-
"./
|
|
35
|
-
"types": "./dist/
|
|
36
|
-
"import": "./dist/
|
|
37
|
-
"default": "./dist/
|
|
34
|
+
"./plugin": {
|
|
35
|
+
"types": "./dist/plugin.d.ts",
|
|
36
|
+
"import": "./dist/plugin.js",
|
|
37
|
+
"default": "./dist/plugin.js"
|
|
38
38
|
},
|
|
39
39
|
"./package.json": "./package.json",
|
|
40
40
|
"./cli": {
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
],
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@arki/assert": "0.0.2",
|
|
66
|
-
"@arki/contracts": "0.0.
|
|
66
|
+
"@arki/contracts": "0.0.3",
|
|
67
67
|
"@arki/log": "0.0.2",
|
|
68
68
|
"@opentelemetry/api": "^1.9.0",
|
|
69
69
|
"zod": "4.3.5"
|
package/src/cli/discover.ts
CHANGED
|
@@ -186,7 +186,7 @@ export async function loadAppFromFile(filePath: string): Promise<DiscoveredApp>
|
|
|
186
186
|
code: DotCliErrorCode.AppInvalidExport,
|
|
187
187
|
message: `Default export from ${filePath} is not a DotApp or DotAppBuilder.`,
|
|
188
188
|
remediation:
|
|
189
|
-
'Export a DotApp or DotAppBuilder as the default. Example: `export default defineApp("my-app").use(
|
|
189
|
+
'Export a DotApp or DotAppBuilder as the default. Example: `export default defineApp("my-app").use(plugin);`',
|
|
190
190
|
metadata: { filePath },
|
|
191
191
|
});
|
|
192
192
|
}
|
|
@@ -204,7 +204,7 @@ export async function discoverApp(options: DiscoveryOptions = {}): Promise<Disco
|
|
|
204
204
|
code: DotCliErrorCode.AppNotFound,
|
|
205
205
|
message: `No DOT app file found in ${cwd}.`,
|
|
206
206
|
remediation:
|
|
207
|
-
'Create `dot.config.ts` exporting your app (e.g. `export default defineApp("my-app").use(
|
|
207
|
+
'Create `dot.config.ts` exporting your app (e.g. `export default defineApp("my-app").use(plugin)`), or pass `--app <path>` to point at an existing file.',
|
|
208
208
|
metadata: { cwd, searched: DEFAULT_APP_PATHS },
|
|
209
209
|
});
|
|
210
210
|
}
|
package/src/cli/error-codes.ts
CHANGED
|
@@ -21,6 +21,14 @@ export const DotCliErrorCode = {
|
|
|
21
21
|
AppLifecycleFailed: 'DOT_CLI_E006',
|
|
22
22
|
/** `dot doctor --observability` ran but no OTel SDK is registered. */
|
|
23
23
|
ObservabilityNoSdk: 'DOT_CLI_E007',
|
|
24
|
+
/** Requested projection format is not registered by the app. */
|
|
25
|
+
ProjectionNotFound: 'DOT_CLI_E008',
|
|
26
|
+
/** Projection module import failed or did not export `project`. */
|
|
27
|
+
ProjectionImportFailed: 'DOT_CLI_E009',
|
|
28
|
+
/** Projection execution failed or returned non-JSON output. */
|
|
29
|
+
ProjectionExecutionFailed: 'DOT_CLI_E010',
|
|
30
|
+
/** Multiple modules claim the same projection format. */
|
|
31
|
+
ProjectionConflict: 'DOT_CLI_E011',
|
|
24
32
|
} as const;
|
|
25
33
|
|
|
26
34
|
export type DotCliErrorCodeValue = (typeof DotCliErrorCode)[keyof typeof DotCliErrorCode];
|
|
@@ -39,6 +47,10 @@ export const DotCliDocsAnchor: Record<DotCliErrorCodeValue, string> = {
|
|
|
39
47
|
[DotCliErrorCode.InvalidArgs]: 'invalid-args',
|
|
40
48
|
[DotCliErrorCode.AppLifecycleFailed]: 'app-lifecycle-failed',
|
|
41
49
|
[DotCliErrorCode.ObservabilityNoSdk]: 'observability-no-sdk',
|
|
50
|
+
[DotCliErrorCode.ProjectionNotFound]: 'projection-not-found',
|
|
51
|
+
[DotCliErrorCode.ProjectionImportFailed]: 'projection-import-failed',
|
|
52
|
+
[DotCliErrorCode.ProjectionExecutionFailed]: 'projection-execution-failed',
|
|
53
|
+
[DotCliErrorCode.ProjectionConflict]: 'projection-conflict',
|
|
42
54
|
};
|
|
43
55
|
|
|
44
56
|
export function dotCliDocsUrl(code: DotCliErrorCodeValue): string {
|
package/src/cli/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { createDebugLogger } from '@arki/log/debug';
|
|
|
24
24
|
|
|
25
25
|
import type { DotApp, DotAppBuilder, DotAppConfigured } from '../define-app.js';
|
|
26
26
|
import type { DiagnosticIssue } from '../diagnostics.js';
|
|
27
|
-
import type { DiscoveredApp } from './discover.js';
|
|
27
|
+
import type { DiscoveredApp, DiscoveryResult } from './discover.js';
|
|
28
28
|
import type { DotCliEnvelope, DotCliEnvelopeStatus } from './render-explain.js';
|
|
29
29
|
import { DotLifecycleError } from '../lifecycle.js';
|
|
30
30
|
import { discoverApp, guards } from './discover.js';
|
|
@@ -34,7 +34,7 @@ import { probeObservability } from './observability-probe.js';
|
|
|
34
34
|
import { renderDoctor } from './render-doctor.js';
|
|
35
35
|
import { renderExplain } from './render-explain.js';
|
|
36
36
|
import { renderGraph } from './render-graph.js';
|
|
37
|
-
import {
|
|
37
|
+
import { renderProjection } from './render-projection.js';
|
|
38
38
|
|
|
39
39
|
const debugCli = createDebugLogger('arki:dot:cli');
|
|
40
40
|
|
|
@@ -59,14 +59,16 @@ Common options:
|
|
|
59
59
|
--app <path> Path to the app entry file (default: discovers
|
|
60
60
|
./dot.config.ts, ./src/app.ts, or ./app.ts)
|
|
61
61
|
--cwd <dir> Working directory (default: current)
|
|
62
|
-
--graph Emit the
|
|
62
|
+
--graph Emit the plugin graph as Mermaid flowchart source
|
|
63
63
|
instead of the standard output. explain shows
|
|
64
64
|
declaration (= boot) order; doctor shows the
|
|
65
65
|
wiring observed during boot. Composes with --json.
|
|
66
|
-
--
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
--as <format> (explain only) Render a registered projection format.
|
|
67
|
+
Examples: openapi, asyncapi, help-text. Composes
|
|
68
|
+
with --json.
|
|
69
|
+
--module <specifier> (explain --as only) Override the registered
|
|
70
|
+
projection module for one invocation.
|
|
71
|
+
--openapi Deprecated alias for --as openapi.
|
|
70
72
|
|
|
71
73
|
\`doctor\` options:
|
|
72
74
|
--observability Also probe whether an OpenTelemetry SDK is
|
|
@@ -101,7 +103,11 @@ export type CliArgs = {
|
|
|
101
103
|
observability?: boolean;
|
|
102
104
|
/** `--graph` (honored by `explain` and `doctor`). */
|
|
103
105
|
graph?: boolean;
|
|
104
|
-
/** `--
|
|
106
|
+
/** `--as` projection format (honored by `explain` only). */
|
|
107
|
+
as?: string;
|
|
108
|
+
/** `--module` projection override (honored by `explain --as` only). */
|
|
109
|
+
module?: string;
|
|
110
|
+
/** Deprecated `--openapi` alias for `--as openapi`. */
|
|
105
111
|
openapi?: boolean;
|
|
106
112
|
};
|
|
107
113
|
|
|
@@ -167,6 +173,8 @@ export function parseArgs(argv: readonly string[]): CliArgs {
|
|
|
167
173
|
force: { type: 'boolean', default: false },
|
|
168
174
|
observability: { type: 'boolean', default: false },
|
|
169
175
|
graph: { type: 'boolean', default: false },
|
|
176
|
+
as: { type: 'string' },
|
|
177
|
+
module: { type: 'string' },
|
|
170
178
|
openapi: { type: 'boolean', default: false },
|
|
171
179
|
},
|
|
172
180
|
});
|
|
@@ -191,6 +199,8 @@ export function parseArgs(argv: readonly string[]): CliArgs {
|
|
|
191
199
|
force?: boolean;
|
|
192
200
|
observability?: boolean;
|
|
193
201
|
graph?: boolean;
|
|
202
|
+
as?: string;
|
|
203
|
+
module?: string;
|
|
194
204
|
openapi?: boolean;
|
|
195
205
|
};
|
|
196
206
|
|
|
@@ -199,18 +209,27 @@ export function parseArgs(argv: readonly string[]): CliArgs {
|
|
|
199
209
|
|
|
200
210
|
if (!command) command = 'help';
|
|
201
211
|
|
|
202
|
-
|
|
212
|
+
const projectionFormat = values.as ?? (values.openapi === true ? 'openapi' : undefined);
|
|
213
|
+
|
|
214
|
+
if (projectionFormat !== undefined && values.graph === true) {
|
|
203
215
|
throw new DotCliError({
|
|
204
216
|
code: DotCliErrorCode.InvalidArgs,
|
|
205
|
-
message: '--openapi and --graph are mutually exclusive.',
|
|
217
|
+
message: '--as/--openapi and --graph are mutually exclusive.',
|
|
206
218
|
remediation: 'Pick one output format per invocation.',
|
|
207
219
|
});
|
|
208
220
|
}
|
|
209
|
-
if (
|
|
221
|
+
if (projectionFormat !== undefined && command === 'doctor') {
|
|
222
|
+
throw new DotCliError({
|
|
223
|
+
code: DotCliErrorCode.InvalidArgs,
|
|
224
|
+
message: '--as is not supported by doctor.',
|
|
225
|
+
remediation: 'Use `dot explain --as <format>` — projections render from the static manifest, no boot needed.',
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (values.module !== undefined && projectionFormat === undefined) {
|
|
210
229
|
throw new DotCliError({
|
|
211
230
|
code: DotCliErrorCode.InvalidArgs,
|
|
212
|
-
message: '--
|
|
213
|
-
remediation: '
|
|
231
|
+
message: '--module requires --as <format>.',
|
|
232
|
+
remediation: 'Pass a projection format with --as, or remove --module.',
|
|
214
233
|
});
|
|
215
234
|
}
|
|
216
235
|
|
|
@@ -239,14 +258,15 @@ export function parseArgs(argv: readonly string[]): CliArgs {
|
|
|
239
258
|
force: values.force ?? false,
|
|
240
259
|
observability: values.observability ?? false,
|
|
241
260
|
graph: values.graph ?? false,
|
|
261
|
+
as: projectionFormat,
|
|
262
|
+
module: values.module,
|
|
242
263
|
openapi: values.openapi ?? false,
|
|
243
264
|
};
|
|
244
265
|
}
|
|
245
266
|
|
|
246
267
|
/** Discovery wrapper — returns `null` for help/version commands. */
|
|
247
|
-
async function loadApp(args: CliArgs): Promise<
|
|
248
|
-
|
|
249
|
-
return app;
|
|
268
|
+
async function loadApp(args: CliArgs): Promise<DiscoveryResult> {
|
|
269
|
+
return discoverApp({ appPath: args.appPath, cwd: args.cwd });
|
|
250
270
|
}
|
|
251
271
|
|
|
252
272
|
/**
|
|
@@ -255,7 +275,16 @@ async function loadApp(args: CliArgs): Promise<DiscoveredApp> {
|
|
|
255
275
|
*/
|
|
256
276
|
export async function runExplain(
|
|
257
277
|
discovered: DiscoveredApp,
|
|
258
|
-
opts: {
|
|
278
|
+
opts: {
|
|
279
|
+
json: boolean;
|
|
280
|
+
graph?: boolean;
|
|
281
|
+
as?: string;
|
|
282
|
+
module?: string;
|
|
283
|
+
appFilePath?: string;
|
|
284
|
+
openapi?: boolean;
|
|
285
|
+
out?: (line: string) => void;
|
|
286
|
+
now?: () => Date;
|
|
287
|
+
},
|
|
259
288
|
): Promise<DotCliEnvelope<unknown>> {
|
|
260
289
|
let configured: DotApp<Record<string, unknown>> | DotAppConfigured<Record<string, unknown>>;
|
|
261
290
|
try {
|
|
@@ -271,9 +300,15 @@ export async function runExplain(
|
|
|
271
300
|
throw wrapLifecycleError(err, 'configure');
|
|
272
301
|
}
|
|
273
302
|
|
|
274
|
-
if (opts.
|
|
275
|
-
return
|
|
276
|
-
{
|
|
303
|
+
if (opts.as !== undefined) {
|
|
304
|
+
return renderProjection(
|
|
305
|
+
{
|
|
306
|
+
manifest: configured.manifest,
|
|
307
|
+
command: 'explain',
|
|
308
|
+
format: opts.as,
|
|
309
|
+
appFilePath: opts.appFilePath ?? process.cwd(),
|
|
310
|
+
...(opts.module === undefined ? {} : { module: opts.module }),
|
|
311
|
+
},
|
|
277
312
|
{ json: opts.json, out: opts.out, now: opts.now },
|
|
278
313
|
);
|
|
279
314
|
}
|
|
@@ -296,7 +331,7 @@ type DoctorRunOptions = {
|
|
|
296
331
|
* present. Default `false`.
|
|
297
332
|
*/
|
|
298
333
|
observability?: boolean;
|
|
299
|
-
/** When `true`, emit the
|
|
334
|
+
/** When `true`, emit the plugin graph (Mermaid) instead of diagnostics. */
|
|
300
335
|
graph?: boolean;
|
|
301
336
|
};
|
|
302
337
|
|
|
@@ -328,7 +363,7 @@ export async function runDoctor(
|
|
|
328
363
|
}
|
|
329
364
|
|
|
330
365
|
// Builder or configured seam: drive lifecycle ourselves. Boot failures fall
|
|
331
|
-
// back to the configured seam's diagnostics so per-
|
|
366
|
+
// back to the configured seam's diagnostics so per-plugin issues stay
|
|
332
367
|
// visible.
|
|
333
368
|
let configured: DotAppConfigured<Record<string, unknown>>;
|
|
334
369
|
try {
|
|
@@ -345,7 +380,7 @@ export async function runDoctor(
|
|
|
345
380
|
bootedApp = await configured.boot();
|
|
346
381
|
} catch (err) {
|
|
347
382
|
// Boot failed — surface diagnostics from the configured seam below.
|
|
348
|
-
// We deliberately swallow the throw so the user sees the per-
|
|
383
|
+
// We deliberately swallow the throw so the user sees the per-plugin
|
|
349
384
|
// issues instead of an opaque wrapper error.
|
|
350
385
|
bootThrew = true;
|
|
351
386
|
debugCli('boot threw, falling back to configured diagnostics: %O', err);
|
|
@@ -354,7 +389,7 @@ export async function runDoctor(
|
|
|
354
389
|
try {
|
|
355
390
|
if (opts.graph === true) {
|
|
356
391
|
// Post-boot manifest carries the observed wiring edges; after a boot
|
|
357
|
-
// failure it carries the edges recorded up to the failing
|
|
392
|
+
// failure it carries the edges recorded up to the failing plugin.
|
|
358
393
|
const manifest = bootedApp ? bootedApp.manifest : configured.manifest;
|
|
359
394
|
const graphEnvelope = renderGraph(
|
|
360
395
|
{ manifest, command: 'doctor' },
|
|
@@ -410,8 +445,8 @@ function wrapLifecycleError(err: unknown, phase: 'configure' | 'boot'): DotCliEr
|
|
|
410
445
|
message: `App ${phase} failed: ${message}`,
|
|
411
446
|
remediation:
|
|
412
447
|
phase === 'configure'
|
|
413
|
-
? 'Check the
|
|
414
|
-
: 'Run `dot doctor` to see per-
|
|
448
|
+
? 'Check the plugins registered in your app for a synchronous `configure` hook that throws or returns a Promise. Run `dot doctor` for per-plugin diagnostics.'
|
|
449
|
+
: 'Run `dot doctor` to see per-plugin diagnostics. The boot hook for one of your plugins failed.',
|
|
415
450
|
metadata: { phase, underlyingCode: code },
|
|
416
451
|
cause: err,
|
|
417
452
|
});
|
|
@@ -519,9 +554,18 @@ export async function main(options: MainOptions): Promise<number> {
|
|
|
519
554
|
let envelope: DotCliEnvelope<unknown>;
|
|
520
555
|
|
|
521
556
|
if (args.command === 'explain') {
|
|
522
|
-
|
|
557
|
+
if (args.openapi === true) {
|
|
558
|
+
stderr('dot: --openapi is deprecated; use --as openapi.\n');
|
|
559
|
+
}
|
|
560
|
+
envelope = await runExplain(discovered.app, {
|
|
561
|
+
...opts,
|
|
562
|
+
as: args.as,
|
|
563
|
+
module: args.module,
|
|
564
|
+
appFilePath: discovered.filePath,
|
|
565
|
+
openapi: args.openapi,
|
|
566
|
+
});
|
|
523
567
|
} else {
|
|
524
|
-
envelope = await runDoctor(discovered, { ...opts, observability: args.observability });
|
|
568
|
+
envelope = await runDoctor(discovered.app, { ...opts, observability: args.observability });
|
|
525
569
|
}
|
|
526
570
|
|
|
527
571
|
return envelopeExitCode(envelope.status);
|
|
@@ -587,11 +631,14 @@ const isMainModule = await (async () => {
|
|
|
587
631
|
/* ignore */
|
|
588
632
|
}
|
|
589
633
|
// Node fallback: compare argv[1]'s resolved file URL with import.meta.url.
|
|
634
|
+
// argv[1] is the bin symlink (node_modules/.bin/dot) while Node resolves
|
|
635
|
+
// import.meta.url to the real file — realpath argv[1] so they can match.
|
|
590
636
|
try {
|
|
591
637
|
const entry = process.argv[1];
|
|
592
638
|
if (typeof entry !== 'string') return false;
|
|
593
639
|
const { pathToFileURL } = await import('node:url');
|
|
594
|
-
|
|
640
|
+
const { realpath } = await import('node:fs/promises');
|
|
641
|
+
return pathToFileURL(await realpath(entry)).href === import.meta.url;
|
|
595
642
|
} catch {
|
|
596
643
|
return false;
|
|
597
644
|
}
|
package/src/cli/new.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* stripped during scaffolding so `package.json.tmpl` becomes
|
|
18
18
|
* `package.json` on disk.
|
|
19
19
|
*
|
|
20
|
-
* Commands deferred to v1.1 (`dot add <
|
|
20
|
+
* Commands deferred to v1.1 (`dot add <plugin>`, `dot dev`,
|
|
21
21
|
* `dot migrate`) are deliberately NOT surfaced here.
|
|
22
22
|
*/
|
|
23
23
|
|
package/src/cli/render-doctor.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Renderers for `dot doctor`.
|
|
3
3
|
*
|
|
4
4
|
* Reads a booted (or fully-failed) app's `diagnostics` snapshot and emits
|
|
5
|
-
* either a JSON envelope or a human-readable per-
|
|
5
|
+
* either a JSON envelope or a human-readable per-plugin status report.
|
|
6
6
|
*
|
|
7
7
|
* Envelope `status` reflects the worst severity present:
|
|
8
8
|
* - `failure` if any issue has severity `error`
|
|
@@ -27,8 +27,8 @@ function nowIso(opts: RenderOptions): string {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Walk every issue carried anywhere in the snapshot (top-level + per-
|
|
31
|
-
* per-
|
|
30
|
+
* Walk every issue carried anywhere in the snapshot (top-level + per-plugin +
|
|
31
|
+
* per-action + per-service + per-lifecycle entry) and pick the worst severity.
|
|
32
32
|
*/
|
|
33
33
|
function worstSeverity(snap: DotDiagnosticsSnapshot): DotCliEnvelopeStatus {
|
|
34
34
|
let hasWarning = false;
|
|
@@ -46,8 +46,8 @@ function worstSeverity(snap: DotDiagnosticsSnapshot): DotCliEnvelopeStatus {
|
|
|
46
46
|
|
|
47
47
|
let hasError = false;
|
|
48
48
|
collect(snap.issues);
|
|
49
|
-
for (const p of snap.
|
|
50
|
-
for (const
|
|
49
|
+
for (const p of snap.plugins) collect(p.issues);
|
|
50
|
+
for (const action of snap.actions) collect(action.issues);
|
|
51
51
|
for (const s of snap.services) collect(s.issues);
|
|
52
52
|
for (const l of snap.lifecycle) collect(l.issues);
|
|
53
53
|
|
|
@@ -72,8 +72,8 @@ export function buildDoctorEnvelope(
|
|
|
72
72
|
for (const issue of issues) errors.push(issue);
|
|
73
73
|
};
|
|
74
74
|
collect(source.diagnostics.issues);
|
|
75
|
-
for (const p of source.diagnostics.
|
|
76
|
-
for (const
|
|
75
|
+
for (const p of source.diagnostics.plugins) collect(p.issues);
|
|
76
|
+
for (const action of source.diagnostics.actions) collect(action.issues);
|
|
77
77
|
for (const s of source.diagnostics.services) collect(s.issues);
|
|
78
78
|
for (const l of source.diagnostics.lifecycle) collect(l.issues);
|
|
79
79
|
|
|
@@ -120,13 +120,13 @@ function renderTextDoctor(snap: DotDiagnosticsSnapshot, status: DotCliEnvelopeSt
|
|
|
120
120
|
lines.push(`generatedAt: ${snap.generatedAt}`);
|
|
121
121
|
lines.push('');
|
|
122
122
|
|
|
123
|
-
// Per-
|
|
124
|
-
lines.push(`
|
|
125
|
-
if (snap.
|
|
123
|
+
// Per-plugin status
|
|
124
|
+
lines.push(`Plugins (${snap.plugins.length})`);
|
|
125
|
+
if (snap.plugins.length === 0) {
|
|
126
126
|
lines.push(' (none)');
|
|
127
127
|
} else {
|
|
128
|
-
for (const p of snap.
|
|
129
|
-
lines.push(` ${statusIcon(p.status)} ${p.
|
|
128
|
+
for (const p of snap.plugins) {
|
|
129
|
+
lines.push(` ${statusIcon(p.status)} ${p.plugin}`);
|
|
130
130
|
if (p.issues.length > 0) {
|
|
131
131
|
lines.push(...renderIssueLines(p.issues, ' '));
|
|
132
132
|
}
|
|
@@ -138,7 +138,7 @@ function renderTextDoctor(snap: DotDiagnosticsSnapshot, status: DotCliEnvelopeSt
|
|
|
138
138
|
if (snap.services.length > 0) {
|
|
139
139
|
lines.push(`Services (${snap.services.length})`);
|
|
140
140
|
for (const s of snap.services) {
|
|
141
|
-
lines.push(` ${statusIcon(s.status)} ${s.service} (
|
|
141
|
+
lines.push(` ${statusIcon(s.status)} ${s.service} (plugin: ${s.plugin})`);
|
|
142
142
|
if (s.issues.length > 0) {
|
|
143
143
|
lines.push(...renderIssueLines(s.issues, ' '));
|
|
144
144
|
}
|
|
@@ -146,13 +146,13 @@ function renderTextDoctor(snap: DotDiagnosticsSnapshot, status: DotCliEnvelopeSt
|
|
|
146
146
|
lines.push('');
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
//
|
|
150
|
-
if (snap.
|
|
151
|
-
lines.push(`
|
|
152
|
-
for (const
|
|
153
|
-
lines.push(` ${statusIcon(
|
|
154
|
-
if (
|
|
155
|
-
lines.push(...renderIssueLines(
|
|
149
|
+
// Actions
|
|
150
|
+
if (snap.actions.length > 0) {
|
|
151
|
+
lines.push(`Actions (${snap.actions.length})`);
|
|
152
|
+
for (const action of snap.actions) {
|
|
153
|
+
lines.push(` ${statusIcon(action.status)} ${action.binding}:${action.id} (plugin: ${action.plugin})`);
|
|
154
|
+
if (action.issues.length > 0) {
|
|
155
|
+
lines.push(...renderIssueLines(action.issues, ' '));
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
lines.push('');
|
|
@@ -163,7 +163,7 @@ function renderTextDoctor(snap: DotDiagnosticsSnapshot, status: DotCliEnvelopeSt
|
|
|
163
163
|
if (lifeWithIssues.length > 0) {
|
|
164
164
|
lines.push(`Lifecycle issues (${lifeWithIssues.length})`);
|
|
165
165
|
for (const l of lifeWithIssues) {
|
|
166
|
-
lines.push(` ${l.
|
|
166
|
+
lines.push(` ${l.plugin}:${l.hook} (order=${l.order}, state=${l.state})`);
|
|
167
167
|
lines.push(...renderIssueLines(l.issues, ' '));
|
|
168
168
|
}
|
|
169
169
|
lines.push('');
|