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

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 (304) 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 +1 -1
  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 +2 -2
  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 +800 -575
  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-storage-reference-api/index.html +4 -3
  101. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api.md +2 -3
  102. package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +16 -11
  103. package/dist/serve/docs-ui/docs/pyric-ai-reference-api.md +14 -13
  104. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +4 -3
  105. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api.md +2 -3
  106. package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +7 -2
  107. package/dist/serve/docs-ui/docs/pyric-app-reference-api.md +5 -2
  108. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +23 -12
  109. package/dist/serve/docs-ui/docs/pyric-auth-reference-api.md +15 -12
  110. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +10 -48
  111. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api.md +11 -24
  112. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +87 -97
  113. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api.md +53 -65
  114. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +14 -15
  115. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api.md +19 -28
  116. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +4 -3
  117. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api.md +2 -3
  118. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +3 -2
  119. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api.md +1 -2
  120. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +51 -0
  121. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api.md +36 -0
  122. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +3 -2
  123. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api.md +1 -2
  124. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +3 -2
  125. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api.md +1 -2
  126. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +3 -2
  127. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api.md +1 -2
  128. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +3 -2
  129. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api.md +1 -2
  130. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1067 -83
  131. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api.md +1291 -160
  132. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +123 -92
  133. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +49 -20
  134. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +72 -7
  135. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api.md +22 -7
  136. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +33 -12
  137. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +15 -12
  138. package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +18 -17
  139. package/dist/serve/docs-ui/docs/pyric-database-reference-api.md +19 -22
  140. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1512 -545
  141. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api.md +1311 -316
  142. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +3 -2
  143. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +1 -2
  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 +374 -910
  155. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +377 -911
  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 +1 -0
  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 +33 -8
  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/studio.d.ts +1 -1
  274. package/dist/serve/worker/host/studio.d.ts.map +1 -1
  275. package/dist/serve/worker/host/studio.js +36 -1
  276. package/dist/serve/worker/host/studio.js.map +1 -1
  277. package/dist/serve/worker/host/subscriptions.d.ts.map +1 -1
  278. package/dist/serve/worker/host/subscriptions.js +27 -7
  279. package/dist/serve/worker/host/subscriptions.js.map +1 -1
  280. package/dist/serve/worker/host-context.d.ts +15 -0
  281. package/dist/serve/worker/host-context.d.ts.map +1 -1
  282. package/dist/serve/worker/host-context.js +10 -0
  283. package/dist/serve/worker/host-context.js.map +1 -1
  284. package/dist/serve/worker/host-messaging.d.ts.map +1 -1
  285. package/dist/serve/worker/host-messaging.js +9 -2
  286. package/dist/serve/worker/host-messaging.js.map +1 -1
  287. package/dist/serve/worker/index.d.ts +1 -1
  288. package/dist/serve/worker/index.d.ts.map +1 -1
  289. package/dist/serve/worker/index.js +2 -0
  290. package/dist/serve/worker/index.js.map +1 -1
  291. package/dist/serve/worker/protocol.d.ts +25 -3
  292. package/dist/serve/worker/protocol.d.ts.map +1 -1
  293. package/dist/serve/worker/protocol.js.map +1 -1
  294. package/dist/serve/worker/serve-init.d.ts +23 -37
  295. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  296. package/dist/serve/worker/serve-init.js +95 -172
  297. package/dist/serve/worker/serve-init.js.map +1 -1
  298. package/dist/vite.d.ts +4 -4
  299. package/dist/vite.d.ts.map +1 -1
  300. package/dist/vite.js +3 -3
  301. package/dist/vite.js.map +1 -1
  302. package/package.json +8 -4
  303. package/dist/serve/studio-ui/assets/index-BvtQIpGn.js +0 -1211
  304. package/dist/serve/studio-ui/assets/seed-3DMedLL3.js +0 -2
@@ -1,5 +1,4 @@
1
-
2
- <!-- Generated from published package declarations via TypeDoc. Do not edit by hand; run bun run docs:api:generate. -->
1
+ <!-- Generated from published package declarations via TypeDoc. Rendered by the api-reference content loader. -->
3
2
 
4
3
  ## Classes
5
4
 
@@ -43,7 +42,7 @@ Error.constructor
43
42
 
44
43
  ### SandboxContextImpl
45
44
 
