@k2b/cloud 0.6.0-rc.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 (540) hide show
  1. package/README.md +98 -0
  2. package/package.json +128 -0
  3. package/public/logo.svg +1 -0
  4. package/scripts/app-favicon.test.ts +49 -0
  5. package/scripts/app-favicon.ts +51 -0
  6. package/scripts/build.ts +205 -0
  7. package/scripts/preload.ts +164 -0
  8. package/scripts/sync-recovery-smoke.ts +200 -0
  9. package/src/_internal/app-favicon.ts +2 -0
  10. package/src/_internal/app-presentation.ts +61 -0
  11. package/src/_internal/app-workspace-state.ts +24 -0
  12. package/src/_internal/bounded-json.ts +47 -0
  13. package/src/_internal/build-metadata.ts +9 -0
  14. package/src/_internal/capabilities.ts +1118 -0
  15. package/src/_internal/define-app.ts +801 -0
  16. package/src/_internal/heartbeat.ts +167 -0
  17. package/src/_internal/help-catalog.ts +241 -0
  18. package/src/_internal/help.ts +95 -0
  19. package/src/_internal/nats-connection.ts +34 -0
  20. package/src/_internal/page-responses.ts +40 -0
  21. package/src/_internal/process-sync.ts +117 -0
  22. package/src/_internal/readiness.ts +3 -0
  23. package/src/_internal/registry-validation.ts +136 -0
  24. package/src/_internal/registry-watch.ts +52 -0
  25. package/src/_internal/registry.ts +338 -0
  26. package/src/_internal/runtime-compatibility.ts +64 -0
  27. package/src/_internal/runtime-context.ts +84 -0
  28. package/src/_internal/runtime-watcher.ts +78 -0
  29. package/src/_internal/static-assets.ts +86 -0
  30. package/src/_internal/status-preserving-ssr.ts +49 -0
  31. package/src/access/PermissionEditor.tsx +486 -0
  32. package/src/access/ResourceApiKeys.tsx +271 -0
  33. package/src/access/messages.ts +121 -0
  34. package/src/access/ui.ts +16 -0
  35. package/src/account/Avatar.tsx +28 -0
  36. package/src/account/EntitySearch.tsx +363 -0
  37. package/src/account/avatar-upload-dialog.tsx +197 -0
  38. package/src/account/avatar-upload.ts +149 -0
  39. package/src/account/ui.ts +3 -0
  40. package/src/ai/admin.ts +47 -0
  41. package/src/ai/approval-routes.ts +130 -0
  42. package/src/ai/approvals.ts +139 -0
  43. package/src/ai/assistant-models.ts +61 -0
  44. package/src/ai/attachments.ts +40 -0
  45. package/src/ai/browser.ts +92 -0
  46. package/src/ai/capabilities.ts +714 -0
  47. package/src/ai/capability-execution.ts +151 -0
  48. package/src/ai/chat/blocks.render.test.tsx +1108 -0
  49. package/src/ai/chat/blocks.tsx +730 -0
  50. package/src/ai/chat/builtin-tools.tsx +428 -0
  51. package/src/ai/chat/capability-table.tsx +87 -0
  52. package/src/ai/chat/composer-adapter.ts +169 -0
  53. package/src/ai/chat/file-tools.tsx +81 -0
  54. package/src/ai/chat/message-actions.tsx +370 -0
  55. package/src/ai/chat/message-utils.ts +479 -0
  56. package/src/ai/chat/messages.ts +43 -0
  57. package/src/ai/chat/presentation.tsx +395 -0
  58. package/src/ai/chat/primitives.tsx +3 -0
  59. package/src/ai/chat/tool-disclosure.tsx +41 -0
  60. package/src/ai/chat/turn-navigator-utils.ts +28 -0
  61. package/src/ai/chat/turn-navigator.tsx +164 -0
  62. package/src/ai/chat/user-message.tsx +149 -0
  63. package/src/ai/chat/visual-tools.tsx +435 -0
  64. package/src/ai/chat/web-tools.tsx +202 -0
  65. package/src/ai/chat-task-contracts.ts +113 -0
  66. package/src/ai/chat-tasks.ts +952 -0
  67. package/src/ai/client/controller.ts +1211 -0
  68. package/src/ai/client/file-source.ts +95 -0
  69. package/src/ai/client/live-connection.ts +140 -0
  70. package/src/ai/client/projection.ts +178 -0
  71. package/src/ai/client/transport.ts +111 -0
  72. package/src/ai/compaction.ts +182 -0
  73. package/src/ai/credentials.ts +124 -0
  74. package/src/ai/default-tool-contracts.ts +108 -0
  75. package/src/ai/default-tools.ts +127 -0
  76. package/src/ai/enrich.ts +280 -0
  77. package/src/ai/executor.ts +1624 -0
  78. package/src/ai/fetch-file-tool.ts +313 -0
  79. package/src/ai/file-context.ts +92 -0
  80. package/src/ai/file-media-type.ts +28 -0
  81. package/src/ai/file-mount.ts +16 -0
  82. package/src/ai/file-tools.ts +566 -0
  83. package/src/ai/files-store.ts +390 -0
  84. package/src/ai/firecrawl-tools.ts +305 -0
  85. package/src/ai/grids-skill.ts +126 -0
  86. package/src/ai/http.ts +265 -0
  87. package/src/ai/index.ts +337 -0
  88. package/src/ai/kit-skill.ts +22 -0
  89. package/src/ai/limits.ts +5 -0
  90. package/src/ai/live-events.ts +289 -0
  91. package/src/ai/live-messages.ts +45 -0
  92. package/src/ai/live-outbox.ts +102 -0
  93. package/src/ai/live-routes.ts +412 -0
  94. package/src/ai/live.ts +2 -0
  95. package/src/ai/maintenance.ts +302 -0
  96. package/src/ai/markdown-pdf-tool.ts +95 -0
  97. package/src/ai/memories.ts +605 -0
  98. package/src/ai/memory-learning-runs.ts +222 -0
  99. package/src/ai/memory-learning.ts +619 -0
  100. package/src/ai/memory-tool.ts +120 -0
  101. package/src/ai/memory-workflow-evidence.ts +158 -0
  102. package/src/ai/migrate.ts +2082 -0
  103. package/src/ai/model-access-migrate.ts +30 -0
  104. package/src/ai/model-access.ts +191 -0
  105. package/src/ai/personal-agent.ts +6 -0
  106. package/src/ai/prefs.ts +72 -0
  107. package/src/ai/project-tool.ts +94 -0
  108. package/src/ai/projects-routes.ts +277 -0
  109. package/src/ai/projects.ts +902 -0
  110. package/src/ai/protocol.ts +279 -0
  111. package/src/ai/provider.ts +73 -0
  112. package/src/ai/public-projection.ts +25 -0
  113. package/src/ai/resource-markers.ts +31 -0
  114. package/src/ai/resource-refs.ts +70 -0
  115. package/src/ai/routes.ts +1131 -0
  116. package/src/ai/runtime-tools.ts +39 -0
  117. package/src/ai/runtime.ts +544 -0
  118. package/src/ai/settings.ts +529 -0
  119. package/src/ai/short-id.ts +45 -0
  120. package/src/ai/skill-catalog.ts +73 -0
  121. package/src/ai/skill-format.ts +145 -0
  122. package/src/ai/skill-seeds.ts +430 -0
  123. package/src/ai/skill-tool.ts +81 -0
  124. package/src/ai/skills-routes.ts +143 -0
  125. package/src/ai/skills.ts +934 -0
  126. package/src/ai/solid.ts +27 -0
  127. package/src/ai/store.ts +3113 -0
  128. package/src/ai/stream.ts +308 -0
  129. package/src/ai/structured-runs.ts +66 -0
  130. package/src/ai/structured.ts +186 -0
  131. package/src/ai/system-prompt.ts +170 -0
  132. package/src/ai/task-prompt.ts +22 -0
  133. package/src/ai/timeline.ts +148 -0
  134. package/src/ai/tool-audit.ts +177 -0
  135. package/src/ai/tool-result-budget.ts +16 -0
  136. package/src/ai/tools.ts +155 -0
  137. package/src/ai/types.ts +967 -0
  138. package/src/ai/ui.tsx +39 -0
  139. package/src/ai/usage-migrate.ts +81 -0
  140. package/src/ai/usage.ts +315 -0
  141. package/src/ai/validate.ts +54 -0
  142. package/src/ai/vision-tool.ts +79 -0
  143. package/src/api/accounts-entities.ts +174 -0
  144. package/src/api/admin-ai-projects.ts +74 -0
  145. package/src/api/admin-ai-skills.ts +76 -0
  146. package/src/api/admin-ai-usage.ts +42 -0
  147. package/src/api/admin-core-settings.ts +508 -0
  148. package/src/api/admin-identity.ts +148 -0
  149. package/src/api/admin-lifecycle.ts +206 -0
  150. package/src/api/admin-linux-identities.ts +66 -0
  151. package/src/api/announcements.ts +131 -0
  152. package/src/api/app-approval.ts +129 -0
  153. package/src/api/apps.ts +51 -0
  154. package/src/api/auth/schemas.ts +54 -0
  155. package/src/api/auth.ts +370 -0
  156. package/src/api/capabilities.ts +658 -0
  157. package/src/api/help-messages.ts +19 -0
  158. package/src/api/help.ts +78 -0
  159. package/src/api/index.ts +108 -0
  160. package/src/api/mcp.ts +744 -0
  161. package/src/api/me-rail.ts +45 -0
  162. package/src/api/me.ts +753 -0
  163. package/src/api/search/schemas.ts +79 -0
  164. package/src/api/search.bench.ts +185 -0
  165. package/src/api/search.ts +407 -0
  166. package/src/api/sync-ops.ts +79 -0
  167. package/src/api/widgets.ts +143 -0
  168. package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
  169. package/src/browser/CloudResourceSearch.tsx +791 -0
  170. package/src/browser/app-approval-vault.ts +305 -0
  171. package/src/browser/app-approval.ts +234 -0
  172. package/src/browser/live-websocket.ts +268 -0
  173. package/src/browser/locale-preference.ts +25 -0
  174. package/src/browser/notification-target.ts +27 -0
  175. package/src/browser/notification-timeout.ts +14 -0
  176. package/src/browser/notifications.ts +181 -0
  177. package/src/browser/resource-clipboard.ts +70 -0
  178. package/src/browser/resource-picker.tsx +39 -0
  179. package/src/browser/resource-search-messages.ts +55 -0
  180. package/src/browser/resource-search.ts +27 -0
  181. package/src/capabilities/client.ts +149 -0
  182. package/src/capabilities/response.ts +39 -0
  183. package/src/capabilities/server.ts +215 -0
  184. package/src/capabilities/signals.ts +4 -0
  185. package/src/capabilities/testing.ts +12 -0
  186. package/src/capabilities/types.ts +32 -0
  187. package/src/cli/access.ts +499 -0
  188. package/src/cli/account.ts +382 -0
  189. package/src/cli/admin/account-administration.ts +45 -0
  190. package/src/cli/admin/account-categories.ts +49 -0
  191. package/src/cli/admin/ai-usage.ts +118 -0
  192. package/src/cli/admin/app-sign-in.ts +49 -0
  193. package/src/cli/admin/data.ts +343 -0
  194. package/src/cli/admin/documentation.ts +34 -0
  195. package/src/cli/admin/gateway.ts +155 -0
  196. package/src/cli/admin/index.ts +61 -0
  197. package/src/cli/admin/instance.ts +250 -0
  198. package/src/cli/admin/jobs.ts +223 -0
  199. package/src/cli/admin/legal.ts +122 -0
  200. package/src/cli/admin/linux.ts +53 -0
  201. package/src/cli/admin/logs.ts +324 -0
  202. package/src/cli/admin/metrics.ts +142 -0
  203. package/src/cli/admin/nats.ts +167 -0
  204. package/src/cli/admin/notifications.ts +516 -0
  205. package/src/cli/admin/shared.ts +113 -0
  206. package/src/cli/admin/sync.ts +188 -0
  207. package/src/cli/admin/telemetry.ts +314 -0
  208. package/src/cli/admin/webhooks.ts +222 -0
  209. package/src/cli/admin/workflows.ts +367 -0
  210. package/src/cli/apps.ts +40 -0
  211. package/src/cli/capabilities.ts +239 -0
  212. package/src/cli/commands.ts +604 -0
  213. package/src/cli/index.ts +61 -0
  214. package/src/cli/locale.ts +31 -0
  215. package/src/cli/output.ts +58 -0
  216. package/src/clients/core.ts +20 -0
  217. package/src/config/env.ts +54 -0
  218. package/src/config/index.ts +6 -0
  219. package/src/contracts/account-categories.ts +62 -0
  220. package/src/contracts/announcements.ts +121 -0
  221. package/src/contracts/app-approval.ts +197 -0
  222. package/src/contracts/app.ts +162 -0
  223. package/src/contracts/capabilities.ts +484 -0
  224. package/src/contracts/index.ts +14 -0
  225. package/src/contracts/notification-live.ts +66 -0
  226. package/src/contracts/notification-types.ts +235 -0
  227. package/src/contracts/posix.ts +69 -0
  228. package/src/contracts/profile.ts +85 -0
  229. package/src/contracts/rail-preferences.ts +53 -0
  230. package/src/contracts/registry.ts +110 -0
  231. package/src/contracts/resource-clipboard.ts +33 -0
  232. package/src/contracts/settings-types.ts +98 -0
  233. package/src/contracts/shared.ts +408 -0
  234. package/src/contracts/user-notifications.ts +101 -0
  235. package/src/contracts/widgets.ts +183 -0
  236. package/src/desktop/index.ts +704 -0
  237. package/src/desktop/solid.tsx +938 -0
  238. package/src/index.ts +58 -0
  239. package/src/server/actor.ts +41 -0
  240. package/src/server/api/index.ts +2 -0
  241. package/src/server/api/respond.ts +96 -0
  242. package/src/server/api-client.ts +54 -0
  243. package/src/server/app-context.ts +39 -0
  244. package/src/server/help.ts +187 -0
  245. package/src/server/index.ts +89 -0
  246. package/src/server/locale.ts +55 -0
  247. package/src/server/middleware/auth.ts +386 -0
  248. package/src/server/middleware/index.ts +28 -0
  249. package/src/server/middleware/invocation.ts +55 -0
  250. package/src/server/middleware/middleware.ts +65 -0
  251. package/src/server/middleware/openapi.ts +126 -0
  252. package/src/server/middleware/rate-limit.ts +125 -0
  253. package/src/server/middleware/request-logger.ts +41 -0
  254. package/src/server/middleware/route-template.ts +46 -0
  255. package/src/server/middleware/runtime.ts +22 -0
  256. package/src/server/middleware/settings.ts +55 -0
  257. package/src/server/middleware/validator.ts +49 -0
  258. package/src/server/middleware/workload.ts +22 -0
  259. package/src/server/ratelimit.ts +151 -0
  260. package/src/server/request-cookies.ts +18 -0
  261. package/src/server/services/access.ts +644 -0
  262. package/src/server/services/freeipa/client.ts +137 -0
  263. package/src/server/services/freeipa/index.ts +9 -0
  264. package/src/server/services/freeipa/session.ts +123 -0
  265. package/src/server/services/freeipa/test-certificates.ts +44 -0
  266. package/src/server/services/freeipa/tls.ts +44 -0
  267. package/src/server/services/freeipa/transport.ts +116 -0
  268. package/src/server/services/freeipa/util.ts +59 -0
  269. package/src/server/services/geo.ts +198 -0
  270. package/src/server/services/index.ts +48 -0
  271. package/src/server/services/pagination.ts +22 -0
  272. package/src/server/services/services.ts +13 -0
  273. package/src/server/time.ts +33 -0
  274. package/src/services/account-category-policy.ts +51 -0
  275. package/src/services/account-lifecycle/audit.ts +41 -0
  276. package/src/services/account-lifecycle/index.ts +940 -0
  277. package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
  278. package/src/services/account-lifecycle/notification-sender.ts +13 -0
  279. package/src/services/account-lifecycle/scheduler.ts +506 -0
  280. package/src/services/account-model.ts +22 -0
  281. package/src/services/accounts/app.ts +1671 -0
  282. package/src/services/accounts/authz.ts +44 -0
  283. package/src/services/accounts/avatar.ts +120 -0
  284. package/src/services/accounts/base-group.ts +11 -0
  285. package/src/services/accounts/base-user.ts +46 -0
  286. package/src/services/accounts/duplicate-emails.ts +82 -0
  287. package/src/services/accounts/entities.ts +622 -0
  288. package/src/services/accounts/group-sql.ts +106 -0
  289. package/src/services/accounts/groups.ts +272 -0
  290. package/src/services/accounts/index.ts +14 -0
  291. package/src/services/accounts/ipa-data.ts +66 -0
  292. package/src/services/accounts/lifecycle.ts +2 -0
  293. package/src/services/accounts/local-groups.ts +499 -0
  294. package/src/services/accounts/model.ts +129 -0
  295. package/src/services/accounts/notification-sender.ts +31 -0
  296. package/src/services/accounts/posix.ts +386 -0
  297. package/src/services/accounts/request-policy.ts +11 -0
  298. package/src/services/accounts/switching.ts +126 -0
  299. package/src/services/accounts/users.ts +745 -0
  300. package/src/services/announcements/index.ts +224 -0
  301. package/src/services/app-approval.ts +498 -0
  302. package/src/services/audit/index.ts +438 -0
  303. package/src/services/auth-flows/index.ts +15 -0
  304. package/src/services/auth-flows/ipa.ts +197 -0
  305. package/src/services/auth-flows/magic-link.ts +185 -0
  306. package/src/services/auth-flows/notification-sender.ts +10 -0
  307. package/src/services/auth-flows/password-reset.ts +268 -0
  308. package/src/services/auth-flows/proxy-return.ts +49 -0
  309. package/src/services/document-extraction.ts +148 -0
  310. package/src/services/freeipa-config.ts +179 -0
  311. package/src/services/gateway.ts +186 -0
  312. package/src/services/identity/constants.ts +18 -0
  313. package/src/services/identity/index.ts +52 -0
  314. package/src/services/identity/invocation-actor.ts +120 -0
  315. package/src/services/identity/invocation-authority.ts +53 -0
  316. package/src/services/identity/invocation-operations.ts +15 -0
  317. package/src/services/identity/invocation-token.ts +276 -0
  318. package/src/services/identity/key-config.ts +41 -0
  319. package/src/services/identity/key-ring.ts +530 -0
  320. package/src/services/identity/metrics.ts +29 -0
  321. package/src/services/identity/routes.ts +18 -0
  322. package/src/services/identity/runtime-config.ts +88 -0
  323. package/src/services/identity/session-token.ts +152 -0
  324. package/src/services/identity/signer-refresh.ts +44 -0
  325. package/src/services/identity/workload-auth.ts +64 -0
  326. package/src/services/index.ts +187 -0
  327. package/src/services/ipa/auth.ts +216 -0
  328. package/src/services/ipa/connection.ts +57 -0
  329. package/src/services/ipa/effective-groups.ts +70 -0
  330. package/src/services/ipa/groups.ts +722 -0
  331. package/src/services/ipa/guard.ts +17 -0
  332. package/src/services/ipa/index.ts +17 -0
  333. package/src/services/ipa/posix.ts +19 -0
  334. package/src/services/ipa/profile.ts +132 -0
  335. package/src/services/ipa/runtime.ts +28 -0
  336. package/src/services/ipa/search.ts +154 -0
  337. package/src/services/ipa/service-account.ts +15 -0
  338. package/src/services/ipa/sync-planning.ts +100 -0
  339. package/src/services/ipa/sync.ts +862 -0
  340. package/src/services/ipa/users.ts +806 -0
  341. package/src/services/legal-consent.ts +76 -0
  342. package/src/services/logging/index.ts +477 -0
  343. package/src/services/logging/redaction.ts +20 -0
  344. package/src/services/logging/trace.ts +1231 -0
  345. package/src/services/mandates/index.ts +855 -0
  346. package/src/services/mandates/policy.ts +85 -0
  347. package/src/services/network-security.ts +31 -0
  348. package/src/services/notifications/batches.ts +722 -0
  349. package/src/services/notifications/browser.ts +252 -0
  350. package/src/services/notifications/catalog.ts +113 -0
  351. package/src/services/notifications/channels.ts +104 -0
  352. package/src/services/notifications/dispatcher.ts +139 -0
  353. package/src/services/notifications/email.ts +118 -0
  354. package/src/services/notifications/index.ts +633 -0
  355. package/src/services/notifications/live.ts +50 -0
  356. package/src/services/notifications/observability.ts +407 -0
  357. package/src/services/notifications/platform.ts +421 -0
  358. package/src/services/notifications/runtime.ts +120 -0
  359. package/src/services/notifications/user.ts +248 -0
  360. package/src/services/notifications/web-push-transport.ts +135 -0
  361. package/src/services/oauth-tokens.ts +209 -0
  362. package/src/services/outbox.ts +187 -0
  363. package/src/services/pdf/gotenberg.ts +290 -0
  364. package/src/services/pdf/index.ts +43 -0
  365. package/src/services/pdf/markdown.ts +209 -0
  366. package/src/services/pdf/template-preview.ts +122 -0
  367. package/src/services/postgres.ts +76 -0
  368. package/src/services/providers/index.ts +27 -0
  369. package/src/services/providers/local/auth.ts +51 -0
  370. package/src/services/providers/local/index.ts +4 -0
  371. package/src/services/providers/local/users.ts +263 -0
  372. package/src/services/rail-preferences.ts +27 -0
  373. package/src/services/runtime-lifecycle.ts +191 -0
  374. package/src/services/secrets.ts +10 -0
  375. package/src/services/service-account-credentials.ts +891 -0
  376. package/src/services/service-accounts.ts +184 -0
  377. package/src/services/session/index.ts +236 -0
  378. package/src/services/session/test-fixture.ts +18 -0
  379. package/src/services/session/user.ts +144 -0
  380. package/src/services/settings/api.ts +61 -0
  381. package/src/services/settings/app.ts +86 -0
  382. package/src/services/settings/core-settings.ts +796 -0
  383. package/src/services/settings/crypto.ts +69 -0
  384. package/src/services/settings/defaults.ts +374 -0
  385. package/src/services/settings/index.ts +215 -0
  386. package/src/services/settings/namespace.ts +9 -0
  387. package/src/services/settings/snapshot.ts +49 -0
  388. package/src/services/settings/store.ts +241 -0
  389. package/src/services/settings/templates.ts +4 -0
  390. package/src/services/sync-ops.ts +403 -0
  391. package/src/services/topic-cursor.ts +22 -0
  392. package/src/services/weather/forecast.ts +323 -0
  393. package/src/services/weather/geo.ts +119 -0
  394. package/src/services/weather/index.ts +67 -0
  395. package/src/services/weather/location.ts +24 -0
  396. package/src/services/weather/locations.ts +133 -0
  397. package/src/services/weather/migrate.ts +45 -0
  398. package/src/services/weather/settings.ts +66 -0
  399. package/src/services/weather/short-id.ts +57 -0
  400. package/src/services/weather/types.ts +61 -0
  401. package/src/services/weather/ui.ts +50 -0
  402. package/src/services/webauthn.ts +385 -0
  403. package/src/shared/account-action-notice.ts +70 -0
  404. package/src/shared/account-display.ts +15 -0
  405. package/src/shared/account-session.ts +15 -0
  406. package/src/shared/ai-platform-prompt.ts +134 -0
  407. package/src/shared/ai-usage.ts +49 -0
  408. package/src/shared/app-presentation.ts +48 -0
  409. package/src/shared/app-url.ts +12 -0
  410. package/src/shared/branding.ts +14 -0
  411. package/src/shared/capability-messages.ts +70 -0
  412. package/src/shared/email-html.ts +54 -0
  413. package/src/shared/format.ts +91 -0
  414. package/src/shared/help.ts +68 -0
  415. package/src/shared/icons.ts +400 -0
  416. package/src/shared/index.ts +54 -0
  417. package/src/shared/locale.ts +48 -0
  418. package/src/shared/login-method.ts +44 -0
  419. package/src/shared/markdown/client.ts +130 -0
  420. package/src/shared/markdown/extensions/code.ts +105 -0
  421. package/src/shared/markdown/extensions/guided-help.ts +100 -0
  422. package/src/shared/markdown/extensions/images.ts +79 -0
  423. package/src/shared/markdown/extensions/info-blocks.ts +97 -0
  424. package/src/shared/markdown/extensions/katex.ts +120 -0
  425. package/src/shared/markdown/extensions/links.ts +43 -0
  426. package/src/shared/markdown/extensions/mark.ts +48 -0
  427. package/src/shared/markdown/extensions/sub-sup.ts +60 -0
  428. package/src/shared/markdown/extensions/tables.ts +109 -0
  429. package/src/shared/markdown/extensions/task-list.ts +53 -0
  430. package/src/shared/markdown/formula.ts +1187 -0
  431. package/src/shared/markdown/index.ts +193 -0
  432. package/src/shared/markdown/shared.ts +36 -0
  433. package/src/shared/mock-cover.ts +130 -0
  434. package/src/shared/network-address.ts +106 -0
  435. package/src/shared/redirect.ts +56 -0
  436. package/src/shared/template-rendering.ts +140 -0
  437. package/src/shared/theme.ts +68 -0
  438. package/src/shared/time.ts +13 -0
  439. package/src/ssr/AdminLayout.tsx +53 -0
  440. package/src/ssr/AdminSidebar.tsx +50 -0
  441. package/src/ssr/AppLaunchpad.island.tsx +236 -0
  442. package/src/ssr/AppWorkspaceController.island.tsx +21 -0
  443. package/src/ssr/BrowserNotifications.island.tsx +135 -0
  444. package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
  445. package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
  446. package/src/ssr/GlobalSearchDialog.tsx +35 -0
  447. package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
  448. package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
  449. package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
  450. package/src/ssr/Layout.render.test.tsx +148 -0
  451. package/src/ssr/Layout.tsx +292 -0
  452. package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
  453. package/src/ssr/LayoutFooter.tsx +22 -0
  454. package/src/ssr/LayoutHeader.tsx +81 -0
  455. package/src/ssr/LayoutHelp.tsx +869 -0
  456. package/src/ssr/LayoutPreferences.island.tsx +36 -0
  457. package/src/ssr/LayoutRail.tsx +58 -0
  458. package/src/ssr/MinimalLayout.tsx +46 -0
  459. package/src/ssr/PageError.tsx +29 -0
  460. package/src/ssr/ProfilePreferences.island.tsx +92 -0
  461. package/src/ssr/RailApps.island.tsx +61 -0
  462. package/src/ssr/RailEditor.tsx +293 -0
  463. package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
  464. package/src/ssr/TimezoneCookie.island.tsx +23 -0
  465. package/src/ssr/admin-active-link.ts +47 -0
  466. package/src/ssr/admin-navigation.ts +78 -0
  467. package/src/ssr/app-appearance.ts +23 -0
  468. package/src/ssr/app-navigation.ts +41 -0
  469. package/src/ssr/help-messages.ts +104 -0
  470. package/src/ssr/index.ts +14 -0
  471. package/src/ssr/islands/SearchBar.island.tsx +80 -0
  472. package/src/ssr/islands/index.ts +16 -0
  473. package/src/ssr/layout-context.ts +17 -0
  474. package/src/ssr/layout-help-markdown.ts +9 -0
  475. package/src/ssr/layout-help-navigation.ts +24 -0
  476. package/src/ssr/layout-help-url.ts +5 -0
  477. package/src/ssr/layout-runtime.ts +22 -0
  478. package/src/ssr/page-error-messages.ts +25 -0
  479. package/src/ssr/platform-messages.ts +135 -0
  480. package/src/ssr/preference-controller.ts +44 -0
  481. package/src/ssr/profile-preferences-messages.ts +29 -0
  482. package/src/ssr/rail-context.ts +21 -0
  483. package/src/ssr/rail-messages.ts +66 -0
  484. package/src/ssr/rail-navigation.ts +60 -0
  485. package/src/ssr/runtime.ts +31 -0
  486. package/src/ssr/url-filter.ts +127 -0
  487. package/src/styles/base-popover.css +36 -0
  488. package/src/styles/effects.css +851 -0
  489. package/src/styles/global.css +268 -0
  490. package/src/styles/input.css +58 -0
  491. package/src/styles/tokens.css +230 -0
  492. package/src/styles/utilities-buttons.css +405 -0
  493. package/src/styles/utilities-data.css +33 -0
  494. package/src/styles/utilities-feedback.css +232 -0
  495. package/src/styles/utilities-layout.css +603 -0
  496. package/src/styles/utilities-markdown-editor.css +525 -0
  497. package/src/styles/utilities-markdown-table.css +234 -0
  498. package/src/styles/utilities-navigation.css +1054 -0
  499. package/src/types/ambient.d.ts +24 -0
  500. package/src/workflows/ai/index.ts +17 -0
  501. package/src/workflows/ai/runtime.ts +286 -0
  502. package/src/workflows/ai/store.ts +314 -0
  503. package/src/workflows/ai/types.ts +111 -0
  504. package/src/workflows/ai-actions.ts +269 -0
  505. package/src/workflows/authoring.ts +88 -0
  506. package/src/workflows/builtins.ts +193 -0
  507. package/src/workflows/contracts.ts +280 -0
  508. package/src/workflows/definition.ts +334 -0
  509. package/src/workflows/editor.ts +113 -0
  510. package/src/workflows/index.ts +71 -0
  511. package/src/workflows/language/binder.ts +45 -0
  512. package/src/workflows/language/canonical.ts +29 -0
  513. package/src/workflows/language/compiler.ts +396 -0
  514. package/src/workflows/language/expressions.ts +34 -0
  515. package/src/workflows/language/index.ts +20 -0
  516. package/src/workflows/language/references.ts +55 -0
  517. package/src/workflows/language/schema.ts +134 -0
  518. package/src/workflows/language/strict-yaml.ts +131 -0
  519. package/src/workflows/manifest.ts +24 -0
  520. package/src/workflows/module.ts +12 -0
  521. package/src/workflows/runtime/coordinator.ts +115 -0
  522. package/src/workflows/runtime/executor.ts +1003 -0
  523. package/src/workflows/runtime/index.ts +60 -0
  524. package/src/workflows/runtime/instant.ts +8 -0
  525. package/src/workflows/runtime/ports.ts +213 -0
  526. package/src/workflows/runtime/schedule.ts +203 -0
  527. package/src/workflows/runtime/trigger.ts +37 -0
  528. package/src/workflows/runtime/wake.ts +13 -0
  529. package/src/workflows/store/actions.ts +438 -0
  530. package/src/workflows/store/budget.ts +145 -0
  531. package/src/workflows/store/definitions.ts +345 -0
  532. package/src/workflows/store/events.ts +383 -0
  533. package/src/workflows/store/index.ts +107 -0
  534. package/src/workflows/store/observability.ts +594 -0
  535. package/src/workflows/store/runs.ts +975 -0
  536. package/src/workflows/store/transaction.ts +23 -0
  537. package/src/workflows/store/worker.ts +439 -0
  538. package/src/workflows/testing/conformance.ts +72 -0
  539. package/src/workflows/testing/index.ts +11 -0
  540. package/src/workflows/testing/process-fixtures.ts +275 -0
