ultimabase 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +7 -0
- package/README.md +4 -2
- package/dist/access.d.ts +62 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +258 -0
- package/dist/adapter-errors.d.ts +43 -0
- package/dist/adapter-errors.d.ts.map +1 -0
- package/dist/adapter-errors.js +72 -0
- package/dist/adapter-json.d.ts +20 -0
- package/dist/adapter-json.d.ts.map +1 -0
- package/dist/adapter-json.js +2 -0
- package/dist/adapter-migrations.d.ts +90 -0
- package/dist/adapter-migrations.d.ts.map +1 -0
- package/dist/adapter-migrations.js +1 -0
- package/dist/adapter.d.ts +333 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +5 -0
- package/dist/aggregate.d.ts +31 -0
- package/dist/aggregate.d.ts.map +1 -0
- package/dist/aggregate.js +99 -0
- package/dist/app-internals.d.ts +10 -0
- package/dist/app-internals.d.ts.map +1 -0
- package/dist/app-internals.js +12 -0
- package/dist/app-options.d.ts +22 -0
- package/dist/app-options.d.ts.map +1 -0
- package/dist/app-options.js +3 -0
- package/dist/cascade.d.ts +47 -0
- package/dist/cascade.d.ts.map +1 -0
- package/dist/cascade.js +209 -0
- package/dist/cli-config.d.ts +6 -0
- package/dist/cli-config.d.ts.map +1 -0
- package/dist/cli-config.js +45 -0
- package/dist/cli-deploy.d.ts +14 -0
- package/dist/cli-deploy.d.ts.map +1 -0
- package/dist/cli-deploy.js +193 -0
- package/dist/cli-main.d.ts +16 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +151 -0
- package/dist/cli-migrate.d.ts +20 -0
- package/dist/cli-migrate.d.ts.map +1 -0
- package/dist/cli-migrate.js +105 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +4 -0
- package/dist/complete.d.ts +13 -0
- package/dist/complete.d.ts.map +1 -0
- package/dist/complete.js +60 -0
- package/dist/console-logger.d.ts +3 -0
- package/dist/console-logger.d.ts.map +1 -0
- package/dist/console-logger.js +62 -0
- package/dist/create-many.d.ts +7 -0
- package/dist/create-many.d.ts.map +1 -0
- package/dist/create-many.js +88 -0
- package/dist/decimal.d.ts +23 -0
- package/dist/decimal.d.ts.map +1 -0
- package/dist/decimal.js +50 -0
- package/dist/define-app.d.ts +59 -0
- package/dist/define-app.d.ts.map +1 -0
- package/dist/define-app.js +5 -0
- package/dist/define-enum.d.ts +6 -0
- package/dist/define-enum.d.ts.map +1 -0
- package/dist/define-enum.js +18 -0
- package/dist/define-function.d.ts +25 -0
- package/dist/define-function.d.ts.map +1 -0
- package/dist/define-function.js +111 -0
- package/dist/define-model.d.ts +48 -0
- package/dist/define-model.d.ts.map +1 -0
- package/dist/define-model.js +85 -0
- package/dist/definition.d.ts +179 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +165 -0
- package/dist/delete-many.d.ts +13 -0
- package/dist/delete-many.d.ts.map +1 -0
- package/dist/delete-many.js +58 -0
- package/dist/engine.d.ts +16 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +174 -0
- package/dist/errors.d.ts +50 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +43 -0
- package/dist/exact.d.ts +20 -0
- package/dist/exact.d.ts.map +1 -0
- package/dist/exact.js +1 -0
- package/dist/feed.d.ts +6 -0
- package/dist/feed.d.ts.map +1 -0
- package/dist/feed.js +33 -0
- package/dist/field-codec.d.ts +11 -0
- package/dist/field-codec.d.ts.map +1 -0
- package/dist/field-codec.js +48 -0
- package/dist/field-rules.d.ts +57 -0
- package/dist/field-rules.d.ts.map +1 -0
- package/dist/field-rules.js +1 -0
- package/dist/field-type.d.ts +51 -0
- package/dist/field-type.d.ts.map +1 -0
- package/dist/field-type.js +106 -0
- package/dist/fields.d.ts +295 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +1 -0
- package/dist/function.d.ts +126 -0
- package/dist/function.d.ts.map +1 -0
- package/dist/function.js +1 -0
- package/dist/functions.d.ts +18 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +99 -0
- package/dist/group-by.d.ts +10 -0
- package/dist/group-by.d.ts.map +1 -0
- package/dist/group-by.js +145 -0
- package/dist/group-having.d.ts +8 -0
- package/dist/group-having.d.ts.map +1 -0
- package/dist/group-having.js +97 -0
- package/dist/hooks.d.ts +41 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +149 -0
- package/dist/hosts.d.ts +3 -0
- package/dist/hosts.d.ts.map +1 -0
- package/dist/hosts.js +8 -0
- package/dist/identify.d.ts +15 -0
- package/dist/identify.d.ts.map +1 -0
- package/dist/identify.js +71 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/internal.d.ts +28 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +39 -0
- package/dist/lifecycle.d.ts +21 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +149 -0
- package/dist/list-update.d.ts +12 -0
- package/dist/list-update.d.ts.map +1 -0
- package/dist/list-update.js +52 -0
- package/dist/live.d.ts +37 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +139 -0
- package/dist/manifest.d.ts +11 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +60 -0
- package/dist/memory-aggregate.d.ts +7 -0
- package/dist/memory-aggregate.d.ts.map +1 -0
- package/dist/memory-aggregate.js +107 -0
- package/dist/memory-conditions.d.ts +8 -0
- package/dist/memory-conditions.d.ts.map +1 -0
- package/dist/memory-conditions.js +82 -0
- package/dist/memory-draft.d.ts +34 -0
- package/dist/memory-draft.d.ts.map +1 -0
- package/dist/memory-draft.js +255 -0
- package/dist/memory-json.d.ts +6 -0
- package/dist/memory-json.d.ts.map +1 -0
- package/dist/memory-json.js +86 -0
- package/dist/memory-read.d.ts +45 -0
- package/dist/memory-read.d.ts.map +1 -0
- package/dist/memory-read.js +244 -0
- package/dist/memory-search.d.ts +16 -0
- package/dist/memory-search.d.ts.map +1 -0
- package/dist/memory-search.js +110 -0
- package/dist/memory-values.d.ts +23 -0
- package/dist/memory-values.d.ts.map +1 -0
- package/dist/memory-values.js +82 -0
- package/dist/memory.d.ts +4 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +157 -0
- package/dist/migrate.d.ts +22 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +131 -0
- package/dist/model.d.ts +239 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +1 -0
- package/dist/nested-write.d.ts +64 -0
- package/dist/nested-write.d.ts.map +1 -0
- package/dist/nested-write.js +274 -0
- package/dist/number-update.d.ts +18 -0
- package/dist/number-update.d.ts.map +1 -0
- package/dist/number-update.js +96 -0
- package/dist/plugin-app.d.ts +48 -0
- package/dist/plugin-app.d.ts.map +1 -0
- package/dist/plugin-app.js +230 -0
- package/dist/plugin-code.d.ts +17 -0
- package/dist/plugin-code.d.ts.map +1 -0
- package/dist/plugin-code.js +125 -0
- package/dist/plugin-use.d.ts +22 -0
- package/dist/plugin-use.d.ts.map +1 -0
- package/dist/plugin-use.js +161 -0
- package/dist/plugin.d.ts +357 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +17 -0
- package/dist/query-aggregate.d.ts +82 -0
- package/dist/query-aggregate.d.ts.map +1 -0
- package/dist/query-aggregate.js +1 -0
- package/dist/query-args.d.ts +41 -0
- package/dist/query-args.d.ts.map +1 -0
- package/dist/query-args.js +263 -0
- package/dist/query-bulk.d.ts +35 -0
- package/dist/query-bulk.d.ts.map +1 -0
- package/dist/query-bulk.js +1 -0
- package/dist/query-conditions.d.ts +9 -0
- package/dist/query-conditions.d.ts.map +1 -0
- package/dist/query-conditions.js +46 -0
- package/dist/query-context.d.ts +32 -0
- package/dist/query-context.d.ts.map +1 -0
- package/dist/query-context.js +55 -0
- package/dist/query-filter.d.ts +46 -0
- package/dist/query-filter.d.ts.map +1 -0
- package/dist/query-filter.js +1 -0
- package/dist/query-group.d.ts +82 -0
- package/dist/query-group.d.ts.map +1 -0
- package/dist/query-group.js +1 -0
- package/dist/query-json.d.ts +9 -0
- package/dist/query-json.d.ts.map +1 -0
- package/dist/query-json.js +66 -0
- package/dist/query-order.d.ts +15 -0
- package/dist/query-order.d.ts.map +1 -0
- package/dist/query-order.js +115 -0
- package/dist/query-project.d.ts +15 -0
- package/dist/query-project.d.ts.map +1 -0
- package/dist/query-project.js +45 -0
- package/dist/query-search.d.ts +24 -0
- package/dist/query-search.d.ts.map +1 -0
- package/dist/query-search.js +2 -0
- package/dist/query-values.d.ts +28 -0
- package/dist/query-values.d.ts.map +1 -0
- package/dist/query-values.js +91 -0
- package/dist/query-where.d.ts +46 -0
- package/dist/query-where.d.ts.map +1 -0
- package/dist/query-where.js +200 -0
- package/dist/query-write.d.ts +14 -0
- package/dist/query-write.d.ts.map +1 -0
- package/dist/query-write.js +260 -0
- package/dist/query.d.ts +254 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +1 -0
- package/dist/record.d.ts +257 -0
- package/dist/record.d.ts.map +1 -0
- package/dist/record.js +1 -0
- package/dist/recorder.d.ts +32 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +152 -0
- package/dist/relation-write.d.ts +41 -0
- package/dist/relation-write.d.ts.map +1 -0
- package/dist/relation-write.js +112 -0
- package/dist/relations.d.ts +42 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/relations.js +119 -0
- package/dist/rules.d.ts +97 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +71 -0
- package/dist/running.d.ts +29 -0
- package/dist/running.d.ts.map +1 -0
- package/dist/running.js +39 -0
- package/dist/schema-indexes.d.ts +11 -0
- package/dist/schema-indexes.d.ts.map +1 -0
- package/dist/schema-indexes.js +59 -0
- package/dist/schema-virtuals.d.ts +19 -0
- package/dist/schema-virtuals.d.ts.map +1 -0
- package/dist/schema-virtuals.js +100 -0
- package/dist/schema.d.ts +35 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +267 -0
- package/dist/scope.d.ts +46 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +82 -0
- package/dist/scratch.d.ts +14 -0
- package/dist/scratch.d.ts.map +1 -0
- package/dist/scratch.js +10 -0
- package/dist/serve.d.ts +2 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +4 -0
- package/dist/symbols.d.ts +10 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/symbols.js +1 -0
- package/dist/transaction.d.ts +28 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +156 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/update-many.d.ts +7 -0
- package/dist/update-many.d.ts.map +1 -0
- package/dist/update-many.js +182 -0
- package/dist/uuid.d.ts +7 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +48 -0
- package/dist/validation.d.ts +24 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +72 -0
- package/dist/virtual-read.d.ts +9 -0
- package/dist/virtual-read.d.ts.map +1 -0
- package/dist/virtual-read.js +145 -0
- package/dist/virtual.d.ts +142 -0
- package/dist/virtual.d.ts.map +1 -0
- package/dist/virtual.js +1 -0
- package/dist/write-context.d.ts +46 -0
- package/dist/write-context.d.ts.map +1 -0
- package/dist/write-context.js +102 -0
- package/dist/write-data.d.ts +26 -0
- package/dist/write-data.d.ts.map +1 -0
- package/dist/write-data.js +144 -0
- package/dist/write-plan.d.ts +107 -0
- package/dist/write-plan.d.ts.map +1 -0
- package/dist/write-plan.js +170 -0
- package/dist/write-target.d.ts +17 -0
- package/dist/write-target.d.ts.map +1 -0
- package/dist/write-target.js +59 -0
- package/dist/writes.d.ts +23 -0
- package/dist/writes.d.ts.map +1 -0
- package/dist/writes.js +246 -0
- package/dist/writing.d.ts +10 -0
- package/dist/writing.d.ts.map +1 -0
- package/dist/writing.js +66 -0
- package/package.json +42 -3
- package/src/access.ts +329 -0
- package/src/adapter-errors.ts +89 -0
- package/src/adapter-json.ts +21 -0
- package/src/adapter-migrations.ts +98 -0
- package/src/adapter.ts +363 -0
- package/src/aggregate.ts +136 -0
- package/src/app-internals.ts +26 -0
- package/src/app-options.ts +24 -0
- package/src/cascade.ts +297 -0
- package/src/cli-config.ts +45 -0
- package/src/cli-deploy.ts +235 -0
- package/src/cli-main.ts +165 -0
- package/src/cli-migrate.ts +143 -0
- package/src/cli.ts +5 -0
- package/src/complete.ts +73 -0
- package/src/console-logger.ts +87 -0
- package/src/create-many.ts +126 -0
- package/src/decimal.ts +74 -0
- package/src/define-app.ts +108 -0
- package/src/define-enum.ts +26 -0
- package/src/define-function.ts +188 -0
- package/src/define-model.ts +220 -0
- package/src/definition.ts +352 -0
- package/src/delete-many.ts +76 -0
- package/src/engine.ts +281 -0
- package/src/errors.ts +97 -0
- package/src/exact.ts +49 -0
- package/src/feed.ts +39 -0
- package/src/field-codec.ts +72 -0
- package/src/field-rules.ts +77 -0
- package/src/field-type.ts +219 -0
- package/src/fields.ts +383 -0
- package/src/function.ts +143 -0
- package/src/functions.ts +154 -0
- package/src/group-by.ts +186 -0
- package/src/group-having.ts +143 -0
- package/src/hooks.ts +215 -0
- package/src/hosts.ts +11 -0
- package/src/identify.ts +95 -0
- package/src/index.ts +125 -0
- package/src/internal.ts +68 -0
- package/src/lifecycle.ts +214 -0
- package/src/list-update.ts +71 -0
- package/src/live.ts +199 -0
- package/src/manifest.ts +71 -0
- package/src/memory-aggregate.ts +162 -0
- package/src/memory-conditions.ts +88 -0
- package/src/memory-draft.ts +319 -0
- package/src/memory-json.ts +91 -0
- package/src/memory-read.ts +311 -0
- package/src/memory-search.ts +149 -0
- package/src/memory-values.ts +106 -0
- package/src/memory.ts +173 -0
- package/src/migrate.ts +152 -0
- package/src/model.ts +312 -0
- package/src/nested-write.ts +390 -0
- package/src/number-update.ts +142 -0
- package/src/plugin-app.ts +308 -0
- package/src/plugin-code.ts +158 -0
- package/src/plugin-use.ts +217 -0
- package/src/plugin.ts +400 -0
- package/src/query-aggregate.ts +96 -0
- package/src/query-args.ts +375 -0
- package/src/query-bulk.ts +53 -0
- package/src/query-conditions.ts +53 -0
- package/src/query-context.ts +104 -0
- package/src/query-filter.ts +54 -0
- package/src/query-group.ts +137 -0
- package/src/query-json.ts +90 -0
- package/src/query-order.ts +158 -0
- package/src/query-project.ts +73 -0
- package/src/query-search.ts +31 -0
- package/src/query-values.ts +131 -0
- package/src/query-where.ts +318 -0
- package/src/query-write.ts +360 -0
- package/src/query.ts +400 -0
- package/src/record.ts +379 -0
- package/src/recorder.ts +212 -0
- package/src/relation-write.ts +176 -0
- package/src/relations.ts +165 -0
- package/src/rules.ts +188 -0
- package/src/running.ts +62 -0
- package/src/schema-indexes.ts +88 -0
- package/src/schema-virtuals.ts +134 -0
- package/src/schema.ts +367 -0
- package/src/scope.ts +123 -0
- package/src/scratch.ts +28 -0
- package/src/serve.ts +5 -0
- package/src/symbols.ts +11 -0
- package/src/transaction.ts +211 -0
- package/src/types.ts +79 -0
- package/src/update-many.ts +268 -0
- package/src/uuid.ts +51 -0
- package/src/validation.ts +107 -0
- package/src/virtual-read.ts +198 -0
- package/src/virtual.ts +187 -0
- package/src/write-context.ts +170 -0
- package/src/write-data.ts +188 -0
- package/src/write-plan.ts +273 -0
- package/src/write-target.ts +92 -0
- package/src/writes.ts +361 -0
- package/src/writing.ts +93 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// `migrate create` and `sync` (D-105), and the migration files both
|
|
2
|
+
// `migrate` commands read.
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { kernelLogger, schemaOf } from './lifecycle.js';
|
|
8
|
+
import { Refused } from './errors.js';
|
|
9
|
+
import { hashOf, migrationsOf, synced } from './migrate.js';
|
|
10
|
+
export const header = '-- ultimabase schema ';
|
|
11
|
+
/** For a statement a transaction refuses: `create index concurrently`. */
|
|
12
|
+
export const noTransaction = '-- ultimabase no transaction';
|
|
13
|
+
export const create = '`npx ultimabase migrate create <name>`';
|
|
14
|
+
/** The `.sql` files of `dir` in name order, and its `schema.json`. */
|
|
15
|
+
export async function read(dir) {
|
|
16
|
+
if (!existsSync(dir))
|
|
17
|
+
return { files: [], snapshot: undefined };
|
|
18
|
+
const names = (await readdir(dir))
|
|
19
|
+
.filter((name) => name.endsWith('.sql'))
|
|
20
|
+
.toSorted();
|
|
21
|
+
const files = await Promise.all(names.map(async (name) => {
|
|
22
|
+
// A Windows checkout's line endings change neither checksum nor header.
|
|
23
|
+
const text = await readFile(join(dir, name), 'utf8');
|
|
24
|
+
const sql = text.replaceAll('\r\n', '\n');
|
|
25
|
+
const checksum = createHash('sha256').update(sql).digest('hex');
|
|
26
|
+
const [first = '', second] = sql.split('\n', 2);
|
|
27
|
+
const schema = first.startsWith(header)
|
|
28
|
+
? first.slice(header.length)
|
|
29
|
+
: undefined;
|
|
30
|
+
return {
|
|
31
|
+
name,
|
|
32
|
+
sql,
|
|
33
|
+
checksum,
|
|
34
|
+
...(schema === undefined ? {} : { schema }),
|
|
35
|
+
transaction: second !== noTransaction,
|
|
36
|
+
};
|
|
37
|
+
}));
|
|
38
|
+
const json = join(dir, 'schema.json');
|
|
39
|
+
if (!existsSync(json)) {
|
|
40
|
+
if (files.length > 0) {
|
|
41
|
+
throw new Refused('migrations/ holds SQL files but no schema.json: restore it from version control.');
|
|
42
|
+
}
|
|
43
|
+
return { files, snapshot: undefined };
|
|
44
|
+
}
|
|
45
|
+
const snapshot = JSON.parse(await readFile(json, 'utf8'));
|
|
46
|
+
return { files, snapshot };
|
|
47
|
+
}
|
|
48
|
+
/** `yyyyMMddHHmmss`, in UTC. */
|
|
49
|
+
const stamp = (now) => now.toISOString().replaceAll(/[-:T]/g, '').slice(0, 'yyyyMMddHHmmss'.length);
|
|
50
|
+
function body(hash, statements, none) {
|
|
51
|
+
const blocks = statements.map(({ sql, loses, tries, rename }) => [
|
|
52
|
+
...(loses === undefined ? [] : [`-- ${loses}.`]),
|
|
53
|
+
...(tries === undefined ? [] : [`-- ${tries}.`]),
|
|
54
|
+
...(rename === undefined ? [] : [`-- a rename? write: ${rename.sql};`]),
|
|
55
|
+
`${sql};`,
|
|
56
|
+
].join('\n'));
|
|
57
|
+
const parts = blocks.length > 0 ? blocks : [none];
|
|
58
|
+
return `${header}${hash}\n\n${parts.join('\n\n')}\n`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Writes the migration from `schema.json` to the models; with no model
|
|
62
|
+
* changed, a file for the developer's SQL, as a data migration.
|
|
63
|
+
*/
|
|
64
|
+
export async function migrateCreate({ options }, dir, name, now) {
|
|
65
|
+
if (!/^[a-z0-9_]+$/.test(name)) {
|
|
66
|
+
throw new Refused(`"${name}" is not a migration name: use lowercase letters, digits and _.`);
|
|
67
|
+
}
|
|
68
|
+
const migrations = migrationsOf(options.database);
|
|
69
|
+
const schema = schemaOf(options);
|
|
70
|
+
const to = migrations.snapshot(schema);
|
|
71
|
+
const { snapshot: from } = await read(dir);
|
|
72
|
+
const hash = hashOf(to);
|
|
73
|
+
const file = `${stamp(now)}_${name}.sql`;
|
|
74
|
+
await mkdir(dir, { recursive: true });
|
|
75
|
+
if (from !== undefined && hashOf(from) === hash) {
|
|
76
|
+
const sql = body(hash, [], '-- No model changed: write your SQL here.');
|
|
77
|
+
await writeFile(join(dir, file), sql, { flag: 'wx' });
|
|
78
|
+
return `Wrote migrations/${file}: no model changed, write your SQL in it.`;
|
|
79
|
+
}
|
|
80
|
+
const statements = migrations.plan(from, schema);
|
|
81
|
+
const sql = body(hash, statements, '-- No statement: only names changed.');
|
|
82
|
+
await writeFile(join(dir, file), sql, { flag: 'wx' });
|
|
83
|
+
await writeFile(join(dir, 'schema.json'), `${JSON.stringify(to, null, '\t')}\n`);
|
|
84
|
+
return `Wrote migrations/${file}.`;
|
|
85
|
+
}
|
|
86
|
+
/** Syncs a development database, losing data only with `force`. */
|
|
87
|
+
export async function sync({ options }, force) {
|
|
88
|
+
if (process.env['NODE_ENV'] === 'production') {
|
|
89
|
+
throw new Refused('sync changes only a development database: run `npx ultimabase migrate deploy` in production.');
|
|
90
|
+
}
|
|
91
|
+
const { database } = options;
|
|
92
|
+
migrationsOf(database);
|
|
93
|
+
const schema = schemaOf(options);
|
|
94
|
+
const logger = kernelLogger(options);
|
|
95
|
+
await database.start(schema, logger);
|
|
96
|
+
try {
|
|
97
|
+
const ran = await synced(database, schema, logger, force);
|
|
98
|
+
return ran === undefined
|
|
99
|
+
? 'The database already matches the models.'
|
|
100
|
+
: undefined;
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
await database.stop();
|
|
104
|
+
}
|
|
105
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Caller, Identity } from './rules.ts';
|
|
2
|
+
/** A plugin's `caller`, bound to its context. */
|
|
3
|
+
export type PluginCaller = {
|
|
4
|
+
readonly plugin: string;
|
|
5
|
+
readonly caller: (identity: Identity) => Promise<unknown>;
|
|
6
|
+
};
|
|
7
|
+
export type Complete = (identity: Identity) => Promise<Caller>;
|
|
8
|
+
/**
|
|
9
|
+
* Completes a user caller the first time the kernel receives it; `plugins`
|
|
10
|
+
* is read on each call, filled once their contexts exist.
|
|
11
|
+
*/
|
|
12
|
+
export declare function completer(plugins: readonly PluginCaller[]): Complete;
|
|
13
|
+
//# sourceMappingURL=complete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../src/complete.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAElD,iDAAiD;AACjD,MAAM,MAAM,YAAY,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACzD,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAmC9D;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,QAAQ,CAqBpE"}
|
package/dist/complete.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// A user caller completed with its plugins' keys (D-121): once per object,
|
|
2
|
+
// each plugin's `caller` in the order of `plugins`, a key given by one only,
|
|
3
|
+
// a symbol key as a string one (D-126).
|
|
4
|
+
import { isObject } from './query-where.js';
|
|
5
|
+
// The keys every caller holds, which no plugin gives.
|
|
6
|
+
const own = new Set(['kind', 'user']);
|
|
7
|
+
async function completed(identity, plugins) {
|
|
8
|
+
const keys = {};
|
|
9
|
+
const givers = new Map();
|
|
10
|
+
for (const { plugin, caller } of plugins) {
|
|
11
|
+
const given = await caller(identity);
|
|
12
|
+
if (!isObject(given)) {
|
|
13
|
+
throw new TypeError(`${plugin}.caller returns an object of keys.`);
|
|
14
|
+
}
|
|
15
|
+
for (const key of Reflect.ownKeys(given)) {
|
|
16
|
+
if (!Object.prototype.propertyIsEnumerable.call(given, key))
|
|
17
|
+
continue;
|
|
18
|
+
const other = own.has(key) ? 'the kernel' : givers.get(key);
|
|
19
|
+
if (other !== undefined) {
|
|
20
|
+
throw new Error(`${plugin}.caller gives the caller ${String(key)}, which ${other} gives; a key has one plugin.`);
|
|
21
|
+
}
|
|
22
|
+
givers.set(key, plugin);
|
|
23
|
+
const value = Reflect.get(given, key);
|
|
24
|
+
keys[key] = value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Unnecessary only while no plugin declares a key: once one does, the
|
|
28
|
+
// kernel's types cannot see it given (D-121). caller-keys-typing proves it.
|
|
29
|
+
// oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- see above.
|
|
30
|
+
return Object.freeze({ ...identity, ...keys });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Completes a user caller the first time the kernel receives it; `plugins`
|
|
34
|
+
* is read on each call, filled once their contexts exist.
|
|
35
|
+
*/
|
|
36
|
+
export function completer(plugins) {
|
|
37
|
+
const pending = new WeakMap();
|
|
38
|
+
const done = new WeakSet();
|
|
39
|
+
// A completed caller, or a user one when no plugin gives a key.
|
|
40
|
+
const kept = (identity) => done.has(identity);
|
|
41
|
+
return async (identity) => {
|
|
42
|
+
if (identity.kind !== 'user')
|
|
43
|
+
return identity;
|
|
44
|
+
if (plugins.length === 0)
|
|
45
|
+
done.add(identity);
|
|
46
|
+
if (kept(identity))
|
|
47
|
+
return identity;
|
|
48
|
+
const known = pending.get(identity);
|
|
49
|
+
if (known !== undefined)
|
|
50
|
+
return known;
|
|
51
|
+
const caller = completed(identity, plugins).then((result) => {
|
|
52
|
+
done.add(result);
|
|
53
|
+
return result;
|
|
54
|
+
});
|
|
55
|
+
pending.set(identity, caller);
|
|
56
|
+
// A failure is not kept: the next request tries again.
|
|
57
|
+
caller.catch(() => pending.delete(identity));
|
|
58
|
+
return caller;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-logger.d.ts","sourceRoot":"","sources":["../src/console-logger.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AA2D9C,wBAAgB,aAAa,CAAC,QAAQ,GAAE,MAAW,GAAG,MAAM,CAuB3D"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// The logger the kernel writes to when the application gives none (D-58,
|
|
2
|
+
// D-88, D-93): a readable line in a terminal, one JSON line with pino's
|
|
3
|
+
// fields everywhere else.
|
|
4
|
+
import { styleText } from 'node:util';
|
|
5
|
+
const colors = { info: 'green', warn: 'yellow', error: 'red' };
|
|
6
|
+
/** An error as pino's `err` serializer writes it. */
|
|
7
|
+
function serialized(value) {
|
|
8
|
+
if (!(value instanceof Error))
|
|
9
|
+
return value;
|
|
10
|
+
return { type: value.name, message: value.message, stack: value.stack };
|
|
11
|
+
}
|
|
12
|
+
function json(level, fields, msg) {
|
|
13
|
+
return JSON.stringify({
|
|
14
|
+
level,
|
|
15
|
+
time: Date.now(),
|
|
16
|
+
...Object.fromEntries(Object.entries(fields).map(([key, value]) => [key, serialized(value)])),
|
|
17
|
+
...(msg === undefined ? {} : { msg }),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/** A value as `key=value` shows it: quoted when it would read ambiguously. */
|
|
21
|
+
function shown(value) {
|
|
22
|
+
if (typeof value === 'string' && /^[^\s"=]+$/u.test(value))
|
|
23
|
+
return value;
|
|
24
|
+
return JSON.stringify(value) ?? String(value);
|
|
25
|
+
}
|
|
26
|
+
/** `10:42:07 INFO [cache] Warm. size=2`, then each error's stack, indented. */
|
|
27
|
+
function readable(level, fields, msg, stream) {
|
|
28
|
+
const { name, ...rest } = fields;
|
|
29
|
+
const errors = Object.values(rest).filter((value) => value instanceof Error);
|
|
30
|
+
const details = Object.entries(rest)
|
|
31
|
+
.filter(([, value]) => !(value instanceof Error))
|
|
32
|
+
.map(([key, value]) => `${key}=${shown(value)}`);
|
|
33
|
+
const line = [
|
|
34
|
+
styleText('dim', new Date().toTimeString().slice(0, 8), { stream }),
|
|
35
|
+
styleText(colors[level], level.toUpperCase().padEnd(5), { stream }),
|
|
36
|
+
name === undefined ? undefined : `[${shown(name)}]`,
|
|
37
|
+
msg,
|
|
38
|
+
...details,
|
|
39
|
+
];
|
|
40
|
+
const stacks = errors.map((error) => (error.stack ?? `${error.name}: ${error.message}`).replaceAll(/^/gmu, ' '));
|
|
41
|
+
return [line.filter((part) => part !== undefined).join(' '), ...stacks].join('\n');
|
|
42
|
+
}
|
|
43
|
+
export function consoleLogger(bindings = {}) {
|
|
44
|
+
const log = (level) => (first, message) => {
|
|
45
|
+
const fields = {
|
|
46
|
+
...bindings,
|
|
47
|
+
...(typeof first === 'string' ? {} : first),
|
|
48
|
+
};
|
|
49
|
+
const msg = typeof first === 'string' ? first : message;
|
|
50
|
+
// console.info writes to stdout, console.warn and console.error to stderr.
|
|
51
|
+
const stream = level === 'info' ? process.stdout : process.stderr;
|
|
52
|
+
console[level](stream.isTTY
|
|
53
|
+
? readable(level, fields, msg, stream)
|
|
54
|
+
: json(level, fields, msg));
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
info: log('info'),
|
|
58
|
+
warn: log('warn'),
|
|
59
|
+
error: log('error'),
|
|
60
|
+
child: (more) => consoleLogger({ ...bindings, ...more }),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Context } from './query-context.ts';
|
|
2
|
+
import type { Commit, Store } from './transaction.ts';
|
|
3
|
+
export declare function createMany(context: Context, store: Store, commits: Commit[], args: unknown): Promise<{
|
|
4
|
+
readonly count: number;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function createManyAndReturn(context: Context, store: Store, commits: Commit[], args: unknown): Promise<readonly unknown[]>;
|
|
7
|
+
//# sourceMappingURL=create-many.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-many.d.ts","sourceRoot":"","sources":["../src/create-many.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAIjD,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AA2FrD,wBAAsB,UAAU,CAC/B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAIrC;AAED,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAK7B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { InvalidQuery } from './errors.js';
|
|
2
|
+
import { rereads, runHook, settle, settles } from './hooks.js';
|
|
3
|
+
import { oneKeys } from './query-args.js';
|
|
4
|
+
import { given, shapedRows } from './query-context.js';
|
|
5
|
+
import { byIds, entries, isObject } from './query-where.js';
|
|
6
|
+
import { createPlan } from './query-write.js';
|
|
7
|
+
import { prepared, readData } from './write-data.js';
|
|
8
|
+
import { guarded } from './write-target.js';
|
|
9
|
+
import { writingOf } from './writing.js';
|
|
10
|
+
import { returned } from './writes.js';
|
|
11
|
+
/** A row's data, as `create` reads it, with no write but `connect` on a `one`. */
|
|
12
|
+
function rowData(context, data, at) {
|
|
13
|
+
const { models, model } = context;
|
|
14
|
+
for (const [name, value] of entries(data, at)) {
|
|
15
|
+
const field = model.fields.get(name);
|
|
16
|
+
if (field === undefined || field.kind === 'scalar')
|
|
17
|
+
continue;
|
|
18
|
+
const path = `${at}.${name}`;
|
|
19
|
+
if (field.kind === 'many') {
|
|
20
|
+
throw new InvalidQuery(path, `${path} is a many relation, which createMany does not write.`);
|
|
21
|
+
}
|
|
22
|
+
if (!isObject(value) ||
|
|
23
|
+
Object.keys(value).some((key) => key !== 'connect')) {
|
|
24
|
+
throw new InvalidQuery(path, `${path} takes only connect in createMany.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return readData(models, model, data, true, at);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Creates the rows of `data`: their ids, or with `at` the rows as it shapes
|
|
31
|
+
* them, in `data`'s order. The rows come back even when a read rule would
|
|
32
|
+
* hide them, as `create`'s does (D-86).
|
|
33
|
+
*/
|
|
34
|
+
async function created(context, store, commits, data, at) {
|
|
35
|
+
const { model, caller, scope } = context;
|
|
36
|
+
const drafts = Array.isArray(data)
|
|
37
|
+
? data.map((row, index) => rowData(context, row, `data.${index}`))
|
|
38
|
+
: [rowData(context, data, 'data')];
|
|
39
|
+
const sent = drafts.map((draft) => ({ ...draft }));
|
|
40
|
+
// One row refused, none written (D-111).
|
|
41
|
+
for (const each of sent)
|
|
42
|
+
scope.access?.allow(model, 'create', { data: each });
|
|
43
|
+
if (drafts.length === 0)
|
|
44
|
+
return [];
|
|
45
|
+
for (const draft of drafts) {
|
|
46
|
+
await prepared(model, draft, caller, true);
|
|
47
|
+
await runHook(model, 'beforeCreate', { caller, data: draft });
|
|
48
|
+
}
|
|
49
|
+
const events = [];
|
|
50
|
+
const writing = writingOf(scope, store, events);
|
|
51
|
+
const changes = [];
|
|
52
|
+
const ids = [];
|
|
53
|
+
for (const [index, draft] of drafts.entries()) {
|
|
54
|
+
const { id, plan } = await createPlan(scope, model, draft, sent[index] ?? {}, store);
|
|
55
|
+
ids.push(id);
|
|
56
|
+
events.push({ model, operation: 'create', id }, ...plan.events);
|
|
57
|
+
await plan.check(writing);
|
|
58
|
+
changes.push(...(await plan.changes(writing)));
|
|
59
|
+
}
|
|
60
|
+
const read = at && { model: model.name, ...at.read, where: byIds(ids) };
|
|
61
|
+
// An `after*` hook may change the rows: they are read once it ran (D-88).
|
|
62
|
+
const again = rereads(events);
|
|
63
|
+
const write = {
|
|
64
|
+
checks: writing.checks.map(({ check }) => check),
|
|
65
|
+
changes,
|
|
66
|
+
...(read && !again && { read }),
|
|
67
|
+
};
|
|
68
|
+
// Nothing follows a write without hooks: it may need no transaction.
|
|
69
|
+
const run = settles(events) ? store.write(write) : store.last(write);
|
|
70
|
+
const rows = await guarded(run, writing.checks);
|
|
71
|
+
await settle(store, caller, events, commits);
|
|
72
|
+
if (at === undefined || read === undefined)
|
|
73
|
+
return ids;
|
|
74
|
+
const found = new Map((again ? await store.find(read) : rows).map((row) => [row.id, row]));
|
|
75
|
+
const ordered = ids.flatMap((id) => found.get(id) ?? []);
|
|
76
|
+
return shapedRows(context, store, ordered, at.shape, scope.access);
|
|
77
|
+
}
|
|
78
|
+
export async function createMany(context, store, commits, args) {
|
|
79
|
+
const found = given(args, context.model.name, ['data'], ['data']);
|
|
80
|
+
const ids = await created(context, store, commits, found.get('data'));
|
|
81
|
+
return { count: ids.length };
|
|
82
|
+
}
|
|
83
|
+
export async function createManyAndReturn(context, store, commits, args) {
|
|
84
|
+
const keys = ['data', ...oneKeys];
|
|
85
|
+
const found = given(args, context.model.name, keys, ['data']);
|
|
86
|
+
const at = await returned(context, found);
|
|
87
|
+
return created(context, store, commits, found.get('data'), at);
|
|
88
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** A decimal string's shape, as transports describe it. */
|
|
2
|
+
export declare const decimalPattern = "^-?\\d+(\\.\\d+)?$";
|
|
3
|
+
/** A decimal field's precision and scale. */
|
|
4
|
+
export type Digits = {
|
|
5
|
+
readonly precision: number;
|
|
6
|
+
readonly scale: number;
|
|
7
|
+
};
|
|
8
|
+
/** A field, which has digits if it is a decimal. */
|
|
9
|
+
type Digited = {
|
|
10
|
+
readonly type: string;
|
|
11
|
+
} & Partial<Digits>;
|
|
12
|
+
/** The message of a value a decimal `field` cannot hold, if it cannot. */
|
|
13
|
+
export declare function decimalIssue(value: unknown, field: Digited): string | undefined;
|
|
14
|
+
/** `value`, which fits, at `scale`: `'012.5'` is `'12.50'`. */
|
|
15
|
+
export declare const normalized: (value: string, scale: number) => string;
|
|
16
|
+
/** A value that fits `field`, as stored: a decimal at its scale. */
|
|
17
|
+
export declare const storedValue: (value: unknown, field: Digited) => unknown;
|
|
18
|
+
/** A decimal string's units at `scale`, which order decimals by value. */
|
|
19
|
+
export declare const unitsOf: (value: string, scale: number) => bigint;
|
|
20
|
+
/** The exact sum of decimal strings, at `scale`. */
|
|
21
|
+
export declare function sumDecimals(values: readonly string[], scale: number): string;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=decimal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decimal.d.ts","sourceRoot":"","sources":["../src/decimal.ts"],"names":[],"mappings":"AAGA,2DAA2D;AAC3D,eAAO,MAAM,cAAc,uBAAuB,CAAA;AA4BlD,6CAA6C;AAC7C,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE3E,oDAAoD;AACpD,KAAK,OAAO,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAE1D,0EAA0E;AAC1E,wBAAgB,YAAY,CAC3B,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,OAAO,GACZ,MAAM,GAAG,SAAS,CAWpB;AAED,+DAA+D;AAC/D,eAAO,MAAM,UAAU,UAAW,MAAM,SAAS,MAAM,KAAG,MACjB,CAAA;AAEzC,oEAAoE;AACpE,eAAO,MAAM,WAAW,UAAW,OAAO,SAAS,OAAO,KAAG,OAGpD,CAAA;AAET,0EAA0E;AAC1E,eAAO,MAAM,OAAO,UAAW,MAAM,SAAS,MAAM,KAAG,MAC3B,CAAA;AAE5B,oDAAoD;AACpD,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5E"}
|
package/dist/decimal.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Decimal values (D-145): exact numbers as strings, normalized to their
|
|
2
|
+
// field's scale, compared and summed through `BigInt`.
|
|
3
|
+
/** A decimal string's shape, as transports describe it. */
|
|
4
|
+
export const decimalPattern = '^-?\\d+(\\.\\d+)?$';
|
|
5
|
+
const shape = new RegExp(decimalPattern);
|
|
6
|
+
function exact(value) {
|
|
7
|
+
const [whole = '', fraction = ''] = value.split('.');
|
|
8
|
+
return { units: BigInt(`${whole}${fraction}`), scale: fraction.length };
|
|
9
|
+
}
|
|
10
|
+
/** `value`'s units at `to` digits after the point; a cut digit is a 0. */
|
|
11
|
+
const scaled = ({ units, scale }, to) => to >= scale
|
|
12
|
+
? units * 10n ** BigInt(to - scale)
|
|
13
|
+
: units / 10n ** BigInt(scale - to);
|
|
14
|
+
function text(units, scale) {
|
|
15
|
+
const digits = (units < 0n ? -units : units)
|
|
16
|
+
.toString()
|
|
17
|
+
.padStart(scale + 1, '0');
|
|
18
|
+
const whole = digits.slice(0, digits.length - scale);
|
|
19
|
+
const sign = units < 0n ? '-' : '';
|
|
20
|
+
return scale === 0
|
|
21
|
+
? `${sign}${whole}`
|
|
22
|
+
: `${sign}${whole}.${digits.slice(-scale)}`;
|
|
23
|
+
}
|
|
24
|
+
/** The message of a value a decimal `field` cannot hold, if it cannot. */
|
|
25
|
+
export function decimalIssue(value, field) {
|
|
26
|
+
const digits = { precision: field.precision ?? 0, scale: field.scale ?? 0 };
|
|
27
|
+
const expected = `Expected a decimal string of at most ${digits.precision} digits, ${digits.scale} after the point, such as '12.50'.`;
|
|
28
|
+
if (typeof value !== 'string' || !shape.test(value))
|
|
29
|
+
return expected;
|
|
30
|
+
const [whole = '', fraction = ''] = value.replace('-', '').split('.');
|
|
31
|
+
const significant = whole.replace(/^0+/, '').length;
|
|
32
|
+
const decimals = fraction.replace(/0+$/, '').length;
|
|
33
|
+
return significant > digits.precision - digits.scale ||
|
|
34
|
+
decimals > digits.scale
|
|
35
|
+
? expected
|
|
36
|
+
: undefined;
|
|
37
|
+
}
|
|
38
|
+
/** `value`, which fits, at `scale`: `'012.5'` is `'12.50'`. */
|
|
39
|
+
export const normalized = (value, scale) => text(scaled(exact(value), scale), scale);
|
|
40
|
+
/** A value that fits `field`, as stored: a decimal at its scale. */
|
|
41
|
+
export const storedValue = (value, field) => field.type === 'decimal' && typeof value === 'string'
|
|
42
|
+
? normalized(value, field.scale ?? 0)
|
|
43
|
+
: value;
|
|
44
|
+
/** A decimal string's units at `scale`, which order decimals by value. */
|
|
45
|
+
export const unitsOf = (value, scale) => scaled(exact(value), scale);
|
|
46
|
+
/** The exact sum of decimal strings, at `scale`. */
|
|
47
|
+
export function sumDecimals(values, scale) {
|
|
48
|
+
const total = values.reduce((sum, value) => sum + unitsOf(value, scale), 0n);
|
|
49
|
+
return text(total, scale);
|
|
50
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Adapter } from './adapter.ts';
|
|
2
|
+
import type { Logger, MasterKey } from './app-options.ts';
|
|
3
|
+
import type { FunctionShape } from './function.ts';
|
|
4
|
+
import type { App, FieldTypePlugins, InverseCheck, ModelShape } from './model.ts';
|
|
5
|
+
import type { PluginValue } from './plugin.ts';
|
|
6
|
+
import type { fieldType, modelFields } from './symbols.ts';
|
|
7
|
+
type Used<M> = (M extends {
|
|
8
|
+
readonly plugins?: infer P;
|
|
9
|
+
} ? P : never) | (M extends {
|
|
10
|
+
readonly [modelFields]: infer F;
|
|
11
|
+
} ? FieldTypePlugins<F> : never);
|
|
12
|
+
type Missing<P extends readonly PluginValue<string, string>[], X> = Exclude<Used<X>, P[number]['name'] | undefined>;
|
|
13
|
+
type RequiresOf<V> = V extends PluginValue<string, infer R> ? R : never;
|
|
14
|
+
type Unmet<P extends readonly PluginValue<string, string>[]> = Exclude<RequiresOf<P[number]>, P[number]['name']>;
|
|
15
|
+
type PluginCheck<P extends readonly PluginValue<string, string>[], M extends readonly ModelShape[], Fn extends readonly FunctionShape[]> = [Missing<P, M[number]>] extends [never] ? [Missing<P, Fn[number]>] extends [never] ? [Unmet<P>] extends [never] ? unknown : {
|
|
16
|
+
readonly pluginError: `a plugin requires '${Unmet<P> & string}', which is not in plugins`;
|
|
17
|
+
} : {
|
|
18
|
+
readonly pluginError: `a function uses '${Missing<P, Fn[number]> & string}', which is not in plugins`;
|
|
19
|
+
} : {
|
|
20
|
+
readonly pluginError: `a model uses '${Missing<P, M[number]> & string}', which is not in plugins`;
|
|
21
|
+
};
|
|
22
|
+
type Targets<X> = X extends {
|
|
23
|
+
readonly [modelFields]: infer F;
|
|
24
|
+
} ? {
|
|
25
|
+
[K in keyof F]: F[K] extends {
|
|
26
|
+
readonly [fieldType]: {
|
|
27
|
+
readonly target: () => infer T;
|
|
28
|
+
};
|
|
29
|
+
} ? T extends {
|
|
30
|
+
readonly name: infer N;
|
|
31
|
+
} ? N : never : never;
|
|
32
|
+
}[keyof F] : never;
|
|
33
|
+
type Unlisted<M extends readonly ModelShape[]> = Exclude<Targets<M[number]>, M[number]['name']>;
|
|
34
|
+
type Names<M> = {
|
|
35
|
+
[I in keyof M]: M[I] extends {
|
|
36
|
+
readonly name: infer N;
|
|
37
|
+
} ? N : never;
|
|
38
|
+
};
|
|
39
|
+
type Twice<N, Seen = never, Found = never> = N extends readonly [
|
|
40
|
+
infer H,
|
|
41
|
+
...infer R
|
|
42
|
+
] ? string extends H ? Twice<R, Seen, Found> : Twice<R, Seen | H, H extends Seen ? Found | H : Found> : Found;
|
|
43
|
+
type ModelCheck<M extends readonly ModelShape[]> = [Twice<Names<M>>] extends [
|
|
44
|
+
never
|
|
45
|
+
] ? [Unlisted<M>] extends [never] ? unknown : {
|
|
46
|
+
readonly modelError: `a relation points to '${Unlisted<M> & string}', which is not in models`;
|
|
47
|
+
} : {
|
|
48
|
+
readonly modelError: `two models are named '${Twice<Names<M>> & string}'`;
|
|
49
|
+
};
|
|
50
|
+
export declare function defineApp<const P extends readonly PluginValue<string, string>[] = [], const M extends readonly ModelShape[] = [], const Fn extends readonly FunctionShape[] = []>(options: {
|
|
51
|
+
readonly database: Adapter;
|
|
52
|
+
readonly plugins?: P;
|
|
53
|
+
readonly models: M;
|
|
54
|
+
readonly functions?: Fn;
|
|
55
|
+
readonly masterKey?: MasterKey;
|
|
56
|
+
readonly logger?: Logger;
|
|
57
|
+
} & NoInfer<PluginCheck<P, M, Fn>> & NoInfer<ModelCheck<M>> & NoInfer<InverseCheck<M>>): App<M>;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=define-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-app.d.ts","sourceRoot":"","sources":["../src/define-app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Enum } from './fields.ts';
|
|
2
|
+
/** A shared enum, frozen, which `f.enumOf` takes in place of its values. */
|
|
3
|
+
export declare function defineEnum<const E extends string>(name: string, values: readonly E[], options?: {
|
|
4
|
+
readonly description?: string;
|
|
5
|
+
}): Enum<E>;
|
|
6
|
+
//# sourceMappingURL=define-enum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-enum.d.ts","sourceRoot":"","sources":["../src/define-enum.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAChD,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,CAAC,EAAE,EACpB,OAAO,GAAE;IAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7C,IAAI,CAAC,CAAC,CAAC,CAeT"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Shared enums (D-150): one name and its values, which every field taking
|
|
2
|
+
// it shows as one GraphQL enum and one OpenAPI schema; storage is unchanged.
|
|
3
|
+
import { describedBy, enumValues, sharedEnums } from './definition.js';
|
|
4
|
+
/** A shared enum, frozen, which `f.enumOf` takes in place of its values. */
|
|
5
|
+
export function defineEnum(name, values, options = {}) {
|
|
6
|
+
if (!/^[A-Z_a-z]\w*$/.test(name) || name.startsWith('__')) {
|
|
7
|
+
throw new Error(`defineEnum takes a GraphQL name: letters, digits and _, not ${JSON.stringify(name)}.`);
|
|
8
|
+
}
|
|
9
|
+
const of = `defineEnum(${name})`;
|
|
10
|
+
enumValues(values, of);
|
|
11
|
+
const shared = Object.freeze({
|
|
12
|
+
name,
|
|
13
|
+
values: Object.freeze([...values]),
|
|
14
|
+
description: describedBy(options.description, of),
|
|
15
|
+
});
|
|
16
|
+
sharedEnums.set(shared, shared);
|
|
17
|
+
return shared;
|
|
18
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FunctionDefinition } from './definition.ts';
|
|
2
|
+
import type { StandardSchema } from './fields.ts';
|
|
3
|
+
import type { Client, FunctionPlugin, FunctionWithKeys, InputCheck, ObjectType, OutputSpec, QueryCheck, ResultOf, RunFacts, ServerFunction, TypeFields } from './function.ts';
|
|
4
|
+
import type { ModelUse } from './plugin.ts';
|
|
5
|
+
export declare function defineType<const N extends string, const F extends TypeFields>(definition: {
|
|
6
|
+
readonly name: N;
|
|
7
|
+
/** What the type means, which the transports show (D-150). */
|
|
8
|
+
readonly description?: string;
|
|
9
|
+
readonly fields: F;
|
|
10
|
+
}): ObjectType<N, F>;
|
|
11
|
+
/** `run`'s call, the input first when the function takes one; a transport
|
|
12
|
+
* passes its client (D-113). */
|
|
13
|
+
export declare function called(value: object, record: FunctionDefinition, args: readonly unknown[], client?: Client): Promise<unknown>;
|
|
14
|
+
export declare function defineFunction<const N extends string, const O extends OutputSpec | undefined = undefined, const I extends StandardSchema | undefined = undefined, const U extends readonly ModelUse<string, FunctionPlugin>[] = []>(definition: {
|
|
15
|
+
readonly name: N;
|
|
16
|
+
/** What the function does, which the transports show (D-150). */
|
|
17
|
+
readonly description?: string;
|
|
18
|
+
/** A query, served over GET too; a mutation by default (D-109). */
|
|
19
|
+
readonly query?: boolean;
|
|
20
|
+
readonly use?: U;
|
|
21
|
+
readonly input?: I;
|
|
22
|
+
readonly output?: O;
|
|
23
|
+
readonly run: (facts: RunFacts<NoInfer<I>>) => NoInfer<ResultOf<O>> | Promise<NoInfer<ResultOf<O>>>;
|
|
24
|
+
} & NoInfer<InputCheck<I>> & NoInfer<QueryCheck<U[number]>>): ServerFunction<N, I, O, U[number]['plugin'], FunctionWithKeys<U[number]>>;
|
|
25
|
+
//# sourceMappingURL=define-function.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-function.d.ts","sourceRoot":"","sources":["../src/define-function.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAU,MAAM,iBAAiB,CAAA;AAajE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EACX,MAAM,EACN,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,UAAU,EACV,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAQ3C,wBAAgB,UAAU,CACzB,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,KAAK,CAAC,CAAC,SAAS,UAAU,EACzB,UAAU,EAAE;IACb,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IAChB,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;CAClB,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAmBnB;AAuBD;gCACgC;AAChC,wBAAsB,MAAM,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA8BlB;AAED,wBAAgB,cAAc,CAC7B,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,KAAK,CAAC,CAAC,SAAS,UAAU,GAAG,SAAS,GAAG,SAAS,EAClD,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,SAAS,GAAG,SAAS,EACtD,KAAK,CAAC,CAAC,SAAS,SAAS,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,GAAG,EAAE,EAEhE,UAAU,EAAE;IACX,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IAChB,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAChB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,CACb,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KACvB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;CACzD,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAC7B,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAuC3E"}
|