every-plugin 0.1.2 → 0.1.4
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/dist/client/index.d.ts +18 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +33 -0
- package/dist/effect.d.ts +2 -0
- package/dist/effect.d.ts.map +1 -0
- package/dist/effect.js +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/orpc.d.ts +3 -0
- package/dist/orpc.d.ts.map +1 -0
- package/dist/orpc.js +2 -0
- package/dist/plugin.d.ts +56 -85
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +33 -103
- package/dist/runtime/errors.d.ts +0 -14
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/errors.js +0 -2
- package/dist/runtime/index.d.ts +40 -18
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +127 -24
- package/dist/runtime/services/module-federation.service.d.ts +3 -2
- package/dist/runtime/services/module-federation.service.d.ts.map +1 -1
- package/dist/runtime/services/module-federation.service.js +21 -23
- package/dist/runtime/services/normalize.d.ts +18 -0
- package/dist/runtime/services/normalize.d.ts.map +1 -0
- package/dist/runtime/services/normalize.js +17 -0
- package/dist/runtime/services/plugin-lifecycle.service.d.ts +17 -0
- package/dist/runtime/services/plugin-lifecycle.service.d.ts.map +1 -0
- package/dist/runtime/services/plugin-lifecycle.service.js +35 -0
- package/dist/runtime/services/plugin-loader.service.d.ts +21 -0
- package/dist/runtime/services/plugin-loader.service.d.ts.map +1 -0
- package/dist/runtime/services/plugin-loader.service.js +83 -0
- package/dist/runtime/services/plugin.service.d.ts +12 -20
- package/dist/runtime/services/plugin.service.d.ts.map +1 -1
- package/dist/runtime/services/plugin.service.js +18 -268
- package/dist/runtime/services/secrets.service.d.ts +2 -3
- package/dist/runtime/services/secrets.service.d.ts.map +1 -1
- package/dist/runtime/services/secrets.service.js +7 -5
- package/dist/runtime/services.d.ts +1 -1
- package/dist/runtime/services.d.ts.map +1 -1
- package/dist/runtime/services.js +1 -1
- package/dist/testing/index.d.ts +11 -16
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +25 -27
- package/dist/testing/mocks/module-federation.service.d.ts +1 -1
- package/dist/testing/mocks/module-federation.service.d.ts.map +1 -1
- package/dist/types.d.ts +177 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/zod.d.ts +2 -0
- package/dist/zod.d.ts.map +1 -0
- package/dist/zod.js +1 -0
- package/package.json +99 -57
- package/dist/runtime/streaming/index.d.ts +0 -4
- package/dist/runtime/streaming/index.d.ts.map +0 -1
- package/dist/runtime/streaming/index.js +0 -2
- package/dist/runtime/streaming/state-effects.d.ts +0 -28
- package/dist/runtime/streaming/state-effects.d.ts.map +0 -1
- package/dist/runtime/streaming/state-effects.js +0 -30
- package/dist/runtime/streaming/streaming.d.ts +0 -8
- package/dist/runtime/streaming/streaming.d.ts.map +0 -1
- package/dist/runtime/streaming/streaming.js +0 -113
- package/dist/runtime/streaming/types.d.ts +0 -19
- package/dist/runtime/streaming/types.d.ts.map +0 -1
- package/dist/runtime/test-utils.d.ts +0 -3
- package/dist/runtime/test-utils.d.ts.map +0 -1
- package/dist/runtime/test-utils.js +0 -7
- package/dist/runtime/types.d.ts +0 -40
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -1
- /package/dist/{runtime/streaming/types.js → types.js} +0 -0
|
@@ -1,275 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { SecretsService } from "./secrets.service";
|
|
9
|
-
export class PluginService extends Effect.Tag("PluginService")() {
|
|
10
|
-
static Live = (registry, secrets) => Layer.effect(PluginService, Effect.gen(function* () {
|
|
11
|
-
const moduleFederationService = yield* ModuleFederationService;
|
|
12
|
-
const secretsService = yield* SecretsService;
|
|
13
|
-
const logger = yield* PluginLoggerTag;
|
|
14
|
-
const activePlugins = new Set();
|
|
15
|
-
const retrySchedule = Schedule.exponential(Duration.millis(100)).pipe(Schedule.compose(Schedule.recurs(2)));
|
|
16
|
-
const resolveUrl = (baseUrl, version) => version && version !== "latest"
|
|
17
|
-
? baseUrl.replace("@latest", `@${version}`)
|
|
18
|
-
: baseUrl;
|
|
19
|
-
// Implementation functions
|
|
20
|
-
const loadPluginImpl = (pluginId) => Effect.gen(function* () {
|
|
21
|
-
const metadata = registry[pluginId];
|
|
22
|
-
if (!metadata) {
|
|
23
|
-
return yield* Effect.fail(new PluginRuntimeError({
|
|
24
|
-
pluginId,
|
|
25
|
-
operation: "load-plugin",
|
|
26
|
-
cause: new Error(`Plugin ${pluginId} not found in registry`),
|
|
27
|
-
retryable: false,
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
const url = resolveUrl(metadata.remoteUrl);
|
|
31
|
-
// Register and load the remote constructor
|
|
32
|
-
yield* moduleFederationService.registerRemote(pluginId, url).pipe(Effect.mapError((error) => new PluginRuntimeError({
|
|
33
|
-
pluginId,
|
|
34
|
-
operation: "register-remote",
|
|
35
|
-
cause: error.cause,
|
|
36
|
-
retryable: true,
|
|
37
|
-
})));
|
|
38
|
-
logger.logDebug(`Trying to load ${pluginId}:${url}`);
|
|
39
|
-
const ctor = yield* moduleFederationService.loadRemoteConstructor(pluginId, url).pipe(Effect.mapError((error) => new PluginRuntimeError({
|
|
40
|
-
pluginId,
|
|
41
|
-
operation: "load-remote",
|
|
42
|
-
cause: error.cause,
|
|
43
|
-
retryable: false
|
|
44
|
-
})));
|
|
45
|
-
return {
|
|
46
|
-
ctor,
|
|
47
|
-
metadata: {
|
|
48
|
-
pluginId,
|
|
49
|
-
version: metadata.version,
|
|
50
|
-
description: metadata.description,
|
|
51
|
-
type: metadata.type,
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
});
|
|
55
|
-
const instantiatePluginImpl = (pluginConstructor) => Effect.gen(function* () {
|
|
56
|
-
const instance = yield* Effect.try({
|
|
57
|
-
try: () => new pluginConstructor.ctor(),
|
|
58
|
-
catch: (error) => new PluginRuntimeError({
|
|
59
|
-
pluginId: pluginConstructor.metadata.pluginId,
|
|
60
|
-
operation: "instantiate-plugin",
|
|
61
|
-
cause: error instanceof Error ? error : new Error(String(error)),
|
|
62
|
-
retryable: false,
|
|
63
|
-
}),
|
|
64
|
-
});
|
|
65
|
-
// Validate plugin ID matches
|
|
66
|
-
if (instance.id !== pluginConstructor.metadata.pluginId) {
|
|
67
|
-
return yield* Effect.fail(new PluginRuntimeError({
|
|
68
|
-
pluginId: pluginConstructor.metadata.pluginId,
|
|
69
|
-
operation: "validate-plugin-id",
|
|
70
|
-
cause: new Error(`Plugin ID mismatch: expected ${pluginConstructor.metadata.pluginId}, got ${instance.id}`),
|
|
71
|
-
retryable: false,
|
|
72
|
-
}));
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
plugin: instance,
|
|
76
|
-
metadata: pluginConstructor.metadata,
|
|
77
|
-
};
|
|
78
|
-
});
|
|
79
|
-
const initializePluginImpl = (pluginInstance, config) => Effect.gen(function* () {
|
|
80
|
-
const { plugin } = pluginInstance;
|
|
81
|
-
// Validate raw config using plugin's Zod schema
|
|
82
|
-
const validatedConfig = yield* validate(plugin.configSchema, config, plugin.id, "config").pipe(Effect.mapError((validationError) => new PluginRuntimeError({
|
|
83
|
-
pluginId: plugin.id,
|
|
84
|
-
operation: "validate-config",
|
|
85
|
-
cause: validationError.zodError,
|
|
86
|
-
retryable: false,
|
|
87
|
-
})));
|
|
88
|
-
// Hydrate secrets
|
|
89
|
-
const hydratedConfig = yield* secretsService.hydrateSecrets(validatedConfig, secrets);
|
|
90
|
-
// Validate hydrated config
|
|
91
|
-
const finalConfig = yield* validate(plugin.configSchema, hydratedConfig, plugin.id, "config").pipe(Effect.mapError((validationError) => new PluginRuntimeError({
|
|
92
|
-
pluginId: plugin.id,
|
|
93
|
-
operation: "validate-hydrated-config",
|
|
94
|
-
cause: validationError.zodError,
|
|
95
|
-
retryable: false,
|
|
96
|
-
})));
|
|
97
|
-
// Initialize plugin with retry logic
|
|
98
|
-
const pluginLayer = Layer.succeed(PluginLoggerTag, logger);
|
|
99
|
-
yield* plugin.initialize(finalConfig).pipe(Effect.mapError((error) => new PluginRuntimeError({
|
|
100
|
-
pluginId: plugin.id,
|
|
101
|
-
operation: "initialize-plugin",
|
|
102
|
-
cause: error,
|
|
103
|
-
retryable: error.retryable ?? false,
|
|
104
|
-
})), Effect.retry(retrySchedule.pipe(Schedule.whileInput((error) => error.retryable))), Effect.provide(pluginLayer));
|
|
105
|
-
const initializedPlugin = {
|
|
106
|
-
plugin,
|
|
107
|
-
metadata: pluginInstance.metadata,
|
|
108
|
-
config: finalConfig, // TODO: config type
|
|
109
|
-
};
|
|
110
|
-
activePlugins.add(initializedPlugin);
|
|
111
|
-
return initializedPlugin;
|
|
112
|
-
});
|
|
113
|
-
// Cache functionality
|
|
114
|
-
const generateConfigHash = (config) => {
|
|
115
|
-
return Hash.structure(config).toString();
|
|
116
|
-
};
|
|
117
|
-
// Store original config by hash for cache lookup
|
|
118
|
-
const configByHash = new Map();
|
|
119
|
-
const cache = yield* Cache.make({
|
|
120
|
-
capacity: 1024,
|
|
121
|
-
timeToLive: Duration.minutes(60),
|
|
122
|
-
lookup: (cacheKey) => {
|
|
123
|
-
const [pluginId, configHash] = cacheKey.split(':');
|
|
124
|
-
if (!pluginId || !configHash) {
|
|
125
|
-
return Effect.fail(new PluginRuntimeError({
|
|
126
|
-
operation: "cache-lookup",
|
|
127
|
-
cause: new Error(`Invalid cache key format: ${cacheKey}`),
|
|
128
|
-
retryable: false,
|
|
129
|
-
}));
|
|
130
|
-
}
|
|
131
|
-
const config = configByHash.get(configHash);
|
|
132
|
-
if (!config) {
|
|
133
|
-
return Effect.fail(new PluginRuntimeError({
|
|
134
|
-
operation: "cache-lookup",
|
|
135
|
-
cause: new Error(`No config found for hash: ${configHash}`),
|
|
136
|
-
retryable: false,
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
139
|
-
return Effect.gen(function* () {
|
|
140
|
-
const ctor = yield* loadPluginImpl(pluginId);
|
|
141
|
-
const instance = yield* instantiatePluginImpl(ctor);
|
|
142
|
-
return yield* initializePluginImpl(instance, config);
|
|
143
|
-
});
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
const usePluginImpl = (pluginId, config) => {
|
|
147
|
-
const configHash = generateConfigHash(config);
|
|
148
|
-
const cacheKey = `${pluginId}:${configHash}`;
|
|
149
|
-
// Store config for cache lookup
|
|
150
|
-
configByHash.set(configHash, config);
|
|
151
|
-
return cache.get(cacheKey).pipe(Effect.map(plugin => plugin));
|
|
152
|
-
};
|
|
153
|
-
// Extract executePlugin implementation for reuse
|
|
154
|
-
const executePluginImpl = (initializedPlugin, input) => Effect.gen(function* () {
|
|
155
|
-
const { plugin } = initializedPlugin;
|
|
156
|
-
// Validate input
|
|
157
|
-
const validatedInput = yield* validate(plugin.inputSchema, input, plugin.id, "input").pipe(Effect.mapError((validationError) => {
|
|
158
|
-
return new PluginRuntimeError({
|
|
159
|
-
pluginId: plugin.id,
|
|
160
|
-
operation: "validate-input",
|
|
161
|
-
cause: validationError.zodError,
|
|
162
|
-
retryable: false,
|
|
163
|
-
});
|
|
164
|
-
}));
|
|
165
|
-
// Get oRPC router and call procedure directly
|
|
166
|
-
const router = plugin.createRouter();
|
|
167
|
-
const procedureName = validatedInput.procedure;
|
|
168
|
-
if (!(procedureName in router)) {
|
|
169
|
-
return yield* Effect.fail(new PluginRuntimeError({
|
|
170
|
-
pluginId: plugin.id,
|
|
171
|
-
operation: "execute-plugin",
|
|
172
|
-
cause: new Error(`Unknown procedure: ${procedureName}`),
|
|
173
|
-
retryable: false,
|
|
174
|
-
}));
|
|
175
|
-
}
|
|
176
|
-
const context = 'state' in validatedInput ? { state: validatedInput.state } : {};
|
|
177
|
-
const output = yield* Effect.tryPromise({
|
|
178
|
-
try: () => {
|
|
179
|
-
const procedure = router[procedureName];
|
|
180
|
-
;
|
|
181
|
-
if (!procedure) {
|
|
182
|
-
throw new Error(`Procedure ${procedureName} not found in router`);
|
|
183
|
-
}
|
|
184
|
-
// Use oRPC call utility as per docs
|
|
185
|
-
return call(procedure, validatedInput.input, { context });
|
|
186
|
-
},
|
|
187
|
-
catch: (error) => new PluginRuntimeError({
|
|
188
|
-
pluginId: plugin.id,
|
|
189
|
-
operation: "execute-plugin",
|
|
190
|
-
cause: error instanceof Error ? error : new Error(String(error)),
|
|
191
|
-
retryable: true,
|
|
192
|
-
}),
|
|
193
|
-
}).pipe(Effect.retry(retrySchedule.pipe(Schedule.whileInput((error) => error.retryable))));
|
|
194
|
-
return output;
|
|
195
|
-
});
|
|
1
|
+
import { Context, Effect, Exit, Layer, Scope } from "effect";
|
|
2
|
+
import { PluginLifecycleService } from "./plugin-lifecycle.service";
|
|
3
|
+
import { PluginLoaderService } from "./plugin-loader.service";
|
|
4
|
+
export class PluginService extends Context.Tag("PluginService")() {
|
|
5
|
+
static Live = (registry, secrets) => Layer.scoped(PluginService, Effect.gen(function* () {
|
|
6
|
+
const loader = yield* PluginLoaderService;
|
|
7
|
+
const lifecycle = yield* PluginLifecycleService;
|
|
196
8
|
return {
|
|
197
|
-
loadPlugin:
|
|
198
|
-
instantiatePlugin:
|
|
199
|
-
initializePlugin:
|
|
200
|
-
executePlugin: executePluginImpl,
|
|
201
|
-
usePlugin: usePluginImpl,
|
|
202
|
-
streamPlugin: (pluginId, config, input, options) => Effect.gen(function* () {
|
|
203
|
-
const initializedPlugin = yield* usePluginImpl(pluginId, config);
|
|
204
|
-
// Check if procedure is streamable and validate state
|
|
205
|
-
const procedureName = input.procedure;
|
|
206
|
-
const isStreamable = initializedPlugin.plugin.isStreamable(procedureName);
|
|
207
|
-
if (!isStreamable) {
|
|
208
|
-
return yield* Effect.fail(new PluginRuntimeError({
|
|
209
|
-
pluginId,
|
|
210
|
-
operation: "stream-plugin-validate",
|
|
211
|
-
cause: new Error(`Procedure ${procedureName} is not streamable`),
|
|
212
|
-
retryable: false,
|
|
213
|
-
}));
|
|
214
|
-
}
|
|
215
|
-
// Check stateSchema exists for streamable procedures
|
|
216
|
-
if (!('stateSchema' in initializedPlugin.plugin) || !initializedPlugin.plugin.stateSchema) {
|
|
217
|
-
return yield* Effect.fail(new PluginRuntimeError({
|
|
218
|
-
pluginId: initializedPlugin.plugin.id,
|
|
219
|
-
operation: "validate-state",
|
|
220
|
-
cause: new Error(`Streamable plugin ${initializedPlugin.plugin.id} must have a stateSchema`),
|
|
221
|
-
retryable: false,
|
|
222
|
-
}));
|
|
223
|
-
}
|
|
224
|
-
// Validate initial state - allow null for initial calls
|
|
225
|
-
const nullableStateSchema = initializedPlugin.plugin.stateSchema.nullable();
|
|
226
|
-
yield* validate(nullableStateSchema, input.state, initializedPlugin.plugin.id, "state").pipe(Effect.mapError((validationError) => new PluginRuntimeError({
|
|
227
|
-
pluginId: initializedPlugin.plugin.id,
|
|
228
|
-
operation: "validate-state",
|
|
229
|
-
cause: validationError.zodError,
|
|
230
|
-
retryable: false,
|
|
231
|
-
})));
|
|
232
|
-
// Create and return the stream
|
|
233
|
-
return createSourceStream(initializedPlugin, executePluginImpl, input, options);
|
|
234
|
-
}),
|
|
9
|
+
loadPlugin: loader.loadPlugin,
|
|
10
|
+
instantiatePlugin: loader.instantiatePlugin,
|
|
11
|
+
initializePlugin: loader.initializePlugin,
|
|
235
12
|
shutdownPlugin: (plugin) => Effect.gen(function* () {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
yield* cache.invalidate(cacheKey).pipe(Effect.catchAll(() => Effect.void) // Ignore if not in cache
|
|
243
|
-
);
|
|
244
|
-
// Clean up config hash
|
|
245
|
-
configByHash.delete(configHash);
|
|
246
|
-
// Shutdown the plugin
|
|
247
|
-
yield* plugin.plugin.shutdown().pipe(Effect.mapError((error) => new PluginRuntimeError({
|
|
248
|
-
pluginId: plugin.plugin.id,
|
|
249
|
-
operation: "shutdown-plugin",
|
|
250
|
-
cause: error,
|
|
251
|
-
retryable: false,
|
|
252
|
-
})), Effect.provide(pluginLayer));
|
|
253
|
-
}),
|
|
254
|
-
clearCache: () => Effect.sync(() => {
|
|
255
|
-
configByHash.clear();
|
|
256
|
-
}),
|
|
257
|
-
cleanup: () => Effect.gen(function* () {
|
|
258
|
-
const pluginLayer = Layer.succeed(PluginLoggerTag, logger);
|
|
259
|
-
// Call shutdown on all active plugins
|
|
260
|
-
for (const initializedPlugin of activePlugins) {
|
|
261
|
-
yield* initializedPlugin.plugin.shutdown().pipe(Effect.catchAll((error) => {
|
|
262
|
-
// Log shutdown errors but don't fail the cleanup
|
|
263
|
-
return logger.logError(`Failed to shutdown plugin ${initializedPlugin.plugin.id}`, error, { pluginId: initializedPlugin.plugin.id });
|
|
264
|
-
}), Effect.provide(pluginLayer));
|
|
265
|
-
}
|
|
266
|
-
// Clear the active plugins set
|
|
267
|
-
activePlugins.clear();
|
|
268
|
-
// Clear cache
|
|
269
|
-
configByHash.clear();
|
|
13
|
+
// Shutdown the plugin first (graceful cleanup)
|
|
14
|
+
yield* plugin.plugin.shutdown().pipe(Effect.catchAll(() => Effect.void));
|
|
15
|
+
// Close the plugin scope to interrupt fibers and release resources
|
|
16
|
+
yield* Scope.close(plugin.scope, Exit.succeed(undefined));
|
|
17
|
+
// Unregister from lifecycle tracking
|
|
18
|
+
yield* lifecycle.unregister(plugin);
|
|
270
19
|
}),
|
|
20
|
+
cleanup: lifecycle.cleanup,
|
|
271
21
|
};
|
|
272
|
-
}));
|
|
22
|
+
})).pipe(Layer.provide(Layer.mergeAll(PluginLoaderService.Live(registry, secrets), PluginLifecycleService.Live)));
|
|
273
23
|
}
|
|
274
24
|
export const PluginServiceTag = PluginService;
|
|
275
25
|
export const PluginServiceLive = PluginService.Live;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
|
+
import type { SecretsConfig } from "../../types";
|
|
2
3
|
import { PluginRuntimeError } from "../errors";
|
|
3
|
-
import type { SecretsConfig } from "../types";
|
|
4
4
|
export interface ISecretsService {
|
|
5
|
-
readonly hydrateSecrets: (config:
|
|
6
|
-
secretsConfig: SecretsConfig) => Effect.Effect<any, PluginRuntimeError>;
|
|
5
|
+
readonly hydrateSecrets: <T>(config: T, secretsConfig: SecretsConfig) => Effect.Effect<T, PluginRuntimeError>;
|
|
7
6
|
}
|
|
8
7
|
declare const SecretsService_base: import("effect/Context").TagClass<SecretsService, "SecretsService", ISecretsService> & Effect.Tag.Proxy<SecretsService, ISecretsService> & {
|
|
9
8
|
use: <X>(body: (_: ISecretsService) => X) => [X] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, R | SecretsService> : [X] extends [PromiseLike<infer A_1>] ? Effect.Effect<A_1, import("effect/Cause").UnknownException, SecretsService> : Effect.Effect<X, never, SecretsService>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.service.d.ts","sourceRoot":"","sources":["../../../src/runtime/services/secrets.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"secrets.service.d.ts","sourceRoot":"","sources":["../../../src/runtime/services/secrets.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC,EAC1B,MAAM,EAAE,CAAC,EACT,aAAa,EAAE,aAAa,KACxB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;CAC1C;;;;AAMD,qBAAa,cAAe,SAAQ,mBAGjC;IACF,MAAM,CAAC,IAAI,GAAI,SAAS,aAAa,+CA8BjC;CACJ;AAED,eAAO,MAAM,iBAAiB,uBAAiB,CAAC;AAChD,eAAO,MAAM,kBAAkB,YAlCN,aAAa,8CAkCe,CAAC"}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
import { PluginRuntimeError } from "../errors";
|
|
4
|
+
const configSchema = z.object({
|
|
5
|
+
secrets: z.record(z.string(), z.unknown())
|
|
6
|
+
}).loose();
|
|
3
7
|
export class SecretsService extends Effect.Tag("SecretsService")() {
|
|
4
8
|
static Live = (secrets) => Layer.succeed(SecretsService, {
|
|
5
9
|
hydrateSecrets: (config, secretsConfig) => Effect.gen(function* () {
|
|
6
|
-
|
|
7
|
-
return config;
|
|
8
|
-
}
|
|
10
|
+
const parseResult = configSchema.parse(config);
|
|
9
11
|
try {
|
|
10
|
-
const configString = JSON.stringify(
|
|
12
|
+
const configString = JSON.stringify(parseResult);
|
|
11
13
|
let hydratedString = configString;
|
|
12
14
|
// Use provided secrets or fallback to secretsConfig
|
|
13
15
|
const effectiveSecrets = { ...secretsConfig, ...secrets };
|
|
14
16
|
// Simple template replacement for {{SECRET_NAME}} patterns
|
|
15
17
|
for (const [key, value] of Object.entries(effectiveSecrets)) {
|
|
16
18
|
const pattern = new RegExp(`{{${key}}}`, "g");
|
|
17
|
-
hydratedString = hydratedString.replace(pattern, value);
|
|
19
|
+
hydratedString = hydratedString.replace(pattern, String(value));
|
|
18
20
|
}
|
|
19
21
|
return JSON.parse(hydratedString);
|
|
20
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/runtime/services.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/runtime/services.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,sCAAsC,CAAA"}
|
package/dist/runtime/services.js
CHANGED
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
import { Effect, Layer, ManagedRuntime } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import type { PluginRuntimeConfig } from "../runtime/types";
|
|
2
|
+
import { PluginRuntimeImpl, PluginRuntimeService } from "../runtime";
|
|
3
|
+
import type { PluginRuntimeConfig, RegistryBindings } from "../types";
|
|
5
4
|
import { type TestPluginMap } from "./mocks/module-federation.service";
|
|
6
|
-
export declare const createTestLogger: () => {
|
|
7
|
-
logInfo: (message: string, context?: unknown) => Effect.Effect<void, never, never>;
|
|
8
|
-
logWarning: (message: string, context?: unknown) => Effect.Effect<void, never, never>;
|
|
9
|
-
logError: (message: string, error?: unknown, context?: unknown) => Effect.Effect<void, never, never>;
|
|
10
|
-
logDebug: (message: string, context?: unknown) => Effect.Effect<void, never, never>;
|
|
11
|
-
};
|
|
12
5
|
/**
|
|
13
6
|
* Creates a test layer with mock ModuleFederationService for unit testing.
|
|
14
|
-
* This
|
|
15
|
-
* without requiring real module federation infrastructure.
|
|
7
|
+
* This mirrors PluginRuntimeService.Live but uses mock ModuleFederationService.
|
|
16
8
|
*/
|
|
17
|
-
export declare const createTestLayer: (config: PluginRuntimeConfig
|
|
9
|
+
export declare const createTestLayer: <R extends RegistryBindings = RegistryBindings>(config: PluginRuntimeConfig<R>, pluginMap: TestPluginMap) => Layer.Layer<PluginRuntimeService, never, never>;
|
|
18
10
|
/**
|
|
19
11
|
* Creates a test plugin runtime using mock services.
|
|
20
|
-
* This
|
|
21
|
-
* instead of the real one, making it suitable for unit tests.
|
|
12
|
+
* This mirrors createPluginRuntime exactly but uses test layer.
|
|
22
13
|
*/
|
|
23
|
-
export declare const createTestPluginRuntime: (config: PluginRuntimeConfig
|
|
24
|
-
|
|
14
|
+
export declare const createTestPluginRuntime: <R extends RegistryBindings = RegistryBindings>(config: PluginRuntimeConfig<R>, pluginMap: TestPluginMap) => {
|
|
15
|
+
runtime: ManagedRuntime.ManagedRuntime<PluginRuntimeService, never>;
|
|
16
|
+
PluginRuntime: Effect.Effect<PluginRuntimeImpl<R>, never, never>;
|
|
17
|
+
};
|
|
18
|
+
export type { PluginRegistry, RegistryBindings } from "../types";
|
|
19
|
+
export type { PluginRuntimeConfig, TestPluginMap };
|
|
25
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGrE,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtE,OAAO,EAA0C,KAAK,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAG/G;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,gBAAgB,GAAG,gBAAgB,EAC3E,QAAQ,mBAAmB,CAAC,CAAC,CAAC,EAC9B,WAAW,aAAa,oDA+BzB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,gBAAgB,GAAG,gBAAgB,EACnF,QAAQ,mBAAmB,CAAC,CAAC,CAAC,EAC9B,WAAW,aAAa;;;CAezB,CAAC;AAGF,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjE,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/testing/index.js
CHANGED
|
@@ -1,40 +1,38 @@
|
|
|
1
1
|
import { Effect, Layer, ManagedRuntime } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import { PluginRuntime } from "../runtime";
|
|
4
|
-
import { PluginService, SecretsService } from "../runtime/services";
|
|
2
|
+
import { PluginRuntimeImpl, PluginRuntimeService } from "../runtime";
|
|
5
3
|
// Import the mock service factory and types
|
|
4
|
+
import { PluginService, SecretsService } from "../runtime/services";
|
|
6
5
|
import { createMockModuleFederationServiceLayer } from "./mocks/module-federation.service";
|
|
7
|
-
// Create test logger
|
|
8
|
-
export const createTestLogger = () => ({
|
|
9
|
-
logInfo: (message, context) => Effect.sync(() => console.log(`[INFO] ${message}`, context || '')),
|
|
10
|
-
logWarning: (message, context) => Effect.sync(() => console.warn(`[WARNING] ${message}`, context || '')),
|
|
11
|
-
logError: (message, error, context) => Effect.sync(() => console.error(`[ERROR] ${message}`, error, context || '')),
|
|
12
|
-
logDebug: (message, context) => Effect.sync(() => console.log(`[DEBUG] ${message}`, context || '')),
|
|
13
|
-
});
|
|
14
6
|
/**
|
|
15
7
|
* Creates a test layer with mock ModuleFederationService for unit testing.
|
|
16
|
-
* This
|
|
17
|
-
* without requiring real module federation infrastructure.
|
|
8
|
+
* This mirrors PluginRuntimeService.Live but uses mock ModuleFederationService.
|
|
18
9
|
*/
|
|
19
10
|
export const createTestLayer = (config, pluginMap) => {
|
|
20
11
|
const secrets = config.secrets || {};
|
|
21
|
-
|
|
22
|
-
return Layer.
|
|
12
|
+
// Same structure as PluginRuntimeService.Live but with mock ModuleFederationService
|
|
13
|
+
return Layer.scoped(PluginRuntimeService, Effect.gen(function* () {
|
|
23
14
|
const pluginService = yield* PluginService;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
streamPlugin: pluginService.streamPlugin,
|
|
31
|
-
shutdown: () => pluginService.cleanup(),
|
|
32
|
-
};
|
|
33
|
-
})).pipe(Layer.provide(PluginService.Live(config.registry, secrets).pipe(Layer.provide(Layer.mergeAll(createMockModuleFederationServiceLayer(pluginMap), SecretsService.Live(secrets), Layer.succeed(PluginLoggerTag, logger))))));
|
|
15
|
+
// Register cleanup finalizer for automatic resource management
|
|
16
|
+
yield* Effect.addFinalizer(() => pluginService.cleanup().pipe(Effect.catchAll(() => Effect.void) // Ensure cleanup never fails
|
|
17
|
+
));
|
|
18
|
+
return pluginService;
|
|
19
|
+
})).pipe(Layer.provide(PluginService.Live(config.registry, secrets).pipe(Layer.provide(Layer.mergeAll(createMockModuleFederationServiceLayer(pluginMap), // Mock instead of real
|
|
20
|
+
SecretsService.Live(secrets))))));
|
|
34
21
|
};
|
|
35
22
|
/**
|
|
36
23
|
* Creates a test plugin runtime using mock services.
|
|
37
|
-
* This
|
|
38
|
-
* instead of the real one, making it suitable for unit tests.
|
|
24
|
+
* This mirrors createPluginRuntime exactly but uses test layer.
|
|
39
25
|
*/
|
|
40
|
-
export const createTestPluginRuntime = (config, pluginMap) =>
|
|
26
|
+
export const createTestPluginRuntime = (config, pluginMap) => {
|
|
27
|
+
const layer = createTestLayer(config, pluginMap);
|
|
28
|
+
const runtime = ManagedRuntime.make(layer);
|
|
29
|
+
// Same exact pattern as createPluginRuntime
|
|
30
|
+
const createTypedRuntime = Effect.gen(function* () {
|
|
31
|
+
const pluginService = yield* PluginRuntimeService;
|
|
32
|
+
return new PluginRuntimeImpl(pluginService, config.registry);
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
runtime,
|
|
36
|
+
PluginRuntime: createTypedRuntime.pipe(Effect.provide(runtime))
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Layer } from "effect";
|
|
2
2
|
import { ModuleFederationService } from "../../runtime/services/module-federation.service";
|
|
3
|
-
import type { AnyPlugin } from "../../
|
|
3
|
+
import type { AnyPlugin } from "../../types";
|
|
4
4
|
export interface TestPluginMap {
|
|
5
5
|
[pluginId: string]: new () => AnyPlugin;
|
|
6
6
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-federation.service.d.ts","sourceRoot":"","sources":["../../../src/testing/mocks/module-federation.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AAGvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAC3F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"module-federation.service.d.ts","sourceRoot":"","sources":["../../../src/testing/mocks/module-federation.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AAGvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAC3F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,SAAS,CAAC;CACzC;AA6CD,eAAO,MAAM,sCAAsC,GAAI,WAAW,aAAa,uDACT,CAAC"}
|