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,975 @@
1
+ import { n as isReadonlyArray, r as sole, t as compact } from "./compact-y6vfu2_f.js";
2
+ import { n as freezeDeep } from "./freeze-CrU8nQeX.js";
3
+ import { E as ownGet, T as isForbiddenKey, i as findPermission } from "./permissions-DVoF6dMt.js";
4
+ import { c as isConditionRef, i as resolveConditionRef, s as isConditionDate, u as parentHop } from "./refs-KYb4ntaz.js";
5
+ import { t as expiredAt } from "./expiry-BoBJ8Tvc.js";
6
+ import { n as sameId } from "./ids-DC4V4CFu.js";
7
+ import { d as scopeList, l as scopeChain, m as tenantOf, o as resolveScope, p as subjectMemberships, r as findScope, t as activeFor, u as scopeIdOf } from "./scopes-CVdPrWN-.js";
8
+ import { E as listRoles, T as listPlans, _ as resourceRoleCondition } from "./grantee-Cv5Sogfv.js";
9
+ import { a as isMembershipExpired, c as membershipField, g as tenantsOf, i as inTeam, t as isActive } from "./validity-uOp8UpYn.js";
10
+ //#region src/conditions/evaluate.ts
11
+ function resolveRef(ref, subject) {
12
+ return resolveConditionRef(ref, subject);
13
+ }
14
+ function unwrap(value, subject) {
15
+ if (isConditionRef(value)) return resolveRef(value.ref, subject);
16
+ if (isConditionDate(value)) return new Date(isoInstant(value.date) ?? NaN);
17
+ if (isReadonlyArray(value)) return value.map((item) => unwrap(item, subject));
18
+ return value;
19
+ }
20
+ const ISO_INSTANT = /^(\d{4}-\d{2}-\d{2})(?:[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)(Z|[+-]\d{2}(?::?\d{2})?)?)?$/u;
21
+ function isoInstant(value) {
22
+ const match = ISO_INSTANT.exec(value);
23
+ if (match === null) return;
24
+ const [, day, time, zone] = match;
25
+ let offset = zone ?? (time === void 0 ? "" : "Z");
26
+ if (/^[+-]\d{2}$/u.test(offset)) offset = `${offset}:00`;
27
+ else if (/^[+-]\d{4}$/u.test(offset)) offset = `${offset.slice(0, 3)}:${offset.slice(3)}`;
28
+ const parsed = Date.parse(time === void 0 ? `${day}` : `${day}T${time}${offset}`);
29
+ return Number.isNaN(parsed) ? void 0 : parsed;
30
+ }
31
+ function instantOf(value) {
32
+ if (value instanceof Date) {
33
+ const time = value.getTime();
34
+ return Number.isNaN(time) ? void 0 : time;
35
+ }
36
+ if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
37
+ if (typeof value === "string") return isoInstant(value);
38
+ }
39
+ function comparablePair(left, right) {
40
+ if (left instanceof Date || right instanceof Date) {
41
+ const a = instantOf(left);
42
+ const b = instantOf(right);
43
+ return a === void 0 || b === void 0 ? void 0 : [a, b];
44
+ }
45
+ const kind = typeof left;
46
+ if (kind === typeof right && (kind === "string" || kind === "number" || kind === "boolean")) return [left, right];
47
+ }
48
+ function compare(op, left, right) {
49
+ if (left === void 0 || left === null || right === void 0 || right === null) return false;
50
+ const comparable = comparablePair(left, right);
51
+ if (comparable === void 0) return false;
52
+ const [a, b] = comparable;
53
+ switch (op) {
54
+ case "eq": return a === b;
55
+ case "ne": return a !== b;
56
+ case "gt": return a > b;
57
+ case "gte": return a >= b;
58
+ case "lt": return a < b;
59
+ case "lte": return a <= b;
60
+ default:
61
+ /* v8 ignore next */
62
+ return false;
63
+ }
64
+ }
65
+ function contains(left, right) {
66
+ if (left === void 0 || left === null || right === void 0 || right === null) return false;
67
+ if (typeof left === "string" && typeof right === "string") return left.includes(right);
68
+ if (Array.isArray(left)) return left.includes(right);
69
+ return false;
70
+ }
71
+ function inList(left, right) {
72
+ if (left === void 0 || left === null || !Array.isArray(right)) return false;
73
+ return right.includes(left);
74
+ }
75
+ function matchesParentHop(data, parents, on) {
76
+ for (const parent of parents ?? []) {
77
+ const hop = parentHop(parent);
78
+ if (hop.resource !== void 0 && hop.resource !== on.resource) continue;
79
+ if (sameId(ownGet(data, hop.field), on.id)) return true;
80
+ }
81
+ return false;
82
+ }
83
+ function evaluateMemberOf(condition, data, subject, now, scopes) {
84
+ if (data === null || typeof data !== "object") return false;
85
+ const rowValue = ownGet(data, condition.field);
86
+ if (rowValue === void 0 || rowValue === null) return false;
87
+ const memberships = subjectMemberships(subject.principal?.memberships, scopes);
88
+ const wanted = new Set(condition.roles);
89
+ const active = subject.principal?.tenant === "" ? void 0 : subject.principal?.tenant;
90
+ const scope = condition.scope === "resource" ? void 0 : resolveScope(scopes, condition.scope);
91
+ if (condition.scope !== "resource" && scope === void 0) return false;
92
+ for (const membership of memberships) {
93
+ if (expiredAt(membership, now)) continue;
94
+ if (wanted.size > 0 && !membership.roles.some((role) => wanted.has(role))) continue;
95
+ if (scope !== void 0) {
96
+ if (membership.scope !== scope || !sameId(membership.id, rowValue)) continue;
97
+ const tenant = tenantOf(membership, scopes);
98
+ if (active !== void 0 && tenant !== void 0 && tenant !== active) continue;
99
+ return true;
100
+ }
101
+ if (membership.on === void 0) continue;
102
+ if (condition.resource !== void 0 && membership.on.resource !== condition.resource) {
103
+ if (matchesParentHop(data, condition.parents, membership.on)) return true;
104
+ continue;
105
+ }
106
+ if (sameId(membership.on.id, rowValue)) return true;
107
+ if (matchesParentHop(data, condition.parents, membership.on)) return true;
108
+ }
109
+ return false;
110
+ }
111
+ function evaluateCondition(condition, data, subject, now = Date.now() / 1e3, scopes = scopeList(void 0), related, onOpaque) {
112
+ switch (condition.op) {
113
+ case "and": return condition.conditions.every((child) => evaluateCondition(child, data, subject, now, scopes, related, onOpaque));
114
+ case "or": return condition.conditions.some((child) => evaluateCondition(child, data, subject, now, scopes, related, onOpaque));
115
+ case "not": return !evaluateCondition(condition.condition, data, subject, now, scopes, related, onOpaque);
116
+ case "isNull": {
117
+ if (data === null || typeof data !== "object") return false;
118
+ const value = ownGet(data, condition.field);
119
+ const isNull = value === null || value === void 0;
120
+ return condition.value ? isNull : !isNull;
121
+ }
122
+ case "in":
123
+ case "notIn": {
124
+ if (data === null || typeof data !== "object") return false;
125
+ const left = ownGet(data, condition.field);
126
+ const matched = inList(left, unwrap(condition.value, subject));
127
+ return condition.op === "in" ? matched : !matched && left !== void 0 && left !== null;
128
+ }
129
+ case "contains":
130
+ if (data === null || typeof data !== "object") return false;
131
+ return contains(ownGet(data, condition.field), unwrap(condition.value, subject));
132
+ case "eq":
133
+ case "ne":
134
+ case "gt":
135
+ case "gte":
136
+ case "lt":
137
+ case "lte":
138
+ if (condition.field === "_" && condition.op === "eq") return condition.value === true;
139
+ if (data === null || typeof data !== "object") return false;
140
+ return compare(condition.op, ownGet(data, condition.field), unwrap(condition.value, subject));
141
+ case "memberOf": return evaluateMemberOf(condition, data, subject, now, scopes);
142
+ case "related": return related?.(condition, data) === true;
143
+ case "sqlFunction": return evaluateCondition(condition.twin, data, subject, now, scopes, related, onOpaque);
144
+ case "opaque":
145
+ onOpaque?.();
146
+ return false;
147
+ case "liveSession": return subject.liveSession === true;
148
+ default:
149
+ /* v8 ignore next */
150
+ return condition;
151
+ }
152
+ }
153
+ //#endregion
154
+ //#region src/core/arazzo.ts
155
+ const VERSION = /^1\.[01]\.\d+(?:-.+)?$/u;
156
+ const STEP_TARGETS = [
157
+ "operationId",
158
+ "operationPath",
159
+ "workflowId",
160
+ "channelPath"
161
+ ];
162
+ const denied = (reason) => freezeDeep({
163
+ outcome: "denied",
164
+ denials: [{
165
+ role: null,
166
+ reason
167
+ }],
168
+ alternatives: []
169
+ });
170
+ function isRecord(value) {
171
+ return value !== null && typeof value === "object" && !Array.isArray(value);
172
+ }
173
+ function asString(value) {
174
+ return typeof value === "string" && value.length > 0 ? value : void 0;
175
+ }
176
+ function parseParameters(raw, components, where) {
177
+ if (raw === void 0) return [];
178
+ if (!Array.isArray(raw)) return `${where}: parameters must be an array`;
179
+ const out = [];
180
+ for (const item of raw) {
181
+ if (!isRecord(item)) return `${where}: a parameter is not an object`;
182
+ const reference = asString(item["reference"]);
183
+ if (reference !== void 0) {
184
+ const match = /^\$components\.parameters\.(.+)$/u.exec(reference);
185
+ const target = match?.[1] === void 0 ? void 0 : components[match[1]];
186
+ const name = isRecord(target) ? asString(target["name"]) : void 0;
187
+ if (!isRecord(target) || name === void 0 || !("value" in target)) return `${where}: unresolved parameter reference ${reference}`;
188
+ out.push({
189
+ name,
190
+ value: "value" in item ? item["value"] : target["value"]
191
+ });
192
+ continue;
193
+ }
194
+ const name = asString(item["name"]);
195
+ if (name === void 0 || !("value" in item)) return `${where}: a parameter needs a name and a value`;
196
+ out.push({
197
+ name,
198
+ value: item["value"]
199
+ });
200
+ }
201
+ return out;
202
+ }
203
+ function parseDocument(arazzo) {
204
+ if (!isRecord(arazzo)) return "Arazzo document is not an object";
205
+ const version = arazzo["arazzo"];
206
+ if (typeof version !== "string" || !VERSION.test(version)) return "arazzo must be a 1.0.x or 1.1.x version";
207
+ if (!isRecord(arazzo["info"])) return "info is required";
208
+ const rawSources = arazzo["sourceDescriptions"];
209
+ if (!Array.isArray(rawSources) || rawSources.length === 0) return "sourceDescriptions needs at least one entry";
210
+ const sources = [];
211
+ for (const source of rawSources) {
212
+ const name = isRecord(source) ? asString(source["name"]) : void 0;
213
+ if (!isRecord(source) || name === void 0 || asString(source["url"]) === void 0) return "a sourceDescription needs a name and a url";
214
+ if (sources.some((item) => item.name === name)) return `duplicate sourceDescription ${name}`;
215
+ sources.push({
216
+ name,
217
+ type: asString(source["type"]) ?? "openapi"
218
+ });
219
+ }
220
+ const components = isRecord(arazzo["components"]) && isRecord(arazzo["components"]["parameters"]) ? arazzo["components"]["parameters"] : {};
221
+ const rawWorkflows = arazzo["workflows"];
222
+ if (!Array.isArray(rawWorkflows) || rawWorkflows.length === 0) return "workflows needs at least one entry";
223
+ const workflows = [];
224
+ for (const item of rawWorkflows) {
225
+ const workflowId = isRecord(item) ? asString(item["workflowId"]) : void 0;
226
+ if (!isRecord(item) || workflowId === void 0) return "a workflow needs a workflowId";
227
+ if (workflows.some((workflow) => workflow.workflowId === workflowId)) return `duplicate workflowId ${workflowId}`;
228
+ const parameters = parseParameters(item["parameters"], components, workflowId);
229
+ if (typeof parameters === "string") return parameters;
230
+ const rawSteps = item["steps"];
231
+ if (!Array.isArray(rawSteps) || rawSteps.length === 0) return `${workflowId}: steps needs at least one entry`;
232
+ const steps = [];
233
+ for (const step of rawSteps) {
234
+ const stepId = isRecord(step) ? asString(step["stepId"]) : void 0;
235
+ if (!isRecord(step) || stepId === void 0) return `${workflowId}: a step needs a stepId`;
236
+ if (steps.some((other) => other.stepId === stepId)) return `${workflowId}: duplicate stepId ${stepId}`;
237
+ const targets = STEP_TARGETS.filter((field) => asString(step[field]) !== void 0);
238
+ if (targets.length !== 1 && !(targets.length === 2 && targets.includes("operationId") && targets.includes("channelPath"))) return `${workflowId}.${stepId}: a step needs exactly one of operationId, operationPath or workflowId`;
239
+ const stepParameters = parseParameters(step["parameters"], components, `${workflowId}.${stepId}`);
240
+ if (typeof stepParameters === "string") return stepParameters;
241
+ steps.push(compact({
242
+ stepId,
243
+ operationId: asString(step["operationId"]),
244
+ operationPath: asString(step["operationPath"]),
245
+ workflowId: asString(step["workflowId"]),
246
+ channelPath: asString(step["channelPath"]),
247
+ parameters: stepParameters
248
+ }));
249
+ }
250
+ workflows.push({
251
+ workflowId,
252
+ parameters,
253
+ steps
254
+ });
255
+ }
256
+ return {
257
+ sources,
258
+ workflows
259
+ };
260
+ }
261
+ function unescapePointer(token) {
262
+ return token.replaceAll("~1", "/").replaceAll("~0", "~");
263
+ }
264
+ function readPointer(doc, pointer) {
265
+ if (!pointer.startsWith("#/")) return;
266
+ let current = doc;
267
+ for (const raw of pointer.slice(2).split("/")) {
268
+ if (!isRecord(current)) return;
269
+ let token;
270
+ try {
271
+ token = unescapePointer(decodeURIComponent(raw));
272
+ } catch {
273
+ return;
274
+ }
275
+ if (!Object.hasOwn(current, token)) return;
276
+ current = current[token];
277
+ }
278
+ return current;
279
+ }
280
+ function findOperation(openapi, operationId) {
281
+ if (!isRecord(openapi) || !isRecord(openapi["paths"])) return;
282
+ for (const pathItem of Object.values(openapi["paths"])) {
283
+ if (!isRecord(pathItem)) continue;
284
+ for (const [method, operation] of Object.entries(pathItem)) if (!method.startsWith("x-") && isRecord(operation) && operation["operationId"] === operationId) return operation;
285
+ }
286
+ }
287
+ /** One description, or a map of descriptions keyed by source `name`. */
288
+ function descriptionFor(openapi, name) {
289
+ if (isRecord(openapi) && !("openapi" in openapi) && !("paths" in openapi) && isRecord(openapi[name])) return openapi[name];
290
+ return openapi;
291
+ }
292
+ function permissionKeysOf(node) {
293
+ if (!isRecord(node)) return [];
294
+ const raw = node["x-permdock-permissions"];
295
+ if (!Array.isArray(raw)) return [];
296
+ return raw.filter((item) => typeof item === "string");
297
+ }
298
+ /** `$inputs.a.b` against the frame's inputs; anything else that is a runtime expression is unknown. */
299
+ function valueOf(value, inputs) {
300
+ if (typeof value !== "string" || !value.includes("$")) return {
301
+ known: true,
302
+ value
303
+ };
304
+ const match = /^\$inputs\.([^#{}]+)$/u.exec(value);
305
+ if (match?.[1] === void 0) return value.startsWith("$") || value.includes("{$") ? { known: false } : {
306
+ known: true,
307
+ value
308
+ };
309
+ let current = inputs;
310
+ for (const part of match[1].split(".")) {
311
+ if (!isRecord(current) || !Object.hasOwn(current, part)) return { known: false };
312
+ current = current[part];
313
+ }
314
+ return {
315
+ known: true,
316
+ value: current
317
+ };
318
+ }
319
+ function bind(parameters, inputs) {
320
+ const record = {};
321
+ let provisional = false;
322
+ for (const parameter of parameters) {
323
+ const bound = valueOf(parameter.value, inputs);
324
+ if (!bound.known) {
325
+ provisional = true;
326
+ continue;
327
+ }
328
+ Object.defineProperty(record, parameter.name, {
329
+ value: bound.value,
330
+ enumerable: true,
331
+ writable: true,
332
+ configurable: true
333
+ });
334
+ }
335
+ return {
336
+ record,
337
+ provisional
338
+ };
339
+ }
340
+ function flatten(doc, workflowId, inputs, seen, out) {
341
+ if (seen.has(workflowId)) return "workflowId cycle";
342
+ const workflow = doc.workflows.find((item) => item.workflowId === workflowId);
343
+ if (workflow === void 0) return `unknown workflowId ${workflowId}`;
344
+ seen.add(workflowId);
345
+ for (const step of workflow.steps) {
346
+ if (step.workflowId === void 0 || step.workflowId.startsWith("$sourceDescriptions.")) {
347
+ out.push({
348
+ step,
349
+ workflow,
350
+ inputs
351
+ });
352
+ continue;
353
+ }
354
+ const nested = flatten(doc, step.workflowId, bind(step.parameters, inputs).record, seen, out);
355
+ if (nested !== void 0) return nested;
356
+ }
357
+ seen.delete(workflowId);
358
+ }
359
+ function locate(doc, openapi, step) {
360
+ if (step.workflowId !== void 0) return {
361
+ reason: "unsupported",
362
+ detail: "workflows in another Arazzo document are not resolved"
363
+ };
364
+ let sourceName;
365
+ let operationId = step.operationId;
366
+ let pointer;
367
+ if (step.operationPath !== void 0) {
368
+ const match = /^\{?\$sourceDescriptions\.([^.}#]+)(?:\.url)?\}?(#.*)?$/u.exec(step.operationPath);
369
+ if (match?.[1] === void 0) return {
370
+ reason: "undocumented",
371
+ detail: "operationPath is not a $sourceDescriptions reference"
372
+ };
373
+ sourceName = match[1];
374
+ pointer = match[2] ?? "";
375
+ } else if (operationId !== void 0) {
376
+ const match = /^\$sourceDescriptions\.([^.]+)\.(.+)$/u.exec(operationId);
377
+ if (match?.[1] !== void 0 && match[2] !== void 0) {
378
+ sourceName = match[1];
379
+ operationId = match[2];
380
+ }
381
+ }
382
+ const candidates = sourceName === void 0 ? doc.sources.filter((source) => source.type !== "arazzo") : doc.sources.filter((source) => source.name === sourceName);
383
+ if (candidates.length === 0) return {
384
+ reason: "undocumented",
385
+ detail: sourceName === void 0 ? "no API sourceDescription" : `unknown sourceDescription ${sourceName}`
386
+ };
387
+ if (step.channelPath !== void 0) return {
388
+ reason: "unsupported",
389
+ detail: "AsyncAPI steps are not evaluated"
390
+ };
391
+ const found = [];
392
+ for (const source of candidates) {
393
+ const description = descriptionFor(openapi, source.name);
394
+ const node = pointer === void 0 ? operationId === void 0 ? void 0 : findOperation(description, operationId) : readPointer(description, pointer);
395
+ if (node !== void 0 || candidates.length === 1) found.push({
396
+ source,
397
+ node
398
+ });
399
+ }
400
+ if (found.length > 1) return {
401
+ reason: "undocumented",
402
+ detail: "operationId matches several sources; qualify it with $sourceDescriptions"
403
+ };
404
+ const [hit] = found;
405
+ if (hit?.source.type === "asyncapi") return {
406
+ reason: "unsupported",
407
+ detail: "AsyncAPI steps are not evaluated"
408
+ };
409
+ if (hit === void 0 || hit.source.type === "arazzo" || !isRecord(hit.node)) return {
410
+ reason: "undocumented",
411
+ detail: "operation not found"
412
+ };
413
+ return compact({
414
+ node: hit.node,
415
+ operationId: asString(hit.node["operationId"]) ?? operationId
416
+ });
417
+ }
418
+ function resolve(doc, openapi, tree, flat) {
419
+ const { step, workflow, inputs } = flat;
420
+ const base = {
421
+ stepId: step.stepId,
422
+ workflowId: workflow.workflowId
423
+ };
424
+ const located = locate(doc, openapi, step);
425
+ const { record, provisional } = bind([...workflow.parameters.filter((parameter) => !step.parameters.some((own) => own.name === parameter.name)), ...step.parameters], inputs);
426
+ const data = Object.keys(inputs).length + Object.keys(record).length === 0 ? void 0 : {
427
+ ...inputs,
428
+ ...record
429
+ };
430
+ if ("reason" in located) return {
431
+ ...base,
432
+ ...compact({ operationId: step.operationId }),
433
+ keys: [],
434
+ finding: located,
435
+ data,
436
+ provisional
437
+ };
438
+ const keys = permissionKeysOf(located.node);
439
+ const unknown = tree === void 0 ? void 0 : keys.find((key) => findPermission(tree, key) === void 0);
440
+ return {
441
+ ...base,
442
+ ...compact({ operationId: located.operationId }),
443
+ keys,
444
+ ...keys.length === 0 ? { finding: {
445
+ reason: "undocumented",
446
+ detail: "operation has no x-permdock-permissions"
447
+ } } : unknown === void 0 ? {} : { finding: {
448
+ reason: "unknown-key",
449
+ detail: unknown
450
+ } },
451
+ data,
452
+ provisional
453
+ };
454
+ }
455
+ function resolveAll(input, tree) {
456
+ const doc = parseDocument(input.arazzo);
457
+ if (typeof doc === "string") return {
458
+ workflowId: input.workflowId ?? "",
459
+ error: doc
460
+ };
461
+ const workflowId = input.workflowId ?? doc.workflows[0]?.workflowId ?? "";
462
+ const flat = [];
463
+ const error = flatten(doc, workflowId, input.inputs ?? {}, /* @__PURE__ */ new Set(), flat);
464
+ if (error !== void 0) return {
465
+ workflowId,
466
+ error
467
+ };
468
+ return {
469
+ workflowId,
470
+ steps: flat.map((item) => resolve(doc, input.openapi, tree, item))
471
+ };
472
+ }
473
+ function arazzoFindings(input, tree) {
474
+ const resolved = resolveAll(input, tree);
475
+ if ("error" in resolved) return [{
476
+ stepId: "document",
477
+ workflowId: resolved.workflowId,
478
+ reason: "validation",
479
+ detail: resolved.error
480
+ }];
481
+ return resolved.steps.flatMap((step) => step.finding === void 0 ? [] : [{
482
+ stepId: step.stepId,
483
+ workflowId: step.workflowId,
484
+ reason: step.finding.reason,
485
+ detail: step.finding.detail
486
+ }]);
487
+ }
488
+ function worst(outcomes) {
489
+ if (outcomes.includes("denied")) return "denied";
490
+ if (outcomes.includes("approval-required")) return "approval-required";
491
+ return "granted";
492
+ }
493
+ function combine(decisions) {
494
+ const outcome = worst(decisions.map((item) => item.outcome));
495
+ return decisions.find((item) => item.outcome === outcome) ?? denied("undocumented");
496
+ }
497
+ function simulateArazzo(input, tree, decide) {
498
+ const resolved = resolveAll(input, tree);
499
+ if ("error" in resolved) return freezeDeep({
500
+ workflowId: resolved.workflowId === "" ? "unknown" : resolved.workflowId,
501
+ outcome: "denied",
502
+ steps: [{
503
+ stepId: "document",
504
+ permissions: [],
505
+ decision: denied("validation")
506
+ }]
507
+ });
508
+ const steps = resolved.steps.map((step) => {
509
+ const owner = step.workflowId === resolved.workflowId ? void 0 : step.workflowId;
510
+ if (step.finding !== void 0) return compact({
511
+ stepId: step.stepId,
512
+ workflowId: owner,
513
+ operationId: step.operationId,
514
+ permissions: [],
515
+ decision: denied(step.finding.reason === "unsupported" ? "unsupported" : "undocumented")
516
+ });
517
+ const permissions = step.keys.flatMap((key) => {
518
+ const leaf = tree === void 0 ? void 0 : findPermission(tree, key);
519
+ return leaf === void 0 ? [] : [leaf];
520
+ });
521
+ return compact({
522
+ stepId: step.stepId,
523
+ workflowId: owner,
524
+ operationId: step.operationId,
525
+ permissions,
526
+ decision: combine(permissions.map((permission) => decide(permission, step.data))),
527
+ provisional: step.provisional ? true : void 0
528
+ });
529
+ });
530
+ return freezeDeep({
531
+ workflowId: resolved.workflowId,
532
+ outcome: worst(steps.map((step) => step.decision.outcome)),
533
+ steps
534
+ });
535
+ }
536
+ function isArazzoSimulateInput(value) {
537
+ return isRecord(value) && "arazzo" in value;
538
+ }
539
+ //#endregion
540
+ //#region src/core/row-pair.ts
541
+ function isRowPair(value) {
542
+ return value !== null && typeof value === "object" && "current" in value && "next" in value;
543
+ }
544
+ /**
545
+ * The row a `where` reads and the row a `check` reads. An instance check
546
+ * takes a `{ current, next }` pair or one row for both; a collection check
547
+ * has no current row, only the input it creates.
548
+ */
549
+ function rowValues(permission, data) {
550
+ if (permission.kind === "collection") return {
551
+ current: void 0,
552
+ next: data
553
+ };
554
+ if (permission.kind === "instance" && isRowPair(data)) return {
555
+ current: data.current,
556
+ next: data.next
557
+ };
558
+ return {
559
+ current: data,
560
+ next: data
561
+ };
562
+ }
563
+ /** The row's id under `field` as a string, or `*` when the row has no string or number there. */
564
+ function rowIdOf(row, field = "id") {
565
+ if (row === null || typeof row !== "object" || !Object.hasOwn(row, field)) return "*";
566
+ const id = Reflect.get(row, field);
567
+ return typeof id === "string" || typeof id === "number" ? String(id) : "*";
568
+ }
569
+ //#endregion
570
+ //#region src/conditions/bind.ts
571
+ function isPrimitive(value) {
572
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
573
+ }
574
+ const ALWAYS$1 = {
575
+ op: "eq",
576
+ field: "_",
577
+ value: true
578
+ };
579
+ const NEVER = {
580
+ op: "or",
581
+ conditions: []
582
+ };
583
+ function bindValue(value, subject, only) {
584
+ if (isConditionRef(value) && only !== void 0 && !only(value.ref)) return value;
585
+ if (isConditionRef(value)) {
586
+ const resolved = resolveConditionRef(value.ref, subject);
587
+ if (Array.isArray(resolved)) return resolved.filter((item) => isPrimitive(item));
588
+ return isPrimitive(resolved) ? resolved : null;
589
+ }
590
+ if (Array.isArray(value)) return value.map((item) => bindValue(item, subject, only));
591
+ return value;
592
+ }
593
+ function bindArg(arg, subject, only) {
594
+ return arg !== null && typeof arg === "object" && "field" in arg && !isConditionRef(arg) ? arg : bindValue(arg, subject, only);
595
+ }
596
+ /**
597
+ * Replaces every `principal.*` / `context.*` ref with the subject's value, and
598
+ * a `liveSession` node with a constant, so a `where()` result compiles the
599
+ * same without the subject. A ref the subject
600
+ * does not hold binds to `null` (or `[]` in a list), which never matches, as
601
+ * in `evaluateCondition`. With `only`, refs it rejects stay refs.
602
+ */
603
+ function bindConditionRefs(condition, subject, only) {
604
+ switch (condition.op) {
605
+ case "and":
606
+ case "or": return {
607
+ op: condition.op,
608
+ conditions: condition.conditions.map((child) => bindConditionRefs(child, subject, only))
609
+ };
610
+ case "not": return {
611
+ op: "not",
612
+ condition: bindConditionRefs(condition.condition, subject, only)
613
+ };
614
+ case "in":
615
+ case "notIn": {
616
+ const value = condition.value;
617
+ if (isConditionRef(value) && only !== void 0 && !only(value.ref)) return condition;
618
+ const bound = bindValue(value, subject, only);
619
+ return {
620
+ ...condition,
621
+ value: Array.isArray(bound) ? bound : []
622
+ };
623
+ }
624
+ case "eq":
625
+ case "ne":
626
+ case "gt":
627
+ case "gte":
628
+ case "lt":
629
+ case "lte":
630
+ case "contains": return {
631
+ ...condition,
632
+ value: bindValue(condition.value, subject, only)
633
+ };
634
+ case "sqlFunction": return {
635
+ ...condition,
636
+ args: condition.args.map((arg) => bindArg(arg, subject, only)),
637
+ twin: bindConditionRefs(condition.twin, subject, only)
638
+ };
639
+ case "liveSession": return subject.liveSession === true ? ALWAYS$1 : NEVER;
640
+ case "isNull":
641
+ case "memberOf":
642
+ case "related":
643
+ case "opaque": return condition;
644
+ default: return condition;
645
+ }
646
+ }
647
+ //#endregion
648
+ //#region src/core/where-scope.ts
649
+ const ALWAYS = {
650
+ op: "eq",
651
+ field: "_",
652
+ value: true
653
+ };
654
+ function eq(field, value) {
655
+ return value === void 0 ? {
656
+ op: "or",
657
+ conditions: []
658
+ } : {
659
+ op: "eq",
660
+ field,
661
+ value
662
+ };
663
+ }
664
+ /** One equality per partitioning scope key on the membership's chain, outermost first. */
665
+ function namedFilters(membership, scope) {
666
+ if (findScope(scope.scopes, membership.scope ?? "") === void 0 || !inTeam(membership, scope.scopes, scope.team) || !activeFor(membership, scope.scopes, scope.tenant) || membership.scope === void 0) return null;
667
+ const filters = [];
668
+ for (const name of scopeChain(scope.scopes, membership.scope).toReversed()) {
669
+ const key = scope.fieldOf?.(name) ?? findScope(scope.scopes, name)?.key;
670
+ if (key !== void 0 && scope.partitioned(name, key)) filters.push(eq(key, scopeIdOf(membership, name)));
671
+ }
672
+ return filters;
673
+ }
674
+ /**
675
+ * The row filters a grant's membership implies, or `null` when the grant
676
+ * reaches no row in this scope (another tenant, no active tenant, expired).
677
+ */
678
+ function scopeFilters(grant, scope) {
679
+ if (grant.scope === void 0) return [];
680
+ const membership = grant.membership;
681
+ if (membership === void 0 || isMembershipExpired(membership, scope.now)) return null;
682
+ if (typeof grant.scope === "string") return membership.scope === grant.scope ? namedFilters(membership, scope) : null;
683
+ const on = membership.on;
684
+ if (on === void 0) return null;
685
+ if (scope.resources === void 0) return on.resource === grant.scope.resource && on.resource === scope.resource ? [eq("id", on.id)] : null;
686
+ const roleResource = scope.resources.get(grant.scope.resource);
687
+ if (on.resource !== grant.scope.resource && membershipField(roleResource, on.resource, scope.resources) === void 0) return null;
688
+ const walked = scope.graph === true ? resourceRoleCondition(scope.resources.get(scope.resource), on.resource, [on.id], scope.resources) : void 0;
689
+ if (walked !== void 0) return [walked];
690
+ const field = membershipField(scope.resources.get(scope.resource), on.resource, scope.resources);
691
+ return field === void 0 ? null : [eq(field, on.id)];
692
+ }
693
+ function all(conditions) {
694
+ if (conditions.length === 0) return ALWAYS;
695
+ return sole(conditions) ?? {
696
+ op: "and",
697
+ conditions: [...conditions]
698
+ };
699
+ }
700
+ function scopedCondition(grant, scope) {
701
+ const filters = scopeFilters(grant, scope);
702
+ if (filters === null) return null;
703
+ const parts = grant.where === void 0 ? filters : [...filters, grant.where];
704
+ return parts.length === 0 ? "always" : all(parts);
705
+ }
706
+ /** Whether an unconditional `deny` removes every row `allow` could reach. */
707
+ function covers(deny, allow, scopes) {
708
+ if (deny.where !== void 0) return false;
709
+ const denied = deny.membership;
710
+ if (deny.scope === void 0 || denied === void 0) return deny.scope === void 0;
711
+ const allowed = allow.membership;
712
+ if (allowed === void 0) return false;
713
+ if (typeof deny.scope === "object") return typeof allow.scope === "object" && denied.on?.resource === allowed.on?.resource && denied.on?.id === allowed.on?.id;
714
+ if (typeof allow.scope !== "string" || allowed.scope === void 0) return false;
715
+ const chain = scopeChain(scopes, deny.scope);
716
+ return scopeChain(scopes, allowed.scope).includes(deny.scope) && chain.every((name) => scopeIdOf(allowed, name) === scopeIdOf(denied, name));
717
+ }
718
+ /**
719
+ * The portable filter for one permission's grants: allows OR together, each
720
+ * narrowed to its membership's tenant, team or resource; any applicable
721
+ * portable deny is subtracted, and an unconditional one empties the result.
722
+ */
723
+ function collect(grants, scope) {
724
+ const none = {
725
+ op: "or",
726
+ conditions: []
727
+ };
728
+ const partial = grants.some((grant) => grant.portable === false);
729
+ const denies = [];
730
+ for (const grant of grants) {
731
+ if (grant.effect !== "deny" || grant.portable === false || !isActive(grant.validity, scope.now)) continue;
732
+ const condition = scopedCondition(grant, scope);
733
+ if (condition === "always") return {
734
+ condition: none,
735
+ partial
736
+ };
737
+ if (condition !== null) denies.push({
738
+ grant,
739
+ condition
740
+ });
741
+ }
742
+ const parts = [];
743
+ for (const grant of grants) {
744
+ if (grant.effect !== "allow" || grant.portable === false || !isActive(grant.validity, scope.now)) continue;
745
+ const condition = scopedCondition(grant, scope);
746
+ if (condition === null || denies.some((deny) => covers(deny.grant, grant, scope.scopes))) continue;
747
+ parts.push(all([...condition === "always" ? [] : [condition], ...denies.map((deny) => ({
748
+ op: "not",
749
+ condition: deny.condition
750
+ }))]));
751
+ }
752
+ if (parts.length === 0) return {
753
+ condition: none,
754
+ partial
755
+ };
756
+ return {
757
+ condition: sole(parts) ?? {
758
+ op: "or",
759
+ conditions: parts
760
+ },
761
+ partial
762
+ };
763
+ }
764
+ /**
765
+ * `collect`, with every subject ref bound to `scope.subject` and the subject
766
+ * attached (not enumerable) for `memberOf`, which needs its memberships.
767
+ */
768
+ function whereFromGrants(grants, scope) {
769
+ const { condition, partial } = collect(grants, scope);
770
+ const result = {
771
+ condition: bindConditionRefs(condition, scope.subject),
772
+ partial
773
+ };
774
+ Object.defineProperty(result, "subject", {
775
+ value: scope.subject,
776
+ enumerable: false
777
+ });
778
+ Object.defineProperty(result, "scopes", {
779
+ value: scope.scopes,
780
+ enumerable: false
781
+ });
782
+ if (scope.resources !== void 0) Object.defineProperty(result, "resources", {
783
+ value: scope.resources,
784
+ enumerable: false
785
+ });
786
+ return freezeDeep(result);
787
+ }
788
+ //#endregion
789
+ //#region src/core/snapshot-sign.ts
790
+ async function signSnapshot(snapshot, signer, audience) {
791
+ const payload = { snapshot };
792
+ if (snapshot.subject.principal !== null) payload["sub"] = snapshot.subject.principal.id;
793
+ return await signer.sign(payload, compact({
794
+ typ: "permdock-snapshot+jwt",
795
+ audience,
796
+ expiresAt: snapshot.expiresAt
797
+ }));
798
+ }
799
+ //#endregion
800
+ //#region src/core/snapshot.ts
801
+ function plainPermission(leaf) {
802
+ return {
803
+ key: leaf.key,
804
+ scope: leaf.scope,
805
+ resource: leaf.resource,
806
+ action: leaf.action,
807
+ meta: leaf.meta,
808
+ kind: leaf.kind
809
+ };
810
+ }
811
+ function plainRole(leaf) {
812
+ return compact({
813
+ key: leaf.key,
814
+ on: leaf.on,
815
+ assignable: leaf.assignable,
816
+ meta: leaf.meta,
817
+ kind: leaf.kind
818
+ });
819
+ }
820
+ function plainPlan(leaf) {
821
+ return {
822
+ key: leaf.key,
823
+ meta: leaf.meta,
824
+ kind: leaf.kind
825
+ };
826
+ }
827
+ function snapshotGrant(grant, membership) {
828
+ const scope = grant.scope === "global" ? void 0 : typeof grant.scope === "string" ? grant.scope : { resource: grant.scope.resource };
829
+ const entry = compact({
830
+ permission: grant.permission.key,
831
+ effect: grant.effect,
832
+ role: grant.role,
833
+ to: grant.to,
834
+ where: grant.portable ? grant.where : void 0,
835
+ check: grant.portable ? grant.check : void 0,
836
+ approval: grant.approval,
837
+ scope,
838
+ membership,
839
+ portable: grant.portable ? void 0 : false,
840
+ fields: grant.fields,
841
+ validity: grant.validity
842
+ });
843
+ return freezeDeep(entry);
844
+ }
845
+ const CARRIED = /* @__PURE__ */ new Set([
846
+ "id",
847
+ "roles",
848
+ "plans",
849
+ "tenant",
850
+ "memberships"
851
+ ]);
852
+ /** Principal refs the snapshot principal does not carry (`claims`, `teamIds`, …) are bound to the subject's values. */
853
+ function isClaimRef(ref) {
854
+ const [root, head] = ref.split(".", 2);
855
+ return root === "principal" && !CARRIED.has(String(head));
856
+ }
857
+ function bindClaims(grant, subject) {
858
+ if (grant.where === void 0 && grant.check === void 0) return grant;
859
+ return freezeDeep(compact({
860
+ ...grant,
861
+ where: grant.where === void 0 ? void 0 : bindConditionRefs(grant.where, subject, isClaimRef),
862
+ check: grant.check === void 0 ? void 0 : bindConditionRefs(grant.check, subject, isClaimRef)
863
+ }));
864
+ }
865
+ function notEntitledOf(items) {
866
+ const seen = /* @__PURE__ */ new Map();
867
+ for (const { grant } of items) {
868
+ const key = `${grant.permission.key}\u0000${grant.role ?? ""}`;
869
+ if (!seen.has(key)) seen.set(key, {
870
+ permission: grant.permission.key,
871
+ role: grant.role,
872
+ to: grant.to
873
+ });
874
+ }
875
+ return seen.size === 0 ? void 0 : [...seen.values()];
876
+ }
877
+ function buildSnapshot(input) {
878
+ const now = input.now ?? Math.floor(Date.now() / 1e3);
879
+ const principal = input.subject.principal;
880
+ const allTenants = tenantsOf(principal, scopeList(input.scopes));
881
+ const tenants = input.tenants === "all" ? allTenants : principal?.tenant === void 0 ? [] : [principal.tenant];
882
+ const include = input.include;
883
+ const included = (permission) => include === void 0 || include.length === 0 || include.some((prefix) => permission.key === prefix || permission.key.startsWith(`${prefix}.`) || permission.resource === prefix);
884
+ const grants = input.grants.filter((item) => included(item.grant.permission)).map((item) => bindClaims(snapshotGrant(item.grant, item.membership), input.subject));
885
+ const ids = {};
886
+ for (const item of input.grants) {
887
+ const name = item.grant.permission.resource;
888
+ const field = input.idOf?.(name);
889
+ if (field !== void 0 && field !== "id" && included(item.grant.permission)) ids[name] = field;
890
+ }
891
+ const assignableFor = input.assignable;
892
+ const assignable = assignableFor === void 0 ? [] : tenants.map((tenant) => {
893
+ const entry = assignableFor(tenant);
894
+ const permissions = entry.permissions.filter(included).map(plainPermission);
895
+ if (entry.levels === void 0) return {
896
+ ...entry,
897
+ permissions
898
+ };
899
+ const keys = new Set(permissions.map((leaf) => leaf.key));
900
+ const levels = Object.fromEntries(Object.entries(entry.levels).filter(([key]) => keys.has(key)));
901
+ return compact({
902
+ ...entry,
903
+ permissions,
904
+ levels: Object.keys(levels).length === 0 ? void 0 : levels
905
+ });
906
+ });
907
+ return freezeDeep(compact({
908
+ v: 1,
909
+ issuedAt: now,
910
+ subject: compact({
911
+ principal: principal === null ? null : compact({
912
+ id: principal.id,
913
+ roles: principal.roles ?? [],
914
+ plans: principal.plans,
915
+ tenant: principal.tenant,
916
+ memberships: principal.memberships
917
+ }),
918
+ delegation: snapshotDelegation(input.subject, input.delegated !== void 0),
919
+ context: input.subject.context
920
+ }),
921
+ roles: input.roles,
922
+ audiences: input.audiences,
923
+ grants,
924
+ tenants,
925
+ include,
926
+ simulated: input.simulated === true ? true : void 0,
927
+ expiresAt: input.subject.expiresAt,
928
+ scopes: input.scopes,
929
+ ids: Object.keys(ids).length === 0 ? void 0 : ids,
930
+ assignable: assignable.length > 0 ? assignable : void 0,
931
+ delegated: input.delegated === void 0 ? void 0 : [...input.delegated].toSorted(),
932
+ notEntitled: notEntitledOf((input.notEntitled ?? []).filter((item) => included(item.grant.permission))),
933
+ vocabulary: input.vocabulary === void 0 ? void 0 : compact({
934
+ roles: listRoles(input.vocabulary.roles).length === 0 ? void 0 : Object.fromEntries(listRoles(input.vocabulary.roles).map((leaf) => [leaf.key, plainRole(leaf)])),
935
+ plans: listPlans(input.vocabulary.plans).length === 0 ? void 0 : Object.fromEntries(listPlans(input.vocabulary.plans).map((leaf) => [leaf.key, plainPlan(leaf)]))
936
+ })
937
+ }));
938
+ }
939
+ /**
940
+ * The token delegation as the client sees it. An actor with no token
941
+ * delegation gets an empty `scopes` list so the client ceiling agrees with
942
+ * `no-delegation`, unless a policy delegation applies (`delegated`), which
943
+ * then is the ceiling.
944
+ */
945
+ function snapshotDelegation(subject, delegated) {
946
+ const delegation = subject.delegation;
947
+ if (subject.actor === void 0 || delegated) return delegation;
948
+ if (delegation?.scopes === void 0 && delegation?.authorizationDetails === void 0 && delegation?.access === void 0) return {
949
+ ...delegation,
950
+ scopes: []
951
+ };
952
+ return delegation;
953
+ }
954
+ function rejectUnsafe(value, path) {
955
+ if (value === null || typeof value !== "object") return;
956
+ if (Array.isArray(value)) {
957
+ for (const [index, item] of value.entries()) rejectUnsafe(item, `${path}[${index}]`);
958
+ return;
959
+ }
960
+ for (const key of Object.keys(value)) {
961
+ if (isForbiddenKey(key)) throw new Error(`PermDock: unsafe snapshot key '${key}' at ${path}`);
962
+ rejectUnsafe(value[key], `${path}.${key}`);
963
+ }
964
+ }
965
+ function parseSnapshot(json) {
966
+ const input = typeof json === "string" ? JSON.parse(json) : json;
967
+ if (input === null || typeof input !== "object" || Array.isArray(input)) throw new Error("PermDock: snapshot must be an object");
968
+ const value = typeof json === "string" || Object.isFrozen(input) ? input : JSON.parse(JSON.stringify(input));
969
+ rejectUnsafe(value, "$");
970
+ const version = value["v"];
971
+ if (version !== 1) throw new Error(`PermDock: unsupported snapshot version '${String(version)}'`);
972
+ return freezeDeep(value);
973
+ }
974
+ //#endregion
975
+ export { whereFromGrants as a, rowIdOf as c, isArazzoSimulateInput as d, simulateArazzo as f, signSnapshot as i, rowValues as l, parseSnapshot as n, bindConditionRefs as o, evaluateCondition as p, snapshotGrant as r, isRowPair as s, buildSnapshot as t, arazzoFindings as u };