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,352 @@
1
+ // What `defineModel`, the field builders and `definePlugin` record at run
2
+ // time: the values behind the types.
3
+ import type { ScalarType } from './adapter.ts'
4
+ import type { Enum, StandardSchema } from './fields.ts'
5
+ import type { Manifest, OutputForm } from './plugin.ts'
6
+ import type { Standard } from './validation.ts'
7
+ import { Refused } from './errors.ts'
8
+ import { isObject } from './query-where.ts'
9
+ import { standardOf } from './validation.ts'
10
+
11
+ export type FieldOptions = { readonly [option: string]: unknown }
12
+
13
+ /** What a plugin field type adds to its stored field (D-122). */
14
+ export type Codec = {
15
+ readonly plugin: string
16
+ /** The field type's own options this field took (D-152). */
17
+ readonly options: { readonly [key: string]: unknown }
18
+ /** The stored value of the rows stored before the field (D-152). */
19
+ readonly filled?: unknown
20
+ readonly decode: (stored: unknown) => unknown
21
+ readonly encode: (value: unknown) => unknown
22
+ readonly schema: Standard
23
+ readonly toJSON: (value: unknown) => unknown
24
+ /** What `toJSON` gives, as a Standard Schema (D-152). */
25
+ readonly shape?: StandardSchema
26
+ }
27
+
28
+ /** A field as its builder recorded it. */
29
+ export type FieldDefinition =
30
+ | {
31
+ readonly kind: 'scalar'
32
+ readonly type: ScalarType
33
+ readonly values?: readonly string[]
34
+ /** The `defineEnum` its values are, if one (D-150). */
35
+ readonly enum?: Enum
36
+ readonly options: FieldOptions
37
+ readonly rules?: object
38
+ /** A plugin field type's, whose `type` is the stored one (D-122). */
39
+ readonly codec?: Codec
40
+ }
41
+ | {
42
+ readonly kind: 'one' | 'many'
43
+ readonly target: () => unknown
44
+ readonly options: FieldOptions
45
+ readonly rules?: object
46
+ }
47
+ | {
48
+ /** Computed on read (D-151): its schema, or a thunk of its output. */
49
+ readonly kind: 'virtual'
50
+ readonly output: unknown
51
+ readonly options: FieldOptions
52
+ readonly rules?: object
53
+ }
54
+
55
+ export type IndexDefinition = {
56
+ readonly fields: readonly string[]
57
+ readonly unique?: boolean
58
+ /** A trigram index, for `contains` and `mode: 'insensitive'` (D-141). */
59
+ readonly contains?: true
60
+ }
61
+
62
+ export type ModelDefinition = {
63
+ readonly name: string
64
+ readonly description: string | undefined
65
+ readonly dbName: string | undefined
66
+ readonly fields: ReadonlyMap<string, FieldDefinition>
67
+ readonly indexes: readonly IndexDefinition[]
68
+ /** The plugins of its `use`, in that order (D-89). */
69
+ readonly uses: readonly PluginUse[]
70
+ /** Its operations, which a field type's plugin is given (D-152). */
71
+ readonly queries: object
72
+ /** What `.with` received, once (D-07). */
73
+ rules: object | undefined
74
+ }
75
+
76
+ /** A plugin's code, as `definePlugin` checked it (D-57, D-89). */
77
+ export type PluginCode = {
78
+ readonly name: string
79
+ readonly manifest: Manifest
80
+ readonly requires: readonly string[]
81
+ readonly optional: readonly string[]
82
+ readonly hooks: { readonly [hook: string]: Function | undefined }
83
+ readonly redirect: Function | undefined
84
+ readonly commands: Function | undefined
85
+ readonly api: Function | undefined
86
+ readonly addedFields: Function | undefined
87
+ /** The indexes it adds to the models that use it (D-122). */
88
+ readonly addedIndexes: readonly IndexDefinition[]
89
+ readonly start: Function | undefined
90
+ readonly stop: Function | undefined
91
+ /** Who sends a request, if the plugin knows it (D-112). */
92
+ readonly identify: Function | undefined
93
+ /** The keys it gives a user caller (D-121). */
94
+ readonly caller: Function | undefined
95
+ /** Takes what each commit changed (D-154). */
96
+ readonly changed: Function | undefined
97
+ readonly missed: Function | undefined
98
+ /** Its own models, which no plugin is given (D-110). */
99
+ readonly models: readonly unknown[]
100
+ /** Builds its functions when the application starts (D-113). */
101
+ readonly functions: Function | undefined
102
+ /** The APIs of the plugins it needs, read when used (D-89). */
103
+ readonly plugins: object
104
+ }
105
+
106
+ /** A plugin's entry in `use`, or its value in `plugins`, with its options. */
107
+ export type PluginRecord = {
108
+ readonly code: PluginCode
109
+ readonly options: object
110
+ }
111
+
112
+ /** A plugin in a model's `use`, and what its code gets of the model. */
113
+ export type PluginUse = {
114
+ readonly code: PluginCode
115
+ readonly model: {
116
+ readonly name: string
117
+ readonly queries: object
118
+ readonly options: object
119
+ readonly plugins: object
120
+ }
121
+ }
122
+
123
+ /** A model or a type, as an output holds it (D-109). */
124
+ export type NamedOutput =
125
+ | {
126
+ readonly kind: 'model'
127
+ /** The `defineModel` value. */
128
+ readonly value: object
129
+ readonly name: string
130
+ }
131
+ | { readonly kind: 'type'; readonly type: TypeDefinition }
132
+
133
+ /** A function's or a type's output, as the kernel reads it (D-109). */
134
+ export type Output =
135
+ | { readonly kind: 'schema'; readonly schema: StandardSchema }
136
+ | (NamedOutput & { readonly form: OutputForm })
137
+
138
+ export type TypeDefinition = {
139
+ readonly name: string
140
+ readonly description: string | undefined
141
+ readonly fields: ReadonlyMap<string, Output>
142
+ }
143
+
144
+ export type FunctionDefinition = {
145
+ readonly name: string
146
+ readonly description: string | undefined
147
+ readonly query: boolean
148
+ readonly input: StandardSchema | undefined
149
+ readonly output: Output | undefined
150
+ /** The plugins of its `use` (D-27). */
151
+ readonly uses: readonly PluginRecord[]
152
+ readonly run: Function
153
+ /** What `.with` received, once. */
154
+ rules: object | undefined
155
+ }
156
+
157
+ export const fieldDefinitions: WeakMap<object, FieldDefinition> = new WeakMap()
158
+ export const modelDefinitions: WeakMap<object, ModelDefinition> = new WeakMap()
159
+ /** The application values `plugin()` returned. */
160
+ export const pluginValues: WeakMap<object, PluginRecord> = new WeakMap()
161
+ /** The model entries `use()` returned. */
162
+ export const pluginEntries: WeakMap<object, PluginRecord> = new WeakMap()
163
+ export const typeDefinitions: WeakMap<object, TypeDefinition> = new WeakMap()
164
+ export const functionDefinitions: WeakMap<object, FunctionDefinition> =
165
+ new WeakMap()
166
+ /** What each `X.nullable()` returned stands for (D-109). */
167
+ export const nullables: WeakMap<object, NamedOutput> = new WeakMap()
168
+
169
+ /** `X.nullable()`, for a model or a type. */
170
+ export function nullable(named: NamedOutput): object {
171
+ const marker = Object.freeze({})
172
+ nullables.set(marker, named)
173
+ return marker
174
+ }
175
+
176
+ /** The name a `define*` call takes: a non-empty string. */
177
+ export function nameOf(name: unknown, by: string): string {
178
+ if (typeof name !== 'string' || name === '') {
179
+ throw new Error(`${by} takes a name.`)
180
+ }
181
+ return name
182
+ }
183
+
184
+ /** A `description` (D-150): a non-empty string, when given. */
185
+ export function describedBy(
186
+ description: unknown,
187
+ of: string,
188
+ ): string | undefined {
189
+ if (description === undefined) return undefined
190
+ if (typeof description !== 'string' || description === '') {
191
+ throw new Error(`${of}: description is a non-empty string.`)
192
+ }
193
+ return description
194
+ }
195
+
196
+ /**
197
+ * The `.with` of a model or a function: its rules, in one call, read when
198
+ * they apply, so it returns the same value.
199
+ */
200
+ export const withOnce =
201
+ (name: string, record: { rules: object | undefined }, value: () => object) =>
202
+ (rules: unknown): object => {
203
+ if (record.rules !== undefined) {
204
+ throw new Refused(`${name} takes its rules in one \`.with\`.`)
205
+ }
206
+ if (!isObject(rules)) throw new Refused(`${name}.with takes an object.`)
207
+ record.rules = rules
208
+ return value()
209
+ }
210
+
211
+ /** A field value, keyed by its definition; `.with` takes its rules once. */
212
+ export function build(definition: FieldDefinition): object {
213
+ describedBy(definition.options['description'], 'A field')
214
+ const field = {
215
+ with: (rules: object): object => {
216
+ if (definition.rules !== undefined) {
217
+ throw new Error('A field takes its rules in one `.with`.')
218
+ }
219
+ return build({ ...definition, rules })
220
+ },
221
+ }
222
+ fieldDefinitions.set(field, definition)
223
+ return field
224
+ }
225
+
226
+ /** A field that `list: true` makes a list (D-120, D-127). */
227
+ function listable(
228
+ type: ScalarType,
229
+ options: FieldOptions = {},
230
+ values?: readonly string[],
231
+ shared?: Enum,
232
+ ): object {
233
+ const list = options['list'] === true
234
+ if (list) {
235
+ if (options['optional'] !== undefined) {
236
+ throw new Error(
237
+ 'A list is never null: left out, it is []. Remove optional.',
238
+ )
239
+ }
240
+ if (options['unique'] !== undefined) {
241
+ throw new Error('A list cannot be unique.')
242
+ }
243
+ }
244
+ if (options['search'] !== undefined && (list || type !== 'string')) {
245
+ throw new Error('search takes a string field that is not a list.')
246
+ }
247
+ return build({
248
+ kind: 'scalar',
249
+ type,
250
+ ...(values === undefined ? {} : { values }),
251
+ ...(shared === undefined ? {} : { enum: shared }),
252
+ options,
253
+ })
254
+ }
255
+
256
+ /** A field no list holds yet (D-127). */
257
+ const scalar =
258
+ (type: ScalarType) =>
259
+ (options?: FieldOptions): object => {
260
+ if (options?.['list'] !== undefined) {
261
+ throw new Error(
262
+ `f.${type}() makes no list: a list holds strings, numbers or enum values.`,
263
+ )
264
+ }
265
+ return build({ kind: 'scalar', type, options: options ?? {} })
266
+ }
267
+
268
+ const whole = (value: unknown, most: number): value is number =>
269
+ Number.isInteger(value) && Number(value) >= 0 && Number(value) <= most
270
+
271
+ /** Exact numbers of `precision` digits, `scale` after the point (D-145). */
272
+ function decimal(options?: FieldOptions): object {
273
+ const { precision, scale } = options ?? {}
274
+ if (!whole(precision, 1000) || precision === 0) {
275
+ throw new Error('f.decimal() takes a precision from 1 to 1000.')
276
+ }
277
+ if (!whole(scale, precision)) {
278
+ throw new Error(
279
+ `f.decimal() takes a scale from 0 to its precision, ${precision}.`,
280
+ )
281
+ }
282
+ return scalar('decimal')(options)
283
+ }
284
+
285
+ const relation =
286
+ (kind: 'one' | 'many') =>
287
+ (target: () => unknown, options?: FieldOptions): object =>
288
+ build({ kind, target, options: options ?? {} })
289
+
290
+ /** The `defineEnum` values, which `enumOf` takes in place of a list. */
291
+ export const sharedEnums: WeakMap<object, Enum> = new WeakMap()
292
+
293
+ /** An enum's values: strings, each once. */
294
+ export function enumValues(values: unknown, of: string): readonly string[] {
295
+ const listed: readonly unknown[] = Array.isArray(values) ? values : []
296
+ const strings = listed.filter((value) => typeof value === 'string')
297
+ if (strings.length === 0 || strings.length !== listed.length) {
298
+ throw new Error(`${of} takes a list of strings.`)
299
+ }
300
+ if (new Set(strings).size !== strings.length) {
301
+ throw new Error(`${of} lists a value twice: ${strings.join(', ')}.`)
302
+ }
303
+ return strings
304
+ }
305
+
306
+ function enumOf(values: unknown, options?: FieldOptions): object {
307
+ const shared = isObject(values) ? sharedEnums.get(values) : undefined
308
+ if (shared !== undefined) {
309
+ return listable('enum', options, shared.values, shared)
310
+ }
311
+ return listable('enum', options, enumValues(values, 'enumOf'))
312
+ }
313
+
314
+ /** The builders `defineModel`'s `fields` callback receives (D-09, D-12). */
315
+ export const fieldBuilders: object = Object.freeze({
316
+ string: (options?: FieldOptions) => listable('string', options),
317
+ number: (options?: FieldOptions) => listable('number', options),
318
+ decimal,
319
+ boolean: scalar('boolean'),
320
+ dateTime: scalar('dateTime'),
321
+ date: scalar('date'),
322
+ json: scalar('json'),
323
+ enumOf,
324
+ one: relation('one'),
325
+ many: relation('many'),
326
+ virtual: (output: unknown, options?: FieldOptions): object =>
327
+ build({ kind: 'virtual', output, options: options ?? {} }),
328
+ })
329
+
330
+ function namedOf(value: unknown): NamedOutput | undefined {
331
+ if (!isObject(value)) return undefined
332
+ const model = modelDefinitions.get(value)
333
+ if (model !== undefined) return { kind: 'model', value, name: model.name }
334
+ const type = typeDefinitions.get(value)
335
+ return type === undefined ? undefined : { kind: 'type', type }
336
+ }
337
+
338
+ /** An output's value, read once at its definition (D-109). */
339
+ export function outputOf(value: unknown, where: string): Output {
340
+ const standard = standardOf(value)
341
+ if (standard !== undefined) return { kind: 'schema', schema: standard.schema }
342
+ const named = namedOf(value)
343
+ if (named !== undefined) return { ...named, form: 'one' }
344
+ const marked = isObject(value) ? nullables.get(value) : undefined
345
+ if (marked !== undefined) return { ...marked, form: 'nullable' }
346
+ const list: readonly unknown[] = Array.isArray(value) ? value : []
347
+ const listed = list.length === 1 ? namedOf(list[0]) : undefined
348
+ if (listed !== undefined) return { ...listed, form: 'list' }
349
+ throw new Refused(
350
+ `${where} is a model, a type, a list of one, X.nullable() or a Standard Schema.`,
351
+ )
352
+ }
@@ -0,0 +1,76 @@
1
+ // `deleteMany` (D-111): what a `delete` runs, on each row its `where`
2
+ // selects, all or nothing; one write when nothing runs (D-102).
3
+ import type { Filter, Row } from './adapter.ts'
4
+ import { cascadeOf, cascadeWhere } from './cascade.ts'
5
+ import type { Event } from './hooks.ts'
6
+ import { settle } from './hooks.ts'
7
+ import type { Context } from './query-context.ts'
8
+ import { filtered, given } from './query-context.ts'
9
+ import { byIds } from './query-where.ts'
10
+ import type { Commit, Store } from './transaction.ts'
11
+ import { checkedWrite, guarded } from './write-target.ts'
12
+ import { cascaded, writingOf } from './writing.ts'
13
+
14
+ export async function removeMany(
15
+ context: Context,
16
+ store: Store,
17
+ commits: Commit[],
18
+ args: unknown,
19
+ ): Promise<{ readonly count: number }> {
20
+ const { models, model, scope } = context
21
+ const found = given(args, model.name, ['where'], [])
22
+ const where: Filter = (await filtered(context, found, 'delete')) ?? {}
23
+ const reach = cascadeWhere(models, model, where)
24
+ if (reach !== undefined && scope.access === undefined) {
25
+ // The write reads its rows as they were, to count them (D-102).
26
+ const read = { model: model.name, where }
27
+ const { checks } = reach
28
+ const rows = await guarded(
29
+ store.last({
30
+ checks: checks.map(({ check }) => check),
31
+ changes: reach.changes,
32
+ read,
33
+ before: true,
34
+ }),
35
+ checks,
36
+ )
37
+ return { count: rows.length }
38
+ }
39
+ // In id order, so two calls on the same rows wait instead of deadlocking.
40
+ const orderBy = [{ field: 'id', direction: 'asc' }] as const
41
+ const rows = await store.find({
42
+ model: model.name,
43
+ where,
44
+ orderBy,
45
+ lock: true,
46
+ })
47
+ // One row refused, none deleted (D-111).
48
+ for (const row of rows) scope.access?.allow(model, 'delete', { record: row })
49
+ if (rows.length > 0) await deleted(context, store, commits, rows)
50
+ return { count: rows.length }
51
+ }
52
+
53
+ /**
54
+ * Deletes `rows`, which the caller may delete, as `delete` does: in one write
55
+ * when nothing runs (D-102), else with their cascade or redirect, then the
56
+ * hooks.
57
+ */
58
+ export async function deleted(
59
+ context: Context,
60
+ store: Store,
61
+ commits: Commit[],
62
+ rows: readonly Row[],
63
+ ): Promise<void> {
64
+ const { models, model, caller, scope } = context
65
+ const locked = cascadeWhere(models, model, byIds(rows.map(({ id }) => id)))
66
+ if (locked !== undefined) {
67
+ await checkedWrite(store, locked.checks, locked.changes)
68
+ return
69
+ }
70
+ const events: Event[] = []
71
+ const writing = writingOf(scope, store, events)
72
+ const cascade = await cascadeOf(models, model, rows, store)
73
+ const changes = await cascaded(writing, cascade)
74
+ if (changes.length > 0) await checkedWrite(store, writing.checks, changes)
75
+ await settle(store, caller, events, commits)
76
+ }
package/src/engine.ts ADDED
@@ -0,0 +1,281 @@
1
+ // The queries a model sends to the running application (D-18): each one
2
+ // checked, run on the adapter, in the open transaction if any (D-36), and
3
+ // returned in the shape it asked for.
4
+ import { accessFor, and } from './access.ts'
5
+ import { InvalidQuery, PublicError } from './errors.ts'
6
+ import type { Identity } from './rules.ts'
7
+ import { isMaster } from './rules.ts'
8
+ import type { Read, Row } from './adapter.ts'
9
+ import type { Running } from './running.ts'
10
+ import { current } from './running.ts'
11
+ import type { ModelInfo } from './schema.ts'
12
+ import type { Context } from './query-context.ts'
13
+ import { filtered, first, given, picked, shapedRows } from './query-context.ts'
14
+ import { oneKeys, plan, restrict, uniqueWhere } from './query-args.ts'
15
+ import { counted } from './virtual-read.ts'
16
+ import { entries, isObject, unknownKey } from './query-where.ts'
17
+ import { QueryScope, planned } from './scope.ts'
18
+ import type { Commit, Store } from './transaction.ts'
19
+ import { reading, writing } from './transaction.ts'
20
+ import { aggregate } from './aggregate.ts'
21
+ import { distinctOf, groupBy } from './group-by.ts'
22
+ import { removeMany } from './delete-many.ts'
23
+ import { updateMany, updateManyAndReturn } from './update-many.ts'
24
+ import { createMany, createManyAndReturn } from './create-many.ts'
25
+ import { create, remove, update, upsert } from './writes.ts'
26
+
27
+ export type Operation =
28
+ | 'findMany'
29
+ | 'findFirst'
30
+ | 'findUnique'
31
+ | 'findFirstOrThrow'
32
+ | 'findUniqueOrThrow'
33
+ | 'count'
34
+ | 'aggregate'
35
+ | 'groupBy'
36
+ | 'create'
37
+ | 'update'
38
+ | 'upsert'
39
+ | 'delete'
40
+ | 'updateMany'
41
+ | 'createMany'
42
+ | 'updateManyAndReturn'
43
+ | 'createManyAndReturn'
44
+ | 'deleteMany'
45
+ // The transports' upsert, which also says whether it created (D-142).
46
+ | 'upserted'
47
+
48
+ const anonymous: Identity = Object.freeze({ kind: 'anonymous' })
49
+
50
+ /**
51
+ * `{ as }` only; without it, the caller is anonymous (D-19). The kernel
52
+ * completes a user one (D-121).
53
+ */
54
+ export function callerOf(options: unknown, model: string): Identity {
55
+ if (options === undefined) return anonymous
56
+ const keys = new Map(entries(options, 'options'))
57
+ for (const key of keys.keys()) {
58
+ if (key !== 'as') throw unknownKey(`options.${key}`, model)
59
+ }
60
+ const caller = keys.get('as')
61
+ if (caller === undefined) return anonymous
62
+ if (!isObject(caller)) {
63
+ throw new InvalidQuery('options.as', 'options.as must be a caller.')
64
+ }
65
+ switch (caller['kind']) {
66
+ case 'anonymous':
67
+ return anonymous
68
+ case 'user':
69
+ if (
70
+ !isObject(caller['user']) ||
71
+ typeof caller['user']['id'] !== 'string'
72
+ ) {
73
+ throw new InvalidQuery('options.as', 'options.as.user must be { id }.')
74
+ }
75
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the kernel reads only the id; the record's other fields are the server code's.
76
+ return caller as Identity
77
+ case 'master':
78
+ // Only the kernel creates Master (D-19): a copy is a forgery.
79
+ if (!isMaster(caller)) {
80
+ throw new Error('options.as is a Master the kernel did not create.')
81
+ }
82
+ return caller
83
+ default:
84
+ throw new InvalidQuery('options.as', 'options.as must be a caller.')
85
+ }
86
+ }
87
+
88
+ const manyKeys = new Set([
89
+ 'where',
90
+ ...oneKeys,
91
+ 'orderBy',
92
+ 'take',
93
+ 'skip',
94
+ 'cursor',
95
+ ])
96
+ const distinctKeys = new Set([...manyKeys, 'distinct'])
97
+
98
+ /** Finds a read's root rows, decoded; a live read may serve them (D-154). */
99
+ export type RootFind = (store: Store, read: Read) => Promise<Row[]>
100
+
101
+ const onStore: RootFind = async (store, read) => store.find(read)
102
+
103
+ async function find(
104
+ context: Context,
105
+ store: Store,
106
+ args: unknown,
107
+ single: boolean,
108
+ root = onStore,
109
+ ): Promise<unknown> {
110
+ const { models, model, scope } = context
111
+ const keys = single ? manyKeys : distinctKeys
112
+ const { read, shape, readable } = await planned(scope, () => ({
113
+ ...plan(models, model, args, '', 'many', keys, scope),
114
+ readable: scope.readable(model.name),
115
+ }))
116
+ const take = read.take ?? 1
117
+ const limited = single ? { ...read, take: Math.sign(take) } : read
118
+ const distinct = single ? undefined : await distinctOf(context, args)
119
+ const rows = await root(store, {
120
+ model: model.name,
121
+ ...restrict(limited, readable),
122
+ ...(distinct && { distinct }),
123
+ })
124
+ if (single) return first(context, store, rows, shape, scope.access)
125
+ return shapedRows(context, store, rows, shape, scope.access)
126
+ }
127
+
128
+ async function findUnique(
129
+ context: Context,
130
+ store: Store,
131
+ args: unknown,
132
+ ): Promise<unknown> {
133
+ const { models, model, scope } = context
134
+ const found = given(args, model.name, ['where', ...oneKeys], ['where'])
135
+ const { read, shape, where } = await planned(scope, () => ({
136
+ ...plan(models, model, picked(found), '', 'one', oneKeys, scope),
137
+ where: and(
138
+ uniqueWhere(models, model, found.get('where'), 'where', scope),
139
+ scope.readable(model.name),
140
+ ),
141
+ }))
142
+ const rows = await store.find({ model: model.name, ...read, where })
143
+ return first(context, store, rows, shape, scope.access)
144
+ }
145
+
146
+ async function count(
147
+ context: Context,
148
+ store: Store,
149
+ args: unknown,
150
+ ): Promise<number> {
151
+ const { model } = context
152
+ const where = await filtered(
153
+ context,
154
+ given(args, model.name, ['where'], []),
155
+ 'read',
156
+ )
157
+ return store.count({ model: model.name, ...(where && { where }) })
158
+ }
159
+
160
+ type Run = (context: Context, store: Store, args: unknown) => Promise<unknown>
161
+ type Write = (
162
+ context: Context,
163
+ store: Store,
164
+ commits: Commit[],
165
+ args: unknown,
166
+ ) => Promise<unknown>
167
+
168
+ type ReadOperation =
169
+ | 'findMany'
170
+ | 'findFirst'
171
+ | 'findUnique'
172
+ | 'findFirstOrThrow'
173
+ | 'findUniqueOrThrow'
174
+ | 'count'
175
+ | 'aggregate'
176
+ | 'groupBy'
177
+
178
+ /** The read, which fails with `NOT_FOUND` rather than return `null` (D-135). */
179
+ const orThrow =
180
+ (read: Run): Run =>
181
+ async (context, store, args) => {
182
+ const row = await read(context, store, args)
183
+ if (row !== null) return row
184
+ throw new PublicError('NOT_FOUND', `No ${context.model.name} matches.`)
185
+ }
186
+
187
+ const reads: { readonly [O in ReadOperation]: Run } = {
188
+ findMany: async (context, store, args) => find(context, store, args, false),
189
+ findFirst: async (context, store, args) => find(context, store, args, true),
190
+ findUnique,
191
+ findFirstOrThrow: orThrow(async (context, store, args) =>
192
+ find(context, store, args, true),
193
+ ),
194
+ findUniqueOrThrow: orThrow(findUnique),
195
+ count,
196
+ aggregate,
197
+ groupBy,
198
+ }
199
+
200
+ const writes: { readonly [O in Exclude<Operation, ReadOperation>]: Write } = {
201
+ create,
202
+ update,
203
+ upsert: async (context, store, commits, args) =>
204
+ (await upsert(context, store, commits, args)).row,
205
+ upserted: upsert,
206
+ delete: remove,
207
+ updateMany,
208
+ createMany,
209
+ updateManyAndReturn,
210
+ createManyAndReturn,
211
+ deleteMany: removeMany,
212
+ }
213
+
214
+ function isRead(operation: Operation): operation is ReadOperation {
215
+ return Object.hasOwn(reads, operation)
216
+ }
217
+
218
+ /** The running application and its model `name`. */
219
+ function runningModel(name: string): {
220
+ readonly app: Running
221
+ readonly model: ModelInfo
222
+ } {
223
+ const app = current()
224
+ const model = app?.resolved.models.get(name)
225
+ if (app === undefined || model === undefined) {
226
+ throw new Error(`${name} is not a model of a started application.`)
227
+ }
228
+ return { app, model }
229
+ }
230
+
231
+ async function contextOf(
232
+ app: Running,
233
+ model: ModelInfo,
234
+ identity: Identity,
235
+ ): Promise<Context> {
236
+ const caller = await app.complete(identity)
237
+ const { models } = app.resolved
238
+ const access = accessFor(models, caller, app.logger)
239
+ const scope = new QueryScope(models, caller, access)
240
+ return { app, models, model, caller, scope }
241
+ }
242
+
243
+ /** Runs one query of a model of the running application. */
244
+ export async function query(
245
+ name: string,
246
+ operation: Operation,
247
+ args: unknown,
248
+ options: unknown,
249
+ ): Promise<unknown> {
250
+ const { app, model } = runningModel(name)
251
+ const identity = callerOf(options, name)
252
+ counted()
253
+ return app.work.run(async () => {
254
+ const context = await contextOf(app, model, identity)
255
+ if (isRead(operation)) {
256
+ const read = reads[operation]
257
+ return reading(app, async (store) => read(context, store, args))
258
+ }
259
+ const write = writes[operation]
260
+ return writing(app, async (store, commits) =>
261
+ write(context, store, commits, args),
262
+ )
263
+ })
264
+ }
265
+
266
+ /** `findMany(args)` as `options` says, its root rows found by `root` (D-154). */
267
+ export async function rootFind(
268
+ name: string,
269
+ args: unknown,
270
+ options: unknown,
271
+ root: RootFind,
272
+ ): Promise<unknown> {
273
+ const { app, model } = runningModel(name)
274
+ const identity = callerOf(options, name)
275
+ return app.work.run(async () => {
276
+ const context = await contextOf(app, model, identity)
277
+ return reading(app, async (store) =>
278
+ find(context, store, args, false, root),
279
+ )
280
+ })
281
+ }