@knapsack/source-conflicts-adapter 4.89.10--canary.7202.53e0d58.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.
Files changed (109) hide show
  1. package/.eslintrc.cjs +9 -0
  2. package/README.md +269 -0
  3. package/dist/analyzer.d.ts +29 -0
  4. package/dist/analyzer.d.ts.map +1 -0
  5. package/dist/analyzer.js +61 -0
  6. package/dist/analyzer.js.map +1 -0
  7. package/dist/analyzer.vitest.d.ts +2 -0
  8. package/dist/analyzer.vitest.d.ts.map +1 -0
  9. package/dist/analyzer.vitest.js +60 -0
  10. package/dist/analyzer.vitest.js.map +1 -0
  11. package/dist/compare/preview.d.ts +28 -0
  12. package/dist/compare/preview.d.ts.map +1 -0
  13. package/dist/compare/preview.js +27 -0
  14. package/dist/compare/preview.js.map +1 -0
  15. package/dist/compare/preview.vitest.d.ts +2 -0
  16. package/dist/compare/preview.vitest.d.ts.map +1 -0
  17. package/dist/compare/preview.vitest.js +67 -0
  18. package/dist/compare/preview.vitest.js.map +1 -0
  19. package/dist/detect.d.ts +13 -0
  20. package/dist/detect.d.ts.map +1 -0
  21. package/dist/detect.js +104 -0
  22. package/dist/detect.js.map +1 -0
  23. package/dist/detect.vitest.d.ts +2 -0
  24. package/dist/detect.vitest.d.ts.map +1 -0
  25. package/dist/detect.vitest.js +116 -0
  26. package/dist/detect.vitest.js.map +1 -0
  27. package/dist/index.d.ts +26 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +26 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/mdx/mdxConflicts.d.ts +160 -0
  32. package/dist/mdx/mdxConflicts.d.ts.map +1 -0
  33. package/dist/mdx/mdxConflicts.js +393 -0
  34. package/dist/mdx/mdxConflicts.js.map +1 -0
  35. package/dist/mdx/mdxConflicts.vitest.d.ts +2 -0
  36. package/dist/mdx/mdxConflicts.vitest.d.ts.map +1 -0
  37. package/dist/mdx/mdxConflicts.vitest.js +101 -0
  38. package/dist/mdx/mdxConflicts.vitest.js.map +1 -0
  39. package/dist/mdx/mdxRules.d.ts +56 -0
  40. package/dist/mdx/mdxRules.d.ts.map +1 -0
  41. package/dist/mdx/mdxRules.js +386 -0
  42. package/dist/mdx/mdxRules.js.map +1 -0
  43. package/dist/mdx/mdxRules.vitest.d.ts +2 -0
  44. package/dist/mdx/mdxRules.vitest.d.ts.map +1 -0
  45. package/dist/mdx/mdxRules.vitest.js +36 -0
  46. package/dist/mdx/mdxRules.vitest.js.map +1 -0
  47. package/dist/mdx/sharedProcessor.d.ts +13 -0
  48. package/dist/mdx/sharedProcessor.d.ts.map +1 -0
  49. package/dist/mdx/sharedProcessor.js +48 -0
  50. package/dist/mdx/sharedProcessor.js.map +1 -0
  51. package/dist/mdx/tableMdx.d.ts +45 -0
  52. package/dist/mdx/tableMdx.d.ts.map +1 -0
  53. package/dist/mdx/tableMdx.js +114 -0
  54. package/dist/mdx/tableMdx.js.map +1 -0
  55. package/dist/mdx/tableMdx.vitest.d.ts +2 -0
  56. package/dist/mdx/tableMdx.vitest.d.ts.map +1 -0
  57. package/dist/mdx/tableMdx.vitest.js +99 -0
  58. package/dist/mdx/tableMdx.vitest.js.map +1 -0
  59. package/dist/multiSourceCompare.d.ts +40 -0
  60. package/dist/multiSourceCompare.d.ts.map +1 -0
  61. package/dist/multiSourceCompare.js +286 -0
  62. package/dist/multiSourceCompare.js.map +1 -0
  63. package/dist/multiSourceCompare.vitest.d.ts +2 -0
  64. package/dist/multiSourceCompare.vitest.d.ts.map +1 -0
  65. package/dist/multiSourceCompare.vitest.js +46 -0
  66. package/dist/multiSourceCompare.vitest.js.map +1 -0
  67. package/dist/parse.d.ts +6 -0
  68. package/dist/parse.d.ts.map +1 -0
  69. package/dist/parse.js +21 -0
  70. package/dist/parse.js.map +1 -0
  71. package/dist/parse.vitest.d.ts +2 -0
  72. package/dist/parse.vitest.d.ts.map +1 -0
  73. package/dist/parse.vitest.js +34 -0
  74. package/dist/parse.vitest.js.map +1 -0
  75. package/dist/parser.d.ts +4 -0
  76. package/dist/parser.d.ts.map +1 -0
  77. package/dist/parser.js +61 -0
  78. package/dist/parser.js.map +1 -0
  79. package/dist/transformer.d.ts +4 -0
  80. package/dist/transformer.d.ts.map +1 -0
  81. package/dist/transformer.js +112 -0
  82. package/dist/transformer.js.map +1 -0
  83. package/dist/types.d.ts +57 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +13 -0
  86. package/dist/types.js.map +1 -0
  87. package/dist/utils/canonicalCache.d.ts +7 -0
  88. package/dist/utils/canonicalCache.d.ts.map +1 -0
  89. package/dist/utils/canonicalCache.js +18 -0
  90. package/dist/utils/canonicalCache.js.map +1 -0
  91. package/dist/utils/canonicalCache.vitest.d.ts +2 -0
  92. package/dist/utils/canonicalCache.vitest.d.ts.map +1 -0
  93. package/dist/utils/canonicalCache.vitest.js +40 -0
  94. package/dist/utils/canonicalCache.vitest.js.map +1 -0
  95. package/dist/utils/frontmatter.d.ts +13 -0
  96. package/dist/utils/frontmatter.d.ts.map +1 -0
  97. package/dist/utils/frontmatter.js +14 -0
  98. package/dist/utils/frontmatter.js.map +1 -0
  99. package/dist/utils/frontmatter.vitest.d.ts +2 -0
  100. package/dist/utils/frontmatter.vitest.d.ts.map +1 -0
  101. package/dist/utils/frontmatter.vitest.js +34 -0
  102. package/dist/utils/frontmatter.vitest.js.map +1 -0
  103. package/dist/validator.d.ts +7 -0
  104. package/dist/validator.d.ts.map +1 -0
  105. package/dist/validator.js +30 -0
  106. package/dist/validator.js.map +1 -0
  107. package/package.json +60 -0
  108. package/tsconfig.json +9 -0
  109. package/vitest.config.mjs +9 -0
