@veap/plugins 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +131 -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 +3 -0
- package/dist/common/index.js +3 -0
- package/dist/common/types.d.ts +195 -0
- package/dist/common/types.js +40 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -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 +142 -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 +13 -0
- package/dist/router/index.js +13 -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 +633 -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,62 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { stubPath } from "./stubs.js";
|
|
4
|
+
const LOCKFILES = [
|
|
5
|
+
{ file: "pnpm-lock.yaml", pm: "pnpm" },
|
|
6
|
+
{ file: "bun.lock", pm: "bun" },
|
|
7
|
+
{ file: "bun.lockb", pm: "bun" },
|
|
8
|
+
{ file: "yarn.lock", pm: "yarn" },
|
|
9
|
+
{ file: "package-lock.json", pm: "npm" },
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* Detects the package manager used by the project in `dir`.
|
|
13
|
+
*
|
|
14
|
+
* Priority: `packageManager` field in package.json > lockfiles > default (pnpm).
|
|
15
|
+
* On conflicting lockfiles pnpm wins (framework default).
|
|
16
|
+
*/
|
|
17
|
+
export function detectPackageManager(dir) {
|
|
18
|
+
const pkgPath = path.join(dir, "package.json");
|
|
19
|
+
if (fs.existsSync(pkgPath)) {
|
|
20
|
+
try {
|
|
21
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
22
|
+
if (typeof pkg.packageManager === "string") {
|
|
23
|
+
// Handles both "pnpm@11.9.0" and "pnpm@https://..." forms
|
|
24
|
+
const name = pkg.packageManager.slice(0, pkg.packageManager.indexOf("@"));
|
|
25
|
+
if (name === "pnpm" ||
|
|
26
|
+
name === "npm" ||
|
|
27
|
+
name === "yarn" ||
|
|
28
|
+
name === "bun") {
|
|
29
|
+
return name;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// malformed package.json - fall through to lockfiles
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const found = LOCKFILES.filter((l) => fs.existsSync(path.join(dir, l.file)));
|
|
38
|
+
if (found.length > 0) {
|
|
39
|
+
return found.find((l) => l.pm === "pnpm")?.pm ?? found[0].pm;
|
|
40
|
+
}
|
|
41
|
+
return "pnpm";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Overwrites the generated pnpm Dockerfile with the variant matching `pm`.
|
|
45
|
+
* No-op for pnpm (baseline stub), missing variants or missing Dockerfile -
|
|
46
|
+
* best effort, Docker config is optional.
|
|
47
|
+
*/
|
|
48
|
+
export function applyDockerfileForPackageManager(projectDir, pm) {
|
|
49
|
+
if (pm === "pnpm")
|
|
50
|
+
return;
|
|
51
|
+
try {
|
|
52
|
+
// Stubs live at the package root of the installed @veap/framework.
|
|
53
|
+
const variantPath = stubPath("docker", `Dockerfile.${pm}.stub`);
|
|
54
|
+
const dockerfilePath = path.join(projectDir, "Dockerfile");
|
|
55
|
+
if (!fs.existsSync(variantPath) || !fs.existsSync(dockerfilePath))
|
|
56
|
+
return;
|
|
57
|
+
fs.writeFileSync(dockerfilePath, fs.readFileSync(variantPath, "utf-8"), "utf-8");
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// best effort - docker config is optional
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locates the package-root `stubs/` directory of the installed @veap/framework.
|
|
3
|
+
*
|
|
4
|
+
* Every stub-based CLI command (docker, make:plugin, make:template) resolves
|
|
5
|
+
* its templates through this resolver instead of hard-coded relative walks.
|
|
6
|
+
* It anchors on the nearest package.json belonging to @veap/framework, so the
|
|
7
|
+
* result is correct regardless of how deep the calling module sits in `dist/`
|
|
8
|
+
* and regardless of the package layout (npm, pnpm's .pnpm store, yarn, bun).
|
|
9
|
+
*
|
|
10
|
+
* Convention: `stubs/` lives at the package root next to `bin/` and `dist/`
|
|
11
|
+
* (the same convention as create-veap) and is shipped in the npm package via
|
|
12
|
+
* the "files" field in package.json.
|
|
13
|
+
*/
|
|
14
|
+
export declare function findStubsDir(): string;
|
|
15
|
+
/** Resolves a path inside the package-root stubs directory. */
|
|
16
|
+
export declare function stubPath(...segments: string[]): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/**
|
|
5
|
+
* Locates the package-root `stubs/` directory of the installed @veap/framework.
|
|
6
|
+
*
|
|
7
|
+
* Every stub-based CLI command (docker, make:plugin, make:template) resolves
|
|
8
|
+
* its templates through this resolver instead of hard-coded relative walks.
|
|
9
|
+
* It anchors on the nearest package.json belonging to @veap/framework, so the
|
|
10
|
+
* result is correct regardless of how deep the calling module sits in `dist/`
|
|
11
|
+
* and regardless of the package layout (npm, pnpm's .pnpm store, yarn, bun).
|
|
12
|
+
*
|
|
13
|
+
* Convention: `stubs/` lives at the package root next to `bin/` and `dist/`
|
|
14
|
+
* (the same convention as create-veap) and is shipped in the npm package via
|
|
15
|
+
* the "files" field in package.json.
|
|
16
|
+
*/
|
|
17
|
+
export function findStubsDir() {
|
|
18
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
for (;;) {
|
|
20
|
+
const pkgPath = path.join(dir, "package.json");
|
|
21
|
+
if (fs.existsSync(pkgPath)) {
|
|
22
|
+
try {
|
|
23
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
24
|
+
if (pkg.name === "@veap/framework") {
|
|
25
|
+
return path.join(dir, "stubs");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Malformed package.json - keep walking up.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const parent = path.dirname(dir);
|
|
33
|
+
if (parent === dir) {
|
|
34
|
+
throw new Error("Unable to locate the @veap/framework package root. The 'stubs' directory ships with the @veap/framework package; reinstall it or run the CLI from a project that depends on it.");
|
|
35
|
+
}
|
|
36
|
+
dir = parent;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Resolves a path inside the package-root stubs directory. */
|
|
40
|
+
export function stubPath(...segments) {
|
|
41
|
+
return path.join(findStubsDir(), ...segments);
|
|
42
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
function getVariableName(pkgName) {
|
|
4
|
+
const clean = pkgName
|
|
5
|
+
.replace(/^@/, "")
|
|
6
|
+
.replace(/\//, "-")
|
|
7
|
+
.replace(/-plugin$/, "")
|
|
8
|
+
.replace(/-module$/, "");
|
|
9
|
+
// Special cases to match original naming if needed
|
|
10
|
+
if (clean === "blog")
|
|
11
|
+
return "BlogModulePlugin";
|
|
12
|
+
if (clean === "panel")
|
|
13
|
+
return "PanelModulePlugin";
|
|
14
|
+
if (clean === "devtools")
|
|
15
|
+
return "DevtoolsModulePlugin";
|
|
16
|
+
if (clean === "backup")
|
|
17
|
+
return "BackupModulePlugin";
|
|
18
|
+
return (clean
|
|
19
|
+
.split("-")
|
|
20
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
21
|
+
.join("") + "Plugin");
|
|
22
|
+
}
|
|
23
|
+
export function regeneratePluginsRegistry(rootDir) {
|
|
24
|
+
const pluginsFound = [];
|
|
25
|
+
// 1. Scan package.json dependencies
|
|
26
|
+
const pkgPath = path.join(rootDir, "package.json");
|
|
27
|
+
if (fs.existsSync(pkgPath)) {
|
|
28
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
29
|
+
const deps = {
|
|
30
|
+
...(pkg.dependencies || {}),
|
|
31
|
+
...(pkg.devDependencies || {}),
|
|
32
|
+
};
|
|
33
|
+
for (const depName of Object.keys(deps)) {
|
|
34
|
+
let isPlugin = depName.endsWith("-plugin") && depName !== "@veap/cli";
|
|
35
|
+
if (!isPlugin) {
|
|
36
|
+
// Check node_modules package.json
|
|
37
|
+
const depPkgPath = path.join(rootDir, "node_modules", depName, "package.json");
|
|
38
|
+
if (fs.existsSync(depPkgPath)) {
|
|
39
|
+
try {
|
|
40
|
+
const depPkg = JSON.parse(fs.readFileSync(depPkgPath, "utf-8"));
|
|
41
|
+
if ((depPkg.veap || depPkg.veap)?.type === "plugin") {
|
|
42
|
+
isPlugin = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (_) { }
|
|
46
|
+
}
|
|
47
|
+
// Check plugins/ and modules/ local folders
|
|
48
|
+
const cleanName = depName
|
|
49
|
+
.replace(/^@veap\//, "")
|
|
50
|
+
.replace(/^@veap\//, "")
|
|
51
|
+
.replace(/^@contractor\//, "");
|
|
52
|
+
const localPluginPkgPath = path.join(rootDir, "plugins", cleanName, "package.json");
|
|
53
|
+
const localModulePkgPath = path.join(rootDir, "modules", cleanName, "package.json");
|
|
54
|
+
for (const candidatePath of [localPluginPkgPath, localModulePkgPath]) {
|
|
55
|
+
if (fs.existsSync(candidatePath)) {
|
|
56
|
+
try {
|
|
57
|
+
const depPkg = JSON.parse(fs.readFileSync(candidatePath, "utf-8"));
|
|
58
|
+
if (depPkg.veap?.type === "plugin" ||
|
|
59
|
+
depPkg.veap?.type === "module") {
|
|
60
|
+
isPlugin = true;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (_) { }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (isPlugin) {
|
|
69
|
+
pluginsFound.push({
|
|
70
|
+
name: depName,
|
|
71
|
+
varName: getVariableName(depName),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Generate code for plugins.gen.ts
|
|
77
|
+
let code = "" +
|
|
78
|
+
"/**\n * FILE AUTOMATICALLY GENERATED BY veap/cli\n * DO NOT EDIT THIS FILE MANUALLY!\n */\n\n";
|
|
79
|
+
// Write imports
|
|
80
|
+
for (const p of pluginsFound) {
|
|
81
|
+
code += "import { default as " + p.varName + ' } from "' + p.name + '";\n';
|
|
82
|
+
}
|
|
83
|
+
code += "\nexport const plugins = [\n";
|
|
84
|
+
for (const p of pluginsFound) {
|
|
85
|
+
code += " " + p.varName + ",\n";
|
|
86
|
+
}
|
|
87
|
+
code += "];\n";
|
|
88
|
+
const pluginsGenPath = path.join(rootDir, "lib", "plugins.gen.ts");
|
|
89
|
+
// Make sure lib/ directory exists
|
|
90
|
+
const libDir = path.dirname(pluginsGenPath);
|
|
91
|
+
if (!fs.existsSync(libDir)) {
|
|
92
|
+
fs.mkdirSync(libDir, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
fs.writeFileSync(pluginsGenPath, code, "utf-8");
|
|
95
|
+
console.log("[Veap CLI] Regenerated " +
|
|
96
|
+
pluginsGenPath +
|
|
97
|
+
" with " +
|
|
98
|
+
pluginsFound.length +
|
|
99
|
+
" plugins.");
|
|
100
|
+
}
|
|
101
|
+
const WORKSPACE_MARKERS = [
|
|
102
|
+
"pnpm-lock.yaml",
|
|
103
|
+
"bun.lock",
|
|
104
|
+
"bun.lockb",
|
|
105
|
+
"yarn.lock",
|
|
106
|
+
"package-lock.json",
|
|
107
|
+
"pnpm-workspace.yaml",
|
|
108
|
+
];
|
|
109
|
+
function hasWorkspacesField(dir) {
|
|
110
|
+
const pkgPath = path.join(dir, "package.json");
|
|
111
|
+
if (!fs.existsSync(pkgPath))
|
|
112
|
+
return false;
|
|
113
|
+
try {
|
|
114
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
115
|
+
const workspaces = pkg.workspaces;
|
|
116
|
+
if (Array.isArray(workspaces) && workspaces.length > 0)
|
|
117
|
+
return true;
|
|
118
|
+
return (!!workspaces &&
|
|
119
|
+
typeof workspaces === "object" &&
|
|
120
|
+
Array.isArray(workspaces.packages) &&
|
|
121
|
+
workspaces.packages.length > 0);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function findProjectRoot(currentDir) {
|
|
128
|
+
if (WORKSPACE_MARKERS.some((marker) => fs.existsSync(path.join(currentDir, marker))) ||
|
|
129
|
+
hasWorkspacesField(currentDir)) {
|
|
130
|
+
return currentDir;
|
|
131
|
+
}
|
|
132
|
+
const parentDir = path.dirname(currentDir);
|
|
133
|
+
if (parentDir === currentDir) {
|
|
134
|
+
return process.cwd();
|
|
135
|
+
}
|
|
136
|
+
return findProjectRoot(parentDir);
|
|
137
|
+
}
|
|
138
|
+
export function processStubs(srcDir, destDir, variables) {
|
|
139
|
+
if (!fs.existsSync(destDir)) {
|
|
140
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
141
|
+
}
|
|
142
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
let destName = entry.name;
|
|
145
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
146
|
+
destName = destName.replace(new RegExp("\\{\\{" + key + "\\}\\}", "g"), value);
|
|
147
|
+
}
|
|
148
|
+
if (destName.endsWith(".stub")) {
|
|
149
|
+
destName = destName.slice(0, -5);
|
|
150
|
+
}
|
|
151
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
152
|
+
const destPath = path.join(destDir, destName);
|
|
153
|
+
if (entry.isDirectory()) {
|
|
154
|
+
processStubs(srcPath, destPath, variables);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
let content = fs.readFileSync(srcPath, "utf-8");
|
|
158
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
159
|
+
content = content.replace(new RegExp("\\{\\{" + key + "\\}\\}", "g"), value);
|
|
160
|
+
}
|
|
161
|
+
fs.writeFileSync(destPath, content);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { notifyPluginsChanged, onPluginsChanged } from "./common/events";
|
|
2
|
+
export { PathPrefixContext, usePathPrefix, } from "./widgets/path-prefix-context";
|
|
3
|
+
export { ExtensionPointClient, PluginExtensionPointClient, usePluginExtensions, } from "./widgets/plugin-extension-point-client";
|
|
4
|
+
export { PluginWidgetAreaClient, usePluginWidgets, WidgetAreaClient, } from "./widgets/plugin-widget-area-client";
|
|
5
|
+
export { type ConfirmOptions, type ConfirmResult, useConfirmAction, } from "./widgets/use-confirm-action";
|
|
6
|
+
export { WidgetComposerClient } from "./widgets/widget-composer-client";
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
export { notifyPluginsChanged, onPluginsChanged } from "./common/events";
|
|
3
|
+
export { PathPrefixContext, usePathPrefix, } from "./widgets/path-prefix-context";
|
|
4
|
+
// Compatibility Aliases & Client Widgets
|
|
5
|
+
export { ExtensionPointClient, PluginExtensionPointClient, usePluginExtensions, } from "./widgets/plugin-extension-point-client";
|
|
6
|
+
export { PluginWidgetAreaClient, usePluginWidgets, WidgetAreaClient, } from "./widgets/plugin-widget-area-client";
|
|
7
|
+
export { useConfirmAction, } from "./widgets/use-confirm-action";
|
|
8
|
+
export { WidgetComposerClient } from "./widgets/widget-composer-client";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NavigationService } from "../navigation/navigation.service";
|
|
2
|
+
import type { PluginRegistry } from "../registry/registry.service";
|
|
3
|
+
import type { RouterService } from "../router/router.service";
|
|
4
|
+
/**
|
|
5
|
+
* Typed boundary between the plugins use cases and the modules that consume
|
|
6
|
+
* them (facades, navigation, router, intl, presentation components).
|
|
7
|
+
*/
|
|
8
|
+
export interface PluginsContext {
|
|
9
|
+
registry: PluginRegistry;
|
|
10
|
+
navigation: NavigationService;
|
|
11
|
+
router?: RouterService;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Binds the resolved plugins services. Called once by the composition root.
|
|
15
|
+
*/
|
|
16
|
+
export declare function bindPluginsContext(context: PluginsContext): void;
|
|
17
|
+
/**
|
|
18
|
+
* Returns true if the plugins context has been bound by PluginsServiceProvider.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isPluginsContextBound(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the bound plugins services.
|
|
23
|
+
*
|
|
24
|
+
* @throws If the plugins service provider has not booted yet.
|
|
25
|
+
*/
|
|
26
|
+
export declare function pluginsContext(): PluginsContext;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AppError } from "@veap/kernel";
|
|
2
|
+
const globalForPluginsContext = globalThis;
|
|
3
|
+
/**
|
|
4
|
+
* Binds the resolved plugins services. Called once by the composition root.
|
|
5
|
+
*/
|
|
6
|
+
export function bindPluginsContext(context) {
|
|
7
|
+
globalForPluginsContext.__VEAP_PLUGINS_CONTEXT__ = context;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Returns true if the plugins context has been bound by PluginsServiceProvider.
|
|
11
|
+
*/
|
|
12
|
+
export function isPluginsContextBound() {
|
|
13
|
+
return Boolean(globalForPluginsContext.__VEAP_PLUGINS_CONTEXT__);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns the bound plugins services.
|
|
17
|
+
*
|
|
18
|
+
* @throws If the plugins service provider has not booted yet.
|
|
19
|
+
*/
|
|
20
|
+
export function pluginsContext() {
|
|
21
|
+
const context = globalForPluginsContext.__VEAP_PLUGINS_CONTEXT__;
|
|
22
|
+
if (!context) {
|
|
23
|
+
throw AppError.Internal("[Plugins] Context is not bound. PluginsServiceProvider must boot before the plugins facades are used.");
|
|
24
|
+
}
|
|
25
|
+
return context;
|
|
26
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side events for plugin state changes.
|
|
3
|
+
*
|
|
4
|
+
* When a plugin is toggled via the manager, we need a way to
|
|
5
|
+
* notify all interested client components (ExtensionPointClient,
|
|
6
|
+
* WidgetAreaClient, etc.) so they can re-fetch their data without
|
|
7
|
+
* requiring a full page refresh.
|
|
8
|
+
*/
|
|
9
|
+
type Listener = () => void;
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to plugin state changes.
|
|
12
|
+
* Returns an unsubscribe function.
|
|
13
|
+
*/
|
|
14
|
+
export declare function onPluginsChanged(cb: Listener): () => void;
|
|
15
|
+
/**
|
|
16
|
+
* Notify all subscribers that a plugin was toggled.
|
|
17
|
+
*/
|
|
18
|
+
export declare function notifyPluginsChanged(): void;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side events for plugin state changes.
|
|
3
|
+
*
|
|
4
|
+
* When a plugin is toggled via the manager, we need a way to
|
|
5
|
+
* notify all interested client components (ExtensionPointClient,
|
|
6
|
+
* WidgetAreaClient, etc.) so they can re-fetch their data without
|
|
7
|
+
* requiring a full page refresh.
|
|
8
|
+
*/
|
|
9
|
+
const listeners = new Set();
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to plugin state changes.
|
|
12
|
+
* Returns an unsubscribe function.
|
|
13
|
+
*/
|
|
14
|
+
export function onPluginsChanged(cb) {
|
|
15
|
+
listeners.add(cb);
|
|
16
|
+
return () => {
|
|
17
|
+
listeners.delete(cb);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Notify all subscribers that a plugin was toggled.
|
|
22
|
+
*/
|
|
23
|
+
export function notifyPluginsChanged() {
|
|
24
|
+
// biome-ignore lint/suspicious/useIterableCallbackReturn: <ignore>
|
|
25
|
+
listeners.forEach((cb) => cb());
|
|
26
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { UserPermission, UserRole } from "@veap/auth";
|
|
2
|
+
import type { SystemEvent } from "@veap/kernel";
|
|
3
|
+
export type { SystemEvent };
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
export type SidebarGroupType = {
|
|
7
|
+
title: string;
|
|
8
|
+
items: SidebarMenuType;
|
|
9
|
+
};
|
|
10
|
+
export type SidebarMenuItemType<T = Record<string, string>> = {
|
|
11
|
+
id?: string;
|
|
12
|
+
title: string;
|
|
13
|
+
icon?: string;
|
|
14
|
+
url: string;
|
|
15
|
+
roles?: string[];
|
|
16
|
+
permissions?: string[];
|
|
17
|
+
priority?: number;
|
|
18
|
+
badge?: string | number | null | undefined;
|
|
19
|
+
badgeVariant?: "default" | "secondary" | "destructive" | "outline" | null | undefined;
|
|
20
|
+
} & T;
|
|
21
|
+
export type SidebarMenuType = SidebarMenuItemType<{
|
|
22
|
+
items?: SidebarMenuItemType<{
|
|
23
|
+
items?: SidebarMenuItemType[];
|
|
24
|
+
}>[];
|
|
25
|
+
}>[];
|
|
26
|
+
export declare const PluginManifestSchema: z.ZodObject<{
|
|
27
|
+
id: z.ZodString;
|
|
28
|
+
name: z.ZodString;
|
|
29
|
+
version: z.ZodString;
|
|
30
|
+
description: z.ZodOptional<z.ZodString>;
|
|
31
|
+
dependencies: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
32
|
+
extends: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
33
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
34
|
+
system: z.ZodDefault<z.ZodBoolean>;
|
|
35
|
+
hasSetup: z.ZodDefault<z.ZodBoolean>;
|
|
36
|
+
author: z.ZodOptional<z.ZodString>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
export interface PluginManifest {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
version: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
dependencies?: string[];
|
|
44
|
+
extends?: string[];
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
system?: boolean;
|
|
47
|
+
hasSetup?: boolean;
|
|
48
|
+
author?: string;
|
|
49
|
+
[key: string]: any;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Shape of the `veap` field in a plugin `package.json`.
|
|
53
|
+
* This replaces the standalone `manifest.json` file.
|
|
54
|
+
*/
|
|
55
|
+
export interface VeapPackageMetadata {
|
|
56
|
+
type?: string;
|
|
57
|
+
id?: string;
|
|
58
|
+
name?: string;
|
|
59
|
+
description?: string;
|
|
60
|
+
enabled?: boolean;
|
|
61
|
+
system?: boolean;
|
|
62
|
+
hasSetup?: boolean;
|
|
63
|
+
dependencies?: string[];
|
|
64
|
+
extends?: string[];
|
|
65
|
+
isNpm?: boolean;
|
|
66
|
+
author?: string;
|
|
67
|
+
[key: string]: any;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Shape of the `package.json` for a Veap plugin.
|
|
71
|
+
*/
|
|
72
|
+
export interface VeapPackageJson {
|
|
73
|
+
name: string;
|
|
74
|
+
version: string;
|
|
75
|
+
description?: string;
|
|
76
|
+
veap?: VeapPackageMetadata;
|
|
77
|
+
[key: string]: any;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Helper to build a `PluginManifest` from a `package.json` import.
|
|
81
|
+
* Eliminates the need for a standalone `manifest.json` file.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* import pkg from "../package.json" with { type: "json" };
|
|
86
|
+
* import { createManifestFromPackageJson } from "@veap/framework/plugins";
|
|
87
|
+
* const manifest = createManifestFromPackageJson(pkg);
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export declare function createManifestFromPackageJson(pkg: VeapPackageJson): PluginManifest;
|
|
91
|
+
export interface PluginHook {
|
|
92
|
+
point: string;
|
|
93
|
+
handler: (data: any, context?: any) => Promise<any> | any;
|
|
94
|
+
priority?: number;
|
|
95
|
+
}
|
|
96
|
+
export interface PluginExtension {
|
|
97
|
+
id: string;
|
|
98
|
+
target: string;
|
|
99
|
+
point: string;
|
|
100
|
+
component: React.ComponentType<any>;
|
|
101
|
+
priority?: number;
|
|
102
|
+
metadata?: any;
|
|
103
|
+
roles?: string[];
|
|
104
|
+
permissions?: string[];
|
|
105
|
+
}
|
|
106
|
+
export interface PluginWidget {
|
|
107
|
+
id: string;
|
|
108
|
+
name: string;
|
|
109
|
+
area: string;
|
|
110
|
+
component: React.ComponentType<any>;
|
|
111
|
+
priority?: number;
|
|
112
|
+
defaultColSpan?: number;
|
|
113
|
+
defaultRowSpan?: number;
|
|
114
|
+
roles?: string[];
|
|
115
|
+
permissions?: string[];
|
|
116
|
+
}
|
|
117
|
+
export interface PluginPageProps {
|
|
118
|
+
params: any;
|
|
119
|
+
searchParams: any;
|
|
120
|
+
context?: VeapMiddlewareContext;
|
|
121
|
+
}
|
|
122
|
+
export interface VeapMiddlewareContext {
|
|
123
|
+
params: any;
|
|
124
|
+
searchParams: any;
|
|
125
|
+
path: string;
|
|
126
|
+
roles?: UserRole[];
|
|
127
|
+
permissions?: UserPermission[];
|
|
128
|
+
[key: string]: any;
|
|
129
|
+
}
|
|
130
|
+
export type VeapMiddleware = (context: VeapMiddlewareContext, next: () => Promise<React.ReactNode>) => Promise<React.ReactNode>;
|
|
131
|
+
export interface BreadcrumbItem {
|
|
132
|
+
label: string;
|
|
133
|
+
href?: string;
|
|
134
|
+
id?: string;
|
|
135
|
+
}
|
|
136
|
+
export type BreadcrumbValue = string | ((props: PluginPageProps) => Promise<string | BreadcrumbItem | BreadcrumbItem[]> | string | BreadcrumbItem | BreadcrumbItem[]);
|
|
137
|
+
export type PluginNavElement = SidebarMenuItemType<{
|
|
138
|
+
items?: SidebarMenuItemType<{
|
|
139
|
+
items?: SidebarMenuItemType[];
|
|
140
|
+
}>[];
|
|
141
|
+
}>;
|
|
142
|
+
export interface PluginNavigationGroupDefinition {
|
|
143
|
+
title?: string;
|
|
144
|
+
priority?: number;
|
|
145
|
+
items: PluginNavElement[];
|
|
146
|
+
}
|
|
147
|
+
export type PluginNavigationGroupMap = Record<string, PluginNavElement[] | PluginNavigationGroupDefinition>;
|
|
148
|
+
export type ModuleNavigation = PluginNavigation;
|
|
149
|
+
export type ModuleNavElement = PluginNavElement;
|
|
150
|
+
export type ModuleManifest = PluginManifest;
|
|
151
|
+
export type ApiMiddleware = (request: Request, context: any, next: () => Promise<Response>) => Promise<Response>;
|
|
152
|
+
export interface PluginNavigation {
|
|
153
|
+
public?: PluginNavElement[];
|
|
154
|
+
admin?: PluginNavigationGroupMap;
|
|
155
|
+
settings?: PluginNavigationGroupMap;
|
|
156
|
+
}
|
|
157
|
+
export interface IPlugin {
|
|
158
|
+
locales?: Record<string, () => Promise<{
|
|
159
|
+
default: any;
|
|
160
|
+
}>>;
|
|
161
|
+
manifest: PluginManifest;
|
|
162
|
+
migrations?: any[];
|
|
163
|
+
init?: () => Promise<void> | void;
|
|
164
|
+
onMigrate?: () => Promise<void> | void;
|
|
165
|
+
onEnable?: (context?: any) => Promise<void> | void;
|
|
166
|
+
onDisable?: () => Promise<void> | void;
|
|
167
|
+
hooks?: PluginHook[];
|
|
168
|
+
extensions?: PluginExtension[];
|
|
169
|
+
widgets?: PluginWidget[];
|
|
170
|
+
navigation?: PluginNavigation;
|
|
171
|
+
plugins?: IPlugin[];
|
|
172
|
+
/**
|
|
173
|
+
* App Router-style route tree definition.
|
|
174
|
+
*
|
|
175
|
+
* This is the standard way to define routes for a plugin in Veap.
|
|
176
|
+
* Typically generated dynamically using `discoverRoutes` from the plugin's `app/` directory.
|
|
177
|
+
*
|
|
178
|
+
* Can contain nested layouts, loading/error boundaries, route groups,
|
|
179
|
+
* parallel routes, and dynamic segments.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* const plugin: IPlugin = {
|
|
184
|
+
* manifest,
|
|
185
|
+
* routeTree: async () => {
|
|
186
|
+
* const { discoverRoutes } = await import("@veap/framework/router");
|
|
187
|
+
* const path = await import("path");
|
|
188
|
+
* const appDir = path.join(process.cwd(), "plugins", "my-plugin", "src", "app");
|
|
189
|
+
* return discoverRoutes(appDir, (relPath) => import(`../src/app/${relPath}`));
|
|
190
|
+
* },
|
|
191
|
+
* };
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
routeTree?: import("../router/route-tree").RouteNode | (() => Promise<import("../router/route-tree").RouteNode> | import("../router/route-tree").RouteNode);
|
|
195
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const PluginManifestSchema = z.object({
|
|
3
|
+
id: z.string(),
|
|
4
|
+
name: z.string(),
|
|
5
|
+
version: z.string(),
|
|
6
|
+
description: z.string().optional(),
|
|
7
|
+
dependencies: z.array(z.string()).default([]),
|
|
8
|
+
extends: z.array(z.string()).default([]),
|
|
9
|
+
enabled: z.boolean().default(true),
|
|
10
|
+
system: z.boolean().default(false),
|
|
11
|
+
hasSetup: z.boolean().default(false),
|
|
12
|
+
author: z.string().optional(),
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Helper to build a `PluginManifest` from a `package.json` import.
|
|
16
|
+
* Eliminates the need for a standalone `manifest.json` file.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import pkg from "../package.json" with { type: "json" };
|
|
21
|
+
* import { createManifestFromPackageJson } from "@veap/framework/plugins";
|
|
22
|
+
* const manifest = createManifestFromPackageJson(pkg);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function createManifestFromPackageJson(pkg) {
|
|
26
|
+
const meta = pkg.veap || {};
|
|
27
|
+
return {
|
|
28
|
+
id: meta.id || pkg.name.replace(/^@[^/]+\//, ""),
|
|
29
|
+
name: meta.name || pkg.name,
|
|
30
|
+
version: pkg.version,
|
|
31
|
+
description: meta.description || pkg.description,
|
|
32
|
+
enabled: meta.enabled ?? true,
|
|
33
|
+
system: meta.system ?? false,
|
|
34
|
+
hasSetup: meta.hasSetup ?? false,
|
|
35
|
+
dependencies: meta.dependencies || [],
|
|
36
|
+
extends: meta.extends || [],
|
|
37
|
+
isNpm: meta.isNpm ?? true,
|
|
38
|
+
author: meta.author,
|
|
39
|
+
};
|
|
40
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED