@pyric/cli 0.1.0-alpha.18 → 0.1.0-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +9 -4
  2. package/README.md.orig +5 -1
  3. package/dist/bridge/server/mcp-contract.d.ts +2 -2
  4. package/dist/bridge/server/mcp-contract.d.ts.map +1 -1
  5. package/dist/bridge/server/mcp-contract.js +3 -0
  6. package/dist/bridge/server/mcp-contract.js.map +1 -1
  7. package/dist/cli/index.d.ts +1 -0
  8. package/dist/cli/index.d.ts.map +1 -1
  9. package/dist/cli/index.js +3 -0
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/cli/mcp-proxy.d.ts.map +1 -1
  12. package/dist/cli/mcp-proxy.js.map +1 -1
  13. package/dist/cli/rules.d.ts +1 -0
  14. package/dist/cli/rules.d.ts.map +1 -1
  15. package/dist/cli/rules.js +18 -4
  16. package/dist/cli/rules.js.map +1 -1
  17. package/dist/cli/service-command-records/storage-rules-resolve.d.ts +4 -0
  18. package/dist/cli/service-command-records/storage-rules-resolve.d.ts.map +1 -0
  19. package/dist/cli/service-command-records/storage-rules-resolve.js +3 -0
  20. package/dist/cli/service-command-records/storage-rules-resolve.js.map +1 -0
  21. package/dist/cli/service-commands.generated.d.ts +3 -0
  22. package/dist/cli/service-commands.generated.d.ts.map +1 -1
  23. package/dist/cli/service-commands.generated.js +2 -0
  24. package/dist/cli/service-commands.generated.js.map +1 -1
  25. package/dist/serve/rules.d.ts +3 -3
  26. package/dist/serve/rules.d.ts.map +1 -1
  27. package/dist/serve/rules.js +13 -5
  28. package/dist/serve/rules.js.map +1 -1
  29. package/dist/serve/site-ui/_astro/{RulesCodeEditor.CzwHdCi9.js → RulesCodeEditor.Bl-XMkhD.js} +1 -1
  30. package/dist/serve/site-ui/_astro/_example_.B9DG9HLJ.css +1 -0
  31. package/dist/serve/site-ui/_astro/{_slug_.C6QfVOxJ.css → _slug_.CupVK4TM.css} +1 -1
  32. package/dist/serve/site-ui/_astro/_studio_.CfxiQWkk.css +1 -0
  33. package/dist/serve/site-ui/_astro/app.Ce3MTLQm.js +1 -0
  34. package/dist/serve/site-ui/_astro/app.taqzyjD0.js +11 -0
  35. package/dist/serve/site-ui/_astro/chess-showcase.Dh5D8kqe.js +354 -0
  36. package/dist/serve/site-ui/_astro/{example-runtime.B0byNQOl.js → example-runtime.CuyAjjiG.js} +2 -26
  37. package/dist/serve/site-ui/_astro/sandbox-controls.BP4M5f1M.js +1169 -0
  38. package/dist/serve/site-ui/_astro/seed.CSUkloMi.js +20 -0
  39. package/dist/serve/site-ui/_astro/writes.C57paAj2.js +99 -0
  40. package/dist/serve/site-ui/auth/index.html +2 -2
  41. package/dist/serve/site-ui/docs/_rhythm/index.html +1 -1
  42. package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +29 -4
  43. package/dist/serve/site-ui/docs/agent/set-up-your-agent.md +8 -5
  44. package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +1 -1
  45. package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +1 -1
  46. package/dist/serve/site-ui/docs/ai-compat/index.html +1 -1
  47. package/dist/serve/site-ui/docs/api-reference/index.html +1 -1
  48. package/dist/serve/site-ui/docs/app-compat/index.html +1 -1
  49. package/dist/serve/site-ui/docs/auth-compat/index.html +1 -1
  50. package/dist/serve/site-ui/docs/build/ai-logic/index.html +1 -1
  51. package/dist/serve/site-ui/docs/build/authentication/index.html +1 -1
  52. package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +30 -52
  53. package/dist/serve/site-ui/docs/build/cloud-firestore.md +40 -36
  54. package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +1 -1
  55. package/dist/serve/site-ui/docs/build/cloud-storage/index.html +1 -1
  56. package/dist/serve/site-ui/docs/build/realtime-database/index.html +1 -1
  57. package/dist/serve/site-ui/docs/conformance-scores/index.html +1 -1
  58. package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +1 -1
  59. package/dist/serve/site-ui/docs/database-compat/index.html +1 -1
  60. package/dist/serve/site-ui/docs/examples/chess/index.html +645 -249
  61. package/dist/serve/site-ui/docs/examples/chess.md +45 -16
  62. package/dist/serve/site-ui/docs/examples/index.html +2 -2
  63. package/dist/serve/site-ui/docs/firestore-compat/index.html +1 -1
  64. package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +1 -1
  65. package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
  66. package/dist/serve/site-ui/docs/get-started/nextjs/index.html +1 -1
  67. package/dist/serve/site-ui/docs/get-started/start-building/index.html +29 -3
  68. package/dist/serve/site-ui/docs/get-started/start-building.md +9 -4
  69. package/dist/serve/site-ui/docs/get-started/vite/index.html +1 -1
  70. package/dist/serve/site-ui/docs/index.json +23 -18
  71. package/dist/serve/site-ui/docs/messaging-compat/index.html +1 -1
  72. package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +1 -1
  73. package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +1 -1
  74. package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +1 -1
  75. package/dist/serve/site-ui/docs/overview/index.html +1 -1
  76. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
  77. package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
  78. package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
  79. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
  80. package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
  81. package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
  82. package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +1 -1
  83. package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
  84. package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +1 -1
  85. package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +1 -1
  86. package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
  87. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
  88. package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
  89. package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
  90. package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
  91. package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
  92. package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
  93. package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
  94. package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
  95. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +1 -1
  96. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +1 -1
  97. package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
  98. package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
  99. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +1 -1
  100. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
  101. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +1 -1
  102. package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +1 -1
  103. package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +1 -1
  104. package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +1 -1
  105. package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
  106. package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +1 -1
  107. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
  108. package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
  109. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +1 -1
  110. package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +1 -1
  111. package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
  112. package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
  113. package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
  114. package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
  115. package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
  116. package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
  117. package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
  118. package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
  119. package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
  120. package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
  121. package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
  122. package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
  123. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
  124. package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
  125. package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
  126. package/dist/serve/site-ui/docs/rules-compat/index.html +1 -1
  127. package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +1 -1
  128. package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +1 -1
  129. package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +1 -1
  130. package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
  131. package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +1 -1
  132. package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +1 -1
  133. package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +1 -1
  134. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  135. package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +1 -1
  136. package/dist/serve/site-ui/docs/ship/test-in-node/index.html +1 -1
  137. package/dist/serve/site-ui/docs/storage-compat/index.html +1 -1
  138. package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
  139. package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +1 -1
  140. package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +1 -1
  141. package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
  142. package/dist/serve/site-ui/docs/tutorial/index.html +1 -1
  143. package/dist/serve/site-ui/examples/chess/index.html +2 -2
  144. package/dist/serve/site-ui/firestore/index.html +2 -2
  145. package/dist/serve/site-ui/index.html +2 -2
  146. package/dist/serve/site-ui/rtdb/index.html +2 -2
  147. package/dist/serve/site-ui/settings/index.html +2 -2
  148. package/dist/serve/site-ui/storage/index.html +2 -2
  149. package/dist/serve/site-ui/traffic/index.html +2 -2
  150. package/dist/serve/vite-rules-source.d.ts.map +1 -1
  151. package/dist/serve/vite-rules-source.js +28 -7
  152. package/dist/serve/vite-rules-source.js.map +1 -1
  153. package/package.json +4 -4
  154. package/src/bridge/server/mcp-contract.ts +3 -0
  155. package/src/cli/index.ts +3 -0
  156. package/src/cli/mcp-proxy.ts +6 -7
  157. package/src/cli/rules.ts +31 -4
  158. package/src/cli/service-command-records/storage-rules-resolve.ts +4 -0
  159. package/src/cli/service-commands.generated.ts +2 -0
  160. package/src/serve/rules.ts +15 -5
  161. package/src/serve/vite-rules-source.ts +29 -7
  162. package/dist/serve/site-ui/_astro/_example_.jevZ7OdV.css +0 -1
  163. package/dist/serve/site-ui/_astro/_studio_.B6b_G1ev.css +0 -1
  164. package/dist/serve/site-ui/_astro/app.B7pBzz5N.js +0 -11
  165. package/dist/serve/site-ui/_astro/app.boex5SnQ.js +0 -1
  166. package/dist/serve/site-ui/_astro/chess-showcase.DDqE-QGe.js +0 -1
  167. package/dist/serve/site-ui/_astro/chess-source-explorer.C5k9c5yK.js +0 -1
  168. package/dist/serve/site-ui/_astro/chess-v2.CFHmd3UL.js +0 -354
  169. package/dist/serve/site-ui/_astro/index.uwPvjH_1.js +0 -1
  170. package/dist/serve/site-ui/_astro/jsx-runtime.D_zvdyIk.js +0 -9
  171. package/dist/serve/site-ui/_astro/reads.Dh3z0fJy.js +0 -1
  172. package/dist/serve/site-ui/_astro/sandbox-controls.C2IqtoSw.js +0 -1252
  173. package/dist/serve/site-ui/_astro/seed.CCmj00su.js +0 -20
  174. package/dist/serve/site-ui/examples/firestore-first-write/index.html +0 -2
