@m2-ai/lattice-cli 0.0.0-stage → 0.8.7

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 (743) hide show
  1. package/DISCLOSURE +1 -0
  2. package/README.md +118 -2
  3. package/bundles/expo-mobile/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  4. package/bundles/expo-mobile/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  5. package/bundles/expo-mobile/.agents/skills/lattice-verify/SKILL.md +27 -0
  6. package/bundles/expo-mobile/.claude/agents/lattice-feature-agent.md +10 -0
  7. package/bundles/expo-mobile/.claude/agents/lattice-release-manager.md +9 -0
  8. package/bundles/expo-mobile/.claude/hooks/README.md +9 -0
  9. package/bundles/expo-mobile/.claude/hooks/pre-commit-verify.mjs +10 -0
  10. package/bundles/expo-mobile/.claude/hooks/pre-tool-use.mjs +51 -0
  11. package/bundles/expo-mobile/.claude/rules/lattice-core.md +20 -0
  12. package/bundles/expo-mobile/.claude/settings.json +38 -0
  13. package/bundles/expo-mobile/.claude/skills/lattice-verify/SKILL.md +27 -0
  14. package/bundles/expo-mobile/.codex/agents/lattice-feature-agent.toml +9 -0
  15. package/bundles/expo-mobile/.codex/agents/lattice-release-manager.toml +9 -0
  16. package/bundles/expo-mobile/.cursor/mcp.json +34 -0
  17. package/bundles/expo-mobile/.cursor/rules/lattice-core.mdc +35 -0
  18. package/bundles/expo-mobile/.cursor/rules.md +94 -0
  19. package/bundles/expo-mobile/.env.example +19 -0
  20. package/bundles/expo-mobile/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  21. package/bundles/expo-mobile/.github/agents/lattice-feature-agent.agent.md +7 -0
  22. package/bundles/expo-mobile/.github/agents/lattice-release-manager.agent.md +7 -0
  23. package/bundles/expo-mobile/.github/copilot-instructions.md +37 -0
  24. package/bundles/expo-mobile/.github/instructions/lattice-core.instructions.md +11 -0
  25. package/bundles/expo-mobile/.github/workflows/ci.yml +26 -0
  26. package/bundles/expo-mobile/.gitmessage +24 -0
  27. package/bundles/expo-mobile/.lattice/config.json +9 -0
  28. package/bundles/expo-mobile/.lattice/dependency-security.json +91 -0
  29. package/bundles/expo-mobile/.lattice/prompts/01-feature.md +36 -0
  30. package/bundles/expo-mobile/.lattice/prompts/02-bugfix.md +32 -0
  31. package/bundles/expo-mobile/.lattice/prompts/03-refactor.md +30 -0
  32. package/bundles/expo-mobile/.lattice/prompts/04-test.md +31 -0
  33. package/bundles/expo-mobile/.lattice/prompts/05-polish.md +38 -0
  34. package/bundles/expo-mobile/.lattice/prompts/06-security.md +32 -0
  35. package/bundles/expo-mobile/.lattice/release-workflow.json +55 -0
  36. package/bundles/expo-mobile/.lattice/schemas/feature-handoff.schema.json +264 -0
  37. package/bundles/expo-mobile/.lattice/templates/feature-handoff.json +52 -0
  38. package/bundles/expo-mobile/.mcp.json +35 -0
  39. package/bundles/expo-mobile/AGENTS.md +58 -0
  40. package/bundles/expo-mobile/CLAUDE.md +75 -0
  41. package/bundles/expo-mobile/PROJECT_GUIDE.md +299 -0
  42. package/bundles/expo-mobile/README.md +39 -0
  43. package/bundles/expo-mobile/__tests__/home.test.tsx +22 -0
  44. package/bundles/expo-mobile/app/_layout.tsx +20 -0
  45. package/bundles/expo-mobile/app/index.tsx +72 -0
  46. package/bundles/expo-mobile/app.json +33 -0
  47. package/bundles/expo-mobile/components/ui/Button.tsx +75 -0
  48. package/bundles/expo-mobile/components/ui/Input.tsx +50 -0
  49. package/bundles/expo-mobile/components/ui/index.ts +2 -0
  50. package/bundles/expo-mobile/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  51. package/bundles/expo-mobile/docs/rules/codex.md +62 -0
  52. package/bundles/expo-mobile/eslint.config.js +7 -0
  53. package/bundles/expo-mobile/lib/supabase/client.ts +22 -0
  54. package/bundles/expo-mobile/lib/supabase/realtime.ts +10 -0
  55. package/bundles/expo-mobile/lib/supabase/storage.ts +6 -0
  56. package/bundles/expo-mobile/package.json +65 -0
  57. package/bundles/expo-mobile/plugins/with-lattice-commons-io.cjs +42 -0
  58. package/bundles/expo-mobile/prompts/01-feature-implementation.md +48 -0
  59. package/bundles/expo-mobile/prompts/02-bug-fix.md +43 -0
  60. package/bundles/expo-mobile/prompts/03-safe-refactor.md +38 -0
  61. package/bundles/expo-mobile/prompts/04-test-first.md +41 -0
  62. package/bundles/expo-mobile/prompts/05-ui-polish.md +44 -0
  63. package/bundles/expo-mobile/prompts/06-security-review.md +39 -0
  64. package/bundles/expo-mobile/prompts/quality.md +143 -0
  65. package/bundles/expo-mobile/supabase/migrations/001_initial_schema.sql +66 -0
  66. package/bundles/expo-mobile/tsconfig.json +21 -0
  67. package/bundles/expo-mobile/types/database.types.ts +76 -0
  68. package/bundles/expo-mobile/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  69. package/bundles/expo-supabase/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  70. package/bundles/expo-supabase/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  71. package/bundles/expo-supabase/.agents/skills/lattice-verify/SKILL.md +27 -0
  72. package/bundles/expo-supabase/.claude/agents/lattice-feature-agent.md +10 -0
  73. package/bundles/expo-supabase/.claude/agents/lattice-release-manager.md +9 -0
  74. package/bundles/expo-supabase/.claude/hooks/README.md +9 -0
  75. package/bundles/expo-supabase/.claude/hooks/pre-commit-verify.mjs +10 -0
  76. package/bundles/expo-supabase/.claude/hooks/pre-tool-use.mjs +51 -0
  77. package/bundles/expo-supabase/.claude/rules/lattice-core.md +20 -0
  78. package/bundles/expo-supabase/.claude/settings.json +38 -0
  79. package/bundles/expo-supabase/.claude/skills/lattice-verify/SKILL.md +27 -0
  80. package/bundles/expo-supabase/.codex/agents/lattice-feature-agent.toml +9 -0
  81. package/bundles/expo-supabase/.codex/agents/lattice-release-manager.toml +9 -0
  82. package/bundles/expo-supabase/.cursor/mcp.json +34 -0
  83. package/bundles/expo-supabase/.cursor/rules/lattice-core.mdc +35 -0
  84. package/bundles/expo-supabase/.cursor/rules.md +94 -0
  85. package/bundles/expo-supabase/.env.example +12 -0
  86. package/bundles/expo-supabase/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  87. package/bundles/expo-supabase/.github/agents/lattice-feature-agent.agent.md +7 -0
  88. package/bundles/expo-supabase/.github/agents/lattice-release-manager.agent.md +7 -0
  89. package/bundles/expo-supabase/.github/copilot-instructions.md +37 -0
  90. package/bundles/expo-supabase/.github/instructions/lattice-core.instructions.md +11 -0
  91. package/bundles/expo-supabase/.github/workflows/ci.yml +26 -0
  92. package/bundles/expo-supabase/.gitmessage +24 -0
  93. package/bundles/expo-supabase/.lattice/config.json +9 -0
  94. package/bundles/expo-supabase/.lattice/dependency-security.json +91 -0
  95. package/bundles/expo-supabase/.lattice/prompts/01-feature.md +36 -0
  96. package/bundles/expo-supabase/.lattice/prompts/02-bugfix.md +32 -0
  97. package/bundles/expo-supabase/.lattice/prompts/03-refactor.md +30 -0
  98. package/bundles/expo-supabase/.lattice/prompts/04-test.md +31 -0
  99. package/bundles/expo-supabase/.lattice/prompts/05-polish.md +38 -0
  100. package/bundles/expo-supabase/.lattice/prompts/06-security.md +32 -0
  101. package/bundles/expo-supabase/.lattice/release-workflow.json +55 -0
  102. package/bundles/expo-supabase/.lattice/schemas/feature-handoff.schema.json +264 -0
  103. package/bundles/expo-supabase/.lattice/templates/feature-handoff.json +52 -0
  104. package/bundles/expo-supabase/.mcp.json +35 -0
  105. package/bundles/expo-supabase/AGENTS.md +58 -0
  106. package/bundles/expo-supabase/CLAUDE.md +75 -0
  107. package/bundles/expo-supabase/PROJECT_GUIDE.md +201 -0
  108. package/bundles/expo-supabase/README.md +38 -0
  109. package/bundles/expo-supabase/__tests__/home.test.tsx +22 -0
  110. package/bundles/expo-supabase/app/_layout.tsx +14 -0
  111. package/bundles/expo-supabase/app/index.tsx +72 -0
  112. package/bundles/expo-supabase/app.json +33 -0
  113. package/bundles/expo-supabase/components/ui/Button.tsx +75 -0
  114. package/bundles/expo-supabase/components/ui/Input.tsx +50 -0
  115. package/bundles/expo-supabase/components/ui/index.ts +2 -0
  116. package/bundles/expo-supabase/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  117. package/bundles/expo-supabase/docs/rules/codex.md +62 -0
  118. package/bundles/expo-supabase/eslint.config.js +7 -0
  119. package/bundles/expo-supabase/lib/supabase/client.ts +22 -0
  120. package/bundles/expo-supabase/lib/supabase/realtime.ts +10 -0
  121. package/bundles/expo-supabase/lib/supabase/storage.ts +6 -0
  122. package/bundles/expo-supabase/package.json +60 -0
  123. package/bundles/expo-supabase/plugins/with-lattice-commons-io.cjs +42 -0
  124. package/bundles/expo-supabase/prompts/01-feature-implementation.md +8 -0
  125. package/bundles/expo-supabase/prompts/02-bug-fix.md +9 -0
  126. package/bundles/expo-supabase/prompts/03-safe-refactor.md +8 -0
  127. package/bundles/expo-supabase/prompts/04-test-first.md +7 -0
  128. package/bundles/expo-supabase/prompts/05-ui-polish.md +7 -0
  129. package/bundles/expo-supabase/prompts/06-security-review.md +7 -0
  130. package/bundles/expo-supabase/prompts/quality.md +143 -0
  131. package/bundles/expo-supabase/supabase/migrations/001_initial_schema.sql +66 -0
  132. package/bundles/expo-supabase/tsconfig.json +21 -0
  133. package/bundles/expo-supabase/types/database.types.ts +76 -0
  134. package/bundles/expo-supabase/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  135. package/bundles/nextjs-landing/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  136. package/bundles/nextjs-landing/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  137. package/bundles/nextjs-landing/.agents/skills/lattice-verify/SKILL.md +27 -0
  138. package/bundles/nextjs-landing/.claude/agents/lattice-feature-agent.md +10 -0
  139. package/bundles/nextjs-landing/.claude/agents/lattice-release-manager.md +9 -0
  140. package/bundles/nextjs-landing/.claude/hooks/README.md +9 -0
  141. package/bundles/nextjs-landing/.claude/hooks/pre-commit-verify.mjs +10 -0
  142. package/bundles/nextjs-landing/.claude/hooks/pre-tool-use.mjs +51 -0
  143. package/bundles/nextjs-landing/.claude/rules/lattice-core.md +20 -0
  144. package/bundles/nextjs-landing/.claude/settings.json +38 -0
  145. package/bundles/nextjs-landing/.claude/skills/lattice-verify/SKILL.md +27 -0
  146. package/bundles/nextjs-landing/.codex/agents/lattice-feature-agent.toml +9 -0
  147. package/bundles/nextjs-landing/.codex/agents/lattice-release-manager.toml +9 -0
  148. package/bundles/nextjs-landing/.cursor/mcp.json +34 -0
  149. package/bundles/nextjs-landing/.cursor/rules/lattice-core.mdc +35 -0
  150. package/bundles/nextjs-landing/.cursor/rules.md +95 -0
  151. package/bundles/nextjs-landing/.env.example +24 -0
  152. package/bundles/nextjs-landing/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  153. package/bundles/nextjs-landing/.github/agents/lattice-feature-agent.agent.md +7 -0
  154. package/bundles/nextjs-landing/.github/agents/lattice-release-manager.agent.md +7 -0
  155. package/bundles/nextjs-landing/.github/copilot-instructions.md +37 -0
  156. package/bundles/nextjs-landing/.github/instructions/lattice-core.instructions.md +11 -0
  157. package/bundles/nextjs-landing/.github/workflows/ci.yml +26 -0
  158. package/bundles/nextjs-landing/.gitmessage +24 -0
  159. package/bundles/nextjs-landing/.lattice/config.json +9 -0
  160. package/bundles/nextjs-landing/.lattice/dependency-security.json +46 -0
  161. package/bundles/nextjs-landing/.lattice/prompts/01-feature.md +36 -0
  162. package/bundles/nextjs-landing/.lattice/prompts/02-bugfix.md +32 -0
  163. package/bundles/nextjs-landing/.lattice/prompts/03-refactor.md +30 -0
  164. package/bundles/nextjs-landing/.lattice/prompts/04-test.md +31 -0
  165. package/bundles/nextjs-landing/.lattice/prompts/05-polish.md +38 -0
  166. package/bundles/nextjs-landing/.lattice/prompts/06-security.md +32 -0
  167. package/bundles/nextjs-landing/.lattice/release-workflow.json +55 -0
  168. package/bundles/nextjs-landing/.lattice/schemas/feature-handoff.schema.json +264 -0
  169. package/bundles/nextjs-landing/.lattice/templates/feature-handoff.json +52 -0
  170. package/bundles/nextjs-landing/.mcp.json +35 -0
  171. package/bundles/nextjs-landing/AGENTS.md +58 -0
  172. package/bundles/nextjs-landing/CLAUDE.md +76 -0
  173. package/bundles/nextjs-landing/PROJECT_GUIDE.md +216 -0
  174. package/bundles/nextjs-landing/README.md +39 -0
  175. package/bundles/nextjs-landing/app/api/example/[id]/route.ts +34 -0
  176. package/bundles/nextjs-landing/app/api/example/route.ts +24 -0
  177. package/bundles/nextjs-landing/app/api/health/route.ts +8 -0
  178. package/bundles/nextjs-landing/app/global-error.tsx +17 -0
  179. package/bundles/nextjs-landing/app/globals.css +55 -0
  180. package/bundles/nextjs-landing/app/layout.tsx +22 -0
  181. package/bundles/nextjs-landing/app/page.tsx +42 -0
  182. package/bundles/nextjs-landing/components/ui/button.tsx +39 -0
  183. package/bundles/nextjs-landing/components/ui/card.tsx +53 -0
  184. package/bundles/nextjs-landing/components/ui/index.ts +4 -0
  185. package/bundles/nextjs-landing/components/ui/input.tsx +23 -0
  186. package/bundles/nextjs-landing/components/ui/spinner.tsx +22 -0
  187. package/bundles/nextjs-landing/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  188. package/bundles/nextjs-landing/docs/rules/codex.md +62 -0
  189. package/bundles/nextjs-landing/eslint.config.mjs +16 -0
  190. package/bundles/nextjs-landing/instrumentation-client.ts +30 -0
  191. package/bundles/nextjs-landing/instrumentation.ts +12 -0
  192. package/bundles/nextjs-landing/jest.config.mjs +16 -0
  193. package/bundles/nextjs-landing/jest.setup.js +1 -0
  194. package/bundles/nextjs-landing/lib/utils.ts +6 -0
  195. package/bundles/nextjs-landing/next-env.d.ts +5 -0
  196. package/bundles/nextjs-landing/next.config.ts +15 -0
  197. package/bundles/nextjs-landing/package.json +48 -0
  198. package/bundles/nextjs-landing/postcss.config.mjs +7 -0
  199. package/bundles/nextjs-landing/prompts/01-feature-implementation.md +9 -0
  200. package/bundles/nextjs-landing/prompts/02-bug-fix.md +10 -0
  201. package/bundles/nextjs-landing/prompts/03-safe-refactor.md +8 -0
  202. package/bundles/nextjs-landing/prompts/04-test-first.md +8 -0
  203. package/bundles/nextjs-landing/prompts/05-ui-polish.md +8 -0
  204. package/bundles/nextjs-landing/prompts/06-security-review.md +7 -0
  205. package/bundles/nextjs-landing/prompts/quality.md +269 -0
  206. package/bundles/nextjs-landing/sentry.edge.config.ts +15 -0
  207. package/bundles/nextjs-landing/sentry.server.config.ts +15 -0
  208. package/bundles/nextjs-landing/tsconfig.json +42 -0
  209. package/bundles/nextjs-landing/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  210. package/bundles/nextjs-saas/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  211. package/bundles/nextjs-saas/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  212. package/bundles/nextjs-saas/.agents/skills/lattice-verify/SKILL.md +27 -0
  213. package/bundles/nextjs-saas/.claude/agents/lattice-feature-agent.md +10 -0
  214. package/bundles/nextjs-saas/.claude/agents/lattice-release-manager.md +9 -0
  215. package/bundles/nextjs-saas/.claude/hooks/README.md +9 -0
  216. package/bundles/nextjs-saas/.claude/hooks/pre-commit-verify.mjs +10 -0
  217. package/bundles/nextjs-saas/.claude/hooks/pre-tool-use.mjs +51 -0
  218. package/bundles/nextjs-saas/.claude/rules/lattice-core.md +20 -0
  219. package/bundles/nextjs-saas/.claude/settings.json +38 -0
  220. package/bundles/nextjs-saas/.claude/skills/lattice-verify/SKILL.md +27 -0
  221. package/bundles/nextjs-saas/.codex/agents/lattice-feature-agent.toml +9 -0
  222. package/bundles/nextjs-saas/.codex/agents/lattice-release-manager.toml +9 -0
  223. package/bundles/nextjs-saas/.cursor/mcp.json +34 -0
  224. package/bundles/nextjs-saas/.cursor/rules/lattice-core.mdc +35 -0
  225. package/bundles/nextjs-saas/.cursor/rules.md +95 -0
  226. package/bundles/nextjs-saas/.env.example +48 -0
  227. package/bundles/nextjs-saas/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  228. package/bundles/nextjs-saas/.github/agents/lattice-feature-agent.agent.md +7 -0
  229. package/bundles/nextjs-saas/.github/agents/lattice-release-manager.agent.md +7 -0
  230. package/bundles/nextjs-saas/.github/copilot-instructions.md +37 -0
  231. package/bundles/nextjs-saas/.github/instructions/lattice-core.instructions.md +11 -0
  232. package/bundles/nextjs-saas/.github/workflows/ci.yml +26 -0
  233. package/bundles/nextjs-saas/.gitmessage +24 -0
  234. package/bundles/nextjs-saas/.lattice/config.json +9 -0
  235. package/bundles/nextjs-saas/.lattice/dependency-security.json +46 -0
  236. package/bundles/nextjs-saas/.lattice/prompts/01-feature.md +36 -0
  237. package/bundles/nextjs-saas/.lattice/prompts/02-bugfix.md +32 -0
  238. package/bundles/nextjs-saas/.lattice/prompts/03-refactor.md +30 -0
  239. package/bundles/nextjs-saas/.lattice/prompts/04-test.md +31 -0
  240. package/bundles/nextjs-saas/.lattice/prompts/05-polish.md +38 -0
  241. package/bundles/nextjs-saas/.lattice/prompts/06-security.md +32 -0
  242. package/bundles/nextjs-saas/.lattice/prompts/quality.md +269 -0
  243. package/bundles/nextjs-saas/.lattice/release-workflow.json +55 -0
  244. package/bundles/nextjs-saas/.lattice/schemas/feature-handoff.schema.json +264 -0
  245. package/bundles/nextjs-saas/.lattice/templates/feature-handoff.json +52 -0
  246. package/bundles/nextjs-saas/.mcp.json +35 -0
  247. package/bundles/nextjs-saas/AGENTS.md +58 -0
  248. package/bundles/nextjs-saas/CLAUDE.md +76 -0
  249. package/bundles/nextjs-saas/PROJECT_GUIDE.md +279 -0
  250. package/bundles/nextjs-saas/README.md +42 -0
  251. package/bundles/nextjs-saas/app/(auth)/forgot-password/page.tsx +101 -0
  252. package/bundles/nextjs-saas/app/(auth)/layout.tsx +7 -0
  253. package/bundles/nextjs-saas/app/(auth)/login/page.tsx +108 -0
  254. package/bundles/nextjs-saas/app/(auth)/reset-password/page.tsx +135 -0
  255. package/bundles/nextjs-saas/app/(auth)/signup/page.tsx +144 -0
  256. package/bundles/nextjs-saas/app/(dashboard)/dashboard/page.tsx +80 -0
  257. package/bundles/nextjs-saas/app/(dashboard)/layout.tsx +49 -0
  258. package/bundles/nextjs-saas/app/api/billing/checkout/route.ts +47 -0
  259. package/bundles/nextjs-saas/app/api/billing/portal/route.ts +29 -0
  260. package/bundles/nextjs-saas/app/api/example/[id]/route.ts +93 -0
  261. package/bundles/nextjs-saas/app/api/example/route.ts +61 -0
  262. package/bundles/nextjs-saas/app/api/health/route.ts +8 -0
  263. package/bundles/nextjs-saas/app/api/stripe/webhook/route.ts +57 -0
  264. package/bundles/nextjs-saas/app/api/user/route.ts +49 -0
  265. package/bundles/nextjs-saas/app/auth/callback/route.ts +16 -0
  266. package/bundles/nextjs-saas/app/auth/signout/route.ts +10 -0
  267. package/bundles/nextjs-saas/app/global-error.tsx +17 -0
  268. package/bundles/nextjs-saas/app/globals.css +55 -0
  269. package/bundles/nextjs-saas/app/layout.tsx +22 -0
  270. package/bundles/nextjs-saas/app/page.tsx +84 -0
  271. package/bundles/nextjs-saas/components/ui/button.tsx +39 -0
  272. package/bundles/nextjs-saas/components/ui/card.tsx +53 -0
  273. package/bundles/nextjs-saas/components/ui/index.ts +4 -0
  274. package/bundles/nextjs-saas/components/ui/input.tsx +23 -0
  275. package/bundles/nextjs-saas/components/ui/spinner.tsx +22 -0
  276. package/bundles/nextjs-saas/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  277. package/bundles/nextjs-saas/docs/rules/codex.md +62 -0
  278. package/bundles/nextjs-saas/eslint.config.mjs +16 -0
  279. package/bundles/nextjs-saas/instrumentation-client.ts +30 -0
  280. package/bundles/nextjs-saas/instrumentation.ts +12 -0
  281. package/bundles/nextjs-saas/jest.config.mjs +16 -0
  282. package/bundles/nextjs-saas/jest.setup.js +1 -0
  283. package/bundles/nextjs-saas/lib/auth/password-recovery.ts +94 -0
  284. package/bundles/nextjs-saas/lib/stripe.ts +10 -0
  285. package/bundles/nextjs-saas/lib/supabase/admin.ts +14 -0
  286. package/bundles/nextjs-saas/lib/supabase/client.ts +8 -0
  287. package/bundles/nextjs-saas/lib/supabase/server.ts +27 -0
  288. package/bundles/nextjs-saas/lib/utils.ts +6 -0
  289. package/bundles/nextjs-saas/next-env.d.ts +5 -0
  290. package/bundles/nextjs-saas/next.config.ts +15 -0
  291. package/bundles/nextjs-saas/package.json +50 -0
  292. package/bundles/nextjs-saas/postcss.config.mjs +7 -0
  293. package/bundles/nextjs-saas/prompts/01-feature-implementation.md +92 -0
  294. package/bundles/nextjs-saas/prompts/02-bug-fix.md +78 -0
  295. package/bundles/nextjs-saas/prompts/03-safe-refactor.md +82 -0
  296. package/bundles/nextjs-saas/prompts/04-test-first.md +80 -0
  297. package/bundles/nextjs-saas/prompts/05-ui-polish.md +82 -0
  298. package/bundles/nextjs-saas/prompts/06-security-review.md +74 -0
  299. package/bundles/nextjs-saas/proxy.ts +61 -0
  300. package/bundles/nextjs-saas/sentry.edge.config.ts +15 -0
  301. package/bundles/nextjs-saas/sentry.server.config.ts +15 -0
  302. package/bundles/nextjs-saas/supabase/migrations/001_initial_schema.sql +66 -0
  303. package/bundles/nextjs-saas/supabase/migrations/002_billing.sql +17 -0
  304. package/bundles/nextjs-saas/tsconfig.json +42 -0
  305. package/bundles/nextjs-saas/types/database.types.ts +76 -0
  306. package/bundles/nextjs-saas/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  307. package/bundles/nextjs-trpc/.agents/skills/lattice-feature-handoff/SKILL.md +31 -0
  308. package/bundles/nextjs-trpc/.agents/skills/lattice-release-manager/SKILL.md +29 -0
  309. package/bundles/nextjs-trpc/.agents/skills/lattice-verify/SKILL.md +27 -0
  310. package/bundles/nextjs-trpc/.claude/agents/lattice-feature-agent.md +10 -0
  311. package/bundles/nextjs-trpc/.claude/agents/lattice-release-manager.md +9 -0
  312. package/bundles/nextjs-trpc/.claude/hooks/README.md +9 -0
  313. package/bundles/nextjs-trpc/.claude/hooks/pre-commit-verify.mjs +10 -0
  314. package/bundles/nextjs-trpc/.claude/hooks/pre-tool-use.mjs +51 -0
  315. package/bundles/nextjs-trpc/.claude/rules/lattice-core.md +20 -0
  316. package/bundles/nextjs-trpc/.claude/settings.json +38 -0
  317. package/bundles/nextjs-trpc/.claude/skills/lattice-verify/SKILL.md +27 -0
  318. package/bundles/nextjs-trpc/.codex/agents/lattice-feature-agent.toml +9 -0
  319. package/bundles/nextjs-trpc/.codex/agents/lattice-release-manager.toml +9 -0
  320. package/bundles/nextjs-trpc/.cursor/mcp.json +34 -0
  321. package/bundles/nextjs-trpc/.cursor/rules/lattice-core.mdc +35 -0
  322. package/bundles/nextjs-trpc/.cursor/rules.md +95 -0
  323. package/bundles/nextjs-trpc/.env.example +21 -0
  324. package/bundles/nextjs-trpc/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  325. package/bundles/nextjs-trpc/.github/agents/lattice-feature-agent.agent.md +7 -0
  326. package/bundles/nextjs-trpc/.github/agents/lattice-release-manager.agent.md +7 -0
  327. package/bundles/nextjs-trpc/.github/copilot-instructions.md +37 -0
  328. package/bundles/nextjs-trpc/.github/instructions/lattice-core.instructions.md +11 -0
  329. package/bundles/nextjs-trpc/.github/workflows/ci.yml +26 -0
  330. package/bundles/nextjs-trpc/.gitmessage +24 -0
  331. package/bundles/nextjs-trpc/.lattice/config.json +9 -0
  332. package/bundles/nextjs-trpc/.lattice/dependency-security.json +46 -0
  333. package/bundles/nextjs-trpc/.lattice/prompts/01-feature.md +36 -0
  334. package/bundles/nextjs-trpc/.lattice/prompts/02-bugfix.md +32 -0
  335. package/bundles/nextjs-trpc/.lattice/prompts/03-refactor.md +30 -0
  336. package/bundles/nextjs-trpc/.lattice/prompts/04-test.md +31 -0
  337. package/bundles/nextjs-trpc/.lattice/prompts/05-polish.md +38 -0
  338. package/bundles/nextjs-trpc/.lattice/prompts/06-security.md +32 -0
  339. package/bundles/nextjs-trpc/.lattice/release-workflow.json +55 -0
  340. package/bundles/nextjs-trpc/.lattice/schemas/feature-handoff.schema.json +264 -0
  341. package/bundles/nextjs-trpc/.lattice/templates/feature-handoff.json +52 -0
  342. package/bundles/nextjs-trpc/.mcp.json +35 -0
  343. package/bundles/nextjs-trpc/AGENTS.md +58 -0
  344. package/bundles/nextjs-trpc/CLAUDE.md +76 -0
  345. package/bundles/nextjs-trpc/PROJECT_GUIDE.md +187 -0
  346. package/bundles/nextjs-trpc/README.md +49 -0
  347. package/bundles/nextjs-trpc/app/api/trpc/[trpc]/route.ts +12 -0
  348. package/bundles/nextjs-trpc/app/global-error.tsx +17 -0
  349. package/bundles/nextjs-trpc/app/globals.css +55 -0
  350. package/bundles/nextjs-trpc/app/layout.tsx +19 -0
  351. package/bundles/nextjs-trpc/app/page.tsx +16 -0
  352. package/bundles/nextjs-trpc/app/providers.tsx +24 -0
  353. package/bundles/nextjs-trpc/components/ui/button.tsx +39 -0
  354. package/bundles/nextjs-trpc/components/ui/card.tsx +53 -0
  355. package/bundles/nextjs-trpc/components/ui/index.ts +4 -0
  356. package/bundles/nextjs-trpc/components/ui/input.tsx +23 -0
  357. package/bundles/nextjs-trpc/components/ui/spinner.tsx +22 -0
  358. package/bundles/nextjs-trpc/components/user-list.tsx +17 -0
  359. package/bundles/nextjs-trpc/docs/LATTICE_RELEASE_WORKFLOW.md +53 -0
  360. package/bundles/nextjs-trpc/docs/rules/codex.md +62 -0
  361. package/bundles/nextjs-trpc/eslint.config.mjs +16 -0
  362. package/bundles/nextjs-trpc/instrumentation-client.ts +20 -0
  363. package/bundles/nextjs-trpc/instrumentation.ts +12 -0
  364. package/bundles/nextjs-trpc/jest.config.mjs +16 -0
  365. package/bundles/nextjs-trpc/jest.setup.js +1 -0
  366. package/bundles/nextjs-trpc/lib/db.ts +17 -0
  367. package/bundles/nextjs-trpc/lib/trpc/react.tsx +7 -0
  368. package/bundles/nextjs-trpc/lib/utils.ts +6 -0
  369. package/bundles/nextjs-trpc/next-env.d.ts +5 -0
  370. package/bundles/nextjs-trpc/next.config.ts +15 -0
  371. package/bundles/nextjs-trpc/package.json +62 -0
  372. package/bundles/nextjs-trpc/postcss.config.mjs +7 -0
  373. package/bundles/nextjs-trpc/prisma/schema.prisma +41 -0
  374. package/bundles/nextjs-trpc/prisma.config.ts +10 -0
  375. package/bundles/nextjs-trpc/prompts/01-feature-implementation.md +9 -0
  376. package/bundles/nextjs-trpc/prompts/02-bug-fix.md +9 -0
  377. package/bundles/nextjs-trpc/prompts/03-safe-refactor.md +8 -0
  378. package/bundles/nextjs-trpc/prompts/04-test-first.md +8 -0
  379. package/bundles/nextjs-trpc/prompts/05-ui-polish.md +7 -0
  380. package/bundles/nextjs-trpc/prompts/06-security-review.md +7 -0
  381. package/bundles/nextjs-trpc/prompts/quality.md +269 -0
  382. package/bundles/nextjs-trpc/sentry.edge.config.ts +15 -0
  383. package/bundles/nextjs-trpc/sentry.server.config.ts +15 -0
  384. package/bundles/nextjs-trpc/server/routers/_app.test.ts +60 -0
  385. package/bundles/nextjs-trpc/server/routers/_app.ts +25 -0
  386. package/bundles/nextjs-trpc/server/trpc.ts +12 -0
  387. package/bundles/nextjs-trpc/tsconfig.json +42 -0
  388. package/bundles/nextjs-trpc/vendor/m2ai-braces-3.0.3-m2ai.1.tar +0 -0
  389. package/dist/agent-install-session.d.ts +17 -0
  390. package/dist/agent-install-session.d.ts.map +1 -0
  391. package/dist/agent-install-session.js +106 -0
  392. package/dist/agent-install-session.js.map +1 -0
  393. package/dist/agent-install-session.test.d.ts +2 -0
  394. package/dist/agent-install-session.test.d.ts.map +1 -0
  395. package/dist/agent-install-session.test.js +62 -0
  396. package/dist/agent-install-session.test.js.map +1 -0
  397. package/dist/agent-install.d.ts +41 -0
  398. package/dist/agent-install.d.ts.map +1 -0
  399. package/dist/agent-install.js +42 -0
  400. package/dist/agent-install.js.map +1 -0
  401. package/dist/agent-install.test.d.ts +2 -0
  402. package/dist/agent-install.test.d.ts.map +1 -0
  403. package/dist/agent-install.test.js +58 -0
  404. package/dist/agent-install.test.js.map +1 -0
  405. package/dist/apply-merge.test.d.ts +2 -0
  406. package/dist/apply-merge.test.d.ts.map +1 -0
  407. package/dist/apply-merge.test.js +123 -0
  408. package/dist/apply-merge.test.js.map +1 -0
  409. package/dist/bundle-download.d.ts +23 -0
  410. package/dist/bundle-download.d.ts.map +1 -0
  411. package/dist/bundle-download.js +180 -0
  412. package/dist/bundle-download.js.map +1 -0
  413. package/dist/bundle-download.test.d.ts +2 -0
  414. package/dist/bundle-download.test.d.ts.map +1 -0
  415. package/dist/bundle-download.test.js +200 -0
  416. package/dist/bundle-download.test.js.map +1 -0
  417. package/dist/bundle-rollback.test.d.ts +2 -0
  418. package/dist/bundle-rollback.test.d.ts.map +1 -0
  419. package/dist/bundle-rollback.test.js +127 -0
  420. package/dist/bundle-rollback.test.js.map +1 -0
  421. package/dist/bundle-scaffold.test.d.ts +2 -0
  422. package/dist/bundle-scaffold.test.d.ts.map +1 -0
  423. package/dist/bundle-scaffold.test.js +105 -0
  424. package/dist/bundle-scaffold.test.js.map +1 -0
  425. package/dist/commands.d.ts +132 -0
  426. package/dist/commands.d.ts.map +1 -0
  427. package/dist/commands.js +2012 -0
  428. package/dist/commands.js.map +1 -0
  429. package/dist/credential-store.d.ts +27 -0
  430. package/dist/credential-store.d.ts.map +1 -0
  431. package/dist/credential-store.js +48 -0
  432. package/dist/credential-store.js.map +1 -0
  433. package/dist/credential-store.test.d.ts +2 -0
  434. package/dist/credential-store.test.d.ts.map +1 -0
  435. package/dist/credential-store.test.js +80 -0
  436. package/dist/credential-store.test.js.map +1 -0
  437. package/dist/delivered-project-files.d.ts +31 -0
  438. package/dist/delivered-project-files.d.ts.map +1 -0
  439. package/dist/delivered-project-files.js +158 -0
  440. package/dist/delivered-project-files.js.map +1 -0
  441. package/dist/delivered-project-files.test.d.ts +2 -0
  442. package/dist/delivered-project-files.test.d.ts.map +1 -0
  443. package/dist/delivered-project-files.test.js +107 -0
  444. package/dist/delivered-project-files.test.js.map +1 -0
  445. package/dist/doctor-agents.test.d.ts +2 -0
  446. package/dist/doctor-agents.test.d.ts.map +1 -0
  447. package/dist/doctor-agents.test.js +65 -0
  448. package/dist/doctor-agents.test.js.map +1 -0
  449. package/dist/hermes-install.d.ts +102 -0
  450. package/dist/hermes-install.d.ts.map +1 -0
  451. package/dist/hermes-install.js +641 -0
  452. package/dist/hermes-install.js.map +1 -0
  453. package/dist/hermes-install.test.d.ts +2 -0
  454. package/dist/hermes-install.test.d.ts.map +1 -0
  455. package/dist/hermes-install.test.js +866 -0
  456. package/dist/hermes-install.test.js.map +1 -0
  457. package/dist/index.d.ts +115 -0
  458. package/dist/index.d.ts.map +1 -0
  459. package/dist/index.js +5311 -0
  460. package/dist/index.js.map +1 -0
  461. package/dist/openclaw-install.d.ts +67 -0
  462. package/dist/openclaw-install.d.ts.map +1 -0
  463. package/dist/openclaw-install.js +358 -0
  464. package/dist/openclaw-install.js.map +1 -0
  465. package/dist/openclaw-install.test.d.ts +2 -0
  466. package/dist/openclaw-install.test.d.ts.map +1 -0
  467. package/dist/openclaw-install.test.js +296 -0
  468. package/dist/openclaw-install.test.js.map +1 -0
  469. package/dist/password-recovery-bundle.test.d.ts +2 -0
  470. package/dist/password-recovery-bundle.test.d.ts.map +1 -0
  471. package/dist/password-recovery-bundle.test.js +132 -0
  472. package/dist/password-recovery-bundle.test.js.map +1 -0
  473. package/dist/project-bundle-env.d.ts +19 -0
  474. package/dist/project-bundle-env.d.ts.map +1 -0
  475. package/dist/project-bundle-env.js +111 -0
  476. package/dist/project-bundle-env.js.map +1 -0
  477. package/dist/project-bundle-env.test.d.ts +2 -0
  478. package/dist/project-bundle-env.test.d.ts.map +1 -0
  479. package/dist/project-bundle-env.test.js +93 -0
  480. package/dist/project-bundle-env.test.js.map +1 -0
  481. package/dist/project-path-confinement.test.d.ts +2 -0
  482. package/dist/project-path-confinement.test.d.ts.map +1 -0
  483. package/dist/project-path-confinement.test.js +222 -0
  484. package/dist/project-path-confinement.test.js.map +1 -0
  485. package/dist/security/api.d.ts +18 -0
  486. package/dist/security/api.d.ts.map +1 -0
  487. package/dist/security/api.js +238 -0
  488. package/dist/security/api.js.map +1 -0
  489. package/dist/security/api.test.d.ts +2 -0
  490. package/dist/security/api.test.d.ts.map +1 -0
  491. package/dist/security/api.test.js +220 -0
  492. package/dist/security/api.test.js.map +1 -0
  493. package/dist/security/artifact.d.ts +10 -0
  494. package/dist/security/artifact.d.ts.map +1 -0
  495. package/dist/security/artifact.js +283 -0
  496. package/dist/security/artifact.js.map +1 -0
  497. package/dist/security/artifact.test.d.ts +2 -0
  498. package/dist/security/artifact.test.d.ts.map +1 -0
  499. package/dist/security/artifact.test.js +229 -0
  500. package/dist/security/artifact.test.js.map +1 -0
  501. package/dist/security/commands.d.ts +42 -0
  502. package/dist/security/commands.d.ts.map +1 -0
  503. package/dist/security/commands.js +825 -0
  504. package/dist/security/commands.js.map +1 -0
  505. package/dist/security/commands.test.d.ts +2 -0
  506. package/dist/security/commands.test.d.ts.map +1 -0
  507. package/dist/security/commands.test.js +1051 -0
  508. package/dist/security/commands.test.js.map +1 -0
  509. package/dist/security/connection.d.ts +19 -0
  510. package/dist/security/connection.d.ts.map +1 -0
  511. package/dist/security/connection.js +145 -0
  512. package/dist/security/connection.js.map +1 -0
  513. package/dist/security/connection.test.d.ts +2 -0
  514. package/dist/security/connection.test.d.ts.map +1 -0
  515. package/dist/security/connection.test.js +62 -0
  516. package/dist/security/connection.test.js.map +1 -0
  517. package/dist/security/index-routing.test.d.ts +2 -0
  518. package/dist/security/index-routing.test.d.ts.map +1 -0
  519. package/dist/security/index-routing.test.js +169 -0
  520. package/dist/security/index-routing.test.js.map +1 -0
  521. package/dist/security/journal.d.ts +26 -0
  522. package/dist/security/journal.d.ts.map +1 -0
  523. package/dist/security/journal.js +441 -0
  524. package/dist/security/journal.js.map +1 -0
  525. package/dist/security/journal.test.d.ts +2 -0
  526. package/dist/security/journal.test.d.ts.map +1 -0
  527. package/dist/security/journal.test.js +226 -0
  528. package/dist/security/journal.test.js.map +1 -0
  529. package/dist/security/package-verifier.d.ts +15 -0
  530. package/dist/security/package-verifier.d.ts.map +1 -0
  531. package/dist/security/package-verifier.js +65 -0
  532. package/dist/security/package-verifier.js.map +1 -0
  533. package/dist/security/package-verifier.test.d.ts +2 -0
  534. package/dist/security/package-verifier.test.d.ts.map +1 -0
  535. package/dist/security/package-verifier.test.js +57 -0
  536. package/dist/security/package-verifier.test.js.map +1 -0
  537. package/dist/security/runtime.d.ts +43 -0
  538. package/dist/security/runtime.d.ts.map +1 -0
  539. package/dist/security/runtime.js +81 -0
  540. package/dist/security/runtime.js.map +1 -0
  541. package/dist/security/runtime.test.d.ts +2 -0
  542. package/dist/security/runtime.test.d.ts.map +1 -0
  543. package/dist/security/runtime.test.js +88 -0
  544. package/dist/security/runtime.test.js.map +1 -0
  545. package/dist/security/types.d.ts +169 -0
  546. package/dist/security/types.d.ts.map +1 -0
  547. package/dist/security/types.js +3 -0
  548. package/dist/security/types.js.map +1 -0
  549. package/dist/security-runtime/LICENSE +202 -0
  550. package/dist/security-runtime/NOTICE +16 -0
  551. package/dist/security-runtime/artifact-manifest.json +960 -0
  552. package/dist/security-runtime/bin/vsg.mjs +5 -0
  553. package/dist/security-runtime/lattice-catalog.json +176585 -0
  554. package/dist/security-runtime/lattice-runtime.mjs +90 -0
  555. package/dist/security-runtime/licensing/candidates/apache-source-license-evidence.json +478 -0
  556. package/dist/security-runtime/licensing/candidates/betterleaks-generic-api-key.json +76 -0
  557. package/dist/security-runtime/licensing/candidates/betterleaks-jwt.json +74 -0
  558. package/dist/security-runtime/licensing/candidates/blackberry-additional-source-declaration-evidence.json +276 -0
  559. package/dist/security-runtime/licensing/candidates/blackberry-source-declaration-evidence.json +369 -0
  560. package/dist/security-runtime/licensing/candidates/cado-source-declaration-evidence.json +283 -0
  561. package/dist/security-runtime/licensing/candidates/commercial-rule-candidate.json +280 -0
  562. package/dist/security-runtime/licensing/candidates/commercial-runtime-release.json +389 -0
  563. package/dist/security-runtime/licensing/candidates/data-feed-evidence.json +259 -0
  564. package/dist/security-runtime/licensing/candidates/gitlab-sast-rule-identities.json +178 -0
  565. package/dist/security-runtime/licensing/candidates/gitlab-sast-source-evidence.json +1824 -0
  566. package/dist/security-runtime/licensing/candidates/gitlab-sast-source-notice-bundle.json +45 -0
  567. package/dist/security-runtime/licensing/candidates/malpedia-source-license-evidence.json +41 -0
  568. package/dist/security-runtime/licensing/candidates/rule-license-evidence.json +376 -0
  569. package/dist/security-runtime/licensing/candidates/runner-image-composition.json +1500 -0
  570. package/dist/security-runtime/licensing/candidates/selected-runtime-coverage.json +91 -0
  571. package/dist/security-runtime/licensing/candidates/yara-forge-20260927.json +58502 -0
  572. package/dist/security-runtime/licensing/commercial-components.json +379 -0
  573. package/dist/security-runtime/licensing/notices/betterleaks-1.9.0-MIT.txt +21 -0
  574. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/28a9529c7d0bb4dc51f4bf5c116a3d16ef247a052f7591466768ddf563fd1cf5.txt +428 -0
  575. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/33acf42afdd03163d75fe856cb431e70710e0b5deede3362e6d1c4697cfe1c6a.txt +28 -0
  576. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986.txt +674 -0
  577. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/4fecb8fcaf80fd9a6315bca5225ee43377015b46a0459c2925495be885cab19a.txt +21 -0
  578. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/811515f8723aa4007de2fbb75a364127fdec51cda534b87a7ea8f2fef2f96dd7.txt +24 -0
  579. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/814442c3987a2c0411171ad8ad43118e8c08f85c9c5d1dcdecc9787c7ca8689b.txt +664 -0
  580. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/8a959b2413d0c83ca1b3ce9153076c649abf2e4bbfebbb493fb24c2074a1eb8a.txt +38 -0
  581. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/995182e185856186fbbe49f150409ae026aa4cda7e94f83c6ed737cbb77fa9c7.txt +23 -0
  582. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/9b8e1305e46d1dd4011b6ed933c9f6223e08c9c4625001c5fd75a34fa930f616.txt +202 -0
  583. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/a01f1a28424bfe7f49a23d339a01847cfa9ace3864d5e7295051772a4bae2271.txt +38 -0
  584. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/a5681bf9b05db14d86776930017c647ad9e6e56ff6bbcfdf21e5848288dfaf1b.txt +165 -0
  585. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/a5b8bd9ae96c630bbf229d61c5c5def20274e00be39abfd2fab4d7879ceab462.txt +24 -0
  586. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/ada32b3747ce108d5e8bab7918af94abbd03f2fe2a46304f74c6333bbfd914ed.txt +8 -0
  587. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt +201 -0
  588. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/c738ae413cf561f174e34f6961f8ca458aae2369a73640dda6234c629b98bcc4.txt +2732 -0
  589. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/ce41c9a2c986277f7f9ab146a2195c1888a5d8da062e22d7c9c88666bb7c38cd.txt +17 -0
  590. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/d5f4c36a0528e01cfdd9e4fc60879201d3af49b41c520200643d321fe53da5f9.txt +17 -0
  591. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/e08c368ccd455f85c7fb9e04830e730f26e9dbb5c0f77bc88e09a5b0cb37df5b.txt +38 -0
  592. package/dist/security-runtime/licensing/notices/candidate-sources/sha256/e9efb2f7daf11bb6379c06c16b870dddcecc4a10f8704bb8aad807529c8b3c97.txt +8 -0
  593. package/dist/security-runtime/licensing/notices/gitleaks-generic-origin-782f3104-MIT.txt +21 -0
  594. package/dist/security-runtime/package.json +17 -0
  595. package/dist/security-runtime/playbook/README.md +76 -0
  596. package/dist/security-runtime/playbook/access-control.md +56 -0
  597. package/dist/security-runtime/playbook/ai-features.md +53 -0
  598. package/dist/security-runtime/playbook/desktop-python.md +59 -0
  599. package/dist/security-runtime/playbook/input-handling.md +59 -0
  600. package/dist/security-runtime/playbook/mobile-expo.md +59 -0
  601. package/dist/security-runtime/playbook/payments.md +53 -0
  602. package/dist/security-runtime/playbook/privacy-account-recovery.md +59 -0
  603. package/dist/security-runtime/playbook/secrets-config.md +47 -0
  604. package/dist/security-runtime/playbook/supabase.md +48 -0
  605. package/dist/security-runtime/playbook/supply-chain.md +62 -0
  606. package/dist/security-runtime/playbook/webhooks-integrations.md +52 -0
  607. package/dist/security-runtime/rules/vsg-ai-key-in-client.yaml +41 -0
  608. package/dist/security-runtime/rules/vsg-ai-output-raw-html.yaml +35 -0
  609. package/dist/security-runtime/rules/vsg-angular-sanitizer-bypass.yaml +23 -0
  610. package/dist/security-runtime/rules/vsg-expo-token-in-asyncstorage.yaml +22 -0
  611. package/dist/security-runtime/rules/vsg-flask-public-bind.yaml +32 -0
  612. package/dist/security-runtime/rules/vsg-nextjs-action-write-no-auth.yaml +144 -0
  613. package/dist/security-runtime/rules/vsg-nextjs-inline-action-write-no-auth.yaml +161 -0
  614. package/dist/security-runtime/rules/vsg-nextjs-route-write-no-auth.yaml +193 -0
  615. package/dist/security-runtime/rules/vsg-nosql-where-injection.yaml +36 -0
  616. package/dist/security-runtime/rules/vsg-python-pickle-load.yaml +26 -0
  617. package/dist/security-runtime/rules/vsg-python-requests-verify-false.yaml +34 -0
  618. package/dist/security-runtime/rules/vsg-python-subprocess-shell.yaml +23 -0
  619. package/dist/security-runtime/rules/vsg-stripe-client-price.yaml +51 -0
  620. package/dist/security-runtime/rules/vsg-stripe-webhook-no-signature.yaml +31 -0
  621. package/dist/security-runtime/rules/vsg-stripe-webhook-pages-no-signature.yaml +51 -0
  622. package/dist/security-runtime/rules/vsg-supabase-service-role-in-client.yaml +27 -0
  623. package/dist/security-runtime/rules/vsg-supabase-service-role-public-env.yaml +24 -0
  624. package/dist/security-runtime/rules/vsg-xml-external-entities.yaml +19 -0
  625. package/dist/security-runtime/src/agent/brief.mjs +95 -0
  626. package/dist/security-runtime/src/agent/discover.mjs +108 -0
  627. package/dist/security-runtime/src/agent/exec.mjs +64 -0
  628. package/dist/security-runtime/src/agent/ingest.mjs +213 -0
  629. package/dist/security-runtime/src/agent/report.mjs +94 -0
  630. package/dist/security-runtime/src/agent/schema.mjs +62 -0
  631. package/dist/security-runtime/src/agent/tokenize.mjs +38 -0
  632. package/dist/security-runtime/src/checks/README.md +8 -0
  633. package/dist/security-runtime/src/checks/_lib/gitfiles.mjs +35 -0
  634. package/dist/security-runtime/src/checks/_lib/lockfiles.mjs +135 -0
  635. package/dist/security-runtime/src/checks/_lib/osv.mjs +6 -0
  636. package/dist/security-runtime/src/checks/_lib/secrets.mjs +381 -0
  637. package/dist/security-runtime/src/checks/_lib/targets.mjs +79 -0
  638. package/dist/security-runtime/src/checks/agent-review/index.mjs +52 -0
  639. package/dist/security-runtime/src/checks/agent-surface/config.mjs +223 -0
  640. package/dist/security-runtime/src/checks/agent-surface/index.mjs +113 -0
  641. package/dist/security-runtime/src/checks/agent-surface/instructions.mjs +87 -0
  642. package/dist/security-runtime/src/checks/agent-surface/patterns.mjs +68 -0
  643. package/dist/security-runtime/src/checks/agent-surface/unicode.mjs +88 -0
  644. package/dist/security-runtime/src/checks/code/auth-helpers.mjs +659 -0
  645. package/dist/security-runtime/src/checks/code/index.mjs +617 -0
  646. package/dist/security-runtime/src/checks/code/opengrep-container.mjs +85 -0
  647. package/dist/security-runtime/src/checks/code/opengrep-runner.mjs +86 -0
  648. package/dist/security-runtime/src/checks/code/selected-opengrep-runner.mjs +112 -0
  649. package/dist/security-runtime/src/checks/dep-change/guarddog.mjs +142 -0
  650. package/dist/security-runtime/src/checks/dep-change/index.mjs +316 -0
  651. package/dist/security-runtime/src/checks/dep-change/registries.mjs +199 -0
  652. package/dist/security-runtime/src/checks/deps-vuln/index.mjs +305 -0
  653. package/dist/security-runtime/src/checks/desktop-config/index.mjs +112 -0
  654. package/dist/security-runtime/src/checks/dns-mail/index.mjs +71 -0
  655. package/dist/security-runtime/src/checks/iac/index.mjs +83 -0
  656. package/dist/security-runtime/src/checks/install-posture/index.mjs +123 -0
  657. package/dist/security-runtime/src/checks/licenses/index.mjs +143 -0
  658. package/dist/security-runtime/src/checks/live-basic/index.mjs +77 -0
  659. package/dist/security-runtime/src/checks/live-js-secrets/index.mjs +100 -0
  660. package/dist/security-runtime/src/checks/malware/clamav.mjs +17 -0
  661. package/dist/security-runtime/src/checks/malware/defender.mjs +43 -0
  662. package/dist/security-runtime/src/checks/malware/index.mjs +186 -0
  663. package/dist/security-runtime/src/checks/malware/targets.mjs +147 -0
  664. package/dist/security-runtime/src/checks/malware/yara-runner.mjs +201 -0
  665. package/dist/security-runtime/src/checks/malware/yara.mjs +124 -0
  666. package/dist/security-runtime/src/checks/mobile-config/index.mjs +173 -0
  667. package/dist/security-runtime/src/checks/mobile-config/mobsf.mjs +105 -0
  668. package/dist/security-runtime/src/checks/posture/index.mjs +133 -0
  669. package/dist/security-runtime/src/checks/project-scripts/index.mjs +187 -0
  670. package/dist/security-runtime/src/checks/sbom/index.mjs +98 -0
  671. package/dist/security-runtime/src/checks/secrets-build/index.mjs +63 -0
  672. package/dist/security-runtime/src/checks/secrets-history/index.mjs +112 -0
  673. package/dist/security-runtime/src/checks/secrets-tree/env.mjs +120 -0
  674. package/dist/security-runtime/src/checks/secrets-tree/index.mjs +73 -0
  675. package/dist/security-runtime/src/checks/supabase-static/index.mjs +240 -0
  676. package/dist/security-runtime/src/checks/supabase-static/sql.mjs +557 -0
  677. package/dist/security-runtime/src/checks/workflows/index.mjs +93 -0
  678. package/dist/security-runtime/src/cli/deep.mjs +13 -0
  679. package/dist/security-runtime/src/cli/doctor.mjs +82 -0
  680. package/dist/security-runtime/src/cli/index.mjs +60 -0
  681. package/dist/security-runtime/src/cli/live-common.mjs +27 -0
  682. package/dist/security-runtime/src/cli/parse.mjs +56 -0
  683. package/dist/security-runtime/src/cli/portfolio.mjs +34 -0
  684. package/dist/security-runtime/src/cli/release.mjs +28 -0
  685. package/dist/security-runtime/src/cli/report.mjs +18 -0
  686. package/dist/security-runtime/src/cli/run-common.mjs +54 -0
  687. package/dist/security-runtime/src/cli/scan.mjs +12 -0
  688. package/dist/security-runtime/src/cli/setup.mjs +59 -0
  689. package/dist/security-runtime/src/cli/url.mjs +38 -0
  690. package/dist/security-runtime/src/core/accept.mjs +141 -0
  691. package/dist/security-runtime/src/core/archive.mjs +114 -0
  692. package/dist/security-runtime/src/core/base-ref.mjs +43 -0
  693. package/dist/security-runtime/src/core/checks.mjs +39 -0
  694. package/dist/security-runtime/src/core/detect.mjs +90 -0
  695. package/dist/security-runtime/src/core/errors.mjs +5 -0
  696. package/dist/security-runtime/src/core/fetch.mjs +139 -0
  697. package/dist/security-runtime/src/core/findings.mjs +177 -0
  698. package/dist/security-runtime/src/core/gate.mjs +131 -0
  699. package/dist/security-runtime/src/core/installer.mjs +255 -0
  700. package/dist/security-runtime/src/core/lock.mjs +38 -0
  701. package/dist/security-runtime/src/core/nested.mjs +107 -0
  702. package/dist/security-runtime/src/core/paths.mjs +20 -0
  703. package/dist/security-runtime/src/core/pipeline.mjs +196 -0
  704. package/dist/security-runtime/src/core/registry.mjs +81 -0
  705. package/dist/security-runtime/src/core/report.mjs +128 -0
  706. package/dist/security-runtime/src/core/runner.mjs +86 -0
  707. package/dist/security-runtime/src/core/sarif.mjs +141 -0
  708. package/dist/security-runtime/src/lattice/artifact.mjs +1201 -0
  709. package/dist/security-runtime/src/lattice/execution.mjs +45 -0
  710. package/dist/security-runtime/src/lattice/export.mjs +215 -0
  711. package/dist/security-runtime/src/lattice/provenance.mjs +70 -0
  712. package/dist/security-runtime/src/lattice/runtime.mjs +158 -0
  713. package/dist/security-runtime/src/lattice/selected-runtime.mjs +189 -0
  714. package/dist/security-runtime/src/lattice/source-acquirer.mjs +51 -0
  715. package/dist/security-runtime/src/lattice/source-fetcher.mjs +88 -0
  716. package/dist/security-runtime/src/lattice/transport.mjs +232 -0
  717. package/dist/security-runtime/src/live/analyze.mjs +101 -0
  718. package/dist/security-runtime/src/live/exposure.mjs +79 -0
  719. package/dist/security-runtime/src/live/secrets.mjs +31 -0
  720. package/dist/security-runtime/src/live/session.mjs +98 -0
  721. package/dist/security-runtime/src/live/targets.mjs +81 -0
  722. package/dist/security-runtime/src/portfolio/index.mjs +116 -0
  723. package/dist/security-runtime/tools/registry/betterleaks.json +29 -0
  724. package/dist/security-runtime/tools/registry/guarddog.json +9 -0
  725. package/dist/security-runtime/tools/registry/mobsf.json +10 -0
  726. package/dist/security-runtime/tools/registry/opengrep.json +28 -0
  727. package/dist/security-runtime/tools/registry/osv-scanner.json +14 -0
  728. package/dist/security-runtime/tools/registry/semgrep-rules.json +21 -0
  729. package/dist/security-runtime/tools/registry/trivy.json +30 -0
  730. package/dist/security-runtime/tools/registry/yara-forge-exclusions.json +14 -0
  731. package/dist/security-runtime/tools/registry/yara-forge.json +22 -0
  732. package/dist/security-runtime/tools/registry/yara-runner.json +10 -0
  733. package/dist/security-runtime/tools/registry/yara-x.json +24 -0
  734. package/dist/security-runtime/tools/registry/zizmor.json +30 -0
  735. package/dist/spinner.d.ts +16 -0
  736. package/dist/spinner.d.ts.map +1 -0
  737. package/dist/spinner.js +72 -0
  738. package/dist/spinner.js.map +1 -0
  739. package/dist/verify.test.d.ts +2 -0
  740. package/dist/verify.test.d.ts.map +1 -0
  741. package/dist/verify.test.js +98 -0
  742. package/dist/verify.test.js.map +1 -0
  743. package/package.json +73 -4
