@lightdash/cli 1.55.0 → 1.56.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.
@@ -0,0 +1,32 @@
1
+ export declare const APP_BUILD_WAIT_BUDGET_MS: number;
2
+ export declare const APP_BUILD_WAIT_INITIAL_DELAY_MS: number;
3
+ export declare const APP_BUILD_WAIT_MAX_DELAY_MS: number;
4
+ /**
5
+ * Shared wait budget for one upload run. Only time spent sleeping on the
6
+ * build cap is deducted, and every accepted upload refills it — so the budget
7
+ * bounds consecutive unproductive waiting (a stuck build queue), not the
8
+ * total duration of a large healthy run. Once exhausted, remaining apps fail
9
+ * fast on 429 exactly as they did before retries existed.
10
+ */
11
+ export type BuildLimitWaitState = {
12
+ remainingMs: () => number;
13
+ spend: (ms: number) => void;
14
+ refill: () => void;
15
+ };
16
+ export declare const createBuildLimitWaitState: (budgetMs?: number) => BuildLimitWaitState;
17
+ export declare const isBuildLimitError: (err: unknown) => boolean;
18
+ /**
19
+ * Runs `post` and, when the server rejects it with the per-project build cap
20
+ * (HTTP 429), retries with capped exponential backoff until the shared wait
21
+ * budget runs out. Builds take minutes, so slots free up slowly — the backoff
22
+ * settles on a slow poll rather than hammering the endpoint. Any other error,
23
+ * or a 429 after the budget is spent, is rethrown to the caller's normal
24
+ * failure handling.
25
+ */
26
+ export declare const withBuildLimitRetry: <T>(post: () => Promise<T>, waitState: BuildLimitWaitState, opts: {
27
+ onWait: (attempt: number, delayMs: number) => void;
28
+ initialDelayMs?: number;
29
+ maxDelayMs?: number;
30
+ sleep?: (ms: number) => Promise<void>;
31
+ }) => Promise<T>;
32
+ //# sourceMappingURL=uploadRetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadRetry.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/uploadRetry.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,wBAAwB,QAAiB,CAAC;AACvD,eAAO,MAAM,+BAA+B,QAAW,CAAC;AACxD,eAAO,MAAM,2BAA2B,QAAY,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,WAAW,EAAE,MAAM,MAAM,CAAC;IAC1B,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,yBAAyB,cACxB,MAAM,KACjB,mBAWF,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAS,OAAO,KAAG,OACU,CAAC;AAO5D;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,GAAU,CAAC,QACjC,MAAM,OAAO,CAAC,CAAC,CAAC,aACX,mBAAmB,QACxB;IACF,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC,KACF,OAAO,CAAC,CAAC,CAwBX,CAAC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withBuildLimitRetry = exports.isBuildLimitError = exports.createBuildLimitWaitState = exports.APP_BUILD_WAIT_MAX_DELAY_MS = exports.APP_BUILD_WAIT_INITIAL_DELAY_MS = exports.APP_BUILD_WAIT_BUDGET_MS = void 0;
4
+ const common_1 = require("@lightdash/common");
5
+ exports.APP_BUILD_WAIT_BUDGET_MS = 10 * 60 * 1000;
6
+ exports.APP_BUILD_WAIT_INITIAL_DELAY_MS = 5 * 1000;
7
+ exports.APP_BUILD_WAIT_MAX_DELAY_MS = 30 * 1000;
8
+ const createBuildLimitWaitState = (budgetMs = exports.APP_BUILD_WAIT_BUDGET_MS) => {
9
+ let remainingMs = budgetMs;
10
+ return {
11
+ remainingMs: () => remainingMs,
12
+ spend: (ms) => {
13
+ remainingMs -= ms;
14
+ },
15
+ refill: () => {
16
+ remainingMs = budgetMs;
17
+ },
18
+ };
19
+ };
20
+ exports.createBuildLimitWaitState = createBuildLimitWaitState;
21
+ const isBuildLimitError = (err) => err instanceof common_1.LightdashError && err.statusCode === 429;
22
+ exports.isBuildLimitError = isBuildLimitError;
23
+ const defaultSleep = (ms) => new Promise((resolve) => {
24
+ setTimeout(resolve, ms);
25
+ });
26
+ /**
27
+ * Runs `post` and, when the server rejects it with the per-project build cap
28
+ * (HTTP 429), retries with capped exponential backoff until the shared wait
29
+ * budget runs out. Builds take minutes, so slots free up slowly — the backoff
30
+ * settles on a slow poll rather than hammering the endpoint. Any other error,
31
+ * or a 429 after the budget is spent, is rethrown to the caller's normal
32
+ * failure handling.
33
+ */
34
+ const withBuildLimitRetry = async (post, waitState, opts) => {
35
+ const maxDelayMs = opts.maxDelayMs ?? exports.APP_BUILD_WAIT_MAX_DELAY_MS;
36
+ const sleep = opts.sleep ?? defaultSleep;
37
+ let delayMs = opts.initialDelayMs ?? exports.APP_BUILD_WAIT_INITIAL_DELAY_MS;
38
+ let attempt = 0;
39
+ for (;;) {
40
+ try {
41
+ // eslint-disable-next-line no-await-in-loop
42
+ const result = await post();
43
+ waitState.refill();
44
+ return result;
45
+ }
46
+ catch (err) {
47
+ if (!(0, exports.isBuildLimitError)(err) || waitState.remainingMs() <= 0) {
48
+ throw err;
49
+ }
50
+ attempt += 1;
51
+ const waitMs = Math.min(delayMs, waitState.remainingMs());
52
+ opts.onWait(attempt, waitMs);
53
+ // eslint-disable-next-line no-await-in-loop
54
+ await sleep(waitMs);
55
+ waitState.spend(waitMs);
56
+ delayMs = Math.min(delayMs * 2, maxDelayMs);
57
+ }
58
+ }
59
+ };
60
+ exports.withBuildLimitRetry = withBuildLimitRetry;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=uploadRetry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadRetry.test.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/uploadRetry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@lightdash/common");
4
+ const uploadRetry_1 = require("./uploadRetry");
5
+ const buildLimitError = () => new common_1.LightdashError({
6
+ message: 'Too many app builds in progress for this project (5/5). Wait for some to finish and try again.',
7
+ name: 'TooManyRequestsError',
8
+ statusCode: 429,
9
+ data: {},
10
+ });
11
+ describe('withBuildLimitRetry', () => {
12
+ it('retries 429s with backoff until the upload is accepted and refills the budget', async () => {
13
+ const sleeps = [];
14
+ const sleep = vi.fn(async (ms) => {
15
+ sleeps.push(ms);
16
+ });
17
+ const onWait = vi.fn();
18
+ const post = vi
19
+ .fn()
20
+ .mockRejectedValueOnce(buildLimitError())
21
+ .mockRejectedValueOnce(buildLimitError())
22
+ .mockResolvedValue('accepted');
23
+ const waitState = (0, uploadRetry_1.createBuildLimitWaitState)(600_000);
24
+ waitState.spend(500_000); // partially spent by earlier apps
25
+ const result = await (0, uploadRetry_1.withBuildLimitRetry)(post, waitState, {
26
+ onWait,
27
+ initialDelayMs: 5_000,
28
+ maxDelayMs: 30_000,
29
+ sleep,
30
+ });
31
+ expect(result).toBe('accepted');
32
+ expect(post).toHaveBeenCalledTimes(3);
33
+ expect(sleeps).toEqual([5_000, 10_000]);
34
+ expect(onWait).toHaveBeenNthCalledWith(1, 1, 5_000);
35
+ expect(onWait).toHaveBeenNthCalledWith(2, 2, 10_000);
36
+ // Progress refills the shared budget for the remaining apps
37
+ expect(waitState.remainingMs()).toBe(600_000);
38
+ });
39
+ it('caps the backoff delay at maxDelayMs', async () => {
40
+ const sleeps = [];
41
+ const post = vi
42
+ .fn()
43
+ .mockRejectedValueOnce(buildLimitError())
44
+ .mockRejectedValueOnce(buildLimitError())
45
+ .mockRejectedValueOnce(buildLimitError())
46
+ .mockRejectedValueOnce(buildLimitError())
47
+ .mockResolvedValue('accepted');
48
+ await (0, uploadRetry_1.withBuildLimitRetry)(post, (0, uploadRetry_1.createBuildLimitWaitState)(600_000), {
49
+ onWait: vi.fn(),
50
+ initialDelayMs: 5_000,
51
+ maxDelayMs: 15_000,
52
+ sleep: async (ms) => {
53
+ sleeps.push(ms);
54
+ },
55
+ });
56
+ expect(sleeps).toEqual([5_000, 10_000, 15_000, 15_000]);
57
+ });
58
+ it('rethrows non-429 errors without sleeping', async () => {
59
+ const sleep = vi.fn();
60
+ const notFound = new common_1.LightdashError({
61
+ message: 'not found',
62
+ name: 'NotFoundError',
63
+ statusCode: 404,
64
+ data: {},
65
+ });
66
+ const post = vi.fn().mockRejectedValue(notFound);
67
+ await expect((0, uploadRetry_1.withBuildLimitRetry)(post, (0, uploadRetry_1.createBuildLimitWaitState)(), {
68
+ onWait: vi.fn(),
69
+ sleep,
70
+ })).rejects.toThrow('not found');
71
+ expect(post).toHaveBeenCalledTimes(1);
72
+ expect(sleep).not.toHaveBeenCalled();
73
+ });
74
+ it('rethrows the 429 once the wait budget is exhausted', async () => {
75
+ const sleeps = [];
76
+ const post = vi.fn().mockRejectedValue(buildLimitError());
77
+ const waitState = (0, uploadRetry_1.createBuildLimitWaitState)(12_000);
78
+ await expect((0, uploadRetry_1.withBuildLimitRetry)(post, waitState, {
79
+ onWait: vi.fn(),
80
+ initialDelayMs: 5_000,
81
+ maxDelayMs: 30_000,
82
+ sleep: async (ms) => {
83
+ sleeps.push(ms);
84
+ },
85
+ })).rejects.toThrow('Too many app builds in progress');
86
+ // 5s, then 10s capped to the 7s remaining, then the budget is spent
87
+ expect(sleeps).toEqual([5_000, 7_000]);
88
+ expect(waitState.remainingMs()).toBe(0);
89
+ expect(post).toHaveBeenCalledTimes(3);
90
+ });
91
+ it('fails fast on 429 when a previous app already exhausted the budget', async () => {
92
+ const sleep = vi.fn();
93
+ const post = vi.fn().mockRejectedValue(buildLimitError());
94
+ const waitState = (0, uploadRetry_1.createBuildLimitWaitState)(600_000);
95
+ waitState.spend(600_000);
96
+ await expect((0, uploadRetry_1.withBuildLimitRetry)(post, waitState, {
97
+ onWait: vi.fn(),
98
+ sleep,
99
+ })).rejects.toThrow('Too many app builds in progress');
100
+ expect(post).toHaveBeenCalledTimes(1);
101
+ expect(sleep).not.toHaveBeenCalled();
102
+ });
103
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/handlers/download.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,WAAW,EAyBX,WAAW,EAGX,eAAe,EAEf,wBAAwB,EAgBxB,KAAK,WAAW,EACnB,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAIH,KAAK,0BAA0B,EAClC,MAAM,iCAAiC,CAAC;AA6DzC,OAAO,EACH,sBAAsB,EAGtB,cAAc,EACd,qBAAqB,EAKrB,cAAc,EACd,cAAc,EACd,8BAA8B,EAC9B,yBAAyB,EACzB,YAAY,EACZ,qBAAqB,EACrB,eAAe,EAElB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,sBAAsB,GAAG;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uBAAuB,EAAE,OAAO,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uBAAuB,EAAE,OAAO,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,0BAA0B,EAAE,OAAO,CAAC;IACpC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,0BAA0B,EAAE,OAAO,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAIF,QAAA,MAAM,sBAAsB,qDAKzB,IAAI,CACH,sBAAsB,EACtB,YAAY,GAAG,eAAe,GAAG,QAAQ,GAAG,UAAU,CACzD,KAAG,OAEoE,CAAC;AAEzE,QAAA,MAAM,iBAAiB,oIAWpB,IAAI,CACH,sBAAsB,EACpB,YAAY,GACZ,QAAQ,GACR,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,qBAAqB,GACrB,cAAc,GACd,qBAAqB,GACrB,MAAM,CACX,KAAG,OAYuC,CAAC;AAgF5C,KAAK,aAAa,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,wBAAwB,UACnB,WAAW,oBACA,OAAO,KAC1B,WAQY,CAAC;AAgXhB,KAAK,mBAAmB,GAAG,QAAQ,GAAG,YAAY,GAAG,WAAW,CAAC;AAkDjE,QAAA,MAAM,6BAA6B,eACnB,eAAe,EAAE,KAC9B,MAAM,EAWR,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,MAAM,EAAE,WAAW,EAAE,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,eAAe,QACnB,MAAM,EAAE,QACP,mBAAmB,aACd,MAAM,eACJ,MAAM,eACN,MAAM,gBACN,OAAO,WACZ,OAAO,eACH,OAAO,qBACD,OAAO,eACb,OAAO,eACN,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,KACtC,OAAO,CAAC,qBAAqB,CAiJ/B,CAAC;AA8DF,QAAA,MAAM,kBAAkB,cACT,MAAM,SACV,MAAM,EAAE,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SACxB,OAAO,aACH,OAAO,eACL,MAAM,KACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA6DhC,CAAC;AAEF,eAAO,MAAM,iCAAiC,SAAU,MAAM,KAAG,MAGzC,CAAC;AAQzB;;;;;;GAMG;AACH,QAAA,MAAM,qCAAqC,UAAW,OAAO,KAAG,OAElB,CAAC;AAE/C,QAAA,MAAM,0BAA0B,UAAW,OAAO,KAAG,OAEP,CAAC;AA8E/C,QAAA,MAAM,2BAA2B,gBAChB,MAAM,KACpB,OAAO,CAAC,wBAAwB,EAAE,CAOpC,CAAC;AAEF,QAAA,MAAM,yBAAyB,cAChB,MAAM,SACV,MAAM,EAAE,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SACxB,OAAO,aACH,OAAO,eACL,MAAM,KACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8EhC,CAAC;AAgDF,QAAA,MAAM,gBAAgB,cACP,MAAM,OACZ,MAAM,EAAE,YACH,OAAO,eACJ,MAAM,KACpB,OAAO,CAAC,MAAM,CAqDhB,CAAC;AAEF,QAAA,MAAM,gBAAgB,gBACL,MAAM,KACpB,OAAO,CAAC,WAAW,EAAE,CAOvB,CAAC;AAcF,QAAA,MAAM,cAAc,cACL,MAAM,SACV,MAAM,EAAE,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SACxB,OAAO,eACD,MAAM,aACT,OAAO,KAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgDhC,CAAC;AAgLF,eAAO,MAAM,eAAe,YACf,sBAAsB,KAChC,OAAO,CAAC,IAAI,CA8rBd,CAAC;AAiDF,QAAA,MAAM,sBAAsB,WAChB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC9B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,0BAA0B,CAAA;CAoBxD,CAAC;AAyaF,QAAA,MAAM,sBAAsB,mBACR,MAAM,EAAE,eACX,MAAM,oBACF,CAAC,eAAe,GAAG;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC,EAAE,KAClE,OAAO,CAAC,MAAM,EAAE,CAId,CAAC;AAEN,QAAA,MAAM,oBAAoB,mBACN,MAAM,EAAE,eACX,MAAM,oBACF,CAAC,eAAe,GAAG;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC,EAAE,KAClE,OAAO,CAAC,MAAM,EAAE,CAId,CAAC;AAIN,QAAA,MAAM,0BAA0B,eAChB,OAAO,kBACH,MAAM,EAAE,KACzB,OAAoD,CAAC;AAExD,eAAO,MAAM,aAAa,YACb,sBAAsB,KAChC,OAAO,CAAC,IAAI,CA80Bd,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BvB,CAAC"}
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/handlers/download.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,7 @@ const contentAsCodeOutput_1 = require("../terminal/contentAsCodeOutput");
20
20
  const appCodeFiles_1 = require("./apps/appCodeFiles");
21
21
  const appsDownload_1 = require("./apps/appsDownload");
22
22
  const scaffolding_1 = require("./apps/scaffolding");
23
+ const uploadRetry_1 = require("./apps/uploadRetry");
23
24
  const projectResources_1 = require("./contentAsCode/projectResources");
24
25
  const resource_1 = require("./contentAsCode/resource");
25
26
  const contentAsCodePaths_1 = require("./contentAsCodePaths");
@@ -1821,6 +1822,7 @@ const uploadHandler = async (options) => {
1821
1822
  : `No app folders found in ${appsDir} for the dashboard(s) being uploaded. Re-run 'lightdash download' to fetch their apps.`));
1822
1823
  }
