@rohal12/twee-ts 1.18.2 → 2.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.
Files changed (42) hide show
  1. package/README.md +23 -3
  2. package/THIRD_PARTY_NOTICES +111 -0
  3. package/UNLICENSE +24 -0
  4. package/dist/bin/twee-ts.js +705 -9447
  5. package/dist/bin/twee-ts.js.map +1 -1
  6. package/dist/diagnostic-text-Bj-UQ0Jl.cjs +27007 -0
  7. package/dist/diagnostic-text-Bj-UQ0Jl.cjs.map +1 -0
  8. package/dist/diagnostic-text-DMVZfBh9.js +26530 -0
  9. package/dist/diagnostic-text-DMVZfBh9.js.map +1 -0
  10. package/dist/index.cjs +91 -9140
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +208 -416
  13. package/dist/index.d.ts +208 -416
  14. package/dist/index.js +4 -9317
  15. package/dist/lint-BkpfLGuP.js +288 -0
  16. package/dist/lint-BkpfLGuP.js.map +1 -0
  17. package/dist/options-DuBkFyqi.d.cts +28 -0
  18. package/dist/options-nc5NpnSc.d.ts +28 -0
  19. package/dist/plugins/rollup.cjs +177 -0
  20. package/dist/plugins/rollup.cjs.map +1 -0
  21. package/dist/plugins/rollup.d.cts +52 -0
  22. package/dist/plugins/rollup.d.ts +13 -12
  23. package/dist/plugins/rollup.js +136 -66
  24. package/dist/plugins/rollup.js.map +1 -1
  25. package/dist/plugins/vite.cjs +2225 -0
  26. package/dist/plugins/vite.cjs.map +1 -0
  27. package/dist/plugins/vite.d.cts +15 -0
  28. package/dist/plugins/vite.d.ts +5 -12
  29. package/dist/plugins/vite.js +2120 -516
  30. package/dist/plugins/vite.js.map +1 -1
  31. package/dist/types-BvI6pg2y.d.cts +389 -0
  32. package/dist/types-BvI6pg2y.d.ts +389 -0
  33. package/dist/watch-targets-BrEYmUwU.cjs +643 -0
  34. package/dist/watch-targets-BrEYmUwU.cjs.map +1 -0
  35. package/dist/watch-targets-DzvPyWDo.js +548 -0
  36. package/dist/watch-targets-DzvPyWDo.js.map +1 -0
  37. package/package.json +80 -27
  38. package/schemas/twee-ts.config.schema.json +55 -17
  39. package/dist/index.js.map +0 -1
  40. package/dist/paths-BuTAg2av.js +0 -7044
  41. package/dist/paths-BuTAg2av.js.map +0 -1
  42. package/dist/types-D8Xfe0l8.d.ts +0 -71
package/dist/index.d.cts CHANGED
@@ -1,333 +1,62 @@
1
- //#region src/types.d.ts
1
+
2
+ import { A as StoryFormatInfo, C as RemoteResolveOptions, D as SourceInput, E as SemVer, F as Twine2Metadata, I as WatchOptions, L as WordCountMethod, M as TweeTsErrorCode, N as Twine1Metadata, O as SourceLocation, P as Twine2FormatJSON, S as RemoteFetchOptions, T as SFAIndexEntry, _ as PassageMetadata, a as DecompileOptions, b as ReadonlyPassage, c as FileCacheEntry, d as InspectOptions, f as ItemType, g as Passage, h as OutputMode, i as CompileToFileOptions, j as TweeTsConfig, k as Story, l as IFID, m as OmittingTag, n as CompileResult, o as Diagnostic, p as LexerItem, r as CompileStats, s as ExcludeGlob, t as CompileOptions, u as InlineSource, v as PassageOmission, w as SFAIndex, x as ReadonlyStory, y as PassageOutputTarget } from "./types-BvI6pg2y.cjs";
3
+ //#region src/filesystem.d.ts
2
4
  /**
3
- * All public interfaces for twee-ts.
5
+ * A watched path that can't be watched: fs.watch failed on it, or on the folder it is
6
+ * watched through. watchFilesystem passes it to its `onError` and keeps watching the rest.
4
7
  */
