@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
package/resources/bundled-skills/app-builder/assets/scaffold/components/contacts-workspace.tsx
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
|
5
|
+
import { Input } from "@/components/ui/input";
|
|
6
|
+
import { Label } from "@/components/ui/label";
|
|
7
|
+
import { Download, Plus, Search, Trash2, Upload } from "lucide-react";
|
|
8
|
+
import { FormEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
9
|
+
|
|
10
|
+
type Contact = {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
email: string;
|
|
14
|
+
company: string;
|
|
15
|
+
status: "new" | "contacted" | "replied" | "paused";
|
|
16
|
+
createdAt: string;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type Draft = {
|
|
21
|
+
name: string;
|
|
22
|
+
email: string;
|
|
23
|
+
company: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const emptyDraft: Draft = { name: "", email: "", company: "" };
|
|
27
|
+
|
|
28
|
+
export function ContactsWorkspace() {
|
|
29
|
+
const [contacts, setContacts] = useState<Contact[]>([]);
|
|
30
|
+
const [draft, setDraft] = useState<Draft>(emptyDraft);
|
|
31
|
+
const [query, setQuery] = useState("");
|
|
32
|
+
const [loading, setLoading] = useState(true);
|
|
33
|
+
const [saving, setSaving] = useState(false);
|
|
34
|
+
const [error, setError] = useState<string | null>(null);
|
|
35
|
+
const importRef = useRef<HTMLInputElement>(null);
|
|
36
|
+
|
|
37
|
+
const loadContacts = useCallback(async () => {
|
|
38
|
+
setLoading(true);
|
|
39
|
+
setError(null);
|
|
40
|
+
try {
|
|
41
|
+
const response = await fetch("/api/contacts", { cache: "no-store" });
|
|
42
|
+
if (!response.ok) throw new Error("Could not load contacts.");
|
|
43
|
+
const body = await response.json() as { contacts: Contact[] };
|
|
44
|
+
setContacts(body.contacts);
|
|
45
|
+
} catch (reason) {
|
|
46
|
+
setError(reason instanceof Error ? reason.message : "Could not load contacts.");
|
|
47
|
+
} finally {
|
|
48
|
+
setLoading(false);
|
|
49
|
+
}
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
void loadContacts();
|
|
54
|
+
}, [loadContacts]);
|
|
55
|
+
|
|
56
|
+
const visibleContacts = useMemo(() => {
|
|
57
|
+
const normalized = query.trim().toLowerCase();
|
|
58
|
+
if (!normalized) return contacts;
|
|
59
|
+
return contacts.filter((contact) => (
|
|
60
|
+
`${contact.name} ${contact.email} ${contact.company} ${contact.status}`
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
.includes(normalized)
|
|
63
|
+
));
|
|
64
|
+
}, [contacts, query]);
|
|
65
|
+
|
|
66
|
+
async function createContact(event: FormEvent) {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
setSaving(true);
|
|
69
|
+
setError(null);
|
|
70
|
+
try {
|
|
71
|
+
const response = await fetch("/api/contacts", {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { "Content-Type": "application/json" },
|
|
74
|
+
body: JSON.stringify(draft),
|
|
75
|
+
});
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
const body = await response.json().catch(() => null) as { error?: string } | null;
|
|
78
|
+
throw new Error(body?.error === "invalid_contact"
|
|
79
|
+
? "Enter a name and a valid email address."
|
|
80
|
+
: "Could not create the contact.");
|
|
81
|
+
}
|
|
82
|
+
setDraft(emptyDraft);
|
|
83
|
+
await loadContacts();
|
|
84
|
+
} catch (reason) {
|
|
85
|
+
setError(reason instanceof Error ? reason.message : "Could not create the contact.");
|
|
86
|
+
} finally {
|
|
87
|
+
setSaving(false);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function deleteContact(contact: Contact) {
|
|
92
|
+
if (!window.confirm(`Delete ${contact.name}? This cannot be undone.`)) return;
|
|
93
|
+
setError(null);
|
|
94
|
+
const response = await fetch(`/api/contacts/${contact.id}`, { method: "DELETE" });
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
setError("Could not delete the contact.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
setContacts((current) => current.filter((candidate) => candidate.id !== contact.id));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function importData(file: File) {
|
|
103
|
+
setError(null);
|
|
104
|
+
try {
|
|
105
|
+
const payload = JSON.parse(await file.text());
|
|
106
|
+
const response = await fetch("/api/data/import", {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: { "Content-Type": "application/json" },
|
|
109
|
+
body: JSON.stringify(payload),
|
|
110
|
+
});
|
|
111
|
+
if (!response.ok) throw new Error("The import file is invalid or incompatible.");
|
|
112
|
+
await loadContacts();
|
|
113
|
+
} catch (reason) {
|
|
114
|
+
setError(reason instanceof Error ? reason.message : "Could not import this file.");
|
|
115
|
+
} finally {
|
|
116
|
+
if (importRef.current) importRef.current.value = "";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div className="mx-auto grid max-w-7xl gap-6 px-4 py-6 lg:grid-cols-[minmax(0,1fr)_22rem] lg:px-8">
|
|
122
|
+
<section className="min-w-0">
|
|
123
|
+
<header className="mb-5 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
|
|
124
|
+
<div>
|
|
125
|
+
<p className="mb-1 text-sm font-medium text-[var(--primary)]">Contacts</p>
|
|
126
|
+
<h1 className="m-0 text-2xl font-semibold tracking-tight">Customer workspace</h1>
|
|
127
|
+
<p className="mb-0 mt-2 text-sm text-[var(--muted-foreground)]">
|
|
128
|
+
Keep the next conversation visible and every record on this device.
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
<div className="flex flex-wrap gap-2">
|
|
132
|
+
<input
|
|
133
|
+
ref={importRef}
|
|
134
|
+
aria-label="Import app data"
|
|
135
|
+
className="sr-only"
|
|
136
|
+
type="file"
|
|
137
|
+
accept="application/json,.json"
|
|
138
|
+
onChange={(event) => {
|
|
139
|
+
const file = event.currentTarget.files?.[0];
|
|
140
|
+
if (file) void importData(file);
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
<Button type="button" variant="outline" onClick={() => importRef.current?.click()}>
|
|
144
|
+
<Upload aria-hidden size={16} />
|
|
145
|
+
Import
|
|
146
|
+
</Button>
|
|
147
|
+
<Button asChild variant="outline">
|
|
148
|
+
<a href="/api/data/export" download>
|
|
149
|
+
<Download aria-hidden size={16} />
|
|
150
|
+
Export
|
|
151
|
+
</a>
|
|
152
|
+
</Button>
|
|
153
|
+
</div>
|
|
154
|
+
</header>
|
|
155
|
+
|
|
156
|
+
<Card>
|
|
157
|
+
<CardHeader className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
158
|
+
<div>
|
|
159
|
+
<h2 className="m-0 text-base font-semibold">All contacts</h2>
|
|
160
|
+
<p className="mb-0 mt-1 text-sm text-[var(--muted-foreground)]">
|
|
161
|
+
{contacts.length} {contacts.length === 1 ? "record" : "records"}
|
|
162
|
+
</p>
|
|
163
|
+
</div>
|
|
164
|
+
<div className="relative w-full sm:w-72">
|
|
165
|
+
<Search
|
|
166
|
+
aria-hidden
|
|
167
|
+
className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[var(--muted-foreground)]"
|
|
168
|
+
size={16}
|
|
169
|
+
/>
|
|
170
|
+
<Input
|
|
171
|
+
aria-label="Search contacts"
|
|
172
|
+
className="pl-9"
|
|
173
|
+
placeholder="Search name, email, company…"
|
|
174
|
+
value={query}
|
|
175
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</CardHeader>
|
|
179
|
+
<div aria-live="polite">
|
|
180
|
+
{loading ? (
|
|
181
|
+
<p className="p-8 text-center text-sm text-[var(--muted-foreground)]">
|
|
182
|
+
Loading contacts…
|
|
183
|
+
</p>
|
|
184
|
+
) : visibleContacts.length === 0 ? (
|
|
185
|
+
<div className="p-10 text-center">
|
|
186
|
+
<h3 className="m-0 text-base font-semibold">
|
|
187
|
+
{contacts.length === 0 ? "No contacts yet" : "No matching contacts"}
|
|
188
|
+
</h3>
|
|
189
|
+
<p className="mb-0 mt-2 text-sm text-[var(--muted-foreground)]">
|
|
190
|
+
{contacts.length === 0
|
|
191
|
+
? "Add the first contact with the form beside this list."
|
|
192
|
+
: "Try a different name, email, company, or status."}
|
|
193
|
+
</p>
|
|
194
|
+
</div>
|
|
195
|
+
) : (
|
|
196
|
+
<div className="overflow-x-auto">
|
|
197
|
+
<table className="w-full min-w-[42rem] border-collapse text-left text-sm">
|
|
198
|
+
<thead className="bg-[var(--muted)] text-xs uppercase tracking-wide text-[var(--muted-foreground)]">
|
|
199
|
+
<tr>
|
|
200
|
+
<th className="px-5 py-3 font-medium" scope="col">Contact</th>
|
|
201
|
+
<th className="px-5 py-3 font-medium" scope="col">Company</th>
|
|
202
|
+
<th className="px-5 py-3 font-medium" scope="col">Status</th>
|
|
203
|
+
<th className="px-5 py-3 text-right font-medium" scope="col">Actions</th>
|
|
204
|
+
</tr>
|
|
205
|
+
</thead>
|
|
206
|
+
<tbody>
|
|
207
|
+
{visibleContacts.map((contact) => (
|
|
208
|
+
<tr className="border-t" key={contact.id}>
|
|
209
|
+
<td className="px-5 py-4">
|
|
210
|
+
<strong className="block font-medium">{contact.name}</strong>
|
|
211
|
+
<span className="text-[var(--muted-foreground)]">{contact.email}</span>
|
|
212
|
+
</td>
|
|
213
|
+
<td className="px-5 py-4">{contact.company || "—"}</td>
|
|
214
|
+
<td className="px-5 py-4">
|
|
215
|
+
<span className="rounded-full bg-[var(--muted)] px-2.5 py-1 text-xs font-medium capitalize">
|
|
216
|
+
{contact.status}
|
|
217
|
+
</span>
|
|
218
|
+
</td>
|
|
219
|
+
<td className="px-5 py-4 text-right">
|
|
220
|
+
<Button
|
|
221
|
+
aria-label={`Delete ${contact.name}`}
|
|
222
|
+
size="sm"
|
|
223
|
+
type="button"
|
|
224
|
+
variant="ghost"
|
|
225
|
+
onClick={() => void deleteContact(contact)}
|
|
226
|
+
>
|
|
227
|
+
<Trash2 aria-hidden size={16} />
|
|
228
|
+
</Button>
|
|
229
|
+
</td>
|
|
230
|
+
</tr>
|
|
231
|
+
))}
|
|
232
|
+
</tbody>
|
|
233
|
+
</table>
|
|
234
|
+
</div>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
</Card>
|
|
238
|
+
</section>
|
|
239
|
+
|
|
240
|
+
<aside>
|
|
241
|
+
<Card>
|
|
242
|
+
<CardHeader>
|
|
243
|
+
<h2 className="m-0 flex items-center gap-2 text-base font-semibold">
|
|
244
|
+
<Plus aria-hidden size={18} />
|
|
245
|
+
Add contact
|
|
246
|
+
</h2>
|
|
247
|
+
</CardHeader>
|
|
248
|
+
<CardContent>
|
|
249
|
+
<form className="grid gap-4" onSubmit={(event) => void createContact(event)}>
|
|
250
|
+
<div className="grid gap-1.5">
|
|
251
|
+
<Label htmlFor="contact-name">Name</Label>
|
|
252
|
+
<Input
|
|
253
|
+
id="contact-name"
|
|
254
|
+
required
|
|
255
|
+
autoComplete="name"
|
|
256
|
+
value={draft.name}
|
|
257
|
+
onChange={(event) => setDraft({ ...draft, name: event.target.value })}
|
|
258
|
+
/>
|
|
259
|
+
</div>
|
|
260
|
+
<div className="grid gap-1.5">
|
|
261
|
+
<Label htmlFor="contact-email">Email</Label>
|
|
262
|
+
<Input
|
|
263
|
+
id="contact-email"
|
|
264
|
+
required
|
|
265
|
+
autoComplete="email"
|
|
266
|
+
type="email"
|
|
267
|
+
value={draft.email}
|
|
268
|
+
onChange={(event) => setDraft({ ...draft, email: event.target.value })}
|
|
269
|
+
/>
|
|
270
|
+
</div>
|
|
271
|
+
<div className="grid gap-1.5">
|
|
272
|
+
<Label htmlFor="contact-company">Company</Label>
|
|
273
|
+
<Input
|
|
274
|
+
id="contact-company"
|
|
275
|
+
autoComplete="organization"
|
|
276
|
+
value={draft.company}
|
|
277
|
+
onChange={(event) => setDraft({ ...draft, company: event.target.value })}
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
{error ? (
|
|
281
|
+
<p className="m-0 text-sm text-[var(--destructive)]" role="alert">{error}</p>
|
|
282
|
+
) : null}
|
|
283
|
+
<Button disabled={saving} type="submit">
|
|
284
|
+
{saving ? "Adding…" : "Add contact"}
|
|
285
|
+
</Button>
|
|
286
|
+
</form>
|
|
287
|
+
</CardContent>
|
|
288
|
+
</Card>
|
|
289
|
+
</aside>
|
|
290
|
+
</div>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
const buttonVariants = cva(
|
|
7
|
+
"inline-flex min-h-10 items-center justify-center gap-2 rounded-lg px-4 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:pointer-events-none disabled:opacity-50",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-[var(--primary)] text-[var(--primary-foreground)] hover:brightness-95",
|
|
12
|
+
outline: "border bg-[var(--card)] hover:bg-[var(--muted)]",
|
|
13
|
+
ghost: "hover:bg-[var(--muted)]",
|
|
14
|
+
destructive: "bg-[var(--destructive)] text-white hover:brightness-95",
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
default: "h-10",
|
|
18
|
+
sm: "h-9 px-3",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
variant: "default",
|
|
23
|
+
size: "default",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export interface ButtonProps
|
|
29
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
30
|
+
VariantProps<typeof buttonVariants> {
|
|
31
|
+
asChild?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function Button({ className, variant, size, asChild, ...props }: ButtonProps) {
|
|
35
|
+
const Component = asChild ? Slot : "button";
|
|
36
|
+
return (
|
|
37
|
+
<Component
|
|
38
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
export function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
5
|
+
return (
|
|
6
|
+
<section
|
|
7
|
+
className={cn("rounded-xl border bg-[var(--card)] text-[var(--card-foreground)] shadow-sm", className)}
|
|
8
|
+
{...props}
|
|
9
|
+
/>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
14
|
+
return <div className={cn("border-b px-5 py-4", className)} {...props} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
18
|
+
return <div className={cn("p-5", className)} {...props} />;
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
export const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
5
|
+
({ className, type, ...props }, ref) => (
|
|
6
|
+
<input
|
|
7
|
+
ref={ref}
|
|
8
|
+
type={type}
|
|
9
|
+
className={cn(
|
|
10
|
+
"flex h-10 w-full rounded-lg border bg-white px-3 py-2 text-sm outline-none placeholder:text-[var(--muted-foreground)] focus:ring-2 focus:ring-[var(--ring)] disabled:opacity-50",
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
),
|
|
16
|
+
);
|
|
17
|
+
Input.displayName = "Input";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig } from "drizzle-kit";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const dataDir = process.env.RUDDER_APP_DATA_DIR
|
|
5
|
+
? path.resolve(process.env.RUDDER_APP_DATA_DIR)
|
|
6
|
+
: path.resolve("data");
|
|
7
|
+
const mode = process.env.RUDDER_APP_DATA_MODE === "production"
|
|
8
|
+
? "app.sqlite"
|
|
9
|
+
: "dev.sqlite";
|
|
10
|
+
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
dialect: "sqlite",
|
|
13
|
+
schema: "./lib/db/schema.ts",
|
|
14
|
+
out: "./migrations",
|
|
15
|
+
dbCredentials: {
|
|
16
|
+
url: path.join(dataDir, mode),
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { contactStatusSchema } from "./domain";
|
|
3
|
+
|
|
4
|
+
export const exportedContactSchema = z.object({
|
|
5
|
+
id: z.string().uuid(),
|
|
6
|
+
name: z.string().min(1).max(120),
|
|
7
|
+
email: z.email().max(320),
|
|
8
|
+
company: z.string().max(160),
|
|
9
|
+
status: contactStatusSchema,
|
|
10
|
+
createdAt: z.iso.datetime(),
|
|
11
|
+
updatedAt: z.iso.datetime(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const importEnvelopeSchema = z.object({
|
|
15
|
+
format: z.literal("rudder-app-data/v1"),
|
|
16
|
+
exportedAt: z.iso.datetime(),
|
|
17
|
+
data: z.object({
|
|
18
|
+
contacts: z.array(exportedContactSchema).max(100_000),
|
|
19
|
+
}),
|
|
20
|
+
}).strict();
|
|
21
|
+
|
|
22
|
+
type ContactRow = {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
email: string;
|
|
26
|
+
company: string;
|
|
27
|
+
status: "new" | "contacted" | "replied" | "paused";
|
|
28
|
+
createdAt: Date;
|
|
29
|
+
updatedAt: Date;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function buildExportEnvelope(rows: ContactRow[]) {
|
|
33
|
+
return {
|
|
34
|
+
format: "rudder-app-data/v1" as const,
|
|
35
|
+
exportedAt: new Date().toISOString(),
|
|
36
|
+
data: {
|
|
37
|
+
contacts: rows.map((row) => ({
|
|
38
|
+
...row,
|
|
39
|
+
createdAt: row.createdAt.toISOString(),
|
|
40
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
41
|
+
})),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
drizzle,
|
|
3
|
+
type SqliteRemoteDatabase,
|
|
4
|
+
} from "drizzle-orm/sqlite-proxy";
|
|
5
|
+
import { mkdirSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { DatabaseSync, type SQLInputValue } from "node:sqlite";
|
|
8
|
+
import * as schema from "./schema";
|
|
9
|
+
|
|
10
|
+
type DatabaseHandle = {
|
|
11
|
+
sqlite: DatabaseSync;
|
|
12
|
+
db: SqliteRemoteDatabase<typeof schema>;
|
|
13
|
+
filePath: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const handles = new Map<string, DatabaseHandle>();
|
|
17
|
+
|
|
18
|
+
export function dataMode() {
|
|
19
|
+
return process.env.RUDDER_APP_DATA_MODE === "production"
|
|
20
|
+
? "production"
|
|
21
|
+
: "development";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function dataDirectory() {
|
|
25
|
+
return process.env.RUDDER_APP_DATA_DIR
|
|
26
|
+
? path.resolve(process.env.RUDDER_APP_DATA_DIR)
|
|
27
|
+
: path.resolve("data");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function databasePath() {
|
|
31
|
+
if (process.env.RUDDER_APP_DATA_DIR) {
|
|
32
|
+
return path.join(
|
|
33
|
+
dataDirectory(),
|
|
34
|
+
dataMode() === "production" ? "app.sqlite" : "dev.sqlite",
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return path.join(
|
|
38
|
+
dataDirectory(),
|
|
39
|
+
dataMode() === "production" ? "production/app.sqlite" : "development/dev.sqlite",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getDatabase(): DatabaseHandle {
|
|
44
|
+
const filePath = databasePath();
|
|
45
|
+
const cached = handles.get(filePath);
|
|
46
|
+
if (cached) return cached;
|
|
47
|
+
|
|
48
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
49
|
+
const sqlite = new DatabaseSync(filePath);
|
|
50
|
+
sqlite.exec("PRAGMA journal_mode = WAL");
|
|
51
|
+
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
52
|
+
sqlite.exec("PRAGMA busy_timeout = 5000");
|
|
53
|
+
const db = drizzle(async (query, parameters, method): Promise<{ rows: unknown[] }> => {
|
|
54
|
+
const statement = sqlite.prepare(query);
|
|
55
|
+
statement.setReturnArrays(true);
|
|
56
|
+
const values = parameters as SQLInputValue[];
|
|
57
|
+
if (method === "run") {
|
|
58
|
+
statement.run(...values);
|
|
59
|
+
return { rows: [] };
|
|
60
|
+
}
|
|
61
|
+
if (method === "get") {
|
|
62
|
+
return { rows: statement.get(...values) as unknown as unknown[] };
|
|
63
|
+
}
|
|
64
|
+
return { rows: statement.all(...values) as unknown as unknown[][] };
|
|
65
|
+
}, { schema });
|
|
66
|
+
const handle: DatabaseHandle = {
|
|
67
|
+
sqlite,
|
|
68
|
+
db,
|
|
69
|
+
filePath,
|
|
70
|
+
};
|
|
71
|
+
handles.set(filePath, handle);
|
|
72
|
+
return handle;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function closeDatabases() {
|
|
76
|
+
for (const handle of handles.values()) handle.sqlite.close();
|
|
77
|
+
handles.clear();
|
|
78
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
|
|
2
|
+
|
|
3
|
+
export const contacts = sqliteTable("contacts", {
|
|
4
|
+
id: text("id").primaryKey(),
|
|
5
|
+
name: text("name").notNull(),
|
|
6
|
+
email: text("email").notNull(),
|
|
7
|
+
company: text("company").notNull().default(""),
|
|
8
|
+
status: text("status", { enum: ["new", "contacted", "replied", "paused"] })
|
|
9
|
+
.notNull()
|
|
10
|
+
.default("new"),
|
|
11
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
12
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(),
|
|
13
|
+
}, (table) => [
|
|
14
|
+
uniqueIndex("contacts_email_uq").on(table.email),
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export const jobs = sqliteTable("jobs", {
|
|
18
|
+
id: text("id").primaryKey(),
|
|
19
|
+
kind: text("kind").notNull(),
|
|
20
|
+
idempotencyKey: text("idempotency_key").notNull(),
|
|
21
|
+
payloadJson: text("payload_json").notNull().default("{}"),
|
|
22
|
+
status: text("status", { enum: ["pending", "running", "completed", "failed", "missed"] })
|
|
23
|
+
.notNull()
|
|
24
|
+
.default("pending"),
|
|
25
|
+
catchUpPolicy: text("catch_up_policy", { enum: ["run", "skip", "prompt"] })
|
|
26
|
+
.notNull()
|
|
27
|
+
.default("prompt"),
|
|
28
|
+
scheduledFor: integer("scheduled_for", { mode: "timestamp_ms" }).notNull(),
|
|
29
|
+
attempts: integer("attempts").notNull().default(0),
|
|
30
|
+
lastError: text("last_error"),
|
|
31
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
32
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(),
|
|
33
|
+
}, (table) => [
|
|
34
|
+
uniqueIndex("jobs_idempotency_key_uq").on(table.idempotencyKey),
|
|
35
|
+
]);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const contactStatusSchema = z.enum(["new", "contacted", "replied", "paused"]);
|
|
4
|
+
|
|
5
|
+
export const contactCreateSchema = z.object({
|
|
6
|
+
name: z.string().trim().min(1).max(120),
|
|
7
|
+
email: z.email().max(320),
|
|
8
|
+
company: z.string().trim().max(160).default(""),
|
|
9
|
+
status: contactStatusSchema.default("new"),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const contactUpdateSchema = contactCreateSchema.partial().refine(
|
|
13
|
+
(value) => Object.keys(value).length > 0,
|
|
14
|
+
"At least one contact field is required",
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export type ContactInput = z.infer<typeof contactCreateSchema>;
|
|
18
|
+
export type ContactStatus = z.infer<typeof contactStatusSchema>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getDatabase } from "@/lib/db/client";
|
|
2
|
+
|
|
3
|
+
type RunnerState = {
|
|
4
|
+
timer: NodeJS.Timeout | null;
|
|
5
|
+
ticking: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const globalState = globalThis as typeof globalThis & {
|
|
9
|
+
__rudderAppJobRunner?: RunnerState;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function state(): RunnerState {
|
|
13
|
+
globalState.__rudderAppJobRunner ??= { timer: null, ticking: false };
|
|
14
|
+
return globalState.__rudderAppJobRunner;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function tick() {
|
|
18
|
+
const current = state();
|
|
19
|
+
if (current.ticking) return;
|
|
20
|
+
current.ticking = true;
|
|
21
|
+
try {
|
|
22
|
+
const { sqlite } = getDatabase();
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
sqlite.prepare(`
|
|
25
|
+
update jobs
|
|
26
|
+
set status = case catch_up_policy
|
|
27
|
+
when 'skip' then 'missed'
|
|
28
|
+
when 'run' then 'pending'
|
|
29
|
+
else 'missed'
|
|
30
|
+
end,
|
|
31
|
+
updated_at = ?
|
|
32
|
+
where status = 'pending' and scheduled_for < ?
|
|
33
|
+
`).run(now, now - 60_000);
|
|
34
|
+
// Domain-specific handlers should claim one pending job transactionally,
|
|
35
|
+
// execute it with the persisted idempotency key, and then complete/fail it.
|
|
36
|
+
} catch {
|
|
37
|
+
// The health endpoint remains authoritative. A missing pre-migration table
|
|
38
|
+
// must not create an unhandled background rejection during startup.
|
|
39
|
+
} finally {
|
|
40
|
+
current.ticking = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function startJobRunner() {
|
|
45
|
+
const current = state();
|
|
46
|
+
if (current.timer) return;
|
|
47
|
+
current.timer = setInterval(() => void tick(), 15_000);
|
|
48
|
+
current.timer.unref();
|
|
49
|
+
void tick();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function stopJobRunner() {
|
|
53
|
+
const current = state();
|
|
54
|
+
if (current.timer) clearInterval(current.timer);
|
|
55
|
+
current.timer = null;
|
|
56
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
CREATE TABLE `contacts` (
|
|
2
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`name` text NOT NULL,
|
|
4
|
+
`email` text NOT NULL,
|
|
5
|
+
`company` text DEFAULT '' NOT NULL,
|
|
6
|
+
`status` text DEFAULT 'new' NOT NULL,
|
|
7
|
+
`created_at` integer NOT NULL,
|
|
8
|
+
`updated_at` integer NOT NULL
|
|
9
|
+
);
|
|
10
|
+
--> statement-breakpoint
|
|
11
|
+
CREATE UNIQUE INDEX `contacts_email_uq` ON `contacts` (`email`);
|
|
12
|
+
--> statement-breakpoint
|
|
13
|
+
CREATE TABLE `jobs` (
|
|
14
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
15
|
+
`kind` text NOT NULL,
|
|
16
|
+
`idempotency_key` text NOT NULL,
|
|
17
|
+
`payload_json` text DEFAULT '{}' NOT NULL,
|
|
18
|
+
`status` text DEFAULT 'pending' NOT NULL,
|
|
19
|
+
`catch_up_policy` text DEFAULT 'prompt' NOT NULL,
|
|
20
|
+
`scheduled_for` integer NOT NULL,
|
|
21
|
+
`attempts` integer DEFAULT 0 NOT NULL,
|
|
22
|
+
`last_error` text,
|
|
23
|
+
`created_at` integer NOT NULL,
|
|
24
|
+
`updated_at` integer NOT NULL
|
|
25
|
+
);
|
|
26
|
+
--> statement-breakpoint
|
|
27
|
+
CREATE UNIQUE INDEX `jobs_idempotency_key_uq` ON `jobs` (`idempotency_key`);
|