@mistriwork/engine 0.0.0-stage → 1.0.0-rc.10

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 (546) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +17 -2
  3. package/dist/adapters/adapter.d.ts +693 -0
  4. package/dist/adapters/adapter.js +444 -0
  5. package/dist/adapters/cache/cacheStore.d.ts +75 -0
  6. package/dist/adapters/cache/cacheStore.js +160 -0
  7. package/dist/adapters/cache/resp.d.ts +52 -0
  8. package/dist/adapters/cache/resp.js +237 -0
  9. package/dist/adapters/commands/canonicalJson.d.ts +19 -0
  10. package/dist/adapters/commands/canonicalJson.js +154 -0
  11. package/dist/adapters/connect.d.ts +29 -0
  12. package/dist/adapters/connect.js +40 -0
  13. package/dist/adapters/connectionBudget.d.ts +101 -0
  14. package/dist/adapters/connectionBudget.js +128 -0
  15. package/dist/adapters/dbLatency.d.ts +35 -0
  16. package/dist/adapters/dbLatency.js +79 -0
  17. package/dist/adapters/epochCache.d.ts +50 -0
  18. package/dist/adapters/epochCache.js +85 -0
  19. package/dist/adapters/http/egress.d.ts +48 -0
  20. package/dist/adapters/http/egress.js +175 -0
  21. package/dist/adapters/http/httpAdapter.d.ts +71 -0
  22. package/dist/adapters/http/httpAdapter.js +512 -0
  23. package/dist/adapters/http/httpDescription.d.ts +32 -0
  24. package/dist/adapters/http/httpDescription.js +246 -0
  25. package/dist/adapters/http/openapi.d.ts +98 -0
  26. package/dist/adapters/http/openapi.js +487 -0
  27. package/dist/adapters/layoutRefusal.d.ts +24 -0
  28. package/dist/adapters/layoutRefusal.js +35 -0
  29. package/dist/adapters/net/networkPolicy.d.ts +44 -0
  30. package/dist/adapters/net/networkPolicy.js +123 -0
  31. package/dist/adapters/net/untrustedDatabase.d.ts +18 -0
  32. package/dist/adapters/net/untrustedDatabase.js +120 -0
  33. package/dist/adapters/poolAdmission.d.ts +43 -0
  34. package/dist/adapters/poolAdmission.js +184 -0
  35. package/dist/adapters/poolMetrics.d.ts +121 -0
  36. package/dist/adapters/poolMetrics.js +185 -0
  37. package/dist/adapters/postgres.d.ts +436 -0
  38. package/dist/adapters/postgres.js +1890 -0
  39. package/dist/adapters/postgresCatalog.d.ts +96 -0
  40. package/dist/adapters/postgresCatalog.js +387 -0
  41. package/dist/adapters/postgresCatalogDigest.d.ts +30 -0
  42. package/dist/adapters/postgresCatalogDigest.js +87 -0
  43. package/dist/adapters/postgresCatalogRows.d.ts +62 -0
  44. package/dist/adapters/postgresCatalogRows.js +37 -0
  45. package/dist/adapters/postgresDates.d.ts +1 -0
  46. package/dist/adapters/postgresDates.js +39 -0
  47. package/dist/adapters/postgresMaintenance.d.ts +33 -0
  48. package/dist/adapters/postgresMaintenance.js +179 -0
  49. package/dist/adapters/postgresPool.d.ts +255 -0
  50. package/dist/adapters/postgresPool.js +838 -0
  51. package/dist/adapters/postgresPooler.d.ts +28 -0
  52. package/dist/adapters/postgresPooler.js +267 -0
  53. package/dist/adapters/postgresReadCatalog.d.ts +31 -0
  54. package/dist/adapters/postgresReadCatalog.js +226 -0
  55. package/dist/adapters/postgresReadSnapshot.d.ts +46 -0
  56. package/dist/adapters/postgresReadSnapshot.js +133 -0
  57. package/dist/adapters/postgresSchemaEpoch.d.ts +24 -0
  58. package/dist/adapters/postgresSchemaEpoch.js +149 -0
  59. package/dist/adapters/postgresTransactionHandle.d.ts +173 -0
  60. package/dist/adapters/postgresTransactionHandle.js +524 -0
  61. package/dist/adapters/postgresValues.d.ts +30 -0
  62. package/dist/adapters/postgresValues.js +75 -0
  63. package/dist/adapters/replicas.d.ts +70 -0
  64. package/dist/adapters/replicas.js +208 -0
  65. package/dist/adapters/reservedSql.d.ts +38 -0
  66. package/dist/adapters/reservedSql.js +264 -0
  67. package/dist/adapters/rolePrivileges.d.ts +17 -0
  68. package/dist/adapters/rolePrivileges.js +22 -0
  69. package/dist/adapters/scoped/keyRows.d.ts +65 -0
  70. package/dist/adapters/scoped/keyRows.js +173 -0
  71. package/dist/adapters/scoped/postgresScopeCatalog.d.ts +16 -0
  72. package/dist/adapters/scoped/postgresScopeCatalog.js +117 -0
  73. package/dist/adapters/scoped/scopedCommandOperations.d.ts +5 -0
  74. package/dist/adapters/scoped/scopedCommandOperations.js +48 -0
  75. package/dist/adapters/scoped/scopedReads.d.ts +20 -0
  76. package/dist/adapters/scoped/scopedReads.js +174 -0
  77. package/dist/adapters/scoped/scopedTransaction.d.ts +89 -0
  78. package/dist/adapters/scoped/scopedTransaction.js +303 -0
  79. package/dist/adapters/scoped/sqliteScopeCatalog.d.ts +16 -0
  80. package/dist/adapters/scoped/sqliteScopeCatalog.js +241 -0
  81. package/dist/adapters/scoped/updateGuard.d.ts +3 -0
  82. package/dist/adapters/scoped/updateGuard.js +40 -0
  83. package/dist/adapters/serverSettings.d.ts +45 -0
  84. package/dist/adapters/serverSettings.js +46 -0
  85. package/dist/adapters/sqlite.d.ts +262 -0
  86. package/dist/adapters/sqlite.js +1434 -0
  87. package/dist/adapters/sqliteCostlyReads.d.ts +33 -0
  88. package/dist/adapters/sqliteCostlyReads.js +49 -0
  89. package/dist/adapters/sqliteDecimals.d.ts +22 -0
  90. package/dist/adapters/sqliteDecimals.js +57 -0
  91. package/dist/adapters/sqliteHandles.d.ts +43 -0
  92. package/dist/adapters/sqliteHandles.js +143 -0
  93. package/dist/adapters/sqliteStatementProcess.d.ts +29 -0
  94. package/dist/adapters/sqliteStatementProcess.js +131 -0
  95. package/dist/adapters/statementControl.d.ts +34 -0
  96. package/dist/adapters/statementControl.js +90 -0
  97. package/dist/adapters/storeHost.d.ts +130 -0
  98. package/dist/adapters/storeHost.js +13 -0
  99. package/dist/adapters/telemetry/drivers.d.ts +21 -0
  100. package/dist/adapters/telemetry/drivers.js +321 -0
  101. package/dist/adapters/telemetry/metrics.d.ts +63 -0
  102. package/dist/adapters/telemetry/metrics.js +122 -0
  103. package/dist/adapters/telemetry/otlp.d.ts +12 -0
  104. package/dist/adapters/telemetry/otlp.js +320 -0
  105. package/dist/adapters/telemetry/otlpEncode.d.ts +48 -0
  106. package/dist/adapters/telemetry/otlpEncode.js +366 -0
  107. package/dist/adapters/telemetry/otlpEnv.d.ts +1 -0
  108. package/dist/adapters/telemetry/otlpEnv.js +12 -0
  109. package/dist/adapters/telemetry/serverTiming.d.ts +83 -0
  110. package/dist/adapters/telemetry/serverTiming.js +503 -0
  111. package/dist/adapters/telemetry/start.d.ts +10 -0
  112. package/dist/adapters/telemetry/start.js +25 -0
  113. package/dist/adapters/telemetry/statement.d.ts +16 -0
  114. package/dist/adapters/telemetry/statement.js +134 -0
  115. package/dist/adapters/telemetry/trace.d.ts +127 -0
  116. package/dist/adapters/telemetry/trace.js +295 -0
  117. package/dist/adapters/writeAhead.d.ts +27 -0
  118. package/dist/adapters/writeAhead.js +46 -0
  119. package/dist/adapters/writeAttribution.d.ts +18 -0
  120. package/dist/adapters/writeAttribution.js +23 -0
  121. package/dist/adapters/writeCapture.d.ts +73 -0
  122. package/dist/adapters/writeCapture.js +20 -0
  123. package/dist/adapters/writeLanes.d.ts +94 -0
  124. package/dist/adapters/writeLanes.js +232 -0
  125. package/dist/engine.d.ts +15 -0
  126. package/dist/engine.js +13 -0
  127. package/dist/server/auth/credentials.d.ts +61 -0
  128. package/dist/server/auth/credentials.js +260 -0
  129. package/dist/server/cacheService.d.ts +50 -0
  130. package/dist/server/cacheService.js +110 -0
  131. package/dist/server/cli/args.d.ts +176 -0
  132. package/dist/server/cli/args.js +342 -0
  133. package/dist/server/cli/bin.d.ts +5 -0
  134. package/dist/server/cli/bin.js +66 -0
  135. package/dist/server/cli/browser.d.ts +15 -0
  136. package/dist/server/cli/browser.js +37 -0
  137. package/dist/server/cli/config.d.ts +50 -0
  138. package/dist/server/cli/config.js +206 -0
  139. package/dist/server/cli/startup.d.ts +36 -0
  140. package/dist/server/cli/startup.js +128 -0
  141. package/dist/server/cli/startupOptions.d.ts +63 -0
  142. package/dist/server/cli/startupOptions.js +143 -0
  143. package/dist/server/cli/subcommand.d.ts +28 -0
  144. package/dist/server/cli/subcommand.js +32 -0
  145. package/dist/server/cli/usage.d.ts +13 -0
  146. package/dist/server/cli/usage.js +31 -0
  147. package/dist/server/clientHttp.d.ts +184 -0
  148. package/dist/server/clientHttp.js +700 -0
  149. package/dist/server/clientSurface.d.ts +128 -0
  150. package/dist/server/clientSurface.js +182 -0
  151. package/dist/server/columnGrants.d.ts +103 -0
  152. package/dist/server/columnGrants.js +730 -0
  153. package/dist/server/compileCache.d.ts +1 -0
  154. package/dist/server/compileCache.js +30 -0
  155. package/dist/server/config/locations.d.ts +1 -0
  156. package/dist/server/config/locations.js +15 -0
  157. package/dist/server/configPaths.d.ts +7 -0
  158. package/dist/server/configPaths.js +34 -0
  159. package/dist/server/configSections.d.ts +64 -0
  160. package/dist/server/configSections.js +72 -0
  161. package/dist/server/connectionRedaction.d.ts +30 -0
  162. package/dist/server/connectionRedaction.js +77 -0
  163. package/dist/server/connections.d.ts +353 -0
  164. package/dist/server/connections.js +978 -0
  165. package/dist/server/constraints.d.ts +82 -0
  166. package/dist/server/constraints.js +169 -0
  167. package/dist/server/demo.d.ts +9 -0
  168. package/dist/server/demo.js +554 -0
  169. package/dist/server/devclient.d.ts +108 -0
  170. package/dist/server/devclient.js +361 -0
  171. package/dist/server/evidence.d.ts +55 -0
  172. package/dist/server/evidence.js +323 -0
  173. package/dist/server/hiddenColumns.d.ts +32 -0
  174. package/dist/server/hiddenColumns.js +238 -0
  175. package/dist/server/http/baselineHeaders.d.ts +24 -0
  176. package/dist/server/http/baselineHeaders.js +12 -0
  177. package/dist/server/http/captured.d.ts +35 -0
  178. package/dist/server/http/captured.js +73 -0
  179. package/dist/server/http/clock.d.ts +1 -0
  180. package/dist/server/http/clock.js +14 -0
  181. package/dist/server/http/connectionSource.d.ts +4 -0
  182. package/dist/server/http/connectionSource.js +48 -0
  183. package/dist/server/http/context.d.ts +189 -0
  184. package/dist/server/http/context.js +11 -0
  185. package/dist/server/http/crawlerText.d.ts +15 -0
  186. package/dist/server/http/crawlerText.js +32 -0
  187. package/dist/server/http/dataRoutes.d.ts +88 -0
  188. package/dist/server/http/dataRoutes.js +137 -0
  189. package/dist/server/http/door.d.ts +57 -0
  190. package/dist/server/http/door.js +39 -0
  191. package/dist/server/http/exportRoute.d.ts +15 -0
  192. package/dist/server/http/exportRoute.js +320 -0
  193. package/dist/server/http/firstPaint.d.ts +108 -0
  194. package/dist/server/http/firstPaint.js +262 -0
  195. package/dist/server/http/httpOperationRoutes.d.ts +11 -0
  196. package/dist/server/http/httpOperationRoutes.js +50 -0
  197. package/dist/server/http/pageReads.d.ts +20 -0
  198. package/dist/server/http/pageReads.js +205 -0
  199. package/dist/server/http/pageRoutes.d.ts +39 -0
  200. package/dist/server/http/pageRoutes.js +291 -0
  201. package/dist/server/http/probes.d.ts +22 -0
  202. package/dist/server/http/probes.js +27 -0
  203. package/dist/server/http/rateLimit.d.ts +131 -0
  204. package/dist/server/http/rateLimit.js +351 -0
  205. package/dist/server/http/readRoutes.d.ts +9 -0
  206. package/dist/server/http/readRoutes.js +131 -0
  207. package/dist/server/http/readRouting.d.ts +20 -0
  208. package/dist/server/http/readRouting.js +80 -0
  209. package/dist/server/http/recordCompose.d.ts +67 -0
  210. package/dist/server/http/recordCompose.js +167 -0
  211. package/dist/server/http/recordWrites.d.ts +89 -0
  212. package/dist/server/http/recordWrites.js +298 -0
  213. package/dist/server/http/refusalReport.d.ts +38 -0
  214. package/dist/server/http/refusalReport.js +63 -0
  215. package/dist/server/http/reply.d.ts +130 -0
  216. package/dist/server/http/reply.js +394 -0
  217. package/dist/server/http/requestOrigin.d.ts +11 -0
  218. package/dist/server/http/requestOrigin.js +8 -0
  219. package/dist/server/http/router.d.ts +45 -0
  220. package/dist/server/http/router.js +22 -0
  221. package/dist/server/http/serverCore.d.ts +68 -0
  222. package/dist/server/http/serverCore.js +177 -0
  223. package/dist/server/http/traceRequest.d.ts +30 -0
  224. package/dist/server/http/traceRequest.js +224 -0
  225. package/dist/server/http/trust.d.ts +261 -0
  226. package/dist/server/http/trust.js +472 -0
  227. package/dist/server/http/writeRefusal.d.ts +33 -0
  228. package/dist/server/http/writeRefusal.js +24 -0
  229. package/dist/server/httpConnection.d.ts +49 -0
  230. package/dist/server/httpConnection.js +170 -0
  231. package/dist/server/impact.d.ts +55 -0
  232. package/dist/server/impact.js +47 -0
  233. package/dist/server/journal.d.ts +134 -0
  234. package/dist/server/journal.js +398 -0
  235. package/dist/server/keysetReadHttp.d.ts +52 -0
  236. package/dist/server/keysetReadHttp.js +85 -0
  237. package/dist/server/layouts.d.ts +49 -0
  238. package/dist/server/layouts.js +192 -0
  239. package/dist/server/migrate/cli.d.ts +55 -0
  240. package/dist/server/migrate/cli.js +517 -0
  241. package/dist/server/migrate/database.d.ts +37 -0
  242. package/dist/server/migrate/database.js +247 -0
  243. package/dist/server/migrate/desired.d.ts +76 -0
  244. package/dist/server/migrate/desired.js +292 -0
  245. package/dist/server/migrate/files.d.ts +34 -0
  246. package/dist/server/migrate/files.js +141 -0
  247. package/dist/server/migrate/flyway.d.ts +7 -0
  248. package/dist/server/migrate/flyway.js +69 -0
  249. package/dist/server/migrate/history.d.ts +70 -0
  250. package/dist/server/migrate/history.js +211 -0
  251. package/dist/server/migrate/runner.d.ts +100 -0
  252. package/dist/server/migrate/runner.js +409 -0
  253. package/dist/server/migrate/snapshot.d.ts +30 -0
  254. package/dist/server/migrate/snapshot.js +239 -0
  255. package/dist/server/migrate/target.d.ts +33 -0
  256. package/dist/server/migrate/target.js +82 -0
  257. package/dist/server/openapiDraft.d.ts +25 -0
  258. package/dist/server/openapiDraft.js +171 -0
  259. package/dist/server/operations/diagnostics.d.ts +101 -0
  260. package/dist/server/operations/diagnostics.js +284 -0
  261. package/dist/server/operations/requestMetrics.d.ts +33 -0
  262. package/dist/server/operations/requestMetrics.js +94 -0
  263. package/dist/server/packageVersion.d.ts +1 -0
  264. package/dist/server/packageVersion.js +11 -0
  265. package/dist/server/poolConfig.d.ts +34 -0
  266. package/dist/server/poolConfig.js +114 -0
  267. package/dist/server/product.d.ts +20 -0
  268. package/dist/server/product.js +12 -0
  269. package/dist/server/recordHistoryOperations.d.ts +39 -0
  270. package/dist/server/recordHistoryOperations.js +43 -0
  271. package/dist/server/recordWriteOperations.d.ts +111 -0
  272. package/dist/server/recordWriteOperations.js +329 -0
  273. package/dist/server/resourceReadHttp.d.ts +81 -0
  274. package/dist/server/resourceReadHttp.js +606 -0
  275. package/dist/server/resourceReadOperations.d.ts +147 -0
  276. package/dist/server/resourceReadOperations.js +452 -0
  277. package/dist/server/scaffoldMachine.d.ts +54 -0
  278. package/dist/server/scaffoldMachine.js +184 -0
  279. package/dist/server/seed/cli.d.ts +20 -0
  280. package/dist/server/seed/cli.js +164 -0
  281. package/dist/server/seed/lexicon.d.ts +26 -0
  282. package/dist/server/seed/lexicon.js +585 -0
  283. package/dist/server/seed/seed.d.ts +70 -0
  284. package/dist/server/seed/seed.js +779 -0
  285. package/dist/server/seed/words.d.ts +21 -0
  286. package/dist/server/seed/words.js +164 -0
  287. package/dist/server/serverRequests.d.ts +6 -0
  288. package/dist/server/serverRequests.js +33 -0
  289. package/dist/server/serverTimeouts.d.ts +33 -0
  290. package/dist/server/serverTimeouts.js +32 -0
  291. package/dist/server/shapeonly.d.ts +32 -0
  292. package/dist/server/shapeonly.js +110 -0
  293. package/dist/server/startupWarnings.d.ts +32 -0
  294. package/dist/server/startupWarnings.js +92 -0
  295. package/dist/shared/addressParams.d.ts +28 -0
  296. package/dist/shared/addressParams.js +47 -0
  297. package/dist/shared/appCards.d.ts +123 -0
  298. package/dist/shared/appCards.js +385 -0
  299. package/dist/shared/appComponents.d.ts +177 -0
  300. package/dist/shared/appComponents.js +422 -0
  301. package/dist/shared/appDocuments.d.ts +43 -0
  302. package/dist/shared/appDocuments.js +131 -0
  303. package/dist/shared/appEmbeds.d.ts +21 -0
  304. package/dist/shared/appEmbeds.js +24 -0
  305. package/dist/shared/appIcons.d.ts +28 -0
  306. package/dist/shared/appIcons.js +223 -0
  307. package/dist/shared/appLinks.d.ts +17 -0
  308. package/dist/shared/appLinks.js +124 -0
  309. package/dist/shared/appListColumns.d.ts +3 -0
  310. package/dist/shared/appListColumns.js +66 -0
  311. package/dist/shared/appLocale.d.ts +36 -0
  312. package/dist/shared/appLocale.js +86 -0
  313. package/dist/shared/appMessages.d.ts +44 -0
  314. package/dist/shared/appMessages.js +78 -0
  315. package/dist/shared/appNoun.d.ts +21 -0
  316. package/dist/shared/appNoun.js +32 -0
  317. package/dist/shared/autoBucket.d.ts +98 -0
  318. package/dist/shared/autoBucket.js +305 -0
  319. package/dist/shared/autoValues.d.ts +25 -0
  320. package/dist/shared/autoValues.js +25 -0
  321. package/dist/shared/breakdown.d.ts +228 -0
  322. package/dist/shared/breakdown.js +557 -0
  323. package/dist/shared/breakdowntext.d.ts +41 -0
  324. package/dist/shared/breakdowntext.js +400 -0
  325. package/dist/shared/builtInThemes.d.ts +2 -0
  326. package/dist/shared/builtInThemes.js +771 -0
  327. package/dist/shared/chartSeparation.d.ts +11 -0
  328. package/dist/shared/chartSeparation.js +58 -0
  329. package/dist/shared/clauses.d.ts +22 -0
  330. package/dist/shared/clauses.js +97 -0
  331. package/dist/shared/columnCaption.d.ts +16 -0
  332. package/dist/shared/columnCaption.js +90 -0
  333. package/dist/shared/commandFieldPath.d.ts +2 -0
  334. package/dist/shared/commandFieldPath.js +7 -0
  335. package/dist/shared/commandWire.d.ts +26 -0
  336. package/dist/shared/commandWire.js +41 -0
  337. package/dist/shared/computed.d.ts +161 -0
  338. package/dist/shared/computed.js +378 -0
  339. package/dist/shared/computedParse.d.ts +13 -0
  340. package/dist/shared/computedParse.js +278 -0
  341. package/dist/shared/computedValue.d.ts +47 -0
  342. package/dist/shared/computedValue.js +191 -0
  343. package/dist/shared/connection.d.ts +30 -0
  344. package/dist/shared/connection.js +1 -0
  345. package/dist/shared/csv.d.ts +20 -0
  346. package/dist/shared/csv.js +63 -0
  347. package/dist/shared/dashLayout.d.ts +30 -0
  348. package/dist/shared/dashLayout.js +132 -0
  349. package/dist/shared/dashboardAsks.d.ts +93 -0
  350. package/dist/shared/dashboardAsks.js +129 -0
  351. package/dist/shared/dashboardTrend.d.ts +20 -0
  352. package/dist/shared/dashboardTrend.js +50 -0
  353. package/dist/shared/declaredReferences.d.ts +50 -0
  354. package/dist/shared/declaredReferences.js +202 -0
  355. package/dist/shared/definedFields.d.ts +63 -0
  356. package/dist/shared/definedFields.js +103 -0
  357. package/dist/shared/definedValues.d.ts +63 -0
  358. package/dist/shared/definedValues.js +184 -0
  359. package/dist/shared/displayDefaults.d.ts +135 -0
  360. package/dist/shared/displayDefaults.js +529 -0
  361. package/dist/shared/dueDate.d.ts +6 -0
  362. package/dist/shared/dueDate.js +6 -0
  363. package/dist/shared/duration.d.ts +4 -0
  364. package/dist/shared/duration.js +23 -0
  365. package/dist/shared/fileBounds.d.ts +9 -0
  366. package/dist/shared/fileBounds.js +9 -0
  367. package/dist/shared/filter.d.ts +217 -0
  368. package/dist/shared/filter.js +305 -0
  369. package/dist/shared/firstPaint.d.ts +24 -0
  370. package/dist/shared/firstPaint.js +33 -0
  371. package/dist/shared/form.d.ts +21 -0
  372. package/dist/shared/form.js +25 -0
  373. package/dist/shared/formValues.d.ts +104 -0
  374. package/dist/shared/formValues.js +352 -0
  375. package/dist/shared/funnel.d.ts +45 -0
  376. package/dist/shared/funnel.js +108 -0
  377. package/dist/shared/httpOperations.d.ts +48 -0
  378. package/dist/shared/httpOperations.js +9 -0
  379. package/dist/shared/iconGlyphs.d.ts +4 -0
  380. package/dist/shared/iconGlyphs.js +151 -0
  381. package/dist/shared/iconWeights.d.ts +3 -0
  382. package/dist/shared/iconWeights.js +162 -0
  383. package/dist/shared/imageWidths.d.ts +32 -0
  384. package/dist/shared/imageWidths.js +38 -0
  385. package/dist/shared/keysetPage.d.ts +64 -0
  386. package/dist/shared/keysetPage.js +161 -0
  387. package/dist/shared/like.d.ts +20 -0
  388. package/dist/shared/like.js +29 -0
  389. package/dist/shared/listEmpty.d.ts +12 -0
  390. package/dist/shared/listEmpty.js +36 -0
  391. package/dist/shared/listFilters.d.ts +28 -0
  392. package/dist/shared/listFilters.js +84 -0
  393. package/dist/shared/listPresentations.d.ts +199 -0
  394. package/dist/shared/listPresentations.js +7 -0
  395. package/dist/shared/listQuestion.d.ts +52 -0
  396. package/dist/shared/listQuestion.js +137 -0
  397. package/dist/shared/lookPalettes.d.ts +62 -0
  398. package/dist/shared/lookPalettes.js +306 -0
  399. package/dist/shared/maskQuoted.d.ts +24 -0
  400. package/dist/shared/maskQuoted.js +48 -0
  401. package/dist/shared/mentionLabel.d.ts +19 -0
  402. package/dist/shared/mentionLabel.js +38 -0
  403. package/dist/shared/mentionLookup.d.ts +17 -0
  404. package/dist/shared/mentionLookup.js +64 -0
  405. package/dist/shared/menuCounts.d.ts +16 -0
  406. package/dist/shared/menuCounts.js +74 -0
  407. package/dist/shared/model.d.ts +79 -0
  408. package/dist/shared/model.js +153 -0
  409. package/dist/shared/money.d.ts +44 -0
  410. package/dist/shared/money.js +81 -0
  411. package/dist/shared/moneyEntry.d.ts +7 -0
  412. package/dist/shared/moneyEntry.js +27 -0
  413. package/dist/shared/mount.d.ts +118 -0
  414. package/dist/shared/mount.js +248 -0
  415. package/dist/shared/niceTicks.d.ts +26 -0
  416. package/dist/shared/niceTicks.js +50 -0
  417. package/dist/shared/order.d.ts +59 -0
  418. package/dist/shared/order.js +133 -0
  419. package/dist/shared/page.d.ts +135 -0
  420. package/dist/shared/page.js +1187 -0
  421. package/dist/shared/pageAnatomy.d.ts +99 -0
  422. package/dist/shared/pageAnatomy.js +370 -0
  423. package/dist/shared/pageConfig.d.ts +25 -0
  424. package/dist/shared/pageConfig.js +1308 -0
  425. package/dist/shared/pageEmptyActions.d.ts +12 -0
  426. package/dist/shared/pageEmptyActions.js +20 -0
  427. package/dist/shared/pageLayouts.d.ts +72 -0
  428. package/dist/shared/pageLayouts.js +205 -0
  429. package/dist/shared/pageModel.d.ts +785 -0
  430. package/dist/shared/pageModel.js +121 -0
  431. package/dist/shared/pagePaths.d.ts +180 -0
  432. package/dist/shared/pagePaths.js +869 -0
  433. package/dist/shared/pagePlans.d.ts +52 -0
  434. package/dist/shared/pagePlans.js +108 -0
  435. package/dist/shared/pageRoutes.d.ts +9 -0
  436. package/dist/shared/pageRoutes.js +9 -0
  437. package/dist/shared/query.d.ts +166 -0
  438. package/dist/shared/query.js +1302 -0
  439. package/dist/shared/queryDates.d.ts +12 -0
  440. package/dist/shared/queryDates.js +101 -0
  441. package/dist/shared/queryOperands.d.ts +99 -0
  442. package/dist/shared/queryOperands.js +131 -0
  443. package/dist/shared/reads.d.ts +103 -0
  444. package/dist/shared/reads.js +154 -0
  445. package/dist/shared/recordLabel.d.ts +51 -0
  446. package/dist/shared/recordLabel.js +123 -0
  447. package/dist/shared/recordReadKeys.d.ts +30 -0
  448. package/dist/shared/recordReadKeys.js +51 -0
  449. package/dist/shared/recordReads.d.ts +268 -0
  450. package/dist/shared/recordReads.js +368 -0
  451. package/dist/shared/recordview.d.ts +198 -0
  452. package/dist/shared/recordview.js +356 -0
  453. package/dist/shared/referenceModel.d.ts +58 -0
  454. package/dist/shared/referenceModel.js +194 -0
  455. package/dist/shared/refusal.d.ts +64 -0
  456. package/dist/shared/refusal.js +144 -0
  457. package/dist/shared/relatedCreate.d.ts +30 -0
  458. package/dist/shared/relatedCreate.js +162 -0
  459. package/dist/shared/resourceHttp.d.ts +464 -0
  460. package/dist/shared/resourceHttp.js +1 -0
  461. package/dist/shared/richText.d.ts +85 -0
  462. package/dist/shared/richText.js +305 -0
  463. package/dist/shared/rollups.d.ts +143 -0
  464. package/dist/shared/rollups.js +64 -0
  465. package/dist/shared/rowScope.d.ts +72 -0
  466. package/dist/shared/rowScope.js +376 -0
  467. package/dist/shared/runtimeTables.d.ts +44 -0
  468. package/dist/shared/runtimeTables.js +79 -0
  469. package/dist/shared/schema.d.ts +523 -0
  470. package/dist/shared/schema.js +627 -0
  471. package/dist/shared/schematext.d.ts +38 -0
  472. package/dist/shared/schematext.js +101 -0
  473. package/dist/shared/scopeCatalog.d.ts +29 -0
  474. package/dist/shared/scopeCatalog.js +468 -0
  475. package/dist/shared/servedApp.d.ts +681 -0
  476. package/dist/shared/servedApp.js +45 -0
  477. package/dist/shared/sessionLease.d.ts +34 -0
  478. package/dist/shared/sessionLease.js +3 -0
  479. package/dist/shared/shape.d.ts +55 -0
  480. package/dist/shared/shape.js +168 -0
  481. package/dist/shared/slug.d.ts +1 -0
  482. package/dist/shared/slug.js +12 -0
  483. package/dist/shared/sortText.d.ts +14 -0
  484. package/dist/shared/sortText.js +26 -0
  485. package/dist/shared/sqlRelation.d.ts +4 -0
  486. package/dist/shared/sqlRelation.js +8 -0
  487. package/dist/shared/sqlsaved.d.ts +44 -0
  488. package/dist/shared/sqlsaved.js +63 -0
  489. package/dist/shared/stitchedPaths.d.ts +27 -0
  490. package/dist/shared/stitchedPaths.js +38 -0
  491. package/dist/shared/structuredFilter.d.ts +22 -0
  492. package/dist/shared/structuredFilter.js +1 -0
  493. package/dist/shared/subtitlePath.d.ts +20 -0
  494. package/dist/shared/subtitlePath.js +24 -0
  495. package/dist/shared/suggest.d.ts +18 -0
  496. package/dist/shared/suggest.js +59 -0
  497. package/dist/shared/tableSizing.d.ts +4 -0
  498. package/dist/shared/tableSizing.js +9 -0
  499. package/dist/shared/tagLists.d.ts +57 -0
  500. package/dist/shared/tagLists.js +34 -0
  501. package/dist/shared/textRules.d.ts +54 -0
  502. package/dist/shared/textRules.js +133 -0
  503. package/dist/shared/theme.d.ts +94 -0
  504. package/dist/shared/theme.js +800 -0
  505. package/dist/shared/themeColor.d.ts +34 -0
  506. package/dist/shared/themeColor.js +109 -0
  507. package/dist/shared/themeFaces.d.ts +30 -0
  508. package/dist/shared/themeFaces.js +42 -0
  509. package/dist/shared/themeInk.d.ts +20 -0
  510. package/dist/shared/themeInk.js +52 -0
  511. package/dist/shared/themeLooks.d.ts +9 -0
  512. package/dist/shared/themeLooks.js +572 -0
  513. package/dist/shared/themeParts.d.ts +26 -0
  514. package/dist/shared/themeParts.js +71 -0
  515. package/dist/shared/themePartsCheck.d.ts +21 -0
  516. package/dist/shared/themePartsCheck.js +91 -0
  517. package/dist/shared/themePresets.d.ts +85 -0
  518. package/dist/shared/themePresets.js +139 -0
  519. package/dist/shared/themeRecipeWords.d.ts +156 -0
  520. package/dist/shared/themeRecipeWords.js +156 -0
  521. package/dist/shared/themeRecipes.d.ts +213 -0
  522. package/dist/shared/themeRecipes.js +234 -0
  523. package/dist/shared/themeResolve.d.ts +55 -0
  524. package/dist/shared/themeResolve.js +52 -0
  525. package/dist/shared/themeTokens.d.ts +107 -0
  526. package/dist/shared/themeTokens.js +116 -0
  527. package/dist/shared/themeType.d.ts +38 -0
  528. package/dist/shared/themeType.js +205 -0
  529. package/dist/shared/unitWord.d.ts +4 -0
  530. package/dist/shared/unitWord.js +37 -0
  531. package/dist/shared/view.d.ts +195 -0
  532. package/dist/shared/view.js +987 -0
  533. package/dist/shared/viewLabels.d.ts +47 -0
  534. package/dist/shared/viewLabels.js +459 -0
  535. package/dist/shared/viewPaths.d.ts +248 -0
  536. package/dist/shared/viewPaths.js +294 -0
  537. package/dist/shared/viewtext.d.ts +57 -0
  538. package/dist/shared/viewtext.js +282 -0
  539. package/dist/shared/vocabulary.d.ts +71 -0
  540. package/dist/shared/vocabulary.js +249 -0
  541. package/dist/shared/weekStart.d.ts +7 -0
  542. package/dist/shared/weekStart.js +36 -0
  543. package/mark.svg +1 -0
  544. package/package.json +261 -4
  545. package/schema/model.d.ts +298 -0
  546. package/schema/theme.d.ts +587 -0