@@ -1,25 +1,26 @@
1
1
  <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Run Cloud Firestore locally · Pyric Studio</title><meta name="description" content="Read, write, query, and stream Firestore documents locally, and derive the composite indexes your queries need from your source."><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_.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" open> <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/" aria-current="page"> 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 &amp; 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 &amp; 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 &amp; 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 &amp; test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Conformance scores </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> </details> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <details class="docs-nav-group" id="nav-group-overview"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-get-started"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/vite/"> Vite </a> </li><li> <a href="/__pyric/ui/docs/get-started/nextjs/"> Next.js </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-build" open> <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/" aria-current="page"> 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 &amp; 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 &amp; 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 &amp; 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 &amp; test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-work-with-an-agent"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-trust"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-conformance"> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Conformance scores </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group" id="nav-group-reference"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> <main class="doc-main" data-astro-cid-mw7aashj> <nav class="docs-breadcrumbs" aria-label="Breadcrumb"> <ol> <li> <a href="/__pyric/ui/docs/build/authentication/">Build</a> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span aria-current="page">Cloud Firestore</span> </li> </ol> </nav> <article class="prose" data-astro-cid-mw7aashj><script type="module">const i=t=>{if(t.dataset.sizingInstalled==="true")return;t.dataset.sizingInstalled="true";let n;const o=()=>{const e=t.contentDocument;if(!e)return;const s=Math.max(Number(t.dataset.minHeight??0),e.documentElement.scrollHeight,e.body?.scrollHeight??0);t.style.height=`${s}px`};t.addEventListener("load",()=>{n?.disconnect();const e=t.contentDocument;e&&(n=new ResizeObserver(o),n.observe(e.documentElement),e.body&&n.observe(e.body),o())})};document.querySelectorAll("[data-example-frame]").forEach(i);</script> <h1 id="run-cloud-firestore-locally">Run Cloud Firestore locally</h1>