5
- type OutputMode = 'html' | 'twee3' | 'twee1' | 'twine2-archive' | 'twine1-archive' | 'json';
6
- type WordCountMethod = 'tweego' | 'whitespace';
7
- type InlineSource = {
8
- filename: string;
9
- content: string | Buffer;
10
- };
11
- type SourceInput = string | InlineSource;
12
- interface CompileOptions {
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[];
19
- /**
20
- * Glob patterns for files to leave out of `sources`, matched against each file's
21
- * path relative to the working directory (as `stats.files` lists it). Modules are not affected.
22
- */
23
- exclude?: readonly string[];
24
- /** Output mode. Default: 'html'. */
25
- outputMode?: OutputMode;
26
- /** Story format directory ID (e.g. 'sugarcube-2'). */
27
- formatId?: string;
28
- /** Name of the starting passage. Default: 'Start'. */
29
- startPassage?: string;
30
- /** Extra directories to search for story formats. */
31
- formatPaths?: readonly string[];
32
- /** Also search TWEEGO_PATH env for formats. Default: true. */
33
- useTweegoPath?: boolean;
34
- /** Module files to inject into <head>. */
35
- modules?: readonly string[];
36
- /** Raw HTML file to append to <head>. */
37
- headFile?: string;
38
- /** Trim passage whitespace. Default: true. */
39
- trim?: boolean;
40
- /** Twee2 compatibility mode. Default: false. */
41
- twee2Compat?: boolean;
42
- /** Enable debug/test mode option. Default: false. */
43
- testMode?: boolean;
44
- /** URLs to SFA-compatible index.json files for remote format lookup. */
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[];
51
- /** Disable remote format fetching. Default: false. */
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;
63
- /** Map alias tags to canonical special tags (e.g. { library: 'script' }). */
64
- tagAliases?: Record<string, string>;
65
- /** Emit source file and line as data- attributes on passage elements. Default: false. */
66
- sourceInfo?: boolean;
67
- /** Word counting method. Default: 'tweego'. */
68
- wordCountMethod?: WordCountMethod;
69
- }
70
- interface CompileToFileOptions extends CompileOptions {
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
- */
76
- outFile: string;
8
+ export declare class WatchPathError extends Error {
9
+ readonly path: string;
10
+ constructor(path: string, cause: unknown);
77
11
  }
78
- interface WatchOptions extends CompileToFileOptions {
79
- /**
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.
83
- */
84
- onBuild?: (result: CompileResult) => void;
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
- */
90
- onError?: (error: Error) => void;
91
- }
92
- type Diagnostic = {
93
- level: 'warning';
94
- message: string;
95
- file?: string;
96
- line?: number;
97
- } | {
98
- level: 'error';
99
- message: string;
100
- file?: string;
101
- line?: number;
102
- fatal?: boolean;
103
- };
104
- interface CompileResult {
105
- /** The compiled output string (HTML, Twee, JSON, etc.). */
106
- output: string;
107
- /** The parsed story model (read-only after compilation). */
108
- story: ReadonlyStory;
109
- /** The format used for compilation (undefined for non-HTML modes). */
110
- format?: StoryFormatInfo;
111
- /** Collected diagnostics. */
112
- diagnostics: Diagnostic[];
113
- /** Compilation statistics. */
114
- stats: CompileStats;
115
- }
116
- interface CompileStats {
117
- passages: number;
118
- storyPassages: number;
119
- words: number;
120
- files: string[];
121
- }
122
- interface SourceLocation {
123
- readonly file: string;
124
- readonly line: number;
125
- }
126
- interface PassageMetadata {
127
- position?: string;
128
- size?: string;
129
- [key: string]: string | undefined;
130
- }
131
- interface Passage {
132
- name: string;
133
- tags: string[];
134
- text: string;
135
- metadata?: PassageMetadata;
136
- source?: SourceLocation;
137
- }
138
- type IFID = string & {
139
- readonly __brand: 'IFID';
140
- };
141
- interface Twine1Metadata {
142
- settings: Map<string, string>;
143
- }
144
- interface Twine2Metadata {
145
- format: string;
146
- formatVersion: string;
147
- options: Map<string, boolean>;
148
- start: string;
149
- tags: string;
150
- tagColors: Map<string, string>;
151
- zoom: number;
152
- }
153
- interface Story {
154
- name: string;
155
- ifid: IFID;
156
- passages: Passage[];
157
- legacyIFID: IFID;
158
- twine1: Twine1Metadata;
159
- twine2: Twine2Metadata;
160
- }
161
- type ReadonlyPassage = Readonly<Omit<Passage, 'tags' | 'metadata'>> & {
162
- readonly tags: readonly string[];
163
- readonly metadata?: Readonly<PassageMetadata>;
164
- };
165
- type ReadonlyStory = Readonly<Omit<Story, 'passages' | 'twine1' | 'twine2'>> & {
166
- readonly passages: readonly ReadonlyPassage[];
167
- readonly twine1: Readonly<Omit<Twine1Metadata, 'settings'>> & {
168
- readonly settings: ReadonlyMap<string, string>;
169
- };
170
- readonly twine2: Readonly<Omit<Twine2Metadata, 'options' | 'tagColors'>> & {
171
- readonly options: ReadonlyMap<string, boolean>;
172
- readonly tagColors: ReadonlyMap<string, string>;
173
- };
174
- };
175
- type OmittingTag = 'Twine.private' | 'script' | 'stylesheet';
176
- type PassageOmission = {
177
- readonly kind: 'special-name';
178
- readonly name: 'StoryData' | 'StoryTitle';
179
- } | {
180
- readonly kind: 'tag';
181
- readonly tag: OmittingTag;
182
- } | {
183
- readonly kind: 'empty-story-settings';
184
- };
185
- /** The output whose rules decide which passages a story emits as passages. */
186
- type PassageOutputTarget = 'twine1' | 'twine2';
187
- interface DecompileOptions {
188
- /**
189
- * Trim whitespace at both ends of passage text (and of the Twine 2 story stylesheet and script),
190
- * as the Twee lexer does. When false, the stored text is kept exactly. Default: true.
191
- */
192
- readonly trim?: boolean;
193
- }
194
- interface InspectOptions {
195
- /**
196
- * Check link destinations against this output's passage rules. A link to a passage that the
197
- * output leaves out (in Twine 2: `script`, `stylesheet` and `Twine.private` passages,
198
- * StoryData, StoryTitle and an empty StorySettings; in Twine 1: `Twine.private` passages) is
199
- * then a broken link, and says why. The passages it leaves out also give no links, as their
200
- * text never reaches the player, except script passages, which Twine 2 output runs. Without a
201
- * target, every source passage gives links, and a link is broken only when no passage has its
202
- * name.
203
- */
204
- readonly target?: PassageOutputTarget;
205
- }
206
- interface StoryFormatInfo {
207
- id: string;
208
- filename: string;
209
- isTwine2: boolean;
210
- name: string;
211
- version: string;
212
- proofing: boolean;
213
- author?: string;
214
- description?: string;
215
- image?: string;
216
- url?: string;
217
- license?: string;
218
- }
219
- interface Twine2FormatJSON {
220
- name: string;
221
- version: string;
222
- proofing?: boolean;
223
- source: string;
224
- author?: string;
225
- description?: string;
226
- image?: string;
227
- url?: string;
228
- license?: string;
229
- }
230
- export declare const ItemType: {
231
- readonly Error: 0;
232
- readonly EOF: 1;
233
- readonly Header: 2;
234
- readonly Name: 3;
235
- readonly Tags: 4;
236
- readonly Metadata: 5;
237
- readonly Content: 6;
238
- };
239
- export type ItemType = (typeof ItemType)[keyof typeof ItemType];
240
- interface LexerItem {
241
- type: ItemType;
242
- line: number;
243
- pos: number;
244
- val: string;
245
- }
246
- interface SFAIndexEntry {
247
- name: string;
248
- version: string;
249
- proofing: boolean;
250
- files: string[];
251
- checksums: Record<string, string>;
252
- }
253
- interface SFAIndex {
254
- twine1: SFAIndexEntry[];
255
- twine2: SFAIndexEntry[];
256
- }
257
- /** Options for the network requests that fetch story formats and their indices. */
258
- interface RemoteFetchOptions {
259
- /** Aborts the requests; the call then rejects with the signal's reason. */
260
- readonly signal?: AbortSignal;
261
- /** Milliseconds each request may take before it fails. 0 turns the limit off. Default: 30000. */
262
- readonly timeout?: number;
263
- }
264
- interface FileCacheEntry {
265
- readonly mtimeMs: number;
266
- /**
267
- * Identity of the parse options (`trim`, `twee2Compat`) the entry was parsed with. An entry is
268
- * reused only when this matches the current options; an entry without it is treated as stale
269
- * and reparsed. The value is opaque: leave it to twee-ts to set.
270
- */
271
- readonly parseOptionsKey?: string;
272
- readonly passages: readonly Passage[];
12
+ //#endregion
13
+ //#region src/errors.d.ts
14
+ /**
15
+ * A build that could not run: nothing was built or written. `code` says why (see TweeTsErrorCode),
16
+ * `diagnostics` holds what the build reported before it stopped, and `cause` the error behind it, if any.
17
+ */
18
+ export declare class TweeTsError extends Error {
273
19
  readonly diagnostics: readonly Diagnostic[];
274
- }
275
- interface TweeTsConfig {
276
- sources?: string[];
277
- exclude?: string[];
278
- output?: string;
279
- outputMode?: OutputMode;
280
- formatId?: string;
281
- startPassage?: string;
282
- formatPaths?: string[];
283
- formatIndices?: string[];
284
- formatUrls?: string[];
285
- useTweegoPath?: boolean;
286
- modules?: string[];
287
- headFile?: string;
288
- trim?: boolean;
289
- twee2Compat?: boolean;
290
- testMode?: boolean;
291
- noRemote?: boolean;
292
- /** Milliseconds each story format request may take. 0 turns the limit off. Default: 30000. */
293
- formatFetchTimeout?: number;
294
- tagAliases?: Record<string, string>;
295
- sourceInfo?: boolean;
296
- wordCountMethod?: WordCountMethod;
20
+ readonly code: TweeTsErrorCode;
21
+ constructor(message: string, diagnostics?: readonly Diagnostic[], options?: {
22
+ readonly code?: TweeTsErrorCode;
23
+ readonly cause?: unknown;
24
+ });
297
25
  }
298
26
  //#endregion
299
27
  //#region src/compiler.d.ts
300
- export declare class TweeTsError extends Error {
301
- diagnostics: Diagnostic[];
302
- constructor(message: string, diagnostics?: Diagnostic[]);
303
- }
304
28
  /**
305
29
  * Compile Twee sources to HTML, Twee, or JSON.
306
30
  */
307
31
  export declare function compile(options: CompileOptions): Promise<CompileResult>;
308
32
  /**
309
- * Compile and write to a file. The file is replaced atomically: a reader sees the previous
310
- * build or the new one, never part of it, and a failed write leaves the previous build.
33
+ * Compile and write to a file. How the file is written depends on what is there (see atomic-write.ts): a
34
+ * regular file is replaced atomically, so a reader sees the previous build or the new one, never part of it,
35
+ * and a failed write leaves the previous build; a FIFO or a device is written through; a read-only file is
36
+ * refused. An output that can't be written (a read-only file, a missing folder) fails before the build.
311
37
  */
312
38
  export declare function compileToFile(options: CompileToFileOptions): Promise<CompileResult>;
313
39
  /**
314
40
  * Compile with incremental caching support.
315
41
  * Plugins and advanced users can manage their own cache and changed-file tracking.
316
42
  *
317
- * Without `changedFiles`, a cached file is reused while its modification time is unchanged.
318
- * With it, a file it names is always reparsed, whatever its modification time, and every
43
+ * Without `changedFiles`, a cached file is reused while its modification time, size, inode and
44
+ * status-change time are unchanged. With it, a file it names is always reparsed, and every
319
45
  * other cached file is reused as it is. A file may be named by an absolute path or by a path
320
- * relative to the working directory, with or without a leading `./`: entries are matched to
321
- * the source files by resolved path. A file that fails to load is dropped from the cache, so
322
- * the next build tries it again.
46
+ * relative to the working directory, with or without a leading `./`, or through a link: entries are
47
+ * matched to the source files by identity (see path-identity.ts). A file that fails to load is
48
+ * dropped from the cache, so the next build tries it again.
323
49
  */
324
50
  export declare function compileIncremental(options: CompileOptions, cache: Map<string, FileCacheEntry>, changedFiles?: ReadonlySet<string>): Promise<CompileResult>;
325
51
  /**
326
- * Watch for file changes and recompile. Every build is written to `outFile` (replacing it
327
- * atomically, as compileToFile() does), including one whose diagnostics report errors.
328
- * Builds run one at a time, and each one is written and reported as it finishes, in order:
329
- * changes made during a build go into one follow-up build after it. A watched path that
330
- * can't be watched is passed to `onError`.
52
+ * Watch for file changes and recompile. Every build is written to `outFile` (as compileToFile()
53
+ * writes it), including one whose diagnostics report errors. Builds run one at a time, and each
54
+ * one is written and reported as it finishes, in order: changes made during a build go into one
55
+ * follow-up build after it. A watched path that can't be watched is passed to `onError`.
56
+ *
57
+ * Options that can't work (an output that is also a named input or an authored file in a source or module
58
+ * folder, an out-of-range option) reject the returned promise before anything is watched. A build that fails for such a reason later (a link
59
+ * changed to make the output an input) is passed to `onError`, and watching stops.
331
60
  *
332
61
  * Aborting the returned controller, or the `signal` in the options, stops watching and
333
62
  * aborts the story format requests of the build in progress.
@@ -337,48 +66,56 @@ export declare function watch(options: WatchOptions): Promise<AbortController>;
337
66
  //#region src/inspect.d.ts
338
67
  interface StoryMap {
339
68
  /** All passage names, in source order. */
340
- passages: string[];
69
+ readonly passages: readonly string[];
341
70
  /** Only story passages (excludes StoryData, StoryTitle, scripts, stylesheets, etc.). */
342
- storyPassages: string[];
71
+ readonly storyPassages: readonly string[];
343
72
  /** Only info/special passages. */
344
- infoPassages: string[];
73
+ readonly infoPassages: readonly string[];
345
74
  /** All unique tags used across the story, sorted alphabetically. */
346
- tags: string[];
75
+ readonly tags: readonly string[];
347
76
  /** Map of tag → passage names that carry that tag, each listed once even if the tag is repeated. */
348
- passagesByTag: Map<string, string[]>;
77
+ readonly passagesByTag: ReadonlyMap<string, readonly string[]>;
349
78
  /** Map of passage name → tags on that passage, as written (a repeated tag appears twice). */
350
- tagsByPassage: Map<string, string[]>;
79
+ readonly tagsByPassage: ReadonlyMap<string, readonly string[]>;
351
80
  /**
352
81
  * Map of passage name → passage names it links to.
353
82
  * Parses `[[target]]`, `[[display->target]]`, `[[target<-display]]`, `[[display|target]]`,
354
- * each with or without a setter (`[[display|target][$x to 1]]`), and SugarCube's
355
- * `<<goto "target">>` / `<<link "display" "target">>`, read as SugarCube 2 reads them
356
- * (see `sugarcube-macros.ts`). Links and calls in comments are not read. A script passage is
83
+ * each with or without a setter (`[[display|target][$x to 1]]`), SugarCube's
84
+ * `<<goto "target">>` / `<<link "display" "target">>` / `<<button "display" "target">>`, and
85
+ * elements with a `data-passage="target"` attribute, read as SugarCube 2 reads them
86
+ * (see "How links are read" in docs/api.md). Links and calls in comments are not read. A script passage is
357
87
  * read for links only in its strings; a stylesheet passage (including a loaded `.css` file) is
358
88
  * CSS and links to nothing. With a `target` (see `InspectOptions`), a passage that output leaves
359
89
  * out links to nothing, except a script passage, which Twine 2 output runs.
360
90
  */
361
- links: Map<string, string[]>;
91
+ readonly links: ReadonlyMap<string, readonly string[]>;
362
92
  /**
363
93
  * Broken links: `{ from, to }` pairs where `to` doesn't exist as a passage, or, with a `target`
364
94
  * (see `InspectOptions`), where that output leaves the `to` passage out (`omission` says why).
365
95
  */
366
- brokenLinks: BrokenLink[];
96
+ readonly brokenLinks: readonly BrokenLink[];
367
97
  /** Story passages with no outgoing links (potential dead ends). */
368
- deadEnds: string[];
369
- /** Story passages that no other passage links to (excluding the start passage). */
370
- orphans: string[];
98
+ readonly deadEnds: readonly string[];
99
+ /**
100
+ * Story passages that the player cannot reach: no chain of links leads to them from the start passage or
101
+ * from an info passage the story format runs or shows without a link (such as StoryInit, PassageHeader, or a
102
+ * `script` or `widget` passage). An `annotation` note, a `Twine.` passage (private or media), StoryData,
103
+ * StorySettings and StoryIncludes are not shown, so links from them count only when a link leads to them. A
104
+ * passage that links only to itself, or a group that links only among itself, is an orphan. Only the links
105
+ * `links` lists count, so a passage that is shown only through a macro such as `<<include>>` is listed too.
106
+ */
107
+ readonly orphans: readonly string[];
371
108
  /** The configured start passage name, if any. */
372
- start: string;
109
+ readonly start: string;
373
110
  }
374
111
  interface BrokenLink {
375
- from: string;
376
- to: string;
112
+ readonly from: string;
113
+ readonly to: string;
377
114
  /**
378
115
  * Set when a passage named `to` exists, but the inspected output leaves it out of the story
379
116
  * (only with a `target`): why it is left out. Absent when no passage has the name.
380
117
  */
381
- omission?: PassageOmission;
118
+ readonly omission?: PassageOmission;
382
119
  }
383
120
  /**
384
121
  * Inspect a story and return its full structural map.
@@ -407,24 +144,53 @@ export declare function storyInspect(story: ReadonlyStory, options?: InspectOpti
407
144
  //#endregion
408
145
  //#region src/story.d.ts
409
146
  /**
410
- * Builder that separates the mutable construction phase from the immutable consumption phase.
411
- * During construction, the internal `Story` is mutable via `add()` and direct access: you may
412
- * push, splice, sort, rename or reassign `story.passages` between calls, and `add()` and `has()`
413
- * see the passages as they are then.
414
- * After `build()`, the story is returned as `ReadonlyStory`.
147
+ * Builds a story from passages, as the compiler does: duplicates replace earlier passages (with a warning),
148
+ * and the special passages StoryTitle, StoryData and StorySettings decide the story metadata.
149
+ *
150
+ * The story is changed only through the builder's methods, so its passage list, its name lookups and its
151
+ * metadata always agree. Passages are copied in and out: changing a passage object after `add()`, or one that
152
+ * `get()`, `passages` or `build()` returned, does not change the builder.
415
153
  */
416
154
  export declare class StoryBuilder {
417
- /** The mutable story, accessible during construction for loader functions. */
418
- readonly story: Story;
419
- constructor();
420
- /** Add a passage, handling special passages (StoryData, StoryTitle, etc.). */
155
+ #private;
156
+ /** Add a passage, handling special passages (StoryData, StoryTitle, etc.) and duplicates. */
421
157
  add(passage: Passage, diagnostics: Diagnostic[]): void;
422
- /** Check if a passage name exists. */
158
+ /** Whether a passage has this name. */
423
159
  has(name: string): boolean;
424
- /** Finalize and return the story as read-only. */
160
+ /** The passage with this name (a frozen copy), or undefined. */
161
+ get(name: string): ReadonlyPassage | undefined;
162
+ /**
163
+ * Remove the passage with this name, and with it the metadata it decided (removing StoryData clears the
164
+ * IFID, format and start). Returns false when no passage has the name.
165
+ */
166
+ remove(name: string): boolean;
167
+ /**
168
+ * Rename a passage, keeping its place in the story. A passage that already has the new name is replaced,
169
+ * with a warning, as a duplicate would be. Returns false when no passage has the old name.
170
+ */
171
+ rename(from: string, to: string, diagnostics: Diagnostic[]): boolean;
172
+ /** The passages, in order (frozen copies). */
173
+ get passages(): readonly ReadonlyPassage[];
174
+ /** The story as it is now, as a frozen copy that later changes to the builder do not reach. */
425
175
  build(): ReadonlyStory;
426
176
  }
427
177
  //#endregion
178
+ //#region src/start-passage.d.ts
179
+ /**
180
+ * Synchronously check that `startName` names a passage the `target` output emits.
181
+ * By default, use the story's effective start, falling back to `Start`, and Twine 2 rules.
182
+ * An explicit name overrides the metadata for this check only; the story is never changed.
183
+ * Returns the compiler's error diagnostics (empty when the start is usable).
184
+ */
185
+ export declare function startPassageDiagnostics(story: ReadonlyStory, startName?: string, target?: PassageOutputTarget): readonly Diagnostic[];
186
+ //#endregion
187
+ //#region src/passage-omission.d.ts
188
+ /**
189
+ * Why the `target` output leaves `passage` out of its passage elements, or `undefined` when it
190
+ * emits the passage (and so a link or the start can name it).
191
+ */
192
+ export declare function passageOmission(story: ReadonlyStory, passage: ReadonlyPassage, target: PassageOutputTarget): PassageOmission | undefined;
193
+ //#endregion
428
194
  //#region src/html-parser.d.ts
429
195
  interface DecompileResult {
430
196
  story: Story;
@@ -441,36 +207,40 @@ export declare function decompileHTML(html: string, options?: DecompileOptions):
441
207
  //#region src/lint.d.ts
442
208
  interface LintResult {
443
209
  /** Compilation diagnostics (errors and warnings). */
444
- diagnostics: Diagnostic[];
210
+ readonly diagnostics: readonly Diagnostic[];
445
211
  /** Compilation statistics. */
446
- stats: CompileStats;
212
+ readonly stats: CompileStats;
447
213
  /** Story format name from StoryData (e.g. "SugarCube"). */
448
- formatName: string;
214
+ readonly formatName: string;
449
215
  /** Story format version from StoryData (e.g. "2.37.3"). */
450
- formatVersion: string;
216
+ readonly formatVersion: string;
451
217
  /** The configured start passage name. */
452
- start: string;
218
+ readonly start: string;
453
219
  /** Total passage count. */
454
- passages: number;
220
+ readonly passages: number;
455
221
  /** Story passage count (excludes StoryData, StoryTitle, scripts, etc.). */
456
- storyPassages: number;
222
+ readonly storyPassages: number;
457
223
  /** Info/special passage count. */
458
- infoPassages: number;
224
+ readonly infoPassages: number;
459
225
  /**
460
226
  * Broken links: link targets that don't exist as passages, or that Twine 2 output leaves out
461
227
  * (script, stylesheet and `Twine.private` passages, StoryData, StoryTitle, an empty
462
228
  * StorySettings); for those, `omission` says why.
463
229
  */
464
- brokenLinks: BrokenLink[];
230
+ readonly brokenLinks: readonly BrokenLink[];
465
231
  /** Story passages with no outgoing links. */
466
- deadEnds: string[];
467
- /** Story passages that no other passage links to. */
468
- orphans: string[];
232
+ readonly deadEnds: readonly string[];
233
+ /**
234
+ * Story passages the player cannot reach: no chain of links leads to them from the start passage or from
235
+ * an info passage the story format runs or shows without a link (see `StoryMap.orphans`).
236
+ */
237
+ readonly orphans: readonly string[];
469
238
  }
470
239
  /**
471
240
  * Lint a story: compile without output rendering, then inspect structure.
472
241
  * Uses JSON output mode internally to avoid format resolution, so it checks
473
- * the starting passage itself, against the Twine 2 passage rules. Link
242
+ * the starting passage itself, against the Twine 2 passage rules, and adds
243
+ * what Twine 2 HTML output reports about the story's own data. Link
474
244
  * destinations are checked against the same rules: a link to a passage that
475
245
  * Twine 2 output leaves out is broken, and a passage it leaves out (other than
476
246
  * a script passage) gives no links.
@@ -483,11 +253,15 @@ export declare function formatLintReport(result: LintResult): string;
483
253
  //#endregion
484
254
  //#region src/passage.d.ts
485
255
  /**
486
- * Apply tag aliases: for each passage carrying an alias tag, add the canonical
487
- * tag if not already present. Returns new passage objects where tags changed;
488
- * unchanged passages are returned as-is. Idempotent — safe to call multiple times.
256
+ * Apply tag aliases: for each passage carrying an alias tag, add the canonical tag if not already present. A
257
+ * canonical tag that is itself an alias is followed (`{ a: 'b', b: 'c' }` adds `b` and `c` to a passage tagged
258
+ * `a`), until no mapping adds anything, so cycles and self-mappings end too. Authored tags are kept as written (a
259
+ * tag written twice stays twice, as in Tweego), whether or not an alias applies; added tags follow in the order they
260
+ * were reached, and none repeats a tag already there. Returns new passage objects where tags changed; unchanged
261
+ * passages are returned as-is. Idempotent: the result carries every tag the mappings reach, so applying the same
262
+ * aliases again adds nothing.
489
263
  */
490
- export declare function applyTagAliases(passages: readonly Passage[], aliases: Record<string, string>): Passage[];
264
+ export declare function applyTagAliases(passages: readonly Passage[], aliases: Readonly<Record<string, string>>): Passage[];
491
265
  //#endregion
492
266
  //#region src/lexer.d.ts
493
267
  /**
@@ -511,11 +285,14 @@ export declare class TweeLexer {
511
285
  //#region src/parser.d.ts
512
286
  interface ParseOptions {
513
287
  /** Filename for diagnostics. */
514
- filename?: string;
515
- /** Trim whitespace from passage content. Default: true. */
516
- trim?: boolean;
288
+ filename?: string | undefined;
289
+ /**
290
+ * Trim white space at both ends of passage content. Default: true. When false, the content is kept as
291
+ * written except for its trailing blank lines, which the Twee 3 specification requires a reader to drop.
292
+ */
293
+ trim?: boolean | undefined;
517
294
  /** Enable Twee2 compatibility. Default: false. */
518
- twee2Compat?: boolean;
295
+ twee2Compat?: boolean | undefined;
519
296
  }
520
297
  interface ParseResult {
521
298
  passages: Passage[];
@@ -524,25 +301,20 @@ interface ParseResult {
524
301
  /**
525
302
  * Parse Twee source text into passages.
526
303
  *
527
- * The source is normalized first, as files are when they are read: a leading UTF-8 BOM is
528
- * removed and CRLF and bare CR line endings become LF. A BOM at the start of a later line,
529
- * directly before `::` (left there by concatenating files), is removed too, so that line
530
- * stays a passage header.
304
+ * The source is normalized first (see `normalizeTweeSource`): a leading UTF-8 BOM is removed, CRLF and bare CR
305
+ * line endings become LF, and a BOM at the start of a later line, directly before `::` (left there by
306
+ * concatenating files), is removed too, so that line stays a passage header.
307
+ *
308
+ * Names and tags are read as Tweego reads them: unescaped, then the name trimmed and the tags split at white
309
+ * space, where white space is Go's `unicode.IsSpace` (see `isTweeSpace`).
531
310
  */
532
311
  export declare function parseTwee(source: string, options?: ParseOptions): ParseResult;
533
312
  //#endregion
534
313
  //#region src/formats.d.ts
535
- /**
536
- * Parse the Twine 2 format.js JSON chunk; null when it cannot be used ({@link decodeFormatJSON}
537
- * gives the reason).
538
- *
539
- * @param _formatId No longer used; kept so existing callers keep compiling.
540
- */
541
- export declare function parseFormatJSON(source: string, _formatId?: string): Twine2FormatJSON | null;
542
314
  /**
543
315
  * Discover all story formats in the given search directories, pruned by SemVer: within each
544
- * (name, major) group only the highest version survives. See {@link discoverAllFormats} for the
545
- * directory ranking and {@link pruneFormats} for pruning.
316
+ * (name, major) group only the highest version survives. A later search directory outranks an earlier one,
317
+ * and a format in it replaces one of the same name and version from an earlier directory.
546
318
  */
547
319
  export declare function discoverFormats(searchDirs: readonly string[]): Map<string, StoryFormatInfo>;
548
320
  /**
@@ -553,20 +325,27 @@ export declare function discoverFormats(searchDirs: readonly string[]): Map<stri
553
325
  * Tweego.
554
326
  */
555
327
  export declare function getFormatSearchDirs(extraPaths?: readonly string[], useTweegoPath?: boolean): string[];
328
+ //#endregion
329
+ //#region src/semver.d.ts
330
+ /** Parse a version string, or return null when it is not a version (see the module comment). */
331
+ export declare function parseVersion(text: string): SemVer | null;
332
+ /** Compare two versions by SemVer precedence: negative when `a` ranks lower, 0 when equal, positive when higher. */
333
+ export declare function compareVersions(a: SemVer, b: SemVer): number;
334
+ //#endregion
335
+ //#region src/format-decode.d.ts
556
336
  /**
557
- * Parse a version string into [major, minor, patch]. Accepts what {@link parseVersion} accepts
558
- * (`v1.2.3`, `1.2`, `2.0.0-beta.1`); the prerelease and build metadata are dropped.
337
+ * Read the format object of a Twine 2 `format.js` (the object literal passed to its `storyFormat()` call);
338
+ * null when the file holds no usable format. Compiling reports why a format cannot be used.
559
339
  */
560
- export declare function parseSemver(v: string): [number, number, number] | null;
561
- /** Compare [major, minor, patch] tuples. Use {@link compareVersions} to take prereleases into account. */
562
- export declare function semverCompare(a: [number, number, number], b: [number, number, number]): number;
340
+ export declare function parseFormatJSON(source: string): Twine2FormatJSON | null;
563
341
  //#endregion
564
342
  //#region src/ifid.d.ts
565
343
  /** Generate a new IFID (UUID v4, uppercase). */
566
344
  export declare function generateIFID(): IFID;
567
345
  /**
568
346
  * Validate and brand an IFID string. Throws on invalid input.
569
- * Returns the IFID in its stored form (see `normalizeIFID`).
347
+ * Returns the IFID in the form the story model stores: the uppercase bare UUID (a `UUID://…//` wrapper is
348
+ * removed).
570
349
  */
571
350
  export declare function createIFID(value: string): IFID;
572
351
  /**
@@ -579,19 +358,24 @@ export declare function validateIFID(ifid: string): string | null;
579
358
  //#region src/config.d.ts
580
359
  export declare const CONFIG_FILENAME = "twee-ts.config.json";
581
360
  /**
582
- * Load a config file from the given directory (default: cwd). Returns null if not found.
361
+ * Load a config file from the given directory (default: cwd). Returns null when nothing is at its path.
583
362
  *
584
363
  * @param diagnostics Receives warnings that do not stop the config from loading: keys the config does not
585
364
  * define, and a file that is not valid UTF-8 (it is read as Windows-1252).
586
- * @throws When the file exists but cannot be read, is not valid JSON or fails {@link validateConfig}.
365
+ * @throws A TweeTsError when something is at the path but cannot be read (a dangling symbolic link, a link loop,
366
+ * a folder that can't be searched, a file it may not read), is not valid JSON or fails {@link validateConfig}.
587
367
  */
588
368
  export declare function loadConfig(dir?: string, diagnostics?: Diagnostic[]): TweeTsConfig | null;
589
369
  /**
590
370
  * Load a config from a specific file path.
591
371
  *
372
+ * The paths in it (`sources`, `output`, `modules`, `headFile`, `formatPaths`) are relative to the folder
373
+ * that holds the config file, and so are the `exclude` globs: the config is returned with them rebased onto
374
+ * the working directory (or absolute, when that folder is outside it).
375
+ *
592
376
  * @param diagnostics Receives warnings that do not stop the config from loading: keys the config does not
593
377
  * define, and a file that is not valid UTF-8 (it is read as Windows-1252).
594
- * @throws When the file cannot be read, is not valid JSON or fails {@link validateConfig}.
378
+ * @throws A TweeTsError when the file cannot be read, is not valid JSON or fails {@link validateConfig}.
595
379
  */
596
380
  export declare function loadConfigFile(filePath: string, diagnostics?: Diagnostic[]): TweeTsConfig;
597
381
  /**
@@ -607,59 +391,67 @@ export declare function validateConfig(data: unknown): string[];
607
391
  * an object, which {@link validateConfig} reports.
608
392
  */
609
393
  export declare function unknownConfigKeyWarnings(data: unknown): string[];
610
- /** Return a default config JSON string for --init scaffolding. */
611
- export declare function scaffoldConfig(): string;
394
+ /** Return a default config JSON string for --init scaffolding, naming the schema of `version` (this twee-ts). */
395
+ export declare function scaffoldConfig(version?: string): string;
612
396
  //#endregion
613
- //#region src/remote-formats.d.ts
614
- /** Get the cache directory for downloaded story formats. */
615
- export declare function getCacheDir(): string;
616
- /**
617
- * Download a format, verify its checksum, check that it is the format the entry names, write it to
618
- * the cache shared by name and version, and return its StoryFormatInfo. Concurrent calls for one
619
- * download URL share one request for the bytes; each call then verifies them against its own
620
- * entry, within its own timeout.
621
- */
622
- export declare function fetchAndCacheFormat(entry: SFAIndexEntry, downloadUrl: string, options?: RemoteFetchOptions): Promise<StoryFormatInfo>;
623
- /**
624
- * Download a direct format.js URL, parse its JSON, cache it under that URL (apart from the
625
- * downloads shared by name and version), and return its StoryFormatInfo. Concurrent calls for
626
- * one URL share one request.
627
- */
628
- export declare function fetchDirectFormat(url: string, options?: RemoteFetchOptions): Promise<StoryFormatInfo>;
397
+ //#region src/format-resolution.d.ts
629
398
  /**
630
- * Try to resolve a remote story format by name and version.
631
- * 1. Try direct format URLs (each one's cached copy, else a download)
632
- * 2. Use an exactly matching download from the shared cache, without touching the network
633
- * 3. Try custom index URLs, then the default SFA indices
634
- * 4. Fall back to a compatible download from the shared cache (e.g. when offline)
399
+ * Resolve a story format by name and version from format URLs and format indices only (no local
400
+ * folders), by the same policy as a compile: `options.urls` first, then `options.indices`, then the
401
+ * Story Formats Archive. Returns undefined when no source has the format.
635
402
  *
636
403
  * `options.signal` aborts the lookup, which then rejects with the signal's reason;
637
- * `options.timeout` limits each request (default {@link DEFAULT_FORMAT_FETCH_TIMEOUT} ms).
404
+ * `options.timeout` limits each request (default 30000 ms), and `options.resolutionTimeout` the
405
+ * whole lookup (default 120000 ms); `options.useDefaultIndices: false` leaves the Story Formats
406
+ * Archive out.
407
+ *
408
+ * @throws A TweeTsError: `INVALID_OPTIONS` for a URL or time limit that cannot be used, before any
409
+ * request; `FORMAT_UNAVAILABLE`, listing every failure (also in its `diagnostics`), when no source
410
+ * has the format and some source failed.
638
411
  */
639
- export declare function resolveRemoteFormat(name: string, version: string, indices?: readonly string[], urls?: readonly string[], options?: RemoteFetchOptions): Promise<StoryFormatInfo | undefined>;
640
- /** Discover all cached remote formats. Returns a Map like discoverFormats(). */
641
- export declare function discoverCachedFormats(): Map<string, StoryFormatInfo>;
412
+ export declare function resolveRemoteFormat(name: string, version: string, options?: RemoteResolveOptions): Promise<StoryFormatInfo | undefined>;
413
+ //#endregion
414
+ //#region src/format-cache.d.ts
415
+ /** Get the cache directory for downloaded story formats. */
416
+ export declare function getCacheDir(): string;
642
417
  /** Info about a cached format entry with size and modification date. */
643
418
  interface CachedFormatEntry {
419
+ /** The format's name and version. */
644
420
  readonly name: string;
645
421
  readonly version: string;
422
+ /** Where it was downloaded from: a format index or a format URL. */
423
+ readonly source: 'index' | 'url';
424
+ /** The format index URL or the format URL. */
425
+ readonly origin: string;
646
426
  readonly sizeBytes: number;
427
+ /** When it was downloaded. */
647
428
  readonly modifiedAt: Date;
648
429
  }
649
- /** List all cached formats with size and modification date. */
430
+ /** List all cached formats (downloads from format indices and from format URLs), in a stable order. */
650
431
  export declare function listCachedFormats(): readonly CachedFormatEntry[];
651
432
  /**
652
- * Clear all cached formats, or only those matching a given name. Returns the number of entries removed.
653
- * Clearing all also removes the downloads from direct format URLs; a name clears only the
654
- * downloads shared by name and version.
655
- * A name must be a single cache entry name (as `listCachedFormats()` reports it); anything with a
656
- * path separator or `.`/`..` throws instead of deleting outside the format's own directory.
433
+ * Every cached format whose files are intact, keyed by cache entry (so two downloads of the same
434
+ * name and version from different origins are both listed).
435
+ */
436
+ export declare function discoverCachedFormats(): Map<string, StoryFormatInfo>;
437
+ /**
438
+ * Clear all cached formats (no name given), or only those with a given name (matched as format names are, without
439
+ * regard to letter case; an empty name matches none). Returns the number of cached formats removed. Clearing all also removes
440
+ * the directories older twee-ts versions wrote.
657
441
  */
658
442
  export declare function clearCachedFormats(name?: string): number;
659
443
  /** Get total cache size in bytes and format count. */
660
444
  export declare function getCacheSize(): {
661
- totalBytes: number;
662
- count: number;
445
+ readonly totalBytes: number;
446
+ readonly count: number;
663
447
  };
664
448
  //#endregion
665
- 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 };
449
+ //#region src/remote-formats.d.ts
450
+ /**
451
+ * Download a direct format.js URL (or confirm that its cached copy is current), cache it under
452
+ * that URL, and return its StoryFormatInfo. Concurrent calls for one URL share one request.
453
+ * Throws when the URL is not usable, the download fails, or the cache cannot be written.
454
+ */
455
+ export declare function fetchDirectFormat(url: string, options?: RemoteFetchOptions): Promise<StoryFormatInfo>;
456
+ //#endregion
457
+ export { type BrokenLink, type CachedFormatEntry, type CompileOptions, type CompileResult, type CompileStats, type CompileToFileOptions, type DecompileOptions, type DecompileResult, type Diagnostic, type ExcludeGlob, type FileCacheEntry, type IFID, type InlineSource, type InspectOptions, ItemType, type LexerItem, type LintResult, type OmittingTag, type OutputMode, type ParseOptions, type ParseResult, type Passage, type PassageMetadata, type PassageOmission, type PassageOutputTarget, type ReadonlyPassage, type ReadonlyStory, type RemoteFetchOptions, type RemoteResolveOptions, type SFAIndex, type SFAIndexEntry, type SemVer, type SourceInput, type SourceLocation, type Story, type StoryFormatInfo, type StoryMap, type TweeTsConfig, type TweeTsErrorCode, type Twine1Metadata, type Twine2FormatJSON, type Twine2Metadata, type WatchOptions, type WordCountMethod };