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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/README.md.orig +2 -2
  2. package/dist/assurance/.generated/conformance-verdicts.d.ts +16 -0
  3. package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
  4. package/dist/assurance/.generated/conformance-verdicts.js +16 -0
  5. package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
  6. package/dist/bridge/server/peer.d.ts +1 -1
  7. package/dist/bridge/server.d.ts +1 -1
  8. package/dist/bridge/server.js +1 -1
  9. package/dist/cli/index.d.ts +11 -0
  10. package/dist/cli/index.d.ts.map +1 -1
  11. package/dist/cli/index.js +13 -1
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/cli/init.d.ts +3 -3
  14. package/dist/cli/init.d.ts.map +1 -1
  15. package/dist/cli/init.js +5 -5
  16. package/dist/cli/init.js.map +1 -1
  17. package/dist/cli/serve.d.ts.map +1 -1
  18. package/dist/cli/serve.js +8 -2
  19. package/dist/cli/serve.js.map +1 -1
  20. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  21. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  22. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  23. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  24. package/dist/conformance/.generated/can-i-use.js +2 -2
  25. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  26. package/dist/conformance/.generated/conformance-docs.d.ts +12 -0
  27. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -0
  28. package/dist/conformance/.generated/conformance-docs.js +2 -0
  29. package/dist/conformance/.generated/conformance-docs.js.map +1 -0
  30. package/dist/conformance/docs.d.ts +6 -0
  31. package/dist/conformance/docs.d.ts.map +1 -0
  32. package/dist/conformance/docs.js +6 -0
  33. package/dist/conformance/docs.js.map +1 -0
  34. package/dist/remote/index.d.ts.map +1 -1
  35. package/dist/remote/index.js +1 -0
  36. package/dist/remote/index.js.map +1 -1
  37. package/dist/serve/activity-guard.d.ts +4 -0
  38. package/dist/serve/activity-guard.d.ts.map +1 -0
  39. package/dist/serve/activity-guard.js +22 -0
  40. package/dist/serve/activity-guard.js.map +1 -0
  41. package/dist/serve/activity-route.d.ts +4 -0
  42. package/dist/serve/activity-route.d.ts.map +1 -0
  43. package/dist/serve/activity-route.js +157 -0
  44. package/dist/serve/activity-route.js.map +1 -0
  45. package/dist/serve/activity-warning.d.ts +4 -0
  46. package/dist/serve/activity-warning.d.ts.map +1 -0
  47. package/dist/serve/activity-warning.js +21 -0
  48. package/dist/serve/activity-warning.js.map +1 -0
  49. package/dist/serve/bridge-mount.d.ts +5 -0
  50. package/dist/serve/bridge-mount.d.ts.map +1 -1
  51. package/dist/serve/bridge-mount.js +1 -0
  52. package/dist/serve/bridge-mount.js.map +1 -1
  53. package/dist/serve/docs-ui/404.html +3 -0
  54. package/dist/serve/docs-ui/_astro/{_slug_.DPUwShm2.css → _slug_.CXh9yTP1.css} +1 -1
  55. package/dist/serve/docs-ui/_astro/_slug_.DUci-duz.css +1 -0
  56. package/dist/serve/docs-ui/docs/_rhythm/index.html +2 -1
  57. package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +3 -2
  58. package/dist/serve/docs-ui/docs/agent/set-up-your-agent.md +1 -1
  59. package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +2 -1
  60. package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +2 -1
  61. package/dist/serve/docs-ui/docs/ai-compat/index.html +2 -1
  62. package/dist/serve/docs-ui/docs/api-reference/index.html +51 -49
  63. package/dist/serve/docs-ui/docs/api-reference.md +3 -3
  64. package/dist/serve/docs-ui/docs/app-compat/index.html +2 -1
  65. package/dist/serve/docs-ui/docs/auth-compat/index.html +2 -1
  66. package/dist/serve/docs-ui/docs/build/ai-logic/index.html +18 -3
  67. package/dist/serve/docs-ui/docs/build/ai-logic.md +30 -2
  68. package/dist/serve/docs-ui/docs/build/authentication/index.html +2 -1
  69. package/dist/serve/docs-ui/docs/build/authentication.md +1 -1
  70. package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +2 -1
  71. package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +46 -2
  72. package/dist/serve/docs-ui/docs/build/cloud-messaging.md +56 -1
  73. package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +2 -1
  74. package/dist/serve/docs-ui/docs/build/cloud-storage.md +1 -1
  75. package/dist/serve/docs-ui/docs/build/realtime-database/index.html +21 -3
  76. package/dist/serve/docs-ui/docs/build/realtime-database.md +25 -1
  77. package/dist/serve/docs-ui/docs/conformance-scores/index.html +2 -1
  78. package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +61 -28
  79. package/dist/serve/docs-ui/docs/create-pyric-reference-api.md +65 -20
  80. package/dist/serve/docs-ui/docs/database-compat/index.html +2 -1
  81. package/dist/serve/docs-ui/docs/firestore-compat/index.html +6 -5
  82. package/dist/serve/docs-ui/docs/firestore-compat.md +4 -4
  83. package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +2 -1
  84. package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
  85. package/dist/serve/docs-ui/docs/get-started/start-building/index.html +4 -3
  86. package/dist/serve/docs-ui/docs/get-started/start-building.md +2 -2
  87. package/dist/serve/docs-ui/docs/index.json +979 -579
  88. package/dist/serve/docs-ui/docs/messaging-compat/index.html +2 -1
  89. package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +2 -1
  90. package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +2 -1
  91. package/dist/serve/docs-ui/docs/overview/index.html +2 -1
  92. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +5 -4
  93. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api.md +3 -4
  94. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +5 -4
  95. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api.md +3 -4
  96. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +5 -4
  97. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api.md +3 -4
  98. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +2503 -12
  99. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api.md +2219 -8
  100. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
  101. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
  102. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +4 -3
  103. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api.md +2 -3
  104. package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +16 -11
  105. package/dist/serve/docs-ui/docs/pyric-ai-reference-api.md +14 -13
  106. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +4 -3
  107. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api.md +2 -3
  108. package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +7 -2
  109. package/dist/serve/docs-ui/docs/pyric-app-reference-api.md +5 -2
  110. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +23 -12
  111. package/dist/serve/docs-ui/docs/pyric-auth-reference-api.md +15 -12
  112. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +10 -48
  113. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api.md +11 -24
  114. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +87 -97
  115. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api.md +53 -65
  116. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +14 -15
  117. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api.md +19 -28
  118. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +4 -3
  119. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api.md +2 -3
  120. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +3 -2
  121. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api.md +1 -2
  122. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +51 -0
  123. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api.md +36 -0
  124. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +3 -2
  125. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api.md +1 -2
  126. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +3 -2
  127. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api.md +1 -2
  128. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +3 -2
  129. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api.md +1 -2
  130. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +3 -2
  131. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api.md +1 -2
  132. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1067 -83
  133. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api.md +1291 -160
  134. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +253 -157
  135. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +179 -85
  136. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +72 -7
  137. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api.md +22 -7
  138. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +33 -12
  139. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +15 -12
  140. package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +18 -17
  141. package/dist/serve/docs-ui/docs/pyric-database-reference-api.md +19 -22
  142. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1512 -545
  143. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api.md +1311 -316
  144. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +5 -4
  145. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api.md +3 -4
  146. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +69 -369
  147. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api.md +103 -216
  148. package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +10 -9
  149. package/dist/serve/docs-ui/docs/pyric-rules-reference-api.md +9 -10
  150. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +8 -7
  151. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api.md +6 -7
  152. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +10 -9
  153. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api.md +9 -10
  154. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +371 -884
  155. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +369 -883
  156. package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +25 -17
  157. package/dist/serve/docs-ui/docs/pyric-storage-reference-api.md +21 -19
  158. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +3 -2
  159. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api.md +1 -2
  160. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +63 -776
  161. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api.md +69 -510
  162. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +22 -21
  163. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api.md +29 -30
  164. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +15 -168
  165. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api.md +17 -87
  166. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +6 -5
  167. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api.md +5 -6
  168. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +82 -970
  169. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api.md +104 -479
  170. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +30 -29
  171. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api.md +54 -55
  172. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +3 -2
  173. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api.md +1 -2
  174. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -4
  175. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api.md +3 -4
  176. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +9 -124
  177. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api.md +7 -58
  178. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +24 -23
  179. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api.md +31 -34
  180. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +104 -1385
  181. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api.md +133 -817
  182. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +34 -33
  183. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api.md +37 -38
  184. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +116 -1169
  185. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api.md +142 -590
  186. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +4 -3
  187. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api.md +3 -4
  188. package/dist/serve/docs-ui/docs/rules-compat/index.html +34 -1
  189. package/dist/serve/docs-ui/docs/rules-compat.md +32 -0
  190. package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +19 -2
  191. package/dist/serve/docs-ui/docs/secure/audit-your-rules.md +32 -0
  192. package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +2 -1
  193. package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +2 -1
  194. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +2 -1
  195. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript.md +1 -1
  196. package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +2 -1
  197. package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +2 -1
  198. package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +2 -1
  199. package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +2 -1
  200. package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +2 -1
  201. package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +2 -1
  202. package/dist/serve/docs-ui/docs/storage-compat/index.html +2 -1
  203. package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +2 -1
  204. package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +2 -1
  205. package/dist/serve/docs-ui/llms.txt +2 -1
  206. package/dist/serve/entries/ai.d.ts.map +1 -1
  207. package/dist/serve/entries/ai.js +39 -1
  208. package/dist/serve/entries/ai.js.map +1 -1
  209. package/dist/serve/entries/messaging-sw.d.ts.map +1 -1
  210. package/dist/serve/entries/messaging-sw.js +5 -1
  211. package/dist/serve/entries/messaging-sw.js.map +1 -1
  212. package/dist/serve/entries/messaging.d.ts.map +1 -1
  213. package/dist/serve/entries/messaging.js +7 -1
  214. package/dist/serve/entries/messaging.js.map +1 -1
  215. package/dist/serve/entries/runtime.d.ts.map +1 -1
  216. package/dist/serve/entries/runtime.js +20 -1
  217. package/dist/serve/entries/runtime.js.map +1 -1
  218. package/dist/serve/init-payload.d.ts +25 -0
  219. package/dist/serve/init-payload.d.ts.map +1 -1
  220. package/dist/serve/namespace.d.ts +39 -0
  221. package/dist/serve/namespace.d.ts.map +1 -1
  222. package/dist/serve/namespace.js +80 -1
  223. package/dist/serve/namespace.js.map +1 -1
  224. package/dist/serve/standalone-assets.d.ts +11 -9
  225. package/dist/serve/standalone-assets.d.ts.map +1 -1
  226. package/dist/serve/standalone-assets.js +6 -5
  227. package/dist/serve/standalone-assets.js.map +1 -1
  228. package/dist/serve/studio-ui/assets/{RulesCodeEditor-CXdSLKhO.js → RulesCodeEditor-CxVYcZXw.js} +1 -1
  229. package/dist/serve/studio-ui/assets/index-cpSfgQ7H.js +1218 -0
  230. package/dist/serve/studio-ui/assets/seed-DeuFKi4J.js +20 -0
  231. package/dist/serve/studio-ui/index.html +1 -1
  232. package/dist/serve/vite-plugin.d.ts +92 -15
  233. package/dist/serve/vite-plugin.d.ts.map +1 -1
  234. package/dist/serve/vite-plugin.js +281 -11
  235. package/dist/serve/vite-plugin.js.map +1 -1
  236. package/dist/serve/worker/activity-bootstrap.d.ts +8 -0
  237. package/dist/serve/worker/activity-bootstrap.d.ts.map +1 -0
  238. package/dist/serve/worker/activity-bootstrap.js +11 -0
  239. package/dist/serve/worker/activity-bootstrap.js.map +1 -0
  240. package/dist/serve/worker/client/connection.d.ts.map +1 -1
  241. package/dist/serve/worker/client/connection.js +17 -5
  242. package/dist/serve/worker/client/connection.js.map +1 -1
  243. package/dist/serve/worker/client/core.d.ts +2 -2
  244. package/dist/serve/worker/client/core.d.ts.map +1 -1
  245. package/dist/serve/worker/client/core.js +33 -5
  246. package/dist/serve/worker/client/core.js.map +1 -1
  247. package/dist/serve/worker/client/firestore-writes.d.ts.map +1 -1
  248. package/dist/serve/worker/client/firestore-writes.js +1 -0
  249. package/dist/serve/worker/client/firestore-writes.js.map +1 -1
  250. package/dist/serve/worker/client/messaging.d.ts +9 -1
  251. package/dist/serve/worker/client/messaging.d.ts.map +1 -1
  252. package/dist/serve/worker/client/messaging.js +15 -0
  253. package/dist/serve/worker/client/messaging.js.map +1 -1
  254. package/dist/serve/worker/client/studio.d.ts +10 -0
  255. package/dist/serve/worker/client/studio.d.ts.map +1 -1
  256. package/dist/serve/worker/client/studio.js +11 -0
  257. package/dist/serve/worker/client/studio.js.map +1 -1
  258. package/dist/serve/worker/durable-persistence.d.ts +12 -0
  259. package/dist/serve/worker/durable-persistence.d.ts.map +1 -0
  260. package/dist/serve/worker/durable-persistence.js +118 -0
  261. package/dist/serve/worker/durable-persistence.js.map +1 -0
  262. package/dist/serve/worker/entry.d.ts +7 -3
  263. package/dist/serve/worker/entry.d.ts.map +1 -1
  264. package/dist/serve/worker/entry.js +7 -3
  265. package/dist/serve/worker/entry.js.map +1 -1
  266. package/dist/serve/worker/host/core.d.ts +9 -5
  267. package/dist/serve/worker/host/core.d.ts.map +1 -1
  268. package/dist/serve/worker/host/core.js +34 -9
  269. package/dist/serve/worker/host/core.js.map +1 -1
  270. package/dist/serve/worker/host/dispatch.d.ts.map +1 -1
  271. package/dist/serve/worker/host/dispatch.js +9 -2
  272. package/dist/serve/worker/host/dispatch.js.map +1 -1
  273. package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
  274. package/dist/serve/worker/host/firestore-writes.js +3 -2
  275. package/dist/serve/worker/host/firestore-writes.js.map +1 -1
  276. package/dist/serve/worker/host/studio.d.ts +1 -1
  277. package/dist/serve/worker/host/studio.d.ts.map +1 -1
  278. package/dist/serve/worker/host/studio.js +36 -1
  279. package/dist/serve/worker/host/studio.js.map +1 -1
  280. package/dist/serve/worker/host/subscriptions.d.ts.map +1 -1
  281. package/dist/serve/worker/host/subscriptions.js +27 -7
  282. package/dist/serve/worker/host/subscriptions.js.map +1 -1
  283. package/dist/serve/worker/host-context.d.ts +15 -0
  284. package/dist/serve/worker/host-context.d.ts.map +1 -1
  285. package/dist/serve/worker/host-context.js +10 -0
  286. package/dist/serve/worker/host-context.js.map +1 -1
  287. package/dist/serve/worker/host-messaging.d.ts.map +1 -1
  288. package/dist/serve/worker/host-messaging.js +9 -2
  289. package/dist/serve/worker/host-messaging.js.map +1 -1
  290. package/dist/serve/worker/index.d.ts +3 -2
  291. package/dist/serve/worker/index.d.ts.map +1 -1
  292. package/dist/serve/worker/index.js +4 -1
  293. package/dist/serve/worker/index.js.map +1 -1
  294. package/dist/serve/worker/protocol.d.ts +25 -3
  295. package/dist/serve/worker/protocol.d.ts.map +1 -1
  296. package/dist/serve/worker/protocol.js +2 -2
  297. package/dist/serve/worker/protocol.js.map +1 -1
  298. package/dist/serve/worker/serve-init.d.ts +23 -37
  299. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  300. package/dist/serve/worker/serve-init.js +95 -172
  301. package/dist/serve/worker/serve-init.js.map +1 -1
  302. package/dist/vite.d.ts +4 -4
  303. package/dist/vite.d.ts.map +1 -1
  304. package/dist/vite.js +3 -3
  305. package/dist/vite.js.map +1 -1
  306. package/package.json +9 -4
  307. package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
  308. package/src/assurance/attachment.ts +211 -0
  309. package/src/assurance/browser.ts +80 -0
  310. package/src/assurance/campaign.ts +496 -0
  311. package/src/assurance/capabilities.ts +446 -0
  312. package/src/assurance/cases.ts +43 -0
  313. package/src/assurance/index.ts +75 -0
  314. package/src/assurance/runner.ts +885 -0
  315. package/src/assurance/tool-names.ts +15 -0
  316. package/src/assurance/tools.ts +830 -0
  317. package/src/assurance/types.ts +352 -0
  318. package/src/assurance/validation.ts +434 -0
  319. package/src/bridge/client/bridge.ts +550 -0
  320. package/src/bridge/client/dispatch.ts +137 -0
  321. package/src/bridge/client.ts +45 -0
  322. package/src/bridge/protocol.ts +351 -0
  323. package/src/bridge/server/audit.ts +53 -0
  324. package/src/bridge/server/bridge.ts +597 -0
  325. package/src/bridge/server/headless.ts +160 -0
  326. package/src/bridge/server/json-schema-to-zod.ts +108 -0
  327. package/src/bridge/server/local-bridge.ts +66 -0
  328. package/src/bridge/server/logger.ts +50 -0
  329. package/src/bridge/server/mcp-contract.ts +93 -0
  330. package/src/bridge/server/mcp.ts +129 -0
  331. package/src/bridge/server/peer.ts +232 -0
  332. package/src/bridge/server/standalone.ts +347 -0
  333. package/src/bridge/server/tool-metadata.ts +99 -0
  334. package/src/bridge/server.ts +28 -0
  335. package/src/cli/can-i-use.ts +50 -0
  336. package/src/cli/cli.test.ts +493 -0
  337. package/src/cli/database-rules.ts +329 -0
  338. package/src/cli/dev-runner.ts +282 -0
  339. package/src/cli/firebase-json.ts +100 -0
  340. package/src/cli/firestore-indexes.ts +70 -0
  341. package/src/cli/index.ts +425 -0
  342. package/src/cli/init.ts +260 -0
  343. package/src/cli/mcp-proxy.ts +196 -0
  344. package/src/cli/parse-args.ts +80 -0
  345. package/src/cli/rules.ts +259 -0
  346. package/src/cli/scope.ts +67 -0
  347. package/src/cli/serve.ts +996 -0
  348. package/src/cli/service-commands.ts +73 -0
  349. package/src/cli/snapshot.ts +164 -0
  350. package/src/cli/storage-rules.ts +172 -0
  351. package/src/cli/verify.ts +416 -0
  352. package/src/conformance/.generated/can-i-use-browser.ts +113 -0
  353. package/src/conformance/.generated/can-i-use.ts +117 -0
  354. package/src/conformance/.generated/conformance-docs.ts +14 -0
  355. package/src/conformance/browser.ts +24 -0
  356. package/src/conformance/can-i-use-tool.ts +51 -0
  357. package/src/conformance/can-i-use.ts +18 -0
  358. package/src/conformance/docs.ts +8 -0
  359. package/src/conformance/index.ts +20 -0
  360. package/src/conformance/tools.ts +16 -0
  361. package/src/credentials/core/memoize-ttl.ts +142 -0
  362. package/src/credentials/core/types.ts +10 -0
  363. package/src/credentials/node/from-adc.ts +96 -0
  364. package/src/credentials/node/from-service-account.ts +133 -0
  365. package/src/credentials/node/index.ts +6 -0
  366. package/src/discover/concurrency.ts +127 -0
  367. package/src/discover/crawler-adapter.ts +142 -0
  368. package/src/discover/crawler.ts +1127 -0
  369. package/src/discover/credential-free.ts +27 -0
  370. package/src/discover/findCollectionGroup.ts +131 -0
  371. package/src/discover/firestore-source.ts +59 -0
  372. package/src/discover/index.ts +5 -0
  373. package/src/discover/merge.ts +523 -0
  374. package/src/discover/session.ts +402 -0
  375. package/src/discover/tools.ts +201 -0
  376. package/src/discover/types.ts +187 -0
  377. package/src/discover/wire.ts +324 -0
  378. package/src/functions-rtdb/child.ts +382 -0
  379. package/src/functions-rtdb/delivery.ts +7 -0
  380. package/src/functions-rtdb/discovery.ts +136 -0
  381. package/src/functions-rtdb/event.ts +64 -0
  382. package/src/functions-rtdb/execution.ts +111 -0
  383. package/src/functions-rtdb/in-memory-delivery.ts +40 -0
  384. package/src/functions-rtdb/project.ts +74 -0
  385. package/src/functions-rtdb/projection.ts +104 -0
  386. package/src/functions-rtdb/reference-pattern.ts +20 -0
  387. package/src/functions-rtdb/remote-delivery.ts +23 -0
  388. package/src/pkg-version.ts +49 -0
  389. package/src/register/esm-exports.ts +63 -0
  390. package/src/register/hooks.ts +30 -0
  391. package/src/register/index.ts +150 -0
  392. package/src/register/mapping.ts +33 -0
  393. package/src/remote/index.ts +1050 -0
  394. package/src/rtdb/crawl-snapshot.ts +118 -0
  395. package/src/rtdb/inspection.ts +137 -0
  396. package/src/rtdb/load-rules-document.ts +56 -0
  397. package/src/rtdb/rules-generation-tool.ts +38 -0
  398. package/src/rtdb/rules-json.ts +29 -0
  399. package/src/serve/activity-guard.ts +29 -0
  400. package/src/serve/activity-route.ts +164 -0
  401. package/src/serve/activity-warning.ts +26 -0
  402. package/src/serve/bridge-mount.ts +200 -0
  403. package/src/serve/bundler.ts +493 -0
  404. package/src/serve/capture-store.ts +65 -0
  405. package/src/serve/discovery.ts +199 -0
  406. package/src/serve/entries/ai.ts +292 -0
  407. package/src/serve/entries/app-backend.ts +4 -0
  408. package/src/serve/entries/app-client.ts +26 -0
  409. package/src/serve/entries/app-session-store.ts +47 -0
  410. package/src/serve/entries/app.ts +11 -0
  411. package/src/serve/entries/auth-helper-core.ts +181 -0
  412. package/src/serve/entries/auth-helper-dom.ts +134 -0
  413. package/src/serve/entries/auth-helper-runtime.ts +20 -0
  414. package/src/serve/entries/auth.ts +255 -0
  415. package/src/serve/entries/bridge-url.ts +31 -0
  416. package/src/serve/entries/database.ts +129 -0
  417. package/src/serve/entries/firestore.ts +232 -0
  418. package/src/serve/entries/init.ts +46 -0
  419. package/src/serve/entries/keepalive.ts +46 -0
  420. package/src/serve/entries/messaging-sw.ts +75 -0
  421. package/src/serve/entries/messaging.ts +96 -0
  422. package/src/serve/entries/runtime.ts +540 -0
  423. package/src/serve/entries/session-store.ts +97 -0
  424. package/src/serve/entries/storage.ts +92 -0
  425. package/src/serve/entries/tab-sync-wiring.ts +274 -0
  426. package/src/serve/entries/worker-runtime.ts +75 -0
  427. package/src/serve/init-payload.ts +42 -0
  428. package/src/serve/namespace.ts +675 -0
  429. package/src/serve/open-browser.ts +68 -0
  430. package/src/serve/rules.ts +272 -0
  431. package/src/serve/sandbox-marker.ts +31 -0
  432. package/src/serve/server.ts +428 -0
  433. package/src/serve/standalone-assets.ts +180 -0
  434. package/src/serve/state-store.ts +166 -0
  435. package/src/serve/studio/disk-project-store.ts +185 -0
  436. package/src/serve/studio/disk-workspace.ts +162 -0
  437. package/src/serve/studio/index.ts +20 -0
  438. package/src/serve/studio/routes.ts +237 -0
  439. package/src/serve/studio/store-types.ts +48 -0
  440. package/src/serve/studio/studio-storage.test.ts +289 -0
  441. package/src/serve/vite-plugin.ts +1102 -0
  442. package/src/serve/worker/activity-bootstrap.ts +24 -0
  443. package/src/serve/worker/client/admin-firestore.ts +38 -0
  444. package/src/serve/worker/client/ai.ts +124 -0
  445. package/src/serve/worker/client/auth.ts +489 -0
  446. package/src/serve/worker/client/connection.ts +202 -0
  447. package/src/serve/worker/client/core.ts +350 -0
  448. package/src/serve/worker/client/disconnect.ts +45 -0
  449. package/src/serve/worker/client/firestore-reads.ts +181 -0
  450. package/src/serve/worker/client/firestore-refs.ts +226 -0
  451. package/src/serve/worker/client/firestore-writes.ts +232 -0
  452. package/src/serve/worker/client/handles.ts +91 -0
  453. package/src/serve/worker/client/messaging.ts +119 -0
  454. package/src/serve/worker/client/presence.ts +174 -0
  455. package/src/serve/worker/client/rtdb.ts +311 -0
  456. package/src/serve/worker/client/rules.ts +63 -0
  457. package/src/serve/worker/client/service-worker-connection.ts +59 -0
  458. package/src/serve/worker/client/snapshots.ts +86 -0
  459. package/src/serve/worker/client/storage.ts +195 -0
  460. package/src/serve/worker/client/studio.ts +88 -0
  461. package/src/serve/worker/client.ts +57 -0
  462. package/src/serve/worker/durable-persistence.ts +137 -0
  463. package/src/serve/worker/entry.ts +179 -0
  464. package/src/serve/worker/host/admin-firestore.ts +88 -0
  465. package/src/serve/worker/host/connection.ts +179 -0
  466. package/src/serve/worker/host/core.ts +387 -0
  467. package/src/serve/worker/host/dispatch.ts +316 -0
  468. package/src/serve/worker/host/firestore-reads.ts +117 -0
  469. package/src/serve/worker/host/firestore-writes.ts +456 -0
  470. package/src/serve/worker/host/presence.ts +312 -0
  471. package/src/serve/worker/host/rtdb.ts +136 -0
  472. package/src/serve/worker/host/rules.ts +128 -0
  473. package/src/serve/worker/host/storage.ts +312 -0
  474. package/src/serve/worker/host/studio.ts +75 -0
  475. package/src/serve/worker/host/subscriptions.ts +238 -0
  476. package/src/serve/worker/host-ai.ts +165 -0
  477. package/src/serve/worker/host-auth.ts +469 -0
  478. package/src/serve/worker/host-context.ts +273 -0
  479. package/src/serve/worker/host-events.ts +94 -0
  480. package/src/serve/worker/host-messaging.ts +240 -0
  481. package/src/serve/worker/host.ts +47 -0
  482. package/src/serve/worker/index.ts +196 -0
  483. package/src/serve/worker/presence-timing.ts +15 -0
  484. package/src/serve/worker/protocol.ts +1112 -0
  485. package/src/serve/worker/serve-init.ts +606 -0
  486. package/src/serve/worker/service-worker-channel.ts +33 -0
  487. package/src/serve/worker/service-worker-relay.ts +86 -0
  488. package/src/serve/writer-lock.ts +53 -0
  489. package/src/verify/cases.ts +233 -0
  490. package/src/verify/fixture.ts +258 -0
  491. package/src/verify/index.ts +519 -0
  492. package/src/verify/tools.ts +108 -0
  493. package/src/version/compat-target.ts +17 -0
  494. package/src/vite.ts +17 -0
  495. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -30
  496. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -30
  497. package/dist/serve/studio-ui/assets/index-BvtQIpGn.js +0 -1211
  498. package/dist/serve/studio-ui/assets/seed-3DMedLL3.js +0 -2
@@ -1,6 +1,7 @@
1
- <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>API reference: pyric/sandbox · Pyric Studio</title><meta name="description" content="Published declarations for pyric/sandbox."><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_.DPUwShm2.css"></head> <body> <header class="studio__bar"> <div class="studio__bar-inner"> <a class="studio__brand" href="/__pyric/ui/" aria-label="Pyric Studio">Pyric</a> <nav class="studio__nav" aria-label="Studio tabs"> <a class="studio__nav-tab" href="/__pyric/ui/"> Home </a><a class="studio__nav-tab" href="/__pyric/ui/firestore"> Firestore </a><a class="studio__nav-tab" href="/__pyric/ui/auth"> Auth </a><a class="studio__nav-tab" href="/__pyric/ui/rtdb"> RTDB </a><a class="studio__nav-tab" href="/__pyric/ui/storage"> Storage </a><a class="studio__nav-tab" href="/__pyric/ui/traffic"> Traffic </a><a class="studio__nav-tab" href="/__pyric/ui/settings"> Settings </a><a class="studio__nav-tab" href="/__pyric/ui/docs" aria-current="page"> Docs </a> </nav> </div> </header> <div class="docs-shell"> <details class="docs-nav-drawer"> <summary>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> <a class="docs-nav-toplink" href="/__pyric/ui/docs/overview/"> Overview </a><details class="docs-nav-group"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Secure &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> </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/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><a class="docs-nav-toplink" href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Trust </a><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/"> Public API coverage </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> 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" open> <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-firestore-values-reference-api/"> pyric/firestore-values </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/" aria-current="page"> 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> </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-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> </details> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <a class="docs-nav-toplink" href="/__pyric/ui/docs/overview/" id="nav-group-overview"> Overview </a><details class="docs-nav-group" id="nav-group-get-started"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-build"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-secure-debug"> <summary>Secure &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> </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/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><a class="docs-nav-toplink" href="/__pyric/ui/docs/trust/versioning-and-compatibility/" id="nav-group-trust"> Trust </a><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/"> Public API coverage </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> 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" open> <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-firestore-values-reference-api/"> pyric/firestore-values </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-reference-api/" aria-current="page"> 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> </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-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> <main class="doc-main"> <nav class="docs-breadcrumbs" aria-label="Breadcrumb"> <ol> <li> <a href="/__pyric/ui/docs/api-reference/">API reference</a> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span>pyric</span> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span aria-current="page">pyric/sandbox</span> </li> </ol> </nav> <header class="api-reference-header"> <p class="api-reference-kicker">API reference</p> <h1><code>pyric/sandbox</code></h1> <p class="api-reference-meta"> 72 published symbols from <code>pyric</code> </p> <p class="api-reference-source">
1
+ <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>API reference: pyric/sandbox · Pyric Studio</title><meta name="description" content="Published declarations for pyric/sandbox."><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_.DUci-duz.css"></head> <body> <header class="studio__bar"> <div class="studio__bar-inner"> <a class="studio__brand" href="/__pyric/ui/" aria-label="Pyric Studio">Pyric</a> <nav class="studio__nav" aria-label="Studio tabs"> <a class="studio__nav-tab" href="/__pyric/ui/"> Home </a><a class="studio__nav-tab" href="/__pyric/ui/firestore"> Firestore </a><a class="studio__nav-tab" href="/__pyric/ui/auth"> Auth </a><a class="studio__nav-tab" href="/__pyric/ui/rtdb"> RTDB </a><a class="studio__nav-tab" href="/__pyric/ui/storage"> Storage </a><a class="studio__nav-tab" href="/__pyric/ui/traffic"> Traffic </a><a class="studio__nav-tab" href="/__pyric/ui/settings"> Settings </a><a class="studio__nav-tab" href="/__pyric/ui/docs" aria-current="page"> Docs </a> </nav> </div> </header> <div class="docs-shell"> <details class="docs-nav-drawer"> <summary>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> <a class="docs-nav-toplink" href="/__pyric/ui/docs/overview/"> Overview </a><details class="docs-nav-group"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group"> <summary>Secure &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> </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/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><a class="docs-nav-toplink" href="/__pyric/ui/docs/trust/versioning-and-compatibility/"> Trust </a><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/"> Public API coverage </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> 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" open> <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/" aria-current="page"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> </details> <nav class="docs-nav" aria-label="Documentation"> <div class="docs-search" data-index-url="/__pyric/ui/docs/index.json"> <div class="docs-search-field"> <svg class="docs-search-icon" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"> <path d="M11.5 11.5 15 15M13 7A6 6 0 1 1 1 7a6 6 0 0 1 12 0Z" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"></path> </svg> <input type="text" class="docs-search-input" placeholder="Search docs" aria-label="Search docs" autocomplete="off" spellcheck="false"> <kbd class="docs-search-kbd">⌘K</kbd> </div> <ul class="docs-search-results" hidden></ul> </div> <a class="docs-nav-toplink" href="/__pyric/ui/docs/overview/" id="nav-group-overview"> Overview </a><details class="docs-nav-group" id="nav-group-get-started"> <summary>Get started</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/get-started/start-building/"> Quickstart </a> </li><li> <a href="/__pyric/ui/docs/get-started/how-the-swap-works/"> How the swap works </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-build"> <summary>Build</summary> <div class="docs-nav-group-body"> <div class="docs-nav-section"> <ul> <li> <a href="/__pyric/ui/docs/build/authentication/"> Authentication </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-firestore/"> Cloud Firestore </a> </li><li> <a href="/__pyric/ui/docs/build/realtime-database/"> Realtime Database </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-storage/"> Cloud Storage </a> </li><li> <a href="/__pyric/ui/docs/build/cloud-messaging/"> Cloud Messaging </a> </li><li> <a href="/__pyric/ui/docs/build/ai-logic/"> AI Logic </a> </li> </ul> </div> </div> </details><details class="docs-nav-group" id="nav-group-secure-debug"> <summary>Secure &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> </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/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><a class="docs-nav-toplink" href="/__pyric/ui/docs/trust/versioning-and-compatibility/" id="nav-group-trust"> Trust </a><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/"> Public API coverage </a> </li><li> <a href="/__pyric/ui/docs/trust/how-we-know-it-matches-firebase/"> How we know it matches Firebase </a> </li><li> <a href="/__pyric/ui/docs/app-compat/"> App </a> </li><li> <a href="/__pyric/ui/docs/ai-compat/"> AI Logic </a> </li><li> <a href="/__pyric/ui/docs/auth-compat/"> Auth </a> </li><li> <a href="/__pyric/ui/docs/firestore-compat/"> 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" open> <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/" aria-current="page"> pyric/sandbox </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-firestore-reference-api/"> pyric/sandbox/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-sandbox-database-reference-api/"> pyric/sandbox/database </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">pyric-admin</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-admin-app-reference-api/"> pyric-admin/app </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-firestore-reference-api/"> pyric-admin/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-auth-reference-api/"> pyric-admin/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-database-reference-api/"> pyric-admin/database </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-storage-reference-api/"> pyric-admin/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-admin-messaging-reference-api/"> pyric-admin/messaging </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">create-pyric</h2> <ul> <li> <a href="/__pyric/ui/docs/create-pyric-reference-api/"> create-pyric </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/cli</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-cli-credentials-node-reference-api/"> @pyric/cli/credentials/node </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-verify-reference-api/"> @pyric/cli/verify </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-reference-api/"> @pyric/cli/conformance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-browser-reference-api/"> @pyric/cli/conformance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-conformance-docs-reference-api/"> @pyric/cli/conformance/docs </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-reference-api/"> @pyric/cli/assurance </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-assurance-browser-reference-api/"> @pyric/cli/assurance/browser </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-reference-api/"> @pyric/cli/bridge </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-bridge-client-reference-api/"> @pyric/cli/bridge/client </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-discover-reference-api/"> @pyric/cli/discover </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-vite-reference-api/"> @pyric/cli/vite </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-serve-worker-reference-api/"> @pyric/cli/serve/worker </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-remote-reference-api/"> @pyric/cli/remote </a> </li><li> <a href="/__pyric/ui/docs/pyric-cli-register-reference-api/"> @pyric/cli/register </a> </li> </ul> </div><div class="docs-nav-section"> <h2 class="docs-nav-heading">@pyric/ui</h2> <ul> <li> <a href="/__pyric/ui/docs/pyric-ui-auth-reference-api/"> @pyric/ui/auth </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-auth-hooks-reference-api/"> @pyric/ui/auth/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-primitives-reference-api/"> @pyric/ui/primitives </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-reference-api/"> @pyric/ui/firestore </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-firestore-hooks-reference-api/"> @pyric/ui/firestore/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rtdb-reference-api/"> @pyric/ui/rtdb </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-reference-api/"> @pyric/ui/storage </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-storage-hooks-reference-api/"> @pyric/ui/storage/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-reference-api/"> @pyric/ui/traffic </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-traffic-hooks-reference-api/"> @pyric/ui/traffic/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-reference-api/"> @pyric/ui/events </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-events-hooks-reference-api/"> @pyric/ui/events/hooks </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-agents-reference-api/"> @pyric/ui/agents </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-reference-api/"> @pyric/ui/rules </a> </li><li> <a href="/__pyric/ui/docs/pyric-ui-rules-hooks-reference-api/"> @pyric/ui/rules/hooks </a> </li> </ul> </div> </div> </details> </nav> <main class="doc-main"> <nav class="docs-breadcrumbs" aria-label="Breadcrumb"> <ol> <li> <a href="/__pyric/ui/docs/api-reference/">API reference</a> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span>pyric</span> </li><li> <span class="docs-breadcrumbs-sep" aria-hidden="true">/</span> <span aria-current="page">pyric/sandbox</span> </li> </ol> </nav> <header class="api-reference-header"> <p class="api-reference-kicker">API reference</p> <h1><code>pyric/sandbox</code></h1> <p class="api-reference-meta"> 73 published symbols from <code>pyric</code> </p> <p class="api-reference-source">
2
3
  Generated from the TypeScript declarations shipped at this import path.
