@hexclave/next 1.0.5 → 1.0.8
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/components-page/account-settings/payments/payments-panel.js +3 -3
- package/dist/components-page/account-settings/payments/payments-panel.js.map +1 -1
- package/dist/components-page/hexclave-handler-client.d.ts +13 -1
- package/dist/components-page/hexclave-handler-client.d.ts.map +1 -1
- package/dist/components-page/hexclave-handler-client.js +44 -9
- package/dist/components-page/hexclave-handler-client.js.map +1 -1
- package/dist/components-page/hexclave-handler-client.test.d.ts +1 -0
- package/dist/components-page/hexclave-handler-client.test.js +51 -0
- package/dist/components-page/hexclave-handler-client.test.js.map +1 -0
- package/dist/dev-tool/dev-tool-core.js +2 -2
- package/dist/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/esm/components-page/account-settings/payments/payments-panel.js +2 -2
- package/dist/esm/components-page/account-settings/payments/payments-panel.js.map +1 -1
- package/dist/esm/components-page/hexclave-handler-client.d.ts +12 -1
- package/dist/esm/components-page/hexclave-handler-client.d.ts.map +1 -1
- package/dist/esm/components-page/hexclave-handler-client.js +46 -12
- package/dist/esm/components-page/hexclave-handler-client.js.map +1 -1
- package/dist/esm/components-page/hexclave-handler-client.test.d.ts +1 -0
- package/dist/esm/components-page/hexclave-handler-client.test.js +51 -0
- package/dist/esm/components-page/hexclave-handler-client.test.js.map +1 -0
- package/dist/esm/dev-tool/dev-tool-core.js +2 -2
- package/dist/esm/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/esm/generated/env.d.ts +26 -0
- package/dist/esm/{lib → generated}/env.d.ts.map +1 -1
- package/dist/esm/generated/env.js +67 -0
- package/dist/esm/generated/env.js.map +1 -0
- package/dist/esm/generated/quetzal-translations.d.ts +2 -2
- package/dist/esm/global.d.ts +8 -1
- package/dist/esm/global.d.ts.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +263 -3
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +3 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +53 -26
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts +8 -8
- package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +28 -14
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
- package/dist/esm/lib/hexclave-app/url-targets.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/url-targets.js +25 -11
- package/dist/esm/lib/hexclave-app/url-targets.js.map +1 -1
- package/dist/esm/lib/hexclave-app/url-targets.test.js +12 -0
- package/dist/esm/lib/hexclave-app/url-targets.test.js.map +1 -1
- package/dist/generated/env.d.ts +26 -0
- package/dist/{lib → generated}/env.d.ts.map +1 -1
- package/dist/generated/env.js +69 -0
- package/dist/generated/env.js.map +1 -0
- package/dist/generated/quetzal-translations.d.ts +2 -2
- package/dist/global.d.ts +8 -1
- package/dist/global.d.ts.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +263 -3
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +3 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +52 -25
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.d.ts +8 -8
- package/dist/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.js +28 -14
- package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -1
- package/dist/lib/hexclave-app/url-targets.d.ts.map +1 -1
- package/dist/lib/hexclave-app/url-targets.js +25 -11
- package/dist/lib/hexclave-app/url-targets.js.map +1 -1
- package/dist/lib/hexclave-app/url-targets.test.js +12 -0
- package/dist/lib/hexclave-app/url-targets.test.js.map +1 -1
- package/package.json +9 -7
- package/src/components-page/account-settings/payments/payments-panel.tsx +2 -2
- package/src/components-page/hexclave-handler-client.test.tsx +64 -0
- package/src/components-page/hexclave-handler-client.tsx +50 -11
- package/src/dev-tool/dev-tool-core.ts +2 -2
- package/src/generated/.gitignore +1 -1
- package/src/global.d.ts +8 -1
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +316 -3
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +69 -25
- package/src/lib/hexclave-app/apps/implementations/common.ts +34 -14
- package/src/lib/hexclave-app/url-targets.test.ts +17 -0
- package/src/lib/hexclave-app/url-targets.ts +25 -7
- package/dist/esm/lib/env.d.ts +0 -42
- package/dist/esm/lib/env.js +0 -93
- package/dist/esm/lib/env.js.map +0 -1
- package/dist/lib/env.d.ts +0 -42
- package/dist/lib/env.js +0 -95
- package/dist/lib/env.js.map +0 -1
- package/src/lib/env.ts +0 -93
|
@@ -13,7 +13,7 @@ let ___section_js = require("../section.js");
|
|
|
13
13
|
let _________index_js = require("../../../index.js");
|
|
14
14
|
let _stripe_react_stripe_js = require("@stripe/react-stripe-js");
|
|
15
15
|
let _stripe_stripe_js = require("@stripe/stripe-js");
|
|
16
|
-
let
|
|
16
|
+
let _________generated_env_js = require("../../../generated/env.js");
|
|
17
17
|
|
|
18
18
|
//#region src/components-page/account-settings/payments/payments-panel.tsx
|
|
19
19
|
function formatPaymentMethod(pm) {
|
|
@@ -192,7 +192,7 @@ function RealPaymentsPanel(props) {
|
|
|
192
192
|
const [switchToProductId, setSwitchToProductId] = (0, react.useState)(null);
|
|
193
193
|
const stripePromise = (0, react.useMemo)(() => {
|
|
194
194
|
if (!setupIntentStripeAccountId) return null;
|
|
195
|
-
const publishableKey =
|
|
195
|
+
const publishableKey = _________generated_env_js.envVars.HEXCLAVE_STRIPE_PUBLISHABLE_KEY;
|
|
196
196
|
if (!publishableKey) return null;
|
|
197
197
|
return (0, _stripe_stripe_js.loadStripe)(publishableKey, { stripeAccount: setupIntentStripeAccountId });
|
|
198
198
|
}, [setupIntentStripeAccountId]);
|
|
@@ -205,7 +205,7 @@ function RealPaymentsPanel(props) {
|
|
|
205
205
|
});
|
|
206
206
|
return;
|
|
207
207
|
}
|
|
208
|
-
alert(`An unhandled error occurred. Please ${
|
|
208
|
+
alert(`An unhandled error occurred. Please ${_________generated_env_js.envVars.NODE_ENV === "development" ? "check the browser console for the full error." : "report this to the developer."}\n\n${error}`);
|
|
209
209
|
};
|
|
210
210
|
const openPaymentDialog = () => {
|
|
211
211
|
(0, _hexclave_shared_dist_utils_promises.runAsynchronously)(async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payments-panel.js","names":["Typography","CardElement","Button","Section","envVars","KnownErrors","ActionDialog","Skeleton","Elements","Result","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Separator","Table","TableHeader","TableRow","TableHead","TableBody","TableCell"],"sources":["../../../../src/components-page/account-settings/payments/payments-panel.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { KnownErrors } from \"@hexclave/shared\";\nimport { runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { ActionDialog, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, Typography } from \"@hexclave/ui\";\nimport { CardElement, Elements, useElements, useStripe } from \"@stripe/react-stripe-js\";\nimport { loadStripe } from \"@stripe/stripe-js\";\nimport { useMemo, useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { envVars } from \"../../../lib/env\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport type { CustomerInvoiceStatus, CustomerInvoicesList, CustomerInvoicesListOptions } from \"../../../lib/hexclave-app/customers\";\n\ntype PaymentMethodSummary = {\n id: string,\n brand: string | null,\n last4: string | null,\n exp_month: number | null,\n exp_year: number | null,\n} | null;\n\nfunction formatPaymentMethod(pm: NonNullable<PaymentMethodSummary>) {\n const details = [\n pm.brand ? pm.brand.toUpperCase() : null,\n pm.last4 ? `•••• ${pm.last4}` : null,\n pm.exp_month && pm.exp_year ? `exp ${pm.exp_month}/${pm.exp_year}` : null,\n ].filter(Boolean);\n return details.join(\" · \");\n}\n\nconst formatInvoiceStatus = (status: CustomerInvoiceStatus, t: (value: string) => string) => {\n if (!status) {\n return t(\"Unknown\");\n }\n switch (status) {\n case \"draft\": {\n return t(\"Draft\");\n }\n case \"open\": {\n return t(\"Open\");\n }\n case \"paid\": {\n return t(\"Paid\");\n }\n case \"uncollectible\": {\n return t(\"Uncollectible\");\n }\n case \"void\": {\n return t(\"Void\");\n }\n default: {\n return t(\"Unknown\");\n }\n }\n};\n\nconst formatInvoiceAmount = (amountTotal: number | null | undefined, t: (value: string) => string) => {\n if (typeof amountTotal !== \"number\" || Number.isNaN(amountTotal)) {\n return t(\"Unknown\");\n }\n const normalized = amountTotal / 100;\n const formatted = new Intl.NumberFormat(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(normalized);\n return `$${formatted}`;\n};\n\nconst formatInvoiceDate = (date: Date | null | undefined, t: (value: string) => string) => {\n if (!date || Number.isNaN(date.getTime())) {\n return t(\"Unknown\");\n }\n return new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(date);\n};\n\ntype CustomerBilling = {\n hasCustomer: boolean,\n defaultPaymentMethod: PaymentMethodSummary,\n};\n\ntype CustomerPaymentMethodSetupIntent = {\n clientSecret: string,\n stripeAccountId: string,\n};\n\ntype CustomerLike = {\n id: string,\n useBilling: () => CustomerBilling,\n useProducts: () => Array<{\n id: string | null,\n quantity: number,\n displayName: string,\n customerType: \"user\" | \"team\" | \"custom\",\n type?: \"one_time\" | \"subscription\",\n switchOptions?: Array<{\n productId: string,\n displayName: string,\n prices: Record<string, { interval?: [number, \"day\" | \"week\" | \"month\" | \"year\"] }>,\n }>,\n subscription: null | {\n subscriptionId: string | null,\n currentPeriodEnd: Date | null,\n cancelAtPeriodEnd: boolean,\n isCancelable: boolean,\n },\n }>,\n useInvoices: (options?: CustomerInvoicesListOptions) => CustomerInvoicesList,\n createPaymentMethodSetupIntent: () => Promise<CustomerPaymentMethodSetupIntent>,\n setDefaultPaymentMethodFromSetupIntent: (setupIntentId: string) => Promise<PaymentMethodSummary>,\n switchSubscription: (options: { fromProductId: string, toProductId: string, priceId?: string, quantity?: number }) => Promise<void>,\n};\n\nfunction SetDefaultPaymentMethodForm(props: {\n clientSecret: string,\n onSetupIntentSucceeded: (setupIntentId: string) => Promise<void>,\n}) {\n const stripe = useStripe();\n const elements = useElements();\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const darkMode = \"color-scheme\" in document.documentElement.style && document.documentElement.style[\"color-scheme\"] === \"dark\";\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Typography className=\"font-medium\">Card details</Typography>\n <div className=\"rounded-md border border-input p-3\">\n <CardElement options={{ hidePostalCode: true, style: { base: { color: darkMode ? \"white\" : \"black\" } } }} />\n </div>\n </div>\n {errorMessage && (\n <Typography variant=\"secondary\" type=\"footnote\">\n {errorMessage}\n </Typography>\n )}\n <Button\n onClick={async () => {\n if (!stripe || !elements) {\n setErrorMessage(\"Stripe is still loading. Please try again.\");\n return;\n }\n const card = elements.getElement(CardElement);\n if (!card) {\n setErrorMessage(\"Card element not found.\");\n return;\n }\n\n const result = await stripe.confirmCardSetup(props.clientSecret, {\n payment_method: { card },\n });\n if (result.error) {\n setErrorMessage(result.error.message ?? \"Failed to save payment method.\");\n return;\n }\n if (!result.setupIntent.id) {\n setErrorMessage(\"No setup intent returned from Stripe.\");\n return;\n }\n await props.onSetupIntentSucceeded(result.setupIntent.id);\n }}\n >\n Save payment method\n </Button>\n </div>\n );\n}\n\nexport function PaymentsPanel(props: {\n title?: string,\n customer?: CustomerLike,\n customerType?: \"user\" | \"team\",\n mockMode?: boolean,\n}) {\n if (props.mockMode) {\n return <MockPaymentsPanel title={props.title} />;\n }\n if (!props.customer) {\n return null;\n }\n return <RealPaymentsPanel title={props.title} customer={props.customer} customerType={props.customerType ?? \"user\"} />;\n}\n\nfunction MockPaymentsPanel(props: { title?: string }) {\n const { t } = useTranslation();\n const defaultPaymentMethod: PaymentMethodSummary = {\n id: \"pm_mock\",\n brand: \"visa\",\n last4: \"4242\",\n exp_month: 12,\n exp_year: 2030,\n };\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n <Button disabled>\n {t(\"Update payment method\")}\n </Button>\n </Section>\n\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Pro\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Renews on\")} Jan 1, 2030</Typography>\n </div>\n <Button disabled variant=\"secondary\" color=\"neutral\">\n {t(\"Cancel subscription\")}\n </Button>\n </div>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Credits pack\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"One-time purchase\")}</Typography>\n </div>\n </div>\n </div>\n </Section>\n </div>\n );\n}\n\nfunction RealPaymentsPanel(props: { title?: string, customer: CustomerLike, customerType: \"user\" | \"team\" }) {\n const { t } = useTranslation();\n const hexclaveApp = useStackApp();\n const billing = props.customer.useBilling();\n const defaultPaymentMethod = billing.defaultPaymentMethod;\n const products = props.customer.useProducts();\n const invoices = props.customer.useInvoices({ limit: 10 });\n const productsForCustomerType = products.filter(product => product.customerType === props.customerType);\n\n const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);\n const [setupIntentClientSecret, setSetupIntentClientSecret] = useState<string | null>(null);\n const [setupIntentStripeAccountId, setSetupIntentStripeAccountId] = useState<string | null>(null);\n const [cancelTarget, setCancelTarget] = useState<{ productId: string, subscriptionId?: string } | null>(null);\n const [switchFromProductId, setSwitchFromProductId] = useState<string | null>(null);\n const [switchToProductId, setSwitchToProductId] = useState<string | null>(null);\n\n const stripePromise = useMemo(() => {\n if (!setupIntentStripeAccountId) return null;\n const publishableKey = envVars.NEXT_PUBLIC_STACK_STRIPE_PUBLISHABLE_KEY;\n if (!publishableKey) return null;\n return loadStripe(publishableKey, { stripeAccount: setupIntentStripeAccountId });\n }, [setupIntentStripeAccountId]);\n\n const handleAsyncError = (error: unknown) => {\n if (error instanceof KnownErrors.DefaultPaymentMethodRequired) {\n toast({\n title: t(\"No default payment method\"),\n description: t(\"Add a payment method before switching plans.\"),\n variant: \"destructive\",\n });\n return;\n }\n alert(`An unhandled error occurred. Please ${envVars.NODE_ENV === \"development\" ? \"check the browser console for the full error.\" : \"report this to the developer.\"}\\n\\n${error}`);\n };\n\n const openPaymentDialog = () => {\n runAsynchronously(async () => {\n setPaymentDialogOpen(true);\n const res = await props.customer.createPaymentMethodSetupIntent();\n setSetupIntentClientSecret(res.clientSecret);\n setSetupIntentStripeAccountId(res.stripeAccountId);\n }, { onError: handleAsyncError });\n };\n\n const closePaymentDialog = () => {\n setPaymentDialogOpen(false);\n setSetupIntentClientSecret(null);\n setSetupIntentStripeAccountId(null);\n };\n\n const openSwitchDialog = (productId: string, firstOptionId: string | null) => {\n setSwitchFromProductId(productId);\n setSwitchToProductId(firstOptionId);\n };\n\n const closeSwitchDialog = () => {\n setSwitchFromProductId(null);\n setSwitchToProductId(null);\n };\n\n const switchSourceProduct = switchFromProductId\n ? productsForCustomerType.find((product) => product.id === switchFromProductId) ?? null\n : null;\n const switchOptions = switchSourceProduct?.switchOptions ?? [];\n const selectedSwitchOption = switchOptions.find((option) => option.productId === switchToProductId) ?? null;\n const selectedPriceId = selectedSwitchOption ? (Object.keys(selectedSwitchOption.prices)[0] ?? null) : null;\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n\n {defaultPaymentMethod && (\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n\n <Button onClick={openPaymentDialog}>\n {t(\"Update payment method\")}\n </Button>\n\n <ActionDialog\n open={paymentDialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n closePaymentDialog();\n } else {\n setPaymentDialogOpen(true);\n }\n }}\n title={t(\"Update payment method\")}\n >\n {!setupIntentClientSecret || !setupIntentStripeAccountId || !stripePromise ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : (\n <Elements\n stripe={stripePromise}\n options={{\n clientSecret: setupIntentClientSecret,\n }}\n >\n <SetDefaultPaymentMethodForm\n clientSecret={setupIntentClientSecret}\n onSetupIntentSucceeded={async (setupIntentId) => {\n await props.customer.setDefaultPaymentMethodFromSetupIntent(setupIntentId);\n closePaymentDialog();\n }}\n />\n </Elements>\n )}\n </ActionDialog>\n </Section>\n )}\n\n {productsForCustomerType.length > 0 && (\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n {productsForCustomerType.map((product, index) => {\n const quantitySuffix = product.quantity !== 1 ? ` ×${product.quantity}` : \"\";\n const isSubscription = product.type === \"subscription\";\n const isCancelable = isSubscription && !!product.subscription?.isCancelable;\n const canSwitchPlans = isSubscription && defaultPaymentMethod && !!product.id && (product.switchOptions?.length ?? 0) > 0;\n const renewsAt = isSubscription ? (product.subscription?.currentPeriodEnd ?? null) : null;\n const subtitle =\n product.type === \"one_time\"\n ? t(\"One-time purchase\")\n : renewsAt\n ? `${t(\"Renews on\")} ${new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(renewsAt)}`\n : t(\"Subscription\");\n\n return (\n <div key={product.id ?? `${product.displayName}-${index}`} className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{product.displayName}{quantitySuffix}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{subtitle}</Typography>\n </div>\n\n <div className=\"flex flex-col items-end gap-2\">\n {canSwitchPlans && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => openSwitchDialog(product.id!, product.switchOptions?.[0]?.productId ?? null)}\n >\n {t(\"Change plan\")}\n </Button>\n )}\n {isCancelable && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => setCancelTarget({ productId: product.id ?? \"_inline\", subscriptionId: product.subscription?.subscriptionId ?? undefined })}\n >\n {t(\"Cancel subscription\")}\n </Button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n <ActionDialog\n open={cancelTarget !== null}\n onOpenChange={(open) => {\n if (!open) setCancelTarget(null);\n }}\n title={t(\"Cancel subscription\")}\n description={t(\"Canceling will stop future renewals for this subscription.\")}\n danger\n cancelButton\n okButton={{\n label: t(\"Cancel subscription\"),\n onClick: async () => {\n if (!cancelTarget) return;\n const { productId, subscriptionId } = cancelTarget;\n if (props.customerType === \"team\") {\n await hexclaveApp.cancelSubscription({ teamId: props.customer.id, productId, subscriptionId });\n } else {\n await hexclaveApp.cancelSubscription({ productId, subscriptionId });\n }\n setCancelTarget(null);\n },\n }}\n />\n\n <ActionDialog\n open={switchFromProductId !== null}\n onOpenChange={(open) => {\n if (!open) closeSwitchDialog();\n }}\n title={t(\"Change plan\")}\n description={t(\"Select a new plan from the same product line.\")}\n cancelButton\n okButton={{\n label: t(\"Switch plan\"),\n onClick: async () => {\n const fromProductId = switchFromProductId;\n const toProductId = switchToProductId;\n if (!fromProductId || !toProductId) return;\n if (!selectedPriceId) return;\n const result = await Result.fromThrowingAsync(() => props.customer.switchSubscription({\n fromProductId,\n toProductId,\n priceId: selectedPriceId,\n }));\n if (result.status === \"error\") {\n handleAsyncError(result.error);\n return \"prevent-close\";\n }\n closeSwitchDialog();\n },\n props: {\n disabled: !switchFromProductId || !switchToProductId || !selectedPriceId,\n },\n }}\n >\n <div className=\"space-y-2\">\n {switchOptions.length === 0 ? (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"No other plans available for this subscription.\")}\n </Typography>\n ) : (\n <>\n <Typography type=\"footnote\">{t(\"Choose a plan\")}</Typography>\n <Select\n value={switchToProductId ?? undefined}\n onValueChange={(value) => setSwitchToProductId(value || null)}\n >\n <SelectTrigger className=\"w-full\">\n <SelectValue placeholder={t(\"Choose a plan\")} />\n </SelectTrigger>\n <SelectContent>\n {switchOptions.map((option: NonNullable<typeof switchOptions>[number]) => (\n <SelectItem key={option.productId} value={option.productId}>\n {option.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )}\n </div>\n </ActionDialog>\n </Section>\n )\n }\n {invoices.length > 0 && (\n <>\n <Separator />\n <div className=\"space-y-2\">\n <div className=\"space-y-1\">\n <Typography className=\"font-medium\">{t(\"Invoices\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Review past invoices and receipts.\")}</Typography>\n </div>\n <div className=\"border rounded-md\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[140px]\">{t(\"Date\")}</TableHead>\n <TableHead className=\"w-[120px]\">{t(\"Status\")}</TableHead>\n <TableHead className=\"w-[120px]\">{t(\"Amount\")}</TableHead>\n <TableHead className=\"w-[120px] text-right\">{t(\"Invoice\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {invoices.map((invoice, index) => {\n const createdAtTime = invoice.createdAt.getTime();\n const invoiceKey = Number.isNaN(createdAtTime) ? `invoice-${index}` : `invoice-${createdAtTime}-${index}`;\n return (\n <TableRow key={invoiceKey}>\n <TableCell>\n <Typography>{formatInvoiceDate(invoice.createdAt, t)}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{formatInvoiceStatus(invoice.status, t)}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{formatInvoiceAmount(invoice.amountTotal, t)}</Typography>\n </TableCell>\n <TableCell align=\"right\">\n {invoice.hostedInvoiceUrl ? (\n <Button asChild variant=\"secondary\" color=\"neutral\" size=\"sm\">\n <a href={invoice.hostedInvoiceUrl} target=\"_blank\" rel=\"noreferrer\">\n {t(\"View\")}\n </a>\n </Button>\n ) : (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"Unavailable\")}\n </Typography>\n )}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </div>\n </>\n )}\n </div >\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"payments-panel.js","names":["Typography","CardElement","Button","Section","envVars","KnownErrors","ActionDialog","Skeleton","Elements","Result","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Separator","Table","TableHeader","TableRow","TableHead","TableBody","TableCell"],"sources":["../../../../src/components-page/account-settings/payments/payments-panel.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { KnownErrors } from \"@hexclave/shared\";\nimport { runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { ActionDialog, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, Typography } from \"@hexclave/ui\";\nimport { CardElement, Elements, useElements, useStripe } from \"@stripe/react-stripe-js\";\nimport { loadStripe } from \"@stripe/stripe-js\";\nimport { useMemo, useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { envVars } from \"../../../generated/env\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport type { CustomerInvoiceStatus, CustomerInvoicesList, CustomerInvoicesListOptions } from \"../../../lib/hexclave-app/customers\";\n\ntype PaymentMethodSummary = {\n id: string,\n brand: string | null,\n last4: string | null,\n exp_month: number | null,\n exp_year: number | null,\n} | null;\n\nfunction formatPaymentMethod(pm: NonNullable<PaymentMethodSummary>) {\n const details = [\n pm.brand ? pm.brand.toUpperCase() : null,\n pm.last4 ? `•••• ${pm.last4}` : null,\n pm.exp_month && pm.exp_year ? `exp ${pm.exp_month}/${pm.exp_year}` : null,\n ].filter(Boolean);\n return details.join(\" · \");\n}\n\nconst formatInvoiceStatus = (status: CustomerInvoiceStatus, t: (value: string) => string) => {\n if (!status) {\n return t(\"Unknown\");\n }\n switch (status) {\n case \"draft\": {\n return t(\"Draft\");\n }\n case \"open\": {\n return t(\"Open\");\n }\n case \"paid\": {\n return t(\"Paid\");\n }\n case \"uncollectible\": {\n return t(\"Uncollectible\");\n }\n case \"void\": {\n return t(\"Void\");\n }\n default: {\n return t(\"Unknown\");\n }\n }\n};\n\nconst formatInvoiceAmount = (amountTotal: number | null | undefined, t: (value: string) => string) => {\n if (typeof amountTotal !== \"number\" || Number.isNaN(amountTotal)) {\n return t(\"Unknown\");\n }\n const normalized = amountTotal / 100;\n const formatted = new Intl.NumberFormat(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(normalized);\n return `$${formatted}`;\n};\n\nconst formatInvoiceDate = (date: Date | null | undefined, t: (value: string) => string) => {\n if (!date || Number.isNaN(date.getTime())) {\n return t(\"Unknown\");\n }\n return new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(date);\n};\n\ntype CustomerBilling = {\n hasCustomer: boolean,\n defaultPaymentMethod: PaymentMethodSummary,\n};\n\ntype CustomerPaymentMethodSetupIntent = {\n clientSecret: string,\n stripeAccountId: string,\n};\n\ntype CustomerLike = {\n id: string,\n useBilling: () => CustomerBilling,\n useProducts: () => Array<{\n id: string | null,\n quantity: number,\n displayName: string,\n customerType: \"user\" | \"team\" | \"custom\",\n type?: \"one_time\" | \"subscription\",\n switchOptions?: Array<{\n productId: string,\n displayName: string,\n prices: Record<string, { interval?: [number, \"day\" | \"week\" | \"month\" | \"year\"] }>,\n }>,\n subscription: null | {\n subscriptionId: string | null,\n currentPeriodEnd: Date | null,\n cancelAtPeriodEnd: boolean,\n isCancelable: boolean,\n },\n }>,\n useInvoices: (options?: CustomerInvoicesListOptions) => CustomerInvoicesList,\n createPaymentMethodSetupIntent: () => Promise<CustomerPaymentMethodSetupIntent>,\n setDefaultPaymentMethodFromSetupIntent: (setupIntentId: string) => Promise<PaymentMethodSummary>,\n switchSubscription: (options: { fromProductId: string, toProductId: string, priceId?: string, quantity?: number }) => Promise<void>,\n};\n\nfunction SetDefaultPaymentMethodForm(props: {\n clientSecret: string,\n onSetupIntentSucceeded: (setupIntentId: string) => Promise<void>,\n}) {\n const stripe = useStripe();\n const elements = useElements();\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const darkMode = \"color-scheme\" in document.documentElement.style && document.documentElement.style[\"color-scheme\"] === \"dark\";\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Typography className=\"font-medium\">Card details</Typography>\n <div className=\"rounded-md border border-input p-3\">\n <CardElement options={{ hidePostalCode: true, style: { base: { color: darkMode ? \"white\" : \"black\" } } }} />\n </div>\n </div>\n {errorMessage && (\n <Typography variant=\"secondary\" type=\"footnote\">\n {errorMessage}\n </Typography>\n )}\n <Button\n onClick={async () => {\n if (!stripe || !elements) {\n setErrorMessage(\"Stripe is still loading. Please try again.\");\n return;\n }\n const card = elements.getElement(CardElement);\n if (!card) {\n setErrorMessage(\"Card element not found.\");\n return;\n }\n\n const result = await stripe.confirmCardSetup(props.clientSecret, {\n payment_method: { card },\n });\n if (result.error) {\n setErrorMessage(result.error.message ?? \"Failed to save payment method.\");\n return;\n }\n if (!result.setupIntent.id) {\n setErrorMessage(\"No setup intent returned from Stripe.\");\n return;\n }\n await props.onSetupIntentSucceeded(result.setupIntent.id);\n }}\n >\n Save payment method\n </Button>\n </div>\n );\n}\n\nexport function PaymentsPanel(props: {\n title?: string,\n customer?: CustomerLike,\n customerType?: \"user\" | \"team\",\n mockMode?: boolean,\n}) {\n if (props.mockMode) {\n return <MockPaymentsPanel title={props.title} />;\n }\n if (!props.customer) {\n return null;\n }\n return <RealPaymentsPanel title={props.title} customer={props.customer} customerType={props.customerType ?? \"user\"} />;\n}\n\nfunction MockPaymentsPanel(props: { title?: string }) {\n const { t } = useTranslation();\n const defaultPaymentMethod: PaymentMethodSummary = {\n id: \"pm_mock\",\n brand: \"visa\",\n last4: \"4242\",\n exp_month: 12,\n exp_year: 2030,\n };\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n <Button disabled>\n {t(\"Update payment method\")}\n </Button>\n </Section>\n\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Pro\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Renews on\")} Jan 1, 2030</Typography>\n </div>\n <Button disabled variant=\"secondary\" color=\"neutral\">\n {t(\"Cancel subscription\")}\n </Button>\n </div>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{t(\"Credits pack\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"One-time purchase\")}</Typography>\n </div>\n </div>\n </div>\n </Section>\n </div>\n );\n}\n\nfunction RealPaymentsPanel(props: { title?: string, customer: CustomerLike, customerType: \"user\" | \"team\" }) {\n const { t } = useTranslation();\n const hexclaveApp = useStackApp();\n const billing = props.customer.useBilling();\n const defaultPaymentMethod = billing.defaultPaymentMethod;\n const products = props.customer.useProducts();\n const invoices = props.customer.useInvoices({ limit: 10 });\n const productsForCustomerType = products.filter(product => product.customerType === props.customerType);\n\n const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);\n const [setupIntentClientSecret, setSetupIntentClientSecret] = useState<string | null>(null);\n const [setupIntentStripeAccountId, setSetupIntentStripeAccountId] = useState<string | null>(null);\n const [cancelTarget, setCancelTarget] = useState<{ productId: string, subscriptionId?: string } | null>(null);\n const [switchFromProductId, setSwitchFromProductId] = useState<string | null>(null);\n const [switchToProductId, setSwitchToProductId] = useState<string | null>(null);\n\n const stripePromise = useMemo(() => {\n if (!setupIntentStripeAccountId) return null;\n const publishableKey = envVars.HEXCLAVE_STRIPE_PUBLISHABLE_KEY;\n if (!publishableKey) return null;\n return loadStripe(publishableKey, { stripeAccount: setupIntentStripeAccountId });\n }, [setupIntentStripeAccountId]);\n\n const handleAsyncError = (error: unknown) => {\n if (error instanceof KnownErrors.DefaultPaymentMethodRequired) {\n toast({\n title: t(\"No default payment method\"),\n description: t(\"Add a payment method before switching plans.\"),\n variant: \"destructive\",\n });\n return;\n }\n alert(`An unhandled error occurred. Please ${envVars.NODE_ENV === \"development\" ? \"check the browser console for the full error.\" : \"report this to the developer.\"}\\n\\n${error}`);\n };\n\n const openPaymentDialog = () => {\n runAsynchronously(async () => {\n setPaymentDialogOpen(true);\n const res = await props.customer.createPaymentMethodSetupIntent();\n setSetupIntentClientSecret(res.clientSecret);\n setSetupIntentStripeAccountId(res.stripeAccountId);\n }, { onError: handleAsyncError });\n };\n\n const closePaymentDialog = () => {\n setPaymentDialogOpen(false);\n setSetupIntentClientSecret(null);\n setSetupIntentStripeAccountId(null);\n };\n\n const openSwitchDialog = (productId: string, firstOptionId: string | null) => {\n setSwitchFromProductId(productId);\n setSwitchToProductId(firstOptionId);\n };\n\n const closeSwitchDialog = () => {\n setSwitchFromProductId(null);\n setSwitchToProductId(null);\n };\n\n const switchSourceProduct = switchFromProductId\n ? productsForCustomerType.find((product) => product.id === switchFromProductId) ?? null\n : null;\n const switchOptions = switchSourceProduct?.switchOptions ?? [];\n const selectedSwitchOption = switchOptions.find((option) => option.productId === switchToProductId) ?? null;\n const selectedPriceId = selectedSwitchOption ? (Object.keys(selectedSwitchOption.prices)[0] ?? null) : null;\n\n return (\n <div className=\"space-y-4\">\n {props.title && <Typography className=\"font-medium\">{props.title}</Typography>}\n\n {defaultPaymentMethod && (\n <Section\n title={t(\"Payment method\")}\n description={t(\"Manage the default payment method used for subscriptions and invoices.\")}\n >\n <Typography>{formatPaymentMethod(defaultPaymentMethod)}</Typography>\n\n <Button onClick={openPaymentDialog}>\n {t(\"Update payment method\")}\n </Button>\n\n <ActionDialog\n open={paymentDialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n closePaymentDialog();\n } else {\n setPaymentDialogOpen(true);\n }\n }}\n title={t(\"Update payment method\")}\n >\n {!setupIntentClientSecret || !setupIntentStripeAccountId || !stripePromise ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : (\n <Elements\n stripe={stripePromise}\n options={{\n clientSecret: setupIntentClientSecret,\n }}\n >\n <SetDefaultPaymentMethodForm\n clientSecret={setupIntentClientSecret}\n onSetupIntentSucceeded={async (setupIntentId) => {\n await props.customer.setDefaultPaymentMethodFromSetupIntent(setupIntentId);\n closePaymentDialog();\n }}\n />\n </Elements>\n )}\n </ActionDialog>\n </Section>\n )}\n\n {productsForCustomerType.length > 0 && (\n <Section\n title={t(\"Active plans\")}\n description={t(\"View your active plans and purchases.\")}\n >\n <div className=\"space-y-3\">\n {productsForCustomerType.map((product, index) => {\n const quantitySuffix = product.quantity !== 1 ? ` ×${product.quantity}` : \"\";\n const isSubscription = product.type === \"subscription\";\n const isCancelable = isSubscription && !!product.subscription?.isCancelable;\n const canSwitchPlans = isSubscription && defaultPaymentMethod && !!product.id && (product.switchOptions?.length ?? 0) > 0;\n const renewsAt = isSubscription ? (product.subscription?.currentPeriodEnd ?? null) : null;\n const subtitle =\n product.type === \"one_time\"\n ? t(\"One-time purchase\")\n : renewsAt\n ? `${t(\"Renews on\")} ${new Intl.DateTimeFormat(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" }).format(renewsAt)}`\n : t(\"Subscription\");\n\n return (\n <div key={product.id ?? `${product.displayName}-${index}`} className=\"flex items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <Typography className=\"truncate\">{product.displayName}{quantitySuffix}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{subtitle}</Typography>\n </div>\n\n <div className=\"flex flex-col items-end gap-2\">\n {canSwitchPlans && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => openSwitchDialog(product.id!, product.switchOptions?.[0]?.productId ?? null)}\n >\n {t(\"Change plan\")}\n </Button>\n )}\n {isCancelable && (\n <Button\n variant=\"secondary\"\n color=\"neutral\"\n onClick={() => setCancelTarget({ productId: product.id ?? \"_inline\", subscriptionId: product.subscription?.subscriptionId ?? undefined })}\n >\n {t(\"Cancel subscription\")}\n </Button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n <ActionDialog\n open={cancelTarget !== null}\n onOpenChange={(open) => {\n if (!open) setCancelTarget(null);\n }}\n title={t(\"Cancel subscription\")}\n description={t(\"Canceling will stop future renewals for this subscription.\")}\n danger\n cancelButton\n okButton={{\n label: t(\"Cancel subscription\"),\n onClick: async () => {\n if (!cancelTarget) return;\n const { productId, subscriptionId } = cancelTarget;\n if (props.customerType === \"team\") {\n await hexclaveApp.cancelSubscription({ teamId: props.customer.id, productId, subscriptionId });\n } else {\n await hexclaveApp.cancelSubscription({ productId, subscriptionId });\n }\n setCancelTarget(null);\n },\n }}\n />\n\n <ActionDialog\n open={switchFromProductId !== null}\n onOpenChange={(open) => {\n if (!open) closeSwitchDialog();\n }}\n title={t(\"Change plan\")}\n description={t(\"Select a new plan from the same product line.\")}\n cancelButton\n okButton={{\n label: t(\"Switch plan\"),\n onClick: async () => {\n const fromProductId = switchFromProductId;\n const toProductId = switchToProductId;\n if (!fromProductId || !toProductId) return;\n if (!selectedPriceId) return;\n const result = await Result.fromThrowingAsync(() => props.customer.switchSubscription({\n fromProductId,\n toProductId,\n priceId: selectedPriceId,\n }));\n if (result.status === \"error\") {\n handleAsyncError(result.error);\n return \"prevent-close\";\n }\n closeSwitchDialog();\n },\n props: {\n disabled: !switchFromProductId || !switchToProductId || !selectedPriceId,\n },\n }}\n >\n <div className=\"space-y-2\">\n {switchOptions.length === 0 ? (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"No other plans available for this subscription.\")}\n </Typography>\n ) : (\n <>\n <Typography type=\"footnote\">{t(\"Choose a plan\")}</Typography>\n <Select\n value={switchToProductId ?? undefined}\n onValueChange={(value) => setSwitchToProductId(value || null)}\n >\n <SelectTrigger className=\"w-full\">\n <SelectValue placeholder={t(\"Choose a plan\")} />\n </SelectTrigger>\n <SelectContent>\n {switchOptions.map((option: NonNullable<typeof switchOptions>[number]) => (\n <SelectItem key={option.productId} value={option.productId}>\n {option.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )}\n </div>\n </ActionDialog>\n </Section>\n )\n }\n {invoices.length > 0 && (\n <>\n <Separator />\n <div className=\"space-y-2\">\n <div className=\"space-y-1\">\n <Typography className=\"font-medium\">{t(\"Invoices\")}</Typography>\n <Typography variant=\"secondary\" type=\"footnote\">{t(\"Review past invoices and receipts.\")}</Typography>\n </div>\n <div className=\"border rounded-md\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[140px]\">{t(\"Date\")}</TableHead>\n <TableHead className=\"w-[120px]\">{t(\"Status\")}</TableHead>\n <TableHead className=\"w-[120px]\">{t(\"Amount\")}</TableHead>\n <TableHead className=\"w-[120px] text-right\">{t(\"Invoice\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {invoices.map((invoice, index) => {\n const createdAtTime = invoice.createdAt.getTime();\n const invoiceKey = Number.isNaN(createdAtTime) ? `invoice-${index}` : `invoice-${createdAtTime}-${index}`;\n return (\n <TableRow key={invoiceKey}>\n <TableCell>\n <Typography>{formatInvoiceDate(invoice.createdAt, t)}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{formatInvoiceStatus(invoice.status, t)}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{formatInvoiceAmount(invoice.amountTotal, t)}</Typography>\n </TableCell>\n <TableCell align=\"right\">\n {invoice.hostedInvoiceUrl ? (\n <Button asChild variant=\"secondary\" color=\"neutral\" size=\"sm\">\n <a href={invoice.hostedInvoiceUrl} target=\"_blank\" rel=\"noreferrer\">\n {t(\"View\")}\n </a>\n </Button>\n ) : (\n <Typography variant=\"secondary\" type=\"footnote\">\n {t(\"Unavailable\")}\n </Typography>\n )}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </div>\n </>\n )}\n </div >\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AccountSettings } from "./account-settings.js";
|
|
2
|
+
import { HandlerUrls } from "../lib/hexclave-app/common.js";
|
|
3
|
+
import { StackClientApp } from "../lib/hexclave-app/apps/interfaces/client-app.js";
|
|
2
4
|
import { CliAuthConfirmation } from "./cli-auth-confirm.js";
|
|
3
5
|
import { EmailVerification } from "./email-verification.js";
|
|
4
6
|
import { ErrorPage } from "./error-page.js";
|
|
@@ -12,6 +14,7 @@ import { SignOut } from "./sign-out.js";
|
|
|
12
14
|
import { TeamInvitation } from "./team-invitation.js";
|
|
13
15
|
import { SignIn } from "./sign-in.js";
|
|
14
16
|
import { SignUp } from "./sign-up.js";
|
|
17
|
+
import { KnownError } from "@hexclave/shared";
|
|
15
18
|
|
|
16
19
|
//#region src/components-page/hexclave-handler-client.d.ts
|
|
17
20
|
type Components = {
|
|
@@ -38,13 +41,22 @@ type RouteProps = {
|
|
|
38
41
|
};
|
|
39
42
|
searchParams: Promise<Record<string, string>> | Record<string, string>;
|
|
40
43
|
};
|
|
44
|
+
type RedirectToPageResult = {
|
|
45
|
+
status: "success";
|
|
46
|
+
} | {
|
|
47
|
+
status: "known-error";
|
|
48
|
+
error: KnownError;
|
|
49
|
+
} | {
|
|
50
|
+
status: "unknown-error";
|
|
51
|
+
};
|
|
41
52
|
type BaseHandlerProps = {
|
|
42
53
|
fullPage: boolean;
|
|
43
54
|
componentProps?: { [K in keyof Components]?: Parameters<Components[K]>[0] };
|
|
44
55
|
};
|
|
56
|
+
declare function getRedirectToPageResult(app: StackClientApp, redirectToPage: keyof HandlerUrls): Promise<RedirectToPageResult>;
|
|
45
57
|
declare function HexclaveHandlerClient(props: BaseHandlerProps & Partial<RouteProps> & {
|
|
46
58
|
location?: string;
|
|
47
59
|
}): any;
|
|
48
60
|
//#endregion
|
|
49
|
-
export { BaseHandlerProps, HexclaveHandlerClient };
|
|
61
|
+
export { BaseHandlerProps, HexclaveHandlerClient, getRedirectToPageResult };
|
|
50
62
|
//# sourceMappingURL=hexclave-handler-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hexclave-handler-client.d.ts","names":[],"sources":["../../src/components-page/hexclave-handler-client.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"hexclave-handler-client.d.ts","names":[],"sources":["../../src/components-page/hexclave-handler-client.tsx"],"mappings":";;;;;;;;;;;;;;;;;;;KAmCK,UAAA;EACH,MAAA,SAAe,MAAA;EACf,MAAA,SAAe,MAAA;EACf,iBAAA,SAA0B,iBAAA;EAC1B,aAAA,SAAsB,aAAA;EACtB,cAAA,SAAuB,cAAA;EACvB,OAAA,SAAgB,OAAA;EAChB,aAAA,SAAsB,aAAA;EACtB,iBAAA,SAA0B,iBAAA;EAC1B,cAAA,SAAuB,cAAA;EACvB,SAAA,SAAkB,SAAA;EAClB,eAAA,SAAwB,eAAA;EACxB,mBAAA,SAA4B,mBAAA;EAC5B,GAAA,SAAY,GAAA;EACZ,UAAA,SAAmB,UAAA;AAAA;AAAA,KAGhB,UAAA;EACH,MAAA,EAAQ,OAAA;IAAU,KAAA;EAAA;IAAwB,KAAA;EAAA;EAC1C,YAAA,EAAc,OAAA,CAAQ,MAAA,oBAA0B,MAAA;AAAA;AAAA,KAG7C,oBAAA;EACC,MAAA;AAAA;EACA,MAAA;EAAuB,KAAA,EAAO,UAAA;AAAA;EAC9B,MAAA;AAAA;AAAA,KA4BM,gBAAA;EACV,QAAA;EACA,cAAA,iBACc,UAAA,IAAc,UAAA,CAAW,UAAA,CAAW,CAAA;AAAA;AAAA,iBAgJ9B,uBAAA,CACpB,GAAA,EAAK,cAAA,EACL,cAAA,QAAsB,WAAA,GACrB,OAAA,CAAQ,oBAAA;AAAA,iBAiCK,qBAAA,CAAsB,KAAA,EAAO,gBAAA,GAAmB,OAAA,CAAQ,UAAA;EAAgB,QAAA;AAAA"}
|
|
@@ -3,17 +3,20 @@
|
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
4
|
const require_chunk = require('../chunk-BE-pF4vm.js');
|
|
5
5
|
let _hexclave_shared_dist_utils_errors = require("@hexclave/shared/dist/utils/errors");
|
|
6
|
-
let _hexclave_shared_dist_utils_promises = require("@hexclave/shared/dist/utils/promises");
|
|
7
6
|
let react = require("react");
|
|
8
7
|
let ___index_js = require("../index.js");
|
|
9
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
9
|
let ___lib_hooks_js = require("../lib/hooks.js");
|
|
10
|
+
let _hexclave_shared = require("@hexclave/shared");
|
|
11
|
+
let _hexclave_shared_dist_utils_react = require("@hexclave/shared/dist/utils/react");
|
|
12
|
+
let ___components_message_cards_known_error_message_card_js = require("../components/message-cards/known-error-message-card.js");
|
|
11
13
|
let ___components_message_cards_message_card_js = require("../components/message-cards/message-card.js");
|
|
14
|
+
let ___components_message_cards_predefined_message_card_js = require("../components/message-cards/predefined-message-card.js");
|
|
12
15
|
let __cli_auth_confirm_js = require("./cli-auth-confirm.js");
|
|
16
|
+
let ___lib_hexclave_app_index_js = require("../lib/hexclave-app/index.js");
|
|
13
17
|
let _hexclave_shared_dist_utils_objects = require("@hexclave/shared/dist/utils/objects");
|
|
14
18
|
let _hexclave_shared_dist_utils_urls = require("@hexclave/shared/dist/utils/urls");
|
|
15
19
|
let next_navigation = require("next/navigation");
|
|
16
|
-
let ___lib_hexclave_app_index_js = require("../lib/hexclave-app/index.js");
|
|
17
20
|
let ___lib_hexclave_app_url_targets_js = require("../lib/hexclave-app/url-targets.js");
|
|
18
21
|
let __account_settings_js = require("./account-settings.js");
|
|
19
22
|
let __email_verification_js = require("./email-verification.js");
|
|
@@ -153,6 +156,34 @@ function renderComponent(props) {
|
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
}
|
|
159
|
+
async function getRedirectToPageResult(app, redirectToPage) {
|
|
160
|
+
try {
|
|
161
|
+
await app[___lib_hexclave_app_index_js.hexclaveAppInternalsSymbol].redirectToHandler(redirectToPage, { replace: true });
|
|
162
|
+
return { status: "success" };
|
|
163
|
+
} catch (e) {
|
|
164
|
+
if (_hexclave_shared.KnownError.isKnownError(e)) return {
|
|
165
|
+
status: "known-error",
|
|
166
|
+
error: e
|
|
167
|
+
};
|
|
168
|
+
(0, _hexclave_shared_dist_utils_errors.captureError)("<HexclaveHandlerClient redirectToPage />", e);
|
|
169
|
+
return { status: "unknown-error" };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function RedirectToPage(props) {
|
|
173
|
+
const redirectResult = (0, _hexclave_shared_dist_utils_react.use)((0, react.useMemo)(() => getRedirectToPageResult(props.app, props.redirectToPage), [props.app, props.redirectToPage]));
|
|
174
|
+
if (redirectResult.status === "known-error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___components_message_cards_known_error_message_card_js.KnownErrorMessageCard, {
|
|
175
|
+
error: redirectResult.error,
|
|
176
|
+
fullPage: props.fullPage
|
|
177
|
+
});
|
|
178
|
+
if (redirectResult.status === "unknown-error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___components_message_cards_predefined_message_card_js.PredefinedMessageCard, {
|
|
179
|
+
type: "unknownError",
|
|
180
|
+
fullPage: props.fullPage
|
|
181
|
+
});
|
|
182
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___components_message_cards_message_card_js.MessageCard, {
|
|
183
|
+
title: "Redirecting...",
|
|
184
|
+
fullPage: props.fullPage
|
|
185
|
+
});
|
|
186
|
+
}
|
|
156
187
|
function HexclaveHandlerClient(props) {
|
|
157
188
|
const hexclaveApp = (0, ___lib_hooks_js.useStackApp)();
|
|
158
189
|
const clientOrigin = useClientOriginAfterHydration();
|
|
@@ -199,13 +230,16 @@ function HexclaveHandlerClient(props) {
|
|
|
199
230
|
app: hexclaveApp
|
|
200
231
|
});
|
|
201
232
|
const redirectToPage = result != null && typeof result === "object" && "redirectToPage" in result ? result.redirectToPage : void 0;
|
|
202
|
-
(0,
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
233
|
+
if (redirectToPage != null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
|
|
234
|
+
fallback: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___components_message_cards_message_card_js.MessageCard, {
|
|
235
|
+
title: "Redirecting...",
|
|
236
|
+
fullPage: props.fullPage
|
|
237
|
+
}),
|
|
238
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RedirectToPage, {
|
|
239
|
+
app: hexclaveApp,
|
|
240
|
+
redirectToPage,
|
|
241
|
+
fullPage: props.fullPage
|
|
242
|
+
})
|
|
209
243
|
});
|
|
210
244
|
if (result && "redirect" in result) (0, next_navigation.redirect)(result.redirect, next_navigation.RedirectType.replace);
|
|
211
245
|
return result;
|
|
@@ -222,4 +256,5 @@ function useClientOriginAfterHydration() {
|
|
|
222
256
|
|
|
223
257
|
//#endregion
|
|
224
258
|
exports.HexclaveHandlerClient = HexclaveHandlerClient;
|
|
259
|
+
exports.getRedirectToPageResult = getRedirectToPageResult;
|
|
225
260
|
//# sourceMappingURL=hexclave-handler-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hexclave-handler-client.js","names":["SignIn","SignUp","EmailVerification","PasswordReset","ForgotPassword","SignOut","OAuthCallback","MagicLinkCallback","TeamInvitation","AccountSettings","ErrorPage","CliAuthConfirmation","MFA","Onboarding","HexclaveAssertionError","hexclaveAppInternalsSymbol","MessageCard","RedirectType"],"sources":["../../src/components-page/hexclave-handler-client.tsx"],"sourcesContent":["\"use client\";\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { HexclaveAssertionError } from \"@hexclave/shared/dist/utils/errors\";\nimport { FilterUndefined, filterUndefined } from \"@hexclave/shared/dist/utils/objects\";\nimport { runAsynchronouslyWithAlert } from \"@hexclave/shared/dist/utils/promises\";\nimport { getRelativePart } from \"@hexclave/shared/dist/utils/urls\";\nimport { notFound, redirect, RedirectType, usePathname, useSearchParams } from 'next/navigation'; // THIS_LINE_PLATFORM next\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { SignIn, SignUp, StackServerApp } from \"..\";\nimport { useStackApp } from \"../lib/hooks\";\nimport { HandlerUrls, StackClientApp, hexclaveAppInternalsSymbol } from \"../lib/hexclave-app\";\nimport { isLocalHandlerUrlTarget, resolveUnknownHandlerPathFallbackUrl } from \"../lib/hexclave-app/url-targets\";\nimport { AccountSettings } from \"./account-settings\";\nimport { CliAuthConfirmation } from \"./cli-auth-confirm\";\nimport { EmailVerification } from \"./email-verification\";\nimport { ErrorPage } from \"./error-page\";\nimport { ForgotPassword } from \"./forgot-password\";\nimport { MagicLinkCallback } from \"./magic-link-callback\";\nimport { MFA } from \"./mfa\";\nimport { OAuthCallback } from \"./oauth-callback\";\nimport { Onboarding } from \"./onboarding\";\nimport { PasswordReset } from \"./password-reset\";\nimport { SignOut } from \"./sign-out\";\nimport { TeamInvitation } from \"./team-invitation\";\n\nimport { MessageCard } from \"../components/message-cards/message-card\";\n\ntype Components = {\n SignIn: typeof SignIn,\n SignUp: typeof SignUp,\n EmailVerification: typeof EmailVerification,\n PasswordReset: typeof PasswordReset,\n ForgotPassword: typeof ForgotPassword,\n SignOut: typeof SignOut,\n OAuthCallback: typeof OAuthCallback,\n MagicLinkCallback: typeof MagicLinkCallback,\n TeamInvitation: typeof TeamInvitation,\n ErrorPage: typeof ErrorPage,\n AccountSettings: typeof AccountSettings,\n CliAuthConfirmation: typeof CliAuthConfirmation,\n MFA: typeof MFA,\n Onboarding: typeof Onboarding,\n};\n\ntype RouteProps = {\n params: Promise<{ stack?: string[] }> | { stack?: string[] },\n searchParams: Promise<Record<string, string>> | Record<string, string>,\n};\n\nconst availablePaths = {\n signIn: 'sign-in',\n signUp: 'sign-up',\n emailVerification: 'email-verification',\n passwordReset: 'password-reset',\n forgotPassword: 'forgot-password',\n signOut: 'sign-out',\n oauthCallback: 'oauth-callback',\n magicLinkCallback: 'magic-link-callback',\n teamInvitation: 'team-invitation',\n accountSettings: 'account-settings',\n cliAuthConfirm: 'cli-auth-confirm',\n mfa: 'mfa',\n error: 'error',\n onboarding: 'onboarding',\n} as const;\n\nconst placeholderOrigin = \"http://example.com\";\n\nconst pathAliases = {\n // also includes the uppercase and non-dashed versions\n ...Object.fromEntries(Object.entries(availablePaths).map(([key, value]) => [value, value])),\n \"log-in\": availablePaths.signIn,\n \"register\": availablePaths.signUp,\n} as const;\n\nexport type BaseHandlerProps = {\n fullPage: boolean,\n componentProps?: {\n [K in keyof Components]?: Parameters<Components[K]>[0];\n },\n};\n\nfunction renderComponent(props: {\n path: string,\n searchParams: Record<string, string>,\n fullPage: boolean,\n componentProps?: BaseHandlerProps['componentProps'],\n shouldRedirectToPage?: (name: keyof HandlerUrls) => boolean,\n getDefaultUnknownPathUrl?: (path: string) => string | null,\n onNotFound: () => any,\n app: StackClientApp<any> | StackServerApp<any>,\n}) {\n const { path, searchParams, fullPage, componentProps, shouldRedirectToPage, getDefaultUnknownPathUrl, onNotFound, app } = props;\n\n switch (path) {\n case availablePaths.signIn: {\n if (shouldRedirectToPage?.('signIn')) return { redirectToPage: 'signIn' as const };\n return <SignIn\n fullPage={fullPage}\n automaticRedirect\n {...filterUndefinedINU(componentProps?.SignIn)}\n />;\n }\n case availablePaths.signUp: {\n if (shouldRedirectToPage?.('signUp')) return { redirectToPage: 'signUp' as const };\n return <SignUp\n fullPage={fullPage}\n automaticRedirect\n {...filterUndefinedINU(componentProps?.SignUp)}\n />;\n }\n case availablePaths.emailVerification: {\n if (shouldRedirectToPage?.('emailVerification')) return { redirectToPage: 'emailVerification' as const };\n return <EmailVerification\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.EmailVerification)}\n />;\n }\n case availablePaths.passwordReset: {\n if (shouldRedirectToPage?.('passwordReset')) return { redirectToPage: 'passwordReset' as const };\n return <PasswordReset\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.PasswordReset)}\n />;\n }\n case availablePaths.forgotPassword: {\n if (shouldRedirectToPage?.('forgotPassword')) return { redirectToPage: 'forgotPassword' as const };\n return <ForgotPassword\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.ForgotPassword)}\n />;\n }\n case availablePaths.signOut: {\n if (shouldRedirectToPage?.('signOut')) return { redirectToPage: 'signOut' as const };\n return <SignOut\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.SignOut)}\n />;\n }\n case availablePaths.oauthCallback: {\n if (shouldRedirectToPage?.('oauthCallback')) return { redirectToPage: 'oauthCallback' as const };\n return <OAuthCallback\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.OAuthCallback)}\n />;\n }\n case availablePaths.magicLinkCallback: {\n if (shouldRedirectToPage?.('magicLinkCallback')) return { redirectToPage: 'magicLinkCallback' as const };\n return <MagicLinkCallback\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.MagicLinkCallback)}\n />;\n }\n case availablePaths.teamInvitation: {\n if (shouldRedirectToPage?.('teamInvitation')) return { redirectToPage: 'teamInvitation' as const };\n return <TeamInvitation\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.TeamInvitation)}\n />;\n }\n case availablePaths.accountSettings: {\n return <AccountSettings\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.AccountSettings)}\n />;\n }\n case availablePaths.error: {\n return <ErrorPage\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.ErrorPage)}\n />;\n }\n case availablePaths.cliAuthConfirm: {\n if (shouldRedirectToPage?.('cliAuthConfirm')) return { redirectToPage: 'cliAuthConfirm' as const };\n return <CliAuthConfirmation\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.CliAuthConfirmation)}\n />;\n }\n case availablePaths.mfa: {\n if (shouldRedirectToPage?.('mfa')) return { redirectToPage: 'mfa' as const };\n return <MFA\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.MFA)}\n />;\n }\n case availablePaths.onboarding: {\n if (shouldRedirectToPage?.('onboarding')) return { redirectToPage: 'onboarding' as const };\n return <Onboarding\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.Onboarding)}\n />;\n }\n default: {\n if (Object.values(availablePaths).includes(path as any)) {\n throw new HexclaveAssertionError(`Path alias ${path} not included in switch statement, but in availablePaths?`, { availablePaths });\n }\n for (const [key, value] of Object.entries(pathAliases)) {\n if (path.toLowerCase().replaceAll('-', '') === key.toLowerCase().replaceAll('-', '')) {\n const redirectUrl = `${app.urls.handler}/${value}?${new URLSearchParams(searchParams).toString()}`;\n return { redirect: redirectUrl };\n }\n }\n const defaultUnknownPathUrl = getDefaultUnknownPathUrl?.(path);\n if (defaultUnknownPathUrl != null) {\n const defaultUnknownPathUrlObject = new URL(defaultUnknownPathUrl, \"http://example.com\");\n for (const [key, value] of Object.entries(searchParams)) {\n defaultUnknownPathUrlObject.searchParams.set(key, value);\n }\n return { redirect: toAbsoluteOrRelativeRedirectTarget(defaultUnknownPathUrlObject) };\n }\n return onNotFound();\n }\n }\n}\n\nexport function HexclaveHandlerClient(props: BaseHandlerProps & Partial<RouteProps> & { location?: string }) {\n // Use hooks to get app\n const hexclaveApp = useStackApp();\n const clientOrigin = useClientOriginAfterHydration();\n\n const pathname = usePathname();\n const searchParamsFromHook = useSearchParams();\n const currentLocation = pathname;\n const searchParamsSource = searchParamsFromHook;\n\n const { path, searchParams, handlerPath } = useMemo(() => {\n const handlerPath = new URL(hexclaveApp.urls.handler, 'http://example.com').pathname;\n const relativePath = currentLocation.startsWith(handlerPath)\n ? currentLocation.slice(handlerPath.length).replace(/^\\/+/, '')\n : currentLocation.replace(/^\\/+/, '');\n\n return {\n path: relativePath,\n searchParams: Object.fromEntries(searchParamsSource.entries()),\n handlerPath,\n };\n }, [currentLocation, searchParamsSource, hexclaveApp.urls.handler]);\n\n const getDefaultUnknownPathUrl = (unknownPath: string): string | null => {\n return resolveUnknownHandlerPathFallbackUrl({\n defaultTarget: hexclaveApp[hexclaveAppInternalsSymbol].getConstructorOptions().urls?.default,\n projectId: hexclaveApp.projectId,\n unknownPath,\n });\n };\n\n const shouldRedirectToPage = (name: keyof HandlerUrls): boolean => {\n const url = hexclaveApp.urls[name];\n const isCrossDomainLocalOauthCallback = name === \"oauthCallback\" && searchParams.hexclave_cross_domain_auth === \"1\";\n if (isCrossDomainLocalOauthCallback) {\n return false;\n }\n return !isLocalHandlerUrlTarget({\n targetUrl: url,\n handlerPath,\n currentOrigin: clientOrigin,\n });\n };\n\n const result = renderComponent({\n path,\n searchParams,\n fullPage: props.fullPage,\n componentProps: props.componentProps,\n shouldRedirectToPage,\n getDefaultUnknownPathUrl,\n onNotFound: () =>\n notFound()\n ,\n app: hexclaveApp,\n });\n\n const redirectToPage = (result != null && typeof result === 'object' && 'redirectToPage' in result) ? result.redirectToPage : undefined;\n\n useEffect(() => {\n if (redirectToPage == null) return;\n runAsynchronouslyWithAlert(\n hexclaveApp[hexclaveAppInternalsSymbol].redirectToHandler(redirectToPage, { replace: true })\n );\n }, [redirectToPage, hexclaveApp]);\n\n if (redirectToPage != null) {\n return (\n <MessageCard title=\"Redirecting...\" fullPage={props.fullPage} />\n );\n }\n\n if (result && 'redirect' in result) {\n redirect(result.redirect, RedirectType.replace);\n }\n\n\n return result;\n}\n\n// filter undefined values in object. if object itself is undefined, return undefined\nfunction filterUndefinedINU<T extends {}>(value: T | undefined): FilterUndefined<T> | undefined {\n return value === undefined ? value : filterUndefined(value);\n}\n\nfunction toAbsoluteOrRelativeRedirectTarget(url: URL): string {\n return url.origin === \"http://example.com\" ? getRelativePart(url) : url.toString();\n}\n\nfunction useClientOriginAfterHydration(): string | undefined {\n // The first hydrated render must match SSR. After hydration, React re-checks\n // the snapshot and lets us distinguish same-path cross-origin handler URLs.\n return useSyncExternalStore(\n () => () => {},\n () => window.location.origin,\n () => undefined,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,MAAM,iBAAiB;CACrB,QAAQ;CACR,QAAQ;CACR,mBAAmB;CACnB,eAAe;CACf,gBAAgB;CAChB,SAAS;CACT,eAAe;CACf,mBAAmB;CACnB,gBAAgB;CAChB,iBAAiB;CACjB,gBAAgB;CAChB,KAAK;CACL,OAAO;CACP,YAAY;CACb;AAID,MAAM,cAAc;CAElB,GAAG,OAAO,YAAY,OAAO,QAAQ,eAAe,CAAC,KAAK,CAAC,KAAK,WAAW,CAAC,OAAO,MAAM,CAAC,CAAC;CAC3F,UAAU,eAAe;CACzB,YAAY,eAAe;CAC5B;AASD,SAAS,gBAAgB,OAStB;CACD,MAAM,EAAE,MAAM,cAAc,UAAU,gBAAgB,sBAAsB,0BAA0B,YAAY,QAAQ;AAE1H,SAAQ,MAAR;EACE,KAAK,eAAe;AAClB,OAAI,uBAAuB,SAAS,CAAE,QAAO,EAAE,gBAAgB,UAAmB;AAClF,UAAO,2CAACA;IACI;IACV;IACA,GAAI,mBAAmB,gBAAgB,OAAO;KAC9C;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,SAAS,CAAE,QAAO,EAAE,gBAAgB,UAAmB;AAClF,UAAO,2CAACC;IACI;IACV;IACA,GAAI,mBAAmB,gBAAgB,OAAO;KAC9C;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,oBAAoB,CAAE,QAAO,EAAE,gBAAgB,qBAA8B;AACxG,UAAO,2CAACC;IACQ;IACJ;IACV,GAAI,mBAAmB,gBAAgB,kBAAkB;KACzD;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,gBAAgB,CAAE,QAAO,EAAE,gBAAgB,iBAA0B;AAChG,UAAO,2CAACC;IACQ;IACJ;IACV,GAAI,mBAAmB,gBAAgB,cAAc;KACrD;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,iBAAiB,CAAE,QAAO,EAAE,gBAAgB,kBAA2B;AAClG,UAAO,2CAACC;IACI;IACV,GAAI,mBAAmB,gBAAgB,eAAe;KACtD;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,UAAU,CAAE,QAAO,EAAE,gBAAgB,WAAoB;AACpF,UAAO,2CAACC;IACQ;IACJ;IACV,GAAI,mBAAmB,gBAAgB,QAAQ;KAC/C;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,gBAAgB,CAAE,QAAO,EAAE,gBAAgB,iBAA0B;AAChG,UAAO,2CAACC;IACI;IACV,GAAI,mBAAmB,gBAAgB,cAAc;KACrD;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,oBAAoB,CAAE,QAAO,EAAE,gBAAgB,qBAA8B;AACxG,UAAO,2CAACC;IACQ;IACJ;IACV,GAAI,mBAAmB,gBAAgB,kBAAkB;KACzD;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,iBAAiB,CAAE,QAAO,EAAE,gBAAgB,kBAA2B;AAClG,UAAO,2CAACC;IACQ;IACJ;IACV,GAAI,mBAAmB,gBAAgB,eAAe;KACtD;EAEJ,KAAK,eAAe,gBAClB,QAAO,2CAACC;GACI;GACV,GAAI,mBAAmB,gBAAgB,gBAAgB;IACvD;EAEJ,KAAK,eAAe,MAClB,QAAO,2CAACC;GACQ;GACJ;GACV,GAAI,mBAAmB,gBAAgB,UAAU;IACjD;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,iBAAiB,CAAE,QAAO,EAAE,gBAAgB,kBAA2B;AAClG,UAAO,2CAACC;IACI;IACV,GAAI,mBAAmB,gBAAgB,oBAAoB;KAC3D;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,MAAM,CAAE,QAAO,EAAE,gBAAgB,OAAgB;AAC5E,UAAO,2CAACC;IACI;IACV,GAAI,mBAAmB,gBAAgB,IAAI;KAC3C;EAEJ,KAAK,eAAe;AAClB,OAAI,uBAAuB,aAAa,CAAE,QAAO,EAAE,gBAAgB,cAAuB;AAC1F,UAAO,2CAACC;IACI;IACV,GAAI,mBAAmB,gBAAgB,WAAW;KAClD;EAEJ,SAAS;AACP,OAAI,OAAO,OAAO,eAAe,CAAC,SAAS,KAAY,CACrD,OAAM,IAAIC,0DAAuB,cAAc,KAAK,4DAA4D,EAAE,gBAAgB,CAAC;AAErI,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,CACpD,KAAI,KAAK,aAAa,CAAC,WAAW,KAAK,GAAG,KAAK,IAAI,aAAa,CAAC,WAAW,KAAK,GAAG,CAElF,QAAO,EAAE,UADW,GAAG,IAAI,KAAK,QAAQ,GAAG,MAAM,GAAG,IAAI,gBAAgB,aAAa,CAAC,UAAU,IAChE;GAGpC,MAAM,wBAAwB,2BAA2B,KAAK;AAC9D,OAAI,yBAAyB,MAAM;IACjC,MAAM,8BAA8B,IAAI,IAAI,uBAAuB,qBAAqB;AACxF,SAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,aAAa,CACrD,6BAA4B,aAAa,IAAI,KAAK,MAAM;AAE1D,WAAO,EAAE,UAAU,mCAAmC,4BAA4B,EAAE;;AAEtF,UAAO,YAAY;;;;AAKzB,SAAgB,sBAAsB,OAAuE;CAE3G,MAAM,gDAA2B;CACjC,MAAM,eAAe,+BAA+B;CAEpD,MAAM,6CAAwB;CAC9B,MAAM,6DAAwC;CAC9C,MAAM,kBAAkB;CACxB,MAAM,qBAAqB;CAE3B,MAAM,EAAE,MAAM,cAAc,yCAA8B;EACxD,MAAM,cAAc,IAAI,IAAI,YAAY,KAAK,SAAS,qBAAqB,CAAC;AAK5E,SAAO;GACL,MALmB,gBAAgB,WAAW,YAAY,GACxD,gBAAgB,MAAM,YAAY,OAAO,CAAC,QAAQ,QAAQ,GAAG,GAC7D,gBAAgB,QAAQ,QAAQ,GAAG;GAIrC,cAAc,OAAO,YAAY,mBAAmB,SAAS,CAAC;GAC9D;GACD;IACA;EAAC;EAAiB;EAAoB,YAAY,KAAK;EAAQ,CAAC;CAEnE,MAAM,4BAA4B,gBAAuC;AACvE,sFAA4C;GAC1C,eAAe,YAAYC,yDAA4B,uBAAuB,CAAC,MAAM;GACrF,WAAW,YAAY;GACvB;GACD,CAAC;;CAGJ,MAAM,wBAAwB,SAAqC;EACjE,MAAM,MAAM,YAAY,KAAK;AAE7B,MADwC,SAAS,mBAAmB,aAAa,+BAA+B,IAE9G,QAAO;AAET,SAAO,iEAAyB;GAC9B,WAAW;GACX;GACA,eAAe;GAChB,CAAC;;CAGJ,MAAM,SAAS,gBAAgB;EAC7B;EACA;EACA,UAAU,MAAM;EAChB,gBAAgB,MAAM;EACtB;EACA;EACA,iDACY;EAEZ,KAAK;EACN,CAAC;CAEF,MAAM,iBAAkB,UAAU,QAAQ,OAAO,WAAW,YAAY,oBAAoB,SAAU,OAAO,iBAAiB;AAE9H,4BAAgB;AACd,MAAI,kBAAkB,KAAM;AAC5B,uEACE,YAAYA,yDAA4B,kBAAkB,gBAAgB,EAAE,SAAS,MAAM,CAAC,CAC7F;IACA,CAAC,gBAAgB,YAAY,CAAC;AAEjC,KAAI,kBAAkB,KACpB,QACE,2CAACC;EAAY,OAAM;EAAiB,UAAU,MAAM;GAAY;AAIpE,KAAI,UAAU,cAAc,OAC1B,+BAAS,OAAO,UAAUC,6BAAa,QAAQ;AAIjD,QAAO;;AAIT,SAAS,mBAAiC,OAAsD;AAC9F,QAAO,UAAU,SAAY,iEAAwB,MAAM;;AAG7D,SAAS,mCAAmC,KAAkB;AAC5D,QAAO,IAAI,WAAW,6EAAuC,IAAI,GAAG,IAAI,UAAU;;AAGpF,SAAS,gCAAoD;AAG3D,oDACc,UACN,OAAO,SAAS,cAChB,OACP"}
|
|
1
|
+
{"version":3,"file":"hexclave-handler-client.js","names":["SignIn","SignUp","EmailVerification","PasswordReset","ForgotPassword","SignOut","OAuthCallback","MagicLinkCallback","TeamInvitation","AccountSettings","ErrorPage","CliAuthConfirmation","MFA","Onboarding","HexclaveAssertionError","hexclaveAppInternalsSymbol","KnownError","KnownErrorMessageCard","PredefinedMessageCard","MessageCard","Suspense","RedirectType"],"sources":["../../src/components-page/hexclave-handler-client.tsx"],"sourcesContent":["\"use client\";\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { KnownError } from \"@hexclave/shared\";\nimport { captureError, HexclaveAssertionError } from \"@hexclave/shared/dist/utils/errors\";\nimport { FilterUndefined, filterUndefined } from \"@hexclave/shared/dist/utils/objects\";\nimport { use } from \"@hexclave/shared/dist/utils/react\";\nimport { getRelativePart } from \"@hexclave/shared/dist/utils/urls\";\nimport { notFound, redirect, RedirectType, usePathname, useSearchParams } from 'next/navigation'; // THIS_LINE_PLATFORM next\nimport { Suspense, useMemo, useSyncExternalStore } from 'react';\nimport { SignIn, SignUp, StackServerApp } from \"..\";\nimport { useStackApp } from \"../lib/hooks\";\nimport { HandlerUrls, StackClientApp, hexclaveAppInternalsSymbol } from \"../lib/hexclave-app\";\nimport { isLocalHandlerUrlTarget, resolveUnknownHandlerPathFallbackUrl } from \"../lib/hexclave-app/url-targets\";\nimport { AccountSettings } from \"./account-settings\";\nimport { CliAuthConfirmation } from \"./cli-auth-confirm\";\nimport { EmailVerification } from \"./email-verification\";\nimport { ErrorPage } from \"./error-page\";\nimport { ForgotPassword } from \"./forgot-password\";\nimport { MagicLinkCallback } from \"./magic-link-callback\";\nimport { MFA } from \"./mfa\";\nimport { OAuthCallback } from \"./oauth-callback\";\nimport { Onboarding } from \"./onboarding\";\nimport { PasswordReset } from \"./password-reset\";\nimport { SignOut } from \"./sign-out\";\nimport { TeamInvitation } from \"./team-invitation\";\n\nimport { KnownErrorMessageCard } from \"../components/message-cards/known-error-message-card\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { PredefinedMessageCard } from \"../components/message-cards/predefined-message-card\";\n\ntype Components = {\n SignIn: typeof SignIn,\n SignUp: typeof SignUp,\n EmailVerification: typeof EmailVerification,\n PasswordReset: typeof PasswordReset,\n ForgotPassword: typeof ForgotPassword,\n SignOut: typeof SignOut,\n OAuthCallback: typeof OAuthCallback,\n MagicLinkCallback: typeof MagicLinkCallback,\n TeamInvitation: typeof TeamInvitation,\n ErrorPage: typeof ErrorPage,\n AccountSettings: typeof AccountSettings,\n CliAuthConfirmation: typeof CliAuthConfirmation,\n MFA: typeof MFA,\n Onboarding: typeof Onboarding,\n};\n\ntype RouteProps = {\n params: Promise<{ stack?: string[] }> | { stack?: string[] },\n searchParams: Promise<Record<string, string>> | Record<string, string>,\n};\n\ntype RedirectToPageResult =\n | { status: \"success\" }\n | { status: \"known-error\", error: KnownError }\n | { status: \"unknown-error\" };\n\nconst availablePaths = {\n signIn: 'sign-in',\n signUp: 'sign-up',\n emailVerification: 'email-verification',\n passwordReset: 'password-reset',\n forgotPassword: 'forgot-password',\n signOut: 'sign-out',\n oauthCallback: 'oauth-callback',\n magicLinkCallback: 'magic-link-callback',\n teamInvitation: 'team-invitation',\n accountSettings: 'account-settings',\n cliAuthConfirm: 'cli-auth-confirm',\n mfa: 'mfa',\n error: 'error',\n onboarding: 'onboarding',\n} as const;\n\nconst placeholderOrigin = \"http://example.com\";\n\nconst pathAliases = {\n // also includes the uppercase and non-dashed versions\n ...Object.fromEntries(Object.entries(availablePaths).map(([key, value]) => [value, value])),\n \"log-in\": availablePaths.signIn,\n \"register\": availablePaths.signUp,\n} as const;\n\nexport type BaseHandlerProps = {\n fullPage: boolean,\n componentProps?: {\n [K in keyof Components]?: Parameters<Components[K]>[0];\n },\n};\n\nfunction renderComponent(props: {\n path: string,\n searchParams: Record<string, string>,\n fullPage: boolean,\n componentProps?: BaseHandlerProps['componentProps'],\n shouldRedirectToPage?: (name: keyof HandlerUrls) => boolean,\n getDefaultUnknownPathUrl?: (path: string) => string | null,\n onNotFound: () => any,\n app: StackClientApp<any> | StackServerApp<any>,\n}) {\n const { path, searchParams, fullPage, componentProps, shouldRedirectToPage, getDefaultUnknownPathUrl, onNotFound, app } = props;\n\n switch (path) {\n case availablePaths.signIn: {\n if (shouldRedirectToPage?.('signIn')) return { redirectToPage: 'signIn' as const };\n return <SignIn\n fullPage={fullPage}\n automaticRedirect\n {...filterUndefinedINU(componentProps?.SignIn)}\n />;\n }\n case availablePaths.signUp: {\n if (shouldRedirectToPage?.('signUp')) return { redirectToPage: 'signUp' as const };\n return <SignUp\n fullPage={fullPage}\n automaticRedirect\n {...filterUndefinedINU(componentProps?.SignUp)}\n />;\n }\n case availablePaths.emailVerification: {\n if (shouldRedirectToPage?.('emailVerification')) return { redirectToPage: 'emailVerification' as const };\n return <EmailVerification\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.EmailVerification)}\n />;\n }\n case availablePaths.passwordReset: {\n if (shouldRedirectToPage?.('passwordReset')) return { redirectToPage: 'passwordReset' as const };\n return <PasswordReset\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.PasswordReset)}\n />;\n }\n case availablePaths.forgotPassword: {\n if (shouldRedirectToPage?.('forgotPassword')) return { redirectToPage: 'forgotPassword' as const };\n return <ForgotPassword\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.ForgotPassword)}\n />;\n }\n case availablePaths.signOut: {\n if (shouldRedirectToPage?.('signOut')) return { redirectToPage: 'signOut' as const };\n return <SignOut\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.SignOut)}\n />;\n }\n case availablePaths.oauthCallback: {\n if (shouldRedirectToPage?.('oauthCallback')) return { redirectToPage: 'oauthCallback' as const };\n return <OAuthCallback\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.OAuthCallback)}\n />;\n }\n case availablePaths.magicLinkCallback: {\n if (shouldRedirectToPage?.('magicLinkCallback')) return { redirectToPage: 'magicLinkCallback' as const };\n return <MagicLinkCallback\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.MagicLinkCallback)}\n />;\n }\n case availablePaths.teamInvitation: {\n if (shouldRedirectToPage?.('teamInvitation')) return { redirectToPage: 'teamInvitation' as const };\n return <TeamInvitation\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.TeamInvitation)}\n />;\n }\n case availablePaths.accountSettings: {\n return <AccountSettings\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.AccountSettings)}\n />;\n }\n case availablePaths.error: {\n return <ErrorPage\n searchParams={searchParams}\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.ErrorPage)}\n />;\n }\n case availablePaths.cliAuthConfirm: {\n if (shouldRedirectToPage?.('cliAuthConfirm')) return { redirectToPage: 'cliAuthConfirm' as const };\n return <CliAuthConfirmation\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.CliAuthConfirmation)}\n />;\n }\n case availablePaths.mfa: {\n if (shouldRedirectToPage?.('mfa')) return { redirectToPage: 'mfa' as const };\n return <MFA\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.MFA)}\n />;\n }\n case availablePaths.onboarding: {\n if (shouldRedirectToPage?.('onboarding')) return { redirectToPage: 'onboarding' as const };\n return <Onboarding\n fullPage={fullPage}\n {...filterUndefinedINU(componentProps?.Onboarding)}\n />;\n }\n default: {\n if (Object.values(availablePaths).includes(path as any)) {\n throw new HexclaveAssertionError(`Path alias ${path} not included in switch statement, but in availablePaths?`, { availablePaths });\n }\n for (const [key, value] of Object.entries(pathAliases)) {\n if (path.toLowerCase().replaceAll('-', '') === key.toLowerCase().replaceAll('-', '')) {\n const redirectUrl = `${app.urls.handler}/${value}?${new URLSearchParams(searchParams).toString()}`;\n return { redirect: redirectUrl };\n }\n }\n const defaultUnknownPathUrl = getDefaultUnknownPathUrl?.(path);\n if (defaultUnknownPathUrl != null) {\n const defaultUnknownPathUrlObject = new URL(defaultUnknownPathUrl, \"http://example.com\");\n for (const [key, value] of Object.entries(searchParams)) {\n defaultUnknownPathUrlObject.searchParams.set(key, value);\n }\n return { redirect: toAbsoluteOrRelativeRedirectTarget(defaultUnknownPathUrlObject) };\n }\n return onNotFound();\n }\n }\n}\n\nexport async function getRedirectToPageResult(\n app: StackClientApp,\n redirectToPage: keyof HandlerUrls,\n): Promise<RedirectToPageResult> {\n try {\n await app[hexclaveAppInternalsSymbol].redirectToHandler(redirectToPage, { replace: true });\n return { status: \"success\" };\n } catch (e) {\n if (KnownError.isKnownError(e)) {\n return { status: \"known-error\", error: e };\n }\n captureError(\"<HexclaveHandlerClient redirectToPage />\", e);\n return { status: \"unknown-error\" };\n }\n}\n\nfunction RedirectToPage(props: {\n app: StackClientApp,\n fullPage?: boolean,\n redirectToPage: keyof HandlerUrls,\n}) {\n const redirectResultPromise = useMemo(\n () => getRedirectToPageResult(props.app, props.redirectToPage),\n [props.app, props.redirectToPage],\n );\n\n const redirectResult = use(redirectResultPromise);\n if (redirectResult.status === \"known-error\") {\n return <KnownErrorMessageCard error={redirectResult.error} fullPage={props.fullPage} />;\n }\n if (redirectResult.status === \"unknown-error\") {\n return <PredefinedMessageCard type=\"unknownError\" fullPage={props.fullPage} />;\n }\n return <MessageCard title=\"Redirecting...\" fullPage={props.fullPage} />;\n}\n\nexport function HexclaveHandlerClient(props: BaseHandlerProps & Partial<RouteProps> & { location?: string }) {\n // Use hooks to get app\n const hexclaveApp = useStackApp();\n const clientOrigin = useClientOriginAfterHydration();\n\n const pathname = usePathname();\n const searchParamsFromHook = useSearchParams();\n const currentLocation = pathname;\n const searchParamsSource = searchParamsFromHook;\n\n const { path, searchParams, handlerPath } = useMemo(() => {\n const handlerPath = new URL(hexclaveApp.urls.handler, 'http://example.com').pathname;\n const relativePath = currentLocation.startsWith(handlerPath)\n ? currentLocation.slice(handlerPath.length).replace(/^\\/+/, '')\n : currentLocation.replace(/^\\/+/, '');\n\n return {\n path: relativePath,\n searchParams: Object.fromEntries(searchParamsSource.entries()),\n handlerPath,\n };\n }, [currentLocation, searchParamsSource, hexclaveApp.urls.handler]);\n\n const getDefaultUnknownPathUrl = (unknownPath: string): string | null => {\n return resolveUnknownHandlerPathFallbackUrl({\n defaultTarget: hexclaveApp[hexclaveAppInternalsSymbol].getConstructorOptions().urls?.default,\n projectId: hexclaveApp.projectId,\n unknownPath,\n });\n };\n\n const shouldRedirectToPage = (name: keyof HandlerUrls): boolean => {\n const url = hexclaveApp.urls[name];\n const isCrossDomainLocalOauthCallback = name === \"oauthCallback\" && searchParams.hexclave_cross_domain_auth === \"1\";\n if (isCrossDomainLocalOauthCallback) {\n return false;\n }\n return !isLocalHandlerUrlTarget({\n targetUrl: url,\n handlerPath,\n currentOrigin: clientOrigin,\n });\n };\n\n const result = renderComponent({\n path,\n searchParams,\n fullPage: props.fullPage,\n componentProps: props.componentProps,\n shouldRedirectToPage,\n getDefaultUnknownPathUrl,\n onNotFound: () =>\n notFound()\n ,\n app: hexclaveApp,\n });\n\n const redirectToPage = (result != null && typeof result === 'object' && 'redirectToPage' in result) ? result.redirectToPage : undefined;\n\n if (redirectToPage != null) {\n return (\n <Suspense fallback={<MessageCard title=\"Redirecting...\" fullPage={props.fullPage} />}>\n <RedirectToPage app={hexclaveApp} redirectToPage={redirectToPage} fullPage={props.fullPage} />\n </Suspense>\n );\n }\n\n if (result && 'redirect' in result) {\n redirect(result.redirect, RedirectType.replace);\n }\n\n\n return result;\n}\n\n// filter undefined values in object. if object itself is undefined, return undefined\nfunction filterUndefinedINU<T extends {}>(value: T | undefined): FilterUndefined<T> | undefined {\n return value === undefined ? value : filterUndefined(value);\n}\n\nfunction toAbsoluteOrRelativeRedirectTarget(url: URL): string {\n return url.origin === \"http://example.com\" ? getRelativePart(url) : url.toString();\n}\n\nfunction useClientOriginAfterHydration(): string | undefined {\n // The first hydrated render must match SSR. After hydration, React re-checks\n // the snapshot and lets us distinguish same-path cross-origin handler URLs.\n return useSyncExternalStore(\n () => () => {},\n () => window.location.origin,\n () => undefined,\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const require_chunk = require('../chunk-BE-pF4vm.js');
|
|
2
|
+
let _hexclave_shared = require("@hexclave/shared");
|
|
3
|
+
let vitest = require("vitest");
|
|
4
|
+
let ___lib_hexclave_app_index_js = require("../lib/hexclave-app/index.js");
|
|
5
|
+
let __hexclave_handler_client_js = require("./hexclave-handler-client.js");
|
|
6
|
+
|
|
7
|
+
//#region src/components-page/hexclave-handler-client.test.tsx
|
|
8
|
+
vitest.vi.mock("next/navigation", () => ({
|
|
9
|
+
RedirectType: { replace: "replace" },
|
|
10
|
+
notFound: () => {
|
|
11
|
+
throw new Error("notFound");
|
|
12
|
+
},
|
|
13
|
+
redirect: (url) => {
|
|
14
|
+
throw new Error(`redirect:${url}`);
|
|
15
|
+
},
|
|
16
|
+
usePathname: () => window.location.pathname,
|
|
17
|
+
useSearchParams: () => new URLSearchParams(window.location.search)
|
|
18
|
+
}));
|
|
19
|
+
function createAppTestDouble(options) {
|
|
20
|
+
const projectId = "00000000-0000-4000-8000-000000000000";
|
|
21
|
+
return {
|
|
22
|
+
projectId,
|
|
23
|
+
urls: {
|
|
24
|
+
handler: "http://localhost/handler",
|
|
25
|
+
signIn: `https://${projectId}.example-stack-hosted.test/handler/sign-in`,
|
|
26
|
+
home: "http://localhost"
|
|
27
|
+
},
|
|
28
|
+
redirectToHome: vitest.vi.fn(async () => {}),
|
|
29
|
+
[___lib_hexclave_app_index_js.hexclaveAppInternalsSymbol]: {
|
|
30
|
+
getConstructorOptions: () => ({ urls: {} }),
|
|
31
|
+
redirectToHandler: options.redirectToHandler
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
(0, vitest.describe)("HexclaveHandlerClient", () => {
|
|
36
|
+
(0, vitest.it)("returns known cross-domain redirect errors instead of treating them as unhandled async failures", async () => {
|
|
37
|
+
const redirectToHandler = vitest.vi.fn(async () => {
|
|
38
|
+
throw new _hexclave_shared.KnownErrors.RedirectUrlNotWhitelisted();
|
|
39
|
+
});
|
|
40
|
+
const result = await (0, __hexclave_handler_client_js.getRedirectToPageResult)(createAppTestDouble({ redirectToHandler }), "signIn");
|
|
41
|
+
(0, vitest.expect)(redirectToHandler).toHaveBeenCalledWith("signIn", { replace: true });
|
|
42
|
+
(0, vitest.expect)(result.status).toBe("known-error");
|
|
43
|
+
if (result.status === "known-error") {
|
|
44
|
+
(0, vitest.expect)(result.error.errorCode).toBe("REDIRECT_URL_NOT_WHITELISTED");
|
|
45
|
+
(0, vitest.expect)(result.error.message).toContain("Redirect URL not whitelisted");
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
//# sourceMappingURL=hexclave-handler-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hexclave-handler-client.test.js","names":["vi","hexclaveAppInternalsSymbol","KnownErrors"],"sources":["../../src/components-page/hexclave-handler-client.test.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport { hexclaveAppInternalsSymbol } from \"../lib/hexclave-app\";\nimport type { StackClientApp } from \"../lib/hexclave-app/apps/interfaces/client-app\";\nimport { getRedirectToPageResult } from \"./hexclave-handler-client\";\n\nvi.mock(\"next/navigation\", () => ({\n RedirectType: {\n replace: \"replace\",\n },\n notFound: () => {\n throw new Error(\"notFound\");\n },\n redirect: (url: string) => {\n throw new Error(`redirect:${url}`);\n },\n usePathname: () => window.location.pathname,\n useSearchParams: () => new URLSearchParams(window.location.search),\n}));\n\nfunction createAppTestDouble(options: {\n redirectToHandler: (name: string, options: { replace: true }) => Promise<void>,\n}) {\n const projectId = \"00000000-0000-4000-8000-000000000000\";\n const app = {\n projectId,\n urls: {\n handler: \"http://localhost/handler\",\n signIn: `https://${projectId}.example-stack-hosted.test/handler/sign-in`,\n home: \"http://localhost\",\n },\n redirectToHome: vi.fn(async () => {}),\n [hexclaveAppInternalsSymbol]: {\n getConstructorOptions: () => ({ urls: {} }),\n redirectToHandler: options.redirectToHandler,\n },\n };\n\n // This test double intentionally implements only the StackClientApp surface\n // that HexclaveHandlerClient touches in this redirect path.\n return app as unknown as StackClientApp<true>;\n}\n\ndescribe(\"HexclaveHandlerClient\", () => {\n it(\"returns known cross-domain redirect errors instead of treating them as unhandled async failures\", async () => {\n const redirectToHandler = vi.fn(async () => {\n throw new KnownErrors.RedirectUrlNotWhitelisted();\n });\n const app = createAppTestDouble({ redirectToHandler });\n\n const result = await getRedirectToPageResult(app, \"signIn\");\n\n expect(redirectToHandler).toHaveBeenCalledWith(\"signIn\", { replace: true });\n expect(result.status).toBe(\"known-error\");\n if (result.status === \"known-error\") {\n expect(result.error.errorCode).toBe(\"REDIRECT_URL_NOT_WHITELISTED\");\n expect(result.error.message).toContain(\"Redirect URL not whitelisted\");\n }\n });\n});\n"],"mappings":";;;;;;;AAUAA,UAAG,KAAK,0BAA0B;CAChC,cAAc,EACZ,SAAS,WACV;CACD,gBAAgB;AACd,QAAM,IAAI,MAAM,WAAW;;CAE7B,WAAW,QAAgB;AACzB,QAAM,IAAI,MAAM,YAAY,MAAM;;CAEpC,mBAAmB,OAAO,SAAS;CACnC,uBAAuB,IAAI,gBAAgB,OAAO,SAAS,OAAO;CACnE,EAAE;AAEH,SAAS,oBAAoB,SAE1B;CACD,MAAM,YAAY;AAiBlB,QAhBY;EACV;EACA,MAAM;GACJ,SAAS;GACT,QAAQ,WAAW,UAAU;GAC7B,MAAM;GACP;EACD,gBAAgBA,UAAG,GAAG,YAAY,GAAG;GACpCC,0DAA6B;GAC5B,8BAA8B,EAAE,MAAM,EAAE,EAAE;GAC1C,mBAAmB,QAAQ;GAC5B;EACF;;qBAOM,+BAA+B;AACtC,gBAAG,mGAAmG,YAAY;EAChH,MAAM,oBAAoBD,UAAG,GAAG,YAAY;AAC1C,SAAM,IAAIE,6BAAY,2BAA2B;IACjD;EAGF,MAAM,SAAS,gEAFH,oBAAoB,EAAE,mBAAmB,CAAC,EAEJ,SAAS;AAE3D,qBAAO,kBAAkB,CAAC,qBAAqB,UAAU,EAAE,SAAS,MAAM,CAAC;AAC3E,qBAAO,OAAO,OAAO,CAAC,KAAK,cAAc;AACzC,MAAI,OAAO,WAAW,eAAe;AACnC,sBAAO,OAAO,MAAM,UAAU,CAAC,KAAK,+BAA+B;AACnE,sBAAO,OAAO,MAAM,QAAQ,CAAC,UAAU,+BAA+B;;GAExE;EACF"}
|
|
@@ -4,7 +4,7 @@ let _hexclave_shared_dist_utils_promises = require("@hexclave/shared/dist/utils/
|
|
|
4
4
|
let ___lib_hexclave_app_common_js = require("../lib/hexclave-app/common.js");
|
|
5
5
|
let _hexclave_shared_dist_utils_urls = require("@hexclave/shared/dist/utils/urls");
|
|
6
6
|
let ___lib_hexclave_app_url_targets_js = require("../lib/hexclave-app/url-targets.js");
|
|
7
|
-
let
|
|
7
|
+
let ___generated_env_js = require("../generated/env.js");
|
|
8
8
|
let ___lib_hexclave_app_apps_implementations_common_js = require("../lib/hexclave-app/apps/implementations/common.js");
|
|
9
9
|
let __dev_tool_styles_js = require("./dev-tool-styles.js");
|
|
10
10
|
let __dev_tool_trigger_position_js = require("./dev-tool-trigger-position.js");
|
|
@@ -151,7 +151,7 @@ function resolveApiBaseUrl(app) {
|
|
|
151
151
|
return (0, ___lib_hexclave_app_apps_implementations_common_js.getBaseUrl)(app[___lib_hexclave_app_common_js.hexclaveAppInternalsSymbol].getConstructorOptions().baseUrl);
|
|
152
152
|
}
|
|
153
153
|
function shouldShowDashboardTab(app) {
|
|
154
|
-
return
|
|
154
|
+
return ___generated_env_js.envVars.HEXCLAVE_IS_LOCAL_EMULATOR === "true" && (0, _hexclave_shared_dist_utils_urls.isLocalhost)(resolveApiBaseUrl(app));
|
|
155
155
|
}
|
|
156
156
|
function getTabsForApp(app) {
|
|
157
157
|
if (shouldShowDashboardTab(app)) return TABS;
|