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,99 @@
1
+ import { called } from './define-function.js';
2
+ import { functionDefinitions } from './definition.js';
3
+ import { isObject } from './query-where.js';
4
+ import { checkInstalled } from './plugin-use.js';
5
+ import { Refused } from './errors.js';
6
+ /** Checks the models and types an output reaches, each type once. */
7
+ function checkOutput(name, output, resolved, types) {
8
+ if (output.kind === 'schema')
9
+ return;
10
+ if (output.kind === 'model') {
11
+ const info = resolved.byValue.get(output.value);
12
+ if (info === undefined) {
13
+ throw new Refused(`${name} returns ${output.name}, which is not in models.`);
14
+ }
15
+ if (info.owner === undefined)
16
+ return;
17
+ throw new Refused(`${name} returns ${output.name}, a model of ${info.owner}.`);
18
+ }
19
+ const { type } = output;
20
+ const known = types.get(type.name);
21
+ if (known === type)
22
+ return;
23
+ if (known !== undefined) {
24
+ throw new Refused(`Two types are named ${type.name}; name each once.`);
25
+ }
26
+ if (resolved.models.has(type.name)) {
27
+ throw new Refused(`The type ${type.name} is named like a model.`);
28
+ }
29
+ types.set(type.name, type);
30
+ for (const field of type.fields.values()) {
31
+ checkOutput(name, field, resolved, types);
32
+ }
33
+ }
34
+ const ownerOf = (plugin, name) => plugin === undefined
35
+ ? `the application's function ${name}`
36
+ : `${plugin}'s function ${name}`;
37
+ /** The functions, each named once, or startup stops with a message. */
38
+ export function resolveFunctions(sources, resolved, plugins) {
39
+ const definitions = new Map();
40
+ const owners = new Map();
41
+ const names = new Map();
42
+ const types = new Map();
43
+ for (const { plugin, values } of sources) {
44
+ const where = plugin === undefined ? 'functions' : `${plugin}.functions`;
45
+ for (const value of values) {
46
+ const definition = isObject(value)
47
+ ? functionDefinitions.get(value)
48
+ : undefined;
49
+ if (definition === undefined || !isObject(value)) {
50
+ throw new Refused(`${where} lists a value that defineFunction did not return.`);
51
+ }
52
+ const { name } = definition;
53
+ const owner = ownerOf(plugin, name);
54
+ const other = names.get(name);
55
+ if (other !== undefined) {
56
+ throw new Refused(other === owner
57
+ ? `${where} lists two functions named ${name}.`
58
+ : `${owner} and ${other} are both named ${name}.`);
59
+ }
60
+ names.set(name, owner);
61
+ for (const use of definition.uses) {
62
+ checkInstalled(name, use.code, plugins);
63
+ }
64
+ if (definition.output !== undefined) {
65
+ checkOutput(name, definition.output, resolved, types);
66
+ }
67
+ definitions.set(value, definition);
68
+ if (plugin !== undefined)
69
+ owners.set(value, plugin);
70
+ }
71
+ }
72
+ // A virtual's type is named once with the functions' (D-151).
73
+ for (const info of resolved.byValue.values()) {
74
+ for (const { name, output } of info.virtuals.values()) {
75
+ if (output.kind !== 'type')
76
+ continue;
77
+ checkOutput(`${info.name}.${name}`, output, resolved, types);
78
+ }
79
+ }
80
+ return { definitions, plugins: owners };
81
+ }
82
+ /** The functions as the plugin `name` reads them, with its `.with` key. */
83
+ export function exposedFunctions(name, functions) {
84
+ return [...functions.definitions].map(([value, record]) => {
85
+ const { rules, input } = record;
86
+ const key = rules === undefined ? undefined : Reflect.get(rules, name);
87
+ return Object.freeze({
88
+ name: record.name,
89
+ description: record.description,
90
+ plugin: functions.plugins.get(value),
91
+ query: record.query,
92
+ used: record.uses.some((use) => use.code.name === name),
93
+ input,
94
+ output: record.output,
95
+ with: key,
96
+ run: async (given, options, client) => called(value, record, input === undefined ? [options] : [given, options], client),
97
+ });
98
+ });
99
+ }
@@ -0,0 +1,10 @@
1
+ import type { GroupKey } from './adapter.ts';
2
+ import type { Context } from './query-context.ts';
3
+ import type { Store } from './transaction.ts';
4
+ /**
5
+ * The keys of a `findMany`'s `distinct`, which a cursor cannot page (D-147):
6
+ * `undefined` without one.
7
+ */
8
+ export declare function distinctOf(context: Context, args: unknown): Promise<GroupKey[] | undefined>;
9
+ export declare function groupBy(context: Context, store: Store, args: unknown): Promise<unknown>;
10
+ //# sourceMappingURL=group-by.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-by.d.ts","sourceRoot":"","sources":["../src/group-by.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAiB,QAAQ,EAAE,MAAM,cAAc,CAAA;AAa3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAKjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AA8C7C;;;GAGG;AACH,wBAAsB,UAAU,CAC/B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,OAAO,GACX,OAAO,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,CAOjC;AA+DD,wBAAsB,OAAO,CAC5B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,CAAC,CAqClB"}
@@ -0,0 +1,145 @@
1
+ import { checked, keys, shaped, toAggregate, typeOf, wanted, } from './aggregate.js';
2
+ import { InvalidQuery } from './errors.js';
3
+ import { having } from './group-having.js';
4
+ import { filtered, given } from './query-context.js';
5
+ import { integer } from './query-args.js';
6
+ import { direction, sortOf } from './query-order.js';
7
+ import { entries, isObject } from './query-where.js';
8
+ /**
9
+ * The fields of `by`, or of `distinct` (D-147), each checked; a `one`
10
+ * relation the row stores.
11
+ */
12
+ function grouping({ model, scope }, value, name) {
13
+ const message = `${name} must be a non-empty list of fields.`;
14
+ const list = Array.isArray(value) ? value : [];
15
+ if (list.length === 0)
16
+ throw new InvalidQuery(name, message);
17
+ const [verb, done] = name === 'by'
18
+ ? ['aggregates', 'grouped']
19
+ : ['compares', 'compared'];
20
+ const fields = [];
21
+ for (const [index, field] of list.entries()) {
22
+ const path = `${name}.${index}`;
23
+ if (typeof field !== 'string')
24
+ throw new InvalidQuery(path, message);
25
+ if (fields.includes(field)) {
26
+ throw new InvalidQuery(path, `${path} repeats ${field}.`);
27
+ }
28
+ const known = model.fields.get(field);
29
+ if (known?.kind !== 'one' || !known.stored) {
30
+ typeOf(model, field, path, done);
31
+ }
32
+ scope.check(model, field, path, verb);
33
+ fields.push(field);
34
+ }
35
+ return fields;
36
+ }
37
+ /** Groups by a relation's target id, `null` when the caller cannot read it. */
38
+ async function keyOf(context, field) {
39
+ const known = context.model.fields.get(field);
40
+ if (known?.kind !== 'one')
41
+ return { field };
42
+ const where = await context.scope.filter(known.target, false);
43
+ if (where === undefined)
44
+ return { field };
45
+ return { field: 'id', through: [{ relation: field, where }] };
46
+ }
47
+ /**
48
+ * The keys of a `findMany`'s `distinct`, which a cursor cannot page (D-147):
49
+ * `undefined` without one.
50
+ */
51
+ export async function distinctOf(context, args) {
52
+ if (!isObject(args) || args['distinct'] === undefined)
53
+ return undefined;
54
+ if (args['cursor'] !== undefined) {
55
+ throw new InvalidQuery('distinct', 'distinct cannot be used with cursor.');
56
+ }
57
+ const fields = grouping(context, args['distinct'], 'distinct');
58
+ return Promise.all(fields.map(async (field) => keyOf(context, field)));
59
+ }
60
+ /**
61
+ * An aggregate's position in a group's values, after its keys; one the
62
+ * result does not ask for is added to `aggregates`.
63
+ */
64
+ const positions = (by, aggregates) => (asked) => {
65
+ const index = aggregates.findIndex((each) => each.key === asked.key && each.field === asked.field);
66
+ if (index !== -1)
67
+ return by.length + index;
68
+ aggregates.push(asked);
69
+ return by.length + aggregates.length - 1;
70
+ };
71
+ /** The groups' sort: each key of `orderBy`, then the other fields of `by`. */
72
+ function sorts(context, value, by, at) {
73
+ const parts = Array.isArray(value) ? value : [value];
74
+ const sorted = [];
75
+ for (const [index, part] of parts.entries()) {
76
+ const base = Array.isArray(value) ? `orderBy.${index}` : 'orderBy';
77
+ for (const [name, order] of entries(part, base)) {
78
+ const path = `${base}.${name}`;
79
+ if (by.includes(name)) {
80
+ const known = context.model.fields.get(name);
81
+ const optional = known?.kind !== 'many' && known?.optional === true;
82
+ sorted.push({ at: by.indexOf(name), ...sortOf(order, path, optional) });
83
+ continue;
84
+ }
85
+ const key = keys.find((each) => each === name);
86
+ if (key === undefined) {
87
+ throw new InvalidQuery(path, `${path} must be a field of by or an aggregate.`);
88
+ }
89
+ if (key === '_count' && !isObject(order)) {
90
+ const asked = { key, field: undefined };
91
+ sorted.push({ at: at(asked), direction: direction(order, path) });
92
+ continue;
93
+ }
94
+ for (const [field, each] of entries(order, path)) {
95
+ const asked = checked(context, key, field, `${path}.${field}`);
96
+ const sort = direction(each, `${path}.${field}`);
97
+ sorted.push({ at: at(asked), direction: sort });
98
+ }
99
+ }
100
+ }
101
+ for (const [index] of by.entries()) {
102
+ if (!sorted.some((sort) => sort.at === index)) {
103
+ sorted.push({ at: index, direction: 'asc' });
104
+ }
105
+ }
106
+ return sorted;
107
+ }
108
+ export async function groupBy(context, store, args) {
109
+ const { model } = context;
110
+ const allowed = ['by', 'where', 'having', 'orderBy', 'skip', 'take', ...keys];
111
+ const found = given(args, model.name, allowed, ['by']);
112
+ const by = grouping(context, found.get('by'), 'by');
113
+ const asked = wanted(context, found);
114
+ const aggregates = [...asked];
115
+ const position = positions(by, aggregates);
116
+ const orderBy = sorts(context, found.get('orderBy') ?? {}, by, position);
117
+ const filter = found.has('having')
118
+ ? having(context, found.get('having'), by, position)
119
+ : undefined;
120
+ const page = {};
121
+ if (found.has('skip'))
122
+ page.skip = integer(found.get('skip'), 'skip', 0);
123
+ if (found.has('take'))
124
+ page.take = integer(found.get('take'), 'take', 0);
125
+ const where = await filtered(context, found, 'read');
126
+ const groupKeys = await Promise.all(by.map(async (field) => keyOf(context, field)));
127
+ const groups = await store.aggregate({
128
+ model: model.name,
129
+ ...(where && { where }),
130
+ aggregates: aggregates.map(toAggregate),
131
+ by: groupKeys,
132
+ ...(filter && { having: filter }),
133
+ orderBy,
134
+ ...page,
135
+ });
136
+ return groups.map((values) => {
137
+ const row = {};
138
+ for (const [index, field] of by.entries()) {
139
+ const value = values[index] ?? null;
140
+ const relation = model.fields.get(field)?.kind === 'one';
141
+ row[field] = relation && value !== null ? { id: value } : value;
142
+ }
143
+ return { ...row, ...shaped(found, asked, values.slice(by.length)) };
144
+ });
145
+ }
@@ -0,0 +1,8 @@
1
+ import type { Filter } from './adapter.ts';
2
+ import type { Asked } from './aggregate.ts';
3
+ import type { Context } from './query-context.ts';
4
+ /** An aggregate's position in the group's values, added if not asked. */
5
+ export type Position = (asked: Asked) => number;
6
+ /** The `having` of the groups by `by`, normalized; `path` names it in errors. */
7
+ export declare function having(context: Context, value: unknown, by: readonly string[], position: Position, path?: string): Filter;
8
+ //# sourceMappingURL=group-having.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-having.d.ts","sourceRoot":"","sources":["../src/group-having.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,KAAK,EAAE,KAAK,EAAO,MAAM,gBAAgB,CAAA;AAIhD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAIjD,yEAAyE;AACzE,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAA;AAkG/C,iFAAiF;AACjF,wBAAgB,MAAM,CACrB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,OAAO,EACd,EAAE,EAAE,SAAS,MAAM,EAAE,EACrB,QAAQ,EAAE,QAAQ,EAClB,IAAI,SAAW,GACb,MAAM,CAuBR"}
@@ -0,0 +1,97 @@
1
+ import { checked, keys, typeOf } from './aggregate.js';
2
+ import { decimalPattern, normalized } from './decimal.js';
3
+ import { InvalidQuery } from './errors.js';
4
+ import { entries, fieldCondition, isObject } from './query-where.js';
5
+ const decimal = new RegExp(decimalPattern);
6
+ const whole = {
7
+ type: 'number',
8
+ check: (item, at) => {
9
+ if (!Number.isSafeInteger(item)) {
10
+ throw new InvalidQuery(at, `${at} must be a whole number.`);
11
+ }
12
+ return item;
13
+ },
14
+ };
15
+ const finite = {
16
+ type: 'number',
17
+ check: (item, at) => {
18
+ if (typeof item !== 'number' || !Number.isFinite(item)) {
19
+ throw new InvalidQuery(at, `${at} must be a finite number.`);
20
+ }
21
+ return item;
22
+ },
23
+ };
24
+ /** A decimal's sum, exact at its field's scale, of any precision (D-145). */
25
+ const sum = (scale) => ({
26
+ type: 'decimal',
27
+ check: (item, at) => {
28
+ if (typeof item !== 'string' || !decimal.test(item)) {
29
+ throw new InvalidQuery(at, `${at} must be a decimal string, such as '12.50'.`);
30
+ }
31
+ return normalized(item, scale);
32
+ },
33
+ });
34
+ /**
35
+ * The operands of aggregate `key` of a field: a count's a whole number, a
36
+ * sum's and an average's a number, a decimal's sum a decimal string, a
37
+ * minimum's and a maximum's the field's own.
38
+ */
39
+ function operandOf(context, key, name) {
40
+ if (key === '_count')
41
+ return whole;
42
+ if (key === '_min' || key === '_max')
43
+ return undefined;
44
+ const field = context.model.fields.get(name);
45
+ return key === '_sum' && field?.kind === 'scalar' && field.type === 'decimal'
46
+ ? sum(field.scale ?? 0)
47
+ : finite;
48
+ }
49
+ /** The conditions of field `name`'s value and aggregates, by position. */
50
+ function conditions(context, name, part, at, by, position) {
51
+ const { model, scope } = context;
52
+ typeOf(model, name, at, 'used in having');
53
+ const found = {};
54
+ const own = [];
55
+ const listed = isObject(part) ? entries(part, at) : [];
56
+ for (const [key, operand] of listed) {
57
+ const aggregate = keys.find((each) => each === key);
58
+ if (aggregate === undefined) {
59
+ own.push([key, operand]);
60
+ continue;
61
+ }
62
+ const where = `${at}.${key}`;
63
+ const asked = checked(context, aggregate, name, where);
64
+ const of = operandOf(context, aggregate, name);
65
+ const condition = fieldCondition(model, name, operand, where, of);
66
+ if (condition !== undefined)
67
+ found[String(position(asked))] = condition;
68
+ }
69
+ if (isObject(part) && own.length === 0)
70
+ return found;
71
+ if (!by.includes(name)) {
72
+ throw new InvalidQuery(at, `${at} is not a field of by: only its aggregates filter the groups.`);
73
+ }
74
+ scope.check(model, name, at, 'filters');
75
+ const value = isObject(part) ? Object.fromEntries(own) : part;
76
+ const condition = fieldCondition(model, name, value, at);
77
+ if (condition !== undefined)
78
+ found[String(by.indexOf(name))] = condition;
79
+ return found;
80
+ }
81
+ /** The `having` of the groups by `by`, normalized; `path` names it in errors. */
82
+ export function having(context, value, by, position, path = 'having') {
83
+ const filter = {};
84
+ for (const [name, part] of entries(value, path)) {
85
+ const at = `${path}.${name}`;
86
+ if (name !== 'AND' && name !== 'OR' && name !== 'NOT') {
87
+ Object.assign(filter, conditions(context, name, part, at, by, position));
88
+ continue;
89
+ }
90
+ if (!Array.isArray(part) && name === 'OR') {
91
+ throw new InvalidQuery(at, `${at} must be a list.`);
92
+ }
93
+ const list = Array.isArray(part) ? part : [part];
94
+ filter[name] = list.map((item, index) => having(context, item, by, position, Array.isArray(part) ? `${at}.${index}` : at));
95
+ }
96
+ return filter;
97
+ }
@@ -0,0 +1,41 @@
1
+ import type { Filter, Row } from './adapter.ts';
2
+ import type { hookNames } from './plugin-code.ts';
3
+ import type { FindOperation } from './model.ts';
4
+ import type { Caller } from './rules.ts';
5
+ import type { Relations } from './relations.ts';
6
+ import type { ModelInfo } from './schema.ts';
7
+ import type { Models } from './query-where.ts';
8
+ import type { Commit, Store } from './transaction.ts';
9
+ type HookName = (typeof hookNames)[number];
10
+ export declare function hasHook(model: ModelInfo, name: HookName): boolean;
11
+ export declare function runHook(model: ModelInfo, name: 'beforeCreate' | 'beforeUpdate' | 'beforeDelete', facts: object): Promise<void>;
12
+ /** The filters every `beforeFind` ANDs into each read of the model (D-71). */
13
+ export declare function beforeFind(models: Models, model: ModelInfo, caller: Caller, operation: FindOperation, relations: Relations): Promise<Filter | undefined>;
14
+ /**
15
+ * The plugin's `redirect.delete`, which replaces the write of a `delete`,
16
+ * given `where: { id }` of the row found (D-15, D-89).
17
+ */
18
+ export declare function redirectOf(model: ModelInfo): ((caller: Caller, id: string) => Promise<unknown>) | undefined;
19
+ /** A row a write changed, whose `after*` hooks run once it is written. */
20
+ export type Event = {
21
+ readonly model: ModelInfo;
22
+ readonly operation: 'create';
23
+ readonly id: string;
24
+ } | {
25
+ readonly model: ModelInfo;
26
+ readonly operation: 'update' | 'delete';
27
+ readonly id: string;
28
+ /** The stored row before the write. */
29
+ readonly before: Row;
30
+ };
31
+ /** Whether an `after*` hook may change the rows, so the result is read again. */
32
+ export declare function rereads(events: readonly Event[]): boolean;
33
+ /** Whether a hook follows the write, which then runs in a transaction. */
34
+ export declare function settles(events: readonly Event[]): boolean;
35
+ /**
36
+ * Runs each event's `after*` hook in the transaction, and queues its
37
+ * `*Commit` hook, with the same facts, for after the commit (D-15, D-71).
38
+ */
39
+ export declare function settle(store: Store, caller: Caller, events: readonly Event[], commits: Commit[]): Promise<void>;
40
+ export {};
41
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAErD,KAAK,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;AA2D1C,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAEjE;AAED,wBAAsB,OAAO,CAC5B,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,cAAc,GAAG,cAAc,GAAG,cAAc,EACtD,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,8EAA8E;AAC9E,wBAAsB,UAAU,CAC/B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,SAAS,GAClB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqB7B;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACzB,KAAK,EAAE,SAAS,GACd,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAShE;AAED,0EAA0E;AAC1E,MAAM,MAAM,KAAK,GACd;IACA,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAA;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAClB,GACD;IACA,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAA;IACvC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAA;CACnB,CAAA;AAQJ,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,OAAO,CAEzD;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,OAAO,CAIzD;AAyBD;;;GAGG;AACH,wBAAsB,MAAM,CAC3B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,OAAO,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,IAAI,CAAC,CAcf"}
package/dist/hooks.js ADDED
@@ -0,0 +1,149 @@
1
+ // The fixed hooks of D-15 at run time (D-71, D-88): the plugins', then the
2
+ // model's `.with` (D-89), run with their facts; `after*` hooks in the
3
+ // transaction, the `*Commit` hooks once it commits.
4
+ import { and, recordOf } from './access.js';
5
+ import { commandOf, outsideCommands } from './plugin-code.js';
6
+ import { trusted } from './relations.js';
7
+ import { filterOf, isReadFilter, readFilter } from './rules.js';
8
+ import { isObject, normalizeWhere } from './query-where.js';
9
+ /** Runs a hook's body; the queries it makes run outside any command. */
10
+ async function called(hook, facts) {
11
+ return outsideCommands(async () => {
12
+ const result = await Reflect.apply(hook, undefined, [facts]);
13
+ return result;
14
+ });
15
+ }
16
+ /** The application's hook `name` of the model, from its `.with` (D-71). */
17
+ function ownHook(model, name) {
18
+ const rules = model.definition.rules;
19
+ const hooks = rules !== undefined && isObject(rules) ? rules['hooks'] : undefined;
20
+ if (hooks === undefined)
21
+ return undefined;
22
+ if (!isObject(hooks)) {
23
+ throw new TypeError(`${model.name}: hooks is an object.`);
24
+ }
25
+ const hook = hooks[name];
26
+ if (hook === undefined)
27
+ return undefined;
28
+ if (typeof hook !== 'function') {
29
+ throw new TypeError(`${model.name}.hooks.${name} is a function.`);
30
+ }
31
+ const path = `${model.name}.hooks.${name}`;
32
+ return { path, run: async (facts) => called(hook, facts) };
33
+ }
34
+ /**
35
+ * The hooks `name` of the model: its plugins' first, in the order of
36
+ * `plugins`, then the application's (D-29). A plugin's facts add what its
37
+ * code gets of the model and the command running (D-89).
38
+ */
39
+ function hooksOf(model, name) {
40
+ const hooks = [];
41
+ for (const use of model.plugins) {
42
+ const hook = use.code.hooks[name];
43
+ if (hook === undefined)
44
+ continue;
45
+ const plugin = use.code.name;
46
+ hooks.push({
47
+ path: `${plugin}.hooks.${name} on ${model.name}`,
48
+ run: async (facts) => called(hook, {
49
+ ...facts,
50
+ ...use.model,
51
+ command: commandOf(model.name, plugin),
52
+ }),
53
+ });
54
+ }
55
+ const own = ownHook(model, name);
56
+ return own === undefined ? hooks : [...hooks, own];
57
+ }
58
+ export function hasHook(model, name) {
59
+ return hooksOf(model, name).length > 0;
60
+ }
61
+ export async function runHook(model, name, facts) {
62
+ for (const hook of hooksOf(model, name))
63
+ await hook.run(facts);
64
+ }
65
+ /** The filters every `beforeFind` ANDs into each read of the model (D-71). */
66
+ export async function beforeFind(models, model, caller, operation, relations) {
67
+ let filter;
68
+ for (const hook of hooksOf(model, 'beforeFind')) {
69
+ const result = await hook.run({ caller, operation, where: readFilter });
70
+ if (result === undefined)
71
+ continue;
72
+ if (!isReadFilter(result)) {
73
+ throw new TypeError(`${hook.path} returns where({ ... }) or nothing.`);
74
+ }
75
+ // Trusted server code, as a read rule's filter (D-86, D-88); the one
76
+ // filter of a plugin field (D-122).
77
+ const where = normalizeWhere(models, model, filterOf(result), hook.path, trusted(relations), true);
78
+ filter = and(where, filter);
79
+ }
80
+ return filter;
81
+ }
82
+ /**
83
+ * The plugin's `redirect.delete`, which replaces the write of a `delete`,
84
+ * given `where: { id }` of the row found (D-15, D-89).
85
+ */
86
+ export function redirectOf(model) {
87
+ const use = model.plugins.find((each) => each.code.redirect !== undefined);
88
+ const redirect = use?.code.redirect;
89
+ if (use === undefined || redirect === undefined)
90
+ return undefined;
91
+ return async (caller, id) => {
92
+ const facts = { caller, where: { id }, ...use.model };
93
+ const result = await Reflect.apply(redirect, undefined, [facts]);
94
+ return result;
95
+ };
96
+ }
97
+ const names = {
98
+ create: ['afterCreate', 'afterCreateCommit'],
99
+ update: ['afterUpdate', 'afterUpdateCommit'],
100
+ delete: ['afterDelete', 'afterDeleteCommit'],
101
+ };
102
+ /** Whether an `after*` hook may change the rows, so the result is read again. */
103
+ export function rereads(events) {
104
+ return events.some((event) => hasHook(event.model, names[event.operation][0]));
105
+ }
106
+ /** Whether a hook follows the write, which then runs in a transaction. */
107
+ export function settles(events) {
108
+ return events.some((event) => names[event.operation].some((name) => hasHook(event.model, name)));
109
+ }
110
+ async function factsOf(store, caller, event) {
111
+ const { model, id } = event;
112
+ if (event.operation === 'delete') {
113
+ return { caller, record: recordOf(model, event.before) };
114
+ }
115
+ // As written, whatever `beforeFind` hides (D-88).
116
+ const [row] = await store.find({
117
+ model: model.name,
118
+ where: { id: { equals: id } },
119
+ take: 1,
120
+ });
121
+ if (row === undefined) {
122
+ throw new Error(`${model.name} ${id} was deleted before its after hooks.`);
123
+ }
124
+ const record = recordOf(model, row);
125
+ if (event.operation === 'create')
126
+ return { caller, record };
127
+ return { caller, record, previous: recordOf(model, event.before) };
128
+ }
129
+ /**
130
+ * Runs each event's `after*` hook in the transaction, and queues its
131
+ * `*Commit` hook, with the same facts, for after the commit (D-15, D-71).
132
+ */
133
+ export async function settle(store, caller, events, commits) {
134
+ for (const event of events) {
135
+ const [now, later] = names[event.operation];
136
+ const afters = hooksOf(event.model, now);
137
+ const laters = hooksOf(event.model, later);
138
+ if (afters.length === 0 && laters.length === 0)
139
+ continue;
140
+ const facts = await factsOf(store, caller, event);
141
+ for (const hook of afters)
142
+ await hook.run(facts);
143
+ for (const hook of laters) {
144
+ commits.push(async () => {
145
+ await hook.run(facts);
146
+ });
147
+ }
148
+ }
149
+ }
@@ -0,0 +1,3 @@
1
+ /** A host only this machine reaches (D-66, D-105, D-115). */
2
+ export declare function isLocalHost(hostname: string): boolean;
3
+ //# sourceMappingURL=hosts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosts.d.ts","sourceRoot":"","sources":["../src/hosts.ts"],"names":[],"mappings":"AAEA,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOrD"}
package/dist/hosts.js ADDED
@@ -0,0 +1,8 @@
1
+ // Which hosts are this machine's own, for the checks that trust them.
2
+ /** A host only this machine reaches (D-66, D-105, D-115). */
3
+ export function isLocalHost(hostname) {
4
+ return (hostname === 'localhost' ||
5
+ hostname.endsWith('.localhost') ||
6
+ hostname === '127.0.0.1' ||
7
+ hostname === '[::1]');
8
+ }
@@ -0,0 +1,15 @@
1
+ import type { Logger, MasterKey } from './app-options.ts';
2
+ import type { Complete } from './complete.ts';
3
+ import type { Caller, Identity } from './rules.ts';
4
+ /** The header a transport reads the master key from. */
5
+ export declare const masterKeyHeader = "x-ultimabase-master-key";
6
+ export type Identify = (request: Request) => Promise<Caller>;
7
+ /** A plugin's `identify`, bound to its context. */
8
+ export type PluginIdentify = (request: Request) => Promise<Identity | undefined>;
9
+ /**
10
+ * Reads the master key and `NODE_ENV` once, when the application starts.
11
+ * `plugins` is read on each request: it is filled once their contexts exist.
12
+ * The caller a plugin knows is completed (D-121).
13
+ */
14
+ export declare function identifier(masterKey: MasterKey, logger: Logger, plugins: readonly PluginIdentify[], complete: Complete): Identify;
15
+ //# sourceMappingURL=identify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identify.d.ts","sourceRoot":"","sources":["../src/identify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAGlD,wDAAwD;AACxD,eAAO,MAAM,eAAe,4BAA4B,CAAA;AAExD,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5D,mDAAmD;AACnD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAA;AAuBhF;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,QAAQ,EAAE,QAAQ,GAChB,QAAQ,CA+CV"}