@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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -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 +4 -0
  30. package/dist/common/index.js +4 -0
  31. package/dist/common/tokens.d.ts +14 -0
  32. package/dist/common/tokens.js +12 -0
  33. package/dist/common/types.d.ts +202 -0
  34. package/dist/common/types.js +39 -0
  35. package/dist/composition.d.ts +15 -0
  36. package/dist/composition.js +20 -0
  37. package/dist/index.d.ts +6 -0
  38. package/dist/index.js +6 -0
  39. package/dist/migrations/0001_create_plugins_tables.d.ts +4 -0
  40. package/dist/migrations/0001_create_plugins_tables.js +29 -0
  41. package/dist/migrations/index.d.ts +4 -0
  42. package/dist/migrations/index.js +7 -0
  43. package/dist/models/SystemPlugin.d.ts +19 -0
  44. package/dist/models/SystemPlugin.js +14 -0
  45. package/dist/models/SystemUserWidget.d.ts +19 -0
  46. package/dist/models/SystemUserWidget.js +17 -0
  47. package/dist/models/index.d.ts +2 -0
  48. package/dist/models/index.js +2 -0
  49. package/dist/models.d.ts +1 -0
  50. package/dist/models.js +1 -0
  51. package/dist/navigation/breadcrumbs.d.ts +2 -0
  52. package/dist/navigation/breadcrumbs.js +84 -0
  53. package/dist/navigation/index.d.ts +2 -0
  54. package/dist/navigation/index.js +2 -0
  55. package/dist/navigation/navigation.service.d.ts +27 -0
  56. package/dist/navigation/navigation.service.js +183 -0
  57. package/dist/providers/index.d.ts +1 -0
  58. package/dist/providers/index.js +1 -0
  59. package/dist/providers/plugins.provider.d.ts +20 -0
  60. package/dist/providers/plugins.provider.js +143 -0
  61. package/dist/registry/contracts.d.ts +72 -0
  62. package/dist/registry/contracts.js +4 -0
  63. package/dist/registry/facade.d.ts +45 -0
  64. package/dist/registry/facade.js +49 -0
  65. package/dist/registry/index.d.ts +5 -0
  66. package/dist/registry/index.js +5 -0
  67. package/dist/registry/migration-runner.d.ts +8 -0
  68. package/dist/registry/migration-runner.js +12 -0
  69. package/dist/registry/plugin.repository.d.ts +19 -0
  70. package/dist/registry/plugin.repository.js +74 -0
  71. package/dist/registry/registry.service.d.ts +69 -0
  72. package/dist/registry/registry.service.js +535 -0
  73. package/dist/router/discovery.d.ts +27 -0
  74. package/dist/router/discovery.js +37 -0
  75. package/dist/router/handler.d.ts +6 -0
  76. package/dist/router/handler.js +40 -0
  77. package/dist/router/index.d.ts +14 -0
  78. package/dist/router/index.js +14 -0
  79. package/dist/router/matcher.d.ts +5 -0
  80. package/dist/router/matcher.js +32 -0
  81. package/dist/router/middlewares.d.ts +25 -0
  82. package/dist/router/middlewares.js +119 -0
  83. package/dist/router/react/error-boundary.d.ts +30 -0
  84. package/dist/router/react/error-boundary.js +68 -0
  85. package/dist/router/react/index.d.ts +4 -0
  86. package/dist/router/react/index.js +4 -0
  87. package/dist/router/react/renderer.d.ts +13 -0
  88. package/dist/router/react/renderer.js +159 -0
  89. package/dist/router/react/soft-navigation.d.ts +4 -0
  90. package/dist/router/react/soft-navigation.js +34 -0
  91. package/dist/router/react/with-router.d.ts +22 -0
  92. package/dist/router/react/with-router.js +19 -0
  93. package/dist/router/route-tree.d.ts +177 -0
  94. package/dist/router/route-tree.js +639 -0
  95. package/dist/router/router.service.d.ts +25 -0
  96. package/dist/router/router.service.js +127 -0
  97. package/dist/router/scanner.d.ts +4 -0
  98. package/dist/router/scanner.js +337 -0
  99. package/dist/router/utils.d.ts +10 -0
  100. package/dist/router/utils.js +48 -0
  101. package/dist/router.d.ts +6 -0
  102. package/dist/router.js +6 -0
  103. package/dist/server.d.ts +11 -0
  104. package/dist/server.js +17 -0
  105. package/dist/widgets/index.d.ts +8 -0
  106. package/dist/widgets/index.js +8 -0
  107. package/dist/widgets/path-prefix-context.d.ts +5 -0
  108. package/dist/widgets/path-prefix-context.js +9 -0
  109. package/dist/widgets/plugin-extension-point-client.d.ts +15 -0
  110. package/dist/widgets/plugin-extension-point-client.js +44 -0
  111. package/dist/widgets/plugin-extension-point.d.ts +14 -0
  112. package/dist/widgets/plugin-extension-point.js +51 -0
  113. package/dist/widgets/plugin-widget-area-client.d.ts +11 -0
  114. package/dist/widgets/plugin-widget-area-client.js +31 -0
  115. package/dist/widgets/plugin-widget-area.d.ts +10 -0
  116. package/dist/widgets/plugin-widget-area.js +19 -0
  117. package/dist/widgets/use-confirm-action.d.ts +19 -0
  118. package/dist/widgets/use-confirm-action.js +68 -0
  119. package/dist/widgets/widget-composer-client.d.ts +21 -0
  120. package/dist/widgets/widget-composer-client.js +281 -0
  121. package/dist/widgets/widget-composer.d.ts +8 -0
  122. package/dist/widgets/widget-composer.js +28 -0
  123. package/package.json +115 -4
