@pyric/cli 0.1.0-alpha.19 → 0.1.0-alpha.21
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 +64 -89
- package/dist/assurance/.generated/conformance-verdicts.d.ts +68 -1
- package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
- package/dist/assurance/.generated/conformance-verdicts.js +68 -1
- package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
- package/dist/bridge/client/dispatch.d.ts +25 -19
- package/dist/bridge/client/dispatch.d.ts.map +1 -1
- package/dist/bridge/client/dispatch.js +41 -48
- package/dist/bridge/client/dispatch.js.map +1 -1
- package/dist/bridge/client/tool-family-factories.d.ts +26 -0
- package/dist/bridge/client/tool-family-factories.d.ts.map +1 -0
- package/dist/bridge/client/tool-family-factories.js +10 -0
- package/dist/bridge/client/tool-family-factories.js.map +1 -0
- package/dist/bridge/server/mcp-contract.d.ts +7 -6
- package/dist/bridge/server/mcp-contract.d.ts.map +1 -1
- package/dist/bridge/server/mcp-contract.js +9 -48
- package/dist/bridge/server/mcp-contract.js.map +1 -1
- package/dist/bridge/server/peer.d.ts +11 -1
- package/dist/bridge/server/peer.d.ts.map +1 -1
- package/dist/bridge/server/peer.js +22 -1
- package/dist/bridge/server/peer.js.map +1 -1
- package/dist/bridge/server/tool-family-factories.d.ts +30 -0
- package/dist/bridge/server/tool-family-factories.d.ts.map +1 -0
- package/dist/bridge/server/tool-family-factories.js +22 -0
- package/dist/bridge/server/tool-family-factories.js.map +1 -0
- package/dist/bridge/server/tool-metadata.d.ts +17 -18
- package/dist/bridge/server/tool-metadata.d.ts.map +1 -1
- package/dist/bridge/server/tool-metadata.js +24 -59
- package/dist/bridge/server/tool-metadata.js.map +1 -1
- package/dist/bridge/tool-families.d.ts +48 -0
- package/dist/bridge/tool-families.d.ts.map +1 -0
- package/dist/bridge/tool-families.generated.d.ts +32 -0
- package/dist/bridge/tool-families.generated.d.ts.map +1 -0
- package/dist/bridge/tool-families.generated.js +15 -0
- package/dist/bridge/tool-families.generated.js.map +1 -0
- package/dist/bridge/tool-families.js +51 -0
- package/dist/bridge/tool-families.js.map +1 -0
- package/dist/bridge/tool-family-records/conformance.d.ts +7 -0
- package/dist/bridge/tool-family-records/conformance.d.ts.map +1 -0
- package/dist/bridge/tool-family-records/conformance.js +6 -0
- package/dist/bridge/tool-family-records/conformance.js.map +1 -0
- package/dist/bridge/tool-family-records/firestore-data.d.ts +7 -0
- package/dist/bridge/tool-family-records/firestore-data.d.ts.map +1 -0
- package/dist/bridge/tool-family-records/firestore-data.js +15 -0
- package/dist/bridge/tool-family-records/firestore-data.js.map +1 -0
- package/dist/bridge/tool-family-records/firestore-inspect.d.ts +7 -0
- package/dist/bridge/tool-family-records/firestore-inspect.d.ts.map +1 -0
- package/dist/bridge/tool-family-records/firestore-inspect.js +6 -0
- package/dist/bridge/tool-family-records/firestore-inspect.js.map +1 -0
- package/dist/bridge/tool-family-records/firestore-rules.d.ts +7 -0
- package/dist/bridge/tool-family-records/firestore-rules.d.ts.map +1 -0
- package/dist/bridge/tool-family-records/firestore-rules.js +15 -0
- package/dist/bridge/tool-family-records/firestore-rules.js.map +1 -0
- package/dist/bridge/tool-family-records/firestore-simulator.d.ts +7 -0
- package/dist/bridge/tool-family-records/firestore-simulator.d.ts.map +1 -0
- package/dist/bridge/tool-family-records/firestore-simulator.js +16 -0
- package/dist/bridge/tool-family-records/firestore-simulator.js.map +1 -0
- package/dist/bridge/tool-family-records/rtdb-inspection.d.ts +7 -0
- package/dist/bridge/tool-family-records/rtdb-inspection.d.ts.map +1 -0
- package/dist/bridge/tool-family-records/rtdb-inspection.js +6 -0
- package/dist/bridge/tool-family-records/rtdb-inspection.js.map +1 -0
- package/dist/bundler/externals.d.ts +37 -0
- package/dist/bundler/externals.d.ts.map +1 -0
- package/dist/bundler/externals.js +52 -0
- package/dist/bundler/externals.js.map +1 -0
- package/dist/bundler/index.d.ts +17 -0
- package/dist/bundler/index.d.ts.map +1 -0
- package/dist/bundler/index.js +17 -0
- package/dist/bundler/index.js.map +1 -0
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +48 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/inlined-sdk-scanner.d.ts +6 -0
- package/dist/cli/inlined-sdk-scanner.d.ts.map +1 -0
- package/dist/cli/inlined-sdk-scanner.js +82 -0
- package/dist/cli/inlined-sdk-scanner.js.map +1 -0
- package/dist/cli/mcp-proxy.js +3 -3
- package/dist/cli/mcp-proxy.js.map +1 -1
- package/dist/cli/parse-args.d.ts +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +51 -15
- package/dist/cli/parse-args.js.map +1 -1
- package/dist/cli/pyric-config.d.ts +24 -0
- package/dist/cli/pyric-config.d.ts.map +1 -0
- package/dist/cli/pyric-config.js +28 -0
- package/dist/cli/pyric-config.js.map +1 -0
- package/dist/cli/sandbox-interlock.d.ts +97 -0
- package/dist/cli/sandbox-interlock.d.ts.map +1 -0
- package/dist/cli/sandbox-interlock.js +112 -0
- package/dist/cli/sandbox-interlock.js.map +1 -0
- package/dist/cli/{dev-runner.d.ts → sandbox-runner.d.ts} +53 -48
- package/dist/cli/sandbox-runner.d.ts.map +1 -0
- package/dist/cli/{dev-runner.js → sandbox-runner.js} +105 -67
- package/dist/cli/sandbox-runner.js.map +1 -0
- package/dist/cli/serve.d.ts +12 -14
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +155 -116
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/snapshot.js +5 -5
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/unsupported-runtime.d.ts +27 -0
- package/dist/cli/unsupported-runtime.d.ts.map +1 -0
- package/dist/cli/unsupported-runtime.js +63 -0
- package/dist/cli/unsupported-runtime.js.map +1 -0
- package/dist/cli/verify.js +1 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.d.ts +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 +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/functions-rtdb/child.d.ts +37 -0
- package/dist/functions-rtdb/child.d.ts.map +1 -1
- package/dist/functions-rtdb/child.js +90 -21
- package/dist/functions-rtdb/child.js.map +1 -1
- package/dist/functions-rtdb/development-runtime.d.ts +3 -0
- package/dist/functions-rtdb/development-runtime.d.ts.map +1 -1
- package/dist/functions-rtdb/development-runtime.js +2 -1
- package/dist/functions-rtdb/development-runtime.js.map +1 -1
- package/dist/functions-rtdb/project.js +8 -8
- package/dist/functions-rtdb/project.js.map +1 -1
- package/dist/google-endpoints.d.ts +67 -0
- package/dist/google-endpoints.d.ts.map +1 -0
- package/dist/google-endpoints.js +119 -0
- package/dist/google-endpoints.js.map +1 -0
- package/dist/next/guard.js +1 -1
- package/dist/next/guard.js.map +1 -1
- package/dist/next/runtime-env.d.ts.map +1 -1
- package/dist/next/runtime-env.js +5 -0
- package/dist/next/runtime-env.js.map +1 -1
- package/dist/next/with-pyric.d.ts +1 -1
- package/dist/next/with-pyric.js +1 -1
- package/dist/register/beacon.d.ts +69 -0
- package/dist/register/beacon.d.ts.map +1 -0
- package/dist/register/beacon.js +199 -0
- package/dist/register/beacon.js.map +1 -0
- package/dist/register/index.d.ts.map +1 -1
- package/dist/register/index.js +65 -6
- package/dist/register/index.js.map +1 -1
- package/dist/register/net-guard.d.ts +110 -0
- package/dist/register/net-guard.d.ts.map +1 -0
- package/dist/register/net-guard.js +462 -0
- package/dist/register/net-guard.js.map +1 -0
- package/dist/remote/index.d.ts +1 -1
- package/dist/remote/index.js +9 -9
- package/dist/remote/index.js.map +1 -1
- package/dist/serve/ai-diagnostics-relay.d.ts +63 -0
- package/dist/serve/ai-diagnostics-relay.d.ts.map +1 -0
- package/dist/serve/ai-diagnostics-relay.js +125 -0
- package/dist/serve/ai-diagnostics-relay.js.map +1 -0
- package/dist/serve/ai-proxy.d.ts +92 -0
- package/dist/serve/ai-proxy.d.ts.map +1 -0
- package/dist/serve/ai-proxy.js +269 -0
- package/dist/serve/ai-proxy.js.map +1 -0
- package/dist/serve/ai-status.d.ts +44 -0
- package/dist/serve/ai-status.d.ts.map +1 -0
- package/dist/serve/ai-status.js +77 -0
- package/dist/serve/ai-status.js.map +1 -0
- package/dist/serve/ai-terminal-blocks.d.ts +97 -0
- package/dist/serve/ai-terminal-blocks.d.ts.map +1 -0
- package/dist/serve/ai-terminal-blocks.js +128 -0
- package/dist/serve/ai-terminal-blocks.js.map +1 -0
- package/dist/serve/ai-terminal-text.d.ts +36 -0
- package/dist/serve/ai-terminal-text.d.ts.map +1 -0
- package/dist/serve/ai-terminal-text.js +48 -0
- package/dist/serve/ai-terminal-text.js.map +1 -0
- package/dist/serve/beacon-route.d.ts +53 -0
- package/dist/serve/beacon-route.d.ts.map +1 -0
- package/dist/serve/beacon-route.js +101 -0
- package/dist/serve/beacon-route.js.map +1 -0
- package/dist/serve/bundler.js +10 -10
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/entries/active-auth-registry.d.ts +16 -0
- package/dist/serve/entries/active-auth-registry.d.ts.map +1 -0
- package/dist/serve/entries/active-auth-registry.js +48 -0
- package/dist/serve/entries/active-auth-registry.js.map +1 -0
- package/dist/serve/entries/active-auth.d.ts +18 -0
- package/dist/serve/entries/active-auth.d.ts.map +1 -0
- package/dist/serve/entries/active-auth.js +84 -0
- package/dist/serve/entries/active-auth.js.map +1 -0
- package/dist/serve/entries/ai.d.ts.map +1 -1
- package/dist/serve/entries/ai.js +27 -1
- package/dist/serve/entries/ai.js.map +1 -1
- package/dist/serve/entries/auth-helper-core.d.ts +1 -0
- package/dist/serve/entries/auth-helper-core.d.ts.map +1 -1
- package/dist/serve/entries/auth-helper-core.js +4 -0
- package/dist/serve/entries/auth-helper-core.js.map +1 -1
- package/dist/serve/entries/auth-helper-dom.d.ts.map +1 -1
- package/dist/serve/entries/auth-helper-dom.js +29 -3
- package/dist/serve/entries/auth-helper-dom.js.map +1 -1
- package/dist/serve/entries/auth-helper-runtime.js +1 -1
- package/dist/serve/entries/auth-helper-runtime.js.map +1 -1
- package/dist/serve/entries/auth.d.ts.map +1 -1
- package/dist/serve/entries/auth.js +8 -0
- package/dist/serve/entries/auth.js.map +1 -1
- package/dist/serve/entries/bridge-url.d.ts +1 -1
- package/dist/serve/entries/bridge-url.js +1 -1
- package/dist/serve/entries/firestore.js +3 -3
- 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 +70 -4
- package/dist/serve/entries/init.js.map +1 -1
- package/dist/serve/entries/keepalive.js +1 -1
- package/dist/serve/entries/keepalive.js.map +1 -1
- package/dist/serve/entries/runtime.d.ts.map +1 -1
- package/dist/serve/entries/runtime.js +30 -14
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/entries/storage.js +1 -1
- package/dist/serve/entries/storage.js.map +1 -1
- package/dist/serve/entries/worker-runtime.js +1 -1
- package/dist/serve/entries/worker-runtime.js.map +1 -1
- package/dist/serve/init-payload.d.ts +4 -0
- package/dist/serve/init-payload.d.ts.map +1 -1
- package/dist/serve/namespace.d.ts +20 -5
- package/dist/serve/namespace.d.ts.map +1 -1
- package/dist/serve/namespace.js +93 -129
- package/dist/serve/namespace.js.map +1 -1
- package/dist/serve/rules.d.ts.map +1 -1
- package/dist/serve/rules.js +44 -21
- package/dist/serve/rules.js.map +1 -1
- package/dist/serve/runtime/chip-config.d.ts.map +1 -1
- package/dist/serve/runtime/chip-config.js +16 -2
- package/dist/serve/runtime/chip-config.js.map +1 -1
- package/dist/serve/runtime/chip-dialog.d.ts +18 -0
- package/dist/serve/runtime/chip-dialog.d.ts.map +1 -0
- package/dist/serve/runtime/chip-dialog.js +315 -0
- package/dist/serve/runtime/chip-dialog.js.map +1 -0
- package/dist/serve/runtime/chip-install.d.ts +2 -0
- package/dist/serve/runtime/chip-install.d.ts.map +1 -1
- package/dist/serve/runtime/chip-install.js +9 -4
- package/dist/serve/runtime/chip-install.js.map +1 -1
- package/dist/serve/runtime/chip-user-search.d.ts +16 -0
- package/dist/serve/runtime/chip-user-search.d.ts.map +1 -0
- package/dist/serve/runtime/chip-user-search.js +354 -0
- package/dist/serve/runtime/chip-user-search.js.map +1 -0
- package/dist/serve/runtime/chip.d.ts +10 -0
- package/dist/serve/runtime/chip.d.ts.map +1 -1
- package/dist/serve/runtime/chip.js +149 -15
- package/dist/serve/runtime/chip.js.map +1 -1
- package/dist/serve/runtime/identity.d.ts +17 -0
- package/dist/serve/runtime/identity.d.ts.map +1 -0
- package/dist/serve/runtime/identity.js +10 -0
- package/dist/serve/runtime/identity.js.map +1 -0
- package/dist/serve/runtime/manifest.d.ts +1 -1
- package/dist/serve/runtime/manifest.d.ts.map +1 -1
- package/dist/serve/runtime/manifest.js +1 -1
- package/dist/serve/runtime/manifest.js.map +1 -1
- package/dist/serve/runtime/status.d.ts.map +1 -1
- package/dist/serve/runtime/status.js +23 -0
- package/dist/serve/runtime/status.js.map +1 -1
- package/dist/serve/sandbox-session.d.ts +8 -0
- package/dist/serve/sandbox-session.d.ts.map +1 -1
- package/dist/serve/sandbox-session.js +12 -4
- package/dist/serve/sandbox-session.js.map +1 -1
- package/dist/serve/server.d.ts +7 -0
- package/dist/serve/server.d.ts.map +1 -1
- package/dist/serve/server.js +15 -1
- package/dist/serve/server.js.map +1 -1
- package/dist/serve/site-ui/404.html +1 -1
- package/dist/serve/site-ui/_astro/{RulesCodeEditor.Bl-XMkhD.js → RulesCodeEditor.DsTAsKIa.js} +1 -1
- package/dist/serve/site-ui/_astro/_slug_.Da5VLhW1.css +1 -0
- package/dist/serve/site-ui/_astro/_studio_.BnfYCKoI.css +1 -0
- package/dist/serve/site-ui/_astro/app.BOr2kwae.js +115 -0
- package/dist/serve/site-ui/_astro/app.DhAcImFD.js +1 -0
- package/dist/serve/site-ui/_astro/app.pMSnN6Qw.css +1 -0
- package/dist/serve/site-ui/_astro/{chess-showcase.Dh5D8kqe.js → chess-showcase.Bn_pP6UJ.js} +2 -2
- package/dist/serve/site-ui/_astro/{example-runtime.CuyAjjiG.js → example-runtime.CXopTGv7.js} +1 -1
- package/dist/serve/site-ui/_astro/index.Tb-31efc.css +1 -0
- package/dist/serve/site-ui/_astro/index.UkCBr-_0.css +1 -0
- package/dist/serve/site-ui/_astro/sandbox-controls.rOuZTOUG.js +510 -0
- package/dist/serve/site-ui/_astro/{seed.CSUkloMi.js → seed.03a08f9t.js} +2 -2
- package/dist/serve/site-ui/_astro/writes.BsPBG3dp.js +653 -0
- package/dist/serve/site-ui/auth/index.html +5 -3
- package/dist/serve/site-ui/david-east.jpg +0 -0
- package/dist/serve/site-ui/docs/_rhythm/index.html +6 -3
- package/dist/serve/site-ui/docs/_rhythm.md +1 -1
- package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +18 -15
- package/dist/serve/site-ui/docs/agent/set-up-your-agent.md +16 -16
- package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +6 -5
- package/dist/serve/site-ui/docs/agent/watch-and-review.md +1 -5
- package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +8 -5
- package/dist/serve/site-ui/docs/agent/work-with-an-agent.md +4 -4
- package/dist/serve/site-ui/docs/ai-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/api-reference/index.html +13 -2
- package/dist/serve/site-ui/docs/api-reference.md +8 -0
- package/dist/serve/site-ui/docs/app-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/auth-compat/index.html +7 -4
- package/dist/serve/site-ui/docs/auth-compat.md +2 -2
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +10 -2
- package/dist/serve/site-ui/docs/build/ai-logic.md +6 -0
- package/dist/serve/site-ui/docs/build/authentication/index.html +7 -4
- package/dist/serve/site-ui/docs/build/authentication.md +2 -2
- package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +5 -2
- package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +5 -2
- package/dist/serve/site-ui/docs/build/cloud-storage/index.html +6 -3
- package/dist/serve/site-ui/docs/build/cloud-storage.md +1 -1
- package/dist/serve/site-ui/docs/build/realtime-database/index.html +9 -6
- package/dist/serve/site-ui/docs/build/realtime-database.md +4 -4
- package/dist/serve/site-ui/docs/conformance-scores/index.html +10 -3
- package/dist/serve/site-ui/docs/conformance-scores.md +5 -1
- package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/create-pyric-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/database-compat/index.html +48 -45
- package/dist/serve/site-ui/docs/database-compat.md +43 -43
- package/dist/serve/site-ui/docs/examples/chess/index.html +5 -2
- package/dist/serve/site-ui/docs/examples/index.html +5 -2
- package/dist/serve/site-ui/docs/firestore-compat/index.html +8 -5
- package/dist/serve/site-ui/docs/firestore-compat.md +3 -3
- package/dist/serve/site-ui/docs/firestore-flutter-compat/index.html +319 -0
- package/dist/serve/site-ui/docs/firestore-flutter-compat.md +344 -0
- package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/get-started/backend-bundlers/index.html +105 -0
- package/dist/serve/site-ui/docs/get-started/backend-bundlers.md +165 -0
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +9 -6
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works.md +5 -5
- package/dist/serve/site-ui/docs/get-started/nextjs/index.html +9 -6
- package/dist/serve/site-ui/docs/get-started/nextjs.md +4 -4
- package/dist/serve/site-ui/docs/get-started/start-building/index.html +14 -9
- package/dist/serve/site-ui/docs/get-started/start-building.md +16 -10
- package/dist/serve/site-ui/docs/get-started/vite/index.html +6 -3
- package/dist/serve/site-ui/docs/get-started/vite.md +1 -1
- package/dist/serve/site-ui/docs/index.json +3128 -1613
- package/dist/serve/site-ui/docs/messaging-compat/index.html +5 -2
- package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +5 -2
- package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +6 -4
- package/dist/serve/site-ui/docs/observe/see-whats-happening.md +2 -4
- package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +9 -6
- package/dist/serve/site-ui/docs/observe/shape-your-data.md +4 -4
- package/dist/serve/site-ui/docs/overview/index.html +16 -10
- package/dist/serve/site-ui/docs/overview.md +19 -11
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +8 -5
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api.md +3 -3
- package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-admin-reference-api/index.html +501 -0
- package/dist/serve/site-ui/docs/pyric-admin-reference-api.md +587 -0
- package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-analytics-reference-api/index.html +124 -0
- package/dist/serve/site-ui/docs/pyric-analytics-reference-api.md +287 -0
- package/dist/serve/site-ui/docs/pyric-app-check-reference-api/index.html +87 -0
- package/dist/serve/site-ui/docs/pyric-app-check-reference-api.md +199 -0
- package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +10 -3
- package/dist/serve/site-ui/docs/pyric-auth-reference-api.md +2 -1
- package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-bundler-reference-api/index.html +56 -0
- package/dist/serve/site-ui/docs/pyric-cli-bundler-reference-api.md +72 -0
- package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +6 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api.md +1 -0
- package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +6 -2
- package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api.md +1 -0
- package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +39 -11
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api.md +17 -9
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +137 -94
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api.md +61 -1
- package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api.md +2 -2
- package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +167 -96
- package/dist/serve/site-ui/docs/pyric-database-reference-api.md +108 -2
- package/dist/serve/site-ui/docs/pyric-firestore-lite-reference-api/index.html +559 -0
- package/dist/serve/site-ui/docs/pyric-firestore-lite-reference-api.md +1240 -0
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +44 -12
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api.md +18 -8
- package/dist/serve/site-ui/docs/pyric-functions-reference-api/index.html +67 -0
- package/dist/serve/site-ui/docs/pyric-functions-reference-api.md +149 -0
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +10 -4
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api.md +5 -2
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +10 -4
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api.md +5 -2
- package/dist/serve/site-ui/docs/pyric-performance-reference-api/index.html +35 -0
- package/dist/serve/site-ui/docs/pyric-performance-reference-api.md +69 -0
- package/dist/serve/site-ui/docs/pyric-remote-config-reference-api/index.html +132 -0
- package/dist/serve/site-ui/docs/pyric-remote-config-reference-api.md +307 -0
- package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +56 -18
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api.md +48 -9
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api.md +2 -2
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +243 -50
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api.md +148 -31
- package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +26 -5
- package/dist/serve/site-ui/docs/pyric-storage-reference-api.md +24 -2
- package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +30 -97
- package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api.md +20 -51
- package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +6 -3
- package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +128 -27
- package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api.md +58 -5
- package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +8 -6
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api.md +3 -4
- package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +5 -2
- package/dist/serve/site-ui/docs/reference/cli/index.html +156 -0
- package/dist/serve/site-ui/docs/reference/cli.md +120 -0
- package/dist/serve/site-ui/docs/rules-compat/index.html +14 -3
- package/dist/serve/site-ui/docs/rules-compat.md +9 -1
- package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +6 -3
- package/dist/serve/site-ui/docs/secure/read-a-denial.md +1 -1
- package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +5 -2
- package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +5 -2
- package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +7 -4
- package/dist/serve/site-ui/docs/ship/ship-to-production.md +2 -2
- package/dist/serve/site-ui/docs/ship/test-in-node/index.html +9 -6
- package/dist/serve/site-ui/docs/ship/test-in-node.md +4 -4
- package/dist/serve/site-ui/docs/storage-compat/index.html +17 -14
- package/dist/serve/site-ui/docs/storage-compat.md +12 -12
- package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +5 -2
- package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +5 -2
- package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +5 -2
- package/dist/serve/site-ui/docs/tutorial/index.html +10 -7
- package/dist/serve/site-ui/docs/tutorial.md +4 -4
- package/dist/serve/site-ui/examples/chess/index.html +4 -2
- package/dist/serve/site-ui/firestore/index.html +5 -3
- package/dist/serve/site-ui/hero-charcoal.jpg +0 -0
- package/dist/serve/site-ui/index.html +9 -7
- package/dist/serve/site-ui/llms.txt +13 -3
- package/dist/serve/site-ui/pyric-campfire-full.png +0 -0
- package/dist/serve/site-ui/pyric-campfire.png +0 -0
- package/dist/serve/site-ui/rtdb/index.html +5 -3
- package/dist/serve/site-ui/settings/index.html +5 -3
- package/dist/serve/site-ui/storage/index.html +5 -3
- package/dist/serve/site-ui/studio/index.html +13 -0
- package/dist/serve/site-ui/studio-routes.json +1 -1
- package/dist/serve/site-ui/traffic/index.html +5 -3
- package/dist/serve/studio/disk-workspace.d.ts.map +1 -1
- package/dist/serve/studio/disk-workspace.js +64 -2
- package/dist/serve/studio/disk-workspace.js.map +1 -1
- package/dist/serve/studio/routes.d.ts +9 -0
- package/dist/serve/studio/routes.d.ts.map +1 -1
- package/dist/serve/studio/routes.js +47 -7
- package/dist/serve/studio/routes.js.map +1 -1
- package/dist/serve/vite-ai-config.d.ts.map +1 -1
- package/dist/serve/vite-ai-config.js +38 -8
- package/dist/serve/vite-ai-config.js.map +1 -1
- package/dist/serve/vite-module-swap.d.ts.map +1 -1
- package/dist/serve/vite-module-swap.js +0 -28
- package/dist/serve/vite-module-swap.js.map +1 -1
- package/dist/serve/vite-plugin.d.ts +7 -7
- package/dist/serve/vite-plugin.js +1 -1
- package/dist/serve/vite-plugin.js.map +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts.map +1 -1
- package/dist/serve/vite-sandbox-generation.js +13 -1
- package/dist/serve/vite-sandbox-generation.js.map +1 -1
- package/dist/serve/worker/client/auth.js +1 -2
- package/dist/serve/worker/client/auth.js.map +1 -1
- package/dist/serve/worker/client/core.d.ts +21 -1
- package/dist/serve/worker/client/core.d.ts.map +1 -1
- package/dist/serve/worker/client/core.js +79 -3
- package/dist/serve/worker/client/core.js.map +1 -1
- package/dist/serve/worker/client/disconnect.js +1 -1
- package/dist/serve/worker/client/disconnect.js.map +1 -1
- package/dist/serve/worker/client/presence.d.ts.map +1 -1
- package/dist/serve/worker/client/presence.js +18 -5
- package/dist/serve/worker/client/presence.js.map +1 -1
- package/dist/serve/worker/client.d.ts +1 -1
- package/dist/serve/worker/client.d.ts.map +1 -1
- package/dist/serve/worker/client.js +1 -1
- package/dist/serve/worker/client.js.map +1 -1
- package/dist/serve/worker/context-resolver.d.ts +30 -0
- package/dist/serve/worker/context-resolver.d.ts.map +1 -0
- package/dist/serve/worker/context-resolver.js +44 -0
- package/dist/serve/worker/context-resolver.js.map +1 -0
- package/dist/serve/worker/entry.js +23 -19
- package/dist/serve/worker/entry.js.map +1 -1
- package/dist/serve/worker/host/core.d.ts +1 -0
- package/dist/serve/worker/host/core.d.ts.map +1 -1
- package/dist/serve/worker/host/core.js +12 -6
- package/dist/serve/worker/host/core.js.map +1 -1
- package/dist/serve/worker/host/storage.js +2 -2
- package/dist/serve/worker/host-ai.d.ts.map +1 -1
- package/dist/serve/worker/host-ai.js +15 -0
- package/dist/serve/worker/host-ai.js.map +1 -1
- package/dist/serve/worker/host-auth.js +1 -2
- package/dist/serve/worker/host-auth.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 +2 -2
- package/dist/serve/worker/index.js.map +1 -1
- package/dist/serve/worker/port-lifecycle.d.ts +18 -0
- package/dist/serve/worker/port-lifecycle.d.ts.map +1 -0
- package/dist/serve/worker/port-lifecycle.js +28 -0
- package/dist/serve/worker/port-lifecycle.js.map +1 -0
- package/dist/serve/worker/protocol.d.ts +4 -0
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js.map +1 -1
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +15 -3
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/dist/verify/fixture.d.ts +1 -1
- package/package.json +17 -6
- package/src/assurance/.generated/conformance-verdicts.ts +68 -1
- package/src/bridge/client/dispatch.ts +52 -56
- package/src/bridge/client/tool-family-factories.ts +34 -0
- package/src/bridge/server/mcp-contract.ts +15 -59
- package/src/bridge/server/peer.ts +24 -1
- package/src/bridge/server/tool-family-factories.ts +40 -0
- package/src/bridge/server/tool-metadata.ts +29 -61
- package/src/bridge/tool-families.generated.ts +17 -0
- package/src/bridge/tool-families.ts +87 -0
- package/src/bridge/tool-family-records/conformance.ts +6 -0
- package/src/bridge/tool-family-records/firestore-data.ts +15 -0
- package/src/bridge/tool-family-records/firestore-inspect.ts +6 -0
- package/src/bridge/tool-family-records/firestore-rules.ts +15 -0
- package/src/bridge/tool-family-records/firestore-simulator.ts +16 -0
- package/src/bridge/tool-family-records/rtdb-inspection.ts +6 -0
- package/src/bundler/externals.ts +55 -0
- package/src/bundler/index.ts +21 -0
- package/src/cli/cli.test.ts +2 -46
- package/src/cli/index.ts +48 -42
- package/src/cli/inlined-sdk-scanner.ts +77 -0
- package/src/cli/mcp-proxy.ts +4 -4
- package/src/cli/parse-args.ts +61 -15
- package/src/cli/pyric-config.ts +45 -0
- package/src/cli/sandbox-interlock.ts +167 -0
- package/src/cli/{dev-runner.ts → sandbox-runner.ts} +142 -86
- package/src/cli/serve.ts +182 -115
- package/src/cli/snapshot.ts +5 -5
- package/src/cli/unsupported-runtime.ts +68 -0
- package/src/cli/verify.ts +1 -1
- package/src/conformance/.generated/can-i-use-browser.ts +2 -2
- package/src/conformance/.generated/can-i-use.ts +2 -2
- package/src/conformance/.generated/conformance-docs.ts +1 -1
- package/src/functions-rtdb/child.ts +115 -21
- package/src/functions-rtdb/development-runtime.ts +5 -1
- package/src/functions-rtdb/project.ts +8 -8
- package/src/google-endpoints.ts +155 -0
- package/src/next/guard.ts +1 -1
- package/src/next/runtime-env.ts +5 -0
- package/src/next/with-pyric.ts +1 -1
- package/src/register/beacon.ts +232 -0
- package/src/register/index.ts +68 -6
- package/src/register/net-guard.ts +567 -0
- package/src/remote/index.ts +9 -9
- package/src/serve/ai-diagnostics-relay.ts +243 -0
- package/src/serve/ai-proxy.ts +327 -0
- package/src/serve/ai-status.ts +109 -0
- package/src/serve/ai-terminal-blocks.ts +210 -0
- package/src/serve/ai-terminal-text.ts +52 -0
- package/src/serve/beacon-route.ts +141 -0
- package/src/serve/bundler.ts +10 -10
- package/src/serve/entries/active-auth-registry.ts +56 -0
- package/src/serve/entries/active-auth.ts +100 -0
- package/src/serve/entries/ai.ts +27 -1
- package/src/serve/entries/auth-helper-core.ts +5 -0
- package/src/serve/entries/auth-helper-dom.ts +27 -3
- package/src/serve/entries/auth-helper-runtime.ts +1 -1
- package/src/serve/entries/auth.ts +8 -0
- package/src/serve/entries/bridge-url.ts +1 -1
- package/src/serve/entries/firestore.ts +3 -3
- package/src/serve/entries/init.ts +78 -3
- package/src/serve/entries/keepalive.ts +1 -1
- package/src/serve/entries/runtime.ts +29 -14
- package/src/serve/entries/storage.ts +1 -1
- package/src/serve/entries/worker-runtime.ts +1 -1
- package/src/serve/init-payload.ts +4 -0
- package/src/serve/namespace.ts +117 -145
- package/src/serve/rules.ts +47 -22
- package/src/serve/runtime/chip-config.ts +16 -2
- package/src/serve/runtime/chip-dialog.ts +366 -0
- package/src/serve/runtime/chip-install.ts +10 -4
- package/src/serve/runtime/chip-user-search.ts +391 -0
- package/src/serve/runtime/chip.ts +177 -15
- package/src/serve/runtime/identity.ts +26 -0
- package/src/serve/runtime/manifest.ts +1 -1
- package/src/serve/runtime/status.ts +24 -0
- package/src/serve/sandbox-session.ts +19 -4
- package/src/serve/server.ts +18 -1
- package/src/serve/studio/disk-workspace.ts +76 -2
- package/src/serve/studio/routes.ts +63 -7
- package/src/serve/vite-ai-config.ts +37 -7
- package/src/serve/vite-module-swap.ts +0 -28
- package/src/serve/vite-plugin.ts +8 -8
- package/src/serve/vite-sandbox-generation.ts +16 -1
- package/src/serve/worker/client/auth.ts +1 -1
- package/src/serve/worker/client/core.ts +80 -3
- package/src/serve/worker/client/disconnect.ts +1 -1
- package/src/serve/worker/client/presence.ts +25 -5
- package/src/serve/worker/client.ts +1 -1
- package/src/serve/worker/context-resolver.ts +60 -0
- package/src/serve/worker/entry.ts +22 -18
- package/src/serve/worker/host/core.ts +9 -7
- package/src/serve/worker/host/storage.ts +2 -2
- package/src/serve/worker/host-ai.ts +15 -0
- package/src/serve/worker/host-auth.ts +1 -1
- package/src/serve/worker/index.ts +4 -1
- package/src/serve/worker/port-lifecycle.ts +45 -0
- package/src/serve/worker/protocol.ts +2 -1
- package/src/serve/worker/serve-init.ts +14 -3
- package/src/verify/fixture.ts +1 -1
- package/README.md.orig +0 -145
- package/dist/cli/dev-runner.d.ts.map +0 -1
- package/dist/cli/dev-runner.js.map +0 -1
- package/dist/serve/site-ui/_astro/_slug_.CXh9yTP1.css +0 -1
- package/dist/serve/site-ui/_astro/_slug_.CupVK4TM.css +0 -1
- package/dist/serve/site-ui/_astro/_studio_.CfxiQWkk.css +0 -1
- package/dist/serve/site-ui/_astro/app.BsFsMjn0.css +0 -1
- package/dist/serve/site-ui/_astro/app.Ce3MTLQm.js +0 -1
- package/dist/serve/site-ui/_astro/app.taqzyjD0.js +0 -11
- package/dist/serve/site-ui/_astro/sandbox-controls.BP4M5f1M.js +0 -1169
- package/dist/serve/site-ui/_astro/writes.C57paAj2.js +0 -99
- package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +0 -582
- package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research.md +0 -448
|
@@ -33,12 +33,22 @@ import type {
|
|
|
33
33
|
WorkspaceChange,
|
|
34
34
|
WorkspaceStore,
|
|
35
35
|
} from './store-types.js';
|
|
36
|
+
import { isAllowedHost, isAllowedOrigin, getHeader } from '../server.js';
|
|
37
|
+
import type { WriterLock } from '../writer-lock.js';
|
|
36
38
|
|
|
37
39
|
export interface StudioRouteOptions {
|
|
38
40
|
/** The single-project file tree served at `/__pyric/workspace`. */
|
|
39
41
|
workspace?: WorkspaceStore;
|
|
40
42
|
/** The project list served at `/__pyric/projects`. */
|
|
41
43
|
projects?: ProjectStore;
|
|
44
|
+
/** Per-boot session capability token required on workspace & project endpoints. */
|
|
45
|
+
sessionToken?: string;
|
|
46
|
+
/** Single-writer lock validator for file mutation & deletion endpoints. */
|
|
47
|
+
writerLock?: WriterLock;
|
|
48
|
+
/** Bound host for DNS rebinding guard. */
|
|
49
|
+
boundHost?: string;
|
|
50
|
+
/** Extra hostnames allowed for DNS rebinding/origin guard. */
|
|
51
|
+
allowedHosts?: string[];
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
/** Collect the raw request body as a UTF-8 string. */
|
|
@@ -207,6 +217,10 @@ async function handleProjects(
|
|
|
207
217
|
.end('method not allowed');
|
|
208
218
|
}
|
|
209
219
|
|
|
220
|
+
function isWorkspaceMutation(method?: string): boolean {
|
|
221
|
+
return method === 'PUT' || method === 'POST' || method === 'PATCH' || method === 'DELETE';
|
|
222
|
+
}
|
|
223
|
+
|
|
210
224
|
/**
|
|
211
225
|
* Build a namespace fragment for the Studio storage routes. Returns a handler
|
|
212
226
|
* shaped like the other `/__pyric/*` fragments: it resolves `true` when it
|
|
@@ -219,13 +233,55 @@ export function createStudioRoutes(opts: StudioRouteOptions) {
|
|
|
219
233
|
url: URL,
|
|
220
234
|
): Promise<boolean> => {
|
|
221
235
|
try {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
236
|
+
const isWorkspaceRoute = opts.workspace && url.pathname.startsWith('/__pyric/workspace');
|
|
237
|
+
const isProjectsRoute = opts.projects && url.pathname.startsWith('/__pyric/projects');
|
|
238
|
+
|
|
239
|
+
if (isWorkspaceRoute || isProjectsRoute) {
|
|
240
|
+
const hostHeader = getHeader(req, 'host');
|
|
241
|
+
const originHeader = getHeader(req, 'origin');
|
|
242
|
+
|
|
243
|
+
// 1. Host and Origin guard (Requirement 1)
|
|
244
|
+
if (!isAllowedHost(hostHeader, opts.boundHost ?? 'localhost', opts.allowedHosts)) {
|
|
245
|
+
res.writeHead(403, { 'content-type': 'text/plain' }).end('Forbidden: host not allowed');
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
if (originHeader && !isAllowedOrigin(originHeader, opts.boundHost ?? 'localhost', opts.allowedHosts)) {
|
|
249
|
+
res.writeHead(403, { 'content-type': 'text/plain' }).end('Forbidden: origin mismatch');
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 2. Per-boot session capability token guard (Requirement 2)
|
|
254
|
+
if (opts.sessionToken) {
|
|
255
|
+
const reqToken =
|
|
256
|
+
getHeader(req, 'x-pyric-session-token') ??
|
|
257
|
+
url.searchParams.get('token');
|
|
258
|
+
if (!reqToken || reqToken !== opts.sessionToken) {
|
|
259
|
+
res.writeHead(401, { 'content-type': 'text/plain' }).end('Unauthorized: invalid session capability token');
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 3. Single-writer lock guard for mutation endpoints (Requirement 4)
|
|
265
|
+
if (opts.writerLock && isWorkspaceMutation(req.method)) {
|
|
266
|
+
const writerId = getHeader(req, 'x-pyric-writer');
|
|
267
|
+
if (!writerId) {
|
|
268
|
+
res.writeHead(423, { 'content-type': 'text/plain' }).end('Locked: missing active writer lock header');
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
if (!opts.writerLock.claim(writerId, Date.now())) {
|
|
272
|
+
res.writeHead(423, { 'content-type': 'text/plain' }).end('Locked: another tab holds the writer lock');
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (isWorkspaceRoute) {
|
|
278
|
+
await handleWorkspace(opts.workspace!, req, res, url);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
if (isProjectsRoute) {
|
|
282
|
+
await handleProjects(opts.projects!, req, res, url);
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
229
285
|
}
|
|
230
286
|
} catch (e) {
|
|
231
287
|
if (!res.headersSent) sendError(res, e);
|
|
@@ -44,7 +44,18 @@ export function loadViteAiEnv(
|
|
|
44
44
|
const resolvedEnvDir = envDir === undefined
|
|
45
45
|
? resolvedRoot
|
|
46
46
|
: path.resolve(resolvedRoot, envDir);
|
|
47
|
-
|
|
47
|
+
const loaded = loadEnv(mode, resolvedEnvDir, '');
|
|
48
|
+
for (const [key, val] of Object.entries(loaded)) {
|
|
49
|
+
const isAuthKey =
|
|
50
|
+
key === 'GEMINI_API_KEY' ||
|
|
51
|
+
key === 'GOOGLE_GENAI_API_KEY' ||
|
|
52
|
+
key === 'VITE_GEMINI_API_KEY' ||
|
|
53
|
+
key === 'PYRIC_API_KEY';
|
|
54
|
+
if (isAuthKey && process.env[key] === undefined) {
|
|
55
|
+
process.env[key] = val;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return loaded;
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
/** Convert a public declarative engine into the JSON-safe worker wire shape. */
|
|
@@ -65,6 +76,21 @@ export function engineConfigToWire(engine: PyricAiEngineConfig): AiEngineConfigW
|
|
|
65
76
|
}
|
|
66
77
|
return result as AiEngineConfigWire;
|
|
67
78
|
}
|
|
79
|
+
const isGeminiEngine = engine.kind === 'gemini';
|
|
80
|
+
if (isGeminiEngine) {
|
|
81
|
+
const result: Record<string, unknown> = {
|
|
82
|
+
kind: 'gemini',
|
|
83
|
+
};
|
|
84
|
+
const hasBaseUrl = engine.baseUrl !== undefined;
|
|
85
|
+
if (hasBaseUrl) {
|
|
86
|
+
result.baseUrl = engine.baseUrl;
|
|
87
|
+
}
|
|
88
|
+
const hasApiKey = engine.apiKey !== undefined;
|
|
89
|
+
if (hasApiKey) {
|
|
90
|
+
result.apiKey = engine.apiKey;
|
|
91
|
+
}
|
|
92
|
+
return result as AiEngineConfigWire;
|
|
93
|
+
}
|
|
68
94
|
const result: Record<string, unknown> = {
|
|
69
95
|
kind: 'scripted',
|
|
70
96
|
};
|
|
@@ -123,12 +149,16 @@ export function resolveViteAiConfig(
|
|
|
123
149
|
}
|
|
124
150
|
|
|
125
151
|
let engineWire: AiEngineConfigWire | undefined = undefined;
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
152
|
+
if (isProductionMode) {
|
|
153
|
+
const apiKey = env.GEMINI_API_KEY ?? env.GOOGLE_GENAI_API_KEY ?? env.VITE_GEMINI_API_KEY;
|
|
154
|
+
engineWire = {
|
|
155
|
+
kind: 'gemini',
|
|
156
|
+
...(apiKey !== undefined && apiKey.trim() !== '' ? { apiKey: apiKey.trim() } : {}),
|
|
157
|
+
};
|
|
158
|
+
} else if (explicitEngine !== undefined) {
|
|
159
|
+
engineWire = engineConfigToWire(explicitEngine);
|
|
160
|
+
} else if (model !== undefined) {
|
|
161
|
+
engineWire = engineConfigToWire({ kind: 'openai', baseUrl: AI_PROXY_PATH, model });
|
|
132
162
|
}
|
|
133
163
|
|
|
134
164
|
let proxyUpstream: string | undefined = undefined;
|
|
@@ -86,20 +86,6 @@ export function createViteModuleSwap(
|
|
|
86
86
|
|
|
87
87
|
const match = FIREBASE_SPECIFIER.exec(args.path);
|
|
88
88
|
const subpath = match !== null && match[1] !== undefined ? match[1] : '';
|
|
89
|
-
const isProductionAiMode = aiMode() === 'production';
|
|
90
|
-
if (isProductionAiMode) {
|
|
91
|
-
const isAiSubpath = subpath === 'ai';
|
|
92
|
-
if (isAiSubpath) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
const isAppSubpath = subpath === 'app';
|
|
96
|
-
const passthroughEntry = entries['app-ai-passthrough'];
|
|
97
|
-
const hasPassthroughEntry = passthroughEntry !== undefined;
|
|
98
|
-
const shouldUsePassthroughBridge = isAppSubpath && hasPassthroughEntry;
|
|
99
|
-
if (shouldUsePassthroughBridge) {
|
|
100
|
-
return { path: passthroughEntry };
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
89
|
|
|
104
90
|
const isServedSubpath = SERVED_FIREBASE_SUBPATHS.has(subpath);
|
|
105
91
|
if (isServedSubpath) {
|
|
@@ -173,20 +159,6 @@ export function createViteModuleSwap(
|
|
|
173
159
|
const isFirebaseSpecifier = firebaseMatch !== null;
|
|
174
160
|
if (isFirebaseSpecifier) {
|
|
175
161
|
const subpath = firebaseMatch[1] !== undefined ? firebaseMatch[1] : '';
|
|
176
|
-
const isProductionAiMode = aiMode() === 'production';
|
|
177
|
-
if (isProductionAiMode) {
|
|
178
|
-
const isAiSubpath = subpath === 'ai';
|
|
179
|
-
if (isAiSubpath) {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
const isAppSubpath = subpath === 'app';
|
|
183
|
-
const passthroughEntry = entries['app-ai-passthrough'];
|
|
184
|
-
const hasPassthroughEntry = passthroughEntry !== undefined;
|
|
185
|
-
const shouldUsePassthroughBridge = isAppSubpath && hasPassthroughEntry;
|
|
186
|
-
if (shouldUsePassthroughBridge) {
|
|
187
|
-
return passthroughEntry;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
162
|
const isServedSubpath = SERVED_FIREBASE_SUBPATHS.has(subpath);
|
|
191
163
|
if (isServedSubpath) {
|
|
192
164
|
const key = entryKey(subpath);
|
package/src/serve/vite-plugin.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@pyric/cli/vite` — the firebase→pyric-sandbox swap as a Vite plugin.
|
|
3
3
|
*
|
|
4
|
-
* The serve analog for SOURCE-driven apps: instead of `vite build && pyric
|
|
4
|
+
* The serve analog for SOURCE-driven apps: instead of `vite build && pyric sandbox
|
|
5
5
|
* dist`, a team keeps `vite dev` (HMR, source maps) with the in-process sandbox
|
|
6
6
|
* standing in for Firebase. The app's `firebase/*` imports are UNCHANGED — the
|
|
7
7
|
* plugin swaps them at the module-resolution layer (`resolveId`), the same way
|
|
8
|
-
* `pyric
|
|
8
|
+
* `pyric sandbox` swaps via a runtime import map. (Design: `plans/pyric-vite-plugin.md`.)
|
|
9
9
|
*
|
|
10
10
|
* Two flavors, one `apply` function. Under `vite dev` the swap is ALWAYS on.
|
|
11
11
|
* For `vite build` the swap is MODE-gated: a plain `vite build` (mode
|
|
12
12
|
* `production`) ships the real `firebase` package — the swap never reaches the
|
|
13
13
|
* prod artifact — while `vite build --mode development` (any NON-production
|
|
14
14
|
* mode) produces a SANDBOX build that bundles pyric's in-page adapters instead
|
|
15
|
-
* of the real SDK: self-contained, meant to be previewed under `pyric
|
|
15
|
+
* of the real SDK: self-contained, meant to be previewed under `pyric sandbox`, and
|
|
16
16
|
* stamped with the sandbox-build marker so it can never be deployed (`pyric
|
|
17
17
|
* deploy hosting` refuses it). `pyric({ swapInBuild })` forces the build
|
|
18
18
|
* behavior on/off regardless of mode. See `sandbox-marker.ts`.
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
* - the host-neutral sandbox session — rules, fixtures, stores, live payload,
|
|
25
25
|
* Studio routes, `/__pyric/*` namespace, and owned cleanup;
|
|
26
26
|
* - the SharedWorker host — `bundleWorker` served at `/__pyric/sdk/worker.js`;
|
|
27
|
-
* - the bridge mount shared with `pyric
|
|
27
|
+
* - the bridge mount shared with `pyric sandbox --bridge`.
|
|
28
28
|
*
|
|
29
29
|
* Scope = M1 (swap + rules) + M2 (SharedWorker multi-tab + persist/capture/seed)
|
|
30
30
|
* + M3 (the MCP bridge fold — `{ bridge }`). M3 reuses `createBridgeMount` (the
|
|
31
|
-
* proven serve-flavored bridge behind `pyric
|
|
31
|
+
* proven serve-flavored bridge behind `pyric sandbox --bridge`), composing it into
|
|
32
32
|
* the same `/__pyric` middleware; on the worker path the bridge peer routes
|
|
33
33
|
* agent tool-calls THROUGH the SharedWorker (`connectBridgePeer`), so the agent
|
|
34
34
|
* and the app share one backend without forcing the page in-page.
|
|
@@ -82,7 +82,7 @@ export interface PyricOptions {
|
|
|
82
82
|
* only when the worker bundle itself fails, bridge or not. */
|
|
83
83
|
bridge?: boolean | { project?: string; disableAuditLog?: boolean };
|
|
84
84
|
/** Serve the **Pyric Studio** app at `/__pyric/ui/` on Vite's dev origin (the
|
|
85
|
-
* `pyric
|
|
85
|
+
* `pyric sandbox --ui` equivalent). Mounts the disk-backed workspace/project
|
|
86
86
|
* routes Studio's `local` mode talks to AND serves the unified Astro site
|
|
87
87
|
* (vendored in this package at `dist/serve/site-ui`). **On by default**,
|
|
88
88
|
* including under `bridge` (the bridge peer routes through the SharedWorker,
|
|
@@ -93,7 +93,7 @@ export interface PyricOptions {
|
|
|
93
93
|
* dev/builds. On by default. Pass `false` to hide it, or
|
|
94
94
|
* `{ initiallyOpen: true }` when actively debugging runtime errors. */
|
|
95
95
|
runtimeChip?: PyricRuntimeChipOption;
|
|
96
|
-
/** RTDB-triggered Cloud Functions under this dev server (the `pyric
|
|
96
|
+
/** RTDB-triggered Cloud Functions under this dev server (the `pyric sandbox`
|
|
97
97
|
* parity fold). By default a `functions` block in `firebase.json` is
|
|
98
98
|
* discovered automatically: its `onValueCreated` triggers run in an isolated
|
|
99
99
|
* node child against the shared sandbox (other trigger kinds warn and are
|
|
@@ -216,7 +216,7 @@ export function pyric(options: PyricOptions = {}): Plugin {
|
|
|
216
216
|
// `vite build` keeps the real firebase package — the swap never reaches
|
|
217
217
|
// production output. A sandbox build applies the same swap so the output
|
|
218
218
|
// bundles pyric's in-page adapters (self-contained; preview it under
|
|
219
|
-
// `pyric
|
|
219
|
+
// `pyric sandbox`, never deploy it).
|
|
220
220
|
apply(config, env) {
|
|
221
221
|
void config;
|
|
222
222
|
return pageRuntime.applies(env);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import type { ViteDevServer } from 'vite';
|
|
3
3
|
import { readFirebaseJson, readFirebaseRc, type FirebaseJson } from '../cli/firebase-json.js';
|
|
4
|
-
import { registerModuleUrl } from '../cli/
|
|
4
|
+
import { registerModuleUrl } from '../cli/sandbox-runner.js';
|
|
5
5
|
import {
|
|
6
6
|
discoverFunctionsRtdbProject,
|
|
7
7
|
type FunctionsRtdbProject,
|
|
8
8
|
} from '../functions-rtdb/project.js';
|
|
9
9
|
import { formatActivityWarning } from './activity-warning.js';
|
|
10
|
+
import { formatAiStatusNote } from './ai-status.js';
|
|
10
11
|
import {
|
|
11
12
|
createBridgeMount,
|
|
12
13
|
type BridgeHostAttachment,
|
|
@@ -217,6 +218,20 @@ export async function createViteSandboxGeneration(
|
|
|
217
218
|
server.config.logger.info(' ⓘ [pyric] fresh: discarded the existing state file; re-seeding');
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
// Say what AI resolved to. This is the one front door where the engine and
|
|
222
|
+
// its model binding ARE known at boot (`ai.engine` / `ai.model` reduced to
|
|
223
|
+
// the wire shape above); the broker itself is still built lazily, in the
|
|
224
|
+
// page or the worker, on the first `ai.*` op. Every generation prints it,
|
|
225
|
+
// so a Vite restart on an `.env`/config edit re-announces the new binding
|
|
226
|
+
// through the same formatter instead of swapping engines in silence.
|
|
227
|
+
server.config.logger.info(
|
|
228
|
+
formatAiStatusNote({
|
|
229
|
+
engine: ai.engineWire,
|
|
230
|
+
mode: ai.mode,
|
|
231
|
+
proxyUpstream: ai.proxyUpstream,
|
|
232
|
+
}),
|
|
233
|
+
);
|
|
234
|
+
|
|
220
235
|
const middlewareInput = { server, bridge, session };
|
|
221
236
|
const disposeMiddleware = attachViteGenerationMiddleware(middlewareInput);
|
|
222
237
|
listenerDisposers.push(disposeMiddleware);
|
|
@@ -312,7 +312,7 @@ export async function restorePortSession(
|
|
|
312
312
|
t: 'op', id: nextId(), method: 'auth.restorePortSession', uid,
|
|
313
313
|
})) as SerializedUser | null;
|
|
314
314
|
const user = toClientUser(auth.port, raw);
|
|
315
|
-
|
|
315
|
+
auth.currentUser = user;
|
|
316
316
|
return user;
|
|
317
317
|
}
|
|
318
318
|
|
|
@@ -259,26 +259,103 @@ export function wirePort(port: ClientPort): void {
|
|
|
259
259
|
// ops (`auth.*`) and `getVersion` are NEVER lensed — they operate the worker's
|
|
260
260
|
// session, not data — so the lens is stamped only on the data-op path.
|
|
261
261
|
|
|
262
|
+
export const AUTH_LENS_STORAGE_KEY = 'pyric:auth-lens';
|
|
263
|
+
|
|
264
|
+
type PersistedAuthLens =
|
|
265
|
+
| Extract<AuthLens, { mode: 'admin' }>
|
|
266
|
+
| Extract<AuthLens, { mode: 'as' }>;
|
|
267
|
+
|
|
268
|
+
export function isPersistedAuthLens(value: unknown): value is PersistedAuthLens {
|
|
269
|
+
if (!value || typeof value !== 'object') return false;
|
|
270
|
+
const candidate = value as { mode?: unknown; uid?: unknown };
|
|
271
|
+
if (candidate.mode === 'admin') return true;
|
|
272
|
+
return candidate.mode === 'as' && typeof candidate.uid === 'string';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function normalizePersistedAuthLens(value: unknown): PersistedAuthLens | undefined {
|
|
276
|
+
if (isPersistedAuthLens(value)) return value;
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Read persisted auth lens from sessionStorage safely. */
|
|
281
|
+
export function hydrateLensFromStorage(): AuthLens | undefined {
|
|
282
|
+
if (typeof sessionStorage === 'undefined') return undefined;
|
|
283
|
+
try {
|
|
284
|
+
const raw = sessionStorage.getItem(AUTH_LENS_STORAGE_KEY);
|
|
285
|
+
if (!raw) return undefined;
|
|
286
|
+
const parsed: unknown = JSON.parse(raw);
|
|
287
|
+
const persistedLens = normalizePersistedAuthLens(parsed);
|
|
288
|
+
if (persistedLens) return persistedLens;
|
|
289
|
+
sessionStorage.removeItem(AUTH_LENS_STORAGE_KEY);
|
|
290
|
+
return undefined;
|
|
291
|
+
} catch {
|
|
292
|
+
try {
|
|
293
|
+
sessionStorage.removeItem(AUTH_LENS_STORAGE_KEY);
|
|
294
|
+
} catch {
|
|
295
|
+
// Ignore storage restrictions
|
|
296
|
+
}
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
262
301
|
/** Module-level default lens. `undefined` ⇒ the worker treats the op as the
|
|
263
302
|
* app's session (the additive default — existing senders omit `actAs`).
|
|
264
303
|
*
|
|
265
304
|
* Exported as a LIVE binding: family modules read it to stamp the lens onto
|
|
266
305
|
* subscriptions (`onSnapshot`, `rtdbOnValue`), and see `setLens(...)` updates
|
|
267
306
|
* through this one singleton. */
|
|
268
|
-
export let _defaultLens: AuthLens | undefined;
|
|
307
|
+
export let _defaultLens: AuthLens | undefined = hydrateLensFromStorage();
|
|
308
|
+
|
|
309
|
+
const lensListeners = new Set<(lens: AuthLens | undefined) => void>();
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Register a listener for auth lens mutations.
|
|
313
|
+
* Returns an unsubscribe function that removes the listener.
|
|
314
|
+
*/
|
|
315
|
+
export function subscribeLens(listener: (lens: AuthLens | undefined) => void): () => void {
|
|
316
|
+
lensListeners.add(listener);
|
|
317
|
+
return () => {
|
|
318
|
+
lensListeners.delete(listener);
|
|
319
|
+
};
|
|
320
|
+
}
|
|
269
321
|
|
|
270
322
|
/**
|
|
271
323
|
* Set the default auth lens applied to subsequent Firestore DATA ops from this
|
|
272
|
-
* client (Pyric Studio). Pass `{ mode: 'as', uid }` to read/write AS a user
|
|
324
|
+
* client (Pyric Studio & Runtime Chip). Pass `{ mode: 'as', uid }` to read/write AS a user
|
|
273
325
|
* (rules apply), `{ mode: 'admin' }` for the admin lens, or
|
|
274
326
|
* `{ mode: 'app-session' }` / `undefined` to revert to the app's own session.
|
|
275
327
|
*
|
|
276
328
|
* The lens is process-wide for this client module (one served page = one
|
|
277
329
|
* worker port), mirroring how Studio drives a single active identity at a time.
|
|
278
330
|
* Auth ops are unaffected — they always operate the real session.
|
|
331
|
+
*
|
|
332
|
+
* Persists active admin and impersonation lenses to sessionStorage under
|
|
333
|
+
* AUTH_LENS_STORAGE_KEY, and removes the key when cleared or reverted to the
|
|
334
|
+
* app session.
|
|
335
|
+
* Notifies all registered subscribeLens listeners.
|
|
279
336
|
*/
|
|
280
337
|
export function setLens(lens: AuthLens | undefined): void {
|
|
281
|
-
_defaultLens = lens
|
|
338
|
+
_defaultLens = normalizePersistedAuthLens(lens);
|
|
339
|
+
|
|
340
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
341
|
+
try {
|
|
342
|
+
if (_defaultLens) {
|
|
343
|
+
sessionStorage.setItem(AUTH_LENS_STORAGE_KEY, JSON.stringify(_defaultLens));
|
|
344
|
+
} else {
|
|
345
|
+
sessionStorage.removeItem(AUTH_LENS_STORAGE_KEY);
|
|
346
|
+
}
|
|
347
|
+
} catch {
|
|
348
|
+
// Ignore quota or disabled storage restrictions
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
for (const listener of [...lensListeners]) {
|
|
353
|
+
try {
|
|
354
|
+
listener(_defaultLens);
|
|
355
|
+
} catch (err) {
|
|
356
|
+
console.error('Error in subscribeLens listener:', err);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
282
359
|
}
|
|
283
360
|
|
|
284
361
|
/** The active default lens (read-only view), for Studio UI to reflect state. */
|
|
@@ -15,7 +15,7 @@ function deleteTimeoutError(): Error & { code: string } {
|
|
|
15
15
|
return Object.assign(
|
|
16
16
|
new Error(
|
|
17
17
|
'Timed out waiting for SharedWorker app cleanup acknowledgment. ' +
|
|
18
|
-
'Close all tabs using this pyric
|
|
18
|
+
'Close all tabs using this pyric sandbox origin so the worker can restart.',
|
|
19
19
|
),
|
|
20
20
|
{ code: 'app/delete-timeout' },
|
|
21
21
|
);
|
|
@@ -25,9 +25,29 @@ import type { ClientDb, Unsubscribe } from './handles.js';
|
|
|
25
25
|
export type { PresenceClientKind, PresenceSnapshot, PresenceVisibility };
|
|
26
26
|
export { PRESENCE_HEARTBEAT_INTERVAL_MS, PRESENCE_STALE_MS };
|
|
27
27
|
|
|
28
|
+
function supportsRandomUUID(): boolean {
|
|
29
|
+
return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function supportsDocumentEventListeners(): boolean {
|
|
33
|
+
return (
|
|
34
|
+
typeof document !== 'undefined' &&
|
|
35
|
+
typeof document.addEventListener === 'function' &&
|
|
36
|
+
typeof document.removeEventListener === 'function'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function supportsWindowEventListeners(): boolean {
|
|
41
|
+
return (
|
|
42
|
+
typeof window !== 'undefined' &&
|
|
43
|
+
typeof window.addEventListener === 'function' &&
|
|
44
|
+
typeof window.removeEventListener === 'function'
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
28
48
|
/** Mint a random client id (page-lifetime). */
|
|
29
49
|
export function mintPresenceClientId(): string {
|
|
30
|
-
if (
|
|
50
|
+
if (supportsRandomUUID()) {
|
|
31
51
|
return crypto.randomUUID();
|
|
32
52
|
}
|
|
33
53
|
return `page-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -114,10 +134,10 @@ export function startPresence(opts: StartPresenceOptions): PresenceSession {
|
|
|
114
134
|
|
|
115
135
|
register();
|
|
116
136
|
|
|
117
|
-
if (
|
|
137
|
+
if (supportsDocumentEventListeners()) {
|
|
118
138
|
document.addEventListener('visibilitychange', update);
|
|
119
139
|
}
|
|
120
|
-
if (
|
|
140
|
+
if (supportsWindowEventListeners()) {
|
|
121
141
|
window.addEventListener('popstate', update);
|
|
122
142
|
window.addEventListener('hashchange', update);
|
|
123
143
|
// Best-effort clean disconnect; lease expiry covers unclean shutdown.
|
|
@@ -139,10 +159,10 @@ export function startPresence(opts: StartPresenceOptions): PresenceSession {
|
|
|
139
159
|
clearInterval(timer);
|
|
140
160
|
timer = null;
|
|
141
161
|
}
|
|
142
|
-
if (
|
|
162
|
+
if (supportsDocumentEventListeners()) {
|
|
143
163
|
document.removeEventListener('visibilitychange', update);
|
|
144
164
|
}
|
|
145
|
-
if (
|
|
165
|
+
if (supportsWindowEventListeners()) {
|
|
146
166
|
window.removeEventListener('popstate', update);
|
|
147
167
|
window.removeEventListener('hashchange', update);
|
|
148
168
|
window.removeEventListener('pagehide', onPageHide);
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
// Shared core: lens/issuer controls + the handle and snapshot types.
|
|
29
|
-
export { setLens, getLens, setOpIssuer } from './client/core.js';
|
|
29
|
+
export { setLens, getLens, subscribeLens, AUTH_LENS_STORAGE_KEY, hydrateLensFromStorage, setOpIssuer } from './client/core.js';
|
|
30
30
|
export type {
|
|
31
31
|
ClientDb,
|
|
32
32
|
ClientRtdb,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SharedWorker context resolver.
|
|
3
|
+
*
|
|
4
|
+
* Coordinates asynchronous initialization of a singleton resource across
|
|
5
|
+
* concurrent inbound connection requests or burst messages.
|
|
6
|
+
*
|
|
7
|
+
* Invariants:
|
|
8
|
+
* 1. Single In-Flight Attempt: When multiple callers request the context
|
|
9
|
+
* concurrently while initialization is in flight, all callers share the
|
|
10
|
+
* single in-flight promise rather than starting parallel initializations.
|
|
11
|
+
* 2. Unpoisoned on Rejection: If initialization rejects, the memoized promise
|
|
12
|
+
* is cleared immediately, allowing subsequent requests to retry initialization
|
|
13
|
+
* rather than permanently caching the rejection.
|
|
14
|
+
* 3. Synchronous Retrieval: Once successfully resolved, the resource is
|
|
15
|
+
* available synchronously for shutdown and cleanup handlers.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export interface ContextResolver<T> {
|
|
19
|
+
/**
|
|
20
|
+
* Return the resolved resource or the in-flight initialization promise.
|
|
21
|
+
* If a previous attempt rejected, starts a new attempt.
|
|
22
|
+
*/
|
|
23
|
+
get(): Promise<T>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Return the successfully resolved resource, or null if not yet resolved
|
|
27
|
+
* or currently in-flight.
|
|
28
|
+
*/
|
|
29
|
+
current(): T | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createContextResolver<T>(factory: () => Promise<T>): ContextResolver<T> {
|
|
33
|
+
let value: T | null = null;
|
|
34
|
+
let inFlight: Promise<T> | null = null;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
async get(): Promise<T> {
|
|
38
|
+
if (value !== null) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
if (inFlight === null) {
|
|
42
|
+
inFlight = factory()
|
|
43
|
+
.then((resolved) => {
|
|
44
|
+
value = resolved;
|
|
45
|
+
return resolved;
|
|
46
|
+
})
|
|
47
|
+
.catch((error: unknown) => {
|
|
48
|
+
inFlight = null;
|
|
49
|
+
value = null;
|
|
50
|
+
throw error;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return inFlight;
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
current(): T | null {
|
|
57
|
+
return value;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -67,36 +67,33 @@ import {
|
|
|
67
67
|
} from './service-worker-channel.js';
|
|
68
68
|
import { createServiceWorkerRelay } from './service-worker-relay.js';
|
|
69
69
|
import { createWorkerRetirement } from './retirement.js';
|
|
70
|
+
import { createContextResolver } from './context-resolver.js';
|
|
71
|
+
import { createPortLifecycleManager } from './port-lifecycle.js';
|
|
70
72
|
|
|
71
73
|
declare const __PYRIC_WORKER_VERSION__: string;
|
|
72
74
|
const workerEpoch = typeof __PYRIC_WORKER_VERSION__ !== 'undefined'
|
|
73
75
|
? __PYRIC_WORKER_VERSION__
|
|
74
76
|
: 'dev';
|
|
75
77
|
const workerScope = self as unknown as SharedWorkerGlobalScope;
|
|
78
|
+
const contextResolver = createContextResolver(buildCtx);
|
|
79
|
+
const portLifecycle = createPortLifecycleManager();
|
|
76
80
|
const retirement = createWorkerRetirement({
|
|
77
81
|
closeWorker: () => workerScope.close(),
|
|
78
|
-
beforeAnnounce: async () => { await
|
|
82
|
+
beforeAnnounce: async () => { await contextResolver.current()?.captureFlush?.(); },
|
|
79
83
|
});
|
|
80
84
|
|
|
81
85
|
// ─── Singleton context ────────────────────────────────────────────────────
|
|
82
86
|
|
|
83
|
-
// `_ctx` is the RESOLVED context, kept for the synchronous `close` handler.
|
|
84
|
-
// The build is memoized as a PROMISE (`_ctxPromise`) — NOT as the resolved
|
|
85
|
-
// value — because init is async (it awaits `/__pyric/init.json` + persistence
|
|
86
|
-
// restore). The first messages arrive in a BURST (the authState sub, the first
|
|
87
|
-
// firestore sub, an op — possibly across two tabs at once); if each re-checked
|
|
88
|
-
// only the resolved `_ctx` they would every one start a fresh init and build a
|
|
89
|
-
// SEPARATE sandbox, so a listener and a write could bind to different
|
|
90
|
-
// instances and never see each other. The promise memo guarantees ONE init.
|
|
91
|
-
let _ctx: HostCtx | null = null;
|
|
92
|
-
let _ctxPromise: Promise<HostCtx> | null = null;
|
|
93
|
-
|
|
94
87
|
/**
|
|
95
88
|
* Return the shared context, memoizing the in-flight init promise so
|
|
96
|
-
* concurrent first messages await ONE build
|
|
89
|
+
* concurrent first messages await ONE build. If initialization rejects,
|
|
90
|
+
* the rejection is cleared so subsequent connection attempts can retry.
|
|
91
|
+
* Any ports that closed while initialization was in flight are drained immediately.
|
|
97
92
|
*/
|
|
98
|
-
function getCtx(): Promise<HostCtx> {
|
|
99
|
-
|
|
93
|
+
async function getCtx(): Promise<HostCtx> {
|
|
94
|
+
const ctx = await contextResolver.get();
|
|
95
|
+
portLifecycle.drainClosedPorts(ctx);
|
|
96
|
+
return ctx;
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
/**
|
|
@@ -125,7 +122,6 @@ async function buildCtx(): Promise<HostCtx> {
|
|
|
125
122
|
? (url) => new EventSource(url) as unknown as EventSourceLike
|
|
126
123
|
: null,
|
|
127
124
|
});
|
|
128
|
-
_ctx = ctx; // publish the resolved ctx for the synchronous close handler
|
|
129
125
|
return ctx;
|
|
130
126
|
}
|
|
131
127
|
|
|
@@ -141,6 +137,7 @@ workerScope.onconnect = (e: MessageEvent) => {
|
|
|
141
137
|
const port = e.ports[0];
|
|
142
138
|
port.start();
|
|
143
139
|
retirement.connect(port);
|
|
140
|
+
void getCtx().catch(() => undefined);
|
|
144
141
|
|
|
145
142
|
let messageQueue = Promise.resolve();
|
|
146
143
|
port.onmessage = (ev: MessageEvent<InboundMessage>) => {
|
|
@@ -172,9 +169,16 @@ workerScope.onconnect = (e: MessageEvent) => {
|
|
|
172
169
|
// cannot overtake an already-posted mutation, and later frames see the
|
|
173
170
|
// disconnected-port tombstone instead of touching the shared backend.
|
|
174
171
|
messageQueue = messageQueue.then(async () => {
|
|
172
|
+
const portLike = port as unknown as PortLike;
|
|
173
|
+
if (portLifecycle.isPortClosed(portLike)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
175
176
|
try {
|
|
176
177
|
const ctx = await getCtx();
|
|
177
|
-
|
|
178
|
+
if (portLifecycle.isPortClosed(portLike)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
await handleMessage(ctx, portLike, ev.data);
|
|
178
182
|
} catch (e) {
|
|
179
183
|
// eslint-disable-next-line no-console
|
|
180
184
|
console.error('[pyric worker] message handler error:', (e as Error)?.stack ?? e, 'msg:', ev.data);
|
|
@@ -188,7 +192,7 @@ workerScope.onconnect = (e: MessageEvent) => {
|
|
|
188
192
|
// best-effort; subscriptions also GC when the worker itself dies.
|
|
189
193
|
port.addEventListener('close', () => {
|
|
190
194
|
retirement.disconnect(port);
|
|
191
|
-
|
|
195
|
+
portLifecycle.onPortClosed(port as unknown as PortLike, contextResolver.current());
|
|
192
196
|
});
|
|
193
197
|
};
|
|
194
198
|
|