admin123 09c106e020 docs: add distribution build and simplified user guide
- Include dist-release/ and release/ for direct colleague use
- Add beginner-friendly installation guide
- Update .gitignore to track distribution builds
2026-05-07 18:48:42 +08:00

3336 lines
116 KiB
JavaScript

"use strict";
(() => {
// src/shared/auth-config.ts
var defaultAuthConfig = {
apiResource: "https://talent-search.intelligrow.cn",
appId: "i4jkllbvih0554r4n0fd3",
enableDevAuthPanel: false,
logtoEndpoint: "https://login-api.intelligrow.cn",
scopes: ["openid", "profile", "offline_access", "talent-search:read"]
};
function readAuthConfig(overrides = {}) {
const nextConfig = {
...defaultAuthConfig,
...overrides
};
if (!nextConfig.logtoEndpoint.trim()) {
throw new Error("auth config logtoEndpoint is required");
}
if (!nextConfig.appId.trim()) {
throw new Error("auth config appId is required");
}
if (!nextConfig.apiResource.trim()) {
throw new Error("auth config apiResource is required");
}
return nextConfig;
}
// src/background/auth/state.ts
function createLoggedOutAuthState(config) {
return {
isAuthenticated: false,
resource: config?.apiResource ?? null
};
}
function createLoggedInAuthState(claims, config) {
return {
accessTokenExpiresAt: null,
isAuthenticated: true,
resource: config?.apiResource ?? null,
scopes: config?.scopes ?? [],
tokenAvailable: true,
userInfo: {
email: readStringClaim(claims, "email"),
name: readStringClaim(claims, "name"),
sub: readStringClaim(claims, "sub"),
username: readStringClaim(claims, "username")
}
};
}
function readStringClaim(claims, key) {
const value = claims?.[key];
return typeof value === "string" ? value : void 0;
}
// src/background/auth/controller.ts
function createAuthController(options) {
const config = options.config ?? readAuthConfig();
return {
async getAccessToken() {
return options.authClient.getAccessToken(config.apiResource);
},
async getAuthState() {
const isAuthenticated = await options.authClient.isAuthenticated();
if (!isAuthenticated) {
return createLoggedOutAuthState(config);
}
const claims = await options.authClient.getIdTokenClaims();
return createLoggedInAuthState(claims, config);
},
async signIn() {
await options.authClient.signIn();
},
async signOut() {
await options.authClient.signOut();
}
};
}
// node_modules/map-obj/index.js
var isObject = (value) => typeof value === "object" && value !== null;
var isObjectCustom = (value) => isObject(value) && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
var mapObjectSkip = /* @__PURE__ */ Symbol("mapObjectSkip");
var _mapObject = (object, mapper, options, isSeen = /* @__PURE__ */ new WeakMap()) => {
options = {
deep: false,
target: {},
...options
};
if (isSeen.has(object)) {
return isSeen.get(object);
}
isSeen.set(object, options.target);
const { target } = options;
delete options.target;
const mapArray = (array) => array.map((element) => isObjectCustom(element) ? _mapObject(element, mapper, options, isSeen) : element);
if (Array.isArray(object)) {
return mapArray(object);
}
for (const [key, value] of Object.entries(object)) {
const mapResult = mapper(key, value, object);
if (mapResult === mapObjectSkip) {
continue;
}
let [newKey, newValue, { shouldRecurse = true } = {}] = mapResult;
if (newKey === "__proto__") {
continue;
}
if (options.deep && shouldRecurse && isObjectCustom(newValue)) {
newValue = Array.isArray(newValue) ? mapArray(newValue) : _mapObject(newValue, mapper, options, isSeen);
}
target[newKey] = newValue;
}
return target;
};
function mapObject(object, mapper, options) {
if (!isObject(object)) {
throw new TypeError(`Expected an object, got \`${object}\` (${typeof object})`);
}
return _mapObject(object, mapper, options);
}
// node_modules/camelcase/index.js
var UPPERCASE = /[\p{Lu}]/u;
var LOWERCASE = /[\p{Ll}]/u;
var LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
var IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
var SEPARATORS = /[_.\- ]+/;
var LEADING_SEPARATORS = new RegExp("^" + SEPARATORS.source);
var SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, "gu");
var NUMBERS_AND_IDENTIFIER = new RegExp("\\d+" + IDENTIFIER.source, "gu");
var preserveCamelCase = (string, toLowerCase, toUpperCase, preserveConsecutiveUppercase2) => {
let isLastCharLower = false;
let isLastCharUpper = false;
let isLastLastCharUpper = false;
let isLastLastCharPreserved = false;
for (let index = 0; index < string.length; index++) {
const character = string[index];
isLastLastCharPreserved = index > 2 ? string[index - 3] === "-" : true;
if (isLastCharLower && UPPERCASE.test(character)) {
string = string.slice(0, index) + "-" + string.slice(index);
isLastCharLower = false;
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = true;
index++;
} else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character) && (!isLastLastCharPreserved || preserveConsecutiveUppercase2)) {
string = string.slice(0, index - 1) + "-" + string.slice(index - 1);
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = false;
isLastCharLower = true;
} else {
isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
isLastLastCharUpper = isLastCharUpper;
isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
}
}
return string;
};
var preserveConsecutiveUppercase = (input, toLowerCase) => {
LEADING_CAPITAL.lastIndex = 0;
return input.replaceAll(LEADING_CAPITAL, (match) => toLowerCase(match));
};
var postProcess = (input, toUpperCase) => {
SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
NUMBERS_AND_IDENTIFIER.lastIndex = 0;
return input.replaceAll(NUMBERS_AND_IDENTIFIER, (match, pattern, offset) => ["_", "-"].includes(input.charAt(offset + match.length)) ? match : toUpperCase(match)).replaceAll(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier));
};
function camelCase(input, options) {
if (!(typeof input === "string" || Array.isArray(input))) {
throw new TypeError("Expected the input to be `string | string[]`");
}
options = {
pascalCase: false,
preserveConsecutiveUppercase: false,
...options
};
if (Array.isArray(input)) {
input = input.map((x) => x.trim()).filter((x) => x.length).join("-");
} else {
input = input.trim();
}
if (input.length === 0) {
return "";
}
const toLowerCase = options.locale === false ? (string) => string.toLowerCase() : (string) => string.toLocaleLowerCase(options.locale);
const toUpperCase = options.locale === false ? (string) => string.toUpperCase() : (string) => string.toLocaleUpperCase(options.locale);
if (input.length === 1) {
if (SEPARATORS.test(input)) {
return "";
}
return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
}
const hasUpperCase = input !== toLowerCase(input);
if (hasUpperCase) {
input = preserveCamelCase(input, toLowerCase, toUpperCase, options.preserveConsecutiveUppercase);
}
input = input.replace(LEADING_SEPARATORS, "");
input = options.preserveConsecutiveUppercase ? preserveConsecutiveUppercase(input, toLowerCase) : toLowerCase(input);
if (options.pascalCase) {
input = toUpperCase(input.charAt(0)) + input.slice(1);
}
return postProcess(input, toUpperCase);
}
// node_modules/quick-lru/index.js
var QuickLRU = class extends Map {
constructor(options = {}) {
super();
if (!(options.maxSize && options.maxSize > 0)) {
throw new TypeError("`maxSize` must be a number greater than 0");
}
if (typeof options.maxAge === "number" && options.maxAge === 0) {
throw new TypeError("`maxAge` must be a number greater than 0");
}
this.maxSize = options.maxSize;
this.maxAge = options.maxAge || Number.POSITIVE_INFINITY;
this.onEviction = options.onEviction;
this.cache = /* @__PURE__ */ new Map();
this.oldCache = /* @__PURE__ */ new Map();
this._size = 0;
}
// TODO: Use private class methods when targeting Node.js 16.
_emitEvictions(cache2) {
if (typeof this.onEviction !== "function") {
return;
}
for (const [key, item] of cache2) {
this.onEviction(key, item.value);
}
}
_deleteIfExpired(key, item) {
if (typeof item.expiry === "number" && item.expiry <= Date.now()) {
if (typeof this.onEviction === "function") {
this.onEviction(key, item.value);
}
return this.delete(key);
}
return false;
}
_getOrDeleteIfExpired(key, item) {
const deleted = this._deleteIfExpired(key, item);
if (deleted === false) {
return item.value;
}
}
_getItemValue(key, item) {
return item.expiry ? this._getOrDeleteIfExpired(key, item) : item.value;
}
_peek(key, cache2) {
const item = cache2.get(key);
return this._getItemValue(key, item);
}
_set(key, value) {
this.cache.set(key, value);
this._size++;
if (this._size >= this.maxSize) {
this._size = 0;
this._emitEvictions(this.oldCache);
this.oldCache = this.cache;
this.cache = /* @__PURE__ */ new Map();
}
}
_moveToRecent(key, item) {
this.oldCache.delete(key);
this._set(key, item);
}
*_entriesAscending() {
for (const item of this.oldCache) {
const [key, value] = item;
if (!this.cache.has(key)) {
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield item;
}
}
}
for (const item of this.cache) {
const [key, value] = item;
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield item;
}
}
}
get(key) {
if (this.cache.has(key)) {
const item = this.cache.get(key);
return this._getItemValue(key, item);
}
if (this.oldCache.has(key)) {
const item = this.oldCache.get(key);
if (this._deleteIfExpired(key, item) === false) {
this._moveToRecent(key, item);
return item.value;
}
}
}
set(key, value, { maxAge = this.maxAge } = {}) {
const expiry = typeof maxAge === "number" && maxAge !== Number.POSITIVE_INFINITY ? Date.now() + maxAge : void 0;
if (this.cache.has(key)) {
this.cache.set(key, {
value,
expiry
});
} else {
this._set(key, { value, expiry });
}
return this;
}
has(key) {
if (this.cache.has(key)) {
return !this._deleteIfExpired(key, this.cache.get(key));
}
if (this.oldCache.has(key)) {
return !this._deleteIfExpired(key, this.oldCache.get(key));
}
return false;
}
peek(key) {
if (this.cache.has(key)) {
return this._peek(key, this.cache);
}
if (this.oldCache.has(key)) {
return this._peek(key, this.oldCache);
}
}
delete(key) {
const deleted = this.cache.delete(key);
if (deleted) {
this._size--;
}
return this.oldCache.delete(key) || deleted;
}
clear() {
this.cache.clear();
this.oldCache.clear();
this._size = 0;
}
resize(newSize) {
if (!(newSize && newSize > 0)) {
throw new TypeError("`maxSize` must be a number greater than 0");
}
const items = [...this._entriesAscending()];
const removeCount = items.length - newSize;
if (removeCount < 0) {
this.cache = new Map(items);
this.oldCache = /* @__PURE__ */ new Map();
this._size = items.length;
} else {
if (removeCount > 0) {
this._emitEvictions(items.slice(0, removeCount));
}
this.oldCache = new Map(items.slice(removeCount));
this.cache = /* @__PURE__ */ new Map();
this._size = 0;
}
this.maxSize = newSize;
}
*keys() {
for (const [key] of this) {
yield key;
}
}
*values() {
for (const [, value] of this) {
yield value;
}
}
*[Symbol.iterator]() {
for (const item of this.cache) {
const [key, value] = item;
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield [key, value.value];
}
}
for (const item of this.oldCache) {
const [key, value] = item;
if (!this.cache.has(key)) {
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield [key, value.value];
}
}
}
}
*entriesDescending() {
let items = [...this.cache];
for (let i = items.length - 1; i >= 0; --i) {
const item = items[i];
const [key, value] = item;
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield [key, value.value];
}
}
items = [...this.oldCache];
for (let i = items.length - 1; i >= 0; --i) {
const item = items[i];
const [key, value] = item;
if (!this.cache.has(key)) {
const deleted = this._deleteIfExpired(key, value);
if (deleted === false) {
yield [key, value.value];
}
}
}
}
*entriesAscending() {
for (const [key, value] of this._entriesAscending()) {
yield [key, value.value];
}
}
get size() {
if (!this._size) {
return this.oldCache.size;
}
let oldCacheSize = 0;
for (const key of this.oldCache.keys()) {
if (!this.cache.has(key)) {
oldCacheSize++;
}
}
return Math.min(this._size + oldCacheSize, this.maxSize);
}
entries() {
return this.entriesAscending();
}
forEach(callbackFunction, thisArgument = this) {
for (const [key, value] of this.entriesAscending()) {
callbackFunction.call(thisArgument, value, key, this);
}
}
get [Symbol.toStringTag]() {
return JSON.stringify([...this.entriesAscending()]);
}
};
// node_modules/camelcase-keys/index.js
var has = (array, key) => array.some((element) => {
if (typeof element === "string") {
return element === key;
}
element.lastIndex = 0;
return element.test(key);
});
var cache = new QuickLRU({ maxSize: 1e5 });
var isObject2 = (value) => typeof value === "object" && value !== null && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
var transform = (input, options = {}) => {
if (!isObject2(input)) {
return input;
}
const {
exclude,
pascalCase = false,
stopPaths,
deep = false,
preserveConsecutiveUppercase: preserveConsecutiveUppercase2 = false
} = options;
const stopPathsSet = new Set(stopPaths);
const makeMapper = (parentPath) => (key, value) => {
if (deep && isObject2(value)) {
const path = parentPath === void 0 ? key : `${parentPath}.${key}`;
if (!stopPathsSet.has(path)) {
value = mapObject(value, makeMapper(path));
}
}
if (!(exclude && has(exclude, key))) {
const cacheKey = pascalCase ? `${key}_` : key;
if (cache.has(cacheKey)) {
key = cache.get(cacheKey);
} else {
const returnValue = camelCase(key, { pascalCase, locale: false, preserveConsecutiveUppercase: preserveConsecutiveUppercase2 });
if (key.length < 100) {
cache.set(cacheKey, returnValue);
}
key = returnValue;
}
}
return [key, value];
};
return mapObject(input, makeMapper(void 0));
};
function camelcaseKeys(input, options) {
if (Array.isArray(input)) {
return Object.keys(input).map((key) => transform(input[key], options));
}
return transform(input, options);
}
// node_modules/@logto/js/lib/consts/openid.js
var ReservedScope;
(function(ReservedScope2) {
ReservedScope2["OpenId"] = "openid";
ReservedScope2["OfflineAccess"] = "offline_access";
})(ReservedScope || (ReservedScope = {}));
var ReservedResource;
(function(ReservedResource2) {
ReservedResource2["Organization"] = "urn:logto:resource:organizations";
})(ReservedResource || (ReservedResource = {}));
var UserScope;
(function(UserScope2) {
UserScope2["Profile"] = "profile";
UserScope2["Email"] = "email";
UserScope2["Phone"] = "phone";
UserScope2["Address"] = "address";
UserScope2["CustomData"] = "custom_data";
UserScope2["Identities"] = "identities";
UserScope2["Roles"] = "roles";
UserScope2["Organizations"] = "urn:logto:scope:organizations";
UserScope2["OrganizationRoles"] = "urn:logto:scope:organization_roles";
UserScope2["Sessions"] = "urn:logto:scope:sessions";
})(UserScope || (UserScope = {}));
var idTokenClaims = Object.freeze({
[UserScope.Profile]: ["name", "picture", "username"],
[UserScope.Email]: ["email", "email_verified"],
[UserScope.Phone]: ["phone_number", "phone_number_verified"],
[UserScope.Address]: [],
[UserScope.Roles]: ["roles"],
[UserScope.Organizations]: ["organizations"],
[UserScope.OrganizationRoles]: ["organization_roles"],
[UserScope.CustomData]: [],
[UserScope.Identities]: [],
[UserScope.Sessions]: []
});
var userinfoClaims = Object.freeze({
[UserScope.Profile]: [],
[UserScope.Email]: [],
[UserScope.Phone]: [],
[UserScope.Address]: [],
[UserScope.Roles]: [],
[UserScope.Organizations]: [],
[UserScope.OrganizationRoles]: [],
[UserScope.CustomData]: ["custom_data"],
[UserScope.Identities]: ["identities"],
[UserScope.Sessions]: []
});
var userClaims = Object.freeze(
// Hard to infer type directly, use `as` for a workaround.
// eslint-disable-next-line no-restricted-syntax
Object.fromEntries(Object.values(UserScope).map((current) => [
current,
[...idTokenClaims[current], ...userinfoClaims[current]]
]))
);
// node_modules/@logto/js/lib/consts/index.js
var ContentType = {
formUrlEncoded: { "Content-Type": "application/x-www-form-urlencoded" }
};
var TokenGrantType;
(function(TokenGrantType2) {
TokenGrantType2["AuthorizationCode"] = "authorization_code";
TokenGrantType2["RefreshToken"] = "refresh_token";
})(TokenGrantType || (TokenGrantType = {}));
var QueryKey;
(function(QueryKey2) {
QueryKey2["ClientId"] = "client_id";
QueryKey2["Code"] = "code";
QueryKey2["CodeChallenge"] = "code_challenge";
QueryKey2["CodeChallengeMethod"] = "code_challenge_method";
QueryKey2["CodeVerifier"] = "code_verifier";
QueryKey2["Error"] = "error";
QueryKey2["ErrorDescription"] = "error_description";
QueryKey2["GrantType"] = "grant_type";
QueryKey2["IdToken"] = "id_token";
QueryKey2["IdTokenHint"] = "id_token_hint";
QueryKey2["LoginHint"] = "login_hint";
QueryKey2["PostLogoutRedirectUri"] = "post_logout_redirect_uri";
QueryKey2["Prompt"] = "prompt";
QueryKey2["RedirectUri"] = "redirect_uri";
QueryKey2["RefreshToken"] = "refresh_token";
QueryKey2["Resource"] = "resource";
QueryKey2["ResponseType"] = "response_type";
QueryKey2["Scope"] = "scope";
QueryKey2["State"] = "state";
QueryKey2["Token"] = "token";
QueryKey2["InteractionMode"] = "interaction_mode";
QueryKey2["OrganizationId"] = "organization_id";
QueryKey2["FirstScreen"] = "first_screen";
QueryKey2["Identifier"] = "identifier";
QueryKey2["DirectSignIn"] = "direct_sign_in";
QueryKey2["OneTimeToken"] = "one_time_token";
})(QueryKey || (QueryKey = {}));
var Prompt;
(function(Prompt2) {
Prompt2["None"] = "none";
Prompt2["Consent"] = "consent";
Prompt2["Login"] = "login";
})(Prompt || (Prompt = {}));
// node_modules/@logto/js/lib/core/fetch-token.js
var fetchTokenByAuthorizationCode = async ({ clientId, tokenEndpoint, redirectUri, codeVerifier, code, resource }, requester) => {
const parameters = new URLSearchParams();
parameters.append(QueryKey.ClientId, clientId);
parameters.append(QueryKey.Code, code);
parameters.append(QueryKey.CodeVerifier, codeVerifier);
parameters.append(QueryKey.RedirectUri, redirectUri);
parameters.append(QueryKey.GrantType, TokenGrantType.AuthorizationCode);
if (resource) {
parameters.append(QueryKey.Resource, resource);
}
const snakeCaseCodeTokenResponse = await requester(tokenEndpoint, {
method: "POST",
headers: ContentType.formUrlEncoded,
body: parameters.toString()
});
return camelcaseKeys(snakeCaseCodeTokenResponse);
};
var fetchTokenByRefreshToken = async (params, requester) => {
const { clientId, tokenEndpoint, refreshToken, resource, organizationId, scopes } = params;
const parameters = new URLSearchParams();
parameters.append(QueryKey.ClientId, clientId);
parameters.append(QueryKey.RefreshToken, refreshToken);
parameters.append(QueryKey.GrantType, TokenGrantType.RefreshToken);
if (resource) {
parameters.append(QueryKey.Resource, resource);
}
if (organizationId) {
parameters.append(QueryKey.OrganizationId, organizationId);
}
if (scopes?.length) {
parameters.append(QueryKey.Scope, scopes.join(" "));
}
const snakeCaseRefreshTokenTokenResponse = await requester(tokenEndpoint, {
method: "POST",
headers: ContentType.formUrlEncoded,
body: parameters.toString()
});
return camelcaseKeys(snakeCaseRefreshTokenTokenResponse);
};
// node_modules/@logto/js/lib/core/oidc-config.js
var discoveryPath = "/oidc/.well-known/openid-configuration";
var fetchOidcConfig = async (endpoint, requester) => camelcaseKeys(await requester(endpoint));
// node_modules/@logto/js/lib/core/revoke.js
var revoke = async (revocationEndpoint, clientId, token, requester) => requester(revocationEndpoint, {
method: "POST",
headers: ContentType.formUrlEncoded,
body: new URLSearchParams({
[QueryKey.ClientId]: clientId,
[QueryKey.Token]: token
}).toString()
});
// node_modules/@logto/js/lib/utils/scopes.js
var withReservedScopes = (originalScopes) => {
const reservedScopes = Object.values(ReservedScope);
const uniqueScopes = /* @__PURE__ */ new Set([...reservedScopes, UserScope.Profile, ...originalScopes ?? []]);
return Array.from(uniqueScopes).join(" ");
};
// node_modules/@logto/js/lib/core/sign-in.js
var codeChallengeMethod = "S256";
var responseType = "code";
var buildPrompt = (prompt) => {
if (Array.isArray(prompt)) {
return prompt.join(" ");
}
return prompt ?? Prompt.Consent;
};
var generateSignInUri = ({ authorizationEndpoint, clientId, redirectUri, codeChallenge, state, scopes, resources, prompt, firstScreen, identifiers: identifier, interactionMode, loginHint, directSignIn, oneTimeToken, extraParams, includeReservedScopes = true }) => {
const urlSearchParameters = new URLSearchParams({
[QueryKey.ClientId]: clientId,
[QueryKey.RedirectUri]: redirectUri,
[QueryKey.CodeChallenge]: codeChallenge,
[QueryKey.CodeChallengeMethod]: codeChallengeMethod,
[QueryKey.State]: state,
[QueryKey.ResponseType]: responseType,
[QueryKey.Prompt]: buildPrompt(prompt)
});
const computedScopes = includeReservedScopes ? withReservedScopes(scopes) : scopes?.join(" ");
if (computedScopes) {
urlSearchParameters.append(QueryKey.Scope, computedScopes);
}
if (loginHint) {
urlSearchParameters.append(QueryKey.LoginHint, loginHint);
}
if (directSignIn) {
urlSearchParameters.append(QueryKey.DirectSignIn, `${directSignIn.method}:${directSignIn.target}`);
}
for (const resource of resources ?? []) {
urlSearchParameters.append(QueryKey.Resource, resource);
}
if (firstScreen) {
urlSearchParameters.append(QueryKey.FirstScreen, firstScreen);
} else if (interactionMode) {
urlSearchParameters.append(QueryKey.InteractionMode, interactionMode);
}
if (identifier && identifier.length > 0) {
urlSearchParameters.append(QueryKey.Identifier, identifier.join(" "));
}
if (oneTimeToken) {
urlSearchParameters.append(QueryKey.OneTimeToken, oneTimeToken);
}
if (extraParams) {
for (const [key, value] of Object.entries(extraParams)) {
urlSearchParameters.append(key, value);
}
}
return `${authorizationEndpoint}?${urlSearchParameters.toString()}`;
};
// node_modules/@logto/js/lib/core/sign-out.js
var generateSignOutUri = ({ endSessionEndpoint, clientId, postLogoutRedirectUri }) => {
const urlSearchParameters = new URLSearchParams({ [QueryKey.ClientId]: clientId });
if (postLogoutRedirectUri) {
urlSearchParameters.append(QueryKey.PostLogoutRedirectUri, postLogoutRedirectUri);
}
return `${endSessionEndpoint}?${urlSearchParameters.toString()}`;
};
// node_modules/@logto/js/lib/core/user-info.js
var fetchUserInfo = async (userInfoEndpoint, accessToken, requester) => requester(userInfoEndpoint, {
headers: { Authorization: `Bearer ${accessToken}` }
});
// node_modules/@silverhand/essentials/lib/utilities/array.js
var deduplicate = (array) => [...new Set(array)];
// node_modules/@silverhand/essentials/lib/utilities/assertions.js
var notFalsy = (value) => Boolean(value);
// node_modules/@silverhand/essentials/lib/utilities/conditional.js
var conditional = (exp) => notFalsy(exp) ? exp : void 0;
var conditionalString = (exp) => notFalsy(exp) ? String(exp) : "";
// node_modules/@silverhand/essentials/lib/utilities/function.js
var isPromise = (value) => value !== null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
var trySafe = (exec, onError) => {
try {
const unwrapped = typeof exec === "function" ? exec() : exec;
return isPromise(unwrapped) ? (
// eslint-disable-next-line promise/prefer-await-to-then
unwrapped.catch((error) => {
onError?.(error);
})
) : unwrapped;
} catch (error) {
onError?.(error);
}
};
// node_modules/@silverhand/essentials/lib/utilities/string.js
var replaceNonUrlSafeCharacters = (base64String) => base64String.replaceAll("+", "-").replaceAll("/", "_").replaceAll(/=+$/g, "");
var restoreNonUrlSafeCharacters = (base64String) => base64String.replaceAll("-", "+").replaceAll("_", "/");
var urlSafeBase64 = {
isSafe: (input) => /^[\w-]*$/.test(input),
encode: (rawString) => {
const encodedString = btoa(unescape(encodeURIComponent(rawString)));
return replaceNonUrlSafeCharacters(encodedString);
},
decode: (encodedString) => {
const nonUrlSafeEncodedString = restoreNonUrlSafeCharacters(encodedString);
return decodeURIComponent(escape(atob(nonUrlSafeEncodedString)));
},
replaceNonUrlSafeCharacters,
restoreNonUrlSafeCharacters
};
// node_modules/@silverhand/essentials/lib/utilities/url.js
var joinPath = (...segments) => {
const result = [];
for (const segment of segments.join("/").split("/")) {
if (!segment || segment === ".") {
continue;
}
if ([...segment].every((char) => char === ".")) {
result.pop();
continue;
}
result.push(segment);
}
return "/" + result.join("/");
};
var appendPath = (baseUrl, ...pathnames) => new URL(joinPath(baseUrl.pathname, ...pathnames), baseUrl);
// node_modules/@logto/js/lib/utils/arbitrary-object.js
var isArbitraryObject = (data) => typeof data === "object" && data !== null;
// node_modules/@logto/js/lib/utils/errors.js
var logtoErrorCodes = Object.freeze({
"id_token.invalid_iat": "Invalid issued at time in the ID token",
"id_token.invalid_token": "Invalid ID token",
"callback_uri_verification.redirect_uri_mismatched": "The callback URI mismatches the redirect URI.",
"callback_uri_verification.error_found": "Error found in the callback URI",
"callback_uri_verification.missing_state": "Missing state in the callback URI",
"callback_uri_verification.state_mismatched": "State mismatched in the callback URI",
"callback_uri_verification.missing_code": "Missing code in the callback URI",
crypto_subtle_unavailable: "Crypto.subtle is unavailable in insecure contexts (non-HTTPS).",
unexpected_response_error: "Unexpected response error from the server."
});
var LogtoError = class extends Error {
constructor(code, data) {
super(logtoErrorCodes[code]);
this.code = code;
this.data = data;
this.name = "LogtoError";
}
};
var isLogtoRequestErrorJson = (data) => {
if (!isArbitraryObject(data)) {
return false;
}
return typeof data.code === "string" && typeof data.message === "string";
};
var LogtoRequestError = class extends Error {
constructor(code, message2, cause) {
super(message2);
this.code = code;
this.cause = cause;
this.name = "LogtoRequestError";
}
};
var OidcError = class {
constructor(error, errorDescription) {
this.error = error;
this.errorDescription = errorDescription;
this.name = "OidcError";
}
};
// node_modules/@logto/js/lib/utils/callback-uri.js
var parseUriParameters = (uri) => {
const [, queryString = ""] = uri.split("?");
return new URLSearchParams(queryString);
};
var verifyAndParseCodeFromCallbackUri = (callbackUri, redirectUri, state) => {
if (!callbackUri.startsWith(redirectUri)) {
throw new LogtoError("callback_uri_verification.redirect_uri_mismatched");
}
const uriParameters = parseUriParameters(callbackUri);
const error = conditional(uriParameters.get(QueryKey.Error));
const errorDescription = conditional(uriParameters.get(QueryKey.ErrorDescription));
if (error) {
throw new LogtoError("callback_uri_verification.error_found", new OidcError(error, errorDescription));
}
const stateFromCallbackUri = uriParameters.get(QueryKey.State);
if (!stateFromCallbackUri) {
throw new LogtoError("callback_uri_verification.missing_state");
}
if (stateFromCallbackUri !== state) {
throw new LogtoError("callback_uri_verification.state_mismatched");
}
const code = uriParameters.get(QueryKey.Code);
if (!code) {
throw new LogtoError("callback_uri_verification.missing_code");
}
return code;
};
// node_modules/@logto/js/lib/utils/id-token.js
function assertIdTokenClaims(data) {
if (!isArbitraryObject(data)) {
throw new TypeError("IdToken is expected to be an object");
}
for (const key of ["iss", "sub", "aud"]) {
if (typeof data[key] !== "string") {
throw new TypeError(`At path: IdToken.${key}: expected a string`);
}
}
for (const key of ["exp", "iat"]) {
if (typeof data[key] !== "number") {
throw new TypeError(`At path: IdToken.${key}: expected a number`);
}
}
for (const key of ["at_hash", "name", "username", "picture", "email", "phone_number"]) {
if (data[key] === void 0) {
continue;
}
if (typeof data[key] !== "string" && data[key] !== null) {
throw new TypeError(`At path: IdToken.${key}: expected null or a string`);
}
}
for (const key of ["email_verified", "phone_number_verified"]) {
if (data[key] === void 0) {
continue;
}
if (typeof data[key] !== "boolean") {
throw new TypeError(`At path: IdToken.${key}: expected a boolean`);
}
}
}
var decodeIdToken = (token) => {
const { 1: encodedPayload } = token.split(".");
if (!encodedPayload) {
throw new LogtoError("id_token.invalid_token");
}
const json = urlSafeBase64.decode(encodedPayload);
const idTokenClaims2 = JSON.parse(json);
assertIdTokenClaims(idTokenClaims2);
return idTokenClaims2;
};
// node_modules/@logto/js/lib/utils/access-token.js
function assertAccessTokenClaims(data) {
if (!isArbitraryObject(data)) {
throw new TypeError("AccessToken is expected to be an object");
}
for (const key of ["jti", "iss", "sub", "aud", "client_id", "scope"]) {
if (data[key] === void 0) {
continue;
}
if (typeof data[key] !== "string" && data[key] !== null) {
throw new TypeError(`At path: AccessToken.${key}: expected null or a string`);
}
}
for (const key of ["exp", "iat"]) {
if (data[key] === void 0) {
continue;
}
if (typeof data[key] !== "number" && data[key] !== null) {
throw new TypeError(`At path: AccessToken.${key}: expected null or a number`);
}
}
}
var decodeAccessToken = (accessToken) => {
const { 1: encodedPayload } = accessToken.split(".");
if (!encodedPayload) {
return {};
}
const json = urlSafeBase64.decode(encodedPayload);
const accessTokenClaims = JSON.parse(json);
assertAccessTokenClaims(accessTokenClaims);
return accessTokenClaims;
};
// node_modules/jose/dist/browser/runtime/webcrypto.js
var webcrypto_default = crypto;
var isCryptoKey = (key) => key instanceof CryptoKey;
// node_modules/jose/dist/browser/lib/buffer_utils.js
var encoder = new TextEncoder();
var decoder = new TextDecoder();
var MAX_INT32 = 2 ** 32;
function concat(...buffers) {
const size = buffers.reduce((acc, { length }) => acc + length, 0);
const buf = new Uint8Array(size);
let i = 0;
for (const buffer of buffers) {
buf.set(buffer, i);
i += buffer.length;
}
return buf;
}
// node_modules/jose/dist/browser/runtime/base64url.js
var decodeBase64 = (encoded) => {
const binary = atob(encoded);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
};
var decode = (input) => {
let encoded = input;
if (encoded instanceof Uint8Array) {
encoded = decoder.decode(encoded);
}
encoded = encoded.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "");
try {
return decodeBase64(encoded);
} catch {
throw new TypeError("The input to be decoded is not correctly encoded.");
}
};
// node_modules/jose/dist/browser/util/errors.js
var JOSEError = class extends Error {
constructor(message2, options) {
super(message2, options);
this.code = "ERR_JOSE_GENERIC";
this.name = this.constructor.name;
Error.captureStackTrace?.(this, this.constructor);
}
};
JOSEError.code = "ERR_JOSE_GENERIC";
var JWTClaimValidationFailed = class extends JOSEError {
constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
super(message2, { cause: { claim, reason, payload } });
this.code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
this.claim = claim;
this.reason = reason;
this.payload = payload;
}
};
JWTClaimValidationFailed.code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
var JWTExpired = class extends JOSEError {
constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
super(message2, { cause: { claim, reason, payload } });
this.code = "ERR_JWT_EXPIRED";
this.claim = claim;
this.reason = reason;
this.payload = payload;
}
};
JWTExpired.code = "ERR_JWT_EXPIRED";
var JOSEAlgNotAllowed = class extends JOSEError {
constructor() {
super(...arguments);
this.code = "ERR_JOSE_ALG_NOT_ALLOWED";
}
};
JOSEAlgNotAllowed.code = "ERR_JOSE_ALG_NOT_ALLOWED";
var JOSENotSupported = class extends JOSEError {
constructor() {
super(...arguments);
this.code = "ERR_JOSE_NOT_SUPPORTED";
}
};
JOSENotSupported.code = "ERR_JOSE_NOT_SUPPORTED";
var JWEDecryptionFailed = class extends JOSEError {
constructor(message2 = "decryption operation failed", options) {
super(message2, options);
this.code = "ERR_JWE_DECRYPTION_FAILED";
}
};
JWEDecryptionFailed.code = "ERR_JWE_DECRYPTION_FAILED";
var JWEInvalid = class extends JOSEError {
constructor() {
super(...arguments);
this.code = "ERR_JWE_INVALID";
}
};
JWEInvalid.code = "ERR_JWE_INVALID";
var JWSInvalid = class extends JOSEError {
constructor() {
super(...arguments);
this.code = "ERR_JWS_INVALID";
}
};
JWSInvalid.code = "ERR_JWS_INVALID";
var JWTInvalid = class extends JOSEError {
constructor() {
super(...arguments);
this.code = "ERR_JWT_INVALID";
}
};
JWTInvalid.code = "ERR_JWT_INVALID";
var JWKInvalid = class extends JOSEError {
constructor() {
super(...arguments);
this.code = "ERR_JWK_INVALID";
}
};
JWKInvalid.code = "ERR_JWK_INVALID";
var JWKSInvalid = class extends JOSEError {
constructor() {
super(...arguments);
this.code = "ERR_JWKS_INVALID";
}
};
JWKSInvalid.code = "ERR_JWKS_INVALID";
var JWKSNoMatchingKey = class extends JOSEError {
constructor(message2 = "no applicable key found in the JSON Web Key Set", options) {
super(message2, options);
this.code = "ERR_JWKS_NO_MATCHING_KEY";
}
};
JWKSNoMatchingKey.code = "ERR_JWKS_NO_MATCHING_KEY";
var JWKSMultipleMatchingKeys = class extends JOSEError {
constructor(message2 = "multiple matching keys found in the JSON Web Key Set", options) {
super(message2, options);
this.code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
}
};
JWKSMultipleMatchingKeys.code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
var JWKSTimeout = class extends JOSEError {
constructor(message2 = "request timed out", options) {
super(message2, options);
this.code = "ERR_JWKS_TIMEOUT";
}
};
JWKSTimeout.code = "ERR_JWKS_TIMEOUT";
var JWSSignatureVerificationFailed = class extends JOSEError {
constructor(message2 = "signature verification failed", options) {
super(message2, options);
this.code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
}
};
JWSSignatureVerificationFailed.code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
// node_modules/jose/dist/browser/lib/crypto_key.js
function unusable(name, prop = "algorithm.name") {
return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
}
function isAlgorithm(algorithm, name) {
return algorithm.name === name;
}
function getHashLength(hash) {
return parseInt(hash.name.slice(4), 10);
}
function getNamedCurve(alg) {
switch (alg) {
case "ES256":
return "P-256";
case "ES384":
return "P-384";
case "ES512":
return "P-521";
default:
throw new Error("unreachable");
}
}
function checkUsage(key, usages) {
if (usages.length && !usages.some((expected) => key.usages.includes(expected))) {
let msg = "CryptoKey does not support this operation, its usages must include ";
if (usages.length > 2) {
const last = usages.pop();
msg += `one of ${usages.join(", ")}, or ${last}.`;
} else if (usages.length === 2) {
msg += `one of ${usages[0]} or ${usages[1]}.`;
} else {
msg += `${usages[0]}.`;
}
throw new TypeError(msg);
}
}
function checkSigCryptoKey(key, alg, ...usages) {
switch (alg) {
case "HS256":
case "HS384":
case "HS512": {
if (!isAlgorithm(key.algorithm, "HMAC"))
throw unusable("HMAC");
const expected = parseInt(alg.slice(2), 10);
const actual = getHashLength(key.algorithm.hash);
if (actual !== expected)
throw unusable(`SHA-${expected}`, "algorithm.hash");
break;
}
case "RS256":
case "RS384":
case "RS512": {
if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5"))
throw unusable("RSASSA-PKCS1-v1_5");
const expected = parseInt(alg.slice(2), 10);
const actual = getHashLength(key.algorithm.hash);
if (actual !== expected)
throw unusable(`SHA-${expected}`, "algorithm.hash");
break;
}
case "PS256":
case "PS384":
case "PS512": {
if (!isAlgorithm(key.algorithm, "RSA-PSS"))
throw unusable("RSA-PSS");
const expected = parseInt(alg.slice(2), 10);
const actual = getHashLength(key.algorithm.hash);
if (actual !== expected)
throw unusable(`SHA-${expected}`, "algorithm.hash");
break;
}
case "EdDSA": {
if (key.algorithm.name !== "Ed25519" && key.algorithm.name !== "Ed448") {
throw unusable("Ed25519 or Ed448");
}
break;
}
case "Ed25519": {
if (!isAlgorithm(key.algorithm, "Ed25519"))
throw unusable("Ed25519");
break;
}
case "ES256":
case "ES384":
case "ES512": {
if (!isAlgorithm(key.algorithm, "ECDSA"))
throw unusable("ECDSA");
const expected = getNamedCurve(alg);
const actual = key.algorithm.namedCurve;
if (actual !== expected)
throw unusable(expected, "algorithm.namedCurve");
break;
}
default:
throw new TypeError("CryptoKey does not support this operation");
}
checkUsage(key, usages);
}
// node_modules/jose/dist/browser/lib/invalid_key_input.js
function message(msg, actual, ...types2) {
types2 = types2.filter(Boolean);
if (types2.length > 2) {
const last = types2.pop();
msg += `one of type ${types2.join(", ")}, or ${last}.`;
} else if (types2.length === 2) {
msg += `one of type ${types2[0]} or ${types2[1]}.`;
} else {
msg += `of type ${types2[0]}.`;
}
if (actual == null) {
msg += ` Received ${actual}`;
} else if (typeof actual === "function" && actual.name) {
msg += ` Received function ${actual.name}`;
} else if (typeof actual === "object" && actual != null) {
if (actual.constructor?.name) {
msg += ` Received an instance of ${actual.constructor.name}`;
}
}
return msg;
}
var invalid_key_input_default = (actual, ...types2) => {
return message("Key must be ", actual, ...types2);
};
function withAlg(alg, actual, ...types2) {
return message(`Key for the ${alg} algorithm must be `, actual, ...types2);
}
// node_modules/jose/dist/browser/runtime/is_key_like.js
var is_key_like_default = (key) => {
if (isCryptoKey(key)) {
return true;
}
return key?.[Symbol.toStringTag] === "KeyObject";
};
var types = ["CryptoKey"];
// node_modules/jose/dist/browser/lib/is_disjoint.js
var isDisjoint = (...headers) => {
const sources = headers.filter(Boolean);
if (sources.length === 0 || sources.length === 1) {
return true;
}
let acc;
for (const header of sources) {
const parameters = Object.keys(header);
if (!acc || acc.size === 0) {
acc = new Set(parameters);
continue;
}
for (const parameter of parameters) {
if (acc.has(parameter)) {
return false;
}
acc.add(parameter);
}
}
return true;
};
var is_disjoint_default = isDisjoint;
// node_modules/jose/dist/browser/lib/is_object.js
function isObjectLike(value) {
return typeof value === "object" && value !== null;
}
function isObject4(input) {
if (!isObjectLike(input) || Object.prototype.toString.call(input) !== "[object Object]") {
return false;
}
if (Object.getPrototypeOf(input) === null) {
return true;
}
let proto = input;
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return Object.getPrototypeOf(input) === proto;
}
// node_modules/jose/dist/browser/runtime/check_key_length.js
var check_key_length_default = (alg, key) => {
if (alg.startsWith("RS") || alg.startsWith("PS")) {
const { modulusLength } = key.algorithm;
if (typeof modulusLength !== "number" || modulusLength < 2048) {
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
}
}
};
// node_modules/jose/dist/browser/lib/is_jwk.js
function isJWK(key) {
return isObject4(key) && typeof key.kty === "string";
}
function isPrivateJWK(key) {
return key.kty !== "oct" && typeof key.d === "string";
}
function isPublicJWK(key) {
return key.kty !== "oct" && typeof key.d === "undefined";
}
function isSecretJWK(key) {
return isJWK(key) && key.kty === "oct" && typeof key.k === "string";
}
// node_modules/jose/dist/browser/runtime/jwk_to_key.js
function subtleMapping(jwk) {
let algorithm;
let keyUsages;
switch (jwk.kty) {
case "RSA": {
switch (jwk.alg) {
case "PS256":
case "PS384":
case "PS512":
algorithm = { name: "RSA-PSS", hash: `SHA-${jwk.alg.slice(-3)}` };
keyUsages = jwk.d ? ["sign"] : ["verify"];
break;
case "RS256":
case "RS384":
case "RS512":
algorithm = { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${jwk.alg.slice(-3)}` };
keyUsages = jwk.d ? ["sign"] : ["verify"];
break;
case "RSA-OAEP":
case "RSA-OAEP-256":
case "RSA-OAEP-384":
case "RSA-OAEP-512":
algorithm = {
name: "RSA-OAEP",
hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}`
};
keyUsages = jwk.d ? ["decrypt", "unwrapKey"] : ["encrypt", "wrapKey"];
break;
default:
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
}
break;
}
case "EC": {
switch (jwk.alg) {
case "ES256":
algorithm = { name: "ECDSA", namedCurve: "P-256" };
keyUsages = jwk.d ? ["sign"] : ["verify"];
break;
case "ES384":
algorithm = { name: "ECDSA", namedCurve: "P-384" };
keyUsages = jwk.d ? ["sign"] : ["verify"];
break;
case "ES512":
algorithm = { name: "ECDSA", namedCurve: "P-521" };
keyUsages = jwk.d ? ["sign"] : ["verify"];
break;
case "ECDH-ES":
case "ECDH-ES+A128KW":
case "ECDH-ES+A192KW":
case "ECDH-ES+A256KW":
algorithm = { name: "ECDH", namedCurve: jwk.crv };
keyUsages = jwk.d ? ["deriveBits"] : [];
break;
default:
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
}
break;
}
case "OKP": {
switch (jwk.alg) {
case "Ed25519":
algorithm = { name: "Ed25519" };
keyUsages = jwk.d ? ["sign"] : ["verify"];
break;
case "EdDSA":
algorithm = { name: jwk.crv };
keyUsages = jwk.d ? ["sign"] : ["verify"];
break;
case "ECDH-ES":
case "ECDH-ES+A128KW":
case "ECDH-ES+A192KW":
case "ECDH-ES+A256KW":
algorithm = { name: jwk.crv };
keyUsages = jwk.d ? ["deriveBits"] : [];
break;
default:
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
}
break;
}
default:
throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value');
}
return { algorithm, keyUsages };
}
var parse = async (jwk) => {
if (!jwk.alg) {
throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
}
const { algorithm, keyUsages } = subtleMapping(jwk);
const rest = [
algorithm,
jwk.ext ?? false,
jwk.key_ops ?? keyUsages
];
const keyData = { ...jwk };
delete keyData.alg;
delete keyData.use;
return webcrypto_default.subtle.importKey("jwk", keyData, ...rest);
};
var jwk_to_key_default = parse;
// node_modules/jose/dist/browser/runtime/normalize_key.js
var exportKeyValue = (k) => decode(k);
var privCache;
var pubCache;
var isKeyObject = (key) => {
return key?.[Symbol.toStringTag] === "KeyObject";
};
var importAndCache = async (cache2, key, jwk, alg, freeze = false) => {
let cached = cache2.get(key);
if (cached?.[alg]) {
return cached[alg];
}
const cryptoKey = await jwk_to_key_default({ ...jwk, alg });
if (freeze)
Object.freeze(key);
if (!cached) {
cache2.set(key, { [alg]: cryptoKey });
} else {
cached[alg] = cryptoKey;
}
return cryptoKey;
};
var normalizePublicKey = (key, alg) => {
if (isKeyObject(key)) {
let jwk = key.export({ format: "jwk" });
delete jwk.d;
delete jwk.dp;
delete jwk.dq;
delete jwk.p;
delete jwk.q;
delete jwk.qi;
if (jwk.k) {
return exportKeyValue(jwk.k);
}
pubCache || (pubCache = /* @__PURE__ */ new WeakMap());
return importAndCache(pubCache, key, jwk, alg);
}
if (isJWK(key)) {
if (key.k)
return decode(key.k);
pubCache || (pubCache = /* @__PURE__ */ new WeakMap());
const cryptoKey = importAndCache(pubCache, key, key, alg, true);
return cryptoKey;
}
return key;
};
var normalizePrivateKey = (key, alg) => {
if (isKeyObject(key)) {
let jwk = key.export({ format: "jwk" });
if (jwk.k) {
return exportKeyValue(jwk.k);
}
privCache || (privCache = /* @__PURE__ */ new WeakMap());
return importAndCache(privCache, key, jwk, alg);
}
if (isJWK(key)) {
if (key.k)
return decode(key.k);
privCache || (privCache = /* @__PURE__ */ new WeakMap());
const cryptoKey = importAndCache(privCache, key, key, alg, true);
return cryptoKey;
}
return key;
};
var normalize_key_default = { normalizePublicKey, normalizePrivateKey };
// node_modules/jose/dist/browser/key/import.js
async function importJWK(jwk, alg) {
if (!isObject4(jwk)) {
throw new TypeError("JWK must be an object");
}
alg || (alg = jwk.alg);
switch (jwk.kty) {
case "oct":
if (typeof jwk.k !== "string" || !jwk.k) {
throw new TypeError('missing "k" (Key Value) Parameter value');
}
return decode(jwk.k);
case "RSA":
if ("oth" in jwk && jwk.oth !== void 0) {
throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');
}
case "EC":
case "OKP":
return jwk_to_key_default({ ...jwk, alg });
default:
throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value');
}
}
// node_modules/jose/dist/browser/lib/check_key_type.js
var tag = (key) => key?.[Symbol.toStringTag];
var jwkMatchesOp = (alg, key, usage) => {
if (key.use !== void 0 && key.use !== "sig") {
throw new TypeError("Invalid key for this operation, when present its use must be sig");
}
if (key.key_ops !== void 0 && key.key_ops.includes?.(usage) !== true) {
throw new TypeError(`Invalid key for this operation, when present its key_ops must include ${usage}`);
}
if (key.alg !== void 0 && key.alg !== alg) {
throw new TypeError(`Invalid key for this operation, when present its alg must be ${alg}`);
}
return true;
};
var symmetricTypeCheck = (alg, key, usage, allowJwk) => {
if (key instanceof Uint8Array)
return;
if (allowJwk && isJWK(key)) {
if (isSecretJWK(key) && jwkMatchesOp(alg, key, usage))
return;
throw new TypeError(`JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present`);
}
if (!is_key_like_default(key)) {
throw new TypeError(withAlg(alg, key, ...types, "Uint8Array", allowJwk ? "JSON Web Key" : null));
}
if (key.type !== "secret") {
throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`);
}
};
var asymmetricTypeCheck = (alg, key, usage, allowJwk) => {
if (allowJwk && isJWK(key)) {
switch (usage) {
case "sign":
if (isPrivateJWK(key) && jwkMatchesOp(alg, key, usage))
return;
throw new TypeError(`JSON Web Key for this operation be a private JWK`);
case "verify":
if (isPublicJWK(key) && jwkMatchesOp(alg, key, usage))
return;
throw new TypeError(`JSON Web Key for this operation be a public JWK`);
}
}
if (!is_key_like_default(key)) {
throw new TypeError(withAlg(alg, key, ...types, allowJwk ? "JSON Web Key" : null));
}
if (key.type === "secret") {
throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type "secret"`);
}
if (usage === "sign" && key.type === "public") {
throw new TypeError(`${tag(key)} instances for asymmetric algorithm signing must be of type "private"`);
}
if (usage === "decrypt" && key.type === "public") {
throw new TypeError(`${tag(key)} instances for asymmetric algorithm decryption must be of type "private"`);
}
if (key.algorithm && usage === "verify" && key.type === "private") {
throw new TypeError(`${tag(key)} instances for asymmetric algorithm verifying must be of type "public"`);
}
if (key.algorithm && usage === "encrypt" && key.type === "private") {
throw new TypeError(`${tag(key)} instances for asymmetric algorithm encryption must be of type "public"`);
}
};
function checkKeyType(allowJwk, alg, key, usage) {
const symmetric = alg.startsWith("HS") || alg === "dir" || alg.startsWith("PBES2") || /^A\d{3}(?:GCM)?KW$/.test(alg);
if (symmetric) {
symmetricTypeCheck(alg, key, usage, allowJwk);
} else {
asymmetricTypeCheck(alg, key, usage, allowJwk);
}
}
var check_key_type_default = checkKeyType.bind(void 0, false);
var checkKeyTypeWithJwk = checkKeyType.bind(void 0, true);
// node_modules/jose/dist/browser/lib/validate_crit.js
function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
if (joseHeader.crit !== void 0 && protectedHeader?.crit === void 0) {
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
}
if (!protectedHeader || protectedHeader.crit === void 0) {
return /* @__PURE__ */ new Set();
}
if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input !== "string" || input.length === 0)) {
throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');
}
let recognized;
if (recognizedOption !== void 0) {
recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]);
} else {
recognized = recognizedDefault;
}
for (const parameter of protectedHeader.crit) {
if (!recognized.has(parameter)) {
throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
}
if (joseHeader[parameter] === void 0) {
throw new Err(`Extension Header Parameter "${parameter}" is missing`);
}
if (recognized.get(parameter) && protectedHeader[parameter] === void 0) {
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
}
}
return new Set(protectedHeader.crit);
}
var validate_crit_default = validateCrit;
// node_modules/jose/dist/browser/lib/validate_algorithms.js
var validateAlgorithms = (option, algorithms) => {
if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== "string"))) {
throw new TypeError(`"${option}" option must be an array of strings`);
}
if (!algorithms) {
return void 0;
}
return new Set(algorithms);
};
var validate_algorithms_default = validateAlgorithms;
// node_modules/jose/dist/browser/runtime/subtle_dsa.js
function subtleDsa(alg, algorithm) {
const hash = `SHA-${alg.slice(-3)}`;
switch (alg) {
case "HS256":
case "HS384":
case "HS512":
return { hash, name: "HMAC" };
case "PS256":
case "PS384":
case "PS512":
return { hash, name: "RSA-PSS", saltLength: alg.slice(-3) >> 3 };
case "RS256":
case "RS384":
case "RS512":
return { hash, name: "RSASSA-PKCS1-v1_5" };
case "ES256":
case "ES384":
case "ES512":
return { hash, name: "ECDSA", namedCurve: algorithm.namedCurve };
case "Ed25519":
return { name: "Ed25519" };
case "EdDSA":
return { name: algorithm.name };
default:
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
}
}
// node_modules/jose/dist/browser/runtime/get_sign_verify_key.js
async function getCryptoKey(alg, key, usage) {
if (usage === "sign") {
key = await normalize_key_default.normalizePrivateKey(key, alg);
}
if (usage === "verify") {
key = await normalize_key_default.normalizePublicKey(key, alg);
}
if (isCryptoKey(key)) {
checkSigCryptoKey(key, alg, usage);
return key;
}
if (key instanceof Uint8Array) {
if (!alg.startsWith("HS")) {
throw new TypeError(invalid_key_input_default(key, ...types));
}
return webcrypto_default.subtle.importKey("raw", key, { hash: `SHA-${alg.slice(-3)}`, name: "HMAC" }, false, [usage]);
}
throw new TypeError(invalid_key_input_default(key, ...types, "Uint8Array", "JSON Web Key"));
}
// node_modules/jose/dist/browser/runtime/verify.js
var verify = async (alg, key, signature, data) => {
const cryptoKey = await getCryptoKey(alg, key, "verify");
check_key_length_default(alg, cryptoKey);
const algorithm = subtleDsa(alg, cryptoKey.algorithm);
try {
return await webcrypto_default.subtle.verify(algorithm, cryptoKey, signature, data);
} catch {
return false;
}
};
var verify_default = verify;
// node_modules/jose/dist/browser/jws/flattened/verify.js
async function flattenedVerify(jws, key, options) {
if (!isObject4(jws)) {
throw new JWSInvalid("Flattened JWS must be an object");
}
if (jws.protected === void 0 && jws.header === void 0) {
throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members');
}
if (jws.protected !== void 0 && typeof jws.protected !== "string") {
throw new JWSInvalid("JWS Protected Header incorrect type");
}
if (jws.payload === void 0) {
throw new JWSInvalid("JWS Payload missing");
}
if (typeof jws.signature !== "string") {
throw new JWSInvalid("JWS Signature missing or incorrect type");
}
if (jws.header !== void 0 && !isObject4(jws.header)) {
throw new JWSInvalid("JWS Unprotected Header incorrect type");
}
let parsedProt = {};
if (jws.protected) {
try {
const protectedHeader = decode(jws.protected);
parsedProt = JSON.parse(decoder.decode(protectedHeader));
} catch {
throw new JWSInvalid("JWS Protected Header is invalid");
}
}
if (!is_disjoint_default(parsedProt, jws.header)) {
throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
}
const joseHeader = {
...parsedProt,
...jws.header
};
const extensions = validate_crit_default(JWSInvalid, /* @__PURE__ */ new Map([["b64", true]]), options?.crit, parsedProt, joseHeader);
let b64 = true;
if (extensions.has("b64")) {
b64 = parsedProt.b64;
if (typeof b64 !== "boolean") {
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
}
}
const { alg } = joseHeader;
if (typeof alg !== "string" || !alg) {
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
}
const algorithms = options && validate_algorithms_default("algorithms", options.algorithms);
if (algorithms && !algorithms.has(alg)) {
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
}
if (b64) {
if (typeof jws.payload !== "string") {
throw new JWSInvalid("JWS Payload must be a string");
}
} else if (typeof jws.payload !== "string" && !(jws.payload instanceof Uint8Array)) {
throw new JWSInvalid("JWS Payload must be a string or an Uint8Array instance");
}
let resolvedKey = false;
if (typeof key === "function") {
key = await key(parsedProt, jws);
resolvedKey = true;
checkKeyTypeWithJwk(alg, key, "verify");
if (isJWK(key)) {
key = await importJWK(key, alg);
}
} else {
checkKeyTypeWithJwk(alg, key, "verify");
}
const data = concat(encoder.encode(jws.protected ?? ""), encoder.encode("."), typeof jws.payload === "string" ? encoder.encode(jws.payload) : jws.payload);
let signature;
try {
signature = decode(jws.signature);
} catch {
throw new JWSInvalid("Failed to base64url decode the signature");
}
const verified = await verify_default(alg, key, signature, data);
if (!verified) {
throw new JWSSignatureVerificationFailed();
}
let payload;
if (b64) {
try {
payload = decode(jws.payload);
} catch {
throw new JWSInvalid("Failed to base64url decode the payload");
}
} else if (typeof jws.payload === "string") {
payload = encoder.encode(jws.payload);
} else {
payload = jws.payload;
}
const result = { payload };
if (jws.protected !== void 0) {
result.protectedHeader = parsedProt;
}
if (jws.header !== void 0) {
result.unprotectedHeader = jws.header;
}
if (resolvedKey) {
return { ...result, key };
}
return result;
}
// node_modules/jose/dist/browser/jws/compact/verify.js
async function compactVerify(jws, key, options) {
if (jws instanceof Uint8Array) {
jws = decoder.decode(jws);
}
if (typeof jws !== "string") {
throw new JWSInvalid("Compact JWS must be a string or Uint8Array");
}
const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split(".");
if (length !== 3) {
throw new JWSInvalid("Invalid Compact JWS");
}
const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options);
const result = { payload: verified.payload, protectedHeader: verified.protectedHeader };
if (typeof key === "function") {
return { ...result, key: verified.key };
}
return result;
}
// node_modules/jose/dist/browser/lib/epoch.js
var epoch_default = (date) => Math.floor(date.getTime() / 1e3);
// node_modules/jose/dist/browser/lib/secs.js
var minute = 60;
var hour = minute * 60;
var day = hour * 24;
var week = day * 7;
var year = day * 365.25;
var REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
var secs_default = (str) => {
const matched = REGEX.exec(str);
if (!matched || matched[4] && matched[1]) {
throw new TypeError("Invalid time period format");
}
const value = parseFloat(matched[2]);
const unit = matched[3].toLowerCase();
let numericDate;
switch (unit) {
case "sec":
case "secs":
case "second":
case "seconds":
case "s":
numericDate = Math.round(value);
break;
case "minute":
case "minutes":
case "min":
case "mins":
case "m":
numericDate = Math.round(value * minute);
break;
case "hour":
case "hours":
case "hr":
case "hrs":
case "h":
numericDate = Math.round(value * hour);
break;
case "day":
case "days":
case "d":
numericDate = Math.round(value * day);
break;
case "week":
case "weeks":
case "w":
numericDate = Math.round(value * week);
break;
default:
numericDate = Math.round(value * year);
break;
}
if (matched[1] === "-" || matched[4] === "ago") {
return -numericDate;
}
return numericDate;
};
// node_modules/jose/dist/browser/lib/jwt_claims_set.js
var normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, "");
var checkAudiencePresence = (audPayload, audOption) => {
if (typeof audPayload === "string") {
return audOption.includes(audPayload);
}
if (Array.isArray(audPayload)) {
return audOption.some(Set.prototype.has.bind(new Set(audPayload)));
}
return false;
};
var jwt_claims_set_default = (protectedHeader, encodedPayload, options = {}) => {
let payload;
try {
payload = JSON.parse(decoder.decode(encodedPayload));
} catch {
}
if (!isObject4(payload)) {
throw new JWTInvalid("JWT Claims Set must be a top-level JSON object");
}
const { typ } = options;
if (typ && (typeof protectedHeader.typ !== "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, "typ", "check_failed");
}
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
const presenceCheck = [...requiredClaims];
if (maxTokenAge !== void 0)
presenceCheck.push("iat");
if (audience !== void 0)
presenceCheck.push("aud");
if (subject !== void 0)
presenceCheck.push("sub");
if (issuer !== void 0)
presenceCheck.push("iss");
for (const claim of new Set(presenceCheck.reverse())) {
if (!(claim in payload)) {
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, "missing");
}
}
if (issuer && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
throw new JWTClaimValidationFailed('unexpected "iss" claim value', payload, "iss", "check_failed");
}
if (subject && payload.sub !== subject) {
throw new JWTClaimValidationFailed('unexpected "sub" claim value', payload, "sub", "check_failed");
}
if (audience && !checkAudiencePresence(payload.aud, typeof audience === "string" ? [audience] : audience)) {
throw new JWTClaimValidationFailed('unexpected "aud" claim value', payload, "aud", "check_failed");
}
let tolerance;
switch (typeof options.clockTolerance) {
case "string":
tolerance = secs_default(options.clockTolerance);
break;
case "number":
tolerance = options.clockTolerance;
break;
case "undefined":
tolerance = 0;
break;
default:
throw new TypeError("Invalid clockTolerance option type");
}
const { currentDate } = options;
const now = epoch_default(currentDate || /* @__PURE__ */ new Date());
if ((payload.iat !== void 0 || maxTokenAge) && typeof payload.iat !== "number") {
throw new JWTClaimValidationFailed('"iat" claim must be a number', payload, "iat", "invalid");
}
if (payload.nbf !== void 0) {
if (typeof payload.nbf !== "number") {
throw new JWTClaimValidationFailed('"nbf" claim must be a number', payload, "nbf", "invalid");
}
if (payload.nbf > now + tolerance) {
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, "nbf", "check_failed");
}
}
if (payload.exp !== void 0) {
if (typeof payload.exp !== "number") {
throw new JWTClaimValidationFailed('"exp" claim must be a number', payload, "exp", "invalid");
}
if (payload.exp <= now - tolerance) {
throw new JWTExpired('"exp" claim timestamp check failed', payload, "exp", "check_failed");
}
}
if (maxTokenAge) {
const age = now - payload.iat;
const max = typeof maxTokenAge === "number" ? maxTokenAge : secs_default(maxTokenAge);
if (age - tolerance > max) {
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, "iat", "check_failed");
}
if (age < 0 - tolerance) {
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, "iat", "check_failed");
}
}
return payload;
};
// node_modules/jose/dist/browser/jwt/verify.js
async function jwtVerify(jwt, key, options) {
const verified = await compactVerify(jwt, key, options);
if (verified.protectedHeader.crit?.includes("b64") && verified.protectedHeader.b64 === false) {
throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
}
const payload = jwt_claims_set_default(verified.protectedHeader, verified.payload, options);
const result = { payload, protectedHeader: verified.protectedHeader };
if (typeof key === "function") {
return { ...result, key: verified.key };
}
return result;
}
// node_modules/jose/dist/browser/jwks/local.js
function getKtyFromAlg(alg) {
switch (typeof alg === "string" && alg.slice(0, 2)) {
case "RS":
case "PS":
return "RSA";
case "ES":
return "EC";
case "Ed":
return "OKP";
default:
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
}
}
function isJWKSLike(jwks) {
return jwks && typeof jwks === "object" && Array.isArray(jwks.keys) && jwks.keys.every(isJWKLike);
}
function isJWKLike(key) {
return isObject4(key);
}
function clone(obj) {
if (typeof structuredClone === "function") {
return structuredClone(obj);
}
return JSON.parse(JSON.stringify(obj));
}
var LocalJWKSet = class {
constructor(jwks) {
this._cached = /* @__PURE__ */ new WeakMap();
if (!isJWKSLike(jwks)) {
throw new JWKSInvalid("JSON Web Key Set malformed");
}
this._jwks = clone(jwks);
}
async getKey(protectedHeader, token) {
const { alg, kid } = { ...protectedHeader, ...token?.header };
const kty = getKtyFromAlg(alg);
const candidates = this._jwks.keys.filter((jwk2) => {
let candidate = kty === jwk2.kty;
if (candidate && typeof kid === "string") {
candidate = kid === jwk2.kid;
}
if (candidate && typeof jwk2.alg === "string") {
candidate = alg === jwk2.alg;
}
if (candidate && typeof jwk2.use === "string") {
candidate = jwk2.use === "sig";
}
if (candidate && Array.isArray(jwk2.key_ops)) {
candidate = jwk2.key_ops.includes("verify");
}
if (candidate) {
switch (alg) {
case "ES256":
candidate = jwk2.crv === "P-256";
break;
case "ES256K":
candidate = jwk2.crv === "secp256k1";
break;
case "ES384":
candidate = jwk2.crv === "P-384";
break;
case "ES512":
candidate = jwk2.crv === "P-521";
break;
case "Ed25519":
candidate = jwk2.crv === "Ed25519";
break;
case "EdDSA":
candidate = jwk2.crv === "Ed25519" || jwk2.crv === "Ed448";
break;
}
}
return candidate;
});
const { 0: jwk, length } = candidates;
if (length === 0) {
throw new JWKSNoMatchingKey();
}
if (length !== 1) {
const error = new JWKSMultipleMatchingKeys();
const { _cached } = this;
error[Symbol.asyncIterator] = async function* () {
for (const jwk2 of candidates) {
try {
yield await importWithAlgCache(_cached, jwk2, alg);
} catch {
}
}
};
throw error;
}
return importWithAlgCache(this._cached, jwk, alg);
}
};
async function importWithAlgCache(cache2, jwk, alg) {
const cached = cache2.get(jwk) || cache2.set(jwk, {}).get(jwk);
if (cached[alg] === void 0) {
const key = await importJWK({ ...jwk, ext: true }, alg);
if (key instanceof Uint8Array || key.type !== "public") {
throw new JWKSInvalid("JSON Web Key Set members must be public keys");
}
cached[alg] = key;
}
return cached[alg];
}
function createLocalJWKSet(jwks) {
const set = new LocalJWKSet(jwks);
const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
Object.defineProperties(localJWKSet, {
jwks: {
value: () => clone(set._jwks),
enumerable: true,
configurable: false,
writable: false
}
});
return localJWKSet;
}
// node_modules/jose/dist/browser/runtime/fetch_jwks.js
var fetchJwks = async (url, timeout, options) => {
let controller;
let id;
let timedOut = false;
if (typeof AbortController === "function") {
controller = new AbortController();
id = setTimeout(() => {
timedOut = true;
controller.abort();
}, timeout);
}
const response = await fetch(url.href, {
signal: controller ? controller.signal : void 0,
redirect: "manual",
headers: options.headers
}).catch((err) => {
if (timedOut)
throw new JWKSTimeout();
throw err;
});
if (id !== void 0)
clearTimeout(id);
if (response.status !== 200) {
throw new JOSEError("Expected 200 OK from the JSON Web Key Set HTTP response");
}
try {
return await response.json();
} catch {
throw new JOSEError("Failed to parse the JSON Web Key Set HTTP response as JSON");
}
};
var fetch_jwks_default = fetchJwks;
// node_modules/jose/dist/browser/jwks/remote.js
function isCloudflareWorkers() {
return typeof WebSocketPair !== "undefined" || typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers" || typeof EdgeRuntime !== "undefined" && EdgeRuntime === "vercel";
}
var USER_AGENT;
if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
const NAME = "jose";
const VERSION = "v5.10.0";
USER_AGENT = `${NAME}/${VERSION}`;
}
var jwksCache = /* @__PURE__ */ Symbol();
function isFreshJwksCache(input, cacheMaxAge) {
if (typeof input !== "object" || input === null) {
return false;
}
if (!("uat" in input) || typeof input.uat !== "number" || Date.now() - input.uat >= cacheMaxAge) {
return false;
}
if (!("jwks" in input) || !isObject4(input.jwks) || !Array.isArray(input.jwks.keys) || !Array.prototype.every.call(input.jwks.keys, isObject4)) {
return false;
}
return true;
}
var RemoteJWKSet = class {
constructor(url, options) {
if (!(url instanceof URL)) {
throw new TypeError("url must be an instance of URL");
}
this._url = new URL(url.href);
this._options = { agent: options?.agent, headers: options?.headers };
this._timeoutDuration = typeof options?.timeoutDuration === "number" ? options?.timeoutDuration : 5e3;
this._cooldownDuration = typeof options?.cooldownDuration === "number" ? options?.cooldownDuration : 3e4;
this._cacheMaxAge = typeof options?.cacheMaxAge === "number" ? options?.cacheMaxAge : 6e5;
if (options?.[jwksCache] !== void 0) {
this._cache = options?.[jwksCache];
if (isFreshJwksCache(options?.[jwksCache], this._cacheMaxAge)) {
this._jwksTimestamp = this._cache.uat;
this._local = createLocalJWKSet(this._cache.jwks);
}
}
}
coolingDown() {
return typeof this._jwksTimestamp === "number" ? Date.now() < this._jwksTimestamp + this._cooldownDuration : false;
}
fresh() {
return typeof this._jwksTimestamp === "number" ? Date.now() < this._jwksTimestamp + this._cacheMaxAge : false;
}
async getKey(protectedHeader, token) {
if (!this._local || !this.fresh()) {
await this.reload();
}
try {
return await this._local(protectedHeader, token);
} catch (err) {
if (err instanceof JWKSNoMatchingKey) {
if (this.coolingDown() === false) {
await this.reload();
return this._local(protectedHeader, token);
}
}
throw err;
}
}
async reload() {
if (this._pendingFetch && isCloudflareWorkers()) {
this._pendingFetch = void 0;
}
const headers = new Headers(this._options.headers);
if (USER_AGENT && !headers.has("User-Agent")) {
headers.set("User-Agent", USER_AGENT);
this._options.headers = Object.fromEntries(headers.entries());
}
this._pendingFetch || (this._pendingFetch = fetch_jwks_default(this._url, this._timeoutDuration, this._options).then((json) => {
this._local = createLocalJWKSet(json);
if (this._cache) {
this._cache.uat = Date.now();
this._cache.jwks = json;
}
this._jwksTimestamp = Date.now();
this._pendingFetch = void 0;
}).catch((err) => {
this._pendingFetch = void 0;
throw err;
}));
await this._pendingFetch;
}
};
function createRemoteJWKSet(url, options) {
const set = new RemoteJWKSet(url, options);
const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
Object.defineProperties(remoteJWKSet, {
coolingDown: {
get: () => set.coolingDown(),
enumerable: true,
configurable: false
},
fresh: {
get: () => set.fresh(),
enumerable: true,
configurable: false
},
reload: {
value: () => set.reload(),
enumerable: true,
configurable: false,
writable: false
},
reloading: {
get: () => !!set._pendingFetch,
enumerable: true,
configurable: false
},
jwks: {
value: () => set._local?.jwks(),
enumerable: true,
configurable: false,
writable: false
}
});
return remoteJWKSet;
}
// node_modules/@logto/client/lib/adapter/defaults.js
var defaultClockTolerance = 300;
var verifyIdToken = async (idToken, clientId, issuer, jwks, clockTolerance = defaultClockTolerance) => {
const result = await jwtVerify(idToken, jwks, { audience: clientId, issuer, clockTolerance });
if (Math.abs((result.payload.iat ?? 0) - Date.now() / 1e3) > clockTolerance) {
throw new LogtoError("id_token.invalid_iat");
}
};
var DefaultJwtVerifier = class {
constructor(client, clockTolerance = defaultClockTolerance) {
this.client = client;
this.clockTolerance = clockTolerance;
}
async verifyIdToken(idToken) {
const { appId } = this.client.logtoConfig;
const { issuer, jwksUri } = await this.client.getOidcConfig();
this.getJwtVerifyGetKey ||= createRemoteJWKSet(new URL(jwksUri));
await verifyIdToken(idToken, appId, issuer, this.getJwtVerifyGetKey, this.clockTolerance);
}
};
// node_modules/@logto/client/lib/adapter/types.js
var PersistKey;
(function(PersistKey2) {
PersistKey2["IdToken"] = "idToken";
PersistKey2["RefreshToken"] = "refreshToken";
PersistKey2["AccessToken"] = "accessToken";
PersistKey2["SignInSession"] = "signInSession";
})(PersistKey || (PersistKey = {}));
var CacheKey;
(function(CacheKey2) {
CacheKey2["OpenidConfig"] = "openidConfiguration";
CacheKey2["Jwks"] = "jwks";
})(CacheKey || (CacheKey = {}));
// node_modules/@logto/client/lib/adapter/index.js
var ClientAdapterInstance = class {
/* END OF IMPLEMENTATION */
constructor(adapter) {
Object.assign(this, adapter);
}
async setStorageItem(key, value) {
if (!value) {
await this.storage.removeItem(key);
return;
}
await this.storage.setItem(key, value);
}
/**
* Try to get the string value from the cache and parse as JSON.
* Return the parsed value if it is an object, return `undefined` otherwise.
*
* @param key The cache key to get value from.
*/
async getCachedObject(key) {
const cached = await trySafe(async () => {
const data = await this.unstable_cache?.getItem(key);
return conditional(data && JSON.parse(data));
});
if (cached && typeof cached === "object") {
return cached;
}
}
/**
* Try to get the value from the cache first, if it doesn't exist in cache,
* run the getter function and store the result into cache.
*
* @param key The cache key to get value from.
*/
async getWithCache(key, getter) {
const cached = await this.getCachedObject(key);
if (cached) {
return cached;
}
const result = await getter();
await this.unstable_cache?.setItem(key, JSON.stringify(result));
return result;
}
};
// node_modules/@logto/client/lib/errors.js
var logtoClientErrorCodes = Object.freeze({
"sign_in_session.invalid": "Invalid sign-in session.",
"sign_in_session.not_found": "Sign-in session not found.",
not_authenticated: "Not authenticated.",
fetch_user_info_failed: "Unable to fetch user info. The access token may be invalid.",
user_cancelled: "The user cancelled the action.",
missing_scope_organizations: `The \`${UserScope.Organizations}\` scope is required`
});
var LogtoClientError = class extends Error {
constructor(code, data) {
super(logtoClientErrorCodes[code]);
this.name = "LogtoClientError";
this.code = code;
this.data = data;
}
};
// node_modules/@logto/client/lib/types/index.js
var normalizeLogtoConfig = (config) => {
const { prompt = Prompt.Consent, scopes = [], resources, ...rest } = config;
const includeReservedScopes = config.includeReservedScopes ?? true;
return {
...rest,
prompt,
scopes: includeReservedScopes ? withReservedScopes(scopes).split(" ") : scopes,
resources: scopes.includes(UserScope.Organizations) ? deduplicate([...resources ?? [], ReservedResource.Organization]) : resources,
includeReservedScopes
};
};
var isLogtoSignInSessionItem = (data) => {
if (!isArbitraryObject(data)) {
return false;
}
return ["redirectUri", "codeVerifier", "state"].every((key) => typeof data[key] === "string");
};
var isLogtoAccessTokenMap = (data) => {
if (!isArbitraryObject(data)) {
return false;
}
return Object.values(data).every((value) => {
if (!isArbitraryObject(value)) {
return false;
}
return typeof value.token === "string" && typeof value.scope === "string" && typeof value.expiresAt === "number";
});
};
// node_modules/@logto/client/lib/utils/index.js
var buildAccessTokenKey = (resource = "", organizationId, scopes = []) => `${scopes.slice().sort().join(" ")}@${resource}${conditionalString(organizationId && `#${organizationId}`)}`;
var getDiscoveryEndpoint = (endpoint) => appendPath(new URL(endpoint), discoveryPath).toString();
// node_modules/@logto/client/lib/utils/memoize.js
function memoize(run) {
const promiseCache = /* @__PURE__ */ new Map();
const memoized = async function(...args) {
const promiseKey = JSON.stringify(args);
const cachedPromise = promiseCache.get(promiseKey);
if (cachedPromise) {
return cachedPromise;
}
const promise = (async () => {
try {
return await run.apply(this, args);
} finally {
promiseCache.delete(promiseKey);
}
})();
promiseCache.set(promiseKey, promise);
return promise;
};
return memoized;
}
// node_modules/@logto/client/lib/utils/once.js
function once2(function_) {
let called = false;
let result;
return function(...args) {
if (!called) {
called = true;
result = function_.apply(this, args);
}
return result;
};
}
// node_modules/@logto/client/lib/client.js
var StandardLogtoClient = class {
get jwtVerifier() {
return this.jwtVerifierInstance;
}
constructor(logtoConfig, adapter, buildJwtVerifier) {
this.getOidcConfig = once2(this.#getOidcConfig);
this.getAccessToken = memoize(this.#getAccessToken);
this.getOrganizationToken = memoize(this.#getOrganizationToken);
this.clearAccessToken = memoize(this.#clearAccessToken);
this.clearAllTokens = memoize(this.#clearAllTokens);
this.handleSignInCallback = memoize(this.#handleSignInCallback);
this.accessTokenMap = /* @__PURE__ */ new Map();
this.logtoConfig = normalizeLogtoConfig(logtoConfig);
this.adapter = new ClientAdapterInstance(adapter);
this.jwtVerifierInstance = buildJwtVerifier(this);
void this.loadAccessTokenMap();
}
/**
* Set the JWT verifier for the client.
* @param buildJwtVerifier The JWT verifier instance or a function that returns the JWT verifier instance.
*/
setJwtVerifier(buildJwtVerifier) {
this.jwtVerifierInstance = typeof buildJwtVerifier === "function" ? buildJwtVerifier(this) : buildJwtVerifier;
}
/**
* Check if the user is authenticated by checking if the ID token exists.
*/
async isAuthenticated() {
return Boolean(await this.getIdToken());
}
/**
* Get the Refresh Token from the storage.
*/
async getRefreshToken() {
return this.adapter.storage.getItem("refreshToken");
}
/**
* Get the ID Token from the storage. If you want to get the ID Token claims,
* use {@link getIdTokenClaims} instead.
*/
async getIdToken() {
return this.adapter.storage.getItem("idToken");
}
/**
* Get the ID Token claims.
*/
async getIdTokenClaims() {
const idToken = await this.getIdToken();
if (!idToken) {
throw new LogtoClientError("not_authenticated", "ID token not found");
}
return decodeIdToken(idToken);
}
/**
* Get the access token claims for the specified resource.
*
* @param resource The resource that the access token is granted for. If not
* specified, the access token will be used for OpenID Connect or the default
* resource, as specified in the Logto Console.
*/
async getAccessTokenClaims(resource) {
const accessToken = await this.getAccessToken(resource);
return decodeAccessToken(accessToken);
}
/**
* Get the organization token claims for the specified organization.
*
* @param organizationId The ID of the organization that the access token is granted for.
*/
async getOrganizationTokenClaims(organizationId) {
const accessToken = await this.getOrganizationToken(organizationId);
return decodeAccessToken(accessToken);
}
/**
* Get the user information from the Userinfo Endpoint.
*
* Note the Userinfo Endpoint will return more claims than the ID Token. See
* {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#fetch-user-information | Fetch user information}
* for more information.
*
* @returns The user information.
* @throws LogtoClientError if the user is not authenticated.
*/
async fetchUserInfo() {
const { userinfoEndpoint } = await this.getOidcConfig();
const accessToken = await this.getAccessToken();
if (!accessToken) {
throw new LogtoClientError("fetch_user_info_failed");
}
return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);
}
async signIn(options, mode, hint) {
const { redirectUri: redirectUriUrl, postRedirectUri: postRedirectUriUrl, firstScreen, identifiers, interactionMode, loginHint, directSignIn, extraParams, prompt, clearTokens } = typeof options === "string" || options instanceof URL ? {
redirectUri: options,
postRedirectUri: void 0,
firstScreen: void 0,
identifiers: void 0,
interactionMode: mode,
loginHint: hint,
directSignIn: void 0,
extraParams: void 0,
prompt: void 0,
clearTokens: true
} : options;
const redirectUri = redirectUriUrl.toString();
const postRedirectUri = postRedirectUriUrl?.toString();
const { appId: clientId, prompt: promptViaConfig, resources, scopes, includeReservedScopes } = this.logtoConfig;
const { authorizationEndpoint } = await this.getOidcConfig();
const [codeVerifier, state] = await Promise.all([
this.adapter.generateCodeVerifier(),
this.adapter.generateState()
]);
const codeChallenge = await this.adapter.generateCodeChallenge(codeVerifier);
const signInUri = generateSignInUri({
authorizationEndpoint,
clientId,
redirectUri: redirectUri.toString(),
codeChallenge,
state,
scopes,
resources,
prompt: prompt ?? promptViaConfig,
firstScreen,
identifiers,
interactionMode,
loginHint,
directSignIn,
extraParams,
includeReservedScopes
});
await Promise.all([
this.setSignInSession({ redirectUri, postRedirectUri, codeVerifier, state }),
clearTokens === false ? void 0 : this.clearAllTokens()
]);
await this.adapter.navigate(signInUri, { redirectUri, for: "sign-in" });
}
/**
* Check if the user is redirected from the sign-in page by checking if the
* current URL matches the redirect URI in the sign-in session.
*
* If there's no sign-in session, it will return `false`.
*
* @param url The current URL.
*/
async isSignInRedirected(url) {
const signInSession = await this.getSignInSession();
if (!signInSession) {
return false;
}
const { redirectUri } = signInSession;
const { origin, pathname } = new URL(url);
return `${origin}${pathname}` === redirectUri;
}
/**
* Start the sign-out flow with the specified redirect URI. The URI must be
* registered in the Logto Console.
*
* It will also revoke all the tokens and clean up the storage.
*
* The user will be redirected that URI after the sign-out flow is completed.
* If the `postLogoutRedirectUri` is not specified, the user will be redirected
* to a default page.
*/
async signOut(postLogoutRedirectUri) {
const { appId: clientId } = this.logtoConfig;
const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();
const refreshToken = await this.getRefreshToken();
if (refreshToken) {
try {
await revoke(revocationEndpoint, clientId, refreshToken, this.adapter.requester);
} catch {
}
}
const url = generateSignOutUri({
endSessionEndpoint,
postLogoutRedirectUri,
clientId
});
await this.clearAllTokens();
await this.adapter.navigate(url, { redirectUri: postLogoutRedirectUri, for: "sign-out" });
}
async getSignInSession() {
const jsonItem = await this.adapter.storage.getItem("signInSession");
if (!jsonItem) {
return null;
}
const item = JSON.parse(jsonItem);
if (!isLogtoSignInSessionItem(item)) {
throw new LogtoClientError("sign_in_session.invalid");
}
return item;
}
async setSignInSession(value) {
return this.adapter.setStorageItem(PersistKey.SignInSession, value && JSON.stringify(value));
}
async setIdToken(value) {
return this.adapter.setStorageItem(PersistKey.IdToken, value);
}
async setRefreshToken(value) {
return this.adapter.setStorageItem(PersistKey.RefreshToken, value);
}
async getAccessTokenByRefreshToken(resource, organizationId) {
const currentRefreshToken = await this.getRefreshToken();
if (!currentRefreshToken) {
throw new LogtoClientError("not_authenticated", "Refresh token not found");
}
const accessTokenKey = buildAccessTokenKey(resource, organizationId);
const { appId: clientId } = this.logtoConfig;
const { tokenEndpoint } = await this.getOidcConfig();
const requestedAt = Math.round(Date.now() / 1e3);
const { accessToken, refreshToken, idToken, scope, expiresIn } = await fetchTokenByRefreshToken({
clientId,
tokenEndpoint,
refreshToken: currentRefreshToken,
resource,
organizationId
}, this.adapter.requester);
this.accessTokenMap.set(accessTokenKey, {
token: accessToken,
scope,
/** The `expiresAt` variable provides an approximate estimation of the actual `exp` property
* in the token claims. It is utilized by the client to determine if the cached access token
* has expired and when a new access token should be requested.
*/
expiresAt: requestedAt + expiresIn
});
await this.saveAccessTokenMap();
if (refreshToken) {
await this.setRefreshToken(refreshToken);
}
if (idToken) {
await this.jwtVerifier.verifyIdToken(idToken);
await this.setIdToken(idToken);
}
return accessToken;
}
async saveAccessTokenMap() {
const data = {};
for (const [key, accessToken] of this.accessTokenMap.entries()) {
data[key] = accessToken;
}
await this.adapter.storage.setItem("accessToken", JSON.stringify(data));
}
async loadAccessTokenMap() {
const raw = await this.adapter.storage.getItem("accessToken");
if (!raw) {
return;
}
try {
const json = JSON.parse(raw);
if (!isLogtoAccessTokenMap(json)) {
return;
}
this.accessTokenMap.clear();
for (const [key, accessToken] of Object.entries(json)) {
this.accessTokenMap.set(key, accessToken);
}
} catch (error) {
console.warn(error);
}
}
async #getOidcConfig() {
return this.adapter.getWithCache(CacheKey.OpenidConfig, async () => {
return fetchOidcConfig(getDiscoveryEndpoint(this.logtoConfig.endpoint), this.adapter.requester);
});
}
async #getAccessToken(resource, organizationId) {
if (!await this.isAuthenticated()) {
throw new LogtoClientError("not_authenticated");
}
const accessTokenKey = buildAccessTokenKey(resource, organizationId);
const accessToken = this.accessTokenMap.get(accessTokenKey);
if (accessToken && accessToken.expiresAt > Date.now() / 1e3) {
return accessToken.token;
}
if (accessToken) {
this.accessTokenMap.delete(accessTokenKey);
}
return this.getAccessTokenByRefreshToken(resource, organizationId);
}
async #getOrganizationToken(organizationId) {
if (!this.logtoConfig.scopes?.includes(UserScope.Organizations)) {
throw new LogtoClientError("missing_scope_organizations");
}
return this.getAccessToken(void 0, organizationId);
}
async #clearAccessToken() {
this.accessTokenMap.clear();
await this.adapter.storage.removeItem("accessToken");
}
async #clearAllTokens() {
await Promise.all([this.setRefreshToken(null), this.setIdToken(null), this.clearAccessToken()]);
}
async #handleSignInCallback(callbackUri) {
const signInSession = await this.getSignInSession();
if (!signInSession) {
throw new LogtoClientError("sign_in_session.not_found");
}
const { redirectUri, postRedirectUri, state, codeVerifier } = signInSession;
const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);
const accessTokenKey = buildAccessTokenKey();
const { appId: clientId } = this.logtoConfig;
const { tokenEndpoint } = await this.getOidcConfig();
const requestedAt = Math.round(Date.now() / 1e3);
const { idToken, refreshToken, accessToken, scope, expiresIn } = await fetchTokenByAuthorizationCode({
clientId,
tokenEndpoint,
redirectUri,
codeVerifier,
code
}, this.adapter.requester);
await this.jwtVerifier.verifyIdToken(idToken);
await this.setRefreshToken(refreshToken ?? null);
await this.setIdToken(idToken);
this.accessTokenMap.set(accessTokenKey, {
token: accessToken,
scope,
/** The `expiresAt` variable provides an approximate estimation of the actual `exp` property
* in the token claims. It is utilized by the client to determine if the cached access token
* has expired and when a new access token should be requested.
*/
expiresAt: requestedAt + expiresIn
});
await this.saveAccessTokenMap();
await this.setSignInSession(null);
if (postRedirectUri) {
await this.adapter.navigate(postRedirectUri, { for: "post-sign-in" });
}
}
};
// node_modules/@logto/client/lib/utils/requester.js
var createRequester = (fetchFunction) => {
return async (...args) => {
const response = await fetchFunction(...args);
if (!response.ok) {
const cloned = response.clone();
const responseJson = await response.json();
console.error(`Logto requester error: [status=${response.status}]`, responseJson);
if (!isLogtoRequestErrorJson(responseJson)) {
throw new LogtoError("unexpected_response_error", responseJson);
}
const { code, message: message2 } = responseJson;
throw new LogtoRequestError(code, message2, cloned);
}
return response.json();
};
};
// node_modules/@logto/client/lib/index.js
var LogtoClient = class extends StandardLogtoClient {
constructor(logtoConfig, adapter, buildJwtVerifier) {
super(logtoConfig, adapter, buildJwtVerifier ?? ((client) => new DefaultJwtVerifier(client)));
}
};
// node_modules/js-base64/base64.mjs
var _hasBuffer = typeof Buffer === "function";
var _TD = typeof TextDecoder === "function" ? new TextDecoder() : void 0;
var _TE = typeof TextEncoder === "function" ? new TextEncoder() : void 0;
var b64ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var b64chs = Array.prototype.slice.call(b64ch);
var b64tab = ((a) => {
let tab = {};
a.forEach((c, i) => tab[c] = i);
return tab;
})(b64chs);
var _fromCC = String.fromCharCode.bind(String);
var _U8Afrom = typeof Uint8Array.from === "function" ? Uint8Array.from.bind(Uint8Array) : (it) => new Uint8Array(Array.prototype.slice.call(it, 0));
var _mkUriSafe = (src) => src.replace(/=/g, "").replace(/[+\/]/g, (m0) => m0 == "+" ? "-" : "_");
var btoaPolyfill = (bin) => {
let u32, c0, c1, c2, asc = "";
const pad = bin.length % 3;
for (let i = 0; i < bin.length; ) {
if ((c0 = bin.charCodeAt(i++)) > 255 || (c1 = bin.charCodeAt(i++)) > 255 || (c2 = bin.charCodeAt(i++)) > 255)
throw new TypeError("invalid character found");
u32 = c0 << 16 | c1 << 8 | c2;
asc += b64chs[u32 >> 18 & 63] + b64chs[u32 >> 12 & 63] + b64chs[u32 >> 6 & 63] + b64chs[u32 & 63];
}
return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
};
var _btoa = typeof btoa === "function" ? (bin) => btoa(bin) : _hasBuffer ? (bin) => Buffer.from(bin, "binary").toString("base64") : btoaPolyfill;
var _fromUint8Array = _hasBuffer ? (u8a) => Buffer.from(u8a).toString("base64") : (u8a) => {
const maxargs = 4096;
let strs = [];
for (let i = 0, l = u8a.length; i < l; i += maxargs) {
strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
}
return _btoa(strs.join(""));
};
var fromUint8Array = (u8a, urlsafe = false) => urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
// node_modules/@logto/browser/lib/utils/generators.js
var generateRandomString = (length = 64) => fromUint8Array(crypto.getRandomValues(new Uint8Array(length)), true);
var generateState = () => generateRandomString();
var generateCodeVerifier = () => generateRandomString();
var generateCodeChallenge = async (codeVerifier) => {
if (crypto.subtle === void 0) {
throw new LogtoError("crypto_subtle_unavailable");
}
const encodedCodeVerifier = new TextEncoder().encode(codeVerifier);
const codeChallenge = new Uint8Array(await crypto.subtle.digest("SHA-256", encodedCodeVerifier));
return fromUint8Array(codeChallenge, true);
};
// node_modules/@logto/chrome-extension/lib/storage.js
var keyPrefix = `logto`;
var ChromeExtensionStorage = class {
constructor(appId) {
this.appId = appId;
}
getKey(item) {
if (item === void 0) {
return `${keyPrefix}:${this.appId}`;
}
return `${keyPrefix}:${this.appId}:${item}`;
}
// eslint-disable-next-line @typescript-eslint/ban-types
async getItem(key) {
const storageKey = this.getKey(key);
const object = await chrome.storage.local.get(storageKey);
return object[storageKey] ? String(object[storageKey]) : null;
}
async setItem(key, value) {
await chrome.storage.local.set({ [this.getKey(key)]: value });
}
async removeItem(key) {
await chrome.storage.local.remove(this.getKey(key));
}
};
// node_modules/@logto/chrome-extension/lib/index.js
var LogtoClient2 = class extends LogtoClient {
/**
* @param config The configuration object for the client.
*/
constructor(config) {
const requester = createRequester(fetch);
const navigate = async (url, params) => {
switch (params.for) {
case "sign-in": {
const responseUrl = await chrome.identity.launchWebAuthFlow({ url, interactive: true });
if (!responseUrl) {
throw new LogtoClientError("user_cancelled");
}
await this.handleSignInCallback(responseUrl);
break;
}
case "sign-out": {
await chrome.identity.launchWebAuthFlow({
url,
interactive: false,
abortOnLoadForNonInteractive: false,
timeoutMsForNonInteractive: 1e4
});
break;
}
default: {
throw new Error(`Unsupported navigation for ${params.for}`);
}
}
};
super(config, {
requester,
navigate,
storage: new ChromeExtensionStorage(config.appId),
generateCodeChallenge,
generateCodeVerifier,
generateState
});
}
};
// src/background/auth/client.ts
function createLogtoAuthClient() {
const config = readAuthConfig();
const client = new LogtoClient2({
appId: config.appId,
endpoint: config.logtoEndpoint,
resources: [config.apiResource],
scopes: config.scopes
});
return {
getAccessToken(resource) {
return client.getAccessToken(resource);
},
getIdTokenClaims() {
return client.getIdTokenClaims();
},
isAuthenticated() {
return client.isAuthenticated();
},
signIn() {
return client.signIn(readChromeIdentity().getRedirectURL("/callback"));
},
signOut() {
return client.signOut(readChromeIdentity().getRedirectURL());
}
};
}
function readChromeIdentity() {
const identity = globalThis.chrome?.identity;
if (typeof identity?.getRedirectURL !== "function") {
throw new Error("chrome.identity.getRedirectURL is unavailable");
}
return {
getRedirectURL: identity.getRedirectURL.bind(identity)
};
}
// src/shared/auth-messages.ts
var authRequestTypes = /* @__PURE__ */ new Set([
"auth:get-state",
"auth:sign-in",
"auth:sign-out",
"auth:get-access-token"
]);
function isAuthRequestMessage(value) {
if (!value || typeof value !== "object") {
return false;
}
const candidate = value;
return typeof candidate.type === "string" && authRequestTypes.has(candidate.type);
}
function isAuthResponseMessage(value) {
if (!value || typeof value !== "object") {
return false;
}
const candidate = value;
if (candidate.ok === false) {
return candidate.type === "auth:error" && typeof candidate.error === "string";
}
if (candidate.ok !== true || typeof candidate.type !== "string") {
return false;
}
if (candidate.type === "auth:ack") {
return true;
}
if (candidate.type === "auth:token") {
return Boolean(
candidate.value && typeof candidate.value === "object" && typeof candidate.value.accessToken === "string"
);
}
if (candidate.type === "auth:state") {
return Boolean(
candidate.value && typeof candidate.value === "object" && typeof candidate.value.isAuthenticated === "boolean"
);
}
return false;
}
// src/shared/batch-submit-config.ts
var DEFAULT_BATCH_SUBMIT_BASE_URL = "http://192.168.31.21:8083";
// src/shared/batch-submit-client.ts
function createBatchSubmitClient(options) {
const baseUrl = options.baseUrl ?? DEFAULT_BATCH_SUBMIT_BASE_URL;
const fetchImpl = options.fetchImpl ?? fetch;
const getAccessToken = options.getAccessToken ?? (() => readAccessToken(options.sendMessage));
return {
async submitBatch(payload) {
const token = await getAccessToken();
const response = await fetchImpl(
buildBatchSubmitUrl(baseUrl),
{
body: JSON.stringify(payload),
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
method: "POST"
}
);
if (response.status === 401 || response.status === 403) {
throw new Error("batch submit unauthorized");
}
if (!response.ok) {
throw new Error(`batch submit failed: ${response.status}`);
}
return readBatchSubmitResponse(await response.json());
}
};
}
function buildBatchSubmitUrl(baseUrl) {
return new URL("/api/v1/batch-status/batches", baseUrl).toString();
}
async function readAccessToken(sendMessage) {
const response = await sendMessage({ type: "auth:get-access-token" });
if (!isAuthResponseMessage(response) || !response.ok || response.type !== "auth:token" || !response.value.accessToken.trim()) {
throw new Error("batch submit token unavailable");
}
return response.value.accessToken;
}
function readBatchSubmitResponse(payload) {
if (!isRecord(payload)) {
throw new Error("batch submit response is invalid");
}
if (payload.success !== true) {
const message2 = typeof payload.msg === "string" && payload.msg.trim() ? payload.msg : "batch submit failed";
throw new Error(message2);
}
return "data" in payload ? payload.data : payload;
}
function isRecord(value) {
return typeof value === "object" && value !== null;
}
// src/shared/backend-metrics-config.ts
var DEFAULT_BACKEND_METRICS_BASE_URL = "https://talent-search.intelligrow.cn";
// src/shared/backend-metrics-client.ts
function createBackendMetricsClient(options) {
const baseUrl = options.baseUrl ?? DEFAULT_BACKEND_METRICS_BASE_URL;
const fetchImpl = options.fetchImpl ?? defaultFetch;
return {
async searchByStarIds(starIds) {
const response = await fetchImpl(buildBackendMetricsSearchUrl(baseUrl), {
body: JSON.stringify(buildBackendMetricsSearchRequestBody(starIds)),
headers: {
Authorization: `Bearer ${await options.getAccessToken()}`,
"Content-Type": "application/json"
},
method: "POST"
});
if (!response.ok) {
throw new Error("backend metrics request failed");
}
return mapBackendMetricsSearchResponse(await response.json());
}
};
}
function buildBackendMetricsSearchUrl(baseUrl) {
return new URL("/api/v1/history/talents/search", baseUrl).toString();
}
function buildBackendMetricsSearchRequestBody(starIds) {
return {
page: 1,
size: Math.max(20, starIds.length),
type: "star_id",
values: starIds
};
}
function mapBackendMetricsSearchResponse(payload) {
const rows = readResponseRows(payload);
if (!rows) {
throw new Error("backend metrics response is invalid");
}
return rows.flatMap((row) => {
if (!isRecord2(row) || typeof row.star_id !== "string") {
return [];
}
return [
{
a3IncreaseCount: formatDecimalValue(
readAverageA3IncreaseCount(row)
),
afterViewSearchCount: formatDecimalValue(row.avg_after_view_search_cnt),
afterViewSearchRate: formatRateValue(row.avg_after_view_search_rate),
cpSearch: formatDecimalValue(row.cp_search),
cpa3: formatDecimalValue(readCpa3Value(row)),
newA3Rate: formatRateValue(row.avg_new_a3_rate),
starId: row.star_id
}
];
});
}
function readAverageA3IncreaseCount(row) {
const directAverage = readFiniteNumber(row.avg_a3_increase_cnt);
if (directAverage !== null) {
return directAverage;
}
const totalNewA3 = readTotalNewA3Value(row);
const videoCount = readFiniteNumber(row.video_count) ?? readNestedVideoCount(row.videos);
if (totalNewA3 === null || videoCount === null || videoCount <= 0) {
return null;
}
return totalNewA3 / videoCount;
}
function readCpa3Value(row) {
const directCpa3 = readFiniteNumber(row.cpa3);
if (directCpa3 !== null) {
return directCpa3;
}
const totalCost = readFiniteNumber(row.total_estimated_video_cost);
const totalNewA3 = readTotalNewA3Value(row);
if (totalCost === null || totalNewA3 === null || totalNewA3 <= 0) {
return null;
}
return totalCost / totalNewA3;
}
function readTotalNewA3Value(row) {
const derivedFromTotals = deriveTotalNewA3FromTotals(row);
if (derivedFromTotals !== null) {
return derivedFromTotals;
}
return deriveTotalNewA3FromVideos(row.videos);
}
function deriveTotalNewA3FromTotals(row) {
const totalPlayCount = readFiniteNumber(row.total_play_cnt);
const averageNewA3Rate = readFiniteNumber(row.avg_new_a3_rate);
if (totalPlayCount === null || averageNewA3Rate === null) {
return null;
}
return totalPlayCount * averageNewA3Rate;
}
function deriveTotalNewA3FromVideos(value) {
if (!Array.isArray(value)) {
return null;
}
let total = 0;
let hasFiniteValue = false;
value.forEach((video) => {
if (!isRecord2(video)) {
return;
}
const newA3 = readFiniteNumber(video.new_a3);
if (newA3 === null) {
return;
}
hasFiniteValue = true;
total += newA3;
});
return hasFiniteValue ? total : null;
}
function readNestedVideoCount(value) {
return Array.isArray(value) ? value.length : null;
}
function readResponseRows(payload) {
if (!isRecord2(payload) || payload.success !== true) {
return null;
}
const topLevelData = isRecord2(payload.data) ? payload.data : null;
return Array.isArray(topLevelData?.data) ? topLevelData.data : null;
}
function formatRateValue(value) {
const number = typeof value === "number" ? value : Number(value);
if (Number.isFinite(number)) {
const percentage = number * 100;
const formatted = new Intl.NumberFormat("en-US", {
maximumFractionDigits: 2,
minimumFractionDigits: percentage % 1 === 0 ? 0 : 2
}).format(percentage);
return `${formatted}%`;
}
return "";
}
function formatDecimalValue(value) {
const number = typeof value === "number" ? value : Number(value);
if (!Number.isFinite(number)) {
return "";
}
return new Intl.NumberFormat("en-US", {
maximumFractionDigits: 2,
minimumFractionDigits: 2
}).format(number);
}
async function defaultFetch(input, init) {
return fetch(input, init);
}
function isRecord2(value) {
return typeof value === "object" && value !== null;
}
function readFiniteNumber(value) {
const number = typeof value === "number" ? value : Number(value);
return Number.isFinite(number) ? number : null;
}
// src/shared/backend-metrics-messages.ts
function isBackendMetricsSearchRequestMessage(value) {
if (!value || typeof value !== "object") {
return false;
}
const candidate = value;
return candidate.type === "backend-metrics:search" && Boolean(
candidate.value && typeof candidate.value === "object" && Array.isArray(candidate.value.starIds) && candidate.value.starIds.every(
(starId) => typeof starId === "string"
)
);
}
// src/background/index.ts
function registerBackgroundMessageHandler(chromeLike = readChromeLike(), dependencies = {}) {
let authController = dependencies.authController;
let searchBackendMetrics = dependencies.searchBackendMetrics;
let submitBatch = dependencies.submitBatch;
chromeLike.runtime?.onMessage?.addListener((message2, _sender, sendResponse) => {
if (isDownloadMarketCsvMessage(message2)) {
void triggerCsvDownload(chromeLike, message2).then(() => {
sendResponse({ ok: true });
}).catch((error) => {
sendResponse({
error: error instanceof Error ? error.message : String(error),
ok: false
});
});
return true;
}
if (isBatchSubmitMessage(message2)) {
authController ??= createAuthController({
authClient: createLogtoAuthClient()
});
submitBatch ??= createBatchSubmitClient({
baseUrl: DEFAULT_BATCH_SUBMIT_BASE_URL,
getAccessToken: () => authController.getAccessToken(),
sendMessage: () => Promise.reject(new Error("background batch submit does not use sendMessage"))
}).submitBatch;
void submitBatch(message2.payload).then((value) => {
sendResponse({
ok: true,
type: "batch:ack",
value
});
}).catch((error) => {
sendResponse({
error: error instanceof Error ? error.message : String(error),
ok: false,
type: "batch:error"
});
});
return true;
}
if (isBackendMetricsSearchRequestMessage(message2)) {
authController ??= createAuthController({
authClient: createLogtoAuthClient()
});
searchBackendMetrics ??= createBackendMetricsClient({
baseUrl: DEFAULT_BACKEND_METRICS_BASE_URL,
getAccessToken: () => authController.getAccessToken()
}).searchByStarIds;
void searchBackendMetrics(message2.value.starIds).then((rows) => {
sendResponse({
ok: true,
type: "backend-metrics:result",
value: {
rows
}
});
}).catch((error) => {
sendResponse({
error: error instanceof Error ? error.message : String(error),
ok: false,
type: "backend-metrics:error"
});
});
return true;
}
if (!isAuthRequestMessage(message2)) {
return;
}
authController ??= createAuthController({
authClient: createLogtoAuthClient()
});
void handleAuthMessage(authController, message2).then((response) => {
sendResponse(response);
}).catch((error) => {
sendResponse({
error: error instanceof Error ? error.message : String(error),
ok: false,
type: "auth:error"
});
});
return true;
});
}
async function handleAuthMessage(authController, message2) {
if (message2.type === "auth:get-state") {
return {
ok: true,
type: "auth:state",
value: await authController.getAuthState()
};
}
if (message2.type === "auth:get-access-token") {
return {
ok: true,
type: "auth:token",
value: {
accessToken: await authController.getAccessToken()
}
};
}
if (message2.type === "auth:sign-in") {
await authController.signIn();
return {
ok: true,
type: "auth:ack"
};
}
await authController.signOut();
return {
ok: true,
type: "auth:ack"
};
}
function readChromeLike() {
return globalThis.chrome ?? {};
}
async function triggerCsvDownload(chromeLike, message2) {
if (!chromeLike.downloads?.download) {
throw new Error("chrome.downloads.download is unavailable");
}
const csvUrl = `data:text/csv;charset=utf-8,${encodeURIComponent(`\uFEFF${message2.csv}`)}`;
await Promise.resolve(
chromeLike.downloads.download({
filename: message2.filename,
saveAs: false,
url: csvUrl
})
);
}
function isDownloadMarketCsvMessage(message2) {
if (!message2 || typeof message2 !== "object") {
return false;
}
const candidate = message2;
return candidate.type === "download-market-csv" && typeof candidate.csv === "string" && typeof candidate.filename === "string";
}
function isBatchSubmitMessage(message2) {
if (!message2 || typeof message2 !== "object") {
return false;
}
const candidate = message2;
return candidate.type === "batch:submit" && "payload" in candidate;
}
registerBackgroundMessageHandler();
})();
/*! Bundled license information:
@silverhand/essentials/lib/utilities/assertions.js:
(*!
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*)
*/