From cef47b026f15deab465262a8fa0491e71552cc8e Mon Sep 17 00:00:00 2001 From: Jules Exel Date: Wed, 17 Jun 2026 10:22:59 -0400 Subject: [PATCH] perf(sync): skip downloading assets/non-model elements for a models-only sync During a sync/push the downloader returned ALL operations regardless of the elements/models scope, so a models-only run still downloaded assets, galleries, content, etc. Detect a models-only scope (--elements="Models" or the simple --models filter) and download only model definitions. --models-with-deps is intentionally excluded: it pulls the full renderable tree (assets included) and is not treated as models-only. Default/unscoped syncs are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../downloaders/download-operations-config.ts | 25 +++++++++++++++++++ .../tests/download-operations-config.test.ts | 24 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/lib/downloaders/download-operations-config.ts b/src/lib/downloaders/download-operations-config.ts index 6411928..e42f298 100644 --- a/src/lib/downloaders/download-operations-config.ts +++ b/src/lib/downloaders/download-operations-config.ts @@ -105,6 +105,14 @@ export class DownloadOperationsRegistry { } if (fromPush) { + // A sync/push normally downloads everything so the push pipeline has complete data for change + // detection. Exception: when the operation is scoped to models only — `--elements="Models"` or + // the simple `--models` filter — only model definitions are needed, so skip assets, galleries, + // content, etc. (`--models-with-deps` still needs the full renderable tree and is NOT treated as + // models-only here.) + if (this.isModelsOnlyScope(resolvedElements)) { + return Object.values(DOWNLOAD_OPERATIONS).filter((operation) => operation.elements.includes("Models")); + } return Object.values(DOWNLOAD_OPERATIONS); } @@ -117,6 +125,23 @@ export class DownloadOperationsRegistry { return relevantOperations; } + /** + * A models-only operation needs only model definitions downloaded — no assets, galleries, content, + * templates, containers, or pages. True when the user scoped elements to just "Models", or used the + * simple `--models` filter. `--models-with-deps` is intentionally excluded: it pulls the full + * dependency tree (assets included) and therefore is NOT models-only. + */ + private static isModelsOnlyScope(resolvedElements: string[]): boolean { + const state = getState(); + + const simpleModelsOnly = + !!(state.models && state.models.trim()) && !(state.modelsWithDeps && state.modelsWithDeps.trim()); + + const elementsModelsOnly = resolvedElements.length === 1 && resolvedElements[0] === "Models"; + + return simpleModelsOnly || elementsModelsOnly; + } + /** * Resolve element dependencies */ diff --git a/src/lib/downloaders/tests/download-operations-config.test.ts b/src/lib/downloaders/tests/download-operations-config.test.ts index 1b439ad..f0c1bed 100644 --- a/src/lib/downloaders/tests/download-operations-config.test.ts +++ b/src/lib/downloaders/tests/download-operations-config.test.ts @@ -83,6 +83,30 @@ describe("DownloadOperationsRegistry.getOperationsForElements", () => { expect(ops.length).toBe(Object.keys(DOWNLOAD_OPERATIONS).length); }); + it("skips assets (and other non-model ops) on a sync scoped to --elements=Models", () => { + setState({ elements: "Models" }); + const ops = DownloadOperationsRegistry.getOperationsForElements(true); + const names = ops.map((o: OperationConfig) => o.name); + expect(names).toEqual(["downloadAllModels"]); + expect(names).not.toContain("downloadAllAssets"); + expect(names).not.toContain("downloadAllGalleries"); + }); + + it("skips assets on a sync that uses the simple --models filter", () => { + setState({ models: "FooterLinks" }); // elements stays at the full default + const ops = DownloadOperationsRegistry.getOperationsForElements(true); + const names = ops.map((o: OperationConfig) => o.name); + expect(names).toEqual(["downloadAllModels"]); + expect(names).not.toContain("downloadAllAssets"); + }); + + it("still downloads everything on a --models-with-deps sync (full tree needs assets)", () => { + setState({ modelsWithDeps: "FooterLinks" }); + const ops = DownloadOperationsRegistry.getOperationsForElements(true); + expect(ops.length).toBe(Object.keys(DOWNLOAD_OPERATIONS).length); + expect(ops.map((o: OperationConfig) => o.name)).toContain("downloadAllAssets"); + }); + it("returns only operations matching state.elements when fromPush is false", () => { setState({ elements: "Models" }); const ops = DownloadOperationsRegistry.getOperationsForElements(false);