openxiangda 1.0.172 → 1.0.174

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,166 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { builtinModules, createRequire } = require('module');
4
+ const esbuild = require('esbuild');
5
+
6
+ function resolveWorkspaceAlias(workspaceRoot, importPath) {
7
+ const raw = path.join(
8
+ workspaceRoot,
9
+ 'src',
10
+ String(importPath || '').replace(/^@\//, '')
11
+ );
12
+ const candidates = [
13
+ raw,
14
+ `${raw}.ts`,
15
+ `${raw}.tsx`,
16
+ `${raw}.js`,
17
+ `${raw}.mjs`,
18
+ `${raw}.cjs`,
19
+ path.join(raw, 'index.ts'),
20
+ path.join(raw, 'index.tsx'),
21
+ path.join(raw, 'index.js'),
22
+ ];
23
+ return candidates.find(candidate => fs.existsSync(candidate)) || raw;
24
+ }
25
+
26
+ async function loadLocalFormSchema(workspaceRoot, formCode) {
27
+ const schemaPath = path.join(
28
+ workspaceRoot,
29
+ 'src',
30
+ 'forms',
31
+ formCode,
32
+ 'schema.ts'
33
+ );
34
+ if (!fs.existsSync(schemaPath)) return null;
35
+ const external = Array.from(
36
+ new Set([...builtinModules, ...builtinModules.map(name => `node:${name}`)])
37
+ );
38
+ const built = await esbuild.build({
39
+ entryPoints: [schemaPath],
40
+ bundle: true,
41
+ format: 'cjs',
42
+ platform: 'node',
43
+ target: ['node20'],
44
+ write: false,
45
+ logLevel: 'silent',
46
+ external,
47
+ plugins: [
48
+ {
49
+ name: 'openxiangda-form-schema-workspace-alias',
50
+ setup(build) {
51
+ build.onResolve({ filter: /^@\// }, args => ({
52
+ path: resolveWorkspaceAlias(workspaceRoot, args.path),
53
+ }));
54
+ },
55
+ },
56
+ ],
57
+ });
58
+ const code = built.outputFiles?.[0]?.text;
59
+ if (!code) {
60
+ throw new Error(`FORM_SCHEMA_BUILD_FAILED: ${formCode} 未生成 schema bundle`);
61
+ }
62
+ const schemaModule = { exports: {} };
63
+ const localRequire = createRequire(schemaPath);
64
+ const evaluate = new Function(
65
+ 'module',
66
+ 'exports',
67
+ 'require',
68
+ '__filename',
69
+ '__dirname',
70
+ code
71
+ );
72
+ evaluate(
73
+ schemaModule,
74
+ schemaModule.exports,
75
+ localRequire,
76
+ schemaPath,
77
+ path.dirname(schemaPath)
78
+ );
79
+ const exported = schemaModule.exports?.default || schemaModule.exports;
80
+ return normalizeLocalFormSchema(exported, formCode);
81
+ }
82
+
83
+ function normalizeLocalFormSchema(schema, formCode) {
84
+ if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
85
+ throw new Error(`FORM_SCHEMA_INVALID: ${formCode} 默认导出必须是对象`);
86
+ }
87
+ if (!schema.formMeta || typeof schema.formMeta !== 'object') {
88
+ throw new Error(`FORM_SCHEMA_INVALID: ${formCode}.formMeta 缺失`);
89
+ }
90
+ if (!Array.isArray(schema.fields) || schema.fields.length === 0) {
91
+ throw new Error(`FORM_SCHEMA_INVALID: ${formCode}.fields 不能为空`);
92
+ }
93
+ const seen = new Set();
94
+ const components = schema.fields.map((field, index) => {
95
+ const fieldId = String(field?.fieldId || '').trim();
96
+ const componentName = normalizeComponentName(field?.componentName);
97
+ if (!fieldId || !componentName) {
98
+ throw new Error(
99
+ `FORM_SCHEMA_INVALID: ${formCode}.fields[${index}] 缺少 fieldId/componentName`
100
+ );
101
+ }
102
+ if (seen.has(fieldId)) {
103
+ throw new Error(`FORM_SCHEMA_INVALID: ${formCode} fieldId 重复: ${fieldId}`);
104
+ }
105
+ seen.add(fieldId);
106
+ return {
107
+ componentName,
108
+ id: field.id || `${fieldId}_${index + 1}`,
109
+ title: field.label || fieldId,
110
+ hidden: false,
111
+ isLocked: false,
112
+ condition: true,
113
+ conditionGroup: '',
114
+ props: {
115
+ ...field,
116
+ componentName,
117
+ isFormComponent: true,
118
+ fieldId,
119
+ label: field.label || fieldId,
120
+ tips: field.tips || '',
121
+ value: field.value || '',
122
+ placeholder: field.placeholder || '',
123
+ },
124
+ };
125
+ });
126
+ const pageSchema = {
127
+ version: '2.0',
128
+ componentsTree: [
129
+ {
130
+ componentName: 'Page',
131
+ id: `${schema.formMeta.formUuid || formCode}_page`,
132
+ props: {},
133
+ children: components,
134
+ },
135
+ ],
136
+ };
137
+ return {
138
+ name: schema.formMeta.title || formCode,
139
+ schema: JSON.stringify(pageSchema),
140
+ packages: JSON.stringify({}),
141
+ formType: normalizeFormType(
142
+ schema.formMeta.formType || schema.template?.formType
143
+ ),
144
+ fieldCount: components.length,
145
+ sourcePath: `src/forms/${formCode}/schema.ts`,
146
+ };
147
+ }
148
+
149
+ function normalizeComponentName(value) {
150
+ const name = String(value || '').trim();
151
+ return name === 'TextAreaField' ? 'TextareaField' : name;
152
+ }
153
+
154
+ function normalizeFormType(value) {
155
+ const normalized = String(value || '').trim().toLowerCase();
156
+ return ['process', 'workflow', 'flow', 'flowform', 'processform'].includes(
157
+ normalized
158
+ )
159
+ ? 'process'
160
+ : 'receipt';
161
+ }
162
+
163
+ module.exports = {
164
+ loadLocalFormSchema,
165
+ normalizeLocalFormSchema,
166
+ };
@@ -0,0 +1,89 @@
1
+ const crypto = require('crypto');
2
+
3
+ function legacyRemoteIdentity(value) {
4
+ const remote = String(value || '').trim();
5
+ try {
6
+ const url = new URL(remote);
7
+ url.username = '';
8
+ url.password = '';
9
+ url.search = '';
10
+ url.hash = '';
11
+ return url.toString();
12
+ } catch {
13
+ return remote
14
+ .replace(/^[^@/]+@([^:]+):/, '$1:')
15
+ .replace(/[?#].*$/, '');
16
+ }
17
+ }
18
+
19
+ function toggleDotGit(value) {
20
+ const normalized = String(value || '').replace(/\/+$/, '');
21
+ return /\.git$/i.test(normalized)
22
+ ? normalized.replace(/\.git$/i, '')
23
+ : `${normalized}.git`;
24
+ }
25
+
26
+ function canonicalRemoteIdentity(value) {
27
+ const legacy = legacyRemoteIdentity(value).replace(/\/+$/, '');
28
+ try {
29
+ const url = new URL(legacy);
30
+ const host = url.hostname.toLowerCase();
31
+ const port = url.port ? `:${url.port}` : '';
32
+ const pathname = url.pathname
33
+ .replace(/^\/+|\/+$/g, '')
34
+ .replace(/\.git$/i, '');
35
+ return `${host}${port}/${pathname}`.replace(/\/+$/, '');
36
+ } catch {
37
+ return legacy
38
+ .replace(/^([^:]+):/, '$1/')
39
+ .replace(/\.git$/i, '')
40
+ .replace(/\/+$/, '');
41
+ }
42
+ }
43
+
44
+ function remoteIdentityVariants(value) {
45
+ const legacy = legacyRemoteIdentity(value).replace(/\/+$/, '');
46
+ return Array.from(
47
+ new Set(
48
+ [
49
+ legacy,
50
+ toggleDotGit(legacy),
51
+ canonicalRemoteIdentity(legacy),
52
+ ].filter(Boolean)
53
+ )
54
+ );
55
+ }
56
+
57
+ function sha256(value) {
58
+ return `sha256:${crypto
59
+ .createHash('sha256')
60
+ .update(String(value))
61
+ .digest('hex')}`;
62
+ }
63
+
64
+ function remoteRepositoryHashes(value) {
65
+ return remoteIdentityVariants(value).map(identity =>
66
+ sha256(`remote:${identity}`)
67
+ );
68
+ }
69
+
70
+ function remoteUrlHashes(value) {
71
+ return remoteIdentityVariants(value).map(sha256);
72
+ }
73
+
74
+ function identitiesIntersect(left, right) {
75
+ const leftSet = new Set(
76
+ (Array.isArray(left) ? left : [left]).filter(Boolean)
77
+ );
78
+ return (Array.isArray(right) ? right : [right]).some(value =>
79
+ leftSet.has(value)
80
+ );
81
+ }
82
+
83
+ module.exports = {
84
+ identitiesIntersect,
85
+ legacyRemoteIdentity,
86
+ remoteIdentityVariants,
87
+ remoteRepositoryHashes,
88
+ remoteUrlHashes,
89
+ };
@@ -1,8 +1,11 @@
1
- const crypto = require('crypto');
2
1
  const path = require('path');
3
2
  const { spawnSync } = require('child_process');
4
3
 
5
4
  const { readGitSourceBase } = require('./change-baseline');
5
+ const {
6
+ identitiesIntersect,
7
+ remoteUrlHashes,
8
+ } = require('./git-remote-identity');
6
9
  const { listRuntimeBuildInputChanges } = require('./runtime-lineage');
7
10
 
8
11
  const GIT_OUTPUT_LIMIT = 16 * 1024 * 1024;
@@ -68,6 +71,7 @@ function prepareReleaseSourceRevision(options = {}) {
68
71
  worktreeRoot: readGit(cwd, ['rev-parse', '--show-toplevel']).stdout,
69
72
  remoteName: mainline.remoteName,
70
73
  remoteUrlHash: mainline.remoteUrlHash,
74
+ remoteUrlHashAliases: mainline.remoteUrlHashAliases,
71
75
  mainBranch: mainline.branchName,
72
76
  mainTipCommit: mainline.tipCommit,
73
77
  mainlineSource: mainline.source,
@@ -82,7 +86,10 @@ function assertReleaseSourceRevisionStable(sourceRevision, options = {}) {
82
86
  const current = readGitSourceBase(cwd, 'HEAD');
83
87
  const dirtyPaths = listRuntimeBuildInputChanges(cwd);
84
88
  if (
85
- expected.repo !== current.repo ||
89
+ !identitiesIntersect(
90
+ [expected.repo, ...(expected.repoAliases || [])],
91
+ [current.repo, ...(current.repoAliases || [])]
92
+ ) ||
86
93
  expected.baseCommit !== current.baseCommit ||
87
94
  expected.treeHash !== current.treeHash ||
88
95
  dirtyPaths.length > 0
@@ -109,7 +116,12 @@ function inspectReleaseSourceIntegration(sourceRevision, options = {}) {
109
116
  const cwd = path.resolve(options.cwd || process.cwd());
110
117
  const source = normalizeSourceRevision(sourceRevision);
111
118
  const currentRepository = readGitSourceBase(cwd, 'HEAD');
112
- if (source.repo !== currentRepository.repo) {
119
+ if (
120
+ !identitiesIntersect(
121
+ [source.repo, ...(source.repoAliases || [])],
122
+ [currentRepository.repo, ...(currentRepository.repoAliases || [])]
123
+ )
124
+ ) {
113
125
  throw releaseMainlineError(
114
126
  'RELEASE_SOURCE_REPOSITORY_MISMATCH',
115
127
  '冻结的发布提交与当前 Git 仓库不一致,不能验证主分支回合状态'
@@ -120,7 +132,10 @@ function inspectReleaseSourceIntegration(sourceRevision, options = {}) {
120
132
  if (
121
133
  source.remoteName !== mainline.remoteName ||
122
134
  source.mainBranch !== mainline.branchName ||
123
- source.remoteUrlHash !== mainline.remoteUrlHash
135
+ !identitiesIntersect(
136
+ [source.remoteUrlHash, ...(source.remoteUrlHashAliases || [])],
137
+ [mainline.remoteUrlHash, ...(mainline.remoteUrlHashAliases || [])]
138
+ )
124
139
  ) {
125
140
  throw releaseMainlineError(
126
141
  'RELEASE_MAINLINE_TARGET_CHANGED',
@@ -204,7 +219,8 @@ function resolveAuthoritativeMainline(options = {}) {
204
219
  }
205
220
 
206
221
  function resolveRemoteMainline({ cwd, remoteName }) {
207
- const remoteUrlHash = readRemoteUrlHash(cwd, remoteName);
222
+ const remoteUrlHashAliases = readRemoteUrlHashes(cwd, remoteName);
223
+ const remoteUrlHash = remoteUrlHashAliases[0];
208
224
  const remoteHead = readGit(
209
225
  cwd,
210
226
  ['ls-remote', '--symref', remoteName, 'HEAD'],
@@ -217,6 +233,7 @@ function resolveRemoteMainline({ cwd, remoteName }) {
217
233
  return {
218
234
  remoteName,
219
235
  remoteUrlHash,
236
+ remoteUrlHashAliases,
220
237
  branchName: parsed.branchName,
221
238
  tipCommit: parsed.tipCommit,
222
239
  source: 'remote-head',
@@ -236,6 +253,7 @@ function resolveRemoteMainline({ cwd, remoteName }) {
236
253
  return {
237
254
  remoteName,
238
255
  remoteUrlHash,
256
+ remoteUrlHashAliases,
239
257
  ...candidates[0],
240
258
  source: 'remote-main-master-fallback',
241
259
  };
@@ -246,6 +264,12 @@ function resolveRemoteMainline({ cwd, remoteName }) {
246
264
  `远端 ${remoteName} 同时存在 main 与 master 且无法读取 remote HEAD;请先修复远端默认分支 HEAD`
247
265
  );
248
266
  }
267
+ if (isGitAuthenticationError(remoteHead.stderr)) {
268
+ throw releaseMainlineError(
269
+ 'RELEASE_GIT_AUTH_REQUIRED',
270
+ `无法认证权威远端 ${remoteName}。请先解锁系统钥匙串或配置可用的 Git credential helper,并确认 \`git ls-remote --symref ${remoteName} HEAD\` 可成功执行;发布会在获取平台租约前停止,不会留下远端发布状态`
271
+ );
272
+ }
249
273
  const detail = remoteHead.stderr
250
274
  ? `: ${sanitizeGitError(remoteHead.stderr)}`
251
275
  : '';
@@ -255,6 +279,12 @@ function resolveRemoteMainline({ cwd, remoteName }) {
255
279
  );
256
280
  }
257
281
 
282
+ function isGitAuthenticationError(value) {
283
+ return /(?:authentication failed|could not read (?:username|password)|terminal prompts? disabled|credential|keychain|access denied|http basic: access denied|response:\s*(?:401|403)|returned error:\s*(?:401|403))/i.test(
284
+ String(value || '')
285
+ );
286
+ }
287
+
258
288
  function readRemoteBranchTip(cwd, remoteName, branchName, options = {}) {
259
289
  const result = readGit(
260
290
  cwd,
@@ -422,6 +452,25 @@ function normalizeSourceRevision(value) {
422
452
  const remoteName = normalizeOptionalName(value.remoteName, 'remote');
423
453
  const mainBranch = String(value.mainBranch || '').trim();
424
454
  const remoteUrlHash = String(value.remoteUrlHash || '').trim().toLowerCase();
455
+ const remoteUrlHashAliases = Array.from(
456
+ new Set(
457
+ [
458
+ remoteUrlHash,
459
+ ...(Array.isArray(value.remoteUrlHashAliases)
460
+ ? value.remoteUrlHashAliases
461
+ : []),
462
+ ]
463
+ .map(item => String(item || '').trim().toLowerCase())
464
+ .filter(item => /^sha256:[0-9a-f]{64}$/.test(item))
465
+ )
466
+ );
467
+ const repoAliases = Array.from(
468
+ new Set(
469
+ [repo, ...(Array.isArray(value.repoAliases) ? value.repoAliases : [])]
470
+ .map(item => String(item || '').trim().toLowerCase())
471
+ .filter(item => /^sha256:[0-9a-f]{64}$/.test(item))
472
+ )
473
+ );
425
474
  if (
426
475
  !/^sha256:[0-9a-f]{64}$/i.test(repo) ||
427
476
  !isObjectId(baseCommit) ||
@@ -439,15 +488,17 @@ function normalizeSourceRevision(value) {
439
488
  ...value,
440
489
  repo,
441
490
  repositoryId: repo,
491
+ repoAliases,
442
492
  baseCommit,
443
493
  treeHash,
444
494
  remoteName,
445
495
  remoteUrlHash,
496
+ remoteUrlHashAliases,
446
497
  mainBranch,
447
498
  };
448
499
  }
449
500
 
450
- function readRemoteUrlHash(cwd, remoteName) {
501
+ function readRemoteUrlHashes(cwd, remoteName) {
451
502
  const result = readGit(cwd, ['remote', 'get-url', remoteName]);
452
503
  if (!result.stdout) {
453
504
  throw releaseMainlineError(
@@ -455,24 +506,7 @@ function readRemoteUrlHash(cwd, remoteName) {
455
506
  `无法读取权威远端地址: ${remoteName}`
456
507
  );
457
508
  }
458
- const identity = normalizeRemoteUrlIdentity(result.stdout);
459
- return `sha256:${crypto.createHash('sha256').update(identity).digest('hex')}`;
460
- }
461
-
462
- function normalizeRemoteUrlIdentity(value) {
463
- const remote = String(value || '').trim();
464
- try {
465
- const url = new URL(remote);
466
- url.username = '';
467
- url.password = '';
468
- url.search = '';
469
- url.hash = '';
470
- return url.toString();
471
- } catch {
472
- return remote
473
- .replace(/^[^@/]+@([^:]+):/, '$1:')
474
- .replace(/[?#].*$/, '');
475
- }
509
+ return remoteUrlHashes(result.stdout);
476
510
  }
477
511
 
478
512
  function assertSafeDefaultBranch(remoteName, branchName) {
@@ -555,6 +589,7 @@ module.exports = {
555
589
  assertReleaseSourceIntegrated,
556
590
  assertReleaseSourceRevisionStable,
557
591
  inspectReleaseSourceIntegration,
592
+ isGitAuthenticationError,
558
593
  prepareReleaseSourceRevision,
559
594
  resolveAuthoritativeMainline,
560
595
  };