3
- <p>Firestore in Pyric is v1. The modular SDK surface, reads and writes, queries, snapshots, transactions, and aggregations, runs locally with your rules enforced, and it is tested against recorded production behavior. Your imports stay <code>firebase/firestore</code>.</p>
4
- <h2 id="write-and-read-documents">Write and read documents</h2>
5
- <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#C678DD">import</span><span style="color:#ABB2BF"> {</span></span>
6
- <span class="line"><span style="color:#E06C75"> getFirestore</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">collection</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">doc</span><span style="color:#ABB2BF">,</span></span>
7
- <span class="line"><span style="color:#E06C75"> addDoc</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">setDoc</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">getDoc</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">serverTimestamp</span><span style="color:#ABB2BF">,</span></span>
8
- <span class="line"><span style="color:#ABB2BF">} </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'firebase/firestore'</span><span style="color:#ABB2BF">;</span></span>
2
+ <link rel="stylesheet" href="/__pyric/ui/_astro/_slug_.CupVK4TM.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" open> <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/" aria-current="page"> 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 &amp; 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 &amp; 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 &amp; 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 &amp; test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Conformance scores </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> </details> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <details class="docs-nav-group" id="nav-group-overview"> <summary>Overview</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/overview/"> Overview </a> </li><li> <a href="/__pyric/ui/docs/tutorial/"> Tutorial </a> </li><li> <a href="/__pyric/ui/docs/examples/"> Examples </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-get-started"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/vite/"> Vite </a> </li><li> <a href="/__pyric/ui/docs/get-started/nextjs/"> Next.js </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-build" open> <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/" aria-current="page"> 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 &amp; 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 &amp; 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 &amp; 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 &amp; test</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/ship/ship-to-production/"> Ship to production </a> </li><li> <a href="/__pyric/ui/docs/ship/test-in-node/"> Test in Node </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-work-with-an-agent"> <summary>Work with an agent</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/agent/set-up-your-agent/"> Connect an agent </a> </li><li> <a href="/__pyric/ui/docs/agent/work-with-an-agent/"> Use the MCP tools </a> </li><li> <a href="/__pyric/ui/docs/agent/watch-and-review/"> Watch in Pyric Studio </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-trust"> <summary>Trust</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Versioning and the fb tag </a> </li><li> <a href="/__pyric/ui/docs/trust/storage-rules-stdlib-research/"> Storage Rules modules: evidence and design </a> </li><li> <a href="/__pyric/ui/docs/trust/rules-standard-library/"> Rules standard library reference </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-conformance"> <summary>Conformance</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/conformance-scores/"> Conformance scores </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> Firestore </a> </li><li> <a href="/__pyric/ui/docs/database-compat/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/storage-compat/"> Storage </a> </li><li> <a href="/__pyric/ui/docs/messaging-compat/"> Messaging </a> </li><li> <a href="/__pyric/ui/docs/functions-rtdb-compat/"> Functions · RTDB </a> </li><li> <a href="/__pyric/ui/docs/rules-compat/"> Rules </a> </li> </ul> </div> </div> </details> <details class="docs-nav-group" id="nav-group-reference"> <summary>Reference</summary> <div class="docs-nav-group-body docs-nav-group-body--reference"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/api-reference/"> API reference </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-app-reference-api/"> pyric/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-firestore-reference-api/"> pyric/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-auth-reference-api/"> pyric/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-reference-api/"> pyric/ai </a> </li><li> <a href="/__pyric/ui/docs/pyric-ai-scripting-reference-api/"> pyric/ai/scripting </a> </li><li> <a href="/__pyric/ui/docs/pyric-database-reference-api/"> pyric/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-storage-reference-api/"> pyric/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-reference-api/"> pyric/messaging </a> </li><li> <a href="/__pyric/ui/docs/pyric-messaging-sw-reference-api/"> pyric/messaging/sw </a> </li><li> <a href="/__pyric/ui/docs/pyric-rules-reference-api/"> pyric/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-next-reference-api/"> @pyric/cli/next </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-app-bridge-reference-api/"> @pyric/cli/register/app-bridge </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> <main class="doc-main" data-astro-cid-mw7aashj> <nav class="docs-breadcrumbs" aria-label="Breadcrumb"> <ol> <li> <a href="/__pyric/ui/docs/build/authentication/">Build</a> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span aria-current="page">Cloud Firestore</span> </li> </ol> </nav> <article class="prose" data-astro-cid-mw7aashj> <h1 id="run-cloud-firestore-locally">Run Cloud Firestore locally</h1>
3
+ <p>Firestore runs locally with your Security Rules enforced. Your application keeps the <code>firebase/firestore</code> imports it will ship.</p>
4
+ <h2 id="write-and-read-a-document">Write and read a document</h2>
5
+ <p>Use the same Firebase SDK calls in local development and production:</p>
6
+ <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#C678DD">import</span><span style="color:#ABB2BF"> { </span><span style="color:#E06C75">doc</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">getDoc</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">getFirestore</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">setDoc</span><span style="color:#ABB2BF"> } </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'firebase/firestore'</span><span style="color:#ABB2BF">;</span></span>
9
7
  <span class="line"></span>
