@pyric/cli 0.1.0-alpha.30 → 0.1.0-alpha.30.2

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 (169) hide show
  1. package/dist/assurance/.generated/conformance-verdicts.d.ts +8 -0
  2. package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
  3. package/dist/assurance/.generated/conformance-verdicts.js +8 -0
  4. package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
  5. package/dist/bridge/server/peer.d.ts +10 -0
  6. package/dist/bridge/server/peer.d.ts.map +1 -1
  7. package/dist/bridge/server/peer.js +46 -1
  8. package/dist/bridge/server/peer.js.map +1 -1
  9. package/dist/cli/serve.d.ts +2 -0
  10. package/dist/cli/serve.d.ts.map +1 -1
  11. package/dist/cli/serve.js +13 -2
  12. package/dist/cli/serve.js.map +1 -1
  13. package/dist/conformance/.generated/can-i-use-browser.js +2 -2
  14. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  15. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  16. package/dist/conformance/.generated/can-i-use.js +1 -1
  17. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  18. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
  19. package/dist/conformance/.generated/conformance-docs.js +1 -1
  20. package/dist/conformance/.generated/conformance-docs.js.map +1 -1
  21. package/dist/register/app-bridge-admin-app-check.d.ts +3 -0
  22. package/dist/register/app-bridge-admin-app-check.d.ts.map +1 -0
  23. package/dist/register/app-bridge-admin-app-check.js +43 -0
  24. package/dist/register/app-bridge-admin-app-check.js.map +1 -0
  25. package/dist/register/app-bridge-app-check.d.ts +4 -0
  26. package/dist/register/app-bridge-app-check.d.ts.map +1 -1
  27. package/dist/register/app-bridge-app-check.js +4 -0
  28. package/dist/register/app-bridge-app-check.js.map +1 -1
  29. package/dist/register/app-bridge-shadow.d.ts +4 -2
  30. package/dist/register/app-bridge-shadow.d.ts.map +1 -1
  31. package/dist/register/app-bridge-shadow.js +29 -5
  32. package/dist/register/app-bridge-shadow.js.map +1 -1
  33. package/dist/register/index.d.ts.map +1 -1
  34. package/dist/register/index.js +21 -0
  35. package/dist/register/index.js.map +1 -1
  36. package/dist/serve/ai-diagnostics-relay.d.ts +4 -1
  37. package/dist/serve/ai-diagnostics-relay.d.ts.map +1 -1
  38. package/dist/serve/ai-diagnostics-relay.js +3 -0
  39. package/dist/serve/ai-diagnostics-relay.js.map +1 -1
  40. package/dist/serve/ai-proxy.d.ts +21 -1
  41. package/dist/serve/ai-proxy.d.ts.map +1 -1
  42. package/dist/serve/ai-proxy.js +39 -14
  43. package/dist/serve/ai-proxy.js.map +1 -1
  44. package/dist/serve/ai-terminal-blocks.d.ts +6 -4
  45. package/dist/serve/ai-terminal-blocks.d.ts.map +1 -1
  46. package/dist/serve/ai-terminal-blocks.js +7 -4
  47. package/dist/serve/ai-terminal-blocks.js.map +1 -1
  48. package/dist/serve/ai-terminal-text.d.ts +8 -0
  49. package/dist/serve/ai-terminal-text.d.ts.map +1 -1
  50. package/dist/serve/ai-terminal-text.js +10 -0
  51. package/dist/serve/ai-terminal-text.js.map +1 -1
  52. package/dist/serve/bundler.d.ts +40 -0
  53. package/dist/serve/bundler.d.ts.map +1 -1
  54. package/dist/serve/bundler.js +133 -2
  55. package/dist/serve/bundler.js.map +1 -1
  56. package/dist/serve/entries/ai.d.ts.map +1 -1
  57. package/dist/serve/entries/ai.js +28 -26
  58. package/dist/serve/entries/ai.js.map +1 -1
  59. package/dist/serve/entries/app-ai-passthrough.d.ts +4 -4
  60. package/dist/serve/entries/app-ai-passthrough.d.ts.map +1 -1
  61. package/dist/serve/entries/app-ai-passthrough.js +8 -52
  62. package/dist/serve/entries/app-ai-passthrough.js.map +1 -1
  63. package/dist/serve/firebase-module-swap.d.ts +22 -0
  64. package/dist/serve/firebase-module-swap.d.ts.map +1 -1
  65. package/dist/serve/firebase-module-swap.js +51 -3
  66. package/dist/serve/firebase-module-swap.js.map +1 -1
  67. package/dist/serve/gemini-error-redaction.d.ts +6 -0
  68. package/dist/serve/gemini-error-redaction.d.ts.map +1 -0
  69. package/dist/serve/gemini-error-redaction.js +61 -0
  70. package/dist/serve/gemini-error-redaction.js.map +1 -0
  71. package/dist/serve/gemini-proxy.d.ts +40 -0
  72. package/dist/serve/gemini-proxy.d.ts.map +1 -0
  73. package/dist/serve/gemini-proxy.js +105 -0
  74. package/dist/serve/gemini-proxy.js.map +1 -0
  75. package/dist/serve/hosted/method-protocol.d.ts +4 -4
  76. package/dist/serve/html-injection.d.ts +2 -0
  77. package/dist/serve/html-injection.d.ts.map +1 -1
  78. package/dist/serve/html-injection.js +4 -2
  79. package/dist/serve/html-injection.js.map +1 -1
  80. package/dist/serve/namespace.d.ts +9 -0
  81. package/dist/serve/namespace.d.ts.map +1 -1
  82. package/dist/serve/namespace.js +71 -15
  83. package/dist/serve/namespace.js.map +1 -1
  84. package/dist/serve/sandbox-session.d.ts +3 -0
  85. package/dist/serve/sandbox-session.d.ts.map +1 -1
  86. package/dist/serve/sandbox-session.js +1 -0
  87. package/dist/serve/sandbox-session.js.map +1 -1
  88. package/dist/serve/site-ui/docs/ai-compat/index.html +29 -10
  89. package/dist/serve/site-ui/docs/ai-compat.md +30 -9
  90. package/dist/serve/site-ui/docs/app-check-compat/index.html +42 -18
  91. package/dist/serve/site-ui/docs/app-check-compat.md +42 -18
  92. package/dist/serve/site-ui/docs/auth-compat/index.html +4 -4
  93. package/dist/serve/site-ui/docs/auth-compat.md +4 -4
  94. package/dist/serve/site-ui/docs/build/ai-logic/index.html +21 -5
  95. package/dist/serve/site-ui/docs/build/ai-logic.md +23 -2
  96. package/dist/serve/site-ui/docs/database-compat/index.html +4 -4
  97. package/dist/serve/site-ui/docs/database-compat.md +4 -4
  98. package/dist/serve/site-ui/docs/firestore-compat/index.html +4 -4
  99. package/dist/serve/site-ui/docs/firestore-compat.md +4 -4
  100. package/dist/serve/site-ui/docs/index.json +10 -0
  101. package/dist/serve/site-ui/docs/messaging-compat/index.html +4 -4
  102. package/dist/serve/site-ui/docs/messaging-compat.md +4 -4
  103. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +4 -0
  104. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api.md +5 -0
  105. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-app-check-reference-api/index.html +2 -0
  106. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-app-check-reference-api.md +3 -0
  107. package/dist/serve/site-ui/docs/storage-compat/index.html +5 -5
  108. package/dist/serve/site-ui/docs/storage-compat.md +5 -5
  109. package/dist/serve/site-ui/docs/tutorial/index.html +11 -1
  110. package/dist/serve/site-ui/docs/tutorial.md +12 -1
  111. package/dist/serve/site-ui/examples/chess/index.html +1 -1
  112. package/dist/serve/vite-sandbox-generation.d.ts.map +1 -1
  113. package/dist/serve/vite-sandbox-generation.js +2 -0
  114. package/dist/serve/vite-sandbox-generation.js.map +1 -1
  115. package/dist/serve/worker/ai-proxy-paths.d.ts +32 -0
  116. package/dist/serve/worker/ai-proxy-paths.d.ts.map +1 -0
  117. package/dist/serve/worker/ai-proxy-paths.js +20 -0
  118. package/dist/serve/worker/ai-proxy-paths.js.map +1 -0
  119. package/dist/serve/worker/client/ai.d.ts +3 -1
  120. package/dist/serve/worker/client/ai.d.ts.map +1 -1
  121. package/dist/serve/worker/client/ai.js +3 -0
  122. package/dist/serve/worker/client/ai.js.map +1 -1
  123. package/dist/serve/worker/entry.js +2 -0
  124. package/dist/serve/worker/entry.js.map +1 -1
  125. package/dist/serve/worker/host-ai.d.ts +4 -5
  126. package/dist/serve/worker/host-ai.d.ts.map +1 -1
  127. package/dist/serve/worker/host-ai.js +19 -23
  128. package/dist/serve/worker/host-ai.js.map +1 -1
  129. package/dist/serve/worker/host-context.d.ts +13 -4
  130. package/dist/serve/worker/host-context.d.ts.map +1 -1
  131. package/dist/serve/worker/host-context.js.map +1 -1
  132. package/dist/serve/worker/inbound-validation/operation-arguments.d.ts.map +1 -1
  133. package/dist/serve/worker/inbound-validation/operation-arguments.js +11 -0
  134. package/dist/serve/worker/inbound-validation/operation-arguments.js.map +1 -1
  135. package/dist/serve/worker/protocol.d.ts +13 -0
  136. package/dist/serve/worker/protocol.d.ts.map +1 -1
  137. package/dist/serve/worker/protocol.js.map +1 -1
  138. package/package.json +4 -4
  139. package/src/assurance/.generated/conformance-verdicts.ts +8 -0
  140. package/src/bridge/server/peer.ts +49 -1
  141. package/src/cli/serve.ts +14 -2
  142. package/src/conformance/.generated/can-i-use-browser.ts +2 -2
  143. package/src/conformance/.generated/can-i-use.ts +1 -1
  144. package/src/conformance/.generated/conformance-docs.ts +1 -1
  145. package/src/register/app-bridge-admin-app-check.ts +46 -0
  146. package/src/register/app-bridge-app-check.ts +4 -0
  147. package/src/register/app-bridge-shadow.ts +35 -5
  148. package/src/register/index.ts +23 -0
  149. package/src/serve/ai-diagnostics-relay.ts +6 -1
  150. package/src/serve/ai-proxy.ts +43 -14
  151. package/src/serve/ai-terminal-blocks.ts +8 -4
  152. package/src/serve/ai-terminal-text.ts +11 -0
  153. package/src/serve/bundler.ts +163 -2
  154. package/src/serve/entries/ai.ts +28 -27
  155. package/src/serve/entries/app-ai-passthrough.ts +8 -58
  156. package/src/serve/firebase-module-swap.ts +52 -3
  157. package/src/serve/gemini-error-redaction.ts +60 -0
  158. package/src/serve/gemini-proxy.ts +140 -0
  159. package/src/serve/html-injection.ts +4 -2
  160. package/src/serve/namespace.ts +74 -15
  161. package/src/serve/sandbox-session.ts +4 -0
  162. package/src/serve/vite-sandbox-generation.ts +2 -0
  163. package/src/serve/worker/ai-proxy-paths.ts +35 -0
  164. package/src/serve/worker/client/ai.ts +6 -1
  165. package/src/serve/worker/entry.ts +2 -0
  166. package/src/serve/worker/host-ai.ts +23 -23
  167. package/src/serve/worker/host-context.ts +13 -3
  168. package/src/serve/worker/inbound-validation/operation-arguments.ts +11 -0
  169. package/src/serve/worker/protocol.ts +14 -2