3
- </p> </header> <article class="prose api-reference-body"> <!-- Generated from published package declarations via TypeDoc. Do not edit by hand; run bun run docs:api:generate. -->
4
+ </p> </header> <article class="prose api-reference-body"> <!-- Generated from published package declarations via TypeDoc. Rendered by the api-reference content loader. -->
4
5
  <h2 id="classes">Classes</h2>
5
6
  <p><a id="persistenceschemaerror"></a></p>
6
7
  <h3 id="persistenceschemaerror">PersistenceSchemaError</h3>
@@ -34,7 +35,7 @@ Generated from the TypeScript declarations shipped at this import path.
34
35
  <hr>
35
36
  <p><a id="sandboxcontextimpl"></a></p>
36
37
  <h3 id="sandboxcontextimpl">SandboxContextImpl</h3>
37
- <p>Identity-bearing handle on a <a href="#sandbox-3">Sandbox</a>. A
38
+ <p>Identity-bearing handle on a <a href="/docs/pyric-firestore-reference-api/#sandbox">Sandbox</a>. A
38
39
  <code>(sandbox, auth, operationContext)</code>
39
40
  tuple — cheap to create, immutable, freely shareable. Service
40
41
  factories require a <code>SandboxContext</code>; bare <code>Sandbox</code> is a type
@@ -45,7 +46,7 @@ from <code>pyric/sandbox</code> for <code>instanceof</code> routing in service
45
46
  factories; consumers don’t construct it directly.</p>
46
47
  <h4 id="implements">Implements</h4>
47
48
  <ul>
48
- <li><a href="#sandboxcontext"><code>SandboxContext</code></a></li>
49
+ <li><a href="/docs/pyric-firestore-reference-api/#sandboxcontext"><code>SandboxContext</code></a></li>
49
50
  </ul>
50
51
  <h4 id="constructors-1">Constructors</h4>
51
52
  <p><a id="constructor-1"></a></p>
@@ -87,7 +88,7 @@ factories; consumers don’t construct it directly.</p>
87
88
 
88
89
 
89
90
 
90
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>sandbox</code></td><td align="left"><a href="#sandbox-3"><code>Sandbox</code></a></td></tr><tr><td align="left"><code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>operationContext?</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td></tr></tbody></table></div>
91
+ <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>sandbox</code></td><td align="left"><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a></td></tr><tr><td align="left"><code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>operationContext?</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td></tr></tbody></table></div>
91
92
  <h6 id="returns-1">Returns</h6>
92
93
  <p><a href="#sandboxcontextimpl"><code>SandboxContextImpl</code></a></p>
93
94
  <h4 id="properties">Properties</h4>
@@ -132,7 +133,7 @@ factories; consumers don’t construct it directly.</p>
132
133
 
133
134
 
134
135
 
135
- <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="auth"></a> <code>auth</code></td><td align="left"><code>readonly</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">The identity rules evaluate under for operations through this context.</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="operationcontext"></a> <code>operationContext</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td><td align="left">Immutable provenance bound to every operation issued through this handle.</td></tr><tr><td align="left"><a id="sandbox"></a> <code>sandbox</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#sandbox-3"><code>Sandbox</code></a></td><td align="left">The data foundation this context operates against.</td></tr></tbody></table></div>
136
+ <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="auth"></a> <code>auth</code></td><td align="left"><code>readonly</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">The identity rules evaluate under for operations through this context.</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="operationcontext"></a> <code>operationContext</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td><td align="left">Immutable provenance bound to every operation issued through this handle.</td></tr><tr><td align="left"><a id="sandbox"></a> <code>sandbox</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a></td><td align="left">The data foundation this context operates against.</td></tr></tbody></table></div>
136
137
  <h4 id="methods">Methods</h4>
137
138
  <p><a id="withauth"></a></p>
138
139
  <h5 id="withauth">withAuth()</h5>
@@ -167,82 +168,15 @@ optional plan identity.</p>
167
168
 
168
169
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
169
170
  <h6 id="returns-2">Returns</h6>
170
- <p><a href="#sandboxcontext"><code>SandboxContext</code></a></p>
171
+ <p><a href="/docs/pyric-firestore-reference-api/#sandboxcontext"><code>SandboxContext</code></a></p>
171
172
  <h6 id="implementation-of">Implementation of</h6>
172
- <p><a href="#sandboxcontext"><code>SandboxContext</code></a>.<a href="#withauth-8"><code>withAuth</code></a></p>
173
- <hr>
174
- <p><a id="sandboxerror"></a></p>
175
- <h3 id="sandboxerror">SandboxError</h3>
176
- <p>Sandbox-layer error. Catch with <code>instanceof SandboxError</code> and switch
177
- on <code>code</code>. <code>denialContext</code> is populated for <code>permission-denied</code> only
178
- (and only after Slice 4 wires it through).</p>
179
- <p>Two construction forms are supported:</p>
180
- <ul>
181
- <li>Positional: <code>new SandboxError(code, message, denialContext?)</code> —
182
- the original signature, kept for backward compatibility with
183
- existing internal call sites.</li>
184
- <li>Options bag: <code>new SandboxError({ code, message, remediation? })</code> —
185
- used when attaching remediation guidance.</li>
186
- </ul>
187
- <h4 id="extends-1">Extends</h4>
188
- <ul>
189
- <li><code>Error</code></li>
190
- </ul>
191
- <h4 id="constructors-2">Constructors</h4>
192
- <p><a id="constructor-2"></a></p>
193
- <h5 id="constructor-2">Constructor</h5>
194
- <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">new</span><span style="color:#61AFEF"> SandboxError</span><span style="color:#ABB2BF">(</span></span>
195
- <span class="line"><span style="color:#E06C75"> code</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">SandboxErrorCode</span><span style="color:#ABB2BF">,</span></span>
196
- <span class="line"><span style="color:#E06C75"> message</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">,</span></span>
197
- <span class="line"><span style="color:#E06C75"> denialContext</span><span style="color:#C678DD">?:</span><span style="color:#E06C75"> DenialContext</span><span style="color:#ABB2BF">): </span><span style="color:#E06C75">SandboxError</span><span style="color:#ABB2BF">;</span></span></code></pre>
198
- <h6 id="parameters-3">Parameters</h6>
199
- <div class="table-scroll">
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>code</code></td><td align="left"><a href="#sandboxerrorcode-1"><code>SandboxErrorCode</code></a></td></tr><tr><td align="left"><code>message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>denialContext?</code></td><td align="left"><a href="#denialcontext-1"><code>DenialContext</code></a></td></tr></tbody></table></div>
221
- <h6 id="returns-3">Returns</h6>
222
- <p><a href="#sandboxerror"><code>SandboxError</code></a></p>
223
- <h6 id="overrides-1">Overrides</h6>
224
- <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:#E5C07B">Error</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">constructor</span></span></code></pre>
225
- <h5 id="constructor-3">Constructor</h5>
226
- <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">new</span><span style="color:#61AFEF"> SandboxError</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">options</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">SandboxErrorOptions</span><span style="color:#ABB2BF">): </span><span style="color:#E06C75">SandboxError</span><span style="color:#ABB2BF">;</span></span></code></pre>
227
- <h6 id="parameters-4">Parameters</h6>
228
- <div class="table-scroll">
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>options</code></td><td align="left"><code>SandboxErrorOptions</code></td></tr></tbody></table></div>
242
- <h6 id="returns-4">Returns</h6>
243
- <p><a href="#sandboxerror"><code>SandboxError</code></a></p>
244
- <h6 id="overrides-2">Overrides</h6>
245
- <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:#E5C07B">Error</span><span style="color:#ABB2BF">.</span><span style="color:#E06C75">constructor</span></span></code></pre>
173
+ <p><a href="/docs/pyric-firestore-reference-api/#sandboxcontext"><code>SandboxContext</code></a>.<a href="/docs/pyric-firestore-reference-api/#withauth-2"><code>withAuth</code></a></p>
174
+ <h2 id="interfaces">Interfaces</h2>
175
+ <p><a id="activityeventprovenance"></a></p>
176
+ <h3 id="activityeventprovenance">ActivityEventProvenance</h3>
177
+ <p>Provenance consumed only by the firestore activity diagnostics. Bundled in
178
+ one optional record so the shared operation type carries a single
179
+ activity-owned seam rather than loose per-feature fields.</p>
246
180
  <h4 id="properties-1">Properties</h4>
247
181
  <div class="table-scroll">
248
182
 
@@ -269,11 +203,11 @@ used when attaching remediation guidance.</li>
269
203
 
270
204
 
271
205
 
272
- <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="code"></a> <code>code</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#sandboxerrorcode-1"><code>SandboxErrorCode</code></a></td></tr><tr><td align="left"><a id="denialcontext"></a> <code>denialContext?</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#denialcontext-1"><code>DenialContext</code></a></td></tr><tr><td align="left"><a id="remediation"></a> <code>remediation?</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
273
- <h2 id="interfaces">Interfaces</h2>
206
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="groupkind"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code></td><td align="left">Marks worker-split operations that belong to a transaction for activity diagnostics.</td></tr><tr><td align="left"><a id="listenerid"></a> <code>listenerId?</code></td><td align="left"><code>string</code></td><td align="left">Stable host subscription identity used only by activity diagnostics.</td></tr><tr><td align="left"><a id="listenerlifecycle"></a> <code>listenerLifecycle?</code></td><td align="left"><code>"reauthorize"</code></td><td align="left">Marks lifecycle caused by transparent auth reauthorization, not app code.</td></tr></tbody></table></div>
207
+ <hr>
274
208
  <p><a id="branch"></a></p>
275
209
  <h3 id="branch">Branch</h3>
276
- <p>An isolated, in-memory experiment seeded from a <a href="#sandboxsnapshot-2">SandboxSnapshot</a>.</p>
210
+ <p>An isolated, in-memory experiment seeded from a <a href="#sandboxsnapshot">SandboxSnapshot</a>.</p>
277
211
  <p>A branch owns its own <a href="#localsandbox">LocalSandbox</a> (fully isolated from the source —
278
212
  separate <code>LocalEnvironment</code>, separate event history) plus the
279
213
  accumulated <a href="#sandboxevent">SandboxEvent</a>s applied to it via <a href="#apply">apply</a>. The
@@ -320,7 +254,7 @@ applied events are what <a href="#promote">promote</a> replays onto the target.<
320
254
 
321
255
 
322
256
 
323
- <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="base"></a> <code>base</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#sandboxsnapshot-2"><code>SandboxSnapshot</code></a></td><td align="left">The snapshot this branch was forked from. Retained so <a href="#diff">diff</a> and <a href="#promote">promote</a> can reason about the baseline.</td></tr><tr><td align="left"><a id="discarded"></a> <code>discarded</code></td><td align="left"><code>public</code></td><td align="left"><code>boolean</code></td><td align="left">Flipped by <a href="#discard">discard</a>; subsequent <a href="#apply">apply</a>/<a href="#promote">promote</a> calls throw.</td></tr><tr><td align="left"><a id="events"></a> <code>events</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#sandboxevent"><code>SandboxEvent</code></a>[]</td><td align="left">Write/op events applied to this branch since fork, in order. These are replayed onto the target by <a href="#promote">promote</a>.</td></tr><tr><td align="left"><a id="rules"></a> <code>rules</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td><td align="left">Rules the branch was forked with — carried so <a href="#promote">promote</a> can re-seed a replay target identically.</td></tr><tr><td align="left"><a id="sandbox-1"></a> <code>sandbox</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#localsandbox"><code>LocalSandbox</code></a></td><td align="left">The branch’s own sandbox. Inspect it directly (<code>branch.sandbox.snapshot()</code>) or read docs via <code>branch.sandbox.admin.getDocument(path)</code>.</td></tr></tbody></table></div>
257
+ <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="base"></a> <code>base</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#sandboxsnapshot"><code>SandboxSnapshot</code></a></td><td align="left">The snapshot this branch was forked from. Retained so <a href="#diff">diff</a> and <a href="#promote">promote</a> can reason about the baseline.</td></tr><tr><td align="left"><a id="discarded"></a> <code>discarded</code></td><td align="left"><code>public</code></td><td align="left"><code>boolean</code></td><td align="left">Flipped by <a href="#discard">discard</a>; subsequent <a href="#apply">apply</a>/<a href="#promote">promote</a> calls throw.</td></tr><tr><td align="left"><a id="events"></a> <code>events</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#sandboxevent"><code>SandboxEvent</code></a>[]</td><td align="left">Write/op events applied to this branch since fork, in order. These are replayed onto the target by <a href="#promote">promote</a>.</td></tr><tr><td align="left"><a id="rules"></a> <code>rules</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td><td align="left">Rules the branch was forked with — carried so <a href="#promote">promote</a> can re-seed a replay target identically.</td></tr><tr><td align="left"><a id="sandbox-1"></a> <code>sandbox</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#localsandbox"><code>LocalSandbox</code></a></td><td align="left">The branch’s own sandbox. Inspect it directly (<code>branch.sandbox.snapshot()</code>) or read docs via <code>branch.sandbox.admin.getDocument(path)</code>.</td></tr></tbody></table></div>
324
258
  <hr>
325
259
  <p><a id="broadcastchannellike"></a></p>
326
260
  <h3 id="broadcastchannellike">BroadcastChannelLike</h3>
@@ -341,7 +275,7 @@ renders that only care about initial data.</p>
341
275
  <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:#61AFEF">addEventListener</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">type</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">"message"</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">listener</span><span style="color:#ABB2BF">: (</span><span style="color:#E06C75;font-style:italic">ev</span><span style="color:#ABB2BF">: {</span></span>
342
276
  <span class="line"><span style="color:#E06C75"> data</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">unknown</span><span style="color:#ABB2BF">;</span></span>
343
277
  <span class="line"><span style="color:#ABB2BF">}) </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
344
- <h6 id="parameters-5">Parameters</h6>
278
+ <h6 id="parameters-3">Parameters</h6>
345
279
  <div class="table-scroll">
346
280
 
347
281
 
@@ -360,17 +294,17 @@ renders that only care about initial data.</p>
360
294
 
361
295
 
362
296
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>type</code></td><td align="left"><code>"message"</code></td></tr><tr><td align="left"><code>listener</code></td><td align="left">(<code>ev</code>: { <code>data</code>: <code>unknown</code>; }) => <code>void</code></td></tr></tbody></table></div>
363
- <h6 id="returns-5">Returns</h6>
297
+ <h6 id="returns-3">Returns</h6>
364
298
  <p><code>void</code></p>
365
299
  <p><a id="close"></a></p>
366
300
  <h5 id="close">close()</h5>
367
301
  <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:#61AFEF">close</span><span style="color:#ABB2BF">(): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
368
- <h6 id="returns-6">Returns</h6>
302
+ <h6 id="returns-4">Returns</h6>
369
303
  <p><code>void</code></p>
370
304
  <p><a id="postmessage"></a></p>
371
305
  <h5 id="postmessage">postMessage()</h5>
372
306
  <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:#61AFEF">postMessage</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">message</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">unknown</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
373
- <h6 id="parameters-6">Parameters</h6>
307
+ <h6 id="parameters-4">Parameters</h6>
374
308
  <div class="table-scroll">
375
309
 
376
310
 
@@ -385,14 +319,14 @@ renders that only care about initial data.</p>
385
319
 
386
320
 
387
321
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>message</code></td><td align="left"><code>unknown</code></td></tr></tbody></table></div>
388
- <h6 id="returns-7">Returns</h6>
322
+ <h6 id="returns-5">Returns</h6>
389
323
  <p><code>void</code></p>
390
324
  <p><a id="removeeventlistener"></a></p>
391
325
  <h5 id="removeeventlistener">removeEventListener()</h5>
392
326
  <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:#61AFEF">removeEventListener</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">type</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">"message"</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">listener</span><span style="color:#ABB2BF">: (</span><span style="color:#E06C75;font-style:italic">ev</span><span style="color:#ABB2BF">: {</span></span>
393
327
  <span class="line"><span style="color:#E06C75"> data</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">unknown</span><span style="color:#ABB2BF">;</span></span>
394
328
  <span class="line"><span style="color:#ABB2BF">}) </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
395
- <h6 id="parameters-7">Parameters</h6>
329
+ <h6 id="parameters-5">Parameters</h6>
396
330
  <div class="table-scroll">
397
331
 
398
332
 
@@ -411,10 +345,10 @@ renders that only care about initial data.</p>
411
345
 
412
346
 
413
347
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>type</code></td><td align="left"><code>"message"</code></td></tr><tr><td align="left"><code>listener</code></td><td align="left">(<code>ev</code>: { <code>data</code>: <code>unknown</code>; }) => <code>void</code></td></tr></tbody></table></div>
414
- <h6 id="returns-8">Returns</h6>
348
+ <h6 id="returns-6">Returns</h6>
415
349
  <p><code>void</code></p>
416
350
  <hr>
417
- <p><a id="denialcontext-1"></a></p>
351
+ <p><a id="denialcontext"></a></p>
418
352
  <h3 id="denialcontext">DenialContext</h3>
419
353
  <p>Structured denial context emitted alongside a <code>permission-denied</code>
420
354
  error. Real Firebase strips this server-side for security; the
@@ -511,12 +445,37 @@ reference traces.</p>
511
445
 
512
446
 
513
447
 
514
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="auth-1"></a> <code>auth?</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Auth identity that was active when the denial fired.</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="failedfields"></a> <code>failedFields?</code></td><td align="left"><code>string</code>[]</td><td align="left">Field paths in <code>request.resource.data</code> that the rule referenced and that failed.</td></tr><tr><td align="left"><a id="reasons"></a> <code>reasons?</code></td><td align="left"><code>string</code>[]</td><td align="left">Raw simulator reasoning lines (the underlying engine’s <code>debugMessages</code>). Always present on <code>permission-denied</code>. Stable enough for log surfacing; not stable as machine-parseable data.</td></tr><tr><td align="left"><a id="request"></a> <code>request?</code></td><td align="left">{ <code>method</code>: <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code> | <code>"delete"</code>; <code>path</code>: <code>string</code>; <code>resourceData?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; }</td><td align="left">Eval-time request shape — what the rule saw on <code>request.*</code>. Lets callers render a “why did this deny” frame (auth, method, path, <code>request.resource.data</code> with sentinels resolved) without re-deriving any of it from out-of-band state.</td></tr><tr><td align="left"><code>request.method</code></td><td align="left"><code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code> | <code>"delete"</code></td><td align="left">-</td></tr><tr><td align="left"><code>request.path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">The user’s proposed <code>request.resource.data</code> — pre-resolution. <code>FieldValue.*</code> sentinels are preserved as their marker shapes (<code>{ __type: 'serverTimestamp' }</code>, etc.). The rule engine evaluated against the resolved form; what surfaces here is the caller’s INTENT so consumers see what they tried to write. Absent for reads (no proposed write) and for <code>delete</code> (no payload).</td></tr><tr><td align="left"><a id="resource"></a> <code>resource?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">Eval-time existing-document snapshot — what the rule saw on <code>resource.data</code>. <code>null</code> data with <code>exists: false</code> mirrors how the rule sees an absent doc. Absent for collection ops (<code>list</code>).</td></tr><tr><td align="left"><code>resource.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>resource.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="rule"></a> <code>rule?</code></td><td align="left">{ <code>expression</code>: <code>string</code>; <code>line</code>: <code>number</code>; }</td><td align="left">The rule whose evaluation produced the denial. Best effort; may be absent until source positions land in the AST.</td></tr><tr><td align="left"><code>rule.expression</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rule.line</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr></tbody></table></div>
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+
458
+
459
+
460
+
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="auth-1"></a> <code>auth?</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Auth identity that was active when the denial fired.</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="failedfields"></a> <code>failedFields?</code></td><td align="left"><code>string</code>[]</td><td align="left">Field paths in <code>request.resource.data</code> that the rule referenced and that failed.</td></tr><tr><td align="left"><a id="query"></a> <code>query?</code></td><td align="left">{ <code>limit?</code>: <code>number</code>; <code>offset?</code>: <code>number</code>; <code>orderBy?</code>: <code>string</code>; <code>where?</code>: { <code>field</code>: <code>string</code>; <code>op</code>: <code>string</code>; <code>value</code>: <code>string</code> | <code>number</code> | <code>boolean</code>; }[]; }</td><td align="left">Machine-readable descriptor of the denied query’s where/orderBy/limit shape (RULES-B11). Populated when a <code>list</code>/query is denied as statically unprovable (“rules are not filters”), so consumers can render the exact query the engine rejected without re-deriving it. Absent for single-doc and non-query denials.</td></tr><tr><td align="left"><code>query.limit?</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><code>query.offset?</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><code>query.orderBy?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>query.where?</code></td><td align="left">{ <code>field</code>: <code>string</code>; <code>op</code>: <code>string</code>; <code>value</code>: <code>string</code> | <code>number</code> | <code>boolean</code>; }[]</td><td align="left">-</td></tr><tr><td align="left"><a id="reasons"></a> <code>reasons?</code></td><td align="left"><code>string</code>[]</td><td align="left">Raw simulator reasoning lines (the underlying engine’s <code>debugMessages</code>). Always present on <code>permission-denied</code>. Stable enough for log surfacing; not stable as machine-parseable data.</td></tr><tr><td align="left"><a id="request"></a> <code>request?</code></td><td align="left">{ <code>method</code>: <code>"delete"</code> | <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code>; <code>path</code>: <code>string</code>; <code>resourceData?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; }</td><td align="left">Eval-time request shape — what the rule saw on <code>request.*</code>. Lets callers render a “why did this deny” frame (auth, method, path, <code>request.resource.data</code> with sentinels resolved) without re-deriving any of it from out-of-band state.</td></tr><tr><td align="left"><code>request.method</code></td><td align="left"><code>"delete"</code> | <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code></td><td align="left">-</td></tr><tr><td align="left"><code>request.path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">The user’s proposed <code>request.resource.data</code> — pre-resolution. <code>FieldValue.*</code> sentinels are preserved as their marker shapes (<code>{ __type: 'serverTimestamp' }</code>, etc.). The rule engine evaluated against the resolved form; what surfaces here is the caller’s INTENT so consumers see what they tried to write. Absent for reads (no proposed write) and for <code>delete</code> (no payload).</td></tr><tr><td align="left"><a id="resource"></a> <code>resource?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">Eval-time existing-document snapshot — what the rule saw on <code>resource.data</code>. <code>null</code> data with <code>exists: false</code> mirrors how the rule sees an absent doc. Absent for collection ops (<code>list</code>).</td></tr><tr><td align="left"><code>resource.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>resource.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="rule"></a> <code>rule?</code></td><td align="left">{ <code>expression</code>: <code>string</code>; <code>line</code>: <code>number</code>; }</td><td align="left">The rule whose evaluation produced the denial. Best effort; may be absent until source positions land in the AST.</td></tr><tr><td align="left"><code>rule.expression</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rule.line</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr></tbody></table></div>
515
474
  <hr>
516
475
  <p><a id="denialevent"></a></p>
517
476
  <h3 id="denialevent">DenialEvent</h3>
518
477
  <p>Eval-time payload emitted to Sandbox.onDenial subscribers.</p>
519
- <p>Mirrors the structured fields <a href="#denialcontext-1">DenialContext</a> carries (<code>request</code></p>
478
+ <p>Mirrors the structured fields <a href="#denialcontext">DenialContext</a> carries (<code>request</code></p>
520
479
  <ul>
521
480
  <li><code>resource</code> + <code>reasons</code> + <code>auth</code>) so a host environment that wants
522
481
  to surface denials independent of try/catch behavior gets the same
@@ -584,7 +543,7 @@ frame either way.</li>
584
543
 
585
544
 
586
545
 
587
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="auth-2"></a> <code>auth?</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="code-1"></a> <code>code</code></td><td align="left"><code>"permission-denied"</code></td></tr><tr><td align="left"><a id="message"></a> <code>message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="reasons-1"></a> <code>reasons?</code></td><td align="left"><code>string</code>[]</td></tr><tr><td align="left"><a id="request-1"></a> <code>request?</code></td><td align="left">{ <code>method</code>: <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code> | <code>"delete"</code>; <code>path</code>: <code>string</code>; <code>resourceData?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; }</td></tr><tr><td align="left"><code>request.method</code></td><td align="left"><code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code> | <code>"delete"</code></td></tr><tr><td align="left"><code>request.path</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="resource-1"></a> <code>resource?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td></tr><tr><td align="left"><code>resource.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>resource.exists</code></td><td align="left"><code>boolean</code></td></tr></tbody></table></div>
546
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="auth-2"></a> <code>auth?</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="code"></a> <code>code</code></td><td align="left"><code>"permission-denied"</code></td></tr><tr><td align="left"><a id="message"></a> <code>message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="reasons-1"></a> <code>reasons?</code></td><td align="left"><code>string</code>[]</td></tr><tr><td align="left"><a id="request-1"></a> <code>request?</code></td><td align="left">{ <code>method</code>: <code>"delete"</code> | <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code>; <code>path</code>: <code>string</code>; <code>resourceData?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; }</td></tr><tr><td align="left"><code>request.method</code></td><td align="left"><code>"delete"</code> | <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code></td></tr><tr><td align="left"><code>request.path</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="resource-1"></a> <code>resource?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td></tr><tr><td align="left"><code>resource.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>resource.exists</code></td><td align="left"><code>boolean</code></td></tr></tbody></table></div>
588
547
  <hr>
589
548
  <p><a id="eventprovenance"></a></p>
590
549
  <h3 id="eventprovenance">EventProvenance</h3>
@@ -626,7 +585,12 @@ consumers migrate to the canonical <code>operationContext</code>.</p>
626
585
 
627
586
 
628
587
 
629
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="actor"></a> <code>actor?</code></td><td align="left"><a href="#eventactor"><code>EventActor</code></a></td><td align="left">-</td></tr><tr><td align="left"><a id="authlens"></a> <code>authLens?</code></td><td align="left"><a href="#authlens-2"><code>AuthLens</code></a></td><td align="left">-</td></tr><tr><td align="left"><a id="operationcontext-1"></a> <code>operationContext?</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td><td align="left">-</td></tr><tr><td align="left"><a id="planid"></a> <code>planId?</code></td><td align="left"><code>string</code></td><td align="left">Set when the op is part of an agent plan.</td></tr><tr><td align="left"><a id="service"></a> <code>service?</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td><td align="left">-</td></tr></tbody></table></div>
588
+
589
+
590
+
591
+
592
+
593
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="activity"></a> <code>activity?</code></td><td align="left"><a href="#activityeventprovenance"><code>ActivityEventProvenance</code></a></td><td align="left">Firestore activity-diagnostics provenance; absent outside that flow.</td></tr><tr><td align="left"><a id="actor"></a> <code>actor?</code></td><td align="left"><a href="#eventactor"><code>EventActor</code></a></td><td align="left">-</td></tr><tr><td align="left"><a id="authlens"></a> <code>authLens?</code></td><td align="left"><a href="#authlens-2"><code>AuthLens</code></a></td><td align="left">-</td></tr><tr><td align="left"><a id="operationcontext-1"></a> <code>operationContext?</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td><td align="left">-</td></tr><tr><td align="left"><a id="planid"></a> <code>planId?</code></td><td align="left"><code>string</code></td><td align="left">Set when the op is part of an agent plan.</td></tr><tr><td align="left"><a id="service"></a> <code>service?</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td><td align="left">-</td></tr></tbody></table></div>
630
594
  <hr>
631
595
  <p><a id="listenerlifecycleevent"></a></p>
632
596
  <h3 id="listenerlifecycleevent">ListenerLifecycleEvent</h3>
@@ -704,17 +668,17 @@ on the errored phase only.</p>
704
668
 
705
669
 
706
670
 
707
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-3"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="error"></a> <code>error?</code></td><td align="left">{ <code>code</code>: <code>"permission-denied"</code>; <code>message</code>: <code>string</code>; <code>reasons?</code>: <code>string</code>[]; }</td><td align="left">Populated on <code>listener_errored</code> only.</td></tr><tr><td align="left"><code>error.code</code></td><td align="left"><code>"permission-denied"</code></td><td align="left">-</td></tr><tr><td align="left"><code>error.message</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>error.reasons?</code></td><td align="left"><code>string</code>[]</td><td align="left">-</td></tr><tr><td align="left"><a id="id"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind"></a> <code>kind</code></td><td align="left"><code>"listener_attach"</code> | <code>"listener_detach"</code> | <code>"listener_errored"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="listenerid"></a> <code>listenerId</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="target"></a> <code>target</code></td><td align="left">| { <code>kind</code>: <code>"doc"</code>; <code>path</code>: <code>string</code>; } | { <code>collection</code>: <code>string</code>; <code>kind</code>: <code>"query"</code>; }</td><td align="left">-</td></tr></tbody></table></div>
671
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-3"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="error"></a> <code>error?</code></td><td align="left">{ <code>code</code>: <code>"permission-denied"</code>; <code>message</code>: <code>string</code>; <code>reasons?</code>: <code>string</code>[]; }</td><td align="left">Populated on <code>listener_errored</code> only.</td></tr><tr><td align="left"><code>error.code</code></td><td align="left"><code>"permission-denied"</code></td><td align="left">-</td></tr><tr><td align="left"><code>error.message</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>error.reasons?</code></td><td align="left"><code>string</code>[]</td><td align="left">-</td></tr><tr><td align="left"><a id="id"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind"></a> <code>kind</code></td><td align="left"><code>"listener_attach"</code> | <code>"listener_detach"</code> | <code>"listener_errored"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="listenerid-1"></a> <code>listenerId</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="target"></a> <code>target</code></td><td align="left">| { <code>kind</code>: <code>"doc"</code>; <code>path</code>: <code>string</code>; } | { <code>collection</code>: <code>string</code>; <code>kind</code>: <code>"query"</code>; <code>query?</code>: <code>unknown</code>; }</td><td align="left">-</td></tr></tbody></table></div>
708
672
  <hr>
709
673
  <p><a id="localsandbox"></a></p>
710
674
  <h3 id="localsandbox">LocalSandbox</h3>
711
675
  <p>An in-process sandbox created by <a href="#initializesandbox">initializeSandbox</a>.</p>
712
676
  <p>Service controls whose implementation requires synchronous access to local
713
- state accept this type. Remote worker handles remain <a href="#sandbox-3">Sandbox</a>s, but
677
+ state accept this type. Remote worker handles remain <a href="/docs/pyric-firestore-reference-api/#sandbox">Sandbox</a>s, but
714
678
  are deliberately not assignable to this local-only interface.</p>
715
- <h4 id="extends-2">Extends</h4>
679
+ <h4 id="extends-1">Extends</h4>
716
680
  <ul>
717
- <li><a href="#sandbox-3"><code>Sandbox</code></a></li>
681
+ <li><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a></li>
718
682
  </ul>
719
683
  <h4 id="properties-7">Properties</h4>
720
684
  <div class="table-scroll">
@@ -758,7 +722,7 @@ are deliberately not assignable to this local-only interface.</p>
758
722
 
759
723
 
760
724
 
761
- <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="local_sandbox"></a> <code>[LOCAL_SANDBOX]</code></td><td align="left"><code>readonly</code></td><td align="left"><code>true</code></td><td align="left">-</td></tr><tr><td align="left"><a id="admin"></a> <code>admin</code></td><td align="left"><code>readonly</code></td><td align="left"><code>SandboxAdmin</code></td><td align="left">Admin-plane access (rule-bypass reads). Identity-agnostic by design — admin reads aren’t gated on auth, so they live on the sandbox, not on a context. See SandboxAdmin.</td></tr><tr><td align="left"><a id="currentuser"></a> <code>currentUser</code></td><td align="left"><code>public</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Current authenticated user across the sandbox. Mutated by <code>pyric/auth</code>’s <code>signInAnonymously</code> / <code>signInWithEmailAndPassword</code> / <code>signOut</code> / <code>sandbox.setUser</code>. Read per-call by service factories (e.g. a future <code>getFirestore(sandbox)</code> overload) so they see auth state changes without re-binding handles. Defaults to <code>null</code> (anonymous / signed out). <strong>Independent of <code>withAuth({uid})</code></strong> — <code>withAuth</code> still produces a frozen <a href="#sandboxcontext">SandboxContext</a> that carries its own identity for the runner’s test code (the existing pattern: explicit identity per service call). <code>currentUser</code> exists for the <code>pyric/auth</code> mirror, where consumer app code drives identity through a stateful <code>Auth</code> handle rather than naming it per call.</td></tr><tr><td align="left"><code>currentUser.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>currentUser.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
725
+ <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="local_sandbox"></a> <code>[LOCAL_SANDBOX]</code></td><td align="left"><code>readonly</code></td><td align="left"><code>true</code></td><td align="left">-</td></tr><tr><td align="left"><a id="admin"></a> <code>admin</code></td><td align="left"><code>readonly</code></td><td align="left"><code>SandboxAdmin</code></td><td align="left">Admin-plane access (rule-bypass reads). Identity-agnostic by design — admin reads aren’t gated on auth, so they live on the sandbox, not on a context. See SandboxAdmin.</td></tr><tr><td align="left"><a id="currentuser"></a> <code>currentUser</code></td><td align="left"><code>public</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Current authenticated user across the sandbox. Mutated by <code>pyric/auth</code>’s <code>signInAnonymously</code> / <code>signInWithEmailAndPassword</code> / <code>signOut</code> / <code>sandbox.setUser</code>. Read per-call by service factories (e.g. a future <code>getFirestore(sandbox)</code> overload) so they see auth state changes without re-binding handles. Defaults to <code>null</code> (anonymous / signed out). <strong>Independent of <code>withAuth({uid})</code></strong> — <code>withAuth</code> still produces a frozen <a href="/docs/pyric-firestore-reference-api/#sandboxcontext">SandboxContext</a> that carries its own identity for the runner’s test code (the existing pattern: explicit identity per service call). <code>currentUser</code> exists for the <code>pyric/auth</code> mirror, where consumer app code drives identity through a stateful <code>Auth</code> handle rather than naming it per call.</td></tr><tr><td align="left"><code>currentUser.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>currentUser.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
762
726
  <h4 id="methods-2">Methods</h4>
763
727
  <p><a id="clearpersistence"></a></p>
764
728
  <h5 id="clearpersistence">clearPersistence()</h5>
@@ -767,10 +731,10 @@ are deliberately not assignable to this local-only interface.</p>
767
731
  is left intact — call <code>reset()</code> if you want both. Useful for
768
732
  “sign out and forget” flows.</p>
769
733
  <p>No-op when persistence is not enabled.</p>
770
- <h6 id="returns-9">Returns</h6>
734
+ <h6 id="returns-7">Returns</h6>
771
735
  <p><code>Promise</code>&#x3C;<code>void</code>></p>
772
736
  <h6 id="inherited-from">Inherited from</h6>
773
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#clearpersistence-4"><code>clearPersistence</code></a></p>
737
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#clearpersistence"><code>clearPersistence</code></a></p>
774
738
  <p><a id="dispose"></a></p>
775
739
  <h5 id="dispose">dispose()</h5>
776
740
  <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:#61AFEF">dispose</span><span style="color:#ABB2BF">(): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -779,10 +743,10 @@ replacing it. Use this when you’re about to discard the sandbox
779
743
  itself (e.g. <code>runner.reseed()</code> builds a fresh sandbox rather than
780
744
  calling <code>reset()</code>) and want to drop callback references on the
781
745
  outgoing instance defensively. Idempotent. Does not touch data.</p>
782
- <h6 id="returns-10">Returns</h6>
746
+ <h6 id="returns-8">Returns</h6>
783
747
  <p><code>void</code></p>
784
748
  <h6 id="inherited-from-1">Inherited from</h6>
785
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#dispose-6"><code>dispose</code></a></p>
749
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#dispose"><code>dispose</code></a></p>
786
750
  <p><a id="enablepersistence"></a></p>
787
751
  <h5 id="enablepersistence">enablePersistence()</h5>
788
752
  <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:#61AFEF">enablePersistence</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">options</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">SandboxPersistenceOptions</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -803,7 +767,7 @@ flush on <code>beforeunload</code> so a page navigation doesn’t lose the
803
767
  tail of the debounce window.</p>
804
768
  <p>See <a href="#sandboxpersistenceoptions">SandboxPersistenceOptions</a> for backend selection and
805
769
  tuning.</p>
806
- <h6 id="parameters-8">Parameters</h6>
770
+ <h6 id="parameters-6">Parameters</h6>
807
771
  <div class="table-scroll">
808
772
 
809
773
 
@@ -818,10 +782,10 @@ tuning.</p>
818
782
 
819
783
 
820
784
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>options</code></td><td align="left"><a href="#sandboxpersistenceoptions"><code>SandboxPersistenceOptions</code></a></td></tr></tbody></table></div>
821
- <h6 id="returns-11">Returns</h6>
785
+ <h6 id="returns-9">Returns</h6>
822
786
  <p><code>Promise</code>&#x3C;<code>void</code>></p>
823
787
  <h6 id="inherited-from-2">Inherited from</h6>
824
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#enablepersistence-4"><code>enablePersistence</code></a></p>
788
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#enablepersistence"><code>enablePersistence</code></a></p>
825
789
  <p><a id="enabletabsync"></a></p>
826
790
  <h5 id="enabletabsync">enableTabSync()</h5>
827
791
  <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:#61AFEF">enableTabSync</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">options</span><span style="color:#C678DD">?:</span><span style="color:#E06C75"> TabSyncOptions</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -839,7 +803,7 @@ produce last-write-wins divergence — there is no conflict resolution.
839
803
  The intended model is one active writer (one user, one tab) with
840
804
  observers in other tabs; this covers the overwhelming majority of
841
805
  local development scenarios.</p>
842
- <h6 id="parameters-9">Parameters</h6>
806
+ <h6 id="parameters-7">Parameters</h6>
843
807
  <div class="table-scroll">
844
808
 
845
809
 
@@ -854,9 +818,9 @@ local development scenarios.</p>
854
818
 
855
819
 
856
820
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>options?</code></td><td align="left"><a href="#tabsyncoptions"><code>TabSyncOptions</code></a></td></tr></tbody></table></div>
857
- <h6 id="returns-12">Returns</h6>
821
+ <h6 id="returns-10">Returns</h6>
858
822
  <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
859
- <h6 id="returns-13">Returns</h6>
823
+ <h6 id="returns-11">Returns</h6>
860
824
  <p><code>void</code></p>
861
825
  <h6 id="see">See</h6>
862
826
  <p><a href="#tabsyncoptions">TabSyncOptions</a> for channel injection (tests) and originId.</p>
@@ -867,7 +831,7 @@ local development scenarios.</p>
867
831
  <span class="line"><span style="color:#7F848E;font-style:italic">// Later, to stop syncing:</span></span>
868
832
  <span class="line"><span style="color:#61AFEF">disableSync</span><span style="color:#ABB2BF">();</span></span></code></pre>
869
833
  <h6 id="inherited-from-3">Inherited from</h6>
870
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#enabletabsync-4"><code>enableTabSync</code></a></p>
834
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#enabletabsync"><code>enableTabSync</code></a></p>
871
835
  <p><a id="flush"></a></p>
872
836
  <h5 id="flush">flush()</h5>
873
837
  <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:#61AFEF">flush</span><span style="color:#ABB2BF">(): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -876,10 +840,10 @@ Useful before a manual navigation, or in tests that need
876
840
  deterministic ordering against the debounce window. Resolves once
877
841
  the write hits the backend.</p>
878
842
  <p>Throws if persistence is not enabled.</p>
879
- <h6 id="returns-14">Returns</h6>
843
+ <h6 id="returns-12">Returns</h6>
880
844
  <p><code>Promise</code>&#x3C;<code>void</code>></p>
881
845
  <h6 id="inherited-from-4">Inherited from</h6>
882
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#flush-6"><code>flush</code></a></p>
846
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#flush"><code>flush</code></a></p>
883
847
  <p><a id="history"></a></p>
884
848
  <h5 id="history">history()</h5>
885
849
  <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:#61AFEF">history</span><span style="color:#ABB2BF">(): </span><span style="color:#E06C75">SandboxEvent</span><span style="color:#ABB2BF">[];</span></span></code></pre>
@@ -888,33 +852,33 @@ the last <code>reset()</code>. Returns a defensive copy.</p>
888
852
  <p>Use this for replay: hand the array to <code>replay(events, rules)</code>
889
853
  from <code>pyric/sandbox</code> and the engine re-issues every
890
854
  captured write against a fresh sandbox.</p>
891
- <p>Unlike <a href="#onevent-4">onEvent</a> (live stream from the moment of subscribe),
855
+ <p>Unlike <a href="/docs/pyric-firestore-reference-api/#onevent">onEvent</a> (live stream from the moment of subscribe),
892
856
  <code>history()</code> returns <em>every</em> event the sandbox has seen — useful
893
857
  for consumers that attach late (e.g., loading a saved session
894
858
  before subscribing) or that need a snapshot at a particular moment.</p>
895
859
  <p><code>reset()</code> and <code>dispose()</code> each append a closing <code>session_boundary</code>
896
860
  event; <code>reset()</code> then clears the history. Consumers that took a
897
861
  snapshot <em>before</em> reset retain the boundary in their copy.</p>
898
- <h6 id="returns-15">Returns</h6>
862
+ <h6 id="returns-13">Returns</h6>
899
863
  <p><a href="#sandboxevent"><code>SandboxEvent</code></a>[]</p>
900
864
  <h6 id="inherited-from-5">Inherited from</h6>
901
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#history-4"><code>history</code></a></p>
865
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#history"><code>history</code></a></p>
902
866
  <p><a id="loadsnapshot"></a></p>
903
867
  <h5 id="loadsnapshot">loadSnapshot()</h5>
904
868
  <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:#61AFEF">loadSnapshot</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">data</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">SandboxSnapshot</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
905
- <p>CLOBBER-restore the sandbox’s entire state from a prior <a href="#snapshot-6">snapshot</a>:
869
+ <p>CLOBBER-restore the sandbox’s entire state from a prior <a href="/docs/pyric-firestore-reference-api/#snapshot">snapshot</a>:
906
870
  <code>reset()</code> (clears firestore + the signed-in session), then rebuild firestore
907
871
  from <code>data</code> and restore each registered service. This is a TOTAL replace —
908
872
  documents absent from <code>data</code> do NOT survive — and is the counterpart to
909
- <a href="#snapshot-6">snapshot</a>. It is what makes “transfer (clobber) one instance’s data
873
+ <a href="/docs/pyric-firestore-reference-api/#snapshot">snapshot</a>. It is what makes “transfer (clobber) one instance’s data
910
874
  into another” and named-branch switching possible.</p>
911
875
  <p>Fires a <code>session_boundary</code> (reset phase), re-evaluates live listeners against
912
876
  the loaded state, and the next persistence flush writes the loaded state.
913
877
  Services present in <code>data</code> but not currently registered are skipped (a
914
- snapshot taken via <a href="#snapshot-6">snapshot</a> always includes every registered
878
+ snapshot taken via <a href="/docs/pyric-firestore-reference-api/#snapshot">snapshot</a> always includes every registered
915
879
  service, so this only affects cross-instance imports from a sandbox that had
916
880
  a service this one lacks).</p>
917
- <h6 id="parameters-10">Parameters</h6>
881
+ <h6 id="parameters-8">Parameters</h6>
918
882
  <div class="table-scroll">
919
883
 
920
884
 
@@ -928,11 +892,11 @@ a service this one lacks).</p>
928
892
 
