@coreframe/scripts 0.1.34 → 0.1.35
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/contracts.d.ts +88 -0
- package/dist/assets/contracts.js +1 -0
- package/dist/assets/credentials.d.ts +35 -0
- package/dist/assets/credentials.js +1 -0
- package/dist/assets/images.d.ts +32 -0
- package/dist/assets/images.js +1 -0
- package/dist/assets/project.d.ts +32 -0
- package/dist/assets/project.js +3 -0
- package/dist/assets/storage.d.ts +51 -0
- package/dist/assets/storage.js +1 -0
- package/dist/assets/workflows.d.ts +52 -0
- package/dist/assets/workflows.js +1 -0
- package/dist/assets.d.ts +4 -118
- package/dist/assets.js +1 -3
- package/dist/config.d.ts +11 -1
- package/dist/coreframe.d.ts +34 -4
- package/dist/coreframe.js +2 -2
- package/dist/db/migrate-held.js +1 -1
- package/dist/deploy/access-token-secret.d.ts +39 -0
- package/dist/deploy/access-token-secret.js +1 -0
- package/dist/deploy/tauri-release.js +1 -1
- package/dist/deploy.js +2 -2
- package/dist/dev-lock.js +1 -1
- package/dist/environment.d.ts +1 -1
- package/dist/environment.js +1 -1
- package/dist/managed-dev.js +4 -4
- package/dist/runner.js +1 -1
- package/dist/tauri-dev-output.js +1 -1
- package/dist/upgrade/check-skill-current.js +1 -1
- package/dist/worker-environment.js +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//#region assets/contracts.d.ts
|
|
2
|
+
declare const catalogSchemaVersion: 1;
|
|
3
|
+
declare const manifestSchemaVersion: 1;
|
|
4
|
+
type AssetCatalog = {
|
|
5
|
+
schemaVersion: typeof catalogSchemaVersion;
|
|
6
|
+
assets: Record<string, AssetCatalogEntry>;
|
|
7
|
+
};
|
|
8
|
+
type AssetCatalogEntry = {
|
|
9
|
+
source: AssetSource;
|
|
10
|
+
alt: string;
|
|
11
|
+
variantWidths?: number[];
|
|
12
|
+
};
|
|
13
|
+
type AssetSource = {
|
|
14
|
+
digest: string;
|
|
15
|
+
filename: string;
|
|
16
|
+
mediaType: string;
|
|
17
|
+
size: number;
|
|
18
|
+
};
|
|
19
|
+
type AssetManifest = {
|
|
20
|
+
schemaVersion: typeof manifestSchemaVersion;
|
|
21
|
+
assets: Record<string, AssetManifestEntry>;
|
|
22
|
+
};
|
|
23
|
+
type AssetManifestEntry = AssetCatalogEntry & {
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
variants: AssetVariant[];
|
|
27
|
+
};
|
|
28
|
+
type AssetVariant = {
|
|
29
|
+
format: "webp";
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
path: string;
|
|
33
|
+
size: number;
|
|
34
|
+
hash: string;
|
|
35
|
+
};
|
|
36
|
+
type AssetProblem = {
|
|
37
|
+
code: "CATALOG_MISSING" | "GENERATED_VARIANT_MISSING" | "INVALID_ALT" | "INVALID_ASSET_ID" | "INVALID_CATALOG" | "INVALID_MANIFEST" | "INVALID_REFERENCE" | "INVALID_SOURCE" | "INVALID_VARIANT_PATH" | "INVALID_VARIANT_WIDTHS" | "MANIFEST_MISSING" | "MANIFEST_STALE" | "REFERENCE_MISSING" | "SOURCE_UNAVAILABLE";
|
|
38
|
+
message: string;
|
|
39
|
+
assetId?: string;
|
|
40
|
+
file?: string;
|
|
41
|
+
fix?: string;
|
|
42
|
+
};
|
|
43
|
+
type AssetStatus = {
|
|
44
|
+
ok: boolean;
|
|
45
|
+
assets: AssetStatusEntry[];
|
|
46
|
+
problems: AssetProblem[];
|
|
47
|
+
};
|
|
48
|
+
type AssetStatusEntry = {
|
|
49
|
+
assetId: string;
|
|
50
|
+
source: AssetSource;
|
|
51
|
+
variants: AssetVariantStatus[];
|
|
52
|
+
};
|
|
53
|
+
type AssetVariantStatus = AssetVariant & {
|
|
54
|
+
exists: boolean;
|
|
55
|
+
};
|
|
56
|
+
type AssetBuildResult = {
|
|
57
|
+
ok: boolean;
|
|
58
|
+
assets: AssetBuildEntry[];
|
|
59
|
+
fetched: string[];
|
|
60
|
+
problems: AssetProblem[];
|
|
61
|
+
};
|
|
62
|
+
type AssetBuildEntry = {
|
|
63
|
+
assetId: string;
|
|
64
|
+
variants: AssetVariant[];
|
|
65
|
+
};
|
|
66
|
+
type AssetPublishResult = {
|
|
67
|
+
ok: boolean;
|
|
68
|
+
uploaded: PublishedAsset[];
|
|
69
|
+
skipped: PublishedAsset[];
|
|
70
|
+
reportPath: string;
|
|
71
|
+
problems: AssetProblem[];
|
|
72
|
+
};
|
|
73
|
+
type PublishedAsset = {
|
|
74
|
+
assetId: string;
|
|
75
|
+
source: string;
|
|
76
|
+
target: string;
|
|
77
|
+
size: number;
|
|
78
|
+
};
|
|
79
|
+
type LoadedAssetCatalog = {
|
|
80
|
+
catalog: AssetCatalog;
|
|
81
|
+
problems: AssetProblem[];
|
|
82
|
+
};
|
|
83
|
+
type LoadedAssetManifest = {
|
|
84
|
+
manifest: AssetManifest;
|
|
85
|
+
problems: AssetProblem[];
|
|
86
|
+
};
|
|
87
|
+
//#endregion
|
|
88
|
+
export { AssetBuildEntry, AssetBuildResult, AssetCatalog, AssetCatalogEntry, AssetManifest, AssetManifestEntry, AssetProblem, AssetPublishResult, AssetSource, AssetStatus, AssetStatusEntry, AssetVariant, AssetVariantStatus, LoadedAssetCatalog, LoadedAssetManifest, PublishedAsset, catalogSchemaVersion, manifestSchemaVersion };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=1,t=1;export{e as catalogSchemaVersion,t as manifestSchemaVersion};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region assets/credentials.d.ts
|
|
2
|
+
declare const assetCredentialEnvironment: {
|
|
3
|
+
readonly applicationKey: "COREFRAME_ASSETS_B2_APPLICATION_KEY";
|
|
4
|
+
readonly applicationKeyId: "COREFRAME_ASSETS_B2_APPLICATION_KEY_ID";
|
|
5
|
+
};
|
|
6
|
+
type AssetCredentials = {
|
|
7
|
+
applicationKey: string;
|
|
8
|
+
applicationKeyId: string;
|
|
9
|
+
};
|
|
10
|
+
type AssetCredentialStore = {
|
|
11
|
+
get(profile: string): Promise<AssetCredentials | undefined>;
|
|
12
|
+
set(profile: string, credentials: AssetCredentials): Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
declare function systemAssetCredentialStore(): AssetCredentialStore;
|
|
15
|
+
declare function resolveAssetCredentials({
|
|
16
|
+
env,
|
|
17
|
+
profile,
|
|
18
|
+
store
|
|
19
|
+
}: {
|
|
20
|
+
env?: NodeJS.ProcessEnv;
|
|
21
|
+
profile: string;
|
|
22
|
+
store?: AssetCredentialStore;
|
|
23
|
+
}): Promise<AssetCredentials>;
|
|
24
|
+
declare function configureAssetCredentials({
|
|
25
|
+
applicationKey,
|
|
26
|
+
applicationKeyId,
|
|
27
|
+
profile,
|
|
28
|
+
store
|
|
29
|
+
}: AssetCredentials & {
|
|
30
|
+
profile: string;
|
|
31
|
+
store?: AssetCredentialStore;
|
|
32
|
+
}): Promise<void>;
|
|
33
|
+
declare function assertAssetCredentialProfile(profile: string): string;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { AssetCredentialStore, AssetCredentials, assertAssetCredentialProfile, assetCredentialEnvironment, configureAssetCredentials, resolveAssetCredentials, systemAssetCredentialStore };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getKeyring as e,getPassword as t,initBackend as n,setPassword as r}from"cross-keychain";const i=`dev.coreframe.assets`,a=new Set([`macos`,`native-macos`,`native-windows`,`secret-service`,`windows`]),o={applicationKey:`COREFRAME_ASSETS_B2_APPLICATION_KEY`,applicationKeyId:`COREFRAME_ASSETS_B2_APPLICATION_KEY_ID`};function s(){let o=!1;async function s(){if(o)return;await n(e=>a.has(e.id));let t=await e();if(!a.has(t.id))throw Error(`No supported operating system credential store is available. Coreframe requires macOS Keychain, Windows Credential Manager, or Linux Secret Service.`);o=!0}return{async get(e){await s();let n=await t(i,e);if(n)try{return f(JSON.parse(n))}catch{throw Error(`The Coreframe asset credential profile "${e}" is invalid. Run: coreframe assets configure ${e}`)}},async set(e,t){await s(),await r(i,e,JSON.stringify(f(t)))}}}async function c({env:e=process.env,profile:t,store:n=s()}){let r=e[o.applicationKeyId],i=e[o.applicationKey];if(r||i){if(!r||!i)throw Error(`${o.applicationKeyId} and ${o.applicationKey} must be provided together.`);return{applicationKey:i,applicationKeyId:r}}let a;try{a=await n.get(t)}catch(e){throw Error(`Coreframe could not access the operating system credential store for asset profile "${t}": ${e.message}`)}if(!a)throw Error(`Coreframe asset credential profile "${t}" is not configured. Run: coreframe assets configure ${t}`);return a}async function l({applicationKey:e,applicationKeyId:t,profile:n,store:r=s()}){d(n);try{await r.set(n,f({applicationKey:e,applicationKeyId:t}))}catch(e){throw Error(`Coreframe could not store asset profile "${n}" in the operating system credential store: ${e.message}`)}}function u(e){return d(e),e}function d(e){if(!/^[a-z0-9][a-z0-9._-]*$/.test(e))throw Error(`Credential profiles must start with a lowercase letter or number and contain only lowercase letters, numbers, dots, underscores, or hyphens.`)}function f(e){if(!e||typeof e!=`object`||typeof e.applicationKeyId!=`string`||!e.applicationKeyId.trim()||typeof e.applicationKey!=`string`||!e.applicationKey.trim())throw Error(`Asset credentials require a non-empty application key ID and application key.`);return{applicationKeyId:e.applicationKeyId.trim(),applicationKey:e.applicationKey}}export{u as assertAssetCredentialProfile,o as assetCredentialEnvironment,l as configureAssetCredentials,c as resolveAssetCredentials,s as systemAssetCredentialStore};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AssetCatalogEntry, AssetProblem, AssetVariant } from "./contracts.js";
|
|
2
|
+
import { AssetsConfig } from "../config.js";
|
|
3
|
+
|
|
4
|
+
//#region assets/images.d.ts
|
|
5
|
+
declare function buildAssetVariants({
|
|
6
|
+
assetId,
|
|
7
|
+
config,
|
|
8
|
+
cwd,
|
|
9
|
+
entry,
|
|
10
|
+
sourceDimensions,
|
|
11
|
+
sourcePath
|
|
12
|
+
}: {
|
|
13
|
+
assetId: string;
|
|
14
|
+
config: AssetsConfig;
|
|
15
|
+
cwd: string;
|
|
16
|
+
entry: AssetCatalogEntry;
|
|
17
|
+
sourceDimensions: {
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
};
|
|
21
|
+
sourcePath: string;
|
|
22
|
+
}): Promise<AssetVariant[]>;
|
|
23
|
+
declare function assetVariantWidths(entry: AssetCatalogEntry, sourceWidth: number): number[];
|
|
24
|
+
declare function assertVariantWidths(widths: number[] | undefined, sourceWidth: number): void;
|
|
25
|
+
declare function variantWidthsProblem(assetId: string, entry: AssetCatalogEntry, sourceWidth: number): AssetProblem | undefined;
|
|
26
|
+
declare function readImageDimensions(file: string): Promise<{
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
}>;
|
|
30
|
+
declare function mediaTypeFor(file: string): string;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { assertVariantWidths, assetVariantWidths, buildAssetVariants, mediaTypeFor, readImageDimensions, variantWidthsProblem };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{catalogPath as e,resolveAssetVariantFile as t}from"./project.js";import{sha256 as n}from"./storage.js";import{mkdir as r,writeFile as i}from"node:fs/promises";import{dirname as a,extname as o}from"node:path";const s=[320,640,960,1280,1920,2560],c=s.at(-1);async function l({assetId:e,config:o,cwd:s,entry:c,sourceDimensions:l,sourcePath:d}){let f=await h();return Promise.all(u(c,l.width).map(async c=>{let u=await f(d).resize({width:c,withoutEnlargement:!0}).webp().toBuffer(),p=n(u),m=`projects/${o.projectId}/assets/${e}.${p.slice(0,12)}.${c}.webp`,h=t(s,m);await r(a(h),{recursive:!0}),await i(h,u);let g=await f(u).metadata();return{format:`webp`,width:g.width??c,height:g.height??Math.round(l.height/l.width*c),path:m,size:u.byteLength,hash:p}}))}function u(e,t){if(e.variantWidths)return e.variantWidths;let n=Math.min(Math.round(t),c);return[...s.filter(e=>e<=n),n].filter((e,t,n)=>t===0||e!==n[t-1])}function d(e,t){let n=p(e,t);if(n)throw Error(`Invalid variant widths: ${n}`)}function f(t,n,r){let i=p(n.variantWidths,r);return i?{code:`INVALID_VARIANT_WIDTHS`,message:`Asset "${t}" has invalid variantWidths: ${i}`,assetId:t,file:e,fix:`Edit ${e}, then run: coreframe assets build --id ${t}`}:void 0}function p(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}.`}}}async function m(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.`)}}function g(e){let t={".avif":`image/avif`,".gif":`image/gif`,".jpeg":`image/jpeg`,".jpg":`image/jpeg`,".png":`image/png`,".svg":`image/svg+xml`,".tif":`image/tiff`,".tiff":`image/tiff`,".webp":`image/webp`}[o(e).toLowerCase()];if(!t)throw Error(`Unsupported source image type: ${o(e)||`unknown`}.`);return t}export{d as assertVariantWidths,u as assetVariantWidths,l as buildAssetVariants,g as mediaTypeFor,m as readImageDimensions,f as variantWidthsProblem};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AssetCatalog, AssetCatalogEntry, AssetManifest, AssetManifestEntry, AssetProblem, AssetSource, LoadedAssetCatalog, LoadedAssetManifest } from "./contracts.js";
|
|
2
|
+
import { AssetsConfig } from "../config.js";
|
|
3
|
+
|
|
4
|
+
//#region assets/project.d.ts
|
|
5
|
+
declare const catalogPath: string;
|
|
6
|
+
declare const manifestPath: string;
|
|
7
|
+
declare const assetIdPattern: RegExp;
|
|
8
|
+
declare function readAssetCatalog(cwd?: string, {
|
|
9
|
+
allowMissing
|
|
10
|
+
}?: {
|
|
11
|
+
allowMissing?: boolean;
|
|
12
|
+
}): Promise<LoadedAssetCatalog>;
|
|
13
|
+
declare function readAssetManifest(cwd?: string, {
|
|
14
|
+
allowMissing
|
|
15
|
+
}?: {
|
|
16
|
+
allowMissing?: boolean;
|
|
17
|
+
}): Promise<LoadedAssetManifest>;
|
|
18
|
+
declare function resolveAssetVariantFile(cwd: string, variantPath: string): string;
|
|
19
|
+
declare function resolveAssetsConfig(cwd: string, provided?: AssetsConfig): Promise<AssetsConfig>;
|
|
20
|
+
declare function validateSource(assetId: string, source: AssetSource): AssetProblem | undefined;
|
|
21
|
+
declare function manifestEntryMatchesCatalog(manifest: AssetManifestEntry, catalog: AssetCatalogEntry): boolean;
|
|
22
|
+
declare function isAssetVariantPath(path: string, projectPrefix: string | undefined): boolean;
|
|
23
|
+
declare function findAssetReferences(cwd: string): Promise<Array<{
|
|
24
|
+
assetId: string;
|
|
25
|
+
file: string;
|
|
26
|
+
}>>;
|
|
27
|
+
declare function writeCatalog(cwd: string, catalog: AssetCatalog): Promise<void>;
|
|
28
|
+
declare function writeManifest(cwd: string, manifest: AssetManifest): Promise<void>;
|
|
29
|
+
declare function assertAssetId(id: string): void;
|
|
30
|
+
declare function formatAssetProblems(problems: AssetProblem[]): string;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { assertAssetId, assetIdPattern, catalogPath, findAssetReferences, formatAssetProblems, isAssetVariantPath, manifestEntryMatchesCatalog, manifestPath, readAssetCatalog, readAssetManifest, resolveAssetVariantFile, resolveAssetsConfig, validateSource, writeCatalog, writeManifest };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{readProjectConfig as e}from"../config.js";import"./contracts.js";import{mkdir as t,readFile as n,stat as r,writeFile as i}from"node:fs/promises";import{dirname as a,join as o,resolve as s}from"node:path";import c from"nano-spawn";const l=`assets`,u=o(l,`catalog.json`),d=o(l,`manifest.json`),f=/^[a-z0-9]+(?:[/-][a-z0-9]+)*$/,p=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,m=/@asset\s+([a-z0-9][a-z0-9/-]*)/g,h=/^[0-9a-f]{64}$/;async function g(e=process.cwd(),{allowMissing:t=!1}={}){let n=await E(e,u,`catalog`,t);return n.missing&&t?{catalog:F(),problems:[]}:n.problem?{catalog:F(),problems:[n.problem]}:D(n.value)?{catalog:n.value,problems:[]}:{catalog:F(),problems:[{code:`INVALID_CATALOG`,message:`${u} has an unsupported shape or schema version.`,file:u}]}}async function _(e=process.cwd(),{allowMissing:t=!1}={}){let n=await E(e,d,`manifest`,t);return n.missing&&t?{manifest:I(),problems:[]}:n.problem?{manifest:I(),problems:[n.problem]}:O(n.value)?{manifest:n.value,problems:[]}:{manifest:I(),problems:[{code:`INVALID_MANIFEST`,message:`${d} has an unsupported shape or schema version.`,file:d}]}}function v(e,t){if(!S(t,C(t)))throw Error(`Invalid asset variant path: ${t}`);return s(e,`.coreframe/assets`,t)}async function y(t,n){let r=n??(await e({cwd:t})).assets;if(!r)throw Error(`Static assets are not configured in coreframe.config.ts.`);for(let[e,t]of Object.entries(r))if(typeof t!=`string`||!t.trim())throw Error(`Static asset configuration "${e}" must be a non-empty string.`);if(!p.test(r.projectId))throw Error(`Static asset projectId must use lowercase hyphen-separated segments.`);for(let e of[`endpoint`,`publicBaseUrl`])if(new URL(r[e]).protocol!==`https:`)throw Error(`Static asset ${e} must use HTTPS.`);return{...r,endpoint:r.endpoint.replace(/\/+$/,``),publicBaseUrl:r.publicBaseUrl.replace(/\/+$/,``)}}function b(e,t){if(!t||typeof t.filename!=`string`||!t.filename||typeof t.mediaType!=`string`||!t.mediaType||!Number.isSafeInteger(t.size)||t.size<=0||!h.test(t.digest))return{code:`INVALID_SOURCE`,message:`Asset "${e}" has invalid immutable source metadata.`,assetId:e,file:u}}function x(e,t){return e.alt===t.alt&&e.source.digest===t.source.digest&&e.source.filename===t.source.filename&&e.source.mediaType===t.source.mediaType&&e.source.size===t.source.size&&JSON.stringify(e.variantWidths)===JSON.stringify(t.variantWidths)}function S(e,t){let n=e.split(`/`);return!!(t&&e.startsWith(`${t}/`)&&n.length>=4&&!e.includes(`\\`)&&n.every(e=>e&&e!==`.`&&e!==`..`))}function C(e){let[t,n,r]=e.split(`/`);return t===`projects`&&p.test(n??``)&&r===`assets`?`projects/${n}/assets`:void 0}async function w(e){let t=[];for(let r of await T(e)){let i=s(e,r);if(!await H(i))continue;let a=await n(i,`utf8`);for(let e of a.matchAll(m))t.push({assetId:e[1],file:r})}return t}async function T(e){try{return(await c(`git`,[`ls-files`,`*.md`,`*.mdx`,`*.ts`,`*.tsx`],{cwd:e})).stdout.split(/\r?\n/).map(e=>e.trim()).filter(e=>e.startsWith(`content/`)||e.startsWith(`src/`))}catch{return[]}}async function E(e,t,r,i){try{return{value:JSON.parse(await n(s(e,t),`utf8`)),missing:!1}}catch(e){return e.code===`ENOENT`?{value:void 0,missing:!0,problem:i?void 0:{code:r===`catalog`?`CATALOG_MISSING`:`MANIFEST_MISSING`,message:`${t} does not exist.`,file:t,fix:r===`catalog`?`Run: coreframe assets add <file> --id <asset-id> --alt "..."`:`Run: coreframe assets build`}}:{value:void 0,missing:!1,problem:{code:r===`catalog`?`INVALID_CATALOG`:`INVALID_MANIFEST`,message:`${t} is not valid JSON: ${e.message}`,file:t}}}}function D(e){return!!(e&&typeof e==`object`&&P(e,[`schemaVersion`,`assets`])&&e.schemaVersion===1&&k(e.assets)&&Object.values(e.assets).every(A))}function O(e){return!!(e&&typeof e==`object`&&P(e,[`schemaVersion`,`assets`])&&e.schemaVersion===1&&k(e.assets)&&Object.values(e.assets).every(j))}function k(e){return!!(e&&typeof e==`object`&&!Array.isArray(e))}function A(e){return k(e)?P(e,[`source`,`alt`,`variantWidths`])&&M(e.source)&&typeof e.alt==`string`&&(e.variantWidths===void 0||Array.isArray(e.variantWidths)&&e.variantWidths.every(e=>typeof e==`number`)):!1}function j(e){return k(e)?P(e,[`source`,`alt`,`variantWidths`,`width`,`height`,`variants`])&&M(e.source)&&typeof e.alt==`string`&&(e.variantWidths===void 0||Array.isArray(e.variantWidths)&&e.variantWidths.every(e=>typeof e==`number`))&&typeof e.width==`number`&&e.width>0&&typeof e.height==`number`&&e.height>0&&Array.isArray(e.variants)&&e.variants.every(N):!1}function M(e){return k(e)?P(e,[`digest`,`filename`,`mediaType`,`size`])&&typeof e.digest==`string`&&typeof e.filename==`string`&&typeof e.mediaType==`string`&&typeof e.size==`number`:!1}function N(e){return k(e)?P(e,[`format`,`width`,`height`,`path`,`size`,`hash`])&&e.format===`webp`&&typeof e.width==`number`&&typeof e.height==`number`&&typeof e.path==`string`&&typeof e.size==`number`&&typeof e.hash==`string`:!1}function P(e,t){let n=new Set(t);return Object.keys(e).every(e=>n.has(e))}function F(){return{schemaVersion:1,assets:{}}}function I(){return{schemaVersion:1,assets:{}}}async function L(e,t){await z(e,u,{...t,assets:B(t.assets)})}async function R(e,t){await z(e,d,{...t,assets:B(t.assets)})}async function z(e,n,r){let o=s(e,n);await t(a(o),{recursive:!0}),await i(o,`${JSON.stringify(r,null,2)}\n`)}function B(e){return Object.fromEntries(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)))}function V(e){if(!f.test(e))throw Error(`Asset ID "${e}" must use lowercase path segments.`)}async function H(e){try{return await r(e),!0}catch{return!1}}function U(e){return e.map(e=>[`${e.code}: ${e.message}`,e.file?` file: ${e.file}`:void 0,e.fix?` fix: ${e.fix}`:void 0].filter(Boolean).join(`
|
|
2
|
+
`)).join(`
|
|
3
|
+
`)}export{V as assertAssetId,f as assetIdPattern,u as catalogPath,w as findAssetReferences,U as formatAssetProblems,S as isAssetVariantPath,x as manifestEntryMatchesCatalog,d as manifestPath,g as readAssetCatalog,_ as readAssetManifest,v as resolveAssetVariantFile,y as resolveAssetsConfig,b as validateSource,L as writeCatalog,R as writeManifest};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { AssetsConfig } from "../config.js";
|
|
2
|
+
import { AssetCredentialStore } from "./credentials.js";
|
|
3
|
+
|
|
4
|
+
//#region assets/storage.d.ts
|
|
5
|
+
type AssetObjectStore = {
|
|
6
|
+
has(bucket: string, key: string): Promise<boolean>;
|
|
7
|
+
get(bucket: string, key: string): Promise<Uint8Array>;
|
|
8
|
+
put(bucket: string, key: string, body: Uint8Array, contentType: string): Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
declare function assetSourceKey(digest: string): string;
|
|
11
|
+
declare function createB2AssetStore({
|
|
12
|
+
config,
|
|
13
|
+
credentialStore,
|
|
14
|
+
env
|
|
15
|
+
}: {
|
|
16
|
+
config: AssetsConfig;
|
|
17
|
+
credentialStore?: AssetCredentialStore;
|
|
18
|
+
env?: NodeJS.ProcessEnv;
|
|
19
|
+
}): AssetObjectStore;
|
|
20
|
+
declare function assetCacheRoot(env?: NodeJS.ProcessEnv): string;
|
|
21
|
+
declare function assetCacheFile(cacheRoot: string, digest: string): string;
|
|
22
|
+
declare function cacheAssetSource({
|
|
23
|
+
body,
|
|
24
|
+
cacheRoot,
|
|
25
|
+
digest,
|
|
26
|
+
env
|
|
27
|
+
}: {
|
|
28
|
+
body: Uint8Array;
|
|
29
|
+
cacheRoot: string;
|
|
30
|
+
digest: string;
|
|
31
|
+
env?: NodeJS.ProcessEnv;
|
|
32
|
+
}): Promise<string>;
|
|
33
|
+
declare function materializeAssetSource({
|
|
34
|
+
bucket,
|
|
35
|
+
cacheRoot,
|
|
36
|
+
digest,
|
|
37
|
+
env,
|
|
38
|
+
store
|
|
39
|
+
}: {
|
|
40
|
+
bucket: string;
|
|
41
|
+
cacheRoot: string;
|
|
42
|
+
digest: string;
|
|
43
|
+
env?: NodeJS.ProcessEnv;
|
|
44
|
+
store: AssetObjectStore;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
fetched: boolean;
|
|
47
|
+
path: string;
|
|
48
|
+
}>;
|
|
49
|
+
declare function sha256(body: Uint8Array): string;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { AssetObjectStore, assetCacheFile, assetCacheRoot, assetSourceKey, cacheAssetSource, createB2AssetStore, materializeAssetSource, sha256 };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{resolveAssetCredentials as e}from"./credentials.js";import{mkdir as t,open as n,readFile as r,readdir as i,rename as a,rm as o,stat as s,utimes as c,writeFile as l}from"node:fs/promises";import{dirname as u,join as d,resolve as f}from"node:path";import{createHash as p,randomUUID as m}from"node:crypto";import{homedir as h,platform as g}from"node:os";import{GetObjectCommand as _,HeadObjectCommand as v,PutObjectCommand as y,S3Client as b}from"@aws-sdk/client-s3";function x(e){return N(e),`sources/v1/sha256/${e.slice(0,2)}/${e}`}function S({config:t,credentialStore:n,env:r=process.env}){let i;async function a(){if(i)return i;let a=await e({env:r,profile:t.credentialProfile,store:n});return i=new b({endpoint:t.endpoint,forcePathStyle:!0,region:t.region,credentials:{accessKeyId:a.applicationKeyId,secretAccessKey:a.applicationKey}}),i}return{async has(e,t){try{return await(await a()).send(new v({Bucket:e,Key:t})),!0}catch(e){if(e.$metadata?.httpStatusCode===404)return!1;throw e}},async get(e,t){let n=await(await a()).send(new _({Bucket:e,Key:t}));if(!n.Body)throw Error(`Backblaze B2 returned an empty body for ${e}/${t}.`);return n.Body.transformToByteArray()},async put(e,t,n,r){await(await a()).send(new y({Bucket:e,Key:t,Body:n,ContentType:r}))}}}function C(e=process.env){return e.COREFRAME_ASSETS_CACHE_DIR?f(e.COREFRAME_ASSETS_CACHE_DIR):g()===`darwin`?d(h(),`Library`,`Caches`,`coreframe`,`assets`):g()===`win32`?d(e.LOCALAPPDATA??d(h(),`AppData`,`Local`),`Coreframe`,`assets`):d(e.XDG_CACHE_HOME??d(h(),`.cache`),`coreframe`,`assets`)}function w(e,t){return N(t),d(e,`v1`,`sha256`,t.slice(0,2),t)}async function T({body:e,cacheRoot:n,digest:r,env:i=process.env}){P(e,r);let s=w(n,r);return await D(s,async()=>{if(await O(s,r)){await k(s);return}await t(u(s),{recursive:!0});let n=`${s}.${process.pid}.${m()}.tmp`;try{await l(n,e,{flag:`wx`}),await a(n,s)}finally{await o(n,{force:!0})}}),await A(n,M(i),s),s}async function E({bucket:e,cacheRoot:n,digest:r,env:i=process.env,store:s}){let c=w(n,r);if(await O(c,r))return await k(c),{fetched:!1,path:c};let d=!1;return await D(c,async()=>{if(await O(c,r)){await k(c);return}let n=await s.get(e,x(r));P(n,r),await t(u(c),{recursive:!0});let i=`${c}.${process.pid}.${m()}.tmp`;try{await l(i,n,{flag:`wx`}),await a(i,c),d=!0}finally{await o(i,{force:!0})}}),await A(n,M(i),c),{fetched:d,path:c}}async function D(e,r){await t(u(e),{recursive:!0});let i=`${e}.lock`,a=Date.now();for(;;){let e;try{e=await n(i,`wx`)}catch(e){if(e.code!==`EEXIST`)throw e;let t=await s(i).catch(()=>void 0);if(t&&Date.now()-t.mtimeMs>12e4){await o(i,{force:!0});continue}if(Date.now()-a>=3e4)throw Error(`Timed out waiting for the asset cache lock at ${i}.`);await new Promise(e=>setTimeout(e,50));continue}try{await r()}finally{await e.close(),await o(i,{force:!0})}return}}async function O(e,t){try{return F(await r(e))===t}catch{return!1}}async function k(e){let t=new Date;await c(e,t,t)}async function A(e,t,n){let r=await j(e),i=r.reduce((e,t)=>e+t.size,0);for(let e of r.sort((e,t)=>e.mtimeMs-t.mtimeMs)){if(i<=t)break;e.path!==n&&(await o(e.path,{force:!0}),i-=e.size)}}async function j(e){let t=[],n=async e=>{let r=await i(e,{withFileTypes:!0}).catch(()=>[]);for(let i of r){let r=d(e,i.name);if(i.isDirectory())await n(r);else if(!i.name.endsWith(`.lock`)&&!i.name.endsWith(`.tmp`)){let e=await s(r);t.push({path:r,size:e.size,mtimeMs:e.mtimeMs})}}};return await n(e),t}function M(e){let t=Number(e.COREFRAME_ASSETS_CACHE_MAX_BYTES);return Number.isSafeInteger(t)&&t>0?t:5368709120}function N(e){if(!/^[0-9a-f]{64}$/.test(e))throw Error(`Invalid SHA-256 asset digest: ${e}`)}function P(e,t){let n=F(e);if(n!==t)throw Error(`Asset integrity check failed: expected sha256:${t}, received sha256:${n}.`)}function F(e){return p(`sha256`).update(e).digest(`hex`)}export{w as assetCacheFile,C as assetCacheRoot,x as assetSourceKey,T as cacheAssetSource,S as createB2AssetStore,E as materializeAssetSource,F as sha256};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { AssetBuildResult, AssetPublishResult, AssetStatus } from "./contracts.js";
|
|
2
|
+
import { AssetsConfig } from "../config.js";
|
|
3
|
+
import { AssetObjectStore } from "./storage.js";
|
|
4
|
+
|
|
5
|
+
//#region assets/workflows.d.ts
|
|
6
|
+
type AssetWorkflowOptions = {
|
|
7
|
+
cacheDir?: string;
|
|
8
|
+
config?: AssetsConfig;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
env?: NodeJS.ProcessEnv;
|
|
11
|
+
store?: AssetObjectStore;
|
|
12
|
+
};
|
|
13
|
+
type AddAssetOptions = AssetWorkflowOptions & {
|
|
14
|
+
file: string;
|
|
15
|
+
id: string;
|
|
16
|
+
alt: string;
|
|
17
|
+
variantWidths?: number[];
|
|
18
|
+
};
|
|
19
|
+
type BuildAssetsOptions = AssetWorkflowOptions & {
|
|
20
|
+
id?: string;
|
|
21
|
+
updateManifest?: boolean;
|
|
22
|
+
};
|
|
23
|
+
type CheckAssetsOptions = AssetWorkflowOptions & {
|
|
24
|
+
fix?: boolean;
|
|
25
|
+
};
|
|
26
|
+
type PublishAssetsOptions = AssetWorkflowOptions;
|
|
27
|
+
declare function addAsset({
|
|
28
|
+
alt,
|
|
29
|
+
cacheDir,
|
|
30
|
+
config,
|
|
31
|
+
cwd,
|
|
32
|
+
env,
|
|
33
|
+
file,
|
|
34
|
+
id,
|
|
35
|
+
store,
|
|
36
|
+
variantWidths
|
|
37
|
+
}: AddAssetOptions): Promise<AssetBuildResult>;
|
|
38
|
+
declare function buildAssets({
|
|
39
|
+
cacheDir,
|
|
40
|
+
config,
|
|
41
|
+
cwd,
|
|
42
|
+
env,
|
|
43
|
+
id,
|
|
44
|
+
store,
|
|
45
|
+
updateManifest
|
|
46
|
+
}?: BuildAssetsOptions): Promise<AssetBuildResult>;
|
|
47
|
+
declare function checkAssets(options?: CheckAssetsOptions): Promise<AssetStatus>;
|
|
48
|
+
declare function publishAssets(options?: PublishAssetsOptions): Promise<AssetPublishResult>;
|
|
49
|
+
declare function materializeAsset(assetId: string, options?: BuildAssetsOptions): Promise<AssetBuildResult>;
|
|
50
|
+
declare function printAssetResult(result: unknown, json?: boolean): void;
|
|
51
|
+
//#endregion
|
|
52
|
+
export { AddAssetOptions, AssetWorkflowOptions, BuildAssetsOptions, CheckAssetsOptions, PublishAssetsOptions, addAsset, buildAssets, checkAssets, materializeAsset, printAssetResult, publishAssets };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./contracts.js";import{assertAssetId as e,assetIdPattern as t,catalogPath as n,findAssetReferences as r,formatAssetProblems as i,isAssetVariantPath as a,manifestEntryMatchesCatalog as o,manifestPath as s,readAssetCatalog as c,readAssetManifest as l,resolveAssetVariantFile as u,resolveAssetsConfig as d,validateSource as f,writeCatalog as p,writeManifest as m}from"./project.js";import{assetCacheRoot as h,assetSourceKey as g,cacheAssetSource as _,createB2AssetStore as v,materializeAssetSource as y,sha256 as b}from"./storage.js";import{assertVariantWidths as x,assetVariantWidths as S,buildAssetVariants as C,mediaTypeFor as w,readImageDimensions as T,variantWidthsProblem as E}from"./images.js";import{mkdir as D,readFile as O,writeFile as k}from"node:fs/promises";import{basename as A,dirname as j,join as M,relative as N,resolve as P,sep as F}from"node:path";const I=M(`.coreframe/assets`,`asset-publish-report.json`);async function L({alt:t,cacheDir:n,config:r,cwd:i=process.cwd(),env:a=process.env,file:o,id:s,store:l,variantWidths:u}){if(e(s),!t.trim())throw Error(`Asset alt text must not be empty.`);let f=await d(i,r),m=l??v({config:f,env:a}),y=P(i,o),S=await O(y);x(u,(await T(y)).width);let C=b(S),E={digest:C,filename:A(o),mediaType:w(o),size:S.byteLength},D=n??h(a);await _({body:S,cacheRoot:D,digest:C,env:a});let k=g(C);if(!await m.has(f.sourceBucket,k)&&(await m.put(f.sourceBucket,k,S,E.mediaType),!await m.has(f.sourceBucket,k)))throw Error(`Backblaze B2 did not confirm source upload ${k}.`);let j=await c(i,{allowMissing:!0});return j.problems.length>0?{ok:!1,assets:[],fetched:[],problems:j.problems}:(j.catalog.assets[s]={source:E,alt:t.trim(),...u?{variantWidths:u}:{}},await p(i,j.catalog),R({cacheDir:D,config:f,cwd:i,env:a,id:s,store:m}))}async function R({cacheDir:e,config:t,cwd:r=process.cwd(),env:i=process.env,id:a,store:o,updateManifest:s=!0}={}){let u=await c(r),p=[...u.problems],_=[],b=[];if(p.length>0)return{ok:!1,assets:_,fetched:b,problems:p};let x=await d(r,t),S=o??v({config:x,env:i}),w=e??h(i),D=(await l(r,{allowMissing:!0})).manifest,O={schemaVersion:1,assets:a?{...D.assets}:{}};for(let[e,t]of Object.entries(u.catalog.assets)){if(a&&e!==a)continue;let n=f(e,t.source);if(n){p.push(n);continue}let o;try{o=await y({bucket:x.sourceBucket,cacheRoot:w,digest:t.source.digest,env:i,store:S})}catch(t){p.push({code:`SOURCE_UNAVAILABLE`,message:`Asset "${e}" source could not be materialized: ${t.message}`,assetId:e,fix:`Check the B2 asset configuration and credential profile "${x.credentialProfile}".`});continue}o.fetched&&b.push(g(t.source.digest));let s=await T(o.path),c=E(e,t,s.width);if(c){p.push(c);continue}let l=await C({assetId:e,config:x,cwd:r,entry:t,sourcePath:o.path,sourceDimensions:s});O.assets[e]={...t,...s,variants:l},_.push({assetId:e,variants:l})}return a&&!u.catalog.assets[a]&&p.push({code:`REFERENCE_MISSING`,message:`Asset "${a}" is not present in ${n}.`,assetId:a,fix:`Run: coreframe assets add <file> --id ${a} --alt "..."`}),s&&p.length===0&&await m(r,O),{ok:p.length===0,assets:_,fetched:b,problems:p}}async function z(e={}){let{cwd:i=process.cwd(),fix:p=!1}=e;if(p){let t=await R(e);if(!t.ok)return{ok:!1,assets:[],problems:t.problems}}let[m,h]=await Promise.all([c(i),l(i)]),g=[...m.problems,...h.problems],_=[],v=await r(i),y=`projects/${(await d(i,e.config)).projectId}/assets`;for(let[e,r]of Object.entries(m.catalog.assets)){t.test(e)||g.push({code:`INVALID_ASSET_ID`,message:`Asset ID "${e}" must use lowercase path segments.`,assetId:e,file:n}),r.alt.trim()||g.push({code:`INVALID_ALT`,message:`Asset "${e}" is missing alt text.`,assetId:e,file:n});let c=f(e,r.source);c&&g.push(c);let l=h.manifest.assets[e];if(!l||!o(l,r)){g.push({code:`MANIFEST_STALE`,message:`Asset "${e}" is missing from the generated manifest or does not match its catalog entry.`,assetId:e,file:s,fix:`Run: coreframe assets build --id ${e}`});continue}let d=E(e,r,l.width);d&&g.push(d);let p=await Promise.all(l.variants.map(async t=>{let n=a(t.path,y),r=!1;if(n)try{let e=await O(u(i,t.path));r=e.byteLength===t.size&&b(e)===t.hash}catch{r=!1}return n||g.push({code:`INVALID_VARIANT_PATH`,message:`Asset "${e}" has an invalid generated variant path.`,assetId:e,file:t.path,fix:`Run: coreframe assets build --id ${e}`}),{...t,exists:r}})),m=S(r,l.width),v=l.variants.map(e=>e.width);!d&&(m.length!==v.length||m.some((e,t)=>e!==v[t])||p.some(e=>!e.exists))&&g.push({code:`GENERATED_VARIANT_MISSING`,message:`Asset "${e}" is missing its expected responsive variants.`,assetId:e,fix:`Run: coreframe assets build --id ${e}`}),_.push({assetId:e,source:r.source,variants:p})}for(let e of Object.keys(h.manifest.assets))m.catalog.assets[e]||g.push({code:`MANIFEST_STALE`,message:`Generated manifest asset "${e}" is not present in the catalog.`,assetId:e,file:s,fix:`Run: coreframe assets build`});for(let e of v)m.catalog.assets[e.assetId]||g.push({code:`INVALID_REFERENCE`,message:`Asset reference "${e.assetId}" is not present in ${n}.`,assetId:e.assetId,file:e.file,fix:`Run: coreframe assets add <file> --id ${e.assetId} --alt "..."`});return{ok:g.length===0,assets:_,problems:g}}async function B(e={}){let{cwd:t=process.cwd(),env:n=process.env}=e,r=[...(await z(e)).problems],i=[],a=[],o=P(t,I);if(r.length===0){let r=await d(t,e.config),o=e.store??v({config:r,env:n}),{manifest:s}=await l(t);for(let[e,n]of Object.entries(s.assets))for(let s of n.variants){let n=u(t,s.path),c={assetId:e,source:N(t,n).split(F).join(`/`),target:`${r.publicBucket}/${s.path}`,size:s.size};if(await o.has(r.publicBucket,s.path)){a.push(c);continue}await o.put(r.publicBucket,s.path,await O(n),`image/webp`),i.push(c)}}let s={ok:r.length===0,uploaded:i,skipped:a,reportPath:o,problems:r};return await D(j(o),{recursive:!0}),await k(o,`${JSON.stringify(s,null,2)}\n`),s}async function V(e,t={}){return R({...t,id:e})}function H(e,t=!1){if(t){console.log(JSON.stringify(e,null,2));return}let n=e;n.problems?.length&&console.error(i(n.problems)),console.log(n.ok===!1?`Asset workflow has problems.`:`Asset workflow complete.`)}export{L as addAsset,R as buildAssets,z as checkAssets,V as materializeAsset,H as printAssetResult,B as publishAssets};
|
package/dist/assets.d.ts
CHANGED
|
@@ -1,118 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
alt: string;
|
|
6
|
-
width: number;
|
|
7
|
-
height: number;
|
|
8
|
-
variantWidths?: number[];
|
|
9
|
-
variants: AssetVariant[];
|
|
10
|
-
};
|
|
11
|
-
type AssetVariant = {
|
|
12
|
-
format: "webp";
|
|
13
|
-
width: number;
|
|
14
|
-
height: number;
|
|
15
|
-
path: string;
|
|
16
|
-
size: number;
|
|
17
|
-
hash: string;
|
|
18
|
-
};
|
|
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_BLOB_NOT_POINTER" | "LFS_NOT_TRACKED" | "MANIFEST_MISSING" | "REFERENCE_MISSING" | "SOURCE_MISSING" | "SOURCE_POINTER_MISSING_OBJECT";
|
|
21
|
-
message: string;
|
|
22
|
-
assetId?: string;
|
|
23
|
-
file?: string;
|
|
24
|
-
fix?: string;
|
|
25
|
-
};
|
|
26
|
-
type AssetStatus = {
|
|
27
|
-
ok: boolean;
|
|
28
|
-
assets: AssetStatusEntry[];
|
|
29
|
-
problems: AssetProblem[];
|
|
30
|
-
};
|
|
31
|
-
type AssetStatusEntry = {
|
|
32
|
-
assetId: string;
|
|
33
|
-
source: string;
|
|
34
|
-
sourceExists: boolean;
|
|
35
|
-
lfsTracked: boolean;
|
|
36
|
-
variants: AssetVariantStatus[];
|
|
37
|
-
};
|
|
38
|
-
type AssetVariantStatus = AssetVariant & {
|
|
39
|
-
exists: boolean;
|
|
40
|
-
};
|
|
41
|
-
type AssetBuildResult = {
|
|
42
|
-
ok: boolean;
|
|
43
|
-
assets: AssetBuildEntry[];
|
|
44
|
-
fetched: string[];
|
|
45
|
-
problems: AssetProblem[];
|
|
46
|
-
};
|
|
47
|
-
type AssetBuildEntry = {
|
|
48
|
-
assetId: string;
|
|
49
|
-
variants: AssetVariant[];
|
|
50
|
-
};
|
|
51
|
-
type AssetPublishResult = {
|
|
52
|
-
ok: boolean;
|
|
53
|
-
uploaded: PublishedAsset[];
|
|
54
|
-
skipped: PublishedAsset[];
|
|
55
|
-
reportPath: string;
|
|
56
|
-
problems: AssetProblem[];
|
|
57
|
-
};
|
|
58
|
-
type PublishedAsset = {
|
|
59
|
-
assetId: string;
|
|
60
|
-
source: string;
|
|
61
|
-
target: string;
|
|
62
|
-
size: number;
|
|
63
|
-
};
|
|
64
|
-
type AssetWorkflowOptions = {
|
|
65
|
-
cwd?: string;
|
|
66
|
-
json?: boolean;
|
|
67
|
-
env?: NodeJS.ProcessEnv;
|
|
68
|
-
};
|
|
69
|
-
type AddAssetOptions = AssetWorkflowOptions & {
|
|
70
|
-
file: string;
|
|
71
|
-
id: string;
|
|
72
|
-
alt: string;
|
|
73
|
-
variantWidths?: number[];
|
|
74
|
-
};
|
|
75
|
-
type BuildAssetsOptions = AssetWorkflowOptions & {
|
|
76
|
-
id?: string;
|
|
77
|
-
pull?: boolean;
|
|
78
|
-
updateManifest?: boolean;
|
|
79
|
-
};
|
|
80
|
-
type CheckAssetsOptions = AssetWorkflowOptions & {
|
|
81
|
-
fix?: boolean;
|
|
82
|
-
};
|
|
83
|
-
type PublishAssetsOptions = AssetWorkflowOptions & {
|
|
84
|
-
outputDir?: string;
|
|
85
|
-
};
|
|
86
|
-
type LoadedAssetManifest = {
|
|
87
|
-
manifest: AssetManifest;
|
|
88
|
-
problems: AssetProblem[];
|
|
89
|
-
};
|
|
90
|
-
declare function addAsset({
|
|
91
|
-
alt,
|
|
92
|
-
cwd,
|
|
93
|
-
file,
|
|
94
|
-
id,
|
|
95
|
-
variantWidths
|
|
96
|
-
}: AddAssetOptions): Promise<AssetBuildResult>;
|
|
97
|
-
declare function buildAssets({
|
|
98
|
-
cwd,
|
|
99
|
-
id,
|
|
100
|
-
pull,
|
|
101
|
-
updateManifest
|
|
102
|
-
}?: BuildAssetsOptions): Promise<AssetBuildResult>;
|
|
103
|
-
declare function checkAssets({
|
|
104
|
-
cwd,
|
|
105
|
-
fix
|
|
106
|
-
}?: CheckAssetsOptions): Promise<AssetStatus>;
|
|
107
|
-
declare function publishAssets({
|
|
108
|
-
cwd,
|
|
109
|
-
env,
|
|
110
|
-
outputDir
|
|
111
|
-
}?: PublishAssetsOptions): Promise<AssetPublishResult>;
|
|
112
|
-
declare function materializeAsset(assetId: string, options?: BuildAssetsOptions): Promise<AssetBuildResult>;
|
|
113
|
-
declare function printAssetResult(result: unknown, json?: boolean): void;
|
|
114
|
-
declare function readAssetManifest(cwd?: string): Promise<LoadedAssetManifest>;
|
|
115
|
-
declare function resolveAssetVariantFile(cwd: string, variantPath: string): string;
|
|
116
|
-
declare function formatAssetProblems(problems: AssetProblem[]): string;
|
|
117
|
-
//#endregion
|
|
118
|
-
export { AddAssetOptions, AssetBuildEntry, AssetBuildResult, AssetManifest, AssetManifestEntry, AssetProblem, AssetPublishResult, AssetStatus, AssetStatusEntry, AssetVariant, AssetVariantStatus, AssetWorkflowOptions, BuildAssetsOptions, CheckAssetsOptions, LoadedAssetManifest, PublishAssetsOptions, PublishedAsset, addAsset, buildAssets, checkAssets, formatAssetProblems, materializeAsset, printAssetResult, publishAssets, readAssetManifest, resolveAssetVariantFile };
|
|
1
|
+
import { AssetBuildEntry, AssetBuildResult, AssetCatalog, AssetCatalogEntry, AssetManifest, AssetManifestEntry, AssetProblem, AssetPublishResult, AssetSource, AssetStatus, AssetStatusEntry, AssetVariant, AssetVariantStatus, LoadedAssetCatalog, LoadedAssetManifest, PublishedAsset } from "./assets/contracts.js";
|
|
2
|
+
import { formatAssetProblems, readAssetCatalog, readAssetManifest, resolveAssetVariantFile } from "./assets/project.js";
|
|
3
|
+
import { AddAssetOptions, AssetWorkflowOptions, BuildAssetsOptions, CheckAssetsOptions, PublishAssetsOptions, addAsset, buildAssets, checkAssets, materializeAsset, printAssetResult, publishAssets } from "./assets/workflows.js";
|
|
4
|
+
export { AddAssetOptions, type AssetBuildEntry, type AssetBuildResult, type AssetCatalog, type AssetCatalogEntry, type AssetManifest, type AssetManifestEntry, type AssetProblem, type AssetPublishResult, type AssetSource, type AssetStatus, type AssetStatusEntry, type AssetVariant, type AssetVariantStatus, AssetWorkflowOptions, BuildAssetsOptions, CheckAssetsOptions, type LoadedAssetCatalog, type LoadedAssetManifest, PublishAssetsOptions, type PublishedAsset, addAsset, buildAssets, checkAssets, formatAssetProblems, materializeAsset, printAssetResult, publishAssets, readAssetCatalog, readAssetManifest, resolveAssetVariantFile };
|
package/dist/assets.js
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`)).join(`
|
|
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};
|
|
1
|
+
import{formatAssetProblems as e,readAssetCatalog as t,readAssetManifest as n,resolveAssetVariantFile as r}from"./assets/project.js";import{addAsset as i,buildAssets as a,checkAssets as o,materializeAsset as s,printAssetResult as c,publishAssets as l}from"./assets/workflows.js";export{i as addAsset,a as buildAssets,o as checkAssets,e as formatAssetProblems,s as materializeAsset,c as printAssetResult,l as publishAssets,t as readAssetCatalog,n as readAssetManifest,r as resolveAssetVariantFile};
|
package/dist/config.d.ts
CHANGED
|
@@ -5,11 +5,21 @@ import { WorkerConfig } from "./worker-environment.js";
|
|
|
5
5
|
|
|
6
6
|
//#region config.d.ts
|
|
7
7
|
type CoreframeConfig = {
|
|
8
|
+
assets?: AssetsConfig;
|
|
8
9
|
database?: DatabaseConfig;
|
|
9
10
|
deploy?: DeployConfig;
|
|
10
11
|
dev?: DevConfig;
|
|
11
12
|
worker?: WorkerConfig;
|
|
12
13
|
};
|
|
14
|
+
type AssetsConfig = {
|
|
15
|
+
credentialProfile: string;
|
|
16
|
+
endpoint: string;
|
|
17
|
+
projectId: string;
|
|
18
|
+
publicBaseUrl: string;
|
|
19
|
+
publicBucket: string;
|
|
20
|
+
region: string;
|
|
21
|
+
sourceBucket: string;
|
|
22
|
+
};
|
|
13
23
|
type DevConfig = {
|
|
14
24
|
platforms?: DevPlatform[];
|
|
15
25
|
};
|
|
@@ -24,4 +34,4 @@ declare function readProjectConfig({
|
|
|
24
34
|
cwd?: string;
|
|
25
35
|
}): Promise<CoreframeConfig>;
|
|
26
36
|
//#endregion
|
|
27
|
-
export { CoreframeConfig, DatabaseConfig, DevConfig, defineConfig, readProjectConfig };
|
|
37
|
+
export { AssetsConfig, CoreframeConfig, DatabaseConfig, DevConfig, defineConfig, readProjectConfig };
|
package/dist/coreframe.d.ts
CHANGED
|
@@ -3,6 +3,11 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
3
3
|
parse(context: import("cmd-ts/dist/cjs/argparser").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser").ParsingResult<{
|
|
4
4
|
command: "assets";
|
|
5
5
|
args: {
|
|
6
|
+
command: "configure";
|
|
7
|
+
args: {
|
|
8
|
+
profile: string;
|
|
9
|
+
};
|
|
10
|
+
} | {
|
|
6
11
|
command: "add";
|
|
7
12
|
args: {
|
|
8
13
|
file: string;
|
|
@@ -15,7 +20,6 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
15
20
|
command: "build";
|
|
16
21
|
args: {
|
|
17
22
|
id: string | undefined;
|
|
18
|
-
noPull: boolean;
|
|
19
23
|
json: boolean;
|
|
20
24
|
};
|
|
21
25
|
} | {
|
|
@@ -27,7 +31,6 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
27
31
|
} | {
|
|
28
32
|
command: "publish";
|
|
29
33
|
args: {
|
|
30
|
-
outputDir: string | undefined;
|
|
31
34
|
json: boolean;
|
|
32
35
|
};
|
|
33
36
|
} | {
|
|
@@ -70,6 +73,12 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
70
73
|
environment: "staging" | "production" | undefined;
|
|
71
74
|
json: boolean;
|
|
72
75
|
};
|
|
76
|
+
} | {
|
|
77
|
+
command: "rotate-access-token";
|
|
78
|
+
args: {
|
|
79
|
+
environment: "staging" | "production";
|
|
80
|
+
yes: boolean;
|
|
81
|
+
};
|
|
73
82
|
} | {
|
|
74
83
|
command: "set";
|
|
75
84
|
args: {
|
|
@@ -194,6 +203,11 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
194
203
|
} & import("cmd-ts/dist/cjs/helpdoc").Named & Partial<import("cmd-ts/dist/cjs/helpdoc").Descriptive & import("cmd-ts/dist/cjs/helpdoc").Versioned> & import("cmd-ts/dist/cjs/helpdoc").PrintHelp & Partial<import("cmd-ts/dist/cjs/helpdoc").Versioned> & import("cmd-ts/dist/cjs/argparser").Register & import("cmd-ts/dist/cjs/runner").Handling<{
|
|
195
204
|
command: "assets";
|
|
196
205
|
args: {
|
|
206
|
+
command: "configure";
|
|
207
|
+
args: {
|
|
208
|
+
profile: string;
|
|
209
|
+
};
|
|
210
|
+
} | {
|
|
197
211
|
command: "add";
|
|
198
212
|
args: {
|
|
199
213
|
file: string;
|
|
@@ -206,7 +220,6 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
206
220
|
command: "build";
|
|
207
221
|
args: {
|
|
208
222
|
id: string | undefined;
|
|
209
|
-
noPull: boolean;
|
|
210
223
|
json: boolean;
|
|
211
224
|
};
|
|
212
225
|
} | {
|
|
@@ -218,7 +231,6 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
218
231
|
} | {
|
|
219
232
|
command: "publish";
|
|
220
233
|
args: {
|
|
221
|
-
outputDir: string | undefined;
|
|
222
234
|
json: boolean;
|
|
223
235
|
};
|
|
224
236
|
} | {
|
|
@@ -261,6 +273,12 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
261
273
|
environment: "staging" | "production" | undefined;
|
|
262
274
|
json: boolean;
|
|
263
275
|
};
|
|
276
|
+
} | {
|
|
277
|
+
command: "rotate-access-token";
|
|
278
|
+
args: {
|
|
279
|
+
environment: "staging" | "production";
|
|
280
|
+
yes: boolean;
|
|
281
|
+
};
|
|
264
282
|
} | {
|
|
265
283
|
command: "set";
|
|
266
284
|
args: {
|
|
@@ -384,6 +402,9 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
384
402
|
}, {
|
|
385
403
|
command: "assets";
|
|
386
404
|
value: {
|
|
405
|
+
command: "configure";
|
|
406
|
+
value: Promise<void>;
|
|
407
|
+
} | {
|
|
387
408
|
command: "add";
|
|
388
409
|
value: Promise<void>;
|
|
389
410
|
} | {
|
|
@@ -416,6 +437,9 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
416
437
|
value: {
|
|
417
438
|
command: "status";
|
|
418
439
|
value: Promise<void>;
|
|
440
|
+
} | {
|
|
441
|
+
command: "rotate-access-token";
|
|
442
|
+
value: Promise<void>;
|
|
419
443
|
} | {
|
|
420
444
|
command: "set";
|
|
421
445
|
value: Promise<void>;
|
|
@@ -493,6 +517,9 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
493
517
|
run(context: import("cmd-ts/dist/cjs/argparser").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser").ParsingResult<{
|
|
494
518
|
command: "assets";
|
|
495
519
|
value: {
|
|
520
|
+
command: "configure";
|
|
521
|
+
value: Promise<void>;
|
|
522
|
+
} | {
|
|
496
523
|
command: "add";
|
|
497
524
|
value: Promise<void>;
|
|
498
525
|
} | {
|
|
@@ -525,6 +552,9 @@ declare const coreframeCli: Partial<import("cmd-ts/dist/cjs/argparser").Register
|
|
|
525
552
|
value: {
|
|
526
553
|
command: "status";
|
|
527
554
|
value: Promise<void>;
|
|
555
|
+
} | {
|
|
556
|
+
command: "rotate-access-token";
|
|
557
|
+
value: Promise<void>;
|
|
528
558
|
} | {
|
|
529
559
|
command: "set";
|
|
530
560
|
value: Promise<void>;
|
package/dist/coreframe.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{addAsset as e,buildAssets as t,checkAssets as n,printAssetResult as r,publishAssets as i}from"./assets.js";import{runDrizzleKitCommand as a}from"./db/drizzle-kit.js";import{seedLocalDb as o}from"./db/seed.js";import{migrateLocalDb as s}from"./db/migrate.js";import{resetLocalDb as c}from"./db/reset.js";import{setupLocalDb as l}from"./db/setup.js";import{ensureLocalDb as u}from"./db/ensure.js";import{migrateHeldMigrations as d}from"./db/migrate-held.js";import{checkSchemaPush as f}from"./db/schema-check.js";import{deployEnvironments as p,tauriReleasePlatforms as m}from"./deploy/config.js";import{discoverStagingUrl as h,reassignStagingHostname as g}from"./staging.js";import{discardPendingWorkerSecret as _,inspectWorkerEnvironment as v,queueWorkerSecret as y}from"./worker-environment.js";import{deployProject as b}from"./deploy.js";import{checkDeployMigrations as x}from"./deploy/check-migrations.js";import{devPlatforms as S,runDevServer as C}from"./dev.js";import{validateRecipeName as w}from"./recipes/manifest.js";import{ensureTemplateRemote as T}from"./upgrade/ensure-template-remote.js";import{applyRecipe as E,printAppliedRecipe as D}from"./recipes/apply.js";import{listRecipes as O,printRecipeCatalog as k}from"./recipes/list.js";import{run as A}from"./runner.js";import{typecheckProject as j}from"./typecheck.js";import{checkUpgradeSkillCurrent as M}from"./upgrade/check-skill-current.js";import{fastForwardTemplateFiles as N}from"./upgrade/fast-forward-template-files.js";import{syncCoreframePackages as P}from"./upgrade/sync-coreframe-packages.js";import*as F from"@clack/prompts";import{array as I,command as L,extendType as R,flag as z,multioption as B,number as V,oneOf as H,option as U,optional as W,positional as G,rest as K,string as q,subcommands as J}from"cmd-ts";const Y=[`Cookies and sessions are origin-scoped.`,`Exact-host passkeys may require re-registration unless a stable eligible parent RP ID was configured in advance.`,`External OAuth callbacks such as Google may need updating.`,`Database data and private backend resources are unchanged.`],X=R(q,{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}}),Z=R(q,{displayName:`recipe`,description:`Coreframe recipe name, such as yam-db.`,async from(e){return w(e)}}),Q=J({name:`coreframe`,cmds:{assets:J({name:`assets`,cmds:{add:L({name:`add`,args:{file:G({type:q,displayName:`file`,description:`Source image to add to the asset library.`}),id:U({long:`id`,type:q,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:U({long:`alt`,type:q,description:`Required alt text for the asset.`}),variantWidths:U({long:`widths`,type:W(X),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:z({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:L({name:`build`,args:{id:U({long:`id`,type:W(q),description:`Build only one logical asset ID.`}),noPull:z({long:`no-pull`,description:`Do not perform targeted Git LFS pulls for missing source objects.`,defaultValue:()=>!1}),json:z({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:L({name:`check`,args:{fix:z({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:z({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:L({name:`publish`,args:{outputDir:U({long:`output-dir`,type:W(q),description:`Root directory for additive local publish output. Defaults to COREFRAME_ASSETS_PUBLISH_DIR or release-upload.`}),json:z({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:L({name:`status`,args:{json:z({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:L({name:`deploy`,args:{environment:G({type:H(p),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:z({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:z({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:z({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:B({long:`tauri-local-platform`,type:I(H(m)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:U({long:`tauri-workflow-name`,type:W(q),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:B({long:`tauri-workflow-platform`,type:I(H(m)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:U({long:`tauri-workflow-ref`,type:W(q),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:z({long:`yes`,short:`y`,description:`Approve the production deployment confirmation.`,defaultValue:()=>!1})},handler:({environment:e,runMigrations:t,skipMigrations:n,skipPostDeployChecks:r,tauriLocalPlatforms:i,tauriWorkflowName:a,tauriWorkflowPlatforms:o,tauriWorkflowRef:s,yes:c})=>b([e,...t?[`--run-migrations`]:[],...n?[`--skip-migrations`]:[],...r?[`--skip-post-deploy-checks`]:[],...i.flatMap(e=>[`--tauri-local-platform`,e]),...a?[`--tauri-workflow-name`,a]:[],...o.flatMap(e=>[`--tauri-workflow-platform`,e]),...s?[`--tauri-workflow-ref`,s]:[],...c?[`--yes`]:[]])}),staging:J({name:`staging`,cmds:{reassign:L({name:`reassign`,args:{json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await g();if(e){console.log(JSON.stringify({...t,notices:Y}));return}console.log(t.url),console.error(`Staging hostname reassigned. ${Y.join(` `)}`)}}),url:L({name:`url`,args:{json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await h();console.log(e?JSON.stringify({url:t}):t)}})}}),secrets:J({name:`secrets`,cmds:{status:L({name:`status`,args:{environment:G({type:W(H(p)),displayName:`environment`,description:`Inspect staging, production, or both when omitted.`}),json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({environment:e,json:t})=>{let n=await v({environment:e});console.log(t?JSON.stringify(n):$(n))}}),set:L({name:`set`,args:{environment:G({type:H(p),displayName:`environment`,description:`Worker environment to update on its next deploy.`}),name:G({type:q,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{await y({environment:e,name:t,value:await ee(t)}),console.log(`Queued ${t} for ${e}. Run coreframe deploy ${e} to apply.`)}}),discard:L({name:`discard`,args:{environment:G({type:H(p),displayName:`environment`,description:`Worker environment with a queued secret update.`}),name:G({type:q,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{let n=await _({environment:e,name:t});console.log(n?`Discarded the queued ${t} update for ${e}.`:`No ${t} update is queued for ${e}.`)}})}}),typecheck:L({name:`typecheck`,args:{},handler:j}),dev:L({name:`dev`,args:{https:z({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:U({long:`host`,type:W(q),description:`Host address for the web development server.`}),managed:z({long:`managed`,description:`Run managed checkout synchronization even without an interactive terminal.`,defaultValue:()=>!1}),noManaged:z({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:G({type:W(H(S)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),device:G({type:W(q),displayName:`device`,description:`Device name or ID for iOS or Android development.`}),port:U({long:`port`,type:W(V),description:`Port for the web development server.`}),verbose:z({long:`verbose`,description:`Show the full native development output.`,defaultValue:()=>!1})},handler:({device:e,host:t,https:n,managed:r,noManaged:i,platform:a,port:o,verbose:s})=>C({device:e,host:t,https:n,managed:r,noManaged:i,platform:a??`web`,verbose:s,webPort:o})}),upgrade:J({name:`upgrade`,cmds:{"ensure-template-remote":L({name:`ensure-template-remote`,args:{remote:U({long:`remote`,type:q,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:z({long:`no-fetch`,description:`Do not fetch the remote after ensuring it exists.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t})=>T({fetch:!e,remote:t})}),"check-skill-current":L({name:`check-skill-current`,args:{target:U({long:`target`,type:q,description:`Target Coreframe template git ref.`}),apply:z({long:`apply`,description:`Replace the local upgrade skill with the target version when it differs.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>{process.exitCode=M({apply:e,target:t})}}),"fast-forward-template-files":L({name:`fast-forward-template-files`,args:{target:U({long:`target`,type:q,description:`Target Coreframe template git ref.`}),base:U({long:`base`,type:W(q),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:z({long:`apply`,description:`Write clean fast-forwards to the worktree.`,defaultValue:()=>!1})},handler:({apply:e,base:t,target:n})=>N({apply:e,base:t,target:n})}),"sync-coreframe-packages":L({name:`sync-coreframe-packages`,args:{target:U({long:`target`,type:q,description:`Target Coreframe template git ref.`}),apply:z({long:`apply`,description:`Update existing published Coreframe dependency specifications.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>P({apply:e,target:t})})}}),recipes:J({name:`recipes`,cmds:{apply:L({name:`apply`,args:{recipe:G({type:Z,displayName:`recipe`,description:`Recipe to prepare and apply.`}),target:U({long:`target`,type:W(q),description:`Coreframe Git ref. Defaults to package.json engines.coreframe.`}),remote:U({long:`remote`,type:q,defaultValue:()=>`template`,description:`Coreframe Git remote name.`}),noFetch:z({long:`no-fetch`,description:`Use already-fetched recipe refs without fetching the remote.`,defaultValue:()=>!1})},handler:({noFetch:e,recipe:t,remote:n,target:r})=>D(E({fetch:!e,name:t,remote:n,target:r}))}),list:L({name:`list`,args:{target:U({long:`target`,type:W(q),description:`Coreframe Git ref. Defaults to package.json engines.coreframe.`}),remote:U({long:`remote`,type:q,defaultValue:()=>`template`,description:`Coreframe Git remote name.`}),noFetch:z({long:`no-fetch`,description:`Use already-fetched recipe refs without fetching the remote.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t,target:n})=>k(O({fetch:!e,remote:t,target:n}))})}}),db:J({name:`db`,cmds:{ensure:L({name:`ensure`,args:{},handler:u}),generate:L({name:`generate`,args:{args:K({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>a(`generate`,e)}),migrate:L({name:`migrate`,args:{args:K({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>a(`migrate`,e)}),"migrate-held":L({name:`migrate-held`,args:{confirm:U({long:`confirm`,type:W(H([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>d({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":L({name:`migrate-local`,args:{},handler:s}),"migrations-check":L({name:`migrations-check`,args:{},handler:x}),reset:L({name:`reset`,args:{},handler:c}),seed:L({name:`seed`,args:{},handler:()=>o()}),"schema-check":L({name:`schema-check`,args:{seed:z({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>f({seed:e})}),setup:L({name:`setup`,args:{},handler:()=>l()}),studio:L({name:`studio`,args:{args:K({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>a(`studio`,e)})}})}});import.meta.main&&A(Q);function $(e){if(e.entries.length===0)return`No Worker environment values are declared in coreframe.config.ts.`;let t=e.entries.map(e=>[e.name,e.kind,e.local===`override`?`override`:`built-in default`,e.deployed.staging.replaceAll(`_`,` `),e.deployed.production.replaceAll(`_`,` `)]),n=[`Name`,`Kind`,`Local`,`Staging`,`Production`],r=n.map((e,n)=>Math.max(e.length,...t.map(e=>e[n].length))),i=e=>e.map((e,t)=>e.padEnd(r[t])).join(` `).trimEnd();return[i(n),i(r.map(e=>`-`.repeat(e))),...t].join(`
|
|
3
|
-
`)}async function
|
|
2
|
+
import{assertAssetCredentialProfile as e,configureAssetCredentials as t}from"./assets/credentials.js";import{addAsset as n,buildAssets as r,checkAssets as i,printAssetResult as a,publishAssets as o}from"./assets/workflows.js";import"./assets.js";import{runDrizzleKitCommand as s}from"./db/drizzle-kit.js";import{seedLocalDb as c}from"./db/seed.js";import{migrateLocalDb as l}from"./db/migrate.js";import{resetLocalDb as u}from"./db/reset.js";import{setupLocalDb as d}from"./db/setup.js";import{ensureLocalDb as f}from"./db/ensure.js";import{migrateHeldMigrations as p}from"./db/migrate-held.js";import{checkSchemaPush as m}from"./db/schema-check.js";import{rotateAccessTokenSigningSecret as h}from"./deploy/access-token-secret.js";import{deployEnvironments as g,tauriReleasePlatforms as _}from"./deploy/config.js";import{discoverStagingUrl as v,reassignStagingHostname as y}from"./staging.js";import{discardPendingWorkerSecret as b,inspectWorkerEnvironment as x,queueWorkerSecret as S}from"./worker-environment.js";import{deployProject as C}from"./deploy.js";import{checkDeployMigrations as w}from"./deploy/check-migrations.js";import{devPlatforms as T,runDevServer as E}from"./dev.js";import{validateRecipeName as ee}from"./recipes/manifest.js";import{ensureTemplateRemote as D}from"./upgrade/ensure-template-remote.js";import{applyRecipe as O,printAppliedRecipe as k}from"./recipes/apply.js";import{listRecipes as A,printRecipeCatalog as j}from"./recipes/list.js";import{run as M}from"./runner.js";import{typecheckProject as N}from"./typecheck.js";import{checkUpgradeSkillCurrent as P}from"./upgrade/check-skill-current.js";import{fastForwardTemplateFiles as F}from"./upgrade/fast-forward-template-files.js";import{syncCoreframePackages as I}from"./upgrade/sync-coreframe-packages.js";import*as L from"@clack/prompts";import{array as R,command as z,extendType as B,flag as V,multioption as H,number as U,oneOf as W,option as G,optional as K,positional as q,rest as J,string as Y,subcommands as X}from"cmd-ts";const Z=[`Cookies and sessions are origin-scoped.`,`Exact-host passkeys may require re-registration unless a stable eligible parent RP ID was configured in advance.`,`External OAuth callbacks such as Google may need updating.`,`Database data and private backend resources are unchanged.`],Q=B(Y,{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}}),te=B(Y,{displayName:`recipe`,description:`Coreframe recipe name, such as yam-db.`,async from(e){return ee(e)}}),$=X({name:`coreframe`,cmds:{assets:X({name:`assets`,cmds:{configure:z({name:`configure`,description:`Store a Backblaze B2 asset key in the operating system credential store.`,args:{profile:q({type:B(Y,{displayName:`profile`,async from(t){return e(t)}}),displayName:`profile`,description:`Credential profile named by coreframe.config.ts.`})},handler:async({profile:e})=>{let n=await L.text({message:`Backblaze B2 application key ID`,validate:e=>e?.trim()?void 0:`The application key ID is required.`});if(L.isCancel(n))throw Error(`Asset credential configuration was cancelled.`);let r=await L.password({message:`Backblaze B2 application key`,validate:e=>e?void 0:`The application key is required.`});if(L.isCancel(r))throw Error(`Asset credential configuration was cancelled.`);await t({applicationKey:r,applicationKeyId:n.trim(),profile:e}),console.log(`Stored Coreframe asset credential profile "${e}".`)}}),add:z({name:`add`,args:{file:q({type:Y,displayName:`file`,description:`Source image to add to the asset library.`}),id:G({long:`id`,type:Y,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:G({long:`alt`,type:Y,description:`Required alt text for the asset.`}),variantWidths:G({long:`widths`,type:K(Q),description:`Generate exactly these comma-separated widths instead of the default ladder.`}),json:V({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({alt:e,file:t,id:r,json:i,variantWidths:o})=>a(await n({alt:e,file:t,id:r,variantWidths:o}),i)}),build:z({name:`build`,args:{id:G({long:`id`,type:K(Y),description:`Build only one logical asset ID.`}),json:V({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({id:e,json:t})=>a(await r({id:e}),t)}),check:z({name:`check`,args:{fix:V({long:`fix`,description:`Build missing generated variants when possible.`,defaultValue:()=>!1}),json:V({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({fix:e,json:t})=>{let n=await i({fix:e});a(n,t),n.ok||(process.exitCode=1)}}),publish:z({name:`publish`,args:{json:V({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await o();a(t,e),t.ok||(process.exitCode=1)}}),status:z({name:`status`,args:{json:V({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await i();a(t,e),t.ok||(process.exitCode=1)}})}}),deploy:z({name:`deploy`,args:{environment:q({type:W(g),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:V({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:V({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:V({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:H({long:`tauri-local-platform`,type:R(W(_)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:G({long:`tauri-workflow-name`,type:K(Y),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:H({long:`tauri-workflow-platform`,type:R(W(_)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:G({long:`tauri-workflow-ref`,type:K(Y),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:V({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})=>C([e,...t?[`--run-migrations`]:[],...n?[`--skip-migrations`]:[],...r?[`--skip-post-deploy-checks`]:[],...i.flatMap(e=>[`--tauri-local-platform`,e]),...a?[`--tauri-workflow-name`,a]:[],...o.flatMap(e=>[`--tauri-workflow-platform`,e]),...s?[`--tauri-workflow-ref`,s]:[],...c?[`--yes`]:[]])}),staging:X({name:`staging`,cmds:{reassign:z({name:`reassign`,args:{json:V({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await y();if(e){console.log(JSON.stringify({...t,notices:Z}));return}console.log(t.url),console.error(`Staging hostname reassigned. ${Z.join(` `)}`)}}),url:z({name:`url`,args:{json:V({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await v();console.log(e?JSON.stringify({url:t}):t)}})}}),secrets:X({name:`secrets`,cmds:{"rotate-access-token":z({name:`rotate-access-token`,args:{environment:q({type:W(g),displayName:`environment`,description:`Worker environment whose access JWTs should be invalidated.`}),yes:V({long:`yes`,short:`y`,description:`Confirm immediate signing-secret replacement and invalidation of outstanding access JWTs.`,defaultValue:()=>!1})},handler:async({environment:e,yes:t})=>{if(!t)throw Error(`Access-token signing-secret rotation requires --yes because it immediately invalidates outstanding access JWTs.`);await h({environment:e}),console.log(`Rotated Coreframe access-token signing material for ${e}. Clients must remint outstanding access JWTs.`)}}),status:z({name:`status`,args:{environment:q({type:K(W(g)),displayName:`environment`,description:`Inspect staging, production, or both when omitted.`}),json:V({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({environment:e,json:t})=>{let n=await x({environment:e});console.log(t?JSON.stringify(n):ne(n))}}),set:z({name:`set`,args:{environment:q({type:W(g),displayName:`environment`,description:`Worker environment to update on its next deploy.`}),name:q({type:Y,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{await S({environment:e,name:t,value:await re(t)}),console.log(`Queued ${t} for ${e}. Run coreframe deploy ${e} to apply.`)}}),discard:z({name:`discard`,args:{environment:q({type:W(g),displayName:`environment`,description:`Worker environment with a queued secret update.`}),name:q({type:Y,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{let n=await b({environment:e,name:t});console.log(n?`Discarded the queued ${t} update for ${e}.`:`No ${t} update is queued for ${e}.`)}})}}),typecheck:z({name:`typecheck`,args:{},handler:N}),dev:z({name:`dev`,args:{https:V({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:G({long:`host`,type:K(Y),description:`Host address for the web development server.`}),managed:V({long:`managed`,description:`Run managed checkout synchronization even without an interactive terminal.`,defaultValue:()=>!1}),noManaged:V({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:q({type:K(W(T)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),device:q({type:K(Y),displayName:`device`,description:`Device name or ID for iOS or Android development.`}),port:G({long:`port`,type:K(U),description:`Port for the web development server.`}),verbose:V({long:`verbose`,description:`Show the full native development output.`,defaultValue:()=>!1})},handler:({device:e,host:t,https:n,managed:r,noManaged:i,platform:a,port:o,verbose:s})=>E({device:e,host:t,https:n,managed:r,noManaged:i,platform:a??`web`,verbose:s,webPort:o})}),upgrade:X({name:`upgrade`,cmds:{"ensure-template-remote":z({name:`ensure-template-remote`,args:{remote:G({long:`remote`,type:Y,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:V({long:`no-fetch`,description:`Do not fetch the remote after ensuring it exists.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t})=>D({fetch:!e,remote:t})}),"check-skill-current":z({name:`check-skill-current`,args:{target:G({long:`target`,type:Y,description:`Target Coreframe template git ref.`}),apply:V({long:`apply`,description:`Replace the local upgrade skill with the target version when it differs.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>{process.exitCode=P({apply:e,target:t})}}),"fast-forward-template-files":z({name:`fast-forward-template-files`,args:{target:G({long:`target`,type:Y,description:`Target Coreframe template git ref.`}),base:G({long:`base`,type:K(Y),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:V({long:`apply`,description:`Write clean fast-forwards to the worktree.`,defaultValue:()=>!1})},handler:({apply:e,base:t,target:n})=>F({apply:e,base:t,target:n})}),"sync-coreframe-packages":z({name:`sync-coreframe-packages`,args:{target:G({long:`target`,type:Y,description:`Target Coreframe template git ref.`}),apply:V({long:`apply`,description:`Update existing published Coreframe dependency specifications.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>I({apply:e,target:t})})}}),recipes:X({name:`recipes`,cmds:{apply:z({name:`apply`,args:{recipe:q({type:te,displayName:`recipe`,description:`Recipe to prepare and apply.`}),target:G({long:`target`,type:K(Y),description:`Coreframe Git ref. Defaults to package.json engines.coreframe.`}),remote:G({long:`remote`,type:Y,defaultValue:()=>`template`,description:`Coreframe Git remote name.`}),noFetch:V({long:`no-fetch`,description:`Use already-fetched recipe refs without fetching the remote.`,defaultValue:()=>!1})},handler:({noFetch:e,recipe:t,remote:n,target:r})=>k(O({fetch:!e,name:t,remote:n,target:r}))}),list:z({name:`list`,args:{target:G({long:`target`,type:K(Y),description:`Coreframe Git ref. Defaults to package.json engines.coreframe.`}),remote:G({long:`remote`,type:Y,defaultValue:()=>`template`,description:`Coreframe Git remote name.`}),noFetch:V({long:`no-fetch`,description:`Use already-fetched recipe refs without fetching the remote.`,defaultValue:()=>!1})},handler:({noFetch:e,remote:t,target:n})=>j(A({fetch:!e,remote:t,target:n}))})}}),db:X({name:`db`,cmds:{ensure:z({name:`ensure`,args:{},handler:f}),generate:z({name:`generate`,args:{args:J({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>s(`generate`,e)}),migrate:z({name:`migrate`,args:{args:J({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>s(`migrate`,e)}),"migrate-held":z({name:`migrate-held`,args:{confirm:G({long:`confirm`,type:K(W([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>p({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":z({name:`migrate-local`,args:{},handler:l}),"migrations-check":z({name:`migrations-check`,args:{},handler:w}),reset:z({name:`reset`,args:{},handler:u}),seed:z({name:`seed`,args:{},handler:()=>c()}),"schema-check":z({name:`schema-check`,args:{seed:V({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>m({seed:e})}),setup:z({name:`setup`,args:{},handler:()=>d()}),studio:z({name:`studio`,args:{args:J({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>s(`studio`,e)})}})}});import.meta.main&&M($);function ne(e){if(e.entries.length===0)return`No Worker environment values are declared in coreframe.config.ts.`;let t=e.entries.map(e=>[e.name,e.kind,e.local===`override`?`override`:`built-in default`,e.deployed.staging.replaceAll(`_`,` `),e.deployed.production.replaceAll(`_`,` `)]),n=[`Name`,`Kind`,`Local`,`Staging`,`Production`],r=n.map((e,n)=>Math.max(e.length,...t.map(e=>e[n].length))),i=e=>e.map((e,t)=>e.padEnd(r[t])).join(` `).trimEnd();return[i(n),i(r.map(e=>`-`.repeat(e))),...t].join(`
|
|
3
|
+
`)}async function re(e){if(!process.stdin.isTTY){let e=``;for await(let t of process.stdin)e+=t.toString();return e.replace(/\r?\n$/,``)}let t=await L.password({message:`Enter the new value for ${e}`});if(L.isCancel(t))throw L.cancel(`Secret update cancelled.`),Error(`Secret update cancelled.`);return t}export{$ as coreframeCli};
|
package/dist/db/migrate-held.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{createProjectDeployDatabase as e}from"./project.js";import{collectHeldMigrationFiles as t,formatMigrationList as n}from"../deploy/migrations.js";import{
|
|
2
|
+
import{createProjectDeployDatabase as e}from"./project.js";import{collectHeldMigrationFiles as t,formatMigrationList as n}from"../deploy/migrations.js";import{readFile as r}from"node:fs/promises";import{createHash as i}from"node:crypto";const a=`--confirm=run-held-migrations`,o=[`-- Held migration:`,`-- Safe after:`,`-- Verification:`];async function s({argv:i=process.argv.slice(2),env:o=process.env}={}){let s=await t();if(console.log(`Held migration files:`),console.log(n(s)),!i.includes(a))throw Error(`Held migrations require explicit confirmation. Re-run with ${a}.`);if(s.length===0){console.log(`No held migrations to run.`);return}let d=await e({env:o});try{await u(d);for(let e of s){let t=await r(e.path,`utf8`);c(t,e.relativePath);let n=l(t),i=await d.execute(f(d.dialect,e.id));if(i.rows.length===1){if(i.rows[0]?.sha256!==n)throw Error(`Held migration ${e.relativePath} changed after it was applied.`);console.log(`Skipping already-applied held migration ${e.relativePath}`);continue}console.log(`Applying held migration ${e.relativePath}`),await h(d,async r=>{await r.execute(t),await r.execute(p(d.dialect,e.id,n))})}}finally{await d.close?.()}}function c(e,t){let n=o.filter(t=>!e.includes(t));if(n.length>0)throw Error(`${t} is missing held migration header fields: ${n.join(`, `)}`)}function l(e){return i(`sha256`).update(e).digest(`hex`)}async function u(e){for(let t of d(e.dialect))await e.execute(t)}function d(e){switch(e){case`postgres`:return[`create schema if not exists drizzle`,`create table if not exists drizzle.__held_migrations (
|
|
3
3
|
id text primary key,
|
|
4
4
|
sha256 text not null,
|
|
5
5
|
applied_at timestamptz not null default now()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DeployEnvironment } from "./config.js";
|
|
2
|
+
|
|
3
|
+
//#region deploy/access-token-secret.d.ts
|
|
4
|
+
declare const accessTokenSigningSecretName = "COREFRAME_ACCESS_TOKEN_SECRET";
|
|
5
|
+
type SecretNamesReader = (environment: DeployEnvironment, cwd: string) => Promise<readonly string[]>;
|
|
6
|
+
type SecretWriter = (environment: DeployEnvironment, name: string, value: string, cwd: string) => Promise<void>;
|
|
7
|
+
type AccessTokenSigningSecretOptions = {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
environment: DeployEnvironment;
|
|
10
|
+
generateSecret?: () => string;
|
|
11
|
+
listSecretNames?: SecretNamesReader;
|
|
12
|
+
};
|
|
13
|
+
type PreparedAccessTokenSigningSecret = {
|
|
14
|
+
provisioned: boolean;
|
|
15
|
+
value?: string;
|
|
16
|
+
};
|
|
17
|
+
declare function prepareAccessTokenSigningSecret({
|
|
18
|
+
cwd,
|
|
19
|
+
environment,
|
|
20
|
+
generateSecret,
|
|
21
|
+
listSecretNames
|
|
22
|
+
}: AccessTokenSigningSecretOptions): Promise<PreparedAccessTokenSigningSecret>;
|
|
23
|
+
declare function verifyAccessTokenSigningSecret({
|
|
24
|
+
cwd,
|
|
25
|
+
environment,
|
|
26
|
+
listSecretNames
|
|
27
|
+
}: Omit<AccessTokenSigningSecretOptions, "generateSecret">): Promise<void>;
|
|
28
|
+
declare function rotateAccessTokenSigningSecret({
|
|
29
|
+
cwd,
|
|
30
|
+
environment,
|
|
31
|
+
generateSecret,
|
|
32
|
+
listSecretNames,
|
|
33
|
+
writeSecret
|
|
34
|
+
}: AccessTokenSigningSecretOptions & {
|
|
35
|
+
writeSecret?: SecretWriter;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
declare function generateAccessTokenSigningSecret(): string;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { PreparedAccessTokenSigningSecret, accessTokenSigningSecretName, generateAccessTokenSigningSecret, prepareAccessTokenSigningSecret, rotateAccessTokenSigningSecret, verifyAccessTokenSigningSecret };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e,{SubprocessError as t}from"nano-spawn";import{randomBytes as n}from"node:crypto";const r=`COREFRAME_ACCESS_TOKEN_SECRET`;async function i({cwd:e=process.cwd(),environment:t,generateSecret:n=s,listSecretNames:i=c}){try{if((await i(t,e)).includes(`COREFRAME_ACCESS_TOKEN_SECRET`))return{provisioned:!1}}catch(e){if(!u(e))throw Error(`Could not inspect the ${t} Worker before provisioning ${r}. Refusing to risk replacing an existing signing secret.`,{cause:e})}return{provisioned:!0,value:n()}}async function a({cwd:e=process.cwd(),environment:t,listSecretNames:n=c}){if(!(await n(t,e)).includes(`COREFRAME_ACCESS_TOKEN_SECRET`))throw Error(`Cloudflare deployed the ${t} Worker without ${r}.`)}async function o({cwd:e=process.cwd(),environment:t,generateSecret:n=s,listSecretNames:i=c,writeSecret:o=l}){await o(t,r,n(),e),await a({cwd:e,environment:t,listSecretNames:i})}function s(){return n(32).toString(`base64url`)}async function c(t,n){let{stdout:r}=await e(`pnpm`,[`exec`,`wrangler`,`secret`,`list`,`--env`,t,`--format`,`json`],{cwd:n,stdin:`ignore`}),i=JSON.parse(r);if(!Array.isArray(i))throw Error(`Wrangler returned an invalid Worker secret list.`);return i.flatMap(e=>e&&typeof e==`object`&&`name`in e&&typeof e.name==`string`?[e.name]:[])}async function l(t,n,r,i){await e(`pnpm`,[`exec`,`wrangler`,`secret`,`put`,n,`--env`,t],{cwd:i,stdin:{string:r},stdout:`inherit`,stderr:`inherit`})}function u(e){return e instanceof t&&e.stderr.includes(`not found`)&&e.stderr.includes("If this is a new Worker, run `wrangler deploy` first")}export{r as accessTokenSigningSecretName,s as generateAccessTokenSigningSecret,i as prepareAccessTokenSigningSecret,o as rotateAccessTokenSigningSecret,a as verifyAccessTokenSigningSecret};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{tauriReleasePlatforms as e}from"./config.js";import{randomUUID as t}from"node:crypto";import{access as n,cp as r,mkdir as i,readFile as a,rm as o,stat as s,writeFile as c}from"node:fs/promises";import{basename as l,dirname as u,join as d,relative as f,resolve as p,sep as m}from"node:path";import h from"nano-spawn";import{constants as g}from"node:fs";import{setTimeout as _}from"node:timers/promises";import{glob as v}from"tinyglobby";const y={android:{artifactGlobs:[`src-tauri/target/**/*.sig`,`src-tauri/gen/android/**/*.sig`],buildArgs:[`tauri`,`android`,`build`,`--target`,`aarch64-linux-android`],targetTriple:`aarch64-linux-android`,updatePlatform:`android-aarch64`},ios:{artifactGlobs:[`src-tauri/target/**/*.sig`,`src-tauri/gen/apple/**/*.sig`],buildArgs:[`tauri`,`ios`,`build`,`--target`,`aarch64-apple-ios`],targetTriple:`aarch64-apple-ios`,updatePlatform:`ios-aarch64`},linux:{artifactGlobs:[`src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`x86_64-unknown-linux-gnu`,`--`,`--locked`],targetTriple:`x86_64-unknown-linux-gnu`,updatePlatform:`linux-x86_64`},macos:{artifactGlobs:[`src-tauri/target/aarch64-apple-darwin/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`aarch64-apple-darwin`,`--`,`--locked`],targetTriple:`aarch64-apple-darwin`,updatePlatform:`darwin-aarch64`},windows:{artifactGlobs:[`src-tauri/target/x86_64-pc-windows-msvc/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`x86_64-pc-windows-msvc`,`--`,`--locked`],targetTriple:`x86_64-pc-windows-msvc`,updatePlatform:`windows-x86_64`}};function b(e){return e?e.split(/[,\s]+/u).filter(Boolean).map(e=>x(e)):[]}function x(t){if(X(t))return t;throw Error(`Invalid Tauri release platform "${t}". Expected one of: ${e.join(`, `)}.`)}function S({args:e,config:t={},env:n}){let r=Y([...t.localPlatforms??[],...b(n.TAURI_RELEASE_LOCAL_PLATFORMS),...e.tauriLocalPlatforms]),i=Y([...t.workflowPlatforms??[],...b(n.TAURI_RELEASE_WORKFLOW_PLATFORMS),...e.tauriWorkflowPlatforms]),a=r.filter(e=>i.includes(e));if(a.length>0)throw Error(`Build each Tauri release platform either locally or through the workflow, not both: ${a.join(`, `)}.`);return{artifactDownloadDir:n.TAURI_RELEASE_ARTIFACT_DOWNLOAD_DIR??t.artifactDownloadDir??`.coreframe/tauri-artifacts`,b2ApplicationKey:n.B2_APPLICATION_KEY??n.B2_SECRET_ACCESS_KEY,b2ApplicationKeyId:n.B2_APPLICATION_KEY_ID??n.B2_KEY_ID??n.B2_ACCESS_KEY_ID,b2Bucket:t.b2Bucket??n.B2_BUCKET,enabled:r.length>0||i.length>0,latestManifestPath:n.TAURI_RELEASE_LATEST_MANIFEST_PATH??t.latestManifestPath??`latest.json`,localPlatforms:r,platform:L(t.platform,n),publicBaseUrl:t.publicBaseUrl??n.TAURI_RELEASE_PUBLIC_BASE_URL??n.UPDATE_PUBLIC_BASE_URL,publicPathPrefix:n.TAURI_RELEASE_PUBLIC_PATH_PREFIX??t.publicPathPrefix??n.UPDATE_PUBLIC_PATH_PREFIX??``,releaseNotesPath:n.TAURI_RELEASE_NOTES_FILE??t.releaseNotesPath,releasePath:n.TAURI_RELEASE_PATH??t.releasePath??"releases/${version}",releaseUploadDir:n.TAURI_RELEASE_UPLOAD_DIR??t.releaseUploadDir??`release-upload`,signingPrivateKey:n.TAURI_SIGNING_PRIVATE_KEY,workflowName:e.tauriWorkflowName??n.TAURI_RELEASE_WORKFLOW_NAME??t.workflowName??`tauri-release-artifacts.yml`,workflowPlatforms:i,workflowPollIntervalMs:J(n.TAURI_RELEASE_WORKFLOW_POLL_SECONDS,`TAURI_RELEASE_WORKFLOW_POLL_SECONDS`)*1e3||t.workflowPollIntervalMs||15e3,workflowRef:e.tauriWorkflowRef??n.TAURI_RELEASE_WORKFLOW_REF??t.workflowRef,workflowTimeoutMs:J(n.TAURI_RELEASE_WORKFLOW_TIMEOUT_MINUTES,`TAURI_RELEASE_WORKFLOW_TIMEOUT_MINUTES`)*6e4||t.workflowTimeoutMs||36e5}}async function C(e,t=process.cwd()){if(!e.enabled)return;let n=[[`TAURI_RELEASE_PUBLIC_BASE_URL`,e.publicBaseUrl],[`B2_APPLICATION_KEY_ID`,e.b2ApplicationKeyId],[`B2_APPLICATION_KEY`,e.b2ApplicationKey],[`B2_BUCKET`,e.b2Bucket]].flatMap(([e,t])=>t?[]:[e]);if(n.length>0)throw Error(`Missing required Tauri release configuration: ${n.join(`, `)}`);try{new URL(e.publicBaseUrl)}catch{throw Error(`Invalid TAURI_RELEASE_PUBLIC_BASE_URL: ${e.publicBaseUrl}`)}let r=d(t,`src-tauri/tauri.conf.json`);if(!await Z(r))throw Error(`Tauri release platforms were requested, but src-tauri/tauri.conf.json does not exist.`);if(w(await a(r,`utf8`),r),e.localPlatforms.length>0&&!e.signingPrivateKey)throw Error(`TAURI_SIGNING_PRIVATE_KEY is required for local Tauri release builds.`);e.workflowPlatforms.length>0&&await h(`gh`,[`workflow`,`view`,e.workflowName,`--ref`,e.workflowRef??await H()],{stdin:`ignore`}),await h(`b2`,[`version`],{stdin:`ignore`})}function w(e,t=`src-tauri/tauri.conf.json`){let n=JSON.parse(e).identifier;if(typeof n!=`string`||n===`com.example`||n.startsWith(`com.example.`))throw Error(`${t} must use an organization-owned reverse-domain identifier before building release artifacts.`);return n}async function T(e,t,n=process.cwd()){if(!e.enabled)return{files:[]};let r=d(n,`src-tauri/tauri.conf.json`),i=d(n,`src-tauri/Cargo.toml`),o=d(n,`src-tauri/Cargo.lock`),s=await a(r,`utf8`),l=await a(i,`utf8`),u=z(l,i),f=[{path:r,source:s},{path:i,source:l}],p=[],m=JSON.parse(s);if(m.version=t,m.bundle={...m.bundle??{},createUpdaterArtifacts:!0},p.push({path:r,source:`${JSON.stringify(m,null,2)}\n`}),p.push({path:i,source:B(l,u,t,i)}),await Z(o)){let e=await a(o,`utf8`);f.push({path:o,source:e}),p.push({path:o,source:V(e,u,t,o)})}for(let e of p)await c(e.path,e.source);return{files:f}}async function E(e){for(let t of e.files)await c(t.path,t.source)}async function D({env:e,plan:t,releaseName:n,version:r}){if(!t.enabled)return;await U(t.artifactDownloadDir),await U(t.releaseUploadDir);let i=t.workflowPlatforms.length>0?await j({plan:t,releaseName:n,version:r}):void 0;for(let n of t.localPlatforms)await P(n,t,e);if(i){let e=await M(t,i.runNameMarker);await h(`gh`,[`run`,`download`,String(e),`--dir`,t.artifactDownloadDir],{stdio:`inherit`})}return A({plan:t,version:r})}async function O(e,t){await h(`b2`,[`account`,`authorize`,e.b2ApplicationKeyId,e.b2ApplicationKey],{stdin:`ignore`});let n=(await v(`**/*`,{cwd:t.uploadDir,onlyFiles:!0})).sort((e,n)=>e===t.manifestPath?1:n===t.manifestPath?-1:e.localeCompare(n));for(let r of n)await h(`b2`,[`file`,`upload`,e.b2Bucket,d(t.uploadDir,r),r],{stdin:`ignore`}),console.log(`Uploaded ${r}`)}function k({artifacts:e,notes:t,pubDate:n=new Date,version:r}){return{...t?{notes:t}:{},platforms:Object.fromEntries(e.map(e=>[e.updatePlatform,{signature:e.signature,url:e.url}])),pub_date:n.toISOString(),version:r}}async function A({plan:e,version:t}){let n=q(e.publicPathPrefix,G(e.releasePath,t)),o=q(e.publicPathPrefix,e.latestManifestPath),s=[...e.localPlatforms,...e.workflowPlatforms],f=[];for(let o of s){let s=e.platform[o],c=e.localPlatforms.includes(o)?`.`:d(e.artifactDownloadDir,`tauri-${o}-${t}`),p=await F({globs:e.localPlatforms.includes(o)?s.artifactGlobs:[`**/*.sig`],sourceRoot:c,version:t}),m=q(n,s.updatePlatform),h=q(m,l(p.artifactPath)),g=q(m,l(p.signaturePath)),_=d(e.releaseUploadDir,...h.split(`/`)),v=d(e.releaseUploadDir,...g.split(`/`)),y=(await a(p.signaturePath,`utf8`)).trim();await i(u(_),{recursive:!0}),await r(p.artifactPath,_),await r(p.signaturePath,v),f.push({signature:y,updatePlatform:s.updatePlatform,url:K(e.publicBaseUrl,h)})}let p=k({artifacts:f,notes:e.releaseNotesPath?await a(e.releaseNotesPath,`utf8`):void 0,version:t}),m=d(e.releaseUploadDir,...o.split(`/`));return await i(u(m),{recursive:!0}),await c(m,`${JSON.stringify(p,null,2)}\n`),{manifestPath:o,uploadDir:e.releaseUploadDir}}async function j({plan:n,releaseName:r,version:i}){let a=n.workflowRef??await H(),o=`coreframe-${Date.now()}-${t().slice(0,8)}`;return await h(`gh`,[`workflow`,`run`,n.workflowName,`--ref`,a,`-f`,`release_name=${r}`,`-f`,`run_id=${o}`,`-f`,`version=${i}`,...e.flatMap(e=>[`-f`,`${e}=${n.workflowPlatforms.includes(e)?`true`:`false`}`])],{stdin:`ignore`}),{runNameMarker:o}}async function M(e,t){let n=Date.now()+e.workflowTimeoutMs;for(;Date.now()<n;){let n=await N(e,t);if(n?.status===`completed`){if(n.conclusion===`success`)return n.databaseId;throw Error(`Tauri release workflow ${n.databaseId} finished with ${n.conclusion}.`)}await _(e.workflowPollIntervalMs)}throw Error(`Timed out waiting for Tauri release workflow after ${Math.round(e.workflowTimeoutMs/6e4)} minutes.`)}async function N(e,t){let{stdout:n}=await h(`gh`,[`run`,`list`,`--workflow`,e.workflowName,`--event`,`workflow_dispatch`,`--json`,`conclusion,databaseId,displayTitle,status`,`--limit`,`50`],{stdin:`ignore`});return JSON.parse(n).find(e=>e.displayTitle.includes(t))}async function P(e,t,n){await h(`pnpm`,t.platform[e].buildArgs,{env:n,stdio:`inherit`})}async function F({globs:e,sourceRoot:t,version:n}){let r=await v(e,{cwd:t,absolute:!0,onlyFiles:!0}),i=[];for(let e of r){let t=e.replace(/\.sig$/u,``);await Q(t)&&i.push({artifactPath:t,signaturePath:e})}if(i.length===0)throw Error(`Could not find a Tauri updater artifact and .sig pair under ${t}.`);return i.sort((e,t)=>{let r=I(e.artifactPath,n);return I(t.artifactPath,n)-r||e.artifactPath.localeCompare(t.artifactPath)})[0]}function I(e,t){let n=0;return e.includes(t)&&(n+=100),/\.(app\.tar\.gz|AppImage\.tar\.gz|zip)$/u.test(e)&&(n+=10),/\.(exe|msi|dmg|AppImage|deb|rpm|apk|aab|ipa)$/u.test(e)&&(n+=5),n}function L(t={},n){return Object.fromEntries(e.map(e=>{let r=t[e]??{},i={...y[e],...r},a=n[`TAURI_RELEASE_${e.toUpperCase()}_TARGET_TRIPLE`],o=n[`TAURI_RELEASE_${e.toUpperCase()}_UPDATE_PLATFORM`],s=a??i.targetTriple,c=r.buildArgs?r.buildArgs:R(e,s);return[e,{...i,artifactGlobs:i.artifactGlobs??y[e].artifactGlobs,buildArgs:c,targetTriple:s,updatePlatform:o??i.updatePlatform}]}))}function R(e,t){return e===`android`?[`tauri`,`android`,`build`,`--target`,t]:e===`ios`?[`tauri`,`ios`,`build`,`--target`,t]:[`tauri`,`build`,`--target`,t,`--`,`--locked`]}function z(e,t){let n=/^\[package\][\s\S]*?^name = "(?<name>[^"]+)"/mu.exec(e);if(!n?.groups?.name)throw Error(`${t} is missing a [package] name.`);return n.groups.name}function B(e,t,n,r){let i=RegExp(`(^\\[package\\][\\s\\S]*?^name = "${$(t)}"[\\s\\S]*?^version = ")[^"]+(")`,`mu`),a=e.replace(i,`$1${n}$2`);if(a===e)throw Error(`${r} is missing a package version for ${t}.`);return a}function V(e,t,n,r){let i=RegExp(`(\\[\\[package\\]\\]\\nname = "${$(t)}"\\nversion = ")[^"]+(")`,`u`),a=e.replace(i,`$1${n}$2`);if(a===e)throw Error(`${r} is missing a package lock entry for ${t}.`);return a}async function H(){let{stdout:e}=await h(`git`,[`rev-parse`,`--abbrev-ref`,`HEAD`],{stdin:`ignore`}),t=e.trim();if(t!==`HEAD`)return t;let{stdout:n}=await h(`git`,[`rev-parse`,`HEAD`],{stdin:`ignore`});return n.trim()}async function U(e){W(e),await o(e,{force:!0,recursive:!0}),await i(e,{recursive:!0})}function W(e){let t=p(e),n=f(process.cwd(),t).split(m).join(`/`);if(n===``||n===`.`||n.startsWith(`../`)||n===`src-tauri`||n.startsWith(`src-tauri/`)||n===`tauri`||n.startsWith(`tauri/`))throw Error(`Refusing to reset unsafe Tauri release directory: ${e}`)}function G(e,t){return e.replaceAll("${version}",t)}function K(e,t){let n=new URL(e),r=q(n.pathname.replace(/\/+$/u,``),t);return n.pathname=r.startsWith(`/`)?r:`/${r}`,n.toString()}function q(...e){return e.flatMap(e=>e.split(`/`)).map(e=>e.trim()).filter(Boolean).join(`/`)}function J(e,t){if(!e)return 0;let n=Number(e);if(!Number.isInteger(n)||n<=0)throw Error(`${t} must be a positive integer.`);return n}function Y(e){return[...new Set(e)]}function X(t){return e.includes(t)}async function Z(e){try{return await n(e,g.F_OK),!0}catch{return!1}}async function Q(e){try{return(await s(e)).isFile()}catch{return!1}}function $(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,`\\$&`)}export{A as createTauriReleaseUpload,k as createTauriUpdateManifest,x as parseTauriReleasePlatform,b as parseTauriReleasePlatformList,D as prepareTauriRelease,S as resolveTauriReleasePlan,E as restoreTauriReleaseMetadata,O as uploadTauriReleaseToB2,w as validateTauriReleaseIdentifier,C as validateTauriReleasePlan,T as writeTauriReleaseMetadata};
|
|
1
|
+
import{tauriReleasePlatforms as e}from"./config.js";import{access as t,cp as n,mkdir as r,readFile as i,rm as a,stat as o,writeFile as s}from"node:fs/promises";import{basename as c,dirname as l,join as u,relative as d,resolve as f,sep as p}from"node:path";import m from"nano-spawn";import{randomUUID as h}from"node:crypto";import{constants as g}from"node:fs";import{setTimeout as _}from"node:timers/promises";import{glob as v}from"tinyglobby";const y={android:{artifactGlobs:[`src-tauri/target/**/*.sig`,`src-tauri/gen/android/**/*.sig`],buildArgs:[`tauri`,`android`,`build`,`--target`,`aarch64-linux-android`],targetTriple:`aarch64-linux-android`,updatePlatform:`android-aarch64`},ios:{artifactGlobs:[`src-tauri/target/**/*.sig`,`src-tauri/gen/apple/**/*.sig`],buildArgs:[`tauri`,`ios`,`build`,`--target`,`aarch64-apple-ios`],targetTriple:`aarch64-apple-ios`,updatePlatform:`ios-aarch64`},linux:{artifactGlobs:[`src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`x86_64-unknown-linux-gnu`,`--`,`--locked`],targetTriple:`x86_64-unknown-linux-gnu`,updatePlatform:`linux-x86_64`},macos:{artifactGlobs:[`src-tauri/target/aarch64-apple-darwin/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`aarch64-apple-darwin`,`--`,`--locked`],targetTriple:`aarch64-apple-darwin`,updatePlatform:`darwin-aarch64`},windows:{artifactGlobs:[`src-tauri/target/x86_64-pc-windows-msvc/release/bundle/**/*.sig`,`src-tauri/target/release/bundle/**/*.sig`,`src-tauri/target/**/release/bundle/**/*.sig`],buildArgs:[`tauri`,`build`,`--target`,`x86_64-pc-windows-msvc`,`--`,`--locked`],targetTriple:`x86_64-pc-windows-msvc`,updatePlatform:`windows-x86_64`}};function b(e){return e?e.split(/[,\s]+/u).filter(Boolean).map(e=>x(e)):[]}function x(t){if(X(t))return t;throw Error(`Invalid Tauri release platform "${t}". Expected one of: ${e.join(`, `)}.`)}function S({args:e,config:t={},env:n}){let r=Y([...t.localPlatforms??[],...b(n.TAURI_RELEASE_LOCAL_PLATFORMS),...e.tauriLocalPlatforms]),i=Y([...t.workflowPlatforms??[],...b(n.TAURI_RELEASE_WORKFLOW_PLATFORMS),...e.tauriWorkflowPlatforms]),a=r.filter(e=>i.includes(e));if(a.length>0)throw Error(`Build each Tauri release platform either locally or through the workflow, not both: ${a.join(`, `)}.`);return{artifactDownloadDir:n.TAURI_RELEASE_ARTIFACT_DOWNLOAD_DIR??t.artifactDownloadDir??`.coreframe/tauri-artifacts`,b2ApplicationKey:n.B2_APPLICATION_KEY??n.B2_SECRET_ACCESS_KEY,b2ApplicationKeyId:n.B2_APPLICATION_KEY_ID??n.B2_KEY_ID??n.B2_ACCESS_KEY_ID,b2Bucket:t.b2Bucket??n.B2_BUCKET,enabled:r.length>0||i.length>0,latestManifestPath:n.TAURI_RELEASE_LATEST_MANIFEST_PATH??t.latestManifestPath??`latest.json`,localPlatforms:r,platform:L(t.platform,n),publicBaseUrl:t.publicBaseUrl??n.TAURI_RELEASE_PUBLIC_BASE_URL??n.UPDATE_PUBLIC_BASE_URL,publicPathPrefix:n.TAURI_RELEASE_PUBLIC_PATH_PREFIX??t.publicPathPrefix??n.UPDATE_PUBLIC_PATH_PREFIX??``,releaseNotesPath:n.TAURI_RELEASE_NOTES_FILE??t.releaseNotesPath,releasePath:n.TAURI_RELEASE_PATH??t.releasePath??"releases/${version}",releaseUploadDir:n.TAURI_RELEASE_UPLOAD_DIR??t.releaseUploadDir??`release-upload`,signingPrivateKey:n.TAURI_SIGNING_PRIVATE_KEY,workflowName:e.tauriWorkflowName??n.TAURI_RELEASE_WORKFLOW_NAME??t.workflowName??`tauri-release-artifacts.yml`,workflowPlatforms:i,workflowPollIntervalMs:J(n.TAURI_RELEASE_WORKFLOW_POLL_SECONDS,`TAURI_RELEASE_WORKFLOW_POLL_SECONDS`)*1e3||t.workflowPollIntervalMs||15e3,workflowRef:e.tauriWorkflowRef??n.TAURI_RELEASE_WORKFLOW_REF??t.workflowRef,workflowTimeoutMs:J(n.TAURI_RELEASE_WORKFLOW_TIMEOUT_MINUTES,`TAURI_RELEASE_WORKFLOW_TIMEOUT_MINUTES`)*6e4||t.workflowTimeoutMs||36e5}}async function C(e,t=process.cwd()){if(!e.enabled)return;let n=[[`TAURI_RELEASE_PUBLIC_BASE_URL`,e.publicBaseUrl],[`B2_APPLICATION_KEY_ID`,e.b2ApplicationKeyId],[`B2_APPLICATION_KEY`,e.b2ApplicationKey],[`B2_BUCKET`,e.b2Bucket]].flatMap(([e,t])=>t?[]:[e]);if(n.length>0)throw Error(`Missing required Tauri release configuration: ${n.join(`, `)}`);try{new URL(e.publicBaseUrl)}catch{throw Error(`Invalid TAURI_RELEASE_PUBLIC_BASE_URL: ${e.publicBaseUrl}`)}let r=u(t,`src-tauri/tauri.conf.json`);if(!await Z(r))throw Error(`Tauri release platforms were requested, but src-tauri/tauri.conf.json does not exist.`);if(w(await i(r,`utf8`),r),e.localPlatforms.length>0&&!e.signingPrivateKey)throw Error(`TAURI_SIGNING_PRIVATE_KEY is required for local Tauri release builds.`);e.workflowPlatforms.length>0&&await m(`gh`,[`workflow`,`view`,e.workflowName,`--ref`,e.workflowRef??await H()],{stdin:`ignore`}),await m(`b2`,[`version`],{stdin:`ignore`})}function w(e,t=`src-tauri/tauri.conf.json`){let n=JSON.parse(e).identifier;if(typeof n!=`string`||n===`com.example`||n.startsWith(`com.example.`))throw Error(`${t} must use an organization-owned reverse-domain identifier before building release artifacts.`);return n}async function T(e,t,n=process.cwd()){if(!e.enabled)return{files:[]};let r=u(n,`src-tauri/tauri.conf.json`),a=u(n,`src-tauri/Cargo.toml`),o=u(n,`src-tauri/Cargo.lock`),c=await i(r,`utf8`),l=await i(a,`utf8`),d=z(l,a),f=[{path:r,source:c},{path:a,source:l}],p=[],m=JSON.parse(c);if(m.version=t,m.bundle={...m.bundle??{},createUpdaterArtifacts:!0},p.push({path:r,source:`${JSON.stringify(m,null,2)}\n`}),p.push({path:a,source:B(l,d,t,a)}),await Z(o)){let e=await i(o,`utf8`);f.push({path:o,source:e}),p.push({path:o,source:V(e,d,t,o)})}for(let e of p)await s(e.path,e.source);return{files:f}}async function E(e){for(let t of e.files)await s(t.path,t.source)}async function D({env:e,plan:t,releaseName:n,version:r}){if(!t.enabled)return;await U(t.artifactDownloadDir),await U(t.releaseUploadDir);let i=t.workflowPlatforms.length>0?await j({plan:t,releaseName:n,version:r}):void 0;for(let n of t.localPlatforms)await P(n,t,e);if(i){let e=await M(t,i.runNameMarker);await m(`gh`,[`run`,`download`,String(e),`--dir`,t.artifactDownloadDir],{stdio:`inherit`})}return A({plan:t,version:r})}async function O(e,t){await m(`b2`,[`account`,`authorize`,e.b2ApplicationKeyId,e.b2ApplicationKey],{stdin:`ignore`});let n=(await v(`**/*`,{cwd:t.uploadDir,onlyFiles:!0})).sort((e,n)=>e===t.manifestPath?1:n===t.manifestPath?-1:e.localeCompare(n));for(let r of n)await m(`b2`,[`file`,`upload`,e.b2Bucket,u(t.uploadDir,r),r],{stdin:`ignore`}),console.log(`Uploaded ${r}`)}function k({artifacts:e,notes:t,pubDate:n=new Date,version:r}){return{...t?{notes:t}:{},platforms:Object.fromEntries(e.map(e=>[e.updatePlatform,{signature:e.signature,url:e.url}])),pub_date:n.toISOString(),version:r}}async function A({plan:e,version:t}){let a=q(e.publicPathPrefix,G(e.releasePath,t)),o=q(e.publicPathPrefix,e.latestManifestPath),d=[...e.localPlatforms,...e.workflowPlatforms],f=[];for(let o of d){let s=e.platform[o],d=e.localPlatforms.includes(o)?`.`:u(e.artifactDownloadDir,`tauri-${o}-${t}`),p=await F({globs:e.localPlatforms.includes(o)?s.artifactGlobs:[`**/*.sig`],sourceRoot:d,version:t}),m=q(a,s.updatePlatform),h=q(m,c(p.artifactPath)),g=q(m,c(p.signaturePath)),_=u(e.releaseUploadDir,...h.split(`/`)),v=u(e.releaseUploadDir,...g.split(`/`)),y=(await i(p.signaturePath,`utf8`)).trim();await r(l(_),{recursive:!0}),await n(p.artifactPath,_),await n(p.signaturePath,v),f.push({signature:y,updatePlatform:s.updatePlatform,url:K(e.publicBaseUrl,h)})}let p=k({artifacts:f,notes:e.releaseNotesPath?await i(e.releaseNotesPath,`utf8`):void 0,version:t}),m=u(e.releaseUploadDir,...o.split(`/`));return await r(l(m),{recursive:!0}),await s(m,`${JSON.stringify(p,null,2)}\n`),{manifestPath:o,uploadDir:e.releaseUploadDir}}async function j({plan:t,releaseName:n,version:r}){let i=t.workflowRef??await H(),a=`coreframe-${Date.now()}-${h().slice(0,8)}`;return await m(`gh`,[`workflow`,`run`,t.workflowName,`--ref`,i,`-f`,`release_name=${n}`,`-f`,`run_id=${a}`,`-f`,`version=${r}`,...e.flatMap(e=>[`-f`,`${e}=${t.workflowPlatforms.includes(e)?`true`:`false`}`])],{stdin:`ignore`}),{runNameMarker:a}}async function M(e,t){let n=Date.now()+e.workflowTimeoutMs;for(;Date.now()<n;){let n=await N(e,t);if(n?.status===`completed`){if(n.conclusion===`success`)return n.databaseId;throw Error(`Tauri release workflow ${n.databaseId} finished with ${n.conclusion}.`)}await _(e.workflowPollIntervalMs)}throw Error(`Timed out waiting for Tauri release workflow after ${Math.round(e.workflowTimeoutMs/6e4)} minutes.`)}async function N(e,t){let{stdout:n}=await m(`gh`,[`run`,`list`,`--workflow`,e.workflowName,`--event`,`workflow_dispatch`,`--json`,`conclusion,databaseId,displayTitle,status`,`--limit`,`50`],{stdin:`ignore`});return JSON.parse(n).find(e=>e.displayTitle.includes(t))}async function P(e,t,n){await m(`pnpm`,t.platform[e].buildArgs,{env:n,stdio:`inherit`})}async function F({globs:e,sourceRoot:t,version:n}){let r=await v(e,{cwd:t,absolute:!0,onlyFiles:!0}),i=[];for(let e of r){let t=e.replace(/\.sig$/u,``);await Q(t)&&i.push({artifactPath:t,signaturePath:e})}if(i.length===0)throw Error(`Could not find a Tauri updater artifact and .sig pair under ${t}.`);return i.sort((e,t)=>{let r=I(e.artifactPath,n);return I(t.artifactPath,n)-r||e.artifactPath.localeCompare(t.artifactPath)})[0]}function I(e,t){let n=0;return e.includes(t)&&(n+=100),/\.(app\.tar\.gz|AppImage\.tar\.gz|zip)$/u.test(e)&&(n+=10),/\.(exe|msi|dmg|AppImage|deb|rpm|apk|aab|ipa)$/u.test(e)&&(n+=5),n}function L(t={},n){return Object.fromEntries(e.map(e=>{let r=t[e]??{},i={...y[e],...r},a=n[`TAURI_RELEASE_${e.toUpperCase()}_TARGET_TRIPLE`],o=n[`TAURI_RELEASE_${e.toUpperCase()}_UPDATE_PLATFORM`],s=a??i.targetTriple,c=r.buildArgs?r.buildArgs:R(e,s);return[e,{...i,artifactGlobs:i.artifactGlobs??y[e].artifactGlobs,buildArgs:c,targetTriple:s,updatePlatform:o??i.updatePlatform}]}))}function R(e,t){return e===`android`?[`tauri`,`android`,`build`,`--target`,t]:e===`ios`?[`tauri`,`ios`,`build`,`--target`,t]:[`tauri`,`build`,`--target`,t,`--`,`--locked`]}function z(e,t){let n=/^\[package\][\s\S]*?^name = "(?<name>[^"]+)"/mu.exec(e);if(!n?.groups?.name)throw Error(`${t} is missing a [package] name.`);return n.groups.name}function B(e,t,n,r){let i=RegExp(`(^\\[package\\][\\s\\S]*?^name = "${$(t)}"[\\s\\S]*?^version = ")[^"]+(")`,`mu`),a=e.replace(i,`$1${n}$2`);if(a===e)throw Error(`${r} is missing a package version for ${t}.`);return a}function V(e,t,n,r){let i=RegExp(`(\\[\\[package\\]\\]\\nname = "${$(t)}"\\nversion = ")[^"]+(")`,`u`),a=e.replace(i,`$1${n}$2`);if(a===e)throw Error(`${r} is missing a package lock entry for ${t}.`);return a}async function H(){let{stdout:e}=await m(`git`,[`rev-parse`,`--abbrev-ref`,`HEAD`],{stdin:`ignore`}),t=e.trim();if(t!==`HEAD`)return t;let{stdout:n}=await m(`git`,[`rev-parse`,`HEAD`],{stdin:`ignore`});return n.trim()}async function U(e){W(e),await a(e,{force:!0,recursive:!0}),await r(e,{recursive:!0})}function W(e){let t=f(e),n=d(process.cwd(),t).split(p).join(`/`);if(n===``||n===`.`||n.startsWith(`../`)||n===`src-tauri`||n.startsWith(`src-tauri/`)||n===`tauri`||n.startsWith(`tauri/`))throw Error(`Refusing to reset unsafe Tauri release directory: ${e}`)}function G(e,t){return e.replaceAll("${version}",t)}function K(e,t){let n=new URL(e),r=q(n.pathname.replace(/\/+$/u,``),t);return n.pathname=r.startsWith(`/`)?r:`/${r}`,n.toString()}function q(...e){return e.flatMap(e=>e.split(`/`)).map(e=>e.trim()).filter(Boolean).join(`/`)}function J(e,t){if(!e)return 0;let n=Number(e);if(!Number.isInteger(n)||n<=0)throw Error(`${t} must be a positive integer.`);return n}function Y(e){return[...new Set(e)]}function X(t){return e.includes(t)}async function Z(e){try{return await t(e,g.F_OK),!0}catch{return!1}}async function Q(e){try{return(await o(e)).isFile()}catch{return!1}}function $(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,`\\$&`)}export{A as createTauriReleaseUpload,k as createTauriUpdateManifest,x as parseTauriReleasePlatform,b as parseTauriReleasePlatformList,D as prepareTauriRelease,S as resolveTauriReleasePlan,E as restoreTauriReleaseMetadata,O as uploadTauriReleaseToB2,w as validateTauriReleaseIdentifier,C as validateTauriReleasePlan,T as writeTauriReleaseMetadata};
|
package/dist/deploy.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{readProjectConfig as e}from"./config.js";import{createProjectDeployDatabase as t}from"./db/project.js";import{collectDeployMigrationFiles as n,formatMigrationList as r,formatMigrationScanFailures as i,scanDeployMigrationFiles as a}from"./deploy/migrations.js";import{
|
|
3
|
-
`),`Pending Worker secret updates`),await
|
|
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{accessTokenSigningSecretName as o,prepareAccessTokenSigningSecret as ee,verifyAccessTokenSigningSecret as te}from"./deploy/access-token-secret.js";import{runPostDeployChecks as ne}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 re,uploadTauriReleaseToB2 as m,validateTauriReleasePlan as h,writeTauriReleaseMetadata as g}from"./deploy/tauri-release.js";import{readPackageJson as _,releaseName as v,resolveReleaseVersion as ie,writePackageVersion as y}from"./deploy/version.js";import{discoverStagingUrl as b}from"./staging.js";import{clearAppliedPendingWorkerSecrets as x,readPendingWorkerSecretSnapshot as S}from"./worker-environment.js";import{mkdtemp as C,readFile as w,rm as T,writeFile as E}from"node:fs/promises";import{join as D}from"node:path";import O from"nano-spawn";import{tmpdir as k}from"node:os";import*as A from"@clack/prompts";import{parseEnv as j}from"node:util";const M=`package.json`;async function N(n=process.argv.slice(2),r=process.env){let s=P(n);H(s);let{packageJson:l,source:u}=await _(M),d=await e(),C=c(d.deploy??{},s.environment,r);q(r,[`POSTHOG_API_KEY`,`POSTHOG_PROJECT_ID`]);let w=await U(C,s.environment,()=>b({env:r})),T=p({args:s,config:s.environment===`production`?C.tauriRelease:{},env:r});K(s.environment,T.enabled),await h(T);let D=await S({environment:s.environment}),k=D.values.DASHBOARD_PASSWORD??await J(r,s.environment),j=await ee({environment:s.environment}),N=await V(),F=ie(s.environment,l.version,N),I=v(l.name,F),L=oe(r,s.environment,I,l.name,F),z=s.environment===`production`,W=!1,Y={files:[]};A.intro(`Deploy ${I} to ${s.environment}`);let Z=Object.keys(D.values).sort();Z.length>0&&A.note(Z.join(`
|
|
3
|
+
`),`Pending Worker secret updates`),j.provisioned&&A.log.info(`Provisioning Coreframe-managed access-token signing material.`),await G(s);let Q=await R(await t({config:d.database,env:r})),$=await a(Q);if($.length>0)throw Error(`Pending deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${i($)}`);let se=await B(s,Q);try{z&&(await y(M,l,F),Y=await g(T,F)),A.log.step(`Running pre-deploy build`),await O(`pnpm`,[`build`],{env:X(L),stdio:`inherit`});let e=T.enabled?await f({env:X(L),plan:T,releaseName:I,version:F}):void 0;A.log.step(`Running release build and uploading PostHog source maps`),W=!0,await O(`pnpm`,[`build`],{env:L,stdio:`inherit`}),se&&(A.log.step(`Running deploy-safe database migrations`),await O(`pnpm`,[`db`,`migrate`],{env:L,stdio:`inherit`})),A.log.step(`Deploying Worker and static assets to Cloudflare`);let t={...k?{DASHBOARD_PASSWORD:k}:{},...j.value?{[o]:j.value}:{},...D.values},n=Object.keys(t).length>0?await ae(t):void 0;try{await O(`pnpm`,[`exec`,`wrangler`,`deploy`,`--env`,s.environment,`--strict`,`--keep-vars`,`--tag`,I,`--message`,`Deploy ${I}`,`--var`,`DEPLOY_ENV:${s.environment}`,`--var`,`POSTHOG_RELEASE:${I}`,...n?[`--secrets-file`,n.path]:[]],{env:L,stdio:`inherit`}),await x(D),await te({environment:s.environment})}finally{await n?.dispose()}s.skipPostDeployChecks||(A.log.step(`Running post-deploy checks`),await ne(C,{environment:s.environment,releaseName:I,url:w,version:F})),e&&(A.log.step(`Uploading Tauri release artifacts to Backblaze B2`),await m(T,e)),A.outro(`Deployed ${I} to ${s.environment}`)}catch(e){throw!W&&z&&(await E(M,u),await re(Y)),e}}function P(e){return{environment:F(e[0]),runMigrations:e.includes(`--run-migrations`),skipMigrations:e.includes(`--skip-migrations`),skipPostDeployChecks:e.includes(`--skip-post-deploy-checks`),tauriLocalPlatforms:I(e,`--tauri-local-platform`).map(e=>d(e)),tauriWorkflowName:L(e,`--tauri-workflow-name`),tauriWorkflowPlatforms:I(e,`--tauri-workflow-platform`).map(e=>d(e)),tauriWorkflowRef:L(e,`--tauri-workflow-ref`),yes:e.includes(`--yes`)}}function F(e){if(e&&s.includes(e))return e;throw Error(`Deploy environment must be one of: ${s.join(`, `)}.`)}function I(e,t){let n=[];for(let r=0;r<e.length;r+=1){let i=e[r];if(i===t){let i=e[r+1];if(!i||i.startsWith(`--`))throw Error(`${t} requires a value.`);n.push(...i.split(`,`).filter(Boolean)),r+=1;continue}i.startsWith(`${t}=`)&&n.push(...i.slice(t.length+1).split(`,`).filter(Boolean))}return n}function L(e,t){let n=I(e,t);if(n.length>1)throw Error(`${t} can only be provided once.`);return n[0]}async function R(e){try{return await z(e)}finally{await e.close?.()}}async function z(e){return l(await n(),await u(e))}async function B(e,t){if(t.length===0)return A.log.info(`No pending deploy migrations.`),!1;if(A.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 A.confirm({initialValue:!0,message:`Run these deploy-safe database migrations before Cloudflare deploy?`});if(A.isCancel(n))throw A.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`);return n}async function V(){let{stdout:e}=await O(`git`,[`status`,`--porcelain`],{stdin:`ignore`});if(e.trim().length>0)throw Error(`Deploy requires a clean git tree.`);let{stdout:t}=await O(`git`,[`rev-parse`,`--short=12`,`HEAD`],{stdin:`ignore`});return t.trim()}function H(e){if(e.runMigrations&&e.skipMigrations)throw Error(`Use only one of --run-migrations or --skip-migrations.`)}async function U(e,t,n=b){return e.url?W(e.url,t):t===`staging`&&e.stagingHostname?W(await n(),t):W(void 0,t)}function W(e,t){if(!e)throw Error(`Missing ${t} deploy URL. Set DEPLOY_URL or deploy.environments.${t}.url in coreframe.config.ts.`);let n;try{n=new URL(e)}catch{throw Error(`Invalid ${t} deploy URL "${e}". Expected an HTTP(S) URL.`)}if(n.protocol!==`http:`&&n.protocol!==`https:`)throw Error(`Invalid ${t} deploy URL "${e}". Expected an HTTP(S) URL.`);return n.toString()}async function G(e){if(e.environment!==`production`||e.yes)return;if(!process.stdout.isTTY)throw Error(`Production deploys require --yes in noninteractive environments.`);let t=await A.confirm({initialValue:!1,message:`Deploy to production?`});if(A.isCancel(t)||!t)throw A.cancel(`Deploy cancelled.`),Error(`Deploy cancelled.`)}function K(e,t){if(e===`staging`&&t)throw Error(`Tauri releases can only be published from production deploys.`)}function q(e,t){let n=t.filter(t=>!e[t]);if(n.length>0)throw Error(`Missing required deploy environment variables: ${n.join(`, `)}`);return Object.fromEntries(t.map(t=>[t,e[t]]))}async function J(e,t){let n=e.DASHBOARD_PASSWORD?.trim();if(n)return n;let r;try{r=j(await w(`.env`,`utf8`))}catch(e){throw Y(e)&&e.code===`ENOENT`?Error(`Missing ${t} dashboard password. Set DASHBOARD_PASSWORD in ".env".`):e}let i=r.DASHBOARD_PASSWORD?.trim();if(!i)throw Error(`Missing ${t} dashboard password. Set DASHBOARD_PASSWORD in ".env".`);return i}async function ae(e){let t=await C(D(k(),`coreframe-deploy-secrets-`)),n=D(t,`secrets.json`);return await E(n,JSON.stringify(e),{mode:384}),{path:n,dispose:()=>T(t,{force:!0,recursive:!0})}}function Y(e){return e instanceof Error&&`code`in e}function oe(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 X(e){let{POSTHOG_API_KEY:t,POSTHOG_PROJECT_ID:n,...r}=e;return r}export{N as deployProject,P as parseDeployArgs,e as readProjectConfig,J as resolveDashboardPassword,U as resolveDeployUrl};
|
package/dist/dev-lock.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{link as e,mkdir as t,readFile as n,unlink as r,writeFile as i}from"node:fs/promises";import a from"node:path";import{randomUUID as o}from"node:crypto";import*as s from"@clack/prompts";import c from"node:process";const l={isRunning:h,stop(e){try{c.kill(e,`SIGTERM`)}catch(e){if(!v(e))throw e}},waitForExit:g};async function u({confirmStop:n=f,cwd:s=c.cwd(),interactive:u=c.stdin.isTTY===!0,pid:p=c.pid,processes:h=l}={}){let g=a.resolve(s,`.coreframe/dev.lock`),v={pid:p,token:o()},y=`${JSON.stringify(v)}\n`,b=`${g}.${v.token}.tmp`,x=!1;await t(a.dirname(g),{recursive:!0}),await i(b,y,{flag:`wx`,mode:384});try{for(;;){try{return await e(b,g),{async release(){await m(g,y)},takeoverApproved:x}}catch(e){if(!_(e))throw e}let t=await d({confirmStop:n,file:g,interactive:u,processes:h});if(t===void 0)return;x||=t}}finally{await r(b).catch(()=>void 0)}}async function d({confirmStop:e,file:t,interactive:n,processes:r}){let i=await p(t);if(!i||!r.isRunning(i.owner.pid))return await m(t,i?.source),!1;if(!n)throw Error(`Coreframe dev is already running for this project (PID ${i.owner.pid}). Stop it before starting another.`);if(await e(i.owner.pid))return r.stop(i.owner.pid),await r.waitForExit(i.owner.pid),await m(t,i.source),!0}async function f(e){let t=await s.confirm({initialValue:!1,message:`Coreframe dev is already running for this project (PID ${e}). Stop it and start here?`});return s.isCancel(t)||!t?(s.cancel(`Keeping the existing Coreframe dev process.`),!1):!0}async function p(e){try{let t=await n(e,`utf8`),r=JSON.parse(t);return!Number.isSafeInteger(r.pid)||Number(r.pid)<=0||!r.token?void 0:{owner:r,source:t}}catch{return}}async function m(e,t){let i=await n(e,`utf8`).catch(()=>void 0);(t===void 0||i===t)&&await r(e).catch(()=>void 0)}function h(e){try{return c.kill(e,0),!0}catch(e){return!v(e)}}async function g(e){let t=Date.now()+1e4;for(;h(e);){if(Date.now()>=t)throw Error(`Coreframe dev process ${e} did not stop within 10 seconds.`);await new Promise(e=>setTimeout(e,50))}}function _(e){return y(e)&&e.code===`EEXIST`}function v(e){return y(e)&&e.code===`ESRCH`}function y(e){return e instanceof Error}export{u as acquireDevLock};
|
package/dist/environment.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { access, chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
2
|
import { homedir, networkInterfaces, tmpdir } from "node:os";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
4
|
|
|
5
5
|
//#region environment.d.ts
|
|
6
6
|
declare const fs: {
|
package/dist/environment.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{access as e,chmod as t,mkdir as n,mkdtemp as r,readFile as i,rm as a,writeFile as o}from"node:fs/promises";import s from"node:
|
|
1
|
+
import{access as e,chmod as t,mkdir as n,mkdtemp as r,readFile as i,rm as a,writeFile as o}from"node:fs/promises";import{homedir as s,networkInterfaces as c,tmpdir as l}from"node:os";import u from"node:process";import{readFileSync as d}from"node:fs";import{createServer as f}from"node:net";const p={access:e,chmod:t,mkdir:n,mkdtemp:r,readFile:i,readFileSync:d,rm:a,writeFile:o},m={getAvailablePort:g},h={homedir:s,kill:u.kill,networkInterfaces:c,tmpdir:l};async function g(){let e=f();try{await new Promise((t,n)=>{e.once(`error`,n),e.listen(0,`127.0.0.1`,t)});let t=e.address();if(!t||typeof t==`string`)throw Error(`Could not allocate an available TCP port.`);return t.port}finally{e.listening&&await new Promise((t,n)=>{e.close(e=>e?n(e):t())})}}export{p as fs,m as net,h as os};
|
package/dist/managed-dev.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createHash as e,randomBytes as t}from"node:crypto";import{access as n,copyFile as r,mkdir as i,mkdtemp as a,open as o,readFile as s,realpath as c,rm as l,unlink as u,writeFile as d}from"node:fs/promises";import f from"node:path";import p,{SubprocessError as m}from"nano-spawn";import{pathToFileURL as h}from"node:url";import g from"node:process";import{constants as _,watch as v}from"node:fs";import{resolve as y}from"import-meta-resolve";const b=`refs/heads/main`,x=`refs/heads/dev`,ee=`refs/remotes/origin/main`,S=/^[a-z][a-z0-9-]*(?:\([^)]+\))?!?:\s+\S/i,C=`coreframe-managed-dev-setup-failure.json`;async function w(e=g.cwd()){let t=await k(e);return t.primary&&t.branch===`main`&&t.head===t.main}async function T(e=g.cwd()){try{let{commonDir:t,gitDir:n}=await E(e);return n===t}catch{return!1}}async function E(e){let t=await c(await K(e,[`rev-parse`,`--show-toplevel`])),n=await c(e),r=f.resolve(n,await K(e,[`rev-parse`,`--git-dir`])),i=f.resolve(n,await K(e,[`rev-parse`,`--git-common-dir`]));return{commonDir:i,gitDir:r,primary:t===n&&r===i}}async function D({config:e,cwd:t=g.cwd(),env:n=g.env,onRestart:r=()=>void 0,onStatusChange:i=()=>void 0,remoteCheckInterval:a=6e4,runCheck:o=e=>se(t,n,e),watchState:s=ne}){let c=n,l={mode:`waiting`,warnings:[]},m=!1,h=!1,_=!1,v=!1,y,w,T,E,D,O=[],A,N,P=Promise.resolve(),F=()=>{m||i(l)},I=async({fetch:e=!1}={})=>{if(!(!h||m||!w)){if(_){v=!0;return}try{let[n,r,i]=await Promise.all([K(t,[`rev-parse`,`--abbrev-ref`,`HEAD`]),K(t,[`rev-parse`,`HEAD`]),K(t,[`rev-parse`,b])]);if(n!==`main`||r!==i){l={message:`Managed synchronization is paused until the primary checkout is back on local main.`,mode:`waiting`,warnings:[]};return}r!==y&&(y=r,A=void 0);let a=await j(t);if(a.tracked||a.untracked.length>0){l={...l,currentCommit:y,draft:a,message:void 0,mode:`managed`,proposal:B(a.fingerprint),target:void 0};return}e&&await q(t,[`remote`,`get-url`,`origin`])&&await G(t,[`fetch`,`origin`,`+refs/heads/main:${ee}`]);let o=await q(t,[`rev-parse`,x]),s=await q(t,[`rev-parse`,ee]),c=[...s?[{commit:s,source:`upstream`}]:[],...o?[{commit:o,source:`dev`}]:[]],u;for(let e of c)if(!await _e(t,e.commit,y)){u=e;break}l={...l,currentCommit:y,draft:void 0,message:void 0,mode:`managed`,proposal:void 0,target:u===void 0?void 0:await me(t,u.commit,u.source)}}catch(e){l={...l,message:Z(e),warnings:[Z(e)]}}finally{F()}}},L=()=>{clearTimeout(D),D=setTimeout(()=>void I(),50),D.unref()},R=e=>{let t=async()=>{let t=[{command:`pnpm typecheck`,status:`running`},{command:`pnpm test`,status:`pending`}],n=()=>{l={...l,checks:{commit:e,commands:structuredClone(t)}},F()};n();for(let[e,r]of[`typecheck`,`test`].entries()){e>0&&(t[e].status=`running`,n());try{await o(r),t[e].status=`passed`}catch(n){t[e].status=`failed`,t[e].output=ce(n)}n()}};P=P.then(t,t)},ue=async()=>{if(h||m)return;let e=await k(t);if(!e.primary){l={message:`Managed development only runs from the primary checkout.`,mode:`unmanaged`,warnings:[]},F();return}if(e.branch!==`main`||e.head!==e.main){let t=`Managed development requires the primary checkout on local main; current branch is ${e.branch??`detached HEAD`}.`;console.warn(`Coreframe managed development disabled: ${t}`),l={message:t,mode:`unmanaged`,warnings:[]},F();return}T=f.join(e.commonDir,`coreframe-managed-dev.lock`),await he(T),await q(t,[`rev-parse`,`--verify`,x])||await G(t,[`branch`,`dev`,e.head]),w=e,y=e.head,h=!0,l={currentCommit:e.head,mode:`managed`,setupFailure:await te(e.commonDir,e.head),warnings:[]},F(),O=await s(e,L),E=setInterval(()=>void I({fetch:!0}),a),E.unref(),await I(),I({fetch:!0})},de=async(e,{onCodexEvent:n=()=>void 0}={})=>{if(!h||l.mode!==`managed`)throw Error(`Managed development is not active.`);if(_)throw Error(`Another managed development action is already running.`);_=!0,l={...l,busy:ye(e),message:void 0},F();try{if(e.type===`reject`)l={...l,draft:void 0,proposal:void 0};else if(e.type===`prepare`){let r=z(e.target),i=await j(t);i.tracked||i.untracked.length>0?l={...l,draft:i,proposal:B(i.fingerprint)}:await U({onCodexEvent:n,source:r.source,target:e.target})}else if(e.type===`message`){let n=await V(e.fingerprint,e.untracked),r=B(n.fingerprint);if(r===void 0){try{if(N??=ie(`codex`,c),!await N)throw Error("Codex CLI is not installed or is unavailable on PATH. Install it with `npm install -g @openai/codex`, then restart the dev server.");r=await re(t,c,n.context)}catch(e){r=``,l={...l,warnings:[...l.warnings,`Codex could not generate a commit message: ${Z(e)}`]}}A={fingerprint:n.fingerprint,message:r}}l={...l,draft:n,proposal:r}}else if(e.type===`commit`){let t=await H(e);l={...l,currentCommit:t,draft:void 0,message:void 0,proposal:void 0,target:void 0},L()}else if(e.type===`retry-install`)await be();else if(e.type===`reset-database`)await xe();else{let r=z(e.target),i=await j(t);if(i.tracked||i.untracked.length>0){if(!e.fingerprint||!e.untracked)throw Error(`Refresh requires the current working-tree confirmation.`);if(await V(e.fingerprint,e.untracked),!e.message||!S.test(Y(e.message)))throw Error(`Enter a commit message with a valid Conventional Commits header.`)}await U({fingerprint:e.fingerprint,message:e.message,onCodexEvent:n,source:r.source,target:e.target,untracked:e.untracked})}}finally{_=!1,l={...l,busy:void 0},F(),v&&(v=!1,L())}return{status:l}},z=e=>{if(!l.target||l.target.commit!==e)throw Error(`The offered update changed. Review the latest target before refreshing.`);return l.target},B=e=>A?.fingerprint===e?A.message:void 0,V=async(e,n)=>{let r=await j(t);if(r.fingerprint!==e)throw A=void 0,Error(`Working-tree changes changed; generate a new commit message.`);if(!ve(r.untracked,n))throw Error(`Include every untracked file or remove it before refreshing.`);return r},H=async({fingerprint:e,message:n,untracked:r})=>{let i=await V(e,r);if(!n||!S.test(Y(n)))throw Error(`Enter a commit message with a valid Conventional Commits header.`);await G(t,[`add`,`--update`]),i.untracked.length>0&&await G(t,[`add`,`--`,...r]),await G(t,[`commit`,`--message`,n],{env:{GIT_EDITOR:`true`}});let a=await K(t,[`rev-parse`,`HEAD`]);return y=a,A=void 0,a},U=async({fingerprint:e,message:n,onCodexEvent:r=()=>void 0,source:i,target:a,untracked:o=[]})=>{z(a);let s=await K(t,[`rev-parse`,`HEAD`]),u,d,f=await j(t);if(f.tracked||f.untracked.length>0){if(!e||f.fingerprint!==e||!ve(f.untracked,o))throw Error(`Working-tree changes changed; review them before refreshing.`);await G(t,[`add`,`--update`]),o.length>0&&await G(t,[`add`,`--`,...o]),await G(t,[`commit`,`--message`,n],{env:{GIT_EDITOR:`true`}}),u=await K(t,[`rev-parse`,`HEAD`]),y=u,d=`refs/coreframe/recovery/${Date.now()}-${u.slice(0,12)}`,await G(t,[`update-ref`,d,u])}let p=u??s,m;try{if(m=await ae({cwd:t,env:c,localMain:p,onCodexEvent:r,source:i,target:a}),await K(t,[`rev-parse`,`HEAD`])!==p)throw Error(`HEAD changed while the accepted update was being prepared.`);if(await K(t,[`rev-parse`,b])!==p)throw Error(`Local main changed while the accepted update was being prepared.`);await oe(t,m,p),y=m.commit,await G(t,[`reset`,`--hard`,m.commit]);let e=J(await K(t,[`diff`,`--name-only`,`-z`,s,m.commit]));A=void 0,d&&await G(t,[`update-ref`,`-d`,d]),l={...l,currentCommit:m.commit,draft:void 0,proposal:void 0,setupFailure:void 0,target:void 0},await W({installDependencies:fe(e),restartRequired:pe(e)})}catch(e){e instanceof M&&(v=!0);let t=d?` Recovery commit: ${d}.`:``;throw l={...l,currentCommit:y,message:`${Z(e)}${t}`},e}},W=async({installDependencies:n,restartRequired:i})=>{let a=y;if(n)try{await Ce([`install`,`--frozen-lockfile`])}catch(e){await Q({command:`pnpm install --frozen-lockfile`,commit:a,error:e,message:`The project was synchronized, but dependency installation failed. Fix the package or lockfile issue, then retry the install.`,restartRequired:i,stage:`dependencies`});return}try{await le({config:e,cwd:t,env:c})}catch(e){await Q({command:`pnpm db migrate`,commit:a,error:e,message:`The project was synchronized, but its database migration failed. The previous database was restored. Reset the database to recreate it, apply migrations, and rerun seed scripts.`,restartRequired:i,stage:`migration`});return}await $(),R(a),i&&r()},be=async()=>{let e=Se(`dependencies`);await $(),await W({installDependencies:!0,restartRequired:e.restartRequired})},xe=async()=>{let e=Se(`migration`);try{await Ce([`db`,`setup`])}catch(t){await Q({command:`pnpm db setup`,commit:e.commit,error:t,message:`The project remains synchronized, but the database reset failed. Review the diagnostics, fix the reported problem, and try the reset again.`,restartRequired:e.restartRequired,stage:`migration`});return}await $(),R(e.commit),e.restartRequired&&r()},Se=e=>{if(!l.setupFailure||l.setupFailure.stage!==e)throw Error(`There is no failed ${e} setup step to recover.`);return l.setupFailure},Ce=async e=>{await p(X(),e,{cwd:t,env:c})},Q=async({command:e,commit:t,error:n,message:r,restartRequired:i,stage:a})=>{let o=ce(n).slice(-1e5),s={command:e,commit:t,message:r,output:o,restartRequired:i,stage:a};l={...l,setupFailure:s,target:void 0},console.error(`Coreframe managed development setup failed (${e}): ${r}\n${o}`);let c=we();c&&await d(c,`${JSON.stringify(s)}\n`,{mode:384}).catch(e=>{console.warn(`Could not persist the managed-development setup failure: ${Z(e)}`)})},$=async()=>{l={...l,setupFailure:void 0};let e=we();e&&await u(e).catch(e=>{e instanceof Error&&`code`in e&&e.code===`ENOENT`||console.warn(`Could not clear the managed-development setup failure: ${Z(e)}`)})},we=()=>w?f.join(w.commonDir,C):void 0;return{action:de,activate:ue,getStatus:()=>l,async close(){if(!m){m=!0,clearInterval(E),clearTimeout(D);for(let e of O)e.close();await ge(T)}}}}async function te(e,t){let n=f.join(e,C);try{let e=JSON.parse(await s(n,`utf8`));if(O(e)&&e.commit===t)return e}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;console.warn(`Could not read the managed-development setup failure: ${Z(e)}`)}await u(n).catch(()=>void 0)}function O(e){if(!e||typeof e!=`object`)return!1;let t=e;return(t.command===`pnpm db migrate`||t.command===`pnpm db setup`||t.command===`pnpm install --frozen-lockfile`)&&typeof t.commit==`string`&&typeof t.message==`string`&&typeof t.output==`string`&&typeof t.restartRequired==`boolean`&&(t.stage===`dependencies`||t.stage===`migration`)}async function k(e){let t=await E(e);return{branch:A(await K(e,[`rev-parse`,`--abbrev-ref`,`HEAD`])),commonDir:t.commonDir,gitDir:t.gitDir,head:await K(e,[`rev-parse`,`HEAD`]),main:await K(e,[`rev-parse`,b]),primary:t.primary}}function A(e){return e===`HEAD`?void 0:e}async function ne(e,t){let n=[e.gitDir,f.join(e.commonDir,`refs`,`heads`)],r=[];for(let e of n)try{r.push(v(e,{persistent:!1},t))}catch{}return r}async function j(t){let[n,r,i]=await Promise.all([K(t,[`status`,`--porcelain`,`--untracked-files=no`]),K(t,[`diff`,`HEAD`,`--binary`]),K(t,[`ls-files`,`--others`,`--exclude-standard`,`-z`])]),a=J(i).sort(),o=e(`sha256`).update(r),c=[`Tracked changes:`,r];for(let e of a){let n=await s(f.join(t,e));o.update(e).update(n),c.push(`\nUntracked file: ${e}\n`,n.toString(`utf8`))}return{context:c.join(`
|
|
2
|
-
`),fingerprint:o.digest(`hex`),tracked:
|
|
3
|
-
`)}\n`}})}catch(r){if(t.devTarget)try{let[r,i]=await Promise.all([K(e,[`rev-parse`,b]),q(e,[`rev-parse`,x])]);if(r===n&&i!==t.devTarget)throw new M}catch(e){if(e instanceof M)throw e}throw r}}async function N(e,t,n,r,i){try{await G(e,n,{env:{GIT_EDITOR:`true`}});return}catch(n){let a=await I(e);if(a.length===0)throw n;try{for(;a.length>0;){await P(e,t,r,a,i),await G(e,[`diff`,`--check`]),await G(e,[`add`,`--all`]);let n=await I(e);if(n.length>0)throw Error(`Codex left unresolved paths: ${n.join(`, `)}`);try{await G(e,[r,`--continue`],{env:{GIT_EDITOR:`true`}})}catch(t){if(a=await I(e),a.length===0)throw t;continue}a=await I(e)}return}catch(t){throw await G(e,[r,`--abort`]).catch(()=>void 0),Error(`Codex could not resolve ${r} conflicts: ${Z(t)}`)}}}async function P(e,t,n,r,i){i({phase:`start`});let a=
|
|
4
|
-
`).trim(),phase:`complete`})}async function F(e,t){for await(let n of e)t(n)}async function I(e){return J(await K(e,[`diff`,`--name-only`,`--diff-filter=U`,`-z`]))}async function se(e,t,n){await
|
|
1
|
+
import{access as e,copyFile as t,mkdir as n,mkdtemp as r,open as i,readFile as a,realpath as o,rm as s,unlink as c,writeFile as l}from"node:fs/promises";import u from"node:path";import d,{SubprocessError as f}from"nano-spawn";import{pathToFileURL as p}from"node:url";import{createHash as m,randomBytes as h}from"node:crypto";import g from"node:process";import{constants as _,watch as v}from"node:fs";import{resolve as y}from"import-meta-resolve";const b=`refs/heads/main`,x=`refs/heads/dev`,ee=`refs/remotes/origin/main`,S=/^[a-z][a-z0-9-]*(?:\([^)]+\))?!?:\s+\S/i,C=`coreframe-managed-dev-setup-failure.json`;async function w(e=g.cwd()){let t=await k(e);return t.primary&&t.branch===`main`&&t.head===t.main}async function T(e=g.cwd()){try{let{commonDir:t,gitDir:n}=await E(e);return n===t}catch{return!1}}async function E(e){let t=await o(await K(e,[`rev-parse`,`--show-toplevel`])),n=await o(e),r=u.resolve(n,await K(e,[`rev-parse`,`--git-dir`])),i=u.resolve(n,await K(e,[`rev-parse`,`--git-common-dir`]));return{commonDir:i,gitDir:r,primary:t===n&&r===i}}async function D({config:e,cwd:t=g.cwd(),env:n=g.env,onRestart:r=()=>void 0,onStatusChange:i=()=>void 0,remoteCheckInterval:a=6e4,runCheck:o=e=>se(t,n,e),watchState:s=ne}){let f=n,p={mode:`waiting`,warnings:[]},m=!1,h=!1,_=!1,v=!1,y,w,T,E,D,O=[],A,N,P=Promise.resolve(),F=()=>{m||i(p)},I=async({fetch:e=!1}={})=>{if(!(!h||m||!w)){if(_){v=!0;return}try{let[n,r,i]=await Promise.all([K(t,[`rev-parse`,`--abbrev-ref`,`HEAD`]),K(t,[`rev-parse`,`HEAD`]),K(t,[`rev-parse`,b])]);if(n!==`main`||r!==i){p={message:`Managed synchronization is paused until the primary checkout is back on local main.`,mode:`waiting`,warnings:[]};return}r!==y&&(y=r,A=void 0);let a=await j(t);if(a.tracked||a.untracked.length>0){p={...p,currentCommit:y,draft:a,message:void 0,mode:`managed`,proposal:B(a.fingerprint),target:void 0};return}e&&await q(t,[`remote`,`get-url`,`origin`])&&await G(t,[`fetch`,`origin`,`+refs/heads/main:${ee}`]);let o=await q(t,[`rev-parse`,x]),s=await q(t,[`rev-parse`,ee]),c=[...s?[{commit:s,source:`upstream`}]:[],...o?[{commit:o,source:`dev`}]:[]],l;for(let e of c)if(!await _e(t,e.commit,y)){l=e;break}p={...p,currentCommit:y,draft:void 0,message:void 0,mode:`managed`,proposal:void 0,target:l===void 0?void 0:await me(t,l.commit,l.source)}}catch(e){p={...p,message:Z(e),warnings:[Z(e)]}}finally{F()}}},L=()=>{clearTimeout(D),D=setTimeout(()=>void I(),50),D.unref()},R=e=>{let t=async()=>{let t=[{command:`pnpm typecheck`,status:`running`},{command:`pnpm test`,status:`pending`}],n=()=>{p={...p,checks:{commit:e,commands:structuredClone(t)}},F()};n();for(let[e,r]of[`typecheck`,`test`].entries()){e>0&&(t[e].status=`running`,n());try{await o(r),t[e].status=`passed`}catch(n){t[e].status=`failed`,t[e].output=ce(n)}n()}};P=P.then(t,t)},ue=async()=>{if(h||m)return;let e=await k(t);if(!e.primary){p={message:`Managed development only runs from the primary checkout.`,mode:`unmanaged`,warnings:[]},F();return}if(e.branch!==`main`||e.head!==e.main){let t=`Managed development requires the primary checkout on local main; current branch is ${e.branch??`detached HEAD`}.`;console.warn(`Coreframe managed development disabled: ${t}`),p={message:t,mode:`unmanaged`,warnings:[]},F();return}T=u.join(e.commonDir,`coreframe-managed-dev.lock`),await he(T),await q(t,[`rev-parse`,`--verify`,x])||await G(t,[`branch`,`dev`,e.head]),w=e,y=e.head,h=!0,p={currentCommit:e.head,mode:`managed`,setupFailure:await te(e.commonDir,e.head),warnings:[]},F(),O=await s(e,L),E=setInterval(()=>void I({fetch:!0}),a),E.unref(),await I(),I({fetch:!0})},de=async(e,{onCodexEvent:n=()=>void 0}={})=>{if(!h||p.mode!==`managed`)throw Error(`Managed development is not active.`);if(_)throw Error(`Another managed development action is already running.`);_=!0,p={...p,busy:ye(e),message:void 0},F();try{if(e.type===`reject`)p={...p,draft:void 0,proposal:void 0};else if(e.type===`prepare`){let r=z(e.target),i=await j(t);i.tracked||i.untracked.length>0?p={...p,draft:i,proposal:B(i.fingerprint)}:await U({onCodexEvent:n,source:r.source,target:e.target})}else if(e.type===`message`){let n=await V(e.fingerprint,e.untracked),r=B(n.fingerprint);if(r===void 0){try{if(N??=ie(`codex`,f),!await N)throw Error("Codex CLI is not installed or is unavailable on PATH. Install it with `npm install -g @openai/codex`, then restart the dev server.");r=await re(t,f,n.context)}catch(e){r=``,p={...p,warnings:[...p.warnings,`Codex could not generate a commit message: ${Z(e)}`]}}A={fingerprint:n.fingerprint,message:r}}p={...p,draft:n,proposal:r}}else if(e.type===`commit`){let t=await H(e);p={...p,currentCommit:t,draft:void 0,message:void 0,proposal:void 0,target:void 0},L()}else if(e.type===`retry-install`)await be();else if(e.type===`reset-database`)await xe();else{let r=z(e.target),i=await j(t);if(i.tracked||i.untracked.length>0){if(!e.fingerprint||!e.untracked)throw Error(`Refresh requires the current working-tree confirmation.`);if(await V(e.fingerprint,e.untracked),!e.message||!S.test(Y(e.message)))throw Error(`Enter a commit message with a valid Conventional Commits header.`)}await U({fingerprint:e.fingerprint,message:e.message,onCodexEvent:n,source:r.source,target:e.target,untracked:e.untracked})}}finally{_=!1,p={...p,busy:void 0},F(),v&&(v=!1,L())}return{status:p}},z=e=>{if(!p.target||p.target.commit!==e)throw Error(`The offered update changed. Review the latest target before refreshing.`);return p.target},B=e=>A?.fingerprint===e?A.message:void 0,V=async(e,n)=>{let r=await j(t);if(r.fingerprint!==e)throw A=void 0,Error(`Working-tree changes changed; generate a new commit message.`);if(!ve(r.untracked,n))throw Error(`Include every untracked file or remove it before refreshing.`);return r},H=async({fingerprint:e,message:n,untracked:r})=>{let i=await V(e,r);if(!n||!S.test(Y(n)))throw Error(`Enter a commit message with a valid Conventional Commits header.`);await G(t,[`add`,`--update`]),i.untracked.length>0&&await G(t,[`add`,`--`,...r]),await G(t,[`commit`,`--message`,n],{env:{GIT_EDITOR:`true`}});let a=await K(t,[`rev-parse`,`HEAD`]);return y=a,A=void 0,a},U=async({fingerprint:e,message:n,onCodexEvent:r=()=>void 0,source:i,target:a,untracked:o=[]})=>{z(a);let s=await K(t,[`rev-parse`,`HEAD`]),c,l,u=await j(t);if(u.tracked||u.untracked.length>0){if(!e||u.fingerprint!==e||!ve(u.untracked,o))throw Error(`Working-tree changes changed; review them before refreshing.`);await G(t,[`add`,`--update`]),o.length>0&&await G(t,[`add`,`--`,...o]),await G(t,[`commit`,`--message`,n],{env:{GIT_EDITOR:`true`}}),c=await K(t,[`rev-parse`,`HEAD`]),y=c,l=`refs/coreframe/recovery/${Date.now()}-${c.slice(0,12)}`,await G(t,[`update-ref`,l,c])}let d=c??s,m;try{if(m=await ae({cwd:t,env:f,localMain:d,onCodexEvent:r,source:i,target:a}),await K(t,[`rev-parse`,`HEAD`])!==d)throw Error(`HEAD changed while the accepted update was being prepared.`);if(await K(t,[`rev-parse`,b])!==d)throw Error(`Local main changed while the accepted update was being prepared.`);await oe(t,m,d),y=m.commit,await G(t,[`reset`,`--hard`,m.commit]);let e=J(await K(t,[`diff`,`--name-only`,`-z`,s,m.commit]));A=void 0,l&&await G(t,[`update-ref`,`-d`,l]),p={...p,currentCommit:m.commit,draft:void 0,proposal:void 0,setupFailure:void 0,target:void 0},await W({installDependencies:fe(e),restartRequired:pe(e)})}catch(e){e instanceof M&&(v=!0);let t=l?` Recovery commit: ${l}.`:``;throw p={...p,currentCommit:y,message:`${Z(e)}${t}`},e}},W=async({installDependencies:n,restartRequired:i})=>{let a=y;if(n)try{await Ce([`install`,`--frozen-lockfile`])}catch(e){await Q({command:`pnpm install --frozen-lockfile`,commit:a,error:e,message:`The project was synchronized, but dependency installation failed. Fix the package or lockfile issue, then retry the install.`,restartRequired:i,stage:`dependencies`});return}try{await le({config:e,cwd:t,env:f})}catch(e){await Q({command:`pnpm db migrate`,commit:a,error:e,message:`The project was synchronized, but its database migration failed. The previous database was restored. Reset the database to recreate it, apply migrations, and rerun seed scripts.`,restartRequired:i,stage:`migration`});return}await $(),R(a),i&&r()},be=async()=>{let e=Se(`dependencies`);await $(),await W({installDependencies:!0,restartRequired:e.restartRequired})},xe=async()=>{let e=Se(`migration`);try{await Ce([`db`,`setup`])}catch(t){await Q({command:`pnpm db setup`,commit:e.commit,error:t,message:`The project remains synchronized, but the database reset failed. Review the diagnostics, fix the reported problem, and try the reset again.`,restartRequired:e.restartRequired,stage:`migration`});return}await $(),R(e.commit),e.restartRequired&&r()},Se=e=>{if(!p.setupFailure||p.setupFailure.stage!==e)throw Error(`There is no failed ${e} setup step to recover.`);return p.setupFailure},Ce=async e=>{await d(X(),e,{cwd:t,env:f})},Q=async({command:e,commit:t,error:n,message:r,restartRequired:i,stage:a})=>{let o=ce(n).slice(-1e5),s={command:e,commit:t,message:r,output:o,restartRequired:i,stage:a};p={...p,setupFailure:s,target:void 0},console.error(`Coreframe managed development setup failed (${e}): ${r}\n${o}`);let c=we();c&&await l(c,`${JSON.stringify(s)}\n`,{mode:384}).catch(e=>{console.warn(`Could not persist the managed-development setup failure: ${Z(e)}`)})},$=async()=>{p={...p,setupFailure:void 0};let e=we();e&&await c(e).catch(e=>{e instanceof Error&&`code`in e&&e.code===`ENOENT`||console.warn(`Could not clear the managed-development setup failure: ${Z(e)}`)})},we=()=>w?u.join(w.commonDir,C):void 0;return{action:de,activate:ue,getStatus:()=>p,async close(){if(!m){m=!0,clearInterval(E),clearTimeout(D);for(let e of O)e.close();await ge(T)}}}}async function te(e,t){let n=u.join(e,C);try{let e=JSON.parse(await a(n,`utf8`));if(O(e)&&e.commit===t)return e}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;console.warn(`Could not read the managed-development setup failure: ${Z(e)}`)}await c(n).catch(()=>void 0)}function O(e){if(!e||typeof e!=`object`)return!1;let t=e;return(t.command===`pnpm db migrate`||t.command===`pnpm db setup`||t.command===`pnpm install --frozen-lockfile`)&&typeof t.commit==`string`&&typeof t.message==`string`&&typeof t.output==`string`&&typeof t.restartRequired==`boolean`&&(t.stage===`dependencies`||t.stage===`migration`)}async function k(e){let t=await E(e);return{branch:A(await K(e,[`rev-parse`,`--abbrev-ref`,`HEAD`])),commonDir:t.commonDir,gitDir:t.gitDir,head:await K(e,[`rev-parse`,`HEAD`]),main:await K(e,[`rev-parse`,b]),primary:t.primary}}function A(e){return e===`HEAD`?void 0:e}async function ne(e,t){let n=[e.gitDir,u.join(e.commonDir,`refs`,`heads`)],r=[];for(let e of n)try{r.push(v(e,{persistent:!1},t))}catch{}return r}async function j(e){let[t,n,r]=await Promise.all([K(e,[`status`,`--porcelain`,`--untracked-files=no`]),K(e,[`diff`,`HEAD`,`--binary`]),K(e,[`ls-files`,`--others`,`--exclude-standard`,`-z`])]),i=J(r).sort(),o=m(`sha256`).update(n),s=[`Tracked changes:`,n];for(let t of i){let n=await a(u.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:i}}async function re(e,t,n){let{stdout:r}=await d(`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,stderr:`ignore`,stdin:{string:n}});return r.trim().replace(/^```(?:text)?\s*|\s*```$/g,``)}async function ie(t,n){let r=n.PATH??n.Path??n.path??``,i=g.platform===`win32`?(n.PATHEXT??`.COM;.EXE;.BAT;.CMD`).split(`;`):[``];for(let n of r.split(u.delimiter))for(let r of i)try{return await e(u.join(n||`.`,`${t}${r}`),_.X_OK),!0}catch{}return!1}var M=class extends Error{constructor(){super(`Local dev advanced while this update was being prepared. Nothing was changed. Review the latest update and try again.`)}};async function ae({cwd:e,env:t,localMain:r,onCodexEvent:i,source:a,target:o}){if(a===`dev`&&await _e(e,r,o))return{commit:o,devTarget:o};let c=u.resolve(e,await K(e,[`rev-parse`,`--git-common-dir`])),l=u.join(c,`coreframe-managed-dev`);await n(l,{recursive:!0});let d=h(8).toString(`hex`),f=u.join(l,`integration-${d}`);try{let n=a===`dev`?o:r,s=a===`dev`?r:o;if(await G(e,[`worktree`,`add`,`--detach`,f,n]),r!==o){let e=await q(f,[`merge-base`,r,o]);await N(f,t,e?[`rebase`,`--onto`,s,e]:[`rebase`,`--root`,`--onto`,s],`rebase`,i)}return{commit:await K(f,[`rev-parse`,`HEAD`]),devTarget:a===`dev`?o:void 0}}finally{await G(e,[`worktree`,`remove`,`--force`,f]).catch(()=>void 0),await s(f,{force:!0,recursive:!0})}}async function oe(e,t,n){let r=[`start`,`update ${b} ${t.commit} ${n}`];t.devTarget&&r.push(t.devTarget===t.commit?`verify ${x} ${t.devTarget}`:`update ${x} ${t.commit} ${t.devTarget}`),r.push(`prepare`,`commit`);try{await d(`git`,[`update-ref`,`--stdin`],{cwd:e,stdin:{string:`${r.join(`
|
|
3
|
+
`)}\n`}})}catch(r){if(t.devTarget)try{let[r,i]=await Promise.all([K(e,[`rev-parse`,b]),q(e,[`rev-parse`,x])]);if(r===n&&i!==t.devTarget)throw new M}catch(e){if(e instanceof M)throw e}throw r}}async function N(e,t,n,r,i){try{await G(e,n,{env:{GIT_EDITOR:`true`}});return}catch(n){let a=await I(e);if(a.length===0)throw n;try{for(;a.length>0;){await P(e,t,r,a,i),await G(e,[`diff`,`--check`]),await G(e,[`add`,`--all`]);let n=await I(e);if(n.length>0)throw Error(`Codex left unresolved paths: ${n.join(`, `)}`);try{await G(e,[r,`--continue`],{env:{GIT_EDITOR:`true`}})}catch(t){if(a=await I(e),a.length===0)throw t;continue}a=await I(e)}return}catch(t){throw await G(e,[r,`--abort`]).catch(()=>void 0),Error(`Codex could not resolve ${r} conflicts: ${Z(t)}`)}}}async function P(e,t,n,r,i){i({phase:`start`});let a=d(`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,stdin:`ignore`}),o=[];try{await Promise.all([F(a.stdout,e=>o.push(e)),F(a.stderr,e=>i({phase:`stderr`,text:`${e}\n`}))])}catch(e){throw i({phase:`error`}),e}i({message:o.join(`
|
|
4
|
+
`).trim(),phase:`complete`})}async function F(e,t){for await(let n of e)t(n)}async function I(e){return J(await K(e,[`diff`,`--name-only`,`--diff-filter=U`,`-z`]))}async function se(e,t,n){await d(X(),[n],{cwd:e,env:t})}function ce(e){if(e instanceof f){let t=e.output.trim();if(t)return t}return Z(e)}async function le({config:e,cwd:t,env:n}){if(!e.database)return;let r=await L({config:e,cwd:t,env:n});try{await d(X(),[`db`,`migrate`],{cwd:t,env:n})}catch(e){throw await r.restore(),e}await r.discard().catch(e=>{console.warn(`Could not remove the managed-development database snapshot: ${Z(e)}`)}),await s(r.directory,{force:!0,recursive:!0})}async function L({config:e,cwd:i,env:a}){let o=u.join(i,`.coreframe`,`managed-dev-backups`);await n(o,{recursive:!0});let c=await r(u.join(o,`refresh-`));if(e.database?.dialect===`sqlite`){let e=[`turso.db`,`turso.db-wal`,`turso.db-shm`],n=new Set;for(let t of e)await R(u.join(i,`.coreframe`,t),u.join(c,t))&&n.add(t);return{directory:c,async discard(){},async restore(){for(let r of e){let e=u.join(i,`.coreframe`,r);n.has(r)?await t(u.join(c,r),e):await s(e,{force:!0})}}}}if(!a.DATABASE_URL)throw Error(`DATABASE_URL is required to snapshot local PostgreSQL.`);let l=await ue(i);if(l)return de({directory:c,connectionString:a.DATABASE_URL,postgres:l});let f=u.join(c,`database.dump`),p=U(a,a.DATABASE_URL);return await d(`pg_dump`,[`--format=custom`,`--file`,f],{cwd:i,env:p,stdin:`ignore`}),{directory:c,async discard(){},async restore(){await d(`pg_restore`,[`--clean`,`--if-exists`,`--no-owner`,`--dbname`,p.PGDATABASE,f],{cwd:i,env:p,stdin:`ignore`})}}}async function R(e,n){try{return await t(e,n),!0}catch(e){if(!(e instanceof Error&&`code`in e&&e.code===`ENOENT`))throw e;return!1}}async function ue(e){let t=JSON.parse(await a(u.join(e,`package.json`),`utf8`));if(!(!t.dependencies?.pg&&!t.devDependencies?.pg&&!t.optionalDependencies?.pg))try{return await import(y(`pg`,p(u.join(e,`package.json`)).href))}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ERR_MODULE_NOT_FOUND`)return;throw e}}async function de({directory:e,connectionString:t,postgres:n}){let r=new URL(t),i=decodeURIComponent(r.pathname.slice(1));if(!i)throw Error(`DATABASE_URL must name a PostgreSQL database to snapshot.`);let a=`coreframe_backup_${h(6).toString(`hex`)}`,o=`${a}_failed`,s=new URL(r);s.pathname=i===`postgres`?`/template1`:`/postgres`;let c=new n.Client({connectionString:s.href});await c.connect();try{await B(c,i,!1),await V(c,i),await c.query(`CREATE DATABASE ${H(a)} WITH TEMPLATE ${H(i)}`),await B(c,a,!0)}finally{await B(c,i,!0).catch(()=>void 0),await c.end()}return{directory:e,async discard(){await z(n,s.href,async e=>{await V(e,a),await e.query(`DROP DATABASE ${H(a)}`)})},async restore(){await z(n,s.href,async e=>{await B(e,i,!1),await V(e,i),await e.query(`ALTER DATABASE ${H(i)} RENAME TO ${H(o)}`);try{await e.query(`ALTER DATABASE ${H(a)} RENAME TO ${H(i)}`)}catch(t){throw await e.query(`ALTER DATABASE ${H(o)} RENAME TO ${H(i)}`).catch(()=>void 0),await B(e,i,!0).catch(()=>void 0),t}await V(e,o),await e.query(`DROP DATABASE ${H(o)}`)})}}}async function z(e,t,n){let r=new e.Client({connectionString:t});await r.connect();try{return await n(r)}finally{await r.end()}}async function B(e,t,n){await e.query(`ALTER DATABASE ${H(t)} ALLOW_CONNECTIONS ${n?`true`:`false`}`)}async function V(e,t){await e.query(`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()`,[t])}function H(e){return`"${e.replaceAll(`"`,`""`)}"`}function U(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 fe(e){return e.some(e=>u.basename(e)===`package.json`||u.basename(e).endsWith(`lock.yaml`)||u.basename(e)===`pnpm-workspace.yaml`)}function pe(e){return e.some(e=>fe([e])||/(^|\/)(vite|coreframe|tsconfig|drizzle)\.[^/]+$/.test(e)||/(^|\/)(drizzle|scripts|src\/api|tauri)(\/|$)/.test(e))}async function me(e,t,n){return{commit:t,shortCommit:t.slice(0,12),source:n,subject:await K(e,[`show`,`-s`,`--format=%s`,t])}}async function he(e){try{let t=await i(e,`wx`);await t.writeFile(`${g.pid}\n`),await t.close()}catch(t){if(!(t instanceof Error&&`code`in t&&t.code===`EEXIST`))throw t;let n=(await a(e,`utf8`).catch(()=>`unknown`)).trim(),r=Number(n);if(Number.isInteger(r)&&!W(r)){await c(e).catch(()=>void 0),await he(e);return}throw Error(`Managed development is already owned by PID ${n}.`)}}function W(e){try{return g.kill(e,0),!0}catch(e){return!(e instanceof Error&&`code`in e&&e.code===`ESRCH`)}}async function ge(e){e&&(await a(e,`utf8`).catch(()=>void 0))?.trim()===String(g.pid)&&await c(e).catch(()=>void 0)}async function G(e,t,{env:n}={}){return d(`git`,t,{cwd:e,env:n,stdin:`ignore`})}async function K(e,t){return(await G(e,t)).stdout.trim()}async function q(e,t){try{return await K(e,t)||void 0}catch(e){if(e instanceof f&&e.exitCode&&e.exitCode>0)return;throw e}}async function _e(e,t,n){try{return await G(e,[`merge-base`,`--is-ancestor`,t,n]),!0}catch(e){if(e instanceof f&&e.exitCode===1)return!1;throw e}}function ve(e,t){return e.length===t.length&&[...e].sort().every((e,n)=>e===[...t].sort()[n])}function J(e){return e.split(`\0`).filter(Boolean)}function Y(e){return e.split(/\r?\n/,1)[0]??``}function ye(e){return e.type===`message`?`Generating commit message`:e.type===`commit`?`Committing local changes`:e.type===`apply`?`Integrating accepted update`:e.type===`prepare`?`Reviewing working tree`:e.type===`retry-install`?`Retrying dependency installation`:e.type===`reset-database`?`Resetting local database`:`Aborting refresh`}function X(){return g.platform===`win32`?`pnpm.cmd`:`pnpm`}function Z(e){return e instanceof Error?e.message:String(e)}export{w as isManagedDevCheckout,T as isPrimaryCheckout,D as startManagedDevSession};
|
package/dist/runner.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as e}from"node:path";import
|
|
1
|
+
import{join as e}from"node:path";import{tmpdir as t}from"node:os";import*as n from"cmd-ts";import{mkdtempSync as r,writeFileSync as i}from"node:fs";import{inspect as a}from"node:util";function o(n,{temporaryDirectory:a=t(),write:o=e=>console.error(e)}={}){let s=c(n);try{let t=e(r(e(a,`coreframe-error-`)),`error.log`);return i(t,`${l(n)}\n`,{mode:384}),o(`Error: ${s}\nFull diagnostic: ${t}`),t}catch{o(`Error: ${s}\nThe full diagnostic could not be written to the temporary directory.`);return}}function s(e){let t=typeof e==`function`?e:()=>n.run(n.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(t).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){try{return a(e,{customInspect:!1,depth:10})}catch{return`The thrown value could not be inspected.`}}export{o as reportCliError,s as run};
|
package/dist/tauri-dev-output.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{mkdtemp as e}from"node:fs/promises";import t from"node:path";import{
|
|
1
|
+
import{mkdtemp as e}from"node:fs/promises";import t from"node:path";import{tmpdir as n}from"node:os";import{createWriteStream as r}from"node:fs";import{stripVTControlCharacters as i}from"node:util";import{once as a}from"node:events";import{finished as o}from"node:stream/promises";async function s({output:i,temporaryDirectory:o=n()}){let s=await e(t.join(o,`coreframe-ios-dev-`)),l=t.join(s,`tauri.log`),u=r(l,{flags:`wx`,mode:384});return await a(u,`open`),new c(l,u,i)}var c=class{logFile;closed=!1;failureLines=[];log;output;runtime=!1;showInstalledBundleId=!1;constructor(e,t,n){this.logFile=e,this.log=t,this.output=n}async consume(e){try{for await(let t of e)await d(this.log,`${t}\n`),this.rememberFailureLine(t),this.shouldShow(t)&&await d(this.output,`${t}\n`)}catch{}}async reportFailure(){await d(this.output,`iOS development failed. Recent diagnostic output:
|
|
2
2
|
`);for(let e of this.failureLines)await d(this.output,`${e}\n`);await d(this.output,`Full iOS log: ${this.logFile}\n`)}async close(){this.closed||(this.closed=!0,this.log.end(),await o(this.log))}shouldShow(e){let t=i(e).trim();if(t===`Building app...`)return this.runtime=!1,!0;if(l(t))return!1;if(t.startsWith(`Launched application with `))return this.runtime=!0,!0;if(this.runtime)return!0;if(t===`App installed:`)return this.showInstalledBundleId=!0,!0;if(this.showInstalledBundleId){if(t.length===0)return!1;if(this.showInstalledBundleId=!1,t.startsWith(`• bundleID:`))return!0}return/^(Archiving app|Exporting app|Extracting IPA|Deploying app to device|Pairing with device)\.\.\.$/u.test(t)||/^\*\* BUILD (?:SUCCEEDED|FAILED) \*\*$/u.test(t)||/^(?:Compiling|Checking|Finished)\b/u.test(t)||/^(?:Warn|Error)\b/u.test(t)||/(?:^|:\d+:\d+: )(?:warning|error)(?:\[[^\]]+\])?:/iu.test(t)||/^(?:fatal:|thread .+ panicked|Failed to |No device found\.|The following build commands failed:|command \[.+\] exited)/iu.test(t)||/^Info Watching\b/u.test(t)||/^note: run with RUST_BACKTRACE=/u.test(t)}rememberFailureLine(e){u(i(e).trim())||(this.failureLines.push(e),this.failureLines.length>40&&this.failureLines.shift())}};function l(e){return e.startsWith(`--- xcodebuild: WARNING: Using the first of multiple matching destinations:`)||/^Failed to load provisioning param(?:e)?ter list due to error:/u.test(e)||e==="`devicectl manage create` may support a reduced set of arguments."||/(?:^|\/)idevicesyslog$/u.test(e)}function u(e){return e.length===0||e.startsWith(`export `)||e.startsWith(`inflating:`)||e.startsWith(`Archive:`)||e.startsWith(`cd `)||e.startsWith(`/bin/sh -c `)||e.startsWith(`Command line invocation:`)||e.startsWith(`Build settings from command line:`)}async function d(e,t){e.write(t)||await a(e,`drain`)}export{s as createTauriDevOutput};
|
|
@@ -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{
|
|
2
|
+
import{coreframeGitRef as e,coreframeTemplateFile as t}from"./template-ref.js";import{join as n}from"node:path";import{tmpdir as r}from"node:os";import{existsSync as i,mkdtempSync as a,readFileSync as o,rmSync as s,writeFileSync as c}from"node:fs";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 i=a(n(r(),`coreframe-upgrade-skill-`)),o=n(i,`SKILL.md`);try{c(o,t);let n=f(d([`--no-pager`,`diff`,`--no-index`,`--color=never`,`--`,e,o]));n&&console.log(n)}finally{s(i,{force:!0,recursive:!0})}}function g({apply:t,target:r}){let a=n(m(),u),s=e(r);if(!i(a))throw Error(`Local ${u} does not exist.`);let l=o(a,`utf8`),d=p(r,u);return l===d?(console.log(`Coreframe upgrade skill matches ${s}:template/${u}.`),0):t?(c(a,d),console.error(`Updated ${u} from ${s}:template. Reread it before continuing the upgrade.`),20):(console.error(`Coreframe upgrade skill differs from ${s}:template/${u}.`),console.error(`Run again with --apply to update it, then reread ${u} before continuing.`),h(a,d),20)}export{g as checkUpgradeSkillCurrent};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readProjectConfig as e}from"./config.js";import{
|
|
1
|
+
import{readProjectConfig as e}from"./config.js";import{accessTokenSigningSecretName as t}from"./deploy/access-token-secret.js";import{deployEnvironments as n}from"./deploy/config.js";import{chmod as r,mkdir as i,readFile as a,rename as o,rm as s,stat as c,writeFile as l}from"node:fs/promises";import{resolve as u}from"node:path";import d from"nano-spawn";import{randomUUID as f}from"node:crypto";import{parseEnv as p}from"node:util";const m=[`secret`,`variable`,`generated`],h=/^[A-Z][A-Z0-9_]*$/,g=`.coreframe/deploy-secrets`;async function _({cwd:r=process.cwd(),environment:i,listSecrets:a=j}={}){let o=S((await e({cwd:r})).worker?.environment),s=await w(r),c=i?[i]:n,l=new Map,u=new Map,d=Object.fromEntries(n.map(e=>[e,{state:`not_checked`}]));await Promise.all(c.map(async e=>{if(u.set(e,new Set(Object.keys((await b({cwd:r,environment:e})).values))),o.some(e=>e.kind===`secret`))try{l.set(e,new Set(await a(e,r))),d[e]={state:`available`}}catch{d[e]={state:`unavailable`,message:`Could not read ${e} Worker secrets. Check Wrangler authentication and whether the Worker exists.`}}}));let f=new Set([t,...o.filter(e=>e.kind===`secret`).map(e=>e.name)]),p=Object.fromEntries(n.map(e=>[e,[...l.get(e)??[]].filter(e=>!f.has(e)).sort()]));return{entries:o.map(e=>({...e,local:s.has(e.name)?`override`:`default`,deployed:Object.fromEntries(n.map(t=>[t,T(e,t,d[t],l.get(t),u.get(t))]))})),providers:d,undeclaredSecrets:p}}async function v({cwd:e=process.cwd(),environment:t,name:n,value:r,ensureIgnored:i=O}){if(await C(e,n),!r.trim())throw Error(`Worker secret values cannot be empty.`);await i(t,e),await D(e,t,{...(await b({cwd:e,environment:t})).values,[n]:r})}async function y({cwd:e=process.cwd(),environment:t,name:n}){await C(e,n);let r=await b({cwd:e,environment:t});if(!(n in r.values))return!1;let i={...r.values};return delete i[n],await D(e,t,i),!0}async function b({cwd:t=process.cwd(),environment:n}){let r=await E(t,n),i=S((await e({cwd:t})).worker?.environment),a=new Set(i.filter(e=>e.kind===`secret`).map(e=>e.name)),o=Object.keys(r).filter(e=>!a.has(e));if(o.length>0)throw Error(`Pending ${n} Worker secrets are not declared as secrets in coreframe.config.ts: ${o.join(`, `)}.`);return{environment:n,values:r}}async function x(e,t=process.cwd()){let n=await E(t,e.environment),r=Object.fromEntries(Object.entries(n).filter(([t,n])=>e.values[t]!==n));await D(t,e.environment,r)}function S(e){return Object.entries(e??{}).map(([e,t])=>{if(!h.test(e))throw Error(`Invalid Worker environment name ${JSON.stringify(e)} in coreframe.config.ts. Use uppercase letters, numbers, and underscores.`);return{...t,name:e,requiredIn:[...new Set(t.requiredIn??[])]}}).sort((e,t)=>e.name.localeCompare(t.name))}async function C(t,n){let r=S((await e({cwd:t})).worker?.environment).find(e=>e.name===n);if(!r)throw Error(`${n} is not declared in coreframe.config.ts.`);if(r.kind!==`secret`)throw Error(`${n} is declared as ${r.kind}, not a Worker secret.`)}async function w(e){try{let t=p(await a(u(e,`.env`),`utf8`));return new Set(Object.entries(t).filter(([,e])=>!!e?.trim()).map(([e])=>e))}catch(e){if(M(e)&&e.code===`ENOENT`)return new Set;throw e}}function T(e,t,n,r,i){return e.kind===`generated`?`generated`:e.kind===`variable`?`declared`:i?.has(e.name)?n.state!==`available`||!r?`pending_update`:r.has(e.name)?`pending_replacement`:`pending_addition`:n.state!==`available`||!r?`unknown`:r.has(e.name)?`configured`:e.requiredIn.includes(t)?`missing`:`not_configured`}async function E(e,t){let n=k(e,t);try{let e=await c(n);if(process.platform!==`win32`&&e.mode&63)throw Error(`Pending Worker secret file ${n} must use mode 0600.`);let t=JSON.parse(await a(n,`utf8`));if(!A(t))throw Error(`Pending Worker secret file ${n} must contain a JSON object of strings.`);return t}catch(e){if(M(e)&&e.code===`ENOENT`)return{};throw e}}async function D(e,t,n){let a=k(e,t);if(Object.keys(n).length===0){await s(a,{force:!0});return}let c=u(e,g);await i(c,{mode:448,recursive:!0}),process.platform!==`win32`&&await r(c,448);let d=`${a}.${process.pid}.${f()}.tmp`;try{await l(d,`${JSON.stringify(n,null,2)}\n`,{flag:`wx`,mode:384}),await o(d,a)}finally{await s(d,{force:!0})}process.platform!==`win32`&&await r(a,384)}async function O(e,t){let n=`${g}/${e}.json`,r=`${g}/.coreframe-ignore-check`;try{await d(`git`,[`check-ignore`,`--quiet`,r],{cwd:t,stdin:`ignore`,stdout:`ignore`,stderr:`ignore`})}catch{throw Error(`Refusing to store plaintext Worker secrets because ${n} is not git-ignored. Add .coreframe/ to .gitignore.`)}}function k(e,t){return u(e,g,`${t}.json`)}function A(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&Object.entries(e).every(([e,t])=>h.test(e)&&typeof t==`string`&&t.length>0)}async function j(e,t){let{stdout:n}=await d(`pnpm`,[`exec`,`wrangler`,`secret`,`list`,`--env`,e,`--format`,`json`],{cwd:t,env:{CI:`true`},stdin:`ignore`,timeout:5e3}),r=JSON.parse(n);if(!Array.isArray(r))throw Error(`Wrangler returned an invalid secret list.`);return r.flatMap(e=>e&&typeof e==`object`&&`name`in e&&typeof e.name==`string`?[e.name]:[])}function M(e){return e instanceof Error&&`code`in e}export{x as clearAppliedPendingWorkerSecrets,y as discardPendingWorkerSecret,_ as inspectWorkerEnvironment,v as queueWorkerSecret,b as readPendingWorkerSecretSnapshot,m as workerEnvironmentKinds};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreframe/scripts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/airlock-labs/coreframe.git",
|
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
+
"@aws-sdk/client-s3": "^3.1090.0",
|
|
24
25
|
"@clack/prompts": "^1.6.0",
|
|
25
26
|
"cmd-ts": "0.15.0",
|
|
27
|
+
"cross-keychain": "1.1.0",
|
|
26
28
|
"drizzle-orm": "1.0.0-rc.4",
|
|
27
29
|
"import-meta-resolve": "^4.2.0",
|
|
28
30
|
"local-postgres": "0.3.0",
|