@lunora/cli 1.0.0-alpha.387 → 1.0.0-alpha.389

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/index.d.mts CHANGED
@@ -1611,6 +1611,14 @@ declare const validatorKindToSqlType: (kind: string) => ColumnSnapshot["sqlType"
1611
1611
  */
1612
1612
  declare const renderCreateTable: (table: TableSnapshot) => string;
1613
1613
  declare const renderDropTable: (tableName: string) => string;
1614
+ /**
1615
+ * `ALTER TABLE … ADD COLUMN`, always nullable. SQLite rejects `ADD COLUMN … NOT NULL` without a default on a table
1616
+ * that already has rows, and D1 is SQLite, so a NOT NULL add fails exactly when it matters. The runtime
1617
+ * auto-provisioner makes the same choice (see `alterGlobalTableDrift` in `@lunora/sql-store`), and the declared
1618
+ * `notNull` is still enforced on write. Existing rows read the new column as NULL until they are written, so for a
1619
+ * required column the statement is followed by a comment that says so and gives the backfill to run. The snapshot
1620
+ * records no default value, so the value is left for the author to fill in rather than guessed.
1621
+ */
1614
1622
  declare const renderAddColumn: (tableName: string, columnName: string, column: ColumnSnapshot) => string;
1615
1623
  declare const renderCreateIndex: (tableName: string, index: IndexSnapshot) => string;
1616
1624
  declare const renderDropIndex: (tableName: string, indexName: string) => string;
package/dist/index.d.ts CHANGED
@@ -1611,6 +1611,14 @@ declare const validatorKindToSqlType: (kind: string) => ColumnSnapshot["sqlType"
1611
1611
  */
1612
1612
  declare const renderCreateTable: (table: TableSnapshot) => string;
1613
1613
  declare const renderDropTable: (tableName: string) => string;
1614
+ /**
1615
+ * `ALTER TABLE … ADD COLUMN`, always nullable. SQLite rejects `ADD COLUMN … NOT NULL` without a default on a table
1616
+ * that already has rows, and D1 is SQLite, so a NOT NULL add fails exactly when it matters. The runtime
1617
+ * auto-provisioner makes the same choice (see `alterGlobalTableDrift` in `@lunora/sql-store`), and the declared
1618
+ * `notNull` is still enforced on write. Existing rows read the new column as NULL until they are written, so for a
1619
+ * required column the statement is followed by a comment that says so and gives the backfill to run. The snapshot
1620
+ * records no default value, so the value is left for the author to fill in rather than guessed.
1621
+ */
1614
1622
  declare const renderAddColumn: (tableName: string, columnName: string, column: ColumnSnapshot) => string;
1615
1623
  declare const renderCreateIndex: (tableName: string, index: IndexSnapshot) => string;
1616
1624
  declare const renderDropIndex: (tableName: string, indexName: string) => string;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{C as o,V as a,r as n}from"./packem_shared/cli-DwEpuZm9.mjs";import{runCodegenCommand as m}from"./packem_chunks/runCodegenCommand.mjs";import{runDeployCommand as p}from"./packem_chunks/runDeployCommand.mjs";import{runDevCommand as f}from"./packem_chunks/runDevCommand.mjs";import{p as i}from"./packem_shared/plan-Dl57T11P.mjs";import{runInitCommand as l}from"./packem_chunks/runInitCommand.mjs";import{runMigrateGenerateCommand as I}from"./packem_shared/runMigrateGenerateCommand-Ii_WMZEA.mjs";import{runResetCommand as g}from"./packem_chunks/runResetCommand.mjs";import{runRpcCommand as T}from"./packem_chunks/runRpcCommand.mjs";import{EXIT_CODE as S,exitCodeForCode as A,exitCodeForError as c,exitCodeForStatus as M}from"./packem_shared/EXIT_CODE-D_XCixv8.mjs";import{insertSchemaExtension as v}from"./packem_shared/insertSchemaExtension-DuiV6cba.mjs";import{createLogger as b,pail as O}from"./packem_shared/createLogger-Cl18I8AX.mjs";import{diffSnapshots as y,renderAddColumn as L,renderCreateIndex as w,renderCreateTable as B,renderDropIndex as P,renderDropTable as U,renderMigrationFile as V,validatorKindToSqlType as W}from"./packem_shared/diffSnapshots-WWwx-KvZ.mjs";import{default as N}from"./packem_shared/schemaIrToSnapshot-BjK-0IRo.mjs";import{createRecordingSpawner as j,defaultSpawner as q}from"./packem_shared/createRecordingSpawner-Ch7zuQQE.mjs";import{default as K}from"./packem_shared/parseManifest-CwPTKdtS.mjs";import{D as Y,r as Z}from"./packem_shared/import-BRM1hs7j.mjs";import{REQUIRED_COMPATIBILITY_DATE as z,REQUIRED_FLAG as J,validateWranglerProject as $,validateWranglerConfig as rr}from"@lunora/config/cloudflare";import{b as or}from"./packem_shared/catalog-CahzmDLV.mjs";import{r as nr,a as tr,b as mr}from"./packem_shared/commands-DgUDxkA8.mjs";import{runExportCommand as pr}from"./packem_shared/runExportCommand-slAq5BCF.mjs";export{o as COMMANDS,Y as DEFAULT_IMPORT_BATCH_SIZE,S as EXIT_CODE,z as REQUIRED_COMPATIBILITY_DATE,J as REQUIRED_FLAG,a as VERSION,or as buildRegistryIndex,b as createLogger,j as createRecordingSpawner,q as defaultSpawner,y as diffSnapshots,A as exitCodeForCode,c as exitCodeForError,M as exitCodeForStatus,v as insertSchemaExtension,O as pail,K as parseManifest,i as planDevCommand,L as renderAddColumn,w as renderCreateIndex,B as renderCreateTable,P as renderDropIndex,U as renderDropTable,V as renderMigrationFile,nr as runAddCommand,tr as runBuildIndexCommand,n as runCli,m as runCodegenCommand,p as runDeployCommand,f as runDevCommand,pr as runExportCommand,Z as runImportCommand,l as runInitCommand,I as runMigrateGenerateCommand,mr as runRegistryViewCommand,g as runResetCommand,T as runRpcCommand,N as schemaIrToSnapshot,$ as validateWrangler,rr as validateWranglerConfig,W as validatorKindToSqlType};
1
+ import{C as o,V as a,r as n}from"./packem_shared/cli-DwEpuZm9.mjs";import{runCodegenCommand as m}from"./packem_chunks/runCodegenCommand.mjs";import{runDeployCommand as p}from"./packem_chunks/runDeployCommand.mjs";import{runDevCommand as f}from"./packem_chunks/runDevCommand.mjs";import{p as i}from"./packem_shared/plan-Dl57T11P.mjs";import{runInitCommand as l}from"./packem_chunks/runInitCommand.mjs";import{runMigrateGenerateCommand as I}from"./packem_shared/runMigrateGenerateCommand-BwemPIOz.mjs";import{runResetCommand as g}from"./packem_chunks/runResetCommand.mjs";import{runRpcCommand as T}from"./packem_chunks/runRpcCommand.mjs";import{EXIT_CODE as S,exitCodeForCode as A,exitCodeForError as c,exitCodeForStatus as M}from"./packem_shared/EXIT_CODE-D_XCixv8.mjs";import{insertSchemaExtension as v}from"./packem_shared/insertSchemaExtension-DuiV6cba.mjs";import{createLogger as b,pail as O}from"./packem_shared/createLogger-Cl18I8AX.mjs";import{diffSnapshots as y,renderAddColumn as L,renderCreateIndex as w,renderCreateTable as B,renderDropIndex as P,renderDropTable as U,renderMigrationFile as V,validatorKindToSqlType as W}from"./packem_shared/diffSnapshots-htqNZT8Q.mjs";import{default as N}from"./packem_shared/schemaIrToSnapshot-BHuNOfgs.mjs";import{createRecordingSpawner as j,defaultSpawner as q}from"./packem_shared/createRecordingSpawner-Ch7zuQQE.mjs";import{default as K}from"./packem_shared/parseManifest-CwPTKdtS.mjs";import{D as Y,r as Z}from"./packem_shared/import-BRM1hs7j.mjs";import{REQUIRED_COMPATIBILITY_DATE as z,REQUIRED_FLAG as J,validateWranglerProject as $,validateWranglerConfig as rr}from"@lunora/config/cloudflare";import{b as or}from"./packem_shared/catalog-CahzmDLV.mjs";import{r as nr,a as tr,b as mr}from"./packem_shared/commands-DgUDxkA8.mjs";import{runExportCommand as pr}from"./packem_shared/runExportCommand-slAq5BCF.mjs";export{o as COMMANDS,Y as DEFAULT_IMPORT_BATCH_SIZE,S as EXIT_CODE,z as REQUIRED_COMPATIBILITY_DATE,J as REQUIRED_FLAG,a as VERSION,or as buildRegistryIndex,b as createLogger,j as createRecordingSpawner,q as defaultSpawner,y as diffSnapshots,A as exitCodeForCode,c as exitCodeForError,M as exitCodeForStatus,v as insertSchemaExtension,O as pail,K as parseManifest,i as planDevCommand,L as renderAddColumn,w as renderCreateIndex,B as renderCreateTable,P as renderDropIndex,U as renderDropTable,V as renderMigrationFile,nr as runAddCommand,tr as runBuildIndexCommand,n as runCli,m as runCodegenCommand,p as runDeployCommand,f as runDevCommand,pr as runExportCommand,Z as runImportCommand,l as runInitCommand,I as runMigrateGenerateCommand,mr as runRegistryViewCommand,g as runResetCommand,T as runRpcCommand,N as schemaIrToSnapshot,$ as validateWrangler,rr as validateWranglerConfig,W as validatorKindToSqlType};
@@ -1 +1 @@
1
- import{d as u}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as c}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as m}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{runMigrateGenerateCommand as l,runMigrateDataCommand as g,runMigrateToHyperdriveCommand as p,runMigrateCreateCommand as y}from"../packem_shared/runMigrateGenerateCommand-Ii_WMZEA.mjs";const b=n=>{const{argument:o,cwd:r,logger:t,options:a}=n,e=l({cwd:r,logger:t,name:o[1]??a.name});return{code:e.code,data:{empty:e.empty,migrationFile:e.migrationFile===""?void 0:e.migrationFile,subcommand:"generate"},...e.error===void 0?{}:{error:e.error}}},f=async n=>{const{logger:o,options:r}=n,t=await p({batchSize:r.batchSize,fromToken:r.fromToken??r.token,fromUrl:r.fromUrl??r.url,logger:o,out:r.out,prod:r.prod===!0,tables:r.tables,toToken:r.toToken??r.token,toUrl:r.toUrl??r.url,yes:r.yes===!0});return{code:t.code,data:{bytes:t.bytes,exported:t.exported,imported:t.imported,subcommand:"d1-to-hyperdrive"},...t.error===void 0?{}:{error:t.error}}},w=async n=>{const{argument:o,cwd:r,logger:t,options:a}=n,e=o[1]??a.name;if(!e){const i="migrate create requires a name. Usage: lunora migrate create <name> [--table <table>]";return t.error(i),{code:c.USAGE,error:i}}const s=await y({cwd:r,logger:t,name:e,table:a.table});return{code:s.code,data:{file:s.file===""?void 0:s.file,name:e,subcommand:"create"}}},h=async(n,o)=>{const{argument:r,cwd:t,logger:a,options:e}=n,s=r[1]??e.name;if(!s){const d=`migrate ${o} requires a migration id. Usage: lunora migrate ${o} <id>`;return a.error(d),{code:c.USAGE,error:d}}const i=await g({batchSize:e.batchSize,cwd:t,dryRun:e.dryRun===!0,id:s,logger:a,maxBatches:e.steps,prod:e.prod===!0,subcommand:o,token:e.token,url:m({cwd:t,prod:e.prod===!0,url:e.url}),yes:e.yes===!0});return{code:i.code,data:{id:s,result:i.body,subcommand:o}}},C=u(async({argument:n,cwd:o,logger:r,options:t})=>{const a=n[0],e={argument:n,cwd:o,logger:r,options:t};switch(a){case"create":return await w(e);case"d1-to-hyperdrive":return await f(e);case"down":case"status":case"up":return await h(e,a);case"generate":return b(e);default:{const s=`unknown migrate subcommand: "${a??""}" — expected generate | create | up | down | status`;return e.logger.error(s),{code:c.USAGE,error:s}}}});export{C as execute};
1
+ import{d as u}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as c}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as m}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{runMigrateGenerateCommand as l,runMigrateDataCommand as g,runMigrateToHyperdriveCommand as p,runMigrateCreateCommand as y}from"../packem_shared/runMigrateGenerateCommand-BwemPIOz.mjs";const b=n=>{const{argument:o,cwd:r,logger:t,options:a}=n,e=l({cwd:r,logger:t,name:o[1]??a.name});return{code:e.code,data:{empty:e.empty,migrationFile:e.migrationFile===""?void 0:e.migrationFile,subcommand:"generate"},...e.error===void 0?{}:{error:e.error}}},f=async n=>{const{logger:o,options:r}=n,t=await p({batchSize:r.batchSize,fromToken:r.fromToken??r.token,fromUrl:r.fromUrl??r.url,logger:o,out:r.out,prod:r.prod===!0,tables:r.tables,toToken:r.toToken??r.token,toUrl:r.toUrl??r.url,yes:r.yes===!0});return{code:t.code,data:{bytes:t.bytes,exported:t.exported,imported:t.imported,subcommand:"d1-to-hyperdrive"},...t.error===void 0?{}:{error:t.error}}},w=async n=>{const{argument:o,cwd:r,logger:t,options:a}=n,e=o[1]??a.name;if(!e){const i="migrate create requires a name. Usage: lunora migrate create <name> [--table <table>]";return t.error(i),{code:c.USAGE,error:i}}const s=await y({cwd:r,logger:t,name:e,table:a.table});return{code:s.code,data:{file:s.file===""?void 0:s.file,name:e,subcommand:"create"}}},h=async(n,o)=>{const{argument:r,cwd:t,logger:a,options:e}=n,s=r[1]??e.name;if(!s){const d=`migrate ${o} requires a migration id. Usage: lunora migrate ${o} <id>`;return a.error(d),{code:c.USAGE,error:d}}const i=await g({batchSize:e.batchSize,cwd:t,dryRun:e.dryRun===!0,id:s,logger:a,maxBatches:e.steps,prod:e.prod===!0,subcommand:o,token:e.token,url:m({cwd:t,prod:e.prod===!0,url:e.url}),yes:e.yes===!0});return{code:i.code,data:{id:s,result:i.body,subcommand:o}}},C=u(async({argument:n,cwd:o,logger:r,options:t})=>{const a=n[0],e={argument:n,cwd:o,logger:r,options:t};switch(a){case"create":return await w(e);case"d1-to-hyperdrive":return await f(e);case"down":case"status":case"up":return await h(e,a);case"generate":return b(e);default:{const s=`unknown migrate subcommand: "${a??""}" — expected generate | create | up | down | status`;return e.logger.error(s),{code:c.USAGE,error:s}}}});export{C as execute};
@@ -1,4 +1,4 @@
1
- import{dirname as F,join as B}from"node:path";import{discoverMigrations as te,readServiceBindings as ne,runCodegen as oe}from"@lunora/codegen";import{readLinkedProject as W,execArgsFor as H,detectPackageManager as b,discoverContainerInfo as V,COMPOSED_WORKER_ENTRY as N,writeLinkedProject as ae,discoverSchemaInfo as G,DEV_VARS_FILE as w,parseDevVariableEntries as K,isMintableSecretKey as I,secretSourcesFromBindings as ie,inferLunoraBindings as q,resolveSchemaDirectory as R,requiredSecrets as se,generateSecretValue as ce,toolchainExecArgs as j,upsertDevVariableLine as de,writeDevVariablesFileAtomically as le,resolveDeployDriver as C,planToolchainInvocation as Y,runPostCodegenHook as ue}from"@lunora/config";import{findWranglerFile as z,readWranglerJsonc as P,mergeWranglerEnvironment as J,warnCloudflareCliConfigOnce as ge,validateWranglerProject as me,wranglerCodegenInputs as fe,reconcileWranglerBindings as ve,reconcileWranglerCompatibilityDate as he,reconcileWranglerCrons as pe,describePreservedCrons as ye}from"@lunora/config/cloudflare";import{Spinner as we}from"@visulima/spinner";import{r as ke,a as be,e as $e}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{p as Se,a as De}from"../packem_shared/deploy-target-DCWwiuAe.mjs";import{mkdirSync as Ee,writeFileSync as x,existsSync as p,readFileSync as k}from"node:fs";import{m as Ae,e as Re,i as je,b as Ce,a as Pe}from"../packem_shared/secret-key-6bCftsxj.mjs";import{w as xe}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{d as _e}from"../packem_shared/command-CAXl2A1S.mjs";import{r as _}from"../packem_shared/wrangler-name-Dsk5K1f-.mjs";import{EXIT_CODE as h}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{containerBuildTag as Te}from"@lunora/container";import{i as Ne,a as Ie}from"../packem_shared/docker-AqLAkOyY.mjs";import{defaultSpawner as D}from"../packem_shared/createRecordingSpawner-Ch7zuQQE.mjs";import{r as X}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{p as Le,H as Me,a as Ue,r as Oe}from"../packem_shared/health-probe-Bw9JFfIy.mjs";import{join as y}from"@visulima/path";import{Project as Fe}from"ts-morph";import{runMigrateDataCommand as Be}from"../packem_shared/runMigrateGenerateCommand-Ii_WMZEA.mjs";import{c as We}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{l as He}from"../packem_shared/wrangler-secrets-3chIVv3H.mjs";const Ve=Object.freeze({edges:[],nodes:[],modules:[],unresolved:[],version:1}),Ge=20,L=e=>`${e.from} -${e.kind}-> ${e.to}`,$=(e,r)=>{const o=new Set(e);return r.filter(t=>!o.has(t)).toSorted((t,n)=>t.localeCompare(n))},Ke=(e,r)=>{const o=e.modules.map(i=>i.name),t=r.modules.map(i=>i.name),n=e.edges.map(i=>L(i)),a=r.edges.map(i=>L(i));return{addedEdges:$(n,a),addedModules:$(o,t),removedEdges:$(a,n),removedModules:$(t,o)}},S=(e,r,o)=>{if(o.length===0)return[];const t=o.slice(0,Ge).map(a=>` ${e} ${a}`),n=o.length-t.length;return[`${r}:`,...t,...n>0?[` … and ${n.toString()} more`]:[]]},qe=e=>{const r=[...S("+","modules added",e.addedModules),...S("-","modules removed",e.removedModules),...S("+","edges added",e.addedEdges),...S("-","edges removed",e.removedEdges)];return r.length===0?void 0:`architecture changes since the last deploy:
1
+ import{dirname as F,join as B}from"node:path";import{discoverMigrations as te,readServiceBindings as ne,runCodegen as oe}from"@lunora/codegen";import{readLinkedProject as W,execArgsFor as H,detectPackageManager as b,discoverContainerInfo as V,COMPOSED_WORKER_ENTRY as N,writeLinkedProject as ae,discoverSchemaInfo as G,DEV_VARS_FILE as w,parseDevVariableEntries as K,isMintableSecretKey as I,secretSourcesFromBindings as ie,inferLunoraBindings as q,resolveSchemaDirectory as R,requiredSecrets as se,generateSecretValue as ce,toolchainExecArgs as j,upsertDevVariableLine as de,writeDevVariablesFileAtomically as le,resolveDeployDriver as C,planToolchainInvocation as Y,runPostCodegenHook as ue}from"@lunora/config";import{findWranglerFile as z,readWranglerJsonc as P,mergeWranglerEnvironment as J,warnCloudflareCliConfigOnce as ge,validateWranglerProject as me,wranglerCodegenInputs as fe,reconcileWranglerBindings as ve,reconcileWranglerCompatibilityDate as he,reconcileWranglerCrons as pe,describePreservedCrons as ye}from"@lunora/config/cloudflare";import{Spinner as we}from"@visulima/spinner";import{r as ke,a as be,e as $e}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{p as Se,a as De}from"../packem_shared/deploy-target-DCWwiuAe.mjs";import{mkdirSync as Ee,writeFileSync as x,existsSync as p,readFileSync as k}from"node:fs";import{m as Ae,e as Re,i as je,b as Ce,a as Pe}from"../packem_shared/secret-key-6bCftsxj.mjs";import{w as xe}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{d as _e}from"../packem_shared/command-CAXl2A1S.mjs";import{r as _}from"../packem_shared/wrangler-name-Dsk5K1f-.mjs";import{EXIT_CODE as h}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{containerBuildTag as Te}from"@lunora/container";import{i as Ne,a as Ie}from"../packem_shared/docker-AqLAkOyY.mjs";import{defaultSpawner as D}from"../packem_shared/createRecordingSpawner-Ch7zuQQE.mjs";import{r as X}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{p as Le,H as Me,a as Ue,r as Oe}from"../packem_shared/health-probe-Bw9JFfIy.mjs";import{join as y}from"@visulima/path";import{Project as Fe}from"ts-morph";import{runMigrateDataCommand as Be}from"../packem_shared/runMigrateGenerateCommand-BwemPIOz.mjs";import{c as We}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{l as He}from"../packem_shared/wrangler-secrets-3chIVv3H.mjs";const Ve=Object.freeze({edges:[],nodes:[],modules:[],unresolved:[],version:1}),Ge=20,L=e=>`${e.from} -${e.kind}-> ${e.to}`,$=(e,r)=>{const o=new Set(e);return r.filter(t=>!o.has(t)).toSorted((t,n)=>t.localeCompare(n))},Ke=(e,r)=>{const o=e.modules.map(i=>i.name),t=r.modules.map(i=>i.name),n=e.edges.map(i=>L(i)),a=r.edges.map(i=>L(i));return{addedEdges:$(n,a),addedModules:$(o,t),removedEdges:$(a,n),removedModules:$(t,o)}},S=(e,r,o)=>{if(o.length===0)return[];const t=o.slice(0,Ge).map(a=>` ${e} ${a}`),n=o.length-t.length;return[`${r}:`,...t,...n>0?[` … and ${n.toString()} more`]:[]]},qe=e=>{const r=[...S("+","modules added",e.addedModules),...S("-","modules removed",e.removedModules),...S("+","edges added",e.addedEdges),...S("-","edges removed",e.removedEdges)];return r.length===0?void 0:`architecture changes since the last deploy:
2
2
  ${r.join(`
3
3
  `)}`},Ye=(e,r)=>B(e,".lunora",r===void 0?"architecture.json":`architecture.${r}.json`),ze=e=>{if(p(e))try{const r=JSON.parse(k(e,"utf8"));return r.version===1&&Array.isArray(r.modules)&&Array.isArray(r.edges)?r:void 0}catch{return}},Je=e=>{const{cwd:r,environment:o,logger:t}=e,n=Ye(r,o),a=ze(n),i=e.current??(a===void 0?void 0:Ve);if(i===void 0)return;const s=a===void 0?void 0:qe(Ke(a,i));return s!==void 0&&t.info(s),()=>{try{Ee(F(n),{recursive:!0}),x(n,`${JSON.stringify(i,void 0,2)}
4
4
  `,"utf8")}catch(c){t.warn(`could not record the deployed architecture at ${n}: ${c instanceof Error?c.message:String(c)}`)}}},Xe=e=>{const{cwd:r,env:o,logger:t,mintedSecretsFile:n}=e;try{const a=W(r),i=a?.workerName??_(r),s=e.url??a?.workerUrl;t.success("deploy complete"),t.info(` worker: ${i??"(see wrangler output above)"}`),o!==void 0&&t.info(` env: ${o}`),s===void 0?t.info(" url: run `lunora link --url <https://your-worker>` to record it"):t.info(` url: ${s}`),n!==void 0&&t.info(` secrets: generated value(s) recorded in ${n}`),e.logsAvailable&&t.info(" logs: lunora logs")}catch{}},Qe=async e=>{if(e.targets.length===0)return{builtTags:[],code:0};if(!(e.railpackAvailable??Ne)()){const t="deploy blocked: a container uses `image: { build }` (Railpack), but Railpack isn't ready. Install the `railpack` CLI and start a BuildKit instance, e.g. `docker run --rm --privileged -d --name buildkit moby/buildkit` then `export BUILDKIT_HOST=docker-container://buildkit`. Alternatively switch the container's `image` to a Dockerfile path or a pre-built registry reference.";return e.logger.error(t),{builtTags:[],code:1,error:t}}const r=e.spawner??D,o=[];for(const t of e.targets){const n=Te(t.exportName),a={args:["build",t.buildDir,"--name",n],command:"railpack",cwd:e.cwd},i=H(b(e.cwd),"wrangler",["containers","push",n]),s={args:i.args,command:i.command,cwd:e.cwd};e.logger.info(`railpack: building "${t.exportName}" → ${n} from ${t.buildDir}`);const c=await r(a);if(c.code!==0){const d=`railpack build failed for container "${t.exportName}" (${t.buildDir})`;return e.logger.error(d),{builtTags:o,code:c.code,error:d}}e.logger.info(`railpack: pushing ${n} to the Cloudflare Registry`);const l=await r(s);if(l.code!==0){const d=`wrangler containers push failed for "${t.exportName}" (${n})`;return e.logger.error(d),{builtTags:o,code:l.code,error:d}}o.push(n)}return{builtTags:o,code:0}},Ze=e=>{const o=[z(e),B(e,"package.json")].filter(t=>t!==void 0&&p(t)).map(t=>[t,k(t,"utf8")]);return()=>{for(const[t,n]of o)k(t,"utf8")!==n&&x(t,n,"utf8")}},Q="<replace-with-d1-create-id>",M=e=>Array.isArray(e)?e.filter(r=>r!=null):void 0,T=(e,r)=>{const o=z(e);if(!o)return;const{parsed:t}=P(o);if(t===void 0)return;const{error:n,merged:a}=J(t,r),i=n===void 0?a:t,{containers:s,d1_databases:c,vars:l}=i;return{containers:M(s),d1_databases:M(c),vars:typeof l=="object"&&l!==null&&!Array.isArray(l)?l:void 0}},er=/^127(?:\.\d{1,3}){3}$/,rr=e=>{try{const{hostname:r}=new URL(e);return r==="localhost"||er.test(r)||r==="::1"||r==="[::1]"}catch{return!1}},tr=e=>e.startsWith("./")||e.startsWith("../")||e.startsWith("/")||e.includes("Dockerfile"),nr=(e,r,o,t="deploy",n)=>{const a=(T(e,n)?.containers??[]).filter(s=>typeof s?.image=="string"&&tr(s.image));if(a.length===0||o())return;const i=`${t} blocked: wrangler.jsonc declares ${String(a.length)} container(s) built from a local Dockerfile, but no Docker-compatible engine is available. Start Docker (or Colima), or point the container's \`image\` at a pre-built registry reference. Note: container images must target linux/amd64.`;return r.error(i),i},Z=e=>p(y(e,N))?N:void 0,or=(e,r,o="deploy")=>{for(const t of V(e,"lunora").containers){const n=t.schedulingPolicy===void 0?t.image:void 0,i=[...n?.kind==="dockerfile"?[{field:"image",path:n.dockerfilePath}]:[],...Object.entries(t.schedulingPolicy==="durable_object"?t.images??{}:{}).flatMap(([s,c])=>c.kind==="dockerfile"?[{field:`images["${s}"]`,path:c.dockerfilePath}]:[])].find(s=>!p(y(e,s.path)));if(i!==void 0){const s=`${o} blocked: container "${t.exportName}" references a Dockerfile at "${i.path}" that does not exist. Create it or fix the \`${i.field}\` path in lunora/containers.ts.`;return r.error(s),s}if(n?.kind==="build"&&!p(y(e,n.buildDir))){const s=`${o} blocked: container "${t.exportName}" references a Railpack build directory "${n.buildDir}" that does not exist. Create it or fix the \`image.build\` path in lunora/containers.ts.`;return r.error(s),s}}},ar=(e,r)=>(T(e,r)?.d1_databases??[]).find(o=>o.database_id===Q)?.binding,ir=e=>{if(!e.migrate||e.dryRun||e.preview)return;if(e.migrateUrl===void 0){const o="--migrate requires --migrate-url <https://your-worker> — the deploy target URL is only known after wrangler runs, and this gate runs before it; refusing to default to localhost";return e.logger.error(o),o}if(e.migrateYes!==!0){const o="--migrate runs production data migrations after deploy. Re-run with --migrate-yes to confirm.";return e.logger.error(o),o}const r=e.migrateToken??process.env.LUNORA_ADMIN_TOKEN;if(r===void 0||r===""){const o="admin token required for --migrate — pass --migrate-token or set LUNORA_ADMIN_TOKEN";return e.logger.error(o),o}},sr=(e,r,o="deploy",t)=>{const n=ar(e,t);if(n===void 0)return;const a=`${o} blocked: the "${n}" D1 binding has a placeholder database_id ("${Q}"). Run \`wrangler d1 create <name>\` to create the database, then replace the placeholder in wrangler.jsonc with the real id before deploying.`;return r.error(a),a},cr=(e,r,o="deploy",t)=>{const n=T(e,t)?.vars;if(!n)return;const a=Object.entries(n).filter(([,s])=>typeof s=="string"&&rr(s)).map(([s])=>s).toSorted((s,c)=>s.localeCompare(c));if(a.length===0)return;const i=`${o} blocked: ${a.join(", ")} in wrangler.jsonc point at localhost. A deployed Worker can't reach a loopback address, so this silently breaks scheduled-job dispatch, reverse cross-shard relations, and auth callbacks. Set each to the deployed worker's public URL (or move it to a secret with \`wrangler secret put\`) before deploying.`;return r.error(i),i},dr=(e,r,o)=>{const t=sr(e,r.logger,o,r.env);if(t!==void 0)return{code:h.USAGE,error:t};const n=cr(e,r.logger,o,r.env);if(n!==void 0)return{code:h.USAGE,error:n};const a=or(e,r.logger,o);if(a!==void 0)return{code:h.USAGE,error:a};const i=nr(e,r.logger,r.dockerAvailable??Ie,o,r.env);return i===void 0?void 0:{code:h.MISSING_DEPENDENCY,error:i}},lr=/https?:\/\/[^\s"'<>]+\.workers\.dev[^\s"'<>]*/u,ur=/https:\/\/[^\s"'<>]+/u,gr=e=>{if(e===void 0)return;const r=lr.exec(e);return r?r[0]:ur.exec(e)?.[0]},U=e=>e===void 0?"":` (--env ${e})`,mr=({cwd:e,env:r,logger:o,now:t,url:n})=>{if(n!==void 0)try{const a=W(e);if(a!==void 0){if(a.workerUrl===n&&a.env===r)return;o.warn(`link: .lunora/project.json records ${a.workerUrl??"(no url)"}${U(a.env)}, but this deploy published ${n}${U(r)}. Keeping the recorded value — run \`lunora link --url ${n}${r===void 0?"":` --env ${r}`}\` to update it.`);return}const i=(t??(()=>new Date().toISOString()))();ae(e,{env:r,linkedAt:i,workerName:_(e),workerUrl:n}),o.success(`link: recorded ${n} in .lunora/project.json (run \`lunora link\` to change)`)}catch{}},fr=async(e,r)=>{const o=new Fe({skipAddingFilesFromTsConfig:!0}),t=y(r,"lunora");let n;try{n=te(o,t)}catch(a){const i=a instanceof Error?a.message:String(a);return e.logger.warn(`--migrate: could not discover migrations (${i}); skipping`),0}if(n.length===0)return e.logger.info("--migrate: no data migrations declared in lunora/"),0;e.logger.info(`--migrate: running ${String(n.length)} migration(s) against deployed worker`);for(const a of n){e.logger.info(`--migrate: up "${a.id}" (table "${a.table}")`);const i={cwd:r,fetchImpl:e.fetchImpl,id:a.id,logger:e.logger,prod:!0,subcommand:"up",token:e.migrateToken,url:e.migrateUrl,yes:e.migrateYes===!0},s=await Be(i);if(s.code!==0)return e.logger.error(`--migrate: migration "${a.id}" failed — see output above`),s.code;e.logger.success(`--migrate: "${a.id}" applied`)}return 0},vr=async(e,r)=>{const{info:o}=G(r,"lunora"),t=o?.vectorMetadata??[];if(t.length===0)return;const n=[];for(const s of t){const c=Ae(s.kind);if(c===void 0){e.logger.warn(`vector index "${s.index}" declares metadata "${s.property}", whose column type cannot be filtered on in Vectorize — it is stored with each vector but no filter will match it.`);continue}n.push({index:s.index,property:s.property,type:c})}if(n.length===0)return;const i=(await Re({cwd:r,entries:n,exec:H(b(r),"wrangler",[]),logger:e.logger,spawner:e.spawner??D})).filter(s=>s.status!=="failed").length;i>0&&e.logger.success(`vectorize metadata indexes ready: ${String(i)}/${String(n.length)}`)},O=5,hr=2e3,pr=async(e,r,o)=>{if(e.healthCheck!==!0)return;const t=o??X({cwd:r,env:e.env});if(t===void 0){const a="--health-check: the deploy succeeded, but no URL to probe could be resolved — wrangler's output carried none and this checkout has no link for this environment. Run `lunora link --url <https://your-worker>` and re-deploy, or drop --health-check.";return e.logger.error(a),{error:a,ok:!1,url:""}}const n=await Le({attempts:O,baseUrl:t,delayMs:hr,fetchImpl:e.healthFetch,paths:[Me,Ue],sleep:e.healthSleep});return n.error===void 0?(e.logger.success(`health check ok (${n.url})`),{ok:!0,url:n.url}):(e.logger.error(`--health-check: the deploy succeeded, but the new version did not answer after ${String(O)} attempt(s) — ${n.error}`),{error:n.error,ok:!1,url:n.url})},yr=async(e,r,o,t,n,a)=>{await vr(e,r);const i=e.migrate?await fr(e,r):0;return i===0&&n?.(),{code:i,descriptor:o,mintedSecretsFile:a,validation:t}},wr=async({cwd:e,descriptor:r,mintedSecretsFile:o,options:t,reblessSchemaBaseline:n,recordArchitecture:a,stdout:i,validation:s})=>{const c={deployedAt:new Date().toISOString(),dryRun:t.dryRun===!0,env:t.env,preview:t.preview===!0,url:t.dryRun===!0?void 0:gr(i),workerName:_(e)};if(t.dryRun===!0||t.preview===!0)return t.healthCheck===!0&&t.logger.warn(`--health-check skipped: ${t.dryRun===!0?"a dry run publishes nothing":"a preview version serves no live traffic"}`),{code:0,deployment:c,descriptor:r,mintedSecretsFile:o,validation:s};t.temporary!==!0&&(a?.(),mr({cwd:e,env:t.env,logger:t.logger,url:c.url}));const l=await pr(t,e,c.url);return l?.error!==void 0?{code:h.UNAVAILABLE,deployment:c,descriptor:r,healthCheck:l,mintedSecretsFile:o,validation:s}:{...await yr(t,e,r,s,n,o),deployment:c,healthCheck:l}},kr=(e,r,o)=>{const t=y(e,w);if(!p(t))return;let n;try{n=K(k(t,"utf8")).length}catch{return}if(n===0)return;const a=o.toolchain?.secretPut!==void 0;r.warn(a?`Note: \`lunora deploy\` does not push secrets. ${w} has ${String(n)} key(s); if you changed them, run \`lunora env push --yes\` to update the deployed secrets.`:`Note: ${o.name} deploys read no secrets. ${w} has ${String(n)} key(s) that only the dev server sees; put the values the deployed worker needs in wrangler \`vars\`.`)},br=async e=>{let r=[];try{r=ie(await q({projectRoot:e,schemaDir:R(e)}))}catch{}const o=se(r).map(n=>n.key);let t=[];try{const n=y(e,w);p(n)&&(t=K(k(n,"utf8")).map(a=>a.key).filter(a=>je(a)))}catch{}return[...new Set([...o,...t])]},$r=async(e,r,o,t)=>{const{logger:n}=r,a=r.spawner??D,i=b(e),s=r.env===void 0?"":` --env ${r.env}`,c=t.toolchain?.secretPut;if(c===void 0)return n.error(`deploy target "${t.id}" has no secret store; cannot push secrets`),{minted:[],ok:!1};const l=[];for(const d of o){const g=ce(),m=c({environment:r.env,key:d,temporary:r.temporary}),u=j(i,m),f=await a({args:u.args,command:u.command,cwd:e,input:g});if(f.code!==0)return n.error(`failed to push secret ${d} (exit ${String(f.code)}); set it manually with \`wrangler secret put ${d}${s}\`.`),{minted:l,ok:!1};l.push({key:d,value:g}),n.success(`generated + pushed ${d}`)}return{minted:l,ok:!0}},Sr=/^[\w-]+$/u,E=[".dev.vars",".dev.vars.*","!.dev.vars.example"],Dr=/\r?\n/u,Er=(e,r)=>{const o=y(e,".gitignore");try{const t=p(o)?k(o,"utf8"):"",n=new Set(t.split(Dr).map(c=>c.trim())),a=E.filter(c=>!n.has(c));if(a.length===0)return;const i=a.some(c=>!c.startsWith("!"))?[...a.filter(c=>!c.startsWith("!")),...E.filter(c=>c.startsWith("!"))]:a,s=t.length>0&&!t.endsWith(`
@@ -0,0 +1,7 @@
1
+ import{quoteIdentifier as a,sqlAffinityForKind as h,frameworkColumnDdl as y,OCC_VERSION_COLUMN as T,MAX_D1_TABLE_COLUMNS as d,columnRef as E,physicalIndexName as c}from"@lunora/d1/dialect";import{LunoraError as L}from"@lunora/errors";const v=e=>h(e),f=(e,i)=>{const s=[a(e),i.sqlType];return i.nullable||s.push("NOT NULL"),s.join(" ")},g=e=>{const i=Object.entries(e.columns).map(([o,r])=>` ${f(o,r)}`),s=[...y(),`${a(T)} INTEGER`],n=s.length+i.length;if(n>d)throw new L("VALIDATION_ERROR",`table "${e.name}" needs ${String(n)} columns, over D1's ${String(d)}-column limit — split the table, or move the extra fields into one object field`);const t=[...s.map(o=>` ${o}`),...i].join(`,
2
+ `);return`CREATE TABLE IF NOT EXISTS ${a(e.name)} (
3
+ ${t}
4
+ );`},O=e=>`DROP TABLE IF EXISTS ${a(e)};`,C=(e,i,s)=>{const n=`ALTER TABLE ${a(e)} ADD COLUMN ${f(i,{...s,nullable:!0})};`;return s.nullable?n:`${n}
5
+ -- ${a(e)}.${a(i)} is required, so rows written before it read NULL. Backfill them:
6
+ -- UPDATE ${a(e)} SET ${a(i)} = <value> WHERE ${a(i)} IS NULL;`},p=(e,i)=>{const s=i.fields.map(t=>E(t)).join(", ");return`CREATE ${i.unique?"UNIQUE ":""}INDEX IF NOT EXISTS ${c(e,i.name)} ON ${a(e)} (${s});`},S=(e,i)=>`DROP INDEX IF EXISTS ${c(e,i)};`,u=e=>e.field===void 0?void 0:JSON.stringify({...e.field,optional:void 0}),I="https://lunora.sh/docs/concepts/migrations#relaxing-not-null-on-a-d1-table",b=(e,i,s,n,t)=>{const o=u(s),r=u(n);if(s.sqlType!==n.sqlType)t.push({kind:"columnTypeChange",summary:`column type change on ${e}.${i}: ${s.sqlType} → ${n.sqlType} (write SQL manually)`});else if(o!==void 0&&r!==void 0&&o!==r){const l=s.field?.kind===n.field?.kind?`${String(n.field?.kind)} shape/constraints changed`:`${String(s.field?.kind)} → ${String(n.field?.kind)}`;t.push({kind:"columnTypeChange",summary:`column type change on ${e}.${i}: ${l} (write SQL manually)`})}if(s.nullable!==n.nullable){const l=n.nullable?`rebuild the table — ${I}`:"write SQL manually";t.push({kind:"columnTypeChange",summary:`nullability change on ${e}.${i}: ${s.nullable?"NULL":"NOT NULL"} → ${n.nullable?"NULL":"NOT NULL"} (${l})`})}},q=(e,i,s,n,t)=>{for(const[o,r]of Object.entries(s)){const l=i[o];if(l===void 0){n.push({kind:"addColumn",sql:C(e,o,r),summary:`ADD COLUMN ${e}.${o}`});continue}b(e,o,l,r,t)}for(const o of Object.keys(i))s[o]===void 0&&t.push({kind:"dropColumn",summary:`DROP COLUMN ${e}.${o} (SQLite drop-column requires careful migration — write SQL manually)`})},N=(e,i,s,n,t)=>{for(const[o,r]of Object.entries(s)){const l=i[o];if(l===void 0){n.push({kind:"createIndex",sql:p(e,r),summary:`CREATE INDEX ${o} ON ${e}`});continue}(!(l.fields.length===r.fields.length&&l.fields.every((m,$)=>m===r.fields[$]))||l.unique!==r.unique)&&t.push({kind:"indexRename",summary:`index ${o} changed on ${e} — drop+create manually if intentional`})}for(const o of Object.keys(i))s[o]===void 0&&n.push({kind:"dropIndex",sql:S(e,o),summary:`DROP INDEX ${o}`})},D=(e,i,s)=>{s.push({kind:"createTable",sql:g(i),summary:`CREATE TABLE ${e}`});for(const n of Object.values(i.indexes))s.push({kind:"createIndex",sql:p(e,n),summary:`CREATE INDEX ${n.name} ON ${e}`})},w=(e,i)=>{const s=e?.tables??{},n=[],t=[];for(const[o,r]of Object.entries(i.tables)){const l=s[o];if(l===void 0){D(o,r,n);continue}q(o,l.columns,r.columns,n,t),N(o,l.indexes,r.indexes,n,t)}for(const o of Object.keys(s))i.tables[o]===void 0&&n.push({kind:"dropTable",sql:O(o),summary:`DROP TABLE ${o}`});return{empty:n.length===0&&t.length===0,entries:n,unsupported:t}},U=(e,i,s)=>{const n=[`-- Lunora migration: ${e}`,`-- Generated at ${s}`,"-- This file was produced by `lunora migrate generate`. Review carefully before applying.",""];for(const t of i.entries)n.push(`-- ${t.summary}`,t.sql,"");if(i.unsupported.length>0){n.push("-- ---------------------------------------------------------------","-- The following deltas are NOT auto-generated in v0.1.","-- Write the appropriate SQL below by hand:","--");for(const t of i.unsupported)n.push(`-- * ${t.summary}`);n.push("-- ---------------------------------------------------------------","")}return i.empty&&n.push("-- No changes detected. Re-running `lunora migrate generate` will overwrite this file.",""),n.join(`
7
+ `)};export{w as diffSnapshots,C as renderAddColumn,p as renderCreateIndex,g as renderCreateTable,S as renderDropIndex,O as renderDropTable,U as renderMigrationFile,v as validatorKindToSqlType};
@@ -1,4 +1,4 @@
1
- import{existsSync as p,mkdirSync as _,writeFileSync as v,mkdtempSync as x,rmSync as F,readFileSync as U}from"node:fs";import{tmpdir as O}from"node:os";import{discoverMigrations as k,discoverSchema as N,readPackageDependencies as D}from"@lunora/codegen";import{isInteractive as P,promptSelect as G,promptText as j}from"@lunora/config";import{LunoraError as T}from"@lunora/errors";import{join as l}from"@visulima/path";import{Project as w}from"ts-morph";import{r as z,t as L}from"./admin-token-VdUnvnKW.mjs";import{r as q,a as H,n as B}from"./admin-url-DJB8P6Jh.mjs";import{isRefusal as W,exitCodeForStatus as J,EXIT_CODE as s}from"./EXIT_CODE-D_XCixv8.mjs";import{diffSnapshots as V,renderMigrationFile as X}from"./diffSnapshots-WWwx-KvZ.mjs";import Z from"./schemaIrToSnapshot-BjK-0IRo.mjs";import{runExportCommand as K}from"./runExportCommand-slAq5BCF.mjs";import{r as Q}from"./import-BRM1hs7j.mjs";import{readAndLogBody as Y}from"../packem_chunks/runRpcCommand.mjs";const h="__lunora_reproject__",ee=e=>{if(!e.startsWith(h))return;const r=e.slice(h.length);return r===""?void 0:r},re=".snapshot.json",R=/[^\da-z]+/gu,M=(e,r)=>{let t=0,o=e.length;for(;t<o&&e[t]===r;)t+=1;for(;o>t&&e[o-1]===r;)o-=1;return e.slice(t,o)},te=e=>{const r=M(e.toLowerCase().replaceAll(R,"_"),"_");return r===""?"auto":r},oe=e=>{const r=(t,o=2)=>t.toString().padStart(o,"0");return`${String(e.getUTCFullYear())}${r(e.getUTCMonth()+1)}${r(e.getUTCDate())}${r(e.getUTCHours())}${r(e.getUTCMinutes())}${r(e.getUTCSeconds())}`},ne=e=>{if(p(e))try{const r=U(e,"utf8"),t=JSON.parse(r);if(t.version!==1)throw new T("INTERNAL",`unsupported snapshot version: ${t.version}`);return t}catch(r){const t=r instanceof Error?r.message:String(r);throw new T("INTERNAL",`failed to read ${e}: ${t}`,{cause:r})}},De=e=>{const r=e.cwd??process.cwd(),t=l(r,"lunora","schema.ts");if(!p(t)){const m=`schema not found: ${t} — run \`vis generate lunora-table --name=<name>\` to create one`;return e.logger.error(m),{code:s.NOT_FOUND,empty:!0,error:m,migrationFile:""}}const o=new w({skipAddingFilesFromTsConfig:!0}),a=N(o,t),c=Z(a),n=l(r,"lunora","migrations"),i=l(n,re);let d;try{d=ne(i)}catch(m){const S=m instanceof Error?m.message:String(m);return e.logger.error(S),{code:s.USAGE,empty:!0,error:S,migrationFile:""}}const u=V(d,c);if(u.empty)return e.logger.info("no schema changes detected — snapshot is already up to date"),{code:0,empty:!0,migrationFile:""};const f=(e.now??(()=>new Date))(),y=te(e.name??"auto"),$=`${oe(f)}_${y}.sql`,b=l(n,$);_(n,{recursive:!0});const C=X(y,u,f.toISOString());return v(b,C,"utf8"),v(i,`${JSON.stringify(c,void 0,4)}
1
+ import{existsSync as p,mkdirSync as _,writeFileSync as v,mkdtempSync as x,rmSync as F,readFileSync as U}from"node:fs";import{tmpdir as O}from"node:os";import{discoverMigrations as k,discoverSchema as N,readPackageDependencies as D}from"@lunora/codegen";import{isInteractive as P,promptSelect as G,promptText as j}from"@lunora/config";import{LunoraError as T}from"@lunora/errors";import{join as l}from"@visulima/path";import{Project as w}from"ts-morph";import{r as z,t as L}from"./admin-token-VdUnvnKW.mjs";import{r as q,a as H,n as B}from"./admin-url-DJB8P6Jh.mjs";import{isRefusal as W,exitCodeForStatus as J,EXIT_CODE as s}from"./EXIT_CODE-D_XCixv8.mjs";import{diffSnapshots as V,renderMigrationFile as X}from"./diffSnapshots-htqNZT8Q.mjs";import Z from"./schemaIrToSnapshot-BHuNOfgs.mjs";import{runExportCommand as K}from"./runExportCommand-slAq5BCF.mjs";import{r as Q}from"./import-BRM1hs7j.mjs";import{readAndLogBody as Y}from"../packem_chunks/runRpcCommand.mjs";const h="__lunora_reproject__",ee=e=>{if(!e.startsWith(h))return;const r=e.slice(h.length);return r===""?void 0:r},re=".snapshot.json",R=/[^\da-z]+/gu,M=(e,r)=>{let t=0,o=e.length;for(;t<o&&e[t]===r;)t+=1;for(;o>t&&e[o-1]===r;)o-=1;return e.slice(t,o)},te=e=>{const r=M(e.toLowerCase().replaceAll(R,"_"),"_");return r===""?"auto":r},oe=e=>{const r=(t,o=2)=>t.toString().padStart(o,"0");return`${String(e.getUTCFullYear())}${r(e.getUTCMonth()+1)}${r(e.getUTCDate())}${r(e.getUTCHours())}${r(e.getUTCMinutes())}${r(e.getUTCSeconds())}`},ne=e=>{if(p(e))try{const r=U(e,"utf8"),t=JSON.parse(r);if(t.version!==1)throw new T("INTERNAL",`unsupported snapshot version: ${t.version}`);return t}catch(r){const t=r instanceof Error?r.message:String(r);throw new T("INTERNAL",`failed to read ${e}: ${t}`,{cause:r})}},De=e=>{const r=e.cwd??process.cwd(),t=l(r,"lunora","schema.ts");if(!p(t)){const m=`schema not found: ${t} — run \`vis generate lunora-table --name=<name>\` to create one`;return e.logger.error(m),{code:s.NOT_FOUND,empty:!0,error:m,migrationFile:""}}const o=new w({skipAddingFilesFromTsConfig:!0}),a=N(o,t),c=Z(a),n=l(r,"lunora","migrations"),i=l(n,re);let d;try{d=ne(i)}catch(m){const S=m instanceof Error?m.message:String(m);return e.logger.error(S),{code:s.USAGE,empty:!0,error:S,migrationFile:""}}const u=V(d,c);if(u.empty)return e.logger.info("no schema changes detected — snapshot is already up to date"),{code:0,empty:!0,migrationFile:""};const f=(e.now??(()=>new Date))(),y=te(e.name??"auto"),$=`${oe(f)}_${y}.sql`,b=l(n,$);_(n,{recursive:!0});const C=X(y,u,f.toISOString());return v(b,C,"utf8"),v(i,`${JSON.stringify(c,void 0,4)}
2
2
  `,"utf8"),e.logger.success(`wrote ${b}`),u.unsupported.length>0&&e.logger.warn(`${String(u.unsupported.length)} unsupported diff(s) — see the comment block in ${$} and write the SQL manually`),{code:0,empty:!1,migrationFile:b}},ae="migrations.ts",I=/^[A-Za-z_]\w*$/u,ie=new Set(["await","break","case","catch","class","const","continue","debugger","default","delete","do","else","enum","export","extends","false","finally","for","function","if","implements","import","in","instanceof","interface","let","new","null","package","private","protected","public","return","static","super","switch","this","throw","true","try","typeof","var","void","while","with","yield"]),A=/^import\s*\{\s*defineMigration\s*\}\s*from\s*["'](?:@lunora\/server|lunorash\/server)["'][^\n]*$/mu,se=e=>`import { defineMigration } from "${D(e)?.has("lunorash")??!1?"lunorash/server":"@lunora/server"}";`,ce="__lunora_admin__:runMigration",de="__lunora_admin__:migrationStatus",le="/_lunora/migrate",ue=e=>M(e.trim().toLowerCase().replaceAll(R,"-"),"-"),me=e=>e.split("-").filter(r=>r.length>0).map((r,t)=>t===0?r:r.charAt(0).toUpperCase()+r.slice(1)).join(""),ge=e=>{const r=l(e,"lunora","schema.ts");if(!p(r))return[];try{const t=new w({skipAddingFilesFromTsConfig:!0});return N(t,r).tables.map(o=>o.name)}catch{return[]}},fe=async e=>e.length>0?G("Which table does this migration iterate?",e.map(r=>({label:r,value:r}))):j("Target table for the migration: "),he=async(e,r)=>{if(r.table!==void 0)return r.table;const t=r.promptTable??(P()?fe:void 0);if(t===void 0){r.logger.error("migrate create requires a target table when not running interactively — re-run with --table <table>");return}const a=(await t(ge(e)))?.trim();if(a===void 0||a===""){r.logger.error("no table selected — re-run with --table <table>");return}return a},Pe=async e=>{const r=e.cwd??process.cwd(),t=ue(e.name);if(t==="")return e.logger.error(`invalid migration name: "${e.name}" — must contain at least one alphanumeric character`),{code:s.USAGE,file:""};const o=me(t);if(!I.test(o)||ie.has(o))return e.logger.error(`invalid migration name: "${e.name}" derives the export \`${o}\`, which is not a valid identifier — pick a name that starts with a letter and isn't a reserved word`),{code:s.USAGE,file:""};const a=await he(r,e);if(a===void 0)return{code:s.USAGE,file:""};if(!I.test(a))return e.logger.error(`invalid table: "${a}" — must be a valid identifier ([A-Za-z_][A-Za-z0-9_]*)`),{code:s.USAGE,file:""};const c=l(r,"lunora"),n=l(c,ae);let i=p(n)?U(n,"utf8"):"";if(i.includes(`id: "${t}"`)||new RegExp(String.raw`\bexport const ${o}\b`,"u").test(i))return e.logger.error(`a migration with id "${t}" (export \`${o}\`) already exists in ${n}`),{code:s.CONFLICT,file:""};const d=se(r);i.trim()===""?i=`${d}
3
3
  `:A.test(i)?i=i.replace(A,d):i=`${d}
4
4
  ${i}`;const u=`export const ${o} = defineMigration({
@@ -1 +1 @@
1
- import{buildSchemaSnapshot as b,isD1GlobalTable as d}from"@lunora/codegen";import{validatorKindToSqlType as p}from"./diffSnapshots-WWwx-KvZ.mjs";const k=new Set(["any","null"]),f=(n,e)=>{const t=e.kindOf(n);return t!==void 0&&k.has(t)||e.nullableOf(n)?!0:t==="union"&&(e.membersOf(n)??[]).some(l=>f(l,e))},m=n=>n.kind==="optional"&&n.inner?m(n.inner):n.kind==="literal"&&n.literalValue==="null"?"null":n.kind,i=n=>n.kind==="optional"&&n.inner?i(n.inner):n,O=n=>f(n,{kindOf:m,membersOf:e=>i(e).members,nullableOf:e=>i(e).column?.notNull===!1}),N=n=>p(n.kind==="optional"&&n.inner?n.inner.kind:n.kind),T=(n,e)=>n.kind==="optional"||O(n)?!0:e===void 0?!1:e.nullable===!0,h=d,y=n=>{const e={},t=b(n,[]);for(const l of n.tables){if(!h(l))continue;const a={};for(const[o,c]of Object.entries(l.shape)){const s=t.tables[l.name]?.fields[o],r={nullable:T(c,s),sqlType:N(c)};a[o]=s===void 0?r:{...r,field:s}}const u={};for(const o of l.indexes)u[o.name]={fields:[...o.fields],name:o.name,unique:o.unique??!1};e[l.name]={columns:a,indexes:u,name:l.name}}return{tables:e,version:1}};export{y as default};
1
+ import{buildSchemaSnapshot as b,isD1GlobalTable as d}from"@lunora/codegen";import{validatorKindToSqlType as p}from"./diffSnapshots-htqNZT8Q.mjs";const k=new Set(["any","null"]),f=(n,e)=>{const t=e.kindOf(n);return t!==void 0&&k.has(t)||e.nullableOf(n)?!0:t==="union"&&(e.membersOf(n)??[]).some(l=>f(l,e))},m=n=>n.kind==="optional"&&n.inner?m(n.inner):n.kind==="literal"&&n.literalValue==="null"?"null":n.kind,i=n=>n.kind==="optional"&&n.inner?i(n.inner):n,O=n=>f(n,{kindOf:m,membersOf:e=>i(e).members,nullableOf:e=>i(e).column?.notNull===!1}),N=n=>p(n.kind==="optional"&&n.inner?n.inner.kind:n.kind),T=(n,e)=>n.kind==="optional"||O(n)?!0:e===void 0?!1:e.nullable===!0,h=d,y=n=>{const e={},t=b(n,[]);for(const l of n.tables){if(!h(l))continue;const a={};for(const[o,c]of Object.entries(l.shape)){const s=t.tables[l.name]?.fields[o],r={nullable:T(c,s),sqlType:N(c)};a[o]=s===void 0?r:{...r,field:s}}const u={};for(const o of l.indexes)u[o.name]={fields:[...o.fields],name:o.name,unique:o.unique??!1};e[l.name]={columns:a,indexes:u,name:l.name}}return{tables:e,version:1}};export{y as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.387",
3
+ "version": "1.0.0-alpha.389",
4
4
  "description": "The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -52,16 +52,16 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@bomb.sh/tab": "0.0.22",
55
- "@lunora/advisor": "1.0.0-alpha.209",
56
- "@lunora/bindings": "1.0.0-alpha.106",
57
- "@lunora/codegen": "1.0.0-alpha.294",
58
- "@lunora/config": "1.0.0-alpha.341",
55
+ "@lunora/advisor": "1.0.0-alpha.210",
56
+ "@lunora/bindings": "1.0.0-alpha.107",
57
+ "@lunora/codegen": "1.0.0-alpha.296",
58
+ "@lunora/config": "1.0.0-alpha.343",
59
59
  "@lunora/container": "1.0.0-alpha.73",
60
- "@lunora/d1": "1.0.0-alpha.172",
60
+ "@lunora/d1": "1.0.0-alpha.173",
61
61
  "@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
62
- "@lunora/mcp": "1.0.0-alpha.232",
63
- "@lunora/runtime": "1.0.0-alpha.192",
64
- "@lunora/seed": "1.0.0-alpha.212",
62
+ "@lunora/mcp": "1.0.0-alpha.233",
63
+ "@lunora/runtime": "1.0.0-alpha.193",
64
+ "@lunora/seed": "1.0.0-alpha.213",
65
65
  "@visulima/cerebro": "3.1.2",
66
66
  "@visulima/error": "6.1.0",
67
67
  "@visulima/fs": "6.0.29",
@@ -1,5 +0,0 @@
1
- import{quoteIdentifier as a,sqlAffinityForKind as $,frameworkColumnDdl as y,OCC_VERSION_COLUMN as T,MAX_D1_TABLE_COLUMNS as d,columnRef as g,physicalIndexName as c}from"@lunora/d1/dialect";import{LunoraError as E}from"@lunora/errors";const v=e=>$(e),f=(e,i)=>{const o=[a(e),i.sqlType];return i.nullable||o.push("NOT NULL"),o.join(" ")},L=e=>{const i=Object.entries(e.columns).map(([s,r])=>` ${f(s,r)}`),o=[...y(),`${a(T)} INTEGER`],n=o.length+i.length;if(n>d)throw new E("VALIDATION_ERROR",`table "${e.name}" needs ${String(n)} columns, over D1's ${String(d)}-column limit — split the table, or move the extra fields into one object field`);const t=[...o.map(s=>` ${s}`),...i].join(`,
2
- `);return`CREATE TABLE IF NOT EXISTS ${a(e.name)} (
3
- ${t}
4
- );`},O=e=>`DROP TABLE IF EXISTS ${a(e)};`,C=(e,i,o)=>`ALTER TABLE ${a(e)} ADD COLUMN ${f(i,o)};`,m=(e,i)=>{const o=i.fields.map(t=>g(t)).join(", ");return`CREATE ${i.unique?"UNIQUE ":""}INDEX IF NOT EXISTS ${c(e,i.name)} ON ${a(e)} (${o});`},N=(e,i)=>`DROP INDEX IF EXISTS ${c(e,i)};`,u=e=>e.field===void 0?void 0:JSON.stringify({...e.field,optional:void 0}),I="https://lunora.sh/docs/concepts/migrations#relaxing-not-null-on-a-d1-table",S=(e,i,o,n,t)=>{const s=u(o),r=u(n);if(o.sqlType!==n.sqlType)t.push({kind:"columnTypeChange",summary:`column type change on ${e}.${i}: ${o.sqlType} → ${n.sqlType} (write SQL manually)`});else if(s!==void 0&&r!==void 0&&s!==r){const l=o.field?.kind===n.field?.kind?`${String(n.field?.kind)} shape/constraints changed`:`${String(o.field?.kind)} → ${String(n.field?.kind)}`;t.push({kind:"columnTypeChange",summary:`column type change on ${e}.${i}: ${l} (write SQL manually)`})}if(o.nullable!==n.nullable){const l=n.nullable?`rebuild the table — ${I}`:"write SQL manually";t.push({kind:"columnTypeChange",summary:`nullability change on ${e}.${i}: ${o.nullable?"NULL":"NOT NULL"} → ${n.nullable?"NULL":"NOT NULL"} (${l})`})}},b=(e,i,o,n,t)=>{for(const[s,r]of Object.entries(o)){const l=i[s];if(l===void 0){n.push({kind:"addColumn",sql:C(e,s,r),summary:`ADD COLUMN ${e}.${s}`});continue}S(e,s,l,r,t)}for(const s of Object.keys(i))o[s]===void 0&&t.push({kind:"dropColumn",summary:`DROP COLUMN ${e}.${s} (SQLite drop-column requires careful migration — write SQL manually)`})},q=(e,i,o,n,t)=>{for(const[s,r]of Object.entries(o)){const l=i[s];if(l===void 0){n.push({kind:"createIndex",sql:m(e,r),summary:`CREATE INDEX ${s} ON ${e}`});continue}(!(l.fields.length===r.fields.length&&l.fields.every((p,h)=>p===r.fields[h]))||l.unique!==r.unique)&&t.push({kind:"indexRename",summary:`index ${s} changed on ${e} — drop+create manually if intentional`})}for(const s of Object.keys(i))o[s]===void 0&&n.push({kind:"dropIndex",sql:N(e,s),summary:`DROP INDEX ${s}`})},D=(e,i,o)=>{o.push({kind:"createTable",sql:L(i),summary:`CREATE TABLE ${e}`});for(const n of Object.values(i.indexes))o.push({kind:"createIndex",sql:m(e,n),summary:`CREATE INDEX ${n.name} ON ${e}`})},w=(e,i)=>{const o=e?.tables??{},n=[],t=[];for(const[s,r]of Object.entries(i.tables)){const l=o[s];if(l===void 0){D(s,r,n);continue}b(s,l.columns,r.columns,n,t),q(s,l.indexes,r.indexes,n,t)}for(const s of Object.keys(o))i.tables[s]===void 0&&n.push({kind:"dropTable",sql:O(s),summary:`DROP TABLE ${s}`});return{empty:n.length===0&&t.length===0,entries:n,unsupported:t}},x=(e,i,o)=>{const n=[`-- Lunora migration: ${e}`,`-- Generated at ${o}`,"-- This file was produced by `lunora migrate generate`. Review carefully before applying.",""];for(const t of i.entries)n.push(`-- ${t.summary}`,t.sql,"");if(i.unsupported.length>0){n.push("-- ---------------------------------------------------------------","-- The following deltas are NOT auto-generated in v0.1.","-- Write the appropriate SQL below by hand:","--");for(const t of i.unsupported)n.push(`-- * ${t.summary}`);n.push("-- ---------------------------------------------------------------","")}return i.empty&&n.push("-- No changes detected. Re-running `lunora migrate generate` will overwrite this file.",""),n.join(`
5
- `)};export{w as diffSnapshots,C as renderAddColumn,m as renderCreateIndex,L as renderCreateTable,N as renderDropIndex,O as renderDropTable,x as renderMigrationFile,v as validatorKindToSqlType};