@pyric/cli 0.1.0-alpha.15 → 0.1.0-alpha.17
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/README.md +13 -9
- package/dist/assurance/.generated/conformance-verdicts.d.ts +80 -72
- package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
- package/dist/assurance/.generated/conformance-verdicts.js +80 -72
- package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
- package/dist/bridge/client/dispatch.js +1 -1
- package/dist/bridge/client/dispatch.js.map +1 -1
- package/dist/cli/database-rules.d.ts.map +1 -1
- package/dist/cli/database-rules.js +6 -4
- package/dist/cli/database-rules.js.map +1 -1
- package/dist/cli/dev-runner.d.ts +9 -0
- package/dist/cli/dev-runner.d.ts.map +1 -1
- package/dist/cli/dev-runner.js +18 -0
- package/dist/cli/dev-runner.js.map +1 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +61 -6
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/verify.js +2 -2
- package/dist/cli/verify.js.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js +1 -1
- 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/next/client-aliases.d.ts +11 -0
- package/dist/next/client-aliases.d.ts.map +1 -0
- package/dist/next/client-aliases.js +38 -0
- package/dist/next/client-aliases.js.map +1 -0
- package/dist/next/dev-rewrites.d.ts +9 -0
- package/dist/next/dev-rewrites.d.ts.map +1 -0
- package/dist/next/dev-rewrites.js +53 -0
- package/dist/next/dev-rewrites.js.map +1 -0
- package/dist/next/guard.d.ts +15 -0
- package/dist/next/guard.d.ts.map +1 -0
- package/dist/next/guard.js +52 -0
- package/dist/next/guard.js.map +1 -0
- package/dist/next/index.d.ts +12 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +11 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/runtime-env.d.ts +7 -0
- package/dist/next/runtime-env.d.ts.map +1 -0
- package/dist/next/runtime-env.js +13 -0
- package/dist/next/runtime-env.js.map +1 -0
- package/dist/next/server-external-packages.d.ts +10 -0
- package/dist/next/server-external-packages.d.ts.map +1 -0
- package/dist/next/server-external-packages.js +34 -0
- package/dist/next/server-external-packages.js.map +1 -0
- package/dist/next/target-url.d.ts +11 -0
- package/dist/next/target-url.d.ts.map +1 -0
- package/dist/next/target-url.js +36 -0
- package/dist/next/target-url.js.map +1 -0
- package/dist/next/turbopack-config.d.ts +9 -0
- package/dist/next/turbopack-config.d.ts.map +1 -0
- package/dist/next/turbopack-config.js +39 -0
- package/dist/next/turbopack-config.js.map +1 -0
- package/dist/next/types.d.ts +40 -0
- package/dist/next/types.d.ts.map +1 -0
- package/dist/next/types.js +5 -0
- package/dist/next/types.js.map +1 -0
- package/dist/next/webpack-config.d.ts +11 -0
- package/dist/next/webpack-config.d.ts.map +1 -0
- package/dist/next/webpack-config.js +51 -0
- package/dist/next/webpack-config.js.map +1 -0
- package/dist/next/with-pyric.d.ts +22 -0
- package/dist/next/with-pyric.d.ts.map +1 -0
- package/dist/next/with-pyric.js +49 -0
- package/dist/next/with-pyric.js.map +1 -0
- package/dist/register/app-bridge.d.ts +9 -0
- package/dist/register/app-bridge.d.ts.map +1 -0
- package/dist/register/app-bridge.js +56 -0
- package/dist/register/app-bridge.js.map +1 -0
- package/dist/register/hooks.d.ts.map +1 -1
- package/dist/register/hooks.js +6 -1
- package/dist/register/hooks.js.map +1 -1
- package/dist/register/index.js +1 -1
- package/dist/register/index.js.map +1 -1
- package/dist/register/mapping.d.ts +3 -1
- package/dist/register/mapping.d.ts.map +1 -1
- package/dist/register/mapping.js +41 -5
- package/dist/register/mapping.js.map +1 -1
- package/dist/rtdb/rules-json.d.ts +12 -0
- package/dist/rtdb/rules-json.d.ts.map +1 -1
- package/dist/rtdb/rules-json.js +98 -0
- package/dist/rtdb/rules-json.js.map +1 -1
- package/dist/serve/bundler.d.ts.map +1 -1
- package/dist/serve/bundler.js +2 -0
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/discovery.d.ts +4 -4
- package/dist/serve/discovery.d.ts.map +1 -1
- package/dist/serve/discovery.js +59 -38
- package/dist/serve/discovery.js.map +1 -1
- package/dist/serve/entries/app-ai-passthrough.d.ts +8 -0
- package/dist/serve/entries/app-ai-passthrough.d.ts.map +1 -0
- package/dist/serve/entries/app-ai-passthrough.js +59 -0
- package/dist/serve/entries/app-ai-passthrough.js.map +1 -0
- package/dist/serve/entries/auth-helper-core.d.ts +9 -0
- package/dist/serve/entries/auth-helper-core.d.ts.map +1 -1
- package/dist/serve/entries/auth-helper-core.js +37 -8
- package/dist/serve/entries/auth-helper-core.js.map +1 -1
- package/dist/serve/entries/auth.d.ts +1 -0
- package/dist/serve/entries/auth.d.ts.map +1 -1
- package/dist/serve/entries/auth.js +7 -4
- package/dist/serve/entries/auth.js.map +1 -1
- package/dist/serve/entries/bridge-url.d.ts.map +1 -1
- package/dist/serve/entries/bridge-url.js +7 -2
- package/dist/serve/entries/bridge-url.js.map +1 -1
- package/dist/serve/entries/database.d.ts +1 -0
- package/dist/serve/entries/database.d.ts.map +1 -1
- package/dist/serve/entries/database.js +1 -0
- package/dist/serve/entries/database.js.map +1 -1
- package/dist/serve/entries/firestore.d.ts +1 -0
- package/dist/serve/entries/firestore.d.ts.map +1 -1
- package/dist/serve/entries/firestore.js +1 -0
- package/dist/serve/entries/firestore.js.map +1 -1
- package/dist/serve/entries/init.d.ts.map +1 -1
- package/dist/serve/entries/init.js +4 -2
- package/dist/serve/entries/init.js.map +1 -1
- package/dist/serve/entries/runtime.js +23 -0
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/entries/storage.d.ts +2 -0
- package/dist/serve/entries/storage.d.ts.map +1 -1
- package/dist/serve/entries/storage.js +9 -2
- package/dist/serve/entries/storage.js.map +1 -1
- package/dist/serve/html-injection.d.ts +3 -1
- package/dist/serve/html-injection.d.ts.map +1 -1
- package/dist/serve/html-injection.js +27 -1
- package/dist/serve/html-injection.js.map +1 -1
- package/dist/serve/rules.js +2 -2
- package/dist/serve/rules.js.map +1 -1
- package/dist/serve/runtime/chip-config.d.ts +1 -1
- package/dist/serve/runtime/chip-config.d.ts.map +1 -1
- package/dist/serve/runtime/chip-config.js +16 -8
- package/dist/serve/runtime/chip-config.js.map +1 -1
- package/dist/serve/sandbox-session.d.ts +1 -0
- package/dist/serve/sandbox-session.d.ts.map +1 -1
- package/dist/serve/sandbox-session.js +30 -2
- package/dist/serve/sandbox-session.js.map +1 -1
- package/dist/serve/site-ui/_astro/_studio_.B6b_G1ev.css +1 -0
- package/dist/serve/site-ui/_astro/app.bnmVTkIY.js +11 -0
- package/dist/serve/site-ui/_astro/app.t6u-adB_.js +1 -0
- package/dist/serve/site-ui/_astro/{chess-showcase.DzPO6kz0.js → chess-showcase.kC8dXLKY.js} +1 -1
- package/dist/serve/site-ui/_astro/{example-runtime.CM0H02rx.js → example-runtime.D79PnoSk.js} +1 -1
- package/dist/serve/site-ui/_astro/index.DzVde04M.js +1 -0
- package/dist/serve/site-ui/_astro/reads.BYBRLbsE.js +1 -0
- package/dist/serve/site-ui/_astro/sandbox-controls.C9Aaazt8.js +1252 -0
- package/dist/serve/site-ui/_astro/{seed.BCn5DhHS.js → seed.DANAIyRw.js} +1 -1
- package/dist/serve/site-ui/auth/index.html +2 -2
- package/dist/serve/site-ui/docs/_rhythm/index.html +1 -1
- package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +1 -1
- package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +1 -1
- package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +1 -1
- package/dist/serve/site-ui/docs/ai-compat/index.html +85 -85
- package/dist/serve/site-ui/docs/ai-compat.md +84 -84
- package/dist/serve/site-ui/docs/api-reference/index.html +3 -1
- package/dist/serve/site-ui/docs/api-reference.md +2 -0
- package/dist/serve/site-ui/docs/app-compat/index.html +28 -28
- package/dist/serve/site-ui/docs/app-compat.md +27 -27
- package/dist/serve/site-ui/docs/auth-compat/index.html +122 -122
- package/dist/serve/site-ui/docs/auth-compat.md +121 -121
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +1 -1
- package/dist/serve/site-ui/docs/build/authentication/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-storage/index.html +1 -1
- package/dist/serve/site-ui/docs/build/realtime-database/index.html +1 -1
- package/dist/serve/site-ui/docs/conformance-scores/index.html +4 -4
- package/dist/serve/site-ui/docs/conformance-scores.md +3 -3
- package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +6 -6
- package/dist/serve/site-ui/docs/create-pyric-reference-api.md +5 -5
- package/dist/serve/site-ui/docs/database-compat/index.html +200 -448
- package/dist/serve/site-ui/docs/database-compat.md +198 -461
- package/dist/serve/site-ui/docs/examples/chess/index.html +2 -2
- package/dist/serve/site-ui/docs/examples/index.html +1 -1
- package/dist/serve/site-ui/docs/firestore-compat/index.html +186 -195
- package/dist/serve/site-ui/docs/firestore-compat.md +184 -195
- package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +15 -15
- package/dist/serve/site-ui/docs/functions-rtdb-compat.md +14 -14
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/nextjs/index.html +57 -0
- package/dist/serve/site-ui/docs/get-started/nextjs.md +86 -0
- package/dist/serve/site-ui/docs/get-started/start-building/index.html +25 -8
- package/dist/serve/site-ui/docs/get-started/start-building.md +34 -5
- package/dist/serve/site-ui/docs/get-started/vite/index.html +26 -0
- package/dist/serve/site-ui/docs/get-started/vite.md +45 -0
- package/dist/serve/site-ui/docs/index.json +668 -103
- package/dist/serve/site-ui/docs/messaging-compat/index.html +56 -56
- package/dist/serve/site-ui/docs/messaging-compat.md +55 -55
- package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +1 -1
- package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +1 -1
- package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +1 -1
- package/dist/serve/site-ui/docs/overview/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +39 -13
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api.md +28 -2
- package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +38 -6
- package/dist/serve/site-ui/docs/pyric-admin-database-reference-api.md +43 -0
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +9 -1
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api.md +8 -0
- package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +27 -4
- package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api.md +23 -0
- package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +2 -2
- package/dist/serve/site-ui/docs/pyric-auth-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +123 -0
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api.md +99 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +127 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api.md +174 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +570 -326
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api.md +198 -6
- package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +2971 -627
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api.md +2873 -517
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +43 -3
- package/dist/serve/site-ui/docs/pyric-rules-reference-api.md +8 -0
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +26 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api.md +24 -0
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +77 -9
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api.md +28 -13
- package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +240 -63
- package/dist/serve/site-ui/docs/pyric-storage-reference-api.md +164 -5
- package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +2 -1
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api.md +1 -0
- package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/rules-compat/index.html +87 -79
- package/dist/serve/site-ui/docs/rules-compat.md +86 -78
- package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
- package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +1 -1
- package/dist/serve/site-ui/docs/ship/test-in-node/index.html +1 -1
- package/dist/serve/site-ui/docs/storage-compat/index.html +119 -125
- package/dist/serve/site-ui/docs/storage-compat.md +118 -124
- package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
- package/dist/serve/site-ui/docs/tutorial/index.html +1 -1
- package/dist/serve/site-ui/examples/chess/index.html +1 -1
- package/dist/serve/site-ui/examples/firestore-first-write/index.html +1 -1
- package/dist/serve/site-ui/firestore/index.html +2 -2
- package/dist/serve/site-ui/index.html +2 -2
- package/dist/serve/site-ui/llms.txt +6 -2
- package/dist/serve/site-ui/rtdb/index.html +2 -2
- package/dist/serve/site-ui/settings/index.html +2 -2
- package/dist/serve/site-ui/storage/index.html +2 -2
- package/dist/serve/site-ui/traffic/index.html +2 -2
- package/dist/serve/vite-ai-config.d.ts +4 -1
- package/dist/serve/vite-ai-config.d.ts.map +1 -1
- package/dist/serve/vite-ai-config.js +83 -19
- package/dist/serve/vite-ai-config.js.map +1 -1
- package/dist/serve/vite-generation-rules-watch.d.ts.map +1 -1
- package/dist/serve/vite-generation-rules-watch.js +61 -14
- package/dist/serve/vite-generation-rules-watch.js.map +1 -1
- package/dist/serve/vite-module-swap.d.ts +4 -1
- package/dist/serve/vite-module-swap.d.ts.map +1 -1
- package/dist/serve/vite-module-swap.js +106 -21
- package/dist/serve/vite-module-swap.js.map +1 -1
- package/dist/serve/vite-plugin.d.ts.map +1 -1
- package/dist/serve/vite-plugin.js +45 -5
- package/dist/serve/vite-plugin.js.map +1 -1
- package/dist/serve/vite-sandbox-generation.js +1 -1
- package/dist/serve/vite-sandbox-generation.js.map +1 -1
- package/dist/serve/worker/client/resumable.d.ts +30 -0
- package/dist/serve/worker/client/resumable.d.ts.map +1 -0
- package/dist/serve/worker/client/resumable.js +416 -0
- package/dist/serve/worker/client/resumable.js.map +1 -0
- package/dist/serve/worker/client/rtdb-references.d.ts.map +1 -1
- package/dist/serve/worker/client/rtdb-references.js +4 -1
- package/dist/serve/worker/client/rtdb-references.js.map +1 -1
- package/dist/serve/worker/client/storage.d.ts +16 -2
- package/dist/serve/worker/client/storage.d.ts.map +1 -1
- package/dist/serve/worker/client/storage.js +57 -6
- package/dist/serve/worker/client/storage.js.map +1 -1
- package/dist/serve/worker/host/rules.d.ts.map +1 -1
- package/dist/serve/worker/host/rules.js +2 -1
- package/dist/serve/worker/host/rules.js.map +1 -1
- package/dist/serve/worker/index.d.ts +1 -1
- package/dist/serve/worker/index.d.ts.map +1 -1
- package/dist/serve/worker/index.js +1 -1
- package/dist/serve/worker/index.js.map +1 -1
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +31 -0
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/package.json +15 -4
- package/src/assurance/.generated/conformance-verdicts.ts +80 -72
- package/src/bridge/client/dispatch.ts +1 -1
- package/src/cli/cli.test.ts +14 -0
- package/src/cli/database-rules.ts +6 -4
- package/src/cli/dev-runner.ts +21 -0
- package/src/cli/serve.ts +62 -5
- package/src/cli/verify.ts +2 -2
- package/src/conformance/.generated/can-i-use-browser.ts +1 -1
- package/src/conformance/.generated/can-i-use.ts +1 -1
- package/src/conformance/.generated/conformance-docs.ts +1 -1
- package/src/next/client-aliases.ts +39 -0
- package/src/next/dev-rewrites.ts +77 -0
- package/src/next/guard.ts +61 -0
- package/src/next/index.ts +16 -0
- package/src/next/runtime-env.ts +18 -0
- package/src/next/server-external-packages.ts +43 -0
- package/src/next/target-url.ts +46 -0
- package/src/next/turbopack-config.ts +54 -0
- package/src/next/types.ts +47 -0
- package/src/next/webpack-config.ts +71 -0
- package/src/next/with-pyric.ts +58 -0
- package/src/register/app-bridge.ts +79 -0
- package/src/register/hooks.ts +6 -1
- package/src/register/index.ts +1 -1
- package/src/register/mapping.ts +50 -4
- package/src/rtdb/rules-json.ts +110 -0
- package/src/serve/bundler.ts +2 -0
- package/src/serve/discovery.ts +65 -44
- package/src/serve/entries/app-ai-passthrough.ts +83 -0
- package/src/serve/entries/auth-helper-core.ts +44 -8
- package/src/serve/entries/auth.ts +9 -4
- package/src/serve/entries/bridge-url.ts +9 -2
- package/src/serve/entries/database.ts +1 -0
- package/src/serve/entries/firestore.ts +1 -0
- package/src/serve/entries/init.ts +5 -2
- package/src/serve/entries/runtime.ts +25 -0
- package/src/serve/entries/storage.ts +12 -1
- package/src/serve/html-injection.ts +26 -1
- package/src/serve/rules.ts +2 -2
- package/src/serve/runtime/chip-config.ts +19 -6
- package/src/serve/sandbox-session.ts +31 -2
- package/src/serve/vite-ai-config.ts +86 -19
- package/src/serve/vite-generation-rules-watch.ts +67 -15
- package/src/serve/vite-module-swap.ts +121 -21
- package/src/serve/vite-plugin.ts +44 -6
- package/src/serve/vite-sandbox-generation.ts +1 -1
- package/src/serve/worker/client/resumable.ts +506 -0
- package/src/serve/worker/client/rtdb-references.ts +4 -1
- package/src/serve/worker/client/storage.ts +77 -7
- package/src/serve/worker/host/rules.ts +2 -1
- package/src/serve/worker/index.ts +4 -0
- package/src/serve/worker/serve-init.ts +30 -0
- package/dist/serve/site-ui/_astro/_studio_.BtYpqe1A.css +0 -1
- package/dist/serve/site-ui/_astro/app.BnMC0ywl.js +0 -109
- package/dist/serve/site-ui/_astro/app.DVXXR-WI.js +0 -1
- package/dist/serve/site-ui/_astro/index.B4Kh80he.js +0 -1
- package/dist/serve/site-ui/_astro/reads.D1exkX1-.js +0 -1
- package/dist/serve/site-ui/_astro/sandbox-controls.BqMePr5V.js +0 -1153
package/src/register/mapping.ts
CHANGED
|
@@ -23,11 +23,57 @@ const MAPPINGS: ReadonlyArray<readonly [from: string, to: string]> = [
|
|
|
23
23
|
* Map a Firebase specifier to its pyric mirror, or return `null` when the
|
|
24
24
|
* specifier is not a Firebase package (leave it for the default resolver).
|
|
25
25
|
*/
|
|
26
|
-
export function mapFirebaseSpecifier(
|
|
27
|
-
|
|
26
|
+
export function mapFirebaseSpecifier(
|
|
27
|
+
specifier: string,
|
|
28
|
+
importer?: string,
|
|
29
|
+
options?: { aiMode?: 'sandbox' | 'production' },
|
|
30
|
+
): string | null {
|
|
31
|
+
const isShadowBridgeImporter = importer !== undefined &&
|
|
32
|
+
(importer.includes('app-bridge') || importer.includes('app-ai-passthrough'));
|
|
33
|
+
const isFirebaseAppSpecifier = specifier === 'firebase/app';
|
|
34
|
+
const isBypassedBridgeImport = isShadowBridgeImporter && isFirebaseAppSpecifier;
|
|
35
|
+
if (isBypassedBridgeImport) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let mode: 'sandbox' | 'production' = 'sandbox';
|
|
40
|
+
const explicitMode = options?.aiMode;
|
|
41
|
+
const hasExplicitMode = explicitMode !== undefined;
|
|
42
|
+
if (hasExplicitMode) {
|
|
43
|
+
mode = explicitMode;
|
|
44
|
+
} else {
|
|
45
|
+
const isEnvProductionMode = process.env.PYRIC_AI_MODE === 'production';
|
|
46
|
+
const isEnvPassthroughFlag = process.env.PYRIC_AI_PASSTHROUGH === '1';
|
|
47
|
+
const isProductionEnv = isEnvProductionMode || isEnvPassthroughFlag;
|
|
48
|
+
if (isProductionEnv) {
|
|
49
|
+
mode = 'production';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const isProductionMode = mode === 'production';
|
|
54
|
+
const isFirebaseAiSpecifier = specifier === 'firebase/ai';
|
|
55
|
+
const isProductionAiPassthrough = isProductionMode && isFirebaseAiSpecifier;
|
|
56
|
+
if (isProductionAiPassthrough) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isFirebaseAppSpecifier) {
|
|
61
|
+
if (isProductionMode) {
|
|
62
|
+
return '@pyric/cli/register/app-bridge';
|
|
63
|
+
}
|
|
64
|
+
return 'pyric/app/register';
|
|
65
|
+
}
|
|
66
|
+
|
|
28
67
|
for (const [from, to] of MAPPINGS) {
|
|
29
|
-
|
|
30
|
-
if (
|
|
68
|
+
const isExactMatch = specifier === from;
|
|
69
|
+
if (isExactMatch) {
|
|
70
|
+
return to;
|
|
71
|
+
}
|
|
72
|
+
const isSubpathMatch = specifier.startsWith(`${from}/`);
|
|
73
|
+
if (isSubpathMatch) {
|
|
74
|
+
const subpath = specifier.slice(from.length);
|
|
75
|
+
return to + subpath;
|
|
76
|
+
}
|
|
31
77
|
}
|
|
32
78
|
return null;
|
|
33
79
|
}
|
package/src/rtdb/rules-json.ts
CHANGED
|
@@ -27,3 +27,113 @@ export function isRtdbRulesDocument(value: unknown): value is RtdbRulesDocument
|
|
|
27
27
|
function isRtdbRulesObject(value: unknown): value is Record<string, unknown> {
|
|
28
28
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Strip JavaScript-style line comments (`//...`) and block comments (`/*...*\/`)
|
|
33
|
+
* from JSON source text without altering string literals. Firebase Realtime
|
|
34
|
+
* Database security rules files (`database.rules.json`) permit comment blocks,
|
|
35
|
+
* requiring pre-processing before evaluation with standard JSON parsers.
|
|
36
|
+
*/
|
|
37
|
+
export function stripJsonComments(text: string): string {
|
|
38
|
+
let result = '';
|
|
39
|
+
let inString = false;
|
|
40
|
+
let inSingleLineComment = false;
|
|
41
|
+
let inMultiLineComment = false;
|
|
42
|
+
let i = 0;
|
|
43
|
+
|
|
44
|
+
while (i < text.length) {
|
|
45
|
+
const char = text[i];
|
|
46
|
+
const nextChar = i + 1 < text.length ? text[i + 1] : '';
|
|
47
|
+
|
|
48
|
+
if (inSingleLineComment) {
|
|
49
|
+
const isNewline = char === '\n' || char === '\r';
|
|
50
|
+
if (isNewline) {
|
|
51
|
+
inSingleLineComment = false;
|
|
52
|
+
result += char;
|
|
53
|
+
}
|
|
54
|
+
i++;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (inMultiLineComment) {
|
|
59
|
+
const isEndOfBlock = char === '*' && nextChar === '/';
|
|
60
|
+
if (isEndOfBlock) {
|
|
61
|
+
inMultiLineComment = false;
|
|
62
|
+
i += 2;
|
|
63
|
+
} else {
|
|
64
|
+
const isNewline = char === '\n' || char === '\r';
|
|
65
|
+
if (isNewline) {
|
|
66
|
+
result += char; // Preserve line numbering for accurate diagnostics
|
|
67
|
+
}
|
|
68
|
+
i++;
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (inString) {
|
|
74
|
+
const isEscape = char === '\\';
|
|
75
|
+
if (isEscape) {
|
|
76
|
+
result += char;
|
|
77
|
+
if (nextChar !== '') {
|
|
78
|
+
result += nextChar;
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
i++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const isQuote = char === '"';
|
|
85
|
+
if (isQuote) {
|
|
86
|
+
inString = false;
|
|
87
|
+
}
|
|
88
|
+
result += char;
|
|
89
|
+
i++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const isQuote = char === '"';
|
|
94
|
+
if (isQuote) {
|
|
95
|
+
inString = true;
|
|
96
|
+
result += char;
|
|
97
|
+
i++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const isLineCommentStart = char === '/' && nextChar === '/';
|
|
102
|
+
if (isLineCommentStart) {
|
|
103
|
+
inSingleLineComment = true;
|
|
104
|
+
i += 2;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const isBlockCommentStart = char === '/' && nextChar === '*';
|
|
109
|
+
if (isBlockCommentStart) {
|
|
110
|
+
inMultiLineComment = true;
|
|
111
|
+
i += 2;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
result += char;
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Parse a JSON string that may contain line or block comments into a validated
|
|
124
|
+
* Realtime Database rules document structure.
|
|
125
|
+
*/
|
|
126
|
+
export function parseRtdbRulesText(
|
|
127
|
+
text: string,
|
|
128
|
+
onInvalid: (error: Error) => Error,
|
|
129
|
+
): RtdbRulesJson {
|
|
130
|
+
const clean = stripJsonComments(text);
|
|
131
|
+
let parsed: unknown;
|
|
132
|
+
try {
|
|
133
|
+
parsed = JSON.parse(clean);
|
|
134
|
+
} catch (e) {
|
|
135
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
136
|
+
throw onInvalid(new Error(`Failed to parse rules JSON: ${detail}`));
|
|
137
|
+
}
|
|
138
|
+
return parseRtdbRulesJson(parsed, () => onInvalid(new Error('Document must contain a top-level "rules" object.')));
|
|
139
|
+
}
|
package/src/serve/bundler.ts
CHANGED
|
@@ -57,6 +57,7 @@ export function defaultSdkEntries(): Record<string, string> {
|
|
|
57
57
|
return {
|
|
58
58
|
ai: pick('ai'),
|
|
59
59
|
app: pick('app'),
|
|
60
|
+
'app-ai-passthrough': pick('app-ai-passthrough'),
|
|
60
61
|
auth: pick('auth'),
|
|
61
62
|
firestore: pick('firestore'),
|
|
62
63
|
database: pick('database'),
|
|
@@ -309,6 +310,7 @@ export async function bundleSdk(opts: BundleOptions): Promise<BundleResult> {
|
|
|
309
310
|
sourcemap: 'linked',
|
|
310
311
|
minify: opts.minify ?? true,
|
|
311
312
|
logLevel: 'silent',
|
|
313
|
+
external: ['firebase/*'],
|
|
312
314
|
// Provenance: any stack frame or "view source" into these bundles must
|
|
313
315
|
// self-identify as the sandbox shim, not the real Firebase SDK.
|
|
314
316
|
chunkNames: 'pyric-sandbox-[hash]',
|
package/src/serve/discovery.ts
CHANGED
|
@@ -17,16 +17,36 @@
|
|
|
17
17
|
* first while the browser is on the other — split-brain.
|
|
18
18
|
*/
|
|
19
19
|
import { existsSync, readFileSync } from 'node:fs';
|
|
20
|
-
import { join } from 'node:path';
|
|
20
|
+
import { dirname, join, resolve } from 'node:path';
|
|
21
21
|
|
|
22
22
|
/** Ports probed when the pointer is absent — serve's default scan window PLUS
|
|
23
|
-
* the
|
|
24
|
-
* vite-only project
|
|
25
|
-
* serves `/health` (not `/__pyric/health`) and
|
|
26
|
-
* registered directly via `claude mcp add`, not discovered here. */
|
|
27
|
-
export const SCAN_PORTS = [3473, 3474, 3475, 3476, 3477, 5174];
|
|
23
|
+
* the standard Vite dev ports (5173-5177), which the plain 3473+ window would miss
|
|
24
|
+
* (so a vite-only or monorepo split-directory project is reliably found by scan).
|
|
25
|
+
* The standalone `pyric bridge` serves `/health` (not `/__pyric/health`) and
|
|
26
|
+
* writes no pointer, so it is registered directly via `claude mcp add`, not discovered here. */
|
|
27
|
+
export const SCAN_PORTS = [3473, 3474, 3475, 3476, 3477, 5173, 5174, 5175, 5176, 5177];
|
|
28
28
|
export const POINTER = join('.pyric', 'serve.json');
|
|
29
29
|
|
|
30
|
+
function candidatePointerPaths(startDir: string): string[] {
|
|
31
|
+
const paths: string[] = [];
|
|
32
|
+
const seen = new Set<string>();
|
|
33
|
+
const subdirs = ['', 'web', 'frontend', 'client', 'app', 'ui', 'www'];
|
|
34
|
+
let current = resolve(startDir);
|
|
35
|
+
while (true) {
|
|
36
|
+
for (const sub of subdirs) {
|
|
37
|
+
const p = sub ? join(current, sub, POINTER) : join(current, POINTER);
|
|
38
|
+
if (!seen.has(p)) {
|
|
39
|
+
seen.add(p);
|
|
40
|
+
paths.push(p);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const parent = dirname(current);
|
|
44
|
+
if (parent === current) break;
|
|
45
|
+
current = parent;
|
|
46
|
+
}
|
|
47
|
+
return paths;
|
|
48
|
+
}
|
|
49
|
+
|
|
30
50
|
export interface HealthLite {
|
|
31
51
|
mode?: string;
|
|
32
52
|
instanceId?: string;
|
|
@@ -141,46 +161,47 @@ export async function discoverServe(
|
|
|
141
161
|
// real localhost ports, which a test environment can't guarantee are free.
|
|
142
162
|
scanPorts: number[] = SCAN_PORTS,
|
|
143
163
|
): Promise<Discovered | null> {
|
|
144
|
-
const pointerPath
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
164
|
+
for (const pointerPath of candidatePointerPaths(cwd)) {
|
|
165
|
+
if (existsSync(pointerPath)) {
|
|
166
|
+
try {
|
|
167
|
+
const p = JSON.parse(readFileSync(pointerPath, 'utf8')) as {
|
|
168
|
+
url?: string;
|
|
169
|
+
mcpUrl?: string;
|
|
170
|
+
port?: number;
|
|
171
|
+
instanceId?: string;
|
|
172
|
+
};
|
|
173
|
+
const port = p.port ?? portOf(p.mcpUrl) ?? portOf(p.url);
|
|
174
|
+
if (port) {
|
|
175
|
+
const expectedId = typeof p.instanceId === 'string' && p.instanceId ? p.instanceId : null;
|
|
176
|
+
const hit = await healthyBase(port, expectedId);
|
|
177
|
+
if (hit) {
|
|
178
|
+
return {
|
|
179
|
+
mcpUrl: `${hit.base}/__pyric/mcp`,
|
|
180
|
+
url: canonicalServeUrl(port, p.url),
|
|
181
|
+
base: hit.base,
|
|
182
|
+
instanceId: hit.instanceId,
|
|
183
|
+
source: `pointer ${pointerPath}`,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
// The pointer named a specific identity we could NOT find on its port:
|
|
187
|
+
// a different sandbox may be squatting it (cross-family collision) or
|
|
188
|
+
// the server stopped. Do NOT scan into a possibly-wrong server — that
|
|
189
|
+
// split-brain is exactly what this identity check prevents. Fail legibly.
|
|
190
|
+
if (expectedId) {
|
|
191
|
+
log(
|
|
192
|
+
`pointer ${pointerPath} names a server (instanceId ${expectedId.slice(0, 8)}…) ` +
|
|
193
|
+
`that isn't answering on port ${port} — another sandbox may be squatting the ` +
|
|
194
|
+
`port on the other loopback family, or the server stopped. Not falling back to ` +
|
|
195
|
+
`a blind port scan (it could hit the wrong sandbox). Restart your dev server, ` +
|
|
196
|
+
`and open the exact URL it prints (http://localhost:<port> by default) — every ` +
|
|
197
|
+
`page must share that ONE origin, or the browser splits into separate sandboxes.`,
|
|
198
|
+
);
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
180
201
|
}
|
|
202
|
+
} catch {
|
|
203
|
+
/* stale/corrupt pointer — fall through to next candidate */
|
|
181
204
|
}
|
|
182
|
-
} catch {
|
|
183
|
-
/* stale/corrupt pointer — fall through to scan */
|
|
184
205
|
}
|
|
185
206
|
}
|
|
186
207
|
for (const port of scanPorts) {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Served `firebase/app`: Pyric client registry with shadow container bridge for production AI pass-through. */
|
|
2
|
+
import { bindAppRegistrySandbox } from 'pyric/app/internal';
|
|
3
|
+
import { sandbox } from './app-backend.js';
|
|
4
|
+
import * as pyricApp from 'pyric/app';
|
|
5
|
+
import * as realApp from 'firebase/app';
|
|
6
|
+
|
|
7
|
+
bindAppRegistrySandbox(sandbox);
|
|
8
|
+
|
|
9
|
+
function decorateApp(
|
|
10
|
+
app: pyricApp.FirebaseApp,
|
|
11
|
+
options?: pyricApp.FirebaseOptions,
|
|
12
|
+
rawSettings?: string | pyricApp.FirebaseAppSettings,
|
|
13
|
+
): pyricApp.FirebaseApp {
|
|
14
|
+
try {
|
|
15
|
+
let shadow: { container?: unknown };
|
|
16
|
+
try {
|
|
17
|
+
shadow = realApp.getApp(app.name) as { container?: unknown };
|
|
18
|
+
} catch {
|
|
19
|
+
const initOptions = (options ?? app.options) as realApp.FirebaseOptions;
|
|
20
|
+
const initSettings = rawSettings ?? app.name;
|
|
21
|
+
const isStringSettings = typeof initSettings === 'string';
|
|
22
|
+
if (isStringSettings) {
|
|
23
|
+
shadow = realApp.initializeApp(initOptions, initSettings) as { container?: unknown };
|
|
24
|
+
} else {
|
|
25
|
+
const objectSettings = initSettings as realApp.FirebaseAppSettings;
|
|
26
|
+
shadow = realApp.initializeApp(initOptions, objectSettings) as { container?: unknown };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const hasShadowContainer = shadow.container !== undefined && shadow.container !== null;
|
|
30
|
+
if (hasShadowContainer) {
|
|
31
|
+
Object.defineProperty(app, 'container', { value: shadow.container, configurable: true });
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// Observational/best-effort: do not break Pyric initialization if shadow initialization fails.
|
|
35
|
+
}
|
|
36
|
+
return app;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function initializeApp(
|
|
40
|
+
options?: pyricApp.FirebaseOptions,
|
|
41
|
+
rawSettings?: string | pyricApp.FirebaseAppSettings,
|
|
42
|
+
): pyricApp.FirebaseApp {
|
|
43
|
+
const app = pyricApp.initializeApp(options, rawSettings);
|
|
44
|
+
return decorateApp(app, options, rawSettings);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getApp(name?: string): pyricApp.FirebaseApp {
|
|
48
|
+
return decorateApp(pyricApp.getApp(name));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getApps(): pyricApp.FirebaseApp[] {
|
|
52
|
+
return pyricApp.getApps().map((app) => decorateApp(app));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function deleteApp(app: pyricApp.FirebaseApp): Promise<void> {
|
|
56
|
+
try {
|
|
57
|
+
const shadow = realApp.getApp(app.name);
|
|
58
|
+
const hasShadow = shadow !== undefined && shadow !== null;
|
|
59
|
+
if (hasShadow) {
|
|
60
|
+
await realApp.deleteApp(shadow);
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore if shadow app was not initialized or already deleted.
|
|
64
|
+
}
|
|
65
|
+
return pyricApp.deleteApp(app);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
FirebaseError,
|
|
70
|
+
SDK_VERSION,
|
|
71
|
+
onLog,
|
|
72
|
+
registerVersion,
|
|
73
|
+
setLogLevel,
|
|
74
|
+
} from 'pyric/app';
|
|
75
|
+
export type {
|
|
76
|
+
FirebaseApp,
|
|
77
|
+
FirebaseAppSettings,
|
|
78
|
+
FirebaseOptions,
|
|
79
|
+
LogCallback,
|
|
80
|
+
LogEntry,
|
|
81
|
+
LogLevel,
|
|
82
|
+
LogOptions,
|
|
83
|
+
} from 'pyric/app';
|
|
@@ -175,8 +175,16 @@ export class ServeAuthHelper {
|
|
|
175
175
|
if (request.kind === 'pick') {
|
|
176
176
|
return bareCredential(request.identity, request.providerId);
|
|
177
177
|
}
|
|
178
|
+
// Same-email add resolves to the existing identity (Google-style account
|
|
179
|
+
// reuse) — mirrors `createSignInCredential`'s in-page policy, and keeps a
|
|
180
|
+
// repeated add idempotent instead of seeding a second account whose email
|
|
181
|
+
// mapping orphans the first.
|
|
182
|
+
const existing = this.findIdentityByEmail(request.spec.email);
|
|
183
|
+
if (existing) {
|
|
184
|
+
return bareCredential(existing, request.providerId);
|
|
185
|
+
}
|
|
178
186
|
const identity: HelperIdentity = {
|
|
179
|
-
uid:
|
|
187
|
+
uid: mintProviderUid(),
|
|
180
188
|
email: request.spec.email,
|
|
181
189
|
displayName: request.spec.displayName ?? null,
|
|
182
190
|
customClaims: request.spec.customClaims ?? {},
|
|
@@ -184,12 +192,44 @@ export class ServeAuthHelper {
|
|
|
184
192
|
await Promise.resolve(this.directory.add?.(identity));
|
|
185
193
|
return bareCredential(identity, request.providerId);
|
|
186
194
|
}
|
|
195
|
+
|
|
196
|
+
private findIdentityByEmail(email: string): HelperIdentity | undefined {
|
|
197
|
+
const normalized = email.toLowerCase();
|
|
198
|
+
return this.identities.find(
|
|
199
|
+
(identity) => identity.email?.toLowerCase() === normalized,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Recover the original custom claims from a helper credential's token claims.
|
|
206
|
+
* `bareCredential` (and the in-page backend mint) seed `sub` and the
|
|
207
|
+
* synthesized `firebase` envelope alongside the custom claims; neither is a
|
|
208
|
+
* custom claim, and seeding them back into the worker would persist
|
|
209
|
+
* synthesized metadata as user data.
|
|
210
|
+
*/
|
|
211
|
+
export function customClaimsFromTokenClaims(
|
|
212
|
+
claims: Record<string, unknown>,
|
|
213
|
+
): Record<string, unknown> {
|
|
214
|
+
const { sub: _sub, firebase: _firebase, ...customClaims } = claims;
|
|
215
|
+
return customClaims;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Provider UIDs are opaque Firebase identifiers, not encoded credentials. */
|
|
219
|
+
function mintProviderUid(): string {
|
|
220
|
+
return `provider-${globalThis.crypto.randomUUID()}`;
|
|
187
221
|
}
|
|
188
222
|
|
|
189
223
|
/** Bare helper User — worker path discards this in favor of `acceptIdentity`. */
|
|
190
224
|
function bareCredential(identity: HelperIdentity, providerId: string): UserCredential {
|
|
191
225
|
const issuedAtTime = new Date().toISOString();
|
|
192
226
|
const expirationTime = new Date(Date.now() + 3600_000).toISOString();
|
|
227
|
+
const claims: Record<string, unknown> = {
|
|
228
|
+
sub: identity.uid,
|
|
229
|
+
...identity.customClaims,
|
|
230
|
+
firebase: { sign_in_provider: providerId },
|
|
231
|
+
};
|
|
232
|
+
const token = `sandbox-id-token-${identity.uid}-0:${JSON.stringify(claims)}`;
|
|
193
233
|
const user: User = {
|
|
194
234
|
uid: identity.uid,
|
|
195
235
|
email: identity.email,
|
|
@@ -209,14 +249,10 @@ function bareCredential(identity: HelperIdentity, providerId: string): UserCrede
|
|
|
209
249
|
providerId,
|
|
210
250
|
},
|
|
211
251
|
],
|
|
212
|
-
getIdToken: async () =>
|
|
252
|
+
getIdToken: async () => token,
|
|
213
253
|
getIdTokenResult: async () => ({
|
|
214
|
-
token
|
|
215
|
-
claims
|
|
216
|
-
sub: identity.uid,
|
|
217
|
-
...identity.customClaims,
|
|
218
|
-
firebase: { sign_in_provider: providerId },
|
|
219
|
-
},
|
|
254
|
+
token,
|
|
255
|
+
claims,
|
|
220
256
|
signInProvider: providerId,
|
|
221
257
|
expirationTime,
|
|
222
258
|
issuedAtTime,
|
|
@@ -13,12 +13,14 @@
|
|
|
13
13
|
* in or seeds a user. Email/password + anonymous also go straight to the
|
|
14
14
|
* worker. The COMPLETE surface is exported on both paths (import-time parity).
|
|
15
15
|
*/
|
|
16
|
+
import './init.js';
|
|
16
17
|
import * as ipAuth from 'pyric/auth';
|
|
17
18
|
import { getAuth as pyricGetAuth, setPersistence as pyricSetPersistence } from 'pyric/auth';
|
|
18
19
|
import * as wcRaw from '../worker/client.js';
|
|
19
20
|
import { acceptProviderCredential, restorePortSession } from '../worker/client.js';
|
|
20
21
|
import { useWorker } from './worker-runtime.js';
|
|
21
22
|
import { sessionStoreForApp } from './app-session-store.js';
|
|
23
|
+
import { customClaimsFromTokenClaims } from './auth-helper-core.js';
|
|
22
24
|
import { resolveServeAuthFlow } from './auth-helper-runtime.js';
|
|
23
25
|
import type { SessionMode } from './session-store.js';
|
|
24
26
|
import { getApp, type FirebaseApp } from 'pyric/app';
|
|
@@ -153,9 +155,10 @@ export const setPersistence = (
|
|
|
153
155
|
|
|
154
156
|
/**
|
|
155
157
|
* Resolve a provider identity through the backend-free ServeAuthHelper, then
|
|
156
|
-
* hand it to the worker. The
|
|
157
|
-
*
|
|
158
|
-
*
|
|
158
|
+
* hand it to the worker. The helper seeds `{ sub, ...claims, firebase }` into
|
|
159
|
+
* the resolved credential's token, so we strip the synthesized `sub` and
|
|
160
|
+
* `firebase` entries to recover the original custom claims for the worker
|
|
161
|
+
* seed (see `customClaimsFromTokenClaims`).
|
|
159
162
|
*
|
|
160
163
|
* Enforcement lives at the hand-off: `auth.acceptIdentity` gates against the
|
|
161
164
|
* WORKER's provider config and rejects `auth/operation-not-allowed` for a
|
|
@@ -173,7 +176,9 @@ async function bridgeProviderSignIn(
|
|
|
173
176
|
kind,
|
|
174
177
|
);
|
|
175
178
|
const tokenResult = await cred.user.getIdTokenResult();
|
|
176
|
-
const
|
|
179
|
+
const customClaims = customClaimsFromTokenClaims(
|
|
180
|
+
(tokenResult.claims ?? {}) as Record<string, unknown>,
|
|
181
|
+
);
|
|
177
182
|
return acceptProviderCredential(auth as never, {
|
|
178
183
|
uid: cred.user.uid,
|
|
179
184
|
email: cred.user.email,
|
|
@@ -22,9 +22,16 @@ export function toPageOriginWsUrl(
|
|
|
22
22
|
loc: { href: string; protocol: string; host: string },
|
|
23
23
|
): string {
|
|
24
24
|
try {
|
|
25
|
-
const
|
|
25
|
+
const rawUrl = new URL(raw, loc.href);
|
|
26
|
+
const locUrl = new URL(loc.href);
|
|
26
27
|
const scheme = loc.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
const hasExplicitLocPort = locUrl.port.length > 0;
|
|
30
|
+
const hasExplicitRawPort = rawUrl.port.length > 0;
|
|
31
|
+
const shouldPreserveRawPort = hasExplicitLocPort && hasExplicitRawPort;
|
|
32
|
+
const hostTarget = shouldPreserveRawPort ? `${locUrl.hostname}:${rawUrl.port}` : locUrl.host;
|
|
33
|
+
|
|
34
|
+
return `${scheme}//${hostTarget}${rawUrl.pathname}`;
|
|
28
35
|
} catch {
|
|
29
36
|
return raw;
|
|
30
37
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* and the Playground. This entry gives served apps the same common modular SDK
|
|
6
6
|
* surface while the in-page fallback delegates to `pyric/database`.
|
|
7
7
|
*/
|
|
8
|
+
import './init.js';
|
|
8
9
|
import * as ip from 'pyric/database';
|
|
9
10
|
import { getDatabase as pyricGetDatabase } from 'pyric/database';
|
|
10
11
|
import { queryIdentifier } from 'pyric/database/internal';
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Browser-bundled by `../bundler.ts`; never imported by node-side code.
|
|
15
15
|
*/
|
|
16
|
+
import './init.js';
|
|
16
17
|
import * as ip from 'pyric/firestore';
|
|
17
18
|
import { getFirestore as pyricGetFirestore } from 'pyric/firestore';
|
|
18
19
|
import * as wcRaw from '../worker/client.js';
|
|
@@ -53,7 +53,10 @@ const helper = workerAuth
|
|
|
53
53
|
const resolver = helper.resolver();
|
|
54
54
|
installServeAuthResolver(resolver);
|
|
55
55
|
if (localAuth) authSandbox.setAuthFlowResolver(localAuth, resolver);
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
|
|
57
|
+
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
|
|
58
|
+
mountAuthHelperDialog(helper);
|
|
59
|
+
installPyricRuntimeChip({ runtime: getPyricRuntimeStatus(), document });
|
|
60
|
+
}
|
|
58
61
|
|
|
59
62
|
export {};
|
|
@@ -553,4 +553,29 @@ if (!useWorker && typeof EventSource !== 'undefined') {
|
|
|
553
553
|
console.error('[pyric dev] rules hot-reload failed:', err instanceof Error ? err.message : String(err));
|
|
554
554
|
}
|
|
555
555
|
});
|
|
556
|
+
events.addEventListener('rtdb-rules-update', (e) => {
|
|
557
|
+
try {
|
|
558
|
+
const { rules, rulesHash } = JSON.parse((e as MessageEvent).data as string) as {
|
|
559
|
+
rules: { rules: Record<string, unknown> };
|
|
560
|
+
rulesHash: string;
|
|
561
|
+
};
|
|
562
|
+
rtdbSandbox.setRules(getDatabase(sandbox), rules);
|
|
563
|
+
diagnostics.databaseRulesDeployed = true;
|
|
564
|
+
let newHash: string | null = null;
|
|
565
|
+
const isHashDefined = rulesHash !== undefined;
|
|
566
|
+
if (isHashDefined) {
|
|
567
|
+
newHash = rulesHash;
|
|
568
|
+
}
|
|
569
|
+
diagnostics.databaseRulesHash = newHash;
|
|
570
|
+
console.info(`[pyric dev] database.rules.json hot-reloaded (hash ${rulesHash})`);
|
|
571
|
+
} catch (err) {
|
|
572
|
+
runtimeStatus.reportError(err, 'runtime');
|
|
573
|
+
const isErrorInstance = err instanceof Error;
|
|
574
|
+
let errorMessage = String(err);
|
|
575
|
+
if (isErrorInstance) {
|
|
576
|
+
errorMessage = (err as Error).message;
|
|
577
|
+
}
|
|
578
|
+
console.error('[pyric dev] database rules hot-reload failed:', errorMessage);
|
|
579
|
+
}
|
|
580
|
+
});
|
|
556
581
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* still lack a worker protocol fail loudly. In in-page fallback mode, the entry
|
|
7
7
|
* delegates to `pyric/storage` against the page sandbox.
|
|
8
8
|
*/
|
|
9
|
+
import './init.js';
|
|
9
10
|
import * as ip from 'pyric/storage';
|
|
10
11
|
import {
|
|
11
12
|
getStorage as pyricGetStorage,
|
|
@@ -20,6 +21,8 @@ import {
|
|
|
20
21
|
listAll as workerListAll,
|
|
21
22
|
ref as workerRef,
|
|
22
23
|
uploadBytes as workerUploadBytes,
|
|
24
|
+
uploadString as workerUploadString,
|
|
25
|
+
uploadBytesResumable as workerUploadBytesResumable,
|
|
23
26
|
deleteObject as workerDeleteObject,
|
|
24
27
|
} from '../worker/client.js';
|
|
25
28
|
import { useWorker } from './worker-runtime.js';
|
|
@@ -85,8 +88,16 @@ function workerOrInPage<T extends (...args: any[]) => unknown>(name: string, fn:
|
|
|
85
88
|
// ruleset; only their active Auth sessions differ. Payloads are capped at
|
|
86
89
|
// 8 MiB per op.
|
|
87
90
|
export const uploadBytes = (useWorker ? workerUploadBytes : ip.uploadBytes) as typeof ip.uploadBytes;
|
|
91
|
+
|
|
92
|
+
let selectedUploadBytesResumable = ip.uploadBytesResumable;
|
|
93
|
+
const isWorkerMode = useWorker === true;
|
|
94
|
+
if (isWorkerMode) {
|
|
95
|
+
selectedUploadBytesResumable = workerUploadBytesResumable as unknown as typeof ip.uploadBytesResumable;
|
|
96
|
+
}
|
|
97
|
+
export const uploadBytesResumable = selectedUploadBytesResumable;
|
|
98
|
+
|
|
88
99
|
export const getBytes = (useWorker ? workerGetBytes : ip.getBytes) as typeof ip.getBytes;
|
|
89
100
|
export const deleteObject = (useWorker ? workerDeleteObject : ip.deleteObject) as typeof ip.deleteObject;
|
|
90
101
|
|
|
91
|
-
export const uploadString =
|
|
102
|
+
export const uploadString = (useWorker ? workerUploadString : ip.uploadString) as typeof ip.uploadString;
|
|
92
103
|
export const updateMetadata = workerOrInPage('updateMetadata', ip.updateMetadata);
|