@@ -823,7 +823,7 @@ on a compiled ruleset share one parse per ruleset source, and the next
823
823
  <div class="compat-list">
824
824
  <details class="compat-row" data-status="diverged">
825
825
  <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">getFirestore(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation on the Node host, <code>firebase/firestore</code> in a server process reads and writes the host's documents under its rules as the process's signed-in user. An open listener keeps the process up; a finished write releases it at once. A CommonJS <code>require('firebase/firestore')</code> resolves the same client. An app whose options differ from the host's gets each call rejected with <code>app/multiple-configs-not-supported</code>, and nothing throws past the call. When the host is not the Node host, every call fails with <code>failed-precondition</code> and a message naming the Node host (<code>pyric sandbox --hosted</code> or <code>pyric({ hosted: true })</code>); when no host is found, with the discovery error <code>pyric-admin</code> gets. No call falls back to an in-process sandbox.</span></span></span></summary>
826
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts a write the page and firebase-admin read, a CommonJS require, an app with other options rejected per call, and that a listener holds the process while a finished write does not. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L201" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div>
826
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts a write the page and firebase-admin read, a CommonJS require, an app with other options rejected per call, and that a listener holds the process while a finished write does not. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L201" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div>
827
827
  <div class="compat-note">a finished write releases the process at once, where production holds it until the write stream's 60 s idle timeout; one configuration per runtime</div></div>