1823
1824
  const matchedRefs = new Set();
1825
+ const buildWaitState = (0, uploadRetry_1.createBuildLimitWaitState)();
1824
1826
  for (const subDir of subDirs) {
1825
1827
  const folderPath = path.join(appsDir, subDir.name);
1826
1828
  try {
@@ -1930,10 +1932,17 @@ const uploadHandler = async (options) => {
1930
1932
  force: options.force,
1931
1933
  });
1932
1934
  // eslint-disable-next-line no-await-in-loop
1933
- const { appUuid, version, action, slug, warnings } = await (0, apiClient_1.lightdashApi)({
1935
+ const { appUuid, version, action, slug, warnings } = await (0, uploadRetry_1.withBuildLimitRetry)(() => (0, apiClient_1.lightdashApi)({
1934
1936
  method: 'POST',
1935
1937
  url: `/api/v1/ee/projects/${projectId}/apps/upload`,
1936
1938
  body: JSON.stringify(body),
1939
+ }), buildWaitState, {
1940
+ onWait: (attempt, delayMs) => {
1941
+ if (attempt === 1) {
1942
+ globalState_1.default.log(styles.warning(`Project build limit reached — waiting for builds to finish before uploading "${subDir.name}"…`));
1943
+ }
1944
+ globalState_1.default.debug(`> Build cap retry ${attempt} for "${subDir.name}" in ${delayMs}ms`);
1945
+ },
1937
1946
  });
1938
1947
  // e.g. a manifest external-connection link whose slug is
1939
1948
  // missing in the target project was skipped
@@ -1980,9 +1989,15 @@ const uploadHandler = async (options) => {
1980
1989
  break;
1981
1990
  }
1982
1991
  appsFailed += 1;
1983
- const hint = status === 404
1984
- ? ' — the enterprise "data apps" feature may not be enabled on this instance'
1985
- : '';
1992
+ let hint = '';
1993
+ if (status === 404) {
1994
+ hint =
1995
+ ' — the enterprise "data apps" feature may not be enabled on this instance';
1996
+ }
1997
+ else if (status === 429) {
1998
+ hint =
1999
+ ' — gave up waiting for a free build slot; re-run the upload once builds finish (unchanged apps are skipped)';
2000
+ }
1986
2001
  globalState_1.default.log(styles.error(`Failed to upload app folder "${subDir.name}"${status ? ` [HTTP ${status}]` : ''}: ${(0, common_1.getErrorMessage)(appErr)}${hint}`));
1987
2002
  }
1988
2003
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/cli",
3
- "version": "1.55.0",
3
+ "version": "1.56.0",
4
4
  "description": "Lightdash CLI tool",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -41,8 +41,8 @@
41
41
  "unique-names-generator": "4.7.1",
42
42
  "uuid": "11.1.1",
43
43
  "yaml": "2.8.3",
44
- "@lightdash/common": "1.55.0",
45
- "@lightdash/warehouses": "1.55.0"
44
+ "@lightdash/common": "1.56.0",
45
+ "@lightdash/warehouses": "1.56.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/inquirer": "8.2.4",