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.
Files changed (411) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +4 -2
  3. package/dist/access.d.ts +62 -0
  4. package/dist/access.d.ts.map +1 -0
  5. package/dist/access.js +258 -0
  6. package/dist/adapter-errors.d.ts +43 -0
  7. package/dist/adapter-errors.d.ts.map +1 -0
  8. package/dist/adapter-errors.js +72 -0
  9. package/dist/adapter-json.d.ts +20 -0
  10. package/dist/adapter-json.d.ts.map +1 -0
  11. package/dist/adapter-json.js +2 -0
  12. package/dist/adapter-migrations.d.ts +90 -0
  13. package/dist/adapter-migrations.d.ts.map +1 -0
  14. package/dist/adapter-migrations.js +1 -0
  15. package/dist/adapter.d.ts +333 -0
  16. package/dist/adapter.d.ts.map +1 -0
  17. package/dist/adapter.js +5 -0
  18. package/dist/aggregate.d.ts +31 -0
  19. package/dist/aggregate.d.ts.map +1 -0
  20. package/dist/aggregate.js +99 -0
  21. package/dist/app-internals.d.ts +10 -0
  22. package/dist/app-internals.d.ts.map +1 -0
  23. package/dist/app-internals.js +12 -0
  24. package/dist/app-options.d.ts +22 -0
  25. package/dist/app-options.d.ts.map +1 -0
  26. package/dist/app-options.js +3 -0
  27. package/dist/cascade.d.ts +47 -0
  28. package/dist/cascade.d.ts.map +1 -0
  29. package/dist/cascade.js +209 -0
  30. package/dist/cli-config.d.ts +6 -0
  31. package/dist/cli-config.d.ts.map +1 -0
  32. package/dist/cli-config.js +45 -0
  33. package/dist/cli-deploy.d.ts +14 -0
  34. package/dist/cli-deploy.d.ts.map +1 -0
  35. package/dist/cli-deploy.js +193 -0
  36. package/dist/cli-main.d.ts +16 -0
  37. package/dist/cli-main.d.ts.map +1 -0
  38. package/dist/cli-main.js +151 -0
  39. package/dist/cli-migrate.d.ts +20 -0
  40. package/dist/cli-migrate.d.ts.map +1 -0
  41. package/dist/cli-migrate.js +105 -0
  42. package/dist/cli.d.ts +3 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +4 -0
  45. package/dist/complete.d.ts +13 -0
  46. package/dist/complete.d.ts.map +1 -0
  47. package/dist/complete.js +60 -0
  48. package/dist/console-logger.d.ts +3 -0
  49. package/dist/console-logger.d.ts.map +1 -0
  50. package/dist/console-logger.js +62 -0
  51. package/dist/create-many.d.ts +7 -0
  52. package/dist/create-many.d.ts.map +1 -0
  53. package/dist/create-many.js +88 -0
  54. package/dist/decimal.d.ts +23 -0
  55. package/dist/decimal.d.ts.map +1 -0
  56. package/dist/decimal.js +50 -0
  57. package/dist/define-app.d.ts +59 -0
  58. package/dist/define-app.d.ts.map +1 -0
  59. package/dist/define-app.js +5 -0
  60. package/dist/define-enum.d.ts +6 -0
  61. package/dist/define-enum.d.ts.map +1 -0
  62. package/dist/define-enum.js +18 -0
  63. package/dist/define-function.d.ts +25 -0
  64. package/dist/define-function.d.ts.map +1 -0
  65. package/dist/define-function.js +111 -0
  66. package/dist/define-model.d.ts +48 -0
  67. package/dist/define-model.d.ts.map +1 -0
  68. package/dist/define-model.js +85 -0
  69. package/dist/definition.d.ts +179 -0
  70. package/dist/definition.d.ts.map +1 -0
  71. package/dist/definition.js +165 -0
  72. package/dist/delete-many.d.ts +13 -0
  73. package/dist/delete-many.d.ts.map +1 -0
  74. package/dist/delete-many.js +58 -0
  75. package/dist/engine.d.ts +16 -0
  76. package/dist/engine.d.ts.map +1 -0
  77. package/dist/engine.js +174 -0
  78. package/dist/errors.d.ts +50 -0
  79. package/dist/errors.d.ts.map +1 -0
  80. package/dist/errors.js +43 -0
  81. package/dist/exact.d.ts +20 -0
  82. package/dist/exact.d.ts.map +1 -0
  83. package/dist/exact.js +1 -0
  84. package/dist/feed.d.ts +6 -0
  85. package/dist/feed.d.ts.map +1 -0
  86. package/dist/feed.js +33 -0
  87. package/dist/field-codec.d.ts +11 -0
  88. package/dist/field-codec.d.ts.map +1 -0
  89. package/dist/field-codec.js +48 -0
  90. package/dist/field-rules.d.ts +57 -0
  91. package/dist/field-rules.d.ts.map +1 -0
  92. package/dist/field-rules.js +1 -0
  93. package/dist/field-type.d.ts +51 -0
  94. package/dist/field-type.d.ts.map +1 -0
  95. package/dist/field-type.js +106 -0
  96. package/dist/fields.d.ts +295 -0
  97. package/dist/fields.d.ts.map +1 -0
  98. package/dist/fields.js +1 -0
  99. package/dist/function.d.ts +126 -0
  100. package/dist/function.d.ts.map +1 -0
  101. package/dist/function.js +1 -0
  102. package/dist/functions.d.ts +18 -0
  103. package/dist/functions.d.ts.map +1 -0
  104. package/dist/functions.js +99 -0
  105. package/dist/group-by.d.ts +10 -0
  106. package/dist/group-by.d.ts.map +1 -0
  107. package/dist/group-by.js +145 -0
  108. package/dist/group-having.d.ts +8 -0
  109. package/dist/group-having.d.ts.map +1 -0
  110. package/dist/group-having.js +97 -0
  111. package/dist/hooks.d.ts +41 -0
  112. package/dist/hooks.d.ts.map +1 -0
  113. package/dist/hooks.js +149 -0
  114. package/dist/hosts.d.ts +3 -0
  115. package/dist/hosts.d.ts.map +1 -0
  116. package/dist/hosts.js +8 -0
  117. package/dist/identify.d.ts +15 -0
  118. package/dist/identify.d.ts.map +1 -0
  119. package/dist/identify.js +71 -0
  120. package/dist/index.d.ts +25 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +12 -0
  123. package/dist/internal.d.ts +28 -0
  124. package/dist/internal.d.ts.map +1 -0
  125. package/dist/internal.js +39 -0
  126. package/dist/lifecycle.d.ts +21 -0
  127. package/dist/lifecycle.d.ts.map +1 -0
  128. package/dist/lifecycle.js +149 -0
  129. package/dist/list-update.d.ts +12 -0
  130. package/dist/list-update.d.ts.map +1 -0
  131. package/dist/list-update.js +52 -0
  132. package/dist/live.d.ts +37 -0
  133. package/dist/live.d.ts.map +1 -0
  134. package/dist/live.js +139 -0
  135. package/dist/manifest.d.ts +11 -0
  136. package/dist/manifest.d.ts.map +1 -0
  137. package/dist/manifest.js +60 -0
  138. package/dist/memory-aggregate.d.ts +7 -0
  139. package/dist/memory-aggregate.d.ts.map +1 -0
  140. package/dist/memory-aggregate.js +107 -0
  141. package/dist/memory-conditions.d.ts +8 -0
  142. package/dist/memory-conditions.d.ts.map +1 -0
  143. package/dist/memory-conditions.js +82 -0
  144. package/dist/memory-draft.d.ts +34 -0
  145. package/dist/memory-draft.d.ts.map +1 -0
  146. package/dist/memory-draft.js +255 -0
  147. package/dist/memory-json.d.ts +6 -0
  148. package/dist/memory-json.d.ts.map +1 -0
  149. package/dist/memory-json.js +86 -0
  150. package/dist/memory-read.d.ts +45 -0
  151. package/dist/memory-read.d.ts.map +1 -0
  152. package/dist/memory-read.js +244 -0
  153. package/dist/memory-search.d.ts +16 -0
  154. package/dist/memory-search.d.ts.map +1 -0
  155. package/dist/memory-search.js +110 -0
  156. package/dist/memory-values.d.ts +23 -0
  157. package/dist/memory-values.d.ts.map +1 -0
  158. package/dist/memory-values.js +82 -0
  159. package/dist/memory.d.ts +4 -0
  160. package/dist/memory.d.ts.map +1 -0
  161. package/dist/memory.js +157 -0
  162. package/dist/migrate.d.ts +22 -0
  163. package/dist/migrate.d.ts.map +1 -0
  164. package/dist/migrate.js +131 -0
  165. package/dist/model.d.ts +239 -0
  166. package/dist/model.d.ts.map +1 -0
  167. package/dist/model.js +1 -0
  168. package/dist/nested-write.d.ts +64 -0
  169. package/dist/nested-write.d.ts.map +1 -0
  170. package/dist/nested-write.js +274 -0
  171. package/dist/number-update.d.ts +18 -0
  172. package/dist/number-update.d.ts.map +1 -0
  173. package/dist/number-update.js +96 -0
  174. package/dist/plugin-app.d.ts +48 -0
  175. package/dist/plugin-app.d.ts.map +1 -0
  176. package/dist/plugin-app.js +230 -0
  177. package/dist/plugin-code.d.ts +17 -0
  178. package/dist/plugin-code.d.ts.map +1 -0
  179. package/dist/plugin-code.js +125 -0
  180. package/dist/plugin-use.d.ts +22 -0
  181. package/dist/plugin-use.d.ts.map +1 -0
  182. package/dist/plugin-use.js +161 -0
  183. package/dist/plugin.d.ts +357 -0
  184. package/dist/plugin.d.ts.map +1 -0
  185. package/dist/plugin.js +17 -0
  186. package/dist/query-aggregate.d.ts +82 -0
  187. package/dist/query-aggregate.d.ts.map +1 -0
  188. package/dist/query-aggregate.js +1 -0
  189. package/dist/query-args.d.ts +41 -0
  190. package/dist/query-args.d.ts.map +1 -0
  191. package/dist/query-args.js +263 -0
  192. package/dist/query-bulk.d.ts +35 -0
  193. package/dist/query-bulk.d.ts.map +1 -0
  194. package/dist/query-bulk.js +1 -0
  195. package/dist/query-conditions.d.ts +9 -0
  196. package/dist/query-conditions.d.ts.map +1 -0
  197. package/dist/query-conditions.js +46 -0
  198. package/dist/query-context.d.ts +32 -0
  199. package/dist/query-context.d.ts.map +1 -0
  200. package/dist/query-context.js +55 -0
  201. package/dist/query-filter.d.ts +46 -0
  202. package/dist/query-filter.d.ts.map +1 -0
  203. package/dist/query-filter.js +1 -0
  204. package/dist/query-group.d.ts +82 -0
  205. package/dist/query-group.d.ts.map +1 -0
  206. package/dist/query-group.js +1 -0
  207. package/dist/query-json.d.ts +9 -0
  208. package/dist/query-json.d.ts.map +1 -0
  209. package/dist/query-json.js +66 -0
  210. package/dist/query-order.d.ts +15 -0
  211. package/dist/query-order.d.ts.map +1 -0
  212. package/dist/query-order.js +115 -0
  213. package/dist/query-project.d.ts +15 -0
  214. package/dist/query-project.d.ts.map +1 -0
  215. package/dist/query-project.js +45 -0
  216. package/dist/query-search.d.ts +24 -0
  217. package/dist/query-search.d.ts.map +1 -0
  218. package/dist/query-search.js +2 -0
  219. package/dist/query-values.d.ts +28 -0
  220. package/dist/query-values.d.ts.map +1 -0
  221. package/dist/query-values.js +91 -0
  222. package/dist/query-where.d.ts +46 -0
  223. package/dist/query-where.d.ts.map +1 -0
  224. package/dist/query-where.js +200 -0
  225. package/dist/query-write.d.ts +14 -0
  226. package/dist/query-write.d.ts.map +1 -0
  227. package/dist/query-write.js +260 -0
  228. package/dist/query.d.ts +254 -0
  229. package/dist/query.d.ts.map +1 -0
  230. package/dist/query.js +1 -0
  231. package/dist/record.d.ts +257 -0
  232. package/dist/record.d.ts.map +1 -0
  233. package/dist/record.js +1 -0
  234. package/dist/recorder.d.ts +32 -0
  235. package/dist/recorder.d.ts.map +1 -0
  236. package/dist/recorder.js +152 -0
  237. package/dist/relation-write.d.ts +41 -0
  238. package/dist/relation-write.d.ts.map +1 -0
  239. package/dist/relation-write.js +112 -0
  240. package/dist/relations.d.ts +42 -0
  241. package/dist/relations.d.ts.map +1 -0
  242. package/dist/relations.js +119 -0
  243. package/dist/rules.d.ts +97 -0
  244. package/dist/rules.d.ts.map +1 -0
  245. package/dist/rules.js +71 -0
  246. package/dist/running.d.ts +29 -0
  247. package/dist/running.d.ts.map +1 -0
  248. package/dist/running.js +39 -0
  249. package/dist/schema-indexes.d.ts +11 -0
  250. package/dist/schema-indexes.d.ts.map +1 -0
  251. package/dist/schema-indexes.js +59 -0
  252. package/dist/schema-virtuals.d.ts +19 -0
  253. package/dist/schema-virtuals.d.ts.map +1 -0
  254. package/dist/schema-virtuals.js +100 -0
  255. package/dist/schema.d.ts +35 -0
  256. package/dist/schema.d.ts.map +1 -0
  257. package/dist/schema.js +267 -0
  258. package/dist/scope.d.ts +46 -0
  259. package/dist/scope.d.ts.map +1 -0
  260. package/dist/scope.js +82 -0
  261. package/dist/scratch.d.ts +14 -0
  262. package/dist/scratch.d.ts.map +1 -0
  263. package/dist/scratch.js +10 -0
  264. package/dist/serve.d.ts +2 -0
  265. package/dist/serve.d.ts.map +1 -0
  266. package/dist/serve.js +4 -0
  267. package/dist/symbols.d.ts +10 -0
  268. package/dist/symbols.d.ts.map +1 -0
  269. package/dist/symbols.js +1 -0
  270. package/dist/transaction.d.ts +28 -0
  271. package/dist/transaction.d.ts.map +1 -0
  272. package/dist/transaction.js +156 -0
  273. package/dist/types.d.ts +58 -0
  274. package/dist/types.d.ts.map +1 -0
  275. package/dist/types.js +1 -0
  276. package/dist/update-many.d.ts +7 -0
  277. package/dist/update-many.d.ts.map +1 -0
  278. package/dist/update-many.js +182 -0
  279. package/dist/uuid.d.ts +7 -0
  280. package/dist/uuid.d.ts.map +1 -0
  281. package/dist/uuid.js +48 -0
  282. package/dist/validation.d.ts +24 -0
  283. package/dist/validation.d.ts.map +1 -0
  284. package/dist/validation.js +72 -0
  285. package/dist/virtual-read.d.ts +9 -0
  286. package/dist/virtual-read.d.ts.map +1 -0
  287. package/dist/virtual-read.js +145 -0
  288. package/dist/virtual.d.ts +142 -0
  289. package/dist/virtual.d.ts.map +1 -0
  290. package/dist/virtual.js +1 -0
  291. package/dist/write-context.d.ts +46 -0
  292. package/dist/write-context.d.ts.map +1 -0
  293. package/dist/write-context.js +102 -0
  294. package/dist/write-data.d.ts +26 -0
  295. package/dist/write-data.d.ts.map +1 -0
  296. package/dist/write-data.js +144 -0
  297. package/dist/write-plan.d.ts +107 -0
  298. package/dist/write-plan.d.ts.map +1 -0
  299. package/dist/write-plan.js +170 -0
  300. package/dist/write-target.d.ts +17 -0
  301. package/dist/write-target.d.ts.map +1 -0
  302. package/dist/write-target.js +59 -0
  303. package/dist/writes.d.ts +23 -0
  304. package/dist/writes.d.ts.map +1 -0
  305. package/dist/writes.js +246 -0
  306. package/dist/writing.d.ts +10 -0
  307. package/dist/writing.d.ts.map +1 -0
  308. package/dist/writing.js +66 -0
  309. package/package.json +42 -3
  310. package/src/access.ts +329 -0
  311. package/src/adapter-errors.ts +89 -0
  312. package/src/adapter-json.ts +21 -0
  313. package/src/adapter-migrations.ts +98 -0
  314. package/src/adapter.ts +363 -0
  315. package/src/aggregate.ts +136 -0
  316. package/src/app-internals.ts +26 -0
  317. package/src/app-options.ts +24 -0
  318. package/src/cascade.ts +297 -0
  319. package/src/cli-config.ts +45 -0
  320. package/src/cli-deploy.ts +235 -0
  321. package/src/cli-main.ts +165 -0
  322. package/src/cli-migrate.ts +143 -0
  323. package/src/cli.ts +5 -0
  324. package/src/complete.ts +73 -0
  325. package/src/console-logger.ts +87 -0
  326. package/src/create-many.ts +126 -0
  327. package/src/decimal.ts +74 -0
  328. package/src/define-app.ts +108 -0
  329. package/src/define-enum.ts +26 -0
  330. package/src/define-function.ts +188 -0
  331. package/src/define-model.ts +220 -0
  332. package/src/definition.ts +352 -0
  333. package/src/delete-many.ts +76 -0
  334. package/src/engine.ts +281 -0
  335. package/src/errors.ts +97 -0
  336. package/src/exact.ts +49 -0
  337. package/src/feed.ts +39 -0
  338. package/src/field-codec.ts +72 -0
  339. package/src/field-rules.ts +77 -0
  340. package/src/field-type.ts +219 -0
  341. package/src/fields.ts +383 -0
  342. package/src/function.ts +143 -0
  343. package/src/functions.ts +154 -0
  344. package/src/group-by.ts +186 -0
  345. package/src/group-having.ts +143 -0
  346. package/src/hooks.ts +215 -0
  347. package/src/hosts.ts +11 -0
  348. package/src/identify.ts +95 -0
  349. package/src/index.ts +125 -0
  350. package/src/internal.ts +68 -0
  351. package/src/lifecycle.ts +214 -0
  352. package/src/list-update.ts +71 -0
  353. package/src/live.ts +199 -0
  354. package/src/manifest.ts +71 -0
  355. package/src/memory-aggregate.ts +162 -0
  356. package/src/memory-conditions.ts +88 -0
  357. package/src/memory-draft.ts +319 -0
  358. package/src/memory-json.ts +91 -0
  359. package/src/memory-read.ts +311 -0
  360. package/src/memory-search.ts +149 -0
  361. package/src/memory-values.ts +106 -0
  362. package/src/memory.ts +173 -0
  363. package/src/migrate.ts +152 -0
  364. package/src/model.ts +312 -0
  365. package/src/nested-write.ts +390 -0
  366. package/src/number-update.ts +142 -0
  367. package/src/plugin-app.ts +308 -0
  368. package/src/plugin-code.ts +158 -0
  369. package/src/plugin-use.ts +217 -0
  370. package/src/plugin.ts +400 -0
  371. package/src/query-aggregate.ts +96 -0
  372. package/src/query-args.ts +375 -0
  373. package/src/query-bulk.ts +53 -0
  374. package/src/query-conditions.ts +53 -0
  375. package/src/query-context.ts +104 -0
  376. package/src/query-filter.ts +54 -0
  377. package/src/query-group.ts +137 -0
  378. package/src/query-json.ts +90 -0
  379. package/src/query-order.ts +158 -0
  380. package/src/query-project.ts +73 -0
  381. package/src/query-search.ts +31 -0
  382. package/src/query-values.ts +131 -0
  383. package/src/query-where.ts +318 -0
  384. package/src/query-write.ts +360 -0
  385. package/src/query.ts +400 -0
  386. package/src/record.ts +379 -0
  387. package/src/recorder.ts +212 -0
  388. package/src/relation-write.ts +176 -0
  389. package/src/relations.ts +165 -0
  390. package/src/rules.ts +188 -0
  391. package/src/running.ts +62 -0
  392. package/src/schema-indexes.ts +88 -0
  393. package/src/schema-virtuals.ts +134 -0
  394. package/src/schema.ts +367 -0
  395. package/src/scope.ts +123 -0
  396. package/src/scratch.ts +28 -0
  397. package/src/serve.ts +5 -0
  398. package/src/symbols.ts +11 -0
  399. package/src/transaction.ts +211 -0
  400. package/src/types.ts +79 -0
  401. package/src/update-many.ts +268 -0
  402. package/src/uuid.ts +51 -0
  403. package/src/validation.ts +107 -0
  404. package/src/virtual-read.ts +198 -0
  405. package/src/virtual.ts +187 -0
  406. package/src/write-context.ts +170 -0
  407. package/src/write-data.ts +188 -0
  408. package/src/write-plan.ts +273 -0
  409. package/src/write-target.ts +92 -0
  410. package/src/writes.ts +361 -0
  411. package/src/writing.ts +93 -0
