@coreframe/scripts 0.1.17 → 0.1.19

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.
package/dist/config.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import { DatabaseDialect } from "./db/client.js";
2
2
  import { DeployConfig } from "./deploy/config.js";
3
3
  import { DevPlatform } from "./dev.js";
4
+ import { WorkerConfig } from "./worker-environment.js";
4
5
 
5
6
  //#region config.d.ts
6
7
  type CoreframeConfig = {
7
8
  database?: DatabaseConfig;
8
9
  deploy?: DeployConfig;
9
10
  dev?: DevConfig;
11
+ worker?: WorkerConfig;
10
12
  };
11
13
  type DevConfig = {
12
14
  platforms?: DevPlatform[];
package/dist/config.js CHANGED
@@ -1 +1 @@
1
- import{access as e}from"node:fs/promises";import{resolve as t}from"node:path";import{pathToFileURL as n}from"node:url";function r(e){return e}async function i({cwd:r=process.cwd()}={}){let i=t(r,`coreframe.config.ts`);try{await e(i)}catch{return{}}return(await import(n(i).href)).default??{}}export{r as defineConfig,i as readProjectConfig};
1
+ import{stat as e}from"node:fs/promises";import{resolve as t}from"node:path";import{pathToFileURL as n}from"node:url";function r(e){return e}async function i({cwd:r=process.cwd()}={}){let i=t(r,`coreframe.config.ts`),a;try{a=(await e(i,{bigint:!0})).mtimeNs}catch{return{}}let o=n(i);return o.searchParams.set(`modified`,a.toString()),(await import(o.href)).default??{}}export{r as defineConfig,i as readProjectConfig};
@@ -62,6 +62,27 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
62
62
  json: boolean;
63
63
  };
64
64
  };
65
+ } | {
66
+ command: "secrets";
67
+ args: {
68
+ command: "status";
69
+ args: {
70
+ environment: "staging" | "production" | undefined;
71
+ json: boolean;
72
+ };
73
+ } | {
74
+ command: "set";
75
+ args: {
76
+ environment: "staging" | "production";
77
+ name: string;
78
+ };
79
+ } | {
80
+ command: "discard";
81
+ args: {
82
+ environment: "staging" | "production";
83
+ name: string;
84
+ };
85
+ };
65
86
  } | {
66
87
  command: "typecheck";
67
88
  args: {};
@@ -206,6 +227,27 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
206
227
  json: boolean;
207
228
  };
208
229
  };
230
+ } | {
231
+ command: "secrets";
232
+ args: {
233
+ command: "status";
234
+ args: {
235
+ environment: "staging" | "production" | undefined;
236
+ json: boolean;
237
+ };
238
+ } | {
239
+ command: "set";
240
+ args: {
241
+ environment: "staging" | "production";
242
+ name: string;
243
+ };
244
+ } | {
245
+ command: "discard";
246
+ args: {
247
+ environment: "staging" | "production";
248
+ name: string;
249
+ };
250
+ };
209
251
  } | {
210
252
  command: "typecheck";
211
253
  args: {};
@@ -317,6 +359,18 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
317
359
  command: "url";
318
360
  value: Promise<void>;
319
361
  };
362
+ } | {
363
+ command: "secrets";
364
+ value: {
365
+ command: "status";
366
+ value: Promise<void>;
367
+ } | {
368
+ command: "set";
369
+ value: Promise<void>;
370
+ } | {
371
+ command: "discard";
372
+ value: Promise<void>;
373
+ };
320
374
  } | {
321
375
  command: "typecheck";
322
376
  value: Promise<void>;
@@ -402,6 +456,18 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
402
456
  command: "url";
403
457
  value: Promise<void>;
404
458
  };
