@jskit-ai/kernel 0.1.244 → 0.1.246

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 (101) hide show
  1. package/client/appConfig.js +2 -1
  2. package/client/asyncModuleRecovery.js +2 -4
  3. package/client/index.d.ts +18 -76
  4. package/client/index.js +7 -2
  5. package/client/logging.js +8 -2
  6. package/client/moduleBootstrap.js +41 -33
  7. package/client/moduleBootstrap.test.js +35 -0
  8. package/client/shellBootstrap.js +38 -10
  9. package/client/shellBootstrap.test.js +85 -6
  10. package/client/shellRouting.js +16 -61
  11. package/client/shellRouting.test.js +30 -1
  12. package/client/vite/clientBootstrapPlugin.js +0 -6
  13. package/client/vite/clientBootstrapPlugin.test.js +2 -3
  14. package/internal/node/installedPackages.js +14 -58
  15. package/package.json +2 -4
  16. package/server/actions/actionCatalogue.js +13 -34
  17. package/{shared → server}/actions/actionContributorHelpers.js +0 -5
  18. package/{shared → server}/actions/actionDefinitions.js +24 -131
  19. package/{shared → server}/actions/actionDefinitions.test.js +75 -21
  20. package/server/actions/actionEvents.js +97 -2
  21. package/server/actions/actionEvents.test.js +79 -0
  22. package/{shared → server}/actions/executionContext.js +3 -13
  23. package/server/actions/idempotency.js +24 -0
  24. package/server/actions/pipeline.js +87 -0
  25. package/{shared → server}/actions/policies.js +20 -33
  26. package/{shared → server}/actions/policies.test.js +6 -6
  27. package/{shared → server}/actions/registry.js +3 -22
  28. package/{shared → server}/actions/registry.test.js +70 -32
  29. package/server/actions/requestMeta.js +17 -0
  30. package/server/http/capabilityHttpRuntime.js +1 -1
  31. package/server/http/capabilityHttpRuntime.test.js +56 -0
  32. package/server/http/errorHandling.js +291 -0
  33. package/server/{runtime/fastifyBootstrap.test.js → http/errorHandling.test.js} +72 -120
  34. package/server/http/lib/routeRegistration.js +36 -150
  35. package/server/http/lib/routeTransport.js +2 -0
  36. package/server/http/lib/routeValidator.js +5 -47
  37. package/server/http/lib/routeValidator.test.js +15 -17
  38. package/server/http/lib/router.js +10 -94
  39. package/server/platform/providerRuntime/capabilityProviderLoader.js +2 -1
  40. package/server/platform/providerRuntime/helpers.js +1 -34
  41. package/server/runtime/integers.js +1 -5
  42. package/server/runtime/serviceAuthorization.js +4 -9
  43. package/server/support/appConfig.js +2 -1
  44. package/server/support/appConfigFiles.js +1 -38
  45. package/server/support/appConfigFiles.test.js +3 -39
  46. package/server/support/index.js +1 -20
  47. package/shared/capabilities/defineProvider.js +1 -1
  48. package/shared/capabilities/index.js +1 -1
  49. package/shared/index.js +1 -1
  50. package/shared/support/index.js +1 -4
  51. package/shared/support/mobileConfig.js +99 -0
  52. package/shared/support/mobileConfig.test.js +113 -0
  53. package/shared/support/normalize.js +0 -108
  54. package/shared/support/normalize.test.js +0 -134
  55. package/shared/{actions/actionContributorHelpers.test.js → support/permissions.test.js} +1 -1
  56. package/shared/support/returnToPath.js +16 -20
  57. package/shared/support/returnToPath.test.js +5 -33
  58. package/shared/support/shellLayoutTargets.js +2 -140
  59. package/shared/support/shellLayoutTargets.test.js +1 -110
  60. package/shared/support/sorting.js +1 -9
  61. package/shared/surface/apiPaths.js +1 -82
  62. package/shared/surface/paths.js +38 -171
  63. package/shared/surface/registry.js +2 -1
  64. package/shared/surface/runtime.js +18 -109
  65. package/shared/surface/runtime.test.js +57 -0
  66. package/shared/validators/index.js +1 -4
  67. package/shared/validators/jsonRestSchemaSupport.js +0 -9
  68. package/shared/validators/recordIdParamsValidator.js +0 -6
  69. package/shared/validators/schemaDefinitions.js +4 -30
  70. package/shared/validators/schemaPayloadValidation.js +2 -6
  71. package/test/barrelExposure.test.js +5 -8
  72. package/test/exportsContract.test.js +1 -3
  73. package/client/mobileLaunchRouting.js +0 -231
  74. package/client/mobileLaunchRouting.test.js +0 -230
  75. package/server/http/lib/controller.js +0 -183
  76. package/server/http/lib/controller.test.js +0 -143
  77. package/server/http/lib/index.js +0 -5
  78. package/server/runtime/canonicalJson.js +0 -74
  79. package/server/runtime/entityChangeEvents.js +0 -307
  80. package/server/runtime/entityChangeEvents.test.js +0 -274
  81. package/server/runtime/fastifyBootstrap.js +0 -553
  82. package/server/runtime/moduleConfig.js +0 -223
  83. package/server/runtime/moduleConfig.test.js +0 -165
  84. package/server/runtime/pagination.js +0 -13
  85. package/server/runtime/realtimeNormalization.js +0 -21
  86. package/server/runtime/routeUtils.js +0 -20
  87. package/server/runtime/securityAudit.js +0 -269
  88. package/server/runtime/securityAudit.test.js +0 -41
  89. package/server/support/pageTargets.js +0 -772
  90. package/server/support/pageTargets.test.js +0 -659
  91. package/server/support/shellOutlets.js +0 -576
  92. package/server/support/shellOutlets.test.js +0 -336
  93. package/server/surface/index.js +0 -18
  94. package/shared/actions/audit.js +0 -7
  95. package/shared/actions/idempotency.js +0 -62
  96. package/shared/actions/observability.js +0 -10
  97. package/shared/actions/pipeline.js +0 -309
  98. package/shared/actions/requestMeta.js +0 -36
  99. package/shared/actions/textNormalization.js +0 -3
  100. package/shared/actions/withActionDefaults.js +0 -34
  101. /package/{shared → server}/actions/executionContext.test.js +0 -0
