nx-json-parser 1.0.0 → 1.1.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/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './types.js';
2
2
  export * from './parser.js';
3
3
  export * from './transformer.js';
4
+ export * from './plugins/bullet-sections.js';
4
5
  /**
5
6
  * Convenience function to transform markdown string to JSON.
6
7
  * @param markdown The markdown string to parse.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AAIjC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAGpD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,8BAA8B,CAAC;AAI7C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAGpD"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './types.js';
2
2
  export * from './parser.js';
3
3
  export * from './transformer.js';
4
+ export * from './plugins/bullet-sections.js';
4
5
  import { JSONTransformer } from './transformer.js';
5
6
  /**
6
7
  * Convenience function to transform markdown string to JSON.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC3C,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,OAAO,WAAW,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,8BAA8B,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC3C,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,OAAO,WAAW,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC3C,CAAC"}
package/dist/parser.d.ts CHANGED
@@ -1,8 +1,29 @@
1
- import { MarkdownSection } from './types.js';
1
+ import { MarkdownSection, ParserOptions } from './types.js';
2
2
  export declare class RemarkParser {
3
3
  private processor;
4
+ private options;
5
+ constructor(options?: ParserOptions);
4
6
  parse(markdown: string): MarkdownSection[];
7
+ /**
8
+ * Process content nodes into appropriate format
9
+ */
5
10
  private processContent;
11
+ /**
12
+ * Process a bullet list item as a section (Mode 2: SECTIONS)
13
+ */
14
+ private processBulletAsSection;
15
+ /**
16
+ * Convert list to simple array of strings (Mode 1: ARRAY)
17
+ */
18
+ private listToArray;
19
+ /**
20
+ * Convert table to array of objects
21
+ */
6
22
  private tableToArray;
23
+ /**
24
+ * Convert sections to object (utility method)
25
+ */
26
+ sectionsToObject(sections: MarkdownSection[]): Record<string, any>;
27
+ private mergeAndPushSection;
7
28
  }
8
29
  //# sourceMappingURL=parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAI7C,qBAAa,YAAY;IACrB,OAAO,CAAC,SAAS,CAGc;IAE/B,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,EAAE;IA0C1C,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,YAAY;CAcvB"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAc,aAAa,EAAE,MAAM,YAAY,CAAC;AAIxE,qBAAa,YAAY;IACrB,OAAO,CAAC,SAAS,CAEG;IAEpB,OAAO,CAAC,OAAO,CAA0B;gBAE7B,OAAO,GAAE,aAAkB;IAOvC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,EAAE;IAmF1C;;OAEG;IACH,OAAO,CAAC,cAAc;IA+BtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAuE9B;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAoBpB;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAWlE,OAAO,CAAC,mBAAmB;CA0B9B"}
package/dist/parser.js CHANGED
@@ -2,24 +2,44 @@ import { unified } from 'unified';
2
2
  import remarkParse from 'remark-parse';
3
3
  import remarkGfm from 'remark-gfm';
4
4
  import { toString } from 'mdast-util-to-string';
5
- import { remarkBulletSections } from './plugins/bullet-sections.js';
5
+ import { BulletMode } from './types.js';
6
+ import { detectBulletMode } from './plugins/detect-bullet-mode.js';
6
7
  import { toCamelCase } from 'nx-helpers';
