@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.
- package/README.md +161 -0
- package/dist/index.cjs +492 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +408 -0
- package/dist/index.d.ts +408 -0
- package/dist/index.js +466 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|