929
893
 
930
894
 
931
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>data</code></td><td align="left"><a href="#sandboxsnapshot-2"><code>SandboxSnapshot</code></a></td></tr></tbody></table></div>
932
- <h6 id="returns-16">Returns</h6>
895
+ <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>data</code></td><td align="left"><a href="#sandboxsnapshot"><code>SandboxSnapshot</code></a></td></tr></tbody></table></div>
896
+ <h6 id="returns-14">Returns</h6>
933
897
  <p><code>void</code></p>
934
898
  <h6 id="inherited-from-6">Inherited from</h6>
935
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#loadsnapshot-4"><code>loadSnapshot</code></a></p>
899
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#loadsnapshot"><code>loadSnapshot</code></a></p>
936
900
  <p><a id="oncurrentuserchanged"></a></p>
937
901
  <h5 id="oncurrentuserchanged">onCurrentUserChanged()</h5>
938
902
  <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:#61AFEF">onCurrentUserChanged</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">cb</span><span style="color:#ABB2BF">: (</span><span style="color:#E06C75;font-style:italic">user</span><span style="color:#ABB2BF">: {</span></span>
@@ -948,7 +912,7 @@ the env.</p>
948
912
  <p>Returns an unsubscribe function. Listener errors are swallowed —
949
913
  subscribers are observational, the sandbox does not propagate
950
914
  their errors.</p>
951
- <h6 id="parameters-11">Parameters</h6>
915
+ <h6 id="parameters-9">Parameters</h6>
952
916
  <div class="table-scroll">
953
917
 
954
918
 
@@ -963,12 +927,12 @@ their errors.</p>
963
927
 
964
928
 
965
929
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>cb</code></td><td align="left">(<code>user</code>: { <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }) => <code>void</code></td></tr></tbody></table></div>
966
- <h6 id="returns-17">Returns</h6>
930
+ <h6 id="returns-15">Returns</h6>
967
931
  <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
968
- <h6 id="returns-18">Returns</h6>
932
+ <h6 id="returns-16">Returns</h6>
969
933
  <p><code>void</code></p>
970
934
  <h6 id="inherited-from-7">Inherited from</h6>
971
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#oncurrentuserchanged-4"><code>onCurrentUserChanged</code></a></p>
935
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#oncurrentuserchanged"><code>onCurrentUserChanged</code></a></p>
972
936
  <p><a id="onevent"></a></p>
973
937
  <h5 id="onevent">onEvent()</h5>
974
938
  <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:#61AFEF">onEvent</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">cb</span><span style="color:#ABB2BF">: (</span><span style="color:#E06C75;font-style:italic">event</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">SandboxEvent</span><span style="color:#ABB2BF">) </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -992,7 +956,7 @@ faulty subscriber can’t change rule semantics or hide other events.
992
956
  Both synchronous throws and rejected Promises from async callbacks
993
957
  are silently discarded — subscribers are <strong>observational</strong>, the
994
958
  sandbox doesn’t await them and doesn’t propagate their errors.</p>
995
- <h6 id="parameters-12">Parameters</h6>
959
+ <h6 id="parameters-10">Parameters</h6>
996
960
  <div class="table-scroll">
997
961
 
998
962
 
@@ -1007,12 +971,12 @@ sandbox doesn’t await them and doesn’t propagate their errors.</p>
1007
971
 
1008
972
 
1009
973
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>cb</code></td><td align="left">(<code>event</code>: <a href="#sandboxevent"><code>SandboxEvent</code></a>) => <code>void</code></td></tr></tbody></table></div>
1010
- <h6 id="returns-19">Returns</h6>
974
+ <h6 id="returns-17">Returns</h6>
1011
975
  <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
1012
- <h6 id="returns-20">Returns</h6>
976
+ <h6 id="returns-18">Returns</h6>
1013
977
  <p><code>void</code></p>
1014
978
  <h6 id="inherited-from-8">Inherited from</h6>
1015
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#onevent-4"><code>onEvent</code></a></p>
979
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#onevent"><code>onEvent</code></a></p>
1016
980
  <p><a id="registerpersistableservice"></a></p>
1017
981
  <h5 id="registerpersistableservice">registerPersistableService()</h5>
1018
982
  <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:#61AFEF">registerPersistableService</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">name</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">hooks</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">PersistableService</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1032,7 +996,7 @@ registration is a caller bug, not a no-op.</p>
1032
996
  <p><strong>Advanced / internal API.</strong> Service packages (auth, storage) call
1033
997
  this when they first attach to a sandbox. Consumer app code should
1034
998
  not need to call this directly.</p>
1035
- <h6 id="parameters-13">Parameters</h6>
999
+ <h6 id="parameters-11">Parameters</h6>
1036
1000
  <div class="table-scroll">
1037
1001
 
1038
1002
 
@@ -1051,12 +1015,12 @@ not need to call this directly.</p>
1051
1015
 
1052
1016
 
1053
1017
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>name</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>hooks</code></td><td align="left"><a href="#persistableservice"><code>PersistableService</code></a></td></tr></tbody></table></div>
1054
- <h6 id="returns-21">Returns</h6>
1018
+ <h6 id="returns-19">Returns</h6>
1055
1019
  <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
1056
- <h6 id="returns-22">Returns</h6>
1020
+ <h6 id="returns-20">Returns</h6>
1057
1021
  <p><code>void</code></p>
1058
1022
  <h6 id="inherited-from-9">Inherited from</h6>
1059
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#registerpersistableservice-4"><code>registerPersistableService</code></a></p>
1023
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#registerpersistableservice"><code>registerPersistableService</code></a></p>
1060
1024
  <p><a id="reset"></a></p>
1061
1025
  <h5 id="reset">reset()</h5>
1062
1026
  <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:#61AFEF">reset</span><span style="color:#ABB2BF">(): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1067,12 +1031,35 @@ the swap — they can’t survive because their target docs have been
1067
1031
  wiped. <code>onEvent</code> subscribers DO survive — the registry lives on
1068
1032
  the sandbox, and a <code>session_boundary</code> event with <code>phase: 'reset'</code>
1069
1033
  fires before the swap so subscribers know the rollover happened.
1070
- Existing <a href="#sandboxcontext">SandboxContext</a>s continue to work — their sandbox
1034
+ Existing <a href="/docs/pyric-firestore-reference-api/#sandboxcontext">SandboxContext</a>s continue to work — their sandbox
1071
1035
  reference is stable; subsequent operations resolve to the new env.</p>
1072
- <h6 id="returns-23">Returns</h6>
1036
+ <h6 id="returns-21">Returns</h6>
1073
1037
  <p><code>void</code></p>
1074
1038
  <h6 id="inherited-from-10">Inherited from</h6>
1075
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#reset-4"><code>reset</code></a></p>
1039
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#reset"><code>reset</code></a></p>
1040
+ <p><a id="resetall"></a></p>
1041
+ <h5 id="resetall">resetAll()</h5>
1042
+ <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:#61AFEF">resetAll</span><span style="color:#ABB2BF">(): </span><span style="color:#E5C07B">Promise</span><span style="color:#ABB2BF">&#x3C;{</span></span>
1043
+ <span class="line"><span style="color:#E06C75"> errors</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">string</span><span style="color:#ABB2BF">[];</span></span>
1044
+ <span class="line"><span style="color:#ABB2BF">}>;</span></span></code></pre>
1045
+ <p>Reset the WHOLE sandbox: <a href="/docs/pyric-firestore-reference-api/#reset">reset</a> (Firestore env + signed-in
1046
+ session), then clear every registered persistable service that
1047
+ provides a <a href="#reset-2">PersistableService.reset</a> hook — auth users, the
1048
+ RTDB tree, storage objects. This is the one sandbox-owned “wipe
1049
+ everything” path: because it iterates the service registry, a new
1050
+ service that registers with a <code>reset</code> hook is cleared automatically,
1051
+ and a consumer (Pyric Studio’s reset) cannot forget one.</p>
1052
+ <p>Service resets may be async (storage clears IndexedDB stores); the
1053
+ returned promise resolves when every service has finished clearing.
1054
+ A service whose <code>reset</code> throws is isolated (others still clear) and
1055
+ REPORTED in the returned <code>errors</code> (as <code>name: message</code>) — a reset that
1056
+ leaves data behind must never look successful to the caller.</p>
1057
+ <h6 id="returns-22">Returns</h6>
1058
+ <p><code>Promise</code>&#x3C;{
1059
+ <code>errors</code>: <code>string</code>[];
1060
+ }></p>
1061
+ <h6 id="inherited-from-11">Inherited from</h6>
1062
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#resetall"><code>resetAll</code></a></p>
1076
1063
  <p><a id="runwithprovenance"></a></p>
1077
1064
  <h5 id="runwithprovenance">runWithProvenance()?</h5>
1078
1065
  <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:#E06C75">optional</span><span style="color:#61AFEF"> runWithProvenance</span><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E5C07B">T</span><span style="color:#ABB2BF">>(</span><span style="color:#E06C75">provenance</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">EventProvenance</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">fn</span><span style="color:#ABB2BF">: () </span><span style="color:#C678DD">=></span><span style="color:#E5C07B"> T</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">T</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1109,7 +1096,7 @@ spell <code>sandbox.runWithProvenance?.(prov, fn) ?? fn()</code>.</p>
1109
1096
 
1110
1097
 
1111
1098
  <table><thead><tr><th align="left">Type Parameter</th></tr></thead><tbody><tr><td align="left"><code>T</code></td></tr></tbody></table></div>
1112
- <h6 id="parameters-14">Parameters</h6>
1099
+ <h6 id="parameters-12">Parameters</h6>
1113
1100
  <div class="table-scroll">
1114
1101
 
1115
1102
 
@@ -1128,20 +1115,20 @@ spell <code>sandbox.runWithProvenance?.(prov, fn) ?? fn()</code>.</p>
1128
1115
 
1129
1116
 
1130
1117
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>provenance</code></td><td align="left"><a href="#eventprovenance"><code>EventProvenance</code></a></td></tr><tr><td align="left"><code>fn</code></td><td align="left">() => <code>T</code></td></tr></tbody></table></div>
1131
- <h6 id="returns-24">Returns</h6>
1118
+ <h6 id="returns-23">Returns</h6>
1132
1119
  <p><code>T</code></p>
1133
- <h6 id="inherited-from-11">Inherited from</h6>
1134
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#runwithprovenance-4"><code>runWithProvenance</code></a></p>
1120
+ <h6 id="inherited-from-12">Inherited from</h6>
1121
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#runwithprovenance"><code>runWithProvenance</code></a></p>
1135
1122
  <p><a id="snapshot"></a></p>
1136
1123
  <h5 id="snapshot">snapshot()</h5>
1137
1124
  <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:#61AFEF">snapshot</span><span style="color:#ABB2BF">(): </span><span style="color:#E06C75">SandboxSnapshot</span><span style="color:#ABB2BF">;</span></span></code></pre>
1138
1125
  <p>Capture a snapshot of every service’s state. For v1 with only
1139
1126
  Firestore, the return value carries a <code>firestore</code> key mapping doc
1140
1127
  paths to data. Future services will add their own keys.</p>
1141
- <h6 id="returns-25">Returns</h6>
1142
- <p><a href="#sandboxsnapshot-2"><code>SandboxSnapshot</code></a></p>
1143
- <h6 id="inherited-from-12">Inherited from</h6>
1144
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#snapshot-6"><code>snapshot</code></a></p>
1128
+ <h6 id="returns-24">Returns</h6>
1129
+ <p><a href="#sandboxsnapshot"><code>SandboxSnapshot</code></a></p>
1130
+ <h6 id="inherited-from-13">Inherited from</h6>
1131
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#snapshot"><code>snapshot</code></a></p>
1145
1132
  <p><a id="withauth-2"></a></p>
1146
1133
  <h5 id="withauth-1">withAuth()</h5>
1147
1134
  <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:#61AFEF">withAuth</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">auth</span><span style="color:#ABB2BF">: {</span></span>
@@ -1156,7 +1143,7 @@ coexist for one sandbox; data is shared.</p>
1156
1143
  optional custom claims). Passing <code>undefined</code> is a deliberate
1157
1144
  error — say <code>withAuth(null)</code> for anonymous so the call site is
1158
1145
  unambiguous.</p>
1159
- <h6 id="parameters-15">Parameters</h6>
1146
+ <h6 id="parameters-13">Parameters</h6>
1160
1147
  <div class="table-scroll">
1161
1148
 
1162
1149
 
@@ -1179,14 +1166,14 @@ unambiguous.</p>
1179
1166
 
1180
1167
 
1181
1168
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
1182
- <h6 id="returns-26">Returns</h6>
1183
- <p><a href="#sandboxcontext"><code>SandboxContext</code></a></p>
1169
+ <h6 id="returns-25">Returns</h6>
1170
+ <p><a href="/docs/pyric-firestore-reference-api/#sandboxcontext"><code>SandboxContext</code></a></p>
1184
1171
  <h6 id="example-1">Example</h6>
1185
1172
  <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">const</span><span style="color:#E5C07B"> sandbox</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> initializeSandbox</span><span style="color:#ABB2BF">();</span></span>
1186
1173
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> dbAlice</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> getFirestore</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">sandbox</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">withAuth</span><span style="color:#ABB2BF">({ </span><span style="color:#E06C75">uid</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'alice'</span><span style="color:#ABB2BF"> }));</span></span>
1187
1174
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> dbAnon</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> getFirestore</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">sandbox</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">withAuth</span><span style="color:#ABB2BF">(</span><span style="color:#D19A66">null</span><span style="color:#ABB2BF">));</span></span></code></pre>
1188
- <h6 id="inherited-from-13">Inherited from</h6>
1189
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#withauth-6"><code>withAuth</code></a></p>
1175
+ <h6 id="inherited-from-14">Inherited from</h6>
1176
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#withauth"><code>withAuth</code></a></p>
1190
1177
  <hr>
1191
1178
  <p><a id="operationcontext-2"></a></p>
1192
1179
  <h3 id="operationcontext">OperationContext</h3>
@@ -1294,13 +1281,13 @@ as a user, while an app or agent may use an admin lens.</p>
1294
1281
 
1295
1282
 
1296
1283
 
1297
- <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="at-1"></a> <code>at</code></td><td align="left"><code>readonly</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="auth-4"></a> <code>auth</code></td><td align="left"><code>readonly</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="context"></a> <code>context</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td></tr><tr><td align="left"><a id="eventkind"></a> <code>eventKind</code></td><td align="left"><code>readonly</code></td><td align="left"><code>"request"</code> | <code>"operation"</code></td></tr><tr><td align="left"><a id="id-1"></a> <code>id</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="method"></a> <code>method</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="path"></a> <code>path?</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="result"></a> <code>result</code></td><td align="left"><code>readonly</code></td><td align="left"><code>"allow"</code> | <code>"deny"</code> | <code>"unsupported"</code> | <code>"error"</code> | <code>"not-applicable"</code></td></tr><tr><td align="left"><a id="rules-1"></a> <code>rules</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#rulesdisposition-2"><code>RulesDisposition</code></a></td></tr><tr><td align="left"><a id="service-1"></a> <code>service</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td></tr></tbody></table></div>
1284
+ <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="at-1"></a> <code>at</code></td><td align="left"><code>readonly</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="auth-4"></a> <code>auth</code></td><td align="left"><code>readonly</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="context"></a> <code>context</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td></tr><tr><td align="left"><a id="eventkind"></a> <code>eventKind</code></td><td align="left"><code>readonly</code></td><td align="left"><code>"request"</code> | <code>"operation"</code></td></tr><tr><td align="left"><a id="id-1"></a> <code>id</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="method"></a> <code>method</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="path"></a> <code>path?</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="result"></a> <code>result</code></td><td align="left"><code>readonly</code></td><td align="left"><code>"unsupported"</code> | <code>"error"</code> | <code>"allow"</code> | <code>"deny"</code> | <code>"not-applicable"</code></td></tr><tr><td align="left"><a id="rules-1"></a> <code>rules</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#rulesdisposition-2"><code>RulesDisposition</code></a></td></tr><tr><td align="left"><a id="service-1"></a> <code>service</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td></tr></tbody></table></div>
1298
1285
  <hr>
1299
1286
  <p><a id="persistableservice"></a></p>
1300
1287
  <h3 id="persistableservice">PersistableService</h3>
1301
1288
  <p>Contract for a service that can contribute its state to the sandbox
1302
1289
  persistence layer. Services (auth, storage, database) register
1303
- themselves via <a href="#registerpersistableservice-4">Sandbox.registerPersistableService</a> so the
1290
+ themselves via <a href="/docs/pyric-firestore-reference-api/#registerpersistableservice">Sandbox.registerPersistableService</a> so the
1304
1291
  sandbox core stays service-agnostic — the sandbox doesn’t know what
1305
1292
  auth or storage look like; it just calls <code>snapshot()</code> / <code>restore()</code>.</p>
1306
1293
  <p><code>subscribe</code> is optional but strongly recommended: without it, a
@@ -1349,8 +1336,22 @@ as Firestore writes.</p>
1349
1336
 
1350
1337
 
1351
1338
 
1352
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="session"></a> <code>session?</code></td><td align="left">{ <code>currentUid</code>: <code>string</code>; <code>mode</code>: <code>"LOCAL"</code> | <code>"SESSION"</code> | <code>"NONE"</code>; <code>restore</code>: <code>void</code>; <code>subscribe</code>: () => <code>void</code>; }</td><td align="left">Optional: session-level persistence hooks. When provided, the persistence controller uses these to save and restore the CURRENTLY SIGNED-IN user (not the user database — that’s <code>snapshot</code>/<code>restore</code>). The controller calls <code>session.subscribe</code> so it hears every sign-in / sign-out, then writes the uid to the appropriate web-storage slot (determined by <code>session.mode()</code>). On init, the controller reads the stored uid and its storage-derived mode, then calls <code>session.restore(uid, mode)</code> to re-establish both before firing <code>onAuthStateChanged</code> as if the user just signed in. Only active when <code>SandboxPersistenceOptions.sessionStorage</code> is provided; omitting <code>sessionStorage</code> causes the controller to skip session persistence entirely (no fake durability). Auth is the only service that provides session hooks today. The field is on the generic interface so the controller stays service-agnostic — if a second service ever needs session-style semantics it can add its own hooks without changing the controller.</td></tr><tr><td align="left"><code>session.currentUid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>session.mode</code></td><td align="left"><code>"LOCAL"</code> | <code>"SESSION"</code> | <code>"NONE"</code></td><td align="left">-</td></tr><tr><td align="left"><code>session.restore</code></td><td align="left"><code>void</code></td><td align="left">-</td></tr><tr><td align="left"><code>session.subscribe</code></td><td align="left">() => <code>void</code></td><td align="left">-</td></tr><tr><td align="left"><a id="subscribe"></a> <code>subscribe?</code></td><td align="left">(<code>onChange</code>: () => <code>void</code>) => () => <code>void</code></td><td align="left">Optional: subscribe to changes in this service’s state. When provided, the persistence controller hooks it up and schedules a debounced flush on each change — ensuring auth-user edits reach the backend promptly, not only on the next Firestore write. Must return an unsubscribe function. The controller unsubscribes on <code>dispose()</code>.</td></tr></tbody></table></div>
1339
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="session"></a> <code>session?</code></td><td align="left">{ <code>currentUid</code>: <code>string</code>; <code>mode</code>: <code>"NONE"</code> | <code>"SESSION"</code> | <code>"LOCAL"</code>; <code>restore</code>: <code>void</code>; <code>subscribe</code>: () => <code>void</code>; }</td><td align="left">Optional: session-level persistence hooks. When provided, the persistence controller uses these to save and restore the CURRENTLY SIGNED-IN user (not the user database — that’s <code>snapshot</code>/<code>restore</code>). The controller calls <code>session.subscribe</code> so it hears every sign-in / sign-out, then writes the uid to the appropriate web-storage slot (determined by <code>session.mode()</code>). On init, the controller reads the stored uid and its storage-derived mode, then calls <code>session.restore(uid, mode)</code> to re-establish both before firing <code>onAuthStateChanged</code> as if the user just signed in. Only active when <code>SandboxPersistenceOptions.sessionStorage</code> is provided; omitting <code>sessionStorage</code> causes the controller to skip session persistence entirely (no fake durability). Auth is the only service that provides session hooks today. The field is on the generic interface so the controller stays service-agnostic — if a second service ever needs session-style semantics it can add its own hooks without changing the controller.</td></tr><tr><td align="left"><code>session.currentUid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>session.mode</code></td><td align="left"><code>"NONE"</code> | <code>"SESSION"</code> | <code>"LOCAL"</code></td><td align="left">-</td></tr><tr><td align="left"><code>session.restore</code></td><td align="left"><code>void</code></td><td align="left">-</td></tr><tr><td align="left"><code>session.subscribe</code></td><td align="left">() => <code>void</code></td><td align="left">-</td></tr><tr><td align="left"><a id="subscribe"></a> <code>subscribe?</code></td><td align="left">(<code>onChange</code>: () => <code>void</code>) => () => <code>void</code></td><td align="left">Optional: subscribe to changes in this service’s state. When provided, the persistence controller hooks it up and schedules a debounced flush on each change — ensuring auth-user edits reach the backend promptly, not only on the next Firestore write. Must return an unsubscribe function. The controller unsubscribes on <code>dispose()</code>.</td></tr></tbody></table></div>
1353
1340
  <h4 id="methods-3">Methods</h4>
1341
+ <p><a id="reset-2"></a></p>
1342
+ <h5 id="reset-1">reset()?</h5>
1343
+ <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:#E06C75">optional</span><span style="color:#61AFEF"> reset</span><span style="color:#ABB2BF">(): </span><span style="color:#C678DD">void</span><span style="color:#56B6C2"> |</span><span style="color:#E5C07B"> Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
1344
+ <p>Optional: clear this service’s state to empty. Called by
1345
+ <a href="/docs/pyric-firestore-reference-api/#resetall">Sandbox.resetAll</a> after the Firestore environment swap, so a
1346
+ single sandbox-owned call wipes EVERY registered service (auth users,
1347
+ RTDB tree, storage objects) — a consumer (Pyric Studio’s reset) can’t
1348
+ forget a service it never knew about. May be async (storage clears an
1349
+ IndexedDB store); <code>resetAll</code> awaits it. A service that omits <code>reset</code>
1350
+ is skipped — its state deliberately survives <code>resetAll</code> (e.g. the
1351
+ per-app auth-session hooks, whose signed-in identity the sandbox core
1352
+ already clears in <code>reset()</code>).</p>
1353
+ <h6 id="returns-26">Returns</h6>
1354
+ <p><code>void</code> | <code>Promise</code>&#x3C;<code>void</code>></p>
1354
1355
  <p><a id="restore"></a></p>
1355
1356
  <h5 id="restore">restore()</h5>
1356
1357
  <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:#61AFEF">restore</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">data</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">unknown</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1359,7 +1360,7 @@ as Firestore writes.</p>
1359
1360
  any service that needs Firestore to be hydrated first can rely on
1360
1361
  that ordering). Guard against bad data — the blob came from disk
1361
1362
  and may be stale or from a schema migration.</p>
1362
- <h6 id="parameters-16">Parameters</h6>
1363
+ <h6 id="parameters-14">Parameters</h6>
1363
1364
  <div class="table-scroll">
1364
1365
 
1365
1366
 
@@ -1398,7 +1399,7 @@ them. (v2 and earlier used a single string blob; v3 is record-shaped.)</p>
1398
1399
  <h5 id="clear">clear()</h5>
1399
1400
  <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:#61AFEF">clear</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">key</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
1400
1401
  <p>Remove ALL records under <code>key</code>. No-op if none exist.</p>
1401
- <h6 id="parameters-17">Parameters</h6>
1402
+ <h6 id="parameters-15">Parameters</h6>
1402
1403
  <div class="table-scroll">
1403
1404
 
1404
1405
 
@@ -1419,7 +1420,7 @@ them. (v2 and earlier used a single string blob; v3 is record-shaped.)</p>
1419
1420
  <h5 id="deleterecords">deleteRecords()</h5>
1420
1421
  <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:#61AFEF">deleteRecords</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">key</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">recordIds</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">readonly</span><span style="color:#E06C75"> string</span><span style="color:#ABB2BF">[]): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
1421
1422
  <p>Delete the given record ids under <code>key</code>. No-op for ids that don’t exist.</p>
1422
- <h6 id="parameters-18">Parameters</h6>
1423
+ <h6 id="parameters-16">Parameters</h6>
1423
1424
  <div class="table-scroll">
1424
1425
 
1425
1426
 
@@ -1459,7 +1460,7 @@ backend that can’t estimate simply omits it.</p>
1459
1460
  <h5 id="getrecord">getRecord()</h5>
1460
1461
  <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:#61AFEF">getRecord</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">key</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">recordId</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#E06C75">unknown</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
1461
1462
  <p>Read one record by id under <code>key</code>. Resolves <code>null</code> when absent.</p>
1462
- <h6 id="parameters-19">Parameters</h6>
1463
+ <h6 id="parameters-17">Parameters</h6>
1463
1464
  <div class="table-scroll">
1464
1465
 
1465
1466
 
@@ -1484,7 +1485,7 @@ backend that can’t estimate simply omits it.</p>
1484
1485
  <h5 id="listrecords">listRecords()</h5>
1485
1486
  <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:#61AFEF">listRecords</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">key</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">[]</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
