@pyric/cli 0.1.0-alpha.11 → 0.1.0-alpha.12

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 (301) hide show
  1. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  2. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  3. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  4. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  5. package/dist/conformance/.generated/can-i-use.js +2 -2
  6. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  7. package/dist/serve/docs-ui/docs/_rhythm/index.html +1 -1
  8. package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +1 -1
  9. package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +1 -1
  10. package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +1 -1
  11. package/dist/serve/docs-ui/docs/ai-compat/index.html +1 -1
  12. package/dist/serve/docs-ui/docs/api-reference/index.html +2 -2
  13. package/dist/serve/docs-ui/docs/api-reference.md +1 -1
  14. package/dist/serve/docs-ui/docs/app-compat/index.html +1 -1
  15. package/dist/serve/docs-ui/docs/auth-compat/index.html +1 -1
  16. package/dist/serve/docs-ui/docs/build/ai-logic/index.html +1 -1
  17. package/dist/serve/docs-ui/docs/build/authentication/index.html +1 -1
  18. package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +1 -1
  19. package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +1 -1
  20. package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +1 -1
  21. package/dist/serve/docs-ui/docs/build/realtime-database/index.html +1 -1
  22. package/dist/serve/docs-ui/docs/conformance-scores/index.html +1 -1
  23. package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +1 -1
  24. package/dist/serve/docs-ui/docs/database-compat/index.html +1 -1
  25. package/dist/serve/docs-ui/docs/firestore-compat/index.html +1 -1
  26. package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +1 -1
  27. package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +1 -1
  28. package/dist/serve/docs-ui/docs/get-started/start-building/index.html +1 -1
  29. package/dist/serve/docs-ui/docs/index.json +200 -25
  30. package/dist/serve/docs-ui/docs/messaging-compat/index.html +1 -1
  31. package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +1 -1
  32. package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +1 -1
  33. package/dist/serve/docs-ui/docs/overview/index.html +1 -1
  34. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
  35. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
  36. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
  37. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
  38. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
  39. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
  40. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
  41. package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +1 -1
  42. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
  43. package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +1 -1
  44. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +1 -1
  45. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
  46. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
  47. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
  48. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
  49. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
  50. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
  51. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
  52. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
  53. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
  54. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
  55. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
  56. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +131 -66
  57. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +130 -65
  58. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
  59. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +1 -1
  60. package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +1 -1
  61. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1 -1
  62. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +1 -1
  63. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
  64. package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +1 -1
  65. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
  66. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
  67. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +37 -14
  68. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +28 -8
  69. package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +1 -1
  70. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
  71. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
  72. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
  73. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
  74. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
  75. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
  76. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
  77. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
  78. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
  79. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
  80. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
  81. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
  82. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
  83. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
  84. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
  85. package/dist/serve/docs-ui/docs/rules-compat/index.html +1 -1
  86. package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +1 -1
  87. package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +1 -1
  88. package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +1 -1
  89. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
  90. package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +1 -1
  91. package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +1 -1
  92. package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +1 -1
  93. package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  94. package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +1 -1
  95. package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +1 -1
  96. package/dist/serve/docs-ui/docs/storage-compat/index.html +1 -1
  97. package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
  98. package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
  99. package/dist/serve/docs-ui/llms.txt +1 -1
  100. package/dist/serve/worker/host/core.js +1 -1
  101. package/dist/serve/worker/host/core.js.map +1 -1
  102. package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
  103. package/dist/serve/worker/host/firestore-writes.js +3 -2
  104. package/dist/serve/worker/host/firestore-writes.js.map +1 -1
  105. package/dist/serve/worker/index.d.ts +2 -1
  106. package/dist/serve/worker/index.d.ts.map +1 -1
  107. package/dist/serve/worker/index.js +2 -1
  108. package/dist/serve/worker/index.js.map +1 -1
  109. package/dist/serve/worker/protocol.js +2 -2
  110. package/dist/serve/worker/protocol.js.map +1 -1
  111. package/package.json +5 -4
  112. package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
  113. package/src/assurance/attachment.ts +211 -0
  114. package/src/assurance/browser.ts +80 -0
  115. package/src/assurance/campaign.ts +496 -0
  116. package/src/assurance/capabilities.ts +446 -0
  117. package/src/assurance/cases.ts +43 -0
  118. package/src/assurance/index.ts +75 -0
  119. package/src/assurance/runner.ts +885 -0
  120. package/src/assurance/tool-names.ts +15 -0
  121. package/src/assurance/tools.ts +830 -0
  122. package/src/assurance/types.ts +352 -0
  123. package/src/assurance/validation.ts +434 -0
  124. package/src/bridge/client/bridge.ts +550 -0
  125. package/src/bridge/client/dispatch.ts +137 -0
  126. package/src/bridge/client.ts +45 -0
  127. package/src/bridge/protocol.ts +351 -0
  128. package/src/bridge/server/audit.ts +53 -0
  129. package/src/bridge/server/bridge.ts +597 -0
  130. package/src/bridge/server/headless.ts +160 -0
  131. package/src/bridge/server/json-schema-to-zod.ts +108 -0
  132. package/src/bridge/server/local-bridge.ts +66 -0
  133. package/src/bridge/server/logger.ts +50 -0
  134. package/src/bridge/server/mcp-contract.ts +93 -0
  135. package/src/bridge/server/mcp.ts +129 -0
  136. package/src/bridge/server/peer.ts +232 -0
  137. package/src/bridge/server/standalone.ts +347 -0
  138. package/src/bridge/server/tool-metadata.ts +99 -0
  139. package/src/bridge/server.ts +28 -0
  140. package/src/cli/can-i-use.ts +50 -0
  141. package/src/cli/cli.test.ts +493 -0
  142. package/src/cli/database-rules.ts +329 -0
  143. package/src/cli/dev-runner.ts +282 -0
  144. package/src/cli/firebase-json.ts +100 -0
  145. package/src/cli/firestore-indexes.ts +70 -0
  146. package/src/cli/index.ts +425 -0
  147. package/src/cli/init.ts +260 -0
  148. package/src/cli/mcp-proxy.ts +196 -0
  149. package/src/cli/parse-args.ts +80 -0
  150. package/src/cli/rules.ts +259 -0
  151. package/src/cli/scope.ts +67 -0
  152. package/src/cli/serve.ts +996 -0
  153. package/src/cli/service-commands.ts +73 -0
  154. package/src/cli/snapshot.ts +164 -0
  155. package/src/cli/storage-rules.ts +172 -0
  156. package/src/cli/verify.ts +416 -0
  157. package/src/conformance/.generated/can-i-use-browser.ts +113 -0
  158. package/src/conformance/.generated/can-i-use.ts +117 -0
  159. package/src/conformance/.generated/conformance-docs.ts +14 -0
  160. package/src/conformance/browser.ts +24 -0
  161. package/src/conformance/can-i-use-tool.ts +51 -0
  162. package/src/conformance/can-i-use.ts +18 -0
  163. package/src/conformance/docs.ts +8 -0
  164. package/src/conformance/index.ts +20 -0
  165. package/src/conformance/tools.ts +16 -0
  166. package/src/credentials/core/memoize-ttl.ts +142 -0
  167. package/src/credentials/core/types.ts +10 -0
  168. package/src/credentials/node/from-adc.ts +96 -0
  169. package/src/credentials/node/from-service-account.ts +133 -0
  170. package/src/credentials/node/index.ts +6 -0
  171. package/src/discover/concurrency.ts +127 -0
  172. package/src/discover/crawler-adapter.ts +142 -0
  173. package/src/discover/crawler.ts +1127 -0
  174. package/src/discover/credential-free.ts +27 -0
  175. package/src/discover/findCollectionGroup.ts +131 -0
  176. package/src/discover/firestore-source.ts +59 -0
  177. package/src/discover/index.ts +5 -0
  178. package/src/discover/merge.ts +523 -0
  179. package/src/discover/session.ts +402 -0
  180. package/src/discover/tools.ts +201 -0
  181. package/src/discover/types.ts +187 -0
  182. package/src/discover/wire.ts +324 -0
  183. package/src/functions-rtdb/child.ts +382 -0
  184. package/src/functions-rtdb/delivery.ts +7 -0
  185. package/src/functions-rtdb/discovery.ts +136 -0
  186. package/src/functions-rtdb/event.ts +64 -0
  187. package/src/functions-rtdb/execution.ts +111 -0
  188. package/src/functions-rtdb/in-memory-delivery.ts +40 -0
  189. package/src/functions-rtdb/project.ts +74 -0
  190. package/src/functions-rtdb/projection.ts +104 -0
  191. package/src/functions-rtdb/reference-pattern.ts +20 -0
  192. package/src/functions-rtdb/remote-delivery.ts +23 -0
  193. package/src/pkg-version.ts +49 -0
  194. package/src/register/esm-exports.ts +63 -0
  195. package/src/register/hooks.ts +30 -0
  196. package/src/register/index.ts +150 -0
  197. package/src/register/mapping.ts +33 -0
  198. package/src/remote/index.ts +1050 -0
  199. package/src/rtdb/crawl-snapshot.ts +118 -0
  200. package/src/rtdb/inspection.ts +137 -0
  201. package/src/rtdb/load-rules-document.ts +56 -0
  202. package/src/rtdb/rules-generation-tool.ts +38 -0
  203. package/src/rtdb/rules-json.ts +29 -0
  204. package/src/serve/activity-guard.ts +29 -0
  205. package/src/serve/activity-route.ts +164 -0
  206. package/src/serve/activity-warning.ts +26 -0
  207. package/src/serve/bridge-mount.ts +200 -0
  208. package/src/serve/bundler.ts +493 -0
  209. package/src/serve/capture-store.ts +65 -0
  210. package/src/serve/discovery.ts +199 -0
  211. package/src/serve/entries/ai.ts +292 -0
  212. package/src/serve/entries/app-backend.ts +4 -0
  213. package/src/serve/entries/app-client.ts +26 -0
  214. package/src/serve/entries/app-session-store.ts +47 -0
  215. package/src/serve/entries/app.ts +11 -0
  216. package/src/serve/entries/auth-helper-core.ts +181 -0
  217. package/src/serve/entries/auth-helper-dom.ts +134 -0
  218. package/src/serve/entries/auth-helper-runtime.ts +20 -0
  219. package/src/serve/entries/auth.ts +255 -0
  220. package/src/serve/entries/bridge-url.ts +31 -0
  221. package/src/serve/entries/database.ts +129 -0
  222. package/src/serve/entries/firestore.ts +232 -0
  223. package/src/serve/entries/init.ts +46 -0
  224. package/src/serve/entries/keepalive.ts +46 -0
  225. package/src/serve/entries/messaging-sw.ts +75 -0
  226. package/src/serve/entries/messaging.ts +96 -0
  227. package/src/serve/entries/runtime.ts +540 -0
  228. package/src/serve/entries/session-store.ts +97 -0
  229. package/src/serve/entries/storage.ts +92 -0
  230. package/src/serve/entries/tab-sync-wiring.ts +274 -0
  231. package/src/serve/entries/worker-runtime.ts +75 -0
  232. package/src/serve/init-payload.ts +42 -0
  233. package/src/serve/namespace.ts +675 -0
  234. package/src/serve/open-browser.ts +68 -0
  235. package/src/serve/rules.ts +272 -0
  236. package/src/serve/sandbox-marker.ts +31 -0
  237. package/src/serve/server.ts +428 -0
  238. package/src/serve/standalone-assets.ts +180 -0
  239. package/src/serve/state-store.ts +166 -0
  240. package/src/serve/studio/disk-project-store.ts +185 -0
  241. package/src/serve/studio/disk-workspace.ts +162 -0
  242. package/src/serve/studio/index.ts +20 -0
  243. package/src/serve/studio/routes.ts +237 -0
  244. package/src/serve/studio/store-types.ts +48 -0
  245. package/src/serve/studio/studio-storage.test.ts +289 -0
  246. package/src/serve/vite-plugin.ts +1102 -0
  247. package/src/serve/worker/activity-bootstrap.ts +24 -0
  248. package/src/serve/worker/client/admin-firestore.ts +38 -0
  249. package/src/serve/worker/client/ai.ts +124 -0
  250. package/src/serve/worker/client/auth.ts +489 -0
  251. package/src/serve/worker/client/connection.ts +202 -0
  252. package/src/serve/worker/client/core.ts +350 -0
  253. package/src/serve/worker/client/disconnect.ts +45 -0
  254. package/src/serve/worker/client/firestore-reads.ts +181 -0
  255. package/src/serve/worker/client/firestore-refs.ts +226 -0
  256. package/src/serve/worker/client/firestore-writes.ts +232 -0
  257. package/src/serve/worker/client/handles.ts +91 -0
  258. package/src/serve/worker/client/messaging.ts +119 -0
  259. package/src/serve/worker/client/presence.ts +174 -0
  260. package/src/serve/worker/client/rtdb.ts +311 -0
  261. package/src/serve/worker/client/rules.ts +63 -0
  262. package/src/serve/worker/client/service-worker-connection.ts +59 -0
  263. package/src/serve/worker/client/snapshots.ts +86 -0
  264. package/src/serve/worker/client/storage.ts +195 -0
  265. package/src/serve/worker/client/studio.ts +88 -0
  266. package/src/serve/worker/client.ts +57 -0
  267. package/src/serve/worker/durable-persistence.ts +137 -0
  268. package/src/serve/worker/entry.ts +179 -0
  269. package/src/serve/worker/host/admin-firestore.ts +88 -0
  270. package/src/serve/worker/host/connection.ts +179 -0
  271. package/src/serve/worker/host/core.ts +387 -0
  272. package/src/serve/worker/host/dispatch.ts +316 -0
  273. package/src/serve/worker/host/firestore-reads.ts +117 -0
  274. package/src/serve/worker/host/firestore-writes.ts +456 -0
  275. package/src/serve/worker/host/presence.ts +312 -0
  276. package/src/serve/worker/host/rtdb.ts +136 -0
  277. package/src/serve/worker/host/rules.ts +128 -0
  278. package/src/serve/worker/host/storage.ts +312 -0
  279. package/src/serve/worker/host/studio.ts +75 -0
  280. package/src/serve/worker/host/subscriptions.ts +238 -0
  281. package/src/serve/worker/host-ai.ts +165 -0
  282. package/src/serve/worker/host-auth.ts +469 -0
  283. package/src/serve/worker/host-context.ts +273 -0
  284. package/src/serve/worker/host-events.ts +94 -0
  285. package/src/serve/worker/host-messaging.ts +240 -0
  286. package/src/serve/worker/host.ts +47 -0
  287. package/src/serve/worker/index.ts +196 -0
  288. package/src/serve/worker/presence-timing.ts +15 -0
  289. package/src/serve/worker/protocol.ts +1112 -0
  290. package/src/serve/worker/serve-init.ts +606 -0
  291. package/src/serve/worker/service-worker-channel.ts +33 -0
  292. package/src/serve/worker/service-worker-relay.ts +86 -0
  293. package/src/serve/writer-lock.ts +53 -0
  294. package/src/verify/cases.ts +233 -0
  295. package/src/verify/fixture.ts +258 -0
  296. package/src/verify/index.ts +519 -0
  297. package/src/verify/tools.ts +108 -0
  298. package/src/version/compat-target.ts +17 -0
  299. package/src/vite.ts +17 -0
  300. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -31
  301. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -29
