@smthrs/plugin 0.0.0-stage → 1.0.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/LICENSE +21 -0
- package/README.md +88 -2
- package/dist/cjs/Config.d.ts +129 -0
- package/dist/cjs/Config.d.ts.map +1 -0
- package/dist/cjs/Config.js +115 -0
- package/dist/cjs/Config.js.map +7 -0
- package/dist/cjs/Hooks.d.ts +176 -0
- package/dist/cjs/Hooks.d.ts.map +1 -0
- package/dist/cjs/Hooks.js +34 -0
- package/dist/cjs/Hooks.js.map +7 -0
- package/dist/cjs/Kernel.d.ts +81 -0
- package/dist/cjs/Kernel.d.ts.map +1 -0
- package/dist/cjs/Kernel.js +57 -0
- package/dist/cjs/Kernel.js.map +7 -0
- package/dist/cjs/Plugin.d.ts +71 -0
- package/dist/cjs/Plugin.d.ts.map +1 -0
- package/dist/cjs/Plugin.js +25 -0
- package/dist/cjs/Plugin.js.map +7 -0
- package/dist/cjs/PluginError.d.ts +50 -0
- package/dist/cjs/PluginError.d.ts.map +1 -0
- package/dist/cjs/PluginError.js +58 -0
- package/dist/cjs/PluginError.js.map +7 -0
- package/dist/cjs/Plugins.d.ts +97 -0
- package/dist/cjs/Plugins.d.ts.map +1 -0
- package/dist/cjs/Plugins.js +169 -0
- package/dist/cjs/Plugins.js.map +7 -0
- package/dist/cjs/Resolve.d.ts +128 -0
- package/dist/cjs/Resolve.d.ts.map +1 -0
- package/dist/cjs/Resolve.js +346 -0
- package/dist/cjs/Resolve.js.map +7 -0
- package/dist/cjs/index.d.ts +83 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +45 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/Boundary.d.ts +96 -0
- package/dist/cjs/internal/Boundary.d.ts.map +1 -0
- package/dist/cjs/internal/Boundary.js +129 -0
- package/dist/cjs/internal/Boundary.js.map +7 -0
- package/dist/cjs/internal/mergePluginLayers.d.ts +21 -0
- package/dist/cjs/internal/mergePluginLayers.d.ts.map +1 -0
- package/dist/cjs/internal/mergePluginLayers.js +57 -0
- package/dist/cjs/internal/mergePluginLayers.js.map +7 -0
- package/dist/cjs/internal/snapshotCacheEnvironment.d.ts +23 -0
- package/dist/cjs/internal/snapshotCacheEnvironment.d.ts.map +1 -0
- package/dist/cjs/internal/snapshotCacheEnvironment.js +92 -0
- package/dist/cjs/internal/snapshotCacheEnvironment.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/Config.d.ts +129 -0
- package/dist/esm/Config.d.ts.map +1 -0
- package/dist/esm/Config.js +160 -0
- package/dist/esm/Config.js.map +1 -0
- package/dist/esm/Hooks.d.ts +176 -0
- package/dist/esm/Hooks.d.ts.map +1 -0
- package/dist/esm/Hooks.js +56 -0
- package/dist/esm/Hooks.js.map +1 -0
- package/dist/esm/Kernel.d.ts +81 -0
- package/dist/esm/Kernel.d.ts.map +1 -0
- package/dist/esm/Kernel.js +58 -0
- package/dist/esm/Kernel.js.map +1 -0
- package/dist/esm/Plugin.d.ts +71 -0
- package/dist/esm/Plugin.d.ts.map +1 -0
- package/dist/esm/Plugin.js +20 -0
- package/dist/esm/Plugin.js.map +1 -0
- package/dist/esm/PluginError.d.ts +50 -0
- package/dist/esm/PluginError.d.ts.map +1 -0
- package/dist/esm/PluginError.js +53 -0
- package/dist/esm/PluginError.js.map +1 -0
- package/dist/esm/Plugins.d.ts +97 -0
- package/dist/esm/Plugins.d.ts.map +1 -0
- package/dist/esm/Plugins.js +190 -0
- package/dist/esm/Plugins.js.map +1 -0
- package/dist/esm/Resolve.d.ts +128 -0
- package/dist/esm/Resolve.d.ts.map +1 -0
- package/dist/esm/Resolve.js +369 -0
- package/dist/esm/Resolve.js.map +1 -0
- package/dist/esm/index.d.ts +83 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +48 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/Boundary.d.ts +96 -0
- package/dist/esm/internal/Boundary.d.ts.map +1 -0
- package/dist/esm/internal/Boundary.js +162 -0
- package/dist/esm/internal/Boundary.js.map +1 -0
- package/dist/esm/internal/mergePluginLayers.d.ts +21 -0
- package/dist/esm/internal/mergePluginLayers.d.ts.map +1 -0
- package/dist/esm/internal/mergePluginLayers.js +41 -0
- package/dist/esm/internal/mergePluginLayers.js.map +1 -0
- package/dist/esm/internal/snapshotCacheEnvironment.d.ts +23 -0
- package/dist/esm/internal/snapshotCacheEnvironment.d.ts.map +1 -0
- package/dist/esm/internal/snapshotCacheEnvironment.js +64 -0
- package/dist/esm/internal/snapshotCacheEnvironment.js.map +1 -0
- package/docs/README.md +133 -0
- package/docs/api.md +524 -0
- package/docs/concepts/configuration.md +111 -0
- package/docs/concepts/hook-kinds.md +107 -0
- package/docs/concepts/resolution.md +132 -0
- package/docs/guides/cache-identity.md +106 -0
- package/docs/guides/contribute-services.md +84 -0
- package/docs/guides/host-your-own-hooks.md +129 -0
- package/docs/guides/order-handlers.md +76 -0
- package/docs/guides/select-plugins.md +72 -0
- package/docs/guides/testing.md +149 -0
- package/docs/installation.md +75 -0
- package/docs/quickstart.md +152 -0
- package/docs/troubleshooting.md +180 -0
- package/package.json +179 -3
- package/src/Config.ts +207 -0
- package/src/Hooks.ts +212 -0
- package/src/Kernel.ts +119 -0
- package/src/Plugin.ts +80 -0
- package/src/PluginError.ts +62 -0
- package/src/Plugins.ts +271 -0
- package/src/Resolve.ts +539 -0
- package/src/index.ts +91 -0
- package/src/internal/Boundary.ts +252 -0
- package/src/internal/mergePluginLayers.ts +57 -0
- package/src/internal/snapshotCacheEnvironment.ts +78 -0
package/src/Hooks.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
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
|
+
import type * as Effect from "effect/Effect"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Dispatch semantics of a hook, fixed by the core.
|
|
22
|
+
*
|
|
23
|
+
* @category models
|
|
24
|
+
* @since 1.0.0-rc.0
|
|
25
|
+
*/
|
|
26
|
+
export type HookKind = "sequential" | "parallel" | "first" | "waterfall"
|
|
27
|
+
|
|
28
|
+
declare const HookTypeId: unique symbol
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Phantom carrier that records a hook's kind and handler type in the type
|
|
32
|
+
* system without adding anything at runtime.
|
|
33
|
+
*
|
|
34
|
+
* @category models
|
|
35
|
+
* @since 1.0.0-rc.0
|
|
36
|
+
*/
|
|
37
|
+
export interface HookMeta<K extends HookKind, F> {
|
|
38
|
+
readonly [HookTypeId]?: { readonly kind: K; readonly handler: F }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The object form of a hook entry: Vite's per-hook ordering object, verbatim.
|
|
43
|
+
*
|
|
44
|
+
* @category models
|
|
45
|
+
* @since 1.0.0-rc.0
|
|
46
|
+
*/
|
|
47
|
+
export interface HookObject<F> {
|
|
48
|
+
readonly order?: "pre" | "post" | undefined
|
|
49
|
+
readonly handler: F
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A hook entry is either the bare handler or `{ order?, handler }`.
|
|
54
|
+
*
|
|
55
|
+
* @category models
|
|
56
|
+
* @since 1.0.0-rc.0
|
|
57
|
+
*/
|
|
58
|
+
export type HookEntry<K extends HookKind, F> = (F | HookObject<F>) & HookMeta<K, F>
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Every handler runs, in resolved order, one at a time.
|
|
62
|
+
*
|
|
63
|
+
* @category models
|
|
64
|
+
* @since 1.0.0-rc.0
|
|
65
|
+
*/
|
|
66
|
+
export type SequentialHook<F> = HookEntry<"sequential", F>
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Every handler runs concurrently; results are ignored and failures are
|
|
70
|
+
* returned to the caller rather than failing it.
|
|
71
|
+
*
|
|
72
|
+
* @category models
|
|
73
|
+
* @since 1.0.0-rc.0
|
|
74
|
+
*/
|
|
75
|
+
export type ParallelHook<F> = HookEntry<"parallel", F>
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Handlers run in order until one returns `Option.some`.
|
|
79
|
+
*
|
|
80
|
+
* @category models
|
|
81
|
+
* @since 1.0.0-rc.0
|
|
82
|
+
*/
|
|
83
|
+
export type FirstHook<F> = HookEntry<"first", F>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Each handler receives the previous handler's output.
|
|
87
|
+
*
|
|
88
|
+
* @category models
|
|
89
|
+
* @since 1.0.0-rc.0
|
|
90
|
+
*/
|
|
91
|
+
export type WaterfallHook<F> = HookEntry<"waterfall", F>
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Extracts the declared kind of a hook entry type.
|
|
95
|
+
*
|
|
96
|
+
* @category type-level
|
|
97
|
+
* @since 1.0.0-rc.0
|
|
98
|
+
*/
|
|
99
|
+
export type KindOf<T> = T extends HookMeta<infer K, any> ? K : never
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The runtime catalog a host supplies for a hook interface: each hook name
|
|
103
|
+
* mapped to its kind. A name declared in `H` must carry the kind its type
|
|
104
|
+
* declares. Names outside `H` are admitted with any kind, because the type
|
|
105
|
+
* system cannot enumerate an augmentable interface; resolution checks them
|
|
106
|
+
* at runtime and dispatch refuses a kind that disagrees with the catalog.
|
|
107
|
+
*
|
|
108
|
+
* @category type-level
|
|
109
|
+
* @since 1.0.0-rc.0
|
|
110
|
+
*/
|
|
111
|
+
export type HookCatalog<H> =
|
|
112
|
+
& { readonly [K in keyof H & string]?: KindOf<H[K]> }
|
|
113
|
+
& { readonly [name: string]: HookKind | undefined }
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Extracts the handler type of a hook entry type.
|
|
117
|
+
*
|
|
118
|
+
* @category type-level
|
|
119
|
+
* @since 1.0.0-rc.0
|
|
120
|
+
*/
|
|
121
|
+
export type HandlerOf<T> = T extends HookMeta<any, infer F> ? F : never
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Selects the hook names of a given kind from a hook interface.
|
|
125
|
+
*
|
|
126
|
+
* @category type-level
|
|
127
|
+
* @since 1.0.0-rc.0
|
|
128
|
+
*/
|
|
129
|
+
export type KeysOfKind<H, K extends HookKind> =
|
|
130
|
+
& {
|
|
131
|
+
[P in keyof H]: KindOf<H[P]> extends K ? P : never
|
|
132
|
+
}[keyof H]
|
|
133
|
+
& string
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Extracts the positional argument tuple accepted by a hook entry.
|
|
137
|
+
*
|
|
138
|
+
* @category type-level
|
|
139
|
+
* @since 1.0.0-rc.0
|
|
140
|
+
*/
|
|
141
|
+
export type ArgsOf<T> = HandlerOf<T> extends (...args: infer A) => any ? A : never
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Extracts the Effect returned by a hook entry.
|
|
145
|
+
*
|
|
146
|
+
* @category type-level
|
|
147
|
+
* @since 1.0.0-rc.0
|
|
148
|
+
*/
|
|
149
|
+
export type ReturnOf<T> = HandlerOf<T> extends (...args: any) => infer R ? R : never
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Extracts the success value returned by a hook entry's Effect.
|
|
153
|
+
*
|
|
154
|
+
* @category type-level
|
|
155
|
+
* @since 1.0.0-rc.0
|
|
156
|
+
*/
|
|
157
|
+
export type SuccessOf<T> = ReturnOf<T> extends Effect.Effect<infer A, any, any> ? A : never
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Extracts the context required by a hook entry's Effect.
|
|
161
|
+
*
|
|
162
|
+
* @category type-level
|
|
163
|
+
* @since 1.0.0-rc.0
|
|
164
|
+
*/
|
|
165
|
+
export type ContextOf<T> = ReturnOf<T> extends Effect.Effect<any, any, infer R> ? R : never
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Runtime catalog of the shared kernel's hook names and kinds.
|
|
169
|
+
*
|
|
170
|
+
* The type system cannot enumerate an augmentable interface, so this constant
|
|
171
|
+
* is what the `unknown_hook` guard checks against. A host passes its own
|
|
172
|
+
* superset to {@link resolve}. The cell harness adds only the hooks it
|
|
173
|
+
* dispatches; lifecycle and engine-policy hooks do not belong here.
|
|
174
|
+
*
|
|
175
|
+
* @category models
|
|
176
|
+
* @since 1.0.0-rc.0
|
|
177
|
+
*/
|
|
178
|
+
const sharedHooks: { config: "waterfall"; configResolved: "parallel" } = Object.create(null)
|
|
179
|
+
Object.defineProperties(sharedHooks, {
|
|
180
|
+
config: { value: "waterfall", enumerable: true },
|
|
181
|
+
configResolved: { value: "parallel", enumerable: true }
|
|
182
|
+
})
|
|
183
|
+
/**
|
|
184
|
+
* Frozen runtime catalog for the shared configuration hooks.
|
|
185
|
+
*
|
|
186
|
+
* @category models
|
|
187
|
+
* @since 1.0.0-rc.0
|
|
188
|
+
*/
|
|
189
|
+
export const engineHooks: Readonly<{
|
|
190
|
+
readonly config: "waterfall"
|
|
191
|
+
readonly configResolved: "parallel"
|
|
192
|
+
}> = Object.freeze(sharedHooks)
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Normalizes a hook entry to its handler.
|
|
196
|
+
*
|
|
197
|
+
* @category utils
|
|
198
|
+
* @since 1.0.0-rc.0
|
|
199
|
+
*/
|
|
200
|
+
export const handlerOf = (entry: unknown): (...args: Array<any>) => unknown =>
|
|
201
|
+
typeof entry === "function"
|
|
202
|
+
? entry as (...args: Array<any>) => unknown
|
|
203
|
+
: (entry as HookObject<(...args: Array<any>) => unknown>).handler
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Reads the per-hook `order` of a hook entry.
|
|
207
|
+
*
|
|
208
|
+
* @category utils
|
|
209
|
+
* @since 1.0.0-rc.0
|
|
210
|
+
*/
|
|
211
|
+
export const orderOf = (entry: unknown): "pre" | "post" | undefined =>
|
|
212
|
+
typeof entry === "function" ? undefined : (entry as HookObject<unknown>).order
|
package/src/Kernel.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
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
|
+
|
|
21
|
+
import * as Effect from "effect/Effect"
|
|
22
|
+
import type * as Layer from "effect/Layer"
|
|
23
|
+
import type { FlowsConfig, ResolvedConfig } from "./Config.ts"
|
|
24
|
+
import * as Config from "./Config.ts"
|
|
25
|
+
import type { ContextOf } from "./Hooks.ts"
|
|
26
|
+
import type { FlowsHooks } from "./index.ts"
|
|
27
|
+
import type { PluginInput } from "./Plugin.ts"
|
|
28
|
+
import type { PluginError } from "./PluginError.ts"
|
|
29
|
+
import * as Plugins from "./Plugins.ts"
|
|
30
|
+
import * as Resolve from "./Resolve.ts"
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Everything startup produces from a plugin list and a raw config.
|
|
34
|
+
*
|
|
35
|
+
* `observerErrors` carries `configResolved` failures: parallel observers are
|
|
36
|
+
* lossy by contract, so they are reported rather than fatal.
|
|
37
|
+
*
|
|
38
|
+
* @category models
|
|
39
|
+
* @since 1.0.0-rc.0
|
|
40
|
+
*/
|
|
41
|
+
export interface Kernel<H = FlowsHooks> {
|
|
42
|
+
readonly plugins: Plugins.Service<H>
|
|
43
|
+
readonly config: ResolvedConfig
|
|
44
|
+
readonly layer: Layer.Layer<any, PluginError, any>
|
|
45
|
+
readonly observerErrors: ReadonlyArray<PluginError>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type HookAt<H, K extends string> = K extends keyof H ? H[K] : never
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The services a hook interface's `config` hook requires at startup. `never`
|
|
52
|
+
* for the shared `FlowsHooks`, whose startup hooks are context-free.
|
|
53
|
+
*
|
|
54
|
+
* @category type-level
|
|
55
|
+
* @since 1.0.0-rc.0
|
|
56
|
+
*/
|
|
57
|
+
export type ConfigContext<H> = ContextOf<HookAt<H, "config">>
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The services a hook interface's startup hooks require: the union of the
|
|
61
|
+
* `config` and `configResolved` requirements. `Kernel.make` cannot supply
|
|
62
|
+
* them itself, so they surface in its Effect and the caller provides them.
|
|
63
|
+
*
|
|
64
|
+
* @category type-level
|
|
65
|
+
* @since 1.0.0-rc.0
|
|
66
|
+
*/
|
|
67
|
+
export type StartupContext<H> = ConfigContext<H> | ContextOf<HookAt<H, "configResolved">>
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Snapshots the config before dispatching the `config` waterfall, then
|
|
71
|
+
* admits the result as a frozen `ResolvedConfig`.
|
|
72
|
+
*
|
|
73
|
+
* @category constructors
|
|
74
|
+
* @since 1.0.0-rc.0
|
|
75
|
+
*/
|
|
76
|
+
export const runConfig = <H = FlowsHooks>(
|
|
77
|
+
plugins: Plugins.Service<H>,
|
|
78
|
+
config: FlowsConfig
|
|
79
|
+
): Effect.Effect<ResolvedConfig, PluginError, ConfigContext<H>> =>
|
|
80
|
+
Effect.gen(function*() {
|
|
81
|
+
const initial = yield* Config.snapshot(config)
|
|
82
|
+
const merged = yield* (plugins.waterfall as unknown as (
|
|
83
|
+
hook: string,
|
|
84
|
+
initial: FlowsConfig,
|
|
85
|
+
merge: (previous: FlowsConfig, patch: unknown) => FlowsConfig
|
|
86
|
+
) => Effect.Effect<FlowsConfig, PluginError, ConfigContext<H>>)("config", initial, Config.merge)
|
|
87
|
+
return yield* Config.resolve(merged)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Resolves plugins, executes the config pipeline, and merges plugin layers.
|
|
92
|
+
* The positional `config` is the kernel's only pre-resolution configuration;
|
|
93
|
+
* the options object cannot declare a second source, and a `config` key
|
|
94
|
+
* supplied anyway fails with `invalid_plugin` at `$options.config`.
|
|
95
|
+
*
|
|
96
|
+
* @category constructors
|
|
97
|
+
* @since 1.0.0-rc.0
|
|
98
|
+
*/
|
|
99
|
+
export const make = <H = FlowsHooks>(
|
|
100
|
+
input: PluginInput<NoInfer<H>>,
|
|
101
|
+
config: FlowsConfig = {},
|
|
102
|
+
options: Omit<Resolve.Options<NoInfer<H>>, "config"> = {}
|
|
103
|
+
): Effect.Effect<Kernel<H>, PluginError, StartupContext<H>> =>
|
|
104
|
+
Effect.gen(function*() {
|
|
105
|
+
const initial = yield* Config.snapshot(config)
|
|
106
|
+
const resolved = yield* Resolve.resolve<H>(input, options, initial)
|
|
107
|
+
const dispatcher = Plugins.make<H>(resolved)
|
|
108
|
+
const frozen = yield* runConfig(dispatcher, initial)
|
|
109
|
+
const observerErrors = yield* (dispatcher.parallel as unknown as (
|
|
110
|
+
hook: string,
|
|
111
|
+
value: ResolvedConfig
|
|
112
|
+
) => Effect.Effect<ReadonlyArray<PluginError>, never, StartupContext<H>>)("configResolved", frozen)
|
|
113
|
+
return Object.freeze({
|
|
114
|
+
plugins: dispatcher,
|
|
115
|
+
config: frozen,
|
|
116
|
+
layer: Resolve.layer(resolved),
|
|
117
|
+
observerErrors
|
|
118
|
+
})
|
|
119
|
+
})
|
package/src/Plugin.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plugin object: a plain record produced by a factory function.
|
|
3
|
+
*
|
|
4
|
+
* The public package contract is documented at
|
|
5
|
+
* {@link https://smithers.sh/docs/reference/api/plugin}. The shipped consumer is the
|
|
6
|
+
* assembled cell host in `@smthrs/agent`;
|
|
7
|
+
* durable-core policy continues to use Effect services and constructor
|
|
8
|
+
* options rather than plugin lifecycle hooks.
|
|
9
|
+
*
|
|
10
|
+
* @since 1.0.0-rc.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type * as Layer from "effect/Layer"
|
|
14
|
+
import type { FlowsConfig } from "./Config.ts"
|
|
15
|
+
import type { FlowsHooks } from "./index.ts"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Conditional-inclusion predicate, mirroring Vite's `apply`.
|
|
19
|
+
*
|
|
20
|
+
* `"engine"` and `"harness"` select the matching plugin-kernel host. The
|
|
21
|
+
* assembled cell host resolves as `"harness"`; this does not make `"engine"` an
|
|
22
|
+
* engine-lifecycle hook registration.
|
|
23
|
+
*
|
|
24
|
+
* @category models
|
|
25
|
+
* @since 1.0.0-rc.0
|
|
26
|
+
*/
|
|
27
|
+
export type Apply = "engine" | "harness" | ((config: FlowsConfig) => boolean)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A flows plugin.
|
|
31
|
+
*
|
|
32
|
+
* Generic over the hook interface so a harness can hold plugins typed against
|
|
33
|
+
* its augmented `FlowsHooks` without any inheritance machinery.
|
|
34
|
+
*
|
|
35
|
+
* @category models
|
|
36
|
+
* @since 1.0.0-rc.0
|
|
37
|
+
*/
|
|
38
|
+
export interface FlowsPlugin<H = FlowsHooks> {
|
|
39
|
+
/** Required, unique. Convention: `flows-plugin-<thing>`. */
|
|
40
|
+
readonly name: string
|
|
41
|
+
/**
|
|
42
|
+
* Semantic identity folded into sealed cache keys. Required when the host
|
|
43
|
+
* declares a cache environment. `Action.CacheEnvironment` is complete by
|
|
44
|
+
* contract, so a versionless composition is refused instead of claiming an
|
|
45
|
+
* ambiguous cross-run identity.
|
|
46
|
+
*/
|
|
47
|
+
readonly version?: string | undefined
|
|
48
|
+
/** Ordering group; omitted means `"normal"`. */
|
|
49
|
+
readonly enforce?: "pre" | "post" | undefined
|
|
50
|
+
/** Conditional inclusion. */
|
|
51
|
+
readonly apply?: Apply | undefined
|
|
52
|
+
/** Services this plugin contributes to the engine environment. */
|
|
53
|
+
readonly layer?: Layer.Layer<never, any, any> | undefined
|
|
54
|
+
/** Typed hooks; only keys declared in the hook interface compile. */
|
|
55
|
+
readonly hooks?: Partial<H> | undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* What a `plugins` array may contain: plugins, falsy entries, and arrays nested
|
|
60
|
+
* up to `Resolve.maximumPluginDepth`: so a preset is just a function returning
|
|
61
|
+
* plugins.
|
|
62
|
+
*
|
|
63
|
+
* @category models
|
|
64
|
+
* @since 1.0.0-rc.0
|
|
65
|
+
*/
|
|
66
|
+
export type PluginInput<H = FlowsHooks> =
|
|
67
|
+
| FlowsPlugin<H>
|
|
68
|
+
| false
|
|
69
|
+
| null
|
|
70
|
+
| undefined
|
|
71
|
+
| ReadonlyArray<PluginInput<H>>
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Identity constructor that pins a plugin literal to `FlowsPlugin` so excess
|
|
75
|
+
* hook keys are rejected at the definition site.
|
|
76
|
+
*
|
|
77
|
+
* @category constructors
|
|
78
|
+
* @since 1.0.0-rc.0
|
|
79
|
+
*/
|
|
80
|
+
export const make = <H = FlowsHooks>(plugin: FlowsPlugin<H>): FlowsPlugin<H> => plugin
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single typed failure of the plugin kernel.
|
|
3
|
+
*
|
|
4
|
+
* The public package contract is documented at
|
|
5
|
+
* {@link https://smithers.sh/docs/reference/api/plugin}; D14 in
|
|
6
|
+
* `docs/pages/design-decisions.md` bounds it to host-owned cell hooks.
|
|
7
|
+
*
|
|
8
|
+
* @since 1.0.0-rc.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as Schema from "effect/Schema"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Closed set of plugin-system failure codes.
|
|
15
|
+
*
|
|
16
|
+
* @category models
|
|
17
|
+
* @since 1.0.0-rc.0
|
|
18
|
+
*/
|
|
19
|
+
export const PluginErrorCode = Schema.Literals([
|
|
20
|
+
"duplicate_name",
|
|
21
|
+
"unknown_hook",
|
|
22
|
+
"hook_kind_mismatch",
|
|
23
|
+
"invalid_plugin",
|
|
24
|
+
"apply_failed",
|
|
25
|
+
"config_invalid",
|
|
26
|
+
"cache_environment_invalid",
|
|
27
|
+
"invalid_hook_result",
|
|
28
|
+
"resource_limit",
|
|
29
|
+
"hook_failed",
|
|
30
|
+
"layer_failed"
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Closed set of plugin-system failure codes.
|
|
35
|
+
*
|
|
36
|
+
* @category models
|
|
37
|
+
* @since 1.0.0-rc.0
|
|
38
|
+
*/
|
|
39
|
+
export type PluginErrorCode = typeof PluginErrorCode.Type
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Failure raised by plugin resolution, hook dispatch, config execution, or
|
|
43
|
+
* layer construction.
|
|
44
|
+
*
|
|
45
|
+
* Parallel observer failures never fail the caller; they are returned from
|
|
46
|
+
* `Plugins.parallel` with this same shape so they can be journalled on the
|
|
47
|
+
* lossy telemetry channel.
|
|
48
|
+
*
|
|
49
|
+
* `cause` is the plugin's unredacted failure and may hold secrets; hosts
|
|
50
|
+
* redact it before logging, persisting, or transmitting the error.
|
|
51
|
+
*
|
|
52
|
+
* @category errors
|
|
53
|
+
* @since 1.0.0-rc.0
|
|
54
|
+
*/
|
|
55
|
+
export class PluginError extends Schema.TaggedError<PluginError>()("flows/plugin/PluginError", {
|
|
56
|
+
code: PluginErrorCode,
|
|
57
|
+
message: Schema.String,
|
|
58
|
+
plugin: Schema.optional(Schema.String),
|
|
59
|
+
hook: Schema.optional(Schema.String),
|
|
60
|
+
path: Schema.optional(Schema.String),
|
|
61
|
+
cause: Schema.optional(Schema.Unknown)
|
|
62
|
+
}) {}
|