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/record.ts
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
// The record a model stores, and the data written to it.
|
|
2
|
+
import type { UniqueWhere } from './query.ts'
|
|
3
|
+
import type { fieldType, modelFields } from './symbols.ts'
|
|
4
|
+
import type { VirtualKeys } from './virtual.ts'
|
|
5
|
+
|
|
6
|
+
type Scalar<V = unknown, O = unknown> = {
|
|
7
|
+
readonly [fieldType]: {
|
|
8
|
+
readonly kind: 'scalar'
|
|
9
|
+
readonly value: V
|
|
10
|
+
readonly options: O
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
type Relation<K extends 'one' | 'many', T = unknown, O = unknown> = {
|
|
14
|
+
readonly [fieldType]: {
|
|
15
|
+
readonly kind: K
|
|
16
|
+
readonly target: T
|
|
17
|
+
readonly options: O
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
type IsOptional<O> = O extends { readonly optional: true } ? true : false
|
|
21
|
+
|
|
22
|
+
/** The fields of the model a relation's thunk returns. */
|
|
23
|
+
export type TargetFields<T> = T extends () => infer M
|
|
24
|
+
? M extends { readonly [modelFields]: infer F }
|
|
25
|
+
? F
|
|
26
|
+
: never
|
|
27
|
+
: never
|
|
28
|
+
|
|
29
|
+
/** What the kernel adds to every record (D-22). */
|
|
30
|
+
export type SystemFields = {
|
|
31
|
+
readonly id: string
|
|
32
|
+
readonly createdAt: Date
|
|
33
|
+
readonly updatedAt: Date
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A stored scalar value: an optional field may be `null`. */
|
|
37
|
+
export type StoredValue<X> =
|
|
38
|
+
X extends Scalar<infer V, infer O>
|
|
39
|
+
? IsOptional<O> extends true
|
|
40
|
+
? V | null
|
|
41
|
+
: V
|
|
42
|
+
: never
|
|
43
|
+
|
|
44
|
+
/** A record without its relations, as a query returns it without `select`. */
|
|
45
|
+
export type RecordOf<F> = SystemFields & {
|
|
46
|
+
readonly [K in keyof F as F[K] extends Scalar ? K : never]: StoredValue<F[K]>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A stored `one` relation: its key, `null` when optional and unset. */
|
|
50
|
+
type StoredRef<X> =
|
|
51
|
+
X extends Relation<'one', unknown, infer O>
|
|
52
|
+
? IsOptional<O> extends true
|
|
53
|
+
? { readonly id: string } | null
|
|
54
|
+
: { readonly id: string }
|
|
55
|
+
: never
|
|
56
|
+
|
|
57
|
+
/** The name of the model a relation's thunk returns. */
|
|
58
|
+
type TargetName<T> = T extends () => infer M
|
|
59
|
+
? M extends { readonly name: infer N extends string }
|
|
60
|
+
? N
|
|
61
|
+
: never
|
|
62
|
+
: never
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether field `J` of target `T` may face field `K` of model `N`, as startup
|
|
66
|
+
* resolves it (D-37).
|
|
67
|
+
*/
|
|
68
|
+
type Facing<N, K, T, J> =
|
|
69
|
+
TargetFields<T>[J & keyof TargetFields<T>] extends Relation<
|
|
70
|
+
'one' | 'many',
|
|
71
|
+
infer B,
|
|
72
|
+
infer O
|
|
73
|
+
>
|
|
74
|
+
? O extends { readonly inverse: infer I extends string }
|
|
75
|
+
? [I] extends [K]
|
|
76
|
+
? true
|
|
77
|
+
: false
|
|
78
|
+
: [J, TargetName<T>] extends [K, N]
|
|
79
|
+
? false
|
|
80
|
+
: [TargetName<B>] extends [N]
|
|
81
|
+
? true
|
|
82
|
+
: false
|
|
83
|
+
: false
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The field of relation `X`'s target that faces it, named `K` on model `N`:
|
|
87
|
+
* the rows it creates leave it out (D-128).
|
|
88
|
+
*/
|
|
89
|
+
type InverseOf<N, K, X> =
|
|
90
|
+
X extends Relation<'one' | 'many', infer T, infer O>
|
|
91
|
+
? O extends { readonly inverse: infer I extends string }
|
|
92
|
+
? I
|
|
93
|
+
: {
|
|
94
|
+
[J in keyof TargetFields<T>]: Facing<N, K, T, J> extends true
|
|
95
|
+
? J
|
|
96
|
+
: never
|
|
97
|
+
}[keyof TargetFields<T>]
|
|
98
|
+
: never
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Whether relation `X`, named `K` on model `N`, faces a `one`: the two
|
|
102
|
+
* sides of a one-to-one, of which the required one stores the key (D-37).
|
|
103
|
+
*/
|
|
104
|
+
type FacesOne<N, K, X> =
|
|
105
|
+
X extends Relation<'one', infer T>
|
|
106
|
+
? [InverseOf<N, K, X>] extends [never]
|
|
107
|
+
? false
|
|
108
|
+
: TargetFields<T>[InverseOf<N, K, X> &
|
|
109
|
+
keyof TargetFields<T>] extends Relation<'one'>
|
|
110
|
+
? true
|
|
111
|
+
: false
|
|
112
|
+
: false
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A `one` relation the row stores: a required one, or an optional one facing
|
|
116
|
+
* a `many`. The optional side of a one-to-one has no column (D-37).
|
|
117
|
+
*/
|
|
118
|
+
type Stored<N, K, X> =
|
|
119
|
+
X extends Relation<'one', unknown, infer O>
|
|
120
|
+
? IsOptional<O> extends true
|
|
121
|
+
? FacesOne<N, K, X> extends true
|
|
122
|
+
? false
|
|
123
|
+
: true
|
|
124
|
+
: true
|
|
125
|
+
: false
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The stored row of model `N` that access rules and hooks receive (D-77):
|
|
129
|
+
* the record plus the key of each `one` relation the row stores, with no
|
|
130
|
+
* query.
|
|
131
|
+
*/
|
|
132
|
+
export type StoredRecord<F, N extends string> = RecordOf<F> & {
|
|
133
|
+
readonly [
|
|
134
|
+
K in keyof F as Stored<N, K, F[K]> extends true ? K : never
|
|
135
|
+
]: StoredRef<F[K]>
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
type Loose = { readonly id: string; readonly [field: string]: unknown }
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The record a field rule receives (D-44): loose, or with the fields the
|
|
142
|
+
* developer declared.
|
|
143
|
+
*/
|
|
144
|
+
export type RecordFor<R extends object> = [R] extends [never]
|
|
145
|
+
? Loose
|
|
146
|
+
: { readonly id: string } & Readonly<R>
|
|
147
|
+
|
|
148
|
+
/** A written scalar value: the schema's input when one transforms (D-23). */
|
|
149
|
+
type WrittenValue<X> = X extends {
|
|
150
|
+
readonly [fieldType]: { readonly input: infer I; readonly options: infer O }
|
|
151
|
+
}
|
|
152
|
+
? IsOptional<O> extends true
|
|
153
|
+
? I | null
|
|
154
|
+
: I
|
|
155
|
+
: StoredValue<X>
|
|
156
|
+
|
|
157
|
+
type Ref = { readonly id: string }
|
|
158
|
+
/** The data of a row relation `K` of model `N` creates (D-128). */
|
|
159
|
+
type Created<N, K, X> =
|
|
160
|
+
X extends Relation<'one' | 'many', infer T>
|
|
161
|
+
? CreateData<TargetFields<T>, TargetName<T>, InverseOf<N, K, X>>
|
|
162
|
+
: never
|
|
163
|
+
/** A unique target of the relation's model, as stored (D-52, D-138). */
|
|
164
|
+
type Target<X> =
|
|
165
|
+
X extends Relation<'one' | 'many', infer T>
|
|
166
|
+
? UniqueWhere<TargetFields<T>>
|
|
167
|
+
: never
|
|
168
|
+
type OrCreate<N, K, X> = {
|
|
169
|
+
readonly where: Target<X>
|
|
170
|
+
readonly create: Created<N, K, X>
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The data of a linked row relation `K` of model `N` updates, without the
|
|
174
|
+
* relation back to the row (D-138).
|
|
175
|
+
*/
|
|
176
|
+
type Updated<N, K, X> =
|
|
177
|
+
X extends Relation<'one' | 'many', infer T>
|
|
178
|
+
? Omit<UpdateData<TargetFields<T>, TargetName<T>>, InverseOf<N, K, X>>
|
|
179
|
+
: never
|
|
180
|
+
/**
|
|
181
|
+
* A `one` is connected or created; `connectOrCreate` only where the caller
|
|
182
|
+
* writes it, as rules and hooks see it resolved (D-138).
|
|
183
|
+
*/
|
|
184
|
+
type Connect<N, K, X, Resolved extends boolean = false> =
|
|
185
|
+
| { readonly connect: Ref }
|
|
186
|
+
| { readonly create: Created<N, K, X> }
|
|
187
|
+
| (Resolved extends true
|
|
188
|
+
? never
|
|
189
|
+
: { readonly connectOrCreate: OrCreate<N, K, X> })
|
|
190
|
+
/** A relation is connected or created (D-20, D-128, D-138). */
|
|
191
|
+
type CreateInput<X, N, K> = X extends Scalar
|
|
192
|
+
? WrittenValue<X>
|
|
193
|
+
: X extends Relation<'one'>
|
|
194
|
+
? Connect<N, K, X>
|
|
195
|
+
: {
|
|
196
|
+
readonly connect?: readonly Ref[]
|
|
197
|
+
readonly create?: readonly Created<N, K, X>[]
|
|
198
|
+
readonly connectOrCreate?: readonly OrCreate<N, K, X>[]
|
|
199
|
+
}
|
|
200
|
+
/** A plain number field: no list, no JSON, no plugin field type. */
|
|
201
|
+
type IsNumber<X> = X extends {
|
|
202
|
+
readonly [fieldType]: {
|
|
203
|
+
readonly value: infer V
|
|
204
|
+
readonly input: infer I
|
|
205
|
+
readonly json: false
|
|
206
|
+
readonly stored: infer S
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
? [V, I, S] extends [number, number, never]
|
|
210
|
+
? [number] extends [V]
|
|
211
|
+
? true
|
|
212
|
+
: false
|
|
213
|
+
: false
|
|
214
|
+
: false
|
|
215
|
+
|
|
216
|
+
/** Exactly one of `T`'s keys; `undefined` leaves the field as it is. */
|
|
217
|
+
type OneOf<T> = {
|
|
218
|
+
[K in keyof T]: { readonly [P in K]: T[K] | undefined } & {
|
|
219
|
+
readonly [P in Exclude<keyof T, K>]?: never
|
|
220
|
+
}
|
|
221
|
+
}[keyof T]
|
|
222
|
+
|
|
223
|
+
/** One operation on a number, computed on the row (D-132). */
|
|
224
|
+
type NumberUpdate<V> = OneOf<{
|
|
225
|
+
readonly set: V
|
|
226
|
+
readonly increment: number
|
|
227
|
+
readonly decrement: number
|
|
228
|
+
readonly multiply: number
|
|
229
|
+
readonly divide: number
|
|
230
|
+
}>
|
|
231
|
+
|
|
232
|
+
/** One operation on a list, with an element or a list of them (D-149). */
|
|
233
|
+
type ListUpdate<E> = OneOf<{
|
|
234
|
+
readonly set: E | readonly E[]
|
|
235
|
+
readonly push: E | readonly E[]
|
|
236
|
+
readonly pushUnique: E | readonly E[]
|
|
237
|
+
readonly remove: E | readonly E[]
|
|
238
|
+
}>
|
|
239
|
+
|
|
240
|
+
/** A list's element type, `never` for any other field. */
|
|
241
|
+
type ElementOf<X> =
|
|
242
|
+
X extends Scalar<unknown, { readonly list: true }>
|
|
243
|
+
? WrittenValue<X> extends readonly (infer E)[]
|
|
244
|
+
? E
|
|
245
|
+
: never
|
|
246
|
+
: never
|
|
247
|
+
|
|
248
|
+
/** A `one` the caller writes; rules and hooks see it resolved (D-138). */
|
|
249
|
+
type OneUpdate<N, K, X, O, Operations extends boolean> =
|
|
250
|
+
| Connect<N, K, X, Not<Operations>>
|
|
251
|
+
| { readonly update: Updated<N, K, X> }
|
|
252
|
+
| (IsOptional<O> extends true
|
|
253
|
+
? { readonly disconnect: true } | { readonly delete: true }
|
|
254
|
+
: never)
|
|
255
|
+
/** A `many` the caller writes; `set` reaches rules and hooks resolved. */
|
|
256
|
+
type ManyUpdate<N, K, X, Operations extends boolean> = {
|
|
257
|
+
readonly connect?: readonly Ref[]
|
|
258
|
+
readonly disconnect?: readonly Ref[]
|
|
259
|
+
readonly create?: readonly Created<N, K, X>[]
|
|
260
|
+
readonly update?: readonly {
|
|
261
|
+
readonly where: Target<X>
|
|
262
|
+
readonly data: Updated<N, K, X>
|
|
263
|
+
}[]
|
|
264
|
+
readonly delete?: readonly Target<X>[]
|
|
265
|
+
} & (Operations extends true
|
|
266
|
+
? {
|
|
267
|
+
readonly set?: readonly Ref[]
|
|
268
|
+
readonly connectOrCreate?: readonly OrCreate<N, K, X>[]
|
|
269
|
+
}
|
|
270
|
+
: unknown)
|
|
271
|
+
type Not<B extends boolean> = B extends true ? false : true
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* An optional `one` relation is unset with `disconnect: true` (D-20); with
|
|
275
|
+
* `Operations`, a number field also takes an operation (D-132), a list one
|
|
276
|
+
* (D-149), and a relation `set` and `connectOrCreate` (D-138).
|
|
277
|
+
*/
|
|
278
|
+
type UpdateInput<X, N, K, Operations extends boolean> = X extends Scalar
|
|
279
|
+
? Operations extends true
|
|
280
|
+
? IsNumber<X> extends true
|
|
281
|
+
? WrittenValue<X> | NumberUpdate<WrittenValue<X>>
|
|
282
|
+
: [ElementOf<X>] extends [never]
|
|
283
|
+
? WrittenValue<X>
|
|
284
|
+
: WrittenValue<X> | ListUpdate<ElementOf<X>>
|
|
285
|
+
: WrittenValue<X>
|
|
286
|
+
: X extends Relation<'one', unknown, infer O>
|
|
287
|
+
? OneUpdate<N, K, X, O, Operations>
|
|
288
|
+
: ManyUpdate<N, K, X, Operations>
|
|
289
|
+
// A list left unwritten is empty (D-120).
|
|
290
|
+
type Needed<X> =
|
|
291
|
+
X extends Scalar<unknown, infer O>
|
|
292
|
+
? IsOptional<O> extends true
|
|
293
|
+
? false
|
|
294
|
+
: O extends { readonly default: unknown } | { readonly list: true }
|
|
295
|
+
? false
|
|
296
|
+
: true
|
|
297
|
+
: X extends Relation<'one', unknown, infer O>
|
|
298
|
+
? IsOptional<O> extends true
|
|
299
|
+
? false
|
|
300
|
+
: O extends { readonly default: unknown }
|
|
301
|
+
? false
|
|
302
|
+
: true
|
|
303
|
+
: false
|
|
304
|
+
|
|
305
|
+
/** `V`'s fields but `E`, each required when `F` needs it to create a row. */
|
|
306
|
+
type Creating<F, E, V extends { readonly [K in keyof F]: unknown }> = {
|
|
307
|
+
readonly [
|
|
308
|
+
K in keyof F as K extends E ? never : Needed<F[K]> extends true ? K : never
|
|
309
|
+
]: V[K]
|
|
310
|
+
} & {
|
|
311
|
+
readonly [
|
|
312
|
+
K in keyof F as K extends E ? never : Needed<F[K]> extends true ? never : K
|
|
313
|
+
]?: V[K] | undefined
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The data that creates a row of model `N`, without its views (D-151) nor
|
|
318
|
+
* field `E`: the relation back to the row that creates it (D-128). Code that does not know `N`
|
|
319
|
+
* leaves that relation to the kernel.
|
|
320
|
+
*/
|
|
321
|
+
export type CreateData<F, N = never, E = never> = Creating<
|
|
322
|
+
F,
|
|
323
|
+
E | Unwritten<F>,
|
|
324
|
+
{ readonly [K in keyof F]: CreateInput<F[K], N, K> }
|
|
325
|
+
>
|
|
326
|
+
|
|
327
|
+
/** A row `createMany` creates: a `one` is connected, a `many` not written (D-135). */
|
|
328
|
+
export type CreateManyData<F> = Creating<
|
|
329
|
+
F,
|
|
330
|
+
| { [K in keyof F]: F[K] extends Relation<'many'> ? K : never }[keyof F]
|
|
331
|
+
| VirtualKeys<F>,
|
|
332
|
+
{
|
|
333
|
+
readonly [K in keyof F]: F[K] extends Scalar
|
|
334
|
+
? WrittenValue<F[K]>
|
|
335
|
+
: { readonly connect: Ref }
|
|
336
|
+
}
|
|
337
|
+
>
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* `undefined` leaves a field unchanged, as in Prisma (D-84). Rules and hooks
|
|
341
|
+
* see the data without `Operations`: each number and list as computed
|
|
342
|
+
* (D-132, D-149), each relation resolved (D-138).
|
|
343
|
+
*/
|
|
344
|
+
export type UpdateData<F, N = never, Operations extends boolean = true> = {
|
|
345
|
+
readonly [K in keyof F as K extends Unwritten<F> ? never : K]?:
|
|
346
|
+
UpdateInput<F[K], N, K, Operations> | undefined
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** The views and virtuals of `F`, which no data writes (D-151). */
|
|
350
|
+
type Unwritten<F> = Views<F> | VirtualKeys<F>
|
|
351
|
+
type Views<F> = {
|
|
352
|
+
[K in keyof F]: F[K] extends Relation<
|
|
353
|
+
'many',
|
|
354
|
+
unknown,
|
|
355
|
+
{ readonly from: string }
|
|
356
|
+
>
|
|
357
|
+
? K
|
|
358
|
+
: never
|
|
359
|
+
}[keyof F]
|
|
360
|
+
|
|
361
|
+
/** The data `updateMany` writes on each row: scalars only (D-133). */
|
|
362
|
+
export type UpdateManyData<F> = Pick<
|
|
363
|
+
UpdateData<F>,
|
|
364
|
+
ScalarKeys<F> & keyof UpdateData<F>
|
|
365
|
+
>
|
|
366
|
+
|
|
367
|
+
export type ScalarKeys<F> = {
|
|
368
|
+
[K in keyof F]: F[K] extends Scalar ? K : never
|
|
369
|
+
}[keyof F]
|
|
370
|
+
|
|
371
|
+
export type UniqueKeys<F> = {
|
|
372
|
+
[K in keyof F]: F[K] extends Scalar<unknown, { readonly unique: true }>
|
|
373
|
+
? K
|
|
374
|
+
: never
|
|
375
|
+
}[keyof F]
|
|
376
|
+
|
|
377
|
+
export type RelationKeys<F> = {
|
|
378
|
+
[K in keyof F]: F[K] extends Relation<'one' | 'many'> ? K : never
|
|
379
|
+
}[keyof F]
|
package/src/recorder.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// The reads a caller's construction makes (D-154): `identify`, the plugins'
|
|
2
|
+
// `caller` and `beforeFind` run inside `recording`, which keeps what each
|
|
3
|
+
// read reached, so a write to one of them rebuilds the caller, and the
|
|
4
|
+
// earliest time a plugin said the caller stops holding.
|
|
5
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
6
|
+
import type {
|
|
7
|
+
AggregateRead,
|
|
8
|
+
CountRead,
|
|
9
|
+
Filter,
|
|
10
|
+
GroupKey,
|
|
11
|
+
Nested,
|
|
12
|
+
Read,
|
|
13
|
+
Row,
|
|
14
|
+
Step,
|
|
15
|
+
} from './adapter.ts'
|
|
16
|
+
import type { Models } from './query-where.ts'
|
|
17
|
+
import { modelOf } from './query-where.ts'
|
|
18
|
+
|
|
19
|
+
/** What one read reached. */
|
|
20
|
+
export type Recorded = {
|
|
21
|
+
readonly model: string
|
|
22
|
+
/** Absent, the read reached every row of the model. */
|
|
23
|
+
readonly where?: Filter
|
|
24
|
+
/** The rows it found; absent for a count, or rows it did not list. */
|
|
25
|
+
readonly ids?: readonly string[]
|
|
26
|
+
/** The relation, as `Model.field`, it followed from another read's rows. */
|
|
27
|
+
readonly relation?: string
|
|
28
|
+
/** The fields it returned or aggregated; absent, every field. */
|
|
29
|
+
readonly fields?: readonly string[]
|
|
30
|
+
/**
|
|
31
|
+
* The fields that chose and ordered its rows: its filter's, sort's and
|
|
32
|
+
* keys'; absent, it may depend on any field.
|
|
33
|
+
*/
|
|
34
|
+
readonly filters?: readonly string[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type Recording = { readonly reads: Recorded[]; expires: number | undefined }
|
|
38
|
+
|
|
39
|
+
const recorder = new AsyncLocalStorage<Recording>()
|
|
40
|
+
|
|
41
|
+
/** Runs `run`, keeping each read the kernel makes until it settles. */
|
|
42
|
+
export async function recording<R>(run: () => Promise<R>): Promise<{
|
|
43
|
+
readonly result: R
|
|
44
|
+
readonly reads: readonly Recorded[]
|
|
45
|
+
readonly expires?: Date
|
|
46
|
+
}> {
|
|
47
|
+
const kept: Recording = { reads: [], expires: undefined }
|
|
48
|
+
const result = await recorder.run(kept, run)
|
|
49
|
+
const { reads, expires } = kept
|
|
50
|
+
return {
|
|
51
|
+
result,
|
|
52
|
+
reads: [...reads],
|
|
53
|
+
...(expires !== undefined && { expires: new Date(expires) }),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Keeps `at` when it is the earliest expiry, if a recording runs. */
|
|
58
|
+
export function recordExpiry(at: Date): void {
|
|
59
|
+
const time = at.getTime()
|
|
60
|
+
if (Number.isNaN(time)) throw new TypeError('expires takes a valid date.')
|
|
61
|
+
const kept = recorder.getStore()
|
|
62
|
+
if (kept === undefined) return
|
|
63
|
+
if (kept.expires === undefined || time < kept.expires) kept.expires = time
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The ids of `rows`, each once: included rows repeat a shared one. */
|
|
67
|
+
const idsOf = (rows: readonly unknown[]): string[] => [
|
|
68
|
+
...new Set(
|
|
69
|
+
rows.flatMap((row) => {
|
|
70
|
+
const id: unknown = Reflect.get(Object(row), 'id')
|
|
71
|
+
return typeof id === 'string' ? [id] : []
|
|
72
|
+
}),
|
|
73
|
+
),
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
/** Adds the fields `where` tests on its rows; false when it tests a text search. */
|
|
77
|
+
function filtered(where: Filter | undefined, into: Set<string>): boolean {
|
|
78
|
+
if (where === undefined) return true
|
|
79
|
+
for (const key of Object.keys(where)) {
|
|
80
|
+
if (key === '_search') return false
|
|
81
|
+
if (key !== 'AND' && key !== 'OR' && key !== 'NOT') into.add(key)
|
|
82
|
+
}
|
|
83
|
+
const nested = [
|
|
84
|
+
...(where.AND ?? []),
|
|
85
|
+
...(where.OR ?? []),
|
|
86
|
+
...(where.NOT ?? []),
|
|
87
|
+
]
|
|
88
|
+
return nested.every((filter) => filtered(filter, into))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A sort's or a key's field on the row: the relation it goes through first. */
|
|
92
|
+
const ownField = (key: GroupKey): string =>
|
|
93
|
+
key.through?.[0]?.relation ?? key.field
|
|
94
|
+
|
|
95
|
+
/** The fields a read returned and the ones that chose its rows. */
|
|
96
|
+
function dependsOn(
|
|
97
|
+
read: Nested & { readonly distinct?: readonly GroupKey[] },
|
|
98
|
+
): Pick<Recorded, 'fields' | 'filters'> {
|
|
99
|
+
const filters = new Set<string>()
|
|
100
|
+
// A sort by relevance comes with a text search, which `filtered` refuses.
|
|
101
|
+
const known = filtered(read.where, filters) && filtered(read.cursor, filters)
|
|
102
|
+
for (const order of read.orderBy ?? []) filters.add(ownField(order))
|
|
103
|
+
for (const key of read.distinct ?? []) filters.add(ownField(key))
|
|
104
|
+
filters.delete('id')
|
|
105
|
+
return {
|
|
106
|
+
...(read.fields && { fields: read.fields }),
|
|
107
|
+
...(known && { filters: [...filters] }),
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Keeps the relation `name` of `model` follows, and returns its target. */
|
|
112
|
+
function follow(
|
|
113
|
+
models: Models,
|
|
114
|
+
reads: Recorded[],
|
|
115
|
+
model: string,
|
|
116
|
+
name: string,
|
|
117
|
+
reached: Omit<Recorded, 'model' | 'relation'> = {},
|
|
118
|
+
): string | undefined {
|
|
119
|
+
const field = modelOf(models, model).fields.get(name)
|
|
120
|
+
if (field === undefined || field.kind === 'scalar') return undefined
|
|
121
|
+
reads.push({ model: field.target, ...reached, relation: `${model}.${name}` })
|
|
122
|
+
return field.target
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Keeps the `one` relations `steps` go through from `model`. */
|
|
126
|
+
function through(
|
|
127
|
+
models: Models,
|
|
128
|
+
reads: Recorded[],
|
|
129
|
+
model: string,
|
|
130
|
+
steps: readonly Step[] = [],
|
|
131
|
+
): string | undefined {
|
|
132
|
+
let on: string | undefined = model
|
|
133
|
+
for (const { relation, where } of steps) {
|
|
134
|
+
if (on === undefined) return undefined
|
|
135
|
+
on = follow(models, reads, on, relation, where && { where })
|
|
136
|
+
}
|
|
137
|
+
return on
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The relations `read` follows from `model`'s rows found as `rows`. */
|
|
141
|
+
function followed(
|
|
142
|
+
models: Models,
|
|
143
|
+
reads: Recorded[],
|
|
144
|
+
model: string,
|
|
145
|
+
read: Nested,
|
|
146
|
+
rows: readonly unknown[],
|
|
147
|
+
): void {
|
|
148
|
+
for (const order of read.orderBy ?? []) {
|
|
149
|
+
const on = through(models, reads, model, order.through)
|
|
150
|
+
if (on !== undefined && order.count !== undefined) {
|
|
151
|
+
follow(models, reads, on, order.field, { where: order.count })
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
for (const [name, where] of Object.entries(read.count ?? {})) {
|
|
155
|
+
follow(models, reads, model, name, { where })
|
|
156
|
+
}
|
|
157
|
+
for (const [name, nested] of Object.entries(read.include ?? {})) {
|
|
158
|
+
const found = rows.flatMap((row): unknown[] => {
|
|
159
|
+
const value: unknown = Reflect.get(Object(row), name)
|
|
160
|
+
if (Array.isArray(value)) return value
|
|
161
|
+
return value === null || value === undefined ? [] : [value]
|
|
162
|
+
})
|
|
163
|
+
const target = follow(models, reads, model, name, {
|
|
164
|
+
...(nested.where && { where: nested.where }),
|
|
165
|
+
ids: idsOf(found),
|
|
166
|
+
...dependsOn(nested),
|
|
167
|
+
})
|
|
168
|
+
if (target !== undefined) followed(models, reads, target, nested, found)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Keeps a find, if a recording runs. */
|
|
173
|
+
export function recordFind(
|
|
174
|
+
models: Models,
|
|
175
|
+
read: Read,
|
|
176
|
+
rows: readonly Row[],
|
|
177
|
+
): void {
|
|
178
|
+
const reads = recorder.getStore()?.reads
|
|
179
|
+
if (reads === undefined) return
|
|
180
|
+
const { model, where } = read
|
|
181
|
+
reads.push({
|
|
182
|
+
model,
|
|
183
|
+
...(where && { where }),
|
|
184
|
+
ids: idsOf(rows),
|
|
185
|
+
...dependsOn(read),
|
|
186
|
+
})
|
|
187
|
+
followed(models, reads, model, read, rows)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Keeps a count or an aggregate, if a recording runs. */
|
|
191
|
+
export function recordCount(
|
|
192
|
+
models: Models,
|
|
193
|
+
read: CountRead | AggregateRead,
|
|
194
|
+
): void {
|
|
195
|
+
const reads = recorder.getStore()?.reads
|
|
196
|
+
if (reads === undefined) return
|
|
197
|
+
const { model, where } = read
|
|
198
|
+
const filters = new Set<string>()
|
|
199
|
+
const known = filtered(where, filters)
|
|
200
|
+
const by = 'by' in read ? (read.by ?? []) : []
|
|
201
|
+
for (const key of by) filters.add(ownField(key))
|
|
202
|
+
const aggregates = 'aggregates' in read ? read.aggregates : []
|
|
203
|
+
const fields = aggregates.flatMap(({ field }) => field ?? [])
|
|
204
|
+
filters.delete('id')
|
|
205
|
+
reads.push({
|
|
206
|
+
model,
|
|
207
|
+
...(where && { where }),
|
|
208
|
+
fields: [...new Set(fields)],
|
|
209
|
+
...(known && { filters: [...filters] }),
|
|
210
|
+
})
|
|
211
|
+
for (const key of by) through(models, reads, model, key.through)
|
|
212
|
+
}
|