7
8
  export class RemarkParser {
8
9
  processor = unified()
9
10
  .use(remarkParse)
10
- .use(remarkGfm)
11
- .use(remarkBulletSections);
11
+ .use(remarkGfm);
12
+ options;
13
+ constructor(options = {}) {
14
+ this.options = {
15
+ bulletMode: options.bulletMode ?? BulletMode.AUTO,
16
+ debug: options.debug ?? false
17
+ };
18
+ }
12
19
  parse(markdown) {
13
20
  const tree = this.processor.runSync(this.processor.parse(markdown));
21
+ // Detect bullet mode if AUTO
22
+ let bulletMode = this.options.bulletMode;
23
+ if (bulletMode === BulletMode.AUTO) {
24
+ const detection = detectBulletMode(tree);
25
+ bulletMode = detection.mode === 'sections' ? BulletMode.SECTIONS : BulletMode.ARRAY;
26
+ if (this.options.debug) {
27
+ console.log('🔍 Bullet Mode Detection:');
28
+ console.log(` Mode: ${detection.mode}`);
29
+ console.log(` Confidence: ${(detection.confidence * 100).toFixed(0)}%`);
30
+ console.log(` Reasons:`);
31
+ detection.reasons.forEach(r => console.log(` - ${r}`));
32
+ }
33
+ }
14
34
  const sections = [];
15
35
  let currentSection = null;
16
36
  let currentNodes = [];
17
37
  const rootChildren = tree.children;
18
38
  for (const node of rootChildren) {
19
39
  if (node.type === 'heading') {
40
+ // Standard heading section (### Section)
20
41
  if (currentSection) {
21
- currentSection.content = this.processContent(currentNodes);
22
- sections.push(currentSection);
42
+ this.mergeAndPushSection(sections, currentSection, currentNodes, bulletMode);
23
43
  }
24
44
  currentSection = {
25
45
  heading: toString(node).trim(),
@@ -29,43 +49,164 @@ export class RemarkParser {
29
49
  };
30
50
  currentNodes = [];
31
51
  }
52
+ else if (node.type === 'list' && bulletMode === BulletMode.SECTIONS) {
53
+ // Bullet-style sections mode: each root-level bullet is a section
54
+ if (currentSection) {
55
+ this.mergeAndPushSection(sections, currentSection, currentNodes, bulletMode);
56
+ currentSection = null;
57
+ currentNodes = [];
58
+ }
59
+ // Process each list item as a section
60
+ const newSections = [];
61
+ for (const listItem of node.children) {
62
+ newSections.push(...this.processBulletAsSection(listItem));
63
+ }
64
+ if (newSections.length > 0) {
65
+ // Push all except the last one as complete sections
66
+ for (let i = 0; i < newSections.length - 1; i++) {
67
+ sections.push(newSections[i]);
68
+ }
69
+ // Keep the last one as currentSection to capture subsequent loose content
70
+ currentSection = newSections[newSections.length - 1];
71
+ }
72
+ }
32
73
  else {
33
74
  currentNodes.push(node);
34
75
  }
35
76
  }
77
+ // Save last section
36
78
  if (currentSection) {
37
- currentSection.content = this.processContent(currentNodes);
38
- sections.push(currentSection);
79
+ this.mergeAndPushSection(sections, currentSection, currentNodes, bulletMode);
39
80
  }
40
81
  else if (currentNodes.length > 0) {
82
+ // No sections found, put everything in root
41
83
  sections.push({
42
84
  heading: 'Root',
43
- content: this.processContent(currentNodes),
85
+ content: this.processContent(currentNodes, bulletMode),
44
86
  level: 0,
45
87
  format: 'text'
46
88
  });
47
89
  }
48
90
  return sections;
49
91
  }
50
- processContent(nodes) {
92
+ /**
93
+ * Process content nodes into appropriate format
94
+ */
95
+ processContent(nodes, bulletMode) {
51
96
  if (nodes.length === 0)
52
97
  return '';
98
+ // Single table - return as array of objects
53
99
  if (nodes.length === 1 && nodes[0].type === 'table') {
54
100
  return this.tableToArray(nodes[0]);
55
101
  }
102
+ // Single list - behavior depends on mode
56
103
  if (nodes.length === 1 && nodes[0].type === 'list') {
57
- return nodes[0].children.map((item) => toString(item).trim());
104
+ if (bulletMode === BulletMode.ARRAY) {
105
+ // Mode 1: Return as simple array of strings
106
+ return this.listToArray(nodes[0]);
107
+ }
108
+ else {
109
+ // Mode 2: In sections mode, nested lists are still arrays
110
+ return this.listToArray(nodes[0]);
111
+ }
58
112
  }
113
+ // Multiple nodes or mixed content
59
114
  return nodes.map(node => {
60
115
  if (node.type === 'table') {
61
- return JSON.stringify(this.tableToArray(node));
116
+ return this.tableToArray(node);
117
+ }
118
+ if (node.type === 'list') {
119
+ return this.listToArray(node);
62
120
  }
63
121
  return toString(node);
64
122
  }).join('\n\n').trim();
65
123
  }
124
+ /**
125
+ * Process a bullet list item as a section (Mode 2: SECTIONS)
126
+ */
127
+ processBulletAsSection(listItem) {
128
+ const sections = [];
129
+ // First child is the bullet text (section heading)
130
+ const firstChild = listItem.children[0];
131
+ if (!firstChild)
132
+ return sections;
133
+ const rawText = toString(firstChild).trim();
134
+ const lines = rawText.split('\n');
135
+ const heading = lines[0]?.trim() || '';
136
+ const sameNodeContent = lines.slice(1).join('\n').trim();
137
+ // Clean heading: remove trailing colon if present
138
+ const cleanHeading = heading.replace(/:$/, '');
139
+ // Rest of the children are the content
140
+ const contentNodes = listItem.children.slice(1);
141
+ let content;
142
+ if (contentNodes.length === 0) {
143
+ // No extra nodes, but maybe same-node content?
144
+ content = sameNodeContent;
145
+ }
146
+ else {
147
+ // If we have sameNodeContent, we should prepend it to the text content?
148
+ // This gets complicated if contentNodes are mixed.
149
+ // Simplified: If sameNodeContent exists, assume it's part of the text.
150
+ // Check for nested list
151
+ const nestedList = contentNodes.find((node) => node.type === 'list');
152
+ if (nestedList && contentNodes.length === 1) {
153
+ // ONLY a nested list - convert to array
154
+ content = this.listToArray(nestedList);
155
+ }
156
+ else if (nestedList) {
157
+ // Mixed content: paragraphs + nested list
158
+ // For now, convert nested list to array and combine
159
+ const paragraphs = contentNodes
160
+ .filter((node) => node.type !== 'list')
161
+ .map((node) => toString(node))
162
+ .join('\n\n')
163
+ .trim();
164
+ const fullText = sameNodeContent ? sameNodeContent + '\n\n' + paragraphs : paragraphs;
165
+ const nestedArray = this.listToArray(nestedList);
166
+ // Combine: return object with text and items
167
+ content = {
168
+ text: fullText,
169
+ items: nestedArray
170
+ };
171
+ }
172
+ else {
173
+ // Only paragraphs/text content
174
+ const nodeText = contentNodes
175
+ .map((node) => toString(node))
176
+ .join('\n\n')
177
+ .trim();
178
+ content = sameNodeContent ? sameNodeContent + '\n\n' + nodeText : nodeText;
179
+ }
180
+ }
181
+ sections.push({
182
+ heading: cleanHeading,
183
+ content,
184
+ level: 1,
185
+ format: 'bullet'
186
+ });
187
+ return sections;
188
+ }
189
+ /**
190
+ * Convert list to simple array of strings (Mode 1: ARRAY)
191
+ */
192
+ listToArray(listNode) {
193
+ return listNode.children.map((item) => {
194
+ // Get just the first paragraph/text, ignore nested content
195
+ const firstChild = item.children[0];
196
+ return toString(firstChild).trim();
197
+ });
198
+ }
199
+ /**
200
+ * Convert table to array of objects
201
+ */
66
202
  tableToArray(tableNode) {
67
- const headers = tableNode.children[0].children.map((cell) => toCamelCase(toString(cell).trim()));
68
- return tableNode.children.slice(1).map((row) => {
203
+ const rows = tableNode.children;
204
+ if (rows.length === 0)
205
+ return [];
206
+ // First row = headers
207
+ const headers = rows[0].children.map((cell) => toCamelCase(toString(cell).trim()));
208
+ // Remaining rows = data
209
+ return rows.slice(1).map((row) => {
69
210
  const obj = {};
70
211
  row.children.forEach((cell, i) => {
71
212
  const key = headers[i] || `column${i}`;
@@ -74,5 +215,42 @@ export class RemarkParser {
74
215
  return obj;
75
216
  });
76
217
  }
218
+ /**
219
+ * Convert sections to object (utility method)
220
+ */
221
+ sectionsToObject(sections) {
222
+ const result = {};
223
+ for (const section of sections) {
224
+ const key = toCamelCase(section.heading);
225
+ result[key] = section.content;
226
+ }
227
+ return result;
228
+ }
229
+ mergeAndPushSection(sections, section, nodes, bulletMode) {
230
+ // If the section is already in the list (because we pushed it in the loop but kept a reference),
231
+ // we shouldn't push it again?
232
+ // Wait, my logic implementation in loop:
233
+ // "newSections.push(...); if (newSections.length > 0) { ... loop push n-1 ... currentSection = nth }"
234
+ // So currentSection is NOT in sections yet.
235
+ // It's safe to push.
236
+ const newContent = this.processContent(nodes, bulletMode);
237
+ if (newContent) {
238
+ if (!section.content) {
239
+ section.content = newContent;
240
+ }
241
+ else if (typeof section.content === 'string' && typeof newContent === 'string') {
242
+ section.content += '\n\n' + newContent;
243
+ }
244
+ else {
245
+ if (Array.isArray(section.content)) {
246
+ section.content.push(newContent);
247
+ }
248
+ else {
249
+ section.content = [section.content, newContent];
250
+ }
251
+ }
252
+ }
253
+ sections.push(section);
254
+ }
77
255
  }
78
256
  //# sourceMappingURL=parser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"bullet-sections.d.ts","sourceRoot":"","sources":["../../src/plugins/bullet-sections.ts"],"names":[],"mappings":"AAEA,wBAAgB,oBAAoB,KACxB,MAAM,GAAG,UAkFpB"}
1
+ {"version":3,"file":"bullet-sections.d.ts","sourceRoot":"","sources":["../../src/plugins/bullet-sections.ts"],"names":[],"mappings":"AAgBA,wBAAgB,oBAAoB,KAC1B,MAAM,GAAG,UAqBjB"}
@@ -1,79 +1,49 @@
1
+ /**
2
+ * Remark plugin to handle bullet-style sections
3
+ * Converts bullets that look like sections into heading nodes
4
+ */
5
+ import { visit } from 'unist-util-visit';
1
6
  import { toString } from 'mdast-util-to-string';
7
+ const SECTION_KEYWORDS = [
8
+ 'answer', 'summary', 'introduction', 'conclusion', 'overview',
9
+ 'assumptions', 'unknowns', 'evidence', 'notes', 'details',
10
+ 'description', 'background', 'analysis', 'findings', 'recommendations',
11
+ 'data', 'identity', 'network', 'security', 'monitoring', 'governance',
12
+ 'availability', 'backup', 'patch', 'operational', 'provider'
13
+ ];
2
14
  export function remarkBulletSections() {
3
15
  return (tree) => {
4
- const children = tree.children;
5
- if (!children)
6
- return;
7
- for (let i = 0; i < children.length; i++) {
8
- const node = children[i];
9
- if (node.type === 'list' && node.ordered === false) {
10
- const items = node.children;
11
- if (items.length === 0)
12
- continue;
13
- const newRootNodes = [];
14
- let currentListItems = [];
15
- items.forEach((item, idx) => {
16
- const firstChild = item.children[0];
17
- const text = firstChild ? toString(firstChild) : '';
18
- const lines = text.trim().split('\n');
19
- const firstLine = lines[0]?.trim() || '';
20
- const isShort = firstLine.length > 0 && firstLine.length < 150;
21
- const hasMoreContent = lines.length > 1 || item.children.length > 1;
22
- // Section detection:
23
- // 1. It's short and has more content.
24
- // 2. OR it's short and is followed by a non-short item? (Hard to check here)
25
- // 3. OR it's one of the "known" section keywords.
26
- const keywords = ['answer', 'assumptions', 'unknowns', 'evidence', 'protection', 'control', 'management', 'design', 'logging', 'monitoring', 'backups', 'compliance', 'governance', 'modeling', 'incident', 'vendor', 'changes'];
27
- const isSectionKeyword = keywords.some(k => firstLine.toLowerCase().includes(k));
28
- if (isShort && (hasMoreContent || isSectionKeyword)) {
29
- // Flush existing list items if any
30
- if (currentListItems.length > 0) {
31
- newRootNodes.push({
32
- type: 'list',
33
- ordered: false,
34
- children: [...currentListItems]
35
- });
36
- currentListItems = [];
37
- }
38
- // Add as heading
39
- newRootNodes.push({
40
- type: 'heading',
41
- depth: 2,
42
- children: [{ type: 'text', value: firstLine }]
43
- });
44
- // Add content
45
- if (lines.length > 1) {
46
- newRootNodes.push({
47
- type: 'paragraph',
48
- children: [{ type: 'text', value: lines.slice(1).join('\n').trim() }]
49
- });
50
- }
51
- if (item.children.length > 1) {
52
- newRootNodes.push(...item.children.slice(1));
53
- }
54
- }
55
- else {
56
- currentListItems.push(item);
57
- }
58
- });
59
- // Flush remaining
60
- if (currentListItems.length > 0) {
61
- newRootNodes.push({
62
- type: 'list',
63
- ordered: false,
64
- children: [...currentListItems]
65
- });
66
- }
67
- if (newRootNodes.length > 0) {
68
- // If we performed any transformation (i.e., we found at least one heading)
69
- const hasHeadings = newRootNodes.some(n => n.type === 'heading');
70
- if (hasHeadings) {
71
- children.splice(i, 1, ...newRootNodes);
72
- i += newRootNodes.length - 1;
73
- }
74
- }
16
+ const transformations = [];
17
+ // First pass: identify bullets that should become headings
18
+ visit(tree, 'list', (node, index, parent) => {
19
+ if (!parent || node.ordered)
20
+ return; // Only unordered lists
21
+ // Don't transform if there are no section bullets
22
+ // This preserves normal list behavior
23
+ if (transformations.length === 0) {
24
+ return tree;
75
25
  }
76
- }
26
+ // For now, just mark them for detection
27
+ // The actual transformation happens in the parser
28
+ return tree;
29
+ });
77
30
  };
78
31
  }
32
+ function isSectionBullet(listItem) {
33
+ if (!listItem.children || listItem.children.length === 0) {
34
+ return false;
35
+ }
36
+ const firstChild = listItem.children[0];
37
+ const text = toString(firstChild).toLowerCase();
38
+ // Check for section indicators
39
+ const hasKeyword = SECTION_KEYWORDS.some(kw => text.includes(kw));
40
+ const hasColon = text.includes(':');
41
+ const hasContent = listItem.children.length > 1;
42
+ const isCapitalized = /^[A-Z]/.test(text);
43
+ const isLong = text.length > 30;
44
+ // It's likely a section if it has multiple indicators
45
+ const indicators = [hasKeyword, hasColon, hasContent, isCapitalized, isLong];
46
+ const score = indicators.filter(Boolean).length;
47
+ return score >= 2;
48
+ }
79
49
  //# sourceMappingURL=bullet-sections.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bullet-sections.js","sourceRoot":"","sources":["../../src/plugins/bullet-sections.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bullet-sections.js","sourceRoot":"","sources":["../../src/plugins/bullet-sections.ts"],"names":[],"mappings":"AACA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,MAAM,gBAAgB,GAAG;IACvB,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU;IAC7D,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS;IACzD,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB;IACtE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY;IACrE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU;CAC7D,CAAC;AAEF,MAAM,UAAU,oBAAoB;IAClC,OAAO,CAAC,IAAS,EAAE,EAAE;QACnB,MAAM,eAAe,GAIhB,EAAE,CAAC;QAER,2DAA2D;QAC9D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,IAAS,EAAE,KAAc,EAAE,MAAY,EAAE,EAAE;YAC/D,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO;gBAAE,OAAO,CAAC,uBAAuB;YAE1D,kDAAkD;YAClD,sCAAsC;YACtC,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjC,OAAO,IAAI,CAAC;YACd,CAAC;YAED,wCAAwC;YACxC,kDAAkD;YAClD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAA;AAAA,CAAC;AAEF,SAAS,eAAe,CAAC,QAAa;IACpC,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IAEhD,+BAA+B;IAC/B,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IAEhC,sDAAsD;IACtD,MAAM,UAAU,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAEhD,OAAO,KAAK,IAAI,CAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface BulletModeResult {
2
+ mode: 'array' | 'sections';
3
+ confidence: number;
4
+ reasons: string[];
5
+ }
6
+ export declare function detectBulletMode(tree: any): BulletModeResult;
7
+ //# sourceMappingURL=detect-bullet-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-bullet-mode.d.ts","sourceRoot":"","sources":["../../src/plugins/detect-bullet-mode.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAUD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,gBAAgB,CA4I5D"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Detect if bullets should be treated as array items or sections
3
+ */
4
+ import { visit } from 'unist-util-visit';
5
+ import { toString } from 'mdast-util-to-string';
6
+ const SECTION_KEYWORDS = [
7
+ 'answer', 'summary', 'introduction', 'conclusion', 'overview',
8
+ 'assumptions', 'unknowns', 'evidence', 'notes', 'details',
9
+ 'description', 'background', 'analysis', 'findings', 'recommendations',
10
+ 'data', 'identity', 'network', 'security', 'monitoring', 'governance',
11
+ 'availability', 'backup', 'patch', 'operational', 'provider'
12
+ ];
13
+ export function detectBulletMode(tree) {
14
+ const reasons = [];
15
+ let sectionScore = 0;
16
+ let arrayScore = 0;
17
+ let bulletCount = 0;
18
+ let bulletsWithContent = 0;
19
+ let bulletsWithColons = 0;
20
+ let bulletsWithNestedLists = 0;
21
+ let bulletsWithKeywords = 0;
22
+ let totalBulletLength = 0;
23
+ let rootLevelBullets = 0;
24
+ // Analyze the tree structure
25
+ visit(tree, 'list', (listNode, index, parent) => {
26
+ // Only analyze root-level lists or lists directly under root
27
+ const isRootLevel = parent?.type === 'root';
28
+ if (!isRootLevel)
29
+ return;
30
+ rootLevelBullets += listNode.children.length;
31
+ for (const listItem of listNode.children) {
32
+ bulletCount++;
33
+ // Get the text of this list item (first paragraph/text only)
34
+ const firstChild = listItem.children[0];
35
+ const itemText = firstChild ? toString(firstChild) : '';
36
+ totalBulletLength += itemText.length;
37
+ // 1. Check for colons (title pattern: "Short answer:" or "Data protection:")
38
+ if (itemText.includes(':')) {
39
+ bulletsWithColons++;
40
+ }
41
+ // 2. Check for content after the bullet (paragraphs, nested items)
42
+ if (listItem.children.length > 1) {
43
+ bulletsWithContent++;
44
+ }
45
+ // 3. Check for nested lists
46
+ const hasNestedList = listItem.children.some((child) => child.type === 'list');
47
+ if (hasNestedList) {
48
+ bulletsWithNestedLists++;
49
+ }
50
+ // 4. Check for section keywords
51
+ const lowerText = itemText.toLowerCase();
52
+ if (SECTION_KEYWORDS.some(kw => lowerText.includes(kw))) {
53
+ bulletsWithKeywords++;
54
+ }
55
+ }
56
+ });
57
+ if (bulletCount === 0) {
58
+ return {
59
+ mode: 'array',
60
+ confidence: 0,
61
+ reasons: ['No bullets found']
62
+ };
63
+ }
64
+ const avgLength = totalBulletLength / bulletCount;
65
+ // === SCORING FOR SECTIONS MODE ===
66
+ if (bulletsWithColons > 0) {
67
+ const percent = (bulletsWithColons / bulletCount * 100).toFixed(0);
68
+ sectionScore += 3;
69
+ reasons.push(`${bulletsWithColons}/${bulletCount} bullets have colons (${percent}%) - strong section indicator`);
70
+ }
71
+ if (bulletsWithContent > 0) {
72
+ const percent = (bulletsWithContent / bulletCount * 100).toFixed(0);
73
+ sectionScore += 3;
74
+ reasons.push(`${bulletsWithContent}/${bulletCount} bullets have content below (${percent}%) - strong section indicator`);
75
+ }
76
+ if (bulletsWithNestedLists > 0) {
77
+ sectionScore += 2;
78
+ reasons.push(`${bulletsWithNestedLists} bullets have nested lists - section indicator`);
79
+ }
80
+ if (bulletsWithKeywords > 0) {
81
+ sectionScore += 2;
82
+ reasons.push(`${bulletsWithKeywords} bullets contain section keywords`);
83
+ }
84
+ if (avgLength > 30) {
85
+ sectionScore += 1;
86
+ reasons.push(`Long bullet text (avg ${avgLength.toFixed(0)} chars) - suggests titles`);
87
+ }
88
+ if (bulletCount <= 5 && (bulletsWithContent > 0 || bulletsWithColons > 0)) {
89
+ sectionScore += 1;
90
+ reasons.push('Few bullets with rich content suggests sections');
91
+ }
92
+ // === SCORING FOR ARRAY MODE ===
93
+ if (bulletsWithColons === 0 && bulletsWithContent === 0 && bulletsWithNestedLists === 0) {
94
+ arrayScore += 4;
95
+ reasons.push('No bullets have content, colons, or nesting - pure list indicator');
96
+ }
97
+ if (avgLength < 30) {
98
+ arrayScore += 2;
99
+ reasons.push(`Short bullet text (avg ${avgLength.toFixed(0)} chars) - suggests list items`);
100
+ }
101
+ if (bulletCount >= 3 && bulletsWithContent === 0) {
102
+ arrayScore += 2;
103
+ reasons.push(`${bulletCount} simple bullets without content - array pattern`);
104
+ }
105
+ if (bulletsWithKeywords === 0) {
106
+ arrayScore += 1;
107
+ reasons.push('No section keywords found');
108
+ }
109
+ // Edge case: all bullets are very short and no special features
110
+ if (avgLength < 20 && bulletsWithContent === 0 && bulletsWithColons === 0) {
111
+ arrayScore += 2;
112
+ reasons.push('Very short bullets with no features - definitely array');
113
+ }
114
+ // === DECISION ===
115
+ const totalScore = sectionScore + arrayScore;
116
+ const mode = sectionScore > arrayScore ? 'sections' : 'array';
117
+ const confidence = totalScore > 0 ? Math.abs(sectionScore - arrayScore) / totalScore : 0;
118
+ // Format reasons (top 5)
119
+ const topReasons = reasons.slice(0, 5);
120
+ return {
121
+ mode,
122
+ confidence,
123
+ reasons: topReasons
124
+ };
125
+ }
126
+ //# sourceMappingURL=detect-bullet-mode.js.map