Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions packages/clerk-js-pinned/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "@clerk/clerk-js-pinned",
"version": "5.114.0",
"description": "Lightweight package for pinning @clerk/clerk-js version via dependency management",
"keywords": [
"clerk",
"auth",
"authentication",
"version",
"pinning"
],
"homepage": "https://clerk.com/",
"bugs": {
"url": "https://github.com/clerk/javascript/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/clerk/javascript.git",
"directory": "packages/clerk-js-pinned"
},
"license": "MIT",
"author": "Clerk",
"sideEffects": false,
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsdown && pnpm build:dts",
"build:dts": "dts-bundle-generator -o dist/index.d.ts src/index.ts --no-check && cp dist/index.d.ts dist/index.d.cts",
"clean": "rimraf ./dist",
"format": "node ../../scripts/format-package.mjs",
"format:check": "node ../../scripts/format-package.mjs --check",
"lint": "eslint src",
"lint:attw": "attw --pack . --profile node16",
"lint:publint": "publint"
},
"devDependencies": {
"@clerk/shared": "workspace:^",
"dts-bundle-generator": "^9.5.1",
"tsdown": "catalog:repo"
},
"engines": {
"node": ">=20.9.0"
},
"publishConfig": {
"access": "public"
}
}
32 changes: 32 additions & 0 deletions packages/clerk-js-pinned/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { ClerkJs } from '@clerk/shared/types';

declare const PACKAGE_VERSION: string;

/**
* The version of @clerk/clerk-js that this package pins to.
* Use this with the `clerkJSVersion` prop for string-based pinning.
*
* @example
* ```tsx
* import { version } from '@clerk/clerk-js-pinned';
* <ClerkProvider clerkJSVersion={version} />
* ```
*/
export const version = PACKAGE_VERSION;

/**
* Branded object for pinning @clerk/clerk-js version.
* Use this with the `clerkJs` prop in ClerkProvider for dependency-managed pinning.
*
* @example
* ```tsx
* import { clerkJs } from '@clerk/clerk-js-pinned';
* <ClerkProvider clerkJs={clerkJs} />
* ```
*/
export const clerkJs = {
version: PACKAGE_VERSION,
} as ClerkJs;

// Re-export the type for consumers who need it
export type { ClerkJs } from '@clerk/shared/types';
24 changes: 24 additions & 0 deletions packages/clerk-js-pinned/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"rootDir": "src",
"verbatimModuleSyntax": true,
"target": "es2022",
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"outDir": "dist",
"noUnusedLocals": true,
"moduleResolution": "bundler",
"moduleDetection": "force",
"module": "preserve",
"lib": ["ES2023"],
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"emitDeclarationOnly": true,
"declaration": true,
"declarationMap": true
},
"exclude": ["node_modules", "dist"],
"include": ["src"]
}
20 changes: 20 additions & 0 deletions packages/clerk-js-pinned/tsdown.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { defineConfig } from 'tsdown';

import packageJson from './package.json' with { type: 'json' };

export default defineConfig({
entry: ['./src/index.ts'],
outDir: './dist',
dts: false, // We use dts-bundle-generator for bundled types
sourcemap: true,
clean: true,
target: 'es2022',
platform: 'neutral',
format: ['cjs', 'esm'],
minify: false,
external: [],
define: {
PACKAGE_NAME: `"${packageJson.name}"`,
PACKAGE_VERSION: `"${packageJson.version}"`,
},
});
10 changes: 8 additions & 2 deletions packages/shared/src/loadClerkJsScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export type LoadClerkJsScriptOptions = {
clerkJSUrl?: string;
clerkJSVariant?: 'headless' | '';
clerkJSVersion?: string;
/**
* Branded object for pinning @clerk/clerk-js version.
* Takes precedence over clerkJSVersion if both are provided.
*/
clerkJs?: { version: string };
sdkMetadata?: SDKMetadata;
proxyUrl?: string;
domain?: string;
Expand Down Expand Up @@ -217,15 +222,16 @@ export const loadClerkUiScript = async (opts?: LoadClerkUiScriptOptions): Promis
};

export const clerkJsScriptUrl = (opts: LoadClerkJsScriptOptions) => {
const { clerkJSUrl, clerkJSVariant, clerkJSVersion, proxyUrl, domain, publishableKey } = opts;
const { clerkJSUrl, clerkJSVariant, clerkJSVersion, clerkJs, proxyUrl, domain, publishableKey } = opts;

if (clerkJSUrl) {
return clerkJSUrl;
}

const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });
const variant = clerkJSVariant ? `${clerkJSVariant.replace(/\.+$/, '')}.` : '';
const version = versionSelector(clerkJSVersion);
// clerkJs object takes precedence over clerkJSVersion string
const version = versionSelector(clerkJs?.version ?? clerkJSVersion);
return `https://${scriptHost}/npm/@clerk/clerk-js@${version}/dist/clerk.${variant}browser.js`;
};

Expand Down
80 changes: 80 additions & 0 deletions packages/shared/src/types/branded.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Branded/Tagged type utilities for creating nominal types.
* These are used to ensure only official Clerk objects can be passed to certain props.
*/

