@segmentstream/ontology 0.0.0-stage → 0.92.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 (483) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +2 -0
  3. package/README.md +1655 -2
  4. package/dist/action.d.ts +193 -0
  5. package/dist/action.js +41 -0
  6. package/dist/agent-chat.d.ts +44 -0
  7. package/dist/agent-chat.js +91 -0
  8. package/dist/agent-task-contract.d.ts +64 -0
  9. package/dist/agent-task-contract.js +15 -0
  10. package/dist/agent-task.d.ts +39 -0
  11. package/dist/agent-task.js +112 -0
  12. package/dist/analyze.d.ts +215 -0
  13. package/dist/analyze.js +2438 -0
  14. package/dist/app-feedback.d.ts +167 -0
  15. package/dist/app-feedback.js +176 -0
  16. package/dist/authoring-input.d.ts +3 -0
  17. package/dist/authoring-input.js +107 -0
  18. package/dist/authoring-names.d.ts +4 -0
  19. package/dist/authoring-names.js +13 -0
  20. package/dist/authoring-records.d.ts +35 -0
  21. package/dist/authoring-records.js +264 -0
  22. package/dist/authoring.d.ts +15 -0
  23. package/dist/authoring.js +331 -0
  24. package/dist/bin/check.d.ts +30 -0
  25. package/dist/bin/check.js +99 -0
  26. package/dist/bin/load.d.ts +48 -0
  27. package/dist/bin/load.js +139 -0
  28. package/dist/bin/main.d.ts +31 -0
  29. package/dist/bin/main.js +121 -0
  30. package/dist/bin/migrate.d.ts +57 -0
  31. package/dist/bin/migrate.js +170 -0
  32. package/dist/bin/output.d.ts +31 -0
  33. package/dist/bin/output.js +84 -0
  34. package/dist/bin.d.ts +2 -0
  35. package/dist/bin.js +31 -0
  36. package/dist/blob.d.ts +52 -0
  37. package/dist/blob.js +87 -0
  38. package/dist/channel-session.d.ts +16 -0
  39. package/dist/channel-session.js +16 -0
  40. package/dist/chat.d.ts +43 -0
  41. package/dist/chat.js +34 -0
  42. package/dist/confirmation.d.ts +46 -0
  43. package/dist/confirmation.js +41 -0
  44. package/dist/connection.d.ts +213 -0
  45. package/dist/connection.js +289 -0
  46. package/dist/deployment.d.ts +35 -0
  47. package/dist/deployment.js +76 -0
  48. package/dist/diagnostics.d.ts +27 -0
  49. package/dist/diagnostics.js +106 -0
  50. package/dist/emails.d.ts +70 -0
  51. package/dist/emails.js +221 -0
  52. package/dist/entity.d.ts +331 -0
  53. package/dist/entity.js +153 -0
  54. package/dist/errors.d.ts +154 -0
  55. package/dist/errors.js +109 -0
  56. package/dist/feedback-config.d.ts +24 -0
  57. package/dist/feedback-config.js +126 -0
  58. package/dist/files.d.ts +39 -0
  59. package/dist/files.js +4 -0
  60. package/dist/groups.d.ts +95 -0
  61. package/dist/groups.js +280 -0
  62. package/dist/index.d.ts +109 -0
  63. package/dist/index.js +70 -0
  64. package/dist/internal/diagnostics.js +594 -0
  65. package/dist/legacy.d.ts +160 -0
  66. package/dist/legacy.js +270 -0
  67. package/dist/manifest-compatibility.d.ts +8 -0
  68. package/dist/manifest-compatibility.js +87 -0
  69. package/dist/manifest.d.ts +3329 -0
  70. package/dist/manifest.js +757 -0
  71. package/dist/me.d.ts +46 -0
  72. package/dist/me.js +109 -0
  73. package/dist/metric-filters.d.ts +48 -0
  74. package/dist/metric-filters.js +252 -0
  75. package/dist/metric-routes.d.ts +132 -0
  76. package/dist/metric-routes.js +498 -0
  77. package/dist/metric.d.ts +172 -0
  78. package/dist/metric.js +132 -0
  79. package/dist/migrate/apply.d.ts +94 -0
  80. package/dist/migrate/apply.js +526 -0
  81. package/dist/migrate/errors.d.ts +46 -0
  82. package/dist/migrate/errors.js +38 -0
  83. package/dist/migrate/index.d.ts +10 -0
  84. package/dist/migrate/index.js +7 -0
  85. package/dist/migrate/model.d.ts +2311 -0
  86. package/dist/migrate/model.js +148 -0
  87. package/dist/migrate/plan.d.ts +51 -0
  88. package/dist/migrate/plan.js +1856 -0
  89. package/dist/migrate/preview.d.ts +13 -0
  90. package/dist/migrate/preview.js +65 -0
  91. package/dist/migrate/storage.d.ts +226 -0
  92. package/dist/migrate/storage.js +774 -0
  93. package/dist/model-metrics.d.ts +22 -0
  94. package/dist/model-metrics.js +545 -0
  95. package/dist/names.d.ts +49 -0
  96. package/dist/names.js +110 -0
  97. package/dist/navigation-artifact.d.ts +13 -0
  98. package/dist/navigation-artifact.js +11 -0
  99. package/dist/navigation-catalog.d.ts +55 -0
  100. package/dist/navigation-catalog.js +297 -0
  101. package/dist/nitro-navigation.d.ts +3 -0
  102. package/dist/nitro-navigation.js +64 -0
  103. package/dist/nitro-workflows.d.ts +108 -0
  104. package/dist/nitro-workflows.js +286 -0
  105. package/dist/nitro.d.ts +40 -0
  106. package/dist/nitro.js +50 -0
  107. package/dist/ontology.d.ts +41 -0
  108. package/dist/ontology.js +46 -0
  109. package/dist/permissions.d.ts +198 -0
  110. package/dist/permissions.js +272 -0
  111. package/dist/property.d.ts +188 -0
  112. package/dist/property.js +301 -0
  113. package/dist/query.d.ts +245 -0
  114. package/dist/query.js +615 -0
  115. package/dist/relationship.d.ts +99 -0
  116. package/dist/relationship.js +33 -0
  117. package/dist/render.d.ts +81 -0
  118. package/dist/render.js +512 -0
  119. package/dist/replication/baseline.d.ts +51 -0
  120. package/dist/replication/baseline.js +215 -0
  121. package/dist/replication/bigquery.d.ts +37 -0
  122. package/dist/replication/bigquery.js +211 -0
  123. package/dist/replication/coverage.d.ts +14 -0
  124. package/dist/replication/coverage.js +53 -0
  125. package/dist/replication/evolution.d.ts +25 -0
  126. package/dist/replication/evolution.js +184 -0
  127. package/dist/replication/identities.d.ts +6 -0
  128. package/dist/replication/identities.js +47 -0
  129. package/dist/replication/legacy-baseline.d.ts +27 -0
  130. package/dist/replication/legacy-baseline.js +109 -0
  131. package/dist/replication/legacy-store.d.ts +19 -0
  132. package/dist/replication/legacy-store.js +63 -0
  133. package/dist/replication/lineage.d.ts +36 -0
  134. package/dist/replication/lineage.js +72 -0
  135. package/dist/replication/migrate.d.ts +21 -0
  136. package/dist/replication/migrate.js +84 -0
  137. package/dist/replication/model.d.ts +52 -0
  138. package/dist/replication/model.js +99 -0
  139. package/dist/replication/scans.d.ts +34 -0
  140. package/dist/replication/scans.js +158 -0
  141. package/dist/replication/schema.d.ts +11 -0
  142. package/dist/replication/schema.js +226 -0
  143. package/dist/replication/shadow-schema.d.ts +4 -0
  144. package/dist/replication/shadow-schema.js +61 -0
  145. package/dist/replication/shadow.d.ts +127 -0
  146. package/dist/replication/shadow.js +614 -0
  147. package/dist/replication/source-replacement.d.ts +33 -0
  148. package/dist/replication/source-replacement.js +76 -0
  149. package/dist/replication/store.d.ts +36 -0
  150. package/dist/replication/store.js +382 -0
  151. package/dist/replication/transaction.d.ts +23 -0
  152. package/dist/replication/transaction.js +88 -0
  153. package/dist/role.d.ts +61 -0
  154. package/dist/role.js +76 -0
  155. package/dist/runtime/action-deadlocks.d.ts +21 -0
  156. package/dist/runtime/action-deadlocks.js +130 -0
  157. package/dist/runtime/action-transactions.d.ts +35 -0
  158. package/dist/runtime/action-transactions.js +54 -0
  159. package/dist/runtime/agent-counters.d.ts +26 -0
  160. package/dist/runtime/agent-counters.js +98 -0
  161. package/dist/runtime/agent-tasks.d.ts +63 -0
  162. package/dist/runtime/agent-tasks.js +120 -0
  163. package/dist/runtime/agents.d.ts +15 -0
  164. package/dist/runtime/agents.js +457 -0
  165. package/dist/runtime/batch-diagnostics.d.ts +22 -0
  166. package/dist/runtime/batch-diagnostics.js +89 -0
  167. package/dist/runtime/batch.d.ts +52 -0
  168. package/dist/runtime/batch.js +534 -0
  169. package/dist/runtime/channel-session-revocation.d.ts +3 -0
  170. package/dist/runtime/channel-session-revocation.js +5 -0
  171. package/dist/runtime/channel-sessions.d.ts +4 -0
  172. package/dist/runtime/channel-sessions.js +61 -0
  173. package/dist/runtime/confirmations.d.ts +29 -0
  174. package/dist/runtime/confirmations.js +57 -0
  175. package/dist/runtime/cursor.d.ts +23 -0
  176. package/dist/runtime/cursor.js +100 -0
  177. package/dist/runtime/deployment.d.ts +7 -0
  178. package/dist/runtime/deployment.js +4 -0
  179. package/dist/runtime/errors.d.ts +118 -0
  180. package/dist/runtime/errors.js +109 -0
  181. package/dist/runtime/federation-tables.d.ts +2 -0
  182. package/dist/runtime/federation-tables.js +20 -0
  183. package/dist/runtime/federation.d.ts +42 -0
  184. package/dist/runtime/federation.js +222 -0
  185. package/dist/runtime/files.d.ts +39 -0
  186. package/dist/runtime/files.js +44 -0
  187. package/dist/runtime/generated-actions.d.ts +37 -0
  188. package/dist/runtime/generated-actions.js +307 -0
  189. package/dist/runtime/history.d.ts +91 -0
  190. package/dist/runtime/history.js +77 -0
  191. package/dist/runtime/ids.d.ts +6 -0
  192. package/dist/runtime/ids.js +39 -0
  193. package/dist/runtime/member-tables.d.ts +8 -0
  194. package/dist/runtime/member-tables.js +33 -0
  195. package/dist/runtime/member-writes.d.ts +78 -0
  196. package/dist/runtime/member-writes.js +202 -0
  197. package/dist/runtime/metric-query.d.ts +19 -0
  198. package/dist/runtime/metric-query.js +411 -0
  199. package/dist/runtime/model.d.ts +67 -0
  200. package/dist/runtime/model.js +150 -0
  201. package/dist/runtime/new-user-hook.d.ts +6 -0
  202. package/dist/runtime/new-user-hook.js +37 -0
  203. package/dist/runtime/oauth.d.ts +9 -0
  204. package/dist/runtime/oauth.js +299 -0
  205. package/dist/runtime/pipeline.d.ts +155 -0
  206. package/dist/runtime/pipeline.js +597 -0
  207. package/dist/runtime/postgres-tls.d.ts +10 -0
  208. package/dist/runtime/postgres-tls.js +14 -0
  209. package/dist/runtime/principals.d.ts +302 -0
  210. package/dist/runtime/principals.js +1307 -0
  211. package/dist/runtime/reads.d.ts +55 -0
  212. package/dist/runtime/reads.js +313 -0
  213. package/dist/runtime/records.d.ts +42 -0
  214. package/dist/runtime/records.js +245 -0
  215. package/dist/runtime/resolve-agent-identity.d.ts +6 -0
  216. package/dist/runtime/resolve-agent-identity.js +181 -0
  217. package/dist/runtime/service-binding.d.ts +18 -0
  218. package/dist/runtime/service-binding.js +266 -0
  219. package/dist/runtime/sign-in-links.d.ts +68 -0
  220. package/dist/runtime/sign-in-links.js +147 -0
  221. package/dist/runtime/sql-runtime.d.ts +131 -0
  222. package/dist/runtime/sql-runtime.js +362 -0
  223. package/dist/runtime/statement.d.ts +18 -0
  224. package/dist/runtime/statement.js +31 -0
  225. package/dist/runtime/user-identities.d.ts +66 -0
  226. package/dist/runtime/user-identities.js +106 -0
  227. package/dist/runtime/user-identity-sql.d.ts +86 -0
  228. package/dist/runtime/user-identity-sql.js +297 -0
  229. package/dist/runtime/user-roles.d.ts +16 -0
  230. package/dist/runtime/user-roles.js +32 -0
  231. package/dist/runtime/validation.d.ts +16 -0
  232. package/dist/runtime/validation.js +26 -0
  233. package/dist/runtime/where.d.ts +57 -0
  234. package/dist/runtime/where.js +404 -0
  235. package/dist/runtime/workflow-admission.d.ts +49 -0
  236. package/dist/runtime/workflow-admission.js +92 -0
  237. package/dist/runtime/workflow-argument-codec.d.ts +10 -0
  238. package/dist/runtime/workflow-argument-codec.js +120 -0
  239. package/dist/runtime/workflow-arguments.d.ts +17 -0
  240. package/dist/runtime/workflow-arguments.js +99 -0
  241. package/dist/runtime/workflow-starts.d.ts +203 -0
  242. package/dist/runtime/workflow-starts.js +493 -0
  243. package/dist/runtime/workflow-users.d.ts +9 -0
  244. package/dist/runtime/workflow-users.js +17 -0
  245. package/dist/runtime/workload.d.ts +25 -0
  246. package/dist/runtime/workload.js +24 -0
  247. package/dist/runtime/writes.d.ts +108 -0
  248. package/dist/runtime/writes.js +491 -0
  249. package/dist/runtime-source.d.ts +22 -0
  250. package/dist/runtime-source.js +28 -0
  251. package/dist/runtime.d.ts +310 -0
  252. package/dist/runtime.js +31 -0
  253. package/dist/server.d.ts +23 -0
  254. package/dist/server.js +107 -0
  255. package/dist/service/agent/attachments.d.ts +33 -0
  256. package/dist/service/agent/attachments.js +228 -0
  257. package/dist/service/agent/authentication.d.ts +22 -0
  258. package/dist/service/agent/authentication.js +58 -0
  259. package/dist/service/agent/confirmations.d.ts +5 -0
  260. package/dist/service/agent/confirmations.js +73 -0
  261. package/dist/service/agent/conversation-routes.d.ts +14 -0
  262. package/dist/service/agent/conversation-routes.js +45 -0
  263. package/dist/service/agent/conversations.d.ts +40 -0
  264. package/dist/service/agent/conversations.js +69 -0
  265. package/dist/service/agent/files.d.ts +12 -0
  266. package/dist/service/agent/files.js +69 -0
  267. package/dist/service/agent/protocol.d.ts +90 -0
  268. package/dist/service/agent/protocol.js +114 -0
  269. package/dist/service/agent/server.d.ts +28 -0
  270. package/dist/service/agent/server.js +374 -0
  271. package/dist/service/agent/task-caller.d.ts +21 -0
  272. package/dist/service/agent/task-caller.js +35 -0
  273. package/dist/service/agent/task-start.d.ts +53 -0
  274. package/dist/service/agent/task-start.js +161 -0
  275. package/dist/service/agent/tasks.d.ts +40 -0
  276. package/dist/service/agent/tasks.js +155 -0
  277. package/dist/service/api.d.ts +50 -0
  278. package/dist/service/api.js +288 -0
  279. package/dist/service/authentication.d.ts +227 -0
  280. package/dist/service/authentication.js +767 -0
  281. package/dist/service/caller-context.d.ts +24 -0
  282. package/dist/service/caller-context.js +6 -0
  283. package/dist/service/catalog.d.ts +42 -0
  284. package/dist/service/catalog.js +66 -0
  285. package/dist/service/channel/provider.d.ts +10 -0
  286. package/dist/service/channel/provider.js +1 -0
  287. package/dist/service/channel/providers/telegram.d.ts +18 -0
  288. package/dist/service/channel/providers/telegram.js +108 -0
  289. package/dist/service/channel/sessions.d.ts +5 -0
  290. package/dist/service/channel/sessions.js +53 -0
  291. package/dist/service/channel/sign-in.d.ts +7 -0
  292. package/dist/service/channel/sign-in.js +78 -0
  293. package/dist/service/chat/archive.d.ts +64 -0
  294. package/dist/service/chat/archive.js +62 -0
  295. package/dist/service/chat/attribution.d.ts +26 -0
  296. package/dist/service/chat/attribution.js +28 -0
  297. package/dist/service/chat/delivery.d.ts +18 -0
  298. package/dist/service/chat/delivery.js +251 -0
  299. package/dist/service/chat/diagnostics.d.ts +2 -0
  300. package/dist/service/chat/diagnostics.js +35 -0
  301. package/dist/service/chat/envelope.d.ts +96 -0
  302. package/dist/service/chat/envelope.js +39 -0
  303. package/dist/service/chat/idempotency.d.ts +19 -0
  304. package/dist/service/chat/idempotency.js +37 -0
  305. package/dist/service/chat/providers/telegram.d.ts +36 -0
  306. package/dist/service/chat/providers/telegram.js +133 -0
  307. package/dist/service/chat/reads.d.ts +25 -0
  308. package/dist/service/chat/reads.js +75 -0
  309. package/dist/service/chat/resolve.d.ts +34 -0
  310. package/dist/service/chat/resolve.js +150 -0
  311. package/dist/service/chat/routes.d.ts +23 -0
  312. package/dist/service/chat/routes.js +75 -0
  313. package/dist/service/chat/schemas.d.ts +43 -0
  314. package/dist/service/chat/schemas.js +31 -0
  315. package/dist/service/config.d.ts +218 -0
  316. package/dist/service/config.js +265 -0
  317. package/dist/service/connection-stream.d.ts +10 -0
  318. package/dist/service/connection-stream.js +191 -0
  319. package/dist/service/connections.d.ts +60 -0
  320. package/dist/service/connections.js +271 -0
  321. package/dist/service/decisions.d.ts +35 -0
  322. package/dist/service/decisions.js +63 -0
  323. package/dist/service/descriptions.d.ts +8 -0
  324. package/dist/service/descriptions.js +20 -0
  325. package/dist/service/discovery-schemas.d.ts +92 -0
  326. package/dist/service/discovery-schemas.js +59 -0
  327. package/dist/service/discovery-search.d.ts +30 -0
  328. package/dist/service/discovery-search.js +95 -0
  329. package/dist/service/discovery.d.ts +37 -0
  330. package/dist/service/discovery.js +436 -0
  331. package/dist/service/docs.d.ts +9 -0
  332. package/dist/service/docs.js +197 -0
  333. package/dist/service/errors.d.ts +145 -0
  334. package/dist/service/errors.js +704 -0
  335. package/dist/service/federation-provider.d.ts +36 -0
  336. package/dist/service/federation-provider.js +110 -0
  337. package/dist/service/federation-server.d.ts +20 -0
  338. package/dist/service/federation-server.js +249 -0
  339. package/dist/service/federation-status.d.ts +40 -0
  340. package/dist/service/federation-status.js +151 -0
  341. package/dist/service/handlers.d.ts +18 -0
  342. package/dist/service/handlers.js +287 -0
  343. package/dist/service/mcp/confirmation.d.ts +68 -0
  344. package/dist/service/mcp/confirmation.js +178 -0
  345. package/dist/service/mcp/find-input.d.ts +12 -0
  346. package/dist/service/mcp/find-input.js +122 -0
  347. package/dist/service/mcp/input-schema.d.ts +6 -0
  348. package/dist/service/mcp/input-schema.js +126 -0
  349. package/dist/service/mcp/listing.d.ts +26 -0
  350. package/dist/service/mcp/listing.js +92 -0
  351. package/dist/service/mcp/metric-tools.d.ts +85 -0
  352. package/dist/service/mcp/metric-tools.js +258 -0
  353. package/dist/service/mcp/request-log.d.ts +5 -0
  354. package/dist/service/mcp/request-log.js +53 -0
  355. package/dist/service/mcp/runtime-policy.d.ts +27 -0
  356. package/dist/service/mcp/runtime-policy.js +62 -0
  357. package/dist/service/mcp/server.d.ts +52 -0
  358. package/dist/service/mcp/server.js +861 -0
  359. package/dist/service/mcp/tools.d.ts +99 -0
  360. package/dist/service/mcp/tools.js +938 -0
  361. package/dist/service/member-file-routes.d.ts +22 -0
  362. package/dist/service/member-file-routes.js +52 -0
  363. package/dist/service/member-files.d.ts +245 -0
  364. package/dist/service/member-files.js +258 -0
  365. package/dist/service/metric-query-platform.d.ts +77 -0
  366. package/dist/service/metric-query-platform.js +198 -0
  367. package/dist/service/metric-query-scopes.d.ts +43 -0
  368. package/dist/service/metric-query-scopes.js +147 -0
  369. package/dist/service/metric-query.d.ts +48 -0
  370. package/dist/service/metric-query.js +200 -0
  371. package/dist/service/name-suggestions.d.ts +3 -0
  372. package/dist/service/name-suggestions.js +38 -0
  373. package/dist/service/navigation-schemas.d.ts +80 -0
  374. package/dist/service/navigation-schemas.js +49 -0
  375. package/dist/service/navigation.d.ts +20 -0
  376. package/dist/service/navigation.js +165 -0
  377. package/dist/service/oauth/protocol.d.ts +70 -0
  378. package/dist/service/oauth/protocol.js +157 -0
  379. package/dist/service/oauth/refresh-replay.d.ts +2 -0
  380. package/dist/service/oauth/refresh-replay.js +20 -0
  381. package/dist/service/oauth/server.d.ts +28 -0
  382. package/dist/service/oauth/server.js +769 -0
  383. package/dist/service/operations.d.ts +170 -0
  384. package/dist/service/operations.js +392 -0
  385. package/dist/service/plan.d.ts +57 -0
  386. package/dist/service/plan.js +96 -0
  387. package/dist/service/rate-limit.d.ts +22 -0
  388. package/dist/service/rate-limit.js +52 -0
  389. package/dist/service/record-reads.d.ts +21 -0
  390. package/dist/service/record-reads.js +155 -0
  391. package/dist/service/replication.d.ts +5 -0
  392. package/dist/service/replication.js +39 -0
  393. package/dist/service/runtime-contract.d.ts +508 -0
  394. package/dist/service/runtime-contract.js +18 -0
  395. package/dist/service/runtime-identity.d.ts +71 -0
  396. package/dist/service/runtime-identity.js +122 -0
  397. package/dist/service/schemas.d.ts +961 -0
  398. package/dist/service/schemas.js +639 -0
  399. package/dist/service/service.d.ts +21 -0
  400. package/dist/service/service.js +526 -0
  401. package/dist/service/warehouse-platform.d.ts +17 -0
  402. package/dist/service/warehouse-platform.js +122 -0
  403. package/dist/service/warehouse-query.d.ts +24 -0
  404. package/dist/service/warehouse-query.js +280 -0
  405. package/dist/service/webhooks.d.ts +106 -0
  406. package/dist/service/webhooks.js +233 -0
  407. package/dist/service/workflow-admission-provider.d.ts +31 -0
  408. package/dist/service/workflow-admission-provider.js +205 -0
  409. package/dist/service/workflow-argument-serialization.d.ts +2 -0
  410. package/dist/service/workflow-argument-serialization.js +14 -0
  411. package/dist/service/workflow-callback.d.ts +22 -0
  412. package/dist/service/workflow-callback.js +75 -0
  413. package/dist/service/workflow-deployment.d.ts +8 -0
  414. package/dist/service/workflow-deployment.js +131 -0
  415. package/dist/service/workflow-diagnostics.d.ts +23 -0
  416. package/dist/service/workflow-diagnostics.js +263 -0
  417. package/dist/service/workflow-errors.d.ts +26 -0
  418. package/dist/service/workflow-errors.js +181 -0
  419. package/dist/service/workflow-logging.d.ts +2 -0
  420. package/dist/service/workflow-logging.js +50 -0
  421. package/dist/service/workflow-queue-fence.d.ts +6 -0
  422. package/dist/service/workflow-queue-fence.js +66 -0
  423. package/dist/service/workflows.d.ts +200 -0
  424. package/dist/service/workflows.js +1549 -0
  425. package/dist/sign-in.d.ts +58 -0
  426. package/dist/sign-in.js +57 -0
  427. package/dist/source.d.ts +30 -0
  428. package/dist/source.js +107 -0
  429. package/dist/start-recovery-contract.d.ts +49 -0
  430. package/dist/start-recovery-contract.js +26 -0
  431. package/dist/start-recovery.d.ts +27 -0
  432. package/dist/start-recovery.js +81 -0
  433. package/dist/storage/api.d.ts +4 -0
  434. package/dist/storage/api.js +40 -0
  435. package/dist/storage/attachments.d.ts +146 -0
  436. package/dist/storage/attachments.js +733 -0
  437. package/dist/storage/authorization.d.ts +38 -0
  438. package/dist/storage/authorization.js +83 -0
  439. package/dist/storage/file-access.d.ts +17 -0
  440. package/dist/storage/file-access.js +8 -0
  441. package/dist/storage/limits.d.ts +12 -0
  442. package/dist/storage/limits.js +17 -0
  443. package/dist/storage/maintenance-auth.d.ts +20 -0
  444. package/dist/storage/maintenance-auth.js +24 -0
  445. package/dist/storage/maintenance.d.ts +11 -0
  446. package/dist/storage/maintenance.js +67 -0
  447. package/dist/storage/neon.d.ts +4 -0
  448. package/dist/storage/neon.js +318 -0
  449. package/dist/storage/operator.d.ts +11 -0
  450. package/dist/storage/operator.js +30 -0
  451. package/dist/storage/provider.d.ts +93 -0
  452. package/dist/storage/provider.js +36 -0
  453. package/dist/storage/sessions.d.ts +184 -0
  454. package/dist/storage/sessions.js +688 -0
  455. package/dist/storage/tables.d.ts +54 -0
  456. package/dist/storage/tables.js +205 -0
  457. package/dist/storage/transfers.d.ts +16 -0
  458. package/dist/storage/transfers.js +153 -0
  459. package/dist/storage/writes.d.ts +7 -0
  460. package/dist/storage/writes.js +88 -0
  461. package/dist/telegram.d.ts +35 -0
  462. package/dist/telegram.js +173 -0
  463. package/dist/user-identity.d.ts +28 -0
  464. package/dist/user-identity.js +106 -0
  465. package/dist/user.d.ts +75 -0
  466. package/dist/user.js +111 -0
  467. package/dist/warehouse-backed.d.ts +6 -0
  468. package/dist/warehouse-backed.js +495 -0
  469. package/dist/webhook-token.d.ts +11 -0
  470. package/dist/webhook-token.js +18 -0
  471. package/dist/webhooks.d.ts +40 -0
  472. package/dist/webhooks.js +55 -0
  473. package/dist/wire.d.ts +10 -0
  474. package/dist/wire.js +35 -0
  475. package/dist/workflow-runtime.d.ts +116 -0
  476. package/dist/workflow-runtime.js +109 -0
  477. package/dist/workflow-topics.d.ts +28 -0
  478. package/dist/workflow-topics.js +28 -0
  479. package/dist/workflow.d.ts +284 -0
  480. package/dist/workflow.js +259 -0
  481. package/dist/workflows.d.ts +24 -0
  482. package/dist/workflows.js +18 -0
  483. package/package.json +127 -4