1486
1487
  <p>List all record ids under <code>key</code>, any order.</p>
1487
- <h6 id="parameters-20">Parameters</h6>
1488
+ <h6 id="parameters-18">Parameters</h6>
1488
1489
  <div class="table-scroll">
1489
1490
 
1490
1491
 
@@ -1505,7 +1506,7 @@ backend that can’t estimate simply omits it.</p>
1505
1506
  <h5 id="putrecords">putRecords()</h5>
1506
1507
  <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:#61AFEF">putRecords</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">key</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">records</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">ReadonlyMap</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">unknown</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
1507
1508
  <p>Write each <code>[recordId, value]</code> under <code>key</code>, replacing any prior value.</p>
1508
- <h6 id="parameters-21">Parameters</h6>
1509
+ <h6 id="parameters-19">Parameters</h6>
1509
1510
  <div class="table-scroll">
1510
1511
 
1511
1512
 
@@ -1568,15 +1569,19 @@ backend that can’t estimate simply omits it.</p>
1568
1569
  <hr>
1569
1570
  <p><a id="remotesandbox"></a></p>
1570
1571
  <h3 id="remotesandbox">RemoteSandbox</h3>
1571
- <p>A branded remote sandbox handle. Structurally a <a href="#sandbox-3">Sandbox</a> — it can
1572
+ <p>A branded remote sandbox handle. Structurally a <a href="/docs/pyric-firestore-reference-api/#sandbox">Sandbox</a> — it can
1572
1573
  be passed anywhere a <code>Sandbox</code> is accepted (notably
1573
1574
  <code>pyric-admin/app</code>’s <code>initializeApp({ sandbox })</code>) — but sync-only members
1574
1575
  that cannot be mirrored over the wire (<code>admin</code>, <code>snapshot()</code>,
1575
1576
  <code>history()</code>, …) throw a remediating error. Consumers with a remote arm
1576
1577
  dispatch on <a href="#isremotesandbox">isRemoteSandbox</a> and use <a href="#channel">channel</a> instead.</p>
1577
- <h4 id="extends-3">Extends</h4>
1578
+ <h4 id="extends-2">Extends</h4>
1578
1579
  <ul>
1579
- <li><a href="#sandbox-3"><code>Sandbox</code></a></li>
1580
+ <li><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a></li>
1581
+ </ul>
1582
+ <h4 id="extended-by">Extended by</h4>
1583
+ <ul>
1584
+ <li><a href="/docs/pyric-cli-remote-reference-api/#remotesandbox"><code>RemoteSandbox</code></a></li>
1580
1585
  </ul>
1581
1586
  <h4 id="properties-12">Properties</h4>
1582
1587
  <div class="table-scroll">
@@ -1632,7 +1637,7 @@ dispatch on <a href="#isremotesandbox">isRemoteSandbox</a> and use <a href="#cha
1632
1637
 
1633
1638
 
1634
1639
 
1635
- <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="remote_sandbox"></a> <code>[REMOTE_SANDBOX]</code></td><td align="left"><code>readonly</code></td><td align="left"><code>true</code></td><td align="left">-</td></tr><tr><td align="left"><a id="admin-1"></a> <code>admin</code></td><td align="left"><code>readonly</code></td><td align="left"><code>SandboxAdmin</code></td><td align="left">Admin-plane access (rule-bypass reads). Identity-agnostic by design — admin reads aren’t gated on auth, so they live on the sandbox, not on a context. See SandboxAdmin.</td></tr><tr><td align="left"><a id="channel"></a> <code>channel</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#remotesandboxchannel-1"><code>RemoteSandboxChannel</code></a></td><td align="left">The raw worker op/sub relay channel.</td></tr><tr><td align="left"><a id="currentuser-1"></a> <code>currentUser</code></td><td align="left"><code>public</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Current authenticated user across the sandbox. Mutated by <code>pyric/auth</code>’s <code>signInAnonymously</code> / <code>signInWithEmailAndPassword</code> / <code>signOut</code> / <code>sandbox.setUser</code>. Read per-call by service factories (e.g. a future <code>getFirestore(sandbox)</code> overload) so they see auth state changes without re-binding handles. Defaults to <code>null</code> (anonymous / signed out). <strong>Independent of <code>withAuth({uid})</code></strong> — <code>withAuth</code> still produces a frozen <a href="#sandboxcontext">SandboxContext</a> that carries its own identity for the runner’s test code (the existing pattern: explicit identity per service call). <code>currentUser</code> exists for the <code>pyric/auth</code> mirror, where consumer app code drives identity through a stateful <code>Auth</code> handle rather than naming it per call.</td></tr><tr><td align="left"><code>currentUser.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>currentUser.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="serveurl"></a> <code>serveUrl</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td><td align="left">Base URL of the <code>pyric dev</code> this handle is attached to (used in error guidance: “open <serveurl> in a browser and retry”).</serveurl></td></tr></tbody></table></div>
1640
+ <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="remote_sandbox"></a> <code>[REMOTE_SANDBOX]</code></td><td align="left"><code>readonly</code></td><td align="left"><code>true</code></td><td align="left">-</td></tr><tr><td align="left"><a id="admin-1"></a> <code>admin</code></td><td align="left"><code>readonly</code></td><td align="left"><code>SandboxAdmin</code></td><td align="left">Admin-plane access (rule-bypass reads). Identity-agnostic by design — admin reads aren’t gated on auth, so they live on the sandbox, not on a context. See SandboxAdmin.</td></tr><tr><td align="left"><a id="channel"></a> <code>channel</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#remotesandboxchannel-1"><code>RemoteSandboxChannel</code></a></td><td align="left">The raw worker op/sub relay channel.</td></tr><tr><td align="left"><a id="currentuser-1"></a> <code>currentUser</code></td><td align="left"><code>public</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Current authenticated user across the sandbox. Mutated by <code>pyric/auth</code>’s <code>signInAnonymously</code> / <code>signInWithEmailAndPassword</code> / <code>signOut</code> / <code>sandbox.setUser</code>. Read per-call by service factories (e.g. a future <code>getFirestore(sandbox)</code> overload) so they see auth state changes without re-binding handles. Defaults to <code>null</code> (anonymous / signed out). <strong>Independent of <code>withAuth({uid})</code></strong> — <code>withAuth</code> still produces a frozen <a href="/docs/pyric-firestore-reference-api/#sandboxcontext">SandboxContext</a> that carries its own identity for the runner’s test code (the existing pattern: explicit identity per service call). <code>currentUser</code> exists for the <code>pyric/auth</code> mirror, where consumer app code drives identity through a stateful <code>Auth</code> handle rather than naming it per call.</td></tr><tr><td align="left"><code>currentUser.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>currentUser.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="serveurl"></a> <code>serveUrl</code></td><td align="left"><code>readonly</code></td><td align="left"><code>string</code></td><td align="left">Base URL of the <code>pyric dev</code> this handle is attached to (used in error guidance: “open <serveurl> in a browser and retry”).</serveurl></td></tr></tbody></table></div>
1636
1641
  <h4 id="methods-6">Methods</h4>
1637
1642
  <p><a id="clearpersistence-2"></a></p>
1638
1643
  <h5 id="clearpersistence-1">clearPersistence()</h5>
@@ -1643,8 +1648,8 @@ is left intact — call <code>reset()</code> if you want both. Useful for
1643
1648
  <p>No-op when persistence is not enabled.</p>
1644
1649
  <h6 id="returns-38">Returns</h6>
1645
1650
  <p><code>Promise</code>&#x3C;<code>void</code>></p>
1646
- <h6 id="inherited-from-14">Inherited from</h6>
1647
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#clearpersistence-4"><code>clearPersistence</code></a></p>
1651
+ <h6 id="inherited-from-15">Inherited from</h6>
1652
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#clearpersistence"><code>clearPersistence</code></a></p>
1648
1653
  <p><a id="dispose-4"></a></p>
1649
1654
  <h5 id="dispose-2">dispose()</h5>
1650
1655
  <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:#61AFEF">dispose</span><span style="color:#ABB2BF">(): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1655,8 +1660,8 @@ calling <code>reset()</code>) and want to drop callback references on the
1655
1660
  outgoing instance defensively. Idempotent. Does not touch data.</p>
1656
1661
  <h6 id="returns-39">Returns</h6>
1657
1662
  <p><code>void</code></p>
1658
- <h6 id="inherited-from-15">Inherited from</h6>
1659
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#dispose-6"><code>dispose</code></a></p>
1663
+ <h6 id="inherited-from-16">Inherited from</h6>
1664
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#dispose"><code>dispose</code></a></p>
1660
1665
  <p><a id="enablepersistence-2"></a></p>
1661
1666
  <h5 id="enablepersistence-1">enablePersistence()</h5>
1662
1667
  <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:#61AFEF">enablePersistence</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">options</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">SandboxPersistenceOptions</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1677,7 +1682,7 @@ flush on <code>beforeunload</code> so a page navigation doesn’t lose the
1677
1682
  tail of the debounce window.</p>
1678
1683
  <p>See <a href="#sandboxpersistenceoptions">SandboxPersistenceOptions</a> for backend selection and
1679
1684
  tuning.</p>
1680
- <h6 id="parameters-22">Parameters</h6>
1685
+ <h6 id="parameters-20">Parameters</h6>
1681
1686
  <div class="table-scroll">
1682
1687
 
1683
1688
 
@@ -1694,8 +1699,8 @@ tuning.</p>
1694
1699
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>options</code></td><td align="left"><a href="#sandboxpersistenceoptions"><code>SandboxPersistenceOptions</code></a></td></tr></tbody></table></div>
1695
1700
  <h6 id="returns-40">Returns</h6>
1696
1701
  <p><code>Promise</code>&#x3C;<code>void</code>></p>
1697
- <h6 id="inherited-from-16">Inherited from</h6>
1698
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#enablepersistence-4"><code>enablePersistence</code></a></p>
1702
+ <h6 id="inherited-from-17">Inherited from</h6>
1703
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#enablepersistence"><code>enablePersistence</code></a></p>
1699
1704
  <p><a id="enabletabsync-2"></a></p>
1700
1705
  <h5 id="enabletabsync-1">enableTabSync()</h5>
1701
1706
  <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:#61AFEF">enableTabSync</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">options</span><span style="color:#C678DD">?:</span><span style="color:#E06C75"> TabSyncOptions</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1713,7 +1718,7 @@ produce last-write-wins divergence — there is no conflict resolution.
1713
1718
  The intended model is one active writer (one user, one tab) with
1714
1719
  observers in other tabs; this covers the overwhelming majority of
1715
1720
  local development scenarios.</p>
1716
- <h6 id="parameters-23">Parameters</h6>
1721
+ <h6 id="parameters-21">Parameters</h6>
1717
1722
  <div class="table-scroll">
1718
1723
 
1719
1724
 
@@ -1740,8 +1745,8 @@ local development scenarios.</p>
1740
1745
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> disableSync</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> sandbox</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">enableTabSync</span><span style="color:#ABB2BF">();</span></span>
1741
1746
  <span class="line"><span style="color:#7F848E;font-style:italic">// Later, to stop syncing:</span></span>
1742
1747
  <span class="line"><span style="color:#61AFEF">disableSync</span><span style="color:#ABB2BF">();</span></span></code></pre>
1743
- <h6 id="inherited-from-17">Inherited from</h6>
1744
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#enabletabsync-4"><code>enableTabSync</code></a></p>
1748
+ <h6 id="inherited-from-18">Inherited from</h6>
1749
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#enabletabsync"><code>enableTabSync</code></a></p>
1745
1750
  <p><a id="flush-4"></a></p>
1746
1751
  <h5 id="flush-2">flush()</h5>
1747
1752
  <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:#61AFEF">flush</span><span style="color:#ABB2BF">(): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1752,8 +1757,8 @@ the write hits the backend.</p>
1752
1757
  <p>Throws if persistence is not enabled.</p>
1753
1758
  <h6 id="returns-43">Returns</h6>
1754
1759
  <p><code>Promise</code>&#x3C;<code>void</code>></p>
1755
- <h6 id="inherited-from-18">Inherited from</h6>
1756
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#flush-6"><code>flush</code></a></p>
1760
+ <h6 id="inherited-from-19">Inherited from</h6>
1761
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#flush"><code>flush</code></a></p>
1757
1762
  <p><a id="history-2"></a></p>
1758
1763
  <h5 id="history-1">history()</h5>
1759
1764
  <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:#61AFEF">history</span><span style="color:#ABB2BF">(): </span><span style="color:#E06C75">SandboxEvent</span><span style="color:#ABB2BF">[];</span></span></code></pre>
@@ -1762,7 +1767,7 @@ the last <code>reset()</code>. Returns a defensive copy.</p>
1762
1767
  <p>Use this for replay: hand the array to <code>replay(events, rules)</code>
1763
1768
  from <code>pyric/sandbox</code> and the engine re-issues every
1764
1769
  captured write against a fresh sandbox.</p>
1765
- <p>Unlike <a href="#onevent-4">onEvent</a> (live stream from the moment of subscribe),
1770
+ <p>Unlike <a href="/docs/pyric-firestore-reference-api/#onevent">onEvent</a> (live stream from the moment of subscribe),
1766
1771
  <code>history()</code> returns <em>every</em> event the sandbox has seen — useful
1767
1772
  for consumers that attach late (e.g., loading a saved session
1768
1773
  before subscribing) or that need a snapshot at a particular moment.</p>
@@ -1771,24 +1776,24 @@ event; <code>reset()</code> then clears the history. Consumers that took a
1771
1776
  snapshot <em>before</em> reset retain the boundary in their copy.</p>
1772
1777
  <h6 id="returns-44">Returns</h6>
1773
1778
  <p><a href="#sandboxevent"><code>SandboxEvent</code></a>[]</p>
1774
- <h6 id="inherited-from-19">Inherited from</h6>
1775
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#history-4"><code>history</code></a></p>
1779
+ <h6 id="inherited-from-20">Inherited from</h6>
1780
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#history"><code>history</code></a></p>
1776
1781
  <p><a id="loadsnapshot-2"></a></p>
1777
1782
  <h5 id="loadsnapshot-1">loadSnapshot()</h5>
1778
1783
  <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:#61AFEF">loadSnapshot</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">data</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">SandboxSnapshot</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
1779
- <p>CLOBBER-restore the sandbox’s entire state from a prior <a href="#snapshot-6">snapshot</a>:
1784
+ <p>CLOBBER-restore the sandbox’s entire state from a prior <a href="/docs/pyric-firestore-reference-api/#snapshot">snapshot</a>:
1780
1785
  <code>reset()</code> (clears firestore + the signed-in session), then rebuild firestore
1781
1786
  from <code>data</code> and restore each registered service. This is a TOTAL replace —
1782
1787
  documents absent from <code>data</code> do NOT survive — and is the counterpart to
1783
- <a href="#snapshot-6">snapshot</a>. It is what makes “transfer (clobber) one instance’s data
1788
+ <a href="/docs/pyric-firestore-reference-api/#snapshot">snapshot</a>. It is what makes “transfer (clobber) one instance’s data
1784
1789
  into another” and named-branch switching possible.</p>
1785
1790
  <p>Fires a <code>session_boundary</code> (reset phase), re-evaluates live listeners against
1786
1791
  the loaded state, and the next persistence flush writes the loaded state.
1787
1792
  Services present in <code>data</code> but not currently registered are skipped (a
1788
- snapshot taken via <a href="#snapshot-6">snapshot</a> always includes every registered
1793
+ snapshot taken via <a href="/docs/pyric-firestore-reference-api/#snapshot">snapshot</a> always includes every registered
1789
1794
  service, so this only affects cross-instance imports from a sandbox that had
1790
1795
  a service this one lacks).</p>
1791
- <h6 id="parameters-24">Parameters</h6>
1796
+ <h6 id="parameters-22">Parameters</h6>
1792
1797
  <div class="table-scroll">
1793
1798
 
1794
1799
 
@@ -1802,11 +1807,11 @@ a service this one lacks).</p>
1802
1807
 
1803
1808
 
1804
1809
 
1805
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>data</code></td><td align="left"><a href="#sandboxsnapshot-2"><code>SandboxSnapshot</code></a></td></tr></tbody></table></div>
1810
+ <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>data</code></td><td align="left"><a href="#sandboxsnapshot"><code>SandboxSnapshot</code></a></td></tr></tbody></table></div>
1806
1811
  <h6 id="returns-45">Returns</h6>
1807
1812
  <p><code>void</code></p>
1808
- <h6 id="inherited-from-20">Inherited from</h6>
1809
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#loadsnapshot-4"><code>loadSnapshot</code></a></p>
1813
+ <h6 id="inherited-from-21">Inherited from</h6>
1814
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#loadsnapshot"><code>loadSnapshot</code></a></p>
1810
1815
  <p><a id="oncurrentuserchanged-2"></a></p>
1811
1816
  <h5 id="oncurrentuserchanged-1">onCurrentUserChanged()</h5>
1812
1817
  <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:#61AFEF">onCurrentUserChanged</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">cb</span><span style="color:#ABB2BF">: (</span><span style="color:#E06C75;font-style:italic">user</span><span style="color:#ABB2BF">: {</span></span>
@@ -1822,7 +1827,7 @@ the env.</p>
1822
1827
  <p>Returns an unsubscribe function. Listener errors are swallowed —
1823
1828
  subscribers are observational, the sandbox does not propagate
1824
1829
  their errors.</p>
1825
- <h6 id="parameters-25">Parameters</h6>
1830
+ <h6 id="parameters-23">Parameters</h6>
1826
1831
  <div class="table-scroll">
1827
1832
 
1828
1833
 
@@ -1841,8 +1846,8 @@ their errors.</p>
1841
1846
  <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
1842
1847
  <h6 id="returns-47">Returns</h6>
1843
1848
  <p><code>void</code></p>
1844
- <h6 id="inherited-from-21">Inherited from</h6>
1845
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#oncurrentuserchanged-4"><code>onCurrentUserChanged</code></a></p>
1849
+ <h6 id="inherited-from-22">Inherited from</h6>
1850
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#oncurrentuserchanged"><code>onCurrentUserChanged</code></a></p>
1846
1851
  <p><a id="onevent-2"></a></p>
1847
1852
  <h5 id="onevent-1">onEvent()</h5>
1848
1853
  <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:#61AFEF">onEvent</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">cb</span><span style="color:#ABB2BF">: (</span><span style="color:#E06C75;font-style:italic">event</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">SandboxEvent</span><span style="color:#ABB2BF">) </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1866,7 +1871,7 @@ faulty subscriber can’t change rule semantics or hide other events.
1866
1871
  Both synchronous throws and rejected Promises from async callbacks
1867
1872
  are silently discarded — subscribers are <strong>observational</strong>, the
1868
1873
  sandbox doesn’t await them and doesn’t propagate their errors.</p>
1869
- <h6 id="parameters-26">Parameters</h6>
1874
+ <h6 id="parameters-24">Parameters</h6>
1870
1875
  <div class="table-scroll">
1871
1876
 
1872
1877
 
@@ -1885,8 +1890,8 @@ sandbox doesn’t await them and doesn’t propagate their errors.</p>
1885
1890
  <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
1886
1891
  <h6 id="returns-49">Returns</h6>
1887
1892
  <p><code>void</code></p>
1888
- <h6 id="inherited-from-22">Inherited from</h6>
1889
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#onevent-4"><code>onEvent</code></a></p>
1893
+ <h6 id="inherited-from-23">Inherited from</h6>
1894
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#onevent"><code>onEvent</code></a></p>
1890
1895
  <p><a id="registerpersistableservice-2"></a></p>
1891
1896
  <h5 id="registerpersistableservice-1">registerPersistableService()</h5>
1892
1897
  <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:#61AFEF">registerPersistableService</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">name</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">hooks</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">PersistableService</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1906,7 +1911,7 @@ registration is a caller bug, not a no-op.</p>
1906
1911
  <p><strong>Advanced / internal API.</strong> Service packages (auth, storage) call
1907
1912
  this when they first attach to a sandbox. Consumer app code should
1908
1913
  not need to call this directly.</p>
1909
- <h6 id="parameters-27">Parameters</h6>
1914
+ <h6 id="parameters-25">Parameters</h6>
1910
1915
  <div class="table-scroll">
1911
1916
 
1912
1917
 
@@ -1929,10 +1934,10 @@ not need to call this directly.</p>
1929
1934
  <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
1930
1935
  <h6 id="returns-51">Returns</h6>
1931
1936
  <p><code>void</code></p>
1932
- <h6 id="inherited-from-23">Inherited from</h6>
1933
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#registerpersistableservice-4"><code>registerPersistableService</code></a></p>
1934
- <p><a id="reset-2"></a></p>
1935
- <h5 id="reset-1">reset()</h5>
1937
+ <h6 id="inherited-from-24">Inherited from</h6>
1938
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#registerpersistableservice"><code>registerPersistableService</code></a></p>
1939
+ <p><a id="reset-4"></a></p>
1940
+ <h5 id="reset-2">reset()</h5>
1936
1941
  <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:#61AFEF">reset</span><span style="color:#ABB2BF">(): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
1937
1942
  <p>Reset the underlying environment to a fresh state — wipes data,
1938
1943
  rules, and any service-specific configuration.</p>
@@ -1941,12 +1946,35 @@ the swap — they can’t survive because their target docs have been
1941
1946
  wiped. <code>onEvent</code> subscribers DO survive — the registry lives on
1942
1947
  the sandbox, and a <code>session_boundary</code> event with <code>phase: 'reset'</code>
1943
1948
  fires before the swap so subscribers know the rollover happened.
1944
- Existing <a href="#sandboxcontext">SandboxContext</a>s continue to work — their sandbox
1949
+ Existing <a href="/docs/pyric-firestore-reference-api/#sandboxcontext">SandboxContext</a>s continue to work — their sandbox
1945
1950
  reference is stable; subsequent operations resolve to the new env.</p>
1946
1951
  <h6 id="returns-52">Returns</h6>
1947
1952
  <p><code>void</code></p>
1948
- <h6 id="inherited-from-24">Inherited from</h6>
1949
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#reset-4"><code>reset</code></a></p>
1953
+ <h6 id="inherited-from-25">Inherited from</h6>
1954
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#reset"><code>reset</code></a></p>
1955
+ <p><a id="resetall-2"></a></p>
1956
+ <h5 id="resetall-1">resetAll()</h5>
1957
+ <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:#61AFEF">resetAll</span><span style="color:#ABB2BF">(): </span><span style="color:#E5C07B">Promise</span><span style="color:#ABB2BF">&#x3C;{</span></span>
1958
+ <span class="line"><span style="color:#E06C75"> errors</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">string</span><span style="color:#ABB2BF">[];</span></span>
1959
+ <span class="line"><span style="color:#ABB2BF">}>;</span></span></code></pre>
1960
+ <p>Reset the WHOLE sandbox: <a href="/docs/pyric-firestore-reference-api/#reset">reset</a> (Firestore env + signed-in
1961
+ session), then clear every registered persistable service that
1962
+ provides a <a href="#reset-2">PersistableService.reset</a> hook — auth users, the
1963
+ RTDB tree, storage objects. This is the one sandbox-owned “wipe
1964
+ everything” path: because it iterates the service registry, a new
1965
+ service that registers with a <code>reset</code> hook is cleared automatically,
1966
+ and a consumer (Pyric Studio’s reset) cannot forget one.</p>
1967
+ <p>Service resets may be async (storage clears IndexedDB stores); the
1968
+ returned promise resolves when every service has finished clearing.
1969
+ A service whose <code>reset</code> throws is isolated (others still clear) and
1970
+ REPORTED in the returned <code>errors</code> (as <code>name: message</code>) — a reset that
1971
+ leaves data behind must never look successful to the caller.</p>
1972
+ <h6 id="returns-53">Returns</h6>
1973
+ <p><code>Promise</code>&#x3C;{
1974
+ <code>errors</code>: <code>string</code>[];
1975
+ }></p>
1976
+ <h6 id="inherited-from-26">Inherited from</h6>
1977
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#resetall"><code>resetAll</code></a></p>
1950
1978
  <p><a id="runwithprovenance-2"></a></p>
1951
1979
  <h5 id="runwithprovenance-1">runWithProvenance()?</h5>
1952
1980
  <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:#E06C75">optional</span><span style="color:#61AFEF"> runWithProvenance</span><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E5C07B">T</span><span style="color:#ABB2BF">>(</span><span style="color:#E06C75">provenance</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">EventProvenance</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">fn</span><span style="color:#ABB2BF">: () </span><span style="color:#C678DD">=></span><span style="color:#E5C07B"> T</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">T</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -1983,7 +2011,7 @@ spell <code>sandbox.runWithProvenance?.(prov, fn) ?? fn()</code>.</p>
1983
2011
 
1984
2012
 
1985
2013
  <table><thead><tr><th align="left">Type Parameter</th></tr></thead><tbody><tr><td align="left"><code>T</code></td></tr></tbody></table></div>
1986
- <h6 id="parameters-28">Parameters</h6>
2014
+ <h6 id="parameters-26">Parameters</h6>
1987
2015
  <div class="table-scroll">
1988
2016
 
1989
2017
 
@@ -2002,20 +2030,20 @@ spell <code>sandbox.runWithProvenance?.(prov, fn) ?? fn()</code>.</p>
2002
2030
 
2003
2031
 
2004
2032
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>provenance</code></td><td align="left"><a href="#eventprovenance"><code>EventProvenance</code></a></td></tr><tr><td align="left"><code>fn</code></td><td align="left">() => <code>T</code></td></tr></tbody></table></div>
2005
- <h6 id="returns-53">Returns</h6>
2033
+ <h6 id="returns-54">Returns</h6>
2006
2034
  <p><code>T</code></p>
2007
- <h6 id="inherited-from-25">Inherited from</h6>
2008
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#runwithprovenance-4"><code>runWithProvenance</code></a></p>
2035
+ <h6 id="inherited-from-27">Inherited from</h6>
2036
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#runwithprovenance"><code>runWithProvenance</code></a></p>
2009
2037
  <p><a id="snapshot-4"></a></p>
2010
2038
  <h5 id="snapshot-2">snapshot()</h5>
2011
2039
  <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:#61AFEF">snapshot</span><span style="color:#ABB2BF">(): </span><span style="color:#E06C75">SandboxSnapshot</span><span style="color:#ABB2BF">;</span></span></code></pre>
2012
2040
  <p>Capture a snapshot of every service’s state. For v1 with only
2013
2041
  Firestore, the return value carries a <code>firestore</code> key mapping doc
2014
2042
  paths to data. Future services will add their own keys.</p>
2015
- <h6 id="returns-54">Returns</h6>
2016
- <p><a href="#sandboxsnapshot-2"><code>SandboxSnapshot</code></a></p>
2017
- <h6 id="inherited-from-26">Inherited from</h6>
2018
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#snapshot-6"><code>snapshot</code></a></p>
2043
+ <h6 id="returns-55">Returns</h6>
2044
+ <p><a href="#sandboxsnapshot"><code>SandboxSnapshot</code></a></p>
2045
+ <h6 id="inherited-from-28">Inherited from</h6>
2046
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#snapshot"><code>snapshot</code></a></p>
2019
2047
  <p><a id="withauth-4"></a></p>
2020
2048
  <h5 id="withauth-2">withAuth()</h5>
2021
2049
  <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:#61AFEF">withAuth</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">auth</span><span style="color:#ABB2BF">: {</span></span>
@@ -2030,7 +2058,7 @@ coexist for one sandbox; data is shared.</p>
2030
2058
  optional custom claims). Passing <code>undefined</code> is a deliberate
2031
2059
  error — say <code>withAuth(null)</code> for anonymous so the call site is
2032
2060
  unambiguous.</p>
2033
- <h6 id="parameters-29">Parameters</h6>
2061
+ <h6 id="parameters-27">Parameters</h6>
2034
2062
  <div class="table-scroll">
2035
2063
 
2036
2064
 
@@ -2053,14 +2081,14 @@ unambiguous.</p>
2053
2081
 
2054
2082
 
2055
2083
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
2056
- <h6 id="returns-55">Returns</h6>
2057
- <p><a href="#sandboxcontext"><code>SandboxContext</code></a></p>
2084
+ <h6 id="returns-56">Returns</h6>
2085
+ <p><a href="/docs/pyric-firestore-reference-api/#sandboxcontext"><code>SandboxContext</code></a></p>
2058
2086
  <h6 id="example-3">Example</h6>
2059
2087
  <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">const</span><span style="color:#E5C07B"> sandbox</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> initializeSandbox</span><span style="color:#ABB2BF">();</span></span>
2060
2088
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> dbAlice</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> getFirestore</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">sandbox</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">withAuth</span><span style="color:#ABB2BF">({ </span><span style="color:#E06C75">uid</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'alice'</span><span style="color:#ABB2BF"> }));</span></span>
2061
2089
  <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> dbAnon</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> getFirestore</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">sandbox</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">withAuth</span><span style="color:#ABB2BF">(</span><span style="color:#D19A66">null</span><span style="color:#ABB2BF">));</span></span></code></pre>
2062
- <h6 id="inherited-from-27">Inherited from</h6>
2063
- <p><a href="#sandbox-3"><code>Sandbox</code></a>.<a href="#withauth-6"><code>withAuth</code></a></p>
2090
+ <h6 id="inherited-from-29">Inherited from</h6>
2091
+ <p><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a>.<a href="/docs/pyric-firestore-reference-api/#withauth"><code>withAuth</code></a></p>
2064
2092
  <hr>
2065
2093
  <p><a id="remotesandboxchannel-1"></a></p>
2066
2094
  <h3 id="remotesandboxchannel">RemoteSandboxChannel</h3>
@@ -2081,7 +2109,7 @@ discriminated unions live in <code>@pyric/cli</code>’ worker protocol); caller
2081
2109
  rejects with an <code>Error</code> carrying a <code>.code</code> (including the fail-fast
2082
2110
  “no browser tab is connected — open <serve url="">” guidance when no
2083
2111
  peer is registered).</serve></p>
2084
- <h6 id="parameters-30">Parameters</h6>
2112
+ <h6 id="parameters-28">Parameters</h6>
2085
2113
  <div class="table-scroll">
2086
2114
 
2087
2115
 
@@ -2096,7 +2124,7 @@ peer is registered).</serve></p>
2096
2124
 
2097
2125
 
2098
2126
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>op</code></td><td align="left">{ <code>method</code>: <code>string</code>; } &#x26; <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr></tbody></table></div>
2099
- <h6 id="returns-56">Returns</h6>
2127
+ <h6 id="returns-57">Returns</h6>
2100
2128
  <p><code>Promise</code>&#x3C;<code>unknown</code>></p>
2101
2129
  <p><a id="subscribe-1"></a></p>
2102
2130
  <h5 id="subscribe">subscribe()</h5>
@@ -2113,7 +2141,7 @@ peer is registered).</serve></p>
2113
2141
  value (initial + updates — and re-delivered fresh after peer
2114
2142
  replacement); an establishment failure routes to <code>onError</code> instead.
2115
2143
  Returns the unsubscribe function.</p>
2116
- <h6 id="parameters-31">Parameters</h6>
2144
+ <h6 id="parameters-29">Parameters</h6>
2117
2145
  <div class="table-scroll">
2118
2146
 
2119
2147
 
@@ -2136,9 +2164,9 @@ Returns the unsubscribe function.</p>
2136
2164
 
2137
2165
 
2138
2166
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>sub</code></td><td align="left">{ <code>target</code>: <code>unknown</code>; } &#x26; <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>onSnap</code></td><td align="left">(<code>value</code>: <code>unknown</code>) => <code>void</code></td></tr><tr><td align="left"><code>onError?</code></td><td align="left">(<code>err</code>: <code>Error</code> &#x26; { <code>code</code>: <code>string</code>; }) => <code>void</code></td></tr></tbody></table></div>
2139
- <h6 id="returns-57">Returns</h6>
2140
- <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2141
2167
  <h6 id="returns-58">Returns</h6>
2168
+ <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2169
+ <h6 id="returns-59">Returns</h6>
2142
2170
  <p><code>void</code></p>
2143
2171
  <hr>
2144
2172
  <p><a id="remotesandboxfactoryoptions"></a></p>
@@ -2404,532 +2432,15 @@ subscriber can’t change rule semantics or hide other events.</p>
2404
2432
 
2405
2433
 
2406
2434
 
