@isaijs/core 0.1.5 → 0.1.8

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.
Files changed (30) hide show
  1. package/dist/adapters/storage/r2/index.d.ts +3 -0
  2. package/dist/adapters/storage/r2/index.js +13 -2
  3. package/dist/auth/index.d.ts +2 -2
  4. package/dist/auth/index.js +2 -2
  5. package/dist/cli/index.js +79 -11
  6. package/dist/client/index.d.ts +4 -0
  7. package/dist/client/index.js +18 -13
  8. package/dist/{create-auth-BHdX68Ng.js → create-auth-BCvMwlVD.js} +18 -10
  9. package/dist/{data-layer-C5oJZ8V1.js → data-layer-CVCFjPyv.js} +15 -10
  10. package/dist/{data-layer-43pme0Gy.d.ts → data-layer-nvm_ivGQ.d.ts} +6 -2
  11. package/dist/db/index.d.ts +2 -2
  12. package/dist/db/index.js +5 -5
  13. package/dist/{index-D0FEnMhn.d.ts → index-Bb9Px6SR.d.ts} +116 -22
  14. package/dist/{index-CYcklQ1l.d.ts → index-CaR3hb0i.d.ts} +5 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +169 -79
  17. package/dist/{make-ByA-84h8.js → make-jMJABOWI.js} +21 -3
  18. package/dist/{makers-P9kg68C8.js → makers-A0Pj6Tda.js} +3 -3
  19. package/dist/{migrator-B25prc9n.js → migrator-DxTpns_-.js} +174 -30
  20. package/dist/{model-Dc3HunKb.js → model-6pJBqqGd.js} +31 -1
  21. package/dist/{policies-KsqmlX67.js → policies-C_oiSgmY.js} +1 -1
  22. package/dist/{resource-BQK7bkPQ.js → resource-BIMAiOre.js} +8 -6
  23. package/dist/{tables-DgSwge7s.js → tables-DjW96Ubl.js} +1 -1
  24. package/dist/testing/index.d.ts +1 -1
  25. package/dist/testing/index.js +4 -4
  26. package/dist/{types-BgOkJy5d.d.ts → types-D5SIVeej.d.ts} +59 -2
  27. package/dist/validation/index.d.ts +2 -2
  28. package/dist/validation/index.js +1 -1
  29. package/dist/{validation-Cppmd0RV.js → validation-C3MCRBQO.js} +26 -3
  30. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { G as ValidationError, M as id } from "./validation-Cppmd0RV.js";
2
- import { A as mapOperands, B as aroundDelete, F as unbound, H as aroundUpsert, I as fromBase64url$1, K as wrap, L as toBase64url, M as readsNow, N as readsOtherRows, O as storageOf, P as tableTotals, R as actionEntry, V as aroundUpdate, W as assertNotLog, _ as Expr, c as rowOf$1, h as Condition, i as columnNode, k as fieldsOf, l as IMPLICIT_FIELDS, n as assertNotEncrypted, o as fieldOf, r as columnName, v as Now, w as conditionNode, z as aroundCreate } from "./model-Dc3HunKb.js";
1
+ import { P as id, q as ValidationError } from "./validation-C3MCRBQO.js";
2
+ import { A as mapOperands, B as aroundDelete, F as unbound, H as aroundUpsert, I as fromBase64url$1, K as wrap, L as toBase64url, M as readsNow, N as readsOtherRows, O as storageOf, P as tableTotals, R as actionEntry, V as aroundUpdate, W as assertNotLog, _ as Expr, c as rowOf$1, h as Condition, i as columnNode, k as fieldsOf, l as IMPLICIT_FIELDS, n as assertNotEncrypted, o as fieldOf, r as columnName, v as Now, w as conditionNode, z as aroundCreate } from "./model-6pJBqqGd.js";
3
3
  import { r as ulid } from "./case-DpXlJCL9.js";
4
4
  import { t as NotImplementedError } from "./errors-mCNLlXg9.js";
5
5
  //#region src/db/errors.ts
@@ -814,13 +814,28 @@ function onConflict(model, conflict, writer) {
814
814
  if ("fromInsert" in action) return `${target} DO UPDATE SET ${action.fromInsert.map((field) => `${column(model, field)} = excluded.${column(model, field)}`).join(", ")}`;
815
815
  return `${target} DO UPDATE SET ${writer.set(action.set)}`;
816
816
  }
817
+ /**
818
+ * A select. With a limit, computed fields are worked out for the rows it returns only: an inner
819
+ * query picks them (filtered, sorted, limited), and the outer one adds the computed values. SQLite
820
+ * otherwise works out every select-list subquery for every matching row before it sorts (a page of
821
+ * 24 products would price all 800 in their category), and each of those reads is billed. Not when
822
+ * the order is by a computed value: the inner query couldn't sort by it.
823
+ */
817
824
  function select(model, query, context) {
818
825
  return withinLimit(context, (mode) => {
819
826
  const writer = new Writer(model, mode, context);
820
- let text = `SELECT ${writer.selection()} FROM ${quote(model.table)}${writer.where(query.where)}`;
821
- if (query.order.length > 0) text += ` ORDER BY ${query.order.map(({ field, direction }) => `${column(model, field)} ${direction.toUpperCase()}`).join(", ")}`;
822
- if (query.limit !== void 0 || query.offset !== void 0) text += ` LIMIT ${writer.value(query.limit ?? -1)}`;
827
+ const table = quote(model.table);
828
+ const order = query.order.length > 0 ? ` ORDER BY ${query.order.map(({ field, direction }) => `${column(model, field)} ${direction.toUpperCase()}`).join(", ")}` : "";
829
+ const limited = query.limit !== void 0 || query.offset !== void 0;
830
+ const computed = Object.values(model.fields).some((spec) => spec.computed);
831
+ const sortsByComputed = query.order.some(({ field }) => model.fields[field]?.computed);
832
+ const selection = writer.selection();
833
+ const inner = limited && computed && !sortsByComputed;
834
+ let text = inner ? `SELECT * FROM ${table}` : `SELECT ${selection} FROM ${table}`;
835
+ text += writer.where(query.where) + order;
836
+ if (limited) text += ` LIMIT ${writer.value(query.limit ?? -1)}`;
823
837
  if (query.offset !== void 0) text += ` OFFSET ${writer.value(query.offset)}`;
838
+ if (inner) text = `SELECT ${selection} FROM (${text}) AS ${table}${order}`;
824
839
  return writer.sql(text);
825
840
  });
826
841
  }
