@contentful/experience-design-system-cli 2.34.5-dev-build-13ecfbdc.0 → 2.34.5-dev-build-12a524fb.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.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.34.5-dev-build-13ecfbdc.0",
3
+ "version": "2.34.5-dev-build-12a524fb.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,6 +1,8 @@
1
1
  export type CompositionCliOptions = {
2
2
  /** `--composition-refresh` — force edge emission to run even over resolved residue. */
3
3
  compositionRefresh?: boolean;
4
+ /** `--no-cache` — force the composition agent to rerun with the rest of the pipeline. */
5
+ noCache?: boolean;
4
6
  };
5
7
  export type ResolvedCompositionSources = {
6
8
  forceAgent: boolean;
@@ -1,3 +1,7 @@
1
1
  import type { Command } from 'commander';
2
+ export declare function resolveExtractNoCache(opts: {
3
+ cache?: boolean;
4
+ noCache?: boolean;
5
+ }): boolean;
2
6
  export declare function collectSourceFiles(directory: string, onProgress?: (scannedCount: number) => void): Promise<string[]>;
3
7
  export declare function registerInternalExtractCommand(program: Command): void;
package/dist/src/index.js CHANGED
@@ -3703,6 +3703,40 @@ async function getCliCacheVersion() {
3703
3703
  return _cliCacheVersionCache;
3704
3704
  }
3705
3705
  }
