@jskit-ai/shell-web 0.1.162 → 0.1.164

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 (46) hide show
  1. package/fixtures/adaptive-shell/src/ScreenPage.vue +1 -1
  2. package/package.json +8 -253
  3. package/patterns/application-shell/PATTERN.md +66 -0
  4. package/{templates → patterns/application-shell/example}/expected-existing/src/pages/home/index.vue +8 -8
  5. package/{templates → patterns/application-shell/example}/src/pages/home/index.vue +8 -8
  6. package/{templates → patterns/application-shell/example}/src/pages/home/settings/general/index.vue +1 -1
  7. package/{templates → patterns/application-shell/example}/src/pages/home/settings.vue +8 -8
  8. package/patterns/page-and-placement/PATTERN.md +76 -0
  9. package/patterns/page-and-placement/example/packages/main/src/client/providers/MainClientProvider.js +14 -0
  10. package/patterns/page-and-placement/example/src/components/SyncStatusElement.vue +16 -0
  11. package/patterns/page-and-placement/example/src/pages/home/reports/activity.vue +8 -0
  12. package/patterns/page-and-placement/example/src/pages/home/reports/index.vue +7 -0
  13. package/patterns/page-and-placement/example/src/pages/home/reports/overview.vue +8 -0
  14. package/patterns/page-and-placement/example/src/pages/home/reports.vue +33 -0
  15. package/patterns/page-and-placement/example/src/placement.js +52 -0
  16. package/patterns/page-and-placement/example/src/placementTopology.js +29 -0
  17. package/src/client/bootstrap/bootstrapPayloadHandlerRegistry.js +30 -42
  18. package/src/client/bootstrap/index.js +2 -3
  19. package/src/client/index.js +2 -11
  20. package/src/client/placement/runtime.js +9 -12
  21. package/src/client/providers/ShellWebClientProvider.js +95 -129
  22. package/src/client/requestRecovery/runtime.js +9 -29
  23. package/src/client/runtime/bootstrapRuntime.js +18 -29
  24. package/src/test/adaptiveShellSmoke.js +3 -9
  25. package/test/bootstrapRuntime.test.js +10 -61
  26. package/test/pageAndPlacementPattern.test.js +38 -0
  27. package/test/placementRuntime.test.js +36 -50
  28. package/test/playwrightContract.test.js +2 -12
  29. package/test/provider.test.js +126 -737
  30. package/test/settingsPlacementContract.test.js +75 -126
  31. package/src/server/support/localLinkItemScaffolds.js +0 -80
  32. package/test/bootstrapClaimContract.test.js +0 -76
  33. package/test/linkItemScaffoldContract.test.js +0 -248
  34. /package/{templates → patterns/application-shell/example}/expected-existing/src/App.vue +0 -0
  35. /package/{templates → patterns/application-shell/example}/expected-existing/src/pages/home.vue +0 -0
  36. /package/{templates → patterns/application-shell/example}/src/App.vue +0 -0
  37. /package/{templates → patterns/application-shell/example}/src/components/ShellLayout.vue +0 -0
  38. /package/{templates → patterns/application-shell/example}/src/components/menus/MenuLinkItem.vue +0 -0
  39. /package/{templates → patterns/application-shell/example}/src/components/menus/SurfaceAwareMenuLinkItem.vue +0 -0
  40. /package/{templates → patterns/application-shell/example}/src/components/menus/TabLinkItem.vue +0 -0
  41. /package/{templates → patterns/application-shell/example}/src/error.js +0 -0
  42. /package/{templates → patterns/application-shell/example}/src/pages/home/settings/index.vue +0 -0
  43. /package/{templates → patterns/application-shell/example}/src/pages/home.vue +0 -0
  44. /package/{templates → patterns/application-shell/example}/src/placement.js +0 -0
  45. /package/{templates → patterns/application-shell/example}/src/placementTopology.js +0 -0
  46. /package/{templates → patterns/application-shell/example}/tests/e2e/adaptive-shell.spec.ts +0 -0
@@ -3,7 +3,6 @@ import path from "node:path";
3
3
  import test from "node:test";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { fileURLToPath } from "node:url";
