@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,382 @@
|
|
|
1
|
+
import { spawn, type ChildProcess } from 'node:child_process';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { dirname, extname, join, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
import type { RemoteSandbox } from '../remote/index.js';
|
|
8
|
+
import {
|
|
9
|
+
startOnValueCreatedExecution,
|
|
10
|
+
type OnValueCreatedExecutionHost,
|
|
11
|
+
} from './execution.js';
|
|
12
|
+
import {
|
|
13
|
+
inspectOnValueCreated,
|
|
14
|
+
listFirebaseEndpoints,
|
|
15
|
+
} from './discovery.js';
|
|
16
|
+
import type { CreatedExecutionResult } from './event.js';
|
|
17
|
+
import { RemoteRtdbTriggerDelivery } from './remote-delivery.js';
|
|
18
|
+
|
|
19
|
+
export interface SerializedFunctionsRtdbError {
|
|
20
|
+
name: string;
|
|
21
|
+
message: string;
|
|
22
|
+
stack?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type FunctionsRtdbChildEvent =
|
|
26
|
+
| {
|
|
27
|
+
type: 'execution';
|
|
28
|
+
exportName: string;
|
|
29
|
+
ref: string;
|
|
30
|
+
params: Record<string, string>;
|
|
31
|
+
status: 'fulfilled';
|
|
32
|
+
}
|
|
33
|
+
| {
|
|
34
|
+
type: 'execution';
|
|
35
|
+
exportName: string;
|
|
36
|
+
ref: string;
|
|
37
|
+
params: Record<string, string>;
|
|
38
|
+
status: 'rejected';
|
|
39
|
+
error: SerializedFunctionsRtdbError;
|
|
40
|
+
}
|
|
41
|
+
| {
|
|
42
|
+
type: 'delivery-error';
|
|
43
|
+
exportName: string;
|
|
44
|
+
error: SerializedFunctionsRtdbError;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export interface FunctionsRtdbChildReady {
|
|
48
|
+
triggerCount: number;
|
|
49
|
+
unsupportedTriggers: UnsupportedFunctionsTrigger[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface UnsupportedFunctionsTrigger {
|
|
53
|
+
exportName: string;
|
|
54
|
+
eventType: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SpawnFunctionsRtdbChildOptions {
|
|
58
|
+
cwd: string;
|
|
59
|
+
entry: string;
|
|
60
|
+
env: NodeJS.ProcessEnv;
|
|
61
|
+
instance: string;
|
|
62
|
+
location: string;
|
|
63
|
+
databaseHost?: string;
|
|
64
|
+
childModuleUrl?: string | URL;
|
|
65
|
+
nodeExecutable?: string;
|
|
66
|
+
onEvent?(event: FunctionsRtdbChildEvent): void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface FunctionsRtdbChildHandle {
|
|
70
|
+
child: ChildProcess;
|
|
71
|
+
ready: Promise<FunctionsRtdbChildReady>;
|
|
72
|
+
exited: Promise<number>;
|
|
73
|
+
stop(): Promise<number>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type FunctionsRtdbChildMessage =
|
|
77
|
+
| ({ type: 'ready' } & FunctionsRtdbChildReady)
|
|
78
|
+
| FunctionsRtdbChildEvent
|
|
79
|
+
| { type: 'fatal'; error: SerializedFunctionsRtdbError }
|
|
80
|
+
| { type: 'stopped' };
|
|
81
|
+
|
|
82
|
+
const FORCE_KILL_AFTER_MS = 2_000;
|
|
83
|
+
|
|
84
|
+
function serializeError(error: unknown): SerializedFunctionsRtdbError {
|
|
85
|
+
if (error instanceof Error) {
|
|
86
|
+
return {
|
|
87
|
+
name: error.name,
|
|
88
|
+
message: error.message,
|
|
89
|
+
...(error.stack === undefined ? {} : { stack: error.stack }),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return { name: 'Error', message: String(error) };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isChildMessage(value: unknown): value is FunctionsRtdbChildMessage {
|
|
96
|
+
return typeof value === 'object' && value !== null && 'type' in value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Spawn the real Firebase Functions SDK in an isolated Node process. */
|
|
100
|
+
export function spawnFunctionsRtdbChild(
|
|
101
|
+
options: SpawnFunctionsRtdbChildOptions,
|
|
102
|
+
): FunctionsRtdbChildHandle {
|
|
103
|
+
const childModuleUrl = options.childModuleUrl ?? new URL('./child.js', import.meta.url);
|
|
104
|
+
const childModulePath = typeof childModuleUrl === 'string' && !childModuleUrl.startsWith('file:')
|
|
105
|
+
? resolve(childModuleUrl)
|
|
106
|
+
: fileURLToPath(childModuleUrl);
|
|
107
|
+
const child = spawn(options.nodeExecutable ?? 'node', [childModulePath], {
|
|
108
|
+
cwd: options.cwd,
|
|
109
|
+
env: {
|
|
110
|
+
...options.env,
|
|
111
|
+
PYRIC_FUNCTIONS_RTDB_CHILD: '1',
|
|
112
|
+
PYRIC_FUNCTIONS_ENTRY: resolve(options.entry),
|
|
113
|
+
PYRIC_FUNCTIONS_INSTANCE: options.instance,
|
|
114
|
+
PYRIC_FUNCTIONS_LOCATION: options.location,
|
|
115
|
+
PYRIC_FUNCTIONS_DATABASE_HOST: options.databaseHost ?? 'firebasedatabase.app',
|
|
116
|
+
},
|
|
117
|
+
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
let readySettled = false;
|
|
121
|
+
let stopping = false;
|
|
122
|
+
let stderr = '';
|
|
123
|
+
child.stderr?.setEncoding('utf8');
|
|
124
|
+
child.stderr?.on('data', (chunk: string) => {
|
|
125
|
+
stderr = `${stderr}${chunk}`.slice(-8_192);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
let resolveReady!: (ready: FunctionsRtdbChildReady) => void;
|
|
129
|
+
let rejectReady!: (error: Error) => void;
|
|
130
|
+
const ready = new Promise<FunctionsRtdbChildReady>((resolvePromise, rejectPromise) => {
|
|
131
|
+
resolveReady = resolvePromise;
|
|
132
|
+
rejectReady = rejectPromise;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
child.on('message', (raw: unknown) => {
|
|
136
|
+
if (!isChildMessage(raw)) return;
|
|
137
|
+
if (raw.type === 'ready') {
|
|
138
|
+
readySettled = true;
|
|
139
|
+
resolveReady({
|
|
140
|
+
triggerCount: raw.triggerCount,
|
|
141
|
+
unsupportedTriggers: raw.unsupportedTriggers,
|
|
142
|
+
});
|
|
143
|
+
} else if (raw.type === 'fatal') {
|
|
144
|
+
if (!readySettled) {
|
|
145
|
+
readySettled = true;
|
|
146
|
+
rejectReady(new Error(
|
|
147
|
+
`Functions RTDB child failed: ${raw.error.stack ?? raw.error.message}`,
|
|
148
|
+
));
|
|
149
|
+
}
|
|
150
|
+
} else if (raw.type === 'execution' || raw.type === 'delivery-error') {
|
|
151
|
+
options.onEvent?.(raw);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
let forceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
156
|
+
const exited = new Promise<number>((resolveExited) => {
|
|
157
|
+
child.once('error', (error) => {
|
|
158
|
+
if (!readySettled) {
|
|
159
|
+
readySettled = true;
|
|
160
|
+
rejectReady(error);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
child.once('close', (code, signal) => {
|
|
164
|
+
if (forceTimer !== undefined) clearTimeout(forceTimer);
|
|
165
|
+
const exitCode = typeof code === 'number' ? code : signal ? 1 : 0;
|
|
166
|
+
if (!readySettled) {
|
|
167
|
+
readySettled = true;
|
|
168
|
+
const diagnostic = stderr.trim();
|
|
169
|
+
rejectReady(new Error(
|
|
170
|
+
`Functions RTDB child exited before ready (code ${exitCode})` +
|
|
171
|
+
(diagnostic ? `\n${diagnostic}` : ''),
|
|
172
|
+
));
|
|
173
|
+
}
|
|
174
|
+
resolveExited(exitCode);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
child,
|
|
180
|
+
ready,
|
|
181
|
+
exited,
|
|
182
|
+
async stop(): Promise<number> {
|
|
183
|
+
if (child.exitCode !== null || child.signalCode !== null) return exited;
|
|
184
|
+
if (!stopping) {
|
|
185
|
+
stopping = true;
|
|
186
|
+
if (child.connected) child.send({ type: 'stop' });
|
|
187
|
+
else child.kill('SIGTERM');
|
|
188
|
+
forceTimer = setTimeout(() => {
|
|
189
|
+
if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL');
|
|
190
|
+
}, FORCE_KILL_AFTER_MS);
|
|
191
|
+
forceTimer.unref();
|
|
192
|
+
}
|
|
193
|
+
return exited;
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface AdminAppModule {
|
|
199
|
+
initializeApp(): { sandbox: RemoteSandbox };
|
|
200
|
+
deleteApp(app: unknown): Promise<void>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function send(message: FunctionsRtdbChildMessage): void {
|
|
204
|
+
process.send?.(message);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function usesCommonJs(entry: string): boolean {
|
|
208
|
+
const extension = extname(entry);
|
|
209
|
+
if (extension === '.cjs') return true;
|
|
210
|
+
if (extension === '.mjs') return false;
|
|
211
|
+
|
|
212
|
+
let directory = dirname(entry);
|
|
213
|
+
while (true) {
|
|
214
|
+
const packageJsonPath = join(directory, 'package.json');
|
|
215
|
+
if (existsSync(packageJsonPath)) {
|
|
216
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
|
|
217
|
+
type?: unknown;
|
|
218
|
+
};
|
|
219
|
+
return packageJson.type !== 'module';
|
|
220
|
+
}
|
|
221
|
+
const parent = dirname(directory);
|
|
222
|
+
if (parent === directory) return true;
|
|
223
|
+
directory = parent;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Load the entry with the semantics selected by its extension and package scope. */
|
|
228
|
+
async function loadFunctionsExports(entry: string): Promise<Record<string, unknown>> {
|
|
229
|
+
if (usesCommonJs(entry)) {
|
|
230
|
+
return createRequire(entry)(entry) as Record<string, unknown>;
|
|
231
|
+
}
|
|
232
|
+
return await import(pathToFileURL(entry).href) as Record<string, unknown>;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function runFunctionsRtdbChild(): Promise<void> {
|
|
236
|
+
const entry = process.env.PYRIC_FUNCTIONS_ENTRY;
|
|
237
|
+
const instance = process.env.PYRIC_FUNCTIONS_INSTANCE;
|
|
238
|
+
const location = process.env.PYRIC_FUNCTIONS_LOCATION;
|
|
239
|
+
const databaseHost = process.env.PYRIC_FUNCTIONS_DATABASE_HOST;
|
|
240
|
+
if (!entry || !instance || !location || !databaseHost) {
|
|
241
|
+
throw new Error('Functions RTDB child is missing its required environment');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const requireFromEntry = createRequire(entry);
|
|
245
|
+
const adminApp = requireFromEntry('firebase-admin/app') as AdminAppModule;
|
|
246
|
+
const app = adminApp.initializeApp();
|
|
247
|
+
let host: OnValueCreatedExecutionHost | undefined;
|
|
248
|
+
let closing: Promise<void> | undefined;
|
|
249
|
+
|
|
250
|
+
const close = (): Promise<void> => {
|
|
251
|
+
if (closing) return closing;
|
|
252
|
+
closing = (async () => {
|
|
253
|
+
host?.close();
|
|
254
|
+
await host?.idle();
|
|
255
|
+
app.sandbox.close();
|
|
256
|
+
await adminApp.deleteApp(app);
|
|
257
|
+
})();
|
|
258
|
+
return closing;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const shutdown = async (): Promise<void> => {
|
|
262
|
+
await close();
|
|
263
|
+
send({ type: 'stopped' });
|
|
264
|
+
process.disconnect?.();
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
process.once('message', (raw: unknown) => {
|
|
268
|
+
if (isChildMessage(raw) && raw.type === 'stopped') return;
|
|
269
|
+
if (typeof raw === 'object' && raw !== null && 'type' in raw && raw.type === 'stop') {
|
|
270
|
+
void shutdown().catch((error) => {
|
|
271
|
+
send({ type: 'fatal', error: serializeError(error) });
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
process.disconnect?.();
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
process.once('SIGINT', () => void shutdown());
|
|
278
|
+
process.once('SIGTERM', () => void shutdown());
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
// The real database provider asks firebase-admin/app for its default app
|
|
282
|
+
// when it wraps a raw CloudEvent. Initializing that app before loading the
|
|
283
|
+
// user's module avoids importing the broad firebase-functions/v2 barrel.
|
|
284
|
+
const exported = await loadFunctionsExports(entry);
|
|
285
|
+
const effectiveInstances = [...new Set(
|
|
286
|
+
inspectOnValueCreated(exported).triggers.map((trigger) =>
|
|
287
|
+
trigger.instance === '*' ? instance : trigger.instance,
|
|
288
|
+
),
|
|
289
|
+
)].sort();
|
|
290
|
+
if (effectiveInstances.length > 1) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
'Functions RTDB first slice supports one database instance; found ' +
|
|
293
|
+
effectiveInstances.join(', '),
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
host = startOnValueCreatedExecution({
|
|
297
|
+
exported,
|
|
298
|
+
delivery: new RemoteRtdbTriggerDelivery(app.sandbox.rtdb),
|
|
299
|
+
eventOptions: (_projection, sequence, trigger) => ({
|
|
300
|
+
id: `${randomUUID()}-${sequence}`,
|
|
301
|
+
time: new Date().toISOString(),
|
|
302
|
+
instance: trigger.instance === '*' ? instance : trigger.instance,
|
|
303
|
+
location: trigger.location ?? location,
|
|
304
|
+
databaseHost,
|
|
305
|
+
}),
|
|
306
|
+
onExecution(result, trigger, projection) {
|
|
307
|
+
sendExecution(result, trigger.exportName, projection.ref, projection.params);
|
|
308
|
+
},
|
|
309
|
+
onDeliveryError(error, trigger) {
|
|
310
|
+
send({
|
|
311
|
+
type: 'delivery-error',
|
|
312
|
+
exportName: trigger.exportName,
|
|
313
|
+
error: serializeError(error),
|
|
314
|
+
});
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
await host.ready;
|
|
318
|
+
send({
|
|
319
|
+
type: 'ready',
|
|
320
|
+
triggerCount: host.triggerCount,
|
|
321
|
+
unsupportedTriggers: findUnsupportedTriggers(exported),
|
|
322
|
+
});
|
|
323
|
+
} catch (error) {
|
|
324
|
+
await close();
|
|
325
|
+
throw error;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function findUnsupportedTriggers(exported: Record<string, unknown>): UnsupportedFunctionsTrigger[] {
|
|
330
|
+
const unsupported: UnsupportedFunctionsTrigger[] = [
|
|
331
|
+
...inspectOnValueCreated(exported).unsupported,
|
|
332
|
+
];
|
|
333
|
+
for (const { exportName, callable } of listFirebaseEndpoints(exported)) {
|
|
334
|
+
const endpoint = callable.__endpoint;
|
|
335
|
+
if (!endpoint) continue;
|
|
336
|
+
const eventType = endpoint.eventTrigger?.eventType;
|
|
337
|
+
if (eventType === 'google.firebase.database.ref.v1.created') continue;
|
|
338
|
+
const label = typeof eventType === 'string'
|
|
339
|
+
? eventType
|
|
340
|
+
: endpoint.callableTrigger !== undefined
|
|
341
|
+
? 'callable'
|
|
342
|
+
: endpoint.httpsTrigger !== undefined
|
|
343
|
+
? 'https'
|
|
344
|
+
: endpoint.scheduleTrigger !== undefined
|
|
345
|
+
? 'schedule'
|
|
346
|
+
: endpoint.taskQueueTrigger !== undefined
|
|
347
|
+
? 'task-queue'
|
|
348
|
+
: 'unknown';
|
|
349
|
+
unsupported.push({ exportName, eventType: label });
|
|
350
|
+
}
|
|
351
|
+
return unsupported;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function sendExecution(
|
|
355
|
+
result: CreatedExecutionResult,
|
|
356
|
+
exportName: string,
|
|
357
|
+
ref: string,
|
|
358
|
+
params: Record<string, string>,
|
|
359
|
+
): void {
|
|
360
|
+
if (result.status === 'fulfilled') {
|
|
361
|
+
send({ type: 'execution', exportName, ref, params, status: 'fulfilled' });
|
|
362
|
+
} else {
|
|
363
|
+
send({
|
|
364
|
+
type: 'execution',
|
|
365
|
+
exportName,
|
|
366
|
+
ref,
|
|
367
|
+
params,
|
|
368
|
+
status: 'rejected',
|
|
369
|
+
error: serializeError(result.error),
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (process.env.PYRIC_FUNCTIONS_RTDB_CHILD === '1') {
|
|
375
|
+
void runFunctionsRtdbChild().catch((error) => {
|
|
376
|
+
const serialized = serializeError(error);
|
|
377
|
+
send({ type: 'fatal', error: serialized });
|
|
378
|
+
process.stderr.write(`${serialized.stack ?? `${serialized.name}: ${serialized.message}`}\n`);
|
|
379
|
+
process.exitCode = 1;
|
|
380
|
+
process.disconnect?.();
|
|
381
|
+
});
|
|
382
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeRtdbReference,
|
|
3
|
+
supportsRtdbReference,
|
|
4
|
+
} from './reference-pattern.js';
|
|
5
|
+
|
|
6
|
+
export type RtdbCreatedCallable = (
|
|
7
|
+
rawEvent: Record<string, unknown>,
|
|
8
|
+
) => unknown | Promise<unknown>;
|
|
9
|
+
|
|
10
|
+
export interface DiscoveredOnValueCreated {
|
|
11
|
+
exportName: string;
|
|
12
|
+
reference: string;
|
|
13
|
+
instance: string;
|
|
14
|
+
location?: string;
|
|
15
|
+
callable: RtdbCreatedCallable;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface UnsupportedOnValueCreated {
|
|
19
|
+
exportName: string;
|
|
20
|
+
eventType: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface OnValueCreatedInspection {
|
|
24
|
+
triggers: DiscoveredOnValueCreated[];
|
|
25
|
+
unsupported: UnsupportedOnValueCreated[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const CREATED_EVENT_TYPE = 'google.firebase.database.ref.v1.created';
|
|
29
|
+
|
|
30
|
+
export type FirebaseEndpointFunction = RtdbCreatedCallable & {
|
|
31
|
+
__endpoint?: {
|
|
32
|
+
omit?: unknown;
|
|
33
|
+
region?: unknown;
|
|
34
|
+
eventTrigger?: {
|
|
35
|
+
eventType?: unknown;
|
|
36
|
+
eventFilters?: Record<string, unknown>;
|
|
37
|
+
eventFilterPathPatterns?: Record<string, unknown>;
|
|
38
|
+
};
|
|
39
|
+
callableTrigger?: unknown;
|
|
40
|
+
httpsTrigger?: unknown;
|
|
41
|
+
scheduleTrigger?: unknown;
|
|
42
|
+
taskQueueTrigger?: unknown;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Walk exports using the same hyphenated nested names as Firebase's loader. */
|
|
47
|
+
export function listFirebaseEndpoints(
|
|
48
|
+
exported: Record<string, unknown>,
|
|
49
|
+
): Array<{ exportName: string; callable: FirebaseEndpointFunction }> {
|
|
50
|
+
const endpoints: Array<{ exportName: string; callable: FirebaseEndpointFunction }> = [];
|
|
51
|
+
const visit = (
|
|
52
|
+
value: Record<string, unknown>,
|
|
53
|
+
prefix: string,
|
|
54
|
+
ancestors: ReadonlySet<object>,
|
|
55
|
+
): void => {
|
|
56
|
+
if (ancestors.has(value)) return;
|
|
57
|
+
const nestedAncestors = new Set(ancestors).add(value);
|
|
58
|
+
for (const [name, candidate] of Object.entries(value)) {
|
|
59
|
+
const exportName = `${prefix}${name}`;
|
|
60
|
+
const callable = candidate as FirebaseEndpointFunction;
|
|
61
|
+
if (typeof candidate === 'function' && callable.__endpoint) {
|
|
62
|
+
endpoints.push({ exportName, callable });
|
|
63
|
+
} else if (typeof candidate === 'object' && candidate !== null) {
|
|
64
|
+
visit(candidate as Record<string, unknown>, `${exportName}-`, nestedAncestors);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
visit(exported, '', new Set());
|
|
69
|
+
return endpoints;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Classify unchanged v2 RTDB create exports against the admitted first slice. */
|
|
73
|
+
export function inspectOnValueCreated(
|
|
74
|
+
exported: Record<string, unknown>,
|
|
75
|
+
): OnValueCreatedInspection {
|
|
76
|
+
const triggers: DiscoveredOnValueCreated[] = [];
|
|
77
|
+
const unsupported: UnsupportedOnValueCreated[] = [];
|
|
78
|
+
for (const { exportName, callable } of listFirebaseEndpoints(exported)) {
|
|
79
|
+
const endpoint = callable.__endpoint;
|
|
80
|
+
const trigger = endpoint?.eventTrigger;
|
|
81
|
+
if (trigger?.eventType !== CREATED_EVENT_TYPE) continue;
|
|
82
|
+
if (endpoint?.omit === true) {
|
|
83
|
+
unsupported.push({
|
|
84
|
+
exportName,
|
|
85
|
+
eventType: `${CREATED_EVENT_TYPE} (omitted from emulation)`,
|
|
86
|
+
});
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (endpoint?.omit !== undefined && endpoint.omit !== false) {
|
|
90
|
+
unsupported.push({
|
|
91
|
+
exportName,
|
|
92
|
+
eventType: `${CREATED_EVENT_TYPE} (unsupported dynamic omit option)`,
|
|
93
|
+
});
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const reference = trigger.eventFilterPathPatterns?.ref;
|
|
97
|
+
const exactInstance = trigger.eventFilters?.instance;
|
|
98
|
+
const instancePattern = trigger.eventFilterPathPatterns?.instance;
|
|
99
|
+
if (typeof reference !== 'string') continue;
|
|
100
|
+
if (typeof exactInstance !== 'string' && instancePattern !== '*') {
|
|
101
|
+
if (typeof instancePattern === 'string') {
|
|
102
|
+
unsupported.push({
|
|
103
|
+
exportName,
|
|
104
|
+
eventType: `${CREATED_EVENT_TYPE} (unsupported instance pattern: ${instancePattern})`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const instance = typeof exactInstance === 'string' ? exactInstance : '*';
|
|
110
|
+
if (!supportsRtdbReference(reference)) {
|
|
111
|
+
unsupported.push({
|
|
112
|
+
exportName,
|
|
113
|
+
eventType: `${CREATED_EVENT_TYPE} (unsupported ref pattern: ${reference})`,
|
|
114
|
+
});
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const region = endpoint?.region;
|
|
118
|
+
const location = Array.isArray(region) && typeof region[0] === 'string'
|
|
119
|
+
? region[0]
|
|
120
|
+
: undefined;
|
|
121
|
+
triggers.push({
|
|
122
|
+
exportName,
|
|
123
|
+
reference: normalizeRtdbReference(reference),
|
|
124
|
+
instance,
|
|
125
|
+
...(location === undefined ? {} : { location }),
|
|
126
|
+
callable,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return { triggers, unsupported };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function discoverOnValueCreated(
|
|
133
|
+
exported: Record<string, unknown>,
|
|
134
|
+
): DiscoveredOnValueCreated[] {
|
|
135
|
+
return inspectOnValueCreated(exported).triggers;
|
|
136
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CREATED_EVENT_TYPE, type DiscoveredOnValueCreated } from './discovery.js';
|
|
2
|
+
import type { CreatedValueProjection } from './projection.js';
|
|
3
|
+
|
|
4
|
+
export interface CreatedEventOptions {
|
|
5
|
+
id: string;
|
|
6
|
+
time: string;
|
|
7
|
+
instance: string;
|
|
8
|
+
location: string;
|
|
9
|
+
databaseHost: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type CreatedExecutionResult =
|
|
13
|
+
| { status: 'fulfilled'; event: Record<string, unknown> }
|
|
14
|
+
| { status: 'rejected'; event: Record<string, unknown>; error: unknown };
|
|
15
|
+
|
|
16
|
+
function canonicalizeRtdbValue(value: unknown): unknown {
|
|
17
|
+
if (Array.isArray(value)) return value.map(canonicalizeRtdbValue);
|
|
18
|
+
if (typeof value !== 'object' || value === null) return value;
|
|
19
|
+
return Object.fromEntries(
|
|
20
|
+
Object.keys(value as Record<string, unknown>)
|
|
21
|
+
.sort()
|
|
22
|
+
.map((key) => [
|
|
23
|
+
key,
|
|
24
|
+
canonicalizeRtdbValue((value as Record<string, unknown>)[key]),
|
|
25
|
+
]),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Invoke the real Firebase Functions wrapper and await the user's result. */
|
|
30
|
+
export async function executeOnValueCreated(
|
|
31
|
+
trigger: DiscoveredOnValueCreated,
|
|
32
|
+
projection: CreatedValueProjection,
|
|
33
|
+
options: CreatedEventOptions,
|
|
34
|
+
): Promise<CreatedExecutionResult> {
|
|
35
|
+
const event: Record<string, unknown> = {
|
|
36
|
+
specversion: '1.0',
|
|
37
|
+
id: options.id,
|
|
38
|
+
// RTDB CloudEvents use the provider resource and a `_` project segment;
|
|
39
|
+
// the concrete database identity lives in `instances/<instance>`.
|
|
40
|
+
source:
|
|
41
|
+
'//firebasedatabase.googleapis.com/projects/_' +
|
|
42
|
+
`/locations/${options.location}/instances/${options.instance}`,
|
|
43
|
+
subject: `refs/${projection.ref}`,
|
|
44
|
+
type: CREATED_EVENT_TYPE,
|
|
45
|
+
time: options.time,
|
|
46
|
+
location: options.location,
|
|
47
|
+
instance: options.instance,
|
|
48
|
+
ref: projection.ref,
|
|
49
|
+
firebasedatabasehost: options.databaseHost,
|
|
50
|
+
authtype: 'unknown',
|
|
51
|
+
authid: null,
|
|
52
|
+
data: {
|
|
53
|
+
data: null,
|
|
54
|
+
delta: canonicalizeRtdbValue(projection.value),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await trigger.callable(event);
|
|
60
|
+
return { status: 'fulfilled', event };
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return { status: 'rejected', event, error };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { discoverOnValueCreated, type DiscoveredOnValueCreated } from './discovery.js';
|
|
2
|
+
import type { RtdbTriggerDelivery } from './delivery.js';
|
|
3
|
+
import {
|
|
4
|
+
executeOnValueCreated,
|
|
5
|
+
type CreatedEventOptions,
|
|
6
|
+
type CreatedExecutionResult,
|
|
7
|
+
} from './event.js';
|
|
8
|
+
import {
|
|
9
|
+
projectValueCreates,
|
|
10
|
+
watchPath,
|
|
11
|
+
type CreatedValueProjection,
|
|
12
|
+
} from './projection.js';
|
|
13
|
+
|
|
14
|
+
export interface OnValueCreatedExecutionOptions {
|
|
15
|
+
exported: Record<string, unknown>;
|
|
16
|
+
delivery: RtdbTriggerDelivery;
|
|
17
|
+
eventOptions(
|
|
18
|
+
projection: CreatedValueProjection,
|
|
19
|
+
sequence: number,
|
|
20
|
+
trigger: DiscoveredOnValueCreated,
|
|
21
|
+
): CreatedEventOptions;
|
|
22
|
+
onExecution?(
|
|
23
|
+
result: CreatedExecutionResult,
|
|
24
|
+
trigger: DiscoveredOnValueCreated,
|
|
25
|
+
projection: CreatedValueProjection,
|
|
26
|
+
): void;
|
|
27
|
+
onDeliveryError?(error: unknown, trigger: DiscoveredOnValueCreated): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface OnValueCreatedExecutionHost {
|
|
31
|
+
readonly triggerCount: number;
|
|
32
|
+
readonly ready: Promise<void>;
|
|
33
|
+
idle(): Promise<void>;
|
|
34
|
+
close(): void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Subscribe discovered functions and serialize their snapshot executions. */
|
|
38
|
+
export function startOnValueCreatedExecution(
|
|
39
|
+
options: OnValueCreatedExecutionOptions,
|
|
40
|
+
): OnValueCreatedExecutionHost {
|
|
41
|
+
const triggers = discoverOnValueCreated(options.exported);
|
|
42
|
+
const unsubscribe: Array<() => void> = [];
|
|
43
|
+
let closed = false;
|
|
44
|
+
let sequence = 0;
|
|
45
|
+
let tail = Promise.resolve();
|
|
46
|
+
let baselinesRemaining = triggers.length;
|
|
47
|
+
let readinessFailed = false;
|
|
48
|
+
let markReady!: () => void;
|
|
49
|
+
let failReady!: (error: unknown) => void;
|
|
50
|
+
const ready = new Promise<void>((resolve, reject) => {
|
|
51
|
+
markReady = resolve;
|
|
52
|
+
failReady = reject;
|
|
53
|
+
});
|
|
54
|
+
if (baselinesRemaining === 0) markReady();
|
|
55
|
+
|
|
56
|
+
for (const trigger of triggers) {
|
|
57
|
+
const path = watchPath(trigger.reference);
|
|
58
|
+
let hasBaseline = false;
|
|
59
|
+
let previous: unknown;
|
|
60
|
+
unsubscribe.push(
|
|
61
|
+
options.delivery.subscribe(
|
|
62
|
+
path,
|
|
63
|
+
(next) => {
|
|
64
|
+
if (closed) return;
|
|
65
|
+
if (!hasBaseline) {
|
|
66
|
+
previous = next;
|
|
67
|
+
hasBaseline = true;
|
|
68
|
+
baselinesRemaining -= 1;
|
|
69
|
+
if (baselinesRemaining === 0 && !readinessFailed) markReady();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const before = previous;
|
|
73
|
+
previous = next;
|
|
74
|
+
for (const projection of projectValueCreates(trigger.reference, {
|
|
75
|
+
path,
|
|
76
|
+
before,
|
|
77
|
+
after: next,
|
|
78
|
+
})) {
|
|
79
|
+
const deliverySequence = ++sequence;
|
|
80
|
+
tail = tail.then(async () => {
|
|
81
|
+
const result = await executeOnValueCreated(
|
|
82
|
+
trigger,
|
|
83
|
+
projection,
|
|
84
|
+
options.eventOptions(projection, deliverySequence, trigger),
|
|
85
|
+
);
|
|
86
|
+
options.onExecution?.(result, trigger, projection);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
(error) => {
|
|
91
|
+
if (!hasBaseline && !readinessFailed) {
|
|
92
|
+
readinessFailed = true;
|
|
93
|
+
failReady(error);
|
|
94
|
+
}
|
|
95
|
+
options.onDeliveryError?.(error, trigger);
|
|
96
|
+
},
|
|
97
|
+
),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
triggerCount: triggers.length,
|
|
103
|
+
ready,
|
|
104
|
+
idle: () => tail,
|
|
105
|
+
close() {
|
|
106
|
+
if (closed) return;
|
|
107
|
+
closed = true;
|
|
108
|
+
for (const stop of unsubscribe.splice(0)) stop();
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|