@veap/plugins 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +132 -2
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/plugin.actions.d.ts +6 -0
- package/dist/actions/plugin.actions.js +60 -0
- package/dist/actions.d.ts +1 -0
- package/dist/actions.js +1 -0
- package/dist/cli/add.d.ts +4 -0
- package/dist/cli/add.js +191 -0
- package/dist/cli/eject.d.ts +2 -0
- package/dist/cli/eject.js +136 -0
- package/dist/cli/generate-plugin.d.ts +3 -0
- package/dist/cli/generate-plugin.js +48 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +6 -0
- package/dist/cli/package-manager.d.ts +14 -0
- package/dist/cli/package-manager.js +62 -0
- package/dist/cli/stubs.d.ts +16 -0
- package/dist/cli/stubs.js +42 -0
- package/dist/cli/utils.d.ts +3 -0
- package/dist/cli/utils.js +164 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.js +8 -0
- package/dist/common/context.d.ts +26 -0
- package/dist/common/context.js +26 -0
- package/dist/common/events.d.ts +19 -0
- package/dist/common/events.js +26 -0
- package/dist/common/index.d.ts +4 -0
- package/dist/common/index.js +4 -0
- package/dist/common/tokens.d.ts +14 -0
- package/dist/common/tokens.js +12 -0
- package/dist/common/types.d.ts +202 -0
- package/dist/common/types.js +39 -0
- package/dist/composition.d.ts +15 -0
- package/dist/composition.js +20 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/migrations/0001_create_plugins_tables.d.ts +4 -0
- package/dist/migrations/0001_create_plugins_tables.js +29 -0
- package/dist/migrations/index.d.ts +4 -0
- package/dist/migrations/index.js +7 -0
- package/dist/models/SystemPlugin.d.ts +19 -0
- package/dist/models/SystemPlugin.js +14 -0
- package/dist/models/SystemUserWidget.d.ts +19 -0
- package/dist/models/SystemUserWidget.js +17 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.js +2 -0
- package/dist/models.d.ts +1 -0
- package/dist/models.js +1 -0
- package/dist/navigation/breadcrumbs.d.ts +2 -0
- package/dist/navigation/breadcrumbs.js +84 -0
- package/dist/navigation/index.d.ts +2 -0
- package/dist/navigation/index.js +2 -0
- package/dist/navigation/navigation.service.d.ts +27 -0
- package/dist/navigation/navigation.service.js +183 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/plugins.provider.d.ts +20 -0
- package/dist/providers/plugins.provider.js +143 -0
- package/dist/registry/contracts.d.ts +72 -0
- package/dist/registry/contracts.js +4 -0
- package/dist/registry/facade.d.ts +45 -0
- package/dist/registry/facade.js +49 -0
- package/dist/registry/index.d.ts +5 -0
- package/dist/registry/index.js +5 -0
- package/dist/registry/migration-runner.d.ts +8 -0
- package/dist/registry/migration-runner.js +12 -0
- package/dist/registry/plugin.repository.d.ts +19 -0
- package/dist/registry/plugin.repository.js +74 -0
- package/dist/registry/registry.service.d.ts +69 -0
- package/dist/registry/registry.service.js +535 -0
- package/dist/router/discovery.d.ts +27 -0
- package/dist/router/discovery.js +37 -0
- package/dist/router/handler.d.ts +6 -0
- package/dist/router/handler.js +40 -0
- package/dist/router/index.d.ts +14 -0
- package/dist/router/index.js +14 -0
- package/dist/router/matcher.d.ts +5 -0
- package/dist/router/matcher.js +32 -0
- package/dist/router/middlewares.d.ts +25 -0
- package/dist/router/middlewares.js +119 -0
- package/dist/router/react/error-boundary.d.ts +30 -0
- package/dist/router/react/error-boundary.js +68 -0
- package/dist/router/react/index.d.ts +4 -0
- package/dist/router/react/index.js +4 -0
- package/dist/router/react/renderer.d.ts +13 -0
- package/dist/router/react/renderer.js +159 -0
- package/dist/router/react/soft-navigation.d.ts +4 -0
- package/dist/router/react/soft-navigation.js +34 -0
- package/dist/router/react/with-router.d.ts +22 -0
- package/dist/router/react/with-router.js +19 -0
- package/dist/router/route-tree.d.ts +177 -0
- package/dist/router/route-tree.js +639 -0
- package/dist/router/router.service.d.ts +25 -0
- package/dist/router/router.service.js +127 -0
- package/dist/router/scanner.d.ts +4 -0
- package/dist/router/scanner.js +337 -0
- package/dist/router/utils.d.ts +10 -0
- package/dist/router/utils.js +48 -0
- package/dist/router.d.ts +6 -0
- package/dist/router.js +6 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +17 -0
- package/dist/widgets/index.d.ts +8 -0
- package/dist/widgets/index.js +8 -0
- package/dist/widgets/path-prefix-context.d.ts +5 -0
- package/dist/widgets/path-prefix-context.js +9 -0
- package/dist/widgets/plugin-extension-point-client.d.ts +15 -0
- package/dist/widgets/plugin-extension-point-client.js +44 -0
- package/dist/widgets/plugin-extension-point.d.ts +14 -0
- package/dist/widgets/plugin-extension-point.js +51 -0
- package/dist/widgets/plugin-widget-area-client.d.ts +11 -0
- package/dist/widgets/plugin-widget-area-client.js +31 -0
- package/dist/widgets/plugin-widget-area.d.ts +10 -0
- package/dist/widgets/plugin-widget-area.js +19 -0
- package/dist/widgets/use-confirm-action.d.ts +19 -0
- package/dist/widgets/use-confirm-action.js +68 -0
- package/dist/widgets/widget-composer-client.d.ts +21 -0
- package/dist/widgets/widget-composer-client.js +281 -0
- package/dist/widgets/widget-composer.d.ts +8 -0
- package/dist/widgets/widget-composer.js +28 -0
- package/package.json +115 -4
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { isDatabaseInitialized } from "@veap/database";
|
|
2
|
+
import { IntlService } from "@veap/intl/server";
|
|
3
|
+
import { eventBus, ServiceProvider } from "@veap/kernel";
|
|
4
|
+
import { bindPluginsContext } from "../common/context.js";
|
|
5
|
+
import { APP_PLUGINS, ROUTER_SERVICE, SITE_LAYOUT } from "../common/tokens.js";
|
|
6
|
+
import { runPluginMigrations } from "../migrations/index.js";
|
|
7
|
+
import { NavigationService } from "../navigation/navigation.service.js";
|
|
8
|
+
import { MIGRATION_RUNNER, PLUGIN_REPOSITORY } from "../registry/contracts.js";
|
|
9
|
+
import { ensurePluginsInitialized, registerPlugins, } from "../registry/facade.js";
|
|
10
|
+
import { KnexMigrationRunner } from "../registry/migration-runner.js";
|
|
11
|
+
import { ActiveRecordPluginRepository } from "../registry/plugin.repository.js";
|
|
12
|
+
import { PluginRegistry } from "../registry/registry.service.js";
|
|
13
|
+
import { RouterService } from "../router/router.service.js";
|
|
14
|
+
export class PluginsServiceProvider extends ServiceProvider {
|
|
15
|
+
config;
|
|
16
|
+
constructor(container, config) {
|
|
17
|
+
super(container);
|
|
18
|
+
this.config = config;
|
|
19
|
+
}
|
|
20
|
+
register() {
|
|
21
|
+
this.container.register({
|
|
22
|
+
token: PLUGIN_REPOSITORY,
|
|
23
|
+
useClass: ActiveRecordPluginRepository,
|
|
24
|
+
singleton: true,
|
|
25
|
+
});
|
|
26
|
+
this.container.register({
|
|
27
|
+
token: MIGRATION_RUNNER,
|
|
28
|
+
useClass: KnexMigrationRunner,
|
|
29
|
+
singleton: true,
|
|
30
|
+
});
|
|
31
|
+
this.container.register({
|
|
32
|
+
token: PluginRegistry,
|
|
33
|
+
useClass: PluginRegistry,
|
|
34
|
+
singleton: true,
|
|
35
|
+
});
|
|
36
|
+
this.container.register({
|
|
37
|
+
token: NavigationService,
|
|
38
|
+
useClass: NavigationService,
|
|
39
|
+
singleton: true,
|
|
40
|
+
});
|
|
41
|
+
this.container.register({
|
|
42
|
+
token: RouterService,
|
|
43
|
+
useClass: RouterService,
|
|
44
|
+
singleton: true,
|
|
45
|
+
});
|
|
46
|
+
this.container.register({
|
|
47
|
+
token: ROUTER_SERVICE,
|
|
48
|
+
useClass: RouterService,
|
|
49
|
+
singleton: true,
|
|
50
|
+
});
|
|
51
|
+
if (this.config?.siteLayout) {
|
|
52
|
+
this.container.register({
|
|
53
|
+
token: SITE_LAYOUT,
|
|
54
|
+
useValue: this.config.siteLayout,
|
|
55
|
+
singleton: true,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async boot() {
|
|
60
|
+
const registry = await this.container.resolve(PluginRegistry);
|
|
61
|
+
const navigation = await this.container.resolve(NavigationService);
|
|
62
|
+
const routerService = await this.container.resolve(RouterService);
|
|
63
|
+
bindPluginsContext({
|
|
64
|
+
registry,
|
|
65
|
+
navigation,
|
|
66
|
+
router: routerService,
|
|
67
|
+
});
|
|
68
|
+
if (this.config?.rewrites) {
|
|
69
|
+
for (const [from, to] of Object.entries(this.config.rewrites)) {
|
|
70
|
+
routerService.addRewrite(from, to);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (this.config?.siteLayout) {
|
|
74
|
+
routerService.setSiteLayout(this.config.siteLayout);
|
|
75
|
+
}
|
|
76
|
+
// Register Intl loader for plugins if IntlService is available
|
|
77
|
+
if (this.container.has(IntlService)) {
|
|
78
|
+
try {
|
|
79
|
+
const intlService = await this.container.resolve(IntlService);
|
|
80
|
+
intlService.registerLoader({
|
|
81
|
+
id: "veap:plugins",
|
|
82
|
+
priority: 100,
|
|
83
|
+
load: async (locale) => {
|
|
84
|
+
let combined = {};
|
|
85
|
+
const plugins = registry.getPlugins();
|
|
86
|
+
for (const plugin of plugins) {
|
|
87
|
+
if (plugin.locales && plugin.locales[locale]) {
|
|
88
|
+
try {
|
|
89
|
+
const pluginDict = await plugin.locales[locale]();
|
|
90
|
+
const data = pluginDict.default || pluginDict;
|
|
91
|
+
combined = { ...combined, ...data };
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
console.error(`[veap:Intl] Error loading locale ${locale} for plugin ${plugin.manifest.id}`, e);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return combined;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
console.warn("[veap:plugins] Intl loader registration deferred:", err?.message || err);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Clear cache when plugins are enabled/disabled
|
|
107
|
+
eventBus.subscribe("system:plugin:toggle", "route-tree-cache", async () => {
|
|
108
|
+
await routerService.clearCache();
|
|
109
|
+
});
|
|
110
|
+
// Clear cache after plugin initialization finishes
|
|
111
|
+
eventBus.subscribe("system:plugins:init:end", "route-tree-cache-init", async () => {
|
|
112
|
+
await routerService.clearCache();
|
|
113
|
+
});
|
|
114
|
+
// Register plugins from config or container
|
|
115
|
+
const pluginsToRegister = this.config?.plugins ||
|
|
116
|
+
(this.container.has(APP_PLUGINS)
|
|
117
|
+
? await this.container.resolve(APP_PLUGINS)
|
|
118
|
+
: this.container.has("AppPlugins")
|
|
119
|
+
? await this.container.resolve("AppPlugins")
|
|
120
|
+
: []);
|
|
121
|
+
if (pluginsToRegister?.length) {
|
|
122
|
+
await registerPlugins(pluginsToRegister);
|
|
123
|
+
}
|
|
124
|
+
// Automatically run plugin migrations if database is initialized and autoMigrate is not disabled
|
|
125
|
+
if (this.config?.autoMigrate !== false && isDatabaseInitialized()) {
|
|
126
|
+
try {
|
|
127
|
+
await runPluginMigrations();
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
console.warn("[veap:plugins] Plugin migration deferred:", err?.message || err);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Auto-bootstrap and synchronize database state
|
|
134
|
+
try {
|
|
135
|
+
await ensurePluginsInitialized();
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
console.warn("[veap:plugins] Plugin initialization deferred:", e?.message || e);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Backwards compatibility aliases
|
|
143
|
+
export { PluginsServiceProvider as PluginServiceProvider, PluginsServiceProvider as RouterServiceProvider, };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { IPlugin } from "../common";
|
|
2
|
+
/**
|
|
3
|
+
* Persisted status of a plugin (mirrors the `plugins` table).
|
|
4
|
+
*/
|
|
5
|
+
export interface PluginStatusRecord {
|
|
6
|
+
id: string;
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
installed: boolean;
|
|
9
|
+
system: boolean;
|
|
10
|
+
/** Raw JSON string of the plugin config, or `null`. */
|
|
11
|
+
config?: string | null;
|
|
12
|
+
lastStep?: string | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Backwards compatibility alias for PluginStatusRecord.
|
|
16
|
+
*/
|
|
17
|
+
export type PluginRecord = PluginStatusRecord;
|
|
18
|
+
/**
|
|
19
|
+
* In-memory status overlay kept by the registry between DB syncs.
|
|
20
|
+
*/
|
|
21
|
+
export interface PluginRuntimeStatus {
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
installed: boolean;
|
|
24
|
+
lastStep?: string | null;
|
|
25
|
+
}
|
|
26
|
+
/** Payload used when a plugin row does not exist yet and must be created. */
|
|
27
|
+
export interface UpsertPluginStatusRecord {
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
installed?: boolean;
|
|
30
|
+
lastStep?: string | null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Plugin status persistence port.
|
|
34
|
+
*/
|
|
35
|
+
export interface IPluginRepository {
|
|
36
|
+
/** Returns all persisted plugin statuses. */
|
|
37
|
+
findAll(): Promise<PluginStatusRecord[]>;
|
|
38
|
+
/** Returns the persisted status of a single plugin, or `null`. */
|
|
39
|
+
findById(id: string): Promise<PluginStatusRecord | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Bulk-seeds statuses for newly registered plugins.
|
|
42
|
+
* Existing rows are left alone except that `system` is re-merged.
|
|
43
|
+
*/
|
|
44
|
+
seed(values: Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
installed: boolean;
|
|
48
|
+
system: boolean;
|
|
49
|
+
}>): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Updates the status of a plugin, creating the row if it does not exist.
|
|
52
|
+
* `system` of the new row is taken from the registered plugin manifest.
|
|
53
|
+
*/
|
|
54
|
+
upsertStatus(id: string, update: UpsertPluginStatusRecord, defaults: {
|
|
55
|
+
system: boolean;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
/** Stores the raw JSON plugin config. */
|
|
58
|
+
updateConfig(id: string, configJson: string): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
/** Injection token for the plugin repository port. */
|
|
61
|
+
export declare const PLUGIN_REPOSITORY: unique symbol;
|
|
62
|
+
/**
|
|
63
|
+
* Migration runner port.
|
|
64
|
+
*/
|
|
65
|
+
export interface IMigrationRunner {
|
|
66
|
+
run(scope: string, migrations: any[]): Promise<void>;
|
|
67
|
+
rollback(scope: string, migrations: any[]): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
/** Injection token for the migration runner port. */
|
|
70
|
+
export declare const MIGRATION_RUNNER: unique symbol;
|
|
71
|
+
/** Convenience type re-export so consumers can name the contract. */
|
|
72
|
+
export type { IPlugin };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { IPlugin } from "../common";
|
|
2
|
+
/**
|
|
3
|
+
* Module-level helpers over the {@link PluginRegistry}.
|
|
4
|
+
*
|
|
5
|
+
* They read the composition-bound plugins context instead of resolving
|
|
6
|
+
* services from the IoC container themselves.
|
|
7
|
+
*/
|
|
8
|
+
export declare const registerPlugins: (plugins: IPlugin[]) => Promise<void>;
|
|
9
|
+
export declare const ensurePluginsInitialized: () => Promise<void>;
|
|
10
|
+
export declare const applyPluginFilters: <T = any>(point: string, data: T,
|
|
11
|
+
/** Optional context (e.g. { userId }) passed to handlers as 2nd argument. */
|
|
12
|
+
context?: any) => Promise<T>;
|
|
13
|
+
export declare const togglePluginState: (id: string, enabled: boolean, context?: any) => Promise<void>;
|
|
14
|
+
export declare const hasPluginHooks: (point: string) => Promise<boolean>;
|
|
15
|
+
export declare const hasPluginExtension: (target: string, point?: string) => Promise<boolean>;
|
|
16
|
+
export declare const getPluginConfig: <T = any>(id: string) => Promise<T | null>;
|
|
17
|
+
export declare const updatePluginConfig: (id: string, config: any) => Promise<void>;
|
|
18
|
+
export declare const getPluginsStatus: () => Promise<{
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
installed: boolean;
|
|
21
|
+
lastStep: string | null;
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
version: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
dependencies?: string[];
|
|
27
|
+
extends?: string[];
|
|
28
|
+
system?: boolean;
|
|
29
|
+
hasSetup?: boolean;
|
|
30
|
+
author?: string;
|
|
31
|
+
}[]>;
|
|
32
|
+
export declare const getPluginStatus: (id: string) => Promise<{
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
installed: boolean;
|
|
35
|
+
lastStep: string | null;
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
version: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
dependencies?: string[];
|
|
41
|
+
extends?: string[];
|
|
42
|
+
system?: boolean;
|
|
43
|
+
hasSetup?: boolean;
|
|
44
|
+
author?: string;
|
|
45
|
+
} | null>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { pluginsContext } from "../common";
|
|
2
|
+
/**
|
|
3
|
+
* Module-level helpers over the {@link PluginRegistry}.
|
|
4
|
+
*
|
|
5
|
+
* They read the composition-bound plugins context instead of resolving
|
|
6
|
+
* services from the IoC container themselves.
|
|
7
|
+
*/
|
|
8
|
+
export const registerPlugins = async (plugins) => {
|
|
9
|
+
const registry = pluginsContext().registry;
|
|
10
|
+
for (const plugin of plugins)
|
|
11
|
+
registry.register(plugin);
|
|
12
|
+
};
|
|
13
|
+
export const ensurePluginsInitialized = async () => {
|
|
14
|
+
await pluginsContext().registry.init();
|
|
15
|
+
};
|
|
16
|
+
export const applyPluginFilters = async (point, data,
|
|
17
|
+
/** Optional context (e.g. { userId }) passed to handlers as 2nd argument. */
|
|
18
|
+
context) => {
|
|
19
|
+
const hooks = await pluginsContext().registry.getHooks(point);
|
|
20
|
+
let result = data;
|
|
21
|
+
for (const hook of hooks) {
|
|
22
|
+
const fn = hook.handler ?? hook.callback;
|
|
23
|
+
if (typeof fn === "function") {
|
|
24
|
+
result = await fn(result, context);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
export const togglePluginState = async (id, enabled, context) => {
|
|
30
|
+
await pluginsContext().registry.togglePlugin(id, enabled, context);
|
|
31
|
+
};
|
|
32
|
+
export const hasPluginHooks = async (point) => {
|
|
33
|
+
return pluginsContext().registry.hasHooks(point);
|
|
34
|
+
};
|
|
35
|
+
export const hasPluginExtension = async (target, point) => {
|
|
36
|
+
return pluginsContext().registry.hasExtension(target, point);
|
|
37
|
+
};
|
|
38
|
+
export const getPluginConfig = async (id) => {
|
|
39
|
+
return pluginsContext().registry.getConfig(id);
|
|
40
|
+
};
|
|
41
|
+
export const updatePluginConfig = async (id, config) => {
|
|
42
|
+
await pluginsContext().registry.updateConfig(id, config);
|
|
43
|
+
};
|
|
44
|
+
export const getPluginsStatus = async () => {
|
|
45
|
+
return pluginsContext().registry.getPluginsStatus();
|
|
46
|
+
};
|
|
47
|
+
export const getPluginStatus = async (id) => {
|
|
48
|
+
return pluginsContext().registry.getPluginStatus(id);
|
|
49
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IMigrationRunner } from "./contracts";
|
|
2
|
+
/**
|
|
3
|
+
* `IMigrationRunner` adapter delegating to the knex-based migration runner.
|
|
4
|
+
*/
|
|
5
|
+
export declare class KnexMigrationRunner implements IMigrationRunner {
|
|
6
|
+
run(scope: string, migrations: any[]): Promise<void>;
|
|
7
|
+
rollback(scope: string, migrations: any[]): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { rollbackMigrations, runMigrations } from "@veap/database";
|
|
2
|
+
/**
|
|
3
|
+
* `IMigrationRunner` adapter delegating to the knex-based migration runner.
|
|
4
|
+
*/
|
|
5
|
+
export class KnexMigrationRunner {
|
|
6
|
+
run(scope, migrations) {
|
|
7
|
+
return runMigrations(scope, migrations);
|
|
8
|
+
}
|
|
9
|
+
rollback(scope, migrations) {
|
|
10
|
+
return rollbackMigrations(scope, migrations);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IPluginRepository, PluginStatusRecord, UpsertPluginStatusRecord } from "./contracts";
|
|
2
|
+
/**
|
|
3
|
+
* `IPluginRepository` adapter backed by the `SystemPlugin` ActiveRecord model
|
|
4
|
+
* and the raw `plugins` table for bulk seeding.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ActiveRecordPluginRepository implements IPluginRepository {
|
|
7
|
+
findAll(): Promise<PluginStatusRecord[]>;
|
|
8
|
+
findById(id: string): Promise<PluginStatusRecord | null>;
|
|
9
|
+
seed(values: Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
installed: boolean;
|
|
13
|
+
system: boolean;
|
|
14
|
+
}>): Promise<void>;
|
|
15
|
+
upsertStatus(id: string, update: UpsertPluginStatusRecord, defaults: {
|
|
16
|
+
system: boolean;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
updateConfig(id: string, configJson: string): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { SystemPlugin } from "../models/SystemPlugin";
|
|
2
|
+
/**
|
|
3
|
+
* `IPluginRepository` adapter backed by the `SystemPlugin` ActiveRecord model
|
|
4
|
+
* and the raw `plugins` table for bulk seeding.
|
|
5
|
+
*/
|
|
6
|
+
export class ActiveRecordPluginRepository {
|
|
7
|
+
async findAll() {
|
|
8
|
+
const rows = await SystemPlugin.all();
|
|
9
|
+
return rows.map((row) => ({
|
|
10
|
+
id: row.id,
|
|
11
|
+
enabled: Boolean(row.enabled),
|
|
12
|
+
installed: Boolean(row.installed),
|
|
13
|
+
system: Boolean(row.system),
|
|
14
|
+
config: row.config ?? null,
|
|
15
|
+
lastStep: row.lastStep ?? null,
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
async findById(id) {
|
|
19
|
+
const row = await SystemPlugin.find(id);
|
|
20
|
+
if (!row)
|
|
21
|
+
return null;
|
|
22
|
+
return {
|
|
23
|
+
id,
|
|
24
|
+
enabled: Boolean(row.enabled),
|
|
25
|
+
installed: Boolean(row.installed),
|
|
26
|
+
system: Boolean(row.system),
|
|
27
|
+
config: row.config ?? null,
|
|
28
|
+
lastStep: row.lastStep ?? null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async seed(values) {
|
|
32
|
+
if (values.length === 0)
|
|
33
|
+
return;
|
|
34
|
+
const { getKnex } = await import("@veap/database");
|
|
35
|
+
const knex = getKnex();
|
|
36
|
+
await knex("plugins")
|
|
37
|
+
.insert(values.map((p) => ({
|
|
38
|
+
id: p.id,
|
|
39
|
+
enabled: p.enabled,
|
|
40
|
+
installed: p.installed,
|
|
41
|
+
system: p.system,
|
|
42
|
+
})))
|
|
43
|
+
.onConflict("id")
|
|
44
|
+
.merge(["system"]);
|
|
45
|
+
}
|
|
46
|
+
async upsertStatus(id, update, defaults) {
|
|
47
|
+
const existing = await SystemPlugin.find(id);
|
|
48
|
+
if (existing) {
|
|
49
|
+
if (update.enabled !== undefined)
|
|
50
|
+
existing.setAttribute("enabled", update.enabled);
|
|
51
|
+
if (update.installed !== undefined)
|
|
52
|
+
existing.setAttribute("installed", update.installed);
|
|
53
|
+
if (update.lastStep !== undefined)
|
|
54
|
+
existing.setAttribute("lastStep", update.lastStep);
|
|
55
|
+
existing.setAttribute("updatedAt", new Date());
|
|
56
|
+
await existing.save();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await SystemPlugin.create({
|
|
60
|
+
id,
|
|
61
|
+
enabled: update.enabled ?? false,
|
|
62
|
+
installed: update.installed ?? false,
|
|
63
|
+
lastStep: update.lastStep ?? null,
|
|
64
|
+
system: defaults.system,
|
|
65
|
+
updatedAt: new Date(),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async updateConfig(id, configJson) {
|
|
69
|
+
await SystemPlugin.where("id", id).update({
|
|
70
|
+
config: configJson,
|
|
71
|
+
updatedAt: new Date(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type IEventBus, type ILogger } from "@veap/kernel";
|
|
2
|
+
import type { IPlugin } from "../common";
|
|
3
|
+
import { type IMigrationRunner, type IPluginRepository } from "./contracts";
|
|
4
|
+
export declare class PluginRegistry {
|
|
5
|
+
private readonly repository;
|
|
6
|
+
private readonly migrationRunner;
|
|
7
|
+
private readonly logger;
|
|
8
|
+
private readonly eventBus;
|
|
9
|
+
private plugins;
|
|
10
|
+
private pluginStatus;
|
|
11
|
+
private processingPlugins;
|
|
12
|
+
private initPromise;
|
|
13
|
+
private initialized;
|
|
14
|
+
constructor(repository: IPluginRepository, migrationRunner: IMigrationRunner, logger: ILogger, eventBus: IEventBus);
|
|
15
|
+
/** Lookup a plugin by id, falling back to the npm-name form. */
|
|
16
|
+
private findPlugin;
|
|
17
|
+
register(plugin: IPlugin): void;
|
|
18
|
+
private updateStatus;
|
|
19
|
+
private updateStep;
|
|
20
|
+
private sortPlugins;
|
|
21
|
+
init(): Promise<void>;
|
|
22
|
+
getPlugins(): IPlugin[];
|
|
23
|
+
getEnabledPlugins(): IPlugin[];
|
|
24
|
+
getPlugin(id: string): IPlugin | undefined;
|
|
25
|
+
getPluginsStatus(): Promise<{
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
installed: boolean;
|
|
28
|
+
lastStep: string | null;
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
version: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
dependencies?: string[];
|
|
34
|
+
extends?: string[];
|
|
35
|
+
system?: boolean;
|
|
36
|
+
hasSetup?: boolean;
|
|
37
|
+
author?: string;
|
|
38
|
+
}[]>;
|
|
39
|
+
getPluginStatus(id: string): Promise<{
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
installed: boolean;
|
|
42
|
+
lastStep: string | null;
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
version: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
dependencies?: string[];
|
|
48
|
+
extends?: string[];
|
|
49
|
+
system?: boolean;
|
|
50
|
+
hasSetup?: boolean;
|
|
51
|
+
author?: string;
|
|
52
|
+
} | null>;
|
|
53
|
+
getHooks(point: string): Promise<import("..").PluginHook[]>;
|
|
54
|
+
getExtensions(target: string, point: string, includeDisabled?: boolean, context?: {
|
|
55
|
+
roles?: string[];
|
|
56
|
+
permissions?: string[];
|
|
57
|
+
}): Promise<import("..").PluginExtension[]>;
|
|
58
|
+
getWidgets(area: string, context?: {
|
|
59
|
+
roles?: string[];
|
|
60
|
+
permissions?: string[];
|
|
61
|
+
}): Promise<import("..").PluginWidget[]>;
|
|
62
|
+
private delay;
|
|
63
|
+
togglePlugin(id: string, enabled: boolean, context?: any): Promise<void>;
|
|
64
|
+
hasHooks(point: string): Promise<boolean>;
|
|
65
|
+
hasExtension(target: string, point?: string): Promise<boolean>;
|
|
66
|
+
getConfig<T>(id: string): Promise<T | null>;
|
|
67
|
+
updateConfig(id: string, config: any): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
export { applyPluginFilters, ensurePluginsInitialized, getPluginConfig, getPluginStatus, getPluginsStatus, hasPluginExtension, hasPluginHooks, registerPlugins, togglePluginState, updatePluginConfig, } from "./facade";
|