create-ailk 0.24.0 → 0.25.1

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 (599) hide show
  1. package/README.md +42 -20
  2. package/component-catalog.json +3 -3
  3. package/component-catalog.md +3 -3
  4. package/dist/add-module.js +4 -2
  5. package/dist/add-route.d.ts +12 -1
  6. package/dist/add-route.js +31 -3
  7. package/dist/cli.js +12 -7
  8. package/dist/derive-architecture.d.ts +3 -4
  9. package/dist/derive-architecture.js +65 -29
  10. package/dist/lib/design-checks.d.ts +9 -4
  11. package/dist/lib/design-checks.js +13 -7
  12. package/dist/lib/extract-module-bundle.js +29 -2
  13. package/dist/lib/fetch-template-snapshot.d.ts +1 -1
  14. package/dist/lib/fetch-template-snapshot.js +7 -3
  15. package/dist/lib/import-graph.d.ts +1 -1
  16. package/dist/lib/import-graph.js +4 -4
  17. package/dist/lib/module-license-gate.d.ts +12 -1
  18. package/dist/lib/module-license-gate.js +34 -12
  19. package/dist/module-architecture.d.ts +20 -3
  20. package/dist/module-architecture.js +117 -550
  21. package/dist/paid-sections.d.ts +25 -1
  22. package/dist/paid-sections.js +88 -3
  23. package/dist/surfaces.js +10 -2
  24. package/dist/template-record.d.ts +14 -4
  25. package/dist/template-record.js +23 -5
  26. package/dist/topology.d.ts +16 -3
  27. package/dist/topology.js +31 -19
  28. package/dist/update.d.ts +10 -1
  29. package/dist/update.js +540 -16
  30. package/package.json +3 -3
  31. package/release-scope.json +1 -1
  32. package/templates/.claude/cloudparity-repo-hooks.json +5 -0
  33. package/templates/.claude/hooks/template-guard.sh +137 -0
  34. package/templates/.claude/rules/extending.md +106 -0
  35. package/templates/.claude/rules/loop-contract.md +2 -2
  36. package/templates/.claude/settings.json +11 -0
  37. package/templates/.env.example +5 -5
  38. package/templates/.github/workflows/ci.yml +7 -4
  39. package/templates/CLAUDE.md +11 -1
  40. package/templates/CONVENTIONS.md +8 -10
  41. package/templates/README.md +14 -13
  42. package/templates/apps/api/.env.example +2 -2
  43. package/templates/apps/api/CLAUDE.md +102 -76
  44. package/templates/apps/api/jest.config.cjs +15 -5
  45. package/templates/apps/api/package.json +16 -20
  46. package/templates/apps/api/src/__tests__/trusted-origins.server.test.ts +2 -2
  47. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +4 -4
  48. package/templates/apps/api/src/index.ts +1 -1
  49. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -3
  50. package/templates/apps/api/src/modules/commerce.ts +10 -0
  51. package/templates/apps/api/src/modules/content-write.ts +10 -0
  52. package/templates/apps/api/src/modules/multi-tenant.ts +10 -0
  53. package/templates/apps/api/src/modules/scheduling.ts +10 -0
  54. package/templates/apps/api/src/modules/signal-collectors.ts +10 -0
  55. package/templates/apps/api/src/modules/workspace.ts +10 -0
  56. package/templates/apps/api/src/server.ts +117 -1882
  57. package/templates/apps/api/vercel.json +2 -2
  58. package/templates/apps/mcp/CLAUDE.md +67 -20
  59. package/templates/apps/mcp/__tests__/app-entry.test.ts +4 -254
  60. package/templates/apps/mcp/__tests__/boot-file.test.ts +92 -0
  61. package/templates/apps/mcp/jest.config.cjs +6 -1
  62. package/templates/apps/mcp/package.json +5 -13
  63. package/templates/apps/mcp/src/app.ts +8 -7
  64. package/templates/apps/mcp/src/index.ts +6 -34
  65. package/templates/apps/mcp/src/lib/paid-tools.generated.ts +10 -0
  66. package/templates/apps/mcp/src/lib/topology.generated.ts +2 -2
  67. package/templates/apps/mcp/src/site.ts +12 -0
  68. package/templates/apps/web/.env.example +1 -1
  69. package/templates/apps/web/CLAUDE.md +10 -3
  70. package/templates/apps/web/app/.well-known/mcp.json/route.ts +9 -3
  71. package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +1 -1
  72. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +22 -1
  73. package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +7 -3
  74. package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +43 -0
  75. package/templates/apps/web/app/[locale]/pricing/page.tsx +7 -3
  76. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +222 -0
  77. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +10 -3
  78. package/templates/apps/web/app/api/revalidate/route.ts +3 -3
  79. package/templates/apps/web/auth.ts +2 -2
  80. package/templates/apps/web/jest.config.cjs +1 -0
  81. package/templates/apps/web/lib/auth-email.ts +1 -1
  82. package/templates/apps/web/lib/relay.ts +2 -2
  83. package/templates/apps/web/lib/release-scope.ts +47 -0
  84. package/templates/apps/web/lib/server-api.ts +1 -1
  85. package/templates/apps/web/src/lib/commerce-claim.ts +1 -1
  86. package/templates/customizations.yaml +37 -0
  87. package/templates/database/prisma.config.ts +10 -14
  88. package/templates/database/seed-test.ts +2 -2
  89. package/templates/database/seed.ts +5 -5
  90. package/templates/package.json +12 -6
  91. package/templates/pnpm-workspace.yaml +0 -1
  92. package/templates/project.yaml +16 -8
  93. package/templates/tools/jest/reporters.cjs +65 -0
  94. package/templates/tools/lint-architecture.ts +553 -7
  95. package/templates/tsconfig.json +1 -2
  96. package/versions.json +48 -35
  97. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +0 -409
  98. package/templates/apps/api/src/__tests__/factories/agent-caller.ts +0 -102
  99. package/templates/apps/api/src/__tests__/factories/content-fixture.ts +0 -138
  100. package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +0 -441
  101. package/templates/apps/api/src/__tests__/factories/index.ts +0 -70
  102. package/templates/apps/api/src/__tests__/factories/lead-fixture.ts +0 -82
  103. package/templates/apps/api/src/__tests__/leads-forwarded-rate-limit.test.ts +0 -135
  104. package/templates/apps/api/src/__tests__/services/enrichment-shim.test.ts +0 -126
  105. package/templates/apps/api/src/__tests__/stripe-boot.test.ts +0 -132
  106. package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +0 -362
  107. package/templates/apps/api/src/bin/__tests__/rate-limit-probe.test.ts +0 -184
  108. package/templates/apps/api/src/bin/abandon-sweep.ts +0 -42
  109. package/templates/apps/api/src/bin/rate-limit-probe.ts +0 -53
  110. package/templates/apps/api/src/bin/spokes.ts +0 -198
  111. package/templates/apps/api/src/bin/stripe-connections-rekey.ts +0 -45
  112. package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +0 -196
  113. package/templates/apps/api/src/config/__tests__/index.test.ts +0 -233
  114. package/templates/apps/api/src/config/__tests__/modules.test.ts +0 -76
  115. package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +0 -185
  116. package/templates/apps/api/src/config/__tests__/trusted-origins.test.ts +0 -238
  117. package/templates/apps/api/src/config/index.ts +0 -596
  118. package/templates/apps/api/src/config/modules.ts +0 -83
  119. package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +0 -581
  120. package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +0 -143
  121. package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +0 -237
  122. package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +0 -353
  123. package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +0 -201
  124. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +0 -224
  125. package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +0 -120
  126. package/templates/apps/api/src/lib/__tests__/errors.test.ts +0 -94
  127. package/templates/apps/api/src/lib/__tests__/file-fallback.test.ts +0 -144
  128. package/templates/apps/api/src/lib/__tests__/fixtures/HomePage/home.mdx +0 -8
  129. package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +0 -149
  130. package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +0 -116
  131. package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +0 -209
  132. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +0 -257
  133. package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +0 -96
  134. package/templates/apps/api/src/lib/__tests__/outbound-http.test.ts +0 -125
  135. package/templates/apps/api/src/lib/__tests__/prisma.test.ts +0 -165
  136. package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +0 -317
  137. package/templates/apps/api/src/lib/__tests__/require-paid-tier.test.ts +0 -129
  138. package/templates/apps/api/src/lib/__tests__/site.test.ts +0 -308
  139. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +0 -89
  140. package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +0 -202
  141. package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +0 -217
  142. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -82
  143. package/templates/apps/api/src/lib/agent-store.ts +0 -613
  144. package/templates/apps/api/src/lib/auth-email-budget.ts +0 -161
  145. package/templates/apps/api/src/lib/consent-store.ts +0 -363
  146. package/templates/apps/api/src/lib/content-adapter.ts +0 -112
  147. package/templates/apps/api/src/lib/deliverable-asset-store.ts +0 -96
  148. package/templates/apps/api/src/lib/error-serializer.ts +0 -164
  149. package/templates/apps/api/src/lib/errors.ts +0 -95
  150. package/templates/apps/api/src/lib/file-fallback.ts +0 -87
  151. package/templates/apps/api/src/lib/flow-completion-store.ts +0 -136
  152. package/templates/apps/api/src/lib/hub-sign-in-store.ts +0 -455
  153. package/templates/apps/api/src/lib/lead-capture-store.ts +0 -122
  154. package/templates/apps/api/src/lib/lead-delivery.ts +0 -52
  155. package/templates/apps/api/src/lib/license-key-store.ts +0 -45
  156. package/templates/apps/api/src/lib/locales.ts +0 -201
  157. package/templates/apps/api/src/lib/outbound-http.ts +0 -127
  158. package/templates/apps/api/src/lib/prisma.ts +0 -21
  159. package/templates/apps/api/src/lib/proposal-paid-store.ts +0 -66
  160. package/templates/apps/api/src/lib/rate-limit-probe.ts +0 -165
  161. package/templates/apps/api/src/lib/rate-limit-store.ts +0 -142
  162. package/templates/apps/api/src/lib/require-commerce-claim.ts +0 -250
  163. package/templates/apps/api/src/lib/require-paid-tier.ts +0 -53
  164. package/templates/apps/api/src/lib/site.ts +0 -257
  165. package/templates/apps/api/src/lib/slice-load.ts +0 -90
  166. package/templates/apps/api/src/lib/spoke-store.ts +0 -180
  167. package/templates/apps/api/src/lib/stripe.ts +0 -321
  168. package/templates/apps/api/src/lib/webhook-idempotency.ts +0 -156
  169. package/templates/apps/api/src/middleware/__tests__/adversarial/bearer-token-format.test.ts +0 -111
  170. package/templates/apps/api/src/middleware/__tests__/adversarial/cookie-or-bearer-precedence.test.ts +0 -172
  171. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-bearer-skip.test.ts +0 -119
  172. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-cookie-path.test.ts +0 -138
  173. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-trusted-origins.test.ts +0 -142
  174. package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +0 -155
  175. package/templates/apps/api/src/middleware/__tests__/adversarial/session-timeout.test.ts +0 -139
  176. package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +0 -562
  177. package/templates/apps/api/src/middleware/__tests__/auth.both-present-precedence.test.ts +0 -76
  178. package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +0 -220
  179. package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +0 -97
  180. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +0 -557
  181. package/templates/apps/api/src/middleware/__tests__/auth.neither-present.test.ts +0 -72
  182. package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +0 -140
  183. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +0 -721
  184. package/templates/apps/api/src/middleware/__tests__/rate-limit-cross-isolate.db.test.ts +0 -220
  185. package/templates/apps/api/src/middleware/__tests__/rate-limit-store.test.ts +0 -430
  186. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +0 -1409
  187. package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +0 -174
  188. package/templates/apps/api/src/middleware/auth.ts +0 -864
  189. package/templates/apps/api/src/middleware/default-tenant.ts +0 -166
  190. package/templates/apps/api/src/middleware/rate-limit.ts +0 -983
  191. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +0 -391
  192. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +0 -182
  193. package/templates/apps/api/src/openapi/index.ts +0 -18
  194. package/templates/apps/api/src/openapi/spec.ts +0 -2148
  195. package/templates/apps/api/src/openapi/surface-drift.ts +0 -195
  196. package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +0 -175
  197. package/templates/apps/api/src/routes/aeo/index.ts +0 -107
  198. package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +0 -196
  199. package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +0 -249
  200. package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +0 -532
  201. package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +0 -313
  202. package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +0 -413
  203. package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +0 -609
  204. package/templates/apps/api/src/routes/auth/hub-sign-in.ts +0 -242
  205. package/templates/apps/api/src/routes/auth/tokens.ts +0 -211
  206. package/templates/apps/api/src/routes/auth/transactional-email.ts +0 -301
  207. package/templates/apps/api/src/routes/auth/user.ts +0 -305
  208. package/templates/apps/api/src/routes/billing/plan-checkout.ts +0 -332
  209. package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +0 -178
  210. package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +0 -1426
  211. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +0 -448
  212. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -1742
  213. package/templates/apps/api/src/routes/checkout/index.ts +0 -34
  214. package/templates/apps/api/src/routes/checkout/session.ts +0 -171
  215. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -540
  216. package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +0 -386
  217. package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +0 -160
  218. package/templates/apps/api/src/routes/consent/account.ts +0 -268
  219. package/templates/apps/api/src/routes/consent/cookie.ts +0 -90
  220. package/templates/apps/api/src/routes/consent/index.ts +0 -26
  221. package/templates/apps/api/src/routes/consent/unsubscribe.ts +0 -127
  222. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +0 -853
  223. package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +0 -230
  224. package/templates/apps/api/src/routes/content/__tests__/list-types.test.ts +0 -44
  225. package/templates/apps/api/src/routes/content/__tests__/locale.test.ts +0 -118
  226. package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +0 -731
  227. package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +0 -753
  228. package/templates/apps/api/src/routes/content/__tests__/rate-limit.test.ts +0 -51
  229. package/templates/apps/api/src/routes/content/__tests__/search.test.ts +0 -450
  230. package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +0 -762
  231. package/templates/apps/api/src/routes/content/get-page.ts +0 -183
  232. package/templates/apps/api/src/routes/content/index.ts +0 -49
  233. package/templates/apps/api/src/routes/content/list-pages.ts +0 -74
  234. package/templates/apps/api/src/routes/content/list-types.ts +0 -19
  235. package/templates/apps/api/src/routes/content/offerings.ts +0 -184
  236. package/templates/apps/api/src/routes/content/pricing.ts +0 -176
  237. package/templates/apps/api/src/routes/content/rate-limit.ts +0 -22
  238. package/templates/apps/api/src/routes/content/search.ts +0 -100
  239. package/templates/apps/api/src/routes/content/shown-offerings.ts +0 -229
  240. package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +0 -88
  241. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +0 -171
  242. package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +0 -515
  243. package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +0 -392
  244. package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +0 -165
  245. package/templates/apps/api/src/routes/leads/index.ts +0 -639
  246. package/templates/apps/api/src/routes/leads/list.ts +0 -104
  247. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +0 -626
  248. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +0 -1268
  249. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +0 -847
  250. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +0 -491
  251. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +0 -476
  252. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +0 -872
  253. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +0 -1022
  254. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +0 -484
  255. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +0 -535
  256. package/templates/apps/api/src/routes/project-listings/index.ts +0 -164
  257. package/templates/apps/api/src/routes/project-listings/me.ts +0 -87
  258. package/templates/apps/api/src/routes/project-listings/patch-copy.ts +0 -224
  259. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +0 -102
  260. package/templates/apps/api/src/routes/project-listings/public.ts +0 -98
  261. package/templates/apps/api/src/routes/project-listings/respond.ts +0 -48
  262. package/templates/apps/api/src/routes/project-listings/resume-token.ts +0 -27
  263. package/templates/apps/api/src/routes/project-listings/start.ts +0 -140
  264. package/templates/apps/api/src/routes/project-listings/submit.ts +0 -130
  265. package/templates/apps/api/src/routes/project-listings/verify-email.ts +0 -150
  266. package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +0 -686
  267. package/templates/apps/api/src/routes/requests/confirm.ts +0 -601
  268. package/templates/apps/api/src/routes/requests/index.ts +0 -54
  269. package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +0 -306
  270. package/templates/apps/api/src/routes/test/auth/session.ts +0 -143
  271. package/templates/apps/api/src/routes/webhooks/README.md +0 -398
  272. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +0 -649
  273. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +0 -749
  274. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -449
  275. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -264
  276. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +0 -159
  277. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +0 -158
  278. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +0 -814
  279. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -301
  280. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-proposal-paid.test.ts +0 -369
  281. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -215
  282. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +0 -301
  283. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +0 -809
  284. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -1155
  285. package/templates/apps/api/src/routes/webhooks/index.ts +0 -141
  286. package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +0 -384
  287. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -2028
  288. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +0 -645
  289. package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +0 -132
  290. package/templates/apps/api/src/services/__tests__/audit-log.test.ts +0 -1028
  291. package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +0 -313
  292. package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +0 -503
  293. package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +0 -65
  294. package/templates/apps/api/src/services/__tests__/consent.test.ts +0 -299
  295. package/templates/apps/api/src/services/__tests__/default-tenant.test.ts +0 -147
  296. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +0 -877
  297. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +0 -379
  298. package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +0 -368
  299. package/templates/apps/api/src/services/__tests__/entitlements.test.ts +0 -142
  300. package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +0 -854
  301. package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +0 -234
  302. package/templates/apps/api/src/services/__tests__/lead-accept-user.test.ts +0 -251
  303. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +0 -2660
  304. package/templates/apps/api/src/services/__tests__/listing-config.test.ts +0 -166
  305. package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +0 -389
  306. package/templates/apps/api/src/services/__tests__/person.test.ts +0 -1204
  307. package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +0 -450
  308. package/templates/apps/api/src/services/__tests__/project-listings.counts.test.ts +0 -202
  309. package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +0 -151
  310. package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +0 -137
  311. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +0 -531
  312. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +0 -372
  313. package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +0 -348
  314. package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +0 -660
  315. package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +0 -941
  316. package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +0 -588
  317. package/templates/apps/api/src/services/__tests__/verified-email.test.ts +0 -78
  318. package/templates/apps/api/src/services/abandon-email.ts +0 -336
  319. package/templates/apps/api/src/services/aeo-score.ts +0 -325
  320. package/templates/apps/api/src/services/answer-distributions.ts +0 -138
  321. package/templates/apps/api/src/services/audit-log.ts +0 -961
  322. package/templates/apps/api/src/services/checkout-buyer.ts +0 -168
  323. package/templates/apps/api/src/services/consent.ts +0 -834
  324. package/templates/apps/api/src/services/default-tenant.ts +0 -416
  325. package/templates/apps/api/src/services/deliverable-fulfillment.ts +0 -750
  326. package/templates/apps/api/src/services/dunning-policy.ts +0 -294
  327. package/templates/apps/api/src/services/enrichment-shim.ts +0 -53
  328. package/templates/apps/api/src/services/entitlements.ts +0 -338
  329. package/templates/apps/api/src/services/hub-sign-in.ts +0 -472
  330. package/templates/apps/api/src/services/lead-accept-user.ts +0 -126
  331. package/templates/apps/api/src/services/lead-promotion.ts +0 -160
  332. package/templates/apps/api/src/services/lead-routing.ts +0 -1372
  333. package/templates/apps/api/src/services/leads-inbox.ts +0 -312
  334. package/templates/apps/api/src/services/listing-config.ts +0 -178
  335. package/templates/apps/api/src/services/magnet-completion.ts +0 -242
  336. package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +0 -348
  337. package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +0 -268
  338. package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +0 -975
  339. package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +0 -416
  340. package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +0 -400
  341. package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +0 -685
  342. package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +0 -231
  343. package/templates/apps/api/src/services/offerings/catalog.ts +0 -318
  344. package/templates/apps/api/src/services/offerings/checkout-authorization.ts +0 -403
  345. package/templates/apps/api/src/services/offerings/checkout-session-record.ts +0 -192
  346. package/templates/apps/api/src/services/offerings/connection.ts +0 -1083
  347. package/templates/apps/api/src/services/offerings/index.ts +0 -614
  348. package/templates/apps/api/src/services/offerings/source.ts +0 -140
  349. package/templates/apps/api/src/services/offerings/store.ts +0 -150
  350. package/templates/apps/api/src/services/owner-workspaces.ts +0 -132
  351. package/templates/apps/api/src/services/person.ts +0 -1211
  352. package/templates/apps/api/src/services/plan-offerings.ts +0 -125
  353. package/templates/apps/api/src/services/project-listing-resume-email.ts +0 -326
  354. package/templates/apps/api/src/services/project-listings.ts +0 -2085
  355. package/templates/apps/api/src/services/public-site-resolver.ts +0 -396
  356. package/templates/apps/api/src/services/request-confirm.ts +0 -698
  357. package/templates/apps/api/src/services/revalidation.ts +0 -235
  358. package/templates/apps/api/src/services/slack-notify.ts +0 -291
  359. package/templates/apps/api/src/services/usage-aggregation.ts +0 -516
  360. package/templates/apps/api/src/services/usage-credits.ts +0 -340
  361. package/templates/apps/api/src/services/verified-email.ts +0 -71
  362. package/templates/apps/mcp/__tests__/abuse/daily-cap.test.ts +0 -52
  363. package/templates/apps/mcp/__tests__/abuse/email-validator.test.ts +0 -46
  364. package/templates/apps/mcp/__tests__/abuse/guard.test.ts +0 -198
  365. package/templates/apps/mcp/__tests__/abuse/honeypot.test.ts +0 -36
  366. package/templates/apps/mcp/__tests__/abuse/user-agent-deny.test.ts +0 -54
  367. package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +0 -116
  368. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +0 -299
  369. package/templates/apps/mcp/__tests__/boot.test.ts +0 -280
  370. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +0 -359
  371. package/templates/apps/mcp/__tests__/config/modules.test.ts +0 -78
  372. package/templates/apps/mcp/__tests__/get_business_hours.test.ts +0 -82
  373. package/templates/apps/mcp/__tests__/get_faq.test.ts +0 -80
  374. package/templates/apps/mcp/__tests__/get_locations.test.ts +0 -65
  375. package/templates/apps/mcp/__tests__/get_offerings.test.ts +0 -557
  376. package/templates/apps/mcp/__tests__/get_pricing.test.ts +0 -524
  377. package/templates/apps/mcp/__tests__/get_site_info.test.ts +0 -81
  378. package/templates/apps/mcp/__tests__/http-transport.test.ts +0 -1232
  379. package/templates/apps/mcp/__tests__/index.test.ts +0 -195
  380. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +0 -191
  381. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +0 -531
  382. package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +0 -70
  383. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -162
  384. package/templates/apps/mcp/__tests__/lib/content-root.test.ts +0 -93
  385. package/templates/apps/mcp/__tests__/lib/errors.test.ts +0 -75
  386. package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +0 -180
  387. package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +0 -124
  388. package/templates/apps/mcp/__tests__/list_pages.test.ts +0 -268
  389. package/templates/apps/mcp/__tests__/locale.test.ts +0 -120
  390. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +0 -175
  391. package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +0 -101
  392. package/templates/apps/mcp/__tests__/rate-limit-write.test.ts +0 -189
  393. package/templates/apps/mcp/__tests__/read_content.test.ts +0 -511
  394. package/templates/apps/mcp/__tests__/request_callback.test.ts +0 -160
  395. package/templates/apps/mcp/__tests__/search_content.test.ts +0 -271
  396. package/templates/apps/mcp/__tests__/server.test.ts +0 -388
  397. package/templates/apps/mcp/__tests__/start_purchase.test.ts +0 -448
  398. package/templates/apps/mcp/__tests__/submit_lead.test.ts +0 -222
  399. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +0 -152
  400. package/templates/apps/mcp/__tests__/tools/create_project.test.ts +0 -243
  401. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +0 -451
  402. package/templates/apps/mcp/src/abuse/daily-cap.ts +0 -47
  403. package/templates/apps/mcp/src/abuse/email-validator.ts +0 -25
  404. package/templates/apps/mcp/src/abuse/honeypot.ts +0 -21
  405. package/templates/apps/mcp/src/abuse/index.ts +0 -349
  406. package/templates/apps/mcp/src/abuse/user-agent-deny.ts +0 -58
  407. package/templates/apps/mcp/src/boot.ts +0 -167
  408. package/templates/apps/mcp/src/config/modules.ts +0 -43
  409. package/templates/apps/mcp/src/http-transport.ts +0 -564
  410. package/templates/apps/mcp/src/lib/audit-single-page.ts +0 -308
  411. package/templates/apps/mcp/src/lib/commerce-claim.ts +0 -263
  412. package/templates/apps/mcp/src/lib/content-adapter.ts +0 -116
  413. package/templates/apps/mcp/src/lib/empty-content-adapter.ts +0 -140
  414. package/templates/apps/mcp/src/lib/errors.ts +0 -61
  415. package/templates/apps/mcp/src/lib/license-key-store.ts +0 -45
  416. package/templates/apps/mcp/src/lib/locales.ts +0 -171
  417. package/templates/apps/mcp/src/lib/prisma.ts +0 -39
  418. package/templates/apps/mcp/src/lib/public-workspace.ts +0 -133
  419. package/templates/apps/mcp/src/lib/resolve-locale.ts +0 -30
  420. package/templates/apps/mcp/src/lib/shown-offerings.ts +0 -105
  421. package/templates/apps/mcp/src/lib/site-config.ts +0 -53
  422. package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +0 -59
  423. package/templates/apps/mcp/src/request-context.ts +0 -52
  424. package/templates/apps/mcp/src/server.ts +0 -343
  425. package/templates/apps/mcp/src/serverless-app.ts +0 -88
  426. package/templates/apps/mcp/src/tools/audit.ts +0 -127
  427. package/templates/apps/mcp/src/tools/capture_lead.ts +0 -66
  428. package/templates/apps/mcp/src/tools/create_project.ts +0 -134
  429. package/templates/apps/mcp/src/tools/get_business_hours.ts +0 -68
  430. package/templates/apps/mcp/src/tools/get_faq.ts +0 -71
  431. package/templates/apps/mcp/src/tools/get_locations.ts +0 -88
  432. package/templates/apps/mcp/src/tools/get_offerings.ts +0 -96
  433. package/templates/apps/mcp/src/tools/get_pricing.ts +0 -94
  434. package/templates/apps/mcp/src/tools/get_site_info.ts +0 -86
  435. package/templates/apps/mcp/src/tools/index.ts +0 -138
  436. package/templates/apps/mcp/src/tools/list_pages.ts +0 -68
  437. package/templates/apps/mcp/src/tools/person_notes.ts +0 -25
  438. package/templates/apps/mcp/src/tools/proposals.ts +0 -27
  439. package/templates/apps/mcp/src/tools/purchase.ts +0 -205
  440. package/templates/apps/mcp/src/tools/read_content.ts +0 -81
  441. package/templates/apps/mcp/src/tools/request_callback.ts +0 -52
  442. package/templates/apps/mcp/src/tools/search_content.ts +0 -63
  443. package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +0 -54
  444. package/templates/apps/mcp/src/workspace-server.ts +0 -224
  445. package/templates/database/CHANGELOG.md +0 -981
  446. package/templates/database/LICENSE +0 -202
  447. package/templates/database/NOTICE +0 -2
  448. package/templates/database/README.md +0 -243
  449. package/templates/database/__tests__/agents.db.test.ts +0 -320
  450. package/templates/database/__tests__/appointment.db.test.ts +0 -106
  451. package/templates/database/__tests__/artifact-content-store.db.test.ts +0 -316
  452. package/templates/database/__tests__/composite-fence.db.test.ts +0 -206
  453. package/templates/database/__tests__/consent-ledger.db.test.ts +0 -345
  454. package/templates/database/__tests__/consent-source-checkout-migration.test.ts +0 -79
  455. package/templates/database/__tests__/fence-fan-out.db.test.ts +0 -715
  456. package/templates/database/__tests__/fixtures/appointment-create-probe.ts +0 -79
  457. package/templates/database/__tests__/fixtures/person-backfill.json +0 -264
  458. package/templates/database/__tests__/fixtures/person-contact-backfill.json +0 -182
  459. package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +0 -100
  460. package/templates/database/__tests__/flow-completion-migration.test.ts +0 -115
  461. package/templates/database/__tests__/flow-completion.db.test.ts +0 -136
  462. package/templates/database/__tests__/flow-workspace-key.db.test.ts +0 -263
  463. package/templates/database/__tests__/flows-listing-diagnostic.test.ts +0 -392
  464. package/templates/database/__tests__/flows-presets.test.ts +0 -173
  465. package/templates/database/__tests__/in-page-magnets-migration.test.ts +0 -161
  466. package/templates/database/__tests__/lead-idempotency-key.db.test.ts +0 -92
  467. package/templates/database/__tests__/member-area.db.test.ts +0 -257
  468. package/templates/database/__tests__/member-contract-migration.test.ts +0 -85
  469. package/templates/database/__tests__/member-contract.db.test.ts +0 -413
  470. package/templates/database/__tests__/offerings-checkout-session.db.test.ts +0 -270
  471. package/templates/database/__tests__/offerings-migration.test.ts +0 -197
  472. package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +0 -86
  473. package/templates/database/__tests__/offerings.db.test.ts +0 -303
  474. package/templates/database/__tests__/outbound-message-action.db.test.ts +0 -80
  475. package/templates/database/__tests__/person-contact.db.test.ts +0 -563
  476. package/templates/database/__tests__/person-notes.db.test.ts +0 -249
  477. package/templates/database/__tests__/person.db.test.ts +0 -586
  478. package/templates/database/__tests__/piece-kind-migration.test.ts +0 -67
  479. package/templates/database/__tests__/piece-kind.db.test.ts +0 -208
  480. package/templates/database/__tests__/prefixed-ids.db.test.ts +0 -222
  481. package/templates/database/__tests__/project-listing-consent-at.db.test.ts +0 -111
  482. package/templates/database/__tests__/seed.test.ts +0 -272
  483. package/templates/database/__tests__/spoke-registry.db.test.ts +0 -191
  484. package/templates/database/__tests__/tenancy.test.ts +0 -141
  485. package/templates/database/__tests__/trial-magnet-migration.test.ts +0 -186
  486. package/templates/database/__tests__/user-cascade.test.ts +0 -140
  487. package/templates/database/_gitignore +0 -8
  488. package/templates/database/auth/.gitkeep +0 -0
  489. package/templates/database/auth/schema.prisma +0 -641
  490. package/templates/database/config/.gitkeep +0 -0
  491. package/templates/database/config/schema.prisma +0 -441
  492. package/templates/database/content/.gitkeep +0 -0
  493. package/templates/database/content/schema.prisma +0 -339
  494. package/templates/database/eslint.config.mjs +0 -69
  495. package/templates/database/flows/listing-diagnostic.ts +0 -182
  496. package/templates/database/flows/presets.ts +0 -315
  497. package/templates/database/inbox/.gitkeep +0 -0
  498. package/templates/database/inbox/README.md +0 -3
  499. package/templates/database/inbox/__tests__/schema.test.ts +0 -44
  500. package/templates/database/inbox/schema.prisma +0 -1447
  501. package/templates/database/jest.config.cjs +0 -36
  502. package/templates/database/migrations/20260508000000_add_inbox_namespace/migration.sql +0 -328
  503. package/templates/database/migrations/20260513000000_better_auth_migration/migration.sql +0 -100
  504. package/templates/database/migrations/20260515000000_add_user_billing_fields/migration.sql +0 -28
  505. package/templates/database/migrations/20260516000000_add_user_tier_care_plan_seat/migration.sql +0 -25
  506. package/templates/database/migrations/20260518000000_add_telemetry_records/migration.sql +0 -42
  507. package/templates/database/migrations/20260525000000_theme_wiring/migration.sql +0 -67
  508. package/templates/database/migrations/20260710000000_retire_commercial_tier/migration.sql +0 -21
  509. package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +0 -201
  510. package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +0 -67
  511. package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +0 -46
  512. package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +0 -25
  513. package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +0 -49
  514. package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +0 -197
  515. package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +0 -32
  516. package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +0 -22
  517. package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +0 -151
  518. package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +0 -223
  519. package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +0 -22
  520. package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +0 -32
  521. package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +0 -40
  522. package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +0 -4
  523. package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +0 -2
  524. package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +0 -2
  525. package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +0 -29
  526. package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +0 -6
  527. package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +0 -39
  528. package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +0 -24
  529. package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +0 -3
  530. package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +0 -33
  531. package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +0 -30
  532. package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +0 -47
  533. package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +0 -16
  534. package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +0 -47
  535. package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +0 -10
  536. package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +0 -26
  537. package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +0 -90
  538. package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +0 -174
  539. package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +0 -14
  540. package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +0 -25
  541. package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +0 -32
  542. package/templates/database/migrations/20260911180000_consent_grants/migration.sql +0 -71
  543. package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +0 -30
  544. package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +0 -49
  545. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +0 -123
  546. package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +0 -15
  547. package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +0 -158
  548. package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +0 -40
  549. package/templates/database/migrations/20260918120000_add_appointment/migration.sql +0 -67
  550. package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +0 -70
  551. package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +0 -181
  552. package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +0 -172
  553. package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +0 -41
  554. package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +0 -185
  555. package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +0 -70
  556. package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +0 -57
  557. package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +0 -68
  558. package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +0 -1067
  559. package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +0 -210
  560. package/templates/database/migrations/20261003120000_spoke_registry/migration.sql +0 -80
  561. package/templates/database/migrations/20261003130000_scheduling_connection/migration.sql +0 -49
  562. package/templates/database/migrations/20261005120000_agents/migration.sql +0 -84
  563. package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +0 -35
  564. package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +0 -20
  565. package/templates/database/migrations/20261009120000_agent_grants/migration.sql +0 -33
  566. package/templates/database/migrations/20261010120000_person/migration.sql +0 -106
  567. package/templates/database/migrations/20261010121000_person_backfill/migration.sql +0 -132
  568. package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +0 -20
  569. package/templates/database/migrations/20261011120000_member_area/migration.sql +0 -158
  570. package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +0 -21
  571. package/templates/database/migrations/20261012120000_member_contract/migration.sql +0 -192
  572. package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +0 -20
  573. package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +0 -122
  574. package/templates/database/migrations/20261014120000_person_contact/migration.sql +0 -231
  575. package/templates/database/migrations/20261015120000_person_notes/migration.sql +0 -77
  576. package/templates/database/migrations/20261015120000_rate_limit_counters/migration.sql +0 -43
  577. package/templates/database/migrations/20261016120000_piece_kind/migration.sql +0 -95
  578. package/templates/database/migrations/20261017120000_outbound_message_action/migration.sql +0 -30
  579. package/templates/database/migrations/20261018120000_flow_completion/migration.sql +0 -52
  580. package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +0 -27
  581. package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +0 -146
  582. package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +0 -77
  583. package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +0 -45
  584. package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +0 -47
  585. package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +0 -16
  586. package/templates/database/migrations/20261025120000_trial_magnet/migration.sql +0 -28
  587. package/templates/database/migrations/20261026120000_trial_magnet_proof_line/migration.sql +0 -23
  588. package/templates/database/migrations/migration_lock.toml +0 -3
  589. package/templates/database/ops/.gitkeep +0 -0
  590. package/templates/database/ops/schema.prisma +0 -43
  591. package/templates/database/package.json +0 -89
  592. package/templates/database/schema.prisma +0 -71
  593. package/templates/database/scripts/db-generate-locked.sh +0 -80
  594. package/templates/database/scripts/person-backfill.sql +0 -132
  595. package/templates/database/tenancy.ts +0 -183
  596. package/templates/database/tsconfig.build.json +0 -16
  597. package/templates/database/tsconfig.flows.json +0 -15
  598. package/templates/database/tsconfig.json +0 -16
  599. package/templates/database/tsconfig.tenancy.json +0 -14
