@columnus/shared 0.1.63 → 0.2.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.
@@ -1,16 +1,17 @@
1
1
  /** Parse and validate a git commit SHA (7–40 hex chars). */
2
2
  export declare function parseGitSha(raw: string | undefined, label?: string): string;
3
3
  export declare function defaultProdDeployUrl(domain?: string): string;
4
- /** True for local defaults that should not beat Coolify preview deploy URLs. */
4
+ /** True for local defaults that should not beat isolated preview deploy URLs. */
5
5
  export declare function isLoopbackDeployServiceUrl(url: string): boolean;
6
6
  /** Production shared deploy host (`deploy.columnus.app`), any path. */
7
7
  export declare function isProdDeployServiceUrl(url: string, domain?: string): boolean;
8
8
  /**
9
9
  * When on a PR preview host, return the per-PR deploy-service URL unless env already
10
- * points at a non-production executor (deploy-preview or Coolify internal).
10
+ * points at a non-production executor (deploy-preview or private cluster).
11
11
  *
12
- * Coolify often copies production `STUDIO_DEPLOY_URL=https://deploy.columnus.app/deploy`
12
+ * A deployment may copy production `STUDIO_DEPLOY_URL=https://deploy.columnus.app/deploy`
13
13
  * onto preview apps — that must not win over `deploy-preview-<pr>`.
14
+ * @throws When preview identity lacks a domain/origin and an explicit non-prod executor.
14
15
  */
15
16
  export declare function derivePreviewStudioDeployUrl(env?: NodeJS.ProcessEnv, domain?: string): string | null;
16
17
  /**
@@ -20,10 +21,9 @@ export declare function derivePreviewStudioDeployUrl(env?: NodeJS.ProcessEnv, do
20
21
  * else `STUDIO_DEPLOY_URL`, else loopback COLUMNUS, else local default.
21
22
  *
22
23
  * Callers that need an origin (no path) should normalize with deploy-service helpers.
24
+ * @throws For hostless preview identity without domain or explicit non-prod executor.
23
25
  */
24
26
  export declare function resolveRuntimeDeployServiceUrl(env?: NodeJS.ProcessEnv, domain?: string): string;