2407
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at-2"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">Wall-clock at op start, ms since epoch.</td></tr><tr><td align="left"><a id="auth-5"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="detail"></a> <code>detail?</code></td><td align="left">{ <code>admin?</code>: <code>boolean</code>; } &#x26; <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">Free-form operation metadata. <code>admin: true</code> marks a rules-bypassing setup/admin operation so fixture tooling can exclude it from protected behavior while still preserving it as replay context.</td></tr><tr><td align="left"><a id="evalms"></a> <code>evalMs</code></td><td align="left"><code>number</code></td><td align="left">Wall-clock duration of the simulator.simulate(…) call, in ms.</td></tr><tr><td align="left"><a id="evaluatedrule"></a> <code>evaluatedRule?</code></td><td align="left"><code>EvaluatedRuleInfo</code></td><td align="left">The DECIDING rule’s verdict + 1-indexed source line + full sub-expression trace, projected from the simulator’s structured <code>RuleEvaluation</code> (additive: present on `result: ‘allow'</td></tr><tr><td align="left"><a id="groupid"></a> <code>groupId?</code></td><td align="left"><code>string</code></td><td align="left">Shared across ops in one batch or transaction. Opaque to consumers.</td></tr><tr><td align="left"><a id="groupkind"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code> | <code>"batch"</code></td><td align="left">Disambiguates `origin: ‘transaction'</td></tr><tr><td align="left"><a id="id-2"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">Unique within a sandbox process. Useful for React list keys.</td></tr><tr><td align="left"><a id="kind-1"></a> <code>kind</code></td><td align="left"><code>"request"</code></td><td align="left">Discriminator.</td></tr><tr><td align="left"><a id="matchedrule"></a> <code>matchedRule?</code></td><td align="left">{ <code>operations</code>: <code>string</code>[]; <code>ruleIndex</code>: <code>number</code>; }</td><td align="left">Parsed from the simulator’s “Rule #N → …” debug line. Absent when no rule matched (e.g. no allow rules at the path — implicit deny).</td></tr><tr><td align="left"><code>matchedRule.operations</code></td><td align="left"><code>string</code>[]</td><td align="left">-</td></tr><tr><td align="left"><code>matchedRule.ruleIndex</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="method-1"></a> <code>method</code></td><td align="left"><code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code> | <code>"delete"</code> | <code>"set"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="origin"></a> <code>origin</code></td><td align="left"><code>"user"</code> | <code>"listener"</code> | <code>"transaction"</code> | <code>"batch"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="path-1"></a> <code>path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="reasons-2"></a> <code>reasons</code></td><td align="left"><code>string</code>[]</td><td align="left">Simulator debug messages — the per-rule trace (<code>Rule #0 (read) → ALLOW</code>). Same shape as <code>DenialEvent.reasons</code> so consumer code can share rendering.</td></tr><tr><td align="left"><a id="request-2"></a> <code>request?</code></td><td align="left">{ <code>resourceData?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; }</td><td align="left">Proposed write payload, for create/update/set. Absent on reads + delete. Pre-resolution: <code>FieldValue.*</code> sentinels are preserved as their marker shapes (<code>{ __type: 'serverTimestamp' }</code>, etc.) so the replay engine can re-resolve them. The rule engine evaluated against the resolved form internally; that resolved form lives on <a href="#nextstate-1">WriteSandboxEvent.nextState</a>, not here.</td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><a id="resourceafter"></a> <code>resourceAfter?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">Projected document state after the write. Absent on reads.</td></tr><tr><td align="left"><code>resourceAfter.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>resourceAfter.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="resourcebefore"></a> <code>resourceBefore?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">Existing document state before the write (or read target for get).</td></tr><tr><td align="left"><code>resourceBefore.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>resourceBefore.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="result-1"></a> <code>result</code></td><td align="left"><code>"allow"</code> | <code>"deny"</code> | <code>"unsupported"</code></td><td align="left"><code>'unsupported'</code> fires when the simulator hit an unmodelled feature and the sandbox upgraded it (today: thrown as SimulatorUnsupportedError, surfaced here as a discrete result so the panel can show it distinctly from a real denial).</td></tr><tr><td align="left"><a id="rulesdisposition"></a> <code>rulesDisposition?</code></td><td align="left"><a href="#rulesdisposition-2"><code>RulesDisposition</code></a></td><td align="left">Canonical statement of whether Security Rules evaluated this request. Added by the sandbox event recorder when an older emitter omits it.</td></tr><tr><td align="left"><a id="triggeredby"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path</code>: <code>string</code>; }</td><td align="left">For listener re-evals: the originating user op that triggered this re-evaluation. Absent on the initial-snapshot fire.</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
2408
- <hr>
2409
- <p><a id="sandbox-3"></a></p>
2410
- <h3 id="sandbox">Sandbox</h3>
2411
- <p>A Firebase sandbox an isolated environment with one auth identity.</p>
2412
- <p>Created via <code>initializeSandbox(config)</code>. Use <code>fork({ auth })</code> to
2413
- derive a new sandbox with a different identity that shares the
2414
- underlying environment (rules, data, state). Fork is the only
2415
- identity-switching mechanism — there are no per-op auth overrides
2416
- and no in-place mutation.</p>
2417
- <h4 id="extended-by">Extended by</h4>
2418
- <ul>
2419
- <li><a href="#localsandbox"><code>LocalSandbox</code></a></li>
2420
- <li><a href="#remotesandbox"><code>RemoteSandbox</code></a></li>
2421
- </ul>
2422
- <h4 id="properties-17">Properties</h4>
2423
- <div class="table-scroll">
2424
-
2425
-
2426
-
2427
-
2428
-
2429
-
2430
-
2431
-
2432
-
2433
-
2434
-
2435
-
2436
-
2437
-
2438
-
2439
-
2440
-
2441
-
2442
-
2443
-
2444
-
2445
-
2446
-
2447
-
2448
-
2449
-
2450
-
2451
-
2452
-
2453
-
2454
-
2455
-
2456
-
2457
-
2458
- <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="admin-2"></a> <code>admin</code></td><td align="left"><code>readonly</code></td><td align="left"><code>SandboxAdmin</code></td><td align="left">Admin-plane access (rule-bypass reads). Identity-agnostic by design — admin reads aren’t gated on auth, so they live on the sandbox, not on a context. See SandboxAdmin.</td></tr><tr><td align="left"><a id="currentuser-2"></a> <code>currentUser</code></td><td align="left"><code>public</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Current authenticated user across the sandbox. Mutated by <code>pyric/auth</code>’s <code>signInAnonymously</code> / <code>signInWithEmailAndPassword</code> / <code>signOut</code> / <code>sandbox.setUser</code>. Read per-call by service factories (e.g. a future <code>getFirestore(sandbox)</code> overload) so they see auth state changes without re-binding handles. Defaults to <code>null</code> (anonymous / signed out). <strong>Independent of <code>withAuth({uid})</code></strong> — <code>withAuth</code> still produces a frozen <a href="#sandboxcontext">SandboxContext</a> that carries its own identity for the runner’s test code (the existing pattern: explicit identity per service call). <code>currentUser</code> exists for the <code>pyric/auth</code> mirror, where consumer app code drives identity through a stateful <code>Auth</code> handle rather than naming it per call.</td></tr><tr><td align="left"><code>currentUser.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>currentUser.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
2459
- <h4 id="methods-8">Methods</h4>
2460
- <p><a id="clearpersistence-4"></a></p>
2461
- <h5 id="clearpersistence-2">clearPersistence()</h5>
2462
- <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:#61AFEF">clearPersistence</span><span style="color:#ABB2BF">(): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
2463
- <p>Wipe the persisted blob for this sandbox’s <code>key</code>. In-memory state
2464
- is left intact — call <code>reset()</code> if you want both. Useful for
2465
- “sign out and forget” flows.</p>
2466
- <p>No-op when persistence is not enabled.</p>
2467
- <h6 id="returns-59">Returns</h6>
2468
- <p><code>Promise</code>&#x3C;<code>void</code>></p>
2469
- <p><a id="dispose-6"></a></p>
2470
- <h5 id="dispose-3">dispose()</h5>
2471
- <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:#61AFEF">dispose</span><span style="color:#ABB2BF">(): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2472
- <p>Tear down listener registries on this sandbox’s environment without
2473
- replacing it. Use this when you’re about to discard the sandbox
2474
- itself (e.g. <code>runner.reseed()</code> builds a fresh sandbox rather than
2475
- calling <code>reset()</code>) and want to drop callback references on the
2476
- outgoing instance defensively. Idempotent. Does not touch data.</p>
2477
- <h6 id="returns-60">Returns</h6>
2478
- <p><code>void</code></p>
2479
- <p><a id="enablepersistence-4"></a></p>
2480
- <h5 id="enablepersistence-2">enablePersistence()</h5>
2481
- <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:#61AFEF">enablePersistence</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">options</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">SandboxPersistenceOptions</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
2482
- <p>Persist the sandbox’s data to a backend and restore it on next
2483
- <code>enablePersistence</code> call. The default <code>'indexedDB'</code> backend turns
2484
- the sandbox into the host page’s local Firestore — writes flush
2485
- automatically and a fresh <code>initializeSandbox()</code> rehydrates from
2486
- the prior session.</p>
2487
- <p>Restoration happens before the promise resolves; awaiting this
2488
- call is sufficient to guarantee in-memory state matches the
2489
- persisted blob.</p>
2490
- <p>Idempotent across the same <code>key</code> — calling twice in one process
2491
- is a no-op on the second call. Different keys are rejected as an
2492
- error (a sandbox can persist to at most one backend at a time).</p>
2493
- <p>Listener semantics: every write event the sandbox emits triggers
2494
- a debounced flush (default 250ms). Browser hosts additionally
2495
- flush on <code>beforeunload</code> so a page navigation doesn’t lose the
2496
- tail of the debounce window.</p>
2497
- <p>See <a href="#sandboxpersistenceoptions">SandboxPersistenceOptions</a> for backend selection and
2498
- tuning.</p>
2499
- <h6 id="parameters-32">Parameters</h6>
2500
- <div class="table-scroll">
2501
-
2502
-
2503
-
2504
-
2505
-
2506
-
2507
-
2508
-
2509
-
2510
-
2511
-
2512
-
2513
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>options</code></td><td align="left"><a href="#sandboxpersistenceoptions"><code>SandboxPersistenceOptions</code></a></td></tr></tbody></table></div>
2514
- <h6 id="returns-61">Returns</h6>
2515
- <p><code>Promise</code>&#x3C;<code>void</code>></p>
2516
- <p><a id="enabletabsync-4"></a></p>
2517
- <h5 id="enabletabsync-2">enableTabSync()</h5>
2518
- <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:#61AFEF">enableTabSync</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">options</span><span style="color:#C678DD">?:</span><span style="color:#E06C75"> TabSyncOptions</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2519
- <p>Enable cross-tab realtime sync via <code>BroadcastChannel</code>. A write in
2520
- this tab will propagate to every OTHER tab of the same origin that
2521
- also called <code>enableTabSync</code>, causing their <code>onSnapshot</code> listeners to
2522
- re-evaluate — restoring production’s cross-client realtime behavior.</p>
2523
- <p><strong>Opt-in, OFF by default.</strong> Firestore only (RTDB is a follow-on).</p>
2524
- <p>Returns a disable function. Calling it removes the <code>onEvent</code>
2525
- subscription, the channel message listener, and closes the channel
2526
- (when it was created internally). After disable, no further propagation
2527
- occurs in either direction.</p>
2528
- <p><strong>Multi-writer note:</strong> concurrent writes from two tabs to the same doc
2529
- produce last-write-wins divergence — there is no conflict resolution.
2530
- The intended model is one active writer (one user, one tab) with
2531
- observers in other tabs; this covers the overwhelming majority of
2532
- local development scenarios.</p>
2533
- <h6 id="parameters-33">Parameters</h6>
2534
- <div class="table-scroll">
2535
-
2536
-
2537
-
2538
-
2539
-
2540
-
2541
-
2542
-
2543
-
2544
-
2545
-
2546
-
2547
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>options?</code></td><td align="left"><a href="#tabsyncoptions"><code>TabSyncOptions</code></a></td></tr></tbody></table></div>
2548
- <h6 id="returns-62">Returns</h6>
2549
- <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2550
- <h6 id="returns-63">Returns</h6>
2551
- <p><code>void</code></p>
2552
- <h6 id="see-3">See</h6>
2553
- <p><a href="#tabsyncoptions">TabSyncOptions</a> for channel injection (tests) and originId.</p>
2554
- <h6 id="example-4">Example</h6>
2555
- <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:#7F848E;font-style:italic">// In every tab that should participate in realtime:</span></span>
2556
- <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> sandbox</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> initializeSandbox</span><span style="color:#ABB2BF">();</span></span>
2557
- <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> disableSync</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> sandbox</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">enableTabSync</span><span style="color:#ABB2BF">();</span></span>
2558
- <span class="line"><span style="color:#7F848E;font-style:italic">// Later, to stop syncing:</span></span>
2559
- <span class="line"><span style="color:#61AFEF">disableSync</span><span style="color:#ABB2BF">();</span></span></code></pre>
2560
- <p><a id="flush-6"></a></p>
2561
- <h5 id="flush-3">flush()</h5>
2562
- <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:#61AFEF">flush</span><span style="color:#ABB2BF">(): </span><span style="color:#E5C07B">Promise</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#C678DD">void</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span></code></pre>
2563
- <p>Force a snapshot to the configured persistence backend right now.
2564
- Useful before a manual navigation, or in tests that need
2565
- deterministic ordering against the debounce window. Resolves once
2566
- the write hits the backend.</p>
2567
- <p>Throws if persistence is not enabled.</p>
2568
- <h6 id="returns-64">Returns</h6>
2569
- <p><code>Promise</code>&#x3C;<code>void</code>></p>
2570
- <p><a id="history-4"></a></p>
2571
- <h5 id="history-2">history()</h5>
2572
- <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:#61AFEF">history</span><span style="color:#ABB2BF">(): </span><span style="color:#E06C75">SandboxEvent</span><span style="color:#ABB2BF">[];</span></span></code></pre>
2573
- <p>Every <a href="#sandboxevent">SandboxEvent</a> this sandbox has emitted since init or
2574
- the last <code>reset()</code>. Returns a defensive copy.</p>
2575
- <p>Use this for replay: hand the array to <code>replay(events, rules)</code>
2576
- from <code>pyric/sandbox</code> and the engine re-issues every
2577
- captured write against a fresh sandbox.</p>
2578
- <p>Unlike <a href="#onevent-4">onEvent</a> (live stream from the moment of subscribe),
2579
- <code>history()</code> returns <em>every</em> event the sandbox has seen — useful
2580
- for consumers that attach late (e.g., loading a saved session
2581
- before subscribing) or that need a snapshot at a particular moment.</p>
2582
- <p><code>reset()</code> and <code>dispose()</code> each append a closing <code>session_boundary</code>
2583
- event; <code>reset()</code> then clears the history. Consumers that took a
2584
- snapshot <em>before</em> reset retain the boundary in their copy.</p>
2585
- <h6 id="returns-65">Returns</h6>
2586
- <p><a href="#sandboxevent"><code>SandboxEvent</code></a>[]</p>
2587
- <p><a id="loadsnapshot-4"></a></p>
2588
- <h5 id="loadsnapshot-2">loadSnapshot()</h5>
2589
- <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:#61AFEF">loadSnapshot</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">data</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">SandboxSnapshot</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2590
- <p>CLOBBER-restore the sandbox’s entire state from a prior <a href="#snapshot-6">snapshot</a>:
2591
- <code>reset()</code> (clears firestore + the signed-in session), then rebuild firestore
2592
- from <code>data</code> and restore each registered service. This is a TOTAL replace —
2593
- documents absent from <code>data</code> do NOT survive — and is the counterpart to
2594
- <a href="#snapshot-6">snapshot</a>. It is what makes “transfer (clobber) one instance’s data
2595
- into another” and named-branch switching possible.</p>
2596
- <p>Fires a <code>session_boundary</code> (reset phase), re-evaluates live listeners against
2597
- the loaded state, and the next persistence flush writes the loaded state.
2598
- Services present in <code>data</code> but not currently registered are skipped (a
2599
- snapshot taken via <a href="#snapshot-6">snapshot</a> always includes every registered
2600
- service, so this only affects cross-instance imports from a sandbox that had
2601
- a service this one lacks).</p>
2602
- <h6 id="parameters-34">Parameters</h6>
2603
- <div class="table-scroll">
2604
-
2605
-
2606
-
2607
-
2608
-
2609
-
2610
-
2611
-
2612
-
2613
-
2614
-
2615
-
2616
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>data</code></td><td align="left"><a href="#sandboxsnapshot-2"><code>SandboxSnapshot</code></a></td></tr></tbody></table></div>
2617
- <h6 id="returns-66">Returns</h6>
2618
- <p><code>void</code></p>
2619
- <p><a id="oncurrentuserchanged-4"></a></p>
2620
- <h5 id="oncurrentuserchanged-2">onCurrentUserChanged()</h5>
2621
- <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:#61AFEF">onCurrentUserChanged</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">cb</span><span style="color:#ABB2BF">: (</span><span style="color:#E06C75;font-style:italic">user</span><span style="color:#ABB2BF">: {</span></span>
2622
- <span class="line"><span style="color:#E06C75"> token</span><span style="color:#C678DD">?</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">Record</span><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E5C07B">string</span><span style="color:#ABB2BF">, </span><span style="color:#E5C07B">unknown</span><span style="color:#ABB2BF">>;</span></span>
2623
- <span class="line"><span style="color:#E06C75"> uid</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">string</span><span style="color:#ABB2BF">;</span></span>
2624
- <span class="line"><span style="color:#ABB2BF">}) </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2625
- <p>Subscribe to <code>currentUser</code> changes. Fires on every mutation —
2626
- sign-in, sign-out, user swap. Does NOT fire on subscribe.</p>
2627
- <p>Survives <code>reset()</code> and <code>dispose()</code> only as a no-op: a disposed
2628
- sandbox emits nothing further; a reset sandbox clears
2629
- <code>currentUser</code> to <code>null</code> (and fires the change) before swapping
2630
- the env.</p>
2631
- <p>Returns an unsubscribe function. Listener errors are swallowed —
2632
- subscribers are observational, the sandbox does not propagate
2633
- their errors.</p>
2634
- <h6 id="parameters-35">Parameters</h6>
2635
- <div class="table-scroll">
2636
-
2637
-
2638
-
2639
-
2640
-
2641
-
2642
-
2643
-
2644
-
2645
-
2646
-
2647
-
2648
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>cb</code></td><td align="left">(<code>user</code>: { <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }) => <code>void</code></td></tr></tbody></table></div>
2649
- <h6 id="returns-67">Returns</h6>
2650
- <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2651
- <h6 id="returns-68">Returns</h6>
2652
- <p><code>void</code></p>
2653
- <p><a id="onevent-4"></a></p>
2654
- <h5 id="onevent-2">onEvent()</h5>
2655
- <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:#61AFEF">onEvent</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">cb</span><span style="color:#ABB2BF">: (</span><span style="color:#E06C75;font-style:italic">event</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">SandboxEvent</span><span style="color:#ABB2BF">) </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2656
- <p>Subscribe to every event the sandbox emits — see <a href="#sandboxevent">SandboxEvent</a>
2657
- for the discriminated-union shape. One subscription covers
2658
- request/denial/snapshot-error/listener-lifecycle/session-boundary;
2659
- filter on <code>event.kind</code> to recover individual streams.</p>
2660
- <p>Replaces the prior three-channel surface (<code>onRequest</code> / <code>onDenial</code>
2661
- / <code>onSnapshotError</code>) — see issue #307. Filter cookbook:</p>
2662
- <ul>
2663
- <li>All denials: <code>event.kind === 'request' &#x26;&#x26; event.result === 'deny'</code></li>
2664
- <li>Stream errors: <code>event.kind === 'listener_errored'</code></li>
2665
- <li>Per-op traffic: <code>event.kind === 'request'</code></li>
2666
- </ul>
2667
- <p>Survives <code>sandbox.reset()</code> — the subscription is held on the
2668
- sandbox, not on the underlying environment. A <code>session_boundary</code>
2669
- event with <code>phase: 'reset'</code> fires before the env swap so consumers
2670
- can segment their stream.</p>
2671
- <p>Returns an unsubscribe function. Listener errors are swallowed so a
2672
- faulty subscriber can’t change rule semantics or hide other events.
2673
- Both synchronous throws and rejected Promises from async callbacks
2674
- are silently discarded — subscribers are <strong>observational</strong>, the
2675
- sandbox doesn’t await them and doesn’t propagate their errors.</p>
2676
- <h6 id="parameters-36">Parameters</h6>
2677
- <div class="table-scroll">
2678
-
2679
-
2680
-
2681
-
2682
-
2683
-
2684
-
2685
-
2686
-
2687
-
2688
-
2689
-
2690
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>cb</code></td><td align="left">(<code>event</code>: <a href="#sandboxevent"><code>SandboxEvent</code></a>) => <code>void</code></td></tr></tbody></table></div>
2691
- <h6 id="returns-69">Returns</h6>
2692
- <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2693
- <h6 id="returns-70">Returns</h6>
2694
- <p><code>void</code></p>
2695
- <p><a id="registerpersistableservice-4"></a></p>
2696
- <h5 id="registerpersistableservice-2">registerPersistableService()</h5>
2697
- <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:#61AFEF">registerPersistableService</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">name</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">hooks</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">PersistableService</span><span style="color:#ABB2BF">): () </span><span style="color:#C678DD">=></span><span style="color:#C678DD"> void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2698
- <p>Register a service (auth, storage, …) as a persistence participant.
2699
- The sandbox calls <code>hooks.snapshot()</code> on every flush and
2700
- <code>hooks.restore(data)</code> on restore. If <code>hooks.subscribe</code> is provided,
2701
- the persistence controller subscribes and schedules a debounced
2702
- flush on each change — so auth-user edits flush promptly, not only
2703
- on the next Firestore write.</p>
2704
- <p>Returns an unregister function — call it if the service is torn
2705
- down before the sandbox is disposed (uncommon in practice; the
2706
- sandbox’s <code>dispose()</code> clears the registry anyway).</p>
2707
- <p>Throws <code>failed-precondition</code> when a service with the same <code>name</code> is
2708
- already registered — the auth package registers <code>'auth'</code> once when
2709
- <code>getAuth(sandbox)</code> first creates a backend, so accidental double-
2710
- registration is a caller bug, not a no-op.</p>
2711
- <p><strong>Advanced / internal API.</strong> Service packages (auth, storage) call
2712
- this when they first attach to a sandbox. Consumer app code should
2713
- not need to call this directly.</p>
2714
- <h6 id="parameters-37">Parameters</h6>
2715
- <div class="table-scroll">
2716
-
2717
-
2718
-
2719
-
2720
-
2721
-
2722
-
2723
-
2724
-
2725
-
2726
-
2727
-
2728
-
2729
-
2730
-
2731
-
2732
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>name</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>hooks</code></td><td align="left"><a href="#persistableservice"><code>PersistableService</code></a></td></tr></tbody></table></div>
2733
- <h6 id="returns-71">Returns</h6>
2734
- <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2735
- <h6 id="returns-72">Returns</h6>
2736
- <p><code>void</code></p>
2737
- <p><a id="reset-4"></a></p>
2738
- <h5 id="reset-2">reset()</h5>
2739
- <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:#61AFEF">reset</span><span style="color:#ABB2BF">(): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
2740
- <p>Reset the underlying environment to a fresh state — wipes data,
2741
- rules, and any service-specific configuration.</p>
2742
- <p>Snapshot listeners attached to the OLD environment are dropped at
2743
- the swap — they can’t survive because their target docs have been
2744
- wiped. <code>onEvent</code> subscribers DO survive — the registry lives on
2745
- the sandbox, and a <code>session_boundary</code> event with <code>phase: 'reset'</code>
2746
- fires before the swap so subscribers know the rollover happened.
2747
- Existing <a href="#sandboxcontext">SandboxContext</a>s continue to work — their sandbox
2748
- reference is stable; subsequent operations resolve to the new env.</p>
2749
- <h6 id="returns-73">Returns</h6>
2750
- <p><code>void</code></p>
2751
- <p><a id="runwithprovenance-4"></a></p>
2752
- <h5 id="runwithprovenance-2">runWithProvenance()?</h5>
2753
- <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:#E06C75">optional</span><span style="color:#61AFEF"> runWithProvenance</span><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E5C07B">T</span><span style="color:#ABB2BF">>(</span><span style="color:#E06C75">provenance</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">EventProvenance</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">fn</span><span style="color:#ABB2BF">: () </span><span style="color:#C678DD">=></span><span style="color:#E5C07B"> T</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">T</span><span style="color:#ABB2BF">;</span></span></code></pre>
2754
- <p>Run <code>fn</code> with ambient <a href="#eventprovenance">EventProvenance</a> defaults: every event
2755
- emitted SYNCHRONOUSLY during <code>fn</code> that doesn’t already carry a
2756
- provenance field (on the event itself or via an explicit per-emit
2757
- override) is stamped with these values instead of the global
2758
- defaults. This is the mechanical “who issued this op” seam the
2759
- serve worker uses to tag Studio-issued ops (<code>actor: { kind: 'studio' }</code>) and to stamp the auth lens an op ran under
2760
- (<code>authLens</code>) — declared by the caller that issues the op, never
2761
- inferred from the op’s shape.</p>
2762
- <p>SYNCHRONOUS WINDOW: the ambient values apply only until <code>fn</code>
2763
- returns (for an async <code>fn</code>, its synchronous prefix — which covers
2764
- the local environment’s rules eval + event emission, since those
2765
- run before the op’s promise is handed back). Work an op DEFERS
2766
- (snapshot-listener deliveries and re-evals drain on a microtask,
2767
- off-stack) is intentionally OUTSIDE the window: a listener re-eval
2768
- belongs to the listener’s owner, not to whoever’s write triggered
2769
- it. Nested calls stack — the innermost window wins per field, and
2770
- each window restores the previous one on exit (including on throw).</p>
2771
- <p>OPTIONAL because remote sandbox proxies can’t provide an ambient
2772
- emit window (events are emitted in the worker they front). Callers
2773
- spell <code>sandbox.runWithProvenance?.(prov, fn) ?? fn()</code>.</p>
2774
- <h6 id="type-parameters-2">Type Parameters</h6>
2775
- <div class="table-scroll">
2776
-
2777
-
2778
-
2779
-
2780
-
2781
-
2782
-
2783
-
2784
-
2785
-
2786
- <table><thead><tr><th align="left">Type Parameter</th></tr></thead><tbody><tr><td align="left"><code>T</code></td></tr></tbody></table></div>
2787
- <h6 id="parameters-38">Parameters</h6>
2788
- <div class="table-scroll">
2789
-
2790
-
2791
-
2792
-
2793
-
2794
-
2795
-
2796
-
2797
-
2798
-
2799
-
2800
-
2801
-
2802
-
2803
-
2804
-
2805
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>provenance</code></td><td align="left"><a href="#eventprovenance"><code>EventProvenance</code></a></td></tr><tr><td align="left"><code>fn</code></td><td align="left">() => <code>T</code></td></tr></tbody></table></div>
2806
- <h6 id="returns-74">Returns</h6>
2807
- <p><code>T</code></p>
2808
- <p><a id="snapshot-6"></a></p>
2809
- <h5 id="snapshot-3">snapshot()</h5>
2810
- <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:#61AFEF">snapshot</span><span style="color:#ABB2BF">(): </span><span style="color:#E06C75">SandboxSnapshot</span><span style="color:#ABB2BF">;</span></span></code></pre>
2811
- <p>Capture a snapshot of every service’s state. For v1 with only
2812
- Firestore, the return value carries a <code>firestore</code> key mapping doc
2813
- paths to data. Future services will add their own keys.</p>
2814
- <h6 id="returns-75">Returns</h6>
2815
- <p><a href="#sandboxsnapshot-2"><code>SandboxSnapshot</code></a></p>
2816
- <p><a id="withauth-6"></a></p>
2817
- <h5 id="withauth-3">withAuth()</h5>
2818
- <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:#61AFEF">withAuth</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">auth</span><span style="color:#ABB2BF">: {</span></span>
2819
- <span class="line"><span style="color:#ABB2BF"> token?: </span><span style="color:#E06C75">Record</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, unknown>;</span></span>
2820
- <span class="line"><span style="color:#E06C75"> uid</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">;</span></span>
2821
- <span class="line"><span style="color:#ABB2BF">}): </span><span style="color:#E06C75">SandboxContext</span><span style="color:#ABB2BF">;</span></span></code></pre>
2822
- <p>Derive a context bound to this sandbox under the given auth
2823
- identity. Operations through services attached to the returned
2824
- context evaluate rules under that identity. Many contexts can
2825
- coexist for one sandbox; data is shared.</p>
2826
- <p><code>null</code> is anonymous; an <code>AuthState</code> object names the user (and
2827
- optional custom claims). Passing <code>undefined</code> is a deliberate
2828
- error — say <code>withAuth(null)</code> for anonymous so the call site is
2829
- unambiguous.</p>
2830
- <h6 id="parameters-39">Parameters</h6>
2831
- <div class="table-scroll">
2832
-
2833
-
2834
-
2835
-
2836
-
2837
-
2838
-
2839
-
2840
-
2841
-
2842
-
2843
-
2844
-
2845
-
2846
-
2847
-
2848
-
2849
-
2850
-
2851
-
2852
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
2853
- <h6 id="returns-76">Returns</h6>
2854
- <p><a href="#sandboxcontext"><code>SandboxContext</code></a></p>
2855
- <h6 id="example-5">Example</h6>
2856
- <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">const</span><span style="color:#E5C07B"> sandbox</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> initializeSandbox</span><span style="color:#ABB2BF">();</span></span>
2857
- <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> dbAlice</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> getFirestore</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">sandbox</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">withAuth</span><span style="color:#ABB2BF">({ </span><span style="color:#E06C75">uid</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">'alice'</span><span style="color:#ABB2BF"> }));</span></span>
2858
- <span class="line"><span style="color:#C678DD">const</span><span style="color:#E5C07B"> dbAnon</span><span style="color:#56B6C2"> =</span><span style="color:#61AFEF"> getFirestore</span><span style="color:#ABB2BF">(</span><span style="color:#E5C07B">sandbox</span><span style="color:#ABB2BF">.</span><span style="color:#61AFEF">withAuth</span><span style="color:#ABB2BF">(</span><span style="color:#D19A66">null</span><span style="color:#ABB2BF">));</span></span></code></pre>
2859
- <hr>
2860
- <p><a id="sandboxcommitevent"></a></p>
2861
- <h3 id="sandboxcommitevent">SandboxCommitEvent</h3>
2862
- <p>Canonical committed mutation event. Unlike <code>operation</code>, this fires only when
2863
- state actually changed. Replay and branch tooling should eventually consume
2864
- these service adapters instead of filtering Firestore-only <code>write</code> events.</p>
2865
- <h4 id="properties-18">Properties</h4>
2866
- <div class="table-scroll">
2867
-
2868
-
2869
-
2870
-
2871
-
2872
-
2873
-
2874
-
2875
-
2876
-
2877
-
2878
-
2879
-
2880
-
2881
-
2882
-
2883
-
2884
-
2885
-
2886
-
2887
-
2888
-
2889
-
2890
-
2891
-
2892
-
2893
-
2894
-
2895
-
2896
-
2897
-
2898
-
2899
-
2900
-
2901
-
2902
-
2903
-
2904
-
2905
-
2906
-
2907
-
2908
-
2909
-
2910
-
2911
-
2912
-
2913
-
2914
-
2915
-
2916
-
2917
-
2918
-
2919
-
2920
-
2921
-
2922
-
2923
-
2924
-
2925
-
2926
-
2927
-
2928
-
2929
-
2930
-
2931
-
2932
-
2435
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at-2"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">Wall-clock at op start, ms since epoch.</td></tr><tr><td align="left"><a id="auth-5"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="detail"></a> <code>detail?</code></td><td align="left">{ <code>admin?</code>: <code>boolean</code>; } &#x26; <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">Free-form operation metadata. <code>admin: true</code> marks a rules-bypassing setup/admin operation so fixture tooling can exclude it from protected behavior while still preserving it as replay context.</td></tr><tr><td align="left"><a id="evalms"></a> <code>evalMs</code></td><td align="left"><code>number</code></td><td align="left">Wall-clock duration of the simulator.simulate(…) call, in ms.</td></tr><tr><td align="left"><a id="evaluatedrule"></a> <code>evaluatedRule?</code></td><td align="left"><a href="/docs/pyric-rules-reference-api/#evaluatedruleinfo"><code>EvaluatedRuleInfo</code></a></td><td align="left">The DECIDING rule’s verdict + 1-indexed source line + full sub-expression trace, projected from the simulator’s structured <code>RuleEvaluation</code> (additive: present on `result: ‘allow'</td></tr><tr><td align="left"><a id="groupid"></a> <code>groupId?</code></td><td align="left"><code>string</code></td><td align="left">Shared across ops in one batch or transaction. Opaque to consumers.</td></tr><tr><td align="left"><a id="groupkind-1"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code> | <code>"batch"</code></td><td align="left">Disambiguates `origin: ‘transaction'</td></tr><tr><td align="left"><a id="id-2"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">Unique within a sandbox process. Useful for React list keys.</td></tr><tr><td align="left"><a id="kind-1"></a> <code>kind</code></td><td align="left"><code>"request"</code></td><td align="left">Discriminator.</td></tr><tr><td align="left"><a id="matchedrule"></a> <code>matchedRule?</code></td><td align="left">{ <code>operations</code>: <code>string</code>[]; <code>ruleIndex</code>: <code>number</code>; }</td><td align="left">Parsed from the simulator’s “Rule #N → …” debug line. Absent when no rule matched (e.g. no allow rules at the path — implicit deny).</td></tr><tr><td align="left"><code>matchedRule.operations</code></td><td align="left"><code>string</code>[]</td><td align="left">-</td></tr><tr><td align="left"><code>matchedRule.ruleIndex</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="method-1"></a> <code>method</code></td><td align="left"><code>"delete"</code> | <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code> | <code>"set"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="origin"></a> <code>origin</code></td><td align="left"><code>"user"</code> | <code>"transaction"</code> | <code>"listener"</code> | <code>"batch"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="path-1"></a> <code>path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="reasons-2"></a> <code>reasons</code></td><td align="left"><code>string</code>[]</td><td align="left">Simulator debug messages — the per-rule trace (<code>Rule #0 (read) → ALLOW</code>). Same shape as <code>DenialEvent.reasons</code> so consumer code can share rendering.</td></tr><tr><td align="left"><a id="request-2"></a> <code>request?</code></td><td align="left">{ <code>resourceData?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; }</td><td align="left">Proposed write payload, for create/update/set. Absent on reads + delete. Pre-resolution: <code>FieldValue.*</code> sentinels are preserved as their marker shapes (<code>{ __type: 'serverTimestamp' }</code>, etc.) so the replay engine can re-resolve them. The rule engine evaluated against the resolved form internally; that resolved form lives on <a href="#nextstate-1">WriteSandboxEvent.nextState</a>, not here.</td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><a id="resourceafter"></a> <code>resourceAfter?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">Projected document state after the write. Absent on reads.</td></tr><tr><td align="left"><code>resourceAfter.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>resourceAfter.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="resourcebefore"></a> <code>resourceBefore?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">Existing document state before the write (or read target for get).</td></tr><tr><td align="left"><code>resourceBefore.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>resourceBefore.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="result-1"></a> <code>result</code></td><td align="left"><code>"unsupported"</code> | <code>"allow"</code> | <code>"deny"</code></td><td align="left"><code>'unsupported'</code> fires when the simulator hit an unmodelled feature and the sandbox upgraded it (today: thrown as SimulatorUnsupportedError, surfaced here as a discrete result so the panel can show it distinctly from a real denial).</td></tr><tr><td align="left"><a id="rulesdisposition"></a> <code>rulesDisposition?</code></td><td align="left"><a href="#rulesdisposition-2"><code>RulesDisposition</code></a></td><td align="left">Canonical statement of whether Security Rules evaluated this request. Added by the sandbox event recorder when an older emitter omits it.</td></tr><tr><td align="left"><a id="triggeredby"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path</code>: <code>string</code>; }</td><td align="left">For listener re-evals: the originating user op that triggered this re-evaluation. Absent on the initial-snapshot fire.</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
2436
+ <hr>
2437
+ <p><a id="sandboxcommitevent"></a></p>
2438
+ <h3 id="sandboxcommitevent">SandboxCommitEvent</h3>
2439
+ <p>Canonical committed mutation event. Unlike <code>operation</code>, this fires only when
2440
+ state actually changed. Replay and branch tooling should eventually consume
2441
+ these service adapters instead of filtering Firestore-only <code>write</code> events.</p>
2442
+ <h4 id="properties-17">Properties</h4>
2443
+ <div class="table-scroll">
2933
2444
 
2934
2445
 
2935
2446
 
@@ -2948,28 +2459,6 @@ these service adapters instead of filtering Firestore-only <code>write</code> ev
2948
2459
 
2949
2460
 
2950
2461
 
2951
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="at-3"></a> <code>at</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="auth-6"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="data"></a> <code>data?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="detail-1"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="groupid-1"></a> <code>groupId?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="groupkind-1"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code> | <code>"batch"</code></td></tr><tr><td align="left"><a id="id-3"></a> <code>id</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="kind-2"></a> <code>kind</code></td><td align="left"><code>"commit"</code></td></tr><tr><td align="left"><a id="method-2"></a> <code>method</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="nextstate"></a> <code>nextState?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="path-2"></a> <code>path?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="priorstate"></a> <code>priorState?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="replay"></a> <code>replay?</code></td><td align="left">{ <code>autoId?</code>: <code>string</code>; <code>requestTime?</code>: <code>number</code>; <code>sentinels?</code>: { <code>field</code>: <code>string</code>; <code>kind</code>: <code>string</code>; }[]; }</td></tr><tr><td align="left"><code>replay.autoId?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>replay.requestTime?</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><code>replay.sentinels?</code></td><td align="left">{ <code>field</code>: <code>string</code>; <code>kind</code>: <code>string</code>; }[]</td></tr><tr><td align="left"><a id="service-2"></a> <code>service</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td></tr></tbody></table></div>
2952
- <hr>
2953
- <p><a id="sandboxconfig"></a></p>
2954
- <h3 id="sandboxconfig">SandboxConfig</h3>
2955
- <p>Initialization config for a sandbox. All fields are optional; an
2956
- empty config produces a sandbox with no rules and no seeded data.</p>
2957
- <p><strong>No <code>auth</code> field.</strong> Identity belongs to <a href="#sandboxcontext">SandboxContext</a>, not
2958
- the sandbox. Service handles always require an explicit context.</p>
2959
- <hr>
2960
- <p><a id="sandboxcontext"></a></p>
2961
- <h3 id="sandboxcontext">SandboxContext</h3>
2962
- <p>Identity-bearing handle on a <a href="#sandbox-3">Sandbox</a>. A
2963
- <code>(sandbox, auth, operationContext)</code>
2964
- tuple — cheap to create, immutable, freely shareable. Service
2965
- factories require a <code>SandboxContext</code>; bare <code>Sandbox</code> is a type
2966
- error so every call site states identity explicitly.</p>
2967
- <p>Constructed via <code>Sandbox.withAuth(auth)</code> or chained via
2968
- <code>SandboxContext.withAuth(auth)</code>. The concrete class is exported
2969
- from <code>pyric/sandbox</code> for <code>instanceof</code> routing in service
2970
- factories; consumers don’t construct it directly.</p>
2971
- <h4 id="properties-19">Properties</h4>
2972
- <div class="table-scroll">
2973
2462
 
2974
2463
 
2975
2464
 
@@ -3010,19 +2499,6 @@ factories; consumers don’t construct it directly.</p>
3010
2499
 
3011
2500
 
3012
2501
 
3013
- <table><thead><tr><th align="left">Property</th><th align="left">Modifier</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="auth-7"></a> <code>auth</code></td><td align="left"><code>readonly</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">The identity rules evaluate under for operations through this context.</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>public</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>public</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="operationcontext-3"></a> <code>operationContext</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#operationcontext-2"><code>OperationContext</code></a></td><td align="left">Immutable provenance bound to every operation issued through this handle.</td></tr><tr><td align="left"><a id="sandbox-4"></a> <code>sandbox</code></td><td align="left"><code>readonly</code></td><td align="left"><a href="#sandbox-3"><code>Sandbox</code></a></td><td align="left">The data foundation this context operates against.</td></tr></tbody></table></div>
3014
- <h4 id="methods-9">Methods</h4>
3015
- <p><a id="withauth-8"></a></p>
3016
- <h5 id="withauth-4">withAuth()</h5>
3017
- <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:#61AFEF">withAuth</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">auth</span><span style="color:#ABB2BF">: {</span></span>
3018
- <span class="line"><span style="color:#ABB2BF"> token?: </span><span style="color:#E06C75">Record</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, unknown>;</span></span>
3019
- <span class="line"><span style="color:#E06C75"> uid</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">;</span></span>
3020
- <span class="line"><span style="color:#ABB2BF">}): </span><span style="color:#E06C75">SandboxContext</span><span style="color:#ABB2BF">;</span></span></code></pre>
3021
- <p>Derive a sibling context on the same sandbox with different auth.
3022
- Replaces auth and its lens while preserving the operation source and
3023
- optional plan identity.</p>
3024
- <h6 id="parameters-40">Parameters</h6>
3025
- <div class="table-scroll">
3026
2502
 