@@ -1,403 +0,0 @@
1
- /**
2
- * The Offerings checkout (#8931, spec
3
- * docs/specs/8931-offerings-checkout-authorization-server-side-spec.md D1,
4
- * D2, D5, D7, D9, D11; contract
5
- * docs/specs/8879-the-offerings-security-contract-key-spec.md A1–A9, A11,
6
- * A13, G1, G3, G6, K6).
7
- *
8
- * THE PATH. `resolveOfferingsCheckoutPath` decides, from the server-resolved
9
- * workspace and its Organization's connection alone, whether a checkout
10
- * request takes the Offerings path, the deployment's env-key path, or
11
- * neither. No request body field names a workspace or an Organization (A1).
12
- * A spoke's workspace with no connection is `unconnected`, never the env
13
- * key: that key sells the deployment's own products, not a spoke's (#9126).
14
- *
15
- * THE SALE. `startOfferingsCheckout` parses the lookup key by the one grammar
16
- * before any read (A3), refuses the hub's own plan keys at any period
17
- * (#9338 DD5), authorizes it at click time against org-enabled ∩
18
- * workspace-shown from the rows — never the snapshot (A4), and for a
19
- * workspace saved per Price only a shown Price (#9070 D5) — resolves it live
20
- * with the connection's own client, sells the authorized Price id in an
21
- * anonymous hosted session stamped `{ site, ailk_offering }` by the server
22
- * (A6, A7), and records the session before it answers; a failed record
23
- * expires the session (A11). Every miss is one `unknown`; every failure one
24
- * `unavailable`; there is never a snapshot fallback (A5), and this path never
25
- * sells on the environment key: an Organization that owns it checks out on the
26
- * env path until it has a stored connection (#9296, #8936).
27
- *
28
- * THE READ. `readOfferingsCheckoutSession` reads a recorded session with its
29
- * record's own connection, never a caller value (A13). Only a stored connection
30
- * records a session (#9296), so this read never runs on the environment key.
31
- *
32
- * LOGGING (D11, T6). Only `workspaceId` or `organizationId`, an 8-character
33
- * session-id prefix, a fixed `reason`, and an error's class name. Never a
34
- * lookup key, a Price or Product id, Stripe text, a URL, a full session id
35
- * or an error message.
36
- */
37
- import { logger } from "@working-theory/observability";
38
- import {
39
- OFFERINGS_NAME_MAX,
40
- isPlanPurchaseKey,
41
- lookupKeyPeriodSchema,
42
- parseLookupKey,
43
- } from "@working-theory/validation";
44
- import type { FastifyRequest } from "fastify";
45
-
46
- import {
47
- createCheckoutSessionForPrice,
48
- expireCheckoutSessionWith,
49
- findActivePriceByLookupKey,
50
- retrieveCheckoutSession,
51
- type RetrieveCheckoutSessionOutput,
52
- UnknownSessionError,
53
- } from "../../lib/stripe.js";
54
- import { resolvePublicWorkspace } from "../public-site-resolver.js";
55
-
56
- import { normalizeCatalog, normalizeText } from "./catalog.js";
57
- import {
58
- CheckoutSessionRecordError,
59
- readCheckoutSessionRecord,
60
- recordCheckoutSession,
61
- } from "./checkout-session-record.js";
62
- import {
63
- getOfferingsConnectionState,
64
- stripeClientForOrganization,
65
- } from "./connection.js";
66
- import { organizationIdOfWorkspace } from "./store.js";
67
-
68
- import { getSellableProductIds } from "./index.js";
69
-
70
- /** One seller's Offerings checkouts a minute, across every caller (D4). */
71
- export const OFFERINGS_CHECKOUT_PER_WORKSPACE_PER_MINUTE = 60;
72
-
73
- /** The resolver's own header; its presence is what makes a refusal a refused key (D2). */
74
- const SPOKE_KEY_HEADER = "x-ailk-spoke-key";
75
-
76
- export type OfferingsCheckoutPath =
77
- | { kind: "env" }
78
- | { kind: "refused" }
79
- | { kind: "unavailable" }
80
- | { kind: "unconnected"; workspaceId: string }
81
- | { kind: "offerings"; workspaceId: string; organizationId: string };
82
-
83
- /**
84
- * Which path a checkout request takes (D2). Rejects when a read rejects —
85
- * the resolver's, the Organization's or the connection summary's — so a
86
- * workspace it could not read never falls through to the env key.
87
- */
88
- export async function resolveOfferingsCheckoutPath(
89
- request: FastifyRequest,
90
- ): Promise<OfferingsCheckoutPath> {
91
- const site = await resolvePublicWorkspace(request);
92
- if (site.kind === "not-required") return { kind: "env" };
93
- if (site.kind === "unresolved") {
94
- // A keyless call on a hub that requires a key is also `spoke-refused`;
95
- // only a presented key that was refused is `refused` (A2, C6).
96
- return site.reason === "spoke-refused" &&
97
- request.headers[SPOKE_KEY_HEADER] !== undefined
98
- ? { kind: "refused" }
99
- : { kind: "env" };
100
- }
101
-
102
- const { workspaceId } = site;
103
- // A keyless call resolves only where no spoke key is required (a deployment
104
- // that is not a hub, or a hub's AILK_HUB_REQUIRE_SPOKE_KEY=off opt-out),
105
- // whose env key sells its own products; a presented key resolved only to
106
- // its spoke's workspace, which sells on its own connection or not at all
107
- // (#9126).
108
- const noConnection: OfferingsCheckoutPath =
109
- request.headers[SPOKE_KEY_HEADER] !== undefined
110
- ? { kind: "unconnected", workspaceId }
111
- : { kind: "env" };
112
- const organizationId = await organizationIdOfWorkspace(workspaceId);
113
- if (organizationId === null) return noConnection;
114
- const summary = await getOfferingsConnectionState(organizationId);
115
- if (summary === null) return noConnection;
116
- // Never the rows, which read a corrupt connection as an empty set (404).
117
- if (summary === "unreadable") return { kind: "unavailable" };
118
- // An Organization on the environment key never takes the Offerings path
119
- // (#9296): that path stamps `ailk_offering`, which the legacy webhook acks
120
- // `offerings_path` without recording and the per-Organization receiver takes
121
- // only for a stored connection's token, so a sale on the environment key
122
- // would be charged and never fulfilled. Its checkout stays on the env path
123
- // until environment-key fulfilment exists (#8936 stores the connection).
124
- if (summary === "environment") return noConnection;
125
- return { kind: "offerings", workspaceId, organizationId };
126
- }
127
-
128
- export interface OfferingsCheckoutInput {
129
- workspaceId: string;
130
- organizationId: string;
131
- productCode: string;
132
- successUrl: string;
133
- cancelUrl: string;
134
- quantity: number;
135
- }
136
-
137
- export type OfferingsCheckoutOutcome =
138
- | { kind: "created"; sessionId: string; url: string }
139
- | { kind: "unknown" }
140
- | { kind: "unavailable" };
141
-
142
- const UNKNOWN = { kind: "unknown" } as const;
143
- const UNAVAILABLE = { kind: "unavailable" } as const;
144
-
145
- function errName(err: unknown): string {
146
- return err instanceof Error ? err.name : "unknown";
147
- }
148
-
149
- /**
150
- * Authorize the key, sell its Price and record the session (D5–D7). Stops at
151
- * the first step that fails; never rejects.
152
- */
153
- export async function startOfferingsCheckout(
154
- input: OfferingsCheckoutInput,
155
- ): Promise<OfferingsCheckoutOutcome> {
156
- const { workspaceId, organizationId, productCode } = input;
157
- const unknown = (): OfferingsCheckoutOutcome => {
158
- logger.info(
159
- { workspaceId, reason: "offerings_unknown_product" },
160
- "[offerings] checkout key not sellable",
161
- );
162
- return UNKNOWN;
163
- };
164
- const unavailable = (
165
- reason: string,
166
- err?: unknown,
167
- ): OfferingsCheckoutOutcome => {
168
- logger.error(
169
- {
170
- workspaceId,
171
- reason,
172
- ...(err !== undefined && { errName: errName(err) }),
173
- },
174
- "[offerings] checkout unavailable",
175
- );
176
- return UNAVAILABLE;
177
- };
178
-
179
- // 1. Grammar first (A3): no read and no Stripe call for a key G1 refuses.
180
- const parsed = parseLookupKey(productCode);
181
- if (parsed === null) return unknown();
182
-
183
- // 1b. Never a hub plan (#9338 DD5): AILK's own Pro / Pro Plus, at any
184
- // period, for every Organization. A sale here would stamp `ailk_offering`,
185
- // which no billing path mints a licence for — the buyer would be charged
186
- // with no key. Same 404, same log reason, before any read.
187
- if (isPlanPurchaseKey(productCode)) return unknown();
188
-
189
- // 2. The connection's own client (K6) — before the rows, so a key that
190
- // cannot be used answers 502 whatever the rows hold.
191
- let connection: Awaited<ReturnType<typeof stripeClientForOrganization>>;
192
- try {
193
- connection = await stripeClientForOrganization(organizationId);
194
- } catch (err) {
195
- return unavailable("offerings_connection_unusable", err);
196
- }
197
- if (connection.kind !== "ok")
198
- return unavailable("offerings_connection_unusable");
199
- const { client, mode } = connection;
200
-
201
- // 3. The rows (A4): org-enabled ∩ workspace-shown, never the snapshot.
202
- let sellable: Awaited<ReturnType<typeof getSellableProductIds>>;
203
- try {
204
- sellable = await getSellableProductIds(workspaceId);
205
- } catch (err) {
206
- return unavailable("offerings_rows_failed", err);
207
- }
208
- if (sellable === null || sellable.productIds.length === 0) return unknown();
209
- if (sellable.organizationId !== organizationId) {
210
- return unavailable("offerings_rows_failed");
211
- }
212
-
213
- // 4. The live lookup (A4), on the connection's client.
214
- let price: Awaited<ReturnType<typeof findActivePriceByLookupKey>>;
215
- try {
216
- price = await findActivePriceByLookupKey(client, productCode);
217
- } catch (err) {
218
- return unavailable("offerings_price_lookup_failed", err);
219
- }
220
- if (price === null) return unknown();
221
-
222
- // 5. The intersection and the period (A4, G3): the Price's own Product
223
- // must be sellable here — and, for a workspace saved per Price, the Price
224
- // itself shown (#9070 D5) — and the one normalizer must keep this exact
225
- // Price under this key and period.
226
- const product: unknown = price.product;
227
- if (typeof product !== "object" || product === null) return unknown();
228
- const productId = (product as { id?: unknown }).id;
229
- if (
230
- typeof productId !== "string" ||
231
- !sellable.productIds.includes(productId) ||
232
- (sellable.priceIds !== null && !sellable.priceIds.includes(price.id))
233
- ) {
234
- return unknown();
235
- }
236
- const offering = normalizeCatalog({
237
- products: [product],
238
- prices: [price],
239
- }).offerings.find((o) => o.productId === productId);
240
- const authorized = offering?.prices.some(
241
- (p) =>
242
- p.priceId === price.id &&
243
- p.lookupKey === productCode &&
244
- p.period === parsed.period,
245
- );
246
- if (!authorized) return unknown();
247
-
248
- // 6. The sale (A6, A7): the authorized Price id, anonymous, stamped by the
249
- // server, its mode from the period step 5 checked against the Price.
250
- const metadata = { site: workspaceId, ailk_offering: productCode };
251
- let created: { sessionId: string; url: string };
252
- try {
253
- created = await createCheckoutSessionForPrice(client, {
254
- priceId: price.id,
255
- mode:
256
- parsed.period === lookupKeyPeriodSchema.enum.once
257
- ? "payment"
258
- : "subscription",
259
- successUrl: input.successUrl,
260
- cancelUrl: input.cancelUrl,
261
- quantity: input.quantity,
262
- allowPromotionCodes: true,
263
- customerCreation: "always",
264
- metadata,
265
- });
266
- } catch (err) {
267
- return unavailable("offerings_session_create_failed", err);
268
- }
269
-
270
- // 7. The record (A11), before the URL leaves the server. A failed record
271
- // expires the session on the same client: an unrecorded session is one the
272
- // webhook never fulfils, so no buyer may reach it.
273
- const sessionIdPrefix = created.sessionId.substring(0, 8);
274
- try {
275
- await recordCheckoutSession({
276
- sessionId: created.sessionId,
277
- workspaceId,
278
- organizationId,
279
- priceId: price.id,
280
- lookupKey: productCode,
281
- mode,
282
- });
283
- } catch (err) {
284
- logger.error(
285
- {
286
- workspaceId,
287
- sessionIdPrefix,
288
- reason: "offerings_record_failed",
289
- errName: errName(err),
290
- ...(err instanceof CheckoutSessionRecordError && {
291
- recordReason: err.reason,
292
- }),
293
- },
294
- "[offerings] checkout session record failed; expiring the session",
295
- );
296
- try {
297
- await expireCheckoutSessionWith(client, created.sessionId);
298
- } catch (expireErr) {
299
- logger.error(
300
- {
301
- workspaceId,
302
- sessionIdPrefix,
303
- reason: "offerings_expire_failed",
304
- errName: errName(expireErr),
305
- },
306
- "[offerings] unrecorded checkout session could not be expired",
307
- );
308
- }
309
- return UNAVAILABLE;
310
- }
311
-
312
- return { kind: "created", sessionId: created.sessionId, url: created.url };
313
- }
314
-
315
- export type OfferingsSessionRead =
316
- | { kind: "env" }
317
- | { kind: "read"; session: RetrieveCheckoutSessionOutput }
318
- | { kind: "unknown" }
319
- | { kind: "unavailable" };
320
-
321
- /**
322
- * Read a session the Offerings checkout recorded, with its record's own
323
- * connection (D9, A13). No record is `env`; a record that cannot be read, or
324
- * a connection that is gone, unusable or in another mode, is `unavailable` —
325
- * never the env path. Never rejects.
326
- */
327
- export async function readOfferingsCheckoutSession(
328
- sessionId: string,
329
- ): Promise<OfferingsSessionRead> {
330
- const sessionIdPrefix = sessionId.substring(0, 8);
331
-
332
- let record: Awaited<ReturnType<typeof readCheckoutSessionRecord>>;
333
- try {
334
- record = await readCheckoutSessionRecord(sessionId);
335
- } catch (err) {
336
- logger.error(
337
- {
338
- sessionIdPrefix,
339
- reason: "offerings_record_failed",
340
- errName: errName(err),
341
- },
342
- "[offerings] checkout session record read failed",
343
- );
344
- return UNAVAILABLE;
345
- }
346
- if (record === null) return { kind: "env" };
347
- if (record === "unreadable") return UNAVAILABLE;
348
-
349
- const { organizationId } = record;
350
- let connection: Awaited<ReturnType<typeof stripeClientForOrganization>>;
351
- try {
352
- connection = await stripeClientForOrganization(organizationId);
353
- } catch (err) {
354
- logger.error(
355
- {
356
- organizationId,
357
- reason: "offerings_connection_unusable",
358
- errName: errName(err),
359
- },
360
- "[offerings] checkout session read unavailable",
361
- );
362
- return UNAVAILABLE;
363
- }
364
- // C7: only the connection the session was made on, in that mode.
365
- if (connection.kind !== "ok" || connection.mode !== record.mode) {
366
- logger.error(
367
- { organizationId, reason: "offerings_connection_unusable" },
368
- "[offerings] checkout session read unavailable",
369
- );
370
- return UNAVAILABLE;
371
- }
372
-
373
- let session: RetrieveCheckoutSessionOutput;
374
- try {
375
- session = await retrieveCheckoutSession(
376
- { sessionId },
377
- { client: connection.client },
378
- );
379
- } catch (err) {
380
- if (err instanceof UnknownSessionError) return UNKNOWN;
381
- logger.error(
382
- {
383
- organizationId,
384
- reason: "offerings_session_read_failed",
385
- errName: errName(err),
386
- },
387
- "[offerings] checkout session read unavailable",
388
- );
389
- return UNAVAILABLE;
390
- }
391
-
392
- // The seller's Product names are Stripe text: served only as T1 bounds them.
393
- return {
394
- kind: "read",
395
- session: {
396
- ...session,
397
- lineItems: session.lineItems.map((line) => ({
398
- ...line,
399
- description: normalizeText(line.description, OFFERINGS_NAME_MAX) ?? "",
400
- })),
401
- },
402
- };
403
- }
@@ -1,192 +0,0 @@
1
- /**
2
- * The Offerings Checkout session record (#8930, spec
3
- * docs/specs/8930-the-offerings-checkout-session-record-spec.md D4, D5, D7;
4
- * contract docs/specs/8879-the-offerings-security-contract-key-spec.md A11,
5
- * W5, A13) — the ONE file naming the OfferingsCheckoutSession delegate.
6
- *
7
- * WRITE ONCE. The checkout route records `{ sessionId, workspaceId,
8
- * organizationId, priceId, lookupKey, mode }` before it answers, and the
9
- * per-Organization webhook and the session read act only on a session
10
- * recorded here — never on metadata alone. One `create` and one `findUnique`
11
- * by Checkout Session id; nothing here updates, upserts, deletes or lists, so
12
- * no record is rebound to another workspace and none is enumerated.
13
- *
14
- * `null` means one thing: the database holds no row for that id (or the id
15
- * has a shape no row can hold). A database error rejects `read_failed`, and a
16
- * row that fails the parse answers `"unreadable"` — neither is ever `null`,
17
- * because a caller that read `null` would treat a paid session as foreign.
18
- *
19
- * ERRORS AND LOGGING (D7, contract T6). A `CheckoutSessionRecordError` carries
20
- * a fixed message per reason and no `cause`, so no Prisma or Postgres text — a
21
- * CHECK violation prints the failing row — reaches a log line or an HTTP
22
- * error. Logs carry only a fixed `reason` (and the workspace on a failed
23
- * write); never a session id, Price id, lookup key or mode.
24
- */
25
- import { idRegex } from "@working-theory/ids";
26
- import { logger } from "@working-theory/observability";
27
- import {
28
- offeringsStripeIdSchema,
29
- parseLookupKey,
30
- } from "@working-theory/validation";
31
- import { z } from "zod";
32
-
33
- import { prisma } from "../../lib/prisma.js";
34
- import { isUniqueViolation } from "../default-tenant.js";
35
-
36
- import { organizationIdOfWorkspace } from "./store.js";
37
-
38
- export type CheckoutSessionMode = "test" | "live";
39
-
40
- export interface CheckoutSessionRecordInput {
41
- sessionId: string;
42
- workspaceId: string;
43
- organizationId: string;
44
- priceId: string;
45
- lookupKey: string;
46
- mode: CheckoutSessionMode;
47
- }
48
-
49
- export interface CheckoutSessionRecord extends CheckoutSessionRecordInput {
50
- createdAt: Date;
51
- }
52
-
53
- export type CheckoutSessionRecordFailure =
54
- | "invalid"
55
- | "workspace_mismatch"
56
- | "conflict"
57
- | "write_failed"
58
- | "read_failed";
59
-
60
- const FAILURE_MESSAGES: Record<CheckoutSessionRecordFailure, string> = {
61
- invalid: "The Checkout session record is malformed",
62
- workspace_mismatch:
63
- "The Checkout session record names a workspace outside its Organization",
64
- conflict: "The Checkout session is already recorded",
65
- write_failed: "The Checkout session record could not be written",
66
- read_failed: "The Checkout session record could not be read",
67
- };
68
-
69
- export class CheckoutSessionRecordError extends Error {
70
- readonly reason: CheckoutSessionRecordFailure;
71
-
72
- constructor(reason: CheckoutSessionRecordFailure) {
73
- super(FAILURE_MESSAGES[reason]);
74
- this.name = "CheckoutSessionRecordError";
75
- this.reason = reason;
76
- }
77
- }
78
-
79
- /** A Checkout Session id: the migration's `_session_id_format` CHECK body. */
80
- const SESSION_ID_PATTERN = /^cs_(test|live)_[A-Za-z0-9]{1,250}$/;
81
-
82
- /** Every column the migration CHECKs, mirrored so a refused write never reaches Postgres. */
83
- const recordShape = {
84
- sessionId: z.string().regex(SESSION_ID_PATTERN),
85
- workspaceId: z.string().regex(idRegex("workspace")),
86
- organizationId: z.string().regex(idRegex("organization")),
87
- priceId: offeringsStripeIdSchema,
88
- lookupKey: z.string().refine((key) => parseLookupKey(key) !== null),
89
- mode: z.enum(["test", "live"]),
90
- };
91
-
92
- /** The `_session_mode_agree` CHECK: a live key makes a `cs_live_` session. */
93
- const modeAgrees = (record: {
94
- sessionId: string;
95
- mode: CheckoutSessionMode;
96
- }): boolean =>
97
- (record.mode === "live") === record.sessionId.startsWith("cs_live_");
98
-
99
- const recordInputSchema = z.object(recordShape).strict().refine(modeAgrees);
100
- const recordRowSchema = z
101
- .object({ ...recordShape, createdAt: z.date() })
102
- .strict()
103
- .refine(modeAgrees);
104
-
105
- /** The record's columns, never its `id`. */
106
- const RECORD_SELECT = {
107
- sessionId: true,
108
- workspaceId: true,
109
- organizationId: true,
110
- priceId: true,
111
- lookupKey: true,
112
- mode: true,
113
- createdAt: true,
114
- } as const;
115
-
116
- /**
117
- * Record one Checkout Session, once, for a workspace of the named
118
- * Organization. Rejects `invalid` (a malformed input), `workspace_mismatch`
119
- * (no such workspace, or another Organization's), `conflict` (the session is
120
- * already recorded; the first record stands) or `write_failed`.
121
- */
122
- export async function recordCheckoutSession(
123
- input: CheckoutSessionRecordInput,
124
- ): Promise<void> {
125
- const parsed = recordInputSchema.safeParse(input);
126
- if (!parsed.success) throw new CheckoutSessionRecordError("invalid");
127
- const data = parsed.data;
128
-
129
- let organizationId: string | null;
130
- try {
131
- organizationId = await organizationIdOfWorkspace(data.workspaceId);
132
- } catch {
133
- throw writeFailed(data.workspaceId);
134
- }
135
- if (organizationId !== data.organizationId) {
136
- throw new CheckoutSessionRecordError("workspace_mismatch");
137
- }
138
-
139
- try {
140
- await prisma.offeringsCheckoutSession.create({
141
- data,
142
- select: { sessionId: true },
143
- });
144
- } catch (err) {
145
- if (isUniqueViolation(err))
146
- throw new CheckoutSessionRecordError("conflict");
147
- throw writeFailed(data.workspaceId);
148
- }
149
- }
150
-
151
- /**
152
- * The record of one Checkout Session: the record, `"unreadable"` for a row
153
- * that fails the parse, or `null` when no row holds that id. Rejects
154
- * `read_failed` when the database read fails.
155
- */
156
- export async function readCheckoutSessionRecord(
157
- sessionId: unknown,
158
- ): Promise<CheckoutSessionRecord | "unreadable" | null> {
159
- if (typeof sessionId !== "string" || !SESSION_ID_PATTERN.test(sessionId))
160
- return null;
161
-
162
- let row: unknown;
163
- try {
164
- row = await prisma.offeringsCheckoutSession.findUnique({
165
- where: { sessionId },
166
- select: RECORD_SELECT,
167
- });
168
- } catch {
169
- logger.error(
170
- { reason: "checkout_record_read_failed" },
171
- "[offerings] checkout session record read failed",
172
- );
173
- throw new CheckoutSessionRecordError("read_failed");
174
- }
175
- if (row === null) return null;
176
-
177
- const record = recordRowSchema.safeParse(row);
178
- if (record.success) return record.data;
179
- logger.error(
180
- { reason: "checkout_record_unreadable" },
181
- "[offerings] checkout session record is unreadable",
182
- );
183
- return "unreadable";
184
- }
185
-
186
- function writeFailed(workspaceId: string): CheckoutSessionRecordError {
187
- logger.error(
188
- { workspaceId, reason: "checkout_record_write_failed" },
189
- "[offerings] checkout session record write failed",
190
- );
191
- return new CheckoutSessionRecordError("write_failed");
192
- }