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
package/src/writes.ts
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// `create`, `update` and `delete` in the order of D-67: access rules on the
|
|
2
|
+
// data sent, defaults, validation, `before*` hooks, the required check, the
|
|
3
|
+
// write, which a plugin's `redirect` may replace (D-89), `after*` hooks; the
|
|
4
|
+
// `*Commit` hooks once it commits (D-71, D-88). The row a write decides from
|
|
5
|
+
// is locked; a write that loads none answers from the rows it changed (D-99,
|
|
6
|
+
// D-104).
|
|
7
|
+
import { and, recordOf } from './access.ts'
|
|
8
|
+
import type { Filter, Nested, Row } from './adapter.ts'
|
|
9
|
+
import { cascadeWhere } from './cascade.ts'
|
|
10
|
+
import { deleted } from './delete-many.ts'
|
|
11
|
+
import { PublicError } from './errors.ts'
|
|
12
|
+
import type { Event } from './hooks.ts'
|
|
13
|
+
import { redirectOf, rereads, runHook, settle, settles } from './hooks.ts'
|
|
14
|
+
import type { Context } from './query-context.ts'
|
|
15
|
+
import { first, given, hiddenBy, picked } from './query-context.ts'
|
|
16
|
+
import { project } from './query-project.ts'
|
|
17
|
+
import { computeVirtuals } from './virtual-read.ts'
|
|
18
|
+
import type { Shape } from './query-args.ts'
|
|
19
|
+
import { oneKeys, plan, uniqueWhere } from './query-args.ts'
|
|
20
|
+
import type { Operations } from './number-update.ts'
|
|
21
|
+
import { computeOn, operationsOf, inRange, validates } from './number-update.ts'
|
|
22
|
+
import { nests, resolveData } from './nested-write.ts'
|
|
23
|
+
import { createPlan, updatePlan } from './query-write.ts'
|
|
24
|
+
import { planned } from './scope.ts'
|
|
25
|
+
import type { Commit, Store } from './transaction.ts'
|
|
26
|
+
import type { Draft } from './write-data.ts'
|
|
27
|
+
import { prepared, readData } from './write-data.ts'
|
|
28
|
+
import type { Checked, Plan } from './write-plan.ts'
|
|
29
|
+
import { updated } from './write-plan.ts'
|
|
30
|
+
import { uuidv7 } from './uuid.ts'
|
|
31
|
+
import { guarded, target } from './write-target.ts'
|
|
32
|
+
import { writingOf } from './writing.ts'
|
|
33
|
+
import { byId } from './query-where.ts'
|
|
34
|
+
|
|
35
|
+
/** The shape a single-row write returns, and each row of a bulk one's (D-149). */
|
|
36
|
+
export async function returned(
|
|
37
|
+
context: Context,
|
|
38
|
+
found: ReadonlyMap<string, unknown>,
|
|
39
|
+
): Promise<{ readonly read: Nested; readonly shape: Shape }> {
|
|
40
|
+
const { models, model, scope } = context
|
|
41
|
+
return planned(scope, () =>
|
|
42
|
+
plan(models, model, picked(found), '', 'one', oneKeys, scope),
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Runs the plan, then the `after*` hooks. The written row comes back even
|
|
48
|
+
* when a read rule or `beforeFind` would hide it (D-86, D-88).
|
|
49
|
+
*/
|
|
50
|
+
async function written(
|
|
51
|
+
context: Context,
|
|
52
|
+
store: Store,
|
|
53
|
+
commits: Commit[],
|
|
54
|
+
steps: Plan,
|
|
55
|
+
events: Event[],
|
|
56
|
+
at: { readonly id: string; readonly read: Nested; readonly shape: Shape },
|
|
57
|
+
checks: Checked[] = [],
|
|
58
|
+
): Promise<unknown> {
|
|
59
|
+
const { model, scope } = context
|
|
60
|
+
const writing = writingOf(scope, store, events, checks)
|
|
61
|
+
// The rows it creates, after the row written, before the rows it moves.
|
|
62
|
+
events.push(...steps.events)
|
|
63
|
+
await steps.check(writing)
|
|
64
|
+
const changes = await steps.changes(writing)
|
|
65
|
+
const read = { model: model.name, ...at.read, where: byId(at.id) }
|
|
66
|
+
const again = rereads(events)
|
|
67
|
+
const write = {
|
|
68
|
+
checks: checks.map(({ check }) => check),
|
|
69
|
+
changes,
|
|
70
|
+
...(again ? {} : { read }),
|
|
71
|
+
}
|
|
72
|
+
// Nothing follows a write without hooks: it may need no transaction.
|
|
73
|
+
const run = settles(events) ? store.write(write) : store.last(write)
|
|
74
|
+
const rows = await guarded(run, checks)
|
|
75
|
+
await settle(store, scope.caller, events, commits)
|
|
76
|
+
// An `after*` hook may have changed the rows: read them again (D-88).
|
|
77
|
+
const result = again ? await store.find(read) : rows
|
|
78
|
+
return first(context, store, result, at.shape, scope.access)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Creates the row of `draft` as the rules allow. */
|
|
82
|
+
async function creating(
|
|
83
|
+
context: Context,
|
|
84
|
+
store: Store,
|
|
85
|
+
commits: Commit[],
|
|
86
|
+
draft: Draft,
|
|
87
|
+
at: { readonly read: Nested; readonly shape: Shape },
|
|
88
|
+
): Promise<unknown> {
|
|
89
|
+
const { model, caller, scope } = context
|
|
90
|
+
const { read, shape } = at
|
|
91
|
+
const made = uuidv7()
|
|
92
|
+
const reach = { scope, reads: store, model, id: made, at: [] }
|
|
93
|
+
await resolveData({ ...reach, creating: true, trusted: false }, draft)
|
|
94
|
+
const sent: Readonly<Draft> = { ...draft }
|
|
95
|
+
scope.access?.allow(model, 'create', { data: sent })
|
|
96
|
+
await prepared(model, draft, caller, true)
|
|
97
|
+
await runHook(model, 'beforeCreate', { caller, data: draft })
|
|
98
|
+
const { id, plan: steps } = await createPlan(
|
|
99
|
+
scope,
|
|
100
|
+
model,
|
|
101
|
+
draft,
|
|
102
|
+
sent,
|
|
103
|
+
store,
|
|
104
|
+
made,
|
|
105
|
+
)
|
|
106
|
+
const events: Event[] = [{ model, operation: 'create', id }]
|
|
107
|
+
return written(context, store, commits, steps, events, { id, read, shape })
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function create(
|
|
111
|
+
context: Context,
|
|
112
|
+
store: Store,
|
|
113
|
+
commits: Commit[],
|
|
114
|
+
args: unknown,
|
|
115
|
+
): Promise<unknown> {
|
|
116
|
+
const { models, model } = context
|
|
117
|
+
const found = given(args, model.name, ['data', ...oneKeys], ['data'])
|
|
118
|
+
const at = await returned(context, found)
|
|
119
|
+
const draft = readData(models, model, found.get('data'), true)
|
|
120
|
+
return creating(context, store, commits, draft, at)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The rows a single-row write may target: the caller reads them (D-72), and
|
|
125
|
+
* the `beforeFind` hooks let it `operation` them (D-124).
|
|
126
|
+
*/
|
|
127
|
+
async function targets(
|
|
128
|
+
context: Context,
|
|
129
|
+
found: ReadonlyMap<string, unknown>,
|
|
130
|
+
operation: 'update' | 'delete',
|
|
131
|
+
): Promise<Filter> {
|
|
132
|
+
const { models, model, scope } = context
|
|
133
|
+
return planned(scope, () =>
|
|
134
|
+
and(
|
|
135
|
+
uniqueWhere(models, model, found.get('where'), 'where', scope),
|
|
136
|
+
scope.readable(model.name, operation),
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const missing = (model: string): string => `No ${model} matches.`
|
|
142
|
+
|
|
143
|
+
/** The `id` a unique `where` gives, if any. */
|
|
144
|
+
function idOf(where: unknown): string | undefined {
|
|
145
|
+
if (typeof where !== 'object' || where === null || !('id' in where)) {
|
|
146
|
+
return undefined
|
|
147
|
+
}
|
|
148
|
+
return typeof where.id === 'string' ? where.id : undefined
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Updates the locked `row` as the rules allow; its number and list updates
|
|
153
|
+
* are computed on it first, so the rules and hooks see the results (D-132,
|
|
154
|
+
* D-149).
|
|
155
|
+
*/
|
|
156
|
+
async function updating(
|
|
157
|
+
context: Context,
|
|
158
|
+
store: Store,
|
|
159
|
+
commits: Commit[],
|
|
160
|
+
data: { readonly draft: Draft; readonly operations: Operations },
|
|
161
|
+
at: { readonly row: Row; readonly read: Nested; readonly shape: Shape },
|
|
162
|
+
): Promise<unknown> {
|
|
163
|
+
const { model, caller, scope } = context
|
|
164
|
+
const { row, read, shape } = at
|
|
165
|
+
const { draft, operations } = data
|
|
166
|
+
computeOn(model, row, draft, operations)
|
|
167
|
+
const reach = { scope, reads: store, model, id: row.id, at: [] }
|
|
168
|
+
await resolveData({ ...reach, creating: false, trusted: false }, draft)
|
|
169
|
+
const sent: Readonly<Draft> = { ...draft }
|
|
170
|
+
scope.access?.allow(model, 'update', { record: row, data: sent })
|
|
171
|
+
await prepared(model, draft, caller, false)
|
|
172
|
+
const record = recordOf(model, row)
|
|
173
|
+
await runHook(model, 'beforeUpdate', { caller, record, data: draft })
|
|
174
|
+
const steps = await updatePlan(scope, model, row.id, draft, sent, store)
|
|
175
|
+
const events: Event[] = [
|
|
176
|
+
{ model, operation: 'update', id: row.id, before: row },
|
|
177
|
+
]
|
|
178
|
+
const to = { id: row.id, read, shape }
|
|
179
|
+
return written(context, store, commits, steps, events, to)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export async function update(
|
|
183
|
+
context: Context,
|
|
184
|
+
store: Store,
|
|
185
|
+
commits: Commit[],
|
|
186
|
+
args: unknown,
|
|
187
|
+
): Promise<unknown> {
|
|
188
|
+
const { models, model, caller, scope } = context
|
|
189
|
+
const found = given(
|
|
190
|
+
args,
|
|
191
|
+
model.name,
|
|
192
|
+
['where', 'data', ...oneKeys],
|
|
193
|
+
['where', 'data'],
|
|
194
|
+
)
|
|
195
|
+
const { read, shape } = await returned(context, found)
|
|
196
|
+
// The data's shape is checked whether or not the row is found.
|
|
197
|
+
const draft = readData(models, model, found.get('data'), false)
|
|
198
|
+
const operations = operationsOf(model, draft)
|
|
199
|
+
const id = idOf(found.get('where'))
|
|
200
|
+
const where = await targets(context, found, 'update')
|
|
201
|
+
if (
|
|
202
|
+
scope.access === undefined &&
|
|
203
|
+
id !== undefined &&
|
|
204
|
+
!updated(model) &&
|
|
205
|
+
!nests(model, draft) &&
|
|
206
|
+
!validates(model, operations)
|
|
207
|
+
) {
|
|
208
|
+
// Nothing needs the stored row: a check stands for its read (D-99),
|
|
209
|
+
// and the write computes its numbers and lists (D-132, D-149).
|
|
210
|
+
const sent: Readonly<Draft> = { ...draft }
|
|
211
|
+
await prepared(model, draft, caller, false)
|
|
212
|
+
const steps = await updatePlan(
|
|
213
|
+
scope,
|
|
214
|
+
model,
|
|
215
|
+
id,
|
|
216
|
+
draft,
|
|
217
|
+
sent,
|
|
218
|
+
store,
|
|
219
|
+
operations,
|
|
220
|
+
)
|
|
221
|
+
const check = { model: model.name, where, count: 1, lock: true }
|
|
222
|
+
const message = missing(model.name)
|
|
223
|
+
const at = { id, read, shape }
|
|
224
|
+
const checks = [{ check, message }]
|
|
225
|
+
const result = await inRange(
|
|
226
|
+
model,
|
|
227
|
+
written(context, store, commits, steps, [], at, checks),
|
|
228
|
+
)
|
|
229
|
+
// The row went between the write's check and its change (D-104).
|
|
230
|
+
if (result === null) throw new PublicError('NOT_FOUND', message)
|
|
231
|
+
return result
|
|
232
|
+
}
|
|
233
|
+
const row = await target(context, store, where)
|
|
234
|
+
const data = { draft, operations }
|
|
235
|
+
return updating(context, store, commits, data, { row, read, shape })
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export async function remove(
|
|
239
|
+
context: Context,
|
|
240
|
+
store: Store,
|
|
241
|
+
commits: Commit[],
|
|
242
|
+
args: unknown,
|
|
243
|
+
): Promise<unknown> {
|
|
244
|
+
const { models, model, scope } = context
|
|
245
|
+
const found = given(args, model.name, ['where', ...oneKeys], ['where'])
|
|
246
|
+
const { read, shape } = await returned(context, found)
|
|
247
|
+
const where = await targets(context, found, 'delete')
|
|
248
|
+
// Its relations, counts and virtuals, as the caller reads them, before
|
|
249
|
+
// the delete removes any: one read in its transaction (D-135, D-151).
|
|
250
|
+
const reaches =
|
|
251
|
+
shape.relations.size > 0 ||
|
|
252
|
+
shape.counts.length > 0 ||
|
|
253
|
+
shape.virtuals.size > 0
|
|
254
|
+
const shaped = async (by: Filter, lock: boolean) => {
|
|
255
|
+
if (!reaches) return undefined
|
|
256
|
+
const rows = await store.find({
|
|
257
|
+
model: model.name,
|
|
258
|
+
...read,
|
|
259
|
+
where: by,
|
|
260
|
+
take: 1,
|
|
261
|
+
lock,
|
|
262
|
+
})
|
|
263
|
+
return rows[0]
|
|
264
|
+
}
|
|
265
|
+
const reach = cascadeWhere(models, model, where)
|
|
266
|
+
if (reach !== undefined && scope.access === undefined) {
|
|
267
|
+
const kept = await shaped(where, true)
|
|
268
|
+
const computed = await computeVirtuals(
|
|
269
|
+
context,
|
|
270
|
+
store,
|
|
271
|
+
kept === undefined ? [] : [kept],
|
|
272
|
+
shape,
|
|
273
|
+
undefined,
|
|
274
|
+
)
|
|
275
|
+
// Nothing needs the stored row: a check stands for its read, and the
|
|
276
|
+
// write returns it as its delete removed it (D-102, D-104).
|
|
277
|
+
const check = { model: model.name, where, count: 1, lock: true }
|
|
278
|
+
const checks: Checked[] = [{ check, message: missing(model.name) }]
|
|
279
|
+
checks.push(...reach.checks)
|
|
280
|
+
const rows = await guarded(
|
|
281
|
+
store.last({
|
|
282
|
+
checks: checks.map((each) => each.check),
|
|
283
|
+
changes: reach.changes,
|
|
284
|
+
read: { model: model.name, where, take: 1 },
|
|
285
|
+
before: true,
|
|
286
|
+
}),
|
|
287
|
+
checks,
|
|
288
|
+
)
|
|
289
|
+
// Another transaction removed it meanwhile (D-104).
|
|
290
|
+
if (rows.length === 0)
|
|
291
|
+
throw new PublicError('NOT_FOUND', missing(model.name))
|
|
292
|
+
return project(kept ?? rows[0], shape, undefined, computed)
|
|
293
|
+
}
|
|
294
|
+
const row = await target(context, store, where)
|
|
295
|
+
scope.access?.allow(model, 'delete', { record: row })
|
|
296
|
+
const redirected = redirectOf(model) !== undefined
|
|
297
|
+
// A redirected row stays: it is read as the redirect left it (D-89).
|
|
298
|
+
const kept = redirected ? undefined : await shaped(byId(row.id), false)
|
|
299
|
+
const hidden = hiddenBy(scope.access)
|
|
300
|
+
const before = redirected
|
|
301
|
+
? undefined
|
|
302
|
+
: await computeVirtuals(context, store, [kept ?? row], shape, hidden)
|
|
303
|
+
await deleted(context, store, commits, [row])
|
|
304
|
+
// The row as the redirect left it, or as it was once gone (D-89).
|
|
305
|
+
const [left] = redirected
|
|
306
|
+
? await store.find({
|
|
307
|
+
model: model.name,
|
|
308
|
+
...read,
|
|
309
|
+
where: byId(row.id),
|
|
310
|
+
take: 1,
|
|
311
|
+
})
|
|
312
|
+
: [kept]
|
|
313
|
+
const result = left ?? row
|
|
314
|
+
const computed =
|
|
315
|
+
before ?? (await computeVirtuals(context, store, [result], shape, hidden))
|
|
316
|
+
return project(result, shape, hidden, computed)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** The row an upsert returns, and whether it created it (D-142). */
|
|
320
|
+
export type Upserted = { readonly row: unknown; readonly created: boolean }
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Updates the row `where` targets, or creates it when the caller finds
|
|
324
|
+
* none: the branch that runs is a plain update or create (D-142).
|
|
325
|
+
*/
|
|
326
|
+
export async function upsert(
|
|
327
|
+
context: Context,
|
|
328
|
+
store: Store,
|
|
329
|
+
commits: Commit[],
|
|
330
|
+
args: unknown,
|
|
331
|
+
): Promise<Upserted> {
|
|
332
|
+
const { models, model } = context
|
|
333
|
+
const found = given(
|
|
334
|
+
args,
|
|
335
|
+
model.name,
|
|
336
|
+
['where', 'create', 'update', ...oneKeys],
|
|
337
|
+
['where', 'create', 'update'],
|
|
338
|
+
)
|
|
339
|
+
const at = await returned(context, found)
|
|
340
|
+
// Both data are checked, whichever runs.
|
|
341
|
+
const creation = readData(models, model, found.get('create'), true)
|
|
342
|
+
const draft = readData(models, model, found.get('update'), false)
|
|
343
|
+
const operations = operationsOf(model, draft)
|
|
344
|
+
const where = await targets(context, found, 'update')
|
|
345
|
+
const [row] = await store.find({
|
|
346
|
+
model: model.name,
|
|
347
|
+
where,
|
|
348
|
+
take: 1,
|
|
349
|
+
lock: true,
|
|
350
|
+
})
|
|
351
|
+
// Another create of the row meanwhile fails on its unique index.
|
|
352
|
+
if (row === undefined) {
|
|
353
|
+
return {
|
|
354
|
+
row: await creating(context, store, commits, creation, at),
|
|
355
|
+
created: true,
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const data = { draft, operations }
|
|
359
|
+
const changed = await updating(context, store, commits, data, { row, ...at })
|
|
360
|
+
return { row: changed, created: false }
|
|
361
|
+
}
|
package/src/writing.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// What one write's changes are made in (write-plan.ts): the rows a move's
|
|
2
|
+
// hooks update planned again, and deletes run with their cascade and hooks
|
|
3
|
+
// (D-88, D-90, D-138).
|
|
4
|
+
import { recordOf } from './access.ts'
|
|
5
|
+
import type { Change } from './adapter.ts'
|
|
6
|
+
import type { Cascade } from './cascade.ts'
|
|
7
|
+
import { cascadeOf } from './cascade.ts'
|
|
8
|
+
import { InvalidQuery } from './errors.ts'
|
|
9
|
+
import type { Event } from './hooks.ts'
|
|
10
|
+
import { redirectOf, runHook } from './hooks.ts'
|
|
11
|
+
import { updatePlan } from './query-write.ts'
|
|
12
|
+
import type { QueryScope } from './scope.ts'
|
|
13
|
+
import type { Store } from './transaction.ts'
|
|
14
|
+
import type { Checked, Drop, Writing } from './write-plan.ts'
|
|
15
|
+
import { moved } from './write-plan.ts'
|
|
16
|
+
import { modelOf } from './query-where.ts'
|
|
17
|
+
|
|
18
|
+
/** The changes and events of a delete's cascade, its hooks run (D-90). */
|
|
19
|
+
export async function cascaded(
|
|
20
|
+
writing: Writing,
|
|
21
|
+
cascade: Cascade,
|
|
22
|
+
): Promise<Change[]> {
|
|
23
|
+
const { caller } = writing.scope
|
|
24
|
+
const changes: Change[] = []
|
|
25
|
+
for (const { model, field, rows } of cascade.nulled) {
|
|
26
|
+
const step = { model: model.name, field, to: null }
|
|
27
|
+
changes.push(...(await moved(step, model, rows, writing)))
|
|
28
|
+
}
|
|
29
|
+
for (const { model, row } of cascade.deleted) {
|
|
30
|
+
await runHook(model, 'beforeDelete', {
|
|
31
|
+
caller,
|
|
32
|
+
record: recordOf(model, row),
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
const ids = new Map<string, string[]>()
|
|
36
|
+
for (const { model, row, redirected } of cascade.deleted) {
|
|
37
|
+
const redirect = redirected ? redirectOf(model) : undefined
|
|
38
|
+
// Its queries run their own pipeline, in this transaction (D-89).
|
|
39
|
+
if (redirect !== undefined) await redirect(caller, row.id)
|
|
40
|
+
else ids.set(model.name, [...(ids.get(model.name) ?? []), row.id])
|
|
41
|
+
}
|
|
42
|
+
for (const [model, deleted] of ids) {
|
|
43
|
+
changes.push({ kind: 'delete', model, where: { id: { in: deleted } } })
|
|
44
|
+
}
|
|
45
|
+
for (const { model, row } of cascade.deleted) {
|
|
46
|
+
writing.events.push({ model, operation: 'delete', id: row.id, before: row })
|
|
47
|
+
}
|
|
48
|
+
return changes
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A nested delete as `Target.delete` runs it: a plugin's redirect, or the
|
|
53
|
+
* cascade, which may not reach a row the write updates (D-138).
|
|
54
|
+
*/
|
|
55
|
+
async function dropped(writing: Writing, step: Drop): Promise<Change[]> {
|
|
56
|
+
const { scope, store } = writing
|
|
57
|
+
const model = modelOf(scope.models, step.model)
|
|
58
|
+
const cascade = await cascadeOf(
|
|
59
|
+
scope.models,
|
|
60
|
+
model,
|
|
61
|
+
step.rows,
|
|
62
|
+
store,
|
|
63
|
+
step.except,
|
|
64
|
+
)
|
|
65
|
+
const reached = cascade.deleted.some(({ model: from, row }) =>
|
|
66
|
+
step.written.has(`${from.name}:${row.id}`),
|
|
67
|
+
)
|
|
68
|
+
if (reached) {
|
|
69
|
+
throw new InvalidQuery(
|
|
70
|
+
step.path,
|
|
71
|
+
`${step.path} deletes a row the write updates.`,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
return cascaded(writing, cascade)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function writingOf(
|
|
78
|
+
scope: QueryScope,
|
|
79
|
+
store: Store,
|
|
80
|
+
events: Event[],
|
|
81
|
+
checks: Checked[] = [],
|
|
82
|
+
): Writing {
|
|
83
|
+
const writing: Writing = {
|
|
84
|
+
store,
|
|
85
|
+
scope,
|
|
86
|
+
events,
|
|
87
|
+
checks,
|
|
88
|
+
replan: async (model, id, draft) =>
|
|
89
|
+
updatePlan(scope, model, id, draft, {}, store),
|
|
90
|
+
drop: async (step) => dropped(writing, step),
|
|
91
|
+
}
|
|
92
|
+
return writing
|
|
93
|
+
}
|