3706
+ function storeExtractCache(db, filePath, fileHash, cliVersion2, components) {
3707
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3708
+ const componentsJson = JSON.stringify(components);
3709
+ db.prepare(
3710
+ `INSERT INTO extract_cache (file_path, file_hash, cli_version, created_at, updated_at, components_json)
3711
+ VALUES (?, ?, ?, ?, ?, ?)
3712
+ ON CONFLICT(file_hash, cli_version) DO UPDATE SET
3713
+ file_path = excluded.file_path,
3714
+ updated_at = excluded.updated_at,
3715
+ components_json = excluded.components_json`
3716
+ ).run(filePath, fileHash, cliVersion2, now, now, componentsJson);
3717
+ }
3718
+ function lookupExtractCache(db, fileHash, cliVersion2) {
3719
+ const row = db.prepare(
3720
+ `SELECT file_path, file_hash, cli_version, created_at, updated_at, components_json
3721
+ FROM extract_cache
3722
+ WHERE file_hash = ? AND cli_version = ?`
3723
+ ).get(fileHash, cliVersion2);
3724
+ if (!row) return null;
3725
+ let components = [];
3726
+ try {
3727
+ components = JSON.parse(row.components_json);
3728
+ } catch {
3729
+ return null;
3730
+ }
3731
+ return {
3732
+ filePath: row.file_path,
3733
+ fileHash: row.file_hash,
3734
+ cliVersion: row.cli_version,
3735
+ createdAt: row.created_at,
3736
+ updatedAt: row.updated_at,
3737
+ components
3738
+ };
3739
+ }
3706
3740
  function storeCompositionCache(db, inputHash, cliVersion2, agentOutput) {
3707
3741
  const now = (/* @__PURE__ */ new Date()).toISOString();
3708
3742
  db.prepare(
@@ -30285,7 +30319,7 @@ var init_resolve_mapping = __esm({
30285
30319
  // packages/experience-design-system-cli/src/analyze/composition/resolve-mapping-cli.ts
30286
30320
  function resolveCompositionSources(opts) {
30287
30321
  return {
30288
- forceAgent: !!opts.compositionRefresh
30322
+ forceAgent: opts.compositionRefresh === true || opts.noCache === true
30289
30323
  };
30290
30324
  }
30291
30325
  var init_resolve_mapping_cli = __esm({
@@ -30454,12 +30488,26 @@ var init_manifest_doc_evidence = __esm({
30454
30488
  // packages/experience-design-system-cli/src/analyze/extract-command.ts
30455
30489
  import { mkdir as mkdir6, readdir as readdir3, readFile as readFile18, stat as stat6 } from "node:fs/promises";
30456
30490
  import { isAbsolute as isAbsolute2, join as join25, relative as relative2, resolve as resolve19 } from "node:path";
30491
+ function resolveExtractNoCache(opts) {
30492
+ return opts.noCache === true || opts.cache === false;
30493
+ }
30457
30494
  function isDenylistedNoiseFile(name) {
30458
30495
  return DENYLISTED_EXACT_FILE_NAMES.has(name) || DENYLISTED_FILE_NAME_PATTERNS.some((pattern) => pattern.test(name));
30459
30496
  }
30460
30497
  function pluralize(count, singular, plural = `${singular}s`) {
30461
30498
  return `${count} ${count === 1 ? singular : plural}`;
30462
30499
  }
30500
+ async function retryDatabaseWrite(operation, attempts = 8) {
30501
+ for (let attempt = 1; ; attempt++) {
30502
+ try {
30503
+ return operation();
30504
+ } catch (error) {
30505
+ const message = error instanceof Error ? error.message : String(error);
30506
+ if (!/database is locked|database is busy/i.test(message) || attempt >= attempts) throw error;
30507
+ await new Promise((resolve21) => setTimeout(resolve21, Math.min(250 * attempt, 2e3)));
30508
+ }
30509
+ }
30510
+ }
30463
30511
  function resolveFromProjectRoot(projectRoot, inputPath) {
30464
30512
  return isAbsolute2(inputPath) ? inputPath : resolve19(projectRoot, inputPath);
30465
30513
  }
@@ -30532,7 +30580,7 @@ function registerInternalExtractCommand(program) {
30532
30580
  "--resolve-unreachable <mode>",
30533
30581
  "Retry pass for unresolved Svelte Props types: 'auto' (default), 'always', or 'never'",
30534
30582
  "auto"
30535
- ).option("--composition-refresh", "Force the mapping agent to run even where deterministic sources answered").option(
30583
+ ).option("--composition-refresh", "Force the mapping agent to run even where deterministic sources answered").option("--no-cache", "Re-run extraction even when all source files are unchanged").option(
30536
30584
  "--prompt <stage=value>",
30537
30585
  "Override a stage prompt (repeatable). value is a file path or literal text, e.g. --prompt composition=./p.md",
30538
30586
  (v, acc) => [...acc, v],
@@ -30542,6 +30590,7 @@ function registerInternalExtractCommand(program) {
30542
30590
  includeModel: false,
30543
30591
  agentDescription: "Coding agent for composition mapping resolution (claude|codex|opencode|cursor)"
30544
30592
  }).action(async (opts) => {
30593
+ const noCache = resolveExtractNoCache(opts);
30545
30594
  const resolveUnreachable = (() => {
30546
30595
  const v = opts.resolveUnreachable ?? "auto";
30547
30596
  if (v !== "auto" && v !== "always" && v !== "never") {
@@ -30579,16 +30628,50 @@ function registerInternalExtractCommand(program) {
30579
30628
  `);
30580
30629
  }
30581
30630
  });
30582
- const extraction = await extractComponents2(
30583
- sourceFiles,
30584
- ({ filesProcessed, componentsFound }) => {
30631
+ const extractionCacheDb = openPipelineDb();
30632
+ let extraction;
30633
+ let extractionCacheHits = 0;
30634
+ try {
30635
+ const cacheVersion = await getCliCacheVersion();
30636
+ const cachedByPath = /* @__PURE__ */ new Map();
30637
+ if (!noCache && sourceFiles.length > 0) {
30638
+ const hashes = await Promise.all(
30639
+ sourceFiles.map(async (filePath) => [filePath, await hashFile(filePath)])
30640
+ );
30641
+ for (const [filePath, fileHash] of hashes) {
30642
+ cachedByPath.set(filePath, lookupExtractCache(extractionCacheDb, fileHash, cacheVersion));
30643
+ }
30644
+ }
30645
+ const allFilesCached = !noCache && sourceFiles.length > 0 && sourceFiles.every((filePath) => cachedByPath.get(filePath) !== null);
30646
+ if (allFilesCached) {
30647
+ const cachedComponents = sourceFiles.flatMap((filePath) => cachedByPath.get(filePath).components);
30648
+ extractionCacheHits = sourceFiles.length;
30649
+ extraction = { components: cachedComponents, warnings: [] };
30585
30650
  if (!process.stdout.isTTY) {
30586
- process.stderr.write(`progress=extract:${filesProcessed}/${sourceFiles.length}:${componentsFound}
30587
- `);
30651
+ process.stderr.write(
30652
+ `progress=extract:${sourceFiles.length}/${sourceFiles.length}:${cachedComponents.length}
30653
+ `
30654
+ );
30588
30655
  }
30589
- },
30590
- { resolveUnreachable, projectRoot }
30591
- );
30656
+ getDebugLogger().event("analyze", "extract.cache-hit", {
30657
+ files: sourceFiles.length,
30658
+ components: cachedComponents.length
30659
+ });
30660
+ } else {
30661
+ extraction = await extractComponents2(
30662
+ sourceFiles,
30663
+ ({ filesProcessed, componentsFound }) => {
30664
+ if (!process.stdout.isTTY) {
30665
+ process.stderr.write(`progress=extract:${filesProcessed}/${sourceFiles.length}:${componentsFound}
30666
+ `);
30667
+ }
30668
+ },
30669
+ { resolveUnreachable, projectRoot }
30670
+ );
30671
+ }
30672
+ } finally {
30673
+ extractionCacheDb.close();
30674
+ }
30592
30675
  await mkdir6(outDir, { recursive: true });
30593
30676
  const db = openPipelineDb();
30594
30677
  const { sessionId: sessionId2 } = getOrCreateSession(db, void 0, void 0, {
@@ -30603,6 +30686,25 @@ function registerInternalExtractCommand(program) {
30603
30686
  const stepId = createStep(db, sessionId2, "analyze extract", {
30604
30687
  project: projectRoot
30605
30688
  });
30689
+ if (extractionCacheHits === 0 && !noCache) {
30690
+ const cacheVersion = await getCliCacheVersion();
30691
+ const componentsBySourcePath = /* @__PURE__ */ new Map();
30692
+ for (const component of extraction.components) {
30693
+ if (!component.sourcePath) continue;
30694
+ const components = componentsBySourcePath.get(component.sourcePath) ?? [];
30695
+ components.push(component);
30696
+ componentsBySourcePath.set(component.sourcePath, components);
30697
+ }
30698
+ for (const filePath of sourceFiles) {
30699
+ storeExtractCache(
30700
+ db,
30701
+ filePath,
30702
+ await hashFile(filePath),
30703
+ cacheVersion,
30704
+ componentsBySourcePath.get(filePath) ?? []
30705
+ );
30706
+ }
30707
+ }
30606
30708
  const classifiedComponents = extraction.components.map(preClassifyComponent);
30607
30709
  const inspectedComponents = await Promise.all(
30608
30710
  classifiedComponents.map(async (component) => ({
@@ -30647,7 +30749,10 @@ function registerInternalExtractCommand(program) {
30647
30749
  }
30648
30750
  let validatedComponents = validateExtractedComponents(filteredComponents);
30649
30751
  {
30650
- const sources = resolveCompositionSources(opts);
30752
+ const sources = resolveCompositionSources({
30753
+ ...opts,
30754
+ noCache
30755
+ });
30651
30756
  const { overrides: promptOverrides, errors: promptErrors } = parsePromptOverrides(opts.prompt ?? []);
30652
30757
  for (const err of promptErrors) {
30653
30758
  process.stderr.write(`Error: ${err}
@@ -30714,7 +30819,7 @@ function registerInternalExtractCommand(program) {
30714
30819
  files: promptFiles,
30715
30820
  ...compositionPrompt ? { promptOverride: compositionPrompt } : {},
30716
30821
  runAgentFn: async ({ prompt }) => {
30717
- if (!opts.compositionRefresh) {
30822
+ if (!noCache && !opts.compositionRefresh) {
30718
30823
  const cached = lookupCompositionCache(db, agentCacheKey, cacheVersion);
30719
30824
  if (cached !== null) {
30720
30825
  emitCompositionProgress("cache-hit");
@@ -30741,7 +30846,7 @@ function registerInternalExtractCommand(program) {
30741
30846
  validatedComponents = result.components;
30742
30847
  }
30743
30848
  }
30744
- storeRawComponents(db, sessionId2, validatedComponents);
30849
+ await retryDatabaseWrite(() => storeRawComponents(db, sessionId2, validatedComponents));
30745
30850
  const cycleInput = validatedComponents.map((c2) => ({
30746
30851
  name: c2.name,
30747
30852
  slots: c2.slots.map((s) => ({ name: s.name, allowedComponents: s.allowedComponents }))
@@ -30751,13 +30856,13 @@ function registerInternalExtractCommand(program) {
30751
30856
  ...cycle,
30752
30857
  suggestedBreak: suggestCycleBreakEdge(cycle, cycles)
30753
30858
  }));
30754
- storeSlotCycles(db, sessionId2, withBreaks);
30859
+ await retryDatabaseWrite(() => storeSlotCycles(db, sessionId2, withBreaks));
30755
30860
  storeScannedFiles(
30756
30861
  db,
30757
30862
  sessionId2,
30758
30863
  sourceFiles.map((f) => relative2(projectRoot, f))
30759
30864
  );
30760
- updateStep(db, stepId, "complete", { sessionId: sessionId2 });
30865
+ await retryDatabaseWrite(() => updateStep(db, stepId, "complete", { sessionId: sessionId2 }));
30761
30866
  enrichCommandResult({ extracted_component_count: validatedComponents.length });
30762
30867
  db.close();
30763
30868
  const allWarnings = [...extraction.warnings, ...filterWarnings];
@@ -30784,6 +30889,7 @@ var init_extract_command = __esm({
30784
30889
  init_agent_model_options();
30785
30890
  init_src3();
30786
30891
  init_db();
30892
+ init_cache_keys();
30787
30893
  init_cycle_detection();
30788
30894
  init_resolve_mapping();
30789
30895
  init_resolve_mapping_cli();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.34.5-dev-build-13ecfbdc.0",
3
+ "version": "2.34.5-dev-build-12a524fb.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,8 +41,8 @@
41
41
  "svelte": "^5.56.4",
42
42
  "ts-morph": "^27.0.2",
43
43
  "typescript": "^5.9.3",
44
- "@contentful/experience-design-system-types": "2.34.5-dev-build-13ecfbdc.0",
45
- "@contentful/experience-design-system-cli-v2": "2.34.5-dev-build-13ecfbdc.0"
44
+ "@contentful/experience-design-system-cli-v2": "2.34.5-dev-build-12a524fb.0",
45
+ "@contentful/experience-design-system-types": "2.34.5-dev-build-12a524fb.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@tsconfig/node24": "^24.0.4",
@@ -54,9 +54,9 @@
54
54
  "ink-testing-library": "^4.0.0",
55
55
  "typescript-eslint": "^8.67.0",
56
56
  "vitest": "^4.0.16",
57
- "@contentful/experience-design-system-extraction": "2.34.5-dev-build-13ecfbdc.0",
58
- "@contentful/experience-design-system-generation": "2.34.5-dev-build-13ecfbdc.0",
59
- "@contentful/experience-design-system-client": "2.34.5-dev-build-13ecfbdc.0"
57
+ "@contentful/experience-design-system-client": "2.34.5-dev-build-12a524fb.0",
58
+ "@contentful/experience-design-system-generation": "2.34.5-dev-build-12a524fb.0",
59
+ "@contentful/experience-design-system-extraction": "2.34.5-dev-build-12a524fb.0"
60
60
  },
61
61
  "repository": {
62
62
  "type": "git",