@lunora/cli 1.0.0-alpha.467 → 1.0.0-alpha.469

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.
@@ -1,13 +1,13 @@
1
- import{existsSync as O,readFileSync as k}from"node:fs";import{writeFile as I,mkdir as U}from"node:fs/promises";import{parseSchema as B,applyAdditiveEdit as F,projectUsesUmbrella as Y}from"@lunora/config";import{join as _}from"@visulima/path";import{d as q}from"../packem_shared/command-BpMv2n0o.mjs";import{EXIT_CODE as L}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{createRequire as P}from"node:module";import{LunoraError as N}from"@lunora/errors";import{addArgsFor as H,detectPackageManager as K}from"@lunora/config/cli";const j=/^\//,G=(e,r)=>{try{const{args:n,command:t}=H(K(e),[r],{dev:!0});return`${t} ${n.join(" ")}`}catch{return`<your-package-manager> add -D ${r}`}},T=(e,r,n)=>{const t=P(_(n,"noop.cjs"));try{t.resolve(e)}catch{throw new N("INTERNAL",`\`lunora introspect\` needs the \`${r}\` driver to read this database, and it isn't installed.
1
+ import{existsSync as O,readFileSync as k}from"node:fs";import{writeFile as I,mkdir as U}from"node:fs/promises";import{parseSchema as B,applyAdditiveEdit as Y,projectUsesUmbrella as q}from"@lunora/config";import{join as E}from"@visulima/path";import{d as F}from"../packem_shared/command-BpMv2n0o.mjs";import{EXIT_CODE as T}from"../packem_shared/EXIT_CODE-DQVhSE-1.mjs";import{createRequire as P}from"node:module";import{LunoraError as S}from"@lunora/errors";import{addArgsFor as H,detectPackageManager as K}from"@lunora/config/cli";import{l as f}from"../packem_shared/js-literal-FWLi94Lh.mjs";const j=/^\//,G=(e,r)=>{try{const{args:n,command:t}=H(K(e),[r],{dev:!0});return`${t} ${n.join(" ")}`}catch{return`<your-package-manager> add -D ${r}`}},L=(e,r,n)=>{const t=P(E(n,"noop.cjs"));try{t.resolve(e)}catch{throw new S("INTERNAL",`\`lunora introspect\` needs the \`${r}\` driver to read this database, and it isn't installed.
2
2
 
3
3
  Install it in your project:
4
4
 
