@isaijs/core 0.1.6 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/storage/r2/index.d.ts +3 -0
- package/dist/adapters/storage/r2/index.js +13 -2
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +2 -2
- package/dist/cli/index.js +79 -11
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +18 -13
- package/dist/{create-auth-BFKvQVOG.js → create-auth-BCvMwlVD.js} +3 -3
- package/dist/{data-layer-C5oJZ8V1.js → data-layer-BOVkYESH.js} +15 -10
- package/dist/{data-layer-43pme0Gy.d.ts → data-layer-nvm_ivGQ.d.ts} +6 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +5 -5
- package/dist/{index-a7ElyL3B.d.ts → index-Bb9Px6SR.d.ts} +97 -18
- package/dist/{index-CYcklQ1l.d.ts → index-CaR3hb0i.d.ts} +5 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +150 -78
- package/dist/{make-ByA-84h8.js → make-D6_caNkv.js} +21 -3
- package/dist/{makers-P9kg68C8.js → makers-DjkBP74P.js} +3 -3
- package/dist/{migrator-B25prc9n.js → migrator-vvvKa0Vk.js} +180 -36
- package/dist/{model-Dc3HunKb.js → model-6pJBqqGd.js} +31 -1
- package/dist/{policies-KsqmlX67.js → policies-C_oiSgmY.js} +1 -1
- package/dist/{resource-BQK7bkPQ.js → resource-DCg6LvMH.js} +8 -6
- package/dist/{tables-DgSwge7s.js → tables-DjW96Ubl.js} +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +4 -4
- package/dist/{types-BgOkJy5d.d.ts → types-D5SIVeej.d.ts} +59 -2
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +1 -1
- package/dist/{validation-Cppmd0RV.js → validation-C3MCRBQO.js} +26 -3
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ export interface R2Binding {
|
|
|
9
9
|
interface R2PutOptions {
|
|
10
10
|
readonly httpMetadata?: {
|
|
11
11
|
readonly contentType?: string;
|
|
12
|
+
readonly cacheControl?: string;
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
interface R2ObjectBodyLike {
|
|
@@ -18,6 +19,8 @@ interface R2ObjectBodyLike {
|
|
|
18
19
|
readonly contentType?: string;
|
|
19
20
|
};
|
|
20
21
|
}
|
|
22
|
+
/** What every object is served with: its key never holds anything else. */
|
|
23
|
+
export declare const CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
21
24
|
export interface R2Options {
|
|
22
25
|
/** Where the bucket serves its files (`https://media.example.com`), to make it a public storage. */
|
|
23
26
|
readonly publicUrl?: string;
|
|
@@ -4,14 +4,25 @@ import { a as publicUrlOf, n as bytesOf, r as checkedStream } from "../../../sto
|
|
|
4
4
|
* `r2`: file storage on Cloudflare R2, by binding (no S3 keys), streamed through the Worker. With a
|
|
5
5
|
* `publicUrl` (the bucket's custom domain, or its r2.dev URL) it's a public storage, and the bucket
|
|
6
6
|
* serves its files; without one it's private, and the app serves them through signed links.
|
|
7
|
+
*
|
|
8
|
+
* Every object is stored with `Cache-Control: public, max-age=31536000, immutable`: isai never
|
|
9
|
+
* writes a key twice (a file's key is its unguessable id), so browsers and Cloudflare's cache can
|
|
10
|
+
* keep a file for good, and a page of photos doesn't read the bucket again for each viewer (R2
|
|
11
|
+
* bills each read). The bucket's custom domain still needs a cache rule to cache files whose keys
|
|
12
|
+
* have no extension.
|
|
7
13
|
*/
|
|
14
|
+
/** What every object is served with: its key never holds anything else. */
|
|
15
|
+
const CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
8
16
|
function r2(binding, options = {}) {
|
|
9
17
|
const publicUrl = publicUrlOf(options.publicUrl);
|
|
10
18
|
return {
|
|
11
19
|
kind: "storage",
|
|
12
20
|
...publicUrl ? { publicUrl } : {},
|
|
13
21
|
put: async (key, body, { size, contentType }) => {
|
|
14
|
-
const httpMetadata = {
|
|
22
|
+
const httpMetadata = {
|
|
23
|
+
contentType,
|
|
24
|
+
cacheControl: CACHE_CONTROL
|
|
25
|
+
};
|
|
15
26
|
if (!(body instanceof ReadableStream)) {
|
|
16
27
|
await binding.put(key, await bytesOf(body, size), { httpMetadata });
|
|
17
28
|
return;
|
|
@@ -32,4 +43,4 @@ function r2(binding, options = {}) {
|
|
|
32
43
|
};
|
|
33
44
|
}
|
|
34
45
|
//#endregion
|
|
35
|
-
export { r2 };
|
|
46
|
+
export { CACHE_CONTROL, r2 };
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { r as AuthTablesOptions } from "../data-layer-
|
|
2
|
-
import {
|
|
1
|
+
import { r as AuthTablesOptions } from "../data-layer-nvm_ivGQ.js";
|
|
2
|
+
import { B as Rules, F as AnyPolicy, I as Bound, L as DefinePolicy, P as Permissions, R as Perms, V as StandardAction, 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 Policy } from "../index-Bb9Px6SR.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 };
|
package/dist/auth/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as Policy } from "../policies-
|
|
2
|
-
import { a as createAuth, t as Auth } from "../create-auth-
|
|
1
|
+
import { t as Policy } from "../policies-C_oiSgmY.js";
|
|
2
|
+
import { a as createAuth, t as Auth } from "../create-auth-BCvMwlVD.js";
|
|
3
3
|
export { Auth, Policy, createAuth };
|
package/dist/cli/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { a as lookupsOf, i as filterableOf, n as isRequired, t as Resource, u as Route } from "../resource-
|
|
5
|
-
import { o as fieldOf, p as sensitivity, t as Model } from "../model-
|
|
2
|
+
import { U as toKind, V as object } from "../validation-C3MCRBQO.js";
|
|
3
|
+
import { I as dialectFor, h as snapshotOf, m as emptySnapshot, r as sync, t as migrate, y as MigrationError } from "../migrator-vvvKa0Vk.js";
|
|
4
|
+
import { a as lookupsOf, i as filterableOf, n as isRequired, t as Resource, u as Route } from "../resource-DCg6LvMH.js";
|
|
5
|
+
import { o as fieldOf, p as sensitivity, r as columnName, t as Model } from "../model-6pJBqqGd.js";
|
|
6
6
|
import { t as kebabCase } from "../case-DpXlJCL9.js";
|
|
7
|
-
import { i as makeMigrations, n as checkSteps, t as checkMigrations } from "../make-
|
|
7
|
+
import { i as makeMigrations, n as checkSteps, t as checkMigrations } from "../make-D6_caNkv.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";
|
|
@@ -87,8 +87,22 @@ function generateClient(source) {
|
|
|
87
87
|
for (const { name, route } of [...source.routes].sort(byName)) {
|
|
88
88
|
const output = outputs.routes[name] ?? "unknown";
|
|
89
89
|
const args = routeArgs(route);
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
const returns = `Promise<${output === "undefined" ? "void" : output}>`;
|
|
91
|
+
const files = route.input === "own" ? [] : Object.entries(route.input).filter(([, shape]) => toKind(shape).spec.file);
|
|
92
|
+
const uploads = files.map(([key]) => key);
|
|
93
|
+
const images = files.filter(([, shape]) => toKind(shape).spec.file?.image).map(([key]) => key);
|
|
94
|
+
if (uploads.length === 0) members.push(` ${name}(${args}): ${returns}`);
|
|
95
|
+
else {
|
|
96
|
+
used.add("FileJSON");
|
|
97
|
+
const fields = uploads.map((key) => JSON.stringify(key)).join(" | ");
|
|
98
|
+
members.push(` ${name}: {\n (${args}): ${returns}\n upload(field: ${fields}, file: Blob): Promise<FileJSON>\n }`);
|
|
99
|
+
}
|
|
100
|
+
const endpoint = [
|
|
101
|
+
`route: [${JSON.stringify(route.method)}, ${JSON.stringify(route.path)}]`,
|
|
102
|
+
...uploads.length > 0 ? [`uploads: ${JSON.stringify(uploads)}`] : [],
|
|
103
|
+
...images.length > 0 ? [`images: ${JSON.stringify(images)}`] : []
|
|
104
|
+
];
|
|
105
|
+
table.push(` ${name}: { ${endpoint.join(", ")} },`);
|
|
92
106
|
}
|
|
93
107
|
const body = [
|
|
94
108
|
...types,
|
|
@@ -550,7 +564,8 @@ const CLIENT_FILE = ".isai/client.ts";
|
|
|
550
564
|
function appModules(root) {
|
|
551
565
|
return globSync("apps/**/*.ts", { cwd: root }).filter((file) => !/\.(test|d)\.ts$/.test(file)).sort().map((file) => join(root, file));
|
|
552
566
|
}
|
|
553
|
-
|
|
567
|
+
/** The app modules' resources and routes, loaded as the app loads them, and which file has which. */
|
|
568
|
+
async function appExports(project) {
|
|
554
569
|
const resources = [];
|
|
555
570
|
const routes = [];
|
|
556
571
|
const modules = [];
|
|
@@ -581,6 +596,14 @@ async function generate$1(project) {
|
|
|
581
596
|
}
|
|
582
597
|
if (found.routes.length + found.resources.length > 0) modules.push(found);
|
|
583
598
|
}
|
|
599
|
+
return {
|
|
600
|
+
resources,
|
|
601
|
+
routes,
|
|
602
|
+
modules
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
async function generate$1(project) {
|
|
606
|
+
const { resources, routes, modules } = await appExports(project);
|
|
584
607
|
const user = (await loadModels(project)).find((found) => found.model.spec.name === "User")?.model;
|
|
585
608
|
return {
|
|
586
609
|
text: generateClient({
|
|
@@ -663,6 +686,42 @@ function watchClient(root, regenerate, settleMs = 200) {
|
|
|
663
686
|
watcher.close();
|
|
664
687
|
};
|
|
665
688
|
}
|
|
689
|
+
//#endregion
|
|
690
|
+
//#region src/cli/indexes.ts
|
|
691
|
+
/**
|
|
692
|
+
* `isai check`'s warning about lists without an index: a resource that sorts by a field no index
|
|
693
|
+
* leads with makes the database read the whole table, and sort it, for every page of the list
|
|
694
|
+
* (and for every refresh of a screen that keeps it open). A warning, never a failure: a small
|
|
695
|
+
* table never needs one, and only the app knows which tables grow. The fix is on the model:
|
|
696
|
+
* `.index(...)` (`db/schema/model.ts`).
|
|
697
|
+
*/
|
|
698
|
+
/** One line per resource field sorted without an index, naming the `.index()` to add. */
|
|
699
|
+
function unindexedSorts(resources, models) {
|
|
700
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
701
|
+
const snapshotFor = (database) => {
|
|
702
|
+
let snapshot = snapshots.get(database);
|
|
703
|
+
if (!snapshot) {
|
|
704
|
+
snapshot = snapshotOf(models.filter((m) => m.database === database).map((m) => m.model));
|
|
705
|
+
snapshots.set(database, snapshot);
|
|
706
|
+
}
|
|
707
|
+
return snapshot;
|
|
708
|
+
};
|
|
709
|
+
const warnings = [];
|
|
710
|
+
for (const { name, resource } of resources) {
|
|
711
|
+
const spec = resource.model.spec;
|
|
712
|
+
const found = models.find((m) => m.model.spec.name === spec.name);
|
|
713
|
+
if (!found) continue;
|
|
714
|
+
const indexes = snapshotFor(found.database).tables[spec.table]?.indexes ?? [];
|
|
715
|
+
for (const field of resource.sortable) {
|
|
716
|
+
const fieldSpec = spec.fields[field];
|
|
717
|
+
if (!fieldSpec || fieldSpec.computed) continue;
|
|
718
|
+
const column = columnName(field, fieldSpec);
|
|
719
|
+
if (column === "id" || indexes.some((index) => index.columns[0] === column)) continue;
|
|
720
|
+
warnings.push(`${name} sorts by ${spec.name}.${field}, which no index leads with: each page reads the whole table. Add .index("${field}") to ${spec.name}${found.file ? ` (db/schema/${found.file}.ts)` : ""}`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return warnings;
|
|
724
|
+
}
|
|
666
725
|
const PLATFORM = "platform";
|
|
667
726
|
/** One history per database: `migrations/<database>/` (`migrations/tenant/`, `migrations/main/`). */
|
|
668
727
|
const migrationsDir = (root, database) => join(root, "migrations", database);
|
|
@@ -1109,11 +1168,13 @@ const commands = {
|
|
|
1109
1168
|
await writeClient(project, log);
|
|
1110
1169
|
return 0;
|
|
1111
1170
|
},
|
|
1112
|
-
/** Fails when something generated is out of date with the app (CI). */
|
|
1171
|
+
/** Fails when something generated is out of date with the app (CI); warns about lists without an index. */
|
|
1113
1172
|
async check(project) {
|
|
1114
1173
|
await checkClient(project);
|
|
1115
1174
|
checkDeployable(project.root);
|
|
1116
|
-
|
|
1175
|
+
const warnings = await indexWarnings(project);
|
|
1176
|
+
for (const warning of warnings) log(`⚠ ${warning}`);
|
|
1177
|
+
log(warnings.length === 0 ? "No problems" : `No problems; ${warnings.length} warning${warnings.length === 1 ? "" : "s"}`);
|
|
1117
1178
|
return 0;
|
|
1118
1179
|
},
|
|
1119
1180
|
/**
|
|
@@ -1140,6 +1201,7 @@ const commands = {
|
|
|
1140
1201
|
await writeClient(project, log).catch((error) => {
|
|
1141
1202
|
log(`\n⚠ The typed client wasn't written: ${error instanceof Error ? error.message : error}\n`);
|
|
1142
1203
|
});
|
|
1204
|
+
for (const warning of await indexWarnings(project).catch(() => [])) log(`⚠ ${warning}`);
|
|
1143
1205
|
const token = crypto.randomUUID();
|
|
1144
1206
|
const served = wranglerDev(project.root, [
|
|
1145
1207
|
...wrangler,
|
|
@@ -1160,6 +1222,11 @@ const commands = {
|
|
|
1160
1222
|
return served;
|
|
1161
1223
|
}
|
|
1162
1224
|
};
|
|
1225
|
+
/** Resources sorted by a field no index leads with (`./indexes`). */
|
|
1226
|
+
async function indexWarnings(project) {
|
|
1227
|
+
const { resources } = await appExports(project);
|
|
1228
|
+
return unindexedSorts(resources, await loadModels(project));
|
|
1229
|
+
}
|
|
1163
1230
|
/**
|
|
1164
1231
|
* `isai client` in a new process: this one has the app's modules cached as they were on start, and
|
|
1165
1232
|
* ESM can't unload them. A failure is printed by the child; the server keeps running.
|
|
@@ -1196,7 +1263,8 @@ const usage = `usage: isai <command>
|
|
|
1196
1263
|
makemigrations [--check] write migration steps for model changes (--check: fail if one is missing)
|
|
1197
1264
|
migrate [--remote] apply pending migrations to the local databases (--remote: the deployed ones)
|
|
1198
1265
|
client write .isai/client.ts, the typed client for the app's frontends
|
|
1199
|
-
check fail if .isai/client.ts is stale, or a D1 database has no database_id
|
|
1266
|
+
check fail if .isai/client.ts is stale, or a D1 database has no database_id;
|
|
1267
|
+
warn when a resource sorts by a field no index leads with
|
|
1200
1268
|
dev [--seed <name>] [wrangler args]
|
|
1201
1269
|
migrate, then run wrangler dev, rewriting the typed client on each change
|
|
1202
1270
|
(--seed: fill the local databases from a seed)`;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -165,6 +165,10 @@ interface ResourceEndpoint {
|
|
|
165
165
|
/** A route in the endpoint table. */
|
|
166
166
|
interface RouteEndpoint {
|
|
167
167
|
readonly route: readonly [Method, string];
|
|
168
|
+
/** Its file inputs: `upload(field, file)` sends a file to name by id in its input. */
|
|
169
|
+
readonly uploads?: readonly string[];
|
|
170
|
+
/** Those of them that are images: resized in the browser before they're sent. */
|
|
171
|
+
readonly images?: readonly string[];
|
|
168
172
|
}
|
|
169
173
|
type Endpoints = Readonly<Record<string, ResourceEndpoint | RouteEndpoint>>;
|
|
170
174
|
type FetchLike = (input: Request | string | URL, init?: RequestInit) => Promise<Response>;
|
package/dist/client/index.js
CHANGED
|
@@ -159,6 +159,7 @@ async function encoded(canvas, name, clear) {
|
|
|
159
159
|
* · <custom action>(key, input?) · upload(field, file) (an image is resized first)
|
|
160
160
|
* (a singleton's calls take no key)
|
|
161
161
|
* routes api.<name>(args) (path params and input in one object)
|
|
162
|
+
* · api.<name>.upload(field, file) (a route with file inputs)
|
|
162
163
|
* auth api.auth.me() · signIn() · signOut() · users · invites
|
|
163
164
|
*
|
|
164
165
|
* Writes survive a dropping connection. Every write (any call but a GET) carries an
|
|
@@ -237,7 +238,7 @@ function createClient(endpoints, options = {}) {
|
|
|
237
238
|
};
|
|
238
239
|
for (const [name, endpoint] of Object.entries(endpoints)) {
|
|
239
240
|
if (Object.hasOwn(api, name)) throw new TypeError(`isai: the client's ${name} is taken`);
|
|
240
|
-
api[name] = "route" in endpoint ? routeCall(call, endpoint) : resourceCalls(call, endpoint);
|
|
241
|
+
api[name] = "route" in endpoint ? routeCall(call, endpoint, name) : resourceCalls(call, endpoint);
|
|
241
242
|
}
|
|
242
243
|
return api;
|
|
243
244
|
}
|
|
@@ -350,18 +351,20 @@ function resourceCalls(call, endpoint) {
|
|
|
350
351
|
default: calls[action] = actionCall(call, keyed, action);
|
|
351
352
|
}
|
|
352
353
|
for (const action of endpoint.custom ?? []) calls[action] = actionCall(call, keyed, action);
|
|
353
|
-
if (endpoint.uploads?.length)
|
|
354
|
-
const fields = new Set(endpoint.uploads);
|
|
355
|
-
const images = new Set(endpoint.images);
|
|
356
|
-
calls.upload = (field, file) => {
|
|
357
|
-
if (!fields.has(field)) throw new TypeError(`isai: ${base} takes no upload for ${field}`);
|
|
358
|
-
const path = `/uploads${base}/${kebabCase(field)}`;
|
|
359
|
-
if (!images.has(field)) return call("POST", path, { body: file });
|
|
360
|
-
return uploadImage(call, path, file);
|
|
361
|
-
};
|
|
362
|
-
}
|
|
354
|
+
if (endpoint.uploads?.length) calls.upload = uploadCall(call, base, endpoint);
|
|
363
355
|
return calls;
|
|
364
356
|
}
|
|
357
|
+
/** `upload(field, file)` to `/uploads<base>/<field>`: an image field's file is resized first. */
|
|
358
|
+
function uploadCall(call, base, endpoint) {
|
|
359
|
+
const fields = new Set(endpoint.uploads);
|
|
360
|
+
const images = new Set(endpoint.images);
|
|
361
|
+
return (field, file) => {
|
|
362
|
+
if (!fields.has(field)) throw new TypeError(`isai: ${base} takes no upload for ${field}`);
|
|
363
|
+
const path = `/uploads${base}/${kebabCase(field)}`;
|
|
364
|
+
if (!images.has(field)) return call("POST", path, { body: file });
|
|
365
|
+
return uploadImage(call, path, file);
|
|
366
|
+
};
|
|
367
|
+
}
|
|
365
368
|
/** An image, resized: the full one, then its smaller copy with `X-Size-Of`; the answer is the image. */
|
|
366
369
|
async function uploadImage(call, path, file) {
|
|
367
370
|
const sizes = await resized(file, file.name ?? "image");
|
|
@@ -377,9 +380,9 @@ function actionCall(call, keyed, action) {
|
|
|
377
380
|
return keyed((path, input) => call("POST", `${path}/${kebabCase(action)}`, input === void 0 ? {} : { body: input }));
|
|
378
381
|
}
|
|
379
382
|
/** A route: `:params` filled from the argument, the rest its input (the body, or the query string on GET / DELETE). */
|
|
380
|
-
function routeCall(call, endpoint) {
|
|
383
|
+
function routeCall(call, endpoint, name) {
|
|
381
384
|
const [method, template] = endpoint.route;
|
|
382
|
-
|
|
385
|
+
const run = (args = {}) => {
|
|
383
386
|
const input = { ...args };
|
|
384
387
|
const path = template.replace(/:([A-Za-z0-9_]+)/g, (_, name) => {
|
|
385
388
|
const value = input[name];
|
|
@@ -392,6 +395,8 @@ function routeCall(call, endpoint) {
|
|
|
392
395
|
for (const [key, value] of Object.entries(input)) if (value !== void 0) query.set(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
393
396
|
return call(method, path, { query });
|
|
394
397
|
};
|
|
398
|
+
if (endpoint.uploads?.length) return Object.assign(run, { upload: uploadCall(call, `/${kebabCase(name)}`, endpoint) });
|
|
399
|
+
return run;
|
|
395
400
|
}
|
|
396
401
|
/** A value in a list's query string: text as it is, a list comma-separated. */
|
|
397
402
|
function queryValue(value) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { I as fromBase64url } from "./model-
|
|
2
|
-
import { n as PROVIDERS } from "./tables-
|
|
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-
|
|
1
|
+
import { I as fromBase64url } from "./model-6pJBqqGd.js";
|
|
2
|
+
import { n as PROVIDERS } from "./tables-DjW96Ubl.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-C_oiSgmY.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
|
-
import {
|
|
2
|
-
import "./migrator-
|
|
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-
|
|
4
|
-
import { a as TableSet, i as platformAuthDefinitions, o as defineBuiltins, r as authTables } from "./tables-
|
|
1
|
+
import { L as quantity, g as host, j as datetime, r as isPhoneRegion } from "./validation-C3MCRBQO.js";
|
|
2
|
+
import "./migrator-vvvKa0Vk.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-6pJBqqGd.js";
|
|
4
|
+
import { a as TableSet, i as platformAuthDefinitions, o as defineBuiltins, r as authTables } from "./tables-DjW96Ubl.js";
|
|
5
5
|
import { n as seed } from "./seed-BObmdKdP.js";
|
|
6
6
|
//#region src/db/fields/slot.ts
|
|
7
7
|
var Slot = class {
|
|
@@ -77,6 +77,8 @@ function uploadTables(f, options) {
|
|
|
77
77
|
fileId: f.line().unique(),
|
|
78
78
|
/** The file as stored, written into the field that attaches it. */
|
|
79
79
|
file: f.file(),
|
|
80
|
+
/** Bytes stored for it, its smaller copies' too: what a cap counts as pending. */
|
|
81
|
+
size: f.quantity().default(0),
|
|
80
82
|
/** Where it may be attached: `<resource or route>/<field>` (`manage-products/images`). */
|
|
81
83
|
target: f.line(),
|
|
82
84
|
/** Who uploaded it (a user's id); empty for an anonymous upload. */
|
|
@@ -173,8 +175,8 @@ var ModelRegistry = class {
|
|
|
173
175
|
/**
|
|
174
176
|
* Traits: behaviour a model takes whole, written before its fields (`model(name, ...traits,
|
|
175
177
|
* fields)`), Laravel's traits typed. A trait adds fields (so the model's own fields already see
|
|
176
|
-
* them; declaring one of them again is an error), computed fields,
|
|
177
|
-
* (`singleton`). Always written `traits.x()`, never imported bare: a bare `publishable()` reads like
|
|
178
|
+
* them; declaring one of them again is an error), computed fields, the indexes its behaviour reads
|
|
179
|
+
* by, and a name the framework reads (`singleton`). Always written `traits.x()`, never imported bare: a bare `publishable()` reads like
|
|
178
180
|
* an action.
|
|
179
181
|
*/
|
|
180
182
|
var Trait = class {
|
|
@@ -188,10 +190,11 @@ var Trait = class {
|
|
|
188
190
|
*
|
|
189
191
|
* - `ordered({ within })`: a `position` the database keeps: a new row goes last in its group (the
|
|
190
192
|
* rows sharing `within`, or the whole table), reads are in position order unless they name
|
|
191
|
-
* their own, and `db.reorder(Model, ids)` sets a group's order.
|
|
193
|
+
* their own, and `db.reorder(Model, ids)` sets a group's order. Indexed by its group, then
|
|
194
|
+
* `position` (which also serves the group's ref).
|
|
192
195
|
* - `singleton()`: one row, with the id `"singleton"`; read it with `findMany(Model).first()`.
|
|
193
196
|
* - `publishable()`: `publishedAt` (empty: a draft; in the future: scheduled) and `published`,
|
|
194
|
-
* true once it has passed.
|
|
197
|
+
* true once it has passed. Indexed by `publishedAt`, for lists of the newest.
|
|
195
198
|
*/
|
|
196
199
|
function builtinTraits() {
|
|
197
200
|
return {
|
|
@@ -201,7 +204,8 @@ function builtinTraits() {
|
|
|
201
204
|
by: "position",
|
|
202
205
|
within: options.within
|
|
203
206
|
} }) },
|
|
204
|
-
computed: {}
|
|
207
|
+
computed: {},
|
|
208
|
+
indexes: [options.within === void 0 ? ["position"] : [options.within, "position"]]
|
|
205
209
|
}),
|
|
206
210
|
singleton: () => new Trait({
|
|
207
211
|
name: "singleton",
|
|
@@ -212,7 +216,8 @@ function builtinTraits() {
|
|
|
212
216
|
return new Trait({
|
|
213
217
|
name: "publishable",
|
|
214
218
|
fields: { publishedAt: field(datetime(), "datetime").optional() },
|
|
215
|
-
computed: { published: (row) => all(row.publishedAt.isNull().not(), row.publishedAt.lte(now)) }
|
|
219
|
+
computed: { published: (row) => all(row.publishedAt.isNull().not(), row.publishedAt.lte(now)) },
|
|
220
|
+
indexes: [["publishedAt"]]
|
|
216
221
|
});
|
|
217
222
|
}
|
|
218
223
|
};
|
|
@@ -1,13 +1,15 @@
|
|
|
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
|
-
import { M as IdInput, Y as StoredFile } from "./index-
|
|
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-
|
|
3
|
+
import { M as IdInput, Y as StoredFile } from "./index-CaR3hb0i.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-D5SIVeej.js";
|
|
5
5
|
//#region src/db/schema/traits.d.ts
|
|
6
6
|
type ComputedDefinitions = Record<string, (row: any) => Condition>;
|
|
7
7
|
interface TraitSpec {
|
|
8
8
|
readonly name: string;
|
|
9
9
|
readonly fields: Fields;
|
|
10
10
|
readonly computed: ComputedDefinitions;
|
|
11
|
+
/** Indexes the model gets, by field names: what the trait's own reads sort or group by. */
|
|
12
|
+
readonly indexes?: readonly (readonly string[])[];
|
|
11
13
|
}
|
|
12
14
|
declare class Trait<F extends Fields = Fields, C extends ComputedDefinitions = ComputedDefinitions> {
|
|
13
15
|
readonly spec: TraitSpec;
|
|
@@ -741,6 +743,8 @@ declare function uploadTables(f: Kinds, options: UploadTablesOptions): TableSet<
|
|
|
741
743
|
}>;
|
|
742
744
|
/** The file as stored, written into the field that attaches it. */
|
|
743
745
|
file: Field<StoredFile, StoredFile, false, false, {}>;
|
|
746
|
+
/** Bytes stored for it, its smaller copies' too: what a cap counts as pending. */
|
|
747
|
+
size: Field<number, number, false, true, {}>;
|
|
744
748
|
/** Where it may be attached: `<resource or route>/<field>` (`manage-products/images`). */
|
|
745
749
|
target: Field<string, string, false, false, {}>;
|
|
746
750
|
/** Who uploaded it (a user's id); empty for an anonymous upload. */
|
package/dist/db/index.d.ts
CHANGED
|
@@ -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-
|
|
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-
|
|
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-D5SIVeej.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-nvm_ivGQ.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
|
-
import {
|
|
2
|
-
import { C as database,
|
|
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-
|
|
4
|
-
import { i as slot, n as row, r as Slot, t as createDataLayer } from "../data-layer-
|
|
1
|
+
import { q as ValidationError } from "../validation-C3MCRBQO.js";
|
|
2
|
+
import { C as database, I as dialectFor, J as DatabaseError, K as newSecretKey, L as sqlite, M as Query, U as fixedClock, W as systemClock, X as ReferenceFailed, Y as NotFound, Z as UniqueFailed, _ 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 CheckFailed, r as sync, s as releaseLock, t as migrate, u as startRelease, v as dataStep, x as Transaction, y as MigrationError } from "../migrator-vvvKa0Vk.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-6pJBqqGd.js";
|
|
4
|
+
import { i as slot, n as row, r as Slot, t as createDataLayer } from "../data-layer-BOVkYESH.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-
|
|
6
|
+
import { a as cleanupMigration, i as makeMigrations, r as makeCleanup, t as checkMigrations } from "../make-D6_caNkv.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) {
|