ReactFlow前端拖拽模型与参数配置功能MVP

This commit is contained in:
ljz committed 2026-07-11 10:49:11 +08:00
1 parent 70c91ed019
commit d3b99304fb
2001 files changed
+349044 -548

No files matched your search

@@ -0,0 +1,178 @@
const Definition = require('@npmcli/config/lib/definitions/definition.js')
const globalDefinitions = require('@npmcli/config/lib/definitions/definitions.js')
const TrustCommand = require('../../trust-cmd.js')
const { trustDefinitions } = require('../../trust-cmd.js')
const { validateUUID } = require('../../utils/validate-uuid.js')
class TrustCircleCI extends TrustCommand {
static description = 'Create a trusted relationship between a package and CircleCI'
static name = 'circleci'
static positionals = 1 // expects at most 1 positional (package name)
static providerName = 'CircleCI'
static providerEntity = 'CircleCI pipeline'
static usage = [
'[package] --org-id <uuid> --project-id <uuid> --pipeline-definition-id <uuid> --vcs-origin <origin> [--context-id <uuid>...] [--allow-publish] [--allow-stage-publish] [-y|--yes]',
]
static definitions = [
new Definition('org-id', {
default: null,
type: String,
required: true,
description: 'CircleCI organization UUID',
}),
new Definition('project-id', {
default: null,
type: String,
required: true,
description: 'CircleCI project UUID',
}),
new Definition('pipeline-definition-id', {
default: null,
type: String,
required: true,
description: 'CircleCI pipeline definition UUID',
}),
new Definition('vcs-origin', {
default: null,
type: String,
required: true,
description: "CircleCI repository origin in format 'provider/owner/repo'",
}),
new Definition('context-id', {
default: null,
type: [null, String, Array],
description: 'CircleCI context UUID to match',
}),
trustDefinitions['allow-publish'],
trustDefinitions['allow-stage-publish'],
// globals are alphabetical
globalDefinitions['dry-run'],
globalDefinitions.json,
globalDefinitions.registry,
globalDefinitions.yes,
]
validateUuid (value, fieldName) {
validateUUID(value, fieldName)
}
validateVcsOrigin (value) {
// Expected format: provider/owner/repo (e.g., github.com/owner/repo, bitbucket.org/owner/repo)
if (value.includes('://')) {
throw new Error("vcs-origin must not include a scheme (e.g., use 'github.com/owner/repo' not 'https://github.com/owner/repo')")
}
const parts = value.split('/')
if (parts.length < 3) {
throw new Error("vcs-origin must be in format 'provider/owner/repo'")
}
}
// Generate a URL from vcs-origin (e.g., github.com/npm/repo -> https://github.com/npm/repo)
getVcsOriginUrl (vcsOrigin) {
if (!vcsOrigin) {
return null
}
// vcs-origin format: github.com/owner/repo or bitbucket.org/owner/repo
return `https://${vcsOrigin}`
}
static optionsToBody (options) {
const { orgId, projectId, pipelineDefinitionId, vcsOrigin, contextIds } = options
const trustConfig = {
type: 'circleci',
claims: {
'oidc.circleci.com/org-id': orgId,
'oidc.circleci.com/project-id': projectId,
'oidc.circleci.com/pipeline-definition-id': pipelineDefinitionId,
'oidc.circleci.com/vcs-origin': vcsOrigin,
},
}
if (contextIds && contextIds.length > 0) {
trustConfig.claims['oidc.circleci.com/context-ids'] = contextIds
}
return trustConfig
}
static bodyToOptions (body) {
return {
...(body.id) && { id: body.id },
...(body.type) && { type: body.type },
...(body.claims?.['oidc.circleci.com/org-id']) && { orgId: body.claims['oidc.circleci.com/org-id'] },
...(body.claims?.['oidc.circleci.com/project-id']) && { projectId: body.claims['oidc.circleci.com/project-id'] },
...(body.claims?.['oidc.circleci.com/pipeline-definition-id']) && {
pipelineDefinitionId: body.claims['oidc.circleci.com/pipeline-definition-id'],
},
...(body.claims?.['oidc.circleci.com/vcs-origin']) && { vcsOrigin: body.claims['oidc.circleci.com/vcs-origin'] },
...(body.claims?.['oidc.circleci.com/context-ids']) && { contextIds: body.claims['oidc.circleci.com/context-ids'] },
}
}
// Override flagsToOptions since CircleCI doesn't use file/entity pattern
async flagsToOptions ({ positionalArgs, flags }) {
const content = await this.optionalPkgJson()
const pkgName = positionalArgs[0] || content.name
if (!pkgName) {
throw new Error('Package name must be specified either as an argument or in package.json file')
}
const orgId = flags['org-id']
const projectId = flags['project-id']
const pipelineDefinitionId = flags['pipeline-definition-id']
const vcsOrigin = flags['vcs-origin']
const contextIds = flags['context-id']
// Validate required flags
if (!orgId) {
throw new Error('org-id is required')
}
if (!projectId) {
throw new Error('project-id is required')
}
if (!pipelineDefinitionId) {
throw new Error('pipeline-definition-id is required')
}
if (!vcsOrigin) {
throw new Error('vcs-origin is required')
}
// Validate formats
this.validateUuid(orgId, 'org-id')
this.validateUuid(projectId, 'project-id')
this.validateUuid(pipelineDefinitionId, 'pipeline-definition-id')
this.validateVcsOrigin(vcsOrigin)
if (contextIds?.length > 0) {
for (const contextId of contextIds) {
this.validateUuid(contextId, 'context-id')
}
}
return {
values: {
package: pkgName,
orgId,
projectId,
pipelineDefinitionId,
vcsOrigin,
...(contextIds?.length > 0 && { contextIds }),
},
fromPackageJson: {},
warnings: [],
urls: {
package: this.getFrontendUrl({ pkgName }),
vcsOrigin: this.getVcsOriginUrl(vcsOrigin),
},
}
}
async exec (positionalArgs, flags) {
await this.createConfigCommand({
positionalArgs,
flags,
})
}
}
module.exports = TrustCircleCI
@@ -0,0 +1,107 @@
const Definition = require('@npmcli/config/lib/definitions/definition.js')
const globalDefinitions = require('@npmcli/config/lib/definitions/definitions.js')
const TrustCommand = require('../../trust-cmd.js')
const { trustDefinitions } = require('../../trust-cmd.js')
const path = require('node:path')
class TrustGitHub extends TrustCommand {
static description = 'Create a trusted relationship between a package and GitHub Actions'
static name = 'github'
static positionals = 1 // expects at most 1 positional (package name)
static providerName = 'GitHub Actions'
static providerEntity = 'GitHub repository'
static providerFile = 'GitHub Actions Workflow'
static providerHostname = 'https://github.com'
// entity means project / repository
static entityKey = 'repository'
static usage = [
'[package] --file [--repo|--repository] [--env|--environment] [--allow-publish] [--allow-stage-publish] [-y|--yes]',
]
static definitions = [
new Definition('file', {
default: null,
type: String,
required: true,
description: 'Name of workflow file within a repositories .GitHub folder (must end in yaml, yml)',
}),
new Definition('repository', {
default: null,
type: String,
description: 'Name of the repository in the format owner/repo',
alias: ['repo'],
}),
new Definition('environment', {
default: null,
type: String,
description: 'CI environment name',
alias: ['env'],
}),
trustDefinitions['allow-publish'],
trustDefinitions['allow-stage-publish'],
// globals are alphabetical
globalDefinitions['dry-run'],
globalDefinitions.json,
globalDefinitions.registry,
globalDefinitions.yes,
]
getEntityUrl ({ providerHostname, file, entity }) {
if (file) {
return new URL(`${entity}/blob/HEAD/.github/workflows/${file}`, providerHostname).toString()
}
return new URL(entity, providerHostname).toString()
}
validateEntity (entity) {
if (entity.split('/').length !== 2) {
throw new Error(`${this.constructor.providerEntity} must be specified in the format owner/repository`)
}
}
validateFile (file) {
if (file !== path.basename(file)) {
throw new Error('GitHub Actions workflow must be just a file not a path')
}
}
static optionsToBody (options) {
const { file, repository, environment } = options
const trustConfig = {
type: 'github',
claims: {
repository,
workflow_ref: {
file,
},
...(environment) && { environment },
},
}
return trustConfig
}
// Convert API response body to options
static bodyToOptions (body) {
const file = body.claims?.workflow_ref?.file
const repository = body.claims?.repository
const environment = body.claims?.environment
return {
...(body.id) && { id: body.id },
...(body.type) && { type: body.type },
...(file) && { file },
...(repository) && { repository },
...(environment) && { environment },
}
}
async exec (positionalArgs, flags) {
await this.createConfigCommand({
positionalArgs,
flags,
})
}
}
module.exports = TrustGitHub
@@ -0,0 +1,108 @@
const Definition = require('@npmcli/config/lib/definitions/definition.js')
const globalDefinitions = require('@npmcli/config/lib/definitions/definitions.js')
const TrustCommand = require('../../trust-cmd.js')
const { trustDefinitions } = require('../../trust-cmd.js')
const path = require('node:path')
class TrustGitLab extends TrustCommand {
static description = 'Create a trusted relationship between a package and GitLab CI/CD'
static name = 'gitlab'
static positionals = 1 // expects at most 1 positional (package name)
static providerName = 'GitLab CI/CD'
static providerEntity = 'GitLab project'
static providerFile = 'GitLab CI/CD Pipeline'
static providerHostname = 'https://gitlab.com'
// entity means project / repository
static entityKey = 'project'
static usage = [
'[package] --file [--project|--repo|--repository] [--env|--environment] [--allow-publish] [--allow-stage-publish] [-y|--yes]',
]
static definitions = [
new Definition('file', {
default: null,
type: String,
required: true,
description: 'Name of pipeline file (e.g., .gitlab-ci.yml)',
}),
new Definition('project', {
default: null,
type: String,
description: 'Name of the project in the format group/project or group/subgroup/project',
}),
new Definition('environment', {
default: null,
type: String,
description: 'CI environment name',
alias: ['env'],
}),
trustDefinitions['allow-publish'],
trustDefinitions['allow-stage-publish'],
// globals are alphabetical
globalDefinitions['dry-run'],
globalDefinitions.json,
globalDefinitions.registry,
globalDefinitions.yes,
]
getEntityUrl ({ providerHostname, file, entity }) {
if (file) {
return new URL(`${entity}/-/blob/HEAD/${file}`, providerHostname).toString()
}
return new URL(entity, providerHostname).toString()
}
validateEntity (entity) {
if (entity.split('/').length < 2) {
throw new Error(`${this.constructor.providerEntity} must be specified in the format group/project or group/subgroup/project`)
}
}
validateFile (file) {
if (file !== path.basename(file)) {
throw new Error('GitLab CI/CD pipeline file must be just a file not a path')
}
}
static optionsToBody (options) {
const { file, project, environment } = options
const trustConfig = {
type: 'gitlab',
claims: {
project_path: project,
// this looks off, but this is correct
/** The ref path to the top-level pipeline definition, for example, gitlab.example.com/my-group/my-project//.gitlab-ci.yml@refs/heads/main. Introduced in GitLab 16.2. This claim is null unless the pipeline definition is located in the same project. */
ci_config_ref_uri: {
file,
},
...(environment) && { environment },
},
}
return trustConfig
}
// Convert API response body to options
static bodyToOptions (body) {
const file = body.claims?.ci_config_ref_uri?.file
const project = body.claims?.project_path
const environment = body.claims?.environment
return {
...(body.id) && { id: body.id },
...(body.type) && { type: body.type },
...(file) && { file },
...(project) && { project },
...(environment) && { environment },
}
}
async exec (positionalArgs, flags) {
await this.createConfigCommand({
positionalArgs,
flags,
})
}
}
module.exports = TrustGitLab
@@ -0,0 +1,25 @@
const BaseCommand = require('../../base-cmd.js')
class Trust extends BaseCommand {
static description = 'Create a trusted relationship between a package and a OIDC provider'
static name = 'trust'
static usage = null
static subcommands = {
github: require('./github.js'),
gitlab: require('./gitlab.js'),
circleci: require('./circleci.js'),
list: require('./list.js'),
revoke: require('./revoke.js'),
}
static async completion (opts) {
const argv = opts.conf.argv.remain
if (argv.length === 2) {
return Object.keys(Trust.subcommands)
}
return []
}
}
module.exports = Trust
@@ -0,0 +1,50 @@
const { otplease } = require('../../utils/auth.js')
const npmFetch = require('npm-registry-fetch')
const npa = require('npm-package-arg')
const TrustCircleCI = require('./circleci.js')
const TrustGithub = require('./github.js')
const TrustGitlab = require('./gitlab.js')
const TrustCommand = require('../../trust-cmd.js')
const globalDefinitions = require('@npmcli/config/lib/definitions/definitions.js')
class TrustList extends TrustCommand {
static description = 'List trusted relationships for a package'
static name = 'list'
static positionals = 1 // expects at most 1 positional (package name)
static usage = [
'[package]',
]
static definitions = [
globalDefinitions.json,
globalDefinitions.registry,
]
static bodyToOptions (body) {
if (body.type === 'circleci') {
return TrustCircleCI.bodyToOptions(body)
} else if (body.type === 'github') {
return TrustGithub.bodyToOptions(body)
} else if (body.type === 'gitlab') {
return TrustGitlab.bodyToOptions(body)
}
return TrustCommand.bodyToOptions(body)
}
async exec (positionalArgs) {
const packageName = positionalArgs[0] || (await this.optionalPkgJson()).name
if (!packageName) {
throw new Error('Package name must be specified either as an argument or in the package.json file')
}
const spec = npa(packageName)
const uri = `/-/package/${spec.escapedName}/trust`
const body = await otplease(this.npm, this.npm.flatOptions, opts => npmFetch.json(uri, {
...opts,
method: 'GET',
}))
this.displayResponseBody({ body, packageName })
}
}
module.exports = TrustList
@@ -0,0 +1,52 @@
const globalDefinitions = require('@npmcli/config/lib/definitions/definitions.js')
const Definition = require('@npmcli/config/lib/definitions/definition.js')
const { otplease } = require('../../utils/auth.js')
const npmFetch = require('npm-registry-fetch')
const npa = require('npm-package-arg')
const TrustCommand = require('../../trust-cmd.js')
class TrustRevoke extends TrustCommand {
static description = 'Revoke a trusted relationship for a package'
static name = 'revoke'
static positionals = 1 // expects at most 1 positional (package name)
static usage = [
'[package] --id=<trust-id>',
]
static definitions = [
new Definition('id', {
default: null,
type: String,
description: 'ID of the trusted relationship to revoke',
required: true,
}),
globalDefinitions['dry-run'],
globalDefinitions.registry,
]
async exec (positionalArgs, flags) {
const dryRun = this.config.get('dry-run')
const pkgName = positionalArgs[0] || (await this.optionalPkgJson()).name
if (!pkgName) {
throw new Error('Package name must be specified either as an argument or in the package.json file')
}
const { id } = flags
if (!id) {
throw new Error('ID of the trusted relationship to revoke must be specified with the --id option')
}
this.dialogue`Attempting to revoke trusted configuration for package ${pkgName} with id ${id}`
if (dryRun) {
return
}
const spec = npa(pkgName)
const uri = `/-/package/${spec.escapedName}/trust/${encodeURIComponent(id)}`
await otplease(this.npm, this.npm.flatOptions, opts => npmFetch(uri, {
...opts,
method: 'DELETE',
}))
this.dialogue`Revoked trusted configuration for package ${pkgName} with id ${id}`
}
}
module.exports = TrustRevoke