0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(core): add POST /saml-applications and DEL /saml-applications/:id APIs

This commit is contained in:
Darcy Ye 2024-11-19 15:54:44 +08:00
parent 0f1c9c1477
commit 0795ba9364
No known key found for this signature in database
GPG key ID: B46F4C07EDEFC610
23 changed files with 691 additions and 126 deletions

View file

@ -12,7 +12,7 @@
}, },
"scripts": { "scripts": {
"precommit": "lint-staged", "precommit": "lint-staged",
"copy:apidocs": "rsync -a -m --include '*/' --include '*.openapi.json' --exclude '*' src/routes/ build/routes/", "copy:apidocs": "rsync -a -m --include '*/' --include '*.openapi.json' --exclude '*' src/routes/ build/routes/ && rsync -a -m --include '*/' --include '*.openapi.json' --exclude '*' src/saml-applications/ build/saml-applications/",
"check": "tsc --noEmit", "check": "tsc --noEmit",
"build": "tsup", "build": "tsup",
"build:test": "rm -rf build/ && tsc -p tsconfig.test.json --sourcemap && pnpm run copy:apidocs", "build:test": "rm -rf build/ && tsc -p tsconfig.test.json --sourcemap && pnpm run copy:apidocs",
@ -80,6 +80,7 @@
"ky": "^1.2.3", "ky": "^1.2.3",
"lru-cache": "^11.0.0", "lru-cache": "^11.0.0",
"nanoid": "^5.0.1", "nanoid": "^5.0.1",
"node-forge": "^1.3.1",
"oidc-provider": "^8.4.6", "oidc-provider": "^8.4.6",
"openapi-types": "^12.1.3", "openapi-types": "^12.1.3",
"otplib": "^12.0.1", "otplib": "^12.0.1",
@ -114,6 +115,7 @@
"@types/koa-send": "^4.1.3", "@types/koa-send": "^4.1.3",
"@types/koa__cors": "^5.0.0", "@types/koa__cors": "^5.0.0",
"@types/node": "^20.9.5", "@types/node": "^20.9.5",
"@types/node-forge": "^1.3.1",
"@types/oidc-provider": "^8.4.4", "@types/oidc-provider": "^8.4.4",
"@types/pluralize": "^0.0.33", "@types/pluralize": "^0.0.33",
"@types/qrcode": "^1.5.2", "@types/qrcode": "^1.5.2",

View file

@ -152,7 +152,7 @@ export default function applicationRoutes<T extends ManagementApiRouter>(
const { oidcClientMetadata, protectedAppMetadata, ...rest } = ctx.guard.body; const { oidcClientMetadata, protectedAppMetadata, ...rest } = ctx.guard.body;
if (rest.type === ApplicationType.SAML) { if (rest.type === ApplicationType.SAML) {
throw new RequestError('application.use_saml_app_api'); throw new RequestError('application.saml.use_saml_app_api');
} }
await Promise.all([ await Promise.all([
@ -268,7 +268,7 @@ export default function applicationRoutes<T extends ManagementApiRouter>(
const pendingUpdateApplication = await queries.applications.findApplicationById(id); const pendingUpdateApplication = await queries.applications.findApplicationById(id);
if (pendingUpdateApplication.type === ApplicationType.SAML) { if (pendingUpdateApplication.type === ApplicationType.SAML) {
throw new RequestError('application.use_saml_app_api'); throw new RequestError('application.saml.use_saml_app_api');
} }
// @deprecated // @deprecated
@ -348,7 +348,7 @@ export default function applicationRoutes<T extends ManagementApiRouter>(
const { type, protectedAppMetadata } = await queries.applications.findApplicationById(id); const { type, protectedAppMetadata } = await queries.applications.findApplicationById(id);
if (type === ApplicationType.SAML) { if (type === ApplicationType.SAML) {
throw new RequestError('application.use_saml_app_api'); throw new RequestError('application.saml.use_saml_app_api');
} }
if (type === ApplicationType.Protected && protectedAppMetadata) { if (type === ApplicationType.Protected && protectedAppMetadata) {

View file

@ -7,6 +7,7 @@ import koaAuditLog from '#src/middleware/koa-audit-log.js';
import koaBodyEtag from '#src/middleware/koa-body-etag.js'; import koaBodyEtag from '#src/middleware/koa-body-etag.js';
import { koaManagementApiHooks } from '#src/middleware/koa-management-api-hooks.js'; import { koaManagementApiHooks } from '#src/middleware/koa-management-api-hooks.js';
import koaTenantGuard from '#src/middleware/koa-tenant-guard.js'; import koaTenantGuard from '#src/middleware/koa-tenant-guard.js';
import samlApplicationRoutes from '#src/saml-applications/routes/index.js';
import type TenantContext from '#src/tenants/TenantContext.js'; import type TenantContext from '#src/tenants/TenantContext.js';
import koaAuth from '../middleware/koa-auth/index.js'; import koaAuth from '../middleware/koa-auth/index.js';
@ -99,6 +100,9 @@ const createRouters = (tenant: TenantContext) => {
systemRoutes(managementRouter, tenant); systemRoutes(managementRouter, tenant);
subjectTokenRoutes(managementRouter, tenant); subjectTokenRoutes(managementRouter, tenant);
accountCentersRoutes(managementRouter, tenant); accountCentersRoutes(managementRouter, tenant);
if (EnvSet.values.isDevFeaturesEnabled) {
samlApplicationRoutes(managementRouter, tenant);
}
const anonymousRouter: AnonymousRouter = new Router(); const anonymousRouter: AnonymousRouter = new Router();

View file

@ -47,11 +47,17 @@ const managementApiIdentifiableEntityNames = Object.freeze([
'organization-role', 'organization-role',
'organization-scope', 'organization-scope',
'organization-invitation', 'organization-invitation',
'saml-application',
]); ]);
/** Additional tags that cannot be inferred from the path. */ /** Additional tags that cannot be inferred from the path. */
const additionalTags = Object.freeze( const additionalTags = Object.freeze(
condArray<string>('Organization applications', 'Custom UI assets', 'Organization users') condArray<string>(
'Organization applications',
'Custom UI assets',
'Organization users',
EnvSet.values.isDevFeaturesEnabled && 'SAML applications'
)
); );
export const buildManagementApiBaseDocument = ( export const buildManagementApiBaseDocument = (
@ -207,7 +213,7 @@ export const buildUserApiBaseDocument = (
}); });
export const getSupplementDocuments = async ( export const getSupplementDocuments = async (
directory = 'routes', directory = 'build',
option?: FindSupplementFilesOptions option?: FindSupplementFilesOptions
) => { ) => {
// Find supplemental documents // Find supplemental documents

View file

@ -35,6 +35,7 @@ const tagMap = new Map([
['sso-connectors', 'SSO connectors'], ['sso-connectors', 'SSO connectors'],
['sso-connector-providers', 'SSO connector providers'], ['sso-connector-providers', 'SSO connector providers'],
['.well-known', 'Well-known'], ['.well-known', 'Well-known'],
['saml-applications', 'SAML applications'],
]); ]);
/** /**
@ -67,25 +68,43 @@ export const findSupplementFiles = async (
option?: FindSupplementFilesOptions option?: FindSupplementFilesOptions
) => { ) => {
const result: string[] = []; const result: string[] = [];
const files = await fs.readdir(directory);
for (const file of await fs.readdir(directory)) { for (const file of files) {
const stats = await fs.stat(path.join(directory, file)); const fullPath = path.join(directory, file);
const stats = await fs.stat(fullPath);
if (
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
option?.excludeDirectories?.includes(file) ||
(option?.includeDirectories && !option.includeDirectories.includes(file))
) {
continue;
}
if (stats.isDirectory()) { if (stats.isDirectory()) {
result.push(...(await findSupplementFiles(path.join(directory, file)))); // Skip if the current directory is excluded
if (option?.excludeDirectories?.includes(file)) {
continue;
}
// Recursively search subdirectories
const subFiles = await findSupplementFiles(fullPath, option);
result.push(...subFiles);
} else if (file.endsWith('.openapi.json')) { } else if (file.endsWith('.openapi.json')) {
result.push(path.join(directory, file)); result.push(fullPath);
} }
} }
// If `includeDirectories` is specified, only keep files that contain the specified subdirectories
if (option?.includeDirectories?.length && option.includeDirectories.length > 0) {
return result.filter((filePath) =>
// The fallback to empty array will never happen here, as we've already checked the length of option.`includeDirectories` before entering this branch
(option.includeDirectories ?? []).some((directory) => filePath.includes(`/${directory}/`))
);
}
// If `excludeDirectories` is specified, exclude files that contain the specified subdirectories
if (option?.excludeDirectories?.length && option.excludeDirectories.length > 0) {
return result.filter(
(filePath) =>
// The fallback to empty array will never happen here, as we've already checked the length of option.`excludeDirectories` before entering this branch
!(option.excludeDirectories ?? []).some((directory) => filePath.includes(`/${directory}/`))
);
}
return result; return result;
}; };
/* eslint-enable @silverhand/fp/no-mutating-methods, no-await-in-loop */ /* eslint-enable @silverhand/fp/no-mutating-methods, no-await-in-loop */

View file

@ -30,7 +30,7 @@ export default function openapiRoutes<T extends AnonymousRouter, R extends Unkno
const { pathMap, tags } = groupRoutesByPath(managementApiRoutes); const { pathMap, tags } = groupRoutesByPath(managementApiRoutes);
// Find supplemental documents // Find supplemental documents
const supplementDocuments = await getSupplementDocuments('routes', { const supplementDocuments = await getSupplementDocuments('build', {
excludeDirectories: ['experience', 'interaction', 'account', 'verification'], excludeDirectories: ['experience', 'interaction', 'account', 'verification'],
}); });
const baseDocument = buildManagementApiBaseDocument(pathMap, tags, ctx.request.origin); const baseDocument = buildManagementApiBaseDocument(pathMap, tags, ctx.request.origin);

View file

@ -0,0 +1,34 @@
import { generateStandardId } from '@logto/shared';
import type Queries from '#src/tenants/Queries.js';
import { generateKeyPairAndCertificate } from './utils.js';
export const createSamlApplicationSecretsLibrary = (queries: Queries) => {
const {
samlApplicationSecrets: { insertSamlApplicationSecret },
} = queries;
const createNewSamlApplicationSecretForApplication = async (
applicationId: string,
// Set certificate life span to 1 year by default.
lifeSpanInDays = 365
) => {
const { privateKey, certificate, notAfter } = await generateKeyPairAndCertificate(
lifeSpanInDays
);
return insertSamlApplicationSecret({
id: generateStandardId(),
applicationId,
privateKey,
certificate,
expiresAt: Math.floor(notAfter.getTime() / 1000),
active: false,
});
};
return {
createNewSamlApplicationSecretForApplication,
};
};

View file

@ -0,0 +1,58 @@
import crypto from 'node:crypto';
import { addDays } from 'date-fns';
import forge from 'node-forge';
export const generateKeyPairAndCertificate = async (lifeSpanInDays = 365) => {
const keypair = forge.pki.rsa.generateKeyPair({ bits: 4096 });
return createCertificate(keypair, lifeSpanInDays);
};
const createCertificate = (keypair: forge.pki.KeyPair, lifeSpanInDays: number) => {
const cert = forge.pki.createCertificate();
const notBefore = new Date();
const notAfter = addDays(notBefore, lifeSpanInDays);
// Can not initialize the certificate with the keypair directly, so we need to set the public key manually.
/* eslint-disable @silverhand/fp/no-mutation */
cert.publicKey = keypair.publicKey;
// Use cryptographically secure pseudorandom number generator (CSPRNG) to generate a random serial number (usually more than 8 bytes).
// `serialNumber` should be IDENTICAL across different certificates, better not to be incremental.
cert.serialNumber = crypto.randomBytes(16).toString('hex');
cert.validity.notBefore = notBefore;
cert.validity.notAfter = notAfter;
/* eslint-enable @silverhand/fp/no-mutation */
// TODO: read from tenant config or let user customize before downloading
const subjectAttributes: forge.pki.CertificateField[] = [
{
name: 'commonName',
value: 'example.com',
},
];
const issuerAttributes: forge.pki.CertificateField[] = [
{
name: 'commonName',
value: 'logto.io',
},
{
name: 'organizationName',
value: 'Logto',
},
{
name: 'countryName',
value: 'US',
},
];
cert.setSubject(subjectAttributes);
cert.setIssuer(issuerAttributes);
cert.sign(keypair.privateKey);
return {
privateKey: forge.pki.privateKeyToPem(keypair.privateKey),
certificate: forge.pki.certificateToPem(cert),
notAfter,
};
};

View file

@ -0,0 +1,30 @@
import { type SamlApplicationConfig, SamlApplicationConfigs } from '@logto/schemas';
import type { CommonQueryMethods } from '@silverhand/slonik';
import { sql } from '@silverhand/slonik';
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import { convertToIdentifiers } from '#src/utils/sql.js';
const { table, fields } = convertToIdentifiers(SamlApplicationConfigs);
export const createSamlApplicationConfigQueries = (pool: CommonQueryMethods) => {
const insertSamlApplicationConfig = buildInsertIntoWithPool(pool)(SamlApplicationConfigs, {
returning: true,
});
const updateSamlApplicationConfig = buildUpdateWhereWithPool(pool)(SamlApplicationConfigs, true);
const findSamlApplicationConfigByApplicationId = async (applicationId: string) =>
pool.maybeOne<SamlApplicationConfig>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.applicationId}=${applicationId}
`);
return {
insertSamlApplicationConfig,
updateSamlApplicationConfig,
findSamlApplicationConfigByApplicationId,
};
};

View file

@ -0,0 +1,39 @@
import { SamlApplicationSecrets, type SamlApplicationSecret } from '@logto/schemas';
import type { CommonQueryMethods } from '@silverhand/slonik';
import { sql } from '@silverhand/slonik';
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
import { convertToIdentifiers } from '#src/utils/sql.js';
const { table, fields } = convertToIdentifiers(SamlApplicationSecrets);
export const createSamlApplicationSecretsQueries = (pool: CommonQueryMethods) => {
const insertSamlApplicationSecret = buildInsertIntoWithPool(pool)(SamlApplicationSecrets, {
returning: true,
});
const findSamlApplicationSecretsByApplicationId = async (applicationId: string) =>
pool.any<SamlApplicationSecret>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.applicationId}=${applicationId}
`);
const deleteSamlApplicationSecretById = async (id: string) => {
const { rowCount } = await pool.query(sql`
delete from ${table}
where ${fields.id} = ${id}
`);
if (rowCount < 1) {
throw new DeletionError(SamlApplicationSecrets.table);
}
};
return {
insertSamlApplicationSecret,
findSamlApplicationSecretsByApplicationId,
deleteSamlApplicationSecretById,
};
};

View file

@ -0,0 +1,95 @@
{
"tags": [
{
"name": "SAML applications",
"description": "SAML applications enable Single Sign-On (SSO) integration between Logto (acting as Identity Provider/IdP) and third-party Service Providers (SP) using the SAML 2.0 protocol. These endpoints allow you to manage SAML application configurations."
},
{
"name": "Dev feature"
}
],
"paths": {
"/api/saml-applications": {
"post": {
"summary": "Create SAML application",
"description": "Create a new SAML application with the given configuration. This will create both the application entity and its SAML-specific configurations.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"properties": {
"name": {
"type": "string",
"description": "The name of the SAML application."
},
"description": {
"type": "string",
"description": "The description of the SAML application."
},
"customData": {
"type": "object",
"description": "Custom data for the application."
},
"config": {
"type": "object",
"properties": {
"attributeMapping": {
"type": "object",
"description": "Mapping of SAML attributes to Logto user properties."
},
"entityId": {
"type": "string",
"description": "Service provider's entityId."
},
"acsUrl": {
"type": "object",
"description": "Service provider assertion consumer service URL configuration."
}
}
}
}
}
}
}
},
"responses": {
"201": {
"description": "The SAML application was created successfully."
},
"400": {
"description": "Invalid request body or SAML configuration."
}
}
}
},
"/api/saml-applications/{id}": {
"delete": {
"summary": "Delete SAML application",
"description": "Delete a SAML application by ID. This will remove both the application entity and its SAML-specific configurations.",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "The ID of the SAML application to delete."
}
],
"responses": {
"204": {
"description": "The SAML application was deleted successfully."
},
"400": {
"description": "Invalid application ID, the application is not a SAML application."
},
"404": {
"description": "The SAML application was not found."
}
}
}
}
}
}

