every-plugin 0.9.0 → 2.2.2

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 (179) hide show
  1. package/dist/_virtual/{rolldown_runtime.cjs → _rolldown/runtime.cjs} +15 -1
  2. package/dist/_virtual/{rolldown_runtime.mjs → _rolldown/runtime.mjs} +15 -2
  3. package/dist/build/rspack/dev-server-middleware.cjs +15 -4
  4. package/dist/build/rspack/dev-server-middleware.cjs.map +1 -1
  5. package/dist/build/rspack/dev-server-middleware.mjs +15 -4
  6. package/dist/build/rspack/dev-server-middleware.mjs.map +1 -1
  7. package/dist/build/rspack/fix-mf-data-uri-plugin.cjs +44 -0
  8. package/dist/build/rspack/fix-mf-data-uri-plugin.cjs.map +1 -0
  9. package/dist/build/rspack/fix-mf-data-uri-plugin.d.cts +11 -0
  10. package/dist/build/rspack/fix-mf-data-uri-plugin.d.cts.map +1 -0
  11. package/dist/build/rspack/fix-mf-data-uri-plugin.d.mts +11 -0
  12. package/dist/build/rspack/fix-mf-data-uri-plugin.d.mts.map +1 -0
  13. package/dist/build/rspack/fix-mf-data-uri-plugin.mjs +43 -0
  14. package/dist/build/rspack/fix-mf-data-uri-plugin.mjs.map +1 -0
  15. package/dist/build/rspack/index.cjs +8 -4
  16. package/dist/build/rspack/index.d.cts +3 -2
  17. package/dist/build/rspack/index.d.mts +3 -2
  18. package/dist/build/rspack/index.mjs +3 -2
  19. package/dist/build/rspack/module-federation.cjs +3 -3
  20. package/dist/build/rspack/module-federation.cjs.map +1 -1
  21. package/dist/build/rspack/module-federation.mjs +1 -1
  22. package/dist/build/rspack/module-federation.mjs.map +1 -1
  23. package/dist/build/rspack/plugin.cjs +83 -5
  24. package/dist/build/rspack/plugin.cjs.map +1 -1
  25. package/dist/build/rspack/plugin.d.cts +12 -1
  26. package/dist/build/rspack/plugin.d.cts.map +1 -1
  27. package/dist/build/rspack/plugin.d.mts +12 -1
  28. package/dist/build/rspack/plugin.d.mts.map +1 -1
  29. package/dist/build/rspack/plugin.mjs +80 -5
  30. package/dist/build/rspack/plugin.mjs.map +1 -1
  31. package/dist/build/rspack/utils.cjs +5 -4
  32. package/dist/build/rspack/utils.cjs.map +1 -1
  33. package/dist/build/rspack/utils.mjs +3 -2
  34. package/dist/build/rspack/utils.mjs.map +1 -1
  35. package/dist/build/shared-deps.cjs +50 -42
  36. package/dist/build/shared-deps.cjs.map +1 -1
  37. package/dist/build/shared-deps.d.cts +27 -2
  38. package/dist/build/shared-deps.d.cts.map +1 -1
  39. package/dist/build/shared-deps.d.mts +27 -2
  40. package/dist/build/shared-deps.d.mts.map +1 -1
  41. package/dist/build/shared-deps.mjs +48 -43
  42. package/dist/build/shared-deps.mjs.map +1 -1
  43. package/dist/errors.cjs +1 -0
  44. package/dist/errors.cjs.map +1 -1
  45. package/dist/errors.d.cts.map +1 -1
  46. package/dist/errors.d.mts.map +1 -1
  47. package/dist/errors.mjs.map +1 -1
  48. package/dist/index.cjs +2 -0
  49. package/dist/index.d.cts +2 -2
  50. package/dist/index.d.mts +2 -2
  51. package/dist/index.mjs +2 -2
  52. package/dist/orpc-client.cjs +17 -0
  53. package/dist/orpc-client.d.cts +2 -0
  54. package/dist/orpc-client.d.mts +2 -0
  55. package/dist/orpc-client.mjs +5 -0
  56. package/dist/orpc-openapi.cjs +17 -0
  57. package/dist/orpc-openapi.d.cts +2 -0
  58. package/dist/orpc-openapi.d.mts +2 -0
  59. package/dist/orpc-openapi.mjs +5 -0
  60. package/dist/orpc.cjs +8 -7
  61. package/dist/plugin.cjs +9 -7
  62. package/dist/plugin.cjs.map +1 -1
  63. package/dist/plugin.d.cts +23 -24
  64. package/dist/plugin.d.cts.map +1 -1
  65. package/dist/plugin.d.mts +23 -24
  66. package/dist/plugin.d.mts.map +1 -1
  67. package/dist/plugin.mjs +8 -7
  68. package/dist/plugin.mjs.map +1 -1
  69. package/dist/runtime/errors.cjs +2 -2
  70. package/dist/runtime/errors.cjs.map +1 -1
  71. package/dist/runtime/errors.d.cts +4 -4
  72. package/dist/runtime/errors.d.cts.map +1 -1
  73. package/dist/runtime/errors.d.mts +4 -4
  74. package/dist/runtime/errors.d.mts.map +1 -1
  75. package/dist/runtime/errors.mjs +1 -1
  76. package/dist/runtime/errors.mjs.map +1 -1
  77. package/dist/runtime/index.cjs +41 -37
  78. package/dist/runtime/index.cjs.map +1 -1
  79. package/dist/runtime/index.d.cts +3 -3
  80. package/dist/runtime/index.d.cts.map +1 -1
  81. package/dist/runtime/index.d.mts +3 -3
  82. package/dist/runtime/index.d.mts.map +1 -1
  83. package/dist/runtime/index.mjs +12 -9
  84. package/dist/runtime/index.mjs.map +1 -1
  85. package/dist/runtime/mf-config.cjs +33 -0
  86. package/dist/runtime/mf-config.cjs.map +1 -0
  87. package/dist/runtime/mf-config.d.cts +41 -0
  88. package/dist/runtime/mf-config.d.cts.map +1 -0
  89. package/dist/runtime/mf-config.d.mts +41 -0
  90. package/dist/runtime/mf-config.d.mts.map +1 -0
  91. package/dist/runtime/mf-config.mjs +29 -0
  92. package/dist/runtime/mf-config.mjs.map +1 -0
  93. package/dist/runtime/services/module-federation.service.cjs +19 -56
  94. package/dist/runtime/services/module-federation.service.cjs.map +1 -1
  95. package/dist/runtime/services/module-federation.service.d.cts.map +1 -1
  96. package/dist/runtime/services/module-federation.service.d.mts.map +1 -1
  97. package/dist/runtime/services/module-federation.service.mjs +18 -55
  98. package/dist/runtime/services/module-federation.service.mjs.map +1 -1
  99. package/dist/runtime/services/normalize.cjs +3 -2
  100. package/dist/runtime/services/normalize.cjs.map +1 -1
  101. package/dist/runtime/services/normalize.mjs +2 -2
  102. package/dist/runtime/services/normalize.mjs.map +1 -1
  103. package/dist/runtime/services/plugin-lifecycle.service.cjs +1 -1
  104. package/dist/runtime/services/plugin-lifecycle.service.cjs.map +1 -1
  105. package/dist/runtime/services/plugin-lifecycle.service.d.cts.map +1 -1
  106. package/dist/runtime/services/plugin-lifecycle.service.d.mts.map +1 -1
  107. package/dist/runtime/services/plugin-lifecycle.service.mjs.map +1 -1
  108. package/dist/runtime/services/plugin-loader.service.cjs +3 -3
  109. package/dist/runtime/services/plugin-loader.service.cjs.map +1 -1
  110. package/dist/runtime/services/plugin-loader.service.d.cts +1 -1
  111. package/dist/runtime/services/plugin-loader.service.d.cts.map +1 -1
  112. package/dist/runtime/services/plugin-loader.service.d.mts +1 -1
  113. package/dist/runtime/services/plugin-loader.service.d.mts.map +1 -1
  114. package/dist/runtime/services/plugin-loader.service.mjs +2 -2
  115. package/dist/runtime/services/plugin-loader.service.mjs.map +1 -1
  116. package/dist/runtime/services/plugin.service.cjs +1 -1
  117. package/dist/runtime/services/plugin.service.cjs.map +1 -1
  118. package/dist/runtime/services/plugin.service.d.cts +6 -7
  119. package/dist/runtime/services/plugin.service.d.cts.map +1 -1
  120. package/dist/runtime/services/plugin.service.d.mts +6 -7
  121. package/dist/runtime/services/plugin.service.d.mts.map +1 -1
  122. package/dist/runtime/services/plugin.service.mjs.map +1 -1
  123. package/dist/runtime/services/secrets.service.cjs +1 -1
  124. package/dist/runtime/services/secrets.service.cjs.map +1 -1
  125. package/dist/runtime/services/secrets.service.d.cts.map +1 -1
  126. package/dist/runtime/services/secrets.service.d.mts.map +1 -1
  127. package/dist/runtime/services/secrets.service.mjs.map +1 -1
  128. package/dist/runtime/validation.cjs +1 -1
  129. package/dist/runtime/validation.cjs.map +1 -1
  130. package/dist/runtime/validation.mjs.map +1 -1
  131. package/dist/testing/index.cjs +2 -1
  132. package/dist/testing/index.cjs.map +1 -1
  133. package/dist/testing/index.d.cts +0 -2
  134. package/dist/testing/index.d.cts.map +1 -1
  135. package/dist/testing/index.d.mts +0 -2
  136. package/dist/testing/index.d.mts.map +1 -1
  137. package/dist/testing/index.mjs +1 -1
  138. package/dist/testing/index.mjs.map +1 -1
  139. package/dist/types.d.cts +6 -6
  140. package/dist/types.d.cts.map +1 -1
  141. package/dist/types.d.mts +6 -6
  142. package/dist/types.d.mts.map +1 -1
  143. package/dist/zod.cjs +4 -3
  144. package/dist/zod.mjs +2 -2
  145. package/package.json +204 -182
  146. package/src/build/rspack/dev-server-middleware.ts +233 -0
  147. package/src/build/rspack/fix-mf-data-uri-plugin.ts +49 -0
  148. package/src/build/rspack/index.ts +14 -0
  149. package/src/build/rspack/module-federation.ts +6 -0
  150. package/src/build/rspack/plugin.ts +252 -0
  151. package/src/build/rspack/utils.ts +60 -0
  152. package/src/build/shared-deps.ts +79 -0
  153. package/src/effect.ts +1 -0
  154. package/src/errors.ts +140 -0
  155. package/src/index.ts +26 -0
  156. package/src/orpc-client.ts +2 -0
  157. package/src/orpc-openapi.ts +2 -0
  158. package/src/orpc.ts +9 -0
  159. package/src/plugin.ts +220 -0
  160. package/src/runtime/errors.ts +332 -0
  161. package/src/runtime/index.ts +258 -0
  162. package/src/runtime/mf-config.ts +29 -0
  163. package/src/runtime/services/module-federation.service.ts +181 -0
  164. package/src/runtime/services/normalize.ts +18 -0
  165. package/src/runtime/services/plugin-lifecycle.service.ts +86 -0
  166. package/src/runtime/services/plugin-loader.service.ts +254 -0
  167. package/src/runtime/services/plugin.service.ts +66 -0
  168. package/src/runtime/services/secrets.service.ts +74 -0
  169. package/src/runtime/services.ts +5 -0
  170. package/src/runtime/validation.ts +23 -0
  171. package/src/testing/index.ts +87 -0
  172. package/src/testing/mocks/module-federation.service.ts +41 -0
  173. package/src/types.ts +309 -0
  174. package/src/zod-core.ts +4 -0
  175. package/src/zod.ts +1 -0
  176. package/dist/package.cjs +0 -45
  177. package/dist/package.cjs.map +0 -1
  178. package/dist/package.mjs +0 -27
  179. package/dist/package.mjs.map +0 -1
