patram 0.1.1 → 0.2.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 (35) hide show
  1. package/lib/build-graph-identity.js +39 -7
  2. package/lib/build-graph.js +14 -1
  3. package/lib/cli-help-metadata.js +552 -0
  4. package/lib/derived-summary.js +278 -0
  5. package/lib/format-derived-summary-row.js +9 -0
  6. package/lib/format-node-header.js +19 -0
  7. package/lib/format-output-item-block.js +22 -0
  8. package/lib/format-output-metadata.js +62 -0
  9. package/lib/layout-stored-queries.js +150 -2
  10. package/lib/load-patram-config.js +401 -2
  11. package/lib/load-patram-config.types.ts +31 -0
  12. package/lib/output-view.types.ts +15 -0
  13. package/lib/parse-cli-arguments-helpers.js +263 -90
  14. package/lib/parse-cli-arguments.js +160 -8
  15. package/lib/parse-cli-arguments.types.ts +48 -3
  16. package/lib/parse-where-clause.js +604 -209
  17. package/lib/parse-where-clause.types.ts +70 -0
  18. package/lib/patram-cli.js +144 -17
  19. package/lib/patram.js +6 -0
  20. package/lib/query-graph.js +231 -119
  21. package/lib/query-inspection.js +523 -0
  22. package/lib/render-check-output.js +1 -1
  23. package/lib/render-cli-help.js +419 -0
  24. package/lib/render-json-output.js +57 -4
  25. package/lib/render-output-view.js +37 -8
  26. package/lib/render-plain-output.js +31 -86
  27. package/lib/render-rich-output.js +34 -87
  28. package/lib/resolve-where-clause.js +18 -3
  29. package/lib/tagged-fenced-block-error.js +17 -0
  30. package/lib/tagged-fenced-block-markdown.js +111 -0
  31. package/lib/tagged-fenced-block-metadata.js +97 -0
  32. package/lib/tagged-fenced-block-parser.js +292 -0
  33. package/lib/tagged-fenced-blocks.js +100 -0
  34. package/lib/tagged-fenced-blocks.types.ts +38 -0
  35. package/package.json +8 -3
