@isaijs/core 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
- import { r as AuthTablesOptions } from "../data-layer-D1BFo3wO.js";
2
- import { A as Permissions, F as DefinePolicy, I as Perms, L as Policy, N as AnyPolicy, P as Bound, R as Rules, a as OAuthMethod, c as Roles, i as Methods, l as SignInMethod, n as AuthConfig, o as Provider, r as AuthDefinition, s as RoleDefinition, t as Auth, u as createAuth, z as StandardAction } from "../index-DlgVg3D8.js";
1
+ import { r as AuthTablesOptions } from "../data-layer-43pme0Gy.js";
2
+ import { A as Permissions, F as Policy, I as Rules, L as StandardAction, M as Bound, N as DefinePolicy, P as Perms, a as OAuthMethod, c as Roles, i as Methods, j as AnyPolicy, l as SignInMethod, n as AuthConfig, o as Provider, r as AuthDefinition, s as RoleDefinition, t as Auth, u as createAuth } from "../index-D0FEnMhn.js";
3
3
  export { type AnyPolicy, Auth, type AuthConfig, type AuthDefinition, type AuthTablesOptions, type Bound, type DefinePolicy, type Methods, type OAuthMethod, type Permissions, type Perms, Policy, type Provider, type RoleDefinition, type Roles, type Rules, type SignInMethod, type StandardAction, createAuth };
@@ -1,3 +1,3 @@
1
- import { t as Policy } from "../policies-D_G5Rmxv.js";
2
- import { a as createAuth, t as Auth } from "../create-auth-9y_43vJf.js";
1
+ import { t as Policy } from "../policies-KsqmlX67.js";
2
+ import { a as createAuth, t as Auth } from "../create-auth-BHdX68Ng.js";
3
3
  export { Auth, Policy, createAuth };
@@ -0,0 +1,63 @@
1
+ //#region src/internal/ulid.ts
2
+ /**
3
+ * ULIDs: 48 bits of milliseconds + 80 random bits, Crockford base32, 26 characters.
4
+ * Generated in JS (works inside a D1 batch, the same on every driver) and sortable by time.
5
+ *
6
+ * Monotonic: ids made in the same millisecond (or after the clock stepped back) reuse the
7
+ * last time and add one to the last random part, so ids always sort in the order they were
8
+ * made within an isolate or process.
9
+ */
10
+ const ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
11
+ const RANDOM_LENGTH = 16;
12
+ function encodeTime(time) {
13
+ let rest = time;
14
+ let text = "";
15
+ for (let i = 0; i < 10; i++) {
16
+ text = ALPHABET.charAt(rest % 32) + text;
17
+ rest = Math.floor(rest / 32);
18
+ }
19
+ return text;
20
+ }
21
+ function randomDigits() {
22
+ return Array.from(crypto.getRandomValues(new Uint8Array(RANDOM_LENGTH)), (byte) => byte % 32);
23
+ }
24
+ /** Adds one to base-32 digits in place; false when they were all at the maximum. */
25
+ function increment(digits) {
26
+ for (let i = digits.length - 1; i >= 0; i--) {
27
+ if ((digits[i] ?? 0) < 31) {
28
+ digits[i] = (digits[i] ?? 0) + 1;
29
+ return true;
30
+ }
31
+ digits[i] = 0;
32
+ }
33
+ return false;
34
+ }
35
+ function monotonicUlid() {
36
+ let lastTime = -1;
37
+ let lastRandom = [];
38
+ return (now) => {
39
+ const time = now.getTime();
40
+ if (time > lastTime) {
41
+ lastTime = time;
42
+ lastRandom = randomDigits();
43
+ } else if (!increment(lastRandom)) {
44
+ lastTime += 1;
45
+ lastRandom = randomDigits();
46
+ }
47
+ return encodeTime(lastTime) + lastRandom.map((digit) => ALPHABET.charAt(digit)).join("");
48
+ };
49
+ }
50
+ /** The process-wide generator every write uses. */
51
+ const ulid = monotonicUlid();
52
+ //#endregion
53
+ //#region src/internal/case.ts
54
+ /** `manageProducts` → `manage-products`, `markPaid` → `mark-paid`: path segments. */
55
+ function kebabCase(name) {
56
+ return snakeCase(name).replace(/_/g, "-");
57
+ }
58
+ /** `RegistrationMember` → `registration_member`, `startsAt` → `starts_at`. */
59
+ function snakeCase(name) {
60
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();
61
+ }
62
+ //#endregion
63
+ export { snakeCase as n, ulid as r, kebabCase as t };
package/dist/cli/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { V as toKind, z as object } from "../validation-Cppmd0RV.js";
3
- import { M as dialectFor, m as emptySnapshot, r as sync, t as migrate, y as MigrationError } from "../migrator-BU3_PHk8.js";
4
- import { a as lookupsOf, i as filterableOf, n as isRequired, t as Resource, u as Route } from "../resource-CkzoR3Rq.js";
5
- import { F as sensitivity, c as fieldOf, r as Model } from "../base64url-C5GTYIL5.js";
6
- import { t as kebabCase } from "../case-BnpcvhdW.js";
7
- import { i as makeMigrations, n as checkSteps, t as checkMigrations } from "../make-DKBn79cH.js";
3
+ import { F as dialectFor, m as emptySnapshot, r as sync, t as migrate, y as MigrationError } from "../migrator-B25prc9n.js";
4
+ import { a as lookupsOf, i as filterableOf, n as isRequired, t as Resource, u as Route } from "../resource-BQK7bkPQ.js";
5
+ import { o as fieldOf, p as sensitivity, t as Model } from "../model-Dc3HunKb.js";
6
+ import { t as kebabCase } from "../case-DpXlJCL9.js";
7
+ import { i as makeMigrations, n as checkSteps, t as checkMigrations } from "../make-ByA-84h8.js";
8
8
  import { d1 } from "../adapters/database/d1/index.js";
