@pyric/cli 0.1.0-alpha.26 → 0.1.0-alpha.28

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 (340) hide show
  1. package/README.md +1 -1
  2. package/dist/assurance/.generated/conformance-verdicts.d.ts +5 -0
  3. package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
  4. package/dist/assurance/.generated/conformance-verdicts.js +5 -0
  5. package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
  6. package/dist/assurance/capabilities.d.ts.map +1 -1
  7. package/dist/assurance/capabilities.js +24 -5
  8. package/dist/assurance/capabilities.js.map +1 -1
  9. package/dist/assurance/runner.d.ts.map +1 -1
  10. package/dist/assurance/runner.js +37 -50
  11. package/dist/assurance/runner.js.map +1 -1
  12. package/dist/assurance/types.d.ts +5 -0
  13. package/dist/assurance/types.d.ts.map +1 -1
  14. package/dist/assurance/types.js.map +1 -1
  15. package/dist/assurance/validation.d.ts +2 -16
  16. package/dist/assurance/validation.d.ts.map +1 -1
  17. package/dist/assurance/validation.js +121 -1
  18. package/dist/assurance/validation.js.map +1 -1
  19. package/dist/bridge/surface/arguments/assurance.d.ts +31 -6
  20. package/dist/bridge/surface/arguments/assurance.d.ts.map +1 -1
  21. package/dist/bridge/surface/arguments/assurance.js +1 -0
  22. package/dist/bridge/surface/arguments/assurance.js.map +1 -1
  23. package/dist/bridge/surface/arguments/messaging.d.ts +2 -2
  24. package/dist/bridge/surface/methods/assurance/define.d.ts +2 -2
  25. package/dist/bridge/surface/methods/assurance/map.d.ts +29 -6
  26. package/dist/bridge/surface/methods/assurance/map.d.ts.map +1 -1
  27. package/dist/bridge/surface/methods/assurance/propose.d.ts +7 -0
  28. package/dist/bridge/surface/methods/assurance/propose.d.ts.map +1 -1
  29. package/dist/bridge/surface/methods/messaging/send.d.ts +4 -4
  30. package/dist/bridge/surface/render/discriminator-schemas.d.ts +6 -6
  31. package/dist/cli/database-rules.js +1 -1
  32. package/dist/cli/database-rules.js.map +1 -1
  33. package/dist/cli/index.d.ts +1 -0
  34. package/dist/cli/index.d.ts.map +1 -1
  35. package/dist/cli/index.js +28 -3
  36. package/dist/cli/index.js.map +1 -1
  37. package/dist/cli/pyric-config.d.ts +8 -0
  38. package/dist/cli/pyric-config.d.ts.map +1 -1
  39. package/dist/cli/pyric-config.js.map +1 -1
  40. package/dist/cli/run.d.ts +42 -0
  41. package/dist/cli/run.d.ts.map +1 -0
  42. package/dist/cli/run.js +115 -0
  43. package/dist/cli/run.js.map +1 -0
  44. package/dist/cli/serve.d.ts.map +1 -1
  45. package/dist/cli/serve.js +5 -3
  46. package/dist/cli/serve.js.map +1 -1
  47. package/dist/cli/verify.d.ts +6 -0
  48. package/dist/cli/verify.d.ts.map +1 -1
  49. package/dist/cli/verify.js +240 -15
  50. package/dist/cli/verify.js.map +1 -1
  51. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  52. package/dist/conformance/.generated/can-i-use.js +1 -1
  53. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  54. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
  55. package/dist/conformance/.generated/conformance-docs.js +1 -1
  56. package/dist/conformance/.generated/conformance-docs.js.map +1 -1
  57. package/dist/remote/local-storage-url.d.ts +2 -15
  58. package/dist/remote/local-storage-url.d.ts.map +1 -1
  59. package/dist/remote/local-storage-url.js +1 -60
  60. package/dist/remote/local-storage-url.js.map +1 -1
  61. package/dist/serve/assets/avatar-route.d.ts.map +1 -1
  62. package/dist/serve/assets/avatar-route.js +6 -6
  63. package/dist/serve/assets/avatar-route.js.map +1 -1
  64. package/dist/serve/assets/resolver.d.ts +3 -0
  65. package/dist/serve/assets/resolver.d.ts.map +1 -1
  66. package/dist/serve/assets/resolver.js +1 -1
  67. package/dist/serve/assets/resolver.js.map +1 -1
  68. package/dist/serve/entries/app-client.d.ts.map +1 -1
  69. package/dist/serve/entries/app-client.js +6 -0
  70. package/dist/serve/entries/app-client.js.map +1 -1
  71. package/dist/serve/repro/denials.d.ts.map +1 -1
  72. package/dist/serve/repro/denials.js +2 -1
  73. package/dist/serve/repro/denials.js.map +1 -1
  74. package/dist/serve/site-ui/_astro/{app.DFdWhpVY.js → app.BCNb6gMq.js} +1 -1
  75. package/dist/serve/site-ui/_astro/app.DhoRPDdN.js +147 -0
  76. package/dist/serve/site-ui/_astro/{seed.vvXV1ioI.js → seed.BO5CXcqE.js} +1 -1
  77. package/dist/serve/site-ui/auth/index.html +1 -1
  78. package/dist/serve/site-ui/docs/_rhythm/index.html +1 -1
  79. package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +1 -1
  80. package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +1 -1
  81. package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +1 -1
  82. package/dist/serve/site-ui/docs/ai-compat/index.html +1 -1
  83. package/dist/serve/site-ui/docs/api-reference/index.html +2 -1
  84. package/dist/serve/site-ui/docs/api-reference.md +1 -0
  85. package/dist/serve/site-ui/docs/app-compat/index.html +1 -1
  86. package/dist/serve/site-ui/docs/auth-compat/index.html +2 -2
  87. package/dist/serve/site-ui/docs/auth-compat.md +1 -1
  88. package/dist/serve/site-ui/docs/auth-flutter-compat/index.html +1 -1
  89. package/dist/serve/site-ui/docs/auth-kotlin-compat/index.html +1 -1
  90. package/dist/serve/site-ui/docs/auth-swift-compat/index.html +1 -1
  91. package/dist/serve/site-ui/docs/build/ai-logic/index.html +1 -1
  92. package/dist/serve/site-ui/docs/build/authentication/index.html +1 -1
  93. package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +1 -1
  94. package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +1 -1
  95. package/dist/serve/site-ui/docs/build/cloud-storage/index.html +13 -2
  96. package/dist/serve/site-ui/docs/build/cloud-storage.md +23 -0
  97. package/dist/serve/site-ui/docs/build/default-avatars/index.html +1 -1
  98. package/dist/serve/site-ui/docs/build/inspect-activity/index.html +1 -1
  99. package/dist/serve/site-ui/docs/build/realtime-database/index.html +1 -1
  100. package/dist/serve/site-ui/docs/conformance-scores/index.html +1 -1
  101. package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +1 -1
  102. package/dist/serve/site-ui/docs/database-compat/index.html +5 -1
  103. package/dist/serve/site-ui/docs/database-compat.md +4 -0
  104. package/dist/serve/site-ui/docs/examples/chess/index.html +1 -1
  105. package/dist/serve/site-ui/docs/examples/index.html +1 -1
  106. package/dist/serve/site-ui/docs/firestore-compat/index.html +1 -1
  107. package/dist/serve/site-ui/docs/firestore-flutter-compat/index.html +1 -1
  108. package/dist/serve/site-ui/docs/firestore-kotlin-compat/index.html +1 -1
  109. package/dist/serve/site-ui/docs/firestore-swift-compat/index.html +1 -1
  110. package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +1 -1
  111. package/dist/serve/site-ui/docs/get-started/backend-bundlers/index.html +1 -1
  112. package/dist/serve/site-ui/docs/get-started/flutter/index.html +1 -1
  113. package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
  114. package/dist/serve/site-ui/docs/get-started/nextjs/index.html +1 -1
  115. package/dist/serve/site-ui/docs/get-started/node-host/index.html +1 -1
  116. package/dist/serve/site-ui/docs/get-started/start-building/index.html +2 -1
  117. package/dist/serve/site-ui/docs/get-started/start-building.md +2 -0
  118. package/dist/serve/site-ui/docs/get-started/vite/index.html +4 -2
  119. package/dist/serve/site-ui/docs/get-started/vite.md +4 -0
  120. package/dist/serve/site-ui/docs/index.json +50 -0
  121. package/dist/serve/site-ui/docs/messaging-compat/index.html +1 -1
  122. package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +1 -1
  123. package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +1 -1
  124. package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +1 -1
  125. package/dist/serve/site-ui/docs/observe/switch-and-impersonate-identities/index.html +1 -1
  126. package/dist/serve/site-ui/docs/overview/index.html +1 -1
  127. package/dist/serve/site-ui/docs/pyric-admin-app-check-reference-api/index.html +1 -1
  128. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
  129. package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
  130. package/dist/serve/site-ui/docs/pyric-admin-data-connect-reference-api/index.html +1 -1
  131. package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
  132. package/dist/serve/site-ui/docs/pyric-admin-eventarc-reference-api/index.html +1 -1
  133. package/dist/serve/site-ui/docs/pyric-admin-extensions-reference-api/index.html +1 -1
  134. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
  135. package/dist/serve/site-ui/docs/pyric-admin-functions-reference-api/index.html +1 -1
  136. package/dist/serve/site-ui/docs/pyric-admin-installations-reference-api/index.html +1 -1
  137. package/dist/serve/site-ui/docs/pyric-admin-instance-id-reference-api/index.html +1 -1
  138. package/dist/serve/site-ui/docs/pyric-admin-machine-learning-reference-api/index.html +1 -1
  139. package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
  140. package/dist/serve/site-ui/docs/pyric-admin-phone-number-verification-reference-api/index.html +1 -1
  141. package/dist/serve/site-ui/docs/pyric-admin-project-management-reference-api/index.html +1 -1
  142. package/dist/serve/site-ui/docs/pyric-admin-reference-api/index.html +1 -1
  143. package/dist/serve/site-ui/docs/pyric-admin-remote-config-reference-api/index.html +1 -1
  144. package/dist/serve/site-ui/docs/pyric-admin-security-rules-reference-api/index.html +1 -1
  145. package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
  146. package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +1 -1
  147. package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
  148. package/dist/serve/site-ui/docs/pyric-analytics-reference-api/index.html +1 -1
  149. package/dist/serve/site-ui/docs/pyric-app-check-reference-api/index.html +1 -1
  150. package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +1 -1
  151. package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +2 -2
  152. package/dist/serve/site-ui/docs/pyric-auth-reference-api.md +1 -1
  153. package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
  154. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +24 -5
  155. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api.md +11 -4
  156. package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
  157. package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
  158. package/dist/serve/site-ui/docs/pyric-cli-bundler-reference-api/index.html +1 -1
  159. package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
  160. package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
  161. package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
  162. package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
  163. package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
  164. package/dist/serve/site-ui/docs/pyric-cli-flow-reference-api/index.html +1 -1
  165. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +1 -1
  166. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +1 -1
  167. package/dist/serve/site-ui/docs/pyric-cli-register-client-app-bridge-reference-api/index.html +1 -1
  168. package/dist/serve/site-ui/docs/pyric-cli-register-client-app-reference-api/index.html +1 -1
  169. package/dist/serve/site-ui/docs/pyric-cli-register-client-auth-reference-api/index.html +1 -1
  170. package/dist/serve/site-ui/docs/pyric-cli-register-client-database-reference-api/index.html +1 -1
  171. package/dist/serve/site-ui/docs/pyric-cli-register-client-firestore-reference-api/index.html +1 -1
  172. package/dist/serve/site-ui/docs/pyric-cli-register-client-storage-reference-api/index.html +1 -1
  173. package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
  174. package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
  175. package/dist/serve/site-ui/docs/pyric-cli-repro-reference-api/index.html +1 -1
  176. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +1 -1
  177. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +16 -3
  178. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api.md +11 -10
  179. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +17 -3
  180. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api.md +7 -5
  181. package/dist/serve/site-ui/docs/pyric-cli-vite-ssr-loader-reference-api/index.html +1 -1
  182. package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +1 -1
  183. package/dist/serve/site-ui/docs/pyric-firestore-lite-reference-api/index.html +1 -1
  184. package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +1 -1
  185. package/dist/serve/site-ui/docs/pyric-functions-reference-api/index.html +1 -1
  186. package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +1 -1
  187. package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
  188. package/dist/serve/site-ui/docs/pyric-performance-reference-api/index.html +1 -1
  189. package/dist/serve/site-ui/docs/pyric-remote-config-reference-api/index.html +1 -1
  190. package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +1 -1
  191. package/dist/serve/site-ui/docs/pyric-sandbox-branches-store-reference-api/index.html +1 -1
  192. package/dist/serve/site-ui/docs/pyric-sandbox-checkpoints-directory-reference-api/index.html +1 -1
  193. package/dist/serve/site-ui/docs/pyric-sandbox-checkpoints-reference-api/index.html +1 -1
  194. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
  195. package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
  196. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +1 -1
  197. package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +1 -1
  198. package/dist/serve/site-ui/docs/pyric-storage-sw-reference-api/index.html +102 -0
  199. package/dist/serve/site-ui/docs/pyric-storage-sw-reference-api.md +76 -0
  200. package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
  201. package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
  202. package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
  203. package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
  204. package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
  205. package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
  206. package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
  207. package/dist/serve/site-ui/docs/pyric-ui-listener-owner-reference-api/index.html +1 -1
  208. package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
  209. package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
  210. package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
  211. package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
  212. package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
  213. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
  214. package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
  215. package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
  216. package/dist/serve/site-ui/docs/reference/cli/index.html +12 -2
  217. package/dist/serve/site-ui/docs/reference/cli.md +24 -0
  218. package/dist/serve/site-ui/docs/rtdb-flutter-compat/index.html +1 -1
  219. package/dist/serve/site-ui/docs/rtdb-kotlin-compat/index.html +1 -1
  220. package/dist/serve/site-ui/docs/rtdb-swift-compat/index.html +1 -1
  221. package/dist/serve/site-ui/docs/rules-compat/index.html +1 -1
  222. package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +1 -1
  223. package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +1 -1
  224. package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +1 -1
  225. package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
  226. package/dist/serve/site-ui/docs/secure/rtdb-rules-standard-library/index.html +1 -1
  227. package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +1 -1
  228. package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +1 -1
  229. package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +1 -1
  230. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +2 -2
  231. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite.md +1 -1
  232. package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +5 -2
  233. package/dist/serve/site-ui/docs/ship/ship-to-production.md +7 -1
  234. package/dist/serve/site-ui/docs/ship/test-in-node/index.html +1 -1
  235. package/dist/serve/site-ui/docs/storage-compat/index.html +1 -1
  236. package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
  237. package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +1 -1
  238. package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
  239. package/dist/serve/site-ui/docs/tutorial/index.html +1 -1
  240. package/dist/serve/site-ui/examples/chess/index.html +1 -1
  241. package/dist/serve/site-ui/firestore/index.html +1 -1
  242. package/dist/serve/site-ui/llms.txt +1 -0
  243. package/dist/serve/site-ui/rtdb/index.html +1 -1
  244. package/dist/serve/site-ui/settings/index.html +1 -1
  245. package/dist/serve/site-ui/storage/index.html +1 -1
  246. package/dist/serve/site-ui/studio/index.html +1 -1
  247. package/dist/serve/site-ui/traffic/index.html +1 -1
  248. package/dist/serve/verify-hint.d.ts +2 -0
  249. package/dist/serve/verify-hint.d.ts.map +1 -0
  250. package/dist/serve/verify-hint.js +10 -0
  251. package/dist/serve/verify-hint.js.map +1 -0
  252. package/dist/serve/vite-generation-rules-watch.d.ts.map +1 -1
  253. package/dist/serve/vite-generation-rules-watch.js +5 -3
  254. package/dist/serve/vite-generation-rules-watch.js.map +1 -1
  255. package/dist/serve/vite-plugin.d.ts +6 -0
  256. package/dist/serve/vite-plugin.d.ts.map +1 -1
  257. package/dist/serve/vite-plugin.js +11 -1
  258. package/dist/serve/vite-plugin.js.map +1 -1
  259. package/dist/serve/vite-service-worker.d.ts +12 -0
  260. package/dist/serve/vite-service-worker.d.ts.map +1 -0
  261. package/dist/serve/vite-service-worker.js +33 -0
  262. package/dist/serve/vite-service-worker.js.map +1 -0
  263. package/dist/serve/worker/client/auth.d.ts.map +1 -1
  264. package/dist/serve/worker/client/auth.js +4 -3
  265. package/dist/serve/worker/client/auth.js.map +1 -1
  266. package/dist/serve/worker/host/auth-action-codes.js +2 -2
  267. package/dist/serve/worker/host/auth-action-codes.js.map +1 -1
  268. package/dist/serve/worker/host/auth-custom-token.d.ts.map +1 -1
  269. package/dist/serve/worker/host/auth-custom-token.js +4 -2
  270. package/dist/serve/worker/host/auth-custom-token.js.map +1 -1
  271. package/dist/serve/worker/host/dispatch.d.ts.map +1 -1
  272. package/dist/serve/worker/host/dispatch.js +3 -1
  273. package/dist/serve/worker/host/dispatch.js.map +1 -1
  274. package/dist/serve/worker/host-auth.d.ts +6 -0
  275. package/dist/serve/worker/host-auth.d.ts.map +1 -1
  276. package/dist/serve/worker/host-auth.js +16 -5
  277. package/dist/serve/worker/host-auth.js.map +1 -1
  278. package/dist/serve/worker/inbound-validation.d.ts.map +1 -1
  279. package/dist/serve/worker/inbound-validation.js +1 -0
  280. package/dist/serve/worker/inbound-validation.js.map +1 -1
  281. package/dist/serve/worker/protocol/storage.d.ts +1 -8
  282. package/dist/serve/worker/protocol/storage.d.ts.map +1 -1
  283. package/dist/serve/worker/protocol/storage.js +2 -14
  284. package/dist/serve/worker/protocol/storage.js.map +1 -1
  285. package/dist/serve/worker/protocol.d.ts +2 -0
  286. package/dist/serve/worker/protocol.d.ts.map +1 -1
  287. package/dist/serve/worker/protocol.js.map +1 -1
  288. package/dist/verify/index.d.ts +16 -2
  289. package/dist/verify/index.d.ts.map +1 -1
  290. package/dist/verify/index.js +100 -18
  291. package/dist/verify/index.js.map +1 -1
  292. package/dist/verify/staging/engine.d.ts +102 -0
  293. package/dist/verify/staging/engine.d.ts.map +1 -0
  294. package/dist/verify/staging/engine.js +371 -0
  295. package/dist/verify/staging/engine.js.map +1 -0
  296. package/dist/verify/staging/rest.d.ts +68 -0
  297. package/dist/verify/staging/rest.d.ts.map +1 -0
  298. package/dist/verify/staging/rest.js +93 -0
  299. package/dist/verify/staging/rest.js.map +1 -0
  300. package/dist/verify/staging/target.d.ts +39 -0
  301. package/dist/verify/staging/target.d.ts.map +1 -0
  302. package/dist/verify/staging/target.js +117 -0
  303. package/dist/verify/staging/target.js.map +1 -0
  304. package/package.json +12 -8
  305. package/src/assurance/.generated/conformance-verdicts.ts +5 -0
  306. package/src/assurance/capabilities.ts +22 -4
  307. package/src/assurance/runner.ts +40 -58
  308. package/src/assurance/types.ts +3 -0
  309. package/src/assurance/validation.ts +137 -0
  310. package/src/bridge/surface/arguments/assurance.ts +1 -0
  311. package/src/cli/database-rules.ts +1 -1
  312. package/src/cli/index.ts +28 -3
  313. package/src/cli/pyric-config.ts +5 -0
  314. package/src/cli/run.ts +139 -0
  315. package/src/cli/serve.ts +6 -3
  316. package/src/cli/verify.ts +244 -18
  317. package/src/conformance/.generated/can-i-use.ts +1 -1
  318. package/src/conformance/.generated/conformance-docs.ts +1 -1
  319. package/src/remote/local-storage-url.ts +1 -58
  320. package/src/serve/assets/avatar-route.ts +6 -7
  321. package/src/serve/assets/resolver.ts +4 -1
  322. package/src/serve/entries/app-client.ts +7 -0
  323. package/src/serve/repro/denials.ts +2 -1
  324. package/src/serve/verify-hint.ts +8 -0
  325. package/src/serve/vite-generation-rules-watch.ts +6 -3
  326. package/src/serve/vite-plugin.ts +15 -1
  327. package/src/serve/vite-service-worker.ts +44 -0
  328. package/src/serve/worker/client/auth.ts +4 -3
  329. package/src/serve/worker/host/auth-action-codes.ts +2 -2
  330. package/src/serve/worker/host/auth-custom-token.ts +4 -2
  331. package/src/serve/worker/host/dispatch.ts +2 -0
  332. package/src/serve/worker/host-auth.ts +18 -5
  333. package/src/serve/worker/inbound-validation.ts +1 -0
  334. package/src/serve/worker/protocol/storage.ts +2 -14
  335. package/src/serve/worker/protocol.ts +2 -0
  336. package/src/verify/index.ts +119 -19
  337. package/src/verify/staging/engine.ts +455 -0
  338. package/src/verify/staging/rest.ts +129 -0
  339. package/src/verify/staging/target.ts +137 -0
  340. package/dist/serve/site-ui/_astro/app.BpEphDR7.js +0 -147
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The staging target `pyric verify --engine staging` replays against, from
3
+ * the project's `pyric.json`:
4
+ *
5
+ * { "verify": { "staging": { "project": "my-staging", "rtdb": "https://my-verify.firebaseio.com" } } }
6
+ *
7
+ * `rtdb` is a database URL or an instance name (`my-verify` resolves to
8
+ * `https://my-verify.firebaseio.com`; an instance outside us-central1 needs
9
+ * its full URL). The user brings the instance; pyric provisions nothing.
10
+ *
11
+ * An instance the app's own `firebase.json` deploys rules to, or the app
12
+ * project's default instance, is refused: those hold the app's data.
13
+ */
14
+ import { readFileSync } from 'node:fs';
15
+ import { resolveDatabaseInstance } from 'pyric/sandbox/internal';
16
+ import { firebaseToolsConfigstorePath } from '../../cli/firebase-project.js';
17
+ import { resolveScope, SERVICE_ACCOUNT_BASE64_ENV_KEY, SERVICE_ACCOUNT_FILE_ENV_KEY } from '../../credentials/node/scope.js';
18
+ export class StagingConfigError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = 'StagingConfigError';
22
+ }
23
+ }
24
+ const CONFIG_EXAMPLE = '{ "verify": { "staging": { "project": "<staging project id>", "rtdb": "https://<dedicated instance>.firebaseio.com" } } }';
25
+ /** The staging target in a parsed `pyric.json`, or a config error naming what is missing. */
26
+ export function stagingTargetFrom(config) {
27
+ const staging = config?.verify?.staging;
28
+ if (staging === undefined) {
29
+ throw new StagingConfigError(`no staging target. Add one to pyric.json: ${CONFIG_EXAMPLE}`);
30
+ }
31
+ const { project, rtdb } = (staging ?? {});
32
+ if (typeof project !== 'string' || project.trim() === '') {
33
+ throw new StagingConfigError(`pyric.json verify.staging.project must name the staging project: ${CONFIG_EXAMPLE}`);
34
+ }
35
+ if (typeof rtdb !== 'string' || rtdb.trim() === '') {
36
+ throw new StagingConfigError(`pyric.json verify.staging.rtdb must name the dedicated instance: ${CONFIG_EXAMPLE}`);
37
+ }
38
+ const trimmed = rtdb.trim();
39
+ const databaseUrl = /^[a-z0-9-]+$/i.test(trimmed) ? `https://${trimmed}.firebaseio.com` : trimmed.replace(/\/+$/, '');
40
+ let instance;
41
+ try {
42
+ instance = resolveDatabaseInstance(databaseUrl)?.name;
43
+ }
44
+ catch {
45
+ instance = undefined;
46
+ }
47
+ if (instance === undefined) {
48
+ throw new StagingConfigError(`pyric.json verify.staging.rtdb "${rtdb}" is not a Realtime Database URL or instance name.`);
49
+ }
50
+ return { projectId: project.trim(), databaseUrl, instance };
51
+ }
52
+ /**
53
+ * Refuse a staging instance the app uses: one `firebase.json` deploys rules
54
+ * to (by instance or by a `.firebaserc` target), or the default instance of
55
+ * the app's project.
56
+ */
57
+ export function assertNotAppInstance(target, app) {
58
+ const declared = new Set(app.targetInstances ?? []);
59
+ const database = app.firebaseJson?.database;
60
+ const entries = Array.isArray(database) ? database : database ? [database] : [];
61
+ for (const entry of entries) {
62
+ const instance = entry.instance;
63
+ if (typeof instance === 'string')
64
+ declared.add(instance);
65
+ }
66
+ if (app.appProjectId)
67
+ declared.add(`${app.appProjectId}-default-rtdb`);
68
+ if (declared.has(target.instance)) {
69
+ throw new StagingConfigError(`refusing staging instance ${target.instance}: the app uses it (firebase.json deploys rules to it, or it is the default instance of project ${app.appProjectId}). The staging engine replaces an instance's rules and data, so it needs one dedicated to it.`);
70
+ }
71
+ }
72
+ /**
73
+ * An OAuth token for the staging project: a service account or Application
74
+ * Default Credentials (`resolveScope`), else the Firebase CLI's login
75
+ * (`firebase login`) while its access token is fresh.
76
+ */
77
+ export async function stagingToken(projectId, deps = {}) {
78
+ const env = deps.env ?? process.env;
79
+ try {
80
+ const { scope, source } = await (deps.resolve ?? resolveScope)({ projectId, ...(deps.env ? { env: deps.env } : {}) });
81
+ return { token: () => scope.resolveToken(), source };
82
+ }
83
+ catch (resolveError) {
84
+ // A configured credential that fails is an error to fix, not a reason to
85
+ // run as the developer's own CLI login instead.
86
+ if (configuredCredential(env))
87
+ throw resolveError;
88
+ const cli = firebaseCliToken(env, (deps.now ?? Date.now)());
89
+ if (cli !== undefined)
90
+ return { token: async () => cli, source: 'firebase login' };
91
+ throw new StagingConfigError(`no credentials for staging project ${projectId}. ${resolveError instanceof Error ? resolveError.message : String(resolveError)} ` +
92
+ 'Or sign in with `firebase login`: its token is used while it has at least 15 minutes left (run any firebase command to refresh it).');
93
+ }
94
+ }
95
+ /** Whether the environment names a service account (`resolveScope`'s first two sources). */
96
+ function configuredCredential(env) {
97
+ return [SERVICE_ACCOUNT_BASE64_ENV_KEY, SERVICE_ACCOUNT_FILE_ENV_KEY].some((key) => (env[key] ?? '').trim() !== '');
98
+ }
99
+ /** How long a CLI token must stay valid for a run to start with it; it cannot be refreshed mid-run. */
100
+ export const CLI_TOKEN_MARGIN_MS = 15 * 60_000;
101
+ /** The Firebase CLI's access token, when its configstore holds one with a known expiry at least the margin away. */
102
+ function firebaseCliToken(env, now) {
103
+ try {
104
+ const store = JSON.parse(readFileSync(firebaseToolsConfigstorePath(env), 'utf8'));
105
+ const token = store.tokens?.access_token;
106
+ const expiresAt = store.tokens?.expires_at;
107
+ if (typeof token !== 'string' || token === '')
108
+ return undefined;
109
+ if (typeof expiresAt !== 'number' || expiresAt - now < CLI_TOKEN_MARGIN_MS)
110
+ return undefined;
111
+ return token;
112
+ }
113
+ catch {
114
+ return undefined;
115
+ }
116
+ }
117
+ //# sourceMappingURL=target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.js","sourceRoot":"","sources":["../../../src/verify/staging/target.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyric/cli",
3
- "version": "0.1.0-alpha.26",
3
+ "version": "0.1.0-alpha.28",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://pyric.dev",
6
6
  "repository": {
@@ -77,7 +77,8 @@
77
77
  },
