tanmi-dock 0.9.1 → 0.9.3

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.
Files changed (50) hide show
  1. package/dist/commands/link.d.ts +27 -1
  2. package/dist/commands/link.d.ts.map +1 -1
  3. package/dist/commands/link.js +362 -163
  4. package/dist/commands/link.js.map +1 -1
  5. package/dist/commands/status.d.ts.map +1 -1
  6. package/dist/commands/status.js +28 -0
  7. package/dist/commands/status.js.map +1 -1
  8. package/dist/commands/unavailable.d.ts +7 -0
  9. package/dist/commands/unavailable.d.ts.map +1 -0
  10. package/dist/commands/unavailable.js +202 -0
  11. package/dist/commands/unavailable.js.map +1 -0
  12. package/dist/core/codepac.d.ts +3 -1
  13. package/dist/core/codepac.d.ts.map +1 -1
  14. package/dist/core/codepac.js +5 -4
  15. package/dist/core/codepac.js.map +1 -1
  16. package/dist/core/platform.d.ts +4 -0
  17. package/dist/core/platform.d.ts.map +1 -1
  18. package/dist/core/platform.js +52 -0
  19. package/dist/core/platform.js.map +1 -1
  20. package/dist/core/registry.d.ts +8 -0
  21. package/dist/core/registry.d.ts.map +1 -1
  22. package/dist/core/registry.js +46 -1
  23. package/dist/core/registry.js.map +1 -1
  24. package/dist/index.js +2 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/types/index.d.ts +3 -0
  27. package/dist/types/index.d.ts.map +1 -1
  28. package/dist/types/index.js +1 -0
  29. package/dist/types/index.js.map +1 -1
  30. package/dist/utils/prompt.d.ts +12 -0
  31. package/dist/utils/prompt.d.ts.map +1 -1
  32. package/dist/utils/prompt.js +102 -0
  33. package/dist/utils/prompt.js.map +1 -1
  34. package/package.json +1 -1
  35. package/dist/commands/doctor.d.ts +0 -7
  36. package/dist/commands/doctor.d.ts.map +0 -1
  37. package/dist/commands/doctor.js +0 -178
  38. package/dist/commands/doctor.js.map +0 -1
  39. package/dist/commands/projects.d.ts +0 -7
  40. package/dist/commands/projects.d.ts.map +0 -1
  41. package/dist/commands/projects.js +0 -126
  42. package/dist/commands/projects.js.map +0 -1
  43. package/dist/commands/repair.d.ts +0 -16
  44. package/dist/commands/repair.d.ts.map +0 -1
  45. package/dist/commands/repair.js +0 -364
  46. package/dist/commands/repair.js.map +0 -1
  47. package/dist/commands/verify.d.ts +0 -11
  48. package/dist/commands/verify.d.ts.map +0 -1
  49. package/dist/commands/verify.js +0 -266
  50. package/dist/commands/verify.js.map +0 -1
@@ -13,7 +13,7 @@ import * as store from '../core/store.js';
13
13
  import * as linker from '../core/linker.js';
14
14
  import * as codepac from '../core/codepac.js';
15
15
  import { setProxyConfig } from '../core/codepac.js';
16
- import { getPlatformHelpText, GENERAL_PLATFORM, SHARED_PLATFORM, pathsEqual, isSparseOnlyCommon, KNOWN_PLATFORM_VALUES } from '../core/platform.js';
16
+ import { getPlatformHelpText, GENERAL_PLATFORM, SHARED_PLATFORM, pathsEqual, isSparseOnlyCommon, KNOWN_PLATFORM_VALUES, getRequestedPlatformTargets, } from '../core/platform.js';
17
17
  import { Transaction } from '../core/transaction.js';
18
18
  import { formatSize, checkDiskSpace } from '../utils/disk.js';
19
19
  import { getDirSize } from '../utils/fs-utils.js';
@@ -60,6 +60,181 @@ export function createLinkCommand() {
60
60
  }
61
61
  });
62
62
  }