828
828
  </details>
829
829
  </div>
@@ -832,7 +832,7 @@ on a compiled ruleset share one parse per ruleset source, and the next
832
832
  <div class="compat-list">
833
833
  <details class="compat-row" data-status="diverged">
834
834
  <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">getFirestore(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">On a hosted dev server, <code>firebase/firestore</code> in a server-rendered module reads and writes the host's documents under its rules as the SSR session's user: a write the rules deny for that user fails with <code>permission-denied</code>. <code>ssrLoadModule</code>, the environment runner and every <code>ssr.noExternal</code> setting share one app and one session. On a SharedWorker dev server every call fails with <code>failed-precondition</code> and a message naming <code>pyric({ hosted: true })</code>. No call reaches production or an in-process sandbox.</span></span></span></summary>
835
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> writes and reads the SSR user's own document, is denied another user's, reads the writes back with <code>firebase-admin</code> in SSR and in a process on the host, with each externalization setting, and refuses on a SharedWorker dev server. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div>
835
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> writes and reads the SSR user's own document, is denied another user's, reads the writes back with <code>firebase-admin</code> in SSR and in a process on the host, with each externalization setting, and refuses on a SharedWorker dev server. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div>
836
836
  <div class="compat-note">one SSR session per dev server process, as one Node server process has one auth state; per-request identity (<code>initializeServerApp</code>) is unsupported</div></div>
837
837
  </details>
838
838
  <details class="compat-row" data-status="diverged">
@@ -868,9 +868,9 @@ on a compiled ruleset share one parse per ruleset source, and the next
868
868
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Writes and reads in flight at a hosted connection drop (web hosted client)</code><span class="compat-sub"><span class="compat-behavior">A Firestore write (<code>setDoc</code>, <code>updateDoc</code>, <code>deleteDoc</code>, a batch <code>commit</code>) or read (<code>getDoc</code>, <code>getDocs</code>) the web hosted client's socket had taken when it dropped fails once with <code>unavailable</code> and the message "The hosted sandbox connection was lost. Requests already sent may have completed; check state before retrying." It is not sent again. In production the Firestore Web SDK keeps a write in its mutation queue and sends it again on the next write stream until the backend acknowledges it, and answers a <code>getDoc</code> from its local cache, with <code>fromCache</code>, or fails it with <code>unavailable</code> when the document is not cached.</span></span></span></summary>
869
869
  <div class="compat-evidence"><div class="compat-note">Production behavior read from <code>@firebase/firestore</code> 4.14.1, as recorded in ADR-0019 ("Reads and writes are treated alike, unlike production"). <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/hosted-reconnect-hold.test.ts#L412" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/serve/worker/hosted-reconnect-hold.test.ts</code></a> drops a real loopback socket with <code>setDoc</code>, <code>getDoc</code> and <code>batchCommit</code> in flight and asserts each fails once with that message and reaches the host once. The Realtime Database follows its SDK here instead (row <code>rtdb-modular#HD1</code>, #1088).</div></div></details>
