@pithy-sh/core 0.1.1 → 0.1.3

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 (393) hide show
  1. package/dist/address/address.d.ts +57 -0
  2. package/dist/address/address.d.ts.map +1 -0
  3. package/dist/address/address.js +71 -0
  4. package/dist/audit/auditEvent.d.ts +89 -0
  5. package/dist/audit/auditEvent.d.ts.map +1 -0
  6. package/dist/audit/auditEvent.js +69 -0
  7. package/dist/audit/recorder.d.ts +18 -0
  8. package/dist/audit/recorder.d.ts.map +1 -0
  9. package/dist/audit/recorder.js +8 -0
  10. package/dist/capability/bindings.d.ts +62 -0
  11. package/dist/capability/bindings.d.ts.map +1 -0
  12. package/dist/capability/bindings.js +97 -0
  13. package/dist/capability/capability.d.ts +501 -0
  14. package/dist/capability/capability.d.ts.map +1 -0
  15. package/dist/capability/capability.js +16 -0
  16. package/dist/capability/client.d.ts +70 -0
  17. package/dist/capability/client.d.ts.map +1 -0
  18. package/dist/capability/client.js +86 -0
  19. package/dist/capability/compose.d.ts +25 -0
  20. package/dist/capability/compose.d.ts.map +1 -0
  21. package/dist/capability/compose.js +54 -0
  22. package/dist/capability/composition.d.ts +26 -0
  23. package/dist/capability/composition.d.ts.map +1 -0
  24. package/dist/capability/composition.js +84 -0
  25. package/dist/capability/config.d.ts +7 -0
  26. package/dist/capability/config.d.ts.map +1 -0
  27. package/dist/capability/config.js +14 -0
  28. package/dist/capability/devSecret.d.ts +32 -0
  29. package/dist/capability/devSecret.d.ts.map +1 -0
  30. package/dist/capability/devSecret.js +28 -0
  31. package/dist/capability/manifest.d.ts +298 -0
  32. package/dist/capability/manifest.d.ts.map +1 -0
  33. package/dist/capability/manifest.js +356 -0
  34. package/dist/capability/secretOrigin.d.ts +121 -0
  35. package/dist/capability/secretOrigin.d.ts.map +1 -0
  36. package/dist/capability/secretOrigin.js +134 -0
  37. package/dist/capability/settings.d.ts +119 -0
  38. package/dist/capability/settings.d.ts.map +1 -0
  39. package/dist/capability/settings.js +66 -0
  40. package/dist/capability/validateBindings.d.ts +4 -0
  41. package/dist/capability/validateBindings.d.ts.map +1 -0
  42. package/dist/capability/validateBindings.js +34 -0
  43. package/dist/capability/vanishingKey.d.ts +31 -0
  44. package/dist/capability/vanishingKey.d.ts.map +1 -0
  45. package/dist/capability/vanishingKey.js +79 -0
  46. package/dist/controlPlane/audit/actions.d.ts +70 -0
  47. package/dist/controlPlane/audit/actions.d.ts.map +1 -0
  48. package/dist/controlPlane/audit/actions.js +76 -0
  49. package/dist/controlPlane/capability.d.ts +47 -0
  50. package/dist/controlPlane/capability.d.ts.map +1 -0
  51. package/dist/controlPlane/capability.js +138 -0
  52. package/dist/controlPlane/config/config.d.ts +19 -0
  53. package/dist/controlPlane/config/config.d.ts.map +1 -0
  54. package/dist/controlPlane/config/config.js +85 -0
  55. package/dist/controlPlane/context.d.ts +27 -0
  56. package/dist/controlPlane/context.d.ts.map +1 -0
  57. package/dist/controlPlane/context.js +29 -0
  58. package/dist/controlPlane/data/connection.d.ts +79 -0
  59. package/dist/controlPlane/data/connection.d.ts.map +1 -0
  60. package/dist/controlPlane/data/connection.js +55 -0
  61. package/dist/controlPlane/data/keyLifecycle.d.ts +62 -0
  62. package/dist/controlPlane/data/keyLifecycle.d.ts.map +1 -0
  63. package/dist/controlPlane/data/keyLifecycle.js +126 -0
  64. package/dist/controlPlane/data/replay.d.ts +18 -0
  65. package/dist/controlPlane/data/replay.d.ts.map +1 -0
  66. package/dist/controlPlane/data/replay.js +20 -0
  67. package/dist/controlPlane/data/tables.d.ts +34 -0
  68. package/dist/controlPlane/data/tables.d.ts.map +1 -0
  69. package/dist/controlPlane/data/tables.js +37 -0
  70. package/dist/controlPlane/discovery/adminRoute.d.ts +313 -0
  71. package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -0
  72. package/dist/controlPlane/discovery/adminRoute.js +136 -0
  73. package/dist/controlPlane/discovery/configuration.d.ts +108 -0
  74. package/dist/controlPlane/discovery/configuration.d.ts.map +1 -0
  75. package/dist/controlPlane/discovery/configuration.js +187 -0
  76. package/dist/controlPlane/discovery/drift.d.ts +64 -0
  77. package/dist/controlPlane/discovery/drift.d.ts.map +1 -0
  78. package/dist/controlPlane/discovery/drift.js +58 -0
  79. package/dist/controlPlane/discovery/health.d.ts +99 -0
  80. package/dist/controlPlane/discovery/health.d.ts.map +1 -0
  81. package/dist/controlPlane/discovery/health.js +151 -0
  82. package/dist/controlPlane/discovery/healthSummary.d.ts +236 -0
  83. package/dist/controlPlane/discovery/healthSummary.d.ts.map +1 -0
  84. package/dist/controlPlane/discovery/healthSummary.js +278 -0
  85. package/dist/controlPlane/error/errors.d.ts +67 -0
  86. package/dist/controlPlane/error/errors.d.ts.map +1 -0
  87. package/dist/controlPlane/error/errors.js +79 -0
  88. package/dist/controlPlane/http/cors.d.ts +112 -0
  89. package/dist/controlPlane/http/cors.d.ts.map +1 -0
  90. package/dist/controlPlane/http/cors.js +192 -0
  91. package/dist/controlPlane/http/guard.d.ts +77 -0
  92. package/dist/controlPlane/http/guard.d.ts.map +1 -0
  93. package/dist/controlPlane/http/guard.js +142 -0
  94. package/dist/controlPlane/http/handlers.d.ts +179 -0
  95. package/dist/controlPlane/http/handlers.d.ts.map +1 -0
  96. package/dist/controlPlane/http/handlers.js +245 -0
  97. package/dist/controlPlane/http/responses.d.ts +99 -0
  98. package/dist/controlPlane/http/responses.d.ts.map +1 -0
  99. package/dist/controlPlane/http/responses.js +63 -0
  100. package/dist/controlPlane/http/routes.d.ts +40 -0
  101. package/dist/controlPlane/http/routes.d.ts.map +1 -0
  102. package/dist/controlPlane/http/routes.js +83 -0
  103. package/dist/controlPlane/http/schemas.d.ts +39 -0
  104. package/dist/controlPlane/http/schemas.d.ts.map +1 -0
  105. package/dist/controlPlane/http/schemas.js +31 -0
  106. package/dist/controlPlane/http/verify.d.ts +84 -0
  107. package/dist/controlPlane/http/verify.d.ts.map +1 -0
  108. package/dist/controlPlane/http/verify.js +65 -0
  109. package/dist/controlPlane/migrations/0001_init.d.ts +51 -0
  110. package/dist/controlPlane/migrations/0001_init.d.ts.map +1 -0
  111. package/dist/controlPlane/migrations/0001_init.js +65 -0
  112. package/dist/controlPlane/replay/d1Guard.d.ts +29 -0
  113. package/dist/controlPlane/replay/d1Guard.d.ts.map +1 -0
  114. package/dist/controlPlane/replay/d1Guard.js +39 -0
  115. package/dist/controlPlane/replay/guard.d.ts +52 -0
  116. package/dist/controlPlane/replay/guard.d.ts.map +1 -0
  117. package/dist/controlPlane/replay/guard.js +0 -0
  118. package/dist/controlPlane/replay/kvGuard.d.ts +99 -0
  119. package/dist/controlPlane/replay/kvGuard.d.ts.map +1 -0
  120. package/dist/controlPlane/replay/kvGuard.js +101 -0
  121. package/dist/controlPlane/scope/scope.d.ts +68 -0
  122. package/dist/controlPlane/scope/scope.d.ts.map +1 -0
  123. package/dist/controlPlane/scope/scope.js +63 -0
  124. package/dist/controlPlane/token/base64url.d.ts +10 -0
  125. package/dist/controlPlane/token/base64url.d.ts.map +1 -0
  126. package/dist/controlPlane/token/base64url.js +48 -0
  127. package/dist/controlPlane/token/claims.d.ts +83 -0
  128. package/dist/controlPlane/token/claims.d.ts.map +1 -0
  129. package/dist/controlPlane/token/claims.js +86 -0
  130. package/dist/controlPlane/token/digest.d.ts +44 -0
  131. package/dist/controlPlane/token/digest.d.ts.map +1 -0
  132. package/dist/controlPlane/token/digest.js +56 -0
  133. package/dist/controlPlane/token/jws.d.ts +52 -0
  134. package/dist/controlPlane/token/jws.d.ts.map +1 -0
  135. package/dist/controlPlane/token/jws.js +65 -0
  136. package/dist/controlPlane/token/mint.d.ts +56 -0
  137. package/dist/controlPlane/token/mint.d.ts.map +1 -0
  138. package/dist/controlPlane/token/mint.js +55 -0
  139. package/dist/controlPlane/wire.d.ts +122 -0
  140. package/dist/controlPlane/wire.d.ts.map +1 -0
  141. package/dist/controlPlane/wire.js +123 -0
  142. package/dist/createBackend.d.ts +58 -0
  143. package/dist/createBackend.d.ts.map +1 -0
  144. package/dist/createBackend.js +170 -0
  145. package/dist/createEntrypoint.d.ts +53 -0
  146. package/dist/createEntrypoint.d.ts.map +1 -0
  147. package/dist/createEntrypoint.js +52 -0
  148. package/dist/data/boundParameters.d.ts +83 -0
  149. package/dist/data/boundParameters.d.ts.map +1 -0
  150. package/dist/data/boundParameters.js +162 -0
  151. package/dist/data/codecs.d.ts +34 -0
  152. package/dist/data/codecs.d.ts.map +1 -0
  153. package/dist/data/codecs.js +135 -0
  154. package/dist/data/cursor.d.ts +81 -0
  155. package/dist/data/cursor.d.ts.map +1 -0
  156. package/dist/data/cursor.js +104 -0
  157. package/dist/data/databases.d.ts +55 -0
  158. package/dist/data/databases.d.ts.map +1 -0
  159. package/dist/data/databases.js +32 -0
  160. package/dist/data/db.d.ts +42 -0
  161. package/dist/data/db.d.ts.map +1 -0
  162. package/dist/data/db.js +24 -0
  163. package/dist/data/withD1Retry.d.ts +91 -0
  164. package/dist/data/withD1Retry.d.ts.map +1 -0
  165. package/dist/data/withD1Retry.js +152 -0
  166. package/dist/entitlement/entitlement.d.ts +118 -0
  167. package/dist/entitlement/entitlement.d.ts.map +1 -0
  168. package/dist/entitlement/entitlement.js +59 -0
  169. package/dist/entitlement/gateScan.d.ts +26 -0
  170. package/dist/entitlement/gateScan.d.ts.map +1 -0
  171. package/dist/entitlement/gateScan.js +96 -0
  172. package/dist/entitlement/require.d.ts +12 -0
  173. package/dist/entitlement/require.d.ts.map +1 -0
  174. package/dist/entitlement/require.js +123 -0
  175. package/dist/env/ambient.d.ts +52 -0
  176. package/dist/env/ambient.d.ts.map +1 -0
  177. package/dist/env/ambient.js +35 -0
  178. package/dist/env/ci.d.ts +36 -0
  179. package/dist/env/ci.d.ts.map +1 -0
  180. package/dist/env/ci.js +40 -0
  181. package/dist/env/stem.d.ts +23 -0
  182. package/dist/env/stem.d.ts.map +1 -0
  183. package/dist/env/stem.js +29 -0
  184. package/dist/error/cause.d.ts +143 -0
  185. package/dist/error/cause.d.ts.map +1 -0
  186. package/dist/error/cause.js +192 -0
  187. package/dist/error/client.d.ts +35 -0
  188. package/dist/error/client.d.ts.map +1 -0
  189. package/dist/error/client.js +38 -0
  190. package/dist/error/extend.d.ts +101 -0
  191. package/dist/error/extend.d.ts.map +1 -0
  192. package/dist/error/extend.js +76 -0
  193. package/dist/error/http.d.ts +1516 -0
  194. package/dist/error/http.d.ts.map +1 -0
  195. package/dist/error/http.js +72 -0
  196. package/dist/error/payload.d.ts +3034 -0
  197. package/dist/error/payload.d.ts.map +1 -0
  198. package/dist/error/payload.js +1110 -0
  199. package/dist/error/pithyError.d.ts +154 -0
  200. package/dist/error/pithyError.d.ts.map +1 -0
  201. package/dist/error/pithyError.js +197 -0
  202. package/dist/error/terminal.d.ts +28 -0
  203. package/dist/error/terminal.d.ts.map +1 -0
  204. package/dist/error/terminal.js +33 -0
  205. package/dist/http/authContext.d.ts +21 -0
  206. package/dist/http/authContext.d.ts.map +1 -0
  207. package/dist/http/authContext.js +23 -0
  208. package/dist/http/routeContract.d.ts +39 -0
  209. package/dist/http/routeContract.d.ts.map +1 -0
  210. package/dist/http/routeContract.js +82 -0
  211. package/dist/http/sameOrigin.d.ts +47 -0
  212. package/dist/http/sameOrigin.d.ts.map +1 -0
  213. package/dist/http/sameOrigin.js +24 -0
  214. package/dist/http/signedWebhook.d.ts +225 -0
  215. package/dist/http/signedWebhook.d.ts.map +1 -0
  216. package/dist/http/signedWebhook.js +247 -0
  217. package/dist/http/validation.d.ts +37 -0
  218. package/dist/http/validation.d.ts.map +1 -0
  219. package/dist/http/validation.js +14 -0
  220. package/dist/http/verification.d.ts +10 -0
  221. package/dist/http/verification.d.ts.map +1 -0
  222. package/dist/http/verification.js +17 -0
  223. package/dist/i18n/acceptLanguage.d.ts +22 -0
  224. package/dist/i18n/acceptLanguage.d.ts.map +1 -0
  225. package/dist/i18n/acceptLanguage.js +55 -0
  226. package/dist/i18n/catalog.d.ts +39 -0
  227. package/dist/i18n/catalog.d.ts.map +1 -0
  228. package/dist/i18n/catalog.js +57 -0
  229. package/dist/i18n/locale.d.ts +74 -0
  230. package/dist/i18n/locale.d.ts.map +1 -0
  231. package/dist/i18n/locale.js +109 -0
  232. package/dist/i18n/localeMarker.d.ts +29 -0
  233. package/dist/i18n/localeMarker.d.ts.map +1 -0
  234. package/dist/i18n/localeMarker.js +112 -0
  235. package/dist/i18n/match.d.ts +26 -0
  236. package/dist/i18n/match.d.ts.map +1 -0
  237. package/dist/i18n/match.js +88 -0
  238. package/dist/i18n/registry.d.ts +25 -0
  239. package/dist/i18n/registry.d.ts.map +1 -0
  240. package/dist/i18n/registry.js +51 -0
  241. package/dist/i18n/translator.d.ts +107 -0
  242. package/dist/i18n/translator.d.ts.map +1 -0
  243. package/dist/i18n/translator.js +80 -0
  244. package/dist/index.d.ts +32 -0
  245. package/dist/index.d.ts.map +1 -0
  246. package/dist/index.js +17 -0
  247. package/dist/kv/kv.d.ts +166 -0
  248. package/dist/kv/kv.d.ts.map +1 -0
  249. package/dist/kv/kv.js +261 -0
  250. package/dist/kv/namespaces.d.ts +58 -0
  251. package/dist/kv/namespaces.d.ts.map +1 -0
  252. package/dist/kv/namespaces.js +40 -0
  253. package/dist/logger/local.d.ts +41 -0
  254. package/dist/logger/local.d.ts.map +1 -0
  255. package/dist/logger/local.js +45 -0
  256. package/dist/logger/logger.d.ts +56 -0
  257. package/dist/logger/logger.d.ts.map +1 -0
  258. package/dist/logger/logger.js +83 -0
  259. package/dist/logger/record.d.ts +919 -0
  260. package/dist/logger/record.d.ts.map +1 -0
  261. package/dist/logger/record.js +79 -0
  262. package/dist/logger/worker.d.ts +74 -0
  263. package/dist/logger/worker.d.ts.map +1 -0
  264. package/dist/logger/worker.js +54 -0
  265. package/dist/migrations/batch.d.ts +10 -0
  266. package/dist/migrations/batch.d.ts.map +1 -0
  267. package/dist/migrations/batch.js +193 -0
  268. package/dist/migrations/bookkeeping.d.ts +24 -0
  269. package/dist/migrations/bookkeeping.d.ts.map +1 -0
  270. package/dist/migrations/bookkeeping.js +57 -0
  271. package/dist/migrations/owner.d.ts +41 -0
  272. package/dist/migrations/owner.d.ts.map +1 -0
  273. package/dist/migrations/owner.js +105 -0
  274. package/dist/migrations/registry.d.ts +36 -0
  275. package/dist/migrations/registry.d.ts.map +1 -0
  276. package/dist/migrations/registry.js +70 -0
  277. package/dist/migrations/runner.d.ts +67 -0
  278. package/dist/migrations/runner.d.ts.map +1 -0
  279. package/dist/migrations/runner.js +218 -0
  280. package/dist/naming/domains.d.ts +108 -0
  281. package/dist/naming/domains.d.ts.map +1 -0
  282. package/dist/naming/domains.js +156 -0
  283. package/dist/naming/environment.d.ts +118 -0
  284. package/dist/naming/environment.d.ts.map +1 -0
  285. package/dist/naming/environment.js +190 -0
  286. package/dist/naming/feature.d.ts +68 -0
  287. package/dist/naming/feature.d.ts.map +1 -0
  288. package/dist/naming/feature.js +106 -0
  289. package/dist/naming/limits.d.ts +229 -0
  290. package/dist/naming/limits.d.ts.map +1 -0
  291. package/dist/naming/limits.js +187 -0
  292. package/dist/naming/provisionScope.d.ts +91 -0
  293. package/dist/naming/provisionScope.d.ts.map +1 -0
  294. package/dist/naming/provisionScope.js +65 -0
  295. package/dist/naming/resource.d.ts +169 -0
  296. package/dist/naming/resource.d.ts.map +1 -0
  297. package/dist/naming/resource.js +220 -0
  298. package/dist/naming/resourceNames.d.ts +83 -0
  299. package/dist/naming/resourceNames.d.ts.map +1 -0
  300. package/dist/naming/resourceNames.js +102 -0
  301. package/dist/naming/segment.d.ts +29 -0
  302. package/dist/naming/segment.d.ts.map +1 -0
  303. package/dist/naming/segment.js +31 -0
  304. package/dist/projection/asRead.d.ts +72 -0
  305. package/dist/projection/asRead.d.ts.map +1 -0
  306. package/dist/projection/asRead.js +169 -0
  307. package/dist/projection/published.d.ts +95 -0
  308. package/dist/projection/published.d.ts.map +1 -0
  309. package/dist/projection/published.js +114 -0
  310. package/dist/schema/describedness.d.ts +55 -0
  311. package/dist/schema/describedness.d.ts.map +1 -0
  312. package/dist/schema/describedness.js +180 -0
  313. package/dist/seed/compose.d.ts +41 -0
  314. package/dist/seed/compose.d.ts.map +1 -0
  315. package/dist/seed/compose.js +51 -0
  316. package/dist/seed/devLogin.d.ts +50 -0
  317. package/dist/seed/devLogin.d.ts.map +1 -0
  318. package/dist/seed/devLogin.js +52 -0
  319. package/dist/seed/exampleIdentities.d.ts +58 -0
  320. package/dist/seed/exampleIdentities.d.ts.map +1 -0
  321. package/dist/seed/exampleIdentities.js +26 -0
  322. package/dist/seed/metadata.d.ts +20 -0
  323. package/dist/seed/metadata.d.ts.map +1 -0
  324. package/dist/seed/metadata.js +27 -0
  325. package/dist/seed/seed.d.ts +264 -0
  326. package/dist/seed/seed.d.ts.map +1 -0
  327. package/dist/seed/seed.js +37 -0
  328. package/dist/seed/seededRows.d.ts +26 -0
  329. package/dist/seed/seededRows.d.ts.map +1 -0
  330. package/dist/seed/seededRows.js +20 -0
  331. package/dist/seed/writeD1.d.ts +43 -0
  332. package/dist/seed/writeD1.d.ts.map +1 -0
  333. package/dist/seed/writeD1.js +73 -0
  334. package/dist/seed/writeKv.d.ts +29 -0
  335. package/dist/seed/writeKv.d.ts.map +1 -0
  336. package/dist/seed/writeKv.js +63 -0
  337. package/dist/semver/semver.d.ts +62 -0
  338. package/dist/semver/semver.d.ts.map +1 -0
  339. package/dist/semver/semver.js +137 -0
  340. package/dist/text/comments.d.ts +62 -0
  341. package/dist/text/comments.d.ts.map +1 -0
  342. package/dist/text/comments.js +161 -0
  343. package/dist/version.generated.d.ts +5 -0
  344. package/dist/version.generated.d.ts.map +1 -0
  345. package/dist/version.generated.js +7 -0
  346. package/dist/worker/health.d.ts +39 -0
  347. package/dist/worker/health.d.ts.map +1 -0
  348. package/dist/worker/health.js +41 -0
  349. package/dist/worker/identity.d.ts +191 -0
  350. package/dist/worker/identity.d.ts.map +1 -0
  351. package/dist/worker/identity.js +162 -0
  352. package/dist/workflow/bindings.d.ts +41 -0
  353. package/dist/workflow/bindings.d.ts.map +1 -0
  354. package/dist/workflow/bindings.js +53 -0
  355. package/dist/workflow/dispatch.d.ts +98 -0
  356. package/dist/workflow/dispatch.d.ts.map +1 -0
  357. package/dist/workflow/dispatch.js +148 -0
  358. package/dist/workflow/dispatchRoute.d.ts +80 -0
  359. package/dist/workflow/dispatchRoute.d.ts.map +1 -0
  360. package/dist/workflow/dispatchRoute.js +134 -0
  361. package/dist/workflow/faults.d.ts +97 -0
  362. package/dist/workflow/faults.d.ts.map +1 -0
  363. package/dist/workflow/faults.js +111 -0
  364. package/dist/workflow/host.d.ts +176 -0
  365. package/dist/workflow/host.d.ts.map +1 -0
  366. package/dist/workflow/host.js +116 -0
  367. package/dist/workflow/hostEntry.d.ts +50 -0
  368. package/dist/workflow/hostEntry.d.ts.map +1 -0
  369. package/dist/workflow/hostEntry.js +24 -0
  370. package/dist/workflow/hostEnv.d.ts +143 -0
  371. package/dist/workflow/hostEnv.d.ts.map +1 -0
  372. package/dist/workflow/hostEnv.js +170 -0
  373. package/dist/workflow/loopback.d.ts +52 -0
  374. package/dist/workflow/loopback.d.ts.map +1 -0
  375. package/dist/workflow/loopback.js +76 -0
  376. package/dist/workflow/naming.d.ts +79 -0
  377. package/dist/workflow/naming.d.ts.map +1 -0
  378. package/dist/workflow/naming.js +138 -0
  379. package/dist/workflow/register.d.ts +19 -0
  380. package/dist/workflow/register.d.ts.map +1 -0
  381. package/dist/workflow/register.js +40 -0
  382. package/dist/workflow/schemas.d.ts +44 -0
  383. package/dist/workflow/schemas.d.ts.map +1 -0
  384. package/dist/workflow/schemas.js +43 -0
  385. package/dist/workflow/spec.d.ts +81 -0
  386. package/dist/workflow/spec.d.ts.map +1 -0
  387. package/dist/workflow/spec.js +0 -0
  388. package/dist/workflow/stepMessage.d.ts +122 -0
  389. package/dist/workflow/stepMessage.d.ts.map +1 -0
  390. package/dist/workflow/stepMessage.js +152 -0
  391. package/package.json +20 -9
  392. package/src/capability/manifest.ts +21 -0
  393. package/src/version.generated.ts +1 -1
