ultimabase 0.0.0-stage → 0.1.1
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 +13 -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 +15 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +155 -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 +166 -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,108 @@
|
|
|
1
|
+
// `defineApp` (D-24, D-59): the application's models and plugins, checked
|
|
2
|
+
// together on its line, and its `start` and `stop` (D-91).
|
|
3
|
+
import type { Adapter } from './adapter.ts'
|
|
4
|
+
import { remember } from './app-internals.ts'
|
|
5
|
+
import type { Logger, MasterKey } from './app-options.ts'
|
|
6
|
+
import type { FunctionShape } from './function.ts'
|
|
7
|
+
import { lifecycle } from './lifecycle.ts'
|
|
8
|
+
import type {
|
|
9
|
+
App,
|
|
10
|
+
FieldTypePlugins,
|
|
11
|
+
InverseCheck,
|
|
12
|
+
ModelShape,
|
|
13
|
+
} from './model.ts'
|
|
14
|
+
import type { PluginValue } from './plugin.ts'
|
|
15
|
+
import type { fieldType, modelFields } from './symbols.ts'
|
|
16
|
+
|
|
17
|
+
// Checked on the `defineApp` line, where every model is typed (D-24).
|
|
18
|
+
type Used<M> =
|
|
19
|
+
| (M extends { readonly plugins?: infer P } ? P : never)
|
|
20
|
+
| (M extends { readonly [modelFields]: infer F }
|
|
21
|
+
? FieldTypePlugins<F>
|
|
22
|
+
: never)
|
|
23
|
+
type Missing<P extends readonly PluginValue<string, string>[], X> = Exclude<
|
|
24
|
+
Used<X>,
|
|
25
|
+
P[number]['name'] | undefined
|
|
26
|
+
>
|
|
27
|
+
type RequiresOf<V> = V extends PluginValue<string, infer R> ? R : never
|
|
28
|
+
type Unmet<P extends readonly PluginValue<string, string>[]> = Exclude<
|
|
29
|
+
RequiresOf<P[number]>,
|
|
30
|
+
P[number]['name']
|
|
31
|
+
>
|
|
32
|
+
type PluginCheck<
|
|
33
|
+
P extends readonly PluginValue<string, string>[],
|
|
34
|
+
M extends readonly ModelShape[],
|
|
35
|
+
Fn extends readonly FunctionShape[],
|
|
36
|
+
> = [Missing<P, M[number]>] extends [never]
|
|
37
|
+
? [Missing<P, Fn[number]>] extends [never]
|
|
38
|
+
? [Unmet<P>] extends [never]
|
|
39
|
+
? unknown
|
|
40
|
+
: {
|
|
41
|
+
readonly pluginError: `a plugin requires '${Unmet<P> & string}', which is not in plugins`
|
|
42
|
+
}
|
|
43
|
+
: {
|
|
44
|
+
readonly pluginError: `a function uses '${Missing<P, Fn[number]> & string}', which is not in plugins`
|
|
45
|
+
}
|
|
46
|
+
: {
|
|
47
|
+
readonly pluginError: `a model uses '${Missing<P, M[number]> & string}', which is not in plugins`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type Targets<X> = X extends { readonly [modelFields]: infer F }
|
|
51
|
+
? {
|
|
52
|
+
[K in keyof F]: F[K] extends {
|
|
53
|
+
readonly [fieldType]: { readonly target: () => infer T }
|
|
54
|
+
}
|
|
55
|
+
? T extends { readonly name: infer N }
|
|
56
|
+
? N
|
|
57
|
+
: never
|
|
58
|
+
: never
|
|
59
|
+
}[keyof F]
|
|
60
|
+
: never
|
|
61
|
+
type Unlisted<M extends readonly ModelShape[]> = Exclude<
|
|
62
|
+
Targets<M[number]>,
|
|
63
|
+
M[number]['name']
|
|
64
|
+
>
|
|
65
|
+
// Over the names alone, tail-recursive, so a long list stays cheap and within
|
|
66
|
+
// TypeScript's depth. A name typed `string`, a model a function builds, is not
|
|
67
|
+
// compared.
|
|
68
|
+
type Names<M> = {
|
|
69
|
+
[I in keyof M]: M[I] extends { readonly name: infer N } ? N : never
|
|
70
|
+
}
|
|
71
|
+
type Twice<N, Seen = never, Found = never> = N extends readonly [
|
|
72
|
+
infer H,
|
|
73
|
+
...infer R,
|
|
74
|
+
]
|
|
75
|
+
? string extends H
|
|
76
|
+
? Twice<R, Seen, Found>
|
|
77
|
+
: Twice<R, Seen | H, H extends Seen ? Found | H : Found>
|
|
78
|
+
: Found
|
|
79
|
+
type ModelCheck<M extends readonly ModelShape[]> = [Twice<Names<M>>] extends [
|
|
80
|
+
never,
|
|
81
|
+
]
|
|
82
|
+
? [Unlisted<M>] extends [never]
|
|
83
|
+
? unknown
|
|
84
|
+
: {
|
|
85
|
+
readonly modelError: `a relation points to '${Unlisted<M> & string}', which is not in models`
|
|
86
|
+
}
|
|
87
|
+
: {
|
|
88
|
+
readonly modelError: `two models are named '${Twice<Names<M>> & string}'`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function defineApp<
|
|
92
|
+
const P extends readonly PluginValue<string, string>[] = [],
|
|
93
|
+
const M extends readonly ModelShape[] = [],
|
|
94
|
+
const Fn extends readonly FunctionShape[] = [],
|
|
95
|
+
>(
|
|
96
|
+
options: {
|
|
97
|
+
readonly database: Adapter
|
|
98
|
+
readonly plugins?: P
|
|
99
|
+
readonly models: M
|
|
100
|
+
readonly functions?: Fn
|
|
101
|
+
readonly masterKey?: MasterKey
|
|
102
|
+
readonly logger?: Logger
|
|
103
|
+
} & NoInfer<PluginCheck<P, M, Fn>> &
|
|
104
|
+
NoInfer<ModelCheck<M>> &
|
|
105
|
+
NoInfer<InverseCheck<M>>,
|
|
106
|
+
): App<M> {
|
|
107
|
+
return remember({ models: options.models, ...lifecycle(options) }, options)
|
|
108
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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.ts'
|
|
4
|
+
import type { Enum } from './fields.ts'
|
|
5
|
+
|
|
6
|
+
/** A shared enum, frozen, which `f.enumOf` takes in place of its values. */
|
|
7
|
+
export function defineEnum<const E extends string>(
|
|
8
|
+
name: string,
|
|
9
|
+
values: readonly E[],
|
|
10
|
+
options: { readonly description?: string } = {},
|
|
11
|
+
): Enum<E> {
|
|
12
|
+
if (!/^[A-Z_a-z]\w*$/.test(name) || name.startsWith('__')) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`defineEnum takes a GraphQL name: letters, digits and _, not ${JSON.stringify(name)}.`,
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
const of = `defineEnum(${name})`
|
|
18
|
+
enumValues(values, of)
|
|
19
|
+
const shared: Enum<E> = Object.freeze({
|
|
20
|
+
name,
|
|
21
|
+
values: Object.freeze([...values]),
|
|
22
|
+
description: describedBy(options.description, of),
|
|
23
|
+
})
|
|
24
|
+
sharedEnums.set(shared, shared)
|
|
25
|
+
return shared
|
|
26
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// `defineFunction` and `defineType` (D-27, D-109): a server operation, its
|
|
2
|
+
// access rule checked and its input validated on each `run` (D-75), and the
|
|
3
|
+
// named objects its output may hold.
|
|
4
|
+
import { refused } from './access.ts'
|
|
5
|
+
import type { FunctionDefinition, Output } from './definition.ts'
|
|
6
|
+
import {
|
|
7
|
+
describedBy,
|
|
8
|
+
functionDefinitions,
|
|
9
|
+
nameOf,
|
|
10
|
+
nullable,
|
|
11
|
+
outputOf,
|
|
12
|
+
typeDefinitions,
|
|
13
|
+
withOnce,
|
|
14
|
+
} from './definition.ts'
|
|
15
|
+
import { entriesOf } from './plugin-use.ts'
|
|
16
|
+
import { callerOf } from './engine.ts'
|
|
17
|
+
import { PublicError, Refused } from './errors.ts'
|
|
18
|
+
import type { StandardSchema } from './fields.ts'
|
|
19
|
+
import type {
|
|
20
|
+
Client,
|
|
21
|
+
FunctionPlugin,
|
|
22
|
+
FunctionWithKeys,
|
|
23
|
+
InputCheck,
|
|
24
|
+
ObjectType,
|
|
25
|
+
OutputSpec,
|
|
26
|
+
QueryCheck,
|
|
27
|
+
ResultOf,
|
|
28
|
+
RunFacts,
|
|
29
|
+
ServerFunction,
|
|
30
|
+
TypeFields,
|
|
31
|
+
} from './function.ts'
|
|
32
|
+
import type { ModelUse } from './plugin.ts'
|
|
33
|
+
import { isObject } from './query-where.ts'
|
|
34
|
+
import type { Caller } from './rules.ts'
|
|
35
|
+
import { isMaster } from './rules.ts'
|
|
36
|
+
import type { Running } from './running.ts'
|
|
37
|
+
import { current } from './running.ts'
|
|
38
|
+
import { checked, standardOf } from './validation.ts'
|
|
39
|
+
|
|
40
|
+
export function defineType<
|
|
41
|
+
const N extends string,
|
|
42
|
+
const F extends TypeFields,
|
|
43
|
+
>(definition: {
|
|
44
|
+
readonly name: N
|
|
45
|
+
/** What the type means, which the transports show (D-150). */
|
|
46
|
+
readonly description?: string
|
|
47
|
+
readonly fields: F
|
|
48
|
+
}): ObjectType<N, F> {
|
|
49
|
+
const { fields } = definition
|
|
50
|
+
const name = nameOf(definition.name, 'defineType')
|
|
51
|
+
const description = describedBy(definition.description, name)
|
|
52
|
+
if (!isObject(fields) || Object.keys(fields).length === 0) {
|
|
53
|
+
throw new Refused(`${name}: fields is an object of one output or more.`)
|
|
54
|
+
}
|
|
55
|
+
const outputs = new Map<string, Output>()
|
|
56
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
57
|
+
outputs.set(field, outputOf(value, `${name}.${field}`))
|
|
58
|
+
}
|
|
59
|
+
const recorded = { name, description, fields: outputs }
|
|
60
|
+
const type: object = Object.freeze({
|
|
61
|
+
name,
|
|
62
|
+
nullable: (): object => nullable({ kind: 'type', type: recorded }),
|
|
63
|
+
})
|
|
64
|
+
typeDefinitions.set(type, recorded)
|
|
65
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the value holds the name and `nullable`; its fields are a type only.
|
|
66
|
+
return type as ObjectType<N, F>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Closed to all but Master by default (D-04); the rule reads the caller. */
|
|
70
|
+
function allowed(
|
|
71
|
+
record: FunctionDefinition,
|
|
72
|
+
caller: Caller,
|
|
73
|
+
app: Running,
|
|
74
|
+
): void {
|
|
75
|
+
if (isMaster(caller)) return
|
|
76
|
+
const rule = isObject(record.rules) ? record.rules['access'] : undefined
|
|
77
|
+
if (rule !== undefined && typeof rule !== 'function') {
|
|
78
|
+
throw new TypeError(`${record.name}: access is a function.`)
|
|
79
|
+
}
|
|
80
|
+
const result: unknown =
|
|
81
|
+
rule === undefined ? false : Reflect.apply(rule, undefined, [{ caller }])
|
|
82
|
+
if (typeof result !== 'boolean') {
|
|
83
|
+
throw new TypeError(`${record.name}: access returns a boolean.`)
|
|
84
|
+
}
|
|
85
|
+
if (result) return
|
|
86
|
+
const { name } = record
|
|
87
|
+
throw refused({ function: name }, `${name} refused.`, caller, app.logger)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** `run`'s call, the input first when the function takes one; a transport
|
|
91
|
+
* passes its client (D-113). */
|
|
92
|
+
export async function called(
|
|
93
|
+
value: object,
|
|
94
|
+
record: FunctionDefinition,
|
|
95
|
+
args: readonly unknown[],
|
|
96
|
+
client?: Client,
|
|
97
|
+
): Promise<unknown> {
|
|
98
|
+
const app = current()
|
|
99
|
+
if (app?.functions.get(value) !== record) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`${record.name} is not a function of a started application.`,
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
const { input: schema, name } = record
|
|
105
|
+
const [input, options] =
|
|
106
|
+
schema === undefined ? [undefined, args[0]] : [args[0], args[1]]
|
|
107
|
+
const identity = callerOf(options, name)
|
|
108
|
+
return app.work.run(async () => {
|
|
109
|
+
const caller = await app.complete(identity)
|
|
110
|
+
allowed(record, caller, app)
|
|
111
|
+
const facts: { [key: string]: unknown } = { caller, client }
|
|
112
|
+
const standard = standardOf(schema)
|
|
113
|
+
if (standard !== undefined) {
|
|
114
|
+
const result = await checked(standard, input, [], name)
|
|
115
|
+
if ('issues' in result) {
|
|
116
|
+
throw new PublicError('VALIDATION_FAILED', 'Invalid input.', {
|
|
117
|
+
details: { issues: result.issues },
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
facts['input'] = result.value
|
|
121
|
+
}
|
|
122
|
+
const output: unknown = await Reflect.apply(record.run, undefined, [
|
|
123
|
+
Object.freeze(facts),
|
|
124
|
+
])
|
|
125
|
+
return record.output === undefined ? undefined : output
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function defineFunction<
|
|
130
|
+
const N extends string,
|
|
131
|
+
const O extends OutputSpec | undefined = undefined,
|
|
132
|
+
const I extends StandardSchema | undefined = undefined,
|
|
133
|
+
const U extends readonly ModelUse<string, FunctionPlugin>[] = [],
|
|
134
|
+
>(
|
|
135
|
+
definition: {
|
|
136
|
+
readonly name: N
|
|
137
|
+
/** What the function does, which the transports show (D-150). */
|
|
138
|
+
readonly description?: string
|
|
139
|
+
/** A query, served over GET too; a mutation by default (D-109). */
|
|
140
|
+
readonly query?: boolean
|
|
141
|
+
readonly use?: U
|
|
142
|
+
readonly input?: I
|
|
143
|
+
readonly output?: O
|
|
144
|
+
readonly run: (
|
|
145
|
+
facts: RunFacts<NoInfer<I>>,
|
|
146
|
+
) => NoInfer<ResultOf<O>> | Promise<NoInfer<ResultOf<O>>>
|
|
147
|
+
} & NoInfer<InputCheck<I>> &
|
|
148
|
+
NoInfer<QueryCheck<U[number]>>,
|
|
149
|
+
): ServerFunction<N, I, O, U[number]['plugin'], FunctionWithKeys<U[number]>> {
|
|
150
|
+
const { query = false, input, output, run } = definition
|
|
151
|
+
const name = nameOf(definition.name, 'defineFunction')
|
|
152
|
+
const description = describedBy(definition.description, name)
|
|
153
|
+
if (typeof query !== 'boolean')
|
|
154
|
+
throw new Refused(`${name}: query is a boolean.`)
|
|
155
|
+
if (input !== undefined && standardOf(input) === undefined) {
|
|
156
|
+
throw new Refused(`${name}: input is a Standard Schema.`)
|
|
157
|
+
}
|
|
158
|
+
if (typeof run !== 'function')
|
|
159
|
+
throw new Refused(`${name}: run is a function.`)
|
|
160
|
+
const record: FunctionDefinition = {
|
|
161
|
+
name,
|
|
162
|
+
description,
|
|
163
|
+
query,
|
|
164
|
+
input,
|
|
165
|
+
output:
|
|
166
|
+
output === undefined ? undefined : outputOf(output, `${name}.output`),
|
|
167
|
+
uses: entriesOf(name, definition.use ?? []),
|
|
168
|
+
run,
|
|
169
|
+
rules: undefined,
|
|
170
|
+
}
|
|
171
|
+
const fn: object = Object.freeze({
|
|
172
|
+
name,
|
|
173
|
+
input,
|
|
174
|
+
output,
|
|
175
|
+
with: withOnce(name, record, () => fn),
|
|
176
|
+
run: async (...args: unknown[]): Promise<unknown> =>
|
|
177
|
+
called(fn, record, args),
|
|
178
|
+
})
|
|
179
|
+
functionDefinitions.set(fn, record)
|
|
180
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the value ServerFunction describes; its types follow the definition.
|
|
181
|
+
return fn as unknown as ServerFunction<
|
|
182
|
+
N,
|
|
183
|
+
I,
|
|
184
|
+
O,
|
|
185
|
+
U[number]['plugin'],
|
|
186
|
+
FunctionWithKeys<U[number]>
|
|
187
|
+
>
|
|
188
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// `defineModel` (D-07, D-12): the fields its callback and its plugins build,
|
|
2
|
+
// recorded for `start`, and the model value whose methods run the queries
|
|
3
|
+
// (D-18) and its plugins' commands (D-89).
|
|
4
|
+
import {
|
|
5
|
+
describedBy,
|
|
6
|
+
fieldBuilders,
|
|
7
|
+
modelDefinitions,
|
|
8
|
+
nameOf,
|
|
9
|
+
nullable,
|
|
10
|
+
withOnce,
|
|
11
|
+
} from './definition.ts'
|
|
12
|
+
import type { FieldDefinition, ModelDefinition } from './definition.ts'
|
|
13
|
+
import { query } from './engine.ts'
|
|
14
|
+
import type { Operation } from './engine.ts'
|
|
15
|
+
import { addFields, usedBy } from './plugin-use.ts'
|
|
16
|
+
import type { FieldBuilders } from './fields.ts'
|
|
17
|
+
import type {
|
|
18
|
+
FieldShape,
|
|
19
|
+
IndexCheck,
|
|
20
|
+
IndexShape,
|
|
21
|
+
Model,
|
|
22
|
+
RecordCheck,
|
|
23
|
+
UniqueIndexFields,
|
|
24
|
+
} from './model.ts'
|
|
25
|
+
import type { ForModel, ModelUse, PluginTypes } from './plugin.ts'
|
|
26
|
+
import { Refused } from './errors.ts'
|
|
27
|
+
|
|
28
|
+
// The intersection of the used plugins' commands; `unknown` without any.
|
|
29
|
+
type CommandFn<U, F> =
|
|
30
|
+
U extends ModelUse<string, infer T>
|
|
31
|
+
? T extends { readonly commands: object }
|
|
32
|
+
? (value: ForModel<T, F>['commands']) => void
|
|
33
|
+
: never
|
|
34
|
+
: never
|
|
35
|
+
type Commands<U, F> = [CommandFn<U, F>] extends [never]
|
|
36
|
+
? unknown
|
|
37
|
+
: CommandFn<U, F> extends (value: infer I) => void
|
|
38
|
+
? I
|
|
39
|
+
: unknown
|
|
40
|
+
|
|
41
|
+
/** Each used plugin's key of `.with`, typed by the model's fields. */
|
|
42
|
+
type WithKeys<U, F> = {
|
|
43
|
+
readonly [
|
|
44
|
+
V in U as V extends ModelUse<infer P, infer T>
|
|
45
|
+
? T extends { readonly with: object | false }
|
|
46
|
+
? P
|
|
47
|
+
: never
|
|
48
|
+
: never
|
|
49
|
+
]?: V extends ModelUse<
|
|
50
|
+
string,
|
|
51
|
+
infer T extends { readonly with: object | false }
|
|
52
|
+
>
|
|
53
|
+
? ForModel<T, F>['with']
|
|
54
|
+
: never
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The types of the plugins in a model's `use`. */
|
|
58
|
+
type UsedTypes<U> = U extends ModelUse<string, infer T> ? T : never
|
|
59
|
+
|
|
60
|
+
// The fields the used plugins add, merged with the model's own.
|
|
61
|
+
type AddFn<U> =
|
|
62
|
+
U extends ModelUse<string, infer T>
|
|
63
|
+
? T extends { readonly addedFields: infer A }
|
|
64
|
+
? (value: A) => void
|
|
65
|
+
: never
|
|
66
|
+
: never
|
|
67
|
+
type Added<U> = [AddFn<U>] extends [never]
|
|
68
|
+
? unknown
|
|
69
|
+
: AddFn<U> extends (value: infer I) => void
|
|
70
|
+
? I
|
|
71
|
+
: unknown
|
|
72
|
+
type AllFields<F, U> = [Added<U>] extends [unknown]
|
|
73
|
+
? unknown extends Added<U>
|
|
74
|
+
? F
|
|
75
|
+
: F & Added<U>
|
|
76
|
+
: F & Added<U>
|
|
77
|
+
|
|
78
|
+
/** A model's fields, as `fields` returns them. */
|
|
79
|
+
export type FieldShapes = { readonly [field: string]: FieldShape }
|
|
80
|
+
|
|
81
|
+
/** The `fields` callback: its builders know the plugins of `use` (D-12, D-57). */
|
|
82
|
+
export type ModelFields<
|
|
83
|
+
F,
|
|
84
|
+
U extends readonly ModelUse<string, PluginTypes>[],
|
|
85
|
+
> = (fields: FieldBuilders<UsedTypes<U[number]>>) => F & NoInfer<RecordCheck<F>>
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The model a declaration with these fields and plugins builds; a plugin's
|
|
89
|
+
* `define<Kind>` returns it with its own entry in `U` (D-13).
|
|
90
|
+
*/
|
|
91
|
+
export type ModelOf<
|
|
92
|
+
N extends string,
|
|
93
|
+
F,
|
|
94
|
+
U extends readonly ModelUse<string, PluginTypes>[],
|
|
95
|
+
X extends readonly IndexShape[] = [],
|
|
96
|
+
> = Model<
|
|
97
|
+
N,
|
|
98
|
+
AllFields<F, U[number]>,
|
|
99
|
+
U[number]['plugin'],
|
|
100
|
+
WithKeys<U[number], AllFields<F, U[number]>>,
|
|
101
|
+
UniqueIndexFields<X>
|
|
102
|
+
> &
|
|
103
|
+
Commands<U[number], AllFields<F, U[number]>>
|
|
104
|
+
|
|
105
|
+
/** `indexes`, whose fields are checked against the model's (D-52). */
|
|
106
|
+
export type ModelIndexes<
|
|
107
|
+
X,
|
|
108
|
+
F,
|
|
109
|
+
U extends readonly ModelUse<string, PluginTypes>[],
|
|
110
|
+
> = X & NoInfer<IndexCheck<X, AllFields<F, U[number]>>>
|
|
111
|
+
|
|
112
|
+
export function defineModel<
|
|
113
|
+
const N extends string,
|
|
114
|
+
const F extends FieldShapes,
|
|
115
|
+
const U extends readonly ModelUse<string, PluginTypes>[] = [],
|
|
116
|
+
const X extends readonly IndexShape[] = [],
|
|
117
|
+
>(definition: {
|
|
118
|
+
readonly name: N
|
|
119
|
+
/** What the model means, which the transports show (D-150). */
|
|
120
|
+
readonly description?: string
|
|
121
|
+
/** The stored table's or collection's name (D-63). */
|
|
122
|
+
readonly dbName?: string
|
|
123
|
+
readonly use?: U
|
|
124
|
+
/** The only form (D-12). */
|
|
125
|
+
readonly fields: ModelFields<F, U>
|
|
126
|
+
readonly indexes?: ModelIndexes<X, F, U>
|
|
127
|
+
}): ModelOf<N, F, U, X> {
|
|
128
|
+
const model = createModel({
|
|
129
|
+
name: definition.name,
|
|
130
|
+
description: definition.description,
|
|
131
|
+
dbName: definition.dbName,
|
|
132
|
+
use: definition.use ?? [],
|
|
133
|
+
fields: definition.fields,
|
|
134
|
+
indexes: definition.indexes ?? [],
|
|
135
|
+
})
|
|
136
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- createModel builds the value ModelOf describes; the types follow the definition.
|
|
137
|
+
return model as unknown as ModelOf<N, F, U, X>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const operations: readonly Operation[] = [
|
|
141
|
+
'findMany',
|
|
142
|
+
'findFirst',
|
|
143
|
+
'findUnique',
|
|
144
|
+
'findFirstOrThrow',
|
|
145
|
+
'findUniqueOrThrow',
|
|
146
|
+
'count',
|
|
147
|
+
'aggregate',
|
|
148
|
+
'groupBy',
|
|
149
|
+
'create',
|
|
150
|
+
'update',
|
|
151
|
+
'upsert',
|
|
152
|
+
'delete',
|
|
153
|
+
'updateMany',
|
|
154
|
+
'createMany',
|
|
155
|
+
'updateManyAndReturn',
|
|
156
|
+
'createManyAndReturn',
|
|
157
|
+
'deleteMany',
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
function createModel(definition: {
|
|
161
|
+
readonly name: unknown
|
|
162
|
+
readonly description: unknown
|
|
163
|
+
readonly dbName: unknown
|
|
164
|
+
readonly use: readonly unknown[]
|
|
165
|
+
readonly fields: Function
|
|
166
|
+
readonly indexes: readonly IndexShape[]
|
|
167
|
+
}): object {
|
|
168
|
+
const { dbName } = definition
|
|
169
|
+
const name = nameOf(definition.name, 'defineModel')
|
|
170
|
+
if (dbName !== undefined && typeof dbName !== 'string') {
|
|
171
|
+
throw new Refused(`${name}: dbName must be a string.`)
|
|
172
|
+
}
|
|
173
|
+
const description = describedBy(definition.description, name)
|
|
174
|
+
const built: unknown = Reflect.apply(definition.fields, undefined, [
|
|
175
|
+
fieldBuilders,
|
|
176
|
+
])
|
|
177
|
+
const fields = new Map<string, FieldDefinition>()
|
|
178
|
+
addFields(name, built, fields, new Map())
|
|
179
|
+
const queries: { [operation: string]: unknown } = {}
|
|
180
|
+
for (const operation of operations) {
|
|
181
|
+
queries[operation] = async (args?: unknown, options?: unknown) =>
|
|
182
|
+
query(name, operation, args, options)
|
|
183
|
+
}
|
|
184
|
+
Object.freeze(queries)
|
|
185
|
+
const { uses, commands, indexes } = usedBy(
|
|
186
|
+
name,
|
|
187
|
+
definition.use,
|
|
188
|
+
fields,
|
|
189
|
+
queries,
|
|
190
|
+
)
|
|
191
|
+
const record: ModelDefinition = {
|
|
192
|
+
name,
|
|
193
|
+
description,
|
|
194
|
+
dbName,
|
|
195
|
+
fields,
|
|
196
|
+
indexes: [
|
|
197
|
+
...definition.indexes.map((index) => ({
|
|
198
|
+
fields: [...index.fields],
|
|
199
|
+
...(index.unique === undefined ? {} : { unique: index.unique }),
|
|
200
|
+
...(index.contains === undefined ? {} : { contains: index.contains }),
|
|
201
|
+
})),
|
|
202
|
+
// A plugin's, checked with the model's at startup (D-122).
|
|
203
|
+
...indexes,
|
|
204
|
+
],
|
|
205
|
+
uses,
|
|
206
|
+
queries,
|
|
207
|
+
rules: undefined,
|
|
208
|
+
}
|
|
209
|
+
const model: { [key: string]: unknown } = {
|
|
210
|
+
name,
|
|
211
|
+
...queries,
|
|
212
|
+
...Object.fromEntries(commands),
|
|
213
|
+
}
|
|
214
|
+
model['with'] = withOnce(name, record, () => frozen)
|
|
215
|
+
model['nullable'] = (): object =>
|
|
216
|
+
nullable({ kind: 'model', value: frozen, name })
|
|
217
|
+
const frozen = Object.freeze(model)
|
|
218
|
+
modelDefinitions.set(frozen, record)
|
|
219
|
+
return frozen
|
|
220
|
+
}
|