@rohal12/twee-ts 1.17.1 → 1.18.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.cts CHANGED
@@ -10,13 +10,17 @@ type InlineSource = {
10
10
  };
11
11
  type SourceInput = string | InlineSource;
12
12
  interface CompileOptions {
13
- /** Files, directories, or inline sources to compile. */
14
- sources: SourceInput[];
13
+ /**
14
+ * Files, directories, or inline sources to compile. Directories are walked
15
+ * recursively; inside one, a symbolic link to a file is read, but a link to a
16
+ * directory is not followed (as in Tweego).
17
+ */
18
+ sources: readonly SourceInput[];
15
19
  /**
16
20
  * Glob patterns for files to leave out of `sources`, matched against each file's
17
21
  * path relative to the working directory (as `stats.files` lists it). Modules are not affected.
18
22
  */
19
- exclude?: string[];
23
+ exclude?: readonly string[];
20
24
  /** Output mode. Default: 'html'. */
21
25
  outputMode?: OutputMode;
22
26
  /** Story format directory ID (e.g. 'sugarcube-2'). */
@@ -24,11 +28,11 @@ interface CompileOptions {
24
28
  /** Name of the starting passage. Default: 'Start'. */
25
29
  startPassage?: string;
26
30
  /** Extra directories to search for story formats. */
27
- formatPaths?: string[];
31
+ formatPaths?: readonly string[];
28
32
  /** Also search TWEEGO_PATH env for formats. Default: true. */
29
33
  useTweegoPath?: boolean;
30
34
  /** Module files to inject into <head>. */
31
- modules?: string[];
35
+ modules?: readonly string[];
32
36
  /** Raw HTML file to append to <head>. */
33
37
  headFile?: string;
34
38
  /** Trim passage whitespace. Default: true. */
@@ -38,11 +42,24 @@ interface CompileOptions {
38
42
  /** Enable debug/test mode option. Default: false. */
39
43
  testMode?: boolean;
40
44
  /** URLs to SFA-compatible index.json files for remote format lookup. */
41
- formatIndices?: string[];
42
- /** Direct URLs to format.js files. */
43
- formatUrls?: string[];
45
+ formatIndices?: readonly string[];
46
+ /**
47
+ * Direct URLs to format.js files. Each URL's download is cached under that URL, and is
48
+ * looked up before the downloads shared by name and version.
49
+ */
50
+ formatUrls?: readonly string[];
44
51
  /** Disable remote format fetching. Default: false. */
45
52
  noRemote?: boolean;
53
+ /**
54
+ * Cancels the compile. Story format requests still in progress are aborted, and the compile
55
+ * rejects with the signal's reason. `watch()` aborts its builds, and stops watching, when it aborts.
56
+ */
57
+ signal?: AbortSignal;
58
+ /**
59
+ * Milliseconds each story format request (an index or a format.js) may take before it fails
60
+ * with a warning and the next source is tried. 0 turns the limit off. Default: 30000.
61
+ */
62
+ formatFetchTimeout?: number;
46
63
  /** Map alias tags to canonical special tags (e.g. { library: 'script' }). */
47
64
  tagAliases?: Record<string, string>;
48
65
  /** Emit source file and line as data- attributes on passage elements. Default: false. */
@@ -51,16 +68,25 @@ interface CompileOptions {
51
68
  wordCountMethod?: WordCountMethod;
52
69
  }
53
70
  interface CompileToFileOptions extends CompileOptions {
54
- /** Output file path. */
71
+ /**
72
+ * Output file path. It is never read as a source, even inside a source folder or
73
+ * reached through a symbolic link. Naming it as a source, a module or the head
74
+ * file is a TweeTsError, and nothing is written.
75
+ */
55
76
  outFile: string;
56
77
  }
57
78
  interface WatchOptions extends CompileToFileOptions {
58
79
  /**
59
- * Called after each build is written, including one whose diagnostics report errors. A build
60
- * that changes made while it ran have superseded is neither written nor reported.
80
+ * Called after each build is written, including one whose diagnostics report errors. Builds
81
+ * run one at a time and are reported in order, each as it finishes, also while changes keep
82
+ * arriving: the changes made during a build go into one follow-up build after it.
61
83
  */
62
84
  onBuild?: (result: CompileResult) => void;
63
- /** Called when a build fails with a fatal error (nothing is written). */
85
+ /**
86
+ * Called when a build fails with a fatal error (nothing is written), when `onBuild` throws,
87
+ * and when a watched path can't be watched (the other paths are still watched). An exception
88
+ * thrown here is reported to the console, and watching goes on.
89
+ */
64
90
  onError?: (error: Error) => void;
65
91
  }
66
92
  type Diagnostic = {
@@ -227,6 +253,13 @@ interface SFAIndex {
227
253
  twine1: SFAIndexEntry[];
228
254
  twine2: SFAIndexEntry[];
229
255
  }
256
+ /** Options for the network requests that fetch story formats and their indices. */
257
+ interface RemoteFetchOptions {
258
+ /** Aborts the requests; the call then rejects with the signal's reason. */
259
+ readonly signal?: AbortSignal;
260
+ /** Milliseconds each request may take before it fails. 0 turns the limit off. Default: 30000. */
261
+ readonly timeout?: number;
262
+ }
230
263
  interface FileCacheEntry {
231
264
  readonly mtimeMs: number;
232
265
  /**
@@ -255,6 +288,8 @@ interface TweeTsConfig {
255
288
  twee2Compat?: boolean;
256
289
  testMode?: boolean;
257
290
  noRemote?: boolean;
291
+ /** Milliseconds each story format request may take. 0 turns the limit off. Default: 30000. */
292
+ formatFetchTimeout?: number;
258
293
  tagAliases?: Record<string, string>;
259
294
  sourceInfo?: boolean;
260
295
  wordCountMethod?: WordCountMethod;
@@ -270,7 +305,8 @@ export declare class TweeTsError extends Error {
270
305
  */
271
306
  export declare function compile(options: CompileOptions): Promise<CompileResult>;
272
307
  /**
273
- * Compile and write to a file.
308
+ * Compile and write to a file. The file is replaced atomically: a reader sees the previous
309
+ * build or the new one, never part of it, and a failed write leaves the previous build.
274
310
  */
275
311
  export declare function compileToFile(options: CompileToFileOptions): Promise<CompileResult>;
276
312
  /**
@@ -278,16 +314,22 @@ export declare function compileToFile(options: CompileToFileOptions): Promise<Co
278
314
  * Plugins and advanced users can manage their own cache and changed-file tracking.
279
315
  *
280
316
  * Without `changedFiles`, a cached file is reused while its modification time is unchanged.
281
- * With it, a file it names (by the path source discovery gives it, relative to the working
282
- * directory) is always reparsed, whatever its modification time, and every other cached
283
- * file is reused as it is.
317
+ * With it, a file it names is always reparsed, whatever its modification time, and every
318
+ * other cached file is reused as it is. A file may be named by an absolute path or by a path
319
+ * relative to the working directory, with or without a leading `./`: entries are matched to
320
+ * the source files by resolved path. A file that fails to load is dropped from the cache, so
321
+ * the next build tries it again.
284
322
  */
285
323
  export declare function compileIncremental(options: CompileOptions, cache: Map<string, FileCacheEntry>, changedFiles?: ReadonlySet<string>): Promise<CompileResult>;
286
324
  /**
287
- * Watch for file changes and recompile. Every build is written to `outFile`, including
288
- * one whose diagnostics report errors. Builds run one at a time: changes made during a
289
- * build go into one follow-up build after it, and the superseded build is neither
290
- * written nor reported.
325
+ * Watch for file changes and recompile. Every build is written to `outFile` (replacing it
326
+ * atomically, as compileToFile() does), including one whose diagnostics report errors.
327
+ * Builds run one at a time, and each one is written and reported as it finishes, in order:
328
+ * changes made during a build go into one follow-up build after it. A watched path that
329
+ * can't be watched is passed to `onError`.
330
+ *
331
+ * Aborting the returned controller, or the `signal` in the options, stops watching and
332
+ * aborts the story format requests of the build in progress.
291
333
  */
292
334
  export declare function watch(options: WatchOptions): Promise<AbortController>;
293
335
  //#endregion
@@ -301,9 +343,9 @@ interface StoryMap {
301
343
  infoPassages: string[];
302
344
  /** All unique tags used across the story, sorted alphabetically. */
303
345
  tags: string[];
304
- /** Map of tag → passage names that carry that tag. */
346
+ /** Map of tag → passage names that carry that tag, each listed once even if the tag is repeated. */
305
347
  passagesByTag: Map<string, string[]>;
306
- /** Map of passage name → tags on that passage. */
348
+ /** Map of passage name → tags on that passage, as written (a repeated tag appears twice). */
307
349
  tagsByPassage: Map<string, string[]>;
308
350
  /**
309
351
  * Map of passage name → passage names it links to.
@@ -365,7 +407,9 @@ export declare function storyInspect(story: ReadonlyStory, options?: InspectOpti
365
407
  //#region src/story.d.ts
366
408
  /**
367
409
  * Builder that separates the mutable construction phase from the immutable consumption phase.
368
- * During construction, the internal `Story` is mutable via `add()` and direct access.
410
+ * During construction, the internal `Story` is mutable via `add()` and direct access: you may
411
+ * push, splice, sort, rename or reassign `story.passages` between calls, and `add()` and `has()`
412
+ * see the passages as they are then.
369
413
  * After `build()`, the story is returned as `ReadonlyStory`.
370
414
  */
371
415
  export declare class StoryBuilder {
@@ -388,6 +432,8 @@ interface DecompileResult {
388
432
  /**
389
433
  * Parse a Twine 2 or Twine 1 compiled HTML file back into a Story model.
390
434
  * Tweego always trims passage text; here `trim: false` keeps it exactly, as for Twee sources.
435
+ * A missing or invalid `tw-storydata` IFID is reported as a warning; an invalid one is kept as
436
+ * written (uppercased), a missing one stays empty.
391
437
  */
392
438
  export declare function decompileHTML(html: string, options?: DecompileOptions): DecompileResult;
393
439
  //#endregion
@@ -478,33 +524,40 @@ interface ParseResult {
478
524
  * Parse Twee source text into passages.
479
525
  *
480
526
  * The source is normalized first, as files are when they are read: a leading UTF-8 BOM is
481
- * removed and CRLF and bare CR line endings become LF.
527
+ * removed and CRLF and bare CR line endings become LF. A BOM at the start of a later line,
528
+ * directly before `::` (left there by concatenating files), is removed too, so that line
529
+ * stays a passage header.
482
530
  */
483
531
  export declare function parseTwee(source: string, options?: ParseOptions): ParseResult;
484
532
  //#endregion
485
533
  //#region src/formats.d.ts
486
534
  /**
487
- * Parse the Twine 2 format.js JSON chunk.
488
- * Handles Harlowe's malformed JSON by stripping the "setup" function property. The workaround
489
- * keys on the property itself, so the same bytes parse whether they come from a `harlowe-3`
490
- * directory or a direct download.
491
- * Per spec, the name key is Optional.
535
+ * Parse the Twine 2 format.js JSON chunk; null when it cannot be used ({@link decodeFormatJSON}
536
+ * gives the reason).
492
537
  *
493
538
  * @param _formatId No longer used; kept so existing callers keep compiling.
494
539
  */
495
540
  export declare function parseFormatJSON(source: string, _formatId?: string): Twine2FormatJSON | null;
496
541
  /**
497
- * Discover all story formats in the given search directories.
498
- * Implements SemVer-based version pruning: within each (name, major) group,
499
- * only the highest minor.patch survives.
542
+ * Discover all story formats in the given search directories, pruned by SemVer: within each
543
+ * (name, major) group only the highest version survives. See {@link discoverAllFormats} for the
544
+ * directory ranking and {@link pruneFormats} for pruning.
545
+ */
546
+ export declare function discoverFormats(searchDirs: readonly string[]): Map<string, StoryFormatInfo>;
547
+ /**
548
+ * Get the format search directories, lowest rank first: the story format subdirectories of the
549
+ * home directory, then of the working directory, then each `TWEEGO_PATH` entry, then `extraPaths`
550
+ * (`formatPaths`). A later directory outranks an earlier one for the same format folder name, so
551
+ * `formatPaths` override `TWEEGO_PATH`, which overrides the home and working directories, as in
552
+ * Tweego.
500
553
  */
501
- export declare function discoverFormats(searchDirs: string[]): Map<string, StoryFormatInfo>;
554
+ export declare function getFormatSearchDirs(extraPaths?: readonly string[], useTweegoPath?: boolean): string[];
502
555
  /**
503
- * Get the default format search directories.
556
+ * Parse a version string into [major, minor, patch]. Accepts what {@link parseVersion} accepts
557
+ * (`v1.2.3`, `1.2`, `2.0.0-beta.1`); the prerelease and build metadata are dropped.
504
558
  */
505
- export declare function getFormatSearchDirs(extraPaths?: string[], useTweegoPath?: boolean): string[];
506
- /** Parse a simple semver string into [major, minor, patch]. */
507
559
  export declare function parseSemver(v: string): [number, number, number] | null;
560
+ /** Compare [major, minor, patch] tuples. Use {@link compareVersions} to take prereleases into account. */
508
561
  export declare function semverCompare(a: [number, number, number], b: [number, number, number]): number;
509
562
  //#endregion
510
563
  //#region src/ifid.d.ts
@@ -524,31 +577,63 @@ export declare function validateIFID(ifid: string): string | null;
524
577
  //#endregion
525
578
  //#region src/config.d.ts
526
579
  export declare const CONFIG_FILENAME = "twee-ts.config.json";
527
- /** Load a config file from the given directory (default: cwd). Returns null if not found. */
528
- export declare function loadConfig(dir?: string): TweeTsConfig | null;
529
- /** Load a config from a specific file path. */
530
- export declare function loadConfigFile(filePath: string): TweeTsConfig;
531
- /** Validate a config object. Returns an array of error strings (empty = valid). */
580
+ /**
581
+ * Load a config file from the given directory (default: cwd). Returns null if not found.
582
+ *
583
+ * @param diagnostics Receives warnings that do not stop the config from loading: keys the config does not
584
+ * define, and a file that is not valid UTF-8 (it is read as Windows-1252).
585
+ * @throws When the file is not valid JSON or fails {@link validateConfig}.
586
+ */
587
+ export declare function loadConfig(dir?: string, diagnostics?: Diagnostic[]): TweeTsConfig | null;
588
+ /**
589
+ * Load a config from a specific file path.
590
+ *
591
+ * @param diagnostics Receives warnings that do not stop the config from loading: keys the config does not
592
+ * define, and a file that is not valid UTF-8 (it is read as Windows-1252).
593
+ * @throws When the file cannot be read, is not valid JSON or fails {@link validateConfig}.
594
+ */
595
+ export declare function loadConfigFile(filePath: string, diagnostics?: Diagnostic[]): TweeTsConfig;
596
+ /**
597
+ * Validate a config object. Returns an array of error strings (empty = valid).
598
+ * Keys the config does not define are not errors; {@link unknownConfigKeyWarnings} reports them.
599
+ */
532
600
  export declare function validateConfig(data: unknown): string[];
601
+ /**
602
+ * Warnings for the keys of a config object that the config does not define (the JSON schema's
603
+ * `additionalProperties: false`), other than `$schema`. twee-ts ignores such a key, so a misspelt one would
604
+ * otherwise leave its option at the default without a word. A key that differs from a defined one only in
605
+ * letter case, `-` or `_` (`formatID`, `output-mode`) gets a suggestion. Returns `[]` for anything that is not
606
+ * an object, which {@link validateConfig} reports.
607
+ */
608
+ export declare function unknownConfigKeyWarnings(data: unknown): string[];
533
609
  /** Return a default config JSON string for --init scaffolding. */
534
610
  export declare function scaffoldConfig(): string;
535
611
  //#endregion
536
612
  //#region src/remote-formats.d.ts
537
613
  /** Get the cache directory for downloaded story formats. */
538
614
  export declare function getCacheDir(): string;
539
- /** Download a format, verify its checksum, write to cache, and return StoryFormatInfo. */
540
- export declare function fetchAndCacheFormat(entry: SFAIndexEntry, downloadUrl: string): Promise<StoryFormatInfo>;
541
- /** Download a direct format.js URL, parse its JSON, cache it, and return StoryFormatInfo. */
542
- export declare function fetchDirectFormat(url: string): Promise<StoryFormatInfo>;
615
+ /**
616
+ * Download a format, verify its checksum, write it to the cache shared by name and version, and
617
+ * return its StoryFormatInfo. Concurrent calls for one download URL share one request.
618
+ */
619
+ export declare function fetchAndCacheFormat(entry: SFAIndexEntry, downloadUrl: string, options?: RemoteFetchOptions): Promise<StoryFormatInfo>;
620
+ /**
621
+ * Download a direct format.js URL, parse its JSON, cache it under that URL (apart from the
622
+ * downloads shared by name and version), and return its StoryFormatInfo. Concurrent calls for
623
+ * one URL share one request.
624
+ */
625
+ export declare function fetchDirectFormat(url: string, options?: RemoteFetchOptions): Promise<StoryFormatInfo>;
543
626
  /**
544
627
  * Try to resolve a remote story format by name and version.
545
- * 0. Use an exactly matching cached download, without touching the network
546
- * 1. Try direct format URLs
547
- * 2. Try custom index URLs
548
- * 3. Try default SFA indices
549
- * 4. Fall back to a compatible cached download (e.g. when offline)
628
+ * 1. Try direct format URLs (each one's cached copy, else a download)
629
+ * 2. Use an exactly matching download from the shared cache, without touching the network
630
+ * 3. Try custom index URLs, then the default SFA indices
631
+ * 4. Fall back to a compatible download from the shared cache (e.g. when offline)
632
+ *
633
+ * `options.signal` aborts the lookup, which then rejects with the signal's reason;
634
+ * `options.timeout` limits each request (default {@link DEFAULT_FORMAT_FETCH_TIMEOUT} ms).
550
635
  */
551
- export declare function resolveRemoteFormat(name: string, version: string, indices?: string[], urls?: string[]): Promise<StoryFormatInfo | undefined>;
636
+ export declare function resolveRemoteFormat(name: string, version: string, indices?: readonly string[], urls?: readonly string[], options?: RemoteFetchOptions): Promise<StoryFormatInfo | undefined>;
552
637
  /** Discover all cached remote formats. Returns a Map like discoverFormats(). */
553
638
  export declare function discoverCachedFormats(): Map<string, StoryFormatInfo>;
554
639
  /** Info about a cached format entry with size and modification date. */
@@ -562,6 +647,8 @@ interface CachedFormatEntry {
562
647
  export declare function listCachedFormats(): readonly CachedFormatEntry[];
563
648
  /**
564
649
  * Clear all cached formats, or only those matching a given name. Returns the number of entries removed.
650
+ * Clearing all also removes the downloads from direct format URLs; a name clears only the
651
+ * downloads shared by name and version.
565
652
  * A name must be a single cache entry name (as `listCachedFormats()` reports it); anything with a
566
653
  * path separator or `.`/`..` throws instead of deleting outside the format's own directory.
567
654
  */
@@ -572,4 +659,4 @@ export declare function getCacheSize(): {
572
659
  count: number;
573
660
  };
574
661
  //#endregion
575
- export type { BrokenLink, CachedFormatEntry, CompileOptions, CompileResult, CompileStats, CompileToFileOptions, DecompileOptions, DecompileResult, Diagnostic, FileCacheEntry, IFID, InlineSource, InspectOptions, LexerItem, LintResult, OmittingTag, OutputMode, Passage, PassageMetadata, PassageOmission, PassageOutputTarget, ReadonlyPassage, ReadonlyStory, SFAIndex, SFAIndexEntry, SourceInput, SourceLocation, Story, StoryFormatInfo, StoryMap, TweeTsConfig, WatchOptions, WordCountMethod };
662
+ export type { BrokenLink, CachedFormatEntry, CompileOptions, CompileResult, CompileStats, CompileToFileOptions, DecompileOptions, DecompileResult, Diagnostic, FileCacheEntry, IFID, InlineSource, InspectOptions, LexerItem, LintResult, OmittingTag, OutputMode, Passage, PassageMetadata, PassageOmission, PassageOutputTarget, ReadonlyPassage, ReadonlyStory, RemoteFetchOptions, SFAIndex, SFAIndexEntry, SourceInput, SourceLocation, Story, StoryFormatInfo, StoryMap, TweeTsConfig, WatchOptions, WordCountMethod };