@@ -0,0 +1,254 @@
1
+ import type { InferSchemaInput, InferSchemaOutput } from "@orpc/contract";
2
+ import { Context, Effect, Scope } from "effect";
3
+ import type { z } from "zod";
4
+ import type {
5
+ AnyPlugin,
6
+ AnyPluginConstructor,
7
+ InitializedPlugin,
8
+ LoadedPlugin,
9
+ PluginInstance,
10
+ PluginMetadata,
11
+ PluginRegistry,
12
+ } from "../../types";
13
+ import { PluginRuntimeError, toPluginRuntimeError } from "../errors";
14
+ import { validate } from "../validation";
15
+ import { ModuleFederationService } from "./module-federation.service";
16
+ import { SecretsService } from "./secrets.service";
17
+
18
+ export class PluginRegistryTag extends Context.Tag("PluginRegistry")<
19
+ PluginRegistryTag,
20
+ PluginRegistry
21
+ >() {}
22
+
23
+ export class PluginMapTag extends Context.Tag("PluginMap")<
24
+ PluginMapTag,
25
+ Record<string, AnyPluginConstructor>
26
+ >() {}
27
+
28
+ export class RegistryService extends Effect.Service<RegistryService>()("RegistryService", {
29
+ effect: Effect.gen(function* () {
30
+ const registry = yield* PluginRegistryTag;
31
+ const pluginMap = yield* PluginMapTag;
32
+
33
+ return {
34
+ get: (pluginId: string) =>
35
+ Effect.gen(function* () {
36
+ const entry = registry[pluginId];
37
+
38
+ if (!entry) {
39
+ return yield* Effect.fail(
40
+ new PluginRuntimeError({
41
+ pluginId,
42
+ operation: "validate-plugin-id",
43
+ cause: new Error(`Plugin ${pluginId} not found in registry`),
44
+ retryable: false,
45
+ }),
46
+ );
47
+ }
48
+
49
+ if ("module" in entry) {
50
+ return {
51
+ constructor: entry.module,
52
+ metadata: {
53
+ remoteUrl: entry.remote || "",
54
+ version: entry.version,
55
+ description: entry.description,
56
+ } as PluginMetadata,
57
+ };
58
+ }
59
+
60
+ return {
61
+ constructor: null,
62
+ metadata: {
63
+ remoteUrl: entry.remote,
64
+ version: entry.version,
65
+ description: entry.description,
66
+ } as PluginMetadata,
67
+ };
68
+ }),
69
+
70
+ getModule: (pluginId: string) => Effect.succeed(pluginMap[pluginId] || null),
71
+ };
72
+ }),
73
+ }) {}
74
+
75
+ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
76
+ "PluginLoaderService",
77
+ {
78
+ effect: Effect.gen(function* () {
79
+ const moduleFederationService = yield* ModuleFederationService;
80
+ const secretsService = yield* SecretsService;
81
+ const registryService = yield* RegistryService;
82
+
83
+ const resolveUrl = (baseUrl: string, version?: string): string =>
84
+ version && version !== "latest" ? baseUrl.replace("@latest", `@${version}`) : baseUrl;
85
+
86
+ return {
87
+ loadPlugin: (pluginId: string) =>
88
+ Effect.gen(function* () {
89
+ const entry = yield* registryService.get(pluginId);
90
+
91
+ if (entry.constructor) {
92
+ yield* Effect.logDebug("Loading plugin from direct module", { pluginId });
93
+
94
+ return {
95
+ ctor: entry.constructor,
96
+ metadata: entry.metadata,
97
+ } satisfies LoadedPlugin;
98
+ }
99
+
100
+ const url = entry.metadata.remoteUrl;
101
+ if (!url) {
102
+ return yield* Effect.fail(
103
+ new PluginRuntimeError({
104
+ pluginId,
105
+ operation: "load-plugin",
106
+ cause: new Error(`Plugin ${pluginId} has no module or remote URL configured`),
107
+ retryable: false,
108
+ }),
109
+ );
110
+ }
111
+
112
+ const resolvedUrl = resolveUrl(url);
113
+
114
+ yield* moduleFederationService
115
+ .registerRemote(pluginId, resolvedUrl)
116
+ .pipe(
117
+ Effect.mapError((error) =>
118
+ toPluginRuntimeError(error, pluginId, undefined, "register-remote", true),
119
+ ),
120
+ );
121
+
122
+ yield* Effect.logDebug("Loading plugin from remote", { pluginId, url: resolvedUrl });
123
+
124
+ const ctor = yield* moduleFederationService
125
+ .loadRemoteConstructor(pluginId, resolvedUrl)
126
+ .pipe(
127
+ Effect.mapError((error) =>
128
+ toPluginRuntimeError(error, pluginId, undefined, "load-remote", false),
129
+ ),
130
+ );
131
+
132
+ return {
133
+ ctor,
134
+ metadata: entry.metadata,
135
+ } satisfies LoadedPlugin;
136
+ }),
137
+
138
+ instantiatePlugin: <T extends AnyPlugin>(pluginId: string, loadedPlugin: LoadedPlugin<T>) =>
139
+ Effect.gen(function* () {
140
+ const instance = yield* Effect.try(() => new loadedPlugin.ctor()).pipe(
141
+ Effect.mapError((error) =>
142
+ toPluginRuntimeError(error, pluginId, undefined, "instantiate-plugin", false),
143
+ ),
144
+ );
145
+
146
+ (instance.id as string) = pluginId;
147
+
148
+ return {
149
+ plugin: instance,
150
+ metadata: loadedPlugin.metadata,
151
+ } satisfies PluginInstance<T>;
152
+ }),
153
+
154
+ initializePlugin: <T extends AnyPlugin>(
155
+ pluginInstance: PluginInstance<T>,
156
+ config: {
157
+ variables: InferSchemaInput<T["configSchema"]["variables"]>;
158
+ secrets: InferSchemaInput<T["configSchema"]["secrets"]>;
159
+ },
160
+ plugins?: Record<string, unknown>,
161
+ ) =>
162
+ Effect.gen(function* () {
163
+ const { plugin } = pluginInstance;
164
+
165
+ // Validate and hydrate config
166
+ const validatedVariables = yield* validate(
167
+ plugin.configSchema.variables as z.ZodSchema<
168
+ InferSchemaOutput<T["configSchema"]["variables"]>
169
+ >,
170
+ config.variables,
171
+ plugin.id,
172
+ "config",
173
+ ).pipe(
174
+ Effect.mapError(
175
+ (validationError) =>
176
+ new PluginRuntimeError({
177
+ pluginId: plugin.id,
178
+ operation: "validate-config",
179
+ cause: validationError.zodError,
180
+ retryable: false,
181
+ }),
182
+ ),
183
+ );
184
+
185
+ // Validate secrets
186
+ const validatedSecrets = yield* validate(
187
+ plugin.configSchema.secrets as z.ZodSchema<
188
+ InferSchemaOutput<T["configSchema"]["secrets"]>
189
+ >,
190
+ config.secrets,
191
+ plugin.id,
192
+ "config",
193
+ ).pipe(
194
+ Effect.mapError(
195
+ (validationError) =>
196
+ new PluginRuntimeError({
197
+ pluginId: plugin.id,
198
+ operation: "validate-secrets",
199
+ cause: validationError.zodError,
200
+ retryable: false,
201
+ }),
202
+ ),
203
+ );
204
+
205
+ // Hydrate secrets in variables
206
+ const hydratedConfig = yield* secretsService.hydrateSecrets({
207
+ variables: validatedVariables,
208
+ secrets: validatedSecrets,
209
+ });
210
+
211
+ const _variables = yield* validate(
212
+ plugin.configSchema.variables as z.ZodSchema<
213
+ InferSchemaOutput<T["configSchema"]["variables"]>
214
+ >,
215
+ hydratedConfig.variables,
216
+ plugin.id,
217
+ "config",
218
+ ).pipe(
219
+ Effect.mapError(
220
+ (validationError) =>
221
+ new PluginRuntimeError({
222
+ pluginId: plugin.id,
223
+ operation: "validate-hydrated-config",
224
+ cause: validationError.zodError,
225
+ retryable: false,
226
+ }),
227
+ ),
228
+ );
229
+
230
+ // Create a long-lived scope for this plugin instance
231
+ const scope = yield* Scope.make();
232
+
233
+ // Initialize plugin within the scope
234
+ const context = yield* plugin
235
+ .initialize({ variables: _variables, secrets: hydratedConfig.secrets }, plugins ?? {})
236
+ .pipe(
237
+ Effect.provideService(Scope.Scope, scope),
238
+ Effect.mapError((error) =>
239
+ toPluginRuntimeError(error, plugin.id, undefined, "initialize-plugin", false),
240
+ ),
241
+ );
242
+
243
+ return {
244
+ plugin,
245
+ metadata: pluginInstance.metadata,
246
+ config: { variables: _variables, secrets: hydratedConfig.secrets },
247
+ context,
248
+ scope,
249
+ } satisfies InitializedPlugin<T>;
250
+ }),
251
+ };
252
+ }),
253
+ },
254
+ ) {}
@@ -0,0 +1,66 @@
1
+ import { Effect, Exit, Layer, Scope } from "effect";
2
+ import type {
3
+ AnyPlugin,
4
+ AnyPluginConstructor,
5
+ InitializedPlugin,
6
+ PluginRegistry,
7
+ SecretsConfig,
8
+ } from "../../types";
9
+ import { ModuleFederationService } from "./module-federation.service";
10
+ import { PluginLifecycleService } from "./plugin-lifecycle.service";
11
+ import {
12
+ PluginLoaderService,
13
+ PluginMapTag,
14
+ PluginRegistryTag,
15
+ RegistryService,
16
+ } from "./plugin-loader.service";
17
+ import { SecretsConfigTag, SecretsService } from "./secrets.service";
18
+
19
+ export class PluginService extends Effect.Service<PluginService>()("PluginService", {
20
+ scoped: Effect.gen(function* () {
21
+ const loader = yield* PluginLoaderService;
22
+ const lifecycle = yield* PluginLifecycleService;
23
+
24
+ return {
25
+ loadPlugin: loader.loadPlugin,
26
+ instantiatePlugin: loader.instantiatePlugin,
27
+ initializePlugin: loader.initializePlugin,
28
+ shutdownPlugin: (plugin: InitializedPlugin<AnyPlugin>) =>
29
+ Effect.gen(function* () {
30
+ // Shutdown the plugin first (graceful cleanup)
31
+ yield* plugin.plugin.shutdown().pipe(Effect.catchAll(() => Effect.void));
32
+
33
+ // Close the plugin scope to interrupt fibers and release resources
34
+ yield* Scope.close(plugin.scope, Exit.succeed(undefined));
35
+
36
+ // Unregister from lifecycle tracking
37
+ yield* lifecycle.unregister(plugin);
38
+ }),
39
+ cleanup: lifecycle.cleanup,
40
+ };
41
+ }),
42
+ }) {
43
+ static Live = (
44
+ registry: PluginRegistry,
45
+ secrets: SecretsConfig,
46
+ pluginMap: Record<string, AnyPluginConstructor> = {},
47
+ ) => {
48
+ const contextLayer = Layer.mergeAll(
49
+ Layer.succeed(PluginRegistryTag, registry),
50
+ Layer.succeed(SecretsConfigTag, secrets),
51
+ Layer.succeed(PluginMapTag, pluginMap),
52
+ );
53
+
54
+ const servicesLayer = Layer.mergeAll(
55
+ ModuleFederationService.Default,
56
+ SecretsService.Default,
57
+ RegistryService.Default,
58
+ PluginLifecycleService.Default,
59
+ ).pipe(Layer.provide(contextLayer));
60
+
61
+ return PluginService.Default.pipe(
62
+ Layer.provide(PluginLoaderService.Default),
63
+ Layer.provide(servicesLayer),
64
+ );
65
+ };
66
+ }
@@ -0,0 +1,74 @@
1
+ import { Context, Effect } from "effect";
2
+ import { z } from "zod";
3
+ import type { SecretsConfig } from "../../types";
4
+ import { PluginRuntimeError } from "../errors";
5
+
6
+ const configSchema = z
7
+ .object({
8
+ secrets: z.record(z.string(), z.unknown()),
9
+ })
10
+ .loose();
11
+
12
+ export class SecretsConfigTag extends Context.Tag("SecretsConfig")<
13
+ SecretsConfigTag,
14
+ SecretsConfig
15
+ >() {}
16
+
17
+ export class SecretsService extends Effect.Service<SecretsService>()("SecretsService", {
18
+ effect: Effect.gen(function* () {
19
+ const secrets = yield* SecretsConfigTag;
20
+
21
+ const hydrateValue = (value: unknown): unknown => {
22
+ if (typeof value === "string") {
23
+ let result = value;
24
+ for (const [key, secretValue] of Object.entries(secrets)) {
25
+ const pattern = new RegExp(`{{${key}}}`, "g");
26
+ result = result.replace(pattern, String(secretValue));
27
+ }
28
+ return result;
29
+ }
30
+
31
+ if (Array.isArray(value)) {
32
+ return value.map(hydrateValue);
33
+ }
34
+
35
+ if (value && typeof value === "object") {
36
+ const isPlainObject = value.constructor === Object || value.constructor === undefined;
37
+
38
+ if (isPlainObject) {
39
+ const hydrated: Record<string, unknown> = {};
40
+ for (const [key, val] of Object.entries(value)) {
41
+ hydrated[key] = hydrateValue(val);
42
+ }
43
+ return hydrated;
44
+ }
45
+
46
+ const hydrated = Object.create(Object.getPrototypeOf(value));
47
+ for (const [key, val] of Object.entries(value)) {
48
+ hydrated[key] = hydrateValue(val);
49
+ }
50
+ return hydrated;
51
+ }
52
+
53
+ return value;
54
+ };
55
+
56
+ return {
57
+ hydrateSecrets: <T>(config: T) =>
58
+ Effect.gen(function* () {
59
+ const parseResult = configSchema.parse(config);
60
+ try {
61
+ return hydrateValue(parseResult) as T;
62
+ } catch (error) {
63
+ return yield* Effect.fail(
64
+ new PluginRuntimeError({
65
+ operation: "hydrate-secrets",
66
+ cause: error instanceof Error ? error : new Error(String(error)),
67
+ retryable: false,
68
+ }),
69
+ );
70
+ }
71
+ }),
72
+ };
73
+ }),
74
+ }) {}
@@ -0,0 +1,5 @@
1
+ export * from "./services/module-federation.service";
2
+ export * from "./services/plugin.service";
3
+ export * from "./services/plugin-lifecycle.service";
4
+ export * from "./services/plugin-loader.service";
5
+ export * from "./services/secrets.service";
@@ -0,0 +1,23 @@
1
+ import { Effect } from "effect";
2
+ import type { z } from "zod";
3
+ import { ValidationError } from "./errors";
4
+
5
+ export const validate = <T>(
6
+ schema: z.ZodSchema<T>,
7
+ data: unknown,
8
+ pluginId: string,
9
+ stage: "config" | "input" | "output" | "state",
10
+ ): Effect.Effect<T, ValidationError> =>
11
+ Effect.gen(function* () {
12
+ const result = schema.safeParse(data);
13
+ if (result.success) {
14
+ return result.data;
15
+ }
16
+ return yield* Effect.fail(
17
+ new ValidationError({
18
+ pluginId,
19
+ stage,
20
+ zodError: result.error,
21
+ }),
22
+ );
23
+ });
@@ -0,0 +1,87 @@
1
+ import type { LoadedPluginWithBinding } from "../plugin";
2
+ import { createPluginRuntime } from "../runtime";
3
+ import type { AnyPluginConstructor, PluginRegistryEntry, PluginRuntimeConfig } from "../types";
4
+
5
+ export type PluginMap = Record<string, LoadedPluginWithBinding<any, any, any, any>>;
6
+
7
+ /**
8
+ * Simplified type inference for local plugin maps.
9
+ * Just use the constructor types directly.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const pluginMap = { "my-plugin": MyPlugin } as const;
14
+ * type MyBindings = InferBindingsFromMap<typeof pluginMap>;
15
+ * ```
16
+ */
17
+ export type InferBindingsFromMap<T extends PluginMap> = {
18
+ [K in keyof T]: T[K];
19
+ };
20
+
21
+ /**
22
+ * Converts old pluginMap format to new registry format
23
+ */
24
+ function convertPluginMapToRegistry<T extends PluginMap>(
25
+ oldRegistry: Record<string, { remoteUrl: string; version?: string; description?: string }>,
26
+ pluginMap: T,
27
+ ): Record<keyof T, PluginRegistryEntry> {
28
+ const registry: Record<string, PluginRegistryEntry> = {};
29
+
30
+ for (const [pluginId, ctor] of Object.entries(pluginMap)) {
31
+ const oldEntry = oldRegistry[pluginId];
32
+ registry[pluginId] = {
33
+ module: ctor as AnyPluginConstructor,
34
+ remote: oldEntry?.remoteUrl,
35
+ version: oldEntry?.version,
36
+ description: oldEntry?.description,
37
+ };
38
+ }
39
+
40
+ return registry as Record<keyof T, PluginRegistryEntry>;
41
+ }
42
+
43
+ /**
44
+ * Creates a plugin runtime for locally available plugins (non-remote).
45
+ * Automatically infers type bindings from the plugin map, eliminating
46
+ * the need for manual RegistryBindings definitions.
47
+ *
48
+ * @deprecated Use `createPluginRuntime` with module entries directly instead.
49
+ * @example
50
+ * ```ts
51
+ * // Old API (deprecated)
52
+ * const runtime = createLocalPluginRuntime(
53
+ * { registry: { "my-plugin": { remoteUrl: "..." } }, secrets },
54
+ * { "my-plugin": MyPlugin }
55
+ * );
56
+ *
57
+ * // New API (recommended)
58
+ * const runtime = createPluginRuntime({
59
+ * registry: { "my-plugin": { module: MyPlugin } },
60
+ * secrets
61
+ * });
62
+ * ```
63
+ */
64
+ export function createLocalPluginRuntime<TMap extends PluginMap>(
65
+ config: {
66
+ registry: Record<string, { remoteUrl: string; version?: string; description?: string }>;
67
+ secrets?: Record<string, string>;
68
+ options?: any;
69
+ },
70
+ pluginMap: TMap,
71
+ ) {
72
+ const newRegistry = convertPluginMapToRegistry(config.registry, pluginMap);
73
+
74
+ return createPluginRuntime({
75
+ registry: newRegistry,
76
+ secrets: config.secrets,
77
+ options: config.options,
78
+ });
79
+ }
80
+
81
+ /**
82
+ * @deprecated Use `createLocalPluginRuntime` or `createPluginRuntime` instead.
83
+ */
84
+ export const createTestPluginRuntime = createLocalPluginRuntime;
85
+
86
+ export type { EveryPlugin, PluginRegistry, RegisteredPlugins } from "../types";
87
+ export type { PluginRuntimeConfig };
@@ -0,0 +1,41 @@
1
+ import { Effect, Layer } from "effect";
2
+ import type { LoadedPluginWithBinding } from "../../plugin";
3
+ import { ModuleFederationError } from "../../runtime/errors";
4
+ import { ModuleFederationService } from "../../runtime/services/module-federation.service";
5
+
6
+ export type PluginMap = Record<string, LoadedPluginWithBinding<any, any, any, any>>;
7
+
8
+ export const createMockModuleFederationServiceLayer = (pluginMap: PluginMap) =>
9
+ Layer.succeed(ModuleFederationService, {
10
+ registerRemote: (pluginId: string, url: string) =>
11
+ Effect.gen(function* () {
12
+ console.log(`[MOCK] registerRemote called for ${pluginId}`);
13
+ if (!(pluginId in pluginMap)) {
14
+ return yield* Effect.fail(
15
+ new ModuleFederationError({
16
+ pluginId,
17
+ remoteUrl: url,
18
+ cause: new Error(`Mock: Plugin ${pluginId} not available in test plugin map`),
19
+ }),
20
+ );
21
+ }
22
+ console.log(`[MOCK] registerRemote succeeded for ${pluginId}`);
23
+ }),
24
+
25
+ loadRemoteConstructor: (pluginId: string, url: string) =>
26
+ Effect.gen(function* () {
27
+ console.log(`[MOCK] loadRemoteConstructor called for ${pluginId}`);
28
+ const PluginConstructor = pluginMap[pluginId];
29
+ if (!PluginConstructor) {
30
+ return yield* Effect.fail(
31
+ new ModuleFederationError({
32
+ pluginId,
33
+ remoteUrl: url,
34
+ cause: new Error(`Mock: Constructor for ${pluginId} not found in test plugin map`),
35
+ }),
36
+ );
37
+ }
38
+ console.log(`[MOCK] loadRemoteConstructor succeeded for ${pluginId}`);
39
+ return PluginConstructor;
40
+ }),
41
+ } as any);