@drhomet/mithaq 0.1.1 → 0.2.0

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 (120) hide show
  1. package/LICENSES/openstatus-time-picker.txt +21 -0
  2. package/README.md +161 -0
  3. package/dist/asas/build.d.ts +10 -2
  4. package/dist/asas/build.js +69 -22
  5. package/dist/asas/cli/mithaq.js +35 -3
  6. package/dist/asas/cli/upgrade.d.ts +22 -9
  7. package/dist/asas/cli/upgrade.js +187 -78
  8. package/dist/asas/http/fetch.d.ts +21 -3
  9. package/dist/asas/http/fetch.js +123 -60
  10. package/dist/asas/http/orpc.d.ts +5 -3
  11. package/dist/asas/http/orpc.js +126 -108
  12. package/dist/asas/runner/bulk.d.ts +1 -1
  13. package/dist/asas/runner/bulk.js +16 -11
  14. package/dist/asas/runner/errors.d.ts +5 -2
  15. package/dist/asas/runner/errors.js +18 -2
  16. package/dist/asas/runner/read.d.ts +6 -2
  17. package/dist/asas/runner/read.js +28 -6
  18. package/dist/asas/runner/steps.d.ts +22 -4
  19. package/dist/asas/runner/steps.js +15 -9
  20. package/dist/asas/runner/store.d.ts +4 -0
  21. package/dist/asas/runner/store.js +21 -0
  22. package/dist/asas/runner/tenant.d.ts +2 -2
  23. package/dist/asas/runner/tenant.js +78 -16
  24. package/dist/asas/runner/write.d.ts +173 -11
  25. package/dist/asas/runner/write.js +274 -46
  26. package/dist/auth/better-auth/access.d.ts +14 -0
  27. package/dist/auth/better-auth/access.js +32 -0
  28. package/dist/auth/better-auth/generate.d.ts +3 -1
  29. package/dist/auth/better-auth/generate.js +26 -8
  30. package/dist/auth/better-auth/index.d.ts +2 -0
  31. package/dist/auth/better-auth/tenant.d.ts +11 -3
  32. package/dist/auth/better-auth/tenant.js +10 -2
  33. package/dist/auth/local/index.js +5 -3
  34. package/dist/base/fields.d.ts +3 -2
  35. package/dist/base/fields.js +3 -2
  36. package/dist/base/index.d.ts +2 -1
  37. package/dist/base/json-schema.d.ts +7 -0
  38. package/dist/base/json-schema.js +35 -0
  39. package/dist/base/kinds.d.ts +21 -0
  40. package/dist/base/kinds.js +46 -1
  41. package/dist/base/rules.d.ts +4 -4
  42. package/dist/base/rules.js +4 -3
  43. package/dist/drizzle/index.d.ts +9 -139
  44. package/dist/drizzle/index.js +143 -24
  45. package/dist/drizzle/kinds.d.ts +1 -0
  46. package/dist/drizzle/kinds.js +3 -1
  47. package/dist/drizzle/migrate.d.ts +26 -0
  48. package/dist/drizzle/migrate.js +69 -26
  49. package/dist/drizzle/rules.d.ts +1 -1
  50. package/dist/index.d.ts +11 -21
  51. package/dist/index.js +8 -18
  52. package/dist/mithaq/define.d.ts +62 -15
  53. package/dist/mithaq/define.js +23 -9
  54. package/dist/mithaq/generate-messages.js +5 -4
  55. package/dist/mithaq/language.d.ts +43 -4
  56. package/dist/mithaq/message-file.d.ts +13 -0
  57. package/dist/mithaq/message-file.js +29 -0
  58. package/dist/mithaq/messages.js +5 -0
  59. package/dist/mithaq/resolve.js +70 -10
  60. package/dist/mithaq/types.d.ts +29 -1
  61. package/dist/rbac/index.d.ts +2 -2
  62. package/dist/rbac/index.js +1 -1
  63. package/dist/rich-text/index.d.ts +1 -0
  64. package/dist/rich-text/index.js +1 -0
  65. package/dist/rich-text/rich-text.d.ts +2 -55
  66. package/dist/rich-text/rich-text.js +2 -125
  67. package/dist/rich-text/value.d.ts +55 -0
  68. package/dist/rich-text/value.js +126 -0
  69. package/dist/screen/client.d.ts +5 -2
  70. package/dist/screen/client.js +5 -2
  71. package/dist/screen/generate.js +48 -6
  72. package/dist/screen/index.d.ts +18 -1
  73. package/dist/screen/index.js +24 -6
  74. package/dist/screen/react.d.ts +2 -0
  75. package/dist/storage/index.d.ts +2 -3
  76. package/dist/storage/index.js +1 -2
  77. package/dist/storage/storage.d.ts +2 -0
  78. package/dist/storage/storage.js +9 -2
  79. package/dist/targets.d.ts +14 -0
  80. package/dist/targets.js +16 -0
  81. package/dist/vite.d.ts +7 -0
  82. package/dist/vite.js +81 -0
  83. package/dist/zod/index.d.ts +12 -3
  84. package/dist/zod/index.js +6 -5
  85. package/dist/zod/kinds.d.ts +3 -1
  86. package/dist/zod/kinds.js +11 -2
  87. package/dist/zod/rules.d.ts +1 -1
  88. package/docs/packages/mithaq/README.md +962 -0
  89. package/docs/packages/mithaq/src/asas/cli/README.md +252 -0
  90. package/docs/packages/mithaq/src/asas/runner/README.md +472 -0
  91. package/docs/packages/mithaq/src/drizzle/README.md +39 -0
  92. package/docs/packages/mithaq-admin/README.md +312 -0
  93. package/docs/templates/asas/README.md +1056 -0
  94. package/package.json +37 -8
  95. package/skills/add-field/SKILL.md +27 -0
  96. package/skills/add-kayan/SKILL.md +26 -0
  97. package/skills/eject-admin-piece/SKILL.md +28 -0
  98. package/skills/upgrade-mithaq/SKILL.md +35 -0
  99. package/r/mithaq-cells.json +0 -20
  100. package/r/mithaq-controls.json +0 -37
  101. package/r/mithaq-directional-icon.json +0 -15
  102. package/r/mithaq-expected-updated-at.json +0 -15
  103. package/r/mithaq-field-shell.json +0 -18
  104. package/r/mithaq-filter-operators.json +0 -15
  105. package/r/mithaq-form.json +0 -33
  106. package/r/mithaq-list-columns.json +0 -23
  107. package/r/mithaq-list-filters.json +0 -24
  108. package/r/mithaq-list-pagination.json +0 -24
  109. package/r/mithaq-list-query.json +0 -18
  110. package/r/mithaq-list-selection.json +0 -19
  111. package/r/mithaq-list.json +0 -33
  112. package/r/mithaq-nav.json +0 -24
  113. package/r/mithaq-nodes.json +0 -34
  114. package/r/mithaq-relationship-cell.json +0 -20
  115. package/r/mithaq-relationship-control.json +0 -20
  116. package/r/mithaq-rich-text.json +0 -40
  117. package/r/mithaq-screens-host.json +0 -96
  118. package/r/mithaq-storage.json +0 -44
  119. package/r/mithaq-upload.json +0 -29
  120. package/r/registry.json +0 -550