10
8
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> db</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> getFirestore</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">app</span><span style="color:#ABB2BF">);</span></span>
9
+ <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> note</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> doc</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">db</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'notes'</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'first'</span><span style="color:#ABB2BF">);</span></span>
11
10
  <span class="line"></span>
12
- <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> noteRef</span><span style="color:#56B6C2"> =</span><span style="color:#C678DD"> await</span><span style="color:#61AFEF"> addDoc</span><span style="color:#ABB2BF">(</span><span style="color:#61AFEF">collection</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">db</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'notes'</span><span style="color:#ABB2BF">), {</span></span>
13
- <span class="line"><span style="color:#E06C75"> title</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'First note'</span><span style="color:#ABB2BF">,</span></span>
14
- <span class="line"><span style="color:#E06C75"> ownerId</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">uid</span><span style="color:#ABB2BF">,</span></span>
15
- <span class="line"><span style="color:#E06C75"> createdAt</span><span style="color:#ABB2BF">: </span><span style="color:#61AFEF">serverTimestamp</span><span style="color:#ABB2BF">(),</span></span>
11
+ <span class="line"><span style="color:#C678DD">await</span><span style="color:#61AFEF"> setDoc</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">note</span><span style="color:#ABB2BF">, {</span></span>
12
+ <span class="line"><span style="color:#E06C75"> title</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'The sandbox is local'</span><span style="color:#ABB2BF">,</span></span>
13
+ <span class="line"><span style="color:#E06C75"> ownerId</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'ada'</span><span style="color:#ABB2BF">,</span></span>
16
14
  <span class="line"><span style="color:#ABB2BF">});</span></span>
17
15
  <span class="line"></span>
18
- <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> snap</span><span style="color:#56B6C2"> =</span><span style="color:#C678DD"> await</span><span style="color:#61AFEF"> getDoc</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">noteRef</span><span style="color:#ABB2BF">);</span></span>
19
- <span class="line"><span style="color:#E5C07B">console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">snap</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">data</span><span style="color:#ABB2BF">());</span></span></code></pre>
20
- <p><code>setDoc</code> writes to a known path (pass <code>{ merge: true }</code> to update in place), <code>updateDoc</code> patches fields, <code>deleteDoc</code> removes. Field sentinels work: <code>serverTimestamp</code>, <code>increment</code>, <code>arrayUnion</code>, <code>arrayRemove</code>, <code>deleteField</code>.</p>
21
- <h2 id="query-with-where-orderby-limit">Query with where, orderBy, limit</h2>
22
- <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#C678DD">import</span><span style="color:#ABB2BF"> { </span><span style="color:#E06C75">query</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">where</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">orderBy</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">limit</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">getDocs</span><span style="color:#ABB2BF"> } </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'firebase/firestore'</span><span style="color:#ABB2BF">;</span></span>
16
+ <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> saved</span><span style="color:#56B6C2"> =</span><span style="color:#C678DD"> await</span><span style="color:#61AFEF"> getDoc</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">note</span><span style="color:#ABB2BF">);</span></span>
17
+ <span class="line"><span style="color:#E5C07B">console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">saved</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">data</span><span style="color:#ABB2BF">());</span></span></code></pre>
18
+ <p>During development, Pyric resolves those imports to the local backend. A production build resolves them to Firebase again.</p>
19
+ <h2 id="query-the-documents-the-current-user-owns">Query the documents the current user owns</h2>
20
+ <p>Combine the ownership filter with the order and result limit your screen needs:</p>
21
+ <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#C678DD">import</span><span style="color:#ABB2BF"> {</span></span>
22
+ <span class="line"><span style="color:#E06C75"> collection</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">getDocs</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">limit</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">orderBy</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">query</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">where</span><span style="color:#ABB2BF">,</span></span>
23
+ <span class="line"><span style="color:#ABB2BF">} </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'firebase/firestore'</span><span style="color:#ABB2BF">;</span></span>
23
24
  <span class="line"></span>