3027
2503
 
3028
2504
 
@@ -3043,16 +2519,27 @@ optional plan identity.</p>
3043
2519
 
3044
2520
 
3045
2521
 
3046
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
3047
- <h6 id="returns-77">Returns</h6>
3048
- <p><a href="#sandboxcontext"><code>SandboxContext</code></a></p>
2522
+
2523
+
2524
+
2525
+
2526
+
2527
+
2528
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="at-3"></a> <code>at</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="auth-6"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="data"></a> <code>data?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="detail-1"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="groupid-1"></a> <code>groupId?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="groupkind-2"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code> | <code>"batch"</code></td></tr><tr><td align="left"><a id="id-3"></a> <code>id</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="kind-2"></a> <code>kind</code></td><td align="left"><code>"commit"</code></td></tr><tr><td align="left"><a id="method-2"></a> <code>method</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="nextstate"></a> <code>nextState?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="path-2"></a> <code>path?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="priorstate"></a> <code>priorState?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="replay"></a> <code>replay?</code></td><td align="left">{ <code>autoId?</code>: <code>string</code>; <code>requestTime?</code>: <code>number</code>; <code>sentinels?</code>: { <code>field</code>: <code>string</code>; <code>kind</code>: <code>string</code>; }[]; }</td></tr><tr><td align="left"><code>replay.autoId?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>replay.requestTime?</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><code>replay.sentinels?</code></td><td align="left">{ <code>field</code>: <code>string</code>; <code>kind</code>: <code>string</code>; }[]</td></tr><tr><td align="left"><a id="service-2"></a> <code>service</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td></tr></tbody></table></div>
2529
+ <hr>
2530
+ <p><a id="sandboxconfig"></a></p>
2531
+ <h3 id="sandboxconfig">SandboxConfig</h3>
2532
+ <p>Initialization config for a sandbox. All fields are optional; an
2533
+ empty config produces a sandbox with no rules and no seeded data.</p>
2534
+ <p><strong>No <code>auth</code> field.</strong> Identity belongs to <a href="/docs/pyric-firestore-reference-api/#sandboxcontext">SandboxContext</a>, not
2535
+ the sandbox. Service handles always require an explicit context.</p>
3049
2536
  <hr>
3050
2537
  <p><a id="sandboxlistenerevent"></a></p>
3051
2538
  <h3 id="sandboxlistenerevent">SandboxListenerEvent</h3>
3052
2539
  <p>Canonical listener lifecycle/delivery event. Firestore’s existing snapshot
3053
2540
  delivery/lifecycle variants are preserved; this shape gives RTDB and future
3054
2541
  service listeners the same debuggable surface.</p>
3055
- <h4 id="properties-20">Properties</h4>
2542
+ <h4 id="properties-18">Properties</h4>
3056
2543
  <div class="table-scroll">
3057
2544
 
3058
2545
 
@@ -3162,7 +2649,7 @@ service listeners the same debuggable surface.</p>
3162
2649
 
3163
2650
 
3164
2651
 
3165
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="at-4"></a> <code>at</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="auth-8"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="detail-2"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="error-1"></a> <code>error?</code></td><td align="left">{ <code>code?</code>: <code>string</code>; <code>message</code>: <code>string</code>; <code>reasons?</code>: <code>string</code>[]; }</td></tr><tr><td align="left"><code>error.code?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>error.message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>error.reasons?</code></td><td align="left"><code>string</code>[]</td></tr><tr><td align="left"><a id="id-4"></a> <code>id</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="kind-3"></a> <code>kind</code></td><td align="left"><code>"listener"</code></td></tr><tr><td align="left"><a id="listenerid-1"></a> <code>listenerId</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="phase"></a> <code>phase</code></td><td align="left"><code>"attach"</code> | <code>"detach"</code> | <code>"delivery"</code> | <code>"suppressed"</code> | <code>"errored"</code></td></tr><tr><td align="left"><a id="reason"></a> <code>reason?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="result-2"></a> <code>result?</code></td><td align="left"><code>"allow"</code> | <code>"deny"</code> | <code>"unsupported"</code> | <code>"error"</code></td></tr><tr><td align="left"><a id="sample"></a> <code>sample?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="service-3"></a> <code>service</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td></tr><tr><td align="left"><a id="size"></a> <code>size?</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="target-1"></a> <code>target</code></td><td align="left">{ <code>kind</code>: <code>string</code>; <code>path?</code>: <code>string</code>; <code>query?</code>: <code>unknown</code>; }</td></tr><tr><td align="left"><code>target.kind</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>target.path?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>target.query?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="triggeredby-1"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path?</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>triggeredBy.path?</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
2652
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="at-4"></a> <code>at</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="auth-7"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="detail-2"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="error-1"></a> <code>error?</code></td><td align="left">{ <code>code?</code>: <code>string</code>; <code>message</code>: <code>string</code>; <code>reasons?</code>: <code>string</code>[]; }</td></tr><tr><td align="left"><code>error.code?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>error.message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>error.reasons?</code></td><td align="left"><code>string</code>[]</td></tr><tr><td align="left"><a id="id-4"></a> <code>id</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="kind-3"></a> <code>kind</code></td><td align="left"><code>"listener"</code></td></tr><tr><td align="left"><a id="listenerid-2"></a> <code>listenerId</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="phase"></a> <code>phase</code></td><td align="left"><code>"attach"</code> | <code>"detach"</code> | <code>"delivery"</code> | <code>"suppressed"</code> | <code>"errored"</code></td></tr><tr><td align="left"><a id="reason"></a> <code>reason?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="result-2"></a> <code>result?</code></td><td align="left"><code>"unsupported"</code> | <code>"error"</code> | <code>"allow"</code> | <code>"deny"</code></td></tr><tr><td align="left"><a id="sample"></a> <code>sample?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="service-3"></a> <code>service</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td></tr><tr><td align="left"><a id="size"></a> <code>size?</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="target-1"></a> <code>target</code></td><td align="left">{ <code>kind</code>: <code>string</code>; <code>path?</code>: <code>string</code>; <code>query?</code>: <code>unknown</code>; }</td></tr><tr><td align="left"><code>target.kind</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>target.path?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>target.query?</code></td><td align="left"><code>unknown</code></td></tr><tr><td align="left"><a id="triggeredby-1"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path?</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>triggeredBy.path?</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
3166
2653
  <hr>
3167
2654
  <p><a id="sandboxoperationevent"></a></p>
3168
2655
  <h3 id="sandboxoperationevent">SandboxOperationEvent</h3>
@@ -3174,7 +2661,7 @@ Storage) or by a service control plane (Auth).</p>
3174
2661
  service consumers should prefer <code>operation</code> because it carries an explicit
3175
2662
  <code>service</code> discriminator and does not require RTDB/Storage/Auth to pretend
3176
2663
  their state is a Firestore document.</p>
3177
- <h4 id="properties-21">Properties</h4>
2664
+ <h4 id="properties-19">Properties</h4>
3178
2665
  <div class="table-scroll">
3179
2666
 
3180
2667
 
@@ -3380,12 +2867,12 @@ their state is a Firestore document.</p>
3380
2867
 
3381
2868
 
3382
2869
 
3383
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at-5"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-9"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="detail-3"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><a id="durationms"></a> <code>durationMs?</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="groupid-2"></a> <code>groupId?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="groupkind-2"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code> | <code>"batch"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="id-5"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-4"></a> <code>kind</code></td><td align="left"><code>"operation"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="method-3"></a> <code>method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="origin-1"></a> <code>origin</code></td><td align="left"><code>"admin"</code> | <code>"user"</code> | <code>"listener"</code> | <code>"transaction"</code> | <code>"batch"</code> | <code>"system"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="path-3"></a> <code>path?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="reasons-3"></a> <code>reasons?</code></td><td align="left"><code>string</code>[]</td><td align="left">-</td></tr><tr><td align="left"><a id="request-3"></a> <code>request?</code></td><td align="left">{ <code>data?</code>: <code>unknown</code>; <code>query?</code>: <code>unknown</code>; <code>resourceData?</code>: <code>unknown</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>request.data?</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><code>request.query?</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><a id="resourceafter-1"></a> <code>resourceAfter?</code></td><td align="left">{ <code>data</code>: <code>unknown</code>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>resourceAfter.data</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><code>resourceAfter.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="resourcebefore-1"></a> <code>resourceBefore?</code></td><td align="left">{ <code>data</code>: <code>unknown</code>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>resourceBefore.data</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><code>resourceBefore.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="result-3"></a> <code>result</code></td><td align="left"><code>"allow"</code> | <code>"deny"</code> | <code>"unsupported"</code> | <code>"error"</code> | <code>"not-applicable"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="rules-2"></a> <code>rules?</code></td><td align="left">{ <code>engine</code>: <code>"firestore"</code> | <code>"storage"</code> | <code>"rtdb"</code>; <code>errorCode?</code>: <code>string</code>; <code>matchedPath?</code>: <code>string</code>; <code>matchedRule?</code>: <code>string</code>; <code>operations?</code>: <code>string</code>[]; <code>pathVariableBindings?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>string</code>>; <code>reason?</code>: <code>string</code>; <code>ruleIndex?</code>: <code>number</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>rules.engine</code></td><td align="left"><code>"firestore"</code> | <code>"storage"</code> | <code>"rtdb"</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.errorCode?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.matchedPath?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.matchedRule?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.operations?</code></td><td align="left"><code>string</code>[]</td><td align="left">-</td></tr><tr><td align="left"><code>rules.pathVariableBindings?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>string</code>></td><td align="left">-</td></tr><tr><td align="left"><code>rules.reason?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.ruleIndex?</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="rulesdisposition-1"></a> <code>rulesDisposition?</code></td><td align="left"><a href="#rulesdisposition-2"><code>RulesDisposition</code></a></td><td align="left">Canonical statement of whether Security Rules evaluated this operation. Service emitters may provide it directly; the recorder normalizes legacy operation shapes at the unified stream seam.</td></tr><tr><td align="left"><a id="service-4"></a> <code>service</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td><td align="left">-</td></tr><tr><td align="left"><a id="triggeredby-2"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path?</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.path?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
2870
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at-5"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-8"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="detail-3"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><a id="durationms"></a> <code>durationMs?</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="groupid-2"></a> <code>groupId?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="groupkind-3"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code> | <code>"batch"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="id-5"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-4"></a> <code>kind</code></td><td align="left"><code>"operation"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="method-3"></a> <code>method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="origin-1"></a> <code>origin</code></td><td align="left"><code>"admin"</code> | <code>"user"</code> | <code>"system"</code> | <code>"transaction"</code> | <code>"listener"</code> | <code>"batch"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="path-3"></a> <code>path?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="reasons-3"></a> <code>reasons?</code></td><td align="left"><code>string</code>[]</td><td align="left">-</td></tr><tr><td align="left"><a id="request-3"></a> <code>request?</code></td><td align="left">{ <code>data?</code>: <code>unknown</code>; <code>query?</code>: <code>unknown</code>; <code>resourceData?</code>: <code>unknown</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>request.data?</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><code>request.query?</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><a id="resourceafter-1"></a> <code>resourceAfter?</code></td><td align="left">{ <code>data</code>: <code>unknown</code>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>resourceAfter.data</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><code>resourceAfter.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="resourcebefore-1"></a> <code>resourceBefore?</code></td><td align="left">{ <code>data</code>: <code>unknown</code>; <code>exists</code>: <code>boolean</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>resourceBefore.data</code></td><td align="left"><code>unknown</code></td><td align="left">-</td></tr><tr><td align="left"><code>resourceBefore.exists</code></td><td align="left"><code>boolean</code></td><td align="left">-</td></tr><tr><td align="left"><a id="result-3"></a> <code>result</code></td><td align="left"><code>"unsupported"</code> | <code>"error"</code> | <code>"allow"</code> | <code>"deny"</code> | <code>"not-applicable"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="rules-2"></a> <code>rules?</code></td><td align="left">{ <code>engine</code>: <code>"firestore"</code> | <code>"rtdb"</code> | <code>"storage"</code>; <code>errorCode?</code>: <code>string</code>; <code>matchedPath?</code>: <code>string</code>; <code>matchedRule?</code>: <code>string</code>; <code>operations?</code>: <code>string</code>[]; <code>pathVariableBindings?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>string</code>>; <code>reason?</code>: <code>string</code>; <code>ruleIndex?</code>: <code>number</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>rules.engine</code></td><td align="left"><code>"firestore"</code> | <code>"rtdb"</code> | <code>"storage"</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.errorCode?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.matchedPath?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.matchedRule?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.operations?</code></td><td align="left"><code>string</code>[]</td><td align="left">-</td></tr><tr><td align="left"><code>rules.pathVariableBindings?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>string</code>></td><td align="left">-</td></tr><tr><td align="left"><code>rules.reason?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>rules.ruleIndex?</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="rulesdisposition-1"></a> <code>rulesDisposition?</code></td><td align="left"><a href="#rulesdisposition-2"><code>RulesDisposition</code></a></td><td align="left">Canonical statement of whether Security Rules evaluated this operation. Service emitters may provide it directly; the recorder normalizes legacy operation shapes at the unified stream seam.</td></tr><tr><td align="left"><a id="service-4"></a> <code>service</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td><td align="left">-</td></tr><tr><td align="left"><a id="triggeredby-2"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path?</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.path?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
3384
2871
  <hr>
3385
2872
  <p><a id="sandboxpersistenceoptions"></a></p>
3386
2873
  <h3 id="sandboxpersistenceoptions">SandboxPersistenceOptions</h3>
3387
- <p>Controller options. See <a href="#enablepersistence-4">Sandbox.enablePersistence</a>.</p>
3388
- <h4 id="properties-22">Properties</h4>
2874
+ <p>Controller options. See <a href="/docs/pyric-firestore-reference-api/#enablepersistence">Sandbox.enablePersistence</a>.</p>
2875
+ <h4 id="properties-20">Properties</h4>
3389
2876
  <div class="table-scroll">
3390
2877
 
3391
2878
 
@@ -3431,12 +2918,12 @@ their state is a Firestore document.</p>
3431
2918
 
3432
2919
 
3433
2920
 
3434
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="backend"></a> <code>backend?</code></td><td align="left"><code>"indexedDB"</code> | <code>"memory"</code></td><td align="left">Storage backend. <code>indexedDB</code> requires a browser environment; in non-browser hosts (Bun, Node, tests) the controller falls back to <code>memory</code> automatically unless an <code>injectedBackend</code> is supplied. Default: <code>indexedDB</code>.</td></tr><tr><td align="left"><a id="flushintervalms"></a> <code>flushIntervalMs?</code></td><td align="left"><code>number</code></td><td align="left">Debounce window before write events are flushed to the backend. Buffers rapid bursts (e.g., a batch of seed writes) into one flush. Default: 250ms.</td></tr><tr><td align="left"><a id="injectedbackend"></a> <code>injectedBackend?</code></td><td align="left"><a href="#persistencebackend"><code>PersistenceBackend</code></a></td><td align="left">Override the backend with an injected implementation. Used by tests and hosts that have their own storage adapter. When set, <code>backend</code> is ignored.</td></tr><tr><td align="left"><a id="key"></a> <code>key</code></td><td align="left"><code>string</code></td><td align="left">IndexedDB database name (or generic bucket key for other backends). Different keys persist to different storage locations — use one key per logical sandbox if you run several in parallel.</td></tr><tr><td align="left"><a id="sessionstorage"></a> <code>sessionStorage?</code></td><td align="left">{ <code>local</code>: <a href="#webstoragelike"><code>WebStorageLike</code></a>; <code>session</code>: <a href="#webstoragelike"><code>WebStorageLike</code></a>; }</td><td align="left">Optional web-storage pair for current-session persistence. When provided, the controller reads and writes the signed-in uid here (honoring the auth <code>setPersistence</code> mode) so page reloads restore the signed-in user — exactly like <code>browserLocalPersistence</code> in prod Firebase. When omitted, the user DATABASE still persists (Phase 1), but the CURRENT SESSION is not restored on reload. This is the honest no-fake-durability choice for environments where web storage isn’t available (Bun tests, Node servers, etc.). <code>local</code> maps to <code>localStorage</code> semantics (survives reload + restart); <code>session</code> maps to <code>sessionStorage</code> semantics (survives reload, cleared on tab close). The controller picks which store to write based on the auth <code>setPersistence</code> mode recorded on the backend: LOCAL → local (default; matches Firebase’s default) SESSION → session NONE → neither (uid is not stored) Both storages are read on restore (mode-agnostic — a prior session may have used a different mode). Exactly one store holds the uid at any time; a mode change migrates the uid to the new store.</td></tr><tr><td align="left"><code>sessionStorage.local</code></td><td align="left"><a href="#webstoragelike"><code>WebStorageLike</code></a></td><td align="left">-</td></tr><tr><td align="left"><code>sessionStorage.session</code></td><td align="left"><a href="#webstoragelike"><code>WebStorageLike</code></a></td><td align="left">-</td></tr></tbody></table></div>
2921
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="backend"></a> <code>backend?</code></td><td align="left"><code>"memory"</code> | <code>"indexedDB"</code></td><td align="left">Storage backend. <code>indexedDB</code> requires a browser environment; in non-browser hosts (Bun, Node, tests) the controller falls back to <code>memory</code> automatically unless an <code>injectedBackend</code> is supplied. Default: <code>indexedDB</code>.</td></tr><tr><td align="left"><a id="flushintervalms"></a> <code>flushIntervalMs?</code></td><td align="left"><code>number</code></td><td align="left">Debounce window before write events are flushed to the backend. Buffers rapid bursts (e.g., a batch of seed writes) into one flush. Default: 250ms.</td></tr><tr><td align="left"><a id="injectedbackend"></a> <code>injectedBackend?</code></td><td align="left"><a href="#persistencebackend"><code>PersistenceBackend</code></a></td><td align="left">Override the backend with an injected implementation. Used by tests and hosts that have their own storage adapter. When set, <code>backend</code> is ignored.</td></tr><tr><td align="left"><a id="key"></a> <code>key</code></td><td align="left"><code>string</code></td><td align="left">IndexedDB database name (or generic bucket key for other backends). Different keys persist to different storage locations — use one key per logical sandbox if you run several in parallel.</td></tr><tr><td align="left"><a id="sessionstorage"></a> <code>sessionStorage?</code></td><td align="left">{ <code>local</code>: <a href="#webstoragelike"><code>WebStorageLike</code></a>; <code>session</code>: <a href="#webstoragelike"><code>WebStorageLike</code></a>; }</td><td align="left">Optional web-storage pair for current-session persistence. When provided, the controller reads and writes the signed-in uid here (honoring the auth <code>setPersistence</code> mode) so page reloads restore the signed-in user — exactly like <code>browserLocalPersistence</code> in prod Firebase. When omitted, the user DATABASE still persists (Phase 1), but the CURRENT SESSION is not restored on reload. This is the honest no-fake-durability choice for environments where web storage isn’t available (Bun tests, Node servers, etc.). <code>local</code> maps to <code>localStorage</code> semantics (survives reload + restart); <code>session</code> maps to <code>sessionStorage</code> semantics (survives reload, cleared on tab close). The controller picks which store to write based on the auth <code>setPersistence</code> mode recorded on the backend: LOCAL → local (default; matches Firebase’s default) SESSION → session NONE → neither (uid is not stored) Both storages are read on restore (mode-agnostic — a prior session may have used a different mode). Exactly one store holds the uid at any time; a mode change migrates the uid to the new store.</td></tr><tr><td align="left"><code>sessionStorage.local</code></td><td align="left"><a href="#webstoragelike"><code>WebStorageLike</code></a></td><td align="left">-</td></tr><tr><td align="left"><code>sessionStorage.session</code></td><td align="left"><a href="#webstoragelike"><code>WebStorageLike</code></a></td><td align="left">-</td></tr></tbody></table></div>
3435
2922
  <hr>
3436
2923
  <p><a id="sandboxruntimeerrorevent"></a></p>
3437
2924
  <h3 id="sandboxruntimeerrorevent">SandboxRuntimeErrorEvent</h3>
3438
2925
  <p>Canonical non-rules operational failure.</p>
3439
- <h4 id="properties-23">Properties</h4>
2926
+ <h4 id="properties-21">Properties</h4>
3440
2927
  <div class="table-scroll">
3441
2928
 
3442
2929
 
@@ -3498,9 +2985,9 @@ their state is a Firestore document.</p>
3498
2985
 
3499
2986
 
3500
2987
 
3501
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="at-6"></a> <code>at</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="auth-10"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="detail-4"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="error-2"></a> <code>error</code></td><td align="left">{ <code>code?</code>: <code>string</code>; <code>message</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>error.code?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>error.message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="id-6"></a> <code>id</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="kind-5"></a> <code>kind</code></td><td align="left"><code>"runtime_error"</code></td></tr><tr><td align="left"><a id="method-4"></a> <code>method</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="path-4"></a> <code>path?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="service-5"></a> <code>service</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td></tr></tbody></table></div>
2988
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="at-6"></a> <code>at</code></td><td align="left"><code>number</code></td></tr><tr><td align="left"><a id="auth-9"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="detail-4"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="error-2"></a> <code>error</code></td><td align="left">{ <code>code?</code>: <code>string</code>; <code>message</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>error.code?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>error.message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="id-6"></a> <code>id</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="kind-5"></a> <code>kind</code></td><td align="left"><code>"runtime_error"</code></td></tr><tr><td align="left"><a id="method-4"></a> <code>method</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="path-4"></a> <code>path?</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="service-5"></a> <code>service</code></td><td align="left"><a href="#eventservice"><code>EventService</code></a></td></tr></tbody></table></div>
3502
2989
  <hr>
3503
- <p><a id="sandboxsnapshot-2"></a></p>
2990
+ <p><a id="sandboxsnapshot"></a></p>
3504
2991
  <h3 id="sandboxsnapshot">SandboxSnapshot</h3>
3505
2992
  <p>Sandbox-level snapshot — a coarse capture of every service’s state
3506
2993
  keyed by service name. The <code>firestore</code> key is always present; the
@@ -3511,7 +2998,7 @@ structural so it stays decoupled from service implementations.</p>
3511
2998
  <p>v2 shape — <code>services</code> was added when the persistable-service registry
3512
2999
  landed. Prior <code>{ firestore }</code> v1 blobs are treated as having an empty
3513
3000
  <code>services</code> map on restore.</p>
3514
- <h4 id="properties-24">Properties</h4>
3001
+ <h4 id="properties-22">Properties</h4>
3515
3002
  <div class="table-scroll">
3516
3003
 
3517
3004
 
@@ -3555,7 +3042,7 @@ existing <code>kind</code>s and never see it. See the design rationale.</p>
3555
3042
  (omitted when not meaningful — e.g. a sign-out has no <code>after</code>). Studio’s
3556
3043
  data grids / Action Center render <code>service</code> + <code>op</code> + <code>path</code> directly and
3557
3044
  diff <code>before</code>→<code>after</code> when both are present.</p>
3558
- <h4 id="properties-25">Properties</h4>
3045
+ <h4 id="properties-23">Properties</h4>
3559
3046
  <div class="table-scroll">
3560
3047
 
3561
3048
 
@@ -3626,7 +3113,7 @@ diff <code>before</code>→<code>after</code> when both are present.</p>
3626
3113
 
3627
3114
 
3628
3115
 
3629
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="after"></a> <code>after?</code></td><td align="left"><code>unknown</code></td><td align="left">Best-effort serializable snapshot of the state AFTER the mutation. Absent on deletes / sign-outs (nothing remains).</td></tr><tr><td align="left"><a id="at-7"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-11"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Identity in effect when the op ran (the service’s <code>request.auth</code> equivalent). <code>null</code> for admin/anonymous-driven mutations (e.g. <code>sandbox.createUser</code>, an unauthenticated RTDB write).</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="before"></a> <code>before?</code></td><td align="left"><code>unknown</code></td><td align="left">Best-effort serializable snapshot of the state BEFORE the mutation. Absent when there was no prior state (a create) or it isn’t cheap to capture.</td></tr><tr><td align="left"><a id="detail-5"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">Free-form, service-specific extras a consumer may surface without re-deriving (e.g. storage <code>{ size, contentType }</code>, rtdb <code>{ committed }</code> for a transaction). Kept loose on purpose — it’s a display hint, not a contract.</td></tr><tr><td align="left"><a id="id-7"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-6"></a> <code>kind</code></td><td align="left"><code>"service_mutation"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="op-2"></a> <code>op</code></td><td align="left"><code>string</code></td><td align="left">Service-scoped operation name. Stable, lowercase, snake/kebab-free: - auth: <code>user_create</code></td></tr><tr><td align="left"><a id="path-5"></a> <code>path?</code></td><td align="left"><code>string</code></td><td align="left">The thing mutated, in the service’s own addressing scheme: - auth: the user <code>uid</code> (or <code>'*'</code> for a clear-all). Absent for a sign-out with no prior user. - storage: the object <code>fullPath</code> (e.g. <code>avatars/alice.png</code>). - rtdb: the database path (e.g. <code>/rooms/r1/messages</code>), or for a multi-path <code>update</code> the ref path the call targeted.</td></tr><tr><td align="left"><a id="service-6"></a> <code>service</code></td><td align="left"><code>"auth"</code> | <code>"storage"</code> | <code>"rtdb"</code> | <code>"messaging"</code> | <code>"ai"</code></td><td align="left">Which service performed the mutation. Always one of the non-Firestore services — Firestore rides its own <code>request</code>/<code>write</code> path. (The provenance <code>service</code> field on the stamped event mirrors this; it is set redundantly here so a consumer matching purely on <code>kind</code> still gets the discriminator without reaching into provenance.)</td></tr></tbody></table></div>
3116
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="after"></a> <code>after?</code></td><td align="left"><code>unknown</code></td><td align="left">Best-effort serializable snapshot of the state AFTER the mutation. Absent on deletes / sign-outs (nothing remains).</td></tr><tr><td align="left"><a id="at-7"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-10"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">Identity in effect when the op ran (the service’s <code>request.auth</code> equivalent). <code>null</code> for admin/anonymous-driven mutations (e.g. <code>sandbox.createUser</code>, an unauthenticated RTDB write).</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="before"></a> <code>before?</code></td><td align="left"><code>unknown</code></td><td align="left">Best-effort serializable snapshot of the state BEFORE the mutation. Absent when there was no prior state (a create) or it isn’t cheap to capture.</td></tr><tr><td align="left"><a id="detail-5"></a> <code>detail?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">Free-form, service-specific extras a consumer may surface without re-deriving (e.g. storage <code>{ size, contentType }</code>, rtdb <code>{ committed }</code> for a transaction). Kept loose on purpose — it’s a display hint, not a contract.</td></tr><tr><td align="left"><a id="id-7"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-6"></a> <code>kind</code></td><td align="left"><code>"service_mutation"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="op-2"></a> <code>op</code></td><td align="left"><code>string</code></td><td align="left">Service-scoped operation name. Stable, lowercase, snake/kebab-free: - auth: <code>user_create</code></td></tr><tr><td align="left"><a id="path-5"></a> <code>path?</code></td><td align="left"><code>string</code></td><td align="left">The thing mutated, in the service’s own addressing scheme: - auth: the user <code>uid</code> (or <code>'*'</code> for a clear-all). Absent for a sign-out with no prior user. - storage: the object <code>fullPath</code> (e.g. <code>avatars/alice.png</code>). - rtdb: the database path (e.g. <code>/rooms/r1/messages</code>), or for a multi-path <code>update</code> the ref path the call targeted.</td></tr><tr><td align="left"><a id="service-6"></a> <code>service</code></td><td align="left"><code>"rtdb"</code> | <code>"auth"</code> | <code>"storage"</code> | <code>"messaging"</code> | <code>"ai"</code></td><td align="left">Which service performed the mutation. Always one of the non-Firestore services — Firestore rides its own <code>request</code>/<code>write</code> path. (The provenance <code>service</code> field on the stamped event mirrors this; it is set redundantly here so a consumer matching purely on <code>kind</code> still gets the discriminator without reaching into provenance.)</td></tr></tbody></table></div>
3630
3117
  <hr>
3631
3118
  <p><a id="sessionboundaryevent"></a></p>
3632
3119
  <h3 id="sessionboundaryevent">SessionBoundaryEvent</h3>
@@ -3634,7 +3121,7 @@ diff <code>before</code>→<code>after</code> when both are present.</p>
3634
3121
  and before <code>sandbox.dispose()</code> tears it down. Lets consumers segment
3635
3122
  a persisted event stream into “session N pre-reset” / “session N+1
3636
3123
  post-reset” runs.</p>
3637
- <h4 id="properties-26">Properties</h4>
3124
+ <h4 id="properties-24">Properties</h4>
3638
3125
  <div class="table-scroll">
3639
3126
 
3640
3127
 
@@ -3682,7 +3169,7 @@ instead.</p>
3682
3169
  <p><code>sample</code> carries best-effort serializable views of the docs the
3683
3170
  callback received; consumers truncate before persisting if the
3684
3171
  scenario produces large snapshots.</p>
3685
- <h4 id="properties-27">Properties</h4>
3172
+ <h4 id="properties-25">Properties</h4>
3686
3173
  <div class="table-scroll">
3687
3174
 
3688
3175
 
@@ -3778,7 +3265,7 @@ scenario produces large snapshots.</p>
3778
3265
 
3779
3266
 
3780
3267
 
3781
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="addedcount"></a> <code>addedCount</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="at-9"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-12"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="id-9"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-8"></a> <code>kind</code></td><td align="left"><code>"snapshot_delivery"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="listenerid-2"></a> <code>listenerId</code></td><td align="left"><code>string</code></td><td align="left">Opaque listener id assigned at attach time.</td></tr><tr><td align="left"><a id="modifiedcount"></a> <code>modifiedCount</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="removedcount"></a> <code>removedCount</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="sample-1"></a> <code>sample?</code></td><td align="left">{ <code>docs</code>: { <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>path</code>: <code>string</code>; }[]; }</td><td align="left">Doc payloads, in the order the user callback saw them.</td></tr><tr><td align="left"><code>sample.docs</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>path</code>: <code>string</code>; }[]</td><td align="left">-</td></tr><tr><td align="left"><a id="size-1"></a> <code>size</code></td><td align="left"><code>number</code></td><td align="left"><code>1</code> for doc-kind (exists) / <code>0</code> (deleted), <code>n</code> for query-kind.</td></tr><tr><td align="left"><a id="target-2"></a> <code>target</code></td><td align="left">| { <code>kind</code>: <code>"doc"</code>; <code>path</code>: <code>string</code>; } | { <code>collection</code>: <code>string</code>; <code>kind</code>: <code>"query"</code>; }</td><td align="left">-</td></tr><tr><td align="left"><a id="triggeredby-3"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path</code>: <code>string</code>; }</td><td align="left">The user op that triggered this re-eval. Absent on initial fire and on <code>deployRules</code>-driven re-evals.</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
3268
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="addedcount"></a> <code>addedCount</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="at-9"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-11"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="id-9"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-8"></a> <code>kind</code></td><td align="left"><code>"snapshot_delivery"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="listenerid-3"></a> <code>listenerId</code></td><td align="left"><code>string</code></td><td align="left">Opaque listener id assigned at attach time.</td></tr><tr><td align="left"><a id="modifiedcount"></a> <code>modifiedCount</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="removedcount"></a> <code>removedCount</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="sample-1"></a> <code>sample?</code></td><td align="left">{ <code>docs</code>: { <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>path</code>: <code>string</code>; }[]; }</td><td align="left">Doc payloads, in the order the user callback saw them.</td></tr><tr><td align="left"><code>sample.docs</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>path</code>: <code>string</code>; }[]</td><td align="left">-</td></tr><tr><td align="left"><a id="size-1"></a> <code>size</code></td><td align="left"><code>number</code></td><td align="left"><code>1</code> for doc-kind (exists) / <code>0</code> (deleted), <code>n</code> for query-kind.</td></tr><tr><td align="left"><a id="target-2"></a> <code>target</code></td><td align="left">| { <code>kind</code>: <code>"doc"</code>; <code>path</code>: <code>string</code>; } | { <code>collection</code>: <code>string</code>; <code>kind</code>: <code>"query"</code>; }</td><td align="left">-</td></tr><tr><td align="left"><a id="triggeredby-3"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path</code>: <code>string</code>; }</td><td align="left">The user op that triggered this re-eval. Absent on initial fire and on <code>deployRules</code>-driven re-evals.</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
3782
3269
  <hr>