@@ -1037,7 +1052,23 @@ function columnDefinition(column) {
1037
1052
  return definition;
1038
1053
  }
1039
1054
  function createTable(table) {
1040
- return `CREATE TABLE ${quote(table.name)} (${table.columns.map(columnDefinition).join(", ")})`;
1055
+ const columns = table.columns.map(columnDefinition).join(", ");
1056
+ return `CREATE TABLE ${quote(table.name)} (${columns})${table.withoutRowid ? " WITHOUT ROWID" : ""}`;
1057
+ }
1058
+ /** Where a rebuilt table's old rows wait while the new table is filled. */
1059
+ const aside = (table) => quote(`_isai_rebuild_${table}`);
1060
+ /** `operations.ts` → rebuild, in SQLite: one atomic step, its foreign keys checked at the end. */
1061
+ function rebuild(operation) {
1062
+ const { tables, copy, triggers } = operation;
1063
+ const columns = (table) => (copy[table] ?? []).map(quote).join(", ");
1064
+ return [
1065
+ "PRAGMA defer_foreign_keys = ON",
1066
+ ...triggers.map((name) => `DROP TRIGGER IF EXISTS ${quote(name)}`),
1067
+ ...tables.map((table) => `ALTER TABLE ${quote(table.name)} RENAME TO ${aside(table.name)}`),
1068
+ ...tables.map(createTable),
1069
+ ...tables.filter((table) => (copy[table.name] ?? []).length > 0).map((table) => `INSERT INTO ${quote(table.name)} (${columns(table.name)}) SELECT ${columns(table.name)} FROM ${aside(table.name)}`),
1070
+ ...[...tables].reverse().map((table) => `DROP TABLE ${aside(table.name)}`)
1071
+ ];
1041
1072
  }
1042
1073
  function createIndex(table, index) {
1043
1074
  const columns = index.columns.map(quote).join(", ");
@@ -1229,6 +1260,7 @@ function render(operation) {
1229
1260
  case "dropRule": return [`DROP ${operation.rule.kind.toUpperCase()} IF EXISTS ${quote(operation.rule.name)}`];
1230
1261
  case "dropColumn": return [`ALTER TABLE ${quote(operation.table)} DROP COLUMN ${quote(operation.column.name)}`, ...dropCounter(operation.table, operation.column)];
1231
1262
  case "dropTable": return [`DROP TABLE ${quote(operation.table.name)}`, ...operation.table.columns.flatMap((column) => dropCounter(operation.table.name, column))];
1263
+ case "rebuild": return rebuild(operation);
1232
1264
  }
1233
1265
  }
1234
1266
  //#endregion
@@ -1500,10 +1532,15 @@ var Engine = class Engine {
1500
1532
  };
1501
1533
  //#endregion
1502
1534
  //#region src/db/orm/include.ts
1535
+ /** How the boundary gives a query its `IncludeRule`: internal, so the app's API never shows it. */
1536
+ const withIncludeRule = Symbol("isai.withIncludeRule");
1503
1537
  /** The rows of `model` whose `field` is one of `values`. */