6
- import { assertGeneratedUiSourceContract } from "@jskit-ai/kernel/shared/support/generatedUiContract";
7
6
  import packageJson from "../package.json" with { type: "json" };
8
7
 
9
8
  const packageMetadata = packageJson.jskit;
@@ -47,24 +46,16 @@ function readPackageImportSpecifiers(source = "") {
47
46
  );
48
47
  }
49
48
 
50
- function readClientContainerTokens() {
51
- const tokens = packageMetadata?.metadata?.apiSummary?.containerTokens?.client;
52
- return Array.isArray(tokens) ? tokens : [];
53
- }
54
-
55
- function findFileMutation(id) {
56
- const files = packageMetadata?.mutations?.files;
57
- return Array.isArray(files)
58
- ? files.find((entry) => String(entry?.id || "").trim() === id) || null
59
- : null;
60
- }
61
-
62
- test("shell-web home settings template exposes surface-derived settings outlets", async () => {
63
- const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "pages", "home", "settings.vue"), "utf8");
49
+ test("shell-web application pattern exposes surface-derived settings outlets", async () => {
50
+ const source = await readFile(
51
+ path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "pages", "home", "settings.vue"),
52
+ "utf8"
53
+ );
64
54
 
65
55
  assert.match(source, /target="home-settings:primary-menu"/);
66
- assert.match(source, /generated-ui-screen generated-ui-screen--settings settings-shell/);
67
- assert.match(source, /--generated-ui-screen-title-size/);
56
+ assert.match(source, /class="settings-shell d-flex flex-column ga-4"/);
57
+ assert.match(source, /--settings-shell-title-size/);
58
+ assert.doesNotMatch(source, /generated-ui/u);
68
59
  assert.doesNotMatch(source, /default-link-component-token/);
69
60
  assert.match(source, /<RouterView \/>/);
70
61
  });
@@ -123,7 +114,10 @@ test("shell-web shell layout registers navigation at the app layout level", asyn
123
114
  assert.match(source, /data-testid="jskit-shell-bottom-nav"/);
124
115
  assert.match(source, /padding:\s*0\.75rem 1rem calc\(1rem \+ env\(safe-area-inset-bottom, 0px\)\)/);
125
116
 
126
- const template = await readFile(path.join(PACKAGE_DIR, "templates", "src", "components", "ShellLayout.vue"), "utf8");
117
+ const template = await readFile(
118
+ path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "components", "ShellLayout.vue"),
119
+ "utf8"
120
+ );
127
121
 
128
122
  assert.match(template, /PackageShellLayout from "@jskit-ai\/shell-web\/client\/components\/ShellLayout"/);
129
123
  assert.match(template, /drawerWidth, railWidth, navigationItemSpacing, and future shell props package-owned/);
@@ -150,8 +144,11 @@ test("shell-web error host keeps snackbar color stable while closing", async ()
150
144
  assert.doesNotMatch(source, /resolveSeverityColor\(snackbarEntry\?\.severity\)/);
151
145
  });
152
146
 