@@ -0,0 +1,419 @@
1
+ /* eslint-disable max-lines */
2
+ /**
3
+ * @import {
4
+ * CliCommandName,
5
+ * CliHelpTopicName,
6
+ * CliParseError,
7
+ * ParsedCliHelpRequest,
8
+ * } from './parse-cli-arguments.types.ts';
9
+ * @import { PatramDiagnostic } from './load-patram-config.types.ts';
10
+ */
11
+
12
+ import {
13
+ getCommandDefinition,
14
+ getHelpTopicDefinition,
15
+ getRootHelpDefinition,
16
+ listCommandNames,
17
+ listHelpTopicNames,
18
+ } from './cli-help-metadata.js';
19
+
20
+ /**
21
+ * @param {ParsedCliHelpRequest} help_request
22
+ * @returns {string}
23
+ */
24
+ export function renderHelpRequest(help_request) {
25
+ if (help_request.target_kind === 'root') {
26
+ return renderRootHelp();
27
+ }
28
+
29
+ if (help_request.target_kind === 'command') {
30
+ return renderCommandHelp(
31
+ /** @type {CliCommandName} */ (help_request.target_name),
32
+ );
33
+ }
34
+
35
+ return renderHelpTopic(
36
+ /** @type {CliHelpTopicName} */ (help_request.target_name),
37
+ );
38
+ }
39
+
40
+ /**
41
+ * @param {CliParseError} parse_error
42
+ * @returns {string}
43
+ */
44
+ export function renderCliParseError(parse_error) {
45
+ if (parse_error.code === 'unknown_command') {
46
+ return renderUnknownCommandError(parse_error.token, parse_error.suggestion);
47
+ }
48
+
49
+ if (parse_error.code === 'unknown_help_target') {
50
+ return renderUnknownHelpTargetError(
51
+ parse_error.token,
52
+ parse_error.suggestion,
53
+ );
54
+ }
55
+
56
+ if (parse_error.code === 'unknown_option') {
57
+ return renderUnknownOptionError(
58
+ parse_error.token,
59
+ parse_error.command_name,
60
+ parse_error.suggestion,
61
+ );
62
+ }
63
+
64
+ if (parse_error.code === 'option_not_valid_for_command') {
65
+ return renderInvalidCommandOptionError(
66
+ parse_error.command_name,
67
+ parse_error.token,
68
+ );
69
+ }
70
+
71
+ if (parse_error.code === 'missing_required_argument') {
72
+ return renderMissingRequiredArgumentError(
73
+ parse_error.command_name,
74
+ parse_error.argument_label,
75
+ );
76
+ }
77
+
78
+ return `${parse_error.message}\n`;
79
+ }
80
+
81
+ /**
82
+ * @param {PatramDiagnostic} diagnostic
83
+ * @returns {string}
84
+ */
85
+ export function renderInvalidWhereDiagnostic(diagnostic) {
86
+ const diagnostic_line = formatDiagnostic(diagnostic);
87
+
88
+ return joinOutputLines([
89
+ 'Invalid where clause:',
90
+ ` ${diagnostic_line}`,
91
+ '',
92
+ 'Next:',
93
+ ' patram help query-language',
94
+ ]);
95
+ }
96
+
97
+ /**
98
+ * @returns {string}
99
+ */
100
+ function renderRootHelp() {
101
+ const root_help = getRootHelpDefinition();
102
+
103
+ return joinOutputLines([
104
+ 'Usage:',
105
+ ...indentLines(root_help.usage_lines),
106
+ '',
107
+ root_help.summary,
108
+ '',
109
+ 'Commands:',
110
+ ...listCommandNames().map((command_name) =>
111
+ formatSummaryLine(
112
+ command_name,
113
+ getCommandDefinition(command_name).root_summary,
114
+ ),
115
+ ),
116
+ '',
117
+ 'Global options:',
118
+ ...indentLines(root_help.global_options),
119
+ '',
120
+ 'Next:',
121
+ ' patram help <command>',
122
+ ]);
123
+ }
124
+
125
+ /**
126
+ * @param {CliCommandName} command_name
127
+ * @returns {string}
128
+ */
129
+ function renderCommandHelp(command_name) {
130
+ const command_definition = getCommandDefinition(command_name);
131
+ /** @type {string[]} */
132
+ const output_lines = [
133
+ 'Usage:',
134
+ ...indentLines(command_definition.usage_lines),
135
+ '',
136
+ command_definition.summary,
137
+ ];
138
+
139
+ if (
140
+ command_definition.syntax_lines &&
141
+ command_definition.syntax_lines.length
142
+ ) {
143
+ output_lines.push(
144
+ '',
145
+ 'Where clause:',
146
+ ...indentLines(command_definition.syntax_lines),
147
+ );
148
+ }
149
+
150
+ output_lines.push(
151
+ '',
152
+ 'Options:',
153
+ ...command_definition.options.map((option) =>
154
+ formatSummaryLine(
155
+ option.label,
156
+ option.description,
157
+ command_definition.option_column_width,
158
+ ),
159
+ ),
160
+ '',
161
+ 'Examples:',
162
+ ...indentLines(command_definition.examples),
163
+ '',
164
+ 'Related:',
165
+ ...indentLines(
166
+ command_definition.related.map(
167
+ (related_name) => `patram ${related_name}`,
168
+ ),
169
+ ),
170
+ );
171
+
172
+ if (command_definition.help_topics.length > 0) {
173
+ output_lines.push(
174
+ '',
175
+ 'Help topics:',
176
+ ...indentLines(
177
+ command_definition.help_topics.map(
178
+ (help_topic_name) => `patram help ${help_topic_name}`,
179
+ ),
180
+ ),
181
+ );
182
+ }
183
+
184
+ return joinOutputLines(output_lines);
185
+ }
186
+
187
+ /**
188
+ * @param {CliHelpTopicName} help_topic_name
189
+ * @returns {string}
190
+ */
191
+ function renderHelpTopic(help_topic_name) {
192
+ const help_topic = getHelpTopicDefinition(help_topic_name);
193
+
194
+ return joinOutputLines([
195
+ help_topic.lead,
196
+ '',
197
+ 'Usage:',
198
+ ...indentLines(help_topic.usage_lines),
199
+ '',
200
+ 'Fields:',
201
+ ...indentLines(help_topic.terms),
202
+ '',
203
+ 'Relations:',
204
+ ...help_topic.relation_terms.map((relation_term) =>
205
+ formatSummaryLine(
206
+ relation_term.label,
207
+ relation_term.description,
208
+ getSummaryColumnWidth(help_topic.relation_terms, 23),
209
+ ),
210
+ ),
211
+ '',
212
+ 'Operators:',
213
+ ...help_topic.operators.map((operator) =>
214
+ formatSummaryLine(
215
+ operator.label,
216
+ operator.description,
217
+ getSummaryColumnWidth(help_topic.operators, 7),
218
+ ),
219
+ ),
220
+ '',
221
+ 'Examples:',
222
+ ...indentLines(help_topic.examples),
223
+ ]);
224
+ }
225
+
226
+ /**
227
+ * @param {string} invalid_token
228
+ * @param {CliCommandName | undefined} suggestion
229
+ * @returns {string}
230
+ */
231
+ function renderUnknownCommandError(invalid_token, suggestion) {
232
+ if (suggestion) {
233
+ return joinOutputLines([
234
+ `Unknown command: ${invalid_token}`,
235
+ '',
236
+ 'Did you mean:',
237
+ ` ${suggestion}`,
238
+ '',
239
+ 'Next:',
240
+ ` patram help ${suggestion}`,
241
+ ]);
242
+ }
243
+
244
+ return joinOutputLines([
245
+ `Unknown command: ${invalid_token}`,
246
+ '',
247
+ 'Commands:',
248
+ ...indentLines(listCommandNames()),
249
+ '',
250
+ 'Next:',
251
+ ' patram --help',
252
+ ]);
253
+ }
254
+
255
+ /**
256
+ * @param {string} invalid_token
257
+ * @param {CliCommandName | undefined} command_name
258
+ * @param {string | undefined} suggestion
259
+ * @returns {string}
260
+ */
261
+ function renderUnknownOptionError(invalid_token, command_name, suggestion) {
262
+ if (suggestion) {
263
+ return joinOutputLines([
264
+ `Unknown option: ${invalid_token}`,
265
+ '',
266
+ 'Did you mean:',
267
+ ` ${suggestion}`,
268
+ '',
269
+ 'Next:',
270
+ ` ${renderCommandHelpPath(command_name)}`,
271
+ ]);
272
+ }
273
+
274
+ /** @type {string[]} */
275
+ const output_lines = [`Unknown option: ${invalid_token}`];
276
+
277
+ if (command_name) {
278
+ output_lines.push(
279
+ '',
280
+ 'Usage:',
281
+ ...indentLines(getCommandDefinition(command_name).usage_lines),
282
+ '',
283
+ 'Next:',
284
+ ` ${renderCommandHelpPath(command_name)}`,
285
+ );
286
+ } else {
287
+ output_lines.push('', 'Next:', ' patram --help');
288
+ }
289
+
290
+ return joinOutputLines(output_lines);
291
+ }
292
+
293
+ /**
294
+ * @param {CliCommandName} command_name
295
+ * @param {string} invalid_token
296
+ * @returns {string}
297
+ */
298
+ function renderInvalidCommandOptionError(command_name, invalid_token) {
299
+ return joinOutputLines([
300
+ `Option not valid for command: ${invalid_token}`,
301
+ '',
302
+ 'Usage:',
303
+ ...indentLines(getCommandDefinition(command_name).usage_lines),
304
+ '',
305
+ 'Next:',
306
+ ` ${renderCommandHelpPath(command_name)}`,
307
+ ]);
308
+ }
309
+
310
+ /**
311
+ * @param {'query' | 'show'} command_name
312
+ * @param {string} argument_label
313
+ * @returns {string}
314
+ */
315
+ function renderMissingRequiredArgumentError(command_name, argument_label) {
316
+ const command_definition = getCommandDefinition(command_name);
317
+
318
+ return joinOutputLines([
319
+ `Missing required argument: ${argument_label}`,
320
+ '',
321
+ 'Usage:',
322
+ ...indentLines(command_definition.missing_usage_lines),
323
+ '',
324
+ 'Examples:',
325
+ ...indentLines(command_definition.missing_argument_examples),
326
+ ]);
327
+ }
328
+
329
+ /**
330
+ * @param {string} invalid_token
331
+ * @param {CliCommandName | CliHelpTopicName | undefined} suggestion
332
+ * @returns {string}
333
+ */
334
+ function renderUnknownHelpTargetError(invalid_token, suggestion) {
335
+ if (suggestion) {
336
+ return joinOutputLines([
337
+ `Unknown help topic or command: ${invalid_token}`,
338
+ '',
339
+ 'Did you mean:',
340
+ ` ${suggestion}`,
341
+ '',
342
+ 'Next:',
343
+ ` patram help ${suggestion}`,
344
+ ]);
345
+ }
346
+
347
+ return joinOutputLines([
348
+ `Unknown help topic or command: ${invalid_token}`,
349
+ '',
350
+ 'Help topics:',
351
+ ...indentLines(listHelpTopicNames()),
352
+ '',
353
+ 'Commands:',
354
+ ...indentLines(listCommandNames()),
355
+ '',
356
+ 'Next:',
357
+ ' patram help query',
358
+ ]);
359
+ }
360
+
361
+ /**
362
+ * @param {PatramDiagnostic} diagnostic
363
+ * @returns {string}
364
+ */
365
+ function formatDiagnostic(diagnostic) {
366
+ return `${diagnostic.path}:${diagnostic.line}:${diagnostic.column} ${diagnostic.level} ${diagnostic.code} ${diagnostic.message}`;
367
+ }
368
+
369
+ /**
370
+ * @param {string | undefined} command_name
371
+ * @returns {string}
372
+ */
373
+ function renderCommandHelpPath(command_name) {
374
+ if (!command_name) {
375
+ return 'patram --help';
376
+ }
377
+
378
+ return `patram help ${command_name}`;
379
+ }
380
+
381
+ /**
382
+ * @param {string[]} lines
383
+ * @returns {string[]}
384
+ */
385
+ function indentLines(lines) {
386
+ return lines.map((line) => ` ${line}`);
387
+ }
388
+
389
+ /**
390
+ * @param {string} label
391
+ * @param {string} description
392
+ * @param {number} width
393
+ * @returns {string}
394
+ */
395
+ function formatSummaryLine(label, description, width = 9) {
396
+ return ` ${label.padEnd(width)}${description}`;
397
+ }
398
+
399
+ /**
400
+ * @param {{ label: string }[]} items
401
+ * @param {number} minimum_width
402
+ * @returns {number}
403
+ */
404
+ function getSummaryColumnWidth(items, minimum_width) {
405
+ const longest_label = items.reduce(
406
+ (current_width, item) => Math.max(current_width, item.label.length),
407
+ 0,
408
+ );
409
+
410
+ return Math.max(minimum_width, longest_label + 2);
411
+ }
412
+
413
+ /**
414
+ * @param {string[]} output_lines
415
+ * @returns {string}
416
+ */
417
+ function joinOutputLines(output_lines) {
418
+ return `${output_lines.join('\n')}\n`;
419
+ }
@@ -39,6 +39,9 @@ export function renderJsonOutput(output_view) {
39
39
  if (output_view.command === 'show') {
40
40
  return `${JSON.stringify(
41
41
  {
42
+ document: output_view.document
43
+ ? formatJsonShowDocument(output_view.document)
44
+ : undefined,
42
45
  source: output_view.source,
43
46
  resolved_links: output_view.items.map(formatJsonResolvedLink),
44
47
  },
@@ -52,10 +55,10 @@ export function renderJsonOutput(output_view) {
52
55
 
53
56
  /**
54
57
  * @param {OutputNodeItem} output_item
55
- * @returns {{ id: string, kind: string, title: string, path: string, status?: string }}
58
+ * @returns {{ derived?: Record<string, boolean | number | string | null>, derived_summary?: string, id: string, kind: string, title: string, path: string, status?: string }}
56
59
  */
57
60
  function formatJsonQueryItem(output_item) {
58
- /** @type {{ id: string, kind: string, title: string, path: string, status?: string }} */
61
+ /** @type {{ derived?: Record<string, boolean | number | string | null>, derived_summary?: string, id: string, kind: string, title: string, path: string, status?: string }} */
59
62
  const query_item = {
60
63
  id: output_item.id,
61
64
  kind: output_item.node_kind,
@@ -67,6 +70,16 @@ function formatJsonQueryItem(output_item) {
67
70
  query_item.status = output_item.status;
68
71
  }
69
72
 
73
+ if (output_item.derived_summary) {
74
+ query_item.derived_summary = output_item.derived_summary.name;
75
+ query_item.derived = Object.fromEntries(
76
+ output_item.derived_summary.fields.map((field) => [
77
+ field.name,
78
+ field.value,
79
+ ]),
80
+ );
81
+ }
82
+
70
83
  return query_item;
71
84
  }
72
85
 
@@ -83,10 +96,10 @@ function formatJsonStoredQuery(output_item) {
83
96
 
84
97
  /**
85
98
  * @param {OutputResolvedLinkItem} output_item
86
- * @returns {{ label: string, reference: number, target: { kind?: string, path: string, status?: string, title: string } }}
99
+ * @returns {{ label: string, reference: number, target: { derived?: Record<string, boolean | number | string | null>, derived_summary?: string, kind?: string, path: string, status?: string, title: string } }}
87
100
  */
88
101
  function formatJsonResolvedLink(output_item) {
89
- /** @type {{ label: string, reference: number, target: { kind?: string, path: string, status?: string, title: string } }} */
102
+ /** @type {{ label: string, reference: number, target: { derived?: Record<string, boolean | number | string | null>, derived_summary?: string, kind?: string, path: string, status?: string, title: string } }} */
90
103
  const resolved_link = {
91
104
  reference: output_item.reference,
92
105
  label: output_item.label,
@@ -104,5 +117,45 @@ function formatJsonResolvedLink(output_item) {
104
117
  resolved_link.target.status = output_item.target.status;
105
118
  }
106
119
 
120
+ if (output_item.target.derived_summary) {
121
+ resolved_link.target.derived_summary =
122
+ output_item.target.derived_summary.name;
123
+ resolved_link.target.derived = Object.fromEntries(
124
+ output_item.target.derived_summary.fields.map((field) => [
125
+ field.name,
126
+ field.value,
127
+ ]),
128
+ );
129
+ }
130
+
107
131
  return resolved_link;
108
132
  }
133
+
134
+ /**
135
+ * @param {OutputNodeItem} output_item
136
+ * @returns {{ derived?: Record<string, boolean | number | string | null>, derived_summary?: string, kind: string, title: string, path: string, status?: string }}
137
+ */
138
+ function formatJsonShowDocument(output_item) {
139
+ /** @type {{ derived?: Record<string, boolean | number | string | null>, derived_summary?: string, kind: string, title: string, path: string, status?: string }} */
140
+ const document_summary = {
141
+ kind: output_item.node_kind,
142
+ path: output_item.path,
143
+ title: output_item.title,
144
+ };
145
+
146
+ if (output_item.status) {
147
+ document_summary.status = output_item.status;
148
+ }
149
+
150
+ if (output_item.derived_summary) {
151
+ document_summary.derived_summary = output_item.derived_summary.name;
152
+ document_summary.derived = Object.fromEntries(
153
+ output_item.derived_summary.fields.map((field) => [
154
+ field.name,
155
+ field.value,
156
+ ]),
157
+ );
158
+ }
159
+
160
+ return document_summary;
161
+ }
@@ -1,6 +1,7 @@
1
1
  /** @import * as $k$$l$output$j$view$k$types$k$ts from './output-view.types.ts'; */
2
2
  /**
3
- * @import { GraphNode } from './build-graph.types.ts';
3
+ * @import { BuildGraphResult, GraphNode } from './build-graph.types.ts';
4
+ * @import { DerivedSummaryEvaluator } from './derived-summary.js';
4
5
  * @import { ParsedCliArguments } from './parse-cli-arguments.types.ts';
5
6
  * @import { OutputStoredQueryItem, OutputView, ResolvedOutputMode, ShowOutputView } from './output-view.types.ts';
6
7
  */
@@ -29,7 +30,7 @@ import { renderRichOutput } from './render-rich-output.js';
29
30
  *
30
31
  * @param {'query' | 'queries'} command_name
31
32
  * @param {GraphNode[] | { name: string, where: string }[]} command_items
32
- * @param {{ hints?: string[], limit?: number, offset?: number, total_count?: number }=} command_options
33
+ * @param {{ derived_summary_evaluator?: DerivedSummaryEvaluator, hints?: string[], limit?: number, offset?: number, total_count?: number }=} command_options
33
34
  * @returns {OutputView}
34
35
  */
35
36
  export function createOutputView(command_name, command_items, command_options) {
@@ -53,17 +54,36 @@ export function createOutputView(command_name, command_items, command_options) {
53
54
  * Create a shared output view for the show command.
54
55
  *
55
56
  * @param {{ path: string, rendered_source: string, resolved_links: Array<{ label: string, reference: number, target: { kind?: string, path: string, status?: string, title: string } }>, source: string }} show_output
57
+ * @param {{ derived_summary_evaluator?: DerivedSummaryEvaluator, graph_nodes?: BuildGraphResult['nodes'] }=} command_options
56
58
  * @returns {ShowOutputView}
57
59
  */
58
- export function createShowOutputView(show_output) {
60
+ export function createShowOutputView(show_output, command_options = {}) {
61
+ const shown_document_node =
62
+ command_options.graph_nodes?.[`doc:${show_output.path}`];
63
+
59
64
  return {
60
65
  command: 'show',
66
+ document: shown_document_node
67
+ ? createOutputNodeItem(
68
+ shown_document_node,
69
+ command_options.derived_summary_evaluator?.evaluate(
70
+ shown_document_node,
71
+ ) ?? null,
72
+ )
73
+ : undefined,
61
74
  hints: [],
62
75
  items: show_output.resolved_links.map((resolved_link) => ({
63
76
  kind: 'resolved_link',
64
77
  label: resolved_link.label,
65
78
  reference: resolved_link.reference,
66
- target: createResolvedLinkTarget(resolved_link.target),
79
+ target: createResolvedLinkTarget(
80
+ resolved_link.target,
81
+ command_options.graph_nodes?.[`doc:${resolved_link.target.path}`]
82
+ ? (command_options.derived_summary_evaluator?.evaluate(
83
+ command_options.graph_nodes[`doc:${resolved_link.target.path}`],
84
+ ) ?? null)
85
+ : null,
86
+ ),
67
87
  })),
68
88
  path: show_output.path,
69
89
  rendered_source: show_output.rendered_source,
@@ -104,7 +124,7 @@ export async function renderOutputView(
104
124
 
105
125
  /**
106
126
  * @param {GraphNode[]} graph_nodes
107
- * @param {{ hints?: string[], limit?: number, offset?: number, total_count?: number }=} command_options
127
+ * @param {{ derived_summary_evaluator?: DerivedSummaryEvaluator, hints?: string[], limit?: number, offset?: number, total_count?: number }=} command_options
108
128
  * @returns {OutputView}
109
129
  */
110
130
  function createQueryOutputView(graph_nodes, command_options = {}) {
@@ -115,7 +135,12 @@ function createQueryOutputView(graph_nodes, command_options = {}) {
115
135
  hints:
116
136
  command_options.hints ??
117
137
  (total_count === 0 ? ['Try: patram query --where "kind=task"'] : []),
118
- items: graph_nodes.map(createOutputNodeItem),
138
+ items: graph_nodes.map((graph_node) =>
139
+ createOutputNodeItem(
140
+ graph_node,
141
+ command_options.derived_summary_evaluator?.evaluate(graph_node) ?? null,
142
+ ),
143
+ ),
119
144
  summary: {
120
145
  count: graph_nodes.length,
121
146
  kind: 'result_list',
@@ -148,9 +173,10 @@ function createStoredQueriesOutputView(stored_queries) {
148
173
 
149
174
  /**
150
175
  * @param {GraphNode} graph_node
176
+ * @param {import('./output-view.types.ts').OutputDerivedSummary | null} derived_summary
151
177
  * @returns {$k$$l$output$j$view$k$types$k$ts.OutputNodeItem}
152
178
  */
153
- function createOutputNodeItem(graph_node) {
179
+ function createOutputNodeItem(graph_node, derived_summary) {
154
180
  const title =
155
181
  graph_node.title ?? graph_node.label ?? graph_node.path ?? graph_node.key;
156
182
 
@@ -161,6 +187,7 @@ function createOutputNodeItem(graph_node) {
161
187
  }
162
188
 
163
189
  return {
190
+ derived_summary: derived_summary ?? undefined,
164
191
  id: graph_node.id,
165
192
  kind: 'node',
166
193
  node_kind: graph_node.kind,
@@ -172,11 +199,13 @@ function createOutputNodeItem(graph_node) {
172
199
 
173
200
  /**
174
201
  * @param {{ kind?: string, path: string, status?: string, title: string }} target
202
+ * @param {import('./output-view.types.ts').OutputDerivedSummary | null} derived_summary
175
203
  * @returns {$k$$l$output$j$view$k$types$k$ts.OutputResolvedLinkTarget}
176
204
  */
177
- function createResolvedLinkTarget(target) {
205
+ function createResolvedLinkTarget(target, derived_summary) {
178
206
  /** @type {$k$$l$output$j$view$k$types$k$ts.OutputResolvedLinkTarget} */
179
207
  const resolved_target = {
208
+ derived_summary: derived_summary ?? undefined,
180
209
  path: target.path,
181
210
  title: target.title,
182
211
  };