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,137 @@
1
+ // `groupBy`'s arguments and result (D-146): `having` (D-149) and `orderBy`
2
+ // name the fields of `by` or an aggregate, and each group has the keys the
3
+ // arguments ask for.
4
+ import type {
5
+ Aggregated,
6
+ AggregateArgs,
7
+ Aggregates,
8
+ DecimalKeys,
9
+ IsOrdered,
10
+ KeysWith,
11
+ Summed,
12
+ ValueOf,
13
+ } from './query-aggregate.ts'
14
+ import type { Exact } from './exact.ts'
15
+ import type { ScalarFilter } from './query-filter.ts'
16
+ import type { RecordOf, StoredRecord } from './record.ts'
17
+
18
+ type Direction = 'asc' | 'desc'
19
+ type Sorted<K extends PropertyKey> = {
20
+ readonly [P in K]?: Direction | undefined
21
+ }
22
+ /** A value's sort: an optional one puts its `null`s first or last (D-149). */
23
+ export type SortOrder<V> = null extends V
24
+ ? | Direction
25
+ | {
26
+ readonly sort: Direction
27
+ readonly nulls?: 'first' | 'last' | undefined
28
+ }
29
+ : Direction
30
+ /** The `one` relations the row stores (D-37). */
31
+ type StoredOnes<F, N> = Exclude<
32
+ keyof StoredRecord<F, N & string>,
33
+ keyof RecordOf<F>
34
+ >
35
+ /** What a group takes: a scalar an aggregate takes, or a stored relation. */
36
+ export type GroupField<F, N> = Aggregated<F> | StoredOnes<F, N>
37
+
38
+ type GroupOrderBy<F, N, B extends PropertyKey> = {
39
+ readonly [K in B]?: SortOrder<GroupValue<F, N, K>> | undefined
40
+ } & {
41
+ readonly _count?:
42
+ | Direction
43
+ | ({ readonly _all?: Direction | undefined } & Sorted<Aggregated<F>>)
44
+ | undefined
45
+ readonly _sum?: Sorted<Summed<F>> | undefined
46
+ readonly _avg?: Sorted<Summed<F>> | undefined
47
+ readonly _min?: Sorted<KeysWith<F, number | string | Date>> | undefined
48
+ readonly _max?: Sorted<KeysWith<F, number | string | Date>> | undefined
49
+ }
50
+
51
+ /** A value's filter: an id's has no `mode`, a decimal's or a day's compares. */
52
+ type ValueFilter<F, K> = K extends 'id'
53
+ ? Omit<ScalarFilter<string>, 'mode'>
54
+ : ScalarFilter<ValueOf<F, K>, K extends keyof F ? IsOrdered<F[K]> : false>
55
+ type Filtered<V, Ordered = false> = V | ScalarFilter<V, Ordered> | undefined
56
+
57
+ /** Field `K`'s aggregates, each filtered as the value it gives (D-149). */
58
+ type AggregateFilters<F, K> = {
59
+ readonly _count?: Filtered<number>
60
+ } & (K extends Summed<F>
61
+ ? {
62
+ readonly _sum?: K extends DecimalKeys<F>
63
+ ? Filtered<string, true>
64
+ : Filtered<number>
65
+ readonly _avg?: Filtered<number>
66
+ }
67
+ : unknown) &
68
+ (K extends KeysWith<F, number | string | Date>
69
+ ? {
70
+ readonly _min?: ValueOf<F, K> | ValueFilter<F, K> | undefined
71
+ readonly _max?: ValueOf<F, K> | ValueFilter<F, K> | undefined
72
+ }
73
+ : unknown)
74
+
75
+ /** The groups kept: a field of `by` by its value, any field by its aggregates. */
76
+ type Having<F, N, B extends PropertyKey> = {
77
+ readonly [K in Aggregated<F>]?:
78
+ | (K extends B
79
+ ? GroupValue<F, N, K> | (ValueFilter<F, K> & AggregateFilters<F, K>)
80
+ : AggregateFilters<F, K>)
81
+ | undefined
82
+ } & {
83
+ readonly AND?: Having<F, N, B> | readonly Having<F, N, B>[] | undefined
84
+ readonly OR?: readonly Having<F, N, B>[] | undefined
85
+ readonly NOT?: Having<F, N, B> | readonly Having<F, N, B>[] | undefined
86
+ }
87
+
88
+ export type GroupByArgs<
89
+ F,
90
+ N,
91
+ B extends GroupField<F, N> = GroupField<F, N>,
92
+ > = AggregateArgs<F> & {
93
+ readonly by: readonly [B, ...B[]]
94
+ readonly having?: Having<F, N, B> | undefined
95
+ readonly orderBy?:
96
+ GroupOrderBy<F, N, B> | readonly GroupOrderBy<F, N, B>[] | undefined
97
+ readonly skip?: number | undefined
98
+ readonly take?: number | undefined
99
+ }
100
+
101
+ /**
102
+ * Arguments `A`, checked against the fields of their `by`: no unknown key,
103
+ * and a `having` that fits them, which `Exact` does not check as it skips
104
+ * values: a value for a field outside `by` would pass.
105
+ */
106
+ export type GroupChecked<
107
+ F,
108
+ N,
109
+ A extends { readonly by: readonly unknown[] },
110
+ > = Exact<A, GroupByArgs<F, N, A['by'][number] & GroupField<F, N>>> &
111
+ (A extends { readonly having: infer H }
112
+ ? [H] extends [Having<F, N, A['by'][number] & PropertyKey> | undefined]
113
+ ? unknown
114
+ : {
115
+ readonly having: {
116
+ readonly error: 'a field outside by takes aggregates only'
117
+ }
118
+ }
119
+ : unknown)
120
+
121
+ /** A relation's target id, `null` when unset or unreadable (D-86). */
122
+ type GroupValue<F, N, K> =
123
+ K extends StoredOnes<F, N>
124
+ ? { readonly id: string } | null
125
+ : K extends keyof StoredRecord<F, N & string>
126
+ ? StoredRecord<F, N & string>[K]
127
+ : never
128
+
129
+ /** What `groupBy` returns for the arguments `A`. */
130
+ export type Groups<F, N, A> = A extends {
131
+ readonly by: readonly (infer B)[]
132
+ }
133
+ ? ({ readonly [K in B & PropertyKey]: GroupValue<F, N, K> } & Aggregates<
134
+ F,
135
+ A
136
+ >)[]
137
+ : never
@@ -0,0 +1,90 @@
1
+ // A json field's path filter (D-149), Prisma's PostgreSQL JSON filter in
2
+ // camelCase: `{ meta: { path: ['author', 'name'], equals: 'Ada' } }`.
3
+ import type { Condition } from './adapter.ts'
4
+ import { InvalidQuery } from './errors.ts'
5
+ import {
6
+ clean,
7
+ cleanText,
8
+ entries,
9
+ isJson,
10
+ isObject,
11
+ unknownKey,
12
+ } from './query-values.ts'
13
+
14
+ const json = (operand: unknown, at: string): unknown => {
15
+ if (isJson(operand)) return operand
16
+ throw new InvalidQuery(at, `${at} must be a JSON value.`)
17
+ }
18
+
19
+ const operands: {
20
+ readonly [operator: string]: (operand: unknown, at: string) => unknown
21
+ } = {
22
+ equals: json,
23
+ not: json,
24
+ ...Object.fromEntries(
25
+ ['lt', 'lte', 'gt', 'gte'].map((operator) => [
26
+ operator,
27
+ (operand: unknown, at: string) => {
28
+ if (typeof operand === 'string' && clean(operand)) return operand
29
+ if (typeof operand === 'number' && Number.isFinite(operand)) {
30
+ return operand
31
+ }
32
+ throw new InvalidQuery(at, `${at} must be a number or a string.`)
33
+ },
34
+ ]),
35
+ ),
36
+ ...Object.fromEntries(
37
+ ['stringContains', 'stringStartsWith', 'stringEndsWith'].map((operator) => [
38
+ operator,
39
+ cleanText,
40
+ ]),
41
+ ),
42
+ mode: (operand, at) => {
43
+ if (operand === 'insensitive') return operand
44
+ throw new InvalidQuery(at, `${at} must be 'insensitive'.`)
45
+ },
46
+ // A value is a list of one, as in Prisma.
47
+ arrayContains: (operand, at) => {
48
+ const value = json(operand, at)
49
+ if (!Array.isArray(value)) return [value]
50
+ const items: readonly unknown[] = value
51
+ return items
52
+ },
53
+ arrayStartsWith: json,
54
+ arrayEndsWith: json,
55
+ }
56
+
57
+ /** Whether a json field's filter is a path filter, which names `path`. */
58
+ export const isPathFilter = (value: unknown): boolean =>
59
+ isObject(value) && value['path'] !== undefined
60
+
61
+ /**
62
+ * A path filter of `model`'s json field, `undefined` when it has no operator,
63
+ * as `mode` alone (D-140).
64
+ */
65
+ export function jsonCondition(
66
+ model: string,
67
+ value: unknown,
68
+ path: string,
69
+ ): Condition | undefined {
70
+ const condition: { [operator: string]: unknown } = {}
71
+ for (const [operator, operand] of entries(value, path)) {
72
+ const at = `${path}.${operator}`
73
+ if (operator === 'path') {
74
+ if (!Array.isArray(operand)) {
75
+ throw new InvalidQuery(at, `${at} must be a list of keys.`)
76
+ }
77
+ condition['path'] = operand.map((key: unknown, index) =>
78
+ cleanText(key, `${at}.${index}`),
79
+ )
80
+ continue
81
+ }
82
+ const check = operands[operator]
83
+ if (check === undefined) throw unknownKey(at, model)
84
+ condition[operator] = check(operand, at)
85
+ }
86
+ const tested = Object.keys(condition).filter(
87
+ (operator) => operator !== 'path' && operator !== 'mode',
88
+ )
89
+ return tested.length === 0 ? undefined : condition
90
+ }
@@ -0,0 +1,158 @@
1
+ // A read's `orderBy` (D-20): the row's fields, a field reached through
2
+ // `one` relations, or the number of a `many` relation's rows (D-134), or
3
+ // how well the row matches the read's `_search` (D-148).
4
+ import type { Order, Sort, Step } from './adapter.ts'
5
+ import { InvalidQuery } from './errors.ts'
6
+ import type { ModelInfo } from './schema.ts'
7
+ import { searchedFields } from './query-search.ts'
8
+ import type { Models, Scope } from './query-where.ts'
9
+ import {
10
+ entries,
11
+ isObject,
12
+ modelOf,
13
+ systemFields,
14
+ unknownKey,
15
+ } from './query-where.ts'
16
+
17
+ export function direction(value: unknown, path: string): 'asc' | 'desc' {
18
+ if (value !== 'asc' && value !== 'desc') {
19
+ throw new InvalidQuery(path, `${path} must be 'asc' or 'desc'.`)
20
+ }
21
+ return value
22
+ }
23
+
24
+ /**
25
+ * A sort key's value: a direction, or on an optional field `{ sort, nulls }`
26
+ * (D-149).
27
+ */
28
+ export function sortOf(value: unknown, path: string, optional: boolean): Sort {
29
+ if (!isObject(value)) return { direction: direction(value, path) }
30
+ if (!optional) {
31
+ throw new InvalidQuery(
32
+ path,
33
+ `${path} is required: it sorts by 'asc' or 'desc'.`,
34
+ )
35
+ }
36
+ const { sort, nulls, ...rest } = value
37
+ const [extra] = Object.keys(rest)
38
+ if (extra !== undefined) {
39
+ throw new InvalidQuery(
40
+ `${path}.${extra}`,
41
+ `${path} takes sort and nulls only.`,
42
+ )
43
+ }
44
+ const sorted = direction(sort, `${path}.sort`)
45
+ if (nulls === undefined) return { direction: sorted }
46
+ if (nulls !== 'first' && nulls !== 'last') {
47
+ const at = `${path}.nulls`
48
+ throw new InvalidQuery(at, `${at} must be 'first' or 'last'.`)
49
+ }
50
+ return { direction: sorted, nulls }
51
+ }
52
+
53
+ /** A scalar field that sorts: no json, no list, no plugin field (D-94). */
54
+ function sortable(model: ModelInfo, field: string, path: string): void {
55
+ const known = model.fields.get(field)
56
+ if (known === undefined) {
57
+ if (!systemFields.includes(field)) throw unknownKey(path, model.name)
58
+ return
59
+ }
60
+ if (known.kind !== 'scalar') return
61
+ if (known.type !== 'json' && !known.list && known.codec === undefined) {
62
+ return
63
+ }
64
+ const kind =
65
+ known.codec !== undefined
66
+ ? `a ${known.codec.plugin} field`
67
+ : known.type === 'json'
68
+ ? 'a json field'
69
+ : 'a list'
70
+ throw new InvalidQuery(path, `${path} is ${kind}, which cannot be sorted.`)
71
+ }
72
+
73
+ /** The sort keys of one object of `orderBy`, in its keys' order. */
74
+ function keys(
75
+ models: Models,
76
+ model: ModelInfo,
77
+ part: unknown,
78
+ at: string,
79
+ scope: Scope | undefined,
80
+ through: readonly Step[],
81
+ search: string | undefined,
82
+ ): Order[] {
83
+ const order: Order[] = []
84
+ const path = through.length === 0 ? {} : { through }
85
+ const searched = searchedFields(model.fields.values()).length > 0
86
+ for (const [field, value] of entries(part, at)) {
87
+ const where = `${at}.${field}`
88
+ if (field === '_relevance' && through.length === 0 && searched) {
89
+ if (search === undefined) {
90
+ throw new InvalidQuery(
91
+ where,
92
+ `${where} sorts by the _search at the top of the read's where, which has none.`,
93
+ )
94
+ }
95
+ order.push({
96
+ field,
97
+ direction: direction(value, where),
98
+ relevance: search,
99
+ })
100
+ continue
101
+ }
102
+ sortable(model, field, where)
103
+ scope?.check(model, field, where, 'sorts')
104
+ const known = model.fields.get(field)
105
+ if (known === undefined || known.kind === 'scalar') {
106
+ const optional = known?.optional === true
107
+ order.push({ field, ...sortOf(value, where, optional), ...path })
108
+ continue
109
+ }
110
+ const target = modelOf(models, known.target)
111
+ if (known.kind === 'one') {
112
+ const readable = scope?.readable(target.name)
113
+ const step = { relation: field, ...(readable && { where: readable }) }
114
+ const next = [...through, step]
115
+ order.push(...keys(models, target, value, where, scope, next, search))
116
+ continue
117
+ }
118
+ const [only, ...rest] = isObject(value) ? Object.entries(value) : []
119
+ if (only?.[0] !== '_count' || rest.length > 0) {
120
+ throw new InvalidQuery(
121
+ where,
122
+ `${where} is a many relation, sorted by { _count: 'asc' | 'desc' }.`,
123
+ )
124
+ }
125
+ const sorted = direction(only[1], `${where}._count`)
126
+ const count = scope?.related(model, known) ?? {}
127
+ order.push({ field, direction: sorted, ...path, count })
128
+ }
129
+ return order
130
+ }
131
+
132
+ /**
133
+ * A read's sort keys, ending with `id` so the order is total; `search` is
134
+ * the text of its where's `_search`.
135
+ */
136
+ export function orderBy(
137
+ models: Models,
138
+ model: ModelInfo,
139
+ value: unknown,
140
+ path: string,
141
+ scope: Scope | undefined,
142
+ search?: string,
143
+ ): Order[] {
144
+ const parts = Array.isArray(value) ? value : [value]
145
+ const order = parts.flatMap((part: unknown, index) => {
146
+ const at = Array.isArray(value) ? `${path}.${index}` : path
147
+ return keys(models, model, part, at, scope, [], search)
148
+ })
149
+ // The order is total, so a cursor and a page are exact.
150
+ const total = order.some(
151
+ (each) =>
152
+ each.field === 'id' &&
153
+ each.through === undefined &&
154
+ each.count === undefined,
155
+ )
156
+ if (!total) order.push({ field: 'id', direction: 'asc' })
157
+ return order
158
+ }
@@ -0,0 +1,73 @@
1
+ // A row as a query returns it (D-149): the fields of its shape, its
2
+ // relations below, its virtuals as the read computed them (D-151), without
3
+ // the fields the caller's read rules hide (D-44).
4
+ import type { Shape } from './query-args.ts'
5
+ import type { ModelInfo } from './schema.ts'
6
+ import { isObject } from './query-where.ts'
7
+
8
+ /** The fields of a row its field read rules hide (D-44). */
9
+ export type Hidden = (
10
+ model: ModelInfo,
11
+ row: { readonly [field: string]: unknown },
12
+ fields: Iterable<string>,
13
+ ) => ReadonlySet<string>
14
+
15
+ /** The values of each row's virtuals, as a read computed them (D-151). */
16
+ export type Computed = WeakMap<object, Map<string, unknown>>
17
+
18
+ /** The virtuals of `shape` a field read rule hides, its own or a need's. */
19
+ export function unseenVirtuals(
20
+ shape: Shape,
21
+ row: { readonly [field: string]: unknown },
22
+ hidden: Hidden | undefined,
23
+ ): ReadonlySet<string> {
24
+ const reads = [...shape.virtuals.values()].map(({ info }) => info)
25
+ const names = reads.flatMap((info) => [info.name, ...info.needs])
26
+ const unseen = hidden?.(shape.model, row, names)
27
+ return new Set(
28
+ reads
29
+ .filter((info) => [info.name, ...info.needs].some((n) => unseen?.has(n)))
30
+ .map((info) => info.name),
31
+ )
32
+ }
33
+
34
+ /** A row as the query returns it, through the shape, without hidden fields. */
35
+ export function project(
36
+ row: unknown,
37
+ shape: Shape,
38
+ hidden?: Hidden,
39
+ computed?: Computed,
40
+ ): unknown {
41
+ if (Array.isArray(row)) {
42
+ return row.map((item: unknown) => project(item, shape, hidden, computed))
43
+ }
44
+ if (!isObject(row)) return null
45
+ const names = [...shape.fields, ...shape.relations.keys(), ...shape.counts]
46
+ const unseen = hidden?.(shape.model, row, names)
47
+ const result: { [field: string]: unknown } = {}
48
+ for (const field of shape.fields) {
49
+ if (!unseen?.has(field)) result[field] = row[field] ?? null
50
+ }
51
+ for (const [name, nested] of shape.relations) {
52
+ if (!unseen?.has(name)) {
53
+ result[name] = project(row[name], nested, hidden, computed)
54
+ }
55
+ }
56
+ const values = computed?.get(row)
57
+ const unseenVirtual =
58
+ shape.virtuals.size > 0 ? unseenVirtuals(shape, row, hidden) : new Set()
59
+ for (const name of shape.virtuals.keys()) {
60
+ if (!unseenVirtual.has(name)) result[name] = values?.get(name) ?? null
61
+ }
62
+ if (shape.counts.length > 0) {
63
+ const counts: { readonly [name: string]: unknown } = isObject(row['_count'])
64
+ ? row['_count']
65
+ : {}
66
+ result['_count'] = Object.fromEntries(
67
+ shape.counts
68
+ .filter((name) => !unseen?.has(name))
69
+ .map((name) => [name, counts[name] ?? 0]),
70
+ )
71
+ }
72
+ return result
73
+ }
@@ -0,0 +1,31 @@
1
+ // Full-text search (D-148): `_search` keeps the rows whose searched fields
2
+ // match a text, `_relevance` sorts them by how well.
3
+ import type { SchemaField } from './adapter.ts'
4
+ import type { fieldType } from './symbols.ts'
5
+
6
+ /** The fields of `F` built with `search: true`. */
7
+ type Searched<F> = {
8
+ [K in keyof F]: F[K] extends {
9
+ readonly [fieldType]: { readonly options: { readonly search: true } }
10
+ }
11
+ ? K
12
+ : never
13
+ }[keyof F]
14
+
15
+ /** A model with a searched field takes `_search` in its `where`. */
16
+ export type SearchWhere<F> = [Searched<F>] extends [never]
17
+ ? unknown
18
+ : { readonly _search?: string | undefined }
19
+
20
+ /** And `_relevance` at the top of its `orderBy`. */
21
+ export type Relevance<F> = [Searched<F>] extends [never]
22
+ ? unknown
23
+ : { readonly _relevance?: 'asc' | 'desc' | undefined }
24
+
25
+ /** The names of a model's searched fields, in declaration order. */
26
+ export const searchedFields = (
27
+ fields: Iterable<SchemaField>,
28
+ ): readonly string[] =>
29
+ [...fields].flatMap((field) =>
30
+ field.kind === 'scalar' && field.search === true ? [field.name] : [],
31
+ )
@@ -0,0 +1,131 @@
1
+ // The values a query or a write may hold, checked against a field (D-94,
2
+ // D-139): what PostgreSQL stores as written, in each scalar type.
3
+ import type { ScalarType } from './adapter.ts'
4
+ import { decimalIssue } from './decimal.ts'
5
+ import { InvalidQuery } from './errors.ts'
6
+ import type { FieldInfo } from './schema.ts'
7
+
8
+ export type Scalar = Extract<FieldInfo, { readonly kind: 'scalar' }>
9
+
10
+ /** What a scalar value is checked against (D-139). */
11
+ export type ScalarCheck = Pick<
12
+ Scalar,
13
+ 'type' | 'list' | 'integer' | 'values' | 'precision' | 'scale'
14
+ >
15
+
16
+ /** An integer field's values (D-132). */
17
+ export const isInteger = (value: unknown): boolean =>
18
+ Number.isInteger(value) &&
19
+ Number(value) >= -2_147_483_648 &&
20
+ Number(value) <= 2_147_483_647
21
+
22
+ export function isObject(
23
+ value: unknown,
24
+ ): value is { readonly [key: string]: unknown } {
25
+ return (
26
+ typeof value === 'object' &&
27
+ value !== null &&
28
+ !Array.isArray(value) &&
29
+ !(value instanceof Date)
30
+ )
31
+ }
32
+
33
+ /** The entries of an argument object, without `undefined` (D-84). */
34
+ export function entries(value: unknown, path: string): [string, unknown][] {
35
+ if (!isObject(value))
36
+ throw new InvalidQuery(path, `${path} must be an object.`)
37
+ return Object.entries(value).filter(([, item]) => item !== undefined)
38
+ }
39
+
40
+ /** A string PostgreSQL stores as written: no NUL, no lone surrogate (D-94). */
41
+ export const clean = (value: string): boolean =>
42
+ value.isWellFormed() && !value.includes('\0')
43
+
44
+ export const cleanText = (value: unknown, at: string): string => {
45
+ if (typeof value === 'string' && clean(value)) return value
46
+ throw new InvalidQuery(at, `${at} must be a clean string.`)
47
+ }
48
+
49
+ /** A date of years 1 to 9999 in UTC, which PostgreSQL reads back (D-94). */
50
+ export const storedDate = (value: unknown): value is Date => {
51
+ const year = value instanceof Date ? value.getUTCFullYear() : Number.NaN
52
+ return year >= 1 && year <= 9999
53
+ }
54
+
55
+ /** A real day of years 1 to 9999, written `'2026-07-31'` (D-150). */
56
+ export function isDay(value: unknown): boolean {
57
+ const match =
58
+ typeof value === 'string' && /^(\d{4})-(\d{2})-(\d{2})$/.exec(value)
59
+ if (!match) return false
60
+ const [year = 0, month = 0, day = 0] = match.slice(1).map(Number)
61
+ const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
62
+ const days =
63
+ month === 2 ? (leap ? 29 : 28) : [4, 6, 9, 11].includes(month) ? 30 : 31
64
+ return year >= 1 && month >= 1 && month <= 12 && day >= 1 && day <= days
65
+ }
66
+
67
+ /** JSON data whose strings and keys are clean (D-94). */
68
+ export function isJson(value: unknown): boolean {
69
+ if (value === null || typeof value === 'boolean') return true
70
+ if (typeof value === 'string') return clean(value)
71
+ if (typeof value === 'number') return Number.isFinite(value)
72
+ if (Array.isArray(value)) return value.every(isJson)
73
+ if (!isObject(value) || Object.getPrototypeOf(value) !== Object.prototype) {
74
+ return false
75
+ }
76
+ return Object.entries(value).every(
77
+ ([key, item]) => clean(key) && isJson(item),
78
+ )
79
+ }
80
+
81
+ export function unknownKey(path: string, model: string): InvalidQuery {
82
+ return new InvalidQuery(path, `Unknown key '${path}' on ${model}.`)
83
+ }
84
+
85
+ export const integerRange = 'from -2,147,483,648 to 2,147,483,647'
86
+
87
+ /** Each scalar type's check, and its message when a value does not fit. */
88
+ const misfits: {
89
+ readonly [T in ScalarType]: (
90
+ value: unknown,
91
+ field: ScalarCheck,
92
+ ) => string | undefined
93
+ } = {
94
+ string: (value) => {
95
+ if (typeof value !== 'string') return 'Expected a string.'
96
+ return clean(value)
97
+ ? undefined
98
+ : 'Expected a string without a NUL or a lone surrogate.'
99
+ },
100
+ number: (value, field) => {
101
+ if (field.integer !== true) {
102
+ return typeof value === 'number' && Number.isFinite(value)
103
+ ? undefined
104
+ : 'Expected a finite number.'
105
+ }
106
+ return isInteger(value)
107
+ ? undefined
108
+ : `Expected a whole number ${integerRange}.`
109
+ },
110
+ decimal: decimalIssue,
111
+ boolean: (value) =>
112
+ typeof value === 'boolean' ? undefined : 'Expected a boolean.',
113
+ dateTime: (value) =>
114
+ storedDate(value) ? undefined : 'Expected a Date of years 1 to 9999.',
115
+ date: (value) =>
116
+ isDay(value) ? undefined : "Expected a day written '2026-07-31'.",
117
+ json: (value) =>
118
+ isJson(value)
119
+ ? undefined
120
+ : 'Expected JSON data, its strings without a NUL or a lone surrogate.',
121
+ enum: (value, field) =>
122
+ typeof value === 'string' && (field.values ?? []).includes(value)
123
+ ? undefined
124
+ : `Expected one of ${(field.values ?? []).join(', ')}.`,
125
+ }
126
+
127
+ /** The message of a value that does not fit `field`, if it does not. */
128
+ export const misfit = (
129
+ value: unknown,
130
+ field: ScalarCheck,
131
+ ): string | undefined => misfits[field.type](value, field)