24
25
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> recent</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> query</span><span style="color:#ABB2BF">(</span></span>
25
26
  <span class="line"><span style="color:#61AFEF"> collection</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">db</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'notes'</span><span style="color:#ABB2BF">),</span></span>
@@ -30,13 +31,9 @@
30
31
  <span class="line"><span style="color:#ABB2BF">);</span></span>
31
32
  <span class="line"></span>
32
33
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> page</span><span style="color:#56B6C2"> =</span><span style="color:#C678DD"> await</span><span style="color:#61AFEF"> getDocs</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">recent</span><span style="color:#ABB2BF">);</span></span></code></pre>
33
- <p>Multiple <code>where</code> clauses AND together; the <code>or</code> and <code>and</code> combinators handle the rest. Cursors (<code>startAfter</code>, <code>endAt</code>) paginate, and collection groups query across every subcollection with a given name. Aggregations run without fetching the documents:</p>
34
- <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#C678DD">import</span><span style="color:#ABB2BF"> { </span><span style="color:#E06C75">getCountFromServer</span><span style="color:#ABB2BF"> } </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'firebase/firestore'</span><span style="color:#ABB2BF">;</span></span>
35
- <span class="line"></span>
36
- <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> count</span><span style="color:#56B6C2"> =</span><span style="color:#C678DD"> await</span><span style="color:#61AFEF"> getCountFromServer</span><span style="color:#ABB2BF">(</span><span style="color:#61AFEF">query</span><span style="color:#ABB2BF">(</span><span style="color:#61AFEF">collection</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">db</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'notes'</span><span style="color:#ABB2BF">), </span><span style="color:#61AFEF">where</span><span style="color:#ABB2BF">(</span><span style="color:#98C379">'archived'</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'=='</span><span style="color:#ABB2BF">, </span><span style="color:#D19A66">false</span><span style="color:#ABB2BF">)));</span></span>
37
- <span class="line"><span style="color:#E5C07B">console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">count</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">data</span><span style="color:#ABB2BF">().</span><span style="color:#E06C75">count</span><span style="color:#ABB2BF">);</span></span></code></pre>
38
- <p><code>sum</code> and <code>average</code> follow the same shape through <code>getAggregateFromServer</code>.</p>
39
- <h2 id="keep-the-ui-live">Keep the UI live</h2>
34
+ <p>The local Rules engine evaluates the query as a query. A rule that allows one document does not automatically make an under-constrained list safe.</p>
35
+ <h2 id="keep-those-results-live">Keep those results live</h2>
36
+ <p>Attach a listener to the same query:</p>
40
37
  <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#C678DD">import</span><span style="color:#ABB2BF"> { </span><span style="color:#E06C75">onSnapshot</span><span style="color:#ABB2BF"> } </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'firebase/firestore'</span><span style="color:#ABB2BF">;</span></span>
41
38
  <span class="line"></span>
42
39
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> unsubscribe</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> onSnapshot</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">recent</span><span style="color:#ABB2BF">, (</span><span style="color:#E06C75;font-style:italic">snap</span><span style="color:#ABB2BF">) </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
@@ -44,9 +41,9 @@
44
41
  <span class="line"><span style="color:#E5C07B"> console</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">log</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">change</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">type</span><span style="color:#ABB2BF">, </span><span style="color:#E5C07B">change</span><span style="color:#ABB2BF">.</span><span style="color:#E5C07B">doc</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">id</span><span style="color:#ABB2BF">);</span></span>
45
42
  <span class="line"><span style="color:#ABB2BF"> }</span></span>
46
43
  <span class="line"><span style="color:#ABB2BF">});</span></span></code></pre>
47
- <p>Listeners fire on every matching change, across tabs, because every tab shares one backend. If your rules deny a listener, the error callback fires with a verdict that names the rule, not a bare <code>permission-denied</code>.</p>
48
- <h2 id="read-then-write-atomically">Read, then write, atomically</h2>
49
- <p>When a write depends on the current value, use a transaction:</p>
44
+ <p>Listeners receive each matching change across tabs. If Rules deny the listener, its error callback receives a verdict that identifies the rule responsible.</p>
45
+ <h2 id="update-a-value-atomically">Update a value atomically</h2>
46
+ <p>Use a transaction when the next write depends on the stored value:</p>
50
47
  <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#C678DD">import</span><span style="color:#ABB2BF"> { </span><span style="color:#E06C75">runTransaction</span><span style="color:#ABB2BF"> } </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'firebase/firestore'</span><span style="color:#ABB2BF">;</span></span>