View file

@ -0,0 +1,98 @@
import {
ApplicationType,
BindingType,
samlApplicationCreateGuard,
samlApplicationResponseGuard,
} from '@logto/schemas';
import { generateStandardId } from '@logto/shared';
import { removeUndefinedKeys } from '@silverhand/essentials';
import { z } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import { buildOidcClientMetadata } from '#src/oidc/utils.js';
import { generateInternalSecret } from '#src/routes/applications/application-secret.js';
import type { ManagementApiRouter, RouterInitArgs } from '#src/routes/types.js';
import { ensembleSamlApplication } from '#src/saml-applications/routes/utils.js';
import assertThat from '#src/utils/assert-that.js';
export default function samlApplicationRoutes<T extends ManagementApiRouter>(
...[router, { queries, libraries }]: RouterInitArgs<T>
) {
const {
applications: { insertApplication, findApplicationById, deleteApplicationById },
samlApplicationConfigs: { insertSamlApplicationConfig },
} = queries;
const {
samlApplicationSecrets: { createNewSamlApplicationSecretForApplication },
} = libraries;
router.post(
'/saml-applications',
koaGuard({
body: samlApplicationCreateGuard,
response: samlApplicationResponseGuard,
status: [201, 400],
}),
async (ctx, next) => {
const { name, description, customData, config } = ctx.guard.body;
// Only HTTP-POST binding is supported for receiving SAML assertions at the moment.
if (config?.acsUrl?.binding && config.acsUrl.binding !== BindingType.POST) {
throw new RequestError({
code: 'application.saml.acs_url_binding_not_supported',
status: 422,
});
}
const application = await insertApplication(
removeUndefinedKeys({
id: generateStandardId(),
secret: generateInternalSecret(),
name,
description,
customData,
oidcClientMetadata: buildOidcClientMetadata(),
isThirdParty: true,
type: ApplicationType.SAML,
})
);
const [samlConfig, samlSecret] = await Promise.all([
insertSamlApplicationConfig({
applicationId: application.id,
...config,
}),
createNewSamlApplicationSecretForApplication(application.id),
]);
ctx.status = 201;
ctx.body = ensembleSamlApplication({ application, samlConfig, samlSecret });
return next();
}
);
router.delete(
'/saml-applications/:id',
koaGuard({
params: z.object({ id: z.string() }),
status: [204, 400, 404],
}),
async (ctx, next) => {
const { id } = ctx.guard.params;
const { type, isThirdParty } = await findApplicationById(id);
assertThat(
type === ApplicationType.SAML && isThirdParty,
'application.saml.saml_application_only'
);
await deleteApplicationById(id);
ctx.status = 204;
return next();
}
);
}

