@luna-park/plugin 1.7.0 → 1.8.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 +229 -139
- package/dist/index.d.mts +17 -3
- package/dist/index.mjs +30 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,139 +1,229 @@
|
|
|
1
|
-
# @luna-park/plugin
|
|
2
|
-
|
|
3
|
-
This package provides the core types and helper functions for creating plugins for **Luna Park**, a visual scripting platform.
|
|
4
|
-
|
|
5
|
-
It exports the necessary interfaces to define plugin configurations, editor components, logic nodes, and lifecycle hooks.
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @luna-park/plugin
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Usage
|
|
14
|
-
|
|
15
|
-
The primary way to define a plugin is using the `makePlugin` helper function. This ensures type safety for your plugin configuration.
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
import { makePlugin, ELogicType } from "@luna-park/plugin";
|
|
19
|
-
import awesomeIcon from "./awesome-icon.svg"; // Icon URL
|
|
20
|
-
|
|
21
|
-
export default makePlugin({
|
|
22
|
-
id: "my-awesome-plugin",
|
|
23
|
-
name: "My Awesome Plugin",
|
|
24
|
-
icon: awesomeIcon, // Icon URL
|
|
25
|
-
description: "Adds amazing capabilities to Luna Park.",
|
|
26
|
-
|
|
27
|
-
// Plugin configuration schema (optional)
|
|
28
|
-
config: LogicType.object({
|
|
29
|
-
apiKey: LogicType.string(),
|
|
30
|
-
theme: LogicType.string({enum: ["light", "dark"], default: "dark"}),
|
|
31
|
-
}),
|
|
32
|
-
|
|
33
|
-
// Lifecycle hooks (optional)
|
|
34
|
-
lifecycle: {
|
|
35
|
-
mount: async (env) => {
|
|
36
|
-
console.log("Plugin mounted", env.config.apiKey);
|
|
37
|
-
},
|
|
38
|
-
update: async ({config}) => {
|
|
39
|
-
console.log("Config updated", config);
|
|
40
|
-
},
|
|
41
|
-
unmount: (env) => {
|
|
42
|
-
console.log("Plugin unmounted");
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
// Extend the editor
|
|
47
|
-
editor: {
|
|
48
|
-
// Register custom Vue components for the editor (optional)
|
|
49
|
-
components: [
|
|
50
|
-
{
|
|
51
|
-
name: "Element/Avatar",
|
|
52
|
-
component: MyAvatarComponent,
|
|
53
|
-
properties: {
|
|
54
|
-
src: LogicType.string({ name: "Avatar URL" }),
|
|
55
|
-
name: LogicType.string({ name: "Name" }),
|
|
56
|
-
},
|
|
57
|
-
slots: {
|
|
58
|
-
default: LogicType.void()
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
// ... component definitions
|
|
62
|
-
],
|
|
63
|
-
|
|
64
|
-
// Register custom logic nodes (optional)
|
|
65
|
-
nodes: [
|
|
66
|
-
makeLogicNode({
|
|
67
|
-
name: "log/stuff",
|
|
68
|
-
inputs: {
|
|
69
|
-
in_exec: LogicType.exec(),
|
|
70
|
-
in_stuff: LogicType.string({ name: "Stuff" })
|
|
71
|
-
},
|
|
72
|
-
outputs: {
|
|
73
|
-
out_exec: LogicType.exec()
|
|
74
|
-
},
|
|
75
|
-
methods: {
|
|
76
|
-
async in_exec() {
|
|
77
|
-
console.log(this.in_stuff);
|
|
78
|
-
await this.out_exec();
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
display: {
|
|
82
|
-
name: "Log stuff"
|
|
83
|
-
}
|
|
84
|
-
})
|
|
85
|
-
// ... node definitions
|
|
86
|
-
],
|
|
87
|
-
|
|
88
|
-
// Register wrapper for the editor (optional)
|
|
89
|
-
wrapper : MyWrapperComponent,
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
// Code injection options (optional)
|
|
93
|
-
inject: {
|
|
94
|
-
css: "body { background-color: #000; }",
|
|
95
|
-
js: "console.log('Hello world!')"
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## API Reference
|
|
101
|
-
|
|
102
|
-
### `makePlugin(plugin)`
|
|
103
|
-
|
|
104
|
-
A helper identity function that takes a `TPlugin` object and returns it. It is used to infer types for the configuration and internal state.
|
|
105
|
-
|
|
106
|
-
### `TPlugin<TConfig, TInternal>`
|
|
107
|
-
|
|
108
|
-
The main interface for defining a plugin.
|
|
109
|
-
|
|
110
|
-
| Property | Type | Description |
|
|
111
|
-
| :--- | :--- | :--- |
|
|
112
|
-
| `id` | `string` | **Required**. Unique identifier for the plugin. |
|
|
113
|
-
| `name` | `string` | **Required**. Display name of the plugin. |
|
|
114
|
-
| `icon` | `string` | **Required**. Icon for the plugin. |
|
|
115
|
-
| `description` | `string` | Brief description of what the plugin does. |
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
1
|
+
# @luna-park/plugin
|
|
2
|
+
|
|
3
|
+
This package provides the core types and helper functions for creating plugins for **Luna Park**, a visual scripting platform.
|
|
4
|
+
|
|
5
|
+
It exports the necessary interfaces to define plugin configurations, editor components, logic nodes, and lifecycle hooks.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @luna-park/plugin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
The primary way to define a plugin is using the `makePlugin` helper function. This ensures type safety for your plugin configuration.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { makePlugin, ELogicType } from "@luna-park/plugin";
|
|
19
|
+
import awesomeIcon from "./awesome-icon.svg"; // Icon URL
|
|
20
|
+
|
|
21
|
+
export default makePlugin({
|
|
22
|
+
id: "my-awesome-plugin",
|
|
23
|
+
name: "My Awesome Plugin",
|
|
24
|
+
icon: awesomeIcon, // Icon URL
|
|
25
|
+
description: "Adds amazing capabilities to Luna Park.",
|
|
26
|
+
|
|
27
|
+
// Plugin configuration schema (optional)
|
|
28
|
+
config: LogicType.object({
|
|
29
|
+
apiKey: LogicType.string(),
|
|
30
|
+
theme: LogicType.string({enum: ["light", "dark"], default: "dark"}),
|
|
31
|
+
}),
|
|
32
|
+
|
|
33
|
+
// Lifecycle hooks (optional)
|
|
34
|
+
lifecycle: {
|
|
35
|
+
mount: async (env) => {
|
|
36
|
+
console.log("Plugin mounted", env.config.apiKey);
|
|
37
|
+
},
|
|
38
|
+
update: async ({config}) => {
|
|
39
|
+
console.log("Config updated", config);
|
|
40
|
+
},
|
|
41
|
+
unmount: (env) => {
|
|
42
|
+
console.log("Plugin unmounted");
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Extend the editor
|
|
47
|
+
editor: {
|
|
48
|
+
// Register custom Vue components for the editor (optional)
|
|
49
|
+
components: [
|
|
50
|
+
{
|
|
51
|
+
name: "Element/Avatar",
|
|
52
|
+
component: MyAvatarComponent,
|
|
53
|
+
properties: {
|
|
54
|
+
src: LogicType.string({ name: "Avatar URL" }),
|
|
55
|
+
name: LogicType.string({ name: "Name" }),
|
|
56
|
+
},
|
|
57
|
+
slots: {
|
|
58
|
+
default: LogicType.void()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// ... component definitions
|
|
62
|
+
],
|
|
63
|
+
|
|
64
|
+
// Register custom logic nodes (optional)
|
|
65
|
+
nodes: [
|
|
66
|
+
makeLogicNode({
|
|
67
|
+
name: "log/stuff",
|
|
68
|
+
inputs: {
|
|
69
|
+
in_exec: LogicType.exec(),
|
|
70
|
+
in_stuff: LogicType.string({ name: "Stuff" })
|
|
71
|
+
},
|
|
72
|
+
outputs: {
|
|
73
|
+
out_exec: LogicType.exec()
|
|
74
|
+
},
|
|
75
|
+
methods: {
|
|
76
|
+
async in_exec() {
|
|
77
|
+
console.log(this.in_stuff);
|
|
78
|
+
await this.out_exec();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
display: {
|
|
82
|
+
name: "Log stuff"
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
// ... node definitions
|
|
86
|
+
],
|
|
87
|
+
|
|
88
|
+
// Register wrapper for the editor (optional)
|
|
89
|
+
wrapper : MyWrapperComponent,
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// Code injection options (optional)
|
|
93
|
+
inject: {
|
|
94
|
+
css: "body { background-color: #000; }",
|
|
95
|
+
js: "console.log('Hello world!')"
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## API Reference
|
|
101
|
+
|
|
102
|
+
### `makePlugin(plugin)`
|
|
103
|
+
|
|
104
|
+
A helper identity function that takes a `TPlugin` object and returns it. It is used to infer types for the configuration and internal state.
|
|
105
|
+
|
|
106
|
+
### `TPlugin<TConfig, TInternal>`
|
|
107
|
+
|
|
108
|
+
The main interface for defining a plugin.
|
|
109
|
+
|
|
110
|
+
| Property | Type | Description |
|
|
111
|
+
| :--- | :--- | :--- |
|
|
112
|
+
| `id` | `string` | **Required**. Unique identifier for the plugin. |
|
|
113
|
+
| `name` | `string` | **Required**. Display name of the plugin. |
|
|
114
|
+
| `icon` | `string` | **Required**. Icon for the plugin. |
|
|
115
|
+
| `description` | `string` | Brief description of what the plugin does. |
|
|
116
|
+
| `api` | `TOption<TEnv, unknown>` | Functions exposed to other plugins (see below). |
|
|
117
|
+
| `config` | `TSchema` | Schema definition for the plugin's configuration settings. |
|
|
118
|
+
| `dependencies` | `Array<{ id, name, target }>` | Other plugins this plugin requires (see below). |
|
|
119
|
+
| `lifecycle` | `object` | Lifecycle hooks (`mount`, `unmount`, `update`). |
|
|
120
|
+
| `editor` | `object` | Extensions for the editor (components, nodes, tokens, wrapper). |
|
|
121
|
+
| `build` | `object` | Build-time configurations (Vite plugins, imports). |
|
|
122
|
+
| `inject` | `object` | CSS/JS injection options. |
|
|
123
|
+
|
|
124
|
+
### Dependencies
|
|
125
|
+
|
|
126
|
+
A plugin can depend on other plugins. Missing dependencies are installed alongside it, and dependencies are always loaded, mounted and injected before the plugins that require them.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
export default makePlugin({
|
|
130
|
+
id: "my-awesome-plugin",
|
|
131
|
+
// ...
|
|
132
|
+
dependencies: [
|
|
133
|
+
{ id: "nuxt-ui", name: "Nuxt UI", target: "@luna-park/plugin-nuxt-ui" }
|
|
134
|
+
]
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Property | Description |
|
|
139
|
+
| :--- | :--- |
|
|
140
|
+
| `id` | The `id` of the required plugin. |
|
|
141
|
+
| `name` | Display name, shown before the dependency is installed. |
|
|
142
|
+
| `target` | npm package name or URL used to install it. |
|
|
143
|
+
|
|
144
|
+
`sortPlugins(plugins)` returns the given plugins in dependency order.
|
|
145
|
+
|
|
146
|
+
### Using another plugin's functions
|
|
147
|
+
|
|
148
|
+
A plugin exposes functions through `api`, built from its own environment (its config and internals). Other plugins get it with `env.getPlugin(id)`. Import only the API type from the dependency so its code is not bundled twice.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// @luna-park/plugin-a
|
|
152
|
+
export type TPluginAApi = { greet: (name: string) => string; };
|
|
153
|
+
|
|
154
|
+
export default makePlugin({
|
|
155
|
+
id: "plugin-a",
|
|
156
|
+
// ...
|
|
157
|
+
api: (env): TPluginAApi => ({
|
|
158
|
+
greet: (name) => `${ env.config.greeting } ${ name }`
|
|
159
|
+
})
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// @luna-park/plugin-b
|
|
163
|
+
import type { TPluginAApi } from "@luna-park/plugin-a";
|
|
164
|
+
|
|
165
|
+
export default makePlugin({
|
|
166
|
+
id: "plugin-b",
|
|
167
|
+
// ...
|
|
168
|
+
dependencies: [{ id: "plugin-a", name: "Plugin A", target: "@luna-park/plugin-a" }],
|
|
169
|
+
lifecycle: {
|
|
170
|
+
mount: async (env) => {
|
|
171
|
+
const pluginA = await env.getPlugin<TPluginAApi>("plugin-a");
|
|
172
|
+
env.log(pluginA?.greet("world") ?? "Plugin A is not installed");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`getPlugin` resolves to `undefined` when the plugin is not installed or exposes no `api`. It works in both `editor` and `build` modes, and dependencies are always loaded first.
|
|
179
|
+
|
|
180
|
+
### `TEnv<TConfig, TInternal>`
|
|
181
|
+
|
|
182
|
+
The environment object passed to lifecycle hooks and dynamic options.
|
|
183
|
+
|
|
184
|
+
| Property | Type | Description |
|
|
185
|
+
|:------------|:----------------------| :--- |
|
|
186
|
+
| `app` | `TApplication` | Access to the core application instance. |
|
|
187
|
+
| `config` | `Static<TConfig>` | The resolved configuration values based on the schema. |
|
|
188
|
+
| `backend` | `object` | Editor backend helpers (see below). |
|
|
189
|
+
| `getPlugin` | `(id) => Promise<TApi>` | Get the `api` of another installed plugin. |
|
|
190
|
+
| `host` | `THost \| undefined` | Desktop app helpers (see below). Editor only. |
|
|
191
|
+
| `mode` | `"build" \| "editor"` | The current running mode. |
|
|
192
|
+
| `internals` | `TInternals` | Internal state (if defined). |
|
|
193
|
+
|
|
194
|
+
#### Transactions
|
|
195
|
+
|
|
196
|
+
Backend logic can run inside a database transaction (the `DB Transaction` node, or the editor's test runs, which are always rolled back). When a plugin changes something outside the database, it follows the transaction with:
|
|
197
|
+
|
|
198
|
+
| Function | Description |
|
|
199
|
+
| :--- | :--- |
|
|
200
|
+
| `backend.onCommit(callback)` | Runs `callback` once the current transaction is committed, or right away outside a transaction. |
|
|
201
|
+
| `backend.onRollback(callback)` | Runs `callback` if the current transaction is rolled back. Ignored outside a transaction. |
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
await saveContent(id, content);
|
|
205
|
+
env.backend.onRollback(() => deleteContent(id));
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The generated backend exposes the same functions as `onCommit` and `onRollback` from `@/database/index.js`.
|
|
209
|
+
|
|
210
|
+
#### Desktop host
|
|
211
|
+
|
|
212
|
+
In the desktop app, `host.available` is `true` and the plugin can use:
|
|
213
|
+
|
|
214
|
+
| Function | Description |
|
|
215
|
+
| :--- | :--- |
|
|
216
|
+
| `host.readFile(path)` | Reads a file of the project folder (`undefined` if it doesn't exist). |
|
|
217
|
+
| `host.writeFile(path, data)` | Writes a file of the project folder, creating its folders. |
|
|
218
|
+
| `host.removeFile(path)` | Removes a file of the project folder. |
|
|
219
|
+
| `host.fetch(input, init)` | `fetch` without CORS restrictions. |
|
|
220
|
+
|
|
221
|
+
Paths are relative to the project folder and can't leave it. In the web editor, `host` is `undefined` or `host.available` is `false`.
|
|
222
|
+
|
|
223
|
+
### `TOption<TEnv, T>`
|
|
224
|
+
|
|
225
|
+
Many properties in `TPlugin` can be either a static value `T` or a function that returns `T` (or `Promise<T>`). This allows for dynamic configuration based on the environment.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
type TOption<TEnv, T> = T | ((env: TEnv) => T | Promise<T>);
|
|
229
|
+
```
|
package/dist/index.d.mts
CHANGED
|
@@ -1025,6 +1025,7 @@ export declare enum EInjectionKey {
|
|
|
1025
1025
|
ServerBody = "serverBody"
|
|
1026
1026
|
}
|
|
1027
1027
|
type TBasePlugin<TConfig extends TSchema = TSchema, TInternals = unknown> = {
|
|
1028
|
+
api?: TOption<TEnv<TConfig, TInternals>, unknown>;
|
|
1028
1029
|
build?: {
|
|
1029
1030
|
backImports?: Array<{
|
|
1030
1031
|
name: string;
|
|
@@ -1087,25 +1088,38 @@ type TEnv<TConfig extends TSchema = TSchema, TInternals = unknown> = {
|
|
|
1087
1088
|
addFile: (file: DeepPartial<TFile<true>> & {
|
|
1088
1089
|
type: EElementType;
|
|
1089
1090
|
}, parentId?: string) => TFile;
|
|
1090
|
-
app: TApplication
|
|
1091
|
+
app: TApplication<boolean>;
|
|
1091
1092
|
backend: {
|
|
1092
1093
|
cookies: Record<string, {
|
|
1093
|
-
options?: Record<string,
|
|
1094
|
+
options?: Record<string, unknown>;
|
|
1094
1095
|
value: string;
|
|
1095
1096
|
}>;
|
|
1097
|
+
onCommit: (callback: () => Promise<void> | void) => Promise<void>;
|
|
1098
|
+
onRollback: (callback: () => Promise<void> | void) => void;
|
|
1096
1099
|
};
|
|
1097
1100
|
config: Static<TConfig>;
|
|
1098
1101
|
getFile: (id: string) => TFile | undefined;
|
|
1102
|
+
getPlugin: <TApi = unknown>(id: string) => Promise<TApi | undefined>;
|
|
1103
|
+
host?: THost;
|
|
1099
1104
|
internals: TInternals;
|
|
1100
1105
|
log: (message: string, severity?: "info" | "warning" | "error") => void;
|
|
1101
1106
|
mode: "build" | "editor";
|
|
1102
1107
|
};
|
|
1108
|
+
type THost = {
|
|
1109
|
+
readonly available: boolean;
|
|
1110
|
+
fetch: typeof fetch;
|
|
1111
|
+
readFile: (path: string) => Promise<Uint8Array | undefined>;
|
|
1112
|
+
removeFile: (path: string) => Promise<void>;
|
|
1113
|
+
writeFile: (path: string, data: Uint8Array) => Promise<void>;
|
|
1114
|
+
};
|
|
1103
1115
|
type TOption<TParams = unknown, TResult = unknown> = TResult | ((env: TParams) => Promise<TResult> | TResult);
|
|
1104
1116
|
//#endregion
|
|
1105
1117
|
//#region src/index.d.ts
|
|
1106
1118
|
export declare function makePlugin<TConfig extends TSchema, TInternal>(plugin: TPlugin<TConfig, TInternal>): TPlugin<TConfig, TInternal>;
|
|
1107
1119
|
export declare function makeComponent<TProps extends Record<string, TSchema>>(component: TComponent<TProps>): TComponent<TProps>;
|
|
1108
1120
|
export declare function loadPlugin<TConfig extends TSchema, TInternal>(plugin: TPlugin<TConfig, TInternal>, env: Partial<TEnv>): Promise<TBasePlugin<TConfig, TInternal>>;
|
|
1121
|
+
export declare function sortPlugins<T extends Pick<TBasePlugin, "dependencies" | "id">>(plugins: Array<T>): Array<T>;
|
|
1122
|
+
export declare function getPluginApi<TApi = unknown>(plugin: Pick<TBasePlugin, "api" | "id"> | undefined, env: Partial<TEnv>): Promise<TApi | undefined>;
|
|
1109
1123
|
export declare function getPluginParam<T>(target: TOption<TEnv, T>, env: Partial<TEnv>): Promise<T>;
|
|
1110
1124
|
//#endregion
|
|
1111
|
-
export { EElementType, ELogicScope, ELogicType, EVarType, _default as LSettingsStoryWrapper, LogicType, LogicUtil, type Static, type TAnyLogic, type TApplication, type TBasePlugin, type TComponent, type TDatabaseCondition, type TElementPanel, type TElementPanelAction, type TElementPanelProps, type TEnv, type TFile, type TFileComponent, type TFileConfig, type TFileCron, type TFileDatabase, type TFileMedia, type TFileRoute, type TFileScript, type TFileStore, type TFileText, type TFileType, type TFolder, type THookParams, type TInterface, type TInterfaceEditorProps, type TLayoutSchema, type TLogic, type TLogicMain, type TLogicNode, type TOption, type TPanels, type TPlugin, type TProxyPlugin, type TRouteGuard, type TSchema, type TSlot, type TTemplate, type TToken, makeLogicNode };
|
|
1125
|
+
export { EElementType, ELogicScope, ELogicType, EVarType, _default as LSettingsStoryWrapper, LogicType, LogicUtil, type Static, type TAnyLogic, type TApplication, type TBasePlugin, type TComponent, type TDatabaseCondition, type TElementPanel, type TElementPanelAction, type TElementPanelProps, type TEnv, type TFile, type TFileComponent, type TFileConfig, type TFileCron, type TFileDatabase, type TFileMedia, type TFileRoute, type TFileScript, type TFileStore, type TFileText, type TFileType, type TFolder, type THookParams, type THost, type TInterface, type TInterfaceEditorProps, type TLayoutSchema, type TLogic, type TLogicMain, type TLogicNode, type TOption, type TPanels, type TPlugin, type TProxyPlugin, type TRouteGuard, type TSchema, type TSlot, type TTemplate, type TToken, makeLogicNode };
|
package/dist/index.mjs
CHANGED
|
@@ -967,6 +967,35 @@ async function loadPlugin(plugin, env) {
|
|
|
967
967
|
});
|
|
968
968
|
return result;
|
|
969
969
|
}
|
|
970
|
+
function sortPlugins(plugins) {
|
|
971
|
+
const byId = new Map(plugins.map((plugin) => [plugin.id, plugin]));
|
|
972
|
+
const visited = /* @__PURE__ */ new Set();
|
|
973
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
974
|
+
const result = [];
|
|
975
|
+
function visit(plugin) {
|
|
976
|
+
if (visited.has(plugin.id)) return;
|
|
977
|
+
if (visiting.has(plugin.id)) throw new Error(`Circular plugin dependency detected on \`${plugin.id}\`.`);
|
|
978
|
+
visiting.add(plugin.id);
|
|
979
|
+
for (const dependency of plugin.dependencies ?? []) {
|
|
980
|
+
const target = byId.get(dependency.id);
|
|
981
|
+
if (target) visit(target);
|
|
982
|
+
}
|
|
983
|
+
visiting.delete(plugin.id);
|
|
984
|
+
visited.add(plugin.id);
|
|
985
|
+
result.push(plugin);
|
|
986
|
+
}
|
|
987
|
+
for (const plugin of plugins) visit(plugin);
|
|
988
|
+
return result;
|
|
989
|
+
}
|
|
990
|
+
async function getPluginApi(plugin, env) {
|
|
991
|
+
if (!plugin?.api) return;
|
|
992
|
+
const { config, internals } = env.app?.config?.plugins?.[plugin.id] ?? {};
|
|
993
|
+
return await getPluginParam(plugin.api, {
|
|
994
|
+
...env,
|
|
995
|
+
config,
|
|
996
|
+
internals
|
|
997
|
+
});
|
|
998
|
+
}
|
|
970
999
|
async function getPluginParam(target, env) {
|
|
971
1000
|
if (typeof target === "function") return target({
|
|
972
1001
|
mode: "editor",
|
|
@@ -975,4 +1004,4 @@ async function getPluginParam(target, env) {
|
|
|
975
1004
|
return target;
|
|
976
1005
|
}
|
|
977
1006
|
//#endregion
|
|
978
|
-
export { EElementType, EInjectionKey, ELogicScope, ELogicType, EPluginHooks, ETokenType, EVarType, LSettingsStoryWrapper_default as LSettingsStoryWrapper, LogicType, LogicUtil, getPluginParam, loadPlugin, makeComponent, makeLogicNode, makePlugin };
|
|
1007
|
+
export { EElementType, EInjectionKey, ELogicScope, ELogicType, EPluginHooks, ETokenType, EVarType, LSettingsStoryWrapper_default as LSettingsStoryWrapper, LogicType, LogicUtil, getPluginApi, getPluginParam, loadPlugin, makeComponent, makeLogicNode, makePlugin, sortPlugins };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luna-park/plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Plugin helper for Luna Park.",
|
|
6
6
|
"homepage": "https://luna-park.app",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"vue": "^3.5.42"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@luna-park/eslint-config": "^1.3
|
|
52
|
+
"@luna-park/eslint-config": "^1.4.3",
|
|
53
53
|
"@luna-park/logicnodes": "^2.0.0",
|
|
54
54
|
"@luna-park/shared": "^1.0.0",
|
|
55
55
|
"@luna-park/utils": "^2.0.0",
|