@pyric/cli 0.1.0-alpha.9

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 (1169) hide show
  1. package/LICENSE +216 -0
  2. package/README.md +156 -0
  3. package/README.md.orig +138 -0
  4. package/dist/assurance/attachment.d.ts +24 -0
  5. package/dist/assurance/attachment.d.ts.map +1 -0
  6. package/dist/assurance/attachment.js +138 -0
  7. package/dist/assurance/attachment.js.map +1 -0
  8. package/dist/assurance/browser.d.ts +7 -0
  9. package/dist/assurance/browser.d.ts.map +1 -0
  10. package/dist/assurance/browser.js +68 -0
  11. package/dist/assurance/browser.js.map +1 -0
  12. package/dist/assurance/campaign.d.ts +41 -0
  13. package/dist/assurance/campaign.d.ts.map +1 -0
  14. package/dist/assurance/campaign.js +364 -0
  15. package/dist/assurance/campaign.js.map +1 -0
  16. package/dist/assurance/capabilities.d.ts +7 -0
  17. package/dist/assurance/capabilities.d.ts.map +1 -0
  18. package/dist/assurance/capabilities.js +295 -0
  19. package/dist/assurance/capabilities.js.map +1 -0
  20. package/dist/assurance/cases.d.ts +14 -0
  21. package/dist/assurance/cases.d.ts.map +1 -0
  22. package/dist/assurance/cases.js +27 -0
  23. package/dist/assurance/cases.js.map +1 -0
  24. package/dist/assurance/generated-capabilities.d.ts +52 -0
  25. package/dist/assurance/generated-capabilities.d.ts.map +1 -0
  26. package/dist/assurance/generated-capabilities.js +329 -0
  27. package/dist/assurance/generated-capabilities.js.map +1 -0
  28. package/dist/assurance/index.d.ts +11 -0
  29. package/dist/assurance/index.d.ts.map +1 -0
  30. package/dist/assurance/index.js +9 -0
  31. package/dist/assurance/index.js.map +1 -0
  32. package/dist/assurance/runner.d.ts +3 -0
  33. package/dist/assurance/runner.d.ts.map +1 -0
  34. package/dist/assurance/runner.js +633 -0
  35. package/dist/assurance/runner.js.map +1 -0
  36. package/dist/assurance/tool-names.d.ts +4 -0
  37. package/dist/assurance/tool-names.d.ts.map +1 -0
  38. package/dist/assurance/tool-names.js +14 -0
  39. package/dist/assurance/tool-names.js.map +1 -0
  40. package/dist/assurance/tools.d.ts +19 -0
  41. package/dist/assurance/tools.d.ts.map +1 -0
  42. package/dist/assurance/tools.js +740 -0
  43. package/dist/assurance/tools.js.map +1 -0
  44. package/dist/assurance/types.d.ts +348 -0
  45. package/dist/assurance/types.d.ts.map +1 -0
  46. package/dist/assurance/types.js +7 -0
  47. package/dist/assurance/types.js.map +1 -0
  48. package/dist/assurance/validation.d.ts +8 -0
  49. package/dist/assurance/validation.d.ts.map +1 -0
  50. package/dist/assurance/validation.js +269 -0
  51. package/dist/assurance/validation.js.map +1 -0
  52. package/dist/bridge/client/bridge.d.ts +133 -0
  53. package/dist/bridge/client/bridge.d.ts.map +1 -0
  54. package/dist/bridge/client/bridge.js +429 -0
  55. package/dist/bridge/client/bridge.js.map +1 -0
  56. package/dist/bridge/client/dispatch.d.ts +41 -0
  57. package/dist/bridge/client/dispatch.d.ts.map +1 -0
  58. package/dist/bridge/client/dispatch.js +111 -0
  59. package/dist/bridge/client/dispatch.js.map +1 -0
  60. package/dist/bridge/client.d.ts +24 -0
  61. package/dist/bridge/client.d.ts.map +1 -0
  62. package/dist/bridge/client.js +26 -0
  63. package/dist/bridge/client.js.map +1 -0
  64. package/dist/bridge/protocol.d.ts +227 -0
  65. package/dist/bridge/protocol.d.ts.map +1 -0
  66. package/dist/bridge/protocol.js +131 -0
  67. package/dist/bridge/protocol.js.map +1 -0
  68. package/dist/bridge/server/audit.d.ts +19 -0
  69. package/dist/bridge/server/audit.d.ts.map +1 -0
  70. package/dist/bridge/server/audit.js +46 -0
  71. package/dist/bridge/server/audit.js.map +1 -0
  72. package/dist/bridge/server/bridge.d.ts +126 -0
  73. package/dist/bridge/server/bridge.d.ts.map +1 -0
  74. package/dist/bridge/server/bridge.js +383 -0
  75. package/dist/bridge/server/bridge.js.map +1 -0
  76. package/dist/bridge/server/headless.d.ts +36 -0
  77. package/dist/bridge/server/headless.d.ts.map +1 -0
  78. package/dist/bridge/server/headless.js +142 -0
  79. package/dist/bridge/server/headless.js.map +1 -0
  80. package/dist/bridge/server/json-schema-to-zod.d.ts +37 -0
  81. package/dist/bridge/server/json-schema-to-zod.d.ts.map +1 -0
  82. package/dist/bridge/server/json-schema-to-zod.js +93 -0
  83. package/dist/bridge/server/json-schema-to-zod.js.map +1 -0
  84. package/dist/bridge/server/local-bridge.d.ts +33 -0
  85. package/dist/bridge/server/local-bridge.d.ts.map +1 -0
  86. package/dist/bridge/server/local-bridge.js +41 -0
  87. package/dist/bridge/server/local-bridge.js.map +1 -0
  88. package/dist/bridge/server/logger.d.ts +28 -0
  89. package/dist/bridge/server/logger.d.ts.map +1 -0
  90. package/dist/bridge/server/logger.js +42 -0
  91. package/dist/bridge/server/logger.js.map +1 -0
  92. package/dist/bridge/server/mcp-contract.d.ts +21 -0
  93. package/dist/bridge/server/mcp-contract.d.ts.map +1 -0
  94. package/dist/bridge/server/mcp-contract.js +62 -0
  95. package/dist/bridge/server/mcp-contract.js.map +1 -0
  96. package/dist/bridge/server/mcp.d.ts +26 -0
  97. package/dist/bridge/server/mcp.d.ts.map +1 -0
  98. package/dist/bridge/server/mcp.js +102 -0
  99. package/dist/bridge/server/mcp.js.map +1 -0
  100. package/dist/bridge/server/peer.d.ts +39 -0
  101. package/dist/bridge/server/peer.d.ts.map +1 -0
  102. package/dist/bridge/server/peer.js +189 -0
  103. package/dist/bridge/server/peer.js.map +1 -0
  104. package/dist/bridge/server/standalone.d.ts +49 -0
  105. package/dist/bridge/server/standalone.d.ts.map +1 -0
  106. package/dist/bridge/server/standalone.js +267 -0
  107. package/dist/bridge/server/standalone.js.map +1 -0
  108. package/dist/bridge/server/tool-metadata.d.ts +38 -0
  109. package/dist/bridge/server/tool-metadata.d.ts.map +1 -0
  110. package/dist/bridge/server/tool-metadata.js +81 -0
  111. package/dist/bridge/server/tool-metadata.js.map +1 -0
  112. package/dist/bridge/server.d.ts +21 -0
  113. package/dist/bridge/server.d.ts.map +1 -0
  114. package/dist/bridge/server.js +18 -0
  115. package/dist/bridge/server.js.map +1 -0
  116. package/dist/cli/database-rules.d.ts +43 -0
  117. package/dist/cli/database-rules.d.ts.map +1 -0
  118. package/dist/cli/database-rules.js +261 -0
  119. package/dist/cli/database-rules.js.map +1 -0
  120. package/dist/cli/dev-runner.d.ts +112 -0
  121. package/dist/cli/dev-runner.d.ts.map +1 -0
  122. package/dist/cli/dev-runner.js +239 -0
  123. package/dist/cli/dev-runner.js.map +1 -0
  124. package/dist/cli/firebase-json.d.ts +66 -0
  125. package/dist/cli/firebase-json.d.ts.map +1 -0
  126. package/dist/cli/firebase-json.js +60 -0
  127. package/dist/cli/firebase-json.js.map +1 -0
  128. package/dist/cli/firestore-indexes.d.ts +21 -0
  129. package/dist/cli/firestore-indexes.d.ts.map +1 -0
  130. package/dist/cli/firestore-indexes.js +39 -0
  131. package/dist/cli/firestore-indexes.js.map +1 -0
  132. package/dist/cli/index.d.ts +56 -0
  133. package/dist/cli/index.d.ts.map +1 -0
  134. package/dist/cli/index.js +379 -0
  135. package/dist/cli/index.js.map +1 -0
  136. package/dist/cli/init.d.ts +50 -0
  137. package/dist/cli/init.d.ts.map +1 -0
  138. package/dist/cli/init.js +182 -0
  139. package/dist/cli/init.js.map +1 -0
  140. package/dist/cli/mcp-proxy.d.ts +10 -0
  141. package/dist/cli/mcp-proxy.d.ts.map +1 -0
  142. package/dist/cli/mcp-proxy.js +129 -0
  143. package/dist/cli/mcp-proxy.js.map +1 -0
  144. package/dist/cli/parse-args.d.ts +20 -0
  145. package/dist/cli/parse-args.d.ts.map +1 -0
  146. package/dist/cli/parse-args.js +69 -0
  147. package/dist/cli/parse-args.js.map +1 -0
  148. package/dist/cli/rules.d.ts +55 -0
  149. package/dist/cli/rules.d.ts.map +1 -0
  150. package/dist/cli/rules.js +199 -0
  151. package/dist/cli/rules.js.map +1 -0
  152. package/dist/cli/scope.d.ts +12 -0
  153. package/dist/cli/scope.d.ts.map +1 -0
  154. package/dist/cli/scope.js +44 -0
  155. package/dist/cli/scope.js.map +1 -0
  156. package/dist/cli/serve.d.ts +122 -0
  157. package/dist/cli/serve.d.ts.map +1 -0
  158. package/dist/cli/serve.js +844 -0
  159. package/dist/cli/serve.js.map +1 -0
  160. package/dist/cli/service-commands.d.ts +8 -0
  161. package/dist/cli/service-commands.d.ts.map +1 -0
  162. package/dist/cli/service-commands.js +58 -0
  163. package/dist/cli/service-commands.js.map +1 -0
  164. package/dist/cli/snapshot.d.ts +22 -0
  165. package/dist/cli/snapshot.d.ts.map +1 -0
  166. package/dist/cli/snapshot.js +139 -0
  167. package/dist/cli/snapshot.js.map +1 -0
  168. package/dist/cli/storage-rules.d.ts +21 -0
  169. package/dist/cli/storage-rules.d.ts.map +1 -0
  170. package/dist/cli/storage-rules.js +121 -0
  171. package/dist/cli/storage-rules.js.map +1 -0
  172. package/dist/cli/verify.d.ts +23 -0
  173. package/dist/cli/verify.d.ts.map +1 -0
  174. package/dist/cli/verify.js +349 -0
  175. package/dist/cli/verify.js.map +1 -0
  176. package/dist/credentials/core/memoize-ttl.d.ts +58 -0
  177. package/dist/credentials/core/memoize-ttl.d.ts.map +1 -0
  178. package/dist/credentials/core/memoize-ttl.js +88 -0
  179. package/dist/credentials/core/memoize-ttl.js.map +1 -0
  180. package/dist/credentials/core/types.d.ts +11 -0
  181. package/dist/credentials/core/types.d.ts.map +1 -0
  182. package/dist/credentials/core/types.js +2 -0
  183. package/dist/credentials/core/types.js.map +1 -0
  184. package/dist/credentials/node/from-adc.d.ts +10 -0
  185. package/dist/credentials/node/from-adc.d.ts.map +1 -0
  186. package/dist/credentials/node/from-adc.js +76 -0
  187. package/dist/credentials/node/from-adc.js.map +1 -0
  188. package/dist/credentials/node/from-service-account.d.ts +24 -0
  189. package/dist/credentials/node/from-service-account.d.ts.map +1 -0
  190. package/dist/credentials/node/from-service-account.js +108 -0
  191. package/dist/credentials/node/from-service-account.js.map +1 -0
  192. package/dist/credentials/node/index.d.ts +7 -0
  193. package/dist/credentials/node/index.d.ts.map +1 -0
  194. package/dist/credentials/node/index.js +7 -0
  195. package/dist/credentials/node/index.js.map +1 -0
  196. package/dist/discover/concurrency.d.ts +42 -0
  197. package/dist/discover/concurrency.d.ts.map +1 -0
  198. package/dist/discover/concurrency.js +113 -0
  199. package/dist/discover/concurrency.js.map +1 -0
  200. package/dist/discover/crawler-adapter.d.ts +48 -0
  201. package/dist/discover/crawler-adapter.d.ts.map +1 -0
  202. package/dist/discover/crawler-adapter.js +92 -0
  203. package/dist/discover/crawler-adapter.js.map +1 -0
  204. package/dist/discover/crawler.d.ts +294 -0
  205. package/dist/discover/crawler.d.ts.map +1 -0
  206. package/dist/discover/crawler.js +764 -0
  207. package/dist/discover/crawler.js.map +1 -0
  208. package/dist/discover/credential-free.d.ts +19 -0
  209. package/dist/discover/credential-free.d.ts.map +1 -0
  210. package/dist/discover/credential-free.js +18 -0
  211. package/dist/discover/credential-free.js.map +1 -0
  212. package/dist/discover/findCollectionGroup.d.ts +44 -0
  213. package/dist/discover/findCollectionGroup.d.ts.map +1 -0
  214. package/dist/discover/findCollectionGroup.js +81 -0
  215. package/dist/discover/findCollectionGroup.js.map +1 -0
  216. package/dist/discover/firestore-source.d.ts +60 -0
  217. package/dist/discover/firestore-source.d.ts.map +1 -0
  218. package/dist/discover/firestore-source.js +11 -0
  219. package/dist/discover/firestore-source.js.map +1 -0
  220. package/dist/discover/index.d.ts +3 -0
  221. package/dist/discover/index.d.ts.map +1 -0
  222. package/dist/discover/index.js +5 -0
  223. package/dist/discover/index.js.map +1 -0
  224. package/dist/discover/merge.d.ts +108 -0
  225. package/dist/discover/merge.d.ts.map +1 -0
  226. package/dist/discover/merge.js +394 -0
  227. package/dist/discover/merge.js.map +1 -0
  228. package/dist/discover/session.d.ts +116 -0
  229. package/dist/discover/session.d.ts.map +1 -0
  230. package/dist/discover/session.js +331 -0
  231. package/dist/discover/session.js.map +1 -0
  232. package/dist/discover/tools.d.ts +58 -0
  233. package/dist/discover/tools.d.ts.map +1 -0
  234. package/dist/discover/tools.js +115 -0
  235. package/dist/discover/tools.js.map +1 -0
  236. package/dist/discover/types.d.ts +190 -0
  237. package/dist/discover/types.d.ts.map +1 -0
  238. package/dist/discover/types.js +12 -0
  239. package/dist/discover/types.js.map +1 -0
  240. package/dist/discover/wire.d.ts +70 -0
  241. package/dist/discover/wire.d.ts.map +1 -0
  242. package/dist/discover/wire.js +309 -0
  243. package/dist/discover/wire.js.map +1 -0
  244. package/dist/functions-rtdb/child.d.ts +52 -0
  245. package/dist/functions-rtdb/child.d.ts.map +1 -0
  246. package/dist/functions-rtdb/child.js +283 -0
  247. package/dist/functions-rtdb/child.js.map +1 -0
  248. package/dist/functions-rtdb/delivery.d.ts +4 -0
  249. package/dist/functions-rtdb/delivery.d.ts.map +1 -0
  250. package/dist/functions-rtdb/delivery.js +2 -0
  251. package/dist/functions-rtdb/delivery.js.map +1 -0
  252. package/dist/functions-rtdb/discovery.d.ts +41 -0
  253. package/dist/functions-rtdb/discovery.d.ts.map +1 -0
  254. package/dist/functions-rtdb/discovery.js +86 -0
  255. package/dist/functions-rtdb/discovery.js.map +1 -0
  256. package/dist/functions-rtdb/event.d.ts +20 -0
  257. package/dist/functions-rtdb/event.d.ts.map +1 -0
  258. package/dist/functions-rtdb/event.js +45 -0
  259. package/dist/functions-rtdb/event.js.map +1 -0
  260. package/dist/functions-rtdb/execution.d.ts +20 -0
  261. package/dist/functions-rtdb/execution.d.ts.map +1 -0
  262. package/dist/functions-rtdb/execution.js +70 -0
  263. package/dist/functions-rtdb/execution.js.map +1 -0
  264. package/dist/functions-rtdb/in-memory-delivery.d.ts +9 -0
  265. package/dist/functions-rtdb/in-memory-delivery.d.ts.map +1 -0
  266. package/dist/functions-rtdb/in-memory-delivery.js +36 -0
  267. package/dist/functions-rtdb/in-memory-delivery.js.map +1 -0
  268. package/dist/functions-rtdb/project.d.ts +7 -0
  269. package/dist/functions-rtdb/project.d.ts.map +1 -0
  270. package/dist/functions-rtdb/project.js +54 -0
  271. package/dist/functions-rtdb/project.js.map +1 -0
  272. package/dist/functions-rtdb/projection.d.ts +14 -0
  273. package/dist/functions-rtdb/projection.d.ts.map +1 -0
  274. package/dist/functions-rtdb/projection.js +65 -0
  275. package/dist/functions-rtdb/projection.js.map +1 -0
  276. package/dist/functions-rtdb/reference-pattern.d.ts +5 -0
  277. package/dist/functions-rtdb/reference-pattern.d.ts.map +1 -0
  278. package/dist/functions-rtdb/reference-pattern.js +14 -0
  279. package/dist/functions-rtdb/reference-pattern.js.map +1 -0
  280. package/dist/functions-rtdb/remote-delivery.d.ts +9 -0
  281. package/dist/functions-rtdb/remote-delivery.d.ts.map +1 -0
  282. package/dist/functions-rtdb/remote-delivery.js +11 -0
  283. package/dist/functions-rtdb/remote-delivery.js.map +1 -0
  284. package/dist/pkg-version.d.ts +2 -0
  285. package/dist/pkg-version.d.ts.map +1 -0
  286. package/dist/pkg-version.js +47 -0
  287. package/dist/pkg-version.js.map +1 -0
  288. package/dist/register/esm-exports.d.ts +26 -0
  289. package/dist/register/esm-exports.d.ts.map +1 -0
  290. package/dist/register/esm-exports.js +61 -0
  291. package/dist/register/esm-exports.js.map +1 -0
  292. package/dist/register/hooks.d.ts +13 -0
  293. package/dist/register/hooks.d.ts.map +1 -0
  294. package/dist/register/hooks.js +15 -0
  295. package/dist/register/hooks.js.map +1 -0
  296. package/dist/register/index.d.ts +5 -0
  297. package/dist/register/index.d.ts.map +1 -0
  298. package/dist/register/index.js +124 -0
  299. package/dist/register/index.js.map +1 -0
  300. package/dist/register/mapping.d.ts +19 -0
  301. package/dist/register/mapping.d.ts.map +1 -0
  302. package/dist/register/mapping.js +35 -0
  303. package/dist/register/mapping.js.map +1 -0
  304. package/dist/remote/index.d.ts +232 -0
  305. package/dist/remote/index.d.ts.map +1 -0
  306. package/dist/remote/index.js +720 -0
  307. package/dist/remote/index.js.map +1 -0
  308. package/dist/rtdb/crawl-snapshot.d.ts +17 -0
  309. package/dist/rtdb/crawl-snapshot.d.ts.map +1 -0
  310. package/dist/rtdb/crawl-snapshot.js +72 -0
  311. package/dist/rtdb/crawl-snapshot.js.map +1 -0
  312. package/dist/rtdb/inspection.d.ts +8 -0
  313. package/dist/rtdb/inspection.d.ts.map +1 -0
  314. package/dist/rtdb/inspection.js +111 -0
  315. package/dist/rtdb/inspection.js.map +1 -0
  316. package/dist/rtdb/load-rules-document.d.ts +25 -0
  317. package/dist/rtdb/load-rules-document.d.ts.map +1 -0
  318. package/dist/rtdb/load-rules-document.js +38 -0
  319. package/dist/rtdb/load-rules-document.js.map +1 -0
  320. package/dist/rtdb/rules-generation-tool.d.ts +4 -0
  321. package/dist/rtdb/rules-generation-tool.d.ts.map +1 -0
  322. package/dist/rtdb/rules-generation-tool.js +35 -0
  323. package/dist/rtdb/rules-generation-tool.js.map +1 -0
  324. package/dist/rtdb/rules-json.d.ts +8 -0
  325. package/dist/rtdb/rules-json.d.ts.map +1 -0
  326. package/dist/rtdb/rules-json.js +17 -0
  327. package/dist/rtdb/rules-json.js.map +1 -0
  328. package/dist/serve/bridge-mount.d.ts +36 -0
  329. package/dist/serve/bridge-mount.d.ts.map +1 -0
  330. package/dist/serve/bridge-mount.js +162 -0
  331. package/dist/serve/bridge-mount.js.map +1 -0
  332. package/dist/serve/bundler.d.ts +110 -0
  333. package/dist/serve/bundler.d.ts.map +1 -0
  334. package/dist/serve/bundler.js +432 -0
  335. package/dist/serve/bundler.js.map +1 -0
  336. package/dist/serve/capture-store.d.ts +27 -0
  337. package/dist/serve/capture-store.d.ts.map +1 -0
  338. package/dist/serve/capture-store.js +47 -0
  339. package/dist/serve/capture-store.js.map +1 -0
  340. package/dist/serve/discovery.d.ts +71 -0
  341. package/dist/serve/discovery.d.ts.map +1 -0
  342. package/dist/serve/discovery.js +158 -0
  343. package/dist/serve/discovery.js.map +1 -0
  344. package/dist/serve/docs-ui/_astro/_slug_.CQYSemvw.css +1 -0
  345. package/dist/serve/docs-ui/docs/_rhythm/index.html +99 -0
  346. package/dist/serve/docs-ui/docs/_rhythm.md +101 -0
  347. package/dist/serve/docs-ui/docs/api-reference/index.html +64 -0
  348. package/dist/serve/docs-ui/docs/api-reference.md +67 -0
  349. package/dist/serve/docs-ui/docs/audit-your-rules/index.html +34 -0
  350. package/dist/serve/docs-ui/docs/audit-your-rules.md +50 -0
  351. package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +498 -0
  352. package/dist/serve/docs-ui/docs/create-pyric-reference-api.md +345 -0
  353. package/dist/serve/docs-ui/docs/firestore-rules-limits/index.html +59 -0
  354. package/dist/serve/docs-ui/docs/firestore-rules-limits.md +54 -0
  355. package/dist/serve/docs-ui/docs/how-the-swap-works/index.html +47 -0
  356. package/dist/serve/docs-ui/docs/how-the-swap-works.md +43 -0
  357. package/dist/serve/docs-ui/docs/how-we-know-it-matches-firebase/index.html +131 -0
  358. package/dist/serve/docs-ui/docs/how-we-know-it-matches-firebase.md +169 -0
  359. package/dist/serve/docs-ui/docs/index.html +2 -0
  360. package/dist/serve/docs-ui/docs/index.json +18637 -0
  361. package/dist/serve/docs-ui/docs/overview/index.html +23 -0
  362. package/dist/serve/docs-ui/docs/overview.md +40 -0
  363. package/dist/serve/docs-ui/docs/pyric-admin-app/index.html +19 -0
  364. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +185 -0
  365. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api.md +206 -0
  366. package/dist/serve/docs-ui/docs/pyric-admin-app.md +27 -0
  367. package/dist/serve/docs-ui/docs/pyric-admin-auth/index.html +24 -0
  368. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +617 -0
  369. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api.md +441 -0
  370. package/dist/serve/docs-ui/docs/pyric-admin-auth.md +29 -0
  371. package/dist/serve/docs-ui/docs/pyric-admin-database/index.html +26 -0
  372. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +583 -0
  373. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api.md +597 -0
  374. package/dist/serve/docs-ui/docs/pyric-admin-database.md +31 -0
  375. package/dist/serve/docs-ui/docs/pyric-admin-firestore/index.html +38 -0
  376. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-error-translation/index.html +57 -0
  377. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-error-translation.md +95 -0
  378. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-per-call-delegate/index.html +58 -0
  379. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-per-call-delegate.md +94 -0
  380. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-why-mirror-admin-shape/index.html +59 -0
  381. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-why-mirror-admin-shape.md +88 -0
  382. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-run-a-transaction/index.html +53 -0
  383. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-run-a-transaction.md +76 -0
  384. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-seed-and-set-rules/index.html +62 -0
  385. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-seed-and-set-rules.md +96 -0
  386. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-translate-denials/index.html +80 -0
  387. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-translate-denials.md +98 -0
  388. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-use-onsnapshot/index.html +57 -0
  389. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-use-onsnapshot.md +88 -0
  390. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-write-a-batch/index.html +50 -0
  391. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-write-a-batch.md +73 -0
  392. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +32 -0
  393. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api.md +30 -0
  394. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-onsnapshot/index.html +68 -0
  395. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-onsnapshot.md +81 -0
  396. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-re-exported-types/index.html +76 -0
  397. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-re-exported-types.md +103 -0
  398. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-sandbox-firestore/index.html +40 -0
  399. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-sandbox-firestore.md +77 -0
  400. package/dist/serve/docs-ui/docs/pyric-admin-firestore-tutorials-01-first-admin-session/index.html +118 -0
  401. package/dist/serve/docs-ui/docs/pyric-admin-firestore-tutorials-01-first-admin-session.md +166 -0
  402. package/dist/serve/docs-ui/docs/pyric-admin-firestore.md +53 -0
  403. package/dist/serve/docs-ui/docs/pyric-admin-storage/index.html +25 -0
  404. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +317 -0
  405. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api.md +285 -0
  406. package/dist/serve/docs-ui/docs/pyric-admin-storage.md +30 -0
  407. package/dist/serve/docs-ui/docs/pyric-ai-compat/index.html +436 -0
  408. package/dist/serve/docs-ui/docs/pyric-ai-compat.md +471 -0
  409. package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +5831 -0
  410. package/dist/serve/docs-ui/docs/pyric-ai-reference-api.md +5440 -0
  411. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +159 -0
  412. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api.md +122 -0
  413. package/dist/serve/docs-ui/docs/pyric-app-compat/index.html +180 -0
  414. package/dist/serve/docs-ui/docs/pyric-app-compat.md +199 -0
  415. package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +420 -0
  416. package/dist/serve/docs-ui/docs/pyric-app-reference-api.md +326 -0
  417. package/dist/serve/docs-ui/docs/pyric-auth/index.html +59 -0
  418. package/dist/serve/docs-ui/docs/pyric-auth-compat/index.html +804 -0
  419. package/dist/serve/docs-ui/docs/pyric-auth-compat.md +847 -0
  420. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +5452 -0
  421. package/dist/serve/docs-ui/docs/pyric-auth-reference-api.md +5271 -0
  422. package/dist/serve/docs-ui/docs/pyric-auth-reference-feature-matrix/index.html +781 -0
  423. package/dist/serve/docs-ui/docs/pyric-auth-reference-feature-matrix.md +198 -0
  424. package/dist/serve/docs-ui/docs/pyric-auth-reference-sandbox-test-driver/index.html +71 -0
  425. package/dist/serve/docs-ui/docs/pyric-auth-reference-sandbox-test-driver.md +108 -0
  426. package/dist/serve/docs-ui/docs/pyric-auth.md +86 -0
  427. package/dist/serve/docs-ui/docs/pyric-cli/index.html +40 -0
  428. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +98 -0
  429. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api.md +88 -0
  430. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +2355 -0
  431. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api.md +1554 -0
  432. package/dist/serve/docs-ui/docs/pyric-cli-bridge/index.html +69 -0
  433. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +546 -0
  434. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api.md +485 -0
  435. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +506 -0
  436. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api.md +419 -0
  437. package/dist/serve/docs-ui/docs/pyric-cli-bridge.md +99 -0
  438. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +67 -0
  439. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api.md +59 -0
  440. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +2355 -0
  441. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api.md +2036 -0
  442. package/dist/serve/docs-ui/docs/pyric-cli-functions-rtdb-compat/index.html +112 -0
  443. package/dist/serve/docs-ui/docs/pyric-cli-functions-rtdb-compat.md +124 -0
  444. package/dist/serve/docs-ui/docs/pyric-cli-how-to-build-a-standalone-binary/index.html +91 -0
  445. package/dist/serve/docs-ui/docs/pyric-cli-how-to-build-a-standalone-binary.md +105 -0
  446. package/dist/serve/docs-ui/docs/pyric-cli-how-to-promote-sandbox-state-to-a-fixture/index.html +52 -0
  447. package/dist/serve/docs-ui/docs/pyric-cli-how-to-promote-sandbox-state-to-a-fixture.md +98 -0
  448. package/dist/serve/docs-ui/docs/pyric-cli-how-to-run-rtdb-onvaluecreated/index.html +66 -0
  449. package/dist/serve/docs-ui/docs/pyric-cli-how-to-run-rtdb-onvaluecreated.md +100 -0
  450. package/dist/serve/docs-ui/docs/pyric-cli-how-to-serve-persistence-and-multi-tab/index.html +228 -0
  451. package/dist/serve/docs-ui/docs/pyric-cli-how-to-serve-persistence-and-multi-tab.md +179 -0
  452. package/dist/serve/docs-ui/docs/pyric-cli-how-to-use-the-vite-plugin/index.html +341 -0
  453. package/dist/serve/docs-ui/docs/pyric-cli-how-to-use-the-vite-plugin.md +309 -0
  454. package/dist/serve/docs-ui/docs/pyric-cli-how-to-verify-against-a-captured-session/index.html +128 -0
  455. package/dist/serve/docs-ui/docs/pyric-cli-how-to-verify-against-a-captured-session.md +207 -0
  456. package/dist/serve/docs-ui/docs/pyric-cli-reference-cli/index.html +404 -0
  457. package/dist/serve/docs-ui/docs/pyric-cli-reference-cli.md +249 -0
  458. package/dist/serve/docs-ui/docs/pyric-cli-reference-package-and-resolution/index.html +93 -0
  459. package/dist/serve/docs-ui/docs/pyric-cli-reference-package-and-resolution.md +65 -0
  460. package/dist/serve/docs-ui/docs/pyric-cli-reference-verify/index.html +81 -0
  461. package/dist/serve/docs-ui/docs/pyric-cli-reference-verify.md +110 -0
  462. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +10 -0
  463. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api.md +16 -0
  464. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1044 -0
  465. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api.md +952 -0
  466. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +5041 -0
  467. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +4739 -0
  468. package/dist/serve/docs-ui/docs/pyric-cli-tutorials-server-adoption/index.html +219 -0
  469. package/dist/serve/docs-ui/docs/pyric-cli-tutorials-server-adoption.md +251 -0
  470. package/dist/serve/docs-ui/docs/pyric-cli-tutorials-wire-claude-code/index.html +136 -0
  471. package/dist/serve/docs-ui/docs/pyric-cli-tutorials-wire-claude-code.md +217 -0
  472. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +926 -0
  473. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api.md +586 -0
  474. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +102 -0
  475. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +76 -0
  476. package/dist/serve/docs-ui/docs/pyric-cli.md +42 -0
  477. package/dist/serve/docs-ui/docs/pyric-conformance-scores/index.html +180 -0
  478. package/dist/serve/docs-ui/docs/pyric-conformance-scores.md +184 -0
  479. package/dist/serve/docs-ui/docs/pyric-database/index.html +43 -0
  480. package/dist/serve/docs-ui/docs/pyric-database-compat/index.html +1224 -0
  481. package/dist/serve/docs-ui/docs/pyric-database-compat.md +1289 -0
  482. package/dist/serve/docs-ui/docs/pyric-database-explanation-rules-authoring-and-deploy-are-separate/index.html +16 -0
  483. package/dist/serve/docs-ui/docs/pyric-database-explanation-rules-authoring-and-deploy-are-separate.md +23 -0
  484. package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +1851 -0
  485. package/dist/serve/docs-ui/docs/pyric-database-reference-api.md +1842 -0
  486. package/dist/serve/docs-ui/docs/pyric-database-reference-constraints/index.html +381 -0
  487. package/dist/serve/docs-ui/docs/pyric-database-reference-constraints.md +168 -0
  488. package/dist/serve/docs-ui/docs/pyric-database-reference-rules-tooling/index.html +218 -0
  489. package/dist/serve/docs-ui/docs/pyric-database-reference-rules-tooling.md +303 -0
  490. package/dist/serve/docs-ui/docs/pyric-database-tutorials-01-author-rtdb-rules-with-constraints/index.html +125 -0
  491. package/dist/serve/docs-ui/docs/pyric-database-tutorials-01-author-rtdb-rules-with-constraints.md +171 -0
  492. package/dist/serve/docs-ui/docs/pyric-database.md +26 -0
  493. package/dist/serve/docs-ui/docs/pyric-explanation-versioning-and-compatibility/index.html +40 -0
  494. package/dist/serve/docs-ui/docs/pyric-explanation-versioning-and-compatibility.md +68 -0
  495. package/dist/serve/docs-ui/docs/pyric-firestore/index.html +48 -0
  496. package/dist/serve/docs-ui/docs/pyric-firestore-compat/index.html +868 -0
  497. package/dist/serve/docs-ui/docs/pyric-firestore-compat.md +939 -0
  498. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-rules-tooling-is-separate/index.html +92 -0
  499. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-rules-tooling-is-separate.md +70 -0
  500. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-target-symbol-opacity/index.html +59 -0
  501. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-target-symbol-opacity.md +91 -0
  502. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-two-backends-one-surface/index.html +43 -0
  503. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-two-backends-one-surface.md +58 -0
  504. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-migrate-from-firebase-firestore/index.html +58 -0
  505. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-migrate-from-firebase-firestore.md +86 -0
  506. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-pick-a-backend/index.html +52 -0
  507. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-pick-a-backend.md +83 -0
  508. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-run-a-transaction/index.html +42 -0
  509. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-run-a-transaction.md +59 -0
  510. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-use-onsnapshot/index.html +66 -0
  511. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-use-onsnapshot.md +96 -0
  512. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-use-sandbox-ops/index.html +61 -0
  513. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-use-sandbox-ops.md +92 -0
  514. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +3623 -0
  515. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api.md +3399 -0
  516. package/dist/serve/docs-ui/docs/pyric-firestore-reference-feature-matrix/index.html +80 -0
  517. package/dist/serve/docs-ui/docs/pyric-firestore-reference-feature-matrix.md +66 -0
  518. package/dist/serve/docs-ui/docs/pyric-firestore-reference-getfirestore/index.html +46 -0
  519. package/dist/serve/docs-ui/docs/pyric-firestore-reference-getfirestore.md +73 -0
  520. package/dist/serve/docs-ui/docs/pyric-firestore-reference-query-constraints/index.html +80 -0
  521. package/dist/serve/docs-ui/docs/pyric-firestore-reference-query-constraints.md +129 -0
  522. package/dist/serve/docs-ui/docs/pyric-firestore-reference-sandbox-ops/index.html +58 -0
  523. package/dist/serve/docs-ui/docs/pyric-firestore-reference-sandbox-ops.md +88 -0
  524. package/dist/serve/docs-ui/docs/pyric-firestore-reference-tool-factories/index.html +51 -0
  525. package/dist/serve/docs-ui/docs/pyric-firestore-reference-tool-factories.md +71 -0
  526. package/dist/serve/docs-ui/docs/pyric-firestore-tutorials-01-write-a-sandbox-demo/index.html +130 -0
  527. package/dist/serve/docs-ui/docs/pyric-firestore-tutorials-01-write-a-sandbox-demo.md +180 -0
  528. package/dist/serve/docs-ui/docs/pyric-firestore-tutorials-02-swap-to-prod-backend/index.html +59 -0
  529. package/dist/serve/docs-ui/docs/pyric-firestore-tutorials-02-swap-to-prod-backend.md +92 -0
  530. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +30 -0
  531. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +30 -0
  532. package/dist/serve/docs-ui/docs/pyric-firestore.md +72 -0
  533. package/dist/serve/docs-ui/docs/pyric-messaging-compat/index.html +300 -0
  534. package/dist/serve/docs-ui/docs/pyric-messaging-compat.md +321 -0
  535. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +508 -0
  536. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api.md +374 -0
  537. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +482 -0
  538. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api.md +348 -0
  539. package/dist/serve/docs-ui/docs/pyric-rules/index.html +87 -0
  540. package/dist/serve/docs-ui/docs/pyric-rules-compat/index.html +328 -0
  541. package/dist/serve/docs-ui/docs/pyric-rules-compat.md +347 -0
  542. package/dist/serve/docs-ui/docs/pyric-rules-explanation-agent-failure-modes/index.html +50 -0
  543. package/dist/serve/docs-ui/docs/pyric-rules-explanation-agent-failure-modes.md +78 -0
  544. package/dist/serve/docs-ui/docs/pyric-rules-explanation-lint-vs-validate-vs-simulate-vs-test/index.html +79 -0
  545. package/dist/serve/docs-ui/docs/pyric-rules-explanation-lint-vs-validate-vs-simulate-vs-test.md +69 -0
  546. package/dist/serve/docs-ui/docs/pyric-rules-explanation-runtime-budget-and-shared-gates/index.html +67 -0
  547. package/dist/serve/docs-ui/docs/pyric-rules-explanation-runtime-budget-and-shared-gates.md +75 -0
  548. package/dist/serve/docs-ui/docs/pyric-rules-explanation-sentinel-expression-engine/index.html +49 -0
  549. package/dist/serve/docs-ui/docs/pyric-rules-explanation-sentinel-expression-engine.md +74 -0
  550. package/dist/serve/docs-ui/docs/pyric-rules-explanation-simulator-vs-rules-test-api/index.html +95 -0
  551. package/dist/serve/docs-ui/docs/pyric-rules-explanation-simulator-vs-rules-test-api.md +80 -0
  552. package/dist/serve/docs-ui/docs/pyric-rules-explanation-the-2-plus-modules-extension/index.html +58 -0
  553. package/dist/serve/docs-ui/docs/pyric-rules-explanation-the-2-plus-modules-extension.md +79 -0
  554. package/dist/serve/docs-ui/docs/pyric-rules-explanation-value-wrappers-design/index.html +63 -0
  555. package/dist/serve/docs-ui/docs/pyric-rules-explanation-value-wrappers-design.md +84 -0
  556. package/dist/serve/docs-ui/docs/pyric-rules-explanation-why-this-package-exists/index.html +34 -0
  557. package/dist/serve/docs-ui/docs/pyric-rules-explanation-why-this-package-exists.md +43 -0
  558. package/dist/serve/docs-ui/docs/pyric-rules-how-to-compare-rulesets-for-weakening/index.html +55 -0
  559. package/dist/serve/docs-ui/docs/pyric-rules-how-to-compare-rulesets-for-weakening.md +77 -0
  560. package/dist/serve/docs-ui/docs/pyric-rules-how-to-inspect-rules-via-the-ast/index.html +90 -0
  561. package/dist/serve/docs-ui/docs/pyric-rules-how-to-inspect-rules-via-the-ast.md +129 -0
  562. package/dist/serve/docs-ui/docs/pyric-rules-how-to-lint-rules-source/index.html +55 -0
  563. package/dist/serve/docs-ui/docs/pyric-rules-how-to-lint-rules-source.md +84 -0
  564. package/dist/serve/docs-ui/docs/pyric-rules-how-to-pin-request-time/index.html +49 -0
  565. package/dist/serve/docs-ui/docs/pyric-rules-how-to-pin-request-time.md +64 -0
  566. package/dist/serve/docs-ui/docs/pyric-rules-how-to-register-tools-with-an-agent/index.html +61 -0
  567. package/dist/serve/docs-ui/docs/pyric-rules-how-to-register-tools-with-an-agent.md +87 -0
  568. package/dist/serve/docs-ui/docs/pyric-rules-how-to-resolve-module-imports/index.html +93 -0
  569. package/dist/serve/docs-ui/docs/pyric-rules-how-to-resolve-module-imports.md +105 -0
  570. package/dist/serve/docs-ui/docs/pyric-rules-how-to-simulate-rules-locally/index.html +89 -0
  571. package/dist/serve/docs-ui/docs/pyric-rules-how-to-simulate-rules-locally.md +134 -0
  572. package/dist/serve/docs-ui/docs/pyric-rules-how-to-test-rules-against-firebase/index.html +66 -0
  573. package/dist/serve/docs-ui/docs/pyric-rules-how-to-test-rules-against-firebase.md +102 -0
  574. package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +2629 -0
  575. package/dist/serve/docs-ui/docs/pyric-rules-reference-api.md +2213 -0
  576. package/dist/serve/docs-ui/docs/pyric-rules-reference-ast/index.html +85 -0
  577. package/dist/serve/docs-ui/docs/pyric-rules-reference-ast.md +121 -0
  578. package/dist/serve/docs-ui/docs/pyric-rules-reference-conformance-gaps/index.html +277 -0
  579. package/dist/serve/docs-ui/docs/pyric-rules-reference-conformance-gaps.md +360 -0
  580. package/dist/serve/docs-ui/docs/pyric-rules-reference-errors/index.html +76 -0
  581. package/dist/serve/docs-ui/docs/pyric-rules-reference-errors.md +123 -0
  582. package/dist/serve/docs-ui/docs/pyric-rules-reference-lint-rules/index.html +148 -0
  583. package/dist/serve/docs-ui/docs/pyric-rules-reference-lint-rules.md +159 -0
  584. package/dist/serve/docs-ui/docs/pyric-rules-reference-simulator-context/index.html +66 -0
  585. package/dist/serve/docs-ui/docs/pyric-rules-reference-simulator-context.md +104 -0
  586. package/dist/serve/docs-ui/docs/pyric-rules-reference-stdlib-modules/index.html +241 -0
  587. package/dist/serve/docs-ui/docs/pyric-rules-reference-stdlib-modules.md +143 -0
  588. package/dist/serve/docs-ui/docs/pyric-rules-reference-test-case-schema/index.html +94 -0
  589. package/dist/serve/docs-ui/docs/pyric-rules-reference-test-case-schema.md +121 -0
  590. package/dist/serve/docs-ui/docs/pyric-rules-reference-validator-findings/index.html +87 -0
  591. package/dist/serve/docs-ui/docs/pyric-rules-reference-validator-findings.md +117 -0
  592. package/dist/serve/docs-ui/docs/pyric-rules-reference-value-wrappers/index.html +102 -0
  593. package/dist/serve/docs-ui/docs/pyric-rules-reference-value-wrappers.md +152 -0
  594. package/dist/serve/docs-ui/docs/pyric-rules.md +93 -0
  595. package/dist/serve/docs-ui/docs/pyric-sandbox/index.html +67 -0
  596. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +120 -0
  597. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api.md +110 -0
  598. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-every-op-is-a-request/index.html +141 -0
  599. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-every-op-is-a-request.md +161 -0
  600. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-identity-is-a-context/index.html +66 -0
  601. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-identity-is-a-context.md +110 -0
  602. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-internal-adapter-protocol/index.html +71 -0
  603. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-internal-adapter-protocol.md +111 -0
  604. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-listener-re-evaluation/index.html +56 -0
  605. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-listener-re-evaluation.md +82 -0
  606. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-local-backend-vs-firestore-offline/index.html +64 -0
  607. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-local-backend-vs-firestore-offline.md +56 -0
  608. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-why-adapters-are-siblings/index.html +92 -0
  609. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-why-adapters-are-siblings.md +90 -0
  610. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-why-this-package-exists/index.html +69 -0
  611. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-why-this-package-exists.md +57 -0
  612. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +202 -0
  613. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api.md +134 -0
  614. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-multiple-isolated-sandboxes/index.html +63 -0
  615. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-multiple-isolated-sandboxes.md +89 -0
  616. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-observe-events/index.html +159 -0
  617. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-observe-events.md +168 -0
  618. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-pick-an-adapter/index.html +79 -0
  619. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-pick-an-adapter.md +75 -0
  620. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-replay-events/index.html +88 -0
  621. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-replay-events.md +92 -0
  622. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-reset-between-tests/index.html +68 -0
  623. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-reset-between-tests.md +92 -0
  624. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-seed-data-and-rules/index.html +91 -0
  625. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-seed-data-and-rules.md +122 -0
  626. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-switch-users/index.html +50 -0
  627. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-switch-users.md +78 -0
  628. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-use-admin-reads/index.html +54 -0
  629. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-use-admin-reads.md +85 -0
  630. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +5019 -0
  631. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +4060 -0
  632. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-divergences/index.html +125 -0
  633. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-divergences.md +151 -0
  634. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-error-codes/index.html +110 -0
  635. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-error-codes.md +118 -0
  636. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-internal-protocol/index.html +72 -0
  637. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-internal-protocol.md +109 -0
  638. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-sandbox-and-context/index.html +90 -0
  639. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-sandbox-and-context.md +140 -0
  640. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-sandbox-event/index.html +146 -0
  641. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-sandbox-event.md +204 -0
  642. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-snapshot-and-admin/index.html +53 -0
  643. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-snapshot-and-admin.md +89 -0
  644. package/dist/serve/docs-ui/docs/pyric-sandbox-tutorials-01-your-first-sandbox-session/index.html +115 -0
  645. package/dist/serve/docs-ui/docs/pyric-sandbox-tutorials-01-your-first-sandbox-session.md +176 -0
  646. package/dist/serve/docs-ui/docs/pyric-sandbox.md +69 -0
  647. package/dist/serve/docs-ui/docs/pyric-storage/index.html +65 -0
  648. package/dist/serve/docs-ui/docs/pyric-storage-compat/index.html +676 -0
  649. package/dist/serve/docs-ui/docs/pyric-storage-compat.md +699 -0
  650. package/dist/serve/docs-ui/docs/pyric-storage-explanation-implementation-scope/index.html +52 -0
  651. package/dist/serve/docs-ui/docs/pyric-storage-explanation-implementation-scope.md +85 -0
  652. package/dist/serve/docs-ui/docs/pyric-storage-explanation-session-archive-use-case/index.html +58 -0
  653. package/dist/serve/docs-ui/docs/pyric-storage-explanation-session-archive-use-case.md +82 -0
  654. package/dist/serve/docs-ui/docs/pyric-storage-explanation-why-indexeddb/index.html +50 -0
  655. package/dist/serve/docs-ui/docs/pyric-storage-explanation-why-indexeddb.md +82 -0
  656. package/dist/serve/docs-ui/docs/pyric-storage-how-to-enforce-rules/index.html +120 -0
  657. package/dist/serve/docs-ui/docs/pyric-storage-how-to-enforce-rules.md +127 -0
  658. package/dist/serve/docs-ui/docs/pyric-storage-how-to-list-and-delete/index.html +53 -0
  659. package/dist/serve/docs-ui/docs/pyric-storage-how-to-list-and-delete.md +86 -0
  660. package/dist/serve/docs-ui/docs/pyric-storage-how-to-switch-backends/index.html +43 -0
  661. package/dist/serve/docs-ui/docs/pyric-storage-how-to-switch-backends.md +65 -0
  662. package/dist/serve/docs-ui/docs/pyric-storage-how-to-test-rule-expressions/index.html +70 -0
  663. package/dist/serve/docs-ui/docs/pyric-storage-how-to-test-rule-expressions.md +88 -0
  664. package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +2120 -0
  665. package/dist/serve/docs-ui/docs/pyric-storage-reference-api.md +1687 -0
  666. package/dist/serve/docs-ui/docs/pyric-storage-reference-conformance-gaps/index.html +137 -0
  667. package/dist/serve/docs-ui/docs/pyric-storage-reference-conformance-gaps.md +171 -0
  668. package/dist/serve/docs-ui/docs/pyric-storage-reference-error-codes/index.html +37 -0
  669. package/dist/serve/docs-ui/docs/pyric-storage-reference-error-codes.md +57 -0
  670. package/dist/serve/docs-ui/docs/pyric-storage-reference-rules-subset/index.html +96 -0
  671. package/dist/serve/docs-ui/docs/pyric-storage-reference-rules-subset.md +134 -0
  672. package/dist/serve/docs-ui/docs/pyric-storage-reference-storage-options/index.html +39 -0
  673. package/dist/serve/docs-ui/docs/pyric-storage-reference-storage-options.md +61 -0
  674. package/dist/serve/docs-ui/docs/pyric-storage.md +67 -0
  675. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +638 -0
  676. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api.md +430 -0
  677. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +813 -0
  678. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api.md +634 -0
  679. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +1708 -0
  680. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api.md +1300 -0
  681. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +177 -0
  682. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api.md +111 -0
  683. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +1542 -0
  684. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api.md +903 -0
  685. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1024 -0
  686. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api.md +640 -0
  687. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +2753 -0
  688. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api.md +2171 -0
  689. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +1005 -0
  690. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api.md +693 -0
  691. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +1388 -0
  692. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api.md +1340 -0
  693. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +131 -0
  694. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api.md +76 -0
  695. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +648 -0
  696. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api.md +508 -0
  697. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1457 -0
  698. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api.md +1029 -0
  699. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +2476 -0
  700. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api.md +1919 -0
  701. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1241 -0
  702. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api.md +817 -0
  703. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +2289 -0
  704. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api.md +1489 -0
  705. package/dist/serve/docs-ui/docs/read-a-denial/index.html +40 -0
  706. package/dist/serve/docs-ui/docs/read-a-denial.md +62 -0
  707. package/dist/serve/docs-ui/docs/receive-messages/index.html +28 -0
  708. package/dist/serve/docs-ui/docs/receive-messages.md +43 -0
  709. package/dist/serve/docs-ui/docs/rtdb-rules-in-typescript/index.html +67 -0
  710. package/dist/serve/docs-ui/docs/rtdb-rules-in-typescript.md +101 -0
  711. package/dist/serve/docs-ui/docs/rules-patterns/index.html +68 -0
  712. package/dist/serve/docs-ui/docs/rules-patterns.md +104 -0
  713. package/dist/serve/docs-ui/docs/rules-standard-library/index.html +87 -0
  714. package/dist/serve/docs-ui/docs/rules-standard-library.md +93 -0
  715. package/dist/serve/docs-ui/docs/run-ai-logic-locally/index.html +27 -0
  716. package/dist/serve/docs-ui/docs/run-ai-logic-locally.md +43 -0
  717. package/dist/serve/docs-ui/docs/secure-it-with-rules/index.html +69 -0
  718. package/dist/serve/docs-ui/docs/secure-it-with-rules.md +101 -0
  719. package/dist/serve/docs-ui/docs/see-whats-happening/index.html +73 -0
  720. package/dist/serve/docs-ui/docs/see-whats-happening.md +77 -0
  721. package/dist/serve/docs-ui/docs/set-up-the-project/index.html +14 -0
  722. package/dist/serve/docs-ui/docs/set-up-the-project.md +19 -0
  723. package/dist/serve/docs-ui/docs/set-up-your-agent/index.html +42 -0
  724. package/dist/serve/docs-ui/docs/set-up-your-agent.md +86 -0
  725. package/dist/serve/docs-ui/docs/shape-your-data/index.html +44 -0
  726. package/dist/serve/docs-ui/docs/shape-your-data.md +88 -0
  727. package/dist/serve/docs-ui/docs/ship-to-production/index.html +36 -0
  728. package/dist/serve/docs-ui/docs/ship-to-production.md +80 -0
  729. package/dist/serve/docs-ui/docs/sign-in-and-manage-users/index.html +31 -0
  730. package/dist/serve/docs-ui/docs/sign-in-and-manage-users.md +50 -0
  731. package/dist/serve/docs-ui/docs/simulate-and-lint/index.html +95 -0
  732. package/dist/serve/docs-ui/docs/simulate-and-lint.md +99 -0
  733. package/dist/serve/docs-ui/docs/skills/index.html +20 -0
  734. package/dist/serve/docs-ui/docs/skills.md +34 -0
  735. package/dist/serve/docs-ui/docs/start-building/index.html +32 -0
  736. package/dist/serve/docs-ui/docs/start-building.md +64 -0
  737. package/dist/serve/docs-ui/docs/store-and-query-data/index.html +16 -0
  738. package/dist/serve/docs-ui/docs/store-and-query-data.md +33 -0
  739. package/dist/serve/docs-ui/docs/store-files/index.html +13 -0
  740. package/dist/serve/docs-ui/docs/store-files.md +25 -0
  741. package/dist/serve/docs-ui/docs/sync-realtime-data/index.html +15 -0
  742. package/dist/serve/docs-ui/docs/sync-realtime-data.md +27 -0
  743. package/dist/serve/docs-ui/docs/test-in-node/index.html +74 -0
  744. package/dist/serve/docs-ui/docs/test-in-node.md +100 -0
  745. package/dist/serve/docs-ui/docs/ui/index.html +76 -0
  746. package/dist/serve/docs-ui/docs/ui-auth-authsigninhelper/index.html +194 -0
  747. package/dist/serve/docs-ui/docs/ui-auth-authsigninhelper.md +114 -0
  748. package/dist/serve/docs-ui/docs/ui-auth-authusers/index.html +263 -0
  749. package/dist/serve/docs-ui/docs/ui-auth-authusers.md +183 -0
  750. package/dist/serve/docs-ui/docs/ui-firestore-collectionlist/index.html +92 -0
  751. package/dist/serve/docs-ui/docs/ui-firestore-collectionlist.md +79 -0
  752. package/dist/serve/docs-ui/docs/ui-firestore-deletewithconfirm/index.html +128 -0
  753. package/dist/serve/docs-ui/docs/ui-firestore-deletewithconfirm.md +104 -0
  754. package/dist/serve/docs-ui/docs/ui-firestore-documenteditor/index.html +154 -0
  755. package/dist/serve/docs-ui/docs/ui-firestore-documenteditor.md +117 -0
  756. package/dist/serve/docs-ui/docs/ui-firestore-documentlist/index.html +132 -0
  757. package/dist/serve/docs-ui/docs/ui-firestore-documentlist.md +92 -0
  758. package/dist/serve/docs-ui/docs/ui-firestore-documentpreview/index.html +159 -0
  759. package/dist/serve/docs-ui/docs/ui-firestore-documentpreview.md +84 -0
  760. package/dist/serve/docs-ui/docs/ui-firestore-querybuilder/index.html +104 -0
  761. package/dist/serve/docs-ui/docs/ui-firestore-querybuilder.md +103 -0
  762. package/dist/serve/docs-ui/docs/ui-firestore-referencepicker/index.html +113 -0
  763. package/dist/serve/docs-ui/docs/ui-firestore-referencepicker.md +93 -0
  764. package/dist/serve/docs-ui/docs/ui-primitives-badge/index.html +56 -0
  765. package/dist/serve/docs-ui/docs/ui-primitives-badge.md +40 -0
  766. package/dist/serve/docs-ui/docs/ui-primitives-confirmdialog/index.html +140 -0
  767. package/dist/serve/docs-ui/docs/ui-primitives-confirmdialog.md +108 -0
  768. package/dist/serve/docs-ui/docs/ui-primitives-copybutton/index.html +58 -0
  769. package/dist/serve/docs-ui/docs/ui-primitives-copybutton.md +37 -0
  770. package/dist/serve/docs-ui/docs/ui-primitives-jsonview/index.html +58 -0
  771. package/dist/serve/docs-ui/docs/ui-primitives-jsonview.md +47 -0
  772. package/dist/serve/docs-ui/docs/ui-primitives-segmentedcontrol/index.html +70 -0
  773. package/dist/serve/docs-ui/docs/ui-primitives-segmentedcontrol.md +50 -0
  774. package/dist/serve/docs-ui/docs/ui-primitives-toast/index.html +87 -0
  775. package/dist/serve/docs-ui/docs/ui-primitives-toast.md +82 -0
  776. package/dist/serve/docs-ui/docs/ui-primitives-virtuallist/index.html +85 -0
  777. package/dist/serve/docs-ui/docs/ui-primitives-virtuallist.md +54 -0
  778. package/dist/serve/docs-ui/docs/ui-storage/index.html +150 -0
  779. package/dist/serve/docs-ui/docs/ui-storage-deleteselectionwithconfirm/index.html +126 -0
  780. package/dist/serve/docs-ui/docs/ui-storage-deleteselectionwithconfirm.md +88 -0
  781. package/dist/serve/docs-ui/docs/ui-storage-objectbrowser/index.html +137 -0
  782. package/dist/serve/docs-ui/docs/ui-storage-objectbrowser.md +90 -0
  783. package/dist/serve/docs-ui/docs/ui-storage-objectinspector/index.html +139 -0
  784. package/dist/serve/docs-ui/docs/ui-storage-objectinspector.md +93 -0
  785. package/dist/serve/docs-ui/docs/ui-storage-pathbreadcrumb/index.html +72 -0
  786. package/dist/serve/docs-ui/docs/ui-storage-pathbreadcrumb.md +57 -0
  787. package/dist/serve/docs-ui/docs/ui-storage-rules-aware-affordances/index.html +60 -0
  788. package/dist/serve/docs-ui/docs/ui-storage-rules-aware-affordances.md +52 -0
  789. package/dist/serve/docs-ui/docs/ui-storage-uploaddropzone/index.html +93 -0
  790. package/dist/serve/docs-ui/docs/ui-storage-uploaddropzone.md +78 -0
  791. package/dist/serve/docs-ui/docs/ui-storage-usestoragerulesgate/index.html +144 -0
  792. package/dist/serve/docs-ui/docs/ui-storage-usestoragerulesgate.md +101 -0
  793. package/dist/serve/docs-ui/docs/ui-storage.md +165 -0
  794. package/dist/serve/docs-ui/docs/ui-traffic/index.html +88 -0
  795. package/dist/serve/docs-ui/docs/ui-traffic-ruleheatmap/index.html +79 -0
  796. package/dist/serve/docs-ui/docs/ui-traffic-ruleheatmap.md +60 -0
  797. package/dist/serve/docs-ui/docs/ui-traffic-trafficdetail/index.html +78 -0
  798. package/dist/serve/docs-ui/docs/ui-traffic-trafficdetail.md +59 -0
  799. package/dist/serve/docs-ui/docs/ui-traffic-trafficlog/index.html +118 -0
  800. package/dist/serve/docs-ui/docs/ui-traffic-trafficlog.md +69 -0
  801. package/dist/serve/docs-ui/docs/ui-traffic-trafficstats/index.html +51 -0
  802. package/dist/serve/docs-ui/docs/ui-traffic-trafficstats.md +46 -0
  803. package/dist/serve/docs-ui/docs/ui-traffic.md +102 -0
  804. package/dist/serve/docs-ui/docs/ui.md +90 -0
  805. package/dist/serve/docs-ui/docs/watch-and-review/index.html +25 -0
  806. package/dist/serve/docs-ui/docs/watch-and-review.md +40 -0
  807. package/dist/serve/docs-ui/docs/what-your-agent-can-do/index.html +81 -0
  808. package/dist/serve/docs-ui/docs/what-your-agent-can-do.md +78 -0
  809. package/dist/serve/docs-ui/docs/whats-experimental/index.html +22 -0
  810. package/dist/serve/docs-ui/docs/whats-experimental.md +33 -0
  811. package/dist/serve/docs-ui/docs/whats-possible/index.html +63 -0
  812. package/dist/serve/docs-ui/docs/whats-possible.md +53 -0
  813. package/dist/serve/docs-ui/docs/which-data-service/index.html +8 -0
  814. package/dist/serve/docs-ui/docs/which-data-service.md +11 -0
  815. package/dist/serve/docs-ui/docs/write-a-rules-test-suite/index.html +85 -0
  816. package/dist/serve/docs-ui/docs/write-a-rules-test-suite.md +119 -0
  817. package/dist/serve/docs-ui/index.html +1 -0
  818. package/dist/serve/docs-ui/llms.txt +308 -0
  819. package/dist/serve/entries/ai.d.ts +4 -0
  820. package/dist/serve/entries/ai.d.ts.map +1 -0
  821. package/dist/serve/entries/ai.js +184 -0
  822. package/dist/serve/entries/ai.js.map +1 -0
  823. package/dist/serve/entries/app-backend.d.ts +2 -0
  824. package/dist/serve/entries/app-backend.d.ts.map +1 -0
  825. package/dist/serve/entries/app-backend.js +4 -0
  826. package/dist/serve/entries/app-backend.js.map +1 -0
  827. package/dist/serve/entries/app-client.d.ts +5 -0
  828. package/dist/serve/entries/app-client.d.ts.map +1 -0
  829. package/dist/serve/entries/app-client.js +23 -0
  830. package/dist/serve/entries/app-client.js.map +1 -0
  831. package/dist/serve/entries/app-session-store.d.ts +14 -0
  832. package/dist/serve/entries/app-session-store.d.ts.map +1 -0
  833. package/dist/serve/entries/app-session-store.js +37 -0
  834. package/dist/serve/entries/app-session-store.js.map +1 -0
  835. package/dist/serve/entries/app.d.ts +2 -0
  836. package/dist/serve/entries/app.d.ts.map +1 -0
  837. package/dist/serve/entries/app.js +10 -0
  838. package/dist/serve/entries/app.js.map +1 -0
  839. package/dist/serve/entries/auth-helper-core.d.ts +52 -0
  840. package/dist/serve/entries/auth-helper-core.d.ts.map +1 -0
  841. package/dist/serve/entries/auth-helper-core.js +118 -0
  842. package/dist/serve/entries/auth-helper-core.js.map +1 -0
  843. package/dist/serve/entries/auth-helper-dom.d.ts +14 -0
  844. package/dist/serve/entries/auth-helper-dom.d.ts.map +1 -0
  845. package/dist/serve/entries/auth-helper-dom.js +116 -0
  846. package/dist/serve/entries/auth-helper-dom.js.map +1 -0
  847. package/dist/serve/entries/auth-helper-runtime.d.ts +5 -0
  848. package/dist/serve/entries/auth-helper-runtime.d.ts.map +1 -0
  849. package/dist/serve/entries/auth-helper-runtime.js +11 -0
  850. package/dist/serve/entries/auth-helper-runtime.js.map +1 -0
  851. package/dist/serve/entries/auth.d.ts +77 -0
  852. package/dist/serve/entries/auth.d.ts.map +1 -0
  853. package/dist/serve/entries/auth.js +201 -0
  854. package/dist/serve/entries/auth.js.map +1 -0
  855. package/dist/serve/entries/bridge-url.d.ts +25 -0
  856. package/dist/serve/entries/bridge-url.d.ts.map +1 -0
  857. package/dist/serve/entries/bridge-url.js +30 -0
  858. package/dist/serve/entries/bridge-url.js.map +1 -0
  859. package/dist/serve/entries/database.d.ts +46 -0
  860. package/dist/serve/entries/database.d.ts.map +1 -0
  861. package/dist/serve/entries/database.js +80 -0
  862. package/dist/serve/entries/database.js.map +1 -0
  863. package/dist/serve/entries/firestore.d.ts +104 -0
  864. package/dist/serve/entries/firestore.d.ts.map +1 -0
  865. package/dist/serve/entries/firestore.js +202 -0
  866. package/dist/serve/entries/firestore.js.map +1 -0
  867. package/dist/serve/entries/init.d.ts +2 -0
  868. package/dist/serve/entries/init.d.ts.map +1 -0
  869. package/dist/serve/entries/init.js +40 -0
  870. package/dist/serve/entries/init.js.map +1 -0
  871. package/dist/serve/entries/keepalive.d.ts +20 -0
  872. package/dist/serve/entries/keepalive.d.ts.map +1 -0
  873. package/dist/serve/entries/keepalive.js +38 -0
  874. package/dist/serve/entries/keepalive.js.map +1 -0
  875. package/dist/serve/entries/messaging-sw.d.ts +10 -0
  876. package/dist/serve/entries/messaging-sw.d.ts.map +1 -0
  877. package/dist/serve/entries/messaging-sw.js +36 -0
  878. package/dist/serve/entries/messaging-sw.js.map +1 -0
  879. package/dist/serve/entries/messaging.d.ts +11 -0
  880. package/dist/serve/entries/messaging.d.ts.map +1 -0
  881. package/dist/serve/entries/messaging.js +53 -0
  882. package/dist/serve/entries/messaging.js.map +1 -0
  883. package/dist/serve/entries/runtime.d.ts +46 -0
  884. package/dist/serve/entries/runtime.d.ts.map +1 -0
  885. package/dist/serve/entries/runtime.js +435 -0
  886. package/dist/serve/entries/runtime.js.map +1 -0
  887. package/dist/serve/entries/session-store.d.ts +48 -0
  888. package/dist/serve/entries/session-store.d.ts.map +1 -0
  889. package/dist/serve/entries/session-store.js +82 -0
  890. package/dist/serve/entries/session-store.js.map +1 -0
  891. package/dist/serve/entries/storage.d.ts +25 -0
  892. package/dist/serve/entries/storage.d.ts.map +1 -0
  893. package/dist/serve/entries/storage.js +61 -0
  894. package/dist/serve/entries/storage.js.map +1 -0
  895. package/dist/serve/entries/tab-sync-wiring.d.ts +88 -0
  896. package/dist/serve/entries/tab-sync-wiring.d.ts.map +1 -0
  897. package/dist/serve/entries/tab-sync-wiring.js +217 -0
  898. package/dist/serve/entries/tab-sync-wiring.js.map +1 -0
  899. package/dist/serve/entries/worker-runtime.d.ts +10 -0
  900. package/dist/serve/entries/worker-runtime.d.ts.map +1 -0
  901. package/dist/serve/entries/worker-runtime.js +59 -0
  902. package/dist/serve/entries/worker-runtime.js.map +1 -0
  903. package/dist/serve/init-payload.d.ts +21 -0
  904. package/dist/serve/init-payload.d.ts.map +1 -0
  905. package/dist/serve/init-payload.js +2 -0
  906. package/dist/serve/init-payload.js.map +1 -0
  907. package/dist/serve/namespace.d.ts +73 -0
  908. package/dist/serve/namespace.d.ts.map +1 -0
  909. package/dist/serve/namespace.js +470 -0
  910. package/dist/serve/namespace.js.map +1 -0
  911. package/dist/serve/open-browser.d.ts +23 -0
  912. package/dist/serve/open-browser.d.ts.map +1 -0
  913. package/dist/serve/open-browser.js +58 -0
  914. package/dist/serve/open-browser.js.map +1 -0
  915. package/dist/serve/rules.d.ts +77 -0
  916. package/dist/serve/rules.d.ts.map +1 -0
  917. package/dist/serve/rules.js +202 -0
  918. package/dist/serve/rules.js.map +1 -0
  919. package/dist/serve/sandbox-marker.d.ts +9 -0
  920. package/dist/serve/sandbox-marker.d.ts.map +1 -0
  921. package/dist/serve/sandbox-marker.js +31 -0
  922. package/dist/serve/sandbox-marker.js.map +1 -0
  923. package/dist/serve/server.d.ts +135 -0
  924. package/dist/serve/server.d.ts.map +1 -0
  925. package/dist/serve/server.js +328 -0
  926. package/dist/serve/server.js.map +1 -0
  927. package/dist/serve/standalone-assets.d.ts +67 -0
  928. package/dist/serve/standalone-assets.d.ts.map +1 -0
  929. package/dist/serve/standalone-assets.js +134 -0
  930. package/dist/serve/standalone-assets.js.map +1 -0
  931. package/dist/serve/state-store.d.ts +45 -0
  932. package/dist/serve/state-store.d.ts.map +1 -0
  933. package/dist/serve/state-store.js +120 -0
  934. package/dist/serve/state-store.js.map +1 -0
  935. package/dist/serve/studio/disk-project-store.d.ts +9 -0
  936. package/dist/serve/studio/disk-project-store.d.ts.map +1 -0
  937. package/dist/serve/studio/disk-project-store.js +173 -0
  938. package/dist/serve/studio/disk-project-store.js.map +1 -0
  939. package/dist/serve/studio/disk-workspace.d.ts +14 -0
  940. package/dist/serve/studio/disk-workspace.d.ts.map +1 -0
  941. package/dist/serve/studio/disk-workspace.js +152 -0
  942. package/dist/serve/studio/disk-workspace.js.map +1 -0
  943. package/dist/serve/studio/index.d.ts +10 -0
  944. package/dist/serve/studio/index.d.ts.map +1 -0
  945. package/dist/serve/studio/index.js +9 -0
  946. package/dist/serve/studio/index.js.map +1 -0
  947. package/dist/serve/studio/routes.d.ts +40 -0
  948. package/dist/serve/studio/routes.d.ts.map +1 -0
  949. package/dist/serve/studio/routes.js +182 -0
  950. package/dist/serve/studio/routes.js.map +1 -0
  951. package/dist/serve/studio/store-types.d.ts +45 -0
  952. package/dist/serve/studio/store-types.d.ts.map +1 -0
  953. package/dist/serve/studio/store-types.js +11 -0
  954. package/dist/serve/studio/store-types.js.map +1 -0
  955. package/dist/serve/studio-ui/assets/RulesCodeEditor-BZpD57II.js +23 -0
  956. package/dist/serve/studio-ui/assets/index-Bsa-TQNK.css +1 -0
  957. package/dist/serve/studio-ui/assets/index-DGaVuOhE.js +1211 -0
  958. package/dist/serve/studio-ui/assets/seed-CTmn6YuO.js +2 -0
  959. package/dist/serve/studio-ui/index.html +32 -0
  960. package/dist/serve/studio-ui/pyric-logo.svg +76 -0
  961. package/dist/serve/vite-plugin.d.ts +57 -0
  962. package/dist/serve/vite-plugin.d.ts.map +1 -0
  963. package/dist/serve/vite-plugin.js +658 -0
  964. package/dist/serve/vite-plugin.js.map +1 -0
  965. package/dist/serve/worker/client/admin-firestore.d.ts +18 -0
  966. package/dist/serve/worker/client/admin-firestore.d.ts.map +1 -0
  967. package/dist/serve/worker/client/admin-firestore.js +26 -0
  968. package/dist/serve/worker/client/admin-firestore.js.map +1 -0
  969. package/dist/serve/worker/client/ai.d.ts +31 -0
  970. package/dist/serve/worker/client/ai.d.ts.map +1 -0
  971. package/dist/serve/worker/client/ai.js +89 -0
  972. package/dist/serve/worker/client/ai.js.map +1 -0
  973. package/dist/serve/worker/client/auth.d.ts +168 -0
  974. package/dist/serve/worker/client/auth.d.ts.map +1 -0
  975. package/dist/serve/worker/client/auth.js +322 -0
  976. package/dist/serve/worker/client/auth.js.map +1 -0
  977. package/dist/serve/worker/client/connection.d.ts +80 -0
  978. package/dist/serve/worker/client/connection.d.ts.map +1 -0
  979. package/dist/serve/worker/client/connection.js +148 -0
  980. package/dist/serve/worker/client/connection.js.map +1 -0
  981. package/dist/serve/worker/client/core.d.ts +109 -0
  982. package/dist/serve/worker/client/core.d.ts.map +1 -0
  983. package/dist/serve/worker/client/core.js +248 -0
  984. package/dist/serve/worker/client/core.js.map +1 -0
  985. package/dist/serve/worker/client/disconnect.d.ts +7 -0
  986. package/dist/serve/worker/client/disconnect.d.ts.map +1 -0
  987. package/dist/serve/worker/client/disconnect.js +26 -0
  988. package/dist/serve/worker/client/disconnect.js.map +1 -0
  989. package/dist/serve/worker/client/firestore-reads.d.ts +49 -0
  990. package/dist/serve/worker/client/firestore-reads.d.ts.map +1 -0
  991. package/dist/serve/worker/client/firestore-reads.js +139 -0
  992. package/dist/serve/worker/client/firestore-reads.js.map +1 -0
  993. package/dist/serve/worker/client/firestore-refs.d.ts +54 -0
  994. package/dist/serve/worker/client/firestore-refs.d.ts.map +1 -0
  995. package/dist/serve/worker/client/firestore-refs.js +167 -0
  996. package/dist/serve/worker/client/firestore-refs.js.map +1 -0
  997. package/dist/serve/worker/client/firestore-writes.d.ts +70 -0
  998. package/dist/serve/worker/client/firestore-writes.d.ts.map +1 -0
  999. package/dist/serve/worker/client/firestore-writes.js +169 -0
  1000. package/dist/serve/worker/client/firestore-writes.js.map +1 -0
  1001. package/dist/serve/worker/client/handles.d.ts +73 -0
  1002. package/dist/serve/worker/client/handles.d.ts.map +1 -0
  1003. package/dist/serve/worker/client/handles.js +4 -0
  1004. package/dist/serve/worker/client/handles.js.map +1 -0
  1005. package/dist/serve/worker/client/messaging.d.ts +16 -0
  1006. package/dist/serve/worker/client/messaging.d.ts.map +1 -0
  1007. package/dist/serve/worker/client/messaging.js +60 -0
  1008. package/dist/serve/worker/client/messaging.js.map +1 -0
  1009. package/dist/serve/worker/client/presence.d.ts +43 -0
  1010. package/dist/serve/worker/client/presence.d.ts.map +1 -0
  1011. package/dist/serve/worker/client/presence.js +132 -0
  1012. package/dist/serve/worker/client/presence.js.map +1 -0
  1013. package/dist/serve/worker/client/rtdb.d.ts +36 -0
  1014. package/dist/serve/worker/client/rtdb.d.ts.map +1 -0
  1015. package/dist/serve/worker/client/rtdb.js +241 -0
  1016. package/dist/serve/worker/client/rtdb.js.map +1 -0
  1017. package/dist/serve/worker/client/rules.d.ts +24 -0
  1018. package/dist/serve/worker/client/rules.d.ts.map +1 -0
  1019. package/dist/serve/worker/client/rules.js +44 -0
  1020. package/dist/serve/worker/client/rules.js.map +1 -0
  1021. package/dist/serve/worker/client/service-worker-connection.d.ts +3 -0
  1022. package/dist/serve/worker/client/service-worker-connection.d.ts.map +1 -0
  1023. package/dist/serve/worker/client/service-worker-connection.js +53 -0
  1024. package/dist/serve/worker/client/service-worker-connection.js.map +1 -0
  1025. package/dist/serve/worker/client/snapshots.d.ts +32 -0
  1026. package/dist/serve/worker/client/snapshots.d.ts.map +1 -0
  1027. package/dist/serve/worker/client/snapshots.js +46 -0
  1028. package/dist/serve/worker/client/snapshots.js.map +1 -0
  1029. package/dist/serve/worker/client/storage.d.ts +64 -0
  1030. package/dist/serve/worker/client/storage.d.ts.map +1 -0
  1031. package/dist/serve/worker/client/storage.js +125 -0
  1032. package/dist/serve/worker/client/storage.js.map +1 -0
  1033. package/dist/serve/worker/client/studio.d.ts +32 -0
  1034. package/dist/serve/worker/client/studio.d.ts.map +1 -0
  1035. package/dist/serve/worker/client/studio.js +61 -0
  1036. package/dist/serve/worker/client/studio.js.map +1 -0
  1037. package/dist/serve/worker/client.d.ts +43 -0
  1038. package/dist/serve/worker/client.d.ts.map +1 -0
  1039. package/dist/serve/worker/client.js +44 -0
  1040. package/dist/serve/worker/client.js.map +1 -0
  1041. package/dist/serve/worker/entry.d.ts +40 -0
  1042. package/dist/serve/worker/entry.d.ts.map +1 -0
  1043. package/dist/serve/worker/entry.js +144 -0
  1044. package/dist/serve/worker/entry.js.map +1 -0
  1045. package/dist/serve/worker/host/admin-firestore.d.ts +17 -0
  1046. package/dist/serve/worker/host/admin-firestore.d.ts.map +1 -0
  1047. package/dist/serve/worker/host/admin-firestore.js +92 -0
  1048. package/dist/serve/worker/host/admin-firestore.js.map +1 -0
  1049. package/dist/serve/worker/host/connection.d.ts +40 -0
  1050. package/dist/serve/worker/host/connection.d.ts.map +1 -0
  1051. package/dist/serve/worker/host/connection.js +153 -0
  1052. package/dist/serve/worker/host/connection.js.map +1 -0
  1053. package/dist/serve/worker/host/core.d.ts +159 -0
  1054. package/dist/serve/worker/host/core.d.ts.map +1 -0
  1055. package/dist/serve/worker/host/core.js +296 -0
  1056. package/dist/serve/worker/host/core.js.map +1 -0
  1057. package/dist/serve/worker/host/dispatch.d.ts +34 -0
  1058. package/dist/serve/worker/host/dispatch.d.ts.map +1 -0
  1059. package/dist/serve/worker/host/dispatch.js +267 -0
  1060. package/dist/serve/worker/host/dispatch.js.map +1 -0
  1061. package/dist/serve/worker/host/firestore-reads.d.ts +17 -0
  1062. package/dist/serve/worker/host/firestore-reads.d.ts.map +1 -0
  1063. package/dist/serve/worker/host/firestore-reads.js +108 -0
  1064. package/dist/serve/worker/host/firestore-reads.js.map +1 -0
  1065. package/dist/serve/worker/host/firestore-writes.d.ts +37 -0
  1066. package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -0
  1067. package/dist/serve/worker/host/firestore-writes.js +409 -0
  1068. package/dist/serve/worker/host/firestore-writes.js.map +1 -0
  1069. package/dist/serve/worker/host/presence.d.ts +38 -0
  1070. package/dist/serve/worker/host/presence.d.ts.map +1 -0
  1071. package/dist/serve/worker/host/presence.js +280 -0
  1072. package/dist/serve/worker/host/presence.js.map +1 -0
  1073. package/dist/serve/worker/host/rtdb.d.ts +18 -0
  1074. package/dist/serve/worker/host/rtdb.d.ts.map +1 -0
  1075. package/dist/serve/worker/host/rtdb.js +128 -0
  1076. package/dist/serve/worker/host/rtdb.js.map +1 -0
  1077. package/dist/serve/worker/host/rules.d.ts +17 -0
  1078. package/dist/serve/worker/host/rules.d.ts.map +1 -0
  1079. package/dist/serve/worker/host/rules.js +118 -0
  1080. package/dist/serve/worker/host/rules.js.map +1 -0
  1081. package/dist/serve/worker/host/storage.d.ts +19 -0
  1082. package/dist/serve/worker/host/storage.d.ts.map +1 -0
  1083. package/dist/serve/worker/host/storage.js +260 -0
  1084. package/dist/serve/worker/host/storage.js.map +1 -0
  1085. package/dist/serve/worker/host/studio.d.ts +13 -0
  1086. package/dist/serve/worker/host/studio.d.ts.map +1 -0
  1087. package/dist/serve/worker/host/studio.js +30 -0
  1088. package/dist/serve/worker/host/studio.js.map +1 -0
  1089. package/dist/serve/worker/host/subscriptions.d.ts +21 -0
  1090. package/dist/serve/worker/host/subscriptions.d.ts.map +1 -0
  1091. package/dist/serve/worker/host/subscriptions.js +162 -0
  1092. package/dist/serve/worker/host/subscriptions.js.map +1 -0
  1093. package/dist/serve/worker/host-ai.d.ts +59 -0
  1094. package/dist/serve/worker/host-ai.d.ts.map +1 -0
  1095. package/dist/serve/worker/host-ai.js +158 -0
  1096. package/dist/serve/worker/host-ai.js.map +1 -0
  1097. package/dist/serve/worker/host-auth.d.ts +47 -0
  1098. package/dist/serve/worker/host-auth.d.ts.map +1 -0
  1099. package/dist/serve/worker/host-auth.js +464 -0
  1100. package/dist/serve/worker/host-auth.js.map +1 -0
  1101. package/dist/serve/worker/host-context.d.ts +220 -0
  1102. package/dist/serve/worker/host-context.d.ts.map +1 -0
  1103. package/dist/serve/worker/host-context.js +45 -0
  1104. package/dist/serve/worker/host-context.js.map +1 -0
  1105. package/dist/serve/worker/host-events.d.ts +21 -0
  1106. package/dist/serve/worker/host-events.d.ts.map +1 -0
  1107. package/dist/serve/worker/host-events.js +87 -0
  1108. package/dist/serve/worker/host-events.js.map +1 -0
  1109. package/dist/serve/worker/host-messaging.d.ts +60 -0
  1110. package/dist/serve/worker/host-messaging.d.ts.map +1 -0
  1111. package/dist/serve/worker/host-messaging.js +226 -0
  1112. package/dist/serve/worker/host-messaging.js.map +1 -0
  1113. package/dist/serve/worker/host.d.ts +36 -0
  1114. package/dist/serve/worker/host.d.ts.map +1 -0
  1115. package/dist/serve/worker/host.js +39 -0
  1116. package/dist/serve/worker/host.js.map +1 -0
  1117. package/dist/serve/worker/index.d.ts +22 -0
  1118. package/dist/serve/worker/index.d.ts.map +1 -0
  1119. package/dist/serve/worker/index.js +61 -0
  1120. package/dist/serve/worker/index.js.map +1 -0
  1121. package/dist/serve/worker/presence-timing.d.ts +14 -0
  1122. package/dist/serve/worker/presence-timing.d.ts.map +1 -0
  1123. package/dist/serve/worker/presence-timing.js +14 -0
  1124. package/dist/serve/worker/presence-timing.js.map +1 -0
  1125. package/dist/serve/worker/protocol.d.ts +1150 -0
  1126. package/dist/serve/worker/protocol.d.ts.map +1 -0
  1127. package/dist/serve/worker/protocol.js +228 -0
  1128. package/dist/serve/worker/protocol.js.map +1 -0
  1129. package/dist/serve/worker/serve-init.d.ts +201 -0
  1130. package/dist/serve/worker/serve-init.d.ts.map +1 -0
  1131. package/dist/serve/worker/serve-init.js +579 -0
  1132. package/dist/serve/worker/serve-init.js.map +1 -0
  1133. package/dist/serve/worker/service-worker-channel.d.ts +23 -0
  1134. package/dist/serve/worker/service-worker-channel.d.ts.map +1 -0
  1135. package/dist/serve/worker/service-worker-channel.js +9 -0
  1136. package/dist/serve/worker/service-worker-channel.js.map +1 -0
  1137. package/dist/serve/worker/service-worker-relay.d.ts +22 -0
  1138. package/dist/serve/worker/service-worker-relay.d.ts.map +1 -0
  1139. package/dist/serve/worker/service-worker-relay.js +63 -0
  1140. package/dist/serve/worker/service-worker-relay.js.map +1 -0
  1141. package/dist/serve/writer-lock.d.ts +32 -0
  1142. package/dist/serve/writer-lock.d.ts.map +1 -0
  1143. package/dist/serve/writer-lock.js +43 -0
  1144. package/dist/serve/writer-lock.js.map +1 -0
  1145. package/dist/verify/cases.d.ts +37 -0
  1146. package/dist/verify/cases.d.ts.map +1 -0
  1147. package/dist/verify/cases.js +173 -0
  1148. package/dist/verify/cases.js.map +1 -0
  1149. package/dist/verify/fixture.d.ts +100 -0
  1150. package/dist/verify/fixture.d.ts.map +1 -0
  1151. package/dist/verify/fixture.js +156 -0
  1152. package/dist/verify/fixture.js.map +1 -0
  1153. package/dist/verify/index.d.ts +102 -0
  1154. package/dist/verify/index.d.ts.map +1 -0
  1155. package/dist/verify/index.js +318 -0
  1156. package/dist/verify/index.js.map +1 -0
  1157. package/dist/verify/tools.d.ts +7 -0
  1158. package/dist/verify/tools.d.ts.map +1 -0
  1159. package/dist/verify/tools.js +83 -0
  1160. package/dist/verify/tools.js.map +1 -0
  1161. package/dist/version/compat-target.d.ts +18 -0
  1162. package/dist/version/compat-target.d.ts.map +1 -0
  1163. package/dist/version/compat-target.js +18 -0
  1164. package/dist/version/compat-target.js.map +1 -0
  1165. package/dist/vite.d.ts +12 -0
  1166. package/dist/vite.d.ts.map +1 -0
  1167. package/dist/vite.js +16 -0
  1168. package/dist/vite.js.map +1 -0
  1169. package/package.json +133 -0