78
78
  "./vite": {
79
79
  "types": "./dist/vite.d.ts",
80
- "import": "./dist/vite.js"
80
+ "import": "./dist/vite.js",
81
+ "default": "./dist/vite.js"
81
82
  },
82
83
  "./vite/ssr-loader": {
83
84
  "types": "./dist/serve/vite-ssr-loader.d.ts",
@@ -88,7 +89,8 @@
88
89
  },
89
90
  "./next": {
90
91
  "types": "./dist/next/index.d.ts",
91
- "import": "./dist/next/index.js"
92
+ "import": "./dist/next/index.js",
93
+ "default": "./dist/next/index.js"
92
94
  },
93
95
  "./bundler": {
94
96
  "types": "./dist/bundler/index.d.ts",
@@ -109,7 +111,8 @@
109
111
  "types": "./dist/register/index.d.ts",
110
112
  "node": {
111
113
  "types": "./dist/register/index.d.ts",
112
- "import": "./dist/register/index.js"
114
+ "import": "./dist/register/index.js",
115
+ "default": "./dist/register/index.js"
113
116
  }
114
117
  },
115
118
  "./register/app-bridge": {
@@ -209,7 +212,8 @@
209
212
  },
210
213
  "./next/internal/react-hook": {
211
214
  "import": "./dist/next/react-hook-entry.js"
212
- }
215
+ },
216
+ "./package.json": "./package.json"
213
217
  },