@@ -0,0 +1,167 @@
1
+ import { findTable } from '../../shared/schema.js';
2
+ import { validatePage } from '../../shared/page.js';
3
+ import { RECORD_COMPOSE_PATH, recordPageReads, rowBody, whenBody, } from '../../shared/recordReads.js';
4
+ import { canonicalText } from '../../shared/recordReadKeys.js';
5
+ import { CapturedReply } from './captured.js';
6
+ import { HttpError, send } from './reply.js';
7
+ export const COMPOSE_PATH = RECORD_COMPOSE_PATH;
8
+ /** How many related rows a reader may ask for a page: the sizes the list offers (client relatedRows.ts), and no more. */
9
+ const MOST_ROWS = 50;
10
+ /** Commands asked about at once, as the availability route takes them. */
11
+ const MOST_CHECKS = 16;
12
+ const failed = (answer) => !answer ||
13
+ answer.status >= 400 ||
14
+ Boolean(answer.body &&
15
+ typeof answer.body === 'object' &&
16
+ ('error' in answer.body ||
17
+ ('errors' in answer.body &&
18
+ Array.isArray(answer.body.errors) &&
19
+ answer.body.errors.length > 0)));
20
+ export async function composeRecordPage({ ctx, source, body, post, hooks, }) {
21
+ const { res, identity } = ctx;
22
+ const key = body.key;
23
+ const rows = body.rows ?? 10;
24
+ const tab = body.tab;
25
+ if (!key ||
26
+ typeof key !== 'object' ||
27
+ Array.isArray(key) ||
28
+ typeof rows !== 'number' ||
29
+ !Number.isInteger(rows) ||
30
+ rows < 1 ||
31
+ rows > MOST_ROWS ||
32
+ (tab !== undefined && (typeof tab !== 'string' || tab.length > 512)) ||
33
+ (typeof body.page !== 'string' && (!body.page || typeof body.page !== 'object' || Array.isArray(body.page)))) {
34
+ throw new HttpError(400, 'A composed record page names a page, a record key and a page size.');
35
+ }
36
+ /* A part asked on the page's behalf: through the route its section's own
37
+ request reaches, answered into memory. What that route would refuse, or
38
+ throw, leaves the part out. */
39
+ const ask = async (path, given) => {
40
+ if (!hooks.mayAsk(ctx, path))
41
+ return undefined;
42
+ const reply = new CapturedReply();
43
+ try {
44
+ await post(reply.asResponse(), path, given);
45
+ }
46
+ catch {
47
+ return undefined;
48
+ }
49
+ return reply.answer();
50
+ };
51
+ const record = key;
52
+ const resources = hooks.resources(ctx, source);
53
+ const names = {
54
+ schema: source.schema,
55
+ app: ctx.appConfigured,
56
+ recordLabel: (table) => resources[table]?.recordLabel,
57
+ defined: (table) => resources[table]?.defined,
58
+ };
59
+ const options = { rows, history: true, ...(tab === undefined ? {} : { tab: tab }) };
60
+ /* What the page reads that its plan does not decide — the record's row,
61
+ its history, its verbs, each `when` — is asked beside the plan, from
62
+ the page as the plan will find it; the lists and figures the plan
63
+ builds are asked once it comes. Should the plan's page not be that
64
+ page after all, the early answers are dropped and asked again. */
65
+ const reading = (page, base, plans, started = []) => {
66
+ const answered = [];
67
+ const keep = (asked, answer) => {
68
+ if (!failed(answer))
69
+ answered.push({ ...asked, status: answer.status, reply: answer.body });
70
+ };
71
+ const { row, parts, reads } = recordPageReads(names, page, base, plans, record, options);
72
+ /* The record's fixed edits ask their `when` of the row, as a section does. */
73
+ const actions = resources[base.id]?.actions ?? [];
74
+ for (const when of new Set(actions.flatMap((action) => (!action.command && action.when ? [action.when] : [])))) {
75
+ reads.push({ path: '/api/run', body: whenBody(base, record, when) });
76
+ }
77
+ const whole = async () => {
78
+ const asked = { path: '/api/view/run', body: rowBody(page.id, base, record, row, 'row') };
79
+ const answer = await ask(asked.path, asked.body);
80
+ if (!failed(answer))
81
+ return keep(asked, answer);
82
+ /* One column this reader may not read refuses the whole row: each reader of it is asked alone, as it would ask. */
83
+ await Promise.all(parts.map(async ({ part, columns }) => {
84
+ const alone = { path: '/api/view/run', body: rowBody(page.id, base, record, columns, part) };
85
+ keep(alone, await ask(alone.path, alone.body));
86
+ }));
87
+ };
88
+ const commands = actions.flatMap((action) => (action.command && action.command.target === base.id ? [action.command] : []));
89
+ const checks = [...new Map(commands.map((command) => [command.id, command])).values()]
90
+ .slice(0, MOST_CHECKS)
91
+ .map((command) => ({ command: command.id, commandVersion: command.version, keys: [record] }));
92
+ const availability = async () => {
93
+ if (!checks.length || identity.id === undefined || !hooks.availability || !hooks.mayAsk(ctx, '/api/commands/availability'))
94
+ return;
95
+ const asked = { path: '/api/commands/availability', body: { checks } };
96
+ keep(asked, await hooks.availability(ctx, checks));
97
+ };
98
+ const begun = new Map(started.map(([asked, answer]) => [readKey(asked), answer]));
99
+ /* The page's own row first, its verbs last: they are advice, and wait behind what is shown. */
100
+ const done = Promise.all([
101
+ whole(),
102
+ ...reads.map(async (asked) => keep(asked, await (begun.get(readKey(asked)) ?? ask(asked.path, asked.body)))),
103
+ availability(),
104
+ ]);
105
+ return { reads: new Set(reads.map((asked) => readKey(asked))), done: done.then(() => answered) };
106
+ };
107
+ const expected = expectedPage(ctx, source, body.page, hooks);
108
+ const expectedBase = expected && findTable(source.schema, expected.base);
109
+ /* The history is asked first: it is the longest chain (its store is
110
+ attested, then read), and a reader pool is a queue. Then the plan, which
111
+ the lists wait on; then the rest. */
112
+ const history = expected && expectedBase
113
+ ? recordPageReads(names, expected, expectedBase, [], record, options)
114
+ .reads.filter((asked) => asked.path === '/api/record-history')
115
+ .map((asked) => [asked, ask(asked.path, asked.body)])
116
+ : [];
117
+ const planning = ask('/api/pages/plan', { page: body.page, key });
118
+ const early = expected && expectedBase ? reading(expected, expectedBase, [], history) : undefined;
119
+ const plan = await planning;
120
+ const planned = failed(plan) ? undefined : plan.body;
121
+ const base = planned && findTable(source.schema, planned.page.base);
122
+ /* No plan, no page: the client asks the plan itself, and is told why in
123
+ the plan's own words. What was asked beside it is dropped, once it has
124
+ settled: every read ends inside the request (and the row policy's
125
+ context) that asked it. */
126
+ if (!planned || !base) {
127
+ await Promise.allSettled([early?.done, ...history.map(([, answer]) => answer)]);
128
+ return send(res, 200, { planned: false });
129
+ }
130
+ const kept = early && canonicalText(expected) === canonicalText(planned.page) ? early : undefined;
131
+ if (early && !kept)
132
+ await early.done;
133
+ let answered;
134
+ if (kept) {
135
+ /* Only what the plan adds: its lists, figures, timelines and custom reads. */
136
+ const { reads } = recordPageReads(names, planned.page, base, planned.plans, record, options);
137
+ const later = reads.filter((asked) => !kept.reads.has(readKey(asked)));
138
+ const more = await Promise.all(later.map(async (asked) => ({ asked, answer: await ask(asked.path, asked.body) })));
139
+ answered = [
140
+ ...(await kept.done),
141
+ ...more.flatMap(({ asked, answer }) => (failed(answer) ? [] : [{ ...asked, status: answer.status, reply: answer.body }])),
142
+ ];
143
+ }
144
+ else
145
+ answered = await reading(planned.page, base, planned.plans).done;
146
+ send(res, 200, { planned: true, page: planned.page, plans: planned.plans, reads: answered });
147
+ }
148
+ const readKey = (asked) => `${asked.path}\u0000${canonicalText(asked.body)}`;
149
+ /**
150
+ * The page the plan will find, found as `/api/pages/plan` finds it
151
+ * (authoringRoutes.ts): given whole, the table's suggested page, or the
152
+ * connection's configured page by name — validated against this schema.
153
+ * Only to ask the page's own reads beside the plan; the plan still decides,
154
+ * and an answer read for any other page is dropped.
155
+ */
156
+ function expectedPage(ctx, source, asked, hooks) {
157
+ const name = ctx.registry.list().find((c) => c.id === source.connectionId)?.name;
158
+ const found = typeof asked === 'object' && asked !== null
159
+ ? asked
160
+ : String(asked ?? '').startsWith('suggested:')
161
+ ? hooks.suggested(source, String(asked).slice('suggested:'.length))
162
+ : ctx.configPages.find((page) => page.id === String(asked ?? '') && (!page.connection || page.connection === source.connectionId || page.connection === name));
163
+ if (!found || typeof found.base !== 'string' || !Array.isArray(found.sections))
164
+ return undefined;
165
+ const page = validatePage(source.schema, found);
166
+ return page.error ? undefined : page;
167
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The record writes both products serve: update, insert (a file's upload
3
+ * among them) and delete. Every one asks the product's write gate first
4
+ * (`gates.write`: the connection's `writable` flag, then the reader's
5
+ * grant), and every committed write goes through the one recorder that
6
+ * anchors it to the source it touched, then to the product's `after.write`.
7
+ *
8
+ * What an App adds is `RecordRules`: the words its refusals are said in, the
9
+ * label its history gives an edit, what it does not delete, a New form's
10
+ * file, tags and lines, and replies that say no more than an App reader is
11
+ * told. Without them a write is the database browser's: journaled for its
12
+ * Undo, and refused in the database's own words.
13
+ */
14
+ import type { ServerResponse } from 'node:http';
15
+ import { type InsertRequest, type InsertResult } from '../../adapters/adapter.js';
16
+ import { type LineCollection } from '../recordWriteOperations.js';
17
+ import { sendResource } from './reply.js';
18
+ import type { PostContext } from './context.js';
19
+ type Values = Record<string, string | number | boolean | null>;
20
+ /** What a write that failed says, in its errors list: a message, and in an App its code and safe fields. */
21
+ type WriteFailure = NonNullable<Parameters<typeof sendResource<'/api/insert'>>[3]['errors']>[number];
22
+ /** A file placed before its row is written, discarded when the row is not (files/fileStorage.ts). */
23
+ interface PlacedValues {
24
+ readonly values: Values;
25
+ discard(): Promise<void>;
26
+ }
27
+ /** What a New form sends beside its record, judged: the link rows its tags are, and its lines. */
28
+ interface Alongside {
29
+ readonly alongside?: (result: InsertResult) => InsertRequest[];
30
+ readonly lines?: LineCollection[];
31
+ }
32
+ /** An answer a rule gave instead of going on. */
33
+ interface Answered {
34
+ readonly status: number;
35
+ readonly error: string;
36
+ }
37
+ /**
38
+ * An App's rules for its record writes (mistri: http/appRecordWrites.ts).
39
+ * Absent, the database browser's: a write is journaled, the upload, tags and
40
+ * lines of a New form are refused as no App declares them, and a failure is
41
+ * said in the database's own words.
42
+ */
43
+ export interface RecordRules {
44
+ /** What the record history calls an edit (recordHistory.ts updateActionLabel). */
45
+ label(post: PostContext, table: string, values: Values): string | undefined;
46
+ /** Whether the App deletes rows of `tableId` at all (appDeletable). */
47
+ deletes(post: PostContext, tableId: string): boolean;
48
+ /** An upload's columns, placed where its resource keeps its bytes. */
49
+ upload(post: PostContext, tableId: string | undefined): Promise<PlacedValues | Answered>;
50
+ /** A New form's tags and lines, judged against what the App declares. */
51
+ alongside(post: PostContext, tableId: string | undefined): Answered | Alongside;
52
+ /** The App's own rules refusing the record (its validation, a line's `consumes`): answered in the App's shape. True when answered. */
53
+ refused(res: ServerResponse, path: '/api/insert' | '/api/update', error: unknown): boolean;
54
+ /** A write that failed, said by code and never in a driver's words (appErrors.ts). */
55
+ failure(post: PostContext, error: unknown, completed: boolean): WriteFailure;
56
+ /** A delete's impact report, when a count fails. */
57
+ impact(error: unknown): string;
58
+ /** A reply cut to what an App reader is told (appResponses.ts). */
59
+ reply(body: Record<string, unknown>, kind: 'read' | 'write'): Record<string, unknown>;
60
+ }
61
+ /** What a product decides about the record writes. */
62
+ export interface RecordHooks {
63
+ readonly gates: {
64
+ /** Why a write to `tableId` is refused, or null. An update may reach a table a role runs fixed actions on. */
65
+ write(post: PostContext, tableId: string | undefined, update?: boolean): string | null;
66
+ };
67
+ readonly after?: {
68
+ /** A committed write's record: its search document follows it, queued and never awaited. */
69
+ write(post: PostContext, table: string, key: Record<string, unknown>): void;
70
+ };
71
+ readonly rules?: RecordRules;
72
+ }
73
+ /**
74
+ * What every write on this request shares: the options the recorded
75
+ * operations take — the source's own adapter, and whether the write is
76
+ * journaled. The database browser journals every write, for its Undo; an App
77
+ * has no Undo and journals none (recordWriteOperations.ts `unrecorded`). The
78
+ * import route (importRoutes.ts) writes through the same.
79
+ */
80
+ export declare function recordedWrites(post: Pick<PostContext, 'adapter' | 'connection' | 'who' | 'resources'>, journal: boolean): {
81
+ writeOptions: (table: string) => {
82
+ adapter: import("@mistriwork/engine/adapters/adapter.js").Adapter;
83
+ connection: string;
84
+ by: string | undefined;
85
+ journal: boolean;
86
+ };
87
+ };
88
+ export declare function recordWriteRoutes(post: PostContext, hooks: RecordHooks): Promise<boolean>;
89
+ export {};
@@ -0,0 +1,298 @@
1
+ import { Refusal, TransactionOutcomeUnknown, WriteConflict } from '../../adapters/adapter.js';
2
+ import { attributeAction } from '../../adapters/writeAttribution.js';
3
+ import { isPoolBusy } from '../../adapters/poolMetrics.js';
4
+ import { lockTimedOut } from '../../adapters/writeLanes.js';
5
+ import { deletionDependents, recordedDelete, recordedInsert, recordedUpdate } from '../recordWriteOperations.js';
6
+ import { AppAccessDenied } from '../columnGrants.js';
7
+ import { deleteImpact } from '../impact.js';
8
+ import { explainWriteFailure, writeFailureOutcome, writeRefusalStatus } from '../constraints.js';
9
+ import { findTable } from '../../shared/schema.js';
10
+ import { FILE_UPLOAD_PATH, bytesLabelled, send, sendBusy, sendResource, sendRowLocked } from './reply.js';
11
+ /* A failed insert or update's status. The database refusing it for good — a key taken, a reference to nothing, a
12
+ value its column cannot hold — is 409 or 422 (constraints.ts writeRefusalStatus); it was a 200 whose body alone
13
+ said `rejected`, and a client that read the status took a refused write for a saved one (a live load test). One
14
+ that may have landed, or that nothing recognizes as refused, keeps the 200 and its `unknown` outcome. */
15
+ const refusedStatus = (err, completed) => (completed ? 200 : (writeRefusalStatus(err) ?? 200));
16
+ /**
17
+ * What every write on this request shares: the options the recorded
18
+ * operations take — the source's own adapter, and whether the write is
19
+ * journaled. The database browser journals every write, for its Undo; an App
20
+ * has no Undo and journals none (recordWriteOperations.ts `unrecorded`). The
21
+ * import route (importRoutes.ts) writes through the same.
22
+ */
23
+ export function recordedWrites(post, journal) {
24
+ const { adapter, connection, who, resources } = post;
25
+ // resources has already been authorized. Anchor the write to the exact
26
+ // source without reopening an unguarded registry adapter or using its default.
27
+ const writeOptions = (table) => {
28
+ const owner = resources?.connectionFor(table) ?? connection;
29
+ return { adapter: resources?.forConnection(owner) ?? adapter, connection: owner, by: who, journal };
30
+ };
31
+ return { writeOptions };
32
+ }
33
+ export async function recordWriteRoutes(post, hooks) {
34
+ const { res, path, body, adapter, schema, registry, access, wholeRowWrites } = post;
35
+ const rules = hooks.rules;
36
+ const { writeOptions } = recordedWrites(post, !rules);
37
+ const indexed = (table, key) => hooks.after?.write(post, table, key);
38
+ /* A record's lines are records of their own, and what follows a write follows them too. */
39
+ const linesIndexed = (lines) => {
40
+ for (const line of lines ?? [])
41
+ for (const key of line.written ?? [])
42
+ indexed(line.table, key);
43
+ };
44
+ /* A file's object and hash are written for the reader and never shown to it (appAccess.ts withFileColumns). */
45
+ const internal = access?.internal?.get(String(body.table ?? ''));
46
+ const shown = (record) => internal && record && typeof record === 'object' && !Array.isArray(record)
47
+ ? Object.fromEntries(Object.entries(record).filter(([column]) => !internal.has(column)))
48
+ : record;
49
+ const sendData = (value) => {
50
+ const reply = { ...value };
51
+ for (const field of ['applied', 'row', 'before'])
52
+ if (field in reply)
53
+ reply[field] = bytesLabelled(shown(reply[field]));
54
+ send(res, 200, rules ? rules.reply(reply, 'write') : reply);
55
+ };
56
+ const failureOf = async (err, completed, values) => rules
57
+ ? rules.failure(post, err, completed)
58
+ : { message: await explainWriteFailure(adapter, schema, String(body.table ?? ''), values, err.message) };
59
+ /**
60
+ * Update one record through the shared transaction and history service.
61
+ *
62
+ * Note what is *not* checked here: whether the client thinks it is in write
63
+ * mode. That toggle is a UI affordance, and a guard that lives in the
64
+ * browser is not a guard. The adapter independently verifies the key is the
65
+ * whole primary key, counts the matching rows, and refuses anything other
66
+ * than exactly one.
67
+ */
68
+ if (path === '/api/update') {
69
+ /* The gate the server can actually enforce.
70
+
71
+ Write mode in the browser is a deliberate act before typing, and it is
72
+ not a permission — the client decides whether to *offer* editing, and a
73
+ guard in the browser is not a guard. Until a connection says it is
74
+ writable, anything that could reach this endpoint could write to it —
75
+ and an app may narrow that to the tables it names. */
76
+ const refused = hooks.gates.write(post, findTable(schema, String(body.table ?? ''))?.id, true);
77
+ if (refused) {
78
+ send(res, 403, { error: refused, code: 'forbidden' });
79
+ return true;
80
+ }
81
+ if (!adapter.update) {
82
+ send(res, 501, { error: `${schema.dialect} connections are read-only in this build.` });
83
+ return true;
84
+ }
85
+ /* `values` is the real shape; `column`/`value` is accepted as the
86
+ one-field form so a single edit does not have to build an object. */
87
+ const values = (body.values ?? (body.column ? { [String(body.column)]: body.value ?? null } : {}));
88
+ let completed = false;
89
+ try {
90
+ const table = String(body.table ?? '');
91
+ const key = (body.key ?? {});
92
+ const expected = body.expected;
93
+ const { result, undoId } = await attributeAction(rules?.label(post, table, values), () => recordedUpdate(writeOptions(table), { table, key, values, ...(expected === undefined ? {} : { expected }) }));
94
+ completed = true;
95
+ indexed(table, key);
96
+ sendData({ ...result, ...(undoId === undefined ? {} : { undoId }) });
97
+ return true;
98
+ }
99
+ catch (err) {
100
+ if (!completed && lockTimedOut(err)) {
101
+ sendRowLocked(res);
102
+ return true;
103
+ }
104
+ if (!completed && isPoolBusy(err)) {
105
+ sendBusy(res, {}, true, err);
106
+ return true;
107
+ }
108
+ if (!completed && rules?.refused(res, path, err))
109
+ return true;
110
+ if (!completed && (err instanceof Refusal || err instanceof WriteConflict))
111
+ throw err;
112
+ /* Pointing an existing row at a row that is not there is the same
113
+ mistake as inserting one, and gets the same answer. */
114
+ const failure = await failureOf(err, completed, values);
115
+ sendResource(res, path, refusedStatus(err, completed), {
116
+ errors: [failure],
117
+ outcome: completed ? 'unknown' : writeFailureOutcome(err),
118
+ });
119
+ return true;
120
+ }
121
+ }
122
+ /**
123
+ * Add a row. The same gate as /api/update, because it is the same act one
124
+ * tense earlier. Refusals — a column that does not exist, null into NOT
125
+ * NULL — are 400s the adapter words; a constraint the *database* enforces
126
+ * comes back with its own message in the `rejected` envelope, because a
127
+ * UNIQUE violation is the user's to fix and the engine names the constraint
128
+ * better than a paraphrase would — at 409 or 422 (refusedStatus), never a
129
+ * 2xx a client reading only the status would take for a save. The outcome
130
+ * distinguishes recognized constraint rejections from unconfirmed failures
131
+ * that must not invite a blind retry.
132
+ */
133
+ if (path === '/api/insert' || path === FILE_UPLOAD_PATH) {
134
+ const tableId = findTable(schema, String(body.table ?? ''))?.id;
135
+ const refused = hooks.gates.write(post, tableId);
136
+ if (refused) {
137
+ send(res, 403, { error: refused, code: 'forbidden' });
138
+ return true;
139
+ }
140
+ if (!adapter.insert) {
141
+ send(res, 501, { error: `${schema.dialect} connections are read-only in this build.` });
142
+ return true;
143
+ }
144
+ let values;
145
+ let placed;
146
+ if (path === FILE_UPLOAD_PATH) {
147
+ /* A file is a row like any other: one insert on this path, with the
148
+ grants, validation and history an insert gets, and the upload's bytes,
149
+ name, type and size in the columns the resource's `file` names. */
150
+ const upload = rules ? await rules.upload(post, tableId) : { status: 404, error: 'This resource does not hold files.' };
151
+ if ('error' in upload) {
152
+ send(res, upload.status, { error: upload.error });
153
+ return true;
154
+ }
155
+ placed = upload;
156
+ values = placed.values;
157
+ }
158
+ else {
159
+ if (!body.values || typeof body.values !== 'object' || Array.isArray(body.values)) {
160
+ send(res, 400, { error: 'Insert values must be an object. Use {} explicitly to insert database defaults.' });
161
+ return true;
162
+ }
163
+ values = body.values;
164
+ }
165
+ /* A record's tags and its lines, sent with it from a New form: each written
166
+ in the record's transaction, so the record never stands without them. */
167
+ let alongside = {};
168
+ if (path === '/api/insert' && (body.tags !== undefined || body.lines !== undefined)) {
169
+ const judged = rules
170
+ ? rules.alongside(post, tableId)
171
+ : body.tags !== undefined
172
+ ? { status: 400, error: 'This resource carries no tags.' }
173
+ : { status: 400, error: 'Lines are saved with a record of an App that declares them.' };
174
+ if ('error' in judged) {
175
+ send(res, judged.status, { error: judged.error });
176
+ return true;
177
+ }
178
+ alongside = judged;
179
+ }
180
+ let completed = false;
181
+ try {
182
+ const table = String(body.table ?? '');
183
+ const { result, undoId } = await recordedInsert(writeOptions(table), { table, values }, alongside.alongside, alongside.lines);
184
+ completed = true;
185
+ indexed(table, result.key);
186
+ linesIndexed(alongside.lines);
187
+ sendData({ ...result, ...(undoId === undefined ? {} : { undoId }) });
188
+ return true;
189
+ }
190
+ catch (err) {
191
+ // A row that was not written leaves no object behind; one that may have been keeps it for the sweep to judge.
192
+ if (placed && !completed && !(err instanceof TransactionOutcomeUnknown))
193
+ await placed.discard();
194
+ if (!completed && lockTimedOut(err)) {
195
+ sendRowLocked(res);
196
+ return true;
197
+ }
198
+ if (!completed && isPoolBusy(err)) {
199
+ sendBusy(res, {}, true, err);
200
+ return true;
201
+ }
202
+ if (!completed && rules?.refused(res, '/api/insert', err))
203
+ return true;
204
+ if (!completed && err instanceof Refusal)
205
+ throw err;
206
+ /* "FOREIGN KEY constraint failed" names nothing on SQLite. The graph
207
+ is right here, and one lookup on the failure path turns it into the
208
+ column, the value and the row that is missing. */
209
+ const failure = await failureOf(err, completed, values);
210
+ sendResource(res, '/api/insert', refusedStatus(err, completed), {
211
+ errors: [failure],
212
+ outcome: completed ? 'unknown' : writeFailureOutcome(err),
213
+ });
214
+ return true;
215
+ }
216
+ }
217
+ /**
218
+ * Remove a row, having first said what removing it would do.
219
+ *
220
+ * This is the endpoint tablewalk can do better than most clients, and the
221
+ * reason is structural rather than clever: it already counts what points at a
222
+ * row, so it can answer "what breaks" *before* the delete rather than
223
+ * reporting a constraint violation afterwards.
224
+ *
225
+ * The count alone is not the answer. Three rows pointing at this one means
226
+ * three rows deleted under `cascade`, three rows left with a hole under `set
227
+ * null`, and a refused delete under `restrict` — three different outcomes
228
+ * behind one number. So the impact report carries the rule beside the count,
229
+ * and a rule the adapter could not determine is reported as unknown rather
230
+ * than assumed harmless.
231
+ *
232
+ * Two calls, deliberately. Without `confirm` this only reports; with it, it
233
+ * deletes. A destructive endpoint that also happens to be the way you ask
234
+ * about the damage is one typo away from doing it.
235
+ */
236
+ if (path === '/api/delete') {
237
+ if (!adapter.remove) {
238
+ send(res, 501, { error: `${schema.dialect} connections cannot delete in this build.` });
239
+ return true;
240
+ }
241
+ const refused = hooks.gates.write(post, findTable(schema, String(body.table ?? ''))?.id);
242
+ if (refused) {
243
+ send(res, 403, { error: refused, code: 'forbidden' });
244
+ return true;
245
+ }
246
+ if (rules && !rules.deletes(post, findTable(schema, String(body.table ?? ''))?.id ?? '')) {
247
+ send(res, 403, { error: 'This app does not delete these records.' });
248
+ return true;
249
+ }
250
+ const tableId = String(body.table ?? '');
251
+ const key = (body.key ?? {});
252
+ const table = findTable(schema, tableId);
253
+ if (!table) {
254
+ send(res, 404, { error: `Unknown table "${tableId}"` });
255
+ return true;
256
+ }
257
+ if (wholeRowWrites && !wholeRowWrites.has(tableId))
258
+ throw new AppAccessDenied();
259
+ const impact = await deleteImpact(adapter, schema, tableId, key, rules ? (error) => rules.impact(error) : undefined);
260
+ if (body.confirm !== true) {
261
+ sendResource(res, path, 200, { impact, deleted: false });
262
+ return true;
263
+ }
264
+ /* `restrict` and `no action` are left to the database rather than refused
265
+ here. The engine enforces them and its message names the constraint;
266
+ a second implementation of the same rule is how the two come to
267
+ disagree, and tablewalk would be the one that was wrong. */
268
+ let completed = false;
269
+ try {
270
+ const options = writeOptions(tableId);
271
+ /* What the deletion may change, for the journal's Undo, is read off
272
+ the database's own catalog. An App journals nothing. */
273
+ const dependents = options.journal
274
+ ? deletionDependents((await registry.open(options.connection)).schema, tableId, (table) => table)
275
+ : undefined;
276
+ const { result, undoId } = await recordedDelete(options, { table: tableId, key }, dependents);
277
+ completed = true;
278
+ indexed(tableId, key);
279
+ sendData({ impact, deleted: true, ...result, ...(undoId === undefined ? {} : { undoId }) });
280
+ return true;
281
+ }
282
+ catch (err) {
283
+ if (!completed && lockTimedOut(err)) {
284
+ sendRowLocked(res, { impact, deleted: false });
285
+ return true;
286
+ }
287
+ if (!completed && isPoolBusy(err)) {
288
+ sendBusy(res, { impact, deleted: false }, true, err);
289
+ return true;
290
+ }
291
+ const outcome = completed ? 'unknown' : err instanceof Refusal ? 'rejected' : writeFailureOutcome(err);
292
+ const errors = [rules ? rules.failure(post, err, completed) : { message: err.message }];
293
+ sendResource(res, path, outcome === 'unknown' ? 200 : (writeRefusalStatus(err) ?? 400), outcome === 'unknown' ? { impact, deleted: null, outcome, errors } : { impact, deleted: false, outcome, errors });
294
+ return true;
295
+ }
296
+ }
297
+ return false;
298
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Every refusal names its cause — to the people allowed to hear it.
3
+ *
4
+ * A caller is told one fixed sentence, and must be: a driver's error names
5
+ * columns and values, a clash on a hidden unique column says that a row
6
+ * exists, and a signed-out visitor is owed nothing about how an App is built.
7
+ * But a bare 503 with nothing logged anywhere cost Keystone's authors hours
8
+ * each time — a scoped command whose parent was outside its footprint, an
9
+ * audit entry past its bound, `me` in a rollup. So the reason is recorded on
10
+ * the refusal where it is decided (`recordRefusalCause`, shared/refusal.ts),
11
+ * and said here, at the one place a request's failure becomes its answer:
12
+ *
13
+ * - on the operator's stderr, always, for a server failure (5xx) or a refusal
14
+ * that recorded why — the terminal an author runs the server in;
15
+ * - in the answer itself, as `cause`, only when the reader is the App's
16
+ * author at work (`noteAuthorRequest`): signed in as its administrator (the
17
+ * `admin` role, the account that may already act as anyone), over a
18
+ * loopback listener nothing else can reach, at an App the server is
19
+ * watching in its source directory — the edit loop, where a save reloads.
20
+ * Never a signed-out visitor or an ordinary account, never a build, never a
21
+ * listener the network reaches or a proxy speaks for.
22
+ */
23
+ import type { IncomingMessage } from 'node:http';
24
+ import { type Bound } from './trust.js';
25
+ /** This App is served from its source and reloads on save. */
26
+ export declare function authoringApp(path: string): void;
27
+ /** Mark this request as the author's own, when it is: see the module comment. */
28
+ export declare function noteAuthorRequest(req: IncomingMessage, bound: Bound, administrator: boolean, apps: ReadonlyArray<{
29
+ path: string;
30
+ build?: unknown;
31
+ }>): void;
32
+ /**
33
+ * Say a refused request to the operator, and return the sentence its author
34
+ * may read, if this request is the author's. Quiet for an ordinary refusal
35
+ * that recorded nothing — a 404 for a typo is not the operator's business —
36
+ * unless `always`: an error that was not a refusal at all, answered as one.
37
+ */
38
+ export declare function reportRefusal(req: IncomingMessage, status: number, code: string | undefined, heard: string | undefined, error: unknown, always?: boolean): string | undefined;
@@ -0,0 +1,63 @@
1
+ import { refusalReasons, refusalSaid } from '../../shared/refusal.js';
2
+ import { clientTrusted } from './trust.js';
3
+ import { AppAccessDenied } from '../columnGrants.js';
4
+ import { TenantContextError } from '../evidence.js';
5
+ import { currentSpan, log } from '../../adapters/telemetry/trace.js';
6
+ import { product } from '../product.js';
7
+ /* A denial's own sentence says nothing its caller was not told: an ordinary
8
+ refusal of an ordinary request, not a reason. */
9
+ const OPAQUE = new Set([new AppAccessDenied().message, new TenantContextError().message]);
10
+ const authors = new WeakSet();
11
+ /* The Apps this process is authoring: served from source and watched, so a
12
+ save reloads them (cli/serve.ts). By path, since a reload replaces the
13
+ loaded App and a request works on a copy of it. */
14
+ const authoring = new Set();
15
+ /** This App is served from its source and reloads on save. */
16
+ export function authoringApp(path) {
17
+ authoring.add(path);
18
+ }
19
+ /** Mark this request as the author's own, when it is: see the module comment. */
20
+ export function noteAuthorRequest(req, bound, administrator, apps) {
21
+ if (administrator &&
22
+ apps.length &&
23
+ apps.every((app) => !app.build && authoring.has(app.path)) &&
24
+ clientTrusted(bound) &&
25
+ !bound.identityHeader &&
26
+ !bound.trustedProxies?.length)
27
+ authors.add(req);
28
+ }
29
+ /** What the operator is told: the error's own words when it is no refusal at
30
+ all (a driver's, a bug), then each reason recorded beneath it. */
31
+ function reasonsFor(error, heard) {
32
+ const own = error instanceof Error && error.message && error.message !== heard ? [error.message] : [];
33
+ return [...new Set([...own, ...recorded(error)])].filter((reason) => reason !== heard && !OPAQUE.has(reason));
34
+ }
35
+ const recorded = (error) => refusalReasons(error).filter((reason) => !OPAQUE.has(reason));
36
+ /**
37
+ * Say a refused request to the operator, and return the sentence its author
38
+ * may read, if this request is the author's. Quiet for an ordinary refusal
39
+ * that recorded nothing — a 404 for a typo is not the operator's business —
40
+ * unless `always`: an error that was not a refusal at all, answered as one.
41
+ */
42
+ export function reportRefusal(req, status, code, heard, error, always = false) {
43
+ const reasons = reasonsFor(error, heard);
44
+ /* The same reasons, on the request's span: the code and status travel, the
45
+ reasons stay local for the dev observer, where "Open in observer" leads. */
46
+ const span = currentSpan();
47
+ span?.setAttributes({ 'mistri.refusal.code': code ?? String(status), 'mistri.refusal.status': status });
48
+ span?.detail(() => ({ refusal: { status, ...(code ? { code } : {}), ...(heard ? { said: heard } : {}) }, because: reasons }));
49
+ log(status >= 500 ? 'error' : 'warn', 'refusal', `Request refused (${status}${code ? ` ${code}` : ''})`, {
50
+ attributes: { 'mistri.refusal.code': code, 'http.response.status_code': status },
51
+ detail: () => ({ said: heard, because: reasons }),
52
+ });
53
+ /* A refusal that said its rule (sayRefusal) is the operator's business too: a role or a policy that refuses
54
+ what its author meant it to allow is found here, not first with diagnostics on (field report TR7). */
55
+ if (status < 500 && !recorded(error).length && !refusalSaid(error) && !always)
56
+ return undefined;
57
+ const path = (req.url ?? '').split('?')[0];
58
+ console.error(`${product()}: ${req.method ?? 'GET'} ${path} refused (${status}${code ? ` ${code}` : ''})${heard ? `: ${heard}` : ''}`);
59
+ for (const reason of reasons)
60
+ console.error(` because: ${reason}`);
61
+ // The innermost reason is the one that names the rule, the table or the column.
62
+ return authors.has(req) ? reasons.at(-1) : undefined;
63
+ }