@coreframe/scripts 0.1.30 → 0.1.31

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.
@@ -94,6 +94,7 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
94
94
  managed: boolean;
95
95
  noManaged: boolean;
96
96
  platform: "ios" | "android" | "web" | "desktop" | undefined;
97
+ device: string | undefined;
97
98
  port: number | undefined;
98
99
  };
99
100
  } | {
@@ -265,6 +266,7 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
265
266
  managed: boolean;
266
267
  noManaged: boolean;
267
268
  platform: "ios" | "android" | "web" | "desktop" | undefined;
269
+ device: string | undefined;
268
270
  port: number | undefined;
269
271
  };
270
272
  } | {
package/dist/coreframe.js CHANGED
@@ -1,3 +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{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{syncCoreframePackages as k}from"./upgrade/sync-coreframe-packages.js";import*as A from"@clack/prompts";import{array as j,command as M,extendType as N,flag as P,multioption as F,number as I,oneOf as L,option as R,optional as z,positional as B,rest as V,string as H,subcommands as U}from"cmd-ts";const W=[`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.`],G=N(H,{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}}),K=U({name:`coreframe`,cmds:{assets:U({name:`assets`,cmds:{add:M({name:`add`,args:{file:B({type:H,displayName:`file`,description:`Source image to add to the asset library.`}),id:R({long:`id`,type:H,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:R({long:`alt`,type:H,description:`Required alt text for the asset.`}),variantWidths:R({long:`widths`,type:z(G),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:P({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:M({name:`build`,args:{id:R({long:`id`,type:z(H),description:`Build only one logical asset ID.`}),noPull:P({long:`no-pull`,description:`Do not perform targeted Git LFS pulls for missing source objects.`,defaultValue:()=>!1}),json:P({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:M({name:`check`,args:{fix:P({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:P({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:M({name:`publish`,args:{outputDir:R({long:`output-dir`,type:z(H),description:`Root directory for additive local publish output. Defaults to COREFRAME_ASSETS_PUBLISH_DIR or release-upload.`}),json:P({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:M({name:`status`,args:{json:P({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:M({name:`deploy`,args:{environment:B({type:L(p),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:P({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:P({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:P({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:F({long:`tauri-local-platform`,type:j(L(m)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:R({long:`tauri-workflow-name`,type:z(H),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:F({long:`tauri-workflow-platform`,type:j(L(m)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:R({long:`tauri-workflow-ref`,type:z(H),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:P({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:U({name:`staging`,cmds:{reassign:M({name:`reassign`,args:{json:P({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:W}));return}console.log(t.url),console.error(`Staging hostname reassigned. ${W.join(` `)}`)}}),url:M({name:`url`,args:{json:P({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:U({name:`secrets`,cmds:{status:M({name:`status`,args:{environment:B({type:z(L(p)),displayName:`environment`,description:`Inspect staging, production, or both when omitted.`}),json:P({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):q(n))}}),set:M({name:`set`,args:{environment:B({type:L(p),displayName:`environment`,description:`Worker environment to update on its next deploy.`}),name:B({type:H,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{await y({environment:e,name:t,value:await J(t)}),console.log(`Queued ${t} for ${e}. Run coreframe deploy ${e} to apply.`)}}),discard:M({name:`discard`,args:{environment:B({type:L(p),displayName:`environment`,description:`Worker environment with a queued secret update.`}),name:B({type:H,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:M({name:`typecheck`,args:{},handler:T}),dev:M({name:`dev`,args:{https:P({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:R({long:`host`,type:z(H),description:`Host address for the web development server.`}),managed:P({long:`managed`,description:`Run managed checkout synchronization even without an interactive terminal.`,defaultValue:()=>!1}),noManaged:P({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:B({type:z(L(S)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),port:R({long:`port`,type:z(I),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:U({name:`upgrade`,cmds:{"ensure-template-remote":M({name:`ensure-template-remote`,args:{remote:R({long:`remote`,type:H,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:P({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":M({name:`check-skill-current`,args:{target:R({long:`target`,type:H,description:`Target Coreframe template git ref.`}),apply:P({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":M({name:`fast-forward-template-files`,args:{target:R({long:`target`,type:H,description:`Target Coreframe template git ref.`}),base:R({long:`base`,type:z(H),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:P({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})}),"sync-coreframe-packages":M({name:`sync-coreframe-packages`,args:{target:R({long:`target`,type:H,description:`Target Coreframe template git ref.`}),apply:P({long:`apply`,description:`Update existing published Coreframe dependency specifications.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>k({apply:e,target:t})})}}),db:U({name:`db`,cmds:{ensure:M({name:`ensure`,args:{},handler:u}),generate:M({name:`generate`,args:{args:V({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>a(`generate`,e)}),migrate:M({name:`migrate`,args:{args:V({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>a(`migrate`,e)}),"migrate-held":M({name:`migrate-held`,args:{confirm:R({long:`confirm`,type:z(L([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>d({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":M({name:`migrate-local`,args:{},handler:s}),"migrations-check":M({name:`migrations-check`,args:{},handler:x}),reset:M({name:`reset`,args:{},handler:c}),seed:M({name:`seed`,args:{},handler:()=>o()}),"schema-check":M({name:`schema-check`,args:{seed:P({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>f({seed:e})}),setup:M({name:`setup`,args:{},handler:()=>l()}),studio:M({name:`studio`,args:{args:V({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>a(`studio`,e)})}})}});import.meta.main&&w(K);function q(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(`
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{syncCoreframePackages as k}from"./upgrade/sync-coreframe-packages.js";import*as A from"@clack/prompts";import{array as j,command as M,extendType as N,flag as P,multioption as F,number as I,oneOf as L,option as R,optional as z,positional as B,rest as V,string as H,subcommands as U}from"cmd-ts";const W=[`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.`],G=N(H,{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}}),K=U({name:`coreframe`,cmds:{assets:U({name:`assets`,cmds:{add:M({name:`add`,args:{file:B({type:H,displayName:`file`,description:`Source image to add to the asset library.`}),id:R({long:`id`,type:H,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:R({long:`alt`,type:H,description:`Required alt text for the asset.`}),variantWidths:R({long:`widths`,type:z(G),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:P({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:M({name:`build`,args:{id:R({long:`id`,type:z(H),description:`Build only one logical asset ID.`}),noPull:P({long:`no-pull`,description:`Do not perform targeted Git LFS pulls for missing source objects.`,defaultValue:()=>!1}),json:P({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:M({name:`check`,args:{fix:P({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:P({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:M({name:`publish`,args:{outputDir:R({long:`output-dir`,type:z(H),description:`Root directory for additive local publish output. Defaults to COREFRAME_ASSETS_PUBLISH_DIR or release-upload.`}),json:P({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:M({name:`status`,args:{json:P({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:M({name:`deploy`,args:{environment:B({type:L(p),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:P({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:P({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:P({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:F({long:`tauri-local-platform`,type:j(L(m)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:R({long:`tauri-workflow-name`,type:z(H),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:F({long:`tauri-workflow-platform`,type:j(L(m)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:R({long:`tauri-workflow-ref`,type:z(H),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:P({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:U({name:`staging`,cmds:{reassign:M({name:`reassign`,args:{json:P({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:W}));return}console.log(t.url),console.error(`Staging hostname reassigned. ${W.join(` `)}`)}}),url:M({name:`url`,args:{json:P({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:U({name:`secrets`,cmds:{status:M({name:`status`,args:{environment:B({type:z(L(p)),displayName:`environment`,description:`Inspect staging, production, or both when omitted.`}),json:P({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):q(n))}}),set:M({name:`set`,args:{environment:B({type:L(p),displayName:`environment`,description:`Worker environment to update on its next deploy.`}),name:B({type:H,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{await y({environment:e,name:t,value:await J(t)}),console.log(`Queued ${t} for ${e}. Run coreframe deploy ${e} to apply.`)}}),discard:M({name:`discard`,args:{environment:B({type:L(p),displayName:`environment`,description:`Worker environment with a queued secret update.`}),name:B({type:H,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:M({name:`typecheck`,args:{},handler:T}),dev:M({name:`dev`,args:{https:P({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:R({long:`host`,type:z(H),description:`Host address for the web development server.`}),managed:P({long:`managed`,description:`Run managed checkout synchronization even without an interactive terminal.`,defaultValue:()=>!1}),noManaged:P({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:B({type:z(L(S)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),device:B({type:z(H),displayName:`device`,description:`Device name or ID for iOS or Android development.`}),port:R({long:`port`,type:z(I),description:`Port for the web development server.`})},handler:({device:e,host:t,https:n,managed:r,noManaged:i,platform:a,port:o})=>C({device:e,host:t,https:n,managed:r,noManaged:i,platform:a??`web`,webPort:o})}),upgrade:U({name:`upgrade`,cmds:{"ensure-template-remote":M({name:`ensure-template-remote`,args:{remote:R({long:`remote`,type:H,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:P({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":M({name:`check-skill-current`,args:{target:R({long:`target`,type:H,description:`Target Coreframe template git ref.`}),apply:P({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":M({name:`fast-forward-template-files`,args:{target:R({long:`target`,type:H,description:`Target Coreframe template git ref.`}),base:R({long:`base`,type:z(H),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:P({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})}),"sync-coreframe-packages":M({name:`sync-coreframe-packages`,args:{target:R({long:`target`,type:H,description:`Target Coreframe template git ref.`}),apply:P({long:`apply`,description:`Update existing published Coreframe dependency specifications.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>k({apply:e,target:t})})}}),db:U({name:`db`,cmds:{ensure:M({name:`ensure`,args:{},handler:u}),generate:M({name:`generate`,args:{args:V({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>a(`generate`,e)}),migrate:M({name:`migrate`,args:{args:V({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>a(`migrate`,e)}),"migrate-held":M({name:`migrate-held`,args:{confirm:R({long:`confirm`,type:z(L([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>d({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":M({name:`migrate-local`,args:{},handler:s}),"migrations-check":M({name:`migrations-check`,args:{},handler:x}),reset:M({name:`reset`,args:{},handler:c}),seed:M({name:`seed`,args:{},handler:()=>o()}),"schema-check":M({name:`schema-check`,args:{seed:P({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>f({seed:e})}),setup:M({name:`setup`,args:{},handler:()=>l()}),studio:M({name:`studio`,args:{args:V({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>a(`studio`,e)})}})}});import.meta.main&&w(K);function q(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
3
  `)}async function J(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 A.password({message:`Enter the new value for ${e}`});if(A.isCancel(t))throw A.cancel(`Secret update cancelled.`),Error(`Secret update cancelled.`);return t}export{K as coreframeCli};
@@ -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(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};
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:[`src-tauri/target/**/*.sig`,`src-tauri/gen/android/**/*.sig`],buildArgs:[`tauri`,`android`,`build`,`--target`,`aarch64-linux-android`],targetTriple:`aarch64-linux-android`,updatePlatform:`android-aarch64`},ios:{artifactGlobs:[`src-tauri/target/**/*.sig`,`src-tauri/gen/apple/**/*.sig`],buildArgs:[`tauri`,`ios`,`build`,`--target`,`aarch64-apple-ios`],targetTriple:`aarch64-apple-ios`,updatePlatform:`ios-aarch64`},linux:{artifactGlobs:[`src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`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:[`src-tauri/target/aarch64-apple-darwin/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`aarch64-apple-darwin`,`--`,`--locked`],targetTriple:`aarch64-apple-darwin`,updatePlatform:`darwin-aarch64`},windows:{artifactGlobs:[`src-tauri/target/x86_64-pc-windows-msvc/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`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,`src-tauri/tauri.conf.json`);if(!await Z(r))throw Error(`Tauri release platforms were requested, but 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=`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,`src-tauri/tauri.conf.json`),i=d(n,`src-tauri/Cargo.toml`),o=d(n,`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===`src-tauri`||n.startsWith(`src-tauri/`)||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};
@@ -0,0 +1,34 @@
1
+ //#region dev-device.d.ts
2
+ type MobileDevPlatform = "android" | "ios";
3
+ type MobileDevDevice = {
4
+ id?: string;
5
+ label: string;
6
+ name: string;
7
+ physical: boolean;
8
+ };
9
+ type SelectMobileDevDeviceOptions = {
10
+ env?: NodeJS.ProcessEnv;
11
+ interactive?: boolean;
12
+ platform: MobileDevPlatform;
13
+ target?: string;
14
+ };
15
+ type ResolveMobileDevHostOptions = {
16
+ device: MobileDevDevice;
17
+ host?: string | true;
18
+ interactive?: boolean;
19
+ platform: MobileDevPlatform;
20
+ };
21
+ declare function selectMobileDevDevice({
22
+ env,
23
+ interactive,
24
+ platform,
25
+ target
26
+ }: SelectMobileDevDeviceOptions): Promise<MobileDevDevice>;
27
+ declare function resolveMobileDevHost({
28
+ device,
29
+ host,
30
+ interactive,
31
+ platform
32
+ }: ResolveMobileDevHostOptions): Promise<string | undefined>;
33
+ //#endregion
34
+ export { MobileDevDevice, MobileDevPlatform, resolveMobileDevHost, selectMobileDevDevice };
@@ -0,0 +1 @@
1
+ import{fs as e,os as t}from"./environment.js";import n from"node:path";import r from"nano-spawn";import*as i from"@clack/prompts";import a from"node:process";async function o({env:e=a.env,interactive:t=a.stdin.isTTY===!0,platform:n,target:r}){let i=await l(n,e).catch(()=>[]),o=r?C(i,r):void 0;if(o)return o;if(!r&&i.length>0)return c(i,n,t);let s=await u(n,e),d=r?C(s,r):void 0;if(d)return d;if(!r&&s.length>0)return c(s,n,t);let f=n===`ios`?`iOS`:`Android`;throw Error(r?`Could not find a ${f} device matching "${r}".`:`No connected ${f} devices or available ${f} ${n===`ios`?`simulators`:`emulators`} were detected.`)}async function s({device:e,host:t,interactive:n=a.stdin.isTTY===!0,platform:r}){if(!(e.physical||r===`android`&&a.platform===`win32`)&&t===void 0)return;if(typeof t==`string`&&t!==`0.0.0.0`&&t!==`::`)return t;let o=T();if(o.length===0)throw Error(`No external IP address was detected. Pass one explicitly with --host.`);if(o.length===1)return o[0];if(!n)throw Error(`Multiple external IP addresses were detected (${o.join(`, `)}). Pass one explicitly with --host.`);let s=await i.select({message:`Which IP address should the mobile device use to reach the development server?`,options:o.map(e=>({label:e,value:e}))});if(i.isCancel(s))throw Error(`Mobile development IP selection was cancelled.`);return s}async function c(e,t,n){if(e.length===1)return e[0];if(!n)throw Error(`Multiple ${t===`ios`?`iOS`:`Android`} devices were detected. Pass a device name explicitly.`);let r=await i.select({message:`Which ${t===`ios`?`iOS`:`Android`} device should Coreframe use?`,options:e.map(e=>({label:e.label,value:e}))});if(i.isCancel(r))throw Error(`Mobile development device selection was cancelled.`);return r}async function l(e,t){return e===`android`?d(t):p(t)}async function u(e,t){return e===`android`?f(t):m(t)}async function d(e){let t=await g(e,`platform-tools`,a.platform===`win32`?`adb.exe`:`adb`),{stdout:n}=await r(t,[`devices`],{env:e}),i=n.split(/\r?\n/).map(e=>e.match(/^(\S+)\s+device(?:\s|$)/)?.[1]).filter(e=>e!==void 0);return(await Promise.all(i.map(async n=>{let r=await v(t,e,n,`ro.product.model`),i=n.startsWith(`emulator-`),a=i?await b(t,e,n,r):await x(t,e,n,r);return{id:n,label:a===r?a:`${a} (${r})`,name:a,physical:!i}}))).sort(E)}async function f(e){let{stdout:t}=await r(await g(e,`emulator`,a.platform===`win32`?`emulator.exe`:`emulator`),[`-list-avds`],{env:e});return t.split(/\r?\n/).map(e=>e.trim()).filter(e=>e.length>0&&!/^(INFO|WARNING|ERROR)\s+\|/.test(e)).map(e=>({id:e,label:e,name:e,physical:!1}))}async function p(i){let a=await e.mkdtemp(n.join(t.tmpdir(),`coreframe-ios-devices-`)),o=n.join(a,`devices.json`);try{return await e.writeFile(o,``),await r(`xcrun`,[`devicectl`,`list`,`devices`,`--json-output`,o],{env:i}),(JSON.parse(await e.readFile(o,`utf8`)).result?.devices??[]).filter(h).map(e=>({id:e.hardwareProperties.udid,label:`${e.deviceProperties.name} (${e.hardwareProperties.productType})`,name:e.deviceProperties.name,physical:!0})).sort(E)}finally{await e.rm(a,{force:!0,recursive:!0})}}async function m(e){let{stdout:t}=await r(`xcrun`,[`simctl`,`list`,`--json`,`devices`,`available`],{env:e}),n=JSON.parse(t);return Object.entries(n.devices??{}).filter(([e])=>e.includes(`iOS-`)).flatMap(([,e])=>e).flatMap(e=>e.name?[{id:e.udid,label:e.name,name:e.name,physical:!1}]:[]).sort(E)}function h(e){return e.connectionProperties?.tunnelState!==`unavailable`&&typeof e.hardwareProperties?.platform==`string`&&(e.hardwareProperties.platform.includes(`iOS`)||e.hardwareProperties.platform.includes(`xrOS`))&&typeof e.deviceProperties?.name==`string`&&typeof e.hardwareProperties.productType==`string`&&typeof e.hardwareProperties.udid==`string`}async function g(t,r,i){let a=t.ANDROID_HOME??t.ANDROID_SDK_ROOT,o=a?[a]:_(t);for(let t of o){let a=n.join(t,r,i);try{return await e.access(a),a}catch{}}return i}function _(e){return a.platform===`darwin`?[n.join(t.homedir(),`Library`,`Android`,`sdk`)]:a.platform===`win32`&&e.LOCALAPPDATA?[n.join(e.LOCALAPPDATA,`Android`,`Sdk`)]:[n.join(t.homedir(),`Android`,`Sdk`)]}async function v(e,t,n,r){return(await y(e,t,[`-s`,n,`shell`,`getprop`,r])).trim()}async function y(e,t,n){return(await r(e,n,{env:t,timeout:3e3})).stdout}async function b(e,t,n,r){return y(e,t,[`-s`,n,`emu`,`avd`,`name`]).then(e=>e.split(/\r?\n/)[0]?.trim()||r).catch(()=>r)}async function x(e,t,n,r){return y(e,t,[`-s`,n,`shell`,`dumpsys`,`bluetooth_manager`]).then(e=>S(e)||r).catch(()=>r)}function S(e){return e.match(/\bname: (.+)/)?.[1]?.trim()}function C(e,t){let n=e.map(e=>({device:e,score:Math.max(w(t,e.name),w(t,e.id??``))})).sort((e,t)=>t.score-e.score)[0];return n&&n.score>0?n.device:void 0}function w(e,t){let n=e.toLowerCase(),r=t.toLowerCase();if(r===n)return 1e4;let i=r.indexOf(n);if(i>=0)return 5e3-i;let a=0,o=0;for(let e of n){let t=r.indexOf(e,o);if(t<0)return 0;a+=t===o?2:1,o=t+1}return a}function T(){return[...new Set(Object.values(t.networkInterfaces()).flatMap(e=>e??[]).filter(e=>!e.internal&&e.family===`IPv4`).map(e=>e.address))].sort()}function E(e,t){return e.label.localeCompare(t.label)}export{s as resolveMobileDevHost,o as selectMobileDevDevice};
@@ -4,13 +4,15 @@ type DevProcessOperations = {
4
4
  stop(pid: number): void;
5
5
  waitForExit(pid: number): Promise<void>;
6
6
  };
7
- type AcquireDevLockOptions = {
7
+ type CheckDevLockOptions = {
8
8
  confirmStop?(pid: number): Promise<boolean>;
9
9
  cwd?: string;
10
10
  interactive?: boolean;
11
- pid?: number;
12
11
  processes?: DevProcessOperations;
13
12
  };
13
+ type AcquireDevLockOptions = CheckDevLockOptions & {
14
+ pid?: number;
15
+ };
14
16
  type DevLock = {
15
17
  release(): Promise<void>;
16
18
  takeoverApproved: boolean;
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*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`,x=!1;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)},takeoverApproved:x}}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;x=!0,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};
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:h,stop(e){try{c.kill(e,`SIGTERM`)}catch(e){if(!v(e))throw e}},waitForExit:g};async function u({confirmStop:r=f,cwd:s=c.cwd(),interactive:u=c.stdin.isTTY===!0,pid:p=c.pid,processes:h=l}={}){let g=o.resolve(s,`.coreframe/dev.lock`),v={pid:p,token:e()},y=`${JSON.stringify(v)}\n`,b=`${g}.${v.token}.tmp`,x=!1;await n(o.dirname(g),{recursive:!0}),await a(b,y,{flag:`wx`,mode:384});try{for(;;){try{return await t(b,g),{async release(){await m(g,y)},takeoverApproved:x}}catch(e){if(!_(e))throw e}let e=await d({confirmStop:r,file:g,interactive:u,processes:h});if(e===void 0)return;x||=e}}finally{await i(b).catch(()=>void 0)}}async function d({confirmStop:e,file:t,interactive:n,processes:r}){let i=await p(t);if(!i||!r.isRunning(i.owner.pid))return await m(t,i?.source),!1;if(!n)throw Error(`Coreframe dev is already running for this project (PID ${i.owner.pid}). Stop it before starting another.`);if(await e(i.owner.pid))return r.stop(i.owner.pid),await r.waitForExit(i.owner.pid),await m(t,i.source),!0}async function f(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 p(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 m(e,t){let n=await r(e,`utf8`).catch(()=>void 0);(t===void 0||n===t)&&await i(e).catch(()=>void 0)}function h(e){try{return c.kill(e,0),!0}catch(e){return!v(e)}}async function g(e){let t=Date.now()+1e4;for(;h(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 _(e){return y(e)&&e.code===`EEXIST`}function v(e){return y(e)&&e.code===`ESRCH`}function y(e){return e instanceof Error}export{u as acquireDevLock};
package/dist/dev.d.ts CHANGED
@@ -3,6 +3,7 @@ import { CoreframeConfig } from "./config.js";
3
3
  //#region dev.d.ts
4
4
  type DevServerOptions = {
5
5
  cwd?: string;
6
+ device?: string;
6
7
  env?: NodeJS.ProcessEnv;
7
8
  host?: string | true;
8
9
  https?: boolean;
@@ -18,6 +19,7 @@ type DevPlatform = "android" | "desktop" | "ios" | "web";
18
19
  declare function resolveDevPlatforms(config: CoreframeConfig): DevPlatform[];
19
20
  declare function runDevServer({
20
21
  cwd,
22
+ device,
21
23
  env,
22
24
  host,
23
25
  https,
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{ProjectPostgresAlreadyRunningError as r,startProjectPostgres as i,stopProjectPostgres as a}from"./db/local-postgres.js";import o from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{devHttpsCertEnv as s,devHttpsKeyEnv as c,prepareDevHttps as l}from"./dev-https.js";import{acquireDevLock as u}from"./dev-lock.js";import{managedDevEnv as d}from"./managed-dev-protocol.js";import{isManagedDevCheckout as f,isPrimaryCheckout as p}from"./managed-dev.js";import m from"node:path";import*as h from"@clack/prompts";import g from"node:process";import*as _ from"node:readline";import{createPrefixStream as v,supervise as y}from"procband";const b=[`web`,`desktop`,`ios`,`android`];function x(e){let t=e.dev?.platforms??[`web`];for(let e of t)if(!b.includes(e))throw Error(`Invalid development platform "${String(e)}". Expected one of: ${b.join(`, `)}.`);return[...new Set(t)]}const S=m.resolve(`node_modules`,`.bin`),C=g.platform===`win32`?`pnpm.cmd`:`pnpm`,w=g.platform===`win32`?`portless.cmd`:`portless`,T=g.platform===`win32`?`react-router.cmd`:`react-router`,E=g.platform===`win32`?`turso.cmd`:`turso`;async function D({cwd:r=g.cwd(),env:i=g.env,host:a,https:b=!1,input:x=g.stdin,interactive:D=g.stdin.isTTY===!0,managed:P=!1,noManaged:F=!1,platform:I=`web`,webPort:L}={}){let R=i.PATH??i.Path??``,z=I===`web`?N(L??i.COREFRAME_WEB_PORT):void 0,B,V,H=i;if(P&&F)throw Error(`Cannot use --managed and --no-managed together.`);if(b&&I!==`web`)throw Error(`Local HTTPS is only supported for the web development platform.`);let U=I===`web`?await u({cwd:r,interactive:D}):void 0;if(!(I===`web`&&!U))try{if(I===`web`){let u=await e({cwd:r}),A=i.PORTLESS!==`0`&&i.TAURI_ENV_PLATFORM===void 0&&await p(r),N=b&&!A?await l({cwd:r}):void 0;H=N?{...i,[s]:N.cert,[c]:N.key}:i;let I=async()=>{let e=!1,t=D?_.createInterface({input:x}):void 0;t?.on(`line`,t=>{t.trim().toLowerCase()!==`r`||!V||e||(e=!0,V.kill(`SIGTERM`)?h.log.info(`Restarting the Vite development server.`):e=!1)}),t&&h.log.info(`Press r + Enter to restart the Vite development server.`);try{for(;;){e=!1,V=y({name:`web`,command:A?w:T,args:A?M({host:a,port:z}):j({host:a,port:z}),cwd:r,env:{...H,CLOUDFLARE_ENV:`development`,PATH:S+m.delimiter+R}}),await V.wait();let t=V;if(V=void 0,!e){await t.expectSuccess();return}}}finally{t?.close()}};if((P||D&&!F)&&await f(r)&&(H={...H,[d]:`1`}),u.database?.dialect===`sqlite`){let e=await n.getAvailablePort(),a=m.resolve(r,`.coreframe`),o=m.join(a,`turso.db`);await t.mkdir(a,{recursive:!0}),H={...H,TURSO_DATABASE_URL:`http://127.0.0.1:${e}`,TURSO_AUTH_TOKEN:`dev`},B=y({name:`turso`,command:E,args:[`dev`,`--port`,String(e),`--db-file`,o],cwd:r,env:i}),await B.waitFor(`listening`),await y({name:`db`,command:C,args:[`db`,`ensure`],cwd:r,env:H,prefix:!1}).expectSuccess()}if(k(u)){let e=v({label:`postgres`});e.pipe(g.stdout,{end:!1});try{try{var W=o();let t=await O({config:u,cwd:r,env:H,interactive:D,postgresOutput:e,takeoverApproved:U?.takeoverApproved??!1});if(!t)return;W.a(t);let n=t.env;H=n,await y({name:`db`,command:C,args:[`db`,`ensure`],cwd:r,env:n,prefix:!1}).expectSuccess(),await I()}catch(e){W.e=e}finally{await W.d()}}finally{e.end()}return}await I();return}let u=A(I);await y({name:I,command:C,args:u,cwd:r,env:i}).expectSuccess()}finally{V?.kill(`SIGTERM`),B&&B.kill(`SIGTERM`),await U?.release()}}async function O({interactive:e,takeoverApproved:t,...n}){try{return await i(n)}catch(o){if(!(o instanceof r)||!e)throw o;if(!t){let e=await h.confirm({initialValue:!1,message:`PostgreSQL is already running for this project (PID ${o.pid}). Stop it and start here?`});if(h.isCancel(e)||!e){h.cancel(`Keeping the existing PostgreSQL process.`);return}}return await a(o),i(n)}}function k(e){return(e.database?.dialect??`postgres`)===`postgres`}function A(e){return[`--dir=tauri`,`tauri`,...e===`desktop`?[]:[e],`dev`]}function j({host:e,port:t}){return[`dev`,...e===void 0?[]:[`--host`,e===!0?`0.0.0.0`:e],...t===void 0?[]:[`--port`,String(t)]]}function M({host:e,port:t}){return[`run`,...t===void 0?[]:[`--app-port`,String(t)],`react-router`,...j({host:e,port:void 0})]}function N(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{b as devPlatforms,x as resolveDevPlatforms,D as runDevServer};
2
+ import{readProjectConfig as e}from"./config.js";import{fs as t,net as n}from"./environment.js";import{ProjectPostgresAlreadyRunningError as r,startProjectPostgres as i,stopProjectPostgres as a}from"./db/local-postgres.js";import o from"./_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{resolveMobileDevHost as s,selectMobileDevDevice as c}from"./dev-device.js";import{devHttpsCertEnv as ee,devHttpsKeyEnv as l,prepareDevHttps as te}from"./dev-https.js";import{acquireDevLock as u}from"./dev-lock.js";import{managedDevEnv as d}from"./managed-dev-protocol.js";import{isManagedDevCheckout as f,isPrimaryCheckout as p}from"./managed-dev.js";import m from"node:path";import h from"nano-spawn";import*as g from"@clack/prompts";import _ from"node:process";import*as ne from"node:readline";import{createPrefixStream as v,supervise as y}from"procband";const b=[`web`,`desktop`,`ios`,`android`];function x(e){let t=e.dev?.platforms??[`web`];for(let e of t)if(!b.includes(e))throw Error(`Invalid development platform "${String(e)}". Expected one of: ${b.join(`, `)}.`);return[...new Set(t)]}const S=m.resolve(`node_modules`,`.bin`),C=_.platform===`win32`?`pnpm.cmd`:`pnpm`,w=_.platform===`win32`?`portless.cmd`:`portless`,T=_.platform===`win32`?`react-router.cmd`:`react-router`,E=_.platform===`win32`?`turso.cmd`:`turso`;async function D({cwd:r=_.cwd(),device:i,env:a=_.env,host:b,https:x=!1,input:D=_.stdin,interactive:P=_.stdin.isTTY===!0,managed:F=!1,noManaged:I=!1,platform:L=`web`,webPort:R}={}){let z=a.PATH??a.Path??``,B=L===`web`?N(R??a.COREFRAME_WEB_PORT):void 0,V,H,U,W=!1,G=a;if(F&&I)throw Error(`Cannot use --managed and --no-managed together.`);if(x&&L!==`web`)throw Error(`Local HTTPS is only supported for the web development platform.`);if(i!==void 0&&L!==`ios`&&L!==`android`)throw Error(`A device name or ID is only supported for iOS or Android development.`);if(L===`web`&&a.TAURI_ENV_PLATFORM!==void 0&&a.COREFRAME_TAURI_DEV!==`1`){let e=a.TAURI_ENV_PLATFORM===`ios`||a.TAURI_ENV_PLATFORM===`android`?a.TAURI_ENV_PLATFORM:`desktop`;throw Error(`Direct Tauri development is not supported. Run \`pnpm dev ${e}\` instead.`)}let K=await u({cwd:r,interactive:P});if(K)try{let u=L===`ios`||L===`android`?L:void 0,N=u?await c({env:a,interactive:P,platform:u,target:i}):void 0,R=u&&N?await s({device:N,host:b,interactive:P,platform:u}):void 0,J=R?!0:b,Y=await e({cwd:r}),X=L===`web`&&a.PORTLESS!==`0`&&a.TAURI_ENV_PLATFORM===void 0&&await p(r),Z=x&&!X?await te({cwd:r}):void 0;G={...a,...L===`web`?void 0:{COREFRAME_TAURI_DEV:`1`,TAURI_ENV_PLATFORM:j(L),...R?{TAURI_DEV_HOST:R}:void 0},...Z?{[ee]:Z.cert,[l]:Z.key}:void 0};let Q=async()=>{let e=!1,t=P?ne.createInterface({input:D}):void 0;t?.on(`line`,t=>{t.trim().toLowerCase()!==`r`||!U||e||(e=!0,U.kill(`SIGTERM`)?g.log.info(`Restarting the Vite development server.`):e=!1)}),t&&g.log.info(`Press r + Enter to restart the Vite development server.`);try{for(;;){e=!1,U=y({name:`web`,command:X?w:T,args:X?re({host:J,port:B}):M({host:J,port:B}),cwd:r,env:{...G,CLOUDFLARE_ENV:`development`,PATH:S+m.delimiter+z}}),await U.wait();let t=U;if(U=void 0,!e){W||await t.expectSuccess();return}}}finally{t?.close()}},$=async()=>{if(L===`web`){await Q();return}let e=Q().then(()=>{if(!W)throw Error(`The Vite development server stopped while Tauri was still running.`)});V=h(C,A(L,N?.name,R),{cwd:r,env:a,stdio:`inherit`});try{await Promise.race([e,V])}finally{W=!0,U?.kill(`SIGTERM`),(await V.nodeChildProcess.catch(()=>void 0))?.kill(`SIGTERM`),await Promise.allSettled([e,V]),V=void 0}};if((F||P&&!I)&&await f(r)&&(G={...G,[d]:`1`}),Y.database?.dialect===`sqlite`){let e=await n.getAvailablePort(),i=m.resolve(r,`.coreframe`),o=m.join(i,`turso.db`);await t.mkdir(i,{recursive:!0}),G={...G,TURSO_DATABASE_URL:`http://127.0.0.1:${e}`,TURSO_AUTH_TOKEN:`dev`},H=y({name:`turso`,command:E,args:[`dev`,`--port`,String(e),`--db-file`,o],cwd:r,env:a}),await H.waitFor(`listening`),await y({name:`db`,command:C,args:[`db`,`ensure`],cwd:r,env:G,prefix:!1}).expectSuccess()}if(k(Y)){let e=v({label:`postgres`});e.pipe(_.stdout,{end:!1});try{try{var q=o();let t=await O({config:Y,cwd:r,env:G,interactive:P,postgresOutput:e,takeoverApproved:K.takeoverApproved});if(!t)return;q.a(t),G=t.env,await y({name:`db`,command:C,args:[`db`,`ensure`],cwd:r,env:G,prefix:!1}).expectSuccess(),await $()}catch(e){q.e=e}finally{await q.d()}}finally{e.end()}return}await $()}finally{W=!0,U?.kill(`SIGTERM`),(await V?.nodeChildProcess.catch(()=>void 0))?.kill(`SIGTERM`),H&&H.kill(`SIGTERM`),await K.release()}}async function O({interactive:e,takeoverApproved:t,...n}){try{return await i(n)}catch(o){if(!(o instanceof r)||!e)throw o;if(!t){let e=await g.confirm({initialValue:!1,message:`PostgreSQL is already running for this project (PID ${o.pid}). Stop it and start here?`});if(g.isCancel(e)||!e){g.cancel(`Keeping the existing PostgreSQL process.`);return}}return await a(o),i(n)}}function k(e){return(e.database?.dialect??`postgres`)===`postgres`}function A(e,t,n){return[`exec`,`tauri`,...e===`desktop`?[]:[e],`dev`,`--config`,JSON.stringify({build:{beforeDevCommand:``}}),...n===void 0?[]:[`--host=${n}`],...t===void 0?[]:[t]]}function j(e){return e===`desktop`?_.platform===`win32`?`windows`:_.platform:e}function M({host:e,port:t}){return[`dev`,...e===void 0?[]:[`--host`,e===!0?`0.0.0.0`:e],...t===void 0?[]:[`--port`,String(t)]]}function re({host:e,port:t}){return[`run`,...t===void 0?[]:[`--app-port`,String(t)],`react-router`,...M({host:e,port:void 0})]}function N(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{b as devPlatforms,x as resolveDevPlatforms,D as runDevServer};
@@ -1,6 +1,6 @@
1
- import { access, chmod, mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
1
+ import { access, chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { readFileSync } from "node:fs";
3
- import { tmpdir } from "node:os";
3
+ import { homedir, networkInterfaces, tmpdir } from "node:os";
4
4
 
5
5
  //#region environment.d.ts
6
6
  declare const fs: {
@@ -11,12 +11,15 @@ declare const fs: {
11
11
  readFile: typeof readFile;
12
12
  readFileSync: typeof readFileSync;
13
13
  rm: typeof rm;
14
+ writeFile: typeof writeFile;
14
15
  };
15
16
  declare const net: {
16
17
  getAvailablePort: typeof getAvailablePort;
17
18
  };
18
19
  declare const os: {
20
+ homedir: typeof homedir;
19
21
  kill: (pid: number, signal?: string | number) => true;
22
+ networkInterfaces: typeof networkInterfaces;
20
23
  tmpdir: typeof tmpdir;
21
24
  };
22
25
  declare function getAvailablePort(): Promise<number>;
@@ -1 +1 @@
1
- import{access as e,chmod as t,mkdir as n,mkdtemp as r,readFile as i,rm as a}from"node:fs/promises";import o from"node:process";import{readFileSync as s}from"node:fs";import{createServer as c}from"node:net";import{tmpdir as l}from"node:os";const u={access:e,chmod:t,mkdir:n,mkdtemp:r,readFile:i,readFileSync:s,rm:a},d={getAvailablePort:p},f={kill:o.kill,tmpdir:l};async function p(){let e=c();try{await new Promise((t,n)=>{e.once(`error`,n),e.listen(0,`127.0.0.1`,t)});let t=e.address();if(!t||typeof t==`string`)throw Error(`Could not allocate an available TCP port.`);return t.port}finally{e.listening&&await new Promise((t,n)=>{e.close(e=>e?n(e):t())})}}export{u as fs,d as net,f as os};
1
+ import{access as e,chmod as t,mkdir as n,mkdtemp as r,readFile as i,rm as a,writeFile as o}from"node:fs/promises";import s from"node:process";import{readFileSync as c}from"node:fs";import{createServer as l}from"node:net";import{homedir as u,networkInterfaces as d,tmpdir as f}from"node:os";const p={access:e,chmod:t,mkdir:n,mkdtemp:r,readFile:i,readFileSync:c,rm:a,writeFile:o},m={getAvailablePort:g},h={homedir:u,kill:s.kill,networkInterfaces:d,tmpdir:f};async function g(){let e=l();try{await new Promise((t,n)=>{e.once(`error`,n),e.listen(0,`127.0.0.1`,t)});let t=e.address();if(!t||typeof t==`string`)throw Error(`Could not allocate an available TCP port.`);return t.port}finally{e.listening&&await new Promise((t,n)=>{e.close(e=>e?n(e):t())})}}export{p as fs,m as net,h as os};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{coreframeGitRef as e,coreframeTemplateTree as t}from"./template-ref.js";import{dirname as n,join as r,resolve as i,sep as a}from"node:path";import{chmodSync as o,existsSync as s,lstatSync as c,mkdirSync as l,readFileSync as u,readlinkSync as d,rmSync as f,symlinkSync as p,writeFileSync as m}from"node:fs";import{spawnSync as h}from"node:child_process";const g=[{matches:e=>e===`drizzle`||e.startsWith(`drizzle/`),reason:`project-owned migration history`},{matches:e=>e===`drizzle-hold`||e.startsWith(`drizzle-hold/`),reason:`project-owned migration history`},{matches:e=>e===`src/web/routes.ts`||e.startsWith(`src/web/app/routes/`),reason:`downstream-owned demonstration routes`},{matches:e=>e===`pnpm-lock.yaml`||e===`tauri/src-tauri/Cargo.lock`,reason:`downstream-owned dependency lockfile`}],_=new Set([`100644`,`100755`,`120000`]);function v(e,t={}){return h(`git`,e,t)}function y(e){let t=v(e,{encoding:`utf8`});if(t.status!==0){let n=t.stderr.toString().trim()||t.stdout.toString().trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout.toString()}function b(e){let t=v(e);if(t.status!==0){let n=t.stderr.toString(`utf8`).trim()||t.stdout.toString(`utf8`).trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout}function x(){return y([`rev-parse`,`--show-toplevel`]).trim()}function S(e){let t=r(e,`package.json`);if(!s(t))throw Error(`Missing --base <git-ref>, and package.json does not exist.`);let n=JSON.parse(u(t,`utf8`)).engines?.coreframe;if(typeof n!=`string`||n.length===0)throw Error(`Missing --base <git-ref>, and package.json engines.coreframe is not set.`);return n}function C(e){let t=[],n=0;for(let r=0;r<e.length;r+=1)e[r]===0&&(t.push(e.subarray(n,r).toString(`utf8`)),n=r+1);return t.filter(e=>e.length>0)}function w(e,n){let r=C(b([`diff`,`--name-status`,`-z`,`--no-renames`,t(e),t(n),`--`])),i=[];for(let e=0;e<r.length;e+=2){let t=r[e],n=r[e+1];if(!t||!n)throw Error(`Could not parse git diff --name-status output.`);i.push({path:n,status:t})}return i}function T(e,n){let r=b([`ls-tree`,`-z`,t(e),`--`,n]);if(r.length===0)return null;let i=C(r)[0],a=i.indexOf(` `);if(a===-1)throw Error(`Could not parse git ls-tree output for ${e}:${n}.`);let[o,s,c]=i.slice(0,a).split(` `);if(!o||!s||!c)throw Error(`Could not parse git ls-tree metadata for ${e}:${n}.`);return{mode:o,object:c,path:n,type:s}}function E(e){return b([`cat-file`,`blob`,e.object])}function D(e,t){let n=i(e,t),r=e.endsWith(a)?e:`${e}${a}`;if(n!==e&&!n.startsWith(r))throw Error(`Refusing to access path outside repository: ${t}`);return n}function O(e){try{return c(e),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw e}}function k(e){return(e.mode&73)!=0}function A(e){return e===null||e.type===`blob`&&_.has(e.mode)}function j(e){return g.find(t=>t.matches(e))?.reason}function M(e,t,n){let r=D(e,t);if(n===null)return{matches:!O(r)};if(!A(n))return{matches:!1,reason:`unsupported template entry mode ${n.mode} ${n.type}`};let i;try{i=c(r)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return{matches:!1,reason:`path is missing from worktree`};throw e}let a=E(n);return n.mode===`120000`?i.isSymbolicLink()?{matches:Buffer.from(d(r),`utf8`).equals(a)}:{matches:!1,reason:`worktree path is not a symlink`}:i.isFile()?(n.mode===`100755`?k(i):!k(i))?{matches:u(r).equals(a)}:{matches:!1,reason:`worktree executable bit differs from template`}:{matches:!1,reason:`worktree path is not a regular file`}}function N(e){O(e)&&f(e,{force:!0,recursive:!1})}function P(e,t,r){let i=D(e,t);if(r===null){N(i);return}if(l(n(i),{recursive:!0}),r.mode===`120000`){N(i),p(E(r).toString(`utf8`),i);return}let a=O(i)?c(i):null;a&&!a.isFile()&&N(i),m(i,E(r)),o(i,r.mode===`100755`?493:420)}function F(e,t){return e===null&&t!==null?`add`:e!==null&&t===null?`delete`:`update`}function I(e,t){if(t.length!==0){console.log(`${e}:`);for(let e of t){let t=e.reason?` (${e.reason})`:``;console.log(` ${e.action}: ${e.path}${t}`)}console.log(``)}}function L(t){let n=x(),r=e(t.base??S(n)),i=e(t.target),a=w(r,i),o=[],s=[],c=[],l=[];for(let e of a){let t=j(e.path);if(t){s.push({action:`ignored`,path:e.path,reason:t});continue}let a=T(r,e.path),u=T(i,e.path),d=F(a,u);if(!A(a)||!A(u)){c.push({action:d,path:e.path,reason:`unsupported template entry type`});continue}if(M(n,e.path,u).matches){l.push({action:`unchanged`,path:e.path});continue}let f=M(n,e.path,a);if(!f.matches){c.push({action:d,path:e.path,reason:f.reason??`downstream differs from base template`});continue}o.push({action:d,path:e.path,targetEntry:u})}if(t.apply)for(let e of o)P(n,e.path,e.targetEntry??null);console.log(`Coreframe template fast-forward: ${r} -> ${i}`),console.log(``),I(t.apply?`Applied clean template updates`:`Would apply clean template updates`,o),I(`Ignored downstream-owned paths`,s),I(`Skipped for agent review`,c),l.length>0&&console.log(`${l.length} changed template path(s) already matched the target state.`),a.length===0&&console.log(`No template file changes found in the requested range.`),!t.apply&&o.length>0&&(console.log(``),console.log(`Run with --apply to write the ${o.length} clean update(s).`))}export{L as fastForwardTemplateFiles};
2
+ import{coreframeGitRef as e,coreframeTemplateTree as t}from"./template-ref.js";import{dirname as n,join as r,resolve as i,sep as a}from"node:path";import{chmodSync as o,existsSync as s,lstatSync as c,mkdirSync as l,readFileSync as u,readlinkSync as d,rmSync as f,symlinkSync as p,writeFileSync as m}from"node:fs";import{spawnSync as h}from"node:child_process";const g=[{matches:e=>e===`drizzle`||e.startsWith(`drizzle/`),reason:`project-owned migration history`},{matches:e=>e===`drizzle-hold`||e.startsWith(`drizzle-hold/`),reason:`project-owned migration history`},{matches:e=>e===`src/web/routes.ts`||e.startsWith(`src/web/app/routes/`),reason:`downstream-owned demonstration routes`},{matches:e=>e===`pnpm-lock.yaml`||e===`src-tauri/Cargo.lock`,reason:`downstream-owned dependency lockfile`}],_=new Set([`100644`,`100755`,`120000`]);function v(e,t={}){return h(`git`,e,t)}function y(e){let t=v(e,{encoding:`utf8`});if(t.status!==0){let n=t.stderr.toString().trim()||t.stdout.toString().trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout.toString()}function b(e){let t=v(e);if(t.status!==0){let n=t.stderr.toString(`utf8`).trim()||t.stdout.toString(`utf8`).trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout}function x(){return y([`rev-parse`,`--show-toplevel`]).trim()}function S(e){let t=r(e,`package.json`);if(!s(t))throw Error(`Missing --base <git-ref>, and package.json does not exist.`);let n=JSON.parse(u(t,`utf8`)).engines?.coreframe;if(typeof n!=`string`||n.length===0)throw Error(`Missing --base <git-ref>, and package.json engines.coreframe is not set.`);return n}function C(e){let t=[],n=0;for(let r=0;r<e.length;r+=1)e[r]===0&&(t.push(e.subarray(n,r).toString(`utf8`)),n=r+1);return t.filter(e=>e.length>0)}function w(e,n){let r=C(b([`diff`,`--name-status`,`-z`,`--no-renames`,t(e),t(n),`--`])),i=[];for(let e=0;e<r.length;e+=2){let t=r[e],n=r[e+1];if(!t||!n)throw Error(`Could not parse git diff --name-status output.`);i.push({path:n,status:t})}return i}function T(e,n){let r=b([`ls-tree`,`-z`,t(e),`--`,n]);if(r.length===0)return null;let i=C(r)[0],a=i.indexOf(` `);if(a===-1)throw Error(`Could not parse git ls-tree output for ${e}:${n}.`);let[o,s,c]=i.slice(0,a).split(` `);if(!o||!s||!c)throw Error(`Could not parse git ls-tree metadata for ${e}:${n}.`);return{mode:o,object:c,path:n,type:s}}function E(e){return b([`cat-file`,`blob`,e.object])}function D(e,t){let n=i(e,t),r=e.endsWith(a)?e:`${e}${a}`;if(n!==e&&!n.startsWith(r))throw Error(`Refusing to access path outside repository: ${t}`);return n}function O(e){try{return c(e),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw e}}function k(e){return(e.mode&73)!=0}function A(e){return e===null||e.type===`blob`&&_.has(e.mode)}function j(e){return g.find(t=>t.matches(e))?.reason}function M(e,t,n){let r=D(e,t);if(n===null)return{matches:!O(r)};if(!A(n))return{matches:!1,reason:`unsupported template entry mode ${n.mode} ${n.type}`};let i;try{i=c(r)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return{matches:!1,reason:`path is missing from worktree`};throw e}let a=E(n);return n.mode===`120000`?i.isSymbolicLink()?{matches:Buffer.from(d(r),`utf8`).equals(a)}:{matches:!1,reason:`worktree path is not a symlink`}:i.isFile()?(n.mode===`100755`?k(i):!k(i))?{matches:u(r).equals(a)}:{matches:!1,reason:`worktree executable bit differs from template`}:{matches:!1,reason:`worktree path is not a regular file`}}function N(e){O(e)&&f(e,{force:!0,recursive:!1})}function P(e,t,r){let i=D(e,t);if(r===null){N(i);return}if(l(n(i),{recursive:!0}),r.mode===`120000`){N(i),p(E(r).toString(`utf8`),i);return}let a=O(i)?c(i):null;a&&!a.isFile()&&N(i),m(i,E(r)),o(i,r.mode===`100755`?493:420)}function F(e,t){return e===null&&t!==null?`add`:e!==null&&t===null?`delete`:`update`}function I(e,t){if(t.length!==0){console.log(`${e}:`);for(let e of t){let t=e.reason?` (${e.reason})`:``;console.log(` ${e.action}: ${e.path}${t}`)}console.log(``)}}function L(t){let n=x(),r=e(t.base??S(n)),i=e(t.target),a=w(r,i),o=[],s=[],c=[],l=[];for(let e of a){let t=j(e.path);if(t){s.push({action:`ignored`,path:e.path,reason:t});continue}let a=T(r,e.path),u=T(i,e.path),d=F(a,u);if(!A(a)||!A(u)){c.push({action:d,path:e.path,reason:`unsupported template entry type`});continue}if(M(n,e.path,u).matches){l.push({action:`unchanged`,path:e.path});continue}let f=M(n,e.path,a);if(!f.matches){c.push({action:d,path:e.path,reason:f.reason??`downstream differs from base template`});continue}o.push({action:d,path:e.path,targetEntry:u})}if(t.apply)for(let e of o)P(n,e.path,e.targetEntry??null);console.log(`Coreframe template fast-forward: ${r} -> ${i}`),console.log(``),I(t.apply?`Applied clean template updates`:`Would apply clean template updates`,o),I(`Ignored downstream-owned paths`,s),I(`Skipped for agent review`,c),l.length>0&&console.log(`${l.length} changed template path(s) already matched the target state.`),a.length===0&&console.log(`No template file changes found in the requested range.`),!t.apply&&o.length>0&&(console.log(``),console.log(`Run with --apply to write the ${o.length} clean update(s).`))}export{L as fastForwardTemplateFiles};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreframe/scripts",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/airlock-labs/coreframe.git",