@@ -0,0 +1,259 @@
1
+ /**
2
+ * `pyric firestore rules *` subcommands — thin wrappers over `pyric/rules`.
3
+ *
4
+ * - `firestore rules lint <path>` runs the AST linter, prints findings JSON.
5
+ * - `firestore rules validate <path>` runs the structural validator, prints
6
+ * findings JSON.
7
+ * - `firestore rules simulate` runs the local simulator. With no flags it
8
+ * reads `firebase.json` for the rules path and runs a single
9
+ * allow-anonymous sanity test. With `--stdin` it reads a JSON
10
+ * request `{ source?, testCases }` from stdin instead — the way
11
+ * CI pipes in a fully scripted simulation.
12
+ *
13
+ * All three exit 0 on success, 1 on usage / file-read error, 2 on
14
+ * library failure (parse error etc.).
15
+ */
16
+
17
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
18
+ import { dirname, resolve as resolvePath } from 'node:path';
19
+ import {
20
+ lintFirestoreRules,
21
+ validateFirestoreRules,
22
+ parseToAST,
23
+ SimulateFirestoreRulesHandler,
24
+ type TestCase,
25
+ type LintResult,
26
+ type ValidationFinding,
27
+ type TestFirestoreRulesResult,
28
+ } from 'pyric/rules/internal';
29
+ import { resolveModules } from 'pyric/rules/internal/node';
30
+ import type { ParsedArgs } from './parse-args.js';
31
+ import { readFirebaseJson, type FirebaseJson } from './firebase-json.js';
32
+
33
+ export interface RulesDeps {
34
+ readFile?: typeof readFile;
35
+ readFirebaseJson?: (cwd: string) => Promise<FirebaseJson>;
36
+ lintFirestoreRules?: typeof lintFirestoreRules;
37
+ validateFirestoreRules?: typeof validateFirestoreRules;
38
+ simulate?: (source: string, cases: TestCase[]) => TestFirestoreRulesResult;
39
+ /** Override cwd — used in tests. */
40
+ cwd?: string;
41
+ /** Override stdin reader — used in tests. */
42
+ readStdin?: () => Promise<string>;
43
+ stdout?: { write(s: string): void };
44
+ stderr?: { write(s: string): void };
45
+ }
46
+
47
+ export interface ResolveRulesDeps {
48
+ readFile?: typeof readFile;
49
+ writeFile?: typeof writeFile;
50
+ mkdir?: typeof mkdir;
51
+ resolveModules?: typeof resolveModules;
52
+ cwd?: string;
53
+ stdout?: { write(s: string): void };
54
+ stderr?: { write(s: string): void };
55
+ }
56
+
57
+ function defaultReadStdin(): Promise<string> {
58
+ return new Promise((resolve, reject) => {
59
+ let data = '';
60
+ process.stdin.setEncoding('utf-8');
61
+ process.stdin.on('data', (chunk) => {
62
+ data += chunk;
63
+ });
64
+ process.stdin.on('end', () => resolve(data));
65
+ process.stdin.on('error', reject);
66
+ });
67
+ }
68
+
69
+ export async function runRulesLint(parsed: ParsedArgs, deps: RulesDeps = {}): Promise<number> {
70
+ const out = deps.stdout ?? process.stdout;
71
+ const err = deps.stderr ?? process.stderr;
72
+ const cwd = deps.cwd ?? process.cwd();
73
+ const readFileFn = deps.readFile ?? readFile;
74
+ const lintFn = deps.lintFirestoreRules ?? lintFirestoreRules;
75
+
76
+ const path = parsed.positional[0];
77
+ if (!path) {
78
+ err.write('pyric firestore rules lint: missing rules-file path. Usage: pyric firestore rules lint <path>\n');
79
+ return 1;
80
+ }
81
+ let source: string;
82
+ try {
83
+ source = await readFileFn(resolvePath(cwd, path), 'utf-8');
84
+ } catch (e) {
85
+ err.write(`pyric firestore rules lint: ${e instanceof Error ? e.message : String(e)}\n`);
86
+ return 1;
87
+ }
88
+ const result: LintResult = lintFn(source);
89
+ out.write(`${JSON.stringify(result, null, 2)}\n`);
90
+ // Non-zero only when the linter signals a hard parse error. Warnings
91
+ // are informational — exit 0 keeps `pyric firestore rules lint` chainable in
92
+ // build pipelines without `|| true`.
93
+ return result.parseError ? 2 : 0;
94
+ }
95
+
96
+ export async function runRulesValidate(parsed: ParsedArgs, deps: RulesDeps = {}): Promise<number> {
97
+ const out = deps.stdout ?? process.stdout;
98
+ const err = deps.stderr ?? process.stderr;
99
+ const cwd = deps.cwd ?? process.cwd();
100
+ const readFileFn = deps.readFile ?? readFile;
101
+ const validateFn = deps.validateFirestoreRules ?? validateFirestoreRules;
102
+
103
+ const path = parsed.positional[0];
104
+ if (!path) {
105
+ err.write('pyric firestore rules validate: missing rules-file path. Usage: pyric firestore rules validate <path>\n');
106
+ return 1;
107
+ }
108
+ let source: string;
109
+ try {
110
+ source = await readFileFn(resolvePath(cwd, path), 'utf-8');
111
+ } catch (e) {
112
+ err.write(`pyric firestore rules validate: ${e instanceof Error ? e.message : String(e)}\n`);
113
+ return 1;
114
+ }
115
+ const ast = parseToAST(source);
116
+ if (!ast) {
117
+ err.write('pyric firestore rules validate: failed to parse rules source.\n');
118
+ return 2;
119
+ }
120
+ const findings: ValidationFinding[] = validateFn(ast);
121
+ out.write(`${JSON.stringify(findings, null, 2)}\n`);
122
+ return 0;
123
+ }
124
+
125
+ export async function runRulesResolve(
126
+ parsed: ParsedArgs,
127
+ deps: ResolveRulesDeps = {},
128
+ ): Promise<number> {
129
+ const out = deps.stdout ?? process.stdout;
130
+ const err = deps.stderr ?? process.stderr;
131
+ const cwd = deps.cwd ?? process.cwd();
132
+ const sourcePath = parsed.positional[0];
133
+ if (!sourcePath) {
134
+ err.write(
135
+ 'pyric firestore rules resolve: missing rules-file path. Usage: pyric firestore rules resolve <path> [--out <path>]\n',
136
+ );
137
+ return 1;
138
+ }
139
+
140
+ const absoluteSourcePath = resolvePath(cwd, sourcePath);
141
+ let source: string;
142
+ try {
143
+ source = await (deps.readFile ?? readFile)(absoluteSourcePath, 'utf-8');
144
+ } catch (error) {
145
+ err.write(
146
+ `pyric firestore rules resolve: ${error instanceof Error ? error.message : String(error)}\n`,
147
+ );
148
+ return 1;
149
+ }
150
+
151
+ const result = (deps.resolveModules ?? resolveModules)(source, {
152
+ basePath: dirname(absoluteSourcePath),
153
+ });
154
+ if (!result.success) {
155
+ err.write(`pyric firestore rules resolve: ${result.error.message}\n`);
156
+ return 2;
157
+ }
158
+
159
+ const outFlag = parsed.flags.get('out');
160
+ if (typeof outFlag !== 'string') {
161
+ out.write(result.data.resolved.endsWith('\n') ? result.data.resolved : `${result.data.resolved}\n`);
162
+ return 0;
163
+ }
164
+
165
+ const outputPath = resolvePath(cwd, outFlag);
166
+ await (deps.mkdir ?? mkdir)(dirname(outputPath), { recursive: true });
167
+ await (deps.writeFile ?? writeFile)(
168
+ outputPath,
169
+ result.data.resolved.endsWith('\n') ? result.data.resolved : `${result.data.resolved}\n`,
170
+ 'utf-8',
171
+ );
172
+ out.write(`pyric firestore rules resolve: wrote ${outputPath}\n`);
173
+ return 0;
174
+ }
175
+
176
+ export async function runRulesSimulate(
177
+ parsed: ParsedArgs,
178
+ deps: RulesDeps = {},
179
+ ): Promise<number> {
180
+ const out = deps.stdout ?? process.stdout;
181
+ const err = deps.stderr ?? process.stderr;
182
+ const cwd = deps.cwd ?? process.cwd();
183
+ const readFileFn = deps.readFile ?? readFile;
184
+ const fjRead = deps.readFirebaseJson ?? readFirebaseJson;
185
+ const simulateFn =
186
+ deps.simulate ??
187
+ ((source: string, cases: TestCase[]) =>
188
+ new SimulateFirestoreRulesHandler().simulate(source, cases));
189
+
190
+ let source: string;
191
+ let testCases: TestCase[];
192
+
193
+ if (parsed.flags.get('stdin') === true) {
194
+ const readStdinFn = deps.readStdin ?? defaultReadStdin;
195
+ const raw = await readStdinFn();
196
+ let payload: { source?: string; testCases?: TestCase[] };
197
+ try {
198
+ payload = JSON.parse(raw) as { source?: string; testCases?: TestCase[] };
199
+ } catch (e) {
200
+ err.write(
201
+ `pyric firestore rules simulate: failed to parse stdin JSON: ${e instanceof Error ? e.message : String(e)}\n`,
202
+ );
203
+ return 1;
204
+ }
205
+ if (!payload.testCases || !Array.isArray(payload.testCases)) {
206
+ err.write('pyric firestore rules simulate: stdin payload must include `testCases: TestCase[]`.\n');
207
+ return 1;
208
+ }
209
+ testCases = payload.testCases;
210
+ if (payload.source) {
211
+ source = payload.source;
212
+ } else {
213
+ const firebaseJson = await fjRead(cwd);
214
+ const rulesPath = firebaseJson.firestore?.rules;
215
+ if (!rulesPath) {
216
+ err.write(
217
+ 'pyric firestore rules simulate: stdin payload omitted `source` and firebase.json has no firestore.rules path.\n',
218
+ );
219
+ return 1;
220
+ }
221
+ source = await readFileFn(resolvePath(cwd, rulesPath), 'utf-8');
222
+ }
223
+ } else {
224
+ let firebaseJson: FirebaseJson;
225
+ try {
226
+ firebaseJson = await fjRead(cwd);
227
+ } catch (e) {
228
+ err.write(`${e instanceof Error ? e.message : String(e)}\n`);
229
+ return 1;
230
+ }
231
+ const rulesPath = firebaseJson.firestore?.rules;
232
+ if (!rulesPath) {
233
+ err.write('pyric firestore rules simulate: firebase.json has no `firestore.rules` path.\n');
234
+ return 1;
235
+ }
236
+ try {
237
+ source = await readFileFn(resolvePath(cwd, rulesPath), 'utf-8');
238
+ } catch (e) {
239
+ err.write(`pyric firestore rules simulate: ${e instanceof Error ? e.message : String(e)}\n`);
240
+ return 1;
241
+ }
242
+ // Default sample test — anonymous read on /sample/x. Useful as a
243
+ // smoke-test that proves the rules file at least parses + an
244
+ // anonymous reader is correctly denied/allowed.
245
+ testCases = [
246
+ {
247
+ description: 'sample anonymous read',
248
+ expectation: 'DENY',
249
+ method: 'get',
250
+ path: 'sample/x',
251
+ auth: null,
252
+ },
253
+ ];
254
+ }
255
+
256
+ const result = simulateFn(source, testCases);
257
+ out.write(`${JSON.stringify(result, null, 2)}\n`);
258
+ return result.success ? 0 : 2;
259
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * `resolveScope` — build a `ProjectScope` for hosted Rules Test API verification.
3
+ *
4
+ * Credential sources, in precedence order:
5
+ * 1. `FIREBASE_SA_BASE64` — base64 service-account JSON (CI). Grants all scopes.
6
+ * 2. `GOOGLE_APPLICATION_CREDENTIALS` — path to a service-account JSON file.
7
+ * 3. ADC (`gcloud auth application-default login` / workload identity) — the
8
+ * ambient keyless fallback.
9
+ *
10
+ * Service accounts win so CI is deterministic. ADC user credentials are not
11
+ * bound to a project, so they need `--project` / `.firebaserc`.
12
+ */
13
+ import { fromAdc } from '../credentials/node/from-adc.js';
14
+ import { fromServiceAccount } from '../credentials/node/from-service-account.js';
15
+ import type { ProjectScope } from '../credentials/core/types.js';
16
+
17
+ export interface ResolvedScope {
18
+ scope: ProjectScope;
19
+ source: 'FIREBASE_SA_BASE64' | 'GOOGLE_APPLICATION_CREDENTIALS' | 'adc';
20
+ }
21
+
22
+ export interface ResolveScopeOptions {
23
+ projectId?: string | undefined;
24
+ env?: NodeJS.ProcessEnv;
25
+ adc?: (projectId: string, env: NodeJS.ProcessEnv) => Promise<ProjectScope | null>;
26
+ }
27
+
28
+ export async function resolveScope(options: ResolveScopeOptions = {}): Promise<ResolvedScope> {
29
+ const env = options.env ?? process.env;
30
+
31
+ // 1 + 2. Service-account env (CI) — highest precedence; grants all scopes.
32
+ const saBase64 = env.FIREBASE_SA_BASE64;
33
+ if (saBase64 && saBase64.trim() !== '') {
34
+ return withProject(await fromServiceAccount(`base64:${saBase64}`), 'FIREBASE_SA_BASE64', options, env);
35
+ }
36
+ const gac = env.GOOGLE_APPLICATION_CREDENTIALS;
37
+ if (gac && gac.trim() !== '') {
38
+ return withProject(await fromServiceAccount(gac), 'GOOGLE_APPLICATION_CREDENTIALS', options, env);
39
+ }
40
+
41
+ // ADC user credentials are not bound to a project.
42
+ const projectId = options.projectId ?? env.PYRIC_PROJECT;
43
+ if (projectId) {
44
+ const adcScope = await (options.adc ?? fromAdc)(projectId, env);
45
+ if (adcScope) return { scope: adcScope, source: 'adc' };
46
+ }
47
+
48
+ throw new Error(
49
+ 'pyric: Rules Test API verification requires FIREBASE_SA_BASE64, ' +
50
+ 'GOOGLE_APPLICATION_CREDENTIALS, or Application Default Credentials from ' +
51
+ '`gcloud auth application-default login`.',
52
+ );
53
+ }
54
+
55
+ function withProject(
56
+ baseScope: ProjectScope,
57
+ source: ResolvedScope['source'],
58
+ options: ResolveScopeOptions,
59
+ env: NodeJS.ProcessEnv,
60
+ ): ResolvedScope {
61
+ const overrideProjectId = options.projectId ?? env.PYRIC_PROJECT;
62
+ const scope =
63
+ overrideProjectId && overrideProjectId !== baseScope.projectId
64
+ ? Object.freeze({ projectId: overrideProjectId, resolveToken: baseScope.resolveToken })
65
+ : baseScope;
66
+ return { scope, source };
67
+ }