@lightdash/cli 2.13.2 → 2.14.0

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.
@@ -33,7 +33,7 @@ export declare const resolveUploadFilterUuids: (filter: Set<string>, listedApps:
33
33
  * refs (including refs parsed out of app URLs) get the slug-identity
34
34
  * explanation: id-free bundles can only be selected by slug.
35
35
  */
36
- export declare const unmatchedUploadRefsWarning: (unmatched: string[]) => string | null;
36
+ export declare const unmatchedUploadRefsWarning: (unmatched: string[], noun?: string) => string | null;
37
37
  /**
38
38
  * Shown when the upload response carries no slug even though the bundle sent
39
39
  * one — the server predates slug identity, so it ignored the slug and matched
@@ -1 +1 @@
1
- {"version":3,"file":"appsDownload.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/appsDownload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"appsDownload.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/appsDownload.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACvB,MAAM,mBAAmB,CAAC;AAc3B,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,eAAO,MAAM,mBAAmB,cAAe,MAAM,KAAG,MAoBvD,CAAC;AAEF,eAAO,MAAM,sBAAsB,eACnB,MAAM,EAAE,eACP,OAAO,KACrB,GAAG,CAAC,MAAM,CAAC,GAAG,IACoD,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,WACpB,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,YAChB,eAAe,KAC1B,OAG2D,CAAC;AAE/D,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,WAClB,GAAG,CAAC,MAAM,CAAC,YACT,eAAe,KAC1B,MAAM,EAGJ,CAAC;AAEN;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QACvB,MAAM,UACH;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KACzC,MAWF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,WACzB,GAAG,CAAC,MAAM,CAAC,cACP;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KACjD,GAAG,CAAC,MAAM,CAcZ,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,cACxB,MAAM,EAAE,SACb,MAAM,KACb,MAAM,GAAG,IAMX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,WAAY,MAAM,KAAG,MAC6K,CAAC;AAEjO;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,aACf,MAAM,GAAG,SAAS,eACf,OAAO,cACT;IACP,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CACpB,KAKF;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAejD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,QAAS,OAAO,KAAG,OACP,CAAC;AAE5D,MAAM,MAAM,qBAAqB,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,YAAa;IAC1C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,KAAG,qBASH,CAAC;AAEF,eAAO,MAAM,aAAa,gBACT,MAAM,EAAE,SACd,MAAM,KACd;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAG7C,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,qBAAqB,SAAU;IACxC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC/B,KAAG,MAAM,EAOT,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,WAC3B,MAAM,QACR,mBAAmB,KAC1B,mBASF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,SAAU;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,KAAG,MAKqD,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,MAAM,MAAM,uBAAuB,GAC7B;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,QAC5B,OAAO,KACb,uBAoBF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,YAAa,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAGpB,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,YACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAChC,OAKF,CAAC;AAEF,eAAO,MAAM,mBAAmB,iBACd,MAAM,SACb,MAAM,YACH,kBAAkB,EAAE,WACrB,MAAM,wBACO,MAAM,SACtB,MAAM,KACb;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAiBxD,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAClC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,SAAgB;IAC1C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,CACN,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,KACb,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAIlC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,MAAM,GAAG,IAAI,CAAC;IAC1D,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D,KAAG,OAAO,CAAC,mBAAmB,CAoG9B,CAAC"}
@@ -73,10 +73,10 @@ exports.resolveUploadFilterUuids = resolveUploadFilterUuids;
73
73
  * refs (including refs parsed out of app URLs) get the slug-identity
74
74
  * explanation: id-free bundles can only be selected by slug.
75
75
  */
76
- const unmatchedUploadRefsWarning = (unmatched) => {
76
+ const unmatchedUploadRefsWarning = (unmatched, noun = 'app') => {
77
77
  if (unmatched.length === 0)
78
78
  return null;
79
- const base = `No local app folder matched: ${unmatched.join(', ')}.`;
79
+ const base = `No local ${noun} folder matched: ${unmatched.join(', ')}.`;
80
80
  return unmatched.some((ref) => (0, uuid_1.validate)(ref))
81
81
  ? `${base} Bundles downloaded with slug identity carry no uuid — select them by slug (the folder name) instead of a UUID or app URL.`
82
82
  : base;
@@ -1,4 +1,4 @@
1
- import { type DataAppCode, type DataAppDataReferences, type DataAppExploreIndex, type DataReferenceLocation } from '@lightdash/common';
1
+ import { type DataAppCode, type DataAppDataReferences, type DataAppExploreIndex, type DataAppManifest, type DataReferenceLocation } from '@lightdash/common';
2
2
  export type AppsValidateFormat = 'human' | 'json';
3
3
  export type AppsValidateOptions = {
4
4
  build: boolean;
@@ -24,6 +24,7 @@ export type AppValidationResult = {
24
24
  path: string;
25
25
  name: string | null;
26
26
  projectUuid: string | null;
27
+ template: DataAppManifest['template'] | null;
27
28
  valid: boolean;
28
29
  errors: AppsValidationIssue[];
29
30
  warnings: AppsValidationIssue[];
@@ -36,6 +37,8 @@ export type AppsValidationReport = {
36
37
  mode: 'live' | 'offline';
37
38
  summary: {
38
39
  apps: number;
40
+ dataApps: number;
41
+ chartTypes: number;
39
42
  errors: number;
40
43
  warnings: number;
41
44
  callSites: number;
@@ -70,6 +73,8 @@ export declare const validateDataAppBuild: (args: {
70
73
  export declare const fetchLiveDataAppExploreIndex: (projectUuid: string) => Promise<DataAppExploreIndex>;
71
74
  export declare const validateLocalDataApp: (appPath: string, options: ValidateAppOptions) => Promise<AppValidationResult>;
72
75
  export declare const buildAppsValidationReport: (apps: AppValidationResult[], live: boolean, build?: boolean) => AppsValidationReport;
76
+ /** "2 data app(s)", "1 custom chart type(s)", or both — driven by manifest templates. */
77
+ export declare const describeValidatedBundles: (summary: AppsValidationReport['summary']) => string;
73
78
  export declare const renderAppsValidationHuman: (report: AppsValidationReport, verbose?: boolean) => string;
74
79
  export declare const renderAppsValidationJson: (report: AppsValidationReport) => string;
75
80
  export declare const appsValidateHandler: (pathArgs: string[] | undefined, options: AppsValidateOptions) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/validate.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.appsValidateHandler = exports.renderAppsValidationJson = exports.renderAppsValidationHuman = exports.buildAppsValidationReport = exports.validateLocalDataApp = exports.fetchLiveDataAppExploreIndex = exports.validateDataAppBuild = void 0;
3
+ exports.appsValidateHandler = exports.renderAppsValidationJson = exports.renderAppsValidationHuman = exports.describeValidatedBundles = exports.buildAppsValidationReport = exports.validateLocalDataApp = exports.fetchLiveDataAppExploreIndex = exports.validateDataAppBuild = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const common_1 = require("@lightdash/common");
6
6
  const execa_1 = tslib_1.__importDefault(require("execa"));
@@ -289,6 +289,7 @@ const validateLocalDataApp = async (appPath, options) => {
289
289
  path: appDir,
290
290
  name: null,
291
291
  projectUuid: null,
292
+ template: null,
292
293
  valid: false,
293
294
  errors: [issue('bundle', (0, common_1.getErrorMessage)(error))],
294
295
  warnings,
@@ -374,6 +375,7 @@ const validateLocalDataApp = async (appPath, options) => {
374
375
  path: appDir,
375
376
  name: typeof manifest.name === 'string' ? manifest.name : null,
376
377
  projectUuid,
378
+ template: manifest.template ?? null,
377
379
  valid: errors.length === 0,
378
380
  errors,
379
381
  warnings,
@@ -385,12 +387,15 @@ exports.validateLocalDataApp = validateLocalDataApp;
385
387
  const buildAppsValidationReport = (apps, live, build = false) => {
386
388
  const errors = apps.reduce((count, app) => count + app.errors.length, 0);
387
389
  const warnings = apps.reduce((count, app) => count + app.warnings.length, 0);
390
+ const chartTypes = apps.filter((app) => app.template === common_1.DATA_APP_VIZ_TEMPLATE).length;
388
391
  return {
389
392
  build,
390
393
  valid: errors === 0,
391
394
  mode: live ? 'live' : 'offline',
392
395
  summary: {
393
396
  apps: apps.length,
397
+ dataApps: apps.length - chartTypes,
398
+ chartTypes,
394
399
  errors,
395
400
  warnings,
396
401
  callSites: apps.reduce((count, app) => count + app.coverage.callSites, 0),
@@ -422,9 +427,27 @@ const referenceKindLabels = {
422
427
  globalFilter: 'global filter',
423
428
  };
424
429
  const formatUnanalyzedReference = (reference) => `${reference.location.path}:${reference.location.line}:${reference.location.column} — ${referenceKindLabels[reference.kind]} (unresolved: ${reference.unresolved.join(', ')})`;
430
+ /** "2 data app(s)", "1 custom chart type(s)", or both — driven by manifest templates. */
431
+ const describeValidatedBundles = (summary) => {
432
+ if (summary.chartTypes > 0 && summary.dataApps > 0) {
433
+ return `${summary.dataApps} data app(s) and ${summary.chartTypes} custom chart type(s)`;
434
+ }
435
+ if (summary.chartTypes > 0) {
436
+ return `${summary.chartTypes} custom chart type(s)`;
437
+ }
438
+ return `${summary.dataApps} data app(s)`;
439
+ };
440
+ exports.describeValidatedBundles = describeValidatedBundles;
441
+ const bundleKindLabel = (summary) => {
442
+ if (summary.chartTypes > 0 && summary.dataApps > 0)
443
+ return 'App bundle';
444
+ if (summary.chartTypes > 0)
445
+ return 'Custom chart type';
446
+ return 'Data app';
447
+ };
425
448
  const renderAppsValidationHuman = (report, verbose = false) => {
426
449
  const lines = [
427
- `Validating ${report.summary.apps} data app(s) using ${report.mode === 'live'
450
+ `Validating ${(0, exports.describeValidatedBundles)(report.summary)} using ${report.mode === 'live'
428
451
  ? 'the live project semantic layer'
429
452
  : 'local semantic layer snapshots'}${report.build ? ' and running Vite production builds' : ''}.`,
430
453
  ];
@@ -451,8 +474,8 @@ const renderAppsValidationHuman = (report, verbose = false) => {
451
474
  }
452
475
  lines.push('');
453
476
  lines.push(report.valid
454
- ? styles.success(`Validation passed for ${report.summary.apps} data app(s) with ${report.summary.warnings} warning(s).`)
455
- : styles.error(`Validation failed with ${report.summary.errors} error(s) across ${report.summary.apps} data app(s).`));
477
+ ? styles.success(`Validation passed for ${(0, exports.describeValidatedBundles)(report.summary)} with ${report.summary.warnings} warning(s).`)
478
+ : styles.error(`Validation failed with ${report.summary.errors} error(s) across ${(0, exports.describeValidatedBundles)(report.summary)}.`));
456
479
  return `${lines.join('\n')}\n`;
457
480
  };
458
481
  exports.renderAppsValidationHuman = renderAppsValidationHuman;
@@ -487,7 +510,7 @@ const appsValidateHandler = async (pathArgs, options) => {
487
510
  ? (0, exports.renderAppsValidationJson)(report)
488
511
  : (0, exports.renderAppsValidationHuman)(report, options.verbose));
489
512
  if (!report.valid) {
490
- throw new common_1.ParameterError(`Data app validation failed with ${report.summary.errors} error(s).`);
513
+ throw new common_1.ParameterError(`${bundleKindLabel(report.summary)} validation failed with ${report.summary.errors} error(s).`);
491
514
  }
492
515
  };
493
516
  exports.appsValidateHandler = appsValidateHandler;
@@ -397,6 +397,7 @@ describe('renderAppsValidationHuman', () => {
397
397
  path: '/tmp/orders-app',
398
398
  name: 'Orders app',
399
399
  projectUuid: 'project-uuid',
400
+ template: null,
400
401
  valid: true,
401
402
  errors: [],
402
403
  warnings: [],
@@ -457,4 +458,45 @@ describe('renderAppsValidationHuman', () => {
457
458
  const output = (0, validate_1.renderAppsValidationJson)(report);
458
459
  expect(JSON.parse(output)).not.toHaveProperty('apps.0.unanalyzedReferences');
459
460
  });
461
+ const buildResult = (template, overrides = {}) => ({
462
+ path: '/tmp/bundle',
463
+ name: 'Bundle',
464
+ projectUuid: null,
465
+ template,
466
+ valid: true,
467
+ errors: [],
468
+ warnings: [],
469
+ coverage: {
470
+ callSites: 0,
471
+ fullyResolved: 0,
472
+ partiallyResolved: 0,
473
+ unresolved: 0,
474
+ unanalyzed: 0,
475
+ },
476
+ unanalyzedReferences: [],
477
+ ...overrides,
478
+ });
479
+ it('describes an all-chart-type run as custom chart types', () => {
480
+ const vizReport = (0, validate_1.buildAppsValidationReport)([buildResult(common_1.DATA_APP_VIZ_TEMPLATE)], false);
481
+ const output = (0, validate_1.renderAppsValidationHuman)(vizReport);
482
+ expect(output).toContain('Validating 1 custom chart type(s)');
483
+ expect(output).toContain('Validation passed for 1 custom chart type(s)');
484
+ expect(output).not.toContain('data app(s)');
485
+ expect(vizReport.summary.chartTypes).toBe(1);
486
+ expect(vizReport.summary.dataApps).toBe(0);
487
+ });
488
+ it('describes a mixed run with both nouns', () => {
489
+ const mixedReport = (0, validate_1.buildAppsValidationReport)([
490
+ buildResult(common_1.DATA_APP_VIZ_TEMPLATE, {
491
+ valid: false,
492
+ errors: [
493
+ { code: 'manifest', message: 'bad', location: null },
494
+ ],
495
+ }),
496
+ buildResult(null),
497
+ ], false);
498
+ const output = (0, validate_1.renderAppsValidationHuman)(mixedReport);
499
+ expect(output).toContain('Validating 1 data app(s) and 1 custom chart type(s)');
500
+ expect(output).toContain('Validation failed with 1 error(s) across 1 data app(s) and 1 custom chart type(s).');
501
+ });
460
502
  });
@@ -1 +1 @@
1
- {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/handlers/download.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/handlers/download.ts"],"names":[],"mappings":"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"}
@@ -1108,6 +1108,9 @@ const downloadHandler = async (options) => {
1108
1108
  });
1109
1109
  const projectName = (0, common_1.generateSlug)(project.name);
1110
1110
  const counts = {};
1111
+ // Per-resource app/chart-type failures are reported inline and tallied
1112
+ // here so the process can exit non-zero without aborting the download.
1113
+ let downloadFailures = 0;
1111
1114
  const start = Date.now();
1112
1115
  await analytics_1.LightdashAnalytics.track({
1113
1116
  event: 'download.started',
@@ -1419,6 +1422,7 @@ const downloadHandler = async (options) => {
1419
1422
  });
1420
1423
  const summary = (0, appsDownload_1.appsDownloadSummary)(successCount, appRefsToDownload.length, failures, appsDir, skippedNotBuiltCount + skippedWrongKindCount);
1421
1424
  counts.appsNum = successCount;
1425
+ downloadFailures += failures.length;
1422
1426
  output.completeItem(`${successCount} downloaded${skippedNotBuiltCount + skippedWrongKindCount > 0
1423
1427
  ? `, ${skippedNotBuiltCount + skippedWrongKindCount} skipped`
1424
1428
  : ''}${failures.length > 0 ? `, ${failures.length} failed` : ''}`, summary.ok ? undefined : 'warning');
@@ -1525,6 +1529,7 @@ const downloadHandler = async (options) => {
1525
1529
  const chartTypesSummary = (0, appsDownload_1.appsDownloadSummary)(chartTypesOutcome.successCount, chartTypeRefsToDownload.length, chartTypesOutcome.failures, chartTypesDir, chartTypesOutcome.skippedNotBuiltCount +
1526
1530
  chartTypesOutcome.skippedWrongKindCount, 'custom chart type');
1527
1531
  counts.chartTypesNum = chartTypesOutcome.successCount;
1532
+ downloadFailures += chartTypesOutcome.failures.length;
1528
1533
  const chartTypesSkipped = chartTypesOutcome.skippedNotBuiltCount +
1529
1534
  chartTypesOutcome.skippedWrongKindCount;
1530
1535
  output.completeItem(`${chartTypesOutcome.successCount} downloaded${chartTypesSkipped > 0
@@ -1568,6 +1573,7 @@ const downloadHandler = async (options) => {
1568
1573
  const linkedSkipped = outcome.skippedNotBuiltCount + outcome.skippedWrongKindCount;
1569
1574
  const linkedSummary = (0, appsDownload_1.appsDownloadSummary)(outcome.successCount, linkedAppSlugs.length, outcome.failures, appsDir, linkedSkipped);
1570
1575
  counts.appsNum = (counts.appsNum ?? 0) + outcome.successCount;
1576
+ downloadFailures += outcome.failures.length;
1571
1577
  output.completeItem(`${outcome.successCount} downloaded${linkedSkipped > 0 ? `, ${linkedSkipped} skipped` : ''}${outcome.failures.length > 0
1572
1578
  ? `, ${outcome.failures.length} failed`
1573
1579
  : ''}`, linkedSummary.ok ? undefined : 'warning');
@@ -1611,6 +1617,7 @@ const downloadHandler = async (options) => {
1611
1617
  counts.chartTypesNum =
1612
1618
  (counts.chartTypesNum ?? 0) +
1613
1619
  linkedChartTypesOutcome.successCount;
1620
+ downloadFailures += linkedChartTypesOutcome.failures.length;
1614
1621
  output.completeItem(`${linkedChartTypesOutcome.successCount} downloaded${linkedChartTypesSkipped > 0
1615
1622
  ? `, ${linkedChartTypesSkipped} skipped`
1616
1623
  : ''}${linkedChartTypesOutcome.failures.length > 0
@@ -1645,6 +1652,10 @@ const downloadHandler = async (options) => {
1645
1652
  if (!renderedSummary) {
1646
1653
  globalState_1.default.log(styles.success(`Downloaded content saved to ${downloadRoot}`));
1647
1654
  }
1655
+ if (downloadFailures > 0) {
1656
+ globalState_1.default.log(styles.error(`${downloadFailures} resource(s) failed to download — see errors above.`));
1657
+ process.exitCode = 1;
1658
+ }
1648
1659
  await analytics_1.LightdashAnalytics.track({
1649
1660
  event: 'download.completed',
1650
1661
  properties: {
@@ -1724,6 +1735,8 @@ const summarizeUploadChanges = (before, after) => {
1724
1735
  const hasFailures = [...totals.keys()].some((label) => label === 'with errors' || label === 'failed');
1725
1736
  return { detail, variant: hasFailures ? 'warning' : undefined };
1726
1737
  };
1738
+ const hasUploadFailures = (changes) => Object.entries(changes).some(([key, value]) => value > 0 &&
1739
+ (key.endsWith('with errors') || key.endsWith('failed')));
1727
1740
  const runUploadChangesPhase = async ({ output, label, changes, action, onCount, }) => {
1728
1741
  const before = { ...changes };
1729
1742
  output.startItem(label);
@@ -1738,7 +1751,7 @@ const upsertSingleItem = async (item, type, projectId, changes, force, config, s
1738
1751
  if (!force && !item.needsUpdating) {
1739
1752
  globalState_1.default.debug(`Skipping ${type} "${item.slug}" with no local changes`);
1740
1753
  changes[`${type} skipped`] = (changes[`${type} skipped`] ?? 0) + 1;
1741
- return;
1754
+ return 'skipped';
1742
1755
  }
1743
1756
  globalState_1.default.debug(`Upserting ${type} ${item.slug}`);
1744
1757
  // SQL charts use a different endpoint
@@ -1806,36 +1819,41 @@ const upsertSingleItem = async (item, type, projectId, changes, force, config, s
1806
1819
  }
1807
1820
  }
1808
1821
  }
1822
+ return 'upserted';
1809
1823
  }
1810
1824
  catch (error) {
1811
1825
  if (error instanceof common_1.LightdashError &&
1812
1826
  error.name === 'NotFoundError' &&
1827
+ // Only the missing-space NotFoundError counts as a space skip;
1828
+ // other NotFoundErrors (e.g. a missing custom chart type) are
1829
+ // real failures even with --skip-space-create.
1830
+ error.message.startsWith('Space ') &&
1813
1831
  skipSpaceCreate) {
1814
1832
  globalState_1.default.log(styles.warning(`Skipping ${type} "${item.slug}" because space "${item.spaceSlug}" does not exist and --skip-space-create is true`));
1815
1833
  changes[`${type} skipped`] = (changes[`${type} skipped`] ?? 0) + 1;
1834
+ return 'skipped';
1816
1835
  }
1817
- else {
1818
- changes[`${type} with errors`] =
1819
- (changes[`${type} with errors`] ?? 0) + 1;
1820
- globalState_1.default.log(styles.error(`Error upserting ${type}:\n\t"${item.name}" (slug: "${item.slug}")\n\t${(0, common_1.getErrorMessage)(error)}`));
1821
- await analytics_1.LightdashAnalytics.track({
1822
- event: 'download.error',
1823
- properties: {
1824
- userId: config.user?.userUuid,
1825
- organizationId: config.user?.organizationUuid,
1826
- projectId,
1827
- type,
1828
- error: (0, common_1.getErrorMessage)(error),
1829
- },
1830
- });
1831
- }
1836
+ changes[`${type} with errors`] =
1837
+ (changes[`${type} with errors`] ?? 0) + 1;
1838
+ globalState_1.default.log(styles.error(`Error upserting ${type}:\n\t"${item.name}" (slug: "${item.slug}")\n\t${(0, common_1.getErrorMessage)(error)}`));
1839
+ await analytics_1.LightdashAnalytics.track({
1840
+ event: 'download.error',
1841
+ properties: {
1842
+ userId: config.user?.userUuid,
1843
+ organizationId: config.user?.organizationUuid,
1844
+ projectId,
1845
+ type,
1846
+ error: (0, common_1.getErrorMessage)(error),
1847
+ },
1848
+ });
1849
+ return 'failed';
1832
1850
  }
1833
1851
  };
1834
1852
  /**
1835
1853
  *
1836
1854
  * @param slugs if slugs are provided, we only force upsert the charts/dashboards that match the slugs, if slugs are empty, we upload files that were locally updated
1837
1855
  */
1838
- const upsertResources = async (type, projectId, changes, force, slugs, canUpload, customPath, skipSpaceCreate, publicSpaceCreate, validate, concurrency = 1, extraItems = [], spaceNames) => {
1856
+ const upsertResources = async (type, projectId, changes, force, slugs, canUpload, customPath, skipSpaceCreate, publicSpaceCreate, validate, concurrency = 1, extraItems = [], spaceNames, skipSlugs) => {
1839
1857
  const config = await (0, config_1.getConfig)();
1840
1858
  const folderItems = await readCodeFiles(type, customPath);
1841
1859
  const items = [...folderItems, ...extraItems];
@@ -1854,17 +1872,37 @@ const upsertResources = async (type, projectId, changes, force, slugs, canUpload
1854
1872
  if (filteredItems.length > 0 && !canUpload) {
1855
1873
  const requiredPermission = type === 'charts' ? 'manage:SavedChart' : 'manage:Dashboard';
1856
1874
  globalState_1.default.log(styles.error(`Error uploading ${type}: the ${requiredPermission} permission is required`));
1857
- return { changes, total: filteredItems.length };
1858
- }
1875
+ return { changes, total: filteredItems.length, failedSlugs: [] };
1876
+ }
1877
+ // Items whose dependencies failed earlier in the upload are held back so
1878
+ // they are not created in a broken state (e.g. dashboards with null
1879
+ // chart tiles).
1880
+ const uploadableItems = skipSlugs
1881
+ ? filteredItems.filter((item) => !skipSlugs.has(item.slug))
1882
+ : filteredItems;
1883
+ filteredItems
1884
+ .filter((item) => !uploadableItems.includes(item))
1885
+ .forEach((item) => {
1886
+ changes[`${type} dependency skipped`] =
1887
+ (changes[`${type} dependency skipped`] ?? 0) + 1;
1888
+ globalState_1.default.log(styles.warning(`Skipped ${type.slice(0, -1)} "${item.slug}" because a chart it references failed to upload`));
1889
+ });
1890
+ const failedSlugs = [];
1891
+ const trackOutcome = (item, outcome) => {
1892
+ if (outcome === 'failed') {
1893
+ failedSlugs.push(item.slug);
1894
+ }
1895
+ };
1859
1896
  if (concurrency <= 1) {
1860
1897
  // Sequential path — preserves original behavior exactly
1861
- for (const item of filteredItems) {
1898
+ for (const item of uploadableItems) {
1862
1899
  // eslint-disable-next-line no-await-in-loop
1863
- await upsertSingleItem(item, type, projectId, changes, force, config, skipSpaceCreate, publicSpaceCreate, validate, spaceNames);
1900
+ const outcome = await upsertSingleItem(item, type, projectId, changes, force, config, skipSpaceCreate, publicSpaceCreate, validate, spaceNames);
1901
+ trackOutcome(item, outcome);
1864
1902
  }
1865
1903
  }
1866
1904
  else {
1867
- const grouped = (0, groupBy_1.default)(filteredItems, (item) => item.spaceSlug);
1905
+ const grouped = (0, groupBy_1.default)(uploadableItems, (item) => item.spaceSlug);
1868
1906
  const seedItems = new Set();
1869
1907
  const remainingItems = [];
1870
1908
  Object.values(grouped).forEach((spaceItems) => {
@@ -1910,15 +1948,17 @@ const upsertResources = async (type, projectId, changes, force, slugs, canUpload
1910
1948
  // Phase 1: Sequential seeding (spaces + dashboard placeholders)
1911
1949
  for (const item of seedItems) {
1912
1950
  // eslint-disable-next-line no-await-in-loop
1913
- await upsertSingleItem(item, type, projectId, changes, force, config, skipSpaceCreate, publicSpaceCreate, validate, spaceNames);
1951
+ const outcome = await upsertSingleItem(item, type, projectId, changes, force, config, skipSpaceCreate, publicSpaceCreate, validate, spaceNames);
1952
+ trackOutcome(item, outcome);
1914
1953
  }
1915
1954
  // Phase 2: Parallel bulk upload of remaining items
1916
1955
  const limit = (0, p_limit_1.default)(concurrency);
1917
1956
  await Promise.all(remainingItems.map((item) => limit(async () => {
1918
- await upsertSingleItem(item, type, projectId, changes, force, config, skipSpaceCreate, publicSpaceCreate, validate, spaceNames);
1957
+ const outcome = await upsertSingleItem(item, type, projectId, changes, force, config, skipSpaceCreate, publicSpaceCreate, validate, spaceNames);
1958
+ trackOutcome(item, outcome);
1919
1959
  })));
1920
1960
  }
1921
- return { changes, total: filteredItems.length };
1961
+ return { changes, total: filteredItems.length, failedSlugs };
1922
1962
  };
1923
1963
  // readCodeFiles walks the whole download folder recursively, so callers that
1924
1964
  // need both chart and app slugs should read once and reuse the result.
@@ -2069,6 +2109,10 @@ const uploadHandler = async (options) => {
2069
2109
  (0, spacesAsCode_1.logUploadChanges)(changes);
2070
2110
  globalState_1.default.log(styles.success(`Uploaded content from ${uploadRoot}`));
2071
2111
  }
2112
+ if (hasUploadFailures(changes)) {
2113
+ globalState_1.default.log(styles.error('Upload completed with failures — see errors above.'));
2114
+ process.exitCode = 1;
2115
+ }
2072
2116
  };
2073
2117
  try {
2074
2118
  const spaceFiles = preflightSpaceFiles;
@@ -2533,7 +2577,7 @@ const uploadHandler = async (options) => {
2533
2577
  }
2534
2578
  }
2535
2579
  if (uploadFilter) {
2536
- const unmatchedWarning = (0, appsDownload_1.unmatchedUploadRefsWarning)([...uploadFilter].filter((ref) => !matchedRefs.has(ref)));
2580
+ const unmatchedWarning = (0, appsDownload_1.unmatchedUploadRefsWarning)([...uploadFilter].filter((ref) => !matchedRefs.has(ref)), phase.noun);
2537
2581
  if (unmatchedWarning) {
2538
2582
  globalState_1.default.log(styles.warning(unmatchedWarning));
2539
2583
  }
@@ -2574,6 +2618,10 @@ const uploadHandler = async (options) => {
2574
2618
  process.exitCode = 1;
2575
2619
  }
2576
2620
  }
2621
+ // Chart slugs that failed to upload in the Charts phase; dashboards
2622
+ // referencing them are held back so they are not created with broken
2623
+ // (null chart) tiles.
2624
+ const failedChartSlugs = new Set();
2577
2625
  changes = await runUploadChangesPhase({
2578
2626
  output,
2579
2627
  label: 'Charts',
@@ -2590,6 +2638,7 @@ const uploadHandler = async (options) => {
2590
2638
  return changes;
2591
2639
  }
2592
2640
  const result = await upsertResources('charts', projectId, changes, options.force, chartSlugs, uploadPermissions.charts, options.path, options.skipSpaceCreate, options.public, options.validate, concurrency, looseFiles.charts, spaceNames);
2641
+ result.failedSlugs.forEach((slug) => failedChartSlugs.add(slug));
2593
2642
  counts.chartsNum = result.total;
2594
2643
  return result.changes;
2595
2644
  },
@@ -2603,7 +2652,16 @@ const uploadHandler = async (options) => {
2603
2652
  globalState_1.default.log(styles.warning(`No dashboard filters provided, skipping`));
2604
2653
  return changes;
2605
2654
  }
2606
- const result = await upsertResources('dashboards', projectId, changes, options.force, options.dashboards, uploadPermissions.dashboards, options.path, options.skipSpaceCreate, options.public, options.validate, concurrency, looseFiles.dashboards, spaceNames);
2655
+ let dashboardsToSkip;
2656
+ if (failedChartSlugs.size > 0) {
2657
+ dashboardsToSkip = new Set((await loadDashboardItems())
2658
+ .filter((dashboard) => dashboard.tiles.some((tile) => 'chartSlug' in tile.properties &&
2659
+ typeof tile.properties.chartSlug ===
2660
+ 'string' &&
2661
+ failedChartSlugs.has(tile.properties.chartSlug)))
2662
+ .map((dashboard) => dashboard.slug));
2663
+ }
2664
+ const result = await upsertResources('dashboards', projectId, changes, options.force, options.dashboards, uploadPermissions.dashboards, options.path, options.skipSpaceCreate, options.public, options.validate, concurrency, looseFiles.dashboards, spaceNames, dashboardsToSkip);
2607
2665
  counts.dashboardsNum = result.total;
2608
2666
  return result.changes;
2609
2667
  },
@@ -1 +1 @@
1
- {"version":3,"file":"metadataFile.d.ts","sourceRoot":"","sources":["../../src/handlers/metadataFile.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GAAG;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CAAC;AAQF,eAAO,MAAM,gBAAgB,YAChB,MAAM,KAChB,OAAO,CAAC,iBAAiB,CAQ3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,YACjB,MAAM,YACL,iBAAiB,KAC5B,OAAO,CAAC,IAAI,CASd,CAAC"}
1
+ {"version":3,"file":"metadataFile.d.ts","sourceRoot":"","sources":["../../src/handlers/metadataFile.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GAAG;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CAAC;AAQF,eAAO,MAAM,gBAAgB,YAChB,MAAM,KAChB,OAAO,CAAC,iBAAiB,CAQ3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,YACjB,MAAM,YACL,iBAAiB,KAC5B,OAAO,CAAC,IAAI,CAYd,CAAC"}
@@ -29,6 +29,9 @@ const writeMetadataFile = async (baseDir, metadata) => {
29
29
  dashboards: { ...existing.dashboards, ...metadata.dashboards },
30
30
  };
31
31
  const filePath = path.join(baseDir, exports.METADATA_FILENAME);
32
+ // An all-skipped download creates no bundle folders, so the output root
33
+ // may not exist yet when the metadata file is written.
34
+ await fs_1.promises.mkdir(baseDir, { recursive: true });
32
35
  await fs_1.promises.writeFile(filePath, JSON.stringify(merged, null, 2));
33
36
  };
34
37
  exports.writeMetadataFile = writeMetadataFile;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=metadataFile.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadataFile.test.d.ts","sourceRoot":"","sources":["../../src/handlers/metadataFile.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const fs_1 = require("fs");
5
+ const os = tslib_1.__importStar(require("os"));
6
+ const path = tslib_1.__importStar(require("path"));
7
+ const metadataFile_1 = require("./metadataFile");
8
+ describe('writeMetadataFile', () => {
9
+ let tmpDir;
10
+ beforeEach(async () => {
11
+ tmpDir = await fs_1.promises.mkdtemp(path.join(os.tmpdir(), 'ld-metadata-'));
12
+ });
13
+ afterEach(async () => {
14
+ await fs_1.promises.rm(tmpDir, { recursive: true, force: true });
15
+ });
16
+ it('creates the output root when it does not exist yet', async () => {
17
+ const baseDir = path.join(tmpDir, 'brand-new-dir');
18
+ await (0, metadataFile_1.writeMetadataFile)(baseDir, {
19
+ version: 1,
20
+ charts: {},
21
+ dashboards: {},
22
+ });
23
+ const written = JSON.parse(await fs_1.promises.readFile(path.join(baseDir, metadataFile_1.METADATA_FILENAME), 'utf-8'));
24
+ expect(written).toEqual({ version: 1, charts: {}, dashboards: {} });
25
+ });
26
+ it('merges with existing metadata', async () => {
27
+ await (0, metadataFile_1.writeMetadataFile)(tmpDir, {
28
+ version: 1,
29
+ charts: { 'chart-a': '2026-01-01T00:00:00Z' },
30
+ dashboards: {},
31
+ });
32
+ await (0, metadataFile_1.writeMetadataFile)(tmpDir, {
33
+ version: 1,
34
+ charts: { 'chart-b': '2026-02-02T00:00:00Z' },
35
+ dashboards: { 'dash-a': '2026-02-02T00:00:00Z' },
36
+ });
37
+ expect(await (0, metadataFile_1.readMetadataFile)(tmpDir)).toEqual({
38
+ version: 1,
39
+ charts: {
40
+ 'chart-a': '2026-01-01T00:00:00Z',
41
+ 'chart-b': '2026-02-02T00:00:00Z',
42
+ },
43
+ dashboards: { 'dash-a': '2026-02-02T00:00:00Z' },
44
+ });
45
+ });
46
+ });