@lightdash/cli 1.54.0 → 1.55.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.
@@ -54,6 +54,11 @@ export declare const applySdkMirrorToTemplateDeps: (templateDependencies: Record
54
54
  * deps (payload stays identical to today's format).
55
55
  */
56
56
  export declare const attachDependenciesToCode: (code: DataAppCode, customDeps: Record<string, string>, deps: DataAppDependencies) => DataAppCode;
57
+ /**
58
+ * Mirrors the chart/dashboard rule: changed when a file that actually gets
59
+ * uploaded has drifted more than 30s from the manifest's downloadedAt.
60
+ */
61
+ export declare const appFolderNeedsUpdating: (dir: string, manifest: DataAppManifest) => Promise<boolean>;
57
62
  export declare const readManifestFromDir: (dir: string) => Promise<DataAppManifest>;
58
63
  export declare const readBundleFromDir: (dir: string) => Promise<DataAppCode>;
59
64
  //# sourceMappingURL=appCodeFiles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"appCodeFiles.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/appCodeFiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,wBAAwB,EAChC,MAAM,mBAAmB,CAAC;AAO3B;;;;GAIG;AACH,eAAO,MAAM,aAAa,SAChB,MAAM,WACH,MAAM,gBACD,GAAG,CAAC,MAAM,CAAC,KAC1B,MAcF,CAAC;AAEF,eAAO,MAAM,eAAe,QACnB,MAAM,SACJ,eAAe,EAAE,KACzB,OAAO,CAAC,IAAI,CAsBd,CAAC;AAEF,eAAO,MAAM,iBAAiB,QACrB,MAAM,WACF,cAAc,KACxB,OAAO,CAAC,IAAI,CASd,CAAC;AAEF,eAAO,MAAM,gBAAgB,QACpB,MAAM,QACL,WAAW,KAClB,OAAO,CAAC,IAAI,CAWd,CAAC;AAmCF,eAAO,MAAM,eAAe,SAClB,WAAW,qBACE,MAAM,QACnB;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,KACF,wBAqBF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,aACnB,eAAe,gBACX,GAAG,CAAC,MAAM,CAAC,KAC1B,MAgBF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAC1B,MAAM,QACL,mBAAmB,KAC1B,OAAO,CAAC,IAAI,CAGd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B,WAAW,EAAE,MAAM,CAAC;IAKpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,QAC3B,MAAM,KACZ,OAAO,CAAC,oBAAoB,GAAG,IAAI,CA6BrC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,eAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,wBACZ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7C,MAAM,EAOH,CAAC;AAEP;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,yBACf,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,eAC/B,MAAM,KACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAgBvB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,SAC3B,WAAW,cACL,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAC5B,mBAAmB,KAC1B,WAC4E,CAAC;AAEhF,eAAO,MAAM,mBAAmB,QACvB,MAAM,KACZ,OAAO,CAAC,eAAe,CAYzB,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAe,MAAM,KAAG,OAAO,CAAC,WAAW,CAYxE,CAAC"}
1
+ {"version":3,"file":"appCodeFiles.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/appCodeFiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,wBAAwB,EAChC,MAAM,mBAAmB,CAAC;AAO3B;;;;GAIG;AACH,eAAO,MAAM,aAAa,SAChB,MAAM,WACH,MAAM,gBACD,GAAG,CAAC,MAAM,CAAC,KAC1B,MAcF,CAAC;AAEF,eAAO,MAAM,eAAe,QACnB,MAAM,SACJ,eAAe,EAAE,KACzB,OAAO,CAAC,IAAI,CAsBd,CAAC;AAEF,eAAO,MAAM,iBAAiB,QACrB,MAAM,WACF,cAAc,KACxB,OAAO,CAAC,IAAI,CASd,CAAC;AAEF,eAAO,MAAM,gBAAgB,QACpB,MAAM,QACL,WAAW,KAClB,OAAO,CAAC,IAAI,CAWd,CAAC;AAmCF,eAAO,MAAM,eAAe,SAClB,WAAW,qBACE,MAAM,QACnB;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,KACF,wBAqBF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,aACnB,eAAe,gBACX,GAAG,CAAC,MAAM,CAAC,KAC1B,MAgBF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAC1B,MAAM,QACL,mBAAmB,KAC1B,OAAO,CAAC,IAAI,CAGd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B,WAAW,EAAE,MAAM,CAAC;IAKpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,QAC3B,MAAM,KACZ,OAAO,CAAC,oBAAoB,GAAG,IAAI,CA6BrC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,eAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,wBACZ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7C,MAAM,EAOH,CAAC;AAEP;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,yBACf,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,eAC/B,MAAM,KACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAgBvB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,SAC3B,WAAW,cACL,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAC5B,mBAAmB,KAC1B,WAC4E,CAAC;AAuBhF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAC1B,MAAM,YACD,eAAe,KAC1B,OAAO,CAAC,OAAO,CAqBjB,CAAC;AAEF,eAAO,MAAM,mBAAmB,QACvB,MAAM,KACZ,OAAO,CAAC,eAAe,CAYzB,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAe,MAAM,KAAG,OAAO,CAAC,WAAW,CAYxE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readBundleFromDir = exports.readManifestFromDir = exports.attachDependenciesToCode = exports.applySdkMirrorToTemplateDeps = exports.buildDepsWarningLines = exports.readDependenciesFromDir = exports.writeDependenciesToDir = exports.resolveAppFolderName = exports.buildImportBody = exports.writeBundleToDir = exports.writeContextToDir = exports.writeFilesToDir = exports.appFolderName = void 0;
3
+ exports.readBundleFromDir = exports.readManifestFromDir = exports.appFolderNeedsUpdating = exports.attachDependenciesToCode = exports.applySdkMirrorToTemplateDeps = exports.buildDepsWarningLines = exports.readDependenciesFromDir = exports.writeDependenciesToDir = exports.resolveAppFolderName = exports.buildImportBody = exports.writeBundleToDir = exports.writeContextToDir = exports.writeFilesToDir = exports.appFolderName = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const common_1 = require("@lightdash/common");
6
6
  const fs_1 = require("fs");
@@ -210,6 +210,46 @@ exports.applySdkMirrorToTemplateDeps = applySdkMirrorToTemplateDeps;
210
210
  */
211
211
  const attachDependenciesToCode = (code, customDeps, deps) => Object.keys(customDeps).length > 0 ? { ...code, dependencies: deps } : code;
212
212
  exports.attachDependenciesToCode = attachDependenciesToCode;
213
+ const CHANGE_TOLERANCE_MS = 30_000;
214
+ const collectMtimes = async (dir) => {
215
+ const entries = await fs_1.promises.readdir(dir, { withFileTypes: true });
216
+ const nested = await Promise.all(entries.map(async (entry) => {
217
+ const entryPath = path.join(dir, entry.name);
218
+ if (entry.isDirectory())
219
+ return collectMtimes(entryPath);
220
+ const stats = await fs_1.promises.stat(entryPath);
221
+ return [stats.mtime.getTime()];
222
+ }));
223
+ return nested.flat();
224
+ };
225
+ const statMtime = async (file) => fs_1.promises
226
+ .stat(file)
227
+ .then((stats) => [stats.mtime.getTime()])
228
+ .catch(() => []);
229
+ /**
230
+ * Mirrors the chart/dashboard rule: changed when a file that actually gets
231
+ * uploaded has drifted more than 30s from the manifest's downloadedAt.
232
+ */
233
+ const appFolderNeedsUpdating = async (dir, manifest) => {
234
+ if (!manifest.downloadedAt)
235
+ return true;
236
+ const downloadedAt = new Date(manifest.downloadedAt).getTime();
237
+ if (Number.isNaN(downloadedAt))
238
+ return true;
239
+ const srcDir = path.join(dir, 'src');
240
+ const srcExists = await fs_1.promises
241
+ .stat(srcDir)
242
+ .then((s) => s.isDirectory())
243
+ .catch(() => false);
244
+ const mtimes = [
245
+ ...(srcExists ? await collectMtimes(srcDir) : []),
246
+ ...(await statMtime(path.join(dir, 'package.json'))),
247
+ ...(await statMtime(path.join(dir, 'pnpm-lock.yaml'))),
248
+ ...(await statMtime(path.join(dir, MANIFEST_FILENAME))),
249
+ ];
250
+ return mtimes.some((mtime) => Math.abs(mtime - downloadedAt) > CHANGE_TOLERANCE_MS);
251
+ };
252
+ exports.appFolderNeedsUpdating = appFolderNeedsUpdating;
213
253
  const readManifestFromDir = async (dir) => {
214
254
  const manifestRaw = await fs_1.promises.readFile(path.join(dir, MANIFEST_FILENAME), 'utf-8');
215
255
  try {
@@ -349,3 +349,63 @@ describe('applySdkMirrorToTemplateDeps', () => {
349
349
  expect((0, appCodeFiles_1.applySdkMirrorToTemplateDeps)(template, 'not-json')).toEqual(template);
350
350
  });
351
351
  });
352
+ // ─── appFolderNeedsUpdating ────────────────────────────────────────────────────
353
+ describe('appFolderNeedsUpdating', () => {
354
+ const DOWNLOADED_AT = '2026-07-30T12:00:00.000Z';
355
+ const manifest = { downloadedAt: DOWNLOADED_AT };
356
+ const buildFolder = () => {
357
+ const dir = (0, fs_1.mkdtempSync)(path.join(os.tmpdir(), 'ld-app-folder-'));
358
+ (0, fs_1.mkdirSync)(path.join(dir, 'src'), { recursive: true });
359
+ (0, fs_1.writeFileSync)(path.join(dir, 'src', 'App.tsx'), 'x');
360
+ (0, fs_1.writeFileSync)(path.join(dir, 'lightdash-app.yml'), 'slug: x');
361
+ (0, fs_1.mkdirSync)(path.join(dir, '.lightdash', 'context'), {
362
+ recursive: true,
363
+ });
364
+ (0, fs_1.writeFileSync)(path.join(dir, '.lightdash', 'context', 'semantic-layer.yml'), 'models: []');
365
+ const at = new Date(DOWNLOADED_AT);
366
+ [
367
+ path.join(dir, 'src', 'App.tsx'),
368
+ path.join(dir, 'lightdash-app.yml'),
369
+ path.join(dir, '.lightdash', 'context', 'semantic-layer.yml'),
370
+ ].forEach((file) => (0, fs_1.utimesSync)(file, at, at));
371
+ return dir;
372
+ };
373
+ it('is false for a freshly downloaded folder', async () => {
374
+ await expect((0, appCodeFiles_1.appFolderNeedsUpdating)(buildFolder(), manifest)).resolves.toBe(false);
375
+ });
376
+ it('is true when a src file was edited', async () => {
377
+ const dir = buildFolder();
378
+ const later = new Date('2026-07-30T13:00:00.000Z');
379
+ (0, fs_1.utimesSync)(path.join(dir, 'src', 'App.tsx'), later, later);
380
+ await expect((0, appCodeFiles_1.appFolderNeedsUpdating)(dir, manifest)).resolves.toBe(true);
381
+ });
382
+ it('is true when the manifest was edited', async () => {
383
+ const dir = buildFolder();
384
+ const later = new Date('2026-07-30T13:00:00.000Z');
385
+ (0, fs_1.utimesSync)(path.join(dir, 'lightdash-app.yml'), later, later);
386
+ await expect((0, appCodeFiles_1.appFolderNeedsUpdating)(dir, manifest)).resolves.toBe(true);
387
+ });
388
+ it('ignores regenerated context files', async () => {
389
+ const dir = buildFolder();
390
+ const later = new Date('2026-07-30T13:00:00.000Z');
391
+ (0, fs_1.utimesSync)(path.join(dir, '.lightdash', 'context', 'semantic-layer.yml'), later, later);
392
+ await expect((0, appCodeFiles_1.appFolderNeedsUpdating)(dir, manifest)).resolves.toBe(false);
393
+ });
394
+ it('is true when downloadedAt is missing', async () => {
395
+ await expect((0, appCodeFiles_1.appFolderNeedsUpdating)(buildFolder(), {})).resolves.toBe(true);
396
+ });
397
+ it('is true when downloadedAt is unparseable', async () => {
398
+ await expect((0, appCodeFiles_1.appFolderNeedsUpdating)(buildFolder(), {
399
+ downloadedAt: 'not-a-date',
400
+ })).resolves.toBe(true);
401
+ });
402
+ it('is true when a nested src file was edited', async () => {
403
+ const dir = buildFolder();
404
+ (0, fs_1.mkdirSync)(path.join(dir, 'src', 'components'), { recursive: true });
405
+ const nested = path.join(dir, 'src', 'components', 'Chart.tsx');
406
+ (0, fs_1.writeFileSync)(nested, 'x');
407
+ const later = new Date('2026-07-30T13:00:00.000Z');
408
+ (0, fs_1.utimesSync)(nested, later, later);
409
+ await expect((0, appCodeFiles_1.appFolderNeedsUpdating)(dir, manifest)).resolves.toBe(true);
410
+ });
411
+ });
@@ -61,6 +61,16 @@ export declare const capListedApps: (listedUuids: string[], limit: number) => {
61
61
  appUuids: string[];
62
62
  truncatedCount: number;
63
63
  };
64
+ /**
65
+ * Data apps a dashboard needs that aren't already covered by an explicit
66
+ * --apps ref, or by a (possibly --apps-limit-truncated) --include-apps listing.
67
+ */
68
+ export declare const computeLinkedAppSlugs: (args: {
69
+ appSlugs: string[];
70
+ explicitRefs: Set<string>;
71
+ includeApps: boolean;
72
+ cappedAppSlugs: Set<string>;
73
+ }) => string[];
64
74
  /**
65
75
  * Pre-context servers return a download payload without `context`.
66
76
  */
@@ -73,6 +83,25 @@ export declare const preSlugUploadHint: (args: {
73
83
  folder: string;
74
84
  slug: string | undefined;
75
85
  }) => string;
86
+ export type AppPresence = {
87
+ kind: 'known';
88
+ slugs: Set<string>;
89
+ } | {
90
+ kind: 'unknown';
91
+ targetProjectUuid: string;
92
+ };
93
+ /**
94
+ * Auto-pushed apps normally upload only when the folder changed, because every
95
+ * upload triggers a sandbox rebuild. The exception is an app the target project
96
+ * does not have yet — without it a dashboard moved to a new project or instance
97
+ * lands with its tile skipped.
98
+ */
99
+ export declare const shouldAutoPushApp: (args: {
100
+ manifest: Pick<DataAppManifest, 'slug' | 'projectUuid'>;
101
+ presence: AppPresence;
102
+ folderChanged: boolean;
103
+ force: boolean;
104
+ }) => boolean;
76
105
  export type AppDownloadFailure = {
77
106
  appRef: string;
78
107
  message: string;
@@ -106,4 +135,24 @@ export declare const appsDownloadSummary: (successCount: number, total: number,
106
135
  message: string;
107
136
  failureLines: string[];
108
137
  };
138
+ export type AppsDownloadOutcome = {
139
+ successCount: number;
140
+ skippedNotBuiltCount: number;
141
+ failures: AppDownloadFailure[];
142
+ };
143
+ /**
144
+ * Downloads each app to its own folder under appsDir, classifying "no built
145
+ * version" 404s as skips rather than failures. Everything the loop needs
146
+ * (fetching, versioning, progress reporting) is injected so this stays free
147
+ * of any dependency on the download handler.
148
+ */
149
+ export declare const downloadAppsToDir: (args: {
150
+ appRefs: string[];
151
+ projectId: string;
152
+ appsDir: string;
153
+ takenFolders: Set<string>;
154
+ cliVersion: string;
155
+ fetchApp: (projectId: string, appRef: string) => Promise<DataAppCodeDownload>;
156
+ onProgress?: (processed: number, total: number) => void;
157
+ }) => Promise<AppsDownloadOutcome>;
109
158
  //# sourceMappingURL=appsDownload.d.ts.map
@@ -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":"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"}
@@ -1,8 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.appsDownloadSummary = exports.shouldWarnAllSkipped = exports.computeUpsertedTotal = exports.classifyAppDownloadError = exports.preSlugUploadHint = exports.ensureDownloadedAppContext = exports.capListedApps = exports.selectAppsToDownload = exports.shouldFallBackToSpaceScopedListing = exports.resolveAppsLimit = exports.preSlugServerHint = exports.unmatchedUploadRefsWarning = exports.matchedUploadRefs = exports.uploadFilterMatches = exports.getDataAppUploadFilter = exports.getDataAppReference = exports.DEFAULT_APPS_LIMIT = void 0;
3
+ exports.downloadAppsToDir = exports.appsDownloadSummary = exports.shouldWarnAllSkipped = exports.computeUpsertedTotal = exports.classifyAppDownloadError = exports.shouldAutoPushApp = exports.preSlugUploadHint = exports.ensureDownloadedAppContext = exports.computeLinkedAppSlugs = exports.capListedApps = exports.selectAppsToDownload = exports.shouldFallBackToSpaceScopedListing = exports.resolveAppsLimit = exports.preSlugServerHint = exports.unmatchedUploadRefsWarning = exports.matchedUploadRefs = exports.uploadFilterMatches = exports.getDataAppUploadFilter = exports.getDataAppReference = exports.DEFAULT_APPS_LIMIT = void 0;
4
+ const tslib_1 = require("tslib");
4
5
  const common_1 = require("@lightdash/common");
6
+ const path = tslib_1.__importStar(require("path"));
5
7
  const uuid_1 = require("uuid");
8
+ const globalState_1 = tslib_1.__importDefault(require("../../globalState"));
9
+ const styles = tslib_1.__importStar(require("../../styles"));
10
+ const appCodeFiles_1 = require("./appCodeFiles");
11
+ const scaffolding_1 = require("./scaffolding");
6
12
  exports.DEFAULT_APPS_LIMIT = 50;
7
13
  const getDataAppReference = (reference) => {
8
14
  let url;
@@ -106,6 +112,16 @@ const capListedApps = (listedUuids, limit) => ({
106
112
  truncatedCount: Math.max(0, listedUuids.length - limit),
107
113
  });
108
114
  exports.capListedApps = capListedApps;
115
+ /**
116
+ * Data apps a dashboard needs that aren't already covered by an explicit
117
+ * --apps ref, or by a (possibly --apps-limit-truncated) --include-apps listing.
118
+ */
119
+ const computeLinkedAppSlugs = (args) => {
120
+ const { appSlugs, explicitRefs, includeApps, cappedAppSlugs } = args;
121
+ return appSlugs.filter((slug) => !explicitRefs.has(slug) &&
122
+ !(includeApps && cappedAppSlugs.has(slug)));
123
+ };
124
+ exports.computeLinkedAppSlugs = computeLinkedAppSlugs;
109
125
  /**
110
126
  * Pre-context servers return a download payload without `context`.
111
127
  */
@@ -124,6 +140,23 @@ const preSlugUploadHint = (args) => `${args.folder}/lightdash-app.yml predates s
124
140
  ? ` (or add \`slug: ${args.slug}\` to lightdash-app.yml)`
125
141
  : ''}. Uploads keep working via uuid matching meanwhile.`;
126
142
  exports.preSlugUploadHint = preSlugUploadHint;
143
+ /**
144
+ * Auto-pushed apps normally upload only when the folder changed, because every
145
+ * upload triggers a sandbox rebuild. The exception is an app the target project
146
+ * does not have yet — without it a dashboard moved to a new project or instance
147
+ * lands with its tile skipped.
148
+ */
149
+ const shouldAutoPushApp = (args) => {
150
+ if (args.force || args.folderChanged)
151
+ return true;
152
+ if (args.manifest.slug === undefined)
153
+ return true;
154
+ if (args.presence.kind === 'known') {
155
+ return !args.presence.slugs.has(args.manifest.slug);
156
+ }
157
+ return args.manifest.projectUuid !== args.presence.targetProjectUuid;
158
+ };
159
+ exports.shouldAutoPushApp = shouldAutoPushApp;
127
160
  /**
128
161
  * A 404 saying the app has no ready version means it exists but never
129
162
  * finished a successful build — nothing to download, so it's a skip, not a
@@ -185,3 +218,59 @@ const appsDownloadSummary = (successCount, total, failures, appsDir, skippedNotB
185
218
  };
186
219
  };
187
220
  exports.appsDownloadSummary = appsDownloadSummary;
221
+ /**
222
+ * Downloads each app to its own folder under appsDir, classifying "no built
223
+ * version" 404s as skips rather than failures. Everything the loop needs
224
+ * (fetching, versioning, progress reporting) is injected so this stays free
225
+ * of any dependency on the download handler.
226
+ */
227
+ const downloadAppsToDir = async (args) => {
228
+ const { appRefs, projectId, appsDir, takenFolders, cliVersion, fetchApp, onProgress, } = args;
229
+ let successCount = 0;
230
+ let skippedNotBuiltCount = 0;
231
+ const failures = [];
232
+ for (const appRef of appRefs) {
233
+ try {
234
+ const code = (0, exports.ensureDownloadedAppContext)(appRef,
235
+ // eslint-disable-next-line no-await-in-loop
236
+ await fetchApp(projectId, appRef));
237
+ const folder = (0, appCodeFiles_1.resolveAppFolderName)(code.manifest, takenFolders);
238
+ takenFolders.add(folder);
239
+ const appDir = path.join(appsDir, folder);
240
+ const manifest = {
241
+ ...code.manifest,
242
+ scaffoldingVersion: cliVersion,
243
+ };
244
+ // eslint-disable-next-line no-await-in-loop
245
+ await (0, appCodeFiles_1.writeBundleToDir)(appDir, { ...code, manifest });
246
+ // eslint-disable-next-line no-await-in-loop
247
+ await (0, appCodeFiles_1.writeFilesToDir)(appDir, (0, scaffolding_1.buildStaticAuthoringFiles)({
248
+ appName: code.manifest.name,
249
+ sdkVersion: cliVersion,
250
+ }));
251
+ // Server-provided deps override the scaffold's
252
+ // template package.json so re-uploads round-trip.
253
+ if (code.dependencies) {
254
+ // eslint-disable-next-line no-await-in-loop
255
+ await (0, appCodeFiles_1.writeDependenciesToDir)(appDir, code.dependencies);
256
+ }
257
+ // eslint-disable-next-line no-await-in-loop
258
+ await (0, appCodeFiles_1.writeContextToDir)(appDir, code.context);
259
+ successCount += 1;
260
+ }
261
+ catch (appErr) {
262
+ const outcome = (0, exports.classifyAppDownloadError)(appErr);
263
+ if (outcome.kind === 'skip-not-built') {
264
+ skippedNotBuiltCount += 1;
265
+ globalState_1.default.debug(`> Skipped app ${appRef}: no built version to download`);
266
+ }
267
+ else {
268
+ failures.push({ appRef, message: outcome.message });
269
+ globalState_1.default.log(styles.error(`Failed to download app ${appRef}: ${outcome.message}`));
270
+ }
271
+ }
272
+ onProgress?.(successCount + skippedNotBuiltCount + failures.length, appRefs.length);
273
+ }
274
+ return { successCount, skippedNotBuiltCount, failures };
275
+ };
276
+ exports.downloadAppsToDir = downloadAppsToDir;
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
3
4
  const common_1 = require("@lightdash/common");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const os = tslib_1.__importStar(require("os"));
7
+ const path = tslib_1.__importStar(require("path"));
4
8
  const appsDownload_1 = require("./appsDownload");
5
9
  describe('getDataAppReference', () => {
6
10
  const appUuid = 'd3afc44c-6f0f-4d9f-a267-fb739efa31dd';
@@ -158,6 +162,59 @@ describe('capListedApps', () => {
158
162
  });
159
163
  });
160
164
  });
165
+ describe('computeLinkedAppSlugs', () => {
166
+ it('returns dashboard-referenced slugs unchanged when apps are not opted in at all', () => {
167
+ expect((0, appsDownload_1.computeLinkedAppSlugs)({
168
+ appSlugs: ['revenue-explorer', 'sales-dash'],
169
+ explicitRefs: new Set(),
170
+ includeApps: false,
171
+ cappedAppSlugs: new Set(),
172
+ })).toEqual(['revenue-explorer', 'sales-dash']);
173
+ });
174
+ it('excludes slugs already covered by an explicit --apps ref', () => {
175
+ expect((0, appsDownload_1.computeLinkedAppSlugs)({
176
+ appSlugs: ['revenue-explorer', 'sales-dash'],
177
+ explicitRefs: new Set(['revenue-explorer']),
178
+ includeApps: false,
179
+ cappedAppSlugs: new Set(),
180
+ })).toEqual(['sales-dash']);
181
+ });
182
+ it('excludes everything already inside an untruncated --include-apps listing', () => {
183
+ expect((0, appsDownload_1.computeLinkedAppSlugs)({
184
+ appSlugs: ['revenue-explorer', 'sales-dash'],
185
+ explicitRefs: new Set(),
186
+ includeApps: true,
187
+ cappedAppSlugs: new Set(['revenue-explorer', 'sales-dash']),
188
+ })).toEqual([]);
189
+ });
190
+ // PROD-9089 regression: --include-apps caps its listing at --apps-limit,
191
+ // so a dashboard's app can fall outside the capped set. It must still be
192
+ // downloaded via the linked step rather than silently dropped.
193
+ it('keeps slugs that fell outside a truncated --include-apps listing', () => {
194
+ expect((0, appsDownload_1.computeLinkedAppSlugs)({
195
+ appSlugs: ['revenue-explorer', 'sales-dash'],
196
+ explicitRefs: new Set(),
197
+ includeApps: true,
198
+ cappedAppSlugs: new Set(['sales-dash']),
199
+ })).toEqual(['revenue-explorer']);
200
+ });
201
+ it('an explicit ref wins over include-apps capping (never double counted)', () => {
202
+ expect((0, appsDownload_1.computeLinkedAppSlugs)({
203
+ appSlugs: ['revenue-explorer'],
204
+ explicitRefs: new Set(['revenue-explorer']),
205
+ includeApps: true,
206
+ cappedAppSlugs: new Set(),
207
+ })).toEqual([]);
208
+ });
209
+ it('returns an empty array when no dashboards reference any app', () => {
210
+ expect((0, appsDownload_1.computeLinkedAppSlugs)({
211
+ appSlugs: [],
212
+ explicitRefs: new Set(['other-app']),
213
+ includeApps: true,
214
+ cappedAppSlugs: new Set(),
215
+ })).toEqual([]);
216
+ });
217
+ });
161
218
  describe('resolveAppsLimit', () => {
162
219
  it('defaults to DEFAULT_APPS_LIMIT when the flag is not passed', () => {
163
220
  expect(appsDownload_1.DEFAULT_APPS_LIMIT).toBe(50);
@@ -400,3 +457,177 @@ describe('appsDownloadSummary', () => {
400
457
  expect(summary.message).toContain('1 failed');
401
458
  });
402
459
  });
460
+ describe('downloadAppsToDir', () => {
461
+ const tmpDir = () => fs.mkdtempSync(path.join(os.tmpdir(), 'ld-apps-download-'));
462
+ const codeFor = (slug) => ({
463
+ manifest: {
464
+ codeVersion: 1,
465
+ projectUuid: 'project-uuid',
466
+ slug,
467
+ version: 1,
468
+ name: slug,
469
+ description: '',
470
+ template: null,
471
+ downloadedAt: '2026-07-30T00:00:00.000Z',
472
+ },
473
+ files: [
474
+ {
475
+ path: 'src/App.tsx',
476
+ contentBase64: Buffer.from('export default () => null;').toString('base64'),
477
+ },
478
+ ],
479
+ context: {
480
+ semanticLayer: {
481
+ path: '.lightdash/context/semantic-layer.yml',
482
+ contentBase64: Buffer.from('models: []').toString('base64'),
483
+ },
484
+ parameters: null,
485
+ promptHistory: {
486
+ path: '.lightdash/context/prompt-history.md',
487
+ contentBase64: Buffer.from('# history').toString('base64'),
488
+ },
489
+ theme: { instructions: null, assets: [], skippedAssetCount: 0 },
490
+ },
491
+ });
492
+ it('writes one folder per app and counts successes', async () => {
493
+ const appsDir = tmpDir();
494
+ const outcome = await (0, appsDownload_1.downloadAppsToDir)({
495
+ appRefs: ['revenue-explorer'],
496
+ projectId: 'project-uuid',
497
+ appsDir,
498
+ takenFolders: new Set(),
499
+ cliVersion: '0.0.0-test',
500
+ fetchApp: async (_p, ref) => codeFor(ref),
501
+ });
502
+ expect(outcome).toEqual({
503
+ successCount: 1,
504
+ skippedNotBuiltCount: 0,
505
+ failures: [],
506
+ });
507
+ expect(fs.existsSync(path.join(appsDir, 'revenue-explorer', 'lightdash-app.yml'))).toBe(true);
508
+ });
509
+ it('writes server-provided dependencies over the scaffold template package.json', async () => {
510
+ const appsDir = tmpDir();
511
+ const packageJson = JSON.stringify({
512
+ name: 'server-provided',
513
+ dependencies: {},
514
+ });
515
+ const lockfile = 'lockfileVersion: 6.0\nserver: provided\n';
516
+ const outcome = await (0, appsDownload_1.downloadAppsToDir)({
517
+ appRefs: ['deps-app'],
518
+ projectId: 'project-uuid',
519
+ appsDir,
520
+ takenFolders: new Set(),
521
+ cliVersion: '0.0.0-test',
522
+ fetchApp: async (_p, ref) => ({
523
+ ...codeFor(ref),
524
+ dependencies: { packageJson, lockfile },
525
+ }),
526
+ });
527
+ expect(outcome.successCount).toBe(1);
528
+ const writtenPackageJson = fs.readFileSync(path.join(appsDir, 'deps-app', 'package.json'), 'utf-8');
529
+ expect(writtenPackageJson).toBe(packageJson);
530
+ const writtenLockfile = fs.readFileSync(path.join(appsDir, 'deps-app', 'pnpm-lock.yaml'), 'utf-8');
531
+ expect(writtenLockfile).toBe(lockfile);
532
+ });
533
+ it('counts an app with no ready version as skipped, not failed', async () => {
534
+ const outcome = await (0, appsDownload_1.downloadAppsToDir)({
535
+ appRefs: ['half-built'],
536
+ projectId: 'project-uuid',
537
+ appsDir: tmpDir(),
538
+ takenFolders: new Set(),
539
+ cliVersion: '0.0.0-test',
540
+ fetchApp: async () => {
541
+ throw new common_1.LightdashError({
542
+ message: 'App has no ready version to download',
543
+ name: 'NotFoundError',
544
+ statusCode: 404,
545
+ data: {},
546
+ });
547
+ },
548
+ });
549
+ expect(outcome.skippedNotBuiltCount).toBe(1);
550
+ expect(outcome.failures).toEqual([]);
551
+ });
552
+ });
553
+ describe('shouldAutoPushApp', () => {
554
+ const manifest = {
555
+ slug: 'revenue-explorer',
556
+ projectUuid: 'source-project',
557
+ };
558
+ const known = (...slugs) => ({
559
+ kind: 'known',
560
+ slugs: new Set(slugs),
561
+ });
562
+ it('pushes when the target project does not have the app', () => {
563
+ expect((0, appsDownload_1.shouldAutoPushApp)({
564
+ manifest,
565
+ presence: known(),
566
+ folderChanged: false,
567
+ force: false,
568
+ })).toBe(true);
569
+ });
570
+ it('pushes when the app exists and the folder changed', () => {
571
+ expect((0, appsDownload_1.shouldAutoPushApp)({
572
+ manifest,
573
+ presence: known('revenue-explorer'),
574
+ folderChanged: true,
575
+ force: false,
576
+ })).toBe(true);
577
+ });
578
+ it('skips when the app exists and nothing changed', () => {
579
+ expect((0, appsDownload_1.shouldAutoPushApp)({
580
+ manifest,
581
+ presence: known('revenue-explorer'),
582
+ folderChanged: false,
583
+ force: false,
584
+ })).toBe(false);
585
+ });
586
+ it('pushes an unchanged existing app under --force', () => {
587
+ expect((0, appsDownload_1.shouldAutoPushApp)({
588
+ manifest,
589
+ presence: known('revenue-explorer'),
590
+ folderChanged: false,
591
+ force: true,
592
+ })).toBe(true);
593
+ });
594
+ it('falls back to the project comparison when presence is unknown', () => {
595
+ expect((0, appsDownload_1.shouldAutoPushApp)({
596
+ manifest,
597
+ presence: {
598
+ kind: 'unknown',
599
+ targetProjectUuid: 'other-project',
600
+ },
601
+ folderChanged: false,
602
+ force: false,
603
+ })).toBe(true);
604
+ expect((0, appsDownload_1.shouldAutoPushApp)({
605
+ manifest,
606
+ presence: {
607
+ kind: 'unknown',
608
+ targetProjectUuid: 'source-project',
609
+ },
610
+ folderChanged: false,
611
+ force: false,
612
+ })).toBe(false);
613
+ });
614
+ it('pushes a slug-less manifest so a pre-slug bundle still lands', () => {
615
+ expect((0, appsDownload_1.shouldAutoPushApp)({
616
+ manifest: { slug: undefined, projectUuid: 'source-project' },
617
+ presence: known('revenue-explorer'),
618
+ folderChanged: false,
619
+ force: false,
620
+ })).toBe(true);
621
+ });
622
+ it('pushes a slug-less manifest against an older server with unknown presence', () => {
623
+ expect((0, appsDownload_1.shouldAutoPushApp)({
624
+ manifest: { slug: undefined, projectUuid: 'source-project' },
625
+ presence: {
626
+ kind: 'unknown',
627
+ targetProjectUuid: 'source-project',
628
+ },
629
+ folderChanged: false,
630
+ force: false,
631
+ })).toBe(true);
632
+ });
633
+ });
@@ -59,7 +59,15 @@ type MetadataEntry = {
59
59
  };
60
60
  declare const sanitizeChartForDownload: (chart: ChartAsCode, stripPivotSeries: boolean) => ChartAsCode;
61
61
  type DownloadContentType = 'charts' | 'dashboards' | 'sqlCharts';
62
- export declare const downloadContent: (ids: string[], type: DownloadContentType, projectId: string, projectName: string, customPath?: string, languageMap?: boolean, nested?: boolean, skipSpaces?: boolean, stripPivotSeries?: boolean, rootSpaces?: boolean, onProgress?: (detail: string) => void) => Promise<[number, string[], MetadataEntry[], SpaceAsCode[]]>;
62
+ declare const extractAppSlugsFromDashboards: (dashboards: DashboardAsCode[]) => string[];
63
+ export type DownloadContentResult = {
64
+ total: number;
65
+ chartSlugs: string[];
66
+ appSlugs: string[];
67
+ metadataEntries: MetadataEntry[];
68
+ spaces: SpaceAsCode[];
69
+ };
70
+ export declare const downloadContent: (ids: string[], type: DownloadContentType, projectId: string, projectName: string, customPath?: string, languageMap?: boolean, nested?: boolean, skipSpaces?: boolean, stripPivotSeries?: boolean, rootSpaces?: boolean, onProgress?: (detail: string) => void) => Promise<DownloadContentResult>;
63
71
  declare const upsertVirtualViews: (projectId: string, slugs: string[], changes: Record<string, number>, force: boolean, canUpload: boolean, customPath?: string) => Promise<Record<string, number>>;
64
72
  export declare const getExternalConnectionSecretEnvVar: (slug: string) => string;
65
73
  /**
@@ -84,16 +92,23 @@ declare const summarizeUploadChanges: (before: Record<string, number>, after: Re
84
92
  declare const getDashboardChartSlugs: (dashboardSlugs: string[], customPath?: string, looseDashboards?: (DashboardAsCode & {
85
93
  needsUpdating: boolean;
86
94
  })[]) => Promise<string[]>;
95
+ declare const getDashboardAppSlugs: (dashboardSlugs: string[], customPath?: string, looseDashboards?: (DashboardAsCode & {
96
+ needsUpdating: boolean;
97
+ })[]) => Promise<string[]>;
98
+ declare const isFilteredWithNoDashboards: (hasFilters: boolean, dashboardSlugs: string[]) => boolean;
87
99
  export declare const uploadHandler: (options: DownloadHandlerOptions) => Promise<void>;
88
100
  export declare const testHelpers: {
89
101
  assertUniqueSpacePaths: typeof assertUniqueSpacePaths;
90
102
  downloadSpaces: typeof downloadSpaces;
103
+ extractAppSlugsFromDashboards: typeof extractAppSlugsFromDashboards;
91
104
  getFlatSpaceFileNames: typeof getFlatSpaceFileNames;
105
+ getDashboardAppSlugs: typeof getDashboardAppSlugs;
92
106
  getDashboardChartSlugs: typeof getDashboardChartSlugs;
93
107
  hasContentFilters: typeof hasContentFilters;
94
108
  isAiAgentsUnavailableError: typeof isAiAgentsUnavailableError;
95
109
  isExternalConnectionsUnavailableError: typeof isExternalConnectionsUnavailableError;
96
110
  downloadAiAgents: typeof downloadAiAgents;
111
+ isFilteredWithNoDashboards: typeof isFilteredWithNoDashboards;
97
112
  readAiAgentFiles: typeof readAiAgentFiles;
98
113
  readExternalConnectionFiles: typeof readExternalConnectionFiles;
99
114
  readSpaceFiles: typeof readSpaceFiles;
@@ -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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"}