51
48
  <span class="line"></span>
52
49
  <span class="line"><span style="color:#C678DD">await</span><span style="color:#61AFEF"> runTransaction</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">db</span><span style="color:#ABB2BF">, </span><span style="color:#C678DD">async</span><span style="color:#ABB2BF"> (</span><span style="color:#E06C75;font-style:italic">tx</span><span style="color:#ABB2BF">) </span><span style="color:#C678DD">=></span><span style="color:#ABB2BF"> {</span></span>
@@ -54,31 +51,12 @@
54
51
  <span class="line"><span style="color:#C678DD"> const</span><span style="color:#E5C07B"> current</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> counter</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">exists</span><span style="color:#ABB2BF">() </span><span style="color:#C678DD">?</span><span style="color:#E5C07B"> counter</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">data</span><span style="color:#ABB2BF">().</span><span style="color:#E06C75">count</span><span style="color:#C678DD"> :</span><span style="color:#D19A66"> 0</span><span style="color:#ABB2BF">;</span></span>
55
52
  <span class="line"><span style="color:#E5C07B"> tx</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">set</span><span style="color:#ABB2BF">(</span><span style="color:#61AFEF">doc</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">db</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'counters'</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">'main'</span><span style="color:#ABB2BF">), { </span><span style="color:#E06C75">count</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">current</span><span style="color:#56B6C2"> +</span><span style="color:#D19A66"> 1</span><span style="color:#ABB2BF"> });</span></span>
56
53
  <span class="line"><span style="color:#ABB2BF">});</span></span></code></pre>
57
- <p>All reads must come before any writes inside the callback. Production enforces that because the engine retries on conflict, and the sandbox enforces it for parity, so the mistake surfaces on your machine instead of in a retry storm. For multiple writes with no read dependency, <code>writeBatch</code> is the cheaper shape.</p>
58
- <h2 id="design-queries-and-the-indexes-they-need">Design queries and the indexes they need</h2>
59
- <p>A query with combined filters and ordering needs a composite index in production, and the missing-index error arrives at the worst time. Pyric derives the index file from your code instead: <code>firestore_extract_indexes</code> statically reads your <code>query(collection(...), where(...), orderBy(...))</code> call sites and returns the <code>firestore.indexes.json</code> they require, with warnings where it suspects overshoot.</p>
60
- <p>When branchy code enumerates more shapes than your app will ever run, guide the extractor with JSDoc annotations on the function:</p>
61
- <ul>
62
- <li><code>@firestore-mutex { fieldA, fieldB }</code> drops combinations where those filters would coexist.</li>
63
- <li><code>@firestore-required fieldA</code> drops combinations missing a filter that is always present.</li>
64
- <li><code>@firestore-budget N</code> is a soft cap that warns when exceeded.</li>
65
- </ul>
66
- <p>The index file stops being a hand-kept artifact. It becomes derived output, and <a href="../../ship/ship-to-production/">ship to production</a> deploys it.</p>
54
+ <p>Keep every transaction read before its first write. Pyric enforces that ordering locally, matching the retry-safe shape Firebase requires.</p>
55
+ <h2 id="generate-the-indexes-your-queries-need">Generate the indexes your queries need</h2>
56
+ <p>Derive <code>firestore.indexes.json</code> from the filters and ordering in your application source:</p>
57
+ <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#61AFEF">pyric</span><span style="color:#98C379"> firestore</span><span style="color:#98C379"> indexes</span><span style="color:#98C379"> generate</span><span style="color:#98C379"> src</span><span style="color:#D19A66"> --out</span><span style="color:#98C379"> firestore.indexes.json</span></span></code></pre>
58
+ <p>Review the generated file with the query open, then deploy it with the rest of the Firebase configuration when you <a href="../../ship/ship-to-production/">ship to production</a>.</p>
67
59
  <h2 id="and-from-an-agent">And from an agent</h2>
68
- <p>An MCP-connected agent can inventory the queries the product performs and compare them with the rules that govern list access. Start with <a href="../../agent/work-with-an-agent/">Work with an agent</a>.</p>
60
+ <p>Ask a connected agent to inventory the Firestore queries in the application, derive their indexes, and compare each query with the Rules that govern list access. <a href="../../agent/work-with-an-agent/">Work with an agent</a> shows how the agent connects to the same local backend.</p>
69
61
  <h2 id="where-to-go-next">Where to go next</h2>