@@ -0,0 +1,659 @@
1
+ // Auth-helper discovery for the code check. The three *-write-no-auth rules accept a fixed set of auth function names; real products wrap
2
+ // the session check in their own helper (requireApiContext -> getCurrentUserContext -> supabase.auth.getUser()). This module finds those wrappers by
3
+ // reading the scanned repository's source as text (a regex scanner, never executing it). Findings are filtered only for resolved local/imported bindings.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { listFiles } from '../../core/detect.mjs';
7
+
8
+ export const MAX_INDEX_FILES = 3000;
9
+ export const MAX_INDEX_FILE_BYTES = 300_000;
10
+ /** Hops from a function to a primitive: 1 = it calls the primitive itself, 2 = it calls a function that does (requireApiContext -> getCurrentUserContext -> getUser). */
11
+ export const MAX_DEPTH = 2;
12
+ /** The sentinel in the rule regexes that explicitly configured names replace at scan time. */
13
+ export const SENTINEL = '__vsg_discovered__';
14
+
15
+ const SOURCE = /\.[cm]?[jt]sx?$/;
16
+ const TEST_FILE = /(?:^|\/)[^/]+\.(?:test|spec)\.[^/]+$|\.d\.ts$/;
17
+ // Names that must never become "auth helpers" however they are written: entry points and framework hooks, not checks.
18
+ const DENY = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'default', 'handler', 'middleware', 'proxy', 'main', 'if', 'for', 'while', 'switch', 'catch', 'return', 'function', 'typeof', 'await', 'async']);
19
+ const IDENT = /^[A-Za-z_$][\w$]*$/;
20
+
21
+ /** What makes a function a session or credential check. Each primitive is matched on source text. */
22
+ const PRIMITIVES = [
23
+ /\bauth\s*\.\s*(?:getUser|getClaims|getSession)\s*\(/, // supabase.auth.getUser() and friends
24
+ /(?<![\w$.])(?:auth|currentUser|getServerSession|jwtVerify|getClaims|getSession)\s*\(/, // Clerk auth(), currentUser(), NextAuth getServerSession(), jose jwtVerify
25
+ ];
26
+ /**
27
+ * Names that preserve a cost-zero imported guard hop after the resolved definition is proven to reach an auth primitive. A name alone cannot
28
+ * create an automatic helper. The write-no-auth rules also accept these conventional guard names directly, plus looser names such as get*User*.
29
+ * This graph heuristic is deliberately stricter than the rule regex: get*User* is too common a name (getUserPreferences) to propagate to callers.
30
+ * test/auth-helpers.test.mjs checks that the shipped rules accept every name this accepts.
31
+ */
32
+ export const AUTH_CALL_NAME = /^(?:auth|getAuth|getSession|getClaims|getServerSession|currentUser|getCurrentUser\w*|(?:require|assert|ensure)\w*(?:User|Auth|Session|Context|Admin|Member|Role|Permission|Owner|Access)\w*|validateRequest|jwtVerify)$/;
33
+
34
+ // SQL functions that read the caller from the database session. A call to one of them through a user-scoped Supabase client (supabase.rpc("name"))
35
+ // checks the caller in the database even though no auth.getUser() appears in the TypeScript.
36
+ const SQL_FUNCTION = /create\s+(?:or\s+replace\s+)?function\s+(?:"?\w+"?\s*\.\s*)?"?(\w+)"?\s*\(/gi;
37
+ const SQL_CALLER = /\bauth\s*\.\s*(?:uid|jwt)\s*\(|current_setting\s*\(\s*'request\.jwt\.claims?(?:\.sub)?'/i;
38
+ const SQL_CALL = /\b([a-z_][a-z0-9_]*)\s*\(/gi;
39
+
40
+ /** Strips SQL comments (-- and block comments); string literals are kept. */
41
+ const stripSqlComments = (sql) => sql.replace(/\/\*[\s\S]*?\*\//g, ' ').replace(/--[^\n]*/g, '');
42
+
43
+ /**
44
+ * Names of SQL functions, defined in the given migration texts, whose body reads the caller (auth.uid(), auth.jwt()) or calls another such function.
45
+ * Pass the texts in migration order: the newest definition of a name wins. Text only, nothing is executed. Returns a sorted array of lower-case names.
46
+ */
47
+ export function sqlAuthFunctions(sqlTexts) {
48
+ const defs = new Map(); // name -> { direct, calls }
49
+ for (const raw of sqlTexts) {
50
+ const sql = stripSqlComments(raw);
51
+ const starts = [...sql.matchAll(SQL_FUNCTION)];
52
+ starts.forEach((m, i) => {
53
+ const slice = sql.slice(m.index + m[0].length, starts[i + 1]?.index ?? sql.length);
54
+ const quote = /\$(\w*)\$/.exec(slice);
55
+ let body = slice;
56
+ if (quote) {
57
+ const open = quote.index + quote[0].length;
58
+ const close = slice.indexOf(quote[0], open);
59
+ body = slice.slice(open, close < 0 ? slice.length : close);
60
+ }
61
+ defs.set(m[1].toLowerCase(), { direct: SQL_CALLER.test(body), calls: new Set([...body.matchAll(SQL_CALL)].map((c) => c[1].toLowerCase())) });
62
+ });
63
+ }
64
+ const auth = new Set([...defs].filter(([, d]) => d.direct).map(([n]) => n));
65
+ for (let pass = 0; pass < 3; pass++) {
66
+ let changed = false;
67
+ for (const [name, d] of defs) if (!auth.has(name) && [...d.calls].some((c) => c !== name && auth.has(c))) { auth.add(name); changed = true; }
68
+ if (!changed) break;
69
+ }
70
+ return [...auth].sort();
71
+ }
72
+
73
+ /** Reads the migration SQL (any .sql file under a migrations/ folder, in file-name order) and returns sqlAuthFunctions of it. */
74
+ export function discoverAuthRpcs(repo, files = listFiles(repo)) {
75
+ const sql = files.filter((f) => /\.sql$/i.test(f) && /(?:^|\/)migrations\//i.test(f)).sort();
76
+ const texts = [];
77
+ for (const f of sql.slice(0, 2000)) {
78
+ try {
79
+ const full = path.join(repo, f);
80
+ if (fs.statSync(full).size > 2_000_000) continue;
81
+ texts.push(fs.readFileSync(full, 'utf8'));
82
+ } catch { /* unreadable: skipped */ }
83
+ }
84
+ return sqlAuthFunctions(texts);
85
+ }
86
+
87
+ /**
88
+ * A primitive for `<client>.rpc("name")` over the given SQL function names, or null when there are none. A service-role or admin client carries
89
+ * no caller (auth.uid() is null for it), so a receiver whose name contains "service" or "admin" never counts.
90
+ */
91
+ export function rpcPrimitive(rpcNames) {
92
+ const names = [...new Set(rpcNames)].filter((n) => /^\w+$/.test(n));
93
+ if (!names.length) return null;
94
+ return new RegExp(`(?<![\\w$.])(?![\\w$.]*(?:service|admin))[\\w$.]+?\\s*\\.\\s*rpc\\s*\\(\\s*["'\`](?:${names.join('|')})["'\`]`, 'i');
95
+ }
96
+
97
+ // Shared-secret checks (timingSafeEqual, Authorization compared with "Bearer <secret>") are recognized in the rules themselves, not discovered here:
98
+ // a function that merely uses timingSafeEqual (a captcha, a webhook of someone else's) is not evidence that it checks the caller.
99
+
100
+ /** Removes comments and keeps strings, so commented-out calls do not count. Not a full tokenizer: regex literals are not recognised. */
101
+ export function stripComments(text) {
102
+ let out = '';
103
+ let i = 0;
104
+ const n = text.length;
105
+ while (i < n) {
106
+ const c = text[i];
107
+ const d = text[i + 1];
108
+ if (c === '/' && d === '/') { while (i < n && text[i] !== '\n') i++; continue; }
109
+ if (c === '/' && d === '*') { const end = text.indexOf('*/', i + 2); i = end < 0 ? n : end + 2; out += ' '; continue; }
110
+ if (c === '"' || c === "'" || c === '`') {
111
+ let j = i + 1;
112
+ while (j < n && text[j] !== c && !(c !== '`' && text[j] === '\n')) j += text[j] === '\\' ? 2 : 1;
113
+ out += text.slice(i, j + 1);
114
+ i = j + 1;
115
+ continue;
116
+ }
117
+ out += c;
118
+ i++;
119
+ }
120
+ return out;
121
+ }
122
+
123
+ /**
124
+ * Replaces JavaScript string and template literals with whitespace while preserving line breaks.
125
+ * Auth discovery is deliberately lexical, but a literal containing `supabase.auth.getUser()` is
126
+ * data, not an authentication check. Keeping the quotes out of the result also prevents the call
127
+ * matcher from treating words in a literal as function calls.
128
+ */
129
+ export function stripLiterals(text) {
130
+ let out = '';
131
+ let i = 0;
132
+ while (i < text.length) {
133
+ const c = text[i];
134
+ if (c !== '"' && c !== "'" && c !== '`') {
135
+ out += c;
136
+ i++;
137
+ continue;
138
+ }
139
+ const quote = c;
140
+ out += ' ';
141
+ i++;
142
+ while (i < text.length) {
143
+ const ch = text[i];
144
+ if (ch === '\n' || ch === '\r') {
145
+ out += ch;
146
+ i++;
147
+ continue;
148
+ }
149
+ out += ' ';
150
+ if (ch === '\\') {
151
+ i++;
152
+ if (i < text.length) {
153
+ out += text[i] === '\n' || text[i] === '\r' ? text[i] : ' ';
154
+ i++;
155
+ }
156
+ continue;
157
+ }
158
+ i++;
159
+ if (ch === quote) break;
160
+ }
161
+ }
162
+ return out;
163
+ }
164
+
165
+ /** Whether an offset points at executable source rather than a comment or quoted literal. */
166
+ function isCodePosition(text, offset) {
167
+ let quote = null;
168
+ for (let i = 0; i < offset; i++) {
169
+ const c = text[i];
170
+ const d = text[i + 1];
171
+ if (quote) {
172
+ if (c === '\\') { i++; continue; }
173
+ if (c === quote) quote = null;
174
+ continue;
175
+ }
176
+ if (c === '/' && d === '/') {
177
+ const end = text.indexOf('\n', i + 2);
178
+ if (end < 0 || end >= offset) return false;
179
+ i = end;
180
+ continue;
181
+ }
182
+ if (c === '/' && d === '*') {
183
+ const end = text.indexOf('*/', i + 2);
184
+ if (end < 0 || end >= offset) return false;
185
+ i = end + 1;
186
+ continue;
187
+ }
188
+ if (c === '"' || c === "'" || c === '`') quote = c;
189
+ }
190
+ return !quote;
191
+ }
192
+
193
+ const executableMatch = (text, re) => {
194
+ const flags = re.flags.includes('g') ? re.flags : `${re.flags}g`;
195
+ const copy = new RegExp(re.source, flags);
196
+ for (const match of text.matchAll(copy)) if (isCodePosition(text, match.index)) return true;
197
+ return false;
198
+ };
199
+
200
+ function topLevelLines(body) {
201
+ const clean = stripLiterals(stripComments(body));
202
+ // Supported bodies start on their own next line. Same-line statements can hide a
203
+ // single-statement conditional around the first apparent top-level call.
204
+ if (!/\{\s*$/.test(clean.split(/\r?\n/, 1)[0])) return [];
205
+ let depth = 0;
206
+ return clean.split(/\r?\n/).map((code) => {
207
+ const atTop = depth === 1;
208
+ depth += (code.match(/{/g) ?? []).length - (code.match(/}/g) ?? []).length;
209
+ return { line: code, code, atTop };
210
+ });
211
+ }
212
+
213
+ function primitiveResultBindings(body) {
214
+ const bindings = [];
215
+ for (const [index, { line, atTop }] of topLevelLines(body).entries()) {
216
+ if (index === 0 || !atTop) continue;
217
+ const assignment = /^\s*const\s+(.+?)\s*=\s*await\s+(.+?)\s*;?\s*$/.exec(line);
218
+ if (!assignment) continue;
219
+ const [, lhs, rhs] = assignment;
220
+ const supabaseMethod = /^(?:[A-Za-z_$][\w$]*\.)+auth\s*\.\s*(?:getUser|getClaims|getSession)\s*\(\s*\)$/.test(rhs);
221
+ if (supabaseMethod) {
222
+ const data = /^\s*\{[^{}]*\bdata\s*:\s*\{([^{}]*)\}[^{}]*\}\s*$/.exec(lhs)?.[1];
223
+ if (!data) continue;
224
+ const names = new Set();
225
+ for (const part of data.split(',')) {
226
+ const property = /^\s*(user|session|claims)(?:\s*:\s*([A-Za-z_$][\w$]*))?\s*$/.exec(part);
227
+ if (property) names.add(property[2] ?? property[1]);
228
+ }
229
+ if (names.size) bindings.push({ index, names });
230
+ continue;
231
+ }
232
+ const directMethod = /^(?:currentUser|getServerSession)\s*\(\s*\)$/.exec(rhs)?.[0];
233
+ if (!directMethod) continue;
234
+ const name = /^\s*([A-Za-z_$][\w$]*)\s*$/.exec(lhs)?.[1];
235
+ if (name && IDENT.test(name)) bindings.push({ index, names: new Set([name]) });
236
+ }
237
+ return bindings;
238
+ }
239
+
240
+ function primitiveResultNames(body) {
241
+ return new Set(primitiveResultBindings(body).flatMap((binding) => [...binding.names]));
242
+ }
243
+
244
+ const CONTROL = /\b(?:if|else|switch|for|while|do|try|catch|return|throw|function)\b|=>/;
245
+ const escapeName = (name) => name.replace(/\$/g, '\\$');
246
+
247
+ // Only simple awaited assignments carry another helper's proven return semantics.
248
+ function helperResultBindings(body, callee) {
249
+ const re = new RegExp(`^\\s*const\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*await\\s+${escapeName(callee)}\\s*\\([^()]*\\)\\s*;?\\s*$`);
250
+ return topLevelLines(body).flatMap(({ code, atTop }, index) => {
251
+ const match = index > 0 && atTop && re.exec(code);
252
+ return match ? [{ index, names: new Set([match[1]]) }] : [];
253
+ });
254
+ }
255
+
256
+ function resultAliases(body, callee) {
257
+ return helperResultBindings(body, callee).flatMap(({ names }) => [...names]);
258
+ }
259
+
260
+ function authBindings(body, authValues) {
261
+ const primitive = primitiveResultBindings(body);
262
+ const scalar = topLevelLines(body).flatMap(({ code, atTop }, index) => {
263
+ const match = index > 0 && atTop && /^\s*const\s+([A-Za-z_$][\w$]*)\s*=\s*await\s+[A-Za-z_$][\w$]*\s*\([^()]*\)\s*;?\s*$/.exec(code);
264
+ return match && authValues.has(match[1]) ? [{ index, names: new Set([match[1]]) }] : [];
265
+ });
266
+ return [...primitive, ...scalar].filter(({ names }) => [...names].some((name) => authValues.has(name)));
267
+ }
268
+
269
+ function straightPrefix(lines, end) {
270
+ return !CONTROL.test(lines.slice(1, end).map(({ code }) => code).join('\n'));
271
+ }
272
+
273
+ function directReturnCall(body, callee) {
274
+ const lines = topLevelLines(body);
275
+ const returns = lines.filter(({ code }) => /\breturn\b/.test(code));
276
+ const re = new RegExp(`^\\s*return\\s+(?:await\\s+)?${escapeName(callee)}\\s*\\([^()]*\\)\\s*;?\\s*$`);
277
+ return returns.length === 1 && returns[0].atTop && re.test(returns[0].code) && straightPrefix(lines, lines.indexOf(returns[0]));
278
+ }
279
+
280
+ function directReturnName(body, name) {
281
+ const lines = topLevelLines(body);
282
+ const escaped = escapeName(name);
283
+ const binding = authBindings(body, new Set([name])).find(({ names }) => names.has(name));
284
+ if (!binding || !straightPrefix(lines, binding.index)) return false;
285
+ const tail = lines.slice(binding.index + 1).filter(({ code }) => code.trim() && !/^\s*}\)?\s*;?\s*$/.test(code));
286
+ // A nullable identity may be returned directly, or mapped to context only after a null exit.
287
+ if (tail.length === 1) return tail[0].atTop && (
288
+ new RegExp(`^\\s*return\\s+${escaped}\\s*;?\\s*$`).test(tail[0].code)
289
+ // A single identity projection preserves null on the missing-identity branch.
290
+ || new RegExp(`^\\s*return\\s+${escaped}\\s*\\?\\s*\\{\\s*[A-Za-z_$][\\w$]*\\s*:\\s*${escaped}\\.[A-Za-z_$][\\w$]*\\s*\\}\\s*:\\s*null\\s*;?\\s*$`).test(tail[0].code)
291
+ );
292
+ return tail.length === 2 && tail.every(({ atTop }) => atTop)
293
+ && new RegExp(`^\\s*if\\s*\\(\\s*!\\s*${escaped}\\s*\\)\\s*return\\s+null\\s*;?\\s*$`).test(tail[0].code)
294
+ && /^\s*return\s*\{[^;]*\}\s*;?\s*$/.test(tail[1].code);
295
+ }
296
+
297
+ // Read only plain top-level object properties; nested keys never describe the returned sentinel.
298
+ function denialProperties(code) {
299
+ const object = /\breturn\s*\{([^;]*)\}\s*;?\s*$/.exec(code)?.[1];
300
+ if (!object || /\.\.\.|\b(?:get|set|return|function)\b|=>/.test(object)) return new Map();
301
+ const parts = [];
302
+ let depth = 0;
303
+ let start = 0;
304
+ for (let i = 0; object.length > i; i++) {
305
+ if ('({['.includes(object[i])) depth++;
306
+ if (')}]'.includes(object[i])) depth--;
307
+ if (depth < 0) return new Map();
308
+ if (object[i] === ',' && depth === 0) { parts.push(object.slice(start, i)); start = i + 1; }
309
+ }
310
+ if (depth !== 0) return new Map();
311
+ parts.push(object.slice(start));
312
+ const properties = new Map();
313
+ for (const part of parts) {
314
+ const property = /^\s*([A-Za-z_$][\w$]*)\s*:\s*(.+?)\s*$/.exec(part);
315
+ if (!property || properties.has(property[1])) return new Map();
316
+ properties.set(property[1], property[2]);
317
+ }
318
+ return properties;
319
+ }
320
+
321
+ function denialResultKeys(body, authValues) {
322
+ const keys = new Set();
323
+ const lines = topLevelLines(body);
324
+ const allCode = lines.map(({ code }) => code).join('\n');
325
+ const returns = lines.map(({ code, atTop }, index) => ({ code, atTop, index })).filter(({ code, atTop }) => atTop && /\breturn\b/.test(code));
326
+ if (returns.length !== 2) return keys;
327
+ if (/\b(?:function|try|catch|switch|for|while)\b|=>/.test(lines.slice(1).map(({ code }) => code).join('\n'))) return keys;
328
+ if ((allCode.match(/\bif\b/g) ?? []).length !== 1) return keys;
329
+ const first = returns[0];
330
+ const second = returns[1];
331
+ if (!/^\s*return\s*\{[^;]*\}\s*;?\s*$/.test(second.code)) return keys;
332
+ const properties = denialProperties(first.code);
333
+ for (const value of authValues) {
334
+ const escaped = value.replace(/\$/g, '\\$');
335
+ if (!new RegExp(`^\\s*if\\s*\\(\\s*!\\s*${escaped}\\s*\\)\\s*return\\s*\\{[^;]*\\}\\s*;?\\s*$`).test(first.code)) continue;
336
+ const binding = authBindings(body, authValues).find((entry) => entry.names.has(value))?.index ?? -1;
337
+ if (binding < 0 || binding >= first.index || lines.slice(binding + 1, first.index).some(({ code }) => new RegExp(`\\b${escaped}\\s*(?:=|\\+\\+|--|\\|=|&&=|\\?\\?=)`).test(code))) continue;
338
+ const prior = lines.slice(1, first.index).map(({ code }) => code).join('\n');
339
+ if (/\b(?:if|else|switch|for|while|try|catch|return|throw)\b|=>/.test(prior)) continue;
340
+ if (lines.slice(first.index + 1, second.index).some(({ code }) => /\b(?:if|else|switch|for|while|try|catch|throw)\b|=>/.test(code))) continue;
341
+ for (const [key, denial] of [
342
+ ['response', /^new\s+Response\s*\([^()]*\)$/],
343
+ ['error', /^new\s+Error\s*\([^()]*\)$/],
344
+ ['denied', /^true$/],
345
+ ['unauthorized', /^true$/],
346
+ ['allowed', /^false$/],
347
+ ]) if (denial.test(properties.get(key) ?? '')) keys.add(key);
348
+ }
349
+ return keys;
350
+ }
351
+ /** True only for a top-level, straight-line await/return of a fail-closed helper. */
352
+ export function callsUnconditionally(body, callee) {
353
+ const lines = topLevelLines(body);
354
+ const escaped = callee.replace(/\$/g, '\\$');
355
+ const callLine = new RegExp(`^\\s*(?:(?:(?:await|return\\s+await)\\s+|return\\s+)|(?:const|let|var)\\s+(?:\\{[^}]+\\}|[A-Za-z_$][\\w$]*)(?:\\s*:[^=]+)?\\s*=\\s*await\\s+)${escaped}\\s*\\(\\s*\\)\\s*;?\\s*$`);
356
+ if (/=>|\bcatch\b/.test(stripLiterals(stripComments(body)))) return false;
357
+ for (let i = 1; i < lines.length; i++) {
358
+ const { code, atTop } = lines[i];
359
+ if (!new RegExp(`(?<![\\w$.])${escaped}\\s*\\(`).test(code)) continue;
360
+ if (!atTop || !callLine.test(code)) return false;
361
+ const prior = lines.slice(1, i).map((entry) => entry.code).join('\n');
362
+ return !CONTROL.test(prior);
363
+ }
364
+ return false;
365
+ }
366
+ // A primitive call is evidence that a function reads caller identity, not that it enforces it.
367
+ // Only an explicit denial throw tied to a result assigned from that primitive is a guard.
368
+ function throwsOnMissingAuth(body, authValues, denialCalls = new Set()) {
369
+ const lines = topLevelLines(body);
370
+ const code = lines.slice(1).map((entry) => entry.code).join('\n');
371
+ if (!authValues.size || /\bcatch\b|=>/.test(code)) return false;
372
+ for (const binding of authBindings(body, authValues)) {
373
+ if (!straightPrefix(lines, binding.index)) continue;
374
+ let next = binding.index + 1;
375
+ while (next < lines.length && !lines[next].code.trim()) next++;
376
+ if (next >= lines.length || !lines[next].atTop) continue;
377
+ for (const value of binding.names) {
378
+ const escaped = value.replace(/\$/g, '\\$');
379
+ const guard = new RegExp(`^\\s*if\\s*\\(\\s*!\\s*${escaped}\\s*\\)\\s*(?:\\{\\s*)?throw\\b`);
380
+ if (guard.test(lines[next].code)) return true;
381
+ for (const denial of denialCalls) {
382
+ // Framework termination is trusted only through an unshadowed import, with a
383
+ // complete denial statement (no conditional call or compound fallback).
384
+ const call = `${escapeName(denial)}\\s*\\([^()]*\\)\\s*;?`;
385
+ const redirect = new RegExp(`^\\s*if\\s*\\(\\s*!\\s*${escaped}\\s*\\)\\s*(?:${call}|\\{\\s*${call}\\s*\\})\\s*$`);
386
+ if (redirect.test(lines[next].code)) return true;
387
+ }
388
+ }
389
+ }
390
+ return false;
391
+ }
392
+
393
+ const FN_START = /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\b\s*\*?\s*([A-Za-z_$][\w$]*)/;
394
+ const CONST_START = /^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::[^=]+)?=/;
395
+ const FUNCTION_VALUE = /^\s*(?:[\w$.]+\(\s*)?(?:async\b|function\b|\(|[\w$]+\s*=>)/;
396
+ const TOP_LEVEL = /^(?:export|function|async|const|let|var|class|type|interface|enum|import|declare|module)\b/;
397
+ const CALL = /([A-Za-z_$][\w$]*)\s*\(/g;
398
+ // `const requireX = cache(requireFreshX);`: one wrapper call around one identifier.
399
+ const ALIAS_VALUE = /^\s*[\w$.]+\(\s*([A-Za-z_$][\w$]*)\s*\)\s*;?\s*$/;
400
+
401
+ /**
402
+ * Splits a module into top-level declarations by column-0 keywords (formatted code; no parsing) and records, per name, whether it is exported,
403
+ * which names its body calls, and whether the body itself contains an auth primitive. Overload signatures merge into the implementation.
404
+ * `const x = cache(y)` (one wrapper call around one named function) is an alias: x calls y. extraPrimitives are further regexes that count as a
405
+ * primitive (see rpcPrimitive).
406
+ */
407
+ export function parseDeclarations(text, { extraPrimitives = [] } = {}) {
408
+ const lines = stripComments(text).split(/\r?\n/);
409
+ const imports = parseImports(text);
410
+ const declared = new Set(lines.flatMap((line) => (FN_START.exec(line) ?? CONST_START.exec(line))?.[1] ?? []));
411
+ const frameworkNames = (source, imported) => new Set([...imports].filter(([name, binding]) => binding.source === source && imported.includes(binding.imported) && !declared.has(name)).map(([name]) => name));
412
+ const redirects = frameworkNames('next/navigation', ['redirect']);
413
+ const caches = frameworkNames('react', ['cache']);
414
+ const decls = new Map();
415
+ const exportedNames = new Set();
416
+ let cur = null;
417
+ const flush = () => {
418
+ if (!cur) return;
419
+ const body = cur.lines.join('\n');
420
+ const d = decls.get(cur.name) ?? { name: cur.name, exported: false, exportedNames: new Set(), calls: new Set(), primitive: false, failsClosed: false, authValues: new Set(), returnsAuthValue: false, denialKeys: new Set() };
421
+ d.body = body;
422
+ d.exported ||= cur.exported;
423
+ if (cur.exported) {
424
+ d.exportedNames.add(cur.name);
425
+ if (/^export\s+default\b/.test(cur.lines[0])) d.exportedNames.add('default');
426
+ }
427
+ const code = stripLiterals(body);
428
+ d.denialCalls = new Set([...redirects].filter((name) => {
429
+ const escaped = escapeName(name);
430
+ // The sole reference must be the denial statement. Extra references include
431
+ // parameters and local bindings, including multiline destructuring shadows.
432
+ return [...code.matchAll(new RegExp(`(?<![\\w$])${escaped}(?![\\w$])`, 'g'))].length === 1;
433
+ }));
434
+ d.transparentAlias = cur.alias && cur.wrapper && caches.has(cur.wrapper)
435
+ && /^\s*(?:export\s+)?const\s+/.test(body)
436
+ && new RegExp(`^\\s*(?:export\\s+)?const\\s+${escapeName(cur.name)}\\s*=\\s*${escapeName(cur.wrapper)}\\s*\\(\\s*${escapeName(cur.alias)}\\s*\\)\\s*;?\\s*$`).test(code)
437
+ ? cur.alias : null;
438
+ for (const m of code.matchAll(CALL)) if (!DENY.has(m[1]) && m[1] !== cur.name) d.calls.add(m[1]);
439
+ if (cur.alias && !DENY.has(cur.alias) && cur.alias !== cur.name) d.calls.add(cur.alias);
440
+ // RPC primitives intentionally include a literal function name, so evaluate those against the
441
+ // original body while the built-in auth primitives use the literal-free code.
442
+ // A name that merely resembles a guard (for example requireAuth()) is not evidence by itself.
443
+ // The definition must reach a primitive, either locally or through the discovered call graph.
444
+ // The declaration itself is part of `body` for one-line functions. Mask its name before
445
+ // primitive matching so `function auth() { return true; }` does not authenticate itself.
446
+ const declarationCall = new RegExp(`(?<![\\w$.])${cur.name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\s*\\(`);
447
+ const primitiveCode = code.replace(declarationCall, `${cur.name} `);
448
+ const reachesPrimitive = PRIMITIVES.some((re) => re.test(primitiveCode)) || extraPrimitives.some((re) => executableMatch(body, re));
449
+ d.primitive ||= reachesPrimitive;
450
+ if (reachesPrimitive) {
451
+ for (const name of primitiveResultNames(body)) d.authValues.add(name);
452
+ d.failsClosed ||= throwsOnMissingAuth(body, d.authValues, d.denialCalls);
453
+ }
454
+ d.returnsAuthValue ||= [...d.authValues].some((name) => directReturnName(body, name));
455
+ for (const key of denialResultKeys(body, d.authValues)) d.denialKeys.add(key);
456
+ decls.set(cur.name, d);
457
+ cur = null;
458
+ };
459
+ for (const line of lines) {
460
+ const named = FN_START.exec(line) ?? CONST_START.exec(line);
461
+ if (named) {
462
+ flush();
463
+ // A const counts as a function only when its value looks like one (arrow, function, or a wrapper call such as cache(async ...)); objects and values never become helpers.
464
+ const rest = line.slice(named[0].length);
465
+ const alias = FN_START.test(line) ? undefined : ALIAS_VALUE.exec(rest)?.[1];
466
+ const fn = FN_START.test(line) || FUNCTION_VALUE.test(rest) || Boolean(alias);
467
+ const wrapper = alias ? /^\s*([A-Za-z_$][\w$]*)\s*\(/.exec(rest)?.[1] : null;
468
+ cur = { name: named[1], exported: /^export\b/.test(line) && fn, lines: [line], alias, wrapper };
469
+ continue;
470
+ }
471
+ const list = /^export\s*\{([^}]*)\}/.exec(line);
472
+ if (list) for (const part of list[1].split(',')) {
473
+ const [original, alias] = part.trim().split(/\s+as\s+/);
474
+ if (original) exportedNames.add(`${original}\t${alias ?? original}`);
475
+ }
476
+ if (TOP_LEVEL.test(line)) { flush(); continue; }
477
+ if (cur) cur.lines.push(line);
478
+ }
479
+ flush();
480
+ for (const entry of exportedNames) {
481
+ const [name, alias] = entry.split('\t');
482
+ if (!decls.has(name)) continue;
483
+ decls.get(name).exported = true;
484
+ decls.get(name).exportedNames.add(alias);
485
+ }
486
+ for (let pass = 0; pass < decls.size + 1; pass++) {
487
+ let changed = false;
488
+ for (const d of decls.values()) {
489
+ for (const callee of d.calls) {
490
+ const target = decls.get(callee);
491
+ if (!target) continue;
492
+ if (target.failsClosed && d.transparentAlias === callee && !d.failsClosed) { d.failsClosed = true; changed = true; }
493
+ if (target.returnsAuthValue) for (const alias of resultAliases(d.body, callee)) {
494
+ if (!d.authValues.has(alias)) { d.authValues.add(alias); changed = true; }
495
+ }
496
+ if (directReturnCall(d.body, callee)) {
497
+ if (target.returnsAuthValue && !d.returnsAuthValue) { d.returnsAuthValue = true; changed = true; }
498
+ for (const key of target.denialKeys) if (!d.denialKeys.has(key)) { d.denialKeys.add(key); changed = true; }
499
+ }
500
+ }
501
+ if (throwsOnMissingAuth(d.body, d.authValues, d.denialCalls) && !d.failsClosed) { d.failsClosed = true; changed = true; }
502
+ if ([...d.authValues].some((name) => directReturnName(d.body, name)) && !d.returnsAuthValue) { d.returnsAuthValue = true; changed = true; }
503
+ for (const key of denialResultKeys(d.body, d.authValues)) if (!d.denialKeys.has(key)) { d.denialKeys.add(key); changed = true; }
504
+ }
505
+ if (!changed) break;
506
+ }
507
+ return decls;
508
+ }
509
+
510
+ /** Returns local bindings for named, default, and namespace imports in a module. */
511
+ export function parseImports(text) {
512
+ const imports = new Map();
513
+ const re = /^\s*import\s+(.+?)\s+from\s+["']([^"']+)["']\s*;?\s*$/gm;
514
+ for (const match of text.matchAll(re)) {
515
+ const clause = match[1].trim();
516
+ const source = match[2];
517
+ if (!isCodePosition(text, match.index)) continue;
518
+ const add = (local, imported) => { if (IDENT.test(local)) imports.set(local, { local, imported, source }); };
519
+ const named = /\{([^}]*)\}/.exec(clause)?.[1];
520
+ if (named !== undefined) for (const part of named.split(',')) {
521
+ const [imported, local] = part.trim().split(/\s+as\s+/);
522
+ if (imported) add(local ?? imported, imported);
523
+ }
524
+ const namespace = /\*\s+as\s+([A-Za-z_$][\w$]*)/.exec(clause)?.[1];
525
+ if (namespace) add(namespace, '*');
526
+ const defaultName = clause.match(/^(?!\{|\*)[A-Za-z_$][\w$]*/)?.[0];
527
+ if (defaultName) add(defaultName, 'default');
528
+ }
529
+ return imports;
530
+ }
531
+
532
+ export const resolveModule = (repo, fromFile, source) => {
533
+ const repoRoot = fs.realpathSync(repo);
534
+ const roots = [];
535
+ if (source.startsWith('./') || source.startsWith('../')) roots.push(path.resolve(repo, path.dirname(fromFile), source));
536
+ else if (source.startsWith('@/') || source.startsWith('~/')) {
537
+ const relative = source.slice(2);
538
+ roots.push(path.resolve(repo, 'src', relative), path.resolve(repo, relative));
539
+ } else return null;
540
+ for (const root of roots) {
541
+ for (const suffix of ['', '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']) {
542
+ const candidate = `${root}${suffix}`;
543
+ try {
544
+ if (!fs.statSync(candidate).isFile()) continue;
545
+ const real = fs.realpathSync(candidate);
546
+ if (real !== repoRoot && !real.startsWith(`${repoRoot}${path.sep}`)) continue;
547
+ return path.relative(repoRoot, real).replaceAll('\\', '/');
548
+ } catch { /* try the next form */ }
549
+ }
550
+ for (const suffix of ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']) {
551
+ const candidate = path.join(root, `index${suffix}`);
552
+ try {
553
+ if (!fs.statSync(candidate).isFile()) continue;
554
+ const real = fs.realpathSync(candidate);
555
+ if (real !== repoRoot && !real.startsWith(`${repoRoot}${path.sep}`)) continue;
556
+ return path.relative(repoRoot, real).replaceAll('\\', '/');
557
+ } catch { /* try the next form */ }
558
+ }
559
+ }
560
+ return null;
561
+ };
562
+
563
+ /**
564
+ * Finds exported functions in the repository whose bodies reach an auth primitive within MAX_DEPTH calls.
565
+ * A call to a non-exported function in the same file does not count as a hop (cache(async function getXCached ...) wrappers).
566
+ * An imported binding whose resolved definition reaches a recognized caller-check name (AUTH_CALL_NAME) may preserve a cost-zero guard hop;
567
+ * the name alone never creates a helper. A Supabase RPC whose migration SQL reads auth.uid() counts as a primitive.
568
+ * Returns { helpers: [{ name, file, depth }], indexedFiles, candidateFiles, capped, authRpcs }.
569
+ */
570
+ export function discoverAuthHelpers(repo, { excludes = [], maxFiles = MAX_INDEX_FILES, maxBytes = MAX_INDEX_FILE_BYTES, maxDepth = MAX_DEPTH } = {}) {
571
+ const skip = new Set(excludes);
572
+ const candidates = listFiles(repo).filter((f) => SOURCE.test(f) && !TEST_FILE.test(f) && !f.split('/').slice(0, -1).some((seg) => skip.has(seg)));
573
+ const files = candidates.slice(0, maxFiles);
574
+ const authRpcs = discoverAuthRpcs(repo);
575
+ const rpcRe = rpcPrimitive(authRpcs);
576
+ const nodes = []; // { file, name, exported, calls, dist }
577
+ const byFile = new Map();
578
+ const importsByFile = new Map();
579
+ let indexed = 0;
580
+ for (const file of files) {
581
+ let text;
582
+ try {
583
+ const full = path.join(repo, file);
584
+ if (fs.statSync(full).size > maxBytes) continue;
585
+ text = fs.readFileSync(full, 'utf8');
586
+ } catch { continue; }
587
+ indexed++;
588
+ importsByFile.set(file, parseImports(text));
589
+ for (const d of parseDeclarations(text, { extraPrimitives: rpcRe ? [rpcRe] : [] }).values()) {
590
+ const node = { file, name: d.name, exports: [...(d.exportedNames ?? [])], exported: d.exported, calls: d.calls, dist: d.primitive ? 1 : Infinity, failsClosed: d.failsClosed, returnsAuthValue: d.returnsAuthValue, denialKeys: new Set(d.denialKeys), authValues: new Set(d.authValues), body: d.body, denialCalls: d.denialCalls, transparentAlias: d.transparentAlias };
591
+ nodes.push(node);
592
+ if (!byFile.has(file)) byFile.set(file, new Map());
593
+ byFile.get(file).set(d.name, node);
594
+ }
595
+ }
596
+ // Relax distances through actual lexical bindings. A same-name export in an unrelated module is
597
+ // never a target unless the caller imports that module's binding.
598
+ for (let pass = 0; pass < maxDepth + 3; pass++) {
599
+ let changed = false;
600
+ for (const node of nodes) {
601
+ for (const callee of node.calls) {
602
+ const local = byFile.get(node.file)?.get(callee);
603
+ let targets = local ? [[local, 0]] : [];
604
+ if (!local) {
605
+ const binding = importsByFile.get(node.file)?.get(callee);
606
+ const targetFile = binding ? resolveModule(repo, node.file, binding.source) : null;
607
+ const target = targetFile ? byFile.get(targetFile) : null;
608
+ if (target) {
609
+ const cost = AUTH_CALL_NAME.test(binding.imported) ? 0 : 1;
610
+ targets = [...target.values()].filter((t) => t.exported && (t.name === binding.imported || t.exports.includes(binding.imported) || (binding.imported === 'default' && t.exports.includes('default')))).map((t) => [t, cost]);
611
+ }
612
+ }
613
+ for (const [t, cost] of targets) {
614
+ if (t.dist + cost < node.dist) { node.dist = t.dist + cost; changed = true; }
615
+ // A wrapper may inherit a throwing guard only when it transparently awaits/returns it and
616
+ // does not catch the failure. Do not treat a returned auth value as a guard.
617
+ if (t.failsClosed && (callsUnconditionally(node.body, callee) || node.transparentAlias === callee)) {
618
+ if (!node.failsClosed) { node.failsClosed = true; changed = true; }
619
+ }
620
+ if (t.returnsAuthValue) for (const alias of resultAliases(node.body, callee)) {
621
+ if (!node.authValues.has(alias)) { node.authValues.add(alias); changed = true; }
622
+ }
623
+ if (directReturnCall(node.body, callee)) {
624
+ if (t.returnsAuthValue && !node.returnsAuthValue) { node.returnsAuthValue = true; changed = true; }
625
+ for (const key of t.denialKeys) if (!node.denialKeys.has(key)) { node.denialKeys.add(key); changed = true; }
626
+ }
627
+ }
628
+ }
629
+ if (throwsOnMissingAuth(node.body, node.authValues, node.denialCalls) && !node.failsClosed) { node.failsClosed = true; changed = true; }
630
+ for (const key of denialResultKeys(node.body, node.authValues)) if (!node.denialKeys.has(key)) { node.denialKeys.add(key); changed = true; }
631
+ if ([...node.authValues].some((name) => directReturnName(node.body, name)) && !node.returnsAuthValue) { node.returnsAuthValue = true; changed = true; }
632
+ }
633
+ if (!changed) break;
634
+ }
635
+ const helpers = nodes
636
+ .filter((node) => node.exported && node.dist <= maxDepth && !DENY.has(node.name) && IDENT.test(node.name) && !/^[A-Z]/.test(node.name))
637
+ .map((node) => ({ name: node.name, file: node.file, depth: node.dist, exports: node.exports, failsClosed: node.failsClosed, returnsAuthValue: node.returnsAuthValue, denialKeys: [...node.denialKeys] }));
638
+ return { helpers: helpers.sort((a, b) => a.name.localeCompare(b.name) || a.file.localeCompare(b.file)), indexedFiles: indexed, candidateFiles: candidates.length, capped: candidates.length > files.length, authRpcs };
639
+ }
640
+
641
+ /** Names that are safe to put in a regex alternation: identifiers only, `$` escaped. */
642
+ export const regexNames = (names) => [...new Set(names)].filter((n) => IDENT.test(n) && !DENY.has(n)).sort().map((n) => n.replace(/\$/g, '\\$'));
643
+
644
+ /**
645
+ * Copies the vsg rule files to outDir with the sentinel in the auth-name regexes replaced by the extra names (the originals are never edited).
646
+ * Only .yaml rule files are copied. Returns the number of rule files that were changed.
647
+ */
648
+ export function writeRulesWithHelpers(srcDir, outDir, names) {
649
+ const alt = regexNames(names).join('|');
650
+ fs.mkdirSync(outDir, { recursive: true });
651
+ let changed = 0;
652
+ for (const f of fs.readdirSync(srcDir).filter((x) => /\.ya?ml$/.test(x))) {
653
+ const text = fs.readFileSync(path.join(srcDir, f), 'utf8');
654
+ const next = text.includes(SENTINEL) ? text.split(SENTINEL).join(alt ? `${alt}|${SENTINEL}` : SENTINEL) : text;
655
+ if (next !== text) changed++;
656
+ fs.writeFileSync(path.join(outDir, f), next);
657
+ }
658
+ return changed;
659
+ }