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
package/src/fields.ts
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
// Field builders (D-09, D-12); a field's `.with` takes the rules of
|
|
2
|
+
// field-rules.ts (D-44).
|
|
3
|
+
import type { Declared, FieldWith } from './field-rules.ts'
|
|
4
|
+
import type { Caller } from './rules.ts'
|
|
5
|
+
import type { calendarDay, fieldType } from './symbols.ts'
|
|
6
|
+
import type { NoOptions, Virtual, VirtualOptions } from './virtual.ts'
|
|
7
|
+
|
|
8
|
+
export type JsonValue =
|
|
9
|
+
| string
|
|
10
|
+
| number
|
|
11
|
+
| boolean
|
|
12
|
+
| null
|
|
13
|
+
| readonly JsonValue[]
|
|
14
|
+
| { readonly [key: string]: JsonValue }
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Standard Schema (standardschema.dev), the part the kernel reads. Its
|
|
18
|
+
* specification asks for the interface to be copied, not imported.
|
|
19
|
+
*/
|
|
20
|
+
export interface StandardSchema<I = unknown, O = I> {
|
|
21
|
+
readonly '~standard': {
|
|
22
|
+
readonly version: 1
|
|
23
|
+
readonly vendor: string
|
|
24
|
+
readonly validate: (
|
|
25
|
+
value: unknown,
|
|
26
|
+
) => SchemaResult<O> | Promise<SchemaResult<O>>
|
|
27
|
+
readonly types?: { readonly input: I; readonly output: O } | undefined
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
type SchemaResult<O> =
|
|
31
|
+
| { readonly value: O; readonly issues?: undefined }
|
|
32
|
+
| { readonly issues: readonly { readonly message: string }[] }
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A field's `validate` (D-23): a Standard Schema, or a function that returns
|
|
36
|
+
* the value to store and throws to refuse it.
|
|
37
|
+
*/
|
|
38
|
+
export type Validate<V> = StandardSchema<V, V> | ((value: V) => V | Promise<V>)
|
|
39
|
+
|
|
40
|
+
/** A default's function form: `undefined` gives no value (D-53). */
|
|
41
|
+
export type DefaultFunction<V> = (facts: {
|
|
42
|
+
readonly caller: Caller
|
|
43
|
+
}) => V | undefined
|
|
44
|
+
|
|
45
|
+
/** What a field means, which the transports show and nothing reads (D-150). */
|
|
46
|
+
export type Described = { readonly description?: string }
|
|
47
|
+
|
|
48
|
+
/** What shapes a scalar field (D-08, D-23, D-52, D-53). */
|
|
49
|
+
export type ScalarOptions<V> = Described & {
|
|
50
|
+
readonly optional?: boolean
|
|
51
|
+
readonly unique?: boolean
|
|
52
|
+
readonly default?: V | DefaultFunction<V>
|
|
53
|
+
readonly validate?: Validate<V>
|
|
54
|
+
/** The stored column's name, when the adapter's convention will not do (D-63). */
|
|
55
|
+
readonly dbName?: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** What a calendar day's options carry in its type, filtered in order (D-150). */
|
|
59
|
+
export type Day = { readonly [calendarDay]: true }
|
|
60
|
+
|
|
61
|
+
/** A decimal's options: its digits are required (D-145). */
|
|
62
|
+
export type DecimalOptions = ScalarOptions<string> & {
|
|
63
|
+
readonly precision: number
|
|
64
|
+
readonly scale: number
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A shared enum, which `defineEnum` returns frozen (D-150). */
|
|
68
|
+
export type Enum<E extends string = string> = {
|
|
69
|
+
/** Its GraphQL enum's and its OpenAPI schema's name. */
|
|
70
|
+
readonly name: string
|
|
71
|
+
readonly values: readonly E[]
|
|
72
|
+
readonly description: string | undefined
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** A field's options without `list` (D-120, D-127). */
|
|
76
|
+
export type SingleOptions<V> = ScalarOptions<V> & { readonly list?: false }
|
|
77
|
+
|
|
78
|
+
/** A string's options: `search` adds it to its model's document (D-148). */
|
|
79
|
+
export type StringOptions = SingleOptions<string> & {
|
|
80
|
+
readonly search?: boolean
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** What shapes a list of strings, numbers or enum values (D-120, D-127). */
|
|
84
|
+
export type ListOptions<V> = Described & {
|
|
85
|
+
readonly list: true
|
|
86
|
+
readonly default?: readonly V[] | DefaultFunction<readonly V[]>
|
|
87
|
+
readonly validate?: Validate<readonly V[]>
|
|
88
|
+
readonly dbName?: string
|
|
89
|
+
}
|
|
90
|
+
// A list is `[]` when empty and may hold a value twice. The messages are
|
|
91
|
+
// objects, as OneCheck's.
|
|
92
|
+
type ListCheck<O> = (O extends { readonly optional: unknown }
|
|
93
|
+
? {
|
|
94
|
+
readonly optional: {
|
|
95
|
+
readonly error: 'a list is [] when empty: it is never optional'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
: unknown) &
|
|
99
|
+
(O extends { readonly unique: unknown }
|
|
100
|
+
? { readonly unique: { readonly error: 'a list cannot be unique' } }
|
|
101
|
+
: unknown) &
|
|
102
|
+
(O extends { readonly search: unknown }
|
|
103
|
+
? { readonly search: { readonly error: 'a list is not searched' } }
|
|
104
|
+
: unknown)
|
|
105
|
+
type ListableCheck<O, V> = O extends { readonly list: true }
|
|
106
|
+
? ListCheck<O> & ValidateCheck<O, readonly V[]>
|
|
107
|
+
: ValidateCheck<O, V>
|
|
108
|
+
type Listable<V, O, T> = O extends { readonly list: true }
|
|
109
|
+
? Field<readonly V[], O, never, T>
|
|
110
|
+
: Field<V, O, never, T>
|
|
111
|
+
|
|
112
|
+
/** The types a Standard Schema declares, `never` without them. */
|
|
113
|
+
export type SchemaTypes<S> = S extends {
|
|
114
|
+
readonly '~standard': { readonly types?: infer T }
|
|
115
|
+
}
|
|
116
|
+
? NonNullable<T>
|
|
117
|
+
: never
|
|
118
|
+
type IsAny<X> = 0 extends 1 & X ? true : false
|
|
119
|
+
/** Refuses a schema typed `any` or without types, under `validate`. */
|
|
120
|
+
export type AnyCheck<S> =
|
|
121
|
+
SchemaTypes<S> extends { readonly input: infer I; readonly output: infer O }
|
|
122
|
+
? IsAny<I> extends true
|
|
123
|
+
? { readonly validate: 'the schema types its input as any' }
|
|
124
|
+
: IsAny<O> extends true
|
|
125
|
+
? { readonly validate: 'the schema types its output as any' }
|
|
126
|
+
: unknown
|
|
127
|
+
: { readonly validate: 'the schema declares no types' }
|
|
128
|
+
// A schema is checked on its line: its input must take every value of the
|
|
129
|
+
// field, which the covariant `types` alone would not refuse.
|
|
130
|
+
type ValidateCheck<O, V> = O extends {
|
|
131
|
+
readonly validate: infer S extends StandardSchema
|
|
132
|
+
}
|
|
133
|
+
? unknown extends AnyCheck<S>
|
|
134
|
+
? SchemaTypes<S> extends { readonly input: infer I }
|
|
135
|
+
? [V] extends [I]
|
|
136
|
+
? unknown
|
|
137
|
+
: {
|
|
138
|
+
readonly validate: 'the schema must accept every value of the field'
|
|
139
|
+
}
|
|
140
|
+
: unknown
|
|
141
|
+
: AnyCheck<S>
|
|
142
|
+
: unknown
|
|
143
|
+
|
|
144
|
+
/** What shapes a `json` field (D-53): its schema gives its types. */
|
|
145
|
+
export type JsonOptions = Described & {
|
|
146
|
+
readonly optional?: boolean
|
|
147
|
+
// Any value; the function member types `caller`. JsonCheck checks it.
|
|
148
|
+
readonly default?: DefaultFunction<unknown> | {} | null
|
|
149
|
+
readonly validate?: StandardSchema | ((value: JsonValue) => unknown)
|
|
150
|
+
readonly dbName?: string
|
|
151
|
+
}
|
|
152
|
+
/** What a `json` field stores and returns: the schema's output. */
|
|
153
|
+
type JsonOutput<O> = O extends { readonly validate: infer S }
|
|
154
|
+
? S extends StandardSchema
|
|
155
|
+
? SchemaTypes<S>['output']
|
|
156
|
+
: S extends (value: JsonValue) => infer R
|
|
157
|
+
? Awaited<R>
|
|
158
|
+
: unknown
|
|
159
|
+
: unknown
|
|
160
|
+
/** What a `json` field is written: the schema's input. */
|
|
161
|
+
type JsonInput<O> = O extends { readonly validate: infer S }
|
|
162
|
+
? S extends StandardSchema
|
|
163
|
+
? SchemaTypes<S>['input']
|
|
164
|
+
: JsonValue
|
|
165
|
+
: JsonValue
|
|
166
|
+
// Defaults are validated (D-67), so a default is the schema's input.
|
|
167
|
+
type JsonCheck<O> = (O extends {
|
|
168
|
+
readonly validate: infer S extends StandardSchema
|
|
169
|
+
}
|
|
170
|
+
? AnyCheck<S>
|
|
171
|
+
: unknown) &
|
|
172
|
+
(O extends { readonly default: infer D }
|
|
173
|
+
? [D] extends [JsonInput<O> | DefaultFunction<JsonInput<O>>]
|
|
174
|
+
? unknown
|
|
175
|
+
: { readonly default: JsonInput<O> | DefaultFunction<JsonInput<O>> }
|
|
176
|
+
: unknown)
|
|
177
|
+
|
|
178
|
+
/** What shapes a `one` relation (D-37, D-54). */
|
|
179
|
+
export type OneOptions = Described & {
|
|
180
|
+
readonly optional?: boolean
|
|
181
|
+
/** The target's id (D-53). */
|
|
182
|
+
readonly default?:
|
|
183
|
+
{ readonly id: string } | DefaultFunction<{ readonly id: string }>
|
|
184
|
+
readonly onDelete?: 'cascade' | 'restrict' | 'setNull'
|
|
185
|
+
readonly inverse?: string
|
|
186
|
+
readonly dbName?: string
|
|
187
|
+
}
|
|
188
|
+
// `setNull` empties the relation, which only an optional one may be (D-54).
|
|
189
|
+
// The message is an object: a literal would empty the whole options type.
|
|
190
|
+
type OneCheck<O> = O extends { readonly optional: true }
|
|
191
|
+
? unknown
|
|
192
|
+
: O extends { readonly onDelete: 'setNull' }
|
|
193
|
+
? {
|
|
194
|
+
readonly onDelete: {
|
|
195
|
+
readonly error: 'setNull needs an optional relation'
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
: unknown
|
|
199
|
+
|
|
200
|
+
export type ManyOptions = Described &
|
|
201
|
+
(
|
|
202
|
+
| { readonly inverse?: string; readonly from?: undefined }
|
|
203
|
+
| {
|
|
204
|
+
/**
|
|
205
|
+
* A view: read-only, on the link of this other `many` of the
|
|
206
|
+
* model, its rows those its filter keeps (D-151).
|
|
207
|
+
*/
|
|
208
|
+
readonly from: string
|
|
209
|
+
readonly inverse?: undefined
|
|
210
|
+
}
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
/** An optional field may be written `null`. */
|
|
214
|
+
type Written<V, O> = O extends { readonly optional: true } ? V | null : V
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* `V` is the stored value and `I` the written one, which differ when a
|
|
218
|
+
* schema transforms (D-23). `P` names the plugin that provides the field
|
|
219
|
+
* type (D-32); `T` is the types of the plugins the model uses, whose field
|
|
220
|
+
* rules `.with` takes. Rules judge what the caller sent (D-67): they see `I`.
|
|
221
|
+
* `J` marks a `json` field, which cannot be sorted (D-94); `S` is a plugin
|
|
222
|
+
* field type's stored field, which a `beforeFind` filters (D-122).
|
|
223
|
+
*/
|
|
224
|
+
export interface Field<
|
|
225
|
+
V,
|
|
226
|
+
O,
|
|
227
|
+
P extends string = never,
|
|
228
|
+
T = never,
|
|
229
|
+
I = V,
|
|
230
|
+
J extends boolean = false,
|
|
231
|
+
S = never,
|
|
232
|
+
> {
|
|
233
|
+
readonly [fieldType]: {
|
|
234
|
+
readonly kind: 'scalar'
|
|
235
|
+
readonly value: V
|
|
236
|
+
readonly input: I
|
|
237
|
+
readonly options: O
|
|
238
|
+
readonly plugin?: P
|
|
239
|
+
readonly json: J
|
|
240
|
+
readonly stored: S
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* `.with({ ... })` types `value` and `record.id`;
|
|
244
|
+
* `.with<{ status: 'draft' | 'published' }>` also types the record fields it
|
|
245
|
+
* declares, each with the model's exact type (D-44).
|
|
246
|
+
*/
|
|
247
|
+
// With a declared record, `.with<{ ... }>`; refused without one, so the
|
|
248
|
+
// next signature applies.
|
|
249
|
+
with<R extends object>(
|
|
250
|
+
rules: [keyof R] extends [never]
|
|
251
|
+
? never
|
|
252
|
+
: FieldWith<NoInfer<R>, Written<I, O>, T>,
|
|
253
|
+
): Field<V, O, P, never, I, J, S> & Declared<R>
|
|
254
|
+
with(
|
|
255
|
+
rules: FieldWith<never, Written<I, O>, T>,
|
|
256
|
+
): Field<V, O, P, never, I, J, S>
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export type RelationValue = { readonly id: string }
|
|
260
|
+
|
|
261
|
+
/** What a `many` relation's write rule sees: the caller's lists (D-86). */
|
|
262
|
+
export type ManyValue = {
|
|
263
|
+
readonly connect: readonly RelationValue[]
|
|
264
|
+
readonly disconnect: readonly RelationValue[]
|
|
265
|
+
/** The linked rows it deletes (D-138). */
|
|
266
|
+
readonly delete: readonly RelationValue[]
|
|
267
|
+
/** The rows it creates, by the ids the kernel gives them (D-128). */
|
|
268
|
+
readonly create: readonly RelationValue[]
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// `T` is the target's thunk, constrained to `object` only: its return type
|
|
272
|
+
// is read lazily, in queries and at registration, never while declaring.
|
|
273
|
+
export interface One<T, O, U = never> {
|
|
274
|
+
readonly [fieldType]: {
|
|
275
|
+
readonly kind: 'one'
|
|
276
|
+
readonly target: T
|
|
277
|
+
readonly options: O
|
|
278
|
+
}
|
|
279
|
+
with<R extends object = never>(
|
|
280
|
+
rules: FieldWith<NoInfer<R>, RelationValue | null, U>,
|
|
281
|
+
): One<T, O> & Declared<R>
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface Many<T, O, U = never> {
|
|
285
|
+
readonly [fieldType]: {
|
|
286
|
+
readonly kind: 'many'
|
|
287
|
+
readonly target: T
|
|
288
|
+
readonly options: O
|
|
289
|
+
}
|
|
290
|
+
with<R extends object = never>(
|
|
291
|
+
rules: FieldWith<NoInfer<R>, ManyValue, U>,
|
|
292
|
+
): Many<T, O> & Declared<R>
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* The field builders, which only `defineModel`'s `fields` callback receives:
|
|
297
|
+
* typed by the model's `use`, a field's `.with` takes those plugins' rules
|
|
298
|
+
* (D-12, D-57).
|
|
299
|
+
*/
|
|
300
|
+
export interface FieldBuilders<T> {
|
|
301
|
+
/**
|
|
302
|
+
* With `list: true`, a list of strings, `[]` when empty (D-120); with
|
|
303
|
+
* `search: true`, matched by its model's `_search` (D-148).
|
|
304
|
+
*/
|
|
305
|
+
readonly string: <
|
|
306
|
+
const O extends StringOptions | ListOptions<string> = StringOptions,
|
|
307
|
+
>(
|
|
308
|
+
options?: O & NoInfer<ListableCheck<O, string>>,
|
|
309
|
+
) => Listable<string, O, T>
|
|
310
|
+
/**
|
|
311
|
+
* With `list: true`, a list of numbers, `[]` when empty (D-127); with
|
|
312
|
+
* `integer: true`, whole numbers from -2,147,483,648 to 2,147,483,647
|
|
313
|
+
* (D-132).
|
|
314
|
+
*/
|
|
315
|
+
readonly number: <
|
|
316
|
+
const O extends (SingleOptions<number> | ListOptions<number>) & {
|
|
317
|
+
readonly integer?: boolean
|
|
318
|
+
} = SingleOptions<number>,
|
|
319
|
+
>(
|
|
320
|
+
options?: O & NoInfer<ListableCheck<O, number>>,
|
|
321
|
+
) => Listable<number, O, T>
|
|
322
|
+
/**
|
|
323
|
+
* Exact numbers of `precision` digits, `scale` of them after the point,
|
|
324
|
+
* read and written as strings at that scale: `'12.50'` (D-145).
|
|
325
|
+
*/
|
|
326
|
+
readonly decimal: <const O extends DecimalOptions>(
|
|
327
|
+
options: O & NoInfer<ValidateCheck<O, string>>,
|
|
328
|
+
) => Field<string, O, never, T>
|
|
329
|
+
readonly boolean: <
|
|
330
|
+
const O extends ScalarOptions<boolean> = ScalarOptions<boolean>,
|
|
331
|
+
>(
|
|
332
|
+
options?: O & NoInfer<ValidateCheck<O, boolean>>,
|
|
333
|
+
) => Field<boolean, O, never, T>
|
|
334
|
+
readonly dateTime: <
|
|
335
|
+
const O extends ScalarOptions<Date> = ScalarOptions<Date>,
|
|
336
|
+
>(
|
|
337
|
+
options?: O & NoInfer<ValidateCheck<O, Date>>,
|
|
338
|
+
) => Field<Date, O, never, T>
|
|
339
|
+
/** A calendar day, the string `'2026-07-31'`, without a time zone (D-150). */
|
|
340
|
+
readonly date: <
|
|
341
|
+
const O extends ScalarOptions<string> = ScalarOptions<string>,
|
|
342
|
+
>(
|
|
343
|
+
options?: O & NoInfer<ValidateCheck<O, string>>,
|
|
344
|
+
) => Field<string, O & Day, never, T>
|
|
345
|
+
/**
|
|
346
|
+
* Without a schema, a `json` value is `unknown` and written as JSON; with
|
|
347
|
+
* one, it reads the schema's output and is written its input (D-53).
|
|
348
|
+
*/
|
|
349
|
+
readonly json: <const O extends JsonOptions = JsonOptions>(
|
|
350
|
+
options?: O & NoInfer<JsonCheck<O>>,
|
|
351
|
+
) => Field<JsonOutput<O>, O, never, T, JsonInput<O>, true>
|
|
352
|
+
/**
|
|
353
|
+
* With `list: true`, a list of its values, `[]` when empty (D-127); a
|
|
354
|
+
* `defineEnum` in place of its values is one type for each field (D-150),
|
|
355
|
+
* and `Object.values(Status)` of a TypeScript string enum types it `Status`.
|
|
356
|
+
*/
|
|
357
|
+
readonly enumOf: <
|
|
358
|
+
const E extends string,
|
|
359
|
+
const O extends SingleOptions<E> | ListOptions<E> = SingleOptions<E>,
|
|
360
|
+
>(
|
|
361
|
+
values: readonly E[] | Enum<E>,
|
|
362
|
+
options?: O & NoInfer<ListableCheck<O, E>>,
|
|
363
|
+
) => Listable<E, O, T>
|
|
364
|
+
readonly one: <M extends object, const O extends OneOptions = OneOptions>(
|
|
365
|
+
target: M,
|
|
366
|
+
options?: O & NoInfer<OneCheck<O>>,
|
|
367
|
+
) => One<M, O, T>
|
|
368
|
+
readonly many: <M extends object, const O extends ManyOptions = ManyOptions>(
|
|
369
|
+
target: M,
|
|
370
|
+
options?: O,
|
|
371
|
+
) => Many<M, O, T>
|
|
372
|
+
/**
|
|
373
|
+
* Computed on read by the model's `.with({ virtuals })` (D-151): a
|
|
374
|
+
* Standard Schema, or a model or a type behind an arrow, `() => [Image]`.
|
|
375
|
+
*/
|
|
376
|
+
readonly virtual: <
|
|
377
|
+
const O extends object,
|
|
378
|
+
const X extends VirtualOptions = NoOptions,
|
|
379
|
+
>(
|
|
380
|
+
output: O,
|
|
381
|
+
options?: X,
|
|
382
|
+
) => Virtual<O, X>
|
|
383
|
+
}
|
package/src/function.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Functions (D-27, D-45, D-109): a server operation with a Standard Schema
|
|
2
|
+
// input and a typed output, which `defineApp({ functions })` adds to the
|
|
3
|
+
// application, and the named objects its output may hold.
|
|
4
|
+
import type { AnyCheck, SchemaTypes, StandardSchema } from './fields.ts'
|
|
5
|
+
import type { ModelShape } from './model.ts'
|
|
6
|
+
import type { ModelUse, PluginTypes } from './plugin.ts'
|
|
7
|
+
import type { QueryOptions } from './query.ts'
|
|
8
|
+
import type { RecordOf } from './record.ts'
|
|
9
|
+
import type { Caller } from './rules.ts'
|
|
10
|
+
import type { modelFields, nullableOf, typeFields } from './symbols.ts'
|
|
11
|
+
|
|
12
|
+
/** `X.nullable()`: a model or a type, or `null` (D-109). */
|
|
13
|
+
export type Nullable<X> = { readonly [nullableOf]: X }
|
|
14
|
+
|
|
15
|
+
/** A `defineType`: a named object, which may hold a model (D-109). */
|
|
16
|
+
export interface ObjectType<N extends string, F> {
|
|
17
|
+
readonly name: N
|
|
18
|
+
readonly [typeFields]: F
|
|
19
|
+
nullable(): Nullable<this>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type Named =
|
|
23
|
+
ModelShape | { readonly name: string; readonly [typeFields]: object }
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* What a function returns (D-109): a model or a type, one or `null`, a list
|
|
27
|
+
* of one, or any Standard Schema.
|
|
28
|
+
*/
|
|
29
|
+
export type OutputSpec =
|
|
30
|
+
Named | Nullable<Named> | readonly [Named] | StandardSchema
|
|
31
|
+
|
|
32
|
+
/** A `defineType`'s fields: any output but nothing. */
|
|
33
|
+
export type TypeFields = { readonly [field: string]: OutputSpec }
|
|
34
|
+
|
|
35
|
+
/** The value of an output: typed, never validated (D-45). */
|
|
36
|
+
export type OutputValue<O> = O extends { readonly [modelFields]: infer F }
|
|
37
|
+
? RecordOf<F>
|
|
38
|
+
: O extends { readonly [typeFields]: infer F }
|
|
39
|
+
? { readonly [K in keyof F]: OutputValue<F[K]> }
|
|
40
|
+
: O extends { readonly [nullableOf]: infer X }
|
|
41
|
+
? OutputValue<X> | null
|
|
42
|
+
: O extends StandardSchema
|
|
43
|
+
? SchemaTypes<O>['output']
|
|
44
|
+
: O extends readonly [infer X]
|
|
45
|
+
? readonly OutputValue<X>[]
|
|
46
|
+
: never
|
|
47
|
+
|
|
48
|
+
/** What `run` returns: nothing without an output. */
|
|
49
|
+
export type ResultOf<O> = [O] extends [undefined] ? void : OutputValue<O>
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A function's access rule reads the caller only: it runs before the input
|
|
53
|
+
* is validated, so a closed function says nothing of its input (D-04).
|
|
54
|
+
*/
|
|
55
|
+
export type FunctionAccess = (facts: { readonly caller: Caller }) => boolean
|
|
56
|
+
|
|
57
|
+
export type FunctionWith<W> = { readonly access?: FunctionAccess } & W
|
|
58
|
+
|
|
59
|
+
/** What a caller passes to `run`: the input before validation (D-75). */
|
|
60
|
+
export type RunArgs<I> = [I] extends [StandardSchema]
|
|
61
|
+
? readonly [input: SchemaTypes<I>['input'], options?: QueryOptions]
|
|
62
|
+
: readonly [options?: QueryOptions]
|
|
63
|
+
|
|
64
|
+
export interface ServerFunction<N extends string, I, O, P, W> {
|
|
65
|
+
readonly name: N
|
|
66
|
+
/** The schemas the transports read (D-45). */
|
|
67
|
+
readonly input: I
|
|
68
|
+
readonly output: O
|
|
69
|
+
/** The plugins it uses, checked against the application's (D-24). */
|
|
70
|
+
readonly plugins?: P
|
|
71
|
+
with(rules: FunctionWith<W>): this
|
|
72
|
+
/** Checks access, validates the input, then runs it (D-75). */
|
|
73
|
+
run(...args: RunArgs<I>): Promise<ResultOf<O>>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type FunctionShape = {
|
|
77
|
+
readonly name: string
|
|
78
|
+
readonly output: OutputSpec | undefined
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A plugin that exposes functions: it keys their `.with` (D-27). */
|
|
82
|
+
export type FunctionPlugin = PluginTypes & {
|
|
83
|
+
readonly functionWith: object | false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Each used plugin's key of a function's `.with`. */
|
|
87
|
+
export type FunctionWithKeys<U> = {
|
|
88
|
+
readonly [
|
|
89
|
+
V in U as V extends ModelUse<infer P, FunctionPlugin> ? P : never
|
|
90
|
+
]?: V extends ModelUse<string, infer T extends FunctionPlugin>
|
|
91
|
+
? T['functionWith']
|
|
92
|
+
: never
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** A cookie's attributes (D-113). */
|
|
96
|
+
export interface CookieOptions {
|
|
97
|
+
/** In seconds. */
|
|
98
|
+
readonly maxAge?: number
|
|
99
|
+
readonly path?: string
|
|
100
|
+
readonly domain?: string
|
|
101
|
+
readonly httpOnly?: boolean
|
|
102
|
+
readonly secure?: boolean
|
|
103
|
+
readonly sameSite?: 'Strict' | 'Lax' | 'None'
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Who calls a function through a transport (D-113). */
|
|
107
|
+
export interface Client {
|
|
108
|
+
/** The connection's address, `''` when the transport has none. */
|
|
109
|
+
readonly address: string
|
|
110
|
+
/** Read from the request, written on the transport's HTTP response. */
|
|
111
|
+
readonly cookies: {
|
|
112
|
+
/** The request's cookie `name`, `undefined` when it sent none. */
|
|
113
|
+
get(name: string): string | undefined
|
|
114
|
+
set(name: string, value: string, options?: CookieOptions): void
|
|
115
|
+
/** Its `path` and `domain` must be those it was set with. */
|
|
116
|
+
clear(name: string, options?: CookieOptions): void
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* What `run` receives: the caller, its client when a transport calls it,
|
|
122
|
+
* and the validated input if any (D-113).
|
|
123
|
+
*/
|
|
124
|
+
export type RunFacts<I> = {
|
|
125
|
+
readonly caller: Caller
|
|
126
|
+
readonly client: Client | undefined
|
|
127
|
+
} & ([I] extends [StandardSchema]
|
|
128
|
+
? { readonly input: SchemaTypes<I>['output'] }
|
|
129
|
+
: unknown)
|
|
130
|
+
|
|
131
|
+
/** Requires `query: true` when a plugin of `use` takes queries only (D-154). */
|
|
132
|
+
export type QueryCheck<U> = [
|
|
133
|
+
Extract<U, ModelUse<string, { readonly queryOnly: true }>>,
|
|
134
|
+
] extends [never]
|
|
135
|
+
? unknown
|
|
136
|
+
: { readonly query: true }
|
|
137
|
+
|
|
138
|
+
/** Refuses an input typed `any` or without types (D-27). */
|
|
139
|
+
export type InputCheck<I> = [I] extends [StandardSchema]
|
|
140
|
+
? AnyCheck<I> extends { readonly validate: infer M }
|
|
141
|
+
? { readonly input: M }
|
|
142
|
+
: unknown
|
|
143
|
+
: unknown
|
package/src/functions.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// The application's and the plugins' functions at `start` (D-27, D-109,
|
|
2
|
+
// D-113): each named once, its plugins installed, the models it returns in
|
|
3
|
+
// `models`, and each type named once, apart from the models.
|
|
4
|
+
import type {
|
|
5
|
+
FunctionDefinition,
|
|
6
|
+
Output,
|
|
7
|
+
PluginCode,
|
|
8
|
+
TypeDefinition,
|
|
9
|
+
} from './definition.ts'
|
|
10
|
+
import { called } from './define-function.ts'
|
|
11
|
+
import type { Client } from './function.ts'
|
|
12
|
+
import { functionDefinitions } from './definition.ts'
|
|
13
|
+
import type { ExposedFunction } from './plugin.ts'
|
|
14
|
+
import { isObject } from './query-where.ts'
|
|
15
|
+
import type { Resolved } from './schema.ts'
|
|
16
|
+
import { checkInstalled } from './plugin-use.ts'
|
|
17
|
+
import { Refused } from './errors.ts'
|
|
18
|
+
|
|
19
|
+
/** Checks the models and types an output reaches, each type once. */
|
|
20
|
+
function checkOutput(
|
|
21
|
+
name: string,
|
|
22
|
+
output: Output,
|
|
23
|
+
resolved: Resolved,
|
|
24
|
+
types: Map<string, TypeDefinition>,
|
|
25
|
+
): void {
|
|
26
|
+
if (output.kind === 'schema') return
|
|
27
|
+
if (output.kind === 'model') {
|
|
28
|
+
const info = resolved.byValue.get(output.value)
|
|
29
|
+
if (info === undefined) {
|
|
30
|
+
throw new Refused(
|
|
31
|
+
`${name} returns ${output.name}, which is not in models.`,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
if (info.owner === undefined) return
|
|
35
|
+
throw new Refused(
|
|
36
|
+
`${name} returns ${output.name}, a model of ${info.owner}.`,
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
const { type } = output
|
|
40
|
+
const known = types.get(type.name)
|
|
41
|
+
if (known === type) return
|
|
42
|
+
if (known !== undefined) {
|
|
43
|
+
throw new Refused(`Two types are named ${type.name}; name each once.`)
|
|
44
|
+
}
|
|
45
|
+
if (resolved.models.has(type.name)) {
|
|
46
|
+
throw new Refused(`The type ${type.name} is named like a model.`)
|
|
47
|
+
}
|
|
48
|
+
types.set(type.name, type)
|
|
49
|
+
for (const field of type.fields.values()) {
|
|
50
|
+
checkOutput(name, field, resolved, types)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Functions listed by the application, or given by a plugin (D-113). */
|
|
55
|
+
export type FunctionSource = {
|
|
56
|
+
readonly plugin: string | undefined
|
|
57
|
+
readonly values: readonly unknown[]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The application's and the plugins' functions, by value, and who gives each. */
|
|
61
|
+
export type Functions = {
|
|
62
|
+
readonly definitions: ReadonlyMap<object, FunctionDefinition>
|
|
63
|
+
readonly plugins: ReadonlyMap<object, string>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const ownerOf = (plugin: string | undefined, name: string): string =>
|
|
67
|
+
plugin === undefined
|
|
68
|
+
? `the application's function ${name}`
|
|
69
|
+
: `${plugin}'s function ${name}`
|
|
70
|
+
|
|
71
|
+
/** The functions, each named once, or startup stops with a message. */
|
|
72
|
+
export function resolveFunctions(
|
|
73
|
+
sources: readonly FunctionSource[],
|
|
74
|
+
resolved: Resolved,
|
|
75
|
+
plugins: readonly PluginCode[],
|
|
76
|
+
): Functions {
|
|
77
|
+
const definitions = new Map<object, FunctionDefinition>()
|
|
78
|
+
const owners = new Map<object, string>()
|
|
79
|
+
const names = new Map<string, string>()
|
|
80
|
+
const types = new Map<string, TypeDefinition>()
|
|
81
|
+
for (const { plugin, values } of sources) {
|
|
82
|
+
const where = plugin === undefined ? 'functions' : `${plugin}.functions`
|
|
83
|
+
for (const value of values) {
|
|
84
|
+
const definition = isObject(value)
|
|
85
|
+
? functionDefinitions.get(value)
|
|
86
|
+
: undefined
|
|
87
|
+
if (definition === undefined || !isObject(value)) {
|
|
88
|
+
throw new Refused(
|
|
89
|
+
`${where} lists a value that defineFunction did not return.`,
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
const { name } = definition
|
|
93
|
+
const owner = ownerOf(plugin, name)
|
|
94
|
+
const other = names.get(name)
|
|
95
|
+
if (other !== undefined) {
|
|
96
|
+
throw new Refused(
|
|
97
|
+
other === owner
|
|
98
|
+
? `${where} lists two functions named ${name}.`
|
|
99
|
+
: `${owner} and ${other} are both named ${name}.`,
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
names.set(name, owner)
|
|
103
|
+
for (const use of definition.uses) {
|
|
104
|
+
checkInstalled(name, use.code, plugins)
|
|
105
|
+
}
|
|
106
|
+
if (definition.output !== undefined) {
|
|
107
|
+
checkOutput(name, definition.output, resolved, types)
|
|
108
|
+
}
|
|
109
|
+
definitions.set(value, definition)
|
|
110
|
+
if (plugin !== undefined) owners.set(value, plugin)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// A virtual's type is named once with the functions' (D-151).
|
|
114
|
+
for (const info of resolved.byValue.values()) {
|
|
115
|
+
for (const { name, output } of info.virtuals.values()) {
|
|
116
|
+
if (output.kind !== 'type') continue
|
|
117
|
+
checkOutput(`${info.name}.${name}`, output, resolved, types)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { definitions, plugins: owners }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The functions as the plugin `name` reads them, with its `.with` key. */
|
|
124
|
+
export function exposedFunctions(
|
|
125
|
+
name: string,
|
|
126
|
+
functions: Functions,
|
|
127
|
+
): readonly ExposedFunction[] {
|
|
128
|
+
return [...functions.definitions].map(([value, record]) => {
|
|
129
|
+
const { rules, input } = record
|
|
130
|
+
const key: unknown =
|
|
131
|
+
rules === undefined ? undefined : Reflect.get(rules, name)
|
|
132
|
+
return Object.freeze({
|
|
133
|
+
name: record.name,
|
|
134
|
+
description: record.description,
|
|
135
|
+
plugin: functions.plugins.get(value),
|
|
136
|
+
query: record.query,
|
|
137
|
+
used: record.uses.some((use) => use.code.name === name),
|
|
138
|
+
input,
|
|
139
|
+
output: record.output,
|
|
140
|
+
with: key,
|
|
141
|
+
run: async (
|
|
142
|
+
given: unknown,
|
|
143
|
+
options: unknown,
|
|
144
|
+
client?: Client,
|
|
145
|
+
): Promise<unknown> =>
|
|
146
|
+
called(
|
|
147
|
+
value,
|
|
148
|
+
record,
|
|
149
|
+
input === undefined ? [options] : [given, options],
|
|
150
|
+
client,
|
|
151
|
+
),
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
}
|