70
- <p>Data without protection is a liability, so <a href="../../secure/secure-it-with-rules/">secure it with rules</a>. And when you need scenarios instead of hand-typed documents, <a href="../../observe/shape-your-data/">shape your data</a> covers seeding, snapshots, and resets.</p> <figure class="pyric-example" data-astro-cid-2qfr4sn2> <figcaption data-astro-cid-2qfr4sn2>Write to an isolated Firestore sandbox</figcaption> <div class="pyric-example-grid" data-astro-cid-2qfr4sn2> <pre data-astro-cid-2qfr4sn2><code data-astro-cid-2qfr4sn2>import { doc, getDoc, getFirestore, setDoc } from &#39;pyric/firestore&#39;;
71
- import type { PyricExampleContext } from &#39;../definition&#39;;
72
-
73
- export async function run({ sandbox }: PyricExampleContext) {
74
- const db = getFirestore(sandbox.withAuth({ uid: &#39;ada&#39; }));
75
- const note = doc(db, &#39;notes&#39;, &#39;first&#39;);
76
-
77
- await setDoc(note, {
78
- title: &#39;The sandbox is local&#39;,
79
- ownerId: &#39;ada&#39;,
80
- });
81
-
82
- const saved = await getDoc(note);
83
- return saved.data();
84
- }</code></pre> <div class="example-stage" data-astro-cid-xgvwrve7> <iframe title="Write to an isolated Firestore sandbox" src="/__pyric/ui/examples/firestore-first-write/" sandbox="allow-scripts allow-same-origin" scrolling="no" data-example-frame data-example-id="firestore-first-write" data-min-height="304" data-astro-cid-xgvwrve7></iframe> </div> </div> </figure> </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="#write-and-read-documents" data-astro-cid-mw7aashj>Write and read documents</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#query-with-where-orderby-limit" data-astro-cid-mw7aashj>Query with where, orderBy, limit</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#keep-the-ui-live" data-astro-cid-mw7aashj>Keep the UI live</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#read-then-write-atomically" data-astro-cid-mw7aashj>Read, then write, atomically</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#design-queries-and-the-indexes-they-need" data-astro-cid-mw7aashj>Design queries and the indexes they need</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#and-from-an-agent" data-astro-cid-mw7aashj>And from an agent</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#where-to-go-next" data-astro-cid-mw7aashj>Where to go next</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>
62
+ <p>Now <a href="../../secure/secure-it-with-rules/">prove your Rules protect the application</a>.</p> </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="#write-and-read-a-document" data-astro-cid-mw7aashj>Write and read a document</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#query-the-documents-the-current-user-owns" data-astro-cid-mw7aashj>Query the documents the current user owns</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#keep-those-results-live" data-astro-cid-mw7aashj>Keep those results live</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#update-a-value-atomically" data-astro-cid-mw7aashj>Update a value atomically</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#generate-the-indexes-your-queries-need" data-astro-cid-mw7aashj>Generate the indexes your queries need</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#and-from-an-agent" data-astro-cid-mw7aashj>And from an agent</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#where-to-go-next" data-astro-cid-mw7aashj>Where to go next</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>
@@ -1,31 +1,37 @@
1
1
 
2
2
  # Run Cloud Firestore locally
3
3
 
4
- Firestore in Pyric is v1. The modular SDK surface, reads and writes, queries, snapshots, transactions, and aggregations, runs locally with your rules enforced, and it is tested against recorded production behavior. Your imports stay `firebase/firestore`.
4
+ Firestore runs locally with your Security Rules enforced. Your application keeps the `firebase/firestore` imports it will ship.
5
+
6
+ ## Write and read a document
7
+
8
+ Use the same Firebase SDK calls in local development and production:
5
9
 
6
- ## Write and read documents
7
10
  ```ts
8
- import {
9
- getFirestore, collection, doc,
10
- addDoc, setDoc, getDoc, serverTimestamp,
11
- } from 'firebase/firestore';
11
+ import { doc, getDoc, getFirestore, setDoc } from 'firebase/firestore';
12
12
 
13
13
  const db = getFirestore(app);
14
+ const note = doc(db, 'notes', 'first');
14
15
 
15
- const noteRef = await addDoc(collection(db, 'notes'), {
16
- title: 'First note',
17
- ownerId: uid,
18
- createdAt: serverTimestamp(),
16
+ await setDoc(note, {
17
+ title: 'The sandbox is local',
18
+ ownerId: 'ada',
19
19
  });
20
20
 
21
- const snap = await getDoc(noteRef);
22
- console.log(snap.data());
21
+ const saved = await getDoc(note);
22
+ console.log(saved.data());
23
23
  ```
24
- `setDoc` writes to a known path (pass `{ merge: true }` to update in place), `updateDoc` patches fields, `deleteDoc` removes. Field sentinels work: `serverTimestamp`, `increment`, `arrayUnion`, `arrayRemove`, `deleteField`.
25
24
 
26
- ## Query with where, orderBy, limit
25
+ During development, Pyric resolves those imports to the local backend. A production build resolves them to Firebase again.
26
+
27
+ ## Query the documents the current user owns
28
+
29
+ Combine the ownership filter with the order and result limit your screen needs:
30
+
27
31
  ```ts
28
- import { query, where, orderBy, limit, getDocs } from 'firebase/firestore';
32
+ import {
33
+ collection, getDocs, limit, orderBy, query, where,
34
+ } from 'firebase/firestore';
29
35
 
30
36
  const recent = query(
31
37
  collection(db, 'notes'),
@@ -37,16 +43,13 @@ const recent = query(
37
43
 
38
44
  const page = await getDocs(recent);
39
45
  ```
40
- Multiple `where` clauses AND together; the `or` and `and` combinators handle the rest. Cursors (`startAfter`, `endAt`) paginate, and collection groups query across every subcollection with a given name. Aggregations run without fetching the documents:
41
- ```ts
42
- import { getCountFromServer } from 'firebase/firestore';
43
46
 
44
- const count = await getCountFromServer(query(collection(db, 'notes'), where('archived', '==', false)));
45
- console.log(count.data().count);
46
- ```
47
- `sum` and `average` follow the same shape through `getAggregateFromServer`.
47
+ The local Rules engine evaluates the query as a query. A rule that allows one document does not automatically make an under-constrained list safe.
48
+
49
+ ## Keep those results live
50
+
51
+ Attach a listener to the same query:
48
52
 
49
- ## Keep the UI live
50
53
  ```ts
51
54
  import { onSnapshot } from 'firebase/firestore';
52
55
 
@@ -56,11 +59,13 @@ const unsubscribe = onSnapshot(recent, (snap) => {
56
59
  }
57
60
  });
58
61
  ```
59
- Listeners fire on every matching change, across tabs, because every tab shares one backend. If your rules deny a listener, the error callback fires with a verdict that names the rule, not a bare `permission-denied`.
60
62
 
61
- ## Read, then write, atomically
63
+ Listeners receive each matching change across tabs. If Rules deny the listener, its error callback receives a verdict that identifies the rule responsible.
64
+
65
+ ## Update a value atomically
66
+
67
+ Use a transaction when the next write depends on the stored value:
62
68
 
63
- When a write depends on the current value, use a transaction:
64
69
  ```ts
65
70
  import { runTransaction } from 'firebase/firestore';
66
71
 
@@ -70,24 +75,23 @@ await runTransaction(db, async (tx) => {
70
75
  tx.set(doc(db, 'counters', 'main'), { count: current + 1 });
71
76
  });
72
77
  ```
73
- All reads must come before any writes inside the callback. Production enforces that because the engine retries on conflict, and the sandbox enforces it for parity, so the mistake surfaces on your machine instead of in a retry storm. For multiple writes with no read dependency, `writeBatch` is the cheaper shape.
74
78
 
75
- ## Design queries and the indexes they need
79
+ Keep every transaction read before its first write. Pyric enforces that ordering locally, matching the retry-safe shape Firebase requires.
76
80
 
77
- A query with combined filters and ordering needs a composite index in production, and the missing-index error arrives at the worst time. Pyric derives the index file from your code instead: `firestore_extract_indexes` statically reads your `query(collection(...), where(...), orderBy(...))` call sites and returns the `firestore.indexes.json` they require, with warnings where it suspects overshoot.
81
+ ## Generate the indexes your queries need
78
82
 
79
- When branchy code enumerates more shapes than your app will ever run, guide the extractor with JSDoc annotations on the function:
83
+ Derive `firestore.indexes.json` from the filters and ordering in your application source:
80
84
 
81
- - `@firestore-mutex { fieldA, fieldB }` drops combinations where those filters would coexist.
82
- - `@firestore-required fieldA` drops combinations missing a filter that is always present.
83
- - `@firestore-budget N` is a soft cap that warns when exceeded.
85
+ ```bash
86
+ pyric firestore indexes generate src --out firestore.indexes.json
87
+ ```
84
88
 
85
- The index file stops being a hand-kept artifact. It becomes derived output, and [ship to production](../ship/ship-to-production.md) deploys it.
89
+ Review the generated file with the query open, then deploy it with the rest of the Firebase configuration when you [ship to production](../ship/ship-to-production.md).
86
90
 
87
91
  ## And from an agent
88
92
 
89
- An MCP-connected agent can inventory the queries the product performs and compare them with the rules that govern list access. Start with [Work with an agent](../agent/work-with-an-agent.md).
93
+ Ask a connected agent to inventory the Firestore queries in the application, derive their indexes, and compare each query with the Rules that govern list access. [Work with an agent](../agent/work-with-an-agent.md) shows how the agent connects to the same local backend.
90
94
 
91
95
  ## Where to go next
92
96
 
93
- Data without protection is a liability, so [secure it with rules](../secure/secure-it-with-rules.md). And when you need scenarios instead of hand-typed documents, [shape your data](../observe/shape-your-data.md) covers seeding, snapshots, and resets.
97
+ Now [prove your Rules protect the application](../secure/secure-it-with-rules.md).