@@ -1,13 +1,12 @@
1
- import { and, asc, count as sqlCount, desc, eq, getTableColumns, getTableName, gt, gte, ilike, inArray, isNotNull, isNull, lt, lte, ne, notInArray, or, sql } from 'drizzle-orm';
1
+ import { and, asc, count as sqlCount, sum as sqlSum, desc, eq, getTableColumns, getTableName, gt, gte, ilike, inArray, isNotNull, isNull, lt, lte, ne, notInArray, or, sql } from 'drizzle-orm';
2
2
  import { drizzle } from 'drizzle-orm/node-postgres';
3
3
  import { boolean as pgBoolean, index, integer as pgInteger, isPgEnum, jsonb, timestamp, uuid as pgUuid, pgEnum, pgSchema, pgTable, primaryKey, unique as uniqueConstraint, uniqueIndex, } from 'drizzle-orm/pg-core';
4
4
  import { isPlainIdentifier, snakeCase, uniqueRuleIndexes, versionsTableOf } from '../mithaq/resolve.js';
5
5
  import { implementationOf } from '../mithaq/rukn.js';
6
- import { conflict, localizedQuery } from '../asas/runner/errors.js';
6
+ import { conflict, invalid, missing, localizedQuery } from '../asas/runner/errors.js';
7
7
  import { Refusal } from '../mithaq/refusal.js';
8
8
  import { providerOf } from '../base/rules.js';