870
870
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getFirestore(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation on the Node host, <code>firebase/firestore</code> in a server process reads and writes the host's documents under its rules as the process's signed-in user. An open listener keeps the process up; a finished write releases it at once. A CommonJS <code>require('firebase/firestore')</code> resolves the same client. An app whose options differ from the host's gets each call rejected with <code>app/multiple-configs-not-supported</code>, and nothing throws past the call. When the host is not the Node host, every call fails with <code>failed-precondition</code> and a message naming the Node host (<code>pyric sandbox --hosted</code> or <code>pyric({ hosted: true })</code>); when no host is found, with the discovery error <code>pyric-admin</code> gets. No call falls back to an in-process sandbox.</span></span></span></summary>
871
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts a write the page and firebase-admin read, a CommonJS require, an app with other options rejected per call, and that a listener holds the process while a finished write does not. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L201" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div></div></details>
871
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts a write the page and firebase-admin read, a CommonJS require, an app with other options rejected per call, and that a listener holds the process while a finished write does not. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L201" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div></div></details>
872
872
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getFirestore(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">On a hosted dev server, <code>firebase/firestore</code> in a server-rendered module reads and writes the host's documents under its rules as the SSR session's user: a write the rules deny for that user fails with <code>permission-denied</code>. <code>ssrLoadModule</code>, the environment runner and every <code>ssr.noExternal</code> setting share one app and one session. On a SharedWorker dev server every call fails with <code>failed-precondition</code> and a message naming <code>pyric({ hosted: true })</code>. No call reaches production or an in-process sandbox.</span></span></span></summary>
873
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> writes and reads the SSR user's own document, is denied another user's, reads the writes back with <code>firebase-admin</code> in SSR and in a process on the host, with each externalization setting, and refuses on a SharedWorker dev server. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
873
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> writes and reads the SSR user's own document, is denied another user's, reads the writes back with <code>firebase-admin</code> in SSR and in a process on the host, with each externalization setting, and refuses on a SharedWorker dev server. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
874
874
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">firebase-admin/firestore in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">On a hosted dev server, <code>firebase-admin/firestore</code> in a server-rendered module reads and writes the dev server host's documents as admin, past the rules, as a <code>firebase-admin</code> process on the host does. On a SharedWorker dev server its first call fails with <code>failed-precondition</code> and a message naming <code>pyric({ hosted: true })</code>.</span></span></span></summary>
875
875
  <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> reads a document the rules let only the SSR user read with <code>firebase-admin</code> in SSR through both loaders and with each externalization setting. On main (ba2c0bf0a) <code>firebase-admin</code> in SSR was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app/ambient-init.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>ambient-init.test.ts</code></a> pins the ambient binding. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
876
876
  </div> </article> </main> <aside class="doc-toc" aria-label="On this page" data-astro-cid-mw7aashj> <h2 class="doc-toc-heading" data-astro-cid-mw7aashj>On this page</h2> <ul data-astro-cid-mw7aashj> <li data-depth="2" data-astro-cid-mw7aashj> <a href="#status-legend" data-astro-cid-mw7aashj>Status legend</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getfirestoretarget--initializer" data-astro-cid-mw7aashj>getFirestore(target) — initializer</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#named-databases" data-astro-cid-mw7aashj>Named databases</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#path-constructors--doc--collection--collectiongroup" data-astro-cid-mw7aashj>Path constructors — doc / collection / collectionGroup</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getdocref--single-doc-read" data-astro-cid-mw7aashj>getDoc(ref) — single-doc read</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#getdocsquery--bulk-read" data-astro-cid-mw7aashj>getDocs(query) — bulk read</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#setdocref-data-options--full-write" data-astro-cid-mw7aashj>setDoc(ref, data[, options]) — full write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#updatedocref-data--partial-write" data-astro-cid-mw7aashj>updateDoc(ref, data) — partial write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#deletedocref" data-astro-cid-mw7aashj>deleteDoc(ref)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#adddoccoll-data--auto-id-write" data-astro-cid-mw7aashj>addDoc(coll, data) — auto-id write</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#withconverter--typed-refs" data-astro-cid-mw7aashj>withConverter — typed refs</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#query-construction--query--where--or--and--orderby--limit" data-astro-cid-mw7aashj>Query construction — query / where / or / and / orderBy / limit</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#cursor-pagination--startat--startafter--endat--endbefore" data-astro-cid-mw7aashj>Cursor pagination — startAt / startAfter / endAt / endBefore</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#aggregates--getcountfromserver--getaggregatefromserver--count--sum--average" data-astro-cid-mw7aashj>Aggregates — getCountFromServer / getAggregateFromServer / count / sum / average</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#onsnapshotreforquery---listeners" data-astro-cid-mw7aashj>onSnapshot(refOrQuery, …) — listeners</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#runtransactiondb-fn" data-astro-cid-mw7aashj>runTransaction(db, fn)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#writebatchdb" data-astro-cid-mw7aashj>writeBatch(db)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#sentinels--servertimestamp--increment--arrayunion--arrayremove--deletefield--fieldvalue--timestamp" data-astro-cid-mw7aashj>Sentinels — serverTimestamp / increment / arrayUnion / arrayRemove / deleteField / FieldValue / Timestamp</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#scalar-types--bytes--geopoint--fieldpath--documentid" data-astro-cid-mw7aashj>Scalar types — Bytes / GeoPoint / FieldPath / documentId</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#equality-helpers--refequal--queryequal--snapshotequal" data-astro-cid-mw7aashj>Equality helpers — refEqual / queryEqual / snapshotEqual</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#connectfirestoreemulator" data-astro-cid-mw7aashj>connectFirestoreEmulator</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#offline--persistence--network-family" data-astro-cid-mw7aashj>Offline / persistence / network family</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#offline--persistence--network-family-continued" data-astro-cid-mw7aashj>Offline / persistence / network family (continued)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#tier-1-cache-init--get-from--family" data-astro-cid-mw7aashj>Tier-1 cache-init + get-from-* family</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#tier-1-cache-init--get-from--family-continued" data-astro-cid-mw7aashj>Tier-1 cache-init + get-from-* family (continued)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#rules-engine-via-setrules-from-pyricsandboxfirestore" data-astro-cid-mw7aashj>Rules engine (via setRules from pyric/sandbox/firestore)</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#calls-in-flight-when-the-hosted-connection-drops" data-astro-cid-mw7aashj>Calls in flight when the hosted connection drops</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#client-sdks-in-a-registered-server-process" data-astro-cid-mw7aashj>Client SDKs in a registered server process</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#server-rendered-modules-under-vite-dev" data-astro-cid-mw7aashj>Server-rendered modules under vite dev</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#current-gaps" data-astro-cid-mw7aashj>Current gaps</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#documented-divergences" data-astro-cid-mw7aashj>Documented divergences</a> </li> </ul> </aside> </div> <script type="module">const a=document.querySelector("[data-api-symbol-filter]"),d=[...document.querySelectorAll("[data-api-symbol-group]")],n=document.querySelector("[data-api-symbol-empty]");a?.addEventListener("input",()=>{const t=a.value.trim().toLowerCase();let o=0;for(const i of d){let e=0;i.querySelectorAll("[data-api-symbol]").forEach(l=>{const r=!t||(l.dataset.apiSymbol??"").includes(t);l.hidden=!r,r&&e++}),i.hidden=e===0,o+=e}n&&(n.hidden=o>0)});</script> <script type="module">const s=new Map;document.querySelectorAll('.doc-toc a[href^="#"]').forEach(t=>s.set(decodeURIComponent(t.hash.slice(1)),t));if(s.size>0){const t=new Set,n=()=>{let o=null;for(const e of s.keys())if(t.has(e)){o=e;break}s.forEach((e,c)=>e.classList.toggle("active",c===o))},r=new IntersectionObserver(o=>{for(const e of o)e.isIntersecting?t.add(e.target.id):t.delete(e.target.id);n()},{rootMargin:"-72px 0px -60% 0px"});s.forEach((o,e)=>{const c=document.getElementById(e);c&&r.observe(c)})}</script> <script type="module">document.querySelector('.studio__nav [aria-current="page"]')?.scrollIntoView({block:"nearest",inline:"center"});</script> </body></html>
@@ -881,7 +881,7 @@ A server process started with `node --import @pyric/cli/register` and `PYRIC_SAN
881
881
  <div class="compat-list">
882
882
  <details class="compat-row" data-status="diverged">
883
883
  <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">getFirestore(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation on the Node host, <code>firebase/firestore</code> in a server process reads and writes the host's documents under its rules as the process's signed-in user. An open listener keeps the process up; a finished write releases it at once. A CommonJS <code>require('firebase/firestore')</code> resolves the same client. An app whose options differ from the host's gets each call rejected with <code>app/multiple-configs-not-supported</code>, and nothing throws past the call. When the host is not the Node host, every call fails with <code>failed-precondition</code> and a message naming the Node host (<code>pyric sandbox --hosted</code> or <code>pyric({ hosted: true })</code>); when no host is found, with the discovery error <code>pyric-admin</code> gets. No call falls back to an in-process sandbox.</span></span></span></summary>
884
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts a write the page and firebase-admin read, a CommonJS require, an app with other options rejected per call, and that a listener holds the process while a finished write does not. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L201" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div>
884
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts a write the page and firebase-admin read, a CommonJS require, an app with other options rejected per call, and that a listener holds the process while a finished write does not. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L201" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div>
885
885
  <div class="compat-note">a finished write releases the process at once, where production holds it until the write stream's 60 s idle timeout; one configuration per runtime</div></div>
886
886
  </details>
887
887
  </div>
@@ -893,7 +893,7 @@ Modules Vite loads for server rendering under `vite dev` with `pyric()` (`server
893
893
  <div class="compat-list">
894
894
  <details class="compat-row" data-status="diverged">
895
895
  <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">getFirestore(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">On a hosted dev server, <code>firebase/firestore</code> in a server-rendered module reads and writes the host's documents under its rules as the SSR session's user: a write the rules deny for that user fails with <code>permission-denied</code>. <code>ssrLoadModule</code>, the environment runner and every <code>ssr.noExternal</code> setting share one app and one session. On a SharedWorker dev server every call fails with <code>failed-precondition</code> and a message naming <code>pyric({ hosted: true })</code>. No call reaches production or an in-process sandbox.</span></span></span></summary>
896
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> writes and reads the SSR user's own document, is denied another user's, reads the writes back with <code>firebase-admin</code> in SSR and in a process on the host, with each externalization setting, and refuses on a SharedWorker dev server. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div>
896
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> writes and reads the SSR user's own document, is denied another user's, reads the writes back with <code>firebase-admin</code> in SSR and in a process on the host, with each externalization setting, and refuses on a SharedWorker dev server. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div>
897
897
  <div class="compat-note">one SSR session per dev server process, as one Node server process has one auth state; per-request identity (<code>initializeServerApp</code>) is unsupported</div></div>
898
898
  </details>
899
899
  <details class="compat-row" data-status="diverged">
@@ -933,9 +933,9 @@ Known differences between Pyric and production Firebase. Each remains tracked as
933
933
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Writes and reads in flight at a hosted connection drop (web hosted client)</code><span class="compat-sub"><span class="compat-behavior">A Firestore write (<code>setDoc</code>, <code>updateDoc</code>, <code>deleteDoc</code>, a batch <code>commit</code>) or read (<code>getDoc</code>, <code>getDocs</code>) the web hosted client's socket had taken when it dropped fails once with <code>unavailable</code> and the message "The hosted sandbox connection was lost. Requests already sent may have completed; check state before retrying." It is not sent again. In production the Firestore Web SDK keeps a write in its mutation queue and sends it again on the next write stream until the backend acknowledges it, and answers a <code>getDoc</code> from its local cache, with <code>fromCache</code>, or fails it with <code>unavailable</code> when the document is not cached.</span></span></span></summary>
934
934
  <div class="compat-evidence"><div class="compat-note">Production behavior read from <code>@firebase/firestore</code> 4.14.1, as recorded in ADR-0019 ("Reads and writes are treated alike, unlike production"). <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/hosted-reconnect-hold.test.ts#L412" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/serve/worker/hosted-reconnect-hold.test.ts</code></a> drops a real loopback socket with <code>setDoc</code>, <code>getDoc</code> and <code>batchCommit</code> in flight and asserts each fails once with that message and reaches the host once. The Realtime Database follows its SDK here instead (row <code>rtdb-modular#HD1</code>, #1088).</div></div></details>
935
935
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getFirestore(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation on the Node host, <code>firebase/firestore</code> in a server process reads and writes the host's documents under its rules as the process's signed-in user. An open listener keeps the process up; a finished write releases it at once. A CommonJS <code>require('firebase/firestore')</code> resolves the same client. An app whose options differ from the host's gets each call rejected with <code>app/multiple-configs-not-supported</code>, and nothing throws past the call. When the host is not the Node host, every call fails with <code>failed-precondition</code> and a message naming the Node host (<code>pyric sandbox --hosted</code> or <code>pyric({ hosted: true })</code>); when no host is found, with the discovery error <code>pyric-admin</code> gets. No call falls back to an in-process sandbox.</span></span></span></summary>
936
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts a write the page and firebase-admin read, a CommonJS require, an app with other options rejected per call, and that a listener holds the process while a finished write does not. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L201" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div></div></details>
936
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts a write the page and firebase-admin read, a CommonJS require, an app with other options rejected per call, and that a listener holds the process while a finished write does not. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L201" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div></div></details>
937
937
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getFirestore(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">On a hosted dev server, <code>firebase/firestore</code> in a server-rendered module reads and writes the host's documents under its rules as the SSR session's user: a write the rules deny for that user fails with <code>permission-denied</code>. <code>ssrLoadModule</code>, the environment runner and every <code>ssr.noExternal</code> setting share one app and one session. On a SharedWorker dev server every call fails with <code>failed-precondition</code> and a message naming <code>pyric({ hosted: true })</code>. No call reaches production or an in-process sandbox.</span></span></span></summary>
938
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> writes and reads the SSR user's own document, is denied another user's, reads the writes back with <code>firebase-admin</code> in SSR and in a process on the host, with each externalization setting, and refuses on a SharedWorker dev server. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
938
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> writes and reads the SSR user's own document, is denied another user's, reads the writes back with <code>firebase-admin</code> in SSR and in a process on the host, with each externalization setting, and refuses on a SharedWorker dev server. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L36" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
939
939
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">firebase-admin/firestore in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">On a hosted dev server, <code>firebase-admin/firestore</code> in a server-rendered module reads and writes the dev server host's documents as admin, past the rules, as a <code>firebase-admin</code> process on the host does. On a SharedWorker dev server its first call fails with <code>failed-precondition</code> and a message naming <code>pyric({ hosted: true })</code>.</span></span></span></summary>
940
940
  <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> reads a document the rules let only the SSR user read with <code>firebase-admin</code> in SSR through both loaders and with each externalization setting. On main (ba2c0bf0a) <code>firebase-admin</code> in SSR was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app/ambient-init.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>ambient-init.test.ts</code></a> pins the ambient binding. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
941
941
  </div>
@@ -807,6 +807,11 @@
807
807
  "group": "Build",
808
808
  "section": "",
809
809
  "headings": [
810
+ {
811
+ "depth": 2,
812
+ "slug": "initialize-app-check-before-getai",
813
+ "text": "Initialize App Check before getAI"
814
+ },
810
815
  {
811
816
  "depth": 2,
812
817
  "slug": "start-with-deterministic-responses",
@@ -2072,6 +2077,11 @@
2072
2077
  "slug": "app-check-on-the-ai-path",
2073
2078
  "text": "App Check on the AI path"
2074
2079
  },
2080
+ {
2081
+ "depth": 2,
2082
+ "slug": "app-check-in-sandbox-ai-mode",
2083
+ "text": "App Check in sandbox AI mode"
2084
+ },
2075
2085
  {
2076
2086
  "depth": 2,
2077
2087
  "slug": "current-gaps",
@@ -307,12 +307,12 @@ html{scrollbar-gutter:stable}
307
307
  <div class="compat-list">
308
308
  <details class="compat-row" data-status="diverged">
309
309
  <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">getMessaging(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation, <code>getMessaging(app)</code> in a server process fails with <code>failed-precondition</code> and a message that names the client services that do reach the Node host: <code>firebase/messaging</code> has no path to the host, and no in-process sandbox stands in for it.</span></span></span></summary>
310
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> calls it under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server and asserts the refusal; on main (942cb175) it returned a handle on a private in-process sandbox. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins that the subpath keeps its mirror under a remote activation, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>node-origin.test.ts</code></a> that a mirror resolved from such an app refuses. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L479" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div>
310
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> calls it under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server and asserts the refusal; on main (942cb175) it returned a handle on a private in-process sandbox. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins that the subpath keeps its mirror under a remote activation, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>node-origin.test.ts</code></a> that a mirror resolved from such an app refuses. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L479" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div>
311
311
  <div class="compat-note">refused in a server process; production runs it in Node</div></div>
312
312
  </details>
313
313
  <details class="compat-row" data-status="diverged">
314
314
  <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">getMessaging(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">Under <code>vite dev</code>, <code>getMessaging(app)</code> in a server-rendered module fails with <code>failed-precondition</code>, naming the client services that reach the host: <code>firebase/messaging</code> has no path to the dev server's host from server code, and the real SDK does not load.</span></span></span></summary>
315
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> calls it in SSR on a hosted dev server and asserts the refusal. On main (ba2c0bf0a) it was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L493" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div>
315
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> calls it in SSR on a hosted dev server and asserts the refusal. On main (ba2c0bf0a) it was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L493" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div>
316
316
  <div class="compat-note">refused in server-rendered modules under vite dev; production runs it in the server's runtime</div></div>
317
317
  </details>
318
318
  </div>
@@ -321,9 +321,9 @@ html{scrollbar-gutter:stable}
321
321
  <p>Known differences between Pyric and production Firebase. Each remains tracked as a non-conforming row.</p>
322
322
  <div class="compat-list">
323
323
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getMessaging(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation, <code>getMessaging(app)</code> in a server process fails with <code>failed-precondition</code> and a message that names the client services that do reach the Node host: <code>firebase/messaging</code> has no path to the host, and no in-process sandbox stands in for it.</span></span></span></summary>
324
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> calls it under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server and asserts the refusal; on main (942cb175) it returned a handle on a private in-process sandbox. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins that the subpath keeps its mirror under a remote activation, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>node-origin.test.ts</code></a> that a mirror resolved from such an app refuses. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L479" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
324
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> calls it under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server and asserts the refusal; on main (942cb175) it returned a handle on a private in-process sandbox. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins that the subpath keeps its mirror under a remote activation, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>node-origin.test.ts</code></a> that a mirror resolved from such an app refuses. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L479" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
325
325
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getMessaging(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">Under <code>vite dev</code>, <code>getMessaging(app)</code> in a server-rendered module fails with <code>failed-precondition</code>, naming the client services that reach the host: <code>firebase/messaging</code> has no path to the dev server's host from server code, and the real SDK does not load.</span></span></span></summary>
326
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> calls it in SSR on a hosted dev server and asserts the refusal. On main (ba2c0bf0a) it was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L493" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
326
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> calls it in SSR on a hosted dev server and asserts the refusal. On main (ba2c0bf0a) it was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L493" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
327
327
  </div>
328
328
  <h3 id="not-implemented-yet">Not implemented yet</h3>
329
329
  <p>Tracked behavior that is not implemented in the current contract.</p>
@@ -330,12 +330,12 @@
330
330
  <div class="compat-list">
331
331
  <details class="compat-row" data-status="diverged">
332
332
  <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">getMessaging(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation, <code>getMessaging(app)</code> in a server process fails with <code>failed-precondition</code> and a message that names the client services that do reach the Node host: <code>firebase/messaging</code> has no path to the host, and no in-process sandbox stands in for it.</span></span></span></summary>
333
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> calls it under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server and asserts the refusal; on main (942cb175) it returned a handle on a private in-process sandbox. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins that the subpath keeps its mirror under a remote activation, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>node-origin.test.ts</code></a> that a mirror resolved from such an app refuses. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L479" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div>
333
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> calls it under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server and asserts the refusal; on main (942cb175) it returned a handle on a private in-process sandbox. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins that the subpath keeps its mirror under a remote activation, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>node-origin.test.ts</code></a> that a mirror resolved from such an app refuses. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L479" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div>
334
334
  <div class="compat-note">refused in a server process; production runs it in Node</div></div>
335
335
  </details>
336
336
  <details class="compat-row" data-status="diverged">
337
337
  <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">getMessaging(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">Under <code>vite dev</code>, <code>getMessaging(app)</code> in a server-rendered module fails with <code>failed-precondition</code>, naming the client services that reach the host: <code>firebase/messaging</code> has no path to the dev server's host from server code, and the real SDK does not load.</span></span></span></summary>
338
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> calls it in SSR on a hosted dev server and asserts the refusal. On main (ba2c0bf0a) it was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L493" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div>
338
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> calls it in SSR on a hosted dev server and asserts the refusal. On main (ba2c0bf0a) it was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L493" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div>
339
339
  <div class="compat-note">refused in server-rendered modules under vite dev; production runs it in the server's runtime</div></div>
340
340
  </details>
341
341
  </div>
@@ -348,9 +348,9 @@ Known differences between Pyric and production Firebase. Each remains tracked as
348
348
 
349
349
  <div class="compat-list">
350
350
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getMessaging(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation, <code>getMessaging(app)</code> in a server process fails with <code>failed-precondition</code> and a message that names the client services that do reach the Node host: <code>firebase/messaging</code> has no path to the host, and no in-process sandbox stands in for it.</span></span></span></summary>
351
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> calls it under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server and asserts the refusal; on main (942cb175) it returned a handle on a private in-process sandbox. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins that the subpath keeps its mirror under a remote activation, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>node-origin.test.ts</code></a> that a mirror resolved from such an app refuses. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L479" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
351
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> calls it under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server and asserts the refusal; on main (942cb175) it returned a handle on a private in-process sandbox. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins that the subpath keeps its mirror under a remote activation, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>node-origin.test.ts</code></a> that a mirror resolved from such an app refuses. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L479" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
352
352
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getMessaging(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">Under <code>vite dev</code>, <code>getMessaging(app)</code> in a server-rendered module fails with <code>failed-precondition</code>, naming the client services that reach the host: <code>firebase/messaging</code> has no path to the dev server's host from server code, and the real SDK does not load.</span></span></span></summary>
353
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> calls it in SSR on a hosted dev server and asserts the refusal. On main (ba2c0bf0a) it was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L493" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
353
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> calls it in SSR on a hosted dev server and asserts the refusal. On main (ba2c0bf0a) it was the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/messaging/oracle-conformance.test.ts#L493" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
354
354
  </div>
355
355
 
356
356
  ### Not implemented yet
@@ -121,6 +121,10 @@ application code asks the development sandbox for production credentials.</p>
121
121
  <p>Link-compatible mirror of <code>firebase-admin/app</code>’s <code>cert(serviceAccountPathOrObject)</code>.
122
122
  Returns an inert sandbox credential token so unchanged application setup code
123
123
  can assign <code>appOptions.credential = cert(...)</code> under <code>@pyric/cli/register</code>.</p>
124
+ <p>It keeps one field, the service account’s project id, because firebase-admin
125
+ finds an app’s project there when the options name none. A path is read as a
126
+ JSON file; a file that is missing, unreadable, or has no project id gives a
127
+ credential without one, and never throws, so apps run without a key on disk.</p>
124
128
  <h4 id="parameters">Parameters</h4>
125
129
  <div class="table-scroll">
126
130
 
@@ -129,6 +129,11 @@ Link-compatible mirror of `firebase-admin/app`'s `cert(serviceAccountPathOrObjec
129
129
  Returns an inert sandbox credential token so unchanged application setup code
130
130
  can assign `appOptions.credential = cert(...)` under `@pyric/cli/register`.
131
131
 
132
+ It keeps one field, the service account's project id, because firebase-admin
133
+ finds an app's project there when the options name none. A path is read as a
134
+ JSON file; a file that is missing, unreadable, or has no project id gives a
135
+ credential without one, and never throws, so apps run without a key on disk.
136
+
132
137
  #### Parameters
133
138
 
134
139
  | Parameter | Type |
@@ -39,6 +39,8 @@ Generated from the TypeScript declarations shipped at this import path.
39
39
  <pre class="astro-code one-dark-pro" style="background-color:#282c34;color:#abb2bf; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#C678DD">function</span><span style="color:#61AFEF"> initializeAppCheck</span><span style="color:#ABB2BF">(</span><span style="color:#E06C75;font-style:italic">app</span><span style="color:#ABB2BF">: {</span></span>
40
40
  <span class="line"><span style="color:#E06C75"> name</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">string</span><span style="color:#ABB2BF">;</span></span>
41
41
  <span class="line"><span style="color:#ABB2BF">}, </span><span style="color:#E06C75;font-style:italic">options</span><span style="color:#ABB2BF">: </span><span style="color:#E5C07B">unknown</span><span style="color:#ABB2BF">): </span><span style="color:#E5C07B">unknown</span><span style="color:#ABB2BF">;</span></span></code></pre>
42
+ <p><code>shadowAppOf</code> throws when the shadow app could not be created, so that
43
+ cause surfaces here before the SDK would fail on a missing container.</p>
42
44
  <h4 id="parameters">Parameters</h4>
43
45
  <div class="table-scroll">
44
46
 
@@ -82,6 +82,9 @@ function initializeAppCheck(app: {
82
82
  }, options: unknown): unknown;
83
83
  ```
84
84
 
85
+ `shadowAppOf` throws when the shadow app could not be created, so that
86
+ cause surfaces here before the SDK would fail on a missing container.
87
+
85
88
  #### Parameters
86
89
 
87
90
  | Parameter | Type |
@@ -77,7 +77,7 @@ html{scrollbar-gutter:stable}
77
77
  </details>
78
78
  <details class="compat-row" data-status="ok">
79
79
  <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">getStorage(app, bucketUrl?)<code> / </code>getStorageSandbox(target, options?)</code><span class="compat-sub"><span class="compat-behavior">The served <code>firebase/storage</code> entry accepts bare <code>getStorage()</code> and returns the page's shared sandbox handle</span></span></span></summary>
80
- <div class="compat-evidence"><div class="compat-probe">The canonical served entry supplies the page sandbox when the app argument is omitted; the entry-path and bundler suites execute the public package shape. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/entry-path/storage.ts#L16" target="_blank" rel="noopener noreferrer"><code>storage.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/bundler.test.ts#L459" target="_blank" rel="noopener noreferrer"><code>bundler.test.ts</code></a>)</div></div>
80
+ <div class="compat-evidence"><div class="compat-probe">The canonical served entry supplies the page sandbox when the app argument is omitted; the entry-path and bundler suites execute the public package shape. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/entry-path/storage.ts#L16" target="_blank" rel="noopener noreferrer"><code>storage.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/bundler.test.ts#L546" target="_blank" rel="noopener noreferrer"><code>bundler.test.ts</code></a>)</div></div>
81
81
  </details>
82
82
  </div>
83
83
  <h2 id="refstorage-path--refparent-path--reference-constructor"><code>ref(storage[, path])</code> / <code>ref(parent, path)</code> — reference constructor</h2>
@@ -564,7 +564,7 @@ API) moved to the native <code>storage-rules</code> surface (<code>docs/rules/CO
564
564
  <div class="compat-list">
565
565
  <details class="compat-row" data-status="diverged">
566
566
  <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">getStorage(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation on the Node host, <code>firebase/storage</code> in a server process uploads and reads the host's objects under its rules, over the host's byte route. An upload issued before the process's connection has attached waits for the attachment, keeps its place in the call order, and never sends its bytes as frames to a host that takes them only over the route; it fails as a held call does if the connection is not restored within 10 seconds. A finished upload does not keep the process up. When the host is not the Node host, every call fails with <code>failed-precondition</code> and a message naming the Node host (<code>pyric sandbox --hosted</code> or <code>pyric({ hosted: true })</code>); when no host is found, with the discovery error <code>pyric-admin</code> gets. No call falls back to an in-process sandbox.</span></span></span></summary>
567
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts an upload issued as the process's first call that firebase-admin downloads, its HTTP download URL, and that a finished upload does not hold the process. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/storage-byte-route-settled.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>storage-byte-route-settled.test.ts</code></a> pins the wait, the call order and the deadline. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L593" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div>
567
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts an upload issued as the process's first call that firebase-admin downloads, its HTTP download URL, and that a finished upload does not hold the process. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/storage-byte-route-settled.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>storage-byte-route-settled.test.ts</code></a> pins the wait, the call order and the deadline. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L596" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div>
568
568
  <div class="compat-note">a resumable upload issued before the connection attaches reports its progress in one step on a byte-route host</div></div>
569
569
  </details>
570
570
  </div>
@@ -573,7 +573,7 @@ API) moved to the native <code>storage-rules</code> surface (<code>docs/rules/CO
573
573
  <div class="compat-list">
574
574
  <details class="compat-row" data-status="diverged">
575
575
  <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">getStorage(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">On a hosted dev server, <code>firebase/storage</code> in a server-rendered module uploads and reads the host's objects under its rules as the SSR session's user, over the host's byte route: an upload to another user's path fails with <code>storage/unauthorized</code>, and a download URL names the host's byte route. On a SharedWorker dev server every call fails with <code>failed-precondition</code> and a message naming <code>pyric({ hosted: true })</code>. No call reaches production or an in-process sandbox.</span></span></span></summary>
576
- <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> uploads to the SSR user's own path, reads its download URL, is denied another user's path, and downloads the object with <code>firebase-admin</code> in a process on the host. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div>
576
+ <div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> uploads to the SSR user's own path, reads its download URL, is denied another user's path, and downloads the object with <code>firebase-admin</code> in a process on the host. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div>
577
577
  <div class="compat-note">one SSR session per dev server process, as one Node server process has one auth state; per-request identity (<code>initializeServerApp</code>) is unsupported</div></div>
578
578
  </details>
579
579
  </div>
@@ -616,9 +616,9 @@ API) moved to the native <code>storage-rules</code> surface (<code>docs/rules/CO
616
616
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Operations in flight at a hosted connection drop (web hosted client)</code><span class="compat-sub"><span class="compat-behavior">A Storage operation (<code>getMetadata</code>, <code>deleteObject</code>, <code>list</code>, or an upload step) the web hosted client's socket had taken when it dropped fails once with <code>unavailable</code> and the message "The hosted sandbox connection was lost. Requests already sent may have completed; check state before retrying." It is not sent again. In production the Storage Web SDK retries a request that fails on the network with exponential backoff, for up to 2 minutes for an operation and 10 minutes for an upload, and then fails it with <code>storage/retry-limit-exceeded</code>.</span></span></span></summary>
617
617
  <div class="compat-evidence"><div class="compat-note">Production behavior read from <code>@firebase/storage</code> 0.14.3 <code>dist/index.esm.js</code>: <code>DEFAULT_MAX_OPERATION_RETRY_TIME</code> (2 minutes), <code>DEFAULT_MAX_UPLOAD_RETRY_TIME</code> (10 minutes), and <code>retryLimitExceeded()</code> in the request retry loop. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/hosted-reconnect-hold.test.ts#L412" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/serve/worker/hosted-reconnect-hold.test.ts</code></a> drops a real loopback socket with <code>storage.getMetadata</code> and <code>storage.deleteObject</code> in flight and asserts each fails once with that message and reaches the host once.</div></div></details>
618
618
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getStorage(app) in a registered server process</code><span class="compat-sub"><span class="compat-behavior">Under a remote register activation on the Node host, <code>firebase/storage</code> in a server process uploads and reads the host's objects under its rules, over the host's byte route. An upload issued before the process's connection has attached waits for the attachment, keeps its place in the call order, and never sends its bytes as frames to a host that takes them only over the route; it fails as a held call does if the connection is not restored within 10 seconds. A finished upload does not keep the process up. When the host is not the Node host, every call fails with <code>failed-precondition</code> and a message naming the Node host (<code>pyric sandbox --hosted</code> or <code>pyric({ hosted: true })</code>); when no host is found, with the discovery error <code>pyric-admin</code> gets. No call falls back to an in-process sandbox.</span></span></span></summary>
619
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts an upload issued as the process's first call that firebase-admin downloads, its HTTP download URL, and that a finished upload does not hold the process. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/storage-byte-route-settled.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>storage-byte-route-settled.test.ts</code></a> pins the wait, the call order and the deadline. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L141" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L593" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div></div></details>
619
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/client-sdk-under-register</code> runs the client SDKs under <code>node --import @pyric/cli/register</code> against a hosted Vite dev server, reads back with <code>firebase-admin</code> and a page, and asserts an upload issued as the process's first call that firebase-admin downloads, its HTTP download URL, and that a finished upload does not hold the process. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/storage-byte-route-settled.test.ts#L11" target="_blank" rel="noopener noreferrer"><code>storage-byte-route-settled.test.ts</code></a> pins the wait, the call order and the deadline. On main (942cb175) it failed: the calls ran against a private in-process sandbox, so an upload returned a <a href="https://github.com/davideast/pyric/blob/main/test/apps/client-sdk-under-register/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>data:</code></a> URL and firebase-admin read nothing they wrote. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/node-origin.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>packages/cli/test/register/node-origin.test.ts</code></a> runs the served surfaces under a Node origin whose host is not the Node host, or cannot be found, and asserts every call fails, <code>useWorker</code> stays true, the served app entry's placeholder sandbox stays empty, and a <code>pyric/*</code> mirror resolved from the app refuses. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/mapping.test.ts#L80" target="_blank" rel="noopener noreferrer"><code>mapping.test.ts</code></a> pins the mapping per activation value.. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/register/register-child.test.ts#L596" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div></div></details>
620
620
  <details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getStorage(app) in a server-rendered module under vite dev</code><span class="compat-sub"><span class="compat-behavior">On a hosted dev server, <code>firebase/storage</code> in a server-rendered module uploads and reads the host's objects under its rules as the SSR session's user, over the host's byte route: an upload to another user's path fails with <code>storage/unauthorized</code>, and a download URL names the host's byte route. On a SharedWorker dev server every call fails with <code>failed-precondition</code> and a message naming <code>pyric({ hosted: true })</code>. No call reaches production or an in-process sandbox.</span></span></span></summary>
621
- <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> uploads to the SSR user's own path, reads its download URL, is denied another user's path, and downloads the object with <code>firebase-admin</code> in a process on the host. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
621
+ <div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> uploads to the SSR user's own path, reads its download URL, is denied another user's path, and downloads the object with <code>firebase-admin</code> in a process on the host. On main (ba2c0bf0a) the scenario failed: SSR sign-in rejected with <code>auth/network-request-failed</code> from the network guard, and the RTDB, Firestore and Storage writes did not settle, because the imports loaded the real SDK. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-module-swap.test.ts#L4" target="_blank" rel="noopener noreferrer"><code>vite-module-swap.test.ts</code></a> pins the routing per specifier, AI mode and environment (Vite 5's <code>ssr</code> option, a runnable environment, a non-runnable one), <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-ssr-activation.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>vite-ssr-activation.test.ts</code></a> the host, the restart hand-over and the SharedWorker refusal, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/vite-plugin-firestore.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>vite-plugin-firestore.test.ts</code></a> runs Vite with <code>firebase</code> installed, so it takes the externalized path. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/test/apps/vite-ssr-client-sdk/scenario.ts#L1" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
622
622
  </div>
623
623
  <h3 id="not-implemented-yet">Not implemented yet</h3>
624
624
  <p>Tracked behavior that is not implemented in the current contract.</p>