46
- Identity-bearing handle on a [Sandbox](#sandbox-3). A
45
+ Identity-bearing handle on a [Sandbox](pyric-firestore-reference-api.md#sandbox). A
47
46
  `(sandbox, auth, operationContext)`
48
47
  tuple — cheap to create, immutable, freely shareable. Service
49
48
  factories require a `SandboxContext`; bare `Sandbox` is a type
@@ -56,7 +55,7 @@ factories; consumers don't construct it directly.
56
55
 
57
56
  #### Implements
58
57
 
59
- - [`SandboxContext`](#sandboxcontext)
58
+ - [`SandboxContext`](pyric-firestore-reference-api.md#sandboxcontext)
60
59
 
61
60
  #### Constructors
62
61
 
@@ -78,7 +77,7 @@ new SandboxContextImpl(
78
77
 
79
78
  | Parameter | Type |
80
79
  | :------ | :------ |
81
- | `sandbox` | [`Sandbox`](#sandbox-3) |
80
+ | `sandbox` | [`Sandbox`](pyric-firestore-reference-api.md#sandbox) |
82
81
  | `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
83
82
  | `auth.token?` | `Record`\<`string`, `unknown`\> |
84
83
  | `auth.uid?` | `string` |
@@ -96,7 +95,7 @@ new SandboxContextImpl(
96
95
  | `auth.token?` | `public` | `Record`\<`string`, `unknown`\> | - |
97
96
  | `auth.uid` | `public` | `string` | - |
98
97
  | <a id="operationcontext"></a> `operationContext` | `readonly` | [`OperationContext`](#operationcontext-2) | Immutable provenance bound to every operation issued through this handle. |
99
- | <a id="sandbox"></a> `sandbox` | `readonly` | [`Sandbox`](#sandbox-3) | The data foundation this context operates against. |
98
+ | <a id="sandbox"></a> `sandbox` | `readonly` | [`Sandbox`](pyric-firestore-reference-api.md#sandbox) | The data foundation this context operates against. |
100
99
 
101
100
  #### Methods
102
101
 
@@ -125,101 +124,37 @@ optional plan identity.
125
124
 
126
125
  ###### Returns
127
126
 
128
- [`SandboxContext`](#sandboxcontext)
127
+ [`SandboxContext`](pyric-firestore-reference-api.md#sandboxcontext)
129
128
 
130
129
  ###### Implementation of
131
130
 
132
- [`SandboxContext`](#sandboxcontext).[`withAuth`](#withauth-8)
133
-
134
- ***
135
-
136
- <a id="sandboxerror"></a>
137
-
138
- ### SandboxError
139
-
140
- Sandbox-layer error. Catch with `instanceof SandboxError` and switch
141
- on `code`. `denialContext` is populated for `permission-denied` only
142
- (and only after Slice 4 wires it through).
143
-
144
- Two construction forms are supported:
145
- - Positional: `new SandboxError(code, message, denialContext?)` —
146
- the original signature, kept for backward compatibility with
147
- existing internal call sites.
148
- - Options bag: `new SandboxError({ code, message, remediation? })` —
149
- used when attaching remediation guidance.
150
-
151
- #### Extends
152
-
153
- - `Error`
154
-
155
- #### Constructors
156
-
157
- <a id="constructor-2"></a>
158
-
159
- ##### Constructor
160
-
161
- ```ts
162
- new SandboxError(
163
- code: SandboxErrorCode,
164
- message: string,
165
- denialContext?: DenialContext): SandboxError;
166
- ```
167
-
168
- ###### Parameters
169
-
170
- | Parameter | Type |
171
- | :------ | :------ |
172
- | `code` | [`SandboxErrorCode`](#sandboxerrorcode-1) |
173
- | `message` | `string` |
174
- | `denialContext?` | [`DenialContext`](#denialcontext-1) |
175
-
176
- ###### Returns
177
-
178
- [`SandboxError`](#sandboxerror)
179
-
180
- ###### Overrides
181
-
182
- ```ts
183
- Error.constructor
184
- ```
185
-
186
- ##### Constructor
187
-
188
- ```ts
189
- new SandboxError(options: SandboxErrorOptions): SandboxError;
190
- ```
191
-
192
- ###### Parameters
193
-
194
- | Parameter | Type |
195
- | :------ | :------ |
196
- | `options` | `SandboxErrorOptions` |
131
+ [`SandboxContext`](pyric-firestore-reference-api.md#sandboxcontext).[`withAuth`](pyric-firestore-reference-api.md#withauth-2)
197
132
 
198
- ###### Returns
133
+ ## Interfaces
199
134
 
200
- [`SandboxError`](#sandboxerror)
135
+ <a id="activityeventprovenance"></a>
201
136
 
202
- ###### Overrides
137
+ ### ActivityEventProvenance
203
138
 
204
- ```ts
205
- Error.constructor
206
- ```
139
+ Provenance consumed only by the firestore activity diagnostics. Bundled in
140
+ one optional record so the shared operation type carries a single
141
+ activity-owned seam rather than loose per-feature fields.
207
142
 
208
143
  #### Properties
209
144
 
210
- | Property | Modifier | Type |
145
+ | Property | Type | Description |
211
146
  | :------ | :------ | :------ |
212
- | <a id="code"></a> `code` | `readonly` | [`SandboxErrorCode`](#sandboxerrorcode-1) |
213
- | <a id="denialcontext"></a> `denialContext?` | `readonly` | [`DenialContext`](#denialcontext-1) |
214
- | <a id="remediation"></a> `remediation?` | `readonly` | `string` |
147
+ | <a id="groupkind"></a> `groupKind?` | `"transaction"` | Marks worker-split operations that belong to a transaction for activity diagnostics. |
148
+ | <a id="listenerid"></a> `listenerId?` | `string` | Stable host subscription identity used only by activity diagnostics. |
149
+ | <a id="listenerlifecycle"></a> `listenerLifecycle?` | `"reauthorize"` | Marks lifecycle caused by transparent auth reauthorization, not app code. |
215
150
 
216
- ## Interfaces
151
+ ***
217
152
 
218
153
  <a id="branch"></a>
219
154
 
220
155
  ### Branch
221
156
 
222
- An isolated, in-memory experiment seeded from a [SandboxSnapshot](#sandboxsnapshot-2).
157
+ An isolated, in-memory experiment seeded from a [SandboxSnapshot](#sandboxsnapshot).
223
158
 
224
159
  A branch owns its own [LocalSandbox](#localsandbox) (fully isolated from the source —
225
160
  separate `LocalEnvironment`, separate event history) plus the
@@ -230,7 +165,7 @@ applied events are what [promote](#promote) replays onto the target.
230
165
 
231
166
  | Property | Modifier | Type | Description |
232
167
  | :------ | :------ | :------ | :------ |
233
- | <a id="base"></a> `base` | `readonly` | [`SandboxSnapshot`](#sandboxsnapshot-2) | The snapshot this branch was forked from. Retained so [diff](#diff) and [promote](#promote) can reason about the baseline. |
168
+ | <a id="base"></a> `base` | `readonly` | [`SandboxSnapshot`](#sandboxsnapshot) | The snapshot this branch was forked from. Retained so [diff](#diff) and [promote](#promote) can reason about the baseline. |
234
169
  | <a id="discarded"></a> `discarded` | `public` | `boolean` | Flipped by [discard](#discard); subsequent [apply](#apply)/[promote](#promote) calls throw. |
235
170
  | <a id="events"></a> `events` | `readonly` | [`SandboxEvent`](#sandboxevent)[] | Write/op events applied to this branch since fork, in order. These are replayed onto the target by [promote](#promote). |
236
171
  | <a id="rules"></a> `rules` | `readonly` | `string` | Rules the branch was forked with — carried so [promote](#promote) can re-seed a replay target identically. |
@@ -335,7 +270,7 @@ removeEventListener(type: "message", listener: (ev: {
335
270
 
336
271
  ***
337
272
 
338
- <a id="denialcontext-1"></a>
273
+ <a id="denialcontext"></a>
339
274
 
340
275
  ### DenialContext
341
276
 
@@ -358,9 +293,14 @@ reference traces.
358
293
  | `auth.token?` | `Record`\<`string`, `unknown`\> | - |
359
294
  | `auth.uid` | `string` | - |
360
295
  | <a id="failedfields"></a> `failedFields?` | `string`[] | Field paths in `request.resource.data` that the rule referenced and that failed. |
296
+ | <a id="query"></a> `query?` | \{ `limit?`: `number`; `offset?`: `number`; `orderBy?`: `string`; `where?`: \{ `field`: `string`; `op`: `string`; `value`: `string` \| `number` \| `boolean`; \}[]; \} | Machine-readable descriptor of the denied query's where/orderBy/limit shape (RULES-B11). Populated when a `list`/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. |
297
+ | `query.limit?` | `number` | - |
298
+ | `query.offset?` | `number` | - |
299
+ | `query.orderBy?` | `string` | - |
300
+ | `query.where?` | \{ `field`: `string`; `op`: `string`; `value`: `string` \| `number` \| `boolean`; \}[] | - |
361
301
  | <a id="reasons"></a> `reasons?` | `string`[] | Raw simulator reasoning lines (the underlying engine's `debugMessages`). Always present on `permission-denied`. Stable enough for log surfacing; not stable as machine-parseable data. |
362
- | <a id="request"></a> `request?` | \{ `method`: `"get"` \| `"list"` \| `"create"` \| `"update"` \| `"delete"`; `path`: `string`; `resourceData?`: `Record`\<`string`, `unknown`\>; \} | Eval-time request shape — what the rule saw on `request.*`. Lets callers render a "why did this deny" frame (auth, method, path, `request.resource.data` with sentinels resolved) without re-deriving any of it from out-of-band state. |
363
- | `request.method` | `"get"` \| `"list"` \| `"create"` \| `"update"` \| `"delete"` | - |
302
+ | <a id="request"></a> `request?` | \{ `method`: `"delete"` \| `"get"` \| `"list"` \| `"create"` \| `"update"`; `path`: `string`; `resourceData?`: `Record`\<`string`, `unknown`\>; \} | Eval-time request shape — what the rule saw on `request.*`. Lets callers render a "why did this deny" frame (auth, method, path, `request.resource.data` with sentinels resolved) without re-deriving any of it from out-of-band state. |
303
+ | `request.method` | `"delete"` \| `"get"` \| `"list"` \| `"create"` \| `"update"` | - |
364
304
  | `request.path` | `string` | - |
365
305
  | `request.resourceData?` | `Record`\<`string`, `unknown`\> | The user's proposed `request.resource.data` — pre-resolution. `FieldValue.*` sentinels are preserved as their marker shapes (`{ __type: 'serverTimestamp' }`, 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 `delete` (no payload). |
366
306
  | <a id="resource"></a> `resource?` | \{ `data`: `Record`\<`string`, `unknown`\>; `exists`: `boolean`; \} | Eval-time existing-document snapshot — what the rule saw on `resource.data`. `null` data with `exists: false` mirrors how the rule sees an absent doc. Absent for collection ops (`list`). |
@@ -378,7 +318,7 @@ reference traces.
378
318
 
379
319
  Eval-time payload emitted to Sandbox.onDenial subscribers.
380
320
 
381
- Mirrors the structured fields [DenialContext](#denialcontext-1) carries (`request`
321
+ Mirrors the structured fields [DenialContext](#denialcontext) carries (`request`
382
322
  + `resource` + `reasons` + `auth`) so a host environment that wants
383
323
  to surface denials independent of try/catch behavior gets the same
384
324
  frame either way.
@@ -390,11 +330,11 @@ frame either way.
390
330
  | <a id="auth-2"></a> `auth?` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
391
331
  | `auth.token?` | `Record`\<`string`, `unknown`\> |
392
332
  | `auth.uid` | `string` |
393
- | <a id="code-1"></a> `code` | `"permission-denied"` |
333
+ | <a id="code"></a> `code` | `"permission-denied"` |
394
334
  | <a id="message"></a> `message` | `string` |
395
335
  | <a id="reasons-1"></a> `reasons?` | `string`[] |
396
- | <a id="request-1"></a> `request?` | \{ `method`: `"get"` \| `"list"` \| `"create"` \| `"update"` \| `"delete"`; `path`: `string`; `resourceData?`: `Record`\<`string`, `unknown`\>; \} |
397
- | `request.method` | `"get"` \| `"list"` \| `"create"` \| `"update"` \| `"delete"` |
336
+ | <a id="request-1"></a> `request?` | \{ `method`: `"delete"` \| `"get"` \| `"list"` \| `"create"` \| `"update"`; `path`: `string`; `resourceData?`: `Record`\<`string`, `unknown`\>; \} |
337
+ | `request.method` | `"delete"` \| `"get"` \| `"list"` \| `"create"` \| `"update"` |
398
338
  | `request.path` | `string` |
399
339
  | `request.resourceData?` | `Record`\<`string`, `unknown`\> |
400
340
  | <a id="resource-1"></a> `resource?` | \{ `data`: `Record`\<`string`, `unknown`\>; `exists`: `boolean`; \} |
@@ -414,6 +354,7 @@ consumers migrate to the canonical `operationContext`.
414
354
 
415
355
  | Property | Type | Description |
416
356
  | :------ | :------ | :------ |
357
+ | <a id="activity"></a> `activity?` | [`ActivityEventProvenance`](#activityeventprovenance) | Firestore activity-diagnostics provenance; absent outside that flow. |
417
358
  | <a id="actor"></a> `actor?` | [`EventActor`](#eventactor) | - |
418
359
  | <a id="authlens"></a> `authLens?` | [`AuthLens`](#authlens-2) | - |
419
360
  | <a id="operationcontext-1"></a> `operationContext?` | [`OperationContext`](#operationcontext-2) | - |
@@ -444,8 +385,8 @@ on the errored phase only.
444
385
  | `error.reasons?` | `string`[] | - |
445
386
  | <a id="id"></a> `id` | `string` | - |
446
387
  | <a id="kind"></a> `kind` | `"listener_attach"` \| `"listener_detach"` \| `"listener_errored"` | - |
447
- | <a id="listenerid"></a> `listenerId` | `string` | - |
448
- | <a id="target"></a> `target` | \| \{ `kind`: `"doc"`; `path`: `string`; \} \| \{ `collection`: `string`; `kind`: `"query"`; \} | - |
388
+ | <a id="listenerid-1"></a> `listenerId` | `string` | - |
389
+ | <a id="target"></a> `target` | \| \{ `kind`: `"doc"`; `path`: `string`; \} \| \{ `collection`: `string`; `kind`: `"query"`; `query?`: `unknown`; \} | - |
449
390
 
450
391
  ***
451
392
 
@@ -456,12 +397,12 @@ on the errored phase only.
456
397
  An in-process sandbox created by [initializeSandbox](#initializesandbox).
457
398
 
458
399
  Service controls whose implementation requires synchronous access to local
459
- state accept this type. Remote worker handles remain [Sandbox](#sandbox-3)s, but
400
+ state accept this type. Remote worker handles remain [Sandbox](pyric-firestore-reference-api.md#sandbox)s, but
460
401
  are deliberately not assignable to this local-only interface.
461
402
 
462
403
  #### Extends
463
404
 
464
- - [`Sandbox`](#sandbox-3)
405
+ - [`Sandbox`](pyric-firestore-reference-api.md#sandbox)
465
406
 
466
407
  #### Properties
467
408
 
@@ -469,7 +410,7 @@ are deliberately not assignable to this local-only interface.
469
410
  | :------ | :------ | :------ | :------ |
470
411
  | <a id="local_sandbox"></a> `[LOCAL_SANDBOX]` | `readonly` | `true` | - |
471
412
  | <a id="admin"></a> `admin` | `readonly` | `SandboxAdmin` | 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. |
472
- | <a id="currentuser"></a> `currentUser` | `public` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | Current authenticated user across the sandbox. Mutated by `pyric/auth`'s `signInAnonymously` / `signInWithEmailAndPassword` / `signOut` / `sandbox.setUser`. Read per-call by service factories (e.g. a future `getFirestore(sandbox)` overload) so they see auth state changes without re-binding handles. Defaults to `null` (anonymous / signed out). **Independent of `withAuth({uid})`** — `withAuth` still produces a frozen [SandboxContext](#sandboxcontext) that carries its own identity for the runner's test code (the existing pattern: explicit identity per service call). `currentUser` exists for the `pyric/auth` mirror, where consumer app code drives identity through a stateful `Auth` handle rather than naming it per call. |
413
+ | <a id="currentuser"></a> `currentUser` | `public` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | Current authenticated user across the sandbox. Mutated by `pyric/auth`'s `signInAnonymously` / `signInWithEmailAndPassword` / `signOut` / `sandbox.setUser`. Read per-call by service factories (e.g. a future `getFirestore(sandbox)` overload) so they see auth state changes without re-binding handles. Defaults to `null` (anonymous / signed out). **Independent of `withAuth({uid})`** — `withAuth` still produces a frozen [SandboxContext](pyric-firestore-reference-api.md#sandboxcontext) that carries its own identity for the runner's test code (the existing pattern: explicit identity per service call). `currentUser` exists for the `pyric/auth` mirror, where consumer app code drives identity through a stateful `Auth` handle rather than naming it per call. |
473
414
  | `currentUser.token?` | `public` | `Record`\<`string`, `unknown`\> | - |
474
415
  | `currentUser.uid` | `public` | `string` | - |
475
416
 
@@ -495,7 +436,7 @@ No-op when persistence is not enabled.
495
436
 
496
437
  ###### Inherited from
497
438
 
498
- [`Sandbox`](#sandbox-3).[`clearPersistence`](#clearpersistence-4)
439
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`clearPersistence`](pyric-firestore-reference-api.md#clearpersistence)
499
440
 
500
441
  <a id="dispose"></a>
501
442
 
@@ -517,7 +458,7 @@ outgoing instance defensively. Idempotent. Does not touch data.
517
458
 
518
459
  ###### Inherited from
519
460
 
520
- [`Sandbox`](#sandbox-3).[`dispose`](#dispose-6)
461
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`dispose`](pyric-firestore-reference-api.md#dispose)
521
462
 
522
463
  <a id="enablepersistence"></a>
523
464
 
@@ -561,7 +502,7 @@ tuning.
561
502
 
562
503
  ###### Inherited from
563
504
 
564
- [`Sandbox`](#sandbox-3).[`enablePersistence`](#enablepersistence-4)
505
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`enablePersistence`](pyric-firestore-reference-api.md#enablepersistence)
565
506
 
566
507
  <a id="enabletabsync"></a>
567
508
 
@@ -621,7 +562,7 @@ disableSync();
621
562
 
622
563
  ###### Inherited from
623
564
 
624
- [`Sandbox`](#sandbox-3).[`enableTabSync`](#enabletabsync-4)
565
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`enableTabSync`](pyric-firestore-reference-api.md#enabletabsync)
625
566
 
626
567
  <a id="flush"></a>
627
568
 
@@ -644,7 +585,7 @@ Throws if persistence is not enabled.
644
585
 
645
586
  ###### Inherited from
646
587
 
647
- [`Sandbox`](#sandbox-3).[`flush`](#flush-6)
588
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`flush`](pyric-firestore-reference-api.md#flush)
648
589
 
649
590
  <a id="history"></a>
650
591
 
@@ -661,7 +602,7 @@ Use this for replay: hand the array to `replay(events, rules)`
661
602
  from `pyric/sandbox` and the engine re-issues every
662
603
  captured write against a fresh sandbox.
663
604
 
664
- Unlike [onEvent](#onevent-4) (live stream from the moment of subscribe),
605
+ Unlike [onEvent](pyric-firestore-reference-api.md#onevent) (live stream from the moment of subscribe),
665
606
  `history()` returns *every* event the sandbox has seen — useful
666
607
  for consumers that attach late (e.g., loading a saved session
667
608
  before subscribing) or that need a snapshot at a particular moment.
@@ -676,7 +617,7 @@ snapshot *before* reset retain the boundary in their copy.
676
617
 
677
618
  ###### Inherited from
678
619
 
679
- [`Sandbox`](#sandbox-3).[`history`](#history-4)
620
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`history`](pyric-firestore-reference-api.md#history)
680
621
 
681
622
  <a id="loadsnapshot"></a>
682
623
 
@@ -686,17 +627,17 @@ snapshot *before* reset retain the boundary in their copy.
686
627
  loadSnapshot(data: SandboxSnapshot): void;
687
628
  ```
688
629
 
689
- CLOBBER-restore the sandbox's entire state from a prior [snapshot](#snapshot-6):
630
+ CLOBBER-restore the sandbox's entire state from a prior [snapshot](pyric-firestore-reference-api.md#snapshot):
690
631
  `reset()` (clears firestore + the signed-in session), then rebuild firestore
691
632
  from `data` and restore each registered service. This is a TOTAL replace —
692
633
  documents absent from `data` do NOT survive — and is the counterpart to
693
- [snapshot](#snapshot-6). It is what makes "transfer (clobber) one instance's data
634
+ [snapshot](pyric-firestore-reference-api.md#snapshot). It is what makes "transfer (clobber) one instance's data
694
635
  into another" and named-branch switching possible.
695
636
 
696
637
  Fires a `session_boundary` (reset phase), re-evaluates live listeners against
697
638
  the loaded state, and the next persistence flush writes the loaded state.
698
639
  Services present in `data` but not currently registered are skipped (a
699
- snapshot taken via [snapshot](#snapshot-6) always includes every registered
640
+ snapshot taken via [snapshot](pyric-firestore-reference-api.md#snapshot) always includes every registered
700
641
  service, so this only affects cross-instance imports from a sandbox that had
701
642
  a service this one lacks).
702
643
 
@@ -704,7 +645,7 @@ a service this one lacks).
704
645
 
705
646
  | Parameter | Type |
706
647
  | :------ | :------ |
707
- | `data` | [`SandboxSnapshot`](#sandboxsnapshot-2) |
648
+ | `data` | [`SandboxSnapshot`](#sandboxsnapshot) |
708
649
 
709
650
  ###### Returns
710
651
 
@@ -712,7 +653,7 @@ a service this one lacks).
712
653
 
713
654
  ###### Inherited from
714
655
 
715
- [`Sandbox`](#sandbox-3).[`loadSnapshot`](#loadsnapshot-4)
656
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`loadSnapshot`](pyric-firestore-reference-api.md#loadsnapshot)
716
657
 
717
658
  <a id="oncurrentuserchanged"></a>
718
659
 
@@ -755,7 +696,7 @@ their errors.
755
696
 
756
697
  ###### Inherited from
757
698
 
758
- [`Sandbox`](#sandbox-3).[`onCurrentUserChanged`](#oncurrentuserchanged-4)
699
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`onCurrentUserChanged`](pyric-firestore-reference-api.md#oncurrentuserchanged)
759
700
 
760
701
  <a id="onevent"></a>
761
702
 
@@ -805,7 +746,7 @@ sandbox doesn't await them and doesn't propagate their errors.
805
746
 
806
747
  ###### Inherited from
807
748
 
808
- [`Sandbox`](#sandbox-3).[`onEvent`](#onevent-4)
749
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`onEvent`](pyric-firestore-reference-api.md#onevent)
809
750
 
810
751
  <a id="registerpersistableservice"></a>
811
752
 
@@ -854,7 +795,7 @@ not need to call this directly.
854
795
 
855
796
  ###### Inherited from
856
797
 
857
- [`Sandbox`](#sandbox-3).[`registerPersistableService`](#registerpersistableservice-4)
798
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`registerPersistableService`](pyric-firestore-reference-api.md#registerpersistableservice)
858
799
 
859
800
  <a id="reset"></a>
860
801
 
@@ -872,7 +813,7 @@ the swap — they can't survive because their target docs have been
872
813
  wiped. `onEvent` subscribers DO survive — the registry lives on
873
814
  the sandbox, and a `session_boundary` event with `phase: 'reset'`
874
815
  fires before the swap so subscribers know the rollover happened.
875
- Existing [SandboxContext](#sandboxcontext)s continue to work — their sandbox
816
+ Existing [SandboxContext](pyric-firestore-reference-api.md#sandboxcontext)s continue to work — their sandbox
876
817
  reference is stable; subsequent operations resolve to the new env.
877
818
 
878
819
  ###### Returns
@@ -881,7 +822,41 @@ reference is stable; subsequent operations resolve to the new env.
881
822
 
882
823
  ###### Inherited from
883
824
 
884
- [`Sandbox`](#sandbox-3).[`reset`](#reset-4)
825
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`reset`](pyric-firestore-reference-api.md#reset)
826
+
827
+ <a id="resetall"></a>
828
+
829
+ ##### resetAll()
830
+
831
+ ```ts
832
+ resetAll(): Promise<{
833
+ errors: string[];
834
+ }>;
835
+ ```
836
+
837
+ Reset the WHOLE sandbox: [reset](pyric-firestore-reference-api.md#reset) (Firestore env + signed-in
838
+ session), then clear every registered persistable service that
839
+ provides a [PersistableService.reset](#reset-2) hook — auth users, the
840
+ RTDB tree, storage objects. This is the one sandbox-owned "wipe
841
+ everything" path: because it iterates the service registry, a new
842
+ service that registers with a `reset` hook is cleared automatically,
843
+ and a consumer (Pyric Studio's reset) cannot forget one.
844
+
845
+ Service resets may be async (storage clears IndexedDB stores); the
846
+ returned promise resolves when every service has finished clearing.
847
+ A service whose `reset` throws is isolated (others still clear) and
848
+ REPORTED in the returned `errors` (as `name: message`) — a reset that
849
+ leaves data behind must never look successful to the caller.
850
+
851
+ ###### Returns
852
+
853
+ `Promise`\<\{
854
+ `errors`: `string`[];
855
+ \}\>
856
+
857
+ ###### Inherited from
858
+
859
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`resetAll`](pyric-firestore-reference-api.md#resetall)
885
860
 
886
861
  <a id="runwithprovenance"></a>
887
862
 
@@ -934,7 +909,7 @@ spell `sandbox.runWithProvenance?.(prov, fn) ?? fn()`.
934
909
 
935
910
  ###### Inherited from
936
911
 
937
- [`Sandbox`](#sandbox-3).[`runWithProvenance`](#runwithprovenance-4)
912
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`runWithProvenance`](pyric-firestore-reference-api.md#runwithprovenance)
938
913
 
939
914
  <a id="snapshot"></a>
940
915
 
@@ -950,11 +925,11 @@ paths to data. Future services will add their own keys.
950
925
 
951
926
  ###### Returns
952
927
 
953
- [`SandboxSnapshot`](#sandboxsnapshot-2)
928
+ [`SandboxSnapshot`](#sandboxsnapshot)
954
929
 
955
930
  ###### Inherited from
956
931
 
957
- [`Sandbox`](#sandbox-3).[`snapshot`](#snapshot-6)
932
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`snapshot`](pyric-firestore-reference-api.md#snapshot)
958
933
 
959
934
  <a id="withauth-2"></a>
960
935
 
@@ -987,7 +962,7 @@ unambiguous.
987
962
 
988
963
  ###### Returns
989
964
 
990
- [`SandboxContext`](#sandboxcontext)
965
+ [`SandboxContext`](pyric-firestore-reference-api.md#sandboxcontext)
991
966
 
992
967
  ###### Example
993
968
 
@@ -999,7 +974,7 @@ const dbAnon = getFirestore(sandbox.withAuth(null));
999
974
 
1000
975
  ###### Inherited from
1001
976
 
1002
- [`Sandbox`](#sandbox-3).[`withAuth`](#withauth-6)
977
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`withAuth`](pyric-firestore-reference-api.md#withauth)
1003
978
 
1004
979
  ***
1005
980
 
@@ -1038,7 +1013,7 @@ as a user, while an app or agent may use an admin lens.
1038
1013
  | <a id="id-1"></a> `id` | `readonly` | `string` |
1039
1014
  | <a id="method"></a> `method` | `readonly` | `string` |
1040
1015
  | <a id="path"></a> `path?` | `readonly` | `string` |
1041
- | <a id="result"></a> `result` | `readonly` | `"allow"` \| `"deny"` \| `"unsupported"` \| `"error"` \| `"not-applicable"` |
1016
+ | <a id="result"></a> `result` | `readonly` | `"unsupported"` \| `"error"` \| `"allow"` \| `"deny"` \| `"not-applicable"` |
1042
1017
  | <a id="rules-1"></a> `rules` | `readonly` | [`RulesDisposition`](#rulesdisposition-2) |
1043
1018
  | <a id="service-1"></a> `service` | `readonly` | [`EventService`](#eventservice) |
1044
1019
 
@@ -1050,7 +1025,7 @@ as a user, while an app or agent may use an admin lens.
1050
1025
 
1051
1026
  Contract for a service that can contribute its state to the sandbox
1052
1027
  persistence layer. Services (auth, storage, database) register
1053
- themselves via [Sandbox.registerPersistableService](#registerpersistableservice-4) so the
1028
+ themselves via [Sandbox.registerPersistableService](pyric-firestore-reference-api.md#registerpersistableservice) so the
1054
1029
  sandbox core stays service-agnostic — the sandbox doesn't know what
1055
1030
  auth or storage look like; it just calls `snapshot()` / `restore()`.
1056
1031
 
@@ -1064,15 +1039,37 @@ as Firestore writes.
1064
1039
 
1065
1040
  | Property | Type | Description |
1066
1041
  | :------ | :------ | :------ |
1067
- | <a id="session"></a> `session?` | \{ `currentUid`: `string`; `mode`: `"LOCAL"` \| `"SESSION"` \| `"NONE"`; `restore`: `void`; `subscribe`: () => `void`; \} | 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 `snapshot`/`restore`). The controller calls `session.subscribe` so it hears every sign-in / sign-out, then writes the uid to the appropriate web-storage slot (determined by `session.mode()`). On init, the controller reads the stored uid and its storage-derived mode, then calls `session.restore(uid, mode)` to re-establish both before firing `onAuthStateChanged` as if the user just signed in. Only active when `SandboxPersistenceOptions.sessionStorage` is provided; omitting `sessionStorage` 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. |
1042
+ | <a id="session"></a> `session?` | \{ `currentUid`: `string`; `mode`: `"NONE"` \| `"SESSION"` \| `"LOCAL"`; `restore`: `void`; `subscribe`: () => `void`; \} | 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 `snapshot`/`restore`). The controller calls `session.subscribe` so it hears every sign-in / sign-out, then writes the uid to the appropriate web-storage slot (determined by `session.mode()`). On init, the controller reads the stored uid and its storage-derived mode, then calls `session.restore(uid, mode)` to re-establish both before firing `onAuthStateChanged` as if the user just signed in. Only active when `SandboxPersistenceOptions.sessionStorage` is provided; omitting `sessionStorage` 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. |
1068
1043
  | `session.currentUid` | `string` | - |
1069
- | `session.mode` | `"LOCAL"` \| `"SESSION"` \| `"NONE"` | - |
1044
+ | `session.mode` | `"NONE"` \| `"SESSION"` \| `"LOCAL"` | - |
1070
1045
  | `session.restore` | `void` | - |
1071
1046
  | `session.subscribe` | () => `void` | - |
1072
1047
  | <a id="subscribe"></a> `subscribe?` | (`onChange`: () => `void`) => () => `void` | 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 `dispose()`. |
1073
1048
 
1074
1049
  #### Methods
1075
1050
 
1051
+ <a id="reset-2"></a>
1052
+
1053
+ ##### reset()?
1054
+
1055
+ ```ts
1056
+ optional reset(): void | Promise<void>;
1057
+ ```
1058
+
1059
+ Optional: clear this service's state to empty. Called by
1060
+ [Sandbox.resetAll](pyric-firestore-reference-api.md#resetall) after the Firestore environment swap, so a
1061
+ single sandbox-owned call wipes EVERY registered service (auth users,
1062
+ RTDB tree, storage objects) — a consumer (Pyric Studio's reset) can't
1063
+ forget a service it never knew about. May be async (storage clears an
1064
+ IndexedDB store); `resetAll` awaits it. A service that omits `reset`
1065
+ is skipped — its state deliberately survives `resetAll` (e.g. the
1066
+ per-app auth-session hooks, whose signed-in identity the sandbox core
1067
+ already clears in `reset()`).
1068
+
1069
+ ###### Returns
1070
+
1071
+ `void` \| `Promise`\<`void`\>
1072
+
1076
1073
  <a id="restore"></a>
1077
1074
 
1078
1075
  ##### restore()
@@ -1316,7 +1313,7 @@ Force a flush of the current sandbox state to the backend.
1316
1313
 
1317
1314
  ### RemoteSandbox
1318
1315
 
1319
- A branded remote sandbox handle. Structurally a [Sandbox](#sandbox-3) — it can
1316
+ A branded remote sandbox handle. Structurally a [Sandbox](pyric-firestore-reference-api.md#sandbox) — it can
1320
1317
  be passed anywhere a `Sandbox` is accepted (notably
1321
1318
  `pyric-admin/app`'s `initializeApp({ sandbox })`) — but sync-only members
1322
1319
  that cannot be mirrored over the wire (`admin`, `snapshot()`,
@@ -1325,7 +1322,11 @@ dispatch on [isRemoteSandbox](#isremotesandbox) and use [channel](#channel) inst
1325
1322
 
1326
1323
  #### Extends
1327
1324
 
1328
- - [`Sandbox`](#sandbox-3)
1325
+ - [`Sandbox`](pyric-firestore-reference-api.md#sandbox)
1326
+
1327
+ #### Extended by
1328
+
1329
+ - [`RemoteSandbox`](pyric-cli-remote-reference-api.md#remotesandbox)
1329
1330
 
1330
1331
  #### Properties
1331
1332
 
@@ -1334,7 +1335,7 @@ dispatch on [isRemoteSandbox](#isremotesandbox) and use [channel](#channel) inst
1334
1335
  | <a id="remote_sandbox"></a> `[REMOTE_SANDBOX]` | `readonly` | `true` | - |
1335
1336
  | <a id="admin-1"></a> `admin` | `readonly` | `SandboxAdmin` | 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. |
1336
1337
  | <a id="channel"></a> `channel` | `readonly` | [`RemoteSandboxChannel`](#remotesandboxchannel-1) | The raw worker op/sub relay channel. |
1337
- | <a id="currentuser-1"></a> `currentUser` | `public` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | Current authenticated user across the sandbox. Mutated by `pyric/auth`'s `signInAnonymously` / `signInWithEmailAndPassword` / `signOut` / `sandbox.setUser`. Read per-call by service factories (e.g. a future `getFirestore(sandbox)` overload) so they see auth state changes without re-binding handles. Defaults to `null` (anonymous / signed out). **Independent of `withAuth({uid})`** — `withAuth` still produces a frozen [SandboxContext](#sandboxcontext) that carries its own identity for the runner's test code (the existing pattern: explicit identity per service call). `currentUser` exists for the `pyric/auth` mirror, where consumer app code drives identity through a stateful `Auth` handle rather than naming it per call. |
1338
+ | <a id="currentuser-1"></a> `currentUser` | `public` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | Current authenticated user across the sandbox. Mutated by `pyric/auth`'s `signInAnonymously` / `signInWithEmailAndPassword` / `signOut` / `sandbox.setUser`. Read per-call by service factories (e.g. a future `getFirestore(sandbox)` overload) so they see auth state changes without re-binding handles. Defaults to `null` (anonymous / signed out). **Independent of `withAuth({uid})`** — `withAuth` still produces a frozen [SandboxContext](pyric-firestore-reference-api.md#sandboxcontext) that carries its own identity for the runner's test code (the existing pattern: explicit identity per service call). `currentUser` exists for the `pyric/auth` mirror, where consumer app code drives identity through a stateful `Auth` handle rather than naming it per call. |
1338
1339
  | `currentUser.token?` | `public` | `Record`\<`string`, `unknown`\> | - |
1339
1340
  | `currentUser.uid` | `public` | `string` | - |
1340
1341
  | <a id="serveurl"></a> `serveUrl` | `readonly` | `string` | Base URL of the `pyric dev` this handle is attached to (used in error guidance: "open <serveUrl> in a browser and retry"). |
@@ -1361,7 +1362,7 @@ No-op when persistence is not enabled.
1361
1362
 
1362
1363
  ###### Inherited from
1363
1364
 
1364
- [`Sandbox`](#sandbox-3).[`clearPersistence`](#clearpersistence-4)
1365
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`clearPersistence`](pyric-firestore-reference-api.md#clearpersistence)
1365
1366
 
1366
1367
  <a id="dispose-4"></a>
1367
1368
 
@@ -1383,7 +1384,7 @@ outgoing instance defensively. Idempotent. Does not touch data.
1383
1384
 
1384
1385
  ###### Inherited from
1385
1386
 
1386
- [`Sandbox`](#sandbox-3).[`dispose`](#dispose-6)
1387
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`dispose`](pyric-firestore-reference-api.md#dispose)
1387
1388
 
1388
1389
  <a id="enablepersistence-2"></a>
1389
1390
 
@@ -1427,7 +1428,7 @@ tuning.
1427
1428
 
1428
1429
  ###### Inherited from
1429
1430
 
1430
- [`Sandbox`](#sandbox-3).[`enablePersistence`](#enablepersistence-4)
1431
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`enablePersistence`](pyric-firestore-reference-api.md#enablepersistence)
1431
1432
 
1432
1433
  <a id="enabletabsync-2"></a>
1433
1434
 
@@ -1487,7 +1488,7 @@ disableSync();
1487
1488
 
1488
1489
  ###### Inherited from
1489
1490
 
1490
- [`Sandbox`](#sandbox-3).[`enableTabSync`](#enabletabsync-4)
1491
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`enableTabSync`](pyric-firestore-reference-api.md#enabletabsync)
1491
1492
 
1492
1493
  <a id="flush-4"></a>
1493
1494
 
@@ -1510,7 +1511,7 @@ Throws if persistence is not enabled.
1510
1511
 
1511
1512
  ###### Inherited from
1512
1513
 
1513
- [`Sandbox`](#sandbox-3).[`flush`](#flush-6)
1514
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`flush`](pyric-firestore-reference-api.md#flush)
1514
1515
 
1515
1516
  <a id="history-2"></a>
1516
1517
 
@@ -1527,7 +1528,7 @@ Use this for replay: hand the array to `replay(events, rules)`
1527
1528
  from `pyric/sandbox` and the engine re-issues every
1528
1529
  captured write against a fresh sandbox.
1529
1530
 
1530
- Unlike [onEvent](#onevent-4) (live stream from the moment of subscribe),
1531
+ Unlike [onEvent](pyric-firestore-reference-api.md#onevent) (live stream from the moment of subscribe),
1531
1532
  `history()` returns *every* event the sandbox has seen — useful
1532
1533
  for consumers that attach late (e.g., loading a saved session
1533
1534
  before subscribing) or that need a snapshot at a particular moment.
@@ -1542,7 +1543,7 @@ snapshot *before* reset retain the boundary in their copy.
1542
1543
 
1543
1544
  ###### Inherited from
1544
1545
 
1545
- [`Sandbox`](#sandbox-3).[`history`](#history-4)
1546
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`history`](pyric-firestore-reference-api.md#history)
1546
1547
 
1547
1548
  <a id="loadsnapshot-2"></a>
1548
1549
 
@@ -1552,17 +1553,17 @@ snapshot *before* reset retain the boundary in their copy.
1552
1553
  loadSnapshot(data: SandboxSnapshot): void;
1553
1554
  ```
1554
1555
 
1555
- CLOBBER-restore the sandbox's entire state from a prior [snapshot](#snapshot-6):
1556
+ CLOBBER-restore the sandbox's entire state from a prior [snapshot](pyric-firestore-reference-api.md#snapshot):
1556
1557
  `reset()` (clears firestore + the signed-in session), then rebuild firestore
1557
1558
  from `data` and restore each registered service. This is a TOTAL replace —
1558
1559
  documents absent from `data` do NOT survive — and is the counterpart to
1559
- [snapshot](#snapshot-6). It is what makes "transfer (clobber) one instance's data
1560
+ [snapshot](pyric-firestore-reference-api.md#snapshot). It is what makes "transfer (clobber) one instance's data
1560
1561
  into another" and named-branch switching possible.
1561
1562
 
1562
1563
  Fires a `session_boundary` (reset phase), re-evaluates live listeners against
1563
1564
  the loaded state, and the next persistence flush writes the loaded state.
1564
1565
  Services present in `data` but not currently registered are skipped (a
1565
- snapshot taken via [snapshot](#snapshot-6) always includes every registered
1566
+ snapshot taken via [snapshot](pyric-firestore-reference-api.md#snapshot) always includes every registered
1566
1567
  service, so this only affects cross-instance imports from a sandbox that had
1567
1568
  a service this one lacks).
1568
1569
 
@@ -1570,7 +1571,7 @@ a service this one lacks).
1570
1571
 
1571
1572
  | Parameter | Type |
1572
1573
  | :------ | :------ |
1573
- | `data` | [`SandboxSnapshot`](#sandboxsnapshot-2) |
1574
+ | `data` | [`SandboxSnapshot`](#sandboxsnapshot) |
1574
1575
 
1575
1576
  ###### Returns
1576
1577
 
@@ -1578,7 +1579,7 @@ a service this one lacks).
1578
1579
 
1579
1580
  ###### Inherited from
1580
1581
 
1581
- [`Sandbox`](#sandbox-3).[`loadSnapshot`](#loadsnapshot-4)
1582
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`loadSnapshot`](pyric-firestore-reference-api.md#loadsnapshot)
1582
1583
 
1583
1584
  <a id="oncurrentuserchanged-2"></a>
1584
1585
 
@@ -1621,7 +1622,7 @@ their errors.
1621
1622
 
1622
1623
  ###### Inherited from
1623
1624
 
1624
- [`Sandbox`](#sandbox-3).[`onCurrentUserChanged`](#oncurrentuserchanged-4)
1625
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`onCurrentUserChanged`](pyric-firestore-reference-api.md#oncurrentuserchanged)
1625
1626
 
1626
1627
  <a id="onevent-2"></a>
1627
1628
 
@@ -1671,7 +1672,7 @@ sandbox doesn't await them and doesn't propagate their errors.
1671
1672
 
1672
1673
  ###### Inherited from
1673
1674
 
1674
- [`Sandbox`](#sandbox-3).[`onEvent`](#onevent-4)
1675
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`onEvent`](pyric-firestore-reference-api.md#onevent)
1675
1676
 
1676
1677
  <a id="registerpersistableservice-2"></a>
1677
1678
 
@@ -1720,9 +1721,9 @@ not need to call this directly.
1720
1721
 
1721
1722
  ###### Inherited from
1722
1723
 
1723
- [`Sandbox`](#sandbox-3).[`registerPersistableService`](#registerpersistableservice-4)
1724
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`registerPersistableService`](pyric-firestore-reference-api.md#registerpersistableservice)
1724
1725
 
1725
- <a id="reset-2"></a>
1726
+ <a id="reset-4"></a>
1726
1727
 
1727
1728
  ##### reset()
1728
1729
 
@@ -1738,7 +1739,7 @@ the swap — they can't survive because their target docs have been
1738
1739
  wiped. `onEvent` subscribers DO survive — the registry lives on
1739
1740
  the sandbox, and a `session_boundary` event with `phase: 'reset'`
1740
1741
  fires before the swap so subscribers know the rollover happened.
1741
- Existing [SandboxContext](#sandboxcontext)s continue to work — their sandbox
1742
+ Existing [SandboxContext](pyric-firestore-reference-api.md#sandboxcontext)s continue to work — their sandbox
1742
1743
  reference is stable; subsequent operations resolve to the new env.
1743
1744
 
1744
1745
  ###### Returns
@@ -1747,7 +1748,41 @@ reference is stable; subsequent operations resolve to the new env.
1747
1748
 
1748
1749
  ###### Inherited from
1749
1750
 
1750
- [`Sandbox`](#sandbox-3).[`reset`](#reset-4)
1751
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`reset`](pyric-firestore-reference-api.md#reset)
1752
+
1753
+ <a id="resetall-2"></a>
1754
+
1755
+ ##### resetAll()
1756
+
1757
+ ```ts
1758
+ resetAll(): Promise<{
1759
+ errors: string[];
1760
+ }>;
1761
+ ```
1762
+
1763
+ Reset the WHOLE sandbox: [reset](pyric-firestore-reference-api.md#reset) (Firestore env + signed-in
1764
+ session), then clear every registered persistable service that
1765
+ provides a [PersistableService.reset](#reset-2) hook — auth users, the
1766
+ RTDB tree, storage objects. This is the one sandbox-owned "wipe
1767
+ everything" path: because it iterates the service registry, a new
1768
+ service that registers with a `reset` hook is cleared automatically,
1769
+ and a consumer (Pyric Studio's reset) cannot forget one.
1770
+
1771
+ Service resets may be async (storage clears IndexedDB stores); the
1772
+ returned promise resolves when every service has finished clearing.
1773
+ A service whose `reset` throws is isolated (others still clear) and
1774
+ REPORTED in the returned `errors` (as `name: message`) — a reset that
1775
+ leaves data behind must never look successful to the caller.
1776
+
1777
+ ###### Returns
1778
+
1779
+ `Promise`\<\{
1780
+ `errors`: `string`[];
1781
+ \}\>
1782
+
1783
+ ###### Inherited from
1784
+
1785
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`resetAll`](pyric-firestore-reference-api.md#resetall)
1751
1786
 
1752
1787
  <a id="runwithprovenance-2"></a>
1753
1788
 
@@ -1800,7 +1835,7 @@ spell `sandbox.runWithProvenance?.(prov, fn) ?? fn()`.
1800
1835
 
1801
1836
  ###### Inherited from
1802
1837
 
1803
- [`Sandbox`](#sandbox-3).[`runWithProvenance`](#runwithprovenance-4)
1838
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`runWithProvenance`](pyric-firestore-reference-api.md#runwithprovenance)
1804
1839
 
1805
1840
  <a id="snapshot-4"></a>
1806
1841
 
@@ -1816,11 +1851,11 @@ paths to data. Future services will add their own keys.
1816
1851
 
1817
1852
  ###### Returns
1818
1853
 
1819
- [`SandboxSnapshot`](#sandboxsnapshot-2)
1854
+ [`SandboxSnapshot`](#sandboxsnapshot)
1820
1855
 
1821
1856
  ###### Inherited from
1822
1857
 
1823
- [`Sandbox`](#sandbox-3).[`snapshot`](#snapshot-6)
1858
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`snapshot`](pyric-firestore-reference-api.md#snapshot)
1824
1859
 
1825
1860
  <a id="withauth-4"></a>
1826
1861
 
@@ -1853,7 +1888,7 @@ unambiguous.
1853
1888
 
1854
1889
  ###### Returns
1855
1890
 
1856
- [`SandboxContext`](#sandboxcontext)
1891
+ [`SandboxContext`](pyric-firestore-reference-api.md#sandboxcontext)
1857
1892
 
1858
1893
  ###### Example
1859
1894
 
@@ -1865,7 +1900,7 @@ const dbAnon = getFirestore(sandbox.withAuth(null));
1865
1900
 
1866
1901
  ###### Inherited from
1867
1902
 
1868
- [`Sandbox`](#sandbox-3).[`withAuth`](#withauth-6)
1903
+ [`Sandbox`](pyric-firestore-reference-api.md#sandbox).[`withAuth`](pyric-firestore-reference-api.md#withauth)
1869
1904
 
1870
1905
  ***
1871
1906
 
@@ -2033,16 +2068,16 @@ traffic-monitor-decision.md for the field-by-field rationale.
2033
2068
  | `auth.uid` | `string` | - |
2034
2069
  | <a id="detail"></a> `detail?` | \{ `admin?`: `boolean`; \} & `Record`\<`string`, `unknown`\> | Free-form operation metadata. `admin: true` marks a rules-bypassing setup/admin operation so fixture tooling can exclude it from protected behavior while still preserving it as replay context. |
2035
2070
  | <a id="evalms"></a> `evalMs` | `number` | Wall-clock duration of the simulator.simulate(...) call, in ms. |
2036
- | <a id="evaluatedrule"></a> `evaluatedRule?` | `EvaluatedRuleInfo` | The DECIDING rule's verdict + 1-indexed source line + full sub-expression trace, projected from the simulator's structured `RuleEvaluation` (additive: present on `result: 'allow' | 'deny'` Firestore events when the simulator produced a per-rule trace — the allowing rule on an allow, the denying rule on a deny). Studio's rules inspector reads this to mark the deciding line and render the evaluation step-through ("show the work"). Absent on an implicit deny (no rule evaluated), a simulator-error deny, and unsupported results. |
2071
+ | <a id="evaluatedrule"></a> `evaluatedRule?` | [`EvaluatedRuleInfo`](pyric-rules-reference-api.md#evaluatedruleinfo) | The DECIDING rule's verdict + 1-indexed source line + full sub-expression trace, projected from the simulator's structured `RuleEvaluation` (additive: present on `result: 'allow' | 'deny'` Firestore events when the simulator produced a per-rule trace — the allowing rule on an allow, the denying rule on a deny). Studio's rules inspector reads this to mark the deciding line and render the evaluation step-through ("show the work"). Absent on an implicit deny (no rule evaluated), a simulator-error deny, and unsupported results. |
2037
2072
  | <a id="groupid"></a> `groupId?` | `string` | Shared across ops in one batch or transaction. Opaque to consumers. |
2038
- | <a id="groupkind"></a> `groupKind?` | `"transaction"` \| `"batch"` | Disambiguates `origin: 'transaction' | 'batch'` cases when consumers need to tell them apart without inspecting `origin` directly. |
2073
+ | <a id="groupkind-1"></a> `groupKind?` | `"transaction"` \| `"batch"` | Disambiguates `origin: 'transaction' | 'batch'` cases when consumers need to tell them apart without inspecting `origin` directly. |
2039
2074
  | <a id="id-2"></a> `id` | `string` | Unique within a sandbox process. Useful for React list keys. |
2040
2075
  | <a id="kind-1"></a> `kind` | `"request"` | Discriminator. |
2041
2076
  | <a id="matchedrule"></a> `matchedRule?` | \{ `operations`: `string`[]; `ruleIndex`: `number`; \} | Parsed from the simulator's "Rule #N → …" debug line. Absent when no rule matched (e.g. no allow rules at the path — implicit deny). |
2042
2077
  | `matchedRule.operations` | `string`[] | - |
2043
2078
  | `matchedRule.ruleIndex` | `number` | - |
2044
- | <a id="method-1"></a> `method` | `"get"` \| `"list"` \| `"create"` \| `"update"` \| `"delete"` \| `"set"` | - |
2045
- | <a id="origin"></a> `origin` | `"user"` \| `"listener"` \| `"transaction"` \| `"batch"` | - |
2079
+ | <a id="method-1"></a> `method` | `"delete"` \| `"get"` \| `"list"` \| `"create"` \| `"update"` \| `"set"` | - |
2080
+ | <a id="origin"></a> `origin` | `"user"` \| `"transaction"` \| `"listener"` \| `"batch"` | - |
2046
2081
  | <a id="path-1"></a> `path` | `string` | - |
2047
2082
  | <a id="reasons-2"></a> `reasons` | `string`[] | Simulator debug messages — the per-rule trace (`Rule #0 (read) → ALLOW`). Same shape as `DenialEvent.reasons` so consumer code can share rendering. |
2048
2083
  | <a id="request-2"></a> `request?` | \{ `resourceData?`: `Record`\<`string`, `unknown`\>; \} | Proposed write payload, for create/update/set. Absent on reads + delete. Pre-resolution: `FieldValue.*` sentinels are preserved as their marker shapes (`{ __type: 'serverTimestamp' }`, etc.) so the replay engine can re-resolve them. The rule engine evaluated against the resolved form internally; that resolved form lives on [WriteSandboxEvent.nextState](#nextstate-1), not here. |
@@ -2053,7 +2088,7 @@ traffic-monitor-decision.md for the field-by-field rationale.
2053
2088
  | <a id="resourcebefore"></a> `resourceBefore?` | \{ `data`: `Record`\<`string`, `unknown`\>; `exists`: `boolean`; \} | Existing document state before the write (or read target for get). |
2054
2089
  | `resourceBefore.data` | `Record`\<`string`, `unknown`\> | - |
2055
2090
  | `resourceBefore.exists` | `boolean` | - |
2056
- | <a id="result-1"></a> `result` | `"allow"` \| `"deny"` \| `"unsupported"` | `'unsupported'` 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). |
2091
+ | <a id="result-1"></a> `result` | `"unsupported"` \| `"allow"` \| `"deny"` | `'unsupported'` 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). |
2057
2092
  | <a id="rulesdisposition"></a> `rulesDisposition?` | [`RulesDisposition`](#rulesdisposition-2) | Canonical statement of whether Security Rules evaluated this request. Added by the sandbox event recorder when an older emitter omits it. |
2058
2093
  | <a id="triggeredby"></a> `triggeredBy?` | \{ `method`: `string`; `path`: `string`; \} | For listener re-evals: the originating user op that triggered this re-evaluation. Absent on the initial-snapshot fire. |
2059
2094
  | `triggeredBy.method` | `string` | - |
@@ -2061,719 +2096,163 @@ traffic-monitor-decision.md for the field-by-field rationale.
2061
2096
 
2062
2097
  ***
2063
2098
 
2064
- <a id="sandbox-3"></a>
2099
+ <a id="sandboxcommitevent"></a>
2065
2100
 
2066
- ### Sandbox
2101
+ ### SandboxCommitEvent
2067
2102
 
2068
- A Firebase sandbox an isolated environment with one auth identity.
2103
+ Canonical committed mutation event. Unlike `operation`, this fires only when
2104
+ state actually changed. Replay and branch tooling should eventually consume
2105
+ these service adapters instead of filtering Firestore-only `write` events.
2069
2106
 
2070
- Created via `initializeSandbox(config)`. Use `fork({ auth })` to
2071
- derive a new sandbox with a different identity that shares the
2072
- underlying environment (rules, data, state). Fork is the only
2073
- identity-switching mechanism — there are no per-op auth overrides
2074
- and no in-place mutation.
2107
+ #### Properties
2075
2108
 
2076
- #### Extended by
2109
+ | Property | Type |
2110
+ | :------ | :------ |
2111
+ | <a id="at-3"></a> `at` | `number` |
2112
+ | <a id="auth-6"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2113
+ | `auth.token?` | `Record`\<`string`, `unknown`\> |
2114
+ | `auth.uid` | `string` |
2115
+ | <a id="data"></a> `data?` | `unknown` |
2116
+ | <a id="detail-1"></a> `detail?` | `Record`\<`string`, `unknown`\> |
2117
+ | <a id="groupid-1"></a> `groupId?` | `string` |
2118
+ | <a id="groupkind-2"></a> `groupKind?` | `"transaction"` \| `"batch"` |
2119
+ | <a id="id-3"></a> `id` | `string` |
2120
+ | <a id="kind-2"></a> `kind` | `"commit"` |
2121
+ | <a id="method-2"></a> `method` | `string` |
2122
+ | <a id="nextstate"></a> `nextState?` | `unknown` |
2123
+ | <a id="path-2"></a> `path?` | `string` |
2124
+ | <a id="priorstate"></a> `priorState?` | `unknown` |
2125
+ | <a id="replay"></a> `replay?` | \{ `autoId?`: `string`; `requestTime?`: `number`; `sentinels?`: \{ `field`: `string`; `kind`: `string`; \}[]; \} |
2126
+ | `replay.autoId?` | `string` |
2127
+ | `replay.requestTime?` | `number` |
2128
+ | `replay.sentinels?` | \{ `field`: `string`; `kind`: `string`; \}[] |
2129
+ | <a id="service-2"></a> `service` | [`EventService`](#eventservice) |
2077
2130
 
2078
- - [`LocalSandbox`](#localsandbox)
2079
- - [`RemoteSandbox`](#remotesandbox)
2131
+ ***
2080
2132
 
2081
- #### Properties
2133
+ <a id="sandboxconfig"></a>
2082
2134
 
2083
- | Property | Modifier | Type | Description |
2084
- | :------ | :------ | :------ | :------ |
2085
- | <a id="admin-2"></a> `admin` | `readonly` | `SandboxAdmin` | 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. |
2086
- | <a id="currentuser-2"></a> `currentUser` | `public` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | Current authenticated user across the sandbox. Mutated by `pyric/auth`'s `signInAnonymously` / `signInWithEmailAndPassword` / `signOut` / `sandbox.setUser`. Read per-call by service factories (e.g. a future `getFirestore(sandbox)` overload) so they see auth state changes without re-binding handles. Defaults to `null` (anonymous / signed out). **Independent of `withAuth({uid})`** — `withAuth` still produces a frozen [SandboxContext](#sandboxcontext) that carries its own identity for the runner's test code (the existing pattern: explicit identity per service call). `currentUser` exists for the `pyric/auth` mirror, where consumer app code drives identity through a stateful `Auth` handle rather than naming it per call. |
2087
- | `currentUser.token?` | `public` | `Record`\<`string`, `unknown`\> | - |
2088
- | `currentUser.uid` | `public` | `string` | - |
2135
+ ### SandboxConfig
2089
2136
 
2090
- #### Methods
2137
+ Initialization config for a sandbox. All fields are optional; an
2138
+ empty config produces a sandbox with no rules and no seeded data.
2091
2139
 
2092
- <a id="clearpersistence-4"></a>
2140
+ **No `auth` field.** Identity belongs to [SandboxContext](pyric-firestore-reference-api.md#sandboxcontext), not
2141
+ the sandbox. Service handles always require an explicit context.
2093
2142
 
2094
- ##### clearPersistence()
2143
+ ***
2095
2144
 
2096
- ```ts
2097
- clearPersistence(): Promise<void>;
2098
- ```
2145
+ <a id="sandboxlistenerevent"></a>
2099
2146
 
2100
- Wipe the persisted blob for this sandbox's `key`. In-memory state
2101
- is left intact — call `reset()` if you want both. Useful for
2102
- "sign out and forget" flows.
2147
+ ### SandboxListenerEvent
2103
2148
 
2104
- No-op when persistence is not enabled.
2149
+ Canonical listener lifecycle/delivery event. Firestore's existing snapshot
2150
+ delivery/lifecycle variants are preserved; this shape gives RTDB and future
2151
+ service listeners the same debuggable surface.
2105
2152
 
2106
- ###### Returns
2153
+ #### Properties
2107
2154
 
2108
- `Promise`\<`void`\>
2155
+ | Property | Type |
2156
+ | :------ | :------ |
2157
+ | <a id="at-4"></a> `at` | `number` |
2158
+ | <a id="auth-7"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2159
+ | `auth.token?` | `Record`\<`string`, `unknown`\> |
2160
+ | `auth.uid` | `string` |
2161
+ | <a id="detail-2"></a> `detail?` | `Record`\<`string`, `unknown`\> |
2162
+ | <a id="error-1"></a> `error?` | \{ `code?`: `string`; `message`: `string`; `reasons?`: `string`[]; \} |
2163
+ | `error.code?` | `string` |
2164
+ | `error.message` | `string` |
2165
+ | `error.reasons?` | `string`[] |
2166
+ | <a id="id-4"></a> `id` | `string` |
2167
+ | <a id="kind-3"></a> `kind` | `"listener"` |
2168
+ | <a id="listenerid-2"></a> `listenerId` | `string` |
2169
+ | <a id="phase"></a> `phase` | `"attach"` \| `"detach"` \| `"delivery"` \| `"suppressed"` \| `"errored"` |
2170
+ | <a id="reason"></a> `reason?` | `string` |
2171
+ | <a id="result-2"></a> `result?` | `"unsupported"` \| `"error"` \| `"allow"` \| `"deny"` |
2172
+ | <a id="sample"></a> `sample?` | `unknown` |
2173
+ | <a id="service-3"></a> `service` | [`EventService`](#eventservice) |
2174
+ | <a id="size"></a> `size?` | `number` |
2175
+ | <a id="target-1"></a> `target` | \{ `kind`: `string`; `path?`: `string`; `query?`: `unknown`; \} |
2176
+ | `target.kind` | `string` |
2177
+ | `target.path?` | `string` |
2178
+ | `target.query?` | `unknown` |
2179
+ | <a id="triggeredby-1"></a> `triggeredBy?` | \{ `method`: `string`; `path?`: `string`; \} |
2180
+ | `triggeredBy.method` | `string` |
2181
+ | `triggeredBy.path?` | `string` |
2109
2182
 
2110
- <a id="dispose-6"></a>
2183
+ ***
2111
2184
 
2112
- ##### dispose()
2185
+ <a id="sandboxoperationevent"></a>
2113
2186
 
2114
- ```ts
2115
- dispose(): void;
2116
- ```
2187
+ ### SandboxOperationEvent
2117
2188
 
2118
- Tear down listener registries on this sandbox's environment without
2119
- replacing it. Use this when you're about to discard the sandbox
2120
- itself (e.g. `runner.reseed()` builds a fresh sandbox rather than
2121
- calling `reset()`) and want to drop callback references on the
2122
- outgoing instance defensively. Idempotent. Does not touch data.
2189
+ Canonical service operation event. This is the service-neutral successor to
2190
+ Firestore's `request` traffic shape: every user-visible operation can be
2191
+ represented here, whether it is backed by security rules (Firestore/RTDB/
2192
+ Storage) or by a service control plane (Auth).
2123
2193
 
2124
- ###### Returns
2194
+ Existing Firestore `request` events remain for compatibility. New cross-
2195
+ service consumers should prefer `operation` because it carries an explicit
2196
+ `service` discriminator and does not require RTDB/Storage/Auth to pretend
2197
+ their state is a Firestore document.
2125
2198
 
2126
- `void`
2199
+ #### Properties
2127
2200
 
2128
- <a id="enablepersistence-4"></a>
2201
+ | Property | Type | Description |
2202
+ | :------ | :------ | :------ |
2203
+ | <a id="at-5"></a> `at` | `number` | - |
2204
+ | <a id="auth-8"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | - |
2205
+ | `auth.token?` | `Record`\<`string`, `unknown`\> | - |
2206
+ | `auth.uid` | `string` | - |
2207
+ | <a id="detail-3"></a> `detail?` | `Record`\<`string`, `unknown`\> | - |
2208
+ | <a id="durationms"></a> `durationMs?` | `number` | - |
2209
+ | <a id="groupid-2"></a> `groupId?` | `string` | - |
2210
+ | <a id="groupkind-3"></a> `groupKind?` | `"transaction"` \| `"batch"` | - |
2211
+ | <a id="id-5"></a> `id` | `string` | - |
2212
+ | <a id="kind-4"></a> `kind` | `"operation"` | - |
2213
+ | <a id="method-3"></a> `method` | `string` | - |
2214
+ | <a id="origin-1"></a> `origin` | `"admin"` \| `"user"` \| `"system"` \| `"transaction"` \| `"listener"` \| `"batch"` | - |
2215
+ | <a id="path-3"></a> `path?` | `string` | - |
2216
+ | <a id="reasons-3"></a> `reasons?` | `string`[] | - |
2217
+ | <a id="request-3"></a> `request?` | \{ `data?`: `unknown`; `query?`: `unknown`; `resourceData?`: `unknown`; \} | - |
2218
+ | `request.data?` | `unknown` | - |
2219
+ | `request.query?` | `unknown` | - |
2220
+ | `request.resourceData?` | `unknown` | - |
2221
+ | <a id="resourceafter-1"></a> `resourceAfter?` | \{ `data`: `unknown`; `exists`: `boolean`; \} | - |
2222
+ | `resourceAfter.data` | `unknown` | - |
2223
+ | `resourceAfter.exists` | `boolean` | - |
2224
+ | <a id="resourcebefore-1"></a> `resourceBefore?` | \{ `data`: `unknown`; `exists`: `boolean`; \} | - |
2225
+ | `resourceBefore.data` | `unknown` | - |
2226
+ | `resourceBefore.exists` | `boolean` | - |
2227
+ | <a id="result-3"></a> `result` | `"unsupported"` \| `"error"` \| `"allow"` \| `"deny"` \| `"not-applicable"` | - |
2228
+ | <a id="rules-2"></a> `rules?` | \{ `engine`: `"firestore"` \| `"rtdb"` \| `"storage"`; `errorCode?`: `string`; `matchedPath?`: `string`; `matchedRule?`: `string`; `operations?`: `string`[]; `pathVariableBindings?`: `Record`\<`string`, `string`\>; `reason?`: `string`; `ruleIndex?`: `number`; \} | - |
2229
+ | `rules.engine` | `"firestore"` \| `"rtdb"` \| `"storage"` | - |
2230
+ | `rules.errorCode?` | `string` | - |
2231
+ | `rules.matchedPath?` | `string` | - |
2232
+ | `rules.matchedRule?` | `string` | - |
2233
+ | `rules.operations?` | `string`[] | - |
2234
+ | `rules.pathVariableBindings?` | `Record`\<`string`, `string`\> | - |
2235
+ | `rules.reason?` | `string` | - |
2236
+ | `rules.ruleIndex?` | `number` | - |
2237
+ | <a id="rulesdisposition-1"></a> `rulesDisposition?` | [`RulesDisposition`](#rulesdisposition-2) | 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. |
2238
+ | <a id="service-4"></a> `service` | [`EventService`](#eventservice) | - |
2239
+ | <a id="triggeredby-2"></a> `triggeredBy?` | \{ `method`: `string`; `path?`: `string`; \} | - |
2240
+ | `triggeredBy.method` | `string` | - |
2241
+ | `triggeredBy.path?` | `string` | - |
2129
2242
 
2130
- ##### enablePersistence()
2243
+ ***
2131
2244
 
2132
- ```ts
2133
- enablePersistence(options: SandboxPersistenceOptions): Promise<void>;
2134
- ```
2135
-
2136
- Persist the sandbox's data to a backend and restore it on next
2137
- `enablePersistence` call. The default `'indexedDB'` backend turns
2138
- the sandbox into the host page's local Firestore — writes flush
2139
- automatically and a fresh `initializeSandbox()` rehydrates from
2140
- the prior session.
2141
-
2142
- Restoration happens before the promise resolves; awaiting this
2143
- call is sufficient to guarantee in-memory state matches the
2144
- persisted blob.
2145
-
2146
- Idempotent across the same `key` — calling twice in one process
2147
- is a no-op on the second call. Different keys are rejected as an
2148
- error (a sandbox can persist to at most one backend at a time).
2149
-
2150
- Listener semantics: every write event the sandbox emits triggers
2151
- a debounced flush (default 250ms). Browser hosts additionally
2152
- flush on `beforeunload` so a page navigation doesn't lose the
2153
- tail of the debounce window.
2154
-
2155
- See [SandboxPersistenceOptions](#sandboxpersistenceoptions) for backend selection and
2156
- tuning.
2157
-
2158
- ###### Parameters
2159
-
2160
- | Parameter | Type |
2161
- | :------ | :------ |
2162
- | `options` | [`SandboxPersistenceOptions`](#sandboxpersistenceoptions) |
2163
-
2164
- ###### Returns
2165
-
2166
- `Promise`\<`void`\>
2167
-
2168
- <a id="enabletabsync-4"></a>
2169
-
2170
- ##### enableTabSync()
2171
-
2172
- ```ts
2173
- enableTabSync(options?: TabSyncOptions): () => void;
2174
- ```
2175
-
2176
- Enable cross-tab realtime sync via `BroadcastChannel`. A write in
2177
- this tab will propagate to every OTHER tab of the same origin that
2178
- also called `enableTabSync`, causing their `onSnapshot` listeners to
2179
- re-evaluate — restoring production's cross-client realtime behavior.
2180
-
2181
- **Opt-in, OFF by default.** Firestore only (RTDB is a follow-on).
2182
-
2183
- Returns a disable function. Calling it removes the `onEvent`
2184
- subscription, the channel message listener, and closes the channel
2185
- (when it was created internally). After disable, no further propagation
2186
- occurs in either direction.
2187
-
2188
- **Multi-writer note:** concurrent writes from two tabs to the same doc
2189
- produce last-write-wins divergence — there is no conflict resolution.
2190
- The intended model is one active writer (one user, one tab) with
2191
- observers in other tabs; this covers the overwhelming majority of
2192
- local development scenarios.
2193
-
2194
- ###### Parameters
2195
-
2196
- | Parameter | Type |
2197
- | :------ | :------ |
2198
- | `options?` | [`TabSyncOptions`](#tabsyncoptions) |
2199
-
2200
- ###### Returns
2201
-
2202
- ```ts
2203
- (): void;
2204
- ```
2205
-
2206
- ###### Returns
2207
-
2208
- `void`
2209
-
2210
- ###### See
2211
-
2212
- [TabSyncOptions](#tabsyncoptions) for channel injection (tests) and originId.
2213
-
2214
- ###### Example
2215
-
2216
- ```ts
2217
- // In every tab that should participate in realtime:
2218
- const sandbox = initializeSandbox();
2219
- const disableSync = sandbox.enableTabSync();
2220
- // Later, to stop syncing:
2221
- disableSync();
2222
- ```
2223
-
2224
- <a id="flush-6"></a>
2225
-
2226
- ##### flush()
2227
-
2228
- ```ts
2229
- flush(): Promise<void>;
2230
- ```
2231
-
2232
- Force a snapshot to the configured persistence backend right now.
2233
- Useful before a manual navigation, or in tests that need
2234
- deterministic ordering against the debounce window. Resolves once
2235
- the write hits the backend.
2236
-
2237
- Throws if persistence is not enabled.
2238
-
2239
- ###### Returns
2240
-
2241
- `Promise`\<`void`\>
2242
-
2243
- <a id="history-4"></a>
2244
-
2245
- ##### history()
2246
-
2247
- ```ts
2248
- history(): SandboxEvent[];
2249
- ```
2250
-
2251
- Every [SandboxEvent](#sandboxevent) this sandbox has emitted since init or
2252
- the last `reset()`. Returns a defensive copy.
2253
-
2254
- Use this for replay: hand the array to `replay(events, rules)`
2255
- from `pyric/sandbox` and the engine re-issues every
2256
- captured write against a fresh sandbox.
2257
-
2258
- Unlike [onEvent](#onevent-4) (live stream from the moment of subscribe),
2259
- `history()` returns *every* event the sandbox has seen — useful
2260
- for consumers that attach late (e.g., loading a saved session
2261
- before subscribing) or that need a snapshot at a particular moment.
2262
-
2263
- `reset()` and `dispose()` each append a closing `session_boundary`
2264
- event; `reset()` then clears the history. Consumers that took a
2265
- snapshot *before* reset retain the boundary in their copy.
2266
-
2267
- ###### Returns
2268
-
2269
- [`SandboxEvent`](#sandboxevent)[]
2270
-
2271
- <a id="loadsnapshot-4"></a>
2272
-
2273
- ##### loadSnapshot()
2274
-
2275
- ```ts
2276
- loadSnapshot(data: SandboxSnapshot): void;
2277
- ```
2278
-
2279
- CLOBBER-restore the sandbox's entire state from a prior [snapshot](#snapshot-6):
2280
- `reset()` (clears firestore + the signed-in session), then rebuild firestore
2281
- from `data` and restore each registered service. This is a TOTAL replace —
2282
- documents absent from `data` do NOT survive — and is the counterpart to
2283
- [snapshot](#snapshot-6). It is what makes "transfer (clobber) one instance's data
2284
- into another" and named-branch switching possible.
2285
-
2286
- Fires a `session_boundary` (reset phase), re-evaluates live listeners against
2287
- the loaded state, and the next persistence flush writes the loaded state.
2288
- Services present in `data` but not currently registered are skipped (a
2289
- snapshot taken via [snapshot](#snapshot-6) always includes every registered
2290
- service, so this only affects cross-instance imports from a sandbox that had
2291
- a service this one lacks).
2292
-
2293
- ###### Parameters
2294
-
2295
- | Parameter | Type |
2296
- | :------ | :------ |
2297
- | `data` | [`SandboxSnapshot`](#sandboxsnapshot-2) |
2298
-
2299
- ###### Returns
2300
-
2301
- `void`
2302
-
2303
- <a id="oncurrentuserchanged-4"></a>
2304
-
2305
- ##### onCurrentUserChanged()
2306
-
2307
- ```ts
2308
- onCurrentUserChanged(cb: (user: {
2309
- token?: Record<string, unknown>;
2310
- uid: string;
2311
- }) => void): () => void;
2312
- ```
2313
-
2314
- Subscribe to `currentUser` changes. Fires on every mutation —
2315
- sign-in, sign-out, user swap. Does NOT fire on subscribe.
2316
-
2317
- Survives `reset()` and `dispose()` only as a no-op: a disposed
2318
- sandbox emits nothing further; a reset sandbox clears
2319
- `currentUser` to `null` (and fires the change) before swapping
2320
- the env.
2321
-
2322
- Returns an unsubscribe function. Listener errors are swallowed —
2323
- subscribers are observational, the sandbox does not propagate
2324
- their errors.
2325
-
2326
- ###### Parameters
2327
-
2328
- | Parameter | Type |
2329
- | :------ | :------ |
2330
- | `cb` | (`user`: \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \}) => `void` |
2331
-
2332
- ###### Returns
2333
-
2334
- ```ts
2335
- (): void;
2336
- ```
2337
-
2338
- ###### Returns
2339
-
2340
- `void`
2341
-
2342
- <a id="onevent-4"></a>
2343
-
2344
- ##### onEvent()
2345
-
2346
- ```ts
2347
- onEvent(cb: (event: SandboxEvent) => void): () => void;
2348
- ```
2349
-
2350
- Subscribe to every event the sandbox emits — see [SandboxEvent](#sandboxevent)
2351
- for the discriminated-union shape. One subscription covers
2352
- request/denial/snapshot-error/listener-lifecycle/session-boundary;
2353
- filter on `event.kind` to recover individual streams.
2354
-
2355
- Replaces the prior three-channel surface (`onRequest` / `onDenial`
2356
- / `onSnapshotError`) — see issue #307. Filter cookbook:
2357
- - All denials: `event.kind === 'request' && event.result === 'deny'`
2358
- - Stream errors: `event.kind === 'listener_errored'`
2359
- - Per-op traffic: `event.kind === 'request'`
2360
-
2361
- Survives `sandbox.reset()` — the subscription is held on the
2362
- sandbox, not on the underlying environment. A `session_boundary`
2363
- event with `phase: 'reset'` fires before the env swap so consumers
2364
- can segment their stream.
2365
-
2366
- Returns an unsubscribe function. Listener errors are swallowed so a
2367
- faulty subscriber can't change rule semantics or hide other events.
2368
- Both synchronous throws and rejected Promises from async callbacks
2369
- are silently discarded — subscribers are **observational**, the
2370
- sandbox doesn't await them and doesn't propagate their errors.
2371
-
2372
- ###### Parameters
2373
-
2374
- | Parameter | Type |
2375
- | :------ | :------ |
2376
- | `cb` | (`event`: [`SandboxEvent`](#sandboxevent)) => `void` |
2377
-
2378
- ###### Returns
2379
-
2380
- ```ts
2381
- (): void;
2382
- ```
2383
-
2384
- ###### Returns
2385
-
2386
- `void`
2387
-
2388
- <a id="registerpersistableservice-4"></a>
2389
-
2390
- ##### registerPersistableService()
2391
-
2392
- ```ts
2393
- registerPersistableService(name: string, hooks: PersistableService): () => void;
2394
- ```
2395
-
2396
- Register a service (auth, storage, …) as a persistence participant.
2397
- The sandbox calls `hooks.snapshot()` on every flush and
2398
- `hooks.restore(data)` on restore. If `hooks.subscribe` is provided,
2399
- the persistence controller subscribes and schedules a debounced
2400
- flush on each change — so auth-user edits flush promptly, not only
2401
- on the next Firestore write.
2402
-
2403
- Returns an unregister function — call it if the service is torn
2404
- down before the sandbox is disposed (uncommon in practice; the
2405
- sandbox's `dispose()` clears the registry anyway).
2406
-
2407
- Throws `failed-precondition` when a service with the same `name` is
2408
- already registered — the auth package registers `'auth'` once when
2409
- `getAuth(sandbox)` first creates a backend, so accidental double-
2410
- registration is a caller bug, not a no-op.
2411
-
2412
- **Advanced / internal API.** Service packages (auth, storage) call
2413
- this when they first attach to a sandbox. Consumer app code should
2414
- not need to call this directly.
2415
-
2416
- ###### Parameters
2417
-
2418
- | Parameter | Type |
2419
- | :------ | :------ |
2420
- | `name` | `string` |
2421
- | `hooks` | [`PersistableService`](#persistableservice) |
2422
-
2423
- ###### Returns
2424
-
2425
- ```ts
2426
- (): void;
2427
- ```
2428
-
2429
- ###### Returns
2430
-
2431
- `void`
2432
-
2433
- <a id="reset-4"></a>
2434
-
2435
- ##### reset()
2436
-
2437
- ```ts
2438
- reset(): void;
2439
- ```
2440
-
2441
- Reset the underlying environment to a fresh state — wipes data,
2442
- rules, and any service-specific configuration.
2443
-
2444
- Snapshot listeners attached to the OLD environment are dropped at
2445
- the swap — they can't survive because their target docs have been
2446
- wiped. `onEvent` subscribers DO survive — the registry lives on
2447
- the sandbox, and a `session_boundary` event with `phase: 'reset'`
2448
- fires before the swap so subscribers know the rollover happened.
2449
- Existing [SandboxContext](#sandboxcontext)s continue to work — their sandbox
2450
- reference is stable; subsequent operations resolve to the new env.
2451
-
2452
- ###### Returns
2453
-
2454
- `void`
2455
-
2456
- <a id="runwithprovenance-4"></a>
2457
-
2458
- ##### runWithProvenance()?
2459
-
2460
- ```ts
2461
- optional runWithProvenance<T>(provenance: EventProvenance, fn: () => T): T;
2462
- ```
2463
-
2464
- Run `fn` with ambient [EventProvenance](#eventprovenance) defaults: every event
2465
- emitted SYNCHRONOUSLY during `fn` that doesn't already carry a
2466
- provenance field (on the event itself or via an explicit per-emit
2467
- override) is stamped with these values instead of the global
2468
- defaults. This is the mechanical "who issued this op" seam the
2469
- serve worker uses to tag Studio-issued ops (`actor: { kind:
2470
- 'studio' }`) and to stamp the auth lens an op ran under
2471
- (`authLens`) — declared by the caller that issues the op, never
2472
- inferred from the op's shape.
2473
-
2474
- SYNCHRONOUS WINDOW: the ambient values apply only until `fn`
2475
- returns (for an async `fn`, its synchronous prefix — which covers
2476
- the local environment's rules eval + event emission, since those
2477
- run before the op's promise is handed back). Work an op DEFERS
2478
- (snapshot-listener deliveries and re-evals drain on a microtask,
2479
- off-stack) is intentionally OUTSIDE the window: a listener re-eval
2480
- belongs to the listener's owner, not to whoever's write triggered
2481
- it. Nested calls stack — the innermost window wins per field, and
2482
- each window restores the previous one on exit (including on throw).
2483
-
2484
- OPTIONAL because remote sandbox proxies can't provide an ambient
2485
- emit window (events are emitted in the worker they front). Callers
2486
- spell `sandbox.runWithProvenance?.(prov, fn) ?? fn()`.
2487
-
2488
- ###### Type Parameters
2489
-
2490
- | Type Parameter |
2491
- | :------ |
2492
- | `T` |
2493
-
2494
- ###### Parameters
2495
-
2496
- | Parameter | Type |
2497
- | :------ | :------ |
2498
- | `provenance` | [`EventProvenance`](#eventprovenance) |
2499
- | `fn` | () => `T` |
2500
-
2501
- ###### Returns
2502
-
2503
- `T`
2504
-
2505
- <a id="snapshot-6"></a>
2506
-
2507
- ##### snapshot()
2508
-
2509
- ```ts
2510
- snapshot(): SandboxSnapshot;
2511
- ```
2512
-
2513
- Capture a snapshot of every service's state. For v1 with only
2514
- Firestore, the return value carries a `firestore` key mapping doc
2515
- paths to data. Future services will add their own keys.
2516
-
2517
- ###### Returns
2518
-
2519
- [`SandboxSnapshot`](#sandboxsnapshot-2)
2520
-
2521
- <a id="withauth-6"></a>
2522
-
2523
- ##### withAuth()
2524
-
2525
- ```ts
2526
- withAuth(auth: {
2527
- token?: Record<string, unknown>;
2528
- uid: string;
2529
- }): SandboxContext;
2530
- ```
2531
-
2532
- Derive a context bound to this sandbox under the given auth
2533
- identity. Operations through services attached to the returned
2534
- context evaluate rules under that identity. Many contexts can
2535
- coexist for one sandbox; data is shared.
2536
-
2537
- `null` is anonymous; an `AuthState` object names the user (and
2538
- optional custom claims). Passing `undefined` is a deliberate
2539
- error — say `withAuth(null)` for anonymous so the call site is
2540
- unambiguous.
2541
-
2542
- ###### Parameters
2543
-
2544
- | Parameter | Type |
2545
- | :------ | :------ |
2546
- | `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2547
- | `auth.token?` | `Record`\<`string`, `unknown`\> |
2548
- | `auth.uid` | `string` |
2549
-
2550
- ###### Returns
2551
-
2552
- [`SandboxContext`](#sandboxcontext)
2553
-
2554
- ###### Example
2555
-
2556
- ```ts
2557
- const sandbox = initializeSandbox();
2558
- const dbAlice = getFirestore(sandbox.withAuth({ uid: 'alice' }));
2559
- const dbAnon = getFirestore(sandbox.withAuth(null));
2560
- ```
2561
-
2562
- ***
2563
-
2564
- <a id="sandboxcommitevent"></a>
2565
-
2566
- ### SandboxCommitEvent
2567
-
2568
- Canonical committed mutation event. Unlike `operation`, this fires only when
2569
- state actually changed. Replay and branch tooling should eventually consume
2570
- these service adapters instead of filtering Firestore-only `write` events.
2571
-
2572
- #### Properties
2573
-
2574
- | Property | Type |
2575
- | :------ | :------ |
2576
- | <a id="at-3"></a> `at` | `number` |
2577
- | <a id="auth-6"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2578
- | `auth.token?` | `Record`\<`string`, `unknown`\> |
2579
- | `auth.uid` | `string` |
2580
- | <a id="data"></a> `data?` | `unknown` |
2581
- | <a id="detail-1"></a> `detail?` | `Record`\<`string`, `unknown`\> |
2582
- | <a id="groupid-1"></a> `groupId?` | `string` |
2583
- | <a id="groupkind-1"></a> `groupKind?` | `"transaction"` \| `"batch"` |
2584
- | <a id="id-3"></a> `id` | `string` |
2585
- | <a id="kind-2"></a> `kind` | `"commit"` |
2586
- | <a id="method-2"></a> `method` | `string` |
2587
- | <a id="nextstate"></a> `nextState?` | `unknown` |
2588
- | <a id="path-2"></a> `path?` | `string` |
2589
- | <a id="priorstate"></a> `priorState?` | `unknown` |
2590
- | <a id="replay"></a> `replay?` | \{ `autoId?`: `string`; `requestTime?`: `number`; `sentinels?`: \{ `field`: `string`; `kind`: `string`; \}[]; \} |
2591
- | `replay.autoId?` | `string` |
2592
- | `replay.requestTime?` | `number` |
2593
- | `replay.sentinels?` | \{ `field`: `string`; `kind`: `string`; \}[] |
2594
- | <a id="service-2"></a> `service` | [`EventService`](#eventservice) |
2595
-
2596
- ***
2597
-
2598
- <a id="sandboxconfig"></a>
2599
-
2600
- ### SandboxConfig
2601
-
2602
- Initialization config for a sandbox. All fields are optional; an
2603
- empty config produces a sandbox with no rules and no seeded data.
2604
-
2605
- **No `auth` field.** Identity belongs to [SandboxContext](#sandboxcontext), not
2606
- the sandbox. Service handles always require an explicit context.
2607
-
2608
- ***
2609
-
2610
- <a id="sandboxcontext"></a>
2611
-
2612
- ### SandboxContext
2613
-
2614
- Identity-bearing handle on a [Sandbox](#sandbox-3). A
2615
- `(sandbox, auth, operationContext)`
2616
- tuple — cheap to create, immutable, freely shareable. Service
2617
- factories require a `SandboxContext`; bare `Sandbox` is a type
2618
- error so every call site states identity explicitly.
2619
-
2620
- Constructed via `Sandbox.withAuth(auth)` or chained via
2621
- `SandboxContext.withAuth(auth)`. The concrete class is exported
2622
- from `pyric/sandbox` for `instanceof` routing in service
2623
- factories; consumers don't construct it directly.
2624
-
2625
- #### Properties
2626
-
2627
- | Property | Modifier | Type | Description |
2628
- | :------ | :------ | :------ | :------ |
2629
- | <a id="auth-7"></a> `auth` | `readonly` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | The identity rules evaluate under for operations through this context. |
2630
- | `auth.token?` | `public` | `Record`\<`string`, `unknown`\> | - |
2631
- | `auth.uid` | `public` | `string` | - |
2632
- | <a id="operationcontext-3"></a> `operationContext` | `readonly` | [`OperationContext`](#operationcontext-2) | Immutable provenance bound to every operation issued through this handle. |
2633
- | <a id="sandbox-4"></a> `sandbox` | `readonly` | [`Sandbox`](#sandbox-3) | The data foundation this context operates against. |
2634
-
2635
- #### Methods
2636
-
2637
- <a id="withauth-8"></a>
2638
-
2639
- ##### withAuth()
2640
-
2641
- ```ts
2642
- withAuth(auth: {
2643
- token?: Record<string, unknown>;
2644
- uid: string;
2645
- }): SandboxContext;
2646
- ```
2647
-
2648
- Derive a sibling context on the same sandbox with different auth.
2649
- Replaces auth and its lens while preserving the operation source and
2650
- optional plan identity.
2651
-
2652
- ###### Parameters
2653
-
2654
- | Parameter | Type |
2655
- | :------ | :------ |
2656
- | `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2657
- | `auth.token?` | `Record`\<`string`, `unknown`\> |
2658
- | `auth.uid` | `string` |
2659
-
2660
- ###### Returns
2661
-
2662
- [`SandboxContext`](#sandboxcontext)
2663
-
2664
- ***
2665
-
2666
- <a id="sandboxlistenerevent"></a>
2667
-
2668
- ### SandboxListenerEvent
2669
-
2670
- Canonical listener lifecycle/delivery event. Firestore's existing snapshot
2671
- delivery/lifecycle variants are preserved; this shape gives RTDB and future
2672
- service listeners the same debuggable surface.
2673
-
2674
- #### Properties
2675
-
2676
- | Property | Type |
2677
- | :------ | :------ |
2678
- | <a id="at-4"></a> `at` | `number` |
2679
- | <a id="auth-8"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2680
- | `auth.token?` | `Record`\<`string`, `unknown`\> |
2681
- | `auth.uid` | `string` |
2682
- | <a id="detail-2"></a> `detail?` | `Record`\<`string`, `unknown`\> |
2683
- | <a id="error-1"></a> `error?` | \{ `code?`: `string`; `message`: `string`; `reasons?`: `string`[]; \} |
2684
- | `error.code?` | `string` |
2685
- | `error.message` | `string` |
2686
- | `error.reasons?` | `string`[] |
2687
- | <a id="id-4"></a> `id` | `string` |
2688
- | <a id="kind-3"></a> `kind` | `"listener"` |
2689
- | <a id="listenerid-1"></a> `listenerId` | `string` |
2690
- | <a id="phase"></a> `phase` | `"attach"` \| `"detach"` \| `"delivery"` \| `"suppressed"` \| `"errored"` |
2691
- | <a id="reason"></a> `reason?` | `string` |
2692
- | <a id="result-2"></a> `result?` | `"allow"` \| `"deny"` \| `"unsupported"` \| `"error"` |
2693
- | <a id="sample"></a> `sample?` | `unknown` |
2694
- | <a id="service-3"></a> `service` | [`EventService`](#eventservice) |
2695
- | <a id="size"></a> `size?` | `number` |
2696
- | <a id="target-1"></a> `target` | \{ `kind`: `string`; `path?`: `string`; `query?`: `unknown`; \} |
2697
- | `target.kind` | `string` |
2698
- | `target.path?` | `string` |
2699
- | `target.query?` | `unknown` |
2700
- | <a id="triggeredby-1"></a> `triggeredBy?` | \{ `method`: `string`; `path?`: `string`; \} |
2701
- | `triggeredBy.method` | `string` |
2702
- | `triggeredBy.path?` | `string` |
2703
-
2704
- ***
2705
-
2706
- <a id="sandboxoperationevent"></a>
2707
-
2708
- ### SandboxOperationEvent
2709
-
2710
- Canonical service operation event. This is the service-neutral successor to
2711
- Firestore's `request` traffic shape: every user-visible operation can be
2712
- represented here, whether it is backed by security rules (Firestore/RTDB/
2713
- Storage) or by a service control plane (Auth).
2714
-
2715
- Existing Firestore `request` events remain for compatibility. New cross-
2716
- service consumers should prefer `operation` because it carries an explicit
2717
- `service` discriminator and does not require RTDB/Storage/Auth to pretend
2718
- their state is a Firestore document.
2719
-
2720
- #### Properties
2721
-
2722
- | Property | Type | Description |
2723
- | :------ | :------ | :------ |
2724
- | <a id="at-5"></a> `at` | `number` | - |
2725
- | <a id="auth-9"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | - |
2726
- | `auth.token?` | `Record`\<`string`, `unknown`\> | - |
2727
- | `auth.uid` | `string` | - |
2728
- | <a id="detail-3"></a> `detail?` | `Record`\<`string`, `unknown`\> | - |
2729
- | <a id="durationms"></a> `durationMs?` | `number` | - |
2730
- | <a id="groupid-2"></a> `groupId?` | `string` | - |
2731
- | <a id="groupkind-2"></a> `groupKind?` | `"transaction"` \| `"batch"` | - |
2732
- | <a id="id-5"></a> `id` | `string` | - |
2733
- | <a id="kind-4"></a> `kind` | `"operation"` | - |
2734
- | <a id="method-3"></a> `method` | `string` | - |
2735
- | <a id="origin-1"></a> `origin` | `"admin"` \| `"user"` \| `"listener"` \| `"transaction"` \| `"batch"` \| `"system"` | - |
2736
- | <a id="path-3"></a> `path?` | `string` | - |
2737
- | <a id="reasons-3"></a> `reasons?` | `string`[] | - |
2738
- | <a id="request-3"></a> `request?` | \{ `data?`: `unknown`; `query?`: `unknown`; `resourceData?`: `unknown`; \} | - |
2739
- | `request.data?` | `unknown` | - |
2740
- | `request.query?` | `unknown` | - |
2741
- | `request.resourceData?` | `unknown` | - |
2742
- | <a id="resourceafter-1"></a> `resourceAfter?` | \{ `data`: `unknown`; `exists`: `boolean`; \} | - |
2743
- | `resourceAfter.data` | `unknown` | - |
2744
- | `resourceAfter.exists` | `boolean` | - |
2745
- | <a id="resourcebefore-1"></a> `resourceBefore?` | \{ `data`: `unknown`; `exists`: `boolean`; \} | - |
2746
- | `resourceBefore.data` | `unknown` | - |
2747
- | `resourceBefore.exists` | `boolean` | - |
2748
- | <a id="result-3"></a> `result` | `"allow"` \| `"deny"` \| `"unsupported"` \| `"error"` \| `"not-applicable"` | - |
2749
- | <a id="rules-2"></a> `rules?` | \{ `engine`: `"firestore"` \| `"storage"` \| `"rtdb"`; `errorCode?`: `string`; `matchedPath?`: `string`; `matchedRule?`: `string`; `operations?`: `string`[]; `pathVariableBindings?`: `Record`\<`string`, `string`\>; `reason?`: `string`; `ruleIndex?`: `number`; \} | - |
2750
- | `rules.engine` | `"firestore"` \| `"storage"` \| `"rtdb"` | - |
2751
- | `rules.errorCode?` | `string` | - |
2752
- | `rules.matchedPath?` | `string` | - |
2753
- | `rules.matchedRule?` | `string` | - |
2754
- | `rules.operations?` | `string`[] | - |
2755
- | `rules.pathVariableBindings?` | `Record`\<`string`, `string`\> | - |
2756
- | `rules.reason?` | `string` | - |
2757
- | `rules.ruleIndex?` | `number` | - |
2758
- | <a id="rulesdisposition-1"></a> `rulesDisposition?` | [`RulesDisposition`](#rulesdisposition-2) | 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. |
2759
- | <a id="service-4"></a> `service` | [`EventService`](#eventservice) | - |
2760
- | <a id="triggeredby-2"></a> `triggeredBy?` | \{ `method`: `string`; `path?`: `string`; \} | - |
2761
- | `triggeredBy.method` | `string` | - |
2762
- | `triggeredBy.path?` | `string` | - |
2763
-
2764
- ***
2765
-
2766
- <a id="sandboxpersistenceoptions"></a>
2245
+ <a id="sandboxpersistenceoptions"></a>
2767
2246
 
2768
2247
  ### SandboxPersistenceOptions
2769
2248
 
2770
- Controller options. See [Sandbox.enablePersistence](#enablepersistence-4).
2249
+ Controller options. See [Sandbox.enablePersistence](pyric-firestore-reference-api.md#enablepersistence).
2771
2250
 
2772
2251
  #### Properties
2773
2252
 
2774
2253
  | Property | Type | Description |
2775
2254
  | :------ | :------ | :------ |
2776
- | <a id="backend"></a> `backend?` | `"indexedDB"` \| `"memory"` | Storage backend. `indexedDB` requires a browser environment; in non-browser hosts (Bun, Node, tests) the controller falls back to `memory` automatically unless an `injectedBackend` is supplied. Default: `indexedDB`. |
2255
+ | <a id="backend"></a> `backend?` | `"memory"` \| `"indexedDB"` | Storage backend. `indexedDB` requires a browser environment; in non-browser hosts (Bun, Node, tests) the controller falls back to `memory` automatically unless an `injectedBackend` is supplied. Default: `indexedDB`. |
2777
2256
  | <a id="flushintervalms"></a> `flushIntervalMs?` | `number` | 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. |
2778
2257
  | <a id="injectedbackend"></a> `injectedBackend?` | [`PersistenceBackend`](#persistencebackend) | Override the backend with an injected implementation. Used by tests and hosts that have their own storage adapter. When set, `backend` is ignored. |
2779
2258
  | <a id="key"></a> `key` | `string` | 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. |
@@ -2794,7 +2273,7 @@ Canonical non-rules operational failure.
2794
2273
  | Property | Type |
2795
2274
  | :------ | :------ |
2796
2275
  | <a id="at-6"></a> `at` | `number` |
2797
- | <a id="auth-10"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2276
+ | <a id="auth-9"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2798
2277
  | `auth.token?` | `Record`\<`string`, `unknown`\> |
2799
2278
  | `auth.uid` | `string` |
2800
2279
  | <a id="detail-4"></a> `detail?` | `Record`\<`string`, `unknown`\> |
@@ -2809,7 +2288,7 @@ Canonical non-rules operational failure.
2809
2288
 
2810
2289
  ***
2811
2290
 
2812
- <a id="sandboxsnapshot-2"></a>
2291
+ <a id="sandboxsnapshot"></a>
2813
2292
 
2814
2293
  ### SandboxSnapshot
2815
2294
 
@@ -2865,7 +2344,7 @@ diff `before`→`after` when both are present.
2865
2344
  | :------ | :------ | :------ |
2866
2345
  | <a id="after"></a> `after?` | `unknown` | Best-effort serializable snapshot of the state AFTER the mutation. Absent on deletes / sign-outs (nothing remains). |
2867
2346
  | <a id="at-7"></a> `at` | `number` | - |
2868
- | <a id="auth-11"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | Identity in effect when the op ran (the service's `request.auth` equivalent). `null` for admin/anonymous-driven mutations (e.g. `sandbox.createUser`, an unauthenticated RTDB write). |
2347
+ | <a id="auth-10"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | Identity in effect when the op ran (the service's `request.auth` equivalent). `null` for admin/anonymous-driven mutations (e.g. `sandbox.createUser`, an unauthenticated RTDB write). |
2869
2348
  | `auth.token?` | `Record`\<`string`, `unknown`\> | - |
2870
2349
  | `auth.uid` | `string` | - |
2871
2350
  | <a id="before"></a> `before?` | `unknown` | 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. |
@@ -2874,7 +2353,7 @@ diff `before`→`after` when both are present.
2874
2353
  | <a id="kind-6"></a> `kind` | `"service_mutation"` | - |
2875
2354
  | <a id="op-2"></a> `op` | `string` | Service-scoped operation name. Stable, lowercase, snake/kebab-free: - auth: `user_create` | `user_update` | `user_delete` | `users_clear` | `sign_in` | `sign_out` - storage: `object_put` | `object_delete` | `metadata_update` - rtdb: `set` | `update` | `remove` | `transaction` - ai: `generate_content` | `stream_generate_content` | `count_tokens` | `request_rejected` New ops can be added without a breaking change (consumers switch with a default branch). |
2876
2355
  | <a id="path-5"></a> `path?` | `string` | The thing mutated, in the service's own addressing scheme: - auth: the user `uid` (or `'*'` for a clear-all). Absent for a sign-out with no prior user. - storage: the object `fullPath` (e.g. `avatars/alice.png`). - rtdb: the database path (e.g. `/rooms/r1/messages`), or for a multi-path `update` the ref path the call targeted. |
2877
- | <a id="service-6"></a> `service` | `"auth"` \| `"storage"` \| `"rtdb"` \| `"messaging"` \| `"ai"` | Which service performed the mutation. Always one of the non-Firestore services — Firestore rides its own `request`/`write` path. (The provenance `service` field on the stamped event mirrors this; it is set redundantly here so a consumer matching purely on `kind` still gets the discriminator without reaching into provenance.) |
2356
+ | <a id="service-6"></a> `service` | `"rtdb"` \| `"auth"` \| `"storage"` \| `"messaging"` \| `"ai"` | Which service performed the mutation. Always one of the non-Firestore services — Firestore rides its own `request`/`write` path. (The provenance `service` field on the stamped event mirrors this; it is set redundantly here so a consumer matching purely on `kind` still gets the discriminator without reaching into provenance.) |
2878
2357
 
2879
2358
  ***
2880
2359
 
@@ -2920,12 +2399,12 @@ scenario produces large snapshots.
2920
2399
  | :------ | :------ | :------ |
2921
2400
  | <a id="addedcount"></a> `addedCount` | `number` | - |
2922
2401
  | <a id="at-9"></a> `at` | `number` | - |
2923
- | <a id="auth-12"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | - |
2402
+ | <a id="auth-11"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | - |
2924
2403
  | `auth.token?` | `Record`\<`string`, `unknown`\> | - |
2925
2404
  | `auth.uid` | `string` | - |
2926
2405
  | <a id="id-9"></a> `id` | `string` | - |
2927
2406
  | <a id="kind-8"></a> `kind` | `"snapshot_delivery"` | - |
2928
- | <a id="listenerid-2"></a> `listenerId` | `string` | Opaque listener id assigned at attach time. |
2407
+ | <a id="listenerid-3"></a> `listenerId` | `string` | Opaque listener id assigned at attach time. |
2929
2408
  | <a id="modifiedcount"></a> `modifiedCount` | `number` | - |
2930
2409
  | <a id="removedcount"></a> `removedCount` | `number` | - |
2931
2410
  | <a id="sample-1"></a> `sample?` | \{ `docs`: \{ `data`: `Record`\<`string`, `unknown`\>; `path`: `string`; \}[]; \} | Doc payloads, in the order the user callback saw them. |
@@ -2962,20 +2441,20 @@ scenario surfaces them.
2962
2441
 
2963
2442
  | Property | Type |
2964
2443
  | :------ | :------ |
2965
- | <a id="auth-13"></a> `auth?` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2444
+ | <a id="auth-12"></a> `auth?` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} |
2966
2445
  | `auth.token?` | `Record`\<`string`, `unknown`\> |
2967
2446
  | `auth.uid` | `string` |
2968
- | <a id="code-2"></a> `code` | `"permission-denied"` |
2447
+ | <a id="code-1"></a> `code` | `"permission-denied"` |
2969
2448
  | <a id="message-1"></a> `message` | `string` |
2970
2449
  | <a id="reasons-4"></a> `reasons?` | `string`[] |
2971
- | <a id="request-4"></a> `request?` | \{ `method`: `"get"` \| `"list"` \| `"create"` \| `"update"` \| `"delete"`; `path`: `string`; `resourceData?`: `Record`\<`string`, `unknown`\>; \} |
2972
- | `request.method` | `"get"` \| `"list"` \| `"create"` \| `"update"` \| `"delete"` |
2450
+ | <a id="request-4"></a> `request?` | \{ `method`: `"delete"` \| `"get"` \| `"list"` \| `"create"` \| `"update"`; `path`: `string`; `resourceData?`: `Record`\<`string`, `unknown`\>; \} |
2451
+ | `request.method` | `"delete"` \| `"get"` \| `"list"` \| `"create"` \| `"update"` |
2973
2452
  | `request.path` | `string` |
2974
2453
  | `request.resourceData?` | `Record`\<`string`, `unknown`\> |
2975
2454
  | <a id="resource-2"></a> `resource?` | \{ `data`: `Record`\<`string`, `unknown`\>; `exists`: `boolean`; \} |
2976
2455
  | `resource.data` | `Record`\<`string`, `unknown`\> |
2977
2456
  | `resource.exists` | `boolean` |
2978
- | <a id="target-3"></a> `target` | \| \{ `kind`: `"doc"`; `path`: `string`; \} \| \{ `collection`: `string`; `kind`: `"query"`; \} |
2457
+ | <a id="target-3"></a> `target` | \| \{ `kind`: `"doc"`; `path`: `string`; \} \| \{ `collection`: `string`; `kind`: `"query"`; `query?`: `unknown`; \} |
2979
2458
 
2980
2459
  ***
2981
2460
 
@@ -2996,12 +2475,12 @@ them.
2996
2475
  | Property | Type | Description |
2997
2476
  | :------ | :------ | :------ |
2998
2477
  | <a id="at-10"></a> `at` | `number` | - |
2999
- | <a id="auth-14"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | - |
2478
+ | <a id="auth-13"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | - |
3000
2479
  | `auth.token?` | `Record`\<`string`, `unknown`\> | - |
3001
2480
  | `auth.uid` | `string` | - |
3002
2481
  | <a id="id-10"></a> `id` | `string` | - |
3003
2482
  | <a id="kind-9"></a> `kind` | `"snapshot_suppressed"` | - |
3004
- | <a id="listenerid-3"></a> `listenerId` | `string` | - |
2483
+ | <a id="listenerid-4"></a> `listenerId` | `string` | - |
3005
2484
  | <a id="reason-1"></a> `reason` | `"no-op"` | Why this re-eval was suppressed. v1 only emits `'no-op'`. |
3006
2485
  | <a id="target-4"></a> `target` | \| \{ `kind`: `"doc"`; `path`: `string`; \} \| \{ `collection`: `string`; `kind`: `"query"`; \} | - |
3007
2486
  | <a id="triggeredby-4"></a> `triggeredBy?` | \{ `method`: `string`; `path`: `string`; \} | - |
@@ -3124,17 +2603,17 @@ sentinel/auto-id capture lands.
3124
2603
  | Property | Type | Description |
3125
2604
  | :------ | :------ | :------ |
3126
2605
  | <a id="at-11"></a> `at` | `number` | - |
3127
- | <a id="auth-15"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | - |
2606
+ | <a id="auth-14"></a> `auth` | \{ `token?`: `Record`\<`string`, `unknown`\>; `uid`: `string`; \} | - |
3128
2607
  | `auth.token?` | `Record`\<`string`, `unknown`\> | - |
3129
2608
  | `auth.uid` | `string` | - |
3130
2609
  | <a id="autoid"></a> `autoId?` | `string` | Minted document ID when this write came from `collection.add()` / `LocalEnvironment.createWithAutoId`. The replay engine aliases the path's last segment to a fresh mint on replay (rather than preserving the original auto-ID). |
3131
2610
  | <a id="data-1"></a> `data?` | `Record`\<`string`, `unknown`\> | Pre-resolution write payload — `FieldValue.*` sentinels preserved as marker shapes (`{ __type: 'serverTimestamp' }`, etc.) so the replay engine can re-resolve them. The rule engine evaluated against the resolved form internally; the resolved form lives on [nextState](#nextstate-1). Absent on `delete`. |
3132
2611
  | <a id="detail-6"></a> `detail?` | \{ `admin?`: `boolean`; \} & `Record`\<`string`, `unknown`\> | Free-form write metadata. `admin: true` marks a rules-bypassing setup/admin commit so replay can apply it as context without asking candidate rules to permit it. |
3133
2612
  | <a id="groupid-3"></a> `groupId?` | `string` | - |
3134
- | <a id="groupkind-3"></a> `groupKind?` | `"transaction"` \| `"batch"` | - |
2613
+ | <a id="groupkind-4"></a> `groupKind?` | `"transaction"` \| `"batch"` | - |
3135
2614
  | <a id="id-11"></a> `id` | `string` | - |
3136
2615
  | <a id="kind-10"></a> `kind` | `"write"` | - |
3137
- | <a id="method-5"></a> `method` | `"create"` \| `"update"` \| `"delete"` \| `"set"` | - |
2616
+ | <a id="method-5"></a> `method` | `"delete"` \| `"create"` \| `"update"` \| `"set"` | - |
3138
2617
  | <a id="nextstate-1"></a> `nextState` | `Record`\<`string`, `unknown`\> | State AFTER this write. `null` on `delete`. |
3139
2618
  | <a id="path-6"></a> `path` | `string` | - |
3140
2619
  | <a id="priorstate-1"></a> `priorState` | `Record`\<`string`, `unknown`\> | State BEFORE this write. `null` for a non-existent doc. |
@@ -3171,32 +2650,6 @@ The identity/rules lens an operation actually ran under.
3171
2650
 
3172
2651
  ***
3173
2652
 
3174
- <a id="authstate"></a>
3175
-
3176
- ### AuthState
3177
-
3178
- ```ts
3179
- type AuthState =
3180
- | {
3181
- token?: Record<string, unknown>;
3182
- uid: string;
3183
- }
3184
- | null;
3185
- ```
3186
-
3187
- A signed-in identity for sandbox operations. `null` is anonymous.
3188
-
3189
- `token` is the Firebase Auth token claims map (custom claims plus
3190
- standard ones). It surfaces the same way it does in production rules
3191
- via `request.auth.token.*`. Omit it for plain UID-only auth.
3192
-
3193
- Renamed from `AuthContext` (pre-multi-context) so the data type
3194
- doesn't visually collide with `SandboxContext` (the identity-bearing
3195
- handle). They sit at different layers — payload vs. handle — and the
3196
- names should reflect that.
3197
-
3198
- ***
3199
-
3200
2653
  <a id="difftarget"></a>
3201
2654
 
3202
2655
  ### DiffTarget
@@ -3257,6 +2710,7 @@ type Divergence =
3257
2710
  ```ts
3258
2711
  type EventActor =
3259
2712
  | {
2713
+ journeyId?: string;
3260
2714
  kind: "app";
3261
2715
  }
3262
2716
  | {
@@ -3342,7 +2796,7 @@ the rules disposition.
3342
2796
 
3343
2797
  ***
3344
2798
 
3345
- <a id="sandboxerrorcode-1"></a>
2799
+ <a id="sandboxerrorcode"></a>
3346
2800
 
3347
2801
  ### SandboxErrorCode
3348
2802
 
@@ -3390,7 +2844,7 @@ type SandboxEvent =
3390
2844
  ```
3391
2845
 
3392
2846
  Discriminated union of every event the sandbox emits to
3393
- [Sandbox.onEvent](#onevent-4) subscribers.
2847
+ [Sandbox.onEvent](pyric-firestore-reference-api.md#onevent) subscribers.
3394
2848
 
3395
2849
  Issue #307 — replaces the prior three-channel surface
3396
2850
  (`onRequest` / `onDenial` / `onSnapshotError`). Filter on `kind`
@@ -3500,7 +2954,7 @@ We handle this in two parts:
3500
2954
 
3501
2955
  | Parameter | Type |
3502
2956
  | :------ | :------ |
3503
- | `sandbox` | [`Sandbox`](#sandbox-3) |
2957
+ | `sandbox` | [`Sandbox`](pyric-firestore-reference-api.md#sandbox) |
3504
2958
  | `rawOptions` | [`SandboxPersistenceOptions`](#sandboxpersistenceoptions) |
3505
2959
 
3506
2960
  #### Returns
@@ -3715,7 +3169,7 @@ touch the source sandbox.
3715
3169
 
3716
3170
  | Parameter | Type | Description |
3717
3171
  | :------ | :------ | :------ |
3718
- | `snapshot` | [`SandboxSnapshot`](#sandboxsnapshot-2) | Baseline state — typically `liveSandbox.snapshot()`. |
3172
+ | `snapshot` | [`SandboxSnapshot`](#sandboxsnapshot) | Baseline state — typically `liveSandbox.snapshot()`. |
3719
3173
  | `rules?` | `string` | Rules source for the branch. Pass the live rules to reproduce production behaviour, or an *edited* ruleset to test a rules change in isolation (Studio F4). |
3720
3174
 
3721
3175
  #### Returns
@@ -3735,7 +3189,7 @@ function initializeSandbox(_config?: SandboxConfig): LocalSandbox;
3735
3189
  Create a sandbox.
3736
3190
 
3737
3191
  Identity is **not** part of init — call `sandbox.withAuth(...)` to
3738
- derive a [SandboxContext](#sandboxcontext) for service operations. Service-
3192
+ derive a [SandboxContext](pyric-firestore-reference-api.md#sandboxcontext) for service operations. Service-
3739
3193
  specific configuration (rules, seed data) happens through service-specific
3740
3194
  sandbox controls — for example, `setRules(sandbox, source)` from
3741
3195
  `pyric/sandbox/firestore`.
@@ -3800,7 +3254,7 @@ than into process-local state.
3800
3254
 
3801
3255
  | Parameter | Type |
3802
3256
  | :------ | :------ |
3803
- | `sandbox` | [`Sandbox`](#sandbox-3) |
3257
+ | `sandbox` | [`Sandbox`](pyric-firestore-reference-api.md#sandbox) |
3804
3258
 
3805
3259
  #### Returns
3806
3260
 
@@ -3924,34 +3378,6 @@ cached, so a restore (list + per-record get) costs one read, not one per record.
3924
3378
 
3925
3379
  ***
3926
3380
 
3927
- <a id="rehydratedocvalue"></a>
3928
-
3929
- ### rehydrateDocValue()
3930
-
3931
- ```ts
3932
- function rehydrateDocValue(value: unknown): unknown;
3933
- ```
3934
-
3935
- Walk a parsed JSON tree and re-wrap any marker shape back into its real
3936
- wrapper-class instance. Visits arrays and plain objects recursively. Plain
3937
- values (and plain objects without a recognized discriminator) pass through.
3938
-
3939
- This is the canonical rehydrate used by BOTH the sandbox persistence
3940
- serializer and the SharedWorker wire protocol, so the IDB format and the
3941
- MessagePort wire format are guaranteed identical.
3942
-
3943
- #### Parameters
3944
-
3945
- | Parameter | Type |
3946
- | :------ | :------ |
3947
- | `value` | `unknown` |
3948
-
3949
- #### Returns
3950
-
3951
- `unknown`
3952
-
3953
- ***
3954
-
3955
3381
  <a id="replay-1"></a>
3956
3382
 
3957
3383
  ### replay()
@@ -4058,3 +3484,43 @@ Project either traffic event family into the canonical record.
4058
3484
  #### Returns
4059
3485
 
4060
3486
  [`OperationRecord`](#operationrecord)
3487
+
3488
+ ## References
3489
+
3490
+ <a id="authstate"></a>
3491
+
3492
+ ### AuthState
3493
+
3494
+ Re-exports [AuthState](pyric-firestore-reference-api.md#authstate)
3495
+
3496
+ ***
3497
+
3498
+ <a id="rehydratedocvalue"></a>
3499
+
3500
+ ### rehydrateDocValue
3501
+
3502
+ Re-exports [rehydrateDocValue](pyric-firestore-values-reference-api.md#rehydratedocvalue)
3503
+
3504
+ ***
3505
+
3506
+ <a id="sandbox-3"></a>
3507
+
3508
+ ### Sandbox
3509
+
3510
+ Re-exports [Sandbox](pyric-firestore-reference-api.md#sandbox)
3511
+
3512
+ ***
3513
+
3514
+ <a id="sandboxcontext"></a>
3515
+
3516
+ ### SandboxContext
3517
+
3518
+ Re-exports [SandboxContext](pyric-firestore-reference-api.md#sandboxcontext)
3519
+
3520
+ ***
3521
+
3522
+ <a id="sandboxerror"></a>
3523
+
3524
+ ### SandboxError
3525
+
3526
+ Re-exports [SandboxError](pyric-firestore-reference-api.md#sandboxerror)