@voobase/cli 0.1.1-nonprod.2039 → 0.1.1-nonprod.2050

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 (109) hide show
  1. package/dist-bundle/index.mjs +69 -61
  2. package/package.json +9 -6
  3. package/runtime-sdk/dist/auth/client.d.ts +14 -0
  4. package/runtime-sdk/dist/auth/client.js +28 -0
  5. package/runtime-sdk/dist/blob/client.d.ts +39 -0
  6. package/runtime-sdk/dist/blob/client.js +148 -0
  7. package/runtime-sdk/dist/blob/client.test.d.ts +1 -0
  8. package/runtime-sdk/dist/blob/client.test.js +494 -0
  9. package/runtime-sdk/dist/browser/app.d.ts +142 -0
  10. package/runtime-sdk/dist/browser/app.js +256 -0
  11. package/runtime-sdk/dist/browser/app.test.d.ts +1 -0
  12. package/runtime-sdk/dist/browser/app.test.js +81 -0
  13. package/runtime-sdk/dist/browser/errors.d.ts +39 -0
  14. package/runtime-sdk/dist/browser/errors.js +265 -0
  15. package/runtime-sdk/dist/browser/errors.test.d.ts +1 -0
  16. package/runtime-sdk/dist/browser/errors.test.js +41 -0
  17. package/runtime-sdk/dist/browser.d.ts +5 -0
  18. package/runtime-sdk/dist/browser.js +5 -0
  19. package/runtime-sdk/dist/client.d.ts +16 -0
  20. package/runtime-sdk/dist/client.js +22 -0
  21. package/runtime-sdk/dist/cms/client.d.ts +39 -0
  22. package/runtime-sdk/dist/cms/client.js +56 -0
  23. package/runtime-sdk/dist/cms/client.test.d.ts +1 -0
  24. package/runtime-sdk/dist/cms/client.test.js +56 -0
  25. package/runtime-sdk/dist/cms/media.d.ts +10 -0
  26. package/runtime-sdk/dist/cms/media.js +113 -0
  27. package/runtime-sdk/dist/cms/media.test.d.ts +1 -0
  28. package/runtime-sdk/dist/cms/media.test.js +76 -0
  29. package/runtime-sdk/dist/cms/taxonomy.d.ts +8 -0
  30. package/runtime-sdk/dist/cms/taxonomy.js +77 -0
  31. package/runtime-sdk/dist/cms/taxonomy.test.d.ts +1 -0
  32. package/runtime-sdk/dist/cms/taxonomy.test.js +65 -0
  33. package/runtime-sdk/dist/data-form/client.d.ts +19 -0
  34. package/runtime-sdk/dist/data-form/client.js +118 -0
  35. package/runtime-sdk/dist/data-form/client.test.d.ts +1 -0
  36. package/runtime-sdk/dist/data-form/client.test.js +210 -0
  37. package/runtime-sdk/dist/data-form/contracts.d.ts +10 -0
  38. package/runtime-sdk/dist/data-form/contracts.js +33 -0
  39. package/runtime-sdk/dist/data-form/index.d.ts +3 -0
  40. package/runtime-sdk/dist/data-form/index.js +2 -0
  41. package/runtime-sdk/dist/data-form/types.d.ts +110 -0
  42. package/runtime-sdk/dist/data-form/types.js +1 -0
  43. package/runtime-sdk/dist/http.d.ts +27 -0
  44. package/runtime-sdk/dist/http.js +126 -0
  45. package/runtime-sdk/dist/index.d.ts +19 -0
  46. package/runtime-sdk/dist/index.js +14 -0
  47. package/runtime-sdk/dist/notification/client.d.ts +16 -0
  48. package/runtime-sdk/dist/notification/client.js +39 -0
  49. package/runtime-sdk/dist/notification/index.d.ts +1 -0
  50. package/runtime-sdk/dist/notification/index.js +1 -0
  51. package/runtime-sdk/dist/react/context.d.ts +18 -0
  52. package/runtime-sdk/dist/react/context.js +28 -0
  53. package/runtime-sdk/dist/react/hooks.d.ts +90 -0
  54. package/runtime-sdk/dist/react/hooks.js +172 -0
  55. package/runtime-sdk/dist/react.d.ts +2 -0
  56. package/runtime-sdk/dist/react.js +2 -0
  57. package/runtime-sdk/dist/session/browser.d.ts +25 -0
  58. package/runtime-sdk/dist/session/browser.js +60 -0
  59. package/runtime-sdk/dist/types/auth.d.ts +38 -0
  60. package/runtime-sdk/dist/types/auth.js +1 -0
  61. package/runtime-sdk/dist/types/cms.d.ts +93 -0
  62. package/runtime-sdk/dist/types/cms.js +1 -0
  63. package/runtime-sdk/dist/types/notification.d.ts +37 -0
  64. package/runtime-sdk/dist/types/notification.js +1 -0
  65. package/runtime-sdk/dist/types/object-storage.d.ts +58 -0
  66. package/runtime-sdk/dist/types/object-storage.js +1 -0
  67. package/runtime-sdk/dist/types/workflow.d.ts +52 -0
  68. package/runtime-sdk/dist/types/workflow.js +1 -0
  69. package/runtime-sdk/dist/workflow/client.d.ts +30 -0
  70. package/runtime-sdk/dist/workflow/client.js +45 -0
  71. package/scaffold-assets-manifest.json +429 -0
  72. package/template-blocks/assets.ts +95 -0
  73. package/template-blocks/contract/capabilities.ts +33 -0
  74. package/template-blocks/contract/facade-contract.ts +59 -0
  75. package/template-blocks/contract/index.ts +4 -0
  76. package/template-blocks/contract/path-builders.ts +76 -0
  77. package/template-blocks/contract/route-path-patterns-source.ts +222 -0
  78. package/template-blocks/contract/route-path-patterns.ts +210 -0
  79. package/template-blocks/contract/runtime-config.ts +256 -0
  80. package/template-blocks/dev-support/index.ts +2 -0
  81. package/template-blocks/dev-support/node/auth-shell.ts +31 -0
  82. package/template-blocks/dev-support/node/index.ts +3 -0
  83. package/template-blocks/dev-support/node/proxy.ts +133 -0
  84. package/template-blocks/dev-support/node/runtime-config.ts +163 -0
  85. package/template-blocks/dev-support/vite/auth-shell.ts +23 -0
  86. package/template-blocks/dev-support/vite/index.ts +3 -0
  87. package/template-blocks/dev-support/vite/proxy.ts +451 -0
  88. package/template-blocks/dev-support/vite/runtime-config.ts +107 -0
  89. package/template-blocks/legacy/app-core.ts +153 -0
  90. package/template-blocks/legacy/index.ts +10 -0
  91. package/template-blocks/legacy/lifecycle.ts +38 -0
  92. package/template-blocks/legacy/metadata.ts +59 -0
  93. package/template-blocks/legacy/pages.ts +156 -0
  94. package/template-blocks/legacy/react-lock.ts +1599 -0
  95. package/template-blocks/legacy/react.ts +823 -0
  96. package/template-blocks/legacy/tailwind.ts +1138 -0
  97. package/template-blocks/legacy/wrappers/auth.ts +105 -0
  98. package/template-blocks/legacy/wrappers/proxy.ts +250 -0
  99. package/template-blocks/legacy/wrappers/runtime.ts +535 -0
  100. package/template-blocks/protocol/auth-routes.ts +82 -0
  101. package/template-blocks/protocol/cms-routes.ts +81 -0
  102. package/template-blocks/protocol/index.ts +4 -0
  103. package/template-blocks/protocol/public-app-routes.ts +48 -0
  104. package/template-blocks/protocol/route-map.ts +75 -0
  105. package/template-blocks/routing.ts +62 -0
  106. package/template-blocks/server/html-injection.ts +45 -0
  107. package/template-blocks/server/proxy-routing.ts +65 -0
  108. package/template-blocks/server/request-context.ts +103 -0
  109. package/template-blocks/server/route-strip.ts +47 -0