declare const Tags: unique symbol;

/**
* Creates a branded/tagged type that prevents arbitrary objects from being assigned.
* The tag exists only at the type level and has no runtime overhead.
*
* @example
* ```typescript
* type UserId = Tagged<string, 'UserId'>;
* const userId: UserId = 'user_123' as UserId;
* ```
*/
export type Tagged<BaseType, Tag extends PropertyKey> = BaseType & { [Tags]: { [K in Tag]: void } };

/**
* Branded type for Clerk JS version pinning objects.
* Used with the `clerkJs` prop in ClerkProvider.
*
* @example
* ```typescript
* import { clerkJs } from '@clerk/clerk-js-pinned';
* <ClerkProvider clerkJs={clerkJs} />
* ```
*/
export type ClerkJs = Tagged<
{
/**
* The version string of @clerk/clerk-js to load from CDN.
*/
version: string;
},
'ClerkJs'
>;

/**
* Branded type for Clerk UI version pinning objects.
* Carries appearance type information via phantom property for type-safe customization.
*
* @example
* ```typescript
* import { ui } from '@clerk/ui-pinned';
* <ClerkProvider ui={ui} appearance={{ ... }} />
* ```
*/
export type Ui<A = any> = Tagged<
{
/**
* The version string of @clerk/ui to load from CDN.
*/
version: string;
/**
* Optional custom URL to load @clerk/ui from.
*/
url?: string;
/**
* Phantom property for type-level appearance inference.
* This property never exists at runtime.
* @internal
*/
__appearanceType?: A;
},
'ClerkUi'
>;

/**
* Extracts the appearance type from a Ui object. We have 3 cases:
* - If the Ui type has __appearanceType with a specific type, extract it
* - If __appearanceType is 'any', fallback to base Appearance type
* - Otherwise, fallback to the base Appearance type
*/
export type ExtractAppearanceType<T, Default> = T extends { __appearanceType?: infer A }
? 0 extends 1 & A // Check if A is 'any' (this trick works because 1 & any = any, and 0 extends any)
? Default
: A
: Default;
22 changes: 19 additions & 3 deletions packages/shared/src/types/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2423,6 +2423,17 @@ export type IsomorphicClerkOptions = Without<ClerkOptions, 'isSatellite'> & {
* The npm version for `@clerk/clerk-js`.
*/
clerkJSVersion?: string;
/**
* Branded object for pinning @clerk/clerk-js version.
* Import from `@clerk/clerk-js-pinned` for dependency-managed version pinning.
*
* @example
* ```tsx
* import { clerkJs } from '@clerk/clerk-js-pinned';
* <ClerkProvider clerkJs={clerkJs} />
* ```
*/
clerkJs?: { version: string };
/**
* The URL that `@clerk/ui` should be hot-loaded from.
*/
Expand All @@ -2441,9 +2452,14 @@ export type IsomorphicClerkOptions = Without<ClerkOptions, 'isSatellite'> & {
*/
nonce?: string;
/**
* @internal
* This is a structural-only type for the `ui` object that can be passed
* to Clerk.load() and ClerkProvider
* Branded object for pinning @clerk/ui version with full appearance type support.
* Import from `@clerk/ui-pinned` for dependency-managed version pinning.
*
* @example
* ```tsx
* import { ui } from '@clerk/ui-pinned';
* <ClerkProvider ui={ui} appearance={{ ... }} />
* ```
*/
ui?: { version: string; url?: string };
} & MultiDomainAndOrProxy;
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type * from './apiKeys';
export type * from './branded';
export type * from './apiKeysSettings';
export type * from './attributes';
export type * from './authConfig';
Expand Down
66 changes: 66 additions & 0 deletions packages/ui-pinned/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"name": "@clerk/ui-pinned",
"version": "0.0.1",
"description": "Lightweight package for pinning @clerk/ui version via dependency management with full type support",
"keywords": [
"clerk",
"ui",
"version",
"pinning",
"theming"
],
"homepage": "https://clerk.com/",
"bugs": {
"url": "https://github.com/clerk/javascript/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/clerk/javascript.git",
"directory": "packages/ui-pinned"
},
"license": "MIT",
"author": "Clerk",
"sideEffects": false,
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsdown && pnpm build:dts",
"build:dts": "dts-bundle-generator -o dist/index.d.ts src/index.ts --no-check && cp dist/index.d.ts dist/index.d.cts",
"clean": "rimraf ./dist",
"format": "node ../../scripts/format-package.mjs",
"format:check": "node ../../scripts/format-package.mjs --check",
"lint": "eslint src",
"lint:attw": "attw --pack . --profile node16",
"lint:publint": "publint"
},
"devDependencies": {
"@clerk/shared": "workspace:^",
"@clerk/ui": "workspace:^",
"dts-bundle-generator": "^9.5.1",
"tsdown": "catalog:repo"
},
"engines": {
"node": ">=20.9.0"
},
"publishConfig": {
"access": "public"
}
}
Loading
Loading