63
+ function resolveRequestedPlatformsByActual(requestedPlatforms, sparse, actualPlatforms, vars) {
64
+ const actualExisting = [...new Set(actualPlatforms)];
65
+ const actualPlatformSet = new Set(actualExisting);
66
+ const satisfiedRequested = requestedPlatforms.filter((requestedPlatform) => {
67
+ const targets = getRequestedPlatformTargets(requestedPlatform, sparse, vars);
68
+ return targets.some((target) => actualPlatformSet.has(target));
69
+ });
70
+ return {
71
+ actualExisting,
72
+ satisfiedRequested,
73
+ missingRequested: requestedPlatforms.filter((platform) => !satisfiedRequested.includes(platform)),
74
+ };
75
+ }
76
+ export function assessDownloadResult(requestedPlatforms, sparse, downloadResult, vars) {
77
+ const downloadedActual = new Set(downloadResult.platformDirs);
78
+ const { satisfiedRequested, missingRequested, } = resolveRequestedPlatformsByActual(requestedPlatforms, sparse, downloadResult.platformDirs, vars);
79
+ const downloadedRequested = [...new Set(satisfiedRequested.flatMap((requestedPlatform) => getRequestedPlatformTargets(requestedPlatform, sparse, vars)
80
+ .filter((target) => downloadedActual.has(target))))];
81
+ const unavailableRequested = missingRequested;
82
+ const hasAnyPlatformArtifacts = downloadResult.allPlatformDirs.length > 0;
83
+ const isPureGeneral = Boolean((!sparse || isSparseOnlyCommon(sparse)) &&
84
+ !hasAnyPlatformArtifacts);
85
+ return {
86
+ downloadedRequested,
87
+ satisfiedRequested,
88
+ unavailableRequested,
89
+ hasAnyPlatformArtifacts,
90
+ isPureGeneral,
91
+ };
92
+ }
93
+ async function resolveRequestedStoreState(dependency, requestedPlatforms, vars) {
94
+ const resolvedExisting = [];
95
+ const candidatePlatforms = [...new Set(requestedPlatforms.flatMap((platform) => getRequestedPlatformTargets(platform, dependency.sparse, vars)))];
96
+ for (const platform of candidatePlatforms) {
97
+ if (await store.exists(dependency.libName, dependency.commit, platform)) {
98
+ resolvedExisting.push(platform);
99
+ }
100
+ }
101
+ return resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, resolvedExisting, vars);
102
+ }
103
+ function getBlockedRequestedPlatforms(registry, dependency, requestedPlatforms, vars) {
104
+ const { manualPlatforms, autoPlatforms } = resolveUnavailablePlatforms(registry, dependency);
105
+ const manualBlocked = requestedPlatforms.filter((platform) => manualPlatforms.includes(platform));
106
+ const autoBlocked = requestedPlatforms.filter((platform) => {
107
+ if (!autoPlatforms.includes(platform))
108
+ return false;
109
+ const targets = getRequestedPlatformTargets(platform, dependency.sparse, vars);
110
+ return targets.length === 1 && targets[0] === platform;
111
+ });
112
+ return [...new Set([...manualBlocked, ...autoBlocked])];
113
+ }
114
+ function clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested) {
115
+ if (satisfiedRequested.length === 0)
116
+ return;
117
+ const libKey = registry.getLibraryKey(dependency.libName, dependency.commit);
118
+ const library = registry.getLibrary(libKey);
119
+ if (!library?.unavailablePlatforms || library.unavailablePlatforms.length === 0)
120
+ return;
121
+ const nextUnavailable = library.unavailablePlatforms.filter((platform) => !satisfiedRequested.includes(platform));
122
+ if (nextUnavailable.length === library.unavailablePlatforms.length) {
123
+ return;
124
+ }
125
+ registry.updateLibrary(libKey, {
126
+ unavailablePlatforms: nextUnavailable,
127
+ lastAccess: new Date().toISOString(),
128
+ });
129
+ }
130
+ function upsertLibraryAvailability(dependency, updates) {
131
+ const registry = getRegistry();
132
+ const libKey = registry.getLibraryKey(dependency.libName, dependency.commit);
133
+ const existing = registry.getLibrary(libKey);
134
+ if (existing) {
135
+ registry.updateLibrary(libKey, {
136
+ unavailablePlatforms: updates.unavailablePlatforms ?? existing.unavailablePlatforms,
137
+ platforms: updates.platforms ?? existing.platforms,
138
+ isGeneral: updates.isGeneral ?? existing.isGeneral,
139
+ lastAccess: new Date().toISOString(),
140
+ });
141
+ return;
142
+ }
143
+ registry.addLibrary({
144
+ libName: dependency.libName,
145
+ commit: dependency.commit,
146
+ branch: dependency.branch,
147
+ url: dependency.url,
148
+ platforms: updates.platforms ?? [],
149
+ size: 0,
150
+ isGeneral: updates.isGeneral,
151
+ referencedBy: [],
152
+ unavailablePlatforms: updates.unavailablePlatforms,
153
+ createdAt: new Date().toISOString(),
154
+ lastAccess: new Date().toISOString(),
155
+ });
156
+ }
157
+ export async function resolveLibraryGeneralState(dependency) {
158
+ const registry = getRegistry();
159
+ const libKey = registry.getLibraryKey(dependency.libName, dependency.commit);
160
+ const library = registry.getLibrary(libKey);
161
+ if (typeof library?.isGeneral === 'boolean') {
162
+ return library.isGeneral;
163
+ }
164
+ return store.isGeneralLib(dependency.libName, dependency.commit);
165
+ }
166
+ export function resolveUnavailablePlatforms(registry, dependency) {
167
+ const libKey = registry.getLibraryKey(dependency.libName, dependency.commit);
168
+ const autoPlatforms = registry.getLibrary(libKey)?.unavailablePlatforms || [];
169
+ const manualPlatforms = registry.getManualUnavailablePlatforms(dependency.libName, dependency.commit);
170
+ return {
171
+ platforms: [...new Set([...manualPlatforms, ...autoPlatforms])],
172
+ manualPlatforms,
173
+ autoPlatforms,
174
+ };
175
+ }
176
+ export async function collectProjectDependencyGraph(projectRoot) {
177
+ const { normalizedPath, configPath } = await resolveProjectRootPath(projectRoot);
178
+ if (!configPath) {
179
+ throw new Error('只能在项目目录下使用');
180
+ }
181
+ const results = [];
182
+ const seenDeps = new Set();
183
+ const visitedConfigs = new Set();
184
+ const queue = [
185
+ { configPath, source: 'direct' },
186
+ ];
187
+ const submodules = await findSubmoduleConfigs(normalizedPath);
188
+ for (const submodule of submodules) {
189
+ queue.push({ configPath: submodule.configPath, scope: submodule.relativePath, source: 'submodule' });
190
+ }
191
+ while (queue.length > 0) {
192
+ const current = queue.shift();
193
+ if (visitedConfigs.has(current.configPath))
194
+ continue;
195
+ visitedConfigs.add(current.configPath);
196
+ let parsedConfig;
197
+ try {
198
+ parsedConfig = await parseCodepacDep(current.configPath);
199
+ }
200
+ catch {
201
+ continue;
202
+ }
203
+ for (const dependency of extractDependencies(parsedConfig)) {
204
+ const key = `${dependency.libName}:${dependency.commit}:${current.scope ?? ''}`;
205
+ if (!seenDeps.has(key)) {
206
+ seenDeps.add(key);
207
+ results.push({ ...dependency, scope: current.scope, source: current.source });
208
+ }
209
+ }
210
+ for (const action of extractActions(parsedConfig)) {
211
+ let parsedAction;
212
+ try {
213
+ parsedAction = parseActionCommand(action.command);
214
+ }
215
+ catch {
216
+ continue;
217
+ }
218
+ const nestedConfigPath = path.resolve(path.dirname(current.configPath), parsedAction.configDir, 'codepac-dep.json');
219
+ let nested;
220
+ try {
221
+ nested = await extractNestedDependencies(nestedConfigPath, parsedAction.libraries);
222
+ }
223
+ catch {
224
+ continue;
225
+ }
226
+ for (const dependency of nested.dependencies) {
227
+ const key = `${dependency.libName}:${dependency.commit}:${current.scope ?? ''}`;
228
+ if (!seenDeps.has(key)) {
229
+ seenDeps.add(key);
230
+ results.push({ ...dependency, scope: current.scope, source: 'nested' });
231
+ }
232
+ }
233
+ queue.push({ configPath: nestedConfigPath, scope: current.scope, source: 'nested' });
234
+ }
235
+ }
236
+ return results;
237
+ }
63
238
  /**
64
239
  * 选择要链接的 submodule
65
240
  */
