feat: link github action repository to schema check (#2701)

This commit is contained in:
Laurin Quast 2023-10-06 16:39:50 +02:00 committed by GitHub
parent 689fed8985
commit fdf71a1c8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 1054 additions and 615 deletions

View file

@ -0,0 +1,7 @@
---
'@graphql-hive/cli': minor
---
Support forwarding GitHub repository information for schema checks and schema publishes when using the `--github` flag.
Please upgrade if you want to correctly forward the information for (federated) subgraphs to the Hive registry.

View file

@ -73,7 +73,7 @@ We recommend the following flow if you are having issues with running Hive local
3. Create a token from that target
4. Go to `packages/libraries/cli` and run `pnpm build`
5. Inside `packages/libraries/cli`, run:
`pnpm start schema:publish --token "YOUR_TOKEN_HERE" --registry "http://localhost:4000/graphql" examples/single.graphql`
`pnpm start schema:publish --registry.accessToken "YOUR_TOKEN_HERE" --registry.endpoint "http://localhost:4000/graphql" examples/single.graphql`
### Setting up Slack App for developing

View file

@ -10,7 +10,6 @@ import {
renderErrors,
renderWarnings,
} from '../../helpers/schema';
import { invariant } from '../../helpers/validation';
const schemaCheckMutation = graphql(/* GraphQL */ `
mutation schemaCheck($input: SchemaCheckInput!, $usesGitHubApp: Boolean!) {
@ -166,24 +165,36 @@ export default class SchemaCheck extends Command {
const commit = flags.commit || git?.commit;
const author = flags.author || git?.author;
invariant(typeof sdl === 'string' && sdl.length > 0, 'Schema seems empty');
if (typeof sdl !== 'string' || sdl.length === 0) {
throw new Errors.CLIError('Schema seems empty');
}
let github: null | {
commit: string;
repository: string | null;
} = null;
if (usesGitHubApp) {
invariant(
typeof commit === 'string',
`Couldn't resolve commit sha required for GitHub Application`,
);
if (!commit) {
throw new Errors.CLIError(`Couldn't resolve commit sha required for GitHub Application`);
}
// eslint-disable-next-line no-process-env
const repository = process.env['GITHUB_REPOSITORY'] ?? null;
if (!repository) {
throw new Errors.CLIError(`Missing "GITHUB_REPOSITORY" environment variable.`);
}
github = {
commit: commit,
repository,
};
}
const result = await this.registryApi(endpoint, accessToken).request(schemaCheckMutation, {
input: {
service,
sdl: minifySchema(sdl),
github: usesGitHubApp
? {
commit: commit!,
}
: null,
github,
meta:
!!commit && !!author
? {

View file

@ -206,6 +206,11 @@ export default class SchemaPublish extends Command {
env: 'HIVE_AUTHOR',
});
let gitHub: null | {
repository: string;
commit: string;
} = null;
if (!commit || !author) {
const git = await gitInfo(() => {
this.warn(`No git information found. Couldn't resolve author and commit.`);
@ -228,6 +233,18 @@ export default class SchemaPublish extends Command {
throw new Errors.CLIError(`Missing "commit"`);
}
if (usesGitHubApp) {
// eslint-disable-next-line no-process-env
const repository = process.env['GITHUB_REPOSITORY'] ?? null;
if (!repository) {
throw new Errors.CLIError(`Missing "GITHUB_REPOSITORY" environment variable.`);
}
gitHub = {
repository,
commit,
};
}
let sdl: string;
try {
const rawSdl = await loadSchema(file);
@ -255,9 +272,9 @@ export default class SchemaPublish extends Command {
force,
experimental_acceptBreakingChanges: experimental_acceptBreakingChanges === true,
metadata,
github: usesGitHubApp,
gitHub,
},
usesGitHubApp,
usesGitHubApp: !!gitHub,
});
if (result.schemaPublish.__typename === 'SchemaPublishSuccess') {

View file

@ -0,0 +1,16 @@
import { type MigrationExecutor } from '../pg-migrator';
export default {
name: '2023.08.03T11.44.36.schema-checks-github-repository.ts',
run: ({ sql }) => sql`
ALTER TABLE "public"."schema_checks"
ADD COLUMN "github_repository" text
, ADD COLUMN "github_sha" text
;
ALTER TABLE "public"."schema_versions"
ADD COLUMN "github_repository" text
, ADD COLUMN "github_sha" text
;
`,
} satisfies MigrationExecutor;

View file

@ -53,6 +53,7 @@ import migration_2023_08_01T11_44_36_schema_checks_expires_at from './actions/20
import migration_2023_09_01T09_54_00_zendesk_support from './actions/2023.09.01T09.54.00.zendesk-support';
import migration_2023_09_25T15_23_00_github_check_with_project_name from './actions/2023.09.25T15.23.00.github-check-with-project-name';
import migration_2023_09_28T14_14_14_native_fed_v2 from './actions/2023.09.28T14.14.14.native-fed-v2';
import migration_2023_10_05T11_44_36_schema_checks_github_repository from './actions/2023.10.05T11.44.36.schema-checks-github-repository';
import { runMigrations } from './pg-migrator';
export const runPGMigrations = (args: { slonik: DatabasePool; runTo?: string }) =>
@ -114,5 +115,6 @@ export const runPGMigrations = (args: { slonik: DatabasePool; runTo?: string })
migration_2023_09_01T09_54_00_zendesk_support,
migration_2023_09_25T15_23_00_github_check_with_project_name,
migration_2023_09_28T14_14_14_native_fed_v2,
migration_2023_10_05T11_44_36_schema_checks_github_repository,
],
});

View file

@ -1,20 +1,17 @@
import { Change, CriticalityLevel } from '@graphql-inspector/core';
import type * as Types from '../../../../__generated__/types';
import {
Alert,
AlertChannel,
Organization,
Project,
SchemaVersion,
Target,
} from '../../../../shared/entities';
import { Alert, AlertChannel, Organization, Project, Target } from '../../../../shared/entities';
export interface SchemaChangeNotificationInput {
event: {
organization: Pick<Organization, 'id' | 'cleanId' | 'name'>;
project: Pick<Project, 'id' | 'cleanId' | 'name'>;
target: Pick<Target, 'id' | 'cleanId' | 'name'>;
schema: Pick<SchemaVersion, 'id' | 'commit' | 'valid'>;
schema: {
id: string;
commit: string;
valid: boolean;
};
changes: Array<Change>;
messages: string[];
errors: Types.SchemaError[];

View file

@ -47,7 +47,6 @@ export default gql`
}
extend type Project {
gitRepository: String
isProjectNameInGitHubCheckEnabled: Boolean!
}
`;

View file

@ -136,6 +136,22 @@ export class GitHubIntegrationManager {
});
}
/**
* Check whether the given organization has access to a given GitHub repository.
*/
async hasAccessToGitHubRepository(props: {
selector: OrganizationSelector;
repositoryName: `${string}/${string}`;
}): Promise<boolean> {
const repositories = await this.getRepositories(props.selector);
if (!repositories) {
return false;
}
return repositories.some(repo => repo.nameWithOwner === props.repositoryName);
}
async getOrganization(selector: { installation: string }) {
const organization = await this.storage.getOrganizationByGitHubInstallationId({
installationId: selector.installation,

View file

@ -9,9 +9,6 @@ export default gql`
extend type Mutation {
createProject(input: CreateProjectInput!): CreateProjectResult!
updateProjectName(input: UpdateProjectNameInput!): UpdateProjectNameResult!
updateProjectGitRepository(
input: UpdateProjectGitRepositoryInput!
): UpdateProjectGitRepositoryResult!
deleteProject(selector: ProjectSelectorInput!): DeleteProjectPayload!
}

View file

@ -171,23 +171,4 @@ export class ProjectManager {
return result;
}
async updateGitRepository(
input: {
gitRepository?: string | null;
} & ProjectSelector,
): Promise<Project> {
const { gitRepository, organization, project } = input;
this.logger.info('Updating a project git repository (input=%o)', input);
await this.authManager.ensureProjectAccess({
...input,
scope: ProjectAccessScope.SETTINGS,
});
return this.storage.updateProjectGitRepository({
gitRepository: gitRepository?.trim() === '' ? null : gitRepository,
organization,
project,
});
}
}

View file

@ -9,10 +9,6 @@ import { ProjectManager } from './providers/project-manager';
const ProjectNameModel = z.string().min(2).max(40);
const URLModel = z.string().url().max(500);
const RepoOwnerWithNameModel = z
.string()
.regex(/^[^/]+\/[^/]+$/, 'Expected owner/name format')
.max(200);
const MaybeModel = <T extends z.ZodType>(value: T) => z.union([z.null(), z.undefined(), value]);
export const resolvers: ProjectModule.Resolvers & { ProjectType: any } = {
@ -155,41 +151,6 @@ export const resolvers: ProjectModule.Resolvers & { ProjectType: any } = {
},
};
},
async updateProjectGitRepository(_, { input }, { injector }) {
const UpdateProjectGitRepositoryModel = z.object({
gitRepository: MaybeModel(RepoOwnerWithNameModel),
});
const result = UpdateProjectGitRepositoryModel.safeParse(input);
if (!result.success) {
return {
error: {
message:
result.error.formErrors.fieldErrors.gitRepository?.[0] ?? 'Please check your input.',
},
};
}
const [organization, project] = await Promise.all([
injector.get(IdTranslator).translateOrganizationId(input),
injector.get(IdTranslator).translateProjectId(input),
]);
return {
ok: {
selector: {
organization: input.organization,
project: input.project,
},
updatedProject: await injector.get(ProjectManager).updateGitRepository({
project,
organization,
gitRepository: input.gitRepository,
}),
},
};
},
},
ProjectType: {
FEDERATION: ProjectType.FEDERATION,

View file

@ -228,6 +228,17 @@ export default gql`
| GitHubSchemaPublishSuccess
| GitHubSchemaPublishError
input SchemaPublishGitHubInput {
"""
The repository name.
"""
repository: String!
"""
The commit sha.
"""
commit: String!
}
input SchemaPublishInput {
service: ID
url: String
@ -244,7 +255,11 @@ export default gql`
"""
Talk to GitHub Application and create a check-run
"""
github: Boolean
github: Boolean @deprecated(reason: "Use SchemaPublishInput.gitHub instead.")
"""
Link GitHub version to a GitHub commit on a repository.
"""
gitHub: SchemaPublishGitHubInput
}
union SchemaCheckPayload =
@ -380,6 +395,10 @@ export default gql`
input GitHubSchemaCheckInput {
commit: String!
"""
The repository name of the schema check.
"""
repository: String
}
input SchemaCompareInput {
@ -475,6 +494,15 @@ export default gql`
"""
explorer(usage: SchemaExplorerUsageInput): SchemaExplorer!
errors: SchemaErrorConnection!
"""
GitHub metadata associated with the schema version.
"""
githubMetadata: SchemaVersionGithubMetadata
}
type SchemaVersionGithubMetadata {
repository: String!
commit: String!
}
type SchemaVersionConnection {

View file

@ -330,6 +330,10 @@ export class SchemaManager {
actionFn(): Promise<void>;
changes: Array<Change>;
previousSchemaVersion: string | null;
github: null | {
repository: string;
sha: string;
};
} & TargetSelector) &
(
| {
@ -740,13 +744,14 @@ export class SchemaManager {
organization: args.organizationId,
project: args.projectId,
});
if (!project.gitRepository) {
const gitRepository = schemaCheck.githubRepository ?? project.gitRepository;
if (!gitRepository) {
this.logger.debug(
'Skip updating GitHub schema check. Project has no git repository connected. (args=%o).',
'Skip updating GitHub schema check. Schema check has no git repository or project has no git repository connected. (args=%o).',
args,
);
} else {
const [owner, repository] = project.gitRepository.split('/');
const [owner, repository] = gitRepository.split('/');
const result = await this.githubIntegrationManager.updateCheckRunToSuccess({
organizationId: args.organizationId,
checkRun: {
@ -867,6 +872,41 @@ export class SchemaManager {
);
return true;
}
async getGitHubMetadata(schemaVersion: SchemaVersion): Promise<null | {
repository: `${string}/${string}`;
commit: string;
}> {
if (schemaVersion.github) {
return {
repository: schemaVersion.github.repository as `${string}/${string}`,
commit: schemaVersion.github.sha,
};
}
const log = await this.getSchemaLog({
commit: schemaVersion.actionId,
organization: schemaVersion.organization,
project: schemaVersion.project,
target: schemaVersion.target,
});
if ('commit' in log && log.commit) {
const project = await this.storage.getProject({
organization: schemaVersion.organization,
project: schemaVersion.project,
});
if (project.gitRepository) {
return {
repository: project.gitRepository,
commit: log.commit,
};
}
}
return null;
}
}
/**

View file

@ -9,6 +9,7 @@ import type { Span } from '@sentry/types';
import * as Types from '../../../__generated__/types';
import { Organization, Project, ProjectType, Schema, Target } from '../../../shared/entities';
import { HiveError } from '../../../shared/errors';
import { isGitHubRepositoryString } from '../../../shared/is-github-repository-string';
import { bolderize } from '../../../shared/markdown';
import { sentry } from '../../../shared/sentry';
import { AlertsManager } from '../../alerts/providers/alerts-manager';
@ -208,6 +209,56 @@ export class SchemaPublisher {
projectType: project.type,
});
let github: null | {
repository: `${string}/${string}`;
sha: string;
} = null;
if (input.github) {
if (input.github.repository) {
if (!isGitHubRepositoryString(input.github.repository)) {
return {
__typename: 'GitHubSchemaCheckError' as const,
message: 'Invalid github repository name provided.',
};
}
github = {
repository: input.github.repository,
sha: input.github.commit,
};
} else if (project.gitRepository == null) {
return {
__typename: 'GitHubSchemaCheckError' as const,
message: 'Git repository is not configured for this project',
};
} else {
github = {
repository: project.gitRepository,
sha: input.github.commit,
};
}
}
if (github != null) {
// Verify that the GitHub repository can be accessed by the user
const hasAccessToGitHubRepository =
await this.gitHubIntegrationManager.hasAccessToGitHubRepository({
selector: {
organization: organization.id,
},
repositoryName: github.repository,
});
if (!hasAccessToGitHubRepository) {
return {
__typename: 'GitHubSchemaCheckError' as const,
message:
`Missing permissions for updating check-runs on GitHub repository '${github.repository}'. ` +
'Please make sure that the GitHub App has access on the repository.',
};
}
}
await this.schemaManager.completeGetStartedCheck({
organization: project.orgId,
step: 'checkingSchema',
@ -331,6 +382,8 @@ export class SchemaPublisher {
isManuallyApproved: false,
manualApprovalUserId: null,
githubCheckRunId: null,
githubRepository: github?.repository ?? null,
githubSha: github?.sha ?? null,
expiresAt,
});
}
@ -395,20 +448,21 @@ export class SchemaPublisher {
isManuallyApproved: false,
manualApprovalUserId: null,
githubCheckRunId: null,
githubRepository: github?.repository ?? null,
githubSha: github?.sha ?? null,
expiresAt,
});
}
if (input.github) {
let result: Awaited<ReturnType<SchemaPublisher['githubCheck']>>;
if (github) {
let result: Awaited<ReturnType<SchemaPublisher['githubSchemaCheck']>>;
if (checkResult.conclusion === SchemaCheckConclusion.Success) {
result = await this.githubCheck({
result = await this.githubSchemaCheck({
project,
target,
organization,
serviceName: input.service ?? null,
sha: input.github.commit,
conclusion: checkResult.conclusion,
changes: checkResult.state?.schemaChanges ?? null,
warnings: checkResult.state?.schemaPolicyWarnings ?? null,
@ -416,14 +470,14 @@ export class SchemaPublisher {
compositionErrors: null,
errors: null,
schemaCheckId: schemaCheck?.id ?? null,
github,
});
} else {
result = await this.githubCheck({
result = await this.githubSchemaCheck({
project,
target,
organization,
serviceName: input.service ?? null,
sha: input.github.commit,
conclusion: checkResult.conclusion,
changes: [
...(checkResult.state.schemaChanges?.breaking ?? []),
@ -434,6 +488,7 @@ export class SchemaPublisher {
warnings: checkResult.state.schemaPolicy?.warnings ?? [],
errors: checkResult.state.schemaPolicy?.errors?.map(formatPolicyError) ?? [],
schemaCheckId: schemaCheck?.id ?? null,
github,
});
}
@ -517,7 +572,7 @@ export class SchemaPublisher {
public async updateVersionStatus(input: TargetSelector & { version: string; valid: boolean }) {
const updateResult = await this.schemaManager.updateSchemaVersionStatus(input);
if (updateResult.valid === true) {
if (updateResult.isComposable === true) {
// Now, when fetching the latest valid version, we should be able to detect
// if it's the version we just updated or not.
// Why?
@ -810,6 +865,52 @@ export class SchemaPublisher {
projectType: project.type,
});
let github: null | {
repository: `${string}/${string}`;
sha: string;
} = null;
if (input.gitHub != null) {
if (!isGitHubRepositoryString(input.gitHub.repository)) {
return {
__typename: 'GitHubSchemaPublishError' as const,
message: 'Invalid github repository name provided.',
} as const;
}
const hasAccessToGitHubRepository =
await this.gitHubIntegrationManager.hasAccessToGitHubRepository({
selector: {
organization: organization.id,
},
repositoryName: input.gitHub.repository,
});
if (!hasAccessToGitHubRepository) {
return {
__typename: 'GitHubSchemaPublishError',
message:
`Missing permissions for updating check-runs on GitHub repository '${input.gitHub.repository}'. ` +
'Please make sure that the GitHub App has access on the repository.',
} as const;
}
github = {
repository: input.gitHub.repository,
sha: input.gitHub.commit,
};
} else if (input.github != null) {
if (!project.gitRepository) {
return {
__typename: 'GitHubSchemaPublishError',
message: 'Git repository is not configured for this project.',
} as const;
}
github = {
repository: project.gitRepository,
sha: input.commit,
};
}
await this.schemaManager.completeGetStartedCheck({
organization: project.orgId,
step: 'publishingSchema',
@ -889,18 +990,6 @@ export class SchemaPublisher {
conclusion: 'ignored',
});
if (input.github) {
return this.createPublishCheckRun({
force: false,
initial: false,
input,
project,
valid: true,
changes: [],
errors: [],
});
}
const linkToWebsite =
typeof this.schemaModuleConfig.schemaPublishLink === 'function'
? this.schemaModuleConfig.schemaPublishLink({
@ -917,8 +1006,22 @@ export class SchemaPublisher {
})
: null;
if (github) {
return this.createPublishCheckRun({
force: false,
initial: false,
valid: true,
changes: [],
errors: [],
organizationId: organization.id,
github,
detailsUrl: linkToWebsite,
});
}
return {
__typename: 'SchemaPublishSuccess' as const,
__typename: 'SchemaPublishSuccess',
initial: false,
valid: true,
changes: [],
@ -1028,6 +1131,7 @@ export class SchemaPublisher {
base_schema: baseSchema,
metadata: input.metadata ?? null,
projectType: project.type,
github,
actionFn: async () => {
if (composable && fullSchemaSdl) {
await this.publishToCDN({
@ -1063,7 +1167,11 @@ export class SchemaPublisher {
organization,
project,
target,
schema: schemaVersion,
schema: {
id: schemaVersion.id,
commit: schemaVersion.actionId,
valid: schemaVersion.isComposable,
},
changes,
messages,
errors,
@ -1094,16 +1202,17 @@ export class SchemaPublisher {
})
: null;
if (input.github) {
if (github) {
return this.createPublishCheckRun({
force: false,
initial: publishResult.state.initial,
input,
project,
valid: publishResult.state.composable,
changes: publishResult.state.changes ?? [],
errors,
messages: publishResult.state.messages ?? [],
organizationId: organization.id,
github,
detailsUrl: linkToWebsite,
});
}
@ -1117,12 +1226,9 @@ export class SchemaPublisher {
};
}
private async githubCheck({
project,
private async githubSchemaCheck({
target,
organization,
serviceName,
sha,
conclusion,
changes,
breakingChanges,
@ -1130,12 +1236,17 @@ export class SchemaPublisher {
errors,
warnings,
schemaCheckId,
...args
}: {
project: Project;
project: {
orgId: string;
cleanId: string;
name: string;
useProjectNameInGithubCheck: boolean;
};
target: Target;
organization: Organization;
serviceName: string | null;
sha: string;
conclusion: SchemaCheckConclusion;
warnings: SchemaCheckWarning[] | null;
changes: Array<Change> | null;
@ -1147,14 +1258,12 @@ export class SchemaPublisher {
message: string;
}> | null;
schemaCheckId: string | null;
github: {
repository: `${string}/${string}`;
sha: string;
};
}) {
if (!project.gitRepository) {
return {
__typename: 'GitHubSchemaCheckError' as const,
message: 'Git repository is not configured for this project',
};
}
const [repositoryOwner, repositoryName] = project.gitRepository.split('/');
const [repositoryOwner, repositoryName] = args.github.repository.split('/');
try {
let title: string;
@ -1186,14 +1295,14 @@ export class SchemaPublisher {
const checkRun = await this.gitHubIntegrationManager.createCheckRun({
name: buildGitHubActionCheckName({
projectName: project.name,
projectName: args.project.name,
targetName: target.name,
serviceName,
includeProjectName: project.useProjectNameInGithubCheck,
includeProjectName: args.project.useProjectNameInGithubCheck,
}),
conclusion: conclusion === SchemaCheckConclusion.Success ? 'success' : 'failure',
sha,
organization: project.orgId,
sha: args.github.sha,
organization: args.project.orgId,
repositoryOwner,
repositoryName,
output: {
@ -1203,9 +1312,9 @@ export class SchemaPublisher {
detailsUrl:
(schemaCheckId &&
this.schemaModuleConfig.schemaCheckLink?.({
project,
project: args.project,
target,
organization,
organization: args.organization,
schemaCheckId,
})) ||
null,
@ -1387,29 +1496,29 @@ export class SchemaPublisher {
private async createPublishCheckRun({
initial,
force,
input,
project,
valid,
changes,
errors,
messages,
organizationId,
github,
detailsUrl,
}: {
initial: boolean;
force?: boolean | null;
input: PublishInput;
project: Project;
valid: boolean;
changes: Array<Change>;
errors: readonly Types.SchemaError[];
messages?: string[];
organizationId: string;
github: {
repository: string;
sha: string;
};
detailsUrl: string | null;
}) {
if (!project.gitRepository) {
return {
__typename: 'GitHubSchemaPublishError' as const,
message: 'Git repository is not configured for this project',
};
}
const [repositoryOwner, repositoryName] = project.gitRepository.split('/');
const [repositoryOwner, repositoryName] = github.repository.split('/');
try {
let title: string;
@ -1447,26 +1556,26 @@ export class SchemaPublisher {
await this.gitHubIntegrationManager.createCheckRun({
name: 'GraphQL Hive - schema:publish',
conclusion: valid ? 'success' : force ? 'neutral' : 'failure',
sha: input.commit,
organization: input.organization,
sha: github.sha,
organization: organizationId,
repositoryOwner,
repositoryName,
output: {
title,
summary,
},
detailsUrl: null,
detailsUrl,
});
return {
__typename: 'GitHubSchemaPublishSuccess' as const,
__typename: 'GitHubSchemaPublishSuccess',
message: title,
};
} catch (error: any) {
} as const;
} catch (error: unknown) {
Sentry.captureException(error);
return {
__typename: 'GitHubSchemaPublishError' as const,
__typename: 'GitHubSchemaPublishError',
message: `Failed to create the check-run`,
};
} as const;
}
}

View file

@ -116,7 +116,7 @@ export const resolvers: SchemaModule.Resolvers = {
target,
});
if ('changes' in result) {
if ('changes' in result && result.changes) {
return {
...result,
changes: result.changes.map(toGraphQLSchemaChange),
@ -753,7 +753,7 @@ export const resolvers: SchemaModule.Resolvers = {
SchemaVersion: {
async log(version, _, { injector }) {
const log = await injector.get(SchemaManager).getSchemaLog({
commit: version.commit,
commit: version.actionId,
organization: version.organization,
project: version.project,
target: version.target,
@ -1022,6 +1022,10 @@ export const resolvers: SchemaModule.Resolvers = {
};
},
date: version => version.createdAt,
githubMetadata(version, _, { injector }) {
return injector.get(SchemaManager).getGitHubMetadata(version);
},
valid: version => version.isComposable,
},
SchemaCompareError: {
__isTypeOf(source: unknown) {

View file

@ -226,10 +226,6 @@ export interface Storage {
_: ProjectSelector & Pick<Project, 'name' | 'cleanId'> & { user: string },
): Promise<Project | never>;
updateProjectGitRepository(
_: ProjectSelector & Pick<Project, 'gitRepository'>,
): Promise<Project | never>;
enableExternalSchemaComposition(
_: ProjectSelector & {
endpoint: string;
@ -407,6 +403,10 @@ export interface Storage {
actionFn(): Promise<void>;
changes: Array<Change>;
previousSchemaVersion: null | string;
github: null | {
repository: string;
sha: string;
};
} & TargetSelector) &
(
| {

View file

@ -78,15 +78,19 @@ export interface DateRange {
export interface SchemaVersion {
id: string;
valid: boolean;
createdAt: string;
commit: string;
isComposable: boolean;
actionId: string;
baseSchema: string | null;
hasPersistedSchemaChanges: boolean;
previousSchemaVersionId: null | string;
compositeSchemaSDL: null | string;
supergraphSDL: null | string;
schemaCompositionErrors: Array<SchemaCompositionError> | null;
github: null | {
repository: string;
sha: string;
};
}
export interface SchemaObject {
@ -261,7 +265,11 @@ export interface Project {
type: ProjectType;
buildUrl?: string | null;
validationUrl?: string | null;
gitRepository?: string | null;
/**
* @deprecated A project is no longer linked to a single git repository as a project can be composed of multiple git repositories.
* TODO: All code referencing this field should be removed at some point.
*/
gitRepository?: `${string}/${string}` | null;
legacyRegistryModel: boolean;
useProjectNameInGithubCheck: boolean;
externalComposition: {

View file

@ -0,0 +1,13 @@
/**
* Verify whether a string is a legit GitHub repository string.
* Example: `foo/bar`
*/
export function isGitHubRepositoryString(repository: string): repository is `${string}/${string}` {
const [owner, name] = repository.split('/');
return !!owner && isLegitGitHubName(owner) && !!name && isLegitGitHubName(name);
}
/** @source https://stackoverflow.com/a/59082561 */
function isLegitGitHubName(str: string) {
return /^[\w-.]+$/i.test(str);
}

View file

@ -174,6 +174,8 @@ export interface schema_checks {
created_at: Date;
expires_at: Date | null;
github_check_run_id: string | null;
github_repository: string | null;
github_sha: string | null;
id: string;
is_manually_approved: boolean | null;
is_success: boolean;
@ -233,6 +235,8 @@ export interface schema_versions {
base_schema: string | null;
composite_schema_sdl: string | null;
created_at: Date;
github_repository: string | null;
github_sha: string | null;
has_persisted_schema_changes: boolean | null;
id: string;
is_composable: boolean;

View file

@ -204,7 +204,7 @@ export async function createStorage(connection: string, maximumPoolSize: number)
type: project.type as ProjectType,
buildUrl: project.build_url,
validationUrl: project.validation_url,
gitRepository: project.git_repository,
gitRepository: project.git_repository as `${string}/${string}` | null,
legacyRegistryModel: project.legacy_registry_model,
useProjectNameInGithubCheck: project.github_check_with_project_name === true,
externalComposition: {
@ -1272,16 +1272,6 @@ export async function createStorage(connection: string, maximumPoolSize: number)
`),
);
},
async updateProjectGitRepository({ gitRepository, organization, project }) {
return transformProject(
await pool.one<Slonik<projects>>(sql`
UPDATE public.projects
SET git_repository = ${gitRepository ?? null}
WHERE id = ${project} AND org_id = ${organization}
RETURNING *
`),
);
},
async enableExternalSchemaComposition({ project, endpoint, encryptedSecret }) {
return transformProject(
await pool.one<Slonik<projects>>(sql`
@ -1710,16 +1700,7 @@ export async function createStorage(connection: string, maximumPoolSize: number)
const version = await pool.maybeOne<unknown>(
sql`
SELECT
sv.id,
sv.is_composable,
to_json(sv.created_at) as "created_at",
sv.action_id,
sv.base_schema,
sv.has_persisted_schema_changes,
sv.previous_schema_version_id,
sv.composite_schema_sdl,
sv.supergraph_sdl,
sv.schema_composition_errors
${schemaVersionSQLFields(sql`sv.`)}
FROM public.schema_versions as sv
WHERE sv.target_id = ${target} AND sv.is_composable IS TRUE
ORDER BY sv.created_at DESC
@ -1737,16 +1718,7 @@ export async function createStorage(connection: string, maximumPoolSize: number)
const version = await pool.maybeOne<unknown>(
sql`
SELECT
sv.id,
sv.is_composable,
to_json(sv.created_at) as "created_at",
sv.action_id,
sv.base_schema,
sv.has_persisted_schema_changes,
sv.previous_schema_version_id,
sv.composite_schema_sdl,
sv.supergraph_sdl,
sv.schema_composition_errors
${schemaVersionSQLFields(sql`sv.`)}
FROM public.schema_versions as sv
WHERE sv.target_id = ${target} AND sv.is_composable IS TRUE
ORDER BY sv.created_at DESC
@ -1760,16 +1732,7 @@ export async function createStorage(connection: string, maximumPoolSize: number)
const version = await pool.maybeOne<unknown>(
sql`
SELECT
sv.id,
sv.is_composable,
to_json(sv.created_at) as "created_at",
sv.action_id,
sv.base_schema,
sv.has_persisted_schema_changes,
sv.previous_schema_version_id,
sv.composite_schema_sdl,
sv.supergraph_sdl,
sv.schema_composition_errors
${schemaVersionSQLFields(sql`sv.`)}
FROM public.schema_versions as sv
LEFT JOIN public.targets as t ON (t.id = sv.target_id)
WHERE sv.target_id = ${target} AND t.project_id = ${project}
@ -1785,16 +1748,7 @@ export async function createStorage(connection: string, maximumPoolSize: number)
const version = await pool.maybeOne<unknown>(
sql`
SELECT
sv.id,
sv.is_composable,
to_json(sv.created_at) as "created_at",
sv.action_id,
sv.base_schema,
sv.has_persisted_schema_changes,
sv.previous_schema_version_id,
sv.composite_schema_sdl,
sv.supergraph_sdl,
sv.schema_composition_errors
${schemaVersionSQLFields(sql`sv.`)}
FROM public.schema_versions as sv
LEFT JOIN public.targets as t ON (t.id = sv.target_id)
WHERE sv.target_id = ${target} AND t.project_id = ${project}
@ -1950,16 +1904,7 @@ export async function createStorage(connection: string, maximumPoolSize: number)
async getVersion({ project, target, version }) {
const result = await pool.one(sql`
SELECT
sv.id,
sv.is_composable,
to_json(sv.created_at) as "created_at",
sv.base_schema,
sv.action_id,
sv.has_persisted_schema_changes,
sv.previous_schema_version_id,
sv.composite_schema_sdl,
sv.supergraph_sdl,
sv.schema_composition_errors
${schemaVersionSQLFields(sql`sv.`)}
FROM public.schema_versions as sv
LEFT JOIN public.schema_log as sl ON (sl.id = sv.action_id)
LEFT JOIN public.targets as t ON (t.id = sv.target_id)
@ -1976,18 +1921,9 @@ export async function createStorage(connection: string, maximumPoolSize: number)
async getVersions({ project, target, after, limit }) {
const query = sql`
SELECT
sv.id,
sv.is_composable,
to_json(sv.created_at) as "created_at",
sv.base_schema,
sv.action_id,
sv.has_persisted_schema_changes,
sv.previous_schema_version_id,
sv.composite_schema_sdl,
sv.supergraph_sdl,
sv.schema_composition_errors,
sl.author,
lower(sl.service_name) as "service_name"
${schemaVersionSQLFields(sql`sv.`)}
, sl.author as "author"
, lower(sl.service_name) as "service_name"
FROM public.schema_versions as sv
LEFT JOIN public.schema_log as sl ON (sl.id = sv.action_id)
LEFT JOIN public.targets as t ON (t.id = sv.target_id)
@ -2058,6 +1994,8 @@ export async function createStorage(connection: string, maximumPoolSize: number)
compositeSchemaSDL: args.compositeSchemaSDL,
supergraphSDL: args.supergraphSDL,
schemaCompositionErrors: args.schemaCompositionErrors,
// Deleting a schema is done via CLI and not associated to a commit or a pull request.
github: null,
});
// Move all the schema_version_to_log entries of the previous version to the new version
@ -2139,6 +2077,7 @@ export async function createStorage(connection: string, maximumPoolSize: number)
compositeSchemaSDL: input.compositeSchemaSDL,
supergraphSDL: input.supergraphSDL,
schemaCompositionErrors: input.schemaCompositionErrors,
github: input.github,
});
await Promise.all(
@ -2200,16 +2139,7 @@ export async function createStorage(connection: string, maximumPoolSize: number)
WHERE
id = ${version}
RETURNING
id,
is_composable,
to_json(created_at) as "created_at",
action_id,
base_schema,
has_persisted_schema_changes,
previous_schema_version_id,
composite_schema_sdl,
supergraph_sdl,
schema_composition_errors
${schemaVersionSQLFields()}
`),
);
},
@ -3524,6 +3454,8 @@ export async function createStorage(connection: string, maximumPoolSize: number)
, "is_manually_approved"
, "manual_approval_user_id"
, "github_check_run_id"
, "github_repository"
, "github_sha"
, "expires_at"
)
VALUES (
@ -3543,6 +3475,8 @@ export async function createStorage(connection: string, maximumPoolSize: number)
, ${args.isManuallyApproved}
, ${args.manualApprovalUserId}
, ${args.githubCheckRunId}
, ${args.githubRepository}
, ${args.githubSha}
, ${args.expiresAt?.toISOString() ?? null}
)
RETURNING
@ -3944,33 +3878,39 @@ function decodeFeatureFlags(column: unknown) {
return FeatureFlagsModel.parse(column);
}
const SchemaVersionModel = zod
.object({
const SchemaVersionModel = zod.intersection(
zod.object({
id: zod.string(),
is_composable: zod.boolean(),
created_at: zod.string(),
base_schema: zod.nullable(zod.string()),
action_id: zod.string(),
has_persisted_schema_changes: zod.nullable(zod.boolean()),
previous_schema_version_id: zod.nullable(zod.string()),
composite_schema_sdl: zod.nullable(zod.string()),
supergraph_sdl: zod.nullable(zod.string()),
schema_composition_errors: zod.nullable(zod.array(SchemaCompositionErrorModel)),
})
.transform(value => ({
id: value.id,
/** @deprecated Use isComposable instead. */
valid: value.is_composable,
isComposable: value.is_composable,
createdAt: value.created_at,
baseSchema: value.base_schema,
commit: value.action_id,
hasPersistedSchemaChanges: value.has_persisted_schema_changes ?? false,
previousSchemaVersionId: value.previous_schema_version_id,
compositeSchemaSDL: value.composite_schema_sdl,
supergraphSDL: value.supergraph_sdl,
schemaCompositionErrors: value.schema_composition_errors,
}));
isComposable: zod.boolean(),
createdAt: zod.string(),
baseSchema: zod.nullable(zod.string()),
actionId: zod.string(),
hasPersistedSchemaChanges: zod.nullable(zod.boolean()).transform(val => val ?? false),
previousSchemaVersionId: zod.nullable(zod.string()),
compositeSchemaSDL: zod.nullable(zod.string()),
supergraphSDL: zod.nullable(zod.string()),
schemaCompositionErrors: zod.nullable(zod.array(SchemaCompositionErrorModel)),
}),
zod
.union([
zod.object({
githubRepository: zod.string(),
githubSha: zod.string(),
}),
zod.object({
githubRepository: zod.null(),
githubSha: zod.null(),
}),
])
.transform(val => ({
github: val.githubRepository
? {
repository: val.githubRepository,
sha: val.githubSha,
}
: null,
})),
);
const DocumentCollectionModel = zod.object({
id: zod.string(),
@ -4046,6 +3986,10 @@ async function insertSchemaVersion(
compositeSchemaSDL: string | null;
supergraphSDL: string | null;
schemaCompositionErrors: Array<SchemaCompositionError> | null;
github: null | {
sha: string;
repository: string;
};
},
) {
const query = sql`
@ -4059,7 +4003,9 @@ async function insertSchemaVersion(
previous_schema_version_id,
composite_schema_sdl,
supergraph_sdl,
schema_composition_errors
schema_composition_errors,
github_repository,
github_sha
)
VALUES
(
@ -4075,19 +4021,12 @@ async function insertSchemaVersion(
args.schemaCompositionErrors
? sql`${JSON.stringify(args.schemaCompositionErrors)}::jsonb`
: sql`${null}`
}
},
${args.github?.repository ?? null},
${args.github?.sha ?? null}
)
RETURNING
id,
is_composable,
to_json(created_at) as "created_at",
action_id,
base_schema,
has_persisted_schema_changes,
previous_schema_version_id,
composite_schema_sdl,
supergraph_sdl,
schema_composition_errors
${schemaVersionSQLFields()}
`;
return await trx.one(query).then(SchemaVersionModel.parse);
@ -4140,10 +4079,27 @@ const schemaCheckSQLFields = sql`
, "composite_schema_sdl" as "compositeSchemaSDL"
, "supergraph_sdl" as "supergraphSDL"
, "github_check_run_id" as "githubCheckRunId"
, "github_repository" as "githubRepository"
, "github_sha" as "githubSha"
, coalesce("is_manually_approved", false) as "isManuallyApproved"
, "manual_approval_user_id" as "manualApprovalUserId"
`;
const schemaVersionSQLFields = (t = sql``) => sql`
${t}"id"
, ${t}"is_composable" as "isComposable"
, to_json(${t}"created_at") as "createdAt"
, ${t}"action_id" as "actionId"
, ${t}"base_schema" as "baseSchema"
, ${t}"has_persisted_schema_changes" as "hasPersistedSchemaChanges"
, ${t}"previous_schema_version_id" as "previousSchemaVersionId"
, ${t}"composite_schema_sdl" as "compositeSchemaSDL"
, ${t}"supergraph_sdl" as "supergraphSDL"
, ${t}"schema_composition_errors" as "schemaCompositionErrors"
, ${t}"github_repository" as "githubRepository"
, ${t}"github_sha" as "githubSha"
`;
const targetSQLFields = sql`
"id",
"clean_id" as "cleanId",

View file

@ -866,7 +866,12 @@ const SchemaCheckSharedFieldsModel = z.object({
commit: z.string(),
})
.nullable(),
// github specific data
githubCheckRunId: z.number().nullable(),
// TODO: these two always come together
// we need to improve the model code to reflect that
githubRepository: z.string().nullable(),
githubSha: z.string().nullable(),
});
const SchemaCheckInputModel = z.intersection(

View file

@ -43,6 +43,10 @@ const HistoryPage_VersionsPageQuery = graphql(`
}
}
baseSchema
githubMetadata {
repository
commit
}
}
pageInfo {
hasNextPage
@ -54,13 +58,11 @@ const HistoryPage_VersionsPageQuery = graphql(`
// URQL's Infinite scrolling pattern
// https://formidable.com/open-source/urql/docs/basics/ui-patterns/#infinite-scrolling
function ListPage({
gitRepository,
variables,
isLastPage,
onLoadMore,
versionId,
}: {
gitRepository?: string;
variables: { after: string; limit: number };
isLastPage: boolean;
onLoadMore: (after: string) => void;
@ -129,12 +131,12 @@ function ListPage({
) : null}
</div>
</NextLink>
{gitRepository && 'commit' in version.log && version.log.commit ? (
{version.githubMetadata ? (
<a
className="text-xs font-medium text-gray-500 hover:text-gray-400"
target="_blank"
rel="noreferrer"
href={`https://github.com/${gitRepository}/commit/${version.log.commit}`}
href={`https://github.com/${version.githubMetadata.repository}/commit/${version.githubMetadata.commit}`}
>
<ExternalLinkIcon className="inline" /> associated with Git commit
</a>
@ -350,7 +352,6 @@ const TargetHistoryPageQuery = graphql(`
}
project(selector: { organization: $organizationId, project: $projectId }) {
...TargetLayout_CurrentProjectFragment
gitRepository
}
target(selector: { organization: $organizationId, project: $projectId, target: $targetId }) {
id
@ -412,7 +413,6 @@ function HistoryPageContent() {
<div className="flex min-w-[420px] grow flex-col gap-2.5 overflow-y-auto rounded-md border border-gray-800/50 p-2.5">
{pageVariables.map((variables, i) => (
<ListPage
gitRepository={currentProject?.gitRepository ?? undefined}
key={variables.after || 'initial'}
variables={variables}
isLastPage={i === pageVariables.length - 1}

View file

@ -19,7 +19,7 @@ import {
} from '@/components/ui/card';
import { Subtitle, Title } from '@/components/ui/page';
import { QueryError } from '@/components/ui/query-error';
import { DocsLink, Input, MetaTitle, Select, Tag } from '@/components/v2';
import { DocsLink, Input, Link, MetaTitle, Tag } from '@/components/v2';
import { HiveLogo } from '@/components/v2/icon';
import { DeleteProjectModal } from '@/components/v2/modals';
import { graphql, useFragment } from '@/gql';
@ -29,27 +29,6 @@ import { getDocsUrl } from '@/lib/docs-url';
import { useNotifications, useRouteSelector, useToggle } from '@/lib/hooks';
import { withSessionProtection } from '@/lib/supertokens/guard';
const ProjectSettingsPage_UpdateProjectGitRepositoryMutation = graphql(`
mutation ProjectSettingsPage_UpdateProjectGitRepository(
$input: UpdateProjectGitRepositoryInput!
) {
updateProjectGitRepository(input: $input) {
ok {
selector {
organization
project
}
updatedProject {
...ProjectFields
}
}
error {
message
}
}
}
`);
const GithubIntegration_GithubIntegrationDetailsQuery = graphql(`
query getGitHubIntegrationDetails($selector: OrganizationSelectorInput!) {
organization(selector: $selector) {
@ -76,13 +55,13 @@ export const GithubIntegration_EnableProjectNameInGitHubCheckMutation = graphql(
`);
function GitHubIntegration(props: {
gitRepository: string | null;
isProjectNameInGitHubCheckEnabled: boolean;
organizationName: string;
projectName: string;
}): ReactElement | null {
const router = useRouteSelector();
const docksLink = getDocsUrl('integrations/ci-cd#github-workflow-for-ci');
const notify = useNotifications();
const [integrationQuery] = useQuery({
query: GithubIntegration_GithubIntegrationDetailsQuery,
variables: {
@ -91,38 +70,10 @@ function GitHubIntegration(props: {
},
},
});
const gitRepository = props.gitRepository ?? '';
const notify = useNotifications();
const [mutation, mutate] = useMutation(ProjectSettingsPage_UpdateProjectGitRepositoryMutation);
const [ghCheckMutation, ghCheckMutate] = useMutation(
GithubIntegration_EnableProjectNameInGitHubCheckMutation,
);
const { handleSubmit, values, handleChange, handleBlur, isSubmitting, errors, touched } =
useFormik({
enableReinitialize: true,
initialValues: {
gitRepository,
},
validationSchema: Yup.object().shape({
gitRepository: Yup.string(),
}),
onSubmit: values =>
mutate({
input: {
organization: router.organizationId,
project: router.projectId,
gitRepository: values.gitRepository === '' ? null : values.gitRepository,
},
}).then(result => {
if (result.data?.updateProjectGitRepository.ok) {
notify('Updated Git repository', 'success');
} else {
notify('Failed to update Git repository', 'error');
}
}),
});
if (integrationQuery.fetching) {
return null;
@ -131,165 +82,149 @@ function GitHubIntegration(props: {
const githubIntegration = integrationQuery.data?.organization?.organization.gitHubIntegration;
return (
<form onSubmit={handleSubmit}>
<Card>
<CardHeader>
<CardTitle>Git Repository</CardTitle>
<CardDescription>
Associate your project with a Git repository to enable commit linking and to allow CI
integration.
<br />
<DocsLink
className="text-muted-foreground text-sm"
href="/management/projects#github-repository"
>
Learn more about GitHub integration
</DocsLink>
</CardDescription>
</CardHeader>
<CardContent>
{!!githubIntegration && !props.isProjectNameInGitHubCheckEnabled ? (
<div className="flex flex-row justify-between items-center rounded-sm p-4 mb-4 gap-x-4 text-sm bg-gray-500/10 text-gray-500">
<div className="space-y-2">
<div>
<div className="text-gray-300 font-bold">Use project's name in GitHub Check</div>
<Card>
<CardHeader>
<CardTitle>Git Repository</CardTitle>
<CardDescription>
Associate your project with a Git repository to enable commit linking and to allow CI
integration.
<br />
<DocsLink
className="text-muted-foreground text-sm"
href="/management/projects#github-repository"
>
Learn more about GitHub integration
</DocsLink>
</CardDescription>
</CardHeader>
<CardContent>
{githubIntegration ? (
<>
{props.isProjectNameInGitHubCheckEnabled ? null : (
<div className="flex flex-row justify-between items-center rounded-sm p-4 mb-4 gap-x-4 text-sm bg-gray-500/10 text-gray-500">
<div className="space-y-2">
<div>
Prevents GitHub Check name collisions when running{' '}
{docksLink ? (
<NextLink href={docksLink}>
<span className="mx-1 text-orange-700 hover:underline hover:underline-offset-4">
$ hive schema:check --github
</span>
</NextLink>
) : (
<span className="mx-1 text-orange-700">$ hive schema:check --github</span>
)}
for more than one project.
</div>
</div>
<div>
<div className="mt-4 mb-2">Here's how it will look like in your CI pipeline:</div>
<div className="flex items-center gap-x-2 pl-1">
<CheckIcon className="w-4 h-4 text-emerald-500" />
<div className="bg-white w-6 h-6 flex items-center justify-center rounded-sm">
<HiveLogo className="w-[80%] h-[80%]" />
<div className="text-gray-300 font-bold">
Use project's name in GitHub Check
</div>
<div>
Prevents GitHub Check name collisions when running{' '}
{docksLink ? (
<NextLink href={docksLink}>
<span className="mx-1 text-orange-700 hover:underline hover:underline-offset-4">
$ hive schema:check --github
</span>
</NextLink>
) : (
<span className="mx-1 text-orange-700">$ hive schema:check --github</span>
)}
for more than one project.
</div>
</div>
<div>
<div className="mt-4 mb-2">
Here's how it will look like in your CI pipeline:
</div>
<div className="flex items-center gap-x-2 pl-1">
<CheckIcon className="w-4 h-4 text-emerald-500" />
<div className="bg-white w-6 h-6 flex items-center justify-center rounded-sm">
<HiveLogo className="w-[80%] h-[80%]" />
</div>
<div className="font-semibold text-[#adbac7]">
{props.organizationName} &gt; schema:check &gt; staging
<div className="font-semibold text-[#adbac7]">
{props.organizationName} &gt; schema:check &gt; staging
</div>
<div className="text-gray-500"> No changes</div>
</div>
<div className="text-gray-500"> No changes</div>
</div>
</div>
<div>
<ArrowBigDownDashIcon className="w-6 h-6" />
</div>
<div>
<div className="flex items-center gap-x-2 pl-1">
<CheckIcon className="w-4 h-4 text-emerald-500" />
<div className="bg-white w-6 h-6 flex items-center justify-center rounded-sm">
<HiveLogo className="w-[80%] h-[80%]" />
</div>
<div>
<ArrowBigDownDashIcon className="w-6 h-6" />
</div>
<div>
<div className="flex items-center gap-x-2 pl-1">
<CheckIcon className="w-4 h-4 text-emerald-500" />
<div className="bg-white w-6 h-6 flex items-center justify-center rounded-sm">
<HiveLogo className="w-[80%] h-[80%]" />
</div>
<div className="font-semibold text-[#adbac7]">
{props.organizationName} &gt; schema:check &gt; {props.projectName} &gt;
staging
<div className="font-semibold text-[#adbac7]">
{props.organizationName} &gt; schema:check &gt; {props.projectName} &gt;
staging
</div>
<div className="text-gray-500"> No changes</div>
</div>
<div className="text-gray-500"> No changes</div>
</div>
</div>
</div>
<div className="pr-6">
<Button
disabled={ghCheckMutation.fetching}
onClick={() => {
void ghCheckMutate({
input: {
organization: router.organizationId,
project: router.projectId,
},
}).then(
result => {
if (result.error) {
<div className="pr-6">
<Button
disabled={ghCheckMutation.fetching}
onClick={() => {
void ghCheckMutate({
input: {
organization: router.organizationId,
project: router.projectId,
},
}).then(
result => {
if (result.error) {
notify('Failed to enable', 'error');
} else {
notify('Migration completed', 'success');
}
},
_ => {
notify('Failed to enable', 'error');
} else {
notify('Migration completed', 'success');
}
},
_ => {
notify('Failed to enable', 'error');
},
);
}}
>
I want to migrate
</Button>
</div>
</div>
) : null}
{githubIntegration ? (
<>
<Select
name="gitRepository"
placeholder="None"
className="w-96"
options={githubIntegration.repositories.map(repo => ({
name: repo.nameWithOwner,
value: repo.nameWithOwner,
}))}
value={values.gitRepository ?? undefined}
onChange={handleChange}
onBlur={handleBlur}
isInvalid={!!(touched.gitRepository && errors.gitRepository)}
/>
{touched.gitRepository && (errors.gitRepository || mutation.error) && (
<div className="mt-2 text-red-500">
{errors.gitRepository ??
mutation.error?.graphQLErrors[0]?.message ??
mutation.error?.message}
</div>
)}
{mutation.data?.updateProjectGitRepository.error && (
<div className="mt-2 text-red-500">
{mutation.data.updateProjectGitRepository.error.message}
</div>
)}
</>
) : (
<Tag className="!p-4 block">
<p>The organization is not connected to our GitHub Application.</p>
<p>
<Button asChild className="p-0 mr-1" variant="link">
<NextLink
href={{
pathname: '/[organizationId]/view/settings',
query: {
organizationId: router.organizationId,
},
},
);
}}
>
Visit organization settings
</NextLink>
</Button>
to configure it.
</p>
I want to migrate
</Button>
</div>
</div>
)}
<p className="text-gray-300">
This project can access and update check-runs of the following GitHub repositories.
</p>
<Tag className="!p-4">
The list of repositories can be adjusted in the organization settings.
<Link
variant="primary"
href={{
pathname: '/[organizationId]/view/settings',
query: {
organizationId: router.organizationId,
},
}}
>
Visit settings
</Link>
</Tag>
)}
</CardContent>
{githubIntegration ? (
<CardFooter>
<Button
type="submit"
className="px-10"
disabled={isSubmitting || gitRepository === values.gitRepository}
<ul className="text-gray-300 mt-2">
{githubIntegration.repositories.map(repository => (
<li key={repository.nameWithOwner}>{repository.nameWithOwner}</li>
))}
</ul>
</>
) : (
<Tag className="!p-4 mt-2">
The organization is not connected to our GitHub Application.
<Link
variant="primary"
href={{
pathname: '/[organizationId]/view/settings',
query: {
organizationId: router.organizationId,
},
}}
>
Save
</Button>
</CardFooter>
) : null}
</Card>
</form>
Visit settings
</Link>
to configure it.
</Tag>
)}
</CardContent>
</Card>
);
}
@ -329,7 +264,6 @@ const ProjectSettingsPage_OrganizationFragment = graphql(`
const ProjectSettingsPage_ProjectFragment = graphql(`
fragment ProjectSettingsPage_ProjectFragment on Project {
name
gitRepository
type
isProjectNameInGitHubCheckEnabled
...ModelMigrationSettings_ProjectFragment
@ -483,7 +417,6 @@ function ProjectSettingsContent() {
{query.data?.isGitHubIntegrationFeatureEnabled ? (
<GitHubIntegration
gitRepository={project.gitRepository ?? null}
organizationName={organization.name}
projectName={project.name}
isProjectNameInGitHubCheckEnabled={project.isProjectNameInGitHubCheckEnabled}

View file

@ -25,7 +25,6 @@ fragment ProjectFields on Project {
cleanId
name
type
gitRepository
registryModel
}

View file

@ -2,3 +2,4 @@ out
build
temp
public/sitemap.xml
public/changelog.json

View file

@ -1,4 +1,7 @@
/* eslint-disable no-process-env */
import fs from 'fs';
import path from 'path';
import { withGuildDocs } from '@theguild/components/next.config';
export default withGuildDocs({
@ -74,4 +77,31 @@ export default withGuildDocs({
},
],
swcMinify: true,
transformPageOpts(pageOpts) {
const changelogItems = pageOpts.pageMap.find(item => item.name === 'changelog').children;
fs.writeFileSync(
path.join('.', 'public', 'changelog.json'),
JSON.stringify(
changelogItems
.filter(
item =>
item.kind === 'MdxPage' &&
item.frontMatter.title &&
item.frontMatter.description &&
item.frontMatter.date,
)
.map(item => ({
route: item.route,
title: item.frontMatter.title,
description: item.frontMatter.description,
date: item.frontMatter.date,
})),
null,
2,
),
);
return pageOpts;
},
});

View file

@ -12,9 +12,11 @@
"@radix-ui/react-tooltip": "1.0.6",
"@theguild/components": "5.2.4",
"clsx": "2.0.0",
"date-fns": "2.30.0",
"next": "13.5.4",
"next-themes": "*",
"react": "18.2.0",
"react-avatar": "5.0.3",
"react-countup": "6.4.2",
"react-dom": "18.2.0",
"react-icons": "4.11.0",

View file

@ -0,0 +1,19 @@
type Author = {
name: string;
link: `https://${string}`;
github?: string;
twitter?: string;
};
export const authors: Record<string, Author> = {
kamil: {
name: 'Kamil Kisiela',
link: 'https://twitter.com/kamilkisiela',
github: 'kamilkisiela',
},
laurin: {
name: 'Laurin Quast',
link: 'https://twitter.com/n1rual',
github: 'n1ru4l',
},
};

View file

@ -0,0 +1,73 @@
import type { ReactElement } from 'react';
import { format } from 'date-fns';
import { Anchor } from '@theguild/components';
import { authors } from '../authors';
import { SocialAvatar } from './social-avatar';
type Meta = {
authors: string[];
date: string;
title: string;
description: string;
};
const Authors = ({ meta }: { meta: Meta }): ReactElement => {
const date = meta.date ? new Date(meta.date) : new Date();
if (meta.authors.length === 1) {
const author = authors[meta.authors[0]];
return (
<div className="my-5 flex flex-row items-center justify-center">
<Anchor href={author.link} title={author.name}>
<SocialAvatar author={author} />
</Anchor>
<div className="ml-2.5 flex flex-col">
<Anchor href={author.link} title={author.name} className="text-[#1cc8ee]">
{author.name}
</Anchor>
<time
dateTime={date.toISOString()}
title={`Posted ${format(date, 'EEEE, LLL do y')}`}
className="text-xs text-[#777]"
>
{format(date, 'EEEE, LLL do y')}
</time>
</div>
</div>
);
}
return (
<>
<time
dateTime={date.toISOString()}
title={`Posted ${format(date, 'EEEE, LLL do y')}`}
className="mt-5 block text-center text-xs text-[#777]"
>
{format(date, 'EEEE, LLL do y')}
</time>
<div className="my-5 flex flex-wrap justify-center gap-5">
{meta.authors.map(authorId => {
const author = authors[authorId];
return (
<div key={authorId}>
<Anchor href={author.link} title={author.name} className="text-[#1cc8ee]">
<SocialAvatar author={author} />
<span className="ml-2.5 text-sm">{author.name}</span>
</Anchor>
</div>
);
})}
</div>
</>
);
};
export const ChangelogBlogPostHeader = ({ meta }: { meta: Meta }): ReactElement => {
return (
<>
<h1>{meta.title}</h1>
<Authors meta={meta} />
</>
);
};

View file

@ -0,0 +1,61 @@
import { ReactElement } from 'react';
import Link from 'next/link';
import { format } from 'date-fns';
function LearnMoreLink(props: { href: string }) {
return (
<Link
href={props.href}
className="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:outline-none focus:ring-gray-200 focus:text-blue-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-gray-700"
>
Learn more{' '}
<svg
className="w-3 h-3 ml-2"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 10"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 5h12m0 0L9 1m4 4L9 9"
/>
</svg>
</Link>
);
}
function ChangelogItem(props: { title: string; date: string; description: string; route: string }) {
return (
<li className="mb-10 ml-4">
<div className="absolute w-3 h-3 bg-gray-200 rounded-full mt-1.5 -left-1.5 border border-white dark:border-gray-900 dark:bg-gray-700" />
<time
className="mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500"
dateTime={props.date}
>
{format(new Date(props.date), 'do MMMM yyyy')}
</time>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">{props.title}</h3>
<p className="mb-4 text-base font-normal text-gray-500 dark:text-gray-400">
{props.description}
</p>
<LearnMoreLink href={props.route} />
</li>
);
}
export const Changelog = ({ changelogs }): ReactElement => {
return (
<>
<h1>Changelog</h1>
<ol className="relative border-l border-gray-200 dark:border-gray-700">
{changelogs.map(item => (
<ChangelogItem key={item.href} {...item} />
))}
</ol>
</>
);
};

View file

@ -0,0 +1,19 @@
import { ReactElement } from 'react';
import ReactAvatar from 'react-avatar';
export const SocialAvatar = ({
author,
}: {
author: { name: string; github?: string; twitter?: string };
}): ReactElement => {
return (
<ReactAvatar
round
githubHandle={author.github}
twitterHandle={author.twitter}
size="40"
title={author.name}
alt={author.name}
/>
);
};

View file

@ -25,5 +25,21 @@
"theme": {
"toc": true
}
},
"changelogLink": {
"title": "Changelog",
"type": "page",
"href": "/changelog"
},
"changelog": {
"type": "page",
"title": "Changelog",
"theme": {
"sidebar": false,
"toc": true,
"breadcrumb": false,
"typesetting": "article"
},
"display": "hidden"
}
}

View file

@ -0,0 +1,12 @@
{
"index": {
"theme": {
"sidebar": false,
"toc": false,
"breadcrumb": false,
"typesetting": "article"
},
"display": "hidden"
},
"link-github-action-repository-to-schema-check": "Link GitHub action repository to schema check"
}

View file

@ -0,0 +1,14 @@
---
title: Changelog
---
import changelogs from '../../../public/changelog.json'
import { Changelog } from '../../components/changelog'
export function getStaticProps() {
return { props: { changelogs } }
}
export default function Foo({ changelogs }) {
return <Changelog changelogs={changelogs} />
}

View file

@ -0,0 +1,23 @@
---
title: Link GitHub action repository to schema check
description: A schema check within a project can now be linked to any GitHub action repository.
date: 2023-10-05
authors: [laurin]
---
It is now possible to link more than one GitHub repository to individual schema checks and schema
versions. This allows you to connect multiple GitHub repositories to a single target, which is
mandatory for Federated GraphQL schemas, where subgraphs are distributed across multiple
repositories.
## How to link a GitHub action repository to a schema check
In order to leverage this new feature you need to upgrade the Hive CLI to the latest version, no
further changes are required.
## My project was already linked to a GitHub repository, what do I need to do?
Previously, we linked a single GitHub repository to a target within GraphQL Hive. This is no longer
the case, and you can now link multiple GitHub repositories to a single target. All you need to do
is grant access to the GitHub repositories within the organization settings. Then, within the target
settings, you will see a list of repositories that can be linked.

View file

@ -236,9 +236,11 @@ For more information please refer to the
### CLI and GitHub Integration
If [GitHub Integration](/docs/management/organizations#github) is enabled for your organization, and
a [GitHub repository is linked to the Hive project](/docs/management/projects#github-repository) is
active, you may specify an additional `--github` flag to report the results back to GitHub as Check
Suite (for `schema:check` and `schema:publish` commands):
the
[GitHub integration has access to the GitHub repository](/docs/management/projects#github-repository),
you may specify an additional `--github` flag to report the results back to GitHub as Check Suite
(for `schema:check` and `schema:publish` commands) when running the Hive CLI from within a GitHub
action:
```bash
hive schema:publish schema.graphql --github

View file

@ -175,7 +175,7 @@ Run the following command in your terminal, to publish your `subgraphs/products.
registry (replace `YOUR_TOKEN_HERE` with the token you have just created):
```bash
hive schema:publish --token YOUR_TOKEN_HERE \
hive schema:publish --registry.accessToken YOUR_TOKEN_HERE \
--service="products" --url="http://fake.com/products/graphql" --author "Me" --commit "First" subgraphs/products.graphql
```
@ -207,7 +207,7 @@ new schema you just published 🎉
Now, let's publish the **Reviews** subgraph schema to Hive:
```bash
hive schema:publish --token YOUR_TOKEN_HERE \
hive schema:publish --registry.accessToken YOUR_TOKEN_HERE \
--service="reviews" --url="http://fake.com/reviews/graphql" --author "Me" --commit "Second" subgraphs/reviews.graphql
```
@ -258,7 +258,7 @@ Now, run the Hive CLI with the `schema:check` command and your modified `subgrap
file:
```bash
hive schema:check --service="reviews" subgraphs/reviews.graphql --token YOUR_TOKEN_HERE
hive schema:check --service="reviews" subgraphs/reviews.graphql --registry.accessToken YOUR_TOKEN_HERE
```
You should see that Hive successfully detect the change you made, and exists with a `0` exit code,
@ -293,7 +293,7 @@ type ReviewSummary {
```
```bash
hive schema:check --service="reviews" subgraphs/reviews.graphql --token YOUR_TOKEN_HERE
hive schema:check --service="reviews" subgraphs/reviews.graphql --registry.accessToken YOUR_TOKEN_HERE
```
In that case, you'll notice that Hive CLI exists with a `1` exit code, meaning that the schema has
@ -341,7 +341,7 @@ Run the Hive CLI with the `schema:check` command again and the modified `subgrap
file:
```bash
hive schema:check --service="reviews" subgraphs/reviews.graphql --token YOUR_TOKEN_HERE
hive schema:check --service="reviews" subgraphs/reviews.graphql --registry.accessToken YOUR_TOKEN_HERE
```
And now you can see that the schema check process has failed, due to conflicts and inconsistencies
@ -388,7 +388,7 @@ type ReviewSummary {
And publish it to Hive:
```bash
hive schema:publish --token YOUR_TOKEN_HERE \
hive schema:publish --registry.accessToken YOUR_TOKEN_HERE \
--service="reviews" --url="http://fake.com/reviews/graphql" --author "Me" --commit "Third" subgraphs/reviews.graphql
```

View file

@ -148,7 +148,7 @@ Run the following command in your terminal, to publish your `subschemas/users.gr
registry (replace `YOUR_TOKEN_HERE` with the token you have just created):
```bash
hive schema:publish --token YOUR_TOKEN_HERE \
hive schema:publish --registry.accessToken YOUR_TOKEN_HERE \
--service="users" --url="http://fake.com/users/graphql" --author "Me" --commit "First" subschemas/users.graphql
```
@ -180,7 +180,7 @@ new schema you just published 🎉
Now, let's publish the **Posts** service schema to Hive:
```bash
hive schema:publish --token YOUR_TOKEN_HERE \
hive schema:publish --registry.accessToken YOUR_TOKEN_HERE \
--service="posts" --url="http://fake.com/posts/graphql" --author "Me" --commit "Second" subschemas/posts.graphql
```
@ -234,7 +234,7 @@ Now, run the Hive CLI with the `schema:check` command and your modified `subsche
file:
```bash
hive schema:check --service="users" subschemas/users.graphql --token YOUR_TOKEN_HERE
hive schema:check --service="users" subschemas/users.graphql --registry.accessToken YOUR_TOKEN_HERE
```
You should see that Hive successfully detect the change you made, and exists with a `0` exit code,
@ -275,7 +275,7 @@ Now, run the Hive CLI with the `schema:check` command again and the modified
`subschemas/users.graphql` file:
```bash
hive schema:check --service="users" subschemas/users.graphql --token YOUR_TOKEN_HERE
hive schema:check --service="users" subschemas/users.graphql --registry.accessToken YOUR_TOKEN_HERE
```
In that case, you'll notice that Hive CLI exists with a `1` exit code, meaning that the schema has
@ -325,7 +325,7 @@ Run the Hive CLI with the `schema:check` command again and the modified `subsche
file:
```bash
hive schema:check --service="users" subschemas/users.graphql --token YOUR_TOKEN_HERE
hive schema:check --service="users" subschemas/users.graphql --registry.accessToken YOUR_TOKEN_HERE
```
And now you can see that the schema check process has failed, due to conflicts and inconsistencies
@ -371,7 +371,7 @@ type Query {
And publish it to Hive:
```bash
hive schema:publish --token YOUR_TOKEN_HERE \
hive schema:publish --registry.accessToken YOUR_TOKEN_HERE \
--service="users" --url="http://fake.com/users/graphql" --author "Me" --commit "Third" subschemas/users.graphql
```

View file

@ -86,7 +86,7 @@ Run the following command in your terminal, to publish your `schema.graphql` to
(replace `YOUR_TOKEN_HERE` with the token you have just created):
```bash
hive schema:publish schema.graphql --token YOUR_TOKEN_HERE
hive schema:publish schema.graphql --registry.accessToken YOUR_TOKEN_HERE
```
<Callout type="info">
@ -146,7 +146,7 @@ enum UserRole {
Now, run the Hive CLI with the `schema:check` command and your modified `schema.graphql` file:
```bash
hive schema:check schema.graphql --token YOUR_TOKEN_HERE
hive schema:check schema.graphql --registry.accessToken YOUR_TOKEN_HERE
```
You should see that Hive successfully detect the change you made, and exists with a `0` exit code,
@ -190,7 +190,7 @@ enum UserRole {
Now, run the Hive CLI with the `schema:check` command and your modified `schema.graphql` file:
```bash
hive schema:check schema.graphql --token YOUR_TOKEN_HERE
hive schema:check schema.graphql --registry.accessToken YOUR_TOKEN_HERE
```
Now, you should see that Hive detected a breaking change, and exists with a `1` exit code, meaning
@ -229,7 +229,7 @@ To publish your latest schema with breaking changes, use the `schema:publish` co
Let's publish the schema we previously modified:
```bash
hive schema:publish schema.graphql --token YOUR_TOKEN_HERE
hive schema:publish schema.graphql --registry.accessToken YOUR_TOKEN_HERE
```
You should see now that Hive accepted your published schema and updated the registry:

View file

@ -20,9 +20,10 @@ If you are using a different runtime environment for your project, you should in
If you are using GitHub Actions, you can specify an additional flag to the Hive CLI: `--github`.
If [GitHub Integration](/docs/management/organizations#github) is enabled for your organization, and
a [GitHub repository is linked to the Hive project](/docs/management/projects#github-repository) is
active, you may specify an additional `--github` flag to report the results back to GitHub as Check
Suite (for `schema:check` and `schema:publish` commands):
the
[GitHub repository has access to the GitHub repository the action is running from](/docs/management/projects#github-repository)
is active, you may specify an additional `--github` flag to report the results back to GitHub as
Check Suite (for `schema:check` and `schema:publish` commands):
```bash
hive schema:publish schema.graphql --github

View file

@ -109,7 +109,9 @@ To activate this feature in your organization, go to the **Integrations** sectio
authorize and approve the GitHub App installation.
<Callout type="info">
Make sure to select the GitHub repositories you plan to run Hive CLI in.
Make sure to select the GitHub repositories you plan to run Hive CLI in. If you use the Hive CLI
from multiple GitHub repositories (e.g. for a federated Graph), you'll need to grant access to
each of those, otherwise the publishes or schema checks will fail.
</Callout>
Now follow the instructions on [Project Management](/docs/management/projects#github-repository)

View file

@ -45,23 +45,21 @@ Choose a project type based on your project's stack, and then enter the project
### GitHub Repository
Once you have your
[Hive organization integrated with GitHub](/docs/management/organizations#github), you can connect
between a project and a GitHub repository.
[Hive organization integrated with GitHub](/docs/management/organizations#github), no additional
steps are needed. Please double check that the GitHub integration has access to all the GitHub
repositories you plan to use with Hive.
From the project's **Settings** tab, you can use the **Git Repository** section to select a
repository to associate with your Hive projects.
<Callout>
Previously, you could only connect a single GitHub repository to a project. However, that does not
make sense when using federated/composite schema project the subgraphs can be in different
repositories.
</Callout>
Associating a repository with your project will allow you to:
- Publish **Check Suite** back to GitHub (when `--github` flag is enabled for your `schema:publish`
and `schema:check` commands - [read more here](/docs/integrations/ci-cd)).
<NextImage
alt="GitHub Check Suite"
src={githubIntegrationImage}
className="mt-6 max-w-lg drop-shadow-md"
/>
Having a Project connected with your GitHub repositories gives you the following benefits:
- Publish **Check Suite** back to GitHub (when `--github` flag is used for your `schema:publish` and
`schema:check` commands -
[you can learn move about this feature here](/docs/api-reference/cli#cli-and-github-integration)).
- Have direct connection between a schema version and a Git commit in your repository in Hive
dashboard.
@ -71,10 +69,6 @@ Associating a repository with your project will allow you to:
className="mt-6 max-w-lg drop-shadow-md"
/>
Once a project is associated with a repository, you can specify `--github` flag when running Hive
CLI commands, and get the results reported back to GitHub
([you can learn move about this feature here](/docs/api-reference/cli#cli-and-github-integration)).
### Rename a project
You can easily change the name of your projects in Hive by adjusting the settings of your project.

View file

@ -29,6 +29,8 @@ Place a `hive.json` file within the root of your project.
```json filename="hive.json"
{
"registry": "http://localhost:8082/graphql"
"registry": {
"endpoint": "http://localhost:8082/graphql"
}
}
```

View file

@ -235,7 +235,16 @@ guide it's `development`) then go to the settings tab, if you scroll donw a bit
the `Tokens` section, click the `Generate New Token` button, give it a name then we'll have to set
some permissions for that token, so expand the `All targets` section then give the `Registry` a
`Read & Write` access. Click the `Generate Token` button, then copy the token value. Head back to
the `hive.json` config file then add a new key `token` and paste the value from your clipboard.
the `hive.json` config file then add the following contents, while replacing the
`<your-access-token>` with the token value you just copied.
```json filename="hive.json"
{
"registry": {
"accessToken": "<your-access-token>"
}
}
```
Now we're all setup to use the GraphQL Hive CLI against our Setup!

View file

@ -1,6 +1,7 @@
/* eslint sort-keys: error */
import { useRouter } from 'next/router';
import { defineConfig, FooterExtended, Giscus, useTheme } from '@theguild/components';
import { defineConfig, FooterExtended, Giscus, useConfig, useTheme } from '@theguild/components';
import { ChangelogBlogPostHeader } from './src/components/changelog-blog-post-header';
export default defineConfig({
docsRepositoryBase: 'https://github.com/kamilkisiela/graphql-hive/tree/main/packages/web/docs',
@ -22,9 +23,24 @@ export default defineConfig({
/>
),
},
main({ children }) {
const { resolvedTheme } = useTheme();
const { route } = useRouter();
const config = useConfig();
if (route === '/changelog') {
return <>{children}</>;
}
if (route.startsWith('/changelog')) {
children = (
<>
<ChangelogBlogPostHeader meta={config.frontMatter as any} />
{children}
</>
);
}
return (
<>

View file

@ -1809,6 +1809,9 @@ importers:
clsx:
specifier: 2.0.0
version: 2.0.0
date-fns:
specifier: 2.30.0
version: 2.30.0
next:
specifier: 13.5.4
version: 13.5.4(@babel/core@7.23.0)(react-dom@18.2.0)(react@18.2.0)
@ -1818,6 +1821,9 @@ importers:
react:
specifier: 18.2.0
version: 18.2.0
react-avatar:
specifier: 5.0.3
version: 5.0.3(@babel/runtime@7.23.1)(core-js-pure@3.28.0)(prop-types@15.8.1)(react@18.2.0)
react-countup:
specifier: 6.4.2
version: 6.4.2(@babel/core@7.23.0)(react@18.2.0)
@ -2429,7 +2435,7 @@ packages:
'@babel/core': 7.21.5
'@babel/generator': 7.23.0
'@babel/parser': 7.23.0
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@babel/traverse': 7.23.0
'@babel/types': 7.23.0
babel-preset-fbjs: 3.4.0(@babel/core@7.21.5)
@ -5078,7 +5084,6 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.0
dev: true
/@babel/template@7.20.7:
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
@ -5166,7 +5171,7 @@ packages:
/@changesets/apply-release-plan@6.1.4:
resolution: {integrity: sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@changesets/config': 2.3.1
'@changesets/get-version-range-type': 0.3.2
'@changesets/git': 2.0.0
@ -5184,7 +5189,7 @@ packages:
/@changesets/assemble-release-plan@5.2.4:
resolution: {integrity: sha512-xJkWX+1/CUaOUWTguXEbCDTyWJFECEhmdtbkjhn5GVBGxdP/JwaHBIU9sW3FR6gD07UwZ7ovpiPclQZs+j+mvg==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@changesets/errors': 0.1.4
'@changesets/get-dependents-graph': 1.3.6
'@changesets/types': 5.2.1
@ -5287,7 +5292,7 @@ packages:
/@changesets/get-release-plan@3.0.17:
resolution: {integrity: sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@changesets/assemble-release-plan': 5.2.4
'@changesets/config': 2.3.1
'@changesets/pre': 1.0.14
@ -5303,7 +5308,7 @@ packages:
/@changesets/git@2.0.0:
resolution: {integrity: sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@changesets/errors': 0.1.4
'@changesets/types': 5.2.1
'@manypkg/get-packages': 1.1.3
@ -5328,7 +5333,7 @@ packages:
/@changesets/pre@1.0.14:
resolution: {integrity: sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@changesets/errors': 0.1.4
'@changesets/types': 5.2.1
'@manypkg/get-packages': 1.1.3
@ -5338,7 +5343,7 @@ packages:
/@changesets/read@0.5.9:
resolution: {integrity: sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@changesets/git': 2.0.0
'@changesets/logger': 0.0.5
'@changesets/parse': 0.3.16
@ -5359,7 +5364,7 @@ packages:
/@changesets/write@0.2.3:
resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@changesets/types': 5.2.1
fs-extra: 7.0.1
human-id: 1.0.2
@ -5456,7 +5461,7 @@ packages:
'@babel/core': 7.23.0
'@babel/helper-module-imports': 7.21.4
'@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.23.0)
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@emotion/hash': 0.9.0
'@emotion/memoize': 0.8.0
'@emotion/serialize': 1.1.1
@ -5513,7 +5518,7 @@ packages:
optional: true
dependencies:
'@babel/core': 7.23.0
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@emotion/babel-plugin': 11.10.5(@babel/core@7.23.0)
'@emotion/cache': 11.10.5
'@emotion/serialize': 1.1.1
@ -8240,7 +8245,7 @@ packages:
/@manypkg/find-root@1.1.0:
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@types/node': 12.20.55
find-up: 4.1.0
fs-extra: 8.1.0
@ -8258,7 +8263,7 @@ packages:
/@manypkg/get-packages@1.1.3:
resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@changesets/types': 4.1.0
'@manypkg/find-root': 1.1.0
fs-extra: 8.1.0
@ -9764,12 +9769,12 @@ packages:
/@radix-ui/number@1.0.1:
resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
/@radix-ui/primitive@1.0.1:
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
/@radix-ui/react-accordion@1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==}
@ -9813,7 +9818,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.22
'@types/react-dom': 18.2.7
@ -9885,7 +9890,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.22)(react@18.2.0)
@ -9913,7 +9918,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
@ -9932,7 +9937,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@types/react': 18.2.22
react: 18.2.0
@ -9945,7 +9950,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@types/react': 18.2.22
react: 18.2.0
@ -9992,7 +9997,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@types/react': 18.2.22
react: 18.2.0
@ -10009,7 +10014,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
@ -10056,7 +10061,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@types/react': 18.2.22
react: 18.2.0
@ -10073,7 +10078,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.22)(react@18.2.0)
@ -10099,7 +10104,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@types/react': 18.2.22
react: 18.2.0
@ -10138,7 +10143,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
@ -10176,7 +10181,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
@ -10244,7 +10249,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@floating-ui/react-dom': 2.0.0(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
@ -10273,7 +10278,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.22
'@types/react-dom': 18.2.7
@ -10293,7 +10298,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@types/react': 18.2.22
@ -10315,7 +10320,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.22)(react@18.2.0)
'@types/react': 18.2.22
'@types/react-dom': 18.2.7
@ -10365,7 +10370,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
@ -10433,7 +10438,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.22
'@types/react-dom': 18.2.7
@ -10480,7 +10485,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@types/react': 18.2.22
react: 18.2.0
@ -10579,7 +10584,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.22)(react@18.2.0)
@ -10655,7 +10660,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@types/react': 18.2.22
react: 18.2.0
@ -10668,7 +10673,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@types/react': 18.2.22
react: 18.2.0
@ -10682,7 +10687,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@types/react': 18.2.22
react: 18.2.0
@ -10696,7 +10701,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@types/react': 18.2.22
react: 18.2.0
@ -10709,7 +10714,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@types/react': 18.2.22
react: 18.2.0
@ -10722,7 +10727,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/rect': 1.0.1
'@types/react': 18.2.22
react: 18.2.0
@ -10736,7 +10741,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.22)(react@18.2.0)
'@types/react': 18.2.22
react: 18.2.0
@ -10754,7 +10759,7 @@ packages:
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.22
'@types/react-dom': 18.2.7
@ -10764,7 +10769,7 @@ packages:
/@radix-ui/rect@1.0.1:
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
/@repeaterjs/repeater@3.0.4:
resolution: {integrity: sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==}
@ -15222,7 +15227,7 @@ packages:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
cosmiconfig: 7.0.1
resolve: 1.22.2
dev: false
@ -16135,6 +16140,10 @@ packages:
/chardet@0.7.0:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
/charenc@0.0.2:
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
dev: false
/check-error@1.0.3:
resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
dependencies:
@ -16779,7 +16788,6 @@ packages:
/core-js-pure@3.28.0:
resolution: {integrity: sha512-DSOVleA9/v3LNj/vFxAPfUHttKTzrB2RXhAPvR5TPXn4vrra3Z2ssytvRyt8eruJwAfwAiFADEbrjcRdcvPLQQ==}
requiresBuild: true
dev: true
/core-js@3.25.5:
resolution: {integrity: sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==}
@ -16935,6 +16943,10 @@ packages:
shebang-command: 2.0.0
which: 2.0.2
/crypt@0.0.2:
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
dev: false
/crypto-browserify@3.12.0:
resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==}
dependencies:
@ -17865,7 +17877,7 @@ packages:
/dom-helpers@5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
csstype: 3.1.1
dev: false
@ -18577,7 +18589,7 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
aria-query: 5.1.3
array-includes: 3.1.6
array.prototype.flatmap: 1.3.1
@ -21942,6 +21954,10 @@ packages:
is-unc-path: 1.0.0
dev: true
/is-retina@1.0.3:
resolution: {integrity: sha512-/tCmbIETZwCd8uHWO+GvbRa7jxwHFHdfetHfiwoP0aN9UDf3prUJMtKn7iBFYipYhqY1bSTjur8hC/Dakt8eyw==}
dev: false
/is-retry-allowed@1.2.0:
resolution: {integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==}
engines: {node: '>=0.10.0'}
@ -23351,7 +23367,7 @@ packages:
/match-sorter@6.3.1:
resolution: {integrity: sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
remove-accents: 0.4.2
dev: false
@ -23362,6 +23378,14 @@ packages:
inherits: 2.0.4
safe-buffer: 5.2.1
/md5@2.3.0:
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
dependencies:
charenc: 0.0.2
crypt: 0.0.2
is-buffer: 1.1.6
dev: false
/mdast-util-definitions@4.0.0:
resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==}
dependencies:
@ -24341,7 +24365,7 @@ packages:
/mjml-accordion@4.14.0:
resolution: {integrity: sha512-Mq5mOGqt+QovGlxBs2ZHcRsvJ+8veNY8iiwUEHxeoWrkprr5GzdIpTde8D9y8qab+evcBr7NdlvRKYxunsLkpQ==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24351,7 +24375,7 @@ packages:
/mjml-body@4.14.0:
resolution: {integrity: sha512-4mY7dmyw35xf5qjxdoDJahgx9Y+ZCJmlmaKMhJo5WI9zPb30U2y+l1YHUAs4AoXPefu816245mRSNIP7nQBYdg==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24361,7 +24385,7 @@ packages:
/mjml-button@4.14.0:
resolution: {integrity: sha512-9rkL8cSGUGl238yS7KgBae1HAMerV+ZNDb/jfO/f0SaypbAD8WAoyMgp/lA+hwRpJmuG+9qMQedM34f9xTtJaw==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24371,7 +24395,7 @@ packages:
/mjml-carousel@4.14.0:
resolution: {integrity: sha512-KHC5AZku034KJZJEjHyitByDlBJoQcvcf6eZL2DEAshk3ZfsLImrrKqXQ8UPL/19T8Adim5YGHQ8hh5+E8Ir1A==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24382,7 +24406,7 @@ packages:
resolution: {integrity: sha512-Y4xdX6vQuxkeHFD4zBx1Xmct5WCCSym5/w9qp+e4f9ueqcZnDw+p39zJ3xWZDrFYbowf/4TrorbnliMx0NqBtw==}
hasBin: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
chokidar: 3.5.3
glob: 7.2.3
html-minifier: 4.0.0
@ -24400,7 +24424,7 @@ packages:
/mjml-column@4.14.0:
resolution: {integrity: sha512-nwMv5MiYvdAhLX0E1vcW+17hVcqze3G17p0MKbeNYQ2NND50e8YsbWD0zyLTyiuKUFexvf5vbg+TZOC3Ta952g==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24410,7 +24434,7 @@ packages:
/mjml-core@4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa):
resolution: {integrity: sha512-LUEVmwz8t5PaPrJKSIWQrBSfhyTphrW7+lapc7/2Nj7G1zy7u6TpX7qwa5gymoEEQLTv6a9wiKQpRumN8y9N4A==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
cheerio: 1.0.0-rc.10
detect-node: 2.0.4
html-minifier: 4.0.0
@ -24428,7 +24452,7 @@ packages:
/mjml-divider@4.14.0:
resolution: {integrity: sha512-e8RytaUk/KIkagxuuYNgV4mZTKzAALsZoCL7ks4UMkVSSeoIUsTIaWeuLoJYq7l9PbsICxltlDBV/Eem9zwQsA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24438,7 +24462,7 @@ packages:
/mjml-group@4.14.0:
resolution: {integrity: sha512-g6HAYK0i+obawfJmqxjZBMbii0O2A8zTGxL1fJz5a0rhKdMS1Yzqj5AMlBz8Olzgio5RTAoXEg6EhSfDTu94VA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24448,7 +24472,7 @@ packages:
/mjml-head-attributes@4.14.0:
resolution: {integrity: sha512-Qu2THq/INAMZ0nZlLHQvAO2gYLrOVSMuYvDKtzbD0JcNilEq8On8ojRU2W/8MGTSzHSgjzUIjxu8+C4nDGbm3Q==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24458,7 +24482,7 @@ packages:
/mjml-head-breakpoint@4.14.0:
resolution: {integrity: sha512-CURDbswoIUB65vXyB7UcbkccezpNxm5yML4r/s4nQJXzTnCb3iYq/twqDUsHJrS7vKjW7GGB1jQbwrjd3pnLkA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24468,7 +24492,7 @@ packages:
/mjml-head-font@4.14.0:
resolution: {integrity: sha512-e/qKkyh1EiNzE1mBI5xutbnphR1+9D5nwoNVtDGxSdfbWf2XJo/xIoPLe0EkmMGRhT5/MNQS3W6R1bxQY2M2rA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24478,7 +24502,7 @@ packages:
/mjml-head-html-attributes@4.14.0:
resolution: {integrity: sha512-ff95Pqn1qzIQIQEGk1o5TDmWTyFavhzxS0FPpNpkMdJkFGHqCyThqegHxiGRV/3KuveZ/bSxgeZxETwmy11daA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24488,7 +24512,7 @@ packages:
/mjml-head-preview@4.14.0:
resolution: {integrity: sha512-kWjv2uZx79vkXRyYmimrqg8tKN+TtaeEBMhIWbf9m8ZhfCf1cXrLE6Suqqls2e3CctNIzckyS4it6ICq5y7hNQ==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24498,7 +24522,7 @@ packages:
/mjml-head-style@4.14.0:
resolution: {integrity: sha512-Rn3Co84pbIBhlZjJLOCHc8U01eoQMJojCk7+8bXjNTeUt6xjUNm26WEQB2M8NW/FYtZo07xrD/5b7+rdHdb8iw==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24508,7 +24532,7 @@ packages:
/mjml-head-title@4.14.0:
resolution: {integrity: sha512-8wz+uNpCH5Yz/afS95r/pcmGQo+K3t46+XQhHnGUUSOhM3bfSZDyLGuDTsgbKdYPkU2jjF+kMOwnKesM6QAqQA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24518,7 +24542,7 @@ packages:
/mjml-head@4.14.0:
resolution: {integrity: sha512-RKX94T4hDFnm7MQAhK38mRsn5tysAkmca0FEUH6eN7Qe9aAz7aKhnwtFEHzkWwtDSKRVzqSUITfmfFj2Rr6E0A==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24528,7 +24552,7 @@ packages:
/mjml-hero@4.14.0:
resolution: {integrity: sha512-0nzwmBcp/jGnOcntIvU6gEWYqTIVenJWJGV6aGHkRaLPbnmAd2ldTs4yM7QodBhkNCkl69H/YxIc2mJI6/2eXg==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24538,7 +24562,7 @@ packages:
/mjml-image@4.14.0:
resolution: {integrity: sha512-r1clCcV67HwGhaTQC+js3RYOQFvp12Ky3/sybKE6tvaPWNxGgRpPNMEiNLCJPebrmW/1lxOMFqSDMpp/0Qz/Cg==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24549,7 +24573,7 @@ packages:
resolution: {integrity: sha512-7Rg0HPcOlYWVrL+STiASPAiT/rnaYlybmcnWQg26wSrnpabDhQxs0djFvWqGTY2Fa+cfhoywmQU7xzW0eQ6BNg==}
hasBin: true
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
js-beautify: 1.14.6
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
@ -24562,7 +24586,7 @@ packages:
/mjml-navbar@4.14.0:
resolution: {integrity: sha512-m8uvzuYus68NNyDep2hk2ejVk51mp/M/3L8mqPvivfw9hdNQY8OOWKT207gBN+J77p5ATdXphXpxOT8zXVKEBg==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24572,7 +24596,7 @@ packages:
/mjml-parser-xml@4.14.0:
resolution: {integrity: sha512-tUThlFtaqp4ZBDhYAw884Vk/Ge+b0AYqY8JJB4YEFwJJa7ZFqjGA0f2QXqgbJvaOK4WaKtgSjaRK68eN2WewfA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
detect-node: 2.0.4
htmlparser2: 4.1.0
lodash: 4.17.21
@ -24581,7 +24605,7 @@ packages:
/mjml-preset-core@4.14.0:
resolution: {integrity: sha512-YGsKeodJ51mdhn77oQzZnq/C0jQXzGIszgIWL3W5NjMGZM+xyndP514efpJ2jVkQc/WDGQ5JGSUhsnaG61WVRw==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
mjml-accordion: 4.14.0
mjml-body: 4.14.0
mjml-button: 4.14.0
@ -24614,7 +24638,7 @@ packages:
/mjml-raw@4.14.0:
resolution: {integrity: sha512-Y+KDKZ3GZCxlJcydwEC6NHgjd2UY8aswDpBXjheSEs2X92rExSXvQKe14xcytu2u2NrY7VDJyf5ARDl/EOTAyQ==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24624,7 +24648,7 @@ packages:
/mjml-section@4.14.0:
resolution: {integrity: sha512-sZoRs7oEgdsrJijA63xBp4IfN5wRLdsP4SHLUqwvZPoLCZEJOMPJnPXAICzScfKORi0GdRQJ3m3jF57ixLYmsg==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24634,7 +24658,7 @@ packages:
/mjml-social@4.14.0:
resolution: {integrity: sha512-HW0WnXCV+8aNsv71qUNCJlaS5ZEv+uhLmWQfd6c4gZoAYXvfbK6v4NWuLHpyBoU6Mj9e1dXgRUhXHxHn/fofmQ==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24644,7 +24668,7 @@ packages:
/mjml-spacer@4.14.0:
resolution: {integrity: sha512-XxMitHX4rjVojZwU8HTKFKeszrJVqaan1xKjKU80kj3d/tvF1b676HTb6XgxR9IEg6LZ3MGZaExBsuHC9ndAJA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24654,7 +24678,7 @@ packages:
/mjml-table@4.14.0:
resolution: {integrity: sha512-EZ/xSK52VdN6HwrIJubJ4lFC8fkKD1oEroxF3QEcru/wvdArE8MHaEHnySD9ZwOC6CVVyD7ZSoS+eYOxHjk2jw==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24664,7 +24688,7 @@ packages:
/mjml-text@4.14.0:
resolution: {integrity: sha512-u9sqxHiLS8Plcx62LTPRrK4ZspYQzd6MTCtIVvgR79tJMdfeaKhpwot5Vfl+2p8eoKejTmsq+xvpDcL5hLge+g==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
transitivePeerDependencies:
@ -24674,13 +24698,13 @@ packages:
/mjml-validator@4.13.0:
resolution: {integrity: sha512-uURYfyQYtHJ6Qz/1A7/+E9ezfcoISoLZhYK3olsxKRViwaA2Mm8gy/J3yggZXnsUXWUns7Qymycm5LglLEIiQg==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
dev: true
/mjml-wrapper@4.14.0:
resolution: {integrity: sha512-JTOrz6FDiHbElaKHCG1EhceANcAOj4dq6qWr4mmSqCWMReBNa5/2YqdJhIyvw/lKFj630jT7ZCFqsbPxodUxPA==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
lodash: 4.17.21
mjml-core: 4.14.0(patch_hash=zxxsxbqejjmcwuzpigutzzq6wa)
mjml-section: 4.14.0
@ -27595,6 +27619,22 @@ packages:
iconv-lite: 0.4.24
unpipe: 1.0.0
/react-avatar@5.0.3(@babel/runtime@7.23.1)(core-js-pure@3.28.0)(prop-types@15.8.1)(react@18.2.0):
resolution: {integrity: sha512-DNc+qkWH9QehSEZqHBhqpXWsPY+rU9W7kD68QFHfu8Atfsvx/3ML0DzAePgTUd96nCXQQ3KZMcC3LKYT8FiBIg==}
peerDependencies:
'@babel/runtime': '>=7'
core-js-pure: '>=3'
prop-types: ^15.0.0 || ^16.0.0
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
dependencies:
'@babel/runtime': 7.23.1
core-js-pure: 3.28.0
is-retina: 1.0.3
md5: 2.3.0
prop-types: 15.8.1
react: 18.2.0
dev: false
/react-colorful@5.6.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==}
peerDependencies:
@ -27718,7 +27758,7 @@ packages:
algoliasearch: '>= 3.1 < 5'
react: '>= 16.3.0 < 19'
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
algoliasearch: 4.19.1
algoliasearch-helper: 3.14.0(algoliasearch@4.19.1)
prop-types: 15.8.1
@ -27733,7 +27773,7 @@ packages:
react: '>= 16.3.0 < 19'
react-dom: '>= 16.3.0 < 19'
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
algoliasearch: 4.19.1
algoliasearch-helper: 3.14.0(algoliasearch@4.19.1)
classnames: 2.3.2
@ -27875,7 +27915,7 @@ packages:
react: '>=16.6.0'
react-dom: '>=16.6.0'
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
dom-helpers: 5.2.1
loose-envify: 1.4.0
prop-types: 15.8.1
@ -28261,7 +28301,7 @@ packages:
/relay-runtime@12.0.0:
resolution: {integrity: sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
fbjs: 3.0.4
invariant: 2.2.4
transitivePeerDependencies:
@ -32440,7 +32480,7 @@ packages:
resolution: {integrity: sha512-RNUGiZ/sQ37CkhzKFoedkeMfJM0vNQyaz+wRZJzxdKE7VfDeVKH8bb4rr7XhRLbHJz5hSjoDNwMEIaKhuMZ8gQ==}
engines: {node: '>=10'}
dependencies:
'@babel/runtime': 7.21.0
'@babel/runtime': 7.23.1
fn-name: 3.0.0
lodash: 4.17.21
lodash-es: 4.17.21