every-plugin 0.0.5 → 0.1.0
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 +320 -6
- package/dist/errors.d.ts +4 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -10
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/plugin.d.ts +99 -54
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +18 -16
- package/dist/runtime/errors.d.ts +62 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +9 -0
- package/dist/runtime/index.d.ts +73 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +133 -0
- package/dist/runtime/mf.test.d.ts +2 -0
- package/dist/runtime/mf.test.d.ts.map +1 -0
- package/dist/runtime/mf.test.js +135 -0
- package/dist/runtime/runtime.test.d.ts +2 -0
- package/dist/runtime/runtime.test.d.ts.map +1 -0
- package/dist/runtime/runtime.test.js +378 -0
- package/dist/runtime/services/module-federation.service.d.ts +44 -0
- package/dist/runtime/services/module-federation.service.d.ts.map +1 -0
- package/dist/runtime/services/module-federation.service.js +131 -0
- package/dist/runtime/services/plugin.service.d.ts +75 -0
- package/dist/runtime/services/plugin.service.d.ts.map +1 -0
- package/dist/runtime/services/plugin.service.js +338 -0
- package/dist/runtime/services/secrets.service.d.ts +37 -0
- package/dist/runtime/services/secrets.service.d.ts.map +1 -0
- package/dist/runtime/services/secrets.service.js +36 -0
- package/dist/runtime/services.d.ts +4 -0
- package/dist/runtime/services.d.ts.map +1 -0
- package/dist/runtime/services.js +3 -0
- package/dist/runtime/streaming.d.ts +29 -0
- package/dist/runtime/streaming.d.ts.map +1 -0
- package/dist/runtime/streaming.js +59 -0
- package/dist/runtime/streaming.test.d.ts +2 -0
- package/dist/runtime/streaming.test.d.ts.map +1 -0
- package/dist/runtime/streaming.test.js +271 -0
- package/dist/runtime/test-utils.d.ts +3 -0
- package/dist/runtime/test-utils.d.ts.map +1 -0
- package/dist/runtime/test-utils.js +13 -0
- package/dist/runtime/types.d.ts +40 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/validation.d.ts +10 -0
- package/dist/runtime/validation.d.ts.map +1 -0
- package/dist/runtime/validation.js +16 -0
- package/dist/source.d.ts +63 -191
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +33 -70
- package/package.json +44 -22
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { Effect, Stream } from "effect";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { createPluginRuntime, PluginRuntime } from "./index";
|
|
4
|
+
// Test registry with our example source plugin
|
|
5
|
+
const TEST_REGISTRY = {
|
|
6
|
+
"test-source-plugin": {
|
|
7
|
+
remoteUrl: "http://localhost:3001/remoteEntry.js",
|
|
8
|
+
type: "source",
|
|
9
|
+
version: "0.0.1",
|
|
10
|
+
description: "Example source plugin for streaming tests",
|
|
11
|
+
},
|
|
12
|
+
"test-plugin": {
|
|
13
|
+
remoteUrl: "http://localhost:3000/remoteEntry.js",
|
|
14
|
+
type: "transformer",
|
|
15
|
+
version: "0.0.1",
|
|
16
|
+
description: "Local template plugin for testing",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
const TEST_CONFIG = {
|
|
20
|
+
variables: {
|
|
21
|
+
baseUrl: "http://localhost:1337",
|
|
22
|
+
timeout: 5000,
|
|
23
|
+
},
|
|
24
|
+
secrets: {
|
|
25
|
+
apiKey: "test-api-key-value",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
// Contract-based input for search procedure
|
|
29
|
+
const TEST_SEARCH_INPUT = {
|
|
30
|
+
procedure: "search",
|
|
31
|
+
input: {
|
|
32
|
+
query: "test query",
|
|
33
|
+
limit: 10,
|
|
34
|
+
},
|
|
35
|
+
state: null,
|
|
36
|
+
};
|
|
37
|
+
// Contract-based input for getById procedure
|
|
38
|
+
const TEST_GETBYID_INPUT = {
|
|
39
|
+
procedure: "getById",
|
|
40
|
+
input: {
|
|
41
|
+
id: "test-id-123",
|
|
42
|
+
},
|
|
43
|
+
state: null,
|
|
44
|
+
};
|
|
45
|
+
// Contract-based input for getBulk procedure
|
|
46
|
+
const TEST_GETBULK_INPUT = {
|
|
47
|
+
procedure: "getBulk",
|
|
48
|
+
input: {
|
|
49
|
+
ids: ["id1", "id2", "id3"],
|
|
50
|
+
},
|
|
51
|
+
state: null,
|
|
52
|
+
};
|
|
53
|
+
const SECRETS_CONFIG = {
|
|
54
|
+
API_KEY: "test-api-key-value",
|
|
55
|
+
};
|
|
56
|
+
describe("Plugin Streaming", () => {
|
|
57
|
+
let runtime;
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
runtime = createPluginRuntime({
|
|
60
|
+
registry: TEST_REGISTRY,
|
|
61
|
+
secrets: SECRETS_CONFIG,
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
afterEach(async () => {
|
|
65
|
+
if (runtime) {
|
|
66
|
+
await runtime.dispose();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
it("should respect maxItems limit", async () => {
|
|
70
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
71
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
72
|
+
// Start from Phase 3 state that immediately produces items
|
|
73
|
+
const inputWithProductiveState = {
|
|
74
|
+
...TEST_SEARCH_INPUT,
|
|
75
|
+
state: { phase: "historical", status: "processing", nextPollMs: 10 }
|
|
76
|
+
};
|
|
77
|
+
const stream = yield* pluginRuntime.streamPlugin("test-source-plugin", TEST_CONFIG, inputWithProductiveState, {
|
|
78
|
+
maxItems: 3, // Strict limit
|
|
79
|
+
maxInvocations: 3
|
|
80
|
+
});
|
|
81
|
+
const items = yield* stream.pipe(Stream.runCollect);
|
|
82
|
+
return Array.from(items);
|
|
83
|
+
}));
|
|
84
|
+
expect(result.length).toBeLessThanOrEqual(3);
|
|
85
|
+
expect(result.length).toBeGreaterThan(0);
|
|
86
|
+
}, 4000);
|
|
87
|
+
it("should validate invalid state and fail with proper error", async () => {
|
|
88
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
89
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
90
|
+
// Use invalid state that doesn't match plugin's stateSchema
|
|
91
|
+
const inputWithInvalidState = {
|
|
92
|
+
...TEST_SEARCH_INPUT,
|
|
93
|
+
state: {
|
|
94
|
+
pageNumber: 10, // Invalid - plugin expects { phase, status?, jobId?, lastId?, nextPollMs? }
|
|
95
|
+
invalidField: "should not be here",
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
// The error should occur when creating the stream, not when running it
|
|
99
|
+
return yield* pluginRuntime.streamPlugin("test-source-plugin", TEST_CONFIG, inputWithInvalidState, {
|
|
100
|
+
stopWhenEmpty: true,
|
|
101
|
+
}).pipe(Effect.flatMap(() =>
|
|
102
|
+
// If we get here, validation didn't work
|
|
103
|
+
Effect.succeed("validation-should-have-failed")), Effect.catchTag("PluginRuntimeError", (error) => {
|
|
104
|
+
// Should catch state validation error
|
|
105
|
+
expect(error.operation).toBe("validate-state");
|
|
106
|
+
expect(error.retryable).toBe(false);
|
|
107
|
+
expect(error.pluginId).toBe("test-source-plugin");
|
|
108
|
+
expect(error.cause).toBeDefined();
|
|
109
|
+
console.debug("Caught expected state validation error:", {
|
|
110
|
+
operation: error.operation,
|
|
111
|
+
pluginId: error.pluginId,
|
|
112
|
+
retryable: error.retryable,
|
|
113
|
+
cause: error.cause?.message,
|
|
114
|
+
});
|
|
115
|
+
return Effect.succeed("state-validation-error-handled");
|
|
116
|
+
}), Effect.catchAll((unexpectedError) => {
|
|
117
|
+
console.error("Unexpected error type:", unexpectedError);
|
|
118
|
+
expect.fail(`Expected PluginRuntimeError but got: ${unexpectedError._tag || typeof unexpectedError}`);
|
|
119
|
+
return Effect.succeed("should-not-reach-here");
|
|
120
|
+
}));
|
|
121
|
+
}));
|
|
122
|
+
expect(result).toBe("state-validation-error-handled");
|
|
123
|
+
}, 4000);
|
|
124
|
+
it("should handle stopWhenEmpty option", async () => {
|
|
125
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
126
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
127
|
+
// Use the deterministic "empty" phase that always returns 0 items
|
|
128
|
+
const inputWithEmptyState = {
|
|
129
|
+
...TEST_SEARCH_INPUT,
|
|
130
|
+
state: {
|
|
131
|
+
phase: "empty",
|
|
132
|
+
nextPollMs: 10,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
const stream = yield* pluginRuntime.streamPlugin("test-source-plugin", TEST_CONFIG, inputWithEmptyState, {
|
|
136
|
+
stopWhenEmpty: true,
|
|
137
|
+
maxInvocations: 3, // Allow a few iterations to verify it stops
|
|
138
|
+
});
|
|
139
|
+
const items = yield* stream.pipe(Stream.runCollect);
|
|
140
|
+
return Array.from(items);
|
|
141
|
+
}));
|
|
142
|
+
// Should stop immediately when no items are returned from empty phase
|
|
143
|
+
expect(Array.isArray(result)).toBe(true);
|
|
144
|
+
expect(result.length).toBe(0); // Empty phase always returns 0 items
|
|
145
|
+
}, 4000);
|
|
146
|
+
it("should validate plugin type", async () => {
|
|
147
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
148
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
149
|
+
return yield* pluginRuntime.streamPlugin("test-plugin", TEST_CONFIG, TEST_SEARCH_INPUT).pipe(Effect.catchTag("PluginRuntimeError", (error) => {
|
|
150
|
+
expect(error.operation).toBe("stream-plugin-validate");
|
|
151
|
+
expect(error.cause?.message).toContain("not a source plugin");
|
|
152
|
+
return Effect.succeed("validation-error-handled");
|
|
153
|
+
}));
|
|
154
|
+
}));
|
|
155
|
+
expect(result).toBe("validation-error-handled");
|
|
156
|
+
});
|
|
157
|
+
it("should handle workflow phases correctly", async () => {
|
|
158
|
+
const pluginRuntime = await runtime.runPromise(PluginRuntime);
|
|
159
|
+
const plugin = await runtime.runPromise(pluginRuntime.usePlugin("test-source-plugin", TEST_CONFIG));
|
|
160
|
+
// Phase 1: null state -> historical job (empty items)
|
|
161
|
+
const phase1 = await runtime.runPromise(pluginRuntime.executePlugin(plugin, TEST_SEARCH_INPUT));
|
|
162
|
+
const phase1Result = phase1;
|
|
163
|
+
expect(phase1Result.items).toHaveLength(0);
|
|
164
|
+
expect(phase1Result.nextState.phase).toBe("historical");
|
|
165
|
+
expect(phase1Result.nextState.jobId).toMatch(/^hist_\d+$/);
|
|
166
|
+
expect(phase1Result.nextState.nextPollMs).toBe(10);
|
|
167
|
+
// Phase 2: historical job -> processing (empty items)
|
|
168
|
+
const phase2Input = { ...TEST_SEARCH_INPUT, state: phase1Result.nextState };
|
|
169
|
+
const phase2 = await runtime.runPromise(pluginRuntime.executePlugin(plugin, phase2Input));
|
|
170
|
+
const phase2Result = phase2;
|
|
171
|
+
expect(phase2Result.items).toHaveLength(0);
|
|
172
|
+
expect(phase2Result.nextState.phase).toBe("historical");
|
|
173
|
+
expect(phase2Result.nextState.status).toBe("processing");
|
|
174
|
+
expect(phase2Result.nextState.nextPollMs).toBe(10);
|
|
175
|
+
// Phase 3: processing -> historical items + realtime transition
|
|
176
|
+
const phase3Input = { ...TEST_SEARCH_INPUT, state: phase2Result.nextState };
|
|
177
|
+
const phase3 = await runtime.runPromise(pluginRuntime.executePlugin(plugin, phase3Input));
|
|
178
|
+
const phase3Result = phase3;
|
|
179
|
+
expect(phase3Result.items.length).toBeGreaterThan(0);
|
|
180
|
+
expect(phase3Result.items[0]?.content).toContain("Historical");
|
|
181
|
+
expect(phase3Result.nextState.phase).toBe("realtime");
|
|
182
|
+
expect(phase3Result.nextState.lastId).toMatch(/^hist_end_\d+$/);
|
|
183
|
+
expect(phase3Result.nextState.nextPollMs).toBe(10);
|
|
184
|
+
// Phase 4: realtime polling (may have items)
|
|
185
|
+
const phase4Input = { ...TEST_SEARCH_INPUT, state: phase3Result.nextState };
|
|
186
|
+
const phase4 = await runtime.runPromise(pluginRuntime.executePlugin(plugin, phase4Input));
|
|
187
|
+
const phase4Result = phase4;
|
|
188
|
+
expect(phase4Result.nextState.phase).toBe("realtime");
|
|
189
|
+
// Items may be empty or contain real-time items (random)
|
|
190
|
+
if (phase4Result.items.length > 0) {
|
|
191
|
+
expect(phase4Result.items[0]?.content).toContain("Real-time");
|
|
192
|
+
}
|
|
193
|
+
}, 4000);
|
|
194
|
+
describe("Contract-based Procedures", () => {
|
|
195
|
+
it("should execute getById procedure", async () => {
|
|
196
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
197
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
198
|
+
const plugin = yield* pluginRuntime.usePlugin("test-source-plugin", TEST_CONFIG);
|
|
199
|
+
const output = yield* pluginRuntime.executePlugin(plugin, TEST_GETBYID_INPUT);
|
|
200
|
+
return output;
|
|
201
|
+
}));
|
|
202
|
+
expect(result).toBeDefined();
|
|
203
|
+
const typedResult = result;
|
|
204
|
+
expect(typedResult.item).toBeDefined();
|
|
205
|
+
expect(typedResult.item.externalId).toBe("test-id-123");
|
|
206
|
+
expect(typedResult.item.content).toContain("Content for item test-id-123");
|
|
207
|
+
});
|
|
208
|
+
it("should execute getBulk procedure", async () => {
|
|
209
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
210
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
211
|
+
const plugin = yield* pluginRuntime.usePlugin("test-source-plugin", TEST_CONFIG);
|
|
212
|
+
const output = yield* pluginRuntime.executePlugin(plugin, TEST_GETBULK_INPUT);
|
|
213
|
+
return output;
|
|
214
|
+
}));
|
|
215
|
+
expect(result).toBeDefined();
|
|
216
|
+
const typedResult = result;
|
|
217
|
+
expect(typedResult.items).toBeDefined();
|
|
218
|
+
expect(Array.isArray(typedResult.items)).toBe(true);
|
|
219
|
+
expect(typedResult.items.length).toBe(3);
|
|
220
|
+
expect(typedResult.items[0].externalId).toBe("id1");
|
|
221
|
+
expect(typedResult.items[1].externalId).toBe("id2");
|
|
222
|
+
expect(typedResult.items[2].externalId).toBe("id3");
|
|
223
|
+
});
|
|
224
|
+
it("should execute search procedure", async () => {
|
|
225
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
226
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
227
|
+
const plugin = yield* pluginRuntime.usePlugin("test-source-plugin", TEST_CONFIG);
|
|
228
|
+
// Use Phase 3 state to get historical items
|
|
229
|
+
const searchInputWithState = {
|
|
230
|
+
...TEST_SEARCH_INPUT,
|
|
231
|
+
state: { phase: "historical", status: "processing" }
|
|
232
|
+
};
|
|
233
|
+
const output = yield* pluginRuntime.executePlugin(plugin, searchInputWithState);
|
|
234
|
+
return output;
|
|
235
|
+
}));
|
|
236
|
+
expect(result).toBeDefined();
|
|
237
|
+
const typedResult = result;
|
|
238
|
+
expect(typedResult.items).toBeDefined();
|
|
239
|
+
expect(Array.isArray(typedResult.items)).toBe(true);
|
|
240
|
+
expect(typedResult.items.length).toBeGreaterThan(0);
|
|
241
|
+
expect(typedResult.items[0].content).toContain("test query");
|
|
242
|
+
expect(typedResult.nextState).toBeDefined();
|
|
243
|
+
});
|
|
244
|
+
it("should stream getById procedure (single item)", async () => {
|
|
245
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
246
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
247
|
+
const stream = yield* pluginRuntime.streamPlugin("test-source-plugin", TEST_CONFIG, TEST_GETBYID_INPUT, {
|
|
248
|
+
maxItems: 5, // Safety limit
|
|
249
|
+
});
|
|
250
|
+
const items = yield* stream.pipe(Stream.runCollect);
|
|
251
|
+
return Array.from(items);
|
|
252
|
+
}));
|
|
253
|
+
expect(result).toBeDefined();
|
|
254
|
+
expect(result.length).toBe(1);
|
|
255
|
+
expect(result[0].externalId).toBe("test-id-123");
|
|
256
|
+
});
|
|
257
|
+
it("should stream getBulk procedure", async () => {
|
|
258
|
+
const result = await runtime.runPromise(Effect.gen(function* () {
|
|
259
|
+
const pluginRuntime = yield* PluginRuntime;
|
|
260
|
+
const stream = yield* pluginRuntime.streamPlugin("test-source-plugin", TEST_CONFIG, TEST_GETBULK_INPUT, {
|
|
261
|
+
maxItems: 5, // Safety limit
|
|
262
|
+
});
|
|
263
|
+
const items = yield* stream.pipe(Stream.runCollect);
|
|
264
|
+
return Array.from(items);
|
|
265
|
+
}));
|
|
266
|
+
expect(result).toBeDefined();
|
|
267
|
+
expect(result.length).toBe(3);
|
|
268
|
+
expect(result.map(item => item.externalId)).toEqual(["id1", "id2", "id3"]);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../src/runtime/test-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,eAAO,MAAM,gBAAgB,QAAO,YAYlC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export const createTestLogger = () => ({
|
|
3
|
+
logInfo: (message, context) =>
|
|
4
|
+
Effect.sync(() => console.log(`[INFO] ${message}`, context || "")),
|
|
5
|
+
logWarning: (message, context) =>
|
|
6
|
+
Effect.sync(() => console.warn(`[WARNING] ${message}`, context || "")),
|
|
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 || "")),
|
|
13
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { Plugin, PluginLogger, PluginRegistry } from "../plugin";
|
|
3
|
+
export interface PluginRuntimeConfig {
|
|
4
|
+
registry: PluginRegistry;
|
|
5
|
+
secrets?: SecretsConfig;
|
|
6
|
+
logger?: PluginLogger;
|
|
7
|
+
}
|
|
8
|
+
export interface SecretsConfig {
|
|
9
|
+
[key: string]: string;
|
|
10
|
+
}
|
|
11
|
+
export type AnyPlugin = Plugin<z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny>;
|
|
12
|
+
export interface PluginConstructor {
|
|
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
|
+
}
|
|
21
|
+
export interface PluginInstance<T extends AnyPlugin = AnyPlugin> {
|
|
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
|
+
}
|
|
30
|
+
export interface InitializedPlugin<T extends AnyPlugin = AnyPlugin> {
|
|
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
|
+
}
|
|
40
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +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,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;AAEzE,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,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;IACF,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;CAC5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import { ValidationError } from "./errors";
|
|
4
|
+
export declare const validate: <T>(
|
|
5
|
+
schema: z.ZodSchema<T>,
|
|
6
|
+
data: unknown,
|
|
7
|
+
pluginId: string,
|
|
8
|
+
stage: "config" | "input" | "output" | "state",
|
|
9
|
+
) => Effect.Effect<T, ValidationError>;
|
|
10
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/runtime/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,QAAQ,GAAI,CAAC,EACzB,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EACtB,MAAM,OAAO,EACb,UAAU,MAAM,EAChB,OAAO,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,KAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,eAAe,CAahC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { ValidationError } from "./errors";
|
|
3
|
+
export const validate = (schema, data, pluginId, stage) =>
|
|
4
|
+
Effect.gen(function* () {
|
|
5
|
+
const result = schema.safeParse(data);
|
|
6
|
+
if (result.success) {
|
|
7
|
+
return result.data;
|
|
8
|
+
}
|
|
9
|
+
return yield* Effect.fail(
|
|
10
|
+
new ValidationError({
|
|
11
|
+
pluginId,
|
|
12
|
+
stage,
|
|
13
|
+
zodError: result.error,
|
|
14
|
+
}),
|
|
15
|
+
);
|
|
16
|
+
});
|
package/dist/source.d.ts
CHANGED
|
@@ -1,198 +1,70 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { Effect } from "effect";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { ConfigurationError, PluginExecutionError } from "./errors";
|
|
4
|
+
import type { PluginLoggerTag } from "./plugin";
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
6
|
+
* Source-specific schema creators for contract-based plugins
|
|
5
7
|
*/
|
|
6
|
-
export declare
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
export declare function createSourceInputSchema<
|
|
9
|
+
TContract,
|
|
10
|
+
TStateSchema extends z.ZodTypeAny,
|
|
11
|
+
>(
|
|
12
|
+
contract: TContract,
|
|
13
|
+
stateSchema: TStateSchema,
|
|
14
|
+
): z.ZodDiscriminatedUnion<
|
|
15
|
+
[z.ZodObject<any, z.core.$strip>, ...z.ZodObject<any, z.core.$strip>[]],
|
|
16
|
+
"procedure"
|
|
17
|
+
>;
|
|
12
18
|
/**
|
|
13
|
-
*
|
|
19
|
+
* Contract-based output schema creator that infers from oRPC contract
|
|
14
20
|
*/
|
|
15
|
-
export declare
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
export declare function createSourceOutputSchema<
|
|
22
|
+
TContract,
|
|
23
|
+
TStateSchema extends z.ZodTypeAny,
|
|
24
|
+
>(
|
|
25
|
+
contract: TContract,
|
|
26
|
+
stateSchema: TStateSchema,
|
|
27
|
+
): z.ZodUnion<
|
|
28
|
+
[
|
|
29
|
+
z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>,
|
|
30
|
+
...z.ZodType<
|
|
31
|
+
unknown,
|
|
32
|
+
unknown,
|
|
33
|
+
z.core.$ZodTypeInternals<unknown, unknown>
|
|
34
|
+
>[],
|
|
35
|
+
]
|
|
36
|
+
>;
|
|
29
37
|
/**
|
|
30
|
-
*
|
|
31
|
-
* Plugins can extend this with custom types.
|
|
38
|
+
* Contract-first source plugin interface following the Plugin pattern
|
|
32
39
|
*/
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
*/
|
|
63
|
-
export interface SourceItem<TRaw = Record<string, any>> extends PluginSourceItem<TRaw> {
|
|
64
|
-
id: string;
|
|
65
|
-
createdAt: string;
|
|
66
|
-
metadata: {
|
|
67
|
-
sourcePlugin: string;
|
|
68
|
-
workflowId: string;
|
|
69
|
-
runId: string;
|
|
70
|
-
[key: string]: any;
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Defines the progress of a job submitted to an external asynchronous service (e.g., Masa).
|
|
75
|
-
*/
|
|
76
|
-
export interface AsyncJobProgress {
|
|
77
|
-
workflowId: string;
|
|
78
|
-
status: "submitted" | "pending" | "processing" | "done" | "error" | "timeout";
|
|
79
|
-
submittedAt: string;
|
|
80
|
-
lastCheckedAt?: string;
|
|
81
|
-
errorMessage?: string;
|
|
82
|
-
}
|
|
83
|
-
export declare const AsyncJobProgressSchema: z.ZodObject<{
|
|
84
|
-
workflowId: z.ZodString;
|
|
85
|
-
status: z.ZodEnum<{
|
|
86
|
-
error: "error";
|
|
87
|
-
submitted: "submitted";
|
|
88
|
-
pending: "pending";
|
|
89
|
-
processing: "processing";
|
|
90
|
-
done: "done";
|
|
91
|
-
timeout: "timeout";
|
|
92
|
-
}>;
|
|
93
|
-
submittedAt: z.ZodString;
|
|
94
|
-
lastCheckedAt: z.ZodOptional<z.ZodString>;
|
|
95
|
-
errorMessage: z.ZodOptional<z.ZodString>;
|
|
96
|
-
}, z.core.$strip>;
|
|
97
|
-
/**
|
|
98
|
-
* Generic platform-specific state for managing resumable searches and long-running jobs.
|
|
99
|
-
*/
|
|
100
|
-
export interface PlatformState {
|
|
101
|
-
latestProcessedId?: string | number | Record<string, any>;
|
|
102
|
-
currentAsyncJob?: AsyncJobProgress | null;
|
|
103
|
-
[key: string]: any;
|
|
104
|
-
}
|
|
105
|
-
export declare const PlatformStateSchema: z.ZodObject<{
|
|
106
|
-
latestProcessedId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodRecord<z.ZodString, z.ZodAny>]>>;
|
|
107
|
-
currentAsyncJob: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
108
|
-
workflowId: z.ZodString;
|
|
109
|
-
status: z.ZodEnum<{
|
|
110
|
-
error: "error";
|
|
111
|
-
submitted: "submitted";
|
|
112
|
-
pending: "pending";
|
|
113
|
-
processing: "processing";
|
|
114
|
-
done: "done";
|
|
115
|
-
timeout: "timeout";
|
|
116
|
-
}>;
|
|
117
|
-
submittedAt: z.ZodString;
|
|
118
|
-
lastCheckedAt: z.ZodOptional<z.ZodString>;
|
|
119
|
-
errorMessage: z.ZodOptional<z.ZodString>;
|
|
120
|
-
}, z.core.$strip>>>;
|
|
121
|
-
}, z.core.$catchall<z.ZodAny>>;
|
|
122
|
-
/**
|
|
123
|
-
* State passed between search calls to enable resumption.
|
|
124
|
-
* TData is expected to be an object conforming to PlatformState or a derivative.
|
|
125
|
-
*/
|
|
126
|
-
export interface LastProcessedState<TData extends PlatformState = PlatformState> {
|
|
127
|
-
data: TData;
|
|
128
|
-
}
|
|
129
|
-
export type SourceConfig<V extends z.ZodTypeAny = z.ZodRecord<z.ZodString, z.ZodUnknown>, S extends z.ZodTypeAny = z.ZodRecord<z.ZodString, z.ZodUnknown>> = Config<V, S>;
|
|
130
|
-
export interface SourceInput<TSearchOptions extends z.ZodTypeAny = z.ZodTypeAny> extends Input<z.ZodAny> {
|
|
131
|
-
searchOptions: TSearchOptions;
|
|
132
|
-
lastProcessedState?: LastProcessedState<PlatformState> | null;
|
|
133
|
-
}
|
|
134
|
-
export interface PluginSourceInput<TSearchOptions = Record<string, unknown>> extends Input<z.ZodAny> {
|
|
135
|
-
searchOptions: TSearchOptions;
|
|
136
|
-
lastProcessedState?: LastProcessedState<PlatformState> | null;
|
|
137
|
-
}
|
|
138
|
-
export interface PluginSourceOutputData<TItem extends PluginSourceItem = PluginSourceItem> {
|
|
139
|
-
items: TItem[];
|
|
140
|
-
nextLastProcessedState?: PlatformState | null;
|
|
141
|
-
}
|
|
142
|
-
export interface PluginSourceOutput<TItem extends PluginSourceItem = PluginSourceItem> extends Output<z.ZodAny> {
|
|
143
|
-
success: boolean;
|
|
144
|
-
data?: PluginSourceOutputData<TItem>;
|
|
145
|
-
errors?: Array<{
|
|
146
|
-
message: string;
|
|
147
|
-
details?: Record<string, unknown>;
|
|
148
|
-
stack?: string;
|
|
149
|
-
}>;
|
|
150
|
-
}
|
|
151
|
-
export interface SourceOutputData {
|
|
152
|
-
items: SourceItem[];
|
|
153
|
-
nextLastProcessedState?: PlatformState | null;
|
|
154
|
-
}
|
|
155
|
-
export interface SourceOutput extends Output<z.ZodAny> {
|
|
156
|
-
success: boolean;
|
|
157
|
-
data?: SourceOutputData;
|
|
158
|
-
errors?: Array<{
|
|
159
|
-
message: string;
|
|
160
|
-
details?: Record<string, unknown>;
|
|
161
|
-
stack?: string;
|
|
162
|
-
}>;
|
|
163
|
-
}
|
|
164
|
-
export interface SourcePlugin<TInputSchema extends z.ZodTypeAny = z.ZodTypeAny, TOutputSchema extends z.ZodTypeAny = z.ZodTypeAny, TConfigSchema extends z.ZodTypeAny = z.ZodTypeAny> extends Plugin<TInputSchema, TOutputSchema, TConfigSchema> {
|
|
165
|
-
readonly type: 'source';
|
|
40
|
+
export interface SourcePlugin<
|
|
41
|
+
TContract = Record<string, any>,
|
|
42
|
+
TConfigSchema extends z.ZodTypeAny = z.ZodTypeAny,
|
|
43
|
+
TStateSchema extends z.ZodTypeAny = z.ZodTypeAny,
|
|
44
|
+
> {
|
|
45
|
+
readonly id: string;
|
|
46
|
+
readonly type: "source";
|
|
47
|
+
readonly contract: TContract;
|
|
48
|
+
readonly configSchema: TConfigSchema;
|
|
49
|
+
readonly stateSchema: TStateSchema;
|
|
50
|
+
readonly inputSchema: ReturnType<
|
|
51
|
+
typeof createSourceInputSchema<TContract, TStateSchema>
|
|
52
|
+
>;
|
|
53
|
+
readonly outputSchema: ReturnType<
|
|
54
|
+
typeof createSourceOutputSchema<TContract, TStateSchema>
|
|
55
|
+
>;
|
|
56
|
+
initialize(
|
|
57
|
+
config?: z.infer<TConfigSchema>,
|
|
58
|
+
): Effect.Effect<void, ConfigurationError, PluginLoggerTag>;
|
|
59
|
+
execute(
|
|
60
|
+
input: z.infer<this["inputSchema"]>,
|
|
61
|
+
): Effect.Effect<
|
|
62
|
+
z.infer<this["outputSchema"]>,
|
|
63
|
+
PluginExecutionError,
|
|
64
|
+
PluginLoggerTag
|
|
65
|
+
>;
|
|
66
|
+
shutdown(): Effect.Effect<void, never, PluginLoggerTag>;
|
|
67
|
+
createRouter(): any;
|
|
68
|
+
isStreamable(procedureName: string): boolean;
|
|
166
69
|
}
|
|
167
|
-
|
|
168
|
-
searchOptions: TSearchOptions;
|
|
169
|
-
lastProcessedState: z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
170
|
-
data: z.ZodNullable<z.ZodOptional<TState>>;
|
|
171
|
-
}, z.core.$strip>>>;
|
|
172
|
-
}, z.core.$strip>;
|
|
173
|
-
export declare const createSourceOutputSchema: <TItem extends z.ZodTypeAny, TState extends z.ZodTypeAny>(itemData: TItem, stateSchema: TState) => z.ZodObject<{
|
|
174
|
-
success: z.ZodBoolean;
|
|
175
|
-
data: z.ZodOptional<z.ZodObject<{
|
|
176
|
-
items: z.ZodArray<z.ZodObject<{
|
|
177
|
-
externalId: z.ZodString;
|
|
178
|
-
content: z.ZodString;
|
|
179
|
-
contentType: z.ZodOptional<z.ZodString>;
|
|
180
|
-
createdAt: z.ZodOptional<z.ZodString>;
|
|
181
|
-
url: z.ZodOptional<z.ZodString>;
|
|
182
|
-
authors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
183
|
-
id: z.ZodOptional<z.ZodString>;
|
|
184
|
-
username: z.ZodOptional<z.ZodString>;
|
|
185
|
-
displayName: z.ZodOptional<z.ZodString>;
|
|
186
|
-
url: z.ZodOptional<z.ZodString>;
|
|
187
|
-
}, z.core.$strip>>>;
|
|
188
|
-
raw: TItem;
|
|
189
|
-
}, z.core.$strip>>;
|
|
190
|
-
nextLastProcessedState: z.ZodNullable<z.ZodOptional<TState>>;
|
|
191
|
-
}, z.core.$strip>>;
|
|
192
|
-
errors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
193
|
-
message: z.ZodString;
|
|
194
|
-
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
195
|
-
stack: z.ZodOptional<z.ZodString>;
|
|
196
|
-
}, z.core.$strip>>>;
|
|
197
|
-
}, z.core.$strip>;
|
|
198
|
-
//# sourceMappingURL=source.d.ts.map
|
|
70
|
+
//# sourceMappingURL=source.d.ts.map
|
package/dist/source.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD;;GAEG;AACH,wBAAgB,uBAAuB,CACtC,SAAS,EACT,YAAY,SAAS,CAAC,CAAC,UAAU,EAEjC,QAAQ,EAAE,SAAS,EACnB,WAAW,EAAE,YAAY,iHAwBzB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACvC,SAAS,EACT,YAAY,SAAS,CAAC,CAAC,UAAU,EAEjC,QAAQ,EAAE,SAAS,EACnB,WAAW,EAAE,YAAY,uKAWzB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAC5B,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,aAAa,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,EACjD,YAAY,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU;IAEhD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;IAC1F,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;IAE5F,UAAU,CACT,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,GAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,EAAE,eAAe,CAAC,CAAC;IAE5D,OAAO,CACN,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,GACjC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,oBAAoB,EAAE,eAAe,CAAC,CAAC;IAEvF,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC;IAExD,YAAY,IAAI,GAAG,CAAC;IAEpB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C"}
|