@@ -0,0 +1,95 @@
1
+ export function buildWebBackendLiteRuntimeAssetsSource(): string {
2
+ return [
3
+ "import { existsSync, readFileSync } from \"node:fs\";",
4
+ "",
5
+ "export function readRuntimeAsset(relativePath) {",
6
+ " if (!/^[a-zA-Z0-9_./-]+$/.test(relativePath) || relativePath.includes(\"..\")) {",
7
+ " return null;",
8
+ " }",
9
+ " try {",
10
+ " const fileUrl = new URL(`../../vendor/runtime-sdk/${relativePath}`, import.meta.url);",
11
+ " if (!existsSync(fileUrl)) {",
12
+ " return null;",
13
+ " }",
14
+ " return readFileSync(fileUrl, \"utf8\");",
15
+ " } catch {",
16
+ " return null;",
17
+ " }",
18
+ "}",
19
+ ""
20
+ ].join("\n");
21
+ }
22
+
23
+ export function buildWebBackendLiteCmsContentPresetCssSource(): string {
24
+ return [
25
+ "/* Platform CMS content preset */",
26
+ "/* Usage: <link rel=\"stylesheet\" href=\"./assets/cms-content.css\" /> and wrap HTML with <div class=\"cms-content\">...</div> */",
27
+ ".cms-content {",
28
+ " color: #243431;",
29
+ " line-height: 1.85;",
30
+ " font-size: 1rem;",
31
+ "}",
32
+ ".cms-content > :first-child { margin-top: 0; }",
33
+ ".cms-content > :last-child { margin-bottom: 0; }",
34
+ ".cms-content h1, .cms-content h2, .cms-content h3, .cms-content h4, .cms-content h5, .cms-content h6 {",
35
+ " color: #13211f;",
36
+ " font-weight: 800;",
37
+ " line-height: 1.2;",
38
+ " letter-spacing: -0.03em;",
39
+ " margin-top: 1.6em;",
40
+ " margin-bottom: 0.6em;",
41
+ "}",
42
+ ".cms-content h1 { font-size: 2rem; }",
43
+ ".cms-content h2 { font-size: 1.55rem; }",
44
+ ".cms-content h3 { font-size: 1.2rem; }",
45
+ ".cms-content h4, .cms-content h5, .cms-content h6 { font-size: 1rem; }",
46
+ ".cms-content p, .cms-content ul, .cms-content ol, .cms-content blockquote, .cms-content pre, .cms-content table, .cms-content hr {",
47
+ " margin-top: 1em;",
48
+ " margin-bottom: 1em;",
49
+ "}",
50
+ ".cms-content ul, .cms-content ol { padding-left: 1.5rem; }",
51
+ ".cms-content ul { list-style: disc; }",
52
+ ".cms-content ol { list-style: decimal; }",
53
+ ".cms-content li + li { margin-top: 0.35rem; }",
54
+ ".cms-content li > p { margin-top: 0; margin-bottom: 0; }",
55
+ ".cms-content li > ul, .cms-content li > ol { margin-top: 0.5rem; margin-bottom: 0; }",
56
+ ".cms-content blockquote {",
57
+ " margin-left: 0;",
58
+ " margin-right: 0;",
59
+ " padding: 0.8rem 1rem;",
60
+ " border-left: 3px solid rgba(15,23,42,0.12);",
61
+ " background: rgba(18, 60, 51, 0.06);",
62
+ " color: #5f6f69;",
63
+ "}",
64
+ ".cms-content hr { border: 0; border-top: 1px solid rgba(15,23,42,0.12); }",
65
+ ".cms-content a { color: #2563eb; text-decoration: underline; text-underline-offset: 0.18em; }",
66
+ ".cms-content code {",
67
+ " font-family: ui-monospace, SFMono-Regular, Menlo, monospace;",
68
+ " font-size: 0.875em;",
69
+ " line-height: 1.45;",
70
+ " padding: 0.15rem 0.35rem;",
71
+ " border-radius: 0.375rem;",
72
+ " background: rgba(18, 60, 51, 0.08);",
73
+ "}",
74
+ ".cms-content pre {",
75
+ " overflow-x: auto;",
76
+ " margin-top: 0.75rem;",
77
+ " margin-bottom: 0.75rem;",
78
+ " padding: 0.8rem 1rem;",
79
+ " border: 1px solid rgba(15,23,42,0.12);",
80
+ " border-radius: 0.9rem;",
81
+ " background: rgba(18, 60, 51, 0.06);",
82
+ "}",
83
+ ".cms-content pre code { padding: 0; border-radius: 0; background: transparent; }",
84
+ ".cms-content table { width: 100%; border-collapse: collapse; font-size: 0.95rem; overflow: hidden; border-radius: 16px; }",
85
+ ".cms-content th, .cms-content td {",
86
+ " padding: 0.7rem 0.85rem;",
87
+ " border: 1px solid rgba(15,23,42,0.12);",
88
+ " text-align: left;",
89
+ " vertical-align: top;",
90
+ "}",
91
+ ".cms-content th { font-weight: 700; color: #13211f; background: rgba(18, 60, 51, 0.08); }",
92
+ ".cms-content img { display: block; max-width: 100%; height: auto; margin: 1rem auto; border-radius: 0.9rem; }",
93
+ ""
94
+ ].join("\n");
95
+ }
@@ -0,0 +1,33 @@
1
+ export const PLATFORM_CAPABILITIES = [
2
+ "auth",
3
+ "cms",
4
+ "workflow",
5
+ "notification",
6
+ "blob"
7
+ ] as const;
8
+
9
+ export type PlatformCapability = (typeof PLATFORM_CAPABILITIES)[number];
10
+
11
+ export function isPlatformCapability(value: string): value is PlatformCapability {
12
+ return PLATFORM_CAPABILITIES.includes(value as PlatformCapability);
13
+ }
14
+
15
+ export function normalizePlatformCapabilities(
16
+ values: readonly string[] | null | undefined
17
+ ): PlatformCapability[] {
18
+ if (!values || values.length === 0) {
19
+ return [];
20
+ }
21
+ const normalized = new Set<PlatformCapability>();
22
+ for (const value of values) {
23
+ if (!value) {
24
+ continue;
25
+ }
26
+ const trimmed = value.trim();
27
+ if (!trimmed || !isPlatformCapability(trimmed)) {
28
+ continue;
29
+ }
30
+ normalized.add(trimmed);
31
+ }
32
+ return [...normalized];
33
+ }
@@ -0,0 +1,59 @@
1
+ import type { PlatformCapability } from "./capabilities.js";
2
+ import type { PlatformRuntimeConfig } from "./runtime-config.js";
3
+
4
+ export type PlatformApiQueryValue =
5
+ | string
6
+ | number
7
+ | boolean
8
+ | null
9
+ | undefined;
10
+
11
+ export type PlatformApiRequestInit = RequestInit & {
12
+ query?: Record<string, PlatformApiQueryValue>;
13
+ };
14
+
15
+ export type PlatformApiFetcher = <TResponse = unknown>(
16
+ path: string,
17
+ init?: PlatformApiRequestInit
18
+ ) => Promise<TResponse>;
19
+
20
+ export type PlatformCapabilityState = {
21
+ capability: PlatformCapability;
22
+ ready: boolean;
23
+ };
24
+
25
+ export interface PlatformCapabilityFacade {
26
+ readonly capability: PlatformCapability;
27
+ isReady(): boolean;
28
+ getState(): PlatformCapabilityState;
29
+ }
30
+
31
+ export interface AuthFacade extends PlatformCapabilityFacade {
32
+ readonly capability: "auth";
33
+ }
34
+
35
+ export interface CmsFacade extends PlatformCapabilityFacade {
36
+ readonly capability: "cms";
37
+ }
38
+
39
+ export interface WorkflowFacade extends PlatformCapabilityFacade {
40
+ readonly capability: "workflow";
41
+ }
42
+
43
+ export interface NotificationFacade extends PlatformCapabilityFacade {
44
+ readonly capability: "notification";
45
+ }
46
+
47
+ export interface BlobFacade extends PlatformCapabilityFacade {
48
+ readonly capability: "blob";
49
+ }
50
+
51
+ export interface PlatformClientContract {
52
+ readonly config: PlatformRuntimeConfig;
53
+ fetchPlatformApi: PlatformApiFetcher;
54
+ auth: AuthFacade;
55
+ cms: CmsFacade;
56
+ workflow: WorkflowFacade;
57
+ notification: NotificationFacade;
58
+ blob: BlobFacade;
59
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./capabilities.js";
2
+ export * from "./facade-contract.js";
3
+ export * from "./path-builders.js";
4
+ export * from "./runtime-config.js";
@@ -0,0 +1,76 @@
1
+ import type { PlatformRuntimeConfig } from "./runtime-config.js";
2
+
3
+ export type PlatformPathContext = Pick<PlatformRuntimeConfig, "basePath">;
4
+
5
+ export type PlatformAbsoluteUrlContext = Pick<
6
+ PlatformRuntimeConfig,
7
+ "basePath" | "requestOrigin" | "publicBaseUrl" | "baseUrl"
8
+ >;
9
+
10
+ export function normalizeBasePath(basePath: string): string {
11
+ const trimmed = basePath.trim();
12
+ if (!trimmed || trimmed === "/") {
13
+ return "/";
14
+ }
15
+
16
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
17
+ return withLeadingSlash.replace(/\/+$/u, "") || "/";
18
+ }
19
+
20
+ export function buildPlatformPath(
21
+ config: PlatformPathContext,
22
+ path = "/"
23
+ ): string {
24
+ const normalizedBasePath = normalizeBasePath(config.basePath);
25
+ const normalizedBase = normalizedBasePath === "/" ? "" : normalizedBasePath.replace(/\/+$/u, "");
26
+ const normalizedPath = normalizeRelativePath(path);
27
+
28
+ if (normalizedPath === "/") {
29
+ return normalizedBase || "/";
30
+ }
31
+
32
+ return `${normalizedBase}${normalizedPath}`;
33
+ }
34
+
35
+ export function buildPlatformAbsoluteUrl(
36
+ config: PlatformAbsoluteUrlContext,
37
+ path = "/"
38
+ ): string {
39
+ const origin = resolveRequestOrigin(config);
40
+ return new URL(buildPlatformPath(config, path), normalizeBaseUrl(origin)).toString();
41
+ }
42
+
43
+ export function buildPlatformAuthPath(config: PlatformPathContext): string {
44
+ return buildPlatformPath(config, "/auth");
45
+ }
46
+
47
+ function normalizeRelativePath(path: string): string {
48
+ if (!path || path === "/") {
49
+ return "/";
50
+ }
51
+
52
+ return path.startsWith("/") ? path : `/${path}`;
53
+ }
54
+
55
+ function normalizeBaseUrl(baseUrl: string): string {
56
+ const trimmed = baseUrl.trim();
57
+ return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
58
+ }
59
+
60
+ function resolveRequestOrigin(config: PlatformAbsoluteUrlContext): string {
61
+ const requestOrigin = config.requestOrigin?.trim();
62
+ if (requestOrigin) {
63
+ return requestOrigin;
64
+ }
65
+
66
+ const publicBaseUrl = config.publicBaseUrl?.trim();
67
+ if (publicBaseUrl) {
68
+ try {
69
+ return new URL(publicBaseUrl).origin;
70
+ } catch {
71
+ return publicBaseUrl;
72
+ }
73
+ }
74
+
75
+ return config.baseUrl;
76
+ }
@@ -0,0 +1,222 @@
1
+ import {
2
+ DNS_LABEL_ROUTE_SEGMENT_SOURCE,
3
+ TENANT_ROUTE_SEGMENT_SOURCE,
4
+ buildLegacyPreviewPathPattern,
5
+ buildProdPathPattern,
6
+ buildTenantPreviewPathPattern,
7
+ buildTypedPreviewPathPattern
8
+ } from "@platform/url-contract";
9
+
10
+ export function buildRoutePathPatternsSource(): string {
11
+ return `// Keep these scaffolded patterns aligned with @platform/url-contract path segment rules.
12
+ const ROUTE_SEGMENT_PATTERN = ${JSON.stringify(DNS_LABEL_ROUTE_SEGMENT_SOURCE)};
13
+ const TENANT_ROUTE_SEGMENT_PATTERN = ${JSON.stringify(TENANT_ROUTE_SEGMENT_SOURCE)};
14
+
15
+ export type PreviewRouteKind = "runtime" | "static";
16
+
17
+ export const PREVIEW_TYPED_PATH_PATTERN =
18
+ ${JSON.stringify(buildTypedPreviewPathPattern())};
19
+ export const PREVIEW_TENANT_PATH_PATTERN =
20
+ ${JSON.stringify(buildTenantPreviewPathPattern())};
21
+ export const PREVIEW_LEGACY_PATH_PATTERN =
22
+ ${JSON.stringify(buildLegacyPreviewPathPattern())};
23
+ export const PROD_PATH_PATTERN =
24
+ ${JSON.stringify(buildProdPathPattern())};
25
+
26
+ export type TypedPreviewPathMatch = {
27
+ routeKind: PreviewRouteKind;
28
+ tenantId: string;
29
+ appId: string;
30
+ releaseId: string;
31
+ routeBasePath: string;
32
+ upstreamPath: string;
33
+ };
34
+
35
+ export type TenantPreviewPathMatch = {
36
+ routeKind: "runtime";
37
+ tenantId: string;
38
+ appId: string;
39
+ releaseId: string;
40
+ routeBasePath: string;
41
+ upstreamPath: string;
42
+ };
43
+
44
+ export type LegacyPreviewPathMatch = {
45
+ routeKind: "static";
46
+ appId: string;
47
+ releaseId: string;
48
+ routeBasePath: string;
49
+ upstreamPath: string;
50
+ };
51
+
52
+ export type ProdPathMatch = {
53
+ routeKind?: PreviewRouteKind;
54
+ tenantId: string;
55
+ appId: string;
56
+ routeBasePath: string;
57
+ upstreamPath: string;
58
+ };
59
+
60
+ export type PlatformPathMatch =
61
+ | ({ kind: "typed-preview"; mode: "preview"; routeMode: "path" } & TypedPreviewPathMatch)
62
+ | ({ kind: "tenant-preview"; mode: "preview"; routeMode: "path" } & TenantPreviewPathMatch)
63
+ | ({ kind: "legacy-preview"; mode: "preview"; routeMode: "path"; tenantId: "" } & LegacyPreviewPathMatch)
64
+ | ({ kind: "prod-path"; mode: "prod-path"; routeMode: "path" } & ProdPathMatch);
65
+
66
+ export function buildInlineParsePlatformPathSource(functionName = "parsePlatformPath"): string {
67
+ return [
68
+ \`function \${functionName}(pathname) {\`,
69
+ \` const typedPreviewMatch = pathname.match(new RegExp(\${JSON.stringify(PREVIEW_TYPED_PATH_PATTERN)}));\`,
70
+ " if (typedPreviewMatch) {",
71
+ " const routeBasePath = '/previews/' + typedPreviewMatch[1] + '/' + typedPreviewMatch[2] + '/' + typedPreviewMatch[3] + '/' + typedPreviewMatch[4];",
72
+ " return { kind: 'typed-preview', mode: 'preview', routeMode: 'path', routeKind: typedPreviewMatch[1] === 's' ? 'static' : 'runtime', tenantId: typedPreviewMatch[2], appId: typedPreviewMatch[3], releaseId: typedPreviewMatch[4], routeBasePath, upstreamPath: inferInlineUpstreamPath(pathname, routeBasePath) };",
73
+ " }",
74
+ \` const tenantPreviewMatch = pathname.match(new RegExp(\${JSON.stringify(PREVIEW_TENANT_PATH_PATTERN)}));\`,
75
+ " if (tenantPreviewMatch) {",
76
+ " const routeBasePath = '/previews/' + tenantPreviewMatch[1] + '/' + tenantPreviewMatch[2] + '/' + tenantPreviewMatch[3];",
77
+ " return { kind: 'tenant-preview', mode: 'preview', routeMode: 'path', routeKind: 'runtime', tenantId: tenantPreviewMatch[1], appId: tenantPreviewMatch[2], releaseId: tenantPreviewMatch[3], routeBasePath, upstreamPath: inferInlineUpstreamPath(pathname, routeBasePath) };",
78
+ " }",
79
+ \` const previewMatch = pathname.match(new RegExp(\${JSON.stringify(PREVIEW_LEGACY_PATH_PATTERN)}));\`,
80
+ " if (previewMatch) {",
81
+ " const routeBasePath = '/previews/' + previewMatch[1] + '/' + previewMatch[2];",
82
+ " return { kind: 'legacy-preview', mode: 'preview', routeMode: 'path', routeKind: 'static', tenantId: '', appId: previewMatch[1], releaseId: previewMatch[2], routeBasePath, upstreamPath: inferInlineUpstreamPath(pathname, routeBasePath) };",
83
+ " }",
84
+ \` const prodMatch = pathname.match(new RegExp(\${JSON.stringify(PROD_PATH_PATTERN)}));\`,
85
+ " if (prodMatch) {",
86
+ " const routeBasePath = '/apps/' + prodMatch[1] + '/' + prodMatch[2];",
87
+ " return { kind: 'prod-path', mode: 'prod-path', routeMode: 'path', routeKind: 'runtime', tenantId: prodMatch[1], appId: prodMatch[2], routeBasePath, upstreamPath: inferInlineUpstreamPath(pathname, routeBasePath) };",
88
+ " }",
89
+ " return null;",
90
+ "}",
91
+ "function inferInlineUpstreamPath(pathname, routeBasePath) {",
92
+ " if (pathname === routeBasePath || pathname === routeBasePath + '/') {",
93
+ " return '/';",
94
+ " }",
95
+ " if (pathname.startsWith(routeBasePath + '/')) {",
96
+ " return pathname.slice(routeBasePath.length) || '/';",
97
+ " }",
98
+ " return '/';",
99
+ "}"
100
+ ].join("\\n");
101
+ }
102
+
103
+ export function parseTypedPreviewPath(pathname: string): TypedPreviewPathMatch | null {
104
+ const match = normalizePathname(pathname).match(new RegExp(PREVIEW_TYPED_PATH_PATTERN, "u"));
105
+ if (!match) {
106
+ return null;
107
+ }
108
+ const routeBasePath = \`/previews/\${match[1]}/\${match[2]}/\${match[3]}/\${match[4]}\`;
109
+ return {
110
+ routeKind: match[1] === "s" ? "static" : "runtime",
111
+ tenantId: match[2] ?? "",
112
+ appId: match[3] ?? "",
113
+ releaseId: match[4] ?? "",
114
+ routeBasePath,
115
+ upstreamPath: inferUpstreamPath(pathname, routeBasePath),
116
+ };
117
+ }
118
+
119
+ export function parseTenantPreviewPath(pathname: string): TenantPreviewPathMatch | null {
120
+ const match = normalizePathname(pathname).match(new RegExp(PREVIEW_TENANT_PATH_PATTERN, "u"));
121
+ if (!match) {
122
+ return null;
123
+ }
124
+ const routeBasePath = \`/previews/\${match[1]}/\${match[2]}/\${match[3]}\`;
125
+ return {
126
+ routeKind: "runtime",
127
+ tenantId: match[1] ?? "",
128
+ appId: match[2] ?? "",
129
+ releaseId: match[3] ?? "",
130
+ routeBasePath,
131
+ upstreamPath: inferUpstreamPath(pathname, routeBasePath),
132
+ };
133
+ }
134
+
135
+ export function parseLegacyPreviewPath(pathname: string): LegacyPreviewPathMatch | null {
136
+ const match = normalizePathname(pathname).match(new RegExp(PREVIEW_LEGACY_PATH_PATTERN, "u"));
137
+ if (!match) {
138
+ return null;
139
+ }
140
+ const routeBasePath = \`/previews/\${match[1]}/\${match[2]}\`;
141
+ return {
142
+ routeKind: "static",
143
+ appId: match[1] ?? "",
144
+ releaseId: match[2] ?? "",
145
+ routeBasePath,
146
+ upstreamPath: inferUpstreamPath(pathname, routeBasePath),
147
+ };
148
+ }
149
+
150
+ export function parseProdPath(pathname: string): ProdPathMatch | null {
151
+ const match = normalizePathname(pathname).match(new RegExp(PROD_PATH_PATTERN, "u"));
152
+ if (!match) {
153
+ return null;
154
+ }
155
+ const routeBasePath = \`/apps/\${match[1]}/\${match[2]}\`;
156
+ return {
157
+ routeKind: "runtime",
158
+ tenantId: match[1] ?? "",
159
+ appId: match[2] ?? "",
160
+ routeBasePath,
161
+ upstreamPath: inferUpstreamPath(pathname, routeBasePath),
162
+ };
163
+ }
164
+
165
+ export function parsePlatformPath(pathname: string): PlatformPathMatch | null {
166
+ const typedPreview = parseTypedPreviewPath(pathname);
167
+ if (typedPreview) {
168
+ return {
169
+ kind: "typed-preview",
170
+ mode: "preview",
171
+ routeMode: "path",
172
+ ...typedPreview
173
+ };
174
+ }
175
+ const tenantPreview = parseTenantPreviewPath(pathname);
176
+ if (tenantPreview) {
177
+ return {
178
+ kind: "tenant-preview",
179
+ mode: "preview",
180
+ routeMode: "path",
181
+ ...tenantPreview
182
+ };
183
+ }
184
+ const legacyPreview = parseLegacyPreviewPath(pathname);
185
+ if (legacyPreview) {
186
+ return {
187
+ kind: "legacy-preview",
188
+ mode: "preview",
189
+ routeMode: "path",
190
+ tenantId: "",
191
+ ...legacyPreview
192
+ };
193
+ }
194
+ const prodPath = parseProdPath(pathname);
195
+ if (prodPath) {
196
+ return {
197
+ kind: "prod-path",
198
+ mode: "prod-path",
199
+ routeMode: "path",
200
+ ...prodPath
201
+ };
202
+ }
203
+ return null;
204
+ }
205
+
206
+ function normalizePathname(pathname: string): string {
207
+ const trimmed = pathname.trim() || "/";
208
+ return trimmed.startsWith("/") ? trimmed : \`/\${trimmed}\`;
209
+ }
210
+
211
+ function inferUpstreamPath(pathname: string, routeBasePath: string): string {
212
+ const normalizedPathname = normalizePathname(pathname);
213
+ if (normalizedPathname === routeBasePath || normalizedPathname === \`\${routeBasePath}/\`) {
214
+ return "/";
215
+ }
216
+ if (normalizedPathname.startsWith(\`\${routeBasePath}/\`)) {
217
+ return normalizedPathname.slice(routeBasePath.length) || "/";
218
+ }
219
+ return "/";
220
+ }
221
+ `;
222
+ }