patram 0.6.2 → 0.8.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 (55) hide show
  1. package/lib/build-graph-identity.d.ts +40 -0
  2. package/lib/build-graph.d.ts +11 -0
  3. package/lib/build-graph.types.d.ts +24 -0
  4. package/lib/claim-helpers.d.ts +20 -0
  5. package/lib/cli-help-metadata.js +48 -9
  6. package/lib/command-output.js +1 -0
  7. package/lib/document-node-identity.d.ts +47 -0
  8. package/lib/inspect-reverse-references.js +184 -0
  9. package/lib/layout-incoming-references.js +105 -0
  10. package/lib/layout-incoming-summary-lines.js +21 -0
  11. package/lib/list-source-files.d.ts +29 -0
  12. package/lib/load-patram-config.d.ts +384 -0
  13. package/lib/load-patram-config.types.d.ts +45 -0
  14. package/lib/load-project-graph.d.ts +35 -0
  15. package/lib/output-view.types.d.ts +88 -0
  16. package/lib/output-view.types.ts +19 -2
  17. package/lib/overlay-graph.d.ts +43 -0
  18. package/lib/overlay-graph.js +191 -0
  19. package/lib/parse-claims.d.ts +40 -0
  20. package/lib/parse-claims.types.d.ts +31 -0
  21. package/lib/parse-cli-arguments-helpers.js +11 -4
  22. package/lib/parse-cli-arguments.types.ts +8 -2
  23. package/lib/parse-jsdoc-blocks.d.ts +15 -0
  24. package/lib/parse-jsdoc-claims.d.ts +9 -0
  25. package/lib/parse-jsdoc-prose.d.ts +28 -0
  26. package/lib/parse-markdown-claims.d.ts +14 -0
  27. package/lib/parse-markdown-directives.d.ts +34 -0
  28. package/lib/parse-where-clause.d.ts +75 -0
  29. package/lib/parse-where-clause.js +157 -37
  30. package/lib/parse-where-clause.types.d.ts +63 -0
  31. package/lib/parse-yaml-claims.d.ts +38 -0
  32. package/lib/patram-cli.js +66 -1
  33. package/lib/patram-config.d.ts +106 -0
  34. package/lib/patram-config.types.d.ts +14 -0
  35. package/lib/patram.d.ts +73 -0
  36. package/lib/patram.js +3 -0
  37. package/lib/query-graph.d.ts +68 -0
  38. package/lib/query-graph.js +27 -24
  39. package/lib/query-inspection.d.ts +86 -0
  40. package/lib/render-cli-help.js +1 -1
  41. package/lib/render-json-output.js +91 -62
  42. package/lib/render-output-view.js +58 -3
  43. package/lib/render-plain-output.js +46 -3
  44. package/lib/render-rich-output.js +50 -5
  45. package/lib/resolve-patram-graph-config.d.ts +9 -0
  46. package/lib/reverse-reference-test-helpers.js +76 -0
  47. package/lib/show-document.js +44 -6
  48. package/lib/source-file-defaults.d.ts +5 -0
  49. package/lib/tagged-fenced-block-error.d.ts +10 -0
  50. package/lib/tagged-fenced-block-markdown.d.ts +47 -0
  51. package/lib/tagged-fenced-block-metadata.d.ts +26 -0
  52. package/lib/tagged-fenced-block-parser.d.ts +61 -0
  53. package/lib/tagged-fenced-blocks.d.ts +39 -0
  54. package/lib/tagged-fenced-blocks.types.d.ts +32 -0
  55. package/package.json +13 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, ShowOutputView } from './output-view.types.ts';
2
+ * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, RefsOutputView, ShowOutputView } from './output-view.types.ts';
3
3
  */
4
4
 
