@rudderhq/server 0.6.6-canary.8 → 0.7.0
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/bootstrap/register-api-routes.d.ts.map +1 -1
- package/dist/bootstrap/register-api-routes.js +6 -0
- package/dist/bootstrap/register-api-routes.js.map +1 -1
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +152 -9
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +3 -3
- package/dist/routes/access.d.ts.map +1 -1
- package/dist/routes/access.js +6 -0
- package/dist/routes/access.js.map +1 -1
- package/dist/routes/agents.management-routes.d.ts.map +1 -1
- package/dist/routes/agents.management-routes.js +22 -0
- package/dist/routes/agents.management-routes.js.map +1 -1
- package/dist/routes/ai-search.d.ts +3 -0
- package/dist/routes/ai-search.d.ts.map +1 -0
- package/dist/routes/ai-search.js +17 -0
- package/dist/routes/ai-search.js.map +1 -0
- package/dist/routes/app-builder.d.ts +3 -0
- package/dist/routes/app-builder.d.ts.map +1 -0
- package/dist/routes/app-builder.js +305 -0
- package/dist/routes/app-builder.js.map +1 -0
- package/dist/routes/browser.d.ts.map +1 -1
- package/dist/routes/browser.js +6 -2
- package/dist/routes/browser.js.map +1 -1
- package/dist/routes/chats.annotation-routes.d.ts.map +1 -1
- package/dist/routes/chats.annotation-routes.js +10 -1
- package/dist/routes/chats.annotation-routes.js.map +1 -1
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +1 -0
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/chats.runtime-controls.d.ts +1 -0
- package/dist/routes/chats.runtime-controls.d.ts.map +1 -1
- package/dist/routes/chats.stream-routes.d.ts.map +1 -1
- package/dist/routes/chats.stream-routes.js +52 -4
- package/dist/routes/chats.stream-routes.js.map +1 -1
- package/dist/routes/chats.stream-support.d.ts +1 -0
- package/dist/routes/chats.stream-support.d.ts.map +1 -1
- package/dist/routes/chats.stream-support.js +8 -0
- package/dist/routes/chats.stream-support.js.map +1 -1
- package/dist/routes/health.d.ts.map +1 -1
- package/dist/routes/health.js +1 -0
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +2 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/issues.mutations.d.ts +1 -1
- package/dist/routes/issues.mutations.d.ts.map +1 -1
- package/dist/routes/issues.mutations.js +71 -7
- package/dist/routes/issues.mutations.js.map +1 -1
- package/dist/routes/onboarding.d.ts.map +1 -1
- package/dist/routes/onboarding.js +35 -32
- package/dist/routes/onboarding.js.map +1 -1
- package/dist/routes/orgs.js +1 -1
- package/dist/routes/orgs.js.map +1 -1
- package/dist/routes/product-analytics-collector.d.ts +10 -0
- package/dist/routes/product-analytics-collector.d.ts.map +1 -0
- package/dist/routes/product-analytics-collector.js +28 -0
- package/dist/routes/product-analytics-collector.js.map +1 -0
- package/dist/routes/product-analytics.d.ts +3 -0
- package/dist/routes/product-analytics.d.ts.map +1 -0
- package/dist/routes/product-analytics.js +169 -0
- package/dist/routes/product-analytics.js.map +1 -0
- package/dist/routes/website-metadata.d.ts +2 -2
- package/dist/routes/website-metadata.d.ts.map +1 -1
- package/dist/routes/website-metadata.js +15 -2
- package/dist/routes/website-metadata.js.map +1 -1
- package/dist/services/activity.d.ts +3 -1
- package/dist/services/activity.d.ts.map +1 -1
- package/dist/services/activity.js +55 -6
- package/dist/services/activity.js.map +1 -1
- package/dist/services/agent-run-context.d.ts.map +1 -1
- package/dist/services/agent-run-context.js +8 -2
- package/dist/services/agent-run-context.js.map +1 -1
- package/dist/services/agents.d.ts +84 -13
- package/dist/services/agents.d.ts.map +1 -1
- package/dist/services/agents.js +35 -1
- package/dist/services/agents.js.map +1 -1
- package/dist/services/ai-search.d.ts +6 -0
- package/dist/services/ai-search.d.ts.map +1 -0
- package/dist/services/ai-search.js +238 -0
- package/dist/services/ai-search.js.map +1 -0
- package/dist/services/ai-search.test.d.ts +2 -0
- package/dist/services/ai-search.test.d.ts.map +1 -0
- package/dist/services/ai-search.test.js +52 -0
- package/dist/services/ai-search.test.js.map +1 -0
- package/dist/services/app-builder.d.ts +13 -0
- package/dist/services/app-builder.d.ts.map +1 -0
- package/dist/services/app-builder.js +241 -0
- package/dist/services/app-builder.js.map +1 -0
- package/dist/services/approvals.d.ts +3 -3
- package/dist/services/automations.d.ts +1 -0
- package/dist/services/automations.d.ts.map +1 -1
- package/dist/services/automations.js +33 -1
- package/dist/services/automations.js.map +1 -1
- package/dist/services/browser-broker.d.ts +1 -0
- package/dist/services/browser-broker.d.ts.map +1 -1
- package/dist/services/browser-broker.js +34 -2
- package/dist/services/browser-broker.js.map +1 -1
- package/dist/services/browser-broker.test.js +73 -0
- package/dist/services/browser-broker.test.js.map +1 -1
- package/dist/services/budgets.d.ts +1 -6
- package/dist/services/budgets.d.ts.map +1 -1
- package/dist/services/budgets.js +30 -103
- package/dist/services/budgets.js.map +1 -1
- package/dist/services/chat-annotation-copy-lineage.d.ts.map +1 -1
- package/dist/services/chat-annotation-copy-lineage.js +6 -1
- package/dist/services/chat-annotation-copy-lineage.js.map +1 -1
- package/dist/services/chat-assistant.annotations.d.ts.map +1 -1
- package/dist/services/chat-assistant.annotations.js +4 -1
- package/dist/services/chat-assistant.annotations.js.map +1 -1
- package/dist/services/chat-assistant.annotations.test.js +18 -2
- package/dist/services/chat-assistant.annotations.test.js.map +1 -1
- package/dist/services/chat-family-groups.d.ts +1 -1
- package/dist/services/chat-family-groups.d.ts.map +1 -1
- package/dist/services/chat-family-groups.js +17 -1
- package/dist/services/chat-family-groups.js.map +1 -1
- package/dist/services/chat-inline-annotation-validation.d.ts.map +1 -1
- package/dist/services/chat-inline-annotation-validation.js +301 -8
- package/dist/services/chat-inline-annotation-validation.js.map +1 -1
- package/dist/services/chat-inline-annotations.test.js +249 -1
- package/dist/services/chat-inline-annotations.test.js.map +1 -1
- package/dist/services/chat-message-branch.d.ts +5 -0
- package/dist/services/chat-message-branch.d.ts.map +1 -0
- package/dist/services/chat-message-branch.js +9 -0
- package/dist/services/chat-message-branch.js.map +1 -0
- package/dist/services/chat-work-manifest.d.ts.map +1 -1
- package/dist/services/chat-work-manifest.js +6 -0
- package/dist/services/chat-work-manifest.js.map +1 -1
- package/dist/services/chats.create.d.ts +2 -0
- package/dist/services/chats.create.d.ts.map +1 -1
- package/dist/services/chats.create.js +76 -4
- package/dist/services/chats.create.js.map +1 -1
- package/dist/services/chats.d.ts +33 -13
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.inline-visual-persistence.d.ts.map +1 -1
- package/dist/services/chats.inline-visual-persistence.js +7 -0
- package/dist/services/chats.inline-visual-persistence.js.map +1 -1
- package/dist/services/chats.js +46 -24
- package/dist/services/chats.js.map +1 -1
- package/dist/services/dashboard.d.ts +0 -1
- package/dist/services/dashboard.d.ts.map +1 -1
- package/dist/services/dashboard.js +0 -1
- package/dist/services/dashboard.js.map +1 -1
- package/dist/services/heartbeat-run-summary.d.ts.map +1 -1
- package/dist/services/heartbeat-run-summary.js +6 -0
- package/dist/services/heartbeat-run-summary.js.map +1 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +2 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/instance-settings.d.ts.map +1 -1
- package/dist/services/instance-settings.js +4 -6
- package/dist/services/instance-settings.js.map +1 -1
- package/dist/services/issue-approvals.d.ts +1 -1
- package/dist/services/issues.d.ts +2 -2
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.js +69 -2
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.import.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.import.js +1 -0
- package/dist/services/knowledge-portability/organization-portability.import.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.js +6 -2
- package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
- package/dist/services/legacy-operator-state.d.ts +14 -0
- package/dist/services/legacy-operator-state.d.ts.map +1 -0
- package/dist/services/legacy-operator-state.js +251 -0
- package/dist/services/legacy-operator-state.js.map +1 -0
- package/dist/services/local-account-auth.d.ts.map +1 -1
- package/dist/services/local-account-auth.js +21 -3
- package/dist/services/local-account-auth.js.map +1 -1
- package/dist/services/messenger-custom-groups.d.ts +15 -0
- package/dist/services/messenger-custom-groups.d.ts.map +1 -0
- package/dist/services/messenger-custom-groups.js +155 -0
- package/dist/services/messenger-custom-groups.js.map +1 -0
- package/dist/services/messenger-saved-views.d.ts +32 -8
- package/dist/services/messenger-saved-views.d.ts.map +1 -1
- package/dist/services/messenger-saved-views.js +67 -2
- package/dist/services/messenger-saved-views.js.map +1 -1
- package/dist/services/messenger.d.ts +3 -3
- package/dist/services/messenger.d.ts.map +1 -1
- package/dist/services/messenger.js +110 -166
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/orgs.d.ts +11 -2
- package/dist/services/orgs.d.ts.map +1 -1
- package/dist/services/orgs.js +23 -1
- package/dist/services/orgs.js.map +1 -1
- package/dist/services/product-analytics-collector.d.ts +86 -0
- package/dist/services/product-analytics-collector.d.ts.map +1 -0
- package/dist/services/product-analytics-collector.js +172 -0
- package/dist/services/product-analytics-collector.js.map +1 -0
- package/dist/services/product-analytics-collector.test.d.ts +2 -0
- package/dist/services/product-analytics-collector.test.d.ts.map +1 -0
- package/dist/services/product-analytics-collector.test.js +81 -0
- package/dist/services/product-analytics-collector.test.js.map +1 -0
- package/dist/services/product-analytics.d.ts +385 -0
- package/dist/services/product-analytics.d.ts.map +1 -0
- package/dist/services/product-analytics.js +698 -0
- package/dist/services/product-analytics.js.map +1 -0
- package/dist/services/product-analytics.test.d.ts +2 -0
- package/dist/services/product-analytics.test.d.ts.map +1 -0
- package/dist/services/product-analytics.test.js +136 -0
- package/dist/services/product-analytics.test.js.map +1 -0
- package/dist/services/projects.d.ts +1 -1
- package/dist/services/runtime-kernel/heartbeat.d.ts +4 -0
- package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.js +53 -1
- package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.misc.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.misc.js +5 -10
- package/dist/services/runtime-kernel/heartbeat.misc.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.terminal.d.ts +3 -0
- package/dist/services/runtime-kernel/heartbeat.terminal.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.terminal.js +56 -1
- package/dist/services/runtime-kernel/heartbeat.terminal.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.wakeup.js +9 -2
- package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -1
- package/dist/services/secrets.d.ts +1 -1
- package/dist/services/side-chats.d.ts +3 -0
- package/dist/services/side-chats.d.ts.map +1 -1
- package/dist/services/side-chats.js +30 -15
- package/dist/services/side-chats.js.map +1 -1
- package/dist/services/website-metadata.d.ts +3 -0
- package/dist/services/website-metadata.d.ts.map +1 -1
- package/dist/services/website-metadata.js +331 -88
- package/dist/services/website-metadata.js.map +1 -1
- package/package.json +16 -14
- package/resources/bundled-skills/app-builder/SKILL.md +104 -0
- package/resources/bundled-skills/app-builder/agents/openai.yaml +14 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/.env.example +3 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/app/api/%5F%5Frudder/health/route.ts +20 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/app/api/contacts/[id]/route.ts +39 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/app/api/contacts/route.ts +31 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/app/api/data/export/route.ts +19 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/app/api/data/import/route.ts +40 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/app/globals.css +32 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/app/layout.tsx +15 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/app/page.tsx +9 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/components/contacts-workspace.tsx +292 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/button.tsx +42 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/card.tsx +19 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/input.tsx +17 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/components/ui/label.tsx +6 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/data/.gitkeep +1 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/drizzle.config.ts +18 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/instrumentation.ts +5 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/lib/data-transfer.ts +44 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/lib/db/client.ts +78 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/lib/db/schema.ts +35 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/lib/domain.ts +18 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/lib/jobs/runner.ts +56 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/lib/utils.ts +6 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/migrations/0000_app_builder_foundation.sql +27 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/migrations/meta/_journal.json +13 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/next-env.d.ts +6 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/next.config.ts +8 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/package.json +47 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/playwright.config.ts +30 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/pnpm-lock.yaml +2687 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/postcss.config.mjs +5 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/rudder.app.json +32 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/scripts/migrate.ts +21 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/scripts/seed.ts +31 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/scripts/snapshot.ts +17 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/tests/e2e/app.spec.ts +50 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/tests/unit/data-transfer.test.ts +28 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/tests/unit/domain.test.ts +23 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/tsconfig.json +41 -0
- package/resources/bundled-skills/app-builder/assets/scaffold/vitest.config.ts +14 -0
- package/resources/bundled-skills/app-builder/evals/evals.json +65 -0
- package/resources/bundled-skills/app-builder/references/data-safety.md +38 -0
- package/resources/bundled-skills/app-builder/references/design-guidelines.md +20 -0
- package/resources/bundled-skills/app-builder/references/migrations-and-promotion.md +36 -0
- package/resources/bundled-skills/app-builder/references/scaffold-contract.md +77 -0
- package/resources/bundled-skills/app-builder/references/verification.md +23 -0
- package/resources/bundled-skills/app-builder/scripts/scaffold.mjs +58 -0
- package/resources/bundled-skills/app-builder/scripts/validate-manifest.mjs +63 -0
- package/skills/app-builder/SKILL.md +104 -0
- package/skills/app-builder/agents/openai.yaml +14 -0
- package/skills/app-builder/assets/scaffold/.env.example +3 -0
- package/skills/app-builder/assets/scaffold/app/api/%5F%5Frudder/health/route.ts +20 -0
- package/skills/app-builder/assets/scaffold/app/api/contacts/[id]/route.ts +39 -0
- package/skills/app-builder/assets/scaffold/app/api/contacts/route.ts +31 -0
- package/skills/app-builder/assets/scaffold/app/api/data/export/route.ts +19 -0
- package/skills/app-builder/assets/scaffold/app/api/data/import/route.ts +40 -0
- package/skills/app-builder/assets/scaffold/app/globals.css +32 -0
- package/skills/app-builder/assets/scaffold/app/layout.tsx +15 -0
- package/skills/app-builder/assets/scaffold/app/page.tsx +9 -0
- package/skills/app-builder/assets/scaffold/components/contacts-workspace.tsx +292 -0
- package/skills/app-builder/assets/scaffold/components/ui/button.tsx +42 -0
- package/skills/app-builder/assets/scaffold/components/ui/card.tsx +19 -0
- package/skills/app-builder/assets/scaffold/components/ui/input.tsx +17 -0
- package/skills/app-builder/assets/scaffold/components/ui/label.tsx +6 -0
- package/skills/app-builder/assets/scaffold/data/.gitkeep +1 -0
- package/skills/app-builder/assets/scaffold/drizzle.config.ts +18 -0
- package/skills/app-builder/assets/scaffold/instrumentation.ts +5 -0
- package/skills/app-builder/assets/scaffold/lib/data-transfer.ts +44 -0
- package/skills/app-builder/assets/scaffold/lib/db/client.ts +78 -0
- package/skills/app-builder/assets/scaffold/lib/db/schema.ts +35 -0
- package/skills/app-builder/assets/scaffold/lib/domain.ts +18 -0
- package/skills/app-builder/assets/scaffold/lib/jobs/runner.ts +56 -0
- package/skills/app-builder/assets/scaffold/lib/utils.ts +6 -0
- package/skills/app-builder/assets/scaffold/migrations/0000_app_builder_foundation.sql +27 -0
- package/skills/app-builder/assets/scaffold/migrations/meta/_journal.json +13 -0
- package/skills/app-builder/assets/scaffold/next-env.d.ts +6 -0
- package/skills/app-builder/assets/scaffold/next.config.ts +8 -0
- package/skills/app-builder/assets/scaffold/package.json +47 -0
- package/skills/app-builder/assets/scaffold/playwright.config.ts +30 -0
- package/skills/app-builder/assets/scaffold/pnpm-lock.yaml +2687 -0
- package/skills/app-builder/assets/scaffold/postcss.config.mjs +5 -0
- package/skills/app-builder/assets/scaffold/rudder.app.json +32 -0
- package/skills/app-builder/assets/scaffold/scripts/migrate.ts +21 -0
- package/skills/app-builder/assets/scaffold/scripts/seed.ts +31 -0
- package/skills/app-builder/assets/scaffold/scripts/snapshot.ts +17 -0
- package/skills/app-builder/assets/scaffold/tests/e2e/app.spec.ts +50 -0
- package/skills/app-builder/assets/scaffold/tests/unit/data-transfer.test.ts +28 -0
- package/skills/app-builder/assets/scaffold/tests/unit/domain.test.ts +23 -0
- package/skills/app-builder/assets/scaffold/tsconfig.json +41 -0
- package/skills/app-builder/assets/scaffold/vitest.config.ts +14 -0
- package/skills/app-builder/evals/evals.json +65 -0
- package/skills/app-builder/references/data-safety.md +38 -0
- package/skills/app-builder/references/design-guidelines.md +20 -0
- package/skills/app-builder/references/migrations-and-promotion.md +36 -0
- package/skills/app-builder/references/scaffold-contract.md +77 -0
- package/skills/app-builder/references/verification.md +23 -0
- package/skills/app-builder/scripts/scaffold.mjs +58 -0
- package/skills/app-builder/scripts/validate-manifest.mjs +63 -0
- package/ui-dist/assets/LibraryLiveSurface-DeUIV5N_.js +1 -0
- package/ui-dist/assets/{abnfDiagram-VRR7QNED-BdM6grBK.js → abnfDiagram-VRR7QNED-BaoLM9Pd.js} +1 -1
- package/ui-dist/assets/{arc-B8Tukeix.js → arc-Cx0L6o2z.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-ZJ3FMSHR-DP_lYJIS.js → architectureDiagram-ZJ3FMSHR-zVfZP7v4.js} +1 -1
- package/ui-dist/assets/{blockDiagram-677ZJIJ3-D_QKZNjP.js → blockDiagram-677ZJIJ3-BiWWFP9R.js} +1 -1
- package/ui-dist/assets/{c4Diagram-LMCZKHZV-DJwVavN4.js → c4Diagram-LMCZKHZV-CX65GSJ6.js} +1 -1
- package/ui-dist/assets/channel-CHc3ibOz.js +1 -0
- package/ui-dist/assets/{chunk-2Q5K7J3B-BjdKg2cx.js → chunk-2Q5K7J3B-CH6PlelX.js} +1 -1
- package/ui-dist/assets/{chunk-32BRIVSS-C_u4l725.js → chunk-32BRIVSS-C7ZEXYO8.js} +1 -1
- package/ui-dist/assets/{chunk-5VM5RSS4-DtzEQCc9.js → chunk-5VM5RSS4-CyInWuue.js} +1 -1
- package/ui-dist/assets/{chunk-EX3LRPZG-Ds_gCBVz.js → chunk-EX3LRPZG-D58qFIdM.js} +1 -1
- package/ui-dist/assets/{chunk-JWPE2WC7-CHX9VuQf.js → chunk-JWPE2WC7-CJ3Qx30f.js} +1 -1
- package/ui-dist/assets/{chunk-MOJQB5TN-D_pj9OWe.js → chunk-MOJQB5TN-BO4bKVLH.js} +1 -1
- package/ui-dist/assets/{chunk-RYQCIY6F-aMYmnUaJ.js → chunk-RYQCIY6F-DxBmxcYc.js} +1 -1
- package/ui-dist/assets/{chunk-V7JOEXUC-Db3LrL-P.js → chunk-V7JOEXUC-Chdnd6Lq.js} +1 -1
- package/ui-dist/assets/{chunk-VR4S4FIN-BssuUpMr.js → chunk-VR4S4FIN-GkW_x_pD.js} +1 -1
- package/ui-dist/assets/{chunk-XXDRQBXY-DVzY-_Tv.js → chunk-XXDRQBXY-BJyBGcSF.js} +1 -1
- package/ui-dist/assets/classDiagram-OUVF2IWQ-9iLZpz3t.js +1 -0
- package/ui-dist/assets/classDiagram-v2-EOCWNBFH-9iLZpz3t.js +1 -0
- package/ui-dist/assets/{cose-bilkent-JH36ORCC-Ch-fM2pW.js → cose-bilkent-JH36ORCC-BWP2r8TX.js} +1 -1
- package/ui-dist/assets/{cynefin-VYW2F7L2-C7gJeesr.js → cynefin-VYW2F7L2-C08dfI3w.js} +1 -1
- package/ui-dist/assets/{cynefinDiagram-TSTJHNR4-BwMpD6aZ.js → cynefinDiagram-TSTJHNR4-h6dXjTaO.js} +1 -1
- package/ui-dist/assets/{dagre-VKFMJZFB-8TgHu2tj.js → dagre-VKFMJZFB-CWDgoFQD.js} +1 -1
- package/ui-dist/assets/{diagram-FQU43EPY-ChODej8C.js → diagram-FQU43EPY-CK1j48d-.js} +1 -1
- package/ui-dist/assets/{diagram-G47NLZAW-CVq1GXvV.js → diagram-G47NLZAW-CoSiG57q.js} +1 -1
- package/ui-dist/assets/{diagram-NH7WQ7WH-D1IO7xe3.js → diagram-NH7WQ7WH-DRG-xXGQ.js} +1 -1
- package/ui-dist/assets/{diagram-OA4YK3LP-CKlttJto.js → diagram-OA4YK3LP-D04mMwYs.js} +1 -1
- package/ui-dist/assets/{diagram-WEI45ONY-DigjsORo.js → diagram-WEI45ONY-ZkC1ZQMq.js} +1 -1
- package/ui-dist/assets/{ebnfDiagram-CCIWWBDH-CZwO7RGO.js → ebnfDiagram-CCIWWBDH-BxMIw9PZ.js} +1 -1
- package/ui-dist/assets/{erDiagram-Q63AITRT-C3fFVPod.js → erDiagram-Q63AITRT-B4Zwbn84.js} +1 -1
- package/ui-dist/assets/{flowDiagram-23GEKE2U-CpccWfFo.js → flowDiagram-23GEKE2U-D5pYz8Px.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-NO4QXBWP-g3KL--4W.js → ganttDiagram-NO4QXBWP-RSramodc.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-IHSO6WYX-Bn59AW1B.js → gitGraphDiagram-IHSO6WYX-BFQfCkMd.js} +1 -1
- package/ui-dist/assets/{graph-LZSMW9ku.js → graph-DuSS8poS.js} +1 -1
- package/ui-dist/assets/{index-CPIoQKcG.js → index-5tzBDABj.js} +1 -1
- package/ui-dist/assets/index-9U78M9Np.js +1 -0
- package/ui-dist/assets/index-B-NP8fOI.js +1 -0
- package/ui-dist/assets/index-BADa8mxG.js +1 -0
- package/ui-dist/assets/index-BGcgy63z.js +1 -0
- package/ui-dist/assets/index-BKXUkeeU.js +1 -0
- package/ui-dist/assets/{index-CrU5g0XU.js → index-BU0KLNTt.js} +588 -558
- package/ui-dist/assets/index-BpvytaKr.js +1 -0
- package/ui-dist/assets/index-CHbXMgL0.js +7 -0
- package/ui-dist/assets/index-Cyq94-QT.js +1 -0
- package/ui-dist/assets/{index-BI9d3zXw.js → index-DRbt_Svi.js} +2 -2
- package/ui-dist/assets/index-De3vaw_P.js +1 -0
- package/ui-dist/assets/index-DzdRgH-y.js +1 -0
- package/ui-dist/assets/index-QrRs-r-4.css +1 -0
- package/ui-dist/assets/index-olsHIcXs.js +1 -0
- package/ui-dist/assets/index-wZlfBtwh.js +1 -0
- package/ui-dist/assets/{infoDiagram-FWYZ7A6U-uxPSmmcv.js → infoDiagram-FWYZ7A6U-f5i4pO3J.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-FXEZZL3T-BeKQmaaL.js → ishikawaDiagram-FXEZZL3T-CVEdv5i_.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-5HDEW3XC-Cnqh1Fyp.js → journeyDiagram-5HDEW3XC-Cvp7vWcy.js} +1 -1
- package/ui-dist/assets/{kanban-definition-HUTT4EX6-DRvoG8e7.js → kanban-definition-HUTT4EX6-C6dURjjK.js} +1 -1
- package/ui-dist/assets/{layout-By7bOuHZ.js → layout-DxdOu9MY.js} +1 -1
- package/ui-dist/assets/{linear-D-kfwm1O.js → linear-CRi8RB_7.js} +1 -1
- package/ui-dist/assets/{map-d3zaWROJ.js → map-BlpDrw_l.js} +1 -1
- package/ui-dist/assets/{mermaid.core-1JtOXpMA.js → mermaid.core-2Gz9YusH.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-LN4V7U3C-D8Jge-_m.js → mindmap-definition-LN4V7U3C-BmSjJLTI.js} +1 -1
- package/ui-dist/assets/{pegDiagram-2B236MQR-UCd9-Gfd.js → pegDiagram-2B236MQR-BTUboUw8.js} +1 -1
- package/ui-dist/assets/{pieDiagram-ENE6RG2P-DffPYn-P.js → pieDiagram-ENE6RG2P-CVPP8SYv.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-ABIIQ3AL-Ds1lQ25G.js → quadrantDiagram-ABIIQ3AL-BG4FmirT.js} +1 -1
- package/ui-dist/assets/{railroadDiagram-RFXS5EU6-P_pLStbY.js → railroadDiagram-RFXS5EU6-DFRNo9hF.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-TGXJPOKE-E_1U7w_T.js → requirementDiagram-TGXJPOKE-DpnVF9qP.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-HTMAVEWB-DFgwdZz7.js → sankeyDiagram-HTMAVEWB-D1ETFdSE.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-DBY2YBRQ-CbI2EnTH.js → sequenceDiagram-DBY2YBRQ-CNqzB_AL.js} +1 -1
- package/ui-dist/assets/{sizeCapture-X5ZJPWSS-Axl0diWc.js → sizeCapture-X5ZJPWSS-DuoMCjdH.js} +1 -1
- package/ui-dist/assets/{stateDiagram-2N3HPSRC-DxZLENhX.js → stateDiagram-2N3HPSRC-fmHeKL8l.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-DajAVikh.js +1 -0
- package/ui-dist/assets/swimlanes-5IMT3BWC-hjosaIhT.js +2 -0
- package/ui-dist/assets/swimlanesDiagram-G3AALYLV-DJjG9OJ-.js +8 -0
- package/ui-dist/assets/{timeline-definition-FHXFAJF6-B0Ha5bZS.js → timeline-definition-FHXFAJF6-xoMEClc-.js} +1 -1
- package/ui-dist/assets/{vennDiagram-L72KCM5P-BskD_1N5.js → vennDiagram-L72KCM5P-sdSXQfX-.js} +1 -1
- package/ui-dist/assets/{wardleyDiagram-EHGQE667-1wE4bYQ8.js → wardleyDiagram-EHGQE667-BbQBSKlD.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-FW5EYKEG-DfcOAfb8.js → xychartDiagram-FW5EYKEG-BfAtadyK.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/LibraryLiveSurface-CUgoFYvG.js +0 -1
- package/ui-dist/assets/channel-Cd4sqN24.js +0 -1
- package/ui-dist/assets/classDiagram-OUVF2IWQ-Bi3tsM-q.js +0 -1
- package/ui-dist/assets/classDiagram-v2-EOCWNBFH-Bi3tsM-q.js +0 -1
- package/ui-dist/assets/index-BG46qMXw.js +0 -1
- package/ui-dist/assets/index-BLUJslyp.js +0 -1
- package/ui-dist/assets/index-BQv2nBgV.js +0 -1
- package/ui-dist/assets/index-BkbdH2n-.js +0 -1
- package/ui-dist/assets/index-BqhkUdAT.js +0 -1
- package/ui-dist/assets/index-C-qWhcfC.css +0 -1
- package/ui-dist/assets/index-C3e6slEn.js +0 -1
- package/ui-dist/assets/index-C5hYxLM9.js +0 -1
- package/ui-dist/assets/index-CLETiubs.js +0 -7
- package/ui-dist/assets/index-CLnCobCG.js +0 -1
- package/ui-dist/assets/index-CQLLvEua.js +0 -1
- package/ui-dist/assets/index-CVwGLbgm.js +0 -1
- package/ui-dist/assets/index-ChTD9DV4.js +0 -1
- package/ui-dist/assets/index-CiyYjAsy.js +0 -1
- package/ui-dist/assets/index-EqVfudOJ.js +0 -1
- package/ui-dist/assets/index-ZFb9ZHAH.js +0 -6
- package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-KzYOpJ1T.js +0 -1
- package/ui-dist/assets/swimlanes-5IMT3BWC-1G26qvUm.js +0 -2
- package/ui-dist/assets/swimlanesDiagram-G3AALYLV--Vi3kUvi.js +0 -8
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { resolveKnownWebsiteIcon } from "@rudderhq/shared";
|
|
2
|
+
import ipaddr from "ipaddr.js";
|
|
2
3
|
import { JSDOM } from "jsdom";
|
|
3
4
|
import { createHash } from "node:crypto";
|
|
4
5
|
import { lookup } from "node:dns/promises";
|
|
5
|
-
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
6
7
|
import { isIP } from "node:net";
|
|
7
8
|
import path from "node:path";
|
|
9
|
+
import { Agent } from "undici";
|
|
8
10
|
import { resolveRudderInstanceRoot } from "../home-paths.js";
|
|
9
11
|
const MAX_HTML_BYTES = 256 * 1024;
|
|
10
12
|
const MAX_ICON_BYTES = 512 * 1024;
|
|
13
|
+
const MAX_PAGE_TITLE_CHARACTERS = 160;
|
|
11
14
|
const FETCH_TIMEOUT_MS = 5_000;
|
|
12
15
|
const MAX_REDIRECTS = 5;
|
|
13
16
|
const SUCCESS_CACHE_TTL_MS = 30 * 60_000;
|
|
14
17
|
const FAILURE_CACHE_TTL_MS = 5 * 60_000;
|
|
18
|
+
const MAX_METADATA_CACHE_ENTRIES = 128;
|
|
15
19
|
const ICON_DISK_CACHE_TTL_MS = 7 * 24 * 60 * 60_000;
|
|
20
|
+
const MAX_ICON_DISK_CACHE_ENTRIES = 128;
|
|
16
21
|
const FALLBACK_FAVICON_SIZE = "64";
|
|
17
22
|
const IMAGE_CONTENT_TYPES = new Set([
|
|
18
23
|
"image/gif",
|
|
@@ -26,9 +31,6 @@ const IMAGE_CONTENT_TYPES = new Set([
|
|
|
26
31
|
]);
|
|
27
32
|
function isBenchmarkNetworkIpAddress(value) {
|
|
28
33
|
const normalized = value.replace(/^\[|\]$/gu, "").toLowerCase();
|
|
29
|
-
const ipv4Mapped = normalized.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/u);
|
|
30
|
-
if (ipv4Mapped)
|
|
31
|
-
return isBenchmarkNetworkIpAddress(ipv4Mapped[1]);
|
|
32
34
|
const parts = normalized.split(".");
|
|
33
35
|
if (parts.length !== 4)
|
|
34
36
|
return false;
|
|
@@ -40,36 +42,17 @@ function isBenchmarkNetworkIpAddress(value) {
|
|
|
40
42
|
}
|
|
41
43
|
function isPrivateIpAddress(value) {
|
|
42
44
|
const normalized = value.replace(/^\[|\]$/gu, "").toLowerCase();
|
|
43
|
-
|
|
44
|
-
if (ipv4Mapped)
|
|
45
|
-
return isPrivateIpAddress(ipv4Mapped[1]);
|
|
46
|
-
if (normalized === "::1" || normalized.startsWith("fe80:") || normalized.startsWith("fc") || normalized.startsWith("fd"))
|
|
47
|
-
return true;
|
|
48
|
-
const parts = normalized.split(".");
|
|
49
|
-
if (parts.length !== 4)
|
|
50
|
-
return false;
|
|
51
|
-
const octets = parts.map((part) => Number(part));
|
|
52
|
-
if (octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255))
|
|
45
|
+
if (!ipaddr.isValid(normalized))
|
|
53
46
|
return false;
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (a === 192 && b === 0)
|
|
64
|
-
return true;
|
|
65
|
-
if (a === 192 && b === 168)
|
|
66
|
-
return true;
|
|
67
|
-
if (isBenchmarkNetworkIpAddress(normalized))
|
|
68
|
-
return true;
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
function isBlockedResolvedIpAddress(value) {
|
|
72
|
-
return isPrivateIpAddress(value) && !isBenchmarkNetworkIpAddress(value);
|
|
47
|
+
const address = ipaddr.process(normalized);
|
|
48
|
+
if (address.kind() === "ipv4") {
|
|
49
|
+
return address.range() !== "unicast"
|
|
50
|
+
|| isBenchmarkNetworkIpAddress(address.toString());
|
|
51
|
+
}
|
|
52
|
+
const bytes = address.toByteArray();
|
|
53
|
+
const isDeprecatedSiteLocal = bytes[0] === 0xfe
|
|
54
|
+
&& ((bytes[1] ?? 0) & 0xc0) === 0xc0;
|
|
55
|
+
return address.range() !== "unicast" || isDeprecatedSiteLocal;
|
|
73
56
|
}
|
|
74
57
|
function isPrivateHostname(hostname) {
|
|
75
58
|
const normalized = hostname.replace(/^\[|\]$/gu, "").toLowerCase();
|
|
@@ -101,6 +84,9 @@ export function parsePublicHttpUrl(value, options = {}) {
|
|
|
101
84
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
102
85
|
throw new Error("Only http and https URLs can be inspected");
|
|
103
86
|
}
|
|
87
|
+
if (parsed.username || parsed.password) {
|
|
88
|
+
throw new Error("Credentialed URLs cannot be inspected");
|
|
89
|
+
}
|
|
104
90
|
if (!options.allowPrivateHosts && isPrivateHostname(parsed.hostname)) {
|
|
105
91
|
throw new Error("Private network URLs cannot be inspected");
|
|
106
92
|
}
|
|
@@ -110,24 +96,120 @@ export function parsePublicHttpUrl(value, options = {}) {
|
|
|
110
96
|
function resolveWebsiteIconCacheDir() {
|
|
111
97
|
return path.resolve(resolveRudderInstanceRoot(), "data", "website-icons");
|
|
112
98
|
}
|
|
113
|
-
async function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
99
|
+
async function awaitWithAbort(promise, signal, onLateValue) {
|
|
100
|
+
const disposeLateValue = (value) => {
|
|
101
|
+
if (!onLateValue)
|
|
102
|
+
return;
|
|
103
|
+
void Promise.resolve(onLateValue(value)).catch(() => undefined);
|
|
104
|
+
};
|
|
105
|
+
if (signal.aborted) {
|
|
106
|
+
void promise.then(disposeLateValue, () => undefined);
|
|
107
|
+
throw signal.reason;
|
|
108
|
+
}
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
let settled = false;
|
|
111
|
+
const onAbort = () => {
|
|
112
|
+
if (settled)
|
|
113
|
+
return;
|
|
114
|
+
settled = true;
|
|
115
|
+
signal.removeEventListener("abort", onAbort);
|
|
116
|
+
reject(signal.reason);
|
|
117
|
+
};
|
|
118
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
119
|
+
void promise.then((value) => {
|
|
120
|
+
if (settled) {
|
|
121
|
+
disposeLateValue(value);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
settled = true;
|
|
125
|
+
signal.removeEventListener("abort", onAbort);
|
|
126
|
+
resolve(value);
|
|
127
|
+
}, (error) => {
|
|
128
|
+
if (settled)
|
|
129
|
+
return;
|
|
130
|
+
settled = true;
|
|
131
|
+
signal.removeEventListener("abort", onAbort);
|
|
132
|
+
reject(error);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
async function resolveValidatedHostAddress(url, options, signal) {
|
|
137
|
+
const hostname = url.hostname.replace(/^\[|\]$/gu, "");
|
|
138
|
+
if (options.fetchImpl || isIP(hostname))
|
|
139
|
+
return null;
|
|
140
|
+
const addresses = await awaitWithAbort(lookup(url.hostname, { all: true, verbatim: true }), signal);
|
|
141
|
+
if (addresses.length === 0)
|
|
142
|
+
throw new Error("Website hostname did not resolve");
|
|
143
|
+
if (!options.allowPrivateHosts
|
|
144
|
+
&& addresses.some((address) => isPrivateIpAddress(address.address))) {
|
|
119
145
|
throw new Error("Private network URLs cannot be inspected");
|
|
120
146
|
}
|
|
147
|
+
return addresses[0] ?? null;
|
|
148
|
+
}
|
|
149
|
+
function createPinnedDispatcher(resolvedHost) {
|
|
150
|
+
if (!resolvedHost)
|
|
151
|
+
return null;
|
|
152
|
+
return new Agent({
|
|
153
|
+
connect: {
|
|
154
|
+
autoSelectFamily: false,
|
|
155
|
+
lookup: (_hostname, _options, callback) => {
|
|
156
|
+
callback(null, resolvedHost.address, resolvedHost.family);
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async function cancelResponseBody(response) {
|
|
162
|
+
const body = response?.body;
|
|
163
|
+
if (!body || body.locked)
|
|
164
|
+
return;
|
|
165
|
+
try {
|
|
166
|
+
await body.cancel();
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Fetch aborts and peer disconnects can close the body before cancellation settles.
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function closeDispatcher(dispatcher, aborted) {
|
|
173
|
+
if (!dispatcher)
|
|
174
|
+
return;
|
|
175
|
+
try {
|
|
176
|
+
if (aborted)
|
|
177
|
+
await dispatcher.destroy();
|
|
178
|
+
else
|
|
179
|
+
await dispatcher.close();
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Cleanup must not replace the request result with a connection teardown error.
|
|
183
|
+
}
|
|
121
184
|
}
|
|
122
185
|
async function fetchWithTimeout(url, options, init) {
|
|
123
186
|
let currentUrl = parsePublicHttpUrl(url.href, options);
|
|
124
187
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
188
|
+
const controller = new AbortController();
|
|
189
|
+
const timeout = setTimeout(() => {
|
|
190
|
+
controller.abort(new Error("Website metadata request timed out"));
|
|
191
|
+
}, FETCH_TIMEOUT_MS);
|
|
192
|
+
timeout.unref?.();
|
|
193
|
+
let activeResponse = null;
|
|
194
|
+
let activeDispatcher = null;
|
|
195
|
+
let released = false;
|
|
196
|
+
const release = async () => {
|
|
197
|
+
if (released)
|
|
198
|
+
return;
|
|
199
|
+
released = true;
|
|
200
|
+
clearTimeout(timeout);
|
|
201
|
+
await cancelResponseBody(activeResponse);
|
|
202
|
+
activeResponse = null;
|
|
203
|
+
await closeDispatcher(activeDispatcher, controller.signal.aborted);
|
|
204
|
+
activeDispatcher = null;
|
|
205
|
+
};
|
|
206
|
+
try {
|
|
207
|
+
for (let redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount += 1) {
|
|
208
|
+
const resolvedHost = await resolveValidatedHostAddress(currentUrl, options, controller.signal);
|
|
209
|
+
if (controller.signal.aborted)
|
|
210
|
+
throw controller.signal.reason;
|
|
211
|
+
activeDispatcher = createPinnedDispatcher(resolvedHost);
|
|
212
|
+
const requestInit = {
|
|
131
213
|
redirect: "manual",
|
|
132
214
|
...init,
|
|
133
215
|
signal: controller.signal,
|
|
@@ -136,25 +218,59 @@ async function fetchWithTimeout(url, options, init) {
|
|
|
136
218
|
"user-agent": "RudderWebsiteMetadata/1.0",
|
|
137
219
|
...(init?.headers ?? {}),
|
|
138
220
|
},
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
221
|
+
...(activeDispatcher ? { dispatcher: activeDispatcher } : {}),
|
|
222
|
+
};
|
|
223
|
+
const response = await awaitWithAbort(fetchImpl(currentUrl.href, { ...requestInit }), controller.signal, cancelResponseBody);
|
|
224
|
+
activeResponse = response;
|
|
225
|
+
if (response.status < 300 || response.status >= 400) {
|
|
226
|
+
return {
|
|
227
|
+
response,
|
|
228
|
+
url: currentUrl,
|
|
229
|
+
signal: controller.signal,
|
|
230
|
+
release,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
142
233
|
const location = response.headers.get("location");
|
|
143
|
-
if (!location)
|
|
144
|
-
return {
|
|
234
|
+
if (!location) {
|
|
235
|
+
return {
|
|
236
|
+
response,
|
|
237
|
+
url: currentUrl,
|
|
238
|
+
signal: controller.signal,
|
|
239
|
+
release,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
await cancelResponseBody(activeResponse);
|
|
243
|
+
activeResponse = null;
|
|
244
|
+
await closeDispatcher(activeDispatcher, controller.signal.aborted);
|
|
245
|
+
activeDispatcher = null;
|
|
145
246
|
currentUrl = parsePublicHttpUrl(new URL(location, currentUrl).href, options);
|
|
146
247
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
248
|
+
throw new Error("Website metadata redirect limit exceeded");
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
await release();
|
|
252
|
+
throw error;
|
|
150
253
|
}
|
|
151
|
-
throw new Error("Website metadata redirect limit exceeded");
|
|
152
254
|
}
|
|
153
|
-
function metadataCacheKey(url) {
|
|
154
|
-
return url.href
|
|
255
|
+
function metadataCacheKey(url, purpose) {
|
|
256
|
+
return `${purpose}:${url.href}`;
|
|
155
257
|
}
|
|
156
258
|
const metadataCache = new Map();
|
|
157
259
|
const metadataInflight = new Map();
|
|
260
|
+
function pruneMetadataCache(now = Date.now()) {
|
|
261
|
+
for (const [key, entry] of metadataCache) {
|
|
262
|
+
if (entry.expiresAt <= now)
|
|
263
|
+
metadataCache.delete(key);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function makeRoomInMetadataCache() {
|
|
267
|
+
while (metadataCache.size >= MAX_METADATA_CACHE_ENTRIES) {
|
|
268
|
+
const oldestKey = metadataCache.keys().next().value;
|
|
269
|
+
if (!oldestKey)
|
|
270
|
+
break;
|
|
271
|
+
metadataCache.delete(oldestKey);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
158
274
|
async function readLimitedBuffer(response, maxBytes, options = {}) {
|
|
159
275
|
const body = response.body;
|
|
160
276
|
if (!body)
|
|
@@ -162,9 +278,15 @@ async function readLimitedBuffer(response, maxBytes, options = {}) {
|
|
|
162
278
|
const reader = body.getReader();
|
|
163
279
|
const chunks = [];
|
|
164
280
|
let total = 0;
|
|
281
|
+
const onAbort = () => {
|
|
282
|
+
void reader.cancel(options.signal?.reason).catch(() => undefined);
|
|
283
|
+
};
|
|
284
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
165
285
|
try {
|
|
166
286
|
while (true) {
|
|
167
287
|
const { value, done } = await reader.read();
|
|
288
|
+
if (options.signal?.aborted)
|
|
289
|
+
throw options.signal.reason;
|
|
168
290
|
if (done)
|
|
169
291
|
break;
|
|
170
292
|
if (!value)
|
|
@@ -178,12 +300,14 @@ async function readLimitedBuffer(response, maxBytes, options = {}) {
|
|
|
178
300
|
await reader.cancel();
|
|
179
301
|
break;
|
|
180
302
|
}
|
|
303
|
+
await reader.cancel();
|
|
181
304
|
throw new Error("Response exceeds metadata size limit");
|
|
182
305
|
}
|
|
183
306
|
chunks.push(Buffer.from(value));
|
|
184
307
|
}
|
|
185
308
|
}
|
|
186
309
|
finally {
|
|
310
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
187
311
|
reader.releaseLock();
|
|
188
312
|
}
|
|
189
313
|
return Buffer.concat(chunks);
|
|
@@ -195,6 +319,7 @@ function isInspectableUrlFailure(error) {
|
|
|
195
319
|
if (!(error instanceof Error))
|
|
196
320
|
return false;
|
|
197
321
|
return error.message === "Only http and https URLs can be inspected"
|
|
322
|
+
|| error.message === "Credentialed URLs cannot be inspected"
|
|
198
323
|
|| error.message === "Private network URLs cannot be inspected"
|
|
199
324
|
|| error.message === "Website metadata redirect limit exceeded";
|
|
200
325
|
}
|
|
@@ -231,6 +356,32 @@ function readSiteName(document) {
|
|
|
231
356
|
const title = document.querySelector("title")?.textContent?.trim();
|
|
232
357
|
return title || null;
|
|
233
358
|
}
|
|
359
|
+
function normalizePageTitle(document, value) {
|
|
360
|
+
if (!value)
|
|
361
|
+
return null;
|
|
362
|
+
const template = document.createElement("template");
|
|
363
|
+
template.innerHTML = value;
|
|
364
|
+
template.content.querySelectorAll("script, style, template, noscript").forEach((element) => element.remove());
|
|
365
|
+
const plainText = (template.content.textContent ?? "")
|
|
366
|
+
.replace(/[\s\p{Cc}\p{Cf}]+/gu, " ")
|
|
367
|
+
.trim();
|
|
368
|
+
if (!plainText)
|
|
369
|
+
return null;
|
|
370
|
+
return Array.from(plainText).slice(0, MAX_PAGE_TITLE_CHARACTERS).join("");
|
|
371
|
+
}
|
|
372
|
+
function readPageTitle(document) {
|
|
373
|
+
const candidates = [
|
|
374
|
+
document.querySelector('meta[property="og:title"]')?.getAttribute("content"),
|
|
375
|
+
document.querySelector('meta[name="twitter:title"]')?.getAttribute("content"),
|
|
376
|
+
document.querySelector("title")?.textContent,
|
|
377
|
+
];
|
|
378
|
+
for (const candidate of candidates) {
|
|
379
|
+
const title = normalizePageTitle(document, candidate);
|
|
380
|
+
if (title)
|
|
381
|
+
return title;
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
234
385
|
function iconPriority(element) {
|
|
235
386
|
const rel = new Set(linkRelTokens(element));
|
|
236
387
|
if (rel.has("icon") && rel.has("shortcut"))
|
|
@@ -263,19 +414,24 @@ function findDeclaredIcon(document, baseUrl, options) {
|
|
|
263
414
|
return null;
|
|
264
415
|
}
|
|
265
416
|
async function validateIconUrl(iconHref, options) {
|
|
417
|
+
let result = null;
|
|
266
418
|
try {
|
|
267
|
-
|
|
419
|
+
result = await fetchWithTimeout(parsePublicHttpUrl(iconHref, options), options, { method: "GET" });
|
|
420
|
+
const { response, signal, url } = result;
|
|
268
421
|
if (!response.ok)
|
|
269
422
|
return null;
|
|
270
423
|
const contentType = contentTypeBase(response.headers.get("content-type"));
|
|
271
424
|
if (!IMAGE_CONTENT_TYPES.has(contentType))
|
|
272
425
|
return null;
|
|
273
|
-
await readLimitedBuffer(response, MAX_ICON_BYTES);
|
|
426
|
+
await readLimitedBuffer(response, MAX_ICON_BYTES, { signal });
|
|
274
427
|
return url.href;
|
|
275
428
|
}
|
|
276
429
|
catch {
|
|
277
430
|
return null;
|
|
278
431
|
}
|
|
432
|
+
finally {
|
|
433
|
+
await result?.release();
|
|
434
|
+
}
|
|
279
435
|
}
|
|
280
436
|
async function findImplicitFavicon(baseUrl, options) {
|
|
281
437
|
const faviconUrl = new URL("/favicon.ico", baseUrl);
|
|
@@ -298,8 +454,15 @@ async function findProviderFavicon(pageUrl, options) {
|
|
|
298
454
|
async function resolveWebsiteMetadataUncached(value, options) {
|
|
299
455
|
const pageUrl = parsePublicHttpUrl(value, options);
|
|
300
456
|
const knownIcon = resolveKnownWebsiteIcon(pageUrl);
|
|
301
|
-
|
|
302
|
-
|
|
457
|
+
const emptyMetadata = {
|
|
458
|
+
url: pageUrl.href,
|
|
459
|
+
siteName: knownIcon?.siteName ?? null,
|
|
460
|
+
pageTitle: null,
|
|
461
|
+
iconUrl: knownIcon?.iconDataUrl ?? null,
|
|
462
|
+
};
|
|
463
|
+
// Preview rendering keeps bundled icons zero-fetch; authoring falls through to resolve a title.
|
|
464
|
+
if (knownIcon && (options.purpose ?? "preview") === "preview") {
|
|
465
|
+
return emptyMetadata;
|
|
303
466
|
}
|
|
304
467
|
let pageResult;
|
|
305
468
|
try {
|
|
@@ -308,25 +471,46 @@ async function resolveWebsiteMetadataUncached(value, options) {
|
|
|
308
471
|
catch (error) {
|
|
309
472
|
if (isInspectableUrlFailure(error))
|
|
310
473
|
throw error;
|
|
311
|
-
return
|
|
474
|
+
return emptyMetadata;
|
|
312
475
|
}
|
|
313
476
|
const { response, url: finalPageUrl } = pageResult;
|
|
314
|
-
|
|
315
|
-
|
|
477
|
+
let html;
|
|
478
|
+
try {
|
|
479
|
+
if (!response.ok)
|
|
480
|
+
return emptyMetadata;
|
|
481
|
+
const contentType = contentTypeBase(response.headers.get("content-type"));
|
|
482
|
+
if (contentType && contentType !== "text/html" && contentType !== "application/xhtml+xml") {
|
|
483
|
+
return emptyMetadata;
|
|
484
|
+
}
|
|
485
|
+
html = (await readLimitedBuffer(response, MAX_HTML_BYTES, {
|
|
486
|
+
signal: pageResult.signal,
|
|
487
|
+
truncate: true,
|
|
488
|
+
})).toString("utf8");
|
|
316
489
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
490
|
+
catch {
|
|
491
|
+
return emptyMetadata;
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
await pageResult.release();
|
|
320
495
|
}
|
|
321
|
-
const html = (await readLimitedBuffer(response, MAX_HTML_BYTES, { truncate: true })).toString("utf8");
|
|
322
496
|
const dom = new JSDOM(html, { url: finalPageUrl.href });
|
|
323
497
|
try {
|
|
324
498
|
const document = dom.window.document;
|
|
499
|
+
if ((options.purpose ?? "preview") === "authoring") {
|
|
500
|
+
return {
|
|
501
|
+
url: pageUrl.href,
|
|
502
|
+
siteName: knownIcon?.siteName ?? readSiteName(document),
|
|
503
|
+
pageTitle: readPageTitle(document),
|
|
504
|
+
iconUrl: knownIcon?.iconDataUrl ?? null,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
325
507
|
const declaredIcon = findDeclaredIcon(document, finalPageUrl, options);
|
|
326
508
|
return {
|
|
327
|
-
url:
|
|
328
|
-
siteName: readSiteName(document),
|
|
329
|
-
|
|
509
|
+
url: pageUrl.href,
|
|
510
|
+
siteName: knownIcon?.siteName ?? readSiteName(document),
|
|
511
|
+
pageTitle: readPageTitle(document),
|
|
512
|
+
iconUrl: knownIcon?.iconDataUrl
|
|
513
|
+
?? (declaredIcon ? await validateIconUrl(declaredIcon, options) : null)
|
|
330
514
|
?? await findImplicitFavicon(finalPageUrl, options)
|
|
331
515
|
?? await findProviderFavicon(finalPageUrl, options),
|
|
332
516
|
};
|
|
@@ -337,18 +521,26 @@ async function resolveWebsiteMetadataUncached(value, options) {
|
|
|
337
521
|
}
|
|
338
522
|
export async function resolveWebsiteMetadata(value, options = {}) {
|
|
339
523
|
const pageUrl = parsePublicHttpUrl(value, options);
|
|
340
|
-
const key = metadataCacheKey(pageUrl);
|
|
524
|
+
const key = metadataCacheKey(pageUrl, options.purpose ?? "preview");
|
|
341
525
|
if (!options.allowPrivateHosts && !options.fetchImpl) {
|
|
526
|
+
pruneMetadataCache();
|
|
342
527
|
const cached = metadataCache.get(key);
|
|
343
|
-
if (cached
|
|
528
|
+
if (cached) {
|
|
529
|
+
metadataCache.delete(key);
|
|
530
|
+
metadataCache.set(key, cached);
|
|
344
531
|
return cached.value;
|
|
532
|
+
}
|
|
345
533
|
const inflight = metadataInflight.get(key);
|
|
346
534
|
if (inflight)
|
|
347
535
|
return inflight;
|
|
348
536
|
const request = resolveWebsiteMetadataUncached(pageUrl.href, options)
|
|
349
537
|
.then((metadata) => {
|
|
538
|
+
pruneMetadataCache();
|
|
539
|
+
makeRoomInMetadataCache();
|
|
350
540
|
metadataCache.set(key, {
|
|
351
|
-
expiresAt: Date.now() + (metadata.iconUrl
|
|
541
|
+
expiresAt: Date.now() + (metadata.iconUrl || metadata.pageTitle || metadata.siteName
|
|
542
|
+
? SUCCESS_CACHE_TTL_MS
|
|
543
|
+
: FAILURE_CACHE_TTL_MS),
|
|
352
544
|
value: metadata,
|
|
353
545
|
});
|
|
354
546
|
return metadata;
|
|
@@ -368,20 +560,26 @@ export async function fetchWebsiteIcon(value, options = {}) {
|
|
|
368
560
|
if (cached)
|
|
369
561
|
return cached;
|
|
370
562
|
}
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
563
|
+
const result = await fetchWithTimeout(iconUrl, options);
|
|
564
|
+
try {
|
|
565
|
+
const { response, signal } = result;
|
|
566
|
+
if (!response.ok)
|
|
567
|
+
return null;
|
|
568
|
+
const contentType = contentTypeBase(response.headers.get("content-type"));
|
|
569
|
+
if (!IMAGE_CONTENT_TYPES.has(contentType))
|
|
570
|
+
return null;
|
|
571
|
+
const body = await readLimitedBuffer(response, MAX_ICON_BYTES, { signal });
|
|
572
|
+
if (body.length <= 0)
|
|
573
|
+
return null;
|
|
574
|
+
const icon = { contentType, body };
|
|
575
|
+
if (!options.allowPrivateHosts && !options.fetchImpl) {
|
|
576
|
+
await writeCachedWebsiteIcon(iconUrl.href, icon).catch(() => undefined);
|
|
577
|
+
}
|
|
578
|
+
return icon;
|
|
579
|
+
}
|
|
580
|
+
finally {
|
|
581
|
+
await result.release();
|
|
383
582
|
}
|
|
384
|
-
return icon;
|
|
385
583
|
}
|
|
386
584
|
function cachedWebsiteIconBasename(iconUrl) {
|
|
387
585
|
return createHash("sha256").update(iconUrl).digest("hex");
|
|
@@ -401,8 +599,13 @@ async function readCachedWebsiteIcon(iconUrl) {
|
|
|
401
599
|
readFile(metadataPath, "utf8"),
|
|
402
600
|
stat(bodyPath),
|
|
403
601
|
]);
|
|
404
|
-
if (Date.now() - bodyStats.mtimeMs > ICON_DISK_CACHE_TTL_MS)
|
|
602
|
+
if (Date.now() - bodyStats.mtimeMs > ICON_DISK_CACHE_TTL_MS) {
|
|
603
|
+
await Promise.all([
|
|
604
|
+
rm(metadataPath, { force: true }),
|
|
605
|
+
rm(bodyPath, { force: true }),
|
|
606
|
+
]);
|
|
405
607
|
return null;
|
|
608
|
+
}
|
|
406
609
|
const metadata = JSON.parse(rawMetadata);
|
|
407
610
|
if (metadata.url !== iconUrl || typeof metadata.contentType !== "string")
|
|
408
611
|
return null;
|
|
@@ -417,13 +620,53 @@ async function readCachedWebsiteIcon(iconUrl) {
|
|
|
417
620
|
return null;
|
|
418
621
|
}
|
|
419
622
|
}
|
|
623
|
+
async function pruneWebsiteIconDiskCache(dir, protectedBasename) {
|
|
624
|
+
const filenames = await readdir(dir);
|
|
625
|
+
const basenames = filenames
|
|
626
|
+
.filter((filename) => /^[a-f0-9]{64}\.json$/u.test(filename))
|
|
627
|
+
.map((filename) => filename.slice(0, -".json".length));
|
|
628
|
+
const entries = await Promise.all(basenames.map(async (basename) => {
|
|
629
|
+
try {
|
|
630
|
+
const [metadataStats, bodyStats] = await Promise.all([
|
|
631
|
+
stat(path.join(dir, `${basename}.json`)),
|
|
632
|
+
stat(path.join(dir, `${basename}.bin`)),
|
|
633
|
+
]);
|
|
634
|
+
return {
|
|
635
|
+
basename,
|
|
636
|
+
mtimeMs: Math.min(metadataStats.mtimeMs, bodyStats.mtimeMs),
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
catch {
|
|
640
|
+
return { basename, mtimeMs: 0 };
|
|
641
|
+
}
|
|
642
|
+
}));
|
|
643
|
+
const now = Date.now();
|
|
644
|
+
const retained = entries
|
|
645
|
+
.filter((entry) => now - entry.mtimeMs <= ICON_DISK_CACHE_TTL_MS)
|
|
646
|
+
.sort((left, right) => {
|
|
647
|
+
if (left.basename === protectedBasename)
|
|
648
|
+
return -1;
|
|
649
|
+
if (right.basename === protectedBasename)
|
|
650
|
+
return 1;
|
|
651
|
+
return right.mtimeMs - left.mtimeMs;
|
|
652
|
+
})
|
|
653
|
+
.slice(0, MAX_ICON_DISK_CACHE_ENTRIES);
|
|
654
|
+
const retainedBasenames = new Set(retained.map((entry) => entry.basename));
|
|
655
|
+
const discarded = entries.filter((entry) => !retainedBasenames.has(entry.basename));
|
|
656
|
+
await Promise.all(discarded.flatMap((entry) => [
|
|
657
|
+
rm(path.join(dir, `${entry.basename}.json`), { force: true }),
|
|
658
|
+
rm(path.join(dir, `${entry.basename}.bin`), { force: true }),
|
|
659
|
+
]));
|
|
660
|
+
}
|
|
420
661
|
async function writeCachedWebsiteIcon(iconUrl, icon) {
|
|
421
662
|
const { metadataPath, bodyPath } = cachedWebsiteIconPaths(iconUrl);
|
|
422
|
-
|
|
663
|
+
const dir = path.dirname(bodyPath);
|
|
664
|
+
await mkdir(dir, { recursive: true });
|
|
423
665
|
await Promise.all([
|
|
424
666
|
writeFile(bodyPath, icon.body),
|
|
425
667
|
writeFile(metadataPath, `${JSON.stringify({ url: iconUrl, contentType: icon.contentType })}\n`, "utf8"),
|
|
426
668
|
]);
|
|
669
|
+
await pruneWebsiteIconDiskCache(dir, path.basename(metadataPath, ".json"));
|
|
427
670
|
}
|
|
428
671
|
export function __clearWebsiteMetadataCacheForTests() {
|
|
429
672
|
metadataCache.clear();
|