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