View file

@ -0,0 +1,22 @@
import {
type SamlApplicationResponse,
type SamlApplicationSecret,
type Application,
type SamlApplicationConfig,
} from '@logto/schemas';
export const ensembleSamlApplication = ({
application,
samlConfig,
samlSecret,
}: {
application: Application;
samlConfig: Pick<SamlApplicationConfig, 'attributeMapping' | 'entityId' | 'acsUrl'>;
samlSecret?: SamlApplicationSecret | SamlApplicationSecret[];
}): SamlApplicationResponse => {
return {
...application,
...samlConfig,
secrets: samlSecret ? (Array.isArray(samlSecret) ? samlSecret : [samlSecret]) : [],
};
};

View file

@ -17,6 +17,7 @@ import { createSocialLibrary } from '#src/libraries/social.js';
import { createSsoConnectorLibrary } from '#src/libraries/sso-connector.js'; import { createSsoConnectorLibrary } from '#src/libraries/sso-connector.js';
import { createUserLibrary } from '#src/libraries/user.js'; import { createUserLibrary } from '#src/libraries/user.js';
import { createVerificationStatusLibrary } from '#src/libraries/verification-status.js'; import { createVerificationStatusLibrary } from '#src/libraries/verification-status.js';
import { createSamlApplicationSecretsLibrary } from '#src/saml-applications/libraries/secrets.js';
import type Queries from './Queries.js'; import type Queries from './Queries.js';
@ -37,6 +38,7 @@ export default class Libraries {
passcodes = createPasscodeLibrary(this.queries, this.connectors); passcodes = createPasscodeLibrary(this.queries, this.connectors);
applications = createApplicationLibrary(this.queries); applications = createApplicationLibrary(this.queries);
verificationStatuses = createVerificationStatusLibrary(this.queries); verificationStatuses = createVerificationStatusLibrary(this.queries);
samlApplicationSecrets = createSamlApplicationSecretsLibrary(this.queries);
roleScopes = createRoleScopeLibrary(this.queries); roleScopes = createRoleScopeLibrary(this.queries);
domains = createDomainLibrary(this.queries); domains = createDomainLibrary(this.queries);
protectedApps = createProtectedAppLibrary(this.queries); protectedApps = createProtectedAppLibrary(this.queries);

View file

@ -28,6 +28,8 @@ import UserSsoIdentityQueries from '#src/queries/user-sso-identities.js';
import { createUserQueries } from '#src/queries/user.js'; import { createUserQueries } from '#src/queries/user.js';
import { createUsersRolesQueries } from '#src/queries/users-roles.js'; import { createUsersRolesQueries } from '#src/queries/users-roles.js';
import { createVerificationStatusQueries } from '#src/queries/verification-status.js'; import { createVerificationStatusQueries } from '#src/queries/verification-status.js';
import { createSamlApplicationConfigQueries } from '#src/saml-applications/queries/configs.js';
import { createSamlApplicationSecretsQueries } from '#src/saml-applications/queries/secrets.js';
import { AccountCenterQueries } from '../queries/account-center.js'; import { AccountCenterQueries } from '../queries/account-center.js';
import { PersonalAccessTokensQueries } from '../queries/personal-access-tokens.js'; import { PersonalAccessTokensQueries } from '../queries/personal-access-tokens.js';
@ -60,6 +62,8 @@ export default class Queries {
ssoConnectors = new SsoConnectorQueries(this.pool); ssoConnectors = new SsoConnectorQueries(this.pool);
userSsoIdentities = new UserSsoIdentityQueries(this.pool); userSsoIdentities = new UserSsoIdentityQueries(this.pool);
subjectTokens = createSubjectTokenQueries(this.pool); subjectTokens = createSubjectTokenQueries(this.pool);
samlApplicationSecrets = createSamlApplicationSecretsQueries(this.pool);
samlApplicationConfigs = createSamlApplicationConfigQueries(this.pool);
personalAccessTokens = new PersonalAccessTokensQueries(this.pool); personalAccessTokens = new PersonalAccessTokensQueries(this.pool);
verificationRecords = new VerificationRecordQueries(this.pool); verificationRecords = new VerificationRecordQueries(this.pool);
accountCenters = new AccountCenterQueries(this.pool); accountCenters = new AccountCenterQueries(this.pool);

View file

@ -0,0 +1,13 @@
import { type SamlApplicationResponse, type CreateSamlApplication } from '@logto/schemas';
import { authedAdminApi } from './api.js';
export const createSamlApplication = async (createSamlApplication: CreateSamlApplication) =>
authedAdminApi
.post('saml-applications', {
json: createSamlApplication,
})
.json<SamlApplicationResponse>();
export const deleteSamlApplication = async (id: string) =>
authedAdminApi.delete(`saml-applications/${id}`);

View file

@ -37,7 +37,7 @@ describe('application APIs', () => {
it('should throw error when creating a SAML application', async () => { it('should throw error when creating a SAML application', async () => {
await expectRejects(createApplication('test-create-saml-app', ApplicationType.SAML), { await expectRejects(createApplication('test-create-saml-app', ApplicationType.SAML), {
code: 'application.use_saml_app_api', code: 'application.saml.use_saml_app_api',
status: 400, status: 400,
}); });
}); });
@ -54,6 +54,7 @@ describe('application APIs', () => {
expect(application.name).toBe(applicationName); expect(application.name).toBe(applicationName);
expect(application.type).toBe(ApplicationType.Traditional); expect(application.type).toBe(ApplicationType.Traditional);
expect(application.isThirdParty).toBe(true); expect(application.isThirdParty).toBe(true);
await deleteApplication(application.id); await deleteApplication(application.id);
}); });

View file

@ -0,0 +1,80 @@
import { ApplicationType, BindingType } from '@logto/schemas';
import { createApplication, deleteApplication } from '#src/api/application.js';
import { createSamlApplication, deleteSamlApplication } from '#src/api/saml-application.js';
import { expectRejects } from '#src/helpers/index.js';
import { devFeatureTest } from '#src/utils.js';
const { it, describe } = devFeatureTest;
describe('SAML application', () => {
it('should create and delete a SAML application successfully', async () => {
const createdSamlApplication = await createSamlApplication({
name: 'test',
description: 'test',
});
// Check secrets array exists and not empty
expect(Array.isArray(createdSamlApplication.secrets)).toBe(true);
expect(createdSamlApplication.secrets.length).toBeGreaterThan(0);
// Check first secret has non-empty privateKey and certificate
// Since we checked the array is not empty in previous check, we can safely access the first element.
const firstSecret = createdSamlApplication.secrets[0]!;
expect(typeof firstSecret.privateKey).toBe('string');
expect(firstSecret.privateKey).not.toBe('');
expect(typeof firstSecret.certificate).toBe('string');
expect(firstSecret.certificate).not.toBe('');
await deleteSamlApplication(createdSamlApplication.id);
});
it('should not support HTTP-Redirect binding', async () => {
await expectRejects(
createSamlApplication({
name: 'test',
description: 'test',
config: {
acsUrl: {
binding: BindingType.REDIRECT,
url: 'https://example.com',
},
},
}),
{
code: 'application.saml.acs_url_binding_not_supported',
status: 422,
}
);
});
it('should be able to create SAML application with `config` field', async () => {
const config = {
entityId: 'https://example.logto.io',
acsUrl: {
binding: BindingType.POST,
url: 'https://example.logto.io/sso/saml',
},
};
const createdSamlApplication = await createSamlApplication({
name: 'test',
description: 'test',
config,
});
expect(createdSamlApplication.entityId).toEqual(config.entityId);
expect(createdSamlApplication.acsUrl).toEqual(config.acsUrl);
await deleteSamlApplication(createdSamlApplication.id);
});
it('can not delete non-SAML applications with `DEL /saml-applications/:id` API', async () => {
const application = await createApplication('test-non-saml-app', ApplicationType.Traditional, {
isThirdParty: true,
});
await expectRejects(deleteSamlApplication(application.id), {
code: 'application.saml.saml_application_only',
status: 400,
});
await deleteApplication(application.id);
});
});

View file

@ -10,7 +10,6 @@ const application = {
protected_app_metadata_is_required: 'Protected app metadata is required.', protected_app_metadata_is_required: 'Protected app metadata is required.',
protected_app_not_configured: protected_app_not_configured:
'Protected app provider is not configured. This feature is not available for open source version.', 'Protected app provider is not configured. This feature is not available for open source version.',
use_saml_app_api: 'Use `[METHOD] /saml-applications(/.*)?` API to operate SAML app.',
cloudflare_unknown_error: 'Got unknown error when requesting Cloudflare API', cloudflare_unknown_error: 'Got unknown error when requesting Cloudflare API',
protected_application_only: 'The feature is only available for protected applications.', protected_application_only: 'The feature is only available for protected applications.',
protected_application_misconfigured: 'Protected application is misconfigured.', protected_application_misconfigured: 'Protected application is misconfigured.',
@ -21,6 +20,12 @@ const application = {
should_delete_custom_domains_first: 'Should delete custom domains first.', should_delete_custom_domains_first: 'Should delete custom domains first.',
no_legacy_secret_found: 'The application does not have a legacy secret.', no_legacy_secret_found: 'The application does not have a legacy secret.',
secret_name_exists: 'Secret name already exists.', secret_name_exists: 'Secret name already exists.',
saml: {
use_saml_app_api: 'Use `[METHOD] /saml-applications(/.*)?` API to operate SAML app.',
saml_application_only: 'The API is only available for SAML applications.',
acs_url_binding_not_supported:
'Only HTTP-POST binding is supported for receiving SAML assertions.',
},
}; };
export default Object.freeze(application); export default Object.freeze(application);

View file

@ -30,3 +30,4 @@ export * from './onboarding.js';
export * from './sign-in-experience.js'; export * from './sign-in-experience.js';
export * from './subject-token.js'; export * from './subject-token.js';
export * from './ssr.js'; export * from './ssr.js';
export * from './saml-application.js';

View file

@ -0,0 +1,43 @@
import { type z } from 'zod';
import { Applications } from '../db-entries/application.js';
import { SamlApplicationConfigs } from '../db-entries/saml-application-config.js';
import { SamlApplicationSecrets } from '../db-entries/saml-application-secret.js';
import { applicationCreateGuard } from './application.js';
const samlAppConfigGuard = SamlApplicationConfigs.guard.pick({
attributeMapping: true,
entityId: true,
acsUrl: true,
});
export const samlApplicationCreateGuard = applicationCreateGuard
.pick({
name: true,
description: true,
customData: true,
})
.extend({
// The reason for encapsulating attributeMapping and spMetadata into an object within the config field is that you cannot provide only one of `attributeMapping` or `spMetadata`. Due to the structure of the `saml_application_configs` table, both must be not null.
config: samlAppConfigGuard.partial().optional(),
});
export type CreateSamlApplication = z.infer<typeof samlApplicationCreateGuard>;
export const samlApplicationResponseGuard = Applications.guard
.merge(
// Partial to allow the optional fields to be omitted in the response.
// When starting to create a SAML application, SAML configuration is optional, which can lead to the absence of SAML configuration.
samlAppConfigGuard
)
.extend({
secrets: SamlApplicationSecrets.guard
.omit({
tenantId: true,
applicationId: true,
})
.array(),
});
export type SamlApplicationResponse = z.infer<typeof samlApplicationResponseGuard>;

View file

@ -1,10 +1,6 @@
/* init_order = 2 */ /* init_order = 2 */
/** /** The SAML application config and SAML-type application have a one-to-one correspondence: 1. a SAML-type application can only have one SAML application config. (CANNOT use "semicolon" in comments, since it indicates the end of query.) 2. a SAML application config can only configure one SAML-type application. */
* The SAML application config and SAML-type application have a one-to-one correspondence:
* - a SAML-type application can only have one SAML application config
* - a SAML application config can only configure one SAML-type application
*/
create table saml_application_configs ( create table saml_application_configs (
application_id varchar(21) not null application_id varchar(21) not null
references applications (id) on update cascade on delete cascade, references applications (id) on update cascade on delete cascade,

View file

@ -44,7 +44,7 @@ importers:
version: 8.8.0 version: 8.8.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.0.2)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.0.2)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.0.2 version: 5.0.2
@ -266,7 +266,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -333,7 +333,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -391,7 +391,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -449,7 +449,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -513,7 +513,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -577,7 +577,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -638,7 +638,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -705,7 +705,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -763,7 +763,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -821,7 +821,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -879,7 +879,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -937,7 +937,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -998,7 +998,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1068,7 +1068,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1129,7 +1129,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1184,7 +1184,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1242,7 +1242,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1300,7 +1300,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1358,7 +1358,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1419,7 +1419,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1477,7 +1477,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1535,7 +1535,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1593,7 +1593,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1651,7 +1651,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1709,7 +1709,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1767,7 +1767,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1825,7 +1825,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1883,7 +1883,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -1950,7 +1950,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2020,7 +2020,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2078,7 +2078,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2133,7 +2133,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2197,7 +2197,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2255,7 +2255,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2313,7 +2313,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2377,7 +2377,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2435,7 +2435,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2493,7 +2493,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2548,7 +2548,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2606,7 +2606,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2664,7 +2664,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -2722,7 +2722,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -3182,6 +3182,9 @@ importers:
nanoid: nanoid:
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.0.1 version: 5.0.1
node-forge:
specifier: ^1.3.1
version: 1.3.1
oidc-provider: oidc-provider:
specifier: ^8.4.6 specifier: ^8.4.6
version: 8.4.6 version: 8.4.6
@ -3279,6 +3282,9 @@ importers:
'@types/node': '@types/node':
specifier: ^20.9.5 specifier: ^20.9.5
version: 20.10.4 version: 20.10.4
'@types/node-forge':
specifier: ^1.3.1
version: 1.3.11
'@types/oidc-provider': '@types/oidc-provider':
specifier: ^8.4.4 specifier: ^8.4.4
version: 8.4.4 version: 8.4.4
@ -3305,7 +3311,7 @@ importers:
version: 8.57.0 version: 8.57.0
jest: jest:
specifier: ^29.7.0 specifier: ^29.7.0
version: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) version: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
jest-matcher-specific-error: jest-matcher-specific-error:
specifier: ^1.0.0 specifier: ^1.0.0
version: 1.0.0 version: 1.0.0
@ -3335,7 +3341,7 @@ importers:
version: 7.0.0 version: 7.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -3504,7 +3510,7 @@ importers:
version: 3.0.0 version: 3.0.0
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
packages/experience: packages/experience:
devDependencies: devDependencies:
@ -3979,7 +3985,7 @@ importers:
version: 10.0.0 version: 10.0.0
jest: jest:
specifier: ^29.7.0 specifier: ^29.7.0
version: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) version: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
jest-matcher-specific-error: jest-matcher-specific-error:
specifier: ^1.0.0 specifier: ^1.0.0
version: 1.0.0 version: 1.0.0
@ -4006,7 +4012,7 @@ importers:
version: 22.6.5(typescript@5.5.3) version: 22.6.5(typescript@5.5.3)
tsup: tsup:
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5) version: 8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5)
typescript: typescript:
specifier: ^5.5.3 specifier: ^5.5.3
version: 5.5.3 version: 5.5.3
@ -7088,6 +7094,9 @@ packages:
'@types/node-fetch@2.6.2': '@types/node-fetch@2.6.2':
resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
'@types/node-forge@1.3.11':
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
'@types/node@12.20.55': '@types/node@12.20.55':
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
@ -15868,7 +15877,7 @@ snapshots:
jest-util: 29.7.0 jest-util: 29.7.0
slash: 3.0.0 slash: 3.0.0
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3))': '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.12.7)(typescript@5.5.3))':
dependencies: dependencies:
'@jest/console': 29.7.0 '@jest/console': 29.7.0
'@jest/reporters': 29.7.0 '@jest/reporters': 29.7.0
@ -15882,7 +15891,7 @@ snapshots:
exit: 0.1.2 exit: 0.1.2
graceful-fs: 4.2.11 graceful-fs: 4.2.11
jest-changed-files: 29.7.0 jest-changed-files: 29.7.0
jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.12.7)(typescript@5.5.3))
jest-haste-map: 29.7.0 jest-haste-map: 29.7.0
jest-message-util: 29.7.0 jest-message-util: 29.7.0
jest-regex-util: 29.6.3 jest-regex-util: 29.6.3
@ -15903,7 +15912,7 @@ snapshots:
- supports-color - supports-color
- ts-node - ts-node
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.12.7)(typescript@5.5.3))': '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))':
dependencies: dependencies:
'@jest/console': 29.7.0 '@jest/console': 29.7.0
'@jest/reporters': 29.7.0 '@jest/reporters': 29.7.0
@ -15917,7 +15926,7 @@ snapshots:
exit: 0.1.2 exit: 0.1.2
graceful-fs: 4.2.11 graceful-fs: 4.2.11
jest-changed-files: 29.7.0 jest-changed-files: 29.7.0
jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.12.7)(typescript@5.5.3)) jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
jest-haste-map: 29.7.0 jest-haste-map: 29.7.0
jest-message-util: 29.7.0 jest-message-util: 29.7.0
jest-regex-util: 29.6.3 jest-regex-util: 29.6.3
@ -17641,6 +17650,10 @@ snapshots:
'@types/node': 20.12.7 '@types/node': 20.12.7
form-data: 3.0.2 form-data: 3.0.2
'@types/node-forge@1.3.11':
dependencies:
'@types/node': 20.12.7
'@types/node@12.20.55': {} '@types/node@12.20.55': {}
'@types/node@20.10.4': '@types/node@20.10.4':
@ -19305,13 +19318,13 @@ snapshots:
dependencies: dependencies:
lodash.get: 4.4.2 lodash.get: 4.4.2
create-jest@29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)): create-jest@29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3)):
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
chalk: 4.1.2 chalk: 4.1.2
exit: 0.1.2 exit: 0.1.2
graceful-fs: 4.2.11 graceful-fs: 4.2.11
jest-config: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) jest-config: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
jest-util: 29.7.0 jest-util: 29.7.0
prompts: 2.4.2 prompts: 2.4.2
transitivePeerDependencies: transitivePeerDependencies:
@ -21725,16 +21738,16 @@ snapshots:
- babel-plugin-macros - babel-plugin-macros
- supports-color - supports-color
jest-cli@29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)): jest-cli@29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3)):
dependencies: dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
chalk: 4.1.2 chalk: 4.1.2
create-jest: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) create-jest: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
exit: 0.1.2 exit: 0.1.2
import-local: 3.1.0 import-local: 3.1.0
jest-config: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) jest-config: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
jest-util: 29.7.0 jest-util: 29.7.0
jest-validate: 29.7.0 jest-validate: 29.7.0
yargs: 17.7.2 yargs: 17.7.2
@ -21763,7 +21776,7 @@ snapshots:
- supports-color - supports-color
- ts-node - ts-node
jest-config@29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)): jest-config@29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3)):
dependencies: dependencies:
'@babel/core': 7.24.4 '@babel/core': 7.24.4
'@jest/test-sequencer': 29.7.0 '@jest/test-sequencer': 29.7.0
@ -21789,38 +21802,7 @@ snapshots:
strip-json-comments: 3.1.1 strip-json-comments: 3.1.1
optionalDependencies: optionalDependencies:
'@types/node': 20.10.4 '@types/node': 20.10.4
ts-node: 10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3) ts-node: 10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
jest-config@29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)):
dependencies:
'@babel/core': 7.24.4
'@jest/test-sequencer': 29.7.0
'@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.24.4)
chalk: 4.1.2
ci-info: 3.8.0
deepmerge: 4.3.1
glob: 7.2.3
graceful-fs: 4.2.11
jest-circus: 29.7.0
jest-environment-node: 29.7.0
jest-get-type: 29.6.3
jest-regex-util: 29.6.3
jest-resolve: 29.7.0
jest-runner: 29.7.0
jest-util: 29.7.0
jest-validate: 29.7.0
micromatch: 4.0.5
parse-json: 5.2.0
pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 20.12.7
ts-node: 10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)
transitivePeerDependencies: transitivePeerDependencies:
- babel-plugin-macros - babel-plugin-macros
- supports-color - supports-color
@ -21856,6 +21838,37 @@ snapshots:
- babel-plugin-macros - babel-plugin-macros
- supports-color - supports-color
jest-config@29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3)):
dependencies:
'@babel/core': 7.24.4
'@jest/test-sequencer': 29.7.0
'@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.24.4)
chalk: 4.1.2
ci-info: 3.8.0
deepmerge: 4.3.1
glob: 7.2.3
graceful-fs: 4.2.11
jest-circus: 29.7.0
jest-environment-node: 29.7.0
jest-get-type: 29.6.3
jest-regex-util: 29.6.3
jest-resolve: 29.7.0
jest-runner: 29.7.0
jest-util: 29.7.0
jest-validate: 29.7.0
micromatch: 4.0.5
parse-json: 5.2.0
pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 20.12.7
ts-node: 10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
jest-dev-server@10.1.1: jest-dev-server@10.1.1:
dependencies: dependencies:
chalk: 4.1.2 chalk: 4.1.2
@ -22164,12 +22177,12 @@ snapshots:
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 8.1.1 supports-color: 8.1.1
jest@29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)): jest@29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3)):
dependencies: dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
'@jest/types': 29.6.3 '@jest/types': 29.6.3
import-local: 3.1.0 import-local: 3.1.0
jest-cli: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3)) jest-cli: 29.7.0(@types/node@20.10.4)(ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3))
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- babel-plugin-macros - babel-plugin-macros
@ -25819,14 +25832,14 @@ snapshots:
ts-interface-checker@0.1.13: {} ts-interface-checker@0.1.13: {}
ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.10.4)(typescript@5.5.3): ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.12.7)(typescript@5.5.3):
dependencies: dependencies:
'@cspotcode/source-map-support': 0.8.1 '@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11 '@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11 '@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3 '@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4 '@tsconfig/node16': 1.0.4
'@types/node': 20.10.4 '@types/node': 20.12.7
acorn: 8.13.0 acorn: 8.13.0
acorn-walk: 8.3.4 acorn-walk: 8.3.4
arg: 4.1.3 arg: 4.1.3
@ -25840,14 +25853,14 @@ snapshots:
'@swc/core': 1.3.52(@swc/helpers@0.5.1) '@swc/core': 1.3.52(@swc/helpers@0.5.1)
optional: true optional: true
ts-node@10.9.2(@swc/core@1.3.52(@swc/helpers@0.5.1))(@types/node@20.12.7)(typescript@5.5.3): ts-node@10.9.2(@swc/core@1.3.52)(@types/node@20.10.4)(typescript@5.5.3):
dependencies: dependencies:
'@cspotcode/source-map-support': 0.8.1 '@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11 '@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11 '@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3 '@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4 '@tsconfig/node16': 1.0.4
'@types/node': 20.12.7 '@types/node': 20.10.4
acorn: 8.13.0 acorn: 8.13.0
acorn-walk: 8.3.4 acorn-walk: 8.3.4
arg: 4.1.3 arg: 4.1.3
@ -25884,7 +25897,7 @@ snapshots:
tsscmp@1.0.6: {} tsscmp@1.0.6: {}
tsup@8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.0.2)(yaml@2.4.5): tsup@8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.0.2)(yaml@2.4.5):
dependencies: dependencies:
bundle-require: 5.0.0(esbuild@0.23.1) bundle-require: 5.0.0(esbuild@0.23.1)
cac: 6.7.14 cac: 6.7.14
@ -25912,7 +25925,7 @@ snapshots:
- tsx - tsx
- yaml - yaml
tsup@8.3.0(@swc/core@1.3.52(@swc/helpers@0.5.1))(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5): tsup@8.3.0(@swc/core@1.3.52)(jiti@1.21.0)(postcss@8.4.39)(typescript@5.5.3)(yaml@2.4.5):
dependencies: dependencies:
bundle-require: 5.0.0(esbuild@0.23.1) bundle-require: 5.0.0(esbuild@0.23.1)
cac: 6.7.14 cac: 6.7.14