1504
- async function rowsWhereIn(engine, model, field, values) {
1538
+ async function rowsWhereIn(engine, model, field, values, rule) {
1505
1539
  const spec = fieldOf(model, field);
1506
1540
  if (!spec) throw new TypeError(`isai: ${model.name} has no field "${field}"`);
1541
+ if (values.length === 0) return [];
1542
+ const allowed = rule ? await rule(model) : void 0;
1543
+ if (allowed === false) return [];
1507
1544
  const chunk = engine.driver.capabilities.maxBoundParams;
1508
1545
  const found = [];
1509
1546
  for (let start = 0; start < values.length; start += chunk) {
@@ -1513,7 +1550,7 @@ async function rowsWhereIn(engine, model, field, values) {
1513
1550
  values: values.slice(start, start + chunk)
1514
1551
  };
1515
1552
  found.push(...await engine.read(model, {
1516
- where: [where],
1553
+ where: allowed ? [where, allowed] : [where],
1517
1554
  order: [{
1518
1555
  field: "id",
1519
1556
  direction: "asc"
@@ -1535,27 +1572,27 @@ function byHead(paths) {
1535
1572
  }
1536
1573
  return groups;
1537
1574
  }
1538
- async function loadIncludes(engine, model, registry, rows, paths) {
1575
+ async function loadIncludes(engine, model, registry, rows, paths, rule) {
1539
1576
  if (rows.length === 0 || paths.length === 0) return;
1540
1577
  for (const [head, tails] of byHead(paths)) {
1541
1578
  const ref = fieldOf(model, head)?.ref;
1542
1579
  if (ref) {
1543
1580
  const target = registry.get(ref.model);
1544
- const found = await rowsWhereIn(engine, target, "id", [...new Set(rows.map((row) => row[head]).filter((id) => id !== null && id !== void 0))]);
1545
- await loadIncludes(engine, target, registry, found, tails);
1581
+ const found = await rowsWhereIn(engine, target, "id", [...new Set(rows.map((row) => row[head]).filter((id) => id !== null && id !== void 0))], rule);
1582
+ await loadIncludes(engine, target, registry, found, tails, rule);
1546
1583
  const byId = new Map(found.map((row) => [row.id, row]));
1547
1584
  for (const row of rows) row[head] = row[head] === null ? null : byId.get(row[head]) ?? null;
1548
1585
  continue;
1549
1586
  }
1550
1587
  const relation = registry.relation(model.name, head);
1551
1588
  if (!relation) throw new TypeError(`isai: ${model.name} has no ref or relation named "${head}"`);
1552
- const found = await rowsWhereIn(engine, relation.model, relation.field, rows.map((row) => row.id));
1589
+ const found = await rowsWhereIn(engine, relation.model, relation.field, rows.map((row) => row.id), rule);
1553
1590
  const groups = /* @__PURE__ */ new Map();
1554
1591
  for (const row of found) {
1555
1592
  const owner = row[relation.field];
1556
1593
  groups.set(owner, [...groups.get(owner) ?? [], row]);
1557
1594
  }
1558
- await loadIncludes(engine, relation.model, registry, found, tails);
1595
+ await loadIncludes(engine, relation.model, registry, found, tails, rule);
1559
1596
  for (const row of rows) row[head] = groups.get(row.id) ?? [];
1560
1597
  }
1561
1598
  }
@@ -1707,15 +1744,21 @@ var Query = class Query {
1707
1744
  target;
1708
1745
  query;
1709
1746
  paths;
1747
+ rule;
1710
1748
  constructor(engine, target, query = {
1711
1749
  where: [],
1712
1750
  order: [],
1713
1751
  limit: void 0
1714
- }, paths = []) {
1752
+ }, paths = [], rule) {
1715
1753
  this.engine = engine;
1716
1754
  this.target = target;
1717
1755
  this.query = query;
1718
1756
  this.paths = paths;
1757
+ this.rule = rule;
1758
+ }
1759
+ /** The same query, its includes held to `rule` (the boundary's; `./include`). */
1760
+ [withIncludeRule](rule) {
1761
+ return new Query(this.engine, this.target, this.query, this.paths, rule);
1719
1762
  }
1720
1763
  /** Repeated calls are ANDed. */
1721
1764
  where(build) {
@@ -1739,7 +1782,7 @@ var Query = class Query {
1739
1782
  }
1740
1783
  /** Loads related rows: refs in place of their ids, relations as lists (`"fest"`, `"registrations.members"`). */
1741
1784
  include(...paths) {
1742
- return new Query(this.engine, this.target, this.query, [...this.paths, ...paths]);
1785
+ return new Query(this.engine, this.target, this.query, [...this.paths, ...paths], this.rule);
1743
1786
  }
1744
1787
  async all() {
1745
1788
  return this.fetch({
@@ -1843,7 +1886,7 @@ var Query = class Query {
1843
1886
  return this.withIncludes(await this.engine.read(this.target.spec, query));
1844
1887
  }
1845
1888
  async withIncludes(rows) {
1846
- await loadIncludes(this.engine, this.target.spec, this.target.registry, rows, this.paths);
1889
+ await loadIncludes(this.engine, this.target.spec, this.target.registry, rows, this.paths, this.rule);
1847
1890
  return rows;
1848
1891
  }
1849
1892
  /** The first row (by the query's order), or `undefined`. */
@@ -1870,7 +1913,7 @@ var Query = class Query {
1870
1913
  return new Query(this.engine, this.target, {
1871
1914
  ...this.query,
1872
1915
  ...change
1873
- }, this.paths);
1916
+ }, this.paths, this.rule);
1874
1917
  }
1875
1918
  };
1876
1919
  /** Kinds a group key may be: never free text. */
@@ -2885,7 +2928,6 @@ var UnsafeMigration = class extends Error {
2885
2928
  const COPY = "add a new field, copy the values in a data step, then remove the old one";
2886
2929
  const KEPT = "add a new field; the database fills it, then remove the old one";
2887
2930
  const PLAIN = "add a plain field, copy the values in a data step, then remove the old one";
2888
- const REBUILD = "it needs a table rebuild, which isn't supported yet; add a new field instead";
2889
2931
  const CLEANUP = "pick another name, or drop the retired one first with `isai cleanup`";
2890
2932
  const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
2891
2933
  /** Why a column can't stay as it is when its field changes, and what to do instead; undefined when it can. */
@@ -2905,9 +2947,88 @@ function columnChange(before, after) {
2905
2947
  change: `it's no longer kept by the database (${before.maintained})`,
2906
2948
  advice: PLAIN
2907
2949
  };
2908
- if (!same(before.references, after.references)) return {
2909
- change: before.references && after.references && before.references.table === after.references.table ? `its onDelete changed from ${before.references.onDelete} to ${after.references.onDelete}` : "its reference changed",
2910
- advice: REBUILD
2950
+ }
2951
+ /** Every table in the database under a schema: live ones with their retired columns, and retired tables. */
2952
+ function physicalTables(schema, alsoRetired = []) {
2953
+ const retired = [...schema.retired, ...alsoRetired];
2954
+ const tables = /* @__PURE__ */ new Map();
2955
+ for (const table of Object.values(schema.tables)) {
2956
+ const gone = retired.flatMap((r) => r.kind === "column" && r.table === table.name ? [r.column] : []);
2957
+ tables.set(table.name, {
2958
+ table,
2959
+ columns: [...table.columns, ...gone]
2960
+ });
2961
+ }
2962
+ for (const r of retired) if (r.kind === "table" && !tables.has(r.table.name)) tables.set(r.table.name, {
2963
+ table: r.table,
2964
+ columns: r.table.columns
2965
+ });
2966
+ return tables;
2967
+ }
2968
+ const referenced = (columns) => new Set(columns.flatMap((c) => c.references ? [c.references.table] : []));
2969
+ /**
2970
+ * The rebuild these schemas need, if any: the tables whose storage or a reference changed, with
2971
+ * every table that references one of them (live or retired, before or after), so no foreign key is
2972
+ * left pointing at a table that was dropped (`operations.ts` → rebuild).
2973
+ */
2974
+ function rebuildOf(previous, next, retiredNow, unsafe) {
2975
+ const before = physicalTables(previous);
2976
+ const after = physicalTables({
2977
+ ...next,
2978
+ retired: previous.retired
2979
+ }, retiredNow);
2980
+ const roots = Object.values(next.tables).filter((table) => {
2981
+ const was = previous.tables[table.name];
2982
+ if (!was) return false;
2983
+ if (Boolean(was.withoutRowid) !== Boolean(table.withoutRowid)) return true;
2984
+ const references = new Map(was.columns.map((c) => [c.name, c.references]));
2985
+ return table.columns.some((c) => references.has(c.name) && !same(references.get(c.name), c.references));
2986
+ });
2987
+ if (roots.length === 0) return void 0;
2988
+ const members = new Set(roots.map((table) => table.name));
2989
+ for (let grew = true; grew;) {
2990
+ grew = false;
2991
+ for (const [name, table] of after) {
2992
+ if (members.has(name) || !before.has(name)) continue;
2993
+ if ([.../* @__PURE__ */ new Set([...referenced(table.columns), ...referenced(before.get(name)?.columns ?? [])])].some((ref) => members.has(ref))) {
2994
+ members.add(name);
2995
+ grew = true;
2996
+ }
2997
+ }
2998
+ }
2999
+ const ordered = [];
3000
+ const pending = new Set(members);
3001
+ while (pending.size > 0) {
3002
+ const ready = [...pending].filter((name) => [...referenced(after.get(name)?.columns ?? [])].every((ref) => ref === name || !pending.has(ref)));
3003
+ if (ready.length === 0) {
3004
+ const tables = [...pending].sort();
3005
+ unsafe.push({
3006
+ model: after.get(tables[0] ?? "")?.table.model ?? "",
3007
+ change: `its table is rebuilt, and ${tables.join(", ")} reference each other`,
3008
+ advice: "break the cycle (make one of the refs a plain field) before the change that rebuilds them"
3009
+ });
3010
+ return;
3011
+ }
3012
+ for (const name of ready.sort()) {
3013
+ ordered.push(name);
3014
+ pending.delete(name);
3015
+ }
3016
+ }
3017
+ const tables = ordered.map((name) => {
3018
+ const { table, columns } = after.get(name);
3019
+ return {
3020
+ ...table,
3021
+ columns
3022
+ };
3023
+ });
3024
+ return {
3025
+ op: "rebuild",
3026
+ tables,
3027
+ copy: Object.fromEntries(tables.map((table) => {
3028
+ const had = new Set(before.get(table.name)?.columns.map((c) => c.name));
3029
+ return [table.name, table.columns.flatMap((c) => had.has(c.name) ? [c.name] : [])];
3030
+ })),
3031
+ triggers: []
2911
3032
  };
2912
3033
  }
2913
3034
  /** A dropped column and an added one of the same kind, in one model: probably a rename. */
@@ -2998,12 +3119,18 @@ function diff(previous, next, since) {
2998
3119
  index
2999
3120
  });
3000
3121
  }
3122
+ const rebuild = rebuildOf(previous, next, retired, unsafe);
3001
3123
  if (unsafe.length > 0) throw new UnsafeMigration(unsafe);
3124
+ const rebuilt = new Set(rebuild?.tables.map((table) => table.name));
3002
3125
  return {
3003
- operations: [...created.map((table) => ({
3004
- op: "createTable",
3005
- table
3006
- })), ...addColumns],
3126
+ operations: [
3127
+ ...rebuild ? [rebuild] : [],
3128
+ ...created.map((table) => ({
3129
+ op: "createTable",
3130
+ table
3131
+ })),
3132
+ ...addColumns.filter((operation) => operation.op !== "addColumn" || !rebuilt.has(operation.table))
3133
+ ],
3007
3134
  retired,
3008
3135
  renames
3009
3136
  };
@@ -3154,7 +3281,15 @@ function tableOf(spec, registry, tables) {
3154
3281
  unique: true
3155
3282
  };
3156
3283
  });
3157
- const refIndexes = columns.filter((c) => c.references && !uniques.some((u) => u.columns[0] === c.name)).map((c) => ({
3284
+ const declared = spec.indexes.map((index) => {
3285
+ const columns = index.fields.map(column);
3286
+ return {
3287
+ name: `${spec.table}_${columns.join("_")}_index`,
3288
+ columns,
3289
+ unique: false
3290
+ };
3291
+ });
3292
+ const refIndexes = columns.filter((c) => c.references && ![...uniques, ...declared].some((index) => index.columns[0] === c.name)).map((c) => ({
3158
3293
  name: `${spec.table}_${c.name}_index`,
3159
3294
  columns: [c.name],
3160
3295
  unique: false
@@ -3169,7 +3304,11 @@ function tableOf(spec, registry, tables) {
3169
3304
  name: spec.table,
3170
3305
  model: spec.name,
3171
3306
  columns,
3172
- indexes: [...uniques, ...refIndexes],
3307
+ indexes: [
3308
+ ...uniques,
3309
+ ...declared,
3310
+ ...refIndexes
3311
+ ],
3173
3312
  guards: [...checks, ...limitGuards(spec)],
3174
3313
  counts: countsOf(spec, registry),
3175
3314
  copies: copiesOf(spec, registry),
@@ -3177,7 +3316,8 @@ function tableOf(spec, registry, tables) {
3177
3316
  field,
3178
3317
  column: columnName(field, fieldSpec),
3179
3318
  transitions: fieldSpec.flow
3180
- }] : [])
3319
+ }] : []),
3320
+ withoutRowid: true
3181
3321
  };
3182
3322
  }
3183
3323
  function snapshotOf(models) {
@@ -3209,7 +3349,11 @@ function generateMigration(options) {
3209
3349
  const { previous, dialect, name, clock = systemClock } = options;
3210
3350
  const live = snapshotOf(options.models);
3211
3351
  const { operations, retired, renames } = diff(previous, live, dayOf(clock.now()));
3212
- const statements = operations.flatMap((operation) => dialect.render(operation));
3352
+ const triggers = dialect.migrations.rules(previous).flatMap((rule) => rule.kind === "trigger" ? [rule.name] : []);
3353
+ const statements = operations.map((operation) => operation.op === "rebuild" ? {
3354
+ ...operation,
3355
+ triggers
3356
+ } : operation).flatMap((operation) => dialect.render(operation));
3213
3357
  const last = statements.length > 0 ? name : previous.last;
3214
3358
  const stillRetired = previous.retired.filter((r) => r.kind !== "index" || !live.tables[r.table]?.indexes.some((index) => index.name === r.index.name));
3215
3359
  const snapshot = {
@@ -3605,4 +3749,4 @@ async function migrateFresh(driver, models, options = {}) {
3605
3749
  await sync(driver, manifest, options);
3606
3750
  }
3607
3751
  //#endregion
3608
- export { planUpdate as A, NeedsDatabase as B, database as C, recordAction as D, forUpkeep as E, dialectFor as F, newSecretKey as G, fixedClock as H, sqlite as I, NotFound as J, CheckFailed as K, PLATFORM as L, Query as M, conditionOf as N, withTableTotals as O, modelOfRow as P, TENANT as R, clockOf as S, driverOf as T, systemClock as U, evaluate as V, utc as W, UniqueFailed as X, ReferenceFailed as Y, TODO as _, currentRelease as a, Database as b, rollback as c, applyRules as d, dayOf as f, UnsafeMigration as g, snapshotOf as h, acquireLock as i, keyCondition as j, planInsert as k, setReleaseStatus as l, emptySnapshot as m, migrateFresh as n, holdOf as o, generateMigration as p, DatabaseError as q, sync as r, releaseLock as s, migrate as t, startRelease as u, dataStep as v, decodeStored as w, Transaction as x, MigrationError as y, checkTenancy as z };
3752
+ export { planUpdate as A, checkTenancy as B, database as C, recordAction as D, forUpkeep as E, modelOfRow as F, utc as G, evaluate as H, dialectFor as I, DatabaseError as J, newSecretKey as K, sqlite as L, Query as M, conditionOf as N, withTableTotals as O, withIncludeRule as P, PLATFORM as R, clockOf as S, driverOf as T, fixedClock as U, NeedsDatabase as V, systemClock as W, ReferenceFailed as X, NotFound as Y, UniqueFailed as Z, TODO as _, currentRelease as a, Database as b, rollback as c, applyRules as d, dayOf as f, UnsafeMigration as g, snapshotOf as h, acquireLock as i, keyCondition as j, planInsert as k, setReleaseStatus as l, emptySnapshot as m, migrateFresh as n, holdOf as o, generateMigration as p, CheckFailed as q, sync as r, releaseLock as s, migrate as t, startRelease as u, dataStep as v, decodeStored as w, Transaction as x, MigrationError as y, TENANT as z };
@@ -1,4 +1,4 @@
1
- import { A as fakeUlid, C as text, D as choice, E as url, F as quantity, I as sequence, M as id, N as int, O as date, P as money, R as list, S as subdomain, T as token, W as kind, _ as newToken, a as file, b as slug, c as image, d as json, f as urls, g as markdown, h as line, j as flag, k as datetime, l as images, n as phoneKind, p as email, s as files, u as define, v as personName, w as textKind, x as slugify, y as secret } from "./validation-Cppmd0RV.js";
1
+ import { A as date, B as list, C as slugify, D as token, E as textKind, F as int, I as money, K as kind, L as quantity, M as fakeUlid, N as flag, O as url, P as id, R as sequence, S as slug, T as text, _ as line, a as document, b as personName, d as images, f as define, h as email, j as datetime, k as choice, l as files, m as urls, n as phoneKind, o as documents, p as json, s as file, u as image, v as markdown, w as subdomain, x as secret, y as newToken } from "./validation-C3MCRBQO.js";
2
2
  import { n as snakeCase, r as ulid } from "./case-DpXlJCL9.js";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  //#region src/internal/scope.ts
@@ -920,6 +920,10 @@ function fieldKinds(defaults) {
920
920
  image: (options = {}) => field(image(options), "json"),
921
921
  files: (options = {}) => field(files(options), "json"),
922
922
  images: (options = {}) => field(images(options), "json"),
923
+ /** A PDF or image to print or read whole (never resized), up to 100 MB; `{ storage }` as for `file`. */
924
+ document: (options = {}) => field(document(options), "json"),
925
+ /** Up to 100 documents: an order's files. */
926
+ documents: (options = {}) => field(documents(options), "json"),
923
927
  /** A row of another model, by its id. Restricts deletes of that row unless `.cascade()` / `.setNull()`. */
924
928
  ref: (model, options = {}) => new Field(fieldSpec(id(`a ${model} id`), "text", { ref: {
925
929
  model,
@@ -1091,6 +1095,31 @@ var Model = class Model {
1091
1095
  });
1092
1096
  }
1093
1097
  /**
1098
+ * An index on these fields, in this order, for the lists the app runs: a list sorted by a field
1099
+ * (`.index("createdAt")`), or filtered by one field then sorted by another
1100
+ * (`.index("status", "createdAt")`). Refs index themselves, and traits bring the indexes their
1101
+ * behaviour needs (`publishable`: `publishedAt`; `ordered`: its group, then `position`), so most
1102
+ * models never call it. `isai check` warns when a resource sorts by a field no index leads with.
1103
+ * Each index costs a row written on every insert, and on each update of its fields.
1104
+ */
1105
+ index(...fields) {
1106
+ const name = this.spec.name;
1107
+ if (fields.length === 0) throw new TypeError(`isai: ${name}.index() takes one or more fields`);
1108
+ if (new Set(fields).size !== fields.length) throw new TypeError(`isai: ${name}.index() lists a field twice`);
1109
+ for (const field of fields) {
1110
+ const spec = this.spec.fields[field];
1111
+ if (!spec || spec.computed) throw new TypeError(`isai: ${name}.index() lists "${field}", which isn't one of ${name}'s stored fields`);
1112
+ assertNotEncrypted(this.spec, field, "indexed");
1113
+ }
1114
+ const key = fields.join(",");
1115
+ if (this.spec.indexes.some((index) => index.fields.join(",") === key)) throw new TypeError(`isai: ${name} already has an index on ${fields.join(", ")}`);
1116
+ if (this.spec.uniques.some((unique) => unique.fields.join(",") === key)) throw new TypeError(`isai: ${name} already has a unique on ${fields.join(", ")}, which is an index too`);
1117
+ return this.derive({
1118
+ ...this.spec,
1119
+ indexes: [...this.spec.indexes, { fields }]
1120
+ });
1121
+ }
1122
+ /**
1094
1123
  * The table it's stored in, when that isn't its name in snake_case: after the model was renamed,
1095
1124
  * so the data stays where it is (`model("Squad", {…}).table("team")`). Physical renames never happen.
1096
1125
  */
@@ -1302,6 +1331,7 @@ function defineModel(registry, name, declared, traits = [], origin = {}) {
1302
1331
  table: snakeCase(name),
1303
1332
  fields: withCopyPaths(name, specs, registry, () => spec),
1304
1333
  uniques,
1334
+ indexes: traits.flatMap((trait) => (trait.spec.indexes ?? []).map((fields) => ({ fields }))),
1305
1335
  checks: [],
1306
1336
  traits: names,
1307
1337
  ...origin.database === void 0 ? {} : { database: origin.database },
@@ -1,4 +1,4 @@
1
- import { G as isRecorded, _ as Expr, c as rowOf, h as Condition, j as nodesOf, r as columnName, w as conditionNode } from "./model-Dc3HunKb.js";
1
+ import { G as isRecorded, _ as Expr, c as rowOf, h as Condition, j as nodesOf, r as columnName, w as conditionNode } from "./model-6pJBqqGd.js";
2
2
  //#region src/auth/enforce.ts
3
3
  /**
4
4
  * Checking a policy: its action's condition, with the
@@ -1,7 +1,7 @@
1
- import { G as ValidationError, H as validate, V as toKind, z as object } from "./validation-Cppmd0RV.js";
2
- import { J as NotFound, K as CheckFailed, X as UniqueFailed, Y as ReferenceFailed } from "./migrator-B25prc9n.js";
3
- import { C as asCondition, _ as Expr, g as Embed, l as IMPLICIT_FIELDS, r as columnName, s as computedFieldOf } from "./model-Dc3HunKb.js";
4
- import { r as actionsOf } from "./policies-KsqmlX67.js";
1
+ import { U as toKind, V as object, W as validate, q as ValidationError } from "./validation-C3MCRBQO.js";
2
+ import { X as ReferenceFailed, Y as NotFound, Z as UniqueFailed, q as CheckFailed } from "./migrator-DxTpns_-.js";
3
+ import { C as asCondition, _ as Expr, g as Embed, l as IMPLICIT_FIELDS, r as columnName, s as computedFieldOf } from "./model-6pJBqqGd.js";
4
+ import { r as actionsOf } from "./policies-C_oiSgmY.js";
5
5
  import { n as RESERVED, t as LOOKUPS } from "./syntax-CrUZFrS7.js";
6
6
  //#region src/api/fail.ts
7
7
  /**
@@ -199,13 +199,15 @@ var Route = class {
199
199
  anonymous;
200
200
  handler;
201
201
  platform;
202
- constructor(method, path, input, anonymous, handler, platform = false) {
202
+ upload;
203
+ constructor(method, path, input, anonymous, handler, platform = false, upload) {
203
204
  this.method = method;
204
205
  this.path = path;
205
206
  this.input = input;
206
207
  this.anonymous = anonymous;
207
208
  this.handler = handler;
208
209
  this.platform = platform;
210
+ this.upload = upload;
209
211
  }
210
212
  };
211
213
  function declare(method) {
@@ -214,7 +216,7 @@ function declare(method) {
214
216
  const handler = typeof second === "function" ? second : third;
215
217
  if (!handler) throw new TypeError(`isai: ${method} ${path} has no handler`);
216
218
  if (!path.startsWith("/")) throw new TypeError(`isai: a route's path starts with "/" (${method} ${path})`);
217
- return new Route(method, path, options.input ?? {}, options.anonymous ?? false, handler, options.platform ?? false);
219
+ return new Route(method, path, options.input ?? {}, options.anonymous ?? false, handler, options.platform ?? false, options.upload);
218
220
  }
219
221
  return define;
220
222
  }
@@ -1,4 +1,4 @@
1
- import { a as defineModel, t as Model } from "./model-Dc3HunKb.js";
1
+ import { a as defineModel, t as Model } from "./model-6pJBqqGd.js";
2
2
  //#region src/db/schema/builtins.ts
3
3
  /**
4
4
  * Built-in tables: sets of models a part of isai owns, switched on
@@ -1,5 +1,5 @@
1
1
  import { a as DatabaseDriver, g as Storage } from "../index-Ce5p2-EB.js";
2
- import { I as Clock, L as FixedClock, R as fixedClock, V as AnyModel, i as Row, l as Overrides, p as Database, s as Makers, u as Seed } from "../types-BgOkJy5d.js";
2
+ import { I as Clock, L as FixedClock, R as fixedClock, V as AnyModel, i as Row, l as Overrides, p as Database, s as Makers, u as Seed } from "../types-D5SIVeej.js";
3
3
  //#region src/testing/conformance/database.d.ts
4
4
  /**
5
5
  * Makes the driver under test. Every call must return a driver on an **empty** database:
@@ -1,8 +1,8 @@
1
1
  import { t as __exportAll } from "../rolldown-runtime-D7D4PA-g.js";
2
- import { F as dialectFor, H as fixedClock, L as PLATFORM, R as TENANT, W as utc, a as currentRelease, b as Database, c as rollback, i as acquireLock, j as keyCondition, l as setReleaseStatus, m as emptySnapshot, n as migrateFresh, o as holdOf, p as generateMigration, r as sync, s as releaseLock, t as migrate, u as startRelease, v as dataStep, y as MigrationError } from "../migrator-B25prc9n.js";
3
- import { L as toBase64url, t as Model } from "../model-Dc3HunKb.js";
4
- import { t as createDataLayer } from "../data-layer-C5oJZ8V1.js";
5
- import { d as forgetGoogleKeys, r as authOf, s as GOOGLE, t as makers, y as seedAs } from "../makers-P9kg68C8.js";
2
+ import { G as utc, I as dialectFor, R as PLATFORM, U as fixedClock, a as currentRelease, b as Database, c as rollback, i as acquireLock, j as keyCondition, l as setReleaseStatus, m as emptySnapshot, n as migrateFresh, o as holdOf, p as generateMigration, r as sync, s as releaseLock, t as migrate, u as startRelease, v as dataStep, y as MigrationError, z as TENANT } from "../migrator-DxTpns_-.js";
3
+ import { L as toBase64url, t as Model } from "../model-6pJBqqGd.js";
4
+ import { t as createDataLayer } from "../data-layer-CVCFjPyv.js";
5
+ import { d as forgetGoogleKeys, r as authOf, s as GOOGLE, t as makers, y as seedAs } from "../makers-A0Pj6Tda.js";
6
6
  import { describe, expect, it } from "vitest";
7
7
  //#region src/testing/conformance/database.ts
8
8
  /**
@@ -1,5 +1,5 @@
1
1
  import { a as DatabaseDriver, f as Row$1, h as Sql, o as DialectName } from "./index-Ce5p2-EB.js";
2
- import { E as list, G as FileType, M as IdInput, T as Shape, Y as StoredFile, ct as Sensitivity, ft as StandardProps, g as KindDefinition, h as Definable, nt as AnyKind, ot as KindTypes, q as ImageType, st as Measure, w as Output, x as Input$2 } from "./index-CYcklQ1l.js";
2
+ import { E as list, G as FileType, M as IdInput, T as Shape, Y as StoredFile, ct as KindTypes, g as KindDefinition, h as Definable, it as AnyKind, lt as Measure, mt as StandardProps, q as ImageType, ut as Sensitivity, w as Output, x as Input$2 } from "./index-CaR3hb0i.js";
3
3
  //#region src/db/fields/slot.d.ts
4
4
  interface SlotSpec {
5
5
  /** Namespaced by module: `auth.owner`. */
@@ -296,6 +296,14 @@ declare function fieldKinds(defaults: () => SchemaDefaults): {
296
296
  readonly types?: readonly ImageType[];
297
297
  readonly storage?: string;
298
298
  }) => Field<StoredFile[], StoredFile[], false, false, {}>;
299
+ /** A PDF or image to print or read whole (never resized), up to 100 MB; `{ storage }` as for `file`. */
300
+ document: (options?: {
301
+ readonly storage?: string;
302
+ }) => Field<StoredFile, StoredFile, false, false, {}>;
303
+ /** Up to 100 documents: an order's files. */
304
+ documents: (options?: {
305
+ readonly storage?: string;
306
+ }) => Field<StoredFile[], StoredFile[], false, false, {}>;
299
307
  /** A row of another model, by its id. Restricts deletes of that row unless `.cascade()` / `.setNull()`. */
300
308
  ref: <const Model extends string, const Related extends string | undefined = undefined>(model: Model, options?: {
301
309
  readonly related?: Related;
@@ -673,6 +681,10 @@ declare class ModelRegistry {
673
681
  interface UniqueSpec {
674
682
  readonly fields: readonly string[];
675
683
  }
684
+ /** An index the model asks for (`.index(...)`, or a trait's), by field names, in order. */
685
+ interface IndexSpec {
686
+ readonly fields: readonly string[];
687
+ }
676
688
  interface CheckSpec {
677
689
  readonly name: string;
678
690
  readonly condition: ConditionNode;
@@ -685,6 +697,8 @@ interface ModelSpec {
685
697
  /** Every field, implicit ones first, in declaration order. */
686
698
  readonly fields: Readonly<Record<string, FieldSpec>>;
687
699
  readonly uniques: readonly UniqueSpec[];
700
+ /** Its plain indexes: its traits' first, then `.index(...)`. Refs' indexes are added by the schema. */
701
+ readonly indexes: readonly IndexSpec[];
688
702
  readonly checks: readonly CheckSpec[];
689
703
  /** The traits it took (`ordered`, `singleton`…), by name. */
690
704
  readonly traits: readonly string[];
@@ -773,6 +787,15 @@ declare class Model<Name extends string = string, F extends Fields = Fields, Uni
773
787
  get name(): Name;
774
788
  /** A unique combination of two or more fields (one field: `.unique()` on the field). */
775
789
  unique<const K extends readonly [keyof F & string, keyof F & string, ...(keyof F & string)[]]>(...fields: K): Model<Name, F, Uniques | K>;
790
+ /**
791
+ * An index on these fields, in this order, for the lists the app runs: a list sorted by a field
792
+ * (`.index("createdAt")`), or filtered by one field then sorted by another
793
+ * (`.index("status", "createdAt")`). Refs index themselves, and traits bring the indexes their
794
+ * behaviour needs (`publishable`: `publishedAt`; `ordered`: its group, then `position`), so most
795
+ * models never call it. `isai check` warns when a resource sorts by a field no index leads with.
796
+ * Each index costs a row written on every insert, and on each update of its fields.
797
+ */
798
+ index<const K extends readonly [keyof (ImplicitFields & F) & string, ...(keyof (ImplicitFields & F) & string)[]]>(...fields: K): Model<Name, F, Uniques>;
776
799
  /**
777
800
  * The table it's stored in, when that isn't its name in snake_case: after the model was renamed,
778
801
  * so the data stays where it is (`model("Squad", {…}).table("team")`). Physical renames never happen.
@@ -941,6 +964,12 @@ interface TableSnapshot {
941
964
  readonly counts: readonly CountSnapshot[];
942
965
  readonly copies: readonly CopySnapshot[];
943
966
  readonly flows: readonly FlowSnapshot[];
967
+ /**
968
+ * Its rows live in its primary key's index (`WITHOUT ROWID`): every model's table, since the id is
969
+ * text, so a plain table would also keep a hidden rowid and a second index on the id, one more row
970
+ * written per insert and read per lookup. Absent in snapshots from before, whose tables are rebuilt.
971
+ */
972
+ readonly withoutRowid?: true;
944
973
  }
945
974
  /**
946
975
  * Something the models no longer have, kept in the database until `isai cleanup` so that an
@@ -1013,6 +1042,25 @@ type Operation = {
1013
1042
  {
1014
1043
  readonly op: "dropTable";
1015
1044
  readonly table: TableSnapshot;
1045
+ } |
1046
+ /**
1047
+ * Tables made again (a new storage, a reference's `onDelete`), with every table that references
1048
+ * one of them, so no foreign key ever points at a dropped table. Cloudflare's SQLite can't turn
1049
+ * foreign keys off (`PRAGMA foreign_keys = OFF` is ignored), and dropping a referenced table
1050
+ * deletes its rows first, which would cascade into the rows that reference them. So: the old
1051
+ * tables are renamed aside, the new ones created and filled, and the old ones dropped children
1052
+ * first, when nothing references them any more. Rules (indexes, triggers) are dropped with them,
1053
+ * every trigger first (one naming a renamed table would stop the rename), and come back from the
1054
+ * schema after the step.
1055
+ */
1056
+ {
1057
+ readonly op: "rebuild";
1058
+ /** The tables as they become, retired columns included, in dependency order (parents first). */
1059
+ readonly tables: readonly TableSnapshot[];
1060
+ /** Each table's columns that hold data now, copied across. */
1061
+ readonly copy: Readonly<Record<string, readonly string[]>>;
1062
+ /** Every trigger the database has now, by name. */
1063
+ readonly triggers: readonly string[];
1016
1064
  };
1017
1065
  //#endregion
1018
1066
  //#region src/db/dialects/dialect.d.ts
@@ -1227,6 +1275,12 @@ declare class Engine {
1227
1275
  private secrets;
1228
1276
  }
1229
1277
  //#endregion
1278
+ //#region src/db/orm/include.d.ts
1279
+ /** The condition a model's related rows must meet, or `false` when none may be read. */
1280
+ type IncludeRule = (model: ModelSpec) => Promise<ConditionNode | false>;
1281
+ /** How the boundary gives a query its `IncludeRule`: internal, so the app's API never shows it. */
1282
+ declare const withIncludeRule: unique symbol;
1283
+ //#endregion
1230
1284
  //#region src/db/orm/page.d.ts
1231
1285
  interface PageOptions {
1232
1286
  /** Rows per page. */
@@ -1312,7 +1366,10 @@ declare class Query<M extends AnyModel, R> implements PromiseLike<R[]> {
1312
1366
  private readonly target;
1313
1367
  private readonly query;
1314
1368
  private readonly paths;
1315
- constructor(engine: Engine, target: M, query?: SelectQuery, paths?: readonly string[]);
1369
+ private readonly rule?;
1370
+ constructor(engine: Engine, target: M, query?: SelectQuery, paths?: readonly string[], rule?: IncludeRule | undefined);
1371
+ /** The same query, its includes held to `rule` (the boundary's; `./include`). */
1372
+ [withIncludeRule](rule: IncludeRule): Query<M, R>;
1316
1373
  /** Repeated calls are ANDed. */
1317
1374
  where(build: (row: RowOf<M>) => Condition): Query<M, R>;
1318
1375
  order(...keys: readonly OrderKey<M>[]): Query<M, R>;
@@ -1,3 +1,3 @@
1
1
  import { a as ValidationError, i as Result, n as IssueCode, r as Path, t as Issue } from "../issues-CKto95IW.js";
2
- import { $ as images, A as validate, B as money, C as ObjectShape, D as object, E as list, F as datetime, G as FileType, H as sequence, I as fakeUlid, J as SIZES_MAX, K as ImageSize, L as flag, M as IdInput, N as choice, O as serialize, P as date, Q as image, R as id, S as LIST_MAX, T as Shape, U as phone, V as quantity, W as phoneKind, X as file, Y as StoredFile, Z as files, _ as define, a as newToken, at as KindSpec, b as CarriesKind, c as slug, ct as Sensitivity, d as text, dt as StandardIssue, et as Fake, f as textKind, ft as StandardProps, g as KindDefinition, h as Definable, i as markdown, it as Kind, j as CalendarDate, k as toKind, l as slugify, lt as TypeText, m as url, mt as StandardSchemaV1, n as host, nt as AnyKind, o as personName, ot as KindTypes, p as token, pt as StandardResult, q as ImageType, r as line, rt as FileSpec, s as secret, st as Measure, t as email, tt as FakeContext, u as subdomain, ut as kind, v as json, w as Output, x as Input, y as urls, z as int } from "../index-CYcklQ1l.js";
3
- export { type AnyKind, type CalendarDate, type CarriesKind, type Definable, Fake, type FakeContext, type FileSpec, type FileType, type IdInput, type ImageSize, type ImageType, type Input, type Issue, type IssueCode, Kind, type KindDefinition, type KindSpec, type KindTypes, LIST_MAX, type Measure, type ObjectShape, type Output, type Path, type Result, SIZES_MAX, type Sensitivity, type Shape, type StandardIssue, type StandardProps, type StandardResult, type StandardSchemaV1, type StoredFile, type TypeText, ValidationError, choice, date, datetime, define, email, fakeUlid, file, files, flag, host, id, image, images, int, json, kind, line, list, markdown, money, newToken, object, personName, phone, phoneKind, quantity, secret, sequence, serialize, slug, slugify, subdomain, text, textKind, toKind, token, url, urls, validate };
2
+ import { $ as files, A as validate, B as money, C as ObjectShape, D as object, E as list, F as datetime, G as FileType, H as sequence, I as fakeUlid, J as SIZES_MAX, K as ImageSize, L as flag, M as IdInput, N as choice, O as serialize, P as date, Q as file, R as id, S as LIST_MAX, T as Shape, U as phone, V as quantity, W as phoneKind, X as document, Y as StoredFile, Z as documents, _ as define, a as newToken, at as FileSpec, b as CarriesKind, c as slug, ct as KindTypes, d as text, dt as TypeText, et as image, f as textKind, ft as kind, g as KindDefinition, gt as StandardSchemaV1, h as Definable, ht as StandardResult, i as markdown, it as AnyKind, j as CalendarDate, k as toKind, l as slugify, lt as Measure, m as url, mt as StandardProps, n as host, nt as Fake, o as personName, ot as Kind, p as token, pt as StandardIssue, q as ImageType, r as line, rt as FakeContext, s as secret, st as KindSpec, t as email, tt as images, u as subdomain, ut as Sensitivity, v as json, w as Output, x as Input, y as urls, z as int } from "../index-CaR3hb0i.js";
3
+ export { type AnyKind, type CalendarDate, type CarriesKind, type Definable, Fake, type FakeContext, type FileSpec, type FileType, type IdInput, type ImageSize, type ImageType, type Input, type Issue, type IssueCode, Kind, type KindDefinition, type KindSpec, type KindTypes, LIST_MAX, type Measure, type ObjectShape, type Output, type Path, type Result, SIZES_MAX, type Sensitivity, type Shape, type StandardIssue, type StandardProps, type StandardResult, type StandardSchemaV1, type StoredFile, type TypeText, ValidationError, choice, date, datetime, define, document, documents, email, fakeUlid, file, files, flag, host, id, image, images, int, json, kind, line, list, markdown, money, newToken, object, personName, phone, phoneKind, quantity, secret, sequence, serialize, slug, slugify, subdomain, text, textKind, toKind, token, url, urls, validate };
@@ -1 +1 @@
1
- export { K as Fake, U as Kind, L as LIST_MAX, i as SIZES_MAX, G as ValidationError, D as choice, O as date, k as datetime, u as define, p as email, A as fakeUlid, a as file, s as files, j as flag, m as host, M as id, c as image, l as images, N as int, d as json, W as kind, h as line, R as list, g as markdown, P as money, _ as newToken, z as object, v as personName, t as phone, n as phoneKind, F as quantity, y as secret, I as sequence, B as serialize, b as slug, x as slugify, S as subdomain, C as text, w as textKind, V as toKind, T as token, E as url, f as urls, H as validate } from "../validation-Cppmd0RV.js";
1
+ export { J as Fake, G as Kind, z as LIST_MAX, i as SIZES_MAX, q as ValidationError, k as choice, A as date, j as datetime, f as define, a as document, o as documents, h as email, M as fakeUlid, s as file, l as files, N as flag, g as host, P as id, u as image, d as images, F as int, p as json, K as kind, _ as line, B as list, v as markdown, I as money, y as newToken, V as object, b as personName, t as phone, n as phoneKind, L as quantity, x as secret, R as sequence, H as serialize, S as slug, C as slugify, w as subdomain, T as text, E as textKind, U as toKind, D as token, O as url, m as urls, W as validate } from "../validation-C3MCRBQO.js";