@pyric/cli 0.1.0-alpha.11 → 0.1.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/serve.js +4 -4
- package/dist/cli/serve.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 +2 -2
- package/dist/conformance/.generated/can-i-use.js.map +1 -1
- package/dist/serve/bundler.d.ts +13 -8
- package/dist/serve/bundler.d.ts.map +1 -1
- package/dist/serve/bundler.js +31 -36
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/docs-ui/docs/_rhythm/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +1 -1
- package/dist/serve/docs-ui/docs/ai-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/api-reference/index.html +2 -2
- package/dist/serve/docs-ui/docs/api-reference.md +1 -1
- package/dist/serve/docs-ui/docs/app-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/auth-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/ai-logic/index.html +44 -22
- package/dist/serve/docs-ui/docs/build/ai-logic.md +69 -21
- package/dist/serve/docs-ui/docs/build/authentication/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/realtime-database/index.html +1 -1
- package/dist/serve/docs-ui/docs/conformance-scores/index.html +1 -1
- package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/database-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/firestore-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
- package/dist/serve/docs-ui/docs/get-started/how-the-swap-works.md +2 -0
- package/dist/serve/docs-ui/docs/get-started/start-building/index.html +3 -2
- package/dist/serve/docs-ui/docs/get-started/start-building.md +3 -1
- package/dist/serve/docs-ui/docs/index.json +349 -29
- package/dist/serve/docs-ui/docs/messaging-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/observe/resolve-runtime-status/index.html +28 -0
- package/dist/serve/docs-ui/docs/observe/resolve-runtime-status.md +47 -0
- package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +1 -1
- package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +1 -1
- package/dist/serve/docs-ui/docs/overview/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
- package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
- package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +168 -76
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +156 -73
- package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +11 -8
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +17 -4
- package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +37 -14
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +28 -8
- package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/rules-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
- package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +1 -1
- package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +1 -1
- package/dist/serve/docs-ui/docs/storage-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
- package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
- package/dist/serve/docs-ui/docs/tutorial/index.html +429 -0
- package/dist/serve/docs-ui/docs/tutorial.md +701 -0
- package/dist/serve/docs-ui/llms.txt +3 -1
- package/dist/serve/entries/init.d.ts.map +1 -1
- package/dist/serve/entries/init.js +3 -0
- package/dist/serve/entries/init.js.map +1 -1
- package/dist/serve/entries/runtime.d.ts.map +1 -1
- package/dist/serve/entries/runtime.js +20 -5
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/entries/worker-runtime.d.ts +2 -2
- package/dist/serve/entries/worker-runtime.d.ts.map +1 -1
- package/dist/serve/entries/worker-runtime.js +45 -10
- package/dist/serve/entries/worker-runtime.js.map +1 -1
- package/dist/serve/runtime/chip-config.d.ts +19 -0
- package/dist/serve/runtime/chip-config.d.ts.map +1 -0
- package/dist/serve/runtime/chip-config.js +19 -0
- package/dist/serve/runtime/chip-config.js.map +1 -0
- package/dist/serve/runtime/chip-install.d.ts +10 -0
- package/dist/serve/runtime/chip-install.d.ts.map +1 -0
- package/dist/serve/runtime/chip-install.js +15 -0
- package/dist/serve/runtime/chip-install.js.map +1 -0
- package/dist/serve/runtime/chip.d.ts +17 -0
- package/dist/serve/runtime/chip.d.ts.map +1 -0
- package/dist/serve/runtime/chip.js +257 -0
- package/dist/serve/runtime/chip.js.map +1 -0
- package/dist/serve/runtime/manifest.d.ts +22 -0
- package/dist/serve/runtime/manifest.d.ts.map +1 -0
- package/dist/serve/runtime/manifest.js +20 -0
- package/dist/serve/runtime/manifest.js.map +1 -0
- package/dist/serve/runtime/status.d.ts +49 -0
- package/dist/serve/runtime/status.d.ts.map +1 -0
- package/dist/serve/runtime/status.js +164 -0
- package/dist/serve/runtime/status.js.map +1 -0
- package/dist/serve/runtime/worker-connection.d.ts +4 -0
- package/dist/serve/runtime/worker-connection.d.ts.map +1 -0
- package/dist/serve/runtime/worker-connection.js +11 -0
- package/dist/serve/runtime/worker-connection.js.map +1 -0
- package/dist/serve/runtime/worker-generation.d.ts +14 -0
- package/dist/serve/runtime/worker-generation.d.ts.map +1 -0
- package/dist/serve/runtime/worker-generation.js +56 -0
- package/dist/serve/runtime/worker-generation.js.map +1 -0
- package/dist/serve/runtime/worker-replacement.d.ts +18 -0
- package/dist/serve/runtime/worker-replacement.d.ts.map +1 -0
- package/dist/serve/runtime/worker-replacement.js +29 -0
- package/dist/serve/runtime/worker-replacement.js.map +1 -0
- package/dist/serve/standalone-assets.d.ts +2 -2
- package/dist/serve/standalone-assets.d.ts.map +1 -1
- package/dist/serve/standalone-assets.js +1 -1
- package/dist/serve/standalone-assets.js.map +1 -1
- package/dist/serve/studio-ui/assets/{RulesCodeEditor-CxVYcZXw.js → RulesCodeEditor-BDlH-5lp.js} +1 -1
- package/dist/serve/studio-ui/assets/{index-cpSfgQ7H.js → index-Bf9SSE8L.js} +5 -5
- package/dist/serve/studio-ui/assets/{seed-DeuFKi4J.js → seed-D_UjxfOU.js} +1 -1
- package/dist/serve/studio-ui/index.html +1 -1
- package/dist/serve/vite-ai-config.d.ts +25 -0
- package/dist/serve/vite-ai-config.d.ts.map +1 -0
- package/dist/serve/vite-ai-config.js +45 -0
- package/dist/serve/vite-ai-config.js.map +1 -0
- package/dist/serve/vite-plugin.d.ts +17 -36
- package/dist/serve/vite-plugin.d.ts.map +1 -1
- package/dist/serve/vite-plugin.js +38 -57
- package/dist/serve/vite-plugin.js.map +1 -1
- package/dist/serve/vite-rules-source.d.ts +8 -0
- package/dist/serve/vite-rules-source.d.ts.map +1 -0
- package/dist/serve/vite-rules-source.js +23 -0
- package/dist/serve/vite-rules-source.js.map +1 -0
- package/dist/serve/vite-worker-runtime.d.ts +20 -0
- package/dist/serve/vite-worker-runtime.d.ts.map +1 -0
- package/dist/serve/vite-worker-runtime.js +25 -0
- package/dist/serve/vite-worker-runtime.js.map +1 -0
- package/dist/serve/worker/client/connection.d.ts +7 -2
- package/dist/serve/worker/client/connection.d.ts.map +1 -1
- package/dist/serve/worker/client/connection.js +13 -16
- package/dist/serve/worker/client/connection.js.map +1 -1
- package/dist/serve/worker/client/core.d.ts +7 -0
- package/dist/serve/worker/client/core.d.ts.map +1 -1
- package/dist/serve/worker/client/core.js +28 -0
- package/dist/serve/worker/client/core.js.map +1 -1
- package/dist/serve/worker/client/runtime-control.d.ts +8 -0
- package/dist/serve/worker/client/runtime-control.d.ts.map +1 -0
- package/dist/serve/worker/client/runtime-control.js +11 -0
- package/dist/serve/worker/client/runtime-control.js.map +1 -0
- package/dist/serve/worker/entry.js +44 -2
- package/dist/serve/worker/entry.js.map +1 -1
- package/dist/serve/worker/host/core.js +1 -1
- package/dist/serve/worker/host/core.js.map +1 -1
- package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
- package/dist/serve/worker/host/firestore-writes.js +3 -2
- package/dist/serve/worker/host/firestore-writes.js.map +1 -1
- package/dist/serve/worker/host/studio.js +1 -1
- package/dist/serve/worker/host/studio.js.map +1 -1
- package/dist/serve/worker/host-context.d.ts +1 -1
- package/dist/serve/worker/host-context.d.ts.map +1 -1
- package/dist/serve/worker/index.d.ts +2 -1
- package/dist/serve/worker/index.d.ts.map +1 -1
- package/dist/serve/worker/index.js +2 -1
- package/dist/serve/worker/index.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +15 -1
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js +2 -2
- package/dist/serve/worker/protocol.js.map +1 -1
- package/dist/serve/worker/retirement.d.ts +21 -0
- package/dist/serve/worker/retirement.d.ts.map +1 -0
- package/dist/serve/worker/retirement.js +97 -0
- package/dist/serve/worker/retirement.js.map +1 -0
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +5 -6
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/dist/vite.d.ts +1 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js.map +1 -1
- package/package.json +6 -4
- package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
- package/src/assurance/attachment.ts +211 -0
- package/src/assurance/browser.ts +80 -0
- package/src/assurance/campaign.ts +496 -0
- package/src/assurance/capabilities.ts +446 -0
- package/src/assurance/cases.ts +43 -0
- package/src/assurance/index.ts +75 -0
- package/src/assurance/runner.ts +885 -0
- package/src/assurance/tool-names.ts +15 -0
- package/src/assurance/tools.ts +830 -0
- package/src/assurance/types.ts +352 -0
- package/src/assurance/validation.ts +434 -0
- package/src/bridge/client/bridge.ts +550 -0
- package/src/bridge/client/dispatch.ts +137 -0
- package/src/bridge/client.ts +45 -0
- package/src/bridge/protocol.ts +351 -0
- package/src/bridge/server/audit.ts +53 -0
- package/src/bridge/server/bridge.ts +597 -0
- package/src/bridge/server/headless.ts +160 -0
- package/src/bridge/server/json-schema-to-zod.ts +108 -0
- package/src/bridge/server/local-bridge.ts +66 -0
- package/src/bridge/server/logger.ts +50 -0
- package/src/bridge/server/mcp-contract.ts +93 -0
- package/src/bridge/server/mcp.ts +129 -0
- package/src/bridge/server/peer.ts +232 -0
- package/src/bridge/server/standalone.ts +347 -0
- package/src/bridge/server/tool-metadata.ts +99 -0
- package/src/bridge/server.ts +28 -0
- package/src/cli/can-i-use.ts +50 -0
- package/src/cli/cli.test.ts +493 -0
- package/src/cli/database-rules.ts +329 -0
- package/src/cli/dev-runner.ts +282 -0
- package/src/cli/firebase-json.ts +100 -0
- package/src/cli/firestore-indexes.ts +70 -0
- package/src/cli/index.ts +425 -0
- package/src/cli/init.ts +260 -0
- package/src/cli/mcp-proxy.ts +196 -0
- package/src/cli/parse-args.ts +80 -0
- package/src/cli/rules.ts +259 -0
- package/src/cli/scope.ts +67 -0
- package/src/cli/serve.ts +996 -0
- package/src/cli/service-commands.ts +73 -0
- package/src/cli/snapshot.ts +164 -0
- package/src/cli/storage-rules.ts +172 -0
- package/src/cli/verify.ts +416 -0
- package/src/conformance/.generated/can-i-use-browser.ts +113 -0
- package/src/conformance/.generated/can-i-use.ts +117 -0
- package/src/conformance/.generated/conformance-docs.ts +14 -0
- package/src/conformance/browser.ts +24 -0
- package/src/conformance/can-i-use-tool.ts +51 -0
- package/src/conformance/can-i-use.ts +18 -0
- package/src/conformance/docs.ts +8 -0
- package/src/conformance/index.ts +20 -0
- package/src/conformance/tools.ts +16 -0
- package/src/credentials/core/memoize-ttl.ts +142 -0
- package/src/credentials/core/types.ts +10 -0
- package/src/credentials/node/from-adc.ts +96 -0
- package/src/credentials/node/from-service-account.ts +133 -0
- package/src/credentials/node/index.ts +6 -0
- package/src/discover/concurrency.ts +127 -0
- package/src/discover/crawler-adapter.ts +142 -0
- package/src/discover/crawler.ts +1127 -0
- package/src/discover/credential-free.ts +27 -0
- package/src/discover/findCollectionGroup.ts +131 -0
- package/src/discover/firestore-source.ts +59 -0
- package/src/discover/index.ts +5 -0
- package/src/discover/merge.ts +523 -0
- package/src/discover/session.ts +402 -0
- package/src/discover/tools.ts +201 -0
- package/src/discover/types.ts +187 -0
- package/src/discover/wire.ts +324 -0
- package/src/functions-rtdb/child.ts +382 -0
- package/src/functions-rtdb/delivery.ts +7 -0
- package/src/functions-rtdb/discovery.ts +136 -0
- package/src/functions-rtdb/event.ts +64 -0
- package/src/functions-rtdb/execution.ts +111 -0
- package/src/functions-rtdb/in-memory-delivery.ts +40 -0
- package/src/functions-rtdb/project.ts +74 -0
- package/src/functions-rtdb/projection.ts +104 -0
- package/src/functions-rtdb/reference-pattern.ts +20 -0
- package/src/functions-rtdb/remote-delivery.ts +23 -0
- package/src/pkg-version.ts +49 -0
- package/src/register/esm-exports.ts +63 -0
- package/src/register/hooks.ts +30 -0
- package/src/register/index.ts +150 -0
- package/src/register/mapping.ts +33 -0
- package/src/remote/index.ts +1050 -0
- package/src/rtdb/crawl-snapshot.ts +118 -0
- package/src/rtdb/inspection.ts +137 -0
- package/src/rtdb/load-rules-document.ts +56 -0
- package/src/rtdb/rules-generation-tool.ts +38 -0
- package/src/rtdb/rules-json.ts +29 -0
- package/src/serve/activity-guard.ts +29 -0
- package/src/serve/activity-route.ts +164 -0
- package/src/serve/activity-warning.ts +26 -0
- package/src/serve/bridge-mount.ts +200 -0
- package/src/serve/bundler.ts +519 -0
- package/src/serve/capture-store.ts +65 -0
- package/src/serve/discovery.ts +199 -0
- package/src/serve/entries/ai.ts +292 -0
- package/src/serve/entries/app-backend.ts +4 -0
- package/src/serve/entries/app-client.ts +26 -0
- package/src/serve/entries/app-session-store.ts +47 -0
- package/src/serve/entries/app.ts +11 -0
- package/src/serve/entries/auth-helper-core.ts +181 -0
- package/src/serve/entries/auth-helper-dom.ts +134 -0
- package/src/serve/entries/auth-helper-runtime.ts +20 -0
- package/src/serve/entries/auth.ts +255 -0
- package/src/serve/entries/bridge-url.ts +31 -0
- package/src/serve/entries/database.ts +129 -0
- package/src/serve/entries/firestore.ts +232 -0
- package/src/serve/entries/init.ts +49 -0
- package/src/serve/entries/keepalive.ts +46 -0
- package/src/serve/entries/messaging-sw.ts +75 -0
- package/src/serve/entries/messaging.ts +96 -0
- package/src/serve/entries/runtime.ts +556 -0
- package/src/serve/entries/session-store.ts +97 -0
- package/src/serve/entries/storage.ts +92 -0
- package/src/serve/entries/tab-sync-wiring.ts +274 -0
- package/src/serve/entries/worker-runtime.ts +135 -0
- package/src/serve/init-payload.ts +42 -0
- package/src/serve/namespace.ts +675 -0
- package/src/serve/open-browser.ts +68 -0
- package/src/serve/rules.ts +272 -0
- package/src/serve/runtime/chip-config.ts +30 -0
- package/src/serve/runtime/chip-install.ts +27 -0
- package/src/serve/runtime/chip.ts +282 -0
- package/src/serve/runtime/manifest.ts +37 -0
- package/src/serve/runtime/status.ts +216 -0
- package/src/serve/runtime/worker-connection.ts +14 -0
- package/src/serve/runtime/worker-generation.ts +75 -0
- package/src/serve/runtime/worker-replacement.ts +42 -0
- package/src/serve/sandbox-marker.ts +31 -0
- package/src/serve/server.ts +428 -0
- package/src/serve/standalone-assets.ts +180 -0
- package/src/serve/state-store.ts +166 -0
- package/src/serve/studio/disk-project-store.ts +185 -0
- package/src/serve/studio/disk-workspace.ts +162 -0
- package/src/serve/studio/index.ts +20 -0
- package/src/serve/studio/routes.ts +237 -0
- package/src/serve/studio/store-types.ts +48 -0
- package/src/serve/studio/studio-storage.test.ts +289 -0
- package/src/serve/vite-ai-config.ts +77 -0
- package/src/serve/vite-plugin.ts +1075 -0
- package/src/serve/vite-rules-source.ts +28 -0
- package/src/serve/vite-worker-runtime.ts +51 -0
- package/src/serve/worker/activity-bootstrap.ts +24 -0
- package/src/serve/worker/client/admin-firestore.ts +38 -0
- package/src/serve/worker/client/ai.ts +124 -0
- package/src/serve/worker/client/auth.ts +489 -0
- package/src/serve/worker/client/connection.ts +227 -0
- package/src/serve/worker/client/core.ts +384 -0
- package/src/serve/worker/client/disconnect.ts +45 -0
- package/src/serve/worker/client/firestore-reads.ts +181 -0
- package/src/serve/worker/client/firestore-refs.ts +226 -0
- package/src/serve/worker/client/firestore-writes.ts +232 -0
- package/src/serve/worker/client/handles.ts +91 -0
- package/src/serve/worker/client/messaging.ts +119 -0
- package/src/serve/worker/client/presence.ts +174 -0
- package/src/serve/worker/client/rtdb.ts +311 -0
- package/src/serve/worker/client/rules.ts +63 -0
- package/src/serve/worker/client/runtime-control.ts +22 -0
- package/src/serve/worker/client/service-worker-connection.ts +59 -0
- package/src/serve/worker/client/snapshots.ts +86 -0
- package/src/serve/worker/client/storage.ts +195 -0
- package/src/serve/worker/client/studio.ts +88 -0
- package/src/serve/worker/client.ts +57 -0
- package/src/serve/worker/durable-persistence.ts +137 -0
- package/src/serve/worker/entry.ts +223 -0
- package/src/serve/worker/host/admin-firestore.ts +88 -0
- package/src/serve/worker/host/connection.ts +179 -0
- package/src/serve/worker/host/core.ts +387 -0
- package/src/serve/worker/host/dispatch.ts +316 -0
- package/src/serve/worker/host/firestore-reads.ts +117 -0
- package/src/serve/worker/host/firestore-writes.ts +456 -0
- package/src/serve/worker/host/presence.ts +312 -0
- package/src/serve/worker/host/rtdb.ts +136 -0
- package/src/serve/worker/host/rules.ts +128 -0
- package/src/serve/worker/host/storage.ts +312 -0
- package/src/serve/worker/host/studio.ts +75 -0
- package/src/serve/worker/host/subscriptions.ts +238 -0
- package/src/serve/worker/host-ai.ts +165 -0
- package/src/serve/worker/host-auth.ts +469 -0
- package/src/serve/worker/host-context.ts +273 -0
- package/src/serve/worker/host-events.ts +94 -0
- package/src/serve/worker/host-messaging.ts +240 -0
- package/src/serve/worker/host.ts +47 -0
- package/src/serve/worker/index.ts +196 -0
- package/src/serve/worker/presence-timing.ts +15 -0
- package/src/serve/worker/protocol.ts +1124 -0
- package/src/serve/worker/retirement.ts +125 -0
- package/src/serve/worker/serve-init.ts +605 -0
- package/src/serve/worker/service-worker-channel.ts +33 -0
- package/src/serve/worker/service-worker-relay.ts +86 -0
- package/src/serve/writer-lock.ts +53 -0
- package/src/verify/cases.ts +233 -0
- package/src/verify/fixture.ts +258 -0
- package/src/verify/index.ts +519 -0
- package/src/verify/tools.ts +108 -0
- package/src/version/compat-target.ts +17 -0
- package/src/vite.ts +18 -0
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -31
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -29
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pyric dev` SDK bundler — builds the browser-standalone ESM bundles the
|
|
3
|
+
* import map points `firebase/*` at (`/__pyric/sdk/*.js`).
|
|
4
|
+
*
|
|
5
|
+
* esbuild (a real dependency — the `pyric` bin runs under node via npx, so
|
|
6
|
+
* Bun.build is unavailable) bundles the wrapper entries in `entries/` with
|
|
7
|
+
* SPLITTING so the shared runtime chunk holds exactly ONE sandbox per page:
|
|
8
|
+
* both the auth and firestore bundles must close over the same instance.
|
|
9
|
+
*
|
|
10
|
+
* Two bundler plugins make pyric browser-standalone:
|
|
11
|
+
* - `pyric/*` resolver — resolves `pyric/...` specifiers from THIS package's
|
|
12
|
+
* dependency context (entries may live in a temp dir that lacks pyric).
|
|
13
|
+
* - node-builtin shims (`url`/`fs`/`path`) — still load-bearing despite the
|
|
14
|
+
* Phase U resolver split: `pyric/firestore` re-exports the agent tool
|
|
15
|
+
* factories (`firestore/index.js` → `rules/tools.js`), and tools.js
|
|
16
|
+
* statically imports the NODE module resolver. The browser path never
|
|
17
|
+
* calls into disk reads, so benign shims suffice. Droppable only when
|
|
18
|
+
* pyric breaks that static chain (tracked in #553).
|
|
19
|
+
*
|
|
20
|
+
* Output is cached under `~/.pyric/serve-cache/<pyric-version>-<hash>/` so a
|
|
21
|
+
* warm `pyric dev` start skips the (~seconds) bundle step. `--no-cache`
|
|
22
|
+
* bypasses.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from 'node:crypto';
|
|
25
|
+
import { mkdirSync, mkdtempSync, readFileSync, readdirSync, existsSync, rmSync, writeFileSync } from 'node:fs';
|
|
26
|
+
import { homedir } from 'node:os';
|
|
27
|
+
import { dirname, join, relative, sep } from 'node:path';
|
|
28
|
+
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import * as esbuild from 'esbuild';
|
|
30
|
+
|
|
31
|
+
/** Modules the import map serves. Keys are the bare specifiers app code uses. */
|
|
32
|
+
export const SDK_MODULES = [
|
|
33
|
+
'firebase/ai',
|
|
34
|
+
'firebase/app',
|
|
35
|
+
'firebase/auth',
|
|
36
|
+
'firebase/firestore',
|
|
37
|
+
'firebase/database',
|
|
38
|
+
'firebase/messaging',
|
|
39
|
+
'firebase/messaging/sw',
|
|
40
|
+
'firebase/storage',
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The wrapper entries shipped with @pyric/cli, located relative to this
|
|
45
|
+
* module: compiled `.js` siblings when running from dist (npx install), `.ts`
|
|
46
|
+
* sources in the workspace (tests / dev). esbuild bundles either.
|
|
47
|
+
*/
|
|
48
|
+
export function defaultSdkEntries(): Record<string, string> {
|
|
49
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
50
|
+
const pick = (name: string): string => {
|
|
51
|
+
const js = join(here, 'entries', `${name}.js`);
|
|
52
|
+
if (existsSync(js)) return js;
|
|
53
|
+
const ts = join(here, 'entries', `${name}.ts`);
|
|
54
|
+
if (existsSync(ts)) return ts;
|
|
55
|
+
throw new Error(`pyric dev: missing SDK entry '${name}' next to ${here}`);
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
ai: pick('ai'),
|
|
59
|
+
app: pick('app'),
|
|
60
|
+
auth: pick('auth'),
|
|
61
|
+
firestore: pick('firestore'),
|
|
62
|
+
database: pick('database'),
|
|
63
|
+
messaging: pick('messaging'),
|
|
64
|
+
'messaging-sw': pick('messaging-sw'),
|
|
65
|
+
storage: pick('storage'),
|
|
66
|
+
init: pick('init'),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the built Studio app dir (the plugin's `ui` option + the CLI's
|
|
72
|
+
* `--ui`). Reached by file path so @pyric/cli never imports `@pyric/studio`:
|
|
73
|
+
* the packaged location (`dist/serve/studio-ui`, copied at build) or, in the
|
|
74
|
+
* monorepo, the sibling studio build. Null when neither exists. The standalone
|
|
75
|
+
* binary embeds these same bytes instead (see `standalone-assets.ts`).
|
|
76
|
+
*/
|
|
77
|
+
export function resolveStudioUiDir(): string | null {
|
|
78
|
+
const candidates = [
|
|
79
|
+
new URL('./studio-ui/', import.meta.url),
|
|
80
|
+
new URL('../../../studio/dist/app/', import.meta.url),
|
|
81
|
+
];
|
|
82
|
+
for (const candidate of candidates) {
|
|
83
|
+
const dir = fileURLToPath(candidate);
|
|
84
|
+
if (existsSync(dir)) return dir;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the built docs site dir (site-docs). `pyric dev --ui` embeds it so
|
|
91
|
+
* the Studio Docs tab has local docs without the hosted site. Built with base
|
|
92
|
+
* `/__pyric/ui/` (see scripts/build.sh) so every page/asset/twin URL resolves
|
|
93
|
+
* under the CLI mount: pages at `/__pyric/ui/docs/<slug>/`, assets at
|
|
94
|
+
* `/__pyric/ui/_astro/*`, the search index at `/__pyric/ui/docs/index.json`,
|
|
95
|
+
* and the shell chrome's tab links back at `/__pyric/ui/<tab>`. The dir holds
|
|
96
|
+
* the site-docs `dist/` verbatim: `docs/`, `_astro/`, `index.html`, `llms.txt`.
|
|
97
|
+
*/
|
|
98
|
+
export function resolveDocsUiDir(): string | null {
|
|
99
|
+
const candidates = [
|
|
100
|
+
new URL('./docs-ui/', import.meta.url),
|
|
101
|
+
];
|
|
102
|
+
for (const candidate of candidates) {
|
|
103
|
+
const dir = fileURLToPath(candidate);
|
|
104
|
+
if (existsSync(dir)) return dir;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── pyric dist discovery ─────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
/** Locate the installed pyric package root (works in the workspace and when
|
|
112
|
+
* @pyric/cli is npm-installed — pyric is a direct dependency). */
|
|
113
|
+
export function pyricPackageRoot(): string {
|
|
114
|
+
// Resolve a real exported subpath, then walk up to the package root.
|
|
115
|
+
const entry = fileURLToPath(import.meta.resolve('pyric/firestore'));
|
|
116
|
+
let dir = dirname(entry);
|
|
117
|
+
while (dir.includes(sep) && !existsSync(join(dir, 'package.json'))) {
|
|
118
|
+
dir = dirname(dir);
|
|
119
|
+
}
|
|
120
|
+
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as { name?: string };
|
|
121
|
+
if (pkg.name !== 'pyric') {
|
|
122
|
+
throw new Error(`pyric dev: expected to resolve the pyric package, found '${pkg.name}' at ${dir}`);
|
|
123
|
+
}
|
|
124
|
+
return dir;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function pyricVersion(root: string = pyricPackageRoot()): string {
|
|
128
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { version?: string };
|
|
129
|
+
return pkg.version ?? '0.0.0';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ─── plugins ──────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
/** Resolve `pyric/*` specifiers from THIS package's own dependency context
|
|
135
|
+
* (`import.meta.resolve`) rather than from the entry file's directory —
|
|
136
|
+
* entries may live in a temp dir or the user's project, neither of which
|
|
137
|
+
* has pyric in scope; @pyric/cli always does (direct dependency). */
|
|
138
|
+
function pyricResolvePlugin(): esbuild.Plugin {
|
|
139
|
+
return {
|
|
140
|
+
name: 'pyric-serve-resolve-pyric',
|
|
141
|
+
setup(build) {
|
|
142
|
+
build.onResolve({ filter: /^pyric(\/|$)/ }, (args) => ({
|
|
143
|
+
path: fileURLToPath(import.meta.resolve(args.path)),
|
|
144
|
+
}));
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The node builtins pyric's browser graph reaches for (via the rules module
|
|
150
|
+
* resolver). Bare and `node:`-prefixed both match. */
|
|
151
|
+
export const NODE_BUILTIN_RE = /^(node:)?(url|fs|path)$/;
|
|
152
|
+
|
|
153
|
+
/** Benign browser shim SOURCE per builtin (see header — droppable only when
|
|
154
|
+
* pyric breaks the `firestore/index → rules/tools → modules/resolver` static
|
|
155
|
+
* chain, #553). Exported so the Vite plugin (`./vite`) reuses the SAME shims
|
|
156
|
+
* through its own resolveId/load + optimizeDeps esbuild pass, instead of
|
|
157
|
+
* re-deriving them. */
|
|
158
|
+
export const NODE_BUILTIN_SHIMS: Record<string, string> = {
|
|
159
|
+
fs: `const no = () => { throw new Error('pyric dev: fs is not available in the browser'); };
|
|
160
|
+
export const readFileSync = no; export const existsSync = () => false;
|
|
161
|
+
export const readdirSync = no; export const writeFileSync = no; export const mkdirSync = no;
|
|
162
|
+
export default { readFileSync, existsSync, readdirSync, writeFileSync, mkdirSync };`,
|
|
163
|
+
url: `export const fileURLToPath = (u) => String(u).replace('file://', '');
|
|
164
|
+
export const pathToFileURL = (p) => new URL('file://' + p);
|
|
165
|
+
export default { fileURLToPath, pathToFileURL };`,
|
|
166
|
+
path: `export const join = (...p) => p.join('/');
|
|
167
|
+
export const dirname = (p) => p.split('/').slice(0, -1).join('/') || '/';
|
|
168
|
+
export const resolve = (...p) => p.join('/');
|
|
169
|
+
export const basename = (p) => p.split('/').pop() ?? '';
|
|
170
|
+
export default { join, dirname, resolve, basename };`,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
function nodeShimPlugin(): esbuild.Plugin {
|
|
174
|
+
return {
|
|
175
|
+
name: 'pyric-serve-node-shims',
|
|
176
|
+
setup(build) {
|
|
177
|
+
build.onResolve({ filter: NODE_BUILTIN_RE }, (args) => ({
|
|
178
|
+
path: args.path.replace(/^node:/, ''),
|
|
179
|
+
namespace: 'pyric-node-shim',
|
|
180
|
+
}));
|
|
181
|
+
build.onLoad({ filter: /.*/, namespace: 'pyric-node-shim' }, (args) => ({
|
|
182
|
+
contents: NODE_BUILTIN_SHIMS[args.path]!,
|
|
183
|
+
loader: 'js',
|
|
184
|
+
}));
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─── cache ────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
export interface BundleOptions {
|
|
192
|
+
/** Entry files (the wrapper modules in `entries/`), keyed by the served
|
|
193
|
+
* basename (`auth` → `/__pyric/sdk/auth.js`). */
|
|
194
|
+
entries: Record<string, string>;
|
|
195
|
+
/** Bypass the cache and build an immutable, process-independent generation. */
|
|
196
|
+
noCache?: boolean;
|
|
197
|
+
/** Override the cache root (tests). Default `~/.pyric/serve-cache`. */
|
|
198
|
+
cacheRoot?: string;
|
|
199
|
+
minify?: boolean;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface BundleResult {
|
|
203
|
+
outDir: string;
|
|
204
|
+
/** Served basename → absolute file path (entry bundles + shared chunks). */
|
|
205
|
+
files: string[];
|
|
206
|
+
cached: boolean;
|
|
207
|
+
/** Release a bypass generation after its server stops. Cache generations
|
|
208
|
+
* deliberately remain durable, so their disposer is a no-op. */
|
|
209
|
+
dispose(): void;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Bump when bundler logic changes in a way that must invalidate caches. */
|
|
213
|
+
const BUNDLER_REV = 9; // 9: cache keys also cover dependency-resolution metadata
|
|
214
|
+
|
|
215
|
+
const RESOLUTION_FILES = [
|
|
216
|
+
'bun.lock',
|
|
217
|
+
'bun.lockb',
|
|
218
|
+
'package-lock.json',
|
|
219
|
+
'pnpm-lock.yaml',
|
|
220
|
+
'yarn.lock',
|
|
221
|
+
] as const;
|
|
222
|
+
|
|
223
|
+
function nearestPackageRoot(start: string): string | undefined {
|
|
224
|
+
let dir = start;
|
|
225
|
+
while (true) {
|
|
226
|
+
if (existsSync(join(dir, 'package.json'))) return dir;
|
|
227
|
+
const parent = dirname(dir);
|
|
228
|
+
if (parent === dir) return undefined;
|
|
229
|
+
dir = parent;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Hash package-manager identity separately from executable source bytes.
|
|
235
|
+
* Workspace dependency upgrades commonly change package.json/bun.lock before
|
|
236
|
+
* the prerelease package version moves; installed packages get the same signal
|
|
237
|
+
* from their consumer lockfile. Package managers own third-party byte
|
|
238
|
+
* integrity, so their manifests/locks are the supported cache boundary.
|
|
239
|
+
*/
|
|
240
|
+
function packageResolutionHash(graphRoot: string, pyricRoot: string): string {
|
|
241
|
+
const h = createHash('sha256');
|
|
242
|
+
const cliRoot = nearestPackageRoot(graphRoot);
|
|
243
|
+
for (const [label, root] of [
|
|
244
|
+
['cli', cliRoot],
|
|
245
|
+
['pyric', pyricRoot],
|
|
246
|
+
] as const) {
|
|
247
|
+
if (!root) continue;
|
|
248
|
+
const manifest = join(root, 'package.json');
|
|
249
|
+
if (!existsSync(manifest)) continue;
|
|
250
|
+
h.update(label);
|
|
251
|
+
h.update(readFileSync(manifest));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let dir = cliRoot ?? graphRoot;
|
|
255
|
+
while (true) {
|
|
256
|
+
for (const name of RESOLUTION_FILES) {
|
|
257
|
+
const file = join(dir, name);
|
|
258
|
+
if (!existsSync(file)) continue;
|
|
259
|
+
h.update(name);
|
|
260
|
+
h.update(readFileSync(file));
|
|
261
|
+
}
|
|
262
|
+
const parent = dirname(dir);
|
|
263
|
+
if (parent === dir) break;
|
|
264
|
+
dir = parent;
|
|
265
|
+
}
|
|
266
|
+
return h.digest('hex');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function cacheKey(
|
|
270
|
+
opts: BundleOptions,
|
|
271
|
+
version: string,
|
|
272
|
+
graphRoot = dirname(dirname(fileURLToPath(import.meta.url))),
|
|
273
|
+
pyricRoot = pyricPackageRoot(),
|
|
274
|
+
): string {
|
|
275
|
+
const h = createHash('sha256');
|
|
276
|
+
h.update(`rev${BUNDLER_REV}`);
|
|
277
|
+
h.update(version);
|
|
278
|
+
// Hash the WHOLE local CLI graph, not just entries or entries/ siblings.
|
|
279
|
+
// Page adapters import worker/client, worker/protocol, bridge, and verify
|
|
280
|
+
// leaves outside that directory; missing one can pair a stale page client
|
|
281
|
+
// with a freshly rebuilt worker. The installed pyric implementation is
|
|
282
|
+
// hashed separately: workspace and prerelease builds can change dist bytes
|
|
283
|
+
// without changing package.json's version.
|
|
284
|
+
h.update(sourceTreeHash(graphRoot));
|
|
285
|
+
h.update(sourceTreeHash(join(pyricRoot, 'dist')));
|
|
286
|
+
h.update(packageResolutionHash(graphRoot, pyricRoot));
|
|
287
|
+
for (const [name, file] of Object.entries(opts.entries).sort()) {
|
|
288
|
+
h.update(name);
|
|
289
|
+
h.update(readFileSync(file, 'utf8'));
|
|
290
|
+
}
|
|
291
|
+
return `${version}-${h.digest('hex').slice(0, 12)}`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ─── the bundle step ──────────────────────────────────────────────────
|
|
295
|
+
|
|
296
|
+
export async function bundleSdk(opts: BundleOptions): Promise<BundleResult> {
|
|
297
|
+
const root = pyricPackageRoot();
|
|
298
|
+
const version = pyricVersion(root);
|
|
299
|
+
const key = cacheKey(opts, version);
|
|
300
|
+
const cacheRoot = opts.cacheRoot ?? join(homedir(), '.pyric', 'serve-cache');
|
|
301
|
+
mkdirSync(cacheRoot, { recursive: true });
|
|
302
|
+
// A running server keeps serving this directory after bundleSdk returns.
|
|
303
|
+
// Therefore `--no-cache` must never overwrite the deterministic warm-cache
|
|
304
|
+
// path: another concurrent `pyric dev --no-cache` could otherwise replace
|
|
305
|
+
// an entry and its shared chunks between browser requests, splitting
|
|
306
|
+
// singleton module state (notably the app registry). Each bypass build gets
|
|
307
|
+
// an immutable generation; the OS-level mkdir primitive makes concurrent
|
|
308
|
+
// callers distinct without coordination.
|
|
309
|
+
const generationRoot = opts.noCache
|
|
310
|
+
? mkdtempSync(join(cacheRoot, `.no-cache-${key}-`))
|
|
311
|
+
: join(cacheRoot, key);
|
|
312
|
+
const outDir = join(generationRoot, 'sdk');
|
|
313
|
+
const dispose = opts.noCache
|
|
314
|
+
? () => rmSync(generationRoot, { recursive: true, force: true })
|
|
315
|
+
: () => {};
|
|
316
|
+
|
|
317
|
+
if (!opts.noCache && existsSync(join(outDir, '.complete'))) {
|
|
318
|
+
const files = (readdirSync(outDir) as string[])
|
|
319
|
+
.filter((f) => f.endsWith('.js'))
|
|
320
|
+
.map((f) => join(outDir, f));
|
|
321
|
+
return { outDir, files, cached: true, dispose };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
mkdirSync(outDir, { recursive: true });
|
|
325
|
+
try {
|
|
326
|
+
const result = await esbuild.build({
|
|
327
|
+
entryPoints: Object.fromEntries(
|
|
328
|
+
Object.entries(opts.entries).map(([name, file]) => [name, file]),
|
|
329
|
+
),
|
|
330
|
+
outdir: outDir,
|
|
331
|
+
bundle: true,
|
|
332
|
+
format: 'esm',
|
|
333
|
+
platform: 'browser',
|
|
334
|
+
splitting: true,
|
|
335
|
+
sourcemap: 'linked',
|
|
336
|
+
minify: opts.minify ?? true,
|
|
337
|
+
logLevel: 'silent',
|
|
338
|
+
// Provenance: any stack frame or "view source" into these bundles must
|
|
339
|
+
// self-identify as the sandbox shim, not the real Firebase SDK.
|
|
340
|
+
chunkNames: 'pyric-sandbox-[hash]',
|
|
341
|
+
banner: {
|
|
342
|
+
js: '/* pyric dev: pyric sandbox shim serving firebase/* — NOT the real Firebase SDK */',
|
|
343
|
+
},
|
|
344
|
+
plugins: [pyricResolvePlugin(), nodeShimPlugin()],
|
|
345
|
+
});
|
|
346
|
+
if (result.errors.length > 0) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
`pyric dev: SDK bundle failed:\n${result.errors.map((e) => e.text).join('\n')}`,
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
writeFileSync(join(outDir, '.complete'), new Date().toISOString());
|
|
352
|
+
const files = (readdirSync(outDir) as string[])
|
|
353
|
+
.filter((f) => f.endsWith('.js'))
|
|
354
|
+
.map((f) => join(outDir, f));
|
|
355
|
+
return { outDir, files, cached: false, dispose };
|
|
356
|
+
} catch (error) {
|
|
357
|
+
dispose();
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ─── the SharedWorker bundle (Phase 3c.A) ─────────────────────────────────
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Locate the SharedWorker entry (`worker/entry.ts`) — compiled `.js` sibling
|
|
366
|
+
* from dist, `.ts` source in the workspace. Same dual-resolution as
|
|
367
|
+
* `defaultSdkEntries`.
|
|
368
|
+
*/
|
|
369
|
+
export function workerEntryPath(): string {
|
|
370
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
371
|
+
const js = join(here, 'worker', 'entry.js');
|
|
372
|
+
if (existsSync(js)) return js;
|
|
373
|
+
const ts = join(here, 'worker', 'entry.ts');
|
|
374
|
+
if (existsSync(ts)) return ts;
|
|
375
|
+
throw new Error(`pyric dev: missing SharedWorker entry next to ${here}`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export interface WorkerBundleOptions {
|
|
379
|
+
/** Where to write `worker.js` — normally the SDK bundle's `outDir` so the
|
|
380
|
+
* existing `/__pyric/sdk/<file>` route serves it with no extra wiring. */
|
|
381
|
+
outDir: string;
|
|
382
|
+
/** Bypass + overwrite the cached worker bundle. */
|
|
383
|
+
noCache?: boolean;
|
|
384
|
+
minify?: boolean;
|
|
385
|
+
/** Test seam for proving epoch identity from an isolated executable graph. */
|
|
386
|
+
entryPath?: string;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const WORKER_EPOCH_PLACEHOLDER = 'PYRIC_EPOCH_HERE';
|
|
390
|
+
const WORKER_EPOCH_FILE = '.worker-epoch';
|
|
391
|
+
|
|
392
|
+
/** Read the executable epoch written alongside a completed worker bundle. */
|
|
393
|
+
function readWorkerBundleEpoch(outDir: string): string {
|
|
394
|
+
const epoch = readFileSync(join(outDir, WORKER_EPOCH_FILE), 'utf8').trim();
|
|
395
|
+
if (!/^[a-f0-9]{16}$/.test(epoch)) {
|
|
396
|
+
throw new Error(`pyric dev: invalid SharedWorker epoch in ${join(outDir, WORKER_EPOCH_FILE)}`);
|
|
397
|
+
}
|
|
398
|
+
return epoch;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Conservative worker CACHE key. The worker bundle shares the SDK `outDir`
|
|
403
|
+
* (keyed by SDK entries), which does not cover its host graph, so this broad
|
|
404
|
+
* source-tree hash decides when esbuild must run again. It is intentionally NOT
|
|
405
|
+
* the user-facing worker epoch: unrelated CLI changes may invalidate this cache,
|
|
406
|
+
* while the epoch returned by {@link bundleWorker} is derived from the executable output and only
|
|
407
|
+
* then decides whether a running SharedWorker needs replacement.
|
|
408
|
+
*/
|
|
409
|
+
export function workerSourceHash(
|
|
410
|
+
pyricRoot = pyricPackageRoot(),
|
|
411
|
+
graphRoot = dirname(dirname(fileURLToPath(import.meta.url))),
|
|
412
|
+
): string {
|
|
413
|
+
const h = createHash('sha256');
|
|
414
|
+
h.update(`rev${BUNDLER_REV}`);
|
|
415
|
+
h.update(pyricVersion(pyricRoot));
|
|
416
|
+
h.update(sourceTreeHash(join(pyricRoot, 'dist')));
|
|
417
|
+
h.update(sourceTreeHash(graphRoot));
|
|
418
|
+
h.update(packageResolutionHash(graphRoot, pyricRoot));
|
|
419
|
+
return h.digest('hex').slice(0, 16);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** Hash every JS/TS source below a directory, including split host/client leaves. */
|
|
423
|
+
export function sourceTreeHash(root: string): string {
|
|
424
|
+
const files: string[] = [];
|
|
425
|
+
const visit = (dir: string): void => {
|
|
426
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
427
|
+
const path = join(dir, entry.name);
|
|
428
|
+
if (entry.isDirectory()) visit(path);
|
|
429
|
+
else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) {
|
|
430
|
+
files.push(path);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
visit(root);
|
|
435
|
+
const h = createHash('sha256');
|
|
436
|
+
for (const file of files.sort()) {
|
|
437
|
+
h.update(relative(root, file).split(sep).join('/'));
|
|
438
|
+
h.update(readFileSync(file, 'utf8'));
|
|
439
|
+
}
|
|
440
|
+
return h.digest('hex');
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Bundle the SharedWorker entry into a single classic worker script served at
|
|
445
|
+
* `/__pyric/sdk/worker.js`.
|
|
446
|
+
*
|
|
447
|
+
* WHY A SEPARATE BUILD (not part of `bundleSdk`'s splitting graph): a
|
|
448
|
+
* SharedWorker script is loaded by URL into its OWN realm — it cannot share
|
|
449
|
+
* the page's runtime chunk. So it is bundled standalone: `splitting: false`,
|
|
450
|
+
* `format: 'iife'` (the client opens it with `new SharedWorker(url, { type:
|
|
451
|
+
* 'classic' })`), everything inlined into one file. It reuses the same three
|
|
452
|
+
* browser-standalone plugins as the SDK bundle (pyric-resolve + node-shims +
|
|
453
|
+
* firebase-stub) so `pyric/*`, node builtins, and the `firebase/*` peer stubs
|
|
454
|
+
* resolve identically.
|
|
455
|
+
*
|
|
456
|
+
* NOTE: the worker legitimately holds the whole sandbox engine — it IS the
|
|
457
|
+
* backend — so this bundle is large, but it loads ONCE per origin (not per
|
|
458
|
+
* page). Trimming node/admin dead paths is tracked as Phase 3b.
|
|
459
|
+
*
|
|
460
|
+
* The `.worker-complete` marker is independent of `bundleSdk`'s `.complete`,
|
|
461
|
+
* while `cacheKey()` includes the complete CLI source graph. A worker/client
|
|
462
|
+
* or protocol edit therefore invalidates the shared SDK generation too, so
|
|
463
|
+
* the page and worker halves cannot come from different source generations.
|
|
464
|
+
*/
|
|
465
|
+
export interface WorkerBundleResult {
|
|
466
|
+
outFile: string;
|
|
467
|
+
epoch: string;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export async function bundleWorker(opts: WorkerBundleOptions): Promise<WorkerBundleResult> {
|
|
471
|
+
const outFile = join(opts.outDir, 'worker.js');
|
|
472
|
+
const marker = join(opts.outDir, '.worker-complete');
|
|
473
|
+
const hash = workerSourceHash();
|
|
474
|
+
if (
|
|
475
|
+
!opts.noCache &&
|
|
476
|
+
existsSync(outFile) &&
|
|
477
|
+
existsSync(marker) &&
|
|
478
|
+
existsSync(join(opts.outDir, WORKER_EPOCH_FILE)) &&
|
|
479
|
+
readFileSync(marker, 'utf8') === hash
|
|
480
|
+
) {
|
|
481
|
+
return { outFile, epoch: readWorkerBundleEpoch(opts.outDir) };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
mkdirSync(opts.outDir, { recursive: true });
|
|
485
|
+
const root = pyricPackageRoot();
|
|
486
|
+
const result = await esbuild.build({
|
|
487
|
+
entryPoints: [opts.entryPath ?? workerEntryPath()],
|
|
488
|
+
outfile: outFile,
|
|
489
|
+
bundle: true,
|
|
490
|
+
format: 'iife',
|
|
491
|
+
platform: 'browser',
|
|
492
|
+
splitting: false,
|
|
493
|
+
sourcemap: 'linked',
|
|
494
|
+
minify: opts.minify ?? true,
|
|
495
|
+
logLevel: 'silent',
|
|
496
|
+
// Reserve a fixed-width self-version slot. After esbuild writes the actual
|
|
497
|
+
// executable graph, we hash that canonical output and replace this token
|
|
498
|
+
// with the resulting epoch (same width, so the sourcemap stays aligned).
|
|
499
|
+
define: { __PYRIC_WORKER_VERSION__: JSON.stringify(WORKER_EPOCH_PLACEHOLDER) },
|
|
500
|
+
banner: {
|
|
501
|
+
js: '/* pyric dev: SharedWorker sandbox host serving firebase/* — NOT the real Firebase SDK */',
|
|
502
|
+
},
|
|
503
|
+
plugins: [pyricResolvePlugin(), nodeShimPlugin()],
|
|
504
|
+
});
|
|
505
|
+
if (result.errors.length > 0) {
|
|
506
|
+
throw new Error(
|
|
507
|
+
`pyric dev: SharedWorker bundle failed:\n${result.errors.map((e) => e.text).join('\n')}`,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
const canonicalSource = readFileSync(outFile, 'utf8');
|
|
511
|
+
if (!canonicalSource.includes(WORKER_EPOCH_PLACEHOLDER)) {
|
|
512
|
+
throw new Error('pyric dev: SharedWorker bundle omitted its epoch placeholder');
|
|
513
|
+
}
|
|
514
|
+
const epoch = createHash('sha256').update(canonicalSource).digest('hex').slice(0, 16);
|
|
515
|
+
writeFileSync(outFile, canonicalSource.replaceAll(WORKER_EPOCH_PLACEHOLDER, epoch));
|
|
516
|
+
writeFileSync(join(opts.outDir, WORKER_EPOCH_FILE), epoch);
|
|
517
|
+
writeFileSync(marker, hash);
|
|
518
|
+
return { outFile, epoch };
|
|
519
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture store for `pyric dev --capture` — the write-side of the
|
|
3
|
+
* `pyric verify` loop.
|
|
4
|
+
*
|
|
5
|
+
* `pyric dev` captures the in-page sandbox session (history + snapshot +
|
|
6
|
+
* rules) and pushes it here whenever the sandbox changes. `pyric verify`
|
|
7
|
+
* (no positional arg) reads `SERVE_CAPTURE_PATH` and replays it against
|
|
8
|
+
* your current rules to surface real divergences. The loop is:
|
|
9
|
+
*
|
|
10
|
+
* pyric dev → use your app → pyric verify
|
|
11
|
+
*
|
|
12
|
+
* The body is stored VERBATIM (the page sends its own JSON.stringify'd
|
|
13
|
+
* fixture — we never re-serialize it so no wrapper-type drift is
|
|
14
|
+
* introduced here). The path is intentionally next to the persist store
|
|
15
|
+
* (.pyric/state/…) so it fits the same .gitignore pattern and feels like
|
|
16
|
+
* one artifact family.
|
|
17
|
+
*/
|
|
18
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { dirname, join } from 'node:path';
|
|
20
|
+
|
|
21
|
+
/** Relative path (from project root) where the serve capture lives.
|
|
22
|
+
* Mirrors `SERVE_CAPTURE_PATH` in `verify.ts` — keep in lockstep. */
|
|
23
|
+
export const CAPTURE_RELATIVE_PATH = join('.pyric', 'last-session.json');
|
|
24
|
+
|
|
25
|
+
export interface CaptureStore {
|
|
26
|
+
/** Absolute path of the capture file (`<projectDir>/.pyric/last-session.json`). */
|
|
27
|
+
path: string;
|
|
28
|
+
/**
|
|
29
|
+
* Write the fixture JSON verbatim. Creates `.pyric/` if absent.
|
|
30
|
+
* The body is the page's own JSON-serialized fixture; we store it
|
|
31
|
+
* as-is so `pyric verify` can hand it straight to the replay engine.
|
|
32
|
+
*/
|
|
33
|
+
write(fixtureJson: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Read back the last-written fixture JSON verbatim, or `null` when the
|
|
36
|
+
* capture file does not exist yet (nothing captured this run). Powers
|
|
37
|
+
* GET /__pyric/capture, which the served SharedWorker fetches on boot to
|
|
38
|
+
* re-hydrate its in-memory event history after a worker death.
|
|
39
|
+
*/
|
|
40
|
+
read(): string | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create a capture store rooted at `projectDir`. Returns a tiny sink that
|
|
45
|
+
* mirrors the minimal shape of `state-store.ts`: a `path` for banner
|
|
46
|
+
* reporting and a `write` for the route handler.
|
|
47
|
+
*/
|
|
48
|
+
export function createCaptureStore(projectDir: string): CaptureStore {
|
|
49
|
+
const path = join(projectDir, CAPTURE_RELATIVE_PATH);
|
|
50
|
+
return {
|
|
51
|
+
path,
|
|
52
|
+
write(fixtureJson: string): void {
|
|
53
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
54
|
+
writeFileSync(path, fixtureJson);
|
|
55
|
+
},
|
|
56
|
+
read(): string | null {
|
|
57
|
+
try {
|
|
58
|
+
return readFileSync(path, 'utf8');
|
|
59
|
+
} catch {
|
|
60
|
+
// ENOENT (nothing captured yet) or any read error → treat as absent.
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|