@lunora/seed 1.0.0-alpha.255 → 1.0.0-alpha.257

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/README.md CHANGED
@@ -128,7 +128,7 @@ lunora seed --reset # wipe local .wrangler/state first (loc
128
128
  lunora seed --dry-run # print NDJSON, write nothing
129
129
  ```
130
130
 
131
- Also: `--batch-size` (rows per HTTP request, default 500), `--url` (worker URL, default `http://localhost:8787`), `--prod` (requires an explicit `--url`), `--token` / `LUNORA_ADMIN_TOKEN` (admin bearer; prefer the env var), `--yes` (skip the non-local confirmation prompt). `--reset` is local-only and cannot be combined with `--prod` or a remote `--url`.
131
+ Also: `--allow-orphans` (let a required foreign key with no parent row take a dangling id instead of failing), `--batch-size` (rows per HTTP request, default 500), `--url` (worker URL, default `http://localhost:8787`), `--prod` (requires an explicit `--url`), `--token` / `LUNORA_ADMIN_TOKEN` (admin bearer; prefer the env var), `--yes` (skip the non-local confirmation prompt). `--reset` is local-only and cannot be combined with `--prod` or a remote `--url`.
132
132
 
133
133
  ## Limitations
134
134
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Schema } from '@lunora/server';
2
- import { O as OverrideContext } from "./packem_shared/plan.d-CDkBhI-1.mjs";
3
- export { type S as SeedCounts, type a as SeedOptions, type b as SeedOverrides, type T as TablePlan, s as seedPlan } from "./packem_shared/plan.d-CDkBhI-1.mjs";
2
+ import { O as OverrideContext } from "./packem_shared/plan.d-CpYSZG6V.mjs";
3
+ export { type S as SeedCounts, type a as SeedOptions, type b as SeedOverrides, type T as TablePlan, s as seedPlan } from "./packem_shared/plan.d-CpYSZG6V.mjs";
4
4
  /**
5
5
  * A typed, schema-aware seed client — the ergonomic DX layer over {@link seedPlan}.
6
6
  *
@@ -42,6 +42,8 @@ interface SeedClientState {
42
42
  type SeedClient<InsertModel> = SeedClientState & { [Table in keyof InsertModel]: TableSeeder<InsertModel, Table>; };
43
43
  /** Options for {@link createSeedClient}. */