@@ -0,0 +1,639 @@
1
+ /**
2
+ * @module route-tree
3
+ * @description Core route tree engine for the Veap unstable custom router.
4
+ *
5
+ * Implements a tree-based routing structure that mirrors Next.js App Router
6
+ * file conventions. Supports static, dynamic, catch-all, optional catch-all,
7
+ * route group, and parallel route segments.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const tree = new RouteTree();
12
+ * tree.addTree({
13
+ * segment: "",
14
+ * layout: RootLayout,
15
+ * children: [
16
+ * { segment: "blog", page: BlogPage, children: [
17
+ * { segment: "[slug]", page: BlogPostPage },
18
+ * ]},
19
+ * ],
20
+ * });
21
+ *
22
+ * const result = tree.match("/blog/hello-world");
23
+ * // result.params => { slug: "hello-world" }
24
+ * ```
25
+ */
26
+ // ─────────────────────────────────────────────────────────────────────────────
27
+ // Segment classification helpers
28
+ // ─────────────────────────────────────────────────────────────────────────────
29
+ export function isGroupSegment(segment) {
30
+ return /^\(.+\)$/.test(segment);
31
+ }
32
+ export function isDynamicSegment(segment) {
33
+ return /^\[[^[\].]+\]$/.test(segment);
34
+ }
35
+ export function isCatchAllSegment(segment) {
36
+ return /^\[\.\.\..+\]$/.test(segment);
37
+ }
38
+ export function isOptionalCatchAllSegment(segment) {
39
+ return /^\[\[\.\.\..+\]\]$/.test(segment);
40
+ }
41
+ export function isParallelSlot(segment) {
42
+ return /^@.+$/.test(segment);
43
+ }
44
+ export function extractParamName(segment) {
45
+ if (isOptionalCatchAllSegment(segment)) {
46
+ return segment.slice(5, -2);
47
+ }
48
+ if (isCatchAllSegment(segment)) {
49
+ return segment.slice(4, -1);
50
+ }
51
+ if (isDynamicSegment(segment)) {
52
+ return segment.slice(1, -1);
53
+ }
54
+ return segment;
55
+ }
56
+ export function segmentMatchesUrlPart(segment, urlPart) {
57
+ if (isGroupSegment(segment)) {
58
+ return false;
59
+ }
60
+ if (isDynamicSegment(segment)) {
61
+ return urlPart.length > 0;
62
+ }
63
+ if (isCatchAllSegment(segment) || isOptionalCatchAllSegment(segment)) {
64
+ return false;
65
+ }
66
+ return segment === urlPart;
67
+ }
68
+ // ─────────────────────────────────────────────────────────────────────────────
69
+ // Internal helpers
70
+ // ─────────────────────────────────────────────────────────────────────────────
71
+ function createRootNode() {
72
+ return { segment: "", children: [] };
73
+ }
74
+ function hasLayoutChainContent(node) {
75
+ return !!(node.layout ||
76
+ node.loading ||
77
+ node.error ||
78
+ node.notFound ||
79
+ (node.middlewares && node.middlewares.length > 0) ||
80
+ node.auth !== undefined ||
81
+ (node.roles && node.roles.length > 0) ||
82
+ (node.permissions && node.permissions.length > 0) ||
83
+ (node.parallelRoutes && Object.keys(node.parallelRoutes).length > 0) ||
84
+ isGroupSegment(node.segment));
85
+ }
86
+ function buildChainEntry(node, consumedSegments) {
87
+ const entry = {};
88
+ if (node.layout)
89
+ entry.layout = node.layout;
90
+ if (node.loading)
91
+ entry.loading = node.loading;
92
+ if (node.error)
93
+ entry.error = node.error;
94
+ if (node.notFound)
95
+ entry.notFound = node.notFound;
96
+ if (node.middlewares?.length)
97
+ entry.middlewares = node.middlewares;
98
+ if (node.id !== undefined)
99
+ entry.id = node.id;
100
+ if (node.auth !== undefined)
101
+ entry.auth = node.auth;
102
+ if (node.roles?.length)
103
+ entry.roles = node.roles;
104
+ if (node.permissions?.length)
105
+ entry.permissions = node.permissions;
106
+ if (node.parallelRoutes && Object.keys(node.parallelRoutes).length > 0) {
107
+ entry.parallelSlots = node.parallelRoutes;
108
+ }
109
+ entry.consumedPath = consumedSegments.join("/");
110
+ return entry;
111
+ }
112
+ function splitPath(path) {
113
+ return path.split("/").filter((s) => s.length > 0);
114
+ }
115
+ function ensureChild(parent, segment) {
116
+ if (!parent.children) {
117
+ parent.children = [];
118
+ }
119
+ const existing = parent.children.find((c) => c.segment === segment);
120
+ if (existing) {
121
+ return existing;
122
+ }
123
+ const child = { segment, children: [] };
124
+ parent.children.push(child);
125
+ return child;
126
+ }
127
+ function matchRecursive(node, remainingSegments, currentParams, currentChain, currentMatched, bestPartialMatch) {
128
+ if (bestPartialMatch && currentMatched.length > bestPartialMatch.depth) {
129
+ bestPartialMatch.depth = currentMatched.length;
130
+ bestPartialMatch.result = {
131
+ node,
132
+ params: { ...currentParams },
133
+ layoutChain: [...currentChain],
134
+ matchedSegments: [...currentMatched],
135
+ };
136
+ }
137
+ const children = node.children ?? [];
138
+ if (remainingSegments.length === 0) {
139
+ if (node.page || node.route) {
140
+ return {
141
+ node,
142
+ params: { ...currentParams },
143
+ layoutChain: [...currentChain],
144
+ matchedSegments: [...currentMatched],
145
+ };
146
+ }
147
+ for (const child of children) {
148
+ if (child.segment === "" && (child.page || child.route)) {
149
+ const entry = buildChainEntry(child, currentMatched);
150
+ return {
151
+ node: child,
152
+ params: { ...currentParams },
153
+ layoutChain: [...currentChain, entry],
154
+ matchedSegments: [...currentMatched],
155
+ };
156
+ }
157
+ }
158
+ for (const child of children) {
159
+ if (isGroupSegment(child.segment)) {
160
+ const groupEntry = buildChainEntry(child, currentMatched);
161
+ const groupResult = matchRecursive(child, [], currentParams, [...currentChain, groupEntry], currentMatched, bestPartialMatch);
162
+ if (groupResult)
163
+ return groupResult;
164
+ }
165
+ }
166
+ for (const child of children) {
167
+ if (isOptionalCatchAllSegment(child.segment) &&
168
+ (child.page || child.route)) {
169
+ const paramName = extractParamName(child.segment);
170
+ const entry = buildChainEntry(child, currentMatched);
171
+ return {
172
+ node: child,
173
+ params: { ...currentParams, [paramName]: "" },
174
+ layoutChain: [...currentChain, entry],
175
+ matchedSegments: [...currentMatched],
176
+ };
177
+ }
178
+ }
179
+ return null;
180
+ }
181
+ const [currentSegment, ...rest] = remainingSegments;
182
+ for (const child of children) {
183
+ if (!isGroupSegment(child.segment) &&
184
+ !isDynamicSegment(child.segment) &&
185
+ !isCatchAllSegment(child.segment) &&
186
+ !isOptionalCatchAllSegment(child.segment) &&
187
+ child.segment === currentSegment) {
188
+ const nextChain = hasLayoutChainContent(child)
189
+ ? [
190
+ ...currentChain,
191
+ buildChainEntry(child, [...currentMatched, currentSegment]),
192
+ ]
193
+ : currentChain;
194
+ const result = matchRecursive(child, rest, currentParams, nextChain, [...currentMatched, currentSegment], bestPartialMatch);
195
+ if (result)
196
+ return result;
197
+ }
198
+ }
199
+ for (const child of children) {
200
+ if (isGroupSegment(child.segment)) {
201
+ const groupEntry = buildChainEntry(child, currentMatched);
202
+ const result = matchRecursive(child, remainingSegments, currentParams, [...currentChain, groupEntry], currentMatched, bestPartialMatch);
203
+ if (result)
204
+ return result;
205
+ }
206
+ }
207
+ for (const child of children) {
208
+ if (isDynamicSegment(child.segment)) {
209
+ const paramName = extractParamName(child.segment);
210
+ const nextChain = hasLayoutChainContent(child)
211
+ ? [
212
+ ...currentChain,
213
+ buildChainEntry(child, [...currentMatched, currentSegment]),
214
+ ]
215
+ : currentChain;
216
+ const result = matchRecursive(child, rest, { ...currentParams, [paramName]: currentSegment }, nextChain, [...currentMatched, currentSegment], bestPartialMatch);
217
+ if (result)
218
+ return result;
219
+ }
220
+ }
221
+ for (const child of children) {
222
+ if (isCatchAllSegment(child.segment) && (child.page || child.route)) {
223
+ const paramName = extractParamName(child.segment);
224
+ const caughtSegments = remainingSegments;
225
+ const entry = buildChainEntry(child, [
226
+ ...currentMatched,
227
+ ...caughtSegments,
228
+ ]);
229
+ return {
230
+ node: child,
231
+ params: {
232
+ ...currentParams,
233
+ [paramName]: caughtSegments.join("/"),
234
+ },
235
+ layoutChain: [...currentChain, entry],
236
+ matchedSegments: [...currentMatched, ...caughtSegments],
237
+ };
238
+ }
239
+ }
240
+ for (const child of children) {
241
+ if (isOptionalCatchAllSegment(child.segment) &&
242
+ (child.page || child.route)) {
243
+ const paramName = extractParamName(child.segment);
244
+ const caughtSegments = remainingSegments;
245
+ const entry = buildChainEntry(child, [
246
+ ...currentMatched,
247
+ ...caughtSegments,
248
+ ]);
249
+ return {
250
+ node: child,
251
+ params: {
252
+ ...currentParams,
253
+ [paramName]: caughtSegments.join("/"),
254
+ },
255
+ layoutChain: [...currentChain, entry],
256
+ matchedSegments: [...currentMatched, ...caughtSegments],
257
+ };
258
+ }
259
+ }
260
+ return null;
261
+ }
262
+ // ─────────────────────────────────────────────────────────────────────────────
263
+ // RouteTree class
264
+ // ─────────────────────────────────────────────────────────────────────────────
265
+ function cloneNode(node) {
266
+ const clone = { ...node };
267
+ if (node.children) {
268
+ clone.children = node.children.map(cloneNode);
269
+ }
270
+ if (node.parallelRoutes) {
271
+ clone.parallelRoutes = {};
272
+ for (const [slot, slotTree] of Object.entries(node.parallelRoutes)) {
273
+ clone.parallelRoutes[slot] = cloneNode(slotTree);
274
+ }
275
+ }
276
+ return clone;
277
+ }
278
+ export function resolveMagicPrefix(node, prefixPath) {
279
+ const clone = { ...node };
280
+ if (clone.children) {
281
+ clone.children = clone.children.flatMap((child) => {
282
+ if (child.segment === "[prefix]") {
283
+ const segments = prefixPath.split("/").filter(Boolean);
284
+ const resolvedChild = resolveMagicPrefix(child, prefixPath);
285
+ if (segments.length === 0) {
286
+ resolvedChild.segment = "(prefix)";
287
+ return [resolvedChild];
288
+ }
289
+ else {
290
+ resolvedChild.segment = segments[segments.length - 1];
291
+ let current = resolvedChild;
292
+ for (let i = segments.length - 2; i >= 0; i--) {
293
+ current = { segment: segments[i], children: [current] };
294
+ }
295
+ return [current];
296
+ }
297
+ }
298
+ else {
299
+ return [resolveMagicPrefix(child, prefixPath)];
300
+ }
301
+ });
302
+ }
303
+ if (clone.parallelRoutes) {
304
+ clone.parallelRoutes = {};
305
+ for (const [slot, slotTree] of Object.entries(node.parallelRoutes)) {
306
+ clone.parallelRoutes[slot] = resolveMagicPrefix(slotTree, prefixPath);
307
+ }
308
+ }
309
+ return clone;
310
+ }
311
+ export class RouteTree {
312
+ root;
313
+ rewrites = new Map();
314
+ constructor(initialRoot, initialRewrites) {
315
+ this.root = initialRoot ? cloneNode(initialRoot) : createRootNode();
316
+ if (initialRewrites) {
317
+ if (initialRewrites instanceof Map) {
318
+ for (const [from, to] of initialRewrites) {
319
+ this.addRewrite(from, to);
320
+ }
321
+ }
322
+ else {
323
+ for (const [from, to] of Object.entries(initialRewrites)) {
324
+ this.addRewrite(from, to);
325
+ }
326
+ }
327
+ }
328
+ }
329
+ normalizeRewritePath(p) {
330
+ let norm = p.trim();
331
+ if (!norm.startsWith("/"))
332
+ norm = "/" + norm;
333
+ if (norm.length > 1 && norm.endsWith("/"))
334
+ norm = norm.slice(0, -1);
335
+ return norm;
336
+ }
337
+ addRewrite(from, to) {
338
+ this.rewrites.set(this.normalizeRewritePath(from), this.normalizeRewritePath(to));
339
+ return this;
340
+ }
341
+ setRewrites(rewrites) {
342
+ if (rewrites instanceof Map) {
343
+ for (const [from, to] of rewrites) {
344
+ this.addRewrite(from, to);
345
+ }
346
+ }
347
+ else {
348
+ for (const [from, to] of Object.entries(rewrites)) {
349
+ this.addRewrite(from, to);
350
+ }
351
+ }
352
+ return this;
353
+ }
354
+ getRewrites() {
355
+ return new Map(this.rewrites);
356
+ }
357
+ resolveRewrite(path) {
358
+ const norm = this.normalizeRewritePath(path);
359
+ const exact = this.rewrites.get(norm);
360
+ if (exact)
361
+ return exact;
362
+ for (const [from, to] of this.rewrites.entries()) {
363
+ const match = this.matchRewritePattern(from, norm);
364
+ if (match) {
365
+ return this.applyRewritePattern(to, match);
366
+ }
367
+ }
368
+ return path;
369
+ }
370
+ matchRewritePattern(fromPattern, path) {
371
+ if (!fromPattern.includes(":") &&
372
+ !fromPattern.includes("[") &&
373
+ !fromPattern.includes("*")) {
374
+ return null;
375
+ }
376
+ const fromParts = fromPattern.split("/").filter(Boolean);
377
+ const pathParts = path.split("/").filter(Boolean);
378
+ const params = {};
379
+ for (let i = 0; i < fromParts.length; i++) {
380
+ const fromPart = fromParts[i];
381
+ if (fromPart === "*" ||
382
+ fromPart.startsWith(":path*") ||
383
+ fromPart.startsWith("[...")) {
384
+ const remaining = pathParts.slice(i).join("/");
385
+ params["path"] = remaining;
386
+ params["*"] = remaining;
387
+ return params;
388
+ }
389
+ if (i >= pathParts.length) {
390
+ return null;
391
+ }
392
+ const pathPart = pathParts[i];
393
+ if (fromPart.startsWith(":") && !fromPart.endsWith("*")) {
394
+ params[fromPart.slice(1)] = pathPart;
395
+ }
396
+ else if (fromPart.startsWith("[") &&
397
+ fromPart.endsWith("]") &&
398
+ !fromPart.startsWith("[...")) {
399
+ params[fromPart.slice(1, -1)] = pathPart;
400
+ }
401
+ else if (fromPart !== pathPart) {
402
+ return null;
403
+ }
404
+ }
405
+ if (pathParts.length !== fromParts.length) {
406
+ return null;
407
+ }
408
+ return params;
409
+ }
410
+ applyRewritePattern(toPattern, params) {
411
+ let result = toPattern;
412
+ for (const [key, value] of Object.entries(params)) {
413
+ if (key === "*" || key === "path")
414
+ continue;
415
+ result = result.replaceAll(`:${key}`, value);
416
+ result = result.replaceAll(`[${key}]`, value);
417
+ result = result.replaceAll(`[...${key}]`, value);
418
+ result = result.replaceAll(`[[...${key}]]`, value);
419
+ }
420
+ if (params["path"] !== undefined) {
421
+ result = result.replaceAll(":path*", params["path"]);
422
+ result = result.replaceAll("[...path]", params["path"]);
423
+ result = result.replaceAll("*", params["path"]);
424
+ }
425
+ return this.normalizeRewritePath(result);
426
+ }
427
+ getRoot() {
428
+ return this.root;
429
+ }
430
+ addTree(source, prefix) {
431
+ if (source instanceof RouteTree) {
432
+ for (const [from, to] of source.getRewrites()) {
433
+ const fullFrom = prefix && prefix !== "/" ? `${prefix}${from}` : from;
434
+ const fullTo = prefix && prefix !== "/" ? `${prefix}${to}` : to;
435
+ this.addRewrite(fullFrom, fullTo);
436
+ }
437
+ }
438
+ const treeNode = source instanceof RouteTree ? source.getRoot() : source;
439
+ if (!prefix || prefix === "/") {
440
+ this.mergeNode(this.root, treeNode);
441
+ return;
442
+ }
443
+ const segments = splitPath(prefix);
444
+ let current = this.root;
445
+ for (const seg of segments) {
446
+ current = ensureChild(current, seg);
447
+ }
448
+ this.mergeNode(current, treeNode);
449
+ }
450
+ mergeNode(target, source) {
451
+ if (source.page)
452
+ target.page = source.page;
453
+ if (source.layout)
454
+ target.layout = source.layout;
455
+ if (source.loading)
456
+ target.loading = source.loading;
457
+ if (source.error)
458
+ target.error = source.error;
459
+ if (source.notFound)
460
+ target.notFound = source.notFound;
461
+ if (source.default)
462
+ target.default = source.default;
463
+ if (source.route)
464
+ target.route = source.route;
465
+ if (source.generateMetadata)
466
+ target.generateMetadata = source.generateMetadata;
467
+ if (source.breadcrumb !== undefined)
468
+ target.breadcrumb = source.breadcrumb;
469
+ if (source.id !== undefined)
470
+ target.id = source.id;
471
+ if (source.auth !== undefined)
472
+ target.auth = source.auth;
473
+ if (source.middlewares?.length) {
474
+ target.middlewares = [
475
+ ...(target.middlewares ?? []),
476
+ ...source.middlewares,
477
+ ];
478
+ }
479
+ if (source.roles?.length) {
480
+ const existing = new Set(target.roles ?? []);
481
+ for (const role of source.roles)
482
+ existing.add(role);
483
+ target.roles = [...existing];
484
+ }
485
+ if (source.permissions?.length) {
486
+ const existing = new Set(target.permissions ?? []);
487
+ for (const perm of source.permissions)
488
+ existing.add(perm);
489
+ target.permissions = [...existing];
490
+ }
491
+ if (source.children?.length) {
492
+ if (!target.children)
493
+ target.children = [];
494
+ for (const sourceChild of source.children) {
495
+ const existingChild = target.children.find((c) => c.segment === sourceChild.segment);
496
+ if (existingChild) {
497
+ this.mergeNode(existingChild, sourceChild);
498
+ }
499
+ else {
500
+ target.children.push(cloneNode(sourceChild));
501
+ }
502
+ }
503
+ }
504
+ if (source.parallelRoutes) {
505
+ if (!target.parallelRoutes)
506
+ target.parallelRoutes = {};
507
+ for (const [slotName, slotTree] of Object.entries(source.parallelRoutes)) {
508
+ if (target.parallelRoutes[slotName]) {
509
+ this.mergeNode(target.parallelRoutes[slotName], slotTree);
510
+ }
511
+ else {
512
+ target.parallelRoutes[slotName] = cloneNode(slotTree);
513
+ }
514
+ }
515
+ }
516
+ }
517
+ match(path) {
518
+ const resolvedPath = this.resolveRewrite(path);
519
+ const segments = splitPath(resolvedPath);
520
+ const rootEntry = buildChainEntry(this.root, []);
521
+ const bestPartialMatch = {
522
+ depth: -1,
523
+ result: null,
524
+ };
525
+ const result = matchRecursive(this.root, segments, {}, [rootEntry], [], bestPartialMatch);
526
+ if (result) {
527
+ return {
528
+ node: result.node,
529
+ params: result.params,
530
+ layoutChain: result.layoutChain,
531
+ matchedSegments: result.matchedSegments,
532
+ isExact: true,
533
+ };
534
+ }
535
+ if (bestPartialMatch.result && bestPartialMatch.depth > 0) {
536
+ return {
537
+ node: bestPartialMatch.result.node,
538
+ params: bestPartialMatch.result.params,
539
+ layoutChain: bestPartialMatch.result.layoutChain,
540
+ matchedSegments: bestPartialMatch.result.matchedSegments,
541
+ isExact: false,
542
+ };
543
+ }
544
+ return null;
545
+ }
546
+ async generateMetadata(path, searchParams = {}) {
547
+ const resolvedPath = this.resolveRewrite(path);
548
+ const matchResult = this.match(resolvedPath);
549
+ if (!matchResult)
550
+ return {};
551
+ const { params, node } = matchResult;
552
+ let mergedMetadata = {};
553
+ const generators = this.collectMetadataGenerators(resolvedPath);
554
+ for (const generator of generators) {
555
+ const metadata = await generator({ params, searchParams });
556
+ mergedMetadata = { ...mergedMetadata, ...metadata };
557
+ }
558
+ if (node.generateMetadata) {
559
+ const leafMetadata = await node.generateMetadata({
560
+ params,
561
+ searchParams,
562
+ });
563
+ mergedMetadata = { ...mergedMetadata, ...leafMetadata };
564
+ }
565
+ return mergedMetadata;
566
+ }
567
+ collectMetadataGenerators(path) {
568
+ const segments = splitPath(path);
569
+ const generators = [];
570
+ if (this.root.generateMetadata) {
571
+ generators.push(this.root.generateMetadata);
572
+ }
573
+ this.collectGeneratorsRecursive(this.root, segments, generators);
574
+ return generators;
575
+ }
576
+ collectGeneratorsRecursive(node, remainingSegments, generators) {
577
+ const children = node.children ?? [];
578
+ if (remainingSegments.length === 0) {
579
+ for (const child of children) {
580
+ if (child.segment === "" && child.page) {
581
+ if (child.generateMetadata)
582
+ generators.push(child.generateMetadata);
583
+ return true;
584
+ }
585
+ }
586
+ return !!node.page;
587
+ }
588
+ const [currentSegment, ...rest] = remainingSegments;
589
+ for (const child of children) {
590
+ if (!isGroupSegment(child.segment) &&
591
+ !isDynamicSegment(child.segment) &&
592
+ !isCatchAllSegment(child.segment) &&
593
+ !isOptionalCatchAllSegment(child.segment) &&
594
+ child.segment === currentSegment) {
595
+ if (child.generateMetadata)
596
+ generators.push(child.generateMetadata);
597
+ if (this.collectGeneratorsRecursive(child, rest, generators))
598
+ return true;
599
+ if (child.generateMetadata)
600
+ generators.pop();
601
+ }
602
+ }
603
+ for (const child of children) {
604
+ if (isGroupSegment(child.segment)) {
605
+ if (child.generateMetadata)
606
+ generators.push(child.generateMetadata);
607
+ if (this.collectGeneratorsRecursive(child, remainingSegments, generators))
608
+ return true;
609
+ if (child.generateMetadata)
610
+ generators.pop();
611
+ }
612
+ }
613
+ for (const child of children) {
614
+ if (isDynamicSegment(child.segment)) {
615
+ if (child.generateMetadata)
616
+ generators.push(child.generateMetadata);
617
+ if (this.collectGeneratorsRecursive(child, rest, generators))
618
+ return true;
619
+ if (child.generateMetadata)
620
+ generators.pop();
621
+ }
622
+ }
623
+ for (const child of children) {
624
+ if (isCatchAllSegment(child.segment) && child.page) {
625
+ if (child.generateMetadata)
626
+ generators.push(child.generateMetadata);
627
+ return true;
628
+ }
629
+ }
630
+ for (const child of children) {
631
+ if (isOptionalCatchAllSegment(child.segment) && child.page) {
632
+ if (child.generateMetadata)
633
+ generators.push(child.generateMetadata);
634
+ return true;
635
+ }
636
+ }
637
+ return false;
638
+ }
639
+ }
@@ -0,0 +1,25 @@
1
+ import { type ICacheProvider, type IConfigService } from "@veap/kernel";
2
+ import type * as React from "react";
3
+ import { NavigationService } from "../navigation/navigation.service";
4
+ import { PluginRegistry } from "../registry";
5
+ import { RouteTree } from "./route-tree";
6
+ export declare class RouterService {
7
+ private cache;
8
+ private config;
9
+ private pluginRegistry;
10
+ private navigation;
11
+ private rewrites;
12
+ private siteLayout?;
13
+ constructor(cache: ICacheProvider, config: IConfigService, pluginRegistry: PluginRegistry, navigation: NavigationService);
14
+ setSiteLayout(layout: React.ComponentType<any>): void;
15
+ getSiteLayout(): React.ComponentType<any> | undefined;
16
+ addRewrite(from: string, to: string): void;
17
+ getRewrites(): Map<string, string>;
18
+ clearCache(): Promise<void>;
19
+ getPluginsWithHomepage(): Promise<{
20
+ id: string;
21
+ name: string;
22
+ }[]>;
23
+ private _buildRouteTree;
24
+ buildRouteTree(includePrivate?: boolean): Promise<RouteTree>;
25
+ }