every-plugin 2.7.1 → 2.9.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/dist/build/rspack/dev-server-middleware.cjs +4 -2
- package/dist/build/rspack/dev-server-middleware.cjs.map +1 -1
- package/dist/build/rspack/dev-server-middleware.mjs +4 -2
- package/dist/build/rspack/dev-server-middleware.mjs.map +1 -1
- package/dist/plugin.cjs +2 -1
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +2 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/runtime/errors.cjs +3 -42
- package/dist/runtime/errors.cjs.map +1 -1
- package/dist/runtime/errors.d.cts +1 -1
- package/dist/runtime/errors.d.cts.map +1 -1
- package/dist/runtime/errors.d.mts +1 -1
- package/dist/runtime/errors.d.mts.map +1 -1
- package/dist/runtime/errors.mjs +3 -42
- package/dist/runtime/errors.mjs.map +1 -1
- package/dist/runtime/index.cjs +4 -4
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.mjs +4 -4
- package/dist/runtime/index.mjs.map +1 -1
- package/dist/runtime/mf-config.cjs +1 -1
- package/dist/runtime/mf-config.mjs +1 -1
- package/dist/runtime/services/module-federation.service.cjs +6 -5
- package/dist/runtime/services/module-federation.service.cjs.map +1 -1
- package/dist/runtime/services/module-federation.service.mjs +6 -5
- package/dist/runtime/services/module-federation.service.mjs.map +1 -1
- package/dist/runtime/services/plugin-loader.service.cjs +4 -4
- package/dist/runtime/services/plugin-loader.service.cjs.map +1 -1
- package/dist/runtime/services/plugin-loader.service.d.cts.map +1 -1
- package/dist/runtime/services/plugin-loader.service.d.mts.map +1 -1
- package/dist/runtime/services/plugin-loader.service.mjs +4 -4
- package/dist/runtime/services/plugin-loader.service.mjs.map +1 -1
- package/package.json +2 -2
- package/skills/plugin-client/SKILL.md +450 -0
- package/skills/plugin-client/references/api-keys.md +145 -0
- package/src/build/rspack/dev-server-middleware.ts +4 -2
- package/src/plugin.ts +2 -1
- package/src/runtime/errors.ts +4 -60
- package/src/runtime/index.ts +4 -4
- package/src/runtime/services/module-federation.service.ts +7 -5
- package/src/runtime/services/plugin-loader.service.ts +17 -7
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# API Keys Reference
|
|
2
|
+
|
|
3
|
+
## Route Table
|
|
4
|
+
|
|
5
|
+
All routes are exposed via the Better-Auth `apiKeyClient()` plugin (not the oRPC `apiClient`). Call them through `authClient.apiKey.*`:
|
|
6
|
+
|
|
7
|
+
| Method | Input | Output | Notes |
|
|
8
|
+
|--------|-------|--------|-------|
|
|
9
|
+
| `list` | `{ organizationId?, limit?, offset?, sortBy?, sortDirection?, configId? }` | `ApiKey[]` | Returns metadata only — no secret |
|
|
10
|
+
| `create` | `{ name?, prefix?, expiresIn?, permissions?, metadata?, rateLimit?, organizationId?, configId? }` | `ApiKey & { key: string }` | Full secret returned **once** |
|
|
11
|
+
| `update` | `{ id, name?, enabled?, permissions?, metadata?, expiresIn?, rateLimit? }` | `ApiKey` | No secret returned |
|
|
12
|
+
| `delete` | `{ id }` | `{ success: boolean }` | Irreversible |
|
|
13
|
+
|
|
14
|
+
### UI Usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
const authClient = useAuthClient();
|
|
18
|
+
|
|
19
|
+
// List keys for an organization
|
|
20
|
+
const { data } = await authClient.apiKey.list({
|
|
21
|
+
query: { configId: "org-keys", organizationId: orgId },
|
|
22
|
+
});
|
|
23
|
+
const keys = data?.apiKeys ?? [];
|
|
24
|
+
|
|
25
|
+
// Create a key
|
|
26
|
+
const { data, error } = await authClient.apiKey.create({
|
|
27
|
+
configId: "org-keys",
|
|
28
|
+
organizationId: orgId,
|
|
29
|
+
name: "CI deploy key",
|
|
30
|
+
expiresIn: 60 * 60 * 24 * 30, // 30 days
|
|
31
|
+
});
|
|
32
|
+
if (data) {
|
|
33
|
+
saveSecret(data.apiKey.key); // "edk_xxxxx..." — only available here
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Delete a key
|
|
37
|
+
const { error } = await authClient.apiKey.delete({
|
|
38
|
+
keyId: "key_abc123",
|
|
39
|
+
configId: "org-keys",
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## ApiKey Shape
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
type ApiKey = {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string | null;
|
|
49
|
+
prefix: string; // first chars for identification (e.g., "edk_abc")
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
permissions: Record<string, string[]> | null; // resource → actions
|
|
52
|
+
requestCount: number;
|
|
53
|
+
expiresAt: number | null; // epoch seconds
|
|
54
|
+
metadata: Record<string, unknown> | null;
|
|
55
|
+
rateLimit: { limit: number; window: number } | null;
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Permission Schema
|
|
60
|
+
|
|
61
|
+
Permissions are a map of resource name to allowed action arrays:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
{
|
|
65
|
+
things: ["create", "read", "delete"],
|
|
66
|
+
projects: ["read"],
|
|
67
|
+
registry: ["publish", "relay"],
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Server-Side Permission Checks
|
|
72
|
+
|
|
73
|
+
`requireApiKey(requiredPermissions)` validates that the presented key's `permissions` cover all required entries:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const { requireApiKey } = createAuthMiddleware(builder);
|
|
77
|
+
|
|
78
|
+
builder.deleteThing
|
|
79
|
+
.use(requireApiKey({ things: ["delete"] }))
|
|
80
|
+
.handler(async ({ context }) => {
|
|
81
|
+
context.apiKey; // ApiKeyContext — non-null, narrowed by middleware
|
|
82
|
+
context.apiKey.permissions; // Record<string, string[]>
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
If any required action is missing, throws `FORBIDDEN` with:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
{
|
|
90
|
+
message: "API key lacks permission: things:delete",
|
|
91
|
+
data: { requiredPermissions, keyPermissions },
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Multi-Resource Permissions
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
builder.deployApp
|
|
99
|
+
.use(requireApiKey({
|
|
100
|
+
registry: ["publish"],
|
|
101
|
+
projects: ["update"],
|
|
102
|
+
}))
|
|
103
|
+
.handler(...)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
All specified resources must be satisfied — partial matches are rejected.
|
|
107
|
+
|
|
108
|
+
## Key Lifecycle
|
|
109
|
+
|
|
110
|
+
1. **Create** — `authClient.apiKey.create()` returns `{ ...apiKey, key }`. The `key` field is the full secret (`edk_...`). Save it now — it is never returned again.
|
|
111
|
+
2. **Identify** — subsequent `list` / `update` calls return only `prefix` (first few chars) for identification.
|
|
112
|
+
3. **Disable** — `update({ id, enabled: false })` revokes the key without deleting it.
|
|
113
|
+
4. **Delete** — `delete({ id })` permanently removes the key.
|
|
114
|
+
|
|
115
|
+
## Scoping
|
|
116
|
+
|
|
117
|
+
### `configId`
|
|
118
|
+
|
|
119
|
+
Keys can be grouped under a config ID (e.g., `"org-keys"`). This is an opaque label for organizing keys by purpose. Pass the same `configId` on create, list, and delete.
|
|
120
|
+
|
|
121
|
+
### `organizationId`
|
|
122
|
+
|
|
123
|
+
Keys scoped to an organization inherit the org's context. The host's session middleware resolves the active organization from the session, and `requireOrganization` / `requireOrgRole` middlewares check org membership.
|
|
124
|
+
|
|
125
|
+
## Browser vs Server Limitations
|
|
126
|
+
|
|
127
|
+
Permissions, rate limits, and refill configuration are **server-only** — they cannot be set from the browser client. The `create` call from the UI accepts `name`, `expiresIn`, `permissions` (if allowed), and `metadata`. Server-side key management (e.g., via a plugin route) can set the full set of options.
|
|
128
|
+
|
|
129
|
+
## Auth Context
|
|
130
|
+
|
|
131
|
+
When an API key is validated, the host's `getContext()` populates:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
{
|
|
135
|
+
authMethod: "apiKey",
|
|
136
|
+
apiKey: {
|
|
137
|
+
id: "key_abc123",
|
|
138
|
+
name: "CI deploy key",
|
|
139
|
+
permissions: { things: ["create", "read"], ... },
|
|
140
|
+
},
|
|
141
|
+
// user, organization, etc. are null unless the key is tied to a user/org
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The `authMethod` field is one of `"session" | "anonymous" | "apiKey" | "none"`. Plugin routes can branch on this to customize responses for programmatic vs interactive access.
|
|
@@ -74,7 +74,8 @@ export function setupPluginMiddleware(
|
|
|
74
74
|
handlers.rpc = new RPCHandler(loaded.router, {
|
|
75
75
|
interceptors: [
|
|
76
76
|
onError((error: any) => {
|
|
77
|
-
formatORPCError(error);
|
|
77
|
+
const formatted = formatORPCError(error);
|
|
78
|
+
if (formatted) console.error(formatted);
|
|
78
79
|
}),
|
|
79
80
|
],
|
|
80
81
|
});
|
|
@@ -87,7 +88,8 @@ export function setupPluginMiddleware(
|
|
|
87
88
|
],
|
|
88
89
|
interceptors: [
|
|
89
90
|
onError((error: any) => {
|
|
90
|
-
formatORPCError(error);
|
|
91
|
+
const formatted = formatORPCError(error);
|
|
92
|
+
if (formatted) console.error(formatted);
|
|
91
93
|
}),
|
|
92
94
|
],
|
|
93
95
|
});
|
package/src/plugin.ts
CHANGED
|
@@ -173,7 +173,8 @@ export const createPlugin: CreatePluginFn = function createPlugin<
|
|
|
173
173
|
throw unwrapped;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
formatORPCError(error);
|
|
176
|
+
const formatted = formatORPCError(error);
|
|
177
|
+
if (formatted) console.error(formatted);
|
|
177
178
|
throw error;
|
|
178
179
|
}) as any;
|
|
179
180
|
|
package/src/runtime/errors.ts
CHANGED
|
@@ -116,15 +116,14 @@ const formatORPCValidationError = (error: any): string[] | null => {
|
|
|
116
116
|
return lines;
|
|
117
117
|
};
|
|
118
118
|
|
|
119
|
-
export const formatORPCError = (error: any):
|
|
119
|
+
export const formatORPCError = (error: any): string | null => {
|
|
120
120
|
if (!(error instanceof ORPCError)) {
|
|
121
|
-
return;
|
|
121
|
+
return null;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
const validationLines = formatORPCValidationError(error);
|
|
125
125
|
if (validationLines) {
|
|
126
|
-
|
|
127
|
-
return;
|
|
126
|
+
return validationLines.join("\n");
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
const lines: string[] = [];
|
|
@@ -182,49 +181,7 @@ export const formatORPCError = (error: any): void => {
|
|
|
182
181
|
}
|
|
183
182
|
|
|
184
183
|
lines.push(`╰${"─".repeat(50)}\n`);
|
|
185
|
-
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const formatPluginError = (
|
|
189
|
-
pluginId: string | undefined,
|
|
190
|
-
operation: string | undefined,
|
|
191
|
-
message: string,
|
|
192
|
-
): void => {
|
|
193
|
-
const lines: string[] = [];
|
|
194
|
-
|
|
195
|
-
lines.push(`\n╭─ Plugin Error ${"─".repeat(40)}`);
|
|
196
|
-
if (pluginId) lines.push(`│ Plugin: ${pluginId}`);
|
|
197
|
-
if (operation) lines.push(`│ During: ${operation}`);
|
|
198
|
-
lines.push(`│`);
|
|
199
|
-
|
|
200
|
-
if (message.includes("ECONNREFUSED")) {
|
|
201
|
-
lines.push(`│ ❌ Connection refused`);
|
|
202
|
-
lines.push(`│ `);
|
|
203
|
-
lines.push(`│ A required service is not running.`);
|
|
204
|
-
lines.push(`│ → Run: docker compose up -d`);
|
|
205
|
-
} else if (message.includes("ENOTFOUND")) {
|
|
206
|
-
lines.push(`│ ❌ Host not found`);
|
|
207
|
-
lines.push(`│ `);
|
|
208
|
-
lines.push(`│ Check your connection URL or network settings.`);
|
|
209
|
-
} else if (message.includes("ETIMEDOUT") || message.includes("timeout")) {
|
|
210
|
-
lines.push(`│ ❌ Connection timeout`);
|
|
211
|
-
lines.push(`│ `);
|
|
212
|
-
lines.push(`│ The service took too long to respond.`);
|
|
213
|
-
} else if (message.includes("EACCES") || message.includes("permission")) {
|
|
214
|
-
lines.push(`│ ❌ Permission denied`);
|
|
215
|
-
lines.push(`│ `);
|
|
216
|
-
lines.push(`│ Check credentials or access permissions.`);
|
|
217
|
-
} else if (message.includes("401") || message.includes("unauthorized")) {
|
|
218
|
-
lines.push(`│ ❌ Authentication failed`);
|
|
219
|
-
lines.push(`│ `);
|
|
220
|
-
lines.push(`│ Check your API key or credentials.`);
|
|
221
|
-
} else {
|
|
222
|
-
lines.push(`│ ❌ ${message}`);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
lines.push(`╰${"─".repeat(50)}\n`);
|
|
226
|
-
|
|
227
|
-
console.error(lines.join("\n"));
|
|
184
|
+
return lines.join("\n");
|
|
228
185
|
};
|
|
229
186
|
|
|
230
187
|
const isRetryableError = (message: string): boolean => {
|
|
@@ -253,11 +210,6 @@ export const wrapORPCError = (
|
|
|
253
210
|
procedureName?: string,
|
|
254
211
|
operation?: string,
|
|
255
212
|
): PluginRuntimeError => {
|
|
256
|
-
const validationLines = formatORPCValidationError(orpcError);
|
|
257
|
-
if (validationLines) {
|
|
258
|
-
console.error(validationLines.join("\n"));
|
|
259
|
-
}
|
|
260
|
-
|
|
261
213
|
return new PluginRuntimeError({
|
|
262
214
|
pluginId,
|
|
263
215
|
operation,
|
|
@@ -314,14 +266,6 @@ export const toPluginRuntimeError = (
|
|
|
314
266
|
return error;
|
|
315
267
|
}
|
|
316
268
|
|
|
317
|
-
const validationLines = formatORPCValidationError(error);
|
|
318
|
-
if (validationLines) {
|
|
319
|
-
console.error(validationLines.join("\n"));
|
|
320
|
-
} else {
|
|
321
|
-
const message = extractErrorMessage(error);
|
|
322
|
-
formatPluginError(pluginId, operation, message);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
269
|
return new PluginRuntimeError({
|
|
326
270
|
pluginId,
|
|
327
271
|
operation,
|
package/src/runtime/index.ts
CHANGED
|
@@ -84,7 +84,7 @@ export class PluginRuntime<R = RegisteredPlugins> {
|
|
|
84
84
|
const initialized = yield* pluginService.initializePlugin(instance, config, plugins);
|
|
85
85
|
|
|
86
86
|
return initialized;
|
|
87
|
-
}).pipe(Effect.provide(this.runtime));
|
|
87
|
+
}).pipe(Effect.annotateLogs({ plugin: pluginId }), Effect.provide(this.runtime));
|
|
88
88
|
|
|
89
89
|
cachedPlugin = Effect.cached(operation).pipe(Effect.flatten);
|
|
90
90
|
this.pluginCache.set(cacheKey, cachedPlugin);
|
|
@@ -112,7 +112,7 @@ export class PluginRuntime<R = RegisteredPlugins> {
|
|
|
112
112
|
const effect = Effect.gen(function* () {
|
|
113
113
|
const pluginService = yield* PluginService;
|
|
114
114
|
return yield* pluginService.loadPlugin(pluginId);
|
|
115
|
-
});
|
|
115
|
+
}).pipe(Effect.annotateLogs({ plugin: pluginId }));
|
|
116
116
|
return this.runPromise(effect) as Promise<LoadedPlugin<RegisteredPlugin<K, R>>>;
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -123,7 +123,7 @@ export class PluginRuntime<R = RegisteredPlugins> {
|
|
|
123
123
|
const effect = Effect.gen(function* () {
|
|
124
124
|
const pluginService = yield* PluginService;
|
|
125
125
|
return yield* pluginService.instantiatePlugin(pluginId, loadedPlugin);
|
|
126
|
-
});
|
|
126
|
+
}).pipe(Effect.annotateLogs({ plugin: pluginId }));
|
|
127
127
|
return this.runPromise(effect) as Promise<PluginInstance<RegisteredPlugin<K, R>>>;
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -135,7 +135,7 @@ export class PluginRuntime<R = RegisteredPlugins> {
|
|
|
135
135
|
const effect = Effect.gen(function* () {
|
|
136
136
|
const pluginService = yield* PluginService;
|
|
137
137
|
return yield* pluginService.initializePlugin(instance, config, plugins);
|
|
138
|
-
});
|
|
138
|
+
}).pipe(Effect.annotateLogs({ plugin: instance.plugin.id }));
|
|
139
139
|
return this.runPromise(effect);
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -86,7 +86,7 @@ export class ModuleFederationService extends Effect.Service<ModuleFederationServ
|
|
|
86
86
|
return {
|
|
87
87
|
registerRemote: (pluginId: string, url: string) =>
|
|
88
88
|
Effect.gen(function* () {
|
|
89
|
-
|
|
89
|
+
yield* Effect.logDebug(`[MF] Registering ${pluginId}`);
|
|
90
90
|
|
|
91
91
|
const remoteName = getNormalizedRemoteName(pluginId);
|
|
92
92
|
const type = url.endsWith("/mf-manifest.json")
|
|
@@ -112,16 +112,16 @@ export class ModuleFederationService extends Effect.Service<ModuleFederationServ
|
|
|
112
112
|
}),
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
yield* Effect.logInfo(`[MF] ✅ Registered ${pluginId}`);
|
|
116
116
|
}),
|
|
117
117
|
|
|
118
118
|
loadRemoteConstructor: (pluginId: string, url: string) =>
|
|
119
119
|
Effect.gen(function* () {
|
|
120
120
|
const remoteName = getNormalizedRemoteName(pluginId);
|
|
121
|
-
|
|
121
|
+
yield* Effect.logDebug(`[MF] Loading remote ${remoteName}`);
|
|
122
122
|
const modulePath = `${remoteName}/plugin`;
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
const pluginConstructor = yield* Effect.tryPromise({
|
|
125
125
|
try: async () => {
|
|
126
126
|
const container = await mf.loadRemote<RemoteModule>(modulePath);
|
|
127
127
|
if (!container) {
|
|
@@ -181,7 +181,6 @@ export class ModuleFederationService extends Effect.Service<ModuleFederationServ
|
|
|
181
181
|
);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
console.log(`[MF] ✅ Loaded constructor for ${pluginId}`);
|
|
185
184
|
return Constructor;
|
|
186
185
|
},
|
|
187
186
|
catch: (error): ModuleFederationError =>
|
|
@@ -191,6 +190,9 @@ export class ModuleFederationService extends Effect.Service<ModuleFederationServ
|
|
|
191
190
|
cause: error instanceof Error ? error : new Error(String(error)),
|
|
192
191
|
}),
|
|
193
192
|
});
|
|
193
|
+
|
|
194
|
+
yield* Effect.logInfo(`[MF] ✅ Loaded constructor for ${pluginId}`);
|
|
195
|
+
return pluginConstructor;
|
|
194
196
|
}),
|
|
195
197
|
};
|
|
196
198
|
}),
|
|
@@ -111,19 +111,23 @@ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
|
|
|
111
111
|
|
|
112
112
|
const resolvedUrl = resolveUrl(url);
|
|
113
113
|
|
|
114
|
-
yield* moduleFederationService
|
|
115
|
-
.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
),
|
|
120
|
-
)
|
|
114
|
+
yield* moduleFederationService.registerRemote(pluginId, resolvedUrl).pipe(
|
|
115
|
+
Effect.tapError((_error) =>
|
|
116
|
+
Effect.logError(`Plugin ${pluginId} failed during register-remote`),
|
|
117
|
+
),
|
|
118
|
+
Effect.mapError((error) =>
|
|
119
|
+
toPluginRuntimeError(error, pluginId, undefined, "register-remote", true),
|
|
120
|
+
),
|
|
121
|
+
);
|
|
121
122
|
|
|
122
123
|
yield* Effect.logDebug("Loading plugin from remote", { pluginId, url: resolvedUrl });
|
|
123
124
|
|
|
124
125
|
const ctor = yield* moduleFederationService
|
|
125
126
|
.loadRemoteConstructor(pluginId, resolvedUrl)
|
|
126
127
|
.pipe(
|
|
128
|
+
Effect.tapError((_error) =>
|
|
129
|
+
Effect.logError(`Plugin ${pluginId} failed during load-remote`),
|
|
130
|
+
),
|
|
127
131
|
Effect.mapError((error) =>
|
|
128
132
|
toPluginRuntimeError(error, pluginId, undefined, "load-remote", false),
|
|
129
133
|
),
|
|
@@ -138,6 +142,9 @@ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
|
|
|
138
142
|
instantiatePlugin: <T extends AnyPlugin>(pluginId: string, loadedPlugin: LoadedPlugin<T>) =>
|
|
139
143
|
Effect.gen(function* () {
|
|
140
144
|
const instance = yield* Effect.try(() => new loadedPlugin.ctor()).pipe(
|
|
145
|
+
Effect.tapError((_error) =>
|
|
146
|
+
Effect.logError(`Plugin ${pluginId} failed during instantiate-plugin`),
|
|
147
|
+
),
|
|
141
148
|
Effect.mapError((error) =>
|
|
142
149
|
toPluginRuntimeError(error, pluginId, undefined, "instantiate-plugin", false),
|
|
143
150
|
),
|
|
@@ -235,6 +242,9 @@ export class PluginLoaderService extends Effect.Service<PluginLoaderService>()(
|
|
|
235
242
|
.initialize({ variables: _variables, secrets: hydratedConfig.secrets }, plugins ?? {})
|
|
236
243
|
.pipe(
|
|
237
244
|
Effect.provideService(Scope.Scope, scope),
|
|
245
|
+
Effect.tapError((_error) =>
|
|
246
|
+
Effect.logError(`Plugin ${plugin.id} failed during initialize-plugin`),
|
|
247
|
+
),
|
|
238
248
|
Effect.mapError((error) =>
|
|
239
249
|
toPluginRuntimeError(error, plugin.id, undefined, "initialize-plugin", false),
|
|
240
250
|
),
|