Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sdks): Add sdkMetadata to Clerk singleton, populate it for each SDK #1857

Merged
merged 9 commits into from
Oct 11, 2023
13 changes: 13 additions & 0 deletions .changeset/nasty-ties-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'gatsby-plugin-clerk': patch
'@clerk/clerk-js': patch
'@clerk/chrome-extension': patch
'@clerk/fastify': patch
'@clerk/nextjs': patch
'@clerk/clerk-react': patch
'@clerk/remix': patch
'@clerk/types': patch
'@clerk/clerk-expo': patch
---

Introduce a new property on the core Clerk singleton, `sdkMetadata`. This will be populated by each host SDK. This metadata will be used to make logging and debugging easier.
9 changes: 9 additions & 0 deletions packages/chrome-extension/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { name, version } = require('./package.json');

module.exports = {
displayName: 'clerk-js',
injectGlobals: true,
Expand All @@ -13,4 +16,10 @@ module.exports = {

testRegex: ['/src/.*.test.[jt]sx?$'],
testPathIgnorePatterns: ['/node_modules/'],

globals: {
__DEV__: true,
PACKAGE_NAME: name,
PACKAGE_VERSION: version,
},
};
5 changes: 5 additions & 0 deletions packages/chrome-extension/src/ClerkProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import type { TokenCache } from './cache';
import { ChromeStorageCache } from './cache';
import { buildClerk } from './singleton';

Clerk.sdkMetadata = {
name: PACKAGE_NAME,
version: PACKAGE_VERSION,
};

__internal__setErrorThrowerOptions({
packageName: '@clerk/chrome-extension',
});
Expand Down
7 changes: 7 additions & 0 deletions packages/chrome-extension/src/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}
7 changes: 7 additions & 0 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type {
PublishableKey,
RedirectOptions,
Resources,
SDKMetadata,
SetActiveParams,
SignInProps,
SignInRedirectOptions,
Expand Down Expand Up @@ -144,7 +145,13 @@ const defaultOptions: ClerkOptions = {

export default class Clerk implements ClerkInterface {
public static mountComponentRenderer?: MountComponentRenderer;

public static version: string = __PKG_VERSION__;
public static sdkMetadata: SDKMetadata = {
name: __PKG_NAME__,
version: __PKG_VERSION__,
};

public client?: ClientResource;
public session?: ActiveSessionResource | null;
public organization?: OrganizationResource | null;
Expand Down
8 changes: 8 additions & 0 deletions packages/expo/src/singleton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ import type { FapiRequestInit, FapiResponse } from '@clerk/clerk-js/dist/types/c
import Clerk from '@clerk/clerk-js/headless';
import type { HeadlessBrowserClerk } from '@clerk/clerk-react';

import { name, version } from '../package.json';
import type { TokenCache } from './cache';

const SDK_METADATA = {
name,
version,
};

const KEY = '__clerk_client_jwt';

export let clerk: HeadlessBrowserClerk;
Expand All @@ -27,6 +33,8 @@ export function buildClerk({ key, tokenCache }: BuildClerkOptions): HeadlessBrow
// TODO: DO NOT ACCEPT THIS
clerk = new Clerk(key);

clerk.sdkMetadata = SDK_METADATA;
BRKalow marked this conversation as resolved.
Show resolved Hide resolved

// @ts-expect-error
clerk.__unstable__onBeforeRequest(async (requestInit: FapiRequestInit) => {
// https://reactnative.dev/docs/0.61/network#known-issues-with-fetch-and-cookie-based-authentication
Expand Down
7 changes: 7 additions & 0 deletions packages/fastify/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { defineConfig } from 'tsup';

import { name, version } from './package.json';

export default defineConfig(overrideOptions => {
const isProd = overrideOptions.env?.NODE_ENV === 'production';

Expand All @@ -11,5 +13,10 @@ export default defineConfig(overrideOptions => {
sourcemap: true,
format: ['cjs', 'esm'],
legacyOutput: true,
define: {
PACKAGE_NAME: `"${name}"`,
PACKAGE_VERSION: `"${version}"`,
__DEV__: `${!isProd}`,
},
};
});
8 changes: 8 additions & 0 deletions packages/gatsby-plugin-clerk/src/GatsbyClerkProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import {
import { navigate } from 'gatsby';
import React from 'react';

import { name, version } from '../package.json';

const SDK_METADATA = {
name,
version,
};
BRKalow marked this conversation as resolved.
Show resolved Hide resolved

__internal__setErrorThrowerOptions({ packageName: 'gatsby-plugin-clerk' });

export type GatsbyClerkProviderProps = {
Expand All @@ -22,6 +29,7 @@ export function ClerkProvider({ children, ...rest }: GatsbyClerkProviderProps) {
<ReactClerkProvider
navigate={to => navigate(to)}
initialState={__clerk_ssr_state || {}}
sdkMetadata={SDK_METADATA}
{...restProps}
>
{__clerk_ssr_interstitial_html ? (
Expand Down
3 changes: 3 additions & 0 deletions packages/nextjs/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ declare global {
}

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;

namespace NodeJS {
interface ProcessEnv {
NEXT_PUBLIC_CLERK_FRONTEND_API: string | undefined;
Expand Down
4 changes: 4 additions & 0 deletions packages/nextjs/src/utils/mergeNextClerkPropsWithEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,9 @@ export const mergeNextClerkPropsWithEnv = (props: Omit<NextClerkProviderProps, '
signUpUrl: props.signUpUrl || process.env.NEXT_PUBLIC_CLERK_SIGN_UP_URL || '',
afterSignInUrl: props.afterSignInUrl || process.env.NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL || '',
afterSignUpUrl: props.afterSignUpUrl || process.env.NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL || '',
sdkMetadata: {
name: PACKAGE_NAME,
version: PACKAGE_VERSION,
},
};
};
2 changes: 2 additions & 0 deletions packages/react/src/isomorphicClerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ export default class IsomorphicClerk {
await global.Clerk.load(this.options);
}

global.Clerk.sdkMetadata = this.options.sdkMetadata ?? { name: PACKAGE_NAME, version: PACKAGE_VERSION };
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For SDKs that use ClerkProvider, this is how we are passing through and setting the metadata. SDKs that access Clerk directly assign it themselves.


if (global.Clerk?.loaded || global.Clerk?.isReady()) {
return this.hydrateClerkJS(global.Clerk);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
LoadedClerk,
MultiDomainAndOrProxy,
PublishableKeyOrFrontendApi,
SDKMetadata,
SessionResource,
SignInRedirectOptions,
SignUpRedirectOptions,
Expand All @@ -26,6 +27,7 @@ export type IsomorphicClerkOptions = Omit<ClerkOptions, 'isSatellite'> & {
clerkJSUrl?: string;
clerkJSVariant?: 'headless' | '';
clerkJSVersion?: string;
sdkMetadata?: SDKMetadata;
} & PublishableKeyOrFrontendApi &
MultiDomainAndOrProxy;

Expand Down
9 changes: 8 additions & 1 deletion packages/remix/src/client/RemixClerkProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ import type { IsomorphicClerkOptions } from '@clerk/clerk-react';
import { ClerkProvider as ReactClerkProvider } from '@clerk/clerk-react';
import React from 'react';

import { name, version } from '../../package.json';
import { assertValidClerkState, warnForSsr } from '../utils';
import { ClerkRemixOptionsProvider } from './RemixOptionsContext';
import type { ClerkState } from './types';
import { useAwaitableNavigate } from './useAwaitableNavigate';

export * from '@clerk/clerk-react';

const SDK_METADATA = {
name,
version,
};

export type RemixClerkProviderProps = {
children: React.ReactNode;
clerkState: ClerkState;
Expand All @@ -20,7 +26,7 @@ export type RemixClerkProviderProps = {
* In the case of a hydration error, the first `navigate` function we get from the `useNavigate` hook will not work
* because the RemixClerkProvider (which is part of the host app) will unmount before the following useEffect within `navigate` fires:
* https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/hooks.tsx#L175
* so isomorphicClerk will initialise with a `navigate` function that will never have `activeRef.current` set to true.
* so isomorphicClerk will initialize with a `navigate` function that will never have `activeRef.current` set to true.
* This variable is just an object ref/cache outside the React rendering cycle that holds a reference to the
* latest `navigate` function. After a hydration error occurs, RemixClerkProvider will *remount* and this variable
* will finally get a `navigate` function that has a `activeRef.current` to true so navigation will function as it should.
Expand Down Expand Up @@ -81,6 +87,7 @@ export function ClerkProvider({ children, ...rest }: RemixClerkProviderProps): J
<ReactClerkProvider
navigate={(to: string) => awaitableNavigateRef.current?.(to)}
initialState={__clerk_ssr_state}
sdkMetadata={SDK_METADATA}
{...mergedProps}
{...restProps}
>
Expand Down
11 changes: 11 additions & 0 deletions packages/types/src/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ import type { DeepPartial, DeepSnakeToCamel } from './utils';

export type InstanceType = 'production' | 'development';

export type SDKMetadata = {
name: string;
version: string;
};

export type ListenerCallback = (emission: Resources) => void;
export type UnsubscribeCallback = () => void;
export type BeforeEmitCallback = (session?: ActiveSessionResource | null) => void | Promise<any>;
Expand Down Expand Up @@ -56,6 +61,12 @@ export interface Clerk {
*/
version?: string;

/**
* If present, contains information about the SDK that the host application is using.
* For example, if Clerk is loaded through `@clerk/nextjs`, this would be `{ name: '@clerk/nextjs', version: '1.0.0' }`
*/
sdkMetadata?: SDKMetadata;

loaded: boolean;

/**
Expand Down