@pyric/cli 0.1.0-alpha.10 → 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.
- package/README.md.orig +2 -2
- package/dist/assurance/.generated/conformance-verdicts.d.ts +16 -0
- package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
- package/dist/assurance/.generated/conformance-verdicts.js +16 -0
- package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
- package/dist/bridge/server/peer.d.ts +1 -1
- package/dist/bridge/server.d.ts +1 -1
- package/dist/bridge/server.js +1 -1
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +13 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +3 -3
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +5 -5
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +8 -2
- package/dist/cli/serve.js.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
- package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use.js +2 -2
- package/dist/conformance/.generated/can-i-use.js.map +1 -1
- package/dist/conformance/.generated/conformance-docs.d.ts +12 -0
- package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -0
- package/dist/conformance/.generated/conformance-docs.js +2 -0
- package/dist/conformance/.generated/conformance-docs.js.map +1 -0
- package/dist/conformance/docs.d.ts +6 -0
- package/dist/conformance/docs.d.ts.map +1 -0
- package/dist/conformance/docs.js +6 -0
- package/dist/conformance/docs.js.map +1 -0
- package/dist/remote/index.d.ts.map +1 -1
- package/dist/remote/index.js +1 -0
- package/dist/remote/index.js.map +1 -1
- package/dist/serve/activity-guard.d.ts +4 -0
- package/dist/serve/activity-guard.d.ts.map +1 -0
- package/dist/serve/activity-guard.js +22 -0
- package/dist/serve/activity-guard.js.map +1 -0
- package/dist/serve/activity-route.d.ts +4 -0
- package/dist/serve/activity-route.d.ts.map +1 -0
- package/dist/serve/activity-route.js +157 -0
- package/dist/serve/activity-route.js.map +1 -0
- package/dist/serve/activity-warning.d.ts +4 -0
- package/dist/serve/activity-warning.d.ts.map +1 -0
- package/dist/serve/activity-warning.js +21 -0
- package/dist/serve/activity-warning.js.map +1 -0
- package/dist/serve/bridge-mount.d.ts +5 -0
- package/dist/serve/bridge-mount.d.ts.map +1 -1
- package/dist/serve/bridge-mount.js +1 -0
- package/dist/serve/bridge-mount.js.map +1 -1
- package/dist/serve/docs-ui/404.html +3 -0
- package/dist/serve/docs-ui/_astro/{_slug_.DPUwShm2.css → _slug_.CXh9yTP1.css} +1 -1
- package/dist/serve/docs-ui/_astro/_slug_.DUci-duz.css +1 -0
- package/dist/serve/docs-ui/docs/_rhythm/index.html +2 -1
- package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +3 -2
- package/dist/serve/docs-ui/docs/agent/set-up-your-agent.md +1 -1
- package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +2 -1
- package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +2 -1
- package/dist/serve/docs-ui/docs/ai-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/api-reference/index.html +51 -49
- package/dist/serve/docs-ui/docs/api-reference.md +3 -3
- package/dist/serve/docs-ui/docs/app-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/auth-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/build/ai-logic/index.html +18 -3
- package/dist/serve/docs-ui/docs/build/ai-logic.md +30 -2
- package/dist/serve/docs-ui/docs/build/authentication/index.html +2 -1
- package/dist/serve/docs-ui/docs/build/authentication.md +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +2 -1
- package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +46 -2
- package/dist/serve/docs-ui/docs/build/cloud-messaging.md +56 -1
- package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +2 -1
- package/dist/serve/docs-ui/docs/build/cloud-storage.md +1 -1
- package/dist/serve/docs-ui/docs/build/realtime-database/index.html +21 -3
- package/dist/serve/docs-ui/docs/build/realtime-database.md +25 -1
- package/dist/serve/docs-ui/docs/conformance-scores/index.html +2 -1
- package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +61 -28
- package/dist/serve/docs-ui/docs/create-pyric-reference-api.md +65 -20
- package/dist/serve/docs-ui/docs/database-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/firestore-compat/index.html +6 -5
- package/dist/serve/docs-ui/docs/firestore-compat.md +4 -4
- package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
- package/dist/serve/docs-ui/docs/get-started/start-building/index.html +4 -3
- package/dist/serve/docs-ui/docs/get-started/start-building.md +2 -2
- package/dist/serve/docs-ui/docs/index.json +979 -579
- package/dist/serve/docs-ui/docs/messaging-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +2 -1
- package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +2 -1
- package/dist/serve/docs-ui/docs/overview/index.html +2 -1
- package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +2503 -12
- package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api.md +2219 -8
- package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
- package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
- package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +4 -3
- package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api.md +2 -3
- package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +16 -11
- package/dist/serve/docs-ui/docs/pyric-ai-reference-api.md +14 -13
- package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +4 -3
- package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api.md +2 -3
- package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +7 -2
- package/dist/serve/docs-ui/docs/pyric-app-reference-api.md +5 -2
- package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +23 -12
- package/dist/serve/docs-ui/docs/pyric-auth-reference-api.md +15 -12
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +10 -48
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api.md +11 -24
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +87 -97
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api.md +53 -65
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +14 -15
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api.md +19 -28
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +4 -3
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api.md +2 -3
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +51 -0
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api.md +36 -0
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1067 -83
- package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api.md +1291 -160
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +253 -157
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +179 -85
- package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +72 -7
- package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api.md +22 -7
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +33 -12
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +15 -12
- package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +18 -17
- package/dist/serve/docs-ui/docs/pyric-database-reference-api.md +19 -22
- package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1512 -545
- package/dist/serve/docs-ui/docs/pyric-firestore-reference-api.md +1311 -316
- package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-messaging-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +69 -369
- package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api.md +103 -216
- package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +10 -9
- package/dist/serve/docs-ui/docs/pyric-rules-reference-api.md +9 -10
- package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +8 -7
- package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api.md +6 -7
- package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +10 -9
- package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api.md +9 -10
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +371 -884
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +369 -883
- package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +25 -17
- package/dist/serve/docs-ui/docs/pyric-storage-reference-api.md +21 -19
- package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +63 -776
- package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api.md +69 -510
- package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +22 -21
- package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api.md +29 -30
- package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +15 -168
- package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api.md +17 -87
- package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +6 -5
- package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api.md +5 -6
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +82 -970
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api.md +104 -479
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +30 -29
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api.md +54 -55
- package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +9 -124
- package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api.md +7 -58
- package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +24 -23
- package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api.md +31 -34
- package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +104 -1385
- package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api.md +133 -817
- package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +34 -33
- package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api.md +37 -38
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +116 -1169
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api.md +142 -590
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +4 -3
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/rules-compat/index.html +34 -1
- package/dist/serve/docs-ui/docs/rules-compat.md +32 -0
- package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +19 -2
- package/dist/serve/docs-ui/docs/secure/audit-your-rules.md +32 -0
- package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript.md +1 -1
- package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +2 -1
- package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +2 -1
- package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +2 -1
- package/dist/serve/docs-ui/docs/storage-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +2 -1
- package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +2 -1
- package/dist/serve/docs-ui/llms.txt +2 -1
- package/dist/serve/entries/ai.d.ts.map +1 -1
- package/dist/serve/entries/ai.js +39 -1
- package/dist/serve/entries/ai.js.map +1 -1
- package/dist/serve/entries/messaging-sw.d.ts.map +1 -1
- package/dist/serve/entries/messaging-sw.js +5 -1
- package/dist/serve/entries/messaging-sw.js.map +1 -1
- package/dist/serve/entries/messaging.d.ts.map +1 -1
- package/dist/serve/entries/messaging.js +7 -1
- package/dist/serve/entries/messaging.js.map +1 -1
- package/dist/serve/entries/runtime.d.ts.map +1 -1
- package/dist/serve/entries/runtime.js +20 -1
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/init-payload.d.ts +25 -0
- package/dist/serve/init-payload.d.ts.map +1 -1
- package/dist/serve/namespace.d.ts +39 -0
- package/dist/serve/namespace.d.ts.map +1 -1
- package/dist/serve/namespace.js +80 -1
- package/dist/serve/namespace.js.map +1 -1
- package/dist/serve/standalone-assets.d.ts +11 -9
- package/dist/serve/standalone-assets.d.ts.map +1 -1
- package/dist/serve/standalone-assets.js +6 -5
- package/dist/serve/standalone-assets.js.map +1 -1
- package/dist/serve/studio-ui/assets/{RulesCodeEditor-CXdSLKhO.js → RulesCodeEditor-CxVYcZXw.js} +1 -1
- package/dist/serve/studio-ui/assets/index-cpSfgQ7H.js +1218 -0
- package/dist/serve/studio-ui/assets/seed-DeuFKi4J.js +20 -0
- package/dist/serve/studio-ui/index.html +1 -1
- package/dist/serve/vite-plugin.d.ts +92 -15
- package/dist/serve/vite-plugin.d.ts.map +1 -1
- package/dist/serve/vite-plugin.js +281 -11
- package/dist/serve/vite-plugin.js.map +1 -1
- package/dist/serve/worker/activity-bootstrap.d.ts +8 -0
- package/dist/serve/worker/activity-bootstrap.d.ts.map +1 -0
- package/dist/serve/worker/activity-bootstrap.js +11 -0
- package/dist/serve/worker/activity-bootstrap.js.map +1 -0
- package/dist/serve/worker/client/connection.d.ts.map +1 -1
- package/dist/serve/worker/client/connection.js +17 -5
- package/dist/serve/worker/client/connection.js.map +1 -1
- package/dist/serve/worker/client/core.d.ts +2 -2
- package/dist/serve/worker/client/core.d.ts.map +1 -1
- package/dist/serve/worker/client/core.js +33 -5
- package/dist/serve/worker/client/core.js.map +1 -1
- package/dist/serve/worker/client/firestore-writes.d.ts.map +1 -1
- package/dist/serve/worker/client/firestore-writes.js +1 -0
- package/dist/serve/worker/client/firestore-writes.js.map +1 -1
- package/dist/serve/worker/client/messaging.d.ts +9 -1
- package/dist/serve/worker/client/messaging.d.ts.map +1 -1
- package/dist/serve/worker/client/messaging.js +15 -0
- package/dist/serve/worker/client/messaging.js.map +1 -1
- package/dist/serve/worker/client/studio.d.ts +10 -0
- package/dist/serve/worker/client/studio.d.ts.map +1 -1
- package/dist/serve/worker/client/studio.js +11 -0
- package/dist/serve/worker/client/studio.js.map +1 -1
- package/dist/serve/worker/durable-persistence.d.ts +12 -0
- package/dist/serve/worker/durable-persistence.d.ts.map +1 -0
- package/dist/serve/worker/durable-persistence.js +118 -0
- package/dist/serve/worker/durable-persistence.js.map +1 -0
- package/dist/serve/worker/entry.d.ts +7 -3
- package/dist/serve/worker/entry.d.ts.map +1 -1
- package/dist/serve/worker/entry.js +7 -3
- package/dist/serve/worker/entry.js.map +1 -1
- package/dist/serve/worker/host/core.d.ts +9 -5
- package/dist/serve/worker/host/core.d.ts.map +1 -1
- package/dist/serve/worker/host/core.js +34 -9
- package/dist/serve/worker/host/core.js.map +1 -1
- package/dist/serve/worker/host/dispatch.d.ts.map +1 -1
- package/dist/serve/worker/host/dispatch.js +9 -2
- package/dist/serve/worker/host/dispatch.js.map +1 -1
- package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
- package/dist/serve/worker/host/firestore-writes.js +3 -2
- package/dist/serve/worker/host/firestore-writes.js.map +1 -1
- package/dist/serve/worker/host/studio.d.ts +1 -1
- package/dist/serve/worker/host/studio.d.ts.map +1 -1
- package/dist/serve/worker/host/studio.js +36 -1
- package/dist/serve/worker/host/studio.js.map +1 -1
- package/dist/serve/worker/host/subscriptions.d.ts.map +1 -1
- package/dist/serve/worker/host/subscriptions.js +27 -7
- package/dist/serve/worker/host/subscriptions.js.map +1 -1
- package/dist/serve/worker/host-context.d.ts +15 -0
- package/dist/serve/worker/host-context.d.ts.map +1 -1
- package/dist/serve/worker/host-context.js +10 -0
- package/dist/serve/worker/host-context.js.map +1 -1
- package/dist/serve/worker/host-messaging.d.ts.map +1 -1
- package/dist/serve/worker/host-messaging.js +9 -2
- package/dist/serve/worker/host-messaging.js.map +1 -1
- package/dist/serve/worker/index.d.ts +3 -2
- package/dist/serve/worker/index.d.ts.map +1 -1
- package/dist/serve/worker/index.js +4 -1
- package/dist/serve/worker/index.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +25 -3
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js +2 -2
- package/dist/serve/worker/protocol.js.map +1 -1
- package/dist/serve/worker/serve-init.d.ts +23 -37
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +95 -172
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/dist/vite.d.ts +4 -4
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +3 -3
- package/dist/vite.js.map +1 -1
- package/package.json +9 -4
- package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
- package/src/assurance/attachment.ts +211 -0
- package/src/assurance/browser.ts +80 -0
- package/src/assurance/campaign.ts +496 -0
- package/src/assurance/capabilities.ts +446 -0
- package/src/assurance/cases.ts +43 -0
- package/src/assurance/index.ts +75 -0
- package/src/assurance/runner.ts +885 -0
- package/src/assurance/tool-names.ts +15 -0
- package/src/assurance/tools.ts +830 -0
- package/src/assurance/types.ts +352 -0
- package/src/assurance/validation.ts +434 -0
- package/src/bridge/client/bridge.ts +550 -0
- package/src/bridge/client/dispatch.ts +137 -0
- package/src/bridge/client.ts +45 -0
- package/src/bridge/protocol.ts +351 -0
- package/src/bridge/server/audit.ts +53 -0
- package/src/bridge/server/bridge.ts +597 -0
- package/src/bridge/server/headless.ts +160 -0
- package/src/bridge/server/json-schema-to-zod.ts +108 -0
- package/src/bridge/server/local-bridge.ts +66 -0
- package/src/bridge/server/logger.ts +50 -0
- package/src/bridge/server/mcp-contract.ts +93 -0
- package/src/bridge/server/mcp.ts +129 -0
- package/src/bridge/server/peer.ts +232 -0
- package/src/bridge/server/standalone.ts +347 -0
- package/src/bridge/server/tool-metadata.ts +99 -0
- package/src/bridge/server.ts +28 -0
- package/src/cli/can-i-use.ts +50 -0
- package/src/cli/cli.test.ts +493 -0
- package/src/cli/database-rules.ts +329 -0
- package/src/cli/dev-runner.ts +282 -0
- package/src/cli/firebase-json.ts +100 -0
- package/src/cli/firestore-indexes.ts +70 -0
- package/src/cli/index.ts +425 -0
- package/src/cli/init.ts +260 -0
- package/src/cli/mcp-proxy.ts +196 -0
- package/src/cli/parse-args.ts +80 -0
- package/src/cli/rules.ts +259 -0
- package/src/cli/scope.ts +67 -0
- package/src/cli/serve.ts +996 -0
- package/src/cli/service-commands.ts +73 -0
- package/src/cli/snapshot.ts +164 -0
- package/src/cli/storage-rules.ts +172 -0
- package/src/cli/verify.ts +416 -0
- package/src/conformance/.generated/can-i-use-browser.ts +113 -0
- package/src/conformance/.generated/can-i-use.ts +117 -0
- package/src/conformance/.generated/conformance-docs.ts +14 -0
- package/src/conformance/browser.ts +24 -0
- package/src/conformance/can-i-use-tool.ts +51 -0
- package/src/conformance/can-i-use.ts +18 -0
- package/src/conformance/docs.ts +8 -0
- package/src/conformance/index.ts +20 -0
- package/src/conformance/tools.ts +16 -0
- package/src/credentials/core/memoize-ttl.ts +142 -0
- package/src/credentials/core/types.ts +10 -0
- package/src/credentials/node/from-adc.ts +96 -0
- package/src/credentials/node/from-service-account.ts +133 -0
- package/src/credentials/node/index.ts +6 -0
- package/src/discover/concurrency.ts +127 -0
- package/src/discover/crawler-adapter.ts +142 -0
- package/src/discover/crawler.ts +1127 -0
- package/src/discover/credential-free.ts +27 -0
- package/src/discover/findCollectionGroup.ts +131 -0
- package/src/discover/firestore-source.ts +59 -0
- package/src/discover/index.ts +5 -0
- package/src/discover/merge.ts +523 -0
- package/src/discover/session.ts +402 -0
- package/src/discover/tools.ts +201 -0
- package/src/discover/types.ts +187 -0
- package/src/discover/wire.ts +324 -0
- package/src/functions-rtdb/child.ts +382 -0
- package/src/functions-rtdb/delivery.ts +7 -0
- package/src/functions-rtdb/discovery.ts +136 -0
- package/src/functions-rtdb/event.ts +64 -0
- package/src/functions-rtdb/execution.ts +111 -0
- package/src/functions-rtdb/in-memory-delivery.ts +40 -0
- package/src/functions-rtdb/project.ts +74 -0
- package/src/functions-rtdb/projection.ts +104 -0
- package/src/functions-rtdb/reference-pattern.ts +20 -0
- package/src/functions-rtdb/remote-delivery.ts +23 -0
- package/src/pkg-version.ts +49 -0
- package/src/register/esm-exports.ts +63 -0
- package/src/register/hooks.ts +30 -0
- package/src/register/index.ts +150 -0
- package/src/register/mapping.ts +33 -0
- package/src/remote/index.ts +1050 -0
- package/src/rtdb/crawl-snapshot.ts +118 -0
- package/src/rtdb/inspection.ts +137 -0
- package/src/rtdb/load-rules-document.ts +56 -0
- package/src/rtdb/rules-generation-tool.ts +38 -0
- package/src/rtdb/rules-json.ts +29 -0
- package/src/serve/activity-guard.ts +29 -0
- package/src/serve/activity-route.ts +164 -0
- package/src/serve/activity-warning.ts +26 -0
- package/src/serve/bridge-mount.ts +200 -0
- package/src/serve/bundler.ts +493 -0
- package/src/serve/capture-store.ts +65 -0
- package/src/serve/discovery.ts +199 -0
- package/src/serve/entries/ai.ts +292 -0
- package/src/serve/entries/app-backend.ts +4 -0
- package/src/serve/entries/app-client.ts +26 -0
- package/src/serve/entries/app-session-store.ts +47 -0
- package/src/serve/entries/app.ts +11 -0
- package/src/serve/entries/auth-helper-core.ts +181 -0
- package/src/serve/entries/auth-helper-dom.ts +134 -0
- package/src/serve/entries/auth-helper-runtime.ts +20 -0
- package/src/serve/entries/auth.ts +255 -0
- package/src/serve/entries/bridge-url.ts +31 -0
- package/src/serve/entries/database.ts +129 -0
- package/src/serve/entries/firestore.ts +232 -0
- package/src/serve/entries/init.ts +46 -0
- package/src/serve/entries/keepalive.ts +46 -0
- package/src/serve/entries/messaging-sw.ts +75 -0
- package/src/serve/entries/messaging.ts +96 -0
- package/src/serve/entries/runtime.ts +540 -0
- package/src/serve/entries/session-store.ts +97 -0
- package/src/serve/entries/storage.ts +92 -0
- package/src/serve/entries/tab-sync-wiring.ts +274 -0
- package/src/serve/entries/worker-runtime.ts +75 -0
- package/src/serve/init-payload.ts +42 -0
- package/src/serve/namespace.ts +675 -0
- package/src/serve/open-browser.ts +68 -0
- package/src/serve/rules.ts +272 -0
- package/src/serve/sandbox-marker.ts +31 -0
- package/src/serve/server.ts +428 -0
- package/src/serve/standalone-assets.ts +180 -0
- package/src/serve/state-store.ts +166 -0
- package/src/serve/studio/disk-project-store.ts +185 -0
- package/src/serve/studio/disk-workspace.ts +162 -0
- package/src/serve/studio/index.ts +20 -0
- package/src/serve/studio/routes.ts +237 -0
- package/src/serve/studio/store-types.ts +48 -0
- package/src/serve/studio/studio-storage.test.ts +289 -0
- package/src/serve/vite-plugin.ts +1102 -0
- package/src/serve/worker/activity-bootstrap.ts +24 -0
- package/src/serve/worker/client/admin-firestore.ts +38 -0
- package/src/serve/worker/client/ai.ts +124 -0
- package/src/serve/worker/client/auth.ts +489 -0
- package/src/serve/worker/client/connection.ts +202 -0
- package/src/serve/worker/client/core.ts +350 -0
- package/src/serve/worker/client/disconnect.ts +45 -0
- package/src/serve/worker/client/firestore-reads.ts +181 -0
- package/src/serve/worker/client/firestore-refs.ts +226 -0
- package/src/serve/worker/client/firestore-writes.ts +232 -0
- package/src/serve/worker/client/handles.ts +91 -0
- package/src/serve/worker/client/messaging.ts +119 -0
- package/src/serve/worker/client/presence.ts +174 -0
- package/src/serve/worker/client/rtdb.ts +311 -0
- package/src/serve/worker/client/rules.ts +63 -0
- package/src/serve/worker/client/service-worker-connection.ts +59 -0
- package/src/serve/worker/client/snapshots.ts +86 -0
- package/src/serve/worker/client/storage.ts +195 -0
- package/src/serve/worker/client/studio.ts +88 -0
- package/src/serve/worker/client.ts +57 -0
- package/src/serve/worker/durable-persistence.ts +137 -0
- package/src/serve/worker/entry.ts +179 -0
- package/src/serve/worker/host/admin-firestore.ts +88 -0
- package/src/serve/worker/host/connection.ts +179 -0
- package/src/serve/worker/host/core.ts +387 -0
- package/src/serve/worker/host/dispatch.ts +316 -0
- package/src/serve/worker/host/firestore-reads.ts +117 -0
- package/src/serve/worker/host/firestore-writes.ts +456 -0
- package/src/serve/worker/host/presence.ts +312 -0
- package/src/serve/worker/host/rtdb.ts +136 -0
- package/src/serve/worker/host/rules.ts +128 -0
- package/src/serve/worker/host/storage.ts +312 -0
- package/src/serve/worker/host/studio.ts +75 -0
- package/src/serve/worker/host/subscriptions.ts +238 -0
- package/src/serve/worker/host-ai.ts +165 -0
- package/src/serve/worker/host-auth.ts +469 -0
- package/src/serve/worker/host-context.ts +273 -0
- package/src/serve/worker/host-events.ts +94 -0
- package/src/serve/worker/host-messaging.ts +240 -0
- package/src/serve/worker/host.ts +47 -0
- package/src/serve/worker/index.ts +196 -0
- package/src/serve/worker/presence-timing.ts +15 -0
- package/src/serve/worker/protocol.ts +1112 -0
- package/src/serve/worker/serve-init.ts +606 -0
- package/src/serve/worker/service-worker-channel.ts +33 -0
- package/src/serve/worker/service-worker-relay.ts +86 -0
- package/src/serve/writer-lock.ts +53 -0
- package/src/verify/cases.ts +233 -0
- package/src/verify/fixture.ts +258 -0
- package/src/verify/index.ts +519 -0
- package/src/verify/tools.ts +108 -0
- package/src/version/compat-target.ts +17 -0
- package/src/vite.ts +17 -0
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -30
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -30
- package/dist/serve/studio-ui/assets/index-BvtQIpGn.js +0 -1211
- package/dist/serve/studio-ui/assets/seed-3DMedLL3.js +0 -2
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/** Structural RTDB traversal over an already-local sandbox snapshot. */
|
|
2
|
+
export interface RtdbStructureNode {
|
|
3
|
+
path: string;
|
|
4
|
+
childCount: number;
|
|
5
|
+
truncated: boolean;
|
|
6
|
+
children: RtdbStructureNode[];
|
|
7
|
+
schema: Record<string, string>;
|
|
8
|
+
valueType?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CrawlSnapshotOptions {
|
|
12
|
+
path?: string;
|
|
13
|
+
maxDepth?: number;
|
|
14
|
+
maxChildren?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function childPath(parent: string, key: string): string {
|
|
18
|
+
return parent === '/' ? `/${key}` : `${parent}/${key}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function valueType(value: unknown): string {
|
|
22
|
+
if (value === null) return 'null';
|
|
23
|
+
if (Array.isArray(value)) return 'array';
|
|
24
|
+
return typeof value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizePath(path: string | undefined): string {
|
|
28
|
+
const segments = (path ?? '/').split('/').filter(Boolean);
|
|
29
|
+
return segments.length === 0 ? '/' : `/${segments.join('/')}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function valueAtPath(value: unknown, path: string): unknown {
|
|
33
|
+
let current = value;
|
|
34
|
+
for (const segment of path.split('/').filter(Boolean)) {
|
|
35
|
+
if (current === null || typeof current !== 'object' || Array.isArray(current)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
current = (current as Record<string, unknown>)[segment];
|
|
39
|
+
}
|
|
40
|
+
return current ?? null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function crawlNode(
|
|
44
|
+
value: unknown,
|
|
45
|
+
path: string,
|
|
46
|
+
depth: number,
|
|
47
|
+
maxDepth: number,
|
|
48
|
+
maxChildren: number,
|
|
49
|
+
): RtdbStructureNode {
|
|
50
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
51
|
+
return {
|
|
52
|
+
path,
|
|
53
|
+
childCount: 0,
|
|
54
|
+
truncated: false,
|
|
55
|
+
children: [],
|
|
56
|
+
schema: {},
|
|
57
|
+
valueType: valueType(value),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const entries = Object.entries(value as Record<string, unknown>)
|
|
62
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
63
|
+
const schema: Record<string, string> = {};
|
|
64
|
+
const objectEntries: Array<[string, Record<string, unknown>]> = [];
|
|
65
|
+
|
|
66
|
+
for (const [key, child] of entries) {
|
|
67
|
+
if (child !== null && typeof child === 'object' && !Array.isArray(child)) {
|
|
68
|
+
objectEntries.push([key, child as Record<string, unknown>]);
|
|
69
|
+
} else {
|
|
70
|
+
schema[key] = valueType(child);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const depthTruncated = depth >= maxDepth && objectEntries.length > 0;
|
|
75
|
+
const childLimitTruncated = entries.length > maxChildren;
|
|
76
|
+
const children = depth >= maxDepth
|
|
77
|
+
? []
|
|
78
|
+
: objectEntries
|
|
79
|
+
.slice(0, maxChildren)
|
|
80
|
+
.map(([key, child]) => crawlNode(
|
|
81
|
+
child,
|
|
82
|
+
childPath(path, key),
|
|
83
|
+
depth + 1,
|
|
84
|
+
maxDepth,
|
|
85
|
+
maxChildren,
|
|
86
|
+
));
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
path,
|
|
90
|
+
childCount: entries.length,
|
|
91
|
+
truncated: depthTruncated || childLimitTruncated,
|
|
92
|
+
children,
|
|
93
|
+
schema,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function crawlSnapshot(
|
|
98
|
+
snapshot: unknown,
|
|
99
|
+
options: CrawlSnapshotOptions = {},
|
|
100
|
+
): RtdbStructureNode {
|
|
101
|
+
const path = normalizePath(options.path);
|
|
102
|
+
const maxDepth = options.maxDepth ?? 10;
|
|
103
|
+
const maxChildren = options.maxChildren ?? 100;
|
|
104
|
+
return crawlNode(
|
|
105
|
+
valueAtPath(snapshot, path),
|
|
106
|
+
path,
|
|
107
|
+
0,
|
|
108
|
+
maxDepth,
|
|
109
|
+
maxChildren,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function countDescendantObjects(node: RtdbStructureNode): number {
|
|
114
|
+
return node.children.reduce(
|
|
115
|
+
(total, child) => total + 1 + countDescendantObjects(child),
|
|
116
|
+
0,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** Local RTDB inspection tools bound to one authoritative sandbox. */
|
|
2
|
+
import type { ToolHandler } from '@inbrowser/agent';
|
|
3
|
+
import { rtdbRules, type RtdbCase } from 'pyric/rules';
|
|
4
|
+
import type { LocalSandbox } from 'pyric/sandbox';
|
|
5
|
+
import { getActiveRules, snapshotState } from 'pyric/sandbox/database';
|
|
6
|
+
import { countDescendantObjects, crawlSnapshot } from './crawl-snapshot.js';
|
|
7
|
+
|
|
8
|
+
export interface RtdbInspectionToolDeps {
|
|
9
|
+
resolveSandbox(): LocalSandbox | Promise<LocalSandbox>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface SimulateAccessArgs {
|
|
13
|
+
operation: 'read' | 'write' | 'validate';
|
|
14
|
+
path: string;
|
|
15
|
+
auth?: { uid: string; claims?: Record<string, unknown> } | null;
|
|
16
|
+
newData?: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface CrawlStructureArgs {
|
|
20
|
+
path?: string;
|
|
21
|
+
maxDepth?: number;
|
|
22
|
+
maxChildren?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createRtdbInspectionTools(
|
|
26
|
+
deps: RtdbInspectionToolDeps,
|
|
27
|
+
): ToolHandler[] {
|
|
28
|
+
return [
|
|
29
|
+
{
|
|
30
|
+
name: 'rtdb_simulate_access',
|
|
31
|
+
description:
|
|
32
|
+
'Simulate one read, write, or validate operation against the RTDB rules and data currently loaded in the local sandbox. No production database is contacted and no prior rules-loading tool call is required.',
|
|
33
|
+
parameters: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
operation: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
enum: ['read', 'write', 'validate'],
|
|
39
|
+
},
|
|
40
|
+
path: { type: 'string' },
|
|
41
|
+
auth: {
|
|
42
|
+
anyOf: [
|
|
43
|
+
{ type: 'null' },
|
|
44
|
+
{
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
uid: { type: 'string' },
|
|
48
|
+
claims: { type: 'object' },
|
|
49
|
+
},
|
|
50
|
+
required: ['uid'],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
newData: {},
|
|
55
|
+
},
|
|
56
|
+
required: ['operation', 'path'],
|
|
57
|
+
},
|
|
58
|
+
async execute(rawArgs) {
|
|
59
|
+
const args = rawArgs as SimulateAccessArgs;
|
|
60
|
+
const sandbox = await deps.resolveSandbox();
|
|
61
|
+
const rules = getActiveRules(sandbox);
|
|
62
|
+
if (!rules) {
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
summary: 'No RTDB rules are loaded in the local sandbox.',
|
|
66
|
+
data: { code: 'NO_ACTIVE_RULES' },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const state = snapshotState(sandbox);
|
|
71
|
+
const data = state !== null && typeof state === 'object' && !Array.isArray(state)
|
|
72
|
+
? state as Record<string, unknown>
|
|
73
|
+
: {};
|
|
74
|
+
const oneCase: RtdbCase = {
|
|
75
|
+
expectation: 'ALLOW',
|
|
76
|
+
operation: args.operation,
|
|
77
|
+
path: args.path,
|
|
78
|
+
auth: args.auth
|
|
79
|
+
? { uid: args.auth.uid, token: args.auth.claims ?? {} }
|
|
80
|
+
: null,
|
|
81
|
+
data,
|
|
82
|
+
...(args.newData !== undefined ? { newData: args.newData } : {}),
|
|
83
|
+
};
|
|
84
|
+
const result = rtdbRules(rules).simulate([oneCase]).cases[0];
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
ok: !result.unsupported,
|
|
88
|
+
summary: result.unsupported
|
|
89
|
+
? `Simulation unsupported: ${result.reason}`
|
|
90
|
+
: `Simulation: ${result.decision.toLowerCase()}`,
|
|
91
|
+
data: {
|
|
92
|
+
decision: result.decision,
|
|
93
|
+
allowed: result.decision === 'ALLOW',
|
|
94
|
+
unsupported: result.unsupported,
|
|
95
|
+
matchedPath: result.matchedPath,
|
|
96
|
+
matchedRule: result.matchedRule,
|
|
97
|
+
reason: result.reason,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'rtdb_crawl_structure',
|
|
104
|
+
description:
|
|
105
|
+
'Describe the structure of the RTDB data currently loaded in the local sandbox without returning leaf values or contacting a production database.',
|
|
106
|
+
parameters: {
|
|
107
|
+
type: 'object',
|
|
108
|
+
properties: {
|
|
109
|
+
path: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
description: 'Root-relative path to inspect. Defaults to /.',
|
|
112
|
+
},
|
|
113
|
+
maxDepth: {
|
|
114
|
+
type: 'number',
|
|
115
|
+
minimum: 0,
|
|
116
|
+
description: 'Maximum object depth to return. Defaults to 10.',
|
|
117
|
+
},
|
|
118
|
+
maxChildren: {
|
|
119
|
+
type: 'number',
|
|
120
|
+
minimum: 1,
|
|
121
|
+
description: 'Maximum object children to return per node. Defaults to 100.',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
async execute(rawArgs) {
|
|
126
|
+
const args = rawArgs as CrawlStructureArgs;
|
|
127
|
+
const sandbox = await deps.resolveSandbox();
|
|
128
|
+
const root = crawlSnapshot(snapshotState(sandbox), args);
|
|
129
|
+
return {
|
|
130
|
+
ok: true,
|
|
131
|
+
summary: `Crawled ${countDescendantObjects(root)} object paths from ${root.path}`,
|
|
132
|
+
data: root,
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads a user's RTDB constraints module (a file that calls
|
|
3
|
+
* `defineRtdbRules(...)` from `pyric/rules`) so the CLI and MCP
|
|
4
|
+
* `generate` surfaces can turn it into static rules JSON without
|
|
5
|
+
* reimplementing compilation — the loaded document's `.toJSON()` still
|
|
6
|
+
* routes through the pure RTDB rules compiler.
|
|
7
|
+
*
|
|
8
|
+
* The module may export the `RtdbRulesDocument` as its default export
|
|
9
|
+
* or as a named `rules` export.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { pathToFileURL } from 'node:url';
|
|
13
|
+
import { resolve as resolvePath } from 'node:path';
|
|
14
|
+
import type { RtdbRulesDocument } from 'pyric/rules/internal/rtdb';
|
|
15
|
+
import { isRtdbRulesDocument } from './rules-json.js';
|
|
16
|
+
|
|
17
|
+
export interface LoadRtdbRulesDocumentOptions {
|
|
18
|
+
cwd?: string;
|
|
19
|
+
/** Named export to look for before falling back to `default`. Defaults to `rules`. */
|
|
20
|
+
exportName?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type LoadRtdbRulesDocumentResult =
|
|
24
|
+
| { ok: true; document: RtdbRulesDocument }
|
|
25
|
+
| { ok: false; message: string };
|
|
26
|
+
|
|
27
|
+
export async function loadRtdbRulesDocument(
|
|
28
|
+
configPath: string,
|
|
29
|
+
options: LoadRtdbRulesDocumentOptions = {},
|
|
30
|
+
): Promise<LoadRtdbRulesDocumentResult> {
|
|
31
|
+
const cwd = options.cwd ?? process.cwd();
|
|
32
|
+
const exportName = options.exportName ?? 'rules';
|
|
33
|
+
const resolved = resolvePath(cwd, configPath);
|
|
34
|
+
|
|
35
|
+
let mod: Record<string, unknown>;
|
|
36
|
+
try {
|
|
37
|
+
mod = (await import(pathToFileURL(resolved).href)) as Record<string, unknown>;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
message: `failed to load RTDB constraints module at ${resolved}: ${e instanceof Error ? e.message : String(e)}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const candidate = mod[exportName] ?? mod.default;
|
|
46
|
+
if (!isRtdbRulesDocument(candidate)) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
message:
|
|
50
|
+
`${resolved} does not export an RtdbRulesDocument (looked for a named ` +
|
|
51
|
+
`'${exportName}' export or a default export produced by defineRtdbRules(...)).`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { ok: true, document: candidate };
|
|
56
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ToolHandler } from '@inbrowser/agent';
|
|
2
|
+
import { loadRtdbRulesDocument } from './load-rules-document.js';
|
|
3
|
+
|
|
4
|
+
/** Local RTDB artifact generation for the CLI's agent registry. */
|
|
5
|
+
export function createRtdbRulesGenerationTools(): ToolHandler[] {
|
|
6
|
+
return [
|
|
7
|
+
{
|
|
8
|
+
name: 'rtdb_generate_rules',
|
|
9
|
+
description:
|
|
10
|
+
'Compile a local RTDB constraints module into database.rules.json data for inspection, tests, or committing.',
|
|
11
|
+
parameters: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
configPath: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description:
|
|
17
|
+
"Path to the constraints module, relative to cwd. Defaults to 'database.rules.ts'.",
|
|
18
|
+
},
|
|
19
|
+
cwd: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Working directory used to resolve configPath. Defaults to process.cwd().',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
async execute(args) {
|
|
26
|
+
const { configPath, cwd } = args as { configPath?: string; cwd?: string };
|
|
27
|
+
const loaded = await loadRtdbRulesDocument(configPath ?? 'database.rules.ts', { cwd });
|
|
28
|
+
if (!loaded.ok) return { ok: false, summary: loaded.message };
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
ok: true,
|
|
32
|
+
summary: 'Compiled RTDB constraints to database.rules.json',
|
|
33
|
+
data: { rulesJson: loaded.document.toJSON() },
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RtdbRulesDocument } from 'pyric/rules/internal/rtdb';
|
|
2
|
+
|
|
3
|
+
export interface RtdbRulesJson {
|
|
4
|
+
rules: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isRtdbRulesJson(value: unknown): value is RtdbRulesJson {
|
|
8
|
+
return isRtdbRulesObject(value) && isRtdbRulesObject(value.rules);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function parseRtdbRulesJson(
|
|
12
|
+
value: unknown,
|
|
13
|
+
onInvalid: () => Error,
|
|
14
|
+
): RtdbRulesJson {
|
|
15
|
+
if (!isRtdbRulesJson(value)) throw onInvalid();
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isRtdbRulesDocument(value: unknown): value is RtdbRulesDocument {
|
|
20
|
+
return (
|
|
21
|
+
typeof value === 'object' &&
|
|
22
|
+
value !== null &&
|
|
23
|
+
typeof (value as { toJSON?: unknown }).toJSON === 'function'
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isRtdbRulesObject(value: unknown): value is Record<string, unknown> {
|
|
28
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
monitorFirebaseActivity,
|
|
3
|
+
type ActivityFeed,
|
|
4
|
+
type ActivityMonitor,
|
|
5
|
+
} from 'pyric/firestore/internal';
|
|
6
|
+
|
|
7
|
+
/** Browser-safe reporter shared by the SharedWorker and in-page pyric dev paths. */
|
|
8
|
+
export function setupFirebaseActivityGuard(
|
|
9
|
+
feed: ActivityFeed,
|
|
10
|
+
fetchFn: typeof fetch,
|
|
11
|
+
activityToken: string,
|
|
12
|
+
): ActivityMonitor {
|
|
13
|
+
return monitorFirebaseActivity(feed, (incident) => {
|
|
14
|
+
try {
|
|
15
|
+
void fetchFn('/__pyric/activity', {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: {
|
|
18
|
+
'content-type': 'application/json',
|
|
19
|
+
'x-pyric-activity-token': activityToken,
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify(incident),
|
|
22
|
+
}).catch(() => {
|
|
23
|
+
/* Diagnostics are best-effort and must never affect app behavior. */
|
|
24
|
+
});
|
|
25
|
+
} catch {
|
|
26
|
+
/* An injected or unavailable fetch must never affect app behavior. */
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import {
|
|
3
|
+
hasGeneratedActivitySemantics,
|
|
4
|
+
type ActivityIncident,
|
|
5
|
+
} from 'pyric/firestore/internal';
|
|
6
|
+
|
|
7
|
+
const MAX_ACTIVITY_BODY_BYTES = 32 * 1024;
|
|
8
|
+
|
|
9
|
+
class ActivityBodyTooLargeError extends Error {}
|
|
10
|
+
|
|
11
|
+
export async function handleActivity(
|
|
12
|
+
onIncident: (incident: ActivityIncident) => void,
|
|
13
|
+
req: IncomingMessage,
|
|
14
|
+
res: ServerResponse,
|
|
15
|
+
activityToken: string,
|
|
16
|
+
): Promise<void> {
|
|
17
|
+
if (req.method !== 'POST') {
|
|
18
|
+
res.writeHead(405, { allow: 'POST' }).end('method not allowed');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const mediaType = req.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase();
|
|
22
|
+
if (mediaType !== 'application/json') {
|
|
23
|
+
res.writeHead(415, { 'content-type': 'text/plain; charset=utf-8' })
|
|
24
|
+
.end('content-type must be application/json');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!hasTrustedOrigin(req)) {
|
|
28
|
+
res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' })
|
|
29
|
+
.end('cross-origin activity reports are not allowed');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (req.headers['x-pyric-activity-token'] !== activityToken) {
|
|
33
|
+
res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' })
|
|
34
|
+
.end('invalid activity capability');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const body = await collectActivityBody(req);
|
|
39
|
+
if (!isActivityIncident(body)) throw new Error('invalid activity incident');
|
|
40
|
+
onIncident(body);
|
|
41
|
+
res.writeHead(204).end();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
res.writeHead(error instanceof ActivityBodyTooLargeError ? 413 : 400, {
|
|
44
|
+
'content-type': 'text/plain; charset=utf-8',
|
|
45
|
+
});
|
|
46
|
+
res.end(error instanceof Error ? error.message : String(error));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function hasTrustedOrigin(req: IncomingMessage): boolean {
|
|
51
|
+
const origin = req.headers.origin;
|
|
52
|
+
if (origin === undefined) return false;
|
|
53
|
+
const host = req.headers.host;
|
|
54
|
+
if (!host) return false;
|
|
55
|
+
try {
|
|
56
|
+
const parsed = new URL(origin);
|
|
57
|
+
const protocol = (req.socket as { encrypted?: boolean }).encrypted ? 'https:' : 'http:';
|
|
58
|
+
return parsed.protocol === protocol && parsed.host === host;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function collectActivityBody(req: IncomingMessage): Promise<unknown> {
|
|
65
|
+
const declaredLength = Number(req.headers['content-length']);
|
|
66
|
+
if (Number.isFinite(declaredLength) && declaredLength > MAX_ACTIVITY_BODY_BYTES) {
|
|
67
|
+
req.resume();
|
|
68
|
+
throw new ActivityBodyTooLargeError('activity incident exceeds 32 KiB');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const chunks: Buffer[] = [];
|
|
73
|
+
let size = 0;
|
|
74
|
+
let settled = false;
|
|
75
|
+
req.on('data', (chunk: Buffer | string) => {
|
|
76
|
+
if (settled) return;
|
|
77
|
+
const bytes = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
78
|
+
size += bytes.byteLength;
|
|
79
|
+
if (size > MAX_ACTIVITY_BODY_BYTES) {
|
|
80
|
+
settled = true;
|
|
81
|
+
chunks.length = 0;
|
|
82
|
+
reject(new ActivityBodyTooLargeError('activity incident exceeds 32 KiB'));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
chunks.push(bytes);
|
|
86
|
+
});
|
|
87
|
+
req.on('end', () => {
|
|
88
|
+
if (settled) return;
|
|
89
|
+
settled = true;
|
|
90
|
+
try {
|
|
91
|
+
resolve(JSON.parse(Buffer.concat(chunks, size).toString('utf8')));
|
|
92
|
+
} catch (error) {
|
|
93
|
+
reject(error);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
req.on('error', (error) => {
|
|
97
|
+
if (settled) return;
|
|
98
|
+
settled = true;
|
|
99
|
+
reject(error);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isActivityIncident(value: unknown): value is ActivityIncident {
|
|
105
|
+
if (!value || typeof value !== 'object') return false;
|
|
106
|
+
const incident = value as Record<string, unknown>;
|
|
107
|
+
const usage = incident.usage as Record<string, unknown> | undefined;
|
|
108
|
+
const actor = incident.actor as Record<string, unknown> | undefined;
|
|
109
|
+
const authLens = incident.authLens as Record<string, unknown> | undefined;
|
|
110
|
+
const listenerBalance = incident.listenerBalance as Record<string, unknown> | undefined;
|
|
111
|
+
return typeof incident.fingerprint === 'string'
|
|
112
|
+
&& incident.fingerprint.length <= 2_000
|
|
113
|
+
&& (incident.pattern === 'repeated-read'
|
|
114
|
+
|| incident.pattern === 'duplicate-listener'
|
|
115
|
+
|| incident.pattern === 'listener-churn')
|
|
116
|
+
&& (incident.confidence === 'medium' || incident.confidence === 'high')
|
|
117
|
+
&& (incident.severity === 'warning' || incident.severity === 'critical')
|
|
118
|
+
&& incident.service === 'firestore'
|
|
119
|
+
&& (incident.method === 'get' || incident.method === 'list' || incident.method === 'listen')
|
|
120
|
+
&& typeof incident.targetFingerprint === 'string'
|
|
121
|
+
&& incident.targetFingerprint.length <= 2_000
|
|
122
|
+
&& actor?.kind === 'app'
|
|
123
|
+
&& Boolean(authLens)
|
|
124
|
+
&& (authLens!.mode === 'app-session'
|
|
125
|
+
|| authLens!.mode === 'anon'
|
|
126
|
+
|| (authLens!.mode === 'as' && typeof authLens!.uid === 'string'))
|
|
127
|
+
&& (incident.authUid === null || typeof incident.authUid === 'string')
|
|
128
|
+
&& isNonNegativeInteger(incident.count)
|
|
129
|
+
&& incident.count > 0
|
|
130
|
+
&& isNonNegativeInteger(incident.windowMs)
|
|
131
|
+
&& Array.isArray(incident.evidenceEventIds)
|
|
132
|
+
&& incident.evidenceEventIds.length <= 8
|
|
133
|
+
&& incident.evidenceEventIds.every((id) => typeof id === 'string')
|
|
134
|
+
&& Boolean(usage)
|
|
135
|
+
&& (usage!.unit === 'document-reads' || usage!.unit === 'listener-attaches')
|
|
136
|
+
&& isNonNegativeInteger(usage!.lowerBound)
|
|
137
|
+
&& usage!.lowerBound === incident.count
|
|
138
|
+
&& Array.isArray(usage!.limitations)
|
|
139
|
+
&& usage!.limitations.length <= 8
|
|
140
|
+
&& usage!.limitations.every(
|
|
141
|
+
(limitation) => typeof limitation === 'string' && limitation.length <= 500,
|
|
142
|
+
)
|
|
143
|
+
&& (incident.sourceAttribution === 'app'
|
|
144
|
+
|| (typeof incident.sourceAttribution === 'string'
|
|
145
|
+
&& /^app [\w.-]{1,64}$/.test(incident.sourceAttribution)))
|
|
146
|
+
&& (listenerBalance === undefined
|
|
147
|
+
|| (isNonNegativeInteger(listenerBalance.attaches)
|
|
148
|
+
&& isNonNegativeInteger(listenerBalance.detaches)
|
|
149
|
+
&& isNonNegativeInteger(listenerBalance.active)
|
|
150
|
+
&& (listenerBalance.isLowerBound === undefined
|
|
151
|
+
|| typeof listenerBalance.isLowerBound === 'boolean')))
|
|
152
|
+
&& (incident.pattern === 'repeated-read'
|
|
153
|
+
? ((incident.method === 'get' || incident.method === 'list')
|
|
154
|
+
&& usage!.unit === 'document-reads'
|
|
155
|
+
&& listenerBalance === undefined)
|
|
156
|
+
: (incident.method === 'listen'
|
|
157
|
+
&& usage!.unit === 'listener-attaches'
|
|
158
|
+
&& listenerBalance !== undefined))
|
|
159
|
+
&& hasGeneratedActivitySemantics(incident as unknown as ActivityIncident);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isNonNegativeInteger(value: unknown): value is number {
|
|
163
|
+
return typeof value === 'number' && Number.isInteger(value) && value >= 0;
|
|
164
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ActivityIncident } from 'pyric/firestore/internal';
|
|
2
|
+
|
|
3
|
+
/** Format a terminal-safe, warning-only summary of observed Firebase activity. */
|
|
4
|
+
export function formatActivityWarning(incident: ActivityIncident): string {
|
|
5
|
+
const target = terminalSafe(incident.targetFingerprint);
|
|
6
|
+
const lowerBound = incident.listenerBalance?.isLowerBound === true ? 'at least ' : '';
|
|
7
|
+
const activity = incident.pattern === 'repeated-read'
|
|
8
|
+
? `${incident.count} repeated Firestore ${incident.method} operations`
|
|
9
|
+
: incident.pattern === 'duplicate-listener'
|
|
10
|
+
? `${lowerBound}${incident.count} duplicate active Firestore listeners`
|
|
11
|
+
: `${lowerBound}${incident.listenerBalance!.attaches} Firestore listener attaches, `
|
|
12
|
+
+ `${incident.listenerBalance!.detaches} detaches, `
|
|
13
|
+
+ `${incident.listenerBalance!.active} still active (listener churn)`;
|
|
14
|
+
const unit = incident.usage.unit === 'document-reads' ? 'document reads' : 'listener attachments';
|
|
15
|
+
return ` ⚠ Firebase Activity Guard: ${activity} on ${target} in ${incident.windowMs}ms. `
|
|
16
|
+
+ `Observed lower bound: ${incident.usage.lowerBound} ${unit}. `
|
|
17
|
+
+ `Source: ${terminalSafe(incident.sourceAttribution)}. Possible causes include repeated render/effect `
|
|
18
|
+
+ 'execution or missing listener cleanup. (warning only; app behavior is unchanged)';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function terminalSafe(value: string): string {
|
|
22
|
+
return value.replace(
|
|
23
|
+
/[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g,
|
|
24
|
+
'',
|
|
25
|
+
);
|
|
26
|
+
}
|