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
@@ -0,0 +1,144 @@
1
+ import { InvalidQuery, PublicError } from './errors.js';
2
+ import { entries, integerRange, isObject, misfit, unknownKey, } from './query-where.js';
3
+ import { checked, validated } from './validation.js';
4
+ import { manyWrite, oneWrite } from './relation-write.js';
5
+ import { refuseSet } from './relations.js';
6
+ /** A value's issue; each element of a list fits its type (D-127). */
7
+ export function issueOf(value, field) {
8
+ if (!field.list)
9
+ return misfit(value, field);
10
+ const fit = (item) => item !== '' && misfit(item, field) === undefined;
11
+ if (Array.isArray(value) && value.every(fit))
12
+ return undefined;
13
+ const of = field.type === 'enum'
14
+ ? `values among ${(field.values ?? []).join(', ')}`
15
+ : field.integer === true
16
+ ? `whole numbers ${integerRange}`
17
+ : field.type === 'number'
18
+ ? 'finite numbers'
19
+ : 'non-empty strings without a NUL or a lone surrogate';
20
+ return `Expected a list of ${of}.`;
21
+ }
22
+ /**
23
+ * The caller's data, its shape checked, as a draft the hooks may change: a
24
+ * copy, down to the rows it creates and updates, which never write the
25
+ * relation they are written through (D-128, D-138).
26
+ */
27
+ export function readData(models, model, data, creating, at = 'data', inverse) {
28
+ const draft = {};
29
+ for (const [name, value] of entries(data, at)) {
30
+ const field = model.fields.get(name);
31
+ const path = `${at}.${name}`;
32
+ // A view is read-only (D-151).
33
+ if (field === undefined ||
34
+ (field.kind === 'many' && field.from !== undefined)) {
35
+ throw unknownKey(path, model.name);
36
+ }
37
+ if (name === inverse) {
38
+ throw new InvalidQuery(path, `${path} cannot be written: it links to the row being written.`);
39
+ }
40
+ draft[name] = value;
41
+ if (field.kind === 'scalar')
42
+ continue;
43
+ const target = models.get(field.target);
44
+ const nested = (item, where, creates = true) => target === undefined
45
+ ? item
46
+ : readData(models, target, item, creates, where, field.inverse);
47
+ const orCreated = (item, where) => ({
48
+ where: item.where,
49
+ create: nested(item.create, `${where}.create`),
50
+ });
51
+ if (field.kind === 'one') {
52
+ const write = oneWrite(value, path, model.name, creating);
53
+ const to = `${path}.${write.kind}`;
54
+ if (write.kind === 'create' || write.kind === 'update') {
55
+ const copy = nested(write.data, to, write.kind === 'create');
56
+ draft[name] = { [write.kind]: copy };
57
+ }
58
+ else if (write.kind === 'connectOrCreate') {
59
+ draft[name] = { connectOrCreate: orCreated(write, to) };
60
+ }
61
+ continue;
62
+ }
63
+ const write = manyWrite(value, path, model.name, creating);
64
+ if (write.set !== undefined)
65
+ refuseSet(model, name, `${path}.set`);
66
+ const copy = { ...(isObject(value) ? value : {}) };
67
+ const to = (key, index) => `${path}.${key}.${index}`;
68
+ if (Object.hasOwn(copy, 'create')) {
69
+ copy['create'] = write.create.map((item, index) => nested(item, to('create', index)));
70
+ }
71
+ if (Object.hasOwn(copy, 'connectOrCreate')) {
72
+ copy['connectOrCreate'] = write.connectOrCreate.map((item, index) => orCreated(item, to('connectOrCreate', index)));
73
+ }
74
+ if (Object.hasOwn(copy, 'update')) {
75
+ copy['update'] = write.update.map((item, index) => ({
76
+ where: item.where,
77
+ data: nested(item.data, `${to('update', index)}.data`, false),
78
+ }));
79
+ }
80
+ draft[name] = copy;
81
+ }
82
+ return draft;
83
+ }
84
+ /** A relation default's id: the target's `{ id }` or a record of it (D-53). */
85
+ function idOf(model, field, value) {
86
+ const id = isObject(value) ? value['id'] : undefined;
87
+ if (typeof id !== 'string') {
88
+ throw new TypeError(`${model.name}.${field.name}: a relation's default is the target's { id } or a record of it.`);
89
+ }
90
+ return id;
91
+ }
92
+ function defaults(model, draft, caller) {
93
+ for (const field of model.fields.values()) {
94
+ if (Object.hasOwn(draft, field.name) || field.kind === 'many')
95
+ continue;
96
+ const fallback = field.options['default'];
97
+ const value = typeof fallback === 'function'
98
+ ? Reflect.apply(fallback, undefined, [{ caller }])
99
+ : fallback;
100
+ if (value === undefined)
101
+ continue;
102
+ draft[field.name] =
103
+ field.kind === 'one'
104
+ ? { connect: { id: idOf(model, field, value) } }
105
+ : value;
106
+ }
107
+ }
108
+ /**
109
+ * Gives a create its defaults, then checks each value's type and runs its
110
+ * `validate`, whose output replaces it: defaults are validated, hook values
111
+ * are not (D-67).
112
+ */
113
+ export async function prepared(model, draft, caller, creating, at = []) {
114
+ if (creating)
115
+ defaults(model, draft, caller);
116
+ const issues = [];
117
+ for (const [name, value] of Object.entries(draft)) {
118
+ const field = model.fields.get(name);
119
+ if (field?.kind !== 'scalar' || value === null)
120
+ continue;
121
+ const { codec } = field;
122
+ // A plugin field's schema takes the value or its JSON (D-122).
123
+ const issue = codec === undefined ? issueOf(value, field) : undefined;
124
+ if (issue !== undefined) {
125
+ issues.push({ path: [name], message: issue });
126
+ continue;
127
+ }
128
+ const result = codec === undefined
129
+ ? await validated(model, field, value)
130
+ : await checked(codec.schema, value, [name], `${model.name}.${name}`);
131
+ if ('issues' in result)
132
+ issues.push(...result.issues);
133
+ else
134
+ draft[name] = result.value;
135
+ }
136
+ if (issues.length > 0) {
137
+ throw invalid(issues.map((one) => ({ ...one, path: [...at, ...one.path] })));
138
+ }
139
+ }
140
+ export function invalid(issues) {
141
+ return new PublicError('VALIDATION_FAILED', 'Invalid data.', {
142
+ details: { issues },
143
+ });
144
+ }
@@ -0,0 +1,107 @@
1
+ import type { Change, Check, Filter, Row } from './adapter.ts';
2
+ import type { Except } from './cascade.ts';
3
+ import type { Issue } from './errors.ts';
4
+ import type { Event } from './hooks.ts';
5
+ import type { ModelInfo } from './schema.ts';
6
+ import type { QueryScope } from './scope.ts';
7
+ import type { Store } from './transaction.ts';
8
+ import type { Draft } from './write-data.ts';
9
+ /** The reads a write needs before it runs. */
10
+ export type Reads = Pick<Store, 'find' | 'count'>;
11
+ /**
12
+ * A relation write from the other side: the target rows `where` selects
13
+ * take `to` as their key `field`, under the target's rules (D-78).
14
+ */
15
+ export type Move = {
16
+ readonly kind: 'move';
17
+ readonly model: string;
18
+ readonly field: string;
19
+ /** The relation written, as `Post.comments` (D-82). */
20
+ readonly via: string;
21
+ readonly where: Filter;
22
+ readonly to: string | null;
23
+ /** A default's or a hook's value, which skips access rules (D-67). */
24
+ readonly trusted: boolean;
25
+ };
26
+ type Link = Extract<Change, {
27
+ readonly kind: 'link' | 'unlink';
28
+ }>;
29
+ /** A join's `unlink`, kept to the `target` rows the caller reads (D-86). */
30
+ export type Detach = Omit<Link, 'kind'> & {
31
+ readonly kind: 'detach';
32
+ readonly target: string;
33
+ readonly trusted: boolean;
34
+ };
35
+ /** A nested delete (D-138): its rows and their cascade, as `Target.delete`. */
36
+ export type Drop = {
37
+ readonly kind: 'drop';
38
+ readonly model: string;
39
+ readonly rows: readonly Row[];
40
+ /** Where it sits in the write's data. */
41
+ readonly path: string;
42
+ /** The key the parent empties itself, which the cascade leaves. */
43
+ readonly except?: Except;
44
+ /** The rows the write updates, as `Model:id`: none may be deleted. */
45
+ readonly written: ReadonlySet<string>;
46
+ };
47
+ export type Step = Change | Move | Detach | Drop;
48
+ /**
49
+ * A check of the write, and what `NOT_FOUND` says when it fails (D-99), or
50
+ * the relation a failed `RELATION_RESTRICTED` names (D-102).
51
+ */
52
+ export type Checked = {
53
+ readonly check: Check;
54
+ readonly message: string;
55
+ readonly restricted?: {
56
+ readonly model: string;
57
+ readonly field: string;
58
+ };
59
+ };
60
+ /**
61
+ * What one write runs in: its store, its scope, the rows it changes and the
62
+ * checks its changes need.
63
+ */
64
+ export type Writing = {
65
+ readonly store: Store;
66
+ readonly scope: QueryScope;
67
+ readonly events: Event[];
68
+ readonly checks: Checked[];
69
+ /** The changes a moved row's `beforeUpdate` added to its data. */
70
+ readonly replan: (model: ModelInfo, id: string, draft: Draft) => Promise<Plan>;
71
+ /** A nested delete's changes, its cascade and hooks run (D-138). */
72
+ readonly drop: (step: Drop) => Promise<Change[]>;
73
+ };
74
+ /** Whether a model has update hooks, which need each row it updates. */
75
+ export declare function updated(model: ModelInfo): boolean;
76
+ /**
77
+ * The changes giving `rows` of `target` the key `to`, through the target's
78
+ * update hooks when it has some (D-88, D-90).
79
+ */
80
+ export declare function moved(step: Pick<Move, 'model' | 'field' | 'to'>, target: ModelInfo, rows: readonly Row[], writing: Writing): Promise<Change[]>;
81
+ /** The steps of one write, and the ids its `connect`s need to exist. */
82
+ export declare class Plan {
83
+ readonly steps: Step[];
84
+ readonly issues: Issue[];
85
+ /** The rows its nested `create`s make, their hooks run (D-128). */
86
+ readonly events: Event[];
87
+ /** The ids of the rows it creates, which a `connect` needs not find. */
88
+ readonly made: Set<string>;
89
+ /** The rows it updates, as `Model:id` (D-138). */
90
+ readonly written: Set<string>;
91
+ private readonly connected;
92
+ /** Connected ids, which a move updates and any other connect reads (D-124). */
93
+ connect(model: string, ids: readonly string[], trusted: boolean, operation: 'read' | 'update'): void;
94
+ /**
95
+ * Fails with the issues, then adds the write's checks: a target that is
96
+ * missing or that the write cannot reach is `NOT_FOUND` (D-86, D-99).
97
+ */
98
+ check(writing: Writing): Promise<void>;
99
+ /**
100
+ * The adapter's changes. A relation written from the other side runs the
101
+ * target's rules and update hooks on each row it changes, and leaves the
102
+ * rows the write cannot reach (D-78, D-86, D-88).
103
+ */
104
+ changes(writing: Writing): Promise<Change[]>;
105
+ }
106
+ export {};
107
+ //# sourceMappingURL=write-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-plan.d.ts","sourceRoot":"","sources":["../src/write-plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,170 @@
1
+ // The steps of one write, before access rules and `beforeFind` turn them
2
+ // into the adapter's changes (D-71, D-78, D-86).
3
+ import { and, recordOf } from './access.js';
4
+ import { PublicError } from './errors.js';
5
+ import { hasHook, runHook } from './hooks.js';
6
+ import { invalid } from './write-data.js';
7
+ import { byId, byIds, modelOf } from './query-where.js';
8
+ /** The shape of every id the kernel makes (D-22). */
9
+ const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
10
+ /** Whether a model has update hooks, which need each row it updates. */
11
+ export function updated(model) {
12
+ return (hasHook(model, 'beforeUpdate') ||
13
+ hasHook(model, 'afterUpdate') ||
14
+ hasHook(model, 'afterUpdateCommit'));
15
+ }
16
+ /** Each moved row's update hooks (D-88): the relation stays as asked. */
17
+ async function hooked(step, target, rows, writing) {
18
+ const { model, field, to } = step;
19
+ const changes = [];
20
+ for (const row of rows) {
21
+ const data = {
22
+ [field]: to === null ? { disconnect: true } : { connect: { id: to } },
23
+ };
24
+ await runHook(target, 'beforeUpdate', {
25
+ caller: writing.scope.caller,
26
+ record: recordOf(target, row),
27
+ data,
28
+ });
29
+ Reflect.deleteProperty(data, field);
30
+ const set = { [field]: to, updatedAt: new Date(Date.now()) };
31
+ changes.push({ kind: 'update', model, where: byId(row.id), set });
32
+ writing.events.push({
33
+ model: target,
34
+ operation: 'update',
35
+ id: row.id,
36
+ before: row,
37
+ });
38
+ if (Object.keys(data).length === 0)
39
+ continue;
40
+ const plan = await writing.replan(target, row.id, data);
41
+ writing.events.push(...plan.events);
42
+ await plan.check(writing);
43
+ changes.push(...(await plan.changes(writing)));
44
+ }
45
+ return changes;
46
+ }
47
+ /**
48
+ * The target rows a move changes, each allowed by the target's rules: it
49
+ * updates them (D-124).
50
+ */
51
+ async function move(step, writing) {
52
+ const { model, field, via, where, to, trusted } = step;
53
+ const { scope, store } = writing;
54
+ const target = modelOf(scope.models, model);
55
+ const filter = await scope.filter(model, trusted, 'update');
56
+ const access = trusted ? undefined : scope.access;
57
+ const set = { [field]: to, updatedAt: new Date(Date.now()) };
58
+ if (access === undefined && !updated(target)) {
59
+ return [{ kind: 'update', model, where: and(where, filter), set }];
60
+ }
61
+ const rows = await store.find({
62
+ model,
63
+ where: and(where, filter),
64
+ lock: true,
65
+ });
66
+ const data = {
67
+ [field]: to === null ? { disconnect: true } : { connect: { id: to } },
68
+ };
69
+ for (const record of rows)
70
+ access?.allow(target, 'update', { record, data, via });
71
+ return moved(step, target, rows, writing);
72
+ }
73
+ /**
74
+ * The changes giving `rows` of `target` the key `to`, through the target's
75
+ * update hooks when it has some (D-88, D-90).
76
+ */
77
+ export async function moved(step, target, rows, writing) {
78
+ if (updated(target))
79
+ return hooked(step, target, rows, writing);
80
+ if (rows.length === 0)
81
+ return [];
82
+ const { model, field, to } = step;
83
+ const set = { [field]: to, updatedAt: new Date(Date.now()) };
84
+ return [
85
+ { kind: 'update', model, where: byIds(rows.map((row) => row.id)), set },
86
+ ];
87
+ }
88
+ /** A join's `unlink`, kept to the targets the write reaches. */
89
+ async function detach(step, writing) {
90
+ const { target, targets, trusted, ...link } = step;
91
+ const filter = await writing.scope.filter(target, trusted);
92
+ const kept = filter === undefined
93
+ ? targets
94
+ : (await writing.store.find({
95
+ model: target,
96
+ where: and(byIds(targets), filter),
97
+ lock: true,
98
+ })).map((row) => row.id);
99
+ if (kept.length === 0)
100
+ return [];
101
+ return [{ ...link, kind: 'unlink', targets: kept }];
102
+ }
103
+ /** The steps of one write, and the ids its `connect`s need to exist. */
104
+ export class Plan {
105
+ steps = [];
106
+ issues = [];
107
+ /** The rows its nested `create`s make, their hooks run (D-128). */
108
+ events = [];
109
+ /** The ids of the rows it creates, which a `connect` needs not find. */
110
+ made = new Set();
111
+ /** The rows it updates, as `Model:id` (D-138). */
112
+ written = new Set();
113
+ connected = new Map();
114
+ /** Connected ids, which a move updates and any other connect reads (D-124). */
115
+ connect(model, ids, trusted, operation) {
116
+ const key = `${model}:${String(trusted)}:${operation}`;
117
+ const group = this.connected.get(key) ?? {
118
+ model,
119
+ trusted,
120
+ operation,
121
+ ids: new Set(),
122
+ };
123
+ for (const id of ids)
124
+ group.ids.add(id);
125
+ this.connected.set(key, group);
126
+ }
127
+ /**
128
+ * Fails with the issues, then adds the write's checks: a target that is
129
+ * missing or that the write cannot reach is `NOT_FOUND` (D-86, D-99).
130
+ */
131
+ async check(writing) {
132
+ if (this.issues.length > 0)
133
+ throw invalid(this.issues);
134
+ for (const group of this.connected.values()) {
135
+ const { model, trusted, operation } = group;
136
+ const ids = new Set([...group.ids].filter((id) => !this.made.has(id)));
137
+ if (ids.size === 0)
138
+ continue;
139
+ const message = `A connected ${model} does not exist.`;
140
+ // No row has an id the kernel cannot have made.
141
+ if (![...ids].every((id) => uuid.test(id))) {
142
+ throw new PublicError('NOT_FOUND', message);
143
+ }
144
+ const filter = await writing.scope.filter(model, trusted, operation);
145
+ const where = and(byIds([...ids]), filter);
146
+ writing.checks.push({ check: { model, where, count: ids.size }, message });
147
+ }
148
+ }
149
+ /**
150
+ * The adapter's changes. A relation written from the other side runs the
151
+ * target's rules and update hooks on each row it changes, and leaves the
152
+ * rows the write cannot reach (D-78, D-86, D-88).
153
+ */
154
+ async changes(writing) {
155
+ const changes = [];
156
+ for (const step of this.steps) {
157
+ if (step.kind === 'move')
158
+ changes.push(...(await move(step, writing)));
159
+ else if (step.kind === 'detach') {
160
+ changes.push(...(await detach(step, writing)));
161
+ }
162
+ else if (step.kind === 'drop') {
163
+ changes.push(...(await writing.drop(step)));
164
+ }
165
+ else
166
+ changes.push(step);
167
+ }
168
+ return changes;
169
+ }
170
+ }
@@ -0,0 +1,17 @@
1
+ import type { Change, Filter, Read, Row } from './adapter.ts';
2
+ import { RelationViolation } from './adapter.ts';
3
+ import { PublicError } from './errors.ts';
4
+ import type { Context } from './query-context.ts';
5
+ import type { Store } from './transaction.ts';
6
+ import type { Checked } from './write-plan.ts';
7
+ /** A relation the database refused, as the kernel's own checks word it. */
8
+ export declare function refused(error: RelationViolation): PublicError;
9
+ /**
10
+ * Writes `changes` after `checks`, each failed check its own error, then
11
+ * reads `read`.
12
+ */
13
+ export declare const checkedWrite: (store: Store, checks: readonly Checked[], changes: readonly Change[], read?: Read) => Promise<Row[]>;
14
+ export declare function guarded(run: Promise<Row[]>, checks: readonly Checked[]): Promise<Row[]>;
15
+ /** The stored row a single-row write targets, locked (D-99, D-104). */
16
+ export declare function target(context: Context, store: Store, where: Filter): Promise<Row>;
17
+ //# sourceMappingURL=write-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-target.d.ts","sourceRoot":"","sources":["../src/write-target.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAe,iBAAiB,EAAmB,MAAM,cAAc,CAAA;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAE9C,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,WAAW,CAa7D;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,UACjB,KAAK,UACJ,SAAS,OAAO,EAAE,WACjB,SAAS,MAAM,EAAE,SACnB,IAAI,KACT,OAAO,CAAC,GAAG,EAAE,CAQd,CAAA;AAEF,wBAAsB,OAAO,CAC5B,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,EACnB,MAAM,EAAE,SAAS,OAAO,EAAE,GACxB,OAAO,CAAC,GAAG,EAAE,CAAC,CAwBhB;AAED,uEAAuE;AACvE,wBAAsB,MAAM,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,GAAG,CAAC,CAad"}
@@ -0,0 +1,59 @@
1
+ import { CheckFailed, RelationViolation, UniqueViolation } from './adapter.js';
2
+ import { PublicError } from './errors.js';
3
+ /** A relation the database refused, as the kernel's own checks word it. */
4
+ export function refused(error) {
5
+ const { model, field } = error;
6
+ return error.restricted
7
+ ? new PublicError('RELATION_RESTRICTED', `${model}.${field} references a ${error.target} the delete removes.`, { details: { model, field }, cause: error })
8
+ : new PublicError('NOT_FOUND', `A connected ${error.target} does not exist.`, { cause: error });
9
+ }
10
+ /**
11
+ * Writes `changes` after `checks`, each failed check its own error, then
12
+ * reads `read`.
13
+ */
14
+ export const checkedWrite = async (store, checks, changes, read) => guarded(store.write({
15
+ checks: checks.map(({ check }) => check),
16
+ changes,
17
+ ...(read && { read }),
18
+ }), checks);
19
+ export async function guarded(run, checks) {
20
+ try {
21
+ return await run;
22
+ }
23
+ catch (error) {
24
+ const failed = error instanceof CheckFailed ? checks[error.index] : undefined;
25
+ if (failed?.restricted !== undefined) {
26
+ throw new PublicError('RELATION_RESTRICTED', failed.message, {
27
+ details: { ...failed.restricted },
28
+ cause: error,
29
+ });
30
+ }
31
+ if (failed !== undefined) {
32
+ throw new PublicError('NOT_FOUND', failed.message, { cause: error });
33
+ }
34
+ if (error instanceof RelationViolation)
35
+ throw refused(error);
36
+ if (error instanceof UniqueViolation) {
37
+ throw new PublicError('UNIQUE_VIOLATION', error.message, {
38
+ details: { model: error.model, fields: [...error.fields] },
39
+ cause: error,
40
+ });
41
+ }
42
+ throw error;
43
+ }
44
+ }
45
+ /** The stored row a single-row write targets, locked (D-99, D-104). */
46
+ export async function target(context, store, where) {
47
+ const { model } = context;
48
+ const [row] = await store.find({
49
+ model: model.name,
50
+ where,
51
+ take: 1,
52
+ lock: true,
53
+ });
54
+ // A row the caller cannot read is not found (D-72).
55
+ if (row === undefined) {
56
+ throw new PublicError('NOT_FOUND', `No ${model.name} matches.`);
57
+ }
58
+ return row;
59
+ }
@@ -0,0 +1,23 @@
1
+ import type { Nested } from './adapter.ts';
2
+ import type { Context } from './query-context.ts';
3
+ import type { Shape } from './query-args.ts';
4
+ import type { Commit, Store } from './transaction.ts';
5
+ /** The shape a single-row write returns, and each row of a bulk one's (D-149). */
6
+ export declare function returned(context: Context, found: ReadonlyMap<string, unknown>): Promise<{
7
+ readonly read: Nested;
8
+ readonly shape: Shape;
9
+ }>;
10
+ export declare function create(context: Context, store: Store, commits: Commit[], args: unknown): Promise<unknown>;
11
+ export declare function update(context: Context, store: Store, commits: Commit[], args: unknown): Promise<unknown>;
12
+ export declare function remove(context: Context, store: Store, commits: Commit[], args: unknown): Promise<unknown>;
13
+ /** The row an upsert returns, and whether it created it (D-142). */
14
+ export type Upserted = {
15
+ readonly row: unknown;
16
+ readonly created: boolean;
17
+ };
18
+ /**
19
+ * Updates the row `where` targets, or creates it when the caller finds
20
+ * none: the branch that runs is a plain update or create (D-142).
21
+ */
22
+ export declare function upsert(context: Context, store: Store, commits: Commit[], args: unknown): Promise<Upserted>;
23
+ //# sourceMappingURL=writes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writes.d.ts","sourceRoot":"","sources":["../src/writes.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,MAAM,EAAO,MAAM,cAAc,CAAA;AAMvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAIjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAO5C,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAUrD,kFAAkF;AAClF,wBAAsB,QAAQ,CAC7B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC,CAK3D;AAkED,wBAAsB,MAAM,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAMlB;AA6DD,wBAAsB,MAAM,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAiDlB;AAED,wBAAsB,MAAM,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CA0ElB;AAED,oEAAoE;AACpE,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAE3E;;;GAGG;AACH,wBAAsB,MAAM,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC,QAAQ,CAAC,CA8BnB"}