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,472 @@
1
+ import { i as describeError } from "./errors-CWfoNg9N.js";
2
+ import { a as sqlFiles, i as rel } from "./files-DWUN9PzI.js";
3
+ import { t as FIELD_VIEWS } from "./rls-fields-BMasREhR.js";
4
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
5
+ import { dirname, join, posix, resolve } from "node:path";
6
+ import { createRequire } from "node:module";
7
+ import { parse } from "smol-toml";
8
+ //#region src/cli/deciding-columns.ts
9
+ /** `schema.table`, unquoted and lower case; `public` when unqualified. */
10
+ function tableKey(name) {
11
+ const parts = name.split(".").map((part) => part.replaceAll("\"", ""));
12
+ return (parts.length === 1 ? ["public", ...parts] : parts).join(".").toLowerCase();
13
+ }
14
+ /**
15
+ * The columns of each membership source's table that decide who holds which
16
+ * membership (user, scope, id, `within`, role, `via`, expiry), and the id and
17
+ * value columns of a table a role or user column references, keyed by
18
+ * {@link tableKey}. A client that can write one can give itself a membership.
19
+ */
20
+ function membershipColumns(config) {
21
+ const sources = config.supabase?.hook?.memberships ?? config.rls?.membershipSources ?? [];
22
+ const byTable = /* @__PURE__ */ new Map();
23
+ for (const source of sources) {
24
+ const table = tableKey(source.sql.table);
25
+ const columns = byTable.get(table) ?? /* @__PURE__ */ new Set();
26
+ for (const column of source.sql.columns) columns.add(column);
27
+ byTable.set(table, columns);
28
+ for (const through of [...source.sql.throughs, ...source.sql.userThrough === void 0 ? [] : [source.sql.userThrough]]) {
29
+ const keys = tableKey(through.table);
30
+ byTable.set(keys, /* @__PURE__ */ new Set([
31
+ ...byTable.get(keys) ?? [],
32
+ through.id,
33
+ through.key
34
+ ]));
35
+ }
36
+ }
37
+ return byTable;
38
+ }
39
+ /**
40
+ * Every `schema.table.column` a claim or a membership is computed from: the
41
+ * membership columns and the `attrs` table columns. Sorted.
42
+ */
43
+ function decidingColumns(config, attrs) {
44
+ const entries = /* @__PURE__ */ new Set();
45
+ for (const [table, columns] of membershipColumns(config)) for (const column of columns) entries.add(`${table}.${column}`);
46
+ if (attrs?.table !== void 0) {
47
+ const table = tableKey(attrs.table);
48
+ for (const column of attrs.columns) entries.add(`${table}.${column}`);
49
+ }
50
+ return [...entries].toSorted();
51
+ }
52
+ //#endregion
53
+ //#region src/cli/sql-statements.ts
54
+ function blankComments(sql) {
55
+ return sql.replaceAll(/--[^\n]*/gu, (comment) => " ".repeat(comment.length)).replaceAll(/\/\*[\s\S]*?\*\//gu, (comment) => comment.replaceAll(/[^\n]/gu, " "));
56
+ }
57
+ /** Split on `;` outside string literals and dollar-quoted bodies. */
58
+ function sqlStatements(sql) {
59
+ const text = blankComments(sql);
60
+ const statements = [];
61
+ const push = (start, end) => {
62
+ const body = text.slice(start, end);
63
+ const lead = body.length - body.trimStart().length;
64
+ const trimmed = body.trim();
65
+ if (trimmed !== "") statements.push({
66
+ line: text.slice(0, start + lead).split("\n").length,
67
+ text: trimmed
68
+ });
69
+ };
70
+ let start = 0;
71
+ let index = 0;
72
+ while (index < text.length) {
73
+ const char = text[index];
74
+ if (char === "'") {
75
+ const end = text.indexOf("'", index + 1);
76
+ index = end === -1 ? text.length : end + 1;
77
+ continue;
78
+ }
79
+ if (char === "$") {
80
+ const tag = /^\$[A-Za-z_]*\$/u.exec(text.slice(index, index + 64));
81
+ if (tag !== null) {
82
+ const end = text.indexOf(tag[0], index + tag[0].length);
83
+ index = end === -1 ? text.length : end + tag[0].length;
84
+ continue;
85
+ }
86
+ }
87
+ if (char === ";") {
88
+ push(start, index);
89
+ start = index + 1;
90
+ }
91
+ index += 1;
92
+ }
93
+ push(start, text.length);
94
+ return statements;
95
+ }
96
+ /** Capture group `index` of a match, or '' when an optional group took no part. */
97
+ function group(match, index) {
98
+ return match[index] ?? "";
99
+ }
100
+ const VIEW = /^create\s+(?:or\s+replace\s+)?view\s+(?<name>\S+?)(?:\s+with\s*\((?<options>[^)]*)\))?\s+as\b/iu;
101
+ const PRIVILEGE = /^(?:grant|revoke)\s[\s\S]*?\son\s+(?<target>[\s\S]+?)\s+(?:to|from)\s/iu;
102
+ const UNDIFFED_TARGET = /^(?:schema|function|procedure|routine|all\s+(?:functions|procedures|routines)\s+in\s+schema)\s/iu;
103
+ function privilegeTable(target) {
104
+ return target.replace(/^table\s+/iu, "").trim();
105
+ }
106
+ function splitUndiffed(sql) {
107
+ const statements = sqlStatements(sql);
108
+ const views = /* @__PURE__ */ new Set();
109
+ const moved = [];
110
+ let kept = sql;
111
+ for (const { text } of statements) {
112
+ const view = VIEW.exec(text)?.groups;
113
+ if (view !== void 0) {
114
+ views.add(view["name"] ?? "");
115
+ if (view["options"] !== void 0) moved.push(`alter view ${view["name"] ?? ""} set (${view["options"]});`);
116
+ continue;
117
+ }
118
+ const target = PRIVILEGE.exec(text)?.groups?.["target"];
119
+ if (target !== void 0 && (UNDIFFED_TARGET.test(target) || views.has(privilegeTable(target)))) {
120
+ moved.push(`${text};`);
121
+ kept = kept.replace(`${text};\n`, "");
122
+ }
123
+ }
124
+ return {
125
+ kept,
126
+ moved
127
+ };
128
+ }
129
+ //#endregion
130
+ //#region src/cli/doctor-project.ts
131
+ /**
132
+ * The directories from `cwd` up to the workspace root: the first one with a
133
+ * `pnpm-workspace.yaml`, a `package.json` that declares `workspaces`, or a
134
+ * `.git`. Skills are often installed once at a monorepo's root.
135
+ */
136
+ function workspaceDirs(cwd) {
137
+ const dirs = [];
138
+ let dir = resolve(cwd);
139
+ for (;;) {
140
+ dirs.push(dir);
141
+ if (isWorkspaceRoot(dir)) return dirs;
142
+ const parent = dirname(dir);
143
+ if (parent === dir) return [resolve(cwd)];
144
+ dir = parent;
145
+ }
146
+ }
147
+ function isWorkspaceRoot(dir) {
148
+ if (existsSync(join(dir, "pnpm-workspace.yaml")) || existsSync(join(dir, ".git"))) return true;
149
+ const manifest = join(dir, "package.json");
150
+ if (!existsSync(manifest)) return false;
151
+ try {
152
+ const parsed = JSON.parse(readFileSync(manifest, "utf8"));
153
+ return parsed !== null && typeof parsed === "object" && Object.hasOwn(parsed, "workspaces");
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+ /** Whether `dir` holds a skills lock: PermDock's own, or the `skills` CLI's naming the `permdock` skill. */
159
+ function hasSkillsLock(dir) {
160
+ if (existsSync(join(dir, ".permdock/skills-lock.json"))) return true;
161
+ const shared = join(dir, "skills-lock.json");
162
+ if (!existsSync(shared)) return false;
163
+ try {
164
+ const parsed = JSON.parse(readFileSync(shared, "utf8"));
165
+ const skills = parsed !== null && typeof parsed === "object" ? Reflect.get(parsed, "skills") : void 0;
166
+ return skills !== null && typeof skills === "object" && Object.hasOwn(skills, "permdock");
167
+ } catch {
168
+ return false;
169
+ }
170
+ }
171
+ function pd005(cwd) {
172
+ const dirs = workspaceDirs(cwd);
173
+ if (!dirs.some((dir) => [
174
+ ".agents/skills",
175
+ ".claude/skills",
176
+ ".cursor/skills"
177
+ ].some((folder) => existsSync(join(dir, folder, "permdock/SKILL.md"))))) return [{
178
+ code: "PD005",
179
+ severity: "warning",
180
+ message: "Agent Skills are not installed",
181
+ fix: "pnpm exec permdock skills install"
182
+ }];
183
+ if (!dirs.some(hasSkillsLock)) return [{
184
+ code: "PD005",
185
+ severity: "warning",
186
+ message: "skills lock is missing",
187
+ fix: "pnpm exec permdock skills install"
188
+ }];
189
+ return [];
190
+ }
191
+ function pd006(cwd) {
192
+ try {
193
+ const pkg = createRequire(resolve(cwd, "package.json"))("typescript/package.json");
194
+ const major = Number(pkg.version.split(".")[0]);
195
+ if (major < 5 || major === 5 && Number(pkg.version.split(".")[1]) < 9) return [{
196
+ code: "PD006",
197
+ severity: "error",
198
+ message: `TypeScript ${pkg.version} is below the supported matrix (5.9, 6, 7)`,
199
+ fix: "upgrade typescript to 5.9 or later"
200
+ }];
201
+ if (major > 7) return [{
202
+ code: "PD006",
203
+ severity: "error",
204
+ message: `TypeScript ${pkg.version} is not in the supported matrix (5.9, 6, 7)`,
205
+ fix: "use TypeScript 5.9, 6 or 7"
206
+ }];
207
+ return [];
208
+ } catch {
209
+ return [{
210
+ code: "PD006",
211
+ severity: "error",
212
+ message: "typescript is not installed",
213
+ fix: "add typescript 5.9, 6 or 7"
214
+ }];
215
+ }
216
+ }
217
+ function pd009(cwd) {
218
+ const copies = [];
219
+ function walk(dir, depth) {
220
+ if (depth > 6 || !existsSync(dir)) return;
221
+ const pkg = join(dir, "node_modules/permdock/package.json");
222
+ if (existsSync(pkg)) copies.push(pkg);
223
+ if (!existsSync(join(dir, "node_modules"))) return;
224
+ for (const name of readdirSync(join(dir, "node_modules"))) {
225
+ if (name.startsWith(".")) continue;
226
+ const nested = join(dir, "node_modules", name);
227
+ try {
228
+ if (statSync(nested).isDirectory()) walk(nested, depth + 1);
229
+ } catch {}
230
+ }
231
+ }
232
+ walk(cwd, 0);
233
+ if (copies.length > 1) return [{
234
+ code: "PD009",
235
+ severity: "error",
236
+ message: `duplicate permdock copies: ${copies.map((item) => rel(cwd, dirname(item))).join(", ")}`,
237
+ fix: "dedupe so only one permdock version is installed"
238
+ }];
239
+ return [];
240
+ }
241
+ function pd012(cwd, config) {
242
+ const docs = config.openapi?.doc ?? [];
243
+ const findings = [];
244
+ for (const doc of docs) {
245
+ const abs = resolve(cwd, doc);
246
+ if (!existsSync(abs)) continue;
247
+ const text = readFileSync(abs, "utf8");
248
+ if (text.includes("\"drafts\"") && !text.includes("overlay")) findings.push({
249
+ code: "PD012",
250
+ severity: "warning",
251
+ message: `${doc} carries a draft pin the CLI no longer emits`,
252
+ fix: "regenerate with permdock openapi"
253
+ });
254
+ }
255
+ return findings;
256
+ }
257
+ const MIGRATION_DIRS = [
258
+ "supabase/migrations",
259
+ "supabase/schemas",
260
+ "migrations",
261
+ "drizzle",
262
+ "prisma/migrations",
263
+ "db/migrations"
264
+ ];
265
+ const VIEW_NAME = String.raw`((?:"[^"]+"|\w+)(?:\.(?:"[^"]+"|\w+))?)`;
266
+ const CREATE_VIEW = new RegExp(String.raw`\bcreate\s+(?:or\s+replace\s+)?(?:temp(?:orary)?\s+)?(?:recursive\s+)?view\s+${VIEW_NAME}([\s\S]*?)\bas\b`, "giu");
267
+ const ALTER_VIEW = new RegExp(String.raw`\balter\s+view\s+(?:if\s+exists\s+)?${VIEW_NAME}\s+set\s*\(([^)]*)\)`, "giu");
268
+ const INVOKER = /\bsecurity_invoker\s*(?:=\s*(?:true|on|'true'|'on'|1)\b|[,)]|$)/iu;
269
+ const COMPANION = new RegExp(String.raw`\bcomment\s+on\s+view\s+${VIEW_NAME}\s+is\s+'${FIELD_VIEWS.comment}\b`, "giu");
270
+ /**
271
+ * Views run as their owner unless `security_invoker` is set, so they read
272
+ * past RLS on the tables beneath them. The `<table>_visible_fields`
273
+ * companion `rls generate --revoke-columns` writes reads as its owner on
274
+ * purpose and carries a comment that says so.
275
+ */
276
+ function pd022(cwd, config) {
277
+ if (config.rls === void 0 && config.doctor?.migrations === void 0) return [];
278
+ const created = /* @__PURE__ */ new Map();
279
+ const invoker = /* @__PURE__ */ new Set();
280
+ const companions = /* @__PURE__ */ new Set();
281
+ for (const file of sqlFiles(cwd, config.doctor?.migrations ?? MIGRATION_DIRS)) {
282
+ const raw = readFileSync(file, "utf8");
283
+ const text = raw.replaceAll(/--[^\n]*/gu, "").replaceAll(/\/\*[\s\S]*?\*\//gu, "");
284
+ for (const [, name = "", options = ""] of text.matchAll(CREATE_VIEW)) {
285
+ const key = tableKey(name);
286
+ created.set(key, rel(cwd, file));
287
+ if (INVOKER.test(options)) invoker.add(key);
288
+ else invoker.delete(key);
289
+ }
290
+ for (const [, name = "", options = ""] of text.matchAll(ALTER_VIEW)) if (INVOKER.test(options)) invoker.add(tableKey(name));
291
+ for (const [, name = ""] of raw.matchAll(COMPANION)) companions.add(tableKey(name));
292
+ }
293
+ return [...created].filter(([key]) => !invoker.has(key) && !companions.has(key)).map(([key, file]) => ({
294
+ code: "PD022",
295
+ severity: "warning",
296
+ message: `view ${key} in ${file} is not security_invoker, so it reads past row level security`,
297
+ fix: `create the view with (security_invoker = true), or alter view ${key} set (security_invoker = true); Postgres 15 or later. For column-level reads, generate field views with permdock rls generate --fields views`
298
+ }));
299
+ }
300
+ const AUTH_ROLE = /\bauth\.role\s*\(\s*\)/giu;
301
+ /**
302
+ * PD040: `auth.role()` in a migration. Supabase deprecated it; a policy
303
+ * names its Postgres roles with `to authenticated` instead, and the role
304
+ * claim it reads is the one PostgREST already switched to.
305
+ */
306
+ function pd040(cwd, config) {
307
+ const findings = [];
308
+ for (const file of sqlFiles(cwd, config.doctor?.migrations ?? MIGRATION_DIRS)) {
309
+ const text = readFileSync(file, "utf8").replaceAll(/--[^\n]*/gu, (comment) => " ".repeat(comment.length)).replaceAll(/\/\*[\s\S]*?\*\//gu, (comment) => comment.replaceAll(/[^\n]/gu, " "));
310
+ for (const match of text.matchAll(AUTH_ROLE)) {
311
+ const line = text.slice(0, match.index).split("\n").length;
312
+ findings.push({
313
+ code: "PD040",
314
+ severity: "warning",
315
+ message: `${rel(cwd, file)}:${String(line)} calls auth.role(), which Supabase deprecated`,
316
+ fix: "name the roles on the policy (create policy ... to authenticated) and drop the auth.role() = 'authenticated' test; call permdock_has or permitted_<scope>_ids for permissions"
317
+ });
318
+ }
319
+ }
320
+ return findings;
321
+ }
322
+ const GRANT = /\b(grant|revoke)\s+([\s\S]+?)\s+on\s+(?:table\s+)?([\w."]+)\s+(?:to|from)\s+([\w\s,"]+?)(?:\s+with\s+grant\s+option|\s+cascade|\s+restrict)?\s*;/giu;
323
+ const CLIENT_ROLES = /* @__PURE__ */ new Set([
324
+ "anon",
325
+ "authenticated",
326
+ "public"
327
+ ]);
328
+ function applyPrivilege(writable, verb, part) {
329
+ const match = /^\s*(all(?:\s+privileges)?|insert|update)\s*(?:\(([^)]*)\))?\s*$/iu.exec(part);
330
+ if (match === null) return;
331
+ const kind = (match[1] ?? "").toLowerCase();
332
+ const sets = kind === "insert" ? [writable.insert] : kind === "update" ? [writable.update] : [writable.insert, writable.update];
333
+ const columns = match[2] === void 0 ? ["*"] : match[2].split(",").map((column) => column.trim().replaceAll("\"", ""));
334
+ for (const set of sets) for (const column of columns) if (verb.toLowerCase() === "grant") set.add(column);
335
+ else if (column === "*") set.clear();
336
+ else set.delete(column);
337
+ }
338
+ const CREATE_TABLE = /^create\s+(?:(?:global|local)\s+)?(?:(?:temp|temporary|unlogged)\s+)?table\s+(?:if\s+not\s+exists\s+)?((?:"[^"]+"|\w+)(?:\.(?:"[^"]+"|\w+))?)\s*\(/iu;
339
+ const REVOKE_DEFAULT_TABLES = /^alter\s+default\s+privileges\s+(?:for\s+(?:role|user)\s+[\w\s,"]+?\s+)?in\s+schema\s+"?public"?\s+revoke\s+[\s\S]+?\s+on\s+tables\s+from\s+[\s\S]*\b(?:anon|authenticated)\b/iu;
340
+ /**
341
+ * Columns of `table` a client role may insert or update, per the migrations
342
+ * in order; `*` for every column. On Supabase a table created in `public`
343
+ * starts writable: the default privileges grant `anon` and `authenticated`
344
+ * everything, until a migration revokes them.
345
+ */
346
+ function clientWritable(cwd, config, target) {
347
+ const writable = {
348
+ insert: /* @__PURE__ */ new Set(),
349
+ update: /* @__PURE__ */ new Set()
350
+ };
351
+ let defaults = config.supabase !== void 0 && target.startsWith("public.");
352
+ for (const file of sqlFiles(cwd, config.doctor?.migrations ?? MIGRATION_DIRS)) for (const { text } of sqlStatements(readFileSync(file, "utf8"))) {
353
+ if (REVOKE_DEFAULT_TABLES.test(text)) {
354
+ defaults = false;
355
+ continue;
356
+ }
357
+ const created = CREATE_TABLE.exec(text);
358
+ if (created !== null) {
359
+ if (defaults && tableKey(created[1] ?? "") === target) {
360
+ writable.insert.add("*");
361
+ writable.update.add("*");
362
+ }
363
+ continue;
364
+ }
365
+ for (const [, verb = "", privileges = "", name = "", roles = ""] of `${text};`.matchAll(GRANT)) {
366
+ if (tableKey(name) !== target) continue;
367
+ if (!roles.split(",").map((role) => role.trim().replaceAll("\"", "").toLowerCase()).some((role) => CLIENT_ROLES.has(role))) continue;
368
+ for (const part of privileges.split(/,(?![^(]*\))/u)) applyPrivilege(writable, verb, part);
369
+ }
370
+ }
371
+ return /* @__PURE__ */ new Set([...writable.insert, ...writable.update]);
372
+ }
373
+ /** Membership columns a client can write: a user who sets `user_id` or `role` grants themselves access. */
374
+ function membershipFindings(cwd, config) {
375
+ const findings = [];
376
+ for (const [table, columns] of membershipColumns(config)) {
377
+ const writable = clientWritable(cwd, config, table);
378
+ const exposed = [...columns].filter((column) => writable.has("*") || writable.has(column));
379
+ if (exposed.length > 0) findings.push({
380
+ code: "PD028",
381
+ severity: "warning",
382
+ message: `${table}.${exposed.join(", ")} decide${exposed.length === 1 ? "s" : ""} memberships, and the migrations let anon or authenticated insert or update ${exposed.length === 1 ? "it" : "them"}: a user could give themselves a membership`,
383
+ fix: `revoke insert, update on ${table} from anon, authenticated, then grant update (<columns clients edit>) on ${table} to authenticated; a column-level revoke alone leaves a table-level grant in place`
384
+ });
385
+ }
386
+ return findings;
387
+ }
388
+ /**
389
+ * `attrs` claims must come from server-owned columns: never `user_metadata`,
390
+ * never a column clients can write. Membership sources' deciding columns are
391
+ * held to the same rule.
392
+ */
393
+ function pd028(cwd, config, plan) {
394
+ const memberships = membershipFindings(cwd, config);
395
+ const attrs = config.supabase?.hook?.attrs;
396
+ if (attrs === void 0) return memberships;
397
+ const planned = plan(attrs);
398
+ const findings = planned.errors.map((message) => ({
399
+ code: "PD028",
400
+ severity: "error",
401
+ message,
402
+ fix: "list server-owned columns or app_metadata.<key> entries; user_metadata is user-editable"
403
+ }));
404
+ if (planned.table !== void 0 && planned.columns.length > 0) {
405
+ const writable = clientWritable(cwd, config, tableKey(planned.table));
406
+ const exposed = planned.columns.filter((column) => writable.has("*") || writable.has(column));
407
+ if (exposed.length > 0) findings.push({
408
+ code: "PD028",
409
+ severity: "warning",
410
+ message: `attrs reads ${exposed.join(", ")} from ${tableKey(planned.table)}, which the migrations let anon or authenticated insert or update: a user could set their own attribute`,
411
+ fix: `revoke insert, update on ${tableKey(planned.table)} from anon, authenticated, and grant column-level update only on columns that are not attributes; the generated hook migration refuses to install otherwise`
412
+ });
413
+ }
414
+ return [...findings, ...memberships];
415
+ }
416
+ //#endregion
417
+ //#region src/cli/supabase-config.ts
418
+ const SUPABASE_CONFIG = "supabase/config.toml";
419
+ function table(value) {
420
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return;
421
+ return value;
422
+ }
423
+ function strings(value) {
424
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : void 0;
425
+ }
426
+ /** `supabase/config.toml`, parsed; `undefined` when the project has none. */
427
+ function readSupabaseConfig(cwd) {
428
+ const file = join(cwd, SUPABASE_CONFIG);
429
+ if (!existsSync(file)) return;
430
+ let root;
431
+ try {
432
+ root = parse(readFileSync(file, "utf8"));
433
+ } catch (error) {
434
+ return {
435
+ ok: false,
436
+ error: describeError(error)
437
+ };
438
+ }
439
+ const expiry = table(root["auth"])?.["jwt_expiry"];
440
+ const paths = table(table(root["db"])?.["migrations"])?.["schema_paths"];
441
+ const schemaPaths = strings(paths);
442
+ const apiSchemas = strings(table(root["api"])?.["schemas"]);
443
+ const pgDelta = table(table(root["experimental"])?.["pgdelta"]);
444
+ const schemaPath = pgDelta?.["declarative_schema_path"];
445
+ return {
446
+ ok: true,
447
+ config: {
448
+ ...typeof expiry === "number" ? { jwtExpiry: expiry } : {},
449
+ ...schemaPaths === void 0 ? {} : { schemaPaths },
450
+ ...apiSchemas === void 0 ? {} : { apiSchemas },
451
+ ...pgDelta?.["enabled"] === true ? { pgDelta: { schemaDir: posix.join("supabase", typeof schemaPath === "string" ? schemaPath : "schemas").replace(/\/$/u, "") } } : {}
452
+ }
453
+ };
454
+ }
455
+ /** The parsed config, or `{}` when it is missing or does not parse (PD045 reports that). */
456
+ function supabaseConfig(cwd) {
457
+ const read = readSupabaseConfig(cwd);
458
+ return read?.ok === true ? read.config : {};
459
+ }
460
+ /** PD045: a `supabase/config.toml` the CLI cannot parse, so every check that reads it falls back to defaults. */
461
+ function pd045(cwd) {
462
+ const read = readSupabaseConfig(cwd);
463
+ if (read === void 0 || read.ok) return [];
464
+ return [{
465
+ code: "PD045",
466
+ severity: "warning",
467
+ message: `${SUPABASE_CONFIG} does not parse: ${read.error.replace(/\n[\s\S]*/u, "")}`,
468
+ fix: `fix the TOML syntax in ${SUPABASE_CONFIG}; until then doctor assumes Supabase's defaults for jwt_expiry and schema_paths`
469
+ }];
470
+ }
471
+ //#endregion
472
+ export { pd006 as a, pd022 as c, group as d, splitUndiffed as f, tableKey as h, pd005 as i, pd028 as l, decidingColumns as m, supabaseConfig as n, pd009 as o, sqlStatements as p, MIGRATION_DIRS as r, pd012 as s, pd045 as t, pd040 as u };