@coreframe/scripts 0.1.16 → 0.1.18

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.
@@ -49,6 +49,19 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
49
49
  tauriWorkflowRef: string | undefined;
50
50
  yes: boolean;
51
51
  };
52
+ } | {
53
+ command: "staging";
54
+ args: {
55
+ command: "reassign";
56
+ args: {
57
+ json: boolean;
58
+ };
59
+ } | {
60
+ command: "url";
61
+ args: {
62
+ json: boolean;
63
+ };
64
+ };
52
65
  } | {
53
66
  command: "typecheck";
54
67
  args: {};
@@ -180,6 +193,19 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
180
193
  tauriWorkflowRef: string | undefined;
181
194
  yes: boolean;
182
195
  };
196
+ } | {
197
+ command: "staging";
198
+ args: {
199
+ command: "reassign";
200
+ args: {
201
+ json: boolean;
202
+ };
203
+ } | {
204
+ command: "url";
205
+ args: {
206
+ json: boolean;
207
+ };
208
+ };
183
209
  } | {
184
210
  command: "typecheck";
185
211
  args: {};
@@ -282,6 +308,15 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
282
308
  } | {
283
309
  command: "deploy";
284
310
  value: Promise<void>;
311
+ } | {
312
+ command: "staging";
313
+ value: {
314
+ command: "reassign";
315
+ value: Promise<void>;
316
+ } | {
317
+ command: "url";
318
+ value: Promise<void>;
319
+ };
285
320
  } | {
286
321
  command: "typecheck";
287
322
  value: Promise<void>;
@@ -358,6 +393,15 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
358
393
  } | {
359
394
  command: "deploy";
360
395
  value: Promise<void>;
396
+ } | {
397
+ command: "staging";
398
+ value: {
399
+ command: "reassign";
400
+ value: Promise<void>;
401
+ } | {
402
+ command: "url";
403
+ value: Promise<void>;
404
+ };
361
405
  } | {
362
406
  command: "typecheck";
363
407
  value: Promise<void>;
package/dist/coreframe.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{addAsset as e,buildAssets as t,checkAssets as n,printAssetResult as r,publishAssets as i}from"./assets.js";import{runDrizzleKitCommand as a}from"./db/drizzle-kit.js";import{seedLocalDb as o}from"./db/seed.js";import{migrateLocalDb as s}from"./db/migrate.js";import{resetLocalDb as c}from"./db/reset.js";import{setupLocalDb as l}from"./db/setup.js";import{ensureLocalDb as u}from"./db/ensure.js";import{migrateHeldMigrations as d}from"./db/migrate-held.js";import{checkSchemaPush as f}from"./db/schema-check.js";import{deployEnvironments as p,tauriReleasePlatforms as m}from"./deploy/config.js";import{deployProject as h}from"./deploy.js";import{checkDeployMigrations as g}from"./deploy/check-migrations.js";import{devPlatforms as _,runDevServer as v}from"./dev.js";import{run as y}from"./runner.js";import{typecheckProject as b}from"./typecheck.js";import{checkUpgradeSkillCurrent as x}from"./upgrade/check-skill-current.js";import{ensureTemplateRemote as S}from"./upgrade/ensure-template-remote.js";import{fastForwardTemplateFiles as C}from"./upgrade/fast-forward-template-files.js";import{array as w,command as T,extendType as E,flag as D,multioption as O,number as k,oneOf as A,option as j,optional as M,positional as N,rest as P,string as F,subcommands as I}from"cmd-ts";const L=E(F,{displayName:`widths`,description:`Comma-separated responsive variant widths.`,async from(e){let t=e.split(`,`).map(e=>Number(e));if(t.length===0||t.some(e=>!Number.isInteger(e)||e<=0)||t.some((e,n)=>n>0&&e<=t[n-1]))throw Error(`Widths must be positive, unique, strictly ascending integers.`);return t}}),R=I({name:`coreframe`,cmds:{assets:I({name:`assets`,cmds:{add:T({name:`add`,args:{file:N({type:F,displayName:`file`,description:`Source image to add to the asset library.`}),id:j({long:`id`,type:F,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:j({long:`alt`,type:F,description:`Required alt text for the asset.`}),variantWidths:j({long:`widths`,type:M(L),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({alt:t,file:n,id:i,json:a,variantWidths:o})=>r(await e({alt:t,file:n,id:i,json:a,variantWidths:o}),a)}),build:T({name:`build`,args:{id:j({long:`id`,type:M(F),description:`Build only one logical asset ID.`}),noPull:D({long:`no-pull`,description:`Do not perform targeted Git LFS pulls for missing source objects.`,defaultValue:()=>!1}),json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({id:e,json:n,noPull:i})=>r(await t({id:e,json:n,pull:!i}),n)}),check:T({name:`check`,args:{fix:D({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({fix:e,json:t})=>{let i=await n({fix:e,json:t});r(i,t),i.ok||(process.exitCode=1)}}),publish:T({name:`publish`,args:{outputDir:j({long:`output-dir`,type:M(F),description:`Root directory for additive local publish output. Defaults to COREFRAME_ASSETS_PUBLISH_DIR or release-upload.`}),json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e,outputDir:t})=>{let n=await i({json:e,outputDir:t});r(n,e),n.ok||(process.exitCode=1)}}),status:T({name:`status`,args:{json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await n({json:e});r(t,e),t.ok||(process.exitCode=1)}})}}),deploy:T({name:`deploy`,args:{environment:N({type:A(p),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:D({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:D({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:D({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:O({long:`tauri-local-platform`,type:w(A(m)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:j({long:`tauri-workflow-name`,type:M(F),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:O({long:`tauri-workflow-platform`,type:w(A(m)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:j({long:`tauri-workflow-ref`,type:M(F),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:D({long:`yes`,short:`y`,description:`Approve the production deployment confirmation.`,defaultValue:()=>!1})},handler:({environment:e,runMigrations:t,skipMigrations:n,skipPostDeployChecks:r,tauriLocalPlatforms:i,tauriWorkflowName:a,tauriWorkflowPlatforms:o,tauriWorkflowRef:s,yes:c})=>h([e,...t?[`--run-migrations`]:[],...n?[`--skip-migrations`]:[],...r?[`--skip-post-deploy-checks`]:[],...i.flatMap(e=>[`--tauri-local-platform`,e]),...a?[`--tauri-workflow-name`,a]:[],...o.flatMap(e=>[`--tauri-workflow-platform`,e]),...s?[`--tauri-workflow-ref`,s]:[],...c?[`--yes`]:[]])}),typecheck:T({name:`typecheck`,args:{},handler:b}),dev:T({name:`dev`,args:{https:D({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:j({long:`host`,type:M(F),description:`Host address for the web development server.`}),managed:D({long:`managed`,description:`Run managed checkout synchronization even without an interactive terminal.`,defaultValue:()=>!1}),noManaged:D({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:N({type:M(A(_)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),port:j({long:`port`,type:M(k),description:`Port for the web development server.`})},handler:({host:e,https:t,managed:n,noManaged:r,platform:i,port:a})=>v({host:e,https:t,managed:n,noManaged:r,platform:i??`web`,webPort:a})}),upgrade:I({name:`upgrade`,cmds:{"ensure-template-remote":T({name:`ensure-template-remote`,args:{remote:j({long:`remote`,type:F,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:D({long:`no-fetch`,description:`Do not fetch the remote after ensuring it exists.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t})=>S({fetch:!e,remote:t})}),"check-skill-current":T({name:`check-skill-current`,args:{target:j({long:`target`,type:F,description:`Target Coreframe template git ref.`}),apply:D({long:`apply`,description:`Replace the local upgrade skill with the target version when it differs.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>{process.exitCode=x({apply:e,target:t})}}),"fast-forward-template-files":T({name:`fast-forward-template-files`,args:{target:j({long:`target`,type:F,description:`Target Coreframe template git ref.`}),base:j({long:`base`,type:M(F),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:D({long:`apply`,description:`Write clean fast-forwards to the worktree.`,defaultValue:()=>!1})},handler:({apply:e,base:t,target:n})=>C({apply:e,base:t,target:n})})}}),db:I({name:`db`,cmds:{ensure:T({name:`ensure`,args:{},handler:u}),generate:T({name:`generate`,args:{args:P({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>a(`generate`,e)}),migrate:T({name:`migrate`,args:{args:P({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>a(`migrate`,e)}),"migrate-held":T({name:`migrate-held`,args:{confirm:j({long:`confirm`,type:M(A([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>d({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":T({name:`migrate-local`,args:{},handler:s}),"migrations-check":T({name:`migrations-check`,args:{},handler:g}),reset:T({name:`reset`,args:{},handler:c}),seed:T({name:`seed`,args:{},handler:()=>o()}),"schema-check":T({name:`schema-check`,args:{seed:D({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>f({seed:e})}),setup:T({name:`setup`,args:{},handler:()=>l()}),studio:T({name:`studio`,args:{args:P({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>a(`studio`,e)})}})}});import.meta.main&&y(R);export{R as coreframeCli};
2
+ import{addAsset as e,buildAssets as t,checkAssets as n,printAssetResult as r,publishAssets as i}from"./assets.js";import{runDrizzleKitCommand as a}from"./db/drizzle-kit.js";import{seedLocalDb as o}from"./db/seed.js";import{migrateLocalDb as s}from"./db/migrate.js";import{resetLocalDb as c}from"./db/reset.js";import{setupLocalDb as l}from"./db/setup.js";import{ensureLocalDb as u}from"./db/ensure.js";import{migrateHeldMigrations as d}from"./db/migrate-held.js";import{checkSchemaPush as f}from"./db/schema-check.js";import{deployEnvironments as p,tauriReleasePlatforms as m}from"./deploy/config.js";import{discoverStagingUrl as h,reassignStagingHostname as g}from"./staging.js";import{deployProject as _}from"./deploy.js";import{checkDeployMigrations as v}from"./deploy/check-migrations.js";import{devPlatforms as y,runDevServer as b}from"./dev.js";import{run as x}from"./runner.js";import{typecheckProject as S}from"./typecheck.js";import{checkUpgradeSkillCurrent as C}from"./upgrade/check-skill-current.js";import{ensureTemplateRemote as w}from"./upgrade/ensure-template-remote.js";import{fastForwardTemplateFiles as T}from"./upgrade/fast-forward-template-files.js";import{array as E,command as D,extendType as O,flag as k,multioption as A,number as j,oneOf as M,option as N,optional as P,positional as F,rest as I,string as L,subcommands as R}from"cmd-ts";const z=[`Cookies and sessions are origin-scoped.`,`Exact-host passkeys may require re-registration unless a stable eligible parent RP ID was configured in advance.`,`External OAuth callbacks such as Google may need updating.`,`Database data and private backend resources are unchanged.`],B=O(L,{displayName:`widths`,description:`Comma-separated responsive variant widths.`,async from(e){let t=e.split(`,`).map(e=>Number(e));if(t.length===0||t.some(e=>!Number.isInteger(e)||e<=0)||t.some((e,n)=>n>0&&e<=t[n-1]))throw Error(`Widths must be positive, unique, strictly ascending integers.`);return t}}),V=R({name:`coreframe`,cmds:{assets:R({name:`assets`,cmds:{add:D({name:`add`,args:{file:F({type:L,displayName:`file`,description:`Source image to add to the asset library.`}),id:N({long:`id`,type:L,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:N({long:`alt`,type:L,description:`Required alt text for the asset.`}),variantWidths:N({long:`widths`,type:P(B),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:k({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({alt:t,file:n,id:i,json:a,variantWidths:o})=>r(await e({alt:t,file:n,id:i,json:a,variantWidths:o}),a)}),build:D({name:`build`,args:{id:N({long:`id`,type:P(L),description:`Build only one logical asset ID.`}),noPull:k({long:`no-pull`,description:`Do not perform targeted Git LFS pulls for missing source objects.`,defaultValue:()=>!1}),json:k({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({id:e,json:n,noPull:i})=>r(await t({id:e,json:n,pull:!i}),n)}),check:D({name:`check`,args:{fix:k({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:k({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({fix:e,json:t})=>{let i=await n({fix:e,json:t});r(i,t),i.ok||(process.exitCode=1)}}),publish:D({name:`publish`,args:{outputDir:N({long:`output-dir`,type:P(L),description:`Root directory for additive local publish output. Defaults to COREFRAME_ASSETS_PUBLISH_DIR or release-upload.`}),json:k({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e,outputDir:t})=>{let n=await i({json:e,outputDir:t});r(n,e),n.ok||(process.exitCode=1)}}),status:D({name:`status`,args:{json:k({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await n({json:e});r(t,e),t.ok||(process.exitCode=1)}})}}),deploy:D({name:`deploy`,args:{environment:F({type:M(p),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:k({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:k({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:k({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:A({long:`tauri-local-platform`,type:E(M(m)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:N({long:`tauri-workflow-name`,type:P(L),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:A({long:`tauri-workflow-platform`,type:E(M(m)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:N({long:`tauri-workflow-ref`,type:P(L),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:k({long:`yes`,short:`y`,description:`Approve the production deployment confirmation.`,defaultValue:()=>!1})},handler:({environment:e,runMigrations:t,skipMigrations:n,skipPostDeployChecks:r,tauriLocalPlatforms:i,tauriWorkflowName:a,tauriWorkflowPlatforms:o,tauriWorkflowRef:s,yes:c})=>_([e,...t?[`--run-migrations`]:[],...n?[`--skip-migrations`]:[],...r?[`--skip-post-deploy-checks`]:[],...i.flatMap(e=>[`--tauri-local-platform`,e]),...a?[`--tauri-workflow-name`,a]:[],...o.flatMap(e=>[`--tauri-workflow-platform`,e]),...s?[`--tauri-workflow-ref`,s]:[],...c?[`--yes`]:[]])}),staging:R({name:`staging`,cmds:{reassign:D({name:`reassign`,args:{json:k({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await g();if(e){console.log(JSON.stringify({...t,notices:z}));return}console.log(t.url),console.error(`Staging hostname reassigned. ${z.join(` `)}`)}}),url:D({name:`url`,args:{json:k({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await h();console.log(e?JSON.stringify({url:t}):t)}})}}),typecheck:D({name:`typecheck`,args:{},handler:S}),dev:D({name:`dev`,args:{https:k({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:N({long:`host`,type:P(L),description:`Host address for the web development server.`}),managed:k({long:`managed`,description:`Run managed checkout synchronization even without an interactive terminal.`,defaultValue:()=>!1}),noManaged:k({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:F({type:P(M(y)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),port:N({long:`port`,type:P(j),description:`Port for the web development server.`})},handler:({host:e,https:t,managed:n,noManaged:r,platform:i,port:a})=>b({host:e,https:t,managed:n,noManaged:r,platform:i??`web`,webPort:a})}),upgrade:R({name:`upgrade`,cmds:{"ensure-template-remote":D({name:`ensure-template-remote`,args:{remote:N({long:`remote`,type:L,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:k({long:`no-fetch`,description:`Do not fetch the remote after ensuring it exists.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t})=>w({fetch:!e,remote:t})}),"check-skill-current":D({name:`check-skill-current`,args:{target:N({long:`target`,type:L,description:`Target Coreframe template git ref.`}),apply:k({long:`apply`,description:`Replace the local upgrade skill with the target version when it differs.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>{process.exitCode=C({apply:e,target:t})}}),"fast-forward-template-files":D({name:`fast-forward-template-files`,args:{target:N({long:`target`,type:L,description:`Target Coreframe template git ref.`}),base:N({long:`base`,type:P(L),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:k({long:`apply`,description:`Write clean fast-forwards to the worktree.`,defaultValue:()=>!1})},handler:({apply:e,base:t,target:n})=>T({apply:e,base:t,target:n})})}}),db:R({name:`db`,cmds:{ensure:D({name:`ensure`,args:{},handler:u}),generate:D({name:`generate`,args:{args:I({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>a(`generate`,e)}),migrate:D({name:`migrate`,args:{args:I({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>a(`migrate`,e)}),"migrate-held":D({name:`migrate-held`,args:{confirm:N({long:`confirm`,type:P(M([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>d({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":D({name:`migrate-local`,args:{},handler:s}),"migrations-check":D({name:`migrations-check`,args:{},handler:v}),reset:D({name:`reset`,args:{},handler:c}),seed:D({name:`seed`,args:{},handler:()=>o()}),"schema-check":D({name:`schema-check`,args:{seed:k({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>f({seed:e})}),setup:D({name:`setup`,args:{},handler:()=>l()}),studio:D({name:`studio`,args:{args:I({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>a(`studio`,e)})}})}});import.meta.main&&x(V);export{V as coreframeCli};
@@ -2,5 +2,6 @@ import { DeployContext, ResolvedDeployConfig } from "./config.js";
2
2
 
3
3
  //#region deploy/checks.d.ts
4
4
  declare function runPostDeployChecks(config: ResolvedDeployConfig, context: DeployContext): Promise<void>;
5
+ declare function runDeploySmokeChecks(config: ResolvedDeployConfig, context: DeployContext, request?: typeof fetch, log?: (message: string) => void): Promise<void>;
5
6
  //#endregion
6
- export { runPostDeployChecks };
7
+ export { runDeploySmokeChecks, runPostDeployChecks };
@@ -1 +1 @@
1
- async function e(e,n){for(let r of e.smokeChecks){let e=r.url??new URL(r.path??`/`,n.url).toString(),i=await fetch(e);if(!t(i.status,r.expectedStatus??`2xx`))throw Error(`${r.name} smoke check failed: ${e} returned ${i.status}.`);console.log(`${r.name} smoke check passed: ${i.status} ${e}`)}await e.afterDeploy?.(n)}function t(e,t){return t===`2xx`?e>=200&&e<300:Array.isArray(t)?t.includes(e):e===t}export{e as runPostDeployChecks};
1
+ async function e(e,n){await t(e,n),await e.afterDeploy?.(n)}async function t(e,t,r=fetch,i=console.log){for(let a of e.smokeChecks){let e=a.url??new URL(a.path??`/`,t.url).toString(),o=await r(e);if(!n(o.status,a.expectedStatus??`2xx`))throw Error(`${a.name} smoke check failed: ${e} returned ${o.status}.`);i(`${a.name} smoke check passed: ${o.status} ${e}`)}}function n(e,t){return t===`2xx`?e>=200&&e<300:Array.isArray(t)?t.includes(e):e===t}export{t as runDeploySmokeChecks,e as runPostDeployChecks};
@@ -41,19 +41,26 @@ type DeploySmokeCheck = {
41
41
  type DeployEnvironmentConfig = {
42
42
  url?: string;
43
43
  };
44
+ type StagingHostnameConfig = {
45
+ managedLabelPrefix: string;
46
+ parentHostname: string;
47
+ workerService: string;
48
+ };
44
49
  type DeployConfig = {
45
50
  afterDeploy?: (context: DeployContext) => Promise<void> | void;
46
51
  environments?: Partial<Record<DeployEnvironment, DeployEnvironmentConfig>>;
47
52
  smokeChecks?: DeploySmokeCheck[];
53
+ stagingHostname?: StagingHostnameConfig;
48
54
  tauriRelease?: TauriReleaseConfig;
49
55
  };
50
56
  type ResolvedDeployConfig = {
51
57
  afterDeploy?: (context: DeployContext) => Promise<void> | void;
52
58
  environment: DeployEnvironment;
53
59
  smokeChecks: DeploySmokeCheck[];
60
+ stagingHostname?: StagingHostnameConfig;
54
61
  tauriRelease: TauriReleaseConfig;
55
62
  url?: string;
56
63
  };
57
64
  declare function resolveDeployConfig(config: DeployConfig, environment: DeployEnvironment, env?: NodeJS.ProcessEnv): ResolvedDeployConfig;
58
65
  //#endregion
59
- export { DeployConfig, DeployContext, DeployEnvironment, DeployEnvironmentConfig, DeploySmokeCheck, ResolvedDeployConfig, TauriReleaseConfig, TauriReleasePlatform, TauriReleasePlatformConfig, deployEnvironments, resolveDeployConfig, tauriReleasePlatforms };
66
+ export { DeployConfig, DeployContext, DeployEnvironment, DeployEnvironmentConfig, DeploySmokeCheck, ResolvedDeployConfig, StagingHostnameConfig, TauriReleaseConfig, TauriReleasePlatform, TauriReleasePlatformConfig, deployEnvironments, resolveDeployConfig, tauriReleasePlatforms };
@@ -1 +1 @@
1
- const e=[`staging`,`production`],t=[`macos`,`windows`,`linux`,`ios`,`android`];function n(e,t,n=process.env){return{afterDeploy:e.afterDeploy,environment:t,smokeChecks:e.smokeChecks??[{name:`home`,path:`/`}],tauriRelease:e.tauriRelease??{},url:n.DEPLOY_URL||e.environments?.[t]?.url}}export{e as deployEnvironments,n as resolveDeployConfig,t as tauriReleasePlatforms};
1
+ const e=[`staging`,`production`],t=[`macos`,`windows`,`linux`,`ios`,`android`];function n(e,t,n=process.env){return{afterDeploy:e.afterDeploy,environment:t,smokeChecks:e.smokeChecks??[{name:`home`,path:`/`}],stagingHostname:e.stagingHostname,tauriRelease:e.tauriRelease??{},url:n.DEPLOY_URL||(t===`staging`&&e.stagingHostname?void 0:e.environments?.[t]?.url)}}export{e as deployEnvironments,n as resolveDeployConfig,t as tauriReleasePlatforms};
@@ -65,6 +65,7 @@ declare function resolveTauriReleasePlan({
65
65
  env: NodeJS.ProcessEnv;
66
66
  }): TauriReleasePlan;
67
67
  declare function validateTauriReleasePlan(plan: TauriReleasePlan, cwd?: string): Promise<void>;
68
+ declare function validateTauriReleaseIdentifier(tauriConfigSource: string, tauriConfigPath?: string): string;
68
69
  declare function writeTauriReleaseMetadata(plan: TauriReleasePlan, version: string, cwd?: string): Promise<TauriReleaseMetadataSnapshot>;
69
70
  declare function restoreTauriReleaseMetadata(snapshot: TauriReleaseMetadataSnapshot): Promise<void>;
70
71
  declare function prepareTauriRelease({
@@ -102,4 +103,4 @@ declare function createTauriReleaseUpload({
102
103
  version: string;
103
104
  }): Promise<PreparedTauriRelease>;
104
105
  //#endregion
105
- export { PreparedTauriRelease, TauriReleaseArgs, TauriReleaseMetadataSnapshot, TauriReleasePlan, createTauriReleaseUpload, createTauriUpdateManifest, parseTauriReleasePlatform, parseTauriReleasePlatformList, prepareTauriRelease, resolveTauriReleasePlan, restoreTauriReleaseMetadata, uploadTauriReleaseToB2, validateTauriReleasePlan, writeTauriReleaseMetadata };
106
+ export { PreparedTauriRelease, TauriReleaseArgs, TauriReleaseMetadataSnapshot, TauriReleasePlan, createTauriReleaseUpload, createTauriUpdateManifest, parseTauriReleasePlatform, parseTauriReleasePlatformList, prepareTauriRelease, resolveTauriReleasePlan, restoreTauriReleaseMetadata, uploadTauriReleaseToB2, validateTauriReleaseIdentifier, validateTauriReleasePlan, writeTauriReleaseMetadata };
@@ -1 +1 @@
1
- import{tauriReleasePlatforms as e}from"./config.js";import{randomUUID as t}from"node:crypto";import{access as n,cp as r,mkdir as i,readFile as a,rm as o,stat as s,writeFile as c}from"node:fs/promises";import{basename as l,dirname as u,join as d,relative as f,resolve as p,sep as m}from"node:path";import h from"nano-spawn";import{constants as g}from"node:fs";import{setTimeout as _}from"node:timers/promises";import{glob as v}from"tinyglobby";const y={android:{artifactGlobs:[`tauri/src-tauri/target/**/*.sig`,`tauri/gen/android/**/*.sig`],buildArgs:[`tauri`,`android`,`build`,`--target`,`aarch64-linux-android`],targetTriple:`aarch64-linux-android`,updatePlatform:`android-aarch64`},ios:{artifactGlobs:[`tauri/src-tauri/target/**/*.sig`,`tauri/gen/apple/**/*.sig`],buildArgs:[`tauri`,`ios`,`build`,`--target`,`aarch64-apple-ios`],targetTriple:`aarch64-apple-ios`,updatePlatform:`ios-aarch64`},linux:{artifactGlobs:[`tauri/src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/**/*.sig`,`tauri/src-tauri/target/release/bundle/**/*.sig`,`tauri/src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`x86_64-unknown-linux-gnu`,`--`,`--locked`],targetTriple:`x86_64-unknown-linux-gnu`,updatePlatform:`linux-x86_64`},macos:{artifactGlobs:[`tauri/src-tauri/target/aarch64-apple-darwin/release/bundle/**/*.sig`,`tauri/src-tauri/target/release/bundle/**/*.sig`,`tauri/src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`aarch64-apple-darwin`,`--`,`--locked`],targetTriple:`aarch64-apple-darwin`,updatePlatform:`darwin-aarch64`},windows:{artifactGlobs:[`tauri/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/**/*.sig`,`tauri/src-tauri/target/release/bundle/**/*.sig`,`tauri/src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`x86_64-pc-windows-msvc`,`--`,`--locked`],targetTriple:`x86_64-pc-windows-msvc`,updatePlatform:`windows-x86_64`}};function b(e){return e?e.split(/[,\s]+/u).filter(Boolean).map(e=>x(e)):[]}function x(t){if(Y(t))return t;throw Error(`Invalid Tauri release platform "${t}". Expected one of: ${e.join(`, `)}.`)}function S({args:e,config:t={},env:n}){let r=J([...t.localPlatforms??[],...b(n.TAURI_RELEASE_LOCAL_PLATFORMS),...e.tauriLocalPlatforms]),i=J([...t.workflowPlatforms??[],...b(n.TAURI_RELEASE_WORKFLOW_PLATFORMS),...e.tauriWorkflowPlatforms]),a=r.filter(e=>i.includes(e));if(a.length>0)throw Error(`Build each Tauri release platform either locally or through the workflow, not both: ${a.join(`, `)}.`);return{artifactDownloadDir:n.TAURI_RELEASE_ARTIFACT_DOWNLOAD_DIR??t.artifactDownloadDir??`.coreframe/tauri-artifacts`,b2ApplicationKey:n.B2_APPLICATION_KEY??n.B2_SECRET_ACCESS_KEY,b2ApplicationKeyId:n.B2_APPLICATION_KEY_ID??n.B2_KEY_ID??n.B2_ACCESS_KEY_ID,b2Bucket:t.b2Bucket??n.B2_BUCKET,enabled:r.length>0||i.length>0,latestManifestPath:n.TAURI_RELEASE_LATEST_MANIFEST_PATH??t.latestManifestPath??`latest.json`,localPlatforms:r,platform:I(t.platform,n),publicBaseUrl:t.publicBaseUrl??n.TAURI_RELEASE_PUBLIC_BASE_URL??n.UPDATE_PUBLIC_BASE_URL,publicPathPrefix:n.TAURI_RELEASE_PUBLIC_PATH_PREFIX??t.publicPathPrefix??n.UPDATE_PUBLIC_PATH_PREFIX??``,releaseNotesPath:n.TAURI_RELEASE_NOTES_FILE??t.releaseNotesPath,releasePath:n.TAURI_RELEASE_PATH??t.releasePath??"releases/${version}",releaseUploadDir:n.TAURI_RELEASE_UPLOAD_DIR??t.releaseUploadDir??`release-upload`,signingPrivateKey:n.TAURI_SIGNING_PRIVATE_KEY,workflowName:e.tauriWorkflowName??n.TAURI_RELEASE_WORKFLOW_NAME??t.workflowName??`tauri-release-artifacts.yml`,workflowPlatforms:i,workflowPollIntervalMs:q(n.TAURI_RELEASE_WORKFLOW_POLL_SECONDS,`TAURI_RELEASE_WORKFLOW_POLL_SECONDS`)*1e3||t.workflowPollIntervalMs||15e3,workflowRef:e.tauriWorkflowRef??n.TAURI_RELEASE_WORKFLOW_REF??t.workflowRef,workflowTimeoutMs:q(n.TAURI_RELEASE_WORKFLOW_TIMEOUT_MINUTES,`TAURI_RELEASE_WORKFLOW_TIMEOUT_MINUTES`)*6e4||t.workflowTimeoutMs||36e5}}async function C(e,t=process.cwd()){if(!e.enabled)return;let n=[[`TAURI_RELEASE_PUBLIC_BASE_URL`,e.publicBaseUrl],[`B2_APPLICATION_KEY_ID`,e.b2ApplicationKeyId],[`B2_APPLICATION_KEY`,e.b2ApplicationKey],[`B2_BUCKET`,e.b2Bucket]].flatMap(([e,t])=>t?[]:[e]);if(n.length>0)throw Error(`Missing required Tauri release configuration: ${n.join(`, `)}`);try{new URL(e.publicBaseUrl)}catch{throw Error(`Invalid TAURI_RELEASE_PUBLIC_BASE_URL: ${e.publicBaseUrl}`)}if(!await X(d(t,`tauri/src-tauri/tauri.conf.json`)))throw Error(`Tauri release platforms were requested, but tauri/src-tauri/tauri.conf.json does not exist.`);if(e.localPlatforms.length>0&&!e.signingPrivateKey)throw Error(`TAURI_SIGNING_PRIVATE_KEY is required for local Tauri release builds.`);e.workflowPlatforms.length>0&&await h(`gh`,[`workflow`,`view`,e.workflowName,`--ref`,e.workflowRef??await V()],{stdin:`ignore`}),await h(`b2`,[`version`],{stdin:`ignore`})}async function w(e,t,n=process.cwd()){if(!e.enabled)return{files:[]};let r=d(n,`tauri/src-tauri/tauri.conf.json`),i=d(n,`tauri/src-tauri/Cargo.toml`),o=d(n,`tauri/src-tauri/Cargo.lock`),s=await a(r,`utf8`),l=await a(i,`utf8`),u=R(l,i),f=[{path:r,source:s},{path:i,source:l}],p=[],m=JSON.parse(s);if(m.version=t,m.bundle={...m.bundle??{},createUpdaterArtifacts:!0},p.push({path:r,source:`${JSON.stringify(m,null,2)}\n`}),p.push({path:i,source:z(l,u,t,i)}),await X(o)){let e=await a(o,`utf8`);f.push({path:o,source:e}),p.push({path:o,source:B(e,u,t,o)})}for(let e of p)await c(e.path,e.source);return{files:f}}async function T(e){for(let t of e.files)await c(t.path,t.source)}async function E({env:e,plan:t,releaseName:n,version:r}){if(!t.enabled)return;await H(t.artifactDownloadDir),await H(t.releaseUploadDir);let i=t.workflowPlatforms.length>0?await A({plan:t,releaseName:n,version:r}):void 0;for(let n of t.localPlatforms)await N(n,t,e);if(i){let e=await j(t,i.runNameMarker);await h(`gh`,[`run`,`download`,String(e),`--dir`,t.artifactDownloadDir],{stdio:`inherit`})}return k({plan:t,version:r})}async function D(e,t){await h(`b2`,[`account`,`authorize`,e.b2ApplicationKeyId,e.b2ApplicationKey],{stdin:`ignore`});let n=(await v(`**/*`,{cwd:t.uploadDir,onlyFiles:!0})).sort((e,n)=>e===t.manifestPath?1:n===t.manifestPath?-1:e.localeCompare(n));for(let r of n)await h(`b2`,[`file`,`upload`,e.b2Bucket,d(t.uploadDir,r),r],{stdin:`ignore`}),console.log(`Uploaded ${r}`)}function O({artifacts:e,notes:t,pubDate:n=new Date,version:r}){return{...t?{notes:t}:{},platforms:Object.fromEntries(e.map(e=>[e.updatePlatform,{signature:e.signature,url:e.url}])),pub_date:n.toISOString(),version:r}}async function k({plan:e,version:t}){let n=K(e.publicPathPrefix,W(e.releasePath,t)),o=K(e.publicPathPrefix,e.latestManifestPath),s=[...e.localPlatforms,...e.workflowPlatforms],f=[];for(let o of s){let s=e.platform[o],c=e.localPlatforms.includes(o)?`.`:d(e.artifactDownloadDir,`tauri-${o}-${t}`),p=await P({globs:e.localPlatforms.includes(o)?s.artifactGlobs:[`**/*.sig`],sourceRoot:c,version:t}),m=K(n,s.updatePlatform),h=K(m,l(p.artifactPath)),g=K(m,l(p.signaturePath)),_=d(e.releaseUploadDir,...h.split(`/`)),v=d(e.releaseUploadDir,...g.split(`/`)),y=(await a(p.signaturePath,`utf8`)).trim();await i(u(_),{recursive:!0}),await r(p.artifactPath,_),await r(p.signaturePath,v),f.push({signature:y,updatePlatform:s.updatePlatform,url:G(e.publicBaseUrl,h)})}let p=O({artifacts:f,notes:e.releaseNotesPath?await a(e.releaseNotesPath,`utf8`):void 0,version:t}),m=d(e.releaseUploadDir,...o.split(`/`));return await i(u(m),{recursive:!0}),await c(m,`${JSON.stringify(p,null,2)}\n`),{manifestPath:o,uploadDir:e.releaseUploadDir}}async function A({plan:n,releaseName:r,version:i}){let a=n.workflowRef??await V(),o=`coreframe-${Date.now()}-${t().slice(0,8)}`;return await h(`gh`,[`workflow`,`run`,n.workflowName,`--ref`,a,`-f`,`release_name=${r}`,`-f`,`run_id=${o}`,`-f`,`version=${i}`,...e.flatMap(e=>[`-f`,`${e}=${n.workflowPlatforms.includes(e)?`true`:`false`}`])],{stdin:`ignore`}),{runNameMarker:o}}async function j(e,t){let n=Date.now()+e.workflowTimeoutMs;for(;Date.now()<n;){let n=await M(e,t);if(n?.status===`completed`){if(n.conclusion===`success`)return n.databaseId;throw Error(`Tauri release workflow ${n.databaseId} finished with ${n.conclusion}.`)}await _(e.workflowPollIntervalMs)}throw Error(`Timed out waiting for Tauri release workflow after ${Math.round(e.workflowTimeoutMs/6e4)} minutes.`)}async function M(e,t){let{stdout:n}=await h(`gh`,[`run`,`list`,`--workflow`,e.workflowName,`--event`,`workflow_dispatch`,`--json`,`conclusion,databaseId,displayTitle,status`,`--limit`,`50`],{stdin:`ignore`});return JSON.parse(n).find(e=>e.displayTitle.includes(t))}async function N(e,t,n){await h(`pnpm`,t.platform[e].buildArgs,{env:n,stdio:`inherit`})}async function P({globs:e,sourceRoot:t,version:n}){let r=await v(e,{cwd:t,absolute:!0,onlyFiles:!0}),i=[];for(let e of r){let t=e.replace(/\.sig$/u,``);await Z(t)&&i.push({artifactPath:t,signaturePath:e})}if(i.length===0)throw Error(`Could not find a Tauri updater artifact and .sig pair under ${t}.`);return i.sort((e,t)=>{let r=F(e.artifactPath,n);return F(t.artifactPath,n)-r||e.artifactPath.localeCompare(t.artifactPath)})[0]}function F(e,t){let n=0;return e.includes(t)&&(n+=100),/\.(app\.tar\.gz|AppImage\.tar\.gz|zip)$/u.test(e)&&(n+=10),/\.(exe|msi|dmg|AppImage|deb|rpm|apk|aab|ipa)$/u.test(e)&&(n+=5),n}function I(t={},n){return Object.fromEntries(e.map(e=>{let r=t[e]??{},i={...y[e],...r},a=n[`TAURI_RELEASE_${e.toUpperCase()}_TARGET_TRIPLE`],o=n[`TAURI_RELEASE_${e.toUpperCase()}_UPDATE_PLATFORM`],s=a??i.targetTriple,c=r.buildArgs?r.buildArgs:L(e,s);return[e,{...i,artifactGlobs:i.artifactGlobs??y[e].artifactGlobs,buildArgs:c,targetTriple:s,updatePlatform:o??i.updatePlatform}]}))}function L(e,t){return e===`android`?[`tauri`,`android`,`build`,`--target`,t]:e===`ios`?[`tauri`,`ios`,`build`,`--target`,t]:[`tauri`,`build`,`--target`,t,`--`,`--locked`]}function R(e,t){let n=/^\[package\][\s\S]*?^name = "(?<name>[^"]+)"/mu.exec(e);if(!n?.groups?.name)throw Error(`${t} is missing a [package] name.`);return n.groups.name}function z(e,t,n,r){let i=RegExp(`(^\\[package\\][\\s\\S]*?^name = "${Q(t)}"[\\s\\S]*?^version = ")[^"]+(")`,`mu`),a=e.replace(i,`$1${n}$2`);if(a===e)throw Error(`${r} is missing a package version for ${t}.`);return a}function B(e,t,n,r){let i=RegExp(`(\\[\\[package\\]\\]\\nname = "${Q(t)}"\\nversion = ")[^"]+(")`,`u`),a=e.replace(i,`$1${n}$2`);if(a===e)throw Error(`${r} is missing a package lock entry for ${t}.`);return a}async function V(){let{stdout:e}=await h(`git`,[`rev-parse`,`--abbrev-ref`,`HEAD`],{stdin:`ignore`}),t=e.trim();if(t!==`HEAD`)return t;let{stdout:n}=await h(`git`,[`rev-parse`,`HEAD`],{stdin:`ignore`});return n.trim()}async function H(e){U(e),await o(e,{force:!0,recursive:!0}),await i(e,{recursive:!0})}function U(e){let t=p(e),n=f(process.cwd(),t).split(m).join(`/`);if(n===``||n===`.`||n.startsWith(`../`)||n===`tauri`||n.startsWith(`tauri/`))throw Error(`Refusing to reset unsafe Tauri release directory: ${e}`)}function W(e,t){return e.replaceAll("${version}",t)}function G(e,t){let n=new URL(e),r=K(n.pathname.replace(/\/+$/u,``),t);return n.pathname=r.startsWith(`/`)?r:`/${r}`,n.toString()}function K(...e){return e.flatMap(e=>e.split(`/`)).map(e=>e.trim()).filter(Boolean).join(`/`)}function q(e,t){if(!e)return 0;let n=Number(e);if(!Number.isInteger(n)||n<=0)throw Error(`${t} must be a positive integer.`);return n}function J(e){return[...new Set(e)]}function Y(t){return e.includes(t)}async function X(e){try{return await n(e,g.F_OK),!0}catch{return!1}}async function Z(e){try{return(await s(e)).isFile()}catch{return!1}}function Q(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,`\\$&`)}export{k as createTauriReleaseUpload,O as createTauriUpdateManifest,x as parseTauriReleasePlatform,b as parseTauriReleasePlatformList,E as prepareTauriRelease,S as resolveTauriReleasePlan,T as restoreTauriReleaseMetadata,D as uploadTauriReleaseToB2,C as validateTauriReleasePlan,w as writeTauriReleaseMetadata};
1
+ import{tauriReleasePlatforms as e}from"./config.js";import{randomUUID as t}from"node:crypto";import{access as n,cp as r,mkdir as i,readFile as a,rm as o,stat as s,writeFile as c}from"node:fs/promises";import{basename as l,dirname as u,join as d,relative as f,resolve as p,sep as m}from"node:path";import h from"nano-spawn";import{constants as g}from"node:fs";import{setTimeout as _}from"node:timers/promises";import{glob as v}from"tinyglobby";const y={android:{artifactGlobs:[`tauri/src-tauri/target/**/*.sig`,`tauri/gen/android/**/*.sig`],buildArgs:[`tauri`,`android`,`build`,`--target`,`aarch64-linux-android`],targetTriple:`aarch64-linux-android`,updatePlatform:`android-aarch64`},ios:{artifactGlobs:[`tauri/src-tauri/target/**/*.sig`,`tauri/gen/apple/**/*.sig`],buildArgs:[`tauri`,`ios`,`build`,`--target`,`aarch64-apple-ios`],targetTriple:`aarch64-apple-ios`,updatePlatform:`ios-aarch64`},linux:{artifactGlobs:[`tauri/src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/**/*.sig`,`tauri/src-tauri/target/release/bundle/**/*.sig`,`tauri/src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`x86_64-unknown-linux-gnu`,`--`,`--locked`],targetTriple:`x86_64-unknown-linux-gnu`,updatePlatform:`linux-x86_64`},macos:{artifactGlobs:[`tauri/src-tauri/target/aarch64-apple-darwin/release/bundle/**/*.sig`,`tauri/src-tauri/target/release/bundle/**/*.sig`,`tauri/src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`aarch64-apple-darwin`,`--`,`--locked`],targetTriple:`aarch64-apple-darwin`,updatePlatform:`darwin-aarch64`},windows:{artifactGlobs:[`tauri/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/**/*.sig`,`tauri/src-tauri/target/release/bundle/**/*.sig`,`tauri/src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`x86_64-pc-windows-msvc`,`--`,`--locked`],targetTriple:`x86_64-pc-windows-msvc`,updatePlatform:`windows-x86_64`}};function b(e){return e?e.split(/[,\s]+/u).filter(Boolean).map(e=>x(e)):[]}function x(t){if(X(t))return t;throw Error(`Invalid Tauri release platform "${t}". Expected one of: ${e.join(`, `)}.`)}function S({args:e,config:t={},env:n}){let r=Y([...t.localPlatforms??[],...b(n.TAURI_RELEASE_LOCAL_PLATFORMS),...e.tauriLocalPlatforms]),i=Y([...t.workflowPlatforms??[],...b(n.TAURI_RELEASE_WORKFLOW_PLATFORMS),...e.tauriWorkflowPlatforms]),a=r.filter(e=>i.includes(e));if(a.length>0)throw Error(`Build each Tauri release platform either locally or through the workflow, not both: ${a.join(`, `)}.`);return{artifactDownloadDir:n.TAURI_RELEASE_ARTIFACT_DOWNLOAD_DIR??t.artifactDownloadDir??`.coreframe/tauri-artifacts`,b2ApplicationKey:n.B2_APPLICATION_KEY??n.B2_SECRET_ACCESS_KEY,b2ApplicationKeyId:n.B2_APPLICATION_KEY_ID??n.B2_KEY_ID??n.B2_ACCESS_KEY_ID,b2Bucket:t.b2Bucket??n.B2_BUCKET,enabled:r.length>0||i.length>0,latestManifestPath:n.TAURI_RELEASE_LATEST_MANIFEST_PATH??t.latestManifestPath??`latest.json`,localPlatforms:r,platform:L(t.platform,n),publicBaseUrl:t.publicBaseUrl??n.TAURI_RELEASE_PUBLIC_BASE_URL??n.UPDATE_PUBLIC_BASE_URL,publicPathPrefix:n.TAURI_RELEASE_PUBLIC_PATH_PREFIX??t.publicPathPrefix??n.UPDATE_PUBLIC_PATH_PREFIX??``,releaseNotesPath:n.TAURI_RELEASE_NOTES_FILE??t.releaseNotesPath,releasePath:n.TAURI_RELEASE_PATH??t.releasePath??"releases/${version}",releaseUploadDir:n.TAURI_RELEASE_UPLOAD_DIR??t.releaseUploadDir??`release-upload`,signingPrivateKey:n.TAURI_SIGNING_PRIVATE_KEY,workflowName:e.tauriWorkflowName??n.TAURI_RELEASE_WORKFLOW_NAME??t.workflowName??`tauri-release-artifacts.yml`,workflowPlatforms:i,workflowPollIntervalMs:J(n.TAURI_RELEASE_WORKFLOW_POLL_SECONDS,`TAURI_RELEASE_WORKFLOW_POLL_SECONDS`)*1e3||t.workflowPollIntervalMs||15e3,workflowRef:e.tauriWorkflowRef??n.TAURI_RELEASE_WORKFLOW_REF??t.workflowRef,workflowTimeoutMs:J(n.TAURI_RELEASE_WORKFLOW_TIMEOUT_MINUTES,`TAURI_RELEASE_WORKFLOW_TIMEOUT_MINUTES`)*6e4||t.workflowTimeoutMs||36e5}}async function C(e,t=process.cwd()){if(!e.enabled)return;let n=[[`TAURI_RELEASE_PUBLIC_BASE_URL`,e.publicBaseUrl],[`B2_APPLICATION_KEY_ID`,e.b2ApplicationKeyId],[`B2_APPLICATION_KEY`,e.b2ApplicationKey],[`B2_BUCKET`,e.b2Bucket]].flatMap(([e,t])=>t?[]:[e]);if(n.length>0)throw Error(`Missing required Tauri release configuration: ${n.join(`, `)}`);try{new URL(e.publicBaseUrl)}catch{throw Error(`Invalid TAURI_RELEASE_PUBLIC_BASE_URL: ${e.publicBaseUrl}`)}let r=d(t,`tauri/src-tauri/tauri.conf.json`);if(!await Z(r))throw Error(`Tauri release platforms were requested, but tauri/src-tauri/tauri.conf.json does not exist.`);if(w(await a(r,`utf8`),r),e.localPlatforms.length>0&&!e.signingPrivateKey)throw Error(`TAURI_SIGNING_PRIVATE_KEY is required for local Tauri release builds.`);e.workflowPlatforms.length>0&&await h(`gh`,[`workflow`,`view`,e.workflowName,`--ref`,e.workflowRef??await H()],{stdin:`ignore`}),await h(`b2`,[`version`],{stdin:`ignore`})}function w(e,t=`tauri/src-tauri/tauri.conf.json`){let n=JSON.parse(e).identifier;if(typeof n!=`string`||n===`com.example`||n.startsWith(`com.example.`))throw Error(`${t} must use an organization-owned reverse-domain identifier before building release artifacts.`);return n}async function T(e,t,n=process.cwd()){if(!e.enabled)return{files:[]};let r=d(n,`tauri/src-tauri/tauri.conf.json`),i=d(n,`tauri/src-tauri/Cargo.toml`),o=d(n,`tauri/src-tauri/Cargo.lock`),s=await a(r,`utf8`),l=await a(i,`utf8`),u=z(l,i),f=[{path:r,source:s},{path:i,source:l}],p=[],m=JSON.parse(s);if(m.version=t,m.bundle={...m.bundle??{},createUpdaterArtifacts:!0},p.push({path:r,source:`${JSON.stringify(m,null,2)}\n`}),p.push({path:i,source:B(l,u,t,i)}),await Z(o)){let e=await a(o,`utf8`);f.push({path:o,source:e}),p.push({path:o,source:V(e,u,t,o)})}for(let e of p)await c(e.path,e.source);return{files:f}}async function E(e){for(let t of e.files)await c(t.path,t.source)}async function D({env:e,plan:t,releaseName:n,version:r}){if(!t.enabled)return;await U(t.artifactDownloadDir),await U(t.releaseUploadDir);let i=t.workflowPlatforms.length>0?await j({plan:t,releaseName:n,version:r}):void 0;for(let n of t.localPlatforms)await P(n,t,e);if(i){let e=await M(t,i.runNameMarker);await h(`gh`,[`run`,`download`,String(e),`--dir`,t.artifactDownloadDir],{stdio:`inherit`})}return A({plan:t,version:r})}async function O(e,t){await h(`b2`,[`account`,`authorize`,e.b2ApplicationKeyId,e.b2ApplicationKey],{stdin:`ignore`});let n=(await v(`**/*`,{cwd:t.uploadDir,onlyFiles:!0})).sort((e,n)=>e===t.manifestPath?1:n===t.manifestPath?-1:e.localeCompare(n));for(let r of n)await h(`b2`,[`file`,`upload`,e.b2Bucket,d(t.uploadDir,r),r],{stdin:`ignore`}),console.log(`Uploaded ${r}`)}function k({artifacts:e,notes:t,pubDate:n=new Date,version:r}){return{...t?{notes:t}:{},platforms:Object.fromEntries(e.map(e=>[e.updatePlatform,{signature:e.signature,url:e.url}])),pub_date:n.toISOString(),version:r}}async function A({plan:e,version:t}){let n=q(e.publicPathPrefix,G(e.releasePath,t)),o=q(e.publicPathPrefix,e.latestManifestPath),s=[...e.localPlatforms,...e.workflowPlatforms],f=[];for(let o of s){let s=e.platform[o],c=e.localPlatforms.includes(o)?`.`:d(e.artifactDownloadDir,`tauri-${o}-${t}`),p=await F({globs:e.localPlatforms.includes(o)?s.artifactGlobs:[`**/*.sig`],sourceRoot:c,version:t}),m=q(n,s.updatePlatform),h=q(m,l(p.artifactPath)),g=q(m,l(p.signaturePath)),_=d(e.releaseUploadDir,...h.split(`/`)),v=d(e.releaseUploadDir,...g.split(`/`)),y=(await a(p.signaturePath,`utf8`)).trim();await i(u(_),{recursive:!0}),await r(p.artifactPath,_),await r(p.signaturePath,v),f.push({signature:y,updatePlatform:s.updatePlatform,url:K(e.publicBaseUrl,h)})}let p=k({artifacts:f,notes:e.releaseNotesPath?await a(e.releaseNotesPath,`utf8`):void 0,version:t}),m=d(e.releaseUploadDir,...o.split(`/`));return await i(u(m),{recursive:!0}),await c(m,`${JSON.stringify(p,null,2)}\n`),{manifestPath:o,uploadDir:e.releaseUploadDir}}async function j({plan:n,releaseName:r,version:i}){let a=n.workflowRef??await H(),o=`coreframe-${Date.now()}-${t().slice(0,8)}`;return await h(`gh`,[`workflow`,`run`,n.workflowName,`--ref`,a,`-f`,`release_name=${r}`,`-f`,`run_id=${o}`,`-f`,`version=${i}`,...e.flatMap(e=>[`-f`,`${e}=${n.workflowPlatforms.includes(e)?`true`:`false`}`])],{stdin:`ignore`}),{runNameMarker:o}}async function M(e,t){let n=Date.now()+e.workflowTimeoutMs;for(;Date.now()<n;){let n=await N(e,t);if(n?.status===`completed`){if(n.conclusion===`success`)return n.databaseId;throw Error(`Tauri release workflow ${n.databaseId} finished with ${n.conclusion}.`)}await _(e.workflowPollIntervalMs)}throw Error(`Timed out waiting for Tauri release workflow after ${Math.round(e.workflowTimeoutMs/6e4)} minutes.`)}async function N(e,t){let{stdout:n}=await h(`gh`,[`run`,`list`,`--workflow`,e.workflowName,`--event`,`workflow_dispatch`,`--json`,`conclusion,databaseId,displayTitle,status`,`--limit`,`50`],{stdin:`ignore`});return JSON.parse(n).find(e=>e.displayTitle.includes(t))}async function P(e,t,n){await h(`pnpm`,t.platform[e].buildArgs,{env:n,stdio:`inherit`})}async function F({globs:e,sourceRoot:t,version:n}){let r=await v(e,{cwd:t,absolute:!0,onlyFiles:!0}),i=[];for(let e of r){let t=e.replace(/\.sig$/u,``);await Q(t)&&i.push({artifactPath:t,signaturePath:e})}if(i.length===0)throw Error(`Could not find a Tauri updater artifact and .sig pair under ${t}.`);return i.sort((e,t)=>{let r=I(e.artifactPath,n);return I(t.artifactPath,n)-r||e.artifactPath.localeCompare(t.artifactPath)})[0]}function I(e,t){let n=0;return e.includes(t)&&(n+=100),/\.(app\.tar\.gz|AppImage\.tar\.gz|zip)$/u.test(e)&&(n+=10),/\.(exe|msi|dmg|AppImage|deb|rpm|apk|aab|ipa)$/u.test(e)&&(n+=5),n}function L(t={},n){return Object.fromEntries(e.map(e=>{let r=t[e]??{},i={...y[e],...r},a=n[`TAURI_RELEASE_${e.toUpperCase()}_TARGET_TRIPLE`],o=n[`TAURI_RELEASE_${e.toUpperCase()}_UPDATE_PLATFORM`],s=a??i.targetTriple,c=r.buildArgs?r.buildArgs:R(e,s);return[e,{...i,artifactGlobs:i.artifactGlobs??y[e].artifactGlobs,buildArgs:c,targetTriple:s,updatePlatform:o??i.updatePlatform}]}))}function R(e,t){return e===`android`?[`tauri`,`android`,`build`,`--target`,t]:e===`ios`?[`tauri`,`ios`,`build`,`--target`,t]:[`tauri`,`build`,`--target`,t,`--`,`--locked`]}function z(e,t){let n=/^\[package\][\s\S]*?^name = "(?<name>[^"]+)"/mu.exec(e);if(!n?.groups?.name)throw Error(`${t} is missing a [package] name.`);return n.groups.name}function B(e,t,n,r){let i=RegExp(`(^\\[package\\][\\s\\S]*?^name = "${$(t)}"[\\s\\S]*?^version = ")[^"]+(")`,`mu`),a=e.replace(i,`$1${n}$2`);if(a===e)throw Error(`${r} is missing a package version for ${t}.`);return a}function V(e,t,n,r){let i=RegExp(`(\\[\\[package\\]\\]\\nname = "${$(t)}"\\nversion = ")[^"]+(")`,`u`),a=e.replace(i,`$1${n}$2`);if(a===e)throw Error(`${r} is missing a package lock entry for ${t}.`);return a}async function H(){let{stdout:e}=await h(`git`,[`rev-parse`,`--abbrev-ref`,`HEAD`],{stdin:`ignore`}),t=e.trim();if(t!==`HEAD`)return t;let{stdout:n}=await h(`git`,[`rev-parse`,`HEAD`],{stdin:`ignore`});return n.trim()}async function U(e){W(e),await o(e,{force:!0,recursive:!0}),await i(e,{recursive:!0})}function W(e){let t=p(e),n=f(process.cwd(),t).split(m).join(`/`);if(n===``||n===`.`||n.startsWith(`../`)||n===`tauri`||n.startsWith(`tauri/`))throw Error(`Refusing to reset unsafe Tauri release directory: ${e}`)}function G(e,t){return e.replaceAll("${version}",t)}function K(e,t){let n=new URL(e),r=q(n.pathname.replace(/\/+$/u,``),t);return n.pathname=r.startsWith(`/`)?r:`/${r}`,n.toString()}function q(...e){return e.flatMap(e=>e.split(`/`)).map(e=>e.trim()).filter(Boolean).join(`/`)}function J(e,t){if(!e)return 0;let n=Number(e);if(!Number.isInteger(n)||n<=0)throw Error(`${t} must be a positive integer.`);return n}function Y(e){return[...new Set(e)]}function X(t){return e.includes(t)}async function Z(e){try{return await n(e,g.F_OK),!0}catch{return!1}}async function Q(e){try{return(await s(e)).isFile()}catch{return!1}}function $(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,`\\$&`)}export{A as createTauriReleaseUpload,k as createTauriUpdateManifest,x as parseTauriReleasePlatform,b as parseTauriReleasePlatformList,D as prepareTauriRelease,S as resolveTauriReleasePlan,E as restoreTauriReleaseMetadata,O as uploadTauriReleaseToB2,w as validateTauriReleaseIdentifier,C as validateTauriReleasePlan,T as writeTauriReleaseMetadata};
package/dist/deploy.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { DeployEnvironment, TauriReleasePlatform } from "./deploy/config.js";
1
+ import { DeployEnvironment, ResolvedDeployConfig, TauriReleasePlatform } from "./deploy/config.js";
2
2
  import { readProjectConfig } from "./config.js";
3
3
 
4
4
  //#region deploy.d.ts
@@ -15,5 +15,7 @@ type DeployArgs = {
15
15
  };
16
16
  declare function deployProject(argv?: string[], env?: NodeJS.ProcessEnv): Promise<void>;
17
17
  declare function parseDeployArgs(argv: string[]): DeployArgs;
18
+ declare function resolveDeployUrl(config: ResolvedDeployConfig, environment: DeployEnvironment, discover?: () => Promise<string>): Promise<string>;
19
+ declare function resolveStagingDashboardPassword(env: NodeJS.ProcessEnv): Promise<string>;
18
20
  //#endregion
19
- export { deployProject, parseDeployArgs, readProjectConfig };
21
+ export { deployProject, parseDeployArgs, readProjectConfig, resolveDeployUrl, resolveStagingDashboardPassword };
package/dist/deploy.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{readProjectConfig as e}from"./config.js";import{createProjectDeployDatabase as t}from"./db/project.js";import{collectDeployMigrationFiles as n,formatMigrationList as r,formatMigrationScanFailures as i,scanDeployMigrationFiles as a}from"./deploy/migrations.js";import{runPostDeployChecks as o}from"./deploy/checks.js";import{deployEnvironments as s,resolveDeployConfig as c}from"./deploy/config.js";import{filterPendingMigrations as l,readAppliedMigrationNames as u}from"./deploy/pending-migrations.js";import{parseTauriReleasePlatform as d,prepareTauriRelease as f,resolveTauriReleasePlan as p,restoreTauriReleaseMetadata as m,uploadTauriReleaseToB2 as h,validateTauriReleasePlan as g,writeTauriReleaseMetadata as _}from"./deploy/tauri-release.js";import{readPackageJson as v,releaseName as y,resolveReleaseVersion as b,writePackageVersion as x}from"./deploy/version.js";import{writeFile as S}from"node:fs/promises";import C from"nano-spawn";import*as w from"@clack/prompts";const T=`package.json`;async function E(n=process.argv.slice(2),r=process.env){let s=D(n);F(s);let{packageJson:l,source:u}=await v(T),d=await e(),E=c(d.deploy??{},s.environment,r);z(r,[`POSTHOG_API_KEY`,`POSTHOG_PROJECT_ID`]);let O=I(E.url,s.environment),k=p({args:s,config:s.environment===`production`?E.tauriRelease:{},env:r});R(s.environment,k.enabled),await g(k);let A=await P(),M=b(s.environment,l.version,A),H=y(l.name,M),U=B(r,s.environment,H,l.name,M),W=s.environment===`production`,G=!1,K={files:[]};w.intro(`Deploy ${H} to ${s.environment}`),await L(s);let q=await j(await t({config:d.database,env:r})),J=await a(q);if(J.length>0)throw Error(`Pending deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${i(J)}`);let Y=await N(s,q);try{W&&(await x(T,l,M),K=await _(k,M)),w.log.step(`Running pre-deploy build`),await C(`pnpm`,[`build`],{env:V(U),stdio:`inherit`});let e=k.enabled?await f({env:V(U),plan:k,releaseName:H,version:M}):void 0;w.log.step(`Running release build and uploading PostHog source maps`),G=!0,await C(`pnpm`,[`build`],{env:U,stdio:`inherit`}),Y&&(w.log.step(`Running deploy-safe database migrations`),await C(`pnpm`,[`db`,`migrate`],{env:U,stdio:`inherit`})),w.log.step(`Deploying Worker and static assets to Cloudflare`),await C(`pnpm`,[`exec`,`wrangler`,`deploy`,`--env`,s.environment,`--strict`,`--keep-vars`,`--tag`,H,`--message`,`Deploy ${H}`,`--var`,`DEPLOY_ENV:${s.environment}`,`--var`,`POSTHOG_RELEASE:${H}`],{env:U,stdio:`inherit`}),s.skipPostDeployChecks||(w.log.step(`Running post-deploy checks`),await o(E,{environment:s.environment,releaseName:H,url:O,version:M})),e&&(w.log.step(`Uploading Tauri release artifacts to Backblaze B2`),await h(k,e)),w.outro(`Deployed ${H} to ${s.environment}`)}catch(e){throw!G&&W&&(await S(T,u),await m(K)),e}}function D(e){return{environment:O(e[0]),runMigrations:e.includes(`--run-migrations`),skipMigrations:e.includes(`--skip-migrations`),skipPostDeployChecks:e.includes(`--skip-post-deploy-checks`),tauriLocalPlatforms:k(e,`--tauri-local-platform`).map(e=>d(e)),tauriWorkflowName:A(e,`--tauri-workflow-name`),tauriWorkflowPlatforms:k(e,`--tauri-workflow-platform`).map(e=>d(e)),tauriWorkflowRef:A(e,`--tauri-workflow-ref`),yes:e.includes(`--yes`)}}function O(e){if(e&&s.includes(e))return e;throw Error(`Deploy environment must be one of: ${s.join(`, `)}.`)}function k(e,t){let n=[];for(let r=0;r<e.length;r+=1){let i=e[r];if(i===t){let i=e[r+1];if(!i||i.startsWith(`--`))throw Error(`${t} requires a value.`);n.push(...i.split(`,`).filter(Boolean)),r+=1;continue}i.startsWith(`${t}=`)&&n.push(...i.slice(t.length+1).split(`,`).filter(Boolean))}return n}function A(e,t){let n=k(e,t);if(n.length>1)throw Error(`${t} can only be provided once.`);return n[0]}async function j(e){try{return await M(e)}finally{await e.close?.()}}async function M(e){return l(await n(),await u(e))}async function N(e,t){if(t.length===0)return w.log.info(`No pending deploy migrations.`),!1;if(w.note(r(t),`Pending deploy migrations`),e.runMigrations)return!0;if(e.skipMigrations)return!1;if(!process.stdout.isTTY)throw Error(`Pending migrations require --run-migrations or --skip-migrations in noninteractive deploys.`);let n=await w.confirm({initialValue:!0,message:`Run these deploy-safe database migrations before Cloudflare deploy?`});if(w.isCancel(n))throw w.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`);return n}async function P(){let{stdout:e}=await C(`git`,[`status`,`--porcelain`],{stdin:`ignore`});if(e.trim().length>0)throw Error(`Deploy requires a clean git tree.`);let{stdout:t}=await C(`git`,[`rev-parse`,`--short=12`,`HEAD`],{stdin:`ignore`});return t.trim()}function F(e){if(e.runMigrations&&e.skipMigrations)throw Error(`Use only one of --run-migrations or --skip-migrations.`)}function I(e,t){if(!e)throw Error(`Missing ${t} deploy URL. Set DEPLOY_URL or deploy.environments.${t}.url in coreframe.config.ts.`);let n;try{n=new URL(e)}catch{throw Error(`Invalid ${t} deploy URL "${e}". Expected an HTTP(S) URL.`)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw Error(`Invalid ${t} deploy URL "${e}". Expected an HTTP(S) URL.`);return n.toString()}async function L(e){if(e.environment!==`production`||e.yes)return;if(!process.stdout.isTTY)throw Error(`Production deploys require --yes in noninteractive environments.`);let t=await w.confirm({initialValue:!1,message:`Deploy to production?`});if(w.isCancel(t)||!t)throw w.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`)}function R(e,t){if(e===`staging`&&t)throw Error(`Tauri releases can only be published from production deploys.`)}function z(e,t){let n=t.filter(t=>!e[t]);if(n.length>0)throw Error(`Missing required deploy environment variables: ${n.join(`, `)}`);return Object.fromEntries(t.map(t=>[t,e[t]]))}function B(e,t,n,r,i){return{...e,CLOUDFLARE_ENV:t,DEPLOY_ENV:t,POSTHOG_RELEASE:n,POSTHOG_RELEASE_NAME:e.POSTHOG_RELEASE_NAME??r,POSTHOG_RELEASE_VERSION:e.POSTHOG_RELEASE_VERSION??i}}function V(e){let{POSTHOG_API_KEY:t,POSTHOG_PROJECT_ID:n,...r}=e;return r}export{E as deployProject,D as parseDeployArgs,e as readProjectConfig};
2
+ import{readProjectConfig as e}from"./config.js";import{createProjectDeployDatabase as t}from"./db/project.js";import{collectDeployMigrationFiles as n,formatMigrationList as r,formatMigrationScanFailures as i,scanDeployMigrationFiles as a}from"./deploy/migrations.js";import{runPostDeployChecks as o}from"./deploy/checks.js";import{deployEnvironments as s,resolveDeployConfig as c}from"./deploy/config.js";import{filterPendingMigrations as l,readAppliedMigrationNames as u}from"./deploy/pending-migrations.js";import{parseTauriReleasePlatform as d,prepareTauriRelease as f,resolveTauriReleasePlan as p,restoreTauriReleaseMetadata as m,uploadTauriReleaseToB2 as h,validateTauriReleasePlan as g,writeTauriReleaseMetadata as _}from"./deploy/tauri-release.js";import{readPackageJson as v,releaseName as y,resolveReleaseVersion as b,writePackageVersion as x}from"./deploy/version.js";import{discoverStagingUrl as S}from"./staging.js";import{mkdtemp as C,readFile as w,rm as T,writeFile as E}from"node:fs/promises";import{join as D}from"node:path";import O from"nano-spawn";import{tmpdir as k}from"node:os";import{parseEnv as A}from"node:util";import*as j from"@clack/prompts";const M=`package.json`;async function N(n=process.argv.slice(2),r=process.env){let s=P(n);H(s);let{packageJson:l,source:u}=await v(M),d=await e(),C=c(d.deploy??{},s.environment,r);q(r,[`POSTHOG_API_KEY`,`POSTHOG_PROJECT_ID`]);let w=await U(C,s.environment,()=>S({env:r})),T=p({args:s,config:s.environment===`production`?C.tauriRelease:{},env:r});K(s.environment,T.enabled),await g(T);let D=s.environment===`staging`?await J(r):void 0,k=await V(),A=b(s.environment,l.version,k),N=y(l.name,A),F=Z(r,s.environment,N,l.name,A),I=s.environment===`production`,L=!1,z={files:[]};j.intro(`Deploy ${N} to ${s.environment}`),await G(s);let W=await R(await t({config:d.database,env:r})),X=await a(W);if(X.length>0)throw Error(`Pending deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${i(X)}`);let $=await B(s,W);try{I&&(await x(M,l,A),z=await _(T,A)),j.log.step(`Running pre-deploy build`),await O(`pnpm`,[`build`],{env:Q(F),stdio:`inherit`});let e=T.enabled?await f({env:Q(F),plan:T,releaseName:N,version:A}):void 0;j.log.step(`Running release build and uploading PostHog source maps`),L=!0,await O(`pnpm`,[`build`],{env:F,stdio:`inherit`}),$&&(j.log.step(`Running deploy-safe database migrations`),await O(`pnpm`,[`db`,`migrate`],{env:F,stdio:`inherit`})),j.log.step(`Deploying Worker and static assets to Cloudflare`);let t=D?await Y(D):void 0;try{await O(`pnpm`,[`exec`,`wrangler`,`deploy`,`--env`,s.environment,`--strict`,`--keep-vars`,`--tag`,N,`--message`,`Deploy ${N}`,`--var`,`DEPLOY_ENV:${s.environment}`,`--var`,`POSTHOG_RELEASE:${N}`,...t?[`--secrets-file`,t.path]:[]],{env:F,stdio:`inherit`})}finally{await t?.dispose()}s.skipPostDeployChecks||(j.log.step(`Running post-deploy checks`),await o(C,{environment:s.environment,releaseName:N,url:w,version:A})),e&&(j.log.step(`Uploading Tauri release artifacts to Backblaze B2`),await h(T,e)),j.outro(`Deployed ${N} to ${s.environment}`)}catch(e){throw!L&&I&&(await E(M,u),await m(z)),e}}function P(e){return{environment:F(e[0]),runMigrations:e.includes(`--run-migrations`),skipMigrations:e.includes(`--skip-migrations`),skipPostDeployChecks:e.includes(`--skip-post-deploy-checks`),tauriLocalPlatforms:I(e,`--tauri-local-platform`).map(e=>d(e)),tauriWorkflowName:L(e,`--tauri-workflow-name`),tauriWorkflowPlatforms:I(e,`--tauri-workflow-platform`).map(e=>d(e)),tauriWorkflowRef:L(e,`--tauri-workflow-ref`),yes:e.includes(`--yes`)}}function F(e){if(e&&s.includes(e))return e;throw Error(`Deploy environment must be one of: ${s.join(`, `)}.`)}function I(e,t){let n=[];for(let r=0;r<e.length;r+=1){let i=e[r];if(i===t){let i=e[r+1];if(!i||i.startsWith(`--`))throw Error(`${t} requires a value.`);n.push(...i.split(`,`).filter(Boolean)),r+=1;continue}i.startsWith(`${t}=`)&&n.push(...i.slice(t.length+1).split(`,`).filter(Boolean))}return n}function L(e,t){let n=I(e,t);if(n.length>1)throw Error(`${t} can only be provided once.`);return n[0]}async function R(e){try{return await z(e)}finally{await e.close?.()}}async function z(e){return l(await n(),await u(e))}async function B(e,t){if(t.length===0)return j.log.info(`No pending deploy migrations.`),!1;if(j.note(r(t),`Pending deploy migrations`),e.runMigrations)return!0;if(e.skipMigrations)return!1;if(!process.stdout.isTTY)throw Error(`Pending migrations require --run-migrations or --skip-migrations in noninteractive deploys.`);let n=await j.confirm({initialValue:!0,message:`Run these deploy-safe database migrations before Cloudflare deploy?`});if(j.isCancel(n))throw j.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`);return n}async function V(){let{stdout:e}=await O(`git`,[`status`,`--porcelain`],{stdin:`ignore`});if(e.trim().length>0)throw Error(`Deploy requires a clean git tree.`);let{stdout:t}=await O(`git`,[`rev-parse`,`--short=12`,`HEAD`],{stdin:`ignore`});return t.trim()}function H(e){if(e.runMigrations&&e.skipMigrations)throw Error(`Use only one of --run-migrations or --skip-migrations.`)}async function U(e,t,n=S){return e.url?W(e.url,t):t===`staging`&&e.stagingHostname?W(await n(),t):W(void 0,t)}function W(e,t){if(!e)throw Error(`Missing ${t} deploy URL. Set DEPLOY_URL or deploy.environments.${t}.url in coreframe.config.ts.`);let n;try{n=new URL(e)}catch{throw Error(`Invalid ${t} deploy URL "${e}". Expected an HTTP(S) URL.`)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw Error(`Invalid ${t} deploy URL "${e}". Expected an HTTP(S) URL.`);return n.toString()}async function G(e){if(e.environment!==`production`||e.yes)return;if(!process.stdout.isTTY)throw Error(`Production deploys require --yes in noninteractive environments.`);let t=await j.confirm({initialValue:!1,message:`Deploy to production?`});if(j.isCancel(t)||!t)throw j.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`)}function K(e,t){if(e===`staging`&&t)throw Error(`Tauri releases can only be published from production deploys.`)}function q(e,t){let n=t.filter(t=>!e[t]);if(n.length>0)throw Error(`Missing required deploy environment variables: ${n.join(`, `)}`);return Object.fromEntries(t.map(t=>[t,e[t]]))}async function J(e){let t=e.DASHBOARD_PASSWORD?.trim();if(t)return t;let n;try{n=A(await w(`.env`,`utf8`))}catch(e){throw X(e)&&e.code===`ENOENT`?Error(`Missing staging dashboard password. Set DASHBOARD_PASSWORD in ".env".`):e}let r=n.DASHBOARD_PASSWORD?.trim();if(!r)throw Error(`Missing staging dashboard password. Set DASHBOARD_PASSWORD in ".env".`);return r}async function Y(e){let t=await C(D(k(),`coreframe-deploy-secrets-`)),n=D(t,`dashboard.json`);return await E(n,JSON.stringify({DASHBOARD_PASSWORD:e}),{mode:384}),{path:n,dispose:()=>T(t,{force:!0,recursive:!0})}}function X(e){return e instanceof Error&&`code`in e}function Z(e,t,n,r,i){return{...e,CLOUDFLARE_ENV:t,DEPLOY_ENV:t,POSTHOG_RELEASE:n,POSTHOG_RELEASE_NAME:e.POSTHOG_RELEASE_NAME??r,POSTHOG_RELEASE_VERSION:e.POSTHOG_RELEASE_VERSION??i}}function Q(e){let{POSTHOG_API_KEY:t,POSTHOG_PROJECT_ID:n,...r}=e;return r}export{N as deployProject,P as parseDeployArgs,e as readProjectConfig,U as resolveDeployUrl,J as resolveStagingDashboardPassword};
package/dist/dev.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{readProjectConfig as e}from"./config.js";import{fs as t,net as n}from"./environment.js";import{startProjectPostgres as r}from"./db/local-postgres.js";import i from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{devHttpsCertEnv as a,devHttpsKeyEnv as o,prepareDevHttps as s}from"./dev-https.js";import{acquireDevLock as c}from"./dev-lock.js";import{isPrimaryGitCheckout as l,startManagedDevSession as u}from"./managed-dev.js";import d from"node:path";import f from"node:process";import{PassThrough as p}from"node:stream";import{supervise as m}from"procband";const h=[`web`,`desktop`,`ios`,`android`];function g(e){let t=e.dev?.platforms??[`web`];for(let e of t)if(!h.includes(e))throw Error(`Invalid development platform "${String(e)}". Expected one of: ${h.join(`, `)}.`);return[...new Set(t)]}const _=d.resolve(`node_modules`,`.bin`),v=f.platform===`win32`?`pnpm.cmd`:`pnpm`,y=f.platform===`win32`?`react-router.cmd`:`react-router`,b=f.platform===`win32`?`turso.cmd`:`turso`;async function x({cwd:h=f.cwd(),env:g=f.env,host:x,https:E=!1,interactive:D=f.stdin.isTTY===!0,managed:O=!1,noManaged:k=!1,platform:A=`web`,webPort:j}={}){let M=g.PATH??g.Path??``,N=A===`web`?T(j??g.COREFRAME_WEB_PORT):void 0,P,F,I,L=!1,R=g;if(O&&k)throw Error(`Cannot use --managed and --no-managed together.`);if(E&&A!==`web`)throw Error(`Local HTTPS is only supported for the web development platform.`);let z=await c({cwd:h,interactive:D});if(!z)return;try{if(A===`web`){let c=await e({cwd:h}),_=E?await s({cwd:h}):void 0;if(R=_?{...g,[a]:_.cert,[o]:_.key}:g,(O||D&&!k)&&await l(h)&&(F=await u({config:c,cwd:h,env:R,onRestart(){I&&(L=!0,I.kill(`SIGTERM`))}}),R=F.env),c.database?.dialect===`sqlite`){let e=await n.getAvailablePort(),r=d.resolve(h,`.coreframe`),i=d.join(r,`turso.db`);await t.mkdir(r,{recursive:!0}),R={...R,TURSO_DATABASE_URL:`http://127.0.0.1:${e}`},F?.setRuntimeEnv(R),P=m({name:`turso`,command:b,args:[`dev`,`--port`,String(e),`--db-file`,i],cwd:h,env:g}),await P.waitFor(`listening`),await m({name:`db`,command:v,args:[`db`,`ensure`],cwd:h,env:R,prefix:!1}).expectSuccess()}if(S(c)){let e=new p,t=m({name:`postgres`,command:f.execPath,args:[`-e`,`process.stdin.pipe(process.stdout)`],cwd:h,stdin:e});try{try{var B=i();let t=B.a(await r({config:c,cwd:h,env:R,postgresOutput:e})).env;F?.setRuntimeEnv(t),R=t,await m({name:`db`,command:v,args:[`db`,`ensure`],cwd:h,env:t,prefix:!1}).expectSuccess(),await V()}catch(e){B.e=e}finally{await B.d()}}finally{e.end(),await t.expectSuccess()}return}F?.setRuntimeEnv(R),await V();return}let c=C(A);await m({name:A,command:v,args:c,cwd:h,env:g}).expectSuccess()}finally{I?.kill(`SIGTERM`),await F?.close(),P&&P.kill(`SIGTERM`),await z.release()}async function V(){let e=async()=>{do L=!1,I=m({name:`web`,command:y,args:w({host:x,port:N}),cwd:h,env:{...R,CLOUDFLARE_ENV:`development`,PATH:_+d.delimiter+M}}),await I.wait(),L||await I.expectSuccess(),I=void 0;while(L)};if(!F){await e();return}await Promise.race([e(),F.failure.catch(e=>{throw L=!1,I?.kill(`SIGTERM`),e})])}}function S(e){return(e.database?.dialect??`postgres`)===`postgres`}function C(e){return[`--dir=tauri`,`tauri`,...e===`desktop`?[]:[e],`dev`]}function w({host:e,port:t}){return[`dev`,...e===void 0?[]:[`--host`,e],...t===void 0?[]:[`--port`,String(t)]]}function T(e){if(e===void 0)return;let t=Number(e);if(!Number.isInteger(t)||t<=0||t>65535)throw Error(`COREFRAME_WEB_PORT must be a valid TCP port.`);return t}export{h as devPlatforms,g as resolveDevPlatforms,x as runDevServer};
2
+ import{readProjectConfig as e}from"./config.js";import{fs as t,net as n}from"./environment.js";import{startProjectPostgres as r}from"./db/local-postgres.js";import i from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{devHttpsCertEnv as a,devHttpsKeyEnv as o,prepareDevHttps as s}from"./dev-https.js";import{acquireDevLock as c}from"./dev-lock.js";import{isPrimaryGitCheckout as l,startManagedDevSession as u}from"./managed-dev.js";import d from"node:path";import f from"node:process";import{createPrefixStream as p,supervise as m}from"procband";const h=[`web`,`desktop`,`ios`,`android`];function g(e){let t=e.dev?.platforms??[`web`];for(let e of t)if(!h.includes(e))throw Error(`Invalid development platform "${String(e)}". Expected one of: ${h.join(`, `)}.`);return[...new Set(t)]}const _=d.resolve(`node_modules`,`.bin`),v=f.platform===`win32`?`pnpm.cmd`:`pnpm`,y=f.platform===`win32`?`react-router.cmd`:`react-router`,b=f.platform===`win32`?`turso.cmd`:`turso`;async function x({cwd:h=f.cwd(),env:g=f.env,host:x,https:E=!1,interactive:D=f.stdin.isTTY===!0,managed:O=!1,noManaged:k=!1,platform:A=`web`,webPort:j}={}){let M=g.PATH??g.Path??``,N=A===`web`?T(j??g.COREFRAME_WEB_PORT):void 0,P,F,I,L=!1,R=g;if(O&&k)throw Error(`Cannot use --managed and --no-managed together.`);if(E&&A!==`web`)throw Error(`Local HTTPS is only supported for the web development platform.`);let z=await c({cwd:h,interactive:D});if(!z)return;try{if(A===`web`){let c=await e({cwd:h}),_=E?await s({cwd:h}):void 0;if(R=_?{...g,[a]:_.cert,[o]:_.key}:g,(O||D&&!k)&&await l(h)&&(F=await u({config:c,cwd:h,env:R,onRestart(){I&&(L=!0,I.kill(`SIGTERM`))}}),R=F.env),c.database?.dialect===`sqlite`){let e=await n.getAvailablePort(),r=d.resolve(h,`.coreframe`),i=d.join(r,`turso.db`);await t.mkdir(r,{recursive:!0}),R={...R,TURSO_DATABASE_URL:`http://127.0.0.1:${e}`},F?.setRuntimeEnv(R),P=m({name:`turso`,command:b,args:[`dev`,`--port`,String(e),`--db-file`,i],cwd:h,env:g}),await P.waitFor(`listening`),await m({name:`db`,command:v,args:[`db`,`ensure`],cwd:h,env:R,prefix:!1}).expectSuccess()}if(S(c)){let e=p({label:`postgres`});e.pipe(f.stdout,{end:!1});try{try{var B=i();let t=B.a(await r({config:c,cwd:h,env:R,postgresOutput:e})).env;F?.setRuntimeEnv(t),R=t,await m({name:`db`,command:v,args:[`db`,`ensure`],cwd:h,env:t,prefix:!1}).expectSuccess(),await V()}catch(e){B.e=e}finally{await B.d()}}finally{e.end()}return}F?.setRuntimeEnv(R),await V();return}let c=C(A);await m({name:A,command:v,args:c,cwd:h,env:g}).expectSuccess()}finally{I?.kill(`SIGTERM`),await F?.close(),P&&P.kill(`SIGTERM`),await z.release()}async function V(){let e=async()=>{do L=!1,I=m({name:`web`,command:y,args:w({host:x,port:N}),cwd:h,env:{...R,CLOUDFLARE_ENV:`development`,PATH:_+d.delimiter+M}}),await I.wait(),L||await I.expectSuccess(),I=void 0;while(L)};if(!F){await e();return}await Promise.race([e(),F.failure.catch(e=>{throw L=!1,I?.kill(`SIGTERM`),e})])}}function S(e){return(e.database?.dialect??`postgres`)===`postgres`}function C(e){return[`--dir=tauri`,`tauri`,...e===`desktop`?[]:[e],`dev`]}function w({host:e,port:t}){return[`dev`,...e===void 0?[]:[`--host`,e],...t===void 0?[]:[`--port`,String(t)]]}function T(e){if(e===void 0)return;let t=Number(e);if(!Number.isInteger(t)||t<=0||t>65535)throw Error(`COREFRAME_WEB_PORT must be a valid TCP port.`);return t}export{h as devPlatforms,g as resolveDevPlatforms,x as runDevServer};
@@ -0,0 +1,33 @@
1
+ import { StagingHostnameConfig } from "./deploy/config.js";
2
+
3
+ //#region staging.d.ts
4
+ declare const stagingHostnameWords: readonly ["amber", "cedar", "cobalt", "coral", "ember", "fern", "indigo", "juniper", "lilac", "maple", "marble", "meadow", "moss", "pebble", "pine", "plum", "quartz", "river", "sable", "sage", "silver", "spruce", "willow"];
5
+ type CloudflareDomain = {
6
+ id: string;
7
+ hostname: string;
8
+ service: string;
9
+ zone_id: string;
10
+ zone_name: string;
11
+ };
12
+ type StagingEnvironment = NodeJS.ProcessEnv & {
13
+ CLOUDFLARE_ACCOUNT_ID?: string;
14
+ CLOUDFLARE_API_TOKEN?: string;
15
+ CLOUDFLARE_ZONE_ID?: string;
16
+ };
17
+ type StagingDependencies = {
18
+ env?: StagingEnvironment;
19
+ fetch?: typeof fetch;
20
+ randomBytes?: (size: number) => Uint8Array;
21
+ sleep?: (milliseconds: number) => Promise<void>;
22
+ };
23
+ type StagingAssignment = {
24
+ previousUrl?: string;
25
+ url: string;
26
+ };
27
+ declare function discoverStagingUrl(dependencies?: StagingDependencies): Promise<string>;
28
+ declare function reassignStagingHostname(dependencies?: StagingDependencies): Promise<StagingAssignment>;
29
+ declare function generateStagingHostname(config: StagingHostnameConfig, random?: (size: number) => Uint8Array): string;
30
+ declare function generateAvailableHostname(config: StagingHostnameConfig, occupied: ReadonlySet<string>, random?: (size: number) => Uint8Array): string;
31
+ declare function discoverManagedDomain(domains: readonly CloudflareDomain[], config: StagingHostnameConfig): CloudflareDomain;
32
+ //#endregion
33
+ export { StagingAssignment, discoverManagedDomain, discoverStagingUrl, generateAvailableHostname, generateStagingHostname, reassignStagingHostname, stagingHostnameWords };
@@ -0,0 +1,3 @@
1
+ import{readProjectConfig as e}from"./config.js";import{runDeploySmokeChecks as t}from"./deploy/checks.js";import{resolveDeployConfig as n}from"./deploy/config.js";import{randomBytes as r}from"node:crypto";const i=`APP_ORIGIN`,a=[`amber`,`cedar`,`cobalt`,`coral`,`ember`,`fern`,`indigo`,`juniper`,`lilac`,`maple`,`marble`,`meadow`,`moss`,`pebble`,`pine`,`plum`,`quartz`,`river`,`sable`,`sage`,`silver`,`spruce`,`willow`];async function o(e={}){let t=await d(e),n=u(await f(t),t.config);if(!n)throw Error("No pinned staging hostname exists. Run `coreframe staging reassign` to assign one.");return T(n)}async function s(r={}){let i=await d(r),a=await f(i),o=u(a,i.config),s=o?new URL(T(o)).origin:await g(i),c=l(i.config,new Set(a.map(e=>e.hostname)),i.random),y=`https://${c}/`,b,x=!1,S=!1;try{x=!0,b=await p(i,c),await v(i,y),await h(i,new URL(y).origin),S=!0,await t(n((await e()).deploy??{},`staging`,{}),{environment:`staging`,releaseName:`staging-hostname-reassignment`,url:y,version:`staging`},i.request,()=>void 0)}catch(e){let t=[];if(S)try{s?await h(i,s):await _(i)}catch(e){t.push(M(e))}let n=b;if(!n&&x)try{n=(await f(i)).find(e=>e.hostname===c&&e.service===i.config.workerService&&e.zone_id===i.zoneId)}catch(e){t.push(M(e))}if(n)try{await m(i,n)}catch(e){t.push(M(e))}throw C(e,t,!!o)}if(o)try{await m(i,o)}catch(e){throw Error(`The new staging URL ${y} is ready, but the old hostname ${o.hostname} could not be detached: ${M(e).message}\nRotation is incomplete and discovery is now ambiguous. In the Cloudflare dashboard, verify ${y} and detach only domain ID ${o.id} (${o.hostname}); do not detach unrelated domains.`)}return{previousUrl:o?T(o):void 0,url:y}}function c(e,t=r){x(e);let n=t(5);if(n.length<5)throw Error(`The secure random source returned too few bytes.`);let i=a[n[0]%a.length],o=Array.from(n.slice(1,5),e=>String.fromCharCode(97+e%26)).join(``);return`${e.managedLabelPrefix}${i}-${o}.${e.parentHostname}`}function l(e,t,n=r){for(let r=0;r<24;r+=1){let r=c(e,n);if(!t.has(r))return r}throw Error(`Could not find an unused staging hostname after 24 attempts.`)}function u(e,t){x(t);let n=S(t),r=e.filter(e=>e.service===t.workerService&&e.hostname.endsWith(`.${t.parentHostname}`)&&n.test(e.hostname));if(r.length>1)throw Error(`Found multiple CLI-managed staging domains for ${t.workerService}: ${r.map(e=>`${e.hostname} (${e.id})`).join(`, `)}. Refusing to guess. In Cloudflare, retain the intended domain and detach only obsolete domains after verifying their IDs, then rerun the command.`);return r[0]}async function d(t){let n=t.env??process.env,i=(await e()).deploy?.stagingHostname;if(!i)throw Error(`Missing deploy.stagingHostname in coreframe.config.ts. Configure workerService, parentHostname, and managedLabelPrefix.`);return x(i),{accountId:w(n,`CLOUDFLARE_ACCOUNT_ID`),apiToken:w(n,`CLOUDFLARE_API_TOKEN`),config:i,env:n,random:t.randomBytes??r,request:t.fetch??fetch,sleep:t.sleep??(e=>new Promise(t=>setTimeout(t,e))),zoneId:w(n,`CLOUDFLARE_ZONE_ID`)}}async function f(e){let t=[],n=1,r=1;do{let i=new URLSearchParams({page:String(n),per_page:`50`,zone_id:e.zoneId}),a=await y(e,`/accounts/${encodeURIComponent(e.accountId)}/workers/domains?${i}`),o=D(a.result,`domain list`).map(b);if(o.some(t=>t.zone_id!==e.zoneId))throw Error(`Cloudflare returned a Custom Domain outside the configured zone.`);t.push(...o),r=E(a.result_info?.total_pages),n+=1}while(n<=r);return t}async function p(e,t){let n=b((await y(e,`/accounts/${encodeURIComponent(e.accountId)}/workers/domains`,{body:JSON.stringify({hostname:t,service:e.config.workerService,zone_id:e.zoneId}),method:`PUT`})).result);if(n.hostname!==t||n.service!==e.config.workerService||n.zone_id!==e.zoneId)throw Error(`Cloudflare returned a Custom Domain that did not match the attach request.`);return n}async function m(e,t){if(t.service!==e.config.workerService||t.zone_id!==e.zoneId||!S(e.config).test(t.hostname))throw Error(`Refusing to detach unmanaged hostname ${t.hostname}.`);await y(e,`/accounts/${encodeURIComponent(e.accountId)}/workers/domains/${encodeURIComponent(t.id)}`,{method:`DELETE`})}async function h(e,t){await y(e,`/accounts/${encodeURIComponent(e.accountId)}/workers/scripts/${encodeURIComponent(e.config.workerService)}/secrets`,{body:JSON.stringify({name:i,text:t,type:`secret_text`}),method:`PUT`})}async function g(e){if(!D((await y(e,`/accounts/${encodeURIComponent(e.accountId)}/workers/scripts/${encodeURIComponent(e.config.workerService)}/secrets`)).result,`secret list`).some(e=>{if(!O(e)||!k(e.name)||!k(e.type))throw Error(`Cloudflare returned an invalid Worker secret response.`);return e.name===i}))return;let t=e.env.DEPLOY_URL;if(!t)throw Error(`The staging Worker already has APP_ORIGIN but no managed hostname identifies its prior value. Refusing to mutate it without rollback information. Rerun with DEPLOY_URL set to the exact current staging origin.`);return N(t,`DEPLOY_URL`)}async function _(e){await y(e,`/accounts/${encodeURIComponent(e.accountId)}/workers/scripts/${encodeURIComponent(e.config.workerService)}/secrets/${i}`,{method:`DELETE`})}async function v(e,t){let n;for(let r=1;r<=24;r+=1)try{await e.request(t,{redirect:`manual`});return}catch(t){n=M(t),r<24&&await e.sleep(5e3)}throw Error(`Staging hostname did not become DNS/TLS/HTTP ready: ${n?.message}`)}async function y(e,t,n={}){let r=await e.request(`https://api.cloudflare.com/client/v4${t}`,{...n,headers:{Authorization:`Bearer ${e.apiToken}`,...n.body?{"Content-Type":`application/json`}:{}}}),i=await r.json().catch(()=>void 0);if(!O(i))throw Error(`Cloudflare returned an invalid response (${r.status}).`);let a=i;if(!r.ok||a.success!==!0){let e=a.errors?.map(e=>e.message).filter(Boolean).join(`; `);throw Error(`Cloudflare API request failed (${r.status})${e?`: ${e}`:`.`}`)}return a}function b(e){if(!O(e)||!k(e.id)||!k(e.hostname)||!k(e.service)||!k(e.zone_id)||!k(e.zone_name))throw Error(`Cloudflare returned an invalid Custom Domain response.`);return{hostname:e.hostname.toLowerCase(),id:e.id,service:e.service,zone_id:e.zone_id,zone_name:e.zone_name}}function x(e){let t=e.parentHostname.toLowerCase();if(e.parentHostname!==t||!A(t)||t.includes(`*`))throw Error(`deploy.stagingHostname.parentHostname must be a lowercase DNS hostname.`);if(!/^[a-z0-9-]*$/.test(e.managedLabelPrefix))throw Error(`deploy.stagingHostname.managedLabelPrefix must contain only lowercase letters, digits, or hyphens.`);let n=Math.max(...a.map(e=>e.length));if(e.managedLabelPrefix.length+n+5>63)throw Error(`deploy.stagingHostname.managedLabelPrefix is too long for a DNS label.`);if(!/^[a-z0-9][a-z0-9-]*$/.test(e.workerService))throw Error(`deploy.stagingHostname.workerService is invalid.`)}function S(e){let t=a.join(`|`);return RegExp(`^${j(e.managedLabelPrefix)}(?:${t})-[a-z]{4}\\.${j(e.parentHostname)}$`)}function C(e,t,n){let r=t.length?`\nRollback also failed: ${t.map(e=>e.message).join(`; `)}`:n?`
2
+ The old staging hostname was retained and the new hostname was rolled back.`:`
3
+ The new hostname was rolled back and APP_ORIGIN was restored to its prior state.`;return Error(`Staging hostname reassignment failed: ${M(e).message}${r}`)}function w(e,t){let n=e[t];if(!n)throw Error(`Missing required environment variable ${t}.`);return n}function T(e){return`https://${e.hostname}/`}function E(e){if(e===void 0)return 1;if(!Number.isInteger(e)||e<1||e>1e4)throw Error(`Cloudflare returned invalid domain-list pagination.`);return e}function D(e,t){if(!Array.isArray(e))throw Error(`Cloudflare returned an invalid ${t} response.`);return e}function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function k(e){return typeof e==`string`&&e.length>0}function A(e){return e.length<=253&&e.split(`.`).every(e=>/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(e))}function j(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function M(e){return e instanceof Error?e:Error(String(e))}function N(e,t){let n;try{n=new URL(e)}catch{throw Error(`${t} must be an exact HTTPS origin.`)}if(n.protocol!==`https:`||n.username||n.password||n.pathname!==`/`||n.search||n.hash)throw Error(`${t} must be an exact HTTPS origin.`);return n.origin}export{u as discoverManagedDomain,o as discoverStagingUrl,l as generateAvailableHostname,c as generateStagingHostname,s as reassignStagingHostname,a as stagingHostnameWords};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreframe/scripts",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/airlock-labs/coreframe.git",
@@ -26,7 +26,7 @@
26
26
  "drizzle-orm": "1.0.0-rc.4",
27
27
  "local-postgres": "0.3.0",
28
28
  "nano-spawn": "^2.1.0",
29
- "procband": "^0.3.6",
29
+ "procband": "^0.3.7",
30
30
  "tinyglobby": "^0.2.17"
31
31
  },
32
32
  "devDependencies": {