@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.
Files changed (113) hide show
  1. package/README.md +86 -86
  2. package/dist/cli/discover.js +2 -2
  3. package/dist/cli/discover.js.map +1 -1
  4. package/dist/cli/error-codes.d.ts +8 -0
  5. package/dist/cli/error-codes.d.ts.map +1 -1
  6. package/dist/cli/error-codes.js +12 -0
  7. package/dist/cli/error-codes.js.map +1 -1
  8. package/dist/cli/index.d.ts +9 -2
  9. package/dist/cli/index.d.ts.map +1 -1
  10. package/dist/cli/index.js +54 -23
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/cli/new.d.ts +1 -1
  13. package/dist/cli/new.js +1 -1
  14. package/dist/cli/render-doctor.d.ts +1 -1
  15. package/dist/cli/render-doctor.js +23 -23
  16. package/dist/cli/render-doctor.js.map +1 -1
  17. package/dist/cli/render-explain.d.ts.map +1 -1
  18. package/dist/cli/render-explain.js +33 -19
  19. package/dist/cli/render-explain.js.map +1 -1
  20. package/dist/cli/render-graph.d.ts +3 -3
  21. package/dist/cli/render-graph.js +12 -12
  22. package/dist/cli/render-graph.js.map +1 -1
  23. package/dist/cli/render-projection.d.ts +29 -0
  24. package/dist/cli/render-projection.d.ts.map +1 -0
  25. package/dist/cli/render-projection.js +202 -0
  26. package/dist/cli/render-projection.js.map +1 -0
  27. package/dist/define-app.d.ts +49 -17
  28. package/dist/define-app.d.ts.map +1 -1
  29. package/dist/define-app.js +16 -9
  30. package/dist/define-app.js.map +1 -1
  31. package/dist/diagnostics.d.ts +19 -15
  32. package/dist/diagnostics.d.ts.map +1 -1
  33. package/dist/diagnostics.js +2 -2
  34. package/dist/index.d.ts +15 -12
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +7 -5
  37. package/dist/index.js.map +1 -1
  38. package/dist/init-pips.d.ts +45 -0
  39. package/dist/init-pips.d.ts.map +1 -0
  40. package/dist/init-pips.js +49 -0
  41. package/dist/init-pips.js.map +1 -0
  42. package/dist/init-plugins.d.ts +45 -0
  43. package/dist/init-plugins.d.ts.map +1 -0
  44. package/dist/init-plugins.js +49 -0
  45. package/dist/init-plugins.js.map +1 -0
  46. package/dist/kernel/app-instance.d.ts +5 -5
  47. package/dist/kernel/app-instance.d.ts.map +1 -1
  48. package/dist/kernel/app-instance.js +375 -244
  49. package/dist/kernel/app-instance.js.map +1 -1
  50. package/dist/kernel/otel.d.ts +19 -19
  51. package/dist/kernel/otel.d.ts.map +1 -1
  52. package/dist/kernel/otel.js +22 -22
  53. package/dist/kernel/otel.js.map +1 -1
  54. package/dist/lifecycle-observer.d.ts +10 -10
  55. package/dist/lifecycle-observer.d.ts.map +1 -1
  56. package/dist/lifecycle-observer.js +1 -1
  57. package/dist/lifecycle.d.ts +18 -14
  58. package/dist/lifecycle.d.ts.map +1 -1
  59. package/dist/lifecycle.js +13 -9
  60. package/dist/lifecycle.js.map +1 -1
  61. package/dist/manifest.d.ts +70 -48
  62. package/dist/manifest.d.ts.map +1 -1
  63. package/dist/manifest.js +74 -5
  64. package/dist/manifest.js.map +1 -1
  65. package/dist/pip-contract.d.ts +29 -21
  66. package/dist/pip-contract.d.ts.map +1 -1
  67. package/dist/pip-contract.js +1 -0
  68. package/dist/pip-contract.js.map +1 -1
  69. package/dist/pip.d.ts +7 -4
  70. package/dist/pip.d.ts.map +1 -1
  71. package/dist/pip.js +2 -0
  72. package/dist/pip.js.map +1 -1
  73. package/dist/plugin-contract.d.ts +335 -143
  74. package/dist/plugin-contract.d.ts.map +1 -1
  75. package/dist/plugin-contract.js +201 -36
  76. package/dist/plugin-contract.js.map +1 -1
  77. package/dist/plugin.d.ts +18 -15
  78. package/dist/plugin.d.ts.map +1 -1
  79. package/dist/plugin.js +11 -11
  80. package/dist/plugin.js.map +1 -1
  81. package/dist/test-harness.d.ts +31 -31
  82. package/dist/test-harness.d.ts.map +1 -1
  83. package/dist/test-harness.js +29 -28
  84. package/dist/test-harness.js.map +1 -1
  85. package/dist/timeline.d.ts +2 -2
  86. package/dist/timeline.js +5 -5
  87. package/dist/timeline.js.map +1 -1
  88. package/package.json +9 -9
  89. package/src/cli/discover.ts +2 -2
  90. package/src/cli/error-codes.ts +12 -0
  91. package/src/cli/index.ts +76 -29
  92. package/src/cli/new.ts +1 -1
  93. package/src/cli/render-doctor.ts +21 -21
  94. package/src/cli/render-explain.ts +37 -19
  95. package/src/cli/render-graph.ts +12 -12
  96. package/src/cli/render-projection.ts +239 -0
  97. package/src/define-app.ts +85 -22
  98. package/src/diagnostics.ts +19 -15
  99. package/src/index.ts +37 -20
  100. package/src/init-plugins.ts +142 -0
  101. package/src/kernel/app-instance.ts +428 -282
  102. package/src/kernel/otel.ts +27 -27
  103. package/src/lifecycle-observer.ts +10 -10
  104. package/src/lifecycle.ts +19 -15
  105. package/src/manifest.ts +128 -50
  106. package/src/{pip-contract.ts → plugin-contract.ts} +73 -62
  107. package/src/{pip.ts → plugin.ts} +37 -18
  108. package/src/test-harness.ts +52 -51
  109. package/src/timeline.ts +6 -6
  110. package/templates/app-minimal/AGENTS.md.tmpl +9 -9
  111. package/templates/app-minimal/README.md.tmpl +1 -1
  112. package/templates/app-minimal/tests/boot.test.ts.tmpl +1 -1
  113. package/src/cli/render-openapi.ts +0 -147