459
+ } | {
460
+ command: "secrets";
461
+ value: {
462
+ command: "status";
463
+ value: Promise<void>;
464
+ } | {
465
+ command: "set";
466
+ value: Promise<void>;
467
+ } | {
468
+ command: "discard";
469
+ value: Promise<void>;
470
+ };
405
471
  } | {
406
472
  command: "typecheck";
407
473
  value: Promise<void>;
package/dist/coreframe.js CHANGED
@@ -1,2 +1,3 @@
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{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
+ 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{discardPendingWorkerSecret as _,inspectWorkerEnvironment as v,queueWorkerSecret as y}from"./worker-environment.js";import{deployProject as b}from"./deploy.js";import{checkDeployMigrations as x}from"./deploy/check-migrations.js";import{devPlatforms as S,runDevServer as C}from"./dev.js";import{run as w}from"./runner.js";import{typecheckProject as T}from"./typecheck.js";import{checkUpgradeSkillCurrent as E}from"./upgrade/check-skill-current.js";import{ensureTemplateRemote as D}from"./upgrade/ensure-template-remote.js";import{fastForwardTemplateFiles as O}from"./upgrade/fast-forward-template-files.js";import*as k from"@clack/prompts";import{array as A,command as j,extendType as M,flag as N,multioption as P,number as F,oneOf as I,option as L,optional as R,positional as z,rest as B,string as V,subcommands as H}from"cmd-ts";const U=[`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.`],W=M(V,{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}}),G=H({name:`coreframe`,cmds:{assets:H({name:`assets`,cmds:{add:j({name:`add`,args:{file:z({type:V,displayName:`file`,description:`Source image to add to the asset library.`}),id:L({long:`id`,type:V,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:L({long:`alt`,type:V,description:`Required alt text for the asset.`}),variantWidths:L({long:`widths`,type:R(W),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:N({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:j({name:`build`,args:{id:L({long:`id`,type:R(V),description:`Build only one logical asset ID.`}),noPull:N({long:`no-pull`,description:`Do not perform targeted Git LFS pulls for missing source objects.`,defaultValue:()=>!1}),json:N({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:j({name:`check`,args:{fix:N({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:N({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:j({name:`publish`,args:{outputDir:L({long:`output-dir`,type:R(V),description:`Root directory for additive local publish output. Defaults to COREFRAME_ASSETS_PUBLISH_DIR or release-upload.`}),json:N({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:j({name:`status`,args:{json:N({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:j({name:`deploy`,args:{environment:z({type:I(p),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:N({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:N({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:N({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:P({long:`tauri-local-platform`,type:A(I(m)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:L({long:`tauri-workflow-name`,type:R(V),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:P({long:`tauri-workflow-platform`,type:A(I(m)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:L({long:`tauri-workflow-ref`,type:R(V),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:N({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})=>b([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:H({name:`staging`,cmds:{reassign:j({name:`reassign`,args:{json:N({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:U}));return}console.log(t.url),console.error(`Staging hostname reassigned. ${U.join(` `)}`)}}),url:j({name:`url`,args:{json:N({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)}})}}),secrets:H({name:`secrets`,cmds:{status:j({name:`status`,args:{environment:z({type:R(I(p)),displayName:`environment`,description:`Inspect staging, production, or both when omitted.`}),json:N({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({environment:e,json:t})=>{let n=await v({environment:e});console.log(t?JSON.stringify(n):K(n))}}),set:j({name:`set`,args:{environment:z({type:I(p),displayName:`environment`,description:`Worker environment to update on its next deploy.`}),name:z({type:V,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{await y({environment:e,name:t,value:await q(t)}),console.log(`Queued ${t} for ${e}. Run coreframe deploy ${e} to apply.`)}}),discard:j({name:`discard`,args:{environment:z({type:I(p),displayName:`environment`,description:`Worker environment with a queued secret update.`}),name:z({type:V,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{let n=await _({environment:e,name:t});console.log(n?`Discarded the queued ${t} update for ${e}.`:`No ${t} update is queued for ${e}.`)}})}}),typecheck:j({name:`typecheck`,args:{},handler:T}),dev:j({name:`dev`,args:{https:N({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:L({long:`host`,type:R(V),description:`Host address for the web development server.`}),managed:N({long:`managed`,description:`Run managed checkout synchronization even without an interactive terminal.`,defaultValue:()=>!1}),noManaged:N({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:z({type:R(I(S)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),port:L({long:`port`,type:R(F),description:`Port for the web development server.`})},handler:({host:e,https:t,managed:n,noManaged:r,platform:i,port:a})=>C({host:e,https:t,managed:n,noManaged:r,platform:i??`web`,webPort:a})}),upgrade:H({name:`upgrade`,cmds:{"ensure-template-remote":j({name:`ensure-template-remote`,args:{remote:L({long:`remote`,type:V,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:N({long:`no-fetch`,description:`Do not fetch the remote after ensuring it exists.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t})=>D({fetch:!e,remote:t})}),"check-skill-current":j({name:`check-skill-current`,args:{target:L({long:`target`,type:V,description:`Target Coreframe template git ref.`}),apply:N({long:`apply`,description:`Replace the local upgrade skill with the target version when it differs.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>{process.exitCode=E({apply:e,target:t})}}),"fast-forward-template-files":j({name:`fast-forward-template-files`,args:{target:L({long:`target`,type:V,description:`Target Coreframe template git ref.`}),base:L({long:`base`,type:R(V),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:N({long:`apply`,description:`Write clean fast-forwards to the worktree.`,defaultValue:()=>!1})},handler:({apply:e,base:t,target:n})=>O({apply:e,base:t,target:n})})}}),db:H({name:`db`,cmds:{ensure:j({name:`ensure`,args:{},handler:u}),generate:j({name:`generate`,args:{args:B({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>a(`generate`,e)}),migrate:j({name:`migrate`,args:{args:B({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>a(`migrate`,e)}),"migrate-held":j({name:`migrate-held`,args:{confirm:L({long:`confirm`,type:R(I([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>d({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":j({name:`migrate-local`,args:{},handler:s}),"migrations-check":j({name:`migrations-check`,args:{},handler:x}),reset:j({name:`reset`,args:{},handler:c}),seed:j({name:`seed`,args:{},handler:()=>o()}),"schema-check":j({name:`schema-check`,args:{seed:N({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>f({seed:e})}),setup:j({name:`setup`,args:{},handler:()=>l()}),studio:j({name:`studio`,args:{args:B({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>a(`studio`,e)})}})}});import.meta.main&&w(G);function K(e){if(e.entries.length===0)return`No Worker environment values are declared in coreframe.config.ts.`;let t=e.entries.map(e=>[e.name,e.kind,e.local===`override`?`override`:`built-in default`,e.deployed.staging.replaceAll(`_`,` `),e.deployed.production.replaceAll(`_`,` `)]),n=[`Name`,`Kind`,`Local`,`Staging`,`Production`],r=n.map((e,n)=>Math.max(e.length,...t.map(e=>e[n].length))),i=e=>e.map((e,t)=>e.padEnd(r[t])).join(` `).trimEnd();return[i(n),i(r.map(e=>`-`.repeat(e))),...t].join(`
3
+ `)}async function q(e){if(!process.stdin.isTTY){let e=``;for await(let t of process.stdin)e+=t.toString();return e.replace(/\r?\n$/,``)}let t=await k.password({message:`Enter the new value for ${e}`});if(k.isCancel(t))throw k.cancel(`Secret update cancelled.`),Error(`Secret update cancelled.`);return t}export{G as coreframeCli};
package/dist/deploy.d.ts CHANGED
@@ -16,6 +16,6 @@ type DeployArgs = {
16
16
  declare function deployProject(argv?: string[], env?: NodeJS.ProcessEnv): Promise<void>;
17
17
  declare function parseDeployArgs(argv: string[]): DeployArgs;
18
18
  declare function resolveDeployUrl(config: ResolvedDeployConfig, environment: DeployEnvironment, discover?: () => Promise<string>): Promise<string>;
19
- declare function resolveStagingDashboardPassword(env: NodeJS.ProcessEnv): Promise<string>;
19
+ declare function resolveDashboardPassword(env: NodeJS.ProcessEnv, environment: DeployEnvironment): Promise<string>;
20
20
  //#endregion
21
- export { deployProject, parseDeployArgs, readProjectConfig, resolveDeployUrl, resolveStagingDashboardPassword };
21
+ export { deployProject, parseDeployArgs, readProjectConfig, resolveDashboardPassword, resolveDeployUrl };
package/dist/deploy.js CHANGED
@@ -1,2 +1,3 @@
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{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};
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 ee}from"./deploy/checks.js";import{deployEnvironments as o,resolveDeployConfig as s}from"./deploy/config.js";import{filterPendingMigrations as c,readAppliedMigrationNames as l}from"./deploy/pending-migrations.js";import{parseTauriReleasePlatform as u,prepareTauriRelease as d,resolveTauriReleasePlan as f,restoreTauriReleaseMetadata as te,uploadTauriReleaseToB2 as ne,validateTauriReleasePlan as p,writeTauriReleaseMetadata as m}from"./deploy/tauri-release.js";import{readPackageJson as h,releaseName as g,resolveReleaseVersion as _,writePackageVersion as v}from"./deploy/version.js";import{discoverStagingUrl as y}from"./staging.js";import{clearAppliedPendingWorkerSecrets as b,readPendingWorkerSecretSnapshot as x}from"./worker-environment.js";import{mkdtemp as S,readFile as C,rm as w,writeFile as T}from"node:fs/promises";import{join as E}from"node:path";import D from"nano-spawn";import*as O from"@clack/prompts";import{tmpdir as k}from"node:os";import{parseEnv as A}from"node:util";const j=`package.json`;async function M(n=process.argv.slice(2),r=process.env){let o=N(n);V(o);let{packageJson:c,source:l}=await h(j),u=await e(),S=s(u.deploy??{},o.environment,r);K(r,[`POSTHOG_API_KEY`,`POSTHOG_PROJECT_ID`]);let C=await H(S,o.environment,()=>y({env:r})),w=f({args:o,config:o.environment===`production`?S.tauriRelease:{},env:r});G(o.environment,w.enabled),await p(w);let E=await x({environment:o.environment}),k=E.values.DASHBOARD_PASSWORD??await q(r,o.environment),A=await B(),M=_(o.environment,c.version,A),P=g(c.name,M),F=X(r,o.environment,P,c.name,M),I=o.environment===`production`,R=!1,U={files:[]};O.intro(`Deploy ${P} to ${o.environment}`);let Y=Object.keys(E.values).sort();Y.length>0&&O.note(Y.join(`
3
+ `),`Pending Worker secret updates`),await W(o);let Q=await L(await t({config:u.database,env:r})),$=await a(Q);if($.length>0)throw Error(`Pending deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${i($)}`);let re=await z(o,Q);try{I&&(await v(j,c,M),U=await m(w,M)),O.log.step(`Running pre-deploy build`),await D(`pnpm`,[`build`],{env:Z(F),stdio:`inherit`});let e=w.enabled?await d({env:Z(F),plan:w,releaseName:P,version:M}):void 0;O.log.step(`Running release build and uploading PostHog source maps`),R=!0,await D(`pnpm`,[`build`],{env:F,stdio:`inherit`}),re&&(O.log.step(`Running deploy-safe database migrations`),await D(`pnpm`,[`db`,`migrate`],{env:F,stdio:`inherit`})),O.log.step(`Deploying Worker and static assets to Cloudflare`);let t={...k?{DASHBOARD_PASSWORD:k}:{},...E.values},n=Object.keys(t).length>0?await J(t):void 0;try{await D(`pnpm`,[`exec`,`wrangler`,`deploy`,`--env`,o.environment,`--strict`,`--keep-vars`,`--tag`,P,`--message`,`Deploy ${P}`,`--var`,`DEPLOY_ENV:${o.environment}`,`--var`,`POSTHOG_RELEASE:${P}`,...n?[`--secrets-file`,n.path]:[]],{env:F,stdio:`inherit`}),await b(E)}finally{await n?.dispose()}o.skipPostDeployChecks||(O.log.step(`Running post-deploy checks`),await ee(S,{environment:o.environment,releaseName:P,url:C,version:M})),e&&(O.log.step(`Uploading Tauri release artifacts to Backblaze B2`),await ne(w,e)),O.outro(`Deployed ${P} to ${o.environment}`)}catch(e){throw!R&&I&&(await T(j,l),await te(U)),e}}function N(e){return{environment:P(e[0]),runMigrations:e.includes(`--run-migrations`),skipMigrations:e.includes(`--skip-migrations`),skipPostDeployChecks:e.includes(`--skip-post-deploy-checks`),tauriLocalPlatforms:F(e,`--tauri-local-platform`).map(e=>u(e)),tauriWorkflowName:I(e,`--tauri-workflow-name`),tauriWorkflowPlatforms:F(e,`--tauri-workflow-platform`).map(e=>u(e)),tauriWorkflowRef:I(e,`--tauri-workflow-ref`),yes:e.includes(`--yes`)}}function P(e){if(e&&o.includes(e))return e;throw Error(`Deploy environment must be one of: ${o.join(`, `)}.`)}function F(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 I(e,t){let n=F(e,t);if(n.length>1)throw Error(`${t} can only be provided once.`);return n[0]}async function L(e){try{return await R(e)}finally{await e.close?.()}}async function R(e){return c(await n(),await l(e))}async function z(e,t){if(t.length===0)return O.log.info(`No pending deploy migrations.`),!1;if(O.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 O.confirm({initialValue:!0,message:`Run these deploy-safe database migrations before Cloudflare deploy?`});if(O.isCancel(n))throw O.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`);return n}async function B(){let{stdout:e}=await D(`git`,[`status`,`--porcelain`],{stdin:`ignore`});if(e.trim().length>0)throw Error(`Deploy requires a clean git tree.`);let{stdout:t}=await D(`git`,[`rev-parse`,`--short=12`,`HEAD`],{stdin:`ignore`});return t.trim()}function V(e){if(e.runMigrations&&e.skipMigrations)throw Error(`Use only one of --run-migrations or --skip-migrations.`)}async function H(e,t,n=y){return e.url?U(e.url,t):t===`staging`&&e.stagingHostname?U(await n(),t):U(void 0,t)}function U(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 W(e){if(e.environment!==`production`||e.yes)return;if(!process.stdout.isTTY)throw Error(`Production deploys require --yes in noninteractive environments.`);let t=await O.confirm({initialValue:!1,message:`Deploy to production?`});if(O.isCancel(t)||!t)throw O.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`)}function G(e,t){if(e===`staging`&&t)throw Error(`Tauri releases can only be published from production deploys.`)}function K(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 q(e,t){let n=e.DASHBOARD_PASSWORD?.trim();if(n)return n;let r;try{r=A(await C(`.env`,`utf8`))}catch(e){throw Y(e)&&e.code===`ENOENT`?Error(`Missing ${t} dashboard password. Set DASHBOARD_PASSWORD in ".env".`):e}let i=r.DASHBOARD_PASSWORD?.trim();if(!i)throw Error(`Missing ${t} dashboard password. Set DASHBOARD_PASSWORD in ".env".`);return i}async function J(e){let t=await S(E(k(),`coreframe-deploy-secrets-`)),n=E(t,`secrets.json`);return await T(n,JSON.stringify(e),{mode:384}),{path:n,dispose:()=>w(t,{force:!0,recursive:!0})}}function Y(e){return e instanceof Error&&`code`in e}function X(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 Z(e){let{POSTHOG_API_KEY:t,POSTHOG_PROJECT_ID:n,...r}=e;return r}export{M as deployProject,N as parseDeployArgs,e as readProjectConfig,q as resolveDashboardPassword,H as resolveDeployUrl};
package/dist/dev-lock.js CHANGED
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{link as t,mkdir as n,readFile as r,unlink as i,writeFile as a}from"node:fs/promises";import o from"node:path";import s from"node:process";import*as c from"@clack/prompts";const l={isRunning:m,stop(e){try{s.kill(e,`SIGTERM`)}catch(e){if(!_(e))throw e}},waitForExit:h};async function u({confirmStop:r=d,cwd:c=s.cwd(),interactive:u=s.stdin.isTTY===!0,pid:m=s.pid,processes:h=l}={}){let _=o.resolve(c,`.coreframe/dev.lock`),v={pid:m,token:e()},y=`${JSON.stringify(v)}\n`,b=`${_}.${v.token}.tmp`;await n(o.dirname(_),{recursive:!0}),await a(b,y,{flag:`wx`,mode:384});try{for(;;){try{return await t(b,_),{async release(){await p(_,y)}}}catch(e){if(!g(e))throw e}let e=await f(_);if(!e||!h.isRunning(e.owner.pid)){await p(_,e?.source);continue}if(!u)throw Error(`Coreframe dev is already running for this project (PID ${e.owner.pid}). Stop it before starting another.`);if(!await r(e.owner.pid))return;h.stop(e.owner.pid),await h.waitForExit(e.owner.pid),await p(_,e.source)}}finally{await i(b).catch(()=>void 0)}}async function d(e){let t=await c.confirm({initialValue:!1,message:`Coreframe dev is already running for this project (PID ${e}). Stop it and start here?`});return c.isCancel(t)||!t?(c.cancel(`Keeping the existing Coreframe dev process.`),!1):!0}async function f(e){try{let t=await r(e,`utf8`),n=JSON.parse(t);return!Number.isSafeInteger(n.pid)||Number(n.pid)<=0||!n.token?void 0:{owner:n,source:t}}catch{return}}async function p(e,t){let n=await r(e,`utf8`).catch(()=>void 0);(t===void 0||n===t)&&await i(e).catch(()=>void 0)}function m(e){try{return s.kill(e,0),!0}catch(e){return!_(e)}}async function h(e){let t=Date.now()+1e4;for(;m(e);){if(Date.now()>=t)throw Error(`Coreframe dev process ${e} did not stop within 10 seconds.`);await new Promise(e=>setTimeout(e,50))}}function g(e){return v(e)&&e.code===`EEXIST`}function _(e){return v(e)&&e.code===`ESRCH`}function v(e){return e instanceof Error}export{u as acquireDevLock};
1
+ import{randomUUID as e}from"node:crypto";import{link as t,mkdir as n,readFile as r,unlink as i,writeFile as a}from"node:fs/promises";import o from"node:path";import*as s from"@clack/prompts";import c from"node:process";const l={isRunning:m,stop(e){try{c.kill(e,`SIGTERM`)}catch(e){if(!_(e))throw e}},waitForExit:h};async function u({confirmStop:r=d,cwd:s=c.cwd(),interactive:u=c.stdin.isTTY===!0,pid:m=c.pid,processes:h=l}={}){let _=o.resolve(s,`.coreframe/dev.lock`),v={pid:m,token:e()},y=`${JSON.stringify(v)}\n`,b=`${_}.${v.token}.tmp`;await n(o.dirname(_),{recursive:!0}),await a(b,y,{flag:`wx`,mode:384});try{for(;;){try{return await t(b,_),{async release(){await p(_,y)}}}catch(e){if(!g(e))throw e}let e=await f(_);if(!e||!h.isRunning(e.owner.pid)){await p(_,e?.source);continue}if(!u)throw Error(`Coreframe dev is already running for this project (PID ${e.owner.pid}). Stop it before starting another.`);if(!await r(e.owner.pid))return;h.stop(e.owner.pid),await h.waitForExit(e.owner.pid),await p(_,e.source)}}finally{await i(b).catch(()=>void 0)}}async function d(e){let t=await s.confirm({initialValue:!1,message:`Coreframe dev is already running for this project (PID ${e}). Stop it and start here?`});return s.isCancel(t)||!t?(s.cancel(`Keeping the existing Coreframe dev process.`),!1):!0}async function f(e){try{let t=await r(e,`utf8`),n=JSON.parse(t);return!Number.isSafeInteger(n.pid)||Number(n.pid)<=0||!n.token?void 0:{owner:n,source:t}}catch{return}}async function p(e,t){let n=await r(e,`utf8`).catch(()=>void 0);(t===void 0||n===t)&&await i(e).catch(()=>void 0)}function m(e){try{return c.kill(e,0),!0}catch(e){return!_(e)}}async function h(e){let t=Date.now()+1e4;for(;m(e);){if(Date.now()>=t)throw Error(`Coreframe dev process ${e} did not stop within 10 seconds.`);await new Promise(e=>setTimeout(e,50))}}function g(e){return v(e)&&e.code===`EEXIST`}function _(e){return v(e)&&e.code===`ESRCH`}function v(e){return e instanceof Error}export{u as acquireDevLock};
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,72 @@
1
+ import { DeployEnvironment } from "./deploy/config.js";
2
+
3
+ //#region worker-environment.d.ts
4
+ declare const workerEnvironmentKinds: readonly ["secret", "variable", "generated"];
5
+ type WorkerEnvironmentKind = (typeof workerEnvironmentKinds)[number];
6
+ type WorkerEnvironmentVariableConfig = {
7
+ description: string;
8
+ kind: WorkerEnvironmentKind;
9
+ requiredIn?: DeployEnvironment[];
10
+ };
11
+ type WorkerConfig = {
12
+ environment?: Record<string, WorkerEnvironmentVariableConfig>;
13
+ };
14
+ type LocalWorkerEnvironmentState = "default" | "override";
15
+ type DeployedWorkerEnvironmentState = "configured" | "declared" | "generated" | "missing" | "not_configured" | "pending_addition" | "pending_replacement" | "pending_update" | "unknown";
16
+ type WorkerEnvironmentEntryStatus = WorkerEnvironmentVariableConfig & {
17
+ name: string;
18
+ local: LocalWorkerEnvironmentState;
19
+ deployed: Record<DeployEnvironment, DeployedWorkerEnvironmentState>;
20
+ };
21
+ type WorkerEnvironmentProviderStatus = {
22
+ state: "available" | "not_checked" | "unavailable";
23
+ message?: string;
24
+ };
25
+ type WorkerEnvironmentStatus = {
26
+ entries: WorkerEnvironmentEntryStatus[];
27
+ providers: Record<DeployEnvironment, WorkerEnvironmentProviderStatus>;
28
+ undeclaredSecrets: Record<DeployEnvironment, string[]>;
29
+ };
30
+ type PendingWorkerSecretSnapshot = {
31
+ environment: DeployEnvironment;
32
+ values: Readonly<Record<string, string>>;
33
+ };
34
+ type InspectWorkerEnvironmentOptions = {
35
+ cwd?: string;
36
+ environment?: DeployEnvironment;
37
+ listSecrets?: (environment: DeployEnvironment, cwd: string) => Promise<string[]>;
38
+ };
39
+ type QueueWorkerSecretOptions = {
40
+ cwd?: string;
41
+ environment: DeployEnvironment;
42
+ name: string;
43
+ value: string;
44
+ ensureIgnored?: (environment: DeployEnvironment, cwd: string) => Promise<void>;
45
+ };
46
+ declare function inspectWorkerEnvironment({
47
+ cwd,
48
+ environment,
49
+ listSecrets
50
+ }?: InspectWorkerEnvironmentOptions): Promise<WorkerEnvironmentStatus>;
51
+ declare function queueWorkerSecret({
52
+ cwd,
53
+ environment,
54
+ name,
55
+ value,
56
+ ensureIgnored
57
+ }: QueueWorkerSecretOptions): Promise<void>;
58
+ declare function discardPendingWorkerSecret({
59
+ cwd,
60
+ environment,
61
+ name
62
+ }: Omit<QueueWorkerSecretOptions, "value" | "ensureIgnored">): Promise<boolean>;
63
+ declare function readPendingWorkerSecretSnapshot({
64
+ cwd,
65
+ environment
66
+ }: {
67
+ cwd?: string;
68
+ environment: DeployEnvironment;
69
+ }): Promise<PendingWorkerSecretSnapshot>;
70
+ declare function clearAppliedPendingWorkerSecrets(snapshot: PendingWorkerSecretSnapshot, cwd?: string): Promise<void>;
71
+ //#endregion
72
+ export { DeployedWorkerEnvironmentState, LocalWorkerEnvironmentState, PendingWorkerSecretSnapshot, WorkerConfig, WorkerEnvironmentEntryStatus, WorkerEnvironmentKind, WorkerEnvironmentProviderStatus, WorkerEnvironmentStatus, WorkerEnvironmentVariableConfig, clearAppliedPendingWorkerSecrets, discardPendingWorkerSecret, inspectWorkerEnvironment, queueWorkerSecret, readPendingWorkerSecretSnapshot, workerEnvironmentKinds };
@@ -0,0 +1 @@
1
+ import{readProjectConfig as e}from"./config.js";import{deployEnvironments as t}from"./deploy/config.js";import{randomUUID as n}from"node:crypto";import{chmod as r,mkdir as i,readFile as a,rename as o,rm as s,stat as c,writeFile as l}from"node:fs/promises";import{resolve as u}from"node:path";import d from"nano-spawn";import{parseEnv as f}from"node:util";const p=[`secret`,`variable`,`generated`],m=/^[A-Z][A-Z0-9_]*$/,h=`.coreframe/deploy-secrets`;async function g({cwd:n=process.cwd(),environment:r,listSecrets:i=A}={}){let a=x((await e({cwd:n})).worker?.environment),o=await C(n),s=r?[r]:t,c=new Map,l=new Map,u=Object.fromEntries(t.map(e=>[e,{state:`not_checked`}]));await Promise.all(s.map(async e=>{if(l.set(e,new Set(Object.keys((await y({cwd:n,environment:e})).values))),a.some(e=>e.kind===`secret`))try{c.set(e,new Set(await i(e,n))),u[e]={state:`available`}}catch{u[e]={state:`unavailable`,message:`Could not read ${e} Worker secrets. Check Wrangler authentication and whether the Worker exists.`}}}));let d=new Set(a.filter(e=>e.kind===`secret`).map(e=>e.name)),f=Object.fromEntries(t.map(e=>[e,[...c.get(e)??[]].filter(e=>!d.has(e)).sort()]));return{entries:a.map(e=>({...e,local:o.has(e.name)?`override`:`default`,deployed:Object.fromEntries(t.map(t=>[t,w(e,t,u[t],c.get(t),l.get(t))]))})),providers:u,undeclaredSecrets:f}}async function _({cwd:e=process.cwd(),environment:t,name:n,value:r,ensureIgnored:i=D}){if(await S(e,n),!r.trim())throw Error(`Worker secret values cannot be empty.`);await i(t,e),await E(e,t,{...(await y({cwd:e,environment:t})).values,[n]:r})}async function v({cwd:e=process.cwd(),environment:t,name:n}){await S(e,n);let r=await y({cwd:e,environment:t});if(!(n in r.values))return!1;let i={...r.values};return delete i[n],await E(e,t,i),!0}async function y({cwd:t=process.cwd(),environment:n}){let r=await T(t,n),i=x((await e({cwd:t})).worker?.environment),a=new Set(i.filter(e=>e.kind===`secret`).map(e=>e.name)),o=Object.keys(r).filter(e=>!a.has(e));if(o.length>0)throw Error(`Pending ${n} Worker secrets are not declared as secrets in coreframe.config.ts: ${o.join(`, `)}.`);return{environment:n,values:r}}async function b(e,t=process.cwd()){let n=await T(t,e.environment),r=Object.fromEntries(Object.entries(n).filter(([t,n])=>e.values[t]!==n));await E(t,e.environment,r)}function x(e){return Object.entries(e??{}).map(([e,t])=>{if(!m.test(e))throw Error(`Invalid Worker environment name ${JSON.stringify(e)} in coreframe.config.ts. Use uppercase letters, numbers, and underscores.`);return{...t,name:e,requiredIn:[...new Set(t.requiredIn??[])]}}).sort((e,t)=>e.name.localeCompare(t.name))}async function S(t,n){let r=x((await e({cwd:t})).worker?.environment).find(e=>e.name===n);if(!r)throw Error(`${n} is not declared in coreframe.config.ts.`);if(r.kind!==`secret`)throw Error(`${n} is declared as ${r.kind}, not a Worker secret.`)}async function C(e){try{let t=f(await a(u(e,`.env`),`utf8`));return new Set(Object.entries(t).filter(([,e])=>!!e?.trim()).map(([e])=>e))}catch(e){if(j(e)&&e.code===`ENOENT`)return new Set;throw e}}function w(e,t,n,r,i){return e.kind===`generated`?`generated`:e.kind===`variable`?`declared`:i?.has(e.name)?n.state!==`available`||!r?`pending_update`:r.has(e.name)?`pending_replacement`:`pending_addition`:n.state!==`available`||!r?`unknown`:r.has(e.name)?`configured`:e.requiredIn.includes(t)?`missing`:`not_configured`}async function T(e,t){let n=O(e,t);try{let e=await c(n);if(process.platform!==`win32`&&e.mode&63)throw Error(`Pending Worker secret file ${n} must use mode 0600.`);let t=JSON.parse(await a(n,`utf8`));if(!k(t))throw Error(`Pending Worker secret file ${n} must contain a JSON object of strings.`);return t}catch(e){if(j(e)&&e.code===`ENOENT`)return{};throw e}}async function E(e,t,a){let c=O(e,t);if(Object.keys(a).length===0){await s(c,{force:!0});return}let d=u(e,h);await i(d,{mode:448,recursive:!0}),process.platform!==`win32`&&await r(d,448);let f=`${c}.${process.pid}.${n()}.tmp`;try{await l(f,`${JSON.stringify(a,null,2)}\n`,{flag:`wx`,mode:384}),await o(f,c)}finally{await s(f,{force:!0})}process.platform!==`win32`&&await r(c,384)}async function D(e,t){let n=`${h}/${e}.json`,r=`${h}/.coreframe-ignore-check`;try{await d(`git`,[`check-ignore`,`--quiet`,r],{cwd:t,stdin:`ignore`,stdout:`ignore`,stderr:`ignore`})}catch{throw Error(`Refusing to store plaintext Worker secrets because ${n} is not git-ignored. Add .coreframe/ to .gitignore.`)}}function O(e,t){return u(e,h,`${t}.json`)}function k(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&Object.entries(e).every(([e,t])=>m.test(e)&&typeof t==`string`&&t.length>0)}async function A(e,t){let{stdout:n}=await d(`pnpm`,[`exec`,`wrangler`,`secret`,`list`,`--env`,e,`--format`,`json`],{cwd:t,env:{CI:`true`},stdin:`ignore`,timeout:5e3}),r=JSON.parse(n);if(!Array.isArray(r))throw Error(`Wrangler returned an invalid secret list.`);return r.flatMap(e=>e&&typeof e==`object`&&`name`in e&&typeof e.name==`string`?[e.name]:[])}function j(e){return e instanceof Error&&`code`in e}export{b as clearAppliedPendingWorkerSecrets,v as discardPendingWorkerSecret,g as inspectWorkerEnvironment,_ as queueWorkerSecret,y as readPendingWorkerSecretSnapshot,p as workerEnvironmentKinds};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreframe/scripts",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
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": {