@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.
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,71 @@
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
+ import type * as Layer from "effect/Layer";
13
+ import type { FlowsConfig } from "./Config.ts";
14
+ import type { FlowsHooks } from "./index.ts";
15
+ /**
16
+ * Conditional-inclusion predicate, mirroring Vite's `apply`.
17
+ *
18
+ * `"engine"` and `"harness"` select the matching plugin-kernel host. The
19
+ * assembled cell host resolves as `"harness"`; this does not make `"engine"` an
20
+ * engine-lifecycle hook registration.
21
+ *
22
+ * @category models
23
+ * @since 1.0.0-rc.0
24
+ */
25
+ export type Apply = "engine" | "harness" | ((config: FlowsConfig) => boolean);
26
+ /**
27
+ * A flows plugin.
28
+ *
29
+ * Generic over the hook interface so a harness can hold plugins typed against
30
+ * its augmented `FlowsHooks` without any inheritance machinery.
31
+ *
32
+ * @category models
33
+ * @since 1.0.0-rc.0
34
+ */
35
+ export interface FlowsPlugin<H = FlowsHooks> {
36
+ /** Required, unique. Convention: `flows-plugin-<thing>`. */
37
+ readonly name: string;
38
+ /**
39
+ * Semantic identity folded into sealed cache keys. Required when the host
40
+ * declares a cache environment. `Action.CacheEnvironment` is complete by
41
+ * contract, so a versionless composition is refused instead of claiming an
42
+ * ambiguous cross-run identity.
43
+ */
44
+ readonly version?: string | undefined;
45
+ /** Ordering group; omitted means `"normal"`. */
46
+ readonly enforce?: "pre" | "post" | undefined;
47
+ /** Conditional inclusion. */
48
+ readonly apply?: Apply | undefined;
49
+ /** Services this plugin contributes to the engine environment. */
50
+ readonly layer?: Layer.Layer<never, any, any> | undefined;
51
+ /** Typed hooks; only keys declared in the hook interface compile. */
52
+ readonly hooks?: Partial<H> | undefined;
53
+ }
54
+ /**
55
+ * What a `plugins` array may contain: plugins, falsy entries, and arrays nested
56
+ * up to `Resolve.maximumPluginDepth`: so a preset is just a function returning
57
+ * plugins.
58
+ *
59
+ * @category models
60
+ * @since 1.0.0-rc.0
61
+ */
62
+ export type PluginInput<H = FlowsHooks> = FlowsPlugin<H> | false | null | undefined | ReadonlyArray<PluginInput<H>>;
63
+ /**
64
+ * Identity constructor that pins a plugin literal to `FlowsPlugin` so excess
65
+ * hook keys are rejected at the definition site.
66
+ *
67
+ * @category constructors
68
+ * @since 1.0.0-rc.0
69
+ */
70
+ export declare const make: <H = FlowsHooks>(plugin: FlowsPlugin<H>) => FlowsPlugin<H>;
71
+ //# sourceMappingURL=Plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;GASG;AACH,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAA;AAE7E;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,UAAU;IACzC,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,gDAAgD;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAA;IAC7C,6BAA6B;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;IAClC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,SAAS,CAAA;IACzD,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;CACxC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,UAAU,IAClC,WAAW,CAAC,CAAC,CAAC,GACd,KAAK,GACL,IAAI,GACJ,SAAS,GACT,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;AAEjC;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,GAAG,UAAU,EAAE,QAAQ,WAAW,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CAAW,CAAA"}
@@ -0,0 +1,20 @@
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
+ * Identity constructor that pins a plugin literal to `FlowsPlugin` so excess
14
+ * hook keys are rejected at the definition site.
15
+ *
16
+ * @category constructors
17
+ * @since 1.0.0-rc.0
18
+ */
19
+ export const make = (plugin) => plugin;
20
+ //# sourceMappingURL=Plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plugin.js","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA8DH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAiB,MAAsB,EAAkB,EAAE,CAAC,MAAM,CAAA","sourcesContent":["/**\n * The plugin object: a plain record produced by a factory function.\n *\n * The public package contract is documented at\n * {@link https://smithers.sh/docs/reference/api/plugin}. The shipped consumer is the\n * assembled cell host in `@smthrs/agent`;\n * durable-core policy continues to use Effect services and constructor\n * options rather than plugin lifecycle hooks.\n *\n * @since 1.0.0-rc.0\n */\n\nimport type * as Layer from \"effect/Layer\"\nimport type { FlowsConfig } from \"./Config.ts\"\nimport type { FlowsHooks } from \"./index.ts\"\n\n/**\n * Conditional-inclusion predicate, mirroring Vite's `apply`.\n *\n * `\"engine\"` and `\"harness\"` select the matching plugin-kernel host. The\n * assembled cell host resolves as `\"harness\"`; this does not make `\"engine\"` an\n * engine-lifecycle hook registration.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type Apply = \"engine\" | \"harness\" | ((config: FlowsConfig) => boolean)\n\n/**\n * A flows plugin.\n *\n * Generic over the hook interface so a harness can hold plugins typed against\n * its augmented `FlowsHooks` without any inheritance machinery.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface FlowsPlugin<H = FlowsHooks> {\n /** Required, unique. Convention: `flows-plugin-<thing>`. */\n readonly name: string\n /**\n * Semantic identity folded into sealed cache keys. Required when the host\n * declares a cache environment. `Action.CacheEnvironment` is complete by\n * contract, so a versionless composition is refused instead of claiming an\n * ambiguous cross-run identity.\n */\n readonly version?: string | undefined\n /** Ordering group; omitted means `\"normal\"`. */\n readonly enforce?: \"pre\" | \"post\" | undefined\n /** Conditional inclusion. */\n readonly apply?: Apply | undefined\n /** Services this plugin contributes to the engine environment. */\n readonly layer?: Layer.Layer<never, any, any> | undefined\n /** Typed hooks; only keys declared in the hook interface compile. */\n readonly hooks?: Partial<H> | undefined\n}\n\n/**\n * What a `plugins` array may contain: plugins, falsy entries, and arrays nested\n * up to `Resolve.maximumPluginDepth`: so a preset is just a function returning\n * plugins.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type PluginInput<H = FlowsHooks> =\n | FlowsPlugin<H>\n | false\n | null\n | undefined\n | ReadonlyArray<PluginInput<H>>\n\n/**\n * Identity constructor that pins a plugin literal to `FlowsPlugin` so excess\n * hook keys are rejected at the definition site.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const make = <H = FlowsHooks>(plugin: FlowsPlugin<H>): FlowsPlugin<H> => plugin\n"]}
@@ -0,0 +1,50 @@
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
+ import * as Schema from "effect/Schema";
11
+ /**
12
+ * Closed set of plugin-system failure codes.
13
+ *
14
+ * @category models
15
+ * @since 1.0.0-rc.0
16
+ */
17
+ export declare const PluginErrorCode: Schema.Literals<readonly ["duplicate_name", "unknown_hook", "hook_kind_mismatch", "invalid_plugin", "apply_failed", "config_invalid", "cache_environment_invalid", "invalid_hook_result", "resource_limit", "hook_failed", "layer_failed"]>;
18
+ /**
19
+ * Closed set of plugin-system failure codes.
20
+ *
21
+ * @category models
22
+ * @since 1.0.0-rc.0
23
+ */
24
+ export type PluginErrorCode = typeof PluginErrorCode.Type;
25
+ declare const PluginError_base: Schema.Class<PluginError, Schema.TaggedStruct<"flows/plugin/PluginError", {
26
+ readonly code: Schema.Literals<readonly ["duplicate_name", "unknown_hook", "hook_kind_mismatch", "invalid_plugin", "apply_failed", "config_invalid", "cache_environment_invalid", "invalid_hook_result", "resource_limit", "hook_failed", "layer_failed"]>;
27
+ readonly message: Schema.String;
28
+ readonly plugin: Schema.optional<Schema.String>;
29
+ readonly hook: Schema.optional<Schema.String>;
30
+ readonly path: Schema.optional<Schema.String>;
31
+ readonly cause: Schema.optional<Schema.Unknown>;
32
+ }>, import("effect/Cause").YieldableError>;
33
+ /**
34
+ * Failure raised by plugin resolution, hook dispatch, config execution, or
35
+ * layer construction.
36
+ *
37
+ * Parallel observer failures never fail the caller; they are returned from
38
+ * `Plugins.parallel` with this same shape so they can be journalled on the
39
+ * lossy telemetry channel.
40
+ *
41
+ * `cause` is the plugin's unredacted failure and may hold secrets; hosts
42
+ * redact it before logging, persisting, or transmitting the error.
43
+ *
44
+ * @category errors
45
+ * @since 1.0.0-rc.0
46
+ */
47
+ export declare class PluginError extends PluginError_base {
48
+ }
49
+ export {};
50
+ //# sourceMappingURL=PluginError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginError.d.ts","sourceRoot":"","sources":["../../src/PluginError.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,eAAe,6OAY1B,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAA;;;;;;;;;AAEzD;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAY,SAAQ,gBAO/B;CAAG"}
@@ -0,0 +1,53 @@
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
+ import * as Schema from "effect/Schema";
11
+ /**
12
+ * Closed set of plugin-system failure codes.
13
+ *
14
+ * @category models
15
+ * @since 1.0.0-rc.0
16
+ */
17
+ export const PluginErrorCode = Schema.Literals([
18
+ "duplicate_name",
19
+ "unknown_hook",
20
+ "hook_kind_mismatch",
21
+ "invalid_plugin",
22
+ "apply_failed",
23
+ "config_invalid",
24
+ "cache_environment_invalid",
25
+ "invalid_hook_result",
26
+ "resource_limit",
27
+ "hook_failed",
28
+ "layer_failed"
29
+ ]);
30
+ /**
31
+ * Failure raised by plugin resolution, hook dispatch, config execution, or
32
+ * layer construction.
33
+ *
34
+ * Parallel observer failures never fail the caller; they are returned from
35
+ * `Plugins.parallel` with this same shape so they can be journalled on the
36
+ * lossy telemetry channel.
37
+ *
38
+ * `cause` is the plugin's unredacted failure and may hold secrets; hosts
39
+ * redact it before logging, persisting, or transmitting the error.
40
+ *
41
+ * @category errors
42
+ * @since 1.0.0-rc.0
43
+ */
44
+ export class PluginError extends Schema.TaggedError()("flows/plugin/PluginError", {
45
+ code: PluginErrorCode,
46
+ message: Schema.String,
47
+ plugin: Schema.optional(Schema.String),
48
+ hook: Schema.optional(Schema.String),
49
+ path: Schema.optional(Schema.String),
50
+ cause: Schema.optional(Schema.Unknown)
51
+ }) {
52
+ }
53
+ //# sourceMappingURL=PluginError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginError.js","sourceRoot":"","sources":["../../src/PluginError.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC7C,gBAAgB;IAChB,cAAc;IACd,oBAAoB;IACpB,gBAAgB;IAChB,cAAc;IACd,gBAAgB;IAChB,2BAA2B;IAC3B,qBAAqB;IACrB,gBAAgB;IAChB,aAAa;IACb,cAAc;CACf,CAAC,CAAA;AAUF;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,WAAY,SAAQ,MAAM,CAAC,WAAW,EAAe,CAAC,0BAA0B,EAAE;IAC7F,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;CACvC,CAAC;CAAG","sourcesContent":["/**\n * The single typed failure of the plugin kernel.\n *\n * The public package contract is documented at\n * {@link https://smithers.sh/docs/reference/api/plugin}; D14 in\n * `docs/pages/design-decisions.md` bounds it to host-owned cell hooks.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Schema from \"effect/Schema\"\n\n/**\n * Closed set of plugin-system failure codes.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport const PluginErrorCode = Schema.Literals([\n \"duplicate_name\",\n \"unknown_hook\",\n \"hook_kind_mismatch\",\n \"invalid_plugin\",\n \"apply_failed\",\n \"config_invalid\",\n \"cache_environment_invalid\",\n \"invalid_hook_result\",\n \"resource_limit\",\n \"hook_failed\",\n \"layer_failed\"\n])\n\n/**\n * Closed set of plugin-system failure codes.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type PluginErrorCode = typeof PluginErrorCode.Type\n\n/**\n * Failure raised by plugin resolution, hook dispatch, config execution, or\n * layer construction.\n *\n * Parallel observer failures never fail the caller; they are returned from\n * `Plugins.parallel` with this same shape so they can be journalled on the\n * lossy telemetry channel.\n *\n * `cause` is the plugin's unredacted failure and may hold secrets; hosts\n * redact it before logging, persisting, or transmitting the error.\n *\n * @category errors\n * @since 1.0.0-rc.0\n */\nexport class PluginError extends Schema.TaggedError<PluginError>()(\"flows/plugin/PluginError\", {\n code: PluginErrorCode,\n message: Schema.String,\n plugin: Schema.optional(Schema.String),\n hook: Schema.optional(Schema.String),\n path: Schema.optional(Schema.String),\n cause: Schema.optional(Schema.Unknown)\n}) {}\n"]}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The dispatcher: a plain service over a resolved plugin list, generic over the
3
+ * hook interface.
4
+ *
5
+ * The public package contract is documented at
6
+ * {@link https://smithers.sh/docs/reference/api/plugin}. Each host holds an instance over its
7
+ * augmented `FlowsHooks` and may dispatch only the runtime catalog it supplied.
8
+ * The shipped cell host owns the three
9
+ * waterfalls declared in `packages/smithers/agent/src/CellPlugin.ts`; there is
10
+ * no engine-wide lifecycle dispatcher.
11
+ *
12
+ * Cancellation is fiber interruption via scope closure; nothing here threads an
13
+ * `AbortSignal`.
14
+ *
15
+ * @since 1.0.0-rc.0
16
+ */
17
+ import * as Context from "effect/Context";
18
+ import * as Effect from "effect/Effect";
19
+ import * as Layer from "effect/Layer";
20
+ import type { ArgsOf, ContextOf, KeysOfKind, SuccessOf } from "./Hooks.ts";
21
+ import type { FlowsHooks } from "./index.ts";
22
+ import { PluginError } from "./PluginError.ts";
23
+ import { type HandlerRecord, type Resolved } from "./Resolve.ts";
24
+ /**
25
+ * The dispatch surface.
26
+ *
27
+ * @category models
28
+ * @since 1.0.0-rc.0
29
+ */
30
+ export interface Service<H = FlowsHooks> {
31
+ /** The resolved list this dispatcher walks. */
32
+ readonly resolved: Resolved<H>;
33
+ /** Frozen, ordered handler records for a hook. */
34
+ readonly handlers: (hook: string) => ReadonlyArray<HandlerRecord>;
35
+ /** Runs every handler in resolved order, one at a time. */
36
+ readonly sequential: <K extends KeysOfKind<H, "sequential">>(hook: K, ...args: ArgsOf<H[K]>) => Effect.Effect<ReadonlyArray<SuccessOf<H[K]>>, PluginError, ContextOf<H[K]>>;
37
+ /**
38
+ * Runs every handler concurrently, ignoring results. Never fails: handler
39
+ * failures are returned so the caller can report them at its own boundary.
40
+ */
41
+ readonly parallel: <K extends KeysOfKind<H, "parallel">>(hook: K, ...args: ArgsOf<H[K]>) => Effect.Effect<ReadonlyArray<PluginError>, never, ContextOf<H[K]>>;
42
+ /** Runs handlers in order until one returns `Option.some`. */
43
+ readonly first: <K extends KeysOfKind<H, "first">>(hook: K, ...args: ArgsOf<H[K]>) => Effect.Effect<SuccessOf<H[K]>, PluginError, ContextOf<H[K]>>;
44
+ /** Threads a value through every handler, merging each returned patch. */
45
+ readonly waterfall: <K extends KeysOfKind<H, "waterfall">>(hook: K, initial: ArgsOf<H[K]>[0], merge: (previous: ArgsOf<H[K]>[0], patch: Exclude<SuccessOf<H[K]>, void>) => ArgsOf<H[K]>[0]) => Effect.Effect<ArgsOf<H[K]>[0], PluginError, ContextOf<H[K]>>;
46
+ }
47
+ /**
48
+ * Builds a dispatcher over an already resolved plugin list.
49
+ *
50
+ * @category constructors
51
+ * @since 1.0.0-rc.0
52
+ */
53
+ export declare const make: <H = FlowsHooks>(resolved: Resolved<H>) => Service<H>;
54
+ /**
55
+ * A dispatcher with no plugins at all.
56
+ *
57
+ * @category constructors
58
+ * @since 1.0.0-rc.0
59
+ */
60
+ export declare const makeNoop: <H = FlowsHooks>() => Service<H>;
61
+ declare const Plugins_base: Context.ServiceClass<Plugins, "flows/plugin/Plugins", Service<FlowsHooks>>;
62
+ /**
63
+ * The shared dispatcher, as a service tag.
64
+ *
65
+ * This tag holds one dispatcher over the process-wide augmented `FlowsHooks`.
66
+ * `declare module "@smthrs/plugin"` augmentation is the supported extension
67
+ * mechanism for values placed in the tag. A host typed against a separate hook
68
+ * interface holds its `Service<H>` directly, as `@smthrs/agent` does, and does
69
+ * not use this tag or its layers.
70
+ *
71
+ * @category context
72
+ * @since 1.0.0-rc.0
73
+ */
74
+ export declare class Plugins extends Plugins_base {
75
+ }
76
+ /**
77
+ * Layer providing a dispatcher over an already resolved plugin list.
78
+ *
79
+ * This layer targets the shared augmented `FlowsHooks` catalog only. A host
80
+ * with a separate hook interface holds its `Service<H>` directly.
81
+ *
82
+ * @category layers
83
+ * @since 1.0.0-rc.0
84
+ */
85
+ export declare const layer: (resolved: Resolved) => Layer.Layer<Plugins>;
86
+ /**
87
+ * Layer providing a dispatcher with no plugins.
88
+ *
89
+ * This layer targets the shared augmented `FlowsHooks` catalog only. A host
90
+ * with a separate hook interface holds its `Service<H>` directly.
91
+ *
92
+ * @category layers
93
+ * @since 1.0.0-rc.0
94
+ */
95
+ export declare const layerNoop: Layer.Layer<Plugins>;
96
+ export {};
97
+ //# sourceMappingURL=Plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plugins.d.ts","sourceRoot":"","sources":["../../src/Plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAY,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAA8B,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAA;AAmE5F;;;;;GAKG;AACH,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,UAAU;IACrC,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,aAAa,CAAC,aAAa,CAAC,CAAA;IACjE,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,EAAE,YAAY,CAAC,EACzD,IAAI,EAAE,CAAC,EACP,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAClB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAChF;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,EAAE,UAAU,CAAC,EACrD,IAAI,EAAE,CAAC,EACP,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAClB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACtE,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,EAC/C,IAAI,EAAE,CAAC,EACP,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAClB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACjE,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,EAAE,WAAW,CAAC,EACvD,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACxB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KACzF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;CAClE;AAED;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,GAAG,UAAU,EAAE,UAAU,QAAQ,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAmFrE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,GAAG,UAAU,OAAK,OAAO,CAAC,CAAC,CAMhD,CAAA;;AAEL;;;;;;;;;;;GAWG;AACH,qBAAa,OAAQ,SAAQ,YAA2D;CAAG;AAE3F;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,QAAQ,KAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAA2C,CAAA;AAEzG;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAsC,CAAA"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * The dispatcher: a plain service over a resolved plugin list, generic over the
3
+ * hook interface.
4
+ *
5
+ * The public package contract is documented at
6
+ * {@link https://smithers.sh/docs/reference/api/plugin}. Each host holds an instance over its
7
+ * augmented `FlowsHooks` and may dispatch only the runtime catalog it supplied.
8
+ * The shipped cell host owns the three
9
+ * waterfalls declared in `packages/smithers/agent/src/CellPlugin.ts`; there is
10
+ * no engine-wide lifecycle dispatcher.
11
+ *
12
+ * Cancellation is fiber interruption via scope closure; nothing here threads an
13
+ * `AbortSignal`.
14
+ *
15
+ * @since 1.0.0-rc.0
16
+ */
17
+ import * as ImmutableMap from "@smthrs/canonical/ReadonlyMap";
18
+ import * as Context from "effect/Context";
19
+ import * as Effect from "effect/Effect";
20
+ import * as Layer from "effect/Layer";
21
+ import * as Option from "effect/Option";
22
+ import { engineHooks } from "./Hooks.js";
23
+ import { PluginError } from "./PluginError.js";
24
+ import { defaultParallelConcurrency } from "./Resolve.js";
25
+ const empty = Object.freeze([]);
26
+ // The typed catalog and the runtime catalog can only drift when a host casts
27
+ // around `KeysOfKind`; refuse before any handler runs rather than execute a
28
+ // hook under semantics its declaration never promised.
29
+ // A hook the catalog does not declare dispatches as empty, unless a
30
+ // hand-built resolution registered handlers for it: those have no declared
31
+ // kind to honor, so they are refused too.
32
+ const kindMismatch = (resolved, hook, kind, handlers) => {
33
+ if (!Object.hasOwn(resolved.kinds, hook)) {
34
+ if (handlers.length === 0)
35
+ return undefined;
36
+ return new PluginError({
37
+ code: "hook_kind_mismatch",
38
+ message: `hook "${hook}" has handlers but is not declared in the runtime catalog`,
39
+ hook
40
+ });
41
+ }
42
+ const declared = resolved.kinds[hook];
43
+ if (declared === kind)
44
+ return undefined;
45
+ return new PluginError({
46
+ code: "hook_kind_mismatch",
47
+ message: `hook "${hook}" is declared ${JSON.stringify(declared)} in the runtime catalog but was dispatched as ${JSON.stringify(kind)}`,
48
+ hook
49
+ });
50
+ };
51
+ const runHandler = (record, args) => {
52
+ const onCause = (cause) => Effect.fail(new PluginError({
53
+ code: "hook_failed",
54
+ message: `hook "${record.hook}" failed in plugin "${record.plugin}"`,
55
+ plugin: record.plugin,
56
+ hook: record.hook,
57
+ cause
58
+ }));
59
+ return Effect.sync(() => record.handler(...args)).pipe(Effect.catchCause(onCause), Effect.flatMap((result) => Effect.isEffect(result)
60
+ ? result.pipe(Effect.catchCause(onCause))
61
+ : Effect.fail(new PluginError({
62
+ code: "hook_failed",
63
+ message: `hook "${record.hook}" in plugin "${record.plugin}" returned a non-Effect (${typeof result})`,
64
+ plugin: record.plugin,
65
+ hook: record.hook
66
+ }))));
67
+ };
68
+ /**
69
+ * Builds a dispatcher over an already resolved plugin list.
70
+ *
71
+ * @category constructors
72
+ * @since 1.0.0-rc.0
73
+ */
74
+ export const make = (resolved) => {
75
+ const handlers = (hook) => resolved.handlers.get(hook) ?? empty;
76
+ const sequential = ((hook, ...args) => Effect.gen(function* () {
77
+ const mismatch = kindMismatch(resolved, hook, "sequential", handlers(hook));
78
+ if (mismatch !== undefined)
79
+ return yield* Effect.fail(mismatch);
80
+ const results = [];
81
+ for (const record of handlers(hook)) {
82
+ results.push(yield* runHandler(record, args));
83
+ }
84
+ return results;
85
+ }));
86
+ const parallel = ((hook, ...args) => {
87
+ const mismatch = kindMismatch(resolved, hook, "parallel", handlers(hook));
88
+ if (mismatch !== undefined)
89
+ return Effect.succeed([mismatch]);
90
+ return Effect.forEach(handlers(hook), (record) => runHandler(record, args).pipe(Effect.match({ onFailure: (error) => [error], onSuccess: () => [] })), { concurrency: resolved.parallelConcurrency }).pipe(Effect.map((chunks) => chunks.flat()));
91
+ });
92
+ const first = ((hook, ...args) => Effect.gen(function* () {
93
+ const mismatch = kindMismatch(resolved, hook, "first", handlers(hook));
94
+ if (mismatch !== undefined)
95
+ return yield* Effect.fail(mismatch);
96
+ for (const record of handlers(hook)) {
97
+ const result = yield* runHandler(record, args);
98
+ if (!Option.isOption(result)) {
99
+ return yield* Effect.fail(new PluginError({
100
+ code: "invalid_hook_result",
101
+ message: `first hook "${record.hook}" in plugin "${record.plugin}" did not return an Option`,
102
+ plugin: record.plugin,
103
+ hook: record.hook
104
+ }));
105
+ }
106
+ if (Option.isSome(result))
107
+ return result;
108
+ }
109
+ return Option.none();
110
+ }));
111
+ const waterfall = ((hook, initial, merge) => Effect.gen(function* () {
112
+ const mismatch = kindMismatch(resolved, hook, "waterfall", handlers(hook));
113
+ if (mismatch !== undefined)
114
+ return yield* Effect.fail(mismatch);
115
+ let value = initial;
116
+ for (const record of handlers(hook)) {
117
+ const patch = yield* runHandler(record, [value]);
118
+ if (patch !== undefined) {
119
+ value = yield* Effect.try({
120
+ try: () => merge(value, patch),
121
+ catch: (cause) => cause instanceof PluginError
122
+ ? new PluginError({
123
+ code: cause.code,
124
+ message: cause.message,
125
+ plugin: record.plugin,
126
+ hook: record.hook,
127
+ path: cause.path,
128
+ cause
129
+ })
130
+ : new PluginError({
131
+ code: "config_invalid",
132
+ message: `waterfall hook "${record.hook}" in plugin "${record.plugin}" returned an invalid patch`,
133
+ plugin: record.plugin,
134
+ hook: record.hook,
135
+ cause
136
+ })
137
+ });
138
+ }
139
+ }
140
+ return value;
141
+ }));
142
+ return Object.freeze({ resolved, handlers, sequential, parallel, first, waterfall });
143
+ };
144
+ /**
145
+ * A dispatcher with no plugins at all.
146
+ *
147
+ * @category constructors
148
+ * @since 1.0.0-rc.0
149
+ */
150
+ export const makeNoop = () => make(Object.freeze({
151
+ plugins: Object.freeze([]),
152
+ handlers: ImmutableMap.make(),
153
+ kinds: engineHooks,
154
+ parallelConcurrency: defaultParallelConcurrency
155
+ }));
156
+ /**
157
+ * The shared dispatcher, as a service tag.
158
+ *
159
+ * This tag holds one dispatcher over the process-wide augmented `FlowsHooks`.
160
+ * `declare module "@smthrs/plugin"` augmentation is the supported extension
161
+ * mechanism for values placed in the tag. A host typed against a separate hook
162
+ * interface holds its `Service<H>` directly, as `@smthrs/agent` does, and does
163
+ * not use this tag or its layers.
164
+ *
165
+ * @category context
166
+ * @since 1.0.0-rc.0
167
+ */
168
+ export class Plugins extends Context.Service()("flows/plugin/Plugins") {
169
+ }
170
+ /**
171
+ * Layer providing a dispatcher over an already resolved plugin list.
172
+ *
173
+ * This layer targets the shared augmented `FlowsHooks` catalog only. A host
174
+ * with a separate hook interface holds its `Service<H>` directly.
175
+ *
176
+ * @category layers
177
+ * @since 1.0.0-rc.0
178
+ */
179
+ export const layer = (resolved) => Layer.succeed(Plugins)(make(resolved));
180
+ /**
181
+ * Layer providing a dispatcher with no plugins.
182
+ *
183
+ * This layer targets the shared augmented `FlowsHooks` catalog only. A host
184
+ * with a separate hook interface holds its `Service<H>` directly.
185
+ *
186
+ * @category layers
187
+ * @since 1.0.0-rc.0
188
+ */
189
+ export const layerNoop = Layer.succeed(Plugins)(makeNoop());
190
+ //# sourceMappingURL=Plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plugins.js","sourceRoot":"","sources":["../../src/Plugins.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The dispatcher: a plain service over a resolved plugin list, generic over the\n * hook interface.\n *\n * The public package contract is documented at\n * {@link https://smithers.sh/docs/reference/api/plugin}. Each host holds an instance over its\n * augmented `FlowsHooks` and may dispatch only the runtime catalog it supplied.\n * The shipped cell host owns the three\n * waterfalls declared in `packages/smithers/agent/src/CellPlugin.ts`; there is\n * no engine-wide lifecycle dispatcher.\n *\n * Cancellation is fiber interruption via scope closure; nothing here threads an\n * `AbortSignal`.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as ImmutableMap from \"@smthrs/canonical/ReadonlyMap\"\nimport type * as Cause from \"effect/Cause\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Option from \"effect/Option\"\nimport type { ArgsOf, ContextOf, HookKind, KeysOfKind, SuccessOf } from \"./Hooks.ts\"\nimport { engineHooks } from \"./Hooks.ts\"\nimport type { FlowsHooks } from \"./index.ts\"\nimport { PluginError } from \"./PluginError.ts\"\nimport { defaultParallelConcurrency, type HandlerRecord, type Resolved } from \"./Resolve.ts\"\n\nconst empty: ReadonlyArray<HandlerRecord> = Object.freeze([])\n\n// The typed catalog and the runtime catalog can only drift when a host casts\n// around `KeysOfKind`; refuse before any handler runs rather than execute a\n// hook under semantics its declaration never promised.\n// A hook the catalog does not declare dispatches as empty, unless a\n// hand-built resolution registered handlers for it: those have no declared\n// kind to honor, so they are refused too.\nconst kindMismatch = (\n resolved: Resolved<unknown>,\n hook: string,\n kind: HookKind,\n handlers: ReadonlyArray<HandlerRecord>\n): PluginError | undefined => {\n if (!Object.hasOwn(resolved.kinds, hook)) {\n if (handlers.length === 0) return undefined\n return new PluginError({\n code: \"hook_kind_mismatch\",\n message: `hook \"${hook}\" has handlers but is not declared in the runtime catalog`,\n hook\n })\n }\n const declared = resolved.kinds[hook]\n if (declared === kind) return undefined\n return new PluginError({\n code: \"hook_kind_mismatch\",\n message: `hook \"${hook}\" is declared ${JSON.stringify(declared)} in the runtime catalog but was dispatched as ${\n JSON.stringify(kind)\n }`,\n hook\n })\n}\n\nconst runHandler = (\n record: HandlerRecord,\n args: ReadonlyArray<unknown>\n): Effect.Effect<unknown, PluginError, any> => {\n const onCause = (cause: Cause.Cause<unknown>) =>\n Effect.fail(\n new PluginError({\n code: \"hook_failed\",\n message: `hook \"${record.hook}\" failed in plugin \"${record.plugin}\"`,\n plugin: record.plugin,\n hook: record.hook,\n cause\n })\n )\n\n return Effect.sync(() => record.handler(...args)).pipe(\n Effect.catchCause(onCause),\n Effect.flatMap((result) =>\n Effect.isEffect(result)\n ? result.pipe(Effect.catchCause(onCause))\n : Effect.fail(\n new PluginError({\n code: \"hook_failed\",\n message: `hook \"${record.hook}\" in plugin \"${record.plugin}\" returned a non-Effect (${typeof result})`,\n plugin: record.plugin,\n hook: record.hook\n })\n )\n )\n )\n}\n\n/**\n * The dispatch surface.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface Service<H = FlowsHooks> {\n /** The resolved list this dispatcher walks. */\n readonly resolved: Resolved<H>\n /** Frozen, ordered handler records for a hook. */\n readonly handlers: (hook: string) => ReadonlyArray<HandlerRecord>\n /** Runs every handler in resolved order, one at a time. */\n readonly sequential: <K extends KeysOfKind<H, \"sequential\">>(\n hook: K,\n ...args: ArgsOf<H[K]>\n ) => Effect.Effect<ReadonlyArray<SuccessOf<H[K]>>, PluginError, ContextOf<H[K]>>\n /**\n * Runs every handler concurrently, ignoring results. Never fails: handler\n * failures are returned so the caller can report them at its own boundary.\n */\n readonly parallel: <K extends KeysOfKind<H, \"parallel\">>(\n hook: K,\n ...args: ArgsOf<H[K]>\n ) => Effect.Effect<ReadonlyArray<PluginError>, never, ContextOf<H[K]>>\n /** Runs handlers in order until one returns `Option.some`. */\n readonly first: <K extends KeysOfKind<H, \"first\">>(\n hook: K,\n ...args: ArgsOf<H[K]>\n ) => Effect.Effect<SuccessOf<H[K]>, PluginError, ContextOf<H[K]>>\n /** Threads a value through every handler, merging each returned patch. */\n readonly waterfall: <K extends KeysOfKind<H, \"waterfall\">>(\n hook: K,\n initial: ArgsOf<H[K]>[0],\n merge: (previous: ArgsOf<H[K]>[0], patch: Exclude<SuccessOf<H[K]>, void>) => ArgsOf<H[K]>[0]\n ) => Effect.Effect<ArgsOf<H[K]>[0], PluginError, ContextOf<H[K]>>\n}\n\n/**\n * Builds a dispatcher over an already resolved plugin list.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const make = <H = FlowsHooks>(resolved: Resolved<H>): Service<H> => {\n const handlers = (hook: string): ReadonlyArray<HandlerRecord> => resolved.handlers.get(hook) ?? empty\n\n const sequential = ((hook: string, ...args: Array<unknown>) =>\n Effect.gen(function*() {\n const mismatch = kindMismatch(resolved, hook, \"sequential\", handlers(hook))\n if (mismatch !== undefined) return yield* Effect.fail(mismatch)\n const results: Array<unknown> = []\n for (const record of handlers(hook)) {\n results.push(yield* runHandler(record, args))\n }\n return results\n })) as Service<H>[\"sequential\"]\n\n const parallel = ((hook: string, ...args: Array<unknown>) => {\n const mismatch = kindMismatch(resolved, hook, \"parallel\", handlers(hook))\n if (mismatch !== undefined) return Effect.succeed([mismatch])\n return Effect.forEach(\n handlers(hook),\n (record) =>\n runHandler(record, args).pipe(\n Effect.match({ onFailure: (error) => [error], onSuccess: () => [] as Array<PluginError> })\n ),\n { concurrency: resolved.parallelConcurrency }\n ).pipe(Effect.map((chunks) => chunks.flat()))\n }) as Service<H>[\"parallel\"]\n\n const first = ((hook: string, ...args: Array<unknown>) =>\n Effect.gen(function*() {\n const mismatch = kindMismatch(resolved, hook, \"first\", handlers(hook))\n if (mismatch !== undefined) return yield* Effect.fail(mismatch)\n for (const record of handlers(hook)) {\n const result = yield* runHandler(record, args)\n if (!Option.isOption(result)) {\n return yield* Effect.fail(\n new PluginError({\n code: \"invalid_hook_result\",\n message: `first hook \"${record.hook}\" in plugin \"${record.plugin}\" did not return an Option`,\n plugin: record.plugin,\n hook: record.hook\n })\n )\n }\n if (Option.isSome(result)) return result\n }\n return Option.none()\n })) as unknown as Service<H>[\"first\"]\n\n const waterfall =\n ((hook: string, initial: unknown, merge: (previous: unknown, patch: unknown) => unknown) =>\n Effect.gen(function*() {\n const mismatch = kindMismatch(resolved, hook, \"waterfall\", handlers(hook))\n if (mismatch !== undefined) return yield* Effect.fail(mismatch)\n let value = initial\n for (const record of handlers(hook)) {\n const patch = yield* runHandler(record, [value])\n if (patch !== undefined) {\n value = yield* Effect.try({\n try: () => merge(value, patch),\n catch: (cause) =>\n cause instanceof PluginError\n ? new PluginError({\n code: cause.code,\n message: cause.message,\n plugin: record.plugin,\n hook: record.hook,\n path: cause.path,\n cause\n })\n : new PluginError({\n code: \"config_invalid\",\n message: `waterfall hook \"${record.hook}\" in plugin \"${record.plugin}\" returned an invalid patch`,\n plugin: record.plugin,\n hook: record.hook,\n cause\n })\n })\n }\n }\n return value\n })) as Service<H>[\"waterfall\"]\n\n return Object.freeze({ resolved, handlers, sequential, parallel, first, waterfall })\n}\n\n/**\n * A dispatcher with no plugins at all.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const makeNoop = <H = FlowsHooks>(): Service<H> =>\n make<H>(Object.freeze({\n plugins: Object.freeze([]),\n handlers: ImmutableMap.make<string, ReadonlyArray<HandlerRecord>>(),\n kinds: engineHooks,\n parallelConcurrency: defaultParallelConcurrency\n }))\n\n/**\n * The shared dispatcher, as a service tag.\n *\n * This tag holds one dispatcher over the process-wide augmented `FlowsHooks`.\n * `declare module \"@smthrs/plugin\"` augmentation is the supported extension\n * mechanism for values placed in the tag. A host typed against a separate hook\n * interface holds its `Service<H>` directly, as `@smthrs/agent` does, and does\n * not use this tag or its layers.\n *\n * @category context\n * @since 1.0.0-rc.0\n */\nexport class Plugins extends Context.Service<Plugins, Service>()(\"flows/plugin/Plugins\") {}\n\n/**\n * Layer providing a dispatcher over an already resolved plugin list.\n *\n * This layer targets the shared augmented `FlowsHooks` catalog only. A host\n * with a separate hook interface holds its `Service<H>` directly.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layer = (resolved: Resolved): Layer.Layer<Plugins> => Layer.succeed(Plugins)(make(resolved))\n\n/**\n * Layer providing a dispatcher with no plugins.\n *\n * This layer targets the shared augmented `FlowsHooks` catalog only. A host\n * with a separate hook interface holds its `Service<H>` directly.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerNoop: Layer.Layer<Plugins> = Layer.succeed(Plugins)(makeNoop())\n"]}