5
5
  import {
@@ -8,6 +8,8 @@ import {
8
8
  } from './format-output-metadata.js';
9
9
  import { formatNodeHeader } from './format-node-header.js';
10
10
  import { formatOutputItemBlock } from './format-output-item-block.js';
11
+ import { layoutIncomingReferenceLines } from './layout-incoming-references.js';
12
+ import { layoutIncomingSummaryLines } from './layout-incoming-summary-lines.js';
11
13
  import { layoutStoredQueries } from './layout-stored-queries.js';
12
14
 
13
15
  /**
@@ -25,6 +27,10 @@ export function renderPlainOutput(output_view) {
25
27
  return renderPlainStoredQueries(output_view.items);
26
28
  }
27
29
 
30
+ if (output_view.command === 'refs') {
31
+ return renderPlainRefsOutput(output_view);
32
+ }
33
+
28
34
  if (output_view.command === 'show') {
29
35
  return renderPlainShowOutput(output_view);
30
36
  }
@@ -85,8 +91,13 @@ function renderPlainShowOutput(output_view) {
85
91
  const document_summary = output_view.document
86
92
  ? formatPlainNodeItem(output_view.document)
87
93
  : '';
94
+ const incoming_summary = renderPlainIncomingSummary(output_view);
88
95
 
89
- if (document_summary.length === 0 && output_view.items.length === 0) {
96
+ if (
97
+ document_summary.length === 0 &&
98
+ output_view.items.length === 0 &&
99
+ incoming_summary.length === 0
100
+ ) {
90
101
  return `${rendered_source}\n`;
91
102
  }
92
103
 
@@ -99,7 +110,28 @@ function renderPlainShowOutput(output_view) {
99
110
 
100
111
  summary_items.push(...output_view.items.map(formatPlainResolvedLinkItem));
101
112
 
102
- return `${rendered_source}\n\n----------------\n${summary_items.join('\n\n')}\n`;
113
+ const summary_blocks = summary_items.filter((summary_item) => summary_item);
114
+
115
+ if (incoming_summary.length > 0) {
116
+ summary_blocks.push(incoming_summary);
117
+ }
118
+
119
+ return `${rendered_source}\n\n----------------\n${summary_blocks.join('\n\n')}\n`;
120
+ }
121
+
122
+ /**
123
+ * @param {RefsOutputView} output_view
124
+ * @returns {string}
125
+ */
126
+ function renderPlainRefsOutput(output_view) {
127
+ const node_summary = formatPlainNodeItem(output_view.node);
128
+ const output_lines = layoutIncomingReferenceLines(output_view.incoming);
129
+
130
+ if (output_lines.length === 0) {
131
+ return `${node_summary}\n\nNo incoming references.\n`;
132
+ }
133
+
134
+ return `${node_summary}\n\n${output_lines.join('\n')}\n`;
103
135
  }
104
136
 
105
137
  /**
@@ -135,6 +167,17 @@ function formatPlainResolvedLinkItem(output_item) {
135
167
  });
136
168
  }
137
169
 
170
+ /**
171
+ * @param {ShowOutputView} output_view
172
+ * @returns {string}
173
+ */
174
+ function renderPlainIncomingSummary(output_view) {
175
+ const output_lines = layoutIncomingSummaryLines(output_view.incoming_summary);
176
+ output_lines.push('', `Hint: patram refs ${output_view.path}`);
177
+
178
+ return output_lines.join('\n');
179
+ }
180
+
138
181
  /**
139
182
  * @param {string} value
140
183
  * @returns {string}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @import { CliColorMode } from './parse-cli-arguments.types.ts';
3
- * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, ShowOutputView } from './output-view.types.ts';
3
+ * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, RefsOutputView, ShowOutputView } from './output-view.types.ts';
4
4
  */
5
5
 
6
6
  import { Ansis } from 'ansis';
@@ -11,6 +11,8 @@ import {
11
11
  } from './format-output-metadata.js';
12
12
  import { formatNodeHeader } from './format-node-header.js';
13
13
  import { formatOutputItemBlock } from './format-output-item-block.js';
14
+ import { layoutIncomingReferenceLines } from './layout-incoming-references.js';
15
+ import { layoutIncomingSummaryLines } from './layout-incoming-summary-lines.js';
14
16
  import { layoutStoredQueries } from './layout-stored-queries.js';
15
17
  import { renderRichSource } from './render-rich-source.js';
16
18
 
@@ -34,6 +36,9 @@ export async function renderRichOutput(output_view, render_options) {
34
36
  return renderRichStoredQueries(output_view.items, ansi);
35
37
  }
36
38
 
39
+ if (output_view.command === 'refs') {
40
+ return renderRichRefsOutput(output_view, ansi);
41
+ }
37
42
  if (output_view.command === 'show') {
38
43
  return renderRichShowOutput(output_view, render_options, ansi);
39
44
  }
@@ -105,10 +110,7 @@ async function renderRichShowOutput(output_view, render_options, ansi) {
105
110
  const document_summary = output_view.document
106
111
  ? formatRichNodeItem(output_view.document, ansi)
107
112
  : '';
108
-
109
- if (document_summary.length === 0 && output_view.items.length === 0) {
110
- return `${rendered_source}\n`;
111
- }
113
+ const incoming_summary = renderRichIncomingSummary(output_view, ansi);
112
114
 
113
115
  /** @type {string[]} */
114
116
  const summary_items = [];
@@ -121,9 +123,34 @@ async function renderRichShowOutput(output_view, render_options, ansi) {
121
123
  ...output_view.items.map((item) => formatRichResolvedLinkItem(item, ansi)),
122
124
  );
123
125
 
126
+ if (incoming_summary.length > 0) {
127
+ summary_items.push(incoming_summary);
128
+ }
129
+
124
130
  return `${rendered_source}\n\n${ansi.gray(FULL_WIDTH_DIVIDER)}\n\n${summary_items.join('\n\n')}\n`;
125
131
  }
126
132
 
133
+ /**
134
+ * @param {RefsOutputView} output_view
135
+ * @param {Ansis} ansi
136
+ * @returns {string}
137
+ */
138
+ function renderRichRefsOutput(output_view, ansi) {
139
+ const node_summary = formatRichNodeItem(output_view.node, ansi);
140
+ const output_lines = layoutIncomingReferenceLines(output_view.incoming, {
141
+ format_node_header(output_item) {
142
+ return ansi.green(formatNodeHeader(output_item));
143
+ },
144
+ format_relation_header(relation_name, relation_count) {
145
+ return `${ansi.bold(relation_name)} ${ansi.gray(`(${relation_count})`)}`;
146
+ },
147
+ });
148
+
149
+ return output_lines.length === 0
150
+ ? `${node_summary}\n\n${ansi.yellow('No incoming references.')}\n`
151
+ : `${node_summary}\n\n${output_lines.join('\n')}\n`;
152
+ }
153
+
127
154
  /**
128
155
  * @param {OutputNodeItem} output_item
129
156
  * @param {Ansis} ansi
@@ -162,6 +189,24 @@ function formatRichResolvedLinkItem(output_item, ansi) {
162
189
  });
163
190
  }
164
191
 
192
+ /**
193
+ * @param {ShowOutputView} output_view
194
+ * @param {Ansis} ansi
195
+ * @returns {string}
196
+ */
197
+ function renderRichIncomingSummary(output_view, ansi) {
198
+ const output_lines = layoutIncomingSummaryLines(output_view.incoming_summary);
199
+ output_lines[0] = ansi.bold(output_lines[0]);
200
+
201
+ if (Object.keys(output_view.incoming_summary).length === 0) {
202
+ output_lines[1] = ` ${ansi.gray('none')}`;
203
+ }
204
+
205
+ output_lines.push('', ansi.gray(`Hint: patram refs ${output_view.path}`));
206
+
207
+ return output_lines.join('\n');
208
+ }
209
+
165
210
  /**
166
211
  * @param {boolean} color_enabled
167
212
  * @returns {Ansis}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Merge built-in Patram graph semantics with repo-defined schema.
3
+ *
4
+ * @param {PatramRepoConfig} repo_config
5
+ * @returns {PatramConfig}
6
+ */
7
+ export function resolvePatramGraphConfig(repo_config: PatramRepoConfig): PatramConfig;
8
+ import type { PatramRepoConfig } from './load-patram-config.types.ts';
9
+ import type { PatramConfig } from './patram-config.types.ts';
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Shared reverse-reference graph fixtures for tests.
3
+ */
4
+
5
+ export function createDecisionNode() {
6
+ return {
7
+ $class: 'decision',
8
+ $id: 'decision:query-language',
9
+ $path: 'docs/decisions/query-language.md',
10
+ id: 'decision:query-language',
11
+ path: 'docs/decisions/query-language.md',
12
+ status: 'accepted',
13
+ title: 'Query Language',
14
+ };
15
+ }
16
+
17
+ export function createReconcileNode() {
18
+ return {
19
+ $class: 'document',
20
+ $id: 'doc:lib/reconcile.js',
21
+ $path: 'lib/reconcile.js',
22
+ id: 'doc:lib/reconcile.js',
23
+ path: 'lib/reconcile.js',
24
+ title: 'Reconciler entrypoint.',
25
+ };
26
+ }
27
+
28
+ export function createResumeNode() {
29
+ return {
30
+ $class: 'document',
31
+ $id: 'doc:lib/resume.js',
32
+ $path: 'lib/resume.js',
33
+ id: 'doc:lib/resume.js',
34
+ path: 'lib/resume.js',
35
+ title: 'Resume entrypoint.',
36
+ };
37
+ }
38
+
39
+ export function createTaskNode() {
40
+ return {
41
+ $class: 'task',
42
+ $id: 'task:reverse-reference-inspection',
43
+ $path: 'docs/tasks/v0/reverse-reference-inspection.md',
44
+ id: 'task:reverse-reference-inspection',
45
+ path: 'docs/tasks/v0/reverse-reference-inspection.md',
46
+ status: 'ready',
47
+ title: 'Implement reverse reference inspection',
48
+ };
49
+ }
50
+
51
+ /**
52
+ * @param {string} edge_id
53
+ * @param {string} from_id
54
+ * @param {string} origin_path
55
+ * @param {string} relation_name
56
+ * @param {string} to_id
57
+ */
58
+ export function createGraphEdge(
59
+ edge_id,
60
+ from_id,
61
+ origin_path,
62
+ relation_name,
63
+ to_id,
64
+ ) {
65
+ return {
66
+ from: from_id,
67
+ id: edge_id,
68
+ origin: {
69
+ column: 1,
70
+ line: 1,
71
+ path: origin_path,
72
+ },
73
+ relation: relation_name,
74
+ to: to_id,
75
+ };
76
+ }
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable max-lines */
2
2
  /**
3
- * @import { GraphNode } from './build-graph.types.ts';
3
+ * @import { BuildGraphResult, GraphNode } from './build-graph.types.ts';
4
4
  * @import { PatramClaim } from './parse-claims.types.ts';
5
5
  * @import { PatramDiagnostic } from './load-patram-config.types.ts';
6
6
  */
@@ -9,6 +9,7 @@ import { readFile } from 'node:fs/promises';
9
9
  import { posix, relative, resolve } from 'node:path';
10
10
 
11
11
  import { resolveDocumentNodeId } from './build-graph-identity.js';
12
+ import { inspectReverseReferences } from './inspect-reverse-references.js';
12
13
  import { parseSourceFile } from './parse-claims.js';
13
14
 
14
15
  /**
@@ -30,11 +31,12 @@ import { parseSourceFile } from './parse-claims.js';
30
31
  /**
31
32
  * @param {string} requested_file_path
32
33
  * @param {string} project_directory
33
- * @param {import('./build-graph.types.ts').BuildGraphResult} graph
34
+ * @param {BuildGraphResult} graph
34
35
  * @returns {Promise<
35
36
  * | {
36
37
  * success: true;
37
38
  * value: {
39
+ * incoming_summary: Record<string, number>;
38
40
  * path: string;
39
41
  * rendered_source: string;
40
42
  * resolved_links: Array<{
@@ -93,6 +95,7 @@ export async function loadShowOutput(
93
95
  source_file_path,
94
96
  source_text,
95
97
  parse_result.claims,
98
+ graph,
96
99
  graph.document_node_ids,
97
100
  graph.nodes,
98
101
  ),
@@ -103,14 +106,16 @@ export async function loadShowOutput(
103
106
  * @param {string} source_file_path
104
107
  * @param {string} source_text
105
108
  * @param {PatramClaim[]} claims
109
+ * @param {BuildGraphResult} graph
106
110
  * @param {import('./build-graph.types.ts').BuildGraphResult['document_node_ids']} document_node_ids
107
111
  * @param {Record<string, GraphNode>} graph_nodes
108
- * @returns {{ path: string, rendered_source: string, resolved_links: Array<{ label: string, reference: number, target: { kind?: string, path: string, status?: string, title: string } }>, source: string }}
112
+ * @returns {{ incoming_summary: Record<string, number>, path: string, rendered_source: string, resolved_links: Array<{ label: string, reference: number, target: { kind?: string, path: string, status?: string, title: string } }>, source: string }}
109
113
  */
110
114
  function createShowOutput(
111
115
  source_file_path,
112
116
  source_text,
113
117
  claims,
118
+ graph,
114
119
  document_node_ids,
115
120
  graph_nodes,
116
121
  ) {
@@ -125,8 +130,15 @@ function createShowOutput(
125
130
  graph_nodes,
126
131
  ),
127
132
  );
133
+ const reverse_references = inspectReverseReferences(
134
+ graph,
135
+ source_file_path,
136
+ undefined,
137
+ undefined,
138
+ );
128
139
 
129
140
  return {
141
+ incoming_summary: summarizeIncomingReferences(reverse_references.incoming),
130
142
  path: source_file_path,
131
143
  rendered_source: renderResolvedSource(
132
144
  source_text,
@@ -269,12 +281,23 @@ function getScalarGraphField(field_value) {
269
281
  * @returns {{ kind?: string, path: string, status?: string, title: string }}
270
282
  */
271
283
  function createResolvedLinkTarget(target_node, target_path, fallback_title) {
272
- return {
273
- kind: getResolvedLinkTargetKind(target_node),
284
+ /** @type {{ kind?: string, path: string, status?: string, title: string }} */
285
+ const resolved_target = {
274
286
  path: getResolvedLinkTargetPath(target_node, target_path),
275
- status: getScalarGraphField(target_node?.status),
276
287
  title: getScalarGraphField(target_node?.title) ?? fallback_title,
277
288
  };
289
+ const target_kind = getResolvedLinkTargetKind(target_node);
290
+ const target_status = getScalarGraphField(target_node?.status);
291
+
292
+ if (target_kind) {
293
+ resolved_target.kind = target_kind;
294
+ }
295
+
296
+ if (target_status) {
297
+ resolved_target.status = target_status;
298
+ }
299
+
300
+ return resolved_target;
278
301
  }
279
302
 
280
303
  /**
@@ -355,6 +378,21 @@ function trimTrailingLineBreaks(value) {
355
378
  return value.replace(/\n+$/du, '');
356
379
  }
357
380
 
381
+ /**
382
+ * @param {Record<string, GraphNode[]>} incoming
383
+ * @returns {Record<string, number>}
384
+ */
385
+ function summarizeIncomingReferences(incoming) {
386
+ /** @type {Record<string, number>} */
387
+ const incoming_summary = {};
388
+
389
+ for (const relation_name of Object.keys(incoming)) {
390
+ incoming_summary[relation_name] = incoming[relation_name].length;
391
+ }
392
+
393
+ return incoming_summary;
394
+ }
395
+
358
396
  /**
359
397
  * @param {unknown} error
360
398
  * @returns {error is NodeJS.ErrnoException}
@@ -0,0 +1,5 @@
1
+ export const MARKDOWN_SOURCE_FILE_EXTENSIONS: string[];
2
+ export const YAML_SOURCE_FILE_EXTENSIONS: string[];
3
+ export const JSDOC_SOURCE_FILE_EXTENSIONS: string[];
4
+ export const SUPPORTED_SOURCE_FILE_EXTENSIONS: string[];
5
+ export const DEFAULT_INCLUDE_PATTERNS: string[];
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @import { TaggedFencedBlockError } from './tagged-fenced-blocks.types.ts';
3
+ */
4
+ /**
5
+ * @param {string} code
6
+ * @param {string} message
7
+ * @returns {TaggedFencedBlockError}
8
+ */
9
+ export function createTaggedFencedBlockError(code: string, message: string): TaggedFencedBlockError;
10
+ import type { TaggedFencedBlockError } from './tagged-fenced-blocks.types.ts';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @param {string[]} lines
3
+ * @returns {number}
4
+ */
5
+ export function findMarkdownBodyStartLineIndex(lines: string[]): number;
6
+ /**
7
+ * @param {string[]} lines
8
+ * @param {number} body_start
9
+ * @returns {string}
10
+ */
11
+ export function getMarkdownTitle(lines: string[], body_start: number): string;
12
+ /**
13
+ * @param {string} line
14
+ * @returns {{ level: number, text: string } | null}
15
+ */
16
+ export function parseHeading(line: string): {
17
+ level: number;
18
+ text: string;
19
+ } | null;
20
+ /**
21
+ * @param {string[]} heading_path
22
+ * @param {string} title
23
+ * @param {{ level: number, text: string }} heading
24
+ * @returns {string[]}
25
+ */
26
+ export function updateHeadingPath(heading_path: string[], title: string, heading: {
27
+ level: number;
28
+ text: string;
29
+ }): string[];
30
+ /**
31
+ * @param {string} line
32
+ * @returns {{ character: string, lang: string, length: number } | null}
33
+ */
34
+ export function parseOpeningMarkdownFence(line: string): {
35
+ character: string;
36
+ lang: string;
37
+ length: number;
38
+ } | null;
39
+ /**
40
+ * @param {string} line
41
+ * @param {{ character: string, length: number }} open_fence
42
+ * @returns {boolean}
43
+ */
44
+ export function isClosingMarkdownFence(line: string, open_fence: {
45
+ character: string;
46
+ length: number;
47
+ }): boolean;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @param {string} file_path
3
+ * @param {{ metadata: Record<string, string>, tag_lines: number[] }} pending_tag_set
4
+ * @param {{ metadata: Record<string, string>, tag_lines: number[] }} next_tag_set
5
+ * @returns {{ metadata: Record<string, string>, tag_lines: number[] }}
6
+ */
7
+ export function mergePendingTagSets(file_path: string, pending_tag_set: {
8
+ metadata: Record<string, string>;
9
+ tag_lines: number[];
10
+ }, next_tag_set: {
11
+ metadata: Record<string, string>;
12
+ tag_lines: number[];
13
+ }): {
14
+ metadata: Record<string, string>;
15
+ tag_lines: number[];
16
+ };
17
+ /**
18
+ * @param {string} file_path
19
+ * @param {string} line
20
+ * @param {number} line_number
21
+ * @returns {{ metadata: Record<string, string>, tag_lines: number[] } | null}
22
+ */
23
+ export function parseTaggedMetadataLine(file_path: string, line: string, line_number: number): {
24
+ metadata: Record<string, string>;
25
+ tag_lines: number[];
26
+ } | null;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @typedef {{ metadata: Record<string, string>, tag_lines: number[] }} PendingTagSet
3
+ */
4
+ /**
5
+ * @typedef {{
6
+ * heading_path: string[];
7
+ * lang: string;
8
+ * line_start: number;
9
+ * metadata: Record<string, string>;
10
+ * tag_lines: number[];
11
+ * value_lines: string[];
12
+ * }} OpenTaggedBlock
13
+ */
14
+ /**
15
+ * @typedef {{ character: string, lang: string, length: number }} OpenFence
16
+ */
17
+ /**
18
+ * @typedef {{
19
+ * blocks: TaggedFencedBlock[];
20
+ * body_start: number;
21
+ * heading_path: string[];
22
+ * open_fence: OpenFence | null;
23
+ * open_tagged_block: OpenTaggedBlock | null;
24
+ * pending_tag_set: PendingTagSet | null;
25
+ * title: string;
26
+ * }} TaggedBlockScannerState
27
+ */
28
+ /**
29
+ * @param {TaggedFencedBlocksInput} input
30
+ * @returns {TaggedFencedBlockFile}
31
+ */
32
+ export function extractTaggedFencedBlocksFromSource(input: TaggedFencedBlocksInput): TaggedFencedBlockFile;
33
+ export type PendingTagSet = {
34
+ metadata: Record<string, string>;
35
+ tag_lines: number[];
36
+ };
37
+ export type OpenTaggedBlock = {
38
+ heading_path: string[];
39
+ lang: string;
40
+ line_start: number;
41
+ metadata: Record<string, string>;
42
+ tag_lines: number[];
43
+ value_lines: string[];
44
+ };
45
+ export type OpenFence = {
46
+ character: string;
47
+ lang: string;
48
+ length: number;
49
+ };
50
+ export type TaggedBlockScannerState = {
51
+ blocks: TaggedFencedBlock[];
52
+ body_start: number;
53
+ heading_path: string[];
54
+ open_fence: OpenFence | null;
55
+ open_tagged_block: OpenTaggedBlock | null;
56
+ pending_tag_set: PendingTagSet | null;
57
+ title: string;
58
+ };
59
+ import type { TaggedFencedBlocksInput } from './tagged-fenced-blocks.types.ts';
60
+ import type { TaggedFencedBlockFile } from './tagged-fenced-blocks.types.ts';
61
+ import type { TaggedFencedBlock } from './tagged-fenced-blocks.types.ts';
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Tagged fenced block public API.
3
+ *
4
+ * Loads or extracts one markdown file worth of tagged fenced blocks and
5
+ * provides exact-match selection helpers.
6
+ *
7
+ * Kind: parse
8
+ * Status: active
9
+ * Tracked in: ../docs/plans/v0/tagged-fenced-block-extraction.md
10
+ * Decided by: ../docs/decisions/tagged-fenced-block-extraction.md
11
+ * @patram
12
+ * @see {@link ../docs/decisions/tagged-fenced-block-extraction.md}
13
+ */
14
+ /**
15
+ * @param {TaggedFencedBlocksInput} input
16
+ * @returns {TaggedFencedBlockFile}
17
+ */
18
+ export function extractTaggedFencedBlocks(input: TaggedFencedBlocksInput): TaggedFencedBlockFile;
19
+ /**
20
+ * @param {string} file_path
21
+ * @returns {Promise<TaggedFencedBlockFile>}
22
+ */
23
+ export function loadTaggedFencedBlocks(file_path: string): Promise<TaggedFencedBlockFile>;
24
+ /**
25
+ * @param {TaggedFencedBlock[]} blocks
26
+ * @param {TaggedFencedBlockCriteria} criteria
27
+ * @returns {TaggedFencedBlock[]}
28
+ */
29
+ export function selectTaggedBlocks(blocks: TaggedFencedBlock[], criteria: TaggedFencedBlockCriteria): TaggedFencedBlock[];
30
+ /**
31
+ * @param {TaggedFencedBlock[]} blocks
32
+ * @param {TaggedFencedBlockCriteria} criteria
33
+ * @returns {TaggedFencedBlock}
34
+ */
35
+ export function selectTaggedBlock(blocks: TaggedFencedBlock[], criteria: TaggedFencedBlockCriteria): TaggedFencedBlock;
36
+ import type { TaggedFencedBlocksInput } from './tagged-fenced-blocks.types.ts';
37
+ import type { TaggedFencedBlockFile } from './tagged-fenced-blocks.types.ts';
38
+ import type { TaggedFencedBlock } from './tagged-fenced-blocks.types.ts';
39
+ import type { TaggedFencedBlockCriteria } from './tagged-fenced-blocks.types.ts';
@@ -0,0 +1,32 @@
1
+ export interface TaggedFencedBlocksInput {
2
+ file_path: string;
3
+ source_text: string;
4
+ }
5
+ export interface TaggedFencedBlockCriteria {
6
+ [key: string]: string;
7
+ }
8
+ export interface TaggedFencedBlockOrigin {
9
+ path: string;
10
+ line_start: number;
11
+ line_end: number;
12
+ tag_lines: number[];
13
+ }
14
+ export interface TaggedFencedBlockContext {
15
+ heading_path: string[];
16
+ }
17
+ export interface TaggedFencedBlock {
18
+ id: string;
19
+ lang: string;
20
+ value: string;
21
+ metadata: Record<string, string>;
22
+ origin: TaggedFencedBlockOrigin;
23
+ context: TaggedFencedBlockContext;
24
+ }
25
+ export interface TaggedFencedBlockFile {
26
+ path: string;
27
+ title: string;
28
+ blocks: TaggedFencedBlock[];
29
+ }
30
+ export interface TaggedFencedBlockError extends Error {
31
+ code: string;
32
+ }
package/package.json CHANGED
@@ -1,18 +1,27 @@
1
1
  {
2
2
  "name": "patram",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "main": "./lib/patram.js",
6
+ "types": "./lib/patram.d.ts",
6
7
  "exports": {
7
- ".": "./lib/patram.js",
8
+ ".": {
9
+ "types": "./lib/patram.d.ts",
10
+ "default": "./lib/patram.js"
11
+ },
8
12
  "./bin/patram.js": "./bin/patram.js"
9
13
  },
10
14
  "files": [
11
15
  "bin/patram.js",
16
+ "lib/**/*.d.ts",
12
17
  "lib/**/*.js",
13
18
  "lib/**/*.ts",
19
+ "!bin/**/*.test.d.ts",
20
+ "!bin/**/*.test-helpers.d.ts",
14
21
  "!bin/**/*.test.js",
15
22
  "!bin/**/*.test-helpers.js",
23
+ "!lib/**/*.test.d.ts",
24
+ "!lib/**/*.test-helpers.d.ts",
16
25
  "!lib/**/*.test.js",
17
26
  "!lib/**/*.test-helpers.js"
18
27
  ],
@@ -37,6 +46,8 @@
37
46
  "check:staged": "lint-staged",
38
47
  "check:types": "tsc",
39
48
  "postversion": "git push && git push --tags",
49
+ "postpack": "node scripts/clean-package-api-declarations.js",
50
+ "prepack": "node scripts/build-package-api-declarations.js",
40
51
  "preversion": "npm run all",
41
52
  "prepare": "husky",
42
53
  "test": "vitest run",