@@ -0,0 +1,209 @@
1
+ import { Marked, Renderer, type Tokens } from "marked";
2
+ import postcss from "postcss";
3
+ import {
4
+ type GotenbergConfig,
5
+ getGotenbergConfig,
6
+ type RenderHtmlToPdfOptions,
7
+ type RenderHtmlToPdfResult,
8
+ renderHtmlToPdfWithConfig,
9
+ } from "./gotenberg";
10
+
11
+ export const MARKDOWN_PDF_MAX_CUSTOM_CSS_BYTES = 32 * 1024;
12
+ export const MARKDOWN_PDF_MAX_MARKDOWN_BYTES = 256 * 1024;
13
+
14
+ export const MARKDOWN_PDF_TEMPLATE_IDS = ["document", "report", "compact"] as const;
15
+ export type MarkdownPdfTemplateId = (typeof MARKDOWN_PDF_TEMPLATE_IDS)[number];
16
+
17
+ export type MarkdownPdfErrorCode = "bad_input" | "invalid_css" | "external_asset_unsupported";
18
+ export type MarkdownPdfErrorReason = "css_too_large" | "markdown_empty" | "unknown_template";
19
+
20
+ export class MarkdownPdfError extends Error {
21
+ constructor(
22
+ readonly code: MarkdownPdfErrorCode,
23
+ message: string,
24
+ readonly reason?: MarkdownPdfErrorReason,
25
+ ) {
26
+ super(message);
27
+ this.name = "MarkdownPdfError";
28
+ }
29
+ }
30
+
31
+ export type RenderMarkdownToPdfInput = {
32
+ markdown: string;
33
+ templateId?: MarkdownPdfTemplateId;
34
+ customCss?: string;
35
+ };
36
+
37
+ export type RenderMarkdownToPdfOptions = RenderHtmlToPdfOptions;
38
+
39
+ const TEMPLATE_CSS: Record<MarkdownPdfTemplateId, string> = {
40
+ document: `
41
+ @page { size: A4; margin: 22mm 20mm 24mm; }
42
+ :root { color: #1f2937; font: 11pt/1.55 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
43
+ body { margin: 0; background: #fff; }
44
+ .markdown-document { max-width: 100%; overflow-wrap: anywhere; }
45
+ h1, h2, h3, h4, h5, h6 { color: #111827; line-height: 1.2; margin: 1.4em 0 .55em; break-after: avoid-page; }
46
+ h1 { margin-top: 0; font-size: 2em; } h2 { font-size: 1.5em; } h3 { font-size: 1.2em; }
47
+ p, ul, ol, blockquote, pre, table { margin: 0 0 1em; }
48
+ a { color: #2563eb; text-decoration: underline; }
49
+ blockquote { margin-left: 0; padding-left: 1em; border-left: 3px solid #cbd5e1; color: #475569; }
50
+ code { border-radius: 3px; background: #f1f5f9; padding: .08em .28em; font: .9em/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }
51
+ pre { overflow-wrap: anywhere; white-space: pre-wrap; break-inside: avoid; border-radius: 6px; background: #f8fafc; padding: 1em; }
52
+ pre code { background: transparent; padding: 0; }
53
+ table { width: 100%; border-collapse: collapse; font-size: .94em; }
54
+ th, td { border: 1px solid #cbd5e1; padding: .5em .65em; text-align: left; vertical-align: top; }
55
+ th { background: #f1f5f9; font-weight: 650; }
56
+ thead { display: table-header-group; } tr { break-inside: avoid; }
57
+ hr { border: 0; border-top: 1px solid #cbd5e1; margin: 1.5em 0; }
58
+ `,
59
+ report: `
60
+ @page { size: A4; margin: 24mm 22mm 26mm; }
61
+ :root { color: #263244; font: 10.75pt/1.58 Georgia, "Times New Roman", serif; }
62
+ body { margin: 0; background: #fff; }
63
+ .markdown-document { max-width: 100%; overflow-wrap: anywhere; }
64
+ h1, h2, h3, h4, h5, h6 { color: #13233a; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; line-height: 1.18; break-after: avoid-page; }
65
+ h1 { margin: 0 0 .9em; padding-bottom: .35em; border-bottom: 2px solid #2f5f87; font-size: 2.15em; }
66
+ h2 { margin: 1.6em 0 .6em; padding-bottom: .2em; border-bottom: 1px solid #b8c7d6; font-size: 1.48em; }
67
+ h3 { margin: 1.35em 0 .5em; font-size: 1.15em; }
68
+ p, ul, ol, blockquote, pre, table { margin: 0 0 1em; }
69
+ a { color: #245a86; }
70
+ blockquote { margin-left: 0; padding: .7em 1em; border-left: 4px solid #4f799d; background: #f4f7fa; color: #41566c; }
71
+ code { border-radius: 3px; background: #edf2f7; padding: .08em .28em; font: .88em/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }
72
+ pre { overflow-wrap: anywhere; white-space: pre-wrap; break-inside: avoid; background: #f4f7fa; padding: 1em; }
73
+ pre code { background: transparent; padding: 0; }
74
+ table { width: 100%; border-collapse: collapse; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: .9em; }
75
+ th, td { border-bottom: 1px solid #b8c7d6; padding: .55em .7em; text-align: left; vertical-align: top; }
76
+ th { background: #e8eff5; color: #13233a; font-weight: 700; }
77
+ thead { display: table-header-group; } tr { break-inside: avoid; }
78
+ hr { border: 0; border-top: 1px solid #b8c7d6; margin: 1.6em 0; }
79
+ `,
80
+ compact: `
81
+ @page { size: A4; margin: 14mm 15mm 16mm; }
82
+ :root { color: #20252b; font: 9pt/1.38 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
83
+ body { margin: 0; background: #fff; }
84
+ .markdown-document { max-width: 100%; overflow-wrap: anywhere; }
85
+ h1, h2, h3, h4, h5, h6 { color: #111827; line-height: 1.15; margin: 1em 0 .35em; break-after: avoid-page; }
86
+ h1 { margin-top: 0; font-size: 1.65em; } h2 { font-size: 1.3em; } h3 { font-size: 1.08em; }
87
+ p, ul, ol, blockquote, pre, table { margin: 0 0 .58em; }
88
+ ul, ol { padding-left: 1.5em; }
89
+ a { color: #1d4ed8; }
90
+ blockquote { margin-left: 0; padding-left: .7em; border-left: 2px solid #9ca3af; color: #4b5563; }
91
+ code { background: #f3f4f6; padding: .05em .2em; font: .88em/1.35 ui-monospace, SFMono-Regular, Menlo, monospace; }
92
+ pre { overflow-wrap: anywhere; white-space: pre-wrap; break-inside: avoid; background: #f7f7f8; padding: .65em; }
93
+ pre code { background: transparent; padding: 0; }
94
+ table { width: 100%; border-collapse: collapse; font-size: .88em; }
95
+ th, td { border: 1px solid #d1d5db; padding: .3em .42em; text-align: left; vertical-align: top; }
96
+ th { background: #f3f4f6; }
97
+ thead { display: table-header-group; } tr { break-inside: avoid; }
98
+ hr { border: 0; border-top: 1px solid #d1d5db; margin: .9em 0; }
99
+ `,
100
+ };
101
+
102
+ const byteLength = (value: string): number => new TextEncoder().encode(value).byteLength;
103
+
104
+ const escapeHtml = (value: string): string =>
105
+ value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
106
+
107
+ const safeLink = (value: string): string | null => {
108
+ const normalized = value.trim().replace(/[\u0000-\u001f\u007f-\u009f\s]+/gu, "");
109
+ return /^(?:javascript|vbscript|data|file):/iu.test(normalized) ? null : value;
110
+ };
111
+
112
+ const renderMarkdown = (source: string): string => {
113
+ const renderer = new Renderer();
114
+ renderer.html = ({ text }: Tokens.HTML | Tokens.Tag) => escapeHtml(text);
115
+ renderer.image = ({ href, title, text }: Tokens.Image) => {
116
+ const label = escapeHtml(text.trim() ? `Image: ${text}` : "Image");
117
+ const safeHref = safeLink(href);
118
+ if (!safeHref) return label;
119
+ const titleAttribute = title ? ` title="${escapeHtml(title)}"` : "";
120
+ return `<a href="${escapeHtml(safeHref)}"${titleAttribute}>${label}</a>`;
121
+ };
122
+ renderer.link = function ({ href, title, tokens }: Tokens.Link) {
123
+ const body = this.parser.parseInline(tokens);
124
+ const safeHref = safeLink(href);
125
+ if (!safeHref) return body;
126
+ const titleAttribute = title ? ` title="${escapeHtml(title)}"` : "";
127
+ return `<a href="${escapeHtml(safeHref)}"${titleAttribute}>${body}</a>`;
128
+ };
129
+
130
+ const parser = new Marked({ breaks: true, gfm: true, renderer });
131
+ try {
132
+ return parser.parse(source, { async: false }) as string;
133
+ } catch (cause) {
134
+ if (cause instanceof MarkdownPdfError) {
135
+ throw new MarkdownPdfError(
136
+ cause.code,
137
+ cause.message.split("\nPlease report this to")[0]?.trim() || "Markdown could not be rendered.",
138
+ cause.reason,
139
+ );
140
+ }
141
+ throw new MarkdownPdfError("bad_input", "Markdown could not be rendered.");
142
+ }
143
+ };
144
+
145
+ const validateCustomCss = (customCss: string): string => {
146
+ if (byteLength(customCss) > MARKDOWN_PDF_MAX_CUSTOM_CSS_BYTES) {
147
+ throw new MarkdownPdfError("invalid_css", "Custom CSS exceeds the 32 KiB limit.", "css_too_large");
148
+ }
149
+
150
+ let root: ReturnType<typeof postcss.parse>;
151
+ try {
152
+ root = postcss.parse(customCss);
153
+ } catch {
154
+ throw new MarkdownPdfError("invalid_css", "Custom CSS is not valid CSS.");
155
+ }
156
+
157
+ const resourceFunction = /(?:url|image-set|-webkit-image-set)\s*\(/iu;
158
+ root.walkAtRules((rule) => {
159
+ if (rule.name.toLowerCase() === "import" || resourceFunction.test(rule.params)) {
160
+ throw new MarkdownPdfError("external_asset_unsupported", "Custom CSS cannot load external resources.");
161
+ }
162
+ });
163
+ root.walkDecls((declaration) => {
164
+ if (resourceFunction.test(declaration.value)) {
165
+ throw new MarkdownPdfError("external_asset_unsupported", "Custom CSS cannot load external resources.");
166
+ }
167
+ });
168
+
169
+ // Keep the CSS inside its style element even when input contains an HTML
170
+ // end tag. The backslash is valid CSS escaping but no longer an HTML token.
171
+ return root.toString().replace(/<\/style/giu, "<\\/style");
172
+ };
173
+
174
+ export const buildMarkdownPdfHtml = (input: RenderMarkdownToPdfInput): string => {
175
+ if (typeof input.markdown !== "string" || !input.markdown.trim()) {
176
+ throw new MarkdownPdfError("bad_input", "Markdown must not be empty.", "markdown_empty");
177
+ }
178
+ const templateId = input.templateId;
179
+ if (templateId !== undefined && !MARKDOWN_PDF_TEMPLATE_IDS.includes(templateId)) {
180
+ throw new MarkdownPdfError("bad_input", "Unknown Markdown PDF template.", "unknown_template");
181
+ }
182
+
183
+ const suppliedCustomCss = input.customCss?.trim() ?? "";
184
+ const customCss = suppliedCustomCss ? validateCustomCss(input.customCss ?? "") : "";
185
+ const presetCss = templateId ? TEMPLATE_CSS[templateId] : customCss ? "" : TEMPLATE_CSS.document;
186
+ const stylesheet = `${presetCss}${presetCss && customCss ? `\n/* Custom CSS overrides */\n` : ""}${customCss}`;
187
+ const content = renderMarkdown(input.markdown);
188
+ return `<!doctype html>
189
+ <html lang="en">
190
+ <head>
191
+ <meta charset="utf-8">
192
+ <meta name="viewport" content="width=device-width, initial-scale=1">
193
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; base-uri 'none'; connect-src 'none'; font-src 'none'; form-action 'none'; frame-src 'none'; img-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'unsafe-inline'">
194
+ <style>${stylesheet}</style>
195
+ </head>
196
+ <body><main class="markdown-document">${content}</main></body>
197
+ </html>`;
198
+ };
199
+
200
+ export const renderMarkdownToPdfWithConfig = (
201
+ input: RenderMarkdownToPdfInput,
202
+ config: GotenbergConfig,
203
+ options: RenderMarkdownToPdfOptions = {},
204
+ ): Promise<RenderHtmlToPdfResult> => renderHtmlToPdfWithConfig({ html: buildMarkdownPdfHtml(input) }, config, options);
205
+
206
+ export const renderMarkdownToPdf = async (
207
+ input: RenderMarkdownToPdfInput,
208
+ options: RenderMarkdownToPdfOptions = {},
209
+ ): Promise<RenderHtmlToPdfResult> => renderMarkdownToPdfWithConfig(input, await getGotenbergConfig(), options);
@@ -0,0 +1,122 @@
1
+ import { type LiquidTemplateOptions, renderLiquidTemplate } from "../../shared/template-rendering";
2
+ import {
3
+ type GotenbergConfig,
4
+ GotenbergRenderError,
5
+ type RenderHtmlToPdfOptions,
6
+ type RenderHtmlToPdfResult,
7
+ renderHtmlToPdf,
8
+ renderHtmlToPdfWithConfig,
9
+ } from "./gotenberg";
10
+
11
+ export type TemplatePdfPreviewPhase = "template" | "pdf";
12
+
13
+ export type TemplatePdfPreviewError = {
14
+ phase: TemplatePdfPreviewPhase;
15
+ message: string;
16
+ status: number;
17
+ code?: string;
18
+ };
19
+
20
+ export type TemplatePdfPreviewResult =
21
+ | {
22
+ ok: true;
23
+ html: string;
24
+ headerHtml: string | null;
25
+ footerHtml: string | null;
26
+ pageCss: string | null;
27
+ pdf: RenderHtmlToPdfResult;
28
+ }
29
+ | {
30
+ ok: false;
31
+ error: TemplatePdfPreviewError;
32
+ };
33
+
34
+ export type RenderTemplatePdfPreviewInput = {
35
+ htmlTemplate: string;
36
+ headerHtmlTemplate?: string | null;
37
+ footerHtmlTemplate?: string | null;
38
+ pageCssTemplate?: string | null;
39
+ data: Record<string, unknown>;
40
+ filters?: LiquidTemplateOptions["filters"];
41
+ filename?: string;
42
+ };
43
+
44
+ export type RenderTemplatePdfPreviewOptions = RenderHtmlToPdfOptions & {
45
+ config?: GotenbergConfig;
46
+ };
47
+
48
+ const pdfErrorStatus = (error: GotenbergRenderError): number => (error.code === "not_configured" ? 400 : 502);
49
+
50
+ const renderPart = (
51
+ template: string | null | undefined,
52
+ data: Record<string, unknown>,
53
+ filters: LiquidTemplateOptions["filters"],
54
+ ): string | null => {
55
+ const source = template?.trim();
56
+ return source ? renderLiquidTemplate(source, data, { filters }) : null;
57
+ };
58
+
59
+ const injectPageCss = (html: string, pageCss: string | null): string => {
60
+ if (!pageCss?.trim()) return html;
61
+ const style = `<style>\n${pageCss}\n</style>`;
62
+ return /<\/head>/i.test(html)
63
+ ? html.replace(/<\/head>/i, `${style}\n</head>`)
64
+ : `<!doctype html><html><head>${style}</head><body>${html}</body></html>`;
65
+ };
66
+
67
+ export const renderTemplatePdfPreview = async (
68
+ input: RenderTemplatePdfPreviewInput,
69
+ options: RenderTemplatePdfPreviewOptions = {},
70
+ ): Promise<TemplatePdfPreviewResult> => {
71
+ let html: string;
72
+ let headerHtml: string | null;
73
+ let footerHtml: string | null;
74
+ let pageCss: string | null;
75
+ try {
76
+ pageCss = renderPart(input.pageCssTemplate, input.data, input.filters);
77
+ html = injectPageCss(renderLiquidTemplate(input.htmlTemplate, input.data, { filters: input.filters }), pageCss);
78
+ headerHtml = renderPart(input.headerHtmlTemplate, input.data, input.filters);
79
+ footerHtml = renderPart(input.footerHtmlTemplate, input.data, input.filters);
80
+ } catch (error) {
81
+ return {
82
+ ok: false,
83
+ error: {
84
+ phase: "template",
85
+ message: error instanceof Error ? error.message : "Template rendering failed.",
86
+ status: 400,
87
+ },
88
+ };
89
+ }
90
+
91
+ try {
92
+ const pdfInput = { html, headerHtml, footerHtml, filename: input.filename };
93
+ return {
94
+ ok: true,
95
+ html,
96
+ headerHtml,
97
+ footerHtml,
98
+ pageCss,
99
+ pdf: options.config ? await renderHtmlToPdfWithConfig(pdfInput, options.config, options) : await renderHtmlToPdf(pdfInput, options),
100
+ };
101
+ } catch (error) {
102
+ if (error instanceof GotenbergRenderError) {
103
+ return {
104
+ ok: false,
105
+ error: {
106
+ phase: "pdf",
107
+ message: error.message,
108
+ status: pdfErrorStatus(error),
109
+ code: error.code,
110
+ },
111
+ };
112
+ }
113
+ return {
114
+ ok: false,
115
+ error: {
116
+ phase: "pdf",
117
+ message: error instanceof Error ? error.message : "PDF rendering failed.",
118
+ status: 500,
119
+ },
120
+ };
121
+ }
122
+ };
@@ -0,0 +1,76 @@
1
+ /** Convert a JS string array to a Postgres TEXT[] literal (Bun sql can't serialize empty arrays). */
2
+ export const toPgTextArray = (values: string[] | null | undefined): string => {
3
+ if (!Array.isArray(values) || values.length === 0) return "{}";
4
+ return `{${values.map((value) => `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`).join(",")}}`;
5
+ };
6
+
7
+ /** Convert UUID strings into a Postgres UUID[] literal for `ANY(...)`/`ALL(...)` filters. */
8
+ export const toPgUuidArray = (values: string[] | null | undefined): string => {
9
+ if (!Array.isArray(values) || values.length === 0) return "{}";
10
+ return `{${values.join(",")}}`;
11
+ };
12
+
13
+ /** Convert 32-bit integers into a Postgres INT[] literal. */
14
+ export const toPgIntArray = (values: number[] | null | undefined): string => {
15
+ if (!Array.isArray(values) || values.length === 0) return "{}";
16
+ if (values.some((value) => !Number.isInteger(value) || value < -2_147_483_648 || value > 2_147_483_647)) {
17
+ throw new RangeError("Postgres INT[] values must be 32-bit integers");
18
+ }
19
+ return `{${values.join(",")}}`;
20
+ };
21
+
22
+ /** Escape a user string for safe use inside a LIKE/ILIKE pattern with `ESCAPE '\'`. */
23
+ export const escapeLikePattern = (value: string): string => value.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
24
+
25
+ /** Normalize a Postgres JSON/JSONB value that may come back as a parsed value or a JSON string. */
26
+ export const parsePgJsonValue = (value: unknown): unknown => {
27
+ if (value == null || typeof value !== "string") return value;
28
+
29
+ const trimmed = value.trim();
30
+ if (!trimmed) return null;
31
+
32
+ try {
33
+ return JSON.parse(trimmed);
34
+ } catch {
35
+ return value;
36
+ }
37
+ };
38
+
39
+ /** Normalize a Postgres JSON/JSONB object value to a plain record. */
40
+ export const parsePgJsonRecord = (value: unknown): Record<string, unknown> | null => {
41
+ const parsed = parsePgJsonValue(value);
42
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
43
+ return parsed as Record<string, unknown>;
44
+ };
45
+
46
+ /**
47
+ * Classify a thrown Postgres error. Use at service boundaries to turn
48
+ * unique-constraint violations into typed 409 results instead of bubbling
49
+ * up raw DB errors to API clients.
50
+ *
51
+ * Two driver shapes coexist in this repo:
52
+ * - postgres.js: SQLSTATE on `code`, constraint name on `constraint_name`
53
+ * - bun.sql: SQLSTATE on `errno`, constraint name on `constraint`
54
+ *
55
+ * Checking only `e.code` silently fails on Bun (the Wave-1.1 migration
56
+ * idempotence bug had the same root cause). Treat either field carrying
57
+ * "23505" as a unique violation so the helper works regardless of which
58
+ * driver instantiated the error.
59
+ */
60
+ export type PgError = {
61
+ code?: string;
62
+ errno?: string;
63
+ constraint?: string;
64
+ constraint_name?: string;
65
+ detail?: string;
66
+ message?: string;
67
+ };
68
+
69
+ export const isUniqueViolation = (error: unknown, constraintName?: string): boolean => {
70
+ if (!error || typeof error !== "object") return false;
71
+ const e = error as PgError;
72
+ const sqlstate = e.code === "23505" || e.errno === "23505";
73
+ if (!sqlstate) return false;
74
+ if (!constraintName) return true;
75
+ return e.constraint === constraintName || e.constraint_name === constraintName;
76
+ };
@@ -0,0 +1,27 @@
1
+ import * as ipaAuth from "../ipa/auth";
2
+ import * as ipaGroups from "../ipa/groups";
3
+ import * as ipaSync from "../ipa/sync";
4
+ import * as ipaUsers from "../ipa/users";
5
+ import { local } from "./local";
6
+
7
+ export const ipa = {
8
+ auth: ipaAuth,
9
+ users: {
10
+ ...ipaUsers,
11
+ create: ipaUsers.addIpa,
12
+ update: ipaUsers.updateProfile,
13
+ remove: ipaUsers.deleteUser,
14
+ },
15
+ groups: {
16
+ ...ipaGroups,
17
+ remove: ipaGroups.del,
18
+ },
19
+ sync: {
20
+ ...ipaSync,
21
+ run: ipaSync.syncFromIpa,
22
+ user: ipaSync.syncUser,
23
+ },
24
+ } as const;
25
+
26
+ export { local };
27
+ export const providers = { ipa, local } as const;
@@ -0,0 +1,51 @@
1
+ import { redis } from "bun";
2
+
3
+ export const createMagicLinkToken = async (params: {
4
+ email: string;
5
+ category?: "guest" | "login";
6
+ ttlSeconds?: number;
7
+ }): Promise<string> => {
8
+ const token = crypto.randomUUID();
9
+ await redis.set(
10
+ `email-login:${token}`,
11
+ JSON.stringify({ email: params.email, category: params.category }),
12
+ "EX",
13
+ params.ttlSeconds ?? 300,
14
+ );
15
+ return token;
16
+ };
17
+
18
+ export const consumeMagicLinkToken = async (token: string): Promise<{ email: string; category?: "guest" | "login" } | null> => {
19
+ const raw = await redis.getdel(`email-login:${token}`);
20
+ if (!raw) return null;
21
+ return JSON.parse(raw) as { email: string; category?: "guest" | "login" };
22
+ };
23
+
24
+ type PasswordResetPayload = {
25
+ userId: string;
26
+ uid: string;
27
+ email: string;
28
+ };
29
+
30
+ const passwordResetTokenKey = (token: string) => `password-reset:${token}`;
31
+
32
+ export const createPasswordResetToken = async (params: PasswordResetPayload & { ttlSeconds?: number }): Promise<string> => {
33
+ const token = crypto.randomUUID();
34
+ await redis.set(
35
+ passwordResetTokenKey(token),
36
+ JSON.stringify({
37
+ userId: params.userId,
38
+ uid: params.uid,
39
+ email: params.email,
40
+ }),
41
+ "EX",
42
+ params.ttlSeconds ?? 900,
43
+ );
44
+ return token;
45
+ };
46
+
47
+ export const consumePasswordResetToken = async (token: string): Promise<PasswordResetPayload | null> => {
48
+ const raw = await redis.getdel(passwordResetTokenKey(token));
49
+ if (!raw) return null;
50
+ return JSON.parse(raw) as PasswordResetPayload;
51
+ };
@@ -0,0 +1,4 @@
1
+ import * as auth from "./auth";
2
+ import * as users from "./users";
3
+
4
+ export const local = { auth, users } as const;