@coreframe/scripts 0.1.13 → 0.1.14

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/assets.d.ts CHANGED
@@ -17,7 +17,7 @@ type AssetVariant = {
17
17
  hash: string;
18
18
  };
19
19
  type AssetProblem = {
20
- code: "GENERATED_VARIANT_MISSING" | "INVALID_ALT" | "INVALID_ASSET_ID" | "INVALID_MANIFEST" | "INVALID_REFERENCE" | "INVALID_SOURCE_PATH" | "INVALID_VARIANT_PATH" | "INVALID_VARIANT_WIDTHS" | "LFS_NOT_TRACKED" | "MANIFEST_MISSING" | "REFERENCE_MISSING" | "SOURCE_MISSING" | "SOURCE_POINTER_MISSING_OBJECT";
20
+ code: "GENERATED_VARIANT_MISSING" | "INVALID_ALT" | "INVALID_ASSET_ID" | "INVALID_MANIFEST" | "INVALID_REFERENCE" | "INVALID_SOURCE_PATH" | "INVALID_VARIANT_PATH" | "INVALID_VARIANT_WIDTHS" | "LFS_BLOB_NOT_POINTER" | "LFS_NOT_TRACKED" | "MANIFEST_MISSING" | "REFERENCE_MISSING" | "SOURCE_MISSING" | "SOURCE_POINTER_MISSING_OBJECT";
21
21
  message: string;
22
22
  assetId?: string;
23
23
  file?: string;
package/dist/assets.js CHANGED
@@ -1,3 +1,3 @@
1
- import{createHash as e}from"node:crypto";import{copyFile as t,mkdir as n,readFile as r,stat as i,writeFile as a}from"node:fs/promises";import{dirname as o,extname as s,join as c,relative as l,resolve as u,sep as d}from"node:path";import f from"nano-spawn";const p=`assets-src`,m=`assets`,h=[320,640,960,1280,1920,2560],g=h.at(-1),_=c(`assets`,`manifest.json`),v=/^[a-z0-9]+(?:[/-][a-z0-9]+)*$/,y=/@asset\s+([a-z0-9][a-z0-9/-]*)/g,b=/^version https:\/\/git-lfs.github.com\/spec\/v1\n/m;async function x({alt:e,cwd:r=process.cwd(),file:i,id:a,variantWidths:s}){D(a);let c=j(a,i),l=u(r,c),d=u(r,i),f=await B(d);P(s,f.width),await n(o(l),{recursive:!0}),await t(d,l),await H(r,c);let p=await O(r);return p.manifest[a]={source:c,alt:e,width:f.width,height:f.height,...s?{variantWidths:s}:{},variants:[]},await k(r,p.manifest),S({cwd:r,id:a,pull:!1})}async function S({cwd:e=process.cwd(),id:t,pull:n=!0,updateManifest:r=!0}={}){let{manifest:i,problems:a}=await O(e),o=[],s=[];for(let[r,c]of Object.entries(i)){if(t&&r!==t)continue;let l=F(r,c);if(l){a.push(l);continue}let d=u(e,c.source);if(!await J(d)){a.push(Y(r,c.source));continue}if(await G(d)&&(n&&(await U(e,c.source),o.push(c.source)),await G(d))){a.push({code:`SOURCE_POINTER_MISSING_OBJECT`,message:`Source asset "${c.source}" is still a Git LFS pointer after a targeted pull.`,assetId:r,file:c.source,fix:`Run: git lfs pull --include="${c.source}"`});continue}let f=await M(e,r,c);i[r]={...c,variants:f},s.push({assetId:r,variants:f})}return t&&!i[t]&&a.push({code:`REFERENCE_MISSING`,message:`Asset "${t}" is not present in ${_}.`,assetId:t,fix:`Run: coreframe assets add <file> --id ${t} --alt "..."`}),r&&a.length===0&&s.length>0&&await k(e,i),{ok:a.length===0,assets:s,fetched:o,problems:a}}async function C({cwd:e=process.cwd(),fix:t=!1}={}){if(t){let t=await S({cwd:e}),n=await C({cwd:e,fix:!1});return{...n,ok:n.ok&&t.ok,problems:[...n.problems,...t.problems]}}let n=await O(e),r=[...n.problems],i=[],a=await K(e);for(let[a,o]of Object.entries(n.manifest)){v.test(a)||r.push({code:`INVALID_ASSET_ID`,message:`Asset ID "${a}" must use lowercase path segments.`,assetId:a}),o.alt.trim()||r.push({code:`INVALID_ALT`,message:`Asset "${a}" is missing alt text.`,assetId:a,file:o.source,fix:`Run: coreframe assets add ${o.source} --id ${a} --alt "..."`}),o.source.startsWith(`${p}/`)||r.push({code:`INVALID_SOURCE_PATH`,message:`Asset "${a}" source must be under ${p}/.`,assetId:a,file:o.source});let n=F(a,o);n&&r.push(n);let s=await J(u(e,o.source)),c=s?await W(e,o.source):!1;s?c||r.push({code:`LFS_NOT_TRACKED`,message:`Source asset "${o.source}" is not tracked by Git LFS.`,assetId:a,file:o.source,fix:`Run: git lfs track "${o.source}" && git add .gitattributes ${o.source}`}):r.push(Y(a,o.source));let l=await Promise.all(o.variants.map(async n=>{let i=z(n.path);return i||r.push({code:`INVALID_VARIANT_PATH`,message:`Asset "${a}" variant path must be under ${m}/.`,assetId:a,file:n.path,fix:t?void 0:`Run: coreframe assets build --id ${a}`}),{...n,exists:i&&await J(L(e,n.path))}})),d=N(o),f=o.variants.map(e=>e.width),h=d.length===f.length&&d.every((e,t)=>e===f[t]);!n&&(!h||l.some(e=>!e.exists))&&r.push({code:`GENERATED_VARIANT_MISSING`,message:`Asset "${a}" is missing its expected responsive variants.`,assetId:a,file:o.source,fix:t?void 0:`Run: coreframe assets build --id ${a}`}),i.push({assetId:a,source:o.source,sourceExists:s,lfsTracked:c,variants:l})}for(let e of a)n.manifest[e.assetId]||r.push({code:`INVALID_REFERENCE`,message:`Asset reference "${e.assetId}" is not present in ${_}.`,assetId:e.assetId,file:e.file,fix:`Run: coreframe assets add <file> --id ${e.assetId} --alt "..."`});return{ok:r.length===0,assets:i,problems:r}}async function w({cwd:e=process.cwd(),env:r=process.env,outputDir:i}={}){let s=[...(await C({cwd:e})).problems],f=[],p=[],m=u(e,i??r.COREFRAME_ASSETS_PUBLISH_DIR??`release-upload`),h=c(m,`asset-publish-report.json`);if(s.length===0){let{manifest:r}=await O(e);for(let[i,a]of Object.entries(r))for(let r of a.variants){let a=L(e,r.path),s=R(m,r.path),c={assetId:i,source:l(e,a).split(d).join(`/`),target:s,size:r.size};if(await J(s)){p.push(c);continue}await n(o(s),{recursive:!0}),await t(a,s),f.push(c)}}let g={ok:s.length===0,uploaded:f,skipped:p,reportPath:h,problems:s};return await n(o(h),{recursive:!0}),await a(h,`${JSON.stringify(g,null,2)}\n`),g}async function T(e,t={}){return S({...t,id:e})}function E(e,t=!1){if(t){console.log(JSON.stringify(e,null,2));return}let n=e;n.problems&&n.problems.length>0&&console.error(X(n.problems)),console.log(n.ok===!1?`Asset workflow has problems.`:`Asset workflow complete.`)}function D(e){if(!v.test(e))throw Error(`Asset ID "${e}" must use lowercase path segments.`)}async function O(e=process.cwd()){let t=u(e,_);if(!await J(t))return{manifest:{},problems:[{code:`MANIFEST_MISSING`,message:`${_} does not exist.`,file:_,fix:`Run: coreframe assets add <file> --id <asset-id> --alt "..."`}]};try{return{manifest:JSON.parse(await r(t,`utf8`)),problems:[]}}catch(e){return{manifest:{},problems:[{code:`INVALID_MANIFEST`,message:`${_} is not valid JSON: ${e.message}`,file:_}]}}}async function k(e,t){let r=u(e,_);await n(o(r),{recursive:!0}),await a(r,`${JSON.stringify(A(t),null,2)}\n`)}function A(e){return Object.fromEntries(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)))}function j(e,t){return c(p,`${e}${s(t).toLowerCase()}`).split(d).join(`/`)}async function M(t,r,i){let s=await V(),c=u(t,i.source);return Promise.all(N(i).map(async l=>{let u=await s(c).resize({width:l,withoutEnlargement:!0}).webp().toBuffer(),d=e(`sha256`).update(u).digest(`hex`).slice(0,12),f=`${m}/${r}.${d}.${l}.webp`,p=L(t,f);await n(o(p),{recursive:!0}),await a(p,u);let h=await s(u).metadata();return{format:`webp`,width:h.width??l,height:h.height??Math.round(i.height/i.width*l),path:f,size:u.byteLength,hash:d}}))}function N(e){if(e.variantWidths)return e.variantWidths;let t=e.width;if(!Number.isFinite(t)||t<=0)return[];let n=Math.min(Math.round(t),g);return[...h.filter(e=>e<=n),n].filter((e,t,n)=>t===0||e!==n[t-1])}function P(e,t){let n=I(e,t);if(n)throw Error(`Invalid variant widths: ${n}`)}function F(e,t){let n=I(t.variantWidths,t.width);if(n)return{code:`INVALID_VARIANT_WIDTHS`,message:`Asset "${e}" has invalid variantWidths: ${n}`,assetId:e,file:_,fix:`Edit ${_} so variantWidths contains positive, strictly ascending integer widths no larger than ${t.width}, then run: coreframe assets build --id ${e}`}}function I(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0)return`expected a non-empty array.`;for(let[n,r]of e.entries()){if(!Number.isInteger(r)||r<=0)return`width ${String(r)} must be a positive integer.`;if(n>0&&r<=e[n-1])return`widths must be unique and strictly ascending.`;if(r>t)return`width ${r} exceeds the source width of ${t}.`}}}function L(e,t){return R(u(e,`.coreframe`),t)}function R(e,t){if(!z(t))throw Error(`Asset variant path must be under ${m}/: ${t}`);return u(e,t)}function z(e){let t=e.split(`/`);return t[0]===m&&t.length>1&&!e.includes(`\\`)&&t.every(e=>e!==``&&e!==`.`&&e!==`..`)}async function B(e){let t=await(await V())(e).metadata();if(!t.width||!t.height)throw Error(`Could not read dimensions for ${e}.`);return{width:t.width,height:t.height}}async function V(){try{return(await import(`sharp`)).default}catch{throw Error(`The assets workflow requires the optional sharp dependency.`)}}async function H(e,t){await f(`git`,[`lfs`,`track`,t],{cwd:e})}async function U(e,t){await f(`git`,[`lfs`,`pull`,`--include=${t}`],{cwd:e})}async function W(e,t){try{return(await f(`git`,[`check-attr`,`filter`,`--`,t],{cwd:e})).stdout.includes(`filter: lfs`)}catch{return!1}}async function G(e){try{let t=await r(e,`utf8`);return b.test(t)}catch{return!1}}async function K(e){let t=await q(e),n=[];for(let i of t){let t=u(e,i);if(!await J(t))continue;let a=await r(t,`utf8`);for(let e of a.matchAll(y))n.push({assetId:e[1],file:i})}return n}async function q(e){return(await f(`git`,[`ls-files`,`*.md`,`*.mdx`,`*.ts`,`*.tsx`],{cwd:e})).stdout.split(/\r?\n/).map(e=>e.trim()).filter(Boolean).filter(e=>e.startsWith(`content/`)||e.startsWith(`src/`))}async function J(e){try{return await i(e),!0}catch{return!1}}function Y(e,t){return{code:`SOURCE_MISSING`,message:`Source asset "${t}" does not exist.`,assetId:e,file:t,fix:`Run: coreframe assets add <file> --id ${e} --alt "..."`}}function X(e){return e.map(e=>[`${e.code}: ${e.message}`,e.file?` file: ${l(process.cwd(),u(e.file))}`:void 0,e.fix?` fix: ${e.fix}`:void 0].filter(Boolean).join(`
1
+ import{createHash as e}from"node:crypto";import{copyFile as t,mkdir as n,readFile as r,stat as i,writeFile as a}from"node:fs/promises";import{dirname as o,extname as s,join as c,relative as l,resolve as u,sep as d}from"node:path";import f from"nano-spawn";const p=`assets-src`,m=`assets`,h=[320,640,960,1280,1920,2560],g=h.at(-1),_=c(`assets`,`manifest.json`),v=/^[a-z0-9]+(?:[/-][a-z0-9]+)*$/,y=/@asset\s+([a-z0-9][a-z0-9/-]*)/g,b=/^version https:\/\/git-lfs.github.com\/spec\/v1\n/m,x=/^version https:\/\/git-lfs.github.com\/spec\/v1\n(?:ext-[^\n]+\n)*oid sha256:[0-9a-f]{64}\nsize [0-9]+\n?$/;async function S({alt:e,cwd:r=process.cwd(),file:i,id:a,variantWidths:s}){O(a);let c=M(a,i),l=u(r,c),d=u(r,i),f=await V(d);F(s,f.width),await n(o(l),{recursive:!0}),await t(d,l),await U(r,c);let p=await k(r);return p.manifest[a]={source:c,alt:e,width:f.width,height:f.height,...s?{variantWidths:s}:{},variants:[]},await A(r,p.manifest),C({cwd:r,id:a,pull:!1})}async function C({cwd:e=process.cwd(),id:t,pull:n=!0,updateManifest:r=!0}={}){let{manifest:i,problems:a}=await k(e),o=[],s=[];for(let[r,c]of Object.entries(i)){if(t&&r!==t)continue;let l=I(r,c);if(l){a.push(l);continue}let d=u(e,c.source);if(!await Z(d)){a.push(Q(r,c.source));continue}if(await K(d)&&(n&&(await W(e,c.source),o.push(c.source)),await K(d))){a.push({code:`SOURCE_POINTER_MISSING_OBJECT`,message:`Source asset "${c.source}" is still a Git LFS pointer after a targeted pull.`,assetId:r,file:c.source,fix:`Run: git lfs pull --include="${c.source}"`});continue}let f=await N(e,r,c);i[r]={...c,variants:f},s.push({assetId:r,variants:f})}return t&&!i[t]&&a.push({code:`REFERENCE_MISSING`,message:`Asset "${t}" is not present in ${_}.`,assetId:t,fix:`Run: coreframe assets add <file> --id ${t} --alt "..."`}),r&&a.length===0&&s.length>0&&await A(e,i),{ok:a.length===0,assets:s,fetched:o,problems:a}}async function w({cwd:e=process.cwd(),fix:t=!1}={}){if(t){let t=await C({cwd:e}),n=await w({cwd:e,fix:!1});return{...n,ok:n.ok&&t.ok,problems:[...n.problems,...t.problems]}}let n=await k(e),r=[...n.problems],i=[],a=await Y(e),o=new Map(Object.entries(n.manifest).map(([e,t])=>[t.source,e]));for(let t of await q(e))await G(e,t)&&!await J(e,t)&&r.push({code:`LFS_BLOB_NOT_POINTER`,message:`Source asset "${t}" is covered by Git LFS attributes, but its staged Git blob is not an LFS pointer.`,assetId:o.get(t),file:t,fix:`Run: git add --renormalize -- "${t}"`});for(let[a,o]of Object.entries(n.manifest)){v.test(a)||r.push({code:`INVALID_ASSET_ID`,message:`Asset ID "${a}" must use lowercase path segments.`,assetId:a}),o.alt.trim()||r.push({code:`INVALID_ALT`,message:`Asset "${a}" is missing alt text.`,assetId:a,file:o.source,fix:`Run: coreframe assets add ${o.source} --id ${a} --alt "..."`}),o.source.startsWith(`${p}/`)||r.push({code:`INVALID_SOURCE_PATH`,message:`Asset "${a}" source must be under ${p}/.`,assetId:a,file:o.source});let n=I(a,o);n&&r.push(n);let s=await Z(u(e,o.source)),c=s?await G(e,o.source):!1;s?c||r.push({code:`LFS_NOT_TRACKED`,message:`Source asset "${o.source}" is not tracked by Git LFS.`,assetId:a,file:o.source,fix:`Run: git lfs track "${o.source}" && git add .gitattributes ${o.source}`}):r.push(Q(a,o.source));let l=await Promise.all(o.variants.map(async n=>{let i=B(n.path);return i||r.push({code:`INVALID_VARIANT_PATH`,message:`Asset "${a}" variant path must be under ${m}/.`,assetId:a,file:n.path,fix:t?void 0:`Run: coreframe assets build --id ${a}`}),{...n,exists:i&&await Z(R(e,n.path))}})),d=P(o),f=o.variants.map(e=>e.width),h=d.length===f.length&&d.every((e,t)=>e===f[t]);!n&&(!h||l.some(e=>!e.exists))&&r.push({code:`GENERATED_VARIANT_MISSING`,message:`Asset "${a}" is missing its expected responsive variants.`,assetId:a,file:o.source,fix:t?void 0:`Run: coreframe assets build --id ${a}`}),i.push({assetId:a,source:o.source,sourceExists:s,lfsTracked:c,variants:l})}for(let e of a)n.manifest[e.assetId]||r.push({code:`INVALID_REFERENCE`,message:`Asset reference "${e.assetId}" is not present in ${_}.`,assetId:e.assetId,file:e.file,fix:`Run: coreframe assets add <file> --id ${e.assetId} --alt "..."`});return{ok:r.length===0,assets:i,problems:r}}async function T({cwd:e=process.cwd(),env:r=process.env,outputDir:i}={}){let s=[...(await w({cwd:e})).problems],f=[],p=[],m=u(e,i??r.COREFRAME_ASSETS_PUBLISH_DIR??`release-upload`),h=c(m,`asset-publish-report.json`);if(s.length===0){let{manifest:r}=await k(e);for(let[i,a]of Object.entries(r))for(let r of a.variants){let a=R(e,r.path),s=z(m,r.path),c={assetId:i,source:l(e,a).split(d).join(`/`),target:s,size:r.size};if(await Z(s)){p.push(c);continue}await n(o(s),{recursive:!0}),await t(a,s),f.push(c)}}let g={ok:s.length===0,uploaded:f,skipped:p,reportPath:h,problems:s};return await n(o(h),{recursive:!0}),await a(h,`${JSON.stringify(g,null,2)}\n`),g}async function E(e,t={}){return C({...t,id:e})}function D(e,t=!1){if(t){console.log(JSON.stringify(e,null,2));return}let n=e;n.problems&&n.problems.length>0&&console.error($(n.problems)),console.log(n.ok===!1?`Asset workflow has problems.`:`Asset workflow complete.`)}function O(e){if(!v.test(e))throw Error(`Asset ID "${e}" must use lowercase path segments.`)}async function k(e=process.cwd()){let t=u(e,_);if(!await Z(t))return{manifest:{},problems:[{code:`MANIFEST_MISSING`,message:`${_} does not exist.`,file:_,fix:`Run: coreframe assets add <file> --id <asset-id> --alt "..."`}]};try{return{manifest:JSON.parse(await r(t,`utf8`)),problems:[]}}catch(e){return{manifest:{},problems:[{code:`INVALID_MANIFEST`,message:`${_} is not valid JSON: ${e.message}`,file:_}]}}}async function A(e,t){let r=u(e,_);await n(o(r),{recursive:!0}),await a(r,`${JSON.stringify(j(t),null,2)}\n`)}function j(e){return Object.fromEntries(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)))}function M(e,t){return c(p,`${e}${s(t).toLowerCase()}`).split(d).join(`/`)}async function N(t,r,i){let s=await H(),c=u(t,i.source);return Promise.all(P(i).map(async l=>{let u=await s(c).resize({width:l,withoutEnlargement:!0}).webp().toBuffer(),d=e(`sha256`).update(u).digest(`hex`).slice(0,12),f=`${m}/${r}.${d}.${l}.webp`,p=R(t,f);await n(o(p),{recursive:!0}),await a(p,u);let h=await s(u).metadata();return{format:`webp`,width:h.width??l,height:h.height??Math.round(i.height/i.width*l),path:f,size:u.byteLength,hash:d}}))}function P(e){if(e.variantWidths)return e.variantWidths;let t=e.width;if(!Number.isFinite(t)||t<=0)return[];let n=Math.min(Math.round(t),g);return[...h.filter(e=>e<=n),n].filter((e,t,n)=>t===0||e!==n[t-1])}function F(e,t){let n=L(e,t);if(n)throw Error(`Invalid variant widths: ${n}`)}function I(e,t){let n=L(t.variantWidths,t.width);if(n)return{code:`INVALID_VARIANT_WIDTHS`,message:`Asset "${e}" has invalid variantWidths: ${n}`,assetId:e,file:_,fix:`Edit ${_} so variantWidths contains positive, strictly ascending integer widths no larger than ${t.width}, then run: coreframe assets build --id ${e}`}}function L(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0)return`expected a non-empty array.`;for(let[n,r]of e.entries()){if(!Number.isInteger(r)||r<=0)return`width ${String(r)} must be a positive integer.`;if(n>0&&r<=e[n-1])return`widths must be unique and strictly ascending.`;if(r>t)return`width ${r} exceeds the source width of ${t}.`}}}function R(e,t){return z(u(e,`.coreframe`),t)}function z(e,t){if(!B(t))throw Error(`Asset variant path must be under ${m}/: ${t}`);return u(e,t)}function B(e){let t=e.split(`/`);return t[0]===m&&t.length>1&&!e.includes(`\\`)&&t.every(e=>e!==``&&e!==`.`&&e!==`..`)}async function V(e){let t=await(await H())(e).metadata();if(!t.width||!t.height)throw Error(`Could not read dimensions for ${e}.`);return{width:t.width,height:t.height}}async function H(){try{return(await import(`sharp`)).default}catch{throw Error(`The assets workflow requires the optional sharp dependency.`)}}async function U(e,t){await f(`git`,[`lfs`,`track`,t],{cwd:e})}async function W(e,t){await f(`git`,[`lfs`,`pull`,`--include=${t}`],{cwd:e})}async function G(e,t){try{return(await f(`git`,[`check-attr`,`filter`,`--`,t],{cwd:e})).stdout.includes(`filter: lfs`)}catch{return!1}}async function K(e){try{let t=await r(e,`utf8`);return b.test(t)}catch{return!1}}async function q(e){return(await f(`git`,[`ls-files`,`-z`,`--`,p],{cwd:e})).stdout.split(`\0`).filter(e=>e.startsWith(`${p}/`))}async function J(e,t){try{let n=`:${t}`,r=Number((await f(`git`,[`cat-file`,`-s`,n],{cwd:e})).stdout);if(!Number.isSafeInteger(r)||r>1024)return!1;let i=(await f(`git`,[`cat-file`,`-p`,n],{cwd:e})).stdout;return x.test(i)}catch{return!1}}async function Y(e){let t=await X(e),n=[];for(let i of t){let t=u(e,i);if(!await Z(t))continue;let a=await r(t,`utf8`);for(let e of a.matchAll(y))n.push({assetId:e[1],file:i})}return n}async function X(e){return(await f(`git`,[`ls-files`,`*.md`,`*.mdx`,`*.ts`,`*.tsx`],{cwd:e})).stdout.split(/\r?\n/).map(e=>e.trim()).filter(Boolean).filter(e=>e.startsWith(`content/`)||e.startsWith(`src/`))}async function Z(e){try{return await i(e),!0}catch{return!1}}function Q(e,t){return{code:`SOURCE_MISSING`,message:`Source asset "${t}" does not exist.`,assetId:e,file:t,fix:`Run: coreframe assets add <file> --id ${e} --alt "..."`}}function $(e){return e.map(e=>[`${e.code}: ${e.message}`,e.file?` file: ${l(process.cwd(),u(e.file))}`:void 0,e.fix?` fix: ${e.fix}`:void 0].filter(Boolean).join(`
2
2
  `)).join(`
3
- `)}export{x as addAsset,S as buildAssets,C as checkAssets,X as formatAssetProblems,T as materializeAsset,E as printAssetResult,w as publishAssets,O as readAssetManifest,L as resolveAssetVariantFile};
3
+ `)}export{S as addAsset,C as buildAssets,w as checkAssets,$ as formatAssetProblems,E as materializeAsset,D as printAssetResult,T as publishAssets,k as readAssetManifest,R as resolveAssetVariantFile};
@@ -39,6 +39,7 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
39
39
  } | {
40
40
  command: "deploy";
41
41
  args: {
42
+ environment: "staging" | "production";
42
43
  runMigrations: boolean;
43
44
  skipMigrations: boolean;
44
45
  skipPostDeployChecks: boolean;
@@ -55,7 +56,10 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
55
56
  command: "dev";
56
57
  args: {
57
58
  https: boolean;
59
+ host: string | undefined;
60
+ noManaged: boolean;
58
61
  platform: "ios" | "android" | "web" | "desktop" | undefined;
62
+ port: number | undefined;
59
63
  };
60
64
  } | {
61
65
  command: "upgrade";
@@ -165,6 +169,7 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
165
169
  } | {
166
170
  command: "deploy";
167
171
  args: {
172
+ environment: "staging" | "production";
168
173
  runMigrations: boolean;
169
174
  skipMigrations: boolean;
170
175
  skipPostDeployChecks: boolean;
@@ -181,7 +186,10 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
181
186
  command: "dev";
182
187
  args: {
183
188
  https: boolean;
189
+ host: string | undefined;
190
+ noManaged: boolean;
184
191
  platform: "ios" | "android" | "web" | "desktop" | undefined;
192
+ port: number | undefined;
185
193
  };
186
194
  } | {
187
195
  command: "upgrade";
package/dist/coreframe.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{addAsset as e,buildAssets as t,checkAssets as n,printAssetResult as r,publishAssets as i}from"./assets.js";import{runDrizzleKitCommand as a}from"./db/drizzle-kit.js";import{seedLocalDb as o}from"./db/seed.js";import{migrateLocalDb as s}from"./db/migrate.js";import{resetLocalDb as c}from"./db/reset.js";import{setupLocalDb as l}from"./db/setup.js";import{ensureLocalDb as u}from"./db/ensure.js";import{migrateHeldMigrations as d}from"./db/migrate-held.js";import{checkSchemaPush as f}from"./db/schema-check.js";import{tauriReleasePlatforms as p}from"./deploy/config.js";import{deployProject as m}from"./deploy.js";import{checkDeployMigrations as h}from"./deploy/check-migrations.js";import{devPlatforms as g,runDevServer as _}from"./dev.js";import{typecheckProject as v}from"./typecheck.js";import{checkUpgradeSkillCurrent as y}from"./upgrade/check-skill-current.js";import{ensureTemplateRemote as b}from"./upgrade/ensure-template-remote.js";import{fastForwardTemplateFiles as x}from"./upgrade/fast-forward-template-files.js";import{array as S,binary as C,command as w,extendType as T,flag as E,multioption as D,oneOf as O,option as k,optional as A,positional as j,rest as M,run as N,string as P,subcommands as F}from"cmd-ts";const I=T(P,{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}}),L=F({name:`coreframe`,cmds:{assets:F({name:`assets`,cmds:{add:w({name:`add`,args:{file:j({type:P,displayName:`file`,description:`Source image to add to the asset library.`}),id:k({long:`id`,type:P,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:k({long:`alt`,type:P,description:`Required alt text for the asset.`}),variantWidths:k({long:`widths`,type:A(I),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:E({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:w({name:`build`,args:{id:k({long:`id`,type:A(P),description:`Build only one logical asset ID.`}),noPull:E({long:`no-pull`,description:`Do not perform targeted Git LFS pulls for missing source objects.`,defaultValue:()=>!1}),json:E({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:w({name:`check`,args:{fix:E({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:E({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:w({name:`publish`,args:{outputDir:k({long:`output-dir`,type:A(P),description:`Root directory for additive local publish output. Defaults to COREFRAME_ASSETS_PUBLISH_DIR or release-upload.`}),json:E({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:w({name:`status`,args:{json:E({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:w({name:`deploy`,args:{runMigrations:E({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:E({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:E({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:D({long:`tauri-local-platform`,type:S(O(p)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:k({long:`tauri-workflow-name`,type:A(P),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:D({long:`tauri-workflow-platform`,type:S(O(p)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:k({long:`tauri-workflow-ref`,type:A(P),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:E({long:`yes`,short:`y`,description:`Approve noninteractive deploy prompts.`,defaultValue:()=>!1})},handler:({runMigrations:e,skipMigrations:t,skipPostDeployChecks:n,tauriLocalPlatforms:r,tauriWorkflowName:i,tauriWorkflowPlatforms:a,tauriWorkflowRef:o,yes:s})=>m([...e?[`--run-migrations`]:[],...t?[`--skip-migrations`]:[],...n?[`--skip-post-deploy-checks`]:[],...r.flatMap(e=>[`--tauri-local-platform`,e]),...i?[`--tauri-workflow-name`,i]:[],...a.flatMap(e=>[`--tauri-workflow-platform`,e]),...o?[`--tauri-workflow-ref`,o]:[],...s?[`--yes`]:[]])}),typecheck:w({name:`typecheck`,args:{},handler:v}),dev:w({name:`dev`,args:{https:E({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),platform:j({type:A(O(g)),displayName:`platform`,description:`Development target platform. Defaults to web.`})},handler:({https:e,platform:t})=>_({https:e,platform:t??`web`})}),upgrade:F({name:`upgrade`,cmds:{"ensure-template-remote":w({name:`ensure-template-remote`,args:{remote:k({long:`remote`,type:P,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:E({long:`no-fetch`,description:`Do not fetch the remote after ensuring it exists.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t})=>b({fetch:!e,remote:t})}),"check-skill-current":w({name:`check-skill-current`,args:{target:k({long:`target`,type:P,description:`Target Coreframe template git ref.`}),apply:E({long:`apply`,description:`Replace the local upgrade skill with the target version when it differs.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>{process.exitCode=y({apply:e,target:t})}}),"fast-forward-template-files":w({name:`fast-forward-template-files`,args:{target:k({long:`target`,type:P,description:`Target Coreframe template git ref.`}),base:k({long:`base`,type:A(P),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:E({long:`apply`,description:`Write clean fast-forwards to the worktree.`,defaultValue:()=>!1})},handler:({apply:e,base:t,target:n})=>x({apply:e,base:t,target:n})})}}),db:F({name:`db`,cmds:{ensure:w({name:`ensure`,args:{},handler:u}),generate:w({name:`generate`,args:{args:M({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>a(`generate`,e)}),migrate:w({name:`migrate`,args:{args:M({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>a(`migrate`,e)}),"migrate-held":w({name:`migrate-held`,args:{confirm:k({long:`confirm`,type:A(O([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>d({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":w({name:`migrate-local`,args:{},handler:s}),"migrations-check":w({name:`migrations-check`,args:{},handler:h}),reset:w({name:`reset`,args:{},handler:c}),seed:w({name:`seed`,args:{},handler:()=>o()}),"schema-check":w({name:`schema-check`,args:{seed:E({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>f({seed:e})}),setup:w({name:`setup`,args:{},handler:()=>l()}),studio:w({name:`studio`,args:{args:M({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>a(`studio`,e)})}})}});import.meta.main&&N(C(L),process.argv);export{L 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{deployProject as h}from"./deploy.js";import{checkDeployMigrations as g}from"./deploy/check-migrations.js";import{devPlatforms as _,runDevServer as v}from"./dev.js";import{run as y}from"./runner.js";import{typecheckProject as b}from"./typecheck.js";import{checkUpgradeSkillCurrent as x}from"./upgrade/check-skill-current.js";import{ensureTemplateRemote as S}from"./upgrade/ensure-template-remote.js";import{fastForwardTemplateFiles as C}from"./upgrade/fast-forward-template-files.js";import{array as w,command as T,extendType as E,flag as D,multioption as O,number as k,oneOf as A,option as j,optional as M,positional as N,rest as P,string as F,subcommands as I}from"cmd-ts";const L=E(F,{displayName:`widths`,description:`Comma-separated responsive variant widths.`,async from(e){let t=e.split(`,`).map(e=>Number(e));if(t.length===0||t.some(e=>!Number.isInteger(e)||e<=0)||t.some((e,n)=>n>0&&e<=t[n-1]))throw Error(`Widths must be positive, unique, strictly ascending integers.`);return t}}),R=I({name:`coreframe`,cmds:{assets:I({name:`assets`,cmds:{add:T({name:`add`,args:{file:N({type:F,displayName:`file`,description:`Source image to add to the asset library.`}),id:j({long:`id`,type:F,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:j({long:`alt`,type:F,description:`Required alt text for the asset.`}),variantWidths:j({long:`widths`,type:M(L),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({alt:t,file:n,id:i,json:a,variantWidths:o})=>r(await e({alt:t,file:n,id:i,json:a,variantWidths:o}),a)}),build:T({name:`build`,args:{id:j({long:`id`,type:M(F),description:`Build only one logical asset ID.`}),noPull:D({long:`no-pull`,description:`Do not perform targeted Git LFS pulls for missing source objects.`,defaultValue:()=>!1}),json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({id:e,json:n,noPull:i})=>r(await t({id:e,json:n,pull:!i}),n)}),check:T({name:`check`,args:{fix:D({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({fix:e,json:t})=>{let i=await n({fix:e,json:t});r(i,t),i.ok||(process.exitCode=1)}}),publish:T({name:`publish`,args:{outputDir:j({long:`output-dir`,type:M(F),description:`Root directory for additive local publish output. Defaults to COREFRAME_ASSETS_PUBLISH_DIR or release-upload.`}),json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e,outputDir:t})=>{let n=await i({json:e,outputDir:t});r(n,e),n.ok||(process.exitCode=1)}}),status:T({name:`status`,args:{json:D({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await n({json:e});r(t,e),t.ok||(process.exitCode=1)}})}}),deploy:T({name:`deploy`,args:{environment:N({type:A(p),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:D({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:D({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:D({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:O({long:`tauri-local-platform`,type:w(A(m)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:j({long:`tauri-workflow-name`,type:M(F),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:O({long:`tauri-workflow-platform`,type:w(A(m)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:j({long:`tauri-workflow-ref`,type:M(F),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:D({long:`yes`,short:`y`,description:`Approve the production deployment confirmation.`,defaultValue:()=>!1})},handler:({environment:e,runMigrations:t,skipMigrations:n,skipPostDeployChecks:r,tauriLocalPlatforms:i,tauriWorkflowName:a,tauriWorkflowPlatforms:o,tauriWorkflowRef:s,yes:c})=>h([e,...t?[`--run-migrations`]:[],...n?[`--skip-migrations`]:[],...r?[`--skip-post-deploy-checks`]:[],...i.flatMap(e=>[`--tauri-local-platform`,e]),...a?[`--tauri-workflow-name`,a]:[],...o.flatMap(e=>[`--tauri-workflow-platform`,e]),...s?[`--tauri-workflow-ref`,s]:[],...c?[`--yes`]:[]])}),typecheck:T({name:`typecheck`,args:{},handler:b}),dev:T({name:`dev`,args:{https:D({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:j({long:`host`,type:M(F),description:`Host address for the web development server.`}),noManaged:D({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:N({type:M(A(_)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),port:j({long:`port`,type:M(k),description:`Port for the web development server.`})},handler:({host:e,https:t,noManaged:n,platform:r,port:i})=>v({host:e,https:t,noManaged:n,platform:r??`web`,webPort:i})}),upgrade:I({name:`upgrade`,cmds:{"ensure-template-remote":T({name:`ensure-template-remote`,args:{remote:j({long:`remote`,type:F,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:D({long:`no-fetch`,description:`Do not fetch the remote after ensuring it exists.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t})=>S({fetch:!e,remote:t})}),"check-skill-current":T({name:`check-skill-current`,args:{target:j({long:`target`,type:F,description:`Target Coreframe template git ref.`}),apply:D({long:`apply`,description:`Replace the local upgrade skill with the target version when it differs.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>{process.exitCode=x({apply:e,target:t})}}),"fast-forward-template-files":T({name:`fast-forward-template-files`,args:{target:j({long:`target`,type:F,description:`Target Coreframe template git ref.`}),base:j({long:`base`,type:M(F),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:D({long:`apply`,description:`Write clean fast-forwards to the worktree.`,defaultValue:()=>!1})},handler:({apply:e,base:t,target:n})=>C({apply:e,base:t,target:n})})}}),db:I({name:`db`,cmds:{ensure:T({name:`ensure`,args:{},handler:u}),generate:T({name:`generate`,args:{args:P({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>a(`generate`,e)}),migrate:T({name:`migrate`,args:{args:P({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>a(`migrate`,e)}),"migrate-held":T({name:`migrate-held`,args:{confirm:j({long:`confirm`,type:M(A([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>d({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":T({name:`migrate-local`,args:{},handler:s}),"migrations-check":T({name:`migrations-check`,args:{},handler:g}),reset:T({name:`reset`,args:{},handler:c}),seed:T({name:`seed`,args:{},handler:()=>o()}),"schema-check":T({name:`schema-check`,args:{seed:D({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>f({seed:e})}),setup:T({name:`setup`,args:{},handler:()=>l()}),studio:T({name:`studio`,args:{args:P({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>a(`studio`,e)})}})}});import.meta.main&&y(R);export{R as coreframeCli};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{startProjectPostgres as e}from"./local-postgres.js";import t from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import n from"node:path";import r from"nano-spawn";import i from"node:process";const a=n.resolve(`node_modules`,`.bin`),o=i.platform===`win32`?`drizzle-kit.cmd`:`drizzle-kit`;async function s(s,c=[],{env:l=i.env}={}){try{var u=t();let i=u.a(await e({env:l})),d=l.PATH??l.Path??``;await r(o,[s,...c],{env:{...i.env,PATH:a+n.delimiter+d},stdio:`inherit`})}catch(e){u.e=e}finally{await u.d()}}import.meta.main&&s(i.argv[2],i.argv.slice(3)).catch(e=>{console.error(e),i.exit(1)});export{s as runDrizzleKitCommand};
2
+ import{readProjectConfig as e}from"../config.js";import{startProjectPostgres as t}from"./local-postgres.js";import n from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import r from"node:path";import i from"nano-spawn";import a from"node:process";const o=r.resolve(`node_modules`,`.bin`),s=a.platform===`win32`?`drizzle-kit.cmd`:`drizzle-kit`;async function c(r,i=[],{env:o=a.env}={}){try{var s=n();if((await e()).database?.dialect===`sqlite`){let e=o.TURSO_DATABASE_URL??o.DATABASE_URL;if(!e)throw Error(`TURSO_DATABASE_URL or DATABASE_URL is required for SQLite Drizzle commands.`);await l(i,r,{...o,DATABASE_URL:e});return}await l(i,r,s.a(await t({env:o})).env)}catch(e){s.e=e}finally{await s.d()}}async function l(e,t,n){let a=n.PATH??n.Path??``;await i(s,[t,...e],{env:{...n,PATH:o+r.delimiter+a},stdio:`inherit`})}export{c as runDrizzleKitCommand};
package/dist/db/ensure.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{importProjectModule as e}from"../project.js";import{createLocalPostgresDb as t,startProjectPostgres as n}from"./local-postgres.js";import r from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{SEED_EMAILS as i,SEED_IDS as a}from"./config.js";import{inferSchemaDialect as o,planLocalSchemaPush as s}from"./schema-push.js";import{seedLocalDb as c}from"./seed.js";import{setupLocalDb as l}from"./setup.js";import{eq as u}from"drizzle-orm";async function d(){return{schema:await e(`src/db/schema.ts`)}}async function f(e,{users:t}){let[n]=await e.select({id:t.id}).from(t).where(u(t.email,i.adminUser)).limit(1);return n?.id===a.adminUser}async function p(e,{schema:t}){let n=await s(o(t),t,e);return n.sqlStatements.length===0?(console.log(`Local database schema already in sync`),{appliedStatements:0,plan:n}):(console.log(`Local database schema drift detected; applying ${n.sqlStatements.length} statement(s)`),await n.apply(),console.log(`Local database schema synced`),{appliedStatements:n.sqlStatements.length,plan:n})}async function m({env:e=process.env}={}){try{var i=r();let a=i.a(await n({env:e})).env,o=await d(),s=await t({env:a});try{if(await h(s,o.schema))console.log(`Local database already set up`);else{console.log(`Local database is missing or unseeded; running setup`),await l(`default`,{env:a});return}(await p(s,o)).appliedStatements>0&&!await h(s,o.schema)&&(console.log(`Local database seed data missing after schema sync; running seed`),await c(void 0,{env:a}))}finally{await s.$client.end()}}catch(e){i.e=e}finally{await i.d()}}async function h(e,t){try{return await f(e,t)}catch{return!1}}import.meta.main&&m().catch(e=>{console.error(e),process.exit(1)});export{m as ensureLocalDb};
2
+ import{importProjectModule as e}from"../project.js";import{createLocalPostgresDb as t,startProjectPostgres as n}from"./local-postgres.js";import r from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{SEED_EMAILS as i,SEED_IDS as a}from"./config.js";import{inferSchemaDialect as o,planLocalSchemaPush as s}from"./schema-push.js";import{seedLocalDb as c}from"./seed.js";import{setupLocalDb as l}from"./setup.js";import{eq as u}from"drizzle-orm";async function d(){return{schema:await e(`src/db/schema.ts`)}}async function f(e,{users:t}){let[n]=await e.select({id:t.id}).from(t).where(u(t.email,i.adminUser)).limit(1);return n?.id===a.adminUser}async function p(e,{schema:t}){let n=await s(o(t),t,e);return n.sqlStatements.length===0?(console.log(`Local database schema already in sync`),{appliedStatements:0,plan:n}):(console.log(`Local database schema drift detected; applying ${n.sqlStatements.length} statement(s)`),await n.apply(),console.log(`Local database schema synced`),{appliedStatements:n.sqlStatements.length,plan:n})}async function m({env:e=process.env}={}){try{var i=r();let a=i.a(await n({env:e})).env,o=await d(),s=await t({env:a});try{if(await h(s,o.schema))console.log(`Local database already set up`);else{console.log(`Local database is missing or unseeded; running setup`),await l(`default`,{env:a});return}(await p(s,o)).appliedStatements>0&&!await h(s,o.schema)&&(console.log(`Local database seed data missing after schema sync; running seed`),await c(void 0,{env:a}))}finally{await s.$client.end()}}catch(e){i.e=e}finally{await i.d()}}async function h(e,t){try{return await f(e,t)}catch{return!1}}export{m as ensureLocalDb};
@@ -7,4 +7,4 @@ import{createProjectDeployDatabase as e}from"./project.js";import{collectHeldMig
7
7
  id text primary key,
8
8
  sha256 text not null,
9
9
  applied_at text not null default current_timestamp
10
- )`]}}function f(e,t){return`select sha256 from ${m(e)} where id = ${g(t)}`}function p(e,t,n){return`insert into ${m(e)} (id, sha256) values (${g(t)}, ${g(n)})`}function m(e){switch(e){case`postgres`:return`drizzle.__held_migrations`;case`sqlite`:return`__held_migrations`}}async function h(e,t){if(e.transaction){await e.transaction(t);return}await e.execute(`begin`);try{await t(e),await e.execute(`commit`)}catch(t){throw await e.execute(`rollback`),t}}function g(e){return`'${e.replaceAll(`'`,`''`)}'`}import.meta.main&&s().catch(e=>{console.error(e),process.exit(1)});export{p as heldMigrationInsertQuery,f as heldMigrationSelectQuery,s as migrateHeldMigrations,c as validateHeldMigrationHeader};
10
+ )`]}}function f(e,t){return`select sha256 from ${m(e)} where id = ${g(t)}`}function p(e,t,n){return`insert into ${m(e)} (id, sha256) values (${g(t)}, ${g(n)})`}function m(e){switch(e){case`postgres`:return`drizzle.__held_migrations`;case`sqlite`:return`__held_migrations`}}async function h(e,t){if(e.transaction){await e.transaction(t);return}await e.execute(`begin`);try{await t(e),await e.execute(`commit`)}catch(t){throw await e.execute(`rollback`),t}}function g(e){return`'${e.replaceAll(`'`,`''`)}'`}export{p as heldMigrationInsertQuery,f as heldMigrationSelectQuery,s as migrateHeldMigrations,c as validateHeldMigrationHeader};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{createLocalPostgresDb as e,startProjectPostgres as t}from"./local-postgres.js";import n from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{MIGRATIONS_FOLDER as r}from"./config.js";import{access as i}from"node:fs/promises";import{resolve as a}from"node:path";import{migrate as o}from"drizzle-orm/node-postgres/migrator";const s=a(r);async function c({env:r=process.env}={}){try{var a=n();await i(s);let c=await e({env:a.a(await t({env:r})).env});try{await o(c,{migrationsFolder:s}),console.log(`Applied local migrations from ${s}`)}finally{await c.$client.end()}}catch(e){a.e=e}finally{await a.d()}}import.meta.main&&c().catch(e=>{console.error(e),process.exit(1)});export{c as migrateLocalDb};
2
+ import{createLocalPostgresDb as e,startProjectPostgres as t}from"./local-postgres.js";import n from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{MIGRATIONS_FOLDER as r}from"./config.js";import{access as i}from"node:fs/promises";import{resolve as a}from"node:path";import{migrate as o}from"drizzle-orm/node-postgres/migrator";const s=a(r);async function c({env:r=process.env}={}){try{var a=n();await i(s);let c=await e({env:a.a(await t({env:r})).env});try{await o(c,{migrationsFolder:s}),console.log(`Applied local migrations from ${s}`)}finally{await c.$client.end()}}catch(e){a.e=e}finally{await a.d()}}export{c as migrateLocalDb};
package/dist/db/reset.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{createLocalPostgresDb as e,startProjectPostgres as t}from"./local-postgres.js";import n from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{sql as r}from"drizzle-orm";async function i({env:i=process.env}={}){try{var a=n();let o=await e({env:a.a(await t({env:i})).env});try{await o.execute(r`drop schema if exists public cascade`),await o.execute(r`create schema public`),console.log(`Reset local PostgreSQL schema`)}finally{await o.$client.end()}}catch(e){a.e=e}finally{await a.d()}}import.meta.main&&i().catch(e=>{console.error(e),process.exit(1)});export{i as resetLocalDb};
2
+ import{createLocalPostgresDb as e,startProjectPostgres as t}from"./local-postgres.js";import n from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{sql as r}from"drizzle-orm";async function i({env:i=process.env}={}){try{var a=n();let o=await e({env:a.a(await t({env:i})).env});try{await o.execute(r`drop schema if exists public cascade`),await o.execute(r`create schema public`),console.log(`Reset local PostgreSQL schema`)}finally{await o.$client.end()}}catch(e){a.e=e}finally{await a.d()}}export{i as resetLocalDb};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{importProjectModule as e}from"../project.js";import{createLocalPostgresDb as t,startTemporaryProjectPostgres as n}from"./local-postgres.js";import r from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{inferSchemaDialect as i,planLocalSchemaPush as a}from"./schema-push.js";import{runProjectSeed as o}from"./seed.js";async function s({env:s=process.env,seed:l=!1}={}){try{var u=r();console.log(`Checking Drizzle schema against a throwaway PostgreSQL database`);let d=u.a(await n({env:s})),f=await e(`src/db/schema.ts`),p=await t({env:d.env});try{let e=i(f);if(e!==`postgres`)throw Error(`Temporary schema push checks require PostgreSQL schema, but found ${e}.`);let t=await a(e,f,p);t.sqlStatements.length===0?console.log(`Drizzle schema check passed with no changes`):(console.log(`Applying ${t.sqlStatements.length} statement(s) to throwaway database`),await t.apply(),console.log(`Drizzle schema check passed`)),l&&(console.log(`Seeding throwaway database`),await o(`default`,{env:d.env}),console.log(`Throwaway database seed check passed`))}finally{await c(p)}}catch(e){u.e=e}finally{await u.d()}}function c(e){return e.$client.end()}import.meta.main&&s().catch(e=>{console.error(e),process.exit(1)});export{s as checkSchemaPush};
2
+ import{importProjectModule as e}from"../project.js";import{createLocalPostgresDb as t,startTemporaryProjectPostgres as n}from"./local-postgres.js";import r from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{inferSchemaDialect as i,planLocalSchemaPush as a}from"./schema-push.js";import{runProjectSeed as o}from"./seed.js";async function s({env:s=process.env,seed:l=!1}={}){try{var u=r();console.log(`Checking Drizzle schema against a throwaway PostgreSQL database`);let d=u.a(await n({env:s})),f=await e(`src/db/schema.ts`),p=await t({env:d.env});try{let e=i(f);if(e!==`postgres`)throw Error(`Temporary schema push checks require PostgreSQL schema, but found ${e}.`);let t=await a(e,f,p);t.sqlStatements.length===0?console.log(`Drizzle schema check passed with no changes`):(console.log(`Applying ${t.sqlStatements.length} statement(s) to throwaway database`),await t.apply(),console.log(`Drizzle schema check passed`)),l&&(console.log(`Seeding throwaway database`),await o(`default`,{env:d.env}),console.log(`Throwaway database seed check passed`))}finally{await c(p)}}catch(e){u.e=e}finally{await u.d()}}function c(e){return e.$client.end()}export{s as checkSchemaPush};
package/dist/db/seed.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{readProjectConfig as e}from"../config.js";import{importProjectModule as t}from"../project.js";import{startProjectPostgres as n}from"./local-postgres.js";import r from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{getSeedProfile as i}from"./config.js";async function a(t=i(),{env:a=process.env}={}){try{var c=r();let i=await e(),l=s(i);await o(t,{env:c.a(await n({config:i,env:a})).env,seedEntryPoint:l}),console.log(`Seeded local database with "${t}" profile`)}catch(e){c.e=e}finally{await c.d()}}async function o(n=i(),{env:r,seedEntryPoint:a}){a??=s(await e()),await l(r,async()=>{await c(await t(a),a)({env:r,profile:n})})}function s(e){let t=e.database?.seed;if(!t)throw Error(`coreframe.config.ts must configure database.seed before running db seed.`);return t}function c(e,t){let n=e.default??e.seed??e.seedLocalDb;if(!n)throw Error(`${t} must export a default seed function, seed function, or seedLocalDb function.`);return n}async function l(e,t){let n=new Map;for(let[t,r]of Object.entries(e))n.set(t,process.env[t]),r===void 0?delete process.env[t]:process.env[t]=r;try{await t()}finally{for(let[e,t]of n)t===void 0?delete process.env[e]:process.env[e]=t}}import.meta.main&&a().catch(e=>{console.error(e),process.exit(1)});export{o as runProjectSeed,a as seedLocalDb};
2
+ import{readProjectConfig as e}from"../config.js";import{importProjectModule as t}from"../project.js";import{startProjectPostgres as n}from"./local-postgres.js";import r from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{getSeedProfile as i}from"./config.js";async function a(t=i(),{env:a=process.env}={}){try{var c=r();let i=await e(),l=s(i);await o(t,{env:c.a(await n({config:i,env:a})).env,seedEntryPoint:l}),console.log(`Seeded local database with "${t}" profile`)}catch(e){c.e=e}finally{await c.d()}}async function o(n=i(),{env:r,seedEntryPoint:a}){a??=s(await e()),await l(r,async()=>{await c(await t(a),a)({env:r,profile:n})})}function s(e){let t=e.database?.seed;if(!t)throw Error(`coreframe.config.ts must configure database.seed before running db seed.`);return t}function c(e,t){let n=e.default??e.seed??e.seedLocalDb;if(!n)throw Error(`${t} must export a default seed function, seed function, or seedLocalDb function.`);return n}async function l(e,t){let n=new Map;for(let[t,r]of Object.entries(e))n.set(t,process.env[t]),r===void 0?delete process.env[t]:process.env[t]=r;try{await t()}finally{for(let[e,t]of n)t===void 0?delete process.env[e]:process.env[e]=t}}export{o as runProjectSeed,a as seedLocalDb};
package/dist/db/setup.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{startProjectPostgres as e}from"./local-postgres.js";import t from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{seedLocalDb as n}from"./seed.js";import{migrateLocalDb as r}from"./migrate.js";import{resetLocalDb as i}from"./reset.js";async function a(a=`default`,{env:o=process.env}={}){try{var s=t();let c=s.a(await e({env:o})).env;await i({env:c}),await r({env:c}),await n(a,{env:c})}catch(e){s.e=e}finally{await s.d()}}import.meta.main&&a().catch(e=>{console.error(e),process.exit(1)});export{a as setupLocalDb};
2
+ import{startProjectPostgres as e}from"./local-postgres.js";import t from"../_virtual/_@oxc-project_runtime@0.138.0/helpers/esm/usingCtx.js";import{seedLocalDb as n}from"./seed.js";import{migrateLocalDb as r}from"./migrate.js";import{resetLocalDb as i}from"./reset.js";async function a(a=`default`,{env:o=process.env}={}){try{var s=t();let c=s.a(await e({env:o})).env;await i({env:c}),await r({env:c}),await n(a,{env:c})}catch(e){s.e=e}finally{await s.d()}}export{a as setupLocalDb};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{collectDeployMigrationFiles as e,formatMigrationList as t,formatMigrationScanFailures as n,scanDeployMigrationFiles as r}from"./migrations.js";async function i(){let i=await e();console.log(`Deploy migration files:`),console.log(t(i));let a=await r(i);if(a.length>0)throw Error(`Deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${n(a)}`);console.log(`Deploy migrations passed safety checks.`)}import.meta.main&&i().catch(e=>{console.error(e),process.exit(1)});export{i as checkDeployMigrations};
2
+ import{collectDeployMigrationFiles as e,formatMigrationList as t,formatMigrationScanFailures as n,scanDeployMigrationFiles as r}from"./migrations.js";async function i(){let i=await e();console.log(`Deploy migration files:`),console.log(t(i));let a=await r(i);if(a.length>0)throw Error(`Deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${n(a)}`);console.log(`Deploy migrations passed safety checks.`)}export{i as checkDeployMigrations};
@@ -1 +1 @@
1
- async function e(e,n){let r=n.productionUrl??e.productionUrl;e.smokeChecks.length>0&&!r&&console.log(`Skipping HTTP smoke checks. Set DEPLOY_URL or productionUrl in deploy config.`);for(let n of e.smokeChecks){if(!r&&!n.url)continue;let e=n.url??new URL(n.path??`/`,r).toString(),i=await fetch(e);if(!t(i.status,n.expectedStatus??`2xx`))throw Error(`${n.name} smoke check failed: ${e} returned ${i.status}.`);console.log(`${n.name} smoke check passed: ${i.status} ${e}`)}await e.afterDeploy?.({...n,productionUrl:r})}function t(e,t){return t===`2xx`?e>=200&&e<300:Array.isArray(t)?t.includes(e):e===t}export{e as runPostDeployChecks};
1
+ async function e(e,n){for(let r of e.smokeChecks){let e=r.url??new URL(r.path??`/`,n.url).toString(),i=await fetch(e);if(!t(i.status,r.expectedStatus??`2xx`))throw Error(`${r.name} smoke check failed: ${e} returned ${i.status}.`);console.log(`${r.name} smoke check passed: ${i.status} ${e}`)}await e.afterDeploy?.(n)}function t(e,t){return t===`2xx`?e>=200&&e<300:Array.isArray(t)?t.includes(e):e===t}export{e as runPostDeployChecks};
@@ -1,8 +1,10 @@
1
1
  //#region deploy/config.d.ts
2
+ declare const deployEnvironments: readonly ["staging", "production"];
3
+ type DeployEnvironment = (typeof deployEnvironments)[number];
2
4
  type DeployContext = {
3
- environment: string;
4
- productionUrl?: string;
5
+ environment: DeployEnvironment;
5
6
  releaseName: string;
7
+ url: string;
6
8
  version: string;
7
9
  };
8
10
  declare const tauriReleasePlatforms: readonly ["macos", "windows", "linux", "ios", "android"];
@@ -36,18 +38,22 @@ type DeploySmokeCheck = {
36
38
  url?: string;
37
39
  expectedStatus?: "2xx" | number | number[];
38
40
  };
41
+ type DeployEnvironmentConfig = {
42
+ url?: string;
43
+ };
39
44
  type DeployConfig = {
40
45
  afterDeploy?: (context: DeployContext) => Promise<void> | void;
41
- productionUrl?: string;
46
+ environments?: Partial<Record<DeployEnvironment, DeployEnvironmentConfig>>;
42
47
  smokeChecks?: DeploySmokeCheck[];
43
48
  tauriRelease?: TauriReleaseConfig;
44
49
  };
45
50
  type ResolvedDeployConfig = {
46
51
  afterDeploy?: (context: DeployContext) => Promise<void> | void;
47
- productionUrl?: string;
52
+ environment: DeployEnvironment;
48
53
  smokeChecks: DeploySmokeCheck[];
49
54
  tauriRelease: TauriReleaseConfig;
55
+ url?: string;
50
56
  };
51
- declare function resolveDeployConfig(config: DeployConfig, env?: NodeJS.ProcessEnv): ResolvedDeployConfig;
57
+ declare function resolveDeployConfig(config: DeployConfig, environment: DeployEnvironment, env?: NodeJS.ProcessEnv): ResolvedDeployConfig;
52
58
  //#endregion
53
- export { DeployConfig, DeployContext, DeploySmokeCheck, ResolvedDeployConfig, TauriReleaseConfig, TauriReleasePlatform, TauriReleasePlatformConfig, resolveDeployConfig, tauriReleasePlatforms };
59
+ export { DeployConfig, DeployContext, DeployEnvironment, DeployEnvironmentConfig, DeploySmokeCheck, ResolvedDeployConfig, TauriReleaseConfig, TauriReleasePlatform, TauriReleasePlatformConfig, deployEnvironments, resolveDeployConfig, tauriReleasePlatforms };
@@ -1 +1 @@
1
- const e=[`macos`,`windows`,`linux`,`ios`,`android`];function t(e,t=process.env){return{afterDeploy:e.afterDeploy,productionUrl:e.productionUrl??t.DEPLOY_URL??t.PRODUCTION_URL,smokeChecks:e.smokeChecks??[{name:`home`,path:`/`}],tauriRelease:e.tauriRelease??{}}}export{t as resolveDeployConfig,e as tauriReleasePlatforms};
1
+ const e=[`staging`,`production`],t=[`macos`,`windows`,`linux`,`ios`,`android`];function n(e,t,n=process.env){return{afterDeploy:e.afterDeploy,environment:t,smokeChecks:e.smokeChecks??[{name:`home`,path:`/`}],tauriRelease:e.tauriRelease??{},url:n.DEPLOY_URL||e.environments?.[t]?.url}}export{e as deployEnvironments,n as resolveDeployConfig,t as tauriReleasePlatforms};
@@ -1,3 +1,5 @@
1
+ import { DeployEnvironment } from "./config.js";
2
+
1
3
  //#region deploy/version.d.ts
2
4
  type PackageJson = {
3
5
  name?: string;
@@ -10,6 +12,8 @@ type ReleasePackageJson = PackageJson & {
10
12
  };
11
13
  declare function nextReleaseVersion(currentVersion: string, now?: Date): string;
12
14
  declare function releaseName(packageName: string, version: string): string;
15
+ declare function stagingReleaseVersion(currentVersion: string, revision: string): string;
16
+ declare function resolveReleaseVersion(environment: DeployEnvironment, currentVersion: string, revision: string, now?: Date): string;
13
17
  declare function formatReleaseDate(date: Date): string;
14
18
  declare function readPackageJson(path?: string): Promise<{
15
19
  packageJson: ReleasePackageJson;
@@ -17,4 +21,4 @@ declare function readPackageJson(path?: string): Promise<{
17
21
  }>;
18
22
  declare function writePackageVersion(path: string, packageJson: PackageJson, version: string): Promise<void>;
19
23
  //#endregion
20
- export { formatReleaseDate, nextReleaseVersion, readPackageJson, releaseName, writePackageVersion };
24
+ export { formatReleaseDate, nextReleaseVersion, readPackageJson, releaseName, resolveReleaseVersion, stagingReleaseVersion, writePackageVersion };
@@ -1 +1 @@
1
- import{readFile as e,writeFile as t}from"node:fs/promises";function n(e,t=new Date){let n=s(e),r=i(t),a=n.minor===r?n.patch+1:0;return`${n.major}.${r}.${a}`}function r(e,t){return`${e}@${t}`}function i(e){return`${String(e.getFullYear())}${String(e.getMonth()+1).padStart(2,`0`)}${String(e.getDate()).padStart(2,`0`)}`}async function a(t=`package.json`){let n=await e(t,`utf8`),r=JSON.parse(n);if(!r.name)throw Error(`${t} is missing a package name.`);if(!r.version)throw Error(`${t} is missing a package version.`);return{packageJson:r,source:n}}async function o(e,n,r){await t(e,`${JSON.stringify({...n,version:r},null,2)}\n`)}function s(e){let t=/^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/u.exec(e);if(!t?.groups)throw Error(`Invalid package version "${e}". Expected major.minor.patch.`);return{major:Number(t.groups.major),minor:t.groups.minor,patch:Number(t.groups.patch)}}export{i as formatReleaseDate,n as nextReleaseVersion,a as readPackageJson,r as releaseName,o as writePackageVersion};
1
+ import{readFile as e,writeFile as t}from"node:fs/promises";function n(e,t=new Date){let n=l(e),r=o(t),i=n.minor===r?n.patch+1:0;return`${n.major}.${r}.${i}`}function r(e,t){return`${e}@${t}`}function i(e,t){return`${e}-staging.${t}`}function a(e,t,r,a=new Date){return e===`production`?n(t,a):i(t,r)}function o(e){return`${String(e.getFullYear())}${String(e.getMonth()+1).padStart(2,`0`)}${String(e.getDate()).padStart(2,`0`)}`}async function s(t=`package.json`){let n=await e(t,`utf8`),r=JSON.parse(n);if(!r.name)throw Error(`${t} is missing a package name.`);if(!r.version)throw Error(`${t} is missing a package version.`);return{packageJson:r,source:n}}async function c(e,n,r){await t(e,`${JSON.stringify({...n,version:r},null,2)}\n`)}function l(e){let t=/^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/u.exec(e);if(!t?.groups)throw Error(`Invalid package version "${e}". Expected major.minor.patch.`);return{major:Number(t.groups.major),minor:t.groups.minor,patch:Number(t.groups.patch)}}export{o as formatReleaseDate,n as nextReleaseVersion,s as readPackageJson,r as releaseName,a as resolveReleaseVersion,i as stagingReleaseVersion,c as writePackageVersion};
package/dist/deploy.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { TauriReleasePlatform } from "./deploy/config.js";
1
+ import { DeployEnvironment, TauriReleasePlatform } from "./deploy/config.js";
2
2
  import { readProjectConfig } from "./config.js";
3
3
 
4
4
  //#region deploy.d.ts
5
5
  type DeployArgs = {
6
+ environment: DeployEnvironment;
6
7
  runMigrations: boolean;
7
8
  skipMigrations: boolean;
8
9
  skipPostDeployChecks: boolean;
package/dist/deploy.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{readProjectConfig as e}from"./config.js";import{createProjectDeployDatabase as t}from"./db/project.js";import{collectDeployMigrationFiles as n,formatMigrationList as r,formatMigrationScanFailures as i,scanDeployMigrationFiles as a}from"./deploy/migrations.js";import{runPostDeployChecks as o}from"./deploy/checks.js";import{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 p,uploadTauriReleaseToB2 as m,validateTauriReleasePlan as h,writeTauriReleaseMetadata as g}from"./deploy/tauri-release.js";import{nextReleaseVersion as _,readPackageJson as v,releaseName as y,writePackageVersion as b}from"./deploy/version.js";import{writeFile as x}from"node:fs/promises";import S from"nano-spawn";import*as C from"@clack/prompts";const w=`package.json`;async function T(n=process.argv.slice(2),r=process.env){let c=E(n),{packageJson:l,source:u}=await v(w),T=_(l.version),D=y(l.name,T),O=r.DEPLOY_ENV??`production`,A=F(r,D,l.name,T),L=await e(),R=s(L.deploy??{},r),z=f({args:c,config:R.tauriRelease,env:r}),B=!1,V={files:[]};C.intro(`Deploy ${D}`),N(c),P(r,[`POSTHOG_API_KEY`,`POSTHOG_PROJECT_ID`]),await M(),await h(z);let H=await k(await t({config:L.database,env:r})),U=await a(H);if(U.length>0)throw Error(`Pending deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${i(U)}`);let W=await j(c,H);try{await b(w,l,T),V=await g(z,T),C.log.step(`Running pre-deploy build`),await S(`pnpm`,[`build`],{env:I(A),stdio:`inherit`});let e=z.enabled?await d({env:I(A),plan:z,releaseName:D,version:T}):void 0;C.log.step(`Running release build and uploading PostHog source maps`),B=!0,await S(`pnpm`,[`build`],{env:A,stdio:`inherit`}),W&&(C.log.step(`Running deploy-safe database migrations`),await S(`pnpm`,[`db`,`migrate`],{env:A,stdio:`inherit`})),C.log.step(`Deploying Worker and static assets to Cloudflare`),await S(`pnpm`,[`exec`,`wrangler`,`deploy`,`--strict`,`--keep-vars`,`--tag`,D,`--message`,`Deploy ${D}`,`--var`,`POSTHOG_RELEASE:${D}`],{env:A,stdio:`inherit`}),c.skipPostDeployChecks||(C.log.step(`Running post-deploy checks`),await o(R,{environment:O,productionUrl:R.productionUrl,releaseName:D,version:T})),e&&(C.log.step(`Uploading Tauri release artifacts to Backblaze B2`),await m(z,e)),C.outro(`Deployed ${D}`)}catch(e){throw B||(await x(w,u),await p(V)),e}}function E(e){return{runMigrations:e.includes(`--run-migrations`)||e.includes(`--yes`),skipMigrations:e.includes(`--skip-migrations`),skipPostDeployChecks:e.includes(`--skip-post-deploy-checks`),tauriLocalPlatforms:D(e,`--tauri-local-platform`).map(e=>u(e)),tauriWorkflowName:O(e,`--tauri-workflow-name`),tauriWorkflowPlatforms:D(e,`--tauri-workflow-platform`).map(e=>u(e)),tauriWorkflowRef:O(e,`--tauri-workflow-ref`),yes:e.includes(`--yes`)}}function D(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 O(e,t){let n=D(e,t);if(n.length>1)throw Error(`${t} can only be provided once.`);return n[0]}async function k(e){try{return await A(e)}finally{await e.close?.()}}async function A(e){return c(await n(),await l(e))}async function j(e,t){if(t.length===0)return C.log.info(`No pending deploy migrations.`),!1;if(C.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 C.confirm({initialValue:!0,message:`Run these deploy-safe database migrations before Cloudflare deploy?`});if(C.isCancel(n))throw C.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`);return n}async function M(){let{stdout:e}=await S(`git`,[`status`,`--porcelain`],{stdin:`ignore`});if(e.trim().length>0)throw Error(`Deploy requires a clean git tree before the release version is applied.`)}function N(e){if(e.runMigrations&&e.skipMigrations)throw Error(`Use only one of --run-migrations, --skip-migrations, or --yes.`)}function P(e,t){let n=t.filter(t=>!e[t]);if(n.length>0)throw Error(`Missing required deploy environment variables: ${n.join(`, `)}`);return Object.fromEntries(t.map(t=>[t,e[t]]))}function F(e,t,n,r){return{...e,POSTHOG_RELEASE:t,POSTHOG_RELEASE_NAME:e.POSTHOG_RELEASE_NAME??n,POSTHOG_RELEASE_VERSION:e.POSTHOG_RELEASE_VERSION??r}}function I(e){let{POSTHOG_API_KEY:t,POSTHOG_PROJECT_ID:n,...r}=e;return r}import.meta.main&&T().catch(e=>{console.error(e),process.exit(1)});export{T as deployProject,E as parseDeployArgs,e as readProjectConfig};
2
+ import{readProjectConfig as e}from"./config.js";import{createProjectDeployDatabase as t}from"./db/project.js";import{collectDeployMigrationFiles as n,formatMigrationList as r,formatMigrationScanFailures as i,scanDeployMigrationFiles as a}from"./deploy/migrations.js";import{runPostDeployChecks as o}from"./deploy/checks.js";import{deployEnvironments as s,resolveDeployConfig as c}from"./deploy/config.js";import{filterPendingMigrations as l,readAppliedMigrationNames as u}from"./deploy/pending-migrations.js";import{parseTauriReleasePlatform as d,prepareTauriRelease as f,resolveTauriReleasePlan as p,restoreTauriReleaseMetadata as m,uploadTauriReleaseToB2 as h,validateTauriReleasePlan as g,writeTauriReleaseMetadata as _}from"./deploy/tauri-release.js";import{readPackageJson as v,releaseName as y,resolveReleaseVersion as b,writePackageVersion as x}from"./deploy/version.js";import{writeFile as S}from"node:fs/promises";import C from"nano-spawn";import*as w from"@clack/prompts";const T=`package.json`;async function E(n=process.argv.slice(2),r=process.env){let s=D(n);F(s);let{packageJson:l,source:u}=await v(T),d=await e(),E=c(d.deploy??{},s.environment,r);z(r,[`POSTHOG_API_KEY`,`POSTHOG_PROJECT_ID`]);let O=I(E.url,s.environment),k=p({args:s,config:s.environment===`production`?E.tauriRelease:{},env:r});R(s.environment,k.enabled),await g(k);let A=await P(),M=b(s.environment,l.version,A),H=y(l.name,M),U=B(r,s.environment,H,l.name,M),W=s.environment===`production`,G=!1,K={files:[]};w.intro(`Deploy ${H} to ${s.environment}`),await L(s);let q=await j(await t({config:d.database,env:r})),J=await a(q);if(J.length>0)throw Error(`Pending deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${i(J)}`);let Y=await N(s,q);try{W&&(await x(T,l,M),K=await _(k,M)),w.log.step(`Running pre-deploy build`),await C(`pnpm`,[`build`],{env:V(U),stdio:`inherit`});let e=k.enabled?await f({env:V(U),plan:k,releaseName:H,version:M}):void 0;w.log.step(`Running release build and uploading PostHog source maps`),G=!0,await C(`pnpm`,[`build`],{env:U,stdio:`inherit`}),Y&&(w.log.step(`Running deploy-safe database migrations`),await C(`pnpm`,[`db`,`migrate`],{env:U,stdio:`inherit`})),w.log.step(`Deploying Worker and static assets to Cloudflare`),await C(`pnpm`,[`exec`,`wrangler`,`deploy`,`--env`,s.environment,`--strict`,`--keep-vars`,`--tag`,H,`--message`,`Deploy ${H}`,`--var`,`DEPLOY_ENV:${s.environment}`,`--var`,`POSTHOG_RELEASE:${H}`],{env:U,stdio:`inherit`}),s.skipPostDeployChecks||(w.log.step(`Running post-deploy checks`),await o(E,{environment:s.environment,releaseName:H,url:O,version:M})),e&&(w.log.step(`Uploading Tauri release artifacts to Backblaze B2`),await h(k,e)),w.outro(`Deployed ${H} to ${s.environment}`)}catch(e){throw!G&&W&&(await S(T,u),await m(K)),e}}function D(e){return{environment:O(e[0]),runMigrations:e.includes(`--run-migrations`),skipMigrations:e.includes(`--skip-migrations`),skipPostDeployChecks:e.includes(`--skip-post-deploy-checks`),tauriLocalPlatforms:k(e,`--tauri-local-platform`).map(e=>d(e)),tauriWorkflowName:A(e,`--tauri-workflow-name`),tauriWorkflowPlatforms:k(e,`--tauri-workflow-platform`).map(e=>d(e)),tauriWorkflowRef:A(e,`--tauri-workflow-ref`),yes:e.includes(`--yes`)}}function O(e){if(e&&s.includes(e))return e;throw Error(`Deploy environment must be one of: ${s.join(`, `)}.`)}function k(e,t){let n=[];for(let r=0;r<e.length;r+=1){let i=e[r];if(i===t){let i=e[r+1];if(!i||i.startsWith(`--`))throw Error(`${t} requires a value.`);n.push(...i.split(`,`).filter(Boolean)),r+=1;continue}i.startsWith(`${t}=`)&&n.push(...i.slice(t.length+1).split(`,`).filter(Boolean))}return n}function A(e,t){let n=k(e,t);if(n.length>1)throw Error(`${t} can only be provided once.`);return n[0]}async function j(e){try{return await M(e)}finally{await e.close?.()}}async function M(e){return l(await n(),await u(e))}async function N(e,t){if(t.length===0)return w.log.info(`No pending deploy migrations.`),!1;if(w.note(r(t),`Pending deploy migrations`),e.runMigrations)return!0;if(e.skipMigrations)return!1;if(!process.stdout.isTTY)throw Error(`Pending migrations require --run-migrations or --skip-migrations in noninteractive deploys.`);let n=await w.confirm({initialValue:!0,message:`Run these deploy-safe database migrations before Cloudflare deploy?`});if(w.isCancel(n))throw w.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`);return n}async function P(){let{stdout:e}=await C(`git`,[`status`,`--porcelain`],{stdin:`ignore`});if(e.trim().length>0)throw Error(`Deploy requires a clean git tree.`);let{stdout:t}=await C(`git`,[`rev-parse`,`--short=12`,`HEAD`],{stdin:`ignore`});return t.trim()}function F(e){if(e.runMigrations&&e.skipMigrations)throw Error(`Use only one of --run-migrations or --skip-migrations.`)}function I(e,t){if(!e)throw Error(`Missing ${t} deploy URL. Set DEPLOY_URL or deploy.environments.${t}.url in coreframe.config.ts.`);let n;try{n=new URL(e)}catch{throw Error(`Invalid ${t} deploy URL "${e}". Expected an HTTP(S) URL.`)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw Error(`Invalid ${t} deploy URL "${e}". Expected an HTTP(S) URL.`);return n.toString()}async function L(e){if(e.environment!==`production`||e.yes)return;if(!process.stdout.isTTY)throw Error(`Production deploys require --yes in noninteractive environments.`);let t=await w.confirm({initialValue:!1,message:`Deploy to production?`});if(w.isCancel(t)||!t)throw w.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`)}function R(e,t){if(e===`staging`&&t)throw Error(`Tauri releases can only be published from production deploys.`)}function z(e,t){let n=t.filter(t=>!e[t]);if(n.length>0)throw Error(`Missing required deploy environment variables: ${n.join(`, `)}`);return Object.fromEntries(t.map(t=>[t,e[t]]))}function B(e,t,n,r,i){return{...e,CLOUDFLARE_ENV:t,DEPLOY_ENV:t,POSTHOG_RELEASE:n,POSTHOG_RELEASE_NAME:e.POSTHOG_RELEASE_NAME??r,POSTHOG_RELEASE_VERSION:e.POSTHOG_RELEASE_VERSION??i}}function V(e){let{POSTHOG_API_KEY:t,POSTHOG_PROJECT_ID:n,...r}=e;return r}export{E as deployProject,D as parseDeployArgs,e as readProjectConfig};
package/dist/dev.d.ts CHANGED
@@ -4,7 +4,9 @@ import { CoreframeConfig } from "./config.js";
4
4
  type DevServerOptions = {
5
5
  cwd?: string;
6
6
  env?: NodeJS.ProcessEnv;
7
+ host?: string;
7
8
  https?: boolean;
9
+ noManaged?: boolean;
8
10
  platform?: DevPlatform;
9
11
  webPort?: number;
10
12
  };
@@ -14,7 +16,9 @@ declare function resolveDevPlatforms(config: CoreframeConfig): DevPlatform[];
14
16
  declare function runDevServer({
15
17
  cwd,
16
18
  env,
19
+ host,
17
20
  https,
21
+ noManaged,
18
22
  platform,
19
23
  webPort
20
24
  }?: DevServerOptions): Promise<void>;
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 c from"node:path";import l from"node:process";import{PassThrough as u}from"node:stream";import{supervise as d}from"procband";const f=[`web`,`desktop`,`ios`,`android`];function p(e){let t=e.dev?.platforms??[`web`];for(let e of t)if(!f.includes(e))throw Error(`Invalid development platform "${String(e)}". Expected one of: ${f.join(`, `)}.`);return[...new Set(t)]}const m=c.resolve(`node_modules`,`.bin`),h=l.platform===`win32`?`pnpm.cmd`:`pnpm`,g=l.platform===`win32`?`react-router.cmd`:`react-router`,_=l.platform===`win32`?`turso.cmd`:`turso`;async function v({cwd:f=l.cwd(),env:p=l.env,https:v=!1,platform:C=`web`,webPort:w}={}){let T=p.PATH??p.Path??``,E=C===`web`?w??S(p.COREFRAME_WEB_PORT):void 0,D,O=p;if(v&&C!==`web`)throw Error(`Local HTTPS is only supported for the web development platform.`);try{if(C===`web`){let b=await e({cwd:f}),S=v?await s({cwd:f}):void 0;if(O=S?{...p,[a]:S.cert,[o]:S.key}:p,b.database?.dialect===`sqlite`){let e=await n.getAvailablePort(),r=c.resolve(f,`.coreframe`),i=c.join(r,`turso.db`);await t.mkdir(r,{recursive:!0}),O={...O,TURSO_DATABASE_URL:`http://127.0.0.1:${e}`},D=d({name:`turso`,command:_,args:[`dev`,`--port`,String(e),`--db-file`,i],cwd:f,env:p}),await D.waitFor(`listening`),await d({name:`db`,command:h,args:[`db`,`ensure`],cwd:f,env:O,prefix:!1}).expectSuccess()}if(y(b)){let e=new u,t=d({name:`postgres`,command:l.execPath,args:[`-e`,`process.stdin.pipe(process.stdout)`],cwd:f,stdin:e});try{try{var k=i();let t=k.a(await r({config:b,cwd:f,env:O,postgresOutput:e})).env;await d({name:`db`,command:h,args:[`db`,`ensure`],cwd:f,env:t,prefix:!1}).expectSuccess(),await d({name:`web`,command:g,args:x(E),cwd:f,env:{...t,CLOUDFLARE_ENV:`development`,PATH:m+c.delimiter+T}}).expectSuccess()}catch(e){k.e=e}finally{await k.d()}}finally{e.end(),await t.expectSuccess()}return}await d({name:`web`,command:g,args:x(E),cwd:f,env:{...O,CLOUDFLARE_ENV:`development`,PATH:m+c.delimiter+T}}).expectSuccess();return}let S=b(C);await d({name:C,command:h,args:S,cwd:f,env:p}).expectSuccess()}finally{D&&D.kill(`SIGTERM`)}}function y(e){return(e.database?.dialect??`postgres`)===`postgres`}function b(e){return[`--dir=tauri`,`tauri`,...e===`desktop`?[]:[e],`dev`]}function x(e){return e===void 0?[`dev`]:[`dev`,`--port`,String(e)]}function S(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}import.meta.main&&v({env:{...l.env,FORCE_COLOR:`1`}}).catch(e=>{console.error(e),l.exit(1)});export{f as devPlatforms,p as resolveDevPlatforms,v 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{isPrimaryGitCheckout as c,startManagedDevSession as l}from"./managed-dev.js";import u from"node:path";import d from"node:process";import{PassThrough as f}from"node:stream";import{supervise as p}from"procband";const m=[`web`,`desktop`,`ios`,`android`];function h(e){let t=e.dev?.platforms??[`web`];for(let e of t)if(!m.includes(e))throw Error(`Invalid development platform "${String(e)}". Expected one of: ${m.join(`, `)}.`);return[...new Set(t)]}const g=u.resolve(`node_modules`,`.bin`),_=d.platform===`win32`?`pnpm.cmd`:`pnpm`,v=d.platform===`win32`?`react-router.cmd`:`react-router`,y=d.platform===`win32`?`turso.cmd`:`turso`;async function b({cwd:m=d.cwd(),env:h=d.env,host:b,https:T=!1,noManaged:E=!1,platform:D=`web`,webPort:O}={}){let k=h.PATH??h.Path??``,A=D===`web`?w(O??h.COREFRAME_WEB_PORT):void 0,j,M,N,P=!1,F=h;if(T&&D!==`web`)throw Error(`Local HTTPS is only supported for the web development platform.`);try{if(D===`web`){let g=await e({cwd:m}),v=T?await s({cwd:m}):void 0;if(F=v?{...h,[a]:v.cert,[o]:v.key}:h,!E&&await c(m)&&(M=await l({config:g,cwd:m,env:F,onRestart(){N&&(P=!0,N.kill(`SIGTERM`))}}),F=M.env),g.database?.dialect===`sqlite`){let e=await n.getAvailablePort(),r=u.resolve(m,`.coreframe`),i=u.join(r,`turso.db`);await t.mkdir(r,{recursive:!0}),F={...F,TURSO_DATABASE_URL:`http://127.0.0.1:${e}`},M?.setRuntimeEnv(F),j=p({name:`turso`,command:y,args:[`dev`,`--port`,String(e),`--db-file`,i],cwd:m,env:h}),await j.waitFor(`listening`),await p({name:`db`,command:_,args:[`db`,`ensure`],cwd:m,env:F,prefix:!1}).expectSuccess()}if(x(g)){let e=new f,t=p({name:`postgres`,command:d.execPath,args:[`-e`,`process.stdin.pipe(process.stdout)`],cwd:m,stdin:e});try{try{var I=i();let t=I.a(await r({config:g,cwd:m,env:F,postgresOutput:e})).env;M?.setRuntimeEnv(t),F=t,await p({name:`db`,command:_,args:[`db`,`ensure`],cwd:m,env:t,prefix:!1}).expectSuccess(),await L()}catch(e){I.e=e}finally{await I.d()}}finally{e.end(),await t.expectSuccess()}return}M?.setRuntimeEnv(F),await L();return}let g=S(D);await p({name:D,command:_,args:g,cwd:m,env:h}).expectSuccess()}finally{N?.kill(`SIGTERM`),await M?.close(),j&&j.kill(`SIGTERM`)}async function L(){let e=async()=>{do P=!1,N=p({name:`web`,command:v,args:C({host:b,port:A}),cwd:m,env:{...F,CLOUDFLARE_ENV:`development`,PATH:g+u.delimiter+k}}),await N.wait(),P||await N.expectSuccess(),N=void 0;while(P)};if(!M){await e();return}await Promise.race([e(),M.failure.catch(e=>{throw P=!1,N?.kill(`SIGTERM`),e})])}}function x(e){return(e.database?.dialect??`postgres`)===`postgres`}function S(e){return[`--dir=tauri`,`tauri`,...e===`desktop`?[]:[e],`dev`]}function C({host:e,port:t}){return[`dev`,...e===void 0?[]:[`--host`,e],...t===void 0?[]:[`--port`,String(t)]]}function w(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{m as devPlatforms,h as resolveDevPlatforms,b as runDevServer};
@@ -0,0 +1,45 @@
1
+ //#region managed-dev-protocol.d.ts
2
+ declare const managedDevUrlEnv = "COREFRAME_MANAGED_DEV_URL";
3
+ declare const managedDevTokenEnv = "COREFRAME_MANAGED_DEV_TOKEN";
4
+ type ManagedDevTarget = {
5
+ commit: string;
6
+ shortCommit: string;
7
+ source: "local" | "upstream";
8
+ subject: string;
9
+ };
10
+ type ManagedDevDraft = {
11
+ fingerprint: string;
12
+ tracked: boolean;
13
+ untracked: string[];
14
+ };
15
+ type ManagedDevStatus = {
16
+ busy?: string;
17
+ currentCommit?: string;
18
+ draft?: ManagedDevDraft;
19
+ message?: string;
20
+ mode: "error" | "managed" | "unmanaged" | "waiting";
21
+ proposal?: string;
22
+ target?: ManagedDevTarget;
23
+ warnings: string[];
24
+ };
25
+ type ManagedDevAction = {
26
+ type: "apply";
27
+ fingerprint?: string;
28
+ message?: string;
29
+ target: string;
30
+ untracked?: string[];
31
+ } | {
32
+ type: "message";
33
+ fingerprint: string;
34
+ untracked: string[];
35
+ } | {
36
+ type: "prepare";
37
+ target: string;
38
+ } | {
39
+ type: "reject";
40
+ };
41
+ type ManagedDevActionResult = {
42
+ status: ManagedDevStatus;
43
+ };
44
+ //#endregion
45
+ export { ManagedDevAction, ManagedDevActionResult, ManagedDevDraft, ManagedDevStatus, ManagedDevTarget, managedDevTokenEnv, managedDevUrlEnv };
@@ -0,0 +1 @@
1
+ const e=`COREFRAME_MANAGED_DEV_URL`,t=`COREFRAME_MANAGED_DEV_TOKEN`;export{t as managedDevTokenEnv,e as managedDevUrlEnv};
@@ -0,0 +1,26 @@
1
+ import { CoreframeConfig } from "./config.js";
2
+
3
+ //#region managed-dev.d.ts
4
+ type ManagedDevSessionOptions = {
5
+ config: CoreframeConfig;
6
+ cwd?: string;
7
+ env?: NodeJS.ProcessEnv;
8
+ onRestart?(): void;
9
+ remoteCheckInterval?: number;
10
+ };
11
+ type ManagedDevSession = {
12
+ env: NodeJS.ProcessEnv;
13
+ failure: Promise<never>;
14
+ setRuntimeEnv(env: NodeJS.ProcessEnv): void;
15
+ close(): Promise<void>;
16
+ };
17
+ declare function isPrimaryGitCheckout(cwd?: string): Promise<boolean>;
18
+ declare function startManagedDevSession({
19
+ config,
20
+ cwd,
21
+ env,
22
+ onRestart,
23
+ remoteCheckInterval
24
+ }: ManagedDevSessionOptions): Promise<ManagedDevSession>;
25
+ //#endregion
26
+ export { ManagedDevSession, isPrimaryGitCheckout, startManagedDevSession };
@@ -0,0 +1,2 @@
1
+ import{managedDevTokenEnv as e,managedDevUrlEnv as t}from"./managed-dev-protocol.js";import{createRequire as n}from"node:module";import{createHash as r,randomBytes as i}from"node:crypto";import{copyFile as a,mkdir as o,mkdtemp as s,open as c,readFile as l,realpath as u,rm as d,unlink as f}from"node:fs/promises";import p from"node:path";import m,{SubprocessError as h}from"nano-spawn";import{pathToFileURL as g}from"node:url";import _ from"node:process";import{watch as v}from"node:fs";import{createServer as ee}from"node:http";const y=`refs/heads/main`,te=/^[a-z][a-z0-9-]*(?:\([^)]+\))?!?:\s+\S/i;async function b(e=_.cwd()){return(await x(e)).primary}async function x(e){let t=await u(await H(e,[`rev-parse`,`--show-toplevel`])),n=await u(e),r=p.resolve(t,await H(e,[`rev-parse`,`--git-dir`])),i=p.resolve(t,await H(e,[`rev-parse`,`--git-common-dir`]));return{commonDir:i,gitDir:r,primary:t===n&&r===i}}async function S({config:n,cwd:r=_.cwd(),env:a=_.env,onRestart:o=()=>void 0,remoteCheckInterval:s=6e4}){let c=i(32).toString(`base64url`),l=a,u={mode:`waiting`,warnings:[]},d,f=!1,h=!1,g=!1,v=!1,b=!1,x,S,C,T,E,D=[],O,k,A=new Promise((e,t)=>{k=t}),j=e=>{let t=e instanceof Error?e:Error(String(e));u={...u,busy:void 0,message:t.message,mode:`error`},k(t)},M=async({fetch:e=!1}={})=>{if(!(!h||f||!S)){if(v){b=!0;return}try{if(await H(r,[`rev-parse`,`HEAD`])!==x){await P(`HEAD changed outside Coreframe; managed synchronization is disabled until restart.`);return}if(e){let e=await U(r,[`for-each-ref`,`--format=%(upstream:short)`,y]);if(e){let t=e.indexOf(`/`);if(t>0){let n=e.slice(0,t),i=e.slice(t+1);await V(r,[`fetch`,n,`+refs/heads/${i}:refs/remotes/${n}/${i}`])}}}let t=await H(r,[`rev-parse`,y]),n=await U(r,[`for-each-ref`,`--format=%(upstream:short)`,y]),i=n?await U(r,[`rev-parse`,`refs/remotes/${n}`]):void 0,a=i&&!await W(r,i,t)?i:t;u={...u,currentCommit:x,draft:u.target?.commit===a?u.draft:void 0,message:void 0,mode:`managed`,proposal:u.target?.commit===a?u.proposal:void 0,target:a===x?void 0:await ue(r,a,a===i?`upstream`:`local`)}}catch(e){u={...u,message:J(e),warnings:[J(e)]}}}},N=()=>{clearTimeout(E),E=setTimeout(()=>void M(),50),E.unref()},P=async e=>{h=!1,g=!0,clearInterval(T),clearTimeout(E);for(let e of D)e.close();D=[],await B(C),C=void 0,u={message:e,mode:`unmanaged`,warnings:[]}},F=async()=>{if(h||f||g)return;let e=await re(r);if(!e.primary){u={message:`Managed development only runs from the primary checkout.`,mode:`unmanaged`,warnings:[]};return}if(e.branch&&(e.branch!==`main`||e.status.length>0)){u={message:`Managed development requires a clean primary checkout on main.`,mode:`unmanaged`,warnings:[]};return}if(!e.branch&&!await W(r,e.head,e.main))throw Error(`Detached HEAD ${e.head.slice(0,12)} is not part of local main's history.`);C=p.join(e.commonDir,`coreframe-managed-dev.lock`),await R(C),e.branch===`main`&&await V(r,[`switch`,`--detach`,e.head]),S={...e,branch:void 0},x=e.head,h=!0,u={currentCommit:e.head,mode:`managed`,warnings:[]},D=await ie(e,N),T=setInterval(()=>void M({fetch:!0}),s),T.unref(),await M(),M({fetch:!0})},I=async e=>{if(!h||u.mode!==`managed`)throw Error(`Managed development is not active.`);if(v)throw Error(`Another managed development action is already running.`);v=!0,u={...u,busy:fe(e),message:void 0};try{if(e.type===`reject`)u={...u,draft:void 0,proposal:void 0};else if(e.type===`prepare`){z(e.target);let t=await w(r);t.tracked||t.untracked.length>0?u={...u,draft:t,proposal:Y(t.fingerprint)}:await Q({target:e.target})}else if(e.type===`message`){let t=await Z(e.fingerprint,e.untracked),n=Y(t.fingerprint);if(n===void 0){try{n=await ae(r,l,t.context)}catch(e){n=``,u={...u,warnings:[...u.warnings,`Codex could not generate a commit message: ${J(e)}`]}}O={fingerprint:t.fingerprint,message:n}}u={...u,draft:t,proposal:n}}else{z(e.target);let t=await w(r);if(t.tracked||t.untracked.length>0){if(!e.fingerprint||!e.untracked)throw Error(`Refresh requires the current working-tree confirmation.`);if(await Z(e.fingerprint,e.untracked),!e.message||!te.test(de(e.message)))throw Error(`Enter a commit message with a valid Conventional Commits header.`)}await Q({fingerprint:e.fingerprint,message:e.message,target:e.target,untracked:e.untracked})}}finally{v=!1,u={...u,busy:void 0},b&&(b=!1,N())}return{status:u}},z=e=>{if(!u.target||u.target.commit!==e)throw Error(`The offered update changed. Review the latest target before refreshing.`)},Y=e=>O?.fingerprint===e?O.message:void 0,Z=async(e,t)=>{let n=await w(r);if(n.fingerprint!==e)throw O=void 0,Error(`Working-tree changes changed; generate a new commit message.`);if(!G(n.untracked,t))throw Error(`Include every untracked file or remove it before refreshing.`);return n},Q=async({fingerprint:e,message:t,target:i,untracked:a=[]})=>{z(i);let s=await H(r,[`rev-parse`,`HEAD`]),c=await H(r,[`rev-parse`,y]),d,f,p=await w(r);if(p.tracked||p.untracked.length>0){if(!e||p.fingerprint!==e||!G(p.untracked,a))throw Error(`Working-tree changes changed; review them before refreshing.`);await V(r,[`add`,`--update`]),a.length>0&&await V(r,[`add`,`--`,...a]),await V(r,[`commit`,`--message`,t],{env:{GIT_EDITOR:`true`}}),d=await H(r,[`rev-parse`,`HEAD`]),x=d,f=`refs/coreframe/recovery/${Date.now()}-${d.slice(0,12)}`,await V(r,[`update-ref`,f,d])}let h=d??s,g;try{if(g=await oe({cwd:r,developerCommit:d,env:l,localMain:c,target:i}),await H(r,[`rev-parse`,`HEAD`])!==h)throw Error(`HEAD changed while the accepted update was being prepared.`);if(await H(r,[`rev-parse`,y])!==c)throw Error(`Local main changed while the accepted update was being prepared.`);await V(r,[`switch`,`--detach`,g.commit]);try{await V(r,[`update-ref`,y,g.commit,c])}catch(e){throw await V(r,[`switch`,`--detach`,h]),e}x=g.commit;let e=K(await H(r,[`diff`,`--name-only`,`-z`,s,g.commit]));L(e)&&await m(q(),[`install`,`--frozen-lockfile`],{cwd:r,env:l,stdio:`inherit`}),g.hadConflicts&&(u={...u,warnings:[...u.warnings,...await se(r,l)]});try{await ce({config:n,cwd:r,env:l})}catch(e){throw await V(r,[`switch`,`--detach`,h]),x=h,u={...u,currentCommit:h},Error(`Database migration failed; the database and preview were restored. ${J(e)}`)}le(e)&&o(),O=void 0,f&&await V(r,[`update-ref`,`-d`,f]),u={...u,currentCommit:g.commit,draft:void 0,proposal:void 0,target:void 0}}catch(e){g?.commit&&x===g.commit&&(await V(r,[`switch`,`--detach`,h]).catch(()=>void 0),x=h);let t=f?` Recovery commit: ${f}.`:``;throw u={...u,currentCommit:x,message:`${J(e)}${t}`},e}};d=ee((e,t)=>{ne(e,t,{action:I,activate:F,getStatus:()=>u,token:c}).catch(n=>{t.headersSent||X(t,400,{error:J(n),status:u}),e.url===`/handshake`&&j(n)})}),await pe(d);let $=d.address();if(!$||typeof $==`string`)throw Error(`Managed dev server failed to listen.`);return{env:{...a,[t]:`http://127.0.0.1:${$.port}`,[e]:c},failure:A,setRuntimeEnv(e){l=e},async close(){f=!0,clearInterval(T),clearTimeout(E);for(let e of D)e.close();await B(C),await me(d)}}}async function ne(e,t,n){if(e.headers.authorization!==`Bearer ${n.token}`){X(t,401,{error:`Invalid managed development capability.`});return}if(e.method===`POST`&&e.url===`/handshake`){await n.activate(),X(t,200,{status:n.getStatus()});return}if(e.method===`GET`&&e.url===`/status`){X(t,200,{status:n.getStatus()});return}if(e.method===`POST`&&e.url===`/action`){let r=await Y(e);X(t,200,await n.action(r));return}X(t,404,{error:`Unknown managed development endpoint.`})}async function re(e){let t=await x(e);return{branch:C(await H(e,[`rev-parse`,`--abbrev-ref`,`HEAD`])),commonDir:t.commonDir,gitDir:t.gitDir,head:await H(e,[`rev-parse`,`HEAD`]),main:await H(e,[`rev-parse`,y]),primary:t.primary,status:await H(e,[`status`,`--porcelain`])}}function C(e){return e===`HEAD`?void 0:e}async function ie(e,t){let n=[e.gitDir,p.join(e.commonDir,`refs`,`heads`)],r=[];for(let e of n)try{r.push(v(e,{persistent:!1},t))}catch{}return r}async function w(e){let[t,n,i]=await Promise.all([H(e,[`status`,`--porcelain`,`--untracked-files=no`]),H(e,[`diff`,`HEAD`,`--binary`]),H(e,[`ls-files`,`--others`,`--exclude-standard`,`-z`])]),a=K(i).sort(),o=r(`sha256`).update(n),s=[`Tracked changes:`,n];for(let t of a){let n=await l(p.join(e,t));o.update(t).update(n),s.push(`\nUntracked file: ${t}\n`,n.toString(`utf8`))}return{context:s.join(`
2
+ `),fingerprint:o.digest(`hex`),tracked:t.length>0,untracked:a}}async function ae(e,t,n){let{stdout:r}=await m(`codex`,[`exec`,`--ephemeral`,`--ignore-user-config`,`--sandbox`,`read-only`,`--cd`,e,`Return only a concise Conventional Commits message for the supplied working-tree changes. Use a body only when it adds material context.`],{cwd:e,env:t,stdin:{string:n}});return r.trim().replace(/^```(?:text)?\s*|\s*```$/g,``)}async function oe({cwd:e,developerCommit:t,env:n,localMain:r,target:a}){let s=p.resolve(e,await H(e,[`rev-parse`,`--git-common-dir`])),c=p.join(s,`coreframe-managed-dev`);await o(c,{recursive:!0});let l=p.join(c,`integration-${i(8).toString(`hex`)}`),u=!1;try{if(await V(e,[`worktree`,`add`,`--detach`,l,r]),r!==a){let e=await U(l,[`merge-base`,r,a]);u=await T(l,n,e?[`rebase`,`--onto`,a,e]:[`rebase`,`--root`,`--onto`,a],`rebase`)||u}return t&&(u=await T(l,n,[`cherry-pick`,t],`cherry-pick`)||u),{commit:await H(l,[`rev-parse`,`HEAD`]),hadConflicts:u}}finally{await V(e,[`worktree`,`remove`,`--force`,l]).catch(()=>void 0),await d(l,{force:!0,recursive:!0})}}async function T(e,t,n,r){try{return await V(e,n,{env:{GIT_EDITOR:`true`}}),!1}catch(n){let i=await D(e);if(i.length===0)throw n;console.warn(`Git ${r} conflicts occurred; Codex is resolving them.`);try{for(;i.length>0;){await E(e,t,r,i),await V(e,[`diff`,`--check`]),await V(e,[`add`,`--all`]);let n=await D(e);if(n.length>0)throw Error(`Codex left unresolved paths: ${n.join(`, `)}`);try{await V(e,[r,`--continue`],{env:{GIT_EDITOR:`true`}})}catch(t){if(i=await D(e),i.length===0)throw t;continue}i=await D(e)}return!0}catch(t){throw await V(e,[r,`--abort`]).catch(()=>void 0),Error(`Codex could not resolve ${r} conflicts: ${J(t)}`)}}}async function E(e,t,n,r){await m(`codex`,[`exec`,`--ephemeral`,`--ignore-user-config`,`--sandbox`,`workspace-write`,`--cd`,e,`Resolve the current Git ${n} conflicts cleanly. Edit only files needed for the resolution, preserve intended behavior from both sides, remove all conflict markers, and do not stage files or run Git continuation commands. Conflicted paths: ${r.join(`, `)}`],{cwd:e,env:t,stdio:`inherit`})}async function D(e){return K(await H(e,[`diff`,`--name-only`,`--diff-filter=U`,`-z`]))}async function se(e,t){let n=[];for(let r of[`test`,`typecheck`,`lint`])try{await m(q(),[r],{cwd:e,env:t,stdio:`inherit`})}catch(e){n.push(`${r} failed after Codex conflict resolution: ${J(e)}`)}return n}async function ce({config:e,cwd:t,env:n}){if(!e.database)return;let r=await O({config:e,cwd:t,env:n});try{await m(q(),[`db`,`migrate`],{cwd:t,env:n,stdio:`inherit`})}catch(e){throw await r.restore(),e}await r.discard().catch(e=>{console.warn(`Could not remove the managed-development database snapshot: ${J(e)}`)}),await d(r.directory,{force:!0,recursive:!0})}async function O({config:e,cwd:t,env:n}){let r=p.join(t,`.coreframe`,`managed-dev-backups`);await o(r,{recursive:!0});let i=await s(p.join(r,`refresh-`));if(e.database?.dialect===`sqlite`){let e=[`turso.db`,`turso.db-wal`,`turso.db-shm`],n=new Set;for(let r of e)await k(p.join(t,`.coreframe`,r),p.join(i,r))&&n.add(r);return{directory:i,async discard(){},async restore(){for(let r of e){let e=p.join(t,`.coreframe`,r);n.has(r)?await a(p.join(i,r),e):await d(e,{force:!0})}}}}if(!n.DATABASE_URL)throw Error(`DATABASE_URL is required to snapshot local PostgreSQL.`);let c=await A(t);if(c)return j({directory:i,connectionString:n.DATABASE_URL,postgres:c});let l=p.join(i,`database.dump`),u=I(n,n.DATABASE_URL);return await m(`pg_dump`,[`--format=custom`,`--file`,l],{cwd:t,env:u,stdin:`ignore`}),{directory:i,async discard(){},async restore(){await m(`pg_restore`,[`--clean`,`--if-exists`,`--no-owner`,`--dbname`,u.PGDATABASE,l],{cwd:t,env:u,stdin:`ignore`})}}}async function k(e,t){try{return await a(e,t),!0}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`ENOENT`))throw e;return!1}}async function A(e){let t=JSON.parse(await l(p.join(e,`package.json`),`utf8`));if(!(!t.dependencies?.pg&&!t.devDependencies?.pg&&!t.optionalDependencies?.pg))try{return await import(g(n(p.join(e,`package.json`)).resolve(`pg`)).href)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`MODULE_NOT_FOUND`)return;throw e}}async function j({directory:e,connectionString:t,postgres:n}){let r=new URL(t),a=decodeURIComponent(r.pathname.slice(1));if(!a)throw Error(`DATABASE_URL must name a PostgreSQL database to snapshot.`);let o=`coreframe_backup_${i(6).toString(`hex`)}`,s=`${o}_failed`,c=new URL(r);c.pathname=a===`postgres`?`/template1`:`/postgres`;let l=new n.Client({connectionString:c.href});await l.connect();try{await N(l,a,!1),await P(l,a),await l.query(`CREATE DATABASE ${F(o)} WITH TEMPLATE ${F(a)}`),await N(l,o,!0)}finally{await N(l,a,!0).catch(()=>void 0),await l.end()}return{directory:e,async discard(){await M(n,c.href,async e=>{await P(e,o),await e.query(`DROP DATABASE ${F(o)}`)})},async restore(){await M(n,c.href,async e=>{await N(e,a,!1),await P(e,a),await e.query(`ALTER DATABASE ${F(a)} RENAME TO ${F(s)}`);try{await e.query(`ALTER DATABASE ${F(o)} RENAME TO ${F(a)}`)}catch(t){throw await e.query(`ALTER DATABASE ${F(s)} RENAME TO ${F(a)}`).catch(()=>void 0),await N(e,a,!0).catch(()=>void 0),t}await P(e,s),await e.query(`DROP DATABASE ${F(s)}`)})}}}async function M(e,t,n){let r=new e.Client({connectionString:t});await r.connect();try{return await n(r)}finally{await r.end()}}async function N(e,t,n){await e.query(`ALTER DATABASE ${F(t)} ALLOW_CONNECTIONS ${n?`true`:`false`}`)}async function P(e,t){await e.query(`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()`,[t])}function F(e){return`"${e.replaceAll(`"`,`""`)}"`}function I(e,t){let n=new URL(t);return{...e,PGDATABASE:decodeURIComponent(n.pathname.slice(1)),PGHOST:n.hostname,PGPASSWORD:decodeURIComponent(n.password),PGPORT:n.port||`5432`,PGUSER:decodeURIComponent(n.username)}}function L(e){return e.some(e=>p.basename(e)===`package.json`||p.basename(e).endsWith(`lock.yaml`)||p.basename(e)===`pnpm-workspace.yaml`)}function le(e){return e.some(e=>L([e])||/(^|\/)(vite|coreframe|tsconfig|drizzle)\.[^/]+$/.test(e)||/(^|\/)(drizzle|scripts|src\/api|tauri)(\/|$)/.test(e))}async function ue(e,t,n){return{commit:t,shortCommit:t.slice(0,12),source:n,subject:await H(e,[`show`,`-s`,`--format=%s`,t])}}async function R(e){try{let t=await c(e,`wx`);await t.writeFile(`${_.pid}\n`),await t.close()}catch(t){if(!(t instanceof Error&&`code`in t&&t.code===`EEXIST`))throw t;let n=(await l(e,`utf8`).catch(()=>`unknown`)).trim(),r=Number(n);if(Number.isInteger(r)&&!z(r)){await f(e).catch(()=>void 0),await R(e);return}throw Error(`Managed development is already owned by PID ${n}.`)}}function z(e){try{return _.kill(e,0),!0}catch(e){return!(e instanceof Error&&`code`in e&&e.code===`ESRCH`)}}async function B(e){e&&(await l(e,`utf8`).catch(()=>void 0))?.trim()===String(_.pid)&&await f(e).catch(()=>void 0)}async function V(e,t,{env:n}={}){return m(`git`,t,{cwd:e,env:n,stdin:`ignore`})}async function H(e,t){return(await V(e,t)).stdout.trim()}async function U(e,t){try{return await H(e,t)||void 0}catch(e){if(e instanceof h&&e.exitCode&&e.exitCode>0)return;throw e}}async function W(e,t,n){try{return await V(e,[`merge-base`,`--is-ancestor`,t,n]),!0}catch(e){if(e instanceof h&&e.exitCode===1)return!1;throw e}}function G(e,t){return e.length===t.length&&[...e].sort().every((e,n)=>e===[...t].sort()[n])}function K(e){return e.split(`\0`).filter(Boolean)}function de(e){return e.split(/\r?\n/,1)[0]??``}function fe(e){return e.type===`message`?`Generating commit message`:e.type===`apply`?`Integrating accepted update`:e.type===`prepare`?`Reviewing working tree`:`Aborting refresh`}function q(){return _.platform===`win32`?`pnpm.cmd`:`pnpm`}function J(e){return e instanceof Error?e.message:String(e)}async function Y(e){let t=``;for await(let n of e)if(t+=String(n),t.length>1e6)throw Error(`Managed development request is too large.`);return t?JSON.parse(t):{}}function X(e,t,n){let r=JSON.stringify(n);e.statusCode=t,e.setHeader(`Content-Type`,`application/json`),e.setHeader(`Content-Length`,Buffer.byteLength(r)),e.end(r)}async function pe(e){await new Promise((t,n)=>{e.once(`error`,n),e.listen(0,`127.0.0.1`,t)})}async function me(e){e.listening&&await new Promise((t,n)=>{e.close(e=>e?n(e):t())})}export{b as isPrimaryGitCheckout,S as startManagedDevSession};
@@ -0,0 +1,17 @@
1
+ import * as cmdts from "cmd-ts";
2
+
3
+ //#region runner.d.ts
4
+ type CliErrorReporterOptions = {
5
+ temporaryDirectory?: string;
6
+ write?: (message: string) => void;
7
+ };
8
+ type Command = Parameters<typeof cmdts.binary>[0];
9
+ type RunFunction = () => unknown | PromiseLike<unknown>;
10
+ declare function reportCliError(error: unknown, {
11
+ temporaryDirectory,
12
+ write
13
+ }?: CliErrorReporterOptions): string | undefined;
14
+ declare function run(command: Command): void;
15
+ declare function run(fn: RunFunction): void;
16
+ //#endregion
17
+ export { reportCliError, run };
package/dist/runner.js ADDED
@@ -0,0 +1 @@
1
+ import{join as e}from"node:path";import*as t from"cmd-ts";import{mkdtempSync as n,writeFileSync as r}from"node:fs";import{tmpdir as i}from"node:os";import{inspect as a}from"node:util";function o(t,{temporaryDirectory:a=i(),write:o=e=>console.error(e)}={}){let s=c(t);try{let i=e(n(e(a,`coreframe-error-`)),`error.log`);return r(i,`${l(t)}\n`,{mode:384}),o(`Error: ${s}\nFull diagnostic: ${i}`),i}catch{o(`Error: ${s}\nThe full diagnostic could not be written to the temporary directory.`);return}}function s(e){let n=typeof e==`function`?e:()=>t.run(t.binary(e),process.argv),r=!1,i=e=>{r||(r=!0,o(e),process.exit(1))};process.once(`uncaughtException`,i),process.once(`unhandledRejection`,i),Promise.resolve().then(n).catch(i)}function c(e){try{return(e instanceof Error?e.message:String(e)).split(/\r?\n/,1)[0]||`Unknown error`}catch{return`Unknown error`}}function l(e){return e instanceof Error?e.stack??`${e.name}: ${e.message}`:a(e,{depth:10})}export{o as reportCliError,s as run};
package/dist/typecheck.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{access as e}from"node:fs/promises";import t from"node:path";import n from"node:process";import{supervise as r}from"procband";const i=t.resolve(`node_modules`,`.bin`);n.env.PATH=i+t.delimiter+n.env.PATH;const a=n.platform===`win32`?`tsc.cmd`:`tsc`,o=[{name:`scripts`,path:`scripts/tsconfig.json`},{name:`shared`,path:`src/shared/tsconfig.json`},{name:`api`,path:`src/api/tsconfig.json`},{name:`worker`,path:`src/api/worker/tsconfig.json`},{name:`web`,path:`src/web/tsconfig.json`}];async function s(){let t=[];for(let n of o)try{await e(n.path),t.push(n)}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`ENOENT`))throw e}let i=t.map(e=>r({name:e.name,command:a,args:[`-p`,e.path]})),s=(await Promise.all(i.map(e=>e.wait()))).filter(e=>e.exitCode!==0);s.length>0&&(n.exitCode=s[0].exitCode)}import.meta.main&&s().catch(e=>{console.error(e),n.exit(1)});export{s as typecheckProject};
2
+ import{access as e}from"node:fs/promises";import t from"node:path";import n from"node:process";import{supervise as r}from"procband";const i=t.resolve(`node_modules`,`.bin`);n.env.PATH=i+t.delimiter+n.env.PATH;const a=n.platform===`win32`?`tsc.cmd`:`tsc`,o=[{name:`scripts`,path:`scripts/tsconfig.json`},{name:`shared`,path:`src/shared/tsconfig.json`},{name:`api`,path:`src/api/tsconfig.json`},{name:`worker`,path:`src/api/worker/tsconfig.json`},{name:`web`,path:`src/web/tsconfig.json`}];async function s(){let t=[];for(let n of o)try{await e(n.path),t.push(n)}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`ENOENT`))throw e}let i=t.map(e=>r({name:e.name,command:a,args:[`-p`,e.path]})),s=(await Promise.all(i.map(e=>e.wait()))).filter(e=>e.exitCode!==0);s.length>0&&(n.exitCode=s[0].exitCode)}export{s as typecheckProject};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{coreframeGitRef as e,coreframeTemplateFile as t}from"./template-ref.js";import{join as n}from"node:path";import{existsSync as r,mkdtempSync as i,readFileSync as a,rmSync as o,writeFileSync as s}from"node:fs";import{tmpdir as c}from"node:os";import{spawnSync as l}from"node:child_process";const u=`.agents/skills/coreframe-upgrade/SKILL.md`;function d(e,t={}){return l(`git`,e,t)}function f(e){return e.stderr.toString(`utf8`).trim()||e.stdout.toString(`utf8`).trim()}function p(e,n){let r=t(e,n),i=d([`show`,r],{encoding:`utf8`});if(i.status!==0){let e=f(i);throw Error(`Could not read ${r}.${e?` ${e}`:``}`)}return i.stdout.toString()}function m(){let e=d([`rev-parse`,`--show-toplevel`],{encoding:`utf8`});if(e.status!==0){let t=f(e);throw Error(`Could not find git repository root.${t?` ${t}`:``}`)}return e.stdout.toString().trim()}function h(e,t){let r=i(n(c(),`coreframe-upgrade-skill-`)),a=n(r,`SKILL.md`);try{s(a,t);let n=f(d([`--no-pager`,`diff`,`--no-index`,`--color=never`,`--`,e,a]));n&&console.log(n)}finally{o(r,{force:!0,recursive:!0})}}function g({apply:t,target:i}){let o=n(m(),u),c=e(i);if(!r(o))throw Error(`Local ${u} does not exist.`);let l=a(o,`utf8`),d=p(i,u);return l===d?(console.log(`Coreframe upgrade skill matches ${c}:template/${u}.`),0):t?(s(o,d),console.error(`Updated ${u} from ${c}:template. Reread it before continuing the upgrade.`),20):(console.error(`Coreframe upgrade skill differs from ${c}:template/${u}.`),console.error(`Run again with --apply to update it, then reread ${u} before continuing.`),h(o,d),20)}if(import.meta.main){let e=process.argv.indexOf(`--target`),t=e===-1?null:process.argv[e+1];t||(console.error(`Usage: coreframe upgrade check-skill-current --target <git-ref> [--apply]`),process.exit(64));try{process.exit(g({apply:process.argv.includes(`--apply`),target:t}))}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exit(65)}}export{g as checkUpgradeSkillCurrent};
2
+ import{coreframeGitRef as e,coreframeTemplateFile as t}from"./template-ref.js";import{join as n}from"node:path";import{existsSync as r,mkdtempSync as i,readFileSync as a,rmSync as o,writeFileSync as s}from"node:fs";import{tmpdir as c}from"node:os";import{spawnSync as l}from"node:child_process";const u=`.agents/skills/coreframe-upgrade/SKILL.md`;function d(e,t={}){return l(`git`,e,t)}function f(e){return e.stderr.toString(`utf8`).trim()||e.stdout.toString(`utf8`).trim()}function p(e,n){let r=t(e,n),i=d([`show`,r],{encoding:`utf8`});if(i.status!==0){let e=f(i);throw Error(`Could not read ${r}.${e?` ${e}`:``}`)}return i.stdout.toString()}function m(){let e=d([`rev-parse`,`--show-toplevel`],{encoding:`utf8`});if(e.status!==0){let t=f(e);throw Error(`Could not find git repository root.${t?` ${t}`:``}`)}return e.stdout.toString().trim()}function h(e,t){let r=i(n(c(),`coreframe-upgrade-skill-`)),a=n(r,`SKILL.md`);try{s(a,t);let n=f(d([`--no-pager`,`diff`,`--no-index`,`--color=never`,`--`,e,a]));n&&console.log(n)}finally{o(r,{force:!0,recursive:!0})}}function g({apply:t,target:i}){let o=n(m(),u),c=e(i);if(!r(o))throw Error(`Local ${u} does not exist.`);let l=a(o,`utf8`),d=p(i,u);return l===d?(console.log(`Coreframe upgrade skill matches ${c}:template/${u}.`),0):t?(s(o,d),console.error(`Updated ${u} from ${c}:template. Reread it before continuing the upgrade.`),20):(console.error(`Coreframe upgrade skill differs from ${c}:template/${u}.`),console.error(`Run again with --apply to update it, then reread ${u} before continuing.`),h(o,d),20)}export{g as checkUpgradeSkillCurrent};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{spawnSync as e}from"node:child_process";const t=`https://github.com/airlock-labs/coreframe.git`;function n(t){return e(`git`,t,{encoding:`utf8`})}function r(e){let t=n(e);if(t.status!==0){let n=t.stderr.trim()||t.stdout.trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout}function i(e){let t=n([`remote`,`get-url`,e]);if(t.status===2)return null;if(t.status!==0){let n=t.stderr.trim()||t.stdout.trim();throw Error(`Could not inspect remote ${e}.${n?`\n${n}`:``}`)}return t.stdout.trim()}function a({fetch:e=!0,remote:n=`template`}={}){if(n.length===0)throw Error(`Remote name must not be empty.`);r([`rev-parse`,`--show-toplevel`]);let a=i(n);if(a===null)r([`remote`,`add`,n,t]),console.log(`Added ${n} remote: ${t}`);else if(a===t)console.log(`${n} remote already points at ${t}`);else throw Error(`${n} remote points at ${a}, not ${t}.\nChoose the intended template source before continuing, then update or rename the remote manually.`);e&&(r([`fetch`,n,`--tags`,`--prune`]),console.log(`Fetched ${n}.`))}if(import.meta.main)try{a({fetch:!process.argv.includes(`--no-fetch`)})}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exit(65)}export{a as ensureTemplateRemote};
2
+ import{spawnSync as e}from"node:child_process";const t=`https://github.com/airlock-labs/coreframe.git`;function n(t){return e(`git`,t,{encoding:`utf8`})}function r(e){let t=n(e);if(t.status!==0){let n=t.stderr.trim()||t.stdout.trim();throw Error(`Git command failed: git ${e.join(` `)}${n?`\n${n}`:``}`)}return t.stdout}function i(e){let t=n([`remote`,`get-url`,e]);if(t.status===2)return null;if(t.status!==0){let n=t.stderr.trim()||t.stdout.trim();throw Error(`Could not inspect remote ${e}.${n?`\n${n}`:``}`)}return t.stdout.trim()}function a({fetch:e=!0,remote:n=`template`}={}){if(n.length===0)throw Error(`Remote name must not be empty.`);r([`rev-parse`,`--show-toplevel`]);let a=i(n);if(a===null)r([`remote`,`add`,n,t]),console.log(`Added ${n} remote: ${t}`);else if(a===t)console.log(`${n} remote already points at ${t}`);else throw Error(`${n} remote points at ${a}, not ${t}.\nChoose the intended template source before continuing, then update or rename the remote manually.`);e&&(r([`fetch`,n,`--tags`,`--prune`]),console.log(`Fetched ${n}.`))}export{a as ensureTemplateRemote};
@@ -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=[`drizzle/`,`drizzle-hold/`],_=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.some(t=>e===t.slice(0,-1)||e.startsWith(t))}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){if(j(e.path)){s.push({action:`ignored`,path:e.path,reason:`project-owned migration history`});continue}let t=T(r,e.path),a=T(i,e.path),u=F(t,a);if(!A(t)||!A(a)){c.push({action:u,path:e.path,reason:`unsupported template entry type`});continue}if(M(n,e.path,a).matches){l.push({action:`unchanged`,path:e.path});continue}let d=M(n,e.path,t);if(!d.matches){c.push({action:u,path:e.path,reason:d.reason??`downstream differs from base template`});continue}o.push({action:u,path:e.path,targetEntry:a})}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 project-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).`))}if(import.meta.main){let e=process.argv.indexOf(`--target`),t=e===-1?null:process.argv[e+1];t||(console.error(`Usage: coreframe upgrade fast-forward-template-files --target <git-ref> [--base <git-ref>] [--apply]`),process.exit(64));try{let e=process.argv.indexOf(`--base`);L({apply:process.argv.includes(`--apply`),base:e===-1?void 0:process.argv[e+1],target:t})}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exit(65)}}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===`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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreframe/scripts",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/airlock-labs/coreframe.git",