package/dist/scope.js ADDED
@@ -0,0 +1,82 @@
1
+ import { and } from './access.js';
2
+ import { beforeFind } from './hooks.js';
3
+ import { Relations } from './relations.js';
4
+ import { modelOf } from './query-where.js';
5
+ /**
6
+ * `readable` is synchronous, as the planning that calls it, but `beforeFind`
7
+ * may not be: a model read before its hook ran is recorded, and `ready` runs
8
+ * the hooks so the query can be planned again with their filters.
9
+ */
10
+ export class QueryScope {
11
+ /** By `operation name`, as `read Post`. */
12
+ narrowed = new Map();
13
+ wanted = new Map();
14
+ models;
15
+ caller;
16
+ /** `undefined` for Master, which skips access rules (D-48). */
17
+ access;
18
+ relations;
19
+ constructor(models, caller, access) {
20
+ this.models = models;
21
+ this.caller = caller;
22
+ this.access = access;
23
+ this.relations = access?.relations ?? new Relations(models, caller);
24
+ }
25
+ check(model, field, path, verb) {
26
+ this.access?.check(model, field, path, verb);
27
+ }
28
+ ruled(model, field) {
29
+ return this.access?.ruled(model, field) ?? false;
30
+ }
31
+ /**
32
+ * The rows of `name` the query reads, or the write `operation` targets:
33
+ * its read rule and `beforeFind`.
34
+ */
35
+ readable(name, operation = 'read') {
36
+ return and(this.narrowing(name, operation), this.access?.readable(name));
37
+ }
38
+ related(model, field) {
39
+ return and(this.relations.filter(model, field), this.readable(field.target));
40
+ }
41
+ /**
42
+ * Runs the `beforeFind` hooks of the models read since the last call;
43
+ * whether one of them narrows, so the query must be planned again.
44
+ */
45
+ async ready() {
46
+ let narrowed = false;
47
+ for (const [key, { name, operation }] of this.wanted) {
48
+ this.wanted.delete(key);
49
+ const model = modelOf(this.models, name);
50
+ const filter = await beforeFind(this.models, model, this.caller, operation, this.relations);
51
+ this.narrowed.set(key, filter);
52
+ if (filter !== undefined)
53
+ narrowed = true;
54
+ }
55
+ return narrowed;
56
+ }
57
+ /**
58
+ * The rows of `name` a write reaches; `trusted` for a value a hook or a
59
+ * default wrote, which skips access rules but not `beforeFind` (D-67).
60
+ */
61
+ async filter(name, trusted, operation = 'read') {
62
+ this.narrowing(name, operation);
63
+ await this.ready();
64
+ return trusted
65
+ ? this.narrowing(name, operation)
66
+ : this.readable(name, operation);
67
+ }
68
+ narrowing(name, operation) {
69
+ const key = `${operation} ${name}`;
70
+ if (this.narrowed.has(key))
71
+ return this.narrowed.get(key);
72
+ this.wanted.set(key, { name, operation });
73
+ return undefined;
74
+ }
75
+ }
76
+ /** Plans a query, then again once the `beforeFind` hooks it reached ran. */
77
+ export async function planned(scope, build) {
78
+ let built = build();
79
+ while (await scope.ready())
80
+ built = build();
81
+ return built;
82
+ }
@@ -0,0 +1,14 @@
1
+ import type { Adapter } from './adapter.ts';
2
+ /**
3
+ * The tables of one test file: until `drop`, each start of the adapter
4
+ * finds them empty, and no other table is read or written.
5
+ */
6
+ export type Scratch = {
7
+ /** Drops them; the adapter's next start is on its own tables again. */
8
+ readonly drop: () => Promise<void>;
9
+ };
10
+ /** Remembers how `adapter` gives a test file its own tables. */
11
+ export declare function scratchable<T extends Adapter>(adapter: T, scratch: () => Scratch): T;
12
+ /** How `adapter` gives a test file its own tables, if it can. */
13
+ export declare function scratchOf(adapter: Adapter): (() => Scratch) | undefined;
14
+ //# sourceMappingURL=scratch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch.d.ts","sourceRoot":"","sources":["../src/scratch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG;IACrB,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC,CAAA;AAID,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,CAAC,SAAS,OAAO,EAC5C,OAAO,EAAE,CAAC,EACV,OAAO,EAAE,MAAM,OAAO,GACpB,CAAC,CAGH;AAED,iEAAiE;AACjE,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,SAAS,CAEvE"}
@@ -0,0 +1,10 @@
1
+ const scratches = new WeakMap();
2
+ /** Remembers how `adapter` gives a test file its own tables. */
3
+ export function scratchable(adapter, scratch) {
4
+ scratches.set(adapter, scratch);
5
+ return adapter;
6
+ }
7
+ /** How `adapter` gives a test file its own tables, if it can. */
8
+ export function scratchOf(adapter) {
9
+ return scratches.get(adapter);
10
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":""}
package/dist/serve.js ADDED
@@ -0,0 +1,4 @@
1
+ // What `ultimabase dev` runs under `node --watch` (D-105): the application
2
+ // of the config file it is given.
3
+ import { serve } from './cli-main.js';
4
+ process.exitCode = await serve(process.argv[2]);
@@ -0,0 +1,10 @@
1
+ export declare const modelFields: unique symbol;
2
+ export declare const fieldType: unique symbol;
3
+ export declare const declaredRecord: unique symbol;
4
+ export declare const pluginTypes: unique symbol;
5
+ export declare const readFilter: unique symbol;
6
+ export declare const typeFields: unique symbol;
7
+ export declare const nullableOf: unique symbol;
8
+ /** Marks a calendar day's options, a string compared in order (D-150). */
9
+ export declare const calendarDay: unique symbol;
10
+ //# sourceMappingURL=symbols.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../src/symbols.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,CAAC,MAAM,WAAW,EAAE,OAAO,MAAM,CAAA;AAC/C,MAAM,CAAC,OAAO,CAAC,MAAM,SAAS,EAAE,OAAO,MAAM,CAAA;AAC7C,MAAM,CAAC,OAAO,CAAC,MAAM,cAAc,EAAE,OAAO,MAAM,CAAA;AAClD,MAAM,CAAC,OAAO,CAAC,MAAM,WAAW,EAAE,OAAO,MAAM,CAAA;AAC/C,MAAM,CAAC,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAA;AAC9C,MAAM,CAAC,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAA;AAC9C,MAAM,CAAC,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAA;AAC9C,0EAA0E;AAC1E,MAAM,CAAC,OAAO,CAAC,MAAM,WAAW,EAAE,OAAO,MAAM,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { Adapter, Transaction } from './adapter.ts';
2
+ import type { Running } from './running.ts';
3
+ /** What a query reads and writes through. */
4
+ export type Store = Pick<Transaction, 'find' | 'count' | 'aggregate' | 'write'> & {
5
+ /**
6
+ * The write's changes when nothing follows them: alone, they need no
7
+ * transaction (D-99).
8
+ */
9
+ readonly last: Transaction['write'];
10
+ };
11
+ /** A `*Commit` hook, run once the transaction commits (D-15). */
12
+ export type Commit = () => Promise<void>;
13
+ /** A read: in the open transaction, else straight on the adapter. */
14
+ export declare function reading<R>(app: Running, run: (store: Store) => Promise<R>): Promise<R>;
15
+ /** A write: in the open transaction, else in its own (D-36). */
16
+ export declare function writing<R>(app: Running, run: (store: Store, commits: Commit[]) => Promise<R>): Promise<R>;
17
+ /**
18
+ * Runs `run` in one transaction: if it throws, or if a query in it fails,
19
+ * every write rolls back and the `*Commit` hooks never run. Inside another
20
+ * `transaction`, it joins that one (D-36).
21
+ */
22
+ export declare function transaction<R>(run: () => Promise<R>): Promise<R>;
23
+ /**
24
+ * Runs an adapter's own statement, `database.sql`, in the running
25
+ * application's open transaction, else alone (D-137).
26
+ */
27
+ export declare function joining<R>(adapter: Adapter, run: (tx: Transaction | undefined) => Promise<R>): Promise<R>;
28
+ //# sourceMappingURL=transaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAKxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAI3C,6CAA6C;AAC7C,MAAM,MAAM,KAAK,GAAG,IAAI,CACvB,WAAW,EACX,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,OAAO,CACxC,GAAG;IACH;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,iEAAiE;AACjE,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;AAyHxC,qEAAqE;AACrE,wBAAsB,OAAO,CAAC,CAAC,EAC9B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,CAAC,CAAC,CAeZ;AAED,gEAAgE;AAChE,wBAAsB,OAAO,CAAC,CAAC,EAC9B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,GAClD,OAAO,CAAC,CAAC,CAAC,CAMZ;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQtE;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC9B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9C,OAAO,CAAC,CAAC,CAAC,CAQZ"}
@@ -0,0 +1,156 @@
1
+ // Transactions (D-36, D-88): `AsyncLocalStorage` carries the open one, so a
2
+ // hook's queries join it without a `tx` argument.
3
+ import { AsyncLocalStorage } from 'node:async_hooks';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { RelationViolation } from './adapter.js';
6
+ import { decoded } from './field-codec.js';
7
+ import { recordCount, recordFind } from './recorder.js';
8
+ import { current } from './running.js';
9
+ import { refused } from './write-target.js';
10
+ const opened = new AsyncLocalStorage();
11
+ async function begun(open) {
12
+ open.tx ??= open.app.adapter.begin();
13
+ return open.tx;
14
+ }
15
+ /**
16
+ * The store's rows with their plugin fields decoded (D-122), each read kept
17
+ * if a recording runs (D-154).
18
+ */
19
+ function decoding(app, store) {
20
+ const { models } = app.resolved;
21
+ return {
22
+ find: async (read) => {
23
+ const rows = await store.find(read);
24
+ recordFind(models, read, rows);
25
+ return decoded(models, read, rows);
26
+ },
27
+ count: async (read) => {
28
+ recordCount(models, read);
29
+ return store.count(read);
30
+ },
31
+ // It reads no plugin field (D-143).
32
+ aggregate: async (read) => {
33
+ recordCount(models, read);
34
+ return store.aggregate(read);
35
+ },
36
+ write: async (write) => decoded(models, write.read, await store.write(write)),
37
+ last: async (write) => decoded(models, write.read, await store.last(write)),
38
+ };
39
+ }
40
+ /** Queries in the transaction; `owner`'s last write may run alone. */
41
+ function storeOf(open, owner) {
42
+ return decoding(open.app, {
43
+ find: async (read) => (await begun(open)).find(read),
44
+ count: async (read) => (await begun(open)).count(read),
45
+ aggregate: async (read) => (await begun(open)).aggregate(read),
46
+ write: async (write) => (await begun(open)).write(write),
47
+ last: async (write) => owner && open.tx === undefined
48
+ ? open.app.adapter.write(write)
49
+ : (await begun(open)).write(write),
50
+ });
51
+ }
52
+ /** Runs in the open transaction; a failure rolls all of it back (D-36). */
53
+ async function joined(open, run) {
54
+ if (open.failure !== undefined) {
55
+ throw new Error('A query failed in this transaction, which rolls back: no other query runs in it.', { cause: open.failure.error });
56
+ }
57
+ try {
58
+ return await run();
59
+ }
60
+ catch (error) {
61
+ open.failure ??= { error };
62
+ throw error;
63
+ }
64
+ }
65
+ async function committed(logger, commits) {
66
+ for (const commit of commits) {
67
+ try {
68
+ await commit();
69
+ }
70
+ catch (error) {
71
+ logger.error({ correlationId: randomUUID(), err: error }, 'A commit hook failed; the write is kept.');
72
+ }
73
+ }
74
+ }
75
+ /** A transaction that failed to begin has nothing to roll back. */
76
+ async function rollback(open) {
77
+ const tx = await open.tx?.catch(() => undefined);
78
+ await tx?.rollback();
79
+ }
80
+ async function atomic(app, run) {
81
+ const open = { app, tx: undefined, commits: [], failure: undefined };
82
+ let result;
83
+ try {
84
+ result = await opened.run(open, run, open);
85
+ }
86
+ catch (error) {
87
+ await rollback(open);
88
+ throw error;
89
+ }
90
+ if (open.failure !== undefined) {
91
+ await rollback(open);
92
+ throw open.failure.error;
93
+ }
94
+ try {
95
+ await (await open.tx)?.commit();
96
+ }
97
+ catch (error) {
98
+ // A deferred foreign key checks at the commit (D-99).
99
+ throw error instanceof RelationViolation ? refused(error) : error;
100
+ }
101
+ // Outside the transaction: a commit hook's queries open their own.
102
+ await committed(app.logger, open.commits);
103
+ return result;
104
+ }
105
+ /** A read: in the open transaction, else straight on the adapter. */
106
+ export async function reading(app, run) {
107
+ const open = opened.getStore();
108
+ if (open === undefined) {
109
+ const { adapter } = app;
110
+ return run(decoding(app, {
111
+ find: async (read) => adapter.find(read),
112
+ count: async (read) => adapter.count(read),
113
+ aggregate: async (read) => adapter.aggregate(read),
114
+ write: async (write) => adapter.write(write),
115
+ last: async (write) => adapter.write(write),
116
+ }));
117
+ }
118
+ return joined(open, async () => run(storeOf(open, false)));
119
+ }
120
+ /** A write: in the open transaction, else in its own (D-36). */
121
+ export async function writing(app, run) {
122
+ const open = opened.getStore();
123
+ if (open !== undefined) {
124
+ return joined(open, async () => run(storeOf(open, false), open.commits));
125
+ }
126
+ return atomic(app, async (own) => run(storeOf(own, true), own.commits));
127
+ }
128
+ /**
129
+ * Runs `run` in one transaction: if it throws, or if a query in it fails,
130
+ * every write rolls back and the `*Commit` hooks never run. Inside another
131
+ * `transaction`, it joins that one (D-36).
132
+ */
133
+ export async function transaction(run) {
134
+ const open = opened.getStore();
135
+ if (open !== undefined)
136
+ return joined(open, run);
137
+ const app = current();
138
+ if (app === undefined) {
139
+ throw new Error('transaction runs on a started application.');
140
+ }
141
+ return app.work.run(async () => atomic(app, run));
142
+ }
143
+ /**
144
+ * Runs an adapter's own statement, `database.sql`, in the running
145
+ * application's open transaction, else alone (D-137).
146
+ */
147
+ export async function joining(adapter, run) {
148
+ const open = opened.getStore();
149
+ if (open?.app.adapter === adapter) {
150
+ return joined(open, async () => run(await begun(open)));
151
+ }
152
+ const app = current();
153
+ if (app?.adapter !== adapter)
154
+ return run(undefined);
155
+ return app.work.run(async () => run(undefined));
156
+ }
@@ -0,0 +1,58 @@
1
+ import type { ResultOf, RunArgs } from './function.ts';
2
+ import type { Model } from './model.ts';
3
+ import type { AggregateArgs } from './query-aggregate.ts';
4
+ import type { CreateManyAndReturnArgs, CreateManyArgs, UpdateManyAndReturnArgs, UpdateManyArgs } from './query-bulk.ts';
5
+ import type { GroupByArgs } from './query-group.ts';
6
+ import type { CreateArgs, FindFirstArgs, FindManyArgs, FindUniqueArgs, Include, OrderBy, Payload, Select, UpdateArgs, UpsertArgs, Where } from './query.ts';
7
+ import type { CreateData, UpdateData } from './record.ts';
8
+ type Parts<M> = M extends Model<infer N, infer F, infer _P, infer _W, infer X> ? {
9
+ readonly name: N;
10
+ readonly fields: F;
11
+ readonly unique: X;
12
+ } : never;
13
+ type FieldsOf<M> = Parts<M>['fields'];
14
+ type UniqueOf<M> = Parts<M>['unique'];
15
+ type NameOf<M> = Parts<M>['name'];
16
+ type Operations<F, X, N> = {
17
+ readonly findMany: FindManyArgs<F, X, N>;
18
+ readonly findFirst: FindFirstArgs<F, X>;
19
+ readonly findUnique: FindUniqueArgs<F, X>;
20
+ readonly findFirstOrThrow: FindFirstArgs<F, X>;
21
+ readonly findUniqueOrThrow: FindUniqueArgs<F, X>;
22
+ readonly count: {
23
+ readonly where?: Where<F>;
24
+ };
25
+ readonly aggregate: AggregateArgs<F>;
26
+ readonly groupBy: GroupByArgs<F, N>;
27
+ readonly create: CreateArgs<F, N>;
28
+ readonly update: UpdateArgs<F, X, N>;
29
+ readonly upsert: UpsertArgs<F, X, N>;
30
+ readonly delete: FindUniqueArgs<F, X>;
31
+ readonly updateMany: UpdateManyArgs<F>;
32
+ readonly createMany: CreateManyArgs<F>;
33
+ readonly updateManyAndReturn: UpdateManyAndReturnArgs<F>;
34
+ readonly createManyAndReturn: CreateManyAndReturnArgs<F>;
35
+ readonly deleteMany: {
36
+ readonly where?: Where<F> | undefined;
37
+ };
38
+ };
39
+ /** One record, as arguments `A` return it; without them, its scalars. */
40
+ export type RecordOf<M, A = {}> = Payload<FieldsOf<M>, A>;
41
+ export type WhereOf<M> = Where<FieldsOf<M>>;
42
+ export type IncludeOf<M> = Include<FieldsOf<M>>;
43
+ export type SelectOf<M> = Select<FieldsOf<M>>;
44
+ export type OrderByOf<M> = OrderBy<FieldsOf<M>>;
45
+ export type CreateDataOf<M> = CreateData<FieldsOf<M>, NameOf<M>>;
46
+ export type UpdateDataOf<M> = UpdateData<FieldsOf<M>, NameOf<M>>;
47
+ /** The arguments of one operation of the model. */
48
+ export type ArgsOf<M, O extends keyof Operations<FieldsOf<M>, UniqueOf<M>, NameOf<M>>> = Operations<FieldsOf<M>, UniqueOf<M>, NameOf<M>>[O];
49
+ /** What a caller sends to a function, before validation. */
50
+ export type InputOf<Fn> = Fn extends {
51
+ readonly input: infer I;
52
+ } ? RunArgs<I> extends readonly [input: infer V, options?: unknown] ? V : undefined : never;
53
+ /** What a function returns. */
54
+ export type OutputOf<Fn> = Fn extends {
55
+ readonly output: infer O;
56
+ } ? ResultOf<O> : never;
57
+ export {};
58
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { Context } from './query-context.ts';
2
+ import type { Commit, Store } from './transaction.ts';
3
+ export declare function updateMany(context: Context, store: Store, commits: Commit[], args: unknown): Promise<{
4
+ readonly count: number;
5
+ }>;
6
+ export declare function updateManyAndReturn(context: Context, store: Store, commits: Commit[], args: unknown): Promise<readonly unknown[]>;
7
+ //# sourceMappingURL=update-many.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-many.d.ts","sourceRoot":"","sources":["../src/update-many.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAKjD,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAoOrD,wBAAsB,UAAU,CAC/B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAIrC;AAED,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAK7B"}
@@ -0,0 +1,182 @@
1
+ // `updateMany` (D-133): what an `update` runs, on each row its `where`
2
+ // selects, all or nothing; one write when nothing needs the rows (D-99).
3
+ // `updateManyAndReturn` returns them in `id` order, each as `update` does
4
+ // (D-149).
5
+ import { recordOf } from './access.js';
6
+ import { InvalidQuery } from './errors.js';
7
+ import { rereads, runHook, settle } from './hooks.js';
8
+ import { computeOn, operationsOf, inRange, validates } from './number-update.js';
9
+ import { oneKeys } from './query-args.js';
10
+ import { filtered, given, shapedRows } from './query-context.js';
11
+ import { byIds, entries } from './query-where.js';
12
+ import { updatePlan } from './query-write.js';
13
+ import { prepared, readData } from './write-data.js';
14
+ import { updated } from './write-plan.js';
15
+ import { checkedWrite, guarded } from './write-target.js';
16
+ import { writingOf } from './writing.js';
17
+ import { returned } from './writes.js';
18
+ // In id order, so two calls on the same rows wait instead of deadlocking.
19
+ const orderBy = [{ field: 'id', direction: 'asc' }];
20
+ /** The scalar data of the rows, as `update` reads it (D-133 point 2). */
21
+ function scalarData(context, data) {
22
+ const { models, model } = context;
23
+ for (const [name] of entries(data, 'data')) {
24
+ if (model.fields.get(name)?.kind === 'scalar')
25
+ continue;
26
+ if (!model.fields.has(name))
27
+ continue;
28
+ const path = `data.${name}`;
29
+ throw new InvalidQuery(path, `${path} is a relation, which updateMany does not write.`);
30
+ }
31
+ return readData(models, model, data, false);
32
+ }
33
+ /** The plan's update, spread to every row `where` selects. */
34
+ function spread(plan, where) {
35
+ return plan.steps.flatMap((step) => step.kind === 'update' ? [{ ...step, where }] : []);
36
+ }
37
+ const same = (a, b) => {
38
+ const keys = Object.keys(a.draft);
39
+ return (keys.length === Object.keys(b.draft).length &&
40
+ keys.every((key) => Object.hasOwn(b.draft, key) && Object.is(a.draft[key], b.draft[key])) &&
41
+ [...a.operations.keys()].join() === [...b.operations.keys()].join());
42
+ };
43
+ /**
44
+ * A row's data after its hooks: a number or list they left as computed is
45
+ * computed by the write again, the row being locked, so rows share one
46
+ * statement.
47
+ */
48
+ function settled(item, operations) {
49
+ const left = new Map(operations);
50
+ const draft = { ...item.draft };
51
+ for (const name of operations.keys()) {
52
+ if (Object.is(item.draft[name], item.sent[name]))
53
+ delete draft[name];
54
+ else
55
+ left.delete(name);
56
+ }
57
+ return { draft, operations: left };
58
+ }
59
+ /** The rows by data; one a hook gave a relation write is planned alone. */
60
+ function grouped(model, items, operations) {
61
+ const groups = [];
62
+ for (const item of items) {
63
+ const each = settled(item, operations);
64
+ const scalars = Object.keys(each.draft).every((name) => model.fields.get(name)?.kind === 'scalar');
65
+ const found = scalars
66
+ ? groups.find((group) => same(group, each))
67
+ : undefined;
68
+ if (found === undefined)
69
+ groups.push({ ...each, items: [item] });
70
+ else
71
+ found.items.push(item);
72
+ }
73
+ return groups;
74
+ }
75
+ /**
76
+ * Each row through the update rule and hooks, then their writes (D-133):
77
+ * the rows changed, or with `at` as it shapes them.
78
+ */
79
+ async function lockedRows(context, store, commits, data, where, at) {
80
+ const { model, caller, scope } = context;
81
+ const { operations } = data;
82
+ const rows = await store.find({
83
+ model: model.name,
84
+ where,
85
+ orderBy,
86
+ lock: true,
87
+ });
88
+ const items = rows.map((row) => {
89
+ const draft = { ...data.draft };
90
+ computeOn(model, row, draft, operations);
91
+ return { row, draft, sent: { ...draft } };
92
+ });
93
+ // One row refused, none written (D-111).
94
+ for (const { row, sent } of items) {
95
+ scope.access?.allow(model, 'update', { record: row, data: sent });
96
+ }
97
+ if (items.length === 0)
98
+ return [];
99
+ for (const { row, draft } of items) {
100
+ await prepared(model, draft, caller, false);
101
+ const record = recordOf(model, row);
102
+ await runHook(model, 'beforeUpdate', { caller, record, data: draft });
103
+ }
104
+ const events = items.map(({ row }) => ({
105
+ model,
106
+ operation: 'update',
107
+ id: row.id,
108
+ before: row,
109
+ }));
110
+ const writing = writingOf(scope, store, events);
111
+ const changes = [];
112
+ for (const group of grouped(model, items, operations)) {
113
+ const [{ row, sent }] = group.items;
114
+ const plan = await updatePlan(scope, model, row.id, group.draft, sent, store, group.operations);
115
+ events.push(...plan.events);
116
+ await plan.check(writing);
117
+ const ids = group.items.map((item) => item.row.id);
118
+ const one = ids.length === 1;
119
+ changes.push(...(one
120
+ ? await plan.changes(writing)
121
+ : spread(plan, { id: { in: ids } })));
122
+ }
123
+ const ids = items.map((item) => item.row.id);
124
+ const read = at && {
125
+ model: model.name,
126
+ ...at.read,
127
+ where: byIds(ids),
128
+ orderBy,
129
+ };
130
+ // An `after*` hook may change the rows: they are read once it ran (D-88).
131
+ const again = rereads(events);
132
+ const written = await inRange(model, checkedWrite(store, writing.checks, changes, again ? undefined : read));
133
+ await settle(store, caller, events, commits);
134
+ if (at === undefined || read === undefined)
135
+ return ids;
136
+ const result = again ? await store.find(read) : written;
137
+ return shapedRows(context, store, result, at.shape, scope.access);
138
+ }
139
+ /**
140
+ * Updates the rows `where` selects: their ids, or with `at` the rows as it
141
+ * shapes them, in `id` order. The rows come back even when a read rule
142
+ * would hide them, as `update`'s does (D-86).
143
+ */
144
+ async function updating(context, store, commits, found, at) {
145
+ const { model, caller, scope } = context;
146
+ const draft = scalarData(context, found.get('data'));
147
+ const operations = operationsOf(model, draft);
148
+ const where = (await filtered(context, found, 'update')) ?? {};
149
+ // A relation read beside the UPDATE would see its rows as they were.
150
+ const nests = at?.read.include !== undefined || at?.read.count !== undefined;
151
+ if (scope.access !== undefined ||
152
+ updated(model) ||
153
+ validates(model, operations) ||
154
+ nests) {
155
+ const data = { draft, operations };
156
+ return lockedRows(context, store, commits, data, where, at);
157
+ }
158
+ // Nothing needs the stored rows: one UPDATE, which computes the numbers
159
+ // and lists and returns the rows it changed (D-104).
160
+ await prepared(model, draft, caller, false);
161
+ const plan = await updatePlan(scope, model, '', draft, draft, store, operations);
162
+ const write = {
163
+ changes: spread(plan, where),
164
+ read: { model: model.name, ...at?.read, orderBy },
165
+ before: true,
166
+ };
167
+ const rows = await inRange(model, guarded(store.last(write), []));
168
+ if (at === undefined)
169
+ return rows.map((row) => row.id);
170
+ return shapedRows(context, store, rows, at.shape, scope.access);
171
+ }
172
+ export async function updateMany(context, store, commits, args) {
173
+ const found = given(args, context.model.name, ['where', 'data'], ['data']);
174
+ const ids = await updating(context, store, commits, found);
175
+ return { count: ids.length };
176
+ }
177
+ export async function updateManyAndReturn(context, store, commits, args) {
178
+ const keys = ['where', 'data', ...oneKeys];
179
+ const found = given(args, context.model.name, keys, ['data']);
180
+ const at = await returned(context, found);
181
+ return updating(context, store, commits, found, at);
182
+ }
package/dist/uuid.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export declare function uuidv7(): string;
2
+ /**
3
+ * The id of the row a nested `create` makes from `data`: made once, so its
4
+ * relation's field rule sees it before the row is planned (D-128).
5
+ */
6
+ export declare function madeId(data: object): string;
7
+ //# sourceMappingURL=uuid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAMA,wBAAgB,MAAM,IAAI,MAAM,CAgC/B;AAID;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI3C"}
package/dist/uuid.js ADDED
@@ -0,0 +1,48 @@
1
+ // UUIDv7 ids (D-22, RFC 9562): a millisecond timestamp, then a 12-bit counter
2
+ // in `rand_a` so that ids made in the same millisecond still sort in order.
3
+ let lastTime = 0;
4
+ let counter = 0;
5
+ export function uuidv7() {
6
+ let time = Date.now();
7
+ if (time > lastTime) {
8
+ lastTime = time;
9
+ counter = 0;
10
+ }
11
+ else {
12
+ counter += 1;
13
+ if (counter > 0xfff) {
14
+ lastTime += 1;
15
+ counter = 0;
16
+ }
17
+ time = lastTime;
18
+ }
19
+ const bytes = new Uint8Array(16);
20
+ crypto.getRandomValues(bytes);
21
+ for (let index = 5; index >= 0; index -= 1) {
22
+ bytes[index] = time % 256;
23
+ time = Math.floor(time / 256);
24
+ }
25
+ bytes[6] = 0x70 | (counter >> 8);
26
+ bytes[7] = counter & 0xff;
27
+ bytes[8] = 0x80 | ((bytes[8] ?? 0) & 0x3f);
28
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0'));
29
+ return [
30
+ hex.slice(0, 4),
31
+ hex.slice(4, 6),
32
+ hex.slice(6, 8),
33
+ hex.slice(8, 10),
34
+ hex.slice(10, 16),
35
+ ]
36
+ .map((part) => part.join(''))
37
+ .join('-');
38
+ }
39
+ const made = new WeakMap();
40
+ /**
41
+ * The id of the row a nested `create` makes from `data`: made once, so its
42
+ * relation's field rule sees it before the row is planned (D-128).
43
+ */
44
+ export function madeId(data) {
45
+ const id = made.get(data) ?? uuidv7();
46
+ made.set(data, id);
47
+ return id;
48
+ }