@mpgd/cli 0.47.0 → 0.48.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 (49) hide show
  1. package/dist/command-launcher.d.ts +13 -0
  2. package/dist/command-launcher.js +24 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +126 -7
  5. package/dist/microsoft-store-submission.js +11 -6
  6. package/dist/native-build-info.json +2 -2
  7. package/dist/native-build-target.js +2121 -350
  8. package/dist/windows-host/Core/BridgeCommand.cs +17 -0
  9. package/dist/windows-host/Core/Mpgd.MicrosoftStore.Host.csproj +9 -0
  10. package/dist/windows-host/Core/NativeBridgeContracts.cs +33 -0
  11. package/dist/windows-host/Core/NativeBridgeHost.cs +162 -0
  12. package/dist/windows-host/Core/NativeBridgeOrigin.cs +19 -0
  13. package/dist/windows-host/Core/NativeBridgeProtocol.cs +335 -0
  14. package/dist/windows-host/Core/NativeBridgeRequestGate.cs +76 -0
  15. package/dist/windows-host/Core/NativeBridgeResult.cs +65 -0
  16. package/dist/windows-host/README.md +118 -0
  17. package/dist/windows-host/WinUI/DispatcherQueueBridgeDispatcher.cs +39 -0
  18. package/dist/windows-host/WinUI/WebView2DocumentTransport.cs +46 -0
  19. package/dist/windows-host/WinUI/WebView2NativeBridge.cs +185 -0
  20. package/dist/windows-host/WinUI/WindowStoreContext.cs +20 -0
  21. package/dist/windows-native-tools.d.ts +4 -0
  22. package/dist/windows-native-tools.js +21 -0
  23. package/native-tools/windows-msix/WindowsMsixAcceptance.psm1 +333 -0
  24. package/package.json +24 -16
  25. package/templates/lite-overlay/.agents/skills/use-mpgd-kit/SKILL.md +22 -0
  26. package/templates/lite-overlay/AGENTS.md +27 -0
  27. package/templates/lite-overlay/README.md +37 -0
  28. package/templates/lite-overlay/agent/acceptance.md +27 -0
  29. package/templates/lite-overlay/agent/brief.md +17 -0
  30. package/templates/lite-overlay/apps/target-devvit/README.md +134 -0
  31. package/templates/lite-overlay/docs/MPGD_KIT_WORKFLOWS.md +37 -0
  32. package/templates/lite-overlay/src/lite/tapGame.ts +77 -0
  33. package/templates/lite-overlay/src/platform/devvitEntrypoint.ts +116 -0
  34. package/templates/lite-overlay/src/platform/devvitInlineMode.css +146 -0
  35. package/templates/lite-overlay/src/runtime/createGame.ts +57 -0
  36. package/templates/phaser-game/README.md +5 -3
  37. package/templates/phaser-game/apps/target-ait/package.json +2 -2
  38. package/templates/phaser-game/apps/target-ait/src/main.ts +5 -5
  39. package/templates/phaser-game/apps/target-devvit/src/server/bridge.ts +137 -18
  40. package/templates/phaser-game/package.json +1 -1
  41. package/templates/phaser-game/pnpm-workspace.yaml +2 -0
  42. package/templates/phaser-game/src/env.d.ts +2 -0
  43. package/templates/phaser-game/src/platform/buildGateways/ait.ts +1 -1
  44. package/templates/phaser-game/src/platform/buildGateways/aitSandbox.ts +9 -4
  45. package/templates/phaser-game/src/platform/buildGateways/microsoftStore.ts +2 -5
  46. package/templates/phaser-game/src/platform/buildGateways/reddit.ts +1 -1
  47. package/templates/phaser-game/src/platform/buildGateways/verse8.ts +1 -2
  48. package/templates/phaser-game/src/platform/verse8Agent8Client.ts +5 -6
  49. package/templates/phaser-game/vite.shared.ts +40 -1
