@hexclave/shared 1.0.3 → 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 +216 -29
- 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 +71 -71
- 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 +215 -30
- 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 +71 -71
- 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 +4 -4
- 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 +13 -13
- package/dist/esm/interface/crud/current-user.d.ts +9 -9
- package/dist/esm/interface/crud/email-outbox.d.ts +112 -112
- package/dist/esm/interface/crud/products.d.ts +1 -1
- package/dist/esm/interface/crud/project-api-keys.d.ts +2 -2
- 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 +28 -28
- package/dist/esm/interface/crud/team-member-profiles.d.ts +22 -22
- 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/users.d.ts +14 -14
- 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 +4 -4
- package/dist/esm/known-errors.js +4 -4
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.d.ts +15 -5
- 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/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 +4 -4
- 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 +13 -13
- package/dist/interface/crud/current-user.d.ts +9 -9
- package/dist/interface/crud/email-outbox.d.ts +112 -112
- package/dist/interface/crud/products.d.ts +1 -1
- package/dist/interface/crud/project-api-keys.d.ts +2 -2
- 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 +28 -28
- package/dist/interface/crud/team-member-profiles.d.ts +22 -22
- package/dist/interface/crud/team-permissions.js +5 -5
- package/dist/interface/crud/team-permissions.js.map +1 -1
- package/dist/interface/crud/users.d.ts +14 -14
- 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 +4 -4
- package/dist/known-errors.js +4 -4
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.ts +15 -5
- 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/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
|
@@ -20,11 +20,11 @@ function createCustomPagePrompt(options) {
|
|
|
20
20
|
const latestPageVersion = Math.max(1, ...Object.keys(options.versions).map(Number));
|
|
21
21
|
const latestSdkVersion = latestPageVersion === 1 ? options.minSdkVersion : options.versions[latestPageVersion].minSdkVersion;
|
|
22
22
|
const fullPrompt = deindent`
|
|
23
|
-
This prompt explains how to implement a custom ${options.title} page for
|
|
23
|
+
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.
|
|
24
24
|
|
|
25
|
-
First, make sure to upgrade the
|
|
25
|
+
First, make sure to upgrade the Hexclave SDK to a recent version. The minimum supported SDK version for this walkthrough is v${latestSdkVersion}.
|
|
26
26
|
|
|
27
|
-
The user's codebase may already have a ${options.title} page that could be suitable (eg. from an earlier version of
|
|
27
|
+
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.
|
|
28
28
|
|
|
29
29
|
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.
|
|
30
30
|
|
|
@@ -41,10 +41,10 @@ function createCustomPagePrompt(options) {
|
|
|
41
41
|
${options.reactExample}
|
|
42
42
|
\`\`\`
|
|
43
43
|
|
|
44
|
-
When you're done, please update the file where the
|
|
44
|
+
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:
|
|
45
45
|
|
|
46
46
|
\`\`\`tsx
|
|
47
|
-
export const
|
|
47
|
+
export const hexclaveServerApp = new HexclaveServerApp({
|
|
48
48
|
tokenStore: "nextjs-cookie",
|
|
49
49
|
urls: {
|
|
50
50
|
default: {
|
|
@@ -87,49 +87,49 @@ function createAuthPagePrompt(type) {
|
|
|
87
87
|
const authVerb = isSignIn ? "sign in" : "sign up";
|
|
88
88
|
const authVerbCapitalized = isSignIn ? "Sign in" : "Sign up";
|
|
89
89
|
const otherAuthVerb = isSignIn ? "sign up" : "sign in";
|
|
90
|
-
const credentialMethodCall = isSignIn ? "
|
|
90
|
+
const credentialMethodCall = isSignIn ? "hexclaveApp.signInWithCredential({ email: form.email, password: form.password })" : "hexclaveApp.signUpWithCredential({ email: form.email, password: form.password })";
|
|
91
91
|
return createCustomPagePrompt({
|
|
92
92
|
key: type,
|
|
93
93
|
title,
|
|
94
94
|
minSdkVersion: "0.0.1",
|
|
95
95
|
structure: deindent`
|
|
96
|
-
- If user is already signed in, regardless of whether restricted or not (ie. \`await
|
|
97
|
-
- If user is restricted, \`await
|
|
98
|
-
- Otherwise, \`await
|
|
99
|
-
- 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
|
|
96
|
+
- If user is already signed in, regardless of whether restricted or not (ie. \`await hexclaveApp.getUser({ includeRestricted: true }) !== null\`):
|
|
97
|
+
- If user is restricted, \`await hexclaveApp.redirectToOnboarding({ replace: true })\`
|
|
98
|
+
- Otherwise, \`await hexclaveApp.redirectToAfterSign${isSignIn ? "In" : "Up"}({ replace: true })\`
|
|
99
|
+
- 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.
|
|
100
100
|
- If user is not signed in:
|
|
101
|
-
${isSignIn ? "- If sign-ups are enabled (\\`project = await
|
|
101
|
+
${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."}
|
|
102
102
|
- Show a ${authVerb} screen. The auth methods that should render:
|
|
103
|
-
- For each OAuth provider (\`project.config.oauthProviders: { readonly id: string }[]\`), render an OAuth button. Clicking the button calls \`await
|
|
104
|
-
${isSignIn ? "- If \\`project.config.passkeyEnabled\\`, render a passkey button. Clicking the button calls \\`await
|
|
103
|
+
- For each OAuth provider (\`project.config.oauthProviders: { readonly id: string }[]\`), render an OAuth button. Clicking the button calls \`await hexclaveApp.signInWithOAuth("<providerId>")\`.
|
|
104
|
+
${isSignIn ? "- If \\`project.config.passkeyEnabled\\`, render a passkey button. Clicking the button calls \\`await hexclaveApp.signInWithPasskey()\\`." : ""}
|
|
105
105
|
- If \`project.config.credentialEnabled\`, render a credential ${authVerb} form:
|
|
106
106
|
- Email + password${isSignIn ? "" : " + repeat password"}
|
|
107
107
|
${isSignIn ? "" : "- Validate password strength with \\`getPasswordError()\\` and ensure repeated password matches"}
|
|
108
|
-
${isSignIn ? "- \"Forgot password?\" link calling \\`await
|
|
108
|
+
${isSignIn ? "- \"Forgot password?\" link calling \\`await hexclaveApp.redirectToForgotPassword()\\`" : ""}
|
|
109
109
|
- Submit calls \`${credentialMethodCall}: ${isSignIn ? "Promise<Result<undefined, KnownErrors[\"EmailPasswordMismatch\"] | KnownErrors[\"InvalidTotpCode\"]>>" : "Promise<Result<undefined, KnownErrors[\"UserWithEmailAlreadyExists\"] | KnownErrors[\"PasswordRequirementsNotMet\"] | KnownErrors[\"BotChallengeFailed\"]>>"}\`
|
|
110
110
|
- On error, display the error message on the email field
|
|
111
111
|
- If \`project.config.magicLinkEnabled\`, render a magic link form:
|
|
112
112
|
- Email input (validated to be a correct email address) + "Send email" button
|
|
113
|
-
- Calls \`
|
|
113
|
+
- Calls \`hexclaveApp.sendMagicLinkEmail(email): Promise<Result<{ nonce: string }, KnownErrors["RedirectUrlNotWhitelisted"] | KnownErrors["BotChallengeFailed"]>>\`
|
|
114
114
|
- After sending, switch to a 6-digit OTP input. User enters the code from their email
|
|
115
|
-
- Submit the OTP + nonce via \`
|
|
115
|
+
- Submit the OTP + nonce via \`hexclaveApp.signInWithMagicLink(otp + nonce): Promise<Result<undefined, KnownErrors["VerificationCodeError"] | KnownErrors["InvalidTotpCode"]>>\` (string concatenation)
|
|
116
116
|
- If both credential and magic-link are enabled, allow the user to choose which flow to use.
|
|
117
117
|
- If none of the above auth methods are enabled, show a message explaining that no authentication methods are enabled.
|
|
118
|
-
- Show a link to the ${otherAuthVerb} page that calls \`await
|
|
118
|
+
- Show a link to the ${otherAuthVerb} page that calls \`await hexclaveApp.redirectTo${isSignIn ? "SignUp" : "SignIn"}()\`.
|
|
119
119
|
`,
|
|
120
120
|
reactExample: deindent`
|
|
121
121
|
export default function Custom${isSignIn ? "SignIn" : "SignUp"}Page() {
|
|
122
|
-
const
|
|
122
|
+
const hexclaveApp = useHexclaveApp();
|
|
123
123
|
const user = useUser({ includeRestricted: true });
|
|
124
|
-
const project =
|
|
124
|
+
const project = hexclaveApp.useProject();
|
|
125
125
|
const [otpState, setOtpState] = useState<null | { nonce: string }>(null);
|
|
126
126
|
|
|
127
127
|
useEffect(() => {
|
|
128
128
|
if (user) {
|
|
129
129
|
if (user.isRestricted) {
|
|
130
|
-
void
|
|
130
|
+
void hexclaveApp.redirectToOnboarding();
|
|
131
131
|
} else {
|
|
132
|
-
void
|
|
132
|
+
void hexclaveApp.redirectToAfterSign${isSignIn ? "In" : "Up"}();
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
}, [user]);
|
|
@@ -138,8 +138,8 @@ function createAuthPagePrompt(type) {
|
|
|
138
138
|
return (
|
|
139
139
|
<div>
|
|
140
140
|
<Typography>You are already signed in.</Typography>
|
|
141
|
-
<Button onClick={async () => await
|
|
142
|
-
<Button onClick={async () => await
|
|
141
|
+
<Button onClick={async () => await hexclaveApp.redirectToSignOut()}>Sign out</Button>
|
|
142
|
+
<Button onClick={async () => await hexclaveApp.redirectToHome()}>Go home</Button>
|
|
143
143
|
</div>
|
|
144
144
|
);
|
|
145
145
|
}
|
|
@@ -152,7 +152,7 @@ function createAuthPagePrompt(type) {
|
|
|
152
152
|
if (otpState) {
|
|
153
153
|
return (
|
|
154
154
|
<Form onSubmit={async (form) => {
|
|
155
|
-
const result = await
|
|
155
|
+
const result = await hexclaveApp.signInWithMagicLink(form.otp + otpState.nonce);
|
|
156
156
|
if (result.status === "error") handleErrorNicely(...);
|
|
157
157
|
}}>
|
|
158
158
|
<Typography>Enter the code from your email</Typography>
|
|
@@ -178,10 +178,10 @@ function createAuthPagePrompt(type) {
|
|
|
178
178
|
<Typography>
|
|
179
179
|
{"Don't have an account? "}
|
|
180
180
|
<a
|
|
181
|
-
href={
|
|
181
|
+
href={hexclaveApp.urls.signUp}
|
|
182
182
|
onClick={async (e) => {
|
|
183
183
|
e.preventDefault();
|
|
184
|
-
await
|
|
184
|
+
await hexclaveApp.redirectToSignUp();
|
|
185
185
|
}}
|
|
186
186
|
>
|
|
187
187
|
Sign up
|
|
@@ -191,10 +191,10 @@ function createAuthPagePrompt(type) {
|
|
|
191
191
|
}` : `<Typography>
|
|
192
192
|
{"Already have an account? "}
|
|
193
193
|
<a
|
|
194
|
-
href={
|
|
194
|
+
href={hexclaveApp.urls.signIn}
|
|
195
195
|
onClick={async (e) => {
|
|
196
196
|
e.preventDefault();
|
|
197
|
-
await
|
|
197
|
+
await hexclaveApp.redirectToSignIn();
|
|
198
198
|
}}
|
|
199
199
|
>
|
|
200
200
|
Sign in
|
|
@@ -207,14 +207,14 @@ function createAuthPagePrompt(type) {
|
|
|
207
207
|
<Button
|
|
208
208
|
key={provider.id}
|
|
209
209
|
onClick={async () => {
|
|
210
|
-
await
|
|
210
|
+
await hexclaveApp.signInWithOAuth(provider.id);
|
|
211
211
|
}}
|
|
212
212
|
>
|
|
213
213
|
${authVerbCapitalized} with {provider.id}
|
|
214
214
|
</Button>
|
|
215
215
|
))}
|
|
216
216
|
${isSignIn ? `{hasPasskey && (
|
|
217
|
-
<Button onClick={async () => await
|
|
217
|
+
<Button onClick={async () => await hexclaveApp.signInWithPasskey()}>
|
|
218
218
|
Sign in with passkey
|
|
219
219
|
</Button>
|
|
220
220
|
)}` : ""}
|
|
@@ -235,7 +235,7 @@ function createAuthPagePrompt(type) {
|
|
|
235
235
|
</TabsList>
|
|
236
236
|
{hasMagicLink && <TabsContent value="magic-link">
|
|
237
237
|
<Form onSubmit={async (form) => {
|
|
238
|
-
const result = await
|
|
238
|
+
const result = await hexclaveApp.sendMagicLinkEmail(form.email);
|
|
239
239
|
if (result.status === "error") handleErrorNicely(...);
|
|
240
240
|
else setOtpState({ nonce: result.data.nonce });
|
|
241
241
|
}}>
|
|
@@ -260,7 +260,7 @@ function createAuthPagePrompt(type) {
|
|
|
260
260
|
<Label htmlFor="password">Password</Label>
|
|
261
261
|
<PasswordInput id="password" />
|
|
262
262
|
|
|
263
|
-
${isSignIn ? `<Button type="button" variant="link" onClick={async () => await
|
|
263
|
+
${isSignIn ? `<Button type="button" variant="link" onClick={async () => await hexclaveApp.redirectToForgotPassword()}>
|
|
264
264
|
Forgot password?
|
|
265
265
|
</Button>` : `<Label htmlFor="password-repeat">Repeat password</Label>
|
|
266
266
|
<PasswordInput id="password-repeat" />`}
|
|
@@ -306,7 +306,7 @@ function getCustomPagePrompts() {
|
|
|
306
306
|
|
|
307
307
|
export default function CustomSignOutPage() {
|
|
308
308
|
const user = useUser({ or: "return-null" });
|
|
309
|
-
const
|
|
309
|
+
const hexclaveApp = useHexclaveApp();
|
|
310
310
|
|
|
311
311
|
if (user) {
|
|
312
312
|
use(cacheSignOut(user));
|
|
@@ -317,7 +317,7 @@ function getCustomPagePrompts() {
|
|
|
317
317
|
title="Signed out"
|
|
318
318
|
primaryButtonText="Go home"
|
|
319
319
|
primaryAction={async () => {
|
|
320
|
-
await
|
|
320
|
+
await hexclaveApp.redirectToHome();
|
|
321
321
|
}}
|
|
322
322
|
>
|
|
323
323
|
You have been signed out successfully.
|
|
@@ -338,8 +338,8 @@ function getCustomPagePrompts() {
|
|
|
338
338
|
- Read the verification code from URL params.
|
|
339
339
|
- If the code is missing, show an invalid-link state.
|
|
340
340
|
- If the code exists, show a confirmation step:
|
|
341
|
-
- Verify action calls \`
|
|
342
|
-
- Cancel action calls \`
|
|
341
|
+
- Verify action calls \`hexclaveApp.verifyEmail(code)\`.
|
|
342
|
+
- Cancel action calls \`hexclaveApp.redirectToHome()\`.
|
|
343
343
|
- Handle verification result:
|
|
344
344
|
- \`VerificationCodeNotFound\` => invalid-link state.
|
|
345
345
|
- \`VerificationCodeExpired\` => expired-link state.
|
|
@@ -349,8 +349,8 @@ function getCustomPagePrompts() {
|
|
|
349
349
|
`,
|
|
350
350
|
reactExample: deindent`
|
|
351
351
|
export default function CustomEmailVerificationPage(props: { searchParams?: Record<string, string> }) {
|
|
352
|
-
const
|
|
353
|
-
const [result, setResult] = useState<Awaited<ReturnType<typeof
|
|
352
|
+
const hexclaveApp = useHexclaveApp();
|
|
353
|
+
const [result, setResult] = useState<Awaited<ReturnType<typeof hexclaveApp.verifyEmail>> | null>(null);
|
|
354
354
|
const code = props.searchParams?.code;
|
|
355
355
|
|
|
356
356
|
if (!code) {
|
|
@@ -363,11 +363,11 @@ function getCustomPagePrompts() {
|
|
|
363
363
|
title="Do you want to verify your email?"
|
|
364
364
|
primaryButtonText="Verify"
|
|
365
365
|
primaryAction={async () => {
|
|
366
|
-
setResult(await
|
|
366
|
+
setResult(await hexclaveApp.verifyEmail(code));
|
|
367
367
|
}}
|
|
368
368
|
secondaryButtonText="Cancel"
|
|
369
369
|
secondaryAction={async () => {
|
|
370
|
-
await
|
|
370
|
+
await hexclaveApp.redirectToHome();
|
|
371
371
|
}}
|
|
372
372
|
/>
|
|
373
373
|
);
|
|
@@ -388,7 +388,7 @@ function getCustomPagePrompts() {
|
|
|
388
388
|
title="Your email has been verified!"
|
|
389
389
|
primaryButtonText="Go home"
|
|
390
390
|
primaryAction={async () => {
|
|
391
|
-
await
|
|
391
|
+
await hexclaveApp.redirectToHome();
|
|
392
392
|
}}
|
|
393
393
|
/>
|
|
394
394
|
);
|
|
@@ -406,7 +406,7 @@ function getCustomPagePrompts() {
|
|
|
406
406
|
structure: deindent`
|
|
407
407
|
- Read the reset code from URL params.
|
|
408
408
|
- If code is missing, show an invalid-link state.
|
|
409
|
-
- Before rendering the form, verify the code via \`
|
|
409
|
+
- Before rendering the form, verify the code via \`hexclaveApp.verifyPasswordResetCode(code)\`.
|
|
410
410
|
- \`VerificationCodeNotFound\` => invalid-link state.
|
|
411
411
|
- \`VerificationCodeExpired\` => expired-link state.
|
|
412
412
|
- \`VerificationCodeAlreadyUsed\` => used-link state.
|
|
@@ -414,13 +414,13 @@ function getCustomPagePrompts() {
|
|
|
414
414
|
- If code is valid, render reset form:
|
|
415
415
|
- New password + repeated password.
|
|
416
416
|
- Validate password strength and ensure repeated password matches.
|
|
417
|
-
- Submit calls \`
|
|
417
|
+
- Submit calls \`hexclaveApp.resetPassword({ password, code })\`.
|
|
418
418
|
- If reset succeeds, show success state.
|
|
419
419
|
- If reset fails, show error state with guidance to request a new link.
|
|
420
420
|
`,
|
|
421
421
|
reactExample: deindent`
|
|
422
422
|
export default function CustomPasswordResetPage(props: { searchParams: Record<string, string> }) {
|
|
423
|
-
const
|
|
423
|
+
const hexclaveApp = useHexclaveApp();
|
|
424
424
|
const code = props.searchParams.code;
|
|
425
425
|
const [password, setPassword] = useState("");
|
|
426
426
|
const [passwordRepeat, setPasswordRepeat] = useState("");
|
|
@@ -428,7 +428,7 @@ function getCustomPagePrompts() {
|
|
|
428
428
|
const [failed, setFailed] = useState(false);
|
|
429
429
|
const [formError, setFormError] = useState<string | null>(null);
|
|
430
430
|
|
|
431
|
-
const cachedVerifyPasswordResetCode = cacheFunction(async (app:
|
|
431
|
+
const cachedVerifyPasswordResetCode = cacheFunction(async (app: HexclaveClientApp<true>, codeToVerify: string) => {
|
|
432
432
|
return await app.verifyPasswordResetCode(codeToVerify);
|
|
433
433
|
});
|
|
434
434
|
|
|
@@ -436,7 +436,7 @@ function getCustomPagePrompts() {
|
|
|
436
436
|
return <MessageCard title="Invalid Password Reset Link" />;
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
const verificationResult = use(cachedVerifyPasswordResetCode(
|
|
439
|
+
const verificationResult = use(cachedVerifyPasswordResetCode(hexclaveApp, code));
|
|
440
440
|
if (verificationResult.status === "error") {
|
|
441
441
|
if (KnownErrors.VerificationCodeNotFound.isInstance(verificationResult.error)) return <MessageCard title="Invalid Password Reset Link" />;
|
|
442
442
|
if (KnownErrors.VerificationCodeExpired.isInstance(verificationResult.error)) return <MessageCard title="Expired Password Reset Link" />;
|
|
@@ -457,7 +457,7 @@ function getCustomPagePrompts() {
|
|
|
457
457
|
return;
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
-
const result = await
|
|
460
|
+
const result = await hexclaveApp.resetPassword({ password, code });
|
|
461
461
|
if (result.status === "error") setFailed(true);
|
|
462
462
|
else setDone(true);
|
|
463
463
|
}}>
|
|
@@ -486,13 +486,13 @@ function getCustomPagePrompts() {
|
|
|
486
486
|
- If a user is already signed in, show a signed-in state instead of the reset form.
|
|
487
487
|
- If user is signed out:
|
|
488
488
|
- Render a forgot-password form with email input.
|
|
489
|
-
- Submit calls \`
|
|
489
|
+
- Submit calls \`hexclaveApp.sendForgotPasswordEmail(email)\`.
|
|
490
490
|
- On success, switch to an email-sent confirmation state.
|
|
491
491
|
- Provide a link back to sign-in.
|
|
492
492
|
`,
|
|
493
493
|
reactExample: deindent`
|
|
494
494
|
export default function CustomForgotPasswordPage() {
|
|
495
|
-
const
|
|
495
|
+
const hexclaveApp = useHexclaveApp();
|
|
496
496
|
const user = useUser({ or: "return-null" });
|
|
497
497
|
const [email, setEmail] = useState("");
|
|
498
498
|
const [sent, setSent] = useState(false);
|
|
@@ -511,7 +511,7 @@ function getCustomPagePrompts() {
|
|
|
511
511
|
<Typography type="h2">Reset Your Password</Typography>
|
|
512
512
|
<Typography>
|
|
513
513
|
{"Don't need to reset? "}
|
|
514
|
-
<a href={
|
|
514
|
+
<a href={hexclaveApp.urls.signIn}>Sign in</a>
|
|
515
515
|
</Typography>
|
|
516
516
|
|
|
517
517
|
<form onSubmit={async (e) => {
|
|
@@ -521,7 +521,7 @@ function getCustomPagePrompts() {
|
|
|
521
521
|
setError("Please enter your email");
|
|
522
522
|
return;
|
|
523
523
|
}
|
|
524
|
-
await
|
|
524
|
+
await hexclaveApp.sendForgotPasswordEmail(email);
|
|
525
525
|
setSent(true);
|
|
526
526
|
}}>
|
|
527
527
|
<Label htmlFor="email">Your Email</Label>
|
|
@@ -543,15 +543,15 @@ function getCustomPagePrompts() {
|
|
|
543
543
|
title: "OAuth Callback",
|
|
544
544
|
minSdkVersion: "0.0.1",
|
|
545
545
|
structure: deindent`
|
|
546
|
-
- Trigger OAuth callback handling once when the page loads by calling \`
|
|
546
|
+
- Trigger OAuth callback handling once when the page loads by calling \`hexclaveApp.callOAuthCallback()\`.
|
|
547
547
|
- If callback handler already redirected, keep a neutral loading state.
|
|
548
|
-
- If callback handler did not redirect, redirect to sign-in with \`
|
|
548
|
+
- If callback handler did not redirect, redirect to sign-in with \`hexclaveApp.redirectToSignIn({ noRedirectBack: true })\`.
|
|
549
549
|
- If callback processing throws, capture/show a useful error state.
|
|
550
550
|
- Provide a fallback "click here" link in case automatic redirect does not happen.
|
|
551
551
|
`,
|
|
552
552
|
reactExample: deindent`
|
|
553
553
|
export default function CustomOAuthCallbackPage() {
|
|
554
|
-
const
|
|
554
|
+
const hexclaveApp = useHexclaveApp();
|
|
555
555
|
const called = useRef(false);
|
|
556
556
|
const [error, setError] = useState<unknown>(null);
|
|
557
557
|
const [showRedirectLink, setShowRedirectLink] = useState(false);
|
|
@@ -561,9 +561,9 @@ function getCustomPagePrompts() {
|
|
|
561
561
|
void runAsynchronously(async () => {
|
|
562
562
|
setTimeout(() => setShowRedirectLink(true), 3000);
|
|
563
563
|
try {
|
|
564
|
-
const hasRedirected = await
|
|
564
|
+
const hasRedirected = await hexclaveApp.callOAuthCallback();
|
|
565
565
|
if (!hasRedirected) {
|
|
566
|
-
await
|
|
566
|
+
await hexclaveApp.redirectToSignIn({ noRedirectBack: true });
|
|
567
567
|
}
|
|
568
568
|
} catch (e) {
|
|
569
569
|
setError(e);
|
|
@@ -577,7 +577,7 @@ function getCustomPagePrompts() {
|
|
|
577
577
|
{showRedirectLink ? (
|
|
578
578
|
<Typography>
|
|
579
579
|
{"If you are not redirected automatically, "}
|
|
580
|
-
<a href={
|
|
580
|
+
<a href={hexclaveApp.urls.home}>click here</a>
|
|
581
581
|
</Typography>
|
|
582
582
|
) : null}
|
|
583
583
|
{error ? <pre>{JSON.stringify(error, null, 2)}</pre> : null}
|
|
@@ -599,8 +599,8 @@ function getCustomPagePrompts() {
|
|
|
599
599
|
- Read the magic-link code from URL params.
|
|
600
600
|
- If code is missing, show invalid-link state.
|
|
601
601
|
- If code exists, show a confirmation step:
|
|
602
|
-
- Confirm action calls \`
|
|
603
|
-
- Cancel action calls \`
|
|
602
|
+
- Confirm action calls \`hexclaveApp.signInWithMagicLink(code)\`.
|
|
603
|
+
- Cancel action calls \`hexclaveApp.redirectToHome()\`.
|
|
604
604
|
- Handle callback result:
|
|
605
605
|
- \`VerificationCodeNotFound\` => invalid-link state.
|
|
606
606
|
- \`VerificationCodeExpired\` => expired-link state.
|
|
@@ -610,9 +610,9 @@ function getCustomPagePrompts() {
|
|
|
610
610
|
`,
|
|
611
611
|
reactExample: deindent`
|
|
612
612
|
export default function CustomMagicLinkCallbackPage(props: { searchParams?: Record<string, string> }) {
|
|
613
|
-
const
|
|
613
|
+
const hexclaveApp = useHexclaveApp();
|
|
614
614
|
const user = useUser({ or: "return-null" });
|
|
615
|
-
const [result, setResult] = useState<Awaited<ReturnType<typeof
|
|
615
|
+
const [result, setResult] = useState<Awaited<ReturnType<typeof hexclaveApp.signInWithMagicLink>> | null>(null);
|
|
616
616
|
const code = props.searchParams?.code;
|
|
617
617
|
|
|
618
618
|
if (user) return <MessageCard title="You are already signed in." />;
|
|
@@ -623,9 +623,9 @@ function getCustomPagePrompts() {
|
|
|
623
623
|
<MessageCard
|
|
624
624
|
title="Do you want to sign in?"
|
|
625
625
|
primaryButtonText="Sign in"
|
|
626
|
-
primaryAction={async () => setResult(await
|
|
626
|
+
primaryAction={async () => setResult(await hexclaveApp.signInWithMagicLink(code))}
|
|
627
627
|
secondaryButtonText="Cancel"
|
|
628
|
-
secondaryAction={async () => await
|
|
628
|
+
secondaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
629
629
|
/>
|
|
630
630
|
);
|
|
631
631
|
}
|
|
@@ -641,7 +641,7 @@ function getCustomPagePrompts() {
|
|
|
641
641
|
<MessageCard
|
|
642
642
|
title="Signed in successfully!"
|
|
643
643
|
primaryButtonText="Go home"
|
|
644
|
-
primaryAction={async () => await
|
|
644
|
+
primaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
645
645
|
/>
|
|
646
646
|
);
|
|
647
647
|
}
|
|
@@ -656,7 +656,7 @@ function getCustomPagePrompts() {
|
|
|
656
656
|
title: "Account Settings",
|
|
657
657
|
minSdkVersion: "0.0.1",
|
|
658
658
|
structure: deindent`
|
|
659
|
-
- Require an authenticated user (\`useUser({ or: "redirect" })\`) and project config (\`
|
|
659
|
+
- Require an authenticated user (\`useUser({ or: "redirect" })\`) and project config (\`hexclaveApp.useProject()\`).
|
|
660
660
|
- Render top-level pages in this order:
|
|
661
661
|
- **My Profile**
|
|
662
662
|
- **Emails & Auth**
|
|
@@ -789,9 +789,9 @@ function getCustomPagePrompts() {
|
|
|
789
789
|
}
|
|
790
790
|
|
|
791
791
|
function PasswordSection() {
|
|
792
|
-
const
|
|
792
|
+
const hexclaveApp = useHexclaveApp();
|
|
793
793
|
const user = useUser({ or: "redirect" });
|
|
794
|
-
const project =
|
|
794
|
+
const project = hexclaveApp.useProject();
|
|
795
795
|
const [oldPassword, setOldPassword] = useState("");
|
|
796
796
|
const [newPassword, setNewPassword] = useState("");
|
|
797
797
|
const [newPasswordRepeat, setNewPasswordRepeat] = useState("");
|
|
@@ -820,9 +820,9 @@ function getCustomPagePrompts() {
|
|
|
820
820
|
}
|
|
821
821
|
|
|
822
822
|
function PasskeySection() {
|
|
823
|
-
const
|
|
823
|
+
const hexclaveApp = useHexclaveApp();
|
|
824
824
|
const user = useUser({ or: "redirect" });
|
|
825
|
-
const project =
|
|
825
|
+
const project = hexclaveApp.useProject();
|
|
826
826
|
const hasVerifiedAuthEmail = user.useContactChannels().some((x) => x.type === "email" && x.isVerified && x.usedForAuth);
|
|
827
827
|
const isOnlyAuthMethod = user.passkeyAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.otpAuthEnabled;
|
|
828
828
|
|
|
@@ -847,9 +847,9 @@ function getCustomPagePrompts() {
|
|
|
847
847
|
}
|
|
848
848
|
|
|
849
849
|
function OtpSection() {
|
|
850
|
-
const
|
|
850
|
+
const hexclaveApp = useHexclaveApp();
|
|
851
851
|
const user = useUser({ or: "redirect" });
|
|
852
|
-
const project =
|
|
852
|
+
const project = hexclaveApp.useProject();
|
|
853
853
|
const hasVerifiedAuthEmail = user.useContactChannels().some((x) => x.type === "email" && x.isVerified && x.usedForAuth);
|
|
854
854
|
const isOnlyAuthMethod = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;
|
|
855
855
|
|
|
@@ -998,7 +998,7 @@ function getCustomPagePrompts() {
|
|
|
998
998
|
<Typography>{product.displayName}</Typography>
|
|
999
999
|
{product.subscription?.isCancelable ? (
|
|
1000
1000
|
<Button variant="secondary" onClick={async () => {
|
|
1001
|
-
await
|
|
1001
|
+
await useHexclaveApp().cancelSubscription({
|
|
1002
1002
|
...(props.customerType === "team" ? { teamId: props.customer.id } : {}),
|
|
1003
1003
|
productId: product.id ?? "",
|
|
1004
1004
|
subscriptionId: product.subscription?.subscriptionId ?? undefined,
|
|
@@ -1023,8 +1023,8 @@ function getCustomPagePrompts() {
|
|
|
1023
1023
|
|
|
1024
1024
|
function TeamSection(props: { team: { displayName: string } }) {
|
|
1025
1025
|
const user = useUser({ or: "redirect" });
|
|
1026
|
-
const
|
|
1027
|
-
const project =
|
|
1026
|
+
const hexclaveApp = useHexclaveApp();
|
|
1027
|
+
const project = hexclaveApp.useProject();
|
|
1028
1028
|
const team = user.useTeam((props.team as any).id);
|
|
1029
1029
|
|
|
1030
1030
|
if (!team) return null;
|
|
@@ -1107,10 +1107,10 @@ function getCustomPagePrompts() {
|
|
|
1107
1107
|
}
|
|
1108
1108
|
|
|
1109
1109
|
function CreateTeamSection() {
|
|
1110
|
-
const
|
|
1110
|
+
const hexclaveApp = useHexclaveApp();
|
|
1111
1111
|
const user = useUser({ or: "redirect" });
|
|
1112
|
-
const project =
|
|
1113
|
-
const navigate =
|
|
1112
|
+
const project = hexclaveApp.useProject();
|
|
1113
|
+
const navigate = hexclaveApp.useNavigate();
|
|
1114
1114
|
const [displayName, setDisplayName] = useState("");
|
|
1115
1115
|
|
|
1116
1116
|
if (!project.config.clientTeamCreationEnabled) {
|
|
@@ -1139,7 +1139,7 @@ function getCustomPagePrompts() {
|
|
|
1139
1139
|
<Button onClick={async () => await user.signOut()}>Sign out</Button>
|
|
1140
1140
|
<Button variant="destructive" onClick={async () => {
|
|
1141
1141
|
await user.delete();
|
|
1142
|
-
await
|
|
1142
|
+
await useHexclaveApp().redirectToHome();
|
|
1143
1143
|
}}>
|
|
1144
1144
|
Delete account
|
|
1145
1145
|
</Button>
|
|
@@ -1148,9 +1148,9 @@ function getCustomPagePrompts() {
|
|
|
1148
1148
|
}
|
|
1149
1149
|
|
|
1150
1150
|
export default function CustomAccountSettingsPage(props: { extraItems?: { id: string, title: string, content: React.ReactNode }[] }) {
|
|
1151
|
-
const
|
|
1151
|
+
const hexclaveApp = useHexclaveApp();
|
|
1152
1152
|
const user = useUser({ or: "redirect" });
|
|
1153
|
-
const project =
|
|
1153
|
+
const project = hexclaveApp.useProject();
|
|
1154
1154
|
const teams = user.useTeams();
|
|
1155
1155
|
const [activeId, setActiveId] = useState("profile");
|
|
1156
1156
|
const [selectedPaymentTeamId, setSelectedPaymentTeamId] = useState<string | null>(null);
|
|
@@ -1249,20 +1249,20 @@ function getCustomPagePrompts() {
|
|
|
1249
1249
|
- Resolve current user with \`includeRestricted: true\`.
|
|
1250
1250
|
- If user is signed out, show a sign-in prompt with cancel path.
|
|
1251
1251
|
- If user is restricted, route user to onboarding first.
|
|
1252
|
-
- Verify invitation code via \`
|
|
1252
|
+
- Verify invitation code via \`hexclaveApp.verifyTeamInvitationCode(code)\`:
|
|
1253
1253
|
- Not found => invalid-link state.
|
|
1254
1254
|
- Expired => expired-link state.
|
|
1255
1255
|
- Already used => used-link state.
|
|
1256
1256
|
- Other errors => throw.
|
|
1257
|
-
- If code is valid, load invitation details via \`
|
|
1257
|
+
- If code is valid, load invitation details via \`hexclaveApp.getTeamInvitationDetails(code)\`.
|
|
1258
1258
|
- Render invitation actions:
|
|
1259
|
-
- Join => \`
|
|
1260
|
-
- Ignore => \`
|
|
1259
|
+
- Join => \`hexclaveApp.acceptTeamInvitation(code)\`.
|
|
1260
|
+
- Ignore => \`hexclaveApp.redirectToHome()\`.
|
|
1261
1261
|
- On successful join, show success state and allow navigation home.
|
|
1262
1262
|
`,
|
|
1263
1263
|
reactExample: deindent`
|
|
1264
1264
|
export default function CustomTeamInvitationPage(props: { searchParams: Record<string, string> }) {
|
|
1265
|
-
const
|
|
1265
|
+
const hexclaveApp = useHexclaveApp();
|
|
1266
1266
|
const user = useUser({ or: "return-null", includeRestricted: true });
|
|
1267
1267
|
const code = props.searchParams.code;
|
|
1268
1268
|
const [accepted, setAccepted] = useState(false);
|
|
@@ -1276,9 +1276,9 @@ function getCustomPagePrompts() {
|
|
|
1276
1276
|
<MessageCard
|
|
1277
1277
|
title="Team invitation"
|
|
1278
1278
|
primaryButtonText="Sign in"
|
|
1279
|
-
primaryAction={async () => await
|
|
1279
|
+
primaryAction={async () => await hexclaveApp.redirectToSignIn()}
|
|
1280
1280
|
secondaryButtonText="Cancel"
|
|
1281
|
-
secondaryAction={async () => await
|
|
1281
|
+
secondaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
1282
1282
|
/>
|
|
1283
1283
|
);
|
|
1284
1284
|
}
|
|
@@ -1288,7 +1288,7 @@ function getCustomPagePrompts() {
|
|
|
1288
1288
|
<MessageCard
|
|
1289
1289
|
title="Complete your account setup"
|
|
1290
1290
|
primaryButtonText="Complete setup"
|
|
1291
|
-
primaryAction={async () => await
|
|
1291
|
+
primaryAction={async () => await hexclaveApp.redirectToOnboarding()}
|
|
1292
1292
|
/>
|
|
1293
1293
|
);
|
|
1294
1294
|
}
|
|
@@ -1304,7 +1304,7 @@ function getCustomPagePrompts() {
|
|
|
1304
1304
|
title="Team invitation"
|
|
1305
1305
|
primaryButtonText="Check invitation"
|
|
1306
1306
|
primaryAction={async () => {
|
|
1307
|
-
const verification = await
|
|
1307
|
+
const verification = await hexclaveApp.verifyTeamInvitationCode(code);
|
|
1308
1308
|
if (verification.status === "error") {
|
|
1309
1309
|
if (KnownErrors.VerificationCodeNotFound.isInstance(verification.error)) {
|
|
1310
1310
|
setPageError("invalid");
|
|
@@ -1321,7 +1321,7 @@ function getCustomPagePrompts() {
|
|
|
1321
1321
|
throw verification.error;
|
|
1322
1322
|
}
|
|
1323
1323
|
|
|
1324
|
-
const invitationDetails = await
|
|
1324
|
+
const invitationDetails = await hexclaveApp.getTeamInvitationDetails(code);
|
|
1325
1325
|
if (invitationDetails.status === "error") {
|
|
1326
1326
|
setPageError("unknown");
|
|
1327
1327
|
return;
|
|
@@ -1330,7 +1330,7 @@ function getCustomPagePrompts() {
|
|
|
1330
1330
|
setDetails(invitationDetails.data);
|
|
1331
1331
|
}}
|
|
1332
1332
|
secondaryButtonText="Cancel"
|
|
1333
|
-
secondaryAction={async () => await
|
|
1333
|
+
secondaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
1334
1334
|
>
|
|
1335
1335
|
We will verify your invitation before showing the join action.
|
|
1336
1336
|
</MessageCard>
|
|
@@ -1346,12 +1346,12 @@ function getCustomPagePrompts() {
|
|
|
1346
1346
|
title="Team invitation"
|
|
1347
1347
|
primaryButtonText="Join"
|
|
1348
1348
|
primaryAction={async () => {
|
|
1349
|
-
const result = await
|
|
1349
|
+
const result = await hexclaveApp.acceptTeamInvitation(code);
|
|
1350
1350
|
if (result.status === "ok") setAccepted(true);
|
|
1351
1351
|
else setPageError("unknown");
|
|
1352
1352
|
}}
|
|
1353
1353
|
secondaryButtonText="Ignore"
|
|
1354
|
-
secondaryAction={async () => await
|
|
1354
|
+
secondaryAction={async () => await hexclaveApp.redirectToHome()}
|
|
1355
1355
|
>
|
|
1356
1356
|
You are invited to join {details.teamDisplayName}
|
|
1357
1357
|
</MessageCard>
|
|
@@ -1423,7 +1423,7 @@ function getCustomPagePrompts() {
|
|
|
1423
1423
|
structure: deindent`
|
|
1424
1424
|
- Read the MFA attempt code from session storage.
|
|
1425
1425
|
- Render OTP input for the one-time code.
|
|
1426
|
-
- When OTP is complete, submit \`
|
|
1426
|
+
- When OTP is complete, submit \`hexclaveApp.signInWithMfa(otp, attemptCode, { noRedirect: true })\`.
|
|
1427
1427
|
- Handle result:
|
|
1428
1428
|
- Success => clear stored attempt code, show success state, then redirect after sign-in.
|
|
1429
1429
|
- \`InvalidTotpCode\` => show invalid-code error and allow retry.
|
|
@@ -1445,7 +1445,7 @@ function getCustomPagePrompts() {
|
|
|
1445
1445
|
}
|
|
1446
1446
|
|
|
1447
1447
|
export default function CustomMfaPage() {
|
|
1448
|
-
const
|
|
1448
|
+
const hexclaveApp = useHexclaveApp();
|
|
1449
1449
|
const [otp, setOtp] = useState("");
|
|
1450
1450
|
const [submitting, setSubmitting] = useState(false);
|
|
1451
1451
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -1459,13 +1459,13 @@ function getCustomPagePrompts() {
|
|
|
1459
1459
|
if (!attemptCode || otp.length !== 6 || submitting) return;
|
|
1460
1460
|
setSubmitting(true);
|
|
1461
1461
|
setError(null);
|
|
1462
|
-
const result = await
|
|
1462
|
+
const result = await hexclaveApp.signInWithMfa(otp, attemptCode, { noRedirect: true });
|
|
1463
1463
|
if (result.status === "ok") {
|
|
1464
1464
|
// Hexclave rebrand: remove both the new and legacy MFA attempt code keys.
|
|
1465
1465
|
window.sessionStorage.removeItem("hexclave_mfa_attempt_code");
|
|
1466
1466
|
window.sessionStorage.removeItem("stack_mfa_attempt_code");
|
|
1467
1467
|
setVerified(true);
|
|
1468
|
-
await
|
|
1468
|
+
await hexclaveApp.redirectToAfterSignIn();
|
|
1469
1469
|
} else if (KnownErrors.InvalidTotpCode.isInstance(result.error)) {
|
|
1470
1470
|
setError("Invalid TOTP code");
|
|
1471
1471
|
setOtp("");
|
|
@@ -1517,7 +1517,7 @@ function getCustomPagePrompts() {
|
|
|
1517
1517
|
`,
|
|
1518
1518
|
reactExample: deindent`
|
|
1519
1519
|
export default function CustomErrorPage(props: { searchParams: Record<string, string> }) {
|
|
1520
|
-
const
|
|
1520
|
+
const hexclaveApp = useHexclaveApp();
|
|
1521
1521
|
const errorCode = props.searchParams.errorCode;
|
|
1522
1522
|
const message = props.searchParams.message;
|
|
1523
1523
|
const details = props.searchParams.details;
|
|
@@ -1538,11 +1538,11 @@ function getCustomPagePrompts() {
|
|
|
1538
1538
|
}
|
|
1539
1539
|
|
|
1540
1540
|
if (KnownErrors.OAuthConnectionAlreadyConnectedToAnotherUser.isInstance(error)) {
|
|
1541
|
-
return <MessageCard title="Failed to connect account" primaryButtonText="Go Home" primaryAction={() =>
|
|
1541
|
+
return <MessageCard title="Failed to connect account" primaryButtonText="Go Home" primaryAction={() => hexclaveApp.redirectToHome()} />;
|
|
1542
1542
|
}
|
|
1543
1543
|
|
|
1544
1544
|
if (KnownErrors.UserAlreadyConnectedToAnotherOAuthConnection.isInstance(error)) {
|
|
1545
|
-
return <MessageCard title="Failed to connect account" primaryButtonText="Go Home" primaryAction={() =>
|
|
1545
|
+
return <MessageCard title="Failed to connect account" primaryButtonText="Go Home" primaryAction={() => hexclaveApp.redirectToHome()} />;
|
|
1546
1546
|
}
|
|
1547
1547
|
|
|
1548
1548
|
if (KnownErrors.OAuthProviderAccessDenied.isInstance(error)) {
|
|
@@ -1550,9 +1550,9 @@ function getCustomPagePrompts() {
|
|
|
1550
1550
|
<MessageCard
|
|
1551
1551
|
title="OAuth provider access denied"
|
|
1552
1552
|
primaryButtonText="Sign in again"
|
|
1553
|
-
primaryAction={() =>
|
|
1553
|
+
primaryAction={() => hexclaveApp.redirectToSignIn()}
|
|
1554
1554
|
secondaryButtonText="Go Home"
|
|
1555
|
-
secondaryAction={() =>
|
|
1555
|
+
secondaryAction={() => hexclaveApp.redirectToHome()}
|
|
1556
1556
|
/>
|
|
1557
1557
|
);
|
|
1558
1558
|
}
|
|
@@ -1572,8 +1572,8 @@ function getCustomPagePrompts() {
|
|
|
1572
1572
|
structure: deindent`
|
|
1573
1573
|
- Resolve user with \`useUser({ or: "return-null", includeRestricted: true })\`.
|
|
1574
1574
|
- Route by user state:
|
|
1575
|
-
- Restricted user resolved to unrestricted => redirect to \`
|
|
1576
|
-
- Missing/anonymous user => redirect to \`
|
|
1575
|
+
- Restricted user resolved to unrestricted => redirect to \`hexclaveApp.redirectToAfterSignIn()\`.
|
|
1576
|
+
- Missing/anonymous user => redirect to \`hexclaveApp.redirectToSignIn()\`.
|
|
1577
1577
|
- Restricted user => continue onboarding flow.
|
|
1578
1578
|
- Handle restricted reasons:
|
|
1579
1579
|
- \`email_not_verified\` and no primary email => ask user for email and call \`user.update({ primaryEmail })\`.
|
|
@@ -1583,18 +1583,18 @@ function getCustomPagePrompts() {
|
|
|
1583
1583
|
`,
|
|
1584
1584
|
reactExample: deindent`
|
|
1585
1585
|
export default function CustomOnboardingPage() {
|
|
1586
|
-
const
|
|
1586
|
+
const hexclaveApp = useHexclaveApp();
|
|
1587
1587
|
const user = useUser({ or: "return-null", includeRestricted: true });
|
|
1588
1588
|
const [email, setEmail] = useState("");
|
|
1589
1589
|
const [changeEmail, setChangeEmail] = useState(false);
|
|
1590
1590
|
|
|
1591
1591
|
if (user && !user.isRestricted) {
|
|
1592
|
-
void runAsynchronously(
|
|
1592
|
+
void runAsynchronously(hexclaveApp.redirectToAfterSignIn());
|
|
1593
1593
|
return null;
|
|
1594
1594
|
}
|
|
1595
1595
|
|
|
1596
1596
|
if (!user || user.isAnonymous) {
|
|
1597
|
-
void runAsynchronously(
|
|
1597
|
+
void runAsynchronously(hexclaveApp.redirectToSignIn());
|
|
1598
1598
|
return null;
|
|
1599
1599
|
}
|
|
1600
1600
|
|