3783
3270
  <p><a id="snapshoterrorevent"></a></p>
3784
3271
  <h3 id="snapshoterrorevent">SnapshotErrorEvent</h3>
@@ -3795,7 +3282,7 @@ attribute the error to a specific watch.</p>
3795
3282
  quota, no concurrent transactions to conflict). Documented divergence
3796
3283
  from production; new codes can be added if a sandbox-specific
3797
3284
  scenario surfaces them.</p>
3798
- <h4 id="properties-28">Properties</h4>
3285
+ <h4 id="properties-26">Properties</h4>
3799
3286
  <div class="table-scroll">
3800
3287
 
3801
3288
 
@@ -3861,7 +3348,7 @@ scenario surfaces them.</p>
3861
3348
 
3862
3349
 
3863
3350
 
3864
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="auth-13"></a> <code>auth?</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="code-2"></a> <code>code</code></td><td align="left"><code>"permission-denied"</code></td></tr><tr><td align="left"><a id="message-1"></a> <code>message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="reasons-4"></a> <code>reasons?</code></td><td align="left"><code>string</code>[]</td></tr><tr><td align="left"><a id="request-4"></a> <code>request?</code></td><td align="left">{ <code>method</code>: <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code> | <code>"delete"</code>; <code>path</code>: <code>string</code>; <code>resourceData?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; }</td></tr><tr><td align="left"><code>request.method</code></td><td align="left"><code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code> | <code>"delete"</code></td></tr><tr><td align="left"><code>request.path</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="resource-2"></a> <code>resource?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td></tr><tr><td align="left"><code>resource.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>resource.exists</code></td><td align="left"><code>boolean</code></td></tr><tr><td align="left"><a id="target-3"></a> <code>target</code></td><td align="left">| { <code>kind</code>: <code>"doc"</code>; <code>path</code>: <code>string</code>; } | { <code>collection</code>: <code>string</code>; <code>kind</code>: <code>"query"</code>; }</td></tr></tbody></table></div>
3351
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><a id="auth-12"></a> <code>auth?</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="code-1"></a> <code>code</code></td><td align="left"><code>"permission-denied"</code></td></tr><tr><td align="left"><a id="message-1"></a> <code>message</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><a id="reasons-4"></a> <code>reasons?</code></td><td align="left"><code>string</code>[]</td></tr><tr><td align="left"><a id="request-4"></a> <code>request?</code></td><td align="left">{ <code>method</code>: <code>"delete"</code> | <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code>; <code>path</code>: <code>string</code>; <code>resourceData?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; }</td></tr><tr><td align="left"><code>request.method</code></td><td align="left"><code>"delete"</code> | <code>"get"</code> | <code>"list"</code> | <code>"create"</code> | <code>"update"</code></td></tr><tr><td align="left"><code>request.path</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>request.resourceData?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><a id="resource-2"></a> <code>resource?</code></td><td align="left">{ <code>data</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>exists</code>: <code>boolean</code>; }</td></tr><tr><td align="left"><code>resource.data</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>resource.exists</code></td><td align="left"><code>boolean</code></td></tr><tr><td align="left"><a id="target-3"></a> <code>target</code></td><td align="left">| { <code>kind</code>: <code>"doc"</code>; <code>path</code>: <code>string</code>; } | { <code>collection</code>: <code>string</code>; <code>kind</code>: <code>"query"</code>; <code>query?</code>: <code>unknown</code>; }</td></tr></tbody></table></div>
3865
3352
  <hr>
3866
3353
  <p><a id="snapshotsuppressedevent"></a></p>
3867
3354
  <h3 id="snapshotsuppressedevent">SnapshotSuppressedEvent</h3>
@@ -3871,7 +3358,7 @@ user callback wasn’t invoked.</p>
3871
3358
  <p>Useful for “why didn’t my listener fire” debugging. Default UIs
3872
3359
  should filter these out; only the inspector-style consumer needs
3873
3360
  them.</p>
3874
- <h4 id="properties-29">Properties</h4>
3361
+ <h4 id="properties-27">Properties</h4>
3875
3362
  <div class="table-scroll">
3876
3363
 
3877
3364
 
@@ -3942,13 +3429,13 @@ them.</p>
3942
3429
 
3943
3430
 
3944
3431
 
3945
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at-10"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-14"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="id-10"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-9"></a> <code>kind</code></td><td align="left"><code>"snapshot_suppressed"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="listenerid-3"></a> <code>listenerId</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="reason-1"></a> <code>reason</code></td><td align="left"><code>"no-op"</code></td><td align="left">Why this re-eval was suppressed. v1 only emits <code>'no-op'</code>.</td></tr><tr><td align="left"><a id="target-4"></a> <code>target</code></td><td align="left">| { <code>kind</code>: <code>"doc"</code>; <code>path</code>: <code>string</code>; } | { <code>collection</code>: <code>string</code>; <code>kind</code>: <code>"query"</code>; }</td><td align="left">-</td></tr><tr><td align="left"><a id="triggeredby-4"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
3432
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at-10"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-13"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="id-10"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-9"></a> <code>kind</code></td><td align="left"><code>"snapshot_suppressed"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="listenerid-4"></a> <code>listenerId</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="reason-1"></a> <code>reason</code></td><td align="left"><code>"no-op"</code></td><td align="left">Why this re-eval was suppressed. v1 only emits <code>'no-op'</code>.</td></tr><tr><td align="left"><a id="target-4"></a> <code>target</code></td><td align="left">| { <code>kind</code>: <code>"doc"</code>; <code>path</code>: <code>string</code>; } | { <code>collection</code>: <code>string</code>; <code>kind</code>: <code>"query"</code>; }</td><td align="left">-</td></tr><tr><td align="left"><a id="triggeredby-4"></a> <code>triggeredBy?</code></td><td align="left">{ <code>method</code>: <code>string</code>; <code>path</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.method</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><code>triggeredBy.path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr></tbody></table></div>
3946
3433
  <hr>
3947
3434
  <p><a id="tabsyncoptions"></a></p>
3948
3435
  <h3 id="tabsyncoptions">TabSyncOptions</h3>
3949
3436
  <p>Options for <code>sandbox.enableTabSync(options?)</code>. All fields are optional;
3950
3437
  the defaults provide a ready-to-use configuration for browser environments.</p>
3951
- <h4 id="properties-30">Properties</h4>
3438
+ <h4 id="properties-28">Properties</h4>
3952
3439
  <div class="table-scroll">
3953
3440
 
3954
3441
 
@@ -3983,11 +3470,11 @@ browsers pass real storages and tests pass in-memory Map-backed fakes.</p>
3983
3470
  length + key() + clear() that aren’t needed here — narrowing the
3984
3471
  contract keeps tests simple and Node/Bun hosts from having to
3985
3472
  implement a complete polyfill.</p>
3986
- <h4 id="methods-10">Methods</h4>
3473
+ <h4 id="methods-8">Methods</h4>
3987
3474
  <p><a id="getitem"></a></p>
3988
3475
  <h5 id="getitem">getItem()</h5>
3989
3476
  <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:#61AFEF">getItem</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">key</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">): </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">;</span></span></code></pre>
3990
- <h6 id="parameters-41">Parameters</h6>
3477
+ <h6 id="parameters-30">Parameters</h6>
3991
3478
  <div class="table-scroll">
3992
3479
 
3993
3480
 
@@ -4002,12 +3489,12 @@ implement a complete polyfill.</p>
4002
3489
 
4003
3490
 
4004
3491
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>key</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
4005
- <h6 id="returns-78">Returns</h6>
3492
+ <h6 id="returns-60">Returns</h6>
4006
3493
  <p><code>string</code></p>
4007
3494
  <p><a id="removeitem"></a></p>
4008
3495
  <h5 id="removeitem">removeItem()</h5>
4009
3496
  <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:#61AFEF">removeItem</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">key</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
4010
- <h6 id="parameters-42">Parameters</h6>
3497
+ <h6 id="parameters-31">Parameters</h6>
4011
3498
  <div class="table-scroll">
4012
3499
 
4013
3500
 
@@ -4022,12 +3509,12 @@ implement a complete polyfill.</p>
4022
3509
 
4023
3510
 
4024
3511
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>key</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
4025
- <h6 id="returns-79">Returns</h6>
3512
+ <h6 id="returns-61">Returns</h6>
4026
3513
  <p><code>void</code></p>
4027
3514
  <p><a id="setitem"></a></p>
4028
3515
  <h5 id="setitem">setItem()</h5>
4029
3516
  <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:#61AFEF">setItem</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75">key</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">value</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">): </span><span style="color:#C678DD">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
4030
- <h6 id="parameters-43">Parameters</h6>
3517
+ <h6 id="parameters-32">Parameters</h6>
4031
3518
  <div class="table-scroll">
4032
3519
 
4033
3520
 
@@ -4046,7 +3533,7 @@ implement a complete polyfill.</p>
4046
3533
 
4047
3534
 
4048
3535
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>key</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>value</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
4049
- <h6 id="returns-80">Returns</h6>
3536
+ <h6 id="returns-62">Returns</h6>
4050
3537
  <p><code>void</code></p>
4051
3538
  <hr>
4052
3539
  <p><a id="writesandboxevent"></a></p>
@@ -4062,7 +3549,7 @@ op. A denied or rolled-back write surfaces as a request-deny only;
4062
3549
  engine — v1 of the unified channel leaves them undefined. The shape
4063
3550
  is locked so consumers can build against it without churn when
4064
3551
  sentinel/auto-id capture lands.</p>
4065
- <h4 id="properties-31">Properties</h4>
3552
+ <h4 id="properties-29">Properties</h4>
4066
3553
  <div class="table-scroll">
4067
3554
 
4068
3555
 
@@ -4168,7 +3655,7 @@ sentinel/auto-id capture lands.</p>
4168
3655
 
4169
3656
 
4170
3657
 
4171
- <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at-11"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-15"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="autoid"></a> <code>autoId?</code></td><td align="left"><code>string</code></td><td align="left">Minted document ID when this write came from <code>collection.add()</code> / <code>LocalEnvironment.createWithAutoId</code>. The replay engine aliases the path’s last segment to a fresh mint on replay (rather than preserving the original auto-ID).</td></tr><tr><td align="left"><a id="data-1"></a> <code>data?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">Pre-resolution write payload — <code>FieldValue.*</code> sentinels preserved as marker shapes (<code>{ __type: 'serverTimestamp' }</code>, etc.) so the replay engine can re-resolve them. The rule engine evaluated against the resolved form internally; the resolved form lives on <a href="#nextstate-1">nextState</a>. Absent on <code>delete</code>.</td></tr><tr><td align="left"><a id="detail-6"></a> <code>detail?</code></td><td align="left">{ <code>admin?</code>: <code>boolean</code>; } &#x26; <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">Free-form write metadata. <code>admin: true</code> marks a rules-bypassing setup/admin commit so replay can apply it as context without asking candidate rules to permit it.</td></tr><tr><td align="left"><a id="groupid-3"></a> <code>groupId?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="groupkind-3"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code> | <code>"batch"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="id-11"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-10"></a> <code>kind</code></td><td align="left"><code>"write"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="method-5"></a> <code>method</code></td><td align="left"><code>"create"</code> | <code>"update"</code> | <code>"delete"</code> | <code>"set"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="nextstate-1"></a> <code>nextState</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">State AFTER this write. <code>null</code> on <code>delete</code>.</td></tr><tr><td align="left"><a id="path-6"></a> <code>path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="priorstate-1"></a> <code>priorState</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">State BEFORE this write. <code>null</code> for a non-existent doc.</td></tr><tr><td align="left"><a id="requesttime"></a> <code>requestTime</code></td><td align="left">{ <code>nanoseconds</code>: <code>number</code>; <code>seconds</code>: <code>number</code>; }</td><td align="left">Server time at which the rule engine evaluated this write — pinned per op (or shared across sub-ops in a batch / transaction). The replay engine re-issues this exact value when re-resolving <code>serverTimestamp()</code> sentinels so resolved fields are bit-identical on replay. Shape mirrors the Firestore Web SDK Timestamp (<code>{ seconds, nanoseconds }</code>).</td></tr><tr><td align="left"><code>requestTime.nanoseconds</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><code>requestTime.seconds</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="sentinels"></a> <code>sentinels?</code></td><td align="left">{ <code>field</code>: <code>string</code>; <code>kind</code>: | <code>"delete"</code> | <code>"serverTimestamp"</code> | <code>"increment"</code> | <code>"arrayUnion"</code> | <code>"arrayRemove"</code>; }[]</td><td align="left">FieldValue sentinels (serverTimestamp / increment / arrayUnion / arrayRemove / deleteField → ‘delete’) extracted from the pre-resolution write payload. The replay engine consumes this to re-issue the same sentinels at replay time without consulting resolved values that would have drifted. Path syntax: dotted with bracket-indices (‘a.b[0].c’). Absent when the write contained no sentinels.</td></tr></tbody></table></div>
3658
+ <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><a id="at-11"></a> <code>at</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="auth-14"></a> <code>auth</code></td><td align="left">{ <code>token?</code>: <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>; <code>uid</code>: <code>string</code>; }</td><td align="left">-</td></tr><tr><td align="left"><code>auth.token?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">-</td></tr><tr><td align="left"><code>auth.uid</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="autoid"></a> <code>autoId?</code></td><td align="left"><code>string</code></td><td align="left">Minted document ID when this write came from <code>collection.add()</code> / <code>LocalEnvironment.createWithAutoId</code>. The replay engine aliases the path’s last segment to a fresh mint on replay (rather than preserving the original auto-ID).</td></tr><tr><td align="left"><a id="data-1"></a> <code>data?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">Pre-resolution write payload — <code>FieldValue.*</code> sentinels preserved as marker shapes (<code>{ __type: 'serverTimestamp' }</code>, etc.) so the replay engine can re-resolve them. The rule engine evaluated against the resolved form internally; the resolved form lives on <a href="#nextstate-1">nextState</a>. Absent on <code>delete</code>.</td></tr><tr><td align="left"><a id="detail-6"></a> <code>detail?</code></td><td align="left">{ <code>admin?</code>: <code>boolean</code>; } &#x26; <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">Free-form write metadata. <code>admin: true</code> marks a rules-bypassing setup/admin commit so replay can apply it as context without asking candidate rules to permit it.</td></tr><tr><td align="left"><a id="groupid-3"></a> <code>groupId?</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="groupkind-4"></a> <code>groupKind?</code></td><td align="left"><code>"transaction"</code> | <code>"batch"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="id-11"></a> <code>id</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="kind-10"></a> <code>kind</code></td><td align="left"><code>"write"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="method-5"></a> <code>method</code></td><td align="left"><code>"delete"</code> | <code>"create"</code> | <code>"update"</code> | <code>"set"</code></td><td align="left">-</td></tr><tr><td align="left"><a id="nextstate-1"></a> <code>nextState</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">State AFTER this write. <code>null</code> on <code>delete</code>.</td></tr><tr><td align="left"><a id="path-6"></a> <code>path</code></td><td align="left"><code>string</code></td><td align="left">-</td></tr><tr><td align="left"><a id="priorstate-1"></a> <code>priorState</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td><td align="left">State BEFORE this write. <code>null</code> for a non-existent doc.</td></tr><tr><td align="left"><a id="requesttime"></a> <code>requestTime</code></td><td align="left">{ <code>nanoseconds</code>: <code>number</code>; <code>seconds</code>: <code>number</code>; }</td><td align="left">Server time at which the rule engine evaluated this write — pinned per op (or shared across sub-ops in a batch / transaction). The replay engine re-issues this exact value when re-resolving <code>serverTimestamp()</code> sentinels so resolved fields are bit-identical on replay. Shape mirrors the Firestore Web SDK Timestamp (<code>{ seconds, nanoseconds }</code>).</td></tr><tr><td align="left"><code>requestTime.nanoseconds</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><code>requestTime.seconds</code></td><td align="left"><code>number</code></td><td align="left">-</td></tr><tr><td align="left"><a id="sentinels"></a> <code>sentinels?</code></td><td align="left">{ <code>field</code>: <code>string</code>; <code>kind</code>: | <code>"delete"</code> | <code>"serverTimestamp"</code> | <code>"increment"</code> | <code>"arrayUnion"</code> | <code>"arrayRemove"</code>; }[]</td><td align="left">FieldValue sentinels (serverTimestamp / increment / arrayUnion / arrayRemove / deleteField → ‘delete’) extracted from the pre-resolution write payload. The replay engine consumes this to re-issue the same sentinels at replay time without consulting resolved values that would have drifted. Path syntax: dotted with bracket-indices (‘a.b[0].c’). Absent when the write contained no sentinels.</td></tr></tbody></table></div>
4172
3659
  <h2 id="type-aliases">Type Aliases</h2>
4173
3660
  <p><a id="authlens-2"></a></p>
4174
3661
  <h3 id="authlens">AuthLens</h3>
@@ -4189,23 +3676,6 @@ sentinel/auto-id capture lands.</p>
4189
3676
  <span class="line"><span style="color:#ABB2BF">};</span></span></code></pre>
4190
3677
  <p>The identity/rules lens an operation actually ran under.</p>
4191
3678
  <hr>
4192
- <p><a id="authstate"></a></p>
4193
- <h3 id="authstate">AuthState</h3>
4194
- <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">type</span><span style="color:#E5C07B"> AuthState</span><span style="color:#56B6C2"> =</span></span>
4195
- <span class="line"><span style="color:#ABB2BF"> | {</span></span>
4196
- <span class="line"><span style="color:#E06C75"> token</span><span style="color:#C678DD">?</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">Record</span><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E5C07B">string</span><span style="color:#ABB2BF">, </span><span style="color:#E5C07B">unknown</span><span style="color:#ABB2BF">>;</span></span>
4197
- <span class="line"><span style="color:#E06C75"> uid</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">string</span><span style="color:#ABB2BF">;</span></span>
4198
- <span class="line"><span style="color:#ABB2BF">}</span></span>
4199
- <span class="line"><span style="color:#ABB2BF"> | </span><span style="color:#E5C07B">null</span><span style="color:#ABB2BF">;</span></span></code></pre>
4200
- <p>A signed-in identity for sandbox operations. <code>null</code> is anonymous.</p>
4201
- <p><code>token</code> is the Firebase Auth token claims map (custom claims plus
4202
- standard ones). It surfaces the same way it does in production rules
4203
- via <code>request.auth.token.*</code>. Omit it for plain UID-only auth.</p>
4204
- <p>Renamed from <code>AuthContext</code> (pre-multi-context) so the data type
4205
- doesn’t visually collide with <code>SandboxContext</code> (the identity-bearing
4206
- handle). They sit at different layers — payload vs. handle — and the
4207
- names should reflect that.</p>
4208
- <hr>
4209
3679
  <p><a id="difftarget"></a></p>
4210
3680
  <h3 id="difftarget">DiffTarget</h3>
4211
3681
  <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">type</span><span style="color:#E5C07B"> DiffTarget</span><span style="color:#56B6C2"> =</span><span style="color:#E5C07B"> LocalSandbox</span><span style="color:#ABB2BF"> | </span><span style="color:#E5C07B">SandboxSnapshot</span><span style="color:#ABB2BF">;</span></span></code></pre>
@@ -4250,6 +3720,7 @@ names should reflect that.</p>
4250
3720
  <h3 id="eventactor">EventActor</h3>
4251
3721
  <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">type</span><span style="color:#E5C07B"> EventActor</span><span style="color:#56B6C2"> =</span></span>
4252
3722
  <span class="line"><span style="color:#ABB2BF"> | {</span></span>
3723
+ <span class="line"><span style="color:#E06C75"> journeyId</span><span style="color:#C678DD">?</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">string</span><span style="color:#ABB2BF">;</span></span>
4253
3724
  <span class="line"><span style="color:#E06C75"> kind</span><span style="color:#ABB2BF">: </span><span style="color:#98C379">"app"</span><span style="color:#ABB2BF">;</span></span>
4254
3725
  <span class="line"><span style="color:#ABB2BF">}</span></span>
4255
3726
  <span class="line"><span style="color:#ABB2BF"> | {</span></span>
@@ -4281,7 +3752,7 @@ explicitly as <code>unattributed</code>; it is never silently promoted to app tr
4281
3752
  <code>initializeApp()</code> is sync in firebase-admin, so the factory must return
4282
3753
  the branded handle without awaiting (connection establishment may be
4283
3754
  lazy inside the handle’s channel).</p>
4284
- <h4 id="parameters-44">Parameters</h4>
3755
+ <h4 id="parameters-33">Parameters</h4>
4285
3756
  <div class="table-scroll">
4286
3757
 
4287
3758
 
@@ -4296,7 +3767,7 @@ lazy inside the handle’s channel).</p>
4296
3767
 
4297
3768
 
4298
3769
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>opts?</code></td><td align="left"><a href="#remotesandboxfactoryoptions"><code>RemoteSandboxFactoryOptions</code></a></td></tr></tbody></table></div>
4299
- <h4 id="returns-81">Returns</h4>
3770
+ <h4 id="returns-63">Returns</h4>
4300
3771
  <p><a href="#remotesandbox"><code>RemoteSandbox</code></a></p>
4301
3772
  <hr>
4302
3773
  <p><a id="rulesdisposition-2"></a></p>
@@ -4317,7 +3788,7 @@ lazy inside the handle’s channel).</p>
4317
3788
  <p>What happened at the Security Rules seam. Admin is a lens; <code>bypassed</code> is
4318
3789
  the rules disposition.</p>
4319
3790
  <hr>
4320
- <p><a id="sandboxerrorcode-1"></a></p>
3791
+ <p><a id="sandboxerrorcode"></a></p>
4321
3792
  <h3 id="sandboxerrorcode">SandboxErrorCode</h3>
4322
3793
  <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">type</span><span style="color:#E5C07B"> SandboxErrorCode</span><span style="color:#56B6C2"> =</span></span>
4323
3794
  <span class="line"><span style="color:#ABB2BF"> | </span><span style="color:#98C379">"invalid-argument"</span></span>
@@ -4352,7 +3823,7 @@ code is wrong” without parsing message strings.</p>
4352
3823
  <span class="line"><span style="color:#ABB2BF"> | </span><span style="color:#E5C07B">SandboxListenerEvent</span></span>
4353
3824
  <span class="line"><span style="color:#ABB2BF"> | </span><span style="color:#E5C07B">SandboxRuntimeErrorEvent</span><span style="color:#ABB2BF"> &#x26; </span><span style="color:#E5C07B">EventProvenance</span><span style="color:#ABB2BF">;</span></span></code></pre>
4354
3825
  <p>Discriminated union of every event the sandbox emits to
4355
- <a href="#onevent-4">Sandbox.onEvent</a> subscribers.</p>
3826
+ <a href="/docs/pyric-firestore-reference-api/#onevent">Sandbox.onEvent</a> subscribers.</p>
4356
3827
  <p>Issue #307 — replaces the prior three-channel surface
4357
3828
  (<code>onRequest</code> / <code>onDenial</code> / <code>onSnapshotError</code>). Filter on <code>kind</code>
4358
3829
  to recover the subset each old channel covered:</p>
@@ -4396,7 +3867,7 @@ fresh empty sandbox — so it composes over base docs (e.g. an <code>update</cod
4396
3867
  lands on a doc the snapshot seeded) and accumulates across multiple
4397
3868
  <code>apply</code> calls. The applied events are folded into <code>branch.events</code> so
4398
3869
  <a href="#promote">promote</a> can reproduce the same sequence on the target.</p>
4399
- <h4 id="parameters-45">Parameters</h4>
3870
+ <h4 id="parameters-34">Parameters</h4>
4400
3871
  <div class="table-scroll">
4401
3872
 
4402
3873
 
@@ -4415,7 +3886,7 @@ lands on a doc the snapshot seeded) and accumulates across multiple
4415
3886
 
4416
3887
 
4417
3888
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>branch</code></td><td align="left"><a href="#branch"><code>Branch</code></a></td></tr><tr><td align="left"><code>events</code></td><td align="left">readonly <a href="#sandboxevent"><code>SandboxEvent</code></a>[]</td></tr></tbody></table></div>
4418
- <h4 id="returns-82">Returns</h4>
3889
+ <h4 id="returns-64">Returns</h4>
4419
3890
  <p><a href="#branch"><code>Branch</code></a></p>
4420
3891
  <p>the same branch (mutated in place) for chaining.</p>
4421
3892
  <hr>
@@ -4437,7 +3908,7 @@ can apply it to late-arriving services.</li>
4437
3908
  immediately apply the saved blob data (if any) AND subscribe the
4438
3909
  service’s change notifier for future flushes.</li>
4439
3910
  </ol>
4440
- <h4 id="parameters-46">Parameters</h4>
3911
+ <h4 id="parameters-35">Parameters</h4>
4441
3912
  <div class="table-scroll">
4442
3913
 
4443
3914
 
@@ -4455,8 +3926,8 @@ service’s change notifier for future flushes.</li>
4455
3926
 
4456
3927
 
4457
3928
 
4458
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>sandbox</code></td><td align="left"><a href="#sandbox-3"><code>Sandbox</code></a></td></tr><tr><td align="left"><code>rawOptions</code></td><td align="left"><a href="#sandboxpersistenceoptions"><code>SandboxPersistenceOptions</code></a></td></tr></tbody></table></div>
4459
- <h4 id="returns-83">Returns</h4>
3929
+ <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>sandbox</code></td><td align="left"><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a></td></tr><tr><td align="left"><code>rawOptions</code></td><td align="left"><a href="#sandboxpersistenceoptions"><code>SandboxPersistenceOptions</code></a></td></tr></tbody></table></div>
3930
+ <h4 id="returns-65">Returns</h4>
4460
3931
  <p><code>Promise</code>&#x3C;<a href="#persistencecontroller"><code>PersistenceController</code></a>></p>
4461
3932
  <hr>
4462
3933
  <p><a id="attachtabsync"></a></p>
@@ -4466,7 +3937,7 @@ service’s change notifier for future flushes.</li>
4466
3937
  kept in a separate module so <code>sandbox-impl.ts</code> stays thin.</p>
4467
3938
  <p>Returns a <code>disable</code> function: calling it unsubscribes the onEvent listener,
4468
3939
  removes the channel message listener, and closes the channel.</p>
4469
- <h4 id="parameters-47">Parameters</h4>
3940
+ <h4 id="parameters-36">Parameters</h4>
4470
3941
  <div class="table-scroll">
4471
3942
 
4472
3943
 
@@ -4488,9 +3959,9 @@ removes the channel message listener, and closes the channel.</p>
4488
3959
 
4489
3960
 
4490
3961
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><code>sandbox</code></td><td align="left"><a href="#localsandbox"><code>LocalSandbox</code></a></td><td align="left">The sandbox to sync. Must expose <code>onEvent</code>, <code>admin</code>, and <code>snapshot</code>.</td></tr><tr><td align="left"><code>options?</code></td><td align="left"><a href="#tabsyncoptions"><code>TabSyncOptions</code></a></td><td align="left">Optional channel and origin override.</td></tr></tbody></table></div>
4491
- <h4 id="returns-84">Returns</h4>
3962
+ <h4 id="returns-66">Returns</h4>
4492
3963
  <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:#ABB2BF">(): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
4493
- <h5 id="returns-85">Returns</h5>
3964
+ <h5 id="returns-67">Returns</h5>
4494
3965
  <p><code>void</code></p>
4495
3966
  <hr>
4496
3967
  <p><a id="bundlerecords"></a></p>
@@ -4501,7 +3972,7 @@ removes the channel message listener, and closes the channel.</p>
4501
3972
  <a href="#parsebundle">parseBundle</a>. This deliberately collapses the chunking into one blob:
4502
3973
  it is the single-artifact EXPORT shape, not the scale path (that is the
4503
3974
  record-shaped backend).</p>
4504
- <h4 id="parameters-48">Parameters</h4>
3975
+ <h4 id="parameters-37">Parameters</h4>
4505
3976
  <div class="table-scroll">
4506
3977
 
4507
3978
 
@@ -4516,7 +3987,7 @@ record-shaped backend).</p>
4516
3987
 
4517
3988
 
4518
3989
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>records</code></td><td align="left"><code>ReadonlyMap</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr></tbody></table></div>
4519
- <h4 id="returns-86">Returns</h4>
3990
+ <h4 id="returns-68">Returns</h4>
4520
3991
  <p><code>string</code></p>
4521
3992
  <hr>
4522
3993
  <p><a id="createindexeddbbackend"></a></p>
@@ -4525,13 +3996,13 @@ record-shaped backend).</p>
4525
3996
  <p>Build an IndexedDB-backed <code>PersistenceBackend</code>. Throws synchronously when
4526
3997
  called outside a browser (no <code>indexedDB</code> global) so callers can detect the
4527
3998
  absence and fall back to memory.</p>
4528
- <h4 id="returns-87">Returns</h4>
3999
+ <h4 id="returns-69">Returns</h4>
4529
4000
  <p><a href="#persistencebackend"><code>PersistenceBackend</code></a></p>
4530
4001
  <hr>
4531
4002
  <p><a id="creatememorybackend"></a></p>
4532
4003
  <h3 id="creatememorybackend">createMemoryBackend()</h3>
4533
4004
  <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">function</span><span style="color:#61AFEF"> createMemoryBackend</span><span style="color:#ABB2BF">(): </span><span style="color:#E5C07B">PersistenceBackend</span><span style="color:#ABB2BF">;</span></span></code></pre>
4534
- <h4 id="returns-88">Returns</h4>
4005
+ <h4 id="returns-70">Returns</h4>
4535
4006
  <p><a href="#persistencebackend"><code>PersistenceBackend</code></a></p>
4536
4007
  <hr>
4537
4008
  <p><a id="deserializefrombuckets"></a></p>
@@ -4542,7 +4013,7 @@ absence and fall back to memory.</p>
4542
4013
  <span class="line"><span style="color:#ABB2BF">};</span></span></code></pre>
4543
4014
  <p>Reassemble a firestore snapshot + services from v3 records (any iterable of
4544
4015
  [recordId, record] pairs). Rehydrates wrapper types from their markers.</p>
4545
- <h4 id="parameters-49">Parameters</h4>
4016
+ <h4 id="parameters-38">Parameters</h4>
4546
4017
  <div class="table-scroll">
4547
4018
 
4548
4019
 
@@ -4557,7 +4028,7 @@ absence and fall back to memory.</p>
4557
4028
 
4558
4029
 
4559
4030
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>records</code></td><td align="left"><code>Iterable</code>&#x3C;[<code>string</code>, <code>unknown</code>]></td></tr></tbody></table></div>
4560
- <h4 id="returns-89">Returns</h4>
4031
+ <h4 id="returns-71">Returns</h4>
4561
4032
  <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:#ABB2BF">{</span></span>
4562
4033
  <span class="line"><span style="color:#E06C75"> firestore</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">Record</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">Record</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">unknown</span><span style="color:#56B6C2">>></span><span style="color:#ABB2BF">;</span></span>
4563
4034
  <span class="line"><span style="color:#E06C75"> services</span><span style="color:#ABB2BF">: </span><span style="color:#E06C75">Record</span><span style="color:#56B6C2">&#x3C;</span><span style="color:#E06C75">string</span><span style="color:#ABB2BF">, </span><span style="color:#E06C75">unknown</span><span style="color:#56B6C2">></span><span style="color:#ABB2BF">;</span></span>
@@ -4577,7 +4048,7 @@ write metadata in play, differences surface as <code>real-divergence</code>
4577
4048
  (field/doc changed) — the honest classification for “branch vs live”.
4578
4049
  Added/removed docs surface as presence divergences (one side
4579
4050
  <code>undefined</code>).</p>
4580
- <h4 id="parameters-50">Parameters</h4>
4051
+ <h4 id="parameters-39">Parameters</h4>
4581
4052
  <div class="table-scroll">
4582
4053
 
4583
4054
 