153
- test("shell-web error template uses intent-driven default presentation", async () => {
154
- const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "error.js"), "utf8");
147
+ test("shell-web application pattern uses intent-driven default error presentation", async () => {
148
+ const source = await readFile(
149
+ path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "error.js"),
150
+ "utf8"
151
+ );
155
152
 
156
153
  assert.match(source, /resourceLoadChannel:\s*"silent"/);
157
154
  assert.match(source, /actionFeedbackChannel:\s*"snackbar"/);
@@ -159,20 +156,19 @@ test("shell-web error template uses intent-driven default presentation", async (
159
156
  assert.match(source, /blockingChannel:\s*"dialog"/);
160
157
  });
161
158
 
162
- test("shell-web installs generated adaptive shell Playwright smoke coverage", async () => {
163
- const source = await readFile(path.join(PACKAGE_DIR, "templates", "tests", "e2e", "adaptive-shell.spec.ts"), "utf8");
159
+ test("shell-web application pattern includes adaptive shell Playwright coverage", async () => {
160
+ const source = await readFile(
161
+ path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "tests", "e2e", "adaptive-shell.spec.ts"),
162
+ "utf8"
163
+ );
164
164
  const helperSource = await readFile(path.join(PACKAGE_DIR, "src", "test", "adaptiveShellSmoke.js"), "utf8");
165
165
 
166
- assertGeneratedUiSourceContract(helperSource, {
167
- profile: "responsive-smoke",
168
- sourceName: "adaptiveShellSmoke.js"
169
- });
170
166
  assert.match(source, /DEFAULT_VIEWPORTS, runAdaptiveShellSmokeCase/);
171
167
  assert.match(source, /test\(`/);
172
168
  assert.match(source, /runAdaptiveShellSmokeCase\(\{ page, expect, viewport \}\)/);
173
169
  assert.doesNotMatch(source, /runAdaptiveShellSmoke\(\{/);
174
170
  assert.match(source, /@jskit-ai\/shell-web\/test\/adaptiveShellSmoke/);
175
- assert.match(helperSource, /generated adaptive shell smoke/);
171
+ assert.match(helperSource, /adaptive shell smoke/);
176
172
  assert.match(helperSource, /390/);
177
173
  assert.match(helperSource, /1024/);
178
174
  assert.match(helperSource, /1280/);
@@ -180,6 +176,8 @@ test("shell-web installs generated adaptive shell Playwright smoke coverage", as
180
176
  assert.match(helperSource, /jskit-shell-drawer/);
181
177
  assert.match(helperSource, /scrollWidth/);
182
178
  assert.match(helperSource, /toBeGreaterThanOrEqual\(48\)/);
179
+ assert.match(helperSource, /getByRole\("button", \{ name: "Close navigation menu" \}\)/);
180
+ assert.doesNotMatch(helperSource, /\.v-navigation-drawer__scrim|generated-ui-screen/u);
183
181
 
184
182
  const packageJson = JSON.parse(await readFile(path.join(PACKAGE_DIR, "package.json"), "utf8"));
185
183
  assert.equal(packageJson?.exports?.["./test/adaptiveShellSmoke"], "./src/test/adaptiveShellSmoke.js");
@@ -296,22 +294,37 @@ test("shell-web route transition key preserves no-motion surfaces and animated p
296
294
  );
297
295
  });
298
296
 
299
- test("shell-web settings landing page redirects to the starter child page", async () => {
300
- const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "pages", "home", "settings", "index.vue"), "utf8");
297
+ test("shell-web pattern settings landing page redirects to its child page", async () => {
298
+ const source = await readFile(
299
+ path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "pages", "home", "settings", "index.vue"),
300
+ "utf8"
301
+ );
301
302
 
302
303
  assert.match(source, /@jskit-ai\/kernel\/client\/pageRedirects/);
303
304
  assert.match(source, /definePage/);
304
305
  assert.match(source, /redirectToChild\("general"\)/);
305
306
  });
306
307
 
307
- test("shell-web settings general child page exposes an adaptive drawer preference", async () => {
308
+ test("shell-web pattern settings page exposes an adaptive drawer preference", async () => {
308
309
  const source = await readFile(
309
- path.join(PACKAGE_DIR, "templates", "src", "pages", "home", "settings", "general", "index.vue"),
310
+ path.join(
311
+ PACKAGE_DIR,
312
+ "patterns",
313
+ "application-shell",
314
+ "example",
315
+ "src",
316
+ "pages",
317
+ "home",
318
+ "settings",
319
+ "general",
320
+ "index.vue"
321
+ ),
310
322
  "utf8"
311
323
  );
312
324
 
313
325
  assert.match(source, /useShellLayoutState/);
314
- assert.match(source, /generated-ui-screen generated-ui-screen--settings settings-general-screen/);
326
+ assert.match(source, /class="settings-general-screen d-flex flex-column ga-4"/);
327
+ assert.doesNotMatch(source, /generated-ui/u);
315
328
  assert.match(source, /drawerDefaultOpen/);
316
329
  assert.match(source, /setDrawerDefaultOpen/);
317
330
  assert.match(source, /collapsed navigation remains available as a rail/);
@@ -321,8 +334,11 @@ test("shell-web settings general child page exposes an adaptive drawer preferenc
321
334
  assert.doesNotMatch(source, /live in this browser only|tiny example|starter settings/);
322
335
  });
323
336
 
324
- test("shell-web placement template seeds default Home and Settings adaptive navigation", async () => {
325
- const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "placement.js"), "utf8");
337
+ test("shell-web application pattern demonstrates Home and Settings adaptive navigation", async () => {
338
+ const source = await readFile(
339
+ path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "placement.js"),
340
+ "utf8"
341
+ );
326
342
 
327
343
  assert.match(source, /id: "shell-web\.home\.menu\.home"/);
328
344
  assert.match(source, /target: "shell\.primary-nav"/);
@@ -341,8 +357,11 @@ test("shell-web placement template seeds default Home and Settings adaptive navi
341
357
  assert.doesNotMatch(source, /to: "\.\/general"/);
342
358
  });
343
359
 
344
- test("shell-web placement topology seeds global actions as a semantic shell placement", async () => {
345
- const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "placementTopology.js"), "utf8");
360
+ test("shell-web application pattern demonstrates semantic global actions", async () => {
361
+ const source = await readFile(
362
+ path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "placementTopology.js"),
363
+ "utf8"
364
+ );
346
365
 
347
366
  assert.match(source, /id: "shell\.global-actions"/);
348
367
  assert.match(source, /description: "Global surface actions that should stay outside primary navigation\."/);
@@ -353,12 +372,12 @@ test("shell-web placement topology seeds global actions as a semantic shell plac
353
372
  assert.match(source, /outlet: "shell-layout:supporting-side-panel"/);
354
373
  });
355
374
 
356
- test("shell-web packageMetadata pre-optimizes package subpaths reached only through dynamic base-shell modules", async () => {
375
+ test("shell-web packageMetadata pre-optimizes package subpaths reached through dynamic shell modules", async () => {
357
376
  const [providerSource, placementSource, placementTopologySource, errorSource] = await Promise.all([
358
377
  readFile(path.join(PACKAGE_DIR, "src", "client", "providers", "ShellWebClientProvider.js"), "utf8"),
359
- readFile(path.join(PACKAGE_DIR, "templates", "src", "placement.js"), "utf8"),
360
- readFile(path.join(PACKAGE_DIR, "templates", "src", "placementTopology.js"), "utf8"),
361
- readFile(path.join(PACKAGE_DIR, "templates", "src", "error.js"), "utf8")
378
+ readFile(path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "placement.js"), "utf8"),
379
+ readFile(path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "placementTopology.js"), "utf8"),
380
+ readFile(path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "error.js"), "utf8")
362
381
  ]);
363
382
 
364
383
  assert.deepEqual(
@@ -389,30 +408,10 @@ test("shell-web packageMetadata pre-optimizes package subpaths reached only thro
389
408
  ]);
390
409
  });
391
410
 
392
- test("shell-web packageMetadata metadata advertises adaptive shell outlets, default links, and installs the scaffold page", () => {
393
- const homePageMutationIds = [
394
- "shell-web-page-home-wrapper",
395
- "shell-web-page-home",
396
- "shell-web-page-home-settings-shell",
397
- "shell-web-page-home-settings",
398
- "shell-web-page-home-settings-general"
399
- ];
400
- for (const mutationId of homePageMutationIds) {
401
- assert.deepEqual(findFileMutation(mutationId)?.when, {
402
- config: "surfaceDefinitions.home.enabled",
403
- equals: "true"
404
- });
405
- }
406
-
407
- assert.deepEqual(readClientContainerTokens(), [
408
- "runtime.web-placement.client",
409
- "runtime.web-bootstrap.client",
410
- "runtime.web-refresh.client",
411
- "runtime.web-async-module-recovery.client",
412
- "runtime.web-request-recovery.client",
413
- "runtime.web-error.client",
414
- "runtime.web-error.presentation-store.client"
415
- ]);
411
+ test("shell-web metadata advertises adaptive shell outlets and pattern-backed links", () => {
412
+ assert.equal(packageMetadata?.mutations, undefined);
413
+ assert.equal(Object.hasOwn(packageMetadata?.metadata?.apiSummary || {}, "containerTokens"), false);
414
+ assert.deepEqual(packageMetadata?.capabilities?.provides, ["client.shell"]);
416
415
 
417
416
  assert.deepEqual(
418
417
  readOutlets("shell-layout:primary-bottom-nav"),
@@ -453,7 +452,7 @@ test("shell-web packageMetadata metadata advertises adaptive shell outlets, defa
453
452
  {
454
453
  target: "home-settings:primary-menu",
455
454
  surfaces: ["home"],
456
- source: "templates/src/pages/home/settings.vue"
455
+ source: "patterns/application-shell/example/src/pages/home/settings.vue"
457
456
  }
458
457
  ]
459
458
  );
@@ -467,7 +466,7 @@ test("shell-web packageMetadata metadata advertises adaptive shell outlets, defa
467
466
  kind: "link",
468
467
  surfaces: ["home"],
469
468
  order: 50,
470
- source: "templates/src/placement.js"
469
+ source: "patterns/application-shell/example/src/placement.js"
471
470
  },
472
471
  {
473
472
  id: "shell-web.home.menu.settings",
@@ -475,7 +474,7 @@ test("shell-web packageMetadata metadata advertises adaptive shell outlets, defa
475
474
  kind: "link",
476
475
  surfaces: ["home"],
477
476
  order: 100,
478
- source: "templates/src/placement.js"
477
+ source: "patterns/application-shell/example/src/placement.js"
479
478
  }
480
479
  ]
481
480
  );
@@ -490,7 +489,7 @@ test("shell-web packageMetadata metadata advertises adaptive shell outlets, defa
490
489
  kind: "link",
491
490
  surfaces: ["home"],
492
491
  order: 100,
493
- source: "templates/src/placement.js"
492
+ source: "patterns/application-shell/example/src/placement.js"
494
493
  }
495
494
  ]
496
495
  );
@@ -519,67 +518,17 @@ test("shell-web packageMetadata metadata advertises adaptive shell outlets, defa
519
518
  "shell-layout:supporting-side-panel"
520
519
  );
521
520
 
522
- assert.deepEqual(findFileMutation("shell-web-page-home-settings-shell"), {
523
- from: "templates/src/pages/home/settings.vue",
524
- toSurface: "home",
525
- toSurfacePath: "settings.vue",
526
- ownership: "app",
527
- reason: "Install shell-driven home settings shell route with section navigation.",
528
- category: "shell-web",
529
- id: "shell-web-page-home-settings-shell",
530
- when: {
531
- config: "surfaceDefinitions.home.enabled",
532
- equals: "true"
533
- }
534
- });
535
-
536
- assert.deepEqual(findFileMutation("shell-web-page-home-settings"), {
537
- from: "templates/src/pages/home/settings/index.vue",
538
- toSurface: "home",
539
- toSurfacePath: "settings/index.vue",
540
- ownership: "app",
541
- reason: "Install shell-driven home settings redirect so the starter settings shell lands on a real child page.",
542
- category: "shell-web",
543
- id: "shell-web-page-home-settings",
544
- when: {
545
- config: "surfaceDefinitions.home.enabled",
546
- equals: "true"
547
- }
548
- });
549
-
550
- assert.deepEqual(findFileMutation("shell-web-page-home-settings-general"), {
551
- from: "templates/src/pages/home/settings/general/index.vue",
552
- toSurface: "home",
553
- toSurfacePath: "settings/general/index.vue",
554
- ownership: "app",
555
- reason: "Install shell-driven general settings child page with a tiny browser-local shell preference example.",
556
- category: "shell-web",
557
- id: "shell-web-page-home-settings-general",
558
- when: {
559
- config: "surfaceDefinitions.home.enabled",
560
- equals: "true"
561
- }
562
- });
563
-
564
- assert.deepEqual(findFileMutation("shell-web-test-adaptive-shell-smoke"), {
565
- from: "templates/tests/e2e/adaptive-shell.spec.ts",
566
- to: "tests/e2e/adaptive-shell.spec.ts",
567
- ownership: "app",
568
- reason: "Install compact/medium/expanded Playwright smoke coverage for the adaptive shell.",
569
- category: "shell-web",
570
- id: "shell-web-test-adaptive-shell-smoke"
571
- });
572
521
  });
573
522
 
574
- test("shell-web home starter page relies on adaptive shell navigation instead of dead feature buttons", async () => {
575
- const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "pages", "home", "index.vue"), "utf8");
523
+ test("shell-web pattern home page relies on adaptive navigation instead of dead feature buttons", async () => {
524
+ const source = await readFile(
525
+ path.join(PACKAGE_DIR, "patterns", "application-shell", "example", "src", "pages", "home", "index.vue"),
526
+ "utf8"
527
+ );
576
528
 
577
- assertGeneratedUiSourceContract(source, {
578
- profile: "shell-home",
579
- sourceName: "shell-web home/index.vue"
580
- });
581
- assert.match(source, /generated-ui-screen generated-ui-screen--app home-surface-screen/);
582
- assert.match(source, /--generated-ui-screen-title-size/);
529
+ assert.match(source, /class="home-surface-screen d-flex flex-column ga-4"/);
530
+ assert.match(source, /--home-surface-title-size/);
531
+ assert.doesNotMatch(source, /generated-ui/u);
583
532
  assert.match(source, /Core services are available\./);
584
533
  assert.match(source, /to="\/home\/settings\/general"/);
585
534
  assert.doesNotMatch(source, /Use bottom navigation|Replace this content|Main public surface/);
@@ -1,80 +0,0 @@
1
- import path from "node:path";
2
- import { readFile } from "node:fs/promises";
3
- import { fileURLToPath } from "node:url";
4
-
5
- const PACKAGE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
6
-
7
- function createLocalLinkItemDefinition({
8
- token = "",
9
- componentFile = "",
10
- componentName = "",
11
- templateFile = ""
12
- } = {}) {
13
- return Object.freeze({
14
- token: String(token || "").trim(),
15
- componentFile: String(componentFile || "").trim(),
16
- componentName: String(componentName || "").trim(),
17
- templateFile: String(templateFile || "").trim()
18
- });
19
- }
20
-
21
- const LOCAL_LINK_ITEM_COMPONENT_DEFINITIONS = Object.freeze([
22
- createLocalLinkItemDefinition({
23
- token: "local.main.ui.menu-link-item",
24
- componentFile: "src/components/menus/MenuLinkItem.vue",
25
- componentName: "MenuLinkItem",
26
- templateFile: "templates/src/components/menus/MenuLinkItem.vue"
27
- }),
28
- createLocalLinkItemDefinition({
29
- token: "local.main.ui.surface-aware-menu-link-item",
30
- componentFile: "src/components/menus/SurfaceAwareMenuLinkItem.vue",
31
- componentName: "SurfaceAwareMenuLinkItem",
32
- templateFile: "templates/src/components/menus/SurfaceAwareMenuLinkItem.vue"
33
- }),
34
- createLocalLinkItemDefinition({
35
- token: "local.main.ui.tab-link-item",
36
- componentFile: "src/components/menus/TabLinkItem.vue",
37
- componentName: "TabLinkItem",
38
- templateFile: "templates/src/components/menus/TabLinkItem.vue"
39
- })
40
- ]);
41
-
42
- const LOCAL_LINK_ITEM_COMPONENT_TOKENS = Object.freeze(
43
- LOCAL_LINK_ITEM_COMPONENT_DEFINITIONS.map((entry) => entry.token)
44
- );
45
-
46
- function findLocalLinkItemDefinition(componentToken = "") {
47
- const normalizedComponentToken = String(componentToken || "").trim();
48
- return LOCAL_LINK_ITEM_COMPONENT_DEFINITIONS.find((entry) => entry.token === normalizedComponentToken) || null;
49
- }
50
-
51
- function resolveLocalLinkItemDefinition(componentTokenOrDefinition = "") {
52
- if (
53
- componentTokenOrDefinition &&
54
- typeof componentTokenOrDefinition === "object" &&
55
- !Array.isArray(componentTokenOrDefinition)
56
- ) {
57
- return componentTokenOrDefinition;
58
- }
59
- return findLocalLinkItemDefinition(componentTokenOrDefinition);
60
- }
61
-
62
- function resolveLocalLinkItemTemplateAbsolutePath(componentTokenOrDefinition = "") {
63
- const definition = resolveLocalLinkItemDefinition(componentTokenOrDefinition);
64
- if (!definition) {
65
- throw new Error(`Unknown local link-item scaffold: ${String(componentTokenOrDefinition || "").trim() || "(empty)"}.`);
66
- }
67
- return path.join(PACKAGE_DIR, definition.templateFile);
68
- }
69
-
70
- async function readLocalLinkItemComponentSource(componentTokenOrDefinition = "") {
71
- return await readFile(resolveLocalLinkItemTemplateAbsolutePath(componentTokenOrDefinition), "utf8");
72
- }
73
-
74
- export {
75
- LOCAL_LINK_ITEM_COMPONENT_DEFINITIONS,
76
- LOCAL_LINK_ITEM_COMPONENT_TOKENS,
77
- findLocalLinkItemDefinition,
78
- readLocalLinkItemComponentSource,
79
- resolveLocalLinkItemTemplateAbsolutePath
80
- };
@@ -1,76 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import path from "node:path";
3
- import test from "node:test";
4
- import { readFile } from "node:fs/promises";
5
- import { fileURLToPath } from "node:url";
6
- import packageJson from "../package.json" with { type: "json" };
7
-
8
- const packageMetadata = packageJson.jskit;
9
-
10
- const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
11
- const PACKAGE_DIR = path.resolve(TEST_DIRECTORY, "..");
12
- const CREATE_APP_TEMPLATE_DIR = path.resolve(PACKAGE_DIR, "..", "..", "tooling", "create-app", "templates", "minimal-shell");
13
-
14
- function findFileMutation(id) {
15
- const files = packageMetadata?.mutations?.files;
16
- return Array.isArray(files)
17
- ? files.find((entry) => String(entry?.id || "").trim() === id) || null
18
- : null;
19
- }
20
-
21
- test("shell-web claims starter shell files as app-owned scaffolds", () => {
22
- assert.deepEqual(findFileMutation("shell-web-app-root"), {
23
- from: "templates/src/App.vue",
24
- to: "src/App.vue",
25
- ownership: "app",
26
- expectedExistingFrom: "templates/expected-existing/src/App.vue",
27
- reason: "Install full-width shell app root with shell-web error host and edge-to-edge layout.",
28
- category: "shell-web",
29
- id: "shell-web-app-root"
30
- });
31
- assert.deepEqual(findFileMutation("shell-web-page-home-wrapper"), {
32
- from: "templates/src/pages/home.vue",
33
- toSurface: "home",
34
- toSurfaceRoot: true,
35
- ownership: "app",
36
- expectedExistingFrom: "templates/expected-existing/src/pages/home.vue",
37
- reason: "Install shell-driven home wrapper page.",
38
- category: "shell-web",
39
- id: "shell-web-page-home-wrapper",
40
- when: {
41
- config: "surfaceDefinitions.home.enabled",
42
- equals: "true"
43
- }
44
- });
45
- assert.deepEqual(findFileMutation("shell-web-page-home"), {
46
- from: "templates/src/pages/home/index.vue",
47
- toSurface: "home",
48
- toSurfacePath: "index.vue",
49
- ownership: "app",
50
- expectedExistingFrom: "templates/expected-existing/src/pages/home/index.vue",
51
- reason: "Install shell-driven home surface starter page.",
52
- category: "shell-web",
53
- id: "shell-web-page-home",
54
- when: {
55
- config: "surfaceDefinitions.home.enabled",
56
- equals: "true"
57
- }
58
- });
59
- });
60
-
61
- test("shell-web expected-existing starter files stay aligned with create-app minimal-shell", async () => {
62
- const comparedFiles = [
63
- "src/App.vue",
64
- "src/pages/home.vue",
65
- "src/pages/home/index.vue"
66
- ];
67
-
68
- for (const relativeFile of comparedFiles) {
69
- const shellWebExpectedSource = await readFile(
70
- path.join(PACKAGE_DIR, "templates", "expected-existing", relativeFile),
71
- "utf8"
72
- );
73
- const createAppSource = await readFile(path.join(CREATE_APP_TEMPLATE_DIR, relativeFile), "utf8");
74
- assert.equal(shellWebExpectedSource, createAppSource, relativeFile);
75
- }
76
- });