permdock 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/README.md +327 -1
  2. package/dist/a2a/index.d.ts +169 -0
  3. package/dist/a2a/index.js +232 -0
  4. package/dist/adapter-context-mlWsVGGg.js +23 -0
  5. package/dist/ai-sdk/index.d.ts +95 -0
  6. package/dist/ai-sdk/index.js +154 -0
  7. package/dist/approval-required-TzNB9oQA.js +6 -0
  8. package/dist/approvals/index.d.ts +3 -0
  9. package/dist/approvals/index.js +5 -0
  10. package/dist/approvals-DbYAuNkG.js +224 -0
  11. package/dist/approvers-DT4AVyIg.js +52 -0
  12. package/dist/arazzo-BAgw9ap6.js +70 -0
  13. package/dist/arazzo-cmvX2jDe.js +54 -0
  14. package/dist/authzen/index.d.ts +39 -0
  15. package/dist/authzen/index.js +302 -0
  16. package/dist/better-auth/index.d.ts +109 -0
  17. package/dist/better-auth/index.js +344 -0
  18. package/dist/better-supabase/index.d.ts +206 -0
  19. package/dist/better-supabase/index.js +620 -0
  20. package/dist/bind-CVy3Espv.d.ts +25 -0
  21. package/dist/bind-VnYn2HKq.js +97 -0
  22. package/dist/boundary-BM8a_P6E.js +13 -0
  23. package/dist/boundary-BmHxVH1J.d.ts +15 -0
  24. package/dist/budget-JqcRqyhK.d.ts +11 -0
  25. package/dist/budget-NbEtbJC_.js +11 -0
  26. package/dist/canonical-json-DemiK4Vp.js +26 -0
  27. package/dist/capability-4qPdO9gk.js +173 -0
  28. package/dist/capability-Dq43tT1u.d.ts +99 -0
  29. package/dist/catalog/index.d.ts +12 -0
  30. package/dist/catalog/index.js +2 -0
  31. package/dist/catalog-BjZSovXQ.js +2 -0
  32. package/dist/catalog-doc-Dsc9y7sd.js +309 -0
  33. package/dist/catalog-h2tlKFkq.js +48 -0
  34. package/dist/catalog-path-CnI0XTFK.js +12 -0
  35. package/dist/catalog-z8q6bi_r.js +55 -0
  36. package/dist/claude-agent/index.d.ts +72 -0
  37. package/dist/claude-agent/index.js +77 -0
  38. package/dist/clerk/index.d.ts +65 -0
  39. package/dist/clerk/index.js +301 -0
  40. package/dist/cli/bin.d.ts +1 -0
  41. package/dist/cli/bin.js +20 -0
  42. package/dist/cli/index.d.ts +46 -0
  43. package/dist/cli/index.js +5 -0
  44. package/dist/clients-C7zLFuWT.d.ts +12 -0
  45. package/dist/clients-DjU5ABMy.js +23 -0
  46. package/dist/cloud/index.d.ts +90 -0
  47. package/dist/cloud/index.js +404 -0
  48. package/dist/cloud-BGhHS-lY.js +50 -0
  49. package/dist/cloud-Bo3e9Ak3.js +118 -0
  50. package/dist/collect-DDDv_ESs.js +502 -0
  51. package/dist/collect-DLlujdHC.js +83 -0
  52. package/dist/collect-rG2IQ3t0.js +2 -0
  53. package/dist/commands-DWOrSQic.js +97 -0
  54. package/dist/compact-y6vfu2_f.js +19 -0
  55. package/dist/compare-pfyVnBjE.js +12 -0
  56. package/dist/compile/index.d.ts +3 -0
  57. package/dist/compile/index.js +2 -0
  58. package/dist/compile-Ba4blROR.js +613 -0
  59. package/dist/compile-CNnl6uIt.d.ts +94 -0
  60. package/dist/config-BJiM-xAm.js +157 -0
  61. package/dist/config-Bn00Z3ks.js +36 -0
  62. package/dist/config-report-Dw_JcUqT.js +44 -0
  63. package/dist/connection-CU8MQxRi.js +194 -0
  64. package/dist/context-Ttp0ekMT.js +82 -0
  65. package/dist/convex/index.d.ts +45 -0
  66. package/dist/convex/index.js +65 -0
  67. package/dist/create-BWyx3jua.d.ts +245 -0
  68. package/dist/create-CPhDL6qx.js +253 -0
  69. package/dist/credential-8P81ff4u.js +306 -0
  70. package/dist/delegation-D8pUEuce.js +103 -0
  71. package/dist/describe-B_gVZEVi.js +94 -0
  72. package/dist/describe-PdoqESvg.d.ts +36 -0
  73. package/dist/diff-B8Tunc3o.js +451 -0
  74. package/dist/diff-CzqLxZ77.js +48 -0
  75. package/dist/digest-B3kc_PgQ.d.ts +19 -0
  76. package/dist/digest-BI-6aa6d.js +27 -0
  77. package/dist/doctor-WpvG6p4k.js +1194 -0
  78. package/dist/doctor-collect-ByfYlNv1.js +664 -0
  79. package/dist/doctor-rOr-4ZfS.js +53 -0
  80. package/dist/drizzle/index.d.ts +73 -0
  81. package/dist/drizzle/index.js +160 -0
  82. package/dist/duration-xK2jGFVc.js +25 -0
  83. package/dist/elysia/index.d.ts +49 -0
  84. package/dist/elysia/index.js +59 -0
  85. package/dist/errors-CWfoNg9N.js +47 -0
  86. package/dist/errors-Cz5zn0mW.js +123 -0
  87. package/dist/errors-DAqevpNk.d.ts +125 -0
  88. package/dist/errors-DRnGZHzF.js +30 -0
  89. package/dist/evaluation-items-CfCCejQd.js +40 -0
  90. package/dist/evaluations-tz6D9A2G.js +461 -0
  91. package/dist/eve/index.d.ts +86 -0
  92. package/dist/eve/index.js +176 -0
  93. package/dist/expiry-BoBJ8Tvc.js +7 -0
  94. package/dist/express/index.d.ts +23 -0
  95. package/dist/express/index.js +78 -0
  96. package/dist/fastify/index.d.ts +33 -0
  97. package/dist/fastify/index.js +92 -0
  98. package/dist/fields-BMl1RxoX.js +26 -0
  99. package/dist/files-DWUN9PzI.js +125 -0
  100. package/dist/fixtures-ht28Cplr.js +67 -0
  101. package/dist/freeze-CrU8nQeX.js +60 -0
  102. package/dist/from-snapshot-C3sHpZNS.js +569 -0
  103. package/dist/generate-CH7ThJ14.js +173 -0
  104. package/dist/grantee-Cv5Sogfv.js +602 -0
  105. package/dist/graph-sql-B-dsAT6j.d.ts +29 -0
  106. package/dist/headers-BZSDieNT.js +6 -0
  107. package/dist/headers-Cf-zWvsO.d.ts +6 -0
  108. package/dist/helpers-H_KARDVC.js +421 -0
  109. package/dist/hono/index.d.ts +48 -0
  110. package/dist/hono/index.js +99 -0
  111. package/dist/hosted-DJQw46hn.js +266 -0
  112. package/dist/http-BIhmLfCc.js +95 -0
  113. package/dist/http-DPuDaChb.d.ts +13 -0
  114. package/dist/http-SO7qGttf.js +44 -0
  115. package/dist/ids-DC4V4CFu.js +18 -0
  116. package/dist/index-A0YNL8jZ.d.ts +177 -0
  117. package/dist/index-CtE07CBF.d.ts +6 -0
  118. package/dist/index-D3vbkoSI.d.ts +483 -0
  119. package/dist/index-D6_cSa1z.d.ts +201 -0
  120. package/dist/index.d.ts +16 -0
  121. package/dist/index.js +26 -0
  122. package/dist/instance-options-BK_rNOSC.d.ts +11 -0
  123. package/dist/instance-options-Dhhcnm3y.js +22 -0
  124. package/dist/json-schema-BsAflUGW.js +106 -0
  125. package/dist/jwt/index.d.ts +91 -0
  126. package/dist/jwt/index.js +590 -0
  127. package/dist/kernel-GSQnMz3O.js +208 -0
  128. package/dist/kysely/index.d.ts +78 -0
  129. package/dist/kysely/index.js +129 -0
  130. package/dist/live-options-H2eG8dfJ.js +60 -0
  131. package/dist/load-DhWEB9P1.js +75 -0
  132. package/dist/load-DwJUafYN.js +2 -0
  133. package/dist/load-jose-BL6LH4bL.js +11 -0
  134. package/dist/lru-8q4WZjBV.js +28 -0
  135. package/dist/manifest-iaoBXHKc.js +783 -0
  136. package/dist/manifest-uDS9y5de.d.ts +206 -0
  137. package/dist/map-AcxK9o_D.js +147 -0
  138. package/dist/map-claims-Z4QZOiFF.js +369 -0
  139. package/dist/map-error-pgUWNNtG.js +23 -0
  140. package/dist/markers-B0hYLvMD.js +60 -0
  141. package/dist/may-use-DndQ5tQ-.js +77 -0
  142. package/dist/mcp/index.d.ts +159 -0
  143. package/dist/mcp/index.js +603 -0
  144. package/dist/nest/index.d.ts +75 -0
  145. package/dist/nest/index.js +261 -0
  146. package/dist/next/client.d.ts +23 -0
  147. package/dist/next/client.js +33 -0
  148. package/dist/next/index.d.ts +93 -0
  149. package/dist/next/index.js +180 -0
  150. package/dist/next/plugin.d.ts +14 -0
  151. package/dist/next/plugin.js +33 -0
  152. package/dist/node/index.d.ts +24 -0
  153. package/dist/node/index.js +34 -0
  154. package/dist/openai/index.d.ts +62 -0
  155. package/dist/openai/index.js +92 -0
  156. package/dist/openapi/index.d.ts +176 -0
  157. package/dist/openapi/index.js +3 -0
  158. package/dist/openapi-Bbg4wGVL.js +164 -0
  159. package/dist/openapi-BqRBvAng.js +538 -0
  160. package/dist/openapi-CqqEbHsL.js +250 -0
  161. package/dist/openapi-import-cmwetbzw.js +5959 -0
  162. package/dist/openapi-schema--ARjyfQr.js +6902 -0
  163. package/dist/orpc/index.d.ts +51 -0
  164. package/dist/orpc/index.js +145 -0
  165. package/dist/otel/index.d.ts +13 -0
  166. package/dist/otel/index.js +202 -0
  167. package/dist/package-root-zGkxO3oN.js +30 -0
  168. package/dist/parse-BuyjxhZV.js +53 -0
  169. package/dist/parse-local-manifest-BkGQNzAh.d.ts +10 -0
  170. package/dist/parse-local-manifest-BrMozJCo.js +36 -0
  171. package/dist/pdp/index.d.ts +22 -0
  172. package/dist/pdp/index.js +682 -0
  173. package/dist/peer-BOr_kJE1.js +31 -0
  174. package/dist/permdock-CzqXjCpU.js +3471 -0
  175. package/dist/permissions-CDgvKG5L.d.ts +52 -0
  176. package/dist/permissions-CpZrivYv.js +142 -0
  177. package/dist/permissions-DVoF6dMt.js +662 -0
  178. package/dist/permissions-DttnqqxC.d.ts +214 -0
  179. package/dist/pins-8ILTftC9.js +12 -0
  180. package/dist/policy-CFZAr_Al.d.ts +2252 -0
  181. package/dist/policy-D8UFyERw.d.ts +20 -0
  182. package/dist/policy-xyWg7QdR.js +1122 -0
  183. package/dist/powersync-A8x_Yyvh.js +2 -0
  184. package/dist/powersync-C7kLglqX.js +540 -0
  185. package/dist/powersync-yW1UUFzp.js +62 -0
  186. package/dist/prisma/index.d.ts +136 -0
  187. package/dist/prisma/index.js +455 -0
  188. package/dist/problem-B43ztKIm.js +191 -0
  189. package/dist/problem-details-CkL50EEf.d.ts +7 -0
  190. package/dist/problem-details-_AMOYz-w.js +144 -0
  191. package/dist/prompt-CXYhLfsS.js +32 -0
  192. package/dist/protected-CNsMzrVd.d.ts +27 -0
  193. package/dist/protected-Cs6kTaHo.js +100 -0
  194. package/dist/protected-bZs6rbT1.d.ts +40 -0
  195. package/dist/provider-Cjr62YMV.d.ts +6 -0
  196. package/dist/provider-DLzpb2Xz.js +52 -0
  197. package/dist/provider-store-DNyC1y48.js +43 -0
  198. package/dist/react/index.d.ts +8 -0
  199. package/dist/react/index.js +7 -0
  200. package/dist/react/provider-client.d.ts +2 -0
  201. package/dist/react/provider-client.js +3 -0
  202. package/dist/react-native/index.d.ts +198 -0
  203. package/dist/react-native/index.js +696 -0
  204. package/dist/refs-KYb4ntaz.js +91 -0
  205. package/dist/relations-cjOL01hZ.js +505 -0
  206. package/dist/replay-BsOyTrdV.js +28 -0
  207. package/dist/replay-sqBGMnNs.d.ts +5 -0
  208. package/dist/resource-ref-CaS9yVPi.js +20 -0
  209. package/dist/revocations-Bhr0aMIC.js +27 -0
  210. package/dist/revocations-Cy06JNx2.d.ts +21 -0
  211. package/dist/rls-BMNydpHk.js +2521 -0
  212. package/dist/rls-CEvs64Kj.js +238 -0
  213. package/dist/rls-fields-BMasREhR.js +3502 -0
  214. package/dist/rls-graph-pc9fuXYm.js +435 -0
  215. package/dist/rls-import-D8F7j35Q.js +943 -0
  216. package/dist/rls-migrate-7wikfrVI.js +367 -0
  217. package/dist/rls-ownership-B5Lkq62W.js +843 -0
  218. package/dist/rls-shared-aOQ4hGTO.js +493 -0
  219. package/dist/rls-verify-D8B7dOTh.js +745 -0
  220. package/dist/row-check-Dt35czLy.d.ts +40 -0
  221. package/dist/rpc-DOK54_CS.js +89 -0
  222. package/dist/run-YXCJDNYk.js +174 -0
  223. package/dist/runtime-CB8gQFvS.js +11 -0
  224. package/dist/saas-Bj_FFkjN.js +1203 -0
  225. package/dist/schema-D3MnDMxa.js +300 -0
  226. package/dist/scim/index.d.ts +2 -0
  227. package/dist/scim/index.js +2 -0
  228. package/dist/scim-BFGBBAqR.js +1445 -0
  229. package/dist/scopes-CVdPrWN-.js +239 -0
  230. package/dist/server/index.d.ts +134 -0
  231. package/dist/server/index.js +11 -0
  232. package/dist/server-DlZ28Oz5.js +268 -0
  233. package/dist/sha256-BO0swRXL.js +150 -0
  234. package/dist/shared-DRu8VFVM.js +123 -0
  235. package/dist/sink-Bd9PSyfq.js +141 -0
  236. package/dist/sink-Gs6TTVa-.d.ts +104 -0
  237. package/dist/skills-Biq5uup3.js +192 -0
  238. package/dist/skills-JaymNCdK.js +56 -0
  239. package/dist/snapshot-Enxd0n-s.js +975 -0
  240. package/dist/snapshot-cache-D6jLelln.js +44 -0
  241. package/dist/snapshot-cache-DwQH8ufx.d.ts +45 -0
  242. package/dist/solid/index.d.ts +93 -0
  243. package/dist/solid/index.js +188 -0
  244. package/dist/sources-B0UvrJ21.js +534 -0
  245. package/dist/sources-BcJgw0lt.d.ts +192 -0
  246. package/dist/sql-xdg5f4Fi.js +27 -0
  247. package/dist/src-CSbUiozF.js +568 -0
  248. package/dist/ssf/index.d.ts +4 -0
  249. package/dist/ssf/index.js +596 -0
  250. package/dist/store-DMAWwAJV.js +371 -0
  251. package/dist/store-D_pjn91-.d.ts +36 -0
  252. package/dist/store-options-yO0V6gRn.js +716 -0
  253. package/dist/stores-4_sqiOA1.js +87 -0
  254. package/dist/stores-DVR5NHLE.d.ts +52 -0
  255. package/dist/stream-DDpvynSB.js +66 -0
  256. package/dist/stream-DlvG2hs-.d.ts +12 -0
  257. package/dist/style-CTm1AXDT.js +22 -0
  258. package/dist/subject-C6dLxqHU.js +24 -0
  259. package/dist/subject-settings-BwBmCdTR.js +128 -0
  260. package/dist/supabase/index.d.ts +183 -0
  261. package/dist/supabase/index.js +5 -0
  262. package/dist/supabase/middleware.d.ts +82 -0
  263. package/dist/supabase/middleware.js +102 -0
  264. package/dist/supabase-CwafwxxF.js +83 -0
  265. package/dist/supabase-DTBaC20O.js +774 -0
  266. package/dist/supabase-config-Bs44dare.js +472 -0
  267. package/dist/supabase-hook-BuxLQLpU.js +1814 -0
  268. package/dist/supabase-hook-D5qVd_D8.js +2 -0
  269. package/dist/supautils-DYZnzRWV.js +91 -0
  270. package/dist/svelte/PermissionBoundary.svelte +37 -0
  271. package/dist/svelte/Protected.svelte +40 -0
  272. package/dist/svelte/index.d.ts +13 -0
  273. package/dist/svelte/index.js +107 -0
  274. package/dist/svelte/runtime.d.ts +7 -0
  275. package/dist/svelte/runtime.js +4 -0
  276. package/dist/svelte/source.d.ts +13 -0
  277. package/dist/svelte/source.js +11 -0
  278. package/dist/terminal/index.d.ts +163 -0
  279. package/dist/terminal/index.js +833 -0
  280. package/dist/testing/index.d.ts +745 -0
  281. package/dist/testing/index.js +3672 -0
  282. package/dist/testing/saas/index.d.ts +194 -0
  283. package/dist/testing/saas/index.js +3 -0
  284. package/dist/testing/saas/permissions.d.ts +2 -0
  285. package/dist/testing/saas/permissions.js +2 -0
  286. package/dist/text-diff-BdkkYSbi.js +15 -0
  287. package/dist/thenable-okc2xDsj.js +10 -0
  288. package/dist/timeout-D51niuQP.js +8 -0
  289. package/dist/token-CIWxJOCh.js +50 -0
  290. package/dist/trpc/index.d.ts +61 -0
  291. package/dist/trpc/index.js +122 -0
  292. package/dist/types-9AWO9EGt.d.ts +197 -0
  293. package/dist/types-BPxgszlT.d.ts +550 -0
  294. package/dist/types-BVyImy5I.d.ts +120 -0
  295. package/dist/types-Bvc4oWT6.d.ts +9 -0
  296. package/dist/types-C3s9xVJG.d.ts +48 -0
  297. package/dist/types-Dnemwd77.d.ts +183 -0
  298. package/dist/types-kxznc7cn.d.ts +52 -0
  299. package/dist/types-vKyAMtcX.d.ts +157 -0
  300. package/dist/types-vNSDF7Dq.d.ts +66 -0
  301. package/dist/types-yF5LmQVD.d.ts +306 -0
  302. package/dist/unplugin/index.d.ts +7 -0
  303. package/dist/unplugin/index.js +31 -0
  304. package/dist/usage-D07N9QJ3.js +42 -0
  305. package/dist/usage-D82l1CwK.js +2 -0
  306. package/dist/usage-DG1oeCca.js +693 -0
  307. package/dist/validation-error-CnjZ4t92.js +33 -0
  308. package/dist/validation-error-CtF23SrC.d.ts +26 -0
  309. package/dist/validity-uOp8UpYn.js +245 -0
  310. package/dist/verifier-BxBo6I44.js +466 -0
  311. package/dist/version-BvacVTZR.js +17 -0
  312. package/dist/views-Soe7bGSC.js +95 -0
  313. package/dist/vocabulary-DA8a8zif.d.ts +56 -0
  314. package/dist/vue/index.d.ts +79 -0
  315. package/dist/vue/index.js +196 -0
  316. package/dist/watch-Etoa_Mvr.js +2 -0
  317. package/dist/watch-UihESpsJ.js +128 -0
  318. package/dist/webmcp/index.d.ts +75 -0
  319. package/dist/webmcp/index.js +307 -0
  320. package/package.json +450 -5
  321. package/rulesets/permdock.yaml +29 -0
  322. package/schemas/approval-request-v1.json +175 -0
  323. package/schemas/catalog-v1.json +535 -0
  324. package/schemas/openapi/oas-3.1.json +1298 -0
  325. package/schemas/openapi/oas-3.2.json +1516 -0
  326. package/schemas/openapi/overlay-1.1.json +79 -0
  327. package/schemas/supabase-claims-v1.json +144 -0
  328. package/schemas/supabase-manifest-v1.json +361 -0
  329. package/skills/permdock/SKILL.md +66 -0
  330. package/skills/permdock-agents/SKILL.md +77 -0
  331. package/skills/permdock-agents/references/runtimes.md +213 -0
  332. package/skills/permdock-approvals/SKILL.md +80 -0
  333. package/skills/permdock-approvals/references/stores-and-resume.md +94 -0
  334. package/skills/permdock-audit/SKILL.md +67 -0
  335. package/skills/permdock-credentials/SKILL.md +55 -0
  336. package/skills/permdock-credentials/references/tokens-keys-links.md +132 -0
  337. package/skills/permdock-data/SKILL.md +57 -0
  338. package/skills/permdock-data/references/queries-and-rls.md +62 -0
  339. package/skills/permdock-data/references/relationships.md +67 -0
  340. package/skills/permdock-tenancy/SKILL.md +80 -0
  341. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +89 -0
  342. package/skills/permdock-wire/SKILL.md +116 -0
  343. package/skills/permdock-wire/references/adapters.md +492 -0
  344. package/skills/permdock-wire/references/policy-options.md +45 -0
