@hexclave/shared 1.0.2 → 1.0.5
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/ai/unified-prompts/reminders.js +4 -3
- package/dist/ai/unified-prompts/reminders.js.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts +3 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +217 -30
- package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.d.ts.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.js +1 -0
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-index.js.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +18 -6
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +23 -8
- package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.js +1 -1
- package/dist/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.js.map +1 -1
- package/dist/config/schema.d.ts +63 -63
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +5 -5
- package/dist/config/schema.js.map +1 -1
- package/dist/config-authoring.d.ts +4 -4
- package/dist/config-authoring.d.ts.map +1 -1
- package/dist/config-authoring.js +2 -2
- package/dist/config-authoring.js.map +1 -1
- package/dist/config-rendering.d.ts +4 -4
- package/dist/config-rendering.d.ts.map +1 -1
- package/dist/config-rendering.js +19 -18
- package/dist/config-rendering.js.map +1 -1
- package/dist/esm/ai/unified-prompts/reminders.js +4 -3
- package/dist/esm/ai/unified-prompts/reminders.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts +3 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +216 -31
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.d.ts.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.js +1 -0
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-index.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +18 -6
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +23 -8
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.js +1 -1
- package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.js.map +1 -1
- package/dist/esm/config/schema.d.ts +63 -63
- package/dist/esm/config/schema.d.ts.map +1 -1
- package/dist/esm/config/schema.js +5 -5
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/config-authoring.d.ts +4 -4
- package/dist/esm/config-authoring.d.ts.map +1 -1
- package/dist/esm/config-authoring.js +2 -2
- package/dist/esm/config-authoring.js.map +1 -1
- package/dist/esm/config-rendering.d.ts +4 -4
- package/dist/esm/config-rendering.d.ts.map +1 -1
- package/dist/esm/config-rendering.js +15 -14
- package/dist/esm/config-rendering.js.map +1 -1
- package/dist/esm/helpers/vault/client-side.d.ts.map +1 -1
- package/dist/esm/helpers/vault/client-side.js.map +1 -1
- package/dist/esm/helpers/vault/server-side.d.ts.map +1 -1
- package/dist/esm/helpers/vault/server-side.js.map +1 -1
- package/dist/esm/hexclave-config-file.d.ts +15 -0
- package/dist/esm/hexclave-config-file.d.ts.map +1 -0
- package/dist/esm/{stack-config-file.js → hexclave-config-file.js} +7 -7
- package/dist/esm/hexclave-config-file.js.map +1 -0
- package/dist/esm/hooks/use-async-callback.js +1 -1
- package/dist/esm/hooks/use-async-external-store.js +1 -1
- package/dist/esm/hooks/use-strict-memo.js +1 -1
- package/dist/esm/interface/admin-metrics.d.ts +8 -8
- package/dist/esm/interface/admin-metrics.js.map +1 -1
- package/dist/esm/interface/client-interface.js +2 -2
- package/dist/esm/interface/client-interface.js.map +1 -1
- package/dist/esm/interface/conversations.d.ts +32 -32
- package/dist/esm/interface/crud/current-user.d.ts +9 -9
- package/dist/esm/interface/crud/email-outbox.d.ts +200 -200
- package/dist/esm/interface/crud/products.d.ts +13 -13
- package/dist/esm/interface/crud/products.d.ts.map +1 -1
- package/dist/esm/interface/crud/project-api-keys.d.ts +4 -4
- package/dist/esm/interface/crud/project-permissions.js +5 -5
- package/dist/esm/interface/crud/project-permissions.js.map +1 -1
- package/dist/esm/interface/crud/projects.d.ts +36 -36
- package/dist/esm/interface/crud/team-member-profiles.d.ts +18 -18
- package/dist/esm/interface/crud/team-permissions.js +5 -5
- package/dist/esm/interface/crud/team-permissions.js.map +1 -1
- package/dist/esm/interface/crud/transactions.d.ts +6 -6
- package/dist/esm/interface/crud/transactions.d.ts.map +1 -1
- package/dist/esm/interface/crud/users.d.ts +12 -12
- package/dist/esm/interface/page-component-versions.js +107 -107
- package/dist/esm/interface/page-component-versions.js.map +1 -1
- package/dist/esm/interface/webhooks.d.ts +2 -2
- package/dist/esm/known-errors.d.ts +2 -2
- package/dist/esm/known-errors.js +4 -4
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.d.ts +17 -7
- package/dist/esm/schema-fields.d.ts.map +1 -1
- package/dist/esm/schema-fields.js +26 -16
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/sessions.d.ts +7 -7
- package/dist/esm/utils/crypto.d.ts.map +1 -1
- package/dist/esm/utils/crypto.js.map +1 -1
- package/dist/esm/utils/currency-constants.d.ts +6 -0
- package/dist/esm/utils/currency-constants.d.ts.map +1 -1
- package/dist/esm/utils/currency-constants.js.map +1 -1
- package/dist/esm/utils/dev-env-state-path.d.ts +3 -3
- package/dist/esm/utils/dev-env-state-path.d.ts.map +1 -1
- package/dist/esm/utils/dev-env-state-path.js +4 -4
- package/dist/esm/utils/dev-env-state-path.js.map +1 -1
- package/dist/esm/utils/errors.js +2 -2
- package/dist/esm/utils/errors.js.map +1 -1
- package/dist/esm/utils/esbuild.d.ts.map +1 -1
- package/dist/esm/utils/esbuild.js +0 -1
- package/dist/esm/utils/esbuild.js.map +1 -1
- package/dist/esm/utils/featurebase.d.ts +3 -3
- package/dist/esm/utils/featurebase.d.ts.map +1 -1
- package/dist/esm/utils/featurebase.js +15 -15
- package/dist/esm/utils/featurebase.js.map +1 -1
- package/dist/esm/utils/fs.js +6 -6
- package/dist/esm/utils/fs.js.map +1 -1
- package/dist/esm/utils/globals.d.ts.map +1 -1
- package/dist/esm/utils/globals.js +10 -10
- package/dist/esm/utils/globals.js.map +1 -1
- package/dist/esm/utils/jwt.d.ts.map +1 -1
- package/dist/esm/utils/jwt.js +3 -3
- package/dist/esm/utils/jwt.js.map +1 -1
- package/dist/esm/utils/redirect-urls.d.ts +3 -3
- package/dist/esm/utils/redirect-urls.d.ts.map +1 -1
- package/dist/esm/utils/redirect-urls.js +5 -5
- package/dist/esm/utils/redirect-urls.js.map +1 -1
- package/dist/esm/utils/urls.js.map +1 -1
- package/dist/helpers/vault/client-side.d.ts.map +1 -1
- package/dist/helpers/vault/client-side.js.map +1 -1
- package/dist/helpers/vault/server-side.d.ts.map +1 -1
- package/dist/helpers/vault/server-side.js.map +1 -1
- package/dist/hexclave-config-file.d.ts +15 -0
- package/dist/hexclave-config-file.d.ts.map +1 -0
- package/dist/{stack-config-file.js → hexclave-config-file.js} +8 -8
- package/dist/hexclave-config-file.js.map +1 -0
- package/dist/hooks/use-async-callback.js +1 -1
- package/dist/hooks/use-async-external-store.js +1 -1
- package/dist/hooks/use-strict-memo.js +1 -1
- package/dist/interface/admin-metrics.d.ts +8 -8
- package/dist/interface/admin-metrics.js.map +1 -1
- package/dist/interface/client-interface.js +2 -2
- package/dist/interface/client-interface.js.map +1 -1
- package/dist/interface/conversations.d.ts +32 -32
- package/dist/interface/crud/current-user.d.ts +9 -9
- package/dist/interface/crud/email-outbox.d.ts +200 -200
- package/dist/interface/crud/products.d.ts +13 -13
- package/dist/interface/crud/products.d.ts.map +1 -1
- package/dist/interface/crud/project-api-keys.d.ts +4 -4
- package/dist/interface/crud/project-permissions.js +5 -5
- package/dist/interface/crud/project-permissions.js.map +1 -1
- package/dist/interface/crud/projects.d.ts +36 -36
- package/dist/interface/crud/team-member-profiles.d.ts +18 -18
- package/dist/interface/crud/team-permissions.js +5 -5
- package/dist/interface/crud/team-permissions.js.map +1 -1
- package/dist/interface/crud/transactions.d.ts +6 -6
- package/dist/interface/crud/transactions.d.ts.map +1 -1
- package/dist/interface/crud/users.d.ts +12 -12
- package/dist/interface/page-component-versions.js +107 -107
- package/dist/interface/page-component-versions.js.map +1 -1
- package/dist/interface/webhooks.d.ts +2 -2
- package/dist/known-errors.d.ts +2 -2
- package/dist/known-errors.js +4 -4
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.ts +17 -7
- package/dist/schema-fields.d.ts.map +1 -1
- package/dist/schema-fields.js +26 -16
- package/dist/schema-fields.js.map +1 -1
- package/dist/sessions.d.ts +7 -7
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/currency-constants.d.ts +6 -0
- package/dist/utils/currency-constants.d.ts.map +1 -1
- package/dist/utils/currency-constants.js.map +1 -1
- package/dist/utils/dev-env-state-path.d.ts +3 -3
- package/dist/utils/dev-env-state-path.d.ts.map +1 -1
- package/dist/utils/dev-env-state-path.js +5 -5
- package/dist/utils/dev-env-state-path.js.map +1 -1
- package/dist/utils/errors.js +2 -2
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/esbuild.d.ts.map +1 -1
- package/dist/utils/esbuild.js +0 -1
- package/dist/utils/esbuild.js.map +1 -1
- package/dist/utils/featurebase.d.ts +3 -3
- package/dist/utils/featurebase.d.ts.map +1 -1
- package/dist/utils/featurebase.js +15 -15
- package/dist/utils/featurebase.js.map +1 -1
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/globals.d.ts.map +1 -1
- package/dist/utils/globals.js +10 -10
- package/dist/utils/globals.js.map +1 -1
- package/dist/utils/jwt.d.ts.map +1 -1
- package/dist/utils/jwt.js +3 -3
- package/dist/utils/jwt.js.map +1 -1
- package/dist/utils/redirect-urls.d.ts +3 -3
- package/dist/utils/redirect-urls.d.ts.map +1 -1
- package/dist/utils/redirect-urls.js +5 -5
- package/dist/utils/redirect-urls.js.map +1 -1
- package/dist/utils/urls.js.map +1 -1
- package/package.json +2 -1
- package/src/ai/image-limits.ts +62 -0
- package/src/ai/llms/llms.ts +36 -0
- package/src/ai/unified-prompts/reminders.ts +27 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.ts +1047 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/brain.ts +5 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/cli-help.ts +5 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/config-docs.ts +5 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/custom-components-instructions.ts +5 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/dashboard-instructions.ts +5 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/docs-index.ts +80 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts +322 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/openapi-specs.ts +5 -0
- package/src/ai/unified-prompts/skill-site-prompt-parts/sdk-interface-source.ts +5 -0
- package/src/ai/unified-prompts/skill-site-prompt.ts +72 -0
- package/src/apps/apps-config.ts +177 -0
- package/src/apps/apps-ui.tsx +112 -0
- package/src/config/README.md +154 -0
- package/src/config/db-sync-mappings.ts +2293 -0
- package/src/config/format.ts +388 -0
- package/src/config/migrate-catalogs-to-product-lines.ts +475 -0
- package/src/config/schema-fuzzer.test.ts +377 -0
- package/src/config/schema.ts +1507 -0
- package/src/config-authoring.test.ts +52 -0
- package/src/config-authoring.ts +37 -0
- package/src/config-rendering.ts +142 -0
- package/src/crud.tsx +275 -0
- package/src/global.d.ts +1 -0
- package/src/helpers/emails.ts +250 -0
- package/src/helpers/init-prompt.ts +39 -0
- package/src/helpers/password.ts +16 -0
- package/src/helpers/production-mode.ts +58 -0
- package/src/helpers/support-sla.test.ts +124 -0
- package/src/helpers/support-sla.ts +93 -0
- package/src/helpers/vault/client-side.ts +92 -0
- package/src/helpers/vault/server-side.ts +153 -0
- package/src/hexclave-config-file.ts +120 -0
- package/src/hooks/use-async-callback.tsx +43 -0
- package/src/hooks/use-async-external-store.tsx +24 -0
- package/src/hooks/use-hash.tsx +14 -0
- package/src/hooks/use-hover.tsx +88 -0
- package/src/hooks/use-strict-memo.tsx +157 -0
- package/src/index.ts +17 -0
- package/src/interface/admin-interface.ts +1043 -0
- package/src/interface/admin-metrics.ts +214 -0
- package/src/interface/client-interface.test.ts +765 -0
- package/src/interface/client-interface.ts +2586 -0
- package/src/interface/conversations.ts +109 -0
- package/src/interface/crud/analytics.ts +11 -0
- package/src/interface/crud/connected-accounts.ts +52 -0
- package/src/interface/crud/contact-channels.ts +74 -0
- package/src/interface/crud/current-user.ts +72 -0
- package/src/interface/crud/email-outbox.ts +263 -0
- package/src/interface/crud/email-templates.ts +50 -0
- package/src/interface/crud/emails.ts +22 -0
- package/src/interface/crud/internal-api-keys.ts +69 -0
- package/src/interface/crud/invoices.ts +36 -0
- package/src/interface/crud/items.ts +24 -0
- package/src/interface/crud/notification-preferences.ts +33 -0
- package/src/interface/crud/oauth-providers.ts +91 -0
- package/src/interface/crud/products.ts +42 -0
- package/src/interface/crud/project-api-keys.ts +100 -0
- package/src/interface/crud/project-permissions.ts +118 -0
- package/src/interface/crud/projects.ts +229 -0
- package/src/interface/crud/session-replays.ts +85 -0
- package/src/interface/crud/sessions.ts +66 -0
- package/src/interface/crud/sign-up-rules.ts +12 -0
- package/src/interface/crud/svix-token.ts +20 -0
- package/src/interface/crud/team-invitation-details.ts +22 -0
- package/src/interface/crud/team-invitation.ts +35 -0
- package/src/interface/crud/team-member-profiles.ts +61 -0
- package/src/interface/crud/team-memberships.ts +56 -0
- package/src/interface/crud/team-permissions.ts +119 -0
- package/src/interface/crud/teams.ts +142 -0
- package/src/interface/crud/transactions.ts +131 -0
- package/src/interface/crud/users.ts +185 -0
- package/src/interface/handler-urls.ts +74 -0
- package/src/interface/page-component-versions.ts +1702 -0
- package/src/interface/server-interface.ts +1146 -0
- package/src/interface/webhooks.ts +28 -0
- package/src/known-errors.tsx +2031 -0
- package/src/local-emulator.ts +5 -0
- package/src/payments/payment-methods.ts +167 -0
- package/src/payments/stripe-limits.ts +18 -0
- package/src/plans.ts +107 -0
- package/src/schema-fields.ts +944 -0
- package/src/sessions.ts +310 -0
- package/src/utils/api-keys.tsx +122 -0
- package/src/utils/arrays.tsx +248 -0
- package/src/utils/auth-methods.ts +8 -0
- package/src/utils/base64.tsx +24 -0
- package/src/utils/booleans.tsx +34 -0
- package/src/utils/browser-compat.tsx +17 -0
- package/src/utils/bytes.tsx +280 -0
- package/src/utils/caches.tsx +280 -0
- package/src/utils/cel-fields.ts +92 -0
- package/src/utils/cloud-hosts.tsx +92 -0
- package/src/utils/compile-time.tsx +11 -0
- package/src/utils/country-codes.ts +39 -0
- package/src/utils/crypto.tsx +174 -0
- package/src/utils/currencies.tsx +53 -0
- package/src/utils/currency-constants.tsx +51 -0
- package/src/utils/dates.tsx +246 -0
- package/src/utils/dev-env-state-path.ts +14 -0
- package/src/utils/dom.tsx +7 -0
- package/src/utils/env.tsx +117 -0
- package/src/utils/errors.tsx +284 -0
- package/src/utils/esbuild.tsx +235 -0
- package/src/utils/featurebase.tsx +247 -0
- package/src/utils/fs.tsx +33 -0
- package/src/utils/functions.tsx +22 -0
- package/src/utils/geo.tsx +16 -0
- package/src/utils/globals.tsx +48 -0
- package/src/utils/hashes.tsx +52 -0
- package/src/utils/html.tsx +60 -0
- package/src/utils/http.tsx +91 -0
- package/src/utils/ips.tsx +47 -0
- package/src/utils/json.tsx +200 -0
- package/src/utils/jsx-editable-transpiler.test.ts +156 -0
- package/src/utils/jsx-editable-transpiler.tsx +401 -0
- package/src/utils/jwt.tsx +170 -0
- package/src/utils/locks.tsx +71 -0
- package/src/utils/maps.tsx +407 -0
- package/src/utils/math.tsx +18 -0
- package/src/utils/monkey-patch.tsx +29 -0
- package/src/utils/node-http.tsx +47 -0
- package/src/utils/numbers.tsx +82 -0
- package/src/utils/oauth.tsx +9 -0
- package/src/utils/objects.tsx +581 -0
- package/src/utils/paginated-lists.test.ts +1039 -0
- package/src/utils/paginated-lists.tsx +479 -0
- package/src/utils/passkey.tsx +1 -0
- package/src/utils/promises.tsx +540 -0
- package/src/utils/proxies.tsx +212 -0
- package/src/utils/react.tsx +308 -0
- package/src/utils/redirect-urls.tsx +299 -0
- package/src/utils/regex.tsx +10 -0
- package/src/utils/results.tsx +411 -0
- package/src/utils/sentry.tsx +20 -0
- package/src/utils/stores.tsx +238 -0
- package/src/utils/strings.nicify.test.ts +253 -0
- package/src/utils/strings.tsx +723 -0
- package/src/utils/telemetry.tsx +37 -0
- package/src/utils/turnstile-browser.ts +106 -0
- package/src/utils/turnstile-flow.test.ts +101 -0
- package/src/utils/turnstile-flow.ts +266 -0
- package/src/utils/turnstile.ts +34 -0
- package/src/utils/typed-arrays.tsx +17 -0
- package/src/utils/types.tsx +208 -0
- package/src/utils/unicode.tsx +24 -0
- package/src/utils/urls.tsx +411 -0
- package/src/utils/uuids.tsx +47 -0
- package/dist/esm/stack-config-file.d.ts +0 -15
- package/dist/esm/stack-config-file.d.ts.map +0 -1
- package/dist/esm/stack-config-file.js.map +0 -1
- package/dist/stack-config-file.d.ts +0 -15
- package/dist/stack-config-file.d.ts.map +0 -1
- package/dist/stack-config-file.js.map +0 -1
|
@@ -22,11 +22,11 @@ function createCustomPagePrompt(options) {
|
|
|
22
22
|
const latestPageVersion = Math.max(1, ...Object.keys(options.versions).map(Number));
|
|
23
23
|
const latestSdkVersion = latestPageVersion === 1 ? options.minSdkVersion : options.versions[latestPageVersion].minSdkVersion;
|
|
24
24
|
const fullPrompt = ___utils_strings_js.deindent`
|
|
25
|
-
This prompt explains how to implement a custom ${options.title} page for
|
|
25
|
+
This prompt explains how to implement a custom ${options.title} page for Hexclave. The version of this page that you are implementing is v${latestPageVersion}. It can be found in Hexclave's documentation, and in the Hexclave devtool indicator.
|
|
26
26
|
|
|
27
|
-
First, make sure to upgrade the
|
|
27
|
+
First, make sure to upgrade the Hexclave SDK to a recent version. The minimum supported SDK version for this walkthrough is v${latestSdkVersion}.
|
|
28
28
|
|
|
29
|
-
The user's codebase may already have a ${options.title} page that could be suitable (eg. from an earlier version of
|
|
29
|
+
The user's codebase may already have a ${options.title} page that could be suitable (eg. from an earlier version of Hexclave, a template, another auth provider before migrating to Hexclave, etc.). Use your critical thinking skills to determine what the user's intent is; it is likely that instead of creating a new page, you can just modify the existing page to use Hexclave & support the logic/structure below.
|
|
30
30
|
|
|
31
31
|
Below is a description of the logical structure of what this page should contain (note that the visual structure and layout may be different, and up to you). The page can have more content than this, but it should always contain at least what's described below.
|
|
32
32
|
|
|
@@ -43,10 +43,10 @@ function createCustomPagePrompt(options) {
|
|
|
43
43
|
${options.reactExample}
|
|
44
44
|
\`\`\`
|
|
45
45
|
|
|
46
|
-
When you're done, please update the file where the
|
|
46
|
+
When you're done, please update the file where the Hexclave app is configured with its URLs, to make sure it points to this page. For example, you may have an object declared like this:
|
|
47
47
|
|
|
48
48
|
\`\`\`tsx
|
|
49
|
-
export const
|
|
49
|
+
export const hexclaveServerApp = new HexclaveServerApp({
|
|
50
50
|
tokenStore: "nextjs-cookie",
|
|
51
51
|
urls: {
|
|
52
52
|
default: {
|
|
@@ -89,49 +89,49 @@ function createAuthPagePrompt(type) {
|
|
|
89
89
|
const authVerb = isSignIn ? "sign in" : "sign up";
|
|
90
90
|
const authVerbCapitalized = isSignIn ? "Sign in" : "Sign up";
|
|
91
91
|
const otherAuthVerb = isSignIn ? "sign up" : "sign in";
|
|
92
|
-
const credentialMethodCall = isSignIn ? "
|
|
92
|
+
const credentialMethodCall = isSignIn ? "hexclaveApp.signInWithCredential({ email: form.email, password: form.password })" : "hexclaveApp.signUpWithCredential({ email: form.email, password: form.password })";
|
|
93
93
|
return createCustomPagePrompt({
|
|
94
94
|
key: type,
|
|
95
95
|
title,
|
|
96
96
|
minSdkVersion: "0.0.1",
|
|
97
97
|
structure: ___utils_strings_js.deindent`
|
|
98
|
-
- If user is already signed in, regardless of whether restricted or not (ie. \`await
|
|
99
|
-
- If user is restricted, \`await
|
|
100
|
-
- Otherwise, \`await
|
|
101
|
-
- While the redirect is happening, you may display a loading indicator, or a note that the user is being redirected. If necessary, or if preferable, you can also render a message card that shows a link to \`await
|
|
98
|
+
- If user is already signed in, regardless of whether restricted or not (ie. \`await hexclaveApp.getUser({ includeRestricted: true }) !== null\`):
|
|
99
|
+
- If user is restricted, \`await hexclaveApp.redirectToOnboarding({ replace: true })\`
|
|
100
|
+
- Otherwise, \`await hexclaveApp.redirectToAfterSign${isSignIn ? "In" : "Up"}({ replace: true })\`
|
|
101
|
+
- While the redirect is happening, you may display a loading indicator, or a note that the user is being redirected. If necessary, or if preferable, you can also render a message card that shows a link to \`await hexclaveApp.redirectToHome()\` and a sign out button.
|
|
102
102
|
- If user is not signed in:
|
|
103
|
-
${isSignIn ? "- If sign-ups are enabled (\\`project = await
|
|
103
|
+
${isSignIn ? "- If sign-ups are enabled (\\`project = await hexclaveApp.getProject(); project.config.signUpEnabled\\`), show a link to the sign-up page." : "- If sign-ups are disabled (\\`project = await hexclaveApp.getProject(); !project.config.signUpEnabled\\`), show a message that sign-up is disabled."}
|
|
104
104
|
- Show a ${authVerb} screen. The auth methods that should render:
|
|
105
|
-
- For each OAuth provider (\`project.config.oauthProviders: { readonly id: string }[]\`), render an OAuth button. Clicking the button calls \`await
|
|
106
|
-
${isSignIn ? "- If \\`project.config.passkeyEnabled\\`, render a passkey button. Clicking the button calls \\`await
|
|
105
|
+
- For each OAuth provider (\`project.config.oauthProviders: { readonly id: string }[]\`), render an OAuth button. Clicking the button calls \`await hexclaveApp.signInWithOAuth("<providerId>")\`.
|
|
106
|
+
${isSignIn ? "- If \\`project.config.passkeyEnabled\\`, render a passkey button. Clicking the button calls \\`await hexclaveApp.signInWithPasskey()\\`." : ""}
|
|
107
107
|
- If \`project.config.credentialEnabled\`, render a credential ${authVerb} form:
|
|
108
108
|
- Email + password${isSignIn ? "" : " + repeat password"}
|
|
109
109
|
${isSignIn ? "" : "- Validate password strength with \\`getPasswordError()\\` and ensure repeated password matches"}
|
|
110
|
-
${isSignIn ? "- \"Forgot password?\" link calling \\`await
|
|
110
|
+
${isSignIn ? "- \"Forgot password?\" link calling \\`await hexclaveApp.redirectToForgotPassword()\\`" : ""}
|
|
111
111
|
- Submit calls \`${credentialMethodCall}: ${isSignIn ? "Promise<Result<undefined, KnownErrors[\"EmailPasswordMismatch\"] | KnownErrors[\"InvalidTotpCode\"]>>" : "Promise<Result<undefined, KnownErrors[\"UserWithEmailAlreadyExists\"] | KnownErrors[\"PasswordRequirementsNotMet\"] | KnownErrors[\"BotChallengeFailed\"]>>"}\`
|
|
112
112
|
- On error, display the error message on the email field
|
|
113
113
|
- If \`project.config.magicLinkEnabled\`, render a magic link form:
|
|
114
114
|
- Email input (validated to be a correct email address) + "Send email" button
|
|
115
|
-
- Calls \`
|
|
115
|
+
- Calls \`hexclaveApp.sendMagicLinkEmail(email): Promise<Result<{ nonce: string }, KnownErrors["RedirectUrlNotWhitelisted"] | KnownErrors["BotChallengeFailed"]>>\`
|
|
116
116
|
- After sending, switch to a 6-digit OTP input. User enters the code from their email
|
|
117
|
-
- Submit the OTP + nonce via \`
|
|
117
|
+
- Submit the OTP + nonce via \`hexclaveApp.signInWithMagicLink(otp + nonce): Promise<Result<undefined, KnownErrors["VerificationCodeError"] | KnownErrors["InvalidTotpCode"]>>\` (string concatenation)
|
|
118
118
|
- If both credential and magic-link are enabled, allow the user to choose which flow to use.
|
|
119
119
|
- If none of the above auth methods are enabled, show a message explaining that no authentication methods are enabled.
|
|
120
|
-
- Show a link to the ${otherAuthVerb} page that calls \`await
|
|
120
|
+
- Show a link to the ${otherAuthVerb} page that calls \`await hexclaveApp.redirectTo${isSignIn ? "SignUp" : "SignIn"}()\`.
|
|
121
121
|
`,
|
|
122
122
|
reactExample: ___utils_strings_js.deindent`
|
|
123
123
|
export default function Custom${isSignIn ? "SignIn" : "SignUp"}Page() {
|
|
124
|
-
const
|
|
124
|
+
const hexclaveApp = useHexclaveApp();
|
|
125
125
|
const user = useUser({ includeRestricted: true });
|
|
126
|
-
const project =
|
|
126
|
+
const project = hexclaveApp.useProject();
|
|
127
127
|
const [otpState, setOtpState] = useState<null | { nonce: string }>(null);
|
|
128
128
|
|
|
129
129
|
useEffect(() => {
|
|
130
130
|
if (user) {
|
|
131
131
|
if (user.isRestricted) {
|
|
132
|
-
void
|
|
132
|
+
void hexclaveApp.redirectToOnboarding();
|
|
133
133
|
} else {
|
|
134
|
-
void
|
|
134
|
+
void hexclaveApp.redirectToAfterSign${isSignIn ? "In" : "Up"}();
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
}, [user]);
|
|
@@ -140,8 +140,8 @@ function createAuthPagePrompt(type) {
|
|
|
140
140
|
return (
|
|
141
141
|
<div>
|
|
142
142
|
<Typography>You are already signed in.</Typography>
|
|
143
|
-
<Button onClick={async () => await
|
|
144
|
-
<Button onClick={async () => await
|
|
143
|
+
<Button onClick={async () => await hexclaveApp.redirectToSignOut()}>Sign out</Button>
|
|
144
|
+
<Button onClick={async () => await hexclaveApp.redirectToHome()}>Go home</Button>
|
|
145
145
|
</div>
|
|
146
146
|
);
|
|
147
147
|
}
|
|
@@ -154,7 +154,7 @@ function createAuthPagePrompt(type) {
|
|
|
154
154
|
if (otpState) {
|
|
155
155
|
return (
|
|
156
156
|
<Form onSubmit={async (form) => {
|
|
157
|
-
const result = await
|
|
157
|
+
const result = await hexclaveApp.signInWithMagicLink(form.otp + otpState.nonce);
|
|
158
158
|
if (result.status === "error") handleErrorNicely(...);
|
|
159
159
|
}}>
|
|
160
160
|
<Typography>Enter the code from your email</Typography>
|
|
@@ -180,10 +180,10 @@ function createAuthPagePrompt(type) {
|
|
|
180
180
|
<Typography>
|
|
181
181
|
{"Don't have an account? "}
|
|
182
182
|
<a
|
|
183
|
-
href={
|
|
183
|
+
href={hexclaveApp.urls.signUp}
|
|
184
184
|
onClick={async (e) => {
|
|
185
185
|
e.preventDefault();
|
|
186
|
-
await
|
|
186
|
+
await hexclaveApp.redirectToSignUp();
|
|
187
187
|
}}
|
|
188
188
|
>
|
|
189
189
|
Sign up
|
|
@@ -193,10 +193,10 @@ function createAuthPagePrompt(type) {
|
|
|
193
193
|
}` : `<Typography>
|
|
194
194
|
{"Already have an account? "}
|
|
195
195
|
<a
|
|
196
|
-
href={
|
|
196
|
+
href={hexclaveApp.urls.signIn}
|
|
197
197
|
onClick={async (e) => {
|
|
198
198
|
e.preventDefault();
|
|
199
|
-
await
|
|
199
|
+
await hexclaveApp.redirectToSignIn();
|
|
200
200
|
}}
|
|
201
201
|
>
|
|
202
202
|
Sign in
|
|
@@ -209,14 +209,14 @@ function createAuthPagePrompt(type) {
|
|
|
209
209
|
<Button
|
|
210
210
|
key={provider.id}
|
|
211
211
|
onClick={async () => {
|
|
212
|
-
await
|
|
212
|
+
await hexclaveApp.signInWithOAuth(provider.id);
|
|
213
213
|
}}
|
|
214
214
|
>
|
|
215
215
|
${authVerbCapitalized} with {provider.id}
|
|
216
216
|
</Button>
|
|
217
217
|
))}
|
|
218
218
|
${isSignIn ? `{hasPasskey && (
|
|
219
|
-
<Button onClick={async () => await
|
|
219
|
+
<Button onClick={async () => await hexclaveApp.signInWithPasskey()}>
|
|
220
220
|
Sign in with passkey
|
|
221
221
|
</Button>
|
|
222
222
|
)}` : ""}
|
|
@@ -237,7 +237,7 @@ function createAuthPagePrompt(type) {
|
|
|
237
237
|
</TabsList>
|
|
238
238
|
{hasMagicLink && <TabsContent value="magic-link">
|
|
239
239
|
<Form onSubmit={async (form) => {
|
|
240
|
-
const result = await
|
|
240
|
+
const result = await hexclaveApp.sendMagicLinkEmail(form.email);
|
|
241
241
|
if (result.status === "error") handleErrorNicely(...);
|
|
242
242
|
else setOtpState({ nonce: result.data.nonce });
|
|
243
243
|
}}>
|
|
@@ -262,7 +262,7 @@ function createAuthPagePrompt(type) {
|
|
|
262
262
|
<Label htmlFor="password">Password</Label>
|
|
263
263
|
<PasswordInput id="password" />
|
|
264
264
|
|
|
265
|
-
${isSignIn ? `<Button type="button" variant="link" onClick={async () => await
|
|
265
|
+
${isSignIn ? `<Button type="button" variant="link" onClick={async () => await hexclaveApp.redirectToForgotPassword()}>
|
|
266
266
|
Forgot password?
|
|
267
267
|
</Button>` : `<Label htmlFor="password-repeat">Repeat password</Label>
|
|
268
268
|
<PasswordInput id="password-repeat" />`}
|
|
@@ -308,7 +308,7 @@ function getCustomPagePrompts() {
|
|
|
308
308
|
|
|
309
309
|
export default function CustomSignOutPage() {
|
|
310
310
|
const user = useUser({ or: "return-null" });
|
|
311
|
-
const
|
|
311
|
+
const hexclaveApp = useHexclaveApp();
|
|
312
312
|
|
|
313
313
|
if (user) {
|
|
314
314
|
use(cacheSignOut(user));
|
|
@@ -319,7 +319,7 @@ function getCustomPagePrompts() {
|
|
|
319
319
|
title="Signed out"
|
|
320
320
|
primaryButtonText="Go home"
|
|
321
321
|
primaryAction={async () => {
|
|
322
|
-
await
|
|
322
|
+
await hexclaveApp.redirectToHome();
|
|
323
323
|
}}
|
|
324
324
|
>
|
|
325
325
|
You have been signed out successfully.
|
|
@@ -340,8 +340,8 @@ function getCustomPagePrompts() {
|
|
|
340
340
|
- Read the verification code from URL params.
|
|
341
341
|
- If the code is missing, show an invalid-link state.
|
|
342
342
|
- If the code exists, show a confirmation step:
|
|
343
|
-
- Verify action calls \`
|
|
344
|
-
- Cancel action calls \`
|
|
343
|
+
- Verify action calls \`hexclaveApp.verifyEmail(code)\`.
|
|
344
|
+
- Cancel action calls \`hexclaveApp.redirectToHome()\`.
|
|
345
345
|
- Handle verification result:
|
|
346
346
|
- \`VerificationCodeNotFound\` => invalid-link state.
|
|
347
347
|
- \`VerificationCodeExpired\` => expired-link state.
|
|
@@ -351,8 +351,8 @@ function getCustomPagePrompts() {
|
|
|
351
351
|
`,
|
|
352
352
|
reactExample: ___utils_strings_js.deindent`
|
|
353
353
|
export default function CustomEmailVerificationPage(props: { searchParams?: Record<string, string> }) {
|
|
354
|
-
const
|
|
355
|
-
const [result, setResult] = useState<Awaited<ReturnType<typeof
|
|
354
|
+
const hexclaveApp = useHexclaveApp();
|
|
355
|
+
const [result, setResult] = useState<Awaited<ReturnType<typeof hexclaveApp.verifyEmail>> | null>(null);
|
|
356
356
|
const code = props.searchParams?.code;
|
|
357
357
|
|
|
358
358
|
if (!code) {
|
|
@@ -365,11 +365,11 @@ function getCustomPagePrompts() {
|
|
|
365
365
|
title="Do you want to verify your email?"
|
|
366
366
|
primaryButtonText="Verify"
|
|
367
367
|
primaryAction={async () => {
|
|
368
|
-
setResult(await
|
|
368
|
+
setResult(await hexclaveApp.verifyEmail(code));
|
|
369
369
|
}}
|
|
370
370
|
secondaryButtonText="Cancel"
|
|
371
371
|
secondaryAction={async () => {
|
|
372
|
-
await
|
|
372
|
+
await hexclaveApp.redirectToHome();
|
|
373
373
|
}}
|
|
374
374
|
/>
|
|
375
375
|
);
|
|
@@ -390,7 +390,7 @@ function getCustomPagePrompts() {
|
|
|
390
390
|
title="Your email has been verified!"
|
|
391
391
|
primaryButtonText="Go home"
|
|
392
392
|
primaryAction={async () => {
|
|
393
|
-
await
|
|
393
|
+
await hexclaveApp.redirectToHome();
|
|
394
394
|
}}
|
|
395
395
|
/>
|
|
396
396
|
);
|
|
@@ -408,7 +408,7 @@ function getCustomPagePrompts() {
|
|
|
408
408
|
structure: ___utils_strings_js.deindent`
|
|
409
409
|
- Read the reset code from URL params.
|
|
410
410
|
- If code is missing, show an invalid-link state.
|
|
411
|
-
- Before rendering the form, verify the code via \`
|
|
411
|
+
- Before rendering the form, verify the code via \`hexclaveApp.verifyPasswordResetCode(code)\`.
|
|
412
412
|
- \`VerificationCodeNotFound\` => invalid-link state.
|
|
413
413
|
- \`VerificationCodeExpired\` => expired-link state.
|
|
414
414
|
- \`VerificationCodeAlreadyUsed\` => used-link state.
|
|
@@ -416,13 +416,13 @@ function getCustomPagePrompts() {
|
|
|
416
416
|
- If code is valid, render reset form:
|
|
417
417
|
- New password + repeated password.
|
|
418
418
|
- Validate password strength and ensure repeated password matches.
|
|
419
|
-
- Submit calls \`
|
|
419
|
+
- Submit calls \`hexclaveApp.resetPassword({ password, code })\`.
|
|
420
420
|
- If reset succeeds, show success state.
|
|
421
421
|
- If reset fails, show error state with guidance to request a new link.
|
|
422
422
|
`,
|
|
423
423
|
reactExample: ___utils_strings_js.deindent`
|
|
424
424
|
export default function CustomPasswordResetPage(props: { searchParams: Record<string, string> }) {
|
|
425
|
-
const
|
|
425
|
+
const hexclaveApp = useHexclaveApp();
|
|
426
426
|
const code = props.searchParams.code;
|
|
427
427
|
const [password, setPassword] = useState("");
|
|
428
428
|
const [passwordRepeat, setPasswordRepeat] = useState("");
|
|
@@ -430,7 +430,7 @@ function getCustomPagePrompts() {
|
|
|
430
430
|
const [failed, setFailed] = useState(false);
|
|
431
431
|
const [formError, setFormError] = useState<string | null>(null);
|
|
432
432
|
|
|
433
|
-
const cachedVerifyPasswordResetCode = cacheFunction(async (app:
|
|
433
|
+
const cachedVerifyPasswordResetCode = cacheFunction(async (app: HexclaveClientApp<true>, codeToVerify: string) => {
|
|
434
434
|
return await app.verifyPasswordResetCode(codeToVerify);
|
|
435
435
|
});
|
|
436
436
|
|
|
@@ -438,7 +438,7 @@ function getCustomPagePrompts() {
|
|
|
438
438
|
return <MessageCard title="Invalid Password Reset Link" />;
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
const verificationResult = use(cachedVerifyPasswordResetCode(
|
|
441
|
+
const verificationResult = use(cachedVerifyPasswordResetCode(hexclaveApp, code));
|
|
442
442
|
if (verificationResult.status === "error") {
|
|
443
443
|
if (KnownErrors.VerificationCodeNotFound.isInstance(verificationResult.error)) return <MessageCard title="Invalid Password Reset Link" />;
|
|
444
444
|
if (KnownErrors.VerificationCodeExpired.isInstance(verificationResult.error)) return <MessageCard title="Expired Password Reset Link" />;
|
|
@@ -459,7 +459,7 @@ function getCustomPagePrompts() {
|
|
|
459
459
|
return;
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
-
const result = await
|
|
462
|
+
const result = await hexclaveApp.resetPassword({ password, code });
|
|
463
463
|
if (result.status === "error") setFailed(true);
|
|
464
464
|
else setDone(true);
|
|
465
465
|
}}>
|
|
@@ -488,13 +488,13 @@ function getCustomPagePrompts() {
|
|
|
488
488
|
- If a user is already signed in, show a signed-in state instead of the reset form.
|
|
489
489
|
- If user is signed out:
|
|
490
490
|
- Render a forgot-password form with email input.
|
|
491
|
-
- Submit calls \`
|
|
491
|
+
- Submit calls \`hexclaveApp.sendForgotPasswordEmail(email)\`.
|
|
492
492
|
- On success, switch to an email-sent confirmation state.
|
|
493
493
|
- Provide a link back to sign-in.
|
|
494
494
|
`,
|
|
495
495
|
reactExample: ___utils_strings_js.deindent`
|
|
496
496
|
export default function CustomForgotPasswordPage() {
|
|
497
|
-
const
|
|
497
|
+
const hexclaveApp = useHexclaveApp();
|
|
498
498
|
const user = useUser({ or: "return-null" });
|
|
499
499
|
const [email, setEmail] = useState("");
|
|
500
500
|
const [sent, setSent] = useState(false);
|
|
@@ -513,7 +513,7 @@ function getCustomPagePrompts() {
|
|
|
513
513
|
<Typography type="h2">Reset Your Password</Typography>
|
|
514
514
|
<Typography>
|
|
515
515
|
{"Don't need to reset? "}
|
|
516
|
-
<a href={
|
|
516
|
+
<a href={hexclaveApp.urls.signIn}>Sign in</a>
|
|
517
517
|
</Typography>
|
|
518
518
|
|
|
519
519
|
<form onSubmit={async (e) => {
|
|
@@ -523,7 +523,7 @@ function getCustomPagePrompts() {
|
|
|
523
523
|
setError("Please enter your email");
|
|
524
524
|
return;
|
|
525
525
|
}
|
|
526
|
-
await
|
|
526
|
+
await hexclaveApp.sendForgotPasswordEmail(email);
|
|
527
527
|
setSent(true);
|
|
528
528
|
}}>
|
|
529
529
|
<Label htmlFor="email">Your Email</Label>
|
|
@@ -545,15 +545,15 @@ function getCustomPagePrompts() {
|
|
|
545
545
|
title: "OAuth Callback",
|
|
546
546
|
minSdkVersion: "0.0.1",
|
|
547
547
|
structure: ___utils_strings_js.deindent`
|
|
548
|
-
- Trigger OAuth callback handling once when the page loads by calling \`
|
|
548
|
+
- Trigger OAuth callback handling once when the page loads by calling \`hexclaveApp.callOAuthCallback()\`.
|
|
549
549
|
- If callback handler already redirected, keep a neutral loading state.
|
|
550
|
-
- If callback handler did not redirect, redirect to sign-in with \`
|
|
550
|
+
- If callback handler did not redirect, redirect to sign-in with \`hexclaveApp.redirectToSignIn({ noRedirectBack: true })\`.
|
|
551
551
|
- If callback processing throws, capture/show a useful error state.
|
|
552
552
|
- Provide a fallback "click here" link in case automatic redirect does not happen.
|
|
553
553
|
`,
|
|
554
554
|
reactExample: ___utils_strings_js.deindent`
|
|
555
555
|
export default function CustomOAuthCallbackPage() {
|
|
556
|
-
const
|
|
556
|
+
const hexclaveApp = useHexclaveApp();
|
|
557
557
|
const called = useRef(false);
|
|
558
558
|
const [error, setError] = useState<unknown>(null);
|
|
559
559
|
const [showRedirectLink, setShowRedirectLink] = useState(false);
|
|
@@ -563,9 +563,9 @@ function getCustomPagePrompts() {
|
|
|
563
563
|
void runAsynchronously(async () => {
|
|
564
564
|
setTimeout(() => setShowRedirectLink(true), 3000);
|
|
565
565
|
try {
|
|
566
|
-
const hasRedirected = await
|
|
566
|
+
const hasRedirected = await hexclaveApp.callOAuthCallback();
|
|
567
567
|
if (!hasRedirected) {
|
|
568
|
-
await
|
|
568
|
+
await hexclaveApp.redirectToSignIn({ noRedirectBack: true });
|
|
569
569
|
}
|
|
570
570
|
} catch (e) {
|
|
571
571
|
setError(e);
|
|
@@ -579,7 +579,7 @@ function getCustomPagePrompts() {
|
|
|
579
579
|
{showRedirectLink ? (
|
|
580
580
|
<Typography>
|
|
581
581
|
{"If you are not redirected automatically, "}
|
|
582
|
-
<a href={
|
|
582
|
+
<a href={hexclaveApp.urls.home}>click here</a>
|
|
583
583
|
</Typography>
|
|
584
584
|
) : null}
|
|
585
585
|
{error ? <pre>{JSON.stringify(error, null, 2)}</pre> : null}
|
|
@@ -601,8 +601,8 @@ function getCustomPagePrompts() {
|
|
|
601
601
|
- Read the magic-link code from URL params.
|
|
602
602
|
- If code is missing, show invalid-link state.
|
|
603
603
|
- If code exists, show a confirmation step:
|
|
604
|
-
- Confirm action calls \`
|
|
605
|
-
- Cancel action calls \`
|
|
604
|
+
- Confirm action calls \`hexclaveApp.signInWithMagicLink(code)\`.
|
|
605
|
+
- Cancel action calls \`hexclaveApp.redirectToHome()\`.
|
|
606
606
|
- Handle callback result:
|
|
607
607
|
- \`VerificationCodeNotFound\` => invalid-link state.
|
|
608
608
|
- \`VerificationCodeExpired\` => expired-link state.
|
|
@@ -612,9 +612,9 @@ function getCustomPagePrompts() {
|
|
|
612
612
|
`,
|
|
613
613
|
reactExample: ___utils_strings_js.deindent`
|
|
614
614
|
export default function CustomMagicLinkCallbackPage(props: { searchParams?: Record<string, string> }) {
|
|
615
|
-
const
|
|
615
|
+
const hexclaveApp = useHexclaveApp();
|
|
616
616
|
const user = useUser({ or: "return-null" });
|
|
617
|
-
const [result, setResult] = useState<Awaited<ReturnType<typeof
|
|
617
|
+
const [result, setResult] = useState<Awaited<ReturnType<typeof hexclaveApp.signInWithMagicLink>> | null>(null);
|
|
618
618
|
const code = props.searchParams?.code;
|
|
619
619
|
|
|
620
620
|
if (user) return <MessageCard title="You are already signed in." />;
|
|
@@ -625,9 +625,9 @@ function getCustomPagePrompts() {
|
|
|
625
625
|
<MessageCard
|
|
626
626
|
title="Do you want to sign in?"
|
|
627
627
|
primaryButtonText="Sign in"
|
|
628
|
-
primaryAction={async () => setResult(await
|
|
628
|
+
primaryAction={async () => setResult(await hexclaveApp.signInWithMagicLink(code))}
|
|
629
629
|
secondaryButtonText="Cancel"
|
|
630
|
-
secondaryAction={async () => await
|
|
630
|
+
secondaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
631
631
|
/>
|
|
632
632
|
);
|
|
633
633
|
}
|
|
@@ -643,7 +643,7 @@ function getCustomPagePrompts() {
|
|
|
643
643
|
<MessageCard
|
|
644
644
|
title="Signed in successfully!"
|
|
645
645
|
primaryButtonText="Go home"
|
|
646
|
-
primaryAction={async () => await
|
|
646
|
+
primaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
647
647
|
/>
|
|
648
648
|
);
|
|
649
649
|
}
|
|
@@ -658,7 +658,7 @@ function getCustomPagePrompts() {
|
|
|
658
658
|
title: "Account Settings",
|
|
659
659
|
minSdkVersion: "0.0.1",
|
|
660
660
|
structure: ___utils_strings_js.deindent`
|
|
661
|
-
- Require an authenticated user (\`useUser({ or: "redirect" })\`) and project config (\`
|
|
661
|
+
- Require an authenticated user (\`useUser({ or: "redirect" })\`) and project config (\`hexclaveApp.useProject()\`).
|
|
662
662
|
- Render top-level pages in this order:
|
|
663
663
|
- **My Profile**
|
|
664
664
|
- **Emails & Auth**
|
|
@@ -791,9 +791,9 @@ function getCustomPagePrompts() {
|
|
|
791
791
|
}
|
|
792
792
|
|
|
793
793
|
function PasswordSection() {
|
|
794
|
-
const
|
|
794
|
+
const hexclaveApp = useHexclaveApp();
|
|
795
795
|
const user = useUser({ or: "redirect" });
|
|
796
|
-
const project =
|
|
796
|
+
const project = hexclaveApp.useProject();
|
|
797
797
|
const [oldPassword, setOldPassword] = useState("");
|
|
798
798
|
const [newPassword, setNewPassword] = useState("");
|
|
799
799
|
const [newPasswordRepeat, setNewPasswordRepeat] = useState("");
|
|
@@ -822,9 +822,9 @@ function getCustomPagePrompts() {
|
|
|
822
822
|
}
|
|
823
823
|
|
|
824
824
|
function PasskeySection() {
|
|
825
|
-
const
|
|
825
|
+
const hexclaveApp = useHexclaveApp();
|
|
826
826
|
const user = useUser({ or: "redirect" });
|
|
827
|
-
const project =
|
|
827
|
+
const project = hexclaveApp.useProject();
|
|
828
828
|
const hasVerifiedAuthEmail = user.useContactChannels().some((x) => x.type === "email" && x.isVerified && x.usedForAuth);
|
|
829
829
|
const isOnlyAuthMethod = user.passkeyAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.otpAuthEnabled;
|
|
830
830
|
|
|
@@ -849,9 +849,9 @@ function getCustomPagePrompts() {
|
|
|
849
849
|
}
|
|
850
850
|
|
|
851
851
|
function OtpSection() {
|
|
852
|
-
const
|
|
852
|
+
const hexclaveApp = useHexclaveApp();
|
|
853
853
|
const user = useUser({ or: "redirect" });
|
|
854
|
-
const project =
|
|
854
|
+
const project = hexclaveApp.useProject();
|
|
855
855
|
const hasVerifiedAuthEmail = user.useContactChannels().some((x) => x.type === "email" && x.isVerified && x.usedForAuth);
|
|
856
856
|
const isOnlyAuthMethod = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;
|
|
857
857
|
|
|
@@ -1000,7 +1000,7 @@ function getCustomPagePrompts() {
|
|
|
1000
1000
|
<Typography>{product.displayName}</Typography>
|
|
1001
1001
|
{product.subscription?.isCancelable ? (
|
|
1002
1002
|
<Button variant="secondary" onClick={async () => {
|
|
1003
|
-
await
|
|
1003
|
+
await useHexclaveApp().cancelSubscription({
|
|
1004
1004
|
...(props.customerType === "team" ? { teamId: props.customer.id } : {}),
|
|
1005
1005
|
productId: product.id ?? "",
|
|
1006
1006
|
subscriptionId: product.subscription?.subscriptionId ?? undefined,
|
|
@@ -1025,8 +1025,8 @@ function getCustomPagePrompts() {
|
|
|
1025
1025
|
|
|
1026
1026
|
function TeamSection(props: { team: { displayName: string } }) {
|
|
1027
1027
|
const user = useUser({ or: "redirect" });
|
|
1028
|
-
const
|
|
1029
|
-
const project =
|
|
1028
|
+
const hexclaveApp = useHexclaveApp();
|
|
1029
|
+
const project = hexclaveApp.useProject();
|
|
1030
1030
|
const team = user.useTeam((props.team as any).id);
|
|
1031
1031
|
|
|
1032
1032
|
if (!team) return null;
|
|
@@ -1109,10 +1109,10 @@ function getCustomPagePrompts() {
|
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
1111
1111
|
function CreateTeamSection() {
|
|
1112
|
-
const
|
|
1112
|
+
const hexclaveApp = useHexclaveApp();
|
|
1113
1113
|
const user = useUser({ or: "redirect" });
|
|
1114
|
-
const project =
|
|
1115
|
-
const navigate =
|
|
1114
|
+
const project = hexclaveApp.useProject();
|
|
1115
|
+
const navigate = hexclaveApp.useNavigate();
|
|
1116
1116
|
const [displayName, setDisplayName] = useState("");
|
|
1117
1117
|
|
|
1118
1118
|
if (!project.config.clientTeamCreationEnabled) {
|
|
@@ -1141,7 +1141,7 @@ function getCustomPagePrompts() {
|
|
|
1141
1141
|
<Button onClick={async () => await user.signOut()}>Sign out</Button>
|
|
1142
1142
|
<Button variant="destructive" onClick={async () => {
|
|
1143
1143
|
await user.delete();
|
|
1144
|
-
await
|
|
1144
|
+
await useHexclaveApp().redirectToHome();
|
|
1145
1145
|
}}>
|
|
1146
1146
|
Delete account
|
|
1147
1147
|
</Button>
|
|
@@ -1150,9 +1150,9 @@ function getCustomPagePrompts() {
|
|
|
1150
1150
|
}
|
|
1151
1151
|
|
|
1152
1152
|
export default function CustomAccountSettingsPage(props: { extraItems?: { id: string, title: string, content: React.ReactNode }[] }) {
|
|
1153
|
-
const
|
|
1153
|
+
const hexclaveApp = useHexclaveApp();
|
|
1154
1154
|
const user = useUser({ or: "redirect" });
|
|
1155
|
-
const project =
|
|
1155
|
+
const project = hexclaveApp.useProject();
|
|
1156
1156
|
const teams = user.useTeams();
|
|
1157
1157
|
const [activeId, setActiveId] = useState("profile");
|
|
1158
1158
|
const [selectedPaymentTeamId, setSelectedPaymentTeamId] = useState<string | null>(null);
|
|
@@ -1251,20 +1251,20 @@ function getCustomPagePrompts() {
|
|
|
1251
1251
|
- Resolve current user with \`includeRestricted: true\`.
|
|
1252
1252
|
- If user is signed out, show a sign-in prompt with cancel path.
|
|
1253
1253
|
- If user is restricted, route user to onboarding first.
|
|
1254
|
-
- Verify invitation code via \`
|
|
1254
|
+
- Verify invitation code via \`hexclaveApp.verifyTeamInvitationCode(code)\`:
|
|
1255
1255
|
- Not found => invalid-link state.
|
|
1256
1256
|
- Expired => expired-link state.
|
|
1257
1257
|
- Already used => used-link state.
|
|
1258
1258
|
- Other errors => throw.
|
|
1259
|
-
- If code is valid, load invitation details via \`
|
|
1259
|
+
- If code is valid, load invitation details via \`hexclaveApp.getTeamInvitationDetails(code)\`.
|
|
1260
1260
|
- Render invitation actions:
|
|
1261
|
-
- Join => \`
|
|
1262
|
-
- Ignore => \`
|
|
1261
|
+
- Join => \`hexclaveApp.acceptTeamInvitation(code)\`.
|
|
1262
|
+
- Ignore => \`hexclaveApp.redirectToHome()\`.
|
|
1263
1263
|
- On successful join, show success state and allow navigation home.
|
|
1264
1264
|
`,
|
|
1265
1265
|
reactExample: ___utils_strings_js.deindent`
|
|
1266
1266
|
export default function CustomTeamInvitationPage(props: { searchParams: Record<string, string> }) {
|
|
1267
|
-
const
|
|
1267
|
+
const hexclaveApp = useHexclaveApp();
|
|
1268
1268
|
const user = useUser({ or: "return-null", includeRestricted: true });
|
|
1269
1269
|
const code = props.searchParams.code;
|
|
1270
1270
|
const [accepted, setAccepted] = useState(false);
|
|
@@ -1278,9 +1278,9 @@ function getCustomPagePrompts() {
|
|
|
1278
1278
|
<MessageCard
|
|
1279
1279
|
title="Team invitation"
|
|
1280
1280
|
primaryButtonText="Sign in"
|
|
1281
|
-
primaryAction={async () => await
|
|
1281
|
+
primaryAction={async () => await hexclaveApp.redirectToSignIn()}
|
|
1282
1282
|
secondaryButtonText="Cancel"
|
|
1283
|
-
secondaryAction={async () => await
|
|
1283
|
+
secondaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
1284
1284
|
/>
|
|
1285
1285
|
);
|
|
1286
1286
|
}
|
|
@@ -1290,7 +1290,7 @@ function getCustomPagePrompts() {
|
|
|
1290
1290
|
<MessageCard
|
|
1291
1291
|
title="Complete your account setup"
|
|
1292
1292
|
primaryButtonText="Complete setup"
|
|
1293
|
-
primaryAction={async () => await
|
|
1293
|
+
primaryAction={async () => await hexclaveApp.redirectToOnboarding()}
|
|
1294
1294
|
/>
|
|
1295
1295
|
);
|
|
1296
1296
|
}
|
|
@@ -1306,7 +1306,7 @@ function getCustomPagePrompts() {
|
|
|
1306
1306
|
title="Team invitation"
|
|
1307
1307
|
primaryButtonText="Check invitation"
|
|
1308
1308
|
primaryAction={async () => {
|
|
1309
|
-
const verification = await
|
|
1309
|
+
const verification = await hexclaveApp.verifyTeamInvitationCode(code);
|
|
1310
1310
|
if (verification.status === "error") {
|
|
1311
1311
|
if (KnownErrors.VerificationCodeNotFound.isInstance(verification.error)) {
|
|
1312
1312
|
setPageError("invalid");
|
|
@@ -1323,7 +1323,7 @@ function getCustomPagePrompts() {
|
|
|
1323
1323
|
throw verification.error;
|
|
1324
1324
|
}
|
|
1325
1325
|
|
|
1326
|
-
const invitationDetails = await
|
|
1326
|
+
const invitationDetails = await hexclaveApp.getTeamInvitationDetails(code);
|
|
1327
1327
|
if (invitationDetails.status === "error") {
|
|
1328
1328
|
setPageError("unknown");
|
|
1329
1329
|
return;
|
|
@@ -1332,7 +1332,7 @@ function getCustomPagePrompts() {
|
|
|
1332
1332
|
setDetails(invitationDetails.data);
|
|
1333
1333
|
}}
|
|
1334
1334
|
secondaryButtonText="Cancel"
|
|
1335
|
-
secondaryAction={async () => await
|
|
1335
|
+
secondaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
1336
1336
|
>
|
|
1337
1337
|
We will verify your invitation before showing the join action.
|
|
1338
1338
|
</MessageCard>
|
|
@@ -1348,12 +1348,12 @@ function getCustomPagePrompts() {
|
|
|
1348
1348
|
title="Team invitation"
|
|
1349
1349
|
primaryButtonText="Join"
|
|
1350
1350
|
primaryAction={async () => {
|
|
1351
|
-
const result = await
|
|
1351
|
+
const result = await hexclaveApp.acceptTeamInvitation(code);
|
|
1352
1352
|
if (result.status === "ok") setAccepted(true);
|
|
1353
1353
|
else setPageError("unknown");
|
|
1354
1354
|
}}
|
|
1355
1355
|
secondaryButtonText="Ignore"
|
|
1356
|
-
secondaryAction={async () => await
|
|
1356
|
+
secondaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
1357
1357
|
>
|
|
1358
1358
|
You are invited to join {details.teamDisplayName}
|
|
1359
1359
|
</MessageCard>
|
|
@@ -1425,7 +1425,7 @@ function getCustomPagePrompts() {
|
|
|
1425
1425
|
structure: ___utils_strings_js.deindent`
|
|
1426
1426
|
- Read the MFA attempt code from session storage.
|
|
1427
1427
|
- Render OTP input for the one-time code.
|
|
1428
|
-
- When OTP is complete, submit \`
|
|
1428
|
+
- When OTP is complete, submit \`hexclaveApp.signInWithMfa(otp, attemptCode, { noRedirect: true })\`.
|
|
1429
1429
|
- Handle result:
|
|
1430
1430
|
- Success => clear stored attempt code, show success state, then redirect after sign-in.
|
|
1431
1431
|
- \`InvalidTotpCode\` => show invalid-code error and allow retry.
|
|
@@ -1447,7 +1447,7 @@ function getCustomPagePrompts() {
|
|
|
1447
1447
|
}
|
|
1448
1448
|
|
|
1449
1449
|
export default function CustomMfaPage() {
|
|
1450
|
-
const
|
|
1450
|
+
const hexclaveApp = useHexclaveApp();
|
|
1451
1451
|
const [otp, setOtp] = useState("");
|
|
1452
1452
|
const [submitting, setSubmitting] = useState(false);
|
|
1453
1453
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -1461,13 +1461,13 @@ function getCustomPagePrompts() {
|
|
|
1461
1461
|
if (!attemptCode || otp.length !== 6 || submitting) return;
|
|
1462
1462
|
setSubmitting(true);
|
|
1463
1463
|
setError(null);
|
|
1464
|
-
const result = await
|
|
1464
|
+
const result = await hexclaveApp.signInWithMfa(otp, attemptCode, { noRedirect: true });
|
|
1465
1465
|
if (result.status === "ok") {
|
|
1466
1466
|
// Hexclave rebrand: remove both the new and legacy MFA attempt code keys.
|
|
1467
1467
|
window.sessionStorage.removeItem("hexclave_mfa_attempt_code");
|
|
1468
1468
|
window.sessionStorage.removeItem("stack_mfa_attempt_code");
|
|
1469
1469
|
setVerified(true);
|
|
1470
|
-
await
|
|
1470
|
+
await hexclaveApp.redirectToAfterSignIn();
|
|
1471
1471
|
} else if (KnownErrors.InvalidTotpCode.isInstance(result.error)) {
|
|
1472
1472
|
setError("Invalid TOTP code");
|
|
1473
1473
|
setOtp("");
|
|
@@ -1519,7 +1519,7 @@ function getCustomPagePrompts() {
|
|
|
1519
1519
|
`,
|
|
1520
1520
|
reactExample: ___utils_strings_js.deindent`
|
|
1521
1521
|
export default function CustomErrorPage(props: { searchParams: Record<string, string> }) {
|
|
1522
|
-
const
|
|
1522
|
+
const hexclaveApp = useHexclaveApp();
|
|
1523
1523
|
const errorCode = props.searchParams.errorCode;
|
|
1524
1524
|
const message = props.searchParams.message;
|
|
1525
1525
|
const details = props.searchParams.details;
|
|
@@ -1540,11 +1540,11 @@ function getCustomPagePrompts() {
|
|
|
1540
1540
|
}
|
|
1541
1541
|
|
|
1542
1542
|
if (KnownErrors.OAuthConnectionAlreadyConnectedToAnotherUser.isInstance(error)) {
|
|
1543
|
-
return <MessageCard title="Failed to connect account" primaryButtonText="Go Home" primaryAction={() =>
|
|
1543
|
+
return <MessageCard title="Failed to connect account" primaryButtonText="Go Home" primaryAction={() => hexclaveApp.redirectToHome()} />;
|
|
1544
1544
|
}
|
|
1545
1545
|
|
|
1546
1546
|
if (KnownErrors.UserAlreadyConnectedToAnotherOAuthConnection.isInstance(error)) {
|
|
1547
|
-
return <MessageCard title="Failed to connect account" primaryButtonText="Go Home" primaryAction={() =>
|
|
1547
|
+
return <MessageCard title="Failed to connect account" primaryButtonText="Go Home" primaryAction={() => hexclaveApp.redirectToHome()} />;
|
|
1548
1548
|
}
|
|
1549
1549
|
|
|
1550
1550
|
if (KnownErrors.OAuthProviderAccessDenied.isInstance(error)) {
|
|
@@ -1552,9 +1552,9 @@ function getCustomPagePrompts() {
|
|
|
1552
1552
|
<MessageCard
|
|
1553
1553
|
title="OAuth provider access denied"
|
|
1554
1554
|
primaryButtonText="Sign in again"
|
|
1555
|
-
primaryAction={() =>
|
|
1555
|
+
primaryAction={() => hexclaveApp.redirectToSignIn()}
|
|
1556
1556
|
secondaryButtonText="Go Home"
|
|
1557
|
-
secondaryAction={() =>
|
|
1557
|
+
secondaryAction={() => hexclaveApp.redirectToHome()}
|
|
1558
1558
|
/>
|
|
1559
1559
|
);
|
|
1560
1560
|
}
|
|
@@ -1574,8 +1574,8 @@ function getCustomPagePrompts() {
|
|
|
1574
1574
|
structure: ___utils_strings_js.deindent`
|
|
1575
1575
|
- Resolve user with \`useUser({ or: "return-null", includeRestricted: true })\`.
|
|
1576
1576
|
- Route by user state:
|
|
1577
|
-
- Restricted user resolved to unrestricted => redirect to \`
|
|
1578
|
-
- Missing/anonymous user => redirect to \`
|
|
1577
|
+
- Restricted user resolved to unrestricted => redirect to \`hexclaveApp.redirectToAfterSignIn()\`.
|
|
1578
|
+
- Missing/anonymous user => redirect to \`hexclaveApp.redirectToSignIn()\`.
|
|
1579
1579
|
- Restricted user => continue onboarding flow.
|
|
1580
1580
|
- Handle restricted reasons:
|
|
1581
1581
|
- \`email_not_verified\` and no primary email => ask user for email and call \`user.update({ primaryEmail })\`.
|
|
@@ -1585,18 +1585,18 @@ function getCustomPagePrompts() {
|
|
|
1585
1585
|
`,
|
|
1586
1586
|
reactExample: ___utils_strings_js.deindent`
|
|
1587
1587
|
export default function CustomOnboardingPage() {
|
|
1588
|
-
const
|
|
1588
|
+
const hexclaveApp = useHexclaveApp();
|
|
1589
1589
|
const user = useUser({ or: "return-null", includeRestricted: true });
|
|
1590
1590
|
const [email, setEmail] = useState("");
|
|
1591
1591
|
const [changeEmail, setChangeEmail] = useState(false);
|
|
1592
1592
|
|
|
1593
1593
|
if (user && !user.isRestricted) {
|
|
1594
|
-
void runAsynchronously(
|
|
1594
|
+
void runAsynchronously(hexclaveApp.redirectToAfterSignIn());
|
|
1595
1595
|
return null;
|
|
1596
1596
|
}
|
|
1597
1597
|
|
|
1598
1598
|
if (!user || user.isAnonymous) {
|
|
1599
|
-
void runAsynchronously(
|
|
1599
|
+
void runAsynchronously(hexclaveApp.redirectToSignIn());
|
|
1600
1600
|
return null;
|
|
1601
1601
|
}
|
|
1602
1602
|
|