@pyric/cli 0.1.0-alpha.16 → 0.1.0-alpha.18
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 +42 -12
- package/dist/assurance/.generated/conformance-verdicts.d.ts +80 -72
- package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
- package/dist/assurance/.generated/conformance-verdicts.js +80 -72
- package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
- package/dist/bridge/server/bridge.d.ts.map +1 -1
- package/dist/bridge/server/bridge.js +4 -2
- package/dist/bridge/server/bridge.js.map +1 -1
- package/dist/bridge/server/peer.d.ts.map +1 -1
- package/dist/bridge/server/peer.js +1 -0
- package/dist/bridge/server/peer.js.map +1 -1
- package/dist/cli/database-rules.d.ts.map +1 -1
- package/dist/cli/database-rules.js +6 -4
- package/dist/cli/database-rules.js.map +1 -1
- package/dist/cli/dev-runner.d.ts +9 -0
- package/dist/cli/dev-runner.d.ts.map +1 -1
- package/dist/cli/dev-runner.js +18 -0
- package/dist/cli/dev-runner.js.map +1 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +62 -6
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/verify.js +2 -2
- package/dist/cli/verify.js.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
- package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use.js +1 -1
- package/dist/conformance/.generated/can-i-use.js.map +1 -1
- package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
- package/dist/conformance/.generated/conformance-docs.js +1 -1
- package/dist/conformance/.generated/conformance-docs.js.map +1 -1
- package/dist/functions-rtdb/child.d.ts +1 -0
- package/dist/functions-rtdb/child.d.ts.map +1 -1
- package/dist/functions-rtdb/child.js +11 -1
- package/dist/functions-rtdb/child.js.map +1 -1
- package/dist/functions-rtdb/development-runtime.d.ts +1 -0
- package/dist/functions-rtdb/development-runtime.d.ts.map +1 -1
- package/dist/functions-rtdb/development-runtime.js +1 -0
- package/dist/functions-rtdb/development-runtime.js.map +1 -1
- package/dist/next/client-aliases.d.ts +11 -0
- package/dist/next/client-aliases.d.ts.map +1 -0
- package/dist/next/client-aliases.js +38 -0
- package/dist/next/client-aliases.js.map +1 -0
- package/dist/next/dev-rewrites.d.ts +9 -0
- package/dist/next/dev-rewrites.d.ts.map +1 -0
- package/dist/next/dev-rewrites.js +53 -0
- package/dist/next/dev-rewrites.js.map +1 -0
- package/dist/next/guard.d.ts +15 -0
- package/dist/next/guard.d.ts.map +1 -0
- package/dist/next/guard.js +52 -0
- package/dist/next/guard.js.map +1 -0
- package/dist/next/index.d.ts +12 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +11 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/runtime-env.d.ts +7 -0
- package/dist/next/runtime-env.d.ts.map +1 -0
- package/dist/next/runtime-env.js +13 -0
- package/dist/next/runtime-env.js.map +1 -0
- package/dist/next/server-external-packages.d.ts +10 -0
- package/dist/next/server-external-packages.d.ts.map +1 -0
- package/dist/next/server-external-packages.js +34 -0
- package/dist/next/server-external-packages.js.map +1 -0
- package/dist/next/target-url.d.ts +11 -0
- package/dist/next/target-url.d.ts.map +1 -0
- package/dist/next/target-url.js +36 -0
- package/dist/next/target-url.js.map +1 -0
- package/dist/next/turbopack-config.d.ts +9 -0
- package/dist/next/turbopack-config.d.ts.map +1 -0
- package/dist/next/turbopack-config.js +39 -0
- package/dist/next/turbopack-config.js.map +1 -0
- package/dist/next/types.d.ts +40 -0
- package/dist/next/types.d.ts.map +1 -0
- package/dist/next/types.js +5 -0
- package/dist/next/types.js.map +1 -0
- package/dist/next/webpack-config.d.ts +11 -0
- package/dist/next/webpack-config.d.ts.map +1 -0
- package/dist/next/webpack-config.js +51 -0
- package/dist/next/webpack-config.js.map +1 -0
- package/dist/next/with-pyric.d.ts +22 -0
- package/dist/next/with-pyric.d.ts.map +1 -0
- package/dist/next/with-pyric.js +49 -0
- package/dist/next/with-pyric.js.map +1 -0
- package/dist/register/app-bridge.d.ts +9 -0
- package/dist/register/app-bridge.d.ts.map +1 -0
- package/dist/register/app-bridge.js +56 -0
- package/dist/register/app-bridge.js.map +1 -0
- package/dist/register/hooks.d.ts.map +1 -1
- package/dist/register/hooks.js +6 -1
- package/dist/register/hooks.js.map +1 -1
- package/dist/register/index.js +1 -1
- package/dist/register/index.js.map +1 -1
- package/dist/register/mapping.d.ts +3 -1
- package/dist/register/mapping.d.ts.map +1 -1
- package/dist/register/mapping.js +41 -5
- package/dist/register/mapping.js.map +1 -1
- package/dist/remote/index.d.ts.map +1 -1
- package/dist/remote/index.js +5 -3
- package/dist/remote/index.js.map +1 -1
- package/dist/rtdb/rules-json.d.ts +12 -0
- package/dist/rtdb/rules-json.d.ts.map +1 -1
- package/dist/rtdb/rules-json.js +98 -0
- package/dist/rtdb/rules-json.js.map +1 -1
- package/dist/serve/bundler.d.ts.map +1 -1
- package/dist/serve/bundler.js +2 -0
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/discovery.d.ts +4 -4
- package/dist/serve/discovery.d.ts.map +1 -1
- package/dist/serve/discovery.js +59 -38
- package/dist/serve/discovery.js.map +1 -1
- package/dist/serve/entries/app-ai-passthrough.d.ts +8 -0
- package/dist/serve/entries/app-ai-passthrough.d.ts.map +1 -0
- package/dist/serve/entries/app-ai-passthrough.js +59 -0
- package/dist/serve/entries/app-ai-passthrough.js.map +1 -0
- package/dist/serve/entries/auth-helper-core.js +9 -7
- package/dist/serve/entries/auth-helper-core.js.map +1 -1
- package/dist/serve/entries/auth.d.ts +1 -0
- package/dist/serve/entries/auth.d.ts.map +1 -1
- package/dist/serve/entries/auth.js +1 -0
- package/dist/serve/entries/auth.js.map +1 -1
- package/dist/serve/entries/bridge-url.d.ts.map +1 -1
- package/dist/serve/entries/bridge-url.js +7 -2
- package/dist/serve/entries/bridge-url.js.map +1 -1
- package/dist/serve/entries/database.d.ts +1 -0
- package/dist/serve/entries/database.d.ts.map +1 -1
- package/dist/serve/entries/database.js +1 -0
- package/dist/serve/entries/database.js.map +1 -1
- package/dist/serve/entries/firestore.d.ts +1 -0
- package/dist/serve/entries/firestore.d.ts.map +1 -1
- package/dist/serve/entries/firestore.js +1 -0
- package/dist/serve/entries/firestore.js.map +1 -1
- package/dist/serve/entries/init.d.ts.map +1 -1
- package/dist/serve/entries/init.js +4 -2
- package/dist/serve/entries/init.js.map +1 -1
- package/dist/serve/entries/runtime.js +23 -0
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/entries/storage.d.ts +2 -0
- package/dist/serve/entries/storage.d.ts.map +1 -1
- package/dist/serve/entries/storage.js +9 -2
- package/dist/serve/entries/storage.js.map +1 -1
- package/dist/serve/html-injection.d.ts +3 -1
- package/dist/serve/html-injection.d.ts.map +1 -1
- package/dist/serve/html-injection.js +27 -1
- package/dist/serve/html-injection.js.map +1 -1
- package/dist/serve/rules.js +2 -2
- package/dist/serve/rules.js.map +1 -1
- package/dist/serve/runtime/chip-config.d.ts +1 -1
- package/dist/serve/runtime/chip-config.d.ts.map +1 -1
- package/dist/serve/runtime/chip-config.js +16 -8
- package/dist/serve/runtime/chip-config.js.map +1 -1
- package/dist/serve/runtime/chip.d.ts.map +1 -1
- package/dist/serve/runtime/chip.js +24 -3
- package/dist/serve/runtime/chip.js.map +1 -1
- package/dist/serve/runtime/status.d.ts +1 -0
- package/dist/serve/runtime/status.d.ts.map +1 -1
- package/dist/serve/runtime/status.js +71 -13
- 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 +30 -2
- package/dist/serve/sandbox-session.js.map +1 -1
- package/dist/serve/site-ui/_astro/_slug_.C6QfVOxJ.css +1 -0
- package/dist/serve/site-ui/_astro/_studio_.B6b_G1ev.css +1 -0
- package/dist/serve/site-ui/_astro/app.B7pBzz5N.js +11 -0
- package/dist/serve/site-ui/_astro/app.boex5SnQ.js +1 -0
- package/dist/serve/site-ui/_astro/{chess-showcase.CnoxIle5.js → chess-showcase.DDqE-QGe.js} +1 -1
- package/dist/serve/site-ui/_astro/{example-runtime.R1d96L6-.js → example-runtime.B0byNQOl.js} +1 -1
- package/dist/serve/site-ui/_astro/index.uwPvjH_1.js +1 -0
- package/dist/serve/site-ui/_astro/reads.Dh3z0fJy.js +1 -0
- package/dist/serve/site-ui/_astro/sandbox-controls.C2IqtoSw.js +1252 -0
- package/dist/serve/site-ui/_astro/{seed.EyI3VYEu.js → seed.CCmj00su.js} +1 -1
- package/dist/serve/site-ui/auth/index.html +2 -2
- package/dist/serve/site-ui/docs/_rhythm/index.html +1 -1
- package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +48 -13
- package/dist/serve/site-ui/docs/agent/set-up-your-agent.md +74 -11
- package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +1 -1
- package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +1 -1
- package/dist/serve/site-ui/docs/ai-compat/index.html +85 -85
- package/dist/serve/site-ui/docs/ai-compat.md +84 -84
- package/dist/serve/site-ui/docs/api-reference/index.html +3 -1
- package/dist/serve/site-ui/docs/api-reference.md +2 -0
- package/dist/serve/site-ui/docs/app-compat/index.html +28 -28
- package/dist/serve/site-ui/docs/app-compat.md +27 -27
- package/dist/serve/site-ui/docs/auth-compat/index.html +122 -122
- package/dist/serve/site-ui/docs/auth-compat.md +121 -121
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +19 -2
- package/dist/serve/site-ui/docs/build/ai-logic.md +28 -0
- package/dist/serve/site-ui/docs/build/authentication/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-storage/index.html +1 -1
- package/dist/serve/site-ui/docs/build/realtime-database/index.html +1 -1
- package/dist/serve/site-ui/docs/conformance-scores/index.html +25 -10
- package/dist/serve/site-ui/docs/conformance-scores.md +23 -8
- package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +6 -6
- package/dist/serve/site-ui/docs/create-pyric-reference-api.md +5 -5
- package/dist/serve/site-ui/docs/database-compat/index.html +200 -448
- package/dist/serve/site-ui/docs/database-compat.md +198 -461
- package/dist/serve/site-ui/docs/examples/chess/index.html +2 -2
- package/dist/serve/site-ui/docs/examples/index.html +1 -1
- package/dist/serve/site-ui/docs/firestore-compat/index.html +186 -195
- package/dist/serve/site-ui/docs/firestore-compat.md +184 -195
- package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +15 -15
- package/dist/serve/site-ui/docs/functions-rtdb-compat.md +14 -14
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/nextjs/index.html +57 -0
- package/dist/serve/site-ui/docs/get-started/nextjs.md +86 -0
- package/dist/serve/site-ui/docs/get-started/start-building/index.html +38 -9
- package/dist/serve/site-ui/docs/get-started/start-building.md +62 -7
- package/dist/serve/site-ui/docs/get-started/vite/index.html +26 -0
- package/dist/serve/site-ui/docs/get-started/vite.md +45 -0
- package/dist/serve/site-ui/docs/index.json +711 -111
- package/dist/serve/site-ui/docs/messaging-compat/index.html +56 -56
- package/dist/serve/site-ui/docs/messaging-compat.md +55 -55
- package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +1 -1
- package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +1 -1
- package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +1 -1
- package/dist/serve/site-ui/docs/overview/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +39 -13
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api.md +28 -2
- package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +38 -6
- package/dist/serve/site-ui/docs/pyric-admin-database-reference-api.md +43 -0
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +9 -1
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api.md +8 -0
- package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +27 -4
- package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api.md +23 -0
- package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +123 -0
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api.md +99 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +127 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api.md +174 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +570 -326
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api.md +198 -6
- package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +2971 -627
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api.md +2873 -517
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +43 -3
- package/dist/serve/site-ui/docs/pyric-rules-reference-api.md +8 -0
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +26 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api.md +24 -0
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +77 -9
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api.md +28 -13
- package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +240 -63
- package/dist/serve/site-ui/docs/pyric-storage-reference-api.md +164 -5
- package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +1 -10
- package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api.md +0 -9
- package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +2 -1
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api.md +1 -0
- package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/rules-compat/index.html +87 -79
- package/dist/serve/site-ui/docs/rules-compat.md +86 -78
- package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
- package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +1 -1
- package/dist/serve/site-ui/docs/ship/test-in-node/index.html +1 -1
- package/dist/serve/site-ui/docs/storage-compat/index.html +119 -125
- package/dist/serve/site-ui/docs/storage-compat.md +118 -124
- package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
- package/dist/serve/site-ui/docs/tutorial/index.html +2 -1
- package/dist/serve/site-ui/docs/tutorial.md +2 -0
- package/dist/serve/site-ui/examples/chess/index.html +1 -1
- package/dist/serve/site-ui/examples/firestore-first-write/index.html +1 -1
- package/dist/serve/site-ui/firestore/index.html +2 -2
- package/dist/serve/site-ui/index.html +2 -2
- package/dist/serve/site-ui/llms.txt +8 -4
- package/dist/serve/site-ui/rtdb/index.html +2 -2
- package/dist/serve/site-ui/settings/index.html +2 -2
- package/dist/serve/site-ui/storage/index.html +2 -2
- package/dist/serve/site-ui/traffic/index.html +2 -2
- package/dist/serve/vite-ai-config.d.ts +4 -1
- package/dist/serve/vite-ai-config.d.ts.map +1 -1
- package/dist/serve/vite-ai-config.js +83 -19
- package/dist/serve/vite-ai-config.js.map +1 -1
- package/dist/serve/vite-functions-development.d.ts.map +1 -1
- package/dist/serve/vite-functions-development.js +1 -0
- package/dist/serve/vite-functions-development.js.map +1 -1
- package/dist/serve/vite-generation-rules-watch.d.ts.map +1 -1
- package/dist/serve/vite-generation-rules-watch.js +61 -14
- package/dist/serve/vite-generation-rules-watch.js.map +1 -1
- package/dist/serve/vite-module-swap.d.ts +4 -1
- package/dist/serve/vite-module-swap.d.ts.map +1 -1
- package/dist/serve/vite-module-swap.js +106 -21
- package/dist/serve/vite-module-swap.js.map +1 -1
- package/dist/serve/vite-plugin.d.ts.map +1 -1
- package/dist/serve/vite-plugin.js +45 -5
- package/dist/serve/vite-plugin.js.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/resumable.d.ts +30 -0
- package/dist/serve/worker/client/resumable.d.ts.map +1 -0
- package/dist/serve/worker/client/resumable.js +416 -0
- package/dist/serve/worker/client/resumable.js.map +1 -0
- package/dist/serve/worker/client/rtdb-references.d.ts.map +1 -1
- package/dist/serve/worker/client/rtdb-references.js +4 -1
- package/dist/serve/worker/client/rtdb-references.js.map +1 -1
- package/dist/serve/worker/client/storage.d.ts +16 -2
- package/dist/serve/worker/client/storage.d.ts.map +1 -1
- package/dist/serve/worker/client/storage.js +57 -6
- package/dist/serve/worker/client/storage.js.map +1 -1
- package/dist/serve/worker/host/rules.d.ts.map +1 -1
- package/dist/serve/worker/host/rules.js +2 -1
- package/dist/serve/worker/host/rules.js.map +1 -1
- package/dist/serve/worker/index.d.ts +1 -1
- package/dist/serve/worker/index.d.ts.map +1 -1
- package/dist/serve/worker/index.js +1 -1
- package/dist/serve/worker/index.js.map +1 -1
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +31 -0
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/package.json +15 -4
- package/src/assurance/.generated/conformance-verdicts.ts +80 -72
- package/src/bridge/server/bridge.ts +5 -2
- package/src/bridge/server/peer.ts +2 -1
- package/src/cli/cli.test.ts +14 -0
- package/src/cli/database-rules.ts +6 -4
- package/src/cli/dev-runner.ts +21 -0
- package/src/cli/serve.ts +63 -5
- package/src/cli/verify.ts +2 -2
- package/src/conformance/.generated/can-i-use-browser.ts +1 -1
- package/src/conformance/.generated/can-i-use.ts +1 -1
- package/src/conformance/.generated/conformance-docs.ts +1 -1
- package/src/functions-rtdb/child.ts +12 -1
- package/src/functions-rtdb/development-runtime.ts +2 -0
- package/src/next/client-aliases.ts +39 -0
- package/src/next/dev-rewrites.ts +77 -0
- package/src/next/guard.ts +61 -0
- package/src/next/index.ts +16 -0
- package/src/next/runtime-env.ts +18 -0
- package/src/next/server-external-packages.ts +43 -0
- package/src/next/target-url.ts +46 -0
- package/src/next/turbopack-config.ts +54 -0
- package/src/next/types.ts +47 -0
- package/src/next/webpack-config.ts +71 -0
- package/src/next/with-pyric.ts +58 -0
- package/src/register/app-bridge.ts +79 -0
- package/src/register/hooks.ts +6 -1
- package/src/register/index.ts +1 -1
- package/src/register/mapping.ts +50 -4
- package/src/remote/index.ts +7 -5
- package/src/rtdb/rules-json.ts +110 -0
- package/src/serve/bundler.ts +2 -0
- package/src/serve/discovery.ts +65 -44
- package/src/serve/entries/app-ai-passthrough.ts +83 -0
- package/src/serve/entries/auth-helper-core.ts +9 -7
- package/src/serve/entries/auth.ts +1 -0
- package/src/serve/entries/bridge-url.ts +9 -2
- package/src/serve/entries/database.ts +1 -0
- package/src/serve/entries/firestore.ts +1 -0
- package/src/serve/entries/init.ts +5 -2
- package/src/serve/entries/runtime.ts +25 -0
- package/src/serve/entries/storage.ts +12 -1
- package/src/serve/html-injection.ts +26 -1
- package/src/serve/rules.ts +2 -2
- package/src/serve/runtime/chip-config.ts +19 -6
- package/src/serve/runtime/chip.ts +24 -3
- package/src/serve/runtime/status.ts +76 -13
- package/src/serve/sandbox-session.ts +31 -2
- package/src/serve/vite-ai-config.ts +86 -19
- package/src/serve/vite-functions-development.ts +1 -0
- package/src/serve/vite-generation-rules-watch.ts +67 -15
- package/src/serve/vite-module-swap.ts +121 -21
- package/src/serve/vite-plugin.ts +44 -6
- package/src/serve/vite-sandbox-generation.ts +1 -1
- package/src/serve/worker/client/resumable.ts +506 -0
- package/src/serve/worker/client/rtdb-references.ts +4 -1
- package/src/serve/worker/client/storage.ts +77 -7
- package/src/serve/worker/host/rules.ts +2 -1
- package/src/serve/worker/index.ts +4 -0
- package/src/serve/worker/serve-init.ts +30 -0
- package/dist/serve/site-ui/_astro/_slug_.BoKXYrnu.css +0 -1
- package/dist/serve/site-ui/_astro/_studio_.BtYpqe1A.css +0 -1
- package/dist/serve/site-ui/_astro/app.4HeLP_FA.js +0 -109
- package/dist/serve/site-ui/_astro/app.BgSnCR5G.js +0 -1
- package/dist/serve/site-ui/_astro/index.BJiOemMX.js +0 -1
- package/dist/serve/site-ui/_astro/reads.BrKNdKAa.js +0 -1
- package/dist/serve/site-ui/_astro/sandbox-controls.BTb3EbQp.js +0 -1153
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>pyric/firestore compatibility matrix · Pyric Studio</title><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/_slug_.CXh9yTP1.css">
|
|
2
|
-
<link rel="stylesheet" href="/__pyric/ui/_astro/_slug_.BoKXYrnu.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/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" open> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Public API coverage </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/" aria-current="page"> 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-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> </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/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" open> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Public API coverage </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/" aria-current="page"> 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-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> </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> <nav class="docs-breadcrumbs" aria-label="Breadcrumb"> <ol> <li> <a href="/__pyric/ui/docs/conformance-scores/">Conformance</a> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span aria-current="page">Firestore</span> </li> </ol> </nav> <article class="prose" data-astro-cid-mw7aashj> <!-- Generated from the conformance model (registry rows + surface contracts). Do not edit by hand; run bun run compat:generate. -->
|
|
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" open> <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/" aria-current="page"> 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" open> <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/" aria-current="page"> 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> <nav class="docs-breadcrumbs" aria-label="Breadcrumb"> <ol> <li> <a href="/__pyric/ui/docs/conformance-scores/">Conformance</a> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span aria-current="page">Firestore</span> </li> </ol> </nav> <article class="prose" data-astro-cid-mw7aashj> <!-- Generated from the conformance model (registry rows + surface contracts). Do not edit by hand; run bun run compat:generate. -->
|
|
3
3
|
<h1 id="pyricfirestore-compatibility-matrix"><code>pyric/firestore</code> compatibility matrix</h1>
|
|
4
4
|
<div class="compat-stat">
|
|
5
5
|
<p class="compat-stat-figure">
|
|
6
|
-
<span class="compat-stat-pct">
|
|
6
|
+
<span class="compat-stat-pct">100%</span>
|
|
7
7
|
<span class="compat-stat-label">of the public API supported</span>
|
|
8
8
|
</p>
|
|
9
9
|
<div class="compat-meters">
|
|
10
|
-
<span class="compat-meter-track"><span class="compat-meter-fill" style="width:
|
|
10
|
+
<span class="compat-meter-track"><span class="compat-meter-fill" style="width: 100%"></span></span>
|
|
11
11
|
</div>
|
|
12
|
-
<p class="compat-stat-denom">
|
|
12
|
+
<p class="compat-stat-denom">182 of 182 public API</p>
|
|
13
13
|
</div>
|
|
14
14
|
<h2 id="status-legend">Status legend</h2>
|
|
15
15
|
<div class="compat-key">
|
|
@@ -20,74 +20,74 @@
|
|
|
20
20
|
<div class="compat-list">
|
|
21
21
|
<details class="compat-row" data-status="ok">
|
|
22
22
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getFirestore(target)</code><span class="compat-sub"><span class="compat-behavior"><code>getFirestore(ctx)</code> returns a tagged sandbox-target handle (frozen identity)</span></span></span></summary>
|
|
23
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
23
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
24
24
|
</details>
|
|
25
25
|
<details class="compat-row" data-status="ok">
|
|
26
26
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getFirestore(target)</code><span class="compat-sub"><span class="compat-behavior"><code>getFirestore(sandbox)</code> returns a tagged sandbox-live handle (per-op identity)</span></span></span></summary>
|
|
27
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code></div></div>
|
|
27
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a></div></div>
|
|
28
28
|
</details>
|
|
29
29
|
<details class="compat-row" data-status="ok">
|
|
30
30
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getFirestore(target)</code><span class="compat-sub"><span class="compat-behavior">Package resolution owns production selection: direct <code>pyric/firestore</code> rejects a real <code>FirebaseApp</code>, while inactive canonical <code>firebase/firestore</code> imports remain the real Firebase SDK</span></span></span></summary>
|
|
31
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:package-resolution.test.ts</code>, <code>node-register:register-child.test.ts</code> (inactive canonical imports are not rewritten)</div></div>
|
|
31
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/package-resolution.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:package-resolution.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>node-register:register-child.test.ts</code></a> (inactive canonical imports are not rewritten)</div></div>
|
|
32
32
|
</details>
|
|
33
33
|
<details class="compat-row" data-status="ok">
|
|
34
34
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getFirestore(target)</code><span class="compat-sub"><span class="compat-behavior"><code>getFirestore(undefined)</code> is wrapped in the playground preview to default to the sandbox; production's unactivated canonical SDK still throws <code>app/no-app</code>, while a direct mirror call rejects the missing sandbox owner</span></span></span></summary>
|
|
35
|
-
<div class="compat-evidence"><div class="compat-probe"><code>playground:firestore-bare-getfirestore</code> — fix from PR #397 + oracle: <code>packages/conformance/observations/firestore/firestore-bare-getfirestore-no-default-app.json</code> (<code>code: 'app/no-app'</code> against blockingfun, fb-js-sdk 12.13.0 — confirms prod throw shape)</div>
|
|
35
|
+
<div class="compat-evidence"><div class="compat-probe"><code>playground:firestore-bare-getfirestore</code> — fix from PR #397 + oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-bare-getfirestore-no-default-app.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-bare-getfirestore-no-default-app.json</code></a> (<code>code: 'app/no-app'</code> against blockingfun, fb-js-sdk 12.13.0 — confirms prod throw shape)</div>
|
|
36
36
|
<div class="compat-note">(wrap)</div></div>
|
|
37
37
|
</details>
|
|
38
38
|
<details class="compat-row" data-status="ok">
|
|
39
39
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getFirestore(target)</code><span class="compat-sub"><span class="compat-behavior">Two <code>getFirestore(sandbox)</code> calls share state (same underlying <code>LocalEnvironment</code>)</span></span></span></summary>
|
|
40
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("two handles share the same sandbox")</div></div>
|
|
40
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("two handles share the same sandbox")</div></div>
|
|
41
41
|
</details>
|
|
42
42
|
<details class="compat-row" data-status="ok">
|
|
43
43
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getFirestore(target)</code><span class="compat-sub"><span class="compat-behavior">Handle dispatch by <code>TARGET_SYMBOL</code> brand — refs/queries route to their owning target via <code>refToTarget</code> WeakMap</span></span></span></summary>
|
|
44
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("throws TypeError for refs not produced by this package")</div></div>
|
|
44
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("throws TypeError for refs not produced by this package")</div></div>
|
|
45
45
|
</details>
|
|
46
46
|
</div>
|
|
47
47
|
<h2 id="path-constructors--doc--collection--collectiongroup">Path constructors — <code>doc</code> / <code>collection</code> / <code>collectionGroup</code></h2>
|
|
48
48
|
<div class="compat-list">
|
|
49
49
|
<details class="compat-row" data-status="ok">
|
|
50
50
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior"><code>doc(db, path)</code> returns a tagged <code>DocumentReference</code> with <code>id</code> / <code>path</code></span></span></span></summary>
|
|
51
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
51
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
52
52
|
</details>
|
|
53
53
|
<details class="compat-row" data-status="ok">
|
|
54
54
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior"><code>doc(db, 'a', 'b', 'c', 'd')</code> joins variadic path segments</span></span></span></summary>
|
|
55
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
55
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L330" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
56
56
|
</details>
|
|
57
57
|
<details class="compat-row" data-status="ok">
|
|
58
58
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior"><code>collection(db, path)</code> returns a tagged <code>CollectionReference</code></span></span></span></summary>
|
|
59
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
59
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
60
60
|
</details>
|
|
61
61
|
<details class="compat-row" data-status="ok">
|
|
62
62
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior"><code>doc(coll, id)</code> appends under a collection ref</span></span></span></summary>
|
|
63
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
63
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
64
64
|
</details>
|
|
65
65
|
<details class="compat-row" data-status="ok">
|
|
66
66
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior"><code>doc(coll)</code> (no id) mints an auto-id <code>DocumentReference</code></span></span></span></summary>
|
|
67
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
67
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
68
68
|
</details>
|
|
69
69
|
<details class="compat-row" data-status="ok">
|
|
70
70
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior"><code>collection(docRef, name)</code> builds a subcollection ref</span></span></span></summary>
|
|
71
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
71
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
72
72
|
</details>
|
|
73
73
|
<details class="compat-row" data-status="ok">
|
|
74
74
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior"><code>collectionGroup(db, id)</code> returns a query spanning every collection with that id</span></span></span></summary>
|
|
75
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("gathers documents across every parent collection")</div></div>
|
|
75
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("gathers documents across every parent collection")</div></div>
|
|
76
76
|
</details>
|
|
77
77
|
<details class="compat-row" data-status="ok">
|
|
78
78
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior">Unknown ref (not produced by this package) → <code>TypeError</code> with "unrecognized reference"</span></span></span></summary>
|
|
79
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
79
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
80
80
|
</details>
|
|
81
81
|
<details class="compat-row" data-status="ok">
|
|
82
82
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Path constructors</code><span class="compat-sub"><span class="compat-behavior">Held doc/coll ref under <code>sandbox-live</code> re-resolves to the chainable under the current user at op time (via rebuild closure)</span></span></span></summary>
|
|
83
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("held doc ref re-resolves under the current user")</div></div>
|
|
83
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L26" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("held doc ref re-resolves under the current user")</div></div>
|
|
84
84
|
</details>
|
|
85
85
|
</div>
|
|
86
86
|
<h2 id="getdocref--single-doc-read"><code>getDoc(ref)</code> — single-doc read</h2>
|
|
87
87
|
<div class="compat-list">
|
|
88
88
|
<details class="compat-row" data-status="ok">
|
|
89
89
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDoc(ref)</code><span class="compat-sub"><span class="compat-behavior">Returns <code>DocumentSnapshot</code> with <code>id</code>, <code>exists</code> (method form), <code>data()</code></span></span></span></summary>
|
|
90
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
90
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
91
91
|
</details>
|
|
92
92
|
<details class="compat-row" data-status="ok">
|
|
93
93
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDoc(ref)</code><span class="compat-sub"><span class="compat-behavior"><code>snap.exists</code> is normalized to method form (<code>snap.exists()</code> returns boolean) to match the modular SDK</span></span></span></summary>
|
|
@@ -95,92 +95,92 @@
|
|
|
95
95
|
</details>
|
|
96
96
|
<details class="compat-row" data-status="ok">
|
|
97
97
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDoc(ref)</code><span class="compat-sub"><span class="compat-behavior"><code>snap.data()</code> returns <code>undefined</code> for missing doc</span></span></span></summary>
|
|
98
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
98
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
99
99
|
</details>
|
|
100
100
|
<details class="compat-row" data-status="ok">
|
|
101
101
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDoc(ref)</code><span class="compat-sub"><span class="compat-behavior"><code>snap.ref</code> is tagged so it routes through <code>targetOf</code> in follow-up ops</span></span></span></summary>
|
|
102
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
102
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
103
103
|
</details>
|
|
104
104
|
<details class="compat-row" data-status="ok">
|
|
105
105
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDoc(ref)</code><span class="compat-sub"><span class="compat-behavior">Re-evaluates rules under current user on every call (sandbox-live) — read denied throws <code>permission-denied</code></span></span></span></summary>
|
|
106
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("doc read denied when current user lacks read access"), oracle: <code>packages/conformance/observations/firestore/firestore-read-denied-error-code.json</code> (prod <code>getDoc</code> on a denied path throws a <code>FirebaseError</code> with <code>.code === 'permission-denied'</code>, <code>.message === 'Missing or insufficient permissions.'</code>, <code>instanceof Error</code>)</div></div>
|
|
106
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L28" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("doc read denied when current user lacks read access"), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-read-denied-error-code.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-read-denied-error-code.json</code></a> (prod <code>getDoc</code> on a denied path throws a <code>FirebaseError</code> with <code>.code === 'permission-denied'</code>, <code>.message === 'Missing or insufficient permissions.'</code>, <code>instanceof Error</code>) (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/query-rules-enforcement.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>query-rules-enforcement.test.ts</code></a>)</div></div>
|
|
107
107
|
</details>
|
|
108
108
|
<details class="compat-row" data-status="ok">
|
|
109
109
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDoc(ref)</code><span class="compat-sub"><span class="compat-behavior">Rules-denied modular writes throw a <code>FirebaseError('permission-denied')</code>, matching the production error class and code</span></span></span></summary>
|
|
110
|
-
<div class="compat-evidence"><div class="compat-probe">Oracle-locked by <code>packages/conformance/observations/firestore/firestore-rules-denied-error.json</code>: prod throws a <code>FirebaseError</code> (name + constructor name both <code>FirebaseError</code>), <code>.code === 'permission-denied'</code>, and the value is an <code>instanceof FirebaseError</code> and <code>Error</code>. <code>oracle:firestore#21</code> replays the same denied modular write against the sandbox and asserts that complete class shape
|
|
110
|
+
<div class="compat-evidence"><div class="compat-probe">Oracle-locked by <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-rules-denied-error.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-rules-denied-error.json</code></a>: prod throws a <code>FirebaseError</code> (name + constructor name both <code>FirebaseError</code>), <code>.code === 'permission-denied'</code>, and the value is an <code>instanceof FirebaseError</code> and <code>Error</code>. <code>oracle:firestore#21</code> replays the same denied modular write against the sandbox and asserts that complete class shape. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L569" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
111
111
|
</details>
|
|
112
112
|
</div>
|
|
113
113
|
<h2 id="getdocsquery--bulk-read"><code>getDocs(query)</code> — bulk read</h2>
|
|
114
114
|
<div class="compat-list">
|
|
115
115
|
<details class="compat-row" data-status="ok">
|
|
116
116
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDocs(query)</code><span class="compat-sub"><span class="compat-behavior">Returns <code>QuerySnapshot</code> with <code>size</code>, <code>empty</code>, <code>docs</code> (<code>QueryDocumentSnapshot[]</code>)</span></span></span></summary>
|
|
117
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-query</code></div></div>
|
|
117
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-query</code> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L41" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
118
118
|
</details>
|
|
119
119
|
<details class="compat-row" data-status="ok">
|
|
120
120
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDocs(query)</code><span class="compat-sub"><span class="compat-behavior">Each <code>snap.docs[i].ref</code> is tagged for follow-up ops</span></span></span></summary>
|
|
121
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
121
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
122
122
|
</details>
|
|
123
123
|
<details class="compat-row" data-status="ok">
|
|
124
124
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDocs(query)</code><span class="compat-sub"><span class="compat-behavior">Sandbox-live: re-evaluates filters under the current user (different docs visible per identity)</span></span></span></summary>
|
|
125
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("query results re-evaluate under the current user")</div></div>
|
|
125
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("query results re-evaluate under the current user")</div></div>
|
|
126
126
|
</details>
|
|
127
127
|
<details class="compat-row" data-status="ok">
|
|
128
128
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDocs(query)</code><span class="compat-sub"><span class="compat-behavior"><strong>Query reads enforce security rules (FS-B1)</strong> — a deny-all / auth-gated rule set throws <code>permission-denied</code>. Pre-FS-B1 query reads went through the rules-bypassing <code>listDocuments</code> and returned the whole collection.</span></span></span></summary>
|
|
129
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:admin-compat/query-rules-enforcement.test.ts</code> (deny-all + auth-gated <code>getDocs</code>/aggregate), <code>unit:admin-compat/per-op-auth.test.ts</code> ("Query.get enforces rules")</div></div>
|
|
129
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/query-rules-enforcement.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/query-rules-enforcement.test.ts</code></a> (deny-all + auth-gated <code>getDocs</code>/aggregate), <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/per-op-auth.test.ts#L158" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/per-op-auth.test.ts</code></a> ("Query.get enforces rules")</div></div>
|
|
130
130
|
</details>
|
|
131
131
|
<details class="compat-row" data-status="ok">
|
|
132
132
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDocs(query)</code><span class="compat-sub"><span class="compat-behavior"><strong>Enforcement follows production's QUERY-PROOF model (RULES-B11)</strong> — "rules are not filters": a doc-data-dependent <code>list</code> rule (<code>resource.data.visibility == 'public'</code>, <code>resource.data.owner == request.auth.uid</code>) is ALLOWED when the query's <code>where()</code> equalities discharge it and the whole query is <code>permission-denied</code> otherwise — never silently truncated to the readable subset. Per-doc <code>get</code> rules do NOT filter query results (the <code>list</code> rule alone governs queries — granular-operations docs). Applies to <code>getDocs</code>, aggregates, and <code>onSnapshot</code> alike. Pre-fix: rules-as-filters (per-doc <code>get</code> omission) + blanket denial of every doc-data-dependent list, even provable ones.</span></span></span></summary>
|
|
133
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/query-proof-enforcement.test.ts</code> (provable/unprovable getDocs + onSnapshot, owner-pinned uid, get-rules-don't-filter, request.query.limit; verified failing pre-fix), <code>unit:simulator/local-environment.test.ts</code> (Slice 6 — flipped from per-doc-filter assertions); prod truth: firebase.google.com/docs/firestore/security/rules-query</div></div>
|
|
133
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/query-proof-enforcement.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:firestore/query-proof-enforcement.test.ts</code></a> (provable/unprovable getDocs + onSnapshot, owner-pinned uid, get-rules-don't-filter, request.query.limit; verified failing pre-fix), <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/simulator/local-environment.test.ts#L617" target="_blank" rel="noopener noreferrer"><code>unit:simulator/local-environment.test.ts</code></a> (Slice 6 — flipped from per-doc-filter assertions); prod truth: firebase.google.com/docs/firestore/security/rules-query</div></div>
|
|
134
134
|
</details>
|
|
135
135
|
<details class="compat-row" data-status="diverged">
|
|
136
136
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getDocs(query)</code><span class="compat-sub"><span class="compat-behavior">Query-proof <strong>prover scope is conservative, with full accounting and a fail-closed classifier</strong> — user functions are inlined (nested helper-calling-helper and multi-parameter helpers included, matching production's rules-function inlining), and a <code>list</code> rule is provable only when its <strong>entire</strong> doc-dependence reduces to top-level AND-conjunct <code>resource.data.<field> == <literal></code> equalities (with <code>request.auth.uid</code> pinned to the caller) each discharged by a <code>where(field, '==', value)</code> filter. Doc-dependence is decided by a fail-closed classifier: an expression counts as doc-independent only when every node is positively recognized as such — any touch of the <code>resource</code> root in any syntactic form (<code>resource.data</code>, <code>resource['data']</code>, slices, <code>resource.id</code>, path-literal segments feeding <code>exists</code>/<code>get</code> lookups) and any unrecognized node shape classifies doc-dependent. Any doc-dependent conjunct that is not a discharged equality — disjunctions over doc data, inequality/range predicates (<code>resource.data.score > 10</code> + <code>where('score','>',10)</code>), <code>in</code> / negated-<code>in</code> membership checks, <code>get(key, default)</code>, <code>keys().hasOnly(...)</code>, <code>is</code> type checks, nested-path predicates, data-keyed document lookups — makes the whole query conservatively DENY, even when every equality conjunct is discharged; production's prover may allow some of these. Divergence is deny-only: mixed equality + non-equality shapes are rejected up front rather than trusted to a residual evaluation that absent-tolerant predicates could pass vacuously.</span></span></span></summary>
|
|
137
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/query-proof.test.ts</code> (function-inlining provable cases; full-accounting rejections for mixed equality + <code>in</code>/<code>get</code>/<code>hasOnly</code>/range/<code>is</code> shapes in inline and helper form; fail-closed classifier probes: bracket-access <code>resource['data']</code>, <code>exists()</code> keyed by <code>resource.data</code> through path-literal segments, slice access, <code>resource.id</code>, and an unrecognized node shape), <code>unit:firestore/query-proof-enforcement.test.ts</code> (seeded end-to-end denials of the demonstrated attacks: forbidden-field absence check in member and bracket form, banned-owner <code>exists()</code> lookup, slice conjunct — each returning zero documents)</div></div>
|
|
137
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/query-proof.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/query-proof.test.ts</code></a> (function-inlining provable cases; full-accounting rejections for mixed equality + <code>in</code>/<code>get</code>/<code>hasOnly</code>/range/<code>is</code> shapes in inline and helper form; fail-closed classifier probes: bracket-access <code>resource['data']</code>, <code>exists()</code> keyed by <code>resource.data</code> through path-literal segments, slice access, <code>resource.id</code>, and an unrecognized node shape), <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/query-proof-enforcement.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:firestore/query-proof-enforcement.test.ts</code></a> (seeded end-to-end denials of the demonstrated attacks: forbidden-field absence check in member and bracket form, banned-owner <code>exists()</code> lookup, slice conjunct — each returning zero documents)</div></div>
|
|
138
138
|
</details>
|
|
139
139
|
<details class="compat-row" data-status="ok">
|
|
140
140
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDocs(query)</code><span class="compat-sub"><span class="compat-behavior">Empty result for a collection with no docs (<code>size === 0</code>, <code>empty === true</code>)</span></span></span></summary>
|
|
141
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
141
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
142
142
|
</details>
|
|
143
143
|
</div>
|
|
144
144
|
<h2 id="setdocref-data-options--full-write"><code>setDoc(ref, data[, options])</code> — full write</h2>
|
|
145
145
|
<div class="compat-list">
|
|
146
146
|
<details class="compat-row" data-status="ok">
|
|
147
147
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setDoc(ref, data[, options])</code><span class="compat-sub"><span class="compat-behavior">No options → replaces the existing document entirely</span></span></span></summary>
|
|
148
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("setDoc default replaces")</div></div>
|
|
148
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("setDoc default replaces")</div></div>
|
|
149
149
|
</details>
|
|
150
150
|
<details class="compat-row" data-status="ok">
|
|
151
151
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setDoc(ref, data[, options])</code><span class="compat-sub"><span class="compat-behavior"><code>{ merge: true }</code> → <strong>deep-merges nested maps</strong> (FS-B6), preserving unspecified fields at every level: <code>setDoc({a:{b:2}}, {merge:true})</code> over <code>{a:{c:1}}</code> yields <code>{a:{b:2,c:1}}</code>. Pre-FS-B6 the wrapper shallow-replaced the whole <code>a</code> map.</span></span></span></summary>
|
|
152
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>unit:admin-compat/field-path-merge.test.ts</code> (FS-B6 nested deep-merge; verified failing pre-fix)</div></div>
|
|
152
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/field-path-merge.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/field-path-merge.test.ts</code></a> (FS-B6 nested deep-merge; verified failing pre-fix)</div></div>
|
|
153
153
|
</details>
|
|
154
154
|
<details class="compat-row" data-status="ok">
|
|
155
155
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setDoc(ref, data[, options])</code><span class="compat-sub"><span class="compat-behavior"><code>{ mergeFields: [...] }</code> → writes only the listed <strong>dot-separated field paths</strong> into the existing doc (FS-B6); other keys in <code>data</code> are ignored, other fields in the existing doc preserved. <code>mergeFields: ['a.b']</code> reaches into a nested map.</span></span></span></summary>
|
|
156
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>unit:admin-compat/field-path-merge.test.ts</code> (dotted mergeField); mask edges (delete/transform outside mask, empty mask, deleteField in mask): <code>unit:upstream-write-aggregate-probes.test.ts</code></div></div>
|
|
156
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/field-path-merge.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/field-path-merge.test.ts</code></a> (dotted mergeField); mask edges (delete/transform outside mask, empty mask, deleteField in mask): <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-write-aggregate-probes.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:upstream-write-aggregate-probes.test.ts</code></a></div></div>
|
|
157
157
|
</details>
|
|
158
158
|
<details class="compat-row" data-status="ok">
|
|
159
159
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setDoc(ref, data[, options])</code><span class="compat-sub"><span class="compat-behavior">Passing both <code>merge</code> and <code>mergeFields</code> — <code>mergeFields</code> wins on sandbox (matches JS SDK effective behavior)</span></span></span></summary>
|
|
160
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
160
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
161
161
|
</details>
|
|
162
162
|
<details class="compat-row" data-status="ok">
|
|
163
163
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setDoc(ref, data[, options])</code><span class="compat-sub"><span class="compat-behavior">Sentinels (<code>serverTimestamp</code>, <code>increment</code>, <code>arrayUnion</code>, <code>arrayRemove</code>, <code>deleteField</code>) resolve in the same call</span></span></span></summary>
|
|
164
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-sentinels</code>, oracle: <code>packages/conformance/observations/firestore/firestore-row-30-sentinels-in-setdoc.json</code> — <code>setDoc({createdAt: serverTimestamp(), count: 5, tags: ['a']})</code> followed by <code>getDoc</code> returns <code>createdAt</code> as a <code>Timestamp</code> instance (constructor name <code>Timestamp</code>, has <code>seconds</code> + <code>nanoseconds</code>), <code>count === 5</code> (number), <code>tags === ['a']</code>. Sentinels resolve server-side and the follow-up read sees concrete values, not the sentinel placeholders.</div></div>
|
|
164
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-sentinels</code>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-30-sentinels-in-setdoc.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-30-sentinels-in-setdoc.json</code></a> — <code>setDoc({createdAt: serverTimestamp(), count: 5, tags: ['a']})</code> followed by <code>getDoc</code> returns <code>createdAt</code> as a <code>Timestamp</code> instance (constructor name <code>Timestamp</code>, has <code>seconds</code> + <code>nanoseconds</code>), <code>count === 5</code> (number), <code>tags === ['a']</code>. Sentinels resolve server-side and the follow-up read sees concrete values, not the sentinel placeholders.</div></div>
|
|
165
165
|
</details>
|
|
166
166
|
<details class="compat-row" data-status="ok">
|
|
167
167
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setDoc(ref, data[, options])</code><span class="compat-sub"><span class="compat-behavior">Converter (via <code>withConverter</code>) runs <code>toFirestore(data)</code> before the write</span></span></span></summary>
|
|
168
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("withConverter on a DocumentReference round-trips")</div></div>
|
|
168
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("withConverter on a DocumentReference round-trips")</div></div>
|
|
169
169
|
</details>
|
|
170
170
|
<details class="compat-row" data-status="ok">
|
|
171
171
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setDoc(ref, data[, options])</code><span class="compat-sub"><span class="compat-behavior">Rules-denied <code>setDoc</code> throws a <code>FirebaseError</code> with code <code>permission-denied</code>, matching production</span></span></span></summary>
|
|
172
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("getDoc denies when rules reject"), <code>playground:rules-data-validation</code>, oracle: <code>packages/conformance/observations/firestore/firestore-write-denied-error-code.json</code> (prod <code>setDoc</code> on a denied path throws a <code>FirebaseError</code> with <code>.code === 'permission-denied'</code>, <code>.message === '7 PERMISSION_DENIED: Missing or insufficient permissions.'</code>, <code>instanceof Error</code>); <code>oracle:firestore#32</code> pins the sandbox error shape</div></div>
|
|
172
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("getDoc denies when rules reject"), <code>playground:rules-data-validation</code>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-write-denied-error-code.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-write-denied-error-code.json</code></a> (prod <code>setDoc</code> on a denied path throws a <code>FirebaseError</code> with <code>.code === 'permission-denied'</code>, <code>.message === '7 PERMISSION_DENIED: Missing or insufficient permissions.'</code>, <code>instanceof Error</code>); <code>oracle:firestore#32</code> pins the sandbox error shape (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L557" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
173
173
|
</details>
|
|
174
174
|
</div>
|
|
175
175
|
<h2 id="updatedocref-data--partial-write"><code>updateDoc(ref, data)</code> — partial write</h2>
|
|
176
176
|
<div class="compat-list">
|
|
177
177
|
<details class="compat-row" data-status="ok">
|
|
178
178
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">updateDoc(ref, data)</code><span class="compat-sub"><span class="compat-behavior">Merges <code>data</code> into the existing doc; missing fields preserved. <strong>Top-level keys are dot-separated FieldPaths</strong> (FS-B5): <code>updateDoc({'a.b': 2})</code> sets the nested leaf <code>a.b</code> (preserving <code>a.c</code>), not a literal <code>"a.b"</code> key; a single-segment map value replaces that field wholesale; <code>deleteField()</code> at a dotted path removes the nested leaf.</span></span></span></summary>
|
|
179
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>unit:admin-compat/field-path-merge.test.ts</code> (FS-B5 dot-path nested write + delete; verified failing pre-fix)</div></div>
|
|
179
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L25" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/field-path-merge.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/field-path-merge.test.ts</code></a> (FS-B5 dot-path nested write + delete; verified failing pre-fix) (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-updatedoc-dotpath-fieldpath.json" target="_blank" rel="noopener noreferrer"><code>firestore-updatedoc-dotpath-fieldpath</code></a>)</div></div>
|
|
180
180
|
</details>
|
|
181
181
|
<details class="compat-row" data-status="ok">
|
|
182
182
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">updateDoc(ref, data)</code><span class="compat-sub"><span class="compat-behavior">Throws <code>not-found</code> (sandbox) / <code>FirebaseError('not-found')</code> (prod) on missing doc</span></span></span></summary>
|
|
183
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> (implicit in writes-fail-on-missing tests), oracle: <code>packages/conformance/observations/firestore/firestore-updatedoc-missing-error.json</code> (prod throws <code>FirebaseError</code> with <code>code: 'not-found'</code>, message <code>"5 NOT_FOUND: No document to update: …"</code>)</div></div>
|
|
183
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L25" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> (implicit in writes-fail-on-missing tests), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-updatedoc-missing-error.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-updatedoc-missing-error.json</code></a> (prod throws <code>FirebaseError</code> with <code>code: 'not-found'</code>, message <code>"5 NOT_FOUND: No document to update: …"</code>)</div></div>
|
|
184
184
|
</details>
|
|
185
185
|
<details class="compat-row" data-status="ok">
|
|
186
186
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">updateDoc(ref, data)</code><span class="compat-sub"><span class="compat-behavior">Does NOT run a converter — partial updates don't have a typed home (matches JS SDK)</span></span></span></summary>
|
|
@@ -188,80 +188,80 @@
|
|
|
188
188
|
</details>
|
|
189
189
|
<details class="compat-row" data-status="ok">
|
|
190
190
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">updateDoc(ref, data)</code><span class="compat-sub"><span class="compat-behavior">Sentinels resolve mid-update (<code>increment(1)</code> against an existing numeric field, etc.)</span></span></span></summary>
|
|
191
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-sentinels</code>, oracle: <code>packages/conformance/observations/firestore/firestore-row-36-sentinels-in-updatedoc.json</code> — after <code>setDoc({count: 5, tags: ['a'], oldField: 'keep-then-remove'})</code> then <code>updateDoc({count: increment(3), tags: arrayUnion('b'), oldField: deleteField()})</code>, the follow-up <code>getDoc</code> returns <code>count: 8</code>, <code>tags: ['a', 'b']</code>, and <code>oldField</code> absent from the doc (the deleteField sentinel actually removes the key). All three sentinels apply in one mid-update commit.</div></div>
|
|
191
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L25" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-sentinels</code>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-36-sentinels-in-updatedoc.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-36-sentinels-in-updatedoc.json</code></a> — after <code>setDoc({count: 5, tags: ['a'], oldField: 'keep-then-remove'})</code> then <code>updateDoc({count: increment(3), tags: arrayUnion('b'), oldField: deleteField()})</code>, the follow-up <code>getDoc</code> returns <code>count: 8</code>, <code>tags: ['a', 'b']</code>, and <code>oldField</code> absent from the doc (the deleteField sentinel actually removes the key). All three sentinels apply in one mid-update commit.</div></div>
|
|
192
192
|
</details>
|
|
193
193
|
<details class="compat-row" data-status="ok">
|
|
194
194
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">updateDoc(ref, data)</code><span class="compat-sub"><span class="compat-behavior">Sandbox-live: each call re-evaluates auth (alice → bob between writes uses bob's auth)</span></span></span></summary>
|
|
195
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("updateDoc re-evaluates auth per call")</div></div>
|
|
195
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L31" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("updateDoc re-evaluates auth per call")</div></div>
|
|
196
196
|
</details>
|
|
197
197
|
</div>
|
|
198
198
|
<h2 id="deletedocref"><code>deleteDoc(ref)</code></h2>
|
|
199
199
|
<div class="compat-list">
|
|
200
200
|
<details class="compat-row" data-status="ok">
|
|
201
201
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">deleteDoc(ref)</code><span class="compat-sub"><span class="compat-behavior">Removes the document; subsequent <code>getDoc</code> returns <code>exists()===false</code></span></span></span></summary>
|
|
202
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
202
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L26" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
203
203
|
</details>
|
|
204
204
|
<details class="compat-row" data-status="ok">
|
|
205
205
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">deleteDoc(ref)</code><span class="compat-sub"><span class="compat-behavior">Idempotent — <code>deleteDoc</code> on missing doc resolves without throwing (matches JS SDK)</span></span></span></summary>
|
|
206
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:deletedoc-missing.test.ts</code>, <code>playground:firestore-deletedoc-missing</code>, oracle: <code>packages/conformance/observations/firestore/firestore-deletedoc-missing.json</code></div></div>
|
|
206
|
+
<div class="compat-evidence"><div class="compat-probe"><code>unit:deletedoc-missing.test.ts</code>, <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-deletedoc-missing.json" target="_blank" rel="noopener noreferrer"><code>playground:firestore-deletedoc-missing</code></a>, oracle: <code>packages/conformance/observations/firestore/firestore-deletedoc-missing.json</code></div></div>
|
|
207
207
|
</details>
|
|
208
208
|
<details class="compat-row" data-status="ok">
|
|
209
209
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">deleteDoc(ref)</code><span class="compat-sub"><span class="compat-behavior">Rules-denied delete throws <code>permission-denied</code></span></span></span></summary>
|
|
210
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> (rules-reject branch), oracle: <code>packages/conformance/observations/firestore/firestore-delete-denied-error-code.json</code> (prod <code>deleteDoc</code> on a denied path throws a <code>FirebaseError</code> with <code>.code === 'permission-denied'</code>, <code>.message === '7 PERMISSION_DENIED: Missing or insufficient permissions.'</code>, <code>instanceof Error</code>)</div></div>
|
|
210
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L26" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> (rules-reject branch), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-delete-denied-error-code.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-delete-denied-error-code.json</code></a> (prod <code>deleteDoc</code> on a denied path throws a <code>FirebaseError</code> with <code>.code === 'permission-denied'</code>, <code>.message === '7 PERMISSION_DENIED: Missing or insufficient permissions.'</code>, <code>instanceof Error</code>)</div></div>
|
|
211
211
|
</details>
|
|
212
212
|
</div>
|
|
213
213
|
<h2 id="adddoccoll-data--auto-id-write"><code>addDoc(coll, data)</code> — auto-id write</h2>
|
|
214
214
|
<div class="compat-list">
|
|
215
215
|
<details class="compat-row" data-status="ok">
|
|
216
216
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">addDoc(coll, data)</code><span class="compat-sub"><span class="compat-behavior">Returns a tagged <code>DocumentReference</code> with auto-id</span></span></span></summary>
|
|
217
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
217
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L27" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
218
218
|
</details>
|
|
219
219
|
<details class="compat-row" data-status="ok">
|
|
220
220
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">addDoc(coll, data)</code><span class="compat-sub"><span class="compat-behavior">Returned ref is usable in subsequent ops (<code>getDoc</code>, <code>setDoc</code>, <code>onSnapshot</code>)</span></span></span></summary>
|
|
221
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, oracle: <code>packages/conformance/observations/firestore/firestore-row-42-adddoc-returned-ref-usable.json</code> — <code>addDoc(coll, {v:1})</code> returned a ref whose <code>.id</code> is a 20-char auto-id; <code>getDoc(ref)</code> returned <code>{v:1}</code> (round-trip), <code>setDoc(ref, {v:2})</code> overwrote without error, follow-up <code>getDoc</code> returned <code>{v:2}</code>, and <code>onSnapshot(ref, cb)</code> registered cleanly and fired once with <code>{exists:true, v:2}</code>. All four follow-up ops succeed on the returned ref without re-tagging.</div></div>
|
|
221
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L27" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-42-adddoc-returned-ref-usable.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-42-adddoc-returned-ref-usable.json</code></a> — <code>addDoc(coll, {v:1})</code> returned a ref whose <code>.id</code> is a 20-char auto-id; <code>getDoc(ref)</code> returned <code>{v:1}</code> (round-trip), <code>setDoc(ref, {v:2})</code> overwrote without error, follow-up <code>getDoc</code> returned <code>{v:2}</code>, and <code>onSnapshot(ref, cb)</code> registered cleanly and fired once with <code>{exists:true, v:2}</code>. All four follow-up ops succeed on the returned ref without re-tagging.</div></div>
|
|
222
222
|
</details>
|
|
223
223
|
<details class="compat-row" data-status="ok">
|
|
224
224
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">addDoc(coll, data)</code><span class="compat-sub"><span class="compat-behavior">Sandbox-live: returned ref is a <em>live</em> ref (rebuild closure recorded) so follow-ups re-resolve auth</span></span></span></summary>
|
|
225
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("addDoc result is a tagged live ref")</div></div>
|
|
225
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L32" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("addDoc result is a tagged live ref")</div></div>
|
|
226
226
|
</details>
|
|
227
227
|
<details class="compat-row" data-status="ok">
|
|
228
228
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">addDoc(coll, data)</code><span class="compat-sub"><span class="compat-behavior">Converter on the parent collection propagates onto the returned ref</span></span></span></summary>
|
|
229
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("addDoc through a converted collection")</div></div>
|
|
229
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L27" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("addDoc through a converted collection")</div></div>
|
|
230
230
|
</details>
|
|
231
231
|
<details class="compat-row" data-status="ok">
|
|
232
232
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">addDoc(coll, data)</code><span class="compat-sub"><span class="compat-behavior">Auto-id format — production and sandbox mint 20-character IDs from <code>[A-Za-z0-9]</code></span></span></span></summary>
|
|
233
|
-
<div class="compat-evidence"><div class="compat-probe">Oracle-locked: <code>packages/conformance/observations/firestore/firestore-adddoc-autoid-format.json</code> records production's 20-character alphanumeric shape; <code>packages/pyric/test/firestore/oracle-conformance.test.ts</code> generates 25 sandbox IDs and checks both invariants against that observation.</div></div>
|
|
233
|
+
<div class="compat-evidence"><div class="compat-probe">Oracle-locked: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-adddoc-autoid-format.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-adddoc-autoid-format.json</code></a> records production's 20-character alphanumeric shape; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L185" target="_blank" rel="noopener noreferrer"><code>packages/pyric/test/firestore/oracle-conformance.test.ts</code></a> generates 25 sandbox IDs and checks both invariants against that observation.</div></div>
|
|
234
234
|
</details>
|
|
235
235
|
</div>
|
|
236
236
|
<h2 id="withconverter--typed-refs"><code>withConverter</code> — typed refs</h2>
|
|
237
237
|
<div class="compat-list">
|
|
238
238
|
<details class="compat-row" data-status="ok">
|
|
239
239
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">withConverter</code><span class="compat-sub"><span class="compat-behavior"><code>withConverter(docRef, converter)</code> returns a shell that runs <code>toFirestore</code> on writes, <code>fromFirestore</code> on reads</span></span></span></summary>
|
|
240
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
240
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
241
241
|
</details>
|
|
242
242
|
<details class="compat-row" data-status="ok">
|
|
243
243
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">withConverter</code><span class="compat-sub"><span class="compat-behavior"><code>withConverter(collRef, converter)</code> propagates onto <code>doc(typedColl, id)</code></span></span></span></summary>
|
|
244
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
244
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
245
245
|
</details>
|
|
246
246
|
<details class="compat-row" data-status="ok">
|
|
247
247
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">withConverter</code><span class="compat-sub"><span class="compat-behavior"><code>withConverter(collRef, converter)</code> propagates through <code>query(typedColl, …)</code> + <code>getDocs()</code></span></span></span></summary>
|
|
248
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
248
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
249
249
|
</details>
|
|
250
250
|
<details class="compat-row" data-status="ok">
|
|
251
251
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">withConverter</code><span class="compat-sub"><span class="compat-behavior"><code>withConverter(ref, null)</code> strips the converter, returns the underlying untyped view</span></span></span></summary>
|
|
252
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
252
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
253
253
|
</details>
|
|
254
254
|
<details class="compat-row" data-status="ok">
|
|
255
255
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">withConverter</code><span class="compat-sub"><span class="compat-behavior">Original untyped ref keeps its identity after <code>withConverter(ref, c)</code> (two views, one path)</span></span></span></summary>
|
|
256
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
256
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
257
257
|
</details>
|
|
258
258
|
<details class="compat-row" data-status="ok">
|
|
259
259
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">withConverter</code><span class="compat-sub"><span class="compat-behavior"><code>setDoc</code> through a converted ref invokes <code>toFirestore(data)</code></span></span></span></summary>
|
|
260
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
260
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
261
261
|
</details>
|
|
262
262
|
<details class="compat-row" data-status="ok">
|
|
263
263
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">withConverter</code><span class="compat-sub"><span class="compat-behavior"><code>getDoc</code> through a converted ref invokes <code>fromFirestore(snapshot)</code>; <code>.data()</code> returns the typed model</span></span></span></summary>
|
|
264
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
264
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
265
265
|
</details>
|
|
266
266
|
<details class="compat-row" data-status="ok">
|
|
267
267
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">withConverter</code><span class="compat-sub"><span class="compat-behavior"><code>updateDoc</code> through a converted ref does NOT invoke the converter</span></span></span></summary>
|
|
@@ -272,67 +272,67 @@
|
|
|
272
272
|
<div class="compat-list">
|
|
273
273
|
<details class="compat-row" data-status="ok">
|
|
274
274
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><code>query(coll, where(…), orderBy(…), limit(…))</code> composes constraints in order</span></span></span></summary>
|
|
275
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-query</code></div></div>
|
|
275
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-query</code></div></div>
|
|
276
276
|
</details>
|
|
277
277
|
<details class="compat-row" data-status="ok">
|
|
278
278
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><code>where(field, op, value)</code> — all 10 ops: <code><</code>, <code><=</code>, <code>==</code>, <code>>=</code>, <code>></code>, <code>!=</code>, <code>in</code>, <code>not-in</code>, <code>array-contains</code>, <code>array-contains-any</code></span></span></span></summary>
|
|
279
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> (canonical query test); membership ops + OR/<code>in</code>/<code>array-contains</code> composites: <code>unit:upstream-query-probes.test.ts</code></div></div>
|
|
279
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L30" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> (canonical query test); membership ops + OR/<code>in</code>/<code>array-contains</code> composites: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-query-probes.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:upstream-query-probes.test.ts</code></a></div></div>
|
|
280
280
|
</details>
|
|
281
281
|
<details class="compat-row" data-status="ok">
|
|
282
282
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><strong>Existence + null filter guards (FS-B7)</strong> — a doc missing the filter field is never returned by <code>==</code>/<code><</code>/<code><=</code>/<code>></code>/<code>>=</code>/<code>in</code>/<code>!=</code>/<code>not-in</code>; <code>!=</code> and <code>not-in</code> additionally exclude null-valued docs and require the field to exist; a <code>null</code> in a <code>not-in</code> operand list matches nothing. Pre-FS-B7, <code>!=</code>/<code>not-in</code> matched missing-field and null docs.</span></span></span></summary>
|
|
283
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:admin-compat/inequality-existence-guards.test.ts</code> (verified failing pre-fix)</div></div>
|
|
283
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/inequality-existence-guards.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/inequality-existence-guards.test.ts</code></a> (verified failing pre-fix)</div></div>
|
|
284
284
|
</details>
|
|
285
285
|
<details class="compat-row" data-status="ok">
|
|
286
286
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><code>or(...)</code> composite — at least one sub-filter matches</span></span></span></summary>
|
|
287
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("or() matches docs where any sub-filter matches"), oracle: <code>packages/conformance/observations/firestore/firestore-or-composite.json</code> (4 seeded docs; <code>or(where('x','==',1), where('y','==',2))</code> returned the exact union <code>{match-both, match-x, match-y}</code> — no implicit index required against cloud Firestore)</div></div>
|
|
287
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L28" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("or() matches docs where any sub-filter matches"), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-or-composite.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-or-composite.json</code></a> (4 seeded docs; <code>or(where('x','==',1), where('y','==',2))</code> returned the exact union <code>{match-both, match-x, match-y}</code> — no implicit index required against cloud Firestore)</div></div>
|
|
288
288
|
</details>
|
|
289
289
|
<details class="compat-row" data-status="ok">
|
|
290
290
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><code>and(...)</code> composite — every sub-filter matches</span></span></span></summary>
|
|
291
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("and() requires every sub-filter"), oracle: <code>packages/conformance/observations/firestore/firestore-and-composite.json</code> (4 seeded docs; <code>and(where('x','==',1), where('y','==',2))</code> returned only the intersection <code>{match-both}</code>)</div></div>
|
|
291
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("and() requires every sub-filter"), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-and-composite.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-and-composite.json</code></a> (4 seeded docs; <code>and(where('x','==',1), where('y','==',2))</code> returned only the intersection <code>{match-both}</code>)</div></div>
|
|
292
292
|
</details>
|
|
293
293
|
<details class="compat-row" data-status="ok">
|
|
294
294
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior">Nested <code>or</code> / <code>and</code> — full composite tree</span></span></span></summary>
|
|
295
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("nested or/and — the canonical composite pattern"), oracle: <code>packages/conformance/observations/firestore/firestore-nested-or-and-composite.json</code> (6 seeded docs; <code>or(and(where('x','==',1), where('y','==',2)), where('z','==',3))</code> returned <code>{inner-and-match, outer-z-match, both-branches}</code> — exact boolean union as predicted)</div></div>
|
|
295
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("nested or/and — the canonical composite pattern"), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-nested-or-and-composite.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-nested-or-and-composite.json</code></a> (6 seeded docs; <code>or(and(where('x','==',1), where('y','==',2)), where('z','==',3))</code> returned <code>{inner-and-match, outer-z-match, both-branches}</code> — exact boolean union as predicted)</div></div>
|
|
296
296
|
</details>
|
|
297
297
|
<details class="compat-row" data-status="ok">
|
|
298
298
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><code>orderBy(field, 'asc'|'desc')</code> — direction parameter</span></span></span></summary>
|
|
299
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
299
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L31" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
300
300
|
</details>
|
|
301
301
|
<details class="compat-row" data-status="ok">
|
|
302
302
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><strong>Canonical type-order comparison (FS-B3)</strong> — orderBy + range filters compare by Firestore's canonical type order (<code>null < bool < number < timestamp < string < bytes < ref < geopoint < array < map</code>), then within-type; numbers sort numerically (not lexicographically), NaN sorts as the smallest number, and range filters (<code><</code>/<code><=</code>/<code>></code>/<code>>=</code>) only match same-type values. Pre-FS-B3 the comparator fell back to <code>String(a).localeCompare(String(b))</code>.</span></span></span></summary>
|
|
303
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/sandbox/query-value-order.test.ts</code> (cross-type ranking, numeric sort, NaN, timestamps, arrays; verified failing pre-fix)</div></div>
|
|
303
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox/query-value-order.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:firestore/sandbox/query-value-order.test.ts</code></a> (cross-type ranking, numeric sort, NaN, timestamps, arrays; verified failing pre-fix)</div></div>
|
|
304
304
|
</details>
|
|
305
305
|
<details class="compat-row" data-status="ok">
|
|
306
306
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><strong>orderBy excludes missing-field docs (FS-B3)</strong> — a doc lacking an orderBy field is omitted from the result (matches prod); pre-fix it was sorted in via <code>compareValues(undefined, …)</code>.</span></span></span></summary>
|
|
307
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/sandbox/query-value-order.test.ts</code> ("excludes the missing-field doc")</div></div>
|
|
307
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox/query-value-order.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:firestore/sandbox/query-value-order.test.ts</code></a> ("excludes the missing-field doc")</div></div>
|
|
308
308
|
</details>
|
|
309
309
|
<details class="compat-row" data-status="ok">
|
|
310
310
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><strong>Implicit orderBy + <code>__name__</code> tiebreak (FS-B8)</strong> — the query's sort is normalized to: explicit orderBy clauses, then an implicit order on each inequality-filtered field, then a final document-key (<code>__name__</code>) clause. Equal-valued docs sort deterministically by key; a <code>where('x','>',v)</code> with no explicit orderBy returns docs ordered by <code>x</code>. Mirrors <code>clones/.../core/query.ts:queryNormalizedOrderBy</code>. Pre-FS-B8 equal-valued docs were nondeterministic and inequality results came back in insertion order.</span></span></span></summary>
|
|
311
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:admin-compat/implicit-order-name.test.ts</code> (key tiebreak, snapshot-cursor disambiguation, implicit inequality order; verified failing pre-fix)</div></div>
|
|
311
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/implicit-order-name.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/implicit-order-name.test.ts</code></a> (key tiebreak, snapshot-cursor disambiguation, implicit inequality order; verified failing pre-fix)</div></div>
|
|
312
312
|
</details>
|
|
313
313
|
<details class="compat-row" data-status="ok">
|
|
314
314
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><code>limit(n)</code> — caps result count</span></span></span></summary>
|
|
315
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
315
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L32" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
316
316
|
</details>
|
|
317
317
|
<details class="compat-row" data-status="ok">
|
|
318
318
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior"><code>limitToLast(n)</code> returns the trailing n documents in an ordered result; without <code>orderBy</code>, it throws a <code>FirestoreError</code> with production's <code>unimplemented</code> code</span></span></span></summary>
|
|
319
|
-
<div class="compat-evidence"><div class="compat-probe">Oracle-locked by <code>packages/conformance/observations/firestore/firestore-limittolast-preconditions.json</code>: production's no-orderBy precondition throws code <code>unimplemented</code>, while ordered trailing-window semantics return <code>["b"]</code>. <code>oracle:firestore#61</code> replays both claims. Cursor composition + descending: <code>unit:upstream-query-probes.test.ts</code>; cursor/empty-snapshot preconditions: <code>unit:sandbox-target.test.ts</code> + <code>unit:admin-compat/cursors.test.ts</code
|
|
319
|
+
<div class="compat-evidence"><div class="compat-probe">Oracle-locked by <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-limittolast-preconditions.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-limittolast-preconditions.json</code></a>: production's no-orderBy precondition throws code <code>unimplemented</code>, while ordered trailing-window semantics return <code>["b"]</code>. <code>oracle:firestore#61</code> replays both claims. Cursor composition + descending: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-query-probes.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:upstream-query-probes.test.ts</code></a>; cursor/empty-snapshot preconditions: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L560" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/cursors.test.ts#L198" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/cursors.test.ts</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L331" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
320
320
|
</details>
|
|
321
321
|
<details class="compat-row" data-status="ok">
|
|
322
322
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior">Composite filters AND with other constraints — <code>query(coll, or(...), orderBy(...), limit(...))</code></span></span></span></summary>
|
|
323
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
323
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
324
324
|
</details>
|
|
325
325
|
<details class="compat-row" data-status="ok">
|
|
326
326
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior">Passing <code>orderBy</code> / <code>limit</code> into <code>or()</code> / <code>and()</code> → <code>TypeError</code></span></span></span></summary>
|
|
327
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
327
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
328
328
|
</details>
|
|
329
329
|
<details class="compat-row" data-status="ok">
|
|
330
330
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior">Zero-arg <code>or()</code> / <code>and()</code> → <code>TypeError</code></span></span></span></summary>
|
|
331
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
331
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
332
332
|
</details>
|
|
333
333
|
<details class="compat-row" data-status="ok">
|
|
334
334
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior">Chained queries re-tag for further constraints (<code>query(query(coll, where), orderBy)</code>)</span></span></span></summary>
|
|
335
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("chained queries are taggable")</div></div>
|
|
335
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("chained queries are taggable")</div></div>
|
|
336
336
|
</details>
|
|
337
337
|
<details class="compat-row" data-status="diverged">
|
|
338
338
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior">Index validation against <code>firestore.indexes.json</code> — sandbox uses <code>LocalEnvironment</code>'s lint pass; prod has its own server-side validation</span></span></span></summary>
|
|
@@ -343,109 +343,109 @@
|
|
|
343
343
|
<div class="compat-list">
|
|
344
344
|
<details class="compat-row" data-status="ok">
|
|
345
345
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Cursor pagination</code><span class="compat-sub"><span class="compat-behavior"><code>startAt(...values)</code> — inclusive value cursor (one positional per <code>orderBy</code> clause)</span></span></span></summary>
|
|
346
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, oracle: <code>packages/conformance/observations/firestore/firestore-cursor-startat-inclusive.json</code> (5 seeded docs at pos=[1..5]; <code>query(c, orderBy('pos'), startAt(3))</code> returned exactly <code>[pos-3, pos-4, pos-5]</code> — the cursor doc IS included)</div></div>
|
|
346
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L577" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-cursor-startat-inclusive.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-cursor-startat-inclusive.json</code></a> (5 seeded docs at pos=[1..5]; <code>query(c, orderBy('pos'), startAt(3))</code> returned exactly <code>[pos-3, pos-4, pos-5]</code> — the cursor doc IS included)</div></div>
|
|
347
347
|
</details>
|
|
348
348
|
<details class="compat-row" data-status="ok">
|
|
349
349
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Cursor pagination</code><span class="compat-sub"><span class="compat-behavior"><code>startAfter(...values)</code> — exclusive value cursor</span></span></span></summary>
|
|
350
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, oracle: <code>packages/conformance/observations/firestore/firestore-cursor-startafter-exclusive.json</code> (5 seeded docs at pos=[1..5]; <code>query(c, orderBy('pos'), startAfter(3))</code> returned exactly <code>[pos-4, pos-5]</code> — the cursor doc is EXCLUDED)</div></div>
|
|
350
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L586" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-cursor-startafter-exclusive.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-cursor-startafter-exclusive.json</code></a> (5 seeded docs at pos=[1..5]; <code>query(c, orderBy('pos'), startAfter(3))</code> returned exactly <code>[pos-4, pos-5]</code> — the cursor doc is EXCLUDED)</div></div>
|
|
351
351
|
</details>
|
|
352
352
|
<details class="compat-row" data-status="ok">
|
|
353
353
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Cursor pagination</code><span class="compat-sub"><span class="compat-behavior"><code>endAt(...values)</code> — inclusive end cursor</span></span></span></summary>
|
|
354
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, oracle: <code>packages/conformance/observations/firestore/firestore-cursor-endat-inclusive.json</code> (5 seeded docs at pos=[1..5]; <code>query(c, orderBy('pos'), endAt(3))</code> returned exactly <code>[pos-1, pos-2, pos-3]</code> — the cursor doc IS included)</div></div>
|
|
354
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L595" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-cursor-endat-inclusive.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-cursor-endat-inclusive.json</code></a> (5 seeded docs at pos=[1..5]; <code>query(c, orderBy('pos'), endAt(3))</code> returned exactly <code>[pos-1, pos-2, pos-3]</code> — the cursor doc IS included)</div></div>
|
|
355
355
|
</details>
|
|
356
356
|
<details class="compat-row" data-status="ok">
|
|
357
357
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Cursor pagination</code><span class="compat-sub"><span class="compat-behavior"><code>endBefore(...values)</code> — exclusive end cursor</span></span></span></summary>
|
|
358
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, oracle: <code>packages/conformance/observations/firestore/firestore-cursor-endbefore-exclusive.json</code> (5 seeded docs at pos=[1..5]; <code>query(c, orderBy('pos'), endBefore(3))</code> returned exactly <code>[pos-1, pos-2]</code> — the cursor doc is EXCLUDED)</div></div>
|
|
358
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L604" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-cursor-endbefore-exclusive.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-cursor-endbefore-exclusive.json</code></a> (5 seeded docs at pos=[1..5]; <code>query(c, orderBy('pos'), endBefore(3))</code> returned exactly <code>[pos-1, pos-2]</code> — the cursor doc is EXCLUDED)</div></div>
|
|
359
359
|
</details>
|
|
360
360
|
<details class="compat-row" data-status="ok">
|
|
361
361
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Cursor pagination</code><span class="compat-sub"><span class="compat-behavior"><code>startAt(snapshot)</code> overload — extracts orderBy field values from the snapshot, positioning against the NORMALIZED orderBy (implicit <code>__name__</code>), so it disambiguates equal-valued docs and is <strong>legal without an explicit orderBy</strong> (FS-B8). A VALUE cursor with more values than explicit orderBy clauses throws <code>invalid-argument</code> ("Too many arguments").</span></span></span></summary>
|
|
362
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>unit:admin-compat/implicit-order-name.test.ts</code> (snapshot cursor w/o orderBy), <code>unit:admin-compat/cursors.test.ts</code> (value-cursor too-many-args throws with <code>.code</code>)</div></div>
|
|
362
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L577" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/implicit-order-name.test.ts#L15" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/implicit-order-name.test.ts</code></a> (snapshot cursor w/o orderBy), <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/cursors.test.ts#L6" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/cursors.test.ts</code></a> (value-cursor too-many-args throws with <code>.code</code>) (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-startat-snapshot-implicit-name.json" target="_blank" rel="noopener noreferrer"><code>firestore-startat-snapshot-implicit-name</code></a>)</div></div>
|
|
363
363
|
</details>
|
|
364
364
|
<details class="compat-row" data-status="ok">
|
|
365
365
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Cursor pagination</code><span class="compat-sub"><span class="compat-behavior"><code>endAt(snapshot)</code> overload</span></span></span></summary>
|
|
366
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("endAt(snapshot) trims to-and-including the anchor")</div></div>
|
|
366
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L595" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("endAt(snapshot) trims to-and-including the anchor")</div></div>
|
|
367
367
|
</details>
|
|
368
368
|
<details class="compat-row" data-status="ok">
|
|
369
369
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Cursor pagination</code><span class="compat-sub"><span class="compat-behavior"><code>startAfter + limit</code> — canonical pagination pattern</span></span></span></summary>
|
|
370
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
370
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L586" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
371
371
|
</details>
|
|
372
372
|
</div>
|
|
373
373
|
<h2 id="aggregates--getcountfromserver--getaggregatefromserver--count--sum--average">Aggregates — <code>getCountFromServer</code> / <code>getAggregateFromServer</code> / <code>count</code> / <code>sum</code> / <code>average</code></h2>
|
|
374
374
|
<div class="compat-list">
|
|
375
375
|
<details class="compat-row" data-status="ok">
|
|
376
376
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Aggregates</code><span class="compat-sub"><span class="compat-behavior"><code>getCountFromServer(query)</code> returns <code>{ data: () => ({ count: N }) }</code></span></span></span></summary>
|
|
377
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>; collectionGroup: <code>unit:upstream-write-aggregate-probes.test.ts</code></div></div>
|
|
377
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L71" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>; collectionGroup: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-write-aggregate-probes.test.ts#L21" target="_blank" rel="noopener noreferrer"><code>unit:upstream-write-aggregate-probes.test.ts</code></a></div></div>
|
|
378
378
|
</details>
|
|
379
379
|
<details class="compat-row" data-status="ok">
|
|
380
380
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Aggregates</code><span class="compat-sub"><span class="compat-behavior"><code>getCountFromServer</code> honors <code>where</code> filters</span></span></span></summary>
|
|
381
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
381
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L791" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
382
382
|
</details>
|
|
383
383
|
<details class="compat-row" data-status="ok">
|
|
384
384
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Aggregates</code><span class="compat-sub"><span class="compat-behavior"><code>getAggregateFromServer(query, spec)</code> returns <code>{ data: () => Record<alias, number|null> }</code></span></span></span></summary>
|
|
385
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>; collectionGroup + nested paths: <code>unit:upstream-write-aggregate-probes.test.ts</code></div></div>
|
|
385
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L807" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>; collectionGroup + nested paths: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-write-aggregate-probes.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:upstream-write-aggregate-probes.test.ts</code></a></div></div>
|
|
386
386
|
</details>
|
|
387
387
|
<details class="compat-row" data-status="ok">
|
|
388
388
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Aggregates</code><span class="compat-sub"><span class="compat-behavior"><code>count()</code> / <code>sum(field)</code> / <code>average(field)</code> compose under one spec — <code>field</code> may be a dotted nested path</span></span></span></summary>
|
|
389
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>; nested <code>sum('metadata.pages')</code>: <code>unit:upstream-write-aggregate-probes.test.ts</code></div></div>
|
|
389
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>; nested <code>sum('metadata.pages')</code>: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-write-aggregate-probes.test.ts#L23" target="_blank" rel="noopener noreferrer"><code>unit:upstream-write-aggregate-probes.test.ts</code></a></div></div>
|
|
390
390
|
</details>
|
|
391
391
|
<details class="compat-row" data-status="ok">
|
|
392
392
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Aggregates</code><span class="compat-sub"><span class="compat-behavior"><code>average</code> returns <code>null</code> on empty input (matches JS SDK)</span></span></span></summary>
|
|
393
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
393
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L807" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
394
394
|
</details>
|
|
395
395
|
<details class="compat-row" data-status="diverged">
|
|
396
396
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Aggregates</code><span class="compat-sub"><span class="compat-behavior">Aggregates count documents server-side without paying read cost per doc in prod; sandbox computes locally (no cost model)</span></span></span></summary>
|
|
397
|
-
<div class="compat-evidence"><div class="compat-probe">divergence: cost behavior differs, observable shape identical. Oracle-locked: <code>packages/conformance/observations/firestore/firestore-count-aggregate-shape.json</code> — <code>getCountFromServer().data()</code> returns <code>{ count: <number> }</code> (single key, no other fields). Empty query returns <code>count: 0</code> (not <code>null</code>/<code>undefined</code>); seeded 3 docs returns <code>count: 3</code>; filtered query honors the <code>where</code> constraint (<code>count: 2</code>).</div></div>
|
|
397
|
+
<div class="compat-evidence"><div class="compat-probe">divergence: cost behavior differs, observable shape identical. Oracle-locked: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-count-aggregate-shape.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-count-aggregate-shape.json</code></a> — <code>getCountFromServer().data()</code> returns <code>{ count: <number> }</code> (single key, no other fields). Empty query returns <code>count: 0</code> (not <code>null</code>/<code>undefined</code>); seeded 3 docs returns <code>count: 3</code>; filtered query honors the <code>where</code> constraint (<code>count: 2</code>).</div></div>
|
|
398
398
|
</details>
|
|
399
399
|
</div>
|
|
400
400
|
<h2 id="onsnapshotreforquery---listeners"><code>onSnapshot(refOrQuery, …)</code> — listeners</h2>
|
|
401
401
|
<div class="compat-list">
|
|
402
402
|
<details class="compat-row" data-status="ok">
|
|
403
403
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior"><code>onSnapshot(docRef, cb)</code> fires the initial snapshot <strong>asynchronously</strong> — never synchronously during the registering call. Prod empirically lands after a <code>setTimeout(0)</code> macrotask (the fire travels the network listener channel); the sandbox defers through its delivery scheduler (microtask). The matrix contract is "asynchronous, never during register", not "exactly the next microtask"</span></span></span></summary>
|
|
404
|
-
<div class="compat-evidence"><div class="compat-probe">Aligned via the listener delivery scheduler (<code>src/sandbox/firestore/local-environment.ts</code>): the initial fire is enqueued and delivered on a microtask, never during register — closing the divergence this row previously documented (the sandbox used to fire synchronously during registration; the sync-body tests were migrated to the flush/await idiom). Machine-checked against <code>packages/conformance/observations/firestore/firestore-row-80-onsnapshot-fires-initial.json</code> (<code>firstFireSyncDuringRegister: false</code>, fire count + contents) in <code>oracle-conformance.test.ts</code>; also <code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-onsnapshot</code> (bundled) + <code>playground:firestore-row-80-onsnapshot-fires-initial</code> (one-claim).</div></div>
|
|
404
|
+
<div class="compat-evidence"><div class="compat-probe">Aligned via the listener delivery scheduler (<code>src/sandbox/firestore/local-environment.ts</code>): the initial fire is enqueued and delivered on a microtask, never during register — closing the divergence this row previously documented (the sandbox used to fire synchronously during registration; the sync-body tests were migrated to the flush/await idiom). Machine-checked against <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-80-onsnapshot-fires-initial.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-80-onsnapshot-fires-initial.json</code></a> (<code>firstFireSyncDuringRegister: false</code>, fire count + contents) in <code>oracle-conformance.test.ts</code>; also <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L330" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-onsnapshot</code> (bundled) + <code>playground:firestore-row-80-onsnapshot-fires-initial</code> (one-claim).</div></div>
|
|
405
405
|
</details>
|
|
406
406
|
<details class="compat-row" data-status="ok">
|
|
407
407
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior"><code>onSnapshot(query, cb)</code> fires on collection writes; <code>QuerySnapshot.docChanges()</code> reports <code>added</code> / <code>modified</code> / <code>removed</code> with <code>oldIndex</code> / <code>newIndex</code></span></span></span></summary>
|
|
408
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, oracle: <code>packages/conformance/observations/firestore/firestore-row-81-onsnapshot-query-fires-on-write.json</code> — listener on <code>query(coll)</code> saw 1 initial fire (empty, <code>size:0</code>), then one fire per write: <code>addDoc</code> → <code>size:1</code>, <code>setDoc(coll, 'known-id')</code> → <code>size:2</code>, <code>deleteDoc(addedRef)</code> → <code>size:1</code>. Total 4 fires, each reflecting the current collection state. Every collection-level write produces a distinct fire. (Note: this oracle used a <em>filterless</em> <code>query(coll)</code>, which masked FS-B2 — see row 81a.) Modular <code>docChanges</code> indexes: <code>unit:upstream-transform-txn-listener-probes.test.ts</code></div></div>
|
|
408
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L34" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-81-onsnapshot-query-fires-on-write.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-81-onsnapshot-query-fires-on-write.json</code></a> — listener on <code>query(coll)</code> saw 1 initial fire (empty, <code>size:0</code>), then one fire per write: <code>addDoc</code> → <code>size:1</code>, <code>setDoc(coll, 'known-id')</code> → <code>size:2</code>, <code>deleteDoc(addedRef)</code> → <code>size:1</code>. Total 4 fires, each reflecting the current collection state. Every collection-level write produces a distinct fire. (Note: this oracle used a <em>filterless</em> <code>query(coll)</code>, which masked FS-B2 — see row 81a.) Modular <code>docChanges</code> indexes: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-transform-txn-listener-probes.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:upstream-transform-txn-listener-probes.test.ts</code></a></div></div>
|
|
409
409
|
</details>
|
|
410
410
|
<details class="compat-row" data-status="ok">
|
|
411
411
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior"><strong>Filtered listeners honor <code>where</code> / <code>orderBy</code> / <code>limit</code> (FS-B2)</strong> — <code>onSnapshot(query(coll, where(…), orderBy(…), limit(…)), cb)</code> delivers the same membership as <code>getDocs(sameQuery)</code>: non-matching docs are excluded on the initial fire and on writes; ordering + limit are applied. Pre-FS-B2 the <code>SnapshotTarget</code> dropped all constraints and delivered the whole collection.</span></span></span></summary>
|
|
412
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:onsnapshot-query-constraints.test.ts</code> (filtered/ordered/limited listeners; verified failing pre-fix)</div></div>
|
|
412
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/onsnapshot-query-constraints.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:onsnapshot-query-constraints.test.ts</code></a> (filtered/ordered/limited listeners; verified failing pre-fix)</div></div>
|
|
413
413
|
</details>
|
|
414
414
|
<details class="compat-row" data-status="ok">
|
|
415
415
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior"><strong>Listener <code>.data()</code> matches <code>getDoc</code> shape (FS-B10)</strong> — the <code>onSnapshot</code> doc + query snapshot path runs the same read-path translation as <code>getDoc</code>/<code>getDocs</code>, so <code>snap.data().createdAt</code> is a compat <code>Timestamp</code> (<code>{seconds, nanoseconds}</code>), not the rules-internal wrapper (<code>{seconds, nanos}</code> + <code>typeName</code>, no <code>nanoseconds</code>). Pre-FS-B10 a listener leaked the internal shape while the single-doc read returned the compat shape.</span></span></span></summary>
|
|
416
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:simulator/listener-read-translation.test.ts</code> (doc + query listener Timestamp shape; verified failing pre-fix)</div></div>
|
|
416
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/simulator/listener-read-translation.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:simulator/listener-read-translation.test.ts</code></a> (doc + query listener Timestamp shape; verified failing pre-fix)</div></div>
|
|
417
417
|
</details>
|
|
418
418
|
<details class="compat-row" data-status="ok">
|
|
419
419
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior">Initial fire for a missing doc has <code>exists() === false</code> and <code>data() === undefined</code></span></span></span></summary>
|
|
420
|
-
<div class="compat-evidence"><div class="compat-probe"><code>playground:firestore-onsnapshot</code> (bundled) + <code>playground:firestore-row-82-onsnapshot-missing-initial</code> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-82-onsnapshot-missing-initial.json</code> — single initial fire with <code>snap.exists() === false</code>, <code>snap.data() === undefined</code>, <code>hasPendingWrites: false</code>, <code>fromCache: false</code>. The missing-doc fire is server-confirmed, not a cache speculation.</div></div>
|
|
420
|
+
<div class="compat-evidence"><div class="compat-probe"><code>playground:firestore-onsnapshot</code> (bundled) + <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-82-onsnapshot-missing-initial.json" target="_blank" rel="noopener noreferrer"><code>playground:firestore-row-82-onsnapshot-missing-initial</code></a> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-82-onsnapshot-missing-initial.json</code> — single initial fire with <code>snap.exists() === false</code>, <code>snap.data() === undefined</code>, <code>hasPendingWrites: false</code>, <code>fromCache: false</code>. The missing-doc fire is server-confirmed, not a cache speculation.</div></div>
|
|
421
421
|
</details>
|
|
422
422
|
<details class="compat-row" data-status="ok">
|
|
423
423
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior">Returned <code>Unsubscribe</code> stops further fires</span></span></span></summary>
|
|
424
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, oracle: <code>packages/conformance/observations/firestore/firestore-row-83-unsubscribe-stops-fires.json</code> — pre-unsubscribe write fired the listener (initial fire + write fire = 2 fires); after <code>unsub()</code>, a subsequent <code>setDoc</code> produced 0 additional fires (<code>postUnsubFireCount: 0</code>). Unsubscribe is durable; no fires arrive on the released callback after a 1.5s settle window.</div></div>
|
|
424
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L34" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-83-unsubscribe-stops-fires.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-83-unsubscribe-stops-fires.json</code></a> — pre-unsubscribe write fired the listener (initial fire + write fire = 2 fires); after <code>unsub()</code>, a subsequent <code>setDoc</code> produced 0 additional fires (<code>postUnsubFireCount: 0</code>). Unsubscribe is durable; no fires arrive on the released callback after a 1.5s settle window.</div></div>
|
|
425
425
|
</details>
|
|
426
426
|
<details class="compat-row" data-status="ok">
|
|
427
427
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior">Observer object form <code>{next, error, complete}</code> works alongside the function form. <strong>Partial observers are accepted — <code>{ error: fn }</code> with no <code>next</code> registers and routes denials to <code>error</code> (FS-B14, <code>isPartialObserver</code> semantics from upstream <code>api/observer.ts</code>); pre-fix it was misrouted as <code>SnapshotListenOptions</code> and threw "missing next handler".</strong></span></span></span></summary>
|
|
428
|
-
<div class="compat-evidence"><div class="compat-probe">oracle: <code>packages/conformance/observations/firestore/firestore-row-84-observer-object-form.json</code> — registered two listeners on the same doc: one as a bare function <code>(snap) => …</code>, one as <code>{next, error, complete}</code>. Both fired once on initial (<code>{v:0}</code>) and again after a write (<code>{v:1}</code>), capturing identical data. <code>error</code> never fired (no rule denial), <code>complete</code> never fired on <code>unsub()</code> (Firebase treats unsubscribe as a teardown, not a "complete" signal — the observer's <code>complete</code> callback is reserved for terminal stream end, which <code>onSnapshot</code> does not produce). The two registration shapes are interchangeable for fire dispatch. <code>unit:onsnapshot-observer-discriminator.test.ts</code> (error-only observer; verified failing pre-fix)</div></div>
|
|
428
|
+
<div class="compat-evidence"><div class="compat-probe">oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-84-observer-object-form.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-84-observer-object-form.json</code></a> — registered two listeners on the same doc: one as a bare function <code>(snap) => …</code>, one as <code>{next, error, complete}</code>. Both fired once on initial (<code>{v:0}</code>) and again after a write (<code>{v:1}</code>), capturing identical data. <code>error</code> never fired (no rule denial), <code>complete</code> never fired on <code>unsub()</code> (Firebase treats unsubscribe as a teardown, not a "complete" signal — the observer's <code>complete</code> callback is reserved for terminal stream end, which <code>onSnapshot</code> does not produce). The two registration shapes are interchangeable for fire dispatch. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/onsnapshot-observer-discriminator.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:onsnapshot-observer-discriminator.test.ts</code></a> (error-only observer; verified failing pre-fix)</div></div>
|
|
429
429
|
</details>
|
|
430
430
|
<details class="compat-row" data-status="ok">
|
|
431
431
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior"><code>SnapshotListenOptions.includeMetadataChanges</code> — one write yields the pending-write local echo (<code>hasPendingWrites: true</code>) then, for metadata listeners, the settled ack fire: default listener 2 fires, metadata listener 3</span></span></span></summary>
|
|
432
|
-
<div class="compat-evidence"><div class="compat-probe">Aligned via the listener delivery scheduler (<code>src/sandbox/firestore/local-environment.ts</code> + <code>snapshot-listeners.ts</code>): the write echo carries <code>hasPendingWrites: true</code> and <code>includeMetadataChanges</code> listeners receive the settled metadata-only ack, reproducing prod's recorded 2/3-fire sequences exactly. Machine-checked against <code>packages/conformance/observations/firestore/firestore-include-metadata-changes.json</code> in <code>oracle-conformance.test.ts</code> (fire counts and per-fire <code>hasPendingWrites</code> sequence asserted from the capture)</div></div>
|
|
432
|
+
<div class="compat-evidence"><div class="compat-probe">Aligned via the listener delivery scheduler (<code>src/sandbox/firestore/local-environment.ts</code> + <code>snapshot-listeners.ts</code>): the write echo carries <code>hasPendingWrites: true</code> and <code>includeMetadataChanges</code> listeners receive the settled metadata-only ack, reproducing prod's recorded 2/3-fire sequences exactly. Machine-checked against <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-include-metadata-changes.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-include-metadata-changes.json</code></a> in <code>oracle-conformance.test.ts</code> (fire counts and per-fire <code>hasPendingWrites</code> sequence asserted from the capture)</div></div>
|
|
433
433
|
</details>
|
|
434
434
|
<details class="compat-row" data-status="ok">
|
|
435
435
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior">Snapshot's <code>.ref</code> / <code>.docs[i].ref</code> are tagged so consumer code can pass them to follow-up ops</span></span></span></summary>
|
|
436
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
436
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L34" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
437
437
|
</details>
|
|
438
438
|
<details class="compat-row" data-status="ok">
|
|
439
439
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior">Sandbox-live: listener registered as alice keeps emitting alice's view after <code>setUser → bob</code> (identity frozen at subscribe)</span></span></span></summary>
|
|
440
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("listener registered as alice keeps emitting alice's view")</div></div>
|
|
440
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L35" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("listener registered as alice keeps emitting alice's view")</div></div>
|
|
441
441
|
</details>
|
|
442
442
|
<details class="compat-row" data-status="ok">
|
|
443
443
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior">Sandbox-live: listener registered as anonymous keeps firing after sign-in (anonymous → signed-in identity persists per listener)</span></span></span></summary>
|
|
444
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("listener registered as anonymous on /public keeps firing after sign-in")</div></div>
|
|
444
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L35" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("listener registered as anonymous on /public keeps firing after sign-in")</div></div>
|
|
445
445
|
</details>
|
|
446
446
|
<details class="compat-row" data-status="ok">
|
|
447
447
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior">Snapshot's ref is usable in follow-up ops under the new user (the ref is live, the listener identity is frozen — distinct)</span></span></span></summary>
|
|
448
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("snapshot ref is usable in subsequent ops under the new user"), oracle: <code>packages/conformance/observations/firestore/firestore-row-89-snapshot-ref-usable.json</code> — captured <code>snap.ref</code> from a docRef listener's first fire and <code>snap.docs[0].ref</code> from a query listener's first fire; both refs round-trip via <code>getDoc</code> (returning the same data) and <code>setDoc</code> (writes succeed and a follow-up <code>getDoc</code> confirms the new payload). <code>snap.ref.path</code> equals the original <code>doc(coll, id).path</code>. Both snap-ref shapes are first-class refs in prod, matching sandbox's tagged-ref guarantee.</div></div>
|
|
448
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L35" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("snapshot ref is usable in subsequent ops under the new user"), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-89-snapshot-ref-usable.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-89-snapshot-ref-usable.json</code></a> — captured <code>snap.ref</code> from a docRef listener's first fire and <code>snap.docs[0].ref</code> from a query listener's first fire; both refs round-trip via <code>getDoc</code> (returning the same data) and <code>setDoc</code> (writes succeed and a follow-up <code>getDoc</code> confirms the new payload). <code>snap.ref.path</code> equals the original <code>doc(coll, id).path</code>. Both snap-ref shapes are first-class refs in prod, matching sandbox's tagged-ref guarantee.</div></div>
|
|
449
449
|
</details>
|
|
450
450
|
<details class="compat-row" data-status="ok">
|
|
451
451
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshot(refOrQuery, …)</code><span class="compat-sub"><span class="compat-behavior">Preview tree mounts the user's component exactly once per session load — no observer subscriptions leak across parallel <code>AppPreview</code> instances. Root cause: <code>PlaygroundPage</code> rendered both <code>WorkspacePanel</code>'s and the mobile <code>AppPanel</code>'s <code>AppPreview</code> unconditionally (the latter <code>md:hidden</code> on desktop but still mounted), producing two live preview trees subscribing in parallel. Fixed by gating <code>AppPanel</code> on <code>useIsMobile() && mobileTab === 'app'</code>.</span></span></span></summary>
|
|
@@ -456,73 +456,73 @@
|
|
|
456
456
|
<div class="compat-list">
|
|
457
457
|
<details class="compat-row" data-status="ok">
|
|
458
458
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">runTransaction(db, fn)</code><span class="compat-sub"><span class="compat-behavior">Atomic read-write — all reads in <code>fn</code> see a consistent snapshot, writes commit together</span></span></span></summary>
|
|
459
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-transaction</code>; get-missing/deleted + empty txn + nested update: <code>unit:upstream-transform-txn-listener-probes.test.ts</code></div></div>
|
|
459
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-transaction</code>; get-missing/deleted + empty txn + nested update: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-transform-txn-listener-probes.test.ts#L27" target="_blank" rel="noopener noreferrer"><code>unit:upstream-transform-txn-listener-probes.test.ts</code></a></div></div>
|
|
460
460
|
</details>
|
|
461
461
|
<details class="compat-row" data-status="ok">
|
|
462
462
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">runTransaction(db, fn)</code><span class="compat-sub"><span class="compat-behavior">Identity is frozen at <code>runTransaction</code> start — mid-transaction <code>setUser</code> does NOT re-auth in-flight reads</span></span></span></summary>
|
|
463
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/sandbox-live-identity.test.ts</code> changes the live session from alice to bob inside the callback; the transaction still reads and commits under alice's captured identity.</div></div>
|
|
463
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>unit:firestore/sandbox-live-identity.test.ts</code></a> changes the live session from alice to bob inside the callback; the transaction still reads and commits under alice's captured identity.</div></div>
|
|
464
464
|
</details>
|
|
465
465
|
<details class="compat-row" data-status="ok">
|
|
466
466
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">runTransaction(db, fn)</code><span class="compat-sub"><span class="compat-behavior">Retry behavior — a read-document conflict reruns the callback against fresh data; <code>maxAttempts</code> bounds persistent contention</span></span></span></summary>
|
|
467
|
-
<div class="compat-evidence"><div class="compat-probe">Oracle: <code>packages/conformance/observations/firestore/firestore-transaction-contention-retries.json</code> — two production Web SDK clients force one retry (<code>[0, 40]</code>, final <code>42</code>) and persistent contention with <code>maxAttempts: 2</code> runs exactly twice then throws <code>failed-precondition</code>. <code>packages/pyric/test/firestore/oracle-conformance.test.ts</code> replays both cases through the sandbox
|
|
467
|
+
<div class="compat-evidence"><div class="compat-probe">Oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-transaction-contention-retries.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-transaction-contention-retries.json</code></a> — two production Web SDK clients force one retry (<code>[0, 40]</code>, final <code>42</code>) and persistent contention with <code>maxAttempts: 2</code> runs exactly twice then throws <code>failed-precondition</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L603" target="_blank" rel="noopener noreferrer"><code>packages/pyric/test/firestore/oracle-conformance.test.ts</code></a> replays both cases through the sandbox. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>sandbox-target.test.ts</code></a>)</div></div>
|
|
468
468
|
</details>
|
|
469
469
|
<details class="compat-row" data-status="ok">
|
|
470
470
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">runTransaction(db, fn)</code><span class="compat-sub"><span class="compat-behavior">Throws <code>FirebaseError('permission-denied')</code> on rule denial inside the transaction (the inner write's denial — not a generic <code>aborted</code>). Sandbox throws <code>FirestoreCompatError</code> with the same <code>code: 'permission-denied'</code></span></span></span></summary>
|
|
471
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> (writes-reject branch), oracle: <code>packages/conformance/observations/firestore/firestore-transaction-rules-denied-error.json</code> (prod throws <code>FirebaseError</code> with <code>code: 'permission-denied'</code>, NOT <code>aborted</code>; the inner callback ran once and the rules-rejected write surfaces as a regular permission-denied at commit)</div></div>
|
|
471
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> (writes-reject branch), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-transaction-rules-denied-error.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-transaction-rules-denied-error.json</code></a> (prod throws <code>FirebaseError</code> with <code>code: 'permission-denied'</code>, NOT <code>aborted</code>; the inner callback ran once and the rules-rejected write surfaces as a regular permission-denied at commit)</div></div>
|
|
472
472
|
</details>
|
|
473
473
|
</div>
|
|
474
474
|
<h2 id="writebatchdb"><code>writeBatch(db)</code></h2>
|
|
475
475
|
<div class="compat-list">
|
|
476
476
|
<details class="compat-row" data-status="ok">
|
|
477
477
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">writeBatch(db)</code><span class="compat-sub"><span class="compat-behavior"><code>batch.set</code> / <code>batch.update</code> / <code>batch.delete</code> queue mutations</span></span></span></summary>
|
|
478
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-batch</code></div></div>
|
|
478
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L37" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-batch</code></div></div>
|
|
479
479
|
</details>
|
|
480
480
|
<details class="compat-row" data-status="ok">
|
|
481
481
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">writeBatch(db)</code><span class="compat-sub"><span class="compat-behavior"><code>batch.commit()</code> applies all queued writes atomically — success path commits all queued mutations together; failure path (one write violating rules) rejects the <strong>whole</strong> batch with no partial application</span></span></span></summary>
|
|
482
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, oracle: <code>packages/conformance/observations/firestore/firestore-row-96-batch-commit-atomic.json</code> — success path: a batch with <code>set</code> (fresh doc), <code>update</code> (existing doc), and <code>delete</code> (existing doc) all land in a single commit (<code>allApplied: true</code>). Failure path: a batch with one write targeting a path <strong>outside</strong> <code>pyric_oracle/*</code> rejects with <code>code: 'permission-denied'</code> and leaves the would-have-set doc absent and the would-have-updated doc at its original value (<code>noPartialApply: true</code>) — atomicity verified end-to-end.</div></div>
|
|
482
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L37" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-96-batch-commit-atomic.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-96-batch-commit-atomic.json</code></a> — success path: a batch with <code>set</code> (fresh doc), <code>update</code> (existing doc), and <code>delete</code> (existing doc) all land in a single commit (<code>allApplied: true</code>). Failure path: a batch with one write targeting a path <strong>outside</strong> <code>pyric_oracle/*</code> rejects with <code>code: 'permission-denied'</code> and leaves the would-have-set doc absent and the would-have-updated doc at its original value (<code>noPartialApply: true</code>) — atomicity verified end-to-end.</div></div>
|
|
483
483
|
</details>
|
|
484
484
|
<details class="compat-row" data-status="ok">
|
|
485
485
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">writeBatch(db)</code><span class="compat-sub"><span class="compat-behavior">Batch is tagged on construction and remains bound to the sandbox owner that created it</span></span></span></summary>
|
|
486
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/sandbox-live-identity.test.ts</code> constructs a batch from sandbox A with a reference from sandbox B and proves the commit remains isolated to A.</div></div>
|
|
486
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L37" target="_blank" rel="noopener noreferrer"><code>unit:firestore/sandbox-live-identity.test.ts</code></a> constructs a batch from sandbox A with a reference from sandbox B and proves the commit remains isolated to A.</div></div>
|
|
487
487
|
</details>
|
|
488
488
|
<details class="compat-row" data-status="ok">
|
|
489
489
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">writeBatch(db)</code><span class="compat-sub"><span class="compat-behavior">Batch identity is frozen at construction (per current implementation)</span></span></span></summary>
|
|
490
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/sandbox-live-identity.test.ts</code> constructs under alice, switches the live session to bob, and proves commit still succeeds under alice's captured identity.</div></div>
|
|
490
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L37" target="_blank" rel="noopener noreferrer"><code>unit:firestore/sandbox-live-identity.test.ts</code></a> constructs under alice, switches the live session to bob, and proves commit still succeeds under alice's captured identity.</div></div>
|
|
491
491
|
</details>
|
|
492
492
|
</div>
|
|
493
493
|
<h2 id="sentinels--servertimestamp--increment--arrayunion--arrayremove--deletefield--fieldvalue--timestamp">Sentinels — <code>serverTimestamp</code> / <code>increment</code> / <code>arrayUnion</code> / <code>arrayRemove</code> / <code>deleteField</code> / <code>FieldValue</code> / <code>Timestamp</code></h2>
|
|
494
494
|
<div class="compat-list">
|
|
495
495
|
<details class="compat-row" data-status="ok">
|
|
496
496
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><code>serverTimestamp()</code> resolves to a <code>Timestamp</code> after the write commits</span></span></span></summary>
|
|
497
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-sentinels</code> (bundled) + <code>playground:firestore-row-99-servertimestamp-resolves</code> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-99-servertimestamp-resolves-to-timestamp.json</code> — <code>setDoc({at: serverTimestamp()})</code> then <code>getDoc</code> yields <code>at instanceof Timestamp === true</code>, <code>constructor.name === 'Timestamp'</code>, with both <code>.seconds</code> (number) and <code>.nanoseconds</code> (number) present.</div></div>
|
|
497
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L40" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-sentinels</code> (bundled) + <code>playground:firestore-row-99-servertimestamp-resolves</code> (one-claim), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-99-servertimestamp-resolves-to-timestamp.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-99-servertimestamp-resolves-to-timestamp.json</code></a> — <code>setDoc({at: serverTimestamp()})</code> then <code>getDoc</code> yields <code>at instanceof Timestamp === true</code>, <code>constructor.name === 'Timestamp'</code>, with both <code>.seconds</code> (number) and <code>.nanoseconds</code> (number) present.</div></div>
|
|
498
498
|
</details>
|
|
499
499
|
<details class="compat-row" data-status="ok">
|
|
500
500
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><code>increment(n)</code> atomically bumps a numeric field; <code>null</code>/missing field starts from 0</span></span></span></summary>
|
|
501
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-sentinels</code> (bundled) + <code>playground:firestore-row-100-increment-bumps-numeric</code> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-100-increment-bumps-numeric.json</code> — <code>setDoc</code> with no <code>count</code> field then <code>updateDoc({count: increment(5)})</code> yields <code>count === 5</code> (starts from 0). Follow-up <code>increment(3)</code> → 8, then <code>increment(-2)</code> → 6 (negative deltas apply, increments accumulate). Merge-create + int↔double + batch-across-docs: <code>unit:upstream-transform-txn-listener-probes.test.ts</code></div></div>
|
|
501
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L41" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-sentinels</code> (bundled) + <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-100-increment-bumps-numeric.json" target="_blank" rel="noopener noreferrer"><code>playground:firestore-row-100-increment-bumps-numeric</code></a> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-100-increment-bumps-numeric.json</code> — <code>setDoc</code> with no <code>count</code> field then <code>updateDoc({count: increment(5)})</code> yields <code>count === 5</code> (starts from 0). Follow-up <code>increment(3)</code> → 8, then <code>increment(-2)</code> → 6 (negative deltas apply, increments accumulate). Merge-create + int↔double + batch-across-docs: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-transform-txn-listener-probes.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>unit:upstream-transform-txn-listener-probes.test.ts</code></a></div></div>
|
|
502
502
|
</details>
|
|
503
503
|
<details class="compat-row" data-status="ok">
|
|
504
504
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><code>arrayUnion(...values)</code> de-dupes against existing members <strong>and</strong> against duplicate args within the same call</span></span></span></summary>
|
|
505
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-sentinels</code> (bundled) + <code>playground:firestore-row-101-arrayunion-dedupes</code> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-101-arrayunion-dedupes.json</code> — <code>setDoc({tags: ['a','b']})</code> then <code>updateDoc({tags: arrayUnion('b','c')})</code> yields <code>['a','b','c']</code> (single <code>b</code>, not double). Follow-up <code>updateDoc({tags: arrayUnion('d','d','a')})</code> yields <code>['a','b','c','d']</code> — both inline duplicate args and existing-member duplicates are de-duped. Merge-path + object members: <code>unit:upstream-transform-txn-listener-probes.test.ts</code></div></div>
|
|
505
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L42" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-sentinels</code> (bundled) + <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-101-arrayunion-dedupes.json" target="_blank" rel="noopener noreferrer"><code>playground:firestore-row-101-arrayunion-dedupes</code></a> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-101-arrayunion-dedupes.json</code> — <code>setDoc({tags: ['a','b']})</code> then <code>updateDoc({tags: arrayUnion('b','c')})</code> yields <code>['a','b','c']</code> (single <code>b</code>, not double). Follow-up <code>updateDoc({tags: arrayUnion('d','d','a')})</code> yields <code>['a','b','c','d']</code> — both inline duplicate args and existing-member duplicates are de-duped. Merge-path + object members: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-transform-txn-listener-probes.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>unit:upstream-transform-txn-listener-probes.test.ts</code></a></div></div>
|
|
506
506
|
</details>
|
|
507
507
|
<details class="compat-row" data-status="ok">
|
|
508
508
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><code>arrayRemove(...values)</code> strips matching members; values not present in the array are silent no-ops</span></span></span></summary>
|
|
509
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code>, <code>playground:firestore-sentinels</code> (bundled) + <code>playground:firestore-row-102-arrayremove-strips</code> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-102-arrayremove-strips.json</code> — <code>setDoc({tags: ['a','b','c']})</code> then <code>updateDoc({tags: arrayRemove('b','d')})</code> yields <code>['a','c']</code>: <code>'b'</code> removed, <code>'d'</code> (absent) was a silent no-op (no error). Merge-path: <code>unit:upstream-transform-txn-listener-probes.test.ts</code></div></div>
|
|
509
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L43" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a>, <code>playground:firestore-sentinels</code> (bundled) + <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-102-arrayremove-strips.json" target="_blank" rel="noopener noreferrer"><code>playground:firestore-row-102-arrayremove-strips</code></a> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-102-arrayremove-strips.json</code> — <code>setDoc({tags: ['a','b','c']})</code> then <code>updateDoc({tags: arrayRemove('b','d')})</code> yields <code>['a','c']</code>: <code>'b'</code> removed, <code>'d'</code> (absent) was a silent no-op (no error). Merge-path: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-transform-txn-listener-probes.test.ts#L30" target="_blank" rel="noopener noreferrer"><code>unit:upstream-transform-txn-listener-probes.test.ts</code></a></div></div>
|
|
510
510
|
</details>
|
|
511
511
|
<details class="compat-row" data-status="ok">
|
|
512
512
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><code>deleteField()</code> removes a field on update — the field is fully absent from the returned data, not merely undefined-valued. Legal at the top level or via a <strong>dot-path</strong> (<code>{'a.b': deleteField()}</code> removes the nested leaf — FS-B5). <strong>Nested inside a map literal (<code>{a: {b: deleteField()}}</code>) it throws <code>invalid-argument</code> (FS-B13)</strong> instead of destroying the sibling map.</span></span></span></summary>
|
|
513
|
-
<div class="compat-evidence"><div class="compat-probe"><code>playground:firestore-sentinels</code> (bundled) + <code>playground:firestore-row-103-deletefield-removes-field</code> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-103-deletefield-removes-field.json</code> — <code>setDoc({keep:1, remove:2})</code> then <code>updateDoc({remove: deleteField()})</code> yields a doc whose <code>data()</code> has keys <code>['keep']</code> only, <code>keep === 1</code> preserved; <code>unit:admin-compat/nested-delete-field.test.ts</code> (nested → invalid-argument; dot-path + top-level still valid; verified failing pre-fix)</div></div>
|
|
513
|
+
<div class="compat-evidence"><div class="compat-probe"><code>playground:firestore-sentinels</code> (bundled) + <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-103-deletefield-removes-field.json" target="_blank" rel="noopener noreferrer"><code>playground:firestore-row-103-deletefield-removes-field</code></a> (one-claim), oracle: <code>packages/conformance/observations/firestore/firestore-row-103-deletefield-removes-field.json</code> — <code>setDoc({keep:1, remove:2})</code> then <code>updateDoc({remove: deleteField()})</code> yields a doc whose <code>data()</code> has keys <code>['keep']</code> only, <code>keep === 1</code> preserved; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/nested-delete-field.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/nested-delete-field.test.ts</code></a> (nested → invalid-argument; dot-path + top-level still valid; verified failing pre-fix)</div></div>
|
|
514
514
|
</details>
|
|
515
515
|
<details class="compat-row" data-status="ok">
|
|
516
516
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><code>Timestamp</code> shape (<code>{seconds, nanoseconds}</code>) is identical between prod and sandbox — round-trips cleanly</span></span></span></summary>
|
|
517
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
517
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L40" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
518
518
|
</details>
|
|
519
519
|
<details class="compat-row" data-status="ok">
|
|
520
520
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><strong><code>Timestamp</code> nanos normalization + value API (FS-B12)</strong> — <code>fromMillis</code>/<code>fromDate</code>/<code>now</code> derive <code>nanoseconds</code> as <code>floor((ms - seconds<em>1000) </em> 1e6)</code> so it is always non-negative; <code>fromMillis(-500).toMillis()</code> round-trips to -500 (was -1500). The class ships <code>isEqual</code> / <code>toString</code> / <code>toJSON</code> / <code>valueOf</code>, mirroring <code>clones/.../lite-api/timestamp.ts</code>.</span></span></span></summary>
|
|
521
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:admin-compat/timestamp-api.test.ts</code> (negative-millis round-trip + value API; pre-fix lacked the methods and mis-normalized)</div></div>
|
|
521
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/admin-compat/timestamp-api.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:admin-compat/timestamp-api.test.ts</code></a> (negative-millis round-trip + value API; pre-fix lacked the methods and mis-normalized)</div></div>
|
|
522
522
|
</details>
|
|
523
523
|
<details class="compat-row" data-status="ok">
|
|
524
524
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><strong>Unified Timestamp storage (FS-B4)</strong> — a <code>Timestamp</code> written directly via the modular SDK (<code>setDoc({createdAt: Timestamp.now()})</code>) is stored as the same rules-internal <code>Timestamp</code> that <code>serverTimestamp()</code>/<code>Date</code> resolve to. Pre-FS-B4 a user-written <code>Timestamp</code> was the compat class only (not a <code>RulesValue</code>), so <code>request.resource.data.createdAt is timestamp</code> returned <strong>false</strong> for it while a <code>serverTimestamp()</code> write passed the same rule, and the two paths stored two different classes. A write-boundary converter now normalizes both.</span></span></span></summary>
|
|
525
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/sandbox-converters/user-timestamp.test.ts</code> (<code>is timestamp</code> passes for a user Timestamp; unified storage class; range-filter regression guard — verified failing pre-fix by removing the converter registration)</div></div>
|
|
525
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-converters/user-timestamp.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:firestore/sandbox-converters/user-timestamp.test.ts</code></a> (<code>is timestamp</code> passes for a user Timestamp; unified storage class; range-filter regression guard — verified failing pre-fix by removing the converter registration)</div></div>
|
|
526
526
|
</details>
|
|
527
527
|
<details class="compat-row" data-status="ok">
|
|
528
528
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><code>FieldValue</code> re-exported from <code>pyric-admin</code> (alias of <code>ChainFieldValue</code>)</span></span></span></summary>
|
|
@@ -530,76 +530,76 @@
|
|
|
530
530
|
</details>
|
|
531
531
|
<details class="compat-row" data-status="ok">
|
|
532
532
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Sentinels</code><span class="compat-sub"><span class="compat-behavior"><strong>Sentinel overwrite on type mismatch (FS-B11)</strong> — <code>increment(n)</code> on a non-numeric (or absent) prior OVERWRITES using a base value of 0 (result <code>n</code>); <code>arrayUnion</code>/<code>arrayRemove</code> on a non-array prior coerce the base to <code>[]</code>. Pre-FS-B11 these threw and surfaced as <code>invalid-argument</code> denials. Mirrors <code>clones/.../model/transform_operation.ts</code> (<code>computeTransformOperationBaseValue</code>, <code>coercedFieldValuesArray</code>).</span></span></span></summary>
|
|
533
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:simulator/converters/fieldvalue.test.ts</code> (FS-B11 overwrite block + flipped unit/integration/batch cases; verified failing pre-fix)</div></div>
|
|
533
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/simulator/converters/fieldvalue.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>unit:simulator/converters/fieldvalue.test.ts</code></a> (FS-B11 overwrite block + flipped unit/integration/batch cases; verified failing pre-fix)</div></div>
|
|
534
534
|
</details>
|
|
535
535
|
</div>
|
|
536
536
|
<h2 id="scalar-types--bytes--geopoint--fieldpath--documentid">Scalar types — <code>Bytes</code> / <code>GeoPoint</code> / <code>FieldPath</code> / <code>documentId</code></h2>
|
|
537
537
|
<div class="compat-list">
|
|
538
538
|
<details class="compat-row" data-status="ok">
|
|
539
539
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Scalar types</code><span class="compat-sub"><span class="compat-behavior">The sandbox mirror owns compatible scalar constructors — <code>Bytes.fromUint8Array(...)</code>, <code>new GeoPoint(lat, lng)</code>, <code>new FieldPath(...)</code>, and <code>documentId()</code> — without importing <code>firebase/firestore</code></span></span></span></summary>
|
|
540
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> (constructibility + round trips), <code>package-edge:package-dependencies.test.ts</code></div></div>
|
|
540
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L46" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> (constructibility + round trips), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/package-dependencies.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>package-edge:package-dependencies.test.ts</code></a></div></div>
|
|
541
541
|
</details>
|
|
542
542
|
<details class="compat-row" data-status="ok">
|
|
543
543
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Scalar types</code><span class="compat-sub"><span class="compat-behavior"><code>documentId()</code> works in <code>where(documentId(), …)</code> / <code>orderBy(documentId())</code> against the sandbox — string ids, DocumentReference operands, ranges, and id sort</span></span></span></summary>
|
|
544
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:upstream-query-probes.test.ts</code> (<code>documentId() filters + orderBy</code>); modular <code>where</code>/<code>orderBy</code> accept <code>FieldPath</code></div></div>
|
|
544
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/upstream-query-probes.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:upstream-query-probes.test.ts</code></a> (<code>documentId() filters + orderBy</code>); modular <code>where</code>/<code>orderBy</code> accept <code>FieldPath</code></div></div>
|
|
545
545
|
</details>
|
|
546
546
|
<details class="compat-row" data-status="ok">
|
|
547
547
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Scalar types</code><span class="compat-sub"><span class="compat-behavior"><code>FieldPath</code> (nested) works in queries against sandbox</span></span></span></summary>
|
|
548
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
548
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L870" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
549
549
|
</details>
|
|
550
550
|
<details class="compat-row" data-status="ok">
|
|
551
551
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Scalar types</code><span class="compat-sub"><span class="compat-behavior"><code>Bytes</code> round-trip through the sandbox wire encoder — <code>Bytes</code> written via <code>setDoc</code> reads back as a <code>Bytes</code> instance with the same base64 representation</span></span></span></summary>
|
|
552
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:packages/pyric/test/sandbox/firestore/wire-encoder-bytes-geopoint.test.ts</code> + <code>unit:packages/pyric/test/firestore/sandbox-target.test.ts</code> ("Bytes + GeoPoint round-trip"), oracle: <code>packages/conformance/observations/firestore/firestore-row-109-bytes-roundtrip.json</code> — <code>setDoc({payload: Bytes.fromUint8Array([1,2,3,4])})</code> then <code>getDoc</code> yields <code>payload instanceof Bytes === true</code>, <code>payload.constructor.name === 'Bytes'</code>, <code>payload.toBase64() === 'AQIDBA=='</code>, and <code>payload.toUint8Array()</code> returns <code>[1,2,3,4]</code> against blockingfun. The sandbox converter stores the rules <code>Bytes</code> wrapper; <code>pyric/firestore</code> finalizes reads into its locally owned <code>Bytes</code> class with the same observed methods and values.</div></div>
|
|
552
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/wire-encoder-bytes-geopoint.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:packages/pyric/test/sandbox/firestore/wire-encoder-bytes-geopoint.test.ts</code></a> + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L46" target="_blank" rel="noopener noreferrer"><code>unit:packages/pyric/test/firestore/sandbox-target.test.ts</code></a> ("Bytes + GeoPoint round-trip"), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-109-bytes-roundtrip.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-109-bytes-roundtrip.json</code></a> — <code>setDoc({payload: Bytes.fromUint8Array([1,2,3,4])})</code> then <code>getDoc</code> yields <code>payload instanceof Bytes === true</code>, <code>payload.constructor.name === 'Bytes'</code>, <code>payload.toBase64() === 'AQIDBA=='</code>, and <code>payload.toUint8Array()</code> returns <code>[1,2,3,4]</code> against blockingfun. The sandbox converter stores the rules <code>Bytes</code> wrapper; <code>pyric/firestore</code> finalizes reads into its locally owned <code>Bytes</code> class with the same observed methods and values.</div></div>
|
|
553
553
|
</details>
|
|
554
554
|
<details class="compat-row" data-status="ok">
|
|
555
555
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Scalar types</code><span class="compat-sub"><span class="compat-behavior"><code>GeoPoint</code> round-trip through the sandbox wire encoder — <code>GeoPoint</code> written via <code>setDoc</code> reads back as a <code>GeoPoint</code> instance with the same latitude / longitude</span></span></span></summary>
|
|
556
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:packages/pyric/test/sandbox/firestore/wire-encoder-bytes-geopoint.test.ts</code> + <code>unit:packages/pyric/test/firestore/sandbox-target.test.ts</code> ("Bytes + GeoPoint round-trip"), oracle: <code>packages/conformance/observations/firestore/firestore-row-110-geopoint-roundtrip.json</code> — <code>setDoc({loc: new GeoPoint(37.7749, -122.4194)})</code> then <code>getDoc</code> yields <code>loc instanceof GeoPoint === true</code>, <code>loc.constructor.name === 'GeoPoint'</code>, <code>loc.latitude === 37.7749</code>, <code>loc.longitude === -122.4194</code> against blockingfun. Sandbox storage uses the rules <code>LatLng</code> wrapper; <code>pyric/firestore</code> finalizes reads into its locally owned <code>GeoPoint</code> class.</div></div>
|
|
556
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/sandbox/firestore/wire-encoder-bytes-geopoint.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:packages/pyric/test/sandbox/firestore/wire-encoder-bytes-geopoint.test.ts</code></a> + <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L47" target="_blank" rel="noopener noreferrer"><code>unit:packages/pyric/test/firestore/sandbox-target.test.ts</code></a> ("Bytes + GeoPoint round-trip"), oracle: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-row-110-geopoint-roundtrip.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-row-110-geopoint-roundtrip.json</code></a> — <code>setDoc({loc: new GeoPoint(37.7749, -122.4194)})</code> then <code>getDoc</code> yields <code>loc instanceof GeoPoint === true</code>, <code>loc.constructor.name === 'GeoPoint'</code>, <code>loc.latitude === 37.7749</code>, <code>loc.longitude === -122.4194</code> against blockingfun. Sandbox storage uses the rules <code>LatLng</code> wrapper; <code>pyric/firestore</code> finalizes reads into its locally owned <code>GeoPoint</code> class.</div></div>
|
|
557
557
|
</details>
|
|
558
558
|
<details class="compat-row" data-status="ok">
|
|
559
559
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Scalar types</code><span class="compat-sub"><span class="compat-behavior">Vector value type (<code>vector()</code> + <code>VectorValue</code>) round-trip: a vector written via <code>setDoc</code> reads back as a <code>VectorValue</code> with the same components</span></span></span></summary>
|
|
560
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> ("Bytes + GeoPoint + VectorValue round-trip", top-level + nested). The locally owned <code>vector()</code> / <code>VectorValue</code> preserve Firebase's observable value shape; the sandbox converter stores the rules <code>Vector</code> wrapper and <code>pyric/firestore</code> finalizes reads back to <code>VectorValue</code>. Oracle observation to follow (cf. #109/#110). <strong>CLIENT surface only:</strong> the web SDK exposes <code>vector()</code> + <code>VectorValue</code> (read/write) but has NO <code>findNearest</code> and NO <code>FieldValue.vector</code>; vector SEARCH is admin/server-only (<code>firebase-admin</code> <code>Query</code>/<code>CollectionReference.findNearest</code> + <code>FieldValue.vector()</code>), out of scope for this client matrix; the admin surface is tracked in the design rationale.</div></div>
|
|
560
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L48" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> ("Bytes + GeoPoint + VectorValue round-trip", top-level + nested). The locally owned <code>vector()</code> / <code>VectorValue</code> preserve Firebase's observable value shape; the sandbox converter stores the rules <code>Vector</code> wrapper and <code>pyric/firestore</code> finalizes reads back to <code>VectorValue</code>. Oracle observation to follow (cf. #109/#110). <strong>CLIENT surface only:</strong> the web SDK exposes <code>vector()</code> + <code>VectorValue</code> (read/write) but has NO <code>findNearest</code> and NO <code>FieldValue.vector</code>; vector SEARCH is admin/server-only (<code>firebase-admin</code> <code>Query</code>/<code>CollectionReference.findNearest</code> + <code>FieldValue.vector()</code>), out of scope for this client matrix; the admin surface is tracked in the design rationale.</div></div>
|
|
561
561
|
</details>
|
|
562
562
|
</div>
|
|
563
563
|
<h2 id="equality-helpers--refequal--queryequal--snapshotequal">Equality helpers — <code>refEqual</code> / <code>queryEqual</code> / <code>snapshotEqual</code></h2>
|
|
564
564
|
<div class="compat-list">
|
|
565
565
|
<details class="compat-row" data-status="ok">
|
|
566
566
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Equality helpers</code><span class="compat-sub"><span class="compat-behavior"><code>refEqual(a, b)</code> — true when paths match under the same target</span></span></span></summary>
|
|
567
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
567
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L840" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
568
568
|
</details>
|
|
569
569
|
<details class="compat-row" data-status="ok">
|
|
570
570
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Equality helpers</code><span class="compat-sub"><span class="compat-behavior"><code>refEqual</code> is <code>true</code> for cross-flavor sandbox vs sandbox-live refs at the same path</span></span></span></summary>
|
|
571
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("refEqual returns true for live and frozen refs at the same path")</div></div>
|
|
571
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L38" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("refEqual returns true for live and frozen refs at the same path")</div></div>
|
|
572
572
|
</details>
|
|
573
573
|
<details class="compat-row" data-status="ok">
|
|
574
574
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Equality helpers</code><span class="compat-sub"><span class="compat-behavior"><code>refEqual</code> is <code>false</code> for refs at different paths</span></span></span></summary>
|
|
575
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code></div></div>
|
|
575
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L38" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a></div></div>
|
|
576
576
|
</details>
|
|
577
577
|
<details class="compat-row" data-status="ok">
|
|
578
578
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Equality helpers</code><span class="compat-sub"><span class="compat-behavior"><code>refEqual(sandboxRef, foreignRef)</code> throws <code>TypeError</code> — references not created by this sandbox mirror are unrecognized</span></span></span></summary>
|
|
579
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code> (foreign refs throw unrecognized-reference TypeError)</div></div>
|
|
579
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L840" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a> (foreign refs throw unrecognized-reference TypeError)</div></div>
|
|
580
580
|
</details>
|
|
581
581
|
<details class="compat-row" data-status="ok">
|
|
582
582
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Equality helpers</code><span class="compat-sub"><span class="compat-behavior"><code>queryEqual(a, b)</code> structurally compares collection and collection-group scope, filter/order/limit/cursor constraint structure, converter identity, and construction-time Firestore value snapshots for both equality and execution without re-observing caller objects. Snapshots cover maps/arrays, scalar objects, references (including raw and converted <code>addDoc()</code> results), and vectors; normalize Date to Timestamp; preserve the distinction between -0 and 0; compare snapshot and explicit bounds for all four cursor overloads by value without invoking snapshot converters, including across live-target rebuilds; reject undefined, bigint, nested arrays for ordinary and array-membership operands, and recursively nested cross-database references during query construction; and preserve Firebase's nested-array allowance for <code>in</code>/<code>not-in</code> candidate lists.</span></span></span></summary>
|
|
583
|
-
<div class="compat-evidence"><div class="compat-probe">Oracle-locked by <code>packages/conformance/observations/firestore/firestore-queryequal-structural.json</code>: collection and collection-group scope, order sequence/direction, limits, composite filters, cursor values/inclusivity, converters, and Firestore operands all distinguish equal from changed queries as production does. The capture executes mutable maps, Timestamp, Bytes, GeoPoint, DocumentReference (including raw and converted <code>addDoc()</code> results), and Vector queries; proves Bytes/Vector source-array mutation cannot change existing or new queries; proves all four snapshot cursor overloads compare with explicit bounds and execute twice without invoking a consumer converter; and locks recursive foreign-reference rejection, Date/Timestamp normalization, and -0 handling. <code>firestore-query-nested-array-validation.json</code> separately pins the production SDK's client-side rejection of nested arrays without implying a cloud round trip. The replay forces live-target identity rebuilds for every snapshot cursor overload. <code>oracle:firestore#116</code> replays every claimed fact
|
|
583
|
+
<div class="compat-evidence"><div class="compat-probe">Oracle-locked by <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-queryequal-structural.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-queryequal-structural.json</code></a>: collection and collection-group scope, order sequence/direction, limits, composite filters, cursor values/inclusivity, converters, and Firestore operands all distinguish equal from changed queries as production does. The capture executes mutable maps, Timestamp, Bytes, GeoPoint, DocumentReference (including raw and converted <code>addDoc()</code> results), and Vector queries; proves Bytes/Vector source-array mutation cannot change existing or new queries; proves all four snapshot cursor overloads compare with explicit bounds and execute twice without invoking a consumer converter; and locks recursive foreign-reference rejection, Date/Timestamp normalization, and -0 handling. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-query-nested-array-validation.json" target="_blank" rel="noopener noreferrer"><code>firestore-query-nested-array-validation.json</code></a> separately pins the production SDK's client-side rejection of nested arrays without implying a cloud round trip. The replay forces live-target identity rebuilds for every snapshot cursor overload. <code>oracle:firestore#116</code> replays every claimed fact. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L1047" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/equality.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>equality.test.ts</code></a>)</div></div>
|
|
584
584
|
</details>
|
|
585
585
|
<details class="compat-row" data-status="ok">
|
|
586
586
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Equality helpers</code><span class="compat-sub"><span class="compat-behavior"><code>snapshotEqual(a, b)</code> follows production snapshot state. A query's first one-shot read differs from the next identical read, then repeated settled reads (including an independently built equivalent query) compare structurally until query state changes and settles again. Listener snapshots compare query, documents, changes, and metadata. Document snapshots compare path, existence, Firestore-typed data, converter identity, and snapshot kind, so two gets of the same document are equal while a query-child snapshot and a direct get are distinct; scalar-shaped plain maps remain distinct from Timestamp, reference, GeoPoint, and Vector values.</span></span></span></summary>
|
|
587
|
-
<div class="compat-evidence"><div class="compat-probe">Oracle-locked by <code>packages/conformance/observations/firestore/firestore-snapshotequal-structural.json</code>: sequential and equivalent-query reads prove the initial-to-settled transition; a changed result restarts that transition; listener sensitivity cases vary query, documents/change history, and metadata-only state; direct document cases vary path, existence, data, converter, and snapshot kind; and four collision probes distinguish scalar-shaped maps from their Timestamp, reference, GeoPoint, and Vector counterparts. <code>oracle:firestore#117</code> replays every reachable fact. JSON reconstruction remains raw characterization only because Pyric does not expose that separate API
|
|
587
|
+
<div class="compat-evidence"><div class="compat-probe">Oracle-locked by <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-snapshotequal-structural.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-snapshotequal-structural.json</code></a>: sequential and equivalent-query reads prove the initial-to-settled transition; a changed result restarts that transition; listener sensitivity cases vary query, documents/change history, and metadata-only state; direct document cases vary path, existence, data, converter, and snapshot kind; and four collision probes distinguish scalar-shaped maps from their Timestamp, reference, GeoPoint, and Vector counterparts. <code>oracle:firestore#117</code> replays every reachable fact. JSON reconstruction remains raw characterization only because Pyric does not expose that separate API. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L1454" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/equality.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>equality.test.ts</code></a>)</div></div>
|
|
588
588
|
</details>
|
|
589
589
|
<details class="compat-row" data-status="ok">
|
|
590
590
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Equality helpers</code><span class="compat-sub"><span class="compat-behavior">Cross-flavor <code>refEqual</code> via <code>QuerySnapshot.docs[i].ref</code> works</span></span></span></summary>
|
|
591
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("cross-flavor refEqual via QuerySnapshot doc refs")</div></div>
|
|
591
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L38" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("cross-flavor refEqual via QuerySnapshot doc refs")</div></div>
|
|
592
592
|
</details>
|
|
593
593
|
</div>
|
|
594
594
|
<h2 id="connectfirestoreemulator"><code>connectFirestoreEmulator</code></h2>
|
|
595
595
|
<div class="compat-list">
|
|
596
596
|
<details class="compat-row" data-status="ok">
|
|
597
597
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">connectFirestoreEmulator</code><span class="compat-sub"><span class="compat-behavior">No-op on sandbox-target handles (the sandbox already IS a local emulator)</span></span></span></summary>
|
|
598
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-target.test.ts</code></div></div>
|
|
598
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-target.test.ts#L861" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-target.test.ts</code></a></div></div>
|
|
599
599
|
</details>
|
|
600
600
|
<details class="compat-row" data-status="ok">
|
|
601
601
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">connectFirestoreEmulator</code><span class="compat-sub"><span class="compat-behavior">Production does not enter the mirror: inactive package resolution leaves Firebase's <code>connectFirestoreEmulator</code> implementation unchanged</span></span></span></summary>
|
|
602
|
-
<div class="compat-evidence"><div class="compat-probe"><code>node-register:register-child.test.ts</code> (inactive canonical Firestore is not rewritten)</div></div>
|
|
602
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>node-register:register-child.test.ts</code></a> (inactive canonical Firestore is not rewritten)</div></div>
|
|
603
603
|
</details>
|
|
604
604
|
<details class="compat-row" data-status="ok">
|
|
605
605
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">connectFirestoreEmulator</code><span class="compat-sub"><span class="compat-behavior">The sandbox mirror accepts Firebase's <code>mockUserToken</code> option shape as an inert compatibility argument; production uses Firebase's untouched implementation</span></span></span></summary>
|
|
@@ -611,27 +611,27 @@
|
|
|
611
611
|
<div class="compat-list">
|
|
612
612
|
<details class="compat-row" data-status="ok">
|
|
613
613
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">enableIndexedDbPersistence</code><span class="compat-sub"><span class="compat-behavior">Resolves before Firestore starts and rejects with <code>failed-precondition</code> after any operation has started the service</span></span></span></summary>
|
|
614
|
-
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <code>firestore-browser-lifecycle.json</code>; replayed through the public modular API by <code>oracle-conformance.test.ts</code> and focused lifecycle coverage
|
|
614
|
+
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-browser-lifecycle.json" target="_blank" rel="noopener noreferrer"><code>firestore-browser-lifecycle.json</code></a>; replayed through the public modular API by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L653" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a> and focused lifecycle coverage. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/persistence-network.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>persistence-network.test.ts</code></a>)</div></div>
|
|
615
615
|
</details>
|
|
616
616
|
<details class="compat-row" data-status="ok">
|
|
617
617
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">enableMultiTabIndexedDbPersistence</code><span class="compat-sub"><span class="compat-behavior">Enables multi-client shared persistence before first use, permits two clients to opt in, and rejects initialization after a client has started with <code>failed-precondition</code></span></span></span></summary>
|
|
618
|
-
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <code>firestore-browser-lifecycle.json</code> exercises fresh, after-use, and two-client initialization; replayed through public APIs by <code>oracle-conformance.test.ts</code> and focused persistence tests
|
|
618
|
+
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-browser-lifecycle.json" target="_blank" rel="noopener noreferrer"><code>firestore-browser-lifecycle.json</code></a> exercises fresh, after-use, and two-client initialization; replayed through public APIs by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L653" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a> and focused persistence tests. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/persistence-network.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>persistence-network.test.ts</code></a>)</div></div>
|
|
619
619
|
</details>
|
|
620
620
|
<details class="compat-row" data-status="ok">
|
|
621
621
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">clearIndexedDbPersistence</code><span class="compat-sub"><span class="compat-behavior">Maps to <code>Sandbox.clearPersistence()</code> — actually wipes the persisted blob (honest, not a no-op); already a no-op when persistence was never enabled</span></span></span></summary>
|
|
622
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/persistence-network.test.ts</code></div></div>
|
|
622
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/persistence-network.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:firestore/persistence-network.test.ts</code></a></div></div>
|
|
623
623
|
</details>
|
|
624
624
|
<details class="compat-row" data-status="ok">
|
|
625
625
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">enableNetwork / disableNetwork</code><span class="compat-sub"><span class="compat-behavior">Disabling network exposes local mutations with cache/pending-write metadata while holding write acknowledgement until network is re-enabled</span></span></span></summary>
|
|
626
|
-
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <code>firestore-browser-lifecycle.json</code>; replayed through the public modular API by <code>oracle-conformance.test.ts</code> and focused network coverage
|
|
626
|
+
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-browser-lifecycle.json" target="_blank" rel="noopener noreferrer"><code>firestore-browser-lifecycle.json</code></a>; replayed through the public modular API by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L653" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a> and focused network coverage. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/persistence-network.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>persistence-network.test.ts</code></a>)</div></div>
|
|
627
627
|
</details>
|
|
628
628
|
<details class="compat-row" data-status="ok">
|
|
629
629
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">waitForPendingWrites</code><span class="compat-sub"><span class="compat-behavior">Waits for outstanding offline write acknowledgements and resolves when network re-enable drains them</span></span></span></summary>
|
|
630
|
-
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <code>firestore-browser-lifecycle.json</code>; replayed through the public modular API by <code>oracle-conformance.test.ts</code> and focused pending-write coverage
|
|
630
|
+
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-browser-lifecycle.json" target="_blank" rel="noopener noreferrer"><code>firestore-browser-lifecycle.json</code></a>; replayed through the public modular API by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L653" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a> and focused pending-write coverage. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/persistence-network.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>persistence-network.test.ts</code></a>)</div></div>
|
|
631
631
|
</details>
|
|
632
632
|
<details class="compat-row" data-status="ok">
|
|
633
633
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">terminate</code><span class="compat-sub"><span class="compat-behavior">Terminates only the selected Firestore service instance: held refs reject with <code>failed-precondition</code>, owned listeners stop, and sibling Firestore or Auth services remain usable</span></span></span></summary>
|
|
634
|
-
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <code>packages/conformance/observations/firestore/firestore-browser-lifecycle.json</code> records <code>failed-precondition</code> on the terminated instance while Auth and a sibling Firestore instance continue. Replayed by <code>oracle-conformance.test.ts</code>; focused ownership coverage in <code>terminate.test.ts</code>.</div></div>
|
|
634
|
+
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-browser-lifecycle.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-browser-lifecycle.json</code></a> records <code>failed-precondition</code> on the terminated instance while Auth and a sibling Firestore instance continue. Replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L653" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>; focused ownership coverage in <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/terminate.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>terminate.test.ts</code></a>.</div></div>
|
|
635
635
|
</details>
|
|
636
636
|
</div>
|
|
637
637
|
<h2 id="tier-1-cache-init--get-from--family">Tier-1 cache-init + get-from-* family</h2>
|
|
@@ -639,30 +639,54 @@
|
|
|
639
639
|
<div class="compat-list">
|
|
640
640
|
<details class="compat-row" data-status="diverged">
|
|
641
641
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">initializeFirestore</code><span class="compat-sub"><span class="compat-behavior">Delegates to sandbox <code>getFirestore(app)</code> and returns the same handle. Accepts the <code>settings</code> argument (so the explicit-init pattern doesn't crash at import) but no-ops the cache/network settings — persistence is always on</span></span></span></summary>
|
|
642
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code></div>
|
|
642
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:firestore/tier1-cache-init-align.test.ts</code></a></div>
|
|
643
643
|
<div class="compat-note">settings accepted but cache/network settings are no-ops</div></div>
|
|
644
644
|
</details>
|
|
645
645
|
<details class="compat-row" data-status="diverged">
|
|
646
646
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">persistentLocalCache / memoryLocalCache / persistentSingleTabManager / persistentMultipleTabManager / memoryEagerGarbageCollector / memoryLruGarbageCollector</code><span class="compat-sub"><span class="compat-behavior">Config token accepted, inert — each returns a small tagged object so identity/usage doesn't crash. Persistence is the sandbox default; there is no cache tier left to configure</span></span></span></summary>
|
|
647
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code></div>
|
|
647
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:firestore/tier1-cache-init-align.test.ts</code></a></div>
|
|
648
648
|
<div class="compat-note">inert config tokens; no cache tier to configure</div></div>
|
|
649
649
|
</details>
|
|
650
650
|
<details class="compat-row" data-status="ok">
|
|
651
651
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDocFromServer / getDocsFromServer</code><span class="compat-sub"><span class="compat-behavior">Delegates to <code>getDoc</code> / <code>getDocs</code> in the sandbox mirror — the sandbox store IS the authoritative source, so there is no separate server round-trip to force and no observable divergence from the default read</span></span></span></summary>
|
|
652
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code></div></div>
|
|
652
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:firestore/tier1-cache-init-align.test.ts</code></a></div></div>
|
|
653
653
|
</details>
|
|
654
654
|
<details class="compat-row" data-status="ok">
|
|
655
655
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getDocFromCache / getDocsFromCache</code><span class="compat-sub"><span class="compat-behavior">Explicit cache reads distinguish cold and warm state: cold document reads reject with <code>unavailable</code>, cold queries return empty, and server/default reads populate subsequent cache reads</span></span></span></summary>
|
|
656
|
-
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <code>firestore-browser-lifecycle.json</code> captures cold and warm document and query cache behavior; <code>oracle-conformance.test.ts</code> replays it through public APIs
|
|
656
|
+
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-browser-lifecycle.json" target="_blank" rel="noopener noreferrer"><code>firestore-browser-lifecycle.json</code></a> captures cold and warm document and query cache behavior; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L653" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a> replays it through public APIs. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L13" target="_blank" rel="noopener noreferrer"><code>tier1-cache-init-align.test.ts</code></a>)</div></div>
|
|
657
657
|
</details>
|
|
658
658
|
<details class="compat-row" data-status="diverged">
|
|
659
659
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">setLogLevel</code><span class="compat-sub"><span class="compat-behavior">Accepted no-op — the sandbox has no modular-SDK-style logger to wire a level into; it uses host-level <code>console</code> logging directly, gated by <code>pyric dev</code>'s own flags, not this call</span></span></span></summary>
|
|
660
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code></div>
|
|
660
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L13" target="_blank" rel="noopener noreferrer"><code>unit:firestore/tier1-cache-init-align.test.ts</code></a></div>
|
|
661
661
|
<div class="compat-note">accepted no-op; no sandbox logger wired</div></div>
|
|
662
662
|
</details>
|
|
663
663
|
<details class="compat-row" data-status="ok">
|
|
664
664
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onSnapshotsInSync</code><span class="compat-sub"><span class="compat-behavior">Emits an initial in-sync signal, batches another signal after snapshot listener delivery, and stops after unsubscribe</span></span></span></summary>
|
|
665
|
-
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <code>firestore-browser-lifecycle.json</code> records initial sync, write snapshot, then sync; replayed by <code>oracle-conformance.test.ts</code> and focused listener tests
|
|
665
|
+
<div class="compat-evidence"><div class="compat-probe">Real Chromium: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-browser-lifecycle.json" target="_blank" rel="noopener noreferrer"><code>firestore-browser-lifecycle.json</code></a> records initial sync, write snapshot, then sync; replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/oracle-conformance.test.ts#L653" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a> and focused listener tests. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>tier1-cache-init-align.test.ts</code></a>)</div></div>
|
|
666
|
+
</details>
|
|
667
|
+
<details class="compat-row" data-status="ok">
|
|
668
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Runtime ES Classes / Constructor Tokens</code><span class="compat-sub"><span class="compat-behavior">Exports ES classes and constructor tokens matching structural type handles for instanceof checks and prototype inheritance</span></span></span></summary>
|
|
669
|
+
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/runtime-classes.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>runtime-classes.test.ts</code></a>)</div></div>
|
|
670
|
+
</details>
|
|
671
|
+
<details class="compat-row" data-status="ok">
|
|
672
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">PersistentCacheIndexManager / Index auto-creation</code><span class="compat-sub"><span class="compat-behavior">Config tokens and index manager controller accepted as honest inert runtime tokens without failing execution</span></span></span></summary>
|
|
673
|
+
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/persistence-tokens.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>persistence-tokens.test.ts</code></a>)</div></div>
|
|
674
|
+
</details>
|
|
675
|
+
<details class="compat-row" data-status="ok">
|
|
676
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">loadBundle / namedQuery</code><span class="compat-sub"><span class="compat-behavior">Decodes string/ArrayBuffer bundle payloads into local sandbox store and registers named queries</span></span></span></summary>
|
|
677
|
+
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/bundles.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>bundles.test.ts</code></a>)</div></div>
|
|
678
|
+
</details>
|
|
679
|
+
<details class="compat-row" data-status="ok">
|
|
680
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">documentSnapshotFromJSON / querySnapshotFromJSON / onSnapshotResume</code><span class="compat-sub"><span class="compat-behavior">Deserializes JSON snapshot objects into active Pyric snapshot wrappers and attaches resume listener</span></span></span></summary>
|
|
681
|
+
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/ssr-snapshots.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>ssr-snapshots.test.ts</code></a>)</div></div>
|
|
682
|
+
</details>
|
|
683
|
+
<details class="compat-row" data-status="ok">
|
|
684
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">aggregateFieldEqual / aggregateQuerySnapshotEqual</code><span class="compat-sub"><span class="compat-behavior">Compares aggregate descriptors and snapshot data for value equality</span></span></span></summary>
|
|
685
|
+
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/runtime-classes.test.ts#L50" target="_blank" rel="noopener noreferrer"><code>runtime-classes.test.ts</code></a>)</div></div>
|
|
686
|
+
</details>
|
|
687
|
+
<details class="compat-row" data-status="ok">
|
|
688
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">ensureFirestoreConfigured / executeWrite</code><span class="compat-sub"><span class="compat-behavior">Validates sandbox initialization and executes write callback within mutation evaluation pipeline</span></span></span></summary>
|
|
689
|
+
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/tier1-cache-init-align.test.ts</code> (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/writes-plumbing.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>writes-plumbing.test.ts</code></a>)</div></div>
|
|
666
690
|
</details>
|
|
667
691
|
</div>
|
|
668
692
|
<h2 id="rules-engine-via-setrules-from-pyricsandboxfirestore">Rules engine (via <code>setRules</code> from <code>pyric/sandbox/firestore</code>)</h2>
|
|
@@ -677,19 +701,19 @@ shape consumer code depends on.</p>
|
|
|
677
701
|
</details>
|
|
678
702
|
<details class="compat-row" data-status="ok">
|
|
679
703
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>request.auth == null</code> when sandbox.currentUser is null (anonymous path)</span></span></summary>
|
|
680
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-live-identity.test.ts</code> ("anonymous fallback")</div></div>
|
|
704
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/sandbox-live-identity.test.ts#L23" target="_blank" rel="noopener noreferrer"><code>unit:sandbox-live-identity.test.ts</code></a> ("anonymous fallback")</div></div>
|
|
681
705
|
</details>
|
|
682
706
|
<details class="compat-row" data-status="ok">
|
|
683
707
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Cross-doc <code>get(/databases/$(database)/documents/...)</code> in rules works under sandbox; <code>get()</code> of a <strong>missing</strong> doc ERRORS (guard with <code>exists()</code>), and <code>get(p).id</code> / <code>get(p).__name__</code> expose the doc identity (RULES-B8)</span></span></summary>
|
|
684
|
-
<div class="compat-evidence"><div class="compat-probe"><code>playground:rules-cross-doc-get</code>, <code>unit:rules/simulator/evaluator.test.ts</code> (RULES-B8 block)</div></div>
|
|
708
|
+
<div class="compat-evidence"><div class="compat-probe"><code>playground:rules-cross-doc-get</code>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/evaluator.test.ts</code></a> (RULES-B8 block)</div></div>
|
|
685
709
|
</details>
|
|
686
710
|
<details class="compat-row" data-status="ok">
|
|
687
711
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>request.resource.data.<field></code> field validation in rules works under sandbox; an <strong>undefined</strong> field read ERRORS (deny), it does NOT read as null (RULES-B2) — guard with <code>'f' in data</code> / <code>data.get('f', d)</code></span></span></summary>
|
|
688
|
-
<div class="compat-evidence"><div class="compat-probe"><code>playground:rules-data-validation</code>, <code>unit:rules/simulator/evaluator.test.ts</code> (RULES-B2 block)</div></div>
|
|
712
|
+
<div class="compat-evidence"><div class="compat-probe"><code>playground:rules-data-validation</code>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/evaluator.test.ts</code></a> (RULES-B2 block)</div></div>
|
|
689
713
|
</details>
|
|
690
714
|
<details class="compat-row" data-status="ok">
|
|
691
715
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>resource.data.<field></code> (existing doc on writes) works under sandbox; undefined-field reads ERROR (RULES-B2)</span></span></summary>
|
|
692
|
-
<div class="compat-evidence"><div class="compat-probe"><code>playground:rules-resource-data-field</code>, <code>unit:rules/simulator/evaluator.test.ts</code> (RULES-B2 block)</div></div>
|
|
716
|
+
<div class="compat-evidence"><div class="compat-probe"><code>playground:rules-resource-data-field</code>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/evaluator.test.ts</code></a> (RULES-B2 block)</div></div>
|
|
693
717
|
</details>
|
|
694
718
|
<details class="compat-row" data-status="ok">
|
|
695
719
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Custom claims in <code>request.auth.token.<claim></code></span></span></summary>
|
|
@@ -697,43 +721,43 @@ shape consumer code depends on.</p>
|
|
|
697
721
|
</details>
|
|
698
722
|
<details class="compat-row" data-status="ok">
|
|
699
723
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Tri-state error semantics: DOTTED field access of a missing key (<code>resource.data.typo</code>), access on null/undefined, undefined variables, and <code>get()</code>-of-missing ERROR → deny; <code>&&</code>/<code>||</code> absorb operand errors <strong>commutatively</strong> (CEL: <code>error || true</code> → true, <code>error && false</code> → false). NOTE: DYNAMIC index access <code>data[expr]</code> stays null-on-miss (the documented may-be-absent-lookup idiom; only dotted access is doc-confirmed to error). (RULES-B2/B3/B8)</span></span></summary>
|
|
700
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/evaluator.test.ts</code> (RULES-B2 / RULES-B3 / RULES-B8 blocks)</div></div>
|
|
724
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/evaluator.test.ts</code></a> (RULES-B2 / RULES-B3 / RULES-B8 blocks)</div></div>
|
|
701
725
|
</details>
|
|
702
726
|
<details class="compat-row" data-status="ok">
|
|
703
727
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>matches()</code> is a <strong>full-string</strong> anchored RE2 test; <code>replace()</code>/<code>split()</code> take regexes (<code>replace</code> = all occurrences) (RULES-B4)</span></span></summary>
|
|
704
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/evaluator.test.ts</code> (RULES-B4 block)</div></div>
|
|
728
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/evaluator.test.ts</code></a> (RULES-B4 block)</div></div>
|
|
705
729
|
</details>
|
|
706
730
|
<details class="compat-row" data-status="ok">
|
|
707
731
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">No JS prototype-chain leakage: <code>'toString' in data</code> → false, <code>data.constructor</code> errors; <code>in</code>/<code>hasAll</code>/<code>get</code> use own keys only (RULES-B7)</span></span></summary>
|
|
708
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/evaluator.test.ts</code> (RULES-B7 block)</div></div>
|
|
732
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/evaluator.test.ts</code></a> (RULES-B7 block)</div></div>
|
|
709
733
|
</details>
|
|
710
734
|
<details class="compat-row" data-status="ok">
|
|
711
735
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Type-strict operators: <code>+</code> requires matching operand types (<code>'a' + 1</code> errors; <code>[1]+[2]</code> concatenates); ordered compares (<code>< > <= >=</code>) error across types; list membership uses value equality; <code>is map</code> excludes MapDiff/Set (RULES-B6 partial / B9 / B12 partial)</span></span></summary>
|
|
712
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/evaluator.test.ts</code> (RULES-B6 / B9 / B12 blocks)</div></div>
|
|
736
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/evaluator.test.ts</code></a> (RULES-B6 / B9 / B12 blocks)</div></div>
|
|
713
737
|
</details>
|
|
714
738
|
<details class="compat-row" data-status="ok">
|
|
715
739
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>FirestoreSet</code> VALUE equality: <code>diff.addedKeys() == [uid].toSet()</code> compares set contents (order-insensitive); <code>set == list</code> is false, not an error (RULES-B13). Pre-fix, ANY two sets compared EQUAL (generic-object deep-equals saw no enumerable keys) — a false-PERMISSIVE divergence found by joining validation</span></span></summary>
|
|
716
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/set-equality.test.ts</code>; live validation: 10/10 both engines</div></div>
|
|
740
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/set-equality.test.ts#L6" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/set-equality.test.ts</code></a>; live validation: 10/10 both engines</div></div>
|
|
717
741
|
</details>
|
|
718
742
|
<details class="compat-row" data-status="ok">
|
|
719
743
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>update</code> exposes <code>request.resource.data</code> / <code>getAfter()</code> as the existing doc <strong>merged</strong> with the payload via the <code>writeMode: { kind: 'update' }</code> path (the agent-facing <code>simulate()</code> opt-in); a sparse no-writeMode payload that drops a field now ERRORS on that field (RULES-B2) rather than silently reading null (RULES-B10)</span></span></summary>
|
|
720
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/handler.test.ts</code> (RULES-B10 block)</div></div>
|
|
744
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/handler.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/handler.test.ts</code></a> (RULES-B10 block)</div></div>
|
|
721
745
|
</details>
|
|
722
746
|
<details class="compat-row" data-status="ok">
|
|
723
747
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Int/float distinction (<code>1.5 is int</code>→false, <code>1 is float</code>→false, <code>1.0 is float</code>→true) + integer division (<code>10 / 4 == 2</code>) + int div/mod-by-zero ERRORS (RULES-B5); strict <code>int('12abc')</code>/<code>float('abc')</code>/<code>bool('false')</code>/<code>bool('yes')</code> parsing (RULES-B6 rest); <code>string(1.0)</code>→"1.0" (RULES-B12 rest)</span></span></summary>
|
|
724
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/evaluator.test.ts</code> (RULES-B5 + "RULES-B6 remainder" blocks); <code>unit:rules/simulator/handler.test.ts</code> ("RULES-B5 end-to-end" block)</div></div>
|
|
748
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/evaluator.test.ts</code></a> (RULES-B5 + "RULES-B6 remainder" blocks); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/handler.test.ts#L1089" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/handler.test.ts</code></a> ("RULES-B5 end-to-end" block)</div></div>
|
|
725
749
|
</details>
|
|
726
750
|
<details class="compat-row" data-status="ok">
|
|
727
751
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Strict boolean control flow matches production: non-booleans in <code>&&</code>, <code>||</code>, or a ternary condition error and deny. Firestore double payloads retain float identity in the simulator (fractional JSON numbers are revived directly; <code>{ __type:'float', value }</code> preserves an explicitly tagged double). On create, <code>resource == null</code> denies while <code>request.resource</code> exposes the incoming document.</span></span></summary>
|
|
728
|
-
<div class="compat-evidence"><div class="compat-probe">Production behavior is recorded by Firestore Rules rows <code>firestore-rules#188</code>, <code>firestore-rules#167</code>, and <code>firestore-rules#166</code>. Direct evaluator/handler tests guard strict operands, fractional-number revival, the explicit float tag, and create-time resource verdicts on the SDK-facing simulator path
|
|
752
|
+
<div class="compat-evidence"><div class="compat-probe">Production behavior is recorded by Firestore Rules rows <code>firestore-rules#188</code>, <code>firestore-rules#167</code>, and <code>firestore-rules#166</code>. Direct evaluator/handler tests guard strict operands, fractional-number revival, the explicit float tag, and create-time resource verdicts on the SDK-facing simulator path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/evaluator.test.ts#L140" target="_blank" rel="noopener noreferrer"><code>evaluator.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/handler.test.ts#L1089" target="_blank" rel="noopener noreferrer"><code>handler.test.ts</code></a>)</div></div>
|
|
729
753
|
</details>
|
|
730
754
|
<details class="compat-row" data-status="ok">
|
|
731
755
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Query-proof EVALUATION — the rules-side decision ("rules are not filters"): given a <code>list</code> rule + query constraints, decide provable-or-reject (a doc-dependent rule like <code>resource.data.visibility == 'public'</code> is provable ONLY with a matching <code>where('visibility','==','public')</code>; otherwise the whole query is rejected) (RULES-B11 rules-side)</span></span></summary>
|
|
732
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:rules/simulator/query-proof.test.ts</code></div></div>
|
|
756
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/query-proof.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/query-proof.test.ts</code></a></div></div>
|
|
733
757
|
</details>
|
|
734
758
|
<details class="compat-row" data-status="ok">
|
|
735
759
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Query-proof ENFORCEMENT wiring — <code>silentReadCollection</code> + <code>readQueryCandidates</code> call <code>evaluateQueryProof</code> (via <code>sandbox/firestore/list-query-proof.ts</code>) instead of the per-doc silent-omission filter; structured <code>where</code>/<code>limit</code>/<code>orderBy</code> constraints are threaded from <code>QueryImpl.structuredConstraints()</code> through both the one-shot (<code>getDocs</code>/aggregate) and listener (<code>SnapshotTarget</code> applier <code>.structured</code>) paths, and <code>request.query.{limit,offset,orderBy}</code> is populated on list test cases (RULES-B11 cross-file)</span></span></summary>
|
|
736
|
-
<div class="compat-evidence"><div class="compat-probe"><code>unit:firestore/query-proof-enforcement.test.ts</code> (both paths; verified failing pre-fix); prover scope caveat: row 24c</div></div>
|
|
760
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/query-proof-enforcement.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>unit:firestore/query-proof-enforcement.test.ts</code></a> (both paths; verified failing pre-fix); prover scope caveat: row 24c</div></div>
|
|
737
761
|
</details>
|
|
738
762
|
</div>
|
|
739
763
|
<h2 id="current-gaps">Current gaps</h2>
|
|
@@ -741,48 +765,15 @@ shape consumer code depends on.</p>
|
|
|
741
765
|
<p>Known differences between Pyric and production Firebase. Each remains tracked as a non-conforming row.</p>
|
|
742
766
|
<div class="compat-list">
|
|
743
767
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getDocs(query)</code><span class="compat-sub"><span class="compat-behavior">Query-proof **prover scope is conservative, with full accounting and a fail-closed classifier** — user functions are inlined (nested helper-calling-helper and multi-parameter helpers included, matching production's rules-function inlining), and a <code>list</code> rule is provable only when its **entire** doc-dependence reduces to top-level AND-conjunct <code>resource.data.<field> == <literal></code> equalities (with <code>request.auth.uid</code> pinned to the caller) each discharged by a <code>where(field, '==', value)</code> filter. Doc-dependence is decided by a fail-closed classifier: an expression counts as doc-independent only when every node is positively recognized as such — any touch of the <code>resource</code> root in any syntactic form (<code>resource.data</code>, <code>resource['data']</code>, slices, <code>resource.id</code>, path-literal segments feeding <code>exists</code>/<code>get</code> lookups) and any unrecognized node shape classifies doc-dependent. Any doc-dependent conjunct that is not a discharged equality — disjunctions over doc data, inequality/range predicates (<code>resource.data.score > 10</code> + <code>where('score','>',10)</code>), <code>in</code> / negated-<code>in</code> membership checks, <code>get(key, default)</code>, <code>keys().hasOnly(...)</code>, <code>is</code> type checks, nested-path predicates, data-keyed document lookups — makes the whole query conservatively DENY, even when every equality conjunct is discharged; production's prover may allow some of these. Divergence is deny-only: mixed equality + non-equality shapes are rejected up front rather than trusted to a residual evaluation that absent-tolerant predicates could pass vacuously.</span></span></span></summary>
|
|
744
|
-
<div class="compat-evidence"><div class="compat-note"><code>unit:rules/simulator/query-proof.test.ts</code> (function-inlining provable cases; full-accounting rejections for mixed equality + <code>in</code>/<code>get</code>/<code>hasOnly</code>/range/<code>is</code> shapes in inline and helper form; fail-closed classifier probes: bracket-access <code>resource['data']</code>, <code>exists()</code> keyed by <code>resource.data</code> through path-literal segments, slice access, <code>resource.id</code>, and an unrecognized node shape), <code>unit:firestore/query-proof-enforcement.test.ts</code> (seeded end-to-end denials of the demonstrated attacks: forbidden-field absence check in member and bracket form, banned-owner <code>exists()</code> lookup, slice conjunct — each returning zero documents)</div></div></details>
|
|
768
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/query-proof.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/query-proof.test.ts</code></a> (function-inlining provable cases; full-accounting rejections for mixed equality + <code>in</code>/<code>get</code>/<code>hasOnly</code>/range/<code>is</code> shapes in inline and helper form; fail-closed classifier probes: bracket-access <code>resource['data']</code>, <code>exists()</code> keyed by <code>resource.data</code> through path-literal segments, slice access, <code>resource.id</code>, and an unrecognized node shape), <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/query-proof-enforcement.test.ts#L19" target="_blank" rel="noopener noreferrer"><code>unit:firestore/query-proof-enforcement.test.ts</code></a> (seeded end-to-end denials of the demonstrated attacks: forbidden-field absence check in member and bracket form, banned-owner <code>exists()</code> lookup, slice conjunct — each returning zero documents)</div></div></details>
|
|
745
769
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Query construction</code><span class="compat-sub"><span class="compat-behavior">Index validation against <code>firestore.indexes.json</code> — sandbox uses <code>LocalEnvironment</code>'s lint pass; prod has its own server-side validation</span></span></span></summary>
|
|
746
770
|
<div class="compat-evidence"><div class="compat-note">divergence: sandbox can mis-pass a query that prod would reject at the server with <code>failed-precondition</code> if no index exists</div></div></details>
|
|
747
771
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Aggregates</code><span class="compat-sub"><span class="compat-behavior">Aggregates count documents server-side without paying read cost per doc in prod; sandbox computes locally (no cost model)</span></span></span></summary>
|
|
748
|
-
<div class="compat-evidence"><div class="compat-note">divergence: cost behavior differs, observable shape identical. Oracle-locked: <code>packages/conformance/observations/firestore/firestore-count-aggregate-shape.json</code> — <code>getCountFromServer().data()</code> returns <code>{ count: <number> }</code> (single key, no other fields). Empty query returns <code>count: 0</code> (not <code>null</code>/<code>undefined</code>); seeded 3 docs returns <code>count: 3</code>; filtered query honors the <code>where</code> constraint (<code>count: 2</code>).</div></div></details>
|
|
772
|
+
<div class="compat-evidence"><div class="compat-note">divergence: cost behavior differs, observable shape identical. Oracle-locked: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore/firestore-count-aggregate-shape.json" target="_blank" rel="noopener noreferrer"><code>packages/conformance/observations/firestore/firestore-count-aggregate-shape.json</code></a> — <code>getCountFromServer().data()</code> returns <code>{ count: <number> }</code> (single key, no other fields). Empty query returns <code>count: 0</code> (not <code>null</code>/<code>undefined</code>); seeded 3 docs returns <code>count: 3</code>; filtered query honors the <code>where</code> constraint (<code>count: 2</code>).</div></div></details>
|
|
749
773
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">initializeFirestore</code><span class="compat-sub"><span class="compat-behavior">Delegates to sandbox <code>getFirestore(app)</code> and returns the same handle. Accepts the <code>settings</code> argument (so the explicit-init pattern doesn't crash at import) but no-ops the cache/network settings — persistence is always on</span></span></span></summary>
|
|
750
|
-
<div class="compat-evidence"><div class="compat-note"><code>unit:firestore/tier1-cache-init-align.test.ts</code></div></div></details>
|
|
774
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:firestore/tier1-cache-init-align.test.ts</code></a></div></div></details>
|
|
751
775
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">persistentLocalCache / memoryLocalCache / persistentSingleTabManager / persistentMultipleTabManager / memoryEagerGarbageCollector / memoryLruGarbageCollector</code><span class="compat-sub"><span class="compat-behavior">Config token accepted, inert — each returns a small tagged object so identity/usage doesn't crash. Persistence is the sandbox default; there is no cache tier left to configure</span></span></span></summary>
|
|
752
|
-
<div class="compat-evidence"><div class="compat-note"><code>unit:firestore/tier1-cache-init-align.test.ts</code></div></div></details>
|
|
776
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:firestore/tier1-cache-init-align.test.ts</code></a></div></div></details>
|
|
753
777
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">setLogLevel</code><span class="compat-sub"><span class="compat-behavior">Accepted no-op — the sandbox has no modular-SDK-style logger to wire a level into; it uses host-level <code>console</code> logging directly, gated by <code>pyric dev</code>'s own flags, not this call</span></span></span></summary>
|
|
754
|
-
<div class="compat-evidence"><div class="compat-note"><code>unit:firestore/tier1-cache-init-align.test.ts</code></div></div></details>
|
|
755
|
-
</div>
|
|
756
|
-
<h2 id="reviewed-public-runtime-gaps">Reviewed public-runtime gaps</h2>
|
|
757
|
-
<div class="compat-list">
|
|
758
|
-
<details class="compat-row" data-status="unsupported">
|
|
759
|
-
<summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">firestore.runtime-class-values</code><span class="compat-sub"><span class="compat-behavior">These Firebase classes and constraint objects are present in Pyric's type surface but are not exported as runtime constructor values. Runtime identity and instanceof-compatible tokens remain unbuilt and cannot be credited by type-only mirrors.</span></span></span></summary>
|
|
760
|
-
<div class="compat-evidence"><div class="compat-note"><code>AbstractUserDataWriter</code> <code>AggregateField</code> <code>AggregateQuerySnapshot</code> <code>CollectionReference</code> <code>DocumentReference</code> <code>DocumentSnapshot</code> <code>Firestore</code> <code>FirestoreError</code> <code>Query</code> <code>QueryCompositeFilterConstraint</code> <code>QueryConstraint</code> <code>QueryDocumentSnapshot</code> <code>QueryEndAtConstraint</code> <code>QueryFieldFilterConstraint</code> <code>QueryLimitConstraint</code> <code>QueryOrderByConstraint</code> <code>QuerySnapshot</code> <code>QueryStartAtConstraint</code> <code>SnapshotMetadata</code> <code>Transaction</code> <code>WriteBatch</code></div>
|
|
761
|
-
<div class="compat-note">upstream:firebase/firestore</div></div>
|
|
762
|
-
</details>
|
|
763
|
-
<details class="compat-row" data-status="unsupported">
|
|
764
|
-
<summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">firestore.cache-indexes</code><span class="compat-sub"><span class="compat-behavior">Index-tuning / GC-policy APIs have no real knob to turn in an in-memory sandbox today, but sibling cache-factory tokens are already honest inert tokens; these are buildable the same way and not genuinely out of scope.</span></span></span></summary>
|
|
765
|
-
<div class="compat-evidence"><div class="compat-note"><code>CACHE_SIZE_UNLIMITED</code> <code>PersistentCacheIndexManager</code> <code>deleteAllPersistentCacheIndexes</code> <code>disablePersistentCacheIndexAutoCreation</code> <code>enablePersistentCacheIndexAutoCreation</code> <code>getPersistentCacheIndexManager</code> <code>setIndexConfiguration</code></div>
|
|
766
|
-
<div class="compat-note">upstream:firebase/firestore</div></div>
|
|
767
|
-
</details>
|
|
768
|
-
<details class="compat-row" data-status="unsupported">
|
|
769
|
-
<summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">firestore.bundle-loading</code><span class="compat-sub"><span class="compat-behavior">Bundle-loading depends on a protobuf packaging format not modeled in the sandbox yet — a data/parsing problem, not external infrastructure, so it is buildable rather than genuinely un-modelable.</span></span></span></summary>
|
|
770
|
-
<div class="compat-evidence"><div class="compat-note"><code>LoadBundleTask</code> <code>loadBundle</code> <code>namedQuery</code></div>
|
|
771
|
-
<div class="compat-note">upstream:firebase/firestore</div></div>
|
|
772
|
-
</details>
|
|
773
|
-
<details class="compat-row" data-status="unsupported">
|
|
774
|
-
<summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">firestore.aggregate-equality-helpers</code><span class="compat-sub"><span class="compat-behavior">Aggregate-field and aggregate-snapshot equality helpers are exported upstream but have no Pyric runtime implementation yet.</span></span></span></summary>
|
|
775
|
-
<div class="compat-evidence"><div class="compat-note"><code>aggregateFieldEqual</code> <code>aggregateQuerySnapshotEqual</code></div>
|
|
776
|
-
<div class="compat-note">upstream:firebase/firestore</div></div>
|
|
777
|
-
</details>
|
|
778
|
-
<details class="compat-row" data-status="unsupported">
|
|
779
|
-
<summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">firestore.snapshot-json-resume</code><span class="compat-sub"><span class="compat-behavior">Snapshot JSON reconstruction and listener-resume helpers depend on serialized snapshot state that the sandbox has not modeled.</span></span></span></summary>
|
|
780
|
-
<div class="compat-evidence"><div class="compat-note"><code>documentSnapshotFromJSON</code> <code>onSnapshotResume</code> <code>querySnapshotFromJSON</code></div>
|
|
781
|
-
<div class="compat-note">upstream:firebase/firestore</div></div>
|
|
782
|
-
</details>
|
|
783
|
-
<details class="compat-row" data-status="unsupported">
|
|
784
|
-
<summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">firestore.public-write-plumbing</code><span class="compat-sub"><span class="compat-behavior">The upstream package publicly exports these low-level configuration/write helpers, but Pyric does not expose equivalent runtime plumbing yet.</span></span></span></summary>
|
|
785
|
-
<div class="compat-evidence"><div class="compat-note"><code>ensureFirestoreConfigured</code> <code>executeWrite</code></div>
|
|
786
|
-
<div class="compat-note">upstream:firebase/firestore</div></div>
|
|
787
|
-
</details>
|
|
788
|
-
</div> </article> </main> <aside class="doc-toc" aria-label="On this page" data-astro-cid-mw7aashj> <h2 class="doc-toc-heading" data-astro-cid-mw7aashj>On this page</h2> <ul data-astro-cid-mw7aashj> <li data-depth="2" data-astro-cid-mw7aashj> <a href="#status-legend" data-astro-cid-mw7aashj>Status legend</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getfirestoretarget--initializer" data-astro-cid-mw7aashj>getFirestore(target) — initializer</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#path-constructors--doc--collection--collectiongroup" data-astro-cid-mw7aashj>Path constructors — doc / collection / collectionGroup</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getdocref--single-doc-read" data-astro-cid-mw7aashj>getDoc(ref) — single-doc read</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getdocsquery--bulk-read" data-astro-cid-mw7aashj>getDocs(query) — bulk read</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#setdocref-data-options--full-write" data-astro-cid-mw7aashj>setDoc(ref, data[, options]) — full write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#updatedocref-data--partial-write" data-astro-cid-mw7aashj>updateDoc(ref, data) — partial write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#deletedocref" data-astro-cid-mw7aashj>deleteDoc(ref)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#adddoccoll-data--auto-id-write" data-astro-cid-mw7aashj>addDoc(coll, data) — auto-id write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#withconverter--typed-refs" data-astro-cid-mw7aashj>withConverter — typed refs</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#query-construction--query--where--or--and--orderby--limit" data-astro-cid-mw7aashj>Query construction — query / where / or / and / orderBy / limit</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#cursor-pagination--startat--startafter--endat--endbefore" data-astro-cid-mw7aashj>Cursor pagination — startAt / startAfter / endAt / endBefore</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#aggregates--getcountfromserver--getaggregatefromserver--count--sum--average" data-astro-cid-mw7aashj>Aggregates — getCountFromServer / getAggregateFromServer / count / sum / average</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#onsnapshotreforquery---listeners" data-astro-cid-mw7aashj>onSnapshot(refOrQuery, …) — listeners</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#runtransactiondb-fn" data-astro-cid-mw7aashj>runTransaction(db, fn)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#writebatchdb" data-astro-cid-mw7aashj>writeBatch(db)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#sentinels--servertimestamp--increment--arrayunion--arrayremove--deletefield--fieldvalue--timestamp" data-astro-cid-mw7aashj>Sentinels — serverTimestamp / increment / arrayUnion / arrayRemove / deleteField / FieldValue / Timestamp</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#scalar-types--bytes--geopoint--fieldpath--documentid" data-astro-cid-mw7aashj>Scalar types — Bytes / GeoPoint / FieldPath / documentId</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#equality-helpers--refequal--queryequal--snapshotequal" data-astro-cid-mw7aashj>Equality helpers — refEqual / queryEqual / snapshotEqual</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#connectfirestoreemulator" data-astro-cid-mw7aashj>connectFirestoreEmulator</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#offline--persistence--network-family" data-astro-cid-mw7aashj>Offline / persistence / network family</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#offline--persistence--network-family-continued" data-astro-cid-mw7aashj>Offline / persistence / network family (continued)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#tier-1-cache-init--get-from--family" data-astro-cid-mw7aashj>Tier-1 cache-init + get-from-* family</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#tier-1-cache-init--get-from--family-continued" data-astro-cid-mw7aashj>Tier-1 cache-init + get-from-* family (continued)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#rules-engine-via-setrules-from-pyricsandboxfirestore" data-astro-cid-mw7aashj>Rules engine (via setRules from pyric/sandbox/firestore)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#current-gaps" data-astro-cid-mw7aashj>Current gaps</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#documented-divergences" data-astro-cid-mw7aashj>Documented divergences</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#reviewed-public-runtime-gaps" data-astro-cid-mw7aashj>Reviewed public-runtime gaps</a> </li> </ul> </aside> </div> <script type="module">const a=document.querySelector("[data-api-symbol-filter]"),d=[...document.querySelectorAll("[data-api-symbol-group]")],n=document.querySelector("[data-api-symbol-empty]");a?.addEventListener("input",()=>{const t=a.value.trim().toLowerCase();let o=0;for(const i of d){let e=0;i.querySelectorAll("[data-api-symbol]").forEach(l=>{const r=!t||(l.dataset.apiSymbol??"").includes(t);l.hidden=!r,r&&e++}),i.hidden=e===0,o+=e}n&&(n.hidden=o>0)});</script> <script type="module">const s=new Map;document.querySelectorAll('.doc-toc a[href^="#"]').forEach(t=>s.set(decodeURIComponent(t.hash.slice(1)),t));if(s.size>0){const t=new Set,n=()=>{let o=null;for(const e of s.keys())if(t.has(e)){o=e;break}s.forEach((e,c)=>e.classList.toggle("active",c===o))},r=new IntersectionObserver(o=>{for(const e of o)e.isIntersecting?t.add(e.target.id):t.delete(e.target.id);n()},{rootMargin:"-72px 0px -60% 0px"});s.forEach((o,e)=>{const c=document.getElementById(e);c&&r.observe(c)})}</script> <script type="module">document.querySelector('.studio__nav [aria-current="page"]')?.scrollIntoView({block:"nearest",inline:"center"});</script> </body></html>
|
|
778
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/tier1-cache-init-align.test.ts#L13" target="_blank" rel="noopener noreferrer"><code>unit:firestore/tier1-cache-init-align.test.ts</code></a></div></div></details>
|
|
779
|
+
</div> </article> </main> <aside class="doc-toc" aria-label="On this page" data-astro-cid-mw7aashj> <h2 class="doc-toc-heading" data-astro-cid-mw7aashj>On this page</h2> <ul data-astro-cid-mw7aashj> <li data-depth="2" data-astro-cid-mw7aashj> <a href="#status-legend" data-astro-cid-mw7aashj>Status legend</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getfirestoretarget--initializer" data-astro-cid-mw7aashj>getFirestore(target) — initializer</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#path-constructors--doc--collection--collectiongroup" data-astro-cid-mw7aashj>Path constructors — doc / collection / collectionGroup</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getdocref--single-doc-read" data-astro-cid-mw7aashj>getDoc(ref) — single-doc read</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getdocsquery--bulk-read" data-astro-cid-mw7aashj>getDocs(query) — bulk read</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#setdocref-data-options--full-write" data-astro-cid-mw7aashj>setDoc(ref, data[, options]) — full write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#updatedocref-data--partial-write" data-astro-cid-mw7aashj>updateDoc(ref, data) — partial write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#deletedocref" data-astro-cid-mw7aashj>deleteDoc(ref)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#adddoccoll-data--auto-id-write" data-astro-cid-mw7aashj>addDoc(coll, data) — auto-id write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#withconverter--typed-refs" data-astro-cid-mw7aashj>withConverter — typed refs</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#query-construction--query--where--or--and--orderby--limit" data-astro-cid-mw7aashj>Query construction — query / where / or / and / orderBy / limit</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#cursor-pagination--startat--startafter--endat--endbefore" data-astro-cid-mw7aashj>Cursor pagination — startAt / startAfter / endAt / endBefore</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#aggregates--getcountfromserver--getaggregatefromserver--count--sum--average" data-astro-cid-mw7aashj>Aggregates — getCountFromServer / getAggregateFromServer / count / sum / average</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#onsnapshotreforquery---listeners" data-astro-cid-mw7aashj>onSnapshot(refOrQuery, …) — listeners</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#runtransactiondb-fn" data-astro-cid-mw7aashj>runTransaction(db, fn)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#writebatchdb" data-astro-cid-mw7aashj>writeBatch(db)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#sentinels--servertimestamp--increment--arrayunion--arrayremove--deletefield--fieldvalue--timestamp" data-astro-cid-mw7aashj>Sentinels — serverTimestamp / increment / arrayUnion / arrayRemove / deleteField / FieldValue / Timestamp</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#scalar-types--bytes--geopoint--fieldpath--documentid" data-astro-cid-mw7aashj>Scalar types — Bytes / GeoPoint / FieldPath / documentId</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#equality-helpers--refequal--queryequal--snapshotequal" data-astro-cid-mw7aashj>Equality helpers — refEqual / queryEqual / snapshotEqual</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#connectfirestoreemulator" data-astro-cid-mw7aashj>connectFirestoreEmulator</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#offline--persistence--network-family" data-astro-cid-mw7aashj>Offline / persistence / network family</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#offline--persistence--network-family-continued" data-astro-cid-mw7aashj>Offline / persistence / network family (continued)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#tier-1-cache-init--get-from--family" data-astro-cid-mw7aashj>Tier-1 cache-init + get-from-* family</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#tier-1-cache-init--get-from--family-continued" data-astro-cid-mw7aashj>Tier-1 cache-init + get-from-* family (continued)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#rules-engine-via-setrules-from-pyricsandboxfirestore" data-astro-cid-mw7aashj>Rules engine (via setRules from pyric/sandbox/firestore)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#current-gaps" data-astro-cid-mw7aashj>Current gaps</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#documented-divergences" data-astro-cid-mw7aashj>Documented divergences</a> </li> </ul> </aside> </div> <script type="module">const a=document.querySelector("[data-api-symbol-filter]"),d=[...document.querySelectorAll("[data-api-symbol-group]")],n=document.querySelector("[data-api-symbol-empty]");a?.addEventListener("input",()=>{const t=a.value.trim().toLowerCase();let o=0;for(const i of d){let e=0;i.querySelectorAll("[data-api-symbol]").forEach(l=>{const r=!t||(l.dataset.apiSymbol??"").includes(t);l.hidden=!r,r&&e++}),i.hidden=e===0,o+=e}n&&(n.hidden=o>0)});</script> <script type="module">const s=new Map;document.querySelectorAll('.doc-toc a[href^="#"]').forEach(t=>s.set(decodeURIComponent(t.hash.slice(1)),t));if(s.size>0){const t=new Set,n=()=>{let o=null;for(const e of s.keys())if(t.has(e)){o=e;break}s.forEach((e,c)=>e.classList.toggle("active",c===o))},r=new IntersectionObserver(o=>{for(const e of o)e.isIntersecting?t.add(e.target.id):t.delete(e.target.id);n()},{rootMargin:"-72px 0px -60% 0px"});s.forEach((o,e)=>{const c=document.getElementById(e);c&&r.observe(c)})}</script> <script type="module">document.querySelector('.studio__nav [aria-current="page"]')?.scrollIntoView({block:"nearest",inline:"center"});</script> </body></html>
|