@@ -0,0 +1,239 @@
1
+ import { T as isForbiddenKey } from "./permissions-DVoF6dMt.js";
2
+ import { t as idText } from "./ids-DC4V4CFu.js";
3
+ //#region src/core/scopes.ts
4
+ const RESERVED = /* @__PURE__ */ new Set(["global", "resource"]);
5
+ const NAME = /^[a-z][a-z0-9_]*$/u;
6
+ /** A policy without `scopes` keeps the historical pair: `tenant`, and `team` inside it. */
7
+ const IMPLICIT = Object.freeze([Object.freeze({ name: "tenant" }), Object.freeze({
8
+ name: "team",
9
+ within: "tenant"
10
+ })]);
11
+ function fail(message) {
12
+ throw new Error(`PermDock: ${message}`);
13
+ }
14
+ /**
15
+ * Validates `definePolicy({ scopes })`. Declaration order is the scope order;
16
+ * every scope after the first names an earlier one in `within`, so the scopes
17
+ * form one tree rooted at the first and have no cycles.
18
+ * A scope named `tenant` is the first and `team` the second, so the aliases
19
+ * never point at two different scopes.
20
+ */
21
+ function defineScopes(input) {
22
+ if (input === void 0) return Object.freeze([]);
23
+ if (input === null || typeof input !== "object" || Array.isArray(input)) fail("definePolicy({ scopes }) must be an object");
24
+ const out = [];
25
+ const seen = /* @__PURE__ */ new Set();
26
+ for (const name of Object.keys(input)) {
27
+ if (isForbiddenKey(name) || RESERVED.has(name) || !NAME.test(name)) fail(`scope name '${name}' must be lower snake case and not global or resource`);
28
+ const declaration = input[name];
29
+ if (declaration === void 0 || declaration === null || typeof declaration !== "object" || typeof declaration.key !== "string" || declaration.key === "") fail(`scopes.${name} needs a key`);
30
+ const index = out.length;
31
+ if (name === "tenant" && index !== 0) fail("scope 'tenant' must be declared first");
32
+ if (name === "team" && index !== 1) fail("scope 'team' must be declared second");
33
+ const within = declaration.within;
34
+ if (within !== void 0 && !seen.has(within)) fail(`scopes.${name}.within names '${within}', which is not declared before it`);
35
+ if (name === "team" && within === void 0 && seen.has("tenant")) fail("scopes.team needs within: 'tenant'");
36
+ if (index > 0 && within === void 0) fail(`scopes.${name} needs within: every scope after the first sits inside an earlier one`);
37
+ seen.add(name);
38
+ out.push(Object.freeze(within === void 0 ? {
39
+ name,
40
+ key: declaration.key
41
+ } : {
42
+ name,
43
+ key: declaration.key,
44
+ within
45
+ }));
46
+ }
47
+ return Object.freeze(out);
48
+ }
49
+ /** The scopes a policy or snapshot evaluates with: the declared list, or the implicit pair. */
50
+ function scopeList(declared) {
51
+ return declared === void 0 || declared.length === 0 ? IMPLICIT : declared;
52
+ }
53
+ /** A declared name, or an alias: `tenant` is the first scope, `team` the second. */
54
+ function resolveScope(scopes, name) {
55
+ if (typeof name !== "string") return;
56
+ if (scopes.some((scope) => scope.name === name)) return name;
57
+ if (name === "tenant") return scopes[0]?.name;
58
+ if (name === "team") return scopes[1]?.name;
59
+ }
60
+ function findScope(scopes, name) {
61
+ return scopes.find((scope) => scope.name === name);
62
+ }
63
+ /** `name` and its ancestors, innermost first. */
64
+ function scopeChain(scopes, name) {
65
+ const chain = [];
66
+ let current = findScope(scopes, name);
67
+ while (current !== void 0 && !chain.includes(current.name)) {
68
+ chain.push(current.name);
69
+ current = current.within === void 0 ? void 0 : findScope(scopes, current.within);
70
+ }
71
+ return chain;
72
+ }
73
+ /** The scope the active tenant selects an instance of: the first declared one. */
74
+ function rootScope(scopes) {
75
+ return scopes[0]?.name;
76
+ }
77
+ /** The id a membership holds for `scope`: its own id, or the `within` entry of an ancestor. */
78
+ function scopeIdOf(membership, scope) {
79
+ if (membership.scope === scope) return membership.id;
80
+ const within = membership.within;
81
+ if (within === void 0 || !Object.hasOwn(within, scope)) return;
82
+ return within[scope];
83
+ }
84
+ /**
85
+ * A membership of the first scope itself (the only scope without a parent):
86
+ * its id is a tenant. Needs no scope list, for code that holds only a subject.
87
+ */
88
+ function rootMembershipId(membership) {
89
+ if (membership.scope !== void 0) return membership.within === void 0 ? membership.id : void 0;
90
+ return membership.team === void 0 ? membership.tenant : void 0;
91
+ }
92
+ /** The instance of the first scope a membership sits in; what the active tenant selects. */
93
+ function tenantOf(membership, scopes) {
94
+ const root = rootScope(scopes);
95
+ return root === void 0 ? void 0 : scopeIdOf(membership, root);
96
+ }
97
+ function isId(value) {
98
+ return typeof value === "string" && value !== "";
99
+ }
100
+ function roleList(value) {
101
+ if (!Array.isArray(value)) return;
102
+ return value.filter((item) => typeof item === "string");
103
+ }
104
+ /**
105
+ * The canonical form of one membership, or `undefined` when it is malformed
106
+ * (fail-closed: it grants nothing). A named membership is
107
+ * `{ scope, id, within }` with one `within` entry per ancestor of `scope`;
108
+ * the `tenant` / `team` input shape and alias names normalise to it. A
109
+ * resource membership keeps `on`. Shapes are exclusive.
110
+ */
111
+ function normalizeMembership(input, scopes) {
112
+ if (input === null || typeof input !== "object" || Array.isArray(input)) return;
113
+ const raw = input;
114
+ const roles = roleList(raw["roles"]);
115
+ if (roles === void 0) return;
116
+ const extra = {};
117
+ if (typeof raw["via"] === "string") extra.via = raw["via"];
118
+ if (typeof raw["expiresAt"] === "number") extra.expiresAt = raw["expiresAt"];
119
+ if (typeof raw["grantedBy"] === "string" && raw["grantedBy"] !== "") extra.grantedBy = raw["grantedBy"];
120
+ if (typeof raw["reason"] === "string" && raw["reason"] !== "") extra.reason = raw["reason"];
121
+ const eligible = roleList(raw["eligible"]);
122
+ if (eligible !== void 0 && eligible.length > 0) extra.eligible = Object.freeze([...eligible]);
123
+ const member = raw["member"] !== null && typeof raw["member"] === "object" && !Array.isArray(raw["member"]) ? raw["member"]["group"] : void 0;
124
+ if (typeof member === "string" && member !== "") extra.member = Object.freeze({ group: member });
125
+ if (raw["managedBy"] === "idp") extra.managedBy = "idp";
126
+ if (Array.isArray(raw["entitlements"])) {
127
+ const seats = raw["entitlements"].filter((item) => typeof item === "string" && item !== "");
128
+ if (seats.length > 0) extra.entitlements = Object.freeze(seats);
129
+ }
130
+ if (raw["keep"] !== void 0) {
131
+ if (!Array.isArray(raw["keep"]) || !raw["keep"].every((key) => typeof key === "string" && key !== "")) return;
132
+ extra.keep = Object.freeze([...raw["keep"]]);
133
+ }
134
+ const named = raw["scope"] !== void 0 || raw["id"] !== void 0;
135
+ const legacy = raw["tenant"] !== void 0 || raw["team"] !== void 0;
136
+ const on = raw["on"];
137
+ if (on !== void 0) {
138
+ if (named || legacy || on === null || typeof on !== "object") return;
139
+ const target = on;
140
+ const targetId = idText(target["id"]);
141
+ if (!isId(target["resource"]) || targetId === void 0) return;
142
+ return Object.freeze({
143
+ on: Object.freeze({
144
+ resource: target["resource"],
145
+ id: targetId
146
+ }),
147
+ roles: Object.freeze([...roles]),
148
+ ...extra
149
+ });
150
+ }
151
+ let scope;
152
+ let id;
153
+ let within = {};
154
+ if (named) {
155
+ if (legacy) return;
156
+ scope = resolveScope(scopes, raw["scope"]);
157
+ id = raw["id"];
158
+ if (raw["within"] !== void 0) {
159
+ if (raw["within"] === null || typeof raw["within"] !== "object" || Array.isArray(raw["within"])) return;
160
+ within = raw["within"];
161
+ }
162
+ } else if (raw["team"] !== void 0) {
163
+ scope = resolveScope(scopes, "team");
164
+ id = raw["team"];
165
+ const parent = resolveScope(scopes, "tenant");
166
+ if (raw["tenant"] !== void 0 && parent !== void 0) within = { [parent]: raw["tenant"] };
167
+ } else if (raw["tenant"] === void 0) return;
168
+ else {
169
+ scope = resolveScope(scopes, "tenant");
170
+ id = raw["tenant"];
171
+ }
172
+ const text = idText(id);
173
+ if (scope === void 0 || text === void 0) return;
174
+ const ancestors = scopeChain(scopes, scope).slice(1);
175
+ const resolved = {};
176
+ for (const key of Object.keys(within)) {
177
+ if (isForbiddenKey(key)) return;
178
+ const name = resolveScope(scopes, key);
179
+ const value = idText(within[key]);
180
+ if (name !== void 0 && ancestors.includes(name) && value !== void 0) resolved[name] = value;
181
+ }
182
+ if (ancestors.some((name) => resolved[name] === void 0)) return;
183
+ return Object.freeze({
184
+ scope,
185
+ id: text,
186
+ ...ancestors.length === 0 ? {} : { within: Object.freeze(resolved) },
187
+ roles: Object.freeze([...roles]),
188
+ ...extra
189
+ });
190
+ }
191
+ function normalizeMemberships(input, scopes) {
192
+ if (!Array.isArray(input)) return [];
193
+ const out = [];
194
+ for (const item of input) {
195
+ const membership = normalizeMembership(item, scopes);
196
+ if (membership !== void 0) out.push(membership);
197
+ }
198
+ return out;
199
+ }
200
+ /**
201
+ * A subject's memberships in canonical form. Resolved subjects already are;
202
+ * this accepts a hand-built subject passed straight to a condition evaluator
203
+ * or compiler.
204
+ */
205
+ function subjectMemberships(memberships, scopes) {
206
+ const list = memberships ?? [];
207
+ return list.some((membership) => membership.tenant !== void 0 || membership.team !== void 0) ? normalizeMemberships(list, scopes) : list;
208
+ }
209
+ /**
210
+ * Whether a named membership applies under the active tenant: a membership
211
+ * whose chain includes the first scope counts only inside the active tenant.
212
+ */
213
+ function activeFor(membership, scopes, active) {
214
+ const root = rootScope(scopes);
215
+ if (root === void 0 || membership.scope === void 0 || !scopeChain(scopes, membership.scope).includes(root)) return true;
216
+ return active !== void 0 && scopeIdOf(membership, root) === active;
217
+ }
218
+ /** Whether `membership` counts for `key`: always, unless a suspension limits it to `keep`. */
219
+ function keepsPermission(membership, key) {
220
+ return membership.keep === void 0 || membership.keep.includes(key);
221
+ }
222
+ /**
223
+ * The subject as it stands for one permission: memberships a suspension
224
+ * limits to other permissions are left out. The same object when none is.
225
+ */
226
+ function subjectForPermission(subject, key) {
227
+ const principal = subject.principal;
228
+ const memberships = principal?.memberships;
229
+ if (principal === null || memberships === void 0 || memberships.every((membership) => keepsPermission(membership, key))) return subject;
230
+ return {
231
+ ...subject,
232
+ principal: {
233
+ ...principal,
234
+ memberships: memberships.filter((membership) => keepsPermission(membership, key))
235
+ }
236
+ };
237
+ }
238
+ //#endregion
239
+ export { normalizeMemberships as a, rootScope as c, scopeList as d, subjectForPermission as f, keepsPermission as i, scopeChain as l, tenantOf as m, defineScopes as n, resolveScope as o, subjectMemberships as p, findScope as r, rootMembershipId as s, activeFor as t, scopeIdOf as u };
@@ -0,0 +1,134 @@
1
+ import { $t as CredentialPrincipal, Ct as SettingsSource, Kt as Credential, Nr as Principal, Pr as Subject, Yn as Decision, at as DecisionSink, et as AuthEvent, jt as SubjectResolver, nt as CredentialVerifier } from "../policy-CFZAr_Al.js";
2
+ import { f as Permission, m as PermissionTree } from "../permissions-DttnqqxC.js";
3
+ import { n as PermDockValidationError, t as PROBLEM_BASE } from "../validation-error-CtF23SrC.js";
4
+ import { t as Disclosure } from "../problem-details-CkL50EEf.js";
5
+ import { a as ProblemDetails, i as PermDockRevokedError, n as PermDockApprovalRequiredError, r as PermDockDeniedError, t as ApprovalHint } from "../errors-DAqevpNk.js";
6
+ import { i as memoryRevocationFeed, n as RevocationFeed, t as RevocationEvent } from "../revocations-Cy06JNx2.js";
7
+ import { A as ConnectionOptions, C as invalidSignatureResponse, D as tenantScope, E as TenantScope, O as Connection, S as invalidSignatureProblem, T as TenantOption, _ as WebBotAuthKeyLookup, a as ScopeGuard, b as WebBotAuthVerifier, c as ServerPermDock, d as createPermDock, f as createServerKernel, g as WebBotAuthJwk, h as InvalidSignatureError, i as ProtectOptions, k as ConnectionData, l as ServerPermDockOptions, m as DiscoverViaSignatureAgentOptions, n as OpenApiHooks, o as ServerKernel, p as createEvaluationsHandler, r as OperationScopes, s as ServerKernelOptions, t as Guard, u as SnapshotQuery, v as WebBotAuthKeys, w as verifyWebBotAuth, x as discoverViaSignatureAgent, y as WebBotAuthOptions } from "../create-BWyx3jua.js";
8
+ import { a as snapshotHeaders, i as cacheLifeFor, n as SnapshotHeaders, o as snapshotTag, r as SnapshotHeadersOptions, t as CacheLifeForOptions } from "../snapshot-cache-DwQH8ufx.js";
9
+ //#region src/server/credentials.d.ts
10
+ /** `on('auth')` causes for an API key that resolves to the anonymous subject. */
11
+ type ApiKeyFailureCause = "malformed" | "unknown-credential" | "invalid-claims" | "expired" | "credential-policy" | "credential-revoked" | "owner-unavailable";
12
+ /** An opaque key split at its last `_`: `pdk_<id>_<secret><checksum>`. */
13
+ type ApiKeyParts = {
14
+ readonly id: string;
15
+ readonly secret: string;
16
+ };
17
+ /** What an application stores per key: the credential and the key's hash, never the key. */
18
+ type StoredCredential = {
19
+ readonly credential: Credential;
20
+ readonly hash: string;
21
+ };
22
+ /**
23
+ * Splits `pdk_<id>_<secret><checksum>`: `id` is 1 to 128 of `A-Z a-z 0-9 _ -`,
24
+ * the secret 43 base62 characters and the checksum the 6 base62 characters of
25
+ * the CRC-32 of everything before it. Anything else, including a checksum
26
+ * that does not match, is `undefined`.
27
+ */
28
+ export declare function parseApiKey(key: unknown): ApiKeyParts | undefined;
29
+ /**
30
+ * A fresh `pdk_<id>_<secret><checksum>`: a 43-character base62 secret (about
31
+ * 256 bits) and a 6-character CRC-32 checksum, so a secret scanner can tell a
32
+ * real key from a lookalike without calling the issuer.
33
+ */
34
+ export declare function generateApiKey(id: string): string;
35
+ /** base64url SHA-256 of the whole key, the value to store and compare. */
36
+ export declare function hashApiKey(key: string): Promise<string>;
37
+ /**
38
+ * A `CredentialVerifier` over the application's own table: `find` looks a
39
+ * key up by the id in it, and the key's hash is compared in constant time.
40
+ * A record that is not a valid v1 credential, or whose id is not the key's,
41
+ * verifies to `null`. A throwing `find` propagates, so the resolver denies
42
+ * with `source-threw`.
43
+ */
44
+ export declare function apiKeyVerifier(options: {
45
+ readonly find: (id: string) => StoredCredential | null | undefined | Promise<StoredCredential | null | undefined>;
46
+ /** Passed through as the verifier's `touch`, for a `lastUsedAt` column. */
47
+ readonly touch?: CredentialVerifier["touch"];
48
+ }): CredentialVerifier;
49
+ type MemoryCredentials = CredentialVerifier & {
50
+ /** Stores `credential` and returns its key; the key is never stored. */
51
+ issue(credential: Credential): Promise<string>;
52
+ /** A new secret for the same id; the old key stops verifying at once. */
53
+ rotate(id: string): Promise<string | undefined>;
54
+ /** `true` when a credential was removed. */
55
+ revoke(id: string): boolean;
56
+ list(): readonly Credential[];
57
+ /** When `touch` last recorded a use of `id`, in Unix seconds. */
58
+ lastUsedAt(id: string): number | undefined;
59
+ };
60
+ /**
61
+ * In-process credentials for tests and single-replica apps. `issue`, `rotate`
62
+ * and `revoke` write `credential` events to `sink` when one is given.
63
+ */
64
+ export declare function memoryCredentials(options?: {
65
+ readonly sink?: DecisionSink;
66
+ readonly source?: string;
67
+ }): MemoryCredentials;
68
+ type ApiKeySubjectOptions = {
69
+ readonly verifier: CredentialVerifier;
70
+ /** The definitions, to turn the credential's permission keys into its delegation. */
71
+ readonly permissions: PermissionTree;
72
+ /**
73
+ * The live owner of a user-bound key: roles and memberships as of this
74
+ * request. `null` (a deleted or suspended user) is anonymous. Without it
75
+ * the owner is `{ id, kind: 'user' }` and its rights come from the
76
+ * `memberships` source passed to `createPermDock`.
77
+ */
78
+ readonly owner?: (id: string) => Principal | null | undefined | Promise<Principal | null | undefined>;
79
+ /** `true` when the application revoked the credential id; a throw denies. */
80
+ readonly revoked?: (id: string) => boolean | Promise<boolean>;
81
+ /**
82
+ * The tenant `credentials` policy, checked on every use so a tightened
83
+ * policy reaches existing keys: a service key's own tenant, a user key's
84
+ * the `tenant` the resolver is called with. A throw denies.
85
+ */
86
+ readonly settings?: SettingsSource;
87
+ /** Receives a `credential` event with operation `used` for sampled uses. */
88
+ readonly sink?: DecisionSink;
89
+ /** Fraction of uses reported to `sink`, in `(0, 1]`; defaults to 1. */
90
+ readonly sample?: number;
91
+ /** The `source` of `used` events; defaults to `permdock`. */
92
+ readonly source?: string;
93
+ readonly onAuth?: (event: AuthEvent) => void;
94
+ readonly requestId?: string;
95
+ };
96
+ /**
97
+ * A `SubjectResolver` for opaque API keys (`pdk_<id>_<secret>`): each call
98
+ * resolves a key into the subject its credential acts as. Every failure (a
99
+ * malformed or unknown key, a record that is not a valid credential, expiry,
100
+ * a tenant policy the key breaks, a revoked id, a missing owner, a throwing
101
+ * verifier or callback) is the anonymous subject, never a throw.
102
+ */
103
+ export declare function subjectFromApiKey(options: ApiKeySubjectOptions): SubjectResolver<string | undefined | null, CredentialPrincipal>;
104
+ //#endregion
105
+ //#region src/server/map-error.d.ts
106
+ type ProblemFromErrorOptions = {
107
+ readonly instance?: string;
108
+ /** Whether the request carried credentials, which picks the `401` challenge. */
109
+ readonly credentials?: boolean;
110
+ };
111
+ /**
112
+ * Turns a thrown PermDock error into its Problem Details response, with the
113
+ * status, `WWW-Authenticate` and rate-limit fields `protect` sends for the
114
+ * same decision. Returns `undefined` for anything else so the framework's own
115
+ * error path takes over.
116
+ */
117
+ export declare function problemFromError(error: unknown, options?: ProblemFromErrorOptions): Response | undefined;
118
+ //#endregion
119
+ //#region src/server/problem.d.ts
120
+ export declare function wwwAuthenticate(decision: Decision, permission: Permission | undefined, credentials?: boolean): string | undefined;
121
+ export declare function problemResponse(details: ProblemDetails, permission?: Permission, decision?: Decision, extra?: Readonly<Record<string, string>>, credentials?: boolean): Response;
122
+ export declare function validationProblem(detail: string): Response;
123
+ export declare function problemFromDecision(decision: Decision, permission: Permission, subject: Subject | undefined, options?: {
124
+ readonly instance?: string;
125
+ readonly approval?: ApprovalHint;
126
+ /** `'hide'` on a loaded row: a denial answers as `404` `/not-found`. */
127
+ readonly disclosure?: Disclosure;
128
+ /** Whether the request carried credentials, which picks the `401` challenge. */
129
+ readonly credentials?: boolean;
130
+ /** The scope an `insufficient_scope` challenge names; the permission's own scope when absent. */
131
+ readonly scope?: string;
132
+ }): Response;
133
+ //#endregion
134
+ export { type ApiKeyFailureCause, type ApiKeyParts, type ApiKeySubjectOptions, type ApprovalHint, type CacheLifeForOptions, type Connection, type ConnectionData, type ConnectionOptions, type DiscoverViaSignatureAgentOptions, type Guard, InvalidSignatureError, type MemoryCredentials, type OpenApiHooks, type OperationScopes, PROBLEM_BASE, PermDockApprovalRequiredError, PermDockDeniedError, PermDockRevokedError, PermDockValidationError, type ProblemFromErrorOptions, type ProtectOptions, type RevocationEvent, type RevocationFeed, type ScopeGuard, type ServerKernel, type ServerKernelOptions, type ServerPermDock, type ServerPermDockOptions, type SnapshotHeaders, type SnapshotHeadersOptions, type SnapshotQuery, type StoredCredential, type TenantOption, type TenantScope, type WebBotAuthJwk, type WebBotAuthKeyLookup, type WebBotAuthKeys, type WebBotAuthOptions, type WebBotAuthVerifier, cacheLifeFor, createEvaluationsHandler, createPermDock, createServerKernel, discoverViaSignatureAgent, invalidSignatureProblem, invalidSignatureResponse, memoryRevocationFeed, snapshotHeaders, snapshotTag, tenantScope, verifyWebBotAuth };
@@ -0,0 +1,11 @@
1
+ import { n as PermDockValidationError, t as PROBLEM_BASE } from "../validation-error-CnjZ4t92.js";
2
+ import { n as PermDockDeniedError, r as PermDockRevokedError, t as PermDockApprovalRequiredError } from "../errors-Cz5zn0mW.js";
3
+ import { t as memoryRevocationFeed } from "../revocations-Bhr0aMIC.js";
4
+ import { d as wwwAuthenticate, o as problemFromDecision, s as problemResponse, u as validationProblem } from "../problem-B43ztKIm.js";
5
+ import { n as snapshotHeaders, r as snapshotTag, t as cacheLifeFor } from "../snapshot-cache-D6jLelln.js";
6
+ import { a as invalidSignatureProblem, i as discoverViaSignatureAgent, n as createEvaluationsHandler, o as invalidSignatureResponse, r as InvalidSignatureError, s as verifyWebBotAuth } from "../evaluations-tz6D9A2G.js";
7
+ import { n as tenantScope } from "../adapter-context-mlWsVGGg.js";
8
+ import { n as createServerKernel, t as createPermDock } from "../create-CPhDL6qx.js";
9
+ import { a as parseApiKey, i as memoryCredentials, n as generateApiKey, o as subjectFromApiKey, r as hashApiKey, t as apiKeyVerifier } from "../server-DlZ28Oz5.js";
10
+ import { t as problemFromError } from "../map-error-pgUWNNtG.js";
11
+ export { InvalidSignatureError, PROBLEM_BASE, PermDockApprovalRequiredError, PermDockDeniedError, PermDockRevokedError, PermDockValidationError, apiKeyVerifier, cacheLifeFor, createEvaluationsHandler, createPermDock, createServerKernel, discoverViaSignatureAgent, generateApiKey, hashApiKey, invalidSignatureProblem, invalidSignatureResponse, memoryCredentials, memoryRevocationFeed, parseApiKey, problemFromDecision, problemFromError, problemResponse, snapshotHeaders, snapshotTag, subjectFromApiKey, tenantScope, validationProblem, verifyWebBotAuth, wwwAuthenticate };
@@ -0,0 +1,268 @@
1
+ import { t as compact } from "./compact-y6vfu2_f.js";
2
+ import { t as bytesToBase64Url } from "./sha256-BO0swRXL.js";
3
+ import { n as isThenable } from "./thenable-okc2xDsj.js";
4
+ import { t as anonymousSubject } from "./subject-C6dLxqHU.js";
5
+ import { r as credentialEvent } from "./sink-Bd9PSyfq.js";
6
+ import { i as credentialTenant, n as credentialPolicyViolation, o as parseCredential, r as credentialSubject } from "./credential-8P81ff4u.js";
7
+ //#region src/server/credentials.ts
8
+ const PREFIX = "pdk_";
9
+ const ID = /^[\w-]{1,128}$/u;
10
+ const TAIL = /^[A-Za-z\d]{49}$/u;
11
+ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
12
+ const SECRET_LENGTH = 43;
13
+ const CHECKSUM_LENGTH = 6;
14
+ const MAX_KEY = 182;
15
+ const CRC_TABLE = (() => {
16
+ const table = /* @__PURE__ */ new Uint32Array(256);
17
+ for (let index = 0; index < 256; index += 1) {
18
+ let value = index;
19
+ for (let bit = 0; bit < 8; bit += 1) value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
20
+ table[index] = value >>> 0;
21
+ }
22
+ return table;
23
+ })();
24
+ /** CRC-32 (IEEE 802.3) of the key's ASCII bytes, as 6 base62 characters. */
25
+ function checksum(text) {
26
+ let crc = 4294967295;
27
+ for (let index = 0; index < text.length; index += 1) crc = (CRC_TABLE[(crc ^ (text.codePointAt(index) ?? 0)) & 255] ?? 0) ^ crc >>> 8;
28
+ let value = (crc ^ 4294967295) >>> 0;
29
+ let out = "";
30
+ for (let index = 0; index < CHECKSUM_LENGTH; index += 1) {
31
+ out = (ALPHABET[value % 62] ?? "") + out;
32
+ value = Math.floor(value / 62);
33
+ }
34
+ return out;
35
+ }
36
+ /**
37
+ * Splits `pdk_<id>_<secret><checksum>`: `id` is 1 to 128 of `A-Z a-z 0-9 _ -`,
38
+ * the secret 43 base62 characters and the checksum the 6 base62 characters of
39
+ * the CRC-32 of everything before it. Anything else, including a checksum
40
+ * that does not match, is `undefined`.
41
+ */
42
+ function parseApiKey(key) {
43
+ if (typeof key !== "string" || key.length > MAX_KEY || !key.startsWith(PREFIX)) return;
44
+ const body = key.slice(4);
45
+ const cut = body.lastIndexOf("_");
46
+ if (cut <= 0) return;
47
+ const id = body.slice(0, cut);
48
+ const tail = body.slice(cut + 1);
49
+ if (!ID.test(id) || !TAIL.test(tail)) return;
50
+ const secret = tail.slice(0, SECRET_LENGTH);
51
+ return tail.slice(SECRET_LENGTH) === checksum(`${PREFIX}${id}_${secret}`) ? {
52
+ id,
53
+ secret
54
+ } : void 0;
55
+ }
56
+ /**
57
+ * A fresh `pdk_<id>_<secret><checksum>`: a 43-character base62 secret (about
58
+ * 256 bits) and a 6-character CRC-32 checksum, so a secret scanner can tell a
59
+ * real key from a lookalike without calling the issuer.
60
+ */
61
+ function generateApiKey(id) {
62
+ if (!ID.test(id)) throw new TypeError("PermDock: an API key id is 1 to 128 of A-Z, a-z, 0-9, _ and -");
63
+ let secret = "";
64
+ const bytes = /* @__PURE__ */ new Uint8Array(64);
65
+ while (secret.length < SECRET_LENGTH) {
66
+ globalThis.crypto.getRandomValues(bytes);
67
+ for (const byte of bytes) if (byte < 248 && secret.length < SECRET_LENGTH) secret += ALPHABET[byte % 62];
68
+ }
69
+ const key = `${PREFIX}${id}_${secret}`;
70
+ return `${key}${checksum(key)}`;
71
+ }
72
+ /** base64url SHA-256 of the whole key, the value to store and compare. */
73
+ async function hashApiKey(key) {
74
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(key));
75
+ return bytesToBase64Url(new Uint8Array(digest));
76
+ }
77
+ function sameHash(left, right) {
78
+ if (left.length !== right.length) return false;
79
+ let difference = 0;
80
+ for (let index = 0; index < left.length; index += 1) difference |= (left.codePointAt(index) ?? 0) ^ (right.codePointAt(index) ?? 0);
81
+ return difference === 0;
82
+ }
83
+ /**
84
+ * A `CredentialVerifier` over the application's own table: `find` looks a
85
+ * key up by the id in it, and the key's hash is compared in constant time.
86
+ * A record that is not a valid v1 credential, or whose id is not the key's,
87
+ * verifies to `null`. A throwing `find` propagates, so the resolver denies
88
+ * with `source-threw`.
89
+ */
90
+ function apiKeyVerifier(options) {
91
+ return Object.freeze({
92
+ ...options.touch === void 0 ? {} : { touch: options.touch },
93
+ async verify(secret) {
94
+ const parts = parseApiKey(secret);
95
+ if (parts === void 0) return null;
96
+ const stored = await options.find(parts.id);
97
+ if (stored === null || stored === void 0) return null;
98
+ const credential = parseCredential(stored.credential);
99
+ if (credential === void 0 || credential.id !== parts.id || typeof stored.hash !== "string" || !sameHash(await hashApiKey(secret), stored.hash)) return null;
100
+ return credential;
101
+ }
102
+ });
103
+ }
104
+ function write(sink, event) {
105
+ try {
106
+ const written = sink.write(event);
107
+ if (isThenable(written)) Promise.resolve(written).catch(() => void 0);
108
+ } catch {}
109
+ }
110
+ /**
111
+ * In-process credentials for tests and single-replica apps. `issue`, `rotate`
112
+ * and `revoke` write `credential` events to `sink` when one is given.
113
+ */
114
+ function memoryCredentials(options = {}) {
115
+ const records = /* @__PURE__ */ new Map();
116
+ const used = /* @__PURE__ */ new Map();
117
+ const verifier = apiKeyVerifier({ find: (id) => records.get(id) });
118
+ const emit = (operation, credential) => {
119
+ if (options.sink !== void 0) write(options.sink, [credentialEvent(compact({
120
+ operation,
121
+ credential,
122
+ source: options.source
123
+ }))]);
124
+ };
125
+ const store = async (credential) => {
126
+ const key = generateApiKey(credential.id);
127
+ records.set(credential.id, {
128
+ credential,
129
+ hash: await hashApiKey(key)
130
+ });
131
+ return key;
132
+ };
133
+ return Object.freeze({
134
+ verify: (secret) => verifier.verify(secret),
135
+ touch(id, at) {
136
+ if (records.has(id)) used.set(id, at);
137
+ },
138
+ lastUsedAt(id) {
139
+ return used.get(id);
140
+ },
141
+ async issue(input) {
142
+ const credential = parseCredential(input);
143
+ if (credential === void 0) throw new TypeError("PermDock: not a valid v1 credential");
144
+ if (records.has(credential.id)) throw new Error(`PermDock: credential '${credential.id}' exists`);
145
+ const key = await store(credential);
146
+ emit("created", credential);
147
+ return key;
148
+ },
149
+ async rotate(id) {
150
+ const current = records.get(id);
151
+ if (current === void 0) return;
152
+ const key = await store(current.credential);
153
+ emit("rotated", current.credential);
154
+ return key;
155
+ },
156
+ revoke(id) {
157
+ const current = records.get(id);
158
+ if (current === void 0) return false;
159
+ records.delete(id);
160
+ used.delete(id);
161
+ emit("revoked", current.credential);
162
+ return true;
163
+ },
164
+ list() {
165
+ return [...records.values()].map((record) => record.credential);
166
+ }
167
+ });
168
+ }
169
+ async function checkStores(options, credential, active) {
170
+ try {
171
+ const tenant = credentialTenant(credential, active);
172
+ const policy = options.settings === void 0 || tenant === void 0 ? void 0 : (await options.settings.settingsFor(tenant))?.credentials;
173
+ if (policy !== void 0 && credentialPolicyViolation(credential, policy) !== void 0) return {
174
+ ok: false,
175
+ cause: "credential-policy"
176
+ };
177
+ if (options.revoked !== void 0 && await options.revoked(credential.id)) return {
178
+ ok: false,
179
+ cause: "credential-revoked"
180
+ };
181
+ if (credential.kind !== "user" || options.owner === void 0) return { ok: true };
182
+ const owner = await options.owner(credential.principal);
183
+ if (owner === null || owner === void 0) return {
184
+ ok: false,
185
+ cause: "owner-unavailable"
186
+ };
187
+ return {
188
+ ok: true,
189
+ owner
190
+ };
191
+ } catch {
192
+ return { ok: false };
193
+ }
194
+ }
195
+ function touch(verifier, id) {
196
+ if (verifier.touch === void 0) return;
197
+ try {
198
+ const touched = verifier.touch(id, Math.floor(Date.now() / 1e3));
199
+ if (isThenable(touched)) Promise.resolve(touched).catch(() => void 0);
200
+ } catch {}
201
+ }
202
+ function sampled(sample) {
203
+ if (sample === 1) return true;
204
+ const draw = /* @__PURE__ */ new Uint32Array(1);
205
+ globalThis.crypto.getRandomValues(draw);
206
+ return (draw[0] ?? 0) / 4294967296 < sample;
207
+ }
208
+ function reportUse(options, credential) {
209
+ const sample = options.sample ?? 1;
210
+ if (options.sink === void 0 || !Number.isFinite(sample) || sample <= 0 || sample > 1 || !sampled(sample)) return;
211
+ write(options.sink, [credentialEvent(compact({
212
+ operation: "used",
213
+ credential,
214
+ source: options.source,
215
+ sample
216
+ }))]);
217
+ }
218
+ /**
219
+ * A `SubjectResolver` for opaque API keys (`pdk_<id>_<secret>`): each call
220
+ * resolves a key into the subject its credential acts as. Every failure (a
221
+ * malformed or unknown key, a record that is not a valid credential, expiry,
222
+ * a tenant policy the key breaks, a revoked id, a missing owner, a throwing
223
+ * verifier or callback) is the anonymous subject, never a throw.
224
+ */
225
+ function subjectFromApiKey(options) {
226
+ return (key, resolve) => resolveApiKey(key, options, resolve?.tenant);
227
+ }
228
+ function anonymous() {
229
+ return anonymousSubject();
230
+ }
231
+ async function resolveApiKey(key, options, tenant) {
232
+ if (typeof key !== "string" || key.length === 0) return anonymous();
233
+ const deny = (cause, reason = "invalid-token") => {
234
+ try {
235
+ options.onAuth?.(compact({
236
+ reason,
237
+ cause,
238
+ source: "api-key",
239
+ requestId: options.requestId
240
+ }));
241
+ } catch {}
242
+ return anonymous();
243
+ };
244
+ const parts = parseApiKey(key);
245
+ if (parts === void 0) return deny("malformed");
246
+ let verified;
247
+ try {
248
+ verified = await options.verifier.verify(key);
249
+ } catch {
250
+ return deny(void 0, "source-threw");
251
+ }
252
+ if (verified === null || verified === void 0) return deny("unknown-credential");
253
+ const credential = parseCredential(verified);
254
+ if (credential === void 0 || credential.id !== parts.id) return deny("invalid-claims");
255
+ if (credential.expiresAt !== void 0 && credential.expiresAt <= Date.now() / 1e3) return deny("expired");
256
+ const checked = await checkStores(options, credential, tenant);
257
+ if (!checked.ok) return checked.cause === void 0 ? deny(void 0, "source-threw") : deny(checked.cause);
258
+ const subject = credentialSubject(credential, compact({
259
+ permissions: options.permissions,
260
+ owner: checked.owner
261
+ }));
262
+ if (subject.principal === null) return deny("owner-unavailable");
263
+ reportUse(options, credential);
264
+ touch(options.verifier, credential.id);
265
+ return subject;
266
+ }
267
+ //#endregion
268
+ export { parseApiKey as a, memoryCredentials as i, generateApiKey as n, subjectFromApiKey as o, hashApiKey as r, apiKeyVerifier as t };