@4kk11/cooklang-sankey 0.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.
@@ -0,0 +1,408 @@
1
+ import { CooklangRecipe, Value, Quantity, Step } from '@cooklang/cooklang';
2
+ export { Content, CooklangParser, CooklangRecipe, Ingredient, Item, Quantity, Section, Step, Value, getNumericValue, quantity_display } from '@cooklang/cooklang';
3
+
4
+ /**
5
+ * Cooklang Parser wrapper using official @cooklang/cooklang package.
6
+ *
7
+ * @remarks
8
+ * This module provides a thin wrapper around the official Cooklang parser,
9
+ * re-exporting types and adding metadata extraction utilities.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+
14
+ /**
15
+ * Metadata extracted from a parsed Cooklang recipe.
16
+ *
17
+ * @remarks
18
+ * Contains standard recipe metadata fields plus any custom metadata
19
+ * defined in the recipe using the `>> key: value` syntax.
20
+ */
21
+ interface RecipeMetadata {
22
+ /** Recipe title from `>> title:` */
23
+ title?: string;
24
+ /** Recipe description from `>> description:` */
25
+ description?: string;
26
+ /** Array of tags from `>> tags:` */
27
+ tags?: string[];
28
+ /** Formatted cooking time string (e.g., "prep: 10min, cook: 30min") */
29
+ cookingTime?: string;
30
+ /** Servings range or value (e.g., "4" or "4-6") */
31
+ servings?: string;
32
+ /** Additional custom metadata fields */
33
+ [key: string]: unknown;
34
+ }
35
+ /**
36
+ * Extracts metadata from a parsed Cooklang recipe.
37
+ *
38
+ * @param recipe - The parsed CooklangRecipe object
39
+ * @returns An object containing extracted metadata fields
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { CooklangParser } from "@cooklang/cooklang";
44
+ * import { extractMetadata } from "./parser";
45
+ *
46
+ * const parser = new CooklangParser();
47
+ * const [recipe] = parser.parse(`
48
+ * >> title: Pasta Carbonara
49
+ * >> servings: 4
50
+ * @pasta{400g} を茹でる
51
+ * `);
52
+ *
53
+ * const metadata = extractMetadata(recipe);
54
+ * // { title: "Pasta Carbonara", servings: "4" }
55
+ * ```
56
+ */
57
+ declare function extractMetadata(recipe: CooklangRecipe): RecipeMetadata;
58
+
59
+ /**
60
+ * Sankey diagram type definitions (framework-agnostic)
61
+ *
62
+ * @remarks
63
+ * These types are designed to be independent of any specific visualization library,
64
+ * allowing integration with D3.js, ECharts, or other charting libraries.
65
+ *
66
+ * @packageDocumentation
67
+ */
68
+ /**
69
+ * Category of a node in the Sankey diagram.
70
+ *
71
+ * - `ingredient` - Raw ingredients from the recipe
72
+ * - `process` - Cooking steps/processes
73
+ * - `final` - The completed dish
74
+ */
75
+ type NodeCategory = "ingredient" | "process" | "final";
76
+ /**
77
+ * Type of transformation occurring between nodes.
78
+ *
79
+ * - `cooking` - Heat-based transformation (frying, boiling, etc.)
80
+ * - `preparation` - Mechanical transformation (chopping, mixing, etc.)
81
+ * - `timing` - Time-based step (marinating, resting, etc.)
82
+ * - `combination` - Combining multiple ingredients
83
+ * - `completion` - Final step to complete the dish
84
+ */
85
+ type TransformationType = "cooking" | "preparation" | "timing" | "combination" | "completion";
86
+ /**
87
+ * Value extracted from an ingredient quantity.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * // Numeric value with unit
92
+ * const numericValue: ExtractedValue = {
93
+ * type: "number",
94
+ * value: 200,
95
+ * label: "200g"
96
+ * };
97
+ *
98
+ * // Text-only value (e.g., "a pinch")
99
+ * const textValue: ExtractedValue = {
100
+ * type: "text",
101
+ * label: "a pinch"
102
+ * };
103
+ * ```
104
+ */
105
+ type ExtractedValue = {
106
+ /** Indicates a numeric value was extracted */
107
+ type: "number";
108
+ /** The numeric portion of the quantity */
109
+ value: number;
110
+ /** Human-readable display label (e.g., "200g") */
111
+ label: string;
112
+ } | {
113
+ /** Indicates only text was available */
114
+ type: "text";
115
+ /** Human-readable display label */
116
+ label: string;
117
+ };
118
+ /**
119
+ * Metadata associated with a node.
120
+ */
121
+ interface BaseNodeMetadata {
122
+ /** Step number in the recipe (1-indexed) */
123
+ stepNumber?: number;
124
+ /** Name of the recipe section containing this node */
125
+ sectionName?: string;
126
+ /** Duration string for timer nodes (e.g., "5 minutes") */
127
+ timerDuration?: string;
128
+ /** Original index in the ingredient/step array */
129
+ originalIndex?: number;
130
+ }
131
+ /**
132
+ * Metadata associated with a link.
133
+ */
134
+ interface BaseLinkMetadata {
135
+ /** Step number where this link originates */
136
+ stepNumber?: number;
137
+ /** Type of transformation this link represents */
138
+ transformationType?: TransformationType;
139
+ }
140
+ /**
141
+ * Base structure for a Sankey diagram node.
142
+ */
143
+ interface BaseSankeyNode {
144
+ /** Unique identifier for the node */
145
+ id: string;
146
+ /** Display name of the node */
147
+ name: string;
148
+ /** Category of the node */
149
+ category: NodeCategory;
150
+ /** Normalized value for visualization sizing */
151
+ value: number;
152
+ /** Short label for display (e.g., step number, quantity) */
153
+ label: string;
154
+ /** Original value before normalization */
155
+ originalValue?: number;
156
+ }
157
+ /**
158
+ * Base structure for a Sankey diagram link.
159
+ */
160
+ interface BaseSankeyLink {
161
+ /** ID of the source node */
162
+ source: string;
163
+ /** ID of the target node */
164
+ target: string;
165
+ /** Flow value determining link width */
166
+ value: number;
167
+ /** Original value before normalization */
168
+ originalValue?: number;
169
+ }
170
+ /**
171
+ * Sankey node with optional metadata.
172
+ *
173
+ * @see {@link BaseSankeyNode} for base properties
174
+ * @see {@link BaseNodeMetadata} for metadata properties
175
+ */
176
+ interface SankeyNode extends BaseSankeyNode {
177
+ /** Additional node metadata */
178
+ metadata?: BaseNodeMetadata;
179
+ }
180
+ /**
181
+ * Sankey link with optional metadata.
182
+ *
183
+ * @see {@link BaseSankeyLink} for base properties
184
+ * @see {@link BaseLinkMetadata} for metadata properties
185
+ */
186
+ interface SankeyLink extends BaseSankeyLink {
187
+ /** Additional link metadata */
188
+ metadata?: BaseLinkMetadata;
189
+ }
190
+ /**
191
+ * Complete Sankey diagram data structure.
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * const data: SankeyData = {
196
+ * nodes: [
197
+ * { id: "0", name: "flour", category: "ingredient", value: 1, label: "200g" },
198
+ * { id: "step_main_1", name: "Mix ingredients", category: "process", value: 1, label: "1" },
199
+ * { id: "final_dish", name: "Bread", category: "final", value: 1, label: "" }
200
+ * ],
201
+ * links: [
202
+ * { source: "0", target: "step_main_1", value: 1 },
203
+ * { source: "step_main_1", target: "final_dish", value: 1 }
204
+ * ]
205
+ * };
206
+ * ```
207
+ */
208
+ interface SankeyData {
209
+ /** Array of nodes in the diagram */
210
+ nodes: SankeyNode[];
211
+ /** Array of links connecting nodes */
212
+ links: SankeyLink[];
213
+ }
214
+ /**
215
+ * DAG (Directed Acyclic Graph) node with dependency tracking.
216
+ *
217
+ * @remarks
218
+ * Extends SankeyNode with input/output arrays for topological sorting
219
+ * and value propagation calculations.
220
+ *
221
+ * @see {@link SankeyNode} for base properties
222
+ */
223
+ interface DAGNode extends SankeyNode {
224
+ /** IDs of nodes that flow into this node */
225
+ inputs: string[];
226
+ /** IDs of nodes that this node flows to */
227
+ outputs: string[];
228
+ }
229
+ /**
230
+ * Edge in the DAG representing a flow between nodes.
231
+ */
232
+ interface DAGEdge {
233
+ /** ID of the source node */
234
+ from: string;
235
+ /** ID of the target node */
236
+ to: string;
237
+ /** Additional edge metadata */
238
+ metadata?: BaseLinkMetadata;
239
+ }
240
+
241
+ /**
242
+ * Sankey diagram data generator from Cooklang recipes.
243
+ *
244
+ * @remarks
245
+ * Main module for transforming parsed Cooklang recipes into
246
+ * Sankey diagram data structures. Orchestrates the DAG building,
247
+ * value calculation, and normalization pipeline.
248
+ *
249
+ * @packageDocumentation
250
+ */
251
+
252
+ /**
253
+ * Configuration options for Sankey diagram generation.
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * const options: SankeyGeneratorOptions = {
258
+ * finalNodeName: "Carbonara",
259
+ * normalization: "logarithmic",
260
+ * minLinkValue: 0.1
261
+ * };
262
+ * ```
263
+ */
264
+ interface SankeyGeneratorOptions {
265
+ /**
266
+ * Display name for the final dish node.
267
+ * @defaultValue "完成品"
268
+ */
269
+ finalNodeName?: string;
270
+ /**
271
+ * Method for normalizing ingredient values.
272
+ * - `logarithmic`: Compress large ranges (recommended)
273
+ * - `linear`: Linear scaling
274
+ * - `none`: Use raw values
275
+ * @defaultValue "logarithmic"
276
+ */
277
+ normalization?: "logarithmic" | "linear" | "none";
278
+ /**
279
+ * Minimum value for links to ensure visibility.
280
+ * @defaultValue 0.1
281
+ */
282
+ minLinkValue?: number;
283
+ }
284
+ /**
285
+ * Generates Sankey diagram data from a parsed Cooklang recipe.
286
+ *
287
+ * @remarks
288
+ * This is the main entry point for the library. It transforms a parsed
289
+ * Cooklang recipe into a Sankey diagram data structure by:
290
+ * 1. Creating nodes for ingredients, steps, and the final dish
291
+ * 2. Building edges representing ingredient flow
292
+ * 3. Performing topological sort for correct value propagation
293
+ * 4. Normalizing values for balanced visualization
294
+ *
295
+ * @param recipe - A parsed CooklangRecipe object
296
+ * @param options - Optional configuration for generation
297
+ * @returns SankeyData structure, or null if generation fails
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * import { CooklangParser, generateSankeyData } from 'cooklang-sankey';
302
+ *
303
+ * const parser = new CooklangParser();
304
+ * const [recipe] = parser.parse(`
305
+ * @pasta{400g} を茹でる。
306
+ * @卵{3個}と@チーズ{100g}を混ぜる。
307
+ * `);
308
+ *
309
+ * const data = generateSankeyData(recipe, {
310
+ * finalNodeName: "Carbonara",
311
+ * normalization: "logarithmic"
312
+ * });
313
+ * ```
314
+ */
315
+ declare const generateSankeyData: (recipe: CooklangRecipe, options?: SankeyGeneratorOptions) => SankeyData | null;
316
+ /**
317
+ * Validates and optimizes Sankey data for visualization.
318
+ *
319
+ * @remarks
320
+ * Performs three optimization steps:
321
+ * 1. **Remove invalid links**: Filters out links referencing non-existent nodes
322
+ * 2. **Remove orphaned nodes**: Keeps only nodes connected to valid links
323
+ * (final nodes are always preserved)
324
+ * 3. **Normalize link values**: Scales values relative to minimum for
325
+ * stable visualization (ensures all values >= 1)
326
+ *
327
+ * @param data - The SankeyData to optimize
328
+ * @returns A new SankeyData with optimizations applied
329
+ *
330
+ * @example
331
+ * ```ts
332
+ * const rawData = generateSankeyData(recipe);
333
+ * if (rawData) {
334
+ * const optimized = optimizeSankeyData(rawData);
335
+ * // Use optimized data for visualization
336
+ * }
337
+ * ```
338
+ */
339
+ declare const optimizeSankeyData: (data: SankeyData) => SankeyData;
340
+
341
+ /**
342
+ * Value formatting utilities using official @cooklang/cooklang package.
343
+ *
344
+ * @remarks
345
+ * Provides utilities for converting Cooklang quantity and value types
346
+ * into human-readable string representations.
347
+ *
348
+ * @packageDocumentation
349
+ */
350
+
351
+ /**
352
+ * Formats a Cooklang Value to a string representation.
353
+ *
354
+ * @param value - The Value object to format (number, range, or text)
355
+ * @returns A string representation of the value, or empty string if null/undefined
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * formatValue({ type: "number", value: 200 }); // "200"
360
+ * formatValue({ type: "range", value: { start: 2, end: 3 } }); // "2-3"
361
+ * formatValue({ type: "text", value: "some" }); // "some"
362
+ * formatValue(null); // ""
363
+ * ```
364
+ */
365
+ declare const formatValue: (value: Value | null | undefined) => string;
366
+ /**
367
+ * Formats a Cooklang Quantity into separate value and unit strings.
368
+ *
369
+ * @param quantity - The Quantity object to format
370
+ * @returns An object with `quantity` (numeric string) and `unit` (unit string)
371
+ *
372
+ * @example
373
+ * ```ts
374
+ * formatQuantityAmount({ value: { type: "number", value: 200 }, unit: "g" });
375
+ * // { quantity: "200", unit: "g" }
376
+ *
377
+ * formatQuantityAmount(null);
378
+ * // { quantity: "", unit: "" }
379
+ * ```
380
+ */
381
+ declare const formatQuantityAmount: (quantity: Quantity | null | undefined) => {
382
+ quantity: string;
383
+ unit: string;
384
+ };
385
+ /**
386
+ * Generates complete text from step items by resolving references.
387
+ *
388
+ * @remarks
389
+ * Converts a Step's items array into a readable string by:
390
+ * - Keeping text items as-is
391
+ * - Resolving ingredient references to "name(quantity)" format
392
+ * - Resolving cookware references to their names
393
+ * - Resolving timer references to their display values
394
+ *
395
+ * @param step - The Step object containing items to format
396
+ * @param recipe - The parent CooklangRecipe for resolving references
397
+ * @returns A concatenated string of all step items
398
+ *
399
+ * @example
400
+ * ```ts
401
+ * // For a step with text "Cook " + ingredient(pasta, 400g) + " until done"
402
+ * generateStepText(step, recipe);
403
+ * // "Cook pasta(400g) until done"
404
+ * ```
405
+ */
406
+ declare const generateStepText: (step: Step, recipe: CooklangRecipe) => string;
407
+
408
+ export { type BaseLinkMetadata, type BaseNodeMetadata, type BaseSankeyLink, type BaseSankeyNode, type DAGEdge, type DAGNode, type ExtractedValue, type NodeCategory, type RecipeMetadata, type SankeyData, type SankeyGeneratorOptions, type SankeyLink, type SankeyNode, type TransformationType, extractMetadata, formatQuantityAmount, formatValue, generateSankeyData, generateStepText, optimizeSankeyData };