214
218
  "bin": {
215
219
  "pyric": "./dist/cli/index.js"
@@ -245,12 +249,12 @@
245
249
  },
246
250
  "dependencies": {
247
251
  "@modelcontextprotocol/sdk": "^1.0.0",
248
- "create-pyric": "^0.1.0-alpha.26",
252
+ "create-pyric": "^0.1.0-alpha.28",
249
253
  "esbuild": "^0.28.0",
250
254
  "fake-indexeddb": "^6.0.0",
251
255
  "jsonc-parser": "^3.3.1",
252
- "pyric": "^0.1.0-alpha.26",
253
- "pyric-admin": "^0.1.0-alpha.26",
256
+ "pyric": "^0.1.0-alpha.28",
257
+ "pyric-admin": "^0.1.0-alpha.28",
254
258
  "ws": "^8.18.0",
255
259
  "zod": "^3.23.0"
256
260
  },
@@ -1372,9 +1372,14 @@ export const CONFORMANCE_VERDICTS = {
1372
1372
  "rtdb-modular#MI4": "supported",
1373
1373
  "rtdb-modular#MI5": "supported",
1374
1374
  "rtdb-modular#MI6": "supported",
1375
+ "rtdb-modular#MI7": "supported",
1375
1376
  "rtdb-modular#RS1": "qualified",
1376
1377
  "rtdb-modular#RS2": "qualified",
1377
1378
  "rtdb-modular#RS3": "qualified",
1379
+ "rtdb-modular#VR1": "supported",
1380
+ "rtdb-modular#VR2": "supported",
1381
+ "rtdb-modular#VR3": "supported",
1382
+ "rtdb-modular#VR4": "supported",
1378
1383
  "rtdb-rules#1": "supported",
1379
1384
  "rtdb-rules#10": "supported",
1380
1385
  "rtdb-rules#11": "supported",
@@ -21,6 +21,7 @@ import {
21
21
  type PathSegment,
22
22
  } from "pyric/rules/internal";
23
23
  import { rtdbRulesSourceRejection } from "pyric/rules/internal/rtdb";
24
+ import { resolveDatabaseInstance } from "pyric/sandbox/internal";
24
25
  import { compileStorageRules } from "pyric/storage/internal";
25
26
  import {
26
27
  CONFORMANCE_VERDICTS,
@@ -101,9 +102,17 @@ export function rulesLoadRefusal(
101
102
  service: AssuranceProbe["control"]["service"],
102
103
  ): string | undefined {
103
104
  if (service === "rtdb") {
104
- if (!target.rules.rtdb) return undefined;
105
- const rejection = rtdbRulesSourceRejection(target.rules.rtdb);
106
- return rejection === null ? undefined : rejection.message;
105
+ if (target.rules.rtdb) {
106
+ const rejection = rtdbRulesSourceRejection(target.rules.rtdb);
107
+ if (rejection !== null) return rejection.message;
108
+ }
109
+ if (target.rules.rtdbInstances) {
110
+ for (const [instanceName, instanceRules] of Object.entries(target.rules.rtdbInstances)) {
111
+ const rejection = rtdbRulesSourceRejection(instanceRules as any);
112
+ if (rejection !== null) return `instance '${instanceName}': ${rejection.message}`;
113
+ }
114
+ }
115
+ return undefined;
107
116
  }
108
117
  const source =
109
118
  service === "firestore"
@@ -389,7 +398,16 @@ function rtdbRequirements(
389
398
  target: LocalFirebaseTarget,
390
399
  probe: AssuranceProbe,
391
400
  ): CapabilityRequirement[] {
392
- const rules = target.rules.rtdb;
401
+ let rules = target.rules.rtdb;
402
+ if (probe.control.service === "rtdb" && probe.control.instance && probe.control.instance !== "default") {
403
+ const resolvedName = resolveDatabaseInstance(probe.control.instance)?.name;
404
+ if (resolvedName && target.rules.rtdbInstances) {
405
+ const declaredEntry = Object.entries(target.rules.rtdbInstances).find(([k]) => resolveDatabaseInstance(k)?.name === resolvedName);
406
+ if (declaredEntry) {
407
+ rules = declaredEntry[1] as any;
408
+ }
409
+ }
410
+ }
393
411
  const operations = [probe.control, probe.mutation.operation];
394
412
  const ruleSources = rules
395
413
  ? collectRtdbRuleSources(rules.rules).filter((rule) =>
@@ -60,15 +60,17 @@ import {
60
60
  initializeSandbox,
61
61
  type AuthState,
62
62
  type LocalSandbox,
63
+ type SandboxContext,
63
64
  type SandboxEvent,
64
65
  } from "pyric/sandbox";
65
- import { getInternalEnv } from "pyric/sandbox/internal";
66
+ import { getInternalEnv, resolveDatabaseInstance } from "pyric/sandbox/internal";
66
67
  import { qualifyProbe, rulesLoadRefusal } from "./capabilities.js";
67
68
  import {
68
69
  assertActor,
69
70
  assertInvariant,
70
71
  assertProbe,
71
72
  assertTarget,
73
+ assertCampaign,
72
74
  } from "./validation.js";
73
75
  import {
74
76
  ASSURANCE_CAMPAIGN_SCHEMA,
@@ -96,9 +98,12 @@ interface ActorRuntime {
96
98
  auth: AuthState | null;
97
99
  }
98
100
 
101
+
102
+
99
103
  interface ServiceRuntime {
100
104
  sandbox: LocalSandbox;
101
105
  actor: ActorRuntime;
106
+ context: SandboxContext;
102
107
  firestore?: Firestore;
103
108
  rtdb?: Database;
104
109
  rtdbAdmin?: Database;
@@ -114,56 +119,6 @@ interface ExecutedOperation {
114
119
 
115
120
  let storageRunSequence = 0;
116
121
 
117
- function assertCampaign(spec: AuthorizationCampaignSpec): void {
118
- if (spec.schema !== ASSURANCE_CAMPAIGN_SCHEMA) {
119
- throw new AssuranceInputError(
120
- `campaign schema must be '${ASSURANCE_CAMPAIGN_SCHEMA}'.`,
121
- );
122
- }
123
- if (!spec.id.trim())
124
- throw new AssuranceInputError("campaign id is required.");
125
- assertTarget(spec.target);
126
- if (!Array.isArray(spec.actors)) {
127
- throw new AssuranceInputError("campaign actors must be an array.");
128
- }
129
- if (!Array.isArray(spec.invariants)) {
130
- throw new AssuranceInputError("campaign invariants must be an array.");
131
- }
132
- if (!Array.isArray(spec.probes)) {
133
- throw new AssuranceInputError("campaign probes must be an array.");
134
- }
135
- for (const actor of spec.actors) assertActor(actor);
136
- for (const invariant of spec.invariants) assertInvariant(invariant);
137
- for (const probe of spec.probes) assertProbe(probe);
138
- uniqueIds(spec.actors, "actor");
139
- uniqueIds(spec.invariants, "invariant");
140
- uniqueIds(spec.probes, "probe");
141
-
142
- for (const probe of spec.probes) {
143
- const actor = spec.actors.find((item) => item.id === probe.actorId);
144
- const invariant = spec.invariants.find(
145
- (item) => item.id === probe.invariantId,
146
- );
147
- if (!actor)
148
- throw new AssuranceInputError(
149
- `probe '${probe.id}' references unknown actor '${probe.actorId}'.`,
150
- );
151
- if (!invariant) {
152
- throw new AssuranceInputError(
153
- `probe '${probe.id}' references unknown invariant '${probe.invariantId}'.`,
154
- );
155
- }
156
- if (
157
- invariant.service !== "cross-service" &&
158
- invariant.service !== probe.control.service
159
- ) {
160
- throw new AssuranceInputError(
161
- `probe '${probe.id}' service does not match invariant '${invariant.id}'.`,
162
- );
163
- }
164
- }
165
- }
166
-
167
122
  function uniqueIds(items: ReadonlyArray<{ id: string }>, label: string): void {
168
123
  const seen = new Set<string>();
169
124
  for (const item of items) {
@@ -337,7 +292,7 @@ async function createRuntime(
337
292
  const sandbox = initializeSandbox();
338
293
  seedAuth(sandbox, target);
339
294
  const context = sandbox.withAuth(actorRuntime.auth);
340
- const runtime: ServiceRuntime = { sandbox, actor: actorRuntime };
295
+ const runtime: ServiceRuntime = { sandbox, actor: actorRuntime, context };
341
296
 
342
297
  if (service === "firestore") {
343
298
  const firestore = getFirestore(context);
@@ -347,12 +302,34 @@ async function createRuntime(
347
302
  } else if (service === "rtdb") {
348
303
  const rtdb = getDatabase(context);
349
304
  const admin = getAdminDatabase(sandbox);
350
- const refusal = rtdbRulesSourceRejection(target.rules.rtdb);
351
- if (refusal !== null) throw new Error(`The assurance target's database rules would not deploy. ${refusal.message}`);
352
- rtdbSandbox.setRules(rtdb, target.rules.rtdb!);
353
- rtdbSandbox.setData(admin, { "/": target.state.rtdb ?? {} });
305
+ if (target.rules.rtdb) {
306
+ const refusal = rtdbRulesSourceRejection(target.rules.rtdb as any);
307
+ if (refusal !== null) throw new Error(`The assurance target's database rules would not deploy. ${refusal.message}`);
308
+ rtdbSandbox.setRules(rtdb, target.rules.rtdb as any);
309
+ rtdbSandbox.setData(admin, { "/": target.state.rtdb ?? {} });
310
+ }
354
311
  runtime.rtdb = rtdb;
355
312
  runtime.rtdbAdmin = admin;
313
+
314
+ if (target.rules.rtdbInstances) {
315
+ const stateByResolved = new Map<string, unknown>();
316
+ if (target.state.rtdbInstances) {
317
+ for (const [k, v] of Object.entries(target.state.rtdbInstances)) {
318
+ stateByResolved.set(resolveDatabaseInstance(k)!.name, v);
319
+ }
320
+ }
321
+ for (const [instanceName, instanceValue] of Object.entries(target.rules.rtdbInstances)) {
322
+ const instanceRefusal = rtdbRulesSourceRejection(instanceValue);
323
+ if (instanceRefusal !== null) throw new Error(`The assurance target's database rules for instance '${instanceName}' would not deploy. ${instanceRefusal.message}`);
324
+
325
+ const resolved = resolveDatabaseInstance(instanceName)!;
326
+ const instDb = getDatabase(context, resolved.url);
327
+ const instAdmin = getAdminDatabase(sandbox, resolved.url);
328
+ rtdbSandbox.setRules(instDb, instanceValue as { rules: Record<string, unknown> });
329
+ const instData = stateByResolved.get(resolved.name);
330
+ rtdbSandbox.setData(instAdmin, { "/": instData ?? {} });
331
+ }
332
+ }
356
333
  } else {
357
334
  if (target.state.firestore) {
358
335
  seedDocuments(
@@ -678,7 +655,12 @@ async function readState(
678
655
  return state[normalizedPath] ?? null;
679
656
  }
680
657
  if (operation.service === "rtdb") {
681
- return (await getRtdb(rtdbRef(runtime.rtdbAdmin!, operation.path))).val();
658
+ let admin = runtime.rtdbAdmin!;
659
+ if (operation.instance && operation.instance !== "default") {
660
+ const url = resolveDatabaseInstance(operation.instance)!.url;
661
+ admin = getAdminDatabase(runtime.sandbox, url);
662
+ }
663
+ return (await getRtdb(rtdbRef(admin, operation.path))).val();
682
664
  }
683
665
  return readStorageState(operation, runtime.storageAdmin!);
684
666
  }
@@ -740,7 +722,7 @@ async function executeOperation(
740
722
  operation.service === "firestore"
741
723
  ? await executeFirestore(operation, runtime.firestore!, runtime)
742
724
  : operation.service === "rtdb"
743
- ? await executeRtdb(operation, runtime.rtdb!)
725
+ ? await executeRtdb(operation, operation.instance && operation.instance !== "default" ? getDatabase(runtime.context, resolveDatabaseInstance(operation.instance)!.url) : runtime.rtdb!)
744
726
  : await executeStorage(operation, runtime.storage!);
745
727
  } catch (error) {
746
728
  const result = errorDecision(error);
@@ -64,11 +64,13 @@ export interface LocalFirebaseTarget {
64
64
  rules: {
65
65
  firestore?: string;
66
66
  rtdb?: { rules: Record<string, unknown> };
67
+ rtdbInstances?: Record<string, { rules: Record<string, unknown> }>;
67
68
  storage?: string;
68
69
  };
69
70
  state: {
70
71
  firestore?: Record<string, Record<string, unknown>>;
71
72
  rtdb?: unknown;
73
+ rtdbInstances?: Record<string, unknown>;
72
74
  storage?: StorageObjectFixture[];
73
75
  auth?: { users: AuthFixtureUser[] };
74
76
  };
@@ -125,6 +127,7 @@ export interface FirestoreOperation {
125
127
 
126
128
  export interface RtdbOperation {
127
129
  service: "rtdb";
130
+ instance?: string;
128
131
  method: "get" | "set" | "update" | "remove";
129
132
  path: string;
130
133
  data?: unknown;
@@ -13,13 +13,17 @@
13
13
  * `observations[0]`, `probes[2]`. A caller that validates one record on its
14
14
  * own passes nothing and gets the record's kind instead.
15
15
  */
16
+ import { resolveDatabaseInstance } from "pyric/sandbox/internal";
17
+ import { rtdbRulesSourceRejection } from "pyric/rules/internal/rtdb";
16
18
  import {
19
+ ASSURANCE_CAMPAIGN_SCHEMA,
17
20
  ASSURANCE_TARGET_SCHEMA,
18
21
  AssuranceInputError,
19
22
  type AssuranceActor,
20
23
  type AssuranceObservation,
21
24
  type AssuranceProbe,
22
25
  type FirebaseOperation,
26
+ type AuthorizationCampaignSpec,
23
27
  type LocalFirebaseTarget,
24
28
  type MutationDimension,
25
29
  type SecurityInvariant,
@@ -225,13 +229,56 @@ export function assertTarget(
225
229
  if (target.rules.rtdb !== undefined) {
226
230
  requiredRecord(target.rules.rtdb, `${where}.rules.rtdb`);
227
231
  requiredRecord(target.rules.rtdb.rules, `${where}.rules.rtdb.rules`);
232
+
233
+ }
234
+
235
+ const declaredRuleInstances = new Map<string, string>();
236
+ if (target.rules.rtdbInstances !== undefined) {
237
+ requiredRecord(target.rules.rtdbInstances, `${where}.rules.rtdbInstances`);
238
+ for (const [instance, rules] of Object.entries(target.rules.rtdbInstances)) {
239
+ const at = `${where}.rules.rtdbInstances[${JSON.stringify(instance)}]`;
240
+ requiredRecord(rules, at);
241
+ requiredRecord((rules as any).rules, `${at}.rules`);
242
+ try {
243
+ const resolvedName = resolveDatabaseInstance(instance)!.name;
244
+ if (declaredRuleInstances.has(resolvedName)) {
245
+ throw new AssuranceInputError(`${at} declares instance '${resolvedName}' which is already declared by key '${declaredRuleInstances.get(resolvedName)}'`);
246
+ }
247
+ declaredRuleInstances.set(resolvedName, instance);
248
+ } catch (e) {
249
+ if (e instanceof AssuranceInputError) throw e;
250
+ throw new AssuranceInputError(`${at} names a malformed RTDB instance: ${e instanceof Error ? e.message : String(e)}`);
251
+ }
252
+ }
228
253
  }
254
+
229
255
  if (target.state.firestore !== undefined) {
230
256
  requiredRecord(target.state.firestore, `${where}.state.firestore`);
231
257
  for (const [docPath, docData] of Object.entries(target.state.firestore)) {
232
258
  requiredRecord(docData, `${where}.state.firestore[${docPath}]`);
233
259
  }
234
260
  }
261
+ if (target.state.rtdbInstances !== undefined) {
262
+ requiredRecord(target.state.rtdbInstances, `${where}.state.rtdbInstances`);
263
+ const declaredStateInstances = new Map<string, string>();
264
+ for (const [instance] of Object.entries(target.state.rtdbInstances)) {
265
+ const at = `${where}.state.rtdbInstances[${JSON.stringify(instance)}]`;
266
+ try {
267
+ const resolvedName = resolveDatabaseInstance(instance)!.name;
268
+ if (!declaredRuleInstances.has(resolvedName)) {
269
+ throw new AssuranceInputError(`${at} provides state for instance '${resolvedName}' which has no declared rules`);
270
+ }
271
+ if (declaredStateInstances.has(resolvedName)) {
272
+ throw new AssuranceInputError(`${at} provides state for instance '${resolvedName}' which is already provided by key '${declaredStateInstances.get(resolvedName)}'`);
273
+ }
274
+ declaredStateInstances.set(resolvedName, instance);
275
+ } catch (e) {
276
+ if (e instanceof AssuranceInputError) throw e;
277
+ throw new AssuranceInputError(`${at} names a malformed RTDB instance: ${e instanceof Error ? e.message : String(e)}`);
278
+ }
279
+ }
280
+ }
281
+
235
282
  if (target.state.storage !== undefined) {
236
283
  if (!Array.isArray(target.state.storage)) {
237
284
  throw new AssuranceInputError(
@@ -367,6 +414,9 @@ function assertOperation(
367
414
  ): asserts operation is FirebaseOperation {
368
415
  requiredRecord(operation, where);
369
416
  enumValue(operation.service, OPERATION_SERVICES, where, "service");
417
+ if (operation.service === "rtdb" && (operation as any).instance !== undefined) {
418
+ requiredString((operation as any).instance, where, "instance");
419
+ }
370
420
  requiredString(operation.path, where, "path");
371
421
  enumValue(
372
422
  operation.method,
@@ -507,3 +557,90 @@ export function changedDimensions(
507
557
  }
508
558
  return changed;
509
559
  }
560
+
561
+
562
+ export function assertCampaign(spec: AuthorizationCampaignSpec): void {
563
+ if (spec.schema !== ASSURANCE_CAMPAIGN_SCHEMA) {
564
+ throw new AssuranceInputError(
565
+ `campaign schema must be '${ASSURANCE_CAMPAIGN_SCHEMA}'.`,
566
+ );
567
+ }
568
+ if (!spec.id.trim())
569
+ throw new AssuranceInputError("campaign id is required.");
570
+ assertTarget(spec.target);
571
+ if (!Array.isArray(spec.actors)) {
572
+ throw new AssuranceInputError("campaign actors must be an array.");
573
+ }
574
+ if (!Array.isArray(spec.invariants)) {
575
+ throw new AssuranceInputError("campaign invariants must be an array.");
576
+ }
577
+ if (!Array.isArray(spec.probes)) {
578
+ throw new AssuranceInputError("campaign probes must be an array.");
579
+ }
580
+ for (const actor of spec.actors) assertActor(actor);
581
+ for (const invariant of spec.invariants) assertInvariant(invariant);
582
+ for (const probe of spec.probes) assertProbe(probe);
583
+ uniqueIds(spec.actors, "actor");
584
+ uniqueIds(spec.invariants, "invariant");
585
+ uniqueIds(spec.probes, "probe");
586
+
587
+ const declaredRtdbInstances = spec.target.rules.rtdbInstances ? Object.keys(spec.target.rules.rtdbInstances).map(k => resolveDatabaseInstance(k)!.name) : [];
588
+ for (const probe of spec.probes) {
589
+ for (const op of [probe.control, probe.mutation.operation]) {
590
+ // test what happens to unknown fields (unrecognized keys)
591
+ const allowedKeys = new Set(["service", "method", "path", "data", "query", "dataBase64", "contentType", "customMetadata", "instance"]);
592
+ for (const key of Object.keys(op)) {
593
+ if (!allowedKeys.has(key)) {
594
+ throw new AssuranceInputError(`operation names unknown field '${key}'`);
595
+ }
596
+ }
597
+
598
+ if (op.service === "rtdb" && op.instance !== undefined && op.instance !== "default") {
599
+ let resolvedName;
600
+ try {
601
+ resolvedName = resolveDatabaseInstance(op.instance)!.name;
602
+ } catch (e) {
603
+ throw new AssuranceInputError(`invalid RTDB instance '${op.instance}': ${e instanceof Error ? e.message : String(e)}`);
604
+ }
605
+ if (!declaredRtdbInstances.includes(resolvedName)) {
606
+ throw new AssuranceInputError(`probe '${probe.id}' names undeclared RTDB instance '${op.instance}'. Declared instances: ${declaredRtdbInstances.length > 0 ? declaredRtdbInstances.join(", ") : "none"}.`);
607
+ }
608
+ }
609
+ }
610
+ }
611
+
612
+ for (const probe of spec.probes) {
613
+ const actor = spec.actors.find((item) => item.id === probe.actorId);
614
+ const invariant = spec.invariants.find(
615
+ (item) => item.id === probe.invariantId,
616
+ );
617
+ if (!actor)
618
+ throw new AssuranceInputError(
619
+ `probe '${probe.id}' references unknown actor '${probe.actorId}'.`,
620
+ );
621
+ if (!invariant) {
622
+ throw new AssuranceInputError(
623
+ `probe '${probe.id}' references unknown invariant '${probe.invariantId}'.`,
624
+ );
625
+ }
626
+ if (
627
+ invariant.service !== "cross-service" &&
628
+ invariant.service !== probe.control.service
629
+ ) {
630
+ throw new AssuranceInputError(
631
+ `probe '${probe.id}' service does not match invariant '${invariant.id}'.`,
632
+ );
633
+ }
634
+ }
635
+ }
636
+
637
+ function uniqueIds(items: ReadonlyArray<{ id: string }>, label: string): void {
638
+ const seen = new Set<string>();
639
+ for (const item of items) {
640
+ if (!item.id.trim())
641
+ throw new AssuranceInputError(`${label} id is required.`);
642
+ if (seen.has(item.id))
643
+ throw new AssuranceInputError(`duplicate ${label} id '${item.id}'.`);
644
+ seen.add(item.id);
645
+ }
646
+ }
@@ -82,6 +82,7 @@ export const OPERATION_METHOD_VALUES: readonly string[] = [
82
82
  export const firebaseOperation = z
83
83
  .object({
84
84
  service: z.enum(OPERATION_SERVICES).describe('The service the operation runs against.'),
85
+ instance: z.string().optional().describe('The named RTDB instance, if the service is rtdb.'),
85
86
  method: z
86
87
  .enum(OPERATION_METHOD_VALUES as [string, ...string[]])
87
88
  .describe('The request method, from the set the named service evaluates.'),
@@ -236,6 +236,6 @@ export async function runDatabaseRulesGenerate(
236
236
  await mkdirFn(dirname(resolvedOut), { recursive: true });
237
237
  await writeFileFn(resolvedOut, `${JSON.stringify(rulesJson, null, 2)}\n`, 'utf-8');
238
238
 
239
- out.write(`pyric database rules generate: wrote ${resolvedOut}\n`);
239
+ out.write(`pyric database rules generate: wrote ${resolvedOut}\n Test before deploy: pyric verify --service rtdb --rules rtdb=${outPath}\n`);
240
240
  return 0;
241
241
  }