From 7cfed80223a2cefa7d984ef60e608c8c9a833747 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Wed, 22 May 2024 10:49:47 -0400 Subject: [PATCH 1/7] Add browser inspector and remove inspector-related code from the production build --- .changeset/khaki-starfishes-allow.md | 7 ++ package-lock.json | 93 +------------------ packages/elements/package.json | 10 +- .../internals/machines/form/form.context.ts | 4 +- .../src/internals/utils/inspector/browser.ts | 85 ----------------- .../utils/inspector/browser/index.ts | 18 ++++ .../utils/inspector/{ => console}/console.ts | 0 .../utils/inspector/console/index.ts | 16 ++++ .../src/internals/utils/inspector/index.ts | 15 +-- packages/elements/src/react/hooks/index.ts | 1 - .../react/hooks/use-browser-inspector.hook.ts | 3 - packages/elements/src/react/sign-in/root.tsx | 4 +- packages/elements/src/react/sign-up/root.tsx | 4 +- packages/elements/tsup.config.ts | 22 ++++- 14 files changed, 83 insertions(+), 199 deletions(-) create mode 100644 .changeset/khaki-starfishes-allow.md delete mode 100644 packages/elements/src/internals/utils/inspector/browser.ts create mode 100644 packages/elements/src/internals/utils/inspector/browser/index.ts rename packages/elements/src/internals/utils/inspector/{ => console}/console.ts (100%) create mode 100644 packages/elements/src/internals/utils/inspector/console/index.ts delete mode 100644 packages/elements/src/react/hooks/use-browser-inspector.hook.ts diff --git a/.changeset/khaki-starfishes-allow.md b/.changeset/khaki-starfishes-allow.md new file mode 100644 index 0000000000..3a2c7d1050 --- /dev/null +++ b/.changeset/khaki-starfishes-allow.md @@ -0,0 +1,7 @@ +--- +'@clerk/elements': minor +--- + +- Adds Stately's Browser Inspector in development builds +- Removes `@statelyai/inspect` from dependencies +- Ensures all inspector-related code is omitted from the build diff --git a/package-lock.json b/package-lock.json index ef95a8e06c..46057f4494 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15977,20 +15977,6 @@ "node": ">= 0.8" } }, - "node_modules/copy-anything": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", - "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", - "dependencies": { - "is-what": "^4.1.8" - }, - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "license": "MIT", @@ -20466,6 +20452,7 @@ }, "node_modules/fast-safe-stringify": { "version": "2.1.1", + "dev": true, "license": "MIT" }, "node_modules/fast-uri": { @@ -24812,17 +24799,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-what": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/is-windows": { "version": "1.0.2", "dev": true, @@ -24871,13 +24847,6 @@ "node": ">=0.10.0" } }, - "node_modules/isomorphic-ws": { - "version": "5.0.0", - "license": "MIT", - "peerDependencies": { - "ws": "*" - } - }, "node_modules/isstream": { "version": "0.1.2", "dev": true, @@ -30576,25 +30545,6 @@ "node": ">= 0.8" } }, - "node_modules/partysocket": { - "version": "0.0.25", - "resolved": "https://registry.npmjs.org/partysocket/-/partysocket-0.0.25.tgz", - "integrity": "sha512-1oCGA65fydX/FgdnsiBh68buOvfxuteoZVSb3Paci2kRp/7lhF0HyA8EDb5X/O6FxId1e+usPTQNRuzFEvkJbQ==", - "dependencies": { - "event-target-shim": "^6.0.2" - } - }, - "node_modules/partysocket/node_modules/event-target-shim": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz", - "integrity": "sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==", - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/pascal-case": { "version": "3.1.2", "dev": true, @@ -35176,17 +35126,6 @@ "node": ">=4.0.0" } }, - "node_modules/superjson": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.13.3.tgz", - "integrity": "sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==", - "dependencies": { - "copy-anything": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/supertest": { "version": "6.3.4", "dev": true, @@ -38502,6 +38441,7 @@ }, "node_modules/ws": { "version": "8.13.0", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -39614,7 +39554,6 @@ "dependencies": { "@radix-ui/react-form": "^0.0.3", "@radix-ui/react-slot": "^1.0.2", - "@statelyai/inspect": "^0.3.0", "@xstate/react": "^4.1.1", "client-only": "^0.0.1", "xstate": "^5.13.0" @@ -39667,22 +39606,6 @@ "node": ">=16" } }, - "packages/elements/node_modules/@statelyai/inspect": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@statelyai/inspect/-/inspect-0.3.0.tgz", - "integrity": "sha512-Fusho7ZTX5HjA0yPZVcz503exezNLIZo0pz3RdrjbJ/etAhP0GFeXcI5jdjYcxEmo0iYKX1uCybn74tyjGu/cw==", - "dependencies": { - "fast-safe-stringify": "^2.1.1", - "isomorphic-ws": "^5.0.0", - "partysocket": "^0.0.25", - "safe-stable-stringify": "^2.4.3", - "superjson": "^1.13.3", - "uuid": "^9.0.1" - }, - "peerDependencies": { - "xstate": "^5.5.1" - } - }, "packages/elements/node_modules/@swc/helpers": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", @@ -39823,18 +39746,6 @@ "node": ">=14.17" } }, - "packages/elements/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "packages/elements/node_modules/xstate": { "version": "5.13.0", "resolved": "https://registry.npmjs.org/xstate/-/xstate-5.13.0.tgz", diff --git a/packages/elements/package.json b/packages/elements/package.json index 2e790bfe9e..0f8421cfd1 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -55,12 +55,13 @@ "app:dev": "(cd examples/nextjs && npm run dev --turbo)", "app:dev:debug": "(cd examples/nextjs && NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG=true npm run dev --turbo)", "app:dev:debug:server": "(cd examples/nextjs && NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG=true CLERK_ELEMENTS_DEBUG_SERVER=true npm run dev --turbo)", + "app:dev:debug:ui": "(cd examples/nextjs && NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG_UI=true npm run dev --turbo)", "app:e2e": "(cd examples/nextjs && npm run e2e)", "app:lint": "(cd examples/nextjs && npm run lint)", - "build": "tsup", - "build:analyze": "tsup --metafile; open https://esbuild.github.io/analyze/", - "build:declarations": "tsc -p tsconfig.json", - "dev": "tsup --watch", + "build": "tsup --env.NODE_ENV production", + "build:analyze": "tsup --env.NODE_ENV production --metafile; open https://esbuild.github.io/analyze/", + "build:declarations": "tsc --env.NODE_ENV production -p tsconfig.json", + "dev": "tsup --env.NODE_ENV development --watch", "dev:example": "concurrently \"npm run dev\" \"npm run app:dev\"", "lint": "eslint src/", "lint:attw": "attw --pack .", @@ -71,7 +72,6 @@ "dependencies": { "@radix-ui/react-form": "^0.0.3", "@radix-ui/react-slot": "^1.0.2", - "@statelyai/inspect": "^0.3.0", "@xstate/react": "^4.1.1", "client-only": "^0.0.1", "xstate": "^5.13.0" diff --git a/packages/elements/src/internals/machines/form/form.context.ts b/packages/elements/src/internals/machines/form/form.context.ts index 0d9d093246..7517c89924 100644 --- a/packages/elements/src/internals/machines/form/form.context.ts +++ b/packages/elements/src/internals/machines/form/form.context.ts @@ -2,11 +2,11 @@ import { createActorContext } from '@xstate/react'; import type { SnapshotFrom } from 'xstate'; import { FormMachine } from '~/internals/machines/form'; -import { consoleInspector } from '~/internals/utils/inspector'; +import { inspect } from '~/internals/utils/inspector'; export type SnapshotState = SnapshotFrom; -const FormMachineContext = createActorContext(FormMachine, { inspect: consoleInspector }); +const FormMachineContext = createActorContext(FormMachine, { inspect }); export const FormStoreProvider = FormMachineContext.Provider; export const useFormStore = FormMachineContext.useActorRef; diff --git a/packages/elements/src/internals/utils/inspector/browser.ts b/packages/elements/src/internals/utils/inspector/browser.ts deleted file mode 100644 index 9f11aa4cf1..0000000000 --- a/packages/elements/src/internals/utils/inspector/browser.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { StatelyInspectionEvent } from '@statelyai/inspect'; -import { useEffect, useState } from 'react'; - -export interface InspectorOptions { - filter?: (event: StatelyInspectionEvent) => boolean; - serialize?: (event: StatelyInspectionEvent) => StatelyInspectionEvent; - /** - * Whether to automatically start the inspector. - * - * @default true - */ - autoStart?: boolean; -} - -export interface BrowserInspectorOptions extends InspectorOptions { - url?: string; - window?: Window; - iframe?: HTMLIFrameElement | null; -} - -/** - * Stately Browser Inspector - * - * Used for debugging state machines in the browser. These hooks are used internally to conditionally - * enable the state inspector in client-only development environments. - * - * @param params.enabled - Whether to enable the inspector (or `NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG=true`) - * @param params.options - Options for the inspector - * - * @example - * const { useBrowserInspector } = createBrowserInspectorReactHook(); - * const { loading: inspectorLoading, inspector } = useBrowserInspector(); - * - * @returns useBrowserInspector - A hook for using the inspector - */ -export function createBrowserInspectorReactHook(params?: { enabled?: boolean; options?: BrowserInspectorOptions }) { - const { enabled = process.env.NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG === 'true', options } = params || {}; - const loadable = typeof window !== 'undefined'; - let storedInspector: any; - - function useDisabledBrowserInspector() { - return { - loading: false, - inspector: undefined, - }; - } - - function useEnabledBrowserInspector() { - const [inspector, setInspector] = useState(storedInspector || undefined); // TODO: No relevant types exported from statelyai/inspect - - useEffect(() => { - if (inspector) return; - - const getInspector = async () => { - const { createBrowserInspector } = (await import('@statelyai/inspect')).default; - return createBrowserInspector(options); - }; - - getInspector() - .then(res => { - storedInspector = res; - setInspector(res); - }) - .catch(console.error); - }, [inspector]); - - return { - /** - * Whether the inspector is loading. - * Will be `false` if the inspector is disabled. - */ - loading: !inspector, - /** - * The inspector instance. - * Will be `undefined` if the inspector is disabled. - * @see https://stately.ai/docs/inspector - */ - inspector, - }; - } - - return { - useBrowserInspector: !loadable || !enabled ? useDisabledBrowserInspector : useEnabledBrowserInspector, - }; -} diff --git a/packages/elements/src/internals/utils/inspector/browser/index.ts b/packages/elements/src/internals/utils/inspector/browser/index.ts new file mode 100644 index 0000000000..d6dcfa7b36 --- /dev/null +++ b/packages/elements/src/internals/utils/inspector/browser/index.ts @@ -0,0 +1,18 @@ +import { isTruthy } from '@clerk/shared/underscore'; +import { createBrowserInspector } from '@statelyai/inspect'; + +export const getInspector = () => { + if ( + typeof window !== 'undefined' && + process.env.NODE_ENV === 'development' && + isTruthy(process.env.NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG_UI) + ) { + const { inspect } = createBrowserInspector({ + autoStart: true, + }); + + return inspect; + } + + return undefined; +}; diff --git a/packages/elements/src/internals/utils/inspector/console.ts b/packages/elements/src/internals/utils/inspector/console/console.ts similarity index 100% rename from packages/elements/src/internals/utils/inspector/console.ts rename to packages/elements/src/internals/utils/inspector/console/console.ts diff --git a/packages/elements/src/internals/utils/inspector/console/index.ts b/packages/elements/src/internals/utils/inspector/console/index.ts new file mode 100644 index 0000000000..2fba903b42 --- /dev/null +++ b/packages/elements/src/internals/utils/inspector/console/index.ts @@ -0,0 +1,16 @@ +import { isTruthy } from '@clerk/shared/underscore'; + +import { createConsoleInspector } from './console'; + +export function getInspector() { + if ( + process.env.NODE_ENV === 'development' && + isTruthy(process.env.NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG ?? process.env.CLERK_ELEMENTS_DEBUG) + ) { + return createConsoleInspector({ + enabled: true, + debugServer: isTruthy(process.env.CLERK_ELEMENTS_DEBUG_SERVER), + }); + } + return undefined; +} diff --git a/packages/elements/src/internals/utils/inspector/index.ts b/packages/elements/src/internals/utils/inspector/index.ts index 00bf6e8982..95f4c02033 100644 --- a/packages/elements/src/internals/utils/inspector/index.ts +++ b/packages/elements/src/internals/utils/inspector/index.ts @@ -1,9 +1,10 @@ -import { isTruthy } from '@clerk/shared/underscore'; +import { getInspector as getBrowserInspector } from './browser'; +import { getInspector as getConsoleInspector } from './console'; -export { createBrowserInspectorReactHook } from './browser'; -import { createConsoleInspector } from './console'; +export const inspect = getBrowserInspector() ?? getConsoleInspector() ?? undefined; -export const consoleInspector = createConsoleInspector({ - enabled: isTruthy(process.env.NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG ?? process.env.CLERK_ELEMENTS_DEBUG), - debugServer: isTruthy(process.env.CLERK_ELEMENTS_DEBUG_SERVER), -}); +const inspector = { + inspect, +}; + +export default inspector; diff --git a/packages/elements/src/react/hooks/index.ts b/packages/elements/src/react/hooks/index.ts index c000eaba5c..3b8850d87b 100644 --- a/packages/elements/src/react/hooks/index.ts +++ b/packages/elements/src/react/hooks/index.ts @@ -1,5 +1,4 @@ export { useActiveStates } from './use-active-states.hook'; export { useActiveTags } from './use-active-tags.hook'; -export { useBrowserInspector } from './use-browser-inspector.hook'; export { useThirdPartyProvider } from './use-third-party-provider.hook'; export { useFocus } from './use-focus.hook'; diff --git a/packages/elements/src/react/hooks/use-browser-inspector.hook.ts b/packages/elements/src/react/hooks/use-browser-inspector.hook.ts deleted file mode 100644 index 9f99a86d43..0000000000 --- a/packages/elements/src/react/hooks/use-browser-inspector.hook.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { createBrowserInspectorReactHook } from '~/internals/utils/inspector'; - -export const { useBrowserInspector } = createBrowserInspectorReactHook(); diff --git a/packages/elements/src/react/sign-in/root.tsx b/packages/elements/src/react/sign-in/root.tsx index 1d12978205..ad0dc420cc 100644 --- a/packages/elements/src/react/sign-in/root.tsx +++ b/packages/elements/src/react/sign-in/root.tsx @@ -7,7 +7,7 @@ import { SIGN_IN_DEFAULT_BASE_PATH, SIGN_UP_DEFAULT_BASE_PATH } from '~/internal import { FormStoreProvider, useFormStore } from '~/internals/machines/form/form.context'; import type { SignInRouterInitEvent } from '~/internals/machines/sign-in'; import { SignInRouterMachine } from '~/internals/machines/sign-in'; -import { consoleInspector } from '~/internals/utils/inspector'; +import { inspect } from '~/internals/utils/inspector'; import { Router, useClerkRouter, useNextRouter } from '~/react/router'; import { SignInRouterCtx } from '~/react/sign-in/context'; @@ -18,7 +18,7 @@ type SignInFlowProviderProps = { exampleMode?: boolean; }; -const actor = createActor(SignInRouterMachine, { inspect: consoleInspector }); +const actor = createActor(SignInRouterMachine, { inspect }); actor.start(); function SignInFlowProvider({ children, exampleMode }: SignInFlowProviderProps) { diff --git a/packages/elements/src/react/sign-up/root.tsx b/packages/elements/src/react/sign-up/root.tsx index bf2c9c198e..0eea9bf0df 100644 --- a/packages/elements/src/react/sign-up/root.tsx +++ b/packages/elements/src/react/sign-up/root.tsx @@ -8,7 +8,7 @@ import { SIGN_IN_DEFAULT_BASE_PATH, SIGN_UP_DEFAULT_BASE_PATH } from '~/internal import { FormStoreProvider, useFormStore } from '~/internals/machines/form/form.context'; import type { SignUpRouterInitEvent } from '~/internals/machines/sign-up'; import { SignUpRouterMachine } from '~/internals/machines/sign-up'; -import { consoleInspector } from '~/internals/utils/inspector'; +import { inspect } from '~/internals/utils/inspector'; import { Router, useClerkRouter, useNextRouter } from '~/react/router'; import { SignUpRouterCtx } from '~/react/sign-up/context'; @@ -19,7 +19,7 @@ type SignUpFlowProviderProps = { exampleMode?: boolean; }; -const actor = createActor(SignUpRouterMachine, { inspect: consoleInspector }); +const actor = createActor(SignUpRouterMachine, { inspect }); const ref = actor.start(); function SignUpFlowProvider({ children, exampleMode }: SignUpFlowProviderProps) { diff --git a/packages/elements/tsup.config.ts b/packages/elements/tsup.config.ts index 86b57be791..13da8423a9 100644 --- a/packages/elements/tsup.config.ts +++ b/packages/elements/tsup.config.ts @@ -1,8 +1,27 @@ +import type { Plugin } from 'esbuild'; import { defineConfig } from 'tsup'; import { version as clerkJsVersion } from '../clerk-js/package.json'; import { name, version } from './package.json'; +/** + * Replaces the inspect with undefined so-as not to bundle + * inspectors outside of our development environment. + */ +export function dynamicInspectorImport(): Plugin { + return { + name: 'dynamicInspectorImport', + setup(build) { + build.onLoad({ filter: /\/inspector\/index.ts/ }, async () => { + return { + contents: 'export const inspect = undefined;', + loader: 'ts', + }; + }); + }, + }; +} + export default defineConfig(overrideOptions => { const isProd = overrideOptions.env?.NODE_ENV === 'production'; @@ -21,9 +40,10 @@ export default defineConfig(overrideOptions => { 'react/sign-in/index': 'src/react/sign-in/index.ts', 'react/sign-up/index': 'src/react/sign-up/index.ts', }, - external: ['react', 'react-dom'], + external: ['react', 'react-dom', 'xstate', '@statelyai/inspect'], format: ['cjs', 'esm'], minify: false, sourcemap: true, + esbuildPlugins: isProd ? [dynamicInspectorImport()] : [], }; }); From 7c5d615439f3178918954de424782f79e04d2732 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Wed, 22 May 2024 11:25:55 -0400 Subject: [PATCH 2/7] fix(elements): Fix linting issues with conditionally bundled code --- .../elements/src/internals/utils/inspector/browser/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/elements/src/internals/utils/inspector/browser/index.ts b/packages/elements/src/internals/utils/inspector/browser/index.ts index d6dcfa7b36..7a9f9c4c9b 100644 --- a/packages/elements/src/internals/utils/inspector/browser/index.ts +++ b/packages/elements/src/internals/utils/inspector/browser/index.ts @@ -1,4 +1,6 @@ import { isTruthy } from '@clerk/shared/underscore'; +// @ts-expect-error - Exists only in devDependencies; Removed by tsup for production builds +// eslint-disable-next-line import/no-unresolved import { createBrowserInspector } from '@statelyai/inspect'; export const getInspector = () => { From 3c5cbdecb0042dfa53f87ad3ad896604f9b7a874 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Wed, 22 May 2024 12:13:22 -0400 Subject: [PATCH 3/7] fix(elements): Re-add missing devDependency --- package-lock.json | 100 +++++++++++++++++++++++++++++++ packages/elements/package.json | 1 + packages/elements/tsup.config.ts | 2 +- 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 46057f4494..3835d5240e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15977,6 +15977,21 @@ "node": ">= 0.8" } }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "license": "MIT", @@ -24799,6 +24814,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "dev": true, @@ -24847,6 +24874,15 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "dev": true, + "peerDependencies": { + "ws": "*" + } + }, "node_modules/isstream": { "version": "0.1.2", "dev": true, @@ -30545,6 +30581,27 @@ "node": ">= 0.8" } }, + "node_modules/partysocket": { + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/partysocket/-/partysocket-0.0.25.tgz", + "integrity": "sha512-1oCGA65fydX/FgdnsiBh68buOvfxuteoZVSb3Paci2kRp/7lhF0HyA8EDb5X/O6FxId1e+usPTQNRuzFEvkJbQ==", + "dev": true, + "dependencies": { + "event-target-shim": "^6.0.2" + } + }, + "node_modules/partysocket/node_modules/event-target-shim": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz", + "integrity": "sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, "node_modules/pascal-case": { "version": "3.1.2", "dev": true, @@ -35126,6 +35183,18 @@ "node": ">=4.0.0" } }, + "node_modules/superjson": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.13.3.tgz", + "integrity": "sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==", + "dev": true, + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/supertest": { "version": "6.3.4", "dev": true, @@ -39563,6 +39632,7 @@ "@clerk/eslint-config-custom": "*", "@clerk/shared": "2.2.0", "@clerk/types": "^4.5.0", + "@statelyai/inspect": "^0.3.1", "@types/node": "^18.17.0", "@types/react": "*", "@types/react-dom": "*", @@ -39606,6 +39676,23 @@ "node": ">=16" } }, + "packages/elements/node_modules/@statelyai/inspect": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@statelyai/inspect/-/inspect-0.3.1.tgz", + "integrity": "sha512-KW3owf5UbPs1+/xOGJoSV4D69hP5xTX7PCzARr2R1senwfUIwyGP8yEsB8dvkMvekYvgFS0qa6lmg1eszYr2tw==", + "dev": true, + "dependencies": { + "fast-safe-stringify": "^2.1.1", + "isomorphic-ws": "^5.0.0", + "partysocket": "^0.0.25", + "safe-stable-stringify": "^2.4.3", + "superjson": "^1.13.3", + "uuid": "^9.0.1" + }, + "peerDependencies": { + "xstate": "^5.5.1" + } + }, "packages/elements/node_modules/@swc/helpers": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", @@ -39746,6 +39833,19 @@ "node": ">=14.17" } }, + "packages/elements/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "packages/elements/node_modules/xstate": { "version": "5.13.0", "resolved": "https://registry.npmjs.org/xstate/-/xstate-5.13.0.tgz", diff --git a/packages/elements/package.json b/packages/elements/package.json index 0f8421cfd1..d5e2e00261 100644 --- a/packages/elements/package.json +++ b/packages/elements/package.json @@ -81,6 +81,7 @@ "@clerk/eslint-config-custom": "*", "@clerk/shared": "2.2.0", "@clerk/types": "^4.5.0", + "@statelyai/inspect": "^0.3.1", "@types/node": "^18.17.0", "@types/react": "*", "@types/react-dom": "*", diff --git a/packages/elements/tsup.config.ts b/packages/elements/tsup.config.ts index 13da8423a9..f6fae86df2 100644 --- a/packages/elements/tsup.config.ts +++ b/packages/elements/tsup.config.ts @@ -40,7 +40,7 @@ export default defineConfig(overrideOptions => { 'react/sign-in/index': 'src/react/sign-in/index.ts', 'react/sign-up/index': 'src/react/sign-up/index.ts', }, - external: ['react', 'react-dom', 'xstate', '@statelyai/inspect'], + external: ['react', 'react-dom', '@statelyai/inspect'], format: ['cjs', 'esm'], minify: false, sourcemap: true, From f72b416d4cc882a53533eec9136de5ccc7270a6c Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Thu, 23 May 2024 11:15:59 -0400 Subject: [PATCH 4/7] chore(elements): Rely on DCE vs overwriting --- .../utils/inspector/browser/index.ts | 5 ++--- .../utils/inspector/console/index.ts | 1 + .../src/internals/utils/inspector/index.ts | 8 +++++++- packages/elements/src/types/globals.d.ts | 7 +++++++ packages/elements/tsup.config.ts | 20 ------------------- 5 files changed, 17 insertions(+), 24 deletions(-) create mode 100644 packages/elements/src/types/globals.d.ts diff --git a/packages/elements/src/internals/utils/inspector/browser/index.ts b/packages/elements/src/internals/utils/inspector/browser/index.ts index 7a9f9c4c9b..ba3d0ff2d0 100644 --- a/packages/elements/src/internals/utils/inspector/browser/index.ts +++ b/packages/elements/src/internals/utils/inspector/browser/index.ts @@ -1,13 +1,12 @@ import { isTruthy } from '@clerk/shared/underscore'; -// @ts-expect-error - Exists only in devDependencies; Removed by tsup for production builds -// eslint-disable-next-line import/no-unresolved import { createBrowserInspector } from '@statelyai/inspect'; export const getInspector = () => { if ( + __DEV__ && typeof window !== 'undefined' && process.env.NODE_ENV === 'development' && - isTruthy(process.env.NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG_UI) + isTruthy(process.env.NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG_UI ?? process.env.CLERK_ELEMENTS_DEBUG_UI) ) { const { inspect } = createBrowserInspector({ autoStart: true, diff --git a/packages/elements/src/internals/utils/inspector/console/index.ts b/packages/elements/src/internals/utils/inspector/console/index.ts index 2fba903b42..62385651a6 100644 --- a/packages/elements/src/internals/utils/inspector/console/index.ts +++ b/packages/elements/src/internals/utils/inspector/console/index.ts @@ -4,6 +4,7 @@ import { createConsoleInspector } from './console'; export function getInspector() { if ( + __DEV__ && process.env.NODE_ENV === 'development' && isTruthy(process.env.NEXT_PUBLIC_CLERK_ELEMENTS_DEBUG ?? process.env.CLERK_ELEMENTS_DEBUG) ) { diff --git a/packages/elements/src/internals/utils/inspector/index.ts b/packages/elements/src/internals/utils/inspector/index.ts index 95f4c02033..8ff89fbdd3 100644 --- a/packages/elements/src/internals/utils/inspector/index.ts +++ b/packages/elements/src/internals/utils/inspector/index.ts @@ -1,7 +1,13 @@ +import type { InspectionEvent, Observer } from 'xstate'; + import { getInspector as getBrowserInspector } from './browser'; import { getInspector as getConsoleInspector } from './console'; -export const inspect = getBrowserInspector() ?? getConsoleInspector() ?? undefined; +export let inspect: Observer | undefined; + +if (__DEV__) { + inspect = getBrowserInspector() ?? getConsoleInspector(); +} const inspector = { inspect, diff --git a/packages/elements/src/types/globals.d.ts b/packages/elements/src/types/globals.d.ts new file mode 100644 index 0000000000..72b67abb15 --- /dev/null +++ b/packages/elements/src/types/globals.d.ts @@ -0,0 +1,7 @@ +export {}; + +declare global { + const PACKAGE_NAME: string; + const PACKAGE_VERSION: string; + const __DEV__: boolean; +} diff --git a/packages/elements/tsup.config.ts b/packages/elements/tsup.config.ts index f6fae86df2..c8cb1e480a 100644 --- a/packages/elements/tsup.config.ts +++ b/packages/elements/tsup.config.ts @@ -1,27 +1,8 @@ -import type { Plugin } from 'esbuild'; import { defineConfig } from 'tsup'; import { version as clerkJsVersion } from '../clerk-js/package.json'; import { name, version } from './package.json'; -/** - * Replaces the inspect with undefined so-as not to bundle - * inspectors outside of our development environment. - */ -export function dynamicInspectorImport(): Plugin { - return { - name: 'dynamicInspectorImport', - setup(build) { - build.onLoad({ filter: /\/inspector\/index.ts/ }, async () => { - return { - contents: 'export const inspect = undefined;', - loader: 'ts', - }; - }); - }, - }; -} - export default defineConfig(overrideOptions => { const isProd = overrideOptions.env?.NODE_ENV === 'production'; @@ -44,6 +25,5 @@ export default defineConfig(overrideOptions => { format: ['cjs', 'esm'], minify: false, sourcemap: true, - esbuildPlugins: isProd ? [dynamicInspectorImport()] : [], }; }); From b74ef3c4abadc880b9d17d3f973afce99e63f333 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Wed, 22 May 2024 13:11:34 -0400 Subject: [PATCH 5/7] fix: Form does not submit after hitting Go Back in ChooseStrategy --- .../machines/sign-in/router.machine.ts | 6 ++++- .../machines/sign-in/verification.machine.ts | 27 ++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/elements/src/internals/machines/sign-in/router.machine.ts b/packages/elements/src/internals/machines/sign-in/router.machine.ts index 8356414e45..35edb8d9c9 100644 --- a/packages/elements/src/internals/machines/sign-in/router.machine.ts +++ b/packages/elements/src/internals/machines/sign-in/router.machine.ts @@ -363,7 +363,11 @@ export const SignInRouterMachine = setup({ ChoosingStrategy: { tags: ['route:choose-strategy'], on: { - 'NAVIGATE.PREVIOUS': 'Idle', + 'NAVIGATE.PREVIOUS': { + description: 'Go to Idle, and also tell firstFactor to go to Pending', + target: 'Idle', + actions: sendTo(({ self }) => self.getSnapshot().children['firstFactor']!, { type: 'NAVIGATE.PREVIOUS' }), + }, }, }, ForgotPassword: { diff --git a/packages/elements/src/internals/machines/sign-in/verification.machine.ts b/packages/elements/src/internals/machines/sign-in/verification.machine.ts index 9b4af28755..4846322a9e 100644 --- a/packages/elements/src/internals/machines/sign-in/verification.machine.ts +++ b/packages/elements/src/internals/machines/sign-in/verification.machine.ts @@ -145,6 +145,7 @@ const SignInVerificationMachine = setup({ }, guards: { isResendable: ({ context }) => context.resendable || context.resendableAfter === 0, + isNeverResendable: ({ context }) => context.currentFactor?.strategy === 'password', }, delays: SignInVerificationDelays, types: {} as SignInVerificationSchema, @@ -161,6 +162,7 @@ const SignInVerificationMachine = setup({ }), initial: 'Init', on: { + 'NAVIGATE.PREVIOUS': '.Hist', 'STRATEGY.REGISTER': { actions: assign({ registeredStrategies: ({ context, event }) => context.registeredStrategies.add(event.factor), @@ -233,11 +235,31 @@ const SignInVerificationMachine = setup({ reenter: true, }, }, - initial: 'NotResendable', + initial: 'Init', states: { + Init: { + description: 'Marks appropriate factors as never resendable.', + always: [ + { + guard: 'isNeverResendable', + target: 'NeverResendable', + }, + { + target: 'NotResendable', + }, + ], + }, Resendable: { description: 'Waiting for user to retry', }, + NeverResendable: { + description: 'Handles never resendable', + on: { + RETRY: { + actions: log('Never retriable'), + }, + }, + }, NotResendable: { description: 'Handle countdowns', on: { @@ -299,6 +321,9 @@ const SignInVerificationMachine = setup({ }, }, }, + Hist: { + type: 'history', + }, }, }); From e49ae886b6b9482c752eabd8e7c6c27e7dbf76d3 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Wed, 22 May 2024 13:21:53 -0400 Subject: [PATCH 6/7] chore: Add changeset --- .changeset/red-gorillas-count.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/red-gorillas-count.md diff --git a/.changeset/red-gorillas-count.md b/.changeset/red-gorillas-count.md new file mode 100644 index 0000000000..73678529f8 --- /dev/null +++ b/.changeset/red-gorillas-count.md @@ -0,0 +1,6 @@ +--- +'@clerk/elements': patch +--- + +Fix: Verification form submission wasn't working after returning from "choosing an alternate strategy" without making a selection. +Perf: Adds a `NeverRetriable` state for applicable strategies so the countdown doesn't run needlessly. From b87a7d323b9bdf75471c671c41bd4b2d6312ca41 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Thu, 23 May 2024 11:25:36 -0400 Subject: [PATCH 7/7] chore(elements): Remove children from `sendTo` call --- .../elements/src/internals/machines/sign-in/router.machine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/elements/src/internals/machines/sign-in/router.machine.ts b/packages/elements/src/internals/machines/sign-in/router.machine.ts index 35edb8d9c9..a2d2a13b54 100644 --- a/packages/elements/src/internals/machines/sign-in/router.machine.ts +++ b/packages/elements/src/internals/machines/sign-in/router.machine.ts @@ -366,7 +366,7 @@ export const SignInRouterMachine = setup({ 'NAVIGATE.PREVIOUS': { description: 'Go to Idle, and also tell firstFactor to go to Pending', target: 'Idle', - actions: sendTo(({ self }) => self.getSnapshot().children['firstFactor']!, { type: 'NAVIGATE.PREVIOUS' }), + actions: sendTo('firstFactor', { type: 'NAVIGATE.PREVIOUS' }), }, }, },