@@ -4599,7 +4070,7 @@ Added/removed docs surface as presence divergences (one side
4599
4070
 
4600
4071
 
4601
4072
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><code>branch</code></td><td align="left"><a href="#branch"><code>Branch</code></a></td><td align="left">The experiment.</td></tr><tr><td align="left"><code>target</code></td><td align="left"><a href="#difftarget"><code>DiffTarget</code></a></td><td align="left">Live sandbox or a snapshot to compare against.</td></tr></tbody></table></div>
4602
- <h4 id="returns-90">Returns</h4>
4073
+ <h4 id="returns-72">Returns</h4>
4603
4074
  <p><a href="#divergence"><code>Divergence</code></a>[]</p>
4604
4075
  <hr>
4605
4076
  <p><a id="discard"></a></p>
@@ -4607,7 +4078,7 @@ Added/removed docs surface as presence divergences (one side
4607
4078
  <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">function</span><span style="color:#61AFEF"> discard</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">branch</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">Branch</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">void</span><span style="color:#ABB2BF">;</span></span></code></pre>
4608
4079
  <p>Discard a branch: drop its sandbox and mark it spent. The target is
4609
4080
  never touched (nothing was promoted). Idempotent.</p>
4610
- <h4 id="parameters-51">Parameters</h4>
4081
+ <h4 id="parameters-40">Parameters</h4>
4611
4082
  <div class="table-scroll">
4612
4083
 
4613
4084
 
@@ -4622,7 +4093,7 @@ never touched (nothing was promoted). Idempotent.</p>
4622
4093
 
4623
4094
 
4624
4095
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>branch</code></td><td align="left"><a href="#branch"><code>Branch</code></a></td></tr></tbody></table></div>
4625
- <h4 id="returns-91">Returns</h4>
4096
+ <h4 id="returns-73">Returns</h4>
4626
4097
  <p><code>void</code></p>
4627
4098
  <hr>
4628
4099
  <p><a id="fork"></a></p>
@@ -4633,7 +4104,7 @@ never touched (nothing was promoted). Idempotent.</p>
4633
4104
  (the same <code>seed({ rules, documents })</code> path replay uses to stand up a
4634
4105
  clean environment). The branch is fully isolated: writes on it never
4635
4106
  touch the source sandbox.</p>
4636
- <h4 id="parameters-52">Parameters</h4>
4107
+ <h4 id="parameters-41">Parameters</h4>
4637
4108
  <div class="table-scroll">
4638
4109
 
4639
4110
 
@@ -4654,8 +4125,8 @@ touch the source sandbox.</p>
4654
4125
 
4655
4126
 
4656
4127
 
4657
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><code>snapshot</code></td><td align="left"><a href="#sandboxsnapshot-2"><code>SandboxSnapshot</code></a></td><td align="left">Baseline state — typically <code>liveSandbox.snapshot()</code>.</td></tr><tr><td align="left"><code>rules?</code></td><td align="left"><code>string</code></td><td align="left">Rules source for the branch. Pass the live rules to reproduce production behaviour, or an <em>edited</em> ruleset to test a rules change in isolation (Studio F4).</td></tr></tbody></table></div>
4658
- <h4 id="returns-92">Returns</h4>
4128
+ <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><code>snapshot</code></td><td align="left"><a href="#sandboxsnapshot"><code>SandboxSnapshot</code></a></td><td align="left">Baseline state — typically <code>liveSandbox.snapshot()</code>.</td></tr><tr><td align="left"><code>rules?</code></td><td align="left"><code>string</code></td><td align="left">Rules source for the branch. Pass the live rules to reproduce production behaviour, or an <em>edited</em> ruleset to test a rules change in isolation (Studio F4).</td></tr></tbody></table></div>
4129
+ <h4 id="returns-74">Returns</h4>
4659
4130
  <p><a href="#branch"><code>Branch</code></a></p>
4660
4131
  <hr>
4661
4132
  <p><a id="initializesandbox"></a></p>
@@ -4663,11 +4134,11 @@ touch the source sandbox.</p>
4663
4134
  <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">function</span><span style="color:#61AFEF"> initializeSandbox</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">_config</span><span style="color:#C678DD">?</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">SandboxConfig</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">LocalSandbox</span><span style="color:#ABB2BF">;</span></span></code></pre>
4664
4135
  <p>Create a sandbox.</p>
4665
4136
  <p>Identity is <strong>not</strong> part of init — call <code>sandbox.withAuth(...)</code> to
4666
- derive a <a href="#sandboxcontext">SandboxContext</a> for service operations. Service-
4137
+ derive a <a href="/docs/pyric-firestore-reference-api/#sandboxcontext">SandboxContext</a> for service operations. Service-
4667
4138
  specific configuration (rules, seed data) happens through service-specific
4668
4139
  sandbox controls — for example, <code>setRules(sandbox, source)</code> from
4669
4140
  <code>pyric/sandbox/firestore</code>.</p>
4670
- <h4 id="parameters-53">Parameters</h4>
4141
+ <h4 id="parameters-42">Parameters</h4>
4671
4142
  <div class="table-scroll">
4672
4143
 
4673
4144
 
@@ -4682,9 +4153,9 @@ sandbox controls — for example, <code>setRules(sandbox, source)</code> from
4682
4153
 
4683
4154
 
4684
4155
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>_config?</code></td><td align="left"><a href="#sandboxconfig"><code>SandboxConfig</code></a></td></tr></tbody></table></div>
4685
- <h4 id="returns-93">Returns</h4>
4156
+ <h4 id="returns-75">Returns</h4>
4686
4157
  <p><a href="#localsandbox"><code>LocalSandbox</code></a></p>
4687
- <h4 id="example-6">Example</h4>
4158
+ <h4 id="example-4">Example</h4>
4688
4159
  <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">initializeSandbox</span><span style="color:#ABB2BF"> } </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'pyric/sandbox'</span><span style="color:#ABB2BF">;</span></span>
4689
4160
  <span class="line"><span style="color:#C678DD">import</span><span style="color:#ABB2BF"> { </span><span style="color:#E06C75">getFirestore</span><span style="color:#ABB2BF"> } </span><span style="color:#C678DD">from</span><span style="color:#98C379"> 'pyric-admin/firestore'</span><span style="color:#ABB2BF">;</span></span>
4690
4161
  <span class="line"></span>
@@ -4695,7 +4166,7 @@ sandbox controls — for example, <code>setRules(sandbox, source)</code> from
4695
4166
  <p><a id="isoperationevent"></a></p>
4696
4167
  <h3 id="isoperationevent">isOperationEvent()</h3>
4697
4168
  <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">function</span><span style="color:#61AFEF"> isOperationEvent</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">event</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">SandboxEvent</span><span style="color:#ABB2BF">): </span><span style="color:#E06C75;font-style:italic">event</span><span style="color:#C678DD"> is</span><span style="color:#E5C07B"> OperationEvent</span><span style="color:#ABB2BF">;</span></span></code></pre>
4698
- <h4 id="parameters-54">Parameters</h4>
4169
+ <h4 id="parameters-43">Parameters</h4>
4699
4170
  <div class="table-scroll">
4700
4171
 
4701
4172
 
@@ -4710,7 +4181,7 @@ sandbox controls — for example, <code>setRules(sandbox, source)</code> from
4710
4181
 
4711
4182
 
4712
4183
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>event</code></td><td align="left"><a href="#sandboxevent"><code>SandboxEvent</code></a></td></tr></tbody></table></div>
4713
- <h4 id="returns-94">Returns</h4>
4184
+ <h4 id="returns-76">Returns</h4>
4714
4185
  <p><code>event is OperationEvent</code></p>
4715
4186
  <hr>
4716
4187
  <p><a id="isremotesandbox"></a></p>
@@ -4720,7 +4191,7 @@ sandbox controls — for example, <code>setRules(sandbox, source)</code> from
4720
4191
  (e.g. <code>pyric-admin</code>’s RTDB/Auth sandbox backends) that must route a
4721
4192
  remote sandbox’s operations through <a href="#channel">RemoteSandbox.channel</a> rather
4722
4193
  than into process-local state.</p>
4723
- <h4 id="parameters-55">Parameters</h4>
4194
+ <h4 id="parameters-44">Parameters</h4>
4724
4195
  <div class="table-scroll">
4725
4196
 
4726
4197
 
@@ -4734,8 +4205,8 @@ than into process-local state.</p>
4734
4205
 
4735
4206
 
4736
4207
 
4737
- <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>sandbox</code></td><td align="left"><a href="#sandbox-3"><code>Sandbox</code></a></td></tr></tbody></table></div>
4738
- <h4 id="returns-95">Returns</h4>
4208
+ <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>sandbox</code></td><td align="left"><a href="/docs/pyric-firestore-reference-api/#sandbox"><code>Sandbox</code></a></td></tr></tbody></table></div>
4209
+ <h4 id="returns-77">Returns</h4>
4739
4210
  <p><code>sandbox is RemoteSandbox</code></p>
4740
4211
  <hr>
4741
4212
  <p><a id="operationcontextfor"></a></p>
@@ -4743,7 +4214,7 @@ than into process-local state.</p>
4743
4214
  <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">function</span><span style="color:#61AFEF"> operationContextFor</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">event</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">Pick</span><span style="color:#ABB2BF">&#x3C;</span><span style="color:#E5C07B">EventProvenance</span><span style="color:#ABB2BF">, </span><span style="color:#98C379">"operationContext"</span><span style="color:#ABB2BF"> | </span><span style="color:#98C379">"actor"</span><span style="color:#ABB2BF"> | </span><span style="color:#98C379">"authLens"</span><span style="color:#ABB2BF"> | </span><span style="color:#98C379">"planId"</span><span style="color:#ABB2BF">>): </span><span style="color:#E5C07B">OperationContext</span><span style="color:#ABB2BF">;</span></span></code></pre>
4744
4215
  <p>The canonical context on a recorded event. Old/pre-context events are
4745
4216
  explicitly unattributed rather than silently asserted to be app traffic.</p>
4746
- <h4 id="parameters-56">Parameters</h4>
4217
+ <h4 id="parameters-45">Parameters</h4>
4747
4218
  <div class="table-scroll">
4748
4219
 
4749
4220
 
@@ -4758,7 +4229,7 @@ explicitly unattributed rather than silently asserted to be app traffic.</p>
4758
4229
 
4759
4230
 
4760
4231
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>event</code></td><td align="left"><code>Pick</code>&#x3C;<a href="#eventprovenance"><code>EventProvenance</code></a>, <code>"operationContext"</code> | <code>"actor"</code> | <code>"authLens"</code> | <code>"planId"</code>></td></tr></tbody></table></div>
4761
- <h4 id="returns-96">Returns</h4>
4232
+ <h4 id="returns-78">Returns</h4>
4762
4233
  <p><a href="#operationcontext-2"><code>OperationContext</code></a></p>
4763
4234
  <hr>
4764
4235
  <p><a id="parsebundle"></a></p>
@@ -4767,7 +4238,7 @@ explicitly unattributed rather than silently asserted to be app traffic.</p>
4767
4238
  <p>Parse a v3 bundle blob back into records. Returns an empty map for an
4768
4239
  unrecognized blob (e.g. a legacy v2 single-blob snapshot); migrate-on-open (a
4769
4240
  later commit) handles converting a v2 blob to v3 records.</p>
4770
- <h4 id="parameters-57">Parameters</h4>
4241
+ <h4 id="parameters-46">Parameters</h4>
4771
4242
  <div class="table-scroll">
4772
4243
 
4773
4244
 
@@ -4782,7 +4253,7 @@ later commit) handles converting a v2 blob to v3 records.</p>
4782
4253
 
4783
4254
 
4784
4255
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>blob</code></td><td align="left"><code>string</code></td></tr></tbody></table></div>
4785
- <h4 id="returns-97">Returns</h4>
4256
+ <h4 id="returns-79">Returns</h4>
4786
4257
  <p><code>Map</code>&#x3C;<code>string</code>, <code>unknown</code>></p>
4787
4258
  <hr>
4788
4259
  <p><a id="promote"></a></p>
@@ -4801,7 +4272,7 @@ the admin plane:</p>
4801
4272
  <p>Admin-plane application fires the target’s listeners (matching how
4802
4273
  persistence restore lands docs), so live UI/handles see the promotion.</p>
4803
4274
  <p>The branch is marked discarded afterward — a promoted branch is spent.</p>
4804
- <h4 id="parameters-58">Parameters</h4>
4275
+ <h4 id="parameters-47">Parameters</h4>
4805
4276
  <div class="table-scroll">
4806
4277
 
4807
4278
 
@@ -4823,7 +4294,7 @@ persistence restore lands docs), so live UI/handles see the promotion.</p>
4823
4294
 
4824
4295
 
4825
4296
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><code>branch</code></td><td align="left"><a href="#branch"><code>Branch</code></a></td><td align="left">The experiment to land.</td></tr><tr><td align="left"><code>target</code></td><td align="left"><a href="#localsandbox"><code>LocalSandbox</code></a></td><td align="left">The live sandbox to land it on.</td></tr></tbody></table></div>
4826
- <h4 id="returns-98">Returns</h4>
4297
+ <h4 id="returns-80">Returns</h4>
4827
4298
  <p><code>void</code></p>
4828
4299
  <hr>
4829
4300
  <p><a id="recordbackendoverblob"></a></p>
@@ -4838,7 +4309,7 @@ The whole record set is bundled into that blob, for serve’s committable export
4838
4309
  file / HTTP state endpoint and any single-key store. It loses chunking’s scale
4839
4310
  benefit by design (the blob IS the single artifact). The blob is loaded once and
4840
4311
  cached, so a restore (list + per-record get) costs one read, not one per record.</p>
4841
- <h4 id="parameters-59">Parameters</h4>
4312
+ <h4 id="parameters-48">Parameters</h4>
4842
4313
  <div class="table-scroll">
4843
4314
 
4844
4315
 
@@ -4865,7 +4336,7 @@ cached, so a restore (list + per-record get) costs one read, not one per record.
4865
4336
 
4866
4337
 
4867
4338
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>io</code></td><td align="left">{ <code>clear</code>: <code>Promise</code>&#x3C;<code>void</code>>; <code>read</code>: <code>Promise</code>&#x3C;<code>string</code>>; <code>write</code>: <code>Promise</code>&#x3C;<code>void</code>>; }</td></tr><tr><td align="left"><code>io.clear</code></td><td align="left"></td></tr><tr><td align="left"><code>io.read</code></td><td align="left"></td></tr><tr><td align="left"><code>io.write</code></td><td align="left"></td></tr></tbody></table></div>
4868
- <h4 id="returns-99">Returns</h4>
4339
+ <h4 id="returns-81">Returns</h4>
4869
4340
  <p><a href="#persistencebackend"><code>PersistenceBackend</code></a></p>
4870
4341
  <hr>
4871
4342
  <p><a id="rehydratedocvalue"></a></p>
@@ -4877,7 +4348,7 @@ values (and plain objects without a recognized discriminator) pass through.</p>
4877
4348
  <p>This is the canonical rehydrate used by BOTH the sandbox persistence
4878
4349
  serializer and the SharedWorker wire protocol, so the IDB format and the
4879
4350
  MessagePort wire format are guaranteed identical.</p>
4880
- <h4 id="parameters-60">Parameters</h4>
4351
+ <h4 id="parameters-49">Parameters</h4>
4881
4352
  <div class="table-scroll">
4882
4353
 
4883
4354
 
@@ -4892,7 +4363,7 @@ MessagePort wire format are guaranteed identical.</p>
4892
4363
 
4893
4364
 
4894
4365
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>value</code></td><td align="left"><code>unknown</code></td></tr></tbody></table></div>
4895
- <h4 id="returns-100">Returns</h4>
4366
+ <h4 id="returns-82">Returns</h4>
4896
4367
  <p><code>unknown</code></p>
4897
4368
  <hr>
4898
4369
  <p><a id="replay-1"></a></p>
@@ -4907,7 +4378,7 @@ MessagePort wire format are guaranteed identical.</p>
4907
4378
  diffs the replayed sandbox’s final state against it and returns
4908
4379
  classified divergences. When omitted, divergences is <code>[]</code> (you still
4909
4380
  get the replayed sandbox; you can inspect its state manually).</p>
4910
- <h4 id="parameters-61">Parameters</h4>
4381
+ <h4 id="parameters-50">Parameters</h4>
4911
4382
  <div class="table-scroll">
4912
4383
 
4913
4384
 
@@ -4934,7 +4405,7 @@ get the replayed sandbox; you can inspect its state manually).</p>
4934
4405
 
4935
4406
 
4936
4407
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>events</code></td><td align="left">readonly <a href="#sandboxevent"><code>SandboxEvent</code></a>[]</td></tr><tr><td align="left"><code>rules</code></td><td align="left"><code>string</code></td></tr><tr><td align="left"><code>options?</code></td><td align="left"><a href="#replayoptions"><code>ReplayOptions</code></a></td></tr><tr><td align="left"><code>originalState?</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>DocData</code>></td></tr></tbody></table></div>
4937
- <h4 id="returns-101">Returns</h4>
4408
+ <h4 id="returns-83">Returns</h4>
4938
4409
  <p><a href="#replayresult"><code>ReplayResult</code></a></p>
4939
4410
  <hr>
4940
4411
  <p><a id="rulesdispositionfor"></a></p>
@@ -4943,7 +4414,7 @@ get the replayed sandbox; you can inspect its state manually).</p>
4943
4414
  <p>Normalize the legacy per-service markers exactly once, at the sandbox
4944
4415
  stream seam. Consumers must not inspect <code>detail.admin</code>, <code>origin</code>, or the
4945
4416
  presence of a trace themselves.</p>
4946
- <h4 id="parameters-62">Parameters</h4>
4417
+ <h4 id="parameters-51">Parameters</h4>
4947
4418
  <div class="table-scroll">
4948
4419
 
4949
4420
 
@@ -4958,7 +4429,7 @@ presence of a trace themselves.</p>
4958
4429
 
4959
4430
 
4960
4431
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>event</code></td><td align="left"><code>OperationEvent</code></td></tr></tbody></table></div>
4961
- <h4 id="returns-102">Returns</h4>
4432
+ <h4 id="returns-84">Returns</h4>
4962
4433
  <p><a href="#rulesdisposition-2"><code>RulesDisposition</code></a></p>
4963
4434
  <hr>
4964
4435
  <p><a id="serializetobuckets"></a></p>
@@ -4970,7 +4441,7 @@ presence of a trace themselves.</p>
4970
4441
  <p>Partition a firestore doc map into v3 records: one bucket record per occupied
4971
4442
  bucket plus the <code>meta</code> record. The returned map’s keys are record ids; values
4972
4443
  are structured-clone-safe.</p>
4973
- <h4 id="parameters-63">Parameters</h4>
4444
+ <h4 id="parameters-52">Parameters</h4>
4974
4445
  <div class="table-scroll">
4975
4446
 
4976
4447
 
@@ -4993,14 +4464,14 @@ are structured-clone-safe.</p>
4993
4464
 
4994
4465
 
4995
4466
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>firestore</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>>></td></tr><tr><td align="left"><code>services</code></td><td align="left"><code>Record</code>&#x3C;<code>string</code>, <code>unknown</code>></td></tr><tr><td align="left"><code>savedAt</code></td><td align="left"><code>number</code></td></tr></tbody></table></div>
4996
- <h4 id="returns-103">Returns</h4>
4467
+ <h4 id="returns-85">Returns</h4>
4997
4468
  <p><code>Map</code>&#x3C;<code>string</code>, <code>BucketRecord</code> | <code>MetaRecord</code>></p>
4998
4469
  <hr>
4999
4470
  <p><a id="tooperationrecord"></a></p>
5000
4471
  <h3 id="tooperationrecord">toOperationRecord()</h3>
5001
4472
  <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">function</span><span style="color:#61AFEF"> toOperationRecord</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">event</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">SandboxEvent</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">OperationRecord</span><span style="color:#ABB2BF">;</span></span></code></pre>
5002
4473
  <p>Project either traffic event family into the canonical record.</p>
5003
- <h4 id="parameters-64">Parameters</h4>
4474
+ <h4 id="parameters-53">Parameters</h4>
5004
4475
  <div class="table-scroll">
5005
4476
 
5006
4477
 
@@ -5015,5 +4486,21 @@ are structured-clone-safe.</p>
5015
4486
 
5016
4487
 
5017
4488
  <table><thead><tr><th align="left">Parameter</th><th align="left">Type</th></tr></thead><tbody><tr><td align="left"><code>event</code></td><td align="left"><a href="#sandboxevent"><code>SandboxEvent</code></a></td></tr></tbody></table></div>
5018
- <h4 id="returns-104">Returns</h4>
5019
- <p><a href="#operationrecord"><code>OperationRecord</code></a></p> </article> </main> <aside class="doc-toc api-symbols" aria-label="API symbols"> <h2 class="doc-toc-heading">Symbols</h2> <input class="api-symbol-filter" type="search" placeholder="Filter symbols" aria-label="Filter API symbols" data-api-symbol-filter> <div class="api-symbol-groups" data-api-symbol-groups> <section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#classes"> Classes </a> <ul> <li data-api-symbol="persistenceschemaerror"> <a href="#persistenceschemaerror">PersistenceSchemaError</a> </li><li data-api-symbol="sandboxcontextimpl"> <a href="#sandboxcontextimpl">SandboxContextImpl</a> </li><li data-api-symbol="sandboxerror"> <a href="#sandboxerror">SandboxError</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#interfaces"> Interfaces </a> <ul> <li data-api-symbol="branch"> <a href="#branch">Branch</a> </li><li data-api-symbol="broadcastchannellike"> <a href="#broadcastchannellike">BroadcastChannelLike</a> </li><li data-api-symbol="denialcontext"> <a href="#denialcontext">DenialContext</a> </li><li data-api-symbol="denialevent"> <a href="#denialevent">DenialEvent</a> </li><li data-api-symbol="eventprovenance"> <a href="#eventprovenance">EventProvenance</a> </li><li data-api-symbol="listenerlifecycleevent"> <a href="#listenerlifecycleevent">ListenerLifecycleEvent</a> </li><li data-api-symbol="localsandbox"> <a href="#localsandbox">LocalSandbox</a> </li><li data-api-symbol="operationcontext"> <a href="#operationcontext">OperationContext</a> </li><li data-api-symbol="operationrecord"> <a href="#operationrecord">OperationRecord</a> </li><li data-api-symbol="persistableservice"> <a href="#persistableservice">PersistableService</a> </li><li data-api-symbol="persistencebackend"> <a href="#persistencebackend">PersistenceBackend</a> </li><li data-api-symbol="persistencecontroller"> <a href="#persistencecontroller">PersistenceController</a> </li><li data-api-symbol="remotesandbox"> <a href="#remotesandbox">RemoteSandbox</a> </li><li data-api-symbol="remotesandboxchannel"> <a href="#remotesandboxchannel">RemoteSandboxChannel</a> </li><li data-api-symbol="remotesandboxfactoryoptions"> <a href="#remotesandboxfactoryoptions">RemoteSandboxFactoryOptions</a> </li><li data-api-symbol="replayoptions"> <a href="#replayoptions">ReplayOptions</a> </li><li data-api-symbol="replayresult"> <a href="#replayresult">ReplayResult</a> </li><li data-api-symbol="requestevent"> <a href="#requestevent">RequestEvent</a> </li><li data-api-symbol="sandbox"> <a href="#sandbox">Sandbox</a> </li><li data-api-symbol="sandboxcommitevent"> <a href="#sandboxcommitevent">SandboxCommitEvent</a> </li><li data-api-symbol="sandboxconfig"> <a href="#sandboxconfig">SandboxConfig</a> </li><li data-api-symbol="sandboxcontext"> <a href="#sandboxcontext">SandboxContext</a> </li><li data-api-symbol="sandboxlistenerevent"> <a href="#sandboxlistenerevent">SandboxListenerEvent</a> </li><li data-api-symbol="sandboxoperationevent"> <a href="#sandboxoperationevent">SandboxOperationEvent</a> </li><li data-api-symbol="sandboxpersistenceoptions"> <a href="#sandboxpersistenceoptions">SandboxPersistenceOptions</a> </li><li data-api-symbol="sandboxruntimeerrorevent"> <a href="#sandboxruntimeerrorevent">SandboxRuntimeErrorEvent</a> </li><li data-api-symbol="sandboxsnapshot"> <a href="#sandboxsnapshot">SandboxSnapshot</a> </li><li data-api-symbol="servicemutationevent"> <a href="#servicemutationevent">ServiceMutationEvent</a> </li><li data-api-symbol="sessionboundaryevent"> <a href="#sessionboundaryevent">SessionBoundaryEvent</a> </li><li data-api-symbol="snapshotdeliveryevent"> <a href="#snapshotdeliveryevent">SnapshotDeliveryEvent</a> </li><li data-api-symbol="snapshoterrorevent"> <a href="#snapshoterrorevent">SnapshotErrorEvent</a> </li><li data-api-symbol="snapshotsuppressedevent"> <a href="#snapshotsuppressedevent">SnapshotSuppressedEvent</a> </li><li data-api-symbol="tabsyncoptions"> <a href="#tabsyncoptions">TabSyncOptions</a> </li><li data-api-symbol="webstoragelike"> <a href="#webstoragelike">WebStorageLike</a> </li><li data-api-symbol="writesandboxevent"> <a href="#writesandboxevent">WriteSandboxEvent</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#type-aliases"> Type Aliases </a> <ul> <li data-api-symbol="authlens"> <a href="#authlens">AuthLens</a> </li><li data-api-symbol="authstate"> <a href="#authstate">AuthState</a> </li><li data-api-symbol="difftarget"> <a href="#difftarget">DiffTarget</a> </li><li data-api-symbol="divergence"> <a href="#divergence">Divergence</a> </li><li data-api-symbol="eventactor"> <a href="#eventactor">EventActor</a> </li><li data-api-symbol="eventservice"> <a href="#eventservice">EventService</a> </li><li data-api-symbol="remotesandboxfactory()"> <a href="#remotesandboxfactory">RemoteSandboxFactory()</a> </li><li data-api-symbol="rulesdisposition"> <a href="#rulesdisposition">RulesDisposition</a> </li><li data-api-symbol="sandboxerrorcode"> <a href="#sandboxerrorcode">SandboxErrorCode</a> </li><li data-api-symbol="sandboxevent"> <a href="#sandboxevent">SandboxEvent</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#variables"> Variables </a> <ul> <li data-api-symbol="remote_sandbox"> <a href="#remote_sandbox">REMOTE_SANDBOX</a> </li><li data-api-symbol="remote_sandbox_factory"> <a href="#remote_sandbox_factory">REMOTE_SANDBOX_FACTORY</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#functions"> Functions </a> <ul> <li data-api-symbol="apply()"> <a href="#apply">apply()</a> </li><li data-api-symbol="attachpersistence()"> <a href="#attachpersistence">attachPersistence()</a> </li><li data-api-symbol="attachtabsync()"> <a href="#attachtabsync">attachTabSync()</a> </li><li data-api-symbol="bundlerecords()"> <a href="#bundlerecords">bundleRecords()</a> </li><li data-api-symbol="createindexeddbbackend()"> <a href="#createindexeddbbackend">createIndexedDBBackend()</a> </li><li data-api-symbol="creatememorybackend()"> <a href="#creatememorybackend">createMemoryBackend()</a> </li><li data-api-symbol="deserializefrombuckets()"> <a href="#deserializefrombuckets">deserializeFromBuckets()</a> </li><li data-api-symbol="diff()"> <a href="#diff">diff()</a> </li><li data-api-symbol="discard()"> <a href="#discard">discard()</a> </li><li data-api-symbol="fork()"> <a href="#fork">fork()</a> </li><li data-api-symbol="initializesandbox()"> <a href="#initializesandbox">initializeSandbox()</a> </li><li data-api-symbol="isoperationevent()"> <a href="#isoperationevent">isOperationEvent()</a> </li><li data-api-symbol="isremotesandbox()"> <a href="#isremotesandbox">isRemoteSandbox()</a> </li><li data-api-symbol="operationcontextfor()"> <a href="#operationcontextfor">operationContextFor()</a> </li><li data-api-symbol="parsebundle()"> <a href="#parsebundle">parseBundle()</a> </li><li data-api-symbol="promote()"> <a href="#promote">promote()</a> </li><li data-api-symbol="recordbackendoverblob()"> <a href="#recordbackendoverblob">recordBackendOverBlob()</a> </li><li data-api-symbol="rehydratedocvalue()"> <a href="#rehydratedocvalue">rehydrateDocValue()</a> </li><li data-api-symbol="replay()"> <a href="#replay">replay()</a> </li><li data-api-symbol="rulesdispositionfor()"> <a href="#rulesdispositionfor">rulesDispositionFor()</a> </li><li data-api-symbol="serializetobuckets()"> <a href="#serializetobuckets">serializeToBuckets()</a> </li><li data-api-symbol="tooperationrecord()"> <a href="#tooperationrecord">toOperationRecord()</a> </li> </ul> </section> <p class="api-symbol-empty" data-api-symbol-empty hidden>No matching symbols</p> </div> </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>
4489
+ <h4 id="returns-86">Returns</h4>
4490
+ <p><a href="#operationrecord"><code>OperationRecord</code></a></p>
4491
+ <h2 id="references">References</h2>
4492
+ <p><a id="authstate"></a></p>
4493
+ <h3 id="authstate">AuthState</h3>
4494
+ <p>Re-exports <a href="/docs/pyric-firestore-reference-api/#authstate">AuthState</a></p>
4495
+ <hr>
4496
+ <p><a id="sandbox-3"></a></p>
4497
+ <h3 id="sandbox">Sandbox</h3>
4498
+ <p>Re-exports <a href="/docs/pyric-firestore-reference-api/#sandbox">Sandbox</a></p>
4499
+ <hr>
4500
+ <p><a id="sandboxcontext"></a></p>
4501
+ <h3 id="sandboxcontext">SandboxContext</h3>
4502
+ <p>Re-exports <a href="/docs/pyric-firestore-reference-api/#sandboxcontext">SandboxContext</a></p>
4503
+ <hr>
4504
+ <p><a id="sandboxerror"></a></p>
4505
+ <h3 id="sandboxerror">SandboxError</h3>
4506
+ <p>Re-exports <a href="/docs/pyric-firestore-reference-api/#sandboxerror">SandboxError</a></p> </article> </main> <aside class="doc-toc api-symbols" aria-label="API symbols"> <h2 class="doc-toc-heading">Symbols</h2> <input class="api-symbol-filter" type="search" placeholder="Filter symbols" aria-label="Filter API symbols" data-api-symbol-filter> <div class="api-symbol-groups" data-api-symbol-groups> <section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#classes"> Classes </a> <ul> <li data-api-symbol="persistenceschemaerror"> <a href="#persistenceschemaerror">PersistenceSchemaError</a> </li><li data-api-symbol="sandboxcontextimpl"> <a href="#sandboxcontextimpl">SandboxContextImpl</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#interfaces"> Interfaces </a> <ul> <li data-api-symbol="activityeventprovenance"> <a href="#activityeventprovenance">ActivityEventProvenance</a> </li><li data-api-symbol="branch"> <a href="#branch">Branch</a> </li><li data-api-symbol="broadcastchannellike"> <a href="#broadcastchannellike">BroadcastChannelLike</a> </li><li data-api-symbol="denialcontext"> <a href="#denialcontext">DenialContext</a> </li><li data-api-symbol="denialevent"> <a href="#denialevent">DenialEvent</a> </li><li data-api-symbol="eventprovenance"> <a href="#eventprovenance">EventProvenance</a> </li><li data-api-symbol="listenerlifecycleevent"> <a href="#listenerlifecycleevent">ListenerLifecycleEvent</a> </li><li data-api-symbol="localsandbox"> <a href="#localsandbox">LocalSandbox</a> </li><li data-api-symbol="operationcontext"> <a href="#operationcontext">OperationContext</a> </li><li data-api-symbol="operationrecord"> <a href="#operationrecord">OperationRecord</a> </li><li data-api-symbol="persistableservice"> <a href="#persistableservice">PersistableService</a> </li><li data-api-symbol="persistencebackend"> <a href="#persistencebackend">PersistenceBackend</a> </li><li data-api-symbol="persistencecontroller"> <a href="#persistencecontroller">PersistenceController</a> </li><li data-api-symbol="remotesandbox"> <a href="#remotesandbox">RemoteSandbox</a> </li><li data-api-symbol="remotesandboxchannel"> <a href="#remotesandboxchannel">RemoteSandboxChannel</a> </li><li data-api-symbol="remotesandboxfactoryoptions"> <a href="#remotesandboxfactoryoptions">RemoteSandboxFactoryOptions</a> </li><li data-api-symbol="replayoptions"> <a href="#replayoptions">ReplayOptions</a> </li><li data-api-symbol="replayresult"> <a href="#replayresult">ReplayResult</a> </li><li data-api-symbol="requestevent"> <a href="#requestevent">RequestEvent</a> </li><li data-api-symbol="sandboxcommitevent"> <a href="#sandboxcommitevent">SandboxCommitEvent</a> </li><li data-api-symbol="sandboxconfig"> <a href="#sandboxconfig">SandboxConfig</a> </li><li data-api-symbol="sandboxlistenerevent"> <a href="#sandboxlistenerevent">SandboxListenerEvent</a> </li><li data-api-symbol="sandboxoperationevent"> <a href="#sandboxoperationevent">SandboxOperationEvent</a> </li><li data-api-symbol="sandboxpersistenceoptions"> <a href="#sandboxpersistenceoptions">SandboxPersistenceOptions</a> </li><li data-api-symbol="sandboxruntimeerrorevent"> <a href="#sandboxruntimeerrorevent">SandboxRuntimeErrorEvent</a> </li><li data-api-symbol="sandboxsnapshot"> <a href="#sandboxsnapshot">SandboxSnapshot</a> </li><li data-api-symbol="servicemutationevent"> <a href="#servicemutationevent">ServiceMutationEvent</a> </li><li data-api-symbol="sessionboundaryevent"> <a href="#sessionboundaryevent">SessionBoundaryEvent</a> </li><li data-api-symbol="snapshotdeliveryevent"> <a href="#snapshotdeliveryevent">SnapshotDeliveryEvent</a> </li><li data-api-symbol="snapshoterrorevent"> <a href="#snapshoterrorevent">SnapshotErrorEvent</a> </li><li data-api-symbol="snapshotsuppressedevent"> <a href="#snapshotsuppressedevent">SnapshotSuppressedEvent</a> </li><li data-api-symbol="tabsyncoptions"> <a href="#tabsyncoptions">TabSyncOptions</a> </li><li data-api-symbol="webstoragelike"> <a href="#webstoragelike">WebStorageLike</a> </li><li data-api-symbol="writesandboxevent"> <a href="#writesandboxevent">WriteSandboxEvent</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#type-aliases"> Type Aliases </a> <ul> <li data-api-symbol="authlens"> <a href="#authlens">AuthLens</a> </li><li data-api-symbol="difftarget"> <a href="#difftarget">DiffTarget</a> </li><li data-api-symbol="divergence"> <a href="#divergence">Divergence</a> </li><li data-api-symbol="eventactor"> <a href="#eventactor">EventActor</a> </li><li data-api-symbol="eventservice"> <a href="#eventservice">EventService</a> </li><li data-api-symbol="remotesandboxfactory()"> <a href="#remotesandboxfactory">RemoteSandboxFactory()</a> </li><li data-api-symbol="rulesdisposition"> <a href="#rulesdisposition">RulesDisposition</a> </li><li data-api-symbol="sandboxerrorcode"> <a href="#sandboxerrorcode">SandboxErrorCode</a> </li><li data-api-symbol="sandboxevent"> <a href="#sandboxevent">SandboxEvent</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#variables"> Variables </a> <ul> <li data-api-symbol="remote_sandbox"> <a href="#remote_sandbox">REMOTE_SANDBOX</a> </li><li data-api-symbol="remote_sandbox_factory"> <a href="#remote_sandbox_factory">REMOTE_SANDBOX_FACTORY</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#functions"> Functions </a> <ul> <li data-api-symbol="apply()"> <a href="#apply">apply()</a> </li><li data-api-symbol="attachpersistence()"> <a href="#attachpersistence">attachPersistence()</a> </li><li data-api-symbol="attachtabsync()"> <a href="#attachtabsync">attachTabSync()</a> </li><li data-api-symbol="bundlerecords()"> <a href="#bundlerecords">bundleRecords()</a> </li><li data-api-symbol="createindexeddbbackend()"> <a href="#createindexeddbbackend">createIndexedDBBackend()</a> </li><li data-api-symbol="creatememorybackend()"> <a href="#creatememorybackend">createMemoryBackend()</a> </li><li data-api-symbol="deserializefrombuckets()"> <a href="#deserializefrombuckets">deserializeFromBuckets()</a> </li><li data-api-symbol="diff()"> <a href="#diff">diff()</a> </li><li data-api-symbol="discard()"> <a href="#discard">discard()</a> </li><li data-api-symbol="fork()"> <a href="#fork">fork()</a> </li><li data-api-symbol="initializesandbox()"> <a href="#initializesandbox">initializeSandbox()</a> </li><li data-api-symbol="isoperationevent()"> <a href="#isoperationevent">isOperationEvent()</a> </li><li data-api-symbol="isremotesandbox()"> <a href="#isremotesandbox">isRemoteSandbox()</a> </li><li data-api-symbol="operationcontextfor()"> <a href="#operationcontextfor">operationContextFor()</a> </li><li data-api-symbol="parsebundle()"> <a href="#parsebundle">parseBundle()</a> </li><li data-api-symbol="promote()"> <a href="#promote">promote()</a> </li><li data-api-symbol="recordbackendoverblob()"> <a href="#recordbackendoverblob">recordBackendOverBlob()</a> </li><li data-api-symbol="rehydratedocvalue()"> <a href="#rehydratedocvalue">rehydrateDocValue()</a> </li><li data-api-symbol="replay()"> <a href="#replay">replay()</a> </li><li data-api-symbol="rulesdispositionfor()"> <a href="#rulesdispositionfor">rulesDispositionFor()</a> </li><li data-api-symbol="serializetobuckets()"> <a href="#serializetobuckets">serializeToBuckets()</a> </li><li data-api-symbol="tooperationrecord()"> <a href="#tooperationrecord">toOperationRecord()</a> </li> </ul> </section><section class="api-symbol-group" data-api-symbol-group> <a class="api-symbol-kind" href="#references"> References </a> <ul> <li data-api-symbol="authstate"> <a href="#authstate">AuthState</a> </li><li data-api-symbol="sandbox"> <a href="#sandbox">Sandbox</a> </li><li data-api-symbol="sandboxcontext"> <a href="#sandboxcontext">SandboxContext</a> </li><li data-api-symbol="sandboxerror"> <a href="#sandboxerror">SandboxError</a> </li> </ul> </section> <p class="api-symbol-empty" data-api-symbol-empty hidden>No matching symbols</p> </div> </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>