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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "every-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"./runtime": {
|
|
12
12
|
"types": "./dist/runtime/index.d.ts",
|
|
13
13
|
"default": "./dist/runtime/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./testing": {
|
|
16
|
+
"types": "./dist/testing/index.d.ts",
|
|
17
|
+
"default": "./dist/testing/index.js"
|
|
14
18
|
}
|
|
15
19
|
},
|
|
16
20
|
"files": [
|
|
@@ -18,29 +22,38 @@
|
|
|
18
22
|
],
|
|
19
23
|
"scripts": {
|
|
20
24
|
"build": "tsc -b",
|
|
21
|
-
"test": "vitest"
|
|
25
|
+
"test": "vitest run --config vitest.unit.config.ts",
|
|
26
|
+
"test:unit": "vitest run --config vitest.unit.config.ts",
|
|
27
|
+
"test:integration": "npm run test:build-plugin && vitest run --config vitest.integration.config.ts",
|
|
28
|
+
"test:all": "npm run test:unit && npm run test:integration",
|
|
29
|
+
"test:watch": "vitest --config vitest.unit.config.ts --watch",
|
|
30
|
+
"test:watch:integration": "vitest --config vitest.integration.config.ts --watch",
|
|
31
|
+
"test:ui": "vitest --ui",
|
|
32
|
+
"test:build-plugin": "cd __tests__/test-plugin && NODE_ENV=test npm run build"
|
|
22
33
|
},
|
|
23
34
|
"peerDependencies": {
|
|
24
|
-
"
|
|
25
|
-
"zod": ">=4.0.0",
|
|
26
|
-
"@module-federation/enhanced": ">=0.18.0",
|
|
27
|
-
"@module-federation/runtime-core": ">=0.18.0",
|
|
28
|
-
"@orpc/server": ">=0.0.1"
|
|
35
|
+
"typescript": "^5.0.0"
|
|
29
36
|
},
|
|
30
37
|
"devDependencies": {
|
|
38
|
+
"@effect/language-service": "^0.38.1",
|
|
31
39
|
"@effect/vitest": "^0.25.1",
|
|
32
|
-
"@
|
|
33
|
-
"@
|
|
40
|
+
"@rspack/cli": "^1.5.2",
|
|
41
|
+
"@rspack/core": "^1.5.2",
|
|
34
42
|
"@types/bun": "^1.2.21",
|
|
43
|
+
"@types/node": "^24.3.1",
|
|
35
44
|
"@vitest/ui": "3.2.4",
|
|
36
45
|
"bun-types": "^1.2.21",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"vitest": "^3.2.4",
|
|
40
|
-
"zod": "^4.0.8"
|
|
46
|
+
"sirv": "^3.0.2",
|
|
47
|
+
"vitest": "^3.2.4"
|
|
41
48
|
},
|
|
42
49
|
"dependencies": {
|
|
43
50
|
"@better-fetch/fetch": "^1.1.18",
|
|
44
|
-
"@
|
|
51
|
+
"@module-federation/enhanced": "^0.18.3",
|
|
52
|
+
"@module-federation/node": "^2.7.15",
|
|
53
|
+
"@module-federation/runtime-core": "^0.18.3",
|
|
54
|
+
"@orpc/contract": "^1.8.6",
|
|
55
|
+
"@orpc/server": "^1.8.6",
|
|
56
|
+
"effect": "^3.17.13",
|
|
57
|
+
"zod": "^4.1.5"
|
|
45
58
|
}
|
|
46
59
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mf.test.d.ts","sourceRoot":"","sources":["../../src/runtime/mf.test.ts"],"names":[],"mappings":""}
|
package/dist/runtime/mf.test.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { Effect, Layer } from "effect";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { PluginLoggerTag } from "../plugin";
|
|
4
|
-
import { ModuleFederationService } from "./services/module-federation.service";
|
|
5
|
-
import { createTestLogger } from "./test-utils";
|
|
6
|
-
|
|
7
|
-
// Test registry for diagnostics
|
|
8
|
-
const TEST_PLUGIN_ID = "test-plugin";
|
|
9
|
-
const TEST_REMOTE_URL = "http://localhost:3000/remoteEntry.js";
|
|
10
|
-
const testLayer = Layer.succeed(PluginLoggerTag, createTestLogger());
|
|
11
|
-
describe("Module Federation Diagnostics", () => {
|
|
12
|
-
it("should verify remote URL is accessible (sanity check)", async () => {
|
|
13
|
-
console.log("=== Testing Remote URL Accessibility ===");
|
|
14
|
-
try {
|
|
15
|
-
const response = await fetch(TEST_REMOTE_URL, { method: "HEAD" });
|
|
16
|
-
console.log(`Remote URL status: ${response.status}`);
|
|
17
|
-
console.log(
|
|
18
|
-
`Remote URL headers:`,
|
|
19
|
-
Object.fromEntries(response.headers.entries()),
|
|
20
|
-
);
|
|
21
|
-
expect(response.ok).toBe(true);
|
|
22
|
-
} catch (error) {
|
|
23
|
-
console.error("Failed to access remote URL:", error);
|
|
24
|
-
throw error;
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
it("should test name transformation logic", async () => {
|
|
28
|
-
console.log("=== Testing Name Transformation ===");
|
|
29
|
-
// Test the same logic used in module-federation.service.ts
|
|
30
|
-
const remoteName = TEST_PLUGIN_ID.toLowerCase()
|
|
31
|
-
.replace("@", "")
|
|
32
|
-
.replace("/", "_");
|
|
33
|
-
const modulePath = `${remoteName}/plugin`;
|
|
34
|
-
console.log(`Original plugin ID: ${TEST_PLUGIN_ID}`);
|
|
35
|
-
console.log(`Transformed remote name: ${remoteName}`);
|
|
36
|
-
console.log(`Module path: ${modulePath}`);
|
|
37
|
-
expect(remoteName).toBe("test-plugin");
|
|
38
|
-
expect(modulePath).toBe("test-plugin/plugin");
|
|
39
|
-
});
|
|
40
|
-
it("should register remote successfully", async () => {
|
|
41
|
-
console.log("=== Testing Remote Registration ===");
|
|
42
|
-
const result = await Effect.runPromise(
|
|
43
|
-
Effect.gen(function* () {
|
|
44
|
-
const mfService = yield* ModuleFederationService;
|
|
45
|
-
console.log("Attempting to register remote...");
|
|
46
|
-
yield* mfService.registerRemote(TEST_PLUGIN_ID, TEST_REMOTE_URL);
|
|
47
|
-
console.log("Remote registration completed successfully");
|
|
48
|
-
return "success";
|
|
49
|
-
}).pipe(
|
|
50
|
-
Effect.provide(ModuleFederationService.Live),
|
|
51
|
-
Effect.provide(testLayer),
|
|
52
|
-
Effect.catchAll((error) => {
|
|
53
|
-
console.error("Remote registration failed:", error);
|
|
54
|
-
return Effect.succeed(`failed: ${error}`);
|
|
55
|
-
}),
|
|
56
|
-
),
|
|
57
|
-
);
|
|
58
|
-
console.log(`Registration result: ${result}`);
|
|
59
|
-
expect(result).toBe("success");
|
|
60
|
-
});
|
|
61
|
-
it.skip("should inspect module federation instance", async () => {
|
|
62
|
-
console.log("=== Inspecting Module Federation Instance ===");
|
|
63
|
-
const result = await Effect.runPromise(
|
|
64
|
-
Effect.gen(function* () {
|
|
65
|
-
const mfService = yield* ModuleFederationService;
|
|
66
|
-
// First register the remote
|
|
67
|
-
yield* mfService.registerRemote(TEST_PLUGIN_ID, TEST_REMOTE_URL);
|
|
68
|
-
// Access the internal MF instance to inspect it
|
|
69
|
-
// Note: This is a bit hacky but needed for diagnostics
|
|
70
|
-
const mfInstance = mfService.mf || globalThis.__FEDERATION__;
|
|
71
|
-
console.log("Module Federation instance:", typeof mfInstance);
|
|
72
|
-
console.log(
|
|
73
|
-
"Available methods:",
|
|
74
|
-
Object.getOwnPropertyNames(mfInstance || {}),
|
|
75
|
-
);
|
|
76
|
-
if (mfInstance && mfInstance.options) {
|
|
77
|
-
console.log("MF options:", mfInstance.options);
|
|
78
|
-
}
|
|
79
|
-
return "inspected";
|
|
80
|
-
}).pipe(
|
|
81
|
-
Effect.provide(ModuleFederationService.Live),
|
|
82
|
-
Effect.provide(testLayer),
|
|
83
|
-
Effect.catchAll((error) => {
|
|
84
|
-
console.error("MF instance inspection failed:", error);
|
|
85
|
-
return Effect.succeed(`failed: ${error}`);
|
|
86
|
-
}),
|
|
87
|
-
),
|
|
88
|
-
);
|
|
89
|
-
expect(result).toBe("inspected");
|
|
90
|
-
});
|
|
91
|
-
it.skip("should attempt to load remote container with detailed logging", async () => {
|
|
92
|
-
console.log("=== Testing Remote Container Loading ===");
|
|
93
|
-
const result = await Effect.runPromise(
|
|
94
|
-
Effect.gen(function* () {
|
|
95
|
-
const mfService = yield* ModuleFederationService;
|
|
96
|
-
// Register remote first
|
|
97
|
-
console.log("Step 1: Registering remote...");
|
|
98
|
-
yield* mfService.registerRemote(TEST_PLUGIN_ID, TEST_REMOTE_URL);
|
|
99
|
-
console.log("Step 1: ✓ Remote registered");
|
|
100
|
-
// Now try to load the constructor
|
|
101
|
-
console.log("Step 2: Loading remote constructor...");
|
|
102
|
-
const ctor = yield* mfService.loadRemoteConstructor(
|
|
103
|
-
TEST_PLUGIN_ID,
|
|
104
|
-
TEST_REMOTE_URL,
|
|
105
|
-
);
|
|
106
|
-
console.log("Step 2: ✓ Constructor loaded:", typeof ctor);
|
|
107
|
-
// Try to instantiate it
|
|
108
|
-
console.log("Step 3: Attempting to instantiate constructor...");
|
|
109
|
-
const instance = new ctor();
|
|
110
|
-
console.log("Step 3: ✓ Instance created:", typeof instance);
|
|
111
|
-
console.log(
|
|
112
|
-
"Instance properties:",
|
|
113
|
-
Object.getOwnPropertyNames(instance),
|
|
114
|
-
);
|
|
115
|
-
console.log("Instance id:", instance.id);
|
|
116
|
-
return "success";
|
|
117
|
-
}).pipe(
|
|
118
|
-
Effect.provide(ModuleFederationService.Live),
|
|
119
|
-
Effect.provide(testLayer),
|
|
120
|
-
Effect.catchAll((error) => {
|
|
121
|
-
console.error("Container loading failed at some step:", error);
|
|
122
|
-
console.error("Error details:", {
|
|
123
|
-
message: error.message,
|
|
124
|
-
cause: error.cause,
|
|
125
|
-
stack: error.stack,
|
|
126
|
-
});
|
|
127
|
-
return Effect.succeed(`failed: ${error.message}`);
|
|
128
|
-
}),
|
|
129
|
-
),
|
|
130
|
-
);
|
|
131
|
-
console.log(`Final result: ${result}`);
|
|
132
|
-
// Don't fail the test - we want to see what happens
|
|
133
|
-
expect(typeof result).toBe("string");
|
|
134
|
-
});
|
|
135
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.test.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
import { Duration, Effect } from "effect";
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
-
import { createPluginRuntime, PluginRuntime } from "./index";
|
|
4
|
-
|
|
5
|
-
// Local template plugin for testing
|
|
6
|
-
const TEST_REGISTRY = {
|
|
7
|
-
"test-plugin": {
|
|
8
|
-
remoteUrl: "http://localhost:3000/remoteEntry.js",
|
|
9
|
-
type: "transformer",
|
|
10
|
-
version: "0.0.1",
|
|
11
|
-
description: "Local template plugin for testing",
|
|
12
|
-
},
|
|
13
|
-
"invalid-plugin": {
|
|
14
|
-
remoteUrl: "https://invalid-plugin-url-that-does-not-exist.com/plugin.js",
|
|
15
|
-
type: "transformer",
|
|
16
|
-
version: "1.0.0",
|
|
17
|
-
description: "Invalid plugin for testing error handling",
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
const TEST_CONFIG = {
|
|
21
|
-
variables: {
|
|
22
|
-
baseUrl: "http://localhost:1337",
|
|
23
|
-
timeout: 5000,
|
|
24
|
-
},
|
|
25
|
-
secrets: {
|
|
26
|
-
apiKey: "{{API_KEY}}", // This will be hydrated from secrets config
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
const TEST_INPUT = {
|
|
30
|
-
query: "test query for processing",
|
|
31
|
-
options: {
|
|
32
|
-
limit: 2,
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
// Secrets configuration for hydration
|
|
36
|
-
const SECRETS_CONFIG = {
|
|
37
|
-
API_KEY: "test-api-key-value",
|
|
38
|
-
};
|
|
39
|
-
describe("Plugin Runtime Integration", () => {
|
|
40
|
-
let runtime;
|
|
41
|
-
beforeEach(() => {
|
|
42
|
-
runtime = createPluginRuntime({
|
|
43
|
-
registry: TEST_REGISTRY,
|
|
44
|
-
secrets: SECRETS_CONFIG,
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
afterEach(async () => {
|
|
48
|
-
if (runtime) {
|
|
49
|
-
await runtime.dispose();
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
it("should create and execute plugin end-to-end", async () => {
|
|
53
|
-
// Execute the test within the runtime
|
|
54
|
-
const result = await runtime.runPromise(
|
|
55
|
-
Effect.gen(function* () {
|
|
56
|
-
const pluginRuntime = yield* PluginRuntime;
|
|
57
|
-
// Create plugin (load → instantiate → initialize)
|
|
58
|
-
const initializedPlugin = yield* pluginRuntime.usePlugin(
|
|
59
|
-
"test-plugin",
|
|
60
|
-
TEST_CONFIG,
|
|
61
|
-
);
|
|
62
|
-
// Verify plugin was initialized
|
|
63
|
-
expect(initializedPlugin).toBeDefined();
|
|
64
|
-
expect(initializedPlugin.metadata.pluginId).toBe("test-plugin");
|
|
65
|
-
expect(initializedPlugin.plugin).toBeDefined();
|
|
66
|
-
// Execute plugin
|
|
67
|
-
const output = yield* pluginRuntime.executePlugin(
|
|
68
|
-
initializedPlugin,
|
|
69
|
-
TEST_INPUT,
|
|
70
|
-
);
|
|
71
|
-
return output;
|
|
72
|
-
}),
|
|
73
|
-
);
|
|
74
|
-
// Validate output structure
|
|
75
|
-
expect(result).toBeDefined();
|
|
76
|
-
expect(typeof result).toBe("object");
|
|
77
|
-
const typedResult = result;
|
|
78
|
-
// Verify success wrapper structure
|
|
79
|
-
expect(typedResult).toHaveProperty("success");
|
|
80
|
-
expect(typedResult).toHaveProperty("data");
|
|
81
|
-
expect(typedResult.success).toBe(true);
|
|
82
|
-
// Verify data content
|
|
83
|
-
expect(typedResult.data).toHaveProperty("results");
|
|
84
|
-
expect(typedResult.data).toHaveProperty("count");
|
|
85
|
-
expect(Array.isArray(typedResult.data.results)).toBe(true);
|
|
86
|
-
expect(typedResult.data.count).toBe(2); // Should match the limit we set
|
|
87
|
-
expect(typedResult.data.results).toHaveLength(2);
|
|
88
|
-
// Verify results contain processed query
|
|
89
|
-
expect(typedResult.data.results[0]).toHaveProperty("id");
|
|
90
|
-
expect(typedResult.data.results[0]).toHaveProperty("content");
|
|
91
|
-
expect(typedResult.data.results[0].content).toContain(
|
|
92
|
-
"test query for processing",
|
|
93
|
-
);
|
|
94
|
-
expect(typedResult.data.results[1].content).toContain(
|
|
95
|
-
"test query for processing",
|
|
96
|
-
);
|
|
97
|
-
});
|
|
98
|
-
it("should handle plugin lifecycle correctly", async () => {
|
|
99
|
-
await runtime.runPromise(
|
|
100
|
-
Effect.gen(function* () {
|
|
101
|
-
const pluginRuntime = yield* PluginRuntime;
|
|
102
|
-
// Test individual lifecycle steps
|
|
103
|
-
// 1. Load plugin
|
|
104
|
-
const pluginConstructor =
|
|
105
|
-
yield* pluginRuntime.loadPlugin("test-plugin");
|
|
106
|
-
expect(pluginConstructor).toBeDefined();
|
|
107
|
-
expect(pluginConstructor.metadata.pluginId).toBe("test-plugin");
|
|
108
|
-
// 2. Instantiate plugin
|
|
109
|
-
const pluginInstance =
|
|
110
|
-
yield* pluginRuntime.instantiatePlugin(pluginConstructor);
|
|
111
|
-
expect(pluginInstance).toBeDefined();
|
|
112
|
-
expect(pluginInstance.plugin).toBeDefined();
|
|
113
|
-
expect(pluginInstance.metadata.pluginId).toBe("test-plugin");
|
|
114
|
-
// 3. Initialize plugin
|
|
115
|
-
const initializedPlugin = yield* pluginRuntime.initializePlugin(
|
|
116
|
-
pluginInstance,
|
|
117
|
-
TEST_CONFIG,
|
|
118
|
-
);
|
|
119
|
-
expect(initializedPlugin).toBeDefined();
|
|
120
|
-
expect(initializedPlugin.config).toBeDefined();
|
|
121
|
-
// 4. Execute plugin
|
|
122
|
-
const output = yield* pluginRuntime.executePlugin(
|
|
123
|
-
initializedPlugin,
|
|
124
|
-
TEST_INPUT,
|
|
125
|
-
);
|
|
126
|
-
expect(output).toBeDefined();
|
|
127
|
-
}),
|
|
128
|
-
);
|
|
129
|
-
});
|
|
130
|
-
it("should handle secret hydration", async () => {
|
|
131
|
-
const configWithSecrets = {
|
|
132
|
-
variables: {
|
|
133
|
-
baseUrl: "http://localhost:1337",
|
|
134
|
-
timeout: 10000,
|
|
135
|
-
},
|
|
136
|
-
secrets: {
|
|
137
|
-
apiKey: "{{API_KEY}}", // This should be hydrated
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
|
-
const secretsConfig = {
|
|
141
|
-
API_KEY: "hydrated-api-key-value",
|
|
142
|
-
};
|
|
143
|
-
const runtime = createPluginRuntime({
|
|
144
|
-
registry: TEST_REGISTRY,
|
|
145
|
-
secrets: secretsConfig,
|
|
146
|
-
});
|
|
147
|
-
await runtime.runPromise(
|
|
148
|
-
Effect.gen(function* () {
|
|
149
|
-
const pluginRuntime = yield* PluginRuntime;
|
|
150
|
-
const initializedPlugin = yield* pluginRuntime.usePlugin(
|
|
151
|
-
"test-plugin",
|
|
152
|
-
configWithSecrets,
|
|
153
|
-
);
|
|
154
|
-
// Verify secrets were hydrated in the config
|
|
155
|
-
expect(initializedPlugin.config).toBeDefined();
|
|
156
|
-
// Verify the config structure and that secrets were hydrated
|
|
157
|
-
const config = initializedPlugin.config;
|
|
158
|
-
expect(config).toHaveProperty("secrets");
|
|
159
|
-
expect(config.secrets).toHaveProperty("apiKey");
|
|
160
|
-
expect(config.secrets.apiKey).toBe("hydrated-api-key-value");
|
|
161
|
-
// Verify variables are also present
|
|
162
|
-
expect(config).toHaveProperty("variables");
|
|
163
|
-
expect(config.variables.baseUrl).toBe("http://localhost:1337");
|
|
164
|
-
expect(config.variables.timeout).toBe(10000);
|
|
165
|
-
// Verify the plugin was successfully initialized with hydrated secrets
|
|
166
|
-
// by executing it to ensure it works with the hydrated config
|
|
167
|
-
const output = yield* pluginRuntime.executePlugin(
|
|
168
|
-
initializedPlugin,
|
|
169
|
-
TEST_INPUT,
|
|
170
|
-
);
|
|
171
|
-
expect(output).toBeDefined();
|
|
172
|
-
}),
|
|
173
|
-
);
|
|
174
|
-
});
|
|
175
|
-
it("should handle validation error for missing apiKey", async () => {
|
|
176
|
-
const configWithoutApiKey = {
|
|
177
|
-
variables: {
|
|
178
|
-
baseUrl: "http://localhost:1337",
|
|
179
|
-
timeout: 5000,
|
|
180
|
-
},
|
|
181
|
-
secrets: {
|
|
182
|
-
// Missing required apiKey
|
|
183
|
-
},
|
|
184
|
-
};
|
|
185
|
-
const result = await runtime.runPromise(
|
|
186
|
-
Effect.gen(function* () {
|
|
187
|
-
const pluginRuntime = yield* PluginRuntime;
|
|
188
|
-
return yield* pluginRuntime
|
|
189
|
-
.usePlugin("test-plugin", configWithoutApiKey)
|
|
190
|
-
.pipe(
|
|
191
|
-
Effect.catchTag("PluginRuntimeError", (error) => {
|
|
192
|
-
// Should catch validation error
|
|
193
|
-
expect(error.operation).toBe("validate-config");
|
|
194
|
-
expect(error.retryable).toBe(false);
|
|
195
|
-
expect(error.pluginId).toBe("test-plugin");
|
|
196
|
-
expect(error.cause).toBeDefined();
|
|
197
|
-
// The error should mention the missing apiKey
|
|
198
|
-
const errorMessage = error.cause?.message || "";
|
|
199
|
-
// Zod validation errors might be in JSON format, so check for both formats
|
|
200
|
-
const lowerMessage = errorMessage.toLowerCase();
|
|
201
|
-
const hasApiKeyError =
|
|
202
|
-
lowerMessage.includes("api key") ||
|
|
203
|
-
lowerMessage.includes("apikey") ||
|
|
204
|
-
lowerMessage.includes("required");
|
|
205
|
-
expect(hasApiKeyError).toBe(true);
|
|
206
|
-
console.debug("Caught expected validation error:", {
|
|
207
|
-
operation: error.operation,
|
|
208
|
-
pluginId: error.pluginId,
|
|
209
|
-
retryable: error.retryable,
|
|
210
|
-
cause: error.cause?.message,
|
|
211
|
-
});
|
|
212
|
-
return Effect.succeed("validation-error-handled-properly");
|
|
213
|
-
}),
|
|
214
|
-
Effect.catchAll((unexpectedError) => {
|
|
215
|
-
console.error("Unexpected error type:", unexpectedError);
|
|
216
|
-
expect.fail(
|
|
217
|
-
`Expected PluginRuntimeError but got: ${unexpectedError._tag || typeof unexpectedError}`,
|
|
218
|
-
);
|
|
219
|
-
return Effect.succeed("should-not-reach-here");
|
|
220
|
-
}),
|
|
221
|
-
);
|
|
222
|
-
}),
|
|
223
|
-
);
|
|
224
|
-
expect(result).toBe("validation-error-handled-properly");
|
|
225
|
-
});
|
|
226
|
-
it("should handle runtime shutdown gracefully", async () => {
|
|
227
|
-
const runtime = createPluginRuntime({
|
|
228
|
-
registry: TEST_REGISTRY,
|
|
229
|
-
secrets: SECRETS_CONFIG,
|
|
230
|
-
});
|
|
231
|
-
await runtime.runPromise(
|
|
232
|
-
Effect.gen(function* () {
|
|
233
|
-
const pluginRuntime = yield* PluginRuntime;
|
|
234
|
-
// Test shutdown
|
|
235
|
-
yield* pluginRuntime.shutdown();
|
|
236
|
-
// Shutdown should complete without errors
|
|
237
|
-
expect(true).toBe(true); // If we reach here, shutdown was successful
|
|
238
|
-
}),
|
|
239
|
-
);
|
|
240
|
-
});
|
|
241
|
-
it("should handle invalid remoteUrl with proper error handling", async () => {
|
|
242
|
-
const result = await runtime.runPromise(
|
|
243
|
-
Effect.gen(function* () {
|
|
244
|
-
const pluginRuntime = yield* PluginRuntime;
|
|
245
|
-
return yield* pluginRuntime.usePlugin("invalid-plugin", {}).pipe(
|
|
246
|
-
Effect.catchTag("PluginRuntimeError", (error) => {
|
|
247
|
-
expect(error.operation).toBe("register-remote");
|
|
248
|
-
expect(error.retryable).toBe(true);
|
|
249
|
-
expect(error.pluginId).toBe("invalid-plugin");
|
|
250
|
-
expect(error.cause).toBeDefined();
|
|
251
|
-
console.debug("Caught expected PluginRuntimeError:", {
|
|
252
|
-
operation: error.operation,
|
|
253
|
-
pluginId: error.pluginId,
|
|
254
|
-
retryable: error.retryable,
|
|
255
|
-
cause: error.cause?.message,
|
|
256
|
-
});
|
|
257
|
-
return Effect.succeed("error-handled-properly");
|
|
258
|
-
}),
|
|
259
|
-
Effect.catchAll((unexpectedError) => {
|
|
260
|
-
// If any other error type is thrown, fail the test
|
|
261
|
-
console.error("Unexpected error type:", unexpectedError);
|
|
262
|
-
expect.fail(
|
|
263
|
-
`Expected PluginRuntimeError but got: ${unexpectedError._tag || typeof unexpectedError}`,
|
|
264
|
-
);
|
|
265
|
-
return Effect.succeed("should-not-reach-here");
|
|
266
|
-
}),
|
|
267
|
-
);
|
|
268
|
-
}),
|
|
269
|
-
);
|
|
270
|
-
expect(result).toBe("error-handled-properly");
|
|
271
|
-
});
|
|
272
|
-
it("should handle plugin not found in registry", async () => {
|
|
273
|
-
// Empty registry to test missing plugin
|
|
274
|
-
const EMPTY_REGISTRY = {};
|
|
275
|
-
const runtime = createPluginRuntime({
|
|
276
|
-
registry: EMPTY_REGISTRY,
|
|
277
|
-
secrets: {},
|
|
278
|
-
});
|
|
279
|
-
const result = await runtime.runPromise(
|
|
280
|
-
Effect.gen(function* () {
|
|
281
|
-
const pluginRuntime = yield* PluginRuntime;
|
|
282
|
-
return yield* pluginRuntime.loadPlugin("non-existent-plugin").pipe(
|
|
283
|
-
Effect.catchTag("PluginRuntimeError", (error) => {
|
|
284
|
-
// Validate error properties
|
|
285
|
-
expect(error.operation).toBe("load-plugin");
|
|
286
|
-
expect(error.retryable).toBe(false);
|
|
287
|
-
expect(error.pluginId).toBe("non-existent-plugin");
|
|
288
|
-
expect(error.cause).toBeDefined();
|
|
289
|
-
expect(error.cause?.message).toContain("not found in registry");
|
|
290
|
-
// Log error details for debugging
|
|
291
|
-
console.debug(
|
|
292
|
-
"Caught expected PluginRuntimeError for missing plugin:",
|
|
293
|
-
{
|
|
294
|
-
operation: error.operation,
|
|
295
|
-
pluginId: error.pluginId,
|
|
296
|
-
retryable: error.retryable,
|
|
297
|
-
cause: error.cause?.message,
|
|
298
|
-
},
|
|
299
|
-
);
|
|
300
|
-
// Return success to make test pass
|
|
301
|
-
return Effect.succeed("plugin-not-found-handled-properly");
|
|
302
|
-
}),
|
|
303
|
-
Effect.catchAll((unexpectedError) => {
|
|
304
|
-
// If any other error type is thrown, fail the test
|
|
305
|
-
console.error("Unexpected error type:", unexpectedError);
|
|
306
|
-
expect.fail(
|
|
307
|
-
`Expected PluginRuntimeError but got: ${unexpectedError._tag || typeof unexpectedError}`,
|
|
308
|
-
);
|
|
309
|
-
return Effect.succeed("should-not-reach-here");
|
|
310
|
-
}),
|
|
311
|
-
);
|
|
312
|
-
}),
|
|
313
|
-
);
|
|
314
|
-
expect(result).toBe("plugin-not-found-handled-properly");
|
|
315
|
-
});
|
|
316
|
-
it("should handle constructor instantiation failure", async () => {
|
|
317
|
-
// Registry with real remote that should fail during instantiation/initialization
|
|
318
|
-
const REAL_REMOTE_REGISTRY = {
|
|
319
|
-
"simple-transform": {
|
|
320
|
-
remoteUrl:
|
|
321
|
-
"https://unpkg.com/@curatedotfun/simple-transform@latest/dist/remoteEntry.js",
|
|
322
|
-
type: "transformer",
|
|
323
|
-
version: "latest",
|
|
324
|
-
description: "Real remote plugin for testing constructor failure",
|
|
325
|
-
},
|
|
326
|
-
};
|
|
327
|
-
const runtime = createPluginRuntime({
|
|
328
|
-
registry: REAL_REMOTE_REGISTRY,
|
|
329
|
-
secrets: {},
|
|
330
|
-
});
|
|
331
|
-
const result = await runtime.runPromise(
|
|
332
|
-
Effect.gen(function* () {
|
|
333
|
-
const pluginRuntime = yield* PluginRuntime;
|
|
334
|
-
return yield* pluginRuntime.usePlugin("simple-transform", {}).pipe(
|
|
335
|
-
Effect.timeout(Duration.seconds(1)), // Timeout after 3 seconds
|
|
336
|
-
Effect.catchTag("PluginRuntimeError", (error) => {
|
|
337
|
-
// Validate error properties
|
|
338
|
-
expect(["load-remote"]).toContain(error.operation);
|
|
339
|
-
expect(error.retryable).toBe(false);
|
|
340
|
-
expect(error.pluginId).toBe("simple-transform");
|
|
341
|
-
expect(error.cause).toBeDefined();
|
|
342
|
-
// Log error details for debugging
|
|
343
|
-
console.debug(
|
|
344
|
-
"Caught expected PluginRuntimeError for constructor failure:",
|
|
345
|
-
{
|
|
346
|
-
operation: error.operation,
|
|
347
|
-
pluginId: error.pluginId,
|
|
348
|
-
retryable: error.retryable,
|
|
349
|
-
cause: error.cause?.message,
|
|
350
|
-
},
|
|
351
|
-
);
|
|
352
|
-
// Return success to make test pass
|
|
353
|
-
return Effect.succeed("constructor-failure-handled-properly");
|
|
354
|
-
}),
|
|
355
|
-
Effect.catchTag("TimeoutException", () => {
|
|
356
|
-
// Handle timeout as expected failure - remote is too slow
|
|
357
|
-
console.debug(
|
|
358
|
-
"Plugin creation timed out - treating as expected failure",
|
|
359
|
-
);
|
|
360
|
-
return Effect.succeed("timeout-handled-properly");
|
|
361
|
-
}),
|
|
362
|
-
Effect.catchAll((unexpectedError) => {
|
|
363
|
-
// If any other error type is thrown, fail the test
|
|
364
|
-
console.error("Unexpected error type:", unexpectedError);
|
|
365
|
-
expect.fail(
|
|
366
|
-
`Expected PluginRuntimeError or TimeoutException but got: ${unexpectedError._tag || typeof unexpectedError}`,
|
|
367
|
-
);
|
|
368
|
-
return Effect.succeed("should-not-reach-here");
|
|
369
|
-
}),
|
|
370
|
-
);
|
|
371
|
-
}),
|
|
372
|
-
);
|
|
373
|
-
expect([
|
|
374
|
-
"constructor-failure-handled-properly",
|
|
375
|
-
"timeout-handled-properly",
|
|
376
|
-
]).toContain(result);
|
|
377
|
-
});
|
|
378
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Effect, Stream } from "effect";
|
|
2
|
-
import type { SourcePlugin } from "../source";
|
|
3
|
-
import { PluginRuntimeError } from "./errors";
|
|
4
|
-
import type { InitializedPlugin } from "./types";
|
|
5
|
-
import z from "zod";
|
|
6
|
-
export interface SourceStreamOptions<TItem = unknown> {
|
|
7
|
-
maxItems?: number;
|
|
8
|
-
maxInvocations?: number;
|
|
9
|
-
stopWhenEmpty?: boolean;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Create a stream from a source plugin
|
|
13
|
-
* Note: Validation is handled at the runtime level before this function is called
|
|
14
|
-
*/
|
|
15
|
-
export declare const createSourceStream: <
|
|
16
|
-
T extends SourcePlugin,
|
|
17
|
-
TInput extends z.infer<T["inputSchema"]>,
|
|
18
|
-
TItem,
|
|
19
|
-
>(
|
|
20
|
-
initializedPlugin: InitializedPlugin<T>,
|
|
21
|
-
executePlugin: (
|
|
22
|
-
plugin: InitializedPlugin<T>,
|
|
23
|
-
input: TInput,
|
|
24
|
-
) => Effect.Effect<any, PluginRuntimeError>,
|
|
25
|
-
input: TInput,
|
|
26
|
-
options: SourceStreamOptions<TItem> | undefined,
|
|
27
|
-
isStreamable: boolean,
|
|
28
|
-
) => Stream.Stream<TItem, PluginRuntimeError>;
|
|
29
|
-
//# sourceMappingURL=streaming.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../../src/runtime/streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,CAAC,MAAM,KAAK,CAAC;AAGpB,MAAM,WAAW,mBAAmB,CAAC,KAAK,GAAG,OAAO;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAMD;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC9B,CAAC,SAAS,YAAY,EACtB,MAAM,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,EACxC,KAAK,EAEL,mBAAmB,iBAAiB,CAAC,CAAC,CAAC,EACvC,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,kBAAkB,CAAC,EACtG,OAAO,MAAM,EACb,SAAS,mBAAmB,CAAC,KAAK,CAAC,YAAK,EACxC,cAAc,OAAO,KACnB,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,kBAAkB,CAqEzC,CAAC"}
|