9
- import { idKinds as drizzleIdKinds, kinds as drizzleKinds } from './kinds.js';
10
- import { rules as drizzleRules } from './rules.js';
9
+ import { kinds as drizzleKinds } from './kinds.js';
11
10
  const modifiable = (column) => {
12
11
  for (const method of ['default', 'notNull', 'primaryKey', 'unique']) {
13
12
  if (typeof Reflect.get(column, method) !== 'function') {
@@ -49,11 +48,13 @@ const refusedSchema = (name) => typeof name !== 'string' || !isPlainIdentifier(n
49
48
  // A plain `Error`: what reaches here is a `where` an access callback returned, the
50
49
  // project's own bug; the read layer refuses a caller's first.
51
50
  const refuseLocalized = (kayan, field, what, locales) => new Error(localizedQuery({ kayan, field, what, locales }));
52
- // Postgres answers `23503`, refused as a conflict naming which kayan points at the
53
- // row, never which row. Read by schema too: another tool may share a table name.
51
+ // Postgres answers `23503`, or from 18 on `23001` for an `ON DELETE RESTRICT` key, refused as a
52
+ // conflict naming which kayan points at the row, never which row. Read by schema too: another
53
+ // tool may share a table name.
54
+ const pointedAt = new Set(['23503', '23001']);
54
55
  const referenced = ({ thrown, name, kayans, tables }) => {
55
56
  const held = thrown;
56
- const error = held?.code === '23503' ? held : held?.cause?.code === '23503' ? held.cause : undefined;
57
+ const error = pointedAt.has(held?.code) ? held : pointedAt.has(held?.cause?.code) ? held.cause : undefined;
57
58
  if (!error)
58
59
  return thrown;
59
60
  // A kayan's join tables are built in its own table's schema.
@@ -122,6 +123,47 @@ const conditionsOf = (column, rules) => {
122
123
  });
123
124
  return checks.length === 1 ? checks[0] : and(...checks);
124
125
  };
126
+ // Payload's own path, `$[*].a[*].b[*]`: a list at any segment, a list inside a list included, is
127
+ // matched by any of its entries. The path and every value are bound parameters, and each key a
128
+ // quoted jsonpath string, so nothing a caller wrote is read as SQL or jsonpath.
129
+ const jsonConditionsOf = (column, path, rules) => {
130
+ const at = `$[*]${path.map((key) => `.${JSON.stringify(key)}[*]`).join('')}`;
131
+ const some = (test) => sql `exists (select 1 from jsonb_path_query(${column}, ${at}::jsonpath) as item(value) where ${test})`;
132
+ // A JSON `null` is no value, as a SQL `NULL` is: `exists` and `equals: null` read both alike.
133
+ const present = some(sql `item.value <> 'null'::jsonb`);
134
+ const text = sql `(item.value #>> '{}')`;
135
+ const equal = (value) => (value === null ? sql `not ${present}` : some(sql `item.value = ${JSON.stringify(value)}::jsonb`));
136
+ // One subquery for the whole list, against a hashed set: one per value, or `= any(array)`, compares
137
+ // every item with every value, which took seconds for a thousand values over 20,000 rows.
138
+ const listed = (value) => {
139
+ const { hasNull, values } = splitNulls(value);
140
+ const hit = values.length ? some(sql `item.value in (select unnest(${sql.param(values.map((one) => JSON.stringify(one)))}::jsonb[]))`) : sql `false`;
141
+ return hasNull ? or(sql `not ${present}`, hit) : hit;
142
+ };
143
+ const checks = Object.entries(rules).map(([operator, value]) => {
144
+ switch (operator) {
145
+ case 'contains':
146
+ return some(ilike(text, pattern(String(value))));
147
+ case 'like': {
148
+ const words = String(value).trim().split(/\s+/).filter(Boolean);
149
+ return words.length ? some(and(...words.map((word) => ilike(text, pattern(word))))) : undefined;
150
+ }
151
+ case 'equals':
152
+ return equal(value);
153
+ case 'not_equals':
154
+ return value === null ? present : and(present, sql `not ${equal(value)}`);
155
+ case 'exists':
156
+ return value ? present : sql `not ${present}`;
157
+ case 'in':
158
+ return listed(value);
159
+ case 'not_in':
160
+ return and(present, sql `not ${listed(value)}`);
161
+ }
162
+ // Unreachable past the kind's operators, but never left out of a `where` that carries an access constraint.
163
+ throw new Error(`Unsupported where operator "${operator}" on a json field.`);
164
+ });
165
+ return checks.length === 1 ? checks[0] : and(...checks);
166
+ };
125
167
  const whereOf = (db, tables, kayanByName, name, where) => {
126
168
  const table = tables[name];
127
169
  const kayan = kayanByName.get(name);
@@ -135,8 +177,23 @@ const whereOf = (db, tables, kayanByName, name, where) => {
135
177
  for (const [field, rules] of Object.entries(where)) {
136
178
  if (field === 'and' || field === 'or')
137
179
  continue;
138
- const [ownField, relatedField] = field.split('.');
180
+ const [ownField, ...path] = field.split('.');
181
+ const relatedField = path[0];
139
182
  const relationField = kayan.fields[ownField];
183
+ if (relationField?.kind === 'json') {
184
+ parts.push(jsonConditionsOf(table[ownField], path, rules));
185
+ continue;
186
+ }
187
+ // Whether a versions table's snapshot holds an id in one field, alone or in a set: the delete's
188
+ // reference check asks it, and a caller's read of versions refuses every path before here.
189
+ // Lowered, since a draft keeps a uuid as its writer sent it.
190
+ if (!relationField && ownField === 'version' && relatedField && table.version) {
191
+ const { equals, ...other } = rules;
192
+ if (equals === undefined || Object.keys(other).length)
193
+ throw new Error(`Kayan "${name}" path "${field}" takes equals alone.`);
194
+ parts.push(sql `lower((${table.version} -> ${relatedField})::text)::jsonb @> ${JSON.stringify(typeof equals === 'string' ? equals.toLowerCase() : equals)}::jsonb`);
195
+ continue;
196
+ }
140
197
  // A `hasMany` field has no column here, so the join table is filtered and its
141
198
  // parent ids filter this table.
142
199
  if (relationField?.hasMany) {
@@ -279,9 +336,7 @@ export const recordMigration = async ({ db, hash, createdAt, name }) => {
279
336
  await db.execute(sql `with gone as (delete from ${historyTable} where name = ${devPushMarker})
280
337
  insert into ${historyTable} (hash, created_at, name) values (${hash}, ${createdAt}, ${name})`);
281
338
  };
282
- /** The drizzle store target's maps: a map per kind and a map per rule. */
283
- export const drizzleTarget = { capability: 'store', type: 'drizzle', idKinds: drizzleIdKinds, kinds: drizzleKinds, rules: drizzleRules };
284
- export const drizzleAdapter = ({ db: ready, connectionString, kinds, rules, idKinds = drizzleIdKinds, push: named, prodMigrations, }) => {
339
+ export const drizzleAdapter = ({ db: ready, connectionString, kinds, rules, idKinds, push: named, prodMigrations, }) => {
285
340
  if (prodMigrations && prodMigrations.pending !== 'run' && prodMigrations.pending !== 'refuse') {
286
341
  throw new Error(`drizzleAdapter's prodMigrations.pending is 'run' or 'refuse', not ${JSON.stringify(prodMigrations.pending)}.`);
287
342
  }
@@ -361,6 +416,27 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
361
416
  }
362
417
  return flat;
363
418
  };
419
+ const declaredData = (name, values) => {
420
+ const described = kayanByName.get(name);
421
+ // A version's fields describe virtual snapshot paths, while core writes the physical snapshot column.
422
+ const fields = described?.fields.parent?.target?.versions?.table === name ? getTableColumns(tables[name]) : described?.fields ?? getTableColumns(tables[name]);
423
+ const field = Object.keys(values).find((key) => !Object.hasOwn(fields, key));
424
+ if (field)
425
+ throw invalid({ message: `Kayan "${name}" declares no field "${field}".`, key: 'error.invalid.value', params: { kayan: name, field } });
426
+ return values;
427
+ };
428
+ const increments = ({ name, values, insert = false }) => {
429
+ const data = { ...declaredData(name, values) };
430
+ for (const [field, value] of Object.entries(data)) {
431
+ if (kayanByName.get(name)?.fields[field]?.kind === 'json' || !value || typeof value !== 'object' || !('$inc' in value))
432
+ continue;
433
+ if (kayanByName.get(name)?.fields[field]?.kind !== 'integer' || !Number.isSafeInteger(value.$inc) || Object.keys(value).length !== 1) {
434
+ throw invalid({ message: `Kayan "${name}" field "${field}" accepts $inc only on an integer field with one safe integer.`, key: 'error.invalid.value', params: { kayan: name, field } });
435
+ }
436
+ data[field] = insert ? value.$inc : sql `${tables[name][field]} + ${value.$inc}`;
437
+ }
438
+ return data;
439
+ };
364
440
  // A field whose every locale column is null is one null, which matches the derived row shape.
365
441
  const expand = (name, row) => {
366
442
  const fields = localizedOf(name);
@@ -472,7 +548,11 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
472
548
  throw new Error(`Kayan "${kayan.name}" asks for the schema ${JSON.stringify(kayan.schemaName)}, ${refused} Give the kayan another schemaName.`);
473
549
  }
474
550
  const built = Object.create(null);
475
- const columns = Object.values(kayan.fields).flatMap(columnsOfField(kayan.store));
551
+ // A table reads its own columns first, whichever place the kayan spread the timestamps in.
552
+ // A column never moves in a table that exists: drizzle-kit diffs columns by name.
553
+ const stamp = (field) => field.name === 'createdAt' || field.name === 'updatedAt';
554
+ const declared = Object.values(kayan.fields).flatMap(columnsOfField(kayan.store));
555
+ const columns = [...declared.filter((field) => !stamp(field)), ...declared.filter(stamp)];
476
556
  // `pgTable` keys on the field name, so a localized `title` and a plain `titleAr` would
477
557
  // both ask for `title_ar` and one field would end with no column.
478
558
  const asked = new Set();
@@ -550,9 +630,9 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
550
630
  if (tables[name]) {
551
631
  throw new Error(`Kayan "${kayan.name}" field "${field.name}" needs the join table "${name}", which is already the table of kayan "${name}". Rename one of the two, or give the kayan a dbName of its own.`);
552
632
  }
553
- // Each end takes the type of the id it points at. `cascade` on both: the row belongs to
554
- // the pair. An upload's own builder answers `restrict`.
555
- const reference = (build, column, target) => modifiable(build({ ...field, name: column, column: `${column}_id`, target, rules: { onDelete: 'cascade' } }, tables)).notNull();
633
+ // Each end takes the type of the id it points at. The parent's end cascades, since the row
634
+ // belongs to its parent; the target's is the field's own `onDelete`.
635
+ const reference = (build, column, target, onDelete) => modifiable(build({ ...field, name: column, column: `${column}_id`, target, rules: { onDelete } }, tables)).notNull();
556
636
  // Named, so the check below holds them to the schema's one namespace.
557
637
  const key = `${name}_pkey`;
558
638
  const targetIndex = `${name}_target_id_index`;
@@ -564,8 +644,8 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
564
644
  indexNames.push({ where: `Kayan "${kayan.name}" field "${field.name}" join table ${what}`, kayan: kayan.name, name: named });
565
645
  }
566
646
  tables[name] = tableIn(schemaOf(tables[kayan.name]))(name, {
567
- parent: reference(kinds.relationship, 'parent', kayan),
568
- target: reference(implementationOf({ table: kinds, shipped: drizzleKinds, name: field.kind, own: field.kindTargets, type: 'drizzle' }), 'target', field.target),
647
+ parent: reference(kinds.relationship, 'parent', kayan, 'cascade'),
648
+ target: reference(implementationOf({ table: kinds, shipped: drizzleKinds, name: field.kind, own: field.kindTargets, type: 'drizzle' }), 'target', field.target, field.rules?.onDelete),
569
649
  // The only order a set of ids has.
570
650
  order: pgInteger('order').notNull(),
571
651
  }, (table) => [
@@ -663,8 +743,7 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
663
743
  if (!tablesFilter.length)
664
744
  return;
665
745
  await refuseStrangers({ db, tables, by: 'push' });
666
- // `drizzle-kit/api-postgres` imports every driver drizzle speaks, so webpack and Turbopack
667
- // are told to leave the import to the runtime.
746
+ (await import('./migrate.js')).holdOwnKit();
668
747
  const { pushSchema } = await import(/* webpackIgnore: true */ 'drizzle-kit/api-postgres');
669
748
  // `owned`: a column of a table of the language has the type, which makes it the language's own.
670
749
  const listed = (values) => sql.join(values.map((value) => sql `${value}`), sql `, `);
@@ -725,7 +804,7 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
725
804
  return db.transaction((tx) => run(tx));
726
805
  },
727
806
  async insert({ tx, kayan: name, data: values }) {
728
- const { columns, sets } = withoutSets(name, values);
807
+ const { columns, sets } = withoutSets(name, declaredData(name, values));
729
808
  let result;
730
809
  try {
731
810
  result = await tx.insert(tables[name]).values(flatten(name, columns)).returning();
@@ -741,16 +820,44 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
741
820
  await writeSets(tx, name, held.id, sets);
742
821
  return withSets(tx, name, held);
743
822
  },
823
+ async upsert({ tx, kayan: name, on, data: values, where }) {
824
+ if (!tx)
825
+ throw new Error('An upsert requires a transaction.');
826
+ const kayan = kayanByName.get(name);
827
+ const table = tables[name];
828
+ const inserted = withoutSets(name, increments({ name, values, insert: true }));
829
+ const updated = withoutSets(name, increments({ name, values }));
830
+ const set = flatten(name, Object.fromEntries(Object.entries(updated.columns).filter(([field]) => field !== 'id' && field !== kayan.tenant && !on.includes(field))));
831
+ // Even a key-only upsert returns the existing row, without changing its identity.
832
+ if (!Object.keys(set).length)
833
+ set[on[0]] = table[on[0]];
834
+ let result;
835
+ try {
836
+ result = await tx.insert(table).values(flatten(name, inserted.columns)).onConflictDoUpdate({ target: on.map((field) => table[field]), set, setWhere: where ? whereOf(tx, tables, kayanByName, name, where) : undefined }).returning();
837
+ }
838
+ catch (thrown) {
839
+ throw collision(thrown, name);
840
+ }
841
+ if (!Array.isArray(result))
842
+ throw new Error('The drizzle store expected upsert to return rows.');
843
+ const [row] = result;
844
+ if (!row)
845
+ throw missing({ message: `No "${name}" row found.`, key: 'error.not_found.row', params: { kayan: name } });
846
+ const held = expand(name, row);
847
+ await writeSets(tx, name, held.id, inserted.sets);
848
+ return withSets(tx, name, held);
849
+ },
744
850
  async update({ tx, kayan: name, id, data: values, where }) {
745
- const { columns, sets } = withoutSets(name, values);
851
+ const { columns, sets } = withoutSets(name, increments({ name, values }));
746
852
  const flat = flatten(name, columns);
747
853
  const matched = idWhere(tx, tables, kayanByName, name, id, where);
748
854
  let result;
749
855
  try {
750
- // drizzle refuses an empty `set`, so a write naming only sets locks the row instead.
856
+ // drizzle refuses an empty `set`, so a write naming only sets locks the row instead, as an UPDATE
857
+ // of no key column would: a stronger lock would deadlock with a write naming the row.
751
858
  result = Object.keys(flat).length
752
859
  ? await tx.update(tables[name]).set(flat).where(matched).returning()
753
- : await tx.select().from(tables[name]).where(matched).for('update');
860
+ : await tx.select().from(tables[name]).where(matched).for('no key update');
754
861
  }
755
862
  catch (thrown) {
756
863
  throw collision(thrown, name);
@@ -787,8 +894,12 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
787
894
  return { ...sets, ...expand(name, row) };
788
895
  },
789
896
  async find({ kayan: name, args: { limit, lock, page, sort, where }, tx }) {
897
+ if (lock && !tx)
898
+ throw new Error('A locked find requires a transaction.');
790
899
  const client = (tx ?? db);
791
900
  const table = tables[name];
901
+ if (!Object.hasOwn(kayanByName.get(name)?.fields ?? getTableColumns(table), sort.field))
902
+ throw invalid({ message: `Kayan "${name}" declares no field "${sort.field}".`, key: 'error.invalid.value', params: { kayan: name, field: sort.field } });
792
903
  if (kayanByName.get(name)?.fields[sort.field]?.rules?.localized) {
793
904
  throw refuseLocalized(name, sort.field, 'sort');
794
905
  }
@@ -805,7 +916,7 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
805
916
  const offset = limit > 0 ? (page - 1) * limit : 0;
806
917
  // `$dynamic()` keeps each branch one type, so drizzle checks `orderBy` against the table.
807
918
  const query = client.select().from(table).$dynamic().where(condition);
808
- const locked = tx && lock === 'update' ? query.for('update') : query;
919
+ const locked = tx && lock ? query.for({ update: 'update', noKeyUpdate: 'no key update', share: 'share', keyShare: 'key share' }[lock]) : query;
809
920
  const ordered = locked.orderBy(...orderBy).offset(offset);
810
921
  const docs = limit > 0 ? await ordered.limit(limit) : await ordered;
811
922
  const rows = await client.select({ totalDocs: sqlCount() }).from(table).where(condition);
@@ -816,6 +927,14 @@ const storeOf = ({ ready, opened, db, connectionString, kinds, rules, idKinds, p
816
927
  totalDocs: Number(rows[0]?.totalDocs ?? 0),
817
928
  };
818
929
  },
930
+ async sum({ tx, kayan: name, field, where }) {
931
+ if (!['integer', 'number', 'decimal'].includes(kayanByName.get(name)?.fields[field]?.kind ?? '')) {
932
+ throw invalid({ message: `Kayan "${name}" field "${field}" is not numeric, so it cannot be summed.`, key: 'error.invalid.value', params: { kayan: name, field } });
933
+ }
934
+ const client = (tx ?? db);
935
+ const rows = await client.select({ total: sqlSum(tables[name][field]) }).from(tables[name]).where(where ? whereOf(client, tables, kayanByName, name, where) : undefined);
936
+ return rows[0]?.total ?? '0';
937
+ },
819
938
  async count({ kayan: name, args: { where }, tx }) {
820
939
  const client = (tx ?? db);
821
940
  // The relationship filters use subqueries, so there are no joins to deduplicate.
@@ -22,6 +22,7 @@ export declare const kinds: {
22
22
  datetime: (field: ResolvedField) => import("drizzle-orm/pg-core").PgTimestampBuilder;
23
23
  date: (field: ResolvedField) => import("drizzle-orm/pg-core").PgDateStringBuilder;
24
24
  join: (field: ResolvedField) => never;
25
+ json: (field: ResolvedField) => import("drizzle-orm/pg-core").PgJsonbBuilder;
25
26
  relationship: ColumnBuilder;
26
27
  };
27
28
  export {};
@@ -1,4 +1,4 @@
1
- import { boolean, date, integer, numeric, serial, text, timestamp, uuid, varchar } from 'drizzle-orm/pg-core';
1
+ import { boolean, date, integer, jsonb, numeric, serial, text, timestamp, uuid, varchar } from 'drizzle-orm/pg-core';
2
2
  /** Length is a rule, not a kind: without a maximum a string column is `text`. */
3
3
  const string = (field) => typeof field.rules?.maxLength !== 'number'
4
4
  ? text(field.column)
@@ -37,5 +37,7 @@ export const kinds = {
37
37
  join: (field) => {
38
38
  throw new Error(`Field "${field.name}" is a join, whose rows are held by kayan "${field.target?.name}", so this store builds no column for it.`);
39
39
  },
40
+ // `jsonb`, as Payload's Postgres adapter stores a json field, so either reads what the other wrote.
41
+ json: (field) => jsonb(field.column),
40
42
  relationship,
41
43
  };
@@ -1,7 +1,33 @@
1
+ import * as modules from 'node:module';
1
2
  import { type NodePgDatabase } from 'drizzle-orm/node-postgres';
2
3
  import { type ProdMigrations } from './index.ts';
3
4
  import type { ResolvedKayan } from '../mithaq/types.ts';
4
5
  export declare const binary: string;
6
+ /**
7
+ * Resolves each drizzle-orm import made under `kit` from `from` instead. drizzle-kit imports
8
+ * drizzle-orm without declaring it, so on its own it loads whatever copy the project hoists,
9
+ * such as a 0.45 beside the one the tables were built with, and fails on it.
10
+ */
11
+ export declare const holdKitOrm: ({ kit, from }: {
12
+ kit: string;
13
+ from: string;
14
+ }) => modules.ModuleHooks;
15
+ /** The drizzle-kit this mithaq installs, and the file its drizzle-orm resolves from. */
16
+ export declare const ownKit: {
17
+ kit: string;
18
+ from: string;
19
+ };
20
+ /** Holds this mithaq's drizzle-kit to its own drizzle-orm in this process, once. */
21
+ export declare const holdOwnKit: () => {};
22
+ /**
23
+ * A module to `--import` that holds `kit`'s drizzle-orm to `from`'s in a child process.
24
+ * ponytail: only the drizzle-kit mithaq starts is held; `drizzle-kit migrate` run by hand beside a
25
+ * second drizzle-orm still loads the hoisted one. Ship a `mithaq` word that applies migrations if that bites.
26
+ */
27
+ export declare const kitOrm: (held: {
28
+ kit: string;
29
+ from: string;
30
+ }) => string;
5
31
  export declare const header = "// Written by `mithaq migrate`. Commit it.";
6
32
  /** One `unresolved` item, and one reply in the hints file, are the same shape. */
7
33
  type Hint = {
@@ -6,8 +6,9 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
- import { execFile } from 'node:child_process';
10
- import { existsSync, readFileSync } from 'node:fs';
9
+ import { spawn } from 'node:child_process';
10
+ import * as modules from 'node:module';
11
+ import { closeSync, existsSync, openSync, readFileSync } from 'node:fs';
11
12
  import { mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'node:fs/promises';
12
13
  import { tmpdir } from 'node:os';
13
14
  import { join, resolve as resolvePath } from 'node:path';
@@ -22,6 +23,34 @@ import { createHistory, devPushMarker, historyLock, historyTable, joinTableOf, m
22
23
  // `bin.cjs` is not in drizzle-kit's `exports`, so it is resolved as the root entry's
23
24
  // sibling; resolved from this file, so a consumer gets its own `mithaq`'s copy.
24
25
  export const binary = fileURLToPath(new URL('bin.cjs', import.meta.resolve('drizzle-kit')));
26
+ /**
27
+ * Resolves each drizzle-orm import made under `kit` from `from` instead. drizzle-kit imports
28
+ * drizzle-orm without declaring it, so on its own it loads whatever copy the project hoists,
29
+ * such as a 0.45 beside the one the tables were built with, and fails on it.
30
+ */
31
+ export const holdKitOrm = ({ kit, from }) =>
32
+ // Node's alone: a runtime without it resolves as it did.
33
+ modules.registerHooks?.({
34
+ resolve: (specifier, context, next) => {
35
+ if (!/^drizzle-orm($|\/)/.test(specifier) || !context.parentURL?.startsWith(kit))
36
+ return next(specifier, context);
37
+ // A require is resolved from the requiring file whatever parentURL says, so it is resolved here.
38
+ return context.conditions.includes('require')
39
+ ? { url: pathToFileURL(modules.createRequire(from).resolve(specifier)).href, shortCircuit: true }
40
+ : next(specifier, { ...context, parentURL: from });
41
+ },
42
+ });
43
+ /** The drizzle-kit this mithaq installs, and the file its drizzle-orm resolves from. */
44
+ export const ownKit = { kit: new URL('.', import.meta.resolve('drizzle-kit')).href, from: import.meta.url };
45
+ let ownHeld;
46
+ /** Holds this mithaq's drizzle-kit to its own drizzle-orm in this process, once. */
47
+ export const holdOwnKit = () => (ownHeld ??= holdKitOrm(ownKit) ?? true);
48
+ /**
49
+ * A module to `--import` that holds `kit`'s drizzle-orm to `from`'s in a child process.
50
+ * ponytail: only the drizzle-kit mithaq starts is held; `drizzle-kit migrate` run by hand beside a
51
+ * second drizzle-orm still loads the hoisted one. Ship a `mithaq` word that applies migrations if that bites.
52
+ */
53
+ export const kitOrm = (held) => `data:text/javascript,${encodeURIComponent(`import { holdKitOrm } from ${JSON.stringify(import.meta.url)}\nholdKitOrm(${JSON.stringify(held)})\n`)}`;
25
54
  const configName = 'drizzle.config.ts';
26
55
  const schemaName = 'schema.ts';
27
56
  const hintsName = 'mithaq.hints.json';
@@ -640,16 +669,19 @@ const moveHistory = async ({ db, directory, check }) => {
640
669
  order by old.id returning name`);
641
670
  console.log(`Copied ${moved.rows.length} ${moved.rows.length === 1 ? 'row' : 'rows'} of this project's history from drizzle.__drizzle_migrations into ${history}, where mithaq keeps it now. The rows in drizzle.__drizzle_migrations are left as they were.`);
642
671
  };
643
- // drizzle's own rule, which matches a folder to a row by `name` alone.
644
- const unapplied = async ({ db, directory }) => {
645
- let localMigrations = [];
672
+ const migrationsIn = (directory) => {
646
673
  try {
647
- localMigrations = readMigrationFiles({ migrationsFolder: fileURLToPath(directory) });
674
+ return readMigrationFiles({ migrationsFolder: fileURLToPath(directory) });
648
675
  }
649
676
  catch (error) {
650
677
  if (error.code !== 'ENOENT')
651
678
  throw error;
679
+ return [];
652
680
  }
681
+ };
682
+ // drizzle's own rule, which matches a folder to a row by `name` alone.
683
+ const unapplied = async ({ db, directory }) => {
684
+ const localMigrations = migrationsIn(directory);
653
685
  if (!localMigrations.length)
654
686
  return [];
655
687
  let dbMigrations = [];
@@ -674,6 +706,10 @@ export const bootMigrations = async ({ db, prodMigrations }) => {
674
706
  throw new Refusal(`prodMigrations names ${migrationsFolder} as the migrations folder, and under ${process.cwd()} it holds no ${configName}, which every folder \`mithaq migrate\` writes holds, so this boot cannot tell which migrations this database has applied. Ship the folder with the deploy, or name where it is. Nothing was checked or applied.`);
675
707
  }
676
708
  const directory = pathToFileURL(`${resolvePath(migrationsFolder)}/`);
709
+ // A new project's folder holds the config and no migration, which would read as nothing pending.
710
+ if (!migrationsIn(directory).length) {
711
+ throw new Refusal(`prodMigrations names ${migrationsFolder}, which holds no migration yet, so a production boot would serve a database nothing built. Write the first with \`mithaq migrate --name init\` against an empty database, ship it, and start again. Nothing was checked or applied.`);
712
+ }
677
713
  const history = `${migrationsTable.schema}.${migrationsTable.table}`;
678
714
  // Read from mithaq's table alone, an unmoved history says nothing was applied.
679
715
  if ((await moveHistory({ db, directory, check: true })) !== undefined) {
@@ -698,26 +734,33 @@ export const bootMigrations = async ({ db, prodMigrations }) => {
698
734
  const names = behind.map((migration) => migration.name);
699
735
  throw new Refusal(`This database has not applied ${names.join(', ')}. prodMigrations refuses a production boot while a migration is pending, so nothing was served and nothing was written. Apply ${names.length === 1 ? 'it' : 'them'} with the installed binary, \`drizzle-kit migrate --config ${join(migrationsFolder, configName)}\`, and start again.`);
700
736
  };
701
- // stdout alone is parsed: stderr carries import-time warnings, never the envelope.
702
- const kit = (cwd, argv) => new Promise((done, fail) => {
703
- // Node's 1 MB default `maxBuffer` kills a child past it, and the envelope grows with the
704
- // migration. Without an `error` listener a child that cannot spawn hangs the command.
705
- const child = execFile(process.execPath, [binary, ...argv], { cwd, maxBuffer: 64 * 1024 * 1024 });
706
- let stdout = '';
707
- let stderr = '';
708
- // Decoded by the stream, so a multibyte character split across chunks stays whole.
709
- child.stdout?.setEncoding('utf8');
710
- child.stderr?.setEncoding('utf8');
711
- child.stdout?.on('data', (chunk) => {
712
- stdout += chunk;
713
- });
714
- child.stderr?.on('data', (chunk) => {
715
- stderr += chunk;
716
- });
717
- child.on('error', fail);
718
- // The signal too, since a child killed from outside closes with no code.
719
- child.on('close', (code, signal) => done({ code, signal, stdout, stderr }));
720
- });
737
+ // stdout alone is parsed: stderr carries import-time warnings, never the envelope. Both go to files:
738
+ // drizzle-kit exits right after printing, and Node writes to a pipe asynchronously off Linux, so
739
+ // an envelope past the pipe's 64 KB was cut short, where a file is written synchronously.
740
+ const kit = async (cwd, argv) => {
741
+ const scratch = await mkdtemp(join(tmpdir(), 'mithaq-kit-'));
742
+ const [out, err] = [join(scratch, 'stdout'), join(scratch, 'stderr')];
743
+ // Opened inside the `try`, so one that fails to open leaves none behind.
744
+ const opened = [];
745
+ try {
746
+ for (const path of [out, err])
747
+ opened.push(openSync(path, 'w'));
748
+ const [outFd, errFd] = opened;
749
+ const { code, signal } = await new Promise((done, fail) => {
750
+ const child = spawn(process.execPath, ['--import', kitOrm(ownKit), binary, ...argv], { cwd, stdio: ['ignore', outFd, errFd] });
751
+ // Without an `error` listener a child that cannot spawn hangs the command.
752
+ child.on('error', fail);
753
+ // The signal too, since a child killed from outside closes with no code.
754
+ child.on('close', (code, signal) => done({ code, signal }));
755
+ });
756
+ return { code, signal, stdout: await readFile(out, 'utf8'), stderr: await readFile(err, 'utf8') };
757
+ }
758
+ finally {
759
+ for (const fd of opened)
760
+ closeSync(fd);
761
+ await rm(scratch, { recursive: true, force: true });
762
+ }
763
+ };
721
764
  const envelopeOf = async (cwd, argv) => {
722
765
  const { code, signal, stdout, stderr } = await kit(cwd, argv);
723
766
  try {
@@ -69,7 +69,7 @@ export declare const rules: {
69
69
  primaryKey(): import("drizzle-orm/pg-core").AnyPgColumnBuilder & /*elided*/ any;
70
70
  unique(): import("drizzle-orm/pg-core").AnyPgColumnBuilder & /*elided*/ any;
71
71
  };
72
- value: string | number | boolean | Date | readonly unknown[] | import("../mithaq/user.ts").Ref;
72
+ value: Date | readonly unknown[] | import("../mithaq/user.ts").Ref | import("../index.ts").Json;
73
73
  field: import("../index.ts").ResolvedField;
74
74
  }) => import("drizzle-orm/pg-core").AnyPgColumnBuilder & {
75
75
  default(value: unknown): import("drizzle-orm/pg-core").AnyPgColumnBuilder & any;
package/dist/index.d.ts CHANGED
@@ -4,42 +4,32 @@ export { betterAuth, betterAuthStrategy } from './auth/better-auth/index.ts';
4
4
  export { defaultLockTime, defaultMaxLoginAttempts, defaultTokenExpiration, local, localAuthFields, localAuthOperations, localAuthStrategy } from './auth/local/index.ts';
5
5
  export { actorPermissions, actorRoles, authActor } from './asas/actor.ts';
6
6
  export { assignableRole, grantedPermissions, writableRoleName } from './rbac/steps.ts';
7
- export { betterAuthTarget, type BetterAuthAccessOptions } from './auth/better-auth/access.ts';
8
- export { betterAuthTenant } from './auth/better-auth/tenant.ts';
9
- export { heldPermissions, rbacTarget, type AccessOptions, type AccessTarget, type Assigned, type CommandActor, type SystemRole, type SystemRoles } from './rbac/index.ts';
7
+ export * from './targets.ts';
8
+ export { heldPermissions, type Assigned, type CommandActor } from './rbac/index.ts';
10
9
  export { holdsPermission, requestOrganization } from './auth/better-auth/index.ts';
11
10
  export type { AssignedResolver, AuthStrategy, KayanAuth } from './asas/actor.ts';
12
- export type { AuthConfig, HttpPlug, Operation, OperationTable } from './asas/build.ts';
13
- export { fetchHttp, type FetchHttpOptions } from './asas/http/fetch.ts';
14
- export { AfterCommitError, AsasError, conflict, denied, invalid, missing, Notice, type MessageKey } from './asas/runner/errors.ts';
11
+ export type { AuthConfig, HttpPlug, Operation, OperationTable, Vocabulary } from './asas/build.ts';
12
+ export { defaultMaxBodyBytes, fetchHttp, type FetchHttpOptions } from './asas/http/fetch.ts';
13
+ export { AfterCommitError, AsasError, conflict, denied, invalid, missing, retryConflict, Notice, type MessageKey } from './asas/runner/errors.ts';
15
14
  export { audit, count, defaultOperations, defaultGlobalOperations, defaults, find, findOne, findVersionByID, findVersions, insert, remove, restoreVersion, update, updateMany, deleteMany, validate, type BulkResult, type Context, type CommonStep, type CommonTools, type ContextOf, type Ctx, type CtxOf, type AuditHandler, type Derives, type FieldsOf, type DerivedSchemas, type DerivedShapes, type SchemasOf, type Lists, type OperationDeclaration, type OperationEffect, type OperationStep, type OperationSteps, type ProjectStep, type SlotSteps, type Operations, type OperationDefaults, type ReadMode, type ReadStep, type ReadTools, type ResolvedOperation, type Sealed, type Step, type StepsOf, type Tools, type WriteStep, type WriteTools, } from './asas/runner/steps.ts';
16
- export { defaultAuthorize } from './asas/runner/write.ts';
17
15
  export type { Authorize, BoundKayans, ByID, CreateOf, Globals, IdOf, Options, Procedures, ReadOptions, Row, RowOf, UpdateOf, Version, Writes } from './asas/runner/write.ts';
18
16
  export type { Where } from './mithaq/where.ts';
19
17
  export { defaultFields, defaultGlobalFields, defaultIdField, defaultTimestampFields } from './base/fields.ts';
20
18
  export { uploadFields } from './storage/storage.ts';
21
19
  export { accessFor, fieldAccessFor } from './mithaq/types.ts';
22
- export type { Access, AccessArgs, AccessResult, AdminAccess, AdminAccessArgs, CompoundIndex, FieldAccess, FieldAccessArgs, Field, Kayan, KayanService, LabelFunction, Localized, LocalesOf, MessageMap, MessageTree, ResolvedField, ResolvedKayan, RuntimeField } from './mithaq/types.ts';
20
+ export type { Access, AccessArgs, AccessResult, AdminAccess, AdminAccessArgs, CompoundIndex, FieldAccess, FieldAccessArgs, Field, FilterOptions, JsonSchema, Kayan, KayanService, LabelFunction, Localized, LocalesOf, MessageMap, MessageTree, ResolvedField, ResolvedKayan, RuntimeField } from './mithaq/types.ts';
23
21
  export type { Copy, MessageRegistry, MessageScope, MessagesResolver, ProjectMessages } from './mithaq/messages.ts';
24
- export { defaultKinds, kind, type Kind, type OnDelete } from './base/kinds.ts';
25
- export type { RichTextNode, RichTextValue } from './rich-text/rich-text.ts';
22
+ export { defaultJsonMaxBytes, defaultKinds, isJson, kind, type Json, type Kind, type OnDelete } from './base/kinds.ts';
23
+ export { jsonSchemaCheck } from './base/json-schema.ts';
24
+ export type { RichTextNode, RichTextValue } from './rich-text/value.ts';
26
25
  export { defineRukn, type KayanConfigs, type Rukn } from './mithaq/rukn.ts';
27
26
  export { base } from './base/index.ts';
28
- export { richText } from './rich-text/index.ts';
29
27
  export { storage, upload } from './storage/index.ts';
30
28
  export { versions } from './versions/index.ts';
31
29
  export { defineMithaq, isAccess, isScreen, isService, isStorage, isStore, isTenant, isVersions, type Command, type CommandInput, type Language, type SchemaTarget, type Store, type Target, type TenantField, type TenantTarget } from './mithaq/language.ts';
32
30
  export { Refusal } from './mithaq/refusal.ts';
33
31
  export { defaultRules, providerOf, rule, type ProvidedBy, type Provider, type Rule, type Rules, type RuleValue } from './base/rules.ts';
34
- export { drizzleAdapter, drizzleTarget, type Adapter, type ColumnBuilder, type ColumnRule, } from './drizzle/index.ts';
35
- export { kinds as drizzleKinds } from './drizzle/kinds.ts';
36
- export { defaultBulkLimit, defaultDepth, defaultJoinLimit, defaultLimit, defaultMaxLimit, serviceTarget, type ServiceTarget } from './service/index.ts';
37
- export { versionsTarget, type KayanVersions, type VersionsTarget } from './versions/index.ts';
38
32
  export { draftFields, versionOperations } from './versions/versions.ts';
39
- export { adapterOf, allowedType, checkReference, defaultFileSize, fileHeaders, readForFile, sanitizeFilename, signReference, storageTarget, withUploadReference, type KayanStorage, type StorageAdapter, type StorageConfig, type StorageKayan, type StorageStep, type StorageTarget, type StoredFile, type SweepArgs, type UploadInstructions, type UploadReference, type UploadRequest, type Verified, type VerifyArgs, } from './storage/storage.ts';
40
- export { screenKinds, folderOf, screenTarget, type ScreenKind, type ScreenButton, type ScreenFrame, type ScreenLayout, type ScreenOwnPage, type ScreenRegions, type Ui, type ScreenField, type ScreenKayan, type ScreenLeaf, type ScreenNode, type ScreenPage, type ScreenRef, type ScreenTarget, } from './screen/index.ts';
41
- export { rules as drizzleRules } from './drizzle/rules.ts';
33
+ export { adapterOf, allowedType, checkReference, fileHeaders, readForFile, sanitizeFilename, signReference, withUploadReference, type KayanStorage, type StorageAdapter, type StorageKayan, type StorageStep, type StoredFile, type SweepArgs, type UploadInstructions, type UploadReference, type UploadRequest, type Verified, type VerifyArgs, } from './storage/storage.ts';
34
+ export { folderOf, type ScreenKind, type ScreenButton, type ScreenFrame, type ScreenLayout, type ScreenOwnPage, type ScreenRegions, type Ui, type ScreenField, type ScreenKayan, type ScreenLeaf, type ScreenNode, type ScreenPage, type ScreenRef, } from './screen/index.ts';
42
35
  export type { InferInput, InferOutput, StandardSchemaV1 } from './mithaq/standard-schema.ts';
43
- export { zodTarget, type ZodRule, type ZodShape } from './zod/index.ts';
44
- export { kinds as zodKinds } from './zod/kinds.ts';
45
- export { rules as zodRules } from './zod/rules.ts';
package/dist/index.js CHANGED
@@ -3,34 +3,24 @@ export { betterAuth, betterAuthStrategy } from './auth/better-auth/index.js';
3
3
  export { defaultLockTime, defaultMaxLoginAttempts, defaultTokenExpiration, local, localAuthFields, localAuthOperations, localAuthStrategy } from './auth/local/index.js';
4
4
  export { actorPermissions, actorRoles, authActor } from './asas/actor.js';
5
5
  export { assignableRole, grantedPermissions, writableRoleName } from './rbac/steps.js';
6
- export { betterAuthTarget } from './auth/better-auth/access.js';
7
- export { betterAuthTenant } from './auth/better-auth/tenant.js';
8
- export { heldPermissions, rbacTarget } from './rbac/index.js';
6
+ export * from './targets.js';
7
+ export { heldPermissions } from './rbac/index.js';
9
8
  export { holdsPermission, requestOrganization } from './auth/better-auth/index.js';
10
- export { fetchHttp } from './asas/http/fetch.js';
11
- export { AfterCommitError, AsasError, conflict, denied, invalid, missing, Notice } from './asas/runner/errors.js';
9
+ export { defaultMaxBodyBytes, fetchHttp } from './asas/http/fetch.js';
10
+ export { AfterCommitError, AsasError, conflict, denied, invalid, missing, retryConflict, Notice } from './asas/runner/errors.js';
12
11
  export { audit, count, defaultOperations, defaultGlobalOperations, defaults, find, findOne, findVersionByID, findVersions, insert, remove, restoreVersion, update, updateMany, deleteMany, validate, } from './asas/runner/steps.js';
13
- export { defaultAuthorize } from './asas/runner/write.js';
14
12
  export { defaultFields, defaultGlobalFields, defaultIdField, defaultTimestampFields } from './base/fields.js';
15
13
  export { uploadFields } from './storage/storage.js';
16
14
  export { accessFor, fieldAccessFor } from './mithaq/types.js';
17
- export { defaultKinds, kind } from './base/kinds.js';
15
+ export { defaultJsonMaxBytes, defaultKinds, isJson, kind } from './base/kinds.js';
16
+ export { jsonSchemaCheck } from './base/json-schema.js';
18
17
  export { defineRukn } from './mithaq/rukn.js';
19
18
  export { base } from './base/index.js';
20
- export { richText } from './rich-text/index.js';
21
19
  export { storage, upload } from './storage/index.js';
22
20
  export { versions } from './versions/index.js';
23
21
  export { defineMithaq, isAccess, isScreen, isService, isStorage, isStore, isTenant, isVersions } from './mithaq/language.js';
24
22
  export { Refusal } from './mithaq/refusal.js';
25
23
  export { defaultRules, providerOf, rule } from './base/rules.js';
26
- export { drizzleAdapter, drizzleTarget, } from './drizzle/index.js';
27
- export { kinds as drizzleKinds } from './drizzle/kinds.js';
28
- export { defaultBulkLimit, defaultDepth, defaultJoinLimit, defaultLimit, defaultMaxLimit, serviceTarget } from './service/index.js';
29
- export { versionsTarget } from './versions/index.js';
30
24
  export { draftFields, versionOperations } from './versions/versions.js';
31
- export { adapterOf, allowedType, checkReference, defaultFileSize, fileHeaders, readForFile, sanitizeFilename, signReference, storageTarget, withUploadReference, } from './storage/storage.js';
32
- export { screenKinds, folderOf, screenTarget, } from './screen/index.js';
33
- export { rules as drizzleRules } from './drizzle/rules.js';
34
- export { zodTarget } from './zod/index.js';
35
- export { kinds as zodKinds } from './zod/kinds.js';
36
- export { rules as zodRules } from './zod/rules.js';
25
+ export { adapterOf, allowedType, checkReference, fileHeaders, readForFile, sanitizeFilename, signReference, withUploadReference, } from './storage/storage.js';
26
+ export { folderOf, } from './screen/index.js';