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,209 @@
1
+ import { PublicError } from './errors.js';
2
+ import { hasHook, redirectOf } from './hooks.js';
3
+ import { updated } from './write-plan.js';
4
+ /** The stored `one` relations to `target`, each with its `onDelete`. */
5
+ function referencesTo(models, target) {
6
+ const references = [];
7
+ for (const model of models.values()) {
8
+ for (const field of model.fields.values()) {
9
+ if (field.kind !== 'one' || !field.stored || field.target !== target) {
10
+ continue;
11
+ }
12
+ references.push({ model, field: field.name, onDelete: field.onDelete });
13
+ }
14
+ }
15
+ return references;
16
+ }
17
+ const keyOf = (model, id) => `${model.name}:${id}`;
18
+ /**
19
+ * Loads every row deleting `deleting` reaches, level by level, as stored: no
20
+ * `beforeFind`, no access rules (D-54, D-90). Fails with
21
+ * `RELATION_RESTRICTED` when a row the delete leaves references a deleted one.
22
+ * A redirected row stays: it cascades nothing (D-89, D-90).
23
+ */
24
+ export async function cascadeOf(models, model, deleting, store, except) {
25
+ const redirected = redirectOf(model) !== undefined;
26
+ const roots = deleting.map((row) => ({ model, row, redirected }));
27
+ if (redirected)
28
+ return { deleted: roots, nulled: [] };
29
+ const deleted = new Map();
30
+ for (const root of roots)
31
+ deleted.set(keyOf(model, root.row.id), root);
32
+ const restricted = [];
33
+ const nulled = new Map();
34
+ let level = roots;
35
+ while (level.length > 0) {
36
+ const next = [];
37
+ const ids = new Map();
38
+ for (const doomed of level) {
39
+ ids.set(doomed.model, [...(ids.get(doomed.model) ?? []), doomed.row.id]);
40
+ }
41
+ for (const [target, targets] of ids) {
42
+ for (const reference of referencesTo(models, target.name)) {
43
+ const { model: from, field, onDelete } = reference;
44
+ const rows = await store.find({
45
+ model: from.name,
46
+ where: { [field]: { is: { id: { in: targets } } } },
47
+ lock: true,
48
+ });
49
+ for (const found of rows) {
50
+ const left = except?.model === from.name &&
51
+ except.field === field &&
52
+ except.id === found.id;
53
+ if (left)
54
+ continue;
55
+ if (onDelete === 'restrict') {
56
+ restricted.push({
57
+ model: from,
58
+ field,
59
+ id: found.id,
60
+ to: target.name,
61
+ });
62
+ }
63
+ else if (onDelete === 'setNull') {
64
+ const key = `${from.name}.${field}`;
65
+ const group = nulled.get(key) ?? { model: from, field, rows: [] };
66
+ // A key holds one target, deleted once: no row comes twice.
67
+ group.rows.push(found);
68
+ nulled.set(key, group);
69
+ }
70
+ else if (!deleted.has(keyOf(from, found.id))) {
71
+ // Not redirected: a row that stayed would keep a key to a
72
+ // deleted one (D-90).
73
+ const doomed = { model: from, row: found, redirected: false };
74
+ deleted.set(keyOf(from, found.id), doomed);
75
+ next.push(doomed);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ level = next;
81
+ }
82
+ // A row the cascade removes no longer references anything.
83
+ const stays = (from, id) => !deleted.has(keyOf(from, id));
84
+ for (const { model: from, field, id, to } of restricted) {
85
+ if (!stays(from, id))
86
+ continue;
87
+ throw new PublicError('RELATION_RESTRICTED', `${from.name}.${field} references a ${to} the delete removes.`, { details: { model: from.name, field } });
88
+ }
89
+ return {
90
+ // The roots last, in the order they were read.
91
+ deleted: [...deleted.values()]
92
+ .slice(roots.length)
93
+ .toReversed()
94
+ .concat(roots),
95
+ nulled: [...nulled.values()].map((group) => ({
96
+ ...group,
97
+ rows: group.rows.filter((found) => stays(group.model, found.id)),
98
+ })),
99
+ };
100
+ }
101
+ /** Whether deleting a row of `model` runs code of its own. */
102
+ const deleteRuns = (model) => redirectOf(model) !== undefined ||
103
+ hasHook(model, 'beforeDelete') ||
104
+ hasHook(model, 'afterDelete') ||
105
+ hasHook(model, 'afterDeleteCommit');
106
+ const anyOf = (filters) => filters.length === 1 && filters[0] !== undefined
107
+ ? filters[0]
108
+ : { OR: filters };
109
+ /**
110
+ * The changes and checks of a delete whose reach runs no code, so no row is
111
+ * loaded (D-102): the root goes by `where`, each level by the rows the level
112
+ * above removed, and `setNull` empties the keys naming them. `undefined` when
113
+ * a reached model has delete hooks or a redirect, a `setNull` one update
114
+ * hooks, a cascade comes back to a model it left, or a model is both deleted
115
+ * and emptied, or emptied twice.
116
+ */
117
+ export function cascadeWhere(models, model, where) {
118
+ const deleted = new Map();
119
+ /** The rows each `setNull` or `restrict` relation reaches, by relation. */
120
+ const reached = new Map();
121
+ const reach = (reference, filter) => {
122
+ const key = `${reference.model.name}.${reference.field}`;
123
+ const group = reached.get(key) ?? { ...reference, filters: [] };
124
+ group.filters.push(filter);
125
+ reached.set(key, group);
126
+ };
127
+ const visit = (at, filter, path, parent) => {
128
+ if (deleteRuns(at))
129
+ return false;
130
+ const entry = deleted.get(at.name) ?? {
131
+ filters: [],
132
+ depth: 0,
133
+ parents: new Map(),
134
+ };
135
+ entry.filters.push(filter);
136
+ entry.depth = Math.max(entry.depth, path.length);
137
+ if (parent !== undefined) {
138
+ entry.parents.set(`${parent.model.name}.${parent.field}`, parent);
139
+ }
140
+ deleted.set(at.name, entry);
141
+ const below = [...path, at.name];
142
+ for (const { model: from, field, onDelete } of referencesTo(models, at.name)) {
143
+ const child = { [field]: { is: filter } };
144
+ if (onDelete === 'cascade') {
145
+ const edge = { model: at, field };
146
+ if (below.includes(from.name) || !visit(from, child, below, edge)) {
147
+ return false;
148
+ }
149
+ }
150
+ else if (onDelete === 'setNull' && updated(from)) {
151
+ return false;
152
+ }
153
+ else {
154
+ const restricts = onDelete === 'restrict';
155
+ reach({ model: from, field, to: at.name, restricts }, child);
156
+ }
157
+ }
158
+ return true;
159
+ };
160
+ if (!visit(model, where, []))
161
+ return undefined;
162
+ const groups = [...reached.values()];
163
+ const nulls = groups.filter((group) => !group.restricts);
164
+ const emptied = new Set(nulls.map((group) => group.model.name));
165
+ // PostgreSQL gives two changes of the same rows in one statement no order.
166
+ const twice = emptied.size < nulls.length ||
167
+ [...emptied].some((name) => deleted.has(name));
168
+ if (twice)
169
+ return undefined;
170
+ // A row the cascade removes is not refusing.
171
+ const left = (from, filters) => {
172
+ const gone = deleted.get(from.name);
173
+ return gone === undefined
174
+ ? anyOf(filters)
175
+ : { AND: [anyOf(filters), { NOT: [anyOf(gone.filters)] }] };
176
+ };
177
+ const checks = groups
178
+ .filter((group) => group.restricts)
179
+ .map((group) => ({
180
+ check: {
181
+ model: group.model.name,
182
+ where: left(group.model, group.filters),
183
+ count: 0,
184
+ },
185
+ message: `${group.model.name}.${group.field} references a ${group.to} the delete removes.`,
186
+ restricted: { model: group.model.name, field: group.field },
187
+ }));
188
+ // A level after the levels above it: its parents have a lower depth.
189
+ const levels = [...deleted].toSorted(([, a], [, b]) => a.depth - b.depth);
190
+ const removedBy = (field, name) => ({
191
+ [field]: { removedBy: levels.findIndex(([each]) => each === name) },
192
+ });
193
+ const changes = [
194
+ ...levels.map(([name, { parents }]) => ({
195
+ kind: 'delete',
196
+ model: name,
197
+ where: parents.size === 0
198
+ ? where
199
+ : anyOf([...parents.values()].map((edge) => removedBy(edge.field, edge.model.name))),
200
+ })),
201
+ ...nulls.map((group) => ({
202
+ kind: 'update',
203
+ model: group.model.name,
204
+ where: removedBy(group.field, group.to),
205
+ set: { [group.field]: null, updatedAt: new Date(Date.now()) },
206
+ })),
207
+ ];
208
+ return { changes, checks };
209
+ }
@@ -0,0 +1,6 @@
1
+ import type { Internals } from './app-internals.ts';
2
+ /** The config file `--config` names, or the first one in `cwd`. */
3
+ export declare function configPath(cwd: string, given: string | undefined): string;
4
+ /** The application the config file default-exports. */
5
+ export declare function load(path: string): Promise<Internals>;
6
+ //# sourceMappingURL=cli-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-config.d.ts","sourceRoot":"","sources":["../src/cli-config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAWnD,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAazE;AAED,uDAAuD;AACvD,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAY3D"}
@@ -0,0 +1,45 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ // `ultimabase.config.ts` (D-105): where the commands find the application.
10
+ import { existsSync } from 'node:fs';
11
+ import { basename, join, resolve } from 'node:path';
12
+ import { pathToFileURL } from 'node:url';
13
+ import { internalsOf } from './app-internals.js';
14
+ import { Refused } from './errors.js';
15
+ const names = [
16
+ 'ultimabase.config.ts',
17
+ 'ultimabase.config.mts',
18
+ 'ultimabase.config.js',
19
+ 'ultimabase.config.mjs',
20
+ ];
21
+ /** The config file `--config` names, or the first one in `cwd`. */
22
+ export function configPath(cwd, given) {
23
+ if (given !== undefined) {
24
+ const path = resolve(cwd, given);
25
+ if (!existsSync(path))
26
+ throw new Refused(`There is no ${path}.`);
27
+ return path;
28
+ }
29
+ const found = names.map((name) => join(cwd, name)).find(existsSync);
30
+ if (found === undefined) {
31
+ throw new Refused(`There is no ultimabase.config.ts in ${cwd}: write one that default-exports defineApp(...), or pass --config <path>.`);
32
+ }
33
+ return found;
34
+ }
35
+ /** The application the config file default-exports. */
36
+ export async function load(path) {
37
+ const module = await import(__rewriteRelativeImportExtension(pathToFileURL(path).href));
38
+ const app = typeof module === 'object' && module !== null
39
+ ? internalsOf(Reflect.get(module, 'default'))
40
+ : undefined;
41
+ if (app === undefined) {
42
+ throw new Refused(`${basename(path)} does not default-export defineApp(...).`);
43
+ }
44
+ return app;
45
+ }
@@ -0,0 +1,14 @@
1
+ import type { Internals } from './app-internals.ts';
2
+ /** Applies the pending migrations, then stores the models' snapshot. */
3
+ export declare function migrateDeploy({ options }: Internals, dir: string): Promise<readonly string[]>;
4
+ /** What a deploy would do or refuse; `ok` when it has nothing to do. */
5
+ export declare function migrateStatus({ options }: Internals, dir: string): Promise<{
6
+ readonly lines: readonly string[];
7
+ readonly ok: boolean;
8
+ }>;
9
+ /**
10
+ * Drops a development database's tables, then applies every migration; one
11
+ * on another machine only with `force`.
12
+ */
13
+ export declare function reset({ options }: Internals, dir: string, force: boolean): Promise<readonly string[]>;
14
+ //# sourceMappingURL=cli-deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-deploy.d.ts","sourceRoot":"","sources":["../src/cli-deploy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAmGnD,wEAAwE;AACxE,wBAAsB,aAAa,CAClC,EAAE,OAAO,EAAE,EAAE,SAAS,EACtB,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAwB5B;AAED,wEAAwE;AACxE,wBAAsB,aAAa,CAClC,EAAE,OAAO,EAAE,EAAE,SAAS,EACtB,GAAG,EAAE,MAAM,GACT,OAAO,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;CAAE,CAAC,CA8BtE;AAmBD;;;GAGG;AACH,wBAAsB,KAAK,CAC1B,EAAE,OAAO,EAAE,EAAE,SAAS,EACtB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACZ,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAwC5B"}
@@ -0,0 +1,193 @@
1
+ import { MigrationFailed } from './adapter.js';
2
+ import { create, read } from './cli-migrate.js';
3
+ import { kernelLogger, schemaOf } from './lifecycle.js';
4
+ import { Refused } from './errors.js';
5
+ import { ahead, hashOf, held, migrationsOf } from './migrate.js';
6
+ const upToDate = 'The database is up to date.';
7
+ const production = () => process.env['NODE_ENV'] === 'production';
8
+ /** How a development database gets back to what the files build. */
9
+ const rebuild = () => production()
10
+ ? []
11
+ : [
12
+ 'Restore the dump you test on, or rebuild this database from the migrations with `npx ultimabase reset`.',
13
+ ];
14
+ const refused = (...lines) => new Refused([...lines, ...rebuild()].join('\n'));
15
+ /**
16
+ * The files the database has not applied, and the files it applied after
17
+ * the last one, which newer code wrote; refuses a file edited or removed.
18
+ */
19
+ function planOf(stored, files) {
20
+ const applied = stored?.applied ?? [];
21
+ const last = files.at(-1)?.name ?? '';
22
+ const later = [];
23
+ for (const { name, checksum } of applied) {
24
+ const file = files.find((given) => given.name === name);
25
+ if (file === undefined && name > last) {
26
+ later.push(name);
27
+ }
28
+ else if (file === undefined) {
29
+ throw refused(`${name} was applied to the database, and migrations/ no longer holds it: restore it from version control.`);
30
+ }
31
+ else if (file.checksum !== checksum) {
32
+ throw refused(`${name} was edited after it was applied to the database: restore it, and write the change with ${create}.`);
33
+ }
34
+ }
35
+ const pending = files.filter((file) => !applied.some((given) => given.name === file.name));
36
+ const [first] = pending;
37
+ if (first !== undefined && later.length > 0) {
38
+ throw refused(`${first.name} is pending, and the database applied ${later.join(', ')} after it, which migrations/ does not hold: deploy the code that holds them.`);
39
+ }
40
+ return { pending, later };
41
+ }
42
+ /** Why the files do not lead to the models whose hash is `hash`, if so. */
43
+ function behind(files, hash) {
44
+ const last = files.at(-1);
45
+ if (last === undefined) {
46
+ return `migrations/ holds no migration: run ${create} first.`;
47
+ }
48
+ if (last.schema === hash)
49
+ return undefined;
50
+ return `The models changed after ${last.name} was written: run ${create}, then deploy again.`;
51
+ }
52
+ const syncedSince = () => refused('Development sync changed this database since its last deploy: deploy applies migrations only to a database they built.');
53
+ /** Applies `files`; a refused one names those applied before it. */
54
+ async function deployed(migrations, files, to, again) {
55
+ const lines = files.map((file) => `Applied ${file.name}.`);
56
+ try {
57
+ await migrations.apply({ kind: 'deploy', files, to });
58
+ }
59
+ catch (error) {
60
+ if (!(error instanceof MigrationFailed))
61
+ throw error;
62
+ const done = files.findIndex((file) => file.name === error.at);
63
+ throw new Refused([
64
+ ...lines.slice(0, done),
65
+ `${error.at} was not applied: ${error.reason}`,
66
+ files[done]?.transaction === false
67
+ ? `It ran without a transaction, so nothing was rolled back: undo what it left, such as an invalid index, then ${again}.`
68
+ : `The database rolled it back: fix it, then ${again}.`,
69
+ ].join('\n'));
70
+ }
71
+ return lines;
72
+ }
73
+ /** Applies the pending migrations, then stores the models' snapshot. */
74
+ export async function migrateDeploy({ options }, dir) {
75
+ const { database } = options;
76
+ const migrations = migrationsOf(database);
77
+ const schema = schemaOf(options);
78
+ const to = migrations.snapshot(schema);
79
+ const hash = hashOf(to);
80
+ const { files } = await read(dir);
81
+ const late = behind(files, hash);
82
+ if (late !== undefined)
83
+ throw new Refused(late);
84
+ await database.start(schema, kernelLogger(options));
85
+ try {
86
+ const stored = await migrations.stored();
87
+ if (stored?.by === 'sync')
88
+ throw syncedSince();
89
+ const { pending, later } = planOf(stored, files);
90
+ // The code was rolled back: its migrations are applied, and more.
91
+ if (later.length > 0)
92
+ return [ahead(later)];
93
+ const current = stored?.snapshot !== undefined && hashOf(stored.snapshot) === hash;
94
+ if (pending.length === 0 && current)
95
+ return [upToDate];
96
+ const lines = await deployed(migrations, pending, to, 'deploy again');
97
+ return lines.length === 0 ? [upToDate] : lines;
98
+ }
99
+ finally {
100
+ await database.stop();
101
+ }
102
+ }
103
+ /** What a deploy would do or refuse; `ok` when it has nothing to do. */
104
+ export async function migrateStatus({ options }, dir) {
105
+ const { database } = options;
106
+ const migrations = migrationsOf(database);
107
+ const schema = schemaOf(options);
108
+ const hash = hashOf(migrations.snapshot(schema));
109
+ const { files } = await read(dir);
110
+ await database.start(schema, kernelLogger(options));
111
+ try {
112
+ const stored = await migrations.stored();
113
+ const late = behind(files, hash);
114
+ const { pending, later } = planOf(stored, files);
115
+ const lines = [
116
+ ...(late === undefined ? [] : [late]),
117
+ ...(stored?.by === 'sync' ? [syncedSince().message] : []),
118
+ ...pending.map((file) => `${file.name} is pending.`),
119
+ ];
120
+ if (lines.length > 0)
121
+ return { lines, ok: false };
122
+ if (later.length > 0)
123
+ return { lines: [ahead(later)], ok: true };
124
+ if (stored?.snapshot !== undefined && hashOf(stored.snapshot) === hash) {
125
+ return { lines: [upToDate], ok: true };
126
+ }
127
+ return {
128
+ lines: [
129
+ "The database's snapshot is not the models': run `npx ultimabase migrate deploy`.",
130
+ ],
131
+ ok: false,
132
+ };
133
+ }
134
+ finally {
135
+ await database.stop();
136
+ }
137
+ }
138
+ /** Drops every table; one another session holds stops the reset. */
139
+ async function dropAll(migrations) {
140
+ try {
141
+ return await migrations.reset();
142
+ }
143
+ catch (error) {
144
+ if (!(error instanceof MigrationFailed && error.locked))
145
+ throw error;
146
+ throw new Refused([
147
+ 'UltimaBase did not reset the database: another session held a table.',
148
+ ` ${error.at};`,
149
+ ` -- ${error.reason}`,
150
+ held,
151
+ ].join('\n'));
152
+ }
153
+ }
154
+ /**
155
+ * Drops a development database's tables, then applies every migration; one
156
+ * on another machine only with `force`.
157
+ */
158
+ export async function reset({ options }, dir, force) {
159
+ if (production()) {
160
+ throw new Refused('reset rebuilds only a development database: it is refused in production.');
161
+ }
162
+ const { database } = options;
163
+ const migrations = migrationsOf(database);
164
+ if (migrations.remote !== undefined && !force) {
165
+ throw new Refused(`reset drops every table, and ${migrations.remote} is not this machine: pass --force if it holds a development database.`);
166
+ }
167
+ const schema = schemaOf(options);
168
+ const { files, snapshot } = await read(dir);
169
+ await database.start(schema, kernelLogger(options));
170
+ try {
171
+ const dropped = await dropAll(migrations);
172
+ const lines = [
173
+ dropped.length === 0
174
+ ? 'The database held no table.'
175
+ : `Dropped ${dropped.join(', ')}.`,
176
+ ];
177
+ const last = files.at(-1);
178
+ if (last === undefined) {
179
+ return [
180
+ ...lines,
181
+ 'migrations/ holds no migration: the next start builds the tables.',
182
+ ];
183
+ }
184
+ lines.push(...(await deployed(migrations, files, snapshot, 'reset again')));
185
+ if (behind(files, hashOf(migrations.snapshot(schema))) !== undefined) {
186
+ lines.push(`The models changed after ${last.name} was written: the next start syncs them.`);
187
+ }
188
+ return lines;
189
+ }
190
+ finally {
191
+ await database.stop();
192
+ }
193
+ }
@@ -0,0 +1,16 @@
1
+ export type Io = {
2
+ readonly cwd: string;
3
+ readonly out: (text: string) => void;
4
+ readonly err: (text: string) => void;
5
+ readonly now: () => Date;
6
+ /** Runs Node with `args` until it exits, and returns its exit code. */
7
+ readonly node: (args: readonly string[]) => Promise<number>;
8
+ };
9
+ /** The file `dev` runs under `node --watch`. */
10
+ export declare const serveFile: string;
11
+ export declare const processIo: Io;
12
+ /** Runs the command `args` name, and returns its exit code. */
13
+ export declare function main(args: readonly string[], io?: Io): Promise<number>;
14
+ /** Starts the application of `path`, for `dev` (D-105). */
15
+ export declare function serve(path: string | undefined, io?: Io): Promise<number>;
16
+ //# sourceMappingURL=cli-main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-main.d.ts","sourceRoot":"","sources":["../src/cli-main.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,EAAE,GAAG;IAChB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,IAAI,CAAA;IACxB,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC3D,CAAA;AAED,gDAAgD;AAChD,eAAO,MAAM,SAAS,EAAE,MAGvB,CAAA;AAeD,eAAO,MAAM,SAAS,EAAE,EAMvB,CAAA;AAiFD,+DAA+D;AAC/D,wBAAsB,IAAI,CACzB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,GAAE,EAAc,GAChB,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED,2DAA2D;AAC3D,wBAAsB,KAAK,CAC1B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,EAAE,GAAE,EAAc,GAChB,OAAO,CAAC,MAAM,CAAC,CAQjB"}
@@ -0,0 +1,151 @@
1
+ // The `ultimabase` command (D-105): `dev`, `start`, `sync`, `reset` and
2
+ // `migrate`.
3
+ import { spawn } from 'node:child_process';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { parseArgs } from 'node:util';
7
+ import { configPath, load } from './cli-config.js';
8
+ import { migrateDeploy, migrateStatus, reset } from './cli-deploy.js';
9
+ import { migrateCreate, sync } from './cli-migrate.js';
10
+ import { Refused } from './errors.js';
11
+ const usage = `Usage: ultimabase <command> [--config <path>]
12
+
13
+ dev Start the application, sync its development
14
+ database, and restart it when a file changes.
15
+ start Start the application in production.
16
+ sync [--force] Sync the development database to the models.
17
+ reset [--force] Rebuild the development database from the
18
+ migrations: every table is dropped. --force
19
+ allows a database on another machine.
20
+ migrate create <name> Write the models' changes as a migration, or an
21
+ empty one for your SQL.
22
+ migrate deploy Apply the pending migrations.
23
+ migrate status Say what a deploy would do; exit 1 if anything.`;
24
+ /** The file `dev` runs under `node --watch`. */
25
+ export const serveFile = join(dirname(fileURLToPath(import.meta.url)), 'serve.ts');
26
+ function runNode(args) {
27
+ return new Promise((done, fail) => {
28
+ const child = spawn(process.execPath, args, { stdio: 'inherit' });
29
+ const forward = () => child.kill('SIGTERM');
30
+ process.on('SIGTERM', forward);
31
+ child.on('error', fail);
32
+ child.on('exit', (code) => {
33
+ process.off('SIGTERM', forward);
34
+ done(code ?? 1);
35
+ });
36
+ });
37
+ }
38
+ export const processIo = {
39
+ cwd: process.cwd(),
40
+ out: (text) => process.stdout.write(`${text}\n`),
41
+ err: (text) => process.stderr.write(`${text}\n`),
42
+ now: () => new Date(),
43
+ node: runNode,
44
+ };
45
+ /**
46
+ * An expected failure, a refusal or an error with a `code` (from Node,
47
+ * PostgreSQL or the kernel), prints its message; any other, a bug, its
48
+ * stack (D-105).
49
+ */
50
+ function report(error, io) {
51
+ if (!(error instanceof Error))
52
+ io.err(String(error));
53
+ else if (error.name === 'Refused' ||
54
+ typeof Reflect.get(error, 'code') === 'string')
55
+ io.err(error.message);
56
+ else
57
+ io.err(error.stack ?? error.message);
58
+ return 1;
59
+ }
60
+ /** Reads `args`, refusing an option it does not know with its first sentence. */
61
+ function read(args) {
62
+ try {
63
+ return parseArgs({
64
+ args: [...args],
65
+ allowPositionals: true,
66
+ options: {
67
+ config: { type: 'string' },
68
+ force: { type: 'boolean', default: false },
69
+ help: { type: 'boolean', default: false },
70
+ },
71
+ });
72
+ }
73
+ catch (error) {
74
+ const message = Reflect.get(Object(error), 'message');
75
+ const first = String(message).replace(/\.( .*)?$/s, '');
76
+ throw new Refused(`${first}. Run \`npx ultimabase --help\`.`);
77
+ }
78
+ }
79
+ async function run(args, io) {
80
+ const { values, positionals } = read(args);
81
+ const [command, sub, name, ...rest] = positionals;
82
+ const known = rest.length === 0 &&
83
+ (command === 'migrate'
84
+ ? (sub === 'create' && name !== undefined) ||
85
+ (['deploy', 'status'].includes(sub ?? '') && name === undefined)
86
+ : ['dev', 'start', 'sync', 'reset'].includes(command ?? '') &&
87
+ sub === undefined);
88
+ if (values.help) {
89
+ io.out(usage);
90
+ return 0;
91
+ }
92
+ if (!known) {
93
+ if (command !== undefined)
94
+ io.err(`Unknown command '${positionals.join(' ')}'.`);
95
+ io.err(usage);
96
+ return 1;
97
+ }
98
+ const path = configPath(io.cwd, values.config);
99
+ if (command === 'dev') {
100
+ process.env['NODE_ENV'] ??= 'development';
101
+ return io.node(['--watch', serveFile, path]);
102
+ }
103
+ if (command === 'start')
104
+ process.env['NODE_ENV'] ??= 'production';
105
+ const app = await load(path);
106
+ const dir = join(dirname(path), 'migrations');
107
+ if (command === 'start')
108
+ await app.app.start();
109
+ else if (command === 'sync') {
110
+ const said = await sync(app, values.force);
111
+ if (said !== undefined)
112
+ io.out(said);
113
+ }
114
+ else if (command === 'reset') {
115
+ for (const line of await reset(app, dir, values.force))
116
+ io.out(line);
117
+ }
118
+ else if (sub === 'deploy') {
119
+ for (const line of await migrateDeploy(app, dir))
120
+ io.out(line);
121
+ }
122
+ else if (sub === 'status') {
123
+ const { lines, ok } = await migrateStatus(app, dir);
124
+ for (const line of lines)
125
+ io.out(line);
126
+ return ok ? 0 : 1;
127
+ }
128
+ else
129
+ io.out(await migrateCreate(app, dir, name ?? '', io.now()));
130
+ return 0;
131
+ }
132
+ /** Runs the command `args` name, and returns its exit code. */
133
+ export async function main(args, io = processIo) {
134
+ try {
135
+ return await run(args, io);
136
+ }
137
+ catch (error) {
138
+ return report(error, io);
139
+ }
140
+ }
141
+ /** Starts the application of `path`, for `dev` (D-105). */
142
+ export async function serve(path, io = processIo) {
143
+ try {
144
+ const { app } = await load(configPath(io.cwd, path));
145
+ await app.start();
146
+ return 0;
147
+ }
148
+ catch (error) {
149
+ return report(error, io);
150
+ }
151
+ }
@@ -0,0 +1,20 @@
1
+ import type { Migration } from './adapter.ts';
2
+ import type { Internals } from './app-internals.ts';
3
+ export declare const header = "-- ultimabase schema ";
4
+ /** For a statement a transaction refuses: `create index concurrently`. */
5
+ export declare const noTransaction = "-- ultimabase no transaction";
6
+ export declare const create = "`npx ultimabase migrate create <name>`";
7
+ export type Folder = {
8
+ readonly files: readonly Migration[];
9
+ readonly snapshot: unknown;
10
+ };
11
+ /** The `.sql` files of `dir` in name order, and its `schema.json`. */
12
+ export declare function read(dir: string): Promise<Folder>;
13
+ /**
14
+ * Writes the migration from `schema.json` to the models; with no model
15
+ * changed, a file for the developer's SQL, as a data migration.
16
+ */
17
+ export declare function migrateCreate({ options }: Internals, dir: string, name: string, now: Date): Promise<string>;
18
+ /** Syncs a development database, losing data only with `force`. */
19
+ export declare function sync({ options }: Internals, force: boolean): Promise<string | undefined>;
20
+ //# sourceMappingURL=cli-migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-migrate.d.ts","sourceRoot":"","sources":["../src/cli-migrate.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAKnD,eAAO,MAAM,MAAM,0BAA0B,CAAA;AAE7C,0EAA0E;AAC1E,eAAO,MAAM,aAAa,iCAAiC,CAAA;AAC3D,eAAO,MAAM,MAAM,2CAA2C,CAAA;AAE9D,MAAM,MAAM,MAAM,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAA;IACpC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED,sEAAsE;AACtE,wBAAsB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCvD;AAuBD;;;GAGG;AACH,wBAAsB,aAAa,CAClC,EAAE,OAAO,EAAE,EAAE,SAAS,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,IAAI,GACP,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAED,mEAAmE;AACnE,wBAAsB,IAAI,CACzB,EAAE,OAAO,EAAE,EAAE,SAAS,EACtB,KAAK,EAAE,OAAO,GACZ,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmB7B"}