@@ -0,0 +1,13 @@
1
+ export interface NativeCommandLaunch {
2
+ readonly command: string;
3
+ readonly args: readonly string[];
4
+ readonly shell?: boolean;
5
+ }
6
+ /** Launch pnpm directly; Windows .cmd shims require a shell and lose argument boundaries. */
7
+ export declare function resolveNativeCommandLaunch(input: {
8
+ readonly command: string;
9
+ readonly args: readonly string[];
10
+ readonly environment: NodeJS.ProcessEnv;
11
+ readonly platform?: NodeJS.Platform;
12
+ readonly nodeExecutable?: string;
13
+ }): NativeCommandLaunch;
@@ -0,0 +1,24 @@
1
+ /** Launch pnpm directly; Windows .cmd shims require a shell and lose argument boundaries. */
2
+ export function resolveNativeCommandLaunch(input) {
3
+ if ((input.platform ?? process.platform) !== 'win32') {
4
+ return { command: input.command, args: input.args };
5
+ }
6
+ if (input.command === './gradlew') {
7
+ return { command: 'gradlew.bat', args: input.args, shell: true };
8
+ }
9
+ if (input.command !== 'pnpm') {
10
+ return { command: input.command, args: input.args };
11
+ }
12
+ const pnpmEntry = input.environment.npm_execpath;
13
+ const pnpmBase = pnpmEntry?.split(/[\\/]/u).at(-1) ?? '';
14
+ if (pnpmEntry === undefined || !/^pnpm(?:\.exe|\.[cm]?js)$/iu.test(pnpmBase)) {
15
+ throw new Error('Windows builds require pnpm exec mpgd so npm_execpath resolves the pnpm executable or JavaScript entrypoint.');
16
+ }
17
+ if (/\.exe$/iu.test(pnpmEntry)) {
18
+ return { command: pnpmEntry, args: input.args };
19
+ }
20
+ return {
21
+ command: input.nodeExecutable ?? process.execPath,
22
+ args: [pnpmEntry, ...input.args],
23
+ };
24
+ }
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export { cloudflarePagesDeploymentProfiles, hostedPwaDeploymentHosts, isSupporte
15
15
  export { defaultOfflinePlaytestArtifactDir, defaultOfflinePlaytestMaximumBytes, defaultOfflinePlaytestOutputDir, offlinePlaytestSchemaVersion, runOfflinePlaytestPackaging, type OfflinePlaytestEvidence, type RunOfflinePlaytestPackagingInput, type RunOfflinePlaytestPackagingResult, } from './offline-playtest.js';
16
16
  export { assertMicrosoftStorePwaCacheTransition, assertMicrosoftStorePwaPrecacheUrl, assertMicrosoftStorePwaReleaseEvidence, inspectMicrosoftStorePwaBrowserCacheTransition, microsoftStorePwaCacheSchema, readMicrosoftStorePwaBrowserReleaseEvidence, requestMicrosoftStorePwaBrowserUpdate, resolveMicrosoftStorePwaScopedCacheName, type AssertMicrosoftStorePwaCacheTransitionInput, type InspectMicrosoftStorePwaCacheTransitionInput, type MicrosoftStorePwaBrowserPage, type MicrosoftStorePwaCachedIndexObservation, type MicrosoftStorePwaCacheTransitionEvidence, type MicrosoftStorePwaCacheTransitionObservation, type MicrosoftStorePwaReleaseEvidence, } from './microsoft-store-pwa-e2e.js';
17
17
  export { createMicrosoftStorePackageAcceptanceRuntime, microsoftStorePackageAcceptanceSchemaVersion, parseMicrosoftStorePackageIdentity, parseWindowsAppCertificationResult, renderMicrosoftStorePackageAcceptanceMarkdown, runMicrosoftStorePackageAcceptance, type CreateMicrosoftStorePackageAcceptanceRuntimeInput, type MicrosoftStorePackageAcceptanceEvidence, type MicrosoftStorePackageAcceptanceRuntime, type MicrosoftStorePackageIdentity, type RunMicrosoftStorePackageAcceptanceInput, } from './microsoft-store-package-acceptance.js';
18
+ export { getMicrosoftStoreNativeAcceptanceModulePath, getMicrosoftStoreNativeHostSourceDirectory, } from './windows-native-tools.js';
18
19
  export { createMicrosoftStorePackageGenerationRuntime, microsoftStorePackageGeneratorEndpoint, microsoftStorePackageGeneratorSourceRevision, microsoftStorePackageGenerationSchemaVersion, renderMicrosoftStorePackageGenerationMarkdown, runMicrosoftStorePackageGeneration, type CreateMicrosoftStorePackageGenerationRuntimeInput, type MicrosoftStoreAddressResolver, type MicrosoftStorePackageGenerationEvidence, type MicrosoftStorePackageGenerationRuntime, type MicrosoftStoreResolvedAddress, type RunMicrosoftStorePackageGenerationInput, } from './microsoft-store-package-generation.js';
19
20
  export { microsoftStoreSubmissionSchemaVersion, parseMicrosoftStoreSubmissionConfig, renderMicrosoftStoreSubmissionMarkdown, runMicrosoftStoreSubmissionPreflight, type MicrosoftStoreSubmissionConfig, type MicrosoftStoreSubmissionCommerce, type MicrosoftStoreSubmissionCommerceProduct, type MicrosoftStoreSubmissionEvidence, type RunMicrosoftStoreSubmissionPreflightInput, } from './microsoft-store-submission.js';
20
21
  export { assertProductionTargetReadiness, type ProductionTargetReadinessInput, } from './production-target-readiness.js';
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import i18n, { defineI18n } from '@gunshi/plugin-i18n';
7
7
  import resources from '@gunshi/resources';
8
8
  import { cli } from 'gunshi';
9
9
  import { assertPlatformVersionLedger } from '@mpgd/target-config';
10
+ import { resolveNativeCommandLaunch } from './command-launcher.js';
10
11
  import { buildAssetPacks } from './asset-pack-build.js';
11
12
  import { verifyAssetPackDelivery } from './asset-pack-verify.js';
12
13
  import { buildAssetPackTarget } from './asset-pack-target.js';
@@ -47,6 +48,7 @@ export { cloudflarePagesDeploymentProfiles, hostedPwaDeploymentHosts, isSupporte
47
48
  export { defaultOfflinePlaytestArtifactDir, defaultOfflinePlaytestMaximumBytes, defaultOfflinePlaytestOutputDir, offlinePlaytestSchemaVersion, runOfflinePlaytestPackaging, } from './offline-playtest.js';
48
49
  export { assertMicrosoftStorePwaCacheTransition, assertMicrosoftStorePwaPrecacheUrl, assertMicrosoftStorePwaReleaseEvidence, inspectMicrosoftStorePwaBrowserCacheTransition, microsoftStorePwaCacheSchema, readMicrosoftStorePwaBrowserReleaseEvidence, requestMicrosoftStorePwaBrowserUpdate, resolveMicrosoftStorePwaScopedCacheName, } from './microsoft-store-pwa-e2e.js';
49
50
  export { createMicrosoftStorePackageAcceptanceRuntime, microsoftStorePackageAcceptanceSchemaVersion, parseMicrosoftStorePackageIdentity, parseWindowsAppCertificationResult, renderMicrosoftStorePackageAcceptanceMarkdown, runMicrosoftStorePackageAcceptance, } from './microsoft-store-package-acceptance.js';
51
+ export { getMicrosoftStoreNativeAcceptanceModulePath, getMicrosoftStoreNativeHostSourceDirectory, } from './windows-native-tools.js';
50
52
  export { createMicrosoftStorePackageGenerationRuntime, microsoftStorePackageGeneratorEndpoint, microsoftStorePackageGeneratorSourceRevision, microsoftStorePackageGenerationSchemaVersion, renderMicrosoftStorePackageGenerationMarkdown, runMicrosoftStorePackageGeneration, } from './microsoft-store-package-generation.js';
51
53
  export { microsoftStoreSubmissionSchemaVersion, parseMicrosoftStoreSubmissionConfig, renderMicrosoftStoreSubmissionMarkdown, runMicrosoftStoreSubmissionPreflight, } from './microsoft-store-submission.js';
52
54
  export { assertProductionTargetReadiness, } from './production-target-readiness.js';
@@ -54,6 +56,18 @@ const sourceDir = path.dirname(fileURLToPath(import.meta.url));
54
56
  const packageRoot = resolvePackageRoot(sourceDir);
55
57
  const detectedKitRoot = resolveDefaultKitRoot();
56
58
  const gameTemplateDir = path.resolve(packageRoot, 'templates/phaser-game');
59
+ const liteTemplateOverlayDir = path.resolve(packageRoot, 'templates/lite-overlay');
60
+ const supportedGameTemplates = ['phaser', 'lite'];
61
+ /** Lite renderers implemented today. dom/svg are reserved extension points in @mpgd/lite-shell. */
62
+ const supportedLiteRenderers = ['canvas'];
63
+ /** Phaser-only base template files that the lite template does not emit. */
64
+ const liteTemplateRemovedFiles = new Set([
65
+ 'src/scenes/BootScene.ts',
66
+ 'src/scenes/LobbyScene.ts',
67
+ 'src/scenes/PlayScene.ts',
68
+ 'src/assets/manifest.ts',
69
+ 'src/assets/marker.svg',
70
+ ]);
57
71
  const cliVersion = readPackageVersion(packageRoot);
58
72
  const recommendedMatrixTargets = 'web,microsoft-store,verse8,ait,reddit';
59
73
  const recommendedMatrixTargetsWithoutMicrosoftStore = recommendedMatrixTargets
@@ -80,6 +94,7 @@ const mpgdTemplateDependencyPackages = [
80
94
  { name: '@mpgd/game-services', packageDir: 'packages/game-services' },
81
95
  { name: '@mpgd/game-runtime', packageDir: 'packages/game-runtime' },
82
96
  { name: '@mpgd/i18n', packageDir: 'packages/i18n' },
97
+ { name: '@mpgd/lite-shell', packageDir: 'packages/lite-shell' },
83
98
  { name: '@mpgd/phaser-assets', packageDir: 'packages/phaser-assets' },
84
99
  { name: '@mpgd/platform', packageDir: 'packages/platform' },
85
100
  { name: '@mpgd/target-config', packageDir: 'packages/target-config' },
@@ -551,8 +566,26 @@ const gameCommand = defineI18n({
551
566
  en: 'Include the Microsoft Store PWA target and release workflow.',
552
567
  ko: 'Microsoft Store PWA 타깃과 출시 워크플로를 포함합니다.',
553
568
  },
569
+ template: {
570
+ en: 'Game template: phaser (default) or lite (no Phaser, @mpgd/lite-shell).',
571
+ ko: '게임 템플릿: phaser(기본) 또는 lite(Phaser 없음, @mpgd/lite-shell).',
572
+ },
573
+ renderer: {
574
+ en: 'Lite template renderer. Only canvas is implemented today.',
575
+ ko: 'lite 템플릿 렌더러. 현재는 canvas만 지원합니다.',
576
+ },
554
577
  }),
555
578
  args: {
579
+ template: {
580
+ type: 'string',
581
+ required: false,
582
+ description: 'Game template: phaser (default) or lite.',
583
+ },
584
+ renderer: {
585
+ type: 'string',
586
+ required: false,
587
+ description: 'Lite template renderer: canvas (default).',
588
+ },
556
589
  directory: {
557
590
  type: 'positional',
558
591
  required: true,
@@ -603,7 +636,10 @@ const gameCommand = defineI18n({
603
636
  ? 'workspace:*'
604
637
  : readOptionalString(ctx.values['dependency-version']);
605
638
  const kitPath = readGameCreateKitPath(ctx.values, ctx.values.workspace === true);
639
+ const { template, renderer } = readGameCreateTemplate(readOptionalString(ctx.values.template), readOptionalString(ctx.values.renderer));
606
640
  createGameApp({
641
+ template,
642
+ ...(renderer === undefined ? {} : { renderer }),
607
643
  directory,
608
644
  ...(title === undefined ? {} : { title }),
609
645
  ...(packageName === undefined ? {} : { packageName }),
@@ -2755,9 +2791,88 @@ function normalizeLegalHtml(content, sourceFile) {
2755
2791
  }
2756
2792
  return content.endsWith('\n') ? content : `${content}\n`;
2757
2793
  }
2794
+ function readGameCreateTemplate(template, renderer) {
2795
+ const resolvedTemplate = template ?? 'phaser';
2796
+ if (!supportedGameTemplates.includes(resolvedTemplate)) {
2797
+ throw new Error(`Unsupported --template ${resolvedTemplate}; expected one of: ${supportedGameTemplates.join(', ')}.`);
2798
+ }
2799
+ if (resolvedTemplate !== 'lite') {
2800
+ if (renderer !== undefined) {
2801
+ throw new Error('--renderer is only available with --template lite.');
2802
+ }
2803
+ return { template: resolvedTemplate };
2804
+ }
2805
+ const resolvedRenderer = renderer ?? 'canvas';
2806
+ if (!supportedLiteRenderers.includes(resolvedRenderer)) {
2807
+ throw new Error(`Unsupported --renderer ${resolvedRenderer}; implemented lite renderers: ${supportedLiteRenderers.join(', ')}. `
2808
+ + 'dom and svg are planned LiteRenderer implementations.');
2809
+ }
2810
+ return { template: 'lite', renderer: resolvedRenderer };
2811
+ }
2812
+ /** Apply the lite overlay to base template files: drop Phaser-only files and swap renderers. */
2813
+ function applyLiteTemplateOverlay(baseFiles, overlayFiles) {
2814
+ const overlay = new Map(overlayFiles.map((file) => [file.relativePath, file]));
2815
+ const merged = baseFiles.flatMap((file) => {
2816
+ if (liteTemplateRemovedFiles.has(file.relativePath)) {
2817
+ return [];
2818
+ }
2819
+ const replacement = overlay.get(file.relativePath);
2820
+ overlay.delete(file.relativePath);
2821
+ if (replacement !== undefined) {
2822
+ return [replacement];
2823
+ }
2824
+ if (file.relativePath === 'package.json') {
2825
+ return [{ ...file, content: liteTemplatePackageJson(file.content) }];
2826
+ }
2827
+ if (file.relativePath === 'agent/game-manifest.json') {
2828
+ return [{ ...file, content: liteTemplateGameManifest(file.content) }];
2829
+ }
2830
+ if (file.relativePath === 'mpgd.targets.json') {
2831
+ return [{ ...file, content: liteTemplateTargets(file.content) }];
2832
+ }
2833
+ return [file];
2834
+ });
2835
+ return [...merged, ...overlay.values()];
2836
+ }
2837
+ function liteTemplateTargets(content) {
2838
+ const value = JSON.parse(content);
2839
+ const reddit = value.targets.reddit;
2840
+ if (reddit !== undefined) {
2841
+ reddit.inlineEntry = 'index.html';
2842
+ reddit.inlineBudget = {
2843
+ maxGzipKb: 40,
2844
+ forbidModules: ['phaser', 'matter-js'],
2845
+ deferredChunks: ['main.js', 'matter.js', 'assets/main-*.js', 'assets/matter-*.js'],
2846
+ };
2847
+ }
2848
+ return `${JSON.stringify(value, null, 2)}\n`;
2849
+ }
2850
+ function liteTemplatePackageJson(content) {
2851
+ const value = JSON.parse(content);
2852
+ value.description = (value.description ?? '').replace('mpgd Phaser game', 'mpgd lite (Canvas2D) game');
2853
+ const dependencies = { ...value.dependencies };
2854
+ delete dependencies.phaser;
2855
+ delete dependencies['@mpgd/phaser-assets'];
2856
+ dependencies['@mpgd/lite-shell'] = '__MPGD_DEPENDENCY_VERSION_LITE_SHELL__';
2857
+ value.dependencies = Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)));
2858
+ return `${JSON.stringify(value, null, 2)}\n`;
2859
+ }
2860
+ function liteTemplateGameManifest(content) {
2861
+ const value = JSON.parse(content);
2862
+ if (value.game !== undefined) {
2863
+ value.game.engine = 'lite-canvas';
2864
+ }
2865
+ value.blocks = (value.blocks ?? []).map((block) => block.id === 'assets.manifest.phaser-loader'
2866
+ ? { id: 'render.lite-shell.canvas', owner: 'src/lite/tapGame.ts' }
2867
+ : block);
2868
+ return `${JSON.stringify(value, null, 2)}\n`;
2869
+ }
2758
2870
  function createGameApp(input) {
2759
2871
  const appDir = resolveAppDirectory(input.directory);
2760
2872
  const gameName = path.basename(appDir);
2873
+ if (input.template === 'lite' && !input.workspace) {
2874
+ throw new Error('The lite template requires --workspace until @mpgd/lite-shell is published.');
2875
+ }
2761
2876
  assertValidGameName(gameName);
2762
2877
  const packageName = input.packageName ?? gameName;
2763
2878
  assertValidPackageName(packageName);
@@ -2777,7 +2892,11 @@ function createGameApp(input) {
2777
2892
  ...(input.title === undefined ? {} : { title: input.title }),
2778
2893
  microsoftStore: input.microsoftStore,
2779
2894
  });
2780
- const files = collectTemplateFiles(gameTemplateDir).flatMap((file) => {
2895
+ const baseTemplateFiles = collectTemplateFiles(gameTemplateDir);
2896
+ const selectedTemplateFiles = input.template === 'lite'
2897
+ ? applyLiteTemplateOverlay(baseTemplateFiles, collectTemplateFiles(liteTemplateOverlayDir))
2898
+ : baseTemplateFiles;
2899
+ const files = selectedTemplateFiles.flatMap((file) => {
2781
2900
  const prepared = prepareBaseGameTemplateFile({
2782
2901
  relativePath: file.relativePath,
2783
2902
  content: file.content,
@@ -3037,9 +3156,10 @@ function renderTemplate(content, context) {
3037
3156
  return rendered;
3038
3157
  }
3039
3158
  function templateOutputPath(relativePath) {
3040
- return path.basename(relativePath) === 'gitignore'
3041
- ? path.join(path.dirname(relativePath), '.gitignore')
3042
- : relativePath;
3159
+ const normalized = relativePath.replace(/\\/gu, '/');
3160
+ return path.posix.basename(normalized) === 'gitignore'
3161
+ ? path.posix.join(path.posix.dirname(normalized), '.gitignore')
3162
+ : normalized;
3043
3163
  }
3044
3164
  function toPascalCase(value) {
3045
3165
  return value
@@ -3506,12 +3626,11 @@ function runPnpm(args, commandEnv) {
3506
3626
  if (kitPath === undefined || kitPath.length === 0) {
3507
3627
  throw new Error('Missing MPGD_KIT_PATH. Pass --kit-path or set MPGD_KIT_PATH.');
3508
3628
  }
3509
- const command = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
3510
- const result = spawnSync(command, [...args], {
3629
+ const launch = resolveNativeCommandLaunch({ command: 'pnpm', args, environment: commandEnv });
3630
+ const result = spawnSync(launch.command, [...launch.args], {
3511
3631
  cwd: kitPath,
3512
3632
  env: commandEnv,
3513
3633
  stdio: 'inherit',
3514
- shell: process.platform === 'win32',
3515
3634
  });
3516
3635
  if (result.error !== undefined) {
3517
3636
  throw result.error;
@@ -3,6 +3,7 @@ import { closeSync, existsSync, fstatSync, lstatSync, openSync, readFileSync, re
3
3
  import { isIP } from 'node:net';
4
4
  import path from 'node:path';
5
5
  import { inflateSync } from 'node:zlib';
6
+ import { normalizeRootRelativeRoute } from '@mpgd/platform/routes';
6
7
  import { escapeMarkdownInline, escapeMarkdownTable, formatError, relativeOrAbsolute, } from './evidence-io.js';
7
8
  import { isMicrosoftStoreSupportedListingLocale } from './microsoft-store-supported-locales.js';
8
9
  export const microsoftStoreSubmissionSchemaVersion = 1;
@@ -445,14 +446,18 @@ function parseListings(input) {
445
446
  }];
446
447
  }));
447
448
  }
449
+ /**
450
+ * An artifact-relative URL: `.` and then a root-relative route already in the
451
+ * form `normalizeRootRelativeRoute` from `@mpgd/platform/routes` returns, the
452
+ * rule share links and notification routes follow, so no client reads it as
453
+ * another host, even once it percent-decodes the path. The manifest ships in
454
+ * the artifact and the Store resolves these against the manifest's own URL,
455
+ * so an absolute URL is never needed.
456
+ */
448
457
  function requireManifestUrl(input, label) {
449
458
  const value = requireNonEmptyString(input, label);
450
- const base = new URL('https://mpgd.invalid/');
451
- const url = new URL(value, base);
452
- if (!value.startsWith('./')
453
- || url.origin !== base.origin
454
- || url.pathname.startsWith('//')
455
- || value !== `.${url.pathname}${url.search}${url.hash}`) {
459
+ const route = value.startsWith('./') ? value.slice(1) : undefined;
460
+ if (route === undefined || normalizeRootRelativeRoute(route) !== route) {
456
461
  throw new Error(`${label} must be an artifact-relative URL beginning with ./`);
457
462
  }
458
463
  return value;
@@ -1,5 +1,5 @@
1
1
  {
2
- "kitGitSha": "a54c0341749bb93a97ddd927205de960294c6511",
2
+ "kitGitSha": "926ff936dc36d2112c042185974dc3c33102377d",
3
3
  "kitDirty": false,
4
- "packageVersion": "0.47.0"
4
+ "packageVersion": "0.48.0"
5
5
  }