package/.eslintrc.cjs ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ extends: [
3
+ '@knapsack/eslint-config-starter/react',
4
+ '@knapsack/eslint-config-starter',
5
+ ],
6
+ parserOptions: { tsconfigRootDir: __dirname },
7
+ ignorePatterns: [],
8
+ rules: {},
9
+ };
package/README.md ADDED
@@ -0,0 +1,269 @@
1
+ # @knapsack/source-conflicts-adapter
2
+
3
+ Detects and compares entity documentation conflicts across multiple ingestion sources (e.g. Storybook, Knapsack, Figma). Implemented as a `ContextSrcAdapter` so it integrates with the ingest pipeline and can also be used standalone via its `analyzer` interface.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ When the same entity (e.g. `button.react.mdx`) is contributed by more than one source, the sections within each file may diverge. This adapter:
10
+
11
+ 1. **Indexes** the entities directory to find files contributed by multiple sources
12
+ 2. **Groups** files that map to the same canonical entity path
13
+ 3. **Detects** conflicts by parsing each MDX/MD file into anchored sections, then comparing section content across sources with a chain of canonicalization rules
14
+ 4. **Reports** per-anchor verdicts: `ALL_SAME`, `DIFFERENT`, or `MISSING`
15
+
16
+ ### Entity file layout
17
+
18
+ ```
19
+ entities/
20
+ └── <entityKind>/ # e.g. "components", "tokens"
21
+ ├── button.react.KNAPSACK-abc123.mdx
22
+ ├── button.react.STORYBOOK-def456.mdx
23
+ └── card.react.KNAPSACK-111aaa.mdx
24
+ ```
25
+
26
+ Files with the same base name and extension (e.g. `button.react.mdx`) but different `<SOURCE>-<hash>` segments are candidates for conflict detection.
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ This package lives in the monorepo at `libs/ingest-pipeline/adapters/source-conflicts/` and is consumed as a workspace dependency:
33
+
34
+ ```json
35
+ "@knapsack/source-conflicts-adapter": "workspace:*"
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Quick start — standalone analyzer
41
+
42
+ The fastest way to detect conflicts without running the full pipeline is via the `analyzer` interface:
43
+
44
+ ```typescript
45
+ import * as fs from 'node:fs/promises';
46
+ import * as path from 'node:path';
47
+ import { analyze } from '@knapsack/source-conflicts-adapter';
48
+ import { createLogger } from '@knapsack/utils';
49
+
50
+ const entitiesRootAbs = '/absolute/path/to/entities';
51
+ const outputFile = path.join(entitiesRootAbs, 'source-conflicts-result.json');
52
+ const logger = createLogger('source-conflicts');
53
+
54
+ const result = await analyze({ entitiesRootAbs }, { logger });
55
+
56
+ await fs.writeFile(outputFile, JSON.stringify(result, null, 2), 'utf8');
57
+ console.log(`Wrote results to ${outputFile}`);
58
+ ```
59
+
60
+ The output file contains the full `SourceConflictsAnalysisResult` as JSON — `groupResults` with per-anchor verdicts and previews, plus `skippedGroupKeys` for any groups that were excluded.
61
+
62
+ ---
63
+
64
+ ## Configuration
65
+
66
+ ```typescript
67
+ import type { SourceConflictsConfig } from '@knapsack/source-conflicts-adapter';
68
+
69
+ const config: SourceConflictsConfig = {
70
+ /** Required: absolute path to the directory containing <entityKind>/ subdirectories. */
71
+ entitiesRootAbs: '/path/to/entities',
72
+
73
+ /** Optional: scope identifiers forwarded to fingerprint computation and logging. */
74
+ siteId: 'my-site',
75
+ userId: 'user-123',
76
+ configId: 'config-abc',
77
+ };
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Pipeline usage
83
+
84
+ The adapter implements the full `ContextSrcAdapter<SourceConflictsConfig, SourceConflictsAnalysisResult>` interface:
85
+
86
+ ```typescript
87
+ import { sourceConflictsAdapter } from '@knapsack/source-conflicts-adapter';
88
+ import type { SourceConflictsConfig } from '@knapsack/source-conflicts-adapter';
89
+ import type { RuntimeDeps } from '@knapsack/adapter-core';
90
+
91
+ // Validate config before running
92
+ const configError = sourceConflictsAdapter.validator.validateConfig(config);
93
+ if (configError) throw new Error(configError);
94
+
95
+ // Parse stage: indexes entities + writes manifest
96
+ await sourceConflictsAdapter.parser.parse(config, parseDeps);
97
+
98
+ // Transform stage: runs conflict detection + writes verdict bundles
99
+ await sourceConflictsAdapter.transformer.transform(config, transformDeps);
100
+
101
+ // Analyze stage: in-process, returns typed results (no file writing)
102
+ const result = await sourceConflictsAdapter.analyzer!.analyze(config, {
103
+ logger,
104
+ });
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Result shape
110
+
111
+ ### `SourceConflictsAnalysisResult`
112
+
113
+ ```typescript
114
+ type SourceConflictsAnalysisResult = {
115
+ /** One entry per processed conflict group. */
116
+ groupResults: ConflictGroupResult[];
117
+ /** Keys of groups that were skipped (non-MDX/MD or too few sources). */
118
+ skippedGroupKeys: string[];
119
+ };
120
+ ```
121
+
122
+ ### `ConflictGroupResult`
123
+
124
+ ```typescript
125
+ type ConflictGroupResult = {
126
+ group: ConflictGroup; // entityKind, relPath, artifacts[]
127
+ bundle: MdxConflictBundle; // source-agnostic per-anchor snapshot (serializable — store in DB)
128
+ baselineSourceKey: string; // frontmatter URI of the baseline source, e.g. "knapsack://patterns/button"
129
+ compareSourceKeys: string[]; // frontmatter URIs of compare sources (up to MAX_COMPARE_SOURCES)
130
+ verdicts: AnchorVerdictView[]; // per-anchor verdict derived from bundle for this baseline/compare
131
+ };
132
+ ```
133
+
134
+ `bundle` is a fully-serializable JSON snapshot — no functions, no circular refs. Storing it once and calling `computeVerdictsForBaselineCompare` with different baseline/compare args is the recommended pattern (see [Option A](#option-a--bundle-first-pattern)).
135
+
136
+ ### `ConflictItemRecord` (inside `MdxConflictBundle.items`)
137
+
138
+ ```typescript
139
+ type ConflictItemRecord = {
140
+ fingerprint: string;
141
+ sourceType: SourceType; // filename-parsed, e.g. "KNAPSACK" — used internally for matching
142
+ sourceKey: string; // frontmatter URI, e.g. "knapsack://patterns/button" — use this for display and storage
143
+ frontmatterSchemaVersion: number;
144
+ frontmatter: Record<string, unknown>;
145
+ };
146
+ ```
147
+
148
+ ### Verdicts
149
+
150
+ | Value | Meaning |
151
+ | ----------- | --------------------------------------------------------------------- |
152
+ | `ALL_SAME` | All sources agree (or content was unified by a canonicalization rule) |
153
+ | `DIFFERENT` | At least one source has different content for this anchor |
154
+ | `MISSING` | At least one source does not contain this anchor |
155
+
156
+ ---
157
+
158
+ ## Option A — bundle-first pattern
159
+
160
+ `MdxConflictBundle` is the stable stored artifact. `computeVerdictsForBaselineCompare` is a pure function that derives verdicts from any stored bundle with no I/O or re-parsing.
161
+
162
+ ### Recommended DB/GraphQL pattern
163
+
164
+ 1. **Store** the bundle when running the detection pipeline.
165
+ 2. **Load** the bundle from storage when serving a request.
166
+ 3. **Call** `computeVerdictsForBaselineCompare` with the caller-specified baseline/compare source URIs.
167
+
168
+ ```typescript
169
+ import {
170
+ computeVerdictsForBaselineCompare,
171
+ getAvailableSourceKeysFromBundle,
172
+ } from '@knapsack/source-conflicts-adapter';
173
+ import type { MdxConflictBundle } from '@knapsack/source-conflicts-adapter';
174
+
175
+ // Load stored bundle from DB (JSON column → parse → use directly, no re-detection)
176
+ const bundle: MdxConflictBundle = JSON.parse(row.bundleJson);
177
+
178
+ // Discover which source URIs are available without re-detecting
179
+ const available = getAvailableSourceKeysFromBundle(bundle);
180
+ // e.g. ["knapsack://patterns/button", "storybook://components/button"]
181
+
182
+ // Derive verdicts for any baseline/compare combination — instant, no I/O
183
+ const verdicts = computeVerdictsForBaselineCompare(
184
+ bundle,
185
+ 'knapsack://patterns/button', // baseline URI
186
+ ['storybook://components/button'], // compare URIs
187
+ );
188
+
189
+ // Swap baseline without re-detecting — just call again
190
+ const swappedVerdicts = computeVerdictsForBaselineCompare(
191
+ bundle,
192
+ 'storybook://components/button',
193
+ ['knapsack://patterns/button'],
194
+ );
195
+ ```
196
+
197
+ ### `getAvailableSourceKeysFromBundle`
198
+
199
+ ```typescript
200
+ function getAvailableSourceKeysFromBundle(bundle: MdxConflictBundle): string[];
201
+ ```
202
+
203
+ Returns the frontmatter source URI (`sourceKey`) for every item in the bundle. Any of these can be passed as `baselineSourceKey` to `computeVerdictsForBaselineCompare` without re-running detection. Sources in `bundle.excludedSourceKeys` (if any, when `MAX_COMPARE_SOURCES` is exceeded) are **not** in the bundle and cannot be used as baseline without a full re-detect.
204
+
205
+ ---
206
+
207
+ ## Comparison rules
208
+
209
+ Sections are compared using a chain of canonicalization rules. The first rule that applies to all sections and produces a single hash bucket wins (result: `EXACT` match method). If no rule unifies, a fallback whitespace rule produces a `FALLBACK` result.
210
+
211
+ | Rule | ID | What it does |
212
+ | -------------------------- | -------------------- | ------------------------------------------- |
213
+ | `ruleExact` | `exact` | Exact string equality |
214
+ | `ruleWhitespace` | `ws_v1` | Collapse whitespace, normalize line endings |
215
+ | `ruleTables` | `table_v1` | Parse and re-serialize Markdown tables |
216
+ | `ruleJsonFences` | `json_fences_v1` | Normalize JSON inside fenced code blocks |
217
+ | `makeRuleAst()` | `ast_v1` | Full MDX AST comparison (strips positions) |
218
+ | `makeRulePrettierFences()` | `prettier_fences_v1` | Format code fences with Prettier |
219
+
220
+ ---
221
+
222
+ ## Supported file types
223
+
224
+ Both `.mdx` and `.md` files are supported. Non-markdown files (e.g. `.json`, `.css`) are indexed but skipped during conflict detection.
225
+
226
+ ---
227
+
228
+ ## Source file layout
229
+
230
+ ```
231
+ src/
232
+ ├── index.ts # Adapter export + public API re-exports
233
+ ├── types.ts # SourceConflictsConfig, ArtifactRef, ConflictGroup, VERDICT, Rule
234
+ ├── validator.ts # validateConfig / validateFull
235
+ ├── parser.ts # parse() — indexes entities, writes manifest
236
+ ├── transformer.ts # transform() — runs detection, writes verdict bundles
237
+ ├── analyzer.ts # analyze() — in-process detection, returns typed result
238
+ ├── detect.ts # indexEntitiesDir / findConflictGroups (filesystem)
239
+ ├── parse.ts # parseEntityKind / parseSourceType (filename parsing)
240
+ ├── multiSourceCompare.ts # compareAnchorMultiSource (rule engine)
241
+ ├── compare/
242
+ │ └── preview.ts # Preview/truncation helpers
243
+ ├── mdx/
244
+ │ ├── mdxConflicts.ts # detectMdxConflictBundle / computeVerdictsForBaselineCompare
245
+ │ ├── mdxRules.ts # Built-in canonicalization rules
246
+ │ ├── tableMdx.ts # Table parsing / canonicalization
247
+ │ └── sharedProcessor.ts # Shared remark processor
248
+ └── utils/
249
+ ├── canonicalCache.ts # In-memory canonical result cache (sha256)
250
+ └── frontmatter.ts # Frontmatter parsing helpers
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Development
256
+
257
+ ```bash
258
+ # Build
259
+ pnpm run build
260
+
261
+ # Watch
262
+ pnpm run dev
263
+
264
+ # Test
265
+ pnpm run test
266
+
267
+ # Lint
268
+ pnpm run lint
269
+ ```
@@ -0,0 +1,29 @@
1
+ import type { Logger } from '@knapsack/adapter-core';
2
+ import type { SourceConflictsConfig } from './types.js';
3
+ import type { AnchorVerdictView, MdxConflictBundle } from './mdx/mdxConflicts.js';
4
+ import type { ConflictGroup } from './types.js';
5
+ export type ConflictGroupResult = {
6
+ group: ConflictGroup;
7
+ bundle: MdxConflictBundle;
8
+ /** Frontmatter sourceKey URI of the baseline source (e.g. "knapsack://patterns/button"). */
9
+ baselineSourceKey: string;
10
+ /** Frontmatter sourceKey URIs of the compare sources. */
11
+ compareSourceKeys: string[];
12
+ verdicts: AnchorVerdictView[];
13
+ };
14
+ export type SourceConflictsAnalysisResult = {
15
+ groupResults: ConflictGroupResult[];
16
+ /** Groups that were skipped (non-MDX or not enough sources). */
17
+ skippedGroupKeys: string[];
18
+ };
19
+ export declare function analyze(config: SourceConflictsConfig, deps: Pick<{
20
+ logger: Logger;
21
+ }, 'logger'>): Promise<SourceConflictsAnalysisResult>;
22
+ /**
23
+ * Returns the source keys present in a stored bundle.
24
+ * Any of these can be passed as `baselineSourceKey` to
25
+ * `computeVerdictsForBaselineCompare` without re-running detection.
26
+ * Sources in `bundle.excludedSourceKeys` are NOT available.
27
+ */
28
+ export declare function getAvailableSourceKeysFromBundle(bundle: MdxConflictBundle): string[];
29
+ //# sourceMappingURL=analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAQxD,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,4FAA4F;IAC5F,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;AAEF,wBAAsB,OAAO,CAC3B,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,IAAI,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,CAAC,GACvC,OAAO,CAAC,6BAA6B,CAAC,CAoExC;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,iBAAiB,GACxB,MAAM,EAAE,CAEV"}
@@ -0,0 +1,61 @@
1
+ import { indexEntitiesDir, findConflictGroups } from './detect.js';
2
+ import { detectMdxConflictBundle, computeVerdictsForBaselineCompare, MAX_COMPARE_SOURCES, MIN_COMPARE_SOURCES, } from './mdx/mdxConflicts.js';
3
+ export async function analyze(config, deps) {
4
+ const { logger } = deps;
5
+ const index = await indexEntitiesDir(config.entitiesRootAbs, logger);
6
+ const groups = findConflictGroups(index, logger);
7
+ const groupResults = [];
8
+ const skippedGroupKeys = [];
9
+ for (const group of groups) {
10
+ if (!group.relPath.endsWith('.mdx') && !group.relPath.endsWith('.md')) {
11
+ skippedGroupKeys.push(group.key);
12
+ continue;
13
+ }
14
+ const sources = [
15
+ ...new Set(group.artifacts.map((a) => a.sourceKey)),
16
+ ].sort();
17
+ if (sources.length < 1 + MIN_COMPARE_SOURCES) {
18
+ skippedGroupKeys.push(group.key);
19
+ continue;
20
+ }
21
+ const baselineSourceKey = sources[0];
22
+ const compareSourceKeys = sources.slice(1, 1 + MAX_COMPARE_SOURCES);
23
+ const bundle = await detectMdxConflictBundle({
24
+ groupKey: group.key,
25
+ artifacts: group.artifacts,
26
+ baselineSourceKey,
27
+ compareSourceKeys,
28
+ siteId: config.siteId,
29
+ userId: config.userId,
30
+ configId: config.configId,
31
+ logger,
32
+ });
33
+ if (!bundle) {
34
+ skippedGroupKeys.push(group.key);
35
+ continue;
36
+ }
37
+ // Map filename-parsed SourceType → frontmatter URI using bundle.items.
38
+ const getUri = (st) => bundle.items.find((i) => i.sourceType === st)?.sourceKey ?? st;
39
+ const baselineUri = getUri(baselineSourceKey);
40
+ const compareUris = compareSourceKeys.map(getUri);
41
+ const verdicts = computeVerdictsForBaselineCompare(bundle, baselineUri, compareUris);
42
+ groupResults.push({
43
+ group,
44
+ bundle,
45
+ baselineSourceKey: baselineUri,
46
+ compareSourceKeys: compareUris,
47
+ verdicts,
48
+ });
49
+ }
50
+ return { groupResults, skippedGroupKeys };
51
+ }
52
+ /**
53
+ * Returns the source keys present in a stored bundle.
54
+ * Any of these can be passed as `baselineSourceKey` to
55
+ * `computeVerdictsForBaselineCompare` without re-running detection.
56
+ * Sources in `bundle.excludedSourceKeys` are NOT available.
57
+ */
58
+ export function getAvailableSourceKeysFromBundle(bundle) {
59
+ return bundle.items.map((item) => item.sourceKey);
60
+ }
61
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=analyzer.vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.vitest.d.ts","sourceRoot":"","sources":["../src/analyzer.vitest.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getAvailableSourceKeysFromBundle } from './analyzer.js';
3
+ function makeBundle(items) {
4
+ return {
5
+ conflict: {
6
+ entityKey: 'component/button.react',
7
+ entityType: 'COMPONENT',
8
+ entityRelPath: 'button.react',
9
+ fingerprint: 'cf_v1_abc',
10
+ createdAt: '2025-01-01T00:00:00Z',
11
+ updatedAt: '2025-01-01T00:00:00Z',
12
+ },
13
+ items,
14
+ anchorDefinitions: [],
15
+ };
16
+ }
17
+ describe('getAvailableSourceKeysFromBundle', () => {
18
+ it('returns URI sourceKeys for all items in the bundle', () => {
19
+ const bundle = makeBundle([
20
+ {
21
+ fingerprint: 'fp1',
22
+ sourceType: 'KNAPSACK',
23
+ sourceKey: 'knapsack://patterns/button',
24
+ frontmatterSchemaVersion: 1,
25
+ frontmatter: {},
26
+ },
27
+ {
28
+ fingerprint: 'fp2',
29
+ sourceType: 'STORYBOOK',
30
+ sourceKey: 'storybook://components/button',
31
+ frontmatterSchemaVersion: 1,
32
+ frontmatter: {},
33
+ },
34
+ ]);
35
+ const keys = getAvailableSourceKeysFromBundle(bundle);
36
+ expect(keys).toEqual([
37
+ 'knapsack://patterns/button',
38
+ 'storybook://components/button',
39
+ ]);
40
+ });
41
+ it('returns an empty array for a bundle with no items', () => {
42
+ const bundle = makeBundle([]);
43
+ expect(getAvailableSourceKeysFromBundle(bundle)).toEqual([]);
44
+ });
45
+ it('returns a single URI for a bundle with one item', () => {
46
+ const bundle = makeBundle([
47
+ {
48
+ fingerprint: 'fp1',
49
+ sourceType: 'KNAPSACK',
50
+ sourceKey: 'knapsack://patterns/button',
51
+ frontmatterSchemaVersion: 1,
52
+ frontmatter: {},
53
+ },
54
+ ]);
55
+ expect(getAvailableSourceKeysFromBundle(bundle)).toEqual([
56
+ 'knapsack://patterns/button',
57
+ ]);
58
+ });
59
+ });
60
+ //# sourceMappingURL=analyzer.vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.vitest.js","sourceRoot":"","sources":["../src/analyzer.vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,gCAAgC,EAAE,MAAM,eAAe,CAAC;AAEjE,SAAS,UAAU,CAAC,KAAiC;IACnD,OAAO;QACL,QAAQ,EAAE;YACR,SAAS,EAAE,wBAAwB;YACnC,UAAU,EAAE,WAAW;YACvB,aAAa,EAAE,cAAc;YAC7B,WAAW,EAAE,WAAW;YACxB,SAAS,EAAE,sBAAsB;YACjC,SAAS,EAAE,sBAAsB;SAClC;QACD,KAAK;QACL,iBAAiB,EAAE,EAAE;KACtB,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,MAAM,GAAG,UAAU,CAAC;YACxB;gBACE,WAAW,EAAE,KAAK;gBAClB,UAAU,EAAE,UAAU;gBACtB,SAAS,EAAE,4BAA4B;gBACvC,wBAAwB,EAAE,CAAC;gBAC3B,WAAW,EAAE,EAAE;aAChB;YACD;gBACE,WAAW,EAAE,KAAK;gBAClB,UAAU,EAAE,WAAW;gBACvB,SAAS,EAAE,+BAA+B;gBAC1C,wBAAwB,EAAE,CAAC;gBAC3B,WAAW,EAAE,EAAE;aAChB;SACF,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,gCAAgC,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACnB,4BAA4B;YAC5B,+BAA+B;SAChC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,gCAAgC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,MAAM,GAAG,UAAU,CAAC;YACxB;gBACE,WAAW,EAAE,KAAK;gBAClB,UAAU,EAAE,UAAU;gBACtB,SAAS,EAAE,4BAA4B;gBACvC,wBAAwB,EAAE,CAAC;gBAC3B,WAAW,EAAE,EAAE;aAChB;SACF,CAAC,CAAC;QACH,MAAM,CAAC,gCAAgC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YACvD,4BAA4B;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { TableNode } from '../mdx/tableMdx.js';
2
+ /** Default bounds for preview size; overridable via PreviewBounds. */
3
+ export declare const DEFAULT_MAX_DEFINITION_TEXT: number;
4
+ export declare const DEFAULT_MAX_DEFINITIONS = 10;
5
+ export declare const DEFAULT_MAX_SOURCES_PER_DEFINITION = 10;
6
+ /** Truncates string to max length and appends a truncation marker. */
7
+ export declare function truncate(s: string, max?: number): string;
8
+ /** Builds bounded preview.definitions from groups (capped counts); includes table value when present. */
9
+ export declare function stablePreviewBounded(args: {
10
+ groups: Array<{
11
+ hash: string;
12
+ sources: string[];
13
+ table?: TableNode;
14
+ }>;
15
+ maxDefinitions?: number;
16
+ maxSourcesPerDefinition?: number;
17
+ }): {
18
+ definitionCount: number;
19
+ definitions: Array<{
20
+ hash: string;
21
+ sources: string[];
22
+ value?: {
23
+ kind: 'TABLE';
24
+ table: TableNode;
25
+ };
26
+ }>;
27
+ };
28
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/compare/preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,sEAAsE;AACtE,eAAO,MAAM,2BAA2B,QAAW,CAAC;AACpD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,kCAAkC,KAAK,CAAC;AAErD,sEAAsE;AACtE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,SAAO,GAAG,MAAM,CAGtD;AAED,yGAAyG;AACzG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC,GAAG;IACF,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,EAAE;YAAE,IAAI,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,SAAS,CAAA;SAAE,CAAC;KAC7C,CAAC,CAAC;CACJ,CAiBA"}
@@ -0,0 +1,27 @@
1
+ /** Default bounds for preview size; overridable via PreviewBounds. */
2
+ export const DEFAULT_MAX_DEFINITION_TEXT = 8 * 1024; // 8KB
3
+ export const DEFAULT_MAX_DEFINITIONS = 10;
4
+ export const DEFAULT_MAX_SOURCES_PER_DEFINITION = 10;
5
+ /** Truncates string to max length and appends a truncation marker. */
6
+ export function truncate(s, max = 2000) {
7
+ if (s.length <= max)
8
+ return s;
9
+ return `${s.slice(0, max)}\n…(truncated)…`;
10
+ }
11
+ /** Builds bounded preview.definitions from groups (capped counts); includes table value when present. */
12
+ export function stablePreviewBounded(args) {
13
+ const maxDefinitions = args.maxDefinitions ?? DEFAULT_MAX_DEFINITIONS;
14
+ const maxSources = args.maxSourcesPerDefinition ?? DEFAULT_MAX_SOURCES_PER_DEFINITION;
15
+ const definitions = args.groups.slice(0, maxDefinitions).map((g) => ({
16
+ hash: g.hash,
17
+ sources: g.sources.slice(0, maxSources),
18
+ ...(g.table != null
19
+ ? { value: { kind: 'TABLE', table: g.table } }
20
+ : {}),
21
+ }));
22
+ return {
23
+ definitionCount: args.groups.length,
24
+ definitions,
25
+ };
26
+ }
27
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/compare/preview.ts"],"names":[],"mappings":"AAEA,sEAAsE;AACtE,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM;AAC3D,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,CAAC;AAErD,sEAAsE;AACtE,MAAM,UAAU,QAAQ,CAAC,CAAS,EAAE,GAAG,GAAG,IAAI;IAC5C,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC;AAC7C,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,oBAAoB,CAAC,IAIpC;IAQC,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,uBAAuB,CAAC;IACtE,MAAM,UAAU,GACd,IAAI,CAAC,uBAAuB,IAAI,kCAAkC,CAAC;IAErE,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;QACvC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI;YACjB,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAgB,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE;YACvD,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;QACnC,WAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=preview.vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.vitest.d.ts","sourceRoot":"","sources":["../../src/compare/preview.vitest.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { truncate, stablePreviewBounded, DEFAULT_MAX_DEFINITIONS, } from './preview.js';
3
+ describe('truncate', () => {
4
+ it('returns string unchanged when within max', () => {
5
+ const s = 'short';
6
+ expect(truncate(s)).toBe(s);
7
+ expect(truncate(s, 10)).toBe(s);
8
+ });
9
+ it('truncates and appends marker when over max', () => {
10
+ const s = 'a'.repeat(100);
11
+ const out = truncate(s, 20);
12
+ expect(out).toHaveLength(20 + '\n…(truncated)…'.length);
13
+ expect(out.startsWith('a'.repeat(20))).toBe(true);
14
+ expect(out.endsWith('…(truncated)…')).toBe(true);
15
+ });
16
+ it('uses default max 2000 when not provided', () => {
17
+ const s = 'x'.repeat(3000);
18
+ const out = truncate(s);
19
+ expect(out.slice(0, 2000)).toBe('x'.repeat(2000));
20
+ expect(out.endsWith('…(truncated)…')).toBe(true);
21
+ });
22
+ });
23
+ describe('stablePreviewBounded', () => {
24
+ const minimalTable = {
25
+ columns: ['a'],
26
+ keyColumn: 'a',
27
+ rows: [{ a: '1' }],
28
+ };
29
+ it('slices definitions by maxDefinitions', () => {
30
+ const groups = [
31
+ { hash: 'h1', sources: ['s1'] },
32
+ { hash: 'h2', sources: ['s2'] },
33
+ { hash: 'h3', sources: ['s3'] },
34
+ ];
35
+ const out = stablePreviewBounded({ groups, maxDefinitions: 2 });
36
+ expect(out.definitionCount).toBe(3);
37
+ expect(out.definitions).toHaveLength(2);
38
+ expect(out.definitions[0].hash).toBe('h1');
39
+ expect(out.definitions[1].hash).toBe('h2');
40
+ });
41
+ it('slices sources per definition by maxSourcesPerDefinition', () => {
42
+ const groups = [{ hash: 'h1', sources: ['s1', 's2', 's3', 's4', 's5'] }];
43
+ const out = stablePreviewBounded({
44
+ groups,
45
+ maxSourcesPerDefinition: 2,
46
+ });
47
+ expect(out.definitions[0].sources).toEqual(['s1', 's2']);
48
+ });
49
+ it('includes table value when present', () => {
50
+ const groups = [{ hash: 'h1', sources: ['s1'], table: minimalTable }];
51
+ const out = stablePreviewBounded({ groups });
52
+ expect(out.definitions[0].value).toEqual({
53
+ kind: 'TABLE',
54
+ table: minimalTable,
55
+ });
56
+ });
57
+ it('uses default max definitions and sources when omitted', () => {
58
+ const groups = Array.from({ length: 20 }, (_, i) => ({
59
+ hash: `h${i}`,
60
+ sources: ['s1'],
61
+ }));
62
+ const out = stablePreviewBounded({ groups });
63
+ expect(out.definitions).toHaveLength(DEFAULT_MAX_DEFINITIONS);
64
+ expect(out.definitionCount).toBe(20);
65
+ });
66
+ });
67
+ //# sourceMappingURL=preview.vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.vitest.js","sourceRoot":"","sources":["../../src/compare/preview.vitest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Logger } from '@knapsack/adapter-core';
2
+ import type { ArtifactRef, ConflictGroup } from './types.js';
3
+ export type { ArtifactRef, ConflictGroup } from './types.js';
4
+ /**
5
+ * Index filesystem artifacts by (entityKind + canonical relPath).
6
+ *
7
+ * Expected layout: entities/<entityKind>/<base>.<sourceType>-<hash>.<ext>
8
+ * Same entity = same entityKind + same base name (e.g. button.react.mdx or button.react.md);
9
+ * sourceType and hash are read from the filename.
10
+ */
11
+ export declare function indexEntitiesDir(entitiesRootAbs: string, logger: Logger): Promise<Map<string, ArtifactRef[]>>;
12
+ export declare function findConflictGroups(index: Map<string, ArtifactRef[]>, logger: Logger): ConflictGroup[];
13
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGrD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE7D,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAiD7D;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAoCrC;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EACjC,MAAM,EAAE,MAAM,GACb,aAAa,EAAE,CAmBjB"}