@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.
- package/dist/assurance/.generated/conformance-verdicts.d.ts +8 -0
- package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
- package/dist/assurance/.generated/conformance-verdicts.js +8 -0
- package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
- package/dist/bridge/server/peer.d.ts +10 -0
- package/dist/bridge/server/peer.d.ts.map +1 -1
- package/dist/bridge/server/peer.js +46 -1
- package/dist/bridge/server/peer.js.map +1 -1
- package/dist/cli/serve.d.ts +2 -0
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +13 -2
- package/dist/cli/serve.js.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js +2 -2
- package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
- package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use.js +1 -1
- package/dist/conformance/.generated/can-i-use.js.map +1 -1
- package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
- package/dist/conformance/.generated/conformance-docs.js +1 -1
- package/dist/conformance/.generated/conformance-docs.js.map +1 -1
- package/dist/register/app-bridge-admin-app-check.d.ts +3 -0
- package/dist/register/app-bridge-admin-app-check.d.ts.map +1 -0
- package/dist/register/app-bridge-admin-app-check.js +43 -0
- package/dist/register/app-bridge-admin-app-check.js.map +1 -0
- package/dist/register/app-bridge-app-check.d.ts +4 -0
- package/dist/register/app-bridge-app-check.d.ts.map +1 -1
- package/dist/register/app-bridge-app-check.js +4 -0
- package/dist/register/app-bridge-app-check.js.map +1 -1
- package/dist/register/app-bridge-shadow.d.ts +4 -2
- package/dist/register/app-bridge-shadow.d.ts.map +1 -1
- package/dist/register/app-bridge-shadow.js +29 -5
- package/dist/register/app-bridge-shadow.js.map +1 -1
- package/dist/register/index.d.ts.map +1 -1
- package/dist/register/index.js +21 -0
- package/dist/register/index.js.map +1 -1
- package/dist/serve/ai-diagnostics-relay.d.ts +4 -1
- package/dist/serve/ai-diagnostics-relay.d.ts.map +1 -1
- package/dist/serve/ai-diagnostics-relay.js +3 -0
- package/dist/serve/ai-diagnostics-relay.js.map +1 -1
- package/dist/serve/ai-proxy.d.ts +21 -1
- package/dist/serve/ai-proxy.d.ts.map +1 -1
- package/dist/serve/ai-proxy.js +39 -14
- package/dist/serve/ai-proxy.js.map +1 -1
- package/dist/serve/ai-terminal-blocks.d.ts +6 -4
- package/dist/serve/ai-terminal-blocks.d.ts.map +1 -1
- package/dist/serve/ai-terminal-blocks.js +7 -4
- package/dist/serve/ai-terminal-blocks.js.map +1 -1
- package/dist/serve/ai-terminal-text.d.ts +8 -0
- package/dist/serve/ai-terminal-text.d.ts.map +1 -1
- package/dist/serve/ai-terminal-text.js +10 -0
- package/dist/serve/ai-terminal-text.js.map +1 -1
- package/dist/serve/bundler.d.ts +40 -0
- package/dist/serve/bundler.d.ts.map +1 -1
- package/dist/serve/bundler.js +133 -2
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/entries/ai.d.ts.map +1 -1
- package/dist/serve/entries/ai.js +28 -26
- package/dist/serve/entries/ai.js.map +1 -1
- package/dist/serve/entries/app-ai-passthrough.d.ts +4 -4
- package/dist/serve/entries/app-ai-passthrough.d.ts.map +1 -1
- package/dist/serve/entries/app-ai-passthrough.js +8 -52
- package/dist/serve/entries/app-ai-passthrough.js.map +1 -1
- package/dist/serve/firebase-module-swap.d.ts +22 -0
- package/dist/serve/firebase-module-swap.d.ts.map +1 -1
- package/dist/serve/firebase-module-swap.js +51 -3
- package/dist/serve/firebase-module-swap.js.map +1 -1
- package/dist/serve/gemini-error-redaction.d.ts +6 -0
- package/dist/serve/gemini-error-redaction.d.ts.map +1 -0
- package/dist/serve/gemini-error-redaction.js +61 -0
- package/dist/serve/gemini-error-redaction.js.map +1 -0
- package/dist/serve/gemini-proxy.d.ts +40 -0
- package/dist/serve/gemini-proxy.d.ts.map +1 -0
- package/dist/serve/gemini-proxy.js +105 -0
- package/dist/serve/gemini-proxy.js.map +1 -0
- package/dist/serve/hosted/method-protocol.d.ts +4 -4
- package/dist/serve/html-injection.d.ts +2 -0
- package/dist/serve/html-injection.d.ts.map +1 -1
- package/dist/serve/html-injection.js +4 -2
- package/dist/serve/html-injection.js.map +1 -1
- package/dist/serve/namespace.d.ts +9 -0
- package/dist/serve/namespace.d.ts.map +1 -1
- package/dist/serve/namespace.js +71 -15
- package/dist/serve/namespace.js.map +1 -1
- package/dist/serve/sandbox-session.d.ts +3 -0
- package/dist/serve/sandbox-session.d.ts.map +1 -1
- package/dist/serve/sandbox-session.js +1 -0
- package/dist/serve/sandbox-session.js.map +1 -1
- package/dist/serve/site-ui/docs/ai-compat/index.html +29 -10
- package/dist/serve/site-ui/docs/ai-compat.md +30 -9
- package/dist/serve/site-ui/docs/app-check-compat/index.html +42 -18
- package/dist/serve/site-ui/docs/app-check-compat.md +42 -18
- package/dist/serve/site-ui/docs/auth-compat/index.html +4 -4
- package/dist/serve/site-ui/docs/auth-compat.md +4 -4
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +21 -5
- package/dist/serve/site-ui/docs/build/ai-logic.md +23 -2
- package/dist/serve/site-ui/docs/database-compat/index.html +4 -4
- package/dist/serve/site-ui/docs/database-compat.md +4 -4
- package/dist/serve/site-ui/docs/firestore-compat/index.html +4 -4
- package/dist/serve/site-ui/docs/firestore-compat.md +4 -4
- package/dist/serve/site-ui/docs/index.json +10 -0
- package/dist/serve/site-ui/docs/messaging-compat/index.html +4 -4
- package/dist/serve/site-ui/docs/messaging-compat.md +4 -4
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +4 -0
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api.md +5 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-app-check-reference-api/index.html +2 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-app-check-reference-api.md +3 -0
- package/dist/serve/site-ui/docs/storage-compat/index.html +5 -5
- package/dist/serve/site-ui/docs/storage-compat.md +5 -5
- package/dist/serve/site-ui/docs/tutorial/index.html +11 -1
- package/dist/serve/site-ui/docs/tutorial.md +12 -1
- package/dist/serve/site-ui/examples/chess/index.html +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts.map +1 -1
- package/dist/serve/vite-sandbox-generation.js +2 -0
- package/dist/serve/vite-sandbox-generation.js.map +1 -1
- package/dist/serve/worker/ai-proxy-paths.d.ts +32 -0
- package/dist/serve/worker/ai-proxy-paths.d.ts.map +1 -0
- package/dist/serve/worker/ai-proxy-paths.js +20 -0
- package/dist/serve/worker/ai-proxy-paths.js.map +1 -0
- package/dist/serve/worker/client/ai.d.ts +3 -1
- package/dist/serve/worker/client/ai.d.ts.map +1 -1
- package/dist/serve/worker/client/ai.js +3 -0
- package/dist/serve/worker/client/ai.js.map +1 -1
- package/dist/serve/worker/entry.js +2 -0
- package/dist/serve/worker/entry.js.map +1 -1
- package/dist/serve/worker/host-ai.d.ts +4 -5
- package/dist/serve/worker/host-ai.d.ts.map +1 -1
- package/dist/serve/worker/host-ai.js +19 -23
- package/dist/serve/worker/host-ai.js.map +1 -1
- package/dist/serve/worker/host-context.d.ts +13 -4
- package/dist/serve/worker/host-context.d.ts.map +1 -1
- package/dist/serve/worker/host-context.js.map +1 -1
- package/dist/serve/worker/inbound-validation/operation-arguments.d.ts.map +1 -1
- package/dist/serve/worker/inbound-validation/operation-arguments.js +11 -0
- package/dist/serve/worker/inbound-validation/operation-arguments.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +13 -0
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js.map +1 -1
- package/package.json +4 -4
- package/src/assurance/.generated/conformance-verdicts.ts +8 -0
- package/src/bridge/server/peer.ts +49 -1
- package/src/cli/serve.ts +14 -2
- package/src/conformance/.generated/can-i-use-browser.ts +2 -2
- package/src/conformance/.generated/can-i-use.ts +1 -1
- package/src/conformance/.generated/conformance-docs.ts +1 -1
- package/src/register/app-bridge-admin-app-check.ts +46 -0
- package/src/register/app-bridge-app-check.ts +4 -0
- package/src/register/app-bridge-shadow.ts +35 -5
- package/src/register/index.ts +23 -0
- package/src/serve/ai-diagnostics-relay.ts +6 -1
- package/src/serve/ai-proxy.ts +43 -14
- package/src/serve/ai-terminal-blocks.ts +8 -4
- package/src/serve/ai-terminal-text.ts +11 -0
- package/src/serve/bundler.ts +163 -2
- package/src/serve/entries/ai.ts +28 -27
- package/src/serve/entries/app-ai-passthrough.ts +8 -58
- package/src/serve/firebase-module-swap.ts +52 -3
- package/src/serve/gemini-error-redaction.ts +60 -0
- package/src/serve/gemini-proxy.ts +140 -0
- package/src/serve/html-injection.ts +4 -2
- package/src/serve/namespace.ts +74 -15
- package/src/serve/sandbox-session.ts +4 -0
- package/src/serve/vite-sandbox-generation.ts +2 -0
- package/src/serve/worker/ai-proxy-paths.ts +35 -0
- package/src/serve/worker/client/ai.ts +6 -1
- package/src/serve/worker/entry.ts +2 -0
- package/src/serve/worker/host-ai.ts +23 -23
- package/src/serve/worker/host-context.ts +13 -3
- package/src/serve/worker/inbound-validation/operation-arguments.ts +11 -0
- package/src/serve/worker/protocol.ts +14 -2
|
@@ -26,7 +26,7 @@ App Check is climbing under Conformance Driven Development. A row below is a tar
|
|
|
26
26
|
</details>
|
|
27
27
|
<details class="compat-row" data-status="ok">
|
|
28
28
|
<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">new CustomProvider({ getToken })</code><span class="compat-sub"><span class="compat-behavior">A <code>CustomProvider</code> constructs and is accepted by <code>initializeAppCheck</code>. With a debug token in effect, the SDK never calls it (a provider whose <code>getToken</code> would reject is called zero times).</span></span></span></summary>
|
|
29
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>customProviderCalls</code>; the capture initialized App Check with a <code>CustomProvider</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
29
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>customProviderCalls</code>; the capture initialized App Check with a <code>CustomProvider</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
30
30
|
</details>
|
|
31
31
|
<details class="compat-row" data-status="unverified">
|
|
32
32
|
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">new ReCaptchaV3Provider(siteKey); new ReCaptchaEnterpriseProvider(siteKey)</code><span class="compat-sub"><span class="compat-behavior">Both reCAPTCHA providers construct from a site key and are accepted by <code>initializeAppCheck</code>.</span></span></span></summary>
|
|
@@ -39,19 +39,19 @@ App Check is climbing under Conformance Driven Development. A row below is a tar
|
|
|
39
39
|
<div class="compat-list">
|
|
40
40
|
<details class="compat-row" data-status="ok">
|
|
41
41
|
<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">getToken(appCheck) on an app without initializeAppCheck</code><span class="compat-sub"><span class="compat-behavior">Rejects with a <code>FirebaseError</code> with code <code>appCheck/use-before-activation</code>, <code>customData.appName</code>, and the message telling the caller to call <code>initializeAppCheck()</code> first.</span></span></span></summary>
|
|
42
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-get-token-before-initialize.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-get-token-before-initialize</code></a> (<code>publicGetTokenWithUninitializedApp</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L148" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
42
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-get-token-before-initialize.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-get-token-before-initialize</code></a> (<code>publicGetTokenWithUninitializedApp</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L148" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
43
43
|
</details>
|
|
44
44
|
<details class="compat-row" data-status="ok">
|
|
45
45
|
<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">getToken(appCheck, forceRefresh?): Promise<AppCheckTokenResult></code><span class="compat-sub"><span class="compat-behavior">Resolves <code>{ token }</code> where <code>token</code> is a three-segment JWT: header <code>alg</code> RS256, <code>typ</code> JWT, with a <code>kid</code>; claims <code>aud</code>, <code>exp</code>, <code>iat</code>, <code>iss</code>, <code>jti</code>, <code>provider</code>, <code>sub</code>; <code>iss</code> is <code>https://firebaseappcheck.googleapis.com/<projectNumber></code>, <code>aud</code> is <code>[projects/<projectNumber>, projects/<projectId>]</code>, <code>sub</code> is the app id, and <code>exp - iat</code> is 3600 seconds.</span></span></span></summary>
|
|
46
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>first</code>, <code>forced</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
46
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>first</code>, <code>forced</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
47
47
|
</details>
|
|
48
48
|
<details class="compat-row" data-status="ok">
|
|
49
49
|
<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">getToken(appCheck, forceRefresh?)</code><span class="compat-sub"><span class="compat-behavior">A second <code>getToken</code> returns the cached token; <code>getToken(appCheck, true)</code> returns a different, newly minted token.</span></span></span></summary>
|
|
50
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>secondEqualsFirst</code>, <code>forceRefreshDiffersFromFirst</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
50
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-debug-get-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-debug-get-token</code></a> (<code>secondEqualsFirst</code>, <code>forceRefreshDiffersFromFirst</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L128" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
51
51
|
</details>
|
|
52
52
|
<details class="compat-row" data-status="ok">
|
|
53
53
|
<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">getLimitedUseToken(appCheck): Promise<AppCheckTokenResult></code><span class="compat-sub"><span class="compat-behavior">Resolves <code>{ token }</code> with the same JWT shape as <code>getToken</code>, a different token on every call, never the cached token, and leaves the cached <code>getToken</code> token unchanged.</span></span></span></summary>
|
|
54
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-limited-use-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-limited-use-token</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L181" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
54
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-limited-use-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-limited-use-token</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L181" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
55
55
|
</details>
|
|
56
56
|
<details class="compat-row" data-status="unsupported">
|
|
57
57
|
<summary class="compat-line"><span class="compat-dot" data-status="unsupported" role="img" aria-label="Not implemented yet" title="Not implemented yet"></span><span class="compat-main"><code class="compat-api">getToken / getLimitedUseToken when attestation fails</code><span class="compat-sub"><span class="compat-behavior">When the exchange is refused (an unregistered debug token), both reject with a <code>FirebaseError</code> with code <code>appCheck/fetch-status-error</code> and <code>customData.httpStatus</code> 403.</span></span></span></summary>
|
|
@@ -64,15 +64,15 @@ App Check is climbing under Conformance Driven Development. A row below is a tar
|
|
|
64
64
|
<div class="compat-list">
|
|
65
65
|
<details class="compat-row" data-status="ok">
|
|
66
66
|
<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">onTokenChanged(appCheck, listener | observer): Unsubscribe</code><span class="compat-sub"><span class="compat-behavior">Returns an unsubscribe function. A listener registered after a token exists is not called synchronously; it is called once, asynchronously, with the current cached token.</span></span></span></summary>
|
|
67
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (<code>unsubscribeType</code>, <code>lateListenerCallsSynchronous</code>, <code>lateListenerCallsWithin500ms</code>, <code>lateListenerTokenSequence</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L205" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
67
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (<code>unsubscribeType</code>, <code>lateListenerCallsSynchronous</code>, <code>lateListenerCallsWithin500ms</code>, <code>lateListenerTokenSequence</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L205" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
68
68
|
</details>
|
|
69
69
|
<details class="compat-row" data-status="diverged">
|
|
70
70
|
<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">onTokenChanged(appCheck, listener | observer)</code><span class="compat-sub"><span class="compat-behavior">Production, debug mode: listeners and observers registered before the first <code>getToken</code> are never called, not by the first token, a forced refresh, or limited-use tokens. Sandbox: they are called once each time the cached token changes (the first token and every forced refresh), never for limited-use tokens, and not after unsubscribing, which is the upstream-documented behavior for an attestation provider.</span></span></span></summary>
|
|
71
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (every <code>listenerCalls*</code> count is 0). The sandbox side and the attested-provider behavior are pinned in the suite; the attested-provider path is upstream-documented, not captured. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L206" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
71
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (every <code>listenerCalls*</code> count is 0). The sandbox side and the attested-provider behavior are pinned in the suite; the attested-provider path is upstream-documented, not captured. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L206" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
72
72
|
</details>
|
|
73
73
|
<details class="compat-row" data-status="ok">
|
|
74
74
|
<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">setTokenAutoRefreshEnabled(appCheck, isEnabled): void</code><span class="compat-sub"><span class="compat-behavior">Returns <code>undefined</code> and does not throw for either value; <code>getToken</code> still returns the cached token afterwards. Background refresh timing is not captured.</span></span></span></summary>
|
|
75
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-auto-refresh-toggle.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-auto-refresh-toggle</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L263" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
75
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-auto-refresh-toggle.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-auto-refresh-toggle</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L263" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L43" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div>
|
|
76
76
|
</details>
|
|
77
77
|
</div>
|
|
78
78
|
|
|
@@ -90,31 +90,47 @@ App Check is climbing under Conformance Driven Development. A row below is a tar
|
|
|
90
90
|
<div class="compat-list">
|
|
91
91
|
<details class="compat-row" data-status="ok">
|
|
92
92
|
<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">getAppCheck().createToken(appId)</code><span class="compat-sub"><span class="compat-behavior">Resolves <code>{ token, ttlMillis: 3600000 }</code>; the token has the web SDK token shape (RS256 JWT with <code>kid</code>, claims <code>aud</code>, <code>exp</code>, <code>iat</code>, <code>iss</code>, <code>jti</code>, <code>provider</code>, <code>sub</code>, <code>exp - iat</code> 3600).</span></span></span></summary>
|
|
93
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-shape.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-shape</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
93
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-shape.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-shape</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L70" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
94
94
|
</details>
|
|
95
95
|
<details class="compat-row" data-status="ok">
|
|
96
96
|
<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">getAppCheck().createToken(appId, { ttlMillis })</code><span class="compat-sub"><span class="compat-behavior"><code>ttlMillis</code> of 30 minutes is honored in the result and in <code>exp - iat</code>; 29 minutes and 7 days plus one second reject with <code>app-check/invalid-argument</code> and the message that names the 30 minute to 7 day range.</span></span></span></summary>
|
|
97
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-custom-ttl.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-custom-ttl</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
97
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-custom-ttl.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-custom-ttl</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L105" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
98
98
|
</details>
|
|
99
99
|
<details class="compat-row" data-status="diverged">
|
|
100
100
|
<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">getAppCheck().createToken(unknownAppId)</code><span class="compat-sub"><span class="compat-behavior">Production: an app id the project does not own rejects with <code>app-check/unknown-error</code> and the message <code>App not registered: <appId>.</code> Sandbox: it has no list of the project's registered apps, so it mints a token for any non-empty app id; an empty app id rejects with <code>app-check/invalid-argument</code>.</span></span></span></summary>
|
|
101
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-unknown-app.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-unknown-app</code></a>; the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
101
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-unknown-app.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-unknown-app</code></a>; the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L113" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
102
102
|
</details>
|
|
103
103
|
<details class="compat-row" data-status="ok">
|
|
104
104
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token)</code><span class="compat-sub"><span class="compat-behavior">Resolves <code>{ appId, token }</code> for an Admin-minted token and for a token from the web debug exchange; the decoded token carries <code>app_id</code>, <code>aud</code>, <code>exp</code>, <code>iat</code>, <code>iss</code>, <code>jti</code>, <code>provider</code>, <code>sub</code>, and no <code>alreadyConsumed</code> key is present.</span></span></span></summary>
|
|
105
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
105
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L126" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
106
106
|
</details>
|
|
107
107
|
<details class="compat-row" data-status="ok">
|
|
108
108
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(badToken)</code><span class="compat-sub"><span class="compat-behavior">A token with an altered signature rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has invalid signature.</code>; a non-JWT string and the empty string reject with <code>app-check/invalid-argument</code> and the decoding message.</span></span></span></summary>
|
|
109
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-tampered.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-tampered</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
109
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-tampered.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-tampered</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L143" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
110
110
|
</details>
|
|
111
111
|
<details class="compat-row" data-status="unverified">
|
|
112
|
-
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, options) with bad input, on another project, or with a foreign issuer, algorithm, subject, key, nbf, or exp</code><span class="compat-sub"><span class="compat-behavior">Options that are not a non-null object throw <code>app-check/invalid-argument</code> with <code>VerifyAppCheckTokenOptions must be a non-null object.</code>, and a token that is not a string throws <code>App check token must be a non-null string.</code>; both throw synchronously. Then, with no project id configured, <code>app-check/invalid-credential</code>. A token with a character outside base64url in any segment fails with the decoding message. Before the signature, the token content is checked in this order: a header <code>alg</code> other than RS256 rejects with <code>The provided App Check token has incorrect algorithm. Expected "RS256" but got "<alg>".</code>; an <code>aud</code> without <code>projects/<projectId></code> of the verifying app rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has incorrect "aud" (audience) claim. Expected "projects/<projectId>" but got "<aud>". Make sure the App Check token comes from the same Firebase project as the service account used to authenticate this SDK.</code>; an <code>iss</code> not starting with <code>https://firebaseappcheck.googleapis.com/</code> rejects with <code>The provided App Check token has incorrect "iss" (issuer) claim.</code>; a <code>sub</code> that is not a string rejects with <code>The provided App Check token has no "sub" (subject) claim.</code>, and an empty one with <code>The provided App Check token has an empty string "sub" (subject) claim.</code> Then a <code>kid</code>
|
|
112
|
+
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, options) with bad input, on another project, or with a foreign issuer, algorithm, subject, key, nbf, or exp</code><span class="compat-sub"><span class="compat-behavior">Options that are not a non-null object throw <code>app-check/invalid-argument</code> with <code>VerifyAppCheckTokenOptions must be a non-null object.</code>, and a token that is not a string throws <code>App check token must be a non-null string.</code>; both throw synchronously. Then, with no project id configured, <code>app-check/invalid-credential</code>. A token with a character outside base64url in any segment fails with the decoding message. Before the signature, the token content is checked in this order: a header <code>alg</code> other than RS256 rejects with <code>The provided App Check token has incorrect algorithm. Expected "RS256" but got "<alg>".</code>; an <code>aud</code> without <code>projects/<projectId></code> of the verifying app rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has incorrect "aud" (audience) claim. Expected "projects/<projectId>" but got "<aud>". Make sure the App Check token comes from the same Firebase project as the service account used to authenticate this SDK.</code>; an <code>iss</code> not starting with <code>https://firebaseappcheck.googleapis.com/</code> rejects with <code>The provided App Check token has incorrect "iss" (issuer) claim.</code>; a <code>sub</code> that is not a string rejects with <code>The provided App Check token has no "sub" (subject) claim.</code>, and an empty one with <code>The provided App Check token has an empty string "sub" (subject) claim.</code> Then a token with no <code>kid</code> is checked against every key. A <code>kid</code> that names a key other than the sandbox's marks a token from Firebase: in sandbox AI mode it rejects with Pyric's message (row <code>verify-token-firebase-token</code>), and in production AI mode it goes to firebase-admin (row <code>verify-token-firebase-token-production-ai-mode</code>). After the signature, a non-numeric or future <code>nbf</code> and a non-numeric <code>exp</code> reject as an invalid signature; an absent <code>nbf</code> or <code>exp</code> does not apply.</span></span></span></summary>
|
|
113
113
|
<div class="compat-evidence"><div class="compat-probe">Upstream-documented only: firebase-admin 13.10.0 <code>lib/app-check/token-verifier.js</code> (<code>verifyToken</code>, <code>verifyContent</code>, <code>ensureProjectId</code>, <code>mapJwtErrorToAppCheckError</code>), <code>lib/utils/jwt.js</code> (<code>getKeyCallback</code>, <code>PublicKeySignatureVerifier.verify</code>), jws 4 <code>verify-stream.js</code> (<code>JWS_REGEX</code>), and jsonwebtoken 9 <code>verify.js</code> (<code>nbf</code>, <code>exp</code>). No capture; verification runs locally in the Admin SDK, so a capture would need no production change.</div></div>
|
|
114
114
|
</details>
|
|
115
115
|
<details class="compat-row" data-status="ok">
|
|
116
116
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, { consume: true })</code><span class="compat-sub"><span class="compat-behavior">The first consume resolves with <code>alreadyConsumed: false</code>, a second consume with <code>alreadyConsumed: true</code>, and a plain verify afterwards has no <code>alreadyConsumed</code> key.</span></span></span></summary>
|
|
117
|
-
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-consume.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-consume</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
117
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-consume.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-consume</code></a>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L197" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
118
|
+
</details>
|
|
119
|
+
<details class="compat-row" data-status="diverged">
|
|
120
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken) in sandbox AI mode</code><span class="compat-sub"><span class="compat-behavior">Production verifies the App Check tokens Firebase issues. Sandbox: in sandbox AI mode, or in a process not started with <code>@pyric/cli/register</code> in production AI mode, a token whose <code>kid</code> names a key other than the sandbox's rejects with <code>app-check/invalid-argument</code> and Pyric's message, which says the token was not minted by the Pyric sandbox and to start the server in production AI mode (<code>PYRIC_AI_MODE=production</code>). The project check comes first, as in production.</span></span></span></summary>
|
|
121
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a> (production verifies a token from its debug exchange); the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
122
|
+
</details>
|
|
123
|
+
<details class="compat-row" data-status="unverified">
|
|
124
|
+
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken) under @pyric/cli/register in production AI mode</code><span class="compat-sub"><span class="compat-behavior">A token whose <code>kid</code> names a key other than the sandbox's goes to firebase-admin's own <code>verifyToken</code>, for an app scoped to the project the mirror resolves. It resolves with production's <code>{ appId, token }</code> shape, the decoded token carrying <code>app_id</code>, and firebase-admin's errors (signature, audience, issuer, expiry) pass through with their codes and messages. Sandbox tokens, tokens with no <code>kid</code>, and malformed tokens keep the sandbox path.</span></span></span></summary>
|
|
125
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a> for the result shape; the verification itself is firebase-admin 13.10.0 <code>lib/app-check/token-verifier.js</code>, run unchanged.</div></div>
|
|
126
|
+
</details>
|
|
127
|
+
<details class="compat-row" data-status="diverged">
|
|
128
|
+
<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">getAppCheck().verifyToken(firebaseToken, { consume: true }) in production AI mode</code><span class="compat-sub"><span class="compat-behavior">Production records consumption on Firebase's servers, once per project: the first consume resolves with <code>alreadyConsumed: false</code> and a second with <code>alreadyConsumed: true</code>. Sandbox: firebase-admin verifies the token without <code>consume</code>, and the mirror records it in the process, in the set it keeps for sandbox tokens, with the same <code>false</code> then <code>true</code> results.</span></span></span></summary>
|
|
129
|
+
<div class="compat-evidence"><div class="compat-probe">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-consume.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-consume</code></a>; the sandbox side is pinned in the admin suite and the production AI mode app scenario. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L198" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div>
|
|
130
|
+
</details>
|
|
131
|
+
<details class="compat-row" data-status="unverified">
|
|
132
|
+
<summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">initializeApp({ credential: cert(serviceAccount) }), then getAppCheck().createToken and verifyToken</code><span class="compat-sub"><span class="compat-behavior">An app initialized with only a cert credential finds its project in the service account's <code>project_id</code> (or <code>projectId</code>), so <code>createToken</code> mints a token whose <code>aud</code> names <code>projects/<project_id></code> and <code>verifyToken</code> accepts it. The <code>projectId</code> app option wins over the credential, and the credential wins over <code>GOOGLE_CLOUD_PROJECT</code> and <code>GCLOUD_PROJECT</code>. The project id is not copied into <code>app.options</code>.</span></span></span></summary>
|
|
133
|
+
<div class="compat-evidence"><div class="compat-probe">Upstream-documented only: firebase-admin 13.6.0 <code>lib/utils/index.js</code> (<code>getExplicitProjectId</code>) and <code>lib/app/credential-internal.js</code> (<code>ServiceAccount</code>, <code>copyAttr</code>). No capture; the project is resolved locally in the Admin SDK.</div></div>
|
|
118
134
|
</details>
|
|
119
135
|
</div>
|
|
120
136
|
|
|
@@ -126,9 +142,13 @@ Known differences between Pyric and production Firebase. Each remains tracked as
|
|
|
126
142
|
|
|
127
143
|
<div class="compat-list">
|
|
128
144
|
<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">onTokenChanged(appCheck, listener | observer)</code><span class="compat-sub"><span class="compat-behavior">Production, debug mode: listeners and observers registered before the first <code>getToken</code> are never called, not by the first token, a forced refresh, or limited-use tokens. Sandbox: they are called once each time the cached token changes (the first token and every forced refresh), never for limited-use tokens, and not after unsubscribing, which is the upstream-documented behavior for an attestation provider.</span></span></span></summary>
|
|
129
|
-
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (every <code>listenerCalls*</code> count is 0). The sandbox side and the attested-provider behavior are pinned in the suite; the attested-provider path is upstream-documented, not captured. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L206" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#
|
|
145
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check/app-check-client-on-token-changed.json" target="_blank" rel="noopener noreferrer"><code>app-check-client-on-token-changed</code></a> (every <code>listenerCalls*</code> count is 0). The sandbox side and the attested-provider behavior are pinned in the suite; the attested-provider path is upstream-documented, not captured. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/app-check/oracle-conformance.test.ts#L206" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/test/apps/sandbox-app-check/scenario.ts#L80" target="_blank" rel="noopener noreferrer"><code>scenario.ts</code></a>)</div></div></details>
|
|
130
146
|
<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">getAppCheck().createToken(unknownAppId)</code><span class="compat-sub"><span class="compat-behavior">Production: an app id the project does not own rejects with <code>app-check/unknown-error</code> and the message <code>App not registered: <appId>.</code> Sandbox: it has no list of the project's registered apps, so it mints a token for any non-empty app id; an empty app id rejects with <code>app-check/invalid-argument</code>.</span></span></span></summary>
|
|
131
|
-
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-unknown-app.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-unknown-app</code></a>; the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#
|
|
147
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-create-token-unknown-app.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-create-token-unknown-app</code></a>; the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L113" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
|
|
148
|
+
<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">getAppCheck().verifyToken(firebaseToken) in sandbox AI mode</code><span class="compat-sub"><span class="compat-behavior">Production verifies the App Check tokens Firebase issues. Sandbox: in sandbox AI mode, or in a process not started with <code>@pyric/cli/register</code> in production AI mode, a token whose <code>kid</code> names a key other than the sandbox's rejects with <code>app-check/invalid-argument</code> and Pyric's message, which says the token was not minted by the Pyric sandbox and to start the server in production AI mode (<code>PYRIC_AI_MODE=production</code>). The project check comes first, as in production.</span></span></span></summary>
|
|
149
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a> (production verifies a token from its debug exchange); the sandbox side is pinned in the admin suite. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L127" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
|
|
150
|
+
<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">getAppCheck().verifyToken(firebaseToken, { consume: true }) in production AI mode</code><span class="compat-sub"><span class="compat-behavior">Production records consumption on Firebase's servers, once per project: the first consume resolves with <code>alreadyConsumed: false</code> and a second with <code>alreadyConsumed: true</code>. Sandbox: firebase-admin verifies the token without <code>consume</code>, and the mirror records it in the process, in the set it keeps for sandbox tokens, with the same <code>false</code> then <code>true</code> results.</span></span></span></summary>
|
|
151
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-consume.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-consume</code></a>; the sandbox side is pinned in the admin suite and the production AI mode app scenario. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric-admin/test/app-check/oracle-conformance.test.ts#L198" target="_blank" rel="noopener noreferrer"><code>oracle-conformance.test.ts</code></a>)</div></div></details>
|
|
132
152
|
</div>
|
|
133
153
|
|
|
134
154
|
### Not implemented yet
|
|
@@ -149,6 +169,10 @@ Tracked behavior whose available evidence does not yet establish the production
|
|
|
149
169
|
<div class="compat-list">
|
|
150
170
|
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">new ReCaptchaV3Provider(siteKey); new ReCaptchaEnterpriseProvider(siteKey)</code><span class="compat-sub"><span class="compat-behavior">Both reCAPTCHA providers construct from a site key and are accepted by <code>initializeAppCheck</code>.</span></span></span></summary>
|
|
151
171
|
<div class="compat-evidence"><div class="compat-note">Upstream-documented only (firebase 12.13.0 typings and the App Check web guide). The oracle web app has no reCAPTCHA registration, so no capture exists; the sandbox never loads reCAPTCHA.</div></div></details>
|
|
152
|
-
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, options) with bad input, on another project, or with a foreign issuer, algorithm, subject, key, nbf, or exp</code><span class="compat-sub"><span class="compat-behavior">Options that are not a non-null object throw <code>app-check/invalid-argument</code> with <code>VerifyAppCheckTokenOptions must be a non-null object.</code>, and a token that is not a string throws <code>App check token must be a non-null string.</code>; both throw synchronously. Then, with no project id configured, <code>app-check/invalid-credential</code>. A token with a character outside base64url in any segment fails with the decoding message. Before the signature, the token content is checked in this order: a header <code>alg</code> other than RS256 rejects with <code>The provided App Check token has incorrect algorithm. Expected "RS256" but got "<alg>".</code>; an <code>aud</code> without <code>projects/<projectId></code> of the verifying app rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has incorrect "aud" (audience) claim. Expected "projects/<projectId>" but got "<aud>". Make sure the App Check token comes from the same Firebase project as the service account used to authenticate this SDK.</code>; an <code>iss</code> not starting with <code>https://firebaseappcheck.googleapis.com/</code> rejects with <code>The provided App Check token has incorrect "iss" (issuer) claim.</code>; a <code>sub</code> that is not a string rejects with <code>The provided App Check token has no "sub" (subject) claim.</code>, and an empty one with <code>The provided App Check token has an empty string "sub" (subject) claim.</code> Then a <code>kid</code>
|
|
172
|
+
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(token, options) with bad input, on another project, or with a foreign issuer, algorithm, subject, key, nbf, or exp</code><span class="compat-sub"><span class="compat-behavior">Options that are not a non-null object throw <code>app-check/invalid-argument</code> with <code>VerifyAppCheckTokenOptions must be a non-null object.</code>, and a token that is not a string throws <code>App check token must be a non-null string.</code>; both throw synchronously. Then, with no project id configured, <code>app-check/invalid-credential</code>. A token with a character outside base64url in any segment fails with the decoding message. Before the signature, the token content is checked in this order: a header <code>alg</code> other than RS256 rejects with <code>The provided App Check token has incorrect algorithm. Expected "RS256" but got "<alg>".</code>; an <code>aud</code> without <code>projects/<projectId></code> of the verifying app rejects with <code>app-check/invalid-argument</code> and <code>The provided App Check token has incorrect "aud" (audience) claim. Expected "projects/<projectId>" but got "<aud>". Make sure the App Check token comes from the same Firebase project as the service account used to authenticate this SDK.</code>; an <code>iss</code> not starting with <code>https://firebaseappcheck.googleapis.com/</code> rejects with <code>The provided App Check token has incorrect "iss" (issuer) claim.</code>; a <code>sub</code> that is not a string rejects with <code>The provided App Check token has no "sub" (subject) claim.</code>, and an empty one with <code>The provided App Check token has an empty string "sub" (subject) claim.</code> Then a token with no <code>kid</code> is checked against every key. A <code>kid</code> that names a key other than the sandbox's marks a token from Firebase: in sandbox AI mode it rejects with Pyric's message (row <code>verify-token-firebase-token</code>), and in production AI mode it goes to firebase-admin (row <code>verify-token-firebase-token-production-ai-mode</code>). After the signature, a non-numeric or future <code>nbf</code> and a non-numeric <code>exp</code> reject as an invalid signature; an absent <code>nbf</code> or <code>exp</code> does not apply.</span></span></span></summary>
|
|
153
173
|
<div class="compat-evidence"><div class="compat-note">Upstream-documented only: firebase-admin 13.10.0 <code>lib/app-check/token-verifier.js</code> (<code>verifyToken</code>, <code>verifyContent</code>, <code>ensureProjectId</code>, <code>mapJwtErrorToAppCheckError</code>), <code>lib/utils/jwt.js</code> (<code>getKeyCallback</code>, <code>PublicKeySignatureVerifier.verify</code>), jws 4 <code>verify-stream.js</code> (<code>JWS_REGEX</code>), and jsonwebtoken 9 <code>verify.js</code> (<code>nbf</code>, <code>exp</code>). No capture; verification runs locally in the Admin SDK, so a capture would need no production change.</div></div></details>
|
|
174
|
+
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">getAppCheck().verifyToken(firebaseToken) under @pyric/cli/register in production AI mode</code><span class="compat-sub"><span class="compat-behavior">A token whose <code>kid</code> names a key other than the sandbox's goes to firebase-admin's own <code>verifyToken</code>, for an app scoped to the project the mirror resolves. It resolves with production's <code>{ appId, token }</code> shape, the decoded token carrying <code>app_id</code>, and firebase-admin's errors (signature, audience, issuer, expiry) pass through with their codes and messages. Sandbox tokens, tokens with no <code>kid</code>, and malformed tokens keep the sandbox path.</span></span></span></summary>
|
|
175
|
+
<div class="compat-evidence"><div class="compat-note">capture <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/app-check-admin/app-check-admin-verify-token.json" target="_blank" rel="noopener noreferrer"><code>app-check-admin-verify-token</code></a> for the result shape; the verification itself is firebase-admin 13.10.0 <code>lib/app-check/token-verifier.js</code>, run unchanged.</div></div></details>
|
|
176
|
+
<details class="compat-row" data-status="unverified"><summary class="compat-line"><span class="compat-dot" data-status="unverified" role="img" aria-label="Unverified" title="Unverified"></span><span class="compat-main"><code class="compat-api">initializeApp({ credential: cert(serviceAccount) }), then getAppCheck().createToken and verifyToken</code><span class="compat-sub"><span class="compat-behavior">An app initialized with only a cert credential finds its project in the service account's <code>project_id</code> (or <code>projectId</code>), so <code>createToken</code> mints a token whose <code>aud</code> names <code>projects/<project_id></code> and <code>verifyToken</code> accepts it. The <code>projectId</code> app option wins over the credential, and the credential wins over <code>GOOGLE_CLOUD_PROJECT</code> and <code>GCLOUD_PROJECT</code>. The project id is not copied into <code>app.options</code>.</span></span></span></summary>
|
|
177
|
+
<div class="compat-evidence"><div class="compat-note">Upstream-documented only: firebase-admin 13.6.0 <code>lib/utils/index.js</code> (<code>getExplicitProjectId</code>) and <code>lib/app/credential-internal.js</code> (<code>ServiceAccount</code>, <code>copyAttr</code>). No capture; the project is resolved locally in the Admin SDK.</div></div></details>
|
|
154
178
|
</div>
|
|
@@ -588,7 +588,7 @@ html{scrollbar-gutter:stable}
|
|
|
588
588
|
<div class="compat-list">
|
|
589
589
|
<details class="compat-row" data-status="diverged">
|
|
590
590
|
<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">getAuth(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/auth</code> in a server process signs in to the host: the session belongs to that process, the user is visible to <code>firebase-admin</code>, and its identity applies to the process's RTDB, Firestore and Storage calls. The session lasts as long as the process. 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>
|
|
591
|
-
<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 user the process created that firebase-admin finds, and rules that allow that user's own path and deny another's. 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#
|
|
591
|
+
<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 user the process created that firebase-admin finds, and rules that allow that user's own path and deny another's. 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#L213" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div>
|
|
592
592
|
<div class="compat-note">one configuration per runtime; the session is not persisted across processes</div></div>
|
|
593
593
|
</details>
|
|
594
594
|
</div>
|
|
@@ -597,7 +597,7 @@ html{scrollbar-gutter:stable}
|
|
|
597
597
|
<div class="compat-list">
|
|
598
598
|
<details class="compat-row" data-status="diverged">
|
|
599
599
|
<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">getAuth(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/auth</code> in a server-rendered module signs in to the host. The session belongs to the dev server process: every SSR request, both of Vite's SSR loaders and a module reloaded after an edit see the same <code>currentUser</code>, and its identity applies to the SSR RTDB, Firestore and Storage calls. Browser sessions are separate. 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>
|
|
600
|
-
<div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> signs in through <code>ssrLoadModule</code>, then finds the same user through the environment runner, and checks that the rules allow that user's paths and deny another's. 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#
|
|
600
|
+
<div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> signs in through <code>ssrLoadModule</code>, then finds the same user through the environment runner, and checks that the rules allow that user's paths and deny another's. 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>
|
|
601
601
|
<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>
|
|
602
602
|
</details>
|
|
603
603
|
</div>
|
|
@@ -636,9 +636,9 @@ html{scrollbar-gutter:stable}
|
|
|
636
636
|
<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">An Auth operation (a sign-in, a profile update, a token refresh) 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 Auth Web SDK fails a request whose network call fails with <code>auth/network-request-failed</code> and does not retry it.</span></span></span></summary>
|
|
637
637
|
<div class="compat-evidence"><div class="compat-note">Production behavior read from <code>@firebase/auth</code> 1.13.1 <code>dist/esm</code>: a failed <code>fetch</code> is reported through <code>_fail(auth, "network-request-failed")</code>, and the request is not retried. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/hosted-reconnect-hold.test.ts#L2" 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>auth.signInEmail</code> in flight and asserts it fails once with that message and reaches the host once.</div></div></details>
|
|
638
638
|
<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">getAuth(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/auth</code> in a server process signs in to the host: the session belongs to that process, the user is visible to <code>firebase-admin</code>, and its identity applies to the process's RTDB, Firestore and Storage calls. The session lasts as long as the process. 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>
|
|
639
|
-
<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 user the process created that firebase-admin finds, and rules that allow that user's own path and deny another's. 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#
|
|
639
|
+
<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 user the process created that firebase-admin finds, and rules that allow that user's own path and deny another's. 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#L213" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div></div></details>
|
|
640
640
|
<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">getAuth(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/auth</code> in a server-rendered module signs in to the host. The session belongs to the dev server process: every SSR request, both of Vite's SSR loaders and a module reloaded after an edit see the same <code>currentUser</code>, and its identity applies to the SSR RTDB, Firestore and Storage calls. Browser sessions are separate. 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>
|
|
641
|
-
<div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> signs in through <code>ssrLoadModule</code>, then finds the same user through the environment runner, and checks that the rules allow that user's paths and deny another's. 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#
|
|
641
|
+
<div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> signs in through <code>ssrLoadModule</code>, then finds the same user through the environment runner, and checks that the rules allow that user's paths and deny another's. 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>
|
|
642
642
|
</div>
|
|
643
643
|
<h3 id="not-implemented-yet">Not implemented yet</h3>
|
|
644
644
|
<p>Tracked behavior that is not implemented in the current contract.</p>
|
|
@@ -635,7 +635,7 @@ A server process started with `node --import @pyric/cli/register` and `PYRIC_SAN
|
|
|
635
635
|
<div class="compat-list">
|
|
636
636
|
<details class="compat-row" data-status="diverged">
|
|
637
637
|
<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">getAuth(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/auth</code> in a server process signs in to the host: the session belongs to that process, the user is visible to <code>firebase-admin</code>, and its identity applies to the process's RTDB, Firestore and Storage calls. The session lasts as long as the process. 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>
|
|
638
|
-
<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 user the process created that firebase-admin finds, and rules that allow that user's own path and deny another's. 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#
|
|
638
|
+
<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 user the process created that firebase-admin finds, and rules that allow that user's own path and deny another's. 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#L213" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div>
|
|
639
639
|
<div class="compat-note">one configuration per runtime; the session is not persisted across processes</div></div>
|
|
640
640
|
</details>
|
|
641
641
|
</div>
|
|
@@ -647,7 +647,7 @@ Modules Vite loads for server rendering under `vite dev` with `pyric()` (`server
|
|
|
647
647
|
<div class="compat-list">
|
|
648
648
|
<details class="compat-row" data-status="diverged">
|
|
649
649
|
<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">getAuth(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/auth</code> in a server-rendered module signs in to the host. The session belongs to the dev server process: every SSR request, both of Vite's SSR loaders and a module reloaded after an edit see the same <code>currentUser</code>, and its identity applies to the SSR RTDB, Firestore and Storage calls. Browser sessions are separate. 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>
|
|
650
|
-
<div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> signs in through <code>ssrLoadModule</code>, then finds the same user through the environment runner, and checks that the rules allow that user's paths and deny another's. 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#
|
|
650
|
+
<div class="compat-evidence"><div class="compat-probe">App scenario <code>test/apps/vite-ssr-client-sdk</code> signs in through <code>ssrLoadModule</code>, then finds the same user through the environment runner, and checks that the rules allow that user's paths and deny another's. 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>
|
|
651
651
|
<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>
|
|
652
652
|
</details>
|
|
653
653
|
</div>
|
|
@@ -690,9 +690,9 @@ Known differences between Pyric and production Firebase. Each remains tracked as
|
|
|
690
690
|
<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">An Auth operation (a sign-in, a profile update, a token refresh) 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 Auth Web SDK fails a request whose network call fails with <code>auth/network-request-failed</code> and does not retry it.</span></span></span></summary>
|
|
691
691
|
<div class="compat-evidence"><div class="compat-note">Production behavior read from <code>@firebase/auth</code> 1.13.1 <code>dist/esm</code>: a failed <code>fetch</code> is reported through <code>_fail(auth, "network-request-failed")</code>, and the request is not retried. <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/worker/hosted-reconnect-hold.test.ts#L2" 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>auth.signInEmail</code> in flight and asserts it fails once with that message and reaches the host once.</div></div></details>
|
|
692
692
|
<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">getAuth(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/auth</code> in a server process signs in to the host: the session belongs to that process, the user is visible to <code>firebase-admin</code>, and its identity applies to the process's RTDB, Firestore and Storage calls. The session lasts as long as the process. 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>
|
|
693
|
-
<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 user the process created that firebase-admin finds, and rules that allow that user's own path and deny another's. 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#
|
|
693
|
+
<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 user the process created that firebase-admin finds, and rules that allow that user's own path and deny another's. 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#L213" target="_blank" rel="noopener noreferrer"><code>register-child.test.ts</code></a>)</div></div></details>
|
|
694
694
|
<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">getAuth(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/auth</code> in a server-rendered module signs in to the host. The session belongs to the dev server process: every SSR request, both of Vite's SSR loaders and a module reloaded after an edit see the same <code>currentUser</code>, and its identity applies to the SSR RTDB, Firestore and Storage calls. Browser sessions are separate. 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>
|
|
695
|
-
<div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> signs in through <code>ssrLoadModule</code>, then finds the same user through the environment runner, and checks that the rules allow that user's paths and deny another's. 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#
|
|
695
|
+
<div class="compat-evidence"><div class="compat-note">App scenario <code>test/apps/vite-ssr-client-sdk</code> signs in through <code>ssrLoadModule</code>, then finds the same user through the environment runner, and checks that the rules allow that user's paths and deny another's. 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>
|
|
696
696
|
</div>
|
|
697
697
|
|
|
698
698
|
### Not implemented yet
|