@@ -3,11 +3,11 @@
3
3
  *
4
4
  * Where the manifest describes the static shape of an app, a
5
5
  * `DotDiagnosticsSnapshot` is a point-in-time observability record: the
6
- * lifecycle state of the app and every pip, plus structured issues with
6
+ * lifecycle state of the app and every plugin, plus structured issues with
7
7
  * remediation guidance.
8
8
  *
9
9
  * CONTRACT: `DotDiagnosticsSnapshot` always exposes the same five arrays
10
- * (`pips`, `routes`, `services`, `lifecycle`, `issues`). Consumers must
10
+ * (`plugins`, `actions`, `services`, `lifecycle`, `issues`). Consumers must
11
11
  * never see an omitted array — empty is empty, but never missing.
12
12
  * This is the "5 arrays" contract referenced by the kernel spec.
13
13
  */
@@ -17,54 +17,58 @@ import type { DotLifecycleHook, DotLifecycleState } from './lifecycle.js';
17
17
  /** Severity of a single diagnostic issue. */
18
18
  export type DiagnosticSeverity = 'info' | 'warning' | 'error';
19
19
 
20
- /** Status of a pip/route/service/lifecycle entry. */
20
+ /** Status of a plugin/action/service/lifecycle entry. */
21
21
  export type DiagnosticStatus = 'ok' | 'degraded' | 'failed' | 'skipped' | 'missing';
22
22
 
23
23
  /**
24
24
  * Single point-in-time diagnostic snapshot of a DotApp.
25
25
  *
26
- * Five top-level arrays — pips, routes, services, lifecycle, issues —
26
+ * Five top-level arrays — plugins, actions, services, lifecycle, issues —
27
27
  * are guaranteed to be present even when empty.
28
28
  */
