Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/metal-lions-switch.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Introduce `getClerkJsMajorVersionOrTag()`, `getScriptUrl()`, `callWithRetry()`
5 changes: 5 additions & 0 deletions .changeset/silent-fishes-change.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Replace utilities with @clerk/shared exports
25,039 changes: 10,855 additions & 14,184 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,6 +44,7 @@
"test:cloudflare-workerd": "tests/cloudflare-workerd/run.sh"
},
"dependencies": {
"@clerk/shared": "^0.23.1",
"@clerk/types": "^3.52.1",
"@peculiar/webcrypto": "1.4.1",
"@types/node": "16.18.6",
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}

export {};
4 changes: 2 additions & 2 deletions packages/backend/src/redirections.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,7 +75,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignIn({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand DownExpand Up@@ -221,7 +221,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignUp({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/redirections.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
import { missingPublishableKeyErrorMessage } from './shared';
import { parsePublishableKey } from './shared';
import { errorThrower, parsePublishableKey } from './util/shared';

type RedirectAdapter = (url: string) => any;

Expand DownExpand Up@@ -46,7 +45,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignUp = ({ returnBackUrl }: SignUpParams = {}) => {
if (!signUpUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
Expand All@@ -55,7 +54,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignIn = ({ returnBackUrl }: SignInParams = {}) => {
if (!signInUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/shared/index.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/backend/src/shared/isDevOrStagingUrl.ts

This file was deleted.

56 changes: 0 additions & 56 deletions packages/backend/src/shared/parsePublishableKey.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/backend/src/shared/url.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/backend/src/tokens/interstitial.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,15 @@ import { API_VERSION, USER_AGENT } from '../constants';
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { joinPaths } from '../util/path';
import {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevOrStagingUrl,
parsePublishableKey,
} from '../shared';
import { joinPaths } from '../util/path';
} from '../util/shared';
import { TokenVerificationError, TokenVerificationErrorAction, TokenVerificationErrorReason } from './errors';
import type { DebugRequestSate } from './request';

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/interstitialRule.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../shared';
import { checkCrossOrigin } from '../util/request';
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
import type { AuthenticateRequestOptions } from './request';
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@ import { API_URL, API_VERSION, MAX_CACHE_LAST_UPDATED_AT_SECONDS } from '../cons
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { callWithRetry } from '../shared';
import { joinPaths } from '../util/path';
import { getErrorObjectByCode } from '../util/request';
import { callWithRetry } from '../util/shared';
import {
TokenVerificationError,
TokenVerificationErrorAction,
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/request.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import { API_URL, API_VERSION, constants } from '../constants';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../shared';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
import type { TokenCarrier } from './errors';
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/util/shared.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
export {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevelopmentFromApiKey,
isProductionFromApiKey,
parsePublishableKey,
} from '@clerk/shared';

import { buildErrorThrower } from '@clerk/shared';
// TODO: replace packageName with `${PACKAGE_NAME}@${PACKAGE_VERSION}` from tsup.config.ts
export const errorThrower = buildErrorThrower({ packageName: '@clerk/backend' });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will fix this in another PR that will update how we build and run tests in backend package (currently tsup is not used in tests)


import { createDevOrStagingUrlCache } from '@clerk/shared';
export const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,12 @@ function wait(ms: number) {

const MAX_NUMBER_OF_RETRIES = 5;

// TODO: Move this to @clerk/shared and reuse it with @clerk/clerk-js
/**
* Retry callback function every few hundred ms (with an exponential backoff
* based on the current attempt) until the maximum attempts has reached or
* the callback is executed successfully. The default number of maximum
* attempts is 5 and retries are triggered when callback throws an error.
*/
export async function callWithRetry<T>(
Comment thread
dimkl marked this conversation as resolved.
fn: (...args: unknown[]) => Promise<T>,
attempt = 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/utils/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,3 +24,5 @@ export * from './globs';
export * from './loadScript';
export * from './runtimeEnvironment';
export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated';
export { callWithRetry } from './callWithRetry';
export { isDevelopmentFromApiKey, isProductionFromApiKey, isStaging } from './instance';
1 change: 1 addition & 0 deletions packages/shared/src/utils/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ export function isLegacyFrontendApiKey(key: string) {
}

export function createDevOrStagingUrlCache() {
// TODO: Check if we can merge it with `./instance.ts#isStaging()`
const DEV_OR_STAGING_SUFFIXES = [

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will cater this in another iteration where we will evaluate the exports of the @clerk/shared

'.lcl.dev',
'.stg.dev',
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/utils/url.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { isStaging } from './instance';

export function parseSearchParams(queryString = ''): URLSearchParams {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
Expand DownExpand Up@@ -25,3 +27,40 @@ export function addClerkPrefix(str: string | undefined) {
const stripped = str.replace(regex, '');
return `clerk.${stripped}`;
}

/**
*
* Retrieve the clerk-js major tag using the major version from the pkgVersion
* param or use the frontendApi to determine if the staging tag should be used.
* The default tag is `latest` and a `next` pkgVersion also exists to retrieve
* the next canary release.
*/
export const getClerkJsMajorVersionOrTag = (frontendApi: string, pkgVersion?: string) => {
Comment thread
dimkl marked this conversation as resolved.
if (!pkgVersion && isStaging(frontendApi)) {
return 'staging';
}

if (!pkgVersion) {
return 'latest';
}

if (pkgVersion.includes('next')) {
return 'next';
}

return pkgVersion.split('.')[0] || 'latest';
};

/**
*
* Retrieve the clerk-js script url from the frontendApi and the major tag
* using the {@link getClerkJsMajorVersionOrTag} or a provided clerkJSVersion tag.
*/
export const getScriptUrl = (
Comment thread
dimkl marked this conversation as resolved.
frontendApi: string,
{ pkgVersion, clerkJSVersion }: { pkgVersion?: string; clerkJSVersion?: string },
) => {
const noSchemeFrontendApi = frontendApi.replace(/http(s)?:\/\//, '');
const major = getClerkJsMajorVersionOrTag(frontendApi, pkgVersion);
return `https://${noSchemeFrontendApi}/npm/@clerk/clerk-js@${clerkJSVersion || major}/dist/clerk.browser.js`;
};
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(shared, nextjs): Support importing @clerk/shared in @clerk/backend by dimkl · Pull Request #1769 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/metal-lions-switch.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Introduce `getClerkJsMajorVersionOrTag()`, `getScriptUrl()`, `callWithRetry()`
5 changes: 5 additions & 0 deletions .changeset/silent-fishes-change.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Replace utilities with @clerk/shared exports
25,039 changes: 10,855 additions & 14,184 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,6 +44,7 @@
"test:cloudflare-workerd": "tests/cloudflare-workerd/run.sh"
},
"dependencies": {
"@clerk/shared": "^0.23.1",
"@clerk/types": "^3.52.1",
"@peculiar/webcrypto": "1.4.1",
"@types/node": "16.18.6",
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}

export {};
4 changes: 2 additions & 2 deletions packages/backend/src/redirections.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,7 +75,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignIn({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand DownExpand Up@@ -221,7 +221,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignUp({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/redirections.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
import { missingPublishableKeyErrorMessage } from './shared';
import { parsePublishableKey } from './shared';
import { errorThrower, parsePublishableKey } from './util/shared';

type RedirectAdapter = (url: string) => any;

Expand DownExpand Up@@ -46,7 +45,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignUp = ({ returnBackUrl }: SignUpParams = {}) => {
if (!signUpUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
Expand All@@ -55,7 +54,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignIn = ({ returnBackUrl }: SignInParams = {}) => {
if (!signInUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/shared/index.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/backend/src/shared/isDevOrStagingUrl.ts

This file was deleted.

56 changes: 0 additions & 56 deletions packages/backend/src/shared/parsePublishableKey.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/backend/src/shared/url.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/backend/src/tokens/interstitial.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,15 @@ import { API_VERSION, USER_AGENT } from '../constants';
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { joinPaths } from '../util/path';
import {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevOrStagingUrl,
parsePublishableKey,
} from '../shared';
import { joinPaths } from '../util/path';
} from '../util/shared';
import { TokenVerificationError, TokenVerificationErrorAction, TokenVerificationErrorReason } from './errors';
import type { DebugRequestSate } from './request';

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/interstitialRule.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../shared';
import { checkCrossOrigin } from '../util/request';
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
import type { AuthenticateRequestOptions } from './request';
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@ import { API_URL, API_VERSION, MAX_CACHE_LAST_UPDATED_AT_SECONDS } from '../cons
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { callWithRetry } from '../shared';
import { joinPaths } from '../util/path';
import { getErrorObjectByCode } from '../util/request';
import { callWithRetry } from '../util/shared';
import {
TokenVerificationError,
TokenVerificationErrorAction,
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/request.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import { API_URL, API_VERSION, constants } from '../constants';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../shared';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
import type { TokenCarrier } from './errors';
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/util/shared.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
export {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevelopmentFromApiKey,
isProductionFromApiKey,
parsePublishableKey,
} from '@clerk/shared';

import { buildErrorThrower } from '@clerk/shared';
// TODO: replace packageName with `${PACKAGE_NAME}@${PACKAGE_VERSION}` from tsup.config.ts
export const errorThrower = buildErrorThrower({ packageName: '@clerk/backend' });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will fix this in another PR that will update how we build and run tests in backend package (currently tsup is not used in tests)


import { createDevOrStagingUrlCache } from '@clerk/shared';
export const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,12 @@ function wait(ms: number) {

const MAX_NUMBER_OF_RETRIES = 5;

// TODO: Move this to @clerk/shared and reuse it with @clerk/clerk-js
/**
* Retry callback function every few hundred ms (with an exponential backoff
* based on the current attempt) until the maximum attempts has reached or
* the callback is executed successfully. The default number of maximum
* attempts is 5 and retries are triggered when callback throws an error.
*/
export async function callWithRetry<T>(
Comment thread
dimkl marked this conversation as resolved.
fn: (...args: unknown[]) => Promise<T>,
attempt = 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/utils/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,3 +24,5 @@ export * from './globs';
export * from './loadScript';
export * from './runtimeEnvironment';
export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated';
export { callWithRetry } from './callWithRetry';
export { isDevelopmentFromApiKey, isProductionFromApiKey, isStaging } from './instance';
1 change: 1 addition & 0 deletions packages/shared/src/utils/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ export function isLegacyFrontendApiKey(key: string) {
}

export function createDevOrStagingUrlCache() {
// TODO: Check if we can merge it with `./instance.ts#isStaging()`
const DEV_OR_STAGING_SUFFIXES = [

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will cater this in another iteration where we will evaluate the exports of the @clerk/shared

'.lcl.dev',
'.stg.dev',
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/utils/url.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { isStaging } from './instance';

export function parseSearchParams(queryString = ''): URLSearchParams {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
Expand DownExpand Up@@ -25,3 +27,40 @@ export function addClerkPrefix(str: string | undefined) {
const stripped = str.replace(regex, '');
return `clerk.${stripped}`;
}

/**
*
* Retrieve the clerk-js major tag using the major version from the pkgVersion
* param or use the frontendApi to determine if the staging tag should be used.
* The default tag is `latest` and a `next` pkgVersion also exists to retrieve
* the next canary release.
*/
export const getClerkJsMajorVersionOrTag = (frontendApi: string, pkgVersion?: string) => {
Comment thread
dimkl marked this conversation as resolved.
if (!pkgVersion && isStaging(frontendApi)) {
return 'staging';
}

if (!pkgVersion) {
return 'latest';
}

if (pkgVersion.includes('next')) {
return 'next';
}

return pkgVersion.split('.')[0] || 'latest';
};

/**
*
* Retrieve the clerk-js script url from the frontendApi and the major tag
* using the {@link getClerkJsMajorVersionOrTag} or a provided clerkJSVersion tag.
*/
export const getScriptUrl = (
Comment thread
dimkl marked this conversation as resolved.
frontendApi: string,
{ pkgVersion, clerkJSVersion }: { pkgVersion?: string; clerkJSVersion?: string },
) => {
const noSchemeFrontendApi = frontendApi.replace(/http(s)?:\/\//, '');
const major = getClerkJsMajorVersionOrTag(frontendApi, pkgVersion);
return `https://${noSchemeFrontendApi}/npm/@clerk/clerk-js@${clerkJSVersion || major}/dist/clerk.browser.js`;
};
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(shared, nextjs): Support importing @clerk/shared in @clerk/backend by dimkl · Pull Request #1769 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/metal-lions-switch.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Introduce `getClerkJsMajorVersionOrTag()`, `getScriptUrl()`, `callWithRetry()`
5 changes: 5 additions & 0 deletions .changeset/silent-fishes-change.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Replace utilities with @clerk/shared exports
25,039 changes: 10,855 additions & 14,184 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,6 +44,7 @@
"test:cloudflare-workerd": "tests/cloudflare-workerd/run.sh"
},
"dependencies": {
"@clerk/shared": "^0.23.1",
"@clerk/types": "^3.52.1",
"@peculiar/webcrypto": "1.4.1",
"@types/node": "16.18.6",
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}

export {};
4 changes: 2 additions & 2 deletions packages/backend/src/redirections.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,7 +75,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignIn({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand DownExpand Up@@ -221,7 +221,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignUp({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/redirections.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
import { missingPublishableKeyErrorMessage } from './shared';
import { parsePublishableKey } from './shared';
import { errorThrower, parsePublishableKey } from './util/shared';

type RedirectAdapter = (url: string) => any;

Expand DownExpand Up@@ -46,7 +45,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignUp = ({ returnBackUrl }: SignUpParams = {}) => {
if (!signUpUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
Expand All@@ -55,7 +54,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignIn = ({ returnBackUrl }: SignInParams = {}) => {
if (!signInUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/shared/index.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/backend/src/shared/isDevOrStagingUrl.ts

This file was deleted.

56 changes: 0 additions & 56 deletions packages/backend/src/shared/parsePublishableKey.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/backend/src/shared/url.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/backend/src/tokens/interstitial.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,15 @@ import { API_VERSION, USER_AGENT } from '../constants';
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { joinPaths } from '../util/path';
import {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevOrStagingUrl,
parsePublishableKey,
} from '../shared';
import { joinPaths } from '../util/path';
} from '../util/shared';
import { TokenVerificationError, TokenVerificationErrorAction, TokenVerificationErrorReason } from './errors';
import type { DebugRequestSate } from './request';

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/interstitialRule.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../shared';
import { checkCrossOrigin } from '../util/request';
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
import type { AuthenticateRequestOptions } from './request';
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@ import { API_URL, API_VERSION, MAX_CACHE_LAST_UPDATED_AT_SECONDS } from '../cons
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { callWithRetry } from '../shared';
import { joinPaths } from '../util/path';
import { getErrorObjectByCode } from '../util/request';
import { callWithRetry } from '../util/shared';
import {
TokenVerificationError,
TokenVerificationErrorAction,
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/request.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import { API_URL, API_VERSION, constants } from '../constants';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../shared';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
import type { TokenCarrier } from './errors';
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/util/shared.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
export {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevelopmentFromApiKey,
isProductionFromApiKey,
parsePublishableKey,
} from '@clerk/shared';

import { buildErrorThrower } from '@clerk/shared';
// TODO: replace packageName with `${PACKAGE_NAME}@${PACKAGE_VERSION}` from tsup.config.ts
export const errorThrower = buildErrorThrower({ packageName: '@clerk/backend' });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will fix this in another PR that will update how we build and run tests in backend package (currently tsup is not used in tests)


import { createDevOrStagingUrlCache } from '@clerk/shared';
export const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,12 @@ function wait(ms: number) {

const MAX_NUMBER_OF_RETRIES = 5;

// TODO: Move this to @clerk/shared and reuse it with @clerk/clerk-js
/**
* Retry callback function every few hundred ms (with an exponential backoff
* based on the current attempt) until the maximum attempts has reached or
* the callback is executed successfully. The default number of maximum
* attempts is 5 and retries are triggered when callback throws an error.
*/
export async function callWithRetry<T>(
Comment thread
dimkl marked this conversation as resolved.
fn: (...args: unknown[]) => Promise<T>,
attempt = 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/utils/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,3 +24,5 @@ export * from './globs';
export * from './loadScript';
export * from './runtimeEnvironment';
export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated';
export { callWithRetry } from './callWithRetry';
export { isDevelopmentFromApiKey, isProductionFromApiKey, isStaging } from './instance';
1 change: 1 addition & 0 deletions packages/shared/src/utils/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ export function isLegacyFrontendApiKey(key: string) {
}

export function createDevOrStagingUrlCache() {
// TODO: Check if we can merge it with `./instance.ts#isStaging()`
const DEV_OR_STAGING_SUFFIXES = [

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will cater this in another iteration where we will evaluate the exports of the @clerk/shared

'.lcl.dev',
'.stg.dev',
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/utils/url.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { isStaging } from './instance';

export function parseSearchParams(queryString = ''): URLSearchParams {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
Expand DownExpand Up@@ -25,3 +27,40 @@ export function addClerkPrefix(str: string | undefined) {
const stripped = str.replace(regex, '');
return `clerk.${stripped}`;
}

/**
*
* Retrieve the clerk-js major tag using the major version from the pkgVersion
* param or use the frontendApi to determine if the staging tag should be used.
* The default tag is `latest` and a `next` pkgVersion also exists to retrieve
* the next canary release.
*/
export const getClerkJsMajorVersionOrTag = (frontendApi: string, pkgVersion?: string) => {
Comment thread
dimkl marked this conversation as resolved.
if (!pkgVersion && isStaging(frontendApi)) {
return 'staging';
}

if (!pkgVersion) {
return 'latest';
}

if (pkgVersion.includes('next')) {
return 'next';
}

return pkgVersion.split('.')[0] || 'latest';
};

/**
*
* Retrieve the clerk-js script url from the frontendApi and the major tag
* using the {@link getClerkJsMajorVersionOrTag} or a provided clerkJSVersion tag.
*/
export const getScriptUrl = (
Comment thread
dimkl marked this conversation as resolved.
frontendApi: string,
{ pkgVersion, clerkJSVersion }: { pkgVersion?: string; clerkJSVersion?: string },
) => {
const noSchemeFrontendApi = frontendApi.replace(/http(s)?:\/\//, '');
const major = getClerkJsMajorVersionOrTag(frontendApi, pkgVersion);
return `https://${noSchemeFrontendApi}/npm/@clerk/clerk-js@${clerkJSVersion || major}/dist/clerk.browser.js`;
};
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(shared, nextjs): Support importing @clerk/shared in @clerk/backend by dimkl · Pull Request #1769 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/metal-lions-switch.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Introduce `getClerkJsMajorVersionOrTag()`, `getScriptUrl()`, `callWithRetry()`
5 changes: 5 additions & 0 deletions .changeset/silent-fishes-change.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Replace utilities with @clerk/shared exports
25,039 changes: 10,855 additions & 14,184 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,6 +44,7 @@
"test:cloudflare-workerd": "tests/cloudflare-workerd/run.sh"
},
"dependencies": {
"@clerk/shared": "^0.23.1",
"@clerk/types": "^3.52.1",
"@peculiar/webcrypto": "1.4.1",
"@types/node": "16.18.6",
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}

export {};
4 changes: 2 additions & 2 deletions packages/backend/src/redirections.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,7 +75,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignIn({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand DownExpand Up@@ -221,7 +221,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignUp({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/redirections.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
import { missingPublishableKeyErrorMessage } from './shared';
import { parsePublishableKey } from './shared';
import { errorThrower, parsePublishableKey } from './util/shared';

type RedirectAdapter = (url: string) => any;

Expand DownExpand Up@@ -46,7 +45,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignUp = ({ returnBackUrl }: SignUpParams = {}) => {
if (!signUpUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
Expand All@@ -55,7 +54,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignIn = ({ returnBackUrl }: SignInParams = {}) => {
if (!signInUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/shared/index.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/backend/src/shared/isDevOrStagingUrl.ts

This file was deleted.

56 changes: 0 additions & 56 deletions packages/backend/src/shared/parsePublishableKey.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/backend/src/shared/url.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/backend/src/tokens/interstitial.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,15 @@ import { API_VERSION, USER_AGENT } from '../constants';
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { joinPaths } from '../util/path';
import {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevOrStagingUrl,
parsePublishableKey,
} from '../shared';
import { joinPaths } from '../util/path';
} from '../util/shared';
import { TokenVerificationError, TokenVerificationErrorAction, TokenVerificationErrorReason } from './errors';
import type { DebugRequestSate } from './request';

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/interstitialRule.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../shared';
import { checkCrossOrigin } from '../util/request';
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
import type { AuthenticateRequestOptions } from './request';
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@ import { API_URL, API_VERSION, MAX_CACHE_LAST_UPDATED_AT_SECONDS } from '../cons
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { callWithRetry } from '../shared';
import { joinPaths } from '../util/path';
import { getErrorObjectByCode } from '../util/request';
import { callWithRetry } from '../util/shared';
import {
TokenVerificationError,
TokenVerificationErrorAction,
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/request.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import { API_URL, API_VERSION, constants } from '../constants';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../shared';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
import type { TokenCarrier } from './errors';
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/util/shared.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
export {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevelopmentFromApiKey,
isProductionFromApiKey,
parsePublishableKey,
} from '@clerk/shared';

import { buildErrorThrower } from '@clerk/shared';
// TODO: replace packageName with `${PACKAGE_NAME}@${PACKAGE_VERSION}` from tsup.config.ts
export const errorThrower = buildErrorThrower({ packageName: '@clerk/backend' });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will fix this in another PR that will update how we build and run tests in backend package (currently tsup is not used in tests)


import { createDevOrStagingUrlCache } from '@clerk/shared';
export const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,12 @@ function wait(ms: number) {

const MAX_NUMBER_OF_RETRIES = 5;

// TODO: Move this to @clerk/shared and reuse it with @clerk/clerk-js
/**
* Retry callback function every few hundred ms (with an exponential backoff
* based on the current attempt) until the maximum attempts has reached or
* the callback is executed successfully. The default number of maximum
* attempts is 5 and retries are triggered when callback throws an error.
*/
export async function callWithRetry<T>(
Comment thread
dimkl marked this conversation as resolved.
fn: (...args: unknown[]) => Promise<T>,
attempt = 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/utils/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,3 +24,5 @@ export * from './globs';
export * from './loadScript';
export * from './runtimeEnvironment';
export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated';
export { callWithRetry } from './callWithRetry';
export { isDevelopmentFromApiKey, isProductionFromApiKey, isStaging } from './instance';
1 change: 1 addition & 0 deletions packages/shared/src/utils/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ export function isLegacyFrontendApiKey(key: string) {
}

export function createDevOrStagingUrlCache() {
// TODO: Check if we can merge it with `./instance.ts#isStaging()`
const DEV_OR_STAGING_SUFFIXES = [

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will cater this in another iteration where we will evaluate the exports of the @clerk/shared

'.lcl.dev',
'.stg.dev',
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/utils/url.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { isStaging } from './instance';

export function parseSearchParams(queryString = ''): URLSearchParams {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
Expand DownExpand Up@@ -25,3 +27,40 @@ export function addClerkPrefix(str: string | undefined) {
const stripped = str.replace(regex, '');
return `clerk.${stripped}`;
}

/**
*
* Retrieve the clerk-js major tag using the major version from the pkgVersion
* param or use the frontendApi to determine if the staging tag should be used.
* The default tag is `latest` and a `next` pkgVersion also exists to retrieve
* the next canary release.
*/
export const getClerkJsMajorVersionOrTag = (frontendApi: string, pkgVersion?: string) => {
Comment thread
dimkl marked this conversation as resolved.
if (!pkgVersion && isStaging(frontendApi)) {
return 'staging';
}

if (!pkgVersion) {
return 'latest';
}

if (pkgVersion.includes('next')) {
return 'next';
}

return pkgVersion.split('.')[0] || 'latest';
};

/**
*
* Retrieve the clerk-js script url from the frontendApi and the major tag
* using the {@link getClerkJsMajorVersionOrTag} or a provided clerkJSVersion tag.
*/
export const getScriptUrl = (
Comment thread
dimkl marked this conversation as resolved.
frontendApi: string,
{ pkgVersion, clerkJSVersion }: { pkgVersion?: string; clerkJSVersion?: string },
) => {
const noSchemeFrontendApi = frontendApi.replace(/http(s)?:\/\//, '');
const major = getClerkJsMajorVersionOrTag(frontendApi, pkgVersion);
return `https://${noSchemeFrontendApi}/npm/@clerk/clerk-js@${clerkJSVersion || major}/dist/clerk.browser.js`;
};
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(shared, nextjs): Support importing @clerk/shared in @clerk/backend by dimkl · Pull Request #1769 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/metal-lions-switch.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Introduce `getClerkJsMajorVersionOrTag()`, `getScriptUrl()`, `callWithRetry()`
5 changes: 5 additions & 0 deletions .changeset/silent-fishes-change.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Replace utilities with @clerk/shared exports
25,039 changes: 10,855 additions & 14,184 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,6 +44,7 @@
"test:cloudflare-workerd": "tests/cloudflare-workerd/run.sh"
},
"dependencies": {
"@clerk/shared": "^0.23.1",
"@clerk/types": "^3.52.1",
"@peculiar/webcrypto": "1.4.1",
"@types/node": "16.18.6",
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}

export {};
4 changes: 2 additions & 2 deletions packages/backend/src/redirections.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,7 +75,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignIn({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand DownExpand Up@@ -221,7 +221,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignUp({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/redirections.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
import { missingPublishableKeyErrorMessage } from './shared';
import { parsePublishableKey } from './shared';
import { errorThrower, parsePublishableKey } from './util/shared';

type RedirectAdapter = (url: string) => any;

Expand DownExpand Up@@ -46,7 +45,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignUp = ({ returnBackUrl }: SignUpParams = {}) => {
if (!signUpUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
Expand All@@ -55,7 +54,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignIn = ({ returnBackUrl }: SignInParams = {}) => {
if (!signInUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/shared/index.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/backend/src/shared/isDevOrStagingUrl.ts

This file was deleted.

56 changes: 0 additions & 56 deletions packages/backend/src/shared/parsePublishableKey.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/backend/src/shared/url.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/backend/src/tokens/interstitial.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,15 @@ import { API_VERSION, USER_AGENT } from '../constants';
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { joinPaths } from '../util/path';
import {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevOrStagingUrl,
parsePublishableKey,
} from '../shared';
import { joinPaths } from '../util/path';
} from '../util/shared';
import { TokenVerificationError, TokenVerificationErrorAction, TokenVerificationErrorReason } from './errors';
import type { DebugRequestSate } from './request';

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/interstitialRule.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../shared';
import { checkCrossOrigin } from '../util/request';
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
import type { AuthenticateRequestOptions } from './request';
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@ import { API_URL, API_VERSION, MAX_CACHE_LAST_UPDATED_AT_SECONDS } from '../cons
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { callWithRetry } from '../shared';
import { joinPaths } from '../util/path';
import { getErrorObjectByCode } from '../util/request';
import { callWithRetry } from '../util/shared';
import {
TokenVerificationError,
TokenVerificationErrorAction,
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/request.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import { API_URL, API_VERSION, constants } from '../constants';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../shared';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
import type { TokenCarrier } from './errors';
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/util/shared.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
export {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevelopmentFromApiKey,
isProductionFromApiKey,
parsePublishableKey,
} from '@clerk/shared';

import { buildErrorThrower } from '@clerk/shared';
// TODO: replace packageName with `${PACKAGE_NAME}@${PACKAGE_VERSION}` from tsup.config.ts
export const errorThrower = buildErrorThrower({ packageName: '@clerk/backend' });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will fix this in another PR that will update how we build and run tests in backend package (currently tsup is not used in tests)


import { createDevOrStagingUrlCache } from '@clerk/shared';
export const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,12 @@ function wait(ms: number) {

const MAX_NUMBER_OF_RETRIES = 5;

// TODO: Move this to @clerk/shared and reuse it with @clerk/clerk-js
/**
* Retry callback function every few hundred ms (with an exponential backoff
* based on the current attempt) until the maximum attempts has reached or
* the callback is executed successfully. The default number of maximum
* attempts is 5 and retries are triggered when callback throws an error.
*/
export async function callWithRetry<T>(
Comment thread
dimkl marked this conversation as resolved.
fn: (...args: unknown[]) => Promise<T>,
attempt = 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/utils/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,3 +24,5 @@ export * from './globs';
export * from './loadScript';
export * from './runtimeEnvironment';
export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated';
export { callWithRetry } from './callWithRetry';
export { isDevelopmentFromApiKey, isProductionFromApiKey, isStaging } from './instance';
1 change: 1 addition & 0 deletions packages/shared/src/utils/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ export function isLegacyFrontendApiKey(key: string) {
}

export function createDevOrStagingUrlCache() {
// TODO: Check if we can merge it with `./instance.ts#isStaging()`
const DEV_OR_STAGING_SUFFIXES = [

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will cater this in another iteration where we will evaluate the exports of the @clerk/shared

'.lcl.dev',
'.stg.dev',
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/utils/url.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { isStaging } from './instance';

export function parseSearchParams(queryString = ''): URLSearchParams {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
Expand DownExpand Up@@ -25,3 +27,40 @@ export function addClerkPrefix(str: string | undefined) {
const stripped = str.replace(regex, '');
return `clerk.${stripped}`;
}

/**
*
* Retrieve the clerk-js major tag using the major version from the pkgVersion
* param or use the frontendApi to determine if the staging tag should be used.
* The default tag is `latest` and a `next` pkgVersion also exists to retrieve
* the next canary release.
*/
export const getClerkJsMajorVersionOrTag = (frontendApi: string, pkgVersion?: string) => {
Comment thread
dimkl marked this conversation as resolved.
if (!pkgVersion && isStaging(frontendApi)) {
return 'staging';
}

if (!pkgVersion) {
return 'latest';
}

if (pkgVersion.includes('next')) {
return 'next';
}

return pkgVersion.split('.')[0] || 'latest';
};

/**
*
* Retrieve the clerk-js script url from the frontendApi and the major tag
* using the {@link getClerkJsMajorVersionOrTag} or a provided clerkJSVersion tag.
*/
export const getScriptUrl = (
Comment thread
dimkl marked this conversation as resolved.
frontendApi: string,
{ pkgVersion, clerkJSVersion }: { pkgVersion?: string; clerkJSVersion?: string },
) => {
const noSchemeFrontendApi = frontendApi.replace(/http(s)?:\/\//, '');
const major = getClerkJsMajorVersionOrTag(frontendApi, pkgVersion);
return `https://${noSchemeFrontendApi}/npm/@clerk/clerk-js@${clerkJSVersion || major}/dist/clerk.browser.js`;
};
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(shared, nextjs): Support importing @clerk/shared in @clerk/backend by dimkl · Pull Request #1769 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/metal-lions-switch.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Introduce `getClerkJsMajorVersionOrTag()`, `getScriptUrl()`, `callWithRetry()`
5 changes: 5 additions & 0 deletions .changeset/silent-fishes-change.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Replace utilities with @clerk/shared exports
25,039 changes: 10,855 additions & 14,184 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,6 +44,7 @@
"test:cloudflare-workerd": "tests/cloudflare-workerd/run.sh"
},
"dependencies": {
"@clerk/shared": "^0.23.1",
"@clerk/types": "^3.52.1",
"@peculiar/webcrypto": "1.4.1",
"@types/node": "16.18.6",
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}

export {};
4 changes: 2 additions & 2 deletions packages/backend/src/redirections.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,7 +75,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignIn({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand DownExpand Up@@ -221,7 +221,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignUp({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/redirections.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
import { missingPublishableKeyErrorMessage } from './shared';
import { parsePublishableKey } from './shared';
import { errorThrower, parsePublishableKey } from './util/shared';

type RedirectAdapter = (url: string) => any;

Expand DownExpand Up@@ -46,7 +45,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignUp = ({ returnBackUrl }: SignUpParams = {}) => {
if (!signUpUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
Expand All@@ -55,7 +54,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignIn = ({ returnBackUrl }: SignInParams = {}) => {
if (!signInUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/shared/index.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/backend/src/shared/isDevOrStagingUrl.ts

This file was deleted.

56 changes: 0 additions & 56 deletions packages/backend/src/shared/parsePublishableKey.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/backend/src/shared/url.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/backend/src/tokens/interstitial.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,15 @@ import { API_VERSION, USER_AGENT } from '../constants';
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { joinPaths } from '../util/path';
import {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevOrStagingUrl,
parsePublishableKey,
} from '../shared';
import { joinPaths } from '../util/path';
} from '../util/shared';
import { TokenVerificationError, TokenVerificationErrorAction, TokenVerificationErrorReason } from './errors';
import type { DebugRequestSate } from './request';

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/interstitialRule.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../shared';
import { checkCrossOrigin } from '../util/request';
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
import type { AuthenticateRequestOptions } from './request';
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@ import { API_URL, API_VERSION, MAX_CACHE_LAST_UPDATED_AT_SECONDS } from '../cons
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { callWithRetry } from '../shared';
import { joinPaths } from '../util/path';
import { getErrorObjectByCode } from '../util/request';
import { callWithRetry } from '../util/shared';
import {
TokenVerificationError,
TokenVerificationErrorAction,
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/request.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import { API_URL, API_VERSION, constants } from '../constants';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../shared';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
import type { TokenCarrier } from './errors';
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/util/shared.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
export {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevelopmentFromApiKey,
isProductionFromApiKey,
parsePublishableKey,
} from '@clerk/shared';

import { buildErrorThrower } from '@clerk/shared';
// TODO: replace packageName with `${PACKAGE_NAME}@${PACKAGE_VERSION}` from tsup.config.ts
export const errorThrower = buildErrorThrower({ packageName: '@clerk/backend' });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will fix this in another PR that will update how we build and run tests in backend package (currently tsup is not used in tests)


import { createDevOrStagingUrlCache } from '@clerk/shared';
export const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,12 @@ function wait(ms: number) {

const MAX_NUMBER_OF_RETRIES = 5;

// TODO: Move this to @clerk/shared and reuse it with @clerk/clerk-js
/**
* Retry callback function every few hundred ms (with an exponential backoff
* based on the current attempt) until the maximum attempts has reached or
* the callback is executed successfully. The default number of maximum
* attempts is 5 and retries are triggered when callback throws an error.
*/
export async function callWithRetry<T>(
Comment thread
dimkl marked this conversation as resolved.
fn: (...args: unknown[]) => Promise<T>,
attempt = 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/utils/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,3 +24,5 @@ export * from './globs';
export * from './loadScript';
export * from './runtimeEnvironment';
export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated';
export { callWithRetry } from './callWithRetry';
export { isDevelopmentFromApiKey, isProductionFromApiKey, isStaging } from './instance';
1 change: 1 addition & 0 deletions packages/shared/src/utils/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ export function isLegacyFrontendApiKey(key: string) {
}

export function createDevOrStagingUrlCache() {
// TODO: Check if we can merge it with `./instance.ts#isStaging()`
const DEV_OR_STAGING_SUFFIXES = [

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will cater this in another iteration where we will evaluate the exports of the @clerk/shared

'.lcl.dev',
'.stg.dev',
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/utils/url.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { isStaging } from './instance';

export function parseSearchParams(queryString = ''): URLSearchParams {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
Expand DownExpand Up@@ -25,3 +27,40 @@ export function addClerkPrefix(str: string | undefined) {
const stripped = str.replace(regex, '');
return `clerk.${stripped}`;
}

/**
*
* Retrieve the clerk-js major tag using the major version from the pkgVersion
* param or use the frontendApi to determine if the staging tag should be used.
* The default tag is `latest` and a `next` pkgVersion also exists to retrieve
* the next canary release.
*/
export const getClerkJsMajorVersionOrTag = (frontendApi: string, pkgVersion?: string) => {
Comment thread
dimkl marked this conversation as resolved.
if (!pkgVersion && isStaging(frontendApi)) {
return 'staging';
}

if (!pkgVersion) {
return 'latest';
}

if (pkgVersion.includes('next')) {
return 'next';
}

return pkgVersion.split('.')[0] || 'latest';
};

/**
*
* Retrieve the clerk-js script url from the frontendApi and the major tag
* using the {@link getClerkJsMajorVersionOrTag} or a provided clerkJSVersion tag.
*/
export const getScriptUrl = (
Comment thread
dimkl marked this conversation as resolved.
frontendApi: string,
{ pkgVersion, clerkJSVersion }: { pkgVersion?: string; clerkJSVersion?: string },
) => {
const noSchemeFrontendApi = frontendApi.replace(/http(s)?:\/\//, '');
const major = getClerkJsMajorVersionOrTag(frontendApi, pkgVersion);
return `https://${noSchemeFrontendApi}/npm/@clerk/clerk-js@${clerkJSVersion || major}/dist/clerk.browser.js`;
};
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(shared, nextjs): Support importing @clerk/shared in @clerk/backend by dimkl · Pull Request #1769 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/metal-lions-switch.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Introduce `getClerkJsMajorVersionOrTag()`, `getScriptUrl()`, `callWithRetry()`
5 changes: 5 additions & 0 deletions .changeset/silent-fishes-change.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Replace utilities with @clerk/shared exports
25,039 changes: 10,855 additions & 14,184 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,6 +44,7 @@
"test:cloudflare-workerd": "tests/cloudflare-workerd/run.sh"
},
"dependencies": {
"@clerk/shared": "^0.23.1",
"@clerk/types": "^3.52.1",
"@peculiar/webcrypto": "1.4.1",
"@types/node": "16.18.6",
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}

export {};
4 changes: 2 additions & 2 deletions packages/backend/src/redirections.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,7 +75,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignIn({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand DownExpand Up@@ -221,7 +221,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignUp({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/redirections.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
import { missingPublishableKeyErrorMessage } from './shared';
import { parsePublishableKey } from './shared';
import { errorThrower, parsePublishableKey } from './util/shared';

type RedirectAdapter = (url: string) => any;

Expand DownExpand Up@@ -46,7 +45,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignUp = ({ returnBackUrl }: SignUpParams = {}) => {
if (!signUpUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
Expand All@@ -55,7 +54,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignIn = ({ returnBackUrl }: SignInParams = {}) => {
if (!signInUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/shared/index.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/backend/src/shared/isDevOrStagingUrl.ts

This file was deleted.

56 changes: 0 additions & 56 deletions packages/backend/src/shared/parsePublishableKey.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/backend/src/shared/url.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/backend/src/tokens/interstitial.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,15 @@ import { API_VERSION, USER_AGENT } from '../constants';
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { joinPaths } from '../util/path';
import {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevOrStagingUrl,
parsePublishableKey,
} from '../shared';
import { joinPaths } from '../util/path';
} from '../util/shared';
import { TokenVerificationError, TokenVerificationErrorAction, TokenVerificationErrorReason } from './errors';
import type { DebugRequestSate } from './request';

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/interstitialRule.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../shared';
import { checkCrossOrigin } from '../util/request';
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
import type { AuthenticateRequestOptions } from './request';
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@ import { API_URL, API_VERSION, MAX_CACHE_LAST_UPDATED_AT_SECONDS } from '../cons
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { callWithRetry } from '../shared';
import { joinPaths } from '../util/path';
import { getErrorObjectByCode } from '../util/request';
import { callWithRetry } from '../util/shared';
import {
TokenVerificationError,
TokenVerificationErrorAction,
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/request.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import { API_URL, API_VERSION, constants } from '../constants';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../shared';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
import type { TokenCarrier } from './errors';
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/util/shared.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
export {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevelopmentFromApiKey,
isProductionFromApiKey,
parsePublishableKey,
} from '@clerk/shared';

import { buildErrorThrower } from '@clerk/shared';
// TODO: replace packageName with `${PACKAGE_NAME}@${PACKAGE_VERSION}` from tsup.config.ts
export const errorThrower = buildErrorThrower({ packageName: '@clerk/backend' });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will fix this in another PR that will update how we build and run tests in backend package (currently tsup is not used in tests)


import { createDevOrStagingUrlCache } from '@clerk/shared';
export const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,12 @@ function wait(ms: number) {

const MAX_NUMBER_OF_RETRIES = 5;

// TODO: Move this to @clerk/shared and reuse it with @clerk/clerk-js
/**
* Retry callback function every few hundred ms (with an exponential backoff
* based on the current attempt) until the maximum attempts has reached or
* the callback is executed successfully. The default number of maximum
* attempts is 5 and retries are triggered when callback throws an error.
*/
export async function callWithRetry<T>(
Comment thread
dimkl marked this conversation as resolved.
fn: (...args: unknown[]) => Promise<T>,
attempt = 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/utils/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,3 +24,5 @@ export * from './globs';
export * from './loadScript';
export * from './runtimeEnvironment';
export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated';
export { callWithRetry } from './callWithRetry';
export { isDevelopmentFromApiKey, isProductionFromApiKey, isStaging } from './instance';
1 change: 1 addition & 0 deletions packages/shared/src/utils/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ export function isLegacyFrontendApiKey(key: string) {
}

export function createDevOrStagingUrlCache() {
// TODO: Check if we can merge it with `./instance.ts#isStaging()`
const DEV_OR_STAGING_SUFFIXES = [

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will cater this in another iteration where we will evaluate the exports of the @clerk/shared

'.lcl.dev',
'.stg.dev',
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/utils/url.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { isStaging } from './instance';

export function parseSearchParams(queryString = ''): URLSearchParams {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
Expand DownExpand Up@@ -25,3 +27,40 @@ export function addClerkPrefix(str: string | undefined) {
const stripped = str.replace(regex, '');
return `clerk.${stripped}`;
}

/**
*
* Retrieve the clerk-js major tag using the major version from the pkgVersion
* param or use the frontendApi to determine if the staging tag should be used.
* The default tag is `latest` and a `next` pkgVersion also exists to retrieve
* the next canary release.
*/
export const getClerkJsMajorVersionOrTag = (frontendApi: string, pkgVersion?: string) => {
Comment thread
dimkl marked this conversation as resolved.
if (!pkgVersion && isStaging(frontendApi)) {
return 'staging';
}

if (!pkgVersion) {
return 'latest';
}

if (pkgVersion.includes('next')) {
return 'next';
}

return pkgVersion.split('.')[0] || 'latest';
};

/**
*
* Retrieve the clerk-js script url from the frontendApi and the major tag
* using the {@link getClerkJsMajorVersionOrTag} or a provided clerkJSVersion tag.
*/
export const getScriptUrl = (
Comment thread
dimkl marked this conversation as resolved.
frontendApi: string,
{ pkgVersion, clerkJSVersion }: { pkgVersion?: string; clerkJSVersion?: string },
) => {
const noSchemeFrontendApi = frontendApi.replace(/http(s)?:\/\//, '');
const major = getClerkJsMajorVersionOrTag(frontendApi, pkgVersion);
return `https://${noSchemeFrontendApi}/npm/@clerk/clerk-js@${clerkJSVersion || major}/dist/clerk.browser.js`;
};
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(shared, nextjs): Support importing @clerk/shared in @clerk/backend by dimkl · Pull Request #1769 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/metal-lions-switch.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': minor
---

Introduce `getClerkJsMajorVersionOrTag()`, `getScriptUrl()`, `callWithRetry()`
5 changes: 5 additions & 0 deletions .changeset/silent-fishes-change.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Replace utilities with @clerk/shared exports
25,039 changes: 10,855 additions & 14,184 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/backend/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,6 +44,7 @@
"test:cloudflare-workerd": "tests/cloudflare-workerd/run.sh"
},
"dependencies": {
"@clerk/shared": "^0.23.1",
"@clerk/types": "^3.52.1",
"@peculiar/webcrypto": "1.4.1",
"@types/node": "16.18.6",
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
export {};

declare global {
const PACKAGE_NAME: string;
const PACKAGE_VERSION: string;
const __DEV__: boolean;
}

export {};
4 changes: 2 additions & 2 deletions packages/backend/src/redirections.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -75,7 +75,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignIn({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand DownExpand Up@@ -221,7 +221,7 @@ export default (QUnit: QUnit) => {
assert.raises(
() => redirectToSignUp({ returnBackUrl }),
new Error(
'Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
'@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.',
Comment thread
anagstef marked this conversation as resolved.
),
);
});
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/redirections.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
import { missingPublishableKeyErrorMessage } from './shared';
import { parsePublishableKey } from './shared';
import { errorThrower, parsePublishableKey } from './util/shared';

type RedirectAdapter = (url: string) => any;

Expand DownExpand Up@@ -46,7 +45,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignUp = ({ returnBackUrl }: SignUpParams = {}) => {
if (!signUpUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
Expand All@@ -55,7 +54,7 @@ export function redirect({ redirectAdapter, signUpUrl, signInUrl, frontendApi, p

const redirectToSignIn = ({ returnBackUrl }: SignInParams = {}) => {
if (!signInUrl && !accountsBaseUrl) {
throw new Error(missingPublishableKeyErrorMessage);
errorThrower.throwMissingPublishableKeyError();
}

const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
Expand Down
15 changes: 0 additions & 15 deletions packages/backend/src/shared/index.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/backend/src/shared/isDevOrStagingUrl.ts

This file was deleted.

56 changes: 0 additions & 56 deletions packages/backend/src/shared/parsePublishableKey.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/backend/src/shared/url.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/backend/src/tokens/interstitial.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,15 @@ import { API_VERSION, USER_AGENT } from '../constants';
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { joinPaths } from '../util/path';
import {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevOrStagingUrl,
parsePublishableKey,
} from '../shared';
import { joinPaths } from '../util/path';
} from '../util/shared';
import { TokenVerificationError, TokenVerificationErrorAction, TokenVerificationErrorReason } from './errors';
import type { DebugRequestSate } from './request';

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/interstitialRule.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../shared';
import { checkCrossOrigin } from '../util/request';
import { isDevelopmentFromApiKey, isProductionFromApiKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedIn, signedOut } from './authStatus';
import type { AuthenticateRequestOptions } from './request';
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,9 @@ import { API_URL, API_VERSION, MAX_CACHE_LAST_UPDATED_AT_SECONDS } from '../cons
// DO NOT CHANGE: Runtime needs to be imported as a default export so that we can stub its dependencies with Sinon.js
// For more information refer to https://sinonjs.org/how-to/stub-dependency/
import runtime from '../runtime';
import { callWithRetry } from '../shared';
import { joinPaths } from '../util/path';
import { getErrorObjectByCode } from '../util/request';
import { callWithRetry } from '../util/shared';
import {
TokenVerificationError,
TokenVerificationErrorAction,
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/request.ts
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
import { API_URL, API_VERSION, constants } from '../constants';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../shared';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { buildRequest, stripAuthorizationHeader } from '../util/IsomorphicRequest';
import { isDevelopmentFromApiKey, parsePublishableKey } from '../util/shared';
import type { RequestState } from './authStatus';
import { AuthErrorReason, interstitial, signedOut, unknownState } from './authStatus';
import type { TokenCarrier } from './errors';
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/util/shared.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
export {
addClerkPrefix,
callWithRetry,
getClerkJsMajorVersionOrTag,
getScriptUrl,
isDevelopmentFromApiKey,
isProductionFromApiKey,
parsePublishableKey,
} from '@clerk/shared';

import { buildErrorThrower } from '@clerk/shared';
// TODO: replace packageName with `${PACKAGE_NAME}@${PACKAGE_VERSION}` from tsup.config.ts
export const errorThrower = buildErrorThrower({ packageName: '@clerk/backend' });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will fix this in another PR that will update how we build and run tests in backend package (currently tsup is not used in tests)


import { createDevOrStagingUrlCache } from '@clerk/shared';
export const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,12 @@ function wait(ms: number) {

const MAX_NUMBER_OF_RETRIES = 5;

// TODO: Move this to @clerk/shared and reuse it with @clerk/clerk-js
/**
* Retry callback function every few hundred ms (with an exponential backoff
* based on the current attempt) until the maximum attempts has reached or
* the callback is executed successfully. The default number of maximum
* attempts is 5 and retries are triggered when callback throws an error.
*/
export async function callWithRetry<T>(
Comment thread
dimkl marked this conversation as resolved.
fn: (...args: unknown[]) => Promise<T>,
attempt = 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/utils/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,3 +24,5 @@ export * from './globs';
export * from './loadScript';
export * from './runtimeEnvironment';
export { deprecated, deprecatedProperty, deprecatedObjectProperty } from './deprecated';
export { callWithRetry } from './callWithRetry';
export { isDevelopmentFromApiKey, isProductionFromApiKey, isStaging } from './instance';
1 change: 1 addition & 0 deletions packages/shared/src/utils/keys.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ export function isLegacyFrontendApiKey(key: string) {
}

export function createDevOrStagingUrlCache() {
// TODO: Check if we can merge it with `./instance.ts#isStaging()`
const DEV_OR_STAGING_SUFFIXES = [

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I will cater this in another iteration where we will evaluate the exports of the @clerk/shared

'.lcl.dev',
'.stg.dev',
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/utils/url.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
import { isStaging } from './instance';

export function parseSearchParams(queryString = ''): URLSearchParams {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
Expand DownExpand Up@@ -25,3 +27,40 @@ export function addClerkPrefix(str: string | undefined) {
const stripped = str.replace(regex, '');
return `clerk.${stripped}`;
}

/**
*
* Retrieve the clerk-js major tag using the major version from the pkgVersion
* param or use the frontendApi to determine if the staging tag should be used.
* The default tag is `latest` and a `next` pkgVersion also exists to retrieve
* the next canary release.
*/
export const getClerkJsMajorVersionOrTag = (frontendApi: string, pkgVersion?: string) => {
Comment thread
dimkl marked this conversation as resolved.
if (!pkgVersion && isStaging(frontendApi)) {
return 'staging';
}

if (!pkgVersion) {
return 'latest';
}

if (pkgVersion.includes('next')) {
return 'next';
}

return pkgVersion.split('.')[0] || 'latest';
};

/**
*
* Retrieve the clerk-js script url from the frontendApi and the major tag
* using the {@link getClerkJsMajorVersionOrTag} or a provided clerkJSVersion tag.
*/
export const getScriptUrl = (
Comment thread
dimkl marked this conversation as resolved.
frontendApi: string,
{ pkgVersion, clerkJSVersion }: { pkgVersion?: string; clerkJSVersion?: string },
) => {
const noSchemeFrontendApi = frontendApi.replace(/http(s)?:\/\//, '');
const major = getClerkJsMajorVersionOrTag(frontendApi, pkgVersion);
return `https://${noSchemeFrontendApi}/npm/@clerk/clerk-js@${clerkJSVersion || major}/dist/clerk.browser.js`;
};