@@ -0,0 +1,847 @@
1
+ <!-- Generated from packages/conformance/registry/*.ts. Do not edit by hand; run bun run compat:generate. -->
2
+
3
+ # `pyric/auth` compatibility matrix
4
+
5
+ <div class="compat-stat">
6
+ <p class="compat-stat-surface"><strong>Public surface:</strong> runtime 82.4% (70/85) <span aria-hidden="true">·</span> types 39.1% (25/64)</p>
7
+ <p class="compat-stat-figure">
8
+ <span class="compat-stat-pct">81.8%</span>
9
+ <span class="compat-stat-label">of tracked behaviors conform</span>
10
+ </p>
11
+ <p class="compat-stat-denom">99 of 121 tracked behaviors</p>
12
+ <div class="compat-stat-bar" role="img" aria-label="Behavior distribution: 99 conform, 16 documented divergences, 0 bugs, 5 unsupported, 1 unverified.">
13
+ <span class="compat-stat-seg" data-status="ok" style="flex-grow: 99" aria-hidden="true"></span>
14
+ <span class="compat-stat-seg" data-status="diverged" style="flex-grow: 16" aria-hidden="true"></span>
15
+ <span class="compat-stat-seg" data-status="unsupported" style="flex-grow: 5" aria-hidden="true"></span>
16
+ <span class="compat-stat-seg" data-status="unverified" style="flex-grow: 1" aria-hidden="true"></span>
17
+ </div>
18
+ <ul class="compat-stat-key" aria-label="Behavior state counts">
19
+ <li class="compat-stat-item"><span class="compat-dot" data-status="ok" aria-hidden="true"></span><span><strong>99</strong> conform</span></li>
20
+ <li class="compat-stat-item"><span class="compat-dot" data-status="diverged" aria-hidden="true"></span><span><strong>16</strong> documented divergences</span></li>
21
+ <li class="compat-stat-item"><span class="compat-dot" data-status="bug" aria-hidden="true"></span><span><strong>0</strong> bugs</span></li>
22
+ <li class="compat-stat-item"><span class="compat-dot" data-status="unsupported" aria-hidden="true"></span><span><strong>5</strong> unsupported</span></li>
23
+ <li class="compat-stat-item"><span class="compat-dot" data-status="unverified" aria-hidden="true"></span><span><strong>1</strong> unverified</span></li>
24
+ </ul>
25
+ <p class="compat-stat-note">Public surface measures whether exports exist. Fidelity measures whether tracked behavior matches production.</p>
26
+ </div>
27
+ [Read how the axes differ.](../pyric-conformance-scores/)
28
+
29
+ The single readable contract for "what this shim guarantees vs the
30
+ production `firebase/auth` SDK."
31
+
32
+ See the design rationale for the methodology (vocabulary
33
+ of conformance / oracle / matrix; how to add rows; how the runner
34
+ attributes failures).
35
+
36
+ ## Status legend
37
+
38
+ <div class="compat-key">
39
+ <span class="compat-key-item"><span class="compat-dot" data-status="ok"></span><strong>Conforming</strong> — sandbox matches prod, locked by a passing probe</span>
40
+ <span class="compat-key-item"><span class="compat-dot" data-status="diverged"></span><strong>Diverged (documented)</strong> — intentional difference with a written reason</span>
41
+ <span class="compat-key-item"><span class="compat-dot" data-status="bug"></span><strong>Bug</strong> — should match prod but doesn't; failing probe pins it</span>
42
+ <span class="compat-key-item"><span class="compat-dot" data-status="unsupported"></span><strong>Unsupported</strong> — not implemented yet (deliberately or pending)</span>
43
+ <span class="compat-key-item"><span class="compat-dot" data-status="unverified"></span><strong>Unverified</strong> — claim from docs that we haven't yet observed prod-side</span>
44
+ </div>
45
+
46
+ Probe references: `playground:<name>` means a fixture under
47
+ `packages/playground/scripts/fixtures/<name>.tsx`. `unit:<file>`
48
+ means a Bun test in `packages/auth/test/<file>`.
49
+
50
+ ---
51
+
52
+ ## `getAuth(target)` — initializer
53
+
54
+ <div class="compat-list">
55
+ <details class="compat-row" data-status="ok">
56
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAuth(target)</code><span class="compat-sub"><span class="compat-behavior">Returns a stable <code>Auth</code> handle for repeated calls with the same input</span></span></span></summary>
57
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-anonymous.test.ts</code> + canonical Node register child (<code>register-child.test.ts</code>)</div></div>
58
+ </details>
59
+ <details class="compat-row" data-status="ok">
60
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAuth(target)</code><span class="compat-sub"><span class="compat-behavior"><code>getAuth(sandbox)</code> dispatches to the sandbox backend</span></span></span></summary>
61
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-anonymous.test.ts</code></div></div>
62
+ </details>
63
+ <details class="compat-row" data-status="unverified">
64
+ <summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAuth(target)</code><span class="compat-sub"><span class="compat-behavior">Without sandbox package swapping, canonical <code>firebase/auth</code> imports remain Firebase and never enter this mirror</span></span></span></summary>
65
+ <div class="compat-evidence"><div class="compat-probe">Direct mirror rejection is locked by <code>unit:package-resolution.test.ts</code>; an unswapped production-resolution observation is still needed</div></div>
66
+ </details>
67
+ <details class="compat-row" data-status="ok">
68
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAuth(target)</code><span class="compat-sub"><span class="compat-behavior">After package swapping, bare <code>getAuth()</code> resolves the registered default sandbox app; without swapping, Firebase retains its <code>app/no-app</code> behavior when no default app exists</span></span></span></summary>
69
+ <div class="compat-evidence"><div class="compat-probe">canonical Node register child (<code>register-child.test.ts</code>) + oracle: <code>packages/conformance/observations/auth/auth-bare-getauth-no-default-app.json</code> (<code>code: 'app/no-app'</code> against blockingfun, fb-js-sdk 12.13.0 — confirms unswapped Firebase behavior)</div>
70
+ <div class="compat-note">(wrap)</div></div>
71
+ </details>
72
+ <details class="compat-row" data-status="ok">
73
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAuth(target)</code><span class="compat-sub"><span class="compat-behavior"><code>auth.currentUser</code> is a live getter, not a snapshot — reads through to the backend on every access</span></span></span></summary>
74
+ <div class="compat-evidence"><div class="compat-probe">implicit in <code>unit:sandbox-anonymous.test.ts</code></div></div>
75
+ </details>
76
+ </div>
77
+
78
+ ## Multi-app Auth session topology
79
+
80
+ <div class="compat-list">
81
+ <details class="compat-row" data-status="ok">
82
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAuth(app)</code><span class="compat-sub"><span class="compat-behavior">Equal-config Firebase apps have independent active Auth sessions; signing in or out on one app does not change another app's currentUser</span></span></span></summary>
83
+ <div class="compat-evidence"><div class="compat-probe">oracle: <code>app-production-multi-app-topology</code> (firebase 12.13.0, real Chromium against production) + twin replay: <code>packages/pyric/test/app/production-multi-app-oracle.test.ts</code></div></div>
84
+ </details>
85
+ <details class="compat-row" data-status="ok">
86
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">browserLocalPersistence across same-named app instances in sibling tabs</code><span class="compat-sub"><span class="compat-behavior">After the sibling receives the LOCAL persistence event, same-named Firebase app instances in sibling tabs keep independent active Auth sessions; an anonymous sign-in in one tab does not update the sibling tab's currentUser during the bounded post-signal observation window</span></span></span></summary>
87
+ <div class="compat-evidence"><div class="compat-probe">oracle: <code>app-production-cross-tab-auth-persistence</code> (firebase 12.13.0, two real Chromium pages in one browser context) + served twin: <code>packages/cli/test/e2e/app-multi-app.pw.ts</code></div></div>
88
+ </details>
89
+ </div>
90
+
91
+ ## `signInAnonymously(auth)` — anonymous
92
+
93
+ <div class="compat-list">
94
+ <details class="compat-row" data-status="ok">
95
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub"><span class="compat-behavior">Returns <code>UserCredential</code> with <code>providerId: null</code>, <code>operationType: 'signIn'</code>, and a <code>User</code> with <code>isAnonymous: true</code>, <code>email: null</code>, <code>displayName: null</code></span></span></span></summary>
96
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-anonymous.test.ts</code>, <code>playground:auth-anonymous</code> (bundled) + <code>playground:auth-row-6-anon-credential-shape</code> (one-claim) + oracle: <code>packages/conformance/observations/auth/auth-anonymous-credential-providerid.json</code> (<code>providerId: null</code> against blockingfun, fb-js-sdk 12.13.0). Prior matrix language said <code>providerId: 'anonymous'</code>; corrected after empirical observation. Sandbox aligned to prod in the same commit.</div></div>
97
+ </details>
98
+ <details class="compat-row" data-status="diverged">
99
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub"><span class="compat-behavior">Auto-generates a uid for fresh sign-ins (sandbox format: <code>anonymous-{N}</code>)</span></span></span></summary>
100
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-anonymous.test.ts</code> — prod uids are 28-char base64-ish; sandbox uses a readable counter for debuggability</div>
101
+ <div class="compat-note">format</div></div>
102
+ </details>
103
+ <details class="compat-row" data-status="ok">
104
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub"><span class="compat-behavior">If an anonymous user is already signed in, returns the SAME user (no fresh uid mint)</span></span></span></summary>
105
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-anonymous.test.ts</code> ("idempotent while signed in") — fix from #399</div></div>
106
+ </details>
107
+ <details class="compat-row" data-status="ok">
108
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub"><span class="compat-behavior">After <code>signOut</code>, a subsequent <code>signInAnonymously</code> mints a fresh uid</span></span></span></summary>
109
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-anonymous.test.ts</code>, <code>playground:auth-anonymous</code> (bundled) + <code>playground:auth-row-9-anon-fresh-uid-after-signout</code> (one-claim)</div></div>
110
+ </details>
111
+ <details class="compat-row" data-status="ok">
112
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub"><span class="compat-behavior">Fires <code>onAuthStateChanged</code> exactly once per state transition (no same-value double-fire)</span></span></span></summary>
113
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code>, <code>playground:auth-anonymous</code> (bundled) + <code>playground:auth-row-10-onauthstatechanged-one-per-transition</code> (one-claim) — fix from #399 + oracle: <code>packages/conformance/observations/auth/auth-row-10-onauthstatechanged-one-per-transition.json</code> (against blockingfun, fb-js-sdk 12.13.0: subscribe → 1 initial null fire → <code>signInAnonymously</code> → +1 → <code>signOut</code> → +1 → <code>signInAnonymously</code> → +1. <code>eachTransitionFiredExactlyOnce: true</code> — every transition produces exactly one fire)</div></div>
114
+ </details>
115
+ <details class="compat-row" data-status="ok">
116
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub"><span class="compat-behavior">Writes through to <code>sandbox.currentUser</code> so rules engines see <code>request.auth.uid</code></span></span></span></summary>
117
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-anonymous.test.ts</code>, <code>playground:auth-anonymous</code> (bundled) + <code>playground:auth-row-11-anon-uid-visible-to-rules</code> (one-claim)</div></div>
118
+ </details>
119
+ <details class="compat-row" data-status="diverged">
120
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub"><span class="compat-behavior">Anonymous users persist across page reload via configured <code>Persistence</code> (prod only — sandbox has no persistence layer)</span></span></span></summary>
121
+ <div class="compat-evidence"><div class="compat-probe">divergence: sandbox memory only; within one tab the user persists, across reload they don't</div></div>
122
+ </details>
123
+ </div>
124
+
125
+ ## `signInWithEmailAndPassword(auth, email, password)` — password
126
+
127
+ <div class="compat-list">
128
+ <details class="compat-row" data-status="ok">
129
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Returns <code>UserCredential</code> with <code>providerId: null</code> (NOT <code>'password'</code> — only OAuth/phone responses carry a providerId; upstream <code>providerIdForResponse</code> returns null for email/password), <code>operationType: 'signIn'</code>, and a <code>User</code> with the stored uid + email</span></span></span></summary>
130
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-password.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-createUser-operationType.json</code> (<code>providerId: null</code> against blockingfun, fb-js-sdk 12.13.0). Prior matrix language said <code>providerId: 'password'</code>; corrected after the oracle contradicted it (AUTH-B2).</div></div>
131
+ </details>
132
+ <details class="compat-row" data-status="ok">
133
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Throws <code>auth/user-not-found</code> when the email isn't seeded / hasn't been created</span></span></span></summary>
134
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-password.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-user-not-found-error-code.json</code> (<code>code: 'auth/user-not-found'</code> against blockingfun, fb-js-sdk 12.13.0; matches sandbox)</div></div>
135
+ </details>
136
+ <details class="compat-row" data-status="ok">
137
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Throws <code>auth/wrong-password</code> when the password doesn't match</span></span></span></summary>
138
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-password.test.ts</code>, <code>playground:auth-email-password</code> (bundled) + <code>playground:auth-row-15-wrong-password-rejects</code> (one-claim) + oracle: <code>packages/conformance/observations/auth/auth-wrong-password-error-code.json</code> (<code>code: 'auth/wrong-password'</code> against blockingfun, fb-js-sdk 12.13.0; matches sandbox)</div></div>
139
+ </details>
140
+ <details class="compat-row" data-status="diverged">
141
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">An EMPTY password throws <code>auth/missing-password</code> (message "A non-empty password must be provided"), fired before the user-DB lookup so it can't be used to enumerate seeded emails. Upstream maps the <code>MISSING_PASSWORD</code> server error (<code>core/errors.ts:92,282,563</code>). ⚠ best-known semantics — message text not yet captured against a live project (STOP-flagged for an oracle pass; the <code>.code</code> is the load-bearing part).</span></span></span></summary>
142
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-cluster-b9-b12.test.ts</code> (locks AUTH-B11)</div></div>
143
+ </details>
144
+ <details class="compat-row" data-status="ok">
145
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Re-signing in after <code>signOut</code> returns the <strong>same</strong> uid (passwords persist within the sandbox lifetime)</span></span></span></summary>
146
+ <div class="compat-evidence"><div class="compat-probe"><code>playground:auth-email-password</code> (bundled) + <code>playground:auth-row-16-resignin-same-uid</code> (one-claim)</div></div>
147
+ </details>
148
+ <details class="compat-row" data-status="ok">
149
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Fires <code>onAuthStateChanged</code> with the new user once</span></span></span></summary>
150
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-17-signin-email-password-fires-once.json</code> (against blockingfun, fb-js-sdk 12.13.0: createUser → signOut → subscribe (1 initial null fire) → <code>signInWithEmailAndPassword</code> → <code>firesForSignIn: 1</code> with the signed-in uid, <code>lastFireUidMatches: true</code>)</div></div>
151
+ </details>
152
+ <details class="compat-row" data-status="ok">
153
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Email validation (RFC 5322ish) — rejects empty, missing <code>@</code>, missing local-part, missing domain with <code>auth/invalid-email</code>. Runs on both <code>signInWithEmailAndPassword</code> and <code>createUserWithEmailAndPassword</code> before any user-DB lookup, so consumers shipping malformed input see the same error sandbox vs prod.</span></span></span></summary>
154
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-password.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-18-invalid-email-error-code.json</code> (<code>code: 'auth/invalid-email'</code>, message <code>Firebase: Error (auth/invalid-email).</code> against blockingfun, fb-js-sdk 12.13.0)</div></div>
155
+ </details>
156
+ <details class="compat-row" data-status="ok">
157
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Password strength requirements — rejects passwords shorter than 6 chars with <code>auth/weak-password</code> on <code>createUserWithEmailAndPassword</code>. Strength is NOT enforced on <code>signInWithEmailAndPassword</code> so previously-seeded weak passwords still let the user in (matches prod's separation of registration vs sign-in).</span></span></span></summary>
158
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-password.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-19-weak-password-error-code.json</code> (<code>code: 'auth/weak-password'</code>, message <code>Firebase: Password should be at least 6 characters (auth/weak-password).</code> against blockingfun, fb-js-sdk 12.13.0; matrix language "≥6 chars per prod default" empirically confirmed)</div></div>
159
+ </details>
160
+ <details class="compat-row" data-status="ok">
161
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Disabled accounts (<code>sandbox.updateUser(…, {disabled: true})</code>) reject sign-in with <code>auth/user-disabled</code> and prod's documented message (<code>The user account has been disabled by an administrator.</code>). Sandbox checks disabled BEFORE the password compare (anti-probing); the exact prod ordering of disabled-vs-wrong-password needs an oracle capture</span></span></span></summary>
162
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-user-admin.test.ts</code> ("disabled users")</div>
163
+ <div class="compat-note">code / ? ordering</div></div>
164
+ </details>
165
+ </div>
166
+
167
+ ## `createUserWithEmailAndPassword(auth, email, password)` — register
168
+
169
+ <div class="compat-list">
170
+ <details class="compat-row" data-status="ok">
171
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">createUserWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Creates a new user, signs them in automatically (currentUser becomes the new user)</span></span></span></summary>
172
+ <div class="compat-evidence"><div class="compat-probe"><code>playground:auth-email-password</code> (bundled) + <code>playground:auth-row-20-create-user-auto-signs-in</code> (one-claim), <code>unit:sandbox-email-password.test.ts</code></div></div>
173
+ </details>
174
+ <details class="compat-row" data-status="ok">
175
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">createUserWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Returns <code>UserCredential</code> with <code>operationType: 'signIn'</code> (NOT <code>'register'</code> — matches prod)</span></span></span></summary>
176
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-password.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-createUser-operationType.json</code> (<code>operationType: 'signIn'</code> against blockingfun, fb-js-sdk 12.13.0; matches sandbox)</div></div>
177
+ </details>
178
+ <details class="compat-row" data-status="ok">
179
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">createUserWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Throws <code>auth/email-already-in-use</code> when the email is already registered</span></span></span></summary>
180
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-password.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-email-already-in-use-error-code.json</code> (<code>code: 'auth/email-already-in-use'</code> against blockingfun, fb-js-sdk 12.13.0; matches sandbox)</div></div>
181
+ </details>
182
+ <details class="compat-row" data-status="ok">
183
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">createUserWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">The created user has <code>isAnonymous: false</code>, <code>email: &lt;input&gt;</code>, <code>displayName: null</code></span></span></span></summary>
184
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-password.test.ts</code> + <code>playground:auth-row-23-create-user-shape</code> (one-claim)</div></div>
185
+ </details>
186
+ <details class="compat-row" data-status="ok">
187
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">createUserWithEmailAndPassword(auth, email, password)</code><span class="compat-sub"><span class="compat-behavior">Fires <code>onAuthStateChanged</code> with the new user once</span></span></span></summary>
188
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-24-createuser-fires-once.json</code> (against blockingfun, fb-js-sdk 12.13.0: subscribe (1 initial null fire) → <code>createUserWithEmailAndPassword</code> → <code>firesForCreate: 1</code> with the newly-created uid, <code>lastFireUidMatches: true</code>)</div></div>
189
+ </details>
190
+ </div>
191
+
192
+ ## `signOut(auth)`
193
+
194
+ <div class="compat-list">
195
+ <details class="compat-row" data-status="ok">
196
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signOut(auth)</code><span class="compat-sub"><span class="compat-behavior">Sets <code>currentUser</code> to <code>null</code> synchronously after resolution</span></span></span></summary>
197
+ <div class="compat-evidence"><div class="compat-probe"><code>playground:auth-anonymous</code> (bundled) + <code>playground:auth-row-25-signout-currentuser-null</code> (one-claim), <code>unit:sandbox-anonymous.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-25-signout-currentuser-null-sync.json</code> (<code>currentUserIsNullSync: true</code> against blockingfun, fb-js-sdk 12.13.0 — <code>auth.currentUser</code> read in the synchronous continuation immediately after <code>await signOut(auth)</code> is already <code>null</code>, with no microtask/macrotask required to settle)</div></div>
198
+ </details>
199
+ <details class="compat-row" data-status="ok">
200
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signOut(auth)</code><span class="compat-sub"><span class="compat-behavior">Fires <code>onAuthStateChanged</code> with <code>null</code> exactly once</span></span></span></summary>
201
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code>, <code>playground:auth-anonymous</code> (bundled) + <code>playground:auth-row-26-signout-fires-null-once</code> (one-claim) + oracle: <code>packages/conformance/observations/auth/auth-row-26-signout-fires-null-once.json</code> (against blockingfun, fb-js-sdk 12.13.0: subscribe (1 initial null fire) → <code>signInAnonymously</code> → <code>signOut</code> → <code>firesForSignOut: 1</code> with <code>lastFireUidWasNull: true</code>)</div></div>
202
+ </details>
203
+ <details class="compat-row" data-status="ok">
204
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signOut(auth)</code><span class="compat-sub"><span class="compat-behavior">Idempotent — <code>signOut</code> on already-signed-out user is a no-op (no listener fire)</span></span></span></summary>
205
+ <div class="compat-evidence"><div class="compat-probe"><code>playground:auth-signout-idempotent</code> + oracle-confirmed: <code>packages/conformance/observations/auth/auth-signout-idempotent.json</code> (<code>threw: false, redundantSignOutFiredListener: false</code> against blockingfun)</div></div>
206
+ </details>
207
+ <details class="compat-row" data-status="diverged">
208
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">signOut(auth)</code><span class="compat-sub"><span class="compat-behavior">Clears the active session's persistence in prod; sandbox has no persistence</span></span></span></summary>
209
+ <div class="compat-evidence"><div class="compat-probe">divergence: same memory-only constraint as the anonymous persistence row</div></div>
210
+ </details>
211
+ </div>
212
+
213
+ ## `onAuthStateChanged(auth, observer)`
214
+
215
+ <div class="compat-list">
216
+ <details class="compat-row" data-status="ok">
217
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Fires immediately on subscribe with the current value (microtask-deferred)</span></span></span></summary>
218
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-29-onauthstatechanged-initial-fire-timing.json</code> (<code>firedSynchronously: 0, firedAfterMicrotask: 1</code> against blockingfun, fb-js-sdk 12.13.0 — initial fire does NOT arrive in the synchronous tick of <code>onAuthStateChanged(...)</code>; it lands after the first microtask flush)</div></div>
219
+ </details>
220
+ <details class="compat-row" data-status="ok">
221
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Fires on every subsequent identity change</span></span></span></summary>
222
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code>, <code>playground:auth-anonymous</code> + oracle: <code>packages/conformance/observations/auth/auth-row-30-onauthstatechanged-fires-on-every-transition.json</code> (against blockingfun, fb-js-sdk 12.13.0: subscribe → <code>signIn</code> → <code>signOut</code> → <code>signIn</code> → <code>signOut</code>, each of the 4 transitions produced exactly 1 fire; <code>eachTransitionFiredExactlyOnce: true</code>)</div></div>
223
+ </details>
224
+ <details class="compat-row" data-status="ok">
225
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Dedups by uid: a same-uid update (profile-shape change, or a same-uid re-sign-in) does NOT re-fire <code>onAuthStateChanged</code> — only an actual uid change does. Mirrors upstream <code>notifyAuthListeners</code>'s <code>lastNotifiedUid</code> gate (<code>auth_impl.ts:718-723</code>). (<code>onIdTokenChanged</code> still fires on those same-uid updates — see row 38a.)</span></span></span></summary>
226
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-uid-dedup.test.ts</code> (locks AUTH-B7 / B8)</div></div>
227
+ </details>
228
+ <details class="compat-row" data-status="ok">
229
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior"><strong>No duplicate fire</strong> when subscribe is followed by a synchronous <code>setCurrentUser</code> — dedup ensures observer sees the new value once, not twice. Sandbox-only concern: prod has no synchronous state-change API, so the dedup window can't be exercised against the cloud SDK; subscribe-then-async-signIn naturally fires twice (initial + new value) because the microtask between them flushes the initial fire</span></span></span></summary>
230
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> (regression test from #399), <code>playground:auth-anonymous</code> + oracle baseline: <code>packages/conformance/observations/auth/auth-row-31-onauthstatechanged-no-dup-on-sync-transition.json</code> (against blockingfun, fb-js-sdk 12.13.0: subscribe + async <code>signInAnonymously</code> in the same tick produces <code>totalFires: 2</code> — <code>{uid: null, ts: 0}</code> then <code>{uid: &lt;user&gt;, ts: ~400ms}</code>. Confirms prod cannot exhibit the same-tick race; the dedup behavior remains a sandbox-only property)</div></div>
231
+ </details>
232
+ <details class="compat-row" data-status="ok">
233
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Returned <code>Unsubscribe</code> removes the observer; subsequent state changes do NOT fire it</span></span></span></summary>
234
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code>, <code>playground:auth-listener-unsub</code> (bundled) + <code>playground:auth-row-32-unsubscribe-stops-fires</code> (one-claim) + oracle: <code>packages/conformance/observations/auth/auth-row-32-unsubscribe-stops-fires.json</code> (against blockingfun, fb-js-sdk 12.13.0: subscribe → 1 fire on <code>signInAnonymously</code> → call <code>unsub()</code> → <code>signOut</code> + <code>signInAnonymously</code> + <code>signOut</code> produce zero further fires; <code>postUnsubFires: 0, unsubscribeStoppedFires: true</code>)</div></div>
235
+ </details>
236
+ <details class="compat-row" data-status="ok">
237
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Multiple subscribers all fire on each change</span></span></span></summary>
238
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-33-multiple-subscribers-all-fire.json</code> (against blockingfun, fb-js-sdk 12.13.0: two subscribers registered back-to-back each see 1 initial null fire, +1 on <code>signInAnonymously</code>, +1 on <code>signOut</code>; <code>bothFiredOnSignIn: true, bothFiredOnSignOut: true</code>)</div></div>
239
+ </details>
240
+ <details class="compat-row" data-status="ok">
241
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Registry is array-backed (matches upstream <code>util/subscribe.ts</code>): the SAME observer fn registered N times produces N independent registrations that each fire, and one <code>Unsubscribe</code> removes exactly one registration. A resubscribe of a previously-unsubscribed fn fires its initial value again. (Per-registration initial-fire bookkeeping, not a shared per-observer dedup.)</span></span></span></summary>
242
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listener-registry.test.ts</code> (locks AUTH-B3 + AUTH-B4)</div></div>
243
+ </details>
244
+ <details class="compat-row" data-status="ok">
245
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Unsubscribing during emission does not skip remaining subscribers (snapshotted iteration)</span></span></span></summary>
246
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code></div></div>
247
+ </details>
248
+ <details class="compat-row" data-status="ok">
249
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">A throwing observer doesn't block other observers from firing</span></span></span></summary>
250
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-35-throwing-observer-doesnt-block-others.json</code> (<code>secondObserverContinuedFiring: true</code> against blockingfun, fb-js-sdk 12.13.0 — observer #1 throws on every call, observer #2 still counts the initial fire AND the post-sign-in fire)</div></div>
251
+ </details>
252
+ <details class="compat-row" data-status="ok">
253
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Observer object form (<code>{next, error, complete}</code>) works alongside the function form</span></span></span></summary>
254
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-36-observer-object-form-works.json</code> (against blockingfun, fb-js-sdk 12.13.0: one observer as <code>NextFn</code>, another as <code>{next, error, complete}</code>. Both forms see 1 initial null fire, +1 on <code>signInAnonymously</code>, +1 on <code>signOut</code>; <code>bothFormsFiredOnSignIn: true, bothFormsFiredOnSignOut: true</code>)</div></div>
255
+ </details>
256
+ <details class="compat-row" data-status="ok">
257
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onAuthStateChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Setting the same user twice does NOT double-fire (structural-equality no-op). Sandbox-internal <code>setCurrentUser</code> claim; the prod analog is <code>signInAnonymously</code> called twice in a row (per fix #399, the second call returns the same user).</span></span></span></summary>
258
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-37-same-user-no-double-fire.json</code> (against blockingfun, fb-js-sdk 12.13.0: first <code>signInAnonymously</code> fires the listener once; second call returns the same uid (<code>sameUserAcrossCalls: true</code>) and does NOT produce a fresh fire (<code>secondSignInProducedFire: false</code>). Prod also recognizes the same-user no-op)</div></div>
259
+ </details>
260
+ </div>
261
+
262
+ ## `onIdTokenChanged(auth, observer)`
263
+
264
+ <div class="compat-list">
265
+ <details class="compat-row" data-status="ok">
266
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onIdTokenChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Fires on user change (sandbox shares the auth-state path)</span></span></span></summary>
267
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-38-onidtokenchanged-fires-on-user-change.json</code> (against blockingfun, fb-js-sdk 12.13.0: subscribe → 1 initial fire (null) → <code>signInAnonymously</code> → +1 (user₁) → <code>signOut</code> → +1 (null) → <code>signInAnonymously</code> → +1 (user₂ with fresh uid). Every identity transition produces exactly one fire, matching <code>onAuthStateChanged</code>)</div></div>
268
+ </details>
269
+ <details class="compat-row" data-status="ok">
270
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onIdTokenChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Fires on EVERY sign-in, including a same-uid re-sign-in (no <code>signOut</code> first) — which mints a fresh token, so a subsequent <code>getIdToken()</code> returns a new string. Mirrors upstream <code>notifyAuthListeners</code>, which calls <code>idTokenSubscription.next</code> on every identity update (<code>auth_impl.ts:716</code>). <code>onAuthStateChanged</code> stays silent on the same-uid case (row 30a).</span></span></span></summary>
271
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-uid-dedup.test.ts</code> (locks AUTH-B8)</div></div>
272
+ </details>
273
+ <details class="compat-row" data-status="ok">
274
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onIdTokenChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Fires on token refresh (<code>getIdToken(true)</code>)</span></span></span></summary>
275
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-token-refresh.test.ts</code> — was ⚠ (documented divergence); aligned to prod in commit — sandbox now mints a fresh token on forceRefresh and fires <code>onIdTokenChanged</code> (NOT <code>onAuthStateChanged</code>, since identity is unchanged). Oracle: <code>packages/conformance/observations/auth/auth-onidtokenchanged-force-refresh.json</code> defines the target shape (<code>refreshFiredListener: true</code> against blockingfun; subscribe → null fire → <code>signInAnonymously</code> → +1 → <code>getIdToken(true)</code> → +1 for a total of 3 fires).</div></div>
276
+ </details>
277
+ <details class="compat-row" data-status="ok">
278
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">onIdTokenChanged(auth, observer)</code><span class="compat-sub"><span class="compat-behavior">Initial-fire semantics match <code>onAuthStateChanged</code></span></span></span></summary>
279
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-listeners.test.ts</code> + oracle: <code>packages/conformance/observations/auth/auth-row-40-onidtokenchanged-matches-onauthstatechanged-initial-fire.json</code> (against blockingfun, fb-js-sdk 12.13.0: subscribing both listeners in the same tick yields <code>sync: {auth: 0, idToken: 0}</code> → <code>microtask: {auth: 1, idToken: 1}</code> → no further fires. Both listeners share the microtask-deferred initial-fire timing)</div></div>
280
+ </details>
281
+ </div>
282
+
283
+ ## `setPersistence(auth, persistence)`
284
+
285
+ <div class="compat-list">
286
+ <details class="compat-row" data-status="ok">
287
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setPersistence(auth, persistence)</code><span class="compat-sub"><span class="compat-behavior">Accepts <code>inMemoryPersistence</code> / <code>browserSessionPersistence</code> / <code>browserLocalPersistence</code> markers without throwing</span></span></span></summary>
288
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:types.test.ts</code></div></div>
289
+ </details>
290
+ <details class="compat-row" data-status="ok">
291
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setPersistence(auth, persistence)</code><span class="compat-sub"><span class="compat-behavior">Returns <code>Promise&lt;void&gt;</code></span></span></span></summary>
292
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:types.test.ts</code></div></div>
293
+ </details>
294
+ <details class="compat-row" data-status="diverged">
295
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">setPersistence(auth, persistence)</code><span class="compat-sub"><span class="compat-behavior">Actually changes where the auth state is persisted</span></span></span></summary>
296
+ <div class="compat-evidence"><div class="compat-probe">divergence: sandbox is a no-op. Prod respects the marker.</div></div>
297
+ </details>
298
+ <details class="compat-row" data-status="ok">
299
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">setPersistence(auth, persistence)</code><span class="compat-sub"><span class="compat-behavior">An unrecognized persistence marker is rejected with <code>auth/argument-error</code> rather than silently coerced to LOCAL</span></span></span></summary>
300
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-cluster-b9-b12.test.ts</code> (locks AUTH-B12)</div></div>
301
+ </details>
302
+ </div>
303
+
304
+ ## `signInWithPopup(auth, provider)` / `signInWithCredential(auth, credential)`
305
+
306
+ <div class="compat-list">
307
+ <details class="compat-row" data-status="ok">
308
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub"><span class="compat-behavior">Returns the pre-staged <code>UserCredential</code> registered via <code>sandbox.mockSignInResult(auth, …)</code></span></span></span></summary>
309
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-providers.test.ts</code></div></div>
310
+ </details>
311
+ <details class="compat-row" data-status="ok">
312
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub"><span class="compat-behavior">Throws <code>auth/no-mock-configured</code> when no mock is pre-staged</span></span></span></summary>
313
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-providers.test.ts</code></div></div>
314
+ </details>
315
+ <details class="compat-row" data-status="ok">
316
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub"><span class="compat-behavior">Mock is consumed after one read (subsequent call without a fresh stage throws again)</span></span></span></summary>
317
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-providers.test.ts</code></div></div>
318
+ </details>
319
+ <details class="compat-row" data-status="ok">
320
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub"><span class="compat-behavior">After successful sign-in, <code>currentUser</code> becomes the mock's <code>user</code>, listeners fire</span></span></span></summary>
321
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-providers.test.ts</code></div></div>
322
+ </details>
323
+ <details class="compat-row" data-status="ok">
324
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub"><span class="compat-behavior">The credential's rich <code>User</code> (email / displayName / isAnonymous) survives the transition — popup/redirect/credential/<code>setUser</code> do NOT clobber it down to the bare <code>AuthState</code>; <code>cred.user === auth.currentUser</code> (reference identity, matches upstream <code>_updateCurrentUser(userCredential.user)</code>)</span></span></span></summary>
325
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-user-identity.test.ts</code> (locks AUTH-B1)</div></div>
326
+ </details>
327
+ <details class="compat-row" data-status="diverged">
328
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub"><span class="compat-behavior">Opens a popup window in prod</span></span></span></summary>
329
+ <div class="compat-evidence"><div class="compat-probe">divergence: sandbox skips the popup; mock pre-stage replaces the popup result</div></div>
330
+ </details>
331
+ <details class="compat-row" data-status="unsupported">
332
+ <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Unsupported" title="Unsupported"></span><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub"><span class="compat-behavior">Cancels with <code>auth/popup-closed-by-user</code> when the user dismisses the popup (prod)</span></span></span></summary>
333
+ <div class="compat-evidence"><div class="compat-probe">not modeled — would require the host to expose a "cancel" affordance on the mock</div></div>
334
+ </details>
335
+ <details class="compat-row" data-status="ok">
336
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub"><span class="compat-behavior">Provider-flow sign-ins (popup / redirect / credential) record the flow's <code>providerId</code> on the identity in the user DB (upsert for unknown uids; append-if-missing for known ones) and reject disabled accounts with <code>auth/user-disabled</code> before any state change</span></span></span></summary>
337
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-user-admin.test.ts</code> ("provider tracking", "disabled users") — provider recording is sandbox bookkeeping for <code>listIdentities</code>/<code>listUsers</code>; prod's auto-link semantics are narrower (same-email Google auto-link only) and not modeled</div></div>
338
+ </details>
339
+ </div>
340
+
341
+ ## `signInWithRedirect` / `getRedirectResult` / resolver seam
342
+
343
+ <div class="compat-list">
344
+ <details class="compat-row" data-status="ok">
345
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithRedirect<code> / </code>getRedirectResult` / resolver seam</code><span class="compat-sub"><span class="compat-behavior"><code>signInWithRedirect(auth, provider, resolver?)</code> resolves the flow (per-call resolver → injected → one-shot mock → <code>auth/argument-error</code>), signs the user in, and stashes the credential for one <code>getRedirectResult</code></span></span></span></summary>
346
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-resolver.test.ts</code></div></div>
347
+ </details>
348
+ <details class="compat-row" data-status="ok">
349
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithRedirect<code> / </code>getRedirectResult` / resolver seam</code><span class="compat-sub"><span class="compat-behavior"><code>getRedirectResult(auth)</code> returns the stashed credential once, then <code>null</code> (one-shot, matches prod)</span></span></span></summary>
350
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-resolver.test.ts</code></div></div>
351
+ </details>
352
+ <details class="compat-row" data-status="ok">
353
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithRedirect<code> / </code>getRedirectResult` / resolver seam</code><span class="compat-sub"><span class="compat-behavior"><code>sandbox.setAuthFlowResolver(auth, resolver | null)</code> installs / clears the popup/redirect resolver (the analog of browser <code>getAuth</code> wiring <code>browserPopupRedirectResolver</code>)</span></span></span></summary>
354
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-resolver.test.ts</code></div></div>
355
+ </details>
356
+ <details class="compat-row" data-status="ok">
357
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithRedirect<code> / </code>getRedirectResult` / resolver seam</code><span class="compat-sub"><span class="compat-behavior"><code>sandbox.listIdentities(auth)</code> snapshots every seeded/created identity for a host account-picker (sandbox-only — no <code>firebase/auth</code> equivalent)</span></span></span></summary>
358
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-resolver.test.ts</code></div></div>
359
+ </details>
360
+ </div>
361
+
362
+ ## `Auth` surface + error shape
363
+
364
+ <div class="compat-list">
365
+ <details class="compat-row" data-status="ok">
366
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Auth` surface + error shape</code><span class="compat-sub"><span class="compat-behavior"><code>auth.signOut()</code> method form works alongside the free <code>signOut(auth)</code> function (<code>firebase/auth</code>'s <code>Auth</code> exposes both) (AUTH-GAP)</span></span></span></summary>
367
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:auth-gap-surface.test.ts</code></div></div>
368
+ </details>
369
+ <details class="compat-row" data-status="ok">
370
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Auth` surface + error shape</code><span class="compat-sub"><span class="compat-behavior">Sandbox auth errors are real <code>FirebaseError</code> instances (<code>err instanceof FirebaseError</code>) carrying the prod message wrapper <code>Firebase: &lt;message&gt; (&lt;auth/...&gt;).</code> — e.g. <code>Firebase: Error (auth/invalid-email).</code>, matching the oracle (AUTH-GAP)</span></span></span></summary>
371
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:auth-gap-surface.test.ts</code></div></div>
372
+ </details>
373
+ </div>
374
+
375
+ ## Provider classes (`GoogleAuthProvider`, `EmailAuthProvider`, etc.)
376
+
377
+ <div class="compat-list">
378
+ <details class="compat-row" data-status="ok">
379
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior">Exports the same constructor signatures as <code>firebase/auth</code> for each provider</span></span></summary>
380
+ <div class="compat-evidence"><div class="compat-probe">type-only smoke in <code>unit:types.test.ts</code></div></div>
381
+ </details>
382
+ <details class="compat-row" data-status="ok">
383
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>Provider.credential(...)</code> static factories produce <code>AuthCredential</code>-shaped objects</span></span></summary>
384
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-providers.test.ts</code></div></div>
385
+ </details>
386
+ <details class="compat-row" data-status="ok">
387
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><span class="compat-behavior"><code>GoogleAuthProvider.providerId === 'google.com'</code> (and per-provider analogs)</span></span></summary>
388
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-providers.test.ts</code></div></div>
389
+ </details>
390
+ <details class="compat-row" data-status="unsupported">
391
+ <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Unsupported" title="Unsupported"></span><span class="compat-main"><span class="compat-behavior">Custom scopes / params / language code</span></span></summary>
392
+ <div class="compat-evidence"><div class="compat-probe">sandbox ignores; prod forwards</div></div>
393
+ </details>
394
+ </div>
395
+
396
+ ## `User` methods
397
+
398
+ <div class="compat-list">
399
+ <details class="compat-row" data-status="ok">
400
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.getIdToken()</code> returns a stable opaque token in sandbox (<code>sandbox-id-token-…</code>)</span></span></span></summary>
401
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-anonymous.test.ts</code></div></div>
402
+ </details>
403
+ <details class="compat-row" data-status="ok">
404
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.getIdToken(true)</code> (forceRefresh) returns a NEW token; subsequent <code>getIdToken(false)</code> returns the cached new token</span></span></span></summary>
405
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-token-refresh.test.ts</code> — was ⚠ (documented divergence); aligned to prod in commit — sandbox now mints a fresh token on forceRefresh and fires <code>onIdTokenChanged</code>. Oracle: <code>packages/conformance/observations/auth/auth-getidtoken-force-refresh.json</code> defines the target shape (<code>forceRefreshReturnedDifferentString: true</code>, <code>token1EqualsToken2: true</code> against blockingfun — the refreshed token is cached, so a subsequent non-forced read returns it, not yet another fresh one). Sandbox tokens stay <code>sandbox-id-token-&lt;uid&gt;-&lt;hash&gt;</code> strings; prod's are real JWTs.</div></div>
406
+ </details>
407
+ <details class="compat-row" data-status="ok">
408
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.getIdTokenResult()</code> returns claims</span></span></span></summary>
409
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-providers.test.ts</code> (custom-claims path)</div></div>
410
+ </details>
411
+ <details class="compat-row" data-status="ok">
412
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.uid</code>, <code>user.email</code>, <code>user.displayName</code>, <code>user.isAnonymous</code> reflect the source</span></span></span></summary>
413
+ <div class="compat-evidence"><div class="compat-probe"><code>playground:auth-anonymous</code>, <code>playground:auth-email-password</code></div></div>
414
+ </details>
415
+ <details class="compat-row" data-status="ok">
416
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.emailVerified</code> — present on every sandbox-minted <code>User</code> (default <code>false</code>; sandbox has no verification flow). Prod passes the real value through (no longer stripped). The admin record (<code>sandbox.listUsers</code>) carries it too</span></span></span></summary>
417
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:auth-gap-surface.test.ts</code> (locks AUTH-GAP)</div></div>
418
+ </details>
419
+ <details class="compat-row" data-status="ok">
420
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.photoURL</code> / <code>user.phoneNumber</code> — present (sandbox default <code>null</code>; prod passes through, no longer stripped)</span></span></span></summary>
421
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:auth-gap-surface.test.ts</code></div></div>
422
+ </details>
423
+ <details class="compat-row" data-status="ok">
424
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.providerId</code> (aggregate, <code>'firebase'</code>) + <code>user.providerData: UserInfo[]</code> — sandbox synthesizes one provider entry for non-anonymous users, empty for anonymous; prod passes the real array through (no longer stripped). The admin record carries the emulator-shaped <code>providerUserInfo</code></span></span></span></summary>
425
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:auth-gap-surface.test.ts</code></div></div>
426
+ </details>
427
+ <details class="compat-row" data-status="unsupported">
428
+ <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Unsupported" title="Unsupported"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.metadata.creationTime</code> / <code>lastSignInTime</code></span></span></span></summary>
429
+ <div class="compat-evidence"><div class="compat-probe">client <code>User.metadata</code> not exposed (AUTH-GAP); the admin record carries <code>createdAt</code>/<code>lastLoginAt</code> (ISO)</div></div>
430
+ </details>
431
+ <details class="compat-row" data-status="ok">
432
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>IdTokenResult.signInProvider</code> reflects the session's provider per flow (<code>'anonymous'</code> / <code>'password'</code> / <code>'google.com'</code> / …); claims include the reserved <code>firebase.sign_in_provider</code> (custom claims can't shadow it)</span></span></span></summary>
433
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-user-admin.test.ts</code> ("IdTokenResult.signInProvider") — prod shape is documented SDK behavior; no oracle capture yet</div></div>
434
+ </details>
435
+ <details class="compat-row" data-status="ok">
436
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior">Custom-claims changes (<code>sandbox.updateUser</code> / re-seed) reach an active session on the next FORCED token refresh, not immediately — claims are read live from the user DB at mint time (prod's refresh-propagation story; AUTH-B10)</span></span></span></summary>
437
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-user-admin.test.ts</code>, <code>unit:sandbox-cluster-b9-b12.test.ts</code></div></div>
438
+ </details>
439
+ <details class="compat-row" data-status="unsupported">
440
+ <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Unsupported" title="Unsupported"></span><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><span class="compat-behavior"><code>user.reload()</code> / <code>user.delete()</code> / <code>user.toJSON()</code> / <code>user.refreshToken</code> / <code>user.tenantId</code></span></span></span></summary>
441
+ <div class="compat-evidence"><div class="compat-probe">not modeled by the sandbox; documented in the deny-list rather than synthesized (AUTH-GAP)</div></div>
442
+ </details>
443
+ <details class="compat-row" data-status="ok">
444
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">updateProfile(user, {displayName, photoURL})</code><span class="compat-sub"><span class="compat-behavior"><code>updateProfile(user, {displayName, photoURL})</code> mutates the held user + stored record in place (null clears a field; omitted fields untouched) without an extra <code>onAuthStateChanged</code> fire. Profile survives <code>signOut</code> → <code>signInWithEmailAndPassword</code>.</span></span></span></summary>
445
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-update-profile.test.ts</code> (in-place mutation, listUsers persist, null clear, no extra listener); re-sign-in persistence: <code>unit:upstream-auth-probes.test.ts</code></div></div>
446
+ </details>
447
+ </div>
448
+
449
+ ## `beforeAuthStateChanged(auth, callback, onAbort?)`
450
+
451
+ <div class="compat-list">
452
+ <details class="compat-row" data-status="ok">
453
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">beforeAuthStateChanged(auth, callback, onAbort?)</code><span class="compat-sub"><span class="compat-behavior">Registers a BLOCKING gate that runs before a real sign-in/sign-out transition commits. Callbacks run in registration order; a callback that throws (or returns a rejected promise) aborts the transition entirely: the pending <code>signInWith…</code>/<code>signOut</code> call rejects with <code>auth/login-blocked</code>, <code>currentUser</code> is unchanged, and <code>onAuthStateChanged</code>/<code>onIdTokenChanged</code> do NOT fire. Covers every sign-in path that exists in <code>pyric/auth</code>: <code>signInAnonymously</code>, <code>signInWithEmailAndPassword</code>, <code>createUserWithEmailAndPassword</code>, <code>signInWithPopup</code>, <code>signInWithRedirect</code>, <code>signInWithCredential</code>, <code>signInWithCustomToken</code>, and <code>signOut</code>. Modeled after the real <code>@firebase/auth</code> <code>AuthMiddlewareQueue.runMiddleware</code> (<code>auth_impl.ts</code>), read directly from the installed SDK source, not just its <code>.d.ts</code>.</span></span></span></summary>
454
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-before-auth-state-changed.test.ts</code> — implementation cross-checked against <code>@firebase/auth</code>'s <code>AuthMiddlewareQueue</code> source (registration-order queue, per-callback try/await, <code>auth/login-blocked</code> wrap). Custom-token allow/block: <code>unit:upstream-auth-probes.test.ts</code>. No live-oracle capture (no observable client-visible signal to probe beyond the documented+sourced contract already read).</div></div>
455
+ </details>
456
+ <details class="compat-row" data-status="ok">
457
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">beforeAuthStateChanged(auth, callback, onAbort?)</code><span class="compat-sub"><span class="compat-behavior"><code>onAbort</code> semantics: when a callback throws, every <code>onAbort</code> registered by a callback that ALREADY SUCCEEDED in the same pass runs, in REVERSE registration order — matches upstream's rollback-stack (<code>runMiddleware</code>'s <code>onAbortStack</code>). An <code>onAbort</code> that itself throws is swallowed so it can't mask the original block reason or skip the remaining rollbacks.</span></span></span></summary>
458
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-before-auth-state-changed.test.ts</code> ("onAbort runs (in reverse order)…", "a callback whose own onAbort throws…")</div></div>
459
+ </details>
460
+ <details class="compat-row" data-status="ok">
461
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">beforeAuthStateChanged(auth, callback, onAbort?)</code><span class="compat-sub"><span class="compat-behavior">Fires on BOTH directions: a real sign-in (<code>nextUser</code> non-null) and a real sign-out (<code>nextUser === null</code>) — a throwing callback blocks <code>signOut</code> too, leaving the previous user signed in. Matches upstream, where the same middleware queue gates <code>_updateCurrentUser</code> and <code>signOut</code>.</span></span></span></summary>
462
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-before-auth-state-changed.test.ts</code> ("fires on sign-out too…")</div></div>
463
+ </details>
464
+ <details class="compat-row" data-status="diverged">
465
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">beforeAuthStateChanged(auth, callback, onAbort?)</code><span class="compat-sub"><span class="compat-behavior"><code>sandbox.setUser</code> (the sandbox-only test driver) BYPASSES the gate entirely — it is a raw identity force with no prod analog (same bypass it already has for provider enforcement / <code>signInProvider</code> tracking), so no registered <code>beforeAuthStateChanged</code> callback runs and none can block it.</span></span></span></summary>
466
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-before-auth-state-changed.test.ts</code> ("sandbox.setUser test driver bypasses the gate")</div></div>
467
+ </details>
468
+ <details class="compat-row" data-status="diverged">
469
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">beforeAuthStateChanged(auth, callback, onAbort?)</code><span class="compat-sub"><span class="compat-behavior">Served-worker path (SharedWorker-backed auth in <code>@pyric/cli</code>): the worker owns the shared user pool and commits transitions on its own side of the port, so a page-local <code>beforeAuthStateChanged</code> registration can't actually gate a worker-driven sign-in. Rather than silently accept a callback that could never run, registering THROWS immediately (<code>auth/operation-not-supported-in-this-environment</code>) — same defensive pattern as <code>signInWithCredential</code> over the worker.</span></span></span></summary>
470
+ <div class="compat-evidence"><div class="compat-probe"><code>packages/cli/src/serve/worker/client.ts</code> (<code>beforeAuthStateChanged</code> throws <code>makeUnsupported</code>)</div></div>
471
+ </details>
472
+ </div>
473
+
474
+ ## User-management and session exports
475
+
476
+ <div class="compat-list">
477
+ <details class="compat-row" data-status="ok">
478
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">initializeAuth(app, deps?)</code><span class="compat-sub"><span class="compat-behavior">Aliases <code>getAuth(app)</code> and returns the same stable <code>Auth</code> handle — an app that calls <code>initializeAuth</code> instead of <code>getAuth</code> gets an equivalent, working instance. The optional <code>Dependencies</code> arg (persistence / popupRedirectResolver) is accepted for signature parity but not applied (persistence is already a documented sandbox no-op). Repeated calls return the cached handle rather than throwing <code>auth/already-initialized</code></span></span></span></summary>
479
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:fruit-aliases.test.ts</code> — returns the same instance as <code>getAuth</code>, with a live <code>currentUser</code></div></div>
480
+ </details>
481
+ <details class="compat-row" data-status="ok">
482
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">deleteUser(user)</code><span class="compat-sub"><span class="compat-behavior">Deletes the account from the user store AND signs the user out if they are current (fires <code>onAuthStateChanged(null)</code>), matching prod where deleting the signed-in user clears <code>auth.currentUser</code>. Real behavior: a subsequent <code>signInWithEmailAndPassword</code> for that identity throws <code>auth/user-not-found</code>; <code>reload(user)</code> after delete rejects with <code>auth/user-token-expired</code></span></span></span></summary>
483
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:fruit-aliases.test.ts</code> — user removed from the store, sign-out fired, re-sign-in throws <code>auth/user-not-found</code>; post-delete <code>reload</code> → <code>auth/user-token-expired</code>: <code>unit:upstream-auth-probes.test.ts</code></div></div>
484
+ </details>
485
+ <details class="compat-row" data-status="diverged">
486
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">updateEmail(user, newEmail)</code><span class="compat-sub"><span class="compat-behavior">Changes the stored email (via the same path as <code>sandbox.updateUser</code>, rejecting <code>auth/email-already-in-use</code> / <code>auth/invalid-email</code>) and mutates the held <code>user</code> in place, so the next sign-in resolves against the new email. Leniency vs prod: the sandbox does NOT enforce <code>auth/requires-recent-login</code> and is not routed through <code>verifyBeforeUpdateEmail</code> (which the real SDK requires when email-enumeration protection is on)</span></span></span></summary>
487
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:fruit-aliases.test.ts</code> — stored email changes; re-sign-in works with the new email, fails with the old</div>
488
+ <div class="compat-note">email really changes; no requires-recent-login / verifyBeforeUpdateEmail enforcement</div></div>
489
+ </details>
490
+ <details class="compat-row" data-status="diverged">
491
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">updatePassword(user, newPassword)</code><span class="compat-sub"><span class="compat-behavior">Sets the stored password (validated for strength). The sandbox DOES store and verify passwords, so this is a real mutation: a subsequent <code>signInWithEmailAndPassword</code> with the new password succeeds and the old one throws <code>auth/wrong-password</code>. Leniency vs prod: no <code>auth/requires-recent-login</code> enforcement</span></span></span></summary>
492
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:fruit-aliases.test.ts</code> — new password signs in, old password throws <code>auth/wrong-password</code></div>
493
+ <div class="compat-note">password really changes + is verified; no requires-recent-login enforcement</div></div>
494
+ </details>
495
+ <details class="compat-row" data-status="ok">
496
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">reload(user)</code><span class="compat-sub"><span class="compat-behavior">Re-reads the stored record into the <code>user</code> object in place, so a change made out of band (e.g. <code>sandbox.updateUser</code>) is reflected on the held reference — matching prod's server refresh. If the identity is absent from the store (including after <code>deleteUser</code>), rejects with <code>auth/user-token-expired</code></span></span></span></summary>
497
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:fruit-aliases.test.ts</code> — an out-of-band <code>sandbox.updateUser</code> displayName change is visible on the held user after <code>reload</code>; missing-store / post-delete: <code>unit:upstream-auth-probes.test.ts</code></div></div>
498
+ </details>
499
+ <details class="compat-row" data-status="ok">
500
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">updateCurrentUser(auth, user)</code><span class="compat-sub"><span class="compat-behavior">Sets the sandbox's current user (pass <code>null</code> to sign out), firing <code>onAuthStateChanged</code> — <code>auth.currentUser</code> reflects the passed user afterward</span></span></span></summary>
501
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:fruit-aliases.test.ts</code> — <code>auth.currentUser</code> becomes the passed user; <code>null</code> signs out</div></div>
502
+ </details>
503
+ <details class="compat-row" data-status="diverged">
504
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">useDeviceLanguage(auth)</code><span class="compat-sub"><span class="compat-behavior">Accepted no-op — the sandbox has no device locale to read, so there is no language to set; accepted so init code that calls it compiles + runs</span></span></span></summary>
505
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:fruit-aliases.test.ts</code> — resolves/returns without error</div>
506
+ <div class="compat-note">no device locale in the sandbox</div></div>
507
+ </details>
508
+ </div>
509
+
510
+ ## `ActionCodeURL` / email-link / action-code — the out-of-band family
511
+
512
+ <div class="compat-list">
513
+ <details class="compat-row" data-status="ok">
514
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">ActionCodeURL.parseLink(link) / parseActionCodeURL(link)</code><span class="compat-sub"><span class="compat-behavior">Parses an out-of-band action link. The <code>mode</code> query param maps to a NORMALIZED operation (<code>mode=resetPassword</code> -&gt; <code>PASSWORD_RESET</code>, <code>mode=signIn</code> -&gt; <code>EMAIL_SIGNIN</code>), <code>oobCode</code> surfaces as <code>code</code>, <code>lang</code> as <code>languageCode</code>, and <code>continueUrl</code> comes out URL-DECODED. A link missing <code>mode</code>, missing <code>oobCode</code>, carrying an unknown mode, or that is not a URL at all parses to <code>null</code> — the parse NEVER throws. <code>parseActionCodeURL</code> and <code>ActionCodeURL.parseLink</code> agree.</span></span></span></summary>
515
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED and project-independent: this is a PURE client-side parse (no network, no project, no mailbox), so the sandbox owes prod an exact match and there is no room for a divergence. Oracle: <code>auth-actioncodeurl-parse</code> against firebase-js-sdk 12.13.0. Replayed in <code>unit:oracle-conformance.test.ts</code>.</div></div>
516
+ </details>
517
+ <details class="compat-row" data-status="ok">
518
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">isSignInWithEmailLink(auth, link)</code><span class="compat-sub"><span class="compat-behavior">Pure predicate over the link string — no network. <code>true</code> only for a link whose mode is <code>signIn</code> AND which carries an <code>oobCode</code>; <code>false</code> for a password-reset link, for <code>mode=signIn</code> with no code, for garbage, and for the empty string. Never throws.</span></span></span></summary>
519
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED, project-independent (no server involved): <code>auth-issigninwithemaillink-predicate</code> captured all five cases against prod. Replayed in <code>unit:oracle-conformance.test.ts</code>.</div></div>
520
+ </details>
521
+ <details class="compat-row" data-status="ok">
522
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">applyActionCode(auth, code)</code><span class="compat-sub"><span class="compat-behavior">Redeems an out-of-band code and performs its state change (<code>VERIFY_EMAIL</code> sets <code>emailVerified</code>, <code>VERIFY_AND_CHANGE_EMAIL</code> moves the account to the new address). Throws <code>auth/invalid-action-code</code> for a code the project never issued, for the empty string, and for a code already redeemed (single-use). A <code>PASSWORD_RESET</code> code is refused — <code>confirmPasswordReset</code> owns that one.</span></span></span></summary>
523
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED on the reject path: <code>auth-action-code-invalid</code> confirmed prod emits <code>auth/invalid-action-code</code> for both a bogus code and the empty string (this endpoint is NOT gated on the password provider, so it answered honestly). The APPLY path (a real code, redeemed) cannot be probed from a client — it needs a code from a real inbox — and is unit-backed end to end against the sandbox outbox in <code>unit:sandbox-email-link.test.ts</code>.</div></div>
524
+ </details>
525
+ <details class="compat-row" data-status="ok">
526
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">sendSignInLinkToEmail(auth, email, settings)</code><span class="compat-sub"><span class="compat-behavior">ActionCodeSettings validation, enforced CLIENT-side before any request leaves the process: a missing or unparseable <code>url</code> throws <code>auth/invalid-continue-uri</code> (NOT <code>auth/missing-continue-uri</code>, despite the constant's name), and <code>handleCodeInApp</code> other than <code>true</code> throws <code>auth/argument-error</code>. On success the sandbox mints a single-use code and posts the message to the outbox.</span></span></span></summary>
527
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED on both client-side arms (project-independent — prod threw before any network call): <code>auth-sendsigninlinktoemail-settings-validation</code> captured <code>missingUrl: auth/invalid-continue-uri</code> and <code>handleCodeInAppFalse: auth/argument-error</code>. Replayed in <code>unit:oracle-conformance.test.ts</code>; the send path is unit-backed in <code>unit:sandbox-email-link.test.ts</code>.</div></div>
528
+ </details>
529
+ <details class="compat-row" data-status="ok">
530
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">signInWithEmailLink(auth, email, link)</code><span class="compat-sub"><span class="compat-behavior">Redeems the code in a sign-in link. A link carrying no <code>oobCode</code> throws <code>auth/argument-error</code> CLIENT-side (the SDK never reaches the server to ask about a code it cannot find in the link). A first-time sign-in for an address CREATES the account, <code>getAdditionalUserInfo(cred).isNewUser</code> is <code>true</code>, and the account arrives <code>emailVerified: true</code> — redeeming a code mailed to that address is proof of control. The code is single-use.</span></span></span></summary>
531
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED on the client-side reject (<code>auth-signinwithemaillink-invalid-link</code>: <code>noOobCode: auth/argument-error</code>). The REDEMPTION path could not be probed: the oracle project has email-link sign-in disabled, so the server arm answered <code>auth/operation-not-allowed</code>. That half is unit-backed end to end against the sandbox outbox (<code>unit:sandbox-email-link.test.ts</code> drives send -&gt; read the outbox -&gt; sign in).</div></div>
532
+ </details>
533
+ <details class="compat-row" data-status="ok">
534
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">sendPasswordResetEmail(auth, email)</code><span class="compat-sub"><span class="compat-behavior">For an address NO account owns, prod RESOLVES silently — it does not throw <code>auth/user-not-found</code>. Email Enumeration Protection is on by default and refusing to leak account existence is the point. The sandbox matches: it resolves and mails nothing. A malformed address still throws <code>auth/invalid-email</code>. For a real account, the sandbox mails a reset code; <code>confirmPasswordReset</code> redeems it and the new password signs in while the old one throws <code>auth/wrong-password</code>.</span></span></span></summary>
535
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED on the enumeration-protection behavior — the fact most likely to be got wrong: <code>auth-sendpasswordresetemail-unknown-user</code> captured <code>resolvedForUnknownUser: true</code>, <code>unknownUserCode: null</code>, <code>malformedEmailCode: auth/invalid-email</code>. A shim that threw <code>auth/user-not-found</code> here would hand agent code an account oracle production deliberately removed. Reset round trip unit-backed in <code>unit:sandbox-email-link.test.ts</code>.</div></div>
536
+ </details>
537
+ <details class="compat-row" data-status="ok">
538
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">sendEmailVerification(user)</code><span class="compat-sub"><span class="compat-behavior">Throws <code>auth/missing-email</code> for a user with no email on the account (an anonymous user). For a real account the mail goes out and NOTHING ELSE HAPPENS: <code>user.emailVerified</code> stays <code>false</code>. Verification happens when the code in the message is redeemed (<code>applyActionCode</code>), not when it is sent.</span></span></span></summary>
539
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED on the anonymous reject: <code>auth-sendemailverification-shape</code> captured <code>anonymousUserCode: auth/missing-email</code> against prod. The send-does-not-verify property is the one the whole flow turns on and is unit-backed (<code>unit:sandbox-email-link.test.ts</code> asserts <code>emailVerified</code> is still false after the send and true only after the code is applied) — it cannot be oracle-confirmed end to end because confirming it would require reading a real inbox.</div></div>
540
+ </details>
541
+ <details class="compat-row" data-status="ok">
542
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">verifyBeforeUpdateEmail(user, newEmail)</code><span class="compat-sub"><span class="compat-behavior">Mails a code to the NEW address and returns. The account's email does NOT change until that code is redeemed — the single guarantee separating this API from a bare <code>updateEmail</code>: the user must prove control of the new address before it becomes theirs. On redemption the account moves and the new address arrives <code>emailVerified: true</code>. <code>checkActionCode</code> on the code reports <code>data.email</code> = the new address and <code>data.previousEmail</code> = the old one.</span></span></span></summary>
543
+ <div class="compat-evidence"><div class="compat-probe">UNIT-BACKED, not oracle-backed, and the reason is recorded rather than hidden: the probe (<code>auth-verifybeforeupdateemail-shape</code>) ran against the real project and came back <code>auth/operation-not-allowed</code> on every arm, because the oracle project has the Email/Password provider DISABLED and the probe could not even create the user whose email it would change. The capture is committed showing exactly that. Behavior is proven end to end against the sandbox in <code>unit:sandbox-email-link.test.ts</code>.</div></div>
544
+ </details>
545
+ <details class="compat-row" data-status="ok">
546
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">sandbox.takeAuthMail(auth) / sandbox.setAuthMailResolver(auth, resolver)</code><span class="compat-sub"><span class="compat-behavior">The sandbox IS the mail server. Every send posts a real message (operation, recipient, single-use code, and the full action link) to an outbox; <code>sandbox.takeAuthMail</code> reads it — the program's substitute for a human opening an inbox. The mailed link round-trips through the public <code>isSignInWithEmailLink</code> / <code>parseActionCodeURL</code> unchanged, and its code is the code the redeemer accepts, so the round trip production cannot complete without a human closes in-process. An installed <code>AuthMailResolver</code> is additionally notified per message; a resolver that THROWS does not fail the send that produced it.</span></span></span></summary>
547
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-email-link.test.ts</code> — sandbox-only driver (no <code>firebase/auth</code> counterpart; this is the seam that makes the family testable at all).</div></div>
548
+ </details>
549
+ <details class="compat-row" data-status="ok">
550
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">checkActionCode(auth, code) / verifyPasswordResetCode(auth, code) / confirmPasswordReset(auth, code, newPassword)</code><span class="compat-sub"><span class="compat-behavior"><code>checkActionCode</code> and <code>verifyPasswordResetCode</code> INSPECT a code without burning it — a check must not destroy the code the subsequent apply needs. <code>confirmPasswordReset</code> redeems it and sets the password, running the same strength check <code>createUserWithEmailAndPassword</code> runs; a weak new password throws <code>auth/weak-password</code> and does NOT burn the code (a typo must not destroy the user's one reset link). A code staged as expired throws <code>auth/expired-action-code</code>.</span></span></span></summary>
551
+ <div class="compat-evidence"><div class="compat-probe">UNIT-BACKED, stated honestly: these three endpoints answered <code>auth/operation-not-allowed</code> in the oracle run (<code>auth-action-code-invalid</code>) because the oracle project's disabled password provider replies before the invalid-code contract can. That is a fact about the project's configuration, not about the API, so it is NOT asserted as evidence. The error codes used here are matched to the oracle-CAPTURED <code>AuthErrorCodes</code> map (<code>INVALID_OOB_CODE = auth/invalid-action-code</code>, <code>EXPIRED_OOB_CODE = auth/expired-action-code</code> — see auth#172). Behavior proven in <code>unit:sandbox-email-link.test.ts</code>.</div></div>
552
+ </details>
553
+ <details class="compat-row" data-status="diverged">
554
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">sendSignInLinkToEmail</code><span class="compat-sub"><span class="compat-category"><code>auth/unauthorized-continue-uri</code> (domain allowlist)</span> · <span class="compat-behavior">Prod rejects a continue URL whose domain is not on the project's authorized-domains list. The sandbox has NO domain allowlist and does not invent one: it accepts any parseable continue URL. A continue URL that would be rejected in production is accepted here.</span></span></span></summary>
555
+ <div class="compat-evidence"><div class="compat-probe">The sandbox has no project config to hold an authorized-domains list, so there is nothing to check against; inventing an allowlist would mean inventing a policy the user never set. The probe attempted the server arm (<code>auth-sendsigninlinktoemail-settings-validation</code>, <code>unauthorizedDomain</code>) but the oracle project has email-link sign-in disabled, so it answered <code>auth/operation-not-allowed</code> rather than <code>auth/unauthorized-continue-uri</code> — the divergence is declared from the documented contract, not from a capture we do not have.</div></div>
556
+ </details>
557
+ </div>
558
+
559
+ ## `linkWithCredential` / `linkWithPopup` / `linkWithRedirect` / `unlink` — account linking
560
+
561
+ <div class="compat-list">
562
+ <details class="compat-row" data-status="ok">
563
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">linkWithCredential(user, credential)</code><span class="compat-sub"><span class="compat-behavior">The ANONYMOUS UPGRADE. Linking an email credential onto an anonymous account upgrades it IN PLACE: the uid is PRESERVED, <code>isAnonymous</code> flips to <code>false</code>, the email is set, and <code>providerData</code> gains the provider. Preserving the uid is what keeps the data the user created while anonymous theirs. Returns a <code>UserCredential</code> with <code>operationType: 'link'</code> and <code>getAdditionalUserInfo().isNewUser === false</code> (a link never creates an identity). The linked credential then works as a first-class <code>signInWithEmailAndPassword</code>.</span></span></span></summary>
564
+ <div class="compat-evidence"><div class="compat-probe">UNIT-BACKED, not oracle-backed — stated plainly. The probe (<code>auth-link-email-credential-to-anonymous</code>) ran against the real project and returned <code>linkCode: auth/operation-not-allowed</code>: the oracle project has the Email/Password provider DISABLED, so no email credential can be minted there and the flow cannot be reached. The capture is committed showing exactly that rather than being dropped. Behavior proven in <code>unit:sandbox-linking-reauth.test.ts</code>, including the uid-preservation invariant.</div></div>
565
+ </details>
566
+ <details class="compat-row" data-status="ok">
567
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">linkWithCredential</code><span class="compat-sub"><span class="compat-category">conflict codes</span> · <span class="compat-behavior"><code>auth/provider-already-linked</code> when the account already carries the provider (one identity per provider). <code>auth/email-already-in-use</code> when the email credential belongs to a DIFFERENT account — an address can back only one identity, so the link cannot be granted without stealing it.</span></span></span></summary>
568
+ <div class="compat-evidence"><div class="compat-probe">UNIT-BACKED. The probe (<code>auth-link-conflicts</code>) ran and every arm returned <code>auth/operation-not-allowed</code> (same disabled Email/Password provider on the oracle project), so the conflict codes could not be observed against prod. They are matched instead to the oracle-CAPTURED <code>AuthErrorCodes</code> map (<code>PROVIDER_ALREADY_LINKED = auth/provider-already-linked</code>, <code>CREDENTIAL_ALREADY_IN_USE = auth/credential-already-in-use</code> — auth#172) and proven in <code>unit:sandbox-linking-reauth.test.ts</code>. NOTE: for an EMAIL credential the sandbox emits <code>auth/email-already-in-use</code>, the code prod uses for an address collision; <code>credential-already-in-use</code> remains the OAuth-credential case.</div></div>
569
+ </details>
570
+ <details class="compat-row" data-status="ok">
571
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">unlink(user, providerId)</code><span class="compat-sub"><span class="compat-behavior">Detaches a provider and returns the updated user with a SHRUNKEN <code>providerData</code>. Unlinking a provider that was never linked throws <code>auth/no-such-provider</code>. Unlinking <code>'password'</code> takes the password with it, so <code>signInWithEmailAndPassword</code> for that account stops working. Unlinking the LAST provider does NOT re-anonymize the account — <code>isAnonymous</code> describes how an identity was born, not what it currently carries.</span></span></span></summary>
572
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED on the reject path — the ONE linking fact the oracle could reach, because it needs no email credential: <code>auth-unlink-provider</code> captured <code>noSuchProviderCode: auth/no-such-provider</code> against prod on an anonymous user. Replayed in <code>unit:oracle-conformance.test.ts</code>. The detach path is unit-backed (<code>unit:sandbox-linking-reauth.test.ts</code>).</div></div>
573
+ </details>
574
+ <details class="compat-row" data-status="ok">
575
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">linkWithPopup(user, provider, resolver?) / linkWithRedirect(...)</code><span class="compat-sub"><span class="compat-behavior">Route through the SAME <code>AuthFlowResolver</code> seam as <code>signInWithPopup</code>, with <code>authType: 'link'</code> on the request so a host UI can present 'link your Google account' rather than 'sign in'. The resolved credential names the provider to attach; the sandbox performs the attach and the uid is preserved. A disabled provider throws <code>auth/operation-not-allowed</code> AHEAD of the resolver check — a code deliberately distinct from <code>auth/argument-error</code>, which keeps meaning 'enabled, but no resolver/mock wired'.</span></span></span></summary>
576
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-linking-reauth.test.ts</code> — asserts the resolver sees <code>authType: 'link'</code>, the uid is preserved, and the two error codes stay distinct. The OAuth arm cannot be oracle-probed at all (it needs a real IdP popup and a human), which is precisely why it goes through the resolver seam.</div></div>
577
+ </details>
578
+ </div>
579
+
580
+ ## `reauthenticateWithCredential` / `reauthenticateWithPopup` / `reauthenticateWithRedirect` — re-authentication
581
+
582
+ <div class="compat-list">
583
+ <details class="compat-row" data-status="ok">
584
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">reauthenticateWithCredential(user, credential)</code><span class="compat-sub"><span class="compat-behavior">Really re-verifies: an email credential is checked against the stored password exactly as <code>signInWithEmailAndPassword</code> checks it. A wrong password throws <code>auth/wrong-password</code>. A credential belonging to a DIFFERENT account throws <code>auth/user-mismatch</code> — checked BEFORE the password compare, so it cannot leak whether the other account's password was right. On success a fresh ID token is minted (a new <code>authTime</code>), and the returned <code>UserCredential</code> carries <code>operationType: 'reauthenticate'</code>.</span></span></span></summary>
585
+ <div class="compat-evidence"><div class="compat-probe">UNIT-BACKED, and the reason it is not oracle-backed is recorded rather than glossed: the probe (<code>auth-reauthenticate-with-credential</code>) ran against the real project and could not even create the two accounts it needs — <code>setupCode: auth/operation-not-allowed</code>, because the oracle project has the Email/Password provider DISABLED. The capture is committed showing that. <code>auth/user-mismatch</code> is matched to the oracle-captured <code>AuthErrorCodes</code> map (auth#172). Behavior proven in <code>unit:sandbox-linking-reauth.test.ts</code>.</div></div>
586
+ </details>
587
+ <details class="compat-row" data-status="diverged">
588
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">reauthenticate*</code><span class="compat-sub"><span class="compat-category"><code>auth/requires-recent-login</code> is NOT enforced</span> · <span class="compat-behavior">In production the POINT of re-authentication is the <code>auth/requires-recent-login</code> gate: <code>updateEmail</code> / <code>updatePassword</code> / <code>deleteUser</code> refuse to run on a session whose sign-in is older than a few minutes, and re-auth is how you clear it. The sandbox does NOT enforce that gate — those three mutations already run on a session of any age (a pre-existing documented divergence), so there is no gate here for re-auth to clear. What re-auth DOES do here is real but narrower: it genuinely re-verifies the credential, mints a fresh token, and returns <code>operationType: 'reauthenticate'</code>. Code that calls it runs unchanged against prod, where it also clears the gate.</span></span></span></summary>
589
+ <div class="compat-evidence"><div class="compat-probe">Declared divergence, not a bug: inventing a recent-login gate would break every existing sandbox flow (which never re-authenticates) while proving nothing. <code>unit:sandbox-linking-reauth.test.ts</code> pins the behavior that IS provided (real credential re-verification + a fresh token).</div></div>
590
+ </details>
591
+ <details class="compat-row" data-status="ok">
592
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">reauthenticateWithPopup(user, provider, resolver?) / reauthenticateWithRedirect(...)</code><span class="compat-sub"><span class="compat-behavior">Route through the shared resolver seam with <code>authType: 'reauth'</code>. The identity the resolver produces MUST be the user being re-authenticated — a resolver that hands back a different uid throws <code>auth/user-mismatch</code>. That check is the entire security content of the flow; without it 're-authentication' would accept anyone.</span></span></span></summary>
593
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-linking-reauth.test.ts</code> — asserts the resolver sees <code>authType: 'reauth'</code> and that an impostor identity is rejected with <code>auth/user-mismatch</code>.</div></div>
594
+ </details>
595
+ </div>
596
+
597
+ ## Constants, credentials, and inert config tokens
598
+
599
+ <div class="compat-list">
600
+ <details class="compat-row" data-status="ok">
601
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAdditionalUserInfo(userCredential)</code><span class="compat-sub"><span class="compat-behavior">Returns <code>{ isNewUser, profile, providerId }</code>. For a fresh anonymous sign-in prod reports <code>{ isNewUser: true, providerId: null, profile: {} }</code> — <code>providerId</code> is NULL, not <code>'anonymous'</code>, because anonymous is not a federated provider. <code>isNewUser</code> is <code>true</code> for <code>createUserWithEmailAndPassword</code>, a first-time email-link sign-in, and a custom-token sign-in that minted the account; <code>false</code> for a returning <code>signInWithEmailAndPassword</code> and for every <code>link</code> / <code>reauthenticate</code>.</span></span></span></summary>
602
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED on the anonymous shape: <code>auth-additional-user-info-shape</code> captured <code>{isNewUser: true, providerId: null, profile: {}}</code> against prod. Replayed in <code>unit:oracle-conformance.test.ts</code>. Email create/sign-in + custom-token mint/return isNewUser matrix (providerId null per AUTH-B2): <code>unit:upstream-auth-probes.test.ts</code>. Email-link + link/reauth arms: <code>unit:sandbox-email-link.test.ts</code>, <code>unit:sandbox-linking-reauth.test.ts</code>.</div></div>
603
+ </details>
604
+ <details class="compat-row" data-status="ok">
605
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">ProviderId / SignInMethod / OperationType / ActionCodeOperation / AuthErrorCodes</code><span class="compat-sub"><span class="compat-behavior">The constant maps, value for value. <code>OperationType.SIGN_IN === 'signIn'</code>, <code>SignInMethod.EMAIL_LINK === 'emailLink'</code>, <code>ProviderId.GOOGLE === 'google.com'</code>, <code>ActionCodeOperation.PASSWORD_RESET === 'PASSWORD_RESET'</code>, and the 106-entry <code>AuthErrorCodes</code> map (<code>INVALID_OOB_CODE === 'auth/invalid-action-code'</code>, <code>PROVIDER_ALREADY_LINKED === 'auth/provider-already-linked'</code>, <code>NO_SUCH_PROVIDER === 'auth/no-such-provider'</code>, <code>USER_MISMATCH === 'auth/user-mismatch'</code>, …).</span></span></span></summary>
606
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED, snapshotted straight from the shipped SDK: <code>auth-mechanical-surface-constants</code> against firebase-js-sdk 12.13.0, replayed value-for-value in <code>unit:oracle-conformance.test.ts</code>. This matters more than it looks: consumer code COMPARES against these constants, so a mirror that got a string wrong would turn every such comparison into a silent <code>false</code> — a worse failure than the export simply being absent, because it typechecks and runs. NOTE: the capture's <code>persistenceTypes</code> block is deliberately NOT asserted — the harness runs under Node, where firebase/auth stubs the browser-only persistence tokens to <code>type: 'NONE'</code> (it reports 'NONE' even for <code>browserLocalPersistence</code>, which is unambiguously 'LOCAL'); asserting it would be asserting a harness artifact. See auth#178.</div></div>
607
+ </details>
608
+ <details class="compat-row" data-status="diverged">
609
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">signInWithCustomToken(auth, customToken)</code><span class="compat-sub"><span class="compat-behavior">Throws <code>auth/invalid-custom-token</code> for a malformed token and for the empty string. The sandbox accepts a token in the two shapes it can honestly read: a JSON (optionally base64url) <code>{uid, claims}</code> payload — exactly what <code>admin.auth().createCustomToken</code> signs, so the pyric-admin mint and this redeem compose — or a real three-part JWT whose payload segment carries <code>uid</code>/<code>sub</code>. The SIGNATURE IS NOT VERIFIED: the sandbox has no service-account key. The identity is created if it does not exist, and the credential carries <code>providerId: null</code> (custom-token sign-in is not a federated provider).</span></span></span></summary>
610
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED on the reject path: <code>auth-signinwithcustomtoken-invalid</code> captured <code>auth/invalid-custom-token</code> for both a malformed token and the empty string, replayed in <code>unit:oracle-conformance.test.ts</code>. Diverged on the ACCEPT path, declared rather than hidden: prod verifies an RS256 signature against the project's service-account key and the sandbox has no key, so it reads the token's claims WITHOUT verifying them. A forged token that prod would reject is accepted here. The happy path cannot be oracle-probed from a Web SDK client at all (it needs an Admin-SDK-signed JWT).</div></div>
611
+ </details>
612
+ <details class="compat-row" data-status="ok">
613
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">validatePassword(auth, password)</code><span class="compat-sub"><span class="compat-behavior">Returns a <code>PasswordValidationStatus</code> against the project's password policy, WITHOUT attempting a sign-up (so a UI can show live strength feedback). The sandbox's policy is <code>minPasswordLength: 6</code>, <code>maxPasswordLength: 4096</code>, <code>enforcementState: 'ENFORCE'</code>, with every character-class requirement UNSET — so a password this function calls valid is exactly one <code>createUserWithEmailAndPassword</code> will accept. The character-class fields are <code>undefined</code>, not <code>false</code>: upstream distinguishes 'not required' from 'required and unmet', and reporting <code>false</code> would claim the password failed a rule the project never had.</span></span></span></summary>
614
+ <div class="compat-evidence"><div class="compat-probe">ORACLE-BACKED: <code>auth-validatepassword-status-shape</code> captured prod's live policy (minPasswordLength 6, maxPasswordLength 4096, ENFORCE, character classes unset) and the status shape for a weak and a strong password. Replayed in <code>unit:oracle-conformance.test.ts</code>. The 6-char minimum agrees with the separately oracle-pinned <code>auth/weak-password</code> threshold on the create path, so the two surfaces draw the same line here exactly as they do in prod.</div></div>
615
+ </details>
616
+ <details class="compat-row" data-status="unsupported">
617
+ <summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Unsupported" title="Unsupported"></span><span class="compat-main"><code class="compat-api">fetchSignInMethodsForEmail(auth, email)</code><span class="compat-sub"><span class="compat-behavior">NOT MIRRORED — the one genuinely out-of-scope symbol in the auth surface. Deprecated upstream as a SECURITY RETRACTION: the shipped <code>@firebase/auth</code> declaration states the API 'returns an empty list when Email Enumeration Protection is enabled, irrespective of the number of authentication methods available for the given email', and that 'migrating off of this method is recommended as a security best-practice'. Enumeration protection is on by default, so against a modern project the production function always returns <code>[]</code>.</span></span></span></summary>
618
+ <div class="compat-evidence"><div class="compat-probe">DISPOSITION, with the reasoning recorded: mirroring it would mean either reproducing a function that always returns nothing (a no-op that misleads whoever calls it) or implementing the pre-deprecation behavior the sandbox COULD provide — which would be strictly worse, because agent code would work against the mirror and then silently get <code>[]</code> in prod and take the wrong branch. A mirror that is MORE capable than production here is a trap, not a feature. Same criterion the two-tier policy already applies to Imagen in <code>firebase/ai</code>. The disposition rests on the SDK's own type declaration (a primary source), NOT on the oracle probe: <code>auth-fetchsigninmethodsforemail-deprecated</code> came back <code>auth/operation-not-allowed</code> because the oracle project has the Email/Password provider disabled, and that capture proves nothing either way. Deny-list entry (tier <code>out-of-scope</code>) in <code>packages/conformance/src/surface-denylist.ts</code>.</div></div>
619
+ </details>
620
+ <details class="compat-row" data-status="diverged">
621
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">inMemoryPersistence / browserSessionPersistence / browserLocalPersistence / indexedDBLocalPersistence / browserCookiePersistence / browserPopupRedirectResolver / debugErrorMap / prodErrorMap</code><span class="compat-sub"><span class="compat-behavior">Inert configuration tokens, accepted so the idiomatic <code>initializeAuth(app, { persistence: indexedDBLocalPersistence })</code> and <code>setPersistence(auth, browserCookiePersistence)</code> compile, run, and behave identically. The <code>.type</code> discriminant matches upstream exactly (<code>NONE</code> / <code>SESSION</code> / <code>LOCAL</code> / <code>LOCAL</code> / <code>COOKIE</code>) because consumer code branches on it. <code>browserPopupRedirectResolver</code> is accepted and ignored — the sandbox has its own first-class pluggable equivalent (<code>sandbox.setAuthFlowResolver</code>). <code>prodErrorMap</code> is accepted and DELIBERATELY not honored: installing it upstream strips error messages, and doing that in a sandbox whose purpose is to tell a developer what went wrong would be actively hostile.</span></span></span></summary>
622
+ <div class="compat-evidence"><div class="compat-probe">In an in-memory sandbox the persistence CHOICE has no observable consequence, so accepting the token and recording the mode is the honest behavior — the same inert-token pattern <code>pyric/firestore</code> uses for its cache-factory tokens. <code>unit:types.test.ts</code> + <code>unit:sandbox-email-link.test.ts</code> cover the exports. Oracle caveat recorded on auth#172: the <code>persistenceTypes</code> block of <code>auth-mechanical-surface-constants</code> reports <code>NONE</code> for every token including <code>browserLocalPersistence</code> — a Node-build stub artifact, not the browser contract, so the <code>.type</code> values here follow the documented <code>Persistence.type</code> union instead.</div></div>
623
+ </details>
624
+ <details class="compat-row" data-status="diverged">
625
+ <summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">revokeAccessToken(auth, token)</code><span class="compat-sub"><span class="compat-behavior">Accepted no-op. In production this reaches OUTSIDE Firebase entirely — it tells the identity provider (in practice Apple) to revoke an OAuth access token, which is a call landing on Apple's servers. There is no external IdP behind a sandbox sign-in, so there is no token out there to revoke and nothing this call could truthfully do. It resolves (so the account-deletion flow Apple requires an app to ship runs end to end against the sandbox) and changes no sandbox state (because claiming otherwise would be a lie).</span></span></span></summary>
626
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:types.test.ts</code> — the export resolves. Deliberately not oracle-probed: a successful probe would revoke a real token at a real IdP, which is a side effect on someone else's system that a conformance run has no business causing.</div></div>
627
+ </details>
628
+ <details class="compat-row" data-status="ok">
629
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">AuthCredential / EmailAuthCredential / OAuthCredential</code><span class="compat-sub"><span class="compat-behavior">Real classes, not markers. <code>EmailAuthProvider.credential(email, password)</code> returns an <code>EmailAuthCredential</code> that CARRIES THE SECRET — which is what makes <code>linkWithCredential</code>, <code>reauthenticateWithCredential</code>, and <code>signInWithCredential</code> decidable in the sandbox with no resolver and no mock (the backend already stores and verifies passwords). <code>credentialWithLink</code> carries the link instead, and <code>signInMethod</code> discriminates (<code>'password'</code> vs <code>'emailLink'</code>). The backing secret is non-enumerable, so a spread or <code>Object.keys</code> walk does not pick it up; <code>toJSON()</code> DOES carry it, matching upstream (whose <code>fromJSON</code> needs it to round-trip). <code>OAuthCredential</code> carries the IdP tokens, which the sandbox cannot verify — those flows keep going through the resolver seam.</span></span></span></summary>
630
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:sandbox-linking-reauth.test.ts</code> — pins the secret-carrying behavior, the enumerable/toJSON split (so nobody 'hardens' it into a divergence later), and that a real email credential now signs in via <code>signInWithCredential</code> instead of throwing the sandbox-only <code>auth/no-mock-configured</code> it used to.</div></div>
631
+ </details>
632
+ <details class="compat-row" data-status="ok">
633
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">TwitterAuthProvider / SAMLAuthProvider</code><span class="compat-sub"><span class="compat-behavior">Provider marker classes. <code>TwitterAuthProvider.credential(token, secret)</code> takes a token AND a secret — Twitter is the one OAuth 1.0a provider in the set, where the OAuth 2.0 providers take a single access token. <code>SAMLAuthProvider</code>'s constructor ENFORCES the <code>saml.</code> providerId prefix (throwing <code>auth/argument-error</code> otherwise), because that id is what routes an assertion to the right configured IdP and a typo there would silently target nothing. SAML has no client-constructible credential, so the class has no <code>credential()</code> factory.</span></span></span></summary>
634
+ <div class="compat-evidence"><div class="compat-probe"><code>unit:types.test.ts</code> — construction, PROVIDER_ID values, and the SAML prefix guard.</div></div>
635
+ </details>
636
+ <details class="compat-row" data-status="ok">
637
+ <summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">User.providerData</code><span class="compat-sub"><span class="compat-behavior">One entry PER LINKED PROVIDER, read from the identity's stored record. Previously the sandbox synthesized a single <code>{providerId: 'password'}</code> entry for EVERY non-anonymous user, so a Google popup sign-in reported its provider as <code>'password'</code> and a linked account could never show more than one. Consumer code branches on this array (that is what it is for — 'is this account linked to Google?'), so the synthesized version was actively misleading. Empty for anonymous users; after <code>unlink</code> of the last provider it is genuinely empty (it does not resurrect the removed provider).</span></span></span></summary>
638
+ <div class="compat-evidence"><div class="compat-probe">Was a BUG until this climb, and worth naming as one: the array was fabricated, not read. Now fixed and locked. <code>unit:sandbox-linking-reauth.test.ts</code> now pins that a Google link surfaces <code>google.com</code> in <code>providerData</code>, that <code>unlink</code> shrinks it, and that unlinking the last provider leaves it empty rather than falling back to a synthesized <code>'password'</code> entry.</div></div>
639
+ </details>
640
+ </div>
641
+
642
+ ## Deny-list (intentionally NOT shimmed)
643
+
644
+ These exist in `firebase/auth` but the sandbox does not mirror them.
645
+
646
+ The list is much shorter than it was. The auth resolver climb removed three whole
647
+ families from it — account linking (`linkWith*` / `unlink`), re-authentication
648
+ (`reauthenticateWith*`), and the email-link / action-code family
649
+ (`sendSignInLinkToEmail`, `signInWithEmailLink`, `applyActionCode`, `ActionCodeURL`, …) —
650
+ by building them, not by re-arguing them. Their old reasons ("v0 scope", "email-link
651
+ flows require an SMTP path") were never good ones: mocking external infrastructure is
652
+ the product, and needing an inbox is not the same as being unmodelable.
653
+
654
+ | Name | Reason |
655
+ |---|---|
656
+ | `fetchSignInMethodsForEmail` | **Out of scope, not deferred.** Deprecated upstream as a security retraction: the shipped `@firebase/auth` declaration says it "returns an empty list when Email Enumeration Protection is enabled, irrespective of the number of authentication methods available", and that "migrating off of this method is recommended as a security best-practice". Enumeration protection is on by default, so in a modern project the production function always returns `[]`. Mirroring the pre-deprecation behavior would make the sandbox MORE capable than prod — agent code would work here and then silently take the wrong branch in production. See row #175. |
657
+ | `multiFactor(user)` / MFA / phone / reCAPTCHA APIs | Deferred, not out of scope — reCAPTCHA and SMS are external infrastructure pyric can mock through the same resolver seam the OAuth and email families now use, and TOTP is pure algorithm work. |
658
+ | `updatePhoneNumber` | Deferred with the rest of the phone family. |
659
+ | `setLanguageCode` (Auth method) | i18n surface. (`useDeviceLanguage` is mirrored as an accepted no-op.) |
660
+ | `User.toJSON()` | Serialization the sandbox doesn't model (AUTH-GAP). (`User.reload()` / `User.delete()` are mirrored via the top-level `reload(user)` / `deleteUser(user)`.) |
661
+ | `User.metadata` / `User.refreshToken` / `User.tenantId` | Not tracked by the sandbox; documented per AUTH-GAP. |
662
+ | Positional listener `error` / `complete` args on `onAuthStateChanged` / `onIdTokenChanged` | Sandbox observers never error/complete (synchronous in-memory fan-out); pass the `{ next, error, complete }` observer object if you need those handlers. Production listener behavior remains owned by the unchanged `firebase/auth` import. |
663
+
664
+ ---
665
+
666
+ ## What the oracle could not reach, and why
667
+
668
+ Recorded here because a coverage number that hides its own blind spots is worth less
669
+ than a smaller one that names them.
670
+
671
+ **The oracle project currently has the Email/Password sign-in provider DISABLED.**
672
+ Anonymous sign-in works; every email/password path returns `auth/operation-not-allowed`.
673
+ That blocked oracle-backing for the linking and reauthentication families outright (an
674
+ email credential cannot be minted there at all) and for the server-side half of the
675
+ email-link family. Those probes were still written, still run, and their captures are
676
+ still committed — showing `auth/operation-not-allowed` — rather than being quietly
677
+ dropped. The rows they would have backed are born `unit-backed` and say so in their
678
+ evidence column. See the `NOT_APPLICABLE` block in
679
+ `packages/pyric/test/auth/oracle-conformance.test.ts`.
680
+
681
+ This also means the EXISTING email/password observations in this surface
682
+ (`auth-row-18-invalid-email`, `auth-row-19-weak-password`, `auth-email-already-in-use`,
683
+ `auth-user-not-found`, `auth-wrong-password`, …) can no longer be RE-captured against
684
+ this project. They were captured when the provider was on; re-running the rig today
685
+ would fail them. That is pre-existing infrastructure decay, surfaced here, not caused
686
+ by this climb.
687
+
688
+ To lift the whole set to oracle-backed: enable Email/Password sign-in on the oracle
689
+ project (Authentication -> Sign-in method) and re-run the probes, which are already
690
+ written and committed.
691
+
692
+ **Genuinely unobservable, for anyone.** No probe and no test can read a human's inbox.
693
+ The email round trip is therefore closed in the sandbox by the mail outbox
694
+ (`sandbox.takeAuthMail`), which hands the program the same real, single-use code a
695
+ human would have clicked — the analog of `mockSignInResult` for the email family. What
696
+ IS fully observable, and is oracle-pinned exactly, is the pure client-side half:
697
+ the `ActionCodeURL` parse contract, the `isSignInWithEmailLink` predicate, and the
698
+ `ActionCodeSettings` validation — none of which touch a network at all.
699
+
700
+ ---
701
+
702
+ ## Visible gaps to address next
703
+
704
+ Rows currently marked **?** (need explicit probes):
705
+
706
+ - #3 canonical production package resolution — landing once the
707
+ empirical oracle harness (`packages/conformance/src/run.ts`) captures the
708
+ observation against a real Firebase project. Harness is in
709
+ place; needs the `PYRIC_ORACLE_FIREBASE_CONFIG` env var pointed
710
+ at a dedicated oracle project before observations can be
711
+ committed. See `packages/conformance/docs/oracle-project-setup.md` for project setup.
712
+ - #69 (ordering only) — disabled-vs-wrong-password precedence on
713
+ `signInWithEmailAndPassword`. Sandbox checks disabled BEFORE the
714
+ password compare (anti-probing best-known semantics); needs an
715
+ oracle capture against a disabled prod account to lock the order.
716
+ - #68 (prod side) — `IdTokenResult.signInProvider` values per flow are
717
+ documented SDK behavior but not yet oracle-captured.
718
+
719
+ Rows **locked by the empirical oracle harness** (committed observations under `packages/conformance/observations/auth/`, captured against the `blockingfun` project):
720
+
721
+ - #10 `onAuthStateChanged` exactly-one-per-transition — oracle confirmed signIn → signOut → signIn each produced exactly 1 fire (in addition to the initial null fire on subscribe); sandbox matches.
722
+ - #14 `signInWithEmailAndPassword` user-not-found — oracle confirmed prod still emits `auth/user-not-found` (not the newer `auth/invalid-credential`); sandbox matches.
723
+ - #17 `signInWithEmailAndPassword` fires once with the new user — oracle confirmed `firesForSignIn: 1` with the signed-in uid against prod; sandbox matches.
724
+ - #18 `auth/invalid-email` format-validation — oracle confirmed `createUserWithEmailAndPassword(auth, 'not-an-email', …)` throws `auth/invalid-email` against prod; sandbox now matches (validation lives in `sandbox-backend.ts` and runs before user-DB lookup on both `signIn` and `createUser` paths).
725
+ - #19 `auth/weak-password` strength-validation — oracle confirmed prod rejects 3-char passwords with `auth/weak-password` and message `Password should be at least 6 characters`; sandbox now matches the same 6-char threshold (validated on `createUser` only — `signIn` lets in previously-seeded weak passwords, mirroring prod).
726
+ - #24 `createUserWithEmailAndPassword` fires once with the new user — oracle confirmed `firesForCreate: 1` with the newly-created uid against prod; sandbox matches.
727
+ - #26 `signOut` fires null exactly once — oracle confirmed the signOut transition produced exactly 1 fire with `uid === null`; sandbox matches.
728
+ - #30 `onAuthStateChanged` on every subsequent identity change — oracle confirmed signIn → signOut → signIn → signOut all 4 fired exactly once; sandbox matches.
729
+ - #32 `Unsubscribe` removes the observer — oracle confirmed post-unsubscribe signOut+signIn+signOut produced zero further fires; sandbox matches.
730
+ - #33 multiple subscribers all fire — oracle confirmed two listeners registered back-to-back both fire on each transition; sandbox matches.
731
+ - #36 observer object form (`{next, error, complete}`) — oracle confirmed prod accepts both forms; both fire on every transition; sandbox matches.
732
+ - #21 `createUserWithEmailAndPassword` `operationType` — oracle confirmed prod returns `'signIn'` (NOT `'register'`); sandbox matches.
733
+ - #22 `createUserWithEmailAndPassword` duplicate email — oracle confirmed prod emits `auth/email-already-in-use`; sandbox matches.
734
+ - #25 `signOut` synchronous-null — oracle confirmed prod sets `auth.currentUser` to `null` in the synchronous continuation immediately after `await signOut(auth)` resolves; sandbox matches.
735
+ - #27 `signOut` idempotent — oracle confirmed prod is a no-op, no listener fire on the redundant call.
736
+ - #29 `onAuthStateChanged` initial-fire timing — oracle confirmed prod fires the initial value on the microtask after subscribe, not synchronously; sandbox matches.
737
+ - #31 `onAuthStateChanged` no-dup on sync transition — oracle baseline: prod has no synchronous state-change API, so the dedup case is sandbox-only. Async subscribe + signIn produces two natural fires (initial null, then user).
738
+ - #35 `onAuthStateChanged` throwing-observer isolation — oracle confirmed a throwing observer does not block subsequent observers; sandbox matches.
739
+ - #37 `onAuthStateChanged` same-user no-double-fire — oracle confirmed calling `signInAnonymously` twice in a row returns the same uid and the second call does NOT produce a fresh listener fire; sandbox matches.
740
+ - #38 `onIdTokenChanged` user-change fires — oracle confirmed every signIn/signOut transition produces exactly one fire; sandbox matches.
741
+ - #39 `onIdTokenChanged` on forced refresh — oracle confirmed prod fires the listener after `getIdToken(true)`; sandbox matches (divergence closed).
742
+ - #40 `onIdTokenChanged` initial-fire parity with `onAuthStateChanged` — oracle confirmed both listeners share the microtask-deferred initial-fire timing; sandbox matches.
743
+ - #55 `getIdToken(forceRefresh)` — oracle confirmed prod returns a different token string after a forced refresh and a subsequent non-forced read returns the cached new token; sandbox matches (divergence closed).
744
+
745
+ Rows currently marked **—** that we might want to fill (rough priority):
746
+
747
+ 1. #57 `user.emailVerified` — used by gating logic in real apps
748
+ 2. #58-61 `user.metadata` / instance `User.reload`/`User.delete`/`toJSON` — full User *instance-method* shape parity (top-level `reload`/`deleteUser`/`updateProfile` are already ✓)
749
+
750
+ Rows currently marked **⚠** that we might want to upgrade to **✓**
751
+ (by aligning the sandbox to prod or by formally documenting the
752
+ divergence in `feature-matrix.md`):
753
+
754
+ - #7 anonymous uid format
755
+ - #12, #28 persistence story
756
+ - #43 setPersistence respect
757
+ - #48 popup window
758
+
759
+
760
+ ## Current gaps
761
+
762
+ ### Documented divergences
763
+
764
+ Known differences between Pyric and production Firebase. Each remains tracked as a non-conforming row.
765
+
766
+ <div class="compat-list compat-list--plain">
767
+ <div class="compat-row">
768
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub">Auto-generates a uid for fresh sign-ins (sandbox format: <code>anonymous-{N}</code>)</span></span></div>
769
+ </div>
770
+ <div class="compat-row">
771
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">signInAnonymously(auth)</code><span class="compat-sub">Anonymous users persist across page reload via configured <code>Persistence</code> (prod only — sandbox has no persistence layer)</span></span></div>
772
+ </div>
773
+ <div class="compat-row">
774
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">signInWithEmailAndPassword(auth, email, password)</code><span class="compat-sub">An EMPTY password throws <code>auth/missing-password</code> (message "A non-empty password must be provided"), fired before the user-DB lookup so it can't be used to enumerate seeded emails. Upstream maps the <code>MISSING_PASSWORD</code> server error (<code>core/errors.ts:92,282,563</code>). ⚠ best-known semantics — message text not yet captured against a live project (STOP-flagged for an oracle pass; the <code>.code</code> is the load-bearing part).</span></span></div>
775
+ </div>
776
+ <div class="compat-row">
777
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">signOut(auth)</code><span class="compat-sub">Clears the active session's persistence in prod; sandbox has no persistence</span></span></div>
778
+ </div>
779
+ <div class="compat-row">
780
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">setPersistence(auth, persistence)</code><span class="compat-sub">Actually changes where the auth state is persisted</span></span></div>
781
+ </div>
782
+ <div class="compat-row">
783
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub">Opens a popup window in prod</span></span></div>
784
+ </div>
785
+ <div class="compat-row">
786
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">beforeAuthStateChanged(auth, callback, onAbort?)</code><span class="compat-sub"><code>sandbox.setUser</code> (the sandbox-only test driver) BYPASSES the gate entirely — it is a raw identity force with no prod analog (same bypass it already has for provider enforcement / <code>signInProvider</code> tracking), so no registered <code>beforeAuthStateChanged</code> callback runs and none can block it.</span></span></div>
787
+ </div>
788
+ <div class="compat-row">
789
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">beforeAuthStateChanged(auth, callback, onAbort?)</code><span class="compat-sub">Served-worker path (SharedWorker-backed auth in <code>@pyric/cli</code>): the worker owns the shared user pool and commits transitions on its own side of the port, so a page-local <code>beforeAuthStateChanged</code> registration can't actually gate a worker-driven sign-in. Rather than silently accept a callback that could never run, registering THROWS immediately (<code>auth/operation-not-supported-in-this-environment</code>) — same defensive pattern as <code>signInWithCredential</code> over the worker.</span></span></div>
790
+ </div>
791
+ <div class="compat-row">
792
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">updateEmail(user, newEmail)</code><span class="compat-sub">Changes the stored email (via the same path as <code>sandbox.updateUser</code>, rejecting <code>auth/email-already-in-use</code> / <code>auth/invalid-email</code>) and mutates the held <code>user</code> in place, so the next sign-in resolves against the new email. Leniency vs prod: the sandbox does NOT enforce <code>auth/requires-recent-login</code> and is not routed through <code>verifyBeforeUpdateEmail</code> (which the real SDK requires when email-enumeration protection is on)</span></span></div>
793
+ </div>
794
+ <div class="compat-row">
795
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">updatePassword(user, newPassword)</code><span class="compat-sub">Sets the stored password (validated for strength). The sandbox DOES store and verify passwords, so this is a real mutation: a subsequent <code>signInWithEmailAndPassword</code> with the new password succeeds and the old one throws <code>auth/wrong-password</code>. Leniency vs prod: no <code>auth/requires-recent-login</code> enforcement</span></span></div>
796
+ </div>
797
+ <div class="compat-row">
798
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">useDeviceLanguage(auth)</code><span class="compat-sub">Accepted no-op — the sandbox has no device locale to read, so there is no language to set; accepted so init code that calls it compiles + runs</span></span></div>
799
+ </div>
800
+ <div class="compat-row">
801
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">sendSignInLinkToEmail</code><span class="compat-sub">Prod rejects a continue URL whose domain is not on the project's authorized-domains list. The sandbox has NO domain allowlist and does not invent one: it accepts any parseable continue URL. A continue URL that would be rejected in production is accepted here.</span></span></div>
802
+ </div>
803
+ <div class="compat-row">
804
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">reauthenticate*</code><span class="compat-sub">In production the POINT of re-authentication is the <code>auth/requires-recent-login</code> gate: <code>updateEmail</code> / <code>updatePassword</code> / <code>deleteUser</code> refuse to run on a session whose sign-in is older than a few minutes, and re-auth is how you clear it. The sandbox does NOT enforce that gate — those three mutations already run on a session of any age (a pre-existing documented divergence), so there is no gate here for re-auth to clear. What re-auth DOES do here is real but narrower: it genuinely re-verifies the credential, mints a fresh token, and returns <code>operationType: 'reauthenticate'</code>. Code that calls it runs unchanged against prod, where it also clears the gate.</span></span></div>
805
+ </div>
806
+ <div class="compat-row">
807
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">signInWithCustomToken(auth, customToken)</code><span class="compat-sub">Throws <code>auth/invalid-custom-token</code> for a malformed token and for the empty string. The sandbox accepts a token in the two shapes it can honestly read: a JSON (optionally base64url) <code>{uid, claims}</code> payload — exactly what <code>admin.auth().createCustomToken</code> signs, so the pyric-admin mint and this redeem compose — or a real three-part JWT whose payload segment carries <code>uid</code>/<code>sub</code>. The SIGNATURE IS NOT VERIFIED: the sandbox has no service-account key. The identity is created if it does not exist, and the credential carries <code>providerId: null</code> (custom-token sign-in is not a federated provider).</span></span></div>
808
+ </div>
809
+ <div class="compat-row">
810
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">inMemoryPersistence / browserSessionPersistence / browserLocalPersistence / indexedDBLocalPersistence / browserCookiePersistence / browserPopupRedirectResolver / debugErrorMap / prodErrorMap</code><span class="compat-sub">Inert configuration tokens, accepted so the idiomatic <code>initializeAuth(app, { persistence: indexedDBLocalPersistence })</code> and <code>setPersistence(auth, browserCookiePersistence)</code> compile, run, and behave identically. The <code>.type</code> discriminant matches upstream exactly (<code>NONE</code> / <code>SESSION</code> / <code>LOCAL</code> / <code>LOCAL</code> / <code>COOKIE</code>) because consumer code branches on it. <code>browserPopupRedirectResolver</code> is accepted and ignored — the sandbox has its own first-class pluggable equivalent (<code>sandbox.setAuthFlowResolver</code>). <code>prodErrorMap</code> is accepted and DELIBERATELY not honored: installing it upstream strips error messages, and doing that in a sandbox whose purpose is to tell a developer what went wrong would be actively hostile.</span></span></div>
811
+ </div>
812
+ <div class="compat-row">
813
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">revokeAccessToken(auth, token)</code><span class="compat-sub">Accepted no-op. In production this reaches OUTSIDE Firebase entirely — it tells the identity provider (in practice Apple) to revoke an OAuth access token, which is a call landing on Apple's servers. There is no external IdP behind a sandbox sign-in, so there is no token out there to revoke and nothing this call could truthfully do. It resolves (so the account-deletion flow Apple requires an app to ship runs end to end against the sandbox) and changes no sandbox state (because claiming otherwise would be a lie).</span></span></div>
814
+ </div>
815
+ </div>
816
+
817
+ ### Unsupported
818
+
819
+ Tracked behavior that is not implemented in the current contract.
820
+
821
+ <div class="compat-list compat-list--plain">
822
+ <div class="compat-row">
823
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">signInWithPopup(auth, provider)<code> / </code>signInWithCredential(auth, credential)</code><span class="compat-sub">Cancels with <code>auth/popup-closed-by-user</code> when the user dismisses the popup (prod)</span></span></div>
824
+ </div>
825
+ <div class="compat-row">
826
+ <div class="compat-line"><span class="compat-main"><span class="compat-behavior">Custom scopes / params / language code</span></span></div>
827
+ </div>
828
+ <div class="compat-row">
829
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><code>user.metadata.creationTime</code> / <code>lastSignInTime</code></span></span></div>
830
+ </div>
831
+ <div class="compat-row">
832
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">User` methods</code><span class="compat-sub"><code>user.reload()</code> / <code>user.delete()</code> / <code>user.toJSON()</code> / <code>user.refreshToken</code> / <code>user.tenantId</code></span></span></div>
833
+ </div>
834
+ <div class="compat-row">
835
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">fetchSignInMethodsForEmail(auth, email)</code><span class="compat-sub">NOT MIRRORED — the one genuinely out-of-scope symbol in the auth surface. Deprecated upstream as a SECURITY RETRACTION: the shipped <code>@firebase/auth</code> declaration states the API 'returns an empty list when Email Enumeration Protection is enabled, irrespective of the number of authentication methods available for the given email', and that 'migrating off of this method is recommended as a security best-practice'. Enumeration protection is on by default, so against a modern project the production function always returns <code>[]</code>.</span></span></div>
836
+ </div>
837
+ </div>
838
+
839
+ ### Unverified
840
+
841
+ Tracked behavior whose available evidence does not yet establish the production result.
842
+
843
+ <div class="compat-list compat-list--plain">
844
+ <div class="compat-row">
845
+ <div class="compat-line"><span class="compat-main"><code class="compat-api">getAuth(target)</code><span class="compat-sub">Without sandbox package swapping, canonical <code>firebase/auth</code> imports remain Firebase and never enter this mirror</span></span></div>
846
+ </div>
847
+ </div>