44
44
  interface SeedClientOptions {
45
+ /** Let a required foreign key with no parent row take a dangling id instead of refusing (see `SeedOptions.allowOrphans`). */
46
+ allowOrphans?: boolean;
45
47
  /** Default row count when a table call passes no spec (default `10`). */
46
48
  defaultCount?: number;
47
49
  /**
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Schema } from '@lunora/server';
2
- import { O as OverrideContext } from "./packem_shared/plan.d-CDkBhI-1.js";
3
- export { type S as SeedCounts, type a as SeedOptions, type b as SeedOverrides, type T as TablePlan, s as seedPlan } from "./packem_shared/plan.d-CDkBhI-1.js";
2
+ import { O as OverrideContext } from "./packem_shared/plan.d-CpYSZG6V.js";
3
+ export { type S as SeedCounts, type a as SeedOptions, type b as SeedOverrides, type T as TablePlan, s as seedPlan } from "./packem_shared/plan.d-CpYSZG6V.js";
4
4
  /**
5
5
  * A typed, schema-aware seed client — the ergonomic DX layer over {@link seedPlan}.
6
6
  *
@@ -42,6 +42,8 @@ interface SeedClientState {
42
42
  type SeedClient<InsertModel> = SeedClientState & { [Table in keyof InsertModel]: TableSeeder<InsertModel, Table>; };
43
43
  /** Options for {@link createSeedClient}. */
44
44
  interface SeedClientOptions {
45
+ /** Let a required foreign key with no parent row take a dangling id instead of refusing (see `SeedOptions.allowOrphans`). */
46
+ allowOrphans?: boolean;
45
47
  /** Default row count when a table call passes no spec (default `10`). */
46
48
  defaultCount?: number;
47
49
  /**
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{createSeedClient as o}from"./packem_shared/createSeedClient-CiPbun5V.mjs";import{s as a}from"./packem_shared/plan-B3lKLUhz.mjs";export{o as createSeedClient,a as seedPlan};
1
+ import{createSeedClient as o}from"./packem_shared/createSeedClient-DfU2eElm.mjs";import{s as a}from"./packem_shared/plan-BO3-4tGm.mjs";export{o as createSeedClient,a as seedPlan};
@@ -0,0 +1 @@
1
+ import{a as C,s as P,c as T}from"./plan-BO3-4tGm.mjs";const A=e=>Array.isArray(e)&&e.length===2&&typeof e[0]=="number"&&typeof e[1]=="number",q=(e,d,u,i)=>{const s=n=>typeof n=="number"?n:T.int([u,d,"count"],{max:n[1],min:n[0]});return e===void 0?{count:i}:typeof e=="number"?{count:e}:typeof e=="function"?{count:e(s)}:A(e)?{count:s(e)}:{count:e.length,partials:e}},B=(e,d,u)=>{const i={};if(e!==void 0){const s=new Set(e.flatMap(n=>Object.keys(n)));for(const n of s)i[n]=l=>e[l.index-u]?.[n]}for(const[s,n]of Object.entries(d??{}))i[s]=n;return i},I=(e,d={})=>{const{allowOrphans:u=!1,defaultCount:i=10,now:s,persist:n,seed:l=0}=d,a={},f={},y={},j=async(t,o,v)=>{C(l);const{count:c,partials:b}=q(o,t,l,i),k=y[t]??0,S=P(e,{allowOrphans:u,counts:{[t]:c},existingIds:f,indexOffset:{[t]:k},now:s,only:[t],overrides:{[t]:B(b,v?.overrides,k)},seed:l});let w=[];for(const{rows:h,table:r}of S){const O=h.map($=>$._id);a[r]??=[],a[r].push(...h),f[r]??=[],f[r].push(...O),y[r]=(y[r]??0)+h.length,r===t&&(w=O),n!==void 0&&await n(r,h)}return{[t]:w}};let m=Promise.resolve();const x=(t,o,v)=>{const c=m.then(()=>j(t,o,v));return m=c.then(()=>{},()=>{}),c},g={$ids:f,$reset:()=>{for(const t of Object.keys(a))delete a[t];for(const t of Object.keys(f))delete f[t];for(const t of Object.keys(y))delete y[t]},$store:a};return new Proxy(g,{get(t,o,v){return typeof o=="string"&&!o.startsWith("$")&&Object.hasOwn(e.tables,o)?(c,b)=>x(o,c,b):Reflect.get(t,o,v)}})};export{I as createSeedClient};
@@ -0,0 +1 @@
1
+ import{LunoraError as T}from"@lunora/errors";import{faker as g}from"@faker-js/faker";import{cyrb53 as we,optionalInner as be}from"@lunora/values";let K=0;const Oe=(e,t)=>e<t?-1:e>t?1:0,ee=e=>e===void 0?"undefined":typeof e=="bigint"?`${e.toString()}n`:e===null||typeof e!="object"?JSON.stringify(e)??"null":Array.isArray(e)?`[${e.map(n=>ee(n)).join(",")}]`:`{${Object.keys(e).toSorted(Oe).map(n=>`${JSON.stringify(n)}:${ee(e[n])}`).join(",")}}`,De="\0",Re=e=>{const t=typeof e=="string"?De+e:ee(e);return we(t,K)%4294967296},Le=e=>{if(typeof e=="number"){K=e>>>0;return}if(typeof e=="string"){K=we(e,0)%4294967296;return}let t=0;for(const n of e)t=Math.imul(t^n,2654435761)>>>0;K=t},v=(e,t)=>(g.seed(Re(e)),t()),Fe=(e,t)=>{if(typeof t=="number")return t;const[n,r]=t;return v(["__count__",e],()=>g.number.int({max:r,min:n}))},Pe="example.com",qe=/[a-z]/,Ce=/[A-Z]/,Ue=/\d/,m={bool(e){return v(e,()=>g.datatype.boolean())},city(e){return v(e,()=>g.location.city())},country(e){return v(e,()=>g.location.country())},email(e){return v(e,()=>g.internet.email({provider:Pe}).toLowerCase())},firstName(e){return v(e,()=>g.person.firstName())},float(e,t){const{fractionDigits:n=2,max:r=1e3,min:a=0}=t??{};return v(e,()=>g.number.float({fractionDigits:n,max:r,min:a}))},fullName(e){return v(e,()=>g.person.fullName())},int(e,t){const{max:n=1e3,min:r=0}=t??{};return v(e,()=>g.number.int({max:n,min:r}))},lastName(e){return v(e,()=>g.person.lastName())},oneOf(e,t){if(t.length!==0)return v(e,()=>g.helpers.arrayElement(t))},paragraph(e){return v(e,()=>g.lorem.paragraph())},password(e){return v(e,()=>g.internet.password())},phoneNumber(e){return v(e,()=>g.phone.number())},scramble(e,t){const n=new Set(t?.preserve);return Array.from({length:e.length},(r,a)=>{const o=e.charAt(a);return n.has(o)?o:qe.test(o)?v([e,a,"l"],()=>g.string.alpha({casing:"lower",length:1})):Ce.test(o)?v([e,a,"u"],()=>g.string.alpha({casing:"upper",length:1})):Ue.test(o)?v([e,a,"d"],()=>String(g.number.int({max:9,min:0}))):o}).join("")},sentence(e,t){return v(e,()=>g.lorem.sentence(t?{max:t.max??8,min:t.min??3}:void 0))},slug(e){return v(e,()=>g.lorem.slug())},streetAddress(e){return v(e,()=>g.location.streetAddress())},times(e,t,n){const r=Fe(e,t),a=[];for(let o=0;o<r;o+=1)a.push(n([e,o]));return a},url(e){return v(e,()=>g.internet.url())},username(e){return v(e,()=>g.internet.username())},uuid(e){return v(e,()=>g.string.uuid())},word(e){return v(e,()=>g.lorem.word())}},I=e=>e._meta??{},ne=e=>be(e)??e,Ie=e=>{const{column:t}=I(e);if(t?.defaultValue!==void 0||t?.defaultFn!==void 0)return!0;const n=be(e);return n===void 0?!1:Ie(n)},Ve=(e,t)=>{const n=ne(t),r=I(n);return{fkTable:n.kind==="id"?r.tableName:void 0,hasServerDefault:Ie(t),kind:n.kind,name:e,nullable:r.column?.notNull===!1,optional:t.kind==="optional",unique:r.column?.unique===!0,uniqueFromIndex:!1,validator:n}},je=e=>{const{tables:t}=e;return Object.entries(t).map(([n,r])=>{const a=r.indexes.filter(s=>s.unique),o=new Set(a.filter(s=>s.fields.length===1).map(s=>s.fields[0]));return{fields:Object.entries(r.shape).map(([s,u])=>{const i=Ve(s,u);return o.has(s)&&!i.unique?{...i,unique:!0,uniqueFromIndex:!0}:i}),name:n,uniqueIndexes:a.filter(s=>s.fields.length>1).map(s=>({fields:s.fields,name:s.name}))}})},Be=(e,t)=>{const n=new Map(e.map(i=>[i.name,i])),r=i=>{const c=n.get(i);if(c===void 0)return new Set;const f=new Set;for(const d of c.fields)d.fkTable!==void 0&&d.fkTable!==i&&t.has(d.fkTable)&&f.add(d.fkTable);return f},a=[],o=new Set,s=[...t].filter(i=>n.has(i)),u=i=>{const c=new Set,f=[...r(i)];for(;f.length>0;){const d=f.pop();if(d===i)return!0;!o.has(d)&&!c.has(d)&&(c.add(d),f.push(...r(d)))}return!1};for(;s.length>0;){const i=s.findIndex(d=>[...r(d)].every(p=>o.has(p))),c=i===-1?s.findIndex(d=>u(d)):i,[f]=s.splice(c,1);if(f===void 0)break;a.push(f),o.add(f)}return a},Ye=(e,t,n=new Set)=>{const r=new Map(e.map(s=>[s.name,s])),a=new Set(t),o=[...a];for(;o.length>0;){const s=o.pop();if(s===void 0)break;const u=r.get(s);if(u!==void 0&&!n.has(s))for(const i of u.fields)i.fkTable!==void 0&&i.fkTable!==s&&r.has(i.fkTable)&&!a.has(i.fkTable)&&(a.add(i.fkTable),o.push(i.fkTable))}return a},Ge=1e-6,He=(e,t,n)=>{const r=n?Math.floor(e)+1:e+Ge,a=r>e?r:e+Math.abs(e)*Number.EPSILON;if(n||t===void 0||a<=t||t<=e)return a;const o=(e+t)/2;return o>e?o:t},G=e=>{const t=I(e).constraints??{},n=t.type==="integer";let{maximum:r,minimum:a}=t;if(typeof t.exclusiveMinimum=="number"){const o=He(t.exclusiveMinimum,r,n);a=a===void 0?o:Math.max(a,o)}return n&&(a=a===void 0?void 0:Math.ceil(a),r=r===void 0?void 0:Math.floor(r)),{...t,maximum:r,minimum:a,minimumFromExclusive:t.minimum===void 0&&typeof t.exclusiveMinimum=="number"}},Qe=[{generate:e=>m.email(e),keywords:["email"]},{generate:e=>m.firstName(e),keywords:["firstname"]},{generate:e=>m.lastName(e),keywords:["lastname","surname"]},{generate:e=>m.username(e),keywords:["username"]},{generate:e=>m.fullName(e),keywords:["name"]},{generate:e=>m.sentence(e,{max:5,min:2}),keywords:["title"]},{generate:e=>m.url(e),keywords:["url","link","image","avatar"]},{generate:e=>m.phoneNumber(e),keywords:["phone"]},{generate:e=>m.paragraph(e),keywords:["description","bio","body","content","text"]},{generate:e=>m.slug(e),keywords:["slug","key","code"]},{generate:e=>m.city(e),keywords:["city"]},{generate:e=>m.country(e),keywords:["country"]},{generate:e=>m.streetAddress(e),keywords:["address","street"]},{generate:e=>m.password(e),keywords:["password","secret","token"]}],We=new Set(["at","date","deadline","expires","expiry","since","timestamp","until"]),Ke=/([a-z\d])([A-Z])/gu,Xe=/[\s_-]+/u,ze=e=>e.replaceAll(Ke,"$1 $2").split(Xe).filter(t=>t!=="").map(t=>t.toLowerCase()),Se={max:1e3,min:0},Ae={max:1e6,min:0},Je=4320*60*60*1e3,re=(e,t,n)=>{const{maximum:r,minimum:a}=e;if(r!==void 0&&a!==void 0&&a>r)throw new T("INTERNAL",`Seed constraint error for field "${n}": minimum (${String(a)}) > maximum (${String(r)}). Adjust the schema constraints.`);const o=t.max-t.min;return{max:r??(a!==void 0&&a>t.max?a+o:t.max),min:a??(r!==void 0&&r<t.min?r-o:t.min)}},Ze=e=>{const n=ze(e).at(-1);return n!==void 0&&We.has(n)},Te=(e,t)=>e.maximum===void 0&&(e.minimum===void 0||e.minimumFromExclusive===!0&&(e.exclusiveMinimum??0)<=0)&&Ze(t),ue=(e,t)=>t-m.int(e,{max:Je,min:0}),V=(e,t)=>{throw new T("INTERNAL",`@lunora/seed: column "${e}" ${t}. Supply a value via \`overrides\` (\`{ <table>: { ${e}: … } }\`) or restrict the run with \`only\` so the table is skipped.`)},ae=(e,t)=>{const n=e===""?"x":e,r=Math.min(Math.max(n.length,t.min),t.max);return n.padEnd(r,n).slice(0,r)},j="@example.com",X="https://",et=/[\s@]/gu,tt=/[^a-z\d]/gu,nt=(e,t)=>{const n=e.lastIndexOf("@"),r=n>0?e.slice(n):"",a=r.length>0&&r.length<t.max?r:j,o=(n>0?e.slice(0,n):e).replaceAll(et,"");return`${ae(o,{max:t.max-a.length,min:Math.max(1,t.min-a.length)})}${a}`},rt=(e,t)=>{const n=URL.canParse(e)?new URL(e).hostname.replaceAll(tt,""):"";return`${X}${ae(n,{max:t.max-X.length,min:Math.max(1,t.min-X.length)})}`},at={email:{fit:nt,generate:e=>m.email(e),minimum:j.length+1},uri:{fit:rt,generate:e=>m.url(e),minimum:X.length+1}},ot=(e,t)=>{const n=e.toLowerCase(),r=Qe.find(a=>a.keywords.some(o=>n.includes(o)));return r===void 0?m.word(t):r.generate(t)},st=(e,t,n)=>{const{format:r,maxLength:a,minLength:o,pattern:s}=n;if(s!==void 0)return V(e,`is constrained to the pattern /${s}/, and the seeder cannot invent a value matching an arbitrary regular expression`);if(a!==void 0&&o!==void 0&&o>a)throw new T("INTERNAL",`Seed constraint error for field "${e}": minLength (${String(o)}) > maxLength (${String(a)}). Adjust the schema constraints.`);const u={max:a??Number.POSITIVE_INFINITY,min:o??0};if(r===void 0)return ae(ot(e,t),u);const i=at[r];if(i===void 0)return V(e,`declares format "${r}", which the seeder has no generator for`);if(u.max<i.minimum)return V(e,`declares format "${r}" with maxLength ${String(a)}, and the shortest ${r} value the seeder can build is ${String(i.minimum)} characters`);const c=i.generate(t);return c.length>=u.min&&c.length<=u.max?c:i.fit(c,u)},it=(e,t,n)=>{const{max:r,min:a}=re(t,Se,n);return t.type==="integer"||Number.isInteger(a)&&Number.isInteger(r)?m.int(e,{max:r,min:a}):Math.ceil(a*100)>Math.floor(r*100)?a+(r-a)*m.float(e,{fractionDigits:6,max:1,min:0}):m.float(e,{max:r,min:a})},Ne=(e,t)=>{const n=e.minItems===void 0?void 0:Math.max(0,Math.ceil(e.minItems)),r=e.maxItems===void 0||e.maxItems===Number.POSITIVE_INFINITY?void 0:Math.floor(e.maxItems),a=n??Math.max(0,Math.min(1,r??1)),o=r??Math.max(3,a);return o<a?V(t,`declares minItems ${String(e.minItems)} and maxItems ${String(e.maxItems)}, which no whole array length satisfies`):[a,o]},D=(e,t,n,r)=>{const a=ne(e),o=G(a);if(o.enum!==void 0&&o.enum.length>0)return m.oneOf(n,o.enum);switch(a.kind){case"any":return m.word(n);case"array":{const s=I(a).inner;if(s===void 0)return[];const[u,i]=Ne(o,t);return m.times(n,[u,i],c=>D(s,t,c,r))}case"bigint":return m.int(n,re(o,Ae,t));case"boolean":return m.bool(n);case"bytes":return Array.from({length:8},(s,u)=>m.int([n,u],{max:255,min:0}));case"date":case"timestamp":return ue(n,r);case"from":return V(t,"is a v.from() validator, whose external Standard Schema the seeder cannot introspect to invent a conforming value (give the column a concrete v.* type instead)");case"id":return m.uuid(n);case"literal":return I(a).value;case"null":return null;case"number":return Te(o,t)?ue(n,r):it(n,o,t);case"object":{const s=I(a).shape??{};return Object.fromEntries(Object.entries(s).map(([u,i])=>[u,D(i,u,[n,u],r)]))}case"record":{const{keyValidator:s,valueValidator:u}=I(a),i=m.times(n,[1,3],c=>{const f=s===void 0?m.word(["k",c]):String(D(s,t,["k",c],r)),d=u===void 0?m.word(["v",c]):D(u,t,["v",c],r);return[f,d]});return Object.fromEntries(i)}case"storage":return`seed/${m.uuid(n)}`;case"string":return st(t,n,o);case"union":{const s=I(a).members??[],u=m.oneOf(n,s);return u===void 0?m.word(n):D(u,t,[n,"u"],r)}default:return m.word(n)}},Q=1e6,ct=4,z=new DataView(new ArrayBuffer(8)),te=2n**63n,le=e=>{z.setFloat64(0,e);const t=z.getBigUint64(0);return t>=te?-(t-te):t},ut=e=>(z.setBigUint64(0,e<0n?te-e:e),z.getFloat64(0)),lt=6e4,mt=2**32,dt=(e,t,n)=>{const r=[...e];for(let a=r.length-1;a>0;a-=1){const o=m.int([t,n,"unique-deal",a],{max:a,min:0}),s=r[a];r[a]=r[o],r[o]=s}return r},ht=(e,t,n)=>{const{maxLength:r}=n,a=String(t),o=e.indexOf("@");if(o>0){const i=e.slice(0,o),c=e.slice(o),f=r===void 0?i.length:r-c.length-a.length-1;if(f>=0)return`${i.slice(0,f)}+${a}${c}`}if(n.format==="email"){const i=r===void 0?e.length:Math.max(0,r-j.length-a.length);return`${e.slice(0,i)}${a}${j}`}const s=`-${a}`,u=r===void 0?e.length:Math.max(0,r-s.length);return`${e.slice(0,u)}${s}`},U=(e,t,n)=>{const r=dt([...new Set(e)],t,n);return{capacity:r.length,valueAt:a=>r[a%r.length]}},ft=(e,t)=>{if(Number.isInteger(e)&&Number.isInteger(t)){let o=1;for(;Math.max(Math.abs(e),Math.abs(t))/o>=2**53;)o*=2;const s=Math.ceil(e/o)*o,u=Math.floor(t/o)*o;if(s>u)return{capacity:1,valueAt:()=>e};const i=(u-s)/o+1;return{capacity:i,valueAt:c=>s+c%i*o}}if(e===t)return{capacity:1,valueAt:()=>e};const n=Math.max(Math.max(Math.abs(e),Math.abs(t))*Number.EPSILON,Number.MIN_VALUE);if((t-e)/Q>=ct*n)return{capacity:Q,valueAt:o=>e+o%Q*(t-e)/Q};const r=le(e),a=Number(le(t)-r)+1;return{capacity:a,valueAt:o=>ut(r+BigInt(o%a))}},me=e=>({capacity:Number.POSITIVE_INFINITY,valueAt:t=>e-t*lt}),gt={capacity:Number.POSITIVE_INFINITY,valueAt:e=>e},B={capacity:Number.POSITIVE_INFINITY,valueAt:(e,t)=>t()},de=8,vt=(e,t)=>{const n=new Set;return{capacity:Number.POSITIVE_INFINITY,valueAt:(r,a)=>{for(let o=0;o<=de;o+=1){const s=a(o);if(!n.has(s))return n.add(s),s}throw new T("BAD_REQUEST",`@lunora/seed: cannot generate unique values for "${e}"."${t}" — the generator's draw and its ${String(de)} redraws all collided with a value an earlier row already took. Supply "${t}" via \`overrides\` (\`{ ${e}: { ${t}: … } }\`).`)}}},he=(e,t,n)=>{if(e.maximum===void 0&&e.minimum===void 0)return gt;if(e.maximum===void 0&&e.minimumFromExclusive===!0){const o=Math.max(Math.floor(e.exclusiveMinimum??0)+1,-Number.MAX_SAFE_INTEGER);return o>Number.MAX_SAFE_INTEGER?{capacity:1,valueAt:()=>e.minimum}:{capacity:Number.POSITIVE_INFINITY,valueAt:s=>o+s}}const{max:r,min:a}=re(e,t,n);return ft(a,r)},_e=(e,t,n)=>{throw new T("INTERNAL",`@lunora/seed: cannot generate unique values for "${e}"."${t}" — ${n}. Supply them via \`overrides\` (\`{ ${e}: { ${t}: … } }\`) or drop \`.unique()\` from the column.`)},pt=({format:e,maxLength:t})=>{if(t===void 0)return Number.POSITIVE_INFINITY;const n=e==="email"?j.length:1;return t<=n?0:10**(t-n)},J=({format:e,pattern:t})=>t!==void 0?"the column is pattern-constrained, and an index-tagged value would no longer match it":e===void 0||e==="email"?void 0:`the column declares format "${e}", which an index-tagged value would no longer satisfy`,yt=(e,t,n)=>{const r=J(e);return r!==void 0&&_e(t,n,r),{capacity:pt(e),valueAt:(a,o)=>ht(String(o()),a,e)}},Ee=(e,t,n)=>U(e,t,n),Y=(e,t)=>t.filter(n=>e.safeParse(n).ok),W=(e,t)=>t===void 0||I(e).column?.notNull!==!1?t:[...new Set([...t,null])],ke=e=>{const t=ne(e),{enum:n}=G(t);if(Array.isArray(n)&&n.length>0)return W(t,[...new Set(n)]);switch(t.kind){case"boolean":return W(t,[!1,!0]);case"literal":return W(t,[I(t).value]);case"null":return[null];case"union":{const r=(I(t).members??[]).map(a=>ke(a));return W(t,r.length>0&&r.every(a=>a!==void 0)?[...new Set(r.flat())]:void 0)}default:return}},oe=e=>{const t=ke(e);return t===void 0?void 0:Y(e,t)},wt=65536,bt=(e,t,n)=>{if(e===0)return t===0?1:0;if(e===1)return Math.max(0,n-t+1);let r=0;for(let a=t;a<=n&&r<=Number.MAX_SAFE_INTEGER;a+=1)r+=e**a;return r>Number.MAX_SAFE_INTEGER?Number.POSITIVE_INFINITY:r},fe=(e,t,n)=>{const r=e.length;if(r===0)return[];if(r===1)return Array.from({length:t+n}).fill(e[0]);let a=n,o=t;for(;a>=r**o;)a-=r**o,o+=1;return Array.from({length:o},(s,u)=>e[Math.floor(a/r**u)%r])},ge=e=>({capacity:e.length,valueAt:t=>e.length===0?void 0:e[t%e.length]}),It=(e,t,n)=>{const r=I(e.validator).inner,[a,o]=Ne(t,e.name);if(r===void 0||o<=0)return ge(Y(e.validator,[[]]));const s=oe(r);if(s===void 0)return B;const u=t.minItems===void 0?0:a,i=t.maxItems===void 0||t.maxItems===Number.POSITIVE_INFINITY?Number.POSITIVE_INFINITY:o,c=bt(s.length,u,i);return c<=wt?ge(Y(e.validator,Array.from({length:c},(f,d)=>fe(s,u,d)))):{capacity:c,valueAt:f=>{const d=fe(s,u,f%c);return e.validator.safeParse(d).ok||_e(n,e.name,`the column's validator rejects the array dealt at row ${String(f)}, and too many arrays exist to check them all up front`),d}}},St=(e,t,n)=>{const r=It(e,t,n);return!e.nullable||!Number.isFinite(r.capacity)?r:{capacity:r.capacity+1,valueAt:(a,o)=>{const s=a%(r.capacity+1);return s===r.capacity?null:r.valueAt(s,o)}}},se=(e,t)=>{const{now:n,table:r}=t,a=G(e.validator),o=a.enum;if(Array.isArray(o)&&o.length>0)return U(Y(e.validator,o),r,e.name);switch(e.kind){case"any":case"string":return e.uniqueFromIndex&&a.pattern===void 0&&J(a)!==void 0?vt(r,e.name):yt(a,r,e.name);case"array":return St(e,a,r);case"bigint":return he(a,Ae,e.name);case"boolean":return U(Y(e.validator,[!1,!0]),r,e.name);case"bytes":return{capacity:mt,valueAt:s=>[Math.floor(s/16777216)%256,Math.floor(s/65536)%256,Math.floor(s/256)%256,s%256,...Array.from({length:4},(u,i)=>m.int([r,e.name,"unique-bytes",i],{max:255,min:0}))]};case"date":case"timestamp":return me(n);case"literal":{const{value:s}=I(e.validator);return U(e.nullable?[s,null]:[s],r,e.name)}case"number":return Te(a,e.name)?me(n):he(a,Se,e.name);case"union":{const s=oe(e.validator);return s===void 0?B:U(s,r,e.name)}default:return B}},At=(e,t)=>{const n=G(e.validator),r=(e.kind==="string"||e.kind==="any")&&!(Array.isArray(n.enum)&&n.enum.length>0);if(r&&J(n)!==void 0)return;const a=se(e,t);if(!(a===B||a.capacity<=1))return{deal:a,rewrites:r||!Number.isFinite(a.capacity)}},Tt=(e,t)=>{const n=G(e.validator);return e.kind==="string"||e.kind==="any"?!(Array.isArray(n.enum)&&n.enum.length>0)&&n.pattern===void 0&&J(n)!==void 0:se(e,t)===B},Me=e=>oe(e.validator)?.includes(null)===!0,ve=(e,t)=>typeof e=="function"?e(t):e,Nt=(e,t,n,r)=>{if(!e.optional){if(e.nullable)return null;if(r)return m.uuid(n);throw new T("BAD_REQUEST",`cannot seed "${t}": required foreign key "${e.name}" points at "${e.fkTable}", which has no rows to reference. Seed "${e.fkTable}" too, make "${e.name}" optional or nullable, supply "${e.name}" yourself, or pass \`allowOrphans: true\` (\`--allow-orphans\` on the CLI) to accept dangling ids.`)}},_t=e=>({get:t=>e[t],length:e.length}),Et=e=>Array.from({length:e.length},(t,n)=>e.get(n)),pe=(e,t)=>t.get(m.int(e,{max:t.length-1,min:0})),kt=(e,t,n,r)=>{const a=e.fkTable,o=r[a]??[];if(a!==t){const u=_t([...new Set([...n.get(a)??[],...o])]);return()=>u}const s=n.get(t)??[];return u=>({get:i=>i<u?s[i]:o[i-u],length:u+o.length})},Mt=(e,t,n,r)=>{const a=r[e]??[],o=n.get(e)??[];if(t<a.length)return a[t];const s=t-a.length;return s<t?o[s]:o[t-1]??a.at(-1)},ye=(e,t,n,r)=>[e,t,n,r],xt=(e,t)=>{const{existingIds:n,idsByTable:r,index:a,input:o,localIndex:s,now:u,pools:i,table:c,uniqueDeals:f}=t;if(e.fkTable!==void 0){const p=i.get(e.name)(s);if(p.length===0)return Nt(e,c,o,t.allowOrphans);const w=f.get(e.name);return!e.unique&&w===void 0?pe(o,p):w===void 0?Mt(c,s,r,n):w.valueAt(a,()=>pe(o,p))}if(e.hasServerDefault)return;const d=f.get(e.name);return d!==void 0?d.valueAt(a,(p=0)=>D(e.validator,e.name,p===0?o:[...o,"unique-redraw",p],u)):D(e.validator,e.name,o,u)},$t=(e,t,n,r)=>{if(!(n(e).length===0||Object.hasOwn(r,e.name)))throw new T("BAD_REQUEST",`cannot seed into the non-empty table "${t}": unique self-referencing column "${e.name}" cannot be dealt across calls, because the values already stored in it are not knowable from the row ids. Seed the table in one call, or supply "${e.name}" yourself.`)},Ot=(e,t,n)=>{const r=[];let a=1;for(const{deal:i}of e)r.push(a),a*=i.capacity;const o=(i,c)=>Math.floor(i/r[c])%e[c].deal.capacity,s=e.map(({deal:i,field:c})=>({capacity:i.capacity,name:c.name})),u=e.findIndex(({field:i})=>Me(i));for(const[i,{deal:c,field:f}]of e.entries())n.set(f.name,{capacity:t,chain:s,valueAt:(d,p)=>{if(i===u&&d>=t)return null;let w=0;for(let N=0;N<i;N+=1)w+=o(d,N);return c.valueAt((o(d,i)+w)%c.capacity,p)}})},Dt=(e,t,n,r,a,o,s,u)=>{const i=[...new Set(e.fields)],c=i.flatMap(l=>t.get(l)??[]),f=l=>l.unique||l.fkTable===n||l.hasServerDefault&&l.fkTable===void 0||l.fkTable!==void 0&&s(l).length===0;if(c.length!==i.length||c.some(l=>f(l)))return;const d=c.filter(l=>u.has(l.name)),p=Math.max(1,...d.map(({name:l})=>{const b=u.get(l)?.chain??[],F=b.findIndex(P=>!i.includes(P.name));return b.slice(0,F===-1?void 0:F).reduce((P,h)=>P*h.capacity,1)})),w=c.filter(l=>!u.has(l.name)).map(l=>{if(l.fkTable!==void 0)return{carrier:{deal:Ee(s(l),n,l.name),field:l},rank:0};const b=At(l,{now:o,table:n});return b===void 0?void 0:{carrier:{deal:b.deal,field:l},rank:b.rewrites?2:1}}).filter(l=>l!==void 0).toSorted((l,b)=>l.rank-b.rank),N=a+r,L=[];let $=1;for(const{carrier:l,rank:b}of w){if(b===2&&Math.max($,p)>=N)break;L.push(l),$*=l.deal.capacity}const _=L.some(({field:l})=>Me(l))?Number.POSITIVE_INFINITY:Math.max($,p);if(N>_&&!c.some(l=>l.fkTable===void 0&&Tt(l,{now:o,table:n})))throw new T("BAD_REQUEST",`cannot seed ${String(N)} rows into "${n}": unique index "${e.name}" over (${e.fields.join(", ")}) has only ${String(_)} distinct combinations`);Ot(L,$,u)},Rt=(e,t,n,r,a,o,s,u)=>{const i=new Map(t.map(c=>[c.name,c]));for(const c of e)Dt(c,i,n,r,a,o,s,u)},Lt=(e,t,n,r)=>{if(t!==void 0)return Ee(t,n.table,e.name);try{return se(e,n)}catch(a){if(r&&a instanceof T)return;throw a}},Ft=(e,t,n,r,a,o,s,u)=>{const i=new Map;for(const c of e){if(!c.unique||c.hasServerDefault&&c.fkTable===void 0)continue;if(c.fkTable===n){$t(c,n,u,s);continue}const f=c.fkTable===void 0?void 0:u(c);if(f?.length===0)continue;const d=Object.hasOwn(s,c.name),p=Lt(c,f,{now:o,table:n},d);if(p===void 0)continue;const w=a+r;if(w>p.capacity&&!d)throw new T("BAD_REQUEST",`cannot seed ${String(w)} rows into "${n}": unique column "${c.name}" has only ${String(p.capacity)} possible values`);i.set(c.name,p)}return Rt(t,e,n,r,a,o,u,i),i},Pt=(e,t)=>{if(t===void 0)return;const n=new Set(e.map(r=>r.name));for(const r of t)if(!n.has(r)){const a=e.map(o=>o.name).join(", ");throw new T("BAD_REQUEST",`unknown table "${r}" in seed \`only\` — schema defines: ${a||"(no tables)"}`)}},Vt=(e,t={})=>{const{allowOrphans:n=!1,counts:r={},defaultCount:a=10,existingIds:o={},indexOffset:s={},now:u=Date.now(),only:i,overrides:c={},seed:f=0}=t;Le(f);const d=je(e);Pt(d,i);const p=new Set(i??d.map(h=>h.name)),w=new Set(Object.keys(o).filter(h=>!p.has(h)&&(o[h]??[]).length>0)),N=new Set([...Ye(d,p,w)].filter(h=>p.has(h)||(o[h]??[]).length===0)),L=Be(d,N),$=new Map(d.map(h=>[h.name,h])),_=new Map,l={},b=[],F=(h,A,y,R=l)=>{const k=c[h]??{};if(Object.hasOwn(k,"_id")){const M=ve(k._id,{field:"_id",index:A,row:y,store:R,table:h});if(M!==void 0)return M}return m.uuid(ye(f,h,A,"_id"))},P=h=>{for(const A of h.fields){const y=A.fkTable;if(y!==void 0&&y!==h.name&&N.has(y)&&$.has(y)&&!_.has(y)){const R=s[y]??0,k=[],M={...l,[y]:k},q=[];for(let O=0;O<(r[y]??a);O+=1){const C=F(y,R+O,{},M);q.push(C),k.push({_id:C})}_.set(y,q)}}};for(const h of L){const A=$.get(h);if(A===void 0)continue;const y=c[h]??{},R=r[h]??a,k=s[h]??0;P(A);const M=_.get(h),q=M??[];_.set(h,q);const O=[];l[h]=O;const C=new Map(A.fields.filter(S=>S.fkTable!==void 0).map(S=>[S.name,kt(S,h,_,o)])),xe=Ft(A.fields,A.uniqueIndexes,h,R,k,u,y,S=>Et(C.get(S.name)(0)));for(let S=0;S<R;S+=1){const H=k+S,x={},$e=(E,Z)=>{if(Object.hasOwn(y,E)){const ce=ve(y[E],{field:E,index:H,row:x,store:l,table:h});if(ce!==void 0){x[E]=ce;return}}const ie=Z();ie!==void 0&&(x[E]=ie)};M===void 0?(x._id=F(h,H,x),q.push(x._id)):x._id=M[S];for(const E of A.fields){const Z=ye(f,h,H,E.name);$e(E.name,()=>xt(E,{allowOrphans:n,existingIds:o,idsByTable:_,index:H,input:Z,localIndex:S,now:u,pools:C,table:h,uniqueDeals:xe}))}O.push(x)}b.push({rows:O,table:h})}return b};export{Le as a,m as c,je as i,Vt as s};
@@ -34,6 +34,12 @@ type SeedOverrides = Record<string, Record<string, unknown>>;
34
34
  /** Per-table row counts. */
35
35
  type SeedCounts = Record<string, number>;
36
36
  interface SeedOptions {
37
+ /**
38
+ * Let a REQUIRED foreign key with no parent to point at take a fresh, dangling
39
+ * uuid instead of refusing. Off by default: a dangling id fails every join and
40
+ * `ctx.db.get` on the row, so it should be a deliberate choice.
41
+ */
42
+ allowOrphans?: boolean;
37
43
  /** Rows per table; falls back to {@link SeedOptions.defaultCount} when a table is absent. */
38
44
  counts?: SeedCounts;
39
45
  /** Count used for any selected table not present in `counts` (default `10`). */
@@ -34,6 +34,12 @@ type SeedOverrides = Record<string, Record<string, unknown>>;
34
34
  /** Per-table row counts. */
35
35
  type SeedCounts = Record<string, number>;
36
36
  interface SeedOptions {
37
+ /**
38
+ * Let a REQUIRED foreign key with no parent to point at take a fresh, dangling
39
+ * uuid instead of refusing. Off by default: a dangling id fails every join and
40
+ * `ctx.db.get` on the row, so it should be a deliberate choice.
41
+ */
42
+ allowOrphans?: boolean;
37
43
  /** Rows per table; falls back to {@link SeedOptions.defaultCount} when a table is absent. */
38
44
  counts?: SeedCounts;
39
45
  /** Count used for any selected table not present in `counts` (default `10`). */
@@ -0,0 +1 @@
1
+ import{s as a}from"./plan-BO3-4tGm.mjs";export{a as seedPlan};
@@ -1,5 +1,5 @@
1
1
  import { Schema } from '@lunora/server';
2
2
  import { TestHarness } from '@lunora/testing';
3
- import { a as SeedOptions } from "./packem_shared/plan.d-CDkBhI-1.mjs";
3
+ import { a as SeedOptions } from "./packem_shared/plan.d-CpYSZG6V.mjs";
4
4
  declare const seed: (harness: TestHarness, schema: Schema, options?: SeedOptions) => Promise<Record<string, string[]>>;
5
5
  export { seed };
package/dist/testing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Schema } from '@lunora/server';
2
2
  import { TestHarness } from '@lunora/testing';
3
- import { a as SeedOptions } from "./packem_shared/plan.d-CDkBhI-1.js";
3
+ import { a as SeedOptions } from "./packem_shared/plan.d-CpYSZG6V.js";
4
4
  declare const seed: (harness: TestHarness, schema: Schema, options?: SeedOptions) => Promise<Record<string, string[]>>;
5
5
  export { seed };
package/dist/testing.mjs CHANGED
@@ -1 +1 @@
1
- import{s as m,i as u}from"./packem_shared/plan-B3lKLUhz.mjs";const g=(r,o,a)=>{if(o.size===0&&a.size===0)return r;const s={...r};for(const n of o){const t=s[n];typeof t=="number"&&(s[n]=BigInt(t))}for(const n of a){const t=s[n];Array.isArray(t)&&(s[n]=Uint8Array.from(t).buffer)}return s},S=async(r,o,a={})=>{const s=m(o,a),n=u(o),t=new Map,l=new Map;for(const i of n){const c=new Set(i.fields.filter(e=>e.kind==="bigint").map(e=>e.name)),f=new Set(i.fields.filter(e=>e.kind==="bytes").map(e=>e.name));c.size>0&&t.set(i.name,c),f.size>0&&l.set(i.name,f)}const d={};return await r.run(async i=>{const c=i.db.insert;for(const{rows:f,table:e}of s){const b=[],p=t.get(e)??new Set,w=l.get(e)??new Set;for(const y of f)b.push(await c(e,g(y,p,w),{allowExplicitId:!0}));d[e]=b}}),d};export{S as seed};
1
+ import{s as m,i as u}from"./packem_shared/plan-BO3-4tGm.mjs";const g=(r,o,a)=>{if(o.size===0&&a.size===0)return r;const s={...r};for(const n of o){const t=s[n];typeof t=="number"&&(s[n]=BigInt(t))}for(const n of a){const t=s[n];Array.isArray(t)&&(s[n]=Uint8Array.from(t).buffer)}return s},S=async(r,o,a={})=>{const s=m(o,a),n=u(o),t=new Map,l=new Map;for(const i of n){const c=new Set(i.fields.filter(e=>e.kind==="bigint").map(e=>e.name)),f=new Set(i.fields.filter(e=>e.kind==="bytes").map(e=>e.name));c.size>0&&t.set(i.name,c),f.size>0&&l.set(i.name,f)}const d={};return await r.run(async i=>{const c=i.db.insert;for(const{rows:f,table:e}of s){const b=[],p=t.get(e)??new Set,w=l.get(e)??new Set;for(const y of f)b.push(await c(e,g(y,p,w),{allowExplicitId:!0}));d[e]=b}}),d};export{S as seed};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/seed",
3
- "version": "1.0.0-alpha.255",
3
+ "version": "1.0.0-alpha.257",
4
4
  "description": "Schema-driven, deterministic database seeding for Lunora: realistic fake data from defineSchema",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -1 +0,0 @@
1
- import{a as $,s as C,c as P}from"./plan-B3lKLUhz.mjs";const T=e=>Array.isArray(e)&&e.length===2&&typeof e[0]=="number"&&typeof e[1]=="number",A=(e,u,l,i)=>{const o=n=>typeof n=="number"?n:P.int([l,u,"count"],{max:n[1],min:n[0]});return e===void 0?{count:i}:typeof e=="number"?{count:e}:typeof e=="function"?{count:e(o)}:T(e)?{count:o(e)}:{count:e.length,partials:e}},q=(e,u,l)=>{const i={};if(e!==void 0){const o=new Set(e.flatMap(n=>Object.keys(n)));for(const n of o)i[n]=f=>e[f.index-l]?.[n]}for(const[o,n]of Object.entries(u??{}))i[o]=n;return i},H=(e,u={})=>{const{defaultCount:l=10,now:i,persist:o,seed:n=0}=u,f={},c={},a={},w=async(t,s,y)=>{$(n);const{count:d,partials:b}=A(s,t,n,l),m=a[t]??0,g=C(e,{counts:{[t]:d},existingIds:c,indexOffset:{[t]:m},now:i,only:[t],overrides:{[t]:q(b,y?.overrides,m)},seed:n});let k=[];for(const{rows:v,table:r}of g){const j=v.map(S=>S._id);f[r]??=[],f[r].push(...v),c[r]??=[],c[r].push(...j),a[r]=(a[r]??0)+v.length,r===t&&(k=j),o!==void 0&&await o(r,v)}return{[t]:k}};let h=Promise.resolve();const x=(t,s,y)=>{const d=h.then(()=>w(t,s,y));return h=d.then(()=>{},()=>{}),d},O={$ids:c,$reset:()=>{for(const t of Object.keys(f))delete f[t];for(const t of Object.keys(c))delete c[t];for(const t of Object.keys(a))delete a[t]},$store:f};return new Proxy(O,{get(t,s,y){return typeof s=="string"&&!s.startsWith("$")&&Object.hasOwn(e.tables,s)?(d,b)=>x(s,d,b):Reflect.get(t,s,y)}})};export{H as createSeedClient};
@@ -1 +0,0 @@
1
- import{LunoraError as y}from"@lunora/errors";import{faker as f}from"@faker-js/faker";import{cyrb53 as K,optionalInner as Z}from"@lunora/values";let O=0;const me=(e,n)=>e<n?-1:e>n?1:0,L=e=>e===void 0?"undefined":typeof e=="bigint"?`${e.toString()}n`:e===null||typeof e!="object"?JSON.stringify(e)??"null":Array.isArray(e)?`[${e.map(t=>L(t)).join(",")}]`:`{${Object.keys(e).toSorted(me).map(t=>`${JSON.stringify(t)}:${L(e[t])}`).join(",")}}`,de="\0",fe=e=>{const n=typeof e=="string"?de+e:L(e);return K(n,O)%4294967296},he=e=>{if(typeof e=="number"){O=e>>>0;return}if(typeof e=="string"){O=K(e,0)%4294967296;return}let n=0;for(const t of e)n=Math.imul(n^t,2654435761)>>>0;O=n},h=(e,n)=>(f.seed(fe(e)),n()),ge=(e,n)=>{if(typeof n=="number")return n;const[t,r]=n;return h(["__count__",e],()=>f.number.int({max:r,min:t}))},pe="example.com",ve=/[a-z]/,we=/[A-Z]/,be=/\d/,u={bool(e){return h(e,()=>f.datatype.boolean())},city(e){return h(e,()=>f.location.city())},country(e){return h(e,()=>f.location.country())},email(e){return h(e,()=>f.internet.email({provider:pe}).toLowerCase())},firstName(e){return h(e,()=>f.person.firstName())},float(e,n){const{fractionDigits:t=2,max:r=1e3,min:o=0}=n??{};return h(e,()=>f.number.float({fractionDigits:t,max:r,min:o}))},fullName(e){return h(e,()=>f.person.fullName())},int(e,n){const{max:t=1e3,min:r=0}=n??{};return h(e,()=>f.number.int({max:t,min:r}))},lastName(e){return h(e,()=>f.person.lastName())},oneOf(e,n){if(n.length!==0)return h(e,()=>f.helpers.arrayElement(n))},paragraph(e){return h(e,()=>f.lorem.paragraph())},password(e){return h(e,()=>f.internet.password())},phoneNumber(e){return h(e,()=>f.phone.number())},scramble(e,n){const t=new Set(n?.preserve);return Array.from({length:e.length},(r,o)=>{const s=e.charAt(o);return t.has(s)?s:ve.test(s)?h([e,o,"l"],()=>f.string.alpha({casing:"lower",length:1})):we.test(s)?h([e,o,"u"],()=>f.string.alpha({casing:"upper",length:1})):be.test(s)?h([e,o,"d"],()=>String(f.number.int({max:9,min:0}))):s}).join("")},sentence(e,n){return h(e,()=>f.lorem.sentence(n?{max:n.max??8,min:n.min??3}:void 0))},slug(e){return h(e,()=>f.lorem.slug())},streetAddress(e){return h(e,()=>f.location.streetAddress())},times(e,n,t){const r=ge(e,n),o=[];for(let s=0;s<r;s+=1)o.push(t([e,s]));return o},url(e){return h(e,()=>f.internet.url())},username(e){return h(e,()=>f.internet.username())},uuid(e){return h(e,()=>f.string.uuid())},word(e){return h(e,()=>f.lorem.word())}},p=e=>e._meta??{},X=e=>Z(e)??e,ee=e=>{const{column:n}=p(e);if(n?.defaultValue!==void 0||n?.defaultFn!==void 0)return!0;const t=Z(e);return t===void 0?!1:ee(t)},ye=(e,n)=>{const t=X(n),r=p(t);return{fkTable:t.kind==="id"?r.tableName:void 0,hasServerDefault:ee(n),kind:t.kind,name:e,nullable:r.column?.notNull===!1,optional:n.kind==="optional",unique:r.column?.unique===!0,validator:t}},Se=e=>{const{tables:n}=e;return Object.entries(n).map(([t,r])=>({fields:Object.entries(r.shape).map(([o,s])=>ye(o,s)),name:t}))},Ae=(e,n)=>{const t=new Map(e.map(a=>[a.name,a])),r=a=>{const c=t.get(a);if(c===void 0)return new Set;const l=new Set;for(const d of c.fields)d.fkTable!==void 0&&d.fkTable!==a&&n.has(d.fkTable)&&l.add(d.fkTable);return l},o=[],s=new Set,i=[...n].filter(a=>t.has(a));for(;i.length>0;){const a=i.findIndex(d=>[...r(d)].every(g=>s.has(g))),c=a===-1?0:a,[l]=i.splice(c,1);if(l===void 0)break;o.push(l),s.add(l)}return o},xe=(e,n,t=new Set)=>{const r=new Map(e.map(i=>[i.name,i])),o=new Set(n),s=[...o];for(;s.length>0;){const i=s.pop();if(i===void 0)break;const a=r.get(i);if(a!==void 0&&!t.has(i))for(const c of a.fields)c.fkTable!==void 0&&c.fkTable!==i&&r.has(c.fkTable)&&!o.has(c.fkTable)&&(o.add(c.fkTable),s.push(c.fkTable))}return o},ne=e=>p(e).constraints??{},Te=[{generate:e=>u.email(e),keywords:["email"]},{generate:e=>u.firstName(e),keywords:["firstname"]},{generate:e=>u.lastName(e),keywords:["lastname","surname"]},{generate:e=>u.username(e),keywords:["username"]},{generate:e=>u.fullName(e),keywords:["name"]},{generate:e=>u.sentence(e,{max:5,min:2}),keywords:["title"]},{generate:e=>u.url(e),keywords:["url","link","image","avatar"]},{generate:e=>u.phoneNumber(e),keywords:["phone"]},{generate:e=>u.paragraph(e),keywords:["description","bio","body","content","text"]},{generate:e=>u.slug(e),keywords:["slug","key","code"]},{generate:e=>u.city(e),keywords:["city"]},{generate:e=>u.country(e),keywords:["country"]},{generate:e=>u.streetAddress(e),keywords:["address","street"]},{generate:e=>u.password(e),keywords:["password","secret","token"]}],Ie=new Set(["at","date","deadline","expires","expiry","since","timestamp","until"]),ke=/([a-z\d])([A-Z])/gu,$e=/[\s_-]+/u,Oe=e=>e.replaceAll(ke,"$1 $2").split($e).filter(n=>n!=="").map(n=>n.toLowerCase()),te={max:1e3,min:0},re={max:1e6,min:0},Ne=4320*60*60*1e3,P=(e,n,t)=>{const{maximum:r,minimum:o}=e;if(r!==void 0&&o!==void 0&&o>r)throw new y("INTERNAL",`Seed constraint error for field "${t}": minimum (${String(o)}) > maximum (${String(r)}). Adjust the schema constraints.`);const s=n.max-n.min;return{max:r??(o!==void 0&&o>n.max?o+s:n.max),min:o??(r!==void 0&&r<n.min?r-s:n.min)}},oe=e=>{const t=Oe(e).at(-1);return t!==void 0&&Ie.has(t)},G=(e,n)=>n-u.int(e,{max:Ne,min:0}),N=(e,n)=>{throw new y("INTERNAL",`@lunora/seed: column "${e}" ${n}. Supply a value via \`overrides\` (\`{ <table>: { ${e}: … } }\`) or restrict the run with \`only\` so the table is skipped.`)},q=(e,n)=>{const t=e===""?"x":e,r=Math.min(Math.max(t.length,n.min),n.max);return t.padEnd(r,t).slice(0,r)},T="@example.com",_="https://",_e=/[\s@]/gu,Ee=/[^a-z\d]/gu,Me=(e,n)=>{const t=e.lastIndexOf("@"),r=t>0?e.slice(t):"",o=r.length>0&&r.length<n.max?r:T,s=(t>0?e.slice(0,t):e).replaceAll(_e,"");return`${q(s,{max:n.max-o.length,min:Math.max(1,n.min-o.length)})}${o}`},De=(e,n)=>{const t=URL.canParse(e)?new URL(e).hostname.replaceAll(Ee,""):"";return`${_}${q(t,{max:n.max-_.length,min:Math.max(1,n.min-_.length)})}`},Re={email:{fit:Me,generate:e=>u.email(e),minimum:T.length+1},uri:{fit:De,generate:e=>u.url(e),minimum:_.length+1}},Le=(e,n)=>{const t=e.toLowerCase(),r=Te.find(o=>o.keywords.some(s=>t.includes(s)));return r===void 0?u.word(n):r.generate(n)},Pe=(e,n,t)=>{const{format:r,maxLength:o,minLength:s,pattern:i}=t;if(i!==void 0)return N(e,`is constrained to the pattern /${i}/, and the seeder cannot invent a value matching an arbitrary regular expression`);if(o!==void 0&&s!==void 0&&s>o)throw new y("INTERNAL",`Seed constraint error for field "${e}": minLength (${String(s)}) > maxLength (${String(o)}). Adjust the schema constraints.`);const a={max:o??Number.POSITIVE_INFINITY,min:s??0};if(r===void 0)return q(Le(e,n),a);const c=Re[r];if(c===void 0)return N(e,`declares format "${r}", which the seeder has no generator for`);if(a.max<c.minimum)return N(e,`declares format "${r}" with maxLength ${String(o)}, and the shortest ${r} value the seeder can build is ${String(c.minimum)} characters`);const l=c.generate(n);return l.length>=a.min&&l.length<=a.max?l:c.fit(l,a)},b=(e,n,t,r)=>{const o=X(e),s=ne(o);if(s.enum!==void 0&&s.enum.length>0)return u.oneOf(t,s.enum);switch(o.kind){case"any":return u.word(t);case"array":{const i=p(o).inner;return i===void 0?[]:u.times(t,[1,3],a=>b(i,n,a,r))}case"bigint":return u.int(t,P(s,re,n));case"boolean":return u.bool(t);case"bytes":return Array.from({length:8},(i,a)=>u.int([t,a],{max:255,min:0}));case"date":case"timestamp":return G(t,r);case"from":return N(n,"is a v.from() validator, whose external Standard Schema the seeder cannot introspect to invent a conforming value (give the column a concrete v.* type instead)");case"id":return u.uuid(t);case"literal":return p(o).value;case"null":return null;case"number":{if(s.maximum===void 0&&s.minimum===void 0&&oe(n))return G(t,r);const{max:i,min:a}=P(s,te,n);return!Number.isInteger(a)||!Number.isInteger(i)?u.float(t,{max:i,min:a}):u.int(t,{max:i,min:a})}case"object":{const i=p(o).shape??{};return Object.fromEntries(Object.entries(i).map(([a,c])=>[a,b(c,a,[t,a],r)]))}case"record":{const{keyValidator:i,valueValidator:a}=p(o),c=u.times(t,[1,3],l=>{const d=i===void 0?u.word(["k",l]):String(b(i,n,["k",l],r)),g=a===void 0?u.word(["v",l]):b(a,n,["v",l],r);return[d,g]});return Object.fromEntries(c)}case"storage":return`seed/${u.uuid(t)}`;case"string":return Pe(n,t,s);case"union":{const i=p(o).members??[],a=u.oneOf(t,i);return a===void 0?u.word(t):b(a,n,[t,"u"],r)}default:return u.word(t)}},R=1e6,qe=6e4,je=2**32,Ce=(e,n,t)=>{const r=[...e];for(let o=r.length-1;o>0;o-=1){const s=u.int([n,t,"unique-deal",o],{max:o,min:0}),i=r[o];r[o]=r[s],r[s]=i}return r},Ue=(e,n,t)=>{const{maxLength:r}=t,o=String(n),s=e.indexOf("@");if(s>0){const c=e.slice(0,s),l=e.slice(s),d=r===void 0?c.length:r-l.length-o.length-1;if(d>=0)return`${c.slice(0,d)}+${o}${l}`}if(t.format==="email"){const c=r===void 0?e.length:Math.max(0,r-T.length-o.length);return`${e.slice(0,c)}${o}${T}`}const i=`-${o}`,a=r===void 0?e.length:Math.max(0,r-i.length);return`${e.slice(0,a)}${i}`},E=(e,n,t)=>{const r=Ce(e,n,t);return{capacity:r.length,valueAt:o=>r[o%r.length]}},Fe=(e,n)=>{if(Number.isInteger(e)&&Number.isInteger(n)){const t=n-e+1;return{capacity:t,valueAt:r=>e+r%t}}return{capacity:R,valueAt:t=>e+t%R*(n-e)/R}},H=e=>({capacity:Number.POSITIVE_INFINITY,valueAt:n=>e-n*qe}),Ve={capacity:Number.POSITIVE_INFINITY,valueAt:e=>e},W={capacity:Number.POSITIVE_INFINITY,valueAt:(e,n)=>n()},z=(e,n,t)=>{if(e.maximum===void 0&&e.minimum===void 0)return Ve;const{max:r,min:o}=P(e,n,t);return Fe(o,r)},Q=(e,n,t)=>{throw new y("INTERNAL",`@lunora/seed: cannot generate unique values for "${e}"."${n}" — ${t}. Supply them via \`overrides\` (\`{ ${e}: { ${n}: … } }\`) or drop \`.unique()\` from the column.`)},Be=({format:e,maxLength:n})=>{if(n===void 0)return Number.POSITIVE_INFINITY;const t=e==="email"?T.length:1;return n<=t?0:10**(n-t)},Ye=(e,n,t)=>(e.pattern!==void 0&&Q(n,t,"the column is pattern-constrained, and an index-tagged value would no longer match it"),e.format!==void 0&&e.format!=="email"&&Q(n,t,`the column declares format "${e.format}", which an index-tagged value would no longer satisfy`),{capacity:Be(e),valueAt:(r,o)=>Ue(String(o()),r,e)}),Ge=(e,n,t)=>E(e,n,t),He=(e,n)=>{const{now:t,table:r}=n,o=ne(e.validator),s=o.enum;if(Array.isArray(s)&&s.length>0)return E(s,r,e.name);switch(e.kind){case"any":case"string":return Ye(o,r,e.name);case"bigint":return z(o,re,e.name);case"boolean":return E([!1,!0],r,e.name);case"bytes":return{capacity:je,valueAt:i=>[Math.floor(i/16777216)%256,Math.floor(i/65536)%256,Math.floor(i/256)%256,i%256,...Array.from({length:4},(a,c)=>u.int([r,e.name,"unique-bytes",c],{max:255,min:0}))]};case"date":case"timestamp":return H(t);case"literal":return{capacity:1,valueAt:()=>p(e.validator).value};case"number":return o.maximum===void 0&&o.minimum===void 0&&oe(e.name)?H(t):z(o,te,e.name);case"union":{const i=p(e.validator).members??[],a=i.filter(c=>c.kind==="literal").map(c=>p(c).value);return a.length>0&&a.length===i.length?E(a,r,e.name):W}default:return W}},We=(e,n)=>typeof e=="function"?e(n):e,ze=(e,n)=>{if(!e.optional)return e.nullable?null:u.uuid(n)},se=(e,n,t,r,o)=>{const s=e.fkTable,i=s===n?(r.get(n)??[]).slice(0,t):r.get(s)??[],a=o[s]??[];return a.length===0?i:[...i,...a]},Qe=(e,n,t,r)=>{const o=[...r[e]??[],...(t.get(e)??[]).slice(0,n)];return o[n]??o.at(-1)},J=(e,n,t,r)=>[e,n,t,r],Je=(e,n)=>{const{existingIds:t,idsByTable:r,index:o,input:s,localIndex:i,now:a,table:c,uniqueDeals:l}=n;if(e.fkTable!==void 0){const g=se(e,c,i,r,t);if(g.length===0)return ze(e,s);if(!e.unique)return u.oneOf(s,g);const I=l.get(e.name);return I===void 0?Qe(c,i,r,t):I.valueAt(o,()=>u.oneOf(s,g))}if(e.hasServerDefault)return;const d=e.unique?l.get(e.name):void 0;return d!==void 0?d.valueAt(o,()=>b(e.validator,e.name,s,a)):b(e.validator,e.name,s,a)},Ke=(e,n,t,r)=>{if(!(t(e).length===0||Object.hasOwn(r,e.name)))throw new y("BAD_REQUEST",`cannot seed into the non-empty table "${n}": unique self-referencing column "${e.name}" cannot be dealt across calls, because the values already stored in it are not knowable from the row ids. Seed the table in one call, or supply "${e.name}" yourself.`)},Ze=(e,n,t,r,o,s,i)=>{const a=new Map;for(const c of e){if(!c.unique||c.hasServerDefault&&c.fkTable===void 0)continue;if(c.fkTable===n){Ke(c,n,i,s);continue}const l=c.fkTable===void 0?void 0:i(c);if(l?.length===0)continue;const d=l===void 0?He(c,{now:o,table:n}):Ge(l,n,c.name),g=r+t;if(g>d.capacity&&!Object.hasOwn(s,c.name))throw new y("BAD_REQUEST",`cannot seed ${String(g)} rows into "${n}": unique column "${c.name}" has only ${String(d.capacity)} possible values`);a.set(c.name,d)}return a},tn=(e,n={})=>{const{counts:t={},defaultCount:r=10,existingIds:o={},indexOffset:s={},now:i=Date.now(),only:a,overrides:c={},seed:l=0}=n;he(l);const d=Se(e);if(a!==void 0){const m=new Set(d.map(w=>w.name));for(const w of a)if(!m.has(w)){const A=d.map(k=>k.name).join(", ");throw new y("BAD_REQUEST",`unknown table "${w}" in seed \`only\` — schema defines: ${A||"(no tables)"}`)}}const g=new Set(a??d.map(m=>m.name)),I=new Set(Object.keys(o).filter(m=>!g.has(m)&&(o[m]??[]).length>0)),ae=new Set([...xe(d,g,I)].filter(m=>g.has(m)||(o[m]??[]).length===0)),ie=Ae(d,ae),ce=new Map(d.map(m=>[m.name,m])),M=new Map,j={},C=[];for(const m of ie){const w=ce.get(m);if(w===void 0)continue;const A=c[m]??{},k=t[m]??r,U=s[m]??0,ue=Ze(w.fields,m,k,U,i,A,S=>se(S,m,0,M,o)),F=[];M.set(m,F);const D=[];j[m]=D;for(let S=0;S<k;S+=1){const $=U+S,x={},V=(v,le)=>{if(Object.hasOwn(A,v)){const Y=We(A[v],{field:v,index:$,row:x,store:j,table:m});if(Y!==void 0){x[v]=Y;return}}const B=le();B!==void 0&&(x[v]=B)};V("_id",()=>u.uuid(J(l,m,$,"_id"))),F.push(x._id);for(const v of w.fields)V(v.name,()=>Je(v,{existingIds:o,idsByTable:M,index:$,input:J(l,m,$,v.name),localIndex:S,now:i,table:m,uniqueDeals:ue}));D.push(x)}C.push({rows:D,table:m})}return C};export{he as a,u as c,Se as i,tn as s};
@@ -1 +0,0 @@
1
- import{s as a}from"./plan-B3lKLUhz.mjs";export{a as seedPlan};