@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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -2
  3. package/dist/actions/index.d.ts +1 -0
  4. package/dist/actions/index.js +1 -0
  5. package/dist/actions/plugin.actions.d.ts +6 -0
  6. package/dist/actions/plugin.actions.js +60 -0
  7. package/dist/actions.d.ts +1 -0
  8. package/dist/actions.js +1 -0
  9. package/dist/cli/add.d.ts +4 -0
  10. package/dist/cli/add.js +191 -0
  11. package/dist/cli/eject.d.ts +2 -0
  12. package/dist/cli/eject.js +136 -0
  13. package/dist/cli/generate-plugin.d.ts +3 -0
  14. package/dist/cli/generate-plugin.js +48 -0
  15. package/dist/cli/index.d.ts +6 -0
  16. package/dist/cli/index.js +6 -0
  17. package/dist/cli/package-manager.d.ts +14 -0
  18. package/dist/cli/package-manager.js +62 -0
  19. package/dist/cli/stubs.d.ts +16 -0
  20. package/dist/cli/stubs.js +42 -0
  21. package/dist/cli/utils.d.ts +3 -0
  22. package/dist/cli/utils.js +164 -0
  23. package/dist/client.d.ts +6 -0
  24. package/dist/client.js +8 -0
  25. package/dist/common/context.d.ts +26 -0
  26. package/dist/common/context.js +26 -0
  27. package/dist/common/events.d.ts +19 -0
  28. package/dist/common/events.js +26 -0
  29. package/dist/common/index.d.ts +3 -0
  30. package/dist/common/index.js +3 -0
  31. package/dist/common/types.d.ts +195 -0
  32. package/dist/common/types.js +40 -0
  33. package/dist/index.d.ts +5 -0
  34. package/dist/index.js +5 -0
  35. package/dist/migrations/0001_create_plugins_tables.d.ts +4 -0
  36. package/dist/migrations/0001_create_plugins_tables.js +29 -0
  37. package/dist/migrations/index.d.ts +4 -0
  38. package/dist/migrations/index.js +7 -0
  39. package/dist/models/SystemPlugin.d.ts +19 -0
  40. package/dist/models/SystemPlugin.js +14 -0
  41. package/dist/models/SystemUserWidget.d.ts +19 -0
  42. package/dist/models/SystemUserWidget.js +17 -0
  43. package/dist/models/index.d.ts +2 -0
  44. package/dist/models/index.js +2 -0
  45. package/dist/models.d.ts +1 -0
  46. package/dist/models.js +1 -0
  47. package/dist/navigation/breadcrumbs.d.ts +2 -0
  48. package/dist/navigation/breadcrumbs.js +84 -0
  49. package/dist/navigation/index.d.ts +2 -0
  50. package/dist/navigation/index.js +2 -0
  51. package/dist/navigation/navigation.service.d.ts +27 -0
  52. package/dist/navigation/navigation.service.js +183 -0
  53. package/dist/providers/index.d.ts +1 -0
  54. package/dist/providers/index.js +1 -0
  55. package/dist/providers/plugins.provider.d.ts +20 -0
  56. package/dist/providers/plugins.provider.js +142 -0
  57. package/dist/registry/contracts.d.ts +72 -0
  58. package/dist/registry/contracts.js +4 -0
  59. package/dist/registry/facade.d.ts +45 -0
  60. package/dist/registry/facade.js +49 -0
  61. package/dist/registry/index.d.ts +5 -0
  62. package/dist/registry/index.js +5 -0
  63. package/dist/registry/migration-runner.d.ts +8 -0
  64. package/dist/registry/migration-runner.js +12 -0
  65. package/dist/registry/plugin.repository.d.ts +19 -0
  66. package/dist/registry/plugin.repository.js +74 -0
  67. package/dist/registry/registry.service.d.ts +69 -0
  68. package/dist/registry/registry.service.js +535 -0
  69. package/dist/router/discovery.d.ts +27 -0
  70. package/dist/router/discovery.js +37 -0
  71. package/dist/router/handler.d.ts +6 -0
  72. package/dist/router/handler.js +40 -0
  73. package/dist/router/index.d.ts +13 -0
  74. package/dist/router/index.js +13 -0
  75. package/dist/router/matcher.d.ts +5 -0
  76. package/dist/router/matcher.js +32 -0
  77. package/dist/router/middlewares.d.ts +25 -0
  78. package/dist/router/middlewares.js +119 -0
  79. package/dist/router/react/error-boundary.d.ts +30 -0
  80. package/dist/router/react/error-boundary.js +68 -0
  81. package/dist/router/react/index.d.ts +4 -0
  82. package/dist/router/react/index.js +4 -0
  83. package/dist/router/react/renderer.d.ts +13 -0
  84. package/dist/router/react/renderer.js +159 -0
  85. package/dist/router/react/soft-navigation.d.ts +4 -0
  86. package/dist/router/react/soft-navigation.js +34 -0
  87. package/dist/router/react/with-router.d.ts +22 -0
  88. package/dist/router/react/with-router.js +19 -0
  89. package/dist/router/route-tree.d.ts +177 -0
  90. package/dist/router/route-tree.js +633 -0
  91. package/dist/router/router.service.d.ts +25 -0
  92. package/dist/router/router.service.js +127 -0
  93. package/dist/router/scanner.d.ts +4 -0
  94. package/dist/router/scanner.js +337 -0
  95. package/dist/router/utils.d.ts +10 -0
  96. package/dist/router/utils.js +48 -0
  97. package/dist/router.d.ts +6 -0
  98. package/dist/router.js +6 -0
  99. package/dist/server.d.ts +11 -0
  100. package/dist/server.js +17 -0
  101. package/dist/widgets/index.d.ts +8 -0
  102. package/dist/widgets/index.js +8 -0
  103. package/dist/widgets/path-prefix-context.d.ts +5 -0
  104. package/dist/widgets/path-prefix-context.js +9 -0
  105. package/dist/widgets/plugin-extension-point-client.d.ts +15 -0
  106. package/dist/widgets/plugin-extension-point-client.js +44 -0
  107. package/dist/widgets/plugin-extension-point.d.ts +14 -0
  108. package/dist/widgets/plugin-extension-point.js +51 -0
  109. package/dist/widgets/plugin-widget-area-client.d.ts +11 -0
  110. package/dist/widgets/plugin-widget-area-client.js +31 -0
  111. package/dist/widgets/plugin-widget-area.d.ts +10 -0
  112. package/dist/widgets/plugin-widget-area.js +19 -0
  113. package/dist/widgets/use-confirm-action.d.ts +19 -0
  114. package/dist/widgets/use-confirm-action.js +68 -0
  115. package/dist/widgets/widget-composer-client.d.ts +21 -0
  116. package/dist/widgets/widget-composer-client.js +281 -0
  117. package/dist/widgets/widget-composer.d.ts +8 -0
  118. package/dist/widgets/widget-composer.js +28 -0
  119. package/package.json +115 -4