25
- /** Coolify internal URL for columnus-deploy preview container (build-time manifest sync). */
26
- export declare function derivePreviewDeployBuildUrl(env?: NodeJS.ProcessEnv): string | null;
27
27
  export type ResolveSyncDeployUrlResult = {
28
28
  ok: true;
29
29
  url: string;
@@ -42,10 +42,10 @@ export type PreviewSourceCommitResult = {
42
42
  ok: false;
43
43
  message: string;
44
44
  };
45
- /** Validates SOURCE_COMMIT for Coolify preview builds (skipped when CI_PREVIEW=true). */
45
+ /** Validates SOURCE_COMMIT for isolated preview builds (skipped when CI_PREVIEW=true). */
46
46
  export declare function validatePreviewSourceCommit(env?: NodeJS.ProcessEnv): PreviewSourceCommitResult;
47
47
  export declare function normalizeConvexUrl(url: string): string;
48
- /** DOCS_SEED_SYNC_EMAIL with PLAYWRIGHT_TEST_EMAIL fallback (Coolify / entrypoint). */
48
+ /** DOCS_SEED_SYNC_EMAIL with PLAYWRIGHT_TEST_EMAIL fallback (CI / entrypoint). */
49
49
  export declare function docsSeedSyncEmailFromEnv(env?: NodeJS.ProcessEnv): string | undefined;
50
50
  /**
51
51
  * Union comma-separated bootstrap admin emails with preview seed identities.
@@ -1,36 +1,34 @@
1
1
  // packages/@columnus/shared/src/previewDeployName.ts
2
- function hostnameFromCoolifyToken(raw) {
3
- const token = raw.trim();
4
- if (!token || /^https?:?$/i.test(token)) return null;
2
+ function previewHostnameFromEnv(env = process.env) {
3
+ const origin = resolveTrustedAuthOrigin(env);
4
+ if (!origin) return null;
5
5
  try {
6
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(token)) {
7
- return new URL(token).hostname || null;
8
- }
6
+ return new URL(origin).hostname;
9
7
  } catch {
10
8
  return null;
11
9
  }
12
- const host = token.split("/")[0]?.split(":")[0] ?? "";
13
- if (!host || !host.includes(".") && host !== "localhost") return null;
14
- return host;
15
- }
16
- function coolifyHostnameFromEnv(env = process.env) {
17
- const raw = env.COOLIFY_FQDN ?? env.COOLIFY_URL;
18
- if (!raw) return null;
19
- for (const part of raw.split(",")) {
20
- const host = hostnameFromCoolifyToken(part);
21
- if (host) return host;
22
- }
23
- return null;
10
+ }
11
+ function resolveTrustedAuthOrigin(env = process.env) {
12
+ const explicit = env.COLUMNUS_AUTH_ORIGIN?.trim();
13
+ if (explicit) return explicit.replace(/\/$/, "");
14
+ const siteUrl = env.SITE_URL?.trim();
15
+ if (siteUrl && !/\.convex\.site(?:\/|$)/i.test(siteUrl)) return siteUrl.replace(/\/$/, "");
16
+ return void 0;
24
17
  }
25
18
  var PULL_HEAD_BRANCH_RE = /^pull\/(\d+)\/head$/i;
26
19
  var PREVIEW_HOST_PR_RE = /^(?:manage-|deploy-|renderer-)?preview-(\d+)\./;
20
+ function previewZoneFromEnv(env = process.env) {
21
+ const host = previewHostnameFromEnv(env);
22
+ const prefix = host ? PREVIEW_HOST_PR_RE.exec(host)?.[0] : void 0;
23
+ return host && prefix ? host.slice(prefix.length) || null : null;
24
+ }
27
25
  function resolvePreviewPrId(env = process.env) {
28
- const host = coolifyHostnameFromEnv(env);
26
+ const host = previewHostnameFromEnv(env);
29
27
  if (host) {
30
28
  const fromHost = PREVIEW_HOST_PR_RE.exec(host)?.[1];
31
29
  if (fromHost) return fromHost;
32
30
  }
33
- const branch = env.COOLIFY_BRANCH ?? env.GIT_BRANCH;
31
+ const branch = env.GIT_BRANCH;
34
32
  if (branch) {
35
33
  const fromBranch = PULL_HEAD_BRANCH_RE.exec(stripQualifiedBranch(branch))?.[1];
36
34
  if (fromBranch) return fromBranch;
@@ -69,21 +67,24 @@ function isLoopbackDeployServiceUrl(url) {
69
67
  function isProdDeployServiceUrl(url, domain = "columnus.app") {
70
68
  return hostnameOf(url) === `deploy.${domain}`;
71
69
  }
72
- function derivePreviewStudioDeployUrl(env = process.env, domain = "columnus.app") {
70
+ function derivePreviewStudioDeployUrl(env = process.env, domain) {
73
71
  const prId = resolvePreviewPrId(env);
74
72
  if (!prId) return null;
75
73
  for (const key of ["COLUMNUS_DEPLOY_SERVICE_URL", "STUDIO_DEPLOY_URL"]) {
76
74
  const current = env[key]?.trim();
77
75
  if (!current) continue;
78
- if (isLoopbackDeployServiceUrl(current) || isProdDeployServiceUrl(current, domain)) {
76
+ if (isLoopbackDeployServiceUrl(current) || isProdDeployServiceUrl(current) || isProdDeployServiceUrl(current, domain || env.COLUMNUS_DOMAIN?.trim())) {
79
77
  continue;
80
78
  }
81
79
  return null;
82
80
  }
83
- return `https://deploy-preview-${prId}.${domain}/ops/v1/deploy-initial`;
81
+ const previewDomain = domain?.trim() || env.COLUMNUS_DOMAIN?.trim() || previewZoneFromEnv(env);
82
+ if (!previewDomain)
83
+ throw new Error("COLUMNUS_DOMAIN is required for hostless preview executor routing");
84
+ return `https://deploy-preview-${prId}.${previewDomain}/ops/v1/deploy-initial`;
84
85
  }
85
86
  var LOCAL_DEPLOY_SERVICE_DEFAULT = "http://127.0.0.1:8787";
86
- function resolveRuntimeDeployServiceUrl(env = process.env, domain = "columnus.app") {
87
+ function resolveRuntimeDeployServiceUrl(env = process.env, domain) {
87
88
  const derived = derivePreviewStudioDeployUrl(env, domain);
88
89
  if (derived) return derived;
89
90
  const columnus = env.COLUMNUS_DEPLOY_SERVICE_URL?.trim();
@@ -93,18 +94,6 @@ function resolveRuntimeDeployServiceUrl(env = process.env, domain = "columnus.ap
93
94
  if (columnus) return columnus;
94
95
  return LOCAL_DEPLOY_SERVICE_DEFAULT;
95
96
  }
96
- function derivePreviewDeployBuildUrl(env = process.env) {
97
- const prId = resolvePreviewPrId(env);
98
- if (!prId) return null;
99
- let uuid = env.COLUMNUS_DEPLOY_RESOURCE_UUID?.trim() ?? "";
100
- uuid = uuid.replace(/^https?:\/\//, "");
101
- uuid = uuid.split("/")[0] ?? "";
102
- uuid = uuid.split("-pr-")[0] ?? uuid;
103
- uuid = uuid.split(":")[0] ?? uuid;
104
- if (!uuid) return null;
105
- const port = env.COLUMNUS_DEPLOY_PREVIEW_PORT?.trim() || "3000";
106
- return `http://${uuid}-pr-${prId}:${port}/ops/v1/deploy-initial`;
107
- }
108
97
  function resolveSyncDeployUrl(env, studioDeployUrl, isPreviewBuild) {
109
98
  const explicit = env.STUDIO_DEPLOY_BUILD_URL?.trim();
110
99
  if (explicit) {
@@ -114,18 +103,7 @@ function resolveSyncDeployUrl(env, studioDeployUrl, isPreviewBuild) {
114
103
  return { ok: true, url: "http://127.0.0.1:8787/ops/v1/deploy-initial" };
115
104
  }
116
105
  if (isPreviewBuild) {
117
- const derived = derivePreviewDeployBuildUrl(env);
118
- if (derived) {
119
- return {
120
- ok: true,
121
- url: derived,
122
- derivedMessage: `derived STUDIO_DEPLOY_BUILD_URL=${derived} (from COLUMNUS_DEPLOY_RESOURCE_UUID + PR id)`
123
- };
124
- }
125
- return {
126
- ok: false,
127
- message: "Set COLUMNUS_DEPLOY_RESOURCE_UUID on columnus-web preview (columnus-deploy Coolify resource UUID) or STUDIO_DEPLOY_BUILD_URL explicitly. See plans/done/021-preview-deploy-service.md"
128
- };
106
+ return { ok: false, message: "Set STUDIO_DEPLOY_BUILD_URL explicitly for preview builds" };
129
107
  }
130
108
  return { ok: true, url: studioDeployUrl };
131
109
  }
@@ -142,7 +120,7 @@ function validatePreviewSourceCommit(env = process.env) {
142
120
  if (!raw.trim() || raw.trim() === "unknown") {
143
121
  return {
144
122
  ok: false,
145
- message: `SOURCE_COMMIT is required for Coolify preview builds (got '${raw}'). Enable Advanced \u2192 Include Source Commit in Build on both columnus-web and columnus-deploy.`
123
+ message: `SOURCE_COMMIT is required for isolated preview builds (got '${raw}'). Pass the same SOURCE_COMMIT build argument to Web and executor.`
146
124
  };
147
125
  }
148
126
  return { ok: false, message };
@@ -167,6 +145,6 @@ function mergeBootstrapAdminEmails(...groups) {
167
145
  return [...emails].join(",");
168
146
  }
169
147
 
170
- export { defaultProdDeployUrl, derivePreviewDeployBuildUrl, derivePreviewStudioDeployUrl, docsSeedSyncEmailFromEnv, isLoopbackDeployServiceUrl, isProdDeployServiceUrl, mergeBootstrapAdminEmails, normalizeConvexUrl, parseGitSha, resolveRuntimeDeployServiceUrl, resolveSyncDeployUrl, validatePreviewSourceCommit };
148
+ export { defaultProdDeployUrl, derivePreviewStudioDeployUrl, docsSeedSyncEmailFromEnv, isLoopbackDeployServiceUrl, isProdDeployServiceUrl, mergeBootstrapAdminEmails, normalizeConvexUrl, parseGitSha, resolveRuntimeDeployServiceUrl, resolveSyncDeployUrl, validatePreviewSourceCommit };
171
149
  //# sourceMappingURL=ciPreviewEnv.js.map
172
150
  //# sourceMappingURL=ciPreviewEnv.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/previewDeployName.ts","../src/ciPreviewEnv.ts"],"names":[],"mappings":"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","file":"ciPreviewEnv.js","sourcesContent":["/**\n * Sanitized Convex preview deployment name for CI / Coolify preview builds.\n * \"/\" breaks preview/<name> lookups in Convex deployment references.\n *\n * Coolify preview env vars: COOLIFY_BRANCH, COOLIFY_IS_PREVIEW, COOLIFY_FQDN,\n * COOLIFY_URL (BuildKit build secrets).\n *\n * Production Coolify often injects a comma-separated FQDN/URL list\n * (`app.columnus.app,bro.columnus.app,…`). Take the first valid hostname —\n * Better Auth Origin must be a single origin, not the whole list.\n */\nfunction hostnameFromCoolifyToken(raw: string): string | null {\n const token = raw.trim();\n if (!token || /^https?:?$/i.test(token)) return null;\n\n try {\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(token)) {\n return new URL(token).hostname || null;\n }\n } catch {\n return null;\n }\n\n const host = token.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n if (!host || (!host.includes(\".\") && host !== \"localhost\")) return null;\n return host;\n}\n\nexport function coolifyHostnameFromEnv(env: NodeJS.ProcessEnv = process.env): string | null {\n const raw = env.COOLIFY_FQDN ?? env.COOLIFY_URL;\n if (!raw) return null;\n for (const part of raw.split(\",\")) {\n const host = hostnameFromCoolifyToken(part);\n if (host) return host;\n }\n return null;\n}\n\n/**\n * Browser origin Better Auth expects on server-side OTP probes.\n * Preview configure sets SITE_URL to `https://<coolify-host>`; Origin must match.\n * Coolify often injects `COOLIFY_URL=http://…` — do not use that scheme.\n */\nexport function resolveTrustedAuthOrigin(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const explicit = env.COLUMNUS_AUTH_ORIGIN?.trim();\n if (explicit) return explicit.replace(/\\/$/, \"\");\n\n const host = coolifyHostnameFromEnv(env);\n if (host) return `https://${host}`;\n\n const siteUrl = env.SITE_URL?.trim();\n if (siteUrl && !/\\.convex\\.site(?:\\/|$)/i.test(siteUrl)) {\n return siteUrl.replace(/\\/$/, \"\");\n }\n return undefined;\n}\n\n/** True when Coolify (or CI_PREVIEW) indicates a PR preview Docker build. */\nexport function isCiPreviewBuild(env: NodeJS.ProcessEnv = process.env): boolean {\n if (env.CI_PREVIEW === \"true\" || env.COOLIFY_IS_PREVIEW === \"true\") {\n return true;\n }\n const host = coolifyHostnameFromEnv(env);\n // preview-<pr>, manage-preview-<pr>, deploy-preview-<pr>, renderer-preview-<pr>\n return Boolean(host && /^(?:manage-|deploy-|renderer-)?preview-\\d+\\./.test(host));\n}\n\nconst PULL_HEAD_BRANCH_RE = /^pull\\/(\\d+)\\/head$/i;\nconst PREVIEW_HOST_PR_RE = /^(?:manage-|deploy-|renderer-)?preview-(\\d+)\\./;\n\n/** PR id from `preview-<n>.<domain>` / `manage-preview-<n>` hostname or `pull/<n>/head` branch. */\nexport function resolvePreviewPrId(env: NodeJS.ProcessEnv = process.env): string | null {\n const host = coolifyHostnameFromEnv(env);\n if (host) {\n const fromHost = PREVIEW_HOST_PR_RE.exec(host)?.[1];\n if (fromHost) return fromHost;\n }\n\n const branch = env.COOLIFY_BRANCH ?? env.GIT_BRANCH;\n if (branch) {\n const fromBranch = PULL_HEAD_BRANCH_RE.exec(stripQualifiedBranch(branch))?.[1];\n if (fromBranch) return fromBranch;\n }\n\n return null;\n}\n\n/**\n * Resolve the git branch used for Convex `--preview-name`.\n * Coolify often sets COOLIFY_BRANCH to `pull/<n>/head` at runtime (see coollabsio/coolify#9572).\n */\nexport function resolvePreviewBranch(env: NodeJS.ProcessEnv = process.env): string | null {\n if (env.GITHUB_HEAD_REF) return stripQualifiedBranch(env.GITHUB_HEAD_REF);\n\n const branch = env.COOLIFY_BRANCH ?? env.GIT_BRANCH;\n const prId = resolvePreviewPrId(env);\n if (!branch) {\n return prId ? `preview-${prId}` : null;\n }\n\n const stripped = stripQualifiedBranch(branch);\n if (PULL_HEAD_BRANCH_RE.test(stripped)) {\n if (prId) return `preview-${prId}`;\n }\n // Coolify PR apps often leave COOLIFY_BRANCH=main while FQDN is *-preview-<n>.\n if ((stripped === \"main\" || stripped === \"master\") && prId) {\n return `preview-${prId}`;\n }\n return stripped;\n}\n\n/** Strip Coolify/Convex qualified suffixes (e.g. `pull-48-head:pr-48-coolify`). */\nfunction stripQualifiedBranch(branch: string): string {\n const colon = branch.indexOf(\":\");\n return colon > 0 ? branch.slice(0, colon) : branch;\n}\n\nexport function sanitizePreviewDeployName(branch: string): string {\n return stripQualifiedBranch(branch).replace(/\\//g, \"-\");\n}\n\n/** Normalize a preview name from Convex env before `convex deploy --preview-name`. */\nexport function normalizePreviewDeployName(name: string): string {\n return sanitizePreviewDeployName(name.trim());\n}\n\nexport function ciPreviewDeployName(env: NodeJS.ProcessEnv = process.env): string | null {\n if (!isCiPreviewBuild(env)) return null;\n\n const branch = resolvePreviewBranch(env);\n if (!branch) return null;\n return sanitizePreviewDeployName(branch);\n}\n\n/** Stale failures from broken preview deploy before the bridge was live. */\nexport const STALE_ENDPOINT_DEPLOY_ERROR =\n /^deploy endpoint returned (?:200(?: OK)?|500(?: Internal Server Error)?)$/;\n\nexport function isStaleEndpointDeployError(error: string | undefined): boolean {\n return error != null && STALE_ENDPOINT_DEPLOY_ERROR.test(error);\n}\n\n/** Infra failures from a prior broken deploy that a fresh save can retry. */\nexport function isRecoverableDeployError(error: string | undefined): boolean {\n if (error == null) return false;\n if (isStaleEndpointDeployError(error)) return true;\n if (error.includes(\"502 Bad Gateway\")) return true;\n if (error.includes(\"deploy endpoint unreachable\")) return true;\n if (error.includes(\"deploy endpoint timed out\")) return true;\n return (\n error.includes(\"convex/bin/main.js\") ||\n error.includes(\"convex CLI (bin/main.js\") ||\n error.includes(\"convex CLI not available\")\n );\n}\n","import { resolvePreviewPrId } from \"./previewDeployName.ts\";\n\nconst GIT_SHA_RE = /^[0-9a-fA-F]{7,40}$/;\n\n/** Parse and validate a git commit SHA (7–40 hex chars). */\nexport function parseGitSha(raw: string | undefined, label = \"commit\"): string {\n const commit = (raw ?? \"\").trim();\n if (!commit || commit === \"unknown\" || !GIT_SHA_RE.test(commit)) {\n throw new Error(`${label} must be a git SHA (7–40 hex chars); got '${raw ?? \"\"}'`);\n }\n return commit;\n}\n\nexport function defaultProdDeployUrl(domain = \"columnus.app\"): string {\n return `https://deploy.${domain}/ops/v1/deploy-initial`;\n}\n\nfunction hostnameOf(url: string): string | null {\n try {\n const withScheme = /:\\/\\//.test(url) ? url : `http://${url}`;\n return new URL(withScheme).hostname;\n } catch {\n return null;\n }\n}\n\n/** True for local defaults that should not beat Coolify preview deploy URLs. */\nexport function isLoopbackDeployServiceUrl(url: string): boolean {\n const host = hostnameOf(url);\n return host === \"127.0.0.1\" || host === \"localhost\" || host === \"::1\";\n}\n\n/** Production shared deploy host (`deploy.columnus.app`), any path. */\nexport function isProdDeployServiceUrl(url: string, domain = \"columnus.app\"): boolean {\n return hostnameOf(url) === `deploy.${domain}`;\n}\n\n/**\n * When on a PR preview host, return the per-PR deploy-service URL unless env already\n * points at a non-production executor (deploy-preview or Coolify internal).\n *\n * Coolify often copies production `STUDIO_DEPLOY_URL=https://deploy.columnus.app/deploy`\n * onto preview apps — that must not win over `deploy-preview-<pr>`.\n */\nexport function derivePreviewStudioDeployUrl(\n env: NodeJS.ProcessEnv = process.env,\n domain = \"columnus.app\",\n): string | null {\n const prId = resolvePreviewPrId(env);\n if (!prId) return null;\n\n for (const key of [\"COLUMNUS_DEPLOY_SERVICE_URL\", \"STUDIO_DEPLOY_URL\"] as const) {\n const current = env[key]?.trim();\n if (!current) continue;\n if (isLoopbackDeployServiceUrl(current) || isProdDeployServiceUrl(current, domain)) {\n continue;\n }\n // Explicit non-prod URL (deploy-preview / Coolify internal) — leave alone.\n return null;\n }\n\n return `https://deploy-preview-${prId}.${domain}/ops/v1/deploy-initial`;\n}\n\nconst LOCAL_DEPLOY_SERVICE_DEFAULT = \"http://127.0.0.1:8787\";\n\n/**\n * Runtime deploy-service URL for Web BFF / container entrypoint.\n *\n * Prefer preview-derived `deploy-preview-<pr>`, else non-loopback `COLUMNUS_DEPLOY_SERVICE_URL`,\n * else `STUDIO_DEPLOY_URL`, else loopback COLUMNUS, else local default.\n *\n * Callers that need an origin (no path) should normalize with deploy-service helpers.\n */\nexport function resolveRuntimeDeployServiceUrl(\n env: NodeJS.ProcessEnv = process.env,\n domain = \"columnus.app\",\n): string {\n const derived = derivePreviewStudioDeployUrl(env, domain);\n if (derived) return derived;\n\n const columnus = env.COLUMNUS_DEPLOY_SERVICE_URL?.trim();\n const studio = env.STUDIO_DEPLOY_URL?.trim();\n if (columnus && !isLoopbackDeployServiceUrl(columnus)) return columnus;\n if (studio) return studio;\n if (columnus) return columnus;\n return LOCAL_DEPLOY_SERVICE_DEFAULT;\n}\n\n/** Coolify internal URL for columnus-deploy preview container (build-time manifest sync). */\nexport function derivePreviewDeployBuildUrl(env: NodeJS.ProcessEnv = process.env): string | null {\n const prId = resolvePreviewPrId(env);\n if (!prId) return null;\n\n let uuid = env.COLUMNUS_DEPLOY_RESOURCE_UUID?.trim() ?? \"\";\n uuid = uuid.replace(/^https?:\\/\\//, \"\");\n uuid = uuid.split(\"/\")[0] ?? \"\";\n uuid = uuid.split(\"-pr-\")[0] ?? uuid;\n uuid = uuid.split(\":\")[0] ?? uuid;\n if (!uuid) return null;\n\n const port = env.COLUMNUS_DEPLOY_PREVIEW_PORT?.trim() || \"3000\";\n return `http://${uuid}-pr-${prId}:${port}/ops/v1/deploy-initial`;\n}\n\nexport type ResolveSyncDeployUrlResult =\n | { ok: true; url: string; derivedMessage?: string }\n | { ok: false; message: string };\n\n/** URL for `sync-manifest-deploy` during Docker/CI builds. */\nexport function resolveSyncDeployUrl(\n env: NodeJS.ProcessEnv,\n studioDeployUrl: string,\n isPreviewBuild: boolean,\n): ResolveSyncDeployUrlResult {\n const explicit = env.STUDIO_DEPLOY_BUILD_URL?.trim();\n if (explicit) {\n return { ok: true, url: explicit };\n }\n\n if (env.CI_PREVIEW === \"true\") {\n return { ok: true, url: \"http://127.0.0.1:8787/ops/v1/deploy-initial\" };\n }\n\n if (isPreviewBuild) {\n const derived = derivePreviewDeployBuildUrl(env);\n if (derived) {\n return {\n ok: true,\n url: derived,\n derivedMessage: `derived STUDIO_DEPLOY_BUILD_URL=${derived} (from COLUMNUS_DEPLOY_RESOURCE_UUID + PR id)`,\n };\n }\n return {\n ok: false,\n message:\n \"Set COLUMNUS_DEPLOY_RESOURCE_UUID on columnus-web preview (columnus-deploy Coolify resource UUID) or STUDIO_DEPLOY_BUILD_URL explicitly. See plans/done/021-preview-deploy-service.md\",\n };\n }\n\n return { ok: true, url: studioDeployUrl };\n}\n\nexport type PreviewSourceCommitResult =\n | { ok: true; commit: string; required: boolean }\n | { ok: false; message: string };\n\n/** Validates SOURCE_COMMIT for Coolify preview builds (skipped when CI_PREVIEW=true). */\nexport function validatePreviewSourceCommit(\n env: NodeJS.ProcessEnv = process.env,\n): PreviewSourceCommitResult {\n if (env.CI_PREVIEW === \"true\") {\n return { ok: true, commit: env.SOURCE_COMMIT?.trim() ?? \"\", required: false };\n }\n\n const raw = env.SOURCE_COMMIT ?? \"\";\n try {\n const commit = parseGitSha(raw, \"SOURCE_COMMIT\");\n return { ok: true, commit, required: true };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!raw.trim() || raw.trim() === \"unknown\") {\n return {\n ok: false,\n message: `SOURCE_COMMIT is required for Coolify preview builds (got '${raw}'). Enable Advanced → Include Source Commit in Build on both columnus-web and columnus-deploy.`,\n };\n }\n return { ok: false, message };\n }\n}\n\nexport function normalizeConvexUrl(url: string): string {\n return url.trim().replace(/\\/+$/, \"\");\n}\n\n/** DOCS_SEED_SYNC_EMAIL with PLAYWRIGHT_TEST_EMAIL fallback (Coolify / entrypoint). */\nexport function docsSeedSyncEmailFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {\n return env.DOCS_SEED_SYNC_EMAIL?.trim() || env.PLAYWRIGHT_TEST_EMAIL?.trim() || undefined;\n}\n\n/**\n * Union comma-separated bootstrap admin emails with preview seed identities.\n * Preview docs seed + Playwright OTP users must be bootstrap-approved or\n * container-startup `sync-docs-seed` fails with `Not approved`.\n */\nexport function mergeBootstrapAdminEmails(\n ...groups: Array<string | undefined>\n): string | undefined {\n const emails = new Set<string>();\n for (const group of groups) {\n if (!group) continue;\n for (const part of group.split(\",\")) {\n const email = part.trim().toLowerCase();\n if (email) emails.add(email);\n }\n }\n if (emails.size === 0) return undefined;\n return [...emails].join(\",\");\n}\n"]}
1
+ {"version":3,"sources":["../src/previewDeployName.ts","../src/ciPreviewEnv.ts"],"names":[],"mappings":";AAQO,SAAS,sBAAA,CAAuB,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAoB;AAC1F,EAAA,MAAM,MAAA,GAAS,yBAAyB,GAAG,CAAA;AAC3C,EAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,GAAA,CAAI,MAAM,CAAA,CAAE,QAAA;AAAA,EACzB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAGO,SAAS,wBAAA,CAAyB,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAyB;AACjG,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,oBAAA,EAAsB,IAAA,EAAK;AAChD,EAAA,IAAI,QAAA,EAAU,OAAO,QAAA,CAAS,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC/C,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,QAAA,EAAU,IAAA,EAAK;AACnC,EAAA,IAAI,OAAA,IAAW,CAAC,yBAAA,CAA0B,IAAA,CAAK,OAAO,GAAG,OAAO,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACzF,EAAA,OAAO,MAAA;AACT;AASA,IAAM,mBAAA,GAAsB,sBAAA;AAC5B,IAAM,kBAAA,GAAqB,gDAAA;AAGpB,SAAS,kBAAA,CAAmB,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAoB;AACtF,EAAA,MAAM,IAAA,GAAO,uBAAuB,GAAG,CAAA;AACvC,EAAA,MAAM,SAAS,IAAA,GAAO,kBAAA,CAAmB,KAAK,IAAI,CAAA,GAAI,CAAC,CAAA,GAAI,MAAA;AAC3D,EAAA,OAAO,QAAQ,MAAA,GAAS,IAAA,CAAK,MAAM,MAAA,CAAO,MAAM,KAAK,IAAA,GAAO,IAAA;AAC9D;AAGO,SAAS,kBAAA,CAAmB,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAoB;AACtF,EAAA,MAAM,IAAA,GAAO,uBAAuB,GAAG,CAAA;AACvC,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,IAAA,CAAK,IAAI,IAAI,CAAC,CAAA;AAClD,IAAA,IAAI,UAAU,OAAO,QAAA;AAAA,EACvB;AAEA,EAAA,MAAM,SAAS,GAAA,CAAI,UAAA;AACnB,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,aAAa,mBAAA,CAAoB,IAAA,CAAK,qBAAqB,MAAM,CAAC,IAAI,CAAC,CAAA;AAC7E,IAAA,IAAI,YAAY,OAAO,UAAA;AAAA,EACzB;AAEA,EAAA,OAAO,IAAA;AACT;AAwBA,SAAS,qBAAqB,MAAA,EAAwB;AACpD,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA;AAChC,EAAA,OAAO,QAAQ,CAAA,GAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA,GAAI,MAAA;AAC9C;;;ACpFA,IAAM,UAAA,GAAa,qBAAA;AAGZ,SAAS,WAAA,CAAY,GAAA,EAAyB,KAAA,GAAQ,QAAA,EAAkB;AAC7E,EAAA,MAAM,MAAA,GAAA,CAAU,GAAA,IAAO,EAAA,EAAI,IAAA,EAAK;AAChC,EAAA,IAAI,CAAC,UAAU,MAAA,KAAW,SAAA,IAAa,CAAC,UAAA,CAAW,IAAA,CAAK,MAAM,CAAA,EAAG;AAC/D,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA,+CAAA,EAA6C,GAAA,IAAO,EAAE,CAAA,CAAA,CAAG,CAAA;AAAA,EACnF;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,oBAAA,CAAqB,SAAS,cAAA,EAAwB;AACpE,EAAA,OAAO,kBAAkB,MAAM,CAAA,sBAAA,CAAA;AACjC;AAEA,SAAS,WAAW,GAAA,EAA4B;AAC9C,EAAA,IAAI;AACF,IAAA,MAAM,aAAa,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA,GAAI,GAAA,GAAM,UAAU,GAAG,CAAA,CAAA;AAC1D,IAAA,OAAO,IAAI,GAAA,CAAI,UAAU,CAAA,CAAE,QAAA;AAAA,EAC7B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAGO,SAAS,2BAA2B,GAAA,EAAsB;AAC/D,EAAA,MAAM,IAAA,GAAO,WAAW,GAAG,CAAA;AAC3B,EAAA,OAAO,IAAA,KAAS,WAAA,IAAe,IAAA,KAAS,WAAA,IAAe,IAAA,KAAS,KAAA;AAClE;AAGO,SAAS,sBAAA,CAAuB,GAAA,EAAa,MAAA,GAAS,cAAA,EAAyB;AACpF,EAAA,OAAO,UAAA,CAAW,GAAG,CAAA,KAAM,CAAA,OAAA,EAAU,MAAM,CAAA,CAAA;AAC7C;AAUO,SAAS,4BAAA,CACd,GAAA,GAAyB,OAAA,CAAQ,GAAA,EACjC,MAAA,EACe;AACf,EAAA,MAAM,IAAA,GAAO,mBAAmB,GAAG,CAAA;AACnC,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,EAAA,KAAA,MAAW,GAAA,IAAO,CAAC,6BAAA,EAA+B,mBAAmB,CAAA,EAAY;AAC/E,IAAA,MAAM,OAAA,GAAU,GAAA,CAAI,GAAG,CAAA,EAAG,IAAA,EAAK;AAC/B,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IACE,0BAAA,CAA2B,OAAO,CAAA,IAClC,sBAAA,CAAuB,OAAO,CAAA,IAC9B,sBAAA,CAAuB,OAAA,EAAS,MAAA,IAAU,GAAA,CAAI,eAAA,EAAiB,IAAA,EAAM,CAAA,EACrE;AACA,MAAA;AAAA,IACF;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,aAAA,GAAgB,QAAQ,IAAA,EAAK,IAAK,IAAI,eAAA,EAAiB,IAAA,EAAK,IAAK,kBAAA,CAAmB,GAAG,CAAA;AAC7F,EAAA,IAAI,CAAC,aAAA;AACH,IAAA,MAAM,IAAI,MAAM,mEAAmE,CAAA;AACrF,EAAA,OAAO,CAAA,uBAAA,EAA0B,IAAI,CAAA,CAAA,EAAI,aAAa,CAAA,sBAAA,CAAA;AACxD;AAEA,IAAM,4BAAA,GAA+B,uBAAA;AAW9B,SAAS,8BAAA,CACd,GAAA,GAAyB,OAAA,CAAQ,GAAA,EACjC,MAAA,EACQ;AACR,EAAA,MAAM,OAAA,GAAU,4BAAA,CAA6B,GAAA,EAAK,MAAM,CAAA;AACxD,EAAA,IAAI,SAAS,OAAO,OAAA;AAEpB,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,2BAAA,EAA6B,IAAA,EAAK;AACvD,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,iBAAA,EAAmB,IAAA,EAAK;AAC3C,EAAA,IAAI,QAAA,IAAY,CAAC,0BAAA,CAA2B,QAAQ,GAAG,OAAO,QAAA;AAC9D,EAAA,IAAI,QAAQ,OAAO,MAAA;AACnB,EAAA,IAAI,UAAU,OAAO,QAAA;AACrB,EAAA,OAAO,4BAAA;AACT;AAOO,SAAS,oBAAA,CACd,GAAA,EACA,eAAA,EACA,cAAA,EAC4B;AAC5B,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,uBAAA,EAAyB,IAAA,EAAK;AACnD,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,GAAA,EAAK,QAAA,EAAS;AAAA,EACnC;AAEA,EAAA,IAAI,GAAA,CAAI,eAAe,MAAA,EAAQ;AAC7B,IAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,GAAA,EAAK,6CAAA,EAA8C;AAAA,EACxE;AAEA,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,OAAA,EAAS,2DAAA,EAA4D;AAAA,EAC3F;AAEA,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,GAAA,EAAK,eAAA,EAAgB;AAC1C;AAOO,SAAS,2BAAA,CACd,GAAA,GAAyB,OAAA,CAAQ,GAAA,EACN;AAC3B,EAAA,IAAI,GAAA,CAAI,eAAe,MAAA,EAAQ;AAC7B,IAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,GAAA,CAAI,eAAe,IAAA,EAAK,IAAK,EAAA,EAAI,QAAA,EAAU,KAAA,EAAM;AAAA,EAC9E;AAEA,EAAA,MAAM,GAAA,GAAM,IAAI,aAAA,IAAiB,EAAA;AACjC,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,WAAA,CAAY,GAAA,EAAK,eAAe,CAAA;AAC/C,IAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,UAAU,IAAA,EAAK;AAAA,EAC5C,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,UAAU,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AACrE,IAAA,IAAI,CAAC,GAAA,CAAI,IAAA,MAAU,GAAA,CAAI,IAAA,OAAW,SAAA,EAAW;AAC3C,MAAA,OAAO;AAAA,QACL,EAAA,EAAI,KAAA;AAAA,QACJ,OAAA,EAAS,+DAA+D,GAAG,CAAA,mEAAA;AAAA,OAC7E;AAAA,IACF;AACA,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,OAAA,EAAQ;AAAA,EAC9B;AACF;AAEO,SAAS,mBAAmB,GAAA,EAAqB;AACtD,EAAA,OAAO,GAAA,CAAI,IAAA,EAAK,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACtC;AAGO,SAAS,wBAAA,CAAyB,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAyB;AACjG,EAAA,OAAO,IAAI,oBAAA,EAAsB,IAAA,MAAU,GAAA,CAAI,qBAAA,EAAuB,MAAK,IAAK,MAAA;AAClF;AAOO,SAAS,6BACX,MAAA,EACiB;AACpB,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAC/B,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA,EAAG;AACnC,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,EAAK,CAAE,WAAA,EAAY;AACtC,MAAA,IAAI,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA;AAAA,IAC7B;AAAA,EACF;AACA,EAAA,IAAI,MAAA,CAAO,IAAA,KAAS,CAAA,EAAG,OAAO,MAAA;AAC9B,EAAA,OAAO,CAAC,GAAG,MAAM,CAAA,CAAE,KAAK,GAAG,CAAA;AAC7B","file":"ciPreviewEnv.js","sourcesContent":["/** Canonical Kubernetes preview PR identity (matches columnus-pr-N namespaces). */\nexport const PREVIEW_PR_ID_RE = /^[1-9][0-9]{0,6}$/;\nexport const PREVIEW_TENANT_SLUG_PREFIX = \"preview-\";\nexport function isPreviewTenantSlug(slug: string): boolean {\n return slug.startsWith(PREVIEW_TENANT_SLUG_PREFIX);\n}\n\n/** Explicit application origin used by CI and runtime preview routing. */\nexport function previewHostnameFromEnv(env: NodeJS.ProcessEnv = process.env): string | null {\n const origin = resolveTrustedAuthOrigin(env);\n if (!origin) return null;\n try {\n return new URL(origin).hostname;\n } catch {\n return null;\n }\n}\n\n/** Browser origin for server-side auth probes; never use a Convex HTTP endpoint. */\nexport function resolveTrustedAuthOrigin(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const explicit = env.COLUMNUS_AUTH_ORIGIN?.trim();\n if (explicit) return explicit.replace(/\\/$/, \"\");\n const siteUrl = env.SITE_URL?.trim();\n if (siteUrl && !/\\.convex\\.site(?:\\/|$)/i.test(siteUrl)) return siteUrl.replace(/\\/$/, \"\");\n return undefined;\n}\n\n/** CI_PREVIEW is the local build mode; COLUMNUS_PREVIEW identifies deployed previews. */\nexport function isCiPreviewBuild(env: NodeJS.ProcessEnv = process.env): boolean {\n if (env.CI_PREVIEW === \"true\" || env.COLUMNUS_PREVIEW === \"true\") return true;\n const host = previewHostnameFromEnv(env);\n return Boolean(host && PREVIEW_HOST_PR_RE.test(host));\n}\n\nconst PULL_HEAD_BRANCH_RE = /^pull\\/(\\d+)\\/head$/i;\nconst PREVIEW_HOST_PR_RE = /^(?:manage-|deploy-|renderer-)?preview-(\\d+)\\./;\n\n/** Domain suffix only from a recognized preview host, never from branch identity. */\nexport function previewZoneFromEnv(env: NodeJS.ProcessEnv = process.env): string | null {\n const host = previewHostnameFromEnv(env);\n const prefix = host ? PREVIEW_HOST_PR_RE.exec(host)?.[0] : undefined;\n return host && prefix ? host.slice(prefix.length) || null : null;\n}\n\n/** PR id from `preview-<n>.<domain>` / `manage-preview-<n>` hostname or `pull/<n>/head` branch. */\nexport function resolvePreviewPrId(env: NodeJS.ProcessEnv = process.env): string | null {\n const host = previewHostnameFromEnv(env);\n if (host) {\n const fromHost = PREVIEW_HOST_PR_RE.exec(host)?.[1];\n if (fromHost) return fromHost;\n }\n\n const branch = env.GIT_BRANCH;\n if (branch) {\n const fromBranch = PULL_HEAD_BRANCH_RE.exec(stripQualifiedBranch(branch))?.[1];\n if (fromBranch) return fromBranch;\n }\n\n return null;\n}\n\n/** Resolve the CI git branch used for Convex preview deployment naming. */\nexport function resolvePreviewBranch(env: NodeJS.ProcessEnv = process.env): string | null {\n if (env.GITHUB_HEAD_REF) return stripQualifiedBranch(env.GITHUB_HEAD_REF);\n\n const branch = env.GIT_BRANCH;\n const prId = resolvePreviewPrId(env);\n if (!branch) {\n return prId ? `preview-${prId}` : null;\n }\n\n const stripped = stripQualifiedBranch(branch);\n if (PULL_HEAD_BRANCH_RE.test(stripped)) {\n if (prId) return `preview-${prId}`;\n }\n // An explicit preview origin takes priority over the base branch.\n if ((stripped === \"main\" || stripped === \"master\") && prId) {\n return `preview-${prId}`;\n }\n return stripped;\n}\n\n/** Strip qualified branch suffixes before deriving a deployment name. */\nfunction stripQualifiedBranch(branch: string): string {\n const colon = branch.indexOf(\":\");\n return colon > 0 ? branch.slice(0, colon) : branch;\n}\n\nexport function sanitizePreviewDeployName(branch: string): string {\n return stripQualifiedBranch(branch).replace(/\\//g, \"-\");\n}\n\n/** Normalize a preview name from Convex env before `convex deploy --preview-name`. */\nexport function normalizePreviewDeployName(name: string): string {\n return sanitizePreviewDeployName(name.trim());\n}\n\n/** Identifier rule shared by CI configuration and Management registration. */\nexport const PREVIEW_DEPLOY_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;\n\n/** Explicit CI identity, including hostless Management builds. */\nexport function explicitPreviewDeployName(env: NodeJS.ProcessEnv = process.env): string | null {\n const name = env.CONVEX_PREVIEW_NAME?.trim();\n if (!name) return null;\n if (!PREVIEW_DEPLOY_NAME_RE.test(name)) {\n throw new Error(\"CONVEX_PREVIEW_NAME must be a 1–64 character preview identifier\");\n }\n return name;\n}\n\nexport function ciPreviewDeployName(env: NodeJS.ProcessEnv = process.env): string | null {\n if (!isCiPreviewBuild(env)) return null;\n const explicit = explicitPreviewDeployName(env);\n if (explicit) return explicit;\n\n const branch = resolvePreviewBranch(env);\n if (!branch) return null;\n return sanitizePreviewDeployName(branch);\n}\n\n/** Stale failures from broken preview deploy before the bridge was live. */\nexport const STALE_ENDPOINT_DEPLOY_ERROR =\n /^deploy endpoint returned (?:200(?: OK)?|500(?: Internal Server Error)?)$/;\n\nexport function isStaleEndpointDeployError(error: string | undefined): boolean {\n return error != null && STALE_ENDPOINT_DEPLOY_ERROR.test(error);\n}\n\n/** Infra failures from a prior broken deploy that a fresh save can retry. */\nexport function isRecoverableDeployError(error: string | undefined): boolean {\n if (error == null) return false;\n if (isStaleEndpointDeployError(error)) return true;\n if (error.includes(\"502 Bad Gateway\")) return true;\n if (error.includes(\"deploy endpoint unreachable\")) return true;\n if (error.includes(\"deploy endpoint timed out\")) return true;\n return (\n error.includes(\"convex/bin/main.js\") ||\n error.includes(\"convex CLI (bin/main.js\") ||\n error.includes(\"convex CLI not available\")\n );\n}\n","import { previewZoneFromEnv, resolvePreviewPrId } from \"./previewDeployName.ts\";\n\nconst GIT_SHA_RE = /^[0-9a-fA-F]{7,40}$/;\n\n/** Parse and validate a git commit SHA (7–40 hex chars). */\nexport function parseGitSha(raw: string | undefined, label = \"commit\"): string {\n const commit = (raw ?? \"\").trim();\n if (!commit || commit === \"unknown\" || !GIT_SHA_RE.test(commit)) {\n throw new Error(`${label} must be a git SHA (7–40 hex chars); got '${raw ?? \"\"}'`);\n }\n return commit;\n}\n\nexport function defaultProdDeployUrl(domain = \"columnus.app\"): string {\n return `https://deploy.${domain}/ops/v1/deploy-initial`;\n}\n\nfunction hostnameOf(url: string): string | null {\n try {\n const withScheme = /:\\/\\//.test(url) ? url : `http://${url}`;\n return new URL(withScheme).hostname;\n } catch {\n return null;\n }\n}\n\n/** True for local defaults that should not beat isolated preview deploy URLs. */\nexport function isLoopbackDeployServiceUrl(url: string): boolean {\n const host = hostnameOf(url);\n return host === \"127.0.0.1\" || host === \"localhost\" || host === \"::1\";\n}\n\n/** Production shared deploy host (`deploy.columnus.app`), any path. */\nexport function isProdDeployServiceUrl(url: string, domain = \"columnus.app\"): boolean {\n return hostnameOf(url) === `deploy.${domain}`;\n}\n\n/**\n * When on a PR preview host, return the per-PR deploy-service URL unless env already\n * points at a non-production executor (deploy-preview or private cluster).\n *\n * A deployment may copy production `STUDIO_DEPLOY_URL=https://deploy.columnus.app/deploy`\n * onto preview apps — that must not win over `deploy-preview-<pr>`.\n * @throws When preview identity lacks a domain/origin and an explicit non-prod executor.\n */\nexport function derivePreviewStudioDeployUrl(\n env: NodeJS.ProcessEnv = process.env,\n domain?: string,\n): string | null {\n const prId = resolvePreviewPrId(env);\n if (!prId) return null;\n\n for (const key of [\"COLUMNUS_DEPLOY_SERVICE_URL\", \"STUDIO_DEPLOY_URL\"] as const) {\n const current = env[key]?.trim();\n if (!current) continue;\n if (\n isLoopbackDeployServiceUrl(current) ||\n isProdDeployServiceUrl(current) ||\n isProdDeployServiceUrl(current, domain || env.COLUMNUS_DOMAIN?.trim())\n ) {\n continue;\n }\n // Explicit non-prod URL (deploy-preview / private cluster) — leave alone.\n return null;\n }\n\n const previewDomain = domain?.trim() || env.COLUMNUS_DOMAIN?.trim() || previewZoneFromEnv(env);\n if (!previewDomain)\n throw new Error(\"COLUMNUS_DOMAIN is required for hostless preview executor routing\");\n return `https://deploy-preview-${prId}.${previewDomain}/ops/v1/deploy-initial`;\n}\n\nconst LOCAL_DEPLOY_SERVICE_DEFAULT = \"http://127.0.0.1:8787\";\n\n/**\n * Runtime deploy-service URL for Web BFF / container entrypoint.\n *\n * Prefer preview-derived `deploy-preview-<pr>`, else non-loopback `COLUMNUS_DEPLOY_SERVICE_URL`,\n * else `STUDIO_DEPLOY_URL`, else loopback COLUMNUS, else local default.\n *\n * Callers that need an origin (no path) should normalize with deploy-service helpers.\n * @throws For hostless preview identity without domain or explicit non-prod executor.\n */\nexport function resolveRuntimeDeployServiceUrl(\n env: NodeJS.ProcessEnv = process.env,\n domain?: string,\n): string {\n const derived = derivePreviewStudioDeployUrl(env, domain);\n if (derived) return derived;\n\n const columnus = env.COLUMNUS_DEPLOY_SERVICE_URL?.trim();\n const studio = env.STUDIO_DEPLOY_URL?.trim();\n if (columnus && !isLoopbackDeployServiceUrl(columnus)) return columnus;\n if (studio) return studio;\n if (columnus) return columnus;\n return LOCAL_DEPLOY_SERVICE_DEFAULT;\n}\n\nexport type ResolveSyncDeployUrlResult =\n | { ok: true; url: string; derivedMessage?: string }\n | { ok: false; message: string };\n\n/** URL for `sync-manifest-deploy` during Docker/CI builds. */\nexport function resolveSyncDeployUrl(\n env: NodeJS.ProcessEnv,\n studioDeployUrl: string,\n isPreviewBuild: boolean,\n): ResolveSyncDeployUrlResult {\n const explicit = env.STUDIO_DEPLOY_BUILD_URL?.trim();\n if (explicit) {\n return { ok: true, url: explicit };\n }\n\n if (env.CI_PREVIEW === \"true\") {\n return { ok: true, url: \"http://127.0.0.1:8787/ops/v1/deploy-initial\" };\n }\n\n if (isPreviewBuild) {\n return { ok: false, message: \"Set STUDIO_DEPLOY_BUILD_URL explicitly for preview builds\" };\n }\n\n return { ok: true, url: studioDeployUrl };\n}\n\nexport type PreviewSourceCommitResult =\n | { ok: true; commit: string; required: boolean }\n | { ok: false; message: string };\n\n/** Validates SOURCE_COMMIT for isolated preview builds (skipped when CI_PREVIEW=true). */\nexport function validatePreviewSourceCommit(\n env: NodeJS.ProcessEnv = process.env,\n): PreviewSourceCommitResult {\n if (env.CI_PREVIEW === \"true\") {\n return { ok: true, commit: env.SOURCE_COMMIT?.trim() ?? \"\", required: false };\n }\n\n const raw = env.SOURCE_COMMIT ?? \"\";\n try {\n const commit = parseGitSha(raw, \"SOURCE_COMMIT\");\n return { ok: true, commit, required: true };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!raw.trim() || raw.trim() === \"unknown\") {\n return {\n ok: false,\n message: `SOURCE_COMMIT is required for isolated preview builds (got '${raw}'). Pass the same SOURCE_COMMIT build argument to Web and executor.`,\n };\n }\n return { ok: false, message };\n }\n}\n\nexport function normalizeConvexUrl(url: string): string {\n return url.trim().replace(/\\/+$/, \"\");\n}\n\n/** DOCS_SEED_SYNC_EMAIL with PLAYWRIGHT_TEST_EMAIL fallback (CI / entrypoint). */\nexport function docsSeedSyncEmailFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {\n return env.DOCS_SEED_SYNC_EMAIL?.trim() || env.PLAYWRIGHT_TEST_EMAIL?.trim() || undefined;\n}\n\n/**\n * Union comma-separated bootstrap admin emails with preview seed identities.\n * Preview docs seed + Playwright OTP users must be bootstrap-approved or\n * container-startup `sync-docs-seed` fails with `Not approved`.\n */\nexport function mergeBootstrapAdminEmails(\n ...groups: Array<string | undefined>\n): string | undefined {\n const emails = new Set<string>();\n for (const group of groups) {\n if (!group) continue;\n for (const part of group.split(\",\")) {\n const email = part.trim().toLowerCase();\n if (email) emails.add(email);\n }\n }\n if (emails.size === 0) return undefined;\n return [...emails].join(\",\");\n}\n"]}
@@ -11,8 +11,8 @@ export declare function isPlaceholderManagementSiteUrl(url: string): boolean;
11
11
  */
12
12
  export declare function resolveBffManagementSiteUrl(env?: ProcessEnvLike): string | undefined;
13
13
  /**
14
- * Runtime BFF management site: prefer image-baked URL over Coolify env.
15
- * Preview Coolify often injects prod `MANAGEMENT_CONVEX_SITE_URL`, which shadows the
14
+ * Runtime BFF management site: prefer the image-baked URL over runtime defaults.
15
+ * A stale runtime `MANAGEMENT_CONVEX_SITE_URL` must not shadow the
16
16
  * ephemeral Manage preview bake and makes prepare return HTTP 404 (no routes on prod).
17
17
  */
18
18
  export declare function resolveRuntimeManagementSiteUrl(options: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/convexSiteUrl.ts"],"names":[],"mappings":"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","file":"convexSiteUrl.js","sourcesContent":["/** Convex deployment cloud URL (.convex.cloud) → HTTP actions / auth issuer (.convex.site). */\nexport function convexCloudToSiteUrl(convexUrl: string): string {\n const trimmed = convexUrl.trim().replace(/\\/$/, \"\");\n if (trimmed.includes(\".convex.site\")) return trimmed;\n if (trimmed.includes(\".convex.cloud\")) {\n return trimmed.replace(/\\.convex\\.cloud\\b/, \".convex.site\");\n }\n return trimmed;\n}\n\nexport function convexSiteToCloudUrl(siteUrl: string): string {\n const trimmed = siteUrl.trim().replace(/\\/$/, \"\");\n if (trimmed.includes(\".convex.cloud\")) return trimmed;\n if (trimmed.includes(\".convex.site\")) {\n return trimmed.replace(/\\.convex\\.site\\b/, \".convex.cloud\");\n }\n return trimmed;\n}\n\ntype ProcessEnvLike = Record<string, string | undefined>;\n\nfunction trimOptional(value?: string): string | undefined {\n const trimmed = value?.trim();\n return trimmed || undefined;\n}\n\nfunction normalizeTrailingSlash(url: string): string {\n return url.trim().replace(/\\/$/, \"\");\n}\n\nfunction deriveSiteFromCloudUrl(cloud?: string): string | undefined {\n const trimmed = cloud?.trim();\n if (!trimmed) return undefined;\n if (trimmed.includes(\".convex.cloud\") || trimmed.includes(\".convex.site\")) {\n return convexCloudToSiteUrl(trimmed);\n }\n return undefined;\n}\n\n/** Docker runtime-base verify placeholder — must not be used for real BFF prepare. */\nexport function isPlaceholderManagementSiteUrl(url: string): boolean {\n try {\n const host = new URL(/:\\/\\//.test(url) ? url : `https://${url}`).hostname;\n return host === \"placeholder.convex.site\" || host.endsWith(\".placeholder.convex.site\");\n } catch {\n return /placeholder\\.convex\\.site/i.test(url);\n }\n}\n\n/**\n * Management `.convex.site` origin for BFF `prepare`.\n * Prefers explicit management site env vars, else derives from `VITE_MANAGEMENT_CONVEX_URL`.\n * Does not read `VITE_CONVEX_SITE_URL` — on web that is the tenant site, not Manage.\n */\nexport function resolveBffManagementSiteUrl(env: ProcessEnvLike = process.env): string | undefined {\n for (const key of [\"COLUMNUS_MANAGEMENT_SITE_URL\", \"MANAGEMENT_CONVEX_SITE_URL\"] as const) {\n const value = trimOptional(env[key]);\n if (value && !isPlaceholderManagementSiteUrl(value)) {\n return normalizeTrailingSlash(value);\n }\n }\n const managementCloud = trimOptional(env.VITE_MANAGEMENT_CONVEX_URL);\n if (managementCloud) return convexCloudToSiteUrl(managementCloud);\n return undefined;\n}\n\n/**\n * Runtime BFF management site: prefer image-baked URL over Coolify env.\n * Preview Coolify often injects prod `MANAGEMENT_CONVEX_SITE_URL`, which shadows the\n * ephemeral Manage preview bake and makes prepare return HTTP 404 (no routes on prod).\n */\nexport function resolveRuntimeManagementSiteUrl(options: {\n bakedSiteUrl?: string | null;\n env?: ProcessEnvLike;\n}): string | undefined {\n const bakedSite = trimOptional(options.bakedSiteUrl ?? undefined);\n if (bakedSite && !isPlaceholderManagementSiteUrl(bakedSite)) {\n return normalizeTrailingSlash(bakedSite);\n }\n return resolveBffManagementSiteUrl(options.env ?? process.env);\n}\n\nfunction convexDeploymentSlug(url: string): string | undefined {\n const match = url.match(/https:\\/\\/([^.]+)\\.convex\\.(?:cloud|site)/);\n return match?.[1];\n}\n\n/** Deployment name from a Convex cloud/site URL (`https://{name}.convex.cloud`). */\nexport function convexDeploymentSlugFromUrl(url: string): string | undefined {\n return convexDeploymentSlug(url);\n}\n\n/**\n * Best-effort `CONVEX_DEPLOYMENT` when only the cloud URL is known (e.g. after `columnus-api pull`).\n * Defaults to `dev:{slug}` — preview/prod deployments may need an explicit value in `.env.local`.\n */\nexport function deriveConvexDeploymentFromCloudUrl(cloudUrl: string): string | undefined {\n const slug = convexDeploymentSlug(cloudUrl);\n return slug ? `dev:${slug}` : undefined;\n}\n\nfunction explicitSiteMatchesCloud(explicit: string, cloud: string): boolean {\n const explicitSlug = convexDeploymentSlug(explicit);\n const cloudSlug = convexDeploymentSlug(cloud);\n if (!explicitSlug || !cloudSlug) return true;\n return explicitSlug === cloudSlug;\n}\n\nexport type ConvexSiteUrlEnv = {\n /** CONVEX_SITE_URL */\n convexSiteUrl?: string;\n /** VITE_CONVEX_SITE_URL */\n viteConvexSiteUrl?: string;\n /** CONVEX_URL */\n convexUrl?: string;\n /** VITE_CONVEX_URL */\n viteConvexUrl?: string;\n /** CONVEX_CLOUD_URL */\n convexCloudUrl?: string;\n};\n\n/** @deprecated Use `ConvexSiteUrlEnv` */\nexport type ResolveConvexSiteUrlOptions = ConvexSiteUrlEnv;\n\nexport function readConvexSiteUrlEnv(env: ProcessEnvLike): ConvexSiteUrlEnv {\n return {\n convexSiteUrl: env.CONVEX_SITE_URL,\n viteConvexSiteUrl: env.VITE_CONVEX_SITE_URL,\n convexUrl: env.CONVEX_URL,\n viteConvexUrl: env.VITE_CONVEX_URL,\n convexCloudUrl: env.CONVEX_CLOUD_URL,\n };\n}\n\n/**\n * Resolve .convex.site URL from env: explicit site URLs first, then derive from Convex cloud/site URLs.\n * Returns empty string when nothing is configured.\n */\nexport function resolveConvexSiteUrlFromEnv(env: ConvexSiteUrlEnv): string {\n const explicit = trimOptional(env.convexSiteUrl) ?? trimOptional(env.viteConvexSiteUrl);\n const cloud =\n trimOptional(env.convexUrl) ??\n trimOptional(env.viteConvexUrl) ??\n trimOptional(env.convexCloudUrl);\n\n if (explicit && cloud && !explicitSiteMatchesCloud(explicit, cloud)) {\n return deriveSiteFromCloudUrl(cloud) ?? normalizeTrailingSlash(explicit);\n }\n\n if (explicit) return normalizeTrailingSlash(explicit);\n\n return deriveSiteFromCloudUrl(cloud) ?? \"\";\n}\n\n/** Better Auth `baseURL` from standard Convex process env. */\nexport function convexSiteUrlFromProcessEnv(env: ProcessEnvLike = process.env): string {\n return resolveConvexSiteUrlFromEnv(readConvexSiteUrlEnv(env));\n}\n\n/** Resolve .convex.site URL for OTP sign-in and scripts (throws when unset). */\nexport function resolveConvexSiteUrl(env?: ConvexSiteUrlEnv): string {\n const url = resolveConvexSiteUrlFromEnv(env ?? {});\n if (!url) {\n throw new Error(\"Convex URL required to derive .convex.site URL for OTP sign-in\");\n }\n return url;\n}\n\nexport type AuthConfigIssuerEnv = ConvexSiteUrlEnv & {\n /** BAKED_AUTH_ISSUER patched at deploy time */\n bakedAuthIssuer?: string;\n};\n\n/**\n * Auth issuer for deploy scripts: baked deploy patch → convex site URL env chain.\n */\nexport function resolveAuthConfigIssuerFromEnv(env: AuthConfigIssuerEnv): string {\n const baked = trimOptional(env.bakedAuthIssuer);\n if (baked) return normalizeTrailingSlash(baked);\n\n return resolveConvexSiteUrlFromEnv(env);\n}\n\nexport type AuthConfigIssuerFromProcessEnvOptions = {\n bakedAuthIssuer?: string;\n /** Derive from CONVEX_URL / VITE_CONVEX_URL when issuer env vars are unset (deploy scripts). */\n deriveFromCloudUrl?: boolean;\n};\n\n/** JWT issuer from process env (auth.config.ts bundle vs deploy scripts). */\nexport function authConfigIssuerFromProcessEnv(\n env: ProcessEnvLike = process.env,\n options: AuthConfigIssuerFromProcessEnvOptions = {},\n): string {\n return resolveAuthConfigIssuerFromEnv({\n bakedAuthIssuer: options.bakedAuthIssuer,\n convexSiteUrl: env.CONVEX_SITE_URL,\n ...(options.deriveFromCloudUrl\n ? {\n convexUrl: env.CONVEX_URL,\n viteConvexUrl: env.VITE_CONVEX_URL,\n }\n : {}),\n });\n}\n\n/** Comma-separated browser origins (SITE_URL) for Better Auth trustedOrigins / crossDomain. */\nexport function parseTrustedSiteUrls(siteUrlRaw?: string): string[] {\n const raw = siteUrlRaw?.trim() ?? \"\";\n if (!raw) return [];\n return raw\n .split(\",\")\n .map((url) => url.trim())\n .filter(Boolean);\n}\n\nexport function primaryTrustedSiteUrl(siteUrlRaw?: string): string {\n const urls = parseTrustedSiteUrls(siteUrlRaw);\n return urls[0] ?? siteUrlRaw?.trim() ?? \"\";\n}\n\n/** Better Auth `trustedOrigins` from SITE_URL. */\nexport function betterAuthTrustedOrigins(siteUrlRaw?: string): string[] {\n const urls = parseTrustedSiteUrls(siteUrlRaw);\n if (urls.length > 0) return urls;\n const primary = primaryTrustedSiteUrl(siteUrlRaw);\n return primary ? [primary] : [];\n}\n"]}
1
+ {"version":3,"sources":["../src/convexSiteUrl.ts"],"names":[],"mappings":";AACO,SAAS,qBAAqB,SAAA,EAA2B;AAC9D,EAAA,MAAM,UAAU,SAAA,CAAU,IAAA,EAAK,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAClD,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,cAAc,CAAA,EAAG,OAAO,OAAA;AAC7C,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,eAAe,CAAA,EAAG;AACrC,IAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,mBAAA,EAAqB,cAAc,CAAA;AAAA,EAC5D;AACA,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,qBAAqB,OAAA,EAAyB;AAC5D,EAAA,MAAM,UAAU,OAAA,CAAQ,IAAA,EAAK,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAChD,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,eAAe,CAAA,EAAG,OAAO,OAAA;AAC9C,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,cAAc,CAAA,EAAG;AACpC,IAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,kBAAA,EAAoB,eAAe,CAAA;AAAA,EAC5D;AACA,EAAA,OAAO,OAAA;AACT;AAIA,SAAS,aAAa,KAAA,EAAoC;AACxD,EAAA,MAAM,OAAA,GAAU,OAAO,IAAA,EAAK;AAC5B,EAAA,OAAO,OAAA,IAAW,MAAA;AACpB;AAEA,SAAS,uBAAuB,GAAA,EAAqB;AACnD,EAAA,OAAO,GAAA,CAAI,IAAA,EAAK,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AACrC;AAEA,SAAS,uBAAuB,KAAA,EAAoC;AAClE,EAAA,MAAM,OAAA,GAAU,OAAO,IAAA,EAAK;AAC5B,EAAA,IAAI,CAAC,SAAS,OAAO,MAAA;AACrB,EAAA,IAAI,QAAQ,QAAA,CAAS,eAAe,KAAK,OAAA,CAAQ,QAAA,CAAS,cAAc,CAAA,EAAG;AACzE,IAAA,OAAO,qBAAqB,OAAO,CAAA;AAAA,EACrC;AACA,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,+BAA+B,GAAA,EAAsB;AACnE,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA,GAAI,GAAA,GAAM,CAAA,QAAA,EAAW,GAAG,CAAA,CAAE,CAAA,CAAE,QAAA;AACjE,IAAA,OAAO,IAAA,KAAS,yBAAA,IAA6B,IAAA,CAAK,QAAA,CAAS,0BAA0B,CAAA;AAAA,EACvF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,4BAAA,CAA6B,KAAK,GAAG,CAAA;AAAA,EAC9C;AACF;AAOO,SAAS,2BAAA,CAA4B,GAAA,GAAsB,OAAA,CAAQ,GAAA,EAAyB;AACjG,EAAA,KAAA,MAAW,GAAA,IAAO,CAAC,8BAAA,EAAgC,4BAA4B,CAAA,EAAY;AACzF,IAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,GAAA,CAAI,GAAG,CAAC,CAAA;AACnC,IAAA,IAAI,KAAA,IAAS,CAAC,8BAAA,CAA+B,KAAK,CAAA,EAAG;AACnD,MAAA,OAAO,uBAAuB,KAAK,CAAA;AAAA,IACrC;AAAA,EACF;AACA,EAAA,MAAM,eAAA,GAAkB,YAAA,CAAa,GAAA,CAAI,0BAA0B,CAAA;AACnE,EAAA,IAAI,eAAA,EAAiB,OAAO,oBAAA,CAAqB,eAAe,CAAA;AAChE,EAAA,OAAO,MAAA;AACT;AAOO,SAAS,gCAAgC,OAAA,EAGzB;AACrB,EAAA,MAAM,SAAA,GAAY,YAAA,CAAa,OAAA,CAAQ,YAAA,IAAgB,MAAS,CAAA;AAChE,EAAA,IAAI,SAAA,IAAa,CAAC,8BAAA,CAA+B,SAAS,CAAA,EAAG;AAC3D,IAAA,OAAO,uBAAuB,SAAS,CAAA;AAAA,EACzC;AACA,EAAA,OAAO,2BAAA,CAA4B,OAAA,CAAQ,GAAA,IAAO,OAAA,CAAQ,GAAG,CAAA;AAC/D;AAEA,SAAS,qBAAqB,GAAA,EAAiC;AAC7D,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,2CAA2C,CAAA;AACnE,EAAA,OAAO,QAAQ,CAAC,CAAA;AAClB;AAGO,SAAS,4BAA4B,GAAA,EAAiC;AAC3E,EAAA,OAAO,qBAAqB,GAAG,CAAA;AACjC;AAMO,SAAS,mCAAmC,QAAA,EAAsC;AACvF,EAAA,MAAM,IAAA,GAAO,qBAAqB,QAAQ,CAAA;AAC1C,EAAA,OAAO,IAAA,GAAO,CAAA,IAAA,EAAO,IAAI,CAAA,CAAA,GAAK,MAAA;AAChC;AAEA,SAAS,wBAAA,CAAyB,UAAkB,KAAA,EAAwB;AAC1E,EAAA,MAAM,YAAA,GAAe,qBAAqB,QAAQ,CAAA;AAClD,EAAA,MAAM,SAAA,GAAY,qBAAqB,KAAK,CAAA;AAC5C,EAAA,IAAI,CAAC,YAAA,IAAgB,CAAC,SAAA,EAAW,OAAO,IAAA;AACxC,EAAA,OAAO,YAAA,KAAiB,SAAA;AAC1B;AAkBO,SAAS,qBAAqB,GAAA,EAAuC;AAC1E,EAAA,OAAO;AAAA,IACL,eAAe,GAAA,CAAI,eAAA;AAAA,IACnB,mBAAmB,GAAA,CAAI,oBAAA;AAAA,IACvB,WAAW,GAAA,CAAI,UAAA;AAAA,IACf,eAAe,GAAA,CAAI,eAAA;AAAA,IACnB,gBAAgB,GAAA,CAAI;AAAA,GACtB;AACF;AAMO,SAAS,4BAA4B,GAAA,EAA+B;AACzE,EAAA,MAAM,WAAW,YAAA,CAAa,GAAA,CAAI,aAAa,CAAA,IAAK,YAAA,CAAa,IAAI,iBAAiB,CAAA;AACtF,EAAA,MAAM,KAAA,GACJ,YAAA,CAAa,GAAA,CAAI,SAAS,CAAA,IAC1B,YAAA,CAAa,GAAA,CAAI,aAAa,CAAA,IAC9B,YAAA,CAAa,GAAA,CAAI,cAAc,CAAA;AAEjC,EAAA,IAAI,YAAY,KAAA,IAAS,CAAC,wBAAA,CAAyB,QAAA,EAAU,KAAK,CAAA,EAAG;AACnE,IAAA,OAAO,sBAAA,CAAuB,KAAK,CAAA,IAAK,sBAAA,CAAuB,QAAQ,CAAA;AAAA,EACzE;AAEA,EAAA,IAAI,QAAA,EAAU,OAAO,sBAAA,CAAuB,QAAQ,CAAA;AAEpD,EAAA,OAAO,sBAAA,CAAuB,KAAK,CAAA,IAAK,EAAA;AAC1C;AAGO,SAAS,2BAAA,CAA4B,GAAA,GAAsB,OAAA,CAAQ,GAAA,EAAa;AACrF,EAAA,OAAO,2BAAA,CAA4B,oBAAA,CAAqB,GAAG,CAAC,CAAA;AAC9D;AAGO,SAAS,qBAAqB,GAAA,EAAgC;AACnE,EAAA,MAAM,GAAA,GAAM,2BAAA,CAA4B,GAAA,IAAO,EAAE,CAAA;AACjD,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,gEAAgE,CAAA;AAAA,EAClF;AACA,EAAA,OAAO,GAAA;AACT;AAUO,SAAS,+BAA+B,GAAA,EAAkC;AAC/E,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,GAAA,CAAI,eAAe,CAAA;AAC9C,EAAA,IAAI,KAAA,EAAO,OAAO,sBAAA,CAAuB,KAAK,CAAA;AAE9C,EAAA,OAAO,4BAA4B,GAAG,CAAA;AACxC;AASO,SAAS,+BACd,GAAA,GAAsB,OAAA,CAAQ,GAAA,EAC9B,OAAA,GAAiD,EAAC,EAC1C;AACR,EAAA,OAAO,8BAAA,CAA+B;AAAA,IACpC,iBAAiB,OAAA,CAAQ,eAAA;AAAA,IACzB,eAAe,GAAA,CAAI,eAAA;AAAA,IACnB,GAAI,QAAQ,kBAAA,GACR;AAAA,MACE,WAAW,GAAA,CAAI,UAAA;AAAA,MACf,eAAe,GAAA,CAAI;AAAA,QAErB;AAAC,GACN,CAAA;AACH;AAGO,SAAS,qBAAqB,UAAA,EAA+B;AAClE,EAAA,MAAM,GAAA,GAAM,UAAA,EAAY,IAAA,EAAK,IAAK,EAAA;AAClC,EAAA,IAAI,CAAC,GAAA,EAAK,OAAO,EAAC;AAClB,EAAA,OAAO,GAAA,CACJ,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAC,GAAA,KAAQ,GAAA,CAAI,IAAA,EAAM,CAAA,CACvB,MAAA,CAAO,OAAO,CAAA;AACnB;AAEO,SAAS,sBAAsB,UAAA,EAA6B;AACjE,EAAA,MAAM,IAAA,GAAO,qBAAqB,UAAU,CAAA;AAC5C,EAAA,OAAO,IAAA,CAAK,CAAC,CAAA,IAAK,UAAA,EAAY,MAAK,IAAK,EAAA;AAC1C;AAGO,SAAS,yBAAyB,UAAA,EAA+B;AACtE,EAAA,MAAM,IAAA,GAAO,qBAAqB,UAAU,CAAA;AAC5C,EAAA,IAAI,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG,OAAO,IAAA;AAC5B,EAAA,MAAM,OAAA,GAAU,sBAAsB,UAAU,CAAA;AAChD,EAAA,OAAO,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI,EAAC;AAChC","file":"convexSiteUrl.js","sourcesContent":["/** Convex deployment cloud URL (.convex.cloud) → HTTP actions / auth issuer (.convex.site). */\nexport function convexCloudToSiteUrl(convexUrl: string): string {\n const trimmed = convexUrl.trim().replace(/\\/$/, \"\");\n if (trimmed.includes(\".convex.site\")) return trimmed;\n if (trimmed.includes(\".convex.cloud\")) {\n return trimmed.replace(/\\.convex\\.cloud\\b/, \".convex.site\");\n }\n return trimmed;\n}\n\nexport function convexSiteToCloudUrl(siteUrl: string): string {\n const trimmed = siteUrl.trim().replace(/\\/$/, \"\");\n if (trimmed.includes(\".convex.cloud\")) return trimmed;\n if (trimmed.includes(\".convex.site\")) {\n return trimmed.replace(/\\.convex\\.site\\b/, \".convex.cloud\");\n }\n return trimmed;\n}\n\ntype ProcessEnvLike = Record<string, string | undefined>;\n\nfunction trimOptional(value?: string): string | undefined {\n const trimmed = value?.trim();\n return trimmed || undefined;\n}\n\nfunction normalizeTrailingSlash(url: string): string {\n return url.trim().replace(/\\/$/, \"\");\n}\n\nfunction deriveSiteFromCloudUrl(cloud?: string): string | undefined {\n const trimmed = cloud?.trim();\n if (!trimmed) return undefined;\n if (trimmed.includes(\".convex.cloud\") || trimmed.includes(\".convex.site\")) {\n return convexCloudToSiteUrl(trimmed);\n }\n return undefined;\n}\n\n/** Docker runtime-base verify placeholder — must not be used for real BFF prepare. */\nexport function isPlaceholderManagementSiteUrl(url: string): boolean {\n try {\n const host = new URL(/:\\/\\//.test(url) ? url : `https://${url}`).hostname;\n return host === \"placeholder.convex.site\" || host.endsWith(\".placeholder.convex.site\");\n } catch {\n return /placeholder\\.convex\\.site/i.test(url);\n }\n}\n\n/**\n * Management `.convex.site` origin for BFF `prepare`.\n * Prefers explicit management site env vars, else derives from `VITE_MANAGEMENT_CONVEX_URL`.\n * Does not read `VITE_CONVEX_SITE_URL` — on web that is the tenant site, not Manage.\n */\nexport function resolveBffManagementSiteUrl(env: ProcessEnvLike = process.env): string | undefined {\n for (const key of [\"COLUMNUS_MANAGEMENT_SITE_URL\", \"MANAGEMENT_CONVEX_SITE_URL\"] as const) {\n const value = trimOptional(env[key]);\n if (value && !isPlaceholderManagementSiteUrl(value)) {\n return normalizeTrailingSlash(value);\n }\n }\n const managementCloud = trimOptional(env.VITE_MANAGEMENT_CONVEX_URL);\n if (managementCloud) return convexCloudToSiteUrl(managementCloud);\n return undefined;\n}\n\n/**\n * Runtime BFF management site: prefer the image-baked URL over runtime defaults.\n * A stale runtime `MANAGEMENT_CONVEX_SITE_URL` must not shadow the\n * ephemeral Manage preview bake and makes prepare return HTTP 404 (no routes on prod).\n */\nexport function resolveRuntimeManagementSiteUrl(options: {\n bakedSiteUrl?: string | null;\n env?: ProcessEnvLike;\n}): string | undefined {\n const bakedSite = trimOptional(options.bakedSiteUrl ?? undefined);\n if (bakedSite && !isPlaceholderManagementSiteUrl(bakedSite)) {\n return normalizeTrailingSlash(bakedSite);\n }\n return resolveBffManagementSiteUrl(options.env ?? process.env);\n}\n\nfunction convexDeploymentSlug(url: string): string | undefined {\n const match = url.match(/https:\\/\\/([^.]+)\\.convex\\.(?:cloud|site)/);\n return match?.[1];\n}\n\n/** Deployment name from a Convex cloud/site URL (`https://{name}.convex.cloud`). */\nexport function convexDeploymentSlugFromUrl(url: string): string | undefined {\n return convexDeploymentSlug(url);\n}\n\n/**\n * Best-effort `CONVEX_DEPLOYMENT` when only the cloud URL is known (e.g. after `columnus-api pull`).\n * Defaults to `dev:{slug}` — preview/prod deployments may need an explicit value in `.env.local`.\n */\nexport function deriveConvexDeploymentFromCloudUrl(cloudUrl: string): string | undefined {\n const slug = convexDeploymentSlug(cloudUrl);\n return slug ? `dev:${slug}` : undefined;\n}\n\nfunction explicitSiteMatchesCloud(explicit: string, cloud: string): boolean {\n const explicitSlug = convexDeploymentSlug(explicit);\n const cloudSlug = convexDeploymentSlug(cloud);\n if (!explicitSlug || !cloudSlug) return true;\n return explicitSlug === cloudSlug;\n}\n\nexport type ConvexSiteUrlEnv = {\n /** CONVEX_SITE_URL */\n convexSiteUrl?: string;\n /** VITE_CONVEX_SITE_URL */\n viteConvexSiteUrl?: string;\n /** CONVEX_URL */\n convexUrl?: string;\n /** VITE_CONVEX_URL */\n viteConvexUrl?: string;\n /** CONVEX_CLOUD_URL */\n convexCloudUrl?: string;\n};\n\n/** @deprecated Use `ConvexSiteUrlEnv` */\nexport type ResolveConvexSiteUrlOptions = ConvexSiteUrlEnv;\n\nexport function readConvexSiteUrlEnv(env: ProcessEnvLike): ConvexSiteUrlEnv {\n return {\n convexSiteUrl: env.CONVEX_SITE_URL,\n viteConvexSiteUrl: env.VITE_CONVEX_SITE_URL,\n convexUrl: env.CONVEX_URL,\n viteConvexUrl: env.VITE_CONVEX_URL,\n convexCloudUrl: env.CONVEX_CLOUD_URL,\n };\n}\n\n/**\n * Resolve .convex.site URL from env: explicit site URLs first, then derive from Convex cloud/site URLs.\n * Returns empty string when nothing is configured.\n */\nexport function resolveConvexSiteUrlFromEnv(env: ConvexSiteUrlEnv): string {\n const explicit = trimOptional(env.convexSiteUrl) ?? trimOptional(env.viteConvexSiteUrl);\n const cloud =\n trimOptional(env.convexUrl) ??\n trimOptional(env.viteConvexUrl) ??\n trimOptional(env.convexCloudUrl);\n\n if (explicit && cloud && !explicitSiteMatchesCloud(explicit, cloud)) {\n return deriveSiteFromCloudUrl(cloud) ?? normalizeTrailingSlash(explicit);\n }\n\n if (explicit) return normalizeTrailingSlash(explicit);\n\n return deriveSiteFromCloudUrl(cloud) ?? \"\";\n}\n\n/** Better Auth `baseURL` from standard Convex process env. */\nexport function convexSiteUrlFromProcessEnv(env: ProcessEnvLike = process.env): string {\n return resolveConvexSiteUrlFromEnv(readConvexSiteUrlEnv(env));\n}\n\n/** Resolve .convex.site URL for OTP sign-in and scripts (throws when unset). */\nexport function resolveConvexSiteUrl(env?: ConvexSiteUrlEnv): string {\n const url = resolveConvexSiteUrlFromEnv(env ?? {});\n if (!url) {\n throw new Error(\"Convex URL required to derive .convex.site URL for OTP sign-in\");\n }\n return url;\n}\n\nexport type AuthConfigIssuerEnv = ConvexSiteUrlEnv & {\n /** BAKED_AUTH_ISSUER patched at deploy time */\n bakedAuthIssuer?: string;\n};\n\n/**\n * Auth issuer for deploy scripts: baked deploy patch → convex site URL env chain.\n */\nexport function resolveAuthConfigIssuerFromEnv(env: AuthConfigIssuerEnv): string {\n const baked = trimOptional(env.bakedAuthIssuer);\n if (baked) return normalizeTrailingSlash(baked);\n\n return resolveConvexSiteUrlFromEnv(env);\n}\n\nexport type AuthConfigIssuerFromProcessEnvOptions = {\n bakedAuthIssuer?: string;\n /** Derive from CONVEX_URL / VITE_CONVEX_URL when issuer env vars are unset (deploy scripts). */\n deriveFromCloudUrl?: boolean;\n};\n\n/** JWT issuer from process env (auth.config.ts bundle vs deploy scripts). */\nexport function authConfigIssuerFromProcessEnv(\n env: ProcessEnvLike = process.env,\n options: AuthConfigIssuerFromProcessEnvOptions = {},\n): string {\n return resolveAuthConfigIssuerFromEnv({\n bakedAuthIssuer: options.bakedAuthIssuer,\n convexSiteUrl: env.CONVEX_SITE_URL,\n ...(options.deriveFromCloudUrl\n ? {\n convexUrl: env.CONVEX_URL,\n viteConvexUrl: env.VITE_CONVEX_URL,\n }\n : {}),\n });\n}\n\n/** Comma-separated browser origins (SITE_URL) for Better Auth trustedOrigins / crossDomain. */\nexport function parseTrustedSiteUrls(siteUrlRaw?: string): string[] {\n const raw = siteUrlRaw?.trim() ?? \"\";\n if (!raw) return [];\n return raw\n .split(\",\")\n .map((url) => url.trim())\n .filter(Boolean);\n}\n\nexport function primaryTrustedSiteUrl(siteUrlRaw?: string): string {\n const urls = parseTrustedSiteUrls(siteUrlRaw);\n return urls[0] ?? siteUrlRaw?.trim() ?? \"\";\n}\n\n/** Better Auth `trustedOrigins` from SITE_URL. */\nexport function betterAuthTrustedOrigins(siteUrlRaw?: string): string[] {\n const urls = parseTrustedSiteUrls(siteUrlRaw);\n if (urls.length > 0) return urls;\n const primary = primaryTrustedSiteUrl(siteUrlRaw);\n return primary ? [primary] : [];\n}\n"]}