@crossdelta/platform-sdk 0.19.0 → 0.19.3
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 +27 -5
- package/bin/chunk-634PL24Z.mjs +20 -0
- package/bin/cli.mjs +604 -0
- package/bin/config-CKQHYOF4.mjs +2 -0
- package/bin/docs/generators/code-style.md +79 -0
- package/bin/docs/generators/natural-language.md +117 -0
- package/bin/docs/generators/service.md +129 -60
- package/bin/templates/hono-microservice/Dockerfile.hbs +3 -1
- package/bin/templates/hono-microservice/src/config/env.ts.hbs +3 -0
- package/bin/templates/nest-microservice/Dockerfile.hbs +6 -2
- package/bin/templates/nest-microservice/src/config/env.ts.hbs +17 -0
- package/bin/templates/nest-microservice/src/main.ts.hbs +2 -1
- package/bin/templates/workspace/.github/actions/prepare-build-context/action.yml +58 -6
- package/bin/templates/workspace/.github/workflows/build-and-deploy.yml.hbs +25 -3
- package/bin/templates/workspace/.github/workflows/publish-packages.yml +6 -8
- package/bin/templates/workspace/biome.json.hbs +4 -1
- package/bin/templates/workspace/infra/package.json.hbs +2 -2
- package/bin/templates/workspace/package.json.hbs +1 -0
- package/bin/templates/workspace/packages/contracts/README.md.hbs +5 -5
- package/bin/templates/workspace/packages/contracts/package.json.hbs +15 -6
- package/bin/templates/workspace/packages/contracts/src/index.ts +1 -1
- package/bin/templates/workspace/packages/contracts/tsconfig.json.hbs +6 -1
- package/bin/templates/workspace/turbo.json +8 -11
- package/bin/templates/workspace/turbo.json.hbs +6 -5
- package/dist/facade.d.mts +840 -0
- package/dist/facade.d.ts +840 -0
- package/dist/facade.js +2294 -0
- package/dist/facade.js.map +1 -0
- package/dist/facade.mjs +2221 -0
- package/dist/facade.mjs.map +1 -0
- package/dist/plugin-types-DQOv97Zh.d.mts +180 -0
- package/dist/plugin-types-DQOv97Zh.d.ts +180 -0
- package/dist/plugin-types.d.mts +1 -0
- package/dist/plugin-types.d.ts +1 -0
- package/dist/plugin-types.js +19 -0
- package/dist/plugin-types.js.map +1 -0
- package/dist/plugin-types.mjs +1 -0
- package/dist/plugin-types.mjs.map +1 -0
- package/dist/plugin.d.mts +31 -0
- package/dist/plugin.d.ts +31 -0
- package/dist/plugin.js +105 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugin.mjs +75 -0
- package/dist/plugin.mjs.map +1 -0
- package/package.json +118 -99
- package/bin/cli.js +0 -540
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin System Types
|
|
3
|
+
*
|
|
4
|
+
* pf owns and defines these interfaces.
|
|
5
|
+
* Plugins MUST adapt to these interfaces.
|
|
6
|
+
* pf is strict and predictable - no duck-typing, no fallbacks.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Base effect type - all domain effects must have a kind
|
|
10
|
+
* pf handles effects generically via kind-based handler registry
|
|
11
|
+
*/
|
|
12
|
+
interface PfEffect {
|
|
13
|
+
readonly kind: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* File write effect - write content to a file
|
|
17
|
+
*/
|
|
18
|
+
interface FileWriteEffect extends PfEffect {
|
|
19
|
+
readonly kind: 'file:write';
|
|
20
|
+
readonly path: string;
|
|
21
|
+
readonly content: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Infrastructure service configuration effect
|
|
25
|
+
*/
|
|
26
|
+
interface InfraAddEffect extends PfEffect {
|
|
27
|
+
readonly kind: 'infra:add';
|
|
28
|
+
readonly serviceName: string;
|
|
29
|
+
readonly port: number;
|
|
30
|
+
readonly framework: string;
|
|
31
|
+
readonly runtime: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Environment variable effect
|
|
35
|
+
*/
|
|
36
|
+
interface EnvAddEffect extends PfEffect {
|
|
37
|
+
readonly kind: 'env:add';
|
|
38
|
+
readonly key: string;
|
|
39
|
+
readonly value: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Field definition for input elicitation
|
|
43
|
+
*
|
|
44
|
+
* Describes a single field that needs user input.
|
|
45
|
+
* Host-agnostic - CLI and MCP interpret this differently.
|
|
46
|
+
*/
|
|
47
|
+
interface ElicitInputField {
|
|
48
|
+
/** Field name (used as key in response) */
|
|
49
|
+
readonly name: string;
|
|
50
|
+
/** Human-readable prompt/question */
|
|
51
|
+
readonly prompt: string;
|
|
52
|
+
/** Whether the field is required */
|
|
53
|
+
readonly required?: boolean;
|
|
54
|
+
/** Detailed description (shown as help text) */
|
|
55
|
+
readonly description?: string;
|
|
56
|
+
/** Default value if user provides nothing */
|
|
57
|
+
readonly defaultValue?: unknown;
|
|
58
|
+
/** Allowed values (enum constraint) */
|
|
59
|
+
readonly enum?: readonly string[];
|
|
60
|
+
/** Field type hint */
|
|
61
|
+
readonly type?: 'string' | 'number' | 'boolean';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Input elicitation effect
|
|
65
|
+
*
|
|
66
|
+
* Returned by tools when required inputs are missing.
|
|
67
|
+
* Pure data - no MCP SDK imports, no host-specific logic.
|
|
68
|
+
*
|
|
69
|
+
* Hosts interpret this effect differently:
|
|
70
|
+
* - CLI: Interactive prompt
|
|
71
|
+
* - MCP: server.elicitInput() or structured error
|
|
72
|
+
*/
|
|
73
|
+
interface ElicitInputEffect extends PfEffect {
|
|
74
|
+
readonly kind: 'elicit:input';
|
|
75
|
+
/** Message explaining what's needed */
|
|
76
|
+
readonly message: string;
|
|
77
|
+
/** Fields to collect from user */
|
|
78
|
+
readonly fields: readonly ElicitInputField[];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Type guard for ElicitInputEffect
|
|
82
|
+
*/
|
|
83
|
+
declare const isElicitInputEffect: (effect: PfEffect) => effect is ElicitInputEffect;
|
|
84
|
+
/**
|
|
85
|
+
* Result of running a command
|
|
86
|
+
*/
|
|
87
|
+
interface PfCommandResult {
|
|
88
|
+
/** Domain effects to be applied by the runtime */
|
|
89
|
+
effects: PfEffect[];
|
|
90
|
+
/** Optional output to display (for read-only commands) */
|
|
91
|
+
output?: string | string[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Command argument definition
|
|
95
|
+
*/
|
|
96
|
+
interface PfCommandArg {
|
|
97
|
+
name: string;
|
|
98
|
+
description: string;
|
|
99
|
+
required: boolean;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Command option definition
|
|
103
|
+
*/
|
|
104
|
+
interface PfCommandOption {
|
|
105
|
+
flags: string;
|
|
106
|
+
description: string;
|
|
107
|
+
default?: unknown;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A CLI command provided by a plugin
|
|
111
|
+
*
|
|
112
|
+
* Plugins MUST implement this exact interface.
|
|
113
|
+
*/
|
|
114
|
+
interface PfCommand {
|
|
115
|
+
name: string;
|
|
116
|
+
description: string;
|
|
117
|
+
args: PfCommandArg[];
|
|
118
|
+
options: PfCommandOption[];
|
|
119
|
+
run(args: Record<string, unknown>, options: Record<string, unknown>): Promise<PfCommandResult>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* An interactive flow provided by a plugin
|
|
123
|
+
*/
|
|
124
|
+
interface PfFlow {
|
|
125
|
+
name: string;
|
|
126
|
+
description: string;
|
|
127
|
+
getSteps(initialContext?: Record<string, unknown>): Promise<unknown[]>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Workspace context with all discovered information
|
|
131
|
+
* pf owns this - plugins receive it during setup
|
|
132
|
+
*/
|
|
133
|
+
interface PfWorkspaceContext {
|
|
134
|
+
/** Absolute path to workspace root */
|
|
135
|
+
workspaceRoot: string;
|
|
136
|
+
/** Discovered services (relative paths like 'services/api-gateway') */
|
|
137
|
+
availableServices: string[];
|
|
138
|
+
/** Contracts package configuration */
|
|
139
|
+
contracts: {
|
|
140
|
+
/** Relative path to contracts source (e.g., 'packages/contracts/src') */
|
|
141
|
+
path: string;
|
|
142
|
+
/** Package name (e.g., '@my-platform/contracts') */
|
|
143
|
+
packageName?: string;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Plugin context provided by pf during setup
|
|
148
|
+
*/
|
|
149
|
+
interface PfPluginContext {
|
|
150
|
+
workspace: PfWorkspaceContext;
|
|
151
|
+
logger: {
|
|
152
|
+
debug: (message: string) => void;
|
|
153
|
+
info: (message: string) => void;
|
|
154
|
+
warn: (message: string) => void;
|
|
155
|
+
error: (message: string) => void;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* A plugin that can be loaded by pf
|
|
160
|
+
*
|
|
161
|
+
* Plugins MUST implement this exact interface.
|
|
162
|
+
* If a plugin has a different internal API, it must provide an adapter.
|
|
163
|
+
*/
|
|
164
|
+
interface PfPlugin {
|
|
165
|
+
name: string;
|
|
166
|
+
version: string;
|
|
167
|
+
description?: string;
|
|
168
|
+
commands: PfCommand[];
|
|
169
|
+
flows: PfFlow[];
|
|
170
|
+
setup: (context: PfPluginContext) => Promise<void> | void;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Result of loading a plugin
|
|
174
|
+
*/
|
|
175
|
+
interface LoadedPlugin {
|
|
176
|
+
plugin: PfPlugin;
|
|
177
|
+
source: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export { type ElicitInputEffect as E, type FileWriteEffect as F, type InfraAddEffect as I, type LoadedPlugin as L, type PfWorkspaceContext as P, type PfPluginContext as a, type PfPlugin as b, type PfCommand as c, type PfCommandArg as d, type PfCommandOption as e, type PfFlow as f, type PfEffect as g, type ElicitInputField as h, type EnvAddEffect as i, type PfCommandResult as j, isElicitInputEffect as k };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin System Types
|
|
3
|
+
*
|
|
4
|
+
* pf owns and defines these interfaces.
|
|
5
|
+
* Plugins MUST adapt to these interfaces.
|
|
6
|
+
* pf is strict and predictable - no duck-typing, no fallbacks.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Base effect type - all domain effects must have a kind
|
|
10
|
+
* pf handles effects generically via kind-based handler registry
|
|
11
|
+
*/
|
|
12
|
+
interface PfEffect {
|
|
13
|
+
readonly kind: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* File write effect - write content to a file
|
|
17
|
+
*/
|
|
18
|
+
interface FileWriteEffect extends PfEffect {
|
|
19
|
+
readonly kind: 'file:write';
|
|
20
|
+
readonly path: string;
|
|
21
|
+
readonly content: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Infrastructure service configuration effect
|
|
25
|
+
*/
|
|
26
|
+
interface InfraAddEffect extends PfEffect {
|
|
27
|
+
readonly kind: 'infra:add';
|
|
28
|
+
readonly serviceName: string;
|
|
29
|
+
readonly port: number;
|
|
30
|
+
readonly framework: string;
|
|
31
|
+
readonly runtime: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Environment variable effect
|
|
35
|
+
*/
|
|
36
|
+
interface EnvAddEffect extends PfEffect {
|
|
37
|
+
readonly kind: 'env:add';
|
|
38
|
+
readonly key: string;
|
|
39
|
+
readonly value: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Field definition for input elicitation
|
|
43
|
+
*
|
|
44
|
+
* Describes a single field that needs user input.
|
|
45
|
+
* Host-agnostic - CLI and MCP interpret this differently.
|
|
46
|
+
*/
|
|
47
|
+
interface ElicitInputField {
|
|
48
|
+
/** Field name (used as key in response) */
|
|
49
|
+
readonly name: string;
|
|
50
|
+
/** Human-readable prompt/question */
|
|
51
|
+
readonly prompt: string;
|
|
52
|
+
/** Whether the field is required */
|
|
53
|
+
readonly required?: boolean;
|
|
54
|
+
/** Detailed description (shown as help text) */
|
|
55
|
+
readonly description?: string;
|
|
56
|
+
/** Default value if user provides nothing */
|
|
57
|
+
readonly defaultValue?: unknown;
|
|
58
|
+
/** Allowed values (enum constraint) */
|
|
59
|
+
readonly enum?: readonly string[];
|
|
60
|
+
/** Field type hint */
|
|
61
|
+
readonly type?: 'string' | 'number' | 'boolean';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Input elicitation effect
|
|
65
|
+
*
|
|
66
|
+
* Returned by tools when required inputs are missing.
|
|
67
|
+
* Pure data - no MCP SDK imports, no host-specific logic.
|
|
68
|
+
*
|
|
69
|
+
* Hosts interpret this effect differently:
|
|
70
|
+
* - CLI: Interactive prompt
|
|
71
|
+
* - MCP: server.elicitInput() or structured error
|
|
72
|
+
*/
|
|
73
|
+
interface ElicitInputEffect extends PfEffect {
|
|
74
|
+
readonly kind: 'elicit:input';
|
|
75
|
+
/** Message explaining what's needed */
|
|
76
|
+
readonly message: string;
|
|
77
|
+
/** Fields to collect from user */
|
|
78
|
+
readonly fields: readonly ElicitInputField[];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Type guard for ElicitInputEffect
|
|
82
|
+
*/
|
|
83
|
+
declare const isElicitInputEffect: (effect: PfEffect) => effect is ElicitInputEffect;
|
|
84
|
+
/**
|
|
85
|
+
* Result of running a command
|
|
86
|
+
*/
|
|
87
|
+
interface PfCommandResult {
|
|
88
|
+
/** Domain effects to be applied by the runtime */
|
|
89
|
+
effects: PfEffect[];
|
|
90
|
+
/** Optional output to display (for read-only commands) */
|
|
91
|
+
output?: string | string[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Command argument definition
|
|
95
|
+
*/
|
|
96
|
+
interface PfCommandArg {
|
|
97
|
+
name: string;
|
|
98
|
+
description: string;
|
|
99
|
+
required: boolean;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Command option definition
|
|
103
|
+
*/
|
|
104
|
+
interface PfCommandOption {
|
|
105
|
+
flags: string;
|
|
106
|
+
description: string;
|
|
107
|
+
default?: unknown;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A CLI command provided by a plugin
|
|
111
|
+
*
|
|
112
|
+
* Plugins MUST implement this exact interface.
|
|
113
|
+
*/
|
|
114
|
+
interface PfCommand {
|
|
115
|
+
name: string;
|
|
116
|
+
description: string;
|
|
117
|
+
args: PfCommandArg[];
|
|
118
|
+
options: PfCommandOption[];
|
|
119
|
+
run(args: Record<string, unknown>, options: Record<string, unknown>): Promise<PfCommandResult>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* An interactive flow provided by a plugin
|
|
123
|
+
*/
|
|
124
|
+
interface PfFlow {
|
|
125
|
+
name: string;
|
|
126
|
+
description: string;
|
|
127
|
+
getSteps(initialContext?: Record<string, unknown>): Promise<unknown[]>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Workspace context with all discovered information
|
|
131
|
+
* pf owns this - plugins receive it during setup
|
|
132
|
+
*/
|
|
133
|
+
interface PfWorkspaceContext {
|
|
134
|
+
/** Absolute path to workspace root */
|
|
135
|
+
workspaceRoot: string;
|
|
136
|
+
/** Discovered services (relative paths like 'services/api-gateway') */
|
|
137
|
+
availableServices: string[];
|
|
138
|
+
/** Contracts package configuration */
|
|
139
|
+
contracts: {
|
|
140
|
+
/** Relative path to contracts source (e.g., 'packages/contracts/src') */
|
|
141
|
+
path: string;
|
|
142
|
+
/** Package name (e.g., '@my-platform/contracts') */
|
|
143
|
+
packageName?: string;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Plugin context provided by pf during setup
|
|
148
|
+
*/
|
|
149
|
+
interface PfPluginContext {
|
|
150
|
+
workspace: PfWorkspaceContext;
|
|
151
|
+
logger: {
|
|
152
|
+
debug: (message: string) => void;
|
|
153
|
+
info: (message: string) => void;
|
|
154
|
+
warn: (message: string) => void;
|
|
155
|
+
error: (message: string) => void;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* A plugin that can be loaded by pf
|
|
160
|
+
*
|
|
161
|
+
* Plugins MUST implement this exact interface.
|
|
162
|
+
* If a plugin has a different internal API, it must provide an adapter.
|
|
163
|
+
*/
|
|
164
|
+
interface PfPlugin {
|
|
165
|
+
name: string;
|
|
166
|
+
version: string;
|
|
167
|
+
description?: string;
|
|
168
|
+
commands: PfCommand[];
|
|
169
|
+
flows: PfFlow[];
|
|
170
|
+
setup: (context: PfPluginContext) => Promise<void> | void;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Result of loading a plugin
|
|
174
|
+
*/
|
|
175
|
+
interface LoadedPlugin {
|
|
176
|
+
plugin: PfPlugin;
|
|
177
|
+
source: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export { type ElicitInputEffect as E, type FileWriteEffect as F, type InfraAddEffect as I, type LoadedPlugin as L, type PfWorkspaceContext as P, type PfPluginContext as a, type PfPlugin as b, type PfCommand as c, type PfCommandArg as d, type PfCommandOption as e, type PfFlow as f, type PfEffect as g, type ElicitInputField as h, type EnvAddEffect as i, type PfCommandResult as j, isElicitInputEffect as k };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { L as LoadedPlugin, c as PfCommand, d as PfCommandArg, e as PfCommandOption, f as PfFlow, b as PfPlugin, a as PfPluginContext } from './plugin-types-DQOv97Zh.mjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { L as LoadedPlugin, c as PfCommand, d as PfCommandArg, e as PfCommandOption, f as PfFlow, b as PfPlugin, a as PfPluginContext } from './plugin-types-DQOv97Zh.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// cli/src/plugin-types.ts
|
|
17
|
+
var plugin_types_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(plugin_types_exports);
|
|
19
|
+
//# sourceMappingURL=plugin-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../cli/src/plugin-types.ts"],"sourcesContent":["/**\n * Plugin System Types - Public API\n *\n * pf owns and defines these interfaces.\n * Plugins MUST import these and implement them.\n *\n * This is the ONLY public export for plugin development.\n */\n\nexport type {\n LoadedPlugin,\n PfCommand,\n PfCommandArg,\n PfCommandOption,\n PfFlow,\n PfPlugin,\n PfPluginContext,\n} from './core/plugins'\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=plugin-types.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { P as PfWorkspaceContext, a as PfPluginContext, L as LoadedPlugin, b as PfPlugin } from './plugin-types-DQOv97Zh.mjs';
|
|
2
|
+
export { c as PfCommand, d as PfCommandArg, e as PfCommandOption, f as PfFlow } from './plugin-types-DQOv97Zh.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Plugin Loader
|
|
6
|
+
*
|
|
7
|
+
* pf owns the plugin interface. Plugins MUST conform exactly.
|
|
8
|
+
* Strict validation - no duck-typing, no fallbacks.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validates that an object conforms to the PfPlugin interface
|
|
13
|
+
*/
|
|
14
|
+
declare const validatePlugin: (obj: unknown) => obj is PfPlugin;
|
|
15
|
+
/**
|
|
16
|
+
* Detailed validation with error messages
|
|
17
|
+
*/
|
|
18
|
+
declare const validatePluginStrict: (obj: unknown) => string[];
|
|
19
|
+
/**
|
|
20
|
+
* Create a plugin context from workspace discovery
|
|
21
|
+
*/
|
|
22
|
+
declare const createPluginContext: (workspace: PfWorkspaceContext, logger?: PfPluginContext["logger"]) => PfPluginContext;
|
|
23
|
+
/**
|
|
24
|
+
* Load and initialize a plugin from a module
|
|
25
|
+
*
|
|
26
|
+
* @param moduleName - The module to import (e.g., '@crossdelta/cloudevents')
|
|
27
|
+
* @param context - Full plugin context with workspace info
|
|
28
|
+
*/
|
|
29
|
+
declare const loadPluginFromModule: (moduleName: string, context: PfPluginContext) => Promise<LoadedPlugin>;
|
|
30
|
+
|
|
31
|
+
export { LoadedPlugin, PfPlugin, PfPluginContext, PfWorkspaceContext, createPluginContext, loadPluginFromModule, validatePlugin, validatePluginStrict };
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { P as PfWorkspaceContext, a as PfPluginContext, L as LoadedPlugin, b as PfPlugin } from './plugin-types-DQOv97Zh.js';
|
|
2
|
+
export { c as PfCommand, d as PfCommandArg, e as PfCommandOption, f as PfFlow } from './plugin-types-DQOv97Zh.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Plugin Loader
|
|
6
|
+
*
|
|
7
|
+
* pf owns the plugin interface. Plugins MUST conform exactly.
|
|
8
|
+
* Strict validation - no duck-typing, no fallbacks.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validates that an object conforms to the PfPlugin interface
|
|
13
|
+
*/
|
|
14
|
+
declare const validatePlugin: (obj: unknown) => obj is PfPlugin;
|
|
15
|
+
/**
|
|
16
|
+
* Detailed validation with error messages
|
|
17
|
+
*/
|
|
18
|
+
declare const validatePluginStrict: (obj: unknown) => string[];
|
|
19
|
+
/**
|
|
20
|
+
* Create a plugin context from workspace discovery
|
|
21
|
+
*/
|
|
22
|
+
declare const createPluginContext: (workspace: PfWorkspaceContext, logger?: PfPluginContext["logger"]) => PfPluginContext;
|
|
23
|
+
/**
|
|
24
|
+
* Load and initialize a plugin from a module
|
|
25
|
+
*
|
|
26
|
+
* @param moduleName - The module to import (e.g., '@crossdelta/cloudevents')
|
|
27
|
+
* @param context - Full plugin context with workspace info
|
|
28
|
+
*/
|
|
29
|
+
declare const loadPluginFromModule: (moduleName: string, context: PfPluginContext) => Promise<LoadedPlugin>;
|
|
30
|
+
|
|
31
|
+
export { LoadedPlugin, PfPlugin, PfPluginContext, PfWorkspaceContext, createPluginContext, loadPluginFromModule, validatePlugin, validatePluginStrict };
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// cli/src/core/plugins/index.ts
|
|
21
|
+
var plugins_exports = {};
|
|
22
|
+
__export(plugins_exports, {
|
|
23
|
+
createPluginContext: () => createPluginContext,
|
|
24
|
+
loadPluginFromModule: () => loadPluginFromModule,
|
|
25
|
+
validatePlugin: () => validatePlugin,
|
|
26
|
+
validatePluginStrict: () => validatePluginStrict
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(plugins_exports);
|
|
29
|
+
|
|
30
|
+
// cli/src/core/plugins/loader.ts
|
|
31
|
+
var defaultLogger = {
|
|
32
|
+
debug: () => {
|
|
33
|
+
},
|
|
34
|
+
info: console.log,
|
|
35
|
+
warn: console.warn,
|
|
36
|
+
error: console.error
|
|
37
|
+
};
|
|
38
|
+
var isObject = (val) => val !== null && typeof val === "object";
|
|
39
|
+
var isNonEmptyString = (val) => typeof val === "string" && val.length > 0;
|
|
40
|
+
var isString = (val) => typeof val === "string";
|
|
41
|
+
var isFunction = (val) => typeof val === "function";
|
|
42
|
+
var validateCommand = (cmd, index) => {
|
|
43
|
+
if (!isObject(cmd)) return [`commands[${index}]: must be an object`];
|
|
44
|
+
return [
|
|
45
|
+
!isNonEmptyString(cmd.name) && `commands[${index}]: missing or invalid 'name'`,
|
|
46
|
+
!isString(cmd.description) && `commands[${index}]: missing or invalid 'description'`,
|
|
47
|
+
!Array.isArray(cmd.args) && `commands[${index}]: missing 'args' array`,
|
|
48
|
+
!Array.isArray(cmd.options) && `commands[${index}]: missing 'options' array`,
|
|
49
|
+
!isFunction(cmd.run) && `commands[${index}]: missing 'run' function`
|
|
50
|
+
].filter((e) => e !== false);
|
|
51
|
+
};
|
|
52
|
+
var validatePlugin = (obj) => {
|
|
53
|
+
if (!isObject(obj)) return false;
|
|
54
|
+
return isNonEmptyString(obj.name) && isString(obj.version) && Array.isArray(obj.commands) && Array.isArray(obj.flows) && isFunction(obj.setup);
|
|
55
|
+
};
|
|
56
|
+
var validatePluginStrict = (obj) => {
|
|
57
|
+
if (!isObject(obj)) return ["Plugin must be an object"];
|
|
58
|
+
const baseErrors = [
|
|
59
|
+
!isNonEmptyString(obj.name) && "Missing or invalid 'name'",
|
|
60
|
+
!isString(obj.version) && "Missing or invalid 'version'",
|
|
61
|
+
!Array.isArray(obj.commands) && "Missing 'commands' array",
|
|
62
|
+
!Array.isArray(obj.flows) && "Missing 'flows' array",
|
|
63
|
+
!isFunction(obj.setup) && "Missing 'setup' function"
|
|
64
|
+
].filter((e) => e !== false);
|
|
65
|
+
const commandErrors = Array.isArray(obj.commands) ? obj.commands.flatMap((cmd, i) => validateCommand(cmd, i)) : [];
|
|
66
|
+
return [...baseErrors, ...commandErrors];
|
|
67
|
+
};
|
|
68
|
+
var createPluginContext = (workspace, logger = defaultLogger) => ({
|
|
69
|
+
workspace,
|
|
70
|
+
logger
|
|
71
|
+
});
|
|
72
|
+
var loadPluginFromModule = async (moduleName, context) => {
|
|
73
|
+
const { logger } = context;
|
|
74
|
+
logger.debug(`Loading plugin from module: ${moduleName}`);
|
|
75
|
+
const mod = await import(moduleName);
|
|
76
|
+
const createPlugin = mod.createPfPlugin || mod.default?.createPfPlugin;
|
|
77
|
+
if (!isFunction(createPlugin)) {
|
|
78
|
+
throw new Error(`Module ${moduleName} does not export createPfPlugin`);
|
|
79
|
+
}
|
|
80
|
+
const plugin = createPlugin({
|
|
81
|
+
contractsPath: context.workspace.contracts.path,
|
|
82
|
+
contractsPackage: context.workspace.contracts.packageName,
|
|
83
|
+
availableServices: context.workspace.availableServices
|
|
84
|
+
});
|
|
85
|
+
const errors = validatePluginStrict(plugin);
|
|
86
|
+
if (errors.length > 0) {
|
|
87
|
+
throw new Error(`Plugin from ${moduleName} does not conform to PfPlugin interface:
|
|
88
|
+
- ${errors.join("\n - ")}`);
|
|
89
|
+
}
|
|
90
|
+
const validatedPlugin = plugin;
|
|
91
|
+
logger.debug(`Loaded plugin: ${validatedPlugin.name} v${validatedPlugin.version}`);
|
|
92
|
+
await validatedPlugin.setup(context);
|
|
93
|
+
return {
|
|
94
|
+
plugin: validatedPlugin,
|
|
95
|
+
source: moduleName
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
99
|
+
0 && (module.exports = {
|
|
100
|
+
createPluginContext,
|
|
101
|
+
loadPluginFromModule,
|
|
102
|
+
validatePlugin,
|
|
103
|
+
validatePluginStrict
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../cli/src/core/plugins/index.ts","../cli/src/core/plugins/loader.ts"],"sourcesContent":["/**\n * Plugin System\n *\n * pf owns and defines the plugin interfaces.\n * Plugins MUST adapt to these interfaces.\n */\n\nexport {\n createPluginContext,\n loadPluginFromModule,\n validatePlugin,\n validatePluginStrict,\n} from './loader'\nexport type {\n LoadedPlugin,\n PfCommand,\n PfCommandArg,\n PfCommandOption,\n PfFlow,\n PfPlugin,\n PfPluginContext,\n PfWorkspaceContext,\n} from './types'\n","/**\n * Plugin Loader\n *\n * pf owns the plugin interface. Plugins MUST conform exactly.\n * Strict validation - no duck-typing, no fallbacks.\n */\nimport type { LoadedPlugin, PfPlugin, PfPluginContext, PfWorkspaceContext } from './types'\n\nconst defaultLogger: PfPluginContext['logger'] = {\n debug: () => {},\n info: console.log,\n warn: console.warn,\n error: console.error,\n}\n\nconst isObject = (val: unknown): val is Record<string, unknown> =>\n val !== null && typeof val === 'object'\n\nconst isNonEmptyString = (val: unknown): val is string =>\n typeof val === 'string' && val.length > 0\n\nconst isString = (val: unknown): val is string =>\n typeof val === 'string'\n\nconst isFunction = (val: unknown): val is (...args: unknown[]) => unknown =>\n typeof val === 'function'\n\n/**\n * Validates a single command conforms to PfCommand interface\n */\nconst validateCommand = (cmd: unknown, index: number): string[] => {\n if (!isObject(cmd)) return [`commands[${index}]: must be an object`]\n\n return [\n !isNonEmptyString(cmd.name) && `commands[${index}]: missing or invalid 'name'`,\n !isString(cmd.description) && `commands[${index}]: missing or invalid 'description'`,\n !Array.isArray(cmd.args) && `commands[${index}]: missing 'args' array`,\n !Array.isArray(cmd.options) && `commands[${index}]: missing 'options' array`,\n !isFunction(cmd.run) && `commands[${index}]: missing 'run' function`,\n ].filter((e): e is string => e !== false)\n}\n\n/**\n * Validates that an object conforms to the PfPlugin interface\n */\nexport const validatePlugin = (obj: unknown): obj is PfPlugin => {\n if (!isObject(obj)) return false\n\n return (\n isNonEmptyString(obj.name) &&\n isString(obj.version) &&\n Array.isArray(obj.commands) &&\n Array.isArray(obj.flows) &&\n isFunction(obj.setup)\n )\n}\n\n/**\n * Detailed validation with error messages\n */\nexport const validatePluginStrict = (obj: unknown): string[] => {\n if (!isObject(obj)) return ['Plugin must be an object']\n\n const baseErrors = [\n !isNonEmptyString(obj.name) && \"Missing or invalid 'name'\",\n !isString(obj.version) && \"Missing or invalid 'version'\",\n !Array.isArray(obj.commands) && \"Missing 'commands' array\",\n !Array.isArray(obj.flows) && \"Missing 'flows' array\",\n !isFunction(obj.setup) && \"Missing 'setup' function\",\n ].filter((e): e is string => e !== false)\n\n const commandErrors = Array.isArray(obj.commands)\n ? obj.commands.flatMap((cmd, i) => validateCommand(cmd, i))\n : []\n\n return [...baseErrors, ...commandErrors]\n}\n\n/**\n * Create a plugin context from workspace discovery\n */\nexport const createPluginContext = (\n workspace: PfWorkspaceContext,\n logger: PfPluginContext['logger'] = defaultLogger,\n): PfPluginContext => ({\n workspace,\n logger,\n})\n\n/**\n * Load and initialize a plugin from a module\n *\n * @param moduleName - The module to import (e.g., '@crossdelta/cloudevents')\n * @param context - Full plugin context with workspace info\n */\nexport const loadPluginFromModule = async (\n moduleName: string,\n context: PfPluginContext,\n): Promise<LoadedPlugin> => {\n const { logger } = context\n\n logger.debug(`Loading plugin from module: ${moduleName}`)\n\n const mod = await import(moduleName)\n\n const createPlugin = mod.createPfPlugin || mod.default?.createPfPlugin\n if (!isFunction(createPlugin)) {\n throw new Error(`Module ${moduleName} does not export createPfPlugin`)\n }\n\n const plugin = createPlugin({\n contractsPath: context.workspace.contracts.path,\n contractsPackage: context.workspace.contracts.packageName,\n availableServices: context.workspace.availableServices,\n })\n\n const errors = validatePluginStrict(plugin)\n if (errors.length > 0) {\n throw new Error(`Plugin from ${moduleName} does not conform to PfPlugin interface:\\n - ${errors.join('\\n - ')}`)\n }\n\n const validatedPlugin = plugin as PfPlugin\n\n logger.debug(`Loaded plugin: ${validatedPlugin.name} v${validatedPlugin.version}`)\n\n await validatedPlugin.setup(context)\n\n return {\n plugin: validatedPlugin,\n source: moduleName,\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/plugin.mjs
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// cli/src/core/plugins/loader.ts
|
|
2
|
+
var defaultLogger = {
|
|
3
|
+
debug: () => {
|
|
4
|
+
},
|
|
5
|
+
info: console.log,
|
|
6
|
+
warn: console.warn,
|
|
7
|
+
error: console.error
|
|
8
|
+
};
|
|
9
|
+
var isObject = (val) => val !== null && typeof val === "object";
|
|
10
|
+
var isNonEmptyString = (val) => typeof val === "string" && val.length > 0;
|
|
11
|
+
var isString = (val) => typeof val === "string";
|
|
12
|
+
var isFunction = (val) => typeof val === "function";
|
|
13
|
+
var validateCommand = (cmd, index) => {
|
|
14
|
+
if (!isObject(cmd)) return [`commands[${index}]: must be an object`];
|
|
15
|
+
return [
|
|
16
|
+
!isNonEmptyString(cmd.name) && `commands[${index}]: missing or invalid 'name'`,
|
|
17
|
+
!isString(cmd.description) && `commands[${index}]: missing or invalid 'description'`,
|
|
18
|
+
!Array.isArray(cmd.args) && `commands[${index}]: missing 'args' array`,
|
|
19
|
+
!Array.isArray(cmd.options) && `commands[${index}]: missing 'options' array`,
|
|
20
|
+
!isFunction(cmd.run) && `commands[${index}]: missing 'run' function`
|
|
21
|
+
].filter((e) => e !== false);
|
|
22
|
+
};
|
|
23
|
+
var validatePlugin = (obj) => {
|
|
24
|
+
if (!isObject(obj)) return false;
|
|
25
|
+
return isNonEmptyString(obj.name) && isString(obj.version) && Array.isArray(obj.commands) && Array.isArray(obj.flows) && isFunction(obj.setup);
|
|
26
|
+
};
|
|
27
|
+
var validatePluginStrict = (obj) => {
|
|
28
|
+
if (!isObject(obj)) return ["Plugin must be an object"];
|
|
29
|
+
const baseErrors = [
|
|
30
|
+
!isNonEmptyString(obj.name) && "Missing or invalid 'name'",
|
|
31
|
+
!isString(obj.version) && "Missing or invalid 'version'",
|
|
32
|
+
!Array.isArray(obj.commands) && "Missing 'commands' array",
|
|
33
|
+
!Array.isArray(obj.flows) && "Missing 'flows' array",
|
|
34
|
+
!isFunction(obj.setup) && "Missing 'setup' function"
|
|
35
|
+
].filter((e) => e !== false);
|
|
36
|
+
const commandErrors = Array.isArray(obj.commands) ? obj.commands.flatMap((cmd, i) => validateCommand(cmd, i)) : [];
|
|
37
|
+
return [...baseErrors, ...commandErrors];
|
|
38
|
+
};
|
|
39
|
+
var createPluginContext = (workspace, logger = defaultLogger) => ({
|
|
40
|
+
workspace,
|
|
41
|
+
logger
|
|
42
|
+
});
|
|
43
|
+
var loadPluginFromModule = async (moduleName, context) => {
|
|
44
|
+
const { logger } = context;
|
|
45
|
+
logger.debug(`Loading plugin from module: ${moduleName}`);
|
|
46
|
+
const mod = await import(moduleName);
|
|
47
|
+
const createPlugin = mod.createPfPlugin || mod.default?.createPfPlugin;
|
|
48
|
+
if (!isFunction(createPlugin)) {
|
|
49
|
+
throw new Error(`Module ${moduleName} does not export createPfPlugin`);
|
|
50
|
+
}
|
|
51
|
+
const plugin = createPlugin({
|
|
52
|
+
contractsPath: context.workspace.contracts.path,
|
|
53
|
+
contractsPackage: context.workspace.contracts.packageName,
|
|
54
|
+
availableServices: context.workspace.availableServices
|
|
55
|
+
});
|
|
56
|
+
const errors = validatePluginStrict(plugin);
|
|
57
|
+
if (errors.length > 0) {
|
|
58
|
+
throw new Error(`Plugin from ${moduleName} does not conform to PfPlugin interface:
|
|
59
|
+
- ${errors.join("\n - ")}`);
|
|
60
|
+
}
|
|
61
|
+
const validatedPlugin = plugin;
|
|
62
|
+
logger.debug(`Loaded plugin: ${validatedPlugin.name} v${validatedPlugin.version}`);
|
|
63
|
+
await validatedPlugin.setup(context);
|
|
64
|
+
return {
|
|
65
|
+
plugin: validatedPlugin,
|
|
66
|
+
source: moduleName
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export {
|
|
70
|
+
createPluginContext,
|
|
71
|
+
loadPluginFromModule,
|
|
72
|
+
validatePlugin,
|
|
73
|
+
validatePluginStrict
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=plugin.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../cli/src/core/plugins/loader.ts"],"sourcesContent":["/**\n * Plugin Loader\n *\n * pf owns the plugin interface. Plugins MUST conform exactly.\n * Strict validation - no duck-typing, no fallbacks.\n */\nimport type { LoadedPlugin, PfPlugin, PfPluginContext, PfWorkspaceContext } from './types'\n\nconst defaultLogger: PfPluginContext['logger'] = {\n debug: () => {},\n info: console.log,\n warn: console.warn,\n error: console.error,\n}\n\nconst isObject = (val: unknown): val is Record<string, unknown> =>\n val !== null && typeof val === 'object'\n\nconst isNonEmptyString = (val: unknown): val is string =>\n typeof val === 'string' && val.length > 0\n\nconst isString = (val: unknown): val is string =>\n typeof val === 'string'\n\nconst isFunction = (val: unknown): val is (...args: unknown[]) => unknown =>\n typeof val === 'function'\n\n/**\n * Validates a single command conforms to PfCommand interface\n */\nconst validateCommand = (cmd: unknown, index: number): string[] => {\n if (!isObject(cmd)) return [`commands[${index}]: must be an object`]\n\n return [\n !isNonEmptyString(cmd.name) && `commands[${index}]: missing or invalid 'name'`,\n !isString(cmd.description) && `commands[${index}]: missing or invalid 'description'`,\n !Array.isArray(cmd.args) && `commands[${index}]: missing 'args' array`,\n !Array.isArray(cmd.options) && `commands[${index}]: missing 'options' array`,\n !isFunction(cmd.run) && `commands[${index}]: missing 'run' function`,\n ].filter((e): e is string => e !== false)\n}\n\n/**\n * Validates that an object conforms to the PfPlugin interface\n */\nexport const validatePlugin = (obj: unknown): obj is PfPlugin => {\n if (!isObject(obj)) return false\n\n return (\n isNonEmptyString(obj.name) &&\n isString(obj.version) &&\n Array.isArray(obj.commands) &&\n Array.isArray(obj.flows) &&\n isFunction(obj.setup)\n )\n}\n\n/**\n * Detailed validation with error messages\n */\nexport const validatePluginStrict = (obj: unknown): string[] => {\n if (!isObject(obj)) return ['Plugin must be an object']\n\n const baseErrors = [\n !isNonEmptyString(obj.name) && \"Missing or invalid 'name'\",\n !isString(obj.version) && \"Missing or invalid 'version'\",\n !Array.isArray(obj.commands) && \"Missing 'commands' array\",\n !Array.isArray(obj.flows) && \"Missing 'flows' array\",\n !isFunction(obj.setup) && \"Missing 'setup' function\",\n ].filter((e): e is string => e !== false)\n\n const commandErrors = Array.isArray(obj.commands)\n ? obj.commands.flatMap((cmd, i) => validateCommand(cmd, i))\n : []\n\n return [...baseErrors, ...commandErrors]\n}\n\n/**\n * Create a plugin context from workspace discovery\n */\nexport const createPluginContext = (\n workspace: PfWorkspaceContext,\n logger: PfPluginContext['logger'] = defaultLogger,\n): PfPluginContext => ({\n workspace,\n logger,\n})\n\n/**\n * Load and initialize a plugin from a module\n *\n * @param moduleName - The module to import (e.g., '@crossdelta/cloudevents')\n * @param context - Full plugin context with workspace info\n */\nexport const loadPluginFromModule = async (\n moduleName: string,\n context: PfPluginContext,\n): Promise<LoadedPlugin> => {\n const { logger } = context\n\n logger.debug(`Loading plugin from module: ${moduleName}`)\n\n const mod = await import(moduleName)\n\n const createPlugin = mod.createPfPlugin || mod.default?.createPfPlugin\n if (!isFunction(createPlugin)) {\n throw new Error(`Module ${moduleName} does not export createPfPlugin`)\n }\n\n const plugin = createPlugin({\n contractsPath: context.workspace.contracts.path,\n contractsPackage: context.workspace.contracts.packageName,\n availableServices: context.workspace.availableServices,\n })\n\n const errors = validatePluginStrict(plugin)\n if (errors.length > 0) {\n throw new Error(`Plugin from ${moduleName} does not conform to PfPlugin interface:\\n - ${errors.join('\\n - ')}`)\n }\n\n const validatedPlugin = plugin as PfPlugin\n\n logger.debug(`Loaded plugin: ${validatedPlugin.name} v${validatedPlugin.version}`)\n\n await validatedPlugin.setup(context)\n\n return {\n plugin: validatedPlugin,\n source: moduleName,\n }\n}\n"],"mappings":";AAQA,IAAM,gBAA2C;AAAA,EAC/C,OAAO,MAAM;AAAA,EAAC;AAAA,EACd,MAAM,QAAQ;AAAA,EACd,MAAM,QAAQ;AAAA,EACd,OAAO,QAAQ;AACjB;AAEA,IAAM,WAAW,CAAC,QAChB,QAAQ,QAAQ,OAAO,QAAQ;AAEjC,IAAM,mBAAmB,CAAC,QACxB,OAAO,QAAQ,YAAY,IAAI,SAAS;AAE1C,IAAM,WAAW,CAAC,QAChB,OAAO,QAAQ;AAEjB,IAAM,aAAa,CAAC,QAClB,OAAO,QAAQ;AAKjB,IAAM,kBAAkB,CAAC,KAAc,UAA4B;AACjE,MAAI,CAAC,SAAS,GAAG,EAAG,QAAO,CAAC,YAAY,KAAK,sBAAsB;AAEnE,SAAO;AAAA,IACL,CAAC,iBAAiB,IAAI,IAAI,KAAK,YAAY,KAAK;AAAA,IAChD,CAAC,SAAS,IAAI,WAAW,KAAK,YAAY,KAAK;AAAA,IAC/C,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,YAAY,KAAK;AAAA,IAC7C,CAAC,MAAM,QAAQ,IAAI,OAAO,KAAK,YAAY,KAAK;AAAA,IAChD,CAAC,WAAW,IAAI,GAAG,KAAK,YAAY,KAAK;AAAA,EAC3C,EAAE,OAAO,CAAC,MAAmB,MAAM,KAAK;AAC1C;AAKO,IAAM,iBAAiB,CAAC,QAAkC;AAC/D,MAAI,CAAC,SAAS,GAAG,EAAG,QAAO;AAE3B,SACE,iBAAiB,IAAI,IAAI,KACzB,SAAS,IAAI,OAAO,KACpB,MAAM,QAAQ,IAAI,QAAQ,KAC1B,MAAM,QAAQ,IAAI,KAAK,KACvB,WAAW,IAAI,KAAK;AAExB;AAKO,IAAM,uBAAuB,CAAC,QAA2B;AAC9D,MAAI,CAAC,SAAS,GAAG,EAAG,QAAO,CAAC,0BAA0B;AAEtD,QAAM,aAAa;AAAA,IACjB,CAAC,iBAAiB,IAAI,IAAI,KAAK;AAAA,IAC/B,CAAC,SAAS,IAAI,OAAO,KAAK;AAAA,IAC1B,CAAC,MAAM,QAAQ,IAAI,QAAQ,KAAK;AAAA,IAChC,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK;AAAA,IAC7B,CAAC,WAAW,IAAI,KAAK,KAAK;AAAA,EAC5B,EAAE,OAAO,CAAC,MAAmB,MAAM,KAAK;AAExC,QAAM,gBAAgB,MAAM,QAAQ,IAAI,QAAQ,IAC5C,IAAI,SAAS,QAAQ,CAAC,KAAK,MAAM,gBAAgB,KAAK,CAAC,CAAC,IACxD,CAAC;AAEL,SAAO,CAAC,GAAG,YAAY,GAAG,aAAa;AACzC;AAKO,IAAM,sBAAsB,CACjC,WACA,SAAoC,mBACf;AAAA,EACrB;AAAA,EACA;AACF;AAQO,IAAM,uBAAuB,OAClC,YACA,YAC0B;AAC1B,QAAM,EAAE,OAAO,IAAI;AAEnB,SAAO,MAAM,+BAA+B,UAAU,EAAE;AAExD,QAAM,MAAM,MAAM,OAAO;AAEzB,QAAM,eAAe,IAAI,kBAAkB,IAAI,SAAS;AACxD,MAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,UAAM,IAAI,MAAM,UAAU,UAAU,iCAAiC;AAAA,EACvE;AAEA,QAAM,SAAS,aAAa;AAAA,IAC1B,eAAe,QAAQ,UAAU,UAAU;AAAA,IAC3C,kBAAkB,QAAQ,UAAU,UAAU;AAAA,IAC9C,mBAAmB,QAAQ,UAAU;AAAA,EACvC,CAAC;AAED,QAAM,SAAS,qBAAqB,MAAM;AAC1C,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,MAAM,eAAe,UAAU;AAAA,MAAiD,OAAO,KAAK,QAAQ,CAAC,EAAE;AAAA,EACnH;AAEA,QAAM,kBAAkB;AAExB,SAAO,MAAM,kBAAkB,gBAAgB,IAAI,KAAK,gBAAgB,OAAO,EAAE;AAEjF,QAAM,gBAAgB,MAAM,OAAO;AAEnC,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF;","names":[]}
|