@@ -0,0 +1,127 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { CACHE_PROVIDER, CONFIG_SERVICE, Inject, Injectable, } from "@veap/kernel";
14
+ import { NavigationService } from "../navigation/navigation.service";
15
+ import { PluginRegistry } from "../registry";
16
+ import { RouteTree, resolveMagicPrefix } from "./route-tree";
17
+ let RouterService = class RouterService {
18
+ cache;
19
+ config;
20
+ pluginRegistry;
21
+ navigation;
22
+ rewrites = new Map();
23
+ siteLayout;
24
+ constructor(cache, config, pluginRegistry, navigation) {
25
+ this.cache = cache;
26
+ this.config = config;
27
+ this.pluginRegistry = pluginRegistry;
28
+ this.navigation = navigation;
29
+ }
30
+ setSiteLayout(layout) {
31
+ this.siteLayout = layout;
32
+ }
33
+ getSiteLayout() {
34
+ return this.siteLayout;
35
+ }
36
+ addRewrite(from, to) {
37
+ this.rewrites.set(from, to);
38
+ }
39
+ getRewrites() {
40
+ return new Map(this.rewrites);
41
+ }
42
+ async clearCache() {
43
+ await this.cache.delete("router:tree");
44
+ }
45
+ async getPluginsWithHomepage() {
46
+ await this.pluginRegistry.init();
47
+ const plugins = this.pluginRegistry.getEnabledPlugins();
48
+ const result = [];
49
+ for (const plugin of plugins) {
50
+ let hasHomepage = false;
51
+ if (plugin.routeTree) {
52
+ const pluginTree = typeof plugin.routeTree === "function"
53
+ ? await plugin.routeTree()
54
+ : plugin.routeTree;
55
+ const tempTree = new RouteTree();
56
+ if (pluginTree instanceof RouteTree) {
57
+ tempTree.addTree(pluginTree.getRoot());
58
+ }
59
+ else {
60
+ tempTree.addTree(pluginTree);
61
+ }
62
+ const match = tempTree.match("/");
63
+ if (match?.isExact) {
64
+ hasHomepage = true;
65
+ }
66
+ }
67
+ if (hasHomepage) {
68
+ result.push({ id: plugin.manifest.id, name: plugin.manifest.name });
69
+ }
70
+ }
71
+ return result;
72
+ }
73
+ async _buildRouteTree(_options) {
74
+ const tree = new RouteTree();
75
+ tree.setRewrites(this.rewrites);
76
+ await this.pluginRegistry.init();
77
+ const plugins = this.pluginRegistry.getEnabledPlugins();
78
+ const prefix = await this.navigation.getPathPrefix();
79
+ for (const plugin of plugins) {
80
+ if (plugin.routeTree) {
81
+ let pluginTree = typeof plugin.routeTree === "function"
82
+ ? await plugin.routeTree()
83
+ : plugin.routeTree;
84
+ if (pluginTree instanceof RouteTree)
85
+ pluginTree = pluginTree.getRoot();
86
+ const resolvedTreeRoot = resolveMagicPrefix(pluginTree, prefix);
87
+ tree.addTree(resolvedTreeRoot);
88
+ }
89
+ }
90
+ // Attach siteLayout only to the "(site)" route group node
91
+ if (this.siteLayout) {
92
+ const rootNode = tree.getRoot();
93
+ if (!rootNode.children)
94
+ rootNode.children = [];
95
+ let siteGroup = rootNode.children.find((c) => c.segment === "(site)");
96
+ if (!siteGroup) {
97
+ siteGroup = { segment: "(site)", children: [] };
98
+ rootNode.children.push(siteGroup);
99
+ }
100
+ siteGroup.layout = this.siteLayout;
101
+ }
102
+ return tree;
103
+ }
104
+ async buildRouteTree(includePrivate = false) {
105
+ const isProd = this.config.get("NODE_ENV") === "production";
106
+ if (isProd) {
107
+ const cached = await this.cache.get("router:tree");
108
+ if (cached)
109
+ return cached;
110
+ }
111
+ const tree = await this._buildRouteTree({ includePrivate });
112
+ if (isProd) {
113
+ await this.cache.set("router:tree", tree);
114
+ }
115
+ return tree;
116
+ }
117
+ };
118
+ RouterService = __decorate([
119
+ Injectable(),
120
+ __param(0, Inject(CACHE_PROVIDER)),
121
+ __param(1, Inject(CONFIG_SERVICE)),
122
+ __param(2, Inject(PluginRegistry)),
123
+ __param(3, Inject(NavigationService)),
124
+ __metadata("design:paramtypes", [Object, Object, PluginRegistry,
125
+ NavigationService])
126
+ ], RouterService);
127
+ export { RouterService };
@@ -0,0 +1,4 @@
1
+ import type { RouteNode } from "./route-tree";
2
+ export declare function discoverRoutes(appDir: string, dynamicImport?: (relPath: string) => Promise<any>): Promise<RouteNode>;
3
+ export declare function globToTree(globResult: Record<string, () => Promise<any>>, baseDir?: string): Promise<RouteNode>;
4
+ export declare function generateRouteManifest(appDir: string, importPrefix: string): string;
@@ -0,0 +1,337 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { logger } from "@veap/kernel";
4
+ const debug = (msg, ...args) => logger.debug("veap:scanner", msg, ...args);
5
+ // ---------------------------------------------------------------------------
6
+ // File conventions
7
+ // ---------------------------------------------------------------------------
8
+ const ROUTE_FILES = {
9
+ page: ["page.tsx", "page.ts", "page.jsx", "page.js"],
10
+ layout: ["layout.tsx", "layout.ts", "layout.jsx", "layout.js"],
11
+ loading: ["loading.tsx", "loading.ts", "loading.jsx", "loading.js"],
12
+ error: ["error.tsx", "error.ts", "error.jsx", "error.js"],
13
+ notFound: ["not-found.tsx", "not-found.ts", "not-found.jsx", "not-found.js"],
14
+ default: ["default.tsx", "default.ts", "default.jsx", "default.js"],
15
+ route: ["route.ts", "route.js"],
16
+ };
17
+ // ---------------------------------------------------------------------------
18
+ // Internal helpers
19
+ // ---------------------------------------------------------------------------
20
+ const IGNORED_DIRS = new Set(["node_modules"]);
21
+ function shouldSkipDir(name) {
22
+ if (IGNORED_DIRS.has(name))
23
+ return true;
24
+ if (name.startsWith("."))
25
+ return true;
26
+ if (name.startsWith("_"))
27
+ return true;
28
+ return false;
29
+ }
30
+ function dirNameToSegment(dirName) {
31
+ return dirName;
32
+ }
33
+ function segmentSortOrder(segment) {
34
+ if (/^\[\[\.\.\./.test(segment))
35
+ return 4;
36
+ if (/^\[\.\.\./.test(segment))
37
+ return 3;
38
+ if (/^\[[^[\\].]+\]$/.test(segment))
39
+ return 2;
40
+ if (segment.startsWith("("))
41
+ return 1;
42
+ return 0;
43
+ }
44
+ function findRouteFile(dir, candidates) {
45
+ for (const candidate of candidates) {
46
+ const fullPath = path.join(dir, candidate);
47
+ if (fs.existsSync(fullPath))
48
+ return fullPath;
49
+ }
50
+ return null;
51
+ }
52
+ function sanitizeImportName(dirName) {
53
+ const optCatchAll = dirName.match(/^\[\[\.\.\.(\w+)\]\]$/);
54
+ if (optCatchAll)
55
+ return `_optspread_${optCatchAll[1]}_`;
56
+ const catchAll = dirName.match(/^\[\.\.\.(\w+)\]$/);
57
+ if (catchAll)
58
+ return `_spread_${catchAll[1]}_`;
59
+ const dynamic = dirName.match(/^\[(\w+)\]$/);
60
+ if (dynamic)
61
+ return `_${dynamic[1]}_`;
62
+ if (dirName.startsWith("(") && dirName.endsWith(")")) {
63
+ return dirName.slice(1, -1);
64
+ }
65
+ if (dirName.startsWith("@")) {
66
+ return `_at_${dirName.slice(1)}`;
67
+ }
68
+ return dirName.replace(/[^a-zA-Z0-9_]/g, "_");
69
+ }
70
+ // ---------------------------------------------------------------------------
71
+ // Runtime route discovery
72
+ // ---------------------------------------------------------------------------
73
+ export async function discoverRoutes(appDir, dynamicImport) {
74
+ return scanDirectory(appDir, "", appDir, dynamicImport);
75
+ }
76
+ async function scanDirectory(dir, segment, baseAppDir, dynamicImport) {
77
+ const relPathToDir = path.relative(baseAppDir, dir).split(path.sep).join("/");
78
+ const nodeId = relPathToDir === "" ? "/" : `/${relPathToDir}`;
79
+ const node = { segment, id: nodeId };
80
+ for (const [key, candidates] of Object.entries(ROUTE_FILES)) {
81
+ const filePath = findRouteFile(dir, candidates);
82
+ if (filePath) {
83
+ let mod;
84
+ if (dynamicImport) {
85
+ const relPath = path
86
+ .relative(baseAppDir, filePath)
87
+ .split(path.sep)
88
+ .join("/");
89
+ mod = await dynamicImport(relPath);
90
+ }
91
+ else {
92
+ const isDev = process.env.NODE_ENV === "development";
93
+ const importPath = isDev ? `${filePath}?v=${Date.now}` : filePath;
94
+ mod = await import(/* turbopackIgnore: true */ importPath);
95
+ }
96
+ const exported = mod.default ?? mod;
97
+ switch (key) {
98
+ case "page":
99
+ node.page = exported;
100
+ break;
101
+ case "layout":
102
+ node.layout = exported;
103
+ break;
104
+ case "loading":
105
+ node.loading = exported;
106
+ break;
107
+ case "error":
108
+ node.error = exported;
109
+ break;
110
+ case "notFound":
111
+ node.notFound = exported;
112
+ break;
113
+ case "default":
114
+ node.default = exported;
115
+ break;
116
+ case "route":
117
+ node.route = exported;
118
+ break;
119
+ }
120
+ if ((key === "page" || key === "layout") && mod.generateMetadata) {
121
+ node.generateMetadata = mod.generateMetadata;
122
+ }
123
+ if (key === "page" || key === "layout" || key === "route") {
124
+ if (mod.auth !== undefined)
125
+ node.auth = mod.auth;
126
+ if (mod.roles !== undefined)
127
+ node.roles = mod.roles;
128
+ if (mod.permissions !== undefined)
129
+ node.permissions = mod.permissions;
130
+ if (mod.middlewares !== undefined)
131
+ node.middlewares = mod.middlewares;
132
+ }
133
+ }
134
+ }
135
+ let entries;
136
+ try {
137
+ entries = fs.readdirSync(dir, { withFileTypes: true });
138
+ }
139
+ catch {
140
+ return node;
141
+ }
142
+ const childDirs = entries.filter((e) => e.isDirectory() && !shouldSkipDir(e.name));
143
+ const children = [];
144
+ const parallelRoutes = {};
145
+ for (const childDir of childDirs) {
146
+ const childPath = path.join(dir, childDir.name);
147
+ if (childDir.name.startsWith("@")) {
148
+ const slotName = childDir.name.slice(1);
149
+ parallelRoutes[slotName] = await scanDirectory(childPath, slotName, baseAppDir, dynamicImport);
150
+ }
151
+ else {
152
+ const childSegment = dirNameToSegment(childDir.name);
153
+ children.push(await scanDirectory(childPath, childSegment, baseAppDir, dynamicImport));
154
+ }
155
+ }
156
+ children.sort((a, b) => {
157
+ const orderA = segmentSortOrder(a.segment);
158
+ const orderB = segmentSortOrder(b.segment);
159
+ if (orderA !== orderB)
160
+ return orderA - orderB;
161
+ return a.segment.localeCompare(b.segment);
162
+ });
163
+ if (children.length > 0) {
164
+ node.children = children;
165
+ }
166
+ if (Object.keys(parallelRoutes).length > 0) {
167
+ node.parallelRoutes = parallelRoutes;
168
+ }
169
+ return node;
170
+ }
171
+ // ---------------------------------------------------------------------------
172
+ // import.meta.glob support (Next.js 16.3 Turbopack)
173
+ // ---------------------------------------------------------------------------
174
+ export async function globToTree(globResult, baseDir = "./app") {
175
+ const root = { segment: "" };
176
+ debug("veap:Router", `Starting with baseDir: ${baseDir}`);
177
+ for (const [filePath, loader] of Object.entries(globResult)) {
178
+ const match = filePath.match(/(?:^|\/)app\/(.+)$/);
179
+ if (!match) {
180
+ debug("veap:Router", `Skipping ${filePath} (does not contain /app/)`);
181
+ continue;
182
+ }
183
+ const relativePath = match[1];
184
+ const parts = relativePath.split("/");
185
+ const fileName = parts.pop() || "";
186
+ const segments = parts;
187
+ let current = root;
188
+ for (const segment of segments) {
189
+ if (segment.startsWith("@")) {
190
+ const slotName = segment.slice(1);
191
+ if (!current.parallelRoutes)
192
+ current.parallelRoutes = {};
193
+ if (!current.parallelRoutes[slotName]) {
194
+ current.parallelRoutes[slotName] = { segment: slotName };
195
+ }
196
+ current = current.parallelRoutes[slotName];
197
+ }
198
+ else {
199
+ if (!current.children)
200
+ current.children = [];
201
+ let child = current.children.find((c) => c.segment === segment);
202
+ if (!child) {
203
+ child = { segment };
204
+ current.children.push(child);
205
+ }
206
+ current = child;
207
+ }
208
+ }
209
+ const fileBase = fileName.replace(/\.[^/.]+$/, "");
210
+ let key;
211
+ if (fileBase === "page")
212
+ key = "page";
213
+ else if (fileBase === "layout")
214
+ key = "layout";
215
+ else if (fileBase === "loading")
216
+ key = "loading";
217
+ else if (fileBase === "error")
218
+ key = "error";
219
+ else if (fileBase === "not-found")
220
+ key = "notFound";
221
+ else if (fileBase === "default")
222
+ key = "default";
223
+ else if (fileBase === "route")
224
+ key = "route";
225
+ if (key) {
226
+ const mod = await loader();
227
+ const exported = mod.default ?? mod;
228
+ current[key] = exported;
229
+ if ((key === "page" || key === "layout") && mod.generateMetadata) {
230
+ current.generateMetadata = mod.generateMetadata;
231
+ }
232
+ if (key === "page" || key === "layout" || key === "route") {
233
+ if (mod.auth !== undefined)
234
+ current.auth = mod.auth;
235
+ if (mod.roles !== undefined)
236
+ current.roles = mod.roles;
237
+ if (mod.permissions !== undefined)
238
+ current.permissions = mod.permissions;
239
+ if (mod.middlewares !== undefined)
240
+ current.middlewares = mod.middlewares;
241
+ }
242
+ }
243
+ }
244
+ function sortTree(node) {
245
+ if (node.children) {
246
+ node.children.sort((a, b) => {
247
+ const orderA = segmentSortOrder(a.segment);
248
+ const orderB = segmentSortOrder(b.segment);
249
+ if (orderA !== orderB)
250
+ return orderA - orderB;
251
+ return a.segment.localeCompare(b.segment);
252
+ });
253
+ for (const child of node.children)
254
+ sortTree(child);
255
+ }
256
+ if (node.parallelRoutes) {
257
+ for (const child of Object.values(node.parallelRoutes))
258
+ sortTree(child);
259
+ }
260
+ }
261
+ sortTree(root);
262
+ return root;
263
+ }
264
+ export function generateRouteManifest(appDir, importPrefix) {
265
+ const imports = [];
266
+ const tree = scanDirectoryForManifest(appDir, "", importPrefix, imports, []);
267
+ const lines = [];
268
+ for (const entry of imports) {
269
+ lines.push(`import ${entry.name} from "${entry.path}";`);
270
+ }
271
+ if (imports.length > 0) {
272
+ lines.push("");
273
+ }
274
+ lines.push(`import type { RouteNode } from "@veap/plugins/router";`);
275
+ lines.push("");
276
+ lines.push(`export const routeTree: RouteNode = ${tree};`);
277
+ lines.push("");
278
+ return lines.join("\n");
279
+ }
280
+ function scanDirectoryForManifest(dir, segment, importPrefix, imports, pathParts) {
281
+ const props = [];
282
+ props.push(`segment: ${JSON.stringify(segment)}`);
283
+ const nameSuffix = pathParts.length === 0
284
+ ? "root"
285
+ : pathParts.map(sanitizeImportName).join("_");
286
+ for (const [key, candidates] of Object.entries(ROUTE_FILES)) {
287
+ const filePath = findRouteFile(dir, candidates);
288
+ if (filePath) {
289
+ const fileExt = path.extname(filePath);
290
+ const fileBase = path.basename(filePath, fileExt);
291
+ const relativeParts = [...pathParts, fileBase];
292
+ const importPath = [importPrefix, ...relativeParts].join("/");
293
+ const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
294
+ const importName = `${capitalizedKey}_${nameSuffix}`;
295
+ imports.push({ name: importName, path: importPath });
296
+ props.push(`${key}: ${importName}`);
297
+ }
298
+ }
299
+ let entries;
300
+ try {
301
+ entries = fs.readdirSync(dir, { withFileTypes: true });
302
+ }
303
+ catch {
304
+ return `{ ${props.join(", ")} }`;
305
+ }
306
+ const childDirs = entries
307
+ .filter((e) => e.isDirectory() && !shouldSkipDir(e.name))
308
+ .sort((a, b) => {
309
+ const orderA = segmentSortOrder(dirNameToSegment(a.name));
310
+ const orderB = segmentSortOrder(dirNameToSegment(b.name));
311
+ if (orderA !== orderB)
312
+ return orderA - orderB;
313
+ return a.name.localeCompare(b.name);
314
+ });
315
+ const childLiterals = [];
316
+ const parallelLiterals = [];
317
+ for (const childDir of childDirs) {
318
+ const childPath = path.join(dir, childDir.name);
319
+ if (childDir.name.startsWith("@")) {
320
+ const slotName = childDir.name.slice(1);
321
+ const slotLiteral = scanDirectoryForManifest(childPath, slotName, importPrefix, imports, [...pathParts, childDir.name]);
322
+ parallelLiterals.push(`${JSON.stringify(slotName)}: ${slotLiteral}`);
323
+ }
324
+ else {
325
+ const childSegment = dirNameToSegment(childDir.name);
326
+ const childLiteral = scanDirectoryForManifest(childPath, childSegment, importPrefix, imports, [...pathParts, childDir.name]);
327
+ childLiterals.push(childLiteral);
328
+ }
329
+ }
330
+ if (childLiterals.length > 0) {
331
+ props.push(`children: [\n ${childLiterals.join(",\n ")},\n ]`);
332
+ }
333
+ if (parallelLiterals.length > 0) {
334
+ props.push(`parallelRoutes: {\n ${parallelLiterals.join(",\n ")},\n }`);
335
+ }
336
+ return `{\n ${props.join(",\n ")},\n}`;
337
+ }
@@ -0,0 +1,10 @@
1
+ import type { UserPermission, UserRole } from "@veap/auth";
2
+ import type { ApiMiddleware, VeapMiddleware } from "../common/types";
3
+ import type { LayoutChainEntry, MatchResult } from "./route-tree";
4
+ export declare function collectMiddlewares(chain: LayoutChainEntry[], node: MatchResult["node"]): VeapMiddleware[];
5
+ export declare function collectApiMiddlewares(chain: LayoutChainEntry[], node: MatchResult["node"]): ApiMiddleware[];
6
+ export declare function collectAuthRequirements(chain: LayoutChainEntry[], node: MatchResult["node"]): {
7
+ needsAuth: boolean;
8
+ roles: UserRole[];
9
+ permissions: UserPermission[];
10
+ };
@@ -0,0 +1,48 @@
1
+ export function collectMiddlewares(chain, node) {
2
+ const all = [];
3
+ for (const entry of chain) {
4
+ if (entry.middlewares) {
5
+ all.push(...entry.middlewares);
6
+ }
7
+ }
8
+ if (node.middlewares) {
9
+ all.push(...node.middlewares);
10
+ }
11
+ return all;
12
+ }
13
+ export function collectApiMiddlewares(chain, node) {
14
+ const all = [];
15
+ for (const entry of chain) {
16
+ if (entry.middlewares) {
17
+ all.push(...entry.middlewares);
18
+ }
19
+ }
20
+ if (node.middlewares) {
21
+ all.push(...node.middlewares);
22
+ }
23
+ return all;
24
+ }
25
+ export function collectAuthRequirements(chain, node) {
26
+ let needsAuth = false;
27
+ const roles = [];
28
+ const permissions = [];
29
+ for (const entry of chain) {
30
+ if (entry.auth)
31
+ needsAuth = true;
32
+ if (entry.roles)
33
+ roles.push(...entry.roles);
34
+ if (entry.permissions)
35
+ permissions.push(...entry.permissions);
36
+ }
37
+ if (node.auth)
38
+ needsAuth = true;
39
+ if (node.roles)
40
+ roles.push(...node.roles);
41
+ if (node.permissions)
42
+ permissions.push(...node.permissions);
43
+ return {
44
+ needsAuth: needsAuth || roles.length > 0 || permissions.length > 0,
45
+ roles: [...new Set(roles)],
46
+ permissions: [...new Set(permissions)],
47
+ };
48
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @module @veap/plugins/router
3
+ * @description Virtual Router API integrated within the Veap Plugin system.
4
+ */
5
+ export * from "./router";
6
+ export * from "./router/index";
package/dist/router.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @module @veap/plugins/router
3
+ * @description Virtual Router API integrated within the Veap Plugin system.
4
+ */
5
+ export * from "./router";
6
+ export * from "./router/index";
@@ -0,0 +1,11 @@
1
+ export * from "./actions";
2
+ export { PluginManifestSchema as ModuleManifestSchema } from "./common/types";
3
+ export * from "./index";
4
+ export * from "./navigation";
5
+ export { getPathPrefix, getPluginBreadcrumbs as getBreadcrumbs, getPluginNavigation as getPublicNavigation, getVeapPluginNavigationGrouped as getVeapModuleNavigationGrouped, } from "./navigation";
6
+ export * from "./registry";
7
+ export { applyPluginFilters as applyFilters, getPluginConfig as getModuleConfig, getPluginStatus as getModule, getPluginStatus as getModuleStatus, getPluginsStatus as getModules, hasPluginExtension as hasExtension, hasPluginHooks as hasHooks, togglePluginState as toggleModuleState, updatePluginConfig as updateModuleConfig, } from "./registry";
8
+ export * from "./router";
9
+ export { ExtensionPoint, PluginExtensionPoint, } from "./widgets/plugin-extension-point";
10
+ export { PluginWidgetArea, WidgetArea, } from "./widgets/plugin-widget-area";
11
+ export { WidgetComposer } from "./widgets/widget-composer";
package/dist/server.js ADDED
@@ -0,0 +1,17 @@
1
+ // Server Navigation & Breadcrumbs
2
+ // Server Actions
3
+ export * from "./actions";
4
+ export { PluginManifestSchema as ModuleManifestSchema } from "./common/types";
5
+ export * from "./index";
6
+ export * from "./navigation";
7
+ export { getPathPrefix, getPluginBreadcrumbs as getBreadcrumbs, getPluginNavigation as getPublicNavigation, getVeapPluginNavigationGrouped as getVeapModuleNavigationGrouped, } from "./navigation";
8
+ // Core: Registry
9
+ export * from "./registry";
10
+ // Core: Compatibility Aliases
11
+ export { applyPluginFilters as applyFilters, getPluginConfig as getModuleConfig, getPluginStatus as getModule, getPluginStatus as getModuleStatus, getPluginsStatus as getModules, hasPluginExtension as hasExtension, hasPluginHooks as hasHooks, togglePluginState as toggleModuleState, updatePluginConfig as updateModuleConfig, } from "./registry";
12
+ // Router Server
13
+ export * from "./router";
14
+ // UI (Server versions)
15
+ export { ExtensionPoint, PluginExtensionPoint, } from "./widgets/plugin-extension-point";
16
+ export { PluginWidgetArea, WidgetArea, } from "./widgets/plugin-widget-area";
17
+ export { WidgetComposer } from "./widgets/widget-composer";
@@ -0,0 +1,8 @@
1
+ export { PathPrefixContext, usePathPrefix, } from "./path-prefix-context";
2
+ export { ExtensionPoint, PluginExtensionPoint, type PluginExtensionPointProps, } from "./plugin-extension-point";
3
+ export { ExtensionPointClient, PluginExtensionPointClient, type PluginExtensionPointClientProps, usePluginExtensions, } from "./plugin-extension-point-client";
4
+ export { PluginWidgetArea, WidgetArea, } from "./plugin-widget-area";
5
+ export { PluginWidgetAreaClient, usePluginWidgets, WidgetAreaClient, } from "./plugin-widget-area-client";
6
+ export { type ConfirmOptions, type ConfirmResult, useConfirmAction, } from "./use-confirm-action";
7
+ export { WidgetComposer } from "./widget-composer";
8
+ export { WidgetComposerClient } from "./widget-composer-client";
@@ -0,0 +1,8 @@
1
+ export { PathPrefixContext, usePathPrefix, } from "./path-prefix-context";
2
+ export { ExtensionPoint, PluginExtensionPoint, } from "./plugin-extension-point";
3
+ export { ExtensionPointClient, PluginExtensionPointClient, usePluginExtensions, } from "./plugin-extension-point-client";
4
+ export { PluginWidgetArea, WidgetArea, } from "./plugin-widget-area";
5
+ export { PluginWidgetAreaClient, usePluginWidgets, WidgetAreaClient, } from "./plugin-widget-area-client";
6
+ export { useConfirmAction, } from "./use-confirm-action";
7
+ export { WidgetComposer } from "./widget-composer";
8
+ export { WidgetComposerClient } from "./widget-composer-client";
@@ -0,0 +1,5 @@
1
+ export declare const PathPrefixContext: import("react").Context<string>;
2
+ /**
3
+ * Hook to get the current Veap path prefix on the client.
4
+ */
5
+ export declare function usePathPrefix(): string;
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import { createContext, useContext } from "react";
3
+ export const PathPrefixContext = createContext("/app");
4
+ /**
5
+ * Hook to get the current Veap path prefix on the client.
6
+ */
7
+ export function usePathPrefix() {
8
+ return useContext(PathPrefixContext);
9
+ }
@@ -0,0 +1,15 @@
1
+ import type React from "react";
2
+ export interface PluginExtensionPointClientProps {
3
+ target: string;
4
+ point: string;
5
+ mode?: "single" | "multiple";
6
+ className?: string;
7
+ props?: any;
8
+ fallback?: React.ReactNode;
9
+ children?: React.ReactNode;
10
+ includeDisabled?: boolean;
11
+ as?: React.ElementType;
12
+ }
13
+ export declare function usePluginExtensions(target: string, point: string, includeDisabled?: boolean): any[];
14
+ export declare function PluginExtensionPointClient({ target, point, mode, className, props, fallback, children, includeDisabled, as: Container, }: PluginExtensionPointClientProps): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
15
+ export { PluginExtensionPointClient as ExtensionPointClient };
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useState } from "react";
4
+ import { getPluginExtensionsAction } from "../actions/index";
5
+ import { onPluginsChanged } from "../common/events";
6
+ export function usePluginExtensions(target, point, includeDisabled = false) {
7
+ const [extensions, setExtensions] = useState([]);
8
+ const fetchExtensions = useCallback(() => {
9
+ getPluginExtensionsAction(target, point, includeDisabled).then(setExtensions);
10
+ }, [target, point, includeDisabled]);
11
+ useEffect(() => {
12
+ fetchExtensions();
13
+ const unsubscribe = onPluginsChanged(() => {
14
+ fetchExtensions();
15
+ });
16
+ return unsubscribe;
17
+ }, [fetchExtensions]);
18
+ return extensions;
19
+ }
20
+ export function PluginExtensionPointClient({ target, point, mode = "multiple", className, props, fallback, children, includeDisabled = false, as: Container, }) {
21
+ const extensions = usePluginExtensions(target, point, includeDisabled);
22
+ const fallbackContent = (children ?? fallback ?? null);
23
+ if (extensions.length === 0)
24
+ return fallbackContent;
25
+ if (mode === "single") {
26
+ const ext = extensions[0];
27
+ const Component = ext?.component;
28
+ if (!Component)
29
+ return fallbackContent;
30
+ if (Container || className) {
31
+ const Wrapper = Container || "div";
32
+ return (_jsx(Wrapper, { className: className, children: _jsx(Component, { ...props }, ext.id) }));
33
+ }
34
+ return _jsx(Component, { ...props }, ext.id);
35
+ }
36
+ const Wrapper = Container || "div";
37
+ return (_jsx(Wrapper, { className: className, children: extensions.map((ext) => {
38
+ const Component = ext.component;
39
+ if (!Component)
40
+ return null;
41
+ return _jsx(Component, { ...props }, ext.id);
42
+ }) }));
43
+ }
44
+ export { PluginExtensionPointClient as ExtensionPointClient };