29
29
  export type DotDiagnosticsSnapshot = {
30
+ /** Diagnostic snapshot schema version. 2 = actions/projections era. */
31
+ snapshotVersion: 2;
30
32
  /** ISO-8601 timestamp at which the snapshot was generated. */
31
33
  generatedAt: string;
32
34
  app: {
33
35
  name: string;
34
36
  state: DotLifecycleState;
35
37
  };
36
- pips: PipDiagnostic[];
37
- routes: RouteDiagnostic[];
38
+ plugins: PluginDiagnostic[];
39
+ actions: ActionDiagnostic[];
38
40
  services: ServiceDiagnostic[];
39
41
  lifecycle: LifecycleDiagnostic[];
40
42
  issues: DiagnosticIssue[];
41
43
  };
42
44
 
43
- export type PipDiagnostic = {
44
- pip: string;
45
+ export type PluginDiagnostic = {
46
+ plugin: string;
45
47
  status: Extract<DiagnosticStatus, 'ok' | 'degraded' | 'failed' | 'skipped'>;
46
48
  issues: DiagnosticIssue[];
47
49
  };
48
50
 
49
- export type RouteDiagnostic = {
51
+ export type ActionDiagnostic = {
50
52
  id: string;
51
- pip: string;
53
+ plugin: string;
54
+ binding: string;
55
+ /** Structural configure-phase health only; not runtime liveness. */
52
56
  status: Extract<DiagnosticStatus, 'ok' | 'degraded' | 'failed'>;
53
57
  issues: DiagnosticIssue[];
54
58
  };
55
59
 
56
60
  export type ServiceDiagnostic = {
57
61
  service: string;
58
- pip: string;
62
+ plugin: string;
59
63
  status: Extract<DiagnosticStatus, 'ok' | 'degraded' | 'failed' | 'missing'>;
60
64
  issues: DiagnosticIssue[];
61
65
  };
62
66
 
63
67
  export type LifecycleDiagnostic = {
64
- pip: string;
68
+ plugin: string;
65
69
  hook: DotLifecycleHook;
66
70
  state: DotLifecycleState;
67
- /** 0-based topological order of this pip within the hook pass. */
71
+ /** 0-based topological order of this plugin within the hook pass. */
68
72
  order: number;
69
73
  /** Wall-clock duration of the hook call, in milliseconds. */
70
74
  durationMs?: number;
@@ -72,7 +76,7 @@ export type LifecycleDiagnostic = {
72
76
  };
73
77
 
74
78
  /**
75
- * Single structured issue surfaced by the kernel or by a pip.
79
+ * Single structured issue surfaced by the kernel or by a plugin.
76
80
  *
77
81
  * `code`, `message`, `remediation` and `docsUrl` are required so that issues
78
82
  * are always actionable — never just "something went wrong".
@@ -81,7 +85,7 @@ export type DiagnosticIssue = {
81
85
  /** Stable machine-readable code, e.g. `DOT_LIFECYCLE_E003`. */
82
86
  code: string;
83
87
  severity: DiagnosticSeverity;
84
- pip?: string;
88
+ plugin?: string;
85
89
  message: string;
86
90
  /** Human-readable remediation: what the developer should do. */
87
91
  remediation: string;
package/src/index.ts CHANGED
@@ -3,20 +3,29 @@
3
3
  *
4
4
  * Public surface:
5
5
  * - `defineApp(name)` — the entry point for composing applications.
6
- * - `pip(config)` — author lifecycle-aware pips with typed needs/provides.
6
+ * - `plugin(config)` — author lifecycle-aware plugins with typed needs/provides.
7
7
  * - `service<T>()` / `token<T>()(key)` — service witnesses for DI wiring.
8
- * - `rename(pip, map)` — mount-time multi-instance primitive.
8
+ * - `rename(plugin, map)` — mount-time multi-instance primitive.
9
9
  * - Lifecycle / manifest / diagnostics types.
10
- * - `testApp` / `bootTestApp` — test harnesses for pip authors.
10
+ * - `testApp` / `bootTestApp` — test harnesses for plugin authors.
11
11
  */
12
12
 
13
13
  // #region Kernel — public surface
14
14
  export { defineApp } from './define-app.js';
15
- export type { DotApp, DotAppBuilder, DotAppConfigured, UseGuard } from './define-app.js';
15
+ export type {
16
+ DotApp,
17
+ DotAppBuilder,
18
+ DotAppConfigured,
19
+ NormalizeProvides,
20
+ UseAllAvail,
21
+ UseAllGuard,
22
+ UseGuard,
23
+ } from './define-app.js';
16
24
 
17
- export { isLazy, lazy, lazyOf, pip, provide, rename, service, token, DotPipError } from './pip-contract.js';
25
+ export { isLazy, lazy, lazyOf, plugin, provide, rename, service, token, DotPluginError } from './plugin-contract.js';
18
26
  export type {
19
- AnyPip,
27
+ AnyPlugin,
28
+ ActionSource,
20
29
  CtxOf,
21
30
  DotConfigureContext,
22
31
  EmptyShape,
@@ -25,15 +34,19 @@ export type {
25
34
  Lazy,
26
35
  LazyService,
27
36
  NeedsShape,
28
- Pip,
29
- PipNeeds,
30
- PipProvides,
37
+ NoReservedKeys,
38
+ Plugin,
39
+ PluginNeeds,
40
+ PluginProvides,
31
41
  RenamedProvides,
32
42
  Service,
33
43
  ServiceRecord,
34
44
  Token,
35
45
  WireNeeds,
36
- } from './pip-contract.js';
46
+ } from './plugin-contract.js';
47
+
48
+ export { initPlugins } from './init-plugins.js';
49
+ export type { InitPluginsFactory } from './init-plugins.js';
37
50
 
38
51
  export {
39
52
  DotLifecycleError,
@@ -43,26 +56,30 @@ export {
43
56
  export type {
44
57
  DotLifecycleHook,
45
58
  DotLifecycleState,
46
- DotLifecyclePipFailure,
59
+ DotLifecyclePluginFailure,
47
60
  DotLifecycleErrorCodeValue,
48
61
  } from './lifecycle.js';
49
62
 
50
63
  export type {
51
64
  DotAppManifest,
52
- PipManifest,
53
- RouteManifest,
65
+ ActionDirection,
66
+ ActionManifest,
67
+ JsonObject,
68
+ JsonValue,
69
+ PluginManifest,
70
+ ProjectionManifest,
54
71
  ServiceManifest,
55
72
  LifecycleManifest,
56
73
  DependencyEdge,
57
74
  DependencyEdgeKind,
58
75
  ServiceKind,
59
- RouteTransport,
60
76
  } from './manifest.js';
77
+ export { toJsonObject } from './manifest.js';
61
78
 
62
79
  export type {
63
80
  DotDiagnosticsSnapshot,
64
- PipDiagnostic,
65
- RouteDiagnostic,
81
+ PluginDiagnostic,
82
+ ActionDiagnostic,
66
83
  ServiceDiagnostic,
67
84
  LifecycleDiagnostic,
68
85
  DiagnosticIssue,
@@ -75,19 +92,19 @@ export type {
75
92
  DotLifecycleEventStatus,
76
93
  DotLifecycleObserver,
77
94
  DotPhaseLifecycleEvent,
78
- DotPipHookLifecycleEvent,
95
+ DotPluginHookLifecycleEvent,
79
96
  } from './lifecycle-observer.js';
80
97
 
81
98
  export { renderTimeline } from './timeline.js';
82
99
  export type { RenderTimelineOptions } from './timeline.js';
83
100
 
84
- export { testApp, bootTestApp, testPip } from './test-harness.js';
85
- export type { TestAppOptions, TestPipBuilder } from './test-harness.js';
101
+ export { testApp, bootTestApp, testPlugin } from './test-harness.js';
102
+ export type { TestAppOptions, TestPluginBuilder } from './test-harness.js';
86
103
 
87
104
  export { hookSignals } from './signals.js';
88
105
  export type { HookSignalsOptions, SignalTarget } from './signals.js';
89
106
 
90
107
  // Task 9b: CLI envelope type is exported so adapter packages can produce the
91
- // same shape from related tooling (release-tooling, pip scaffolds, etc.).
108
+ // same shape from related tooling (release-tooling, plugin scaffolds, etc.).
92
109
  export type { DotCliEnvelope, DotCliEnvelopeStatus } from './cli/render-explain.js';
93
110
  // #endregion
@@ -0,0 +1,142 @@
1
+ import type {
2
+ CtxOf,
3
+ DotConfigureContext,
4
+ EmptyShape,
5
+ InferredProvides,
6
+ KernelCtx,
7
+ NeedsShape,
8
+ NoReservedKeys,
9
+ Plugin,
10
+ ActionSource,
11
+ Service,
12
+ ServiceRecord,
13
+ WireNeeds,
14
+ } from './plugin-contract.js';
15
+ import { plugin as definePlugin, service } from './plugin-contract.js';
16
+
17
+ type MaybePromise<T> = T | Promise<T>;
18
+ type ServiceKey<TServices extends object> = Extract<keyof TServices, string>;
19
+ type ServiceKeys<TServices extends object> = readonly ServiceKey<TServices>[];
20
+
21
+ type PickServices<TServices extends object, TKeys extends ServiceKeys<TServices>> = {
22
+ readonly [K in TKeys[number]]: TServices[K];
23
+ };
24
+
25
+ type WitnessesFor<TServices extends object, TKeys extends ServiceKeys<TServices>> = {
26
+ readonly [K in TKeys[number]]: Service<TServices[K]>;
27
+ };
28
+
29
+ type SchemaCheckedProvides<TServices extends object, TProvides extends ServiceRecord> = {
30
+ readonly [K in keyof TProvides]: K extends keyof TServices
31
+ ? TProvides[K] extends TServices[K]
32
+ ? TProvides[K]
33
+ : never
34
+ : TProvides[K];
35
+ };
36
+
37
+ type CommonPluginDefinition = {
38
+ readonly name: string;
39
+ readonly version?: string;
40
+ readonly actions?: readonly ActionSource[];
41
+ readonly configure?: (ctx: DotConfigureContext) => void;
42
+ };
43
+
44
+ type KeyArrayPluginDefinition<
45
+ TServices extends object,
46
+ TKeys extends ServiceKeys<TServices>,
47
+ TProvides extends ServiceRecord & NoReservedKeys,
48
+ > = CommonPluginDefinition & {
49
+ readonly needs?: TKeys;
50
+ readonly boot?: (ctx: PickServices<TServices, TKeys> & KernelCtx) => MaybePromise<SchemaCheckedProvides<TServices, TProvides> | void>;
51
+ readonly start?: (ctx: PickServices<TServices, TKeys> & TProvides & KernelCtx) => MaybePromise<void>;
52
+ readonly stop?: (ctx: PickServices<TServices, TKeys> & TProvides & KernelCtx) => MaybePromise<void>;
53
+ readonly dispose?: (ctx: PickServices<TServices, TKeys> & TProvides & KernelCtx) => MaybePromise<void>;
54
+ };
55
+
56
+ type WitnessPluginDefinition<
57
+ TServices extends object,
58
+ TShape extends NeedsShape & NoReservedKeys,
59
+ TProvides extends ServiceRecord & NoReservedKeys,
60
+ > = CommonPluginDefinition & {
61
+ readonly needs: TShape;
62
+ readonly boot?: (ctx: CtxOf<TShape> & KernelCtx) => MaybePromise<SchemaCheckedProvides<TServices, TProvides> | void>;
63
+ readonly start?: (ctx: CtxOf<TShape> & TProvides & KernelCtx) => MaybePromise<void>;
64
+ readonly stop?: (ctx: CtxOf<TShape> & TProvides & KernelCtx) => MaybePromise<void>;
65
+ readonly dispose?: (ctx: CtxOf<TShape> & TProvides & KernelCtx) => MaybePromise<void>;
66
+ };
67
+
68
+ export type InitPluginsFactory<TServices extends object> = {
69
+ plugin<
70
+ const TKeys extends ServiceKeys<TServices> = readonly [],
71
+ TProvides extends ServiceRecord & NoReservedKeys = EmptyShape,
72
+ >(
73
+ def: KeyArrayPluginDefinition<TServices, TKeys, TProvides>,
74
+ ): Plugin<PickServices<TServices, TKeys>, InferredProvides<TProvides>>;
75
+ plugin<TShape extends NeedsShape & NoReservedKeys, TProvides extends ServiceRecord & NoReservedKeys = EmptyShape>(
76
+ def: WitnessPluginDefinition<TServices, TShape, TProvides>,
77
+ ): Plugin<WireNeeds<TShape>, InferredProvides<TProvides>>;
78
+ needs<const TKeys extends ServiceKeys<TServices>>(...keys: TKeys): WitnessesFor<TServices, TKeys>;
79
+ };
80
+
81
+ type ErasedPluginDefinition<TServices extends object> =
82
+ | KeyArrayPluginDefinition<TServices, ServiceKeys<TServices>, ServiceRecord & NoReservedKeys>
83
+ | WitnessPluginDefinition<TServices, NeedsShape & NoReservedKeys, ServiceRecord & NoReservedKeys>;
84
+
85
+ type KernelPluginDefinition = Parameters<
86
+ typeof definePlugin<NeedsShape & NoReservedKeys, ServiceRecord & NoReservedKeys>
87
+ >[0];
88
+
89
+ function needsFor<TServices extends object, const TKeys extends ServiceKeys<TServices>>(
90
+ keys: TKeys,
91
+ ): WitnessesFor<TServices, TKeys> {
92
+ const needs: Record<string, Service<unknown>> = {};
93
+ for (const key of keys) {
94
+ needs[key] = service<TServices[typeof key]>();
95
+ }
96
+ return needs as WitnessesFor<TServices, TKeys>;
97
+ }
98
+
99
+ function isServiceKeyArray<TServices extends object>(
100
+ needs: ErasedPluginDefinition<TServices>['needs'],
101
+ ): needs is ServiceKeys<TServices> {
102
+ return Array.isArray(needs);
103
+ }
104
+
105
+ function createPluginFactory<TServices extends object>(): InitPluginsFactory<TServices> {
106
+ const factory = {
107
+ plugin(def: ErasedPluginDefinition<TServices>): Plugin<ServiceRecord, ServiceRecord> {
108
+ const rawNeeds = def.needs;
109
+ const needs = isServiceKeyArray<TServices>(rawNeeds)
110
+ ? needsFor<TServices, ServiceKeys<TServices>>(rawNeeds)
111
+ : rawNeeds;
112
+ // Factory seam: key-array authoring is runtime-compiled to the same
113
+ // witness record that the kernel already understands.
114
+ const kernelDef: KernelPluginDefinition = {
115
+ name: def.name,
116
+ ...(def.version === undefined ? {} : { version: def.version }),
117
+ ...(needs === undefined ? {} : { needs }),
118
+ ...(def.actions === undefined ? {} : { actions: def.actions }),
119
+ ...(def.configure === undefined ? {} : { configure: def.configure }),
120
+ ...(def.boot === undefined ? {} : { boot: def.boot as KernelPluginDefinition['boot'] }),
121
+ ...(def.start === undefined ? {} : { start: def.start as KernelPluginDefinition['start'] }),
122
+ ...(def.stop === undefined ? {} : { stop: def.stop as KernelPluginDefinition['stop'] }),
123
+ ...(def.dispose === undefined ? {} : { dispose: def.dispose as KernelPluginDefinition['dispose'] }),
124
+ };
125
+ return definePlugin(kernelDef) as unknown as Plugin<ServiceRecord, ServiceRecord>;
126
+ },
127
+ needs<const TKeys extends ServiceKeys<TServices>>(...keys: TKeys): WitnessesFor<TServices, TKeys> {
128
+ return needsFor<TServices, typeof keys>(keys);
129
+ },
130
+ };
131
+ return factory as InitPluginsFactory<TServices>;
132
+ }
133
+
134
+ export const initPlugins = {
135
+ context<TServices extends object>() {
136
+ return {
137
+ create(): InitPluginsFactory<TServices> {
138
+ return createPluginFactory<TServices>();
139
+ },
140
+ };
141
+ },
142
+ };