@@ -0,0 +1,688 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { Effect, Schema } from "effect";
3
+ import { isSqlError } from "effect/unstable/sql/SqlError";
4
+ import { acceptsBlobContentType, uploadInput, BLOB_UPLOAD_ANNOTATION, MAX_BLOB_BYTES, } from "../blob.js";
5
+ import { wireStruct } from "../wire.js";
6
+ import { ActionRejected, Conflict, NotFound } from "../errors.js";
7
+ import { Files } from "../files.js";
8
+ import { uploadFile, openFile } from "./transfers.js";
9
+ import { canonicalJson } from "../manifest.js";
10
+ import { effectiveScope } from "../permissions.js";
11
+ import { isRecordId } from "../runtime/records.js";
12
+ import { authorizeBlobOperation } from "./authorization.js";
13
+ import { MAX_PENDING_UPLOADS, storageQuotaRejection } from "./limits.js";
14
+ import { resolveAction } from "../runtime/pipeline.js";
15
+ import { makeOntologyRuntime } from "../runtime/sql-runtime.js";
16
+ import { BlobStorageError } from "./provider.js";
17
+ const Id = Schema.String.check(Schema.isUUID());
18
+ export const BeginUpload = wireStruct({
19
+ entity: Schema.String,
20
+ property: Schema.String,
21
+ action: Schema.String,
22
+ recordId: Schema.optionalKey(Id),
23
+ createIntent: Schema.optionalKey(Id),
24
+ idempotencyKey: Schema.optionalKey(Id),
25
+ filename: Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(255)),
26
+ contentType: Schema.String,
27
+ size: Schema.Int.check(Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(MAX_BLOB_BYTES)),
28
+ }, {
29
+ recordId: "record_id",
30
+ createIntent: "create_intent",
31
+ idempotencyKey: "idempotency_key",
32
+ contentType: "content_type",
33
+ });
34
+ export const UploadView = wireStruct({
35
+ id: Id,
36
+ state: Schema.String,
37
+ expiresAt: Schema.String,
38
+ ticket: Schema.optionalKey(uploadInput()),
39
+ }, { expiresAt: "expires_at" });
40
+ export const OpenUpload = wireStruct({
41
+ id: Id,
42
+ state: Schema.String,
43
+ filename: Schema.String,
44
+ contentType: Schema.String,
45
+ size: Schema.Int,
46
+ entity: Schema.String,
47
+ property: Schema.String,
48
+ action: Schema.String,
49
+ recordId: Schema.NullOr(Id),
50
+ createIntent: Schema.NullOr(Id),
51
+ createdAt: Schema.String,
52
+ expiresAt: Schema.String,
53
+ awaitingAction: Schema.Boolean,
54
+ }, {
55
+ contentType: "content_type",
56
+ recordId: "record_id",
57
+ createIntent: "create_intent",
58
+ createdAt: "created_at",
59
+ expiresAt: "expires_at",
60
+ awaitingAction: "awaiting_action",
61
+ });
62
+ export const OpenUploads = Schema.Struct({
63
+ uploads: Schema.Array(OpenUpload),
64
+ pending: Schema.Int,
65
+ max: Schema.Int,
66
+ });
67
+ export const TransferGrant = wireStruct({
68
+ url: Schema.String,
69
+ method: Schema.Literals(["GET", "PUT"]),
70
+ headers: Schema.Record(Schema.String, Schema.String),
71
+ expiresAt: Schema.String,
72
+ }, { expiresAt: "expires_at" });
73
+ export const DownloadRequest = wireStruct({
74
+ revisionId: Schema.optionalKey(Id),
75
+ inline: Schema.optionalKey(Schema.Boolean),
76
+ }, { revisionId: "revision_id" });
77
+ function allowsUpload(schema, entity, property) {
78
+ if (typeof schema !== "object" || schema === null) {
79
+ return false;
80
+ }
81
+ const object = schema;
82
+ const binding = object[BLOB_UPLOAD_ANNOTATION];
83
+ return ((binding?.entity === entity && binding.property === property) ||
84
+ Object.values(object).some((child) => allowsUpload(child, entity, property)));
85
+ }
86
+ function sniff(bytes, contentType) {
87
+ const starts = (magic) => magic.every((byte, index) => bytes[index] === byte);
88
+ switch (contentType.toLowerCase()) {
89
+ case "application/pdf":
90
+ return starts([37, 80, 68, 70, 45]);
91
+ case "image/png":
92
+ return starts([137, 80, 78, 71, 13, 10, 26, 10]);
93
+ case "image/jpeg":
94
+ return starts([255, 216, 255]);
95
+ case "text/plain":
96
+ try {
97
+ // Streaming decoding allows the prefix to end in the middle of a UTF-8 character.
98
+ new TextDecoder("utf-8", { fatal: true }).decode(bytes, { stream: true });
99
+ return bytes.every((byte) => byte >= 32 || [9, 10, 13].includes(byte));
100
+ }
101
+ catch {
102
+ return false;
103
+ }
104
+ default:
105
+ // Other MIME types describe opaque bytes; storage does not parse their formats.
106
+ return true;
107
+ }
108
+ }
109
+ /** Persisted phases surround every provider call; no external I/O holds a database transaction. */
110
+ export function blobRuntime(ontology, sql, provider, fetchImpl = fetch) {
111
+ const binding = provider.binding;
112
+ const conflict = () => new Conflict({ entity: "upload", message: "The upload is unavailable or busy." });
113
+ // An uncertain storage outcome keeps its claim; the same request recovers once it is stale.
114
+ const busy = (row) => new Conflict({
115
+ entity: "upload",
116
+ message: "Another attempt holds the upload.",
117
+ retryAfter: row.claim_wait ?? 300,
118
+ });
119
+ const view = (row) => ({
120
+ id: row.id,
121
+ state: row.state,
122
+ expiresAt: row.expires_at,
123
+ ...(row.state === "ready" && !row.expired ? { ticket: { uploadId: row.id } } : {}),
124
+ });
125
+ const load = (id, principal) => Effect.gen(function* () {
126
+ const rows = yield* sql.unsafe(`SELECT u.*, b.key,
127
+ to_char(u.expires_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS expires_at,
128
+ u.expires_at <= now() AS expired,
129
+ u.claimed_at < now() - interval '5 minutes' AS claim_stale,
130
+ GREATEST(1, CEIL(EXTRACT(EPOCH FROM u.claimed_at + interval '5 minutes' - now())))::int
131
+ AS claim_wait
132
+ FROM "_blob_uploads" u JOIN "_blobs" b ON b.id = u.blob_id
133
+ WHERE u.id = $1::uuid AND u.principal_id = $2::uuid AND u.branch_id = $3
134
+ FOR UPDATE OF u, b`, [id, principal.id, binding.branchId]);
135
+ const row = rows[0];
136
+ if (!row) {
137
+ return yield* new NotFound({ entity: "upload", id, message: "Upload not found." });
138
+ }
139
+ const [entity, action] = row.action.split(".");
140
+ const found = resolveAction(ontology, entity, action);
141
+ if (!found || row.manifest_hash !== ontology.manifest.hash) {
142
+ return yield* conflict();
143
+ }
144
+ yield* authorizeBlobOperation(ontology, sql, principal.id, found.permissions, principal.execution, principal.workflow);
145
+ return row;
146
+ });
147
+ const beginUpload = (given, principal, preparedAction) => Effect.gen(function* () {
148
+ // UUIDs in either case name one record; uuid columns answer in lower case.
149
+ const input = {
150
+ ...given,
151
+ ...(given.recordId === undefined ? {} : { recordId: given.recordId.toLowerCase() }),
152
+ ...(given.createIntent === undefined
153
+ ? {}
154
+ : { createIntent: given.createIntent.toLowerCase() }),
155
+ };
156
+ const entity = ontology.manifest.entities.find((item) => item.name === input.entity);
157
+ const property = entity?.properties.find((item) => item.name === input.property && item.type === "blob");
158
+ const qualified = preparedAction ?? `${input.entity}.${input.action}`;
159
+ const [actionEntity, actionName] = qualified.split(".");
160
+ const found = resolveAction(ontology, actionEntity, actionName);
161
+ if (!entity ||
162
+ entity.source ||
163
+ !property ||
164
+ !found ||
165
+ !Number.isSafeInteger(input.size) ||
166
+ input.size <= 0 ||
167
+ input.size > MAX_BLOB_BYTES ||
168
+ !input.filename ||
169
+ input.filename.length > 255 ||
170
+ (input.recordId !== undefined && !isRecordId(input.recordId)) ||
171
+ (input.createIntent !== undefined && !isRecordId(input.createIntent)) ||
172
+ !acceptsBlobContentType(property.accept, input.contentType) ||
173
+ input.size > (property.maxBytes ?? MAX_BLOB_BYTES) ||
174
+ Boolean(input.recordId) === Boolean(input.createIntent) ||
175
+ /[\x00-\x1f\x7f/\\]/.test(input.filename)) {
176
+ return yield* new ActionRejected({ message: "The file or upload target is invalid." });
177
+ }
178
+ const schema = Schema.toJsonSchemaDocument(found.action.input, {
179
+ includeAnnotationKey: (key) => key === BLOB_UPLOAD_ANNOTATION,
180
+ });
181
+ const declared = preparedAction !== undefined &&
182
+ found.action.files?.some((target) => target.entity === input.entity && target.property === input.property);
183
+ if (!declared && !allowsUpload(schema, input.entity, input.property)) {
184
+ return yield* new ActionRejected({
185
+ message: "This action does not accept an upload for that field.",
186
+ });
187
+ }
188
+ // A caller-generated UUID is also the session identity, without changing existing tables.
189
+ const id = input.idempotencyKey ?? randomUUID();
190
+ const blobId = randomUUID();
191
+ const claim = randomUUID();
192
+ const key = `revisions/${blobId}`;
193
+ const reservation = yield* sql.withTransaction(Effect.gen(function* () {
194
+ yield* authorizeBlobOperation(ontology, sql, principal.id, found.permissions, principal.execution, principal.workflow);
195
+ yield* sql.unsafe(`SELECT pg_advisory_xact_lock(818723)`);
196
+ const existing = yield* sql.unsafe(`SELECT id FROM "_blob_uploads" WHERE id = $1::uuid`, [
197
+ id,
198
+ ]);
199
+ const row = existing.length ? yield* load(id, principal) : undefined;
200
+ if (row) {
201
+ const replay = preparedAction !== undefined && row.state === "consumed";
202
+ if ((!replay && row.expired) ||
203
+ row.action !== qualified ||
204
+ row.entity_id !== entity.id ||
205
+ row.property_id !== property.name ||
206
+ row.record_id !== (input.recordId ?? null) ||
207
+ row.create_intent !== (input.createIntent ?? null) ||
208
+ row.filename !== input.filename ||
209
+ row.content_type !== input.contentType ||
210
+ Number(row.size) !== input.size) {
211
+ return yield* conflict();
212
+ }
213
+ // A retried workflow step needs the original ticket to replay its committed action.
214
+ // Attachment still requires a ready session, so this cannot consume it a second time.
215
+ if (replay) {
216
+ return { row, recover: false };
217
+ }
218
+ }
219
+ if (input.recordId) {
220
+ yield* makeOntologyRuntime(ontology, sql).get({
221
+ entity: input.entity,
222
+ id: input.recordId,
223
+ });
224
+ }
225
+ if (row) {
226
+ if (["uploading", "completing", "ready"].includes(row.state)) {
227
+ return { row, recover: false };
228
+ }
229
+ if (row.state !== "initializing") {
230
+ return yield* conflict();
231
+ }
232
+ if (row.claim && !row.claim_stale) {
233
+ return yield* busy(row);
234
+ }
235
+ yield* sql.unsafe(`UPDATE "_blob_uploads" SET claim = $1::uuid, claimed_at = now() WHERE id = $2::uuid`, [claim, row.id]);
236
+ return { row, recover: true };
237
+ }
238
+ const [usage] = yield* sql.unsafe(`SELECT (SELECT coalesce(sum(size), 0)::text FROM "_blobs" WHERE state <> 'deleted') AS bytes,
239
+ (SELECT count(*)::text FROM "_blob_uploads" WHERE principal_id = $1::uuid
240
+ AND branch_id = $2 AND manifest_hash = $3 AND expires_at > now()
241
+ AND state IN ('initializing', 'uploading', 'completing', 'ready', 'deleting')) AS pending`, [principal.id, binding.branchId, ontology.manifest.hash]);
242
+ const overQuota = storageQuotaRejection({
243
+ usedBytes: Number(usage.bytes),
244
+ requestedBytes: input.size,
245
+ quotaBytes: binding.quotaBytes,
246
+ });
247
+ if (overQuota) {
248
+ return yield* overQuota;
249
+ }
250
+ if (Number(usage.pending) >= MAX_PENDING_UPLOADS) {
251
+ // The caller is told which uploads hold its capacity, so it can store or cancel them.
252
+ const open = yield* sql.unsafe(`SELECT id::text, filename,
253
+ to_char(expires_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS expires_at
254
+ FROM "_blob_uploads" WHERE principal_id = $1::uuid
255
+ AND branch_id = $2 AND manifest_hash = $3 AND expires_at > now()
256
+ AND state IN ('initializing', 'uploading', 'completing', 'ready', 'deleting')
257
+ ORDER BY created_at, id LIMIT $4::int`, [principal.id, binding.branchId, ontology.manifest.hash, MAX_PENDING_UPLOADS]);
258
+ return yield* new ActionRejected({
259
+ message: `Pending upload limit reached: ${MAX_PENDING_UPLOADS} uploads are open. ` +
260
+ "Store their files with their actions or cancel the uploads you will not use.",
261
+ limit: {
262
+ kind: "pending_uploads",
263
+ pending: Number(usage.pending),
264
+ max: MAX_PENDING_UPLOADS,
265
+ uploads: open.map((upload) => ({
266
+ id: upload.id,
267
+ filename: upload.filename,
268
+ expiresAt: upload.expires_at,
269
+ })),
270
+ },
271
+ });
272
+ }
273
+ yield* sql.unsafe(`INSERT INTO "_blobs"
274
+ (id, branch_id, key, filename, content_type, size, state)
275
+ VALUES ($1::uuid, $2, $3, $4, $5, $6::bigint, 'ready')`, [blobId, binding.branchId, key, input.filename, input.contentType, input.size]);
276
+ yield* sql.unsafe(`INSERT INTO "_blob_uploads"
277
+ (id, blob_id, principal_id, branch_id, manifest_hash, action, entity_id, property_id,
278
+ record_id, create_intent, filename, content_type, size, state, claim, claimed_at)
279
+ VALUES ($1::uuid, $2::uuid, $3::uuid, $4, $5, $6, $7, $8, $9::uuid, $10::uuid, $11, $12,
280
+ $13::bigint, 'initializing', $14::uuid, now())`, [
281
+ id,
282
+ blobId,
283
+ principal.id,
284
+ binding.branchId,
285
+ ontology.manifest.hash,
286
+ qualified,
287
+ entity.id,
288
+ property.name,
289
+ input.recordId ?? null,
290
+ input.createIntent ?? null,
291
+ input.filename,
292
+ input.contentType,
293
+ input.size,
294
+ claim,
295
+ ]);
296
+ return { row: yield* load(id, principal), recover: false };
297
+ }));
298
+ const { row } = reservation;
299
+ if (row.state !== "initializing") {
300
+ return preparedAction !== undefined && row.state === "consumed"
301
+ ? { ...view(row), ticket: { uploadId: row.id } }
302
+ : view(row);
303
+ }
304
+ const handles = reservation.recover ? yield* provider.pending(row.key) : [];
305
+ const uploadId = handles[0] ??
306
+ (yield* provider
307
+ .initiate({
308
+ key: row.key,
309
+ filename: row.filename,
310
+ contentType: row.content_type,
311
+ sessionId: row.id,
312
+ })
313
+ .pipe(Effect.catchTag("BlobStorageError", (error) => Effect.gen(function* () {
314
+ if (error.initiationRejected) {
315
+ // A definitive refusal created no handle or bytes. Remove only our still-held
316
+ // reservation so retrying the same idempotency key can start again immediately.
317
+ yield* sql
318
+ .withTransaction(Effect.gen(function* () {
319
+ const removed = yield* sql.unsafe(`DELETE FROM "_blob_uploads"
320
+ WHERE id = $1::uuid AND state = 'initializing' AND claim = $2::uuid
321
+ RETURNING blob_id`, [row.id, claim]);
322
+ if (removed.length) {
323
+ yield* sql.unsafe(`DELETE FROM "_blobs" WHERE id = $1::uuid`, [
324
+ removed[0].blob_id,
325
+ ]);
326
+ }
327
+ }))
328
+ .pipe(Effect.catch((cleanupError) => Effect.logError("ontology.storage.initialization_cleanup_failed", cleanupError)));
329
+ }
330
+ return yield* error;
331
+ }))));
332
+ const persisted = yield* sql.withTransaction(sql.unsafe(`UPDATE "_blob_uploads" SET
333
+ state = 'uploading', provider_upload_id = $1, claim = NULL, claimed_at = NULL
334
+ WHERE id = $2::uuid AND state = 'initializing' AND claim = $3::uuid RETURNING id`, [uploadId, row.id, claim]));
335
+ if (!persisted.length) {
336
+ const [latest] = yield* sql.withTransaction(sql.unsafe(`SELECT state, provider_upload_id FROM "_blob_uploads" WHERE id = $1::uuid`, [row.id]));
337
+ // A replacement claim may have adopted this handle after our response was delayed.
338
+ if (latest?.state !== "initializing" && latest?.provider_upload_id !== uploadId) {
339
+ yield* provider.abort({ key: row.key, uploadId });
340
+ yield* sql.withTransaction(sql.unsafe(`UPDATE "_blob_uploads" SET claim = NULL, claimed_at = NULL
341
+ WHERE id = $1::uuid AND state = 'canceled' AND claim = $2::uuid`, [row.id, claim]));
342
+ }
343
+ return yield* conflict();
344
+ }
345
+ return yield* sql.withTransaction(load(row.id, principal).pipe(Effect.map(view)));
346
+ });
347
+ const begin = (input, principal) => beginUpload(input, principal);
348
+ // This is the caller's own upload intent metadata, never another member's files or grants.
349
+ // Match the admission check exactly: expired sessions and earlier deployments hold no slots.
350
+ const list = (principal) => sql.withTransaction(Effect.gen(function* () {
351
+ const rows = yield* sql.unsafe(`SELECT u.*,
352
+ to_char(u.created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS created_at,
353
+ to_char(u.expires_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS expires_at
354
+ FROM "_blob_uploads" u WHERE u.principal_id = $1::uuid
355
+ AND u.branch_id = $2 AND u.manifest_hash = $3 AND u.expires_at > now()
356
+ AND u.state IN ('initializing', 'uploading', 'completing', 'ready', 'deleting')
357
+ ORDER BY u.created_at, u.id LIMIT $4::int`, [principal.id, binding.branchId, ontology.manifest.hash, MAX_PENDING_UPLOADS]);
358
+ return {
359
+ uploads: rows.map((row) => ({
360
+ id: row.id,
361
+ state: row.state,
362
+ filename: row.filename,
363
+ contentType: row.content_type,
364
+ size: Number(row.size),
365
+ entity: ontology.manifest.entities.find((entity) => entity.id === row.entity_id).name,
366
+ property: row.property_id,
367
+ action: row.action,
368
+ recordId: row.record_id,
369
+ createIntent: row.create_intent,
370
+ createdAt: row.created_at,
371
+ expiresAt: row.expires_at,
372
+ awaitingAction: row.state === "ready",
373
+ })),
374
+ pending: rows.length,
375
+ max: MAX_PENDING_UPLOADS,
376
+ };
377
+ }));
378
+ const status = (id, principal) => sql.withTransaction(load(id, principal).pipe(Effect.map(view)));
379
+ // Server extensions can recheck record-level policy before continuing a
380
+ // session. Resolve its persisted binding, never a target supplied by a caller.
381
+ const target = (id, principal) => sql.withTransaction(load(id, principal).pipe(Effect.map((row) => {
382
+ const [entity, action] = row.action.split(".");
383
+ return {
384
+ entity: entity,
385
+ action: action,
386
+ property: row.property_id,
387
+ recordId: row.record_id,
388
+ createIntent: row.create_intent,
389
+ };
390
+ })));
391
+ const part = (id, principal) => Effect.gen(function* () {
392
+ const row = yield* sql.withTransaction(load(id, principal).pipe(Effect.flatMap((row) => row.state === "uploading" && !row.expired && row.provider_upload_id && !row.claim
393
+ ? Effect.succeed(row)
394
+ : Effect.fail(conflict()))));
395
+ return yield* provider.signPart({
396
+ key: row.key,
397
+ uploadId: row.provider_upload_id,
398
+ partNumber: 1,
399
+ size: Number(row.size),
400
+ });
401
+ });
402
+ const completeUpload = (id, principal, heldClaim) => Effect.gen(function* () {
403
+ const claim = randomUUID();
404
+ const row = yield* sql.withTransaction(Effect.gen(function* () {
405
+ const row = yield* load(id, principal);
406
+ if (row.state === "ready" && !row.expired) {
407
+ return row;
408
+ }
409
+ if (heldClaim !== undefined && row.claim !== heldClaim) {
410
+ return yield* conflict();
411
+ }
412
+ if (row.claim && row.claim !== heldClaim && !row.claim_stale) {
413
+ return yield* busy(row);
414
+ }
415
+ if (row.state === "completing" &&
416
+ !row.claim_stale &&
417
+ !row.expired &&
418
+ row.provider_upload_id) {
419
+ return yield* busy(row);
420
+ }
421
+ if (row.expired ||
422
+ !row.provider_upload_id ||
423
+ !(row.state === "uploading" || (row.state === "completing" && row.claim_stale))) {
424
+ return yield* conflict();
425
+ }
426
+ yield* sql.unsafe(`UPDATE "_blob_uploads" SET state = 'completing', claim = $1::uuid, claimed_at = now()
427
+ WHERE id = $2::uuid`, [claim, id]);
428
+ return row;
429
+ }));
430
+ if (row.state === "ready") {
431
+ return view(row);
432
+ }
433
+ let stored = yield* provider.inspect(row.key);
434
+ if (!stored) {
435
+ const parts = yield* provider.parts({ key: row.key, uploadId: row.provider_upload_id });
436
+ if (parts.length !== 1 || parts[0]?.number !== 1 || parts[0].size !== Number(row.size)) {
437
+ return yield* new ActionRejected({
438
+ message: "The uploaded file does not match its declared size.",
439
+ });
440
+ }
441
+ yield* provider.complete({ key: row.key, uploadId: row.provider_upload_id, parts });
442
+ stored = yield* provider.inspect(row.key);
443
+ }
444
+ const prefix = yield* provider.readPrefix(row.key);
445
+ if (!stored ||
446
+ stored.size !== Number(row.size) ||
447
+ stored.sessionId !== id ||
448
+ stored.contentType !== row.content_type ||
449
+ !sniff(prefix, row.content_type)) {
450
+ yield* sql.withTransaction(sql.unsafe(`UPDATE "_blob_uploads" SET state = 'canceled', expires_at = now(),
451
+ claim = NULL WHERE id = $1::uuid AND claim = $2::uuid`, [id, claim]));
452
+ return yield* new ActionRejected({
453
+ message: "The file's contents do not match its declared type or size.",
454
+ });
455
+ }
456
+ return yield* sql.withTransaction(Effect.gen(function* () {
457
+ const current = yield* load(id, principal);
458
+ if (current.state !== "completing" || current.claim !== claim || current.expired) {
459
+ return yield* conflict();
460
+ }
461
+ yield* sql.unsafe(`UPDATE "_blob_uploads" SET state = 'ready', expires_at = now() + interval '24 hours',
462
+ claim = NULL, claimed_at = NULL WHERE id = $1::uuid`, [id]);
463
+ return view(yield* load(id, principal));
464
+ }));
465
+ });
466
+ const cancel = (id, principal) => sql.withTransaction(Effect.gen(function* () {
467
+ const row = yield* load(id, principal);
468
+ if (row.state === "consumed" || row.state === "completing") {
469
+ return yield* conflict();
470
+ }
471
+ if (["deleted", "deleting", "canceled"].includes(row.state)) {
472
+ return view(row);
473
+ }
474
+ yield* sql.unsafe(
475
+ // An initializing provider request may still create a handle after cancellation.
476
+ // Keep its recovery window until a late response is aborted or the claim expires.
477
+ `UPDATE "_blob_uploads" SET state = 'canceled', expires_at = now(),
478
+ claim = CASE WHEN state = 'initializing' THEN claim ELSE NULL END,
479
+ claimed_at = CASE WHEN state = 'initializing' THEN claimed_at ELSE NULL END
480
+ WHERE id = $1::uuid`, [id]);
481
+ return view(yield* load(id, principal));
482
+ }));
483
+ const complete = (id, principal) => completeUpload(id, principal);
484
+ const prepareDownload = (entityName, recordId, propertyName, input, principal) => Effect.gen(function* () {
485
+ const entity = ontology.manifest.entities.find((item) => item.name === entityName);
486
+ const property = entity?.properties.find((item) => item.name === propertyName && item.type === "blob");
487
+ if (!entity || !property) {
488
+ return yield* new NotFound({
489
+ entity: entityName,
490
+ id: recordId,
491
+ message: "File not found.",
492
+ });
493
+ }
494
+ const blob = yield* sql.withTransaction(Effect.gen(function* () {
495
+ const reader = yield* authorizeBlobOperation(ontology, sql, principal.id, [`${entityName}:read`], principal.execution, principal.workflow);
496
+ let revisionId = input.revisionId;
497
+ if (!revisionId || effectiveScope(`${entityName}:read`, reader) !== undefined) {
498
+ const record = yield* makeOntologyRuntime(ontology, sql).get({
499
+ entity: entityName,
500
+ id: recordId,
501
+ reader,
502
+ });
503
+ revisionId ??= record[propertyName]?.id;
504
+ }
505
+ const [blob] = yield* sql.unsafe(`SELECT b.id::text, b.key, b.filename, b.content_type, b.size::text,
506
+ to_char(b.created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS created_at
507
+ FROM "_blobs" b JOIN "_blob_references" r ON r.blob_id = b.id
508
+ WHERE b.id = $1::uuid AND b.state = 'retained' AND b.branch_id = $5 AND r.entity_id = $2
509
+ AND r.property_id = $3 AND r.record_id = $4::uuid FOR SHARE OF b`, [revisionId ?? null, entity.id, property.name, recordId, binding.branchId]);
510
+ if (!blob) {
511
+ return yield* new NotFound({
512
+ entity: entityName,
513
+ id: recordId,
514
+ message: "File not found.",
515
+ });
516
+ }
517
+ return blob;
518
+ }));
519
+ return blob;
520
+ });
521
+ const download = (...args) => Effect.gen(function* () {
522
+ const blob = yield* prepareDownload(...args);
523
+ return yield* provider.download({
524
+ key: blob.key,
525
+ filename: blob.filename,
526
+ contentType: blob.content_type,
527
+ inline: args[3].inline === true &&
528
+ ["image/jpeg", "image/png"].includes(blob.content_type.toLowerCase()),
529
+ });
530
+ });
531
+ const downloadFile = (...args) => Effect.gen(function* () {
532
+ const blob = yield* prepareDownload(...args);
533
+ const sha256 = yield* provider.sha256(blob.key, Number(blob.size));
534
+ // Recheck live access after hashing; no provider I/O holds a database transaction.
535
+ yield* prepareDownload(args[0], args[1], args[2], { revisionId: blob.id }, args[4]);
536
+ const grant = yield* provider.download({
537
+ key: blob.key,
538
+ filename: blob.filename,
539
+ contentType: blob.content_type,
540
+ inline: false,
541
+ });
542
+ return {
543
+ file: {
544
+ id: args[1],
545
+ entity: args[0],
546
+ property: args[2],
547
+ revision_id: blob.id,
548
+ filename: blob.filename,
549
+ content_type: blob.content_type,
550
+ size: Number(blob.size),
551
+ sha256,
552
+ },
553
+ download: grant,
554
+ };
555
+ });
556
+ const files = (invocation) => {
557
+ const publicErrors = (effect) => effect.pipe(Effect.catch((cause) => {
558
+ if (isSqlError(cause)) {
559
+ return Effect.die(cause);
560
+ }
561
+ if (cause instanceof BlobStorageError) {
562
+ return Effect.fail(new ActionRejected({ message: "file_storage_failed", cause }));
563
+ }
564
+ // Caller stream errors retain their exact identity and retry policy.
565
+ return Effect.fail(cause);
566
+ }));
567
+ return Files.of({
568
+ stage: (input) => publicErrors(Effect.gen(function* () {
569
+ const action = invocation.action ?? input.action;
570
+ const actionKey = invocation.idempotencyKey;
571
+ if (!action ||
572
+ !actionKey ||
573
+ !input.key ||
574
+ input.key.length > 512 ||
575
+ (input.action !== undefined && input.action !== action)) {
576
+ return yield* new ActionRejected({ message: "invalid_file_invocation" });
577
+ }
578
+ const [actionEntity, actionName, extra] = action.split(".");
579
+ const found = actionEntity && actionName && !extra
580
+ ? resolveAction(ontology, actionEntity, actionName)
581
+ : undefined;
582
+ const allowed = found?.action.files?.some((target) => target.entity === input.target.entity && target.property === input.target.property);
583
+ if (!found || (!allowed && invocation.action !== undefined)) {
584
+ return yield* new ActionRejected({ message: "file_target_not_declared" });
585
+ }
586
+ const createIntent = (input.createIntent ??
587
+ (input.recordId === undefined ? invocation.createIntent : undefined))?.toLowerCase();
588
+ const hash = createHash("sha256")
589
+ .update(canonicalJson({
590
+ principal: invocation.principal.id,
591
+ branch: binding.branchId,
592
+ manifest: ontology.manifest.hash,
593
+ action,
594
+ actionKey,
595
+ source: input.key,
596
+ target: input.target,
597
+ record: input.recordId ?? null,
598
+ createIntent: createIntent ?? null,
599
+ filename: input.filename,
600
+ contentType: input.contentType,
601
+ size: input.size,
602
+ }))
603
+ .digest();
604
+ hash[6] = (hash[6] & 15) | 80;
605
+ hash[8] = (hash[8] & 63) | 128;
606
+ const hex = hash.subarray(0, 16).toString("hex");
607
+ const id = [
608
+ hex.slice(0, 8),
609
+ hex.slice(8, 12),
610
+ hex.slice(12, 16),
611
+ hex.slice(16, 20),
612
+ hex.slice(20),
613
+ ].join("-");
614
+ let upload = yield* beginUpload({
615
+ entity: input.target.entity,
616
+ property: input.target.property,
617
+ action: actionName,
618
+ ...(input.recordId ? { recordId: input.recordId } : {}),
619
+ ...(createIntent ? { createIntent } : {}),
620
+ idempotencyKey: id,
621
+ filename: input.filename,
622
+ contentType: input.contentType,
623
+ size: input.size,
624
+ }, invocation.principal, action);
625
+ if (upload.ticket) {
626
+ return upload.ticket;
627
+ }
628
+ if (upload.state === "completing") {
629
+ upload = yield* complete(id, invocation.principal);
630
+ }
631
+ else {
632
+ const claim = randomUUID();
633
+ const transfer = yield* sql.withTransaction(Effect.gen(function* () {
634
+ const row = yield* load(id, invocation.principal);
635
+ if (row.state !== "uploading" || row.expired || !row.provider_upload_id) {
636
+ return yield* conflict();
637
+ }
638
+ if (row.claim && !row.claim_stale) {
639
+ return yield* busy(row);
640
+ }
641
+ yield* sql.unsafe(`UPDATE "_blob_uploads" SET claim = $1::uuid, claimed_at = now() WHERE id = $2::uuid`, [claim, id]);
642
+ return row;
643
+ }));
644
+ const grant = yield* provider.signPart({
645
+ key: transfer.key,
646
+ uploadId: transfer.provider_upload_id,
647
+ partNumber: 1,
648
+ size: input.size,
649
+ });
650
+ const source = yield* input.open();
651
+ yield* uploadFile({ grant, size: input.size, source, fetch: fetchImpl });
652
+ upload = yield* completeUpload(id, invocation.principal, claim);
653
+ }
654
+ if (!upload.ticket) {
655
+ return yield* conflict();
656
+ }
657
+ return upload.ticket;
658
+ })),
659
+ open: (input) => publicErrors(Effect.gen(function* () {
660
+ const args = [
661
+ input.entity,
662
+ input.id,
663
+ input.property,
664
+ { revisionId: input.revisionId },
665
+ invocation.principal,
666
+ ];
667
+ const blob = yield* prepareDownload(...args);
668
+ const grant = yield* provider.download({
669
+ key: blob.key,
670
+ filename: blob.filename,
671
+ contentType: blob.content_type,
672
+ inline: false,
673
+ });
674
+ return {
675
+ file: {
676
+ id: blob.id,
677
+ filename: blob.filename,
678
+ contentType: blob.content_type,
679
+ size: Number(blob.size),
680
+ createdAt: blob.created_at,
681
+ },
682
+ body: yield* openFile({ grant, size: Number(blob.size), fetch: fetchImpl }),
683
+ };
684
+ })),
685
+ });
686
+ };
687
+ return { begin, list, status, target, part, complete, cancel, download, downloadFile, files };
688
+ }