@smthrs/plugin 0.0.0-stage → 1.0.0-rc.12

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 (118) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/LICENSE +21 -0
  3. package/README.md +88 -2
  4. package/dist/cjs/Config.d.ts +129 -0
  5. package/dist/cjs/Config.d.ts.map +1 -0
  6. package/dist/cjs/Config.js +115 -0
  7. package/dist/cjs/Config.js.map +7 -0
  8. package/dist/cjs/Hooks.d.ts +176 -0
  9. package/dist/cjs/Hooks.d.ts.map +1 -0
  10. package/dist/cjs/Hooks.js +34 -0
  11. package/dist/cjs/Hooks.js.map +7 -0
  12. package/dist/cjs/Kernel.d.ts +81 -0
  13. package/dist/cjs/Kernel.d.ts.map +1 -0
  14. package/dist/cjs/Kernel.js +57 -0
  15. package/dist/cjs/Kernel.js.map +7 -0
  16. package/dist/cjs/Plugin.d.ts +71 -0
  17. package/dist/cjs/Plugin.d.ts.map +1 -0
  18. package/dist/cjs/Plugin.js +25 -0
  19. package/dist/cjs/Plugin.js.map +7 -0
  20. package/dist/cjs/PluginError.d.ts +50 -0
  21. package/dist/cjs/PluginError.d.ts.map +1 -0
  22. package/dist/cjs/PluginError.js +58 -0
  23. package/dist/cjs/PluginError.js.map +7 -0
  24. package/dist/cjs/Plugins.d.ts +97 -0
  25. package/dist/cjs/Plugins.d.ts.map +1 -0
  26. package/dist/cjs/Plugins.js +169 -0
  27. package/dist/cjs/Plugins.js.map +7 -0
  28. package/dist/cjs/Resolve.d.ts +128 -0
  29. package/dist/cjs/Resolve.d.ts.map +1 -0
  30. package/dist/cjs/Resolve.js +346 -0
  31. package/dist/cjs/Resolve.js.map +7 -0
  32. package/dist/cjs/index.d.ts +83 -0
  33. package/dist/cjs/index.d.ts.map +1 -0
  34. package/dist/cjs/index.js +45 -0
  35. package/dist/cjs/index.js.map +7 -0
  36. package/dist/cjs/internal/Boundary.d.ts +96 -0
  37. package/dist/cjs/internal/Boundary.d.ts.map +1 -0
  38. package/dist/cjs/internal/Boundary.js +129 -0
  39. package/dist/cjs/internal/Boundary.js.map +7 -0
  40. package/dist/cjs/internal/mergePluginLayers.d.ts +21 -0
  41. package/dist/cjs/internal/mergePluginLayers.d.ts.map +1 -0
  42. package/dist/cjs/internal/mergePluginLayers.js +57 -0
  43. package/dist/cjs/internal/mergePluginLayers.js.map +7 -0
  44. package/dist/cjs/internal/snapshotCacheEnvironment.d.ts +23 -0
  45. package/dist/cjs/internal/snapshotCacheEnvironment.d.ts.map +1 -0
  46. package/dist/cjs/internal/snapshotCacheEnvironment.js +92 -0
  47. package/dist/cjs/internal/snapshotCacheEnvironment.js.map +7 -0
  48. package/dist/cjs/package.json +1 -0
  49. package/dist/esm/Config.d.ts +129 -0
  50. package/dist/esm/Config.d.ts.map +1 -0
  51. package/dist/esm/Config.js +160 -0
  52. package/dist/esm/Config.js.map +1 -0
  53. package/dist/esm/Hooks.d.ts +176 -0
  54. package/dist/esm/Hooks.d.ts.map +1 -0
  55. package/dist/esm/Hooks.js +56 -0
  56. package/dist/esm/Hooks.js.map +1 -0
  57. package/dist/esm/Kernel.d.ts +81 -0
  58. package/dist/esm/Kernel.d.ts.map +1 -0
  59. package/dist/esm/Kernel.js +58 -0
  60. package/dist/esm/Kernel.js.map +1 -0
  61. package/dist/esm/Plugin.d.ts +71 -0
  62. package/dist/esm/Plugin.d.ts.map +1 -0
  63. package/dist/esm/Plugin.js +20 -0
  64. package/dist/esm/Plugin.js.map +1 -0
  65. package/dist/esm/PluginError.d.ts +50 -0
  66. package/dist/esm/PluginError.d.ts.map +1 -0
  67. package/dist/esm/PluginError.js +53 -0
  68. package/dist/esm/PluginError.js.map +1 -0
  69. package/dist/esm/Plugins.d.ts +97 -0
  70. package/dist/esm/Plugins.d.ts.map +1 -0
  71. package/dist/esm/Plugins.js +190 -0
  72. package/dist/esm/Plugins.js.map +1 -0
  73. package/dist/esm/Resolve.d.ts +128 -0
  74. package/dist/esm/Resolve.d.ts.map +1 -0
  75. package/dist/esm/Resolve.js +369 -0
  76. package/dist/esm/Resolve.js.map +1 -0
  77. package/dist/esm/index.d.ts +83 -0
  78. package/dist/esm/index.d.ts.map +1 -0
  79. package/dist/esm/index.js +48 -0
  80. package/dist/esm/index.js.map +1 -0
  81. package/dist/esm/internal/Boundary.d.ts +96 -0
  82. package/dist/esm/internal/Boundary.d.ts.map +1 -0
  83. package/dist/esm/internal/Boundary.js +162 -0
  84. package/dist/esm/internal/Boundary.js.map +1 -0
  85. package/dist/esm/internal/mergePluginLayers.d.ts +21 -0
  86. package/dist/esm/internal/mergePluginLayers.d.ts.map +1 -0
  87. package/dist/esm/internal/mergePluginLayers.js +41 -0
  88. package/dist/esm/internal/mergePluginLayers.js.map +1 -0
  89. package/dist/esm/internal/snapshotCacheEnvironment.d.ts +23 -0
  90. package/dist/esm/internal/snapshotCacheEnvironment.d.ts.map +1 -0
  91. package/dist/esm/internal/snapshotCacheEnvironment.js +64 -0
  92. package/dist/esm/internal/snapshotCacheEnvironment.js.map +1 -0
  93. package/docs/README.md +133 -0
  94. package/docs/api.md +524 -0
  95. package/docs/concepts/configuration.md +111 -0
  96. package/docs/concepts/hook-kinds.md +107 -0
  97. package/docs/concepts/resolution.md +132 -0
  98. package/docs/guides/cache-identity.md +106 -0
  99. package/docs/guides/contribute-services.md +84 -0
  100. package/docs/guides/host-your-own-hooks.md +129 -0
  101. package/docs/guides/order-handlers.md +76 -0
  102. package/docs/guides/select-plugins.md +72 -0
  103. package/docs/guides/testing.md +149 -0
  104. package/docs/installation.md +75 -0
  105. package/docs/quickstart.md +152 -0
  106. package/docs/troubleshooting.md +180 -0
  107. package/package.json +179 -3
  108. package/src/Config.ts +207 -0
  109. package/src/Hooks.ts +212 -0
  110. package/src/Kernel.ts +119 -0
  111. package/src/Plugin.ts +80 -0
  112. package/src/PluginError.ts +62 -0
  113. package/src/Plugins.ts +271 -0
  114. package/src/Resolve.ts +539 -0
  115. package/src/index.ts +91 -0
  116. package/src/internal/Boundary.ts +252 -0
  117. package/src/internal/mergePluginLayers.ts +57 -0
  118. package/src/internal/snapshotCacheEnvironment.ts +78 -0
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Bounded JSON configuration consumed and produced by the plugin kernel.
3
+ *
4
+ * Configuration is an extension namespace, not an engine-policy surface.
5
+ * Durable retry, concurrency, and storage policy stay on their owning Effect
6
+ * services and constructor options. Every accepted value is copied before any
7
+ * plugin observes it, then frozen recursively.
8
+ *
9
+ * @since 1.0.0-rc.0
10
+ */
11
+ import * as Effect from "effect/Effect";
12
+ import * as Schema from "effect/Schema";
13
+ import * as SchemaGetter from "effect/SchemaGetter";
14
+ import * as SchemaIssue from "effect/SchemaIssue";
15
+ import * as Boundary from "./internal/Boundary.js";
16
+ import { PluginError } from "./PluginError.js";
17
+ /**
18
+ * Maximum encoded bytes accepted for one plugin configuration.
19
+ *
20
+ * @category limits
21
+ * @since 1.0.0-rc.0
22
+ */
23
+ export const maximumConfigBytes = Boundary.defaultLimits.maxBytes;
24
+ /**
25
+ * Maximum nesting accepted for one plugin configuration.
26
+ *
27
+ * @category limits
28
+ * @since 1.0.0-rc.0
29
+ */
30
+ export const maximumConfigDepth = Boundary.defaultLimits.maxDepth;
31
+ /**
32
+ * Maximum aggregate members accepted for one plugin configuration.
33
+ *
34
+ * @category limits
35
+ * @since 1.0.0-rc.0
36
+ */
37
+ export const maximumConfigMembers = Boundary.defaultLimits.maxMembers;
38
+ /**
39
+ * Maximum aggregate values accepted for one plugin configuration.
40
+ *
41
+ * @category limits
42
+ * @since 1.0.0-rc.0
43
+ */
44
+ export const maximumConfigNodes = Boundary.defaultLimits.maxNodes;
45
+ /**
46
+ * One value accepted in a plugin-owned configuration namespace.
47
+ *
48
+ * @category models
49
+ * @since 1.0.0-rc.0
50
+ */
51
+ export const ConfigValue = Schema.Json;
52
+ const admittedConfigs = new WeakSet();
53
+ const configurationSchema = Schema.Unknown.pipe(Schema.decodeTo(Schema.declare((value) => typeof value === "object" && value !== null && admittedConfigs.has(value)), {
54
+ decode: SchemaGetter.transformEffect((value) => Effect.try({
55
+ try: () => snapshotRecord(value),
56
+ catch: (cause) => {
57
+ const error = cause;
58
+ return new SchemaIssue.InvalidValue({ message: `${error.path}: ${error.message}` });
59
+ }
60
+ })),
61
+ encode: SchemaGetter.transform((value) => value)
62
+ }));
63
+ /**
64
+ * Admits pre-resolution configuration as a detached, recursively frozen snapshot.
65
+ *
66
+ * Keys name plugin-owned namespaces. `engine`, `retry`, `store`, and
67
+ * `plugins` are refused because accepting those names would imply policy the
68
+ * plugin kernel does not own or apply.
69
+ *
70
+ * @category models
71
+ * @since 1.0.0-rc.0
72
+ */
73
+ export const FlowsConfig = configurationSchema;
74
+ /**
75
+ * Admits the detached, recursively frozen configuration handed to plugins.
76
+ *
77
+ * @category models
78
+ * @since 1.0.0-rc.0
79
+ */
80
+ export const ResolvedConfig = configurationSchema;
81
+ const reservedRootKeys = new Set(["engine", "retry", "store", "plugins"]);
82
+ const invalid = (path, complaint) => new PluginError({
83
+ code: "config_invalid",
84
+ message: `plugin configuration ${complaint}`,
85
+ path
86
+ });
87
+ const snapshotRecord = (input, reuse = true) => {
88
+ if (reuse && typeof input === "object" && input !== null && admittedConfigs.has(input))
89
+ return input;
90
+ const admitted = Boundary.record(input);
91
+ if (!admitted.ok)
92
+ throw invalid(admitted.path, admitted.complaint);
93
+ const record = admitted.value;
94
+ for (const key of Object.keys(record)) {
95
+ if (reservedRootKeys.has(key)) {
96
+ throw invalid(`$.${key}`, "uses a runtime-policy key that the plugin kernel does not own");
97
+ }
98
+ }
99
+ admittedConfigs.add(record);
100
+ return record;
101
+ };
102
+ /**
103
+ * Empty defaults. Engine policy is deliberately not defaulted here.
104
+ *
105
+ * @category constructors
106
+ * @since 1.0.0-rc.0
107
+ */
108
+ export const defaults = snapshotRecord({});
109
+ /**
110
+ * Copies and deep-merges a configuration patch over a base configuration.
111
+ *
112
+ * Records merge key-by-key and every other JSON value replaces wholesale.
113
+ * Raw operands are admitted; known snapshots retain unchanged subtrees.
114
+ * Cached subtree totals enforce merged output bounds. Unsafe property names,
115
+ * accessors, cycles, exotic prototypes, and aggregate values outside the
116
+ * resource limits fail with `config_invalid`.
117
+ *
118
+ * @category combinators
119
+ * @since 1.0.0-rc.0
120
+ */
121
+ export const merge = (base, patch) => {
122
+ const safeBase = snapshotRecord(base);
123
+ if (patch === undefined)
124
+ return safeBase;
125
+ // Even a known patch is copied so its tree cannot alias the retained base.
126
+ const safePatch = snapshotRecord(patch, false);
127
+ const merged = Boundary.mergeRecords(safeBase, safePatch);
128
+ if (!merged.ok)
129
+ throw invalid(merged.path, merged.complaint);
130
+ const record = merged.value;
131
+ admittedConfigs.add(record);
132
+ return record;
133
+ };
134
+ /**
135
+ * Copies and freezes one JSON value without retaining caller-owned objects.
136
+ *
137
+ * @category utils
138
+ * @since 1.0.0-rc.0
139
+ */
140
+ export const deepFreeze = (value) => {
141
+ const admitted = Boundary.admit(value);
142
+ if (!admitted.ok)
143
+ throw invalid(admitted.path, admitted.complaint);
144
+ return admitted.value;
145
+ };
146
+ /**
147
+ * Admits a raw pre-resolution configuration as an immutable snapshot.
148
+ *
149
+ * @category constructors
150
+ * @since 1.0.0-rc.0
151
+ */
152
+ export const snapshot = (config) => Effect.try({ try: () => snapshotRecord(config), catch: (cause) => cause });
153
+ /**
154
+ * Decodes the post-waterfall configuration into its final immutable form.
155
+ *
156
+ * @category constructors
157
+ * @since 1.0.0-rc.0
158
+ */
159
+ export const resolve = (config) => snapshot(config);
160
+ //# sourceMappingURL=Config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Config.js","sourceRoot":"","sources":["../../src/Config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bounded JSON configuration consumed and produced by the plugin kernel.\n *\n * Configuration is an extension namespace, not an engine-policy surface.\n * Durable retry, concurrency, and storage policy stay on their owning Effect\n * services and constructor options. Every accepted value is copied before any\n * plugin observes it, then frozen recursively.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaGetter from \"effect/SchemaGetter\"\nimport * as SchemaIssue from \"effect/SchemaIssue\"\nimport * as Boundary from \"./internal/Boundary.ts\"\nimport { PluginError } from \"./PluginError.ts\"\n\n/**\n * Maximum encoded bytes accepted for one plugin configuration.\n *\n * @category limits\n * @since 1.0.0-rc.0\n */\nexport const maximumConfigBytes = Boundary.defaultLimits.maxBytes\n\n/**\n * Maximum nesting accepted for one plugin configuration.\n *\n * @category limits\n * @since 1.0.0-rc.0\n */\nexport const maximumConfigDepth = Boundary.defaultLimits.maxDepth\n\n/**\n * Maximum aggregate members accepted for one plugin configuration.\n *\n * @category limits\n * @since 1.0.0-rc.0\n */\nexport const maximumConfigMembers = Boundary.defaultLimits.maxMembers\n\n/**\n * Maximum aggregate values accepted for one plugin configuration.\n *\n * @category limits\n * @since 1.0.0-rc.0\n */\nexport const maximumConfigNodes = Boundary.defaultLimits.maxNodes\n\n/**\n * One value accepted in a plugin-owned configuration namespace.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport const ConfigValue = Schema.Json\n\n/**\n * One value accepted in a plugin-owned configuration namespace.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ConfigValue = typeof ConfigValue.Type\n\nconst admittedConfigs = new WeakSet<object>()\n\nconst configurationSchema = Schema.Unknown.pipe(\n Schema.decodeTo(\n Schema.declare<Readonly<Record<string, ConfigValue>>>((value): value is Readonly<Record<string, ConfigValue>> =>\n typeof value === \"object\" && value !== null && admittedConfigs.has(value)\n ),\n {\n decode: SchemaGetter.transformEffect((value) =>\n Effect.try({\n try: () => snapshotRecord(value),\n catch: (cause) => {\n const error = cause as PluginError\n return new SchemaIssue.InvalidValue({ message: `${error.path}: ${error.message}` })\n }\n })\n ),\n encode: SchemaGetter.transform((value) => value)\n }\n )\n)\n\n/**\n * Admits pre-resolution configuration as a detached, recursively frozen snapshot.\n *\n * Keys name plugin-owned namespaces. `engine`, `retry`, `store`, and\n * `plugins` are refused because accepting those names would imply policy the\n * plugin kernel does not own or apply.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport const FlowsConfig = configurationSchema\n\n/**\n * The decoded form of {@link FlowsConfig}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type FlowsConfig = typeof FlowsConfig.Type\n\n/**\n * Admits the detached, recursively frozen configuration handed to plugins.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport const ResolvedConfig = configurationSchema\n\n/**\n * The decoded form of {@link ResolvedConfig}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ResolvedConfig = typeof ResolvedConfig.Type\n\nconst reservedRootKeys = new Set([\"engine\", \"retry\", \"store\", \"plugins\"])\n\nconst invalid = (path: string, complaint: string): PluginError =>\n new PluginError({\n code: \"config_invalid\",\n message: `plugin configuration ${complaint}`,\n path\n })\n\nconst snapshotRecord = (input: unknown, reuse = true): Readonly<Record<string, ConfigValue>> => {\n if (reuse && typeof input === \"object\" && input !== null && admittedConfigs.has(input)) return input as ResolvedConfig\n const admitted = Boundary.record(input)\n if (!admitted.ok) throw invalid(admitted.path, admitted.complaint)\n const record = admitted.value as Readonly<Record<string, ConfigValue>>\n for (const key of Object.keys(record)) {\n if (reservedRootKeys.has(key)) {\n throw invalid(`$.${key}`, \"uses a runtime-policy key that the plugin kernel does not own\")\n }\n }\n admittedConfigs.add(record)\n return record\n}\n\n/**\n * Empty defaults. Engine policy is deliberately not defaulted here.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const defaults: ResolvedConfig = snapshotRecord({})\n\n/**\n * Copies and deep-merges a configuration patch over a base configuration.\n *\n * Records merge key-by-key and every other JSON value replaces wholesale.\n * Raw operands are admitted; known snapshots retain unchanged subtrees.\n * Cached subtree totals enforce merged output bounds. Unsafe property names,\n * accessors, cycles, exotic prototypes, and aggregate values outside the\n * resource limits fail with `config_invalid`.\n *\n * @category combinators\n * @since 1.0.0-rc.0\n */\nexport const merge = (base: FlowsConfig, patch: unknown): FlowsConfig => {\n const safeBase = snapshotRecord(base)\n if (patch === undefined) return safeBase\n // Even a known patch is copied so its tree cannot alias the retained base.\n const safePatch = snapshotRecord(patch, false)\n const merged = Boundary.mergeRecords(safeBase, safePatch)\n if (!merged.ok) throw invalid(merged.path, merged.complaint)\n const record = merged.value as ResolvedConfig\n admittedConfigs.add(record)\n return record\n}\n\n/**\n * Copies and freezes one JSON value without retaining caller-owned objects.\n *\n * @category utils\n * @since 1.0.0-rc.0\n */\nexport const deepFreeze = <A extends ConfigValue>(value: A): A => {\n const admitted = Boundary.admit(value)\n if (!admitted.ok) throw invalid(admitted.path, admitted.complaint)\n return admitted.value as A\n}\n\n/**\n * Admits a raw pre-resolution configuration as an immutable snapshot.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const snapshot = (config: unknown): Effect.Effect<FlowsConfig, PluginError> =>\n Effect.try({ try: () => snapshotRecord(config), catch: (cause) => cause as PluginError })\n\n/**\n * Decodes the post-waterfall configuration into its final immutable form.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const resolve = (config: unknown): Effect.Effect<ResolvedConfig, PluginError> => snapshot(config)\n"]}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * The typed hook surface: hook kinds, the hook entry shape, and the shared
3
+ * kernel's base hook catalog.
4
+ *
5
+ * The public package contract is documented at
6
+ * {@link https://smithers.sh/docs/reference/api/plugin}. The shared catalog is limited to
7
+ * `config` and `configResolved`;
8
+ * `@smthrs/agent` adds only `cellRegistry`, `cellFlows`, and
9
+ * `cellModelRequest`. Durable-core lifecycle policy is not a hook catalog.
10
+ *
11
+ * `FlowsHooks` is **open for augmentation, closed for dispatch**: a host
12
+ * declares its hooks with `declare module "@smthrs/plugin"`, supplies the
13
+ * matching runtime catalog, and dispatches only that bounded set.
14
+ *
15
+ * @since 1.0.0-rc.0
16
+ */
17
+ import type * as Effect from "effect/Effect";
18
+ /**
19
+ * Dispatch semantics of a hook, fixed by the core.
20
+ *
21
+ * @category models
22
+ * @since 1.0.0-rc.0
23
+ */
24
+ export type HookKind = "sequential" | "parallel" | "first" | "waterfall";
25
+ declare const HookTypeId: unique symbol;
26
+ /**
27
+ * Phantom carrier that records a hook's kind and handler type in the type
28
+ * system without adding anything at runtime.
29
+ *
30
+ * @category models
31
+ * @since 1.0.0-rc.0
32
+ */
33
+ export interface HookMeta<K extends HookKind, F> {
34
+ readonly [HookTypeId]?: {
35
+ readonly kind: K;
36
+ readonly handler: F;
37
+ };
38
+ }
39
+ /**
40
+ * The object form of a hook entry: Vite's per-hook ordering object, verbatim.
41
+ *
42
+ * @category models
43
+ * @since 1.0.0-rc.0
44
+ */
45
+ export interface HookObject<F> {
46
+ readonly order?: "pre" | "post" | undefined;
47
+ readonly handler: F;
48
+ }
49
+ /**
50
+ * A hook entry is either the bare handler or `{ order?, handler }`.
51
+ *
52
+ * @category models
53
+ * @since 1.0.0-rc.0
54
+ */
55
+ export type HookEntry<K extends HookKind, F> = (F | HookObject<F>) & HookMeta<K, F>;
56
+ /**
57
+ * Every handler runs, in resolved order, one at a time.
58
+ *
59
+ * @category models
60
+ * @since 1.0.0-rc.0
61
+ */
62
+ export type SequentialHook<F> = HookEntry<"sequential", F>;
63
+ /**
64
+ * Every handler runs concurrently; results are ignored and failures are
65
+ * returned to the caller rather than failing it.
66
+ *
67
+ * @category models
68
+ * @since 1.0.0-rc.0
69
+ */
70
+ export type ParallelHook<F> = HookEntry<"parallel", F>;
71
+ /**
72
+ * Handlers run in order until one returns `Option.some`.
73
+ *
74
+ * @category models
75
+ * @since 1.0.0-rc.0
76
+ */
77
+ export type FirstHook<F> = HookEntry<"first", F>;
78
+ /**
79
+ * Each handler receives the previous handler's output.
80
+ *
81
+ * @category models
82
+ * @since 1.0.0-rc.0
83
+ */
84
+ export type WaterfallHook<F> = HookEntry<"waterfall", F>;
85
+ /**
86
+ * Extracts the declared kind of a hook entry type.
87
+ *
88
+ * @category type-level
89
+ * @since 1.0.0-rc.0
90
+ */
91
+ export type KindOf<T> = T extends HookMeta<infer K, any> ? K : never;
92
+ /**
93
+ * The runtime catalog a host supplies for a hook interface: each hook name
94
+ * mapped to its kind. A name declared in `H` must carry the kind its type
95
+ * declares. Names outside `H` are admitted with any kind, because the type
96
+ * system cannot enumerate an augmentable interface; resolution checks them
97
+ * at runtime and dispatch refuses a kind that disagrees with the catalog.
98
+ *
99
+ * @category type-level
100
+ * @since 1.0.0-rc.0
101
+ */
102
+ export type HookCatalog<H> = {
103
+ readonly [K in keyof H & string]?: KindOf<H[K]>;
104
+ } & {
105
+ readonly [name: string]: HookKind | undefined;
106
+ };
107
+ /**
108
+ * Extracts the handler type of a hook entry type.
109
+ *
110
+ * @category type-level
111
+ * @since 1.0.0-rc.0
112
+ */
113
+ export type HandlerOf<T> = T extends HookMeta<any, infer F> ? F : never;
114
+ /**
115
+ * Selects the hook names of a given kind from a hook interface.
116
+ *
117
+ * @category type-level
118
+ * @since 1.0.0-rc.0
119
+ */
120
+ export type KeysOfKind<H, K extends HookKind> = {
121
+ [P in keyof H]: KindOf<H[P]> extends K ? P : never;
122
+ }[keyof H] & string;
123
+ /**
124
+ * Extracts the positional argument tuple accepted by a hook entry.
125
+ *
126
+ * @category type-level
127
+ * @since 1.0.0-rc.0
128
+ */
129
+ export type ArgsOf<T> = HandlerOf<T> extends (...args: infer A) => any ? A : never;
130
+ /**
131
+ * Extracts the Effect returned by a hook entry.
132
+ *
133
+ * @category type-level
134
+ * @since 1.0.0-rc.0
135
+ */
136
+ export type ReturnOf<T> = HandlerOf<T> extends (...args: any) => infer R ? R : never;
137
+ /**
138
+ * Extracts the success value returned by a hook entry's Effect.
139
+ *
140
+ * @category type-level
141
+ * @since 1.0.0-rc.0
142
+ */
143
+ export type SuccessOf<T> = ReturnOf<T> extends Effect.Effect<infer A, any, any> ? A : never;
144
+ /**
145
+ * Extracts the context required by a hook entry's Effect.
146
+ *
147
+ * @category type-level
148
+ * @since 1.0.0-rc.0
149
+ */
150
+ export type ContextOf<T> = ReturnOf<T> extends Effect.Effect<any, any, infer R> ? R : never;
151
+ /**
152
+ * Frozen runtime catalog for the shared configuration hooks.
153
+ *
154
+ * @category models
155
+ * @since 1.0.0-rc.0
156
+ */
157
+ export declare const engineHooks: Readonly<{
158
+ readonly config: "waterfall";
159
+ readonly configResolved: "parallel";
160
+ }>;
161
+ /**
162
+ * Normalizes a hook entry to its handler.
163
+ *
164
+ * @category utils
165
+ * @since 1.0.0-rc.0
166
+ */
167
+ export declare const handlerOf: (entry: unknown) => (...args: Array<any>) => unknown;
168
+ /**
169
+ * Reads the per-hook `order` of a hook entry.
170
+ *
171
+ * @category utils
172
+ * @since 1.0.0-rc.0
173
+ */
174
+ export declare const orderOf: (entry: unknown) => "pre" | "post" | undefined;
175
+ export {};
176
+ //# sourceMappingURL=Hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Hooks.d.ts","sourceRoot":"","sources":["../../src/Hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The typed hook surface: hook kinds, the hook entry shape, and the shared
3
+ * kernel's base hook catalog.
4
+ *
5
+ * The public package contract is documented at
6
+ * {@link https://smithers.sh/docs/reference/api/plugin}. The shared catalog is limited to
7
+ * `config` and `configResolved`;
8
+ * `@smthrs/agent` adds only `cellRegistry`, `cellFlows`, and
9
+ * `cellModelRequest`. Durable-core lifecycle policy is not a hook catalog.
10
+ *
11
+ * `FlowsHooks` is **open for augmentation, closed for dispatch**: a host
12
+ * declares its hooks with `declare module "@smthrs/plugin"`, supplies the
13
+ * matching runtime catalog, and dispatches only that bounded set.
14
+ *
15
+ * @since 1.0.0-rc.0
16
+ */
17
+ /**
18
+ * Runtime catalog of the shared kernel's hook names and kinds.
19
+ *
20
+ * The type system cannot enumerate an augmentable interface, so this constant
21
+ * is what the `unknown_hook` guard checks against. A host passes its own
22
+ * superset to {@link resolve}. The cell harness adds only the hooks it
23
+ * dispatches; lifecycle and engine-policy hooks do not belong here.
24
+ *
25
+ * @category models
26
+ * @since 1.0.0-rc.0
27
+ */
28
+ const sharedHooks = Object.create(null);
29
+ Object.defineProperties(sharedHooks, {
30
+ config: { value: "waterfall", enumerable: true },
31
+ configResolved: { value: "parallel", enumerable: true }
32
+ });
33
+ /**
34
+ * Frozen runtime catalog for the shared configuration hooks.
35
+ *
36
+ * @category models
37
+ * @since 1.0.0-rc.0
38
+ */
39
+ export const engineHooks = Object.freeze(sharedHooks);
40
+ /**
41
+ * Normalizes a hook entry to its handler.
42
+ *
43
+ * @category utils
44
+ * @since 1.0.0-rc.0
45
+ */
46
+ export const handlerOf = (entry) => typeof entry === "function"
47
+ ? entry
48
+ : entry.handler;
49
+ /**
50
+ * Reads the per-hook `order` of a hook entry.
51
+ *
52
+ * @category utils
53
+ * @since 1.0.0-rc.0
54
+ */
55
+ export const orderOf = (entry) => typeof entry === "function" ? undefined : entry.order;
56
+ //# sourceMappingURL=Hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Hooks.js","sourceRoot":"","sources":["../../src/Hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAuJH;;;;;;;;;;GAUG;AACH,MAAM,WAAW,GAAwD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AAC5F,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE;IACnC,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE;IAChD,cAAc,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE;CACxD,CAAC,CAAA;AACF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAGnB,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;AAE/B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAc,EAAoC,EAAE,CAC5E,OAAO,KAAK,KAAK,UAAU;IACzB,CAAC,CAAC,KAAyC;IAC3C,CAAC,CAAE,KAAsD,CAAC,OAAO,CAAA;AAErE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAc,EAA8B,EAAE,CACpE,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,KAA6B,CAAC,KAAK,CAAA","sourcesContent":["/**\n * The typed hook surface: hook kinds, the hook entry shape, and the shared\n * kernel's base hook catalog.\n *\n * The public package contract is documented at\n * {@link https://smithers.sh/docs/reference/api/plugin}. The shared catalog is limited to\n * `config` and `configResolved`;\n * `@smthrs/agent` adds only `cellRegistry`, `cellFlows`, and\n * `cellModelRequest`. Durable-core lifecycle policy is not a hook catalog.\n *\n * `FlowsHooks` is **open for augmentation, closed for dispatch**: a host\n * declares its hooks with `declare module \"@smthrs/plugin\"`, supplies the\n * matching runtime catalog, and dispatches only that bounded set.\n *\n * @since 1.0.0-rc.0\n */\n\nimport type * as Effect from \"effect/Effect\"\n\n/**\n * Dispatch semantics of a hook, fixed by the core.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type HookKind = \"sequential\" | \"parallel\" | \"first\" | \"waterfall\"\n\ndeclare const HookTypeId: unique symbol\n\n/**\n * Phantom carrier that records a hook's kind and handler type in the type\n * system without adding anything at runtime.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HookMeta<K extends HookKind, F> {\n readonly [HookTypeId]?: { readonly kind: K; readonly handler: F }\n}\n\n/**\n * The object form of a hook entry: Vite's per-hook ordering object, verbatim.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HookObject<F> {\n readonly order?: \"pre\" | \"post\" | undefined\n readonly handler: F\n}\n\n/**\n * A hook entry is either the bare handler or `{ order?, handler }`.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type HookEntry<K extends HookKind, F> = (F | HookObject<F>) & HookMeta<K, F>\n\n/**\n * Every handler runs, in resolved order, one at a time.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type SequentialHook<F> = HookEntry<\"sequential\", F>\n\n/**\n * Every handler runs concurrently; results are ignored and failures are\n * returned to the caller rather than failing it.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ParallelHook<F> = HookEntry<\"parallel\", F>\n\n/**\n * Handlers run in order until one returns `Option.some`.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type FirstHook<F> = HookEntry<\"first\", F>\n\n/**\n * Each handler receives the previous handler's output.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type WaterfallHook<F> = HookEntry<\"waterfall\", F>\n\n/**\n * Extracts the declared kind of a hook entry type.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type KindOf<T> = T extends HookMeta<infer K, any> ? K : never\n\n/**\n * The runtime catalog a host supplies for a hook interface: each hook name\n * mapped to its kind. A name declared in `H` must carry the kind its type\n * declares. Names outside `H` are admitted with any kind, because the type\n * system cannot enumerate an augmentable interface; resolution checks them\n * at runtime and dispatch refuses a kind that disagrees with the catalog.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type HookCatalog<H> =\n & { readonly [K in keyof H & string]?: KindOf<H[K]> }\n & { readonly [name: string]: HookKind | undefined }\n\n/**\n * Extracts the handler type of a hook entry type.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type HandlerOf<T> = T extends HookMeta<any, infer F> ? F : never\n\n/**\n * Selects the hook names of a given kind from a hook interface.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type KeysOfKind<H, K extends HookKind> =\n & {\n [P in keyof H]: KindOf<H[P]> extends K ? P : never\n }[keyof H]\n & string\n\n/**\n * Extracts the positional argument tuple accepted by a hook entry.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type ArgsOf<T> = HandlerOf<T> extends (...args: infer A) => any ? A : never\n\n/**\n * Extracts the Effect returned by a hook entry.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type ReturnOf<T> = HandlerOf<T> extends (...args: any) => infer R ? R : never\n\n/**\n * Extracts the success value returned by a hook entry's Effect.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type SuccessOf<T> = ReturnOf<T> extends Effect.Effect<infer A, any, any> ? A : never\n\n/**\n * Extracts the context required by a hook entry's Effect.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type ContextOf<T> = ReturnOf<T> extends Effect.Effect<any, any, infer R> ? R : never\n\n/**\n * Runtime catalog of the shared kernel's hook names and kinds.\n *\n * The type system cannot enumerate an augmentable interface, so this constant\n * is what the `unknown_hook` guard checks against. A host passes its own\n * superset to {@link resolve}. The cell harness adds only the hooks it\n * dispatches; lifecycle and engine-policy hooks do not belong here.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nconst sharedHooks: { config: \"waterfall\"; configResolved: \"parallel\" } = Object.create(null)\nObject.defineProperties(sharedHooks, {\n config: { value: \"waterfall\", enumerable: true },\n configResolved: { value: \"parallel\", enumerable: true }\n})\n/**\n * Frozen runtime catalog for the shared configuration hooks.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport const engineHooks: Readonly<{\n readonly config: \"waterfall\"\n readonly configResolved: \"parallel\"\n}> = Object.freeze(sharedHooks)\n\n/**\n * Normalizes a hook entry to its handler.\n *\n * @category utils\n * @since 1.0.0-rc.0\n */\nexport const handlerOf = (entry: unknown): (...args: Array<any>) => unknown =>\n typeof entry === \"function\"\n ? entry as (...args: Array<any>) => unknown\n : (entry as HookObject<(...args: Array<any>) => unknown>).handler\n\n/**\n * Reads the per-hook `order` of a hook entry.\n *\n * @category utils\n * @since 1.0.0-rc.0\n */\nexport const orderOf = (entry: unknown): \"pre\" | \"post\" | undefined =>\n typeof entry === \"function\" ? undefined : (entry as HookObject<unknown>).order\n"]}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Startup: resolve the plugin list, run the config pipeline, hand back a
3
+ * dispatcher, a frozen config, and the merged layer.
4
+ *
5
+ * The public package contract is documented at
6
+ * {@link https://smithers.sh/docs/reference/api/plugin}, with the shipped host catalog in
7
+ * `packages/smithers/agent/src/CellPlugin.ts`. D14 in
8
+ * `docs/pages/design-decisions.md` keeps durable policy outside these hooks.
9
+ *
10
+ * Order of operations, per the spec:
11
+ *
12
+ * 1. flatten / filter / validate / order the plugin list;
13
+ * 2. `config` waterfall: each plugin may return a partial to deep-merge;
14
+ * 3. decode and freeze into a `ResolvedConfig` (`config_invalid` on failure);
15
+ * 4. `configResolved` parallel: observers capture the final config;
16
+ * 5. plugin `layer`s merge left-to-right.
17
+ *
18
+ * @since 1.0.0-rc.0
19
+ */
20
+ import * as Effect from "effect/Effect";
21
+ import type * as Layer from "effect/Layer";
22
+ import type { FlowsConfig, ResolvedConfig } from "./Config.ts";
23
+ import type { ContextOf } from "./Hooks.ts";
24
+ import type { FlowsHooks } from "./index.ts";
25
+ import type { PluginInput } from "./Plugin.ts";
26
+ import type { PluginError } from "./PluginError.ts";
27
+ import * as Plugins from "./Plugins.ts";
28
+ import * as Resolve from "./Resolve.ts";
29
+ /**
30
+ * Everything startup produces from a plugin list and a raw config.
31
+ *
32
+ * `observerErrors` carries `configResolved` failures: parallel observers are
33
+ * lossy by contract, so they are reported rather than fatal.
34
+ *
35
+ * @category models
36
+ * @since 1.0.0-rc.0
37
+ */
38
+ export interface Kernel<H = FlowsHooks> {
39
+ readonly plugins: Plugins.Service<H>;
40
+ readonly config: ResolvedConfig;
41
+ readonly layer: Layer.Layer<any, PluginError, any>;
42
+ readonly observerErrors: ReadonlyArray<PluginError>;
43
+ }
44
+ type HookAt<H, K extends string> = K extends keyof H ? H[K] : never;
45
+ /**
46
+ * The services a hook interface's `config` hook requires at startup. `never`
47
+ * for the shared `FlowsHooks`, whose startup hooks are context-free.
48
+ *
49
+ * @category type-level
50
+ * @since 1.0.0-rc.0
51
+ */
52
+ export type ConfigContext<H> = ContextOf<HookAt<H, "config">>;
53
+ /**
54
+ * The services a hook interface's startup hooks require: the union of the
55
+ * `config` and `configResolved` requirements. `Kernel.make` cannot supply
56
+ * them itself, so they surface in its Effect and the caller provides them.
57
+ *
58
+ * @category type-level
59
+ * @since 1.0.0-rc.0
60
+ */
61
+ export type StartupContext<H> = ConfigContext<H> | ContextOf<HookAt<H, "configResolved">>;
62
+ /**
63
+ * Snapshots the config before dispatching the `config` waterfall, then
64
+ * admits the result as a frozen `ResolvedConfig`.
65
+ *
66
+ * @category constructors
67
+ * @since 1.0.0-rc.0
68
+ */
69
+ export declare const runConfig: <H = FlowsHooks>(plugins: Plugins.Service<H>, config: FlowsConfig) => Effect.Effect<ResolvedConfig, PluginError, ConfigContext<H>>;
70
+ /**
71
+ * Resolves plugins, executes the config pipeline, and merges plugin layers.
72
+ * The positional `config` is the kernel's only pre-resolution configuration;
73
+ * the options object cannot declare a second source, and a `config` key
74
+ * supplied anyway fails with `invalid_plugin` at `$options.config`.
75
+ *
76
+ * @category constructors
77
+ * @since 1.0.0-rc.0
78
+ */
79
+ export declare const make: <H = FlowsHooks>(input: PluginInput<NoInfer<H>>, config?: FlowsConfig, options?: Omit<Resolve.Options<NoInfer<H>>, "config">) => Effect.Effect<Kernel<H>, PluginError, StartupContext<H>>;
80
+ export {};
81
+ //# sourceMappingURL=Kernel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Kernel.d.ts","sourceRoot":"","sources":["../../src/Kernel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,GAAG,UAAU;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IACpC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,CAAA;IAClD,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;CACpD;AAED,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;AAEnE;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;AAE7D;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAA;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,GAAG,UAAU,EACtC,SAAS,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAC3B,QAAQ,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,CAS1D,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,GAAG,UAAU,EACjC,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC9B,SAAQ,WAAgB,EACxB,UAAS,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAM,KACxD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,CAgBtD,CAAA"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Startup: resolve the plugin list, run the config pipeline, hand back a
3
+ * dispatcher, a frozen config, and the merged layer.
4
+ *
5
+ * The public package contract is documented at
6
+ * {@link https://smithers.sh/docs/reference/api/plugin}, with the shipped host catalog in
7
+ * `packages/smithers/agent/src/CellPlugin.ts`. D14 in
8
+ * `docs/pages/design-decisions.md` keeps durable policy outside these hooks.
9
+ *
10
+ * Order of operations, per the spec:
11
+ *
12
+ * 1. flatten / filter / validate / order the plugin list;
13
+ * 2. `config` waterfall: each plugin may return a partial to deep-merge;
14
+ * 3. decode and freeze into a `ResolvedConfig` (`config_invalid` on failure);
15
+ * 4. `configResolved` parallel: observers capture the final config;
16
+ * 5. plugin `layer`s merge left-to-right.
17
+ *
18
+ * @since 1.0.0-rc.0
19
+ */
20
+ import * as Effect from "effect/Effect";
21
+ import * as Config from "./Config.js";
22
+ import * as Plugins from "./Plugins.js";
23
+ import * as Resolve from "./Resolve.js";
24
+ /**
25
+ * Snapshots the config before dispatching the `config` waterfall, then
26
+ * admits the result as a frozen `ResolvedConfig`.
27
+ *
28
+ * @category constructors
29
+ * @since 1.0.0-rc.0
30
+ */
31
+ export const runConfig = (plugins, config) => Effect.gen(function* () {
32
+ const initial = yield* Config.snapshot(config);
33
+ const merged = yield* plugins.waterfall("config", initial, Config.merge);
34
+ return yield* Config.resolve(merged);
35
+ });
36
+ /**
37
+ * Resolves plugins, executes the config pipeline, and merges plugin layers.
38
+ * The positional `config` is the kernel's only pre-resolution configuration;
39
+ * the options object cannot declare a second source, and a `config` key
40
+ * supplied anyway fails with `invalid_plugin` at `$options.config`.
41
+ *
42
+ * @category constructors
43
+ * @since 1.0.0-rc.0
44
+ */
45
+ export const make = (input, config = {}, options = {}) => Effect.gen(function* () {
46
+ const initial = yield* Config.snapshot(config);
47
+ const resolved = yield* Resolve.resolve(input, options, initial);
48
+ const dispatcher = Plugins.make(resolved);
49
+ const frozen = yield* runConfig(dispatcher, initial);
50
+ const observerErrors = yield* dispatcher.parallel("configResolved", frozen);
51
+ return Object.freeze({
52
+ plugins: dispatcher,
53
+ config: frozen,
54
+ layer: Resolve.layer(resolved),
55
+ observerErrors
56
+ });
57
+ });
58
+ //# sourceMappingURL=Kernel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Kernel.js","sourceRoot":"","sources":["../../src/Kernel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAKrC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAuCvC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,OAA2B,EAC3B,MAAmB,EAC2C,EAAE,CAChE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAE,OAAO,CAAC,SAIgC,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;IAChG,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AACtC,CAAC,CAAC,CAAA;AAEJ;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAClB,KAA8B,EAC9B,SAAsB,EAAE,EACxB,UAAuD,EAAE,EACC,EAAE,CAC5D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAI,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACnE,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAI,QAAQ,CAAC,CAAA;IAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACpD,MAAM,cAAc,GAAG,KAAK,CAAC,CAAE,UAAU,CAAC,QAG+B,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAA;IACnG,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,UAAU;QACnB,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC9B,cAAc;KACf,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["/**\n * Startup: resolve the plugin list, run the config pipeline, hand back a\n * dispatcher, a frozen config, and the merged layer.\n *\n * The public package contract is documented at\n * {@link https://smithers.sh/docs/reference/api/plugin}, with the shipped host catalog in\n * `packages/smithers/agent/src/CellPlugin.ts`. D14 in\n * `docs/pages/design-decisions.md` keeps durable policy outside these hooks.\n *\n * Order of operations, per the spec:\n *\n * 1. flatten / filter / validate / order the plugin list;\n * 2. `config` waterfall: each plugin may return a partial to deep-merge;\n * 3. decode and freeze into a `ResolvedConfig` (`config_invalid` on failure);\n * 4. `configResolved` parallel: observers capture the final config;\n * 5. plugin `layer`s merge left-to-right.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Effect from \"effect/Effect\"\nimport type * as Layer from \"effect/Layer\"\nimport type { FlowsConfig, ResolvedConfig } from \"./Config.ts\"\nimport * as Config from \"./Config.ts\"\nimport type { ContextOf } from \"./Hooks.ts\"\nimport type { FlowsHooks } from \"./index.ts\"\nimport type { PluginInput } from \"./Plugin.ts\"\nimport type { PluginError } from \"./PluginError.ts\"\nimport * as Plugins from \"./Plugins.ts\"\nimport * as Resolve from \"./Resolve.ts\"\n\n/**\n * Everything startup produces from a plugin list and a raw config.\n *\n * `observerErrors` carries `configResolved` failures: parallel observers are\n * lossy by contract, so they are reported rather than fatal.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface Kernel<H = FlowsHooks> {\n readonly plugins: Plugins.Service<H>\n readonly config: ResolvedConfig\n readonly layer: Layer.Layer<any, PluginError, any>\n readonly observerErrors: ReadonlyArray<PluginError>\n}\n\ntype HookAt<H, K extends string> = K extends keyof H ? H[K] : never\n\n/**\n * The services a hook interface's `config` hook requires at startup. `never`\n * for the shared `FlowsHooks`, whose startup hooks are context-free.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type ConfigContext<H> = ContextOf<HookAt<H, \"config\">>\n\n/**\n * The services a hook interface's startup hooks require: the union of the\n * `config` and `configResolved` requirements. `Kernel.make` cannot supply\n * them itself, so they surface in its Effect and the caller provides them.\n *\n * @category type-level\n * @since 1.0.0-rc.0\n */\nexport type StartupContext<H> = ConfigContext<H> | ContextOf<HookAt<H, \"configResolved\">>\n\n/**\n * Snapshots the config before dispatching the `config` waterfall, then\n * admits the result as a frozen `ResolvedConfig`.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const runConfig = <H = FlowsHooks>(\n plugins: Plugins.Service<H>,\n config: FlowsConfig\n): Effect.Effect<ResolvedConfig, PluginError, ConfigContext<H>> =>\n Effect.gen(function*() {\n const initial = yield* Config.snapshot(config)\n const merged = yield* (plugins.waterfall as unknown as (\n hook: string,\n initial: FlowsConfig,\n merge: (previous: FlowsConfig, patch: unknown) => FlowsConfig\n ) => Effect.Effect<FlowsConfig, PluginError, ConfigContext<H>>)(\"config\", initial, Config.merge)\n return yield* Config.resolve(merged)\n })\n\n/**\n * Resolves plugins, executes the config pipeline, and merges plugin layers.\n * The positional `config` is the kernel's only pre-resolution configuration;\n * the options object cannot declare a second source, and a `config` key\n * supplied anyway fails with `invalid_plugin` at `$options.config`.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const make = <H = FlowsHooks>(\n input: PluginInput<NoInfer<H>>,\n config: FlowsConfig = {},\n options: Omit<Resolve.Options<NoInfer<H>>, \"config\"> = {}\n): Effect.Effect<Kernel<H>, PluginError, StartupContext<H>> =>\n Effect.gen(function*() {\n const initial = yield* Config.snapshot(config)\n const resolved = yield* Resolve.resolve<H>(input, options, initial)\n const dispatcher = Plugins.make<H>(resolved)\n const frozen = yield* runConfig(dispatcher, initial)\n const observerErrors = yield* (dispatcher.parallel as unknown as (\n hook: string,\n value: ResolvedConfig\n ) => Effect.Effect<ReadonlyArray<PluginError>, never, StartupContext<H>>)(\"configResolved\", frozen)\n return Object.freeze({\n plugins: dispatcher,\n config: frozen,\n layer: Resolve.layer(resolved),\n observerErrors\n })\n })\n"]}