9
9
  import { spawn } from "node:child_process";
10
10
  import { parseArgs } from "node:util";
@@ -57,7 +57,11 @@ function typeOf(node, models, registry) {
57
57
  //#endregion
58
58
  //#region src/codegen/client.ts
59
59
  /** Names the client has for itself; an export can't take them. */
60
- const TAKEN = /* @__PURE__ */ new Set(["auth", "onSignedOut"]);
60
+ const TAKEN = /* @__PURE__ */ new Set([
61
+ "auth",
62
+ "onSignedOut",
63
+ "onPending"
64
+ ]);
61
65
  function generateClient(source) {
62
66
  const names = [...source.resources.map((r) => r.name), ...source.routes.map((r) => r.name)];
63
67
  const clash = names.find((name, i) => names.indexOf(name) !== i || TAKEN.has(name));
@@ -181,6 +181,11 @@ interface ClientBase<U> {
181
181
  readonly auth: AuthApi<U>;
182
182
  /** Called on any 401 (but `auth.me()`'s): the session ended or never was. Returns an unsubscribe. */
183
183
  onSignedOut(listener: () => void): () => void;
184
+ /**
185
+ * Called with how many writes are waiting to be saved: now, then on every change (0 once all
186
+ * are). Returns an unsubscribe.
187
+ */
188
+ onPending(listener: (count: number) => void): () => void;
184
189
  }
185
190
  /** The client for an app's endpoints; the generated `createApi` types it. */
186
191
  export declare function createClient(endpoints: Endpoints, options?: ClientOptions): unknown;
@@ -1,4 +1,4 @@
1
- import { t as kebabCase } from "../case-BnpcvhdW.js";
1
+ import { r as ulid, t as kebabCase } from "../case-DpXlJCL9.js";
2
2
  //#region src/client/errors.ts
3
3
  /**
4
4
  * A failed call: the API's one error shape,
@@ -160,19 +160,50 @@ async function encoded(canvas, name, clear) {
160
160
  * (a singleton's calls take no key)
161
161
  * routes api.<name>(args) (path params and input in one object)
162
162
  * auth api.auth.me() · signIn() · signOut() · users · invites
163
+ *
164
+ * Writes survive a dropping connection. Every write (any call but a GET) carries an
165
+ * `Idempotency-Key` of its own, and goes out through one queue, in the order it was made: a
166
+ * product isn't created before the upload it names. A write the network lost (`fetch` threw, the
167
+ * connection went offline under it, or a 5xx came from a proxy rather than the app) is sent again
168
+ * with the same key, so the server runs it once (`api/idempotency.ts`); so is one the server says
169
+ * is still running (409 `InProgress`). It waits 1 s, 2 s, 4 s… up to 30 s between tries, and for
170
+ * the browser to be back online, and never gives up: the call resolves once it's saved. An answer
171
+ * from the app (a 400, a refusal, a 500) fails the call as it did. `api.onPending(count => …)`
172
+ * says how many writes are waiting, for a "saving…" sign; while any are, the page asks before
173
+ * it's closed. Reads aren't queued or retried: a frontend's cache retries them.
163
174
  */
164
175
  const QUERY_METHODS = /* @__PURE__ */ new Set(["GET", "DELETE"]);
176
+ /** The waits between a lost write's tries, in ms; the last repeats. */
177
+ const BACKOFF = [
178
+ 1e3,
179
+ 2e3,
180
+ 4e3,
181
+ 8e3,
182
+ 16e3,
183
+ 3e4
184
+ ];
165
185
  /** The client for an app's endpoints; the generated `createApi` types it. */
166
186
  function createClient(endpoints, options = {}) {
167
187
  const base = (options.baseUrl ?? "/api").replace(/\/$/, "");
168
188
  const send = options.fetch ?? ((input, init) => fetch(input, init));
169
189
  const listeners = /* @__PURE__ */ new Set();
170
- async function call(method, path, { query, body, headers: extra, signedOutIsAnswer = false } = {}) {
190
+ const writes = writeQueue();
191
+ function call(method, path, options = {}) {
192
+ if (method === "GET") return attempt(method, path, options);
193
+ const key = { "Idempotency-Key": ulid(/* @__PURE__ */ new Date()) };
194
+ return writes.add(() => retried((signal) => attempt(method, path, {
195
+ ...options,
196
+ key,
197
+ signal
198
+ })));
199
+ }
200
+ async function attempt(method, path, { query, body, headers: extra, key, signal, signedOutIsAnswer = false }) {
171
201
  const search = query && query.size > 0 ? `?${query.toString().replace(/=(?=&|$)/g, "")}` : "";
172
202
  const headers = {
173
203
  Accept: "application/json",
174
204
  ...options.headers,
175
- ...extra
205
+ ...extra,
206
+ ...key
176
207
  };
177
208
  const file = body instanceof Blob ? body : void 0;
178
209
  if (file) {
@@ -183,8 +214,10 @@ function createClient(endpoints, options = {}) {
183
214
  const response = await send(`${base}${path}${search}`, {
184
215
  method,
185
216
  headers,
186
- ...body === void 0 ? {} : { body: file ?? JSON.stringify(body) }
217
+ ...body === void 0 ? {} : { body: file ?? JSON.stringify(body) },
218
+ ...signal ? { signal } : {}
187
219
  });
220
+ if (key && await lost(response)) throw new Lost();
188
221
  if (response.status === 401 && signedOutIsAnswer) return void 0;
189
222
  if (!response.ok) {
190
223
  const error = await errorOf(response);
@@ -199,7 +232,8 @@ function createClient(endpoints, options = {}) {
199
232
  onSignedOut(listener) {
200
233
  listeners.add(listener);
201
234
  return () => listeners.delete(listener);
202
- }
235
+ },
236
+ onPending: writes.onPending
203
237
  };
204
238
  for (const [name, endpoint] of Object.entries(endpoints)) {
205
239
  if (Object.hasOwn(api, name)) throw new TypeError(`isai: the client's ${name} is taken`);
@@ -207,6 +241,86 @@ function createClient(endpoints, options = {}) {
207
241
  }
208
242
  return api;
209
243
  }
244
+ /** A try the network lost: sent again. */
245
+ var Lost = class extends Error {};
246
+ /**
247
+ * A write's answer that isn't the app's: a 5xx from a proxy (every answer from the app carries
248
+ * `X-Request-Id`), or the app saying the same key is still running.
249
+ */
250
+ async function lost(response) {
251
+ if (response.status >= 500) return !response.headers.has("X-Request-Id");
252
+ if (response.status !== 409) return false;
253
+ return (await response.clone().json().catch(() => ({}))).error?.code === "InProgress";
254
+ }
255
+ /**
256
+ * `run` until it's answered: again after each try the network lost, waiting longer each time and
257
+ * for the browser to be online. A try in flight is aborted when the browser goes offline, since a
258
+ * request on a dropped connection may never end.
259
+ */
260
+ async function retried(run) {
261
+ for (let tries = 0;; tries++) {
262
+ const aborted = new AbortController();
263
+ const stop = on("offline", () => aborted.abort());
264
+ try {
265
+ return await run(aborted.signal);
266
+ } catch (error) {
267
+ if (!(error instanceof Lost) && !(error instanceof TypeError) && !aborted.signal.aborted) throw error;
268
+ } finally {
269
+ stop();
270
+ }
271
+ await Promise.all([pause(BACKOFF[Math.min(tries, BACKOFF.length - 1)]), online()]);
272
+ }
273
+ }
274
+ const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
275
+ /** Resolves once the browser is online (at once outside one). */
276
+ function online() {
277
+ if (globalThis.navigator?.onLine !== false) return Promise.resolve();
278
+ return new Promise((resolve) => {
279
+ const stop = on("online", () => {
280
+ stop();
281
+ resolve();
282
+ });
283
+ });
284
+ }
285
+ /** Listens for a window event; nothing outside a browser. Returns the unsubscribe. */
286
+ function on(event, listener) {
287
+ const target = globalThis;
288
+ if (typeof target.addEventListener !== "function") return () => {};
289
+ target.addEventListener(event, listener);
290
+ return () => globalThis.removeEventListener(event, listener);
291
+ }
292
+ /**
293
+ * Writes one at a time, in the order they were added, each settling as it's done. A failed one
294
+ * doesn't hold back the next. While any are waiting, closing the page asks first.
295
+ */
296
+ function writeQueue() {
297
+ let last = Promise.resolve();
298
+ let pending = 0;
299
+ const listeners = /* @__PURE__ */ new Set();
300
+ let stopAsking;
301
+ const changed = (by) => {
302
+ pending += by;
303
+ if (pending > 0) stopAsking ??= on("beforeunload", (event) => event.preventDefault());
304
+ else {
305
+ stopAsking?.();
306
+ stopAsking = void 0;
307
+ }
308
+ for (const listener of [...listeners]) listener(pending);
309
+ };
310
+ return {
311
+ add(write) {
312
+ changed(1);
313
+ const done = last.then(write);
314
+ last = done.catch(() => void 0);
315
+ return done.finally(() => changed(-1));
316
+ },
317
+ onPending(listener) {
318
+ listeners.add(listener);
319
+ listener(pending);
320
+ return () => listeners.delete(listener);
321
+ }
322
+ };
323
+ }
210
324
  const segment = (value) => encodeURIComponent(String(value));
211
325
  function resourceCalls(call, endpoint) {
212
326
  const base = endpoint.resource;
@@ -1,6 +1,6 @@
1
- import { t as fromBase64url } from "./base64url-C5GTYIL5.js";
2
- import { n as PROVIDERS } from "./tables-IrLjsomf.js";
3
- import { a as byRole, c as tenant, i as anyone, l as user, n as PolicyRegistry, o as owner, s as readsTenant } from "./policies-D_G5Rmxv.js";
1
+ import { I as fromBase64url } from "./model-Dc3HunKb.js";
2
+ import { n as PROVIDERS } from "./tables-DgSwge7s.js";
3
+ import { a as byRole, c as tenant, i as anyone, l as user, n as PolicyRegistry, o as owner, s as readsTenant } from "./policies-KsqmlX67.js";
4
4
  //#region src/auth/create-auth.ts
5
5
  const methods = { oauth: (provider, options) => ({
6
6
  kind: "oauth",
@@ -1,7 +1,7 @@
1
1
  import { a as DatabaseDriver, o as DialectName } from "./index-Ce5p2-EB.js";
2
2
  import { t as Issue } from "./issues-CKto95IW.js";
3
3
  import { M as IdInput, Y as StoredFile } from "./index-CYcklQ1l.js";
4
- import { A as Dialect, G as Model, H as Columns, I as Clock, K as ModelSpec, M as RetiredSnapshot, N as SchemaSnapshot, U as ComputedFields, V as AnyModel, W as Fields, X as Condition, at as count, b as RowOf, dt as Kinds, et as Now, f as seed, ft as SchemaDefaults, ht as Field, i as Row, it as any, mt as AnyField, n as Patch, ot as not, p as Database, q as ModelRegistry, rt as all, yt as Slot } from "./types-S6zF5IXV.js";
4
+ import { $ as Expr, A as Dialect, G as Model, H as Columns, I as Clock, K as ModelSpec, M as RetiredSnapshot, N as SchemaSnapshot, U as ComputedFields, V as AnyModel, W as Fields, X as Condition, at as count, b as RowOf, dt as Kinds, et as Now, f as seed, ft as SchemaDefaults, ht as Field, i as Row, it as any, mt as AnyField, n as Patch, ot as not, p as Database, q as ModelRegistry, rt as all, yt as Slot } from "./types-BgOkJy5d.js";
5
5
  //#region src/db/schema/traits.d.ts
6
6
  type ComputedDefinitions = Record<string, (row: any) => Condition>;
7
7
  interface TraitSpec {
@@ -948,6 +948,17 @@ type Base = Kinds & {
948
948
  type ProjectKinds<P> = { readonly [K in keyof P]: P[K] extends AnyField ? () => P[K] : P[K]; };
949
949
  /** The placeholder for conditions over a model's rows: `const event = row(Event)`. */
950
950
  declare function row<M extends AnyModel>(target: M): Columns<M["~fields"], M["name"]>;
951
+ /** Every row of a model, for a rule that counts them. See `rows`. */
952
+ interface TableRows<Rows> {
953
+ /** How many rows there are, or how many match. */
954
+ count(build?: (row: Rows) => Condition): Expr<number>;
955
+ }
956
+ /**
957
+ * Every row of a model, for a rule over all of them: `rows(Product).count()` in a policy's create
958
+ * rule caps how many a tenant may have (`create: rows(Product).count().lt(tenant.maxProducts)`).
959
+ * A create checks it with one query before the insert; two creates at once may both pass.
960
+ */
961
+ declare function rows<M extends AnyModel>(target: M): TableRows<Columns<M["~fields"], M["name"]>>;
951
962
  declare function createDataLayer<P extends Record<string, ProjectKind> = {}, const U extends readonly AnyTableSet[] = []>(define?: (base: Base) => DataLayerDefinition<P, U>): {
952
963
  model: {
953
964
  <const Name extends string, const T extends readonly AnyTrait[], F extends Fields>(name: Name, ...rest: [...traits: T, fields: F]): Model<Name, TraitFields<T> & F>;
@@ -979,6 +990,7 @@ declare function createDataLayer<P extends Record<string, ProjectKind> = {}, con
979
990
  };
980
991
  defaults: Readonly<SchemaDefaults>;
981
992
  row: typeof row;
993
+ rows: typeof rows;
982
994
  all: typeof all;
983
995
  any: typeof any;
984
996
  not: typeof not;
@@ -1,7 +1,7 @@
1
1
  import { F as quantity, k as datetime, m as host, r as isPhoneRegion } from "./validation-Cppmd0RV.js";
2
- import "./migrator-BU3_PHk8.js";
3
- import { C as not, M as field, N as fieldKinds, S as count, s as defineModel, u as rowOf, v as all, w as now, y as any } from "./base64url-C5GTYIL5.js";
4
- import { a as TableSet, i as platformAuthDefinitions, o as defineBuiltins, r as authTables } from "./tables-IrLjsomf.js";
2
+ import "./migrator-B25prc9n.js";
3
+ import { D as now, E as not, S as any, T as count, a as defineModel, c as rowOf, d as fieldKinds, u as field, x as all, y as Relation } from "./model-Dc3HunKb.js";
4
+ import { a as TableSet, i as platformAuthDefinitions, o as defineBuiltins, r as authTables } from "./tables-DgSwge7s.js";
5
5
  import { n as seed } from "./seed-BObmdKdP.js";
6
6
  //#region src/db/fields/slot.ts
7
7
  var Slot = class {
@@ -233,6 +233,18 @@ const isOptions = (value) => typeof value === "object" && value !== null && !(va
233
233
  function row(target) {
234
234
  return rowOf(target.spec, target.registry);
235
235
  }
236
+ /**
237
+ * Every row of a model, for a rule over all of them: `rows(Product).count()` in a policy's create
238
+ * rule caps how many a tenant may have (`create: rows(Product).count().lt(tenant.maxProducts)`).
239
+ * A create checks it with one query before the insert; two creates at once may both pass.
240
+ */
241
+ function rows(target) {
242
+ const { spec, registry } = target;
243
+ return new Relation({
244
+ table: spec.table,
245
+ column: ""
246
+ }, (inner) => rowOf(spec, registry, inner));
247
+ }
236
248
  function checkDefaults(defaults) {
237
249
  const { phoneRegion, currency } = defaults;
238
250
  if (phoneRegion !== void 0 && !isPhoneRegion(phoneRegion)) throw new TypeError(`isai: defaults.phoneRegion "${phoneRegion}" isn't a region (an ISO country code such as "IN")`);
@@ -286,6 +298,7 @@ function createDataLayer(define) {
286
298
  traits: builtinTraits(),
287
299
  defaults,
288
300
  row,
301
+ rows,
289
302
  all,
290
303
  any,
291
304
  not,
@@ -1,4 +1,4 @@
1
1
  import { a as ValidationError } from "../issues-CKto95IW.js";
2
- import { $ as Expr, A as Dialect, B as newSecretKey, F as snapshotOf, G as Model, I as Clock, J as Models, K as ModelSpec, L as FixedClock, M as RetiredSnapshot, N as SchemaSnapshot, O as Write, P as emptySnapshot, R as fixedClock, V as AnyModel, X as Condition, Y as Column, _ as database, _t as Storage, a as Visible, at as count, bt as slot, c as Many, d as SeedContext, dt as Kinds, et as Now, ft as SchemaDefaults, g as Transaction, gt as FieldSpec, h as RawValue, ht as Field, i as Row, it as any, j as Rule, k as WriteListener, l as Overrides, lt as ConditionNode, m as DatabaseOptions, n as Patch, nt as Total, o as Make, ot as not, p as Database, pt as fieldKinds, r as Planned, rt as all, st as now, t as Insert, tt as Relation, u as Seed, v as OrderKey, vt as sensitivity, y as Query, yt as Slot, z as systemClock } from "../types-S6zF5IXV.js";
3
- import { A as MigrateOptions, B as NotFound, C as MigrationErrorCode, D as Cleanup, E as UnsafeMigration, F as DataStep, H as UniqueFailed, I as TODO, L as dataStep, M as migrate, N as migrateFresh, O as cleanupMigration, P as sync, R as CheckFailed, S as MigrationError, T as UnsafeChange, U as dialectFor, V as ReferenceFailed, W as sqlite, _ as Generated, b as manifest, c as acquireLock, d as setReleaseStatus, f as startRelease, g as makeMigrations, h as makeCleanup, j as Migration, k as Manifest, l as releaseLock, m as checkMigrations, n as row, o as Release, p as applyRules, s as ReleaseStatus, t as createDataLayer, u as rollback, v as generateMigration, w as SuspectedRename, x as structureStep, y as cleanupStep, z as DatabaseError } from "../data-layer-D1BFo3wO.js";
2
+ import { $ as Expr, A as Dialect, B as newSecretKey, F as snapshotOf, G as Model, I as Clock, J as Models, K as ModelSpec, L as FixedClock, M as RetiredSnapshot, N as SchemaSnapshot, O as Write, P as emptySnapshot, R as fixedClock, V as AnyModel, X as Condition, Y as Column, _ as database, _t as Storage, a as Visible, at as count, bt as slot, c as Many, d as SeedContext, dt as Kinds, et as Now, ft as SchemaDefaults, g as Transaction, gt as FieldSpec, h as RawValue, ht as Field, i as Row, it as any, j as Rule, k as WriteListener, l as Overrides, lt as ConditionNode, m as DatabaseOptions, n as Patch, nt as Total, o as Make, ot as not, p as Database, pt as fieldKinds, r as Planned, rt as all, st as now, t as Insert, tt as Relation, u as Seed, v as OrderKey, vt as sensitivity, y as Query, yt as Slot, z as systemClock } from "../types-BgOkJy5d.js";
3
+ import { A as MigrateOptions, B as NotFound, C as MigrationErrorCode, D as Cleanup, E as UnsafeMigration, F as DataStep, H as UniqueFailed, I as TODO, L as dataStep, M as migrate, N as migrateFresh, O as cleanupMigration, P as sync, R as CheckFailed, S as MigrationError, T as UnsafeChange, U as dialectFor, V as ReferenceFailed, W as sqlite, _ as Generated, b as manifest, c as acquireLock, d as setReleaseStatus, f as startRelease, g as makeMigrations, h as makeCleanup, j as Migration, k as Manifest, l as releaseLock, m as checkMigrations, n as row, o as Release, p as applyRules, s as ReleaseStatus, t as createDataLayer, u as rollback, v as generateMigration, w as SuspectedRename, x as structureStep, y as cleanupStep, z as DatabaseError } from "../data-layer-43pme0Gy.js";
4
4
  export { type AnyModel, CheckFailed, type Cleanup, type Clock, Column, Condition, type ConditionNode, type DataStep, Database, DatabaseError, type DatabaseOptions, type Dialect, Expr, Field, type FieldSpec, type FixedClock, type Generated, type Insert, type Kinds, type Make, type Manifest, type Many, type MigrateOptions, type Migration, MigrationError, type MigrationErrorCode, Model, type ModelSpec, type Models, NotFound, Now, type OrderKey, type Overrides, type Patch, type Planned, Query, type RawValue, ReferenceFailed, Relation, type Release, type ReleaseStatus, type RetiredSnapshot, type Row, type Rule, type SchemaDefaults, type SchemaSnapshot, Seed, type SeedContext, Slot, type Storage, type SuspectedRename, TODO, Total, Transaction, UniqueFailed, type UnsafeChange, UnsafeMigration, ValidationError, type Visible, type Write, type WriteListener, acquireLock, all, any, applyRules, checkMigrations, cleanupMigration, cleanupStep, count, createDataLayer, dataStep, database, dialectFor, emptySnapshot, fieldKinds, fixedClock, generateMigration, makeCleanup, makeMigrations, manifest, migrate, migrateFresh, newSecretKey, not, now, releaseLock, rollback, row, sensitivity, setReleaseStatus, slot, snapshotOf, sqlite, startRelease, structureStep, sync, systemClock };
package/dist/db/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { G as ValidationError } from "../validation-Cppmd0RV.js";
2
- import { A as Query, C as database, F as systemClock, G as DatabaseError, I as newSecretKey, J as UniqueFailed, K as NotFound, M as dialectFor, N as sqlite, P as fixedClock, W as CheckFailed, _ as TODO, b as Database, c as rollback, d as applyRules, g as UnsafeMigration, h as snapshotOf, i as acquireLock, l as setReleaseStatus, m as emptySnapshot, n as migrateFresh, p as generateMigration, q as ReferenceFailed, r as sync, s as releaseLock, t as migrate, u as startRelease, v as dataStep, x as Transaction, y as MigrationError } from "../migrator-BU3_PHk8.js";
3
- import { C as not, F as sensitivity, N as fieldKinds, P as Field, S as count, _ as Total, d as Column, f as Condition, g as Relation, h as Now, m as Expr, r as Model, v as all, w as now, y as any } from "../base64url-C5GTYIL5.js";
4
- import { i as slot, n as row, r as Slot, t as createDataLayer } from "../data-layer-_TZtuy-F.js";
2
+ import { C as database, F as dialectFor, G as newSecretKey, H as fixedClock, I as sqlite, J as NotFound, K as CheckFailed, M as Query, U as systemClock, X as UniqueFailed, Y as ReferenceFailed, _ as TODO, b as Database, c as rollback, d as applyRules, g as UnsafeMigration, h as snapshotOf, i as acquireLock, l as setReleaseStatus, m as emptySnapshot, n as migrateFresh, p as generateMigration, q as DatabaseError, r as sync, s as releaseLock, t as migrate, u as startRelease, v as dataStep, x as Transaction, y as MigrationError } from "../migrator-B25prc9n.js";
3
+ import { D as now, E as not, S as any, T as count, _ as Expr, b as Total, d as fieldKinds, f as Field, h as Condition, m as Column, p as sensitivity, t as Model, v as Now, x as all, y as Relation } from "../model-Dc3HunKb.js";
4
+ import { i as slot, n as row, r as Slot, t as createDataLayer } from "../data-layer-C5oJZ8V1.js";
5
5
  import { t as Seed } from "../seed-BObmdKdP.js";
6
- import { a as cleanupMigration, i as makeMigrations, r as makeCleanup, t as checkMigrations } from "../make-DKBn79cH.js";
6
+ import { a as cleanupMigration, i as makeMigrations, r as makeCleanup, t as checkMigrations } from "../make-ByA-84h8.js";
7
7
  //#region src/db/migrations/files.ts
8
8
  /** A structure step file: adds tables and columns. Applied once, never edited. */
9
9
  function structureStep(options) {
@@ -1,7 +1,22 @@
1
1
  import { a as DatabaseDriver, b as TenantDatabases, d as RateLimiter, g as Storage, l as PdfRenderer, n as Body, p as Runtime, r as Cache, s as MailDriver, u as Queue } from "./index-Ce5p2-EB.js";
2
2
  import { C as ObjectShape, Y as StoredFile, w as Output } from "./index-CYcklQ1l.js";
3
- import { $ as Expr, C as WithIncludes, D as PageOptions, E as Page, J as Models, Q as Embed, S as Key, T as NumberedPageOptions, V as AnyModel, X as Condition, Z as Conditional, b as RowOf, ct as ColumnNode, gt as FieldSpec, i as Row, lt as ConditionNode, n as Patch, p as Database, t as Insert, ut as Operand, v as OrderKey, w as NumberedPage, x as IncludePath } from "./types-S6zF5IXV.js";
4
- import { a as PROVIDERS, i as HELD, k as Manifest } from "./data-layer-D1BFo3wO.js";
3
+ import { $ as Expr, C as WithIncludes, D as PageOptions, E as Page, J as Models, Q as Embed, S as Key, T as NumberedPageOptions, V as AnyModel, X as Condition, Z as Conditional, b as RowOf, ct as ColumnNode, gt as FieldSpec, i as Row, lt as ConditionNode, n as Patch, p as Database, t as Insert, u as Seed, ut as Operand, v as OrderKey, w as NumberedPage, x as IncludePath } from "./types-BgOkJy5d.js";
4
+ import { a as PROVIDERS, i as HELD, k as Manifest } from "./data-layer-43pme0Gy.js";
5
+ //#region src/api/fail.d.ts
6
+ /**
7
+ * `fail(code, message)`: the app's own business rules, for what can't be a `.check()`, a flow or a
8
+ * unique. Always a 409; app code never picks a status.
9
+ */
10
+ declare class Rejected extends Error {
11
+ /** Stable and machine-readable (`alreadyCheckedIn`). */
12
+ readonly code: string;
13
+ readonly name = "Rejected";
14
+ constructor(
15
+ /** Stable and machine-readable (`alreadyCheckedIn`). */
16
+ code: string, message: string);
17
+ }
18
+ declare function fail(code: string, message: string): never;
19
+ //#endregion
5
20
  //#region src/auth/policies.d.ts
6
21
  /** The actions every model's policy has. */
7
22
  declare const STANDARD_ACTIONS: readonly ["read", "create", "update", "delete"];
@@ -16,6 +31,15 @@ interface BuiltinUser {
16
31
  readonly emailVerifiedAt: Date | null;
17
32
  readonly suspendedAt: Date | null;
18
33
  }
34
+ type UserRow = Models extends {
35
+ readonly User: infer U extends AnyModel;
36
+ } ? Row<U> : BuiltinUser;
37
+ type TenantRow$1 = Models extends {
38
+ readonly Tenant: infer T extends AnyModel;
39
+ } ? Row<T> : {
40
+ readonly id: string;
41
+ readonly name: string;
42
+ };
19
43
  type Rules = Readonly<Record<string, Conditional>>;
20
44
  /** Each action's permission key, and `all` of them. */
21
45
  type Perms<Action extends string> = { readonly [K in Action]: string; } & {
@@ -55,21 +79,6 @@ interface DefinePolicy {
55
79
  <const N extends string, const R extends Rules>(name: N, rules: R): Policy<keyof R & string>;
56
80
  }
57
81
  //#endregion
58
- //#region src/api/fail.d.ts
59
- /**
60
- * `fail(code, message)`: the app's own business rules, for what can't be a `.check()`, a flow or a
61
- * unique. Always a 409; app code never picks a status.
62
- */
63
- declare class Rejected extends Error {
64
- /** Stable and machine-readable (`alreadyCheckedIn`). */
65
- readonly code: string;
66
- readonly name = "Rejected";
67
- constructor(
68
- /** Stable and machine-readable (`alreadyCheckedIn`). */
69
- code: string, message: string);
70
- }
71
- declare function fail(code: string, message: string): never;
72
- //#endregion
73
82
  //#region src/auth/roles.d.ts
74
83
  /** What a user's roles give them: every permission (`{ all: true }`), or these keys. */
75
84
  interface Permissions {
@@ -519,6 +528,12 @@ interface TenantHandle {
519
528
  * are copied (structured clones), so they're plain values.
520
529
  */
521
530
  run<S extends Service>(service: S, ...args: Parameters<S>): Promise<Awaited<ReturnType<S>>>;
531
+ /**
532
+ * Runs a seed from `db/seeds.ts` in the tenant, as `inTenant` does from the platform's seed: it
533
+ * fills only an empty database, so on one with rows it does nothing. On a deployed app too (a
534
+ * tenant filled with demo data).
535
+ */
536
+ run(seed: Seed): Promise<void>;
522
537
  }
523
538
  /** The app's seeds (`db/seeds.ts`), as a lazy import: `() => import("#db/seeds")`. */
524
539
  type AppSeeds = () => Promise<object>;
@@ -616,18 +631,21 @@ type RoleDefinition = {
616
631
  readonly all: true;
617
632
  } | readonly string[];
618
633
  type Roles = Readonly<Record<string, RoleDefinition>>;
634
+ /**
635
+ * What `createAuth` gives. Written out so `user` and `tenant` stay typed by the app's `Models`
636
+ * (its `User` and `Tenant` columns): an inferred type would be fixed to the built-in rows when
637
+ * isai's declarations are built.
638
+ */
639
+ interface CreatedAuth<Env> {
640
+ readonly auth: Auth<Env>;
641
+ readonly policy: DefinePolicy;
642
+ readonly user: Bound<UserRow>;
643
+ readonly tenant: Bound<TenantRow$1>;
644
+ readonly owner: Condition;
645
+ readonly byRole: Condition;
646
+ readonly anyone: Condition;
647
+ }
619
648
  /** The auth instance, from `auth.ts`, with `policy()` and the placeholders policies are written with. */
620
- declare function createAuth<Env = unknown>(define: (env: Env) => AuthDefinition): {
621
- auth: Auth<Env>;
622
- policy: DefinePolicy;
623
- user: Bound<BuiltinUser>;
624
- tenant: Bound<{
625
- readonly id: string;
626
- readonly name: string;
627
- }>;
628
- owner: Condition;
629
- byRole: Condition;
630
- anyone: Condition;
631
- };
649
+ declare function createAuth<Env = unknown>(define: (env: Env) => AuthDefinition): CreatedAuth<Env>;
632
650
  //#endregion
633
- export { Permissions as A, Params as C, RouteHandler as D, RouteContext as E, DefinePolicy as F, Perms as I, Policy as L, fail as M, AnyPolicy as N, RouteOptions as O, Bound as P, Rules as R, Method as S, RouteBuilder as T, CustomAction as _, OAuthMethod as a, AppConfig as b, Roles as c, NotImplementedError as d, AppModules as f, createApp as g, CreatedApp as h, Methods as i, Rejected as j, User as k, SignInMethod as l, AppOptions as m, AuthConfig as n, Provider as o, App as p, AuthDefinition as r, RoleDefinition as s, Auth as t, createAuth as u, Resource as v, Route as w, Named as x, ResourceOptions as y, StandardAction as z };
651
+ export { Permissions as A, Params as C, RouteHandler as D, RouteContext as E, Policy as F, Rules as I, StandardAction as L, Bound as M, DefinePolicy as N, RouteOptions as O, Perms as P, Rejected as R, Method as S, RouteBuilder as T, CustomAction as _, OAuthMethod as a, AppConfig as b, Roles as c, NotImplementedError as d, AppModules as f, createApp as g, CreatedApp as h, Methods as i, AnyPolicy as j, User as k, SignInMethod as l, AppOptions as m, AuthConfig as n, Provider as o, App as p, AuthDefinition as r, RoleDefinition as s, Auth as t, createAuth as u, Resource as v, Route as w, Named as x, ResourceOptions as y, fail as z };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { p as Runtime, t as AppCore } from "./index-Ce5p2-EB.js";
2
- import { C as Params, D as RouteHandler, E as RouteContext, M as fail, O as RouteOptions, S as Method, T as RouteBuilder, _ as CustomAction, b as AppConfig, d as NotImplementedError, f as AppModules, g as createApp, h as CreatedApp, j as Rejected, k as User, m as AppOptions, p as App, v as Resource, w as Route, x as Named, y as ResourceOptions } from "./index-DlgVg3D8.js";
2
+ import { C as Params, D as RouteHandler, E as RouteContext, O as RouteOptions, R as Rejected, S as Method, T as RouteBuilder, _ as CustomAction, b as AppConfig, d as NotImplementedError, f as AppModules, g as createApp, h as CreatedApp, k as User, m as AppOptions, p as App, v as Resource, w as Route, x as Named, y as ResourceOptions, z as fail } from "./index-D0FEnMhn.js";
3
3
  export { type App, type AppConfig, type AppCore, type AppModules, type AppOptions, type CreatedApp, type CustomAction, type Method, type Named, NotImplementedError, type Params, Rejected, type Resource, type ResourceOptions, Route, type RouteBuilder, type RouteContext, type RouteHandler, type RouteOptions, type Runtime, type User, createApp, fail };