@@ -1,576 +0,0 @@
1
- import path from "node:path";
2
- import { readdir, readFile } from "node:fs/promises";
3
- import { pathToFileURL } from "node:url";
4
- import { discoverInstalledPackages } from "../../internal/node/installedPackages.js";
5
- import { normalizeObject, normalizeText } from "../../shared/support/normalize.js";
6
- import { resolveRequiredAppRoot, toPosixPath } from "./path.js";
7
- import {
8
- describeShellOutletTargets,
9
- discoverShellOutletTargetsFromVueSource,
10
- findShellOutletTargetById,
11
- normalizePlacementTopologyDefinition,
12
- normalizeSemanticPlacementId,
13
- normalizeShellOutletTargetId,
14
- normalizeShellOutletTargetRecord
15
- } from "../../shared/support/shellLayoutTargets.js";
16
-
17
- const VUE_DISCOVERY_IGNORED_ERROR_CODES = new Set(["ENOENT", "ENOTDIR", "EACCES", "EPERM"]);
18
- const PLACEMENT_TOPOLOGY_RELATIVE_PATH = "src/placementTopology.js";
19
- const ROUTE_TAG_PATTERN = /<route\b([^>]*)>([\s\S]*?)<\/route>/g;
20
- const ATTRIBUTE_PATTERN = /([:@]?[A-Za-z_][A-Za-z0-9_-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'))?/g;
21
-
22
- function parseTagAttributes(attributesSource = "") {
23
- const attributes = {};
24
- const source = String(attributesSource || "");
25
- for (const match of source.matchAll(ATTRIBUTE_PATTERN)) {
26
- const attributeName = normalizeText(match[1]);
27
- if (!attributeName) {
28
- continue;
29
- }
30
-
31
- const hasValue = match[2] != null || match[3] != null;
32
- const attributeValue = hasValue ? String(match[2] ?? match[3] ?? "") : true;
33
- attributes[attributeName] = attributeValue;
34
- }
35
-
36
- return attributes;
37
- }
38
-
39
- function isDefaultEnabled(value) {
40
- if (value === true) {
41
- return true;
42
- }
43
- if (value === false || value == null) {
44
- return false;
45
- }
46
-
47
- const normalized = normalizeText(value).toLowerCase();
48
- if (!normalized) {
49
- return false;
50
- }
51
-
52
- return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
53
- }
54
-
55
- function normalizeAppRouteOutletTarget({
56
- outlet = {},
57
- sourcePath = ""
58
- } = {}) {
59
- const normalizedTarget = normalizeShellOutletTargetRecord(outlet, {
60
- context: sourcePath || "route meta"
61
- });
62
- if (!normalizedTarget) {
63
- return null;
64
- }
65
- return Object.freeze({
66
- ...normalizedTarget,
67
- default: isDefaultEnabled(normalizedTarget.default),
68
- sourcePath
69
- });
70
- }
71
-
72
- function discoverRouteMetaOutletTargetsFromVueSource(
73
- source = "",
74
- {
75
- context = "shell layout",
76
- enforceSingleDefault = true
77
- } = {}
78
- ) {
79
- const sourceText = String(source || "");
80
- const resolvedContext = normalizeText(context) || "shell layout";
81
- const targetById = new Map();
82
- let defaultTargetId = "";
83
-
84
- for (const routeTagMatch of sourceText.matchAll(ROUTE_TAG_PATTERN)) {
85
- const routeTagAttributes = parseTagAttributes(routeTagMatch[1]);
86
- const routeTagLanguage = normalizeText(routeTagAttributes.lang).toLowerCase();
87
- if (routeTagLanguage !== "json") {
88
- continue;
89
- }
90
-
91
- const routeMetaSource = String(routeTagMatch[2] || "").trim();
92
- if (!routeMetaSource) {
93
- continue;
94
- }
95
-
96
- let routeMetaRecord = null;
97
- try {
98
- routeMetaRecord = JSON.parse(routeMetaSource);
99
- } catch (error) {
100
- throw new Error(
101
- `${resolvedContext} contains invalid <route lang="json"> block: ${String(error?.message || error || "unknown error")}`
102
- );
103
- }
104
-
105
- const routeMeta = normalizeObject(normalizeObject(routeMetaRecord).meta);
106
- const jskitMeta = normalizeObject(routeMeta.jskit);
107
- const placementsMeta = normalizeObject(jskitMeta.placements);
108
- const outlets = Array.isArray(placementsMeta.outlets) ? placementsMeta.outlets : [];
109
- for (const outlet of outlets) {
110
- const normalizedTarget = normalizeAppRouteOutletTarget({
111
- outlet,
112
- sourcePath: resolvedContext
113
- });
114
- if (!normalizedTarget) {
115
- continue;
116
- }
117
- if (targetById.has(normalizedTarget.id)) {
118
- throw new Error(`${resolvedContext} contains duplicate route meta placement target "${normalizedTarget.id}".`);
119
- }
120
- if (normalizedTarget.default === true) {
121
- if (enforceSingleDefault === true && defaultTargetId && defaultTargetId !== normalizedTarget.id) {
122
- throw new Error(
123
- `${resolvedContext} defines multiple default route meta placement targets: "${defaultTargetId}" and "${normalizedTarget.id}".`
124
- );
125
- }
126
- if (!defaultTargetId) {
127
- defaultTargetId = normalizedTarget.id;
128
- }
129
- }
130
- targetById.set(normalizedTarget.id, normalizedTarget);
131
- }
132
- }
133
-
134
- return Object.freeze({
135
- targets: Object.freeze([...targetById.values()]),
136
- defaultTargetId
137
- });
138
- }
139
-
140
- async function collectVueFilePaths(rootDirectoryPath) {
141
- const absoluteRoot = path.resolve(String(rootDirectoryPath || ""));
142
- const stack = [absoluteRoot];
143
- const files = [];
144
-
145
- while (stack.length > 0) {
146
- const currentDirectory = stack.pop();
147
- let directoryEntries = [];
148
- try {
149
- directoryEntries = await readdir(currentDirectory, { withFileTypes: true });
150
- } catch (error) {
151
- const errorCode = normalizeText(error?.code).toUpperCase();
152
- if (!VUE_DISCOVERY_IGNORED_ERROR_CODES.has(errorCode)) {
153
- throw error;
154
- }
155
- continue;
156
- }
157
-
158
- for (const entry of directoryEntries) {
159
- const entryPath = path.join(currentDirectory, entry.name);
160
- if (entry.isDirectory()) {
161
- stack.push(entryPath);
162
- continue;
163
- }
164
-
165
- if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".vue")) {
166
- continue;
167
- }
168
-
169
- files.push(entryPath);
170
- }
171
- }
172
-
173
- return files.sort((left, right) => left.localeCompare(right));
174
- }
175
-
176
- function normalizePackageOutletTarget({
177
- packageId = "",
178
- outlet = {},
179
- manifestPath = ""
180
- } = {}) {
181
- const normalizedPackageId = normalizeText(packageId);
182
- if (!normalizedPackageId) {
183
- return null;
184
- }
185
-
186
- const normalizedTarget = normalizeShellOutletTargetRecord(outlet, {
187
- context: `package:${normalizedPackageId}`
188
- });
189
- if (!normalizedTarget) {
190
- return null;
191
- }
192
-
193
- const outletRecord = normalizeObject(outlet);
194
- const source = normalizeText(outletRecord.source);
195
- const sourcePath = source
196
- ? `package:${normalizedPackageId}:${toPosixPath(source)}`
197
- : `package:${normalizedPackageId}${manifestPath ? `:${toPosixPath(manifestPath)}` : ""}`;
198
-
199
- return Object.freeze({
200
- ...normalizedTarget,
201
- default: false,
202
- sourcePath,
203
- sourcePackageId: normalizedPackageId
204
- });
205
- }
206
-
207
- async function collectInstalledPackageOutletTargets(appRoot) {
208
- const installedPackages = await discoverInstalledPackages({ appRoot });
209
- const targets = [];
210
-
211
- for (const installedPackage of installedPackages) {
212
- const packageId = installedPackage.packageId;
213
- const packageMetadata = normalizeObject(installedPackage.packageMetadata);
214
- const metadata = normalizeObject(packageMetadata.metadata);
215
- const ui = normalizeObject(metadata.ui);
216
- const placements = normalizeObject(ui.placements);
217
- const outlets = Array.isArray(placements.outlets) ? placements.outlets : [];
218
- for (const outlet of outlets) {
219
- const normalizedTarget = normalizePackageOutletTarget({
220
- packageId,
221
- outlet,
222
- manifestPath: installedPackage.manifestPath
223
- });
224
- if (normalizedTarget) {
225
- targets.push(normalizedTarget);
226
- }
227
- }
228
- }
229
-
230
- return targets;
231
- }
232
-
233
- function withTopologySource(placement = {}, sourcePath = "") {
234
- return Object.freeze({
235
- ...placement,
236
- sourcePath
237
- });
238
- }
239
-
240
- async function loadAppPlacementTopology(appRoot) {
241
- const topologyPath = path.resolve(appRoot, PLACEMENT_TOPOLOGY_RELATIVE_PATH);
242
- try {
243
- await readFile(topologyPath, "utf8");
244
- } catch (error) {
245
- const errorCode = normalizeText(error?.code).toUpperCase();
246
- if (errorCode === "ENOENT" || errorCode === "ENOTDIR") {
247
- return [];
248
- }
249
- throw error;
250
- }
251
-
252
- let moduleNamespace = null;
253
- try {
254
- moduleNamespace = await import(pathToFileURL(topologyPath).href);
255
- } catch (error) {
256
- throw new Error(
257
- `Could not load ${PLACEMENT_TOPOLOGY_RELATIVE_PATH}: ${String(error?.message || error || "unknown error")}`
258
- );
259
- }
260
-
261
- const exported = moduleNamespace?.default;
262
- const resolved = typeof exported === "function" ? exported() : exported;
263
- const normalized = normalizePlacementTopologyDefinition(resolved, {
264
- context: PLACEMENT_TOPOLOGY_RELATIVE_PATH
265
- });
266
- return normalized.placements.map((placement) => withTopologySource(placement, PLACEMENT_TOPOLOGY_RELATIVE_PATH));
267
- }
268
-
269
- async function collectInstalledPackagePlacementTopology(appRoot) {
270
- const installedPackages = await discoverInstalledPackages({ appRoot });
271
- const placements = [];
272
-
273
- for (const installedPackage of installedPackages) {
274
- const packageId = installedPackage.packageId;
275
- const packageMetadata = normalizeObject(installedPackage.packageMetadata);
276
- const metadata = normalizeObject(packageMetadata.metadata);
277
- const ui = normalizeObject(metadata.ui);
278
- const placementsMeta = normalizeObject(ui.placements);
279
- const topology = placementsMeta.topology;
280
- if (!topology) {
281
- continue;
282
- }
283
-
284
- const normalized = normalizePlacementTopologyDefinition(topology, {
285
- context: `package:${packageId}:metadata.ui.placements.topology`
286
- });
287
- for (const placement of normalized.placements) {
288
- placements.push(
289
- withTopologySource(
290
- placement,
291
- `package:${packageId}${installedPackage.manifestPath ? `:${toPosixPath(installedPackage.manifestPath)}` : ""}`
292
- )
293
- );
294
- }
295
- }
296
-
297
- return placements;
298
- }
299
-
300
- async function discoverPlacementTopologyFromApp({ appRoot } = {}) {
301
- const resolvedAppRoot = resolveRequiredAppRoot(appRoot, {
302
- context: "discoverPlacementTopologyFromApp"
303
- });
304
- const appPlacements = await loadAppPlacementTopology(resolvedAppRoot);
305
- const packagePlacements = await collectInstalledPackagePlacementTopology(resolvedAppRoot);
306
- const placementByKey = new Map();
307
-
308
- for (const placement of [...packagePlacements, ...appPlacements]) {
309
- const key = `${placement.id}::${placement.owner || ""}`;
310
- placementByKey.set(key, placement);
311
- }
312
-
313
- return Object.freeze({
314
- placements: Object.freeze(
315
- [...placementByKey.values()].sort((left, right) => {
316
- const idCompare = left.id.localeCompare(right.id);
317
- if (idCompare !== 0) {
318
- return idCompare;
319
- }
320
- return String(left.owner || "").localeCompare(String(right.owner || ""));
321
- })
322
- )
323
- });
324
- }
325
-
326
- function findSemanticPlacementById(placements = [], { id = "", owner = "", surface = "" } = {}) {
327
- const normalizedId = normalizeSemanticPlacementId(id);
328
- const normalizedOwner = normalizeText(owner).toLowerCase();
329
- const normalizedSurface = normalizeText(surface).toLowerCase();
330
- if (!normalizedId) {
331
- return null;
332
- }
333
-
334
- return (Array.isArray(placements) ? placements : []).find((placement) => {
335
- if (placement.id !== normalizedId) {
336
- return false;
337
- }
338
- if (normalizedOwner && placement.owner !== normalizedOwner) {
339
- return false;
340
- }
341
- if (!normalizedOwner && placement.owner) {
342
- return false;
343
- }
344
- const surfaces = Array.isArray(placement.surfaces) ? placement.surfaces : ["*"];
345
- return !normalizedSurface || surfaces.includes("*") || surfaces.includes(normalizedSurface);
346
- }) || null;
347
- }
348
-
349
- async function resolveSemanticPlacementTargetFromApp({
350
- appRoot,
351
- placement = "",
352
- owner = "",
353
- surface = "",
354
- context = "ui-generator"
355
- } = {}) {
356
- const resolvedContext = normalizeText(context) || "ui-generator";
357
- const topology = await discoverPlacementTopologyFromApp({ appRoot });
358
- const placements = Array.isArray(topology.placements) ? topology.placements : [];
359
- if (placements.length < 1) {
360
- throw new Error(`${resolvedContext} could not find semantic placement topology in ${PLACEMENT_TOPOLOGY_RELATIVE_PATH}.`);
361
- }
362
-
363
- const requestedPlacement = normalizeText(placement);
364
- if (requestedPlacement) {
365
- const requestedId = normalizeSemanticPlacementId(requestedPlacement);
366
- if (!requestedId) {
367
- throw new Error(`${resolvedContext} option "placement" must be a semantic target in "area.slot" format.`);
368
- }
369
- const match = findSemanticPlacementById(placements, {
370
- id: requestedId,
371
- owner,
372
- surface
373
- }) || (owner
374
- ? findSemanticPlacementById(placements, {
375
- id: requestedId,
376
- owner: "",
377
- surface
378
- })
379
- : null);
380
- if (!match) {
381
- throw new Error(`${resolvedContext} semantic placement "${requestedId}" is not declared in app placement topology.`);
382
- }
383
- return match;
384
- }
385
-
386
- const defaultPlacement =
387
- placements.find((entry) => entry.default === true && (!surface || entry.surfaces.includes("*") || entry.surfaces.includes(surface))) ||
388
- placements.find((entry) => !entry.owner && (!surface || entry.surfaces.includes("*") || entry.surfaces.includes(surface))) ||
389
- null;
390
- if (defaultPlacement) {
391
- return defaultPlacement;
392
- }
393
-
394
- throw new Error(`${resolvedContext} could not resolve a default semantic placement target.`);
395
- }
396
-
397
- async function collectAppSourceShellOutletTargets({
398
- appRoot,
399
- sourceRoot = "src",
400
- enforceSingleDefault = true,
401
- context = "discoverShellOutletTargetsFromApp"
402
- } = {}) {
403
- const resolvedAppRoot = resolveRequiredAppRoot(appRoot, {
404
- context
405
- });
406
-
407
- const sourceDirectory = path.resolve(resolvedAppRoot, String(sourceRoot || "src"));
408
- const targetById = new Map();
409
- let defaultTargetId = "";
410
- let defaultTargetSource = "";
411
- const vueFiles = await collectVueFilePaths(sourceDirectory);
412
-
413
- for (const absoluteFilePath of vueFiles) {
414
- const relativePath = toPosixPath(path.relative(resolvedAppRoot, absoluteFilePath));
415
- const source = await readFile(absoluteFilePath, "utf8");
416
- if (!source.includes("<ShellOutlet") && !source.includes("<route")) {
417
- continue;
418
- }
419
-
420
- const discoveredShellOutlets = source.includes("<ShellOutlet")
421
- ? discoverShellOutletTargetsFromVueSource(source, {
422
- context: relativePath,
423
- enforceSingleDefault
424
- })
425
- : { targets: [], defaultTargetId: "" };
426
- const discoveredRouteMetaOutlets = source.includes("<route")
427
- ? discoverRouteMetaOutletTargetsFromVueSource(source, {
428
- context: relativePath,
429
- enforceSingleDefault
430
- })
431
- : { targets: [], defaultTargetId: "" };
432
- const discoveredTargets = [
433
- ...(Array.isArray(discoveredShellOutlets.targets) ? discoveredShellOutlets.targets : []),
434
- ...(Array.isArray(discoveredRouteMetaOutlets.targets)
435
- ? discoveredRouteMetaOutlets.targets
436
- : [])
437
- ];
438
- for (const target of discoveredTargets) {
439
- if (!targetById.has(target.id)) {
440
- targetById.set(
441
- target.id,
442
- Object.freeze({
443
- ...target,
444
- sourcePath: relativePath
445
- })
446
- );
447
- }
448
- }
449
-
450
- const discoveredDefaultTargetIds = [
451
- normalizeShellOutletTargetId(discoveredShellOutlets.defaultTargetId),
452
- normalizeShellOutletTargetId(discoveredRouteMetaOutlets.defaultTargetId)
453
- ].filter(Boolean);
454
- for (const discoveredDefaultTargetId of discoveredDefaultTargetIds) {
455
- if (enforceSingleDefault === true && defaultTargetId && discoveredDefaultTargetId !== defaultTargetId) {
456
- throw new Error(
457
- `Multiple default ShellOutlet targets found in app source: "${defaultTargetId}" (${defaultTargetSource}) and ` +
458
- `"${discoveredDefaultTargetId}" (${relativePath}).`
459
- );
460
- }
461
-
462
- if (!defaultTargetId) {
463
- defaultTargetId = discoveredDefaultTargetId;
464
- defaultTargetSource = relativePath;
465
- }
466
- }
467
- }
468
-
469
- return Object.freeze({
470
- resolvedAppRoot,
471
- targetById,
472
- defaultTargetId
473
- });
474
- }
475
-
476
- async function discoverShellOutletTargetsFromApp({ appRoot, sourceRoot = "src" } = {}) {
477
- const discoveredAppTargets = await collectAppSourceShellOutletTargets({
478
- appRoot,
479
- sourceRoot,
480
- enforceSingleDefault: true,
481
- context: "discoverShellOutletTargetsFromApp"
482
- });
483
- const resolvedAppRoot = discoveredAppTargets.resolvedAppRoot;
484
- const targetById = new Map(discoveredAppTargets.targetById);
485
-
486
- const packageTargets = await collectInstalledPackageOutletTargets(resolvedAppRoot);
487
- for (const target of packageTargets) {
488
- if (!targetById.has(target.id)) {
489
- targetById.set(target.id, target);
490
- }
491
- }
492
-
493
- const targets = [...targetById.values()].sort((left, right) => left.id.localeCompare(right.id));
494
- const normalizedTargets = targets.map((target) =>
495
- Object.freeze({
496
- ...target,
497
- default: target.id === discoveredAppTargets.defaultTargetId
498
- })
499
- );
500
-
501
- return Object.freeze({
502
- targets: Object.freeze(normalizedTargets),
503
- defaultTargetId: discoveredAppTargets.defaultTargetId
504
- });
505
- }
506
-
507
- async function discoverShellOutletSourcePathsFromApp({ appRoot, sourceRoot = "src" } = {}) {
508
- const discoveredAppTargets = await collectAppSourceShellOutletTargets({
509
- appRoot,
510
- sourceRoot,
511
- enforceSingleDefault: false,
512
- context: "discoverShellOutletSourcePathsFromApp"
513
- });
514
- const targetById = new Map(discoveredAppTargets.targetById);
515
-
516
- const packageTargets = await collectInstalledPackageOutletTargets(discoveredAppTargets.resolvedAppRoot);
517
- for (const target of packageTargets) {
518
- if (!targetById.has(target.id)) {
519
- targetById.set(target.id, target);
520
- }
521
- }
522
-
523
- return Object.freeze({
524
- targets: Object.freeze([...targetById.values()].sort((left, right) => left.id.localeCompare(right.id)))
525
- });
526
- }
527
-
528
- async function resolveShellOutletPlacementTargetFromApp({ appRoot, placement = "", context = "ui-generator" } = {}) {
529
- const resolvedContext = normalizeText(context) || "ui-generator";
530
- const requestedPlacementOption = normalizeText(placement);
531
- const requestedPlacementTargetId = normalizeShellOutletTargetId(requestedPlacementOption);
532
- if (requestedPlacementOption && !requestedPlacementTargetId) {
533
- throw new Error(`${resolvedContext} option "placement" must be a target in "host:position" format.`);
534
- }
535
-
536
- const discovered = await discoverShellOutletTargetsFromApp({ appRoot, sourceRoot: "src" });
537
- const targets = Array.isArray(discovered.targets) ? discovered.targets : [];
538
- if (targets.length < 1) {
539
- throw new Error(
540
- `${resolvedContext} could not find any placement targets from app Vue outlets or installed package metadata.`
541
- );
542
- }
543
-
544
- if (requestedPlacementTargetId) {
545
- const requestedTarget = findShellOutletTargetById(targets, requestedPlacementTargetId);
546
- if (!requestedTarget) {
547
- const availableTargets = describeShellOutletTargets(targets);
548
- throw new Error(
549
- `${resolvedContext} option "placement" target "${requestedPlacementTargetId}" is not declared in app or package placement outlets. ` +
550
- `Available targets: ${availableTargets || "<none>"}.`
551
- );
552
- }
553
-
554
- return requestedTarget;
555
- }
556
-
557
- const defaultTarget = findShellOutletTargetById(targets, discovered.defaultTargetId);
558
- if (defaultTarget) {
559
- return defaultTarget;
560
- }
561
-
562
- const availableTargets = describeShellOutletTargets(targets);
563
- throw new Error(
564
- `${resolvedContext} could not resolve a default ShellOutlet target from app Vue outlets. ` +
565
- `Set one outlet as default (e.g. <ShellOutlet target="shell-layout:primary-menu" default />) ` +
566
- `or pass "--placement shell-layout:primary-menu". Available targets: ${availableTargets || "<none>"}.`
567
- );
568
- }
569
-
570
- export {
571
- discoverPlacementTopologyFromApp,
572
- discoverShellOutletSourcePathsFromApp,
573
- discoverShellOutletTargetsFromApp,
574
- resolveSemanticPlacementTargetFromApp,
575
- resolveShellOutletPlacementTargetFromApp
576
- };