@wopr-network/platform-ui-core 1.27.8 → 1.27.9
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/next.config.ts +1 -2
- package/package.json +17 -17
- package/src/__tests__/account-switcher.test.tsx +21 -20
- package/src/__tests__/activity-page.test.tsx +2 -6
- package/src/__tests__/add-payment-method-dialog.test.tsx +9 -32
- package/src/__tests__/admin-api.test.ts +1 -6
- package/src/__tests__/admin-gpu-api.test.ts +1 -3
- package/src/__tests__/admin-marketplace-api.test.ts +1 -4
- package/src/__tests__/admin-middleware.test.ts +76 -83
- package/src/__tests__/affiliate-dashboard.test.tsx +3 -3
- package/src/__tests__/api-401-redirect.test.ts +46 -9
- package/src/__tests__/api-client.test.ts +3 -5
- package/src/__tests__/api-config.test.ts +22 -42
- package/src/__tests__/api-fleet-resources.test.ts +1 -2
- package/src/__tests__/api-fleet-trpc.test.ts +2 -8
- package/src/__tests__/api-null-guards.test.ts +3 -1
- package/src/__tests__/audit-log-table-pagination.test.tsx +2 -6
- package/src/__tests__/auth-password-reset.test.tsx +7 -21
- package/src/__tests__/auth-redirect.test.tsx +8 -2
- package/src/__tests__/auth.test.tsx +25 -23
- package/src/__tests__/auto-topup-card.test.tsx +4 -12
- package/src/__tests__/backups-tab.test.tsx +3 -4
- package/src/__tests__/billing-layout-nav-hidden.test.tsx +5 -37
- package/src/__tests__/billing-payment-org-invoices.test.tsx +2 -18
- package/src/__tests__/billing.test.tsx +8 -39
- package/src/__tests__/bot-settings/resources-tab.test.tsx +1 -3
- package/src/__tests__/bot-settings/storage-tab.test.tsx +1 -3
- package/src/__tests__/bot-settings/vps-upgrade-card.test.tsx +1 -3
- package/src/__tests__/bot-settings-restart.test.tsx +1 -3
- package/src/__tests__/bot-settings.test.tsx +2 -6
- package/src/__tests__/brand.test.ts +6 -26
- package/src/__tests__/buy-credits-panel.test.tsx +1 -3
- package/src/__tests__/buy-crypto-credits-panel.test.tsx +101 -119
- package/src/__tests__/capability-conflicts.test.ts +2 -8
- package/src/__tests__/capability-resolver.test.tsx +2 -12
- package/src/__tests__/channel-wizard.test.tsx +4 -17
- package/src/__tests__/chat/chat-panel.test.tsx +1 -4
- package/src/__tests__/chat-store.test.ts +5 -15
- package/src/__tests__/command-center.test.tsx +10 -12
- package/src/__tests__/compliance-retention-edit.test.tsx +3 -6
- package/src/__tests__/confirmation-tracker.test.tsx +3 -18
- package/src/__tests__/coupon-input.test.tsx +1 -3
- package/src/__tests__/create-instance.test.tsx +1 -3
- package/src/__tests__/credit-balance.test.tsx +4 -12
- package/src/__tests__/credits.test.tsx +32 -85
- package/src/__tests__/email-verification-banner.test.tsx +2 -6
- package/src/__tests__/error-boundaries.test.tsx +0 -1
- package/src/__tests__/fetch-pricing.test.ts +2 -1
- package/src/__tests__/field-oauth.test.tsx +2 -6
- package/src/__tests__/fixtures/mock-manifests-data.js +1 -3
- package/src/__tests__/fixtures/mock-manifests.ts +2 -4
- package/src/__tests__/fleet-health-timestamp.test.tsx +1 -8
- package/src/__tests__/fleet-health-update.test.tsx +1 -8
- package/src/__tests__/gpu-dashboard.test.tsx +2 -6
- package/src/__tests__/instance-detail.test.tsx +3 -9
- package/src/__tests__/instance-list.test.tsx +1 -5
- package/src/__tests__/layout-snapshots.test.tsx +64 -11
- package/src/__tests__/marketplace-admin.test.tsx +2 -6
- package/src/__tests__/marketplace.test.tsx +11 -35
- package/src/__tests__/merge-api-rates.test.ts +1 -6
- package/src/__tests__/middleware.test.ts +32 -219
- package/src/__tests__/next-config-headers.test.ts +1 -3
- package/src/__tests__/notifications.test.tsx +4 -11
- package/src/__tests__/oauth-buttons.test.tsx +36 -59
- package/src/__tests__/oauth-error-mapping.test.tsx +2 -6
- package/src/__tests__/observability.test.tsx +23 -36
- package/src/__tests__/onboarding-page.test.tsx +4 -6
- package/src/__tests__/org-billing-api.test.tsx +1 -6
- package/src/__tests__/plugin-install-flow.test.tsx +28 -58
- package/src/__tests__/plugin-registry.test.tsx +3 -11
- package/src/__tests__/plugin-tool-sync.test.ts +1 -3
- package/src/__tests__/plugins-catalog-error.test.tsx +2 -6
- package/src/__tests__/plugins-toggle-race.test.tsx +3 -5
- package/src/__tests__/portfolio-chart.test.tsx +2 -6
- package/src/__tests__/promotion-form.test.tsx +2 -6
- package/src/__tests__/promotions-list.test.tsx +1 -3
- package/src/__tests__/provider-key-api.test.ts +2 -1
- package/src/__tests__/resend-verification-button.test.tsx +8 -24
- package/src/__tests__/secrets-audit-pagination.test.tsx +1 -3
- package/src/__tests__/settings.test.tsx +11 -21
- package/src/__tests__/setup-checklist.test.tsx +3 -9
- package/src/__tests__/setup.ts +25 -6
- package/src/__tests__/snapshot-api.test.ts +2 -1
- package/src/__tests__/step-superpowers.test.tsx +1 -3
- package/src/__tests__/tenant-context.test.tsx +1 -6
- package/src/__tests__/tenant-keys-api.test.ts +3 -4
- package/src/__tests__/tenant-table-pagination.test.tsx +2 -6
- package/src/__tests__/terminal-log-cleanup.test.tsx +0 -1
- package/src/__tests__/transaction-history.test.tsx +190 -238
- package/src/__tests__/trpc-types.test.ts +2 -6
- package/src/__tests__/use-chat.test.ts +1 -3
- package/src/__tests__/use-plugin-setup-chat-stale-closure.test.ts +1 -4
- package/src/__tests__/use-sidecar-bridge.test.tsx +105 -0
- package/src/__tests__/use-webmcp.test.ts +1 -3
- package/src/__tests__/validate-elevenlabs-key.test.ts +2 -1
- package/src/__tests__/verify-page.test.tsx +4 -13
- package/src/__tests__/verify-redirect.test.tsx +2 -6
- package/src/app/(auth)/error.tsx +1 -7
- package/src/app/(auth)/forgot-password/page.tsx +4 -18
- package/src/app/(auth)/login/page.tsx +5 -22
- package/src/app/(auth)/reset-password/page.tsx +2 -12
- package/src/app/(auth)/signup/page.tsx +10 -44
- package/src/app/(auth)/verify/page.tsx +47 -0
- package/src/app/(dashboard)/billing/credits/page.tsx +14 -67
- package/src/app/(dashboard)/billing/error.tsx +2 -10
- package/src/app/(dashboard)/billing/layout.tsx +12 -62
- package/src/app/(dashboard)/billing/payment/page.tsx +17 -68
- package/src/app/(dashboard)/billing/plans/page.tsx +3 -9
- package/src/app/(dashboard)/billing/usage/hosted/page.tsx +8 -25
- package/src/app/(dashboard)/billing/usage/page.tsx +63 -103
- package/src/app/(dashboard)/changesets/[id]/changeset-detail-client.tsx +9 -27
- package/src/app/(dashboard)/changesets/[id]/error.tsx +2 -6
- package/src/app/(dashboard)/changesets/error.tsx +1 -7
- package/src/app/(dashboard)/chat/page.tsx +2 -6
- package/src/app/(dashboard)/dashboard/network/page.tsx +5 -19
- package/src/app/(dashboard)/error.tsx +1 -7
- package/src/app/(dashboard)/layout.tsx +15 -36
- package/src/app/(dashboard)/marketplace/[plugin]/page.tsx +14 -51
- package/src/app/(dashboard)/marketplace/error.tsx +1 -7
- package/src/app/(dashboard)/marketplace/page.tsx +6 -27
- package/src/app/(dashboard)/not-found.tsx +2 -5
- package/src/app/(dashboard)/onboarding/page.tsx +5 -22
- package/src/app/(dashboard)/settings/account/page.tsx +1 -6
- package/src/app/(dashboard)/settings/activity/page.tsx +8 -34
- package/src/app/(dashboard)/settings/api-keys/page.tsx +15 -60
- package/src/app/(dashboard)/settings/brain/page.tsx +9 -31
- package/src/app/(dashboard)/settings/error.tsx +2 -10
- package/src/app/(dashboard)/settings/notifications/page.tsx +2 -6
- package/src/app/(dashboard)/settings/org/page.tsx +13 -56
- package/src/app/(dashboard)/settings/page.tsx +1 -0
- package/src/app/(dashboard)/settings/profile/page.tsx +126 -73
- package/src/app/(dashboard)/settings/providers/page.tsx +21 -78
- package/src/app/(dashboard)/settings/secrets/page.tsx +13 -58
- package/src/app/(dashboard)/settings/security/page.tsx +31 -111
- package/src/app/admin/email-templates/email-templates-client.tsx +15 -58
- package/src/app/admin/error.tsx +1 -7
- package/src/app/admin/fleet-updates/error.tsx +1 -7
- package/src/app/admin/fleet-updates/fleet-updates-client.tsx +10 -50
- package/src/app/admin/layout.tsx +4 -0
- package/src/app/admin/payment-methods/page.tsx +9 -38
- package/src/app/admin/products/error.tsx +2 -7
- package/src/app/admin/products/page.tsx +1 -4
- package/src/app/admin/promotions/[id]/page.tsx +9 -38
- package/src/app/admin/promotions/page.tsx +9 -36
- package/src/app/admin/rate-overrides/page.tsx +9 -45
- package/src/app/auth/callback/[provider]/page.tsx +1 -8
- package/src/app/auth/verify/page.tsx +9 -36
- package/src/app/channels/error.tsx +2 -10
- package/src/app/channels/layout.tsx +9 -0
- package/src/app/channels/page.tsx +8 -20
- package/src/app/channels/setup/[plugin]/page.tsx +3 -5
- package/src/app/error.tsx +1 -7
- package/src/app/fleet/error.tsx +1 -7
- package/src/app/fleet/layout.tsx +5 -0
- package/src/app/fleet/settings/page.tsx +1 -3
- package/src/app/global-error.tsx +2 -10
- package/src/app/globals.css +1 -4
- package/src/app/instances/[id]/instance-detail-client.tsx +51 -125
- package/src/app/instances/error.tsx +2 -10
- package/src/app/instances/instance-list-client.tsx +20 -69
- package/src/app/instances/layout.tsx +9 -0
- package/src/app/instances/new/create-instance-client.tsx +10 -31
- package/src/app/layout.tsx +2 -10
- package/src/app/not-found.tsx +1 -3
- package/src/app/page.tsx +1 -2
- package/src/app/plugins/error.tsx +2 -10
- package/src/app/plugins/layout.tsx +5 -0
- package/src/app/plugins/page.tsx +16 -48
- package/src/app/pricing/error.tsx +1 -7
- package/src/app/privacy/page.tsx +93 -150
- package/src/app/status/error.tsx +1 -7
- package/src/app/terms/page.tsx +89 -144
- package/src/components/account-switcher.tsx +25 -52
- package/src/components/admin/accounting-dashboard.tsx +1 -3
- package/src/components/admin/admin-guard.tsx +1 -3
- package/src/components/admin/admin-nav.tsx +1 -3
- package/src/components/admin/affiliate-dashboard.tsx +25 -94
- package/src/components/admin/audit-log-table.tsx +13 -49
- package/src/components/admin/billing-health-dashboard.tsx +7 -25
- package/src/components/admin/bulk-actions-bar.test.tsx +1 -7
- package/src/components/admin/bulk-actions-bar.tsx +1 -3
- package/src/components/admin/bulk-export-dialog.test.tsx +1 -7
- package/src/components/admin/bulk-export-dialog.tsx +6 -32
- package/src/components/admin/bulk-grant-dialog.test.tsx +2 -6
- package/src/components/admin/bulk-grant-dialog.tsx +4 -15
- package/src/components/admin/bulk-preview-dialog.tsx +3 -12
- package/src/components/admin/bulk-reactivate-dialog.tsx +1 -7
- package/src/components/admin/bulk-select-all-banner.tsx +1 -6
- package/src/components/admin/bulk-suspend-dialog.tsx +5 -12
- package/src/components/admin/bulk-undo-toast.tsx +1 -2
- package/src/components/admin/compliance-dashboard.tsx +31 -101
- package/src/components/admin/gpu-dashboard.tsx +21 -70
- package/src/components/admin/grant-credits-dialog.tsx +4 -17
- package/src/components/admin/incident-dashboard.tsx +10 -25
- package/src/components/admin/inference-dashboard.tsx +14 -54
- package/src/components/admin/marketplace-admin.tsx +18 -60
- package/src/components/admin/migrations-dashboard.tsx +9 -42
- package/src/components/admin/onboarding-dashboard.tsx +14 -64
- package/src/components/admin/pool-config-dashboard.tsx +4 -10
- package/src/components/admin/products/fleet-form.tsx +2 -11
- package/src/components/admin/products/nav-editor.tsx +3 -10
- package/src/components/admin/promotions/promotion-form.tsx +9 -42
- package/src/components/admin/roles-dashboard.tsx +7 -34
- package/src/components/admin/suspend-dialog.tsx +4 -11
- package/src/components/admin/tenant-notes-panel.tsx +1 -3
- package/src/components/admin/tenant-row-actions.tsx +4 -20
- package/src/components/admin/tenant-table.tsx +12 -49
- package/src/components/auth/auth-redirect.tsx +11 -3
- package/src/components/auth/email-verification-result-banner.tsx +1 -3
- package/src/components/auth/resend-verification-button.tsx +2 -10
- package/src/components/auth/wopr-wordmark.tsx +1 -3
- package/src/components/billing/add-payment-method-dialog.tsx +1 -2
- package/src/components/billing/affiliate-dashboard.tsx +4 -16
- package/src/components/billing/amount-selector.tsx +1 -3
- package/src/components/billing/auto-topup-card.tsx +2 -11
- package/src/components/billing/buy-credits-panel.tsx +4 -14
- package/src/components/billing/byok-callout.tsx +6 -8
- package/src/components/billing/confirmation-tracker.tsx +4 -14
- package/src/components/billing/credit-balance-badge.tsx +22 -0
- package/src/components/billing/credit-balance.tsx +3 -9
- package/src/components/billing/crypto-checkout.tsx +5 -24
- package/src/components/billing/degraded-state-banner.tsx +1 -3
- package/src/components/billing/deposit-view.tsx +301 -41
- package/src/components/billing/dividend-banner.tsx +1 -3
- package/src/components/billing/dividend-eligibility.tsx +3 -12
- package/src/components/billing/dividend-pool-stats.tsx +6 -20
- package/src/components/billing/first-dividend-dialog.tsx +2 -2
- package/src/components/billing/org-billing-page.tsx +8 -31
- package/src/components/billing/payment-method-picker.tsx +2 -10
- package/src/components/billing/suspension-banner.tsx +2 -7
- package/src/components/billing/transaction-history.tsx +10 -58
- package/src/components/billing/unified-checkout.tsx +547 -0
- package/src/components/bot-settings/backups-tab.tsx +9 -33
- package/src/components/bot-settings/bot-settings-client.tsx +32 -134
- package/src/components/bot-settings/resources-tab.tsx +2 -9
- package/src/components/bot-settings/storage-tab.tsx +19 -48
- package/src/components/bot-settings/vps-info-panel.tsx +3 -11
- package/src/components/bot-settings/vps-upgrade-card.tsx +3 -4
- package/src/components/brand-hydrator.tsx +13 -0
- package/src/components/channel-wizard/field-interactive.tsx +1 -3
- package/src/components/channel-wizard/field-qr.tsx +10 -39
- package/src/components/channel-wizard/step-renderer.tsx +5 -28
- package/src/components/channel-wizard/wizard.tsx +6 -31
- package/src/components/chat/chat-message.tsx +1 -4
- package/src/components/chat/chat-panel.tsx +4 -18
- package/src/components/chat/chat-widget.tsx +3 -14
- package/src/components/dashboard/command-center.tsx +15 -61
- package/src/components/fleet/update-settings-card.tsx +7 -23
- package/src/components/instance-update-banner.tsx +130 -0
- package/src/components/instances/friends-tab.test.tsx +2 -9
- package/src/components/instances/friends-tab.tsx +18 -74
- package/src/components/instances/update-available-badge.tsx +2 -11
- package/src/components/landing/hero.tsx +3 -9
- package/src/components/landing/landing-page.tsx +1 -3
- package/src/components/landing/portfolio-chart.tsx +4 -9
- package/src/components/landing/story-sections.tsx +1 -3
- package/src/components/landing/terminal-sequence.tsx +4 -17
- package/src/components/marketplace/empty-state.tsx +2 -6
- package/src/components/marketplace/first-visit-hero.tsx +1 -3
- package/src/components/marketplace/install-wizard.tsx +20 -77
- package/src/components/marketplace/marketplace-tabs.tsx +1 -4
- package/src/components/marketplace/plugin-card.tsx +2 -9
- package/src/components/marketplace/superpower-content.tsx +1 -3
- package/src/components/marketplace/terminal-search.tsx +2 -8
- package/src/components/oauth-buttons.tsx +29 -14
- package/src/components/observability/fleet-health.tsx +5 -18
- package/src/components/observability/health-overview.tsx +7 -20
- package/src/components/observability/logs-viewer.tsx +8 -32
- package/src/components/observability/metrics-dashboard.tsx +2 -15
- package/src/components/onboarding/fallback-setup.tsx +6 -25
- package/src/components/onboarding/setup-checklist.tsx +18 -51
- package/src/components/onboarding/step-superpowers.tsx +1 -4
- package/src/components/plugin-setup/setup-chat-panel.tsx +6 -22
- package/src/components/pricing/dividend-calculator.tsx +6 -12
- package/src/components/pricing/dividend-stats.tsx +5 -17
- package/src/components/pricing/pricing-page.tsx +17 -36
- package/src/components/settings/create-org-wizard.tsx +2 -5
- package/src/components/sidebar.tsx +7 -42
- package/src/components/sidecar-frame.tsx +78 -0
- package/src/components/status/status-page.tsx +6 -28
- package/src/components/ui/alert-dialog.tsx +8 -25
- package/src/components/ui/badge.tsx +2 -8
- package/src/components/ui/banner.tsx +1 -6
- package/src/components/ui/card.tsx +5 -24
- package/src/components/ui/checkbox.tsx +1 -5
- package/src/components/ui/collapsible.tsx +3 -8
- package/src/components/ui/dialog.tsx +4 -10
- package/src/components/ui/dropdown-menu.tsx +9 -18
- package/src/components/ui/form.tsx +2 -16
- package/src/components/ui/popover.tsx +3 -23
- package/src/components/ui/progress.tsx +1 -5
- package/src/components/ui/radio-group.tsx +3 -15
- package/src/components/ui/select.tsx +4 -17
- package/src/components/ui/sheet.tsx +5 -19
- package/src/components/ui/skeleton.tsx +1 -7
- package/src/components/ui/table.tsx +5 -22
- package/src/components/ui/tabs.tsx +3 -13
- package/src/components/ui/tooltip.tsx +1 -1
- package/src/components/unified-sidebar.tsx +493 -0
- package/src/hooks/__tests__/use-fleet-sse.test.ts +1 -4
- package/src/hooks/__tests__/use-save-queue.test.ts +2 -8
- package/src/hooks/use-credit-balance.ts +27 -0
- package/src/hooks/use-my-org-role.ts +1 -3
- package/src/hooks/use-plugin-registry.ts +8 -14
- package/src/hooks/use-plugin-setup-chat.ts +2 -5
- package/src/hooks/use-sidecar-bridge.tsx +148 -0
- package/src/hooks/use-webmcp.ts +1 -4
- package/src/lib/__tests__/admin-api.test.ts +1 -3
- package/src/lib/__tests__/api-bot-crud.test.ts +8 -18
- package/src/lib/__tests__/api-fetch.test.ts +4 -16
- package/src/lib/__tests__/org-billing-api.test.ts +1 -3
- package/src/lib/__tests__/pricing-data.test.ts +0 -8
- package/src/lib/__tests__/settings-api.test.ts +1 -3
- package/src/lib/admin-affiliate-api.ts +2 -7
- package/src/lib/admin-api.ts +6 -26
- package/src/lib/admin-incident-api.ts +11 -19
- package/src/lib/admin-marketplace-api.ts +1 -5
- package/src/lib/api-config.test.ts +5 -50
- package/src/lib/api.ts +143 -122
- package/src/lib/auth-client.ts +1 -2
- package/src/lib/bot-settings-data.ts +11 -36
- package/src/lib/brand-config.ts +56 -115
- package/src/lib/brand.ts +2 -15
- package/src/lib/chat/use-chat.ts +2 -7
- package/src/lib/cost-comparison-data.test.ts +1 -3
- package/src/lib/cost-comparison-data.ts +1 -4
- package/src/lib/errors.ts +1 -4
- package/src/lib/fetch-utils.test.ts +26 -9
- package/src/lib/fetch-utils.ts +40 -11
- package/src/lib/logger.ts +2 -0
- package/src/lib/marketplace-data.ts +3 -11
- package/src/lib/oauth-errors.ts +2 -4
- package/src/lib/onboarding-data.ts +3 -11
- package/src/lib/org-api.ts +2 -10
- package/src/lib/org-billing-api.ts +5 -19
- package/src/lib/plugin/tool-definitions.ts +1 -2
- package/src/lib/require-auth.ts +57 -0
- package/src/lib/settings-api.ts +1 -4
- package/src/lib/sidecar-routes.ts +43 -0
- package/src/lib/trpc-server.ts +49 -0
- package/src/lib/trpc-types.ts +4 -6
- package/src/lib/trpc.tsx +12 -4
- package/src/lib/validate-redirect-url.ts +1 -4
- package/src/lib/webmcp/marketplace-onboarding-tools.ts +6 -16
- package/src/lib/webmcp/register.ts +1 -4
- package/src/lib/webmcp/tools.ts +2 -9
- package/src/proxy.ts +35 -212
- package/src/types/missing-deps.d.ts +2 -8
- package/tsconfig.json +1 -8
- package/biome.json +0 -52
- package/src/__tests__/__snapshots__/layout-snapshots.test.tsx.snap +0 -741
- package/src/__tests__/billing-byok-callout.test.tsx +0 -76
- package/src/lib/__tests__/__snapshots__/pricing-data.test.ts.snap +0 -112
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
5
|
+
|
|
6
|
+
import { fromSidecarPath } from "@/lib/sidecar-routes";
|
|
7
|
+
|
|
8
|
+
// Types matching the sidecar's postMessage protocol
|
|
9
|
+
|
|
10
|
+
export interface SidebarAgent {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
status: string;
|
|
14
|
+
icon: string | null;
|
|
15
|
+
liveRun: boolean;
|
|
16
|
+
liveRunCount: number;
|
|
17
|
+
pauseReason: string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SidebarProject {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
urlKey: string;
|
|
24
|
+
color: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SidecarSidebarData {
|
|
28
|
+
companyName: string;
|
|
29
|
+
companyIssuePrefix: string;
|
|
30
|
+
brandColor: string | null;
|
|
31
|
+
projects: SidebarProject[];
|
|
32
|
+
agents: SidebarAgent[];
|
|
33
|
+
inboxBadge: number;
|
|
34
|
+
failedRuns: number;
|
|
35
|
+
liveRunCount: number;
|
|
36
|
+
/** Whether the Projects feature is exposed in this deployment. Older
|
|
37
|
+
* sidecar versions don't send this — undefined means "fall back to
|
|
38
|
+
* showing the section so we don't break standalone installs". */
|
|
39
|
+
showProjects?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface SidecarBridgeState {
|
|
43
|
+
ready: boolean;
|
|
44
|
+
sidebarData: SidecarSidebarData | null;
|
|
45
|
+
currentSidecarPath: string | null;
|
|
46
|
+
navigate: (path: string) => void;
|
|
47
|
+
command: (action: string) => void;
|
|
48
|
+
setIframeRef: (iframe: HTMLIFrameElement | null) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const SidecarBridgeContext = createContext<SidecarBridgeState>({
|
|
52
|
+
ready: false,
|
|
53
|
+
sidebarData: null,
|
|
54
|
+
currentSidecarPath: null,
|
|
55
|
+
navigate: () => {},
|
|
56
|
+
command: () => {},
|
|
57
|
+
setIframeRef: () => {},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export function useSidecarBridge() {
|
|
61
|
+
return useContext(SidecarBridgeContext);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function SidecarBridgeProvider({ children }: { children: ReactNode }) {
|
|
65
|
+
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
|
66
|
+
const [ready, setReady] = useState(false);
|
|
67
|
+
const [sidebarData, setSidebarData] = useState<SidecarSidebarData | null>(null);
|
|
68
|
+
const [currentSidecarPath, setCurrentSidecarPath] = useState<string | null>(null);
|
|
69
|
+
|
|
70
|
+
const setIframeRef = useCallback((iframe: HTMLIFrameElement | null) => {
|
|
71
|
+
iframeRef.current = iframe;
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
const postToSidecar = useCallback((message: unknown) => {
|
|
75
|
+
if (!iframeRef.current?.contentWindow) return;
|
|
76
|
+
iframeRef.current.contentWindow.postMessage(message, window.location.origin);
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
const navigate = useCallback(
|
|
80
|
+
(path: string) => {
|
|
81
|
+
postToSidecar({ type: "navigate", path });
|
|
82
|
+
},
|
|
83
|
+
[postToSidecar],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const command = useCallback(
|
|
87
|
+
(action: string) => {
|
|
88
|
+
postToSidecar({ type: "command", action });
|
|
89
|
+
},
|
|
90
|
+
[postToSidecar],
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
function onMessage(event: MessageEvent) {
|
|
95
|
+
if (event.origin !== window.location.origin) return;
|
|
96
|
+
const data = event.data;
|
|
97
|
+
if (!data || typeof data.type !== "string") return;
|
|
98
|
+
|
|
99
|
+
switch (data.type) {
|
|
100
|
+
case "ready": {
|
|
101
|
+
// Initial deep-link is now passed through the iframe src
|
|
102
|
+
// (`?initial-path=…` → SidecarFrame). Forwarding on ready would
|
|
103
|
+
// race with the sidecar's CompanyRootRedirect Navigate and
|
|
104
|
+
// sometimes lose the deep link.
|
|
105
|
+
setReady(true);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case "routeChanged": {
|
|
109
|
+
const platformPath = fromSidecarPath(data.path);
|
|
110
|
+
setCurrentSidecarPath(platformPath);
|
|
111
|
+
const current = window.location.pathname + window.location.search;
|
|
112
|
+
if (current !== platformPath) {
|
|
113
|
+
window.history.replaceState(null, "", platformPath);
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case "sidebarData":
|
|
118
|
+
setSidebarData(data.payload);
|
|
119
|
+
break;
|
|
120
|
+
case "toast":
|
|
121
|
+
import("sonner").then(({ toast }) => {
|
|
122
|
+
if (data.level === "error") toast.error(data.message);
|
|
123
|
+
else if (data.level === "success") toast.success(data.message);
|
|
124
|
+
else toast.info(data.message);
|
|
125
|
+
});
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
window.addEventListener("message", onMessage);
|
|
131
|
+
return () => window.removeEventListener("message", onMessage);
|
|
132
|
+
}, []);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<SidecarBridgeContext.Provider
|
|
136
|
+
value={{
|
|
137
|
+
ready,
|
|
138
|
+
sidebarData,
|
|
139
|
+
currentSidecarPath,
|
|
140
|
+
navigate,
|
|
141
|
+
command,
|
|
142
|
+
setIframeRef,
|
|
143
|
+
}}
|
|
144
|
+
>
|
|
145
|
+
{children}
|
|
146
|
+
</SidecarBridgeContext.Provider>
|
|
147
|
+
);
|
|
148
|
+
}
|
package/src/hooks/use-webmcp.ts
CHANGED
|
@@ -17,10 +17,7 @@ export function useWebMCP(): void {
|
|
|
17
17
|
const registeredRef = useRef(false);
|
|
18
18
|
const router = useRouter();
|
|
19
19
|
|
|
20
|
-
const confirm = useCallback(
|
|
21
|
-
(message: string): Promise<boolean> => Promise.resolve(window.confirm(message)),
|
|
22
|
-
[],
|
|
23
|
-
);
|
|
20
|
+
const confirm = useCallback((message: string): Promise<boolean> => Promise.resolve(window.confirm(message)), []);
|
|
24
21
|
|
|
25
22
|
useEffect(() => {
|
|
26
23
|
const isAuthenticated = session?.user != null;
|
|
@@ -167,9 +167,7 @@ describe("getTenantNotes", () => {
|
|
|
167
167
|
afterEach(() => vi.clearAllMocks());
|
|
168
168
|
|
|
169
169
|
it("unwraps notes array from response", async () => {
|
|
170
|
-
const notes = [
|
|
171
|
-
{ id: "n-1", tenant_id: "t-1", admin_user: "admin", content: "Note", created_at: 1000 },
|
|
172
|
-
];
|
|
170
|
+
const notes = [{ id: "n-1", tenant_id: "t-1", admin_user: "admin", content: "Note", created_at: 1000 }];
|
|
173
171
|
mockNotesListQuery.mockResolvedValue({ notes });
|
|
174
172
|
|
|
175
173
|
const result = await getTenantNotes("t-1");
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
3
|
// Use vi.hoisted so mock fns are available inside vi.mock factory (which gets hoisted)
|
|
4
|
-
const { mockListInstances, mockGetInstance, mockCreateInstance, mockControlInstance } = vi.hoisted(
|
|
5
|
-
()
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}),
|
|
11
|
-
);
|
|
4
|
+
const { mockListInstances, mockGetInstance, mockCreateInstance, mockControlInstance } = vi.hoisted(() => ({
|
|
5
|
+
mockListInstances: vi.fn(),
|
|
6
|
+
mockGetInstance: vi.fn(),
|
|
7
|
+
mockCreateInstance: vi.fn(),
|
|
8
|
+
mockControlInstance: vi.fn(),
|
|
9
|
+
}));
|
|
12
10
|
|
|
13
11
|
vi.mock("@/lib/trpc", () => ({
|
|
14
12
|
trpcVanilla: {
|
|
@@ -66,13 +64,7 @@ vi.mock("@/lib/tenant-context", () => ({
|
|
|
66
64
|
getActiveTenantId: vi.fn(() => "tenant-123"),
|
|
67
65
|
}));
|
|
68
66
|
|
|
69
|
-
import {
|
|
70
|
-
controlInstance,
|
|
71
|
-
createInstance,
|
|
72
|
-
deployInstance,
|
|
73
|
-
getInstance,
|
|
74
|
-
listInstances,
|
|
75
|
-
} from "@/lib/api";
|
|
67
|
+
import { controlInstance, createInstance, deployInstance, getInstance, listInstances } from "@/lib/api";
|
|
76
68
|
|
|
77
69
|
describe("listInstances", () => {
|
|
78
70
|
afterEach(() => {
|
|
@@ -315,9 +307,7 @@ describe("deployInstance", () => {
|
|
|
315
307
|
|
|
316
308
|
await deployInstance({ name: "EnvBot", env: { FOO: "bar" } });
|
|
317
309
|
|
|
318
|
-
expect(mockCreateInstance).toHaveBeenCalledWith(
|
|
319
|
-
expect.objectContaining({ env: { FOO: "bar" } }),
|
|
320
|
-
);
|
|
310
|
+
expect(mockCreateInstance).toHaveBeenCalledWith(expect.objectContaining({ env: { FOO: "bar" } }));
|
|
321
311
|
});
|
|
322
312
|
|
|
323
313
|
it("defaults description to empty string", async () => {
|
|
@@ -53,10 +53,7 @@ describe("apiFetch", () => {
|
|
|
53
53
|
await apiFetch("/v1/test");
|
|
54
54
|
|
|
55
55
|
expect(mockFetch).toHaveBeenCalledOnce();
|
|
56
|
-
const [url, init] = mockFetch.mock.calls[0] as [
|
|
57
|
-
string,
|
|
58
|
-
RequestInit & { headers: Record<string, string> },
|
|
59
|
-
];
|
|
56
|
+
const [url, init] = mockFetch.mock.calls[0] as [string, RequestInit & { headers: Record<string, string> }];
|
|
60
57
|
expect(url).toBe("http://localhost:3001/api/v1/test");
|
|
61
58
|
expect(init.credentials).toBe("include");
|
|
62
59
|
expect(init.headers["Content-Type"]).toBe("application/json");
|
|
@@ -74,10 +71,7 @@ describe("apiFetch", () => {
|
|
|
74
71
|
const { apiFetch } = await import("@/lib/api");
|
|
75
72
|
await apiFetch("/v1/bots");
|
|
76
73
|
|
|
77
|
-
const [, init] = mockFetch.mock.calls[0] as [
|
|
78
|
-
string,
|
|
79
|
-
RequestInit & { headers: Record<string, string> },
|
|
80
|
-
];
|
|
74
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit & { headers: Record<string, string> }];
|
|
81
75
|
expect(init.headers["x-tenant-id"]).toBe("tenant-abc");
|
|
82
76
|
});
|
|
83
77
|
|
|
@@ -93,10 +87,7 @@ describe("apiFetch", () => {
|
|
|
93
87
|
const { apiFetch } = await import("@/lib/api");
|
|
94
88
|
await apiFetch("/v1/bots");
|
|
95
89
|
|
|
96
|
-
const [, init] = mockFetch.mock.calls[0] as [
|
|
97
|
-
string,
|
|
98
|
-
RequestInit & { headers: Record<string, string> },
|
|
99
|
-
];
|
|
90
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit & { headers: Record<string, string> }];
|
|
100
91
|
expect(init.headers["x-tenant-id"]).toBeUndefined();
|
|
101
92
|
});
|
|
102
93
|
|
|
@@ -170,10 +161,7 @@ describe("apiFetch", () => {
|
|
|
170
161
|
body: JSON.stringify({ name: "test" }),
|
|
171
162
|
});
|
|
172
163
|
|
|
173
|
-
const [, init] = mockFetch.mock.calls[0] as [
|
|
174
|
-
string,
|
|
175
|
-
RequestInit & { headers: Record<string, string> },
|
|
176
|
-
];
|
|
164
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit & { headers: Record<string, string> }];
|
|
177
165
|
expect(init.method).toBe("POST");
|
|
178
166
|
expect(init.headers["X-Custom"]).toBe("value");
|
|
179
167
|
expect(init.headers["Content-Type"]).toBe("application/json");
|
|
@@ -193,9 +193,7 @@ describe("createOrgTopupCheckout", () => {
|
|
|
193
193
|
|
|
194
194
|
it("propagates tRPC errors", async () => {
|
|
195
195
|
mockOrgTopupCheckoutMutate.mockRejectedValue(new Error("Invalid price"));
|
|
196
|
-
await expect(
|
|
197
|
-
createOrgTopupCheckout("org-1", "bad-price", "http://s", "http://c"),
|
|
198
|
-
).rejects.toThrow("Invalid price");
|
|
196
|
+
await expect(createOrgTopupCheckout("org-1", "bad-price", "http://s", "http://c")).rejects.toThrow("Invalid price");
|
|
199
197
|
});
|
|
200
198
|
});
|
|
201
199
|
|
|
@@ -3,10 +3,6 @@ import { capabilityMeta, pricingData } from "@/lib/pricing-data";
|
|
|
3
3
|
|
|
4
4
|
describe("pricing-data", () => {
|
|
5
5
|
describe("pricingData", () => {
|
|
6
|
-
it("matches snapshot", () => {
|
|
7
|
-
expect(pricingData).toMatchSnapshot();
|
|
8
|
-
});
|
|
9
|
-
|
|
10
6
|
it("has expected bot price", () => {
|
|
11
7
|
expect(pricingData.bot_price).toEqual({ amount: 5, period: "month" });
|
|
12
8
|
});
|
|
@@ -21,10 +17,6 @@ describe("pricing-data", () => {
|
|
|
21
17
|
});
|
|
22
18
|
|
|
23
19
|
describe("capabilityMeta", () => {
|
|
24
|
-
it("matches snapshot", () => {
|
|
25
|
-
expect(capabilityMeta).toMatchSnapshot();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
20
|
it("maps all expected backend keys", () => {
|
|
29
21
|
expect(Object.keys(capabilityMeta).sort()).toEqual(["image_gen", "llm", "sms", "stt", "tts"]);
|
|
30
22
|
});
|
|
@@ -111,9 +111,7 @@ describe("updateNotificationPreferences", () => {
|
|
|
111
111
|
|
|
112
112
|
it("propagates tRPC errors", async () => {
|
|
113
113
|
mockUpdateNotificationPreferencesMutate.mockRejectedValue(new Error("Forbidden"));
|
|
114
|
-
await expect(updateNotificationPreferences({ billing_receipts: true })).rejects.toThrow(
|
|
115
|
-
"Forbidden",
|
|
116
|
-
);
|
|
114
|
+
await expect(updateNotificationPreferences({ billing_receipts: true })).rejects.toThrow("Forbidden");
|
|
117
115
|
});
|
|
118
116
|
});
|
|
119
117
|
|
|
@@ -33,10 +33,7 @@ export async function getAffiliateSuppressions(
|
|
|
33
33
|
return trpcVanilla.admin.affiliateSuppressions.query({ limit, offset });
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export async function getAffiliateVelocity(
|
|
37
|
-
capReferrals = 20,
|
|
38
|
-
capCredits = 20000,
|
|
39
|
-
): Promise<VelocityReferrer[]> {
|
|
36
|
+
export async function getAffiliateVelocity(capReferrals = 20, capCredits = 20000): Promise<VelocityReferrer[]> {
|
|
40
37
|
return trpcVanilla.admin.affiliateVelocity.query({ capReferrals, capCredits });
|
|
41
38
|
}
|
|
42
39
|
|
|
@@ -44,8 +41,6 @@ export async function getAffiliateFingerprintClusters(): Promise<FingerprintClus
|
|
|
44
41
|
return trpcVanilla.admin.affiliateFingerprintClusters.query(undefined);
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
export async function blockAffiliateFingerprint(
|
|
48
|
-
fingerprint: string,
|
|
49
|
-
): Promise<{ success: boolean }> {
|
|
44
|
+
export async function blockAffiliateFingerprint(fingerprint: string): Promise<{ success: boolean }> {
|
|
50
45
|
return trpcVanilla.admin.affiliateBlockFingerprint.mutate({ fingerprint });
|
|
51
46
|
}
|
package/src/lib/admin-api.ts
CHANGED
|
@@ -109,19 +109,11 @@ export async function reactivateTenant(tenantId: string): Promise<void> {
|
|
|
109
109
|
await trpcVanilla.admin.reactivateTenant.mutate({ tenantId });
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
export async function grantCredits(
|
|
113
|
-
tenantId: string,
|
|
114
|
-
amount_cents: number,
|
|
115
|
-
reason: string,
|
|
116
|
-
): Promise<void> {
|
|
112
|
+
export async function grantCredits(tenantId: string, amount_cents: number, reason: string): Promise<void> {
|
|
117
113
|
await trpcVanilla.admin.creditsGrant.mutate({ tenantId, amount_cents, reason });
|
|
118
114
|
}
|
|
119
115
|
|
|
120
|
-
export async function refundCredits(
|
|
121
|
-
tenantId: string,
|
|
122
|
-
amount_cents: number,
|
|
123
|
-
reason: string,
|
|
124
|
-
): Promise<void> {
|
|
116
|
+
export async function refundCredits(tenantId: string, amount_cents: number, reason: string): Promise<void> {
|
|
125
117
|
await trpcVanilla.admin.creditsRefund.mutate({ tenantId, amount_cents, reason });
|
|
126
118
|
}
|
|
127
119
|
|
|
@@ -156,10 +148,7 @@ export async function getTransactions(
|
|
|
156
148
|
return trpcVanilla.admin.creditsTransactions.query({ tenantId, ...filters });
|
|
157
149
|
}
|
|
158
150
|
|
|
159
|
-
export async function getTenantUsageByCapability(
|
|
160
|
-
tenantId: string,
|
|
161
|
-
days = 30,
|
|
162
|
-
): Promise<UsageSummary[]> {
|
|
151
|
+
export async function getTenantUsageByCapability(tenantId: string, days = 30): Promise<UsageSummary[]> {
|
|
163
152
|
const result = await trpcVanilla.admin.tenantUsageByCapability.query({ tenantId, days });
|
|
164
153
|
return result.usage;
|
|
165
154
|
}
|
|
@@ -172,11 +161,7 @@ export async function getUsersList(params?: {
|
|
|
172
161
|
return trpcVanilla.admin.usersList.query(params ?? {});
|
|
173
162
|
}
|
|
174
163
|
|
|
175
|
-
export async function bulkGrantCredits(
|
|
176
|
-
tenantIds: string[],
|
|
177
|
-
amountCents: number,
|
|
178
|
-
reason: string,
|
|
179
|
-
): Promise<void> {
|
|
164
|
+
export async function bulkGrantCredits(tenantIds: string[], amountCents: number, reason: string): Promise<void> {
|
|
180
165
|
await trpcVanilla.admin.bulkGrant.mutate({ tenantIds, amountCents, reason });
|
|
181
166
|
}
|
|
182
167
|
|
|
@@ -310,16 +295,11 @@ export async function getMigrationSnapshots(tenantId: string): Promise<Migration
|
|
|
310
295
|
return result.snapshots;
|
|
311
296
|
}
|
|
312
297
|
|
|
313
|
-
export async function restoreMigrationSnapshot(
|
|
314
|
-
tenantId: string,
|
|
315
|
-
snapshotId: string,
|
|
316
|
-
): Promise<void> {
|
|
298
|
+
export async function restoreMigrationSnapshot(tenantId: string, snapshotId: string): Promise<void> {
|
|
317
299
|
await trpcVanilla.admin.migrationRestore.mutate({ tenantId, snapshotId });
|
|
318
300
|
}
|
|
319
301
|
|
|
320
|
-
export async function getMigrationRestoreHistory(
|
|
321
|
-
tenantId: string,
|
|
322
|
-
): Promise<MigrationRestoreRecord[]> {
|
|
302
|
+
export async function getMigrationRestoreHistory(tenantId: string): Promise<MigrationRestoreRecord[]> {
|
|
323
303
|
const result = await trpcVanilla.admin.migrationRestoreHistory.query({ tenantId });
|
|
324
304
|
return result.history;
|
|
325
305
|
}
|
|
@@ -67,14 +67,11 @@ export interface PostmortemInput {
|
|
|
67
67
|
// --- API calls ---
|
|
68
68
|
|
|
69
69
|
export async function classifyIncidentSeverity(signals: SeveritySignals): Promise<SeverityResult> {
|
|
70
|
-
const result = await apiFetch<{ success: boolean; error?: string } & SeverityResult>(
|
|
71
|
-
"
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
body: JSON.stringify(signals),
|
|
76
|
-
},
|
|
77
|
-
);
|
|
70
|
+
const result = await apiFetch<{ success: boolean; error?: string } & SeverityResult>("/admin/incidents/severity", {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: { "Content-Type": "application/json" },
|
|
73
|
+
body: JSON.stringify(signals),
|
|
74
|
+
});
|
|
78
75
|
if (!result.success) throw new Error(result.error ?? "Request failed");
|
|
79
76
|
return result;
|
|
80
77
|
}
|
|
@@ -91,9 +88,7 @@ export async function getResponseProcedure(severity: IncidentSeverity): Promise<
|
|
|
91
88
|
return result.procedure;
|
|
92
89
|
}
|
|
93
90
|
|
|
94
|
-
export async function getCommunicationTemplates(
|
|
95
|
-
context: CommunicateContext,
|
|
96
|
-
): Promise<CommunicationTemplates> {
|
|
91
|
+
export async function getCommunicationTemplates(context: CommunicateContext): Promise<CommunicationTemplates> {
|
|
97
92
|
const result = await apiFetch<{
|
|
98
93
|
success: boolean;
|
|
99
94
|
templates: CommunicationTemplates;
|
|
@@ -108,14 +103,11 @@ export async function getCommunicationTemplates(
|
|
|
108
103
|
}
|
|
109
104
|
|
|
110
105
|
export async function generatePostmortem(input: PostmortemInput): Promise<string> {
|
|
111
|
-
const result = await apiFetch<{ success: boolean; report: string; error?: string }>(
|
|
112
|
-
"
|
|
113
|
-
{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
body: JSON.stringify(input),
|
|
117
|
-
},
|
|
118
|
-
);
|
|
106
|
+
const result = await apiFetch<{ success: boolean; report: string; error?: string }>("/admin/incidents/postmortem", {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: { "Content-Type": "application/json" },
|
|
109
|
+
body: JSON.stringify(input),
|
|
110
|
+
});
|
|
119
111
|
if (!result.success) throw new Error(result.error ?? "Request failed");
|
|
120
112
|
return result.report;
|
|
121
113
|
}
|
|
@@ -70,11 +70,7 @@ export async function addPluginByNpm(req: AddPluginRequest): Promise<AdminPlugin
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export async function reorderPlugins(orderedIds: string[]): Promise<void> {
|
|
73
|
-
await Promise.all(
|
|
74
|
-
orderedIds.map((id, i) =>
|
|
75
|
-
trpcVanilla.adminMarketplace.updatePlugin.mutate({ id, sort_order: i }),
|
|
76
|
-
),
|
|
77
|
-
);
|
|
73
|
+
await Promise.all(orderedIds.map((id, i) => trpcVanilla.adminMarketplace.updatePlugin.mutate({ id, sort_order: i })));
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
export async function deletePlugin(id: string): Promise<void> {
|
|
@@ -6,64 +6,19 @@ describe("api-config", () => {
|
|
|
6
6
|
vi.resetModules();
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
describe("
|
|
10
|
-
it("
|
|
11
|
-
vi.stubEnv("NODE_ENV", "production");
|
|
12
|
-
vi.stubEnv("NEXT_RUNTIME", "nodejs");
|
|
13
|
-
vi.stubEnv("NEXT_PUBLIC_API_URL", "http://localhost:3001");
|
|
14
|
-
vi.resetModules();
|
|
15
|
-
await expect(import("./api-config")).rejects.toThrow(/internal hostname/i);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("throws when NEXT_PUBLIC_API_URL contains a Docker-internal hostname in production runtime", async () => {
|
|
19
|
-
vi.stubEnv("NODE_ENV", "production");
|
|
20
|
-
vi.stubEnv("NEXT_RUNTIME", "nodejs");
|
|
21
|
-
vi.stubEnv("NEXT_PUBLIC_API_URL", "http://platform-api:3001");
|
|
22
|
-
vi.resetModules();
|
|
23
|
-
await expect(import("./api-config")).rejects.toThrow(/internal hostname/i);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("throws when NEXT_PUBLIC_API_URL is unset in production runtime", async () => {
|
|
27
|
-
vi.stubEnv("NODE_ENV", "production");
|
|
28
|
-
vi.stubEnv("NEXT_RUNTIME", "nodejs");
|
|
29
|
-
vi.stubEnv("NEXT_PUBLIC_API_URL", undefined as unknown as string);
|
|
30
|
-
vi.resetModules();
|
|
31
|
-
await expect(import("./api-config")).rejects.toThrow(/internal hostname/i);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("throws when NEXT_PUBLIC_API_URL uses http (not https) in production runtime", async () => {
|
|
35
|
-
vi.stubEnv("NODE_ENV", "production");
|
|
36
|
-
vi.stubEnv("NEXT_RUNTIME", "nodejs");
|
|
37
|
-
vi.stubEnv("NEXT_PUBLIC_API_URL", "http://api.example.com");
|
|
38
|
-
vi.resetModules();
|
|
39
|
-
await expect(import("./api-config")).rejects.toThrow(/https/i);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("allows https public URL in production runtime", async () => {
|
|
43
|
-
vi.stubEnv("NODE_ENV", "production");
|
|
44
|
-
vi.stubEnv("NEXT_RUNTIME", "nodejs");
|
|
45
|
-
vi.stubEnv("NEXT_PUBLIC_API_URL", "https://api.example.com");
|
|
46
|
-
vi.resetModules();
|
|
47
|
-
const mod = await import("./api-config");
|
|
48
|
-
expect(mod.PLATFORM_BASE_URL).toBe("https://api.example.com");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("skips validation during build time (no NEXT_RUNTIME)", async () => {
|
|
52
|
-
vi.stubEnv("NODE_ENV", "production");
|
|
53
|
-
vi.stubEnv("NEXT_RUNTIME", undefined as unknown as string);
|
|
9
|
+
describe("URL resolution", () => {
|
|
10
|
+
it("uses NEXT_PUBLIC_API_URL directly when set", async () => {
|
|
54
11
|
vi.stubEnv("NEXT_PUBLIC_API_URL", "http://localhost:3001");
|
|
55
12
|
vi.resetModules();
|
|
56
13
|
const mod = await import("./api-config");
|
|
57
14
|
expect(mod.PLATFORM_BASE_URL).toBe("http://localhost:3001");
|
|
58
15
|
});
|
|
59
16
|
|
|
60
|
-
it("
|
|
61
|
-
vi.stubEnv("
|
|
62
|
-
vi.stubEnv("NEXT_RUNTIME", "nodejs");
|
|
63
|
-
vi.stubEnv("NEXT_PUBLIC_API_URL", "http://localhost:3001");
|
|
17
|
+
it("accepts https URL", async () => {
|
|
18
|
+
vi.stubEnv("NEXT_PUBLIC_API_URL", "https://api.example.com");
|
|
64
19
|
vi.resetModules();
|
|
65
20
|
const mod = await import("./api-config");
|
|
66
|
-
expect(mod.PLATFORM_BASE_URL).toBe("
|
|
21
|
+
expect(mod.PLATFORM_BASE_URL).toBe("https://api.example.com");
|
|
67
22
|
});
|
|
68
23
|
});
|
|
69
24
|
|