5
- ${G(n,r)}`)}return t(e)},Q=e=>{const r=e.slice(0,Math.max(0,e.indexOf(":"))).toLowerCase();if(r==="postgres"||r==="postgresql")return"postgres";if(r==="mysql"||r==="mariadb")return"mysql";throw new N("INTERNAL",`Unrecognised database URL scheme \`${r}\`. Expected a \`postgres://\`, \`postgresql://\`, \`mysql://\`, or \`mariadb://\` connection string.`)},J=e=>{try{const r=new URL(e).pathname.replace(j,"");return r===""?void 0:decodeURIComponent(r)}catch{return}},W=async(e,r,n,t=process.cwd())=>{if(r==="postgres"){const m=T("pg","pg",t).Client,u=new m({connectionString:e});return await u.connect(),{close:async()=>{await u.end()},execute:async(l,p)=>(await u.query(l,[...p])).rows,schema:n??"public"}}const a=T("mysql2/promise","mysql2",t).createConnection,o=await a(e),i=n??J(e);if(i===void 0)throw new N("INTERNAL","A MySQL connection string must name a database (`mysql://host/<database>`), or pass `--schema`.");return{close:async()=>{await o.end()},execute:async(c,m)=>{const[u]=await o.execute(c,[...m]);return Array.isArray(u)?u:[]},schema:i}},h=new Set(["_creationTime","_id"]),X={bigint:"v.bigint()",bigserial:"v.bigint()",bit:"v.string()",bool:"v.boolean()",boolean:"v.boolean()",box:"v.string()",bpchar:"v.string()",bytea:"v.bytes()",char:"v.string()",character:"v.string()","character varying":"v.string()",cidr:"v.string()",circle:"v.string()",citext:"v.string()",date:"v.date()","double precision":"v.number()",float4:"v.number()",float8:"v.number()",inet:"v.string()",int:"v.number()",int2:"v.number()",int4:"v.number()",int8:"v.bigint()",integer:"v.number()",interval:"v.string()",json:"v.any()",jsonb:"v.any()",line:"v.string()",lseg:"v.string()",macaddr:"v.string()",money:"v.number()",name:"v.string()",numeric:"v.number()",path:"v.string()",point:"v.string()",polygon:"v.string()",real:"v.number()",serial:"v.number()",smallint:"v.number()",smallserial:"v.number()",text:"v.string()",time:"v.string()","time with time zone":"v.string()","time without time zone":"v.string()",timestamp:"v.timestamp()","timestamp with time zone":"v.timestamp()","timestamp without time zone":"v.timestamp()",timestamptz:"v.timestamp()",tsquery:"v.string()",tsvector:"v.string()",uuid:"v.string()",varbit:"v.string()",varchar:"v.string()",xml:"v.string()"},z={bigint:"v.bigint()",binary:"v.bytes()",bit:"v.number()",blob:"v.bytes()",char:"v.string()",date:"v.date()",datetime:"v.timestamp()",decimal:"v.number()",double:"v.number()",enum:"v.string()",float:"v.number()",int:"v.number()",integer:"v.number()",json:"v.any()",longblob:"v.bytes()",longtext:"v.string()",mediumblob:"v.bytes()",mediumint:"v.number()",mediumtext:"v.string()",numeric:"v.number()",set:"v.string()",smallint:"v.number()",text:"v.string()",time:"v.string()",timestamp:"v.timestamp()",tinyblob:"v.bytes()",tinyint:"v.number()",tinytext:"v.string()",varbinary:"v.bytes()",varchar:"v.string()",year:"v.number()"},w=(e,r)=>{const t=(r==="postgres"?X:z)[e.dataType],s=e.references===void 0?t:`v.id(${JSON.stringify(e.references.table)})`;let a=s??"v.any()";for(let o=0;o<e.arrayDepth;o+=1)a=`v.array(${a})`;return e.nullable&&(a=`v.optional(${a})`),{expression:a,known:s!==void 0}},M=/^[A-Z_$][\w$]*$/i,Z=/[^\dA-Z]+(.)?/gi,V=/^\d+/,ee=/[^\w-]/g,ne=/[\u003C\u003E\u002F\u2028\u2029]/g,te={"/":String.raw`\u002F`,"\u2028":String.raw`\u2028`,"\u2029":String.raw`\u2029`,"<":String.raw`\u003C`,">":String.raw`\u003E`},f=e=>JSON.stringify(e).replaceAll(ne,r=>te[r]??r),S=e=>M.test(e)?e:f(e),re=e=>M.test(e)?`.${e}`:`[${f(e)}]`,x=e=>e.replaceAll("*/",String.raw`*\/`),ae=e=>{const r=e.replaceAll(ee,"_");return r===""?"table":r},se=e=>{const n=e.replaceAll(Z,(t,s)=>s?.toUpperCase()??"").replace(V,"");return n===""?"table":`${n.charAt(0).toLowerCase()}${n.slice(1)}`},C=e=>e.columns.filter(r=>!h.has(r.name)),oe=e=>{const r=new Set(e.primaryKey);for(const n of e.indexes)for(const t of n.columns)r.add(t);for(const n of e.columns)n.references!==void 0&&r.add(n.name);return[...r].filter(n=>!h.has(n))},D=(e,r,n,t)=>e.references===void 0||r.has(e.references.table)?e:(t.push(`${n}.${e.name}: references \`${e.references.table}\`, which isn't in the generated schema — emitted as a plain column instead of \`v.id(...)\`.`),{arrayDepth:e.arrayDepth,dataType:e.dataType,name:e.name,nullable:e.nullable}),ie=(e,r,n,t)=>{const s=[];for(const o of e.columns){if(h.has(o.name)){t.push(`${e.name}.${o.name}: skipped — \`${o.name}\` is a Lunora system column. Rename it in the source database or map it by hand.`);continue}const i=D(o,n,e.name,t),{expression:c,known:m}=w(i,r);!m&&i.references===void 0&&(t.push(`${e.name}.${o.name}: no mapping for SQL type \`${o.dataType}\` — emitted as \`v.any()\`.`),s.push(` // TODO: \`${x(o.dataType)}\` has no direct validator; narrow this.`)),s.push(` ${S(o.name)}: ${c},`)}const a=[' .global({ backend: "hyperdrive" })'];if(e.primaryKey.length>0&&e.primaryKey.every(o=>!h.has(o))){const o=e.primaryKey.map(i=>f(i)).join(", ");a.push(` .index(${f(`by_${e.primaryKey.join("_")}`)}, [${o}], { unique: true })`)}for(const o of e.indexes){const i=o.columns.filter(u=>!h.has(u));if(i.length===0)continue;const c=i.map(u=>f(u)).join(", "),m=o.unique?", { unique: true }":"";a.push(` .index(${f(o.name)}, [${c}]${m})`)}return` ${S(e.name)}: defineTable({
5
+ ${G(n,r)}`)}return t(e)},Q=e=>{const r=e.slice(0,Math.max(0,e.indexOf(":"))).toLowerCase();if(r==="postgres"||r==="postgresql")return"postgres";if(r==="mysql"||r==="mariadb")return"mysql";throw new S("INTERNAL",`Unrecognised database URL scheme \`${r}\`. Expected a \`postgres://\`, \`postgresql://\`, \`mysql://\`, or \`mariadb://\` connection string.`)},W=e=>{try{const r=new URL(e).pathname.replace(j,"");return r===""?void 0:decodeURIComponent(r)}catch{return}},J=async(e,r,n,t=process.cwd())=>{if(r==="postgres"){const m=L("pg","pg",t).Client,u=new m({connectionString:e});return await u.connect(),{close:async()=>{await u.end()},execute:async(l,p)=>(await u.query(l,[...p])).rows,schema:n??"public"}}const a=L("mysql2/promise","mysql2",t).createConnection,o=await a(e),i=n??W(e);if(i===void 0)throw new S("INTERNAL","A MySQL connection string must name a database (`mysql://host/<database>`), or pass `--schema`.");return{close:async()=>{await o.end()},execute:async(c,m)=>{const[u]=await o.execute(c,[...m]);return Array.isArray(u)?u:[]},schema:i}},h=new Set(["_creationTime","_id"]),X={bigint:"v.bigint()",bigserial:"v.bigint()",bit:"v.string()",bool:"v.boolean()",boolean:"v.boolean()",box:"v.string()",bpchar:"v.string()",bytea:"v.bytes()",char:"v.string()",character:"v.string()","character varying":"v.string()",cidr:"v.string()",circle:"v.string()",citext:"v.string()",date:"v.date()","double precision":"v.number()",float4:"v.number()",float8:"v.number()",inet:"v.string()",int:"v.number()",int2:"v.number()",int4:"v.number()",int8:"v.bigint()",integer:"v.number()",interval:"v.string()",json:"v.any()",jsonb:"v.any()",line:"v.string()",lseg:"v.string()",macaddr:"v.string()",money:"v.number()",name:"v.string()",numeric:"v.number()",path:"v.string()",point:"v.string()",polygon:"v.string()",real:"v.number()",serial:"v.number()",smallint:"v.number()",smallserial:"v.number()",text:"v.string()",time:"v.string()","time with time zone":"v.string()","time without time zone":"v.string()",timestamp:"v.timestamp()","timestamp with time zone":"v.timestamp()","timestamp without time zone":"v.timestamp()",timestamptz:"v.timestamp()",tsquery:"v.string()",tsvector:"v.string()",uuid:"v.string()",varbit:"v.string()",varchar:"v.string()",xml:"v.string()"},z={bigint:"v.bigint()",binary:"v.bytes()",bit:"v.number()",blob:"v.bytes()",char:"v.string()",date:"v.date()",datetime:"v.timestamp()",decimal:"v.number()",double:"v.number()",enum:"v.string()",float:"v.number()",int:"v.number()",integer:"v.number()",json:"v.any()",longblob:"v.bytes()",longtext:"v.string()",mediumblob:"v.bytes()",mediumint:"v.number()",mediumtext:"v.string()",numeric:"v.number()",set:"v.string()",smallint:"v.number()",text:"v.string()",time:"v.string()",timestamp:"v.timestamp()",tinyblob:"v.bytes()",tinyint:"v.number()",tinytext:"v.string()",varbinary:"v.bytes()",varchar:"v.string()",year:"v.number()"},w=(e,r)=>{const t=(r==="postgres"?X:z)[e.dataType],s=e.references===void 0?t:`v.id(${JSON.stringify(e.references.table)})`;let a=s??"v.any()";for(let o=0;o<e.arrayDepth;o+=1)a=`v.array(${a})`;return e.nullable&&(a=`v.optional(${a})`),{expression:a,known:s!==void 0}},M=/^[A-Z_$][\w$]*$/i,Z=/[^\dA-Z]+(.)?/gi,V=/^\d+/,ee=/[^\w-]/g,N=e=>M.test(e)?e:f(e),ne=e=>M.test(e)?`.${e}`:`[${f(e)}]`,x=e=>e.replaceAll("*/",String.raw`*\/`),te=e=>{const r=e.replaceAll(ee,"_");return r===""?"table":r},re=e=>{const n=e.replaceAll(Z,(t,s)=>s?.toUpperCase()??"").replace(V,"");return n===""?"table":`${n.charAt(0).toLowerCase()}${n.slice(1)}`},D=e=>e.columns.filter(r=>!h.has(r.name)),ae=e=>{const r=new Set(e.primaryKey);for(const n of e.indexes)for(const t of n.columns)r.add(t);for(const n of e.columns)n.references!==void 0&&r.add(n.name);return[...r].filter(n=>!h.has(n))},C=(e,r,n,t)=>e.references===void 0||r.has(e.references.table)?e:(t.push(`${n}.${e.name}: references \`${e.references.table}\`, which isn't in the generated schema — emitted as a plain column instead of \`v.id(...)\`.`),{arrayDepth:e.arrayDepth,dataType:e.dataType,name:e.name,nullable:e.nullable}),se=(e,r,n,t)=>{const s=[];for(const o of e.columns){if(h.has(o.name)){t.push(`${e.name}.${o.name}: skipped — \`${o.name}\` is a Lunora system column. Rename it in the source database or map it by hand.`);continue}const i=C(o,n,e.name,t),{expression:c,known:m}=w(i,r);!m&&i.references===void 0&&(t.push(`${e.name}.${o.name}: no mapping for SQL type \`${o.dataType}\` — emitted as \`v.any()\`.`),s.push(` // TODO: \`${x(o.dataType)}\` has no direct validator; narrow this.`)),s.push(` ${N(o.name)}: ${c},`)}const a=[' .global({ backend: "hyperdrive" })'];if(e.primaryKey.length>0&&e.primaryKey.every(o=>!h.has(o))){const o=e.primaryKey.map(i=>f(i)).join(", ");a.push(` .index(${f(`by_${e.primaryKey.join("_")}`)}, [${o}], { unique: true })`)}for(const o of e.indexes){const i=o.columns.filter(u=>!h.has(u));if(i.length===0)continue;const c=i.map(u=>f(u)).join(", "),m=o.unique?", { unique: true }":"";a.push(` .index(${f(o.name)}, [${c}]${m})`)}return` ${N(e.name)}: defineTable({
6
6
  ${s.join(`
7
7
  `)}
8
8
  })
9
9
  ${a.join(`
10
- `)},`},ce=(e,r,n)=>{const t=new Set(e.tables.map(a=>a.name)),s=e.tables.map(a=>ie(a,e.dialect,t,n)).join(`
10
+ `)},`},oe=(e,r,n)=>{const t=new Set(e.tables.map(a=>a.name)),s=e.tables.map(a=>se(a,e.dialect,t,n)).join(`
11
11
  `);return`/**
12
12
  * Generated by \`lunora introspect\` from an existing ${e.dialect==="postgres"?"Postgres":"MySQL"} database.
13
13
  *
@@ -22,7 +22,7 @@ import { defineSchema, defineTable, v } from "${r.serverImport}";
22
22
  export default defineSchema({
23
23
  ${s}
24
24
  });
25
- `},me=(e,r,n,t=new Set([e.name]),s=[])=>{const a=se(e.name),o=oe(e),i=new Map(C(e).map(l=>[l.name,l])),c=o.map(l=>{const p=i.get(l);if(p===void 0)return;const v=D({...p,nullable:!1},t,e.name,[]),{expression:d,known:A}=w(v,r);return!A&&v.references===void 0&&s.push(`${e.name}.${l}: filter falls back to \`v.any()\` (no mapping for SQL type \`${p.dataType}\`).`),` ${S(l)}: ${d},`}).filter(l=>l!==void 0),m=[f("_creationTime"),...o.map(l=>f(l))].join(", "),u=`ctx.db${re(e.name)}`;return`/**
25
+ `},ie=(e,r,n,t=new Set([e.name]),s=[])=>{const a=re(e.name),o=ae(e),i=new Map(D(e).map(l=>[l.name,l])),c=o.map(l=>{const p=i.get(l);if(p===void 0)return;const v=C({...p,nullable:!1},t,e.name,[]),{expression:d,known:A}=w(v,r);return!A&&v.references===void 0&&s.push(`${e.name}.${l}: filter falls back to \`v.any()\` (no mapping for SQL type \`${p.dataType}\`).`),` ${N(l)}: ${d},`}).filter(l=>l!==void 0),m=[f("_creationTime"),...o.map(l=>f(l))].join(", "),u=`ctx.db${ne(e.name)}`;return`/**
26
26
  * Generated by \`lunora introspect\` for the \`${x(e.name)}\` table — a
27
27
  * starting point you own and edit.
28
28
  *
@@ -50,26 +50,26 @@ ${c.join(`
50
50
  export const list = c.query.input(${a}List.args).query(({ args, ctx }) => ${u}.findMany(${a}List.toQueryArgs(args)));
51
51
 
52
52
  export const get = c.query.input({ id: v.id(${f(e.name)}) }).query(({ args, ctx }) => ${u}.get(args.id));
53
- `},le=(e,r)=>{const n=[],t=new Set(e.tables.map(a=>a.name)),s=[{contents:ce(e,r,n),path:"schema.ts"}];if(r.procedures){const a=new Map([["schema","the generated schema module"]]);for(const o of e.tables){if(C(o).length===0){n.push(`${o.name}: no usable columns — procedure module skipped.`);continue}const i=ae(o.name);i!==o.name&&n.push(`${o.name}: written as \`${i}.ts\` — the table name isn't usable as a filename.`);const c=a.get(i);if(c!==void 0){n.push(`${o.name}: procedure module skipped — its filename \`${i}.ts\` collides with table \`${c}\`.`);continue}a.set(i,o.name),s.push({contents:me(o,e.dialect,r,t,n),path:`${i}.ts`})}}return{files:s,warnings:n}},y=/^[A-Z_$][\w$]*$/i,ue=e=>e.startsWith("v.optional(")&&e.endsWith(")")?e.slice(11,-1):e,de=(e,r,n)=>{const t=[],s=new Set(r?.columns.map(a=>a.name));for(const a of e.columns){if(h.has(a.name)||s.has(a.name))continue;if(!y.test(a.name)){n.warnings.push(`${e.name}.${a.name}: skipped — column names must be bare identifiers to merge.`);continue}const o=a.references!==void 0&&!n.present.has(a.references.table),{expression:i}=w(o?{...a,references:void 0}:a,n.dialect);t.push({column:a.name,kind:"addOptionalColumn",table:e.name,validator:ue(i)}),!a.nullable&&r!==void 0&&n.warnings.push(`${e.name}.${a.name}: added as \`v.optional(...)\` — a required column on an existing table needs a backfill migration.`)}return t},pe=(e,r,n)=>{const t=[],s=new Set(r?.indexes.map(a=>a.name));for(const a of e.indexes){const o=a.columns.filter(i=>!h.has(i));if(!(s.has(a.name)||o.length===0)){if(!y.test(a.name)||!o.every(i=>y.test(i))){n.push(`${e.name}: index \`${a.name}\` skipped — index and column names must be bare identifiers to merge.`);continue}t.push({fields:o,kind:"addIndex",name:a.name,table:e.name,...a.unique?{unique:!0}:{}})}}return t},ge=(e,r,n)=>{const t=[],s=[],a=new Map(r.map(i=>[i.name,i])),o=new Set([...a.keys(),...e.tables.map(i=>i.name)]);for(const i of e.tables){if(!y.test(i.name)){s.push(`${i.name}: skipped — merging into an existing schema needs a table name that is a bare identifier.`);continue}const c=a.get(i.name);c===void 0&&t.push({global:{backend:"hyperdrive"},kind:"addTable",table:i.name}),t.push(...de(i,c,{dialect:n,present:o,warnings:s}),...pe(i,c,s))}return{edits:t,warnings:s}},fe=(e,r,n)=>{const t=B(e);if(!t.ok)return{applied:0,warnings:[`lunora/schema.ts could not be parsed (${t.reason}) — leave it alone and merge by hand, or pass --force.`]};const s=ge(r,t.tables,n),a=[...s.warnings];let o=e,i=0;for(const c of s.edits){const m=F(o,c);m.ok?(o=m.text,i+=1):a.push(`${c.table}: skipped one edit (${m.reason}).`)}return{applied:i,warnings:a,...i===0?{}:{text:o}}},g=(e,r)=>{const n=e[r]??e[r.toLowerCase()]??e[r.toUpperCase()];return typeof n=="string"?n:typeof n=="number"||typeof n=="bigint"||typeof n=="boolean"?String(n):""},ve=`
53
+ `},ce=(e,r)=>{const n=[],t=new Set(e.tables.map(a=>a.name)),s=[{contents:oe(e,r,n),path:"schema.ts"}];if(r.procedures){const a=new Map([["schema","the generated schema module"]]);for(const o of e.tables){if(D(o).length===0){n.push(`${o.name}: no usable columns — procedure module skipped.`);continue}const i=te(o.name);i!==o.name&&n.push(`${o.name}: written as \`${i}.ts\` — the table name isn't usable as a filename.`);const c=a.get(i);if(c!==void 0){n.push(`${o.name}: procedure module skipped — its filename \`${i}.ts\` collides with table \`${c}\`.`);continue}a.set(i,o.name),s.push({contents:ie(o,e.dialect,r,t,n),path:`${i}.ts`})}}return{files:s,warnings:n}},y=/^[A-Z_$][\w$]*$/i,me=e=>e.startsWith("v.optional(")&&e.endsWith(")")?e.slice(11,-1):e,le=(e,r,n)=>{const t=[],s=new Set(r?.columns.map(a=>a.name));for(const a of e.columns){if(h.has(a.name)||s.has(a.name))continue;if(!y.test(a.name)){n.warnings.push(`${e.name}.${a.name}: skipped — column names must be bare identifiers to merge.`);continue}const o=a.references!==void 0&&!n.present.has(a.references.table),{expression:i}=w(o?{...a,references:void 0}:a,n.dialect);t.push({column:a.name,kind:"addOptionalColumn",table:e.name,validator:me(i)}),!a.nullable&&r!==void 0&&n.warnings.push(`${e.name}.${a.name}: added as \`v.optional(...)\` — a required column on an existing table needs a backfill migration.`)}return t},ue=(e,r,n)=>{const t=[],s=new Set(r?.indexes.map(a=>a.name));for(const a of e.indexes){const o=a.columns.filter(i=>!h.has(i));if(!(s.has(a.name)||o.length===0)){if(!y.test(a.name)||!o.every(i=>y.test(i))){n.push(`${e.name}: index \`${a.name}\` skipped — index and column names must be bare identifiers to merge.`);continue}t.push({fields:o,kind:"addIndex",name:a.name,table:e.name,...a.unique?{unique:!0}:{}})}}return t},de=(e,r,n)=>{const t=[],s=[],a=new Map(r.map(i=>[i.name,i])),o=new Set([...a.keys(),...e.tables.map(i=>i.name)]);for(const i of e.tables){if(!y.test(i.name)){s.push(`${i.name}: skipped — merging into an existing schema needs a table name that is a bare identifier.`);continue}const c=a.get(i.name);c===void 0&&t.push({global:{backend:"hyperdrive"},kind:"addTable",table:i.name}),t.push(...le(i,c,{dialect:n,present:o,warnings:s}),...ue(i,c,s))}return{edits:t,warnings:s}},pe=(e,r,n)=>{const t=B(e);if(!t.ok)return{applied:0,warnings:[`lunora/schema.ts could not be parsed (${t.reason}) — leave it alone and merge by hand, or pass --force.`]};const s=de(r,t.tables,n),a=[...s.warnings];let o=e,i=0;for(const c of s.edits){const m=Y(o,c);m.ok?(o=m.text,i+=1):a.push(`${c.table}: skipped one edit (${m.reason}).`)}return{applied:i,warnings:a,...i===0?{}:{text:o}}},g=(e,r)=>{const n=e[r]??e[r.toLowerCase()]??e[r.toUpperCase()];return typeof n=="string"?n:typeof n=="number"||typeof n=="bigint"||typeof n=="boolean"?String(n):""},ge=`
54
54
  SELECT c.table_name, c.column_name, c.is_nullable, c.data_type, c.udt_name
55
55
  FROM information_schema.columns c
56
56
  JOIN information_schema.tables t
57
57
  ON t.table_schema = c.table_schema AND t.table_name = c.table_name
58
58
  WHERE c.table_schema = $1 AND t.table_type = 'BASE TABLE'
59
- ORDER BY c.table_name, c.ordinal_position`,be=`
59
+ ORDER BY c.table_name, c.ordinal_position`,fe=`
60
60
  SELECT tc.table_name, kcu.column_name
61
61
  FROM information_schema.table_constraints tc
62
62
  JOIN information_schema.key_column_usage kcu
63
63
  ON kcu.constraint_name = tc.constraint_name AND kcu.table_schema = tc.table_schema
64
64
  WHERE tc.table_schema = $1 AND tc.constraint_type = 'PRIMARY KEY'
65
- ORDER BY tc.table_name, kcu.ordinal_position`,he=`
65
+ ORDER BY tc.table_name, kcu.ordinal_position`,ve=`
66
66
  SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table, ccu.column_name AS foreign_column
67
67
  FROM information_schema.table_constraints tc
68
68
  JOIN information_schema.key_column_usage kcu
69
69
  ON kcu.constraint_name = tc.constraint_name AND kcu.table_schema = tc.table_schema
70
70
  JOIN information_schema.constraint_column_usage ccu
71
71
  ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema
72
- WHERE tc.table_schema = $1 AND tc.constraint_type = 'FOREIGN KEY'`,Ee=`
72
+ WHERE tc.table_schema = $1 AND tc.constraint_type = 'FOREIGN KEY'`,be=`
73
73
  SELECT t.relname AS table_name, i.relname AS index_name, ix.indisunique AS is_unique, a.attname AS column_name
74
74
  FROM pg_class t
75
75
  JOIN pg_index ix ON t.oid = ix.indrelid
@@ -78,22 +78,22 @@ export const get = c.query.input({ id: v.id(${f(e.name)}) }).query(({ args, ctx
78
78
  CROSS JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS k(attnum, ord)
79
79
  JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
80
80
  WHERE n.nspname = $1 AND t.relkind = 'r' AND NOT ix.indisprimary
81
- ORDER BY t.relname, i.relname, k.ord`,_e=`
81
+ ORDER BY t.relname, i.relname, k.ord`,he=`
82
82
  SELECT c.TABLE_NAME, c.COLUMN_NAME, c.IS_NULLABLE, c.DATA_TYPE
83
83
  FROM information_schema.COLUMNS c
84
84
  JOIN information_schema.TABLES t
85
85
  ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.TABLE_NAME = c.TABLE_NAME
86
86
  WHERE c.TABLE_SCHEMA = ? AND t.TABLE_TYPE = 'BASE TABLE'
87
- ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION`,ye=`
87
+ ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION`,_e=`
88
88
  SELECT TABLE_NAME, COLUMN_NAME
89
89
  FROM information_schema.KEY_COLUMN_USAGE
90
90
  WHERE TABLE_SCHEMA = ? AND CONSTRAINT_NAME = 'PRIMARY'
91
- ORDER BY TABLE_NAME, ORDINAL_POSITION`,Ae=`
91
+ ORDER BY TABLE_NAME, ORDINAL_POSITION`,Ee=`
92
92
  SELECT TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME AS foreign_table, REFERENCED_COLUMN_NAME AS foreign_column
93
93
  FROM information_schema.KEY_COLUMN_USAGE
94
- WHERE TABLE_SCHEMA = ? AND REFERENCED_TABLE_NAME IS NOT NULL`,Se=`
94
+ WHERE TABLE_SCHEMA = ? AND REFERENCED_TABLE_NAME IS NOT NULL`,ye=`
95
95
  SELECT TABLE_NAME, INDEX_NAME AS index_name, NON_UNIQUE, COLUMN_NAME
96
96
  FROM information_schema.STATISTICS
97
97
  WHERE TABLE_SCHEMA = ? AND INDEX_NAME <> 'PRIMARY'
98
- ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX`,E=e=>{const r=new Map;for(const n of e){const t=g(n,"table_name"),s=r.get(t);s===void 0?r.set(t,[n]):s.push(n)}return r},Ne=(e,r)=>{const n=g(e,"data_type").toLowerCase();if(r!=="postgres")return{arrayDepth:0,dataType:n};const t=g(e,"udt_name").toLowerCase();return n==="array"&&t.startsWith("_")?{arrayDepth:1,dataType:t.slice(1)}:{arrayDepth:0,dataType:t===""?n:t}},we=(e,r)=>{const n=new Map;for(const t of e){const s=g(t,"index_name"),a=r==="postgres"?g(t,"is_unique")==="true":g(t,"NON_UNIQUE")==="0",o=n.get(s);o===void 0?n.set(s,{columns:[g(t,"column_name")],unique:a}):o.columns.push(g(t,"column_name"))}return[...n.entries()].map(([t,s])=>({columns:s.columns,name:t,unique:s.unique}))},Re=async(e,r,n)=>{const t=r==="postgres",[s,a,o,i]=await Promise.all([e(t?ve:_e,[n]),e(t?be:ye,[n]),e(t?he:Ae,[n]),e(t?Ee:Se,[n])]),c=E(a),m=E(o),u=E(i),l=[];for(const[p,v]of E(s)){const d=new Map((m.get(p)??[]).map(b=>[g(b,"column_name"),{column:g(b,"foreign_column"),table:g(b,"foreign_table")}])),A=v.map(b=>{const R=g(b,"column_name"),$=d.get(R);return{...Ne(b,r),name:R,nullable:g(b,"is_nullable").toUpperCase()==="YES",...$===void 0?{}:{references:$}}});l.push({columns:A,indexes:we(u.get(p)??[],r),name:p,primaryKey:(c.get(p)??[]).map(b=>g(b,"column_name"))})}return l.sort((p,v)=>p.name.localeCompare(v.name)),{dialect:r,tables:l}},$e=e=>Y(e)?"lunorash/server":"@lunora/server",Le=async(e,r,n)=>{const t=_(r,e.path);return O(t)&&n.force!==!0?(n.logger.warn(`skipped lunora/${e.path} — it already exists (pass --force to overwrite)`),!1):n.dryRun===!0?(n.logger.info(`would write lunora/${e.path} (${String(e.contents.split(`
99
- `).length)} lines)`),!1):(await U(r,{recursive:!0}),await I(t,e.contents,"utf8"),n.logger.info(`wrote lunora/${e.path}`),!0)},Te=(e,r)=>{const n=r.tables;if(n===void 0||n.length===0)return e.tables;for(const t of n)e.tables.some(s=>s.name===t)||r.logger.warn(`--tables: no table named "${t}" exists in this schema — skipped.`);return e.tables.filter(t=>n.includes(t.name))},Oe=async(e,r,n,t)=>{const s=fe(k(e,"utf8"),r,n);for(const a of s.warnings)t.logger.warn(a);return s.text===void 0?(t.logger.info("lunora/schema.ts is already up to date with the database."),!1):t.dryRun===!0?(t.logger.info(`would merge ${String(s.applied)} addition(s) into lunora/schema.ts`),!1):(await I(e,s.text,"utf8"),t.logger.info(`merged ${String(s.applied)} addition(s) into lunora/schema.ts`),!0)},Ie=async e=>{const r=e.cwd??process.cwd();if(e.connection===void 0&&(e.url===void 0||e.url==="")){const d="`lunora introspect` needs a database URL: pass --url, or set DATABASE_URL.";return e.logger.error(d),{code:L.USAGE,error:d,written:[]}}const n=e.connection===void 0?Q(e.url):e.dialect??"postgres",t=e.connection??await W(e.url,n,e.schema,r);let s;try{s=await Re(t.execute,n,t.schema)}finally{e.connection===void 0&&await t.close()}const a=Te(s,e);if(a.length===0){const d="no tables found to introspect — check --schema and --tables.";return e.logger.error(d),{code:L.NOT_FOUND,error:d,written:[]}}const{files:o,warnings:i}=le({...s,tables:a},{procedures:e.procedures!==!1,serverImport:$e(r)}),c=_(r,"lunora"),m=[],u=_(c,"schema.ts"),l=O(u)&&e.force!==!0,p=l?o.filter(d=>d.path!=="schema.ts"):o;l&&await Oe(u,{...s,tables:a},n,e)&&m.push("schema.ts");for(const d of p)await Le(d,c,e)&&m.push(d.path);for(const d of i)e.logger.warn(d);e.logger.info(`introspected ${String(a.length)} table(s) from ${n}. Review the generated files before running \`lunora dev\`.`);const v=a.map(d=>d.name);return{code:0,dialect:n,tables:v,written:m}},qe=q(async({cwd:e,format:r,logger:n,options:t})=>{const s=await Ie({cwd:e,dryRun:t.dryRun===!0,force:t.force===!0,format:r,logger:n,procedures:t.procedures!==!1,schema:t.schema,tables:t.tables===void 0?void 0:t.tables.split(",").map(a=>a.trim()),url:t.url??process.env.DATABASE_URL});return{code:s.code,data:s.dialect===void 0?void 0:{dialect:s.dialect,dryRun:t.dryRun===!0,tables:s.tables??[],written:s.written},error:s.error}});export{qe as execute,$e as resolveServerImport,Ie as runIntrospectCommand};
98
+ ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX`,_=e=>{const r=new Map;for(const n of e){const t=g(n,"table_name"),s=r.get(t);s===void 0?r.set(t,[n]):s.push(n)}return r},Ae=(e,r)=>{const n=g(e,"data_type").toLowerCase();if(r!=="postgres")return{arrayDepth:0,dataType:n};const t=g(e,"udt_name").toLowerCase();return n==="array"&&t.startsWith("_")?{arrayDepth:1,dataType:t.slice(1)}:{arrayDepth:0,dataType:t===""?n:t}},Ne=(e,r)=>{const n=new Map;for(const t of e){const s=g(t,"index_name"),a=r==="postgres"?g(t,"is_unique")==="true":g(t,"NON_UNIQUE")==="0",o=n.get(s);o===void 0?n.set(s,{columns:[g(t,"column_name")],unique:a}):o.columns.push(g(t,"column_name"))}return[...n.entries()].map(([t,s])=>({columns:s.columns,name:t,unique:s.unique}))},Se=async(e,r,n)=>{const t=r==="postgres",[s,a,o,i]=await Promise.all([e(t?ge:he,[n]),e(t?fe:_e,[n]),e(t?ve:Ee,[n]),e(t?be:ye,[n])]),c=_(a),m=_(o),u=_(i),l=[];for(const[p,v]of _(s)){const d=new Map((m.get(p)??[]).map(b=>[g(b,"column_name"),{column:g(b,"foreign_column"),table:g(b,"foreign_table")}])),A=v.map(b=>{const R=g(b,"column_name"),$=d.get(R);return{...Ae(b,r),name:R,nullable:g(b,"is_nullable").toUpperCase()==="YES",...$===void 0?{}:{references:$}}});l.push({columns:A,indexes:Ne(u.get(p)??[],r),name:p,primaryKey:(c.get(p)??[]).map(b=>g(b,"column_name"))})}return l.sort((p,v)=>p.name.localeCompare(v.name)),{dialect:r,tables:l}},we=e=>q(e)?"lunorash/server":"@lunora/server",Re=async(e,r,n)=>{const t=E(r,e.path);return O(t)&&n.force!==!0?(n.logger.warn(`skipped lunora/${e.path} — it already exists (pass --force to overwrite)`),!1):n.dryRun===!0?(n.logger.info(`would write lunora/${e.path} (${String(e.contents.split(`
99
+ `).length)} lines)`),!1):(await U(r,{recursive:!0}),await I(t,e.contents,"utf8"),n.logger.info(`wrote lunora/${e.path}`),!0)},$e=(e,r)=>{const n=r.tables;if(n===void 0||n.length===0)return e.tables;for(const t of n)e.tables.some(s=>s.name===t)||r.logger.warn(`--tables: no table named "${t}" exists in this schema — skipped.`);return e.tables.filter(t=>n.includes(t.name))},Te=async(e,r,n,t)=>{const s=pe(k(e,"utf8"),r,n);for(const a of s.warnings)t.logger.warn(a);return s.text===void 0?(t.logger.info("lunora/schema.ts is already up to date with the database."),!1):t.dryRun===!0?(t.logger.info(`would merge ${String(s.applied)} addition(s) into lunora/schema.ts`),!1):(await I(e,s.text,"utf8"),t.logger.info(`merged ${String(s.applied)} addition(s) into lunora/schema.ts`),!0)},Le=async e=>{const r=e.cwd??process.cwd();if(e.connection===void 0&&(e.url===void 0||e.url==="")){const d="`lunora introspect` needs a database URL: pass --url, or set DATABASE_URL.";return e.logger.error(d),{code:T.USAGE,error:d,written:[]}}const n=e.connection===void 0?Q(e.url):e.dialect??"postgres",t=e.connection??await J(e.url,n,e.schema,r);let s;try{s=await Se(t.execute,n,t.schema)}finally{e.connection===void 0&&await t.close()}const a=$e(s,e);if(a.length===0){const d="no tables found to introspect — check --schema and --tables.";return e.logger.error(d),{code:T.NOT_FOUND,error:d,written:[]}}const{files:o,warnings:i}=ce({...s,tables:a},{procedures:e.procedures!==!1,serverImport:we(r)}),c=E(r,"lunora"),m=[],u=E(c,"schema.ts"),l=O(u)&&e.force!==!0,p=l?o.filter(d=>d.path!=="schema.ts"):o;l&&await Te(u,{...s,tables:a},n,e)&&m.push("schema.ts");for(const d of p)await Re(d,c,e)&&m.push(d.path);for(const d of i)e.logger.warn(d);e.logger.info(`introspected ${String(a.length)} table(s) from ${n}. Review the generated files before running \`lunora dev\`.`);const v=a.map(d=>d.name);return{code:0,dialect:n,tables:v,written:m}},qe=F(async({cwd:e,format:r,logger:n,options:t})=>{const s=await Le({cwd:e,dryRun:t.dryRun===!0,force:t.force===!0,format:r,logger:n,procedures:t.procedures!==!1,schema:t.schema,tables:t.tables===void 0?void 0:t.tables.split(",").map(a=>a.trim()),url:t.url??process.env.DATABASE_URL});return{code:s.code,data:s.dialect===void 0?void 0:{dialect:s.dialect,dryRun:t.dryRun===!0,tables:s.tables??[],written:s.written},error:s.error}});export{qe as execute,we as resolveServerImport,Le as runIntrospectCommand};
@@ -0,0 +1 @@
1
+ const t=/[\u003C\u003E\u002F\u2028\u2029]/g,S={"/":String.raw`\u002F`,"\u2028":String.raw`\u2028`,"\u2029":String.raw`\u2029`,"<":String.raw`\u003C`,">":String.raw`\u003E`},n=u=>JSON.stringify(u).replaceAll(t,r=>S[r]??r);export{n as l};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.467",
3
+ "version": "1.0.0-alpha.469",
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",
@@ -56,16 +56,16 @@
56
56
  },
57
57
  "dependencies": {
58
58
  "@bomb.sh/tab": "0.0.22",
59
- "@lunora/advisor": "1.0.0-alpha.281",
60
- "@lunora/bindings": "1.0.0-alpha.147",
61
- "@lunora/codegen": "1.0.0-alpha.385",
62
- "@lunora/config": "1.0.0-alpha.442",
63
- "@lunora/container": "1.0.0-alpha.105",
64
- "@lunora/d1": "1.0.0-alpha.228",
59
+ "@lunora/advisor": "1.0.0-alpha.283",
60
+ "@lunora/bindings": "1.0.0-alpha.149",
61
+ "@lunora/codegen": "1.0.0-alpha.388",
62
+ "@lunora/config": "1.0.0-alpha.445",
63
+ "@lunora/container": "1.0.0-alpha.107",
64
+ "@lunora/d1": "1.0.0-alpha.230",
65
65
  "@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
66
- "@lunora/mcp": "1.0.0-alpha.285",
67
- "@lunora/runtime": "1.0.0-alpha.269",
68
- "@lunora/seed": "1.0.0-alpha.277",
66
+ "@lunora/mcp": "1.0.0-alpha.287",
67
+ "@lunora/runtime": "1.0.0-alpha.271",
68
+ "@lunora/seed": "1.0.0-alpha.278",
69
69
  "@lunora/wire": "1.0.0-alpha.6",
70
70
  "@visulima/cerebro": "4.0.1",
71
71
  "@visulima/error": "6.1.0",
@@ -79,18 +79,28 @@ thunk instead. It runs once per shard construction, not per request:
79
79
  ```ts title="src/server.ts"
80
80
  import type { HyperdriveLike } from "@lunora/hyperdrive";
81
81
  import { createHyperdrive, fromPostgresJs } from "@lunora/hyperdrive";
82
- import postgres from "postgres";
83
82
 
84
83
  import { defineApp } from "../lunora/_generated/app.js";
85
84
 
86
85
  const app = defineApp<Env>()
87
86
  .shard((env) => env.SHARD)
88
- .hyperdrive((env) => fromPostgresJs(postgres(createHyperdrive(env.HYPERDRIVE as HyperdriveLike).connectionString, { fetch_types: false, max: 5 })))
87
+ .hyperdrive((env) =>
88
+ fromPostgresJs(async () => {
89
+ const { default: postgres } = await import("postgres");
90
+
91
+ return postgres(createHyperdrive(env.HYPERDRIVE as HyperdriveLike).connectionString, { fetch_types: false, max: 5 });
92
+ }),
93
+ )
89
94
  .build();
90
95
 
91
96
  export const ShardDO = app.ShardDO;
92
97
  ```
93
98
 
99
+ The adapters take a loader, not a driver. It runs on the first query, so the
100
+ driver stays out of the Worker's startup graph; never `import postgres from
101
+ "postgres"` statically in the entry. For `pg`, the loader may `await
102
+ client.connect()` before returning.
103
+
94
104
  Then query from any action:
95
105
 
96
106
  ```ts
@@ -152,7 +162,11 @@ messages: defineTable({ body: v.string(), channelId: v.string() })
152
162
 
153
163
  ```ts
154
164
  // worker entry: one client per binding name
155
- .sourceClient((env, binding) => fromPostgresJs(postgres((env[binding] as { connectionString: string }).connectionString, { fetch_types: false, max: 5 })))
165
+ .sourceClient((env, binding) => fromPostgresJs(async () => {
166
+ const { default: postgres } = await import("postgres");
167
+
168
+ return postgres((env[binding] as { connectionString: string }).connectionString, { fetch_types: false, max: 5 });
169
+ }))
156
170
  ```
157
171
 
158
172
  How `.source()` behaves:
@@ -36,7 +36,7 @@ same shapes D1 uses (boolean → 1/0, JSON → text/json, bigint → decimal).
36
36
  ```bash
37
37
  pnpm add @lunora/hyperdrive @lunora/sql-store
38
38
  pnpm add postgres # Postgres engine → buildPgExec(fromPostgresJs(...))
39
- # or: pnpm add mysql2 → buildMysqlExec(mysql2/promise pool), MySQL 8.0+
39
+ # or: pnpm add mysql2 → buildMysqlExec(loader of a mysql2/promise pool), MySQL 8.0+
40
40
  ```
41
41
 
42
42
  `@lunora/hyperdrive/global` is a subpath export of `@lunora/hyperdrive`, not a
@@ -96,7 +96,6 @@ Codegen emits a `.hyperdriveGlobal({ engine, exec, origin? })` builder method.
96
96
  ```ts
97
97
  import { fromPostgresJs } from "@lunora/hyperdrive";
98
98
  import { buildPgExec } from "@lunora/hyperdrive/global";
99
- import postgres from "postgres";
100
99
 
101
100
  import { defineApp } from "../lunora/_generated/app.js";
102
101
 
@@ -104,7 +103,14 @@ const app = defineApp<Env>()
104
103
  .shard((env) => env.SHARD)
105
104
  .hyperdriveGlobal({
106
105
  engine: "postgres",
107
- exec: (env) => buildPgExec(fromPostgresJs(postgres(env.HYPERDRIVE.connectionString, { fetch_types: false, max: 5 }))),
106
+ exec: (env) =>
107
+ buildPgExec(
108
+ fromPostgresJs(async () => {
109
+ const { default: postgres } = await import("postgres");
110
+
111
+ return postgres(env.HYPERDRIVE.connectionString, { fetch_types: false, max: 5 });
112
+ }),
113
+ ),
108
114
  })
109
115
  .build();
110
116
 
@@ -113,26 +119,33 @@ export const ShardDO = app.ShardDO;
113
119
 
114
120
  For MySQL, create the pool with the `FOUND_ROWS` flag. The optimistic-concurrency
115
121
  guard needs matched-row counts. Without the flag, an idempotent write reports
116
- zero affected rows and raises a spurious conflict. `buildMysqlExec` throws at
117
- construction when the flag is missing. If it can't read the flag, it warns once instead.
122
+ zero affected rows and raises a spurious conflict. `buildMysqlExec` takes a loader,
123
+ like `fromPostgresJs`, and runs it on the first statement; that statement fails when the
124
+ flag is missing. If it can't read the flag, it warns once instead. Import `mysql2` inside
125
+ the loader, never at the top of the file, so the driver stays out of the Worker's startup.
118
126
 
119
127
  ```ts
120
128
  import { buildMysqlExec } from "@lunora/hyperdrive/global";
121
- import mysql from "mysql2/promise";
122
129
 
123
130
  // inside the builder chain:
124
131
  .hyperdriveGlobal({
125
132
  engine: "mysql",
126
133
  // disableEval: mysql2 needs it to run on Workers.
127
- exec: (env) => buildMysqlExec(mysql.createPool({ uri: env.HYPERDRIVE.connectionString, disableEval: true, flags: ["FOUND_ROWS"] })),
134
+ exec: (env) =>
135
+ buildMysqlExec(async () => {
136
+ const { createPool } = await import("mysql2/promise");
137
+
138
+ return createPool({ uri: env.HYPERDRIVE.connectionString, disableEval: true, flags: ["FOUND_ROWS"] });
139
+ }),
128
140
  })
129
141
  ```
130
142
 
131
143
  Each write commits its row and its aggregate, rank and CDC companion rows in one
132
- transaction when the driver can hold one connection for it: postgres.js
133
- (`fromPostgresJs`, through `sql.begin`) and a mysql2 **pool** (`getConnection`).
134
- `fromNodePg` and a single mysql2 connection have no such handle, so there the
135
- companions commit one statement at a time after the row. Every query and write
144
+ transaction. postgres.js (`fromPostgresJs`, through `sql.begin`) and a mysql2 **pool**
145
+ (`getConnection`) can hold that transaction. A postgres.js client without `begin` and a
146
+ single mysql2 connection cannot, so a write to a table with companions fails there with
147
+ `GLOBAL_TRANSACTION_UNSUPPORTED` before it writes anything. `fromNodePg` has no such
148
+ handle, so its companions commit one statement at a time after the row. Every query and write
136
149
  statement, and each write's transaction as a whole, is abandoned after 30 seconds
137
150
  with a retryable `SERVICE_UNAVAILABLE` (`createHyperdriveGlobalCtxDb` takes a
138
151
  `queryTimeoutMs` to change it; `0` disables it). Table, aggregate, rank and CDC