@revoengine/cli 1.0.8 → 1.0.10
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.
- package/README.md +181 -4
- package/dist/src/cli.js +9 -1
- package/dist/src/client.d.ts +10 -0
- package/dist/src/client.js +157 -0
- package/dist/src/commands/component.js +607 -298
- package/dist/src/commands/env.d.ts +2 -0
- package/dist/src/commands/env.js +360 -0
- package/dist/src/commands/index.d.ts +2 -0
- package/dist/src/commands/index.js +2 -0
- package/dist/src/commands/metadata.d.ts +2 -0
- package/dist/src/commands/metadata.js +137 -0
- package/dist/src/commands/project.js +75 -1
- package/dist/src/component-lock.d.ts +28 -3
- package/dist/src/component-lock.js +92 -9
- package/dist/src/config.d.ts +12 -0
- package/dist/src/config.js +89 -4
- package/dist/src/env-sync.d.ts +86 -0
- package/dist/src/env-sync.js +331 -0
- package/dist/src/metadata-backfill.d.ts +46 -0
- package/dist/src/metadata-backfill.js +125 -0
- package/dist/src/project.d.ts +15 -9
- package/dist/src/project.js +89 -0
- package/dist/src/resource-metadata.d.ts +24 -0
- package/dist/src/resource-metadata.js +129 -0
- package/dist/src/tracked-resources.d.ts +7 -0
- package/dist/src/tracked-resources.js +37 -0
- package/dist/src/types.d.ts +3 -0
- package/dist/src/ui.js +12 -3
- package/package.json +2 -2
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
|
-
import { ApiError, PermissionDeniedError } from "../client.js";
|
|
5
|
-
import { hashComponentSource, hashStable, readComponentLock, sanitizeComponentSource, upsertComponentLockEntry, } from "../component-lock.js";
|
|
6
|
-
import { buildSandboxDebugStreamUrl, buildSandboxDebugUrl, extractSandboxEndpoint, resolveProjectRoot, resolveProjectWorkspace, } from "../project.js";
|
|
4
|
+
import { ApiError, COMPONENT_LIST_PAGE_SIZE, PermissionDeniedError } from "../client.js";
|
|
5
|
+
import { REVO_LOCK_FILE, componentLockMatchesIdentity, getComponentLockEntry, hashComponentSource, hashPortableComponentSource, hashStable, readComponentLock, sanitizeComponentSource, upsertComponentLockEntry, writeComponentLock, } from "../component-lock.js";
|
|
6
|
+
import { buildSandboxDebugStreamUrl, buildSandboxDebugUrl, extractSandboxEndpoint, readProjectComponentIdentity, readProjectMetadata, resolveProjectRoot, resolveProjectWorkspace, } from "../project.js";
|
|
7
7
|
import { isInteractiveTerminal, promptConfirm } from "../prompt.js";
|
|
8
|
+
import { readComponentIdentityValue, } from "../resource-metadata.js";
|
|
8
9
|
import { deepClone, readBoolFlag, readFlag, readValues, sanitizeSegment, writeJsonFile } from "../utils.js";
|
|
9
10
|
const NULL_CATEGORY_FOLDER = '__no_category__';
|
|
10
|
-
const COMPONENT_LIST_PAGE_SIZE = 200;
|
|
11
|
-
const ACTIVE_COMPONENT_LIST_FILTER = {
|
|
12
|
-
'filter[and][0][field]': 'deletedAt',
|
|
13
|
-
'filter[and][0][op]': 'isNull',
|
|
14
|
-
};
|
|
15
11
|
const ANSI = {
|
|
16
12
|
reset: '\u001b[0m',
|
|
17
13
|
bold: '\u001b[1m',
|
|
@@ -120,27 +116,6 @@ function readWorkspaceComponentSafe(manifestPath) {
|
|
|
120
116
|
return null;
|
|
121
117
|
}
|
|
122
118
|
}
|
|
123
|
-
function findLocalManifestInfo(cwd, componentId) {
|
|
124
|
-
const manifestPath = findComponentManifestsById(cwd, componentId)[0];
|
|
125
|
-
if (!manifestPath) {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
const manifest = readWorkspaceManifest(manifestPath);
|
|
130
|
-
return {
|
|
131
|
-
manifestPath,
|
|
132
|
-
targetPath: componentWorkspacePath(cwd, manifestPath),
|
|
133
|
-
version: typeof manifest.version === 'number' ? manifest.version : null,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
return {
|
|
138
|
-
manifestPath,
|
|
139
|
-
targetPath: componentWorkspacePath(cwd, manifestPath),
|
|
140
|
-
version: null,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
119
|
function isNotModifiedError(error) {
|
|
145
120
|
if (!(error instanceof ApiError) || error.status !== 400) {
|
|
146
121
|
return false;
|
|
@@ -198,7 +173,28 @@ function getWorkspaceRoot(cwd) {
|
|
|
198
173
|
return path.join(projectWorkspace || cwd, 'Components');
|
|
199
174
|
}
|
|
200
175
|
function getProjectRoot(cwd) {
|
|
201
|
-
|
|
176
|
+
const configuredRoot = resolveProjectRoot(cwd);
|
|
177
|
+
if (configuredRoot) {
|
|
178
|
+
return configuredRoot;
|
|
179
|
+
}
|
|
180
|
+
let currentDir = path.resolve(cwd);
|
|
181
|
+
const { root } = path.parse(currentDir);
|
|
182
|
+
while (true) {
|
|
183
|
+
if (fs.existsSync(path.join(currentDir, REVO_LOCK_FILE))) {
|
|
184
|
+
return currentDir;
|
|
185
|
+
}
|
|
186
|
+
if (currentDir === root) {
|
|
187
|
+
return cwd;
|
|
188
|
+
}
|
|
189
|
+
currentDir = path.dirname(currentDir);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function resolveComponentIdentity(cwd) {
|
|
193
|
+
const metadata = readProjectMetadata(cwd);
|
|
194
|
+
if (metadata) {
|
|
195
|
+
return readProjectComponentIdentity(cwd);
|
|
196
|
+
}
|
|
197
|
+
return readComponentLock(getProjectRoot(cwd)).componentIdentity;
|
|
202
198
|
}
|
|
203
199
|
function getCategoryFolder(component) {
|
|
204
200
|
if (component.category == null || component.category === '') {
|
|
@@ -298,6 +294,18 @@ function findComponentManifestsById(cwd, componentId) {
|
|
|
298
294
|
return getComponentIdFromPath(path.dirname(manifestPath)).includes(componentId);
|
|
299
295
|
});
|
|
300
296
|
}
|
|
297
|
+
function findComponentManifestsByTarget(cwd, target) {
|
|
298
|
+
const identity = resolveComponentIdentity(cwd);
|
|
299
|
+
const matches = findComponentManifestsById(cwd, target);
|
|
300
|
+
if (identity.mode !== 'stableKey') {
|
|
301
|
+
return matches;
|
|
302
|
+
}
|
|
303
|
+
const stableMatches = getComponentManifestPaths(getWorkspaceRoot(cwd)).filter((manifestPath) => {
|
|
304
|
+
const component = readWorkspaceComponentSafe(manifestPath);
|
|
305
|
+
return component && readComponentIdentityValue(component, identity) === target;
|
|
306
|
+
});
|
|
307
|
+
return [...new Set([...stableMatches, ...matches])];
|
|
308
|
+
}
|
|
301
309
|
function isRelativeInside(parent, child) {
|
|
302
310
|
const relative = path.relative(parent, child);
|
|
303
311
|
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
@@ -392,79 +400,6 @@ function unwrapList(value) {
|
|
|
392
400
|
function isRecord(value) {
|
|
393
401
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
394
402
|
}
|
|
395
|
-
function readNumber(value) {
|
|
396
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
397
|
-
}
|
|
398
|
-
function resolveNextComponentListRequest(value) {
|
|
399
|
-
if (typeof value === 'string' && value) {
|
|
400
|
-
return {
|
|
401
|
-
path: value,
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
if (!isRecord(value)) {
|
|
405
|
-
return null;
|
|
406
|
-
}
|
|
407
|
-
for (const key of ['path', 'url', 'href']) {
|
|
408
|
-
if (typeof value[key] === 'string' && value[key]) {
|
|
409
|
-
return {
|
|
410
|
-
path: value[key],
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
const query = {};
|
|
415
|
-
for (const key of ['cursor', 'page', 'skip', 'take', 'limit', 'offset']) {
|
|
416
|
-
const candidate = value[key];
|
|
417
|
-
if (typeof candidate === 'string' || typeof candidate === 'number') {
|
|
418
|
-
query[key] = candidate;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return Object.keys(query).length > 0 ? { query } : null;
|
|
422
|
-
}
|
|
423
|
-
function buildActiveComponentListQuery(skip, take = COMPONENT_LIST_PAGE_SIZE) {
|
|
424
|
-
return {
|
|
425
|
-
take,
|
|
426
|
-
skip,
|
|
427
|
-
count: true,
|
|
428
|
-
...ACTIVE_COMPONENT_LIST_FILTER,
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
function withActiveComponentListFilter(request) {
|
|
432
|
-
if (!request.query) {
|
|
433
|
-
return request;
|
|
434
|
-
}
|
|
435
|
-
return {
|
|
436
|
-
...request,
|
|
437
|
-
query: {
|
|
438
|
-
...request.query,
|
|
439
|
-
count: request.query.count ?? true,
|
|
440
|
-
...ACTIVE_COMPONENT_LIST_FILTER,
|
|
441
|
-
},
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
function unwrapComponentListPage(value) {
|
|
445
|
-
const items = unwrapList(value);
|
|
446
|
-
if (!isRecord(value)) {
|
|
447
|
-
return {
|
|
448
|
-
items,
|
|
449
|
-
total: null,
|
|
450
|
-
nextRequest: null,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
const meta = isRecord(value.meta) ? value.meta : null;
|
|
454
|
-
const pagination = isRecord(value.pagination) ? value.pagination : null;
|
|
455
|
-
const total = readNumber(value.total)
|
|
456
|
-
?? readNumber(value.count)
|
|
457
|
-
?? (meta ? readNumber(meta.total) ?? readNumber(meta.count) : null)
|
|
458
|
-
?? (pagination ? readNumber(pagination.total) ?? readNumber(pagination.count) : null);
|
|
459
|
-
const nextRequest = resolveNextComponentListRequest(value.next)
|
|
460
|
-
?? (meta ? resolveNextComponentListRequest(meta.next) : null)
|
|
461
|
-
?? (pagination ? resolveNextComponentListRequest(pagination.next) : null);
|
|
462
|
-
return {
|
|
463
|
-
items,
|
|
464
|
-
total,
|
|
465
|
-
nextRequest,
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
403
|
function unwrapComponent(value) {
|
|
469
404
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
470
405
|
const candidate = value;
|
|
@@ -478,11 +413,12 @@ function normalizeValue(value, fallback = null) {
|
|
|
478
413
|
return value ?? fallback;
|
|
479
414
|
}
|
|
480
415
|
function normalizeElementContract(element) {
|
|
416
|
+
const order = Number.isFinite(Number(element.order)) ? Number(element.order) : 0;
|
|
481
417
|
return {
|
|
482
|
-
key: element.key,
|
|
418
|
+
key: typeof element.key === 'string' ? element.key : '',
|
|
483
419
|
desc: normalizeValue(element.desc),
|
|
484
420
|
hidden: Boolean(element.hidden),
|
|
485
|
-
order
|
|
421
|
+
order,
|
|
486
422
|
details: element.details ?? '',
|
|
487
423
|
};
|
|
488
424
|
}
|
|
@@ -508,92 +444,317 @@ function componentIdOf(component) {
|
|
|
508
444
|
function componentTargetPath(cwd, component) {
|
|
509
445
|
return path.relative(cwd, path.join(getWorkspaceRoot(cwd), getComponentFolder(component)));
|
|
510
446
|
}
|
|
511
|
-
function
|
|
512
|
-
|
|
513
|
-
|
|
447
|
+
function componentSourceHash(component, identity) {
|
|
448
|
+
return identity.mode === 'stableKey'
|
|
449
|
+
? hashPortableComponentSource(component)
|
|
450
|
+
: hashComponentSource(component);
|
|
451
|
+
}
|
|
452
|
+
function identityDescription(identity) {
|
|
453
|
+
return identity.mode === 'stableKey'
|
|
454
|
+
? `metadata.${identity.metadataProperty}`
|
|
455
|
+
: 'componentId';
|
|
456
|
+
}
|
|
457
|
+
function addCatalogEntry(catalog, entry, source) {
|
|
458
|
+
const componentId = componentIdOf(entry.component);
|
|
459
|
+
if (componentId) {
|
|
460
|
+
const duplicateId = catalog.byComponentId.get(componentId);
|
|
461
|
+
if (duplicateId && duplicateId.manifestPath !== entry.manifestPath) {
|
|
462
|
+
throw new Error(`Duplicate ${source} componentId "${componentId}".`);
|
|
463
|
+
}
|
|
464
|
+
catalog.byComponentId.set(componentId, entry);
|
|
465
|
+
}
|
|
466
|
+
const identityKey = readComponentIdentityValue(entry.component, catalog.identity);
|
|
467
|
+
if (!identityKey) {
|
|
468
|
+
catalog.unmanaged.push(entry);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const duplicate = catalog.byIdentity.get(identityKey);
|
|
472
|
+
if (duplicate && duplicate.component !== entry.component) {
|
|
473
|
+
const duplicateIds = [componentIdOf(duplicate.component), componentId]
|
|
474
|
+
.filter(Boolean)
|
|
475
|
+
.join(', ');
|
|
476
|
+
throw new Error(`Duplicate ${source} ${identityDescription(catalog.identity)} "${identityKey}"${duplicateIds ? ` (${duplicateIds})` : ''}.`);
|
|
477
|
+
}
|
|
478
|
+
catalog.byIdentity.set(identityKey, entry);
|
|
479
|
+
}
|
|
480
|
+
function createComponentCatalog(identity) {
|
|
481
|
+
return {
|
|
482
|
+
identity,
|
|
483
|
+
byIdentity: new Map(),
|
|
484
|
+
byComponentId: new Map(),
|
|
485
|
+
unmanaged: [],
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function buildLocalComponentCatalog(cwd, identity) {
|
|
489
|
+
const catalog = createComponentCatalog(identity);
|
|
490
|
+
for (const manifestPath of getComponentManifestPaths(getWorkspaceRoot(cwd))) {
|
|
491
|
+
const component = readWorkspaceComponentSafe(manifestPath);
|
|
492
|
+
if (!component) {
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
addCatalogEntry(catalog, {
|
|
496
|
+
component,
|
|
497
|
+
hash: componentSourceHash(component, identity),
|
|
498
|
+
manifestPath,
|
|
499
|
+
}, 'local');
|
|
500
|
+
}
|
|
501
|
+
return catalog;
|
|
502
|
+
}
|
|
503
|
+
async function buildRemoteComponentCatalog(context, identity, summaries) {
|
|
504
|
+
const catalog = createComponentCatalog(identity);
|
|
505
|
+
const source = summaries || (await fetchAllComponentSummaries(context)).components;
|
|
506
|
+
for (const summary of source) {
|
|
507
|
+
const componentId = String(summary.componentId || summary.id || '');
|
|
508
|
+
if (!componentId) {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
const component = unwrapComponent(await context.client.getComponent(componentId));
|
|
512
|
+
addCatalogEntry(catalog, {
|
|
513
|
+
component,
|
|
514
|
+
hash: componentSourceHash(component, identity),
|
|
515
|
+
}, 'remote');
|
|
516
|
+
}
|
|
517
|
+
return catalog;
|
|
518
|
+
}
|
|
519
|
+
function migrateComponentLockIdentity(cwd, identity, localCatalog, remoteCatalog) {
|
|
520
|
+
const projectRoot = getProjectRoot(cwd);
|
|
521
|
+
const lock = readComponentLock(projectRoot);
|
|
522
|
+
if (componentLockMatchesIdentity(lock, identity)) {
|
|
523
|
+
return lock;
|
|
524
|
+
}
|
|
525
|
+
const migratedComponents = {};
|
|
526
|
+
if (identity.mode === 'stableKey' && lock.componentIdentity.mode === 'componentId') {
|
|
527
|
+
const legacyEntries = Object.values(lock.components);
|
|
528
|
+
for (const [stableKey, remote] of remoteCatalog.byIdentity) {
|
|
529
|
+
const local = findLocalEntryForRemote(remote.component, localCatalog);
|
|
530
|
+
const localComponentId = local ? componentIdOf(local.component) : '';
|
|
531
|
+
const remoteComponentId = componentIdOf(remote.component);
|
|
532
|
+
const legacy = legacyEntries.find((entry) => (entry.componentId === localComponentId
|
|
533
|
+
|| entry.remoteComponentId === localComponentId
|
|
534
|
+
|| entry.componentId === remoteComponentId
|
|
535
|
+
|| entry.remoteComponentId === remoteComponentId));
|
|
536
|
+
if (!legacy || !remoteComponentId) {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
const remoteLegacyHash = hashComponentSource(remote.component);
|
|
540
|
+
const localLegacyHash = local ? hashComponentSource(local.component) : null;
|
|
541
|
+
let portableBaselineHash = null;
|
|
542
|
+
if (legacy.remoteHash === remoteLegacyHash) {
|
|
543
|
+
portableBaselineHash = remote.hash;
|
|
544
|
+
}
|
|
545
|
+
else if (local && legacy.remoteHash === localLegacyHash) {
|
|
546
|
+
portableBaselineHash = local.hash;
|
|
547
|
+
}
|
|
548
|
+
else if (local && local.hash === remote.hash) {
|
|
549
|
+
portableBaselineHash = remote.hash;
|
|
550
|
+
}
|
|
551
|
+
if (!portableBaselineHash) {
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
migratedComponents[stableKey] = {
|
|
555
|
+
identityMode: 'stableKey',
|
|
556
|
+
metadataProperty: identity.metadataProperty,
|
|
557
|
+
stableKey,
|
|
558
|
+
componentId: localComponentId || legacy.componentId || remoteComponentId,
|
|
559
|
+
remoteComponentId,
|
|
560
|
+
componentName: remote.component.name || local?.component.name || legacy.componentName,
|
|
561
|
+
category: normalizeValue(remote.component.category, normalizeValue(local?.component.category, legacy.category)),
|
|
562
|
+
path: local?.manifestPath
|
|
563
|
+
? componentWorkspacePath(cwd, local.manifestPath)
|
|
564
|
+
: legacy.path || componentTargetPath(cwd, remote.component),
|
|
565
|
+
remoteVersion: legacy.remoteVersion,
|
|
566
|
+
remoteHash: portableBaselineHash,
|
|
567
|
+
sourceHash: local?.hash || portableBaselineHash,
|
|
568
|
+
pulledAt: legacy.pulledAt,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
const migrated = {
|
|
573
|
+
schemaVersion: 2,
|
|
574
|
+
componentIdentity: identity,
|
|
575
|
+
components: migratedComponents,
|
|
576
|
+
};
|
|
577
|
+
writeComponentLock(projectRoot, migrated);
|
|
578
|
+
return migrated;
|
|
579
|
+
}
|
|
580
|
+
function findLocalEntryForRemote(remote, localCatalog) {
|
|
581
|
+
const identityKey = readComponentIdentityValue(remote, localCatalog.identity);
|
|
582
|
+
if (!identityKey) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
const identityMatch = localCatalog.byIdentity.get(identityKey);
|
|
586
|
+
if (identityMatch) {
|
|
587
|
+
return identityMatch;
|
|
588
|
+
}
|
|
589
|
+
if (localCatalog.identity.mode !== 'stableKey') {
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
const remoteComponentId = componentIdOf(remote);
|
|
593
|
+
const idMatch = remoteComponentId
|
|
594
|
+
? localCatalog.byComponentId.get(remoteComponentId)
|
|
595
|
+
: null;
|
|
596
|
+
if (!idMatch) {
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
const localIdentityKey = readComponentIdentityValue(idMatch.component, localCatalog.identity);
|
|
600
|
+
if (localIdentityKey && localIdentityKey !== identityKey) {
|
|
601
|
+
throw new Error(`Cannot match component ${remoteComponentId}: local ${identityDescription(localCatalog.identity)} "${localIdentityKey}" does not match remote "${identityKey}".`);
|
|
602
|
+
}
|
|
603
|
+
return idMatch;
|
|
604
|
+
}
|
|
605
|
+
function upsertLockFromRemote(cwd, remote, targetPath, local) {
|
|
606
|
+
const identity = resolveComponentIdentity(cwd);
|
|
607
|
+
const remoteComponentId = componentIdOf(remote);
|
|
608
|
+
const localComponentId = componentIdOf(local || remote);
|
|
609
|
+
const identityKey = readComponentIdentityValue(remote, identity);
|
|
610
|
+
if (!remoteComponentId || !localComponentId || !identityKey) {
|
|
514
611
|
return;
|
|
515
612
|
}
|
|
516
|
-
const remoteHash =
|
|
613
|
+
const remoteHash = componentSourceHash(remote, identity);
|
|
517
614
|
upsertComponentLockEntry(getProjectRoot(cwd), {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
615
|
+
identityMode: identity.mode,
|
|
616
|
+
...(identity.mode === 'stableKey'
|
|
617
|
+
? {
|
|
618
|
+
metadataProperty: identity.metadataProperty,
|
|
619
|
+
stableKey: identityKey,
|
|
620
|
+
}
|
|
621
|
+
: {}),
|
|
622
|
+
componentId: localComponentId,
|
|
623
|
+
remoteComponentId,
|
|
624
|
+
componentName: remote.name || local?.name || '',
|
|
625
|
+
category: normalizeValue(remote.category, normalizeValue(local?.category)),
|
|
626
|
+
path: targetPath || componentTargetPath(cwd, local || remote),
|
|
522
627
|
remoteVersion: componentVersion(remote),
|
|
523
628
|
remoteHash,
|
|
524
|
-
sourceHash: remoteHash,
|
|
629
|
+
sourceHash: local ? componentSourceHash(local, identity) : remoteHash,
|
|
525
630
|
pulledAt: new Date().toISOString(),
|
|
526
|
-
});
|
|
631
|
+
}, identity);
|
|
527
632
|
}
|
|
528
|
-
function buildPullDecision(cwd, remote) {
|
|
633
|
+
function buildPullDecision(cwd, remote, localCatalog = buildLocalComponentCatalog(cwd, resolveComponentIdentity(cwd))) {
|
|
634
|
+
const identity = localCatalog.identity;
|
|
529
635
|
const remoteTargetPath = componentTargetPath(cwd, remote);
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
636
|
+
const identityKey = readComponentIdentityValue(remote, identity);
|
|
637
|
+
if (!identityKey) {
|
|
638
|
+
return {
|
|
639
|
+
kind: 'skip',
|
|
640
|
+
targetPath: remoteTargetPath,
|
|
641
|
+
reason: `missing ${identityDescription(identity)}; unmanaged`,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
const local = findLocalEntryForRemote(remote, localCatalog);
|
|
645
|
+
if (!local?.manifestPath) {
|
|
533
646
|
return {
|
|
534
647
|
kind: 'pull',
|
|
535
648
|
targetPath: remoteTargetPath,
|
|
536
649
|
};
|
|
537
650
|
}
|
|
538
|
-
const
|
|
539
|
-
|
|
651
|
+
const targetPath = componentWorkspacePath(cwd, local.manifestPath);
|
|
652
|
+
const lockEntry = getComponentLockEntry(readComponentLock(getProjectRoot(cwd)), identity, remote);
|
|
653
|
+
const localHash = local.hash;
|
|
654
|
+
const remoteHash = componentSourceHash(remote, identity);
|
|
655
|
+
const base = {
|
|
656
|
+
targetPath,
|
|
657
|
+
manifestPath: local.manifestPath,
|
|
658
|
+
localComponent: local.component,
|
|
659
|
+
};
|
|
660
|
+
if (localHash === remoteHash) {
|
|
540
661
|
return {
|
|
541
662
|
kind: 'skip',
|
|
542
|
-
|
|
543
|
-
reason: '
|
|
663
|
+
...base,
|
|
664
|
+
reason: 'no changes',
|
|
544
665
|
};
|
|
545
666
|
}
|
|
546
|
-
const lockEntry = readComponentLock(getProjectRoot(cwd)).components[componentId];
|
|
547
|
-
const localHash = hashComponentSource(localComponent);
|
|
548
|
-
const remoteHash = hashComponentSource(remote);
|
|
549
667
|
if (!lockEntry) {
|
|
550
|
-
if (localHash === remoteHash) {
|
|
551
|
-
return {
|
|
552
|
-
kind: 'skip',
|
|
553
|
-
targetPath: localInfo.targetPath || remoteTargetPath,
|
|
554
|
-
reason: 'no changes',
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
668
|
return {
|
|
558
669
|
kind: 'skip',
|
|
559
|
-
|
|
670
|
+
...base,
|
|
560
671
|
reason: 'missing lock',
|
|
561
672
|
};
|
|
562
673
|
}
|
|
563
674
|
if (localHash === lockEntry.remoteHash && remoteHash !== lockEntry.remoteHash) {
|
|
564
675
|
return {
|
|
565
676
|
kind: 'pull',
|
|
566
|
-
|
|
677
|
+
...base,
|
|
567
678
|
reason: 'remote changed',
|
|
568
679
|
};
|
|
569
680
|
}
|
|
570
681
|
if (localHash !== lockEntry.remoteHash && remoteHash === lockEntry.remoteHash) {
|
|
571
682
|
return {
|
|
572
683
|
kind: 'skip',
|
|
573
|
-
|
|
684
|
+
...base,
|
|
574
685
|
reason: 'local changes',
|
|
575
686
|
};
|
|
576
687
|
}
|
|
577
688
|
if (localHash !== lockEntry.remoteHash && remoteHash !== lockEntry.remoteHash && localHash !== remoteHash) {
|
|
578
689
|
return {
|
|
579
690
|
kind: 'skip',
|
|
580
|
-
|
|
691
|
+
...base,
|
|
581
692
|
reason: 'conflict',
|
|
582
693
|
};
|
|
583
694
|
}
|
|
584
|
-
if (isSameComponentContract(
|
|
695
|
+
if (identity.mode === 'componentId' && isSameComponentContract(local.component, remote)) {
|
|
585
696
|
return {
|
|
586
697
|
kind: 'skip',
|
|
587
|
-
|
|
698
|
+
...base,
|
|
588
699
|
reason: 'no changes',
|
|
589
700
|
};
|
|
590
701
|
}
|
|
591
702
|
return {
|
|
592
703
|
kind: 'skip',
|
|
593
|
-
|
|
704
|
+
...base,
|
|
594
705
|
reason: 'changed',
|
|
595
706
|
};
|
|
596
707
|
}
|
|
708
|
+
function syncLocalIdentityMetadata(manifestPath, remote, identity) {
|
|
709
|
+
if (identity.mode !== 'stableKey' || !manifestPath) {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
const remoteStableKey = readComponentIdentityValue(remote, identity);
|
|
713
|
+
if (!remoteStableKey) {
|
|
714
|
+
return false;
|
|
715
|
+
}
|
|
716
|
+
let manifest;
|
|
717
|
+
try {
|
|
718
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
719
|
+
}
|
|
720
|
+
catch {
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
const localStableKey = readComponentIdentityValue(manifest, identity);
|
|
724
|
+
if (localStableKey) {
|
|
725
|
+
if (localStableKey !== remoteStableKey) {
|
|
726
|
+
throw new Error(`Cannot pull ${componentIdOf(remote)}: local ${identityDescription(identity)} "${localStableKey}" does not match remote "${remoteStableKey}".`);
|
|
727
|
+
}
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
const metadata = isRecord(manifest.metadata) ? deepClone(manifest.metadata) : {};
|
|
731
|
+
manifest.metadata = {
|
|
732
|
+
...metadata,
|
|
733
|
+
[identity.metadataProperty]: remoteStableKey,
|
|
734
|
+
};
|
|
735
|
+
writeJsonFile(manifestPath, manifest);
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
function buildPulledManifest(remote, local, identity) {
|
|
739
|
+
const manifest = stripDetails(remote);
|
|
740
|
+
if (identity.mode !== 'stableKey' || !local) {
|
|
741
|
+
return manifest;
|
|
742
|
+
}
|
|
743
|
+
const localComponentId = componentIdOf(local);
|
|
744
|
+
if (localComponentId) {
|
|
745
|
+
manifest.componentId = localComponentId;
|
|
746
|
+
delete manifest.id;
|
|
747
|
+
}
|
|
748
|
+
const localElementsByKey = new Map((local.elements || []).map((element) => [element.key, element]));
|
|
749
|
+
manifest.elements = (manifest.elements || []).map((element) => {
|
|
750
|
+
const localElementId = localElementsByKey.get(element.key)?.componentElementId;
|
|
751
|
+
return {
|
|
752
|
+
...element,
|
|
753
|
+
...(localElementId ? { componentElementId: localElementId } : {}),
|
|
754
|
+
};
|
|
755
|
+
});
|
|
756
|
+
return manifest;
|
|
757
|
+
}
|
|
597
758
|
async function confirmSingleStalePull(context, targetPath) {
|
|
598
759
|
if (!isInteractiveTerminal()) {
|
|
599
760
|
throw new Error(`Local component is stale at ${targetPath}. Re-run with --stale or --force, or use an interactive terminal to confirm overwrite.`);
|
|
@@ -601,20 +762,60 @@ async function confirmSingleStalePull(context, targetPath) {
|
|
|
601
762
|
printNotice(context.println, `Local component is stale at ${targetPath}.`);
|
|
602
763
|
return promptConfirm('Overwrite local component with the remote version?', false);
|
|
603
764
|
}
|
|
604
|
-
async function
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
765
|
+
async function resolveRemoteComponentForTarget(context, target, identity, remoteCatalog) {
|
|
766
|
+
const localManifestPath = findComponentManifestsById(context.cwd, target)[0];
|
|
767
|
+
const local = localManifestPath ? readWorkspaceComponentSafe(localManifestPath) : null;
|
|
768
|
+
const localIdentityKey = local ? readComponentIdentityValue(local, identity) : null;
|
|
769
|
+
let mismatchedDirectIdentity = null;
|
|
770
|
+
if (identity.mode === 'componentId' && !remoteCatalog) {
|
|
771
|
+
try {
|
|
772
|
+
return unwrapComponent(await context.client.getComponent(target));
|
|
773
|
+
}
|
|
774
|
+
catch (error) {
|
|
775
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
rethrowComponentAccessError(error, 'pull');
|
|
779
|
+
}
|
|
609
780
|
}
|
|
610
|
-
|
|
611
|
-
|
|
781
|
+
if (!remoteCatalog) {
|
|
782
|
+
try {
|
|
783
|
+
const direct = unwrapComponent(await context.client.getComponent(target));
|
|
784
|
+
const directIdentityKey = direct ? readComponentIdentityValue(direct, identity) : null;
|
|
785
|
+
if (directIdentityKey && (!localIdentityKey || localIdentityKey === directIdentityKey)) {
|
|
786
|
+
return direct;
|
|
787
|
+
}
|
|
788
|
+
if (directIdentityKey && localIdentityKey && localIdentityKey !== directIdentityKey) {
|
|
789
|
+
mismatchedDirectIdentity = directIdentityKey;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
catch (error) {
|
|
793
|
+
if (!(error instanceof ApiError && error.status === 404)) {
|
|
794
|
+
rethrowComponentAccessError(error, 'pull');
|
|
795
|
+
}
|
|
796
|
+
}
|
|
612
797
|
}
|
|
613
|
-
|
|
798
|
+
const catalog = remoteCatalog || await buildRemoteComponentCatalog(context, identity);
|
|
799
|
+
const resolved = localIdentityKey
|
|
800
|
+
? catalog.byIdentity.get(localIdentityKey)?.component || null
|
|
801
|
+
: catalog.byIdentity.get(target)?.component
|
|
802
|
+
|| catalog.byComponentId.get(target)?.component
|
|
803
|
+
|| null;
|
|
804
|
+
if (!resolved && mismatchedDirectIdentity && localIdentityKey) {
|
|
805
|
+
throw new Error(`Cannot match component ${target}: local ${identityDescription(identity)} "${localIdentityKey}" does not match remote "${mismatchedDirectIdentity}".`);
|
|
806
|
+
}
|
|
807
|
+
return resolved;
|
|
808
|
+
}
|
|
809
|
+
async function pullResolvedComponent(context, component, mode, localCatalog) {
|
|
810
|
+
const { cwd, println } = context;
|
|
811
|
+
const identity = resolveComponentIdentity(cwd);
|
|
812
|
+
const componentId = componentIdOf(component);
|
|
813
|
+
const decision = buildPullDecision(cwd, component, localCatalog || buildLocalComponentCatalog(cwd, identity));
|
|
814
|
+
if (decision.kind === 'skip' && decision.reason.includes('; unmanaged')) {
|
|
614
815
|
const result = {
|
|
615
816
|
status: 'skipped',
|
|
616
|
-
targetPath:
|
|
617
|
-
reason:
|
|
817
|
+
targetPath: decision.targetPath,
|
|
818
|
+
reason: decision.reason,
|
|
618
819
|
};
|
|
619
820
|
printSyncStatus(println, {
|
|
620
821
|
status: 'Skipped',
|
|
@@ -624,7 +825,6 @@ async function pullSingleComponent(context, componentId, mode = { force: false,
|
|
|
624
825
|
});
|
|
625
826
|
return result;
|
|
626
827
|
}
|
|
627
|
-
const decision = buildPullDecision(cwd, component);
|
|
628
828
|
if (decision.kind === 'skip' && !mode.force) {
|
|
629
829
|
if (decision.reason === 'stale version') {
|
|
630
830
|
if (mode.stale) {
|
|
@@ -666,16 +866,21 @@ async function pullSingleComponent(context, componentId, mode = { force: false,
|
|
|
666
866
|
if (decision.reason === 'conflict' || decision.reason === 'missing lock') {
|
|
667
867
|
throw new Error(`Cannot pull ${componentId}: ${decision.reason} at ${decision.targetPath}. Re-run with --force only if you want to overwrite local files.`);
|
|
668
868
|
}
|
|
869
|
+
let metadataUpdated = false;
|
|
669
870
|
if (decision.reason === 'no changes') {
|
|
670
|
-
|
|
871
|
+
metadataUpdated = syncLocalIdentityMetadata(decision.manifestPath, component, identity);
|
|
872
|
+
const localComponent = decision.manifestPath
|
|
873
|
+
? readWorkspaceComponentSafe(decision.manifestPath) || decision.localComponent
|
|
874
|
+
: decision.localComponent;
|
|
875
|
+
upsertLockFromRemote(cwd, component, decision.targetPath, localComponent);
|
|
671
876
|
}
|
|
672
877
|
const result = {
|
|
673
|
-
status: 'skipped',
|
|
878
|
+
status: metadataUpdated ? 'pulled' : 'skipped',
|
|
674
879
|
targetPath: decision.targetPath,
|
|
675
|
-
reason: decision.reason,
|
|
880
|
+
reason: metadataUpdated ? 'stable-key metadata' : decision.reason,
|
|
676
881
|
};
|
|
677
882
|
printSyncStatus(println, {
|
|
678
|
-
status: 'Skipped',
|
|
883
|
+
status: metadataUpdated ? 'Pulled' : 'Skipped',
|
|
679
884
|
direction: 'pull',
|
|
680
885
|
targetPath: result.targetPath,
|
|
681
886
|
reason: result.reason,
|
|
@@ -684,7 +889,9 @@ async function pullSingleComponent(context, componentId, mode = { force: false,
|
|
|
684
889
|
}
|
|
685
890
|
}
|
|
686
891
|
const root = getWorkspaceRoot(cwd);
|
|
687
|
-
const folder =
|
|
892
|
+
const folder = decision.manifestPath
|
|
893
|
+
? path.dirname(decision.manifestPath)
|
|
894
|
+
: path.join(root, getComponentFolder(component));
|
|
688
895
|
const targetPath = path.relative(cwd, folder);
|
|
689
896
|
const elementsDir = path.join(folder, 'elements');
|
|
690
897
|
fs.mkdirSync(elementsDir, { recursive: true });
|
|
@@ -694,8 +901,10 @@ async function pullSingleComponent(context, componentId, mode = { force: false,
|
|
|
694
901
|
const filePath = path.join(elementsDir, `${element.order}_${element.key}.${extension}`);
|
|
695
902
|
fs.writeFileSync(filePath, content);
|
|
696
903
|
}
|
|
697
|
-
|
|
698
|
-
|
|
904
|
+
const manifestPath = path.join(folder, 'component.json');
|
|
905
|
+
writeJsonFile(manifestPath, buildPulledManifest(component, decision.localComponent, identity));
|
|
906
|
+
const localComponent = readWorkspaceComponentSafe(manifestPath) || decision.localComponent;
|
|
907
|
+
upsertLockFromRemote(cwd, component, targetPath, localComponent);
|
|
699
908
|
const result = {
|
|
700
909
|
status: 'pulled',
|
|
701
910
|
targetPath,
|
|
@@ -711,58 +920,40 @@ async function pullSingleComponent(context, componentId, mode = { force: false,
|
|
|
711
920
|
});
|
|
712
921
|
return result;
|
|
713
922
|
}
|
|
923
|
+
async function pullSingleComponent(context, target, mode = { force: false, stale: false }) {
|
|
924
|
+
const identity = resolveComponentIdentity(context.cwd);
|
|
925
|
+
let remoteCatalog;
|
|
926
|
+
let localCatalog;
|
|
927
|
+
if (identity.mode === 'stableKey'
|
|
928
|
+
&& !componentLockMatchesIdentity(readComponentLock(getProjectRoot(context.cwd)), identity)) {
|
|
929
|
+
remoteCatalog = await buildRemoteComponentCatalog(context, identity);
|
|
930
|
+
localCatalog = buildLocalComponentCatalog(context.cwd, identity);
|
|
931
|
+
migrateComponentLockIdentity(context.cwd, identity, localCatalog, remoteCatalog);
|
|
932
|
+
}
|
|
933
|
+
const component = await resolveRemoteComponentForTarget(context, target, identity, remoteCatalog);
|
|
934
|
+
if (!component) {
|
|
935
|
+
const result = {
|
|
936
|
+
status: 'skipped',
|
|
937
|
+
targetPath: target,
|
|
938
|
+
reason: 'component not found',
|
|
939
|
+
};
|
|
940
|
+
printSyncStatus(context.println, {
|
|
941
|
+
status: 'Skipped',
|
|
942
|
+
direction: 'pull',
|
|
943
|
+
targetPath: result.targetPath,
|
|
944
|
+
reason: result.reason,
|
|
945
|
+
});
|
|
946
|
+
return result;
|
|
947
|
+
}
|
|
948
|
+
return pullResolvedComponent(context, component, mode, localCatalog);
|
|
949
|
+
}
|
|
714
950
|
async function fetchAllComponentSummaries(context) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const seenRequests = new Set();
|
|
718
|
-
let nextRequest = {
|
|
719
|
-
query: buildActiveComponentListQuery(0),
|
|
720
|
-
};
|
|
721
|
-
let discoveredTotal = null;
|
|
722
|
-
while (nextRequest) {
|
|
723
|
-
const requestKey = JSON.stringify(nextRequest);
|
|
724
|
-
if (seenRequests.has(requestKey)) {
|
|
725
|
-
throw new Error('Component list pagination loop detected while pulling all components.');
|
|
726
|
-
}
|
|
727
|
-
seenRequests.add(requestKey);
|
|
728
|
-
let pageValue;
|
|
729
|
-
try {
|
|
730
|
-
pageValue = await client.listComponents(nextRequest);
|
|
731
|
-
}
|
|
732
|
-
catch (error) {
|
|
733
|
-
rethrowComponentAccessError(error, 'list');
|
|
734
|
-
}
|
|
735
|
-
const page = unwrapComponentListPage(pageValue);
|
|
736
|
-
if (page.total !== null) {
|
|
737
|
-
discoveredTotal = page.total;
|
|
738
|
-
}
|
|
739
|
-
for (const item of page.items) {
|
|
740
|
-
if (isRecord(item)) {
|
|
741
|
-
components.push(item);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
if (page.total !== null && components.length >= page.total) {
|
|
745
|
-
nextRequest = null;
|
|
746
|
-
continue;
|
|
747
|
-
}
|
|
748
|
-
if (page.nextRequest) {
|
|
749
|
-
nextRequest = withActiveComponentListFilter(page.nextRequest);
|
|
750
|
-
continue;
|
|
751
|
-
}
|
|
752
|
-
if (nextRequest.query
|
|
753
|
-
&& typeof nextRequest.query.take === 'number'
|
|
754
|
-
&& page.items.length === nextRequest.query.take) {
|
|
755
|
-
nextRequest = {
|
|
756
|
-
query: buildActiveComponentListQuery(components.length, nextRequest.query.take),
|
|
757
|
-
};
|
|
758
|
-
continue;
|
|
759
|
-
}
|
|
760
|
-
nextRequest = null;
|
|
951
|
+
try {
|
|
952
|
+
return await context.client.listAllComponents();
|
|
761
953
|
}
|
|
762
|
-
|
|
763
|
-
|
|
954
|
+
catch (error) {
|
|
955
|
+
rethrowComponentAccessError(error, 'list');
|
|
764
956
|
}
|
|
765
|
-
return { components, discoveredTotal };
|
|
766
957
|
}
|
|
767
958
|
async function pullAllComponents(context, options) {
|
|
768
959
|
const { components, discoveredTotal } = await fetchAllComponentSummaries(context);
|
|
@@ -770,19 +961,26 @@ async function pullAllComponents(context, options) {
|
|
|
770
961
|
context.println('No components found.');
|
|
771
962
|
return [];
|
|
772
963
|
}
|
|
773
|
-
|
|
964
|
+
const identity = resolveComponentIdentity(context.cwd);
|
|
965
|
+
const remoteCatalog = await buildRemoteComponentCatalog(context, identity, components);
|
|
966
|
+
const managedCount = remoteCatalog.byIdentity.size;
|
|
967
|
+
if (managedCount === 0) {
|
|
968
|
+
context.println(`No components managed by ${identityDescription(identity)} were found.`);
|
|
969
|
+
return [];
|
|
970
|
+
}
|
|
971
|
+
await confirmBulkAction(context, 'pull', identity.mode === 'componentId'
|
|
972
|
+
? discoveredTotal ?? (components.length >= COMPONENT_LIST_PAGE_SIZE ? `at least ${components.length} components` : components.length)
|
|
973
|
+
: managedCount, options.force || options.yes);
|
|
774
974
|
const startedAt = Date.now();
|
|
775
975
|
const results = [];
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
}
|
|
781
|
-
const result = await pullSingleComponent(context, String(componentId), {
|
|
976
|
+
const localCatalog = buildLocalComponentCatalog(context.cwd, identity);
|
|
977
|
+
migrateComponentLockIdentity(context.cwd, identity, localCatalog, remoteCatalog);
|
|
978
|
+
for (const entry of remoteCatalog.byComponentId.values()) {
|
|
979
|
+
const result = await pullResolvedComponent(context, entry.component, {
|
|
782
980
|
force: options.force,
|
|
783
981
|
stale: options.stale,
|
|
784
982
|
promptOnStale: false,
|
|
785
|
-
});
|
|
983
|
+
}, localCatalog);
|
|
786
984
|
if (result) {
|
|
787
985
|
results.push(result);
|
|
788
986
|
}
|
|
@@ -790,39 +988,120 @@ async function pullAllComponents(context, options) {
|
|
|
790
988
|
printSummary(context.println, 'Pulled', results, Date.now() - startedAt);
|
|
791
989
|
return results;
|
|
792
990
|
}
|
|
793
|
-
async function
|
|
991
|
+
async function resolveRemoteComponentForLocal(context, component, identity, remoteCatalog) {
|
|
992
|
+
const localComponentId = componentIdOf(component);
|
|
993
|
+
if (identity.mode === 'componentId') {
|
|
994
|
+
if (!localComponentId) {
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
997
|
+
if (typeof context.client.getComponent !== 'function') {
|
|
998
|
+
return component;
|
|
999
|
+
}
|
|
1000
|
+
try {
|
|
1001
|
+
return unwrapComponent(await context.client.getComponent(localComponentId));
|
|
1002
|
+
}
|
|
1003
|
+
catch (error) {
|
|
1004
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
rethrowComponentAccessError(error, 'push');
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const identityKey = readComponentIdentityValue(component, identity);
|
|
1011
|
+
if (!identityKey) {
|
|
1012
|
+
return null;
|
|
1013
|
+
}
|
|
1014
|
+
const catalog = remoteCatalog || await buildRemoteComponentCatalog(context, identity);
|
|
1015
|
+
return catalog.byIdentity.get(identityKey)?.component || null;
|
|
1016
|
+
}
|
|
1017
|
+
function buildPushElements(component, remote, identity) {
|
|
1018
|
+
const remoteElementsByKey = new Map((remote.elements || []).map((element) => [element.key, element]));
|
|
1019
|
+
return (component.elements || []).map((element) => {
|
|
1020
|
+
const targetElementId = identity.mode === 'stableKey'
|
|
1021
|
+
? remoteElementsByKey.get(element.key)?.componentElementId
|
|
1022
|
+
: element.componentElementId;
|
|
1023
|
+
return {
|
|
1024
|
+
...(typeof targetElementId === 'string' && targetElementId
|
|
1025
|
+
? { componentElementId: targetElementId }
|
|
1026
|
+
: {}),
|
|
1027
|
+
key: element.key,
|
|
1028
|
+
desc: element.desc,
|
|
1029
|
+
details: element.details || '',
|
|
1030
|
+
hidden: Boolean(element.hidden),
|
|
1031
|
+
order: element.order,
|
|
1032
|
+
};
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
async function pushSingleComponent(context, manifestPath, remoteCatalog) {
|
|
794
1036
|
const { client, println } = context;
|
|
795
1037
|
const component = readWorkspaceComponent(manifestPath);
|
|
796
|
-
const
|
|
1038
|
+
const localComponentId = componentIdOf(component);
|
|
797
1039
|
const targetPath = componentWorkspacePath(context.cwd, manifestPath);
|
|
798
|
-
|
|
1040
|
+
const identity = resolveComponentIdentity(context.cwd);
|
|
1041
|
+
const identityKey = readComponentIdentityValue(component, identity);
|
|
1042
|
+
if (!localComponentId) {
|
|
799
1043
|
throw new Error(`Missing componentId in ${manifestPath}.`);
|
|
800
1044
|
}
|
|
801
1045
|
if (!component.name) {
|
|
802
1046
|
throw new Error(`Missing component name in ${manifestPath}.`);
|
|
803
1047
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1048
|
+
if (!identityKey) {
|
|
1049
|
+
const result = {
|
|
1050
|
+
status: 'skipped',
|
|
1051
|
+
targetPath,
|
|
1052
|
+
reason: `missing ${identityDescription(identity)}; unmanaged`,
|
|
1053
|
+
};
|
|
1054
|
+
printSyncStatus(println, {
|
|
1055
|
+
status: 'Skipped',
|
|
1056
|
+
direction: 'push',
|
|
1057
|
+
targetPath,
|
|
1058
|
+
reason: result.reason,
|
|
1059
|
+
});
|
|
1060
|
+
return result;
|
|
1061
|
+
}
|
|
1062
|
+
let resolvedRemoteCatalog = remoteCatalog;
|
|
1063
|
+
if (identity.mode === 'stableKey'
|
|
1064
|
+
&& !componentLockMatchesIdentity(readComponentLock(getProjectRoot(context.cwd)), identity)) {
|
|
1065
|
+
resolvedRemoteCatalog = resolvedRemoteCatalog || await buildRemoteComponentCatalog(context, identity);
|
|
1066
|
+
migrateComponentLockIdentity(context.cwd, identity, buildLocalComponentCatalog(context.cwd, identity), resolvedRemoteCatalog);
|
|
1067
|
+
}
|
|
814
1068
|
try {
|
|
1069
|
+
const remote = await resolveRemoteComponentForLocal(context, component, identity, resolvedRemoteCatalog);
|
|
1070
|
+
if (!remote) {
|
|
1071
|
+
const result = {
|
|
1072
|
+
status: 'skipped',
|
|
1073
|
+
targetPath,
|
|
1074
|
+
reason: "doesn't exist remotely",
|
|
1075
|
+
};
|
|
1076
|
+
printSyncStatus(println, {
|
|
1077
|
+
status: 'Skipped',
|
|
1078
|
+
direction: 'push',
|
|
1079
|
+
targetPath,
|
|
1080
|
+
reason: result.reason,
|
|
1081
|
+
});
|
|
1082
|
+
return result;
|
|
1083
|
+
}
|
|
1084
|
+
const remoteComponentId = componentIdOf(remote);
|
|
1085
|
+
if (!remoteComponentId) {
|
|
1086
|
+
throw new Error(`Remote component matched by ${identityDescription(identity)} "${identityKey}" has no componentId.`);
|
|
1087
|
+
}
|
|
815
1088
|
if (!readBoolFlag(context.args, ['force', 'f'])) {
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1089
|
+
const lockEntry = getComponentLockEntry(readComponentLock(getProjectRoot(context.cwd)), identity, component);
|
|
1090
|
+
if (!lockEntry) {
|
|
1091
|
+
throw new Error(`Cannot push ${targetPath}: missing lock entry. Run \`revo component pull ${identityKey} --force\` to establish a remote baseline.`);
|
|
819
1092
|
}
|
|
820
|
-
|
|
821
|
-
|
|
1093
|
+
if (lockEntry.remoteComponentId !== remoteComponentId) {
|
|
1094
|
+
throw new Error(`Cannot push ${targetPath}: ${identityDescription(identity)} "${identityKey}" now resolves to remote component ${remoteComponentId}, but the lock references ${lockEntry.remoteComponentId}. Pull/reconcile first.`);
|
|
1095
|
+
}
|
|
1096
|
+
const remoteHash = componentSourceHash(remote, identity);
|
|
1097
|
+
const localHash = componentSourceHash(component, identity);
|
|
1098
|
+
if (remoteHash !== lockEntry.remoteHash) {
|
|
1099
|
+
if (localHash === remoteHash) {
|
|
1100
|
+
upsertLockFromRemote(context.cwd, remote, targetPath, component);
|
|
822
1101
|
const result = {
|
|
823
1102
|
status: 'skipped',
|
|
824
1103
|
targetPath,
|
|
825
|
-
reason:
|
|
1104
|
+
reason: 'local matches remote; lock refreshed',
|
|
826
1105
|
};
|
|
827
1106
|
printSyncStatus(println, {
|
|
828
1107
|
status: 'Skipped',
|
|
@@ -832,17 +1111,8 @@ async function pushSingleComponent(context, manifestPath) {
|
|
|
832
1111
|
});
|
|
833
1112
|
return result;
|
|
834
1113
|
}
|
|
835
|
-
rethrowComponentAccessError(error, 'push');
|
|
836
|
-
}
|
|
837
|
-
const lockEntry = readComponentLock(getProjectRoot(context.cwd)).components[componentId];
|
|
838
|
-
if (!lockEntry) {
|
|
839
|
-
throw new Error(`Cannot push ${targetPath}: missing lock entry. Run \`revo component pull ${componentId} --force\` to establish a remote baseline.`);
|
|
840
|
-
}
|
|
841
|
-
const remoteHash = hashComponentSource(remote);
|
|
842
|
-
if (remoteHash !== lockEntry.remoteHash) {
|
|
843
1114
|
throw new Error(`Cannot push ${targetPath}: remote changed since the last lock baseline. Run \`revo component plan --all\` and pull/reconcile first.`);
|
|
844
1115
|
}
|
|
845
|
-
const localHash = hashComponentSource(component);
|
|
846
1116
|
if (localHash === lockEntry.remoteHash) {
|
|
847
1117
|
const result = {
|
|
848
1118
|
status: 'skipped',
|
|
@@ -858,7 +1128,8 @@ async function pushSingleComponent(context, manifestPath) {
|
|
|
858
1128
|
return result;
|
|
859
1129
|
}
|
|
860
1130
|
}
|
|
861
|
-
const
|
|
1131
|
+
const elements = buildPushElements(component, remote, identity);
|
|
1132
|
+
const response = await client.saveComponentElements(remoteComponentId, elements);
|
|
862
1133
|
if (response.status === 304) {
|
|
863
1134
|
const result = {
|
|
864
1135
|
status: 'skipped',
|
|
@@ -866,11 +1137,11 @@ async function pushSingleComponent(context, manifestPath) {
|
|
|
866
1137
|
reason: 'no changes',
|
|
867
1138
|
};
|
|
868
1139
|
try {
|
|
869
|
-
const
|
|
870
|
-
upsertLockFromRemote(context.cwd,
|
|
1140
|
+
const refreshedRemote = unwrapComponent(await client.getComponent(remoteComponentId));
|
|
1141
|
+
upsertLockFromRemote(context.cwd, refreshedRemote, targetPath, component);
|
|
871
1142
|
}
|
|
872
1143
|
catch {
|
|
873
|
-
upsertLockFromRemote(context.cwd,
|
|
1144
|
+
upsertLockFromRemote(context.cwd, remote, targetPath, component);
|
|
874
1145
|
}
|
|
875
1146
|
printSyncStatus(println, {
|
|
876
1147
|
status: 'Skipped',
|
|
@@ -885,11 +1156,15 @@ async function pushSingleComponent(context, manifestPath) {
|
|
|
885
1156
|
targetPath,
|
|
886
1157
|
};
|
|
887
1158
|
try {
|
|
888
|
-
const
|
|
889
|
-
upsertLockFromRemote(context.cwd,
|
|
1159
|
+
const refreshedRemote = unwrapComponent(await client.getComponent(remoteComponentId));
|
|
1160
|
+
upsertLockFromRemote(context.cwd, refreshedRemote, targetPath, component);
|
|
890
1161
|
}
|
|
891
1162
|
catch {
|
|
892
|
-
upsertLockFromRemote(context.cwd,
|
|
1163
|
+
upsertLockFromRemote(context.cwd, {
|
|
1164
|
+
...component,
|
|
1165
|
+
componentId: remoteComponentId,
|
|
1166
|
+
...(componentVersion(remote) != null ? { version: componentVersion(remote) } : {}),
|
|
1167
|
+
}, targetPath, component);
|
|
893
1168
|
}
|
|
894
1169
|
printSyncStatus(println, {
|
|
895
1170
|
status: 'Deployed',
|
|
@@ -911,7 +1186,6 @@ async function pushSingleComponent(context, manifestPath) {
|
|
|
911
1186
|
targetPath,
|
|
912
1187
|
reason: result.reason,
|
|
913
1188
|
});
|
|
914
|
-
upsertLockFromRemote(context.cwd, component, targetPath);
|
|
915
1189
|
return result;
|
|
916
1190
|
}
|
|
917
1191
|
if (error instanceof PermissionDeniedError) {
|
|
@@ -937,6 +1211,7 @@ async function pushSingleComponent(context, manifestPath) {
|
|
|
937
1211
|
async function pushAllComponents(context, options) {
|
|
938
1212
|
const { cwd, println } = context;
|
|
939
1213
|
const root = getWorkspaceRoot(cwd);
|
|
1214
|
+
const identity = resolveComponentIdentity(cwd);
|
|
940
1215
|
let manifests = getComponentManifestPaths(root);
|
|
941
1216
|
if (manifests.length === 0) {
|
|
942
1217
|
println(`No component.json files found in ${path.relative(cwd, root) || 'Components'}.`);
|
|
@@ -954,10 +1229,16 @@ async function pushAllComponents(context, options) {
|
|
|
954
1229
|
}
|
|
955
1230
|
}
|
|
956
1231
|
await confirmBulkAction(context, 'push', manifests.length, options.force || options.yes);
|
|
1232
|
+
const remoteCatalog = identity.mode === 'stableKey'
|
|
1233
|
+
? await buildRemoteComponentCatalog(context, identity)
|
|
1234
|
+
: undefined;
|
|
1235
|
+
if (remoteCatalog) {
|
|
1236
|
+
migrateComponentLockIdentity(cwd, identity, buildLocalComponentCatalog(cwd, identity), remoteCatalog);
|
|
1237
|
+
}
|
|
957
1238
|
const startedAt = Date.now();
|
|
958
1239
|
const results = [];
|
|
959
1240
|
for (const manifestPath of manifests) {
|
|
960
|
-
const result = await pushSingleComponent(context, manifestPath);
|
|
1241
|
+
const result = await pushSingleComponent(context, manifestPath, remoteCatalog);
|
|
961
1242
|
if (result) {
|
|
962
1243
|
results.push(result);
|
|
963
1244
|
}
|
|
@@ -969,52 +1250,40 @@ async function buildComponentPlan(context, scope = { enabled: false, base: '', c
|
|
|
969
1250
|
const projectRoot = getProjectRoot(context.cwd);
|
|
970
1251
|
const lock = readComponentLock(projectRoot);
|
|
971
1252
|
const root = getWorkspaceRoot(context.cwd);
|
|
972
|
-
const
|
|
973
|
-
const
|
|
974
|
-
for (const manifestPath of localManifests) {
|
|
975
|
-
const component = readWorkspaceComponentSafe(manifestPath);
|
|
976
|
-
const componentId = component ? componentIdOf(component) : '';
|
|
977
|
-
if (component && componentId) {
|
|
978
|
-
localById.set(componentId, {
|
|
979
|
-
manifestPath,
|
|
980
|
-
component,
|
|
981
|
-
hash: hashComponentSource(component),
|
|
982
|
-
});
|
|
983
|
-
}
|
|
984
|
-
}
|
|
1253
|
+
const identity = resolveComponentIdentity(context.cwd);
|
|
1254
|
+
const localCatalog = buildLocalComponentCatalog(context.cwd, identity);
|
|
985
1255
|
const { components } = await fetchAllComponentSummaries(context);
|
|
986
|
-
const
|
|
987
|
-
const
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
hash: hashComponentSource(remote),
|
|
993
|
-
});
|
|
994
|
-
}
|
|
995
|
-
const allIds = [...new Set([
|
|
996
|
-
...localById.keys(),
|
|
997
|
-
...remoteById.keys(),
|
|
998
|
-
...Object.keys(lock.components),
|
|
1256
|
+
const remoteCatalog = await buildRemoteComponentCatalog(context, identity, components);
|
|
1257
|
+
const lockEntries = componentLockMatchesIdentity(lock, identity) ? lock.components : {};
|
|
1258
|
+
const allIdentityKeys = [...new Set([
|
|
1259
|
+
...localCatalog.byIdentity.keys(),
|
|
1260
|
+
...remoteCatalog.byIdentity.keys(),
|
|
1261
|
+
...Object.keys(lockEntries),
|
|
999
1262
|
])].sort();
|
|
1000
|
-
const items =
|
|
1001
|
-
const local =
|
|
1002
|
-
const remote =
|
|
1003
|
-
const lockEntry =
|
|
1263
|
+
const items = allIdentityKeys.map((identityKey) => {
|
|
1264
|
+
const local = localCatalog.byIdentity.get(identityKey) || null;
|
|
1265
|
+
const remote = remoteCatalog.byIdentity.get(identityKey) || null;
|
|
1266
|
+
const lockEntry = lockEntries[identityKey] || null;
|
|
1004
1267
|
const component = local?.component || remote?.component;
|
|
1005
|
-
const
|
|
1268
|
+
const localComponentId = local ? componentIdOf(local.component) : '';
|
|
1269
|
+
const remoteComponentId = remote ? componentIdOf(remote.component) : lockEntry?.remoteComponentId || '';
|
|
1270
|
+
const componentId = localComponentId || remoteComponentId || lockEntry?.componentId || identityKey;
|
|
1271
|
+
const componentName = component?.name || lockEntry?.componentName || identityKey;
|
|
1006
1272
|
const category = normalizeValue(component?.category, lockEntry?.category ?? null);
|
|
1007
|
-
const targetPath = local
|
|
1273
|
+
const targetPath = local?.manifestPath
|
|
1008
1274
|
? componentWorkspacePath(context.cwd, local.manifestPath)
|
|
1009
1275
|
: remote
|
|
1010
1276
|
? componentTargetPath(context.cwd, remote.component)
|
|
1011
|
-
: lockEntry?.path ||
|
|
1277
|
+
: lockEntry?.path || identityKey;
|
|
1012
1278
|
const localHash = local?.hash || null;
|
|
1013
1279
|
const remoteHash = remote?.hash || null;
|
|
1014
1280
|
const lockHash = lockEntry?.remoteHash || null;
|
|
1015
1281
|
let status = 'clean';
|
|
1016
1282
|
let reason = 'local, lock, and remote match';
|
|
1017
|
-
if (!local && remote) {
|
|
1283
|
+
if (!local && !remote) {
|
|
1284
|
+
reason = 'lock entry has no matching local or remote component';
|
|
1285
|
+
}
|
|
1286
|
+
else if (!local && remote) {
|
|
1018
1287
|
status = 'safe-update';
|
|
1019
1288
|
reason = 'remote component is missing locally';
|
|
1020
1289
|
}
|
|
@@ -1045,9 +1314,14 @@ async function buildComponentPlan(context, scope = { enabled: false, base: '', c
|
|
|
1045
1314
|
status = 'conflict';
|
|
1046
1315
|
reason = 'local and remote changed since lock';
|
|
1047
1316
|
}
|
|
1048
|
-
const inScope = !scope.enabled
|
|
1317
|
+
const inScope = !scope.enabled
|
|
1318
|
+
|| scope.componentIds.has(localComponentId)
|
|
1319
|
+
|| scope.componentIds.has(remoteComponentId);
|
|
1049
1320
|
return {
|
|
1050
1321
|
componentId,
|
|
1322
|
+
identityKey,
|
|
1323
|
+
...(identity.mode === 'stableKey' ? { stableKey: identityKey } : {}),
|
|
1324
|
+
...(remoteComponentId ? { remoteComponentId } : {}),
|
|
1051
1325
|
componentName,
|
|
1052
1326
|
category,
|
|
1053
1327
|
path: targetPath,
|
|
@@ -1060,10 +1334,16 @@ async function buildComponentPlan(context, scope = { enabled: false, base: '', c
|
|
|
1060
1334
|
...(scope.enabled ? { inScope } : {}),
|
|
1061
1335
|
};
|
|
1062
1336
|
});
|
|
1337
|
+
const scopedItems = scope.enabled ? items.filter((item) => item.inScope) : items;
|
|
1063
1338
|
return {
|
|
1064
1339
|
schemaVersion: 1,
|
|
1065
1340
|
generatedAt: new Date().toISOString(),
|
|
1066
1341
|
workspaceRoot: path.relative(context.cwd, root) || 'Components',
|
|
1342
|
+
componentIdentity: identity,
|
|
1343
|
+
unmanaged: {
|
|
1344
|
+
local: localCatalog.unmanaged.length,
|
|
1345
|
+
remote: remoteCatalog.unmanaged.length,
|
|
1346
|
+
},
|
|
1067
1347
|
...(scope.enabled ? {
|
|
1068
1348
|
scope: {
|
|
1069
1349
|
mode: 'changed',
|
|
@@ -1071,7 +1351,7 @@ async function buildComponentPlan(context, scope = { enabled: false, base: '', c
|
|
|
1071
1351
|
componentIds: [...scope.componentIds].sort(),
|
|
1072
1352
|
},
|
|
1073
1353
|
} : {}),
|
|
1074
|
-
counts:
|
|
1354
|
+
counts: scopedItems.reduce((counts, item) => {
|
|
1075
1355
|
counts[item.status] += 1;
|
|
1076
1356
|
return counts;
|
|
1077
1357
|
}, {
|
|
@@ -1083,7 +1363,7 @@ async function buildComponentPlan(context, scope = { enabled: false, base: '', c
|
|
|
1083
1363
|
create: 0,
|
|
1084
1364
|
'missing-lock': 0,
|
|
1085
1365
|
}),
|
|
1086
|
-
items,
|
|
1366
|
+
items: scopedItems,
|
|
1087
1367
|
};
|
|
1088
1368
|
}
|
|
1089
1369
|
function printPlan(context, plan) {
|
|
@@ -1139,21 +1419,45 @@ function buildDebugElements(component) {
|
|
|
1139
1419
|
if (!componentName) {
|
|
1140
1420
|
throw new Error('Missing component name in local manifest.');
|
|
1141
1421
|
}
|
|
1142
|
-
return
|
|
1422
|
+
return sortDebugElements(component.elements || [], componentName).map((element) => {
|
|
1423
|
+
const key = readDebugElementKey(element, componentName);
|
|
1424
|
+
const order = readDebugElementOrder(element, key, componentName);
|
|
1143
1425
|
const details = element.details || '';
|
|
1144
1426
|
return {
|
|
1145
|
-
key
|
|
1427
|
+
key,
|
|
1146
1428
|
desc: element.desc ?? null,
|
|
1147
1429
|
initValue: details,
|
|
1148
1430
|
details,
|
|
1149
1431
|
componentId,
|
|
1150
1432
|
componentName,
|
|
1151
|
-
order
|
|
1433
|
+
order,
|
|
1152
1434
|
hidden: Boolean(element.hidden),
|
|
1153
1435
|
uuid: typeof element.uuid === 'string' ? element.uuid : undefined,
|
|
1154
1436
|
};
|
|
1155
1437
|
});
|
|
1156
1438
|
}
|
|
1439
|
+
function readDebugElementKey(element, componentName) {
|
|
1440
|
+
if (typeof element.key === 'string' && element.key.trim()) {
|
|
1441
|
+
return element.key;
|
|
1442
|
+
}
|
|
1443
|
+
const order = Number.isFinite(Number(element.order)) ? Number(element.order) : 'unknown';
|
|
1444
|
+
throw new Error(`Debug element at order ${order} in "${componentName}" is missing a valid key.`);
|
|
1445
|
+
}
|
|
1446
|
+
function readDebugElementOrder(element, key, componentName) {
|
|
1447
|
+
const order = Number(element.order);
|
|
1448
|
+
if (Number.isFinite(order)) {
|
|
1449
|
+
return order;
|
|
1450
|
+
}
|
|
1451
|
+
throw new Error(`Debug element "${key}" in "${componentName}" is missing a valid numeric order.`);
|
|
1452
|
+
}
|
|
1453
|
+
function sortDebugElements(elements, componentName) {
|
|
1454
|
+
return [...elements].sort((left, right) => {
|
|
1455
|
+
const leftOrder = readDebugElementOrder(left, readDebugElementKey(left, componentName), componentName);
|
|
1456
|
+
const rightOrder = readDebugElementOrder(right, readDebugElementKey(right, componentName), componentName);
|
|
1457
|
+
return leftOrder - rightOrder
|
|
1458
|
+
|| readDebugElementKey(left, componentName).localeCompare(readDebugElementKey(right, componentName));
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1157
1461
|
function resolveElementSourcePath(manifestPath, component, element) {
|
|
1158
1462
|
const componentDir = path.dirname(manifestPath);
|
|
1159
1463
|
const detailDirs = [
|
|
@@ -1183,12 +1487,14 @@ function buildDebugSourceComponent(role, manifestPath, component) {
|
|
|
1183
1487
|
componentName: component.name || '',
|
|
1184
1488
|
type: normalizeComponentType(component),
|
|
1185
1489
|
manifestPath,
|
|
1186
|
-
elements:
|
|
1490
|
+
elements: sortDebugElements(component.elements || [], component.name || component.componentId || component.id || 'component').map((element) => {
|
|
1491
|
+
const key = readDebugElementKey(element, component.name || component.componentId || component.id || 'component');
|
|
1492
|
+
const order = readDebugElementOrder(element, key, component.name || component.componentId || component.id || 'component');
|
|
1187
1493
|
const sourcePath = resolveElementSourcePath(manifestPath, component, element);
|
|
1188
1494
|
const details = element.details || '';
|
|
1189
1495
|
return {
|
|
1190
|
-
key
|
|
1191
|
-
order
|
|
1496
|
+
key,
|
|
1497
|
+
order,
|
|
1192
1498
|
sourcePath,
|
|
1193
1499
|
bytes: Buffer.byteLength(details, 'utf8'),
|
|
1194
1500
|
};
|
|
@@ -1242,12 +1548,14 @@ function collectDebugExtraLibs(cwd, currentComponentId) {
|
|
|
1242
1548
|
.filter(({ component }) => {
|
|
1243
1549
|
const type = normalizeComponentType(component);
|
|
1244
1550
|
const componentId = component.componentId || component.id || '';
|
|
1245
|
-
return isDebugLibraryType(type) && componentId !== currentComponentId && Boolean(component.name);
|
|
1551
|
+
return isDebugLibraryType(type) && Boolean(componentId) && componentId !== currentComponentId && Boolean(component.name);
|
|
1246
1552
|
})
|
|
1247
1553
|
.map(({ manifestPath, component }) => ({
|
|
1248
1554
|
payload: {
|
|
1555
|
+
componentId: component.componentId || component.id || '',
|
|
1249
1556
|
name: component.name,
|
|
1250
1557
|
type: normalizeComponentType(component),
|
|
1558
|
+
...(typeof component.version === 'number' ? { version: component.version } : {}),
|
|
1251
1559
|
elements: buildDebugElements(component),
|
|
1252
1560
|
},
|
|
1253
1561
|
source: buildDebugSourceComponent('extraLib', manifestPath, component),
|
|
@@ -1314,7 +1622,8 @@ async function debugSingleComponent(context, componentId) {
|
|
|
1314
1622
|
const timeout = parseDebugNumber(context.args, ['timeout', 't'], 10, 600, 'Timeout');
|
|
1315
1623
|
const memory = parseDebugNumber(context.args, ['memory', 'm'], 128, 1024, 'Memory');
|
|
1316
1624
|
const stream = readBoolFlag(context.args, ['stream']);
|
|
1317
|
-
const includeExtraLibs =
|
|
1625
|
+
const includeExtraLibs = context.args['extra-libs'] !== false
|
|
1626
|
+
&& !readBoolFlag(context.args, ['no-extra-libs']);
|
|
1318
1627
|
const rawDebug = readBoolFlag(context.args, ['raw-debug', 'debug-raw', 'debug-payload']);
|
|
1319
1628
|
const profile = await context.client.me();
|
|
1320
1629
|
const sandboxEndpoint = extractSandboxEndpoint(profile);
|
|
@@ -1435,7 +1744,7 @@ export async function handleComponentCommand(context) {
|
|
|
1435
1744
|
await pushAllComponents(context, { force, yes, scope: changedScope });
|
|
1436
1745
|
return;
|
|
1437
1746
|
}
|
|
1438
|
-
const manifests = targets.flatMap((
|
|
1747
|
+
const manifests = targets.flatMap((target) => findComponentManifestsByTarget(context.cwd, target));
|
|
1439
1748
|
if (manifests.length === 0) {
|
|
1440
1749
|
throw new Error(`Unable to find local component for ${targets.join(', ')}.`);
|
|
1441
1750
|
}
|