every-plugin 0.1.0 → 0.1.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.
- package/README.md +1 -329
- package/dist/errors.d.ts +11 -7
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -14
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/plugin.d.ts +149 -103
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +155 -23
- package/dist/runtime/errors.d.ts +41 -53
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/errors.js +48 -8
- package/dist/runtime/index.d.ts +17 -66
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +22 -126
- package/dist/runtime/services/module-federation.service.d.ts +10 -38
- package/dist/runtime/services/module-federation.service.d.ts.map +1 -1
- package/dist/runtime/services/module-federation.service.js +131 -124
- package/dist/runtime/services/plugin.service.d.ts +21 -67
- package/dist/runtime/services/plugin.service.d.ts.map +1 -1
- package/dist/runtime/services/plugin.service.js +265 -328
- package/dist/runtime/services/secrets.service.d.ts +9 -29
- package/dist/runtime/services/secrets.service.js +26 -30
- package/dist/runtime/services.d.ts +1 -1
- package/dist/runtime/streaming/index.d.ts +4 -0
- package/dist/runtime/streaming/index.d.ts.map +1 -0
- package/dist/runtime/streaming/index.js +2 -0
- package/dist/runtime/streaming/state-effects.d.ts +28 -0
- package/dist/runtime/streaming/state-effects.d.ts.map +1 -0
- package/dist/runtime/streaming/state-effects.js +30 -0
- package/dist/runtime/streaming/streaming.d.ts +8 -0
- package/dist/runtime/streaming/streaming.d.ts.map +1 -0
- package/dist/runtime/streaming/streaming.js +113 -0
- package/dist/runtime/streaming/types.d.ts +19 -0
- package/dist/runtime/streaming/types.d.ts.map +1 -0
- package/dist/runtime/streaming/types.js +1 -0
- package/dist/runtime/test-utils.d.ts +1 -1
- package/dist/runtime/test-utils.js +4 -10
- package/dist/runtime/types.d.ts +28 -28
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/validation.d.ts +2 -7
- package/dist/runtime/validation.js +11 -14
- package/dist/testing/index.d.ts +25 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +40 -0
- package/dist/testing/mocks/module-federation.service.d.ts +8 -0
- package/dist/testing/mocks/module-federation.service.d.ts.map +1 -0
- package/dist/testing/mocks/module-federation.service.js +35 -0
- package/package.json +27 -14
- package/dist/runtime/mf.test.d.ts +0 -2
- package/dist/runtime/mf.test.d.ts.map +0 -1
- package/dist/runtime/mf.test.js +0 -135
- package/dist/runtime/runtime.test.d.ts +0 -2
- package/dist/runtime/runtime.test.d.ts.map +0 -1
- package/dist/runtime/runtime.test.js +0 -378
- package/dist/runtime/streaming.d.ts +0 -29
- package/dist/runtime/streaming.d.ts.map +0 -1
- package/dist/runtime/streaming.js +0 -59
- package/dist/runtime/streaming.test.d.ts +0 -2
- package/dist/runtime/streaming.test.d.ts.map +0 -1
- package/dist/runtime/streaming.test.js +0 -271
- package/dist/source.d.ts +0 -70
- package/dist/source.d.ts.map +0 -1
- package/dist/source.js +0 -37
|
@@ -2,36 +2,16 @@ import { Effect, Layer } from "effect";
|
|
|
2
2
|
import { PluginRuntimeError } from "../errors";
|
|
3
3
|
import type { SecretsConfig } from "../types";
|
|
4
4
|
export interface ISecretsService {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
secretsConfig: SecretsConfig,
|
|
8
|
-
) => Effect.Effect<any, PluginRuntimeError>;
|
|
5
|
+
readonly hydrateSecrets: (config: any, // TODO:
|
|
6
|
+
secretsConfig: SecretsConfig) => Effect.Effect<any, PluginRuntimeError>;
|
|
9
7
|
}
|
|
10
|
-
declare const SecretsService_base: import("effect/Context").TagClass<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
ISecretsService
|
|
14
|
-
> &
|
|
15
|
-
Effect.Tag.Proxy<SecretsService, ISecretsService> & {
|
|
16
|
-
use: <X>(
|
|
17
|
-
body: (_: ISecretsService) => X,
|
|
18
|
-
) => [X] extends [Effect.Effect<infer A, infer E, infer R>]
|
|
19
|
-
? Effect.Effect<A, E, R | SecretsService>
|
|
20
|
-
: [X] extends [PromiseLike<infer A_1>]
|
|
21
|
-
? Effect.Effect<
|
|
22
|
-
A_1,
|
|
23
|
-
import("effect/Cause").UnknownException,
|
|
24
|
-
SecretsService
|
|
25
|
-
>
|
|
26
|
-
: Effect.Effect<X, never, SecretsService>;
|
|
27
|
-
};
|
|
8
|
+
declare const SecretsService_base: import("effect/Context").TagClass<SecretsService, "SecretsService", ISecretsService> & Effect.Tag.Proxy<SecretsService, ISecretsService> & {
|
|
9
|
+
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>;
|
|
10
|
+
};
|
|
28
11
|
export declare class SecretsService extends SecretsService_base {
|
|
29
|
-
|
|
30
|
-
secrets: SecretsConfig,
|
|
31
|
-
) => Layer.Layer<SecretsService, never, never>;
|
|
12
|
+
static Live: (secrets: SecretsConfig) => Layer.Layer<SecretsService, never, never>;
|
|
32
13
|
}
|
|
33
14
|
export declare const SecretsServiceTag: typeof SecretsService;
|
|
34
|
-
export declare const SecretsServiceLive: (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//# sourceMappingURL=secrets.service.d.ts.map
|
|
15
|
+
export declare const SecretsServiceLive: (secrets: SecretsConfig) => Layer.Layer<SecretsService, never, never>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=secrets.service.d.ts.map
|
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
2
|
import { PluginRuntimeError } from "../errors";
|
|
3
3
|
export class SecretsService extends Effect.Tag("SecretsService")() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
}),
|
|
33
|
-
});
|
|
4
|
+
static Live = (secrets) => Layer.succeed(SecretsService, {
|
|
5
|
+
hydrateSecrets: (config, secretsConfig) => Effect.gen(function* () {
|
|
6
|
+
if (!config || typeof config !== "object" || !config.secrets) {
|
|
7
|
+
return config;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const configString = JSON.stringify(config);
|
|
11
|
+
let hydratedString = configString;
|
|
12
|
+
// Use provided secrets or fallback to secretsConfig
|
|
13
|
+
const effectiveSecrets = { ...secretsConfig, ...secrets };
|
|
14
|
+
// Simple template replacement for {{SECRET_NAME}} patterns
|
|
15
|
+
for (const [key, value] of Object.entries(effectiveSecrets)) {
|
|
16
|
+
const pattern = new RegExp(`{{${key}}}`, "g");
|
|
17
|
+
hydratedString = hydratedString.replace(pattern, value);
|
|
18
|
+
}
|
|
19
|
+
return JSON.parse(hydratedString);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return yield* Effect.fail(new PluginRuntimeError({
|
|
23
|
+
operation: "hydrate-secrets",
|
|
24
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
25
|
+
retryable: false,
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
34
30
|
}
|
|
35
31
|
export const SecretsServiceTag = SecretsService;
|
|
36
32
|
export const SecretsServiceLive = SecretsService.Live;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/streaming/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State transition helpers for common patterns
|
|
3
|
+
*/
|
|
4
|
+
export declare const StateTransitions: {
|
|
5
|
+
/**
|
|
6
|
+
* Transition to a new phase with optional updates
|
|
7
|
+
*/
|
|
8
|
+
to: <T extends {
|
|
9
|
+
phase?: string;
|
|
10
|
+
}>(phase: string, updates?: Partial<T>) => (state: T) => T;
|
|
11
|
+
/**
|
|
12
|
+
* Add polling delay to state
|
|
13
|
+
*/
|
|
14
|
+
withPolling: <T>(delayMs: number) => (state: T) => T;
|
|
15
|
+
/**
|
|
16
|
+
* Set error state with message and stop polling
|
|
17
|
+
*/
|
|
18
|
+
withError: <T>(errorMessage: string) => (state: T) => T;
|
|
19
|
+
/**
|
|
20
|
+
* Preserve existing state with updates
|
|
21
|
+
*/
|
|
22
|
+
update: <T>(updates: Partial<T>) => (state: T) => T;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Simple pipe utility for composing state transformations
|
|
26
|
+
*/
|
|
27
|
+
export declare const pipe: <T>(value: T, ...fns: Array<(val: T) => T>) => T;
|
|
28
|
+
//# sourceMappingURL=state-effects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-effects.d.ts","sourceRoot":"","sources":["../../../src/runtime/streaming/state-effects.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,gBAAgB;IAC3B;;OAEG;SACE,CAAC,SAAS;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,SAAS,MAAM,YAAW,OAAO,CAAC,CAAC,CAAC,MAClE,OAAO,CAAC,KAAG,CAAC;IAEf;;OAEG;kBACW,CAAC,WAAW,MAAM,MAC7B,OAAO,CAAC,KAAG,CAAC;IAEf;;OAEG;gBACS,CAAC,gBAAgB,MAAM,MAChC,OAAO,CAAC,KAAG,CAAC;IAOf;;OAEG;aACM,CAAC,WAAW,OAAO,CAAC,CAAC,CAAC,MAC5B,OAAO,CAAC,KAAG,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,KAAG,CACxB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State transition helpers for common patterns
|
|
3
|
+
*/
|
|
4
|
+
export const StateTransitions = {
|
|
5
|
+
/**
|
|
6
|
+
* Transition to a new phase with optional updates
|
|
7
|
+
*/
|
|
8
|
+
to: (phase, updates = {}) => (state) => ({ ...state, phase, ...updates }),
|
|
9
|
+
/**
|
|
10
|
+
* Add polling delay to state
|
|
11
|
+
*/
|
|
12
|
+
withPolling: (delayMs) => (state) => ({ ...state, nextPollMs: delayMs }),
|
|
13
|
+
/**
|
|
14
|
+
* Set error state with message and stop polling
|
|
15
|
+
*/
|
|
16
|
+
withError: (errorMessage) => (state) => ({
|
|
17
|
+
...state,
|
|
18
|
+
phase: 'error',
|
|
19
|
+
errorMessage,
|
|
20
|
+
nextPollMs: null
|
|
21
|
+
}),
|
|
22
|
+
/**
|
|
23
|
+
* Preserve existing state with updates
|
|
24
|
+
*/
|
|
25
|
+
update: (updates) => (state) => ({ ...state, ...updates }),
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Simple pipe utility for composing state transformations
|
|
29
|
+
*/
|
|
30
|
+
export const pipe = (value, ...fns) => fns.reduce((acc, fn) => fn(acc), value);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Effect, Stream } from "effect";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import type { Contract, Plugin } from "../../plugin";
|
|
4
|
+
import { type PluginRuntimeError } from "../errors";
|
|
5
|
+
import type { InitializedPlugin } from "../types";
|
|
6
|
+
import type { StreamingOptions } from "./types";
|
|
7
|
+
export declare const createSourceStream: <T extends Plugin<Contract>, TInput extends z.infer<T["inputSchema"]>, TItem, TPluginState extends z.infer<T["stateSchema"]>>(initializedPlugin: InitializedPlugin<T>, executePlugin: (plugin: InitializedPlugin<T>, input: TInput) => Effect.Effect<z.infer<T["outputSchema"]>, PluginRuntimeError>, input: TInput, options?: StreamingOptions<TItem, TPluginState>) => Stream.Stream<TItem, PluginRuntimeError>;
|
|
8
|
+
//# sourceMappingURL=streaming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../../../src/runtime/streaming/streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,KAAK,kBAAkB,EAAwB,MAAM,WAAW,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AAsB7D,eAAO,MAAM,kBAAkB,GAC7B,CAAC,SAAS,MAAM,CAAC,QAAQ,CAAC,EAC1B,MAAM,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,EACxC,KAAK,EACL,YAAY,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,EAE9C,mBAAmB,iBAAiB,CAAC,CAAC,CAAC,EACvC,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,EAAE,kBAAkB,CAAC,EAC7H,OAAO,MAAM,EACb,UAAS,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAM,KAClD,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,kBAAkB,CAuHzC,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Duration, Effect, Stream } from "effect";
|
|
2
|
+
import { toPluginRuntimeError } from "../errors";
|
|
3
|
+
/**
|
|
4
|
+
* Extract delay from plugin state (nextPollMs field)
|
|
5
|
+
* Returns null if nextPollMs is null (signals termination) or undefined (no delay specified)
|
|
6
|
+
*/
|
|
7
|
+
const extractDelayFromState = (state) => {
|
|
8
|
+
const stateWithPolling = state;
|
|
9
|
+
if (stateWithPolling?.nextPollMs === null) {
|
|
10
|
+
return null; // Explicit termination signal
|
|
11
|
+
}
|
|
12
|
+
return stateWithPolling?.nextPollMs ? Duration.millis(stateWithPolling.nextPollMs) : null;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Check if plugin state signals stream termination
|
|
16
|
+
*/
|
|
17
|
+
const isTerminalState = (state) => {
|
|
18
|
+
const stateWithPolling = state;
|
|
19
|
+
return stateWithPolling?.nextPollMs === null;
|
|
20
|
+
};
|
|
21
|
+
export const createSourceStream = (initializedPlugin, executePlugin, input, options = {}) => {
|
|
22
|
+
// Create initial stream state with plugin's initial state
|
|
23
|
+
const initialStreamState = {
|
|
24
|
+
invocations: 0,
|
|
25
|
+
pluginState: input.state,
|
|
26
|
+
lastItemCount: 0,
|
|
27
|
+
};
|
|
28
|
+
// Use Stream.async for full control over emission timing and delays
|
|
29
|
+
return Stream.async((emit) => {
|
|
30
|
+
let currentStreamState = initialStreamState;
|
|
31
|
+
let totalItemsEmitted = 0;
|
|
32
|
+
const loop = async () => {
|
|
33
|
+
try {
|
|
34
|
+
while (true) {
|
|
35
|
+
console.log(`[STREAMING] Starting iteration ${currentStreamState.invocations + 1}`);
|
|
36
|
+
// Check invocation limit BEFORE executing
|
|
37
|
+
if (options.maxInvocations && currentStreamState.invocations >= options.maxInvocations) {
|
|
38
|
+
console.log(`[STREAMING] Reached maxInvocations limit: ${options.maxInvocations}`);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
// Check maxItems limit BEFORE executing
|
|
42
|
+
if (options.maxItems && totalItemsEmitted >= options.maxItems) {
|
|
43
|
+
console.log(`[STREAMING] Reached maxItems limit: ${options.maxItems}`);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
// Execute plugin with current plugin state
|
|
47
|
+
const pluginInput = {
|
|
48
|
+
...input,
|
|
49
|
+
state: currentStreamState.pluginState ?? null
|
|
50
|
+
};
|
|
51
|
+
console.log(`[STREAMING] About to execute plugin with input:`, JSON.stringify(pluginInput, null, 2));
|
|
52
|
+
console.log(`[STREAMING] Plugin state:`, JSON.stringify(currentStreamState.pluginState, null, 2));
|
|
53
|
+
const rawResult = await Effect.runPromise(executePlugin(initializedPlugin, pluginInput).pipe(Effect.catchAll((error) => {
|
|
54
|
+
return toPluginRuntimeError(error);
|
|
55
|
+
})));
|
|
56
|
+
console.log(`[STREAMING] Plugin execution completed`);
|
|
57
|
+
// Transform raw plugin result to streaming format
|
|
58
|
+
const resultObj = rawResult;
|
|
59
|
+
const items = Array.isArray(resultObj.items) ? resultObj.items : [];
|
|
60
|
+
const nextPluginState = resultObj.nextState;
|
|
61
|
+
console.log(`[STREAMING] Transformed result: ${items.length} items`);
|
|
62
|
+
// Update stream state (increment AFTER execution)
|
|
63
|
+
currentStreamState = {
|
|
64
|
+
invocations: currentStreamState.invocations + 1,
|
|
65
|
+
pluginState: nextPluginState,
|
|
66
|
+
lastItemCount: items.length,
|
|
67
|
+
};
|
|
68
|
+
console.log(`[STREAMING] Invocation ${currentStreamState.invocations}, Items: ${items.length}`);
|
|
69
|
+
// Call state change hook if provided
|
|
70
|
+
if (options.onStateChange) {
|
|
71
|
+
await Effect.runPromise(options.onStateChange(nextPluginState, items).pipe(Effect.catchAll((error) => {
|
|
72
|
+
console.warn(`[STREAMING] onStateChange failed:`, error);
|
|
73
|
+
return Effect.void;
|
|
74
|
+
})));
|
|
75
|
+
}
|
|
76
|
+
// Emit items immediately (before any delays)
|
|
77
|
+
for (const item of items) {
|
|
78
|
+
if (options.maxItems && totalItemsEmitted >= options.maxItems) {
|
|
79
|
+
console.log(`[STREAMING] Stopping emission at maxItems limit: ${options.maxItems}`);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
emit.single(item);
|
|
83
|
+
totalItemsEmitted++;
|
|
84
|
+
}
|
|
85
|
+
// Check for termination conditions
|
|
86
|
+
if (nextPluginState && isTerminalState(nextPluginState)) {
|
|
87
|
+
console.log(`[STREAMING] Plugin signaled termination`);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
// Check stopWhenEmpty condition
|
|
91
|
+
if (options.stopWhenEmpty && items.length === 0) {
|
|
92
|
+
console.log(`[STREAMING] Stopping due to empty result and stopWhenEmpty=true`);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
// Handle delay AFTER emission, BEFORE next iteration
|
|
96
|
+
const nextDelay = extractDelayFromState(nextPluginState);
|
|
97
|
+
if (nextDelay && Duration.toMillis(nextDelay) > 0) {
|
|
98
|
+
console.log(`[STREAMING] Sleeping for ${Duration.toMillis(nextDelay)}ms before next iteration`);
|
|
99
|
+
await Effect.runPromise(Effect.sleep(nextDelay));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// End the stream
|
|
103
|
+
emit.end();
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.log(`[STREAMING] Error occurred:`, error);
|
|
107
|
+
emit.fail(error);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
// Start the loop
|
|
111
|
+
loop();
|
|
112
|
+
});
|
|
113
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Effect } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Internal stream state managed by the runtime
|
|
4
|
+
*/
|
|
5
|
+
export interface StreamState<TPluginState> {
|
|
6
|
+
readonly invocations: number;
|
|
7
|
+
readonly pluginState: TPluginState;
|
|
8
|
+
readonly lastItemCount: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Enhanced streaming options with Effect-based hooks
|
|
12
|
+
*/
|
|
13
|
+
export interface StreamingOptions<TItem = unknown, TPluginState = unknown> {
|
|
14
|
+
maxItems?: number;
|
|
15
|
+
maxInvocations?: number;
|
|
16
|
+
stopWhenEmpty?: boolean;
|
|
17
|
+
onStateChange?: (newPluginState: TPluginState, items: TItem[]) => Effect.Effect<void, Error>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/runtime/streaming/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,YAAY;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,YAAY,GAAG,OAAO;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,aAAa,CAAC,EAAE,CAAC,cAAc,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;CAC9F"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
export const createTestLogger = () => ({
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
logError: (message, error, context) =>
|
|
8
|
-
Effect.sync(() =>
|
|
9
|
-
console.error(`[ERROR] ${message}`, error, context || ""),
|
|
10
|
-
),
|
|
11
|
-
logDebug: (message, context) =>
|
|
12
|
-
Effect.sync(() => console.log(`[DEBUG] ${message}`, context || "")),
|
|
3
|
+
logInfo: (message, context) => Effect.sync(() => console.log(`[INFO] ${message}`, context || '')),
|
|
4
|
+
logWarning: (message, context) => Effect.sync(() => console.warn(`[WARNING] ${message}`, context || '')),
|
|
5
|
+
logError: (message, error, context) => Effect.sync(() => console.error(`[ERROR] ${message}`, error, context || '')),
|
|
6
|
+
logDebug: (message, context) => Effect.sync(() => console.log(`[DEBUG] ${message}`, context || '')),
|
|
13
7
|
});
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
2
|
import type { Plugin, PluginLogger, PluginRegistry } from "../plugin";
|
|
3
3
|
export interface PluginRuntimeConfig {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
registry: PluginRegistry;
|
|
5
|
+
secrets?: SecretsConfig;
|
|
6
|
+
logger?: PluginLogger;
|
|
7
7
|
}
|
|
8
8
|
export interface SecretsConfig {
|
|
9
|
-
|
|
9
|
+
[key: string]: string;
|
|
10
10
|
}
|
|
11
|
-
export type AnyPlugin = Plugin<
|
|
11
|
+
export type AnyPlugin = Plugin<any, z.ZodTypeAny, z.ZodTypeAny>;
|
|
12
12
|
export interface PluginConstructor {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
readonly ctor: new () => AnyPlugin;
|
|
14
|
+
readonly metadata: {
|
|
15
|
+
readonly pluginId: string;
|
|
16
|
+
readonly version?: string;
|
|
17
|
+
readonly description?: string;
|
|
18
|
+
readonly type?: string;
|
|
19
|
+
};
|
|
20
20
|
}
|
|
21
21
|
export interface PluginInstance<T extends AnyPlugin = AnyPlugin> {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
readonly plugin: T;
|
|
23
|
+
readonly metadata: {
|
|
24
|
+
readonly pluginId: string;
|
|
25
|
+
readonly version?: string;
|
|
26
|
+
readonly description?: string;
|
|
27
|
+
readonly type?: string;
|
|
28
|
+
};
|
|
29
29
|
}
|
|
30
30
|
export interface InitializedPlugin<T extends AnyPlugin = AnyPlugin> {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
readonly plugin: T;
|
|
32
|
+
readonly metadata: {
|
|
33
|
+
readonly pluginId: string;
|
|
34
|
+
readonly version?: string;
|
|
35
|
+
readonly description?: string;
|
|
36
|
+
readonly type?: string;
|
|
37
|
+
};
|
|
38
|
+
readonly config: z.infer<T["configSchema"]>;
|
|
39
39
|
}
|
|
40
|
-
//# sourceMappingURL=types.d.ts.map
|
|
40
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGtE,MAAM,WAAW,mBAAmB;IACnC,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACtB;AAGD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGtE,MAAM,WAAW,mBAAmB;IACnC,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACtB;AAGD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,EAAE,UAAU,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACF;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS;IAC9D,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACF;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS;IACjE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IAClB,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;CAC5C"}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import type { z } from "zod";
|
|
3
3
|
import { ValidationError } from "./errors";
|
|
4
|
-
export declare const validate: <T>(
|
|
5
|
-
|
|
6
|
-
data: unknown,
|
|
7
|
-
pluginId: string,
|
|
8
|
-
stage: "config" | "input" | "output" | "state",
|
|
9
|
-
) => Effect.Effect<T, ValidationError>;
|
|
10
|
-
//# sourceMappingURL=validation.d.ts.map
|
|
4
|
+
export declare const validate: <T>(schema: z.ZodSchema<T>, data: unknown, pluginId: string, stage: "config" | "input" | "output" | "state") => Effect.Effect<T, ValidationError>;
|
|
5
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { ValidationError } from "./errors";
|
|
3
|
-
export const validate = (schema, data, pluginId, stage) =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}),
|
|
15
|
-
);
|
|
16
|
-
});
|
|
3
|
+
export const validate = (schema, data, pluginId, stage) => Effect.gen(function* () {
|
|
4
|
+
const result = schema.safeParse(data);
|
|
5
|
+
if (result.success) {
|
|
6
|
+
return result.data;
|
|
7
|
+
}
|
|
8
|
+
return yield* Effect.fail(new ValidationError({
|
|
9
|
+
pluginId,
|
|
10
|
+
stage,
|
|
11
|
+
zodError: result.error,
|
|
12
|
+
}));
|
|
13
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Effect, Layer, ManagedRuntime } from "effect";
|
|
2
|
+
import { type PluginRegistry } from "../plugin";
|
|
3
|
+
import { PluginRuntime } from "../runtime";
|
|
4
|
+
import type { PluginRuntimeConfig } from "../runtime/types";
|
|
5
|
+
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
|
+
/**
|
|
13
|
+
* Creates a test layer with mock ModuleFederationService for unit testing.
|
|
14
|
+
* This layer provides all the necessary services for testing plugin runtime
|
|
15
|
+
* without requiring real module federation infrastructure.
|
|
16
|
+
*/
|
|
17
|
+
export declare const createTestLayer: (config: PluginRuntimeConfig, pluginMap: TestPluginMap) => Layer.Layer<PluginRuntime, never, never>;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a test plugin runtime using mock services.
|
|
20
|
+
* This is similar to createPluginRuntime but uses MockModuleFederationService
|
|
21
|
+
* instead of the real one, making it suitable for unit tests.
|
|
22
|
+
*/
|
|
23
|
+
export declare const createTestPluginRuntime: (config: PluginRuntimeConfig, pluginMap: TestPluginMap) => ManagedRuntime.ManagedRuntime<PluginRuntime, never>;
|
|
24
|
+
export type { PluginRegistry, PluginRuntimeConfig, TestPluginMap };
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,EAAmB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAA0C,KAAK,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAG/G,eAAO,MAAM,gBAAgB;uBACR,MAAM,YAAY,OAAO;0BAEtB,MAAM,YAAY,OAAO;wBAE3B,MAAM,UAAU,OAAO,YAAY,OAAO;wBAE1C,MAAM,YAAY,OAAO;CAE7C,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,mBAAmB,EAAE,WAAW,aAAa,6CAgCpF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,mBAAmB,EAAE,WAAW,aAAa,wDACpC,CAAC;AAG1D,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Effect, Layer, ManagedRuntime } from "effect";
|
|
2
|
+
import { PluginLoggerTag } from "../plugin";
|
|
3
|
+
import { PluginRuntime } from "../runtime";
|
|
4
|
+
import { PluginService, SecretsService } from "../runtime/services";
|
|
5
|
+
// Import the mock service factory and types
|
|
6
|
+
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
|
+
/**
|
|
15
|
+
* Creates a test layer with mock ModuleFederationService for unit testing.
|
|
16
|
+
* This layer provides all the necessary services for testing plugin runtime
|
|
17
|
+
* without requiring real module federation infrastructure.
|
|
18
|
+
*/
|
|
19
|
+
export const createTestLayer = (config, pluginMap) => {
|
|
20
|
+
const secrets = config.secrets || {};
|
|
21
|
+
const logger = config.logger || createTestLogger();
|
|
22
|
+
return Layer.effect(PluginRuntime, Effect.gen(function* () {
|
|
23
|
+
const pluginService = yield* PluginService;
|
|
24
|
+
return {
|
|
25
|
+
loadPlugin: pluginService.loadPlugin,
|
|
26
|
+
instantiatePlugin: pluginService.instantiatePlugin,
|
|
27
|
+
initializePlugin: pluginService.initializePlugin,
|
|
28
|
+
executePlugin: pluginService.executePlugin,
|
|
29
|
+
usePlugin: pluginService.usePlugin,
|
|
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))))));
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Creates a test plugin runtime using mock services.
|
|
37
|
+
* This is similar to createPluginRuntime but uses MockModuleFederationService
|
|
38
|
+
* instead of the real one, making it suitable for unit tests.
|
|
39
|
+
*/
|
|
40
|
+
export const createTestPluginRuntime = (config, pluginMap) => ManagedRuntime.make(createTestLayer(config, pluginMap));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { ModuleFederationService } from "../../runtime/services/module-federation.service";
|
|
3
|
+
import type { AnyPlugin } from "../../runtime/types";
|
|
4
|
+
export interface TestPluginMap {
|
|
5
|
+
[pluginId: string]: new () => AnyPlugin;
|
|
6
|
+
}
|
|
7
|
+
export declare const createMockModuleFederationServiceLayer: (pluginMap: TestPluginMap) => Layer.Layer<ModuleFederationService, never, never>;
|
|
8
|
+
//# sourceMappingURL=module-federation.service.d.ts.map
|
|
@@ -0,0 +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,qBAAqB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,SAAS,CAAC;CACzC;AA6CD,eAAO,MAAM,sCAAsC,GAAI,WAAW,aAAa,uDACT,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { ModuleFederationError } from "../../runtime/errors";
|
|
3
|
+
import { ModuleFederationService } from "../../runtime/services/module-federation.service";
|
|
4
|
+
// Create the mock service implementation
|
|
5
|
+
const createMockService = (pluginMap) => ({
|
|
6
|
+
registerRemote: (pluginId, url) => Effect.gen(function* () {
|
|
7
|
+
// Mock registration - succeeds if plugin is in the map
|
|
8
|
+
// Use original pluginId as key since TestPluginMap uses original plugin IDs
|
|
9
|
+
if (pluginId in pluginMap) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
// Fail for plugins not in the map
|
|
13
|
+
return yield* Effect.fail(new ModuleFederationError({
|
|
14
|
+
pluginId,
|
|
15
|
+
remoteUrl: url,
|
|
16
|
+
cause: new Error(`Mock: Plugin ${pluginId} not available in test plugin map`),
|
|
17
|
+
}));
|
|
18
|
+
}),
|
|
19
|
+
loadRemoteConstructor: (pluginId, url) => Effect.gen(function* () {
|
|
20
|
+
// Return the plugin constructor from the map
|
|
21
|
+
// Use original pluginId as key since TestPluginMap uses original plugin IDs
|
|
22
|
+
const PluginConstructor = pluginMap[pluginId];
|
|
23
|
+
if (PluginConstructor) {
|
|
24
|
+
return PluginConstructor;
|
|
25
|
+
}
|
|
26
|
+
// Fail for plugins not in the map
|
|
27
|
+
return yield* Effect.fail(new ModuleFederationError({
|
|
28
|
+
pluginId,
|
|
29
|
+
remoteUrl: url,
|
|
30
|
+
cause: new Error(`Mock: Constructor for ${pluginId} not found in test plugin map`),
|
|
31
|
+
}));
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
// Create a Layer that provides the mock ModuleFederationService
|
|
35
|
+
export const createMockModuleFederationServiceLayer = (pluginMap) => Layer.succeed(ModuleFederationService, createMockService(pluginMap));
|