@@ -0,0 +1,53 @@
1
+ import type { ExecutionContext, ForwardableEmailMessage } from "@cloudflare/workers-types";
2
+ import type { Capability } from "./capability/capability";
3
+ import { type CreateBackendOptions } from "./createBackend";
4
+ /**
5
+ * A deployable Worker entrypoint: `fetch`, the single inbound `email` handler the runtime permits
6
+ * per Worker, and the single `scheduled` handler it permits for cron triggers. `createBackend`
7
+ * already produces the `fetch` side (a Hono app); this wraps it and composes every capability's
8
+ * inbound-email handler and every cron-carrying durable job behind those two entries, so a Worker
9
+ * that mounts `@pithy-sh/email` receives bounce/complaint mail — and fires its scheduler — without
10
+ * the user authoring a handler.
11
+ */
12
+ export interface PithyEntrypoint {
13
+ /** The Worker `fetch` handler — the composed Hono app. */
14
+ fetch: (request: Request, env: Record<string, unknown>, ctx: ExecutionContext) => Response | Promise<Response>;
15
+ /**
16
+ * The Worker `email` handler — fans each incoming message to every capability that declares one.
17
+ * **Present only when at least one capability handles inbound mail.** Omitted otherwise, so a Worker
18
+ * that no capability wired for email doesn't expose a no-op `email()` that would silently *drop* any
19
+ * message routed to it (an exported-but-inert handler consumes the message without forwarding or
20
+ * rejecting it).
21
+ */
22
+ email?: (message: ForwardableEmailMessage, env: Record<string, unknown>, ctx: ExecutionContext) => Promise<void>;
23
+ /**
24
+ * The Worker `scheduled` handler — starts every registered job that declares a `schedule`.
25
+ *
26
+ * **Present only when at least one job carries a cron.** The reason differs from `email`'s: an
27
+ * inert `email()` silently *drops* mail, which is a correctness bug, whereas an inert `scheduled()`
28
+ * is merely harmless. It is omitted anyway so that the export tracks the composition: a Worker
29
+ * mounting no scheduled job does not advertise a schedule it has none of.
30
+ *
31
+ * The converse does happen and is deliberate. A capability whose cron belongs to its *prebuilt host*
32
+ * — `@pithy-sh/email`'s every-minute scheduler is the shipped example — still declares that schedule
33
+ * on its spec, so an app worker composing it exports a `scheduled` handler while its own
34
+ * `wrangler.jsonc` declares no `triggers.crons`. Nothing ever invokes it, and if something did, the
35
+ * job's binding lives only on the host, so dispatch degrades with a logged warning. Deriving the
36
+ * export from "does this deployment declare crons" is not available here: `createEntrypoint` sees
37
+ * the composed capabilities, never the wrangler config.
38
+ *
39
+ * A cron is an *additional* entry point, never the only one: every scheduled job stays dispatchable
40
+ * through `c.var.workflows.trigger(...)`, because a backfill nobody can run on demand cannot be
41
+ * tested in staging.
42
+ */
43
+ scheduled?: (controller: unknown, env: Record<string, unknown>, ctx: ExecutionContext) => Promise<void>;
44
+ }
45
+ /**
46
+ * Assemble capabilities into a Worker entrypoint object — `{ fetch, email }`. Use this instead of
47
+ * `createBackend` directly when any capability handles inbound email (e.g. `@pithy-sh/email`'s bounce
48
+ * handler). `fetch` is the same Hono app `createBackend` returns; `email` fans each incoming message
49
+ * to every capability's `email` handler, replaying the single-use raw stream so each sees the full
50
+ * message. Handlers run sequentially in registration order.
51
+ */
52
+ export declare function createEntrypoint<const Caps extends readonly Capability[], const App extends Capability = Capability<Record<never, never>, Record<never, never>>>(options: CreateBackendOptions<Caps, App>): PithyEntrypoint;
53
+ //# sourceMappingURL=createEntrypoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createEntrypoint.d.ts","sourceRoot":"","sources":["../src/createEntrypoint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAA0B,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,KAAK,oBAAoB,EAAiB,MAAM,iBAAiB,CAAC;AAI3E;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,gBAAgB,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/G;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjH;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzG;AAkBD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,CAAC,IAAI,SAAS,SAAS,UAAU,EAAE,EACxC,KAAK,CAAC,GAAG,SAAS,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EACrF,OAAO,EAAE,oBAAoB,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,eAAe,CA+C3D"}
@@ -0,0 +1,52 @@
1
+ import { triggerWorkflow } from "./workflow/dispatch.js";
2
+ import { composeWorkflows, scheduledWorkflows } from "./workflow/register.js";
3
+ import { createBackend } from "./createBackend.js";
4
+ //#region src/createEntrypoint.ts
5
+ /**
6
+ * The raw MIME stream of a `ForwardableEmailMessage` is **single-use** — once one handler reads it,
7
+ * the next sees an empty stream. When more than one capability handles inbound mail we buffer the
8
+ * body once and hand each handler a proxy whose `raw` yields a fresh stream over that buffer; every
9
+ * other property/method delegates to (and stays bound to) the real message.
10
+ */
11
+ function replayable(message, raw) {
12
+ return new Proxy(message, { get(target, prop, receiver) {
13
+ if (prop === "raw") return new Response(raw.slice(0)).body;
14
+ const value = Reflect.get(target, prop, receiver);
15
+ return typeof value === "function" ? value.bind(target) : value;
16
+ } });
17
+ }
18
+ /**
19
+ * Assemble capabilities into a Worker entrypoint object — `{ fetch, email }`. Use this instead of
20
+ * `createBackend` directly when any capability handles inbound email (e.g. `@pithy-sh/email`'s bounce
21
+ * handler). `fetch` is the same Hono app `createBackend` returns; `email` fans each incoming message
22
+ * to every capability's `email` handler, replaying the single-use raw stream so each sees the full
23
+ * message. Handlers run sequentially in registration order.
24
+ */
25
+ function createEntrypoint(options) {
26
+ const app = createBackend(options);
27
+ const all = options.app ? [...options.capabilities, options.app] : [...options.capabilities];
28
+ const handlers = all.flatMap((cap) => cap.email ? [cap.email] : []);
29
+ const registry = composeWorkflows(all);
30
+ const scheduled = scheduledWorkflows(registry);
31
+ const entrypoint = { fetch: (request, env, ctx) => app.fetch(request, env, ctx) };
32
+ if (scheduled.length > 0) entrypoint.scheduled = async (_controller, env) => {
33
+ const failures = [];
34
+ for (const entry of scheduled) try {
35
+ await triggerWorkflow(env, registry, entry.key, {});
36
+ } catch (error) {
37
+ failures.push(error);
38
+ }
39
+ if (failures[0] !== void 0) throw failures[0];
40
+ };
41
+ if (handlers.length > 0) entrypoint.email = async (message, env, ctx) => {
42
+ if (handlers.length === 1) {
43
+ await handlers[0]?.(message, env, ctx);
44
+ return;
45
+ }
46
+ const raw = await new Response(message.raw).arrayBuffer();
47
+ for (const handler of handlers) await handler(replayable(message, raw), env, ctx);
48
+ };
49
+ return entrypoint;
50
+ }
51
+ //#endregion
52
+ export { createEntrypoint };
@@ -0,0 +1,83 @@
1
+ import type { D1Database } from "@cloudflare/workers-types";
2
+ /**
3
+ * D1's bound-parameter ceiling, and the chunking every `in (...)` list needs to stay under it.
4
+ *
5
+ * D1 rejects a statement carrying more than 100 bound parameters. The failure mode is the nastiest kind:
6
+ * every small test passes, and the query only breaks once real data arrives. Worse, the naive fix —
7
+ * chunking the list at exactly 100 — is *also* wrong whenever the statement binds anything else. A
8
+ * `where("indexName", "=", name).where("id", "in", <100 ids>)` binds 101, and an `update … set(a, b)`
9
+ * ahead of the list binds two more before a single id is counted.
10
+ *
11
+ * So the unit here is not "the cap" but **the budget left after the statement's own fixed parameters**.
12
+ * A call site names how many parameters its statement binds besides the list, and the chunk size is
13
+ * derived. Adding a `where` to one of those queries is then a one-number edit next to it, not a silent
14
+ * re-break of a limit nobody re-derived.
15
+ *
16
+ * Lives in core rather than in each capability because the arithmetic is the part that was got wrong,
17
+ * and because `data/` is already where D1's quirks live (see `withD1Retry`).
18
+ */
19
+ /** D1's hard cap on bound parameters in one statement. Cloudflare documents it; over it is an error, not a truncation. */
20
+ export declare const MAX_BOUND_PARAMETERS = 100;
21
+ /**
22
+ * How many list values one statement may still bind once its fixed parameters are paid for.
23
+ *
24
+ * `fixed` is everything the statement binds that is *not* part of the chunked list — each `where` value,
25
+ * each `set` column, each limit. Throws when a statement leaves no room at all: that is a bug in the
26
+ * query, not a runtime condition, and silently returning zero would spin the caller's loop forever.
27
+ */
28
+ export declare function boundParameterBudget(fixed: number): number;
29
+ /**
30
+ * Split `values` into chunks, each small enough that one statement binding `fixed` other parameters
31
+ * stays under D1's cap. An empty input yields no chunks, so a caller's loop simply does not run.
32
+ */
33
+ export declare function chunkByBoundParameters<T>(values: readonly T[], fixed: number): T[][];
34
+ /**
35
+ * Split `rows` into chunks small enough that one multi-row `insert` stays under D1's cap.
36
+ *
37
+ * The sibling of {@link chunkByBoundParameters}, and a separate function because the arithmetic is
38
+ * genuinely different: an `in (…)` list binds **one** parameter per value, while an insert binds one
39
+ * per *column* per row. Reusing the list chunker for an insert silently allows `columns` times too
40
+ * many rows, which is a bug that passes every small test and only appears once real data arrives —
41
+ * `@pithy-sh/support` shipped exactly that, capping attachments at 10 rows of 11 columns and losing
42
+ * every one of them to `too many SQL variables`.
43
+ */
44
+ export declare function chunkRowsByBoundParameters<T>(rows: readonly T[], columnsPerRow: number, fixed?: number): T[][];
45
+ /**
46
+ * D1, wrapped so a statement over the cap fails as **the rule** rather than as SQLite's complaint.
47
+ *
48
+ * `createDatabase` applies this to every database it builds, which is the whole point: the rule lives at
49
+ * the thing being called, not at each of the query sites that call it. A capability written next month
50
+ * that has never heard of the cap is covered by it, and so is one written last year — which matters,
51
+ * because the list of sites that got this wrong has been wrong five times running. Four capabilities
52
+ * bound past the cap while the arithmetic to avoid it sat in this file, unimported (#250).
53
+ *
54
+ * The check is at `bind`, because that is the only place the number is real: it is what the driver hands
55
+ * the platform, after Kysely has compiled the statement and after any chunking a caller did. Anything
56
+ * measured earlier measures intent.
57
+ *
58
+ * It does not chunk. It cannot — splitting an arbitrary statement is the caller's arithmetic, and
59
+ * {@link chunkByBoundParameters} and {@link chunkRowsByBoundParameters} are where it belongs. What this
60
+ * buys is that the failure names the rule that was broken, one statement before the platform says
61
+ * `too many SQL variables` and leaves the author to work out which list was too long.
62
+ */
63
+ export declare function guardBoundParameters(d1: D1Database): D1Database;
64
+ /** What one recorded run bound, and whatever it threw. */
65
+ export interface BoundParameterRecording {
66
+ /** Every parameter count D1 was asked to bind, in the order the statements ran. */
67
+ counts: number[];
68
+ /** What `run` threw, or `undefined`. Returned rather than propagated — see below. */
69
+ error: unknown;
70
+ }
71
+ /**
72
+ * Run `run` against a recording D1 and report every parameter count it bound.
73
+ *
74
+ * A gate wants to assert the ceiling *before* it rethrows, so the failure an author reads is the
75
+ * invariant rather than whatever the platform said about it. Hence the throw comes back in the result
76
+ * instead of propagating: the caller asserts the counts, then rethrows anything left.
77
+ *
78
+ * The counterpart to {@link guardBoundParameters} rather than a duplicate of it. The guard proves a
79
+ * statement was *refused*; this proves what was *bound* — that chunks fill the budget rather than
80
+ * creeping under it, and that chunking lost no rows on the way.
81
+ */
82
+ export declare function recordBoundParameters(d1: D1Database, run: (d1: D1Database) => Promise<void>): Promise<BoundParameterRecording>;
83
+ //# sourceMappingURL=boundParameters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundParameters.d.ts","sourceRoot":"","sources":["../../src/data/boundParameters.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAuB,MAAM,2BAA2B,CAAC;AAGjF;;;;;;;;;;;;;;;;GAgBG;AAEH,0HAA0H;AAC1H,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAe1D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CAKpF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,CAAC,EAAE,EAAE,CAiBzG;AAcD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,UAAU,GAAG,UAAU,CA0B/D;AAED,0DAA0D;AAC1D,MAAM,WAAW,uBAAuB;IACtC,mFAAmF;IACnF,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,qFAAqF;IACrF,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,UAAU,EACd,GAAG,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GACrC,OAAO,CAAC,uBAAuB,CAAC,CAyBlC"}
@@ -0,0 +1,162 @@
1
+ import { InternalError } from "../error/pithyError.js";
2
+ //#region src/data/boundParameters.ts
3
+ /**
4
+ * D1's bound-parameter ceiling, and the chunking every `in (...)` list needs to stay under it.
5
+ *
6
+ * D1 rejects a statement carrying more than 100 bound parameters. The failure mode is the nastiest kind:
7
+ * every small test passes, and the query only breaks once real data arrives. Worse, the naive fix —
8
+ * chunking the list at exactly 100 — is *also* wrong whenever the statement binds anything else. A
9
+ * `where("indexName", "=", name).where("id", "in", <100 ids>)` binds 101, and an `update … set(a, b)`
10
+ * ahead of the list binds two more before a single id is counted.
11
+ *
12
+ * So the unit here is not "the cap" but **the budget left after the statement's own fixed parameters**.
13
+ * A call site names how many parameters its statement binds besides the list, and the chunk size is
14
+ * derived. Adding a `where` to one of those queries is then a one-number edit next to it, not a silent
15
+ * re-break of a limit nobody re-derived.
16
+ *
17
+ * Lives in core rather than in each capability because the arithmetic is the part that was got wrong,
18
+ * and because `data/` is already where D1's quirks live (see `withD1Retry`).
19
+ */
20
+ /** D1's hard cap on bound parameters in one statement. Cloudflare documents it; over it is an error, not a truncation. */
21
+ const MAX_BOUND_PARAMETERS = 100;
22
+ /**
23
+ * How many list values one statement may still bind once its fixed parameters are paid for.
24
+ *
25
+ * `fixed` is everything the statement binds that is *not* part of the chunked list — each `where` value,
26
+ * each `set` column, each limit. Throws when a statement leaves no room at all: that is a bug in the
27
+ * query, not a runtime condition, and silently returning zero would spin the caller's loop forever.
28
+ */
29
+ function boundParameterBudget(fixed) {
30
+ if (!Number.isInteger(fixed) || fixed < 0) throw new InternalError({
31
+ message: "Something unexpected happened.",
32
+ detail: `boundParameterBudget requires a non-negative integer count of fixed parameters; got ${fixed}.`
33
+ });
34
+ const budget = 100 - fixed;
35
+ if (budget < 1) throw new InternalError({
36
+ message: "Something unexpected happened.",
37
+ detail: `A statement binding ${fixed} fixed parameters leaves no room under D1's cap of 100.`
38
+ });
39
+ return budget;
40
+ }
41
+ /**
42
+ * Split `values` into chunks, each small enough that one statement binding `fixed` other parameters
43
+ * stays under D1's cap. An empty input yields no chunks, so a caller's loop simply does not run.
44
+ */
45
+ function chunkByBoundParameters(values, fixed) {
46
+ const size = boundParameterBudget(fixed);
47
+ const chunks = [];
48
+ for (let start = 0; start < values.length; start += size) chunks.push(values.slice(start, start + size));
49
+ return chunks;
50
+ }
51
+ /**
52
+ * Split `rows` into chunks small enough that one multi-row `insert` stays under D1's cap.
53
+ *
54
+ * The sibling of {@link chunkByBoundParameters}, and a separate function because the arithmetic is
55
+ * genuinely different: an `in (…)` list binds **one** parameter per value, while an insert binds one
56
+ * per *column* per row. Reusing the list chunker for an insert silently allows `columns` times too
57
+ * many rows, which is a bug that passes every small test and only appears once real data arrives —
58
+ * `@pithy-sh/support` shipped exactly that, capping attachments at 10 rows of 11 columns and losing
59
+ * every one of them to `too many SQL variables`.
60
+ */
61
+ function chunkRowsByBoundParameters(rows, columnsPerRow, fixed = 0) {
62
+ if (!Number.isInteger(columnsPerRow) || columnsPerRow < 1) throw new InternalError({
63
+ message: "Something unexpected happened.",
64
+ detail: `chunkRowsByBoundParameters requires a positive column count; got ${columnsPerRow}.`
65
+ });
66
+ const size = Math.floor(boundParameterBudget(fixed) / columnsPerRow);
67
+ if (size < 1) throw new InternalError({
68
+ message: "Something unexpected happened.",
69
+ detail: `A row of ${columnsPerRow} columns cannot fit under D1's cap of 100.`
70
+ });
71
+ const chunks = [];
72
+ for (let start = 0; start < rows.length; start += size) chunks.push(rows.slice(start, start + size));
73
+ return chunks;
74
+ }
75
+ /** Read a property off a platform object without losing its `this` — workerd's D1 objects need it. */
76
+ function passThrough(target, property) {
77
+ const value = Reflect.get(target, property);
78
+ return typeof value === "function" ? value.bind(target) : value;
79
+ }
80
+ /** Enough of a statement to name it in a failure, without carrying a query of unbounded length. */
81
+ function nameOf(query) {
82
+ const collapsed = query.replace(/\s+/g, " ").trim();
83
+ return collapsed.length > 160 ? `${collapsed.slice(0, 157)}…` : collapsed;
84
+ }
85
+ /**
86
+ * D1, wrapped so a statement over the cap fails as **the rule** rather than as SQLite's complaint.
87
+ *
88
+ * `createDatabase` applies this to every database it builds, which is the whole point: the rule lives at
89
+ * the thing being called, not at each of the query sites that call it. A capability written next month
90
+ * that has never heard of the cap is covered by it, and so is one written last year — which matters,
91
+ * because the list of sites that got this wrong has been wrong five times running. Four capabilities
92
+ * bound past the cap while the arithmetic to avoid it sat in this file, unimported (#250).
93
+ *
94
+ * The check is at `bind`, because that is the only place the number is real: it is what the driver hands
95
+ * the platform, after Kysely has compiled the statement and after any chunking a caller did. Anything
96
+ * measured earlier measures intent.
97
+ *
98
+ * It does not chunk. It cannot — splitting an arbitrary statement is the caller's arithmetic, and
99
+ * {@link chunkByBoundParameters} and {@link chunkRowsByBoundParameters} are where it belongs. What this
100
+ * buys is that the failure names the rule that was broken, one statement before the platform says
101
+ * `too many SQL variables` and leaves the author to work out which list was too long.
102
+ */
103
+ function guardBoundParameters(d1) {
104
+ return new Proxy(d1, { get(target, property) {
105
+ if (property !== "prepare") return passThrough(target, property);
106
+ return (query) => {
107
+ const statement = target.prepare(query);
108
+ return new Proxy(statement, { get(inner, key) {
109
+ if (key !== "bind") return passThrough(inner, key);
110
+ return (...values) => {
111
+ if (values.length > 100) throw new InternalError({
112
+ message: "Something unexpected happened.",
113
+ action: "Retry. If it persists, the query needs chunking.",
114
+ detail: `A statement bound ${values.length} parameters; D1 accepts 100. Chunk the list through chunkByBoundParameters (one parameter per value) or chunkRowsByBoundParameters (one per column per row). Statement: ${nameOf(query)}`
115
+ });
116
+ return inner.bind(...values);
117
+ };
118
+ } });
119
+ };
120
+ } });
121
+ }
122
+ /**
123
+ * Run `run` against a recording D1 and report every parameter count it bound.
124
+ *
125
+ * A gate wants to assert the ceiling *before* it rethrows, so the failure an author reads is the
126
+ * invariant rather than whatever the platform said about it. Hence the throw comes back in the result
127
+ * instead of propagating: the caller asserts the counts, then rethrows anything left.
128
+ *
129
+ * The counterpart to {@link guardBoundParameters} rather than a duplicate of it. The guard proves a
130
+ * statement was *refused*; this proves what was *bound* — that chunks fill the budget rather than
131
+ * creeping under it, and that chunking lost no rows on the way.
132
+ */
133
+ async function recordBoundParameters(d1, run) {
134
+ const counts = [];
135
+ const recorder = new Proxy(d1, { get(target, property) {
136
+ if (property !== "prepare") return passThrough(target, property);
137
+ return (query) => {
138
+ const statement = target.prepare(query);
139
+ return new Proxy(statement, { get(inner, key) {
140
+ if (key !== "bind") return passThrough(inner, key);
141
+ return (...values) => {
142
+ counts.push(values.length);
143
+ return inner.bind(...values);
144
+ };
145
+ } });
146
+ };
147
+ } });
148
+ try {
149
+ await run(recorder);
150
+ return {
151
+ counts,
152
+ error: void 0
153
+ };
154
+ } catch (error) {
155
+ return {
156
+ counts,
157
+ error
158
+ };
159
+ }
160
+ }
161
+ //#endregion
162
+ export { MAX_BOUND_PARAMETERS, boundParameterBudget, chunkByBoundParameters, chunkRowsByBoundParameters, guardBoundParameters, recordBoundParameters };
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ /** SQLiteBoolean: boolean ↔ 0|1. Decode is lenient (number/boolean/string). */
3
+ export declare const SQLiteBoolean: z.ZodCodec<z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodBoolean, z.ZodString]>, z.ZodBoolean>;
4
+ /** SQLiteDate: Date ↔ ms-epoch number. Decode accepts number/string/Date. */
5
+ export declare const SQLiteDate: z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString, z.ZodDate]>, z.ZodDate>;
6
+ /** JsonDate: Date ↔ ISO string, for dates nested inside `sqliteJson` payloads. */
7
+ export declare const JsonDate: z.ZodCodec<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodDate]>, z.ZodDate>;
8
+ /**
9
+ * sqliteJson: T ↔ JSON string, validated by `schema` on both sides. Decode
10
+ * accepts a JSON string or an already-parsed value; the result is validated
11
+ * against `schema` before it reaches the app.
12
+ *
13
+ * **Both `JSON` functions throw**, and both were reachable. `JSON.parse` throws a
14
+ * `SyntaxError` on any string that is not a JSON document — which a column holding
15
+ * text an older writer put there, or a payload a customer's Worker returned, can be.
16
+ * `JSON.stringify` throws a `TypeError` on a `BigInt` and on a cycle, either of
17
+ * which a `schema` can legitimately admit. Both become an issue (#358).
18
+ */
19
+ export declare function sqliteJson<T extends z.ZodType>(schema: T): z.ZodCodec<z.ZodUnion<readonly [z.ZodString, T]>, T>;
20
+ export declare const MAX_TIMEZONE_LENGTH = 64;
21
+ /**
22
+ * Trim, validate, and canonicalize an IANA timezone; anything invalid becomes
23
+ * `undefined`. Returns Intl's canonical casing (`america/new_york` →
24
+ * `America/New_York`) so a zone is stored and compared as one value.
25
+ */
26
+ export declare function normalizeIanaTimezone(value: unknown): string | undefined;
27
+ /** Whether `value` is a usable IANA timezone (bounded length, recognized by Intl). */
28
+ export declare function isValidIanaTimezone(value: string): boolean;
29
+ /**
30
+ * IanaTimezone: validated/bounded IANA timezone string. Coerces invalid input
31
+ * to `undefined` on both decode and encode rather than throwing.
32
+ */
33
+ export declare const IanaTimezone: z.ZodCodec<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodOptional<z.ZodString>>;
34
+ //# sourceMappingURL=codecs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codecs.d.ts","sourceRoot":"","sources":["../../src/data/codecs.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA+CxB,+EAA+E;AAC/E,eAAO,MAAM,aAAa,8GAGxB,CAAC;AAoBH,6EAA6E;AAC7E,eAAO,MAAM,UAAU,mFAGrB,CAAC;AAEH,kFAAkF;AAClF,eAAO,MAAM,QAAQ,mFAGnB,CAAC;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,wDA8BxD;AAED,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CASxE;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,mFAGvB,CAAC"}
@@ -0,0 +1,135 @@
1
+ import { z } from "zod";
2
+ //#region src/data/codecs.ts
3
+ const TRUTHY = [
4
+ true,
5
+ 1,
6
+ "1",
7
+ "true",
8
+ "True",
9
+ "TRUE"
10
+ ];
11
+ function isTruthy(value) {
12
+ return TRUTHY.includes(value);
13
+ }
14
+ /** SQLiteBoolean: boolean ↔ 0|1. Decode is lenient (number/boolean/string). */
15
+ const SQLiteBoolean = z.codec(z.union([
16
+ z.literal(0),
17
+ z.literal(1),
18
+ z.boolean(),
19
+ z.string()
20
+ ]), z.boolean(), {
21
+ decode: (input) => typeof input === "boolean" ? input : isTruthy(input),
22
+ encode: (value) => value ? 1 : 0
23
+ });
24
+ /**
25
+ * The shared decode for both date codecs. A string is parsed, a number is ms-epoch — what `encode`
26
+ * writes, so a caller ingesting seconds-epoch from elsewhere converts explicitly — and a `Date` passes
27
+ * through by identity.
28
+ *
29
+ * **Every branch can produce an unusable `Date`**, not only the string one: `new Date(8.64e15 + 1)` is
30
+ * as invalid as `new Date("not-a-date")`. So the check is on the result, once, rather than on the input
31
+ * shape — which is what stops the next accepted input type from arriving without it.
32
+ */
33
+ function toDate(input, payload) {
34
+ const parsed = input instanceof Date ? input : new Date(input);
35
+ if (Number.isNaN(parsed.getTime())) {
36
+ payload.issues.push({
37
+ code: "invalid_format",
38
+ format: "datetime",
39
+ input: String(input),
40
+ message: "Not a date."
41
+ });
42
+ return z.NEVER;
43
+ }
44
+ return parsed;
45
+ }
46
+ /** SQLiteDate: Date ↔ ms-epoch number. Decode accepts number/string/Date. */
47
+ const SQLiteDate = z.codec(z.union([
48
+ z.number(),
49
+ z.string(),
50
+ z.date()
51
+ ]), z.date(), {
52
+ decode: toDate,
53
+ encode: (value) => value.getTime()
54
+ });
55
+ /** JsonDate: Date ↔ ISO string, for dates nested inside `sqliteJson` payloads. */
56
+ const JsonDate = z.codec(z.union([
57
+ z.string(),
58
+ z.number(),
59
+ z.date()
60
+ ]), z.date(), {
61
+ decode: toDate,
62
+ encode: (value) => value.toISOString()
63
+ });
64
+ /**
65
+ * sqliteJson: T ↔ JSON string, validated by `schema` on both sides. Decode
66
+ * accepts a JSON string or an already-parsed value; the result is validated
67
+ * against `schema` before it reaches the app.
68
+ *
69
+ * **Both `JSON` functions throw**, and both were reachable. `JSON.parse` throws a
70
+ * `SyntaxError` on any string that is not a JSON document — which a column holding
71
+ * text an older writer put there, or a payload a customer's Worker returned, can be.
72
+ * `JSON.stringify` throws a `TypeError` on a `BigInt` and on a cycle, either of
73
+ * which a `schema` can legitimately admit. Both become an issue (#358).
74
+ */
75
+ function sqliteJson(schema) {
76
+ return z.codec(z.union([z.string(), schema]), schema, {
77
+ decode: (value, payload) => {
78
+ if (typeof value !== "string") return value;
79
+ try {
80
+ return JSON.parse(value);
81
+ } catch {
82
+ payload.issues.push({
83
+ code: "invalid_format",
84
+ format: "json",
85
+ input: value,
86
+ message: "Not valid JSON."
87
+ });
88
+ return z.NEVER;
89
+ }
90
+ },
91
+ encode: (value, payload) => {
92
+ try {
93
+ const json = JSON.stringify(value);
94
+ if (typeof json === "string") return json;
95
+ } catch {}
96
+ payload.issues.push({
97
+ code: "invalid_format",
98
+ format: "json",
99
+ input: void 0,
100
+ message: "Not serializable as JSON."
101
+ });
102
+ return z.NEVER;
103
+ }
104
+ });
105
+ }
106
+ const MAX_TIMEZONE_LENGTH = 64;
107
+ /**
108
+ * Trim, validate, and canonicalize an IANA timezone; anything invalid becomes
109
+ * `undefined`. Returns Intl's canonical casing (`america/new_york` →
110
+ * `America/New_York`) so a zone is stored and compared as one value.
111
+ */
112
+ function normalizeIanaTimezone(value) {
113
+ if (typeof value !== "string") return void 0;
114
+ const trimmed = value.trim();
115
+ if (trimmed.length === 0 || trimmed.length > 64) return void 0;
116
+ try {
117
+ return Intl.DateTimeFormat(void 0, { timeZone: trimmed }).resolvedOptions().timeZone;
118
+ } catch {
119
+ return;
120
+ }
121
+ }
122
+ /** Whether `value` is a usable IANA timezone (bounded length, recognized by Intl). */
123
+ function isValidIanaTimezone(value) {
124
+ return normalizeIanaTimezone(value) !== void 0;
125
+ }
126
+ /**
127
+ * IanaTimezone: validated/bounded IANA timezone string. Coerces invalid input
128
+ * to `undefined` on both decode and encode rather than throwing.
129
+ */
130
+ const IanaTimezone = z.codec(z.string().nullish(), z.string().optional(), {
131
+ decode: (input) => normalizeIanaTimezone(input),
132
+ encode: (value) => normalizeIanaTimezone(value)
133
+ });
134
+ //#endregion
135
+ export { IanaTimezone, JsonDate, MAX_TIMEZONE_LENGTH, SQLiteBoolean, SQLiteDate, isValidIanaTimezone, normalizeIanaTimezone, sqliteJson };
@@ -0,0 +1,81 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Keyset pagination, once, for every capability that lists an unbounded table.
4
+ *
5
+ * ## Why keyset and never offset
6
+ *
7
+ * `OFFSET` shifts under a client whenever a row is inserted, and every table these paginate — audit
8
+ * events, email jobs, users, ledger transactions — is written to constantly while somebody is reading
9
+ * it. With `OFFSET 25` a row inserted at the head pushes one row from page 1 onto page 2, so a client
10
+ * paging through sees it twice and misses nothing only by luck; a deletion drops a row entirely. A
11
+ * keyset cursor names the last row's sort position, so the next page starts exactly where the previous
12
+ * ended whatever happened in between.
13
+ *
14
+ * ## Why this is in core rather than copied
15
+ *
16
+ * `@pithy-sh/support` had the only implementation, and Part 3 of #89 needs the same thing in four more
17
+ * packages. Four copies of a security-adjacent decode — where the difference between "malformed cursor"
18
+ * and "500" is a caller's ability to probe — is four places for one of them to be wrong. Capabilities
19
+ * depend on core seams, which is exactly what this is.
20
+ *
21
+ * ## The shape of a page
22
+ *
23
+ * A caller asks for `limit`; the query fetches `limit + 1` and, when it gets them, drops the extra and
24
+ * returns a `nextCursor`. That is what makes "is there another page" answerable **without a count
25
+ * query**, which on an audit table is the difference between a page load and a table scan.
26
+ */
27
+ /** A sensible page when a caller names none. Small enough to render, large enough to be one request. */
28
+ export declare const DEFAULT_PAGE_SIZE = 25;
29
+ /** The most a caller may ask for in one page, whatever they send. An unbounded page is a table scan. */
30
+ export declare const MAX_PAGE_SIZE = 100;
31
+ /**
32
+ * The `limit` a query should actually use, clamped into range.
33
+ *
34
+ * Clamped rather than rejected: a client asking for 1000 wants "as many as you'll give me", and failing
35
+ * the request teaches it nothing a capped page does not. A caller asking for 0 or a negative gets one
36
+ * row rather than an empty page that looks like the end of the list.
37
+ */
38
+ export declare function pageLimit(requested: number | undefined): number;
39
+ /**
40
+ * An opaque position in a descending `(sort, id)` ordering.
41
+ *
42
+ * `sort` is the primary column's value — a ms-epoch for a date, a number for a monotonic id. `id` is the
43
+ * tiebreak that makes the position exact: without it, two rows sharing a timestamp straddle a page
44
+ * boundary and one of them is skipped or repeated.
45
+ */
46
+ export declare const PageCursor: z.ZodObject<{
47
+ sort: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
48
+ id: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
49
+ }, z.core.$strip>;
50
+ export type PageCursor = z.infer<typeof PageCursor>;
51
+ /**
52
+ * Encode a cursor for the wire.
53
+ *
54
+ * Base64url over JSON, and **opaque by intent rather than by obscurity**: the point is that a client
55
+ * cannot construct one by hand and therefore cannot come to depend on its shape, so the ordering can
56
+ * change later without breaking every caller. It is not a secret — it holds a sort value and an id the
57
+ * caller was just given, both of which were in the page it came from.
58
+ */
59
+ export declare function encodeCursor(cursor: PageCursor): string;
60
+ /**
61
+ * Decode a cursor. Returns undefined for anything malformed.
62
+ *
63
+ * **A bad cursor is a first page, not a 500.** Cursors travel through URLs, get truncated by clients,
64
+ * and outlive deploys; treating a malformed one as an error turns an ordinary client bug into a page
65
+ * that never loads, and hands anyone probing the endpoint a way to tell a parse failure from a
66
+ * not-found. Every failure mode here — bad base64, bad JSON, wrong shape — collapses to the same
67
+ * undefined.
68
+ */
69
+ export declare function decodeCursor(value: string | undefined): PageCursor | undefined;
70
+ /**
71
+ * Split an over-fetched result into the page and the cursor that follows it.
72
+ *
73
+ * Pass the rows a query returned when asked for `limit + 1`. When there are more than `limit`, the extra
74
+ * is dropped and `nextCursor` names the last row of the page; otherwise `nextCursor` is null and the
75
+ * client knows it has reached the end without a second request.
76
+ */
77
+ export declare function toPage<T>(rows: readonly T[], limit: number, position: (row: T) => PageCursor): {
78
+ items: T[];
79
+ nextCursor: string | null;
80
+ };
81
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/data/cursor.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,wGAAwG;AACxG,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,wGAAwG;AACxG,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE/D;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU;;;iBAa4E,CAAC;AACpG,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAQvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAU9E;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,CAAC,EACtB,IAAI,EAAE,SAAS,CAAC,EAAE,EAClB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,UAAU,GAC/B;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAK3C"}