@coreframe/scripts 0.1.44 → 0.1.45
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/storage.d.ts +2 -5
- package/dist/assets/storage.js +1 -1
- package/dist/config.d.ts +13 -1
- package/dist/coreframe.d.ts +60 -2
- package/dist/coreframe.js +2 -2
- package/dist/deploy.js +2 -2
- package/dist/i18n/catalogs.d.ts +61 -0
- package/dist/i18n/catalogs.js +1 -0
- package/dist/i18n/contracts.d.ts +61 -0
- package/dist/i18n/contracts.js +1 -0
- package/dist/i18n/paths.d.ts +12 -0
- package/dist/i18n/paths.js +1 -0
- package/dist/i18n/storage.d.ts +117 -0
- package/dist/i18n/storage.js +1 -0
- package/dist/i18n/workflows.d.ts +39 -0
- package/dist/i18n/workflows.js +1 -0
- package/dist/private-object-store.d.ts +28 -0
- package/dist/private-object-store.js +1 -0
- package/dist/project.d.ts +1 -1
- package/dist/project.js +1 -1
- package/dist/worker-environment.d.ts +24 -1
- package/dist/worker-environment.js +1 -1
- package/package.json +1 -1
package/dist/assets/storage.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { AssetsConfig } from "../config.js";
|
|
2
2
|
import { AssetCredentialStore } from "./credentials.js";
|
|
3
|
+
import { PrivateObjectStore } from "../private-object-store.js";
|
|
3
4
|
|
|
4
5
|
//#region assets/storage.d.ts
|
|
5
|
-
type AssetObjectStore =
|
|
6
|
-
has(bucket: string, key: string): Promise<boolean>;
|
|
7
|
-
download(bucket: string, key: string, target: string): Promise<void>;
|
|
8
|
-
upload(bucket: string, key: string, source: string, contentType: string): Promise<void>;
|
|
9
|
-
};
|
|
6
|
+
type AssetObjectStore = PrivateObjectStore;
|
|
10
7
|
declare function assetSourceKey(digest: string): string;
|
|
11
8
|
declare function createB2AssetStore({
|
|
12
9
|
config,
|
package/dist/assets/storage.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createB2PrivateObjectStore as e}from"../private-object-store.js";import{createHash as t,randomUUID as n}from"node:crypto";import{copyFile as r,mkdir as i,open as a,readdir as o,rename as s,rm as c,stat as l,utimes as u,writeFile as d}from"node:fs/promises";import{dirname as f,join as p,resolve as m}from"node:path";import{createReadStream as h}from"node:fs";import{homedir as g,platform as _}from"node:os";function v(e){return j(e),`sources/v1/sha256/${e.slice(0,2)}/${e}`}function y({config:t,credentialStore:n,env:r=process.env}){return e({config:t,credentialStore:n,env:r})}function b(e=process.env){return e.COREFRAME_ASSETS_CACHE_DIR?m(e.COREFRAME_ASSETS_CACHE_DIR):_()===`darwin`?p(g(),`Library`,`Caches`,`coreframe`,`assets`):_()===`win32`?p(e.LOCALAPPDATA??p(g(),`AppData`,`Local`),`Coreframe`,`assets`):p(e.XDG_CACHE_HOME??p(g(),`.cache`),`coreframe`,`assets`)}function x(e,t){return j(t),p(e,`v1`,`sha256`,t.slice(0,2),t)}async function S({body:e,cacheRoot:t,digest:r,env:a=process.env}){M(e,r);let o=x(t,r);return await T(o,async()=>{if(await E(o,r)){await D(o);return}await i(f(o),{recursive:!0});let t=`${o}.${process.pid}.${n()}.tmp`;try{await d(t,e,{flag:`wx`}),await s(t,o)}finally{await c(t,{force:!0})}}),await O(t,A(a),o),o}async function C({cacheRoot:e,digest:t,env:a=process.env,source:o}){if(await P(o)!==t)throw Error(`Asset source does not match declared SHA-256 digest ${t}.`);let l=x(e,t);return await T(l,async()=>{if(await E(l,t)){await D(l);return}await i(f(l),{recursive:!0});let e=`${l}.${process.pid}.${n()}.tmp`;try{if(await r(o,e),await P(e)!==t)throw Error(`Asset source changed while it was being cached.`);await s(e,l)}finally{await c(e,{force:!0})}}),await O(e,A(a),l),l}async function w({bucket:e,cacheRoot:t,digest:r,env:a=process.env,store:o}){let l=x(t,r);if(await E(l,r))return await D(l),{fetched:!1,path:l};let u=!1;return await T(l,async()=>{if(await E(l,r)){await D(l);return}await i(f(l),{recursive:!0});let t=`${l}.${process.pid}.${n()}.tmp`;try{if(await o.download(e,v(r),t),await P(t)!==r)throw Error(`Asset source does not match declared SHA-256 digest ${r}.`);await s(t,l),u=!0}finally{await c(t,{force:!0})}}),await O(t,A(a),l),{fetched:u,path:l}}async function T(e,t){await i(f(e),{recursive:!0});let n=`${e}.lock`,r=Date.now();for(;;){let e;try{e=await a(n,`wx`)}catch(e){if(e.code!==`EEXIST`)throw e;let t=await l(n).catch(()=>void 0);if(t&&Date.now()-t.mtimeMs>12e4){await c(n,{force:!0});continue}if(Date.now()-r>=3e4)throw Error(`Timed out waiting for the asset cache lock at ${n}.`);await new Promise(e=>setTimeout(e,50));continue}try{await t()}finally{await e.close(),await c(n,{force:!0})}return}}async function E(e,t){try{return await P(e)===t}catch{return!1}}async function D(e){let t=new Date;await u(e,t,t)}async function O(e,t,n){let r=await k(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 c(e.path,{force:!0}),i-=e.size)}}async function k(e){let t=[],n=async e=>{let r=await o(e,{withFileTypes:!0}).catch(()=>[]);for(let i of r){let r=p(e,i.name);if(i.isDirectory())await n(r);else if(!i.name.endsWith(`.lock`)&&!i.name.endsWith(`.tmp`)){let e=await l(r);t.push({path:r,size:e.size,mtimeMs:e.mtimeMs})}}};return await n(e),t}function A(e){let t=Number(e.COREFRAME_ASSETS_CACHE_MAX_BYTES);return Number.isSafeInteger(t)&&t>0?t:5368709120}function j(e){if(!/^[0-9a-f]{64}$/.test(e))throw Error(`Invalid SHA-256 asset digest: ${e}`)}function M(e,t){let n=N(e);if(n!==t)throw Error(`Asset integrity check failed: expected sha256:${t}, received sha256:${n}.`)}function N(e){return t(`sha256`).update(e).digest(`hex`)}async function P(e){let n=t(`sha256`);for await(let t of h(e))n.update(t);return n.digest(`hex`)}export{x as assetCacheFile,b as assetCacheRoot,v as assetSourceKey,S as cacheAssetSource,C as cacheAssetSourceFile,y as createB2AssetStore,w as materializeAssetSource,N as sha256,P as sha256File};
|
package/dist/config.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ type CoreframeConfig = {
|
|
|
9
9
|
database?: DatabaseConfig;
|
|
10
10
|
deploy?: DeployConfig;
|
|
11
11
|
dev?: DevConfig;
|
|
12
|
+
i18n?: I18nConfig;
|
|
12
13
|
worker?: WorkerConfig;
|
|
13
14
|
};
|
|
14
15
|
type AssetsConfig = {
|
|
@@ -23,6 +24,17 @@ type AssetsConfig = {
|
|
|
23
24
|
type DevConfig = {
|
|
24
25
|
platforms?: DevPlatform[];
|
|
25
26
|
};
|
|
27
|
+
type I18nConfig = {
|
|
28
|
+
catalogs: string[];
|
|
29
|
+
directory?: string;
|
|
30
|
+
generator?: {
|
|
31
|
+
args?: string[];
|
|
32
|
+
command: string;
|
|
33
|
+
id: string;
|
|
34
|
+
};
|
|
35
|
+
locales: string[];
|
|
36
|
+
sourceLocale: string;
|
|
37
|
+
};
|
|
26
38
|
type DatabaseConfig = {
|
|
27
39
|
dialect: DatabaseDialect;
|
|
28
40
|
seed?: string;
|
|
@@ -34,4 +46,4 @@ declare function readProjectConfig({
|
|
|
34
46
|
cwd?: string;
|
|
35
47
|
}): Promise<CoreframeConfig>;
|
|
36
48
|
//#endregion
|
|
37
|
-
export { AssetsConfig, CoreframeConfig, DatabaseConfig, DevConfig, defineConfig, readProjectConfig };
|
|
49
|
+
export { AssetsConfig, CoreframeConfig, DatabaseConfig, DevConfig, I18nConfig, defineConfig, readProjectConfig };
|
package/dist/coreframe.d.ts
CHANGED
|
@@ -44,6 +44,22 @@ declare const coreframeCli: Partial<import("@alloc/cmd-ts/dist/cjs/argparser").R
|
|
|
44
44
|
json: boolean;
|
|
45
45
|
};
|
|
46
46
|
};
|
|
47
|
+
} | {
|
|
48
|
+
command: "i18n";
|
|
49
|
+
args: {
|
|
50
|
+
command: "build";
|
|
51
|
+
args: {};
|
|
52
|
+
} | {
|
|
53
|
+
command: "materialize";
|
|
54
|
+
args: {
|
|
55
|
+
revision: string | undefined;
|
|
56
|
+
};
|
|
57
|
+
} | {
|
|
58
|
+
command: "prepare";
|
|
59
|
+
args: {
|
|
60
|
+
tag: string;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
47
63
|
} | {
|
|
48
64
|
command: "deploy";
|
|
49
65
|
args: {
|
|
@@ -88,7 +104,8 @@ declare const coreframeCli: Partial<import("@alloc/cmd-ts/dist/cjs/argparser").R
|
|
|
88
104
|
command: "set";
|
|
89
105
|
args: {
|
|
90
106
|
environment: "staging" | "production";
|
|
91
|
-
name: string;
|
|
107
|
+
name: string | undefined;
|
|
108
|
+
bulk: boolean;
|
|
92
109
|
};
|
|
93
110
|
} | {
|
|
94
111
|
command: "discard";
|
|
@@ -250,6 +267,22 @@ declare const coreframeCli: Partial<import("@alloc/cmd-ts/dist/cjs/argparser").R
|
|
|
250
267
|
json: boolean;
|
|
251
268
|
};
|
|
252
269
|
};
|
|
270
|
+
} | {
|
|
271
|
+
command: "i18n";
|
|
272
|
+
args: {
|
|
273
|
+
command: "build";
|
|
274
|
+
args: {};
|
|
275
|
+
} | {
|
|
276
|
+
command: "materialize";
|
|
277
|
+
args: {
|
|
278
|
+
revision: string | undefined;
|
|
279
|
+
};
|
|
280
|
+
} | {
|
|
281
|
+
command: "prepare";
|
|
282
|
+
args: {
|
|
283
|
+
tag: string;
|
|
284
|
+
};
|
|
285
|
+
};
|
|
253
286
|
} | {
|
|
254
287
|
command: "deploy";
|
|
255
288
|
args: {
|
|
@@ -294,7 +327,8 @@ declare const coreframeCli: Partial<import("@alloc/cmd-ts/dist/cjs/argparser").R
|
|
|
294
327
|
command: "set";
|
|
295
328
|
args: {
|
|
296
329
|
environment: "staging" | "production";
|
|
297
|
-
name: string;
|
|
330
|
+
name: string | undefined;
|
|
331
|
+
bulk: boolean;
|
|
298
332
|
};
|
|
299
333
|
} | {
|
|
300
334
|
command: "discard";
|
|
@@ -432,6 +466,18 @@ declare const coreframeCli: Partial<import("@alloc/cmd-ts/dist/cjs/argparser").R
|
|
|
432
466
|
command: "status";
|
|
433
467
|
value: Promise<void>;
|
|
434
468
|
};
|
|
469
|
+
} | {
|
|
470
|
+
command: "i18n";
|
|
471
|
+
value: {
|
|
472
|
+
command: "build";
|
|
473
|
+
value: Promise<void>;
|
|
474
|
+
} | {
|
|
475
|
+
command: "materialize";
|
|
476
|
+
value: Promise<void>;
|
|
477
|
+
} | {
|
|
478
|
+
command: "prepare";
|
|
479
|
+
value: Promise<void>;
|
|
480
|
+
};
|
|
435
481
|
} | {
|
|
436
482
|
command: "deploy";
|
|
437
483
|
value: Promise<void>;
|
|
@@ -547,6 +593,18 @@ declare const coreframeCli: Partial<import("@alloc/cmd-ts/dist/cjs/argparser").R
|
|
|
547
593
|
command: "status";
|
|
548
594
|
value: Promise<void>;
|
|
549
595
|
};
|
|
596
|
+
} | {
|
|
597
|
+
command: "i18n";
|
|
598
|
+
value: {
|
|
599
|
+
command: "build";
|
|
600
|
+
value: Promise<void>;
|
|
601
|
+
} | {
|
|
602
|
+
command: "materialize";
|
|
603
|
+
value: Promise<void>;
|
|
604
|
+
} | {
|
|
605
|
+
command: "prepare";
|
|
606
|
+
value: Promise<void>;
|
|
607
|
+
};
|
|
550
608
|
} | {
|
|
551
609
|
command: "deploy";
|
|
552
610
|
value: Promise<void>;
|
package/dist/coreframe.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
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{formatDbErrorMessage as p}from"./db/error.js";import{migrateHeldMigrations as m}from"./db/migrate-held.js";import{checkSchemaPush as h}from"./db/schema-check.js";import{rotateAccessTokenSigningSecret as g}from"./deploy/access-token-secret.js";import{deployEnvironments as _,tauriReleasePlatforms as v}from"./deploy/config.js";import{discoverStagingUrl as y,reassignStagingHostname as b}from"./staging.js";import{discardPendingWorkerSecret as ee,inspectWorkerEnvironment as te,queueWorkerSecret as x}from"./worker-environment.js";import{deployProject as S}from"./deploy.js";import{checkDeployMigrations as C}from"./deploy/check-migrations.js";import{devDebugFeatures as w,devPlatforms as T,runDevServer as ne}from"./dev.js";import{validateRecipeName as re}from"./recipes/manifest.js";import{ensureTemplateRemote as E}from"./upgrade/ensure-template-remote.js";import{applyRecipe as D,printAppliedRecipe as O}from"./recipes/apply.js";import{listRecipes as k,printRecipeCatalog as A}from"./recipes/list.js";import{run as j}from"./runner.js";import{typecheckProject as M}from"./typecheck.js";import{checkUpgradeSkillCurrent as N}from"./upgrade/check-skill-current.js";import{fastForwardTemplateFiles as P}from"./upgrade/fast-forward-template-files.js";import{syncCoreframePackages as F}from"./upgrade/sync-coreframe-packages.js";import{array as I,command as L,extendType as R,flag as z,multioption as B,number as ie,oneOf as V,option as H,optional as U,positional as W,rest as G,string as K,subcommands as q}from"@alloc/cmd-ts";import*as J from"@clack/prompts";const Y=R(V(w),{displayName:`feature`,from:async e=>e}),X=[`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.`],Z=[`Use a B2 Multi-Bucket Application Key restricted to the configured private source and public generated-assets buckets.`,`Required capabilities: listAllBucketNames, readFiles, and writeFiles.`,`Deletion, bucket-management, and key-management capabilities are not needed.`].join(` `),ae=R(K,{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}}),oe=R(K,{displayName:`recipe`,description:`Coreframe recipe name, such as yam-db.`,async from(e){return re(e)}}),se=R(K,{displayName:`profile`,async from(t){return e(t)}}),Q=R(K,{displayName:`pack`,description:`Asset pack name, such as training-videos.`,async from(e){if(!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(e))throw Error(`Asset pack names must use lowercase hyphen-separated segments.`);return e}}),$=q({name:`coreframe`,cmds:{assets:q({name:`assets`,cmds:{configure:L({name:`configure`,description:`Store a Backblaze B2 asset key in the operating system credential store. ${Z}`,args:{profile:W({type:se,displayName:`profile`,description:`Credential profile named by coreframe.config.ts.`})},handler:async({profile:e})=>{J.note(Z,`Required B2 key access`);let n=await J.text({message:`Backblaze B2 application key ID`,validate:e=>e?.trim()?void 0:`The application key ID is required.`});if(J.isCancel(n))throw Error(`Asset credential configuration was cancelled.`);let r=await J.password({message:`Backblaze B2 application key`,validate:e=>e?void 0:`The application key is required.`});if(J.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:L({name:`add`,args:{file:W({type:K,displayName:`file`,description:`Source raster image, SVG, MP3, MP4, or Lottie JSON to add to the asset library.`}),id:H({long:`id`,type:K,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:H({long:`alt`,type:U(K),description:`Required alt text for raster image and SVG assets.`}),pack:H({long:`pack`,type:U(Q),description:`Place the asset in a named, lazily loaded pack.`}),variantWidths:H({long:`widths`,type:U(ae),description:`Generate exactly these raster-image widths instead of the default ladder.`}),json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({alt:e,file:t,id:r,json:i,pack:o,variantWidths:s})=>a(await n({alt:e,file:t,id:r,pack:o,variantWidths:s}),i)}),build:L({name:`build`,args:{id:H({long:`id`,type:U(K),description:`Build only one logical asset ID.`}),pack:H({long:`pack`,type:U(Q),description:`Build only one named asset pack.`}),json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({id:e,json:t,pack:n})=>a(await r({id:e,pack:n}),t)}),check:L({name:`check`,args:{fix:z({long:`fix`,description:`Migrate asset state and build missing generated output when possible.`,defaultValue:()=>!1}),pack:H({long:`pack`,type:U(Q),description:`Check only one named asset pack.`}),json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({fix:e,json:t,pack:n})=>{let r=await i({fix:e,pack:n});a(r,t),r.ok||(process.exitCode=1)}}),publish:L({name:`publish`,args:{pack:H({long:`pack`,type:U(Q),description:`Publish only one named asset pack.`}),json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e,pack:t})=>{let n=await o({pack:t});a(n,e),n.ok||(process.exitCode=1)}}),status:L({name:`status`,args:{pack:H({long:`pack`,type:U(Q),description:`Report status for only one named asset pack.`}),json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e,pack:t})=>{let n=await i({pack:t});a(n,e),n.ok||(process.exitCode=1)}})}}),deploy:L({name:`deploy`,args:{environment:W({type:V(_),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(V(v)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:H({long:`tauri-workflow-name`,type:U(K),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:B({long:`tauri-workflow-platform`,type:I(V(v)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:H({long:`tauri-workflow-ref`,type:U(K),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})=>S([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:q({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 b();if(e){console.log(JSON.stringify({...t,notices:X}));return}console.log(t.url),console.error(`Staging hostname reassigned. ${X.join(` `)}`)}}),url:L({name:`url`,args:{json:z({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await y();console.log(e?JSON.stringify({url:t}):t)}})}}),secrets:q({name:`secrets`,cmds:{"rotate-access-token":L({name:`rotate-access-token`,args:{environment:W({type:V(_),displayName:`environment`,description:`Worker environment whose access JWTs should be invalidated.`}),yes:z({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 g({environment:e}),console.log(`Rotated Coreframe access-token signing material for ${e}. Clients must remint outstanding access JWTs.`)}}),status:L({name:`status`,args:{environment:W({type:U(V(_)),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 te({environment:e});console.log(t?JSON.stringify(n):ce(n))}}),set:L({name:`set`,args:{environment:W({type:V(_),displayName:`environment`,description:`Worker environment to update on its next deploy.`}),name:W({type:K,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{await x({environment:e,name:t,value:await le(t)}),console.log(`Queued ${t} for ${e}. Run coreframe deploy ${e} to apply.`)}}),discard:L({name:`discard`,args:{environment:W({type:V(_),displayName:`environment`,description:`Worker environment with a queued secret update.`}),name:W({type:K,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{let n=await ee({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:M}),dev:L({name:`dev`,args:{debug:H({long:`debug`,type:U(Y),description:`Launch a development feature in isolated debug mode.`}),https:z({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:H({long:`host`,type:U(K),implicitValue:`0.0.0.0`,description:`Host address for the web development server. Pass without an address to use 0.0.0.0.`}),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:W({type:U(V(T)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),device:W({type:U(K),displayName:`device`,description:`Device name or ID for iOS or Android development.`}),port:H({long:`port`,type:U(ie),description:`Port for the web development server.`}),verbose:z({long:`verbose`,description:`Show the full native development output.`,defaultValue:()=>!1})},handler:({debug:e,device:t,host:n,https:r,managed:i,noManaged:a,platform:o,port:s,verbose:c})=>ne({debug:e,device:t,host:n,https:r,managed:i,noManaged:a,platform:o??`web`,verbose:c,webPort:s})}),upgrade:q({name:`upgrade`,cmds:{"ensure-template-remote":L({name:`ensure-template-remote`,args:{remote:H({long:`remote`,type:K,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})=>E({fetch:!e,remote:t})}),"check-skill-current":L({name:`check-skill-current`,args:{target:H({long:`target`,type:K,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=N({apply:e,target:t})}}),"fast-forward-template-files":L({name:`fast-forward-template-files`,args:{target:H({long:`target`,type:K,description:`Target Coreframe template git ref.`}),base:H({long:`base`,type:U(K),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})=>P({apply:e,base:t,target:n})}),"sync-coreframe-packages":L({name:`sync-coreframe-packages`,args:{target:H({long:`target`,type:K,description:`Target Coreframe template git ref.`}),apply:z({long:`apply`,description:`Update existing published Coreframe dependency specifications.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>F({apply:e,target:t})})}}),recipes:q({name:`recipes`,cmds:{apply:L({name:`apply`,args:{recipe:W({type:oe,displayName:`recipe`,description:`Recipe to prepare and apply.`}),target:H({long:`target`,type:U(K),description:`Coreframe Git ref. Defaults to package.json engines.coreframe.`}),remote:H({long:`remote`,type:K,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})=>O(D({fetch:!e,name:t,remote:n,target:r}))}),list:L({name:`list`,args:{target:H({long:`target`,type:U(K),description:`Coreframe Git ref. Defaults to package.json engines.coreframe.`}),remote:H({long:`remote`,type:K,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})=>A(k({fetch:!e,remote:t,target:n}))})}}),db:q({name:`db`,cmds:{ensure:L({name:`ensure`,args:{},handler:f}),generate:L({name:`generate`,args:{args:G({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>s(`generate`,e)}),migrate:L({name:`migrate`,args:{args:G({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>s(`migrate`,e)}),"migrate-held":L({name:`migrate-held`,args:{confirm:H({long:`confirm`,type:U(V([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>m({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":L({name:`migrate-local`,args:{},handler:l}),"migrations-check":L({name:`migrations-check`,args:{},handler:C}),reset:L({name:`reset`,args:{},handler:u}),seed:L({name:`seed`,args:{},handler:()=>c()}),"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})=>h({seed:e})}),setup:L({name:`setup`,args:{},handler:()=>d()}),studio:L({name:`studio`,args:{args:G({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>s(`studio`,e)})}})}});import.meta.main&&j($,{formatMessage:process.argv[2]===`db`?p:void 0});function ce(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{formatDbErrorMessage as p}from"./db/error.js";import{migrateHeldMigrations as ee}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{buildI18n as te,materializeI18nSnapshot as ne,prepareI18nRelease as re}from"./i18n/workflows.js";import{discoverStagingUrl as ie,reassignStagingHostname as ae}from"./staging.js";import{discardPendingWorkerSecret as oe,inspectWorkerEnvironment as se,parseDeclaredWorkerSecretEnv as ce,queueWorkerSecret as le,queueWorkerSecrets as ue,readDeclaredWorkerSecrets as v}from"./worker-environment.js";import{deployProject as y}from"./deploy.js";import{checkDeployMigrations as b}from"./deploy/check-migrations.js";import{devDebugFeatures as x,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{array as F,command as I,extendType as L,flag as R,multioption as z,number as B,oneOf as V,option as H,optional as U,positional as W,rest as G,string as K,subcommands as q}from"@alloc/cmd-ts";import*as J from"@clack/prompts";const de=L(V(x),{displayName:`feature`,from:async e=>e}),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=[`Use a B2 Multi-Bucket Application Key restricted to the configured private build-object and public generated-assets buckets.`,`Required capabilities: listAllBucketNames, readFiles, and writeFiles.`,`Deletion, bucket-management, and key-management capabilities are not needed.`].join(` `),fe=L(K,{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}}),pe=L(K,{displayName:`recipe`,description:`Coreframe recipe name, such as yam-db.`,async from(e){return w(e)}}),me=L(K,{displayName:`profile`,async from(t){return e(t)}}),Z=L(K,{displayName:`pack`,description:`Asset pack name, such as training-videos.`,async from(e){if(!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(e))throw Error(`Asset pack names must use lowercase hyphen-separated segments.`);return e}}),Q=q({name:`coreframe`,cmds:{assets:q({name:`assets`,cmds:{configure:I({name:`configure`,description:`Store a Backblaze B2 private build-object key in the operating system credential store. ${X}`,args:{profile:W({type:me,displayName:`profile`,description:`Credential profile named by coreframe.config.ts.`})},handler:async({profile:e})=>{J.note(X,`Required B2 key access`);let n=await J.text({message:`Backblaze B2 application key ID`,validate:e=>e?.trim()?void 0:`The application key ID is required.`});if(J.isCancel(n))throw Error(`Asset credential configuration was cancelled.`);let r=await J.password({message:`Backblaze B2 application key`,validate:e=>e?void 0:`The application key is required.`});if(J.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:I({name:`add`,args:{file:W({type:K,displayName:`file`,description:`Source raster image, SVG, MP3, MP4, or Lottie JSON to add to the asset library.`}),id:H({long:`id`,type:K,description:`Logical asset ID, such as blog/my-post/hero.`}),alt:H({long:`alt`,type:U(K),description:`Required alt text for raster image and SVG assets.`}),pack:H({long:`pack`,type:U(Z),description:`Place the asset in a named, lazily loaded pack.`}),variantWidths:H({long:`widths`,type:U(fe),description:`Generate exactly these raster-image widths instead of the default ladder.`}),json:R({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({alt:e,file:t,id:r,json:i,pack:o,variantWidths:s})=>a(await n({alt:e,file:t,id:r,pack:o,variantWidths:s}),i)}),build:I({name:`build`,args:{id:H({long:`id`,type:U(K),description:`Build only one logical asset ID.`}),pack:H({long:`pack`,type:U(Z),description:`Build only one named asset pack.`}),json:R({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({id:e,json:t,pack:n})=>a(await r({id:e,pack:n}),t)}),check:I({name:`check`,args:{fix:R({long:`fix`,description:`Migrate asset state and build missing generated output when possible.`,defaultValue:()=>!1}),pack:H({long:`pack`,type:U(Z),description:`Check only one named asset pack.`}),json:R({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({fix:e,json:t,pack:n})=>{let r=await i({fix:e,pack:n});a(r,t),r.ok||(process.exitCode=1)}}),publish:I({name:`publish`,args:{pack:H({long:`pack`,type:U(Z),description:`Publish only one named asset pack.`}),json:R({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e,pack:t})=>{let n=await o({pack:t});a(n,e),n.ok||(process.exitCode=1)}}),status:I({name:`status`,args:{pack:H({long:`pack`,type:U(Z),description:`Report status for only one named asset pack.`}),json:R({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e,pack:t})=>{let n=await i({pack:t});a(n,e),n.ok||(process.exitCode=1)}})}}),i18n:q({name:`i18n`,cmds:{build:I({name:`build`,description:`Extract source messages and compile ignored source-fallback locale modules.`,args:{},handler:async()=>{await te()}}),materialize:I({name:`materialize`,description:`Materialize the immutable i18n snapshot for an exact commit.`,args:{revision:H({long:`revision`,type:U(K),description:`Full commit SHA. Defaults to the checked-out commit.`})},handler:async({revision:e})=>{let t=await ne({revision:e});t&&console.log(`Materialized i18n snapshot ${t.snapshot}.`)}}),prepare:I({name:`prepare`,description:`Fill missing translations, strictly compile, and publish an immutable tagged snapshot.`,args:{tag:H({long:`tag`,type:K,description:`Pushed v* release tag that points to the checked-out commit.`})},handler:async({tag:e})=>{let t=await re({tag:e});console.log(`${t.reused?`Reused`:`Published`} i18n snapshot ${t.snapshot}.`)}})}}),deploy:I({name:`deploy`,args:{environment:W({type:V(g),displayName:`environment`,description:`Deploy to the staging or production environment.`}),runMigrations:R({long:`run-migrations`,description:`Run pending deploy-safe database migrations before deploying.`,defaultValue:()=>!1}),skipMigrations:R({long:`skip-migrations`,description:`Deploy without running pending deploy-safe database migrations.`,defaultValue:()=>!1}),skipPostDeployChecks:R({long:`skip-post-deploy-checks`,description:`Skip post-deploy checks after Cloudflare deployment.`,defaultValue:()=>!1}),tauriLocalPlatforms:z({long:`tauri-local-platform`,type:F(V(_)),description:`Build one Tauri release platform locally. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowName:H({long:`tauri-workflow-name`,type:U(K),description:`GitHub Actions workflow file or name for remote Tauri release builds.`}),tauriWorkflowPlatforms:z({long:`tauri-workflow-platform`,type:F(V(_)),description:`Build one Tauri release platform with GitHub Actions. Repeat for multiple platforms.`,defaultValue:()=>[]}),tauriWorkflowRef:H({long:`tauri-workflow-ref`,type:U(K),description:`Git ref used when dispatching remote Tauri release builds.`}),yes:R({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})=>y([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:q({name:`staging`,cmds:{reassign:I({name:`reassign`,args:{json:R({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await ae();if(e){console.log(JSON.stringify({...t,notices:Y}));return}console.log(t.url),console.error(`Staging hostname reassigned. ${Y.join(` `)}`)}}),url:I({name:`url`,args:{json:R({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({json:e})=>{let t=await ie();console.log(e?JSON.stringify({url:t}):t)}})}}),secrets:q({name:`secrets`,cmds:{"rotate-access-token":I({name:`rotate-access-token`,args:{environment:W({type:V(g),displayName:`environment`,description:`Worker environment whose access JWTs should be invalidated.`}),yes:R({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:I({name:`status`,args:{environment:W({type:U(V(g)),displayName:`environment`,description:`Inspect staging, production, or both when omitted.`}),json:R({long:`json`,description:`Print machine-readable JSON.`,defaultValue:()=>!1})},handler:async({environment:e,json:t})=>{let n=await se({environment:e});console.log(t?JSON.stringify(n):he(n))}}),set:I({name:`set`,args:{environment:W({type:V(g),displayName:`environment`,description:`Worker environment to update on its next deploy.`}),name:W({type:U(K),displayName:`name`,description:`Declared Worker secret name; omit to configure known secrets.`}),bulk:R({long:`bulk`,description:`Queue known secrets from pasted or piped dotenv-formatted contents.`,defaultValue:()=>!1})},handler:async({environment:e,name:t,bulk:n})=>{if(n&&t)throw Error(`Use either a secret name or --bulk, not both.`);if(t){await le({environment:e,name:t,value:await ge(t)}),console.log(`Queued ${t} for ${e}. Run coreframe deploy ${e} to apply.`);return}let r=await v(),i=n?await _e(r):await $(r,e);await ue({environment:e,values:i});let a=Object.keys(i).sort();console.log(a.length>0?`Queued ${a.join(`, `)} for ${e}. Run coreframe deploy ${e} to apply.`:`No ${e} Worker secret updates were queued.`)}}),discard:I({name:`discard`,args:{environment:W({type:V(g),displayName:`environment`,description:`Worker environment with a queued secret update.`}),name:W({type:K,displayName:`name`,description:`Declared Worker secret name.`})},handler:async({environment:e,name:t})=>{let n=await oe({environment:e,name:t});console.log(n?`Discarded the queued ${t} update for ${e}.`:`No ${t} update is queued for ${e}.`)}})}}),typecheck:I({name:`typecheck`,args:{},handler:j}),dev:I({name:`dev`,args:{debug:H({long:`debug`,type:U(de),description:`Launch a development feature in isolated debug mode.`}),https:R({long:`https`,description:`Serve the web app over locally trusted HTTPS.`,defaultValue:()=>!1}),host:H({long:`host`,type:U(K),implicitValue:`0.0.0.0`,description:`Host address for the web development server. Pass without an address to use 0.0.0.0.`}),managed:R({long:`managed`,description:`Run managed checkout synchronization even without an interactive terminal.`,defaultValue:()=>!1}),noManaged:R({long:`no-managed`,description:`Run without managed checkout synchronization for this invocation.`,defaultValue:()=>!1}),platform:W({type:U(V(S)),displayName:`platform`,description:`Development target platform. Defaults to web.`}),device:W({type:U(K),displayName:`device`,description:`Device name or ID for iOS or Android development.`}),port:H({long:`port`,type:U(B),description:`Port for the web development server.`}),verbose:R({long:`verbose`,description:`Show the full native development output.`,defaultValue:()=>!1})},handler:({debug:e,device:t,host:n,https:r,managed:i,noManaged:a,platform:o,port:s,verbose:c})=>C({debug:e,device:t,host:n,https:r,managed:i,noManaged:a,platform:o??`web`,verbose:c,webPort:s})}),upgrade:q({name:`upgrade`,cmds:{"ensure-template-remote":I({name:`ensure-template-remote`,args:{remote:H({long:`remote`,type:K,defaultValue:()=>`template`,description:`Remote name to ensure.`}),noFetch:R({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":I({name:`check-skill-current`,args:{target:H({long:`target`,type:K,description:`Target Coreframe template git ref.`}),apply:R({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":I({name:`fast-forward-template-files`,args:{target:H({long:`target`,type:K,description:`Target Coreframe template git ref.`}),base:H({long:`base`,type:U(K),description:`Previous Coreframe template git ref. Defaults to package.json engines.coreframe.`}),apply:R({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":I({name:`sync-coreframe-packages`,args:{target:H({long:`target`,type:K,description:`Target Coreframe template git ref.`}),apply:R({long:`apply`,description:`Update existing published Coreframe dependency specifications.`,defaultValue:()=>!1})},handler:({apply:e,target:t})=>P({apply:e,target:t})})}}),recipes:q({name:`recipes`,cmds:{apply:I({name:`apply`,args:{recipe:W({type:pe,displayName:`recipe`,description:`Recipe to prepare and apply.`}),target:H({long:`target`,type:U(K),description:`Coreframe Git ref. Defaults to package.json engines.coreframe.`}),remote:H({long:`remote`,type:K,defaultValue:()=>`template`,description:`Coreframe Git remote name.`}),noFetch:R({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:I({name:`list`,args:{target:H({long:`target`,type:U(K),description:`Coreframe Git ref. Defaults to package.json engines.coreframe.`}),remote:H({long:`remote`,type:K,defaultValue:()=>`template`,description:`Coreframe Git remote name.`}),noFetch:R({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:q({name:`db`,cmds:{ensure:I({name:`ensure`,args:{},handler:f}),generate:I({name:`generate`,args:{args:G({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit generate.`})},handler:({args:e})=>s(`generate`,e)}),migrate:I({name:`migrate`,args:{args:G({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit migrate.`})},handler:({args:e})=>s(`migrate`,e)}),"migrate-held":I({name:`migrate-held`,args:{confirm:H({long:`confirm`,type:U(V([`run-held-migrations`])),description:`Required confirmation value for held migrations.`})},handler:({confirm:e})=>ee({argv:e?[`--confirm=${e}`]:[]})}),"migrate-local":I({name:`migrate-local`,args:{},handler:l}),"migrations-check":I({name:`migrations-check`,args:{},handler:b}),reset:I({name:`reset`,args:{},handler:u}),seed:I({name:`seed`,args:{},handler:()=>c()}),"schema-check":I({name:`schema-check`,args:{seed:R({long:`seed`,description:`Run the configured project seed against the throwaway database.`,defaultValue:()=>!1})},handler:({seed:e})=>m({seed:e})}),setup:I({name:`setup`,args:{},handler:()=>d()}),studio:I({name:`studio`,args:{args:G({displayName:`drizzle-kit args`,description:`Additional arguments passed to drizzle-kit studio.`})},handler:({args:e})=>s(`studio`,e)})}})}});import.meta.main&&A(Q,{formatMessage:process.argv[2]===`db`?p:void 0});function he(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 ge(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 J.password({message:`Enter the new value for ${e}`});if(J.isCancel(t))throw J.cancel(`Secret update cancelled.`),Error(`Secret update cancelled.`);return t}async function $(e,t){if(!process.stdin.isTTY)throw Error(`Interactive secret setup requires a terminal. Provide a secret name or use --bulk with piped dotenv contents.`);if(e.length===0)throw Error(`No Worker secrets are declared in coreframe.config.ts.`);let n={};for(let r of e){let e=r.requiredIn.includes(t),i=await J.password({message:`${r.name}: ${r.description}${e?` (required)`:` (optional; leave blank to skip)`}`,validate:n=>e&&!n?.trim()?`${r.name} is required in ${t}.`:void 0});if(J.isCancel(i))throw J.cancel(`Secret setup cancelled.`),Error(`Secret setup cancelled.`);i.trim()&&(n[r.name]=i)}return n}async function _e(e){if(e.length===0)throw Error(`No Worker secrets are declared in coreframe.config.ts.`);let t=process.stdin.isTTY?await J.multiline({message:`Paste dotenv-formatted Worker secrets. Press Enter twice to submit.`,validate:e=>e?.trim()?void 0:`Paste at least one dotenv entry.`}):await ve();if(J.isCancel(t))throw J.cancel(`Secret setup cancelled.`),Error(`Secret setup cancelled.`);let{ignoredNames:n,values:r}=ce(t,e);return n.length>0&&J.log.info(`Ignored non-secret or undeclared names: ${n.join(`, `)}.`),r}async function ve(){let e=``;for await(let t of process.stdin)e+=t.toString();return e}export{Q as coreframeCli};
|
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 ee}from"./deploy/migrations.js";import{accessTokenSigningSecretName as a,prepareAccessTokenSigningSecret as o,verifyAccessTokenSigningSecret as
|
|
3
|
-
`),`Pending Worker secret updates`),j.provisioned&&A.log.info(`Provisioning Coreframe-managed access-token signing material.`),await G(
|
|
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 ee}from"./deploy/migrations.js";import{accessTokenSigningSecretName as a,prepareAccessTokenSigningSecret as o,verifyAccessTokenSigningSecret as te}from"./deploy/access-token-secret.js";import{runAfterWorkerDeploy as ne,runPostDeployChecks as re}from"./deploy/checks.js";import{deployEnvironments as s,resolveDeployConfig as c}from"./deploy/config.js";import{filterPendingMigrations as l,readAppliedMigrationNames as u}from"./deploy/pending-migrations.js";import{parseTauriReleasePlatform as d,prepareTauriRelease as f,resolveTauriReleasePlan as p,restoreTauriReleaseMetadata as m,uploadTauriReleaseToB2 as h,validateTauriReleasePlan as g,writeTauriReleaseMetadata as _}from"./deploy/tauri-release.js";import{readPackageJson as v,releaseName as ie,resolveReleaseVersion as ae,writePackageVersion as oe}from"./deploy/version.js";import{materializeI18nSnapshot as y}from"./i18n/workflows.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 v(M),d=await e(),C=c(d.deploy??{},s.environment,r);se(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 g(T);let D=await S({environment:s.environment}),k=D.values.DASHBOARD_PASSWORD??await q(r,s.environment),j=await o({environment:s.environment}),N=await V();await y({env:r,revision:N});let F=N.slice(0,12),I=ae(s.environment,l.version,F),L=ie(l.name,I),z={...le(r,s.environment,L,l.name,I),COREFRAME_I18N_RELEASE_REVISION:N},W=s.environment===`production`,J=!1,X={files:[]};A.intro(`Deploy ${L} 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 ee(Q);if($.length>0)throw Error(`Pending deploy migrations contain unsafe SQL. Move unsafe statements to held migrations.\n${i($)}`);let ue=await B(s,Q);try{W&&(await oe(M,l,I),X=await _(T,I)),A.log.step(`Running pre-deploy build`),await O(`pnpm`,[`build`],{env:Y(z),stdio:`inherit`});let e=T.enabled?await f({env:Y(z),plan:T,releaseName:L,version:I}):void 0;A.log.step(`Running release build and uploading PostHog source maps`),J=!0,await O(`pnpm`,[`build`],{env:z,stdio:`inherit`}),ue&&(A.log.step(`Running deploy-safe database migrations`),await O(`pnpm`,[`db`,`migrate`],{env:z,stdio:`inherit`})),A.log.step(`Deploying Worker and static assets to Cloudflare`);let t={...k?{DASHBOARD_PASSWORD:k}:{},...j.value?{[a]:j.value}:{},...D.values},n=Object.keys(t).length>0?await ce(t):void 0;try{await O(`pnpm`,[`exec`,`wrangler`,`deploy`,`--env`,s.environment,`--strict`,`--keep-vars`,`--tag`,L,`--message`,`Deploy ${L}`,`--var`,`DEPLOY_ENV:${s.environment}`,`--var`,`POSTHOG_RELEASE:${L}`,...n?[`--secrets-file`,n.path]:[]],{env:z,stdio:`inherit`}),await x(D),await te({environment:s.environment})}finally{await n?.dispose()}let r={environment:s.environment,releaseName:L,url:w,version:I};C.afterWorkerDeploy&&(A.log.step(`Running required after-Worker deploy hook`),await ne(C,r)),s.skipPostDeployChecks||(A.log.step(`Running post-deploy checks`),await re(C,r)),e&&(A.log.step(`Uploading Tauri release artifacts to Backblaze B2`),await h(T,e)),A.outro(`Deployed ${L} to ${s.environment}`)}catch(e){throw!J&&W&&(await E(M,u),await m(X)),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`,`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 se(e,t){let n=t.filter(t=>!e[t]);if(n.length>0)throw Error(`Missing required deploy environment variables: ${n.join(`, `)}`);return Object.fromEntries(t.map(t=>[t,e[t]]))}async function q(e,t){let n=e.DASHBOARD_PASSWORD?.trim();if(n)return n;let r;try{r=j(await w(`.env`,`utf8`))}catch(e){throw J(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 ce(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 J(e){return e instanceof Error&&`code`in e}function le(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 Y(e){let{POSTHOG_API_KEY:t,POSTHOG_PROJECT_ID:n,...r}=e;return r}export{N as deployProject,P as parseDeployArgs,e as readProjectConfig,q as resolveDashboardPassword,U as resolveDeployUrl};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { I18nConfig } from "../config.js";
|
|
2
|
+
import { RichCatalog, TranslationRequestMessage, TranslationResponse } from "./contracts.js";
|
|
3
|
+
|
|
4
|
+
//#region i18n/catalogs.d.ts
|
|
5
|
+
type ResolvedI18nConfig = Required<Pick<I18nConfig, "catalogs" | "locales" | "sourceLocale">> & Pick<I18nConfig, "generator"> & {
|
|
6
|
+
directory: string;
|
|
7
|
+
};
|
|
8
|
+
declare function catalogRelativePath(config: ResolvedI18nConfig, locale: string, catalog: string): string;
|
|
9
|
+
declare function compiledRelativePath(config: ResolvedI18nConfig, locale: string, catalog: string): string;
|
|
10
|
+
declare function runLinguiExtract(cwd: string): Promise<void>;
|
|
11
|
+
declare function runLinguiCompile({
|
|
12
|
+
config,
|
|
13
|
+
cwd,
|
|
14
|
+
strict,
|
|
15
|
+
temporaryDirectory
|
|
16
|
+
}: {
|
|
17
|
+
config: ResolvedI18nConfig;
|
|
18
|
+
cwd: string;
|
|
19
|
+
strict: boolean;
|
|
20
|
+
temporaryDirectory: string;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
declare function collectMissingTranslations({
|
|
23
|
+
config,
|
|
24
|
+
cwd
|
|
25
|
+
}: {
|
|
26
|
+
config: ResolvedI18nConfig;
|
|
27
|
+
cwd: string;
|
|
28
|
+
}): Promise<TranslationRequestMessage[]>;
|
|
29
|
+
declare function generateMissingTranslations({
|
|
30
|
+
config,
|
|
31
|
+
cwd,
|
|
32
|
+
messages,
|
|
33
|
+
env
|
|
34
|
+
}: {
|
|
35
|
+
config: ResolvedI18nConfig;
|
|
36
|
+
cwd: string;
|
|
37
|
+
messages: TranslationRequestMessage[];
|
|
38
|
+
env?: NodeJS.ProcessEnv;
|
|
39
|
+
}): Promise<TranslationResponse>;
|
|
40
|
+
declare function applyGeneratedTranslations({
|
|
41
|
+
config,
|
|
42
|
+
cwd,
|
|
43
|
+
missing,
|
|
44
|
+
response
|
|
45
|
+
}: {
|
|
46
|
+
config: ResolvedI18nConfig;
|
|
47
|
+
cwd: string;
|
|
48
|
+
missing: TranslationRequestMessage[];
|
|
49
|
+
response: TranslationResponse;
|
|
50
|
+
}): Promise<void>;
|
|
51
|
+
declare function assertCompleteCatalogs({
|
|
52
|
+
config,
|
|
53
|
+
cwd
|
|
54
|
+
}: {
|
|
55
|
+
config: ResolvedI18nConfig;
|
|
56
|
+
cwd: string;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
declare function readRichCatalog(path: string): Promise<RichCatalog>;
|
|
59
|
+
declare function writeRichCatalog(path: string, catalog: RichCatalog): Promise<void>;
|
|
60
|
+
//#endregion
|
|
61
|
+
export { ResolvedI18nConfig, applyGeneratedTranslations, assertCompleteCatalogs, catalogRelativePath, collectMissingTranslations, compiledRelativePath, generateMissingTranslations, readRichCatalog, runLinguiCompile, runLinguiExtract, writeRichCatalog };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{importProjectDependency as e}from"../project.js";import{resolveProjectArtifact as t}from"./paths.js";import{copyFile as n,mkdir as r,readFile as i,rename as a,writeFile as o}from"node:fs/promises";import{dirname as s,join as c}from"node:path";import l from"nano-spawn";function u(e,t,n){return`${e.directory}/${t}/${n}.json`}function d(e,t,n){return`${e.directory}/${t}/${n}.mjs`}async function f(e){await l(`pnpm`,[`exec`,`lingui`,`extract`],{cwd:e,stdio:`inherit`})}async function p({config:e,cwd:i,strict:a,temporaryDirectory:o}){let d=[];try{for(let a of e.locales)for(let l of e.catalogs){let f=t(i,u(e,a,l)),p=await v(f),m=Object.fromEntries(Object.entries(p).filter(([,e])=>!e.obsolete)),h=c(o,`${a}-${l}.json`);await r(s(h),{recursive:!0}),await n(f,h),d.push({path:f,backup:h}),await y(f,m)}await l(`pnpm`,[`exec`,`lingui`,`compile`,...a?[`--strict`]:[]],{cwd:i,stdio:`inherit`})}finally{for(let{path:e,backup:t}of d)await n(t,e)}}async function m({config:e,cwd:n}){let r=[];for(let i of e.locales)if(i!==e.sourceLocale)for(let a of e.catalogs){let o=await v(t(n,u(e,i,a)));for(let[e,t]of Object.entries(o)){if(t.obsolete||t.translation?.trim())continue;let n=t.message;if(typeof n!=`string`||!n)throw Error(`Missing source message metadata for ${i}:${e}.`);r.push({catalog:a,comments:t.comments?.filter(e=>typeof e==`string`)??[],context:t.context,id:e,locale:i,message:n,origins:t.origin?.map(([e,t])=>t?`${e}:${t}`:e)??[],placeholders:Object.keys(t.placeholders??{}).sort()})}}return r.sort((e,t)=>w(e).localeCompare(w(t)))}async function h({config:e,cwd:t,messages:n,env:r=process.env}){if(n.length===0)return{translations:[],version:1};if(!e.generator)throw Error(`I18n preparation found ${n.length} missing translations, but no generator is configured.`);let i={messages:n,sourceLocale:e.sourceLocale,version:1};return b(await x({args:e.generator.args??[],command:e.generator.command,cwd:t,env:r,input:JSON.stringify(i)}))}async function g({config:n,cwd:r,missing:i,response:a}){let o=new Map(i.map(e=>[w(e),e])),s=new Map;for(let e of a.translations){let t=w(e);if(!o.has(t))throw Error(`Translation generator returned unexpected entry ${e.locale}:${e.catalog}:${e.id}.`);if(s.has(t))throw Error(`Translation generator returned duplicate entry ${e.locale}:${e.catalog}:${e.id}.`);if(!e.translation.trim())throw Error(`Translation generator returned an empty entry ${e.locale}:${e.id}.`);s.set(t,e.translation)}let c=[...o.keys()].filter(e=>!s.has(e));if(c.length>0)throw Error(`Translation generator omitted ${c.length} required entries.`);let l=await e(`@lingui/message-utils/compileMessage`,r);for(let e of i){let t=s.get(w(e));S({compileMessage:l,id:e.id,locale:e.locale,source:e.message,translation:t})}for(let e of n.locales)if(e!==n.sourceLocale)for(let i of n.catalogs){let a=t(r,u(n,e,i)),o=await v(a),c=!1;for(let[t,n]of Object.entries(o)){let r=s.get(w({catalog:i,id:t,locale:e}));if(r!==void 0){if(n.translation?.trim())throw Error(`Refusing to overwrite preserved translation ${e}:${t}.`);n.translation=r,c=!0}}c&&await y(a,o)}}async function _({config:n,cwd:r}){let i=await m({config:n,cwd:r});if(i.length>0)throw Error(`I18n catalogs remain incomplete: ${i.length} translations are missing.`);let a=await e(`@lingui/message-utils/compileMessage`,r);for(let e of n.locales)if(e!==n.sourceLocale)for(let i of n.catalogs){let o=await v(t(r,u(n,e,i)));for(let[t,n]of Object.entries(o))if(!n.obsolete){if(typeof n.message!=`string`||typeof n.translation!=`string`)throw Error(`Incomplete source metadata for active translation ${e}:${t}.`);S({compileMessage:a,id:t,locale:e,source:n.message,translation:n.translation})}}}async function v(e){let t=JSON.parse(await i(e,`utf8`));if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Lingui rich JSON catalog must be an object: ${e}.`);for(let[n,r]of Object.entries(t)){if(!r||typeof r!=`object`||Array.isArray(r))throw Error(`Invalid Lingui rich JSON message ${n} in ${e}.`);let t=r;if(t.translation!==void 0&&typeof t.translation!=`string`)throw Error(`Invalid translation value for ${n} in ${e}.`);if(t.message!==void 0&&t.message!==null&&typeof t.message!=`string`)throw Error(`Invalid source message value for ${n} in ${e}.`);if(t.context!==void 0&&typeof t.context!=`string`)throw Error(`Invalid context value for ${n} in ${e}.`);if(t.obsolete!==void 0&&typeof t.obsolete!=`boolean`)throw Error(`Invalid obsolete marker for ${n} in ${e}.`);if(t.comments!==void 0&&(!Array.isArray(t.comments)||t.comments.some(e=>typeof e!=`string`)))throw Error(`Invalid comments for ${n} in ${e}.`);if(t.origin!==void 0&&(!Array.isArray(t.origin)||t.origin.some(e=>!Array.isArray(e)||typeof e[0]!=`string`||e[1]!==void 0&&typeof e[1]!=`number`)))throw Error(`Invalid origins for ${n} in ${e}.`);if(t.placeholders!==void 0&&(!t.placeholders||typeof t.placeholders!=`object`||Array.isArray(t.placeholders)))throw Error(`Invalid placeholders for ${n} in ${e}.`)}return t}async function y(e,t){await r(s(e),{recursive:!0});let n=`${e}.${process.pid}.tmp`;await o(n,`${JSON.stringify(t,null,2)}\n`),await a(n,e)}function b(e){let t;try{t=JSON.parse(e)}catch{throw Error(`Translation generator returned invalid JSON.`)}if(!t||typeof t!=`object`||t.version!==1||!Array.isArray(t.translations))throw Error(`Translation generator returned an invalid response envelope.`);for(let e of t.translations)if(!e||typeof e!=`object`||typeof e.id!=`string`||typeof e.catalog!=`string`||typeof e.locale!=`string`||typeof e.translation!=`string`)throw Error(`Translation generator returned an invalid translation entry.`);return t}function x({args:e,command:t,cwd:n,env:r,input:i}){return l(t,e,{cwd:n,env:r,stdio:[{string:i},`pipe`,`ignore`]}).then(({stdout:e})=>{if(Buffer.byteLength(e)>10*1024*1024)throw Error(`Translation generator output exceeded 10 MiB.`);return e}).catch(()=>{throw Error(`Translation generator exited unsuccessfully.`)})}function S({compileMessage:e,id:t,locale:n,source:r,translation:i}){let a,o;try{a=e.compileMessageOrThrow(r),o=e.compileMessageOrThrow(i)}catch{throw Error(`Translation ${n}:${t} is not valid ICU MessageFormat.`)}let s=C(a),c=C(o);if(s.length!==c.length||s.some((e,t)=>e!==c[t]))throw Error(`Translation ${n}:${t} does not preserve source ICU placeholders.`)}function C(e){let t=[];function n(e){for(let r of e){if(typeof r==`string`)continue;let[e,i=`argument`,a]=r;if(t.push(`${e}:${i}`),a&&typeof a==`object`&&!Array.isArray(a))for(let e of Object.values(a))Array.isArray(e)&&n(e)}}return n(e),t.sort()}function w(e){return`${e.locale}\0${e.catalog}\0${e.id}`}export{g as applyGeneratedTranslations,_ as assertCompleteCatalogs,u as catalogRelativePath,m as collectMissingTranslations,d as compiledRelativePath,h as generateMissingTranslations,v as readRichCatalog,p as runLinguiCompile,f as runLinguiExtract,y as writeRichCatalog};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//#region i18n/contracts.d.ts
|
|
2
|
+
type RichCatalogMessage = {
|
|
3
|
+
comments?: string[];
|
|
4
|
+
context?: string;
|
|
5
|
+
extra?: unknown;
|
|
6
|
+
message?: string | null;
|
|
7
|
+
obsolete?: boolean;
|
|
8
|
+
origin?: [string, number?][];
|
|
9
|
+
placeholders?: Record<string, unknown>;
|
|
10
|
+
translation?: string;
|
|
11
|
+
};
|
|
12
|
+
type RichCatalog = Record<string, RichCatalogMessage>;
|
|
13
|
+
type TranslationRequestMessage = {
|
|
14
|
+
catalog: string;
|
|
15
|
+
comments: string[];
|
|
16
|
+
context?: string;
|
|
17
|
+
id: string;
|
|
18
|
+
locale: string;
|
|
19
|
+
message: string;
|
|
20
|
+
origins: string[];
|
|
21
|
+
placeholders: string[];
|
|
22
|
+
};
|
|
23
|
+
type TranslationRequest = {
|
|
24
|
+
messages: TranslationRequestMessage[];
|
|
25
|
+
sourceLocale: string;
|
|
26
|
+
version: 1;
|
|
27
|
+
};
|
|
28
|
+
type TranslationResponse = {
|
|
29
|
+
translations: {
|
|
30
|
+
catalog: string;
|
|
31
|
+
id: string;
|
|
32
|
+
locale: string;
|
|
33
|
+
translation: string;
|
|
34
|
+
}[];
|
|
35
|
+
version: 1;
|
|
36
|
+
};
|
|
37
|
+
type I18nSnapshotFile = {
|
|
38
|
+
digest: string;
|
|
39
|
+
kind: "catalog" | "compiled";
|
|
40
|
+
path: string;
|
|
41
|
+
size: number;
|
|
42
|
+
};
|
|
43
|
+
type I18nSnapshotManifest = {
|
|
44
|
+
baseline?: string;
|
|
45
|
+
catalogs: string[];
|
|
46
|
+
files: I18nSnapshotFile[];
|
|
47
|
+
generator: string;
|
|
48
|
+
linguiVersion: string;
|
|
49
|
+
locales: string[];
|
|
50
|
+
projectId: string;
|
|
51
|
+
revision: string;
|
|
52
|
+
sourceLocale: string;
|
|
53
|
+
version: 1;
|
|
54
|
+
};
|
|
55
|
+
type I18nSnapshotReference = {
|
|
56
|
+
revision: string;
|
|
57
|
+
snapshot: string;
|
|
58
|
+
version: 1;
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { I18nSnapshotFile, I18nSnapshotManifest, I18nSnapshotReference, RichCatalog, RichCatalogMessage, TranslationRequest, TranslationRequestMessage, TranslationResponse };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region i18n/paths.d.ts
|
|
2
|
+
declare function assertSha256(value: string): string;
|
|
3
|
+
declare function assertRevision(value: string): string;
|
|
4
|
+
declare function assertI18nSegment(value: string, label: string): string;
|
|
5
|
+
declare function i18nNamespace(projectId: string): string;
|
|
6
|
+
declare function i18nObjectKey(projectId: string, digest: string): string;
|
|
7
|
+
declare function i18nCommitReferenceKey(projectId: string, revision: string): string;
|
|
8
|
+
declare function i18nReleaseReferenceKey(projectId: string, tag: string): string;
|
|
9
|
+
declare function assertReleaseTag(tag: string): string;
|
|
10
|
+
declare function resolveProjectArtifact(cwd: string, path: string): string;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { assertI18nSegment, assertReleaseTag, assertRevision, assertSha256, i18nCommitReferenceKey, i18nNamespace, i18nObjectKey, i18nReleaseReferenceKey, resolveProjectArtifact };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{resolve as e,sep as t}from"node:path";const n=/^[a-f0-9]{64}$/,r=/^[a-f0-9]{40}$/,i=/^[A-Za-z0-9][A-Za-z0-9._-]*$/;function a(e){if(!n.test(e))throw Error(`Invalid SHA-256 digest "${e}".`);return e}function o(e){if(!r.test(e))throw Error(`Invalid full Git revision "${e}".`);return e}function s(e,t){if(!i.test(e))throw Error(`${t} must contain only letters, numbers, dots, underscores, or hyphens.`);return e}function c(e){return`projects/${s(e,`Project ID`)}/i18n/v1`}function l(e,t){return a(t),`${c(e)}/objects/sha256/${t.slice(0,2)}/${t}`}function u(e,t){return o(t),`${c(e)}/commits/${t}.json`}function d(e,t){return f(t),`${c(e)}/releases/${encodeURIComponent(t)}.json`}function f(e){if(!e.startsWith(`v`)||e.length<2||e.length>128||/\s/.test(e)||e.includes(`..`))throw Error(`Release tag "${e}" must be a valid v-prefixed tag.`);return e}function p(n,r){if(!r||r.startsWith(`/`)||r.includes(`\0`))throw Error(`Unsafe i18n artifact path "${r}".`);let i=e(n),a=e(i,r);if(a!==i&&!a.startsWith(`${i}${t}`))throw Error(`Unsafe i18n artifact path "${r}".`);return a}export{s as assertI18nSegment,f as assertReleaseTag,o as assertRevision,a as assertSha256,u as i18nCommitReferenceKey,c as i18nNamespace,l as i18nObjectKey,d as i18nReleaseReferenceKey,p as resolveProjectArtifact};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { PrivateObjectStore } from "../private-object-store.js";
|
|
2
|
+
import { I18nSnapshotManifest, I18nSnapshotReference } from "./contracts.js";
|
|
3
|
+
|
|
4
|
+
//#region i18n/storage.d.ts
|
|
5
|
+
declare function sha256(body: Uint8Array | string): string;
|
|
6
|
+
declare function sha256File(path: string): Promise<string>;
|
|
7
|
+
declare function serializeJson(value: unknown): Uint8Array;
|
|
8
|
+
declare function ensureImmutableObject({
|
|
9
|
+
bucket,
|
|
10
|
+
digest,
|
|
11
|
+
projectId,
|
|
12
|
+
source,
|
|
13
|
+
store
|
|
14
|
+
}: {
|
|
15
|
+
bucket: string;
|
|
16
|
+
digest: string;
|
|
17
|
+
projectId: string;
|
|
18
|
+
source: string;
|
|
19
|
+
store: PrivateObjectStore;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
declare function ensureImmutableReference({
|
|
22
|
+
bucket,
|
|
23
|
+
key,
|
|
24
|
+
reference,
|
|
25
|
+
store,
|
|
26
|
+
temporaryDirectory
|
|
27
|
+
}: {
|
|
28
|
+
bucket: string;
|
|
29
|
+
key: string;
|
|
30
|
+
reference: I18nSnapshotReference;
|
|
31
|
+
store: PrivateObjectStore;
|
|
32
|
+
temporaryDirectory: string;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
declare function publishSnapshotManifest({
|
|
35
|
+
bucket,
|
|
36
|
+
manifest,
|
|
37
|
+
projectId,
|
|
38
|
+
store,
|
|
39
|
+
temporaryDirectory
|
|
40
|
+
}: {
|
|
41
|
+
bucket: string;
|
|
42
|
+
manifest: I18nSnapshotManifest;
|
|
43
|
+
projectId: string;
|
|
44
|
+
store: PrivateObjectStore;
|
|
45
|
+
temporaryDirectory: string;
|
|
46
|
+
}): Promise<string>;
|
|
47
|
+
declare function readSnapshotReference({
|
|
48
|
+
bucket,
|
|
49
|
+
key,
|
|
50
|
+
store,
|
|
51
|
+
temporaryDirectory
|
|
52
|
+
}: {
|
|
53
|
+
bucket: string;
|
|
54
|
+
key: string;
|
|
55
|
+
store: PrivateObjectStore;
|
|
56
|
+
temporaryDirectory: string;
|
|
57
|
+
}): Promise<I18nSnapshotReference | undefined>;
|
|
58
|
+
declare function readCommitReference({
|
|
59
|
+
bucket,
|
|
60
|
+
projectId,
|
|
61
|
+
revision,
|
|
62
|
+
store,
|
|
63
|
+
temporaryDirectory
|
|
64
|
+
}: {
|
|
65
|
+
bucket: string;
|
|
66
|
+
projectId: string;
|
|
67
|
+
revision: string;
|
|
68
|
+
store: PrivateObjectStore;
|
|
69
|
+
temporaryDirectory: string;
|
|
70
|
+
}): Promise<I18nSnapshotReference | undefined>;
|
|
71
|
+
declare function writeSnapshotReferences({
|
|
72
|
+
bucket,
|
|
73
|
+
projectId,
|
|
74
|
+
reference,
|
|
75
|
+
store,
|
|
76
|
+
tag,
|
|
77
|
+
temporaryDirectory
|
|
78
|
+
}: {
|
|
79
|
+
bucket: string;
|
|
80
|
+
projectId: string;
|
|
81
|
+
reference: I18nSnapshotReference;
|
|
82
|
+
store: PrivateObjectStore;
|
|
83
|
+
tag?: string;
|
|
84
|
+
temporaryDirectory: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
declare function materializeObject({
|
|
87
|
+
bucket,
|
|
88
|
+
cacheRoot,
|
|
89
|
+
digest,
|
|
90
|
+
projectId,
|
|
91
|
+
store,
|
|
92
|
+
target
|
|
93
|
+
}: {
|
|
94
|
+
bucket: string;
|
|
95
|
+
cacheRoot: string;
|
|
96
|
+
digest: string;
|
|
97
|
+
projectId: string;
|
|
98
|
+
store: PrivateObjectStore;
|
|
99
|
+
target: string;
|
|
100
|
+
}): Promise<void>;
|
|
101
|
+
declare function readSnapshotManifest({
|
|
102
|
+
bucket,
|
|
103
|
+
cacheRoot,
|
|
104
|
+
digest,
|
|
105
|
+
projectId,
|
|
106
|
+
store,
|
|
107
|
+
temporaryDirectory
|
|
108
|
+
}: {
|
|
109
|
+
bucket: string;
|
|
110
|
+
cacheRoot: string;
|
|
111
|
+
digest: string;
|
|
112
|
+
projectId: string;
|
|
113
|
+
store: PrivateObjectStore;
|
|
114
|
+
temporaryDirectory: string;
|
|
115
|
+
}): Promise<I18nSnapshotManifest>;
|
|
116
|
+
//#endregion
|
|
117
|
+
export { ensureImmutableObject, ensureImmutableReference, materializeObject, publishSnapshotManifest, readCommitReference, readSnapshotManifest, readSnapshotReference, serializeJson, sha256, sha256File, writeSnapshotReferences };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{assertRevision as e,assertSha256 as t,i18nCommitReferenceKey as n,i18nObjectKey as r,i18nReleaseReferenceKey as i}from"./paths.js";import{createHash as a,randomUUID as o}from"node:crypto";import{copyFile as s,mkdir as c,readFile as l,rename as u,rm as d,stat as f,writeFile as p}from"node:fs/promises";import{dirname as m,join as h,resolve as g}from"node:path";function _(e){return a(`sha256`).update(e).digest(`hex`)}async function v(e){return _(await l(e))}function y(e){return Buffer.from(`${JSON.stringify(e,null,2)}\n`)}async function b({bucket:e,digest:n,projectId:i,source:a,store:s}){if(t(n),await v(a)!==n)throw Error(`I18n object source does not match declared SHA-256 digest ${n}.`);let c=r(i,n);await s.has(e,c)||await s.upload(e,c,a,`application/octet-stream`);let l=`${a}.${process.pid}.${o()}.verify`;try{if(await s.download(e,c,l),await v(l)!==n)throw Error(`Existing immutable i18n object ${c} failed integrity verification.`)}finally{await d(l,{force:!0})}}async function x({bucket:e,key:t,reference:n,store:r,temporaryDirectory:i}){let a=y(n),s=h(i,`reference-${o()}.json`);if(await r.has(e,t))try{if(await r.download(e,t,s),!(await l(s)).equals(a))throw Error(`Immutable i18n reference ${t} already resolves to different snapshot data.`);return}finally{await d(s,{force:!0})}await p(s,a,{flag:`wx`});try{await r.upload(e,t,s,`application/json`)}finally{await d(s,{force:!0})}let c=h(i,`reference-${o()}.verify.json`);try{if(await r.download(e,t,c),!(await l(c)).equals(a))throw Error(`Uploaded immutable i18n reference ${t} failed verification.`)}finally{await d(c,{force:!0})}}async function S({bucket:e,manifest:t,projectId:n,store:r,temporaryDirectory:i}){let a=y(t),o=_(a),s=h(i,`manifest-${o}.json`);await p(s,a,{flag:`wx`});try{await b({bucket:e,digest:o,projectId:n,source:s,store:r})}finally{await d(s,{force:!0})}return o}async function C({bucket:n,key:r,store:i,temporaryDirectory:a}){if(!await i.has(n,r))return;let s=h(a,`reference-${o()}.json`);try{await i.download(n,r,s);let a=JSON.parse(await l(s,`utf8`));if(a.version!==1||typeof a.revision!=`string`||typeof a.snapshot!=`string`)throw Error(`Invalid i18n reference at ${r}.`);return e(a.revision),t(a.snapshot),a}finally{await d(s,{force:!0})}}async function w({bucket:e,projectId:t,revision:r,store:i,temporaryDirectory:a}){let o=await C({bucket:e,key:n(t,r),store:i,temporaryDirectory:a});if(o&&o.revision!==r)throw Error(`I18n commit reference does not match requested revision ${r}.`);return o}async function T({bucket:e,projectId:t,reference:r,store:a,tag:o,temporaryDirectory:s}){await x({bucket:e,key:n(t,r.revision),reference:r,store:a,temporaryDirectory:s}),o&&await x({bucket:e,key:i(t,o),reference:r,store:a,temporaryDirectory:s})}async function E({bucket:e,cacheRoot:n,digest:i,projectId:a,store:l,target:f}){t(i);let p=h(g(n),`v1`,`sha256`,i.slice(0,2),i);if(await O(p,i)===!1){await c(m(p),{recursive:!0});let t=`${p}.${process.pid}.${o()}.tmp`;try{if(await l.download(e,r(a,i),t),await v(t)!==i)throw Error(`Downloaded i18n object failed SHA-256 verification: ${i}.`);await d(p,{force:!0}),await u(t,p)}finally{await d(t,{force:!0})}}await c(m(f),{recursive:!0});let _=`${f}.${process.pid}.${o()}.tmp`;try{if(await s(p,_),await v(_)!==i)throw Error(`Cached i18n object failed SHA-256 verification: ${i}.`);await d(f,{force:!0}),await u(_,f)}finally{await d(_,{force:!0})}}async function D({bucket:n,cacheRoot:r,digest:i,projectId:a,store:o,temporaryDirectory:s}){let c=h(s,`manifest-${i}.json`);await E({bucket:n,cacheRoot:r,digest:i,projectId:a,store:o,target:c});let u=JSON.parse(await l(c,`utf8`));if(u.version!==1||u.projectId!==a||typeof u.revision!=`string`||!Array.isArray(u.files)||!Array.isArray(u.catalogs)||u.catalogs.some(e=>typeof e!=`string`)||!Array.isArray(u.locales)||u.locales.some(e=>typeof e!=`string`)||typeof u.sourceLocale!=`string`||typeof u.generator!=`string`||typeof u.linguiVersion!=`string`)throw Error(`Invalid i18n snapshot manifest ${i}.`);e(u.revision),u.baseline!==void 0&&t(u.baseline);for(let e of u.files){if(!e||typeof e!=`object`||typeof e.path!=`string`||typeof e.digest!=`string`||typeof e.size!=`number`||e.kind!==`catalog`&&e.kind!==`compiled`)throw Error(`Invalid file entry in i18n snapshot manifest ${i}.`);t(e.digest)}return u}async function O(e,t){try{return(await f(e)).isFile()&&await v(e)===t}catch{return!1}}export{b as ensureImmutableObject,x as ensureImmutableReference,E as materializeObject,S as publishSnapshotManifest,w as readCommitReference,D as readSnapshotManifest,C as readSnapshotReference,y as serializeJson,_ as sha256,v as sha256File,T as writeSnapshotReferences};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { I18nConfig } from "../config.js";
|
|
2
|
+
import { PrivateObjectStore } from "../private-object-store.js";
|
|
3
|
+
import { ResolvedI18nConfig } from "./catalogs.js";
|
|
4
|
+
|
|
5
|
+
//#region i18n/workflows.d.ts
|
|
6
|
+
type WorkflowOptions = {
|
|
7
|
+
cwd?: string;
|
|
8
|
+
env?: NodeJS.ProcessEnv;
|
|
9
|
+
store?: PrivateObjectStore;
|
|
10
|
+
};
|
|
11
|
+
declare function buildI18n({
|
|
12
|
+
cwd,
|
|
13
|
+
env
|
|
14
|
+
}?: Pick<WorkflowOptions, "cwd" | "env">): Promise<void>;
|
|
15
|
+
declare function prepareI18nRelease({
|
|
16
|
+
cwd,
|
|
17
|
+
env,
|
|
18
|
+
store,
|
|
19
|
+
tag
|
|
20
|
+
}: WorkflowOptions & {
|
|
21
|
+
tag: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
reused: boolean;
|
|
24
|
+
snapshot: string;
|
|
25
|
+
}>;
|
|
26
|
+
declare function materializeI18nSnapshot({
|
|
27
|
+
cwd,
|
|
28
|
+
env,
|
|
29
|
+
revision,
|
|
30
|
+
store
|
|
31
|
+
}?: WorkflowOptions & {
|
|
32
|
+
revision?: string;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
revision: string;
|
|
35
|
+
snapshot: string;
|
|
36
|
+
} | undefined>;
|
|
37
|
+
declare function resolveI18nConfig(config: I18nConfig): ResolvedI18nConfig;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { buildI18n, materializeI18nSnapshot, prepareI18nRelease, resolveI18nConfig };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readProjectConfig as e}from"../config.js";import{createB2PrivateObjectStore as t}from"../private-object-store.js";import{importProjectDependency as n}from"../project.js";import{assertI18nSegment as r,assertReleaseTag as i,assertRevision as a,resolveProjectArtifact as o}from"./paths.js";import{applyGeneratedTranslations as s,assertCompleteCatalogs as c,catalogRelativePath as l,collectMissingTranslations as u,compiledRelativePath as d,generateMissingTranslations as f,runLinguiCompile as p,runLinguiExtract as m,writeRichCatalog as h}from"./catalogs.js";import{ensureImmutableObject as g,materializeObject as _,publishSnapshotManifest as v,readCommitReference as y,readSnapshotManifest as b,sha256File as x,writeSnapshotReferences as S}from"./storage.js";import{access as C,mkdir as w,mkdtemp as T,rename as E,rm as D,stat as O}from"node:fs/promises";import{dirname as k,join as A,resolve as j}from"node:path";import M from"nano-spawn";import{tmpdir as N}from"node:os";async function P({cwd:t=process.cwd(),env:n=process.env}={}){let r=await e({cwd:t});if(!r.i18n)return;if(n.COREFRAME_I18N_RELEASE_REVISION){await I({cwd:t,env:n,revision:n.COREFRAME_I18N_RELEASE_REVISION});return}let i=L(r.i18n),a=await T(A(N(),`coreframe-i18n-build-`));try{await m(t),await p({config:i,cwd:t,strict:!1,temporaryDirectory:a})}finally{await D(a,{force:!0,recursive:!0})}}async function F({cwd:e=process.cwd(),env:t=process.env,store:n,tag:r}){i(r);let a=await R({cwd:e,env:t,store:n});await K(e);let o=await G(e);await q(e,r,o);let l=await T(A(N(),`coreframe-i18n-prepare-`));try{let n=await y({bucket:a.assets.sourceBucket,projectId:a.assets.projectId,revision:o,store:a.store,temporaryDirectory:l});if(n){let e=await b({bucket:a.assets.sourceBucket,cacheRoot:a.cacheRoot,digest:n.snapshot,projectId:a.assets.projectId,store:a.store,temporaryDirectory:l});return W(a,e,o),await B(a,e),await S({bucket:a.assets.sourceBucket,projectId:a.assets.projectId,reference:n,store:a.store,tag:r,temporaryDirectory:l}),{reused:!0,snapshot:n.snapshot}}return await U(a,l,async()=>{let n=await z(a,o,l);n||await H(a),await m(e);let i=await u({config:a.config,cwd:e}),d=await f({config:a.config,cwd:e,env:t,messages:i});await s({config:a.config,cwd:e,missing:i,response:d}),await c({config:a.config,cwd:e}),await p({config:a.config,cwd:e,strict:!0,temporaryDirectory:l});let h=await V(a),g={...n?{baseline:n}:{},catalogs:a.config.catalogs,files:h,generator:i.length>0?a.config.generator?.id??`unconfigured`:`none`,linguiVersion:await J(e),locales:a.config.locales,projectId:a.assets.projectId,revision:o,sourceLocale:a.config.sourceLocale,version:1},_=await v({bucket:a.assets.sourceBucket,manifest:g,projectId:a.assets.projectId,store:a.store,temporaryDirectory:l});return await S({bucket:a.assets.sourceBucket,projectId:a.assets.projectId,reference:{revision:o,snapshot:_,version:1},store:a.store,tag:r,temporaryDirectory:l}),{reused:!1,snapshot:_}})}finally{await D(l,{force:!0,recursive:!0})}}async function I({cwd:t=process.cwd(),env:n=process.env,revision:r,store:i}={}){let o=await e({cwd:t});if(!o.i18n)return;let s=await R({cwd:t,env:n,store:i,projectConfig:o}),c=a(r??await G(t)),l=await T(A(N(),`coreframe-i18n-materialize-`));try{let e=await y({bucket:s.assets.sourceBucket,projectId:s.assets.projectId,revision:c,store:s.store,temporaryDirectory:l});if(!e)throw Error(`No prepared i18n snapshot exists for commit ${c}. Push a v* tag for this commit and wait for the translation gate.`);let t=await b({bucket:s.assets.sourceBucket,cacheRoot:s.cacheRoot,digest:e.snapshot,projectId:s.assets.projectId,store:s.store,temporaryDirectory:l});return W(s,t,c),await B(s,t),{revision:c,snapshot:e.snapshot}}finally{await D(l,{force:!0,recursive:!0})}}function L(e){let t=r(e.sourceLocale,`Source locale`),n=[...new Set(e.locales.map(e=>r(e,`Locale`)))],i=[...new Set(e.catalogs.map(e=>r(e,`Catalog`)))];if(n.length===0||!n.includes(t))throw Error(`I18n locales must include the source locale.`);if(i.length===0)throw Error(`I18n configuration requires at least one catalog.`);if(e.generator&&(!e.generator.command.trim()||!e.generator.id.trim()))throw Error(`I18n generator command and ID must be non-empty.`);return{catalogs:i,directory:e.directory??`locales`,generator:e.generator,locales:n,sourceLocale:t}}async function R({cwd:n=process.cwd(),env:r=process.env,projectConfig:i,store:a}){let o=i??await e({cwd:n});if(!o.i18n)throw Error(`Missing i18n configuration in coreframe.config.ts.`);if(!o.assets)throw Error(`I18n snapshots require the existing private build bucket configured under assets.`);let s=o.assets;return{assets:s,cacheRoot:j(n,`.coreframe`,`i18n`,`cache`),config:L(o.i18n),cwd:j(n),env:r,store:a??t({config:s,env:r})}}async function z(e,t,n){let{stdout:r}=await M(`git`,[`rev-list`,`--first-parent`,`--skip=1`,t],{cwd:e.cwd,stdin:`ignore`});for(let t of r.split(/\r?\n/).filter(Boolean)){let r=await y({bucket:e.assets.sourceBucket,projectId:e.assets.projectId,revision:a(t),store:e.store,temporaryDirectory:n});if(!r)continue;let i=await b({bucket:e.assets.sourceBucket,cacheRoot:e.cacheRoot,digest:r.snapshot,projectId:e.assets.projectId,store:e.store,temporaryDirectory:n});return W(e,i,t),await B(e,i,`catalog`),r.snapshot}}async function B(e,t,n){for(let r of t.files)if(!(n&&r.kind!==n)&&(await _({bucket:e.assets.sourceBucket,cacheRoot:e.cacheRoot,digest:r.digest,projectId:e.assets.projectId,store:e.store,target:o(e.cwd,r.path)}),(await O(o(e.cwd,r.path))).size!==r.size))throw Error(`Materialized i18n artifact size does not match manifest: ${r.path}.`)}async function V(e){let t=[];for(let n of e.config.locales)for(let r of e.config.catalogs)for(let i of[`catalog`,`compiled`]){let a=i===`catalog`?l(e.config,n,r):d(e.config,n,r),s=o(e.cwd,a),c=await x(s);await g({bucket:e.assets.sourceBucket,digest:c,projectId:e.assets.projectId,source:s,store:e.store}),t.push({digest:c,kind:i,path:a,size:(await O(s)).size})}return t.sort((e,t)=>e.path.localeCompare(t.path))}async function H(e){let{stdout:t}=await M(`git`,[`rev-parse`,`--show-prefix`],{cwd:e.cwd,stdin:`ignore`}),r=t.trim(),{stdout:i}=await M(`git`,[`log`,`-1`,`--format=%H`,`--diff-filter=AM`,`--`,`:(top)${`${r}${e.config.directory}/${e.config.sourceLocale}/${e.config.catalogs[0]}.po`}`],{cwd:e.cwd,stdin:`ignore`}),a=i.trim();if(!a)return!1;let s=(await n(`@lingui/format-po`,e.cwd)).formatter({lineNumbers:!1}),c=!1;for(let t of e.config.locales)for(let n of e.config.catalogs){let i=`${r}${`${e.config.directory}/${t}/${n}.po`}`;try{let{stdout:r}=await M(`git`,[`show`,`${a}:${i}`],{cwd:e.cwd,stdin:`ignore`});await h(o(e.cwd,l(e.config,t,n)),s.parse(r)),c=!0}catch{}}return c}async function U(e,t,n){let r=[];for(let n of e.config.locales)for(let i of e.config.catalogs)for(let a of[l(e.config,n,i),d(e.config,n,i)]){let n=o(e.cwd,a);try{await C(n)}catch{continue}let i=A(t,`previous-catalogs`,a);await w(k(i),{recursive:!0}),await E(n,i),r.push({backup:i,path:n})}try{let e=await n();return await D(A(t,`previous-catalogs`),{force:!0,recursive:!0}),e}catch(t){for(let t of e.config.locales)for(let n of e.config.catalogs)await D(o(e.cwd,l(e.config,t,n)),{force:!0}),await D(o(e.cwd,d(e.config,t,n)),{force:!0});for(let{backup:e,path:t}of r)await w(k(t),{recursive:!0}),await E(e,t);throw t}}function W(e,t,n){if(t.revision!==n||t.projectId!==e.assets.projectId||t.sourceLocale!==e.config.sourceLocale||JSON.stringify(t.locales)!==JSON.stringify(e.config.locales)||JSON.stringify(t.catalogs)!==JSON.stringify(e.config.catalogs))throw Error(`I18n snapshot does not match commit or project configuration.`);let r=e.config.locales.flatMap(t=>e.config.catalogs.flatMap(n=>[`catalog:${l(e.config,t,n)}`,`compiled:${d(e.config,t,n)}`])),i=t.files.map(e=>`${e.kind}:${e.path}`);if(new Set(i).size!==i.length||JSON.stringify(i.sort())!==JSON.stringify(r.sort()))throw Error(`I18n snapshot has an incomplete or unexpected artifact set.`)}async function G(e){let{stdout:t}=await M(`git`,[`rev-parse`,`HEAD`],{cwd:e,stdin:`ignore`});return a(t.trim())}async function K(e){let{stdout:t}=await M(`git`,[`status`,`--porcelain`],{cwd:e,stdin:`ignore`});if(t.trim())throw Error(`Tagged i18n preparation requires a clean Git tree.`)}async function q(e,t,n){let{stdout:r}=await M(`git`,[`rev-parse`,`${t}^{commit}`],{cwd:e,stdin:`ignore`});if(r.trim()!==n)throw Error(`Release tag ${t} does not point to checked-out commit ${n}.`)}async function J(e){let{stdout:t}=await M(`pnpm`,[`exec`,`lingui`,`--version`],{cwd:e,stdin:`ignore`});return t.trim()}export{P as buildI18n,I as materializeI18nSnapshot,F as prepareI18nRelease,L as resolveI18nConfig};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AssetCredentialStore } from "./assets/credentials.js";
|
|
2
|
+
|
|
3
|
+
//#region private-object-store.d.ts
|
|
4
|
+
type PrivateObjectStore = {
|
|
5
|
+
has(bucket: string, key: string): Promise<boolean>;
|
|
6
|
+
download(bucket: string, key: string, target: string): Promise<void>;
|
|
7
|
+
upload(bucket: string, key: string, source: string, contentType: string): Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
type PrivateObjectStoreConfig = {
|
|
10
|
+
credentialProfile: string;
|
|
11
|
+
endpoint: string;
|
|
12
|
+
region: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Creates the shared private S3-compatible store used by managed-asset sources and build-only state
|
|
16
|
+
* such as i18n snapshots. The credential names remain compatible with existing Coreframe projects.
|
|
17
|
+
*/
|
|
18
|
+
declare function createB2PrivateObjectStore({
|
|
19
|
+
config,
|
|
20
|
+
credentialStore,
|
|
21
|
+
env
|
|
22
|
+
}: {
|
|
23
|
+
config: PrivateObjectStoreConfig;
|
|
24
|
+
credentialStore?: AssetCredentialStore;
|
|
25
|
+
env?: NodeJS.ProcessEnv;
|
|
26
|
+
}): PrivateObjectStore;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { PrivateObjectStore, PrivateObjectStoreConfig, createB2PrivateObjectStore };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{resolveAssetCredentials as e}from"./assets/credentials.js";import{stat as t}from"node:fs/promises";import{createReadStream as n,createWriteStream as r}from"node:fs";import{pipeline as i}from"node:stream/promises";import{GetObjectCommand as a,HeadObjectCommand as o,PutObjectCommand as s,S3Client as c}from"@aws-sdk/client-s3";function l({config:l,credentialStore:u,env:d=process.env}){let f;async function p(){if(f)return f;let t=await e({env:d,profile:l.credentialProfile,store:u});return f=new c({endpoint:l.endpoint,forcePathStyle:!0,region:l.region,credentials:{accessKeyId:t.applicationKeyId,secretAccessKey:t.applicationKey}}),f}return{async has(e,t){try{return await(await p()).send(new o({Bucket:e,Key:t})),!0}catch(e){if(e.$metadata?.httpStatusCode===404)return!1;throw e}},async download(e,t,n){let o=await(await p()).send(new a({Bucket:e,Key:t}));if(!o.Body)throw Error(`Backblaze B2 returned an empty body for ${e}/${t}.`);await i(o.Body,r(n,{flags:`wx`}))},async upload(e,r,i,a){let{size:o}=await t(i);await(await p()).send(new s({Bucket:e,Key:r,Body:n(i),ContentLength:o,ContentType:a}))}}}export{l as createB2PrivateObjectStore};
|
package/dist/project.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region project.d.ts
|
|
2
2
|
declare function importProjectModule<T>(path: string): Promise<T>;
|
|
3
|
-
declare function importProjectDependency<T>(specifier: string): Promise<T>;
|
|
3
|
+
declare function importProjectDependency<T>(specifier: string, cwd?: string): Promise<T>;
|
|
4
4
|
//#endregion
|
|
5
5
|
export { importProjectDependency, importProjectModule };
|
package/dist/project.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolve as e}from"node:path";import{pathToFileURL as t}from"node:url";import{resolve as n}from"import-meta-resolve";async function r(n){return await import(t(e(process.cwd(),n)).href)}async function i(r){return await import(n(r,t(e(
|
|
1
|
+
import{resolve as e}from"node:path";import{pathToFileURL as t}from"node:url";import{resolve as n}from"import-meta-resolve";async function r(n){return await import(t(e(process.cwd(),n)).href)}async function i(r,i=process.cwd()){return await import(n(r,t(e(i,`package.json`)).href))}export{i as importProjectDependency,r as importProjectModule};
|
|
@@ -18,6 +18,10 @@ type WorkerEnvironmentEntryStatus = WorkerEnvironmentVariableConfig & {
|
|
|
18
18
|
local: LocalWorkerEnvironmentState;
|
|
19
19
|
deployed: Record<DeployEnvironment, DeployedWorkerEnvironmentState>;
|
|
20
20
|
};
|
|
21
|
+
type DeclaredWorkerSecret = WorkerEnvironmentVariableConfig & {
|
|
22
|
+
name: string;
|
|
23
|
+
requiredIn: DeployEnvironment[];
|
|
24
|
+
};
|
|
21
25
|
type WorkerEnvironmentProviderStatus = {
|
|
22
26
|
state: "available" | "not_checked" | "unavailable";
|
|
23
27
|
message?: string;
|
|
@@ -43,6 +47,12 @@ type QueueWorkerSecretOptions = {
|
|
|
43
47
|
value: string;
|
|
44
48
|
ensureIgnored?: (environment: DeployEnvironment, cwd: string) => Promise<void>;
|
|
45
49
|
};
|
|
50
|
+
type QueueWorkerSecretsOptions = {
|
|
51
|
+
cwd?: string;
|
|
52
|
+
environment: DeployEnvironment;
|
|
53
|
+
values: Readonly<Record<string, string>>;
|
|
54
|
+
ensureIgnored?: (environment: DeployEnvironment, cwd: string) => Promise<void>;
|
|
55
|
+
};
|
|
46
56
|
declare function inspectWorkerEnvironment({
|
|
47
57
|
cwd,
|
|
48
58
|
environment,
|
|
@@ -55,6 +65,19 @@ declare function queueWorkerSecret({
|
|
|
55
65
|
value,
|
|
56
66
|
ensureIgnored
|
|
57
67
|
}: QueueWorkerSecretOptions): Promise<void>;
|
|
68
|
+
declare function queueWorkerSecrets({
|
|
69
|
+
cwd,
|
|
70
|
+
environment,
|
|
71
|
+
values,
|
|
72
|
+
ensureIgnored
|
|
73
|
+
}: QueueWorkerSecretsOptions): Promise<void>;
|
|
74
|
+
declare function readDeclaredWorkerSecrets(cwd?: string): Promise<DeclaredWorkerSecret[]>;
|
|
75
|
+
declare function parseDeclaredWorkerSecretEnv(contents: string, declarations: DeclaredWorkerSecret[]): {
|
|
76
|
+
ignoredNames: string[];
|
|
77
|
+
values: {
|
|
78
|
+
[k: string]: string;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
58
81
|
declare function discardPendingWorkerSecret({
|
|
59
82
|
cwd,
|
|
60
83
|
environment,
|
|
@@ -69,4 +92,4 @@ declare function readPendingWorkerSecretSnapshot({
|
|
|
69
92
|
}): Promise<PendingWorkerSecretSnapshot>;
|
|
70
93
|
declare function clearAppliedPendingWorkerSecrets(snapshot: PendingWorkerSecretSnapshot, cwd?: string): Promise<void>;
|
|
71
94
|
//#endregion
|
|
72
|
-
export { DeployedWorkerEnvironmentState, LocalWorkerEnvironmentState, PendingWorkerSecretSnapshot, WorkerConfig, WorkerEnvironmentEntryStatus, WorkerEnvironmentKind, WorkerEnvironmentProviderStatus, WorkerEnvironmentStatus, WorkerEnvironmentVariableConfig, clearAppliedPendingWorkerSecrets, discardPendingWorkerSecret, inspectWorkerEnvironment, queueWorkerSecret, readPendingWorkerSecretSnapshot, workerEnvironmentKinds };
|
|
95
|
+
export { DeclaredWorkerSecret, DeployedWorkerEnvironmentState, LocalWorkerEnvironmentState, PendingWorkerSecretSnapshot, WorkerConfig, WorkerEnvironmentEntryStatus, WorkerEnvironmentKind, WorkerEnvironmentProviderStatus, WorkerEnvironmentStatus, WorkerEnvironmentVariableConfig, clearAppliedPendingWorkerSecrets, discardPendingWorkerSecret, inspectWorkerEnvironment, parseDeclaredWorkerSecretEnv, queueWorkerSecret, queueWorkerSecrets, readDeclaredWorkerSecrets, readPendingWorkerSecretSnapshot, workerEnvironmentKinds };
|
|
@@ -1 +1 @@
|
|
|
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{randomUUID as r}from"node:crypto";import{chmod as i,mkdir as a,readFile as o,rename as s,rm as c,stat as l,writeFile as u}from"node:fs/promises";import{resolve as d}from"node:path";import f from"nano-spawn";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=
|
|
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{randomUUID as r}from"node:crypto";import{chmod as i,mkdir as a,readFile as o,rename as s,rm as c,stat as l,writeFile as u}from"node:fs/promises";import{resolve as d}from"node:path";import f from"nano-spawn";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=P}={}){let o=T((await e({cwd:r})).worker?.environment),s=await D(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 C({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,O(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=j}){await y({cwd:e,environment:t,values:{[n]:r},ensureIgnored:i})}async function y({cwd:e=process.cwd(),environment:t,values:n,ensureIgnored:r=j}){let i=new Map((await b(e)).map(e=>[e.name,e]));for(let[t,r]of Object.entries(n))if(i.has(t)||await E(e,t),!r.trim())throw Error(`Worker secret ${t} cannot be empty.`);Object.keys(n).length!==0&&(await r(t,e),await A(e,t,{...(await C({cwd:e,environment:t})).values,...n}))}async function b(t=process.cwd()){return T((await e({cwd:t})).worker?.environment).filter(e=>e.kind===`secret`)}function x(e,t){let n;try{n=p(e)}catch{throw Error(`Could not parse the pasted dotenv contents.`)}let r=new Set(t.map(({name:e})=>e)),i=Object.fromEntries(Object.entries(n).filter(e=>r.has(e[0])&&typeof e[1]==`string`)),a=Object.entries(i).filter(([,e])=>!e.trim()).map(([e])=>e);if(a.length>0)throw Error(`Worker secrets cannot be empty: ${a.join(`, `)}.`);if(Object.keys(i).length===0)throw Error(`The dotenv contents do not contain any declared Worker secrets.`);return{ignoredNames:Object.keys(n).filter(e=>!r.has(e)).sort(),values:i}}async function S({cwd:e=process.cwd(),environment:t,name:n}){await E(e,n);let r=await C({cwd:e,environment:t});if(!(n in r.values))return!1;let i={...r.values};return delete i[n],await A(e,t,i),!0}async function C({cwd:t=process.cwd(),environment:n}){let r=await k(t,n),i=T((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 w(e,t=process.cwd()){let n=await k(t,e.environment),r=Object.fromEntries(Object.entries(n).filter(([t,n])=>e.values[t]!==n));await A(t,e.environment,r)}function T(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 E(t,n){let r=T((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 D(e){try{let t=p(await o(d(e,`.env`),`utf8`));return new Set(Object.entries(t).filter(([,e])=>!!e?.trim()).map(([e])=>e))}catch(e){if(F(e)&&e.code===`ENOENT`)return new Set;throw e}}function O(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 k(e,t){let n=M(e,t);try{let e=await l(n);if(process.platform!==`win32`&&e.mode&63)throw Error(`Pending Worker secret file ${n} must use mode 0600.`);let t=JSON.parse(await o(n,`utf8`));if(!N(t))throw Error(`Pending Worker secret file ${n} must contain a JSON object of strings.`);return t}catch(e){if(F(e)&&e.code===`ENOENT`)return{};throw e}}async function A(e,t,n){let o=M(e,t);if(Object.keys(n).length===0){await c(o,{force:!0});return}let l=d(e,g);await a(l,{mode:448,recursive:!0}),process.platform!==`win32`&&await i(l,448);let f=`${o}.${process.pid}.${r()}.tmp`;try{await u(f,`${JSON.stringify(n,null,2)}\n`,{flag:`wx`,mode:384}),await s(f,o)}finally{await c(f,{force:!0})}process.platform!==`win32`&&await i(o,384)}async function j(e,t){let n=`${g}/${e}.json`,r=`${g}/.coreframe-ignore-check`;try{await f(`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 M(e,t){return d(e,g,`${t}.json`)}function N(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 P(e,t){let{stdout:n}=await f(`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 F(e){return e instanceof Error&&`code`in e}export{w as clearAppliedPendingWorkerSecrets,S as discardPendingWorkerSecret,_ as inspectWorkerEnvironment,x as parseDeclaredWorkerSecretEnv,v as queueWorkerSecret,y as queueWorkerSecrets,b as readDeclaredWorkerSecrets,C as readPendingWorkerSecretSnapshot,m as workerEnvironmentKinds};
|