@@ -144,7 +319,7 @@ async function linkScope(params) {
144
319
  blank();
145
320
  // 2. 分类依赖
146
321
  const scopePath = path.dirname(path.dirname(configPath));
147
- const classified = await classifyDependencies(dependencies, scopePath, configPath, platforms);
322
+ const classified = await classifyDependencies(dependencies, scopePath, configPath, platforms, configVars);
148
323
  const stats = {
149
324
  linked: 0, relink: 0, replace: 0, absorb: 0, missing: 0, linkNew: 0,
150
325
  };
@@ -223,13 +398,21 @@ async function linkScope(params) {
223
398
  const needDownloadItems = [];
224
399
  for (const item of classified) {
225
400
  const { dependency, status } = item;
401
+ const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
402
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
226
403
  if (status === DependencyStatus.MISSING) {
227
- needDownloadItems.push({
228
- libName: dependency.libName, commit: dependency.commit, reason: '缺失',
229
- });
404
+ if (requestedPlatforms.length > 0) {
405
+ const { missingRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
406
+ if (missingRequested.length === 0) {
407
+ continue;
408
+ }
409
+ needDownloadItems.push({
410
+ libName: dependency.libName, commit: dependency.commit, reason: '缺失',
411
+ });
412
+ }
230
413
  }
231
414
  else if (status === DependencyStatus.ABSORB) {
232
- const { missing } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
415
+ const { missingRequested: missing } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
233
416
  if (missing.length > 0) {
234
417
  needDownloadItems.push({
235
418
  libName: dependency.libName, commit: dependency.commit,
@@ -238,7 +421,7 @@ async function linkScope(params) {
238
421
  }
239
422
  }
240
423
  else if (status === DependencyStatus.LINK_NEW) {
241
- const { missing } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
424
+ const { missingRequested: missing } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
242
425
  if (missing.length > 0) {
243
426
  needDownloadItems.push({
244
427
  libName: dependency.libName, commit: dependency.commit,
@@ -280,25 +463,32 @@ async function linkScope(params) {
280
463
  await store.ensureCompatibleStore(storePath, dependency.libName, dependency.commit);
281
464
  switch (status) {
282
465
  case DependencyStatus.LINKED: {
283
- const isLinkedGeneral = await store.isGeneralLib(dependency.libName, dependency.commit);
466
+ const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
467
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
468
+ const isLinkedGeneral = await resolveLibraryGeneralState(dependency);
284
469
  if (!isLinkedGeneral) {
285
470
  const supplementResult = await supplementMissingPlatforms(dependency, platforms, registry, tx, { vars: configVars });
286
471
  await registerNestedLibraries(supplementResult.nestedLibraries, projectHash);
287
472
  if (supplementResult.downloaded.length > 0) {
288
473
  const linkedCommitPath = path.join(storePath, dependency.libName, dependency.commit);
289
- const { existing: allExisting } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
474
+ const { actualExisting: allExisting, satisfiedRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
475
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
290
476
  tx.recordOp('link', localPath, linkedCommitPath);
291
477
  await linker.linkLib(localPath, linkedCommitPath, allExisting);
292
478
  await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, allExisting, projectHash, false);
293
479
  success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已补充平台 [${supplementResult.downloaded.join(', ')}]`);
294
480
  }
295
481
  }
296
- await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, isLinkedGeneral ? [GENERAL_PLATFORM] : platforms, projectHash, isLinkedGeneral);
482
+ const { actualExisting: linkedExisting, satisfiedRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
483
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
484
+ await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, isLinkedGeneral ? [GENERAL_PLATFORM] : linkedExisting, projectHash, isLinkedGeneral);
297
485
  break;
298
486
  }
299
487
  case DependencyStatus.RELINK: {
488
+ const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
489
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
300
490
  const relinkCommitPath = path.join(storePath, dependency.libName, dependency.commit);
301
- const isRelinkGeneral = await store.isGeneralLib(dependency.libName, dependency.commit);
491
+ const isRelinkGeneral = await resolveLibraryGeneralState(dependency);
302
492
  if (isRelinkGeneral) {
303
493
  tx.recordOp('unlink', localPath);
304
494
  await linker.unlink(localPath);
@@ -312,21 +502,23 @@ async function linkScope(params) {
312
502
  else {
313
503
  const relinkSupplementResult = await supplementMissingPlatforms(dependency, platforms, registry, tx, { vars: configVars });
314
504
  await registerNestedLibraries(relinkSupplementResult.nestedLibraries, projectHash);
315
- const { existing: relinkExisting } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
505
+ const { actualExisting: relinkExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
506
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
316
507
  if (relinkExisting.length === 0) {
317
508
  const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
318
509
  const relinkCommitEntries = await fs.readdir(relinkCommitPath, { withFileTypes: true });
319
510
  const relinkAvailablePlatforms = relinkCommitEntries
320
511
  .filter(e => e.isDirectory() && e.name !== '_shared' && KNOWN_PLATFORM_VALUES.includes(e.name))
321
512
  .map(e => e.name);
322
- warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 不支持 ${platforms.join('/')} 平台 [可用: ${relinkAvailablePlatforms.join(', ')}]`);
513
+ warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已按规则跳过平台 [${platforms.join('/')}], 可用平台 [${relinkAvailablePlatforms.join(', ')}]`);
323
514
  const relinkLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
324
515
  const relinkLib = registry.getLibrary(relinkLibKey);
325
516
  if (relinkLib) {
326
517
  const unavailable = relinkLib.unavailablePlatforms || [];
327
- for (const p of platforms) {
328
- if (!unavailable.includes(p) && !relinkAvailablePlatforms.includes(p))
329
- unavailable.push(p);
518
+ const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, relinkAvailablePlatforms, configVars).missingRequested;
519
+ for (const requestedPlatform of missingRequested) {
520
+ if (!unavailable.includes(requestedPlatform))
521
+ unavailable.push(requestedPlatform);
330
522
  }
331
523
  registry.updateLibrary(relinkLibKey, { unavailablePlatforms: unavailable });
332
524
  }
@@ -347,9 +539,11 @@ async function linkScope(params) {
347
539
  break;
348
540
  }
349
541
  case DependencyStatus.REPLACE: {
542
+ const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
543
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
350
544
  const replaceSize = await getDirSize(localPath);
351
545
  const replaceCommitPath = path.join(storePath, dependency.libName, dependency.commit);
352
- const isReplaceGeneral = await store.isGeneralLib(dependency.libName, dependency.commit);
546
+ const isReplaceGeneral = await resolveLibraryGeneralState(dependency);
353
547
  if (isReplaceGeneral) {
354
548
  const sharedPath = path.join(replaceCommitPath, '_shared');
355
549
  tx.recordOp('replace', localPath, sharedPath);
@@ -362,21 +556,23 @@ async function linkScope(params) {
362
556
  else {
363
557
  const replaceSupplementResult = await supplementMissingPlatforms(dependency, platforms, registry, tx, { vars: configVars });
364
558
  await registerNestedLibraries(replaceSupplementResult.nestedLibraries, projectHash);
365
- const { existing: replaceExisting } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
559
+ const { actualExisting: replaceExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
560
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
366
561
  if (replaceExisting.length === 0) {
367
562
  const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
368
563
  const replaceCommitEntries = await fs.readdir(replaceCommitPath, { withFileTypes: true });
369
564
  const replaceAvailablePlatforms = replaceCommitEntries
370
565
  .filter(e => e.isDirectory() && e.name !== '_shared' && KNOWN_PLATFORM_VALUES.includes(e.name))
371
566
  .map(e => e.name);
372
- warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 不支持 ${platforms.join('/')} 平台 [可用: ${replaceAvailablePlatforms.join(', ')}]`);
567
+ warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已按规则跳过平台 [${platforms.join('/')}], 可用平台 [${replaceAvailablePlatforms.join(', ')}]`);
373
568
  const replaceLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
374
569
  const replaceLib = registry.getLibrary(replaceLibKey);
375
570
  if (replaceLib) {
376
571
  const unavailable = replaceLib.unavailablePlatforms || [];
377
- for (const p of platforms) {
378
- if (!unavailable.includes(p) && !replaceAvailablePlatforms.includes(p))
379
- unavailable.push(p);
572
+ const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, replaceAvailablePlatforms, configVars).missingRequested;
573
+ for (const requestedPlatform of missingRequested) {
574
+ if (!unavailable.includes(requestedPlatform))
575
+ unavailable.push(requestedPlatform);
380
576
  }
381
577
  registry.updateLibrary(replaceLibKey, { unavailablePlatforms: unavailable });
382
578
  }
@@ -396,22 +592,26 @@ async function linkScope(params) {
396
592
  break;
397
593
  }
398
594
  case DependencyStatus.ABSORB: {
595
+ const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
596
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
399
597
  const storeCommitPath = path.join(storePath, dependency.libName, dependency.commit);
400
598
  const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
401
599
  const localDirEntries = await fs.readdir(localPath, { withFileTypes: true });
402
600
  const localPlatforms = localDirEntries
403
601
  .filter(entry => entry.isDirectory() && KNOWN_PLATFORM_VALUES.includes(entry.name))
404
602
  .map(entry => entry.name);
405
- const finalPlatforms = localPlatforms.filter(p => finalLinkPlatforms.includes(p));
603
+ const requestedTargets = [...new Set(requestedPlatforms.flatMap((platform) => getRequestedPlatformTargets(platform, dependency.sparse, configVars)))];
604
+ const finalPlatforms = localPlatforms.filter((platform) => requestedTargets.includes(platform));
406
605
  if (finalPlatforms.length === 0 && localPlatforms.length > 0) {
407
- warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 不支持 ${platforms.join('/')} 平台 [本地有: ${localPlatforms.join(', ')}]`);
606
+ warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已按规则跳过平台 [${platforms.join('/')}], 本地有 [${localPlatforms.join(', ')}]`);
408
607
  const absorbLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
409
608
  const absorbLib = registry.getLibrary(absorbLibKey);
410
609
  if (absorbLib) {
411
610
  const unavailable = absorbLib.unavailablePlatforms || [];
412
- for (const p of platforms) {
413
- if (!unavailable.includes(p) && !localPlatforms.includes(p))
414
- unavailable.push(p);
611
+ const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, localPlatforms, configVars).missingRequested;
612
+ for (const requestedPlatform of missingRequested) {
613
+ if (!unavailable.includes(requestedPlatform))
614
+ unavailable.push(requestedPlatform);
415
615
  }
416
616
  registry.updateLibrary(absorbLibKey, { unavailablePlatforms: unavailable });
417
617
  }
@@ -442,6 +642,7 @@ async function linkScope(params) {
442
642
  registry.addLibrary({
443
643
  libName: dependency.libName, commit: dependency.commit, branch: dependency.branch,
444
644
  url: dependency.url, platforms: absorbLinkPlatforms, size: absorbSize,
645
+ isGeneral: false,
445
646
  referencedBy: [], createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
446
647
  });
447
648
  }
@@ -505,6 +706,7 @@ async function linkScope(params) {
505
706
  registry.addLibrary({
506
707
  libName: dependency.libName, commit: dependency.commit, branch: dependency.branch,
507
708
  url: dependency.url, platforms: [GENERAL_PLATFORM], size: sharedSize,
709
+ isGeneral: true,
508
710
  referencedBy: [], createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
509
711
  });
510
712
  }
@@ -521,7 +723,9 @@ async function linkScope(params) {
521
723
  break;
522
724
  case DependencyStatus.LINK_NEW: {
523
725
  const linkNewCommitPath = path.join(storePath, dependency.libName, dependency.commit);
524
- const { missing } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
726
+ const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
727
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
728
+ const { missingRequested: missing } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
525
729
  const linkNewLibId = `${dependency.libName}@${dependency.commit}`;
526
730
  if (missing.length > 0 && !skipAllDownloads && downloadConfirmedLibs.has(linkNewLibId)) {
527
731
  info(`${dependency.libName} 缺少平台 [${missing.join(', ')}],开始下载...`);
@@ -546,7 +750,9 @@ async function linkScope(params) {
546
750
  hint(` 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
547
751
  }
548
752
  try {
549
- const filteredDownloaded = downloadResult.platformDirs.filter(p => missing.includes(p));
753
+ const assessment = assessDownloadResult(missing, dependency.sparse, downloadResult, configVars);
754
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, assessment.satisfiedRequested);
755
+ const filteredDownloaded = assessment.downloadedRequested;
550
756
  if (filteredDownloaded.length > 0) {
551
757
  tx.recordOp('absorb', linkNewCommitPath, downloadResult.libDir);
552
758
  const linkNewAbsorbResult = await store.absorbLib(downloadResult.libDir, filteredDownloaded, dependency.libName, dependency.commit);
@@ -557,8 +763,9 @@ async function linkScope(params) {
557
763
  await fs.rm(downloadResult.tempDir, { recursive: true, force: true }).catch(() => { });
558
764
  }
559
765
  }
560
- const { existing: linkNewExisting } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
561
- const isLinkNewGeneral = await store.isGeneralLib(dependency.libName, dependency.commit);
766
+ const { actualExisting: linkNewExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
767
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
768
+ const isLinkNewGeneral = await resolveLibraryGeneralState(dependency);
562
769
  if (isLinkNewGeneral) {
563
770
  const sharedPath = path.join(linkNewCommitPath, '_shared');
564
771
  tx.recordOp('link', localPath, sharedPath);
@@ -578,9 +785,10 @@ async function linkScope(params) {
578
785
  const lnLib = registry.getLibrary(lnLibKey);
579
786
  if (lnLib) {
580
787
  const unavailable = lnLib.unavailablePlatforms || [];
581
- for (const p of platforms) {
582
- if (!unavailable.includes(p) && !availablePlatforms.includes(p))
583
- unavailable.push(p);
788
+ const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, availablePlatforms, configVars).missingRequested;
789
+ for (const requestedPlatform of missingRequested) {
790
+ if (!unavailable.includes(requestedPlatform))
791
+ unavailable.push(requestedPlatform);
584
792
  }
585
793
  registry.updateLibrary(lnLibKey, { unavailablePlatforms: unavailable });
586
794
  }
@@ -596,7 +804,8 @@ async function linkScope(params) {
596
804
  }
597
805
  }
598
806
  // 无论当前是跳过、补平台还是重建链接,都要把本地残留的平台目录清理到最终平台集合
599
- await linker.cleanupLocalExtraPlatforms(localPath, finalLinkPlatforms);
807
+ const desiredLocalPlatforms = await resolveStoredPlatforms(dependency.libName, dependency.commit, finalLinkPlatforms);
808
+ await linker.cleanupLocalExtraPlatforms(localPath, desiredLocalPlatforms);
600
809
  await tx.save();
601
810
  }
602
811
  // 8. 并行处理 MISSING 依赖
@@ -623,23 +832,26 @@ async function linkScope(params) {
623
832
  };
624
833
  try {
625
834
  const storeCommitPath = path.join(storePath, dependency.libName, dependency.commit);
626
- const { existing, missing } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
835
+ const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
836
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
837
+ const { actualExisting: existing, missingRequested: missing, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
838
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
627
839
  if (missing.length === 0) {
628
840
  pLog.info(`${dependency.libName} 所有平台已存在,直接链接...`);
629
841
  tx.recordOp('link', localPath, storeCommitPath);
630
- await linker.linkLib(localPath, storeCommitPath, platforms);
631
- await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, platforms, projectHash, false);
632
- pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 链接完成 [${platforms.join(', ')}]`);
633
- return { success: true, name: dependency.libName, downloadedPlatforms: platforms, skippedPlatforms: [] };
842
+ await linker.linkLib(localPath, storeCommitPath, existing);
843
+ await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, existing, projectHash, false);
844
+ pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 链接完成 [${existing.join(', ')}]`);
845
+ return { success: true, name: dependency.libName, downloadedPlatforms: existing, skippedPlatforms: [] };
634
846
  }
635
847
  const dlLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
636
848
  const historyLib = registry.getLibrary(dlLibKey);
637
- const unavailablePlatforms = historyLib?.unavailablePlatforms || [];
849
+ const unavailablePlatforms = blockedPlatforms;
638
850
  const dlToDownload = missing.filter(p => !unavailablePlatforms.includes(p));
639
851
  const knownUnavailable = missing.filter(p => unavailablePlatforms.includes(p));
640
852
  if (dlToDownload.length === 0) {
641
853
  if (knownUnavailable.length > 0) {
642
- pLog.warn(`${dependency.libName} 平台 [${knownUnavailable.join(', ')}] 不支持(远程不存在)`);
854
+ pLog.warn(`${dependency.libName} 平台 [${knownUnavailable.join(', ')}] 已按规则跳过`);
643
855
  }
644
856
  return { success: false, name: dependency.libName, skipped: true, skippedPlatforms: missing, unsupported: true };
645
857
  }
@@ -666,8 +878,13 @@ async function linkScope(params) {
666
878
  pLog.hint(` 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
667
879
  }
668
880
  try {
669
- const isNewGeneral = (!dependency.sparse || isSparseOnlyCommon(dependency.sparse)) && downloadResult.platformDirs.length === 0;
670
- if (isNewGeneral) {
881
+ const assessment = assessDownloadResult(dlToDownload, dependency.sparse, downloadResult, configVars);
882
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, assessment.satisfiedRequested);
883
+ if (assessment.isPureGeneral) {
884
+ upsertLibraryAvailability(dependency, {
885
+ platforms: [GENERAL_PLATFORM],
886
+ isGeneral: true,
887
+ });
671
888
  tx.recordOp('absorb', storeCommitPath, downloadResult.libDir);
672
889
  await store.absorbGeneral(downloadResult.libDir, dependency.libName, dependency.commit);
673
890
  const sharedPath = path.join(storeCommitPath, '_shared');
@@ -678,22 +895,14 @@ async function linkScope(params) {
678
895
  pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,下载完成`);
679
896
  return { success: true, name: dependency.libName, downloadedPlatforms: [GENERAL_PLATFORM], skippedPlatforms: [], isGeneral: true };
680
897
  }
681
- const filteredDownloaded = downloadResult.platformDirs.filter(p => dlToDownload.includes(p));
682
- const newUnavailable = dlToDownload.filter(p => !filteredDownloaded.includes(p));
898
+ const filteredDownloaded = assessment.downloadedRequested;
899
+ const newUnavailable = assessment.unavailableRequested;
683
900
  if (newUnavailable.length > 0) {
684
- const updateLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
685
- if (historyLib) {
686
- const updatedUnavailable = [...new Set([...unavailablePlatforms, ...newUnavailable])];
687
- registry.updateLibrary(updateLibKey, { unavailablePlatforms: updatedUnavailable });
688
- }
689
- else {
690
- registry.addLibrary({
691
- libName: dependency.libName, commit: dependency.commit, branch: dependency.branch,
692
- url: dependency.url, platforms: [], size: 0, referencedBy: [],
693
- unavailablePlatforms: newUnavailable,
694
- createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
695
- });
696
- }
901
+ const updatedUnavailable = [...new Set([...unavailablePlatforms, ...newUnavailable])];
902
+ upsertLibraryAvailability(dependency, {
903
+ unavailablePlatforms: updatedUnavailable,
904
+ isGeneral: false,
905
+ });
697
906
  pLog.warn(`${dependency.libName} 平台 [${newUnavailable.join(', ')}] 远程不存在,已记录`);
698
907
  }
699
908
  if (filteredDownloaded.length > 0) {
@@ -702,23 +911,18 @@ async function linkScope(params) {
702
911
  await registerNestedLibraries(downloadAbsorbResult.nestedLibraries, projectHash);
703
912
  }
704
913
  const linkPlatforms = [...existing, ...filteredDownloaded];
705
- const isDownloadGeneral = await store.isGeneralLib(dependency.libName, dependency.commit);
706
- if (isDownloadGeneral) {
707
- const sharedPath = path.join(storeCommitPath, '_shared');
708
- tx.recordOp('link', localPath, sharedPath);
709
- await linker.linkGeneral(localPath, sharedPath);
710
- await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], projectHash, true);
711
- generalLibs.add(dependency.libName);
712
- pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,下载完成`);
713
- return { success: true, name: dependency.libName, downloadedPlatforms: [GENERAL_PLATFORM], skippedPlatforms: [], isGeneral: true };
714
- }
715
914
  if (linkPlatforms.length === 0) {
716
- return { success: false, name: dependency.libName, skipped: true, skippedPlatforms: platforms };
915
+ return {
916
+ success: false,
917
+ name: dependency.libName,
918
+ skipped: true,
919
+ skippedPlatforms: requestedPlatforms,
920
+ };
717
921
  }
718
922
  tx.recordOp('link', localPath, storeCommitPath);
719
923
  await linker.linkLib(localPath, storeCommitPath, linkPlatforms);
720
924
  await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, linkPlatforms, projectHash, false);
721
- const notLinkedPlatforms = platforms.filter((p) => !linkPlatforms.includes(p));
925
+ const notLinkedPlatforms = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, linkPlatforms, configVars).missingRequested;
722
926
  pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 下载完成 [${linkPlatforms.join(', ')}]`);
723
927
  return { success: true, name: dependency.libName, downloadedPlatforms: linkPlatforms, skippedPlatforms: notLinkedPlatforms };
724
928
  }
@@ -783,20 +987,22 @@ async function linkScope(params) {
783
987
  // 10. 同步 cache 文件
784
988
  await syncCacheFile(configPath);
785
989
  // 11. 构建返回结果
786
- const primaryPlatform = platforms[0];
787
- const linkedDeps = classified
788
- .filter((c) => {
789
- if (c.status === DependencyStatus.MISSING)
790
- return downloadedLibs.includes(c.dependency.libName);
791
- return true;
792
- })
793
- .map((c) => ({
794
- libName: c.dependency.libName,
795
- commit: c.dependency.commit,
796
- platform: generalLibs.has(c.dependency.libName) ? GENERAL_PLATFORM : primaryPlatform,
797
- linkedPath: path.relative(projectRoot, c.localPath),
798
- scope,
799
- }));
990
+ const linkedDeps = [];
991
+ for (const item of classified) {
992
+ if (item.status === DependencyStatus.MISSING && !downloadedLibs.includes(item.dependency.libName)) {
993
+ continue;
994
+ }
995
+ const actualPlatforms = generalLibs.has(item.dependency.libName)
996
+ ? [GENERAL_PLATFORM]
997
+ : await resolveStoredPlatforms(item.dependency.libName, item.dependency.commit, platforms);
998
+ linkedDeps.push({
999
+ libName: item.dependency.libName,
1000
+ commit: item.dependency.commit,
1001
+ platform: actualPlatforms[0] ?? platforms[0],
1002
+ linkedPath: path.relative(projectRoot, item.localPath),
1003
+ scope,
1004
+ });
1005
+ }
800
1006
  return {
801
1007
  linkedDeps, nestedLinkedDeps, generalLibs, downloadedLibs, savedBytes, finalLinkPlatforms, stats,
802
1008
  };
@@ -1271,7 +1477,7 @@ async function ensureStoreInfo(libName, commit, platform, branch, url, projectHa
1271
1477
  registry.addStoreReference(storeKey, projectHash);
1272
1478
  }
1273
1479
  }
1274
- async function syncLibraryInfo(libName, commit, branch, url, platforms) {
1480
+ async function syncLibraryInfo(libName, commit, branch, url, platforms, isGeneral) {
1275
1481
  const registry = getRegistry();
1276
1482
  const libKey = registry.getLibraryKey(libName, commit);
1277
1483
  const existingLibrary = registry.getLibrary(libKey);
@@ -1288,6 +1494,7 @@ async function syncLibraryInfo(libName, commit, branch, url, platforms) {
1288
1494
  url,
1289
1495
  platforms: normalizedPlatforms,
1290
1496
  size: totalSize,
1497
+ isGeneral,
1291
1498
  referencedBy: [],
1292
1499
  createdAt: new Date().toISOString(),
1293
1500
  lastAccess: new Date().toISOString(),
@@ -1299,6 +1506,7 @@ async function syncLibraryInfo(libName, commit, branch, url, platforms) {
1299
1506
  url: url || existingLibrary.url,
1300
1507
  platforms: normalizedPlatforms,
1301
1508
  size: totalSize,
1509
+ isGeneral,
1302
1510
  lastAccess: new Date().toISOString(),
1303
1511
  });
1304
1512
  }
@@ -1330,7 +1538,7 @@ async function ensureLinkedRegistryState(libName, commit, branch, url, platforms
1330
1538
  if (!isGeneral) {
1331
1539
  await registerSharedStore(libName, commit, branch, url);
1332
1540
  }
1333
- await syncLibraryInfo(libName, commit, branch, url, libraryPlatforms);
1541
+ await syncLibraryInfo(libName, commit, branch, url, libraryPlatforms, isGeneral);
1334
1542
  }
1335
1543
  /**
1336
1544
  * 注册嵌套依赖到 Registry
@@ -1372,6 +1580,7 @@ async function registerNestedLibraries(nestedLibraries, projectHash) {
1372
1580
  url: '',
1373
1581
  platforms: [GENERAL_PLATFORM],
1374
1582
  size: nested.size,
1583
+ isGeneral: true,
1375
1584
  referencedBy: [],
1376
1585
  createdAt: new Date().toISOString(),
1377
1586
  lastAccess: new Date().toISOString(),
@@ -1410,6 +1619,7 @@ async function registerNestedLibraries(nestedLibraries, projectHash) {
1410
1619
  url: '',
1411
1620
  platforms: nested.platforms,
1412
1621
  size: nested.size,
1622
+ isGeneral: false,
1413
1623
  referencedBy: [],
1414
1624
  createdAt: new Date().toISOString(),
1415
1625
  lastAccess: new Date().toISOString(),
@@ -1474,7 +1684,7 @@ export async function classifyLinkStatus(localPath, storeCommitPath, isGeneral,
1474
1684
  * @param configPath 配置文件路径
1475
1685
  * @param platforms 请求的平台列表
1476
1686
  */
1477
- async function classifyDependencies(dependencies, projectPath, configPath, platforms) {
1687
+ async function classifyDependencies(dependencies, projectPath, configPath, platforms, vars) {
1478
1688
  const result = [];
1479
1689
  const thirdPartyDir = path.dirname(configPath);
1480
1690
  const storePath = await store.getStorePath();
@@ -1485,8 +1695,10 @@ async function classifyDependencies(dependencies, projectPath, configPath, platf
1485
1695
  for (const dep of dependencies) {
1486
1696
  const localPath = path.join(thirdPartyDir, dep.libName);
1487
1697
  const storeLibPath = store.getLibraryPath(storePath, dep.libName, dep.commit, primaryPlatform);
1488
- // 检查 Store 中是否有任意请求的平台(而非只检查主平台)
1489
- const { existing: rawExisting } = await store.checkPlatformCompleteness(dep.libName, dep.commit, platforms);
1698
+ const blockedPlatforms = getBlockedRequestedPlatforms(getRegistry(), dep, platforms, vars);
1699
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
1700
+ // 检查 Store 中是否有任意请求的平台(支持 sparse 承载目录)
1701
+ const { actualExisting: rawExisting, satisfiedRequested, } = await resolveRequestedStoreState(dep, requestedPlatforms, vars);
1490
1702
  // 完整性校验:验证 existing 平台的文件完整性
1491
1703
  const { valid: verifiedExisting, corrupted } = await verifyExistingPlatforms(dep.libName, dep.commit, rawExisting);
1492
1704
  // 清除损坏的平台数据
@@ -1501,8 +1713,11 @@ async function classifyDependencies(dependencies, projectPath, configPath, platf
1501
1713
  }
1502
1714
  const existing = verifiedExisting;
1503
1715
  // 也检查是否为 General 库(有 _shared 且有内容)
1504
- const isGeneral = await store.isGeneralLib(dep.libName, dep.commit);
1716
+ const isGeneral = await resolveLibraryGeneralState(dep);
1505
1717
  const inStore = existing.length > 0 || isGeneral;
1718
+ if (satisfiedRequested.length > 0) {
1719
+ clearSatisfiedAutoUnavailablePlatforms(getRegistry(), dep, satisfiedRequested);
1720
+ }
1506
1721
  // 用于非 inStore 情况的本地路径状态检查
1507
1722
  const expectedTarget = isGeneral
1508
1723
  ? path.join(storePath, dep.libName, dep.commit, '_shared')
@@ -1584,15 +1799,17 @@ async function classifyDependencies(dependencies, projectPath, configPath, platf
1584
1799
  }
1585
1800
  async function supplementMissingPlatforms(dependency, platforms, registry, tx, options = {}) {
1586
1801
  const result = { downloaded: [], unavailable: [], nestedLibraries: [] };
1802
+ const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, options.vars);
1803
+ const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
1587
1804
  // 1. 检查平台完整性
1588
- const { missing } = await store.checkPlatformCompleteness(dependency.libName, dependency.commit, platforms);
1805
+ const { missingRequested, satisfiedRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, options.vars);
1806
+ const missing = missingRequested;
1589
1807
  if (missing.length === 0) {
1808
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
1590
1809
  return result;
1591
1810
  }
1592
1811
  // 2. 获取已知不可用平台
1593
- const libKey = registry.getLibraryKey(dependency.libName, dependency.commit);
1594
- const libInfo = registry.getLibrary(libKey);
1595
- const unavailablePlatforms = libInfo?.unavailablePlatforms || [];
1812
+ const unavailablePlatforms = blockedPlatforms;
1596
1813
  // 3. 计算需要下载的平台(排除已知不可用的,除非强制下载)
1597
1814
  const toDownload = options.forceDownload
1598
1815
  ? missing
@@ -1600,7 +1817,7 @@ async function supplementMissingPlatforms(dependency, platforms, registry, tx, o
1600
1817
  if (toDownload.length === 0) {
1601
1818
  // 所有缺失平台都是已知不可用的
1602
1819
  if (missing.length > 0) {
1603
- hint(`${dependency.libName} 平台 [${missing.join(', ')}] 远程不存在(已记录)`);
1820
+ hint(`${dependency.libName} 平台 [${missing.join(', ')}] 已按规则跳过`);
1604
1821
  }
1605
1822
  result.unavailable = missing.filter((p) => unavailablePlatforms.includes(p));
1606
1823
  return result;
@@ -1622,18 +1839,20 @@ async function supplementMissingPlatforms(dependency, platforms, registry, tx, o
1622
1839
  hint(` 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
1623
1840
  }
1624
1841
  try {
1625
- // 5. 检查实际下载了什么
1626
- const downloaded = downloadResult.platformDirs.filter((p) => toDownload.includes(p));
1842
+ // 5. 解释下载结果
1843
+ const assessment = assessDownloadResult(toDownload, dependency.sparse, downloadResult, options.vars);
1844
+ const downloaded = assessment.downloadedRequested;
1627
1845
  result.downloaded = downloaded;
1846
+ clearSatisfiedAutoUnavailablePlatforms(registry, dependency, assessment.satisfiedRequested);
1628
1847
  // 6. 记录新发现的不可用平台
1629
- const notFound = toDownload.filter((p) => !downloaded.includes(p));
1848
+ const notFound = assessment.unavailableRequested;
1630
1849
  if (notFound.length > 0) {
1631
1850
  result.unavailable = notFound;
1632
- // 更新 LibraryInfo(如果存在)
1633
- if (libInfo) {
1634
- const newUnavailable = [...new Set([...unavailablePlatforms, ...notFound])];
1635
- registry.updateLibrary(libKey, { unavailablePlatforms: newUnavailable });
1636
- }
1851
+ const newUnavailable = [...new Set([...unavailablePlatforms, ...notFound])];
1852
+ upsertLibraryAvailability(dependency, {
1853
+ unavailablePlatforms: newUnavailable,
1854
+ isGeneral: false,
1855
+ });
1637
1856
  warn(`${dependency.libName} 平台 [${notFound.join(', ')}] 远程不存在,已记录`);
1638
1857
  }
1639
1858
  // 7. 吸收下载的内容到 Store(不做链接,由调用者负责)
@@ -1886,7 +2105,6 @@ async function linkNestedDependencies(dependencies, params) {
1886
2105
  const nestedTargetDir = path.join(thirdPartyDir, targetDir);
1887
2106
  const { tx, registry, projectHash, projectRoot, dryRun, download, generalLibs, downloadedLibs, nestedLinkedDeps } = options;
1888
2107
  const { platforms, vars } = context;
1889
- const primaryPlatform = platforms[0];
1890
2108
  for (const dep of dependencies) {
1891
2109
  const localPath = path.join(nestedTargetDir, dep.libName);
1892
2110
  const storePath = await store.getStorePath();
@@ -1905,18 +2123,18 @@ async function linkNestedDependencies(dependencies, params) {
1905
2123
  // 检查 Store 状态和历史记录
1906
2124
  const libKey = registry.getLibraryKey(dep.libName, dep.commit);
1907
2125
  const historyLib = registry.getLibrary(libKey);
1908
- const unavailablePlatforms = historyLib?.unavailablePlatforms || [];
2126
+ const unavailablePlatforms = getBlockedRequestedPlatforms(registry, dep, platforms, vars);
1909
2127
  // 过滤掉已知不可用的平台
1910
2128
  const availablePlatforms = platforms.filter(p => !unavailablePlatforms.includes(p));
1911
2129
  const knownUnavailable = platforms.filter(p => unavailablePlatforms.includes(p));
1912
2130
  let storeHas = false;
1913
2131
  const existingPlatforms = [];
1914
- for (const p of availablePlatforms) {
1915
- if (await store.exists(dep.libName, dep.commit, p)) {
1916
- existingPlatforms.push(p);
1917
- storeHas = true;
1918
- }
2132
+ const initialStoreState = await resolveRequestedStoreState(dep, availablePlatforms, vars);
2133
+ existingPlatforms.push(...initialStoreState.actualExisting);
2134
+ if (initialStoreState.satisfiedRequested.length > 0) {
2135
+ clearSatisfiedAutoUnavailablePlatforms(registry, dep, initialStoreState.satisfiedRequested);
1919
2136
  }
2137
+ storeHas = existingPlatforms.length > 0;
1920
2138
  // 完整性校验:验证 existing 平台的文件完整性(与 classifyDependencies 同逻辑)
1921
2139
  if (existingPlatforms.length > 0) {
1922
2140
  const { valid: verifiedPlatforms, corrupted } = await verifyExistingPlatforms(dep.libName, dep.commit, existingPlatforms);
@@ -1930,10 +2148,10 @@ async function linkNestedDependencies(dependencies, params) {
1930
2148
  storeHas = verifiedPlatforms.length > 0;
1931
2149
  }
1932
2150
  }
1933
- let isGeneral = await store.isGeneralLib(dep.libName, dep.commit);
2151
+ let isGeneral = await resolveLibraryGeneralState(dep);
1934
2152
  // 如果所有平台都已知不可用,跳过
1935
2153
  if (availablePlatforms.length === 0 && knownUnavailable.length > 0 && !isGeneral) {
1936
- warn(`${indent} ${dep.libName} - 平台 [${knownUnavailable.join(', ')}] 不支持(远程不存在)`);
2154
+ warn(`${indent} ${dep.libName} - 平台 [${knownUnavailable.join(', ')}] 已按规则跳过`);
1937
2155
  continue;
1938
2156
  }
1939
2157
  // Store 没有,但本地是目录时,验证 commit 并尝试 absorb
@@ -1970,14 +2188,16 @@ async function linkNestedDependencies(dependencies, params) {
1970
2188
  sparse: dep.sparse,
1971
2189
  vars,
1972
2190
  });
1973
- if (downloadResult.platformDirs.length > 0) {
2191
+ const assessment = assessDownloadResult(availablePlatforms, dep.sparse, downloadResult, vars);
2192
+ clearSatisfiedAutoUnavailablePlatforms(registry, dep, assessment.satisfiedRequested);
2193
+ if (assessment.downloadedRequested.length > 0) {
1974
2194
  // 有平台内容 → 吸收平台目录,重新分类为平台库
1975
2195
  tx.recordOp('absorb', storeCommitPath, downloadResult.libDir);
1976
- const nestedAbsorbResult = await store.absorbLib(downloadResult.libDir, downloadResult.platformDirs, dep.libName, dep.commit);
2196
+ const nestedAbsorbResult = await store.absorbLib(downloadResult.libDir, assessment.downloadedRequested, dep.libName, dep.commit);
1977
2197
  await registerNestedLibraries(nestedAbsorbResult.nestedLibraries, projectHash);
1978
- existingPlatforms.push(...downloadResult.platformDirs);
2198
+ existingPlatforms.push(...assessment.downloadedRequested);
1979
2199
  isGeneral = false;
1980
- info(`${indent} ${dep.libName} - 补充平台内容 [${downloadResult.platformDirs.join(', ')}]`);
2200
+ info(`${indent} ${dep.libName} - 补充平台内容 [${assessment.downloadedRequested.join(', ')}]`);
1981
2201
  }
1982
2202
  if (downloadResult.cleanedPlatforms.length > 0) {
1983
2203
  hint(`${indent} 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
@@ -2032,7 +2252,7 @@ async function linkNestedDependencies(dependencies, params) {
2032
2252
  nestedLinkedDeps.push({
2033
2253
  libName: dep.libName,
2034
2254
  commit: dep.commit,
2035
- platform: isGeneral ? GENERAL_PLATFORM : primaryPlatform,
2255
+ platform: isGeneral ? GENERAL_PLATFORM : (linkedPlatforms[0] ?? availablePlatforms[0]),
2036
2256
  linkedPath: path.relative(projectRoot, localPath),
2037
2257
  });
2038
2258
  if (!isGeneral) {
@@ -2095,7 +2315,7 @@ async function linkNestedDependencies(dependencies, params) {
2095
2315
  nestedLinkedDeps.push({
2096
2316
  libName: dep.libName,
2097
2317
  commit: dep.commit,
2098
- platform: primaryPlatform,
2318
+ platform: allPlatforms[0],
2099
2319
  linkedPath: path.relative(projectRoot, localPath),
2100
2320
  });
2101
2321
  success(`${indent} ${dep.libName} - 链接完成 [${allPlatforms.join(', ')}]`);
@@ -2137,10 +2357,14 @@ async function linkNestedDependencies(dependencies, params) {
2137
2357
  if (downloadResult.cleanedPlatforms.length > 0) {
2138
2358
  hint(`${indent} 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
2139
2359
  }
2140
- // 检测是否为 General 库(没有 sparse 配置,或 sparse 只有 common,且没有平台目录)
2141
- const isNewGeneral = (!dep.sparse || isSparseOnlyCommon(dep.sparse)) && downloadResult.platformDirs.length === 0;
2142
- if (isNewGeneral) {
2360
+ const assessment = assessDownloadResult(availablePlatforms, dep.sparse, downloadResult, vars);
2361
+ clearSatisfiedAutoUnavailablePlatforms(registry, dep, assessment.satisfiedRequested);
2362
+ if (assessment.isPureGeneral) {
2143
2363
  // General 库处理
2364
+ upsertLibraryAvailability(dep, {
2365
+ platforms: [GENERAL_PLATFORM],
2366
+ isGeneral: true,
2367
+ });
2144
2368
  tx.recordOp('absorb', storeCommitPath, downloadResult.libDir);
2145
2369
  await store.absorbGeneral(downloadResult.libDir, dep.libName, dep.commit);
2146
2370
  const sharedPath = path.join(storeCommitPath, '_shared');
@@ -2158,67 +2382,42 @@ async function linkNestedDependencies(dependencies, params) {
2158
2382
  });
2159
2383
  success(`${indent} ${dep.libName} - 下载完成 (General)`);
2160
2384
  }
2161
- else if (downloadResult.platformDirs.length > 0) {
2385
+ else if (assessment.downloadedRequested.length > 0) {
2162
2386
  // 平台库处理
2163
2387
  tx.recordOp('absorb', storeCommitPath, downloadResult.libDir);
2164
- const nestedAbsorbResult = await store.absorbLib(downloadResult.libDir, downloadResult.platformDirs, dep.libName, dep.commit);
2388
+ const nestedAbsorbResult = await store.absorbLib(downloadResult.libDir, assessment.downloadedRequested, dep.libName, dep.commit);
2165
2389
  await registerNestedLibraries(nestedAbsorbResult.nestedLibraries, projectHash);
2166
2390
  tx.recordOp('link', localPath, storeCommitPath);
2167
- await linker.linkLib(localPath, storeCommitPath, downloadResult.platformDirs);
2168
- await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, downloadResult.platformDirs, projectHash, false);
2391
+ await linker.linkLib(localPath, storeCommitPath, assessment.downloadedRequested);
2392
+ await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, assessment.downloadedRequested, projectHash, false);
2169
2393
  downloadedLibs.push(dep.libName);
2170
2394
  // 记录到 nestedLinkedDeps
2171
2395
  nestedLinkedDeps.push({
2172
2396
  libName: dep.libName,
2173
2397
  commit: dep.commit,
2174
- platform: primaryPlatform,
2398
+ platform: assessment.downloadedRequested[0],
2175
2399
  linkedPath: path.relative(projectRoot, localPath),
2176
2400
  });
2177
2401
  // 检查并记录新发现的不可用平台
2178
- const newUnavailable = availablePlatforms.filter(p => !downloadResult.platformDirs.includes(p));
2402
+ const newUnavailable = assessment.unavailableRequested;
2179
2403
  if (newUnavailable.length > 0) {
2180
2404
  const updatedUnavailable = [...new Set([...unavailablePlatforms, ...newUnavailable])];
2181
- if (historyLib) {
2182
- registry.updateLibrary(libKey, { unavailablePlatforms: updatedUnavailable });
2183
- }
2184
- else {
2185
- registry.addLibrary({
2186
- libName: dep.libName,
2187
- commit: dep.commit,
2188
- branch: dep.branch,
2189
- url: dep.url,
2190
- platforms: downloadResult.platformDirs,
2191
- size: 0,
2192
- referencedBy: [],
2193
- unavailablePlatforms: newUnavailable,
2194
- createdAt: new Date().toISOString(),
2195
- lastAccess: new Date().toISOString(),
2196
- });
2197
- }
2405
+ upsertLibraryAvailability(dep, {
2406
+ unavailablePlatforms: updatedUnavailable,
2407
+ platforms: assessment.downloadedRequested,
2408
+ isGeneral: false,
2409
+ });
2198
2410
  warn(`${indent} ${dep.libName} 平台 [${newUnavailable.join(', ')}] 远程不存在,已记录`);
2199
2411
  }
2200
- success(`${indent} ${dep.libName} - 下载完成 [${downloadResult.platformDirs.join(', ')}]`);
2412
+ success(`${indent} ${dep.libName} - 下载完成 [${assessment.downloadedRequested.join(', ')}]`);
2201
2413
  }
2202
2414
  else {
2203
2415
  // 所有请求的平台都不可用,记录到 registry
2204
2416
  const newUnavailable = [...new Set([...unavailablePlatforms, ...availablePlatforms])];
2205
- if (historyLib) {
2206
- registry.updateLibrary(libKey, { unavailablePlatforms: newUnavailable });
2207
- }
2208
- else {
2209
- registry.addLibrary({
2210
- libName: dep.libName,
2211
- commit: dep.commit,
2212
- branch: dep.branch,
2213
- url: dep.url,
2214
- platforms: [],
2215
- size: 0,
2216
- referencedBy: [],
2217
- unavailablePlatforms: newUnavailable,
2218
- createdAt: new Date().toISOString(),
2219
- lastAccess: new Date().toISOString(),
2220
- });
2221
- }
2417
+ upsertLibraryAvailability(dep, {
2418
+ unavailablePlatforms: newUnavailable,
2419
+ isGeneral: false,
2420
+ });
2222
2421
  warn(`${indent} ${dep.libName} - 下载成功但平台 [${availablePlatforms.join(', ')}] 不可用,已记录`);
2223
2422
  }
2224
2423
  // 清理临时目录