@pyric/cli 0.1.0-alpha.18 → 0.1.0-alpha.20
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 +14 -9
- package/dist/bridge/server/mcp-contract.d.ts +2 -2
- package/dist/bridge/server/mcp-contract.d.ts.map +1 -1
- package/dist/bridge/server/mcp-contract.js +3 -0
- package/dist/bridge/server/mcp-contract.js.map +1 -1
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +51 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-proxy.d.ts.map +1 -1
- package/dist/cli/mcp-proxy.js +3 -3
- package/dist/cli/mcp-proxy.js.map +1 -1
- package/dist/cli/parse-args.d.ts +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +51 -15
- package/dist/cli/parse-args.js.map +1 -1
- package/dist/cli/pyric-config.d.ts +24 -0
- package/dist/cli/pyric-config.d.ts.map +1 -0
- package/dist/cli/pyric-config.js +28 -0
- package/dist/cli/pyric-config.js.map +1 -0
- package/dist/cli/rules.d.ts +1 -0
- package/dist/cli/rules.d.ts.map +1 -1
- package/dist/cli/rules.js +18 -4
- package/dist/cli/rules.js.map +1 -1
- package/dist/cli/{dev-runner.d.ts → sandbox-runner.d.ts} +33 -36
- package/dist/cli/sandbox-runner.d.ts.map +1 -0
- package/dist/cli/{dev-runner.js → sandbox-runner.js} +90 -55
- package/dist/cli/sandbox-runner.js.map +1 -0
- package/dist/cli/serve.d.ts +6 -4
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +99 -40
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/service-command-records/storage-rules-resolve.d.ts +4 -0
- package/dist/cli/service-command-records/storage-rules-resolve.d.ts.map +1 -0
- package/dist/cli/service-command-records/storage-rules-resolve.js +3 -0
- package/dist/cli/service-command-records/storage-rules-resolve.js.map +1 -0
- package/dist/cli/service-commands.generated.d.ts +3 -0
- package/dist/cli/service-commands.generated.d.ts.map +1 -1
- package/dist/cli/service-commands.generated.js +2 -0
- package/dist/cli/service-commands.generated.js.map +1 -1
- package/dist/cli/snapshot.js +5 -5
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/verify.js +1 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/functions-rtdb/development-runtime.js +1 -1
- package/dist/functions-rtdb/development-runtime.js.map +1 -1
- package/dist/functions-rtdb/project.js +8 -8
- package/dist/functions-rtdb/project.js.map +1 -1
- package/dist/next/guard.js +1 -1
- package/dist/next/guard.js.map +1 -1
- package/dist/remote/index.d.ts +1 -1
- package/dist/remote/index.js +9 -9
- package/dist/remote/index.js.map +1 -1
- package/dist/serve/bundler.js +10 -10
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/entries/ai.d.ts.map +1 -1
- package/dist/serve/entries/ai.js +27 -1
- package/dist/serve/entries/ai.js.map +1 -1
- package/dist/serve/entries/auth-helper-dom.js +1 -1
- package/dist/serve/entries/auth-helper-dom.js.map +1 -1
- package/dist/serve/entries/auth-helper-runtime.js +1 -1
- package/dist/serve/entries/auth-helper-runtime.js.map +1 -1
- package/dist/serve/entries/firestore.js +3 -3
- package/dist/serve/entries/firestore.js.map +1 -1
- package/dist/serve/entries/keepalive.js +1 -1
- package/dist/serve/entries/keepalive.js.map +1 -1
- package/dist/serve/entries/runtime.js +21 -14
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/entries/storage.js +1 -1
- package/dist/serve/entries/storage.js.map +1 -1
- package/dist/serve/entries/worker-runtime.js +1 -1
- package/dist/serve/entries/worker-runtime.js.map +1 -1
- package/dist/serve/init-payload.d.ts +4 -0
- package/dist/serve/init-payload.d.ts.map +1 -1
- package/dist/serve/namespace.d.ts +6 -0
- package/dist/serve/namespace.d.ts.map +1 -1
- package/dist/serve/namespace.js +36 -5
- package/dist/serve/namespace.js.map +1 -1
- package/dist/serve/rules.d.ts +3 -3
- package/dist/serve/rules.d.ts.map +1 -1
- package/dist/serve/rules.js +56 -25
- package/dist/serve/rules.js.map +1 -1
- package/dist/serve/runtime/chip.d.ts.map +1 -1
- package/dist/serve/runtime/chip.js +32 -1
- package/dist/serve/runtime/chip.js.map +1 -1
- package/dist/serve/runtime/manifest.d.ts +1 -1
- package/dist/serve/runtime/manifest.d.ts.map +1 -1
- package/dist/serve/runtime/manifest.js +1 -1
- package/dist/serve/runtime/manifest.js.map +1 -1
- package/dist/serve/runtime/status.d.ts.map +1 -1
- package/dist/serve/runtime/status.js +23 -0
- package/dist/serve/runtime/status.js.map +1 -1
- package/dist/serve/sandbox-session.d.ts +1 -0
- package/dist/serve/sandbox-session.d.ts.map +1 -1
- package/dist/serve/sandbox-session.js +10 -4
- package/dist/serve/sandbox-session.js.map +1 -1
- package/dist/serve/server.d.ts +7 -0
- package/dist/serve/server.d.ts.map +1 -1
- package/dist/serve/server.js +15 -1
- package/dist/serve/server.js.map +1 -1
- package/dist/serve/site-ui/404.html +1 -1
- package/dist/serve/site-ui/_astro/{RulesCodeEditor.CzwHdCi9.js → RulesCodeEditor.XtDmHOlN.js} +1 -1
- package/dist/serve/site-ui/_astro/_example_.B9DG9HLJ.css +1 -0
- package/dist/serve/site-ui/_astro/_slug_.Da5VLhW1.css +1 -0
- package/dist/serve/site-ui/_astro/_studio_.BnfYCKoI.css +1 -0
- package/dist/serve/site-ui/_astro/app.B52xr-DK.js +1 -0
- package/dist/serve/site-ui/_astro/app.DWBraPUw.js +12 -0
- package/dist/serve/site-ui/_astro/app.pMSnN6Qw.css +1 -0
- package/dist/serve/site-ui/_astro/chess-showcase.B72XYeoh.js +354 -0
- package/dist/serve/site-ui/_astro/{example-runtime.B0byNQOl.js → example-runtime.C25_lx6Z.js} +2 -26
- package/dist/serve/site-ui/_astro/index.Tb-31efc.css +1 -0
- package/dist/serve/site-ui/_astro/index.UkCBr-_0.css +1 -0
- package/dist/serve/site-ui/_astro/sandbox-controls.DEpnKg-O.js +1169 -0
- package/dist/serve/site-ui/_astro/seed.Co0yUJY5.js +20 -0
- package/dist/serve/site-ui/_astro/writes.rpjrFzmu.js +102 -0
- package/dist/serve/site-ui/auth/index.html +5 -3
- package/dist/serve/site-ui/david-east.jpg +0 -0
- package/dist/serve/site-ui/docs/_rhythm/index.html +6 -3
- package/dist/serve/site-ui/docs/_rhythm.md +1 -1
- package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +45 -17
- package/dist/serve/site-ui/docs/agent/set-up-your-agent.md +20 -17
- package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +6 -5
- package/dist/serve/site-ui/docs/agent/watch-and-review.md +1 -5
- package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +8 -5
- package/dist/serve/site-ui/docs/agent/work-with-an-agent.md +4 -4
- package/dist/serve/site-ui/docs/ai-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/api-reference/index.html +6 -2
- package/dist/serve/site-ui/docs/api-reference.md +1 -0
- package/dist/serve/site-ui/docs/app-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/auth-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +5 -2
- package/dist/serve/site-ui/docs/build/authentication/index.html +7 -4
- package/dist/serve/site-ui/docs/build/authentication.md +2 -2
- package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +34 -53
- package/dist/serve/site-ui/docs/build/cloud-firestore.md +40 -36
- package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +5 -2
- package/dist/serve/site-ui/docs/build/cloud-storage/index.html +6 -3
- package/dist/serve/site-ui/docs/build/cloud-storage.md +1 -1
- package/dist/serve/site-ui/docs/build/realtime-database/index.html +9 -6
- package/dist/serve/site-ui/docs/build/realtime-database.md +4 -4
- package/dist/serve/site-ui/docs/conformance-scores/index.html +5 -2
- package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/create-pyric-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/database-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/examples/chess/index.html +649 -250
- package/dist/serve/site-ui/docs/examples/chess.md +45 -16
- package/dist/serve/site-ui/docs/examples/index.html +6 -3
- package/dist/serve/site-ui/docs/firestore-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +9 -6
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works.md +5 -5
- package/dist/serve/site-ui/docs/get-started/nextjs/index.html +9 -6
- package/dist/serve/site-ui/docs/get-started/nextjs.md +4 -4
- package/dist/serve/site-ui/docs/get-started/start-building/index.html +41 -10
- package/dist/serve/site-ui/docs/get-started/start-building.md +21 -10
- package/dist/serve/site-ui/docs/get-started/vite/index.html +6 -3
- package/dist/serve/site-ui/docs/get-started/vite.md +1 -1
- package/dist/serve/site-ui/docs/index.json +203 -168
- package/dist/serve/site-ui/docs/messaging-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +5 -2
- package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +6 -4
- package/dist/serve/site-ui/docs/observe/see-whats-happening.md +2 -4
- package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +9 -6
- package/dist/serve/site-ui/docs/observe/shape-your-data.md +4 -4
- package/dist/serve/site-ui/docs/overview/index.html +16 -10
- package/dist/serve/site-ui/docs/overview.md +19 -11
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +8 -5
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api.md +3 -3
- package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-reference-api/index.html +501 -0
- package/dist/serve/site-ui/docs/pyric-admin-reference-api.md +587 -0
- package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +166 -95
- package/dist/serve/site-ui/docs/pyric-database-reference-api.md +107 -1
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +10 -4
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api.md +5 -2
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +10 -4
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api.md +5 -2
- package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api.md +2 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/reference/cli/index.html +153 -0
- package/dist/serve/site-ui/docs/reference/cli.md +114 -0
- package/dist/serve/site-ui/docs/rules-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +6 -3
- package/dist/serve/site-ui/docs/secure/read-a-denial.md +1 -1
- package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +5 -2
- package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +7 -4
- package/dist/serve/site-ui/docs/ship/ship-to-production.md +2 -2
- package/dist/serve/site-ui/docs/ship/test-in-node/index.html +9 -6
- package/dist/serve/site-ui/docs/ship/test-in-node.md +4 -4
- package/dist/serve/site-ui/docs/storage-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +5 -2
- package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +5 -2
- package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +5 -2
- package/dist/serve/site-ui/docs/tutorial/index.html +10 -7
- package/dist/serve/site-ui/docs/tutorial.md +4 -4
- package/dist/serve/site-ui/examples/chess/index.html +4 -2
- package/dist/serve/site-ui/firestore/index.html +5 -3
- package/dist/serve/site-ui/hero-charcoal.jpg +0 -0
- package/dist/serve/site-ui/index.html +9 -7
- package/dist/serve/site-ui/llms.txt +4 -3
- package/dist/serve/site-ui/pyric-campfire-full.png +0 -0
- package/dist/serve/site-ui/pyric-campfire.png +0 -0
- package/dist/serve/site-ui/rtdb/index.html +5 -3
- package/dist/serve/site-ui/settings/index.html +5 -3
- package/dist/serve/site-ui/storage/index.html +5 -3
- package/dist/serve/site-ui/studio/index.html +13 -0
- package/dist/serve/site-ui/studio-routes.json +1 -1
- package/dist/serve/site-ui/traffic/index.html +5 -3
- package/dist/serve/studio/routes.d.ts +9 -0
- package/dist/serve/studio/routes.d.ts.map +1 -1
- package/dist/serve/studio/routes.js +47 -7
- package/dist/serve/studio/routes.js.map +1 -1
- package/dist/serve/vite-ai-config.d.ts.map +1 -1
- package/dist/serve/vite-ai-config.js +38 -8
- package/dist/serve/vite-ai-config.js.map +1 -1
- package/dist/serve/vite-module-swap.d.ts.map +1 -1
- package/dist/serve/vite-module-swap.js +0 -28
- package/dist/serve/vite-module-swap.js.map +1 -1
- package/dist/serve/vite-rules-source.d.ts.map +1 -1
- package/dist/serve/vite-rules-source.js +28 -7
- package/dist/serve/vite-rules-source.js.map +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts.map +1 -1
- package/dist/serve/vite-sandbox-generation.js +1 -1
- package/dist/serve/vite-sandbox-generation.js.map +1 -1
- package/dist/serve/worker/client/disconnect.js +1 -1
- package/dist/serve/worker/client/disconnect.js.map +1 -1
- package/dist/serve/worker/client/presence.d.ts.map +1 -1
- package/dist/serve/worker/client/presence.js +18 -5
- package/dist/serve/worker/client/presence.js.map +1 -1
- package/dist/serve/worker/host-ai.d.ts.map +1 -1
- package/dist/serve/worker/host-ai.js +15 -0
- package/dist/serve/worker/host-ai.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +4 -0
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js.map +1 -1
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +7 -1
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/package.json +5 -5
- package/src/bridge/server/mcp-contract.ts +3 -0
- package/src/cli/cli.test.ts +2 -2
- package/src/cli/index.ts +51 -42
- package/src/cli/mcp-proxy.ts +10 -11
- package/src/cli/parse-args.ts +61 -15
- package/src/cli/pyric-config.ts +45 -0
- package/src/cli/rules.ts +31 -4
- package/src/cli/{dev-runner.ts → sandbox-runner.ts} +117 -72
- package/src/cli/serve.ts +115 -47
- package/src/cli/service-command-records/storage-rules-resolve.ts +4 -0
- package/src/cli/service-commands.generated.ts +2 -0
- package/src/cli/snapshot.ts +5 -5
- package/src/cli/verify.ts +1 -1
- package/src/functions-rtdb/development-runtime.ts +1 -1
- package/src/functions-rtdb/project.ts +8 -8
- package/src/next/guard.ts +1 -1
- package/src/remote/index.ts +9 -9
- package/src/serve/bundler.ts +10 -10
- package/src/serve/entries/ai.ts +27 -1
- package/src/serve/entries/auth-helper-dom.ts +1 -1
- package/src/serve/entries/auth-helper-runtime.ts +1 -1
- package/src/serve/entries/firestore.ts +3 -3
- package/src/serve/entries/keepalive.ts +1 -1
- package/src/serve/entries/runtime.ts +19 -14
- package/src/serve/entries/storage.ts +1 -1
- package/src/serve/entries/worker-runtime.ts +1 -1
- package/src/serve/init-payload.ts +4 -0
- package/src/serve/namespace.ts +44 -5
- package/src/serve/rules.ts +61 -26
- package/src/serve/runtime/chip.ts +38 -1
- package/src/serve/runtime/manifest.ts +1 -1
- package/src/serve/runtime/status.ts +24 -0
- package/src/serve/sandbox-session.ts +10 -4
- package/src/serve/server.ts +18 -1
- package/src/serve/studio/routes.ts +63 -7
- package/src/serve/vite-ai-config.ts +37 -7
- package/src/serve/vite-module-swap.ts +0 -28
- package/src/serve/vite-rules-source.ts +29 -7
- package/src/serve/vite-sandbox-generation.ts +1 -1
- package/src/serve/worker/client/disconnect.ts +1 -1
- package/src/serve/worker/client/presence.ts +25 -5
- package/src/serve/worker/host-ai.ts +15 -0
- package/src/serve/worker/protocol.ts +2 -1
- package/src/serve/worker/serve-init.ts +5 -1
- package/README.md.orig +0 -141
- package/dist/cli/dev-runner.d.ts.map +0 -1
- package/dist/cli/dev-runner.js.map +0 -1
- package/dist/serve/site-ui/_astro/_example_.jevZ7OdV.css +0 -1
- package/dist/serve/site-ui/_astro/_slug_.C6QfVOxJ.css +0 -1
- package/dist/serve/site-ui/_astro/_slug_.CXh9yTP1.css +0 -1
- package/dist/serve/site-ui/_astro/_studio_.B6b_G1ev.css +0 -1
- package/dist/serve/site-ui/_astro/app.B7pBzz5N.js +0 -11
- package/dist/serve/site-ui/_astro/app.BsFsMjn0.css +0 -1
- package/dist/serve/site-ui/_astro/app.boex5SnQ.js +0 -1
- package/dist/serve/site-ui/_astro/chess-showcase.DDqE-QGe.js +0 -1
- package/dist/serve/site-ui/_astro/chess-source-explorer.C5k9c5yK.js +0 -1
- package/dist/serve/site-ui/_astro/chess-v2.CFHmd3UL.js +0 -354
- package/dist/serve/site-ui/_astro/index.uwPvjH_1.js +0 -1
- package/dist/serve/site-ui/_astro/jsx-runtime.D_zvdyIk.js +0 -9
- package/dist/serve/site-ui/_astro/reads.Dh3z0fJy.js +0 -1
- package/dist/serve/site-ui/_astro/sandbox-controls.C2IqtoSw.js +0 -1252
- package/dist/serve/site-ui/_astro/seed.CCmj00su.js +0 -20
- package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +0 -582
- package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research.md +0 -448
- package/dist/serve/site-ui/examples/firestore-first-write/index.html +0 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import{s as n,a as f,b as h,u as d,r as m}from"./app.DWBraPUw.js";import{s as w,T as A}from"./sandbox-controls.DEpnKg-O.js";import{s as o,M as p,d as s,K as g}from"./writes.rpjrFzmu.js";import"./index.DBy5LfQW.js";import"./index.DNyk7q8W.js";async function y(a){n.createUser(a,{uid:"alice",email:"alice@gmail.com",displayName:"Alice Nguyen",emailVerified:!0,photoUrl:"https://lh3.googleusercontent.com/a/alice",providerUserInfo:[{providerId:"google.com"}],customClaims:{plan:"pro"}}),n.createUser(a,{uid:"bob",email:"bob@example.com",password:"pyric-demo",displayName:"Bob Carter",emailVerified:!0,customClaims:{role:"editor"}}),n.createUser(a,{uid:"carol",email:"carol@example.com",displayName:"Carol Diaz",emailVerified:!1}),await f(a),await h(a)}const b=new g(37.7749,-122.4194);async function C(a){const r=Date.now(),i=e=>A.fromMillis(r-e*6e4),l=[{id:"alice",name:"Alice Nguyen",email:"alice@gmail.com",plan:"pro"},{id:"bob",name:"Bob Carter",email:"bob@example.com",plan:"free"},{id:"carol",name:"Carol Diaz",email:"carol@example.com",plan:"free"}];for(const e of l)await o(s(a,"users",e.id),{name:e.name,email:e.email,plan:e.plan,createdAt:p()});const t=["alice","bob","carol"],c=["Ship the redesign","Reply to carol","Buy milk","Draft the launch post","Review the rules","Plan offsite","Renew the domain","Read the spec","Fix the flaky test","Call the bank","Water the plants","Book the flights"];for(let e=0;e<c.length;e++){const u=t[e%t.length];await o(s(a,"notes",`note-${String(e+1).padStart(2,"0")}`),{title:c[e],done:e%3===0,pinned:e===0,priority:e%3+1,ownerId:u,ownerRef:`users/${u}`,tags:e%2===0?["work","urgent"]:["personal"],meta:{source:e%2===0?"app":"import",revision:e+1,flags:{starred:e%4===0,archived:!1}},location:e%3===0?b:null,createdAt:i(120-e*7),updatedAt:p()})}await o(s(a,"posts","post-hello"),{title:"Hello world",body:"First post from the seeded sandbox.",authorRef:"users/alice",published:!0,publishedAt:i(8)}),await o(s(a,"posts","post-draft"),{title:"Draft: roadmap",body:"Unpublished draft.",authorRef:"users/bob",published:!1})}async function R(a){const r=new TextEncoder,l=Uint8Array.from(atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="),t=>t.charCodeAt(0));await d(m(a,"avatars/alice.png"),l,{contentType:"image/png"}),await d(m(a,"notes/note-01/attachment.txt"),r.encode(`Attachment for the redesign note.
|
|
2
|
+
`),{contentType:"text/plain"})}const x=`rules_version = '2';
|
|
3
|
+
service cloud.firestore {
|
|
4
|
+
match /databases/{database}/documents {
|
|
5
|
+
match /users/{userId} {
|
|
6
|
+
allow read: if request.auth != null;
|
|
7
|
+
allow write: if request.auth != null && request.auth.uid == userId;
|
|
8
|
+
}
|
|
9
|
+
match /notes/{noteId} {
|
|
10
|
+
allow read: if request.auth != null;
|
|
11
|
+
allow create: if request.auth != null && request.resource.data.ownerId == request.auth.uid;
|
|
12
|
+
allow update, delete: if request.auth != null && resource.data.ownerId == request.auth.uid;
|
|
13
|
+
}
|
|
14
|
+
match /posts/{postId} {
|
|
15
|
+
allow read: if true;
|
|
16
|
+
allow write: if request.auth != null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`;function S(a){w(a,x)}async function k(a){await C(a.adminFirestore),await y(a.auth),await R(a.storage)}export{k as applySeed,S as deploySeedRules,y as seedAuth};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import{G as Tt,H,I as _t,J as W,K as R,L as fe,M as Dt,N as jt,O as Ke,P as Pe,c as Pt,Q as Se,U as re,V as It,W as _,X as qt,Y as f,_ as Vt,$ as Nt,a0 as zt,a1 as oe,a2 as v,a3 as Mt,a4 as $t,h as Ee,a5 as k,a6 as Xe,a7 as Lt,a8 as Ut,q as Ce,D as m,B as F,T as Ie,l as Wt,a9 as Bt,aa as Qt,ab as Ht,t as Re,ac as Jt,ad as Gt,ae as Yt,af as Kt,ag as Xt,S as Zt,ah as en}from"./sandbox-controls.DEpnKg-O.js";const tn=`RtdbExpr {
|
|
2
|
+
Expr = Ternary
|
|
3
|
+
|
|
4
|
+
Ternary
|
|
5
|
+
= Logical "?" Ternary ":" Ternary -- ternary
|
|
6
|
+
| Logical
|
|
7
|
+
|
|
8
|
+
Logical
|
|
9
|
+
= Logical "&&" Comparison -- and
|
|
10
|
+
| Logical "||" Comparison -- or
|
|
11
|
+
| Comparison
|
|
12
|
+
|
|
13
|
+
Comparison
|
|
14
|
+
= Comparison "===" Additive -- strictEq
|
|
15
|
+
| Comparison "!==" Additive -- strictNeq
|
|
16
|
+
| Comparison ">=" Additive -- gte
|
|
17
|
+
| Comparison "<=" Additive -- lte
|
|
18
|
+
| Comparison ">" Additive -- gt
|
|
19
|
+
| Comparison "<" Additive -- lt
|
|
20
|
+
| Comparison "==" Additive -- looseEq
|
|
21
|
+
| Comparison "!=" Additive -- looseNeq
|
|
22
|
+
| Additive
|
|
23
|
+
|
|
24
|
+
Additive
|
|
25
|
+
= Additive "+" Multiplicative -- add
|
|
26
|
+
| Additive "-" Multiplicative -- sub
|
|
27
|
+
| Multiplicative
|
|
28
|
+
|
|
29
|
+
Multiplicative
|
|
30
|
+
= Multiplicative "*" UnaryExpr -- mul
|
|
31
|
+
| Multiplicative "/" UnaryExpr -- div
|
|
32
|
+
| Multiplicative "%" UnaryExpr -- mod
|
|
33
|
+
| UnaryExpr
|
|
34
|
+
|
|
35
|
+
UnaryExpr
|
|
36
|
+
= "!" UnaryExpr -- not
|
|
37
|
+
| "-" UnaryExpr -- neg
|
|
38
|
+
| CallExpr
|
|
39
|
+
|
|
40
|
+
CallExpr
|
|
41
|
+
= CallExpr "." ident "(" ListOf<Expr, ","> ")" -- methodCall
|
|
42
|
+
| CallExpr "." ident -- memberAccess
|
|
43
|
+
| CallExpr "[" Expr "]" -- indexAccess
|
|
44
|
+
| Primary
|
|
45
|
+
|
|
46
|
+
Primary
|
|
47
|
+
= "(" Expr ")" -- paren
|
|
48
|
+
| Array
|
|
49
|
+
| literal
|
|
50
|
+
| ident
|
|
51
|
+
|
|
52
|
+
Array = "[" ListOf<Expr, ","> "]"
|
|
53
|
+
|
|
54
|
+
literal
|
|
55
|
+
= number
|
|
56
|
+
| string
|
|
57
|
+
| regex
|
|
58
|
+
| bool
|
|
59
|
+
| null
|
|
60
|
+
|
|
61
|
+
number
|
|
62
|
+
= digit+ "." digit+ exponent? -- float
|
|
63
|
+
| digit+ exponent -- exp
|
|
64
|
+
| digit+ -- int
|
|
65
|
+
|
|
66
|
+
exponent = ("e" | "E") ("+" | "-")? digit+
|
|
67
|
+
|
|
68
|
+
string
|
|
69
|
+
= "\\"" doubleStringChar* "\\"" -- double
|
|
70
|
+
| "'" singleStringChar* "'" -- single
|
|
71
|
+
|
|
72
|
+
doubleStringChar
|
|
73
|
+
= "\\\\" any -- escape
|
|
74
|
+
| ~"\\"" any -- char
|
|
75
|
+
|
|
76
|
+
singleStringChar
|
|
77
|
+
= "\\\\" any -- escape
|
|
78
|
+
| ~"'" any -- char
|
|
79
|
+
|
|
80
|
+
regex = "/" regexBody "/" regexFlags
|
|
81
|
+
|
|
82
|
+
regexBody = regexChar+
|
|
83
|
+
|
|
84
|
+
regexChar
|
|
85
|
+
= "\\\\" any -- escape
|
|
86
|
+
| ~("/" | "\\n") any -- char
|
|
87
|
+
|
|
88
|
+
regexFlags = alnum*
|
|
89
|
+
|
|
90
|
+
bool
|
|
91
|
+
= "true" ~identChar -- true
|
|
92
|
+
| "false" ~identChar -- false
|
|
93
|
+
|
|
94
|
+
null = "null" ~identChar
|
|
95
|
+
|
|
96
|
+
ident = "$"? identStart identChar*
|
|
97
|
+
|
|
98
|
+
identStart = letter | "_"
|
|
99
|
+
|
|
100
|
+
identChar = alnum | "_" | "$"
|
|
101
|
+
}
|
|
102
|
+
`;let qe;function Ze(){return qe??=Tt(tn),qe}function nn(t){return Ze().match(t.trim())}function rn(){return Ze().createSemantics()}const on=Symbol.for("pyric.remote.sandbox");function ve(t){return t!=null&&t[on]===!0}const sn={read:new Set(["auth","data","root","now"]),write:new Set(["auth","data","newData","root","now"]),validate:new Set(["auth","data","newData","root","now"])},an=new Set(["val","exists","hasChild","hasChildren","isString","isNumber","isBoolean","child","parent","getPriority"]),cn=new Set(["matches","contains","beginsWith","endsWith","replace","toLowerCase","toUpperCase","length"]),un=new Set([...an,...cn]);let q=[],J="read",et=new Set,ce;function dn(){if(ce)return ce;const t=rn();return t.addOperation("validate",{_nonterminal(...e){e.forEach(n=>n.validate())},_iter(...e){e.forEach(n=>n.validate())},_terminal(){},CallExpr_methodCall(e,n,r,o,s,i){e.validate();const a=r.sourceString;un.has(a)||q.push({code:"UNKNOWN_METHOD",message:`Unknown method '${a}'`}),s.asIteration().children.forEach(u=>u.validate())},CallExpr_memberAccess(e,n,r){e.validate()},CallExpr_indexAccess(e,n,r,o){e.validate(),r.validate()},ident(e,n,r){const o=this.sourceString;if(e.sourceString==="$")return;const i=sn[J];i&&(!i.has(o)&&!et.has(o)&&q.push({code:"UNKNOWN_IDENTIFIER",message:`Identifier '${o}' is not allowed in '${J}' context`}),o==="newData"&&J==="read"&&q.push({code:"NEWDATA_IN_READ",message:"'newData' is not available in 'read' context"}))}}),ce=t,t}function nr(t,e,n=[]){const r=nn(t);return r.failed()?[]:(q=[],J=e,et=new Set(n.map(o=>o.startsWith("$")?o.slice(1):o)),dn()(r).validate(),[...q])}const rr=H({operation:Ke(["read","write","validate"]),path:R().min(1).startsWith("/"),auth:Dt([H({uid:R(),token:fe(W())}),jt()]),mockData:fe(W()),newData:W().optional(),updates:_t(H({path:R().min(1).startsWith("/"),value:W()})).optional()});Ke(["INVALID_INPUT","NO_MATCHING_RULE","EVALUATION_ERROR"]);H({allowed:Pe(),unsupported:Pe().optional(),matchedPath:R(),matchedRule:R(),reason:R(),pathVariableBindings:fe(R())});const Ve="-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";let Ne=0;const E=new Array(12).fill(0);function or(t=Date.now()){const e=t===Ne;Ne=t;const n=new Array(8);let r=t;for(let s=7;s>=0;s--)n[s]=Ve.charAt(r%64),r=Math.floor(r/64);if(r!==0)throw new Error("RTDB push-id: timestamp overflow.");let o=n.join("");if(e){let s;for(s=11;s>=0&&E[s]===63;s--)E[s]=0;if(s<0)for(let i=0;i<12;i++)E[i]=Math.floor(Math.random()*64);else E[s]=(E[s]??0)+1}else for(let s=0;s<12;s++)E[s]=Math.floor(Math.random()*64);for(let s=0;s<12;s++)o+=Ve.charAt(E[s]);if(o.length!==20)throw new Error(`RTDB push-id: bad length ${o.length}.`);return o}function sr(t={}){return Pt()}function N(t){return t.replace(/^\/+|\/+$/g,"").split("/")}function tt(t){return N(t).length%2===0&&N(t).length>0}function Oe(t){return N(t).length%2===1}function se(t){const e=N(t);return e[e.length-1]??""}function nt(t){return N(t).slice(0,-1).join("/")}function rt(t,e){const n=e!==void 0,r=n?Se(e):void 0;return{id:t.id,ref:t,exists:n,data:()=>n?r:void 0,get:o=>re(r,o)}}const ze=new WeakMap;let ln=1;function Me(t){let e=ze.get(t);return e===void 0&&(e=ln++,ze.set(t,e)),e}function X(t){if(t===null||typeof t=="boolean")return t;if(typeof t=="string")return It(t);if(typeof t>"u")return{type:"undefined"};if(typeof t=="bigint")return _("bigint",t.toString());if(typeof t=="number"){const n=Number.isNaN(t)?"NaN":t===1/0?"Infinity":t===-1/0?"-Infinity":Object.is(t,-0)?"-0":String(t);return _("number",n)}if(typeof t!="object"&&typeof t!="function")return{type:typeof t,value:String(t)};const e=qt(t);if(e!==void 0)return e;try{if(Array.isArray(t))return{type:"array",id:Me(t)}}catch{}return{type:"opaque-object",id:Me(t)}}function x(t){return new f({code:"invalid-argument",message:t})}function pe(t,e,n,r=!1,o=!1){if(t===null||typeof t=="string"||typeof t=="boolean")return{comparison:t,execution:t};if(typeof t=="number")return{comparison:t,execution:t};if(typeof t>"u"||typeof t=="bigint"||typeof t=="function"||typeof t=="symbol")throw x(`Unsupported Firestore query value: ${typeof t}.`);if(typeof t!="object")throw x("Unsupported Firestore query value.");const s=Vt(t);if(s!==void 0){const a=Nt(t);if(a!==void 0&&n!==void 0&&a!==n)throw x("Document reference belongs to a different Firestore database.");return{comparison:{type:"registered-firestore-value",value:s},execution:zt(t)}}if(t instanceof Date){const a=t.getTime();if(!Number.isFinite(a))throw x("Invalid Date query value.");const u=Math.floor(a/1e3),c=Object.freeze({seconds:u,nanoseconds:Math.floor((a-u*1e3)*1e6)}),l={type:"timestamp",...c};return oe(c,l,()=>Object.freeze({...c})),{comparison:{type:"registered-firestore-value",value:l},execution:c}}if(t instanceof Uint8Array){const a=t.slice();return{comparison:{type:"bytes",values:Array.from(a)},execution:a}}if(e.has(t))throw x("Cyclic query operands are not Firestore values.");const i=Object.getPrototypeOf(t);if(i!==Object.prototype&&i!==null&&!Array.isArray(t))throw x("Firestore query maps must be plain objects.");e.add(t);try{if(Array.isArray(t)){if(o&&!r)throw x("Nested arrays are not supported.");const c=t.map(d=>pe(d,e,n,r,!0)),l=c.map(d=>d.execution);return v(l,X(t)),{comparison:{type:"array",values:c.map(d=>d.comparison)},execution:l}}const a=Object.keys(t).sort().map(c=>({key:c,captured:pe(t[c],e,n,r,!1)})),u=Object.fromEntries(a.map(({key:c,captured:l})=>[c,l.execution]));return v(u,X(t)),{comparison:{type:"map",entries:a.map(({key:c,captured:l})=>[c,l.comparison])},execution:u}}finally{e.delete(t)}}function ot(t,e,n=!1){try{const r=pe(t,new Set,e,n);return Object.freeze({kind:"canonical",value:r.comparison,executionValue:r.execution})}catch(r){if(r instanceof f)throw r;const o=r instanceof Error?r.message:String(r);throw x(`Invalid Firestore query value: ${o}`)}}function st(t,e){return Mt(t.value,e.value)}function G(t,e){if(t.kind==="where"){const n="comparisonValue"in t?t.comparisonValue:ot(t.value,e,t.op==="in"||t.op==="not-in");return Object.freeze({kind:"where",field:t.field,op:t.op,value:n.executionValue,comparisonValue:n})}return Object.freeze({kind:t.kind,filters:Object.freeze(t.filters.map(n=>G(n,e)))})}function B(t,e,n,r){const o=t.map(s=>ot(s,r));return Object.freeze({values:Object.freeze(o.map(s=>s.executionValue)),comparisonValues:Object.freeze(o),inclusive:e,fromSnapshot:n})}function it(t,e){return t.length!==e.length?!1:t.every((n,r)=>{const o=e[r];return n.kind!==o.kind?!1:n.kind==="where"&&o.kind==="where"?n.field===o.field&&n.op===o.op&&st(n.comparisonValue,o.comparisonValue):n.kind!=="where"&&o.kind!=="where"&&it(n.filters,o.filters)})}function $e(t,e){return t===void 0||e===void 0?t===e:t.inclusive===e.inclusive&&t.comparisonValues.length===e.comparisonValues.length&&t.comparisonValues.every((n,r)=>st(n,e.comparisonValues[r]))}function hn(t,e){return t.kind!==e.kind?!1:t.kind==="collection"&&e.kind==="collection"?t.path===e.path:t.kind==="collection-group"&&e.kind==="collection-group"&&t.collectionId===e.collectionId}function at(t){return t.kind==="where"?Object.freeze({kind:t.kind,field:t.field,op:t.op,value:t.value}):Object.freeze({kind:t.kind,filters:Object.freeze(t.filters.map(at))})}function Le(t){if(t!==void 0)return Object.freeze({values:t.values,inclusive:t.inclusive,fromSnapshot:t.fromSnapshot})}function fn(t,e){return it(t.filters,e.filters)&&t.orders.length===e.orders.length&&t.orders.every((n,r)=>{const o=e.orders[r];return n.field===o.field&&n.direction===o.direction})&&t.limitCount===e.limitCount&&t.limitFromEnd===e.limitFromEnd&&$e(t.start,e.start)&&$e(t.end,e.end)}const ct="__name__";function Ue(t,e){const n=t.data();if(n===void 0)throw new f({code:"not-found",message:`Snapshot-based cursors require an existing document — got an empty snapshot for ${t.id??"<unknown>"}.`});return e.map(r=>r.field===ct?t.ref:n[r.field])}class z{env;auth;collectionPath;clauses;orders;limitCount;limitFromEnd;start;end;bypassRules;documentRef;constructor(e){this.env=e.env,this.auth=e.auth,this.collectionPath=e.collectionPath,this.documentRef=e.documentRef,this.clauses=Object.freeze((e.clauses??[]).map(n=>G(n))),this.orders=Object.freeze((e.orders??[]).map(n=>Object.freeze({...n}))),this.limitCount=e.limitCount,this.limitFromEnd=e.limitFromEnd??!1,this.start=e.start===void 0?void 0:Object.freeze({...e.start,values:Object.freeze(e.start.comparisonValues.map(n=>n.executionValue)),comparisonValues:e.start.comparisonValues}),this.end=e.end===void 0?void 0:Object.freeze({...e.end,values:Object.freeze(e.end.comparisonValues.map(n=>n.executionValue)),comparisonValues:e.end.comparisonValues}),this.bypassRules=e.bypassRules??!1}where(e,n,r){return this.clone({clauses:[...this.clauses,G({kind:"where",field:e,op:n,value:r})]})}applyFilter(e){return this.clone({clauses:[...this.clauses,G(e)]})}orderBy(e,n="asc"){return this.clone({orders:[...this.orders,{field:e,direction:n}]})}limit(e){return this.clone({limitCount:e,limitFromEnd:!1})}limitToLast(e){return this.clone({limitCount:e,limitFromEnd:!0})}startCursor(e,n){return this.clone({start:B(this.normalizeExplicitCursorValues(e),n,!1)})}endCursor(e,n){return this.clone({end:B(this.normalizeExplicitCursorValues(e),n,!1)})}startCursorFromSnapshot(e,n){return this.clone({start:B(Ue(e,this.normalizedOrders()),n,!0)})}endCursorFromSnapshot(e,n){return this.clone({end:B(Ue(e,this.normalizedOrders()),n,!0)})}isStructurallyEqual(e){return e instanceof z?this.env===e.env&&this.bypassRules===e.bypassRules&&hn(this.queryScope(),e.queryScope())&&fn(this.comparableExecutionSpec(),e.comparableExecutionSpec()):!1}clone(e){return new z({env:this.env,auth:this.auth,collectionPath:this.collectionPath,documentRef:this.documentRef,clauses:e.clauses??this.clauses,orders:e.orders??this.orders,limitCount:"limitCount"in e?e.limitCount:this.limitCount,limitFromEnd:e.limitFromEnd??this.limitFromEnd,start:"start"in e?e.start:this.start,end:"end"in e?e.end:this.end,bypassRules:this.bypassRules})}queryScope(){return{kind:"collection",path:this.collectionPath}}readQueryRows(e){const n=e?.auth!==void 0?e.auth:this.auth;let r;try{r=this.env.runQuery({scope:this.queryScope(),auth:n,execution:this.executionSpec(),bypassRules:this.bypassRules,activityQuery:this.activityQuery()})}catch(o){const s=o?.simError;throw s?new f(s):o}if(!r.allowed)throw new f(r.error);return r.docs}activityQuery(){const e=r=>r.kind==="where"?{kind:"where",field:r.field,op:r.op,value:X(r.value)}:{kind:r.kind,filters:r.filters.map(e)},n=r=>r===void 0?null:{values:r.values.map(o=>X(o)),inclusive:r.inclusive,fromSnapshot:r.fromSnapshot};return{scope:this.activityScope(),filters:this.clauses.map(e),orderBy:this.orders.map(r=>({...r})),limit:this.limitCount??null,limitFromEnd:this.limitFromEnd,start:n(this.start),end:n(this.end)}}activityScope(){return{kind:"collection"}}normalizedOrders(){return $t(this.executionSpec())}normalizeExplicitCursorValues(e){const n=this.orders;return e.map((r,o)=>{if(n[o]?.field!==ct)return r;if(typeof r!="string")throw new f({code:"invalid-argument",message:"Expected a string for a document ID cursor value."});const s=r.includes("/")?r:`${this.collectionPath}/${r}`;return this.documentRef(s)})}executionSpec(){const e={filters:Object.freeze(this.clauses.map(at)),orders:this.orders,limitCount:this.limitCount,limitFromEnd:this.limitFromEnd,start:Le(this.start),end:Le(this.end)};return Object.freeze(e)}comparableExecutionSpec(){return{filters:this.clauses,orders:this.orders,limitCount:this.limitCount,limitFromEnd:this.limitFromEnd,start:this.start,end:this.end}}snapshotConstraints(){return Object.freeze({execution:this.executionSpec(),activityQuery:this.activityQuery()})}async get(e){const r=this.readQueryRows(e).map(o=>{const s=this.documentRef(o.path),i=Se(o.data);return{id:s.id,ref:s,exists:!0,data:()=>i,get:a=>re(i,a)}});return{size:r.length,empty:r.length===0,docs:r,forEach(o){r.forEach(o)}}}async aggregate(e,n){const r=this.readQueryRows(n),o={};for(const s of Object.keys(e))o[s]=mn(e[s],r);return{data:()=>o}}}function pn(t,e){if(!e.includes("."))return t[e];let n=t;for(const r of e.split(".")){if(typeof n!="object"||n===null||Array.isArray(n))return;n=n[r]}return n}function mn(t,e){if(t.kind==="count")return e.length;let n=0,r=0;for(const o of e){const s=pn(o.data,t.field);typeof s=="number"&&Number.isFinite(s)&&(n+=s,r++)}return t.kind==="sum"?n:r===0?null:n/r}function A(t,e){if(t.error)throw new f(t.error);const n=t.debugMessages.length>0?t.debugMessages.join("; "):"(no debug message)";throw new f(k("permission-denied",`${e}: ${n}`))}class yn{env;auth;bypassRules;collectionRef;id;path;constructor(e,n,r,o=!1,s){this.env=e,this.auth=n,this.bypassRules=o,this.collectionRef=s,this.path=r,this.id=se(r),v(this,_("reference",r)),Ee(this,r,e)}get parent(){return this.collectionRef(nt(this.path))}collection(e){return this.collectionRef(`${this.path}/${e}`)}async get(e){const n=this.env.execute({method:"get",path:this.path,auth:e?.auth!==void 0?e.auth:this.auth,bypassRules:this.bypassRules});n.allowed||A(n,"get denied");const r=n.data===void 0||n.data===null?void 0:n.data;return rt(this,r)}async set(e,n){const r=n?.auth!==void 0?n.auth:this.auth;if(n?.mergeFields!==void 0){const s=n.mergeFields.map(c=>String(c)),a=this.env.getDocument(this.path)===null?"create":"update",u=this.env.execute({method:a,path:this.path,data:e,auth:r,merge:{mergeFields:s},bypassRules:this.bypassRules});u.allowed||A(u,`set denied (as ${a})`);return}if(n?.merge===!0){const i=this.env.getDocument(this.path)===null?"create":"update",a=this.env.execute({method:i,path:this.path,data:e,auth:r,merge:!0,bypassRules:this.bypassRules});a.allowed||A(a,`set denied (as ${i})`);return}const o=this.env.execute({method:"set",path:this.path,data:e,auth:r,bypassRules:this.bypassRules});o.allowed||A(o,"set denied")}async update(e,n){const r=this.env.execute({method:"update",path:this.path,data:e,auth:n?.auth!==void 0?n.auth:this.auth,bypassRules:this.bypassRules});r.allowed||A(r,"update denied")}async delete(e){const n=this.env.execute({method:"delete",path:this.path,auth:e?.auth!==void 0?e.auth:this.auth,bypassRules:this.bypassRules});n.allowed||A(n,"delete denied")}}function Z(t,e,n,r){return new yn(t,e,n,r,o=>new ut(t,e,o,r))}class ut extends z{id;path;constructor(e,n,r,o=!1){super({env:e,auth:n,collectionPath:r,bypassRules:o,documentRef:s=>Z(e,n,s,o)}),this.path=r,this.id=se(r)}doc(e){const n=e??Xe();return Z(this.env,this.auth,`${this.path}/${n}`,this.bypassRules)}async add(e,n){const r=this.doc();return await r.set(e,n),r}}class Ae extends z{collectionId;constructor(e){super({...e,collectionPath:"",documentRef:n=>Z(e.env,e.auth,n,e.bypassRules??!1)}),this.collectionId=e.collectionId}clone(e){return new Ae({env:this.env,auth:this.auth,collectionId:this.collectionId,clauses:e.clauses??this.clauses,orders:e.orders??this.orders,limitCount:"limitCount"in e?e.limitCount:this.limitCount,limitFromEnd:e.limitFromEnd??this.limitFromEnd,start:"start"in e?e.start:this.start,end:"end"in e?e.end:this.end,bypassRules:this.bypassRules})}queryScope(){return{kind:"collection-group",collectionId:this.collectionId}}activityScope(){return{kind:"collection-group"}}}class bn{env;auth;bypassRules;ops=[];constructor(e,n,r=!1){this.env=e,this.auth=n,this.bypassRules=r}set(e,n){const o=this.env.getDocument(e.path)===null?"create":"update";return this.ops.push({method:o,path:e.path,data:n}),this}update(e,n){return this.ops.push({method:"update",path:e.path,data:n}),this}delete(e){return this.ops.push({method:"delete",path:e.path}),this}async commit(e){if(this.ops.length===0)return;const n=this.env.batch(this.ops,e?.auth!==void 0?e.auth:this.auth,this.bypassRules);if(n.allowed)return;if(n.error)throw new f(n.error);const o=n.results.find(s=>!s.allowed)?.debugMessages.join("; ")??"batch denied";throw new f(k("permission-denied",`batch failed: ${o}`))}}function gn(t){return typeof t.where=="function"}class wn{simTx;constructor(e){this.simTx=e}async get(e){if(gn(e)){const o=await e.get();return o.size>0&&this.simTx.getAll(...o.docs.map(s=>s.ref.path)),o}const r=this.simTx.get(e.path).data();return rt(e,r)}set(e,n){return this.simTx.set(e.path,n),this}update(e,n){return this.simTx.update(e.path,n),this}delete(e){return this.simTx.delete(e.path),this}}class xn{env;auth;bypassRules;provenance;constructor(e,n,r=!1,o){this.env=e,this.auth=n,this.bypassRules=r,this.provenance=o}collection(e){if(!Oe(e))throw new f(k("invalid-argument",`collection path must have an odd number of segments: ${e}`));return new ut(this.env,this.auth,e,this.bypassRules)}doc(e){if(!tt(e))throw new f(k("invalid-argument",`document path must have an even number of segments: ${e}`));return Z(this.env,this.auth,e,this.bypassRules)}collectionGroup(e){if(e.length===0||e.includes("/"))throw new f(k("invalid-argument",`collection group id must be a single non-empty segment with no '/': ${e}`));return new Ae({env:this.env,auth:this.auth,collectionId:e,bypassRules:this.bypassRules})}batch(){return new bn(this.env,this.auth,this.bypassRules)}async runTransaction(e,n){let r;try{r=await this.env.transaction(o=>{const s=new wn(o);return e(s)},{auth:n?.auth!==void 0?n.auth:this.auth,bypassRules:this.bypassRules,provenance:this.provenance,maxAttempts:n?.maxAttempts})}catch(o){throw o instanceof Lt?new f(o.simError):o instanceof Ut?new f(o.simError):o}if(!r.allowed){if(r.error)throw new f(r.error);const s=r.writes.find(i=>!i.allowed)?.debugMessages.join("; ")??"transaction denied at commit";throw new f(k("permission-denied",`transaction failed: ${s}`))}return r.returnValue}}function Sn(t,e){return new xn(t,e?.auth??null,e?.bypassRules??!1,e?.provenance)}const dt=Symbol("pyric/sandbox/context"),lt=Symbol("pyric/sandbox/bypassRules");let me=null;function En(t){me=t}function Cn(t){const e=t;return typeof e.path=="string"&&e.path.length>0||typeof e.collectionPath=="string"&&e.collectionPath.length>0}function Rn(t){const e=t.trim();if(!e)return[];const n=e.indexOf(": ");if(n!==-1){const o=e.slice(n+2).trim().split("; ").map(s=>s.trim()).filter(s=>s.length>0);if(o.length>0)return o}return[e]}function We(t,e){if(t instanceof f===!1)return t;const r=t.code;if(r==="permission-denied"===!1)return new m(r,t.message);const s={auth:e.auth,reasons:Rn(t.message)},i=t.simError;if(i!==void 0){if(i.request!==void 0){const p=i.request;s.request={method:p.method,path:p.path},p.resourceData!==void 0&&(s.request.resourceData=p.resourceData)}if(i.resource!==void 0&&(s.resource={data:i.resource.data,exists:i.resource.exists}),i.query!==void 0&&(s.query=i.query),i.rule!==void 0&&(s.rule=i.rule),i.remediation!==void 0)return new m({code:r,message:t.message,denialContext:s,remediation:i.remediation})}return new m(r,t.message,s)}function ee(t,e,n=!1){return new Proxy(t,{get(r,o,s){if(o===dt)return e;if(o===lt)return n;if(o==="onSnapshot"&&me!==null&&!(o in r)&&Cn(r))return(...a)=>me(s,...a);const i=Reflect.get(r,o,s);return typeof i!="function"?i:function(...a){let u;try{const c=()=>i.apply(r,a);u=e.sandbox.runWithProvenance?.(Ce(e.operationContext),c)??c()}catch(c){throw We(c,e)}return u instanceof Promise?u.then(c=>c&&typeof c=="object"?ee(c,e,n):c).catch(c=>{throw We(c,e)}):u&&typeof u=="object"?ee(u,e,n):u}}})}function ht(t,e=!1){if(ve(t.sandbox))throw new Error("pyric/sandbox/admin-firestore: internal — a remote sandbox context reached the local engine builder; remote dispatch should have handled it. Please report this.");const n=()=>Sn(F(t.sandbox),{auth:t.auth,bypassRules:e,provenance:Ce(t.operationContext)});return{collection(r){return n().collection(r)},doc(r){return n().doc(r)},collectionGroup(r){return n().collectionGroup(r)},batch(){return n().batch()},runTransaction(r,o){return n().runTransaction(r,o)},setRules(r){return F(t.sandbox).deployRules(r)},seed(r){const o=F(t.sandbox);return o.seed({rules:o.getRules(),documents:r?.documents??{}})},snapshot(){return F(t.sandbox).snapshot()}}}function ye(t){if(t instanceof m)return t;if(t!==null&&typeof t=="object"){const e=t;if(typeof e.code=="string"&&typeof e.message=="string"){const n=e.denialContext!==null&&typeof e.denialContext=="object"?e.denialContext:void 0;return new m(e.code,e.message,n)}}return t}function g(t){return new m("invalid-argument",t)}async function y(t,e){try{return await t.sandbox.channel.op({...e,actAs:t.lens})}catch(n){throw ye(n)}}const vn=new Set(["serverTimestamp","increment","arrayUnion","arrayRemove","deleteField"]);function On(t){return t!==null&&typeof t=="object"&&typeof t.__type=="string"&&vn.has(t.__type)}function An(t){switch(t.__type){case"serverTimestamp":return{__sentinel:"serverTimestamp"};case"increment":return{__sentinel:"increment",n:t.value};case"arrayUnion":return{__sentinel:"arrayUnion",values:t.values.map(w)};case"arrayRemove":return{__sentinel:"arrayRemove",values:t.values.map(w)};case"deleteField":return{__sentinel:"deleteField"}}}function w(t){if(t===null||typeof t!="object")return t;if(On(t))return An(t);if(t instanceof Ie)return t.toJSON();if(t instanceof Date)return Ie.fromDate(t).toJSON();if(Array.isArray(t))return t.map(w);const e=Object.getPrototypeOf(t);if(e!==Object.prototype&&e!==null){const r=t.toJSON;if(typeof r=="function")return r.call(t);throw g(`remote Firestore: cannot serialize a ${e.constructor?.name??"class"} instance for the wire — use plain data, Timestamp, Date, or FieldValue sentinels.`)}const n={};for(const[r,o]of Object.entries(t))n[r]=w(o);return n}function O(t){return w(t)}function be(t){return Wt(JSON.parse(t.json))}function ft(t){return Se(be(t))}function pt(t,e){const n=e.exists&&e.data?ft(e.data):void 0;return{id:t.id,ref:t,exists:e.exists,data:()=>n,get:r=>re(n,r)}}function mt(t,e){const n=e.docs.map(r=>{const o=r.path??r.id,s=te(t,o),i=r.data?ft(r.data):{};return{id:s.id,ref:s,exists:!0,data:()=>i,get:a=>re(i,a)}});return{size:n.length,empty:n.length===0,docs:n,forEach(r){n.forEach(o=>r(o))}}}const ke=Symbol("pyric/sandbox/admin-firestore/remoteSnapshotRegistrar");function kn(t){if(t===null||typeof t!="object")return;const e=t[ke];return typeof e=="function"?e:void 0}let ge=null;function Fn(t){ge=t}function yt(...t){if(ge===null)throw new m("failed-precondition","remote Firestore: onSnapshot is not wired yet — import pyric-admin/firestore (or pyric/sandbox/admin-firestore) before subscribing.");return ge(this,...t)}function bt(t,e,n,r,o){const s=n.includeMetadataChanges!==!0;let i,a;try{a=t.sandbox.channel.subscribe({target:e,actAs:t.lens},u=>{if(!r)return;const c=u;if(Array.isArray(c.docs)){const l=c.docs.map(p=>({path:p.path??p.id,data:p.data?be(p.data):{}})),d=e.__ref==="query"?e.source.__ref==="group"?e.source.collectionId:e.source.path:e.__ref==="group"?e.collectionId:e.path,h=Bt({path:d},l,{excludesMetadataChanges:s},i);i=l,r(h)}else if(typeof c.id=="string"){const l=c.path??c.id,d=c.exists&&c.data?be(c.data):null;r(Qt(l,d))}},u=>{const c=ye(u);o?o(c):console.error("pyric remote Firestore: uncaught onSnapshot error:",c)})}catch(u){throw ye(u)}return a}const gt=Symbol("pyric/sandbox/admin-firestore/remoteQueryState");function Tn(t){if(!(t===null||typeof t!="object"))return t[gt]}function wt(t){return t.kind==="where"?(xt(t.op),{kind:"where",field:t.field,op:t.op,value:w(t.value)}):{kind:t.kind,filters:t.filters.map(wt)}}const _n=new Set(["<","<=","==","!=",">=",">","in","not-in","array-contains","array-contains-any"]);function xt(t){if(!_n.has(t))throw g(`invalid where operator: ${String(t)}`)}function Y(t){const e=[...t.filters];for(const n of t.orders)e.push({kind:"orderBy",field:n.field,direction:n.direction});return t.start&&e.push({kind:t.start.inclusive?"startAt":"startAfter",values:t.start.values.map(w)}),t.end&&e.push({kind:t.end.inclusive?"endAt":"endBefore",values:t.end.values.map(w)}),t.limitCount!==void 0&&e.push(t.limitFromEnd?{kind:"limitToLast",n:t.limitCount}:{kind:"limit",n:t.limitCount}),e.length===0?t.source:{__ref:"query",source:t.source,constraints:e}}function Be(t,e,n){if(e.length===0)throw g(`${n}(snapshot) on a remote sandbox requires at least one explicit orderBy() clause — the relay protocol has no document-key cursor.`);if(e.some(o=>o.field==="__name__"))throw g(`${n}(snapshot) on a remote sandbox does not support orderBy('__name__') — the relay protocol has no document-key cursor, and __name__ is not a data field a value cursor can carry. Order by a real document field instead.`);const r=t.data();if(r===void 0)throw new m("not-found",`Snapshot-based cursors require an existing document — got an empty snapshot for ${t.id??"<unknown>"}.`);return e.map(o=>r[o.field])}function Fe(t,e){const n=o=>Fe(t,{...e,...o});return{[gt]:e,where(o,s,i){return xt(s),n({filters:[...e.filters,{kind:"where",field:o,op:s,value:w(i)}]})},applyFilter(o){return n({filters:[...e.filters,wt(o)]})},orderBy(o,s="asc"){return n({orders:[...e.orders,{field:o,direction:s}]})},limit(o){return n({limitCount:o,limitFromEnd:!1})},limitToLast(o){return n({limitCount:o,limitFromEnd:!0})},startCursor(o,s){return n({start:{values:[...o],inclusive:s}})},endCursor(o,s){return n({end:{values:[...o],inclusive:s}})},startCursorFromSnapshot(o,s){return n({start:{values:Be(o,e.orders,s?"startAt":"startAfter"),inclusive:s}})},endCursorFromSnapshot(o,s){return n({end:{values:Be(o,e.orders,s?"endAt":"endBefore"),inclusive:s}})},async get(o){K(e);const s=await y(t,{method:"getDocs",source:Y(e)});return mt(t,s)},async aggregate(o){K(e);const s=await y(t,{method:"aggregate",source:Y(e),spec:o});return{data:()=>s.data}},[ke]:(o,s,i)=>(K(e),bt(t,Y(e),o,s,i)),onSnapshot:yt}}function K(t){if(t.limitFromEnd&&t.orders.length===0)throw g("limitToLast() queries require at least one orderBy clause.")}function we(t,e){const n=Fe(t,{source:{__ref:"collection",path:e},filters:[],orders:[],limitFromEnd:!1});return Object.assign(Object.create(Object.getPrototypeOf(n)),n,{id:se(e),path:e,doc(o){const s=o??Xe();return te(t,`${e}/${s}`)},async add(o,s){const i=await y(t,{method:"addDoc",collectionPath:e,data:O(o)});return te(t,i.path)}})}function te(t,e){const n={id:se(e),path:e,get parent(){return we(t,nt(e))},collection(r){const o=`${e}/${r}`;if(!Oe(o))throw g(`collection path must have an odd number of segments: ${o}`);return we(t,o)},async get(r){const o=await y(t,{method:"getDoc",path:e});return pt(n,o)},async set(r,o){await y(t,{method:"setDoc",path:e,data:O(r),...Qe(o)?{options:Qe(o)}:{}})},async update(r,o){await y(t,{method:"updateDoc",path:e,data:O(r)})},async delete(r){await y(t,{method:"deleteDoc",path:e})},[ke]:(r,o,s)=>bt(t,{__ref:"doc",path:e},r,o,s),onSnapshot:yt};return n}function Qe(t){if(!t)return;const e={};return t.merge!==void 0&&(e.merge=t.merge),t.mergeFields!==void 0&&(e.mergeFields=[...t.mergeFields]),e.merge!==void 0||e.mergeFields!==void 0?e:void 0}function Dn(t){const e=[];let n=!1;const r={set(s,i){return o(),e.push({method:"set",path:s.path,data:O(i)}),r},update(s,i){return o(),e.push({method:"update",path:s.path,data:O(i)}),r},delete(s){return o(),e.push({method:"delete",path:s.path}),r},async commit(s){o(),n=!0,await y(t,{method:"batchCommit",writes:e})}};function o(){if(n)throw new m("failed-precondition","WriteBatch has already been committed.")}return r}const jn=5;async function Pn(t,e,n=jn){for(let r=0;r<n;r++){const o=[],s=[],i=()=>{if(s.length>0)throw new m("failed-precondition",Ht)},a={get(d){i();const h=Tn(d);return h!==void 0?c(h):u(d)},set(d,h){return s.push({method:"set",path:d.path,data:O(h)}),a},update(d,h){return s.push({method:"update",path:d.path,data:O(h)}),a},delete(d){return s.push({method:"delete",path:d.path}),a}};async function u(d){const h=await y(t,{method:"getDoc",path:d.path});return o.push({path:h.path??d.path,data:h.exists&&h.data?h.data:null}),pt(d,h)}async function c(d){K(d);const h=await y(t,{method:"getDocs",source:Y(d)});for(const p of h.docs)o.push({path:p.path??p.id,data:p.data??null});return mt(t,h)}const l=await e(a);try{return await y(t,{method:"txnCommit",reads:o,writes:s}),l}catch(d){if(d instanceof m&&d.code==="aborted")continue;throw d}}throw new m("aborted",`Transaction failed after ${n} attempts due to repeated conflicts. Another writer is concurrently updating the same documents.`)}function ue(t,e){return new m("unimplemented",`SandboxFirestore.${t} is not available on a remote sandbox — its return value is synchronous and the data lives in the browser worker. ${e}`)}function St(t,e){const n={sandbox:t,lens:e};return{collection(r){if(!Oe(r))throw g(`collection path must have an odd number of segments: ${r}`);return we(n,r)},doc(r){if(!tt(r))throw g(`document path must have an even number of segments: ${r}`);return te(n,r)},collectionGroup(r){if(r.length===0||r.includes("/"))throw g(`collection group id must be a single non-empty segment with no '/': ${r}`);return Fe(n,{source:{__ref:"group",collectionId:r},filters:[],orders:[],limitFromEnd:!1})},batch(){return Dn(n)},runTransaction(r,o){return Pn(n,r,o?.maxAttempts)},setRules(){throw ue("setRules","Deploy rules asynchronously through the relay instead: `await sandbox.channel.op({ method: 'setFirestoreRules', source })`.")},seed(){throw ue("seed","The relay has no atomic seed op; write seed docs through this handle (`db.doc(path).set(data)` / `db.batch()`), or drive `sandbox.channel.op({ method: 'admin.setDocument', path, data })` per document.")},snapshot(){throw ue("snapshot","Read the worker state asynchronously instead: `await sandbox.channel.op({ method: 'admin.readState' })`.")}}}const He=new WeakMap;function Et(t){const e=He.get(t);if(e)return e;const n=ve(t.sandbox)?St(t.sandbox,In(t.auth)):ee(ht(t),t);return He.set(t,n),n}function In(t){return t==null?{mode:"anon"}:t.token===void 0?{mode:"as",uid:t.uid}:{mode:"as",uid:t.uid,token:t.token}}const Je=new WeakMap;function qn(t){const e=Vn(t)?t:t.withAuth(null),n=Je.get(e);if(n)return n;const r=e.operationContext.authLens.mode==="admin"?e:Re(e,{source:e.operationContext.source,authLens:{mode:"admin"},...e.operationContext.planId===void 0?{}:{planId:e.operationContext.planId}}),o=ve(r.sandbox)?St(r.sandbox,{mode:"admin"}):ee(ht(r,!0),r,!0);return Je.set(e,o),o}function Vn(t){return t!==null&&typeof t=="object"&&"sandbox"in t&&typeof t.withAuth=="function"}const Q=Symbol("pyric/firestore/followsCurrentUser"),de=Symbol("pyric/firestore/authSessionScope");function Nn(t){if(t===null||typeof t!="object")return null;const e=t;return typeof e.path=="string"&&e.path.length>0?e.path:typeof e.collectionPath=="string"&&e.collectionPath.length>0?e.collectionPath:null}function zn(t){if(t!==null&&typeof t=="object"){const e=t.snapshotConstraints;if(typeof e=="function")return e.call(t)}}function Mn(t){if(t===null||typeof t!="object")return!1;const e=t;return typeof e.path=="string"&&e.path.length>0&&e.parent!==void 0&&e.parent!==null}function Ct(t,e,n,r,o){let s={},i,a;if(typeof e=="object"&&e!==null&&typeof e.next!="function"&&typeof e.error!="function"&&typeof e.complete!="function"){if(s=e,typeof n=="function")i=n,a=r;else if(typeof n=="object"&&n!==null){const b=n;i=b.next,a=b.error}}else if(typeof e=="function")i=e,a=n;else if(typeof e=="object"&&e!==null){const b=e;i=b.next,a=b.error}if(!i&&!a)throw new TypeError("onSnapshot: missing handler. Provide a callback, or an observer with a `next` and/or `error` method.");const c=kn(t);if(c){if(s[Q]===!0)throw new m("unimplemented","onSnapshot: live (follows-current-user) listeners are not supported on a remote sandbox — remote listeners are frozen to the identity of the context that created the ref.");return c(s,i,a)}const l=$n(t),d=F(l.sandbox);let h;if(Mn(t))h={kind:"doc",path:t.path};else{const b=Nn(t);if(b===null)throw new TypeError("onSnapshot: could not determine the collection path for this Query. Pass a CollectionReference or a Query produced by `.where`/`.orderBy`/`.limit` on a CollectionReference obtained from `getFirestore(ctx)`.");const I=zn(t);h={kind:"query",collection:b,constraints:I}}const p=s[Q]===!0,De=s[de],Ft=t[lt]===!0;if(Q in s){const{[Q]:b,...I}=s;s=I}if(de in s){const{[de]:b,...I}=s;s=I}const je=()=>d.addSnapshotListener(h,i??(()=>{}),s,l.auth,a,p,Ft,De);return l.sandbox.runWithProvenance?.(Ce(l.operationContext),je)??je()}function $n(t){if(t===null||typeof t!="object")throw new TypeError("onSnapshot: reference must be a DocumentReference or Query.");const e=t[dt];if(!e)throw new TypeError("onSnapshot: reference was not produced by a sandbox context — pass a ref obtained from `getFirestore(ctx)`.");return e}En(Ct);Fn(Ct);class C{bytes;constructor(e){this.bytes=e,v(this,Jt(e)),oe(this,Object.freeze({type:"bytes",values:Object.freeze(Array.from(e))}),()=>new C(e.slice()))}static fromBase64String(e){const n=atob(e),r=new Uint8Array(n.length);for(let o=0;o<n.length;o+=1)r[o]=n.charCodeAt(o);return new C(r)}static fromUint8Array(e){return new C(e.slice())}toBase64(){let e="";for(const n of this.bytes)e+=String.fromCharCode(n);return btoa(e)}toUint8Array(){return this.bytes.slice()}toString(){return`Bytes(base64: ${this.toBase64()})`}isEqual(e){return e instanceof C&&this.bytes.length===e.bytes.length&&this.bytes.every((n,r)=>n===e.bytes[r])}toJSON(){return{type:"firestore/bytes/1.0",bytes:this.toBase64()}}static fromJSON(e){const n=e;if(n.type!=="firestore/bytes/1.0"||typeof n.bytes!="string")throw new TypeError("Invalid Bytes JSON value.");return C.fromBase64String(n.bytes)}}class V{lat;lng;constructor(e,n){if(this.lat=e,this.lng=n,!Number.isFinite(e)||e<-90||e>90)throw new TypeError("Latitude must be a number between -90 and 90.");if(!Number.isFinite(n)||n<-180||n>180)throw new TypeError("Longitude must be a number between -180 and 180.");v(this,_("geo-point",String(e),"\0",String(n))),oe(this,Object.freeze({type:"geo-point",latitude:e,longitude:n}),()=>new V(e,n))}get latitude(){return this.lat}get longitude(){return this.lng}isEqual(e){return e instanceof V&&this.lat===e.lat&&this.lng===e.lng}toJSON(){return{latitude:this.lat,longitude:this.lng,type:"firestore/geoPoint/1.0"}}static fromJSON(e){const n=e;if(n.type!=="firestore/geoPoint/1.0"||typeof n.latitude!="number"||typeof n.longitude!="number")throw new TypeError("Invalid GeoPoint JSON value.");return new V(n.latitude,n.longitude)}}class T{_values;constructor(e){this._values=e,oe(this,Object.freeze({type:"vector",values:Object.freeze(e.slice())}),()=>new T(e.slice()))}static create(e){if(!e.every(n=>typeof n=="number"))throw new TypeError("Vector values must be numbers.");return new T(e.slice())}toArray(){return this._values.slice()}isEqual(e){const n=e instanceof T?e._values:void 0;return n!==void 0&&this._values.length===n.length&&this._values.every((r,o)=>r===n[o])}toJSON(){return{type:"firestore/vectorValue/1.0",vectorValues:this.toArray()}}static fromJSON(e){const n=e;if(n.type!=="firestore/vectorValue/1.0"||!Array.isArray(n.vectorValues)||!n.vectorValues.every(r=>typeof r=="number"))throw new TypeError("Invalid VectorValue JSON value.");return T.create(n.vectorValues)}}function Ln(t=[]){return T.create(t)}function ir(){return Gt.serverTimestamp()}const S=Symbol("pyric/firestore/target");function L(t){return t.kind==="sandbox"?t.db:t.getDb()}const ie=new WeakMap;function Un(t,e){return ie.set(t,e),t}const U=new WeakMap;function Wn(t,e,n){return ie.set(t,e),U.set(t,n),t}function Te(t,e){const n=P(e);if(t.kind==="sandbox")return n;const r=U.get(n);if(!r)throw new TypeError("pyric/firestore: live ref missing rebuild closure — was it produced by a factory in this package?");return r(L(t))}function Bn(t,e){const n=P(e);if(t.kind==="sandbox")return n;const r=U.get(n);if(!r)throw new TypeError("pyric/firestore: live collection ref missing rebuild closure.");return r(L(t))}function ar(t,e){const n=P(e);if(t.kind==="sandbox")return n;const r=U.get(n);if(!r)throw new TypeError("pyric/firestore: live query missing rebuild closure.");return r(L(t))}function M(t,e,n){return e.kind==="sandbox-live"?Wn(t,e,n):Un(t,e)}function cr(t){const e=P(t),n=U.get(e);return n||(()=>e)}function j(t){const e=Qn(t);return e.assertUsable?.(),e}function Qn(t){let e;if(S in t?e=t[S]:e=ie.get(t),!e)throw new TypeError("pyric/firestore: unrecognized reference — was it produced by a factory in this package?");return e}const Rt=new WeakMap,vt=new WeakMap;function _e(t){return Rt.get(t)}function P(t){return vt.get(t)??t}function Ot(t,e,n){const r={id:t.id??"",path:t.path??""};return Rt.set(r,n),vt.set(r,t),ie.set(r,e),r}function Hn(t){return t instanceof Xt?Array.from(t.value):null}function xe(t){if(t instanceof Yt)return C.fromUint8Array(t.data);if(t instanceof Kt)return new V(t.lat,t.lng);const e=Hn(t);if(e)return Ln(e);if(Array.isArray(t))return t.map(xe);if(t!==null&&typeof t=="object"){const n=Object.getPrototypeOf(t);if(n===Object.prototype||n===null){const r={};for(const[o,s]of Object.entries(t))r[o]=xe(s);return r}}return t}function ur(t){if(t!==void 0)return xe(t)}class D extends Error{code;customData;name="FirebaseError";constructor(e,n,r){super(n),this.code=e,this.customData=r,Object.setPrototypeOf(this,new.target.prototype)}}Object.defineProperty(D,"name",{value:"FirebaseError"});let ne;function dr(t){ne=t}function At(t){return ne?.resolve(t,!1)}function Jn(){if(!ne)throw new TypeError("No default sandbox app registry is installed - import and initialize pyric/app before calling a service factory without an app.");return ne.defaultApp()}function Gn(t){if(t===void 0)return Gn(Jn());const e=At(t);if(e)return e.service("firestore/default",()=>{const{sandbox:n,session:r}=e,o=le();e.onDelete(o.terminate);const s=e.authScope;s&&o.own(r.onCurrentUserChanged(a=>{F(n).reevaluateLiveListeners(a,s)}));const i={kind:"sandbox-live",sandbox:n,getDb:Ge(n,()=>r.currentUser),authScope:s,...o};return{[S]:i,app:t}});if(kt(t)){const r={kind:"sandbox",db:Et(t),sandbox:t.sandbox,...le()};return{[S]:r}}if(Kn(t)){const n={kind:"sandbox-live",sandbox:t,getDb:Ge(t),...le()};return{[S]:n}}throw new TypeError("pyric/firestore is a sandbox-only mirror. Package resolution must leave firebase/firestore unchanged for production; activate pyric sandbox or @pyric/cli/register before importing to select the sandbox.")}function le(){let t=!1;const e=new Set;return{own(n){return t?(n(),()=>{}):(e.add(n),()=>e.delete(n))},assertUsable(){if(t)throw new D("failed-precondition","The client has already been terminated.")},terminate(){if(!t){t=!0;for(const n of e)n();e.clear()}}}}function Yn(t){const e=At(t);if(e){e.assertAlive();const i=e.sandbox;return Yn(Re(i.withAuth(null),{source:{kind:"app"},authLens:{mode:"admin"}}))}const n=kt(t)?t:t.withAuth(null),r=n.sandbox,s={kind:"sandbox",db:qn(n),sandbox:r};return{[S]:s}}function kt(t){return t instanceof Zt}function Kn(t){if(t===null||typeof t!="object")return!1;const e=t;return typeof e.withAuth=="function"&&typeof e.onCurrentUserChanged=="function"&&"currentUser"in e&&"admin"in e}function Ge(t,e=()=>t.currentUser){return()=>{const n=Re(t.withAuth(e()),{source:{kind:"app"},authLens:{mode:"app-session"}});return Et(n)}}class Xn{started=!1;networkEnabled=!0;persistenceMode;pending=new Set;cachedPaths=new Set;cachedQueries=new WeakSet;syncObservers=new Set;syncScheduled=!1;markStarted(){this.started=!0}enablePersistence(e){if(this.started||this.persistenceMode!==void 0)throw new D("failed-precondition","Firestore has already been started and persistence can no longer be enabled.");this.persistenceMode=e,this.started=!0}disableNetwork(){this.started=!0,this.networkEnabled=!1}async enableNetwork(){this.started=!0,this.networkEnabled=!0;for(const e of this.pending)e.localDone&&e.resolve(e.value);await Promise.all([...this.pending].map(e=>e.promise))}runWrite(e,n){if(this.started=!0,n&&this.cachedPaths.add(n),this.networkEnabled)return e();let r,o;const s=new Promise((a,u)=>{r=a,o=u}),i={path:n,promise:s,localDone:!1,resolve:r,reject:o};return this.pending.add(i),e().then(a=>{i.localDone=!0,i.value=a,this.networkEnabled&&i.resolve(a)},a=>i.reject(a)),s.then(()=>this.pending.delete(i),()=>this.pending.delete(i)),s}waitForPendingWrites(){this.started=!0;const e=[...this.pending].map(n=>n.promise);return Promise.all(e).then(()=>{})}cachePath(e){this.started=!0,this.cachedPaths.add(e)}assertPathCached(e){if(this.started=!0,!this.cachedPaths.has(e))throw new D("unavailable","Failed to get document from cache. (However, this document may exist on the server.)")}cacheQuery(e){this.started=!0,this.cachedQueries.add(e)}hasCachedQuery(e){return this.started=!0,this.cachedQueries.has(e)||this.cachedPaths.size>0}snapshotMetadata(e){return Object.freeze({fromCache:!this.networkEnabled,hasPendingWrites:[...this.pending].some(n=>n.path===e)})}querySnapshotMetadata(){return Object.freeze({fromCache:!this.networkEnabled,hasPendingWrites:this.pending.size>0})}addSnapshotsInSyncObserver(e){return this.syncObservers.add(e),e(),()=>this.syncObservers.delete(e)}notifySnapshotDelivered(){this.syncScheduled||this.syncObservers.size===0||(this.syncScheduled=!0,queueMicrotask(()=>{this.syncScheduled=!1;for(const e of[...this.syncObservers])e()}))}}const Ye=new WeakMap;function $(t){let e=Ye.get(t);return e||(e=new Xn,Ye.set(t,e)),e}function he(t,e,n){return v(t,_("reference",e)),Ee(t,e,n),t}function lr(t,...e){const n=j(t),r=S in t,o=r?void 0:_e(t),s=L(n);if(r){if(e.length===0)throw new TypeError("doc(db, path) requires at least one path segment.");const l=e.join("/"),d=s.doc(l),h=M(d,n,p=>p.doc(l));return he(h,l,n)}const i=P(t),a=e.length===0?i.doc():i.doc(e.join("/")),u=a.path,c=M(a,n,l=>l.doc(u));if(o){const l=Ot(c,n,o);return he(l,u,n)}return he(c,u,n)}function hr(t,...e){const n=j(t),r=S in t;if(e.length===0)throw new TypeError("collection() requires at least one path segment.");if(r){const u=e.join("/"),c=L(n).collection(u);return M(c,n,l=>l.collection(u))}const o=P(t),s=e.join("/"),i=o.collection(s),a=i.path;return M(i,n,u=>u.collection(a))}function Zn(t){return t instanceof Error&&"code"in t&&typeof t.code=="string"}function er(t){if(t instanceof D||!Zn(t))return t;const e={};t.denialContext!==void 0&&(e.denialContext=t.denialContext),t.remediation!==void 0&&(e.remediation=t.remediation);const n=new D(t.code,t.message,Object.keys(e).length===0?void 0:e);return t.denialContext!==void 0&&Object.assign(n,{denialContext:t.denialContext}),t.remediation!==void 0&&Object.assign(n,{remediation:t.remediation}),n}async function ae(t){try{return await t()}catch(e){throw er(e)}}async function fr(t,e,n){const r=j(t),o=_e(t),s=o?o.toFirestore(e):e;return $(r).runWrite(()=>ae(()=>Te(r,t).set(s,n)),t.path)}async function pr(t,e){const n=j(t);return $(n).runWrite(()=>ae(()=>Te(n,t).update(e)),t.path)}async function mr(t){const e=j(t);return $(e).runWrite(()=>ae(()=>Te(e,t).delete()),t.path)}async function yr(t,e){const n=j(t),r=_e(t),o=r?r.toFirestore(e):e,s=await $(n).runWrite(async()=>{const u=await ae(()=>Bn(n,t).add(o));return $(n).cachePath(u.path),u}),i=s.path,a=M(s,n,u=>u.doc(i));if(v(a,_("reference",i)),Ee(a,i,n,a),r){const u=Ot(a,n,r);return en(a,u),u}return a}export{de as A,hr as B,yr as C,pr as D,mr as E,Q as F,dr as G,Yn as H,Jn as I,At as J,V as K,C as L,ir as M,rr as S,or as a,$ as b,rn as c,lr as d,_e as e,ur as f,Gn as g,L as h,sr as i,U as j,ot as k,st as l,nn as m,j as n,Te as o,ar as p,cr as q,vt as r,fr as s,Un as t,P as u,nr as v,er as w,M as x,Ot as y,D as z};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Pyric Studio</title><link rel="icon" type="image/svg+xml" href="/__pyric/ui/pyric-logo.svg"><script>
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><title>Pyric Studio</title><link rel="icon" type="image/svg+xml" href="/__pyric/ui/pyric-logo.svg"><script>
|
|
2
2
|
try {
|
|
3
3
|
localStorage.setItem('pyric-studio:theme', 'dark');
|
|
4
4
|
} catch {}
|
|
5
5
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
6
|
-
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/_studio_.
|
|
7
|
-
<
|
|
6
|
+
</script><link rel="preconnect" href="https://use.typekit.net" crossorigin><link rel="stylesheet" href="https://use.typekit.net/jwb3ycu.css"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/_studio_.BnfYCKoI.css">
|
|
7
|
+
<style>html{scrollbar-gutter:stable}
|
|
8
|
+
</style>
|
|
9
|
+
<link rel="stylesheet" href="/__pyric/ui/_astro/app.pMSnN6Qw.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.B52xr-DK.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{"data-astro-cid-w5sm6wlz":[0,true]}" ssr client="only" opts="{"name":"StudioApp","value":"react"}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
|
|
8
10
|
Starting Pyric Studio…
|
|
9
11
|
</p><template data-astro-template="fallback"><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
|
|
10
12
|
Starting Pyric Studio…
|
|
Binary file
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Rhythm audit · Pyric Studio</title><meta name="description" content="Internal spacing audit page — every block adjacency in one column."><link rel="icon" type="image/svg+xml" href="/__pyric/ui/pyric-logo.svg"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/
|
|
2
|
-
<link rel="stylesheet" href="/__pyric/ui/_astro/_slug_.C6QfVOxJ.css"></head> <body> <header class="studio__bar" data-astro-cid-mw7aashj> <div class="studio__bar-inner" data-astro-cid-mw7aashj> <a class="studio__brand" href="/__pyric/ui/" aria-label="Pyric Studio" data-astro-cid-mw7aashj>Pyric</a> <nav class="studio__nav" aria-label="Studio tabs" data-astro-cid-mw7aashj> <a class="studio__nav-tab" href="/__pyric/ui/" data-astro-cid-mw7aashj> Home </a><a class="studio__nav-tab" href="/__pyric/ui/firestore" data-astro-cid-mw7aashj> Firestore </a><a class="studio__nav-tab" href="/__pyric/ui/auth" data-astro-cid-mw7aashj> Auth </a><a class="studio__nav-tab" href="/__pyric/ui/rtdb" data-astro-cid-mw7aashj> RTDB </a><a class="studio__nav-tab" href="/__pyric/ui/storage" data-astro-cid-mw7aashj> Storage </a><a class="studio__nav-tab" href="/__pyric/ui/traffic" data-astro-cid-mw7aashj> Traffic </a><a class="studio__nav-tab" href="/__pyric/ui/settings" data-astro-cid-mw7aashj> Settings </a><a class="studio__nav-tab" href="/__pyric/ui/docs" aria-current="page" data-astro-cid-mw7aashj> Docs </a> </nav> </div> </header> <div class="docs-shell" data-astro-cid-mw7aashj> <details class="docs-nav-drawer" data-astro-cid-mw7aashj> <summary data-astro-cid-mw7aashj>Navigate</summary> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <script type="module">let i=null;function d(t){return i||(i=fetch(t).then(e=>e.json())),i}function l(t,e){return t.title.toLowerCase().includes(e)||t.excerpt.toLowerCase().includes(e)?!0:t.headings.some(r=>r.text.toLowerCase().includes(e))}function u(t,e,r){if(e.innerHTML="",t.length===0){e.hidden=!1;{const n=document.createElement("li");n.className="docs-search-empty",n.textContent="No matches",e.appendChild(n)}return}e.hidden=!1;for(const n of t.slice(0,8)){const s=document.createElement("li"),c=document.createElement("a");c.href=n.path;const o=document.createElement("span");o.className="docs-search-result-title",o.textContent=n.title;const a=document.createElement("span");a.className="docs-search-result-meta",a.textContent=[n.group,n.section].filter(Boolean).join(" / "),c.append(o,a),s.appendChild(c),e.appendChild(s)}}document.querySelectorAll(".docs-search").forEach(t=>{const e=t.dataset.indexUrl,r=t.querySelector(".docs-search-input"),n=t.querySelector(".docs-search-results");r.addEventListener("input",async()=>{const s=r.value.trim().toLowerCase();if(!s){n.hidden=!0,n.innerHTML="";return}const{pages:c}=await d(e);r.value.trim().toLowerCase()===s&&u(c.filter(o=>l(o,s)),n)}),r.addEventListener("focus",()=>{d(e),n.childElementCount>0&&(n.hidden=!1)}),r.addEventListener("keydown",s=>{s.key==="Escape"&&(r.value="",n.hidden=!0,n.innerHTML="",r.blur())}),r.addEventListener("blur",()=>{window.setTimeout(()=>{n.hidden=!0},150)})});document.addEventListener("keydown",t=>{if(!(t.metaKey||t.ctrlKey)||t.key.toLowerCase()!=="k")return;const e=[...document.querySelectorAll(".docs-search-input")].find(r=>r.offsetParent!==null);e&&(t.preventDefault(),e.focus(),e.select())});</script> <details class="docs-nav-group"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/vite/"> Vite </a> </li><li> <a href="/__pyric/ui/docs/get-started/nextjs/"> Next.js </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Secure & debug</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/secure/secure-it-with-rules/"> Security Rules </a> </li><li> <a href="/__pyric/ui/docs/secure/simulate-and-lint/"> Simulate and lint before you deploy </a> </li><li> <a href="/__pyric/ui/docs/secure/write-a-rules-test-suite/"> Write a rules test suite </a> </li><li> <a href="/__pyric/ui/docs/secure/read-a-denial/"> Read a denial and understand it </a> </li><li> <a href="/__pyric/ui/docs/secure/rules-standard-library/"> The rules standard library </a> </li><li> <a href="/__pyric/ui/docs/secure/rtdb-rules-in-typescript/"> RTDB rules in TypeScript </a> </li><li> <a href="/__pyric/ui/docs/secure/firestore-rules-limits/"> Rules limits </a> </li><li> <a href="/__pyric/ui/docs/secure/audit-your-rules/"> Audit rules and data </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading"> Examples </h2> <ul> <li> <a href="/__pyric/ui/docs/examples/chess/"> Chess showcase </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Observe & shape</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/observe/see-whats-happening/"> Traffic & rule verdicts </a> </li><li> <a href="/__pyric/ui/docs/observe/resolve-runtime-status/"> Runtime status </a> </li><li> <a href="/__pyric/ui/docs/observe/shape-your-data/"> Seed, snapshot, replay </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Ship & test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Conformance scores </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> </details> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <details class="docs-nav-group" id="nav-group-overview"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-get-started"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/vite/"> Vite </a> </li><li> <a href="/__pyric/ui/docs/get-started/nextjs/"> Next.js </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-build"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-secure-debug"> <summary>Secure & debug</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/secure/secure-it-with-rules/"> Security Rules </a> </li><li> <a href="/__pyric/ui/docs/secure/simulate-and-lint/"> Simulate and lint before you deploy </a> </li><li> <a href="/__pyric/ui/docs/secure/write-a-rules-test-suite/"> Write a rules test suite </a> </li><li> <a href="/__pyric/ui/docs/secure/read-a-denial/"> Read a denial and understand it </a> </li><li> <a href="/__pyric/ui/docs/secure/rules-standard-library/"> The rules standard library </a> </li><li> <a href="/__pyric/ui/docs/secure/rtdb-rules-in-typescript/"> RTDB rules in TypeScript </a> </li><li> <a href="/__pyric/ui/docs/secure/firestore-rules-limits/"> Rules limits </a> </li><li> <a href="/__pyric/ui/docs/secure/audit-your-rules/"> Audit rules and data </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading" id="nav-section-secure-debug-examples"> Examples </h2> <ul> <li> <a href="/__pyric/ui/docs/examples/chess/"> Chess showcase </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-observe-shape"> <summary>Observe & shape</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/observe/see-whats-happening/"> Traffic & rule verdicts </a> </li><li> <a href="/__pyric/ui/docs/observe/resolve-runtime-status/"> Runtime status </a> </li><li> <a href="/__pyric/ui/docs/observe/shape-your-data/"> Seed, snapshot, replay </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-ship-test"> <summary>Ship & test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-work-with-an-agent"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-trust"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-conformance"> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Conformance scores </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group" id="nav-group-reference"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> <main class="doc-main" data-astro-cid-mw7aashj> <article class="prose" data-astro-cid-mw7aashj> <h1 id="rhythm-audit">Rhythm audit</h1>
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><title>Rhythm audit · Pyric Studio</title><meta name="description" content="Internal spacing audit page — every block adjacency in one column."><link rel="icon" type="image/svg+xml" href="/__pyric/ui/pyric-logo.svg"><link rel="preconnect" href="https://use.typekit.net" crossorigin><link rel="stylesheet" href="https://use.typekit.net/jwb3ycu.css"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/index.UkCBr-_0.css">
|
|
2
|
+
<link rel="stylesheet" href="/__pyric/ui/_astro/_slug_.Da5VLhW1.css">
|
|
3
|
+
<style>:root{--rhythm-para: .875rem;--rhythm-group: 1.5rem;--rhythm-subsection: 2.25rem;--rhythm-section: 3.25rem;--rhythm-list: calc(var(--rhythm-para) * .75)}.prose>*,.prose blockquote>*,.prose li>*,.prose .compat-stat>*{margin-block:0}.prose>*+*{margin-block-start:var(--rhythm-group)}.prose>*+h2{margin-block-start:var(--rhythm-section)}.prose>*+h3,.prose>*+h4{margin-block-start:var(--rhythm-subsection)}.prose>h1+*,.prose>h2+*,.prose>h3+*,.prose>h4+*{margin-block-start:var(--rhythm-para)}.prose>p+p{margin-block-start:var(--rhythm-para)}.prose>p+ul,.prose>p+ol,.prose>p+blockquote{margin-block-start:var(--rhythm-para)}.prose li+li{margin-block-start:var(--rhythm-list)}.prose li>ul,.prose li>ol{margin-block-start:var(--rhythm-list)}.prose li>*+*,.prose blockquote>*+*{margin-block-start:var(--rhythm-para)}
|
|
4
|
+
html{scrollbar-gutter:stable}
|
|
5
|
+
</style></head> <body> <header class="studio__chrome"> <div class="studio__bar"> <div class="studio__chrome-inner"> <nav class="studio__nav" aria-label="Site"> <a class="studio__brand" href="/__pyric/ui/" aria-label="Pyric">pyric</a> <a class="studio__nav-tab" href="/__pyric/ui/"> Home </a><a class="studio__nav-tab" href="/__pyric/ui/docs" aria-current="page"> Docs </a><a class="studio__nav-tab" href="/__pyric/ui/studio"> Studio </a> </nav> </div> </div> </header> <div class="docs-shell" data-astro-cid-mw7aashj> <details class="docs-nav-drawer" data-astro-cid-mw7aashj> <summary data-astro-cid-mw7aashj>Navigate</summary> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <script type="module">let i=null;function d(t){return i||(i=fetch(t).then(e=>e.json())),i}function l(t,e){return t.title.toLowerCase().includes(e)||t.excerpt.toLowerCase().includes(e)?!0:t.headings.some(r=>r.text.toLowerCase().includes(e))}function u(t,e,r){if(e.innerHTML="",t.length===0){e.hidden=!1;{const n=document.createElement("li");n.className="docs-search-empty",n.textContent="No matches",e.appendChild(n)}return}e.hidden=!1;for(const n of t.slice(0,8)){const s=document.createElement("li"),c=document.createElement("a");c.href=n.path;const o=document.createElement("span");o.className="docs-search-result-title",o.textContent=n.title;const a=document.createElement("span");a.className="docs-search-result-meta",a.textContent=[n.group,n.section].filter(Boolean).join(" / "),c.append(o,a),s.appendChild(c),e.appendChild(s)}}document.querySelectorAll(".docs-search").forEach(t=>{const e=t.dataset.indexUrl,r=t.querySelector(".docs-search-input"),n=t.querySelector(".docs-search-results");r.addEventListener("input",async()=>{const s=r.value.trim().toLowerCase();if(!s){n.hidden=!0,n.innerHTML="";return}const{pages:c}=await d(e);r.value.trim().toLowerCase()===s&&u(c.filter(o=>l(o,s)),n)}),r.addEventListener("focus",()=>{d(e),n.childElementCount>0&&(n.hidden=!1)}),r.addEventListener("keydown",s=>{s.key==="Escape"&&(r.value="",n.hidden=!0,n.innerHTML="",r.blur())}),r.addEventListener("blur",()=>{window.setTimeout(()=>{n.hidden=!0},150)})});document.addEventListener("keydown",t=>{if(!(t.metaKey||t.ctrlKey)||t.key.toLowerCase()!=="k")return;const e=[...document.querySelectorAll(".docs-search-input")].find(r=>r.offsetParent!==null);e&&(t.preventDefault(),e.focus(),e.select())});</script> <details class="docs-nav-group"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/vite/"> Vite </a> </li><li> <a href="/__pyric/ui/docs/get-started/nextjs/"> Next.js </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Secure & debug</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/secure/secure-it-with-rules/"> Security Rules </a> </li><li> <a href="/__pyric/ui/docs/secure/simulate-and-lint/"> Simulate and lint before you deploy </a> </li><li> <a href="/__pyric/ui/docs/secure/write-a-rules-test-suite/"> Write a rules test suite </a> </li><li> <a href="/__pyric/ui/docs/secure/read-a-denial/"> Read a denial and understand it </a> </li><li> <a href="/__pyric/ui/docs/secure/rules-standard-library/"> The rules standard library </a> </li><li> <a href="/__pyric/ui/docs/secure/rtdb-rules-in-typescript/"> RTDB rules in TypeScript </a> </li><li> <a href="/__pyric/ui/docs/secure/firestore-rules-limits/"> Rules limits </a> </li><li> <a href="/__pyric/ui/docs/secure/audit-your-rules/"> Audit rules and data </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading"> Examples </h2> <ul> <li> <a href="/__pyric/ui/docs/examples/chess/"> Chess showcase </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Observe & shape</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/observe/see-whats-happening/"> Traffic & rule verdicts </a> </li><li> <a href="/__pyric/ui/docs/observe/resolve-runtime-status/"> Runtime status </a> </li><li> <a href="/__pyric/ui/docs/observe/shape-your-data/"> Seed, snapshot, replay </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Ship & test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Conformance scores </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-reference-api/"> pyric-admin </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/reference/cli/"> CLI </a> </li> </ul> </div> </div> </details> </nav> </details> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <details class="docs-nav-group" id="nav-group-overview"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-get-started"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/vite/"> Vite </a> </li><li> <a href="/__pyric/ui/docs/get-started/nextjs/"> Next.js </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-build"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-secure-debug"> <summary>Secure & debug</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/secure/secure-it-with-rules/"> Security Rules </a> </li><li> <a href="/__pyric/ui/docs/secure/simulate-and-lint/"> Simulate and lint before you deploy </a> </li><li> <a href="/__pyric/ui/docs/secure/write-a-rules-test-suite/"> Write a rules test suite </a> </li><li> <a href="/__pyric/ui/docs/secure/read-a-denial/"> Read a denial and understand it </a> </li><li> <a href="/__pyric/ui/docs/secure/rules-standard-library/"> The rules standard library </a> </li><li> <a href="/__pyric/ui/docs/secure/rtdb-rules-in-typescript/"> RTDB rules in TypeScript </a> </li><li> <a href="/__pyric/ui/docs/secure/firestore-rules-limits/"> Rules limits </a> </li><li> <a href="/__pyric/ui/docs/secure/audit-your-rules/"> Audit rules and data </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading" id="nav-section-secure-debug-examples"> Examples </h2> <ul> <li> <a href="/__pyric/ui/docs/examples/chess/"> Chess showcase </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-observe-shape"> <summary>Observe & shape</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/observe/see-whats-happening/"> Traffic & rule verdicts </a> </li><li> <a href="/__pyric/ui/docs/observe/resolve-runtime-status/"> Runtime status </a> </li><li> <a href="/__pyric/ui/docs/observe/shape-your-data/"> Seed, snapshot, replay </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-ship-test"> <summary>Ship & test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-work-with-an-agent"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-trust"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-conformance"> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Conformance scores </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group" id="nav-group-reference"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-reference-api/"> pyric-admin </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/reference/cli/"> CLI </a> </li> </ul> </div> </div> </details> </nav> <main class="doc-main" data-astro-cid-mw7aashj> <article class="prose" data-astro-cid-mw7aashj> <h1 id="rhythm-audit">Rhythm audit</h1>
|
|
3
6
|
<p>This page exists to audit vertical rhythm in one screenshot. Every
|
|
4
7
|
adjacency styled by <code>rhythm.css</code> appears below, labeled. It is excluded
|
|
5
8
|
from the nav, llms.txt, and the search index.</p>
|
|
@@ -43,7 +46,7 @@ uses the default, since code blocks are their own block group):</p>
|
|
|
43
46
|
<span class="line"><span style="color:#E5C07B"> console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">finding</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">code</span><span style="color:#ABB2BF">, </span><span style="color:#E5C07B">finding</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">severity</span><span style="color:#ABB2BF">);</span></span>
|
|
44
47
|
<span class="line"><span style="color:#ABB2BF">}</span></span></code></pre>
|
|
45
48
|
<p>The paragraph after a code block returns at the group beat. Inline
|
|
46
|
-
code like <code>pyric
|
|
49
|
+
code like <code>pyric sandbox --persist</code> participates in the line, not the
|
|
47
50
|
rhythm.</p>
|
|
48
51
|
<pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#7F848E;font-style:italic"># pre + pre: two code blocks at the group beat</span></span>
|
|
49
52
|
<span class="line"><span style="color:#61AFEF">pyric</span><span style="color:#98C379"> firestore</span><span style="color:#98C379"> rules</span><span style="color:#98C379"> lint</span><span style="color:#98C379"> firestore.rules</span></span></code></pre>
|
|
@@ -53,7 +53,7 @@ for (const finding of result.findings) {
|
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
The paragraph after a code block returns at the group beat. Inline
|
|
56
|
-
code like `pyric
|
|
56
|
+
code like `pyric sandbox --persist` participates in the line, not the
|
|
57
57
|
rhythm.
|
|
58
58
|
```bash
|
|
59
59
|
# pre + pre: two code blocks at the group beat
|