@patterkit/model 0.14.0 → 0.16.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.cjs CHANGED
@@ -31,10 +31,15 @@ __export(index_exports, {
31
31
  DEFAULT_RECORDING_STATUSES: () => DEFAULT_RECORDING_STATUSES,
32
32
  DEFAULT_SPEAKER_PATTERN: () => DEFAULT_SPEAKER_PATTERN,
33
33
  DEFAULT_WRITING_STATUSES: () => DEFAULT_WRITING_STATUSES,
34
+ EXTENSIONS_DIR: () => EXTENSIONS_DIR,
35
+ EXTENSION_SCHEMA: () => EXTENSION_SCHEMA,
36
+ EXTENSION_SHARD: () => EXTENSION_SHARD,
34
37
  FLOW_SCHEMA: () => FLOW_SCHEMA,
35
38
  HANDOFF_SCHEMA: () => HANDOFF_SCHEMA,
39
+ MANIFEST_EXTENSION: () => MANIFEST_EXTENSION,
36
40
  PAD_AFTER_MAX: () => PAD_AFTER_MAX,
37
41
  PAD_AFTER_MIN: () => PAD_AFTER_MIN,
42
+ PROJECT_EXTENSION_STEM: () => PROJECT_EXTENSION_STEM,
38
43
  PROJECT_LOCALE_SCENE: () => PROJECT_LOCALE_SCENE,
39
44
  PROJECT_SCHEMA: () => PROJECT_SCHEMA,
40
45
  RERECORD_STATUS: () => RERECORD_STATUS,
@@ -44,6 +49,7 @@ __export(index_exports, {
44
49
  SAVE_VERSION: () => SAVE_VERSION,
45
50
  SPEAKER_PATTERN_KEY: () => SPEAKER_PATTERN_KEY,
46
51
  STRINGS_SCHEMA: () => STRINGS_SCHEMA,
52
+ UNRESOLVED_KEY: () => UNRESOLVED_KEY,
47
53
  castStringKey: () => castStringKey,
48
54
  deriveRecordingFolders: () => deriveRecordingFolders,
49
55
  effectiveGameId: () => effectiveGameId,
@@ -199,7 +205,7 @@ var DEFAULT_CAPTION_DELIMITERS = { open: "[", close: "]" };
199
205
  var DEFAULT_CAPTION_CHARACTER = "SFX";
200
206
  var SAVE_SCHEMA = "patter/save@0";
201
207
  var SAVE_VERSION = 3;
202
- var DEFAULT_LAYOUT = { flow: "scenes/", strings: "loc/", authoring: "authoring/" };
208
+ var DEFAULT_LAYOUT = { flow: "scenes/", strings: "loc/", authoring: "authoring/", extensions: "extensions/" };
203
209
  function projectLayout(project) {
204
210
  return { ...DEFAULT_LAYOUT, ...project.layout };
205
211
  }
@@ -207,6 +213,12 @@ var FLOW_SCHEMA = "patter/flow@0";
207
213
  var PROJECT_SCHEMA = "patter/project@0";
208
214
  var STRINGS_SCHEMA = "patter/strings@0";
209
215
  var AUTHORING_SCHEMA = "patter/authoring@0";
216
+ var EXTENSION_SCHEMA = "patter/extension@0";
217
+ var EXTENSION_SHARD = ".patterextension";
218
+ var PROJECT_EXTENSION_STEM = "_project";
219
+ var MANIFEST_EXTENSION = ".extension.json";
220
+ var EXTENSIONS_DIR = "extensions";
221
+ var UNRESOLVED_KEY = "unresolved";
210
222
  // Annotate the CommonJS export names for ESM import in node:
211
223
  0 && (module.exports = {
212
224
  AUTHORING_SCHEMA,
@@ -220,10 +232,15 @@ var AUTHORING_SCHEMA = "patter/authoring@0";
220
232
  DEFAULT_RECORDING_STATUSES,
221
233
  DEFAULT_SPEAKER_PATTERN,
222
234
  DEFAULT_WRITING_STATUSES,
235
+ EXTENSIONS_DIR,
236
+ EXTENSION_SCHEMA,
237
+ EXTENSION_SHARD,
223
238
  FLOW_SCHEMA,
224
239
  HANDOFF_SCHEMA,
240
+ MANIFEST_EXTENSION,
225
241
  PAD_AFTER_MAX,
226
242
  PAD_AFTER_MIN,
243
+ PROJECT_EXTENSION_STEM,
227
244
  PROJECT_LOCALE_SCENE,
228
245
  PROJECT_SCHEMA,
229
246
  RERECORD_STATUS,
@@ -233,6 +250,7 @@ var AUTHORING_SCHEMA = "patter/authoring@0";
233
250
  SAVE_VERSION,
234
251
  SPEAKER_PATTERN_KEY,
235
252
  STRINGS_SCHEMA,
253
+ UNRESOLVED_KEY,
236
254
  castStringKey,
237
255
  deriveRecordingFolders,
238
256
  effectiveGameId,
package/dist/index.d.cts CHANGED
@@ -33,6 +33,10 @@ interface LineBeat {
33
33
  * Never negative on a snippet's last line or text beat: the runtime clamps it to zero there. */
34
34
  padAfter?: number;
35
35
  gameData?: GameData;
36
+ /** Bundle only: this node's extension values, by extension id, raw as authored (editor-only fields
37
+ * stripped). Source keeps them in extension shards (`.patterextension`), never here. Absent when the
38
+ * node has none. */
39
+ extensions?: NodeExtensions;
36
40
  /** Author-defined freeform tags (#215): a cross-cutting label layer that travels to the runtime.
37
41
  * At runtime a beat's tags are the UNION of its own and every ancestor's (scene → block → group(s) →
38
42
  * snippet → beat). Each tag is letters/digits/symbols with NO comma and NO whitespace; deduped. */
@@ -53,6 +57,10 @@ interface TextBeat {
53
57
  * Never negative on a snippet's last line or text beat: the runtime clamps it to zero there. */
54
58
  padAfter?: number;
55
59
  gameData?: GameData;
60
+ /** Bundle only: this node's extension values, by extension id, raw as authored (editor-only fields
61
+ * stripped). Source keeps them in extension shards (`.patterextension`), never here. Absent when the
62
+ * node has none. */
63
+ extensions?: NodeExtensions;
56
64
  /** Author tags (#215). See LineBeat.tags. */
57
65
  tags?: string[];
58
66
  }
@@ -66,6 +74,10 @@ interface GameEventBeat {
66
74
  id: string;
67
75
  kind: "gameEvent";
68
76
  gameData?: GameData;
77
+ /** Bundle only: this node's extension values, by extension id, raw as authored (editor-only fields
78
+ * stripped). Source keeps them in extension shards (`.patterextension`), never here. Absent when the
79
+ * node has none. */
80
+ extensions?: NodeExtensions;
69
81
  /** Author tags (#215). See LineBeat.tags. */
70
82
  tags?: string[];
71
83
  }
@@ -400,21 +412,31 @@ interface BundleQualifier {
400
412
  * the bundle; this type states the resulting contract for anyone reading a bundle. */
401
413
  type BundleCastMember = Omit<CastMember, "notes" | "actor" | "gender">;
402
414
  /** A node TYPE that can carry author-defined gameData fields (the gameData schema, project-level).
403
- * Beat kinds map straight through: dialogue = `line`, narration = `text`, game event = `gameEvent`. */
404
- type GameDataNodeKind = "scene" | "block" | "snippet" | "line" | "text" | "gameEvent";
405
- /** The value type of a gameData field - the property-type vocabulary plus a multiline-text variant.
406
- * Drives the inspector's editor widget (text / textarea / number / toggle / enum dropdown). */
407
- type GameDataFieldType = "text" | "multiline" | "number" | "boolean" | "enum";
415
+ * Beat kinds map straight through: dialogue = `line`, narration = `text`, game event = `gameEvent`.
416
+ * `option` is a choice's option: a group that is a direct child of a `choice` group, whose gameData the
417
+ * host reads off the option `getChoices` hands it. Any other group's gameData stays undeclared. */
418
+ type GameDataNodeKind = "scene" | "block" | "snippet" | "line" | "text" | "gameEvent" | "option";
419
+ /** The value type of a gameData field: the family's one field-type list, shared with Storylets' card and
420
+ * outcome fields (patterkit design/family-parity-2026-10.md item 9, patter-qualities.md section 1). Drives
421
+ * the inspector's editor widget (text / textarea / number / toggle / enum dropdown / flags checklist /
422
+ * quality ladder). A `quality` value is a stage of the field's own ladder (`stages`), compared by position by
423
+ * a host, never by the engine. */
424
+ type GameDataFieldType = "text" | "multiline" | "number" | "boolean" | "enum" | "flags" | "quality";
408
425
  /** One author-defined custom field on a node type (host-integration metadata, NOT expression state). */
409
426
  interface GameDataField {
410
427
  /** Field key - also the key a node stores its value under in `gameData` (values are name-keyed). */
411
428
  name: string;
412
429
  type: GameDataFieldType;
413
430
  /** The value used when a node sets nothing. Storage is SPARSE - nodes hold only their overrides, and
414
- * a reader falls back to this default (so changing it here propagates to every node that didn't set it). */
431
+ * a reader falls back to this default (so changing it here propagates to every node that didn't set it).
432
+ * Optional in source: left out, the type's default holds (by the kernel's `defaultFor`), and the compiler
433
+ * writes it into the bundle, so a bundle's field always has one. An enum's may be `""`, none chosen. */
415
434
  default?: ScalarValue;
416
- /** Allowed values when `type` is "enum". */
435
+ /** Allowed values when `type` is "enum", or the flags a "flags" value may hold. */
417
436
  values?: string[];
437
+ /** A "quality" field's ordered ladder, by the family's one rule (at least two stages, none empty, none
438
+ * twice; `@wildwinter/expr`'s `qualityDeclarationProblems`). */
439
+ stages?: string[];
418
440
  /** Free-text description of what the field is for - shown as a rollover hint in the inspector. */
419
441
  purpose?: string;
420
442
  }
@@ -596,6 +618,7 @@ interface ProjectFile {
596
618
  flow?: string;
597
619
  strings?: string;
598
620
  authoring?: string;
621
+ extensions?: string;
599
622
  };
600
623
  /** `bundle`: the compiled `.patterc` output path (relative to the project root,
601
624
  * or absolute); default `../patter-dist/<project-file-stem>.patterc`, beside the project (spec §11).
@@ -640,6 +663,14 @@ interface ProjectFile {
640
663
  * Absent = `DEFAULT_PAD_AFTER`. */
641
664
  padAfterDefault?: number;
642
665
  gameDataFields?: GameDataFields;
666
+ /**
667
+ * The Patter extensions this project takes, by id (ruled 2026-10-18: the project opts in), each pinned to a
668
+ * version, with its settings (the host fields its claims present, by setting name). The manifest itself
669
+ * sits in `extensions/<id>.extension.json`, and the data in `.patterextension` shards. Authoring config:
670
+ * the bundle carries each one's compiled declarations and values, never this block. An extension named
671
+ * here whose manifest is missing is a warning, and its data is left untouched.
672
+ */
673
+ extensions?: Record<string, ProjectExtension>;
643
674
  /** Ordered writing-status ladder (not-done -> done); default `DEFAULT_WRITING_STATUSES`. */
644
675
  writingStatuses?: WritingStatusDecl[];
645
676
  /** Ordered recording-status ladder (not-done -> done); default `DEFAULT_RECORDING_STATUSES`. */
@@ -943,6 +974,8 @@ interface CompiledSnippet {
943
974
  onExit?: CompiledEffect[];
944
975
  padAfterDefault?: number;
945
976
  gameData?: GameData;
977
+ /** This node's extension values, by extension id (editor-only fields stripped). Absent when it has none. */
978
+ extensions?: NodeExtensions;
946
979
  tags?: string[];
947
980
  jump?: Jump;
948
981
  secretUntilEligible?: boolean;
@@ -963,6 +996,8 @@ interface CompiledGroup {
963
996
  children: Array<CompiledGroup | CompiledSnippet>;
964
997
  padAfterDefault?: number;
965
998
  gameData?: GameData;
999
+ /** This node's extension values, by extension id (editor-only fields stripped). Absent when it has none. */
1000
+ extensions?: NodeExtensions;
966
1001
  tags?: string[];
967
1002
  /** Option-position fields (spec §5) - only when a direct child of a `choice`. */
968
1003
  prompt?: PromptBeat;
@@ -981,6 +1016,8 @@ interface CompiledBlock {
981
1016
  children: Array<CompiledGroup | CompiledSnippet>;
982
1017
  padAfterDefault?: number;
983
1018
  gameData?: GameData;
1019
+ /** This node's extension values, by extension id (editor-only fields stripped). Absent when it has none. */
1020
+ extensions?: NodeExtensions;
984
1021
  tags?: string[];
985
1022
  }
986
1023
  interface CompiledScene {
@@ -991,6 +1028,8 @@ interface CompiledScene {
991
1028
  gameId?: string;
992
1029
  padAfterDefault?: number;
993
1030
  gameData?: GameData;
1031
+ /** This node's extension values, by extension id (editor-only fields stripped). Absent when it has none. */
1032
+ extensions?: NodeExtensions;
994
1033
  tags?: string[];
995
1034
  onEntry?: CompiledEffect[];
996
1035
  sceneProps?: PropertyDecl[];
@@ -1031,6 +1070,13 @@ interface Bundle {
1031
1070
  * one. Absent when none. */
1032
1071
  externalScopes?: string[];
1033
1072
  gameDataFields?: GameDataFields;
1073
+ /**
1074
+ * The extensions the project takes, by id: each one's version and its compiled declarations by node kind,
1075
+ * every default resolved (what `extensionDecls` reads), and the project node's own values. A node's values
1076
+ * sit on the node, under `extensions`. Absent when the project takes none, so a bundle without them is
1077
+ * byte for byte what it was.
1078
+ */
1079
+ extensions?: Record<string, BundleExtension>;
1034
1080
  scenes: Record<string, CompiledScene>;
1035
1081
  /** locale -> (beatId -> text). In "embedded" localisation this carries every included locale; in "ids"
1036
1082
  * it is EMPTY (the runtime emits beat IDs), unless `localisation.sourceDebug` embedded the source locale
@@ -1203,24 +1249,109 @@ declare const DEFAULT_LAYOUT: {
1203
1249
  readonly flow: "scenes/";
1204
1250
  readonly strings: "loc/";
1205
1251
  readonly authoring: "authoring/";
1252
+ readonly extensions: "extensions/";
1206
1253
  };
1207
- /** A project's layout with the defaults filled in: the one place they are written down (the loader,
1208
- * validate, localisation, the scene scaffold, and Patterpad each had a copy). */
1209
- declare function projectLayout(project: {
1210
- layout?: {
1211
- flow?: string;
1212
- strings?: string;
1213
- authoring?: string;
1214
- };
1215
- }): {
1254
+ /** Where a project keeps each kind of shard, relative to the project root (`ProjectFile.layout`). */
1255
+ interface ProjectLayout {
1216
1256
  flow: string;
1217
1257
  strings: string;
1218
1258
  authoring: string;
1219
- };
1259
+ extensions: string;
1260
+ }
1261
+ /** A project's layout with the defaults filled in: the one place they are written down (the loader,
1262
+ * validate, localisation, the scene scaffold, and Patterpad each had a copy). */
1263
+ declare function projectLayout(project: {
1264
+ layout?: Partial<ProjectLayout>;
1265
+ }): ProjectLayout;
1220
1266
  /** The source shards' schema tags (spec §10), written by every tool that makes one. */
1221
1267
  declare const FLOW_SCHEMA = "patter/flow@0";
1222
1268
  declare const PROJECT_SCHEMA = "patter/project@0";
1223
1269
  declare const STRINGS_SCHEMA = "patter/strings@0";
1224
1270
  declare const AUTHORING_SCHEMA = "patter/authoring@0";
1271
+ /** An extension data shard's schema. */
1272
+ declare const EXTENSION_SCHEMA = "patter/extension@0";
1273
+ /** An extension data shard's file suffix: one per extension per scene
1274
+ * (`<layout.extensions>/<extension id>/<scene file stem>.patterextension`), and one for project-level
1275
+ * data (`<layout.extensions>/<extension id>/_project.patterextension`). Its own shard because a tool's
1276
+ * pass and a writer's edit must not collide on one file. */
1277
+ declare const EXTENSION_SHARD = ".patterextension";
1278
+ /** The stem of an extension's project-level data shard, beside its scene shards. */
1279
+ declare const PROJECT_EXTENSION_STEM = "_project";
1280
+ /**
1281
+ * An extension's manifest, as a project keeps it: `extensions/<id>.extension.json` at the project root
1282
+ * (`@wildwinter/toolkit/extensions`' `MANIFEST_FILE_SUFFIX`). Not a shard: JSON the extension ships, never
1283
+ * written by the tools nor put in the canonical form, but source all the same, so a pack carries it and a
1284
+ * project compiles from it.
1285
+ */
1286
+ declare const MANIFEST_EXTENSION = ".extension.json";
1287
+ /** Where a project keeps its extensions' manifests, relative to the root. Fixed, unlike the data shards'
1288
+ * folder, so a merge driver handed one shard's path can always find the manifest. */
1289
+ declare const EXTENSIONS_DIR = "extensions";
1290
+ /**
1291
+ * The extensions a project takes (`ProjectFile.extensions`), one each: the version it is pinned to and its
1292
+ * settings (the host fields its claims present, by setting name).
1293
+ */
1294
+ interface ProjectExtension {
1295
+ version: string;
1296
+ settings?: Record<string, string>;
1297
+ }
1298
+ /**
1299
+ * One extension's data for one scene, or for the project (`scene: "@project"`).
1300
+ *
1301
+ * `entries` is the extension's, by node kind (`scene`, `block`, `snippet`, `line`, `text`, `gameEvent`,
1302
+ * `option`, `project`) and then node id, and the canonical form never reorders anything inside it: its
1303
+ * lists are the extension's, in the extension's order. The values are raw, as a tool wrote them; the
1304
+ * extension's manifest says their types.
1305
+ */
1306
+ interface ExtensionShard {
1307
+ schema: typeof EXTENSION_SCHEMA;
1308
+ /** The extension's id: the same as the folder the shard sits in. */
1309
+ extension: string;
1310
+ /** The scene this shard's entries belong to, or `PROJECT_LOCALE_SCENE` (`"@project"`) for the project's. */
1311
+ scene: string;
1312
+ entries: Record<string, Record<string, Record<string, unknown>>>;
1313
+ }
1314
+ /**
1315
+ * The reserved key a merged shard carries an unresolved merge under, in every shard type
1316
+ * (`@wildwinter/toolkit/extensions`' `UNRESOLVED_KEY`, ruled 2026-10-18 in both formats): the clashes, each
1317
+ * with its path, its kind and the three values, ours being what the shard now holds. Validation, export
1318
+ * and pack refuse a shard carrying it; resolving removes it.
1319
+ */
1320
+ declare const UNRESOLVED_KEY = "unresolved";
1321
+ /** One extension as the bundle carries it. */
1322
+ interface BundleExtension {
1323
+ version: string;
1324
+ /** Compiled declarations by node kind: no editor-only fields, no editor words. */
1325
+ fields: Record<string, ExtensionFieldDecl[]>;
1326
+ /** The project node's values, raw, when it has any. */
1327
+ project?: Record<string, unknown>;
1328
+ }
1329
+ /**
1330
+ * A compiled extension field declaration (`@wildwinter/toolkit/extensions`' FieldDecl, less the editor's
1331
+ * words). Its `default` is always present. Structured types carry their parts: `fields` for a record,
1332
+ * `key` and `of` for a map, `of` and `id` for a list, `to` for a reference.
1333
+ */
1334
+ interface ExtensionFieldDecl {
1335
+ name: string;
1336
+ type: string;
1337
+ default: unknown;
1338
+ values?: string[];
1339
+ stages?: string[];
1340
+ to?: string;
1341
+ fields?: ExtensionFieldDecl[];
1342
+ key?: {
1343
+ type: string;
1344
+ to?: string;
1345
+ };
1346
+ of?: Omit<ExtensionFieldDecl, "name" | "default"> & {
1347
+ default?: unknown;
1348
+ };
1349
+ id?: string;
1350
+ min?: number;
1351
+ max?: number;
1352
+ required?: boolean;
1353
+ }
1354
+ /** A node's extension values in a bundle: extension id -> field -> value, raw. */
1355
+ type NodeExtensions = Record<string, Record<string, unknown>>;
1225
1356
 
1226
- export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleQualifier, COMMON_GENDERS, type CaptionDelimiters, type CastMember, type Comment, type CommentMessage, type CommentRange, type CompiledBlock, type CompiledEffect, type CompiledGroup, type CompiledScene, type CompiledSnippet, type CoverageDriver, DEFAULT_CAPTION_CHARACTER, DEFAULT_CAPTION_DELIMITERS, DEFAULT_DOCUMENTATION_CLASSES, DEFAULT_LAYOUT, DEFAULT_PAD_AFTER, DEFAULT_QUALIFIERS, DEFAULT_RECORDING_STATUSES, DEFAULT_SPEAKER_PATTERN, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type FlowSnapshotV2, type FlowState, type FlowValues, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type GrammaticalGender, type Group, HANDOFF_SCHEMA, type HandoffFile, type HandoffImport, type HandoffLine, type HandoffRow, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectFile, type PromptBeat, type PropertyDecl, type PropertyType, RERECORD_STATUS, RERECORD_STATUS_DECL, type RecordingFolder, type RecordingStatusDecl, SAVE_SCHEMA, SAVE_VERSION, SPEAKER_PATTERN_KEY, STRINGS_SCHEMA, type SaveEnvelope, type SaveGame, type SaveGameV2, type SavedChoice, type SavedChoiceOption, type SavedChoicePrompt, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SelectorSnapshot, type SequenceOptions, type Snippet, type SpeakerQualifier, type StackFrame, type Suggestion, type SuggestionSource, type TextBeat, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, formatSpeakerLabel, gameIdify, isContentlessBeat, isValidGameId, projectLayout, projectQualifiers, qualifierStringKey, speakerLabelKey, usedQualifiers, usedSpeakerPairs, walkNodes };
1357
+ export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleExtension, type BundleQualifier, COMMON_GENDERS, type CaptionDelimiters, type CastMember, type Comment, type CommentMessage, type CommentRange, type CompiledBlock, type CompiledEffect, type CompiledGroup, type CompiledScene, type CompiledSnippet, type CoverageDriver, DEFAULT_CAPTION_CHARACTER, DEFAULT_CAPTION_DELIMITERS, DEFAULT_DOCUMENTATION_CLASSES, DEFAULT_LAYOUT, DEFAULT_PAD_AFTER, DEFAULT_QUALIFIERS, DEFAULT_RECORDING_STATUSES, DEFAULT_SPEAKER_PATTERN, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, EXTENSIONS_DIR, EXTENSION_SCHEMA, EXTENSION_SHARD, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, type ExtensionFieldDecl, type ExtensionShard, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type FlowSnapshotV2, type FlowState, type FlowValues, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type GrammaticalGender, type Group, HANDOFF_SCHEMA, type HandoffFile, type HandoffImport, type HandoffLine, type HandoffRow, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, MANIFEST_EXTENSION, type NodeExtensions, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_EXTENSION_STEM, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectExtension, type ProjectFile, type ProjectLayout, type PromptBeat, type PropertyDecl, type PropertyType, RERECORD_STATUS, RERECORD_STATUS_DECL, type RecordingFolder, type RecordingStatusDecl, SAVE_SCHEMA, SAVE_VERSION, SPEAKER_PATTERN_KEY, STRINGS_SCHEMA, type SaveEnvelope, type SaveGame, type SaveGameV2, type SavedChoice, type SavedChoiceOption, type SavedChoicePrompt, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SelectorSnapshot, type SequenceOptions, type Snippet, type SpeakerQualifier, type StackFrame, type Suggestion, type SuggestionSource, type TextBeat, UNRESOLVED_KEY, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, formatSpeakerLabel, gameIdify, isContentlessBeat, isValidGameId, projectLayout, projectQualifiers, qualifierStringKey, speakerLabelKey, usedQualifiers, usedSpeakerPairs, walkNodes };
package/dist/index.d.ts CHANGED
@@ -33,6 +33,10 @@ interface LineBeat {
33
33
  * Never negative on a snippet's last line or text beat: the runtime clamps it to zero there. */
34
34
  padAfter?: number;
35
35
  gameData?: GameData;
36
+ /** Bundle only: this node's extension values, by extension id, raw as authored (editor-only fields
37
+ * stripped). Source keeps them in extension shards (`.patterextension`), never here. Absent when the
38
+ * node has none. */
39
+ extensions?: NodeExtensions;
36
40
  /** Author-defined freeform tags (#215): a cross-cutting label layer that travels to the runtime.
37
41
  * At runtime a beat's tags are the UNION of its own and every ancestor's (scene → block → group(s) →
38
42
  * snippet → beat). Each tag is letters/digits/symbols with NO comma and NO whitespace; deduped. */
@@ -53,6 +57,10 @@ interface TextBeat {
53
57
  * Never negative on a snippet's last line or text beat: the runtime clamps it to zero there. */
54
58
  padAfter?: number;
55
59
  gameData?: GameData;
60
+ /** Bundle only: this node's extension values, by extension id, raw as authored (editor-only fields
61
+ * stripped). Source keeps them in extension shards (`.patterextension`), never here. Absent when the
62
+ * node has none. */
63
+ extensions?: NodeExtensions;
56
64
  /** Author tags (#215). See LineBeat.tags. */
57
65
  tags?: string[];
58
66
  }
@@ -66,6 +74,10 @@ interface GameEventBeat {
66
74
  id: string;
67
75
  kind: "gameEvent";
68
76
  gameData?: GameData;
77
+ /** Bundle only: this node's extension values, by extension id, raw as authored (editor-only fields
78
+ * stripped). Source keeps them in extension shards (`.patterextension`), never here. Absent when the
79
+ * node has none. */
80
+ extensions?: NodeExtensions;
69
81
  /** Author tags (#215). See LineBeat.tags. */
70
82
  tags?: string[];
71
83
  }
@@ -400,21 +412,31 @@ interface BundleQualifier {
400
412
  * the bundle; this type states the resulting contract for anyone reading a bundle. */
401
413
  type BundleCastMember = Omit<CastMember, "notes" | "actor" | "gender">;
402
414
  /** A node TYPE that can carry author-defined gameData fields (the gameData schema, project-level).
403
- * Beat kinds map straight through: dialogue = `line`, narration = `text`, game event = `gameEvent`. */
404
- type GameDataNodeKind = "scene" | "block" | "snippet" | "line" | "text" | "gameEvent";
405
- /** The value type of a gameData field - the property-type vocabulary plus a multiline-text variant.
406
- * Drives the inspector's editor widget (text / textarea / number / toggle / enum dropdown). */
407
- type GameDataFieldType = "text" | "multiline" | "number" | "boolean" | "enum";
415
+ * Beat kinds map straight through: dialogue = `line`, narration = `text`, game event = `gameEvent`.
416
+ * `option` is a choice's option: a group that is a direct child of a `choice` group, whose gameData the
417
+ * host reads off the option `getChoices` hands it. Any other group's gameData stays undeclared. */
418
+ type GameDataNodeKind = "scene" | "block" | "snippet" | "line" | "text" | "gameEvent" | "option";
419
+ /** The value type of a gameData field: the family's one field-type list, shared with Storylets' card and
420
+ * outcome fields (patterkit design/family-parity-2026-10.md item 9, patter-qualities.md section 1). Drives
421
+ * the inspector's editor widget (text / textarea / number / toggle / enum dropdown / flags checklist /
422
+ * quality ladder). A `quality` value is a stage of the field's own ladder (`stages`), compared by position by
423
+ * a host, never by the engine. */
424
+ type GameDataFieldType = "text" | "multiline" | "number" | "boolean" | "enum" | "flags" | "quality";
408
425
  /** One author-defined custom field on a node type (host-integration metadata, NOT expression state). */
409
426
  interface GameDataField {
410
427
  /** Field key - also the key a node stores its value under in `gameData` (values are name-keyed). */
411
428
  name: string;
412
429
  type: GameDataFieldType;
413
430
  /** The value used when a node sets nothing. Storage is SPARSE - nodes hold only their overrides, and
414
- * a reader falls back to this default (so changing it here propagates to every node that didn't set it). */
431
+ * a reader falls back to this default (so changing it here propagates to every node that didn't set it).
432
+ * Optional in source: left out, the type's default holds (by the kernel's `defaultFor`), and the compiler
433
+ * writes it into the bundle, so a bundle's field always has one. An enum's may be `""`, none chosen. */
415
434
  default?: ScalarValue;
416
- /** Allowed values when `type` is "enum". */
435
+ /** Allowed values when `type` is "enum", or the flags a "flags" value may hold. */
417
436
  values?: string[];
437
+ /** A "quality" field's ordered ladder, by the family's one rule (at least two stages, none empty, none
438
+ * twice; `@wildwinter/expr`'s `qualityDeclarationProblems`). */
439
+ stages?: string[];
418
440
  /** Free-text description of what the field is for - shown as a rollover hint in the inspector. */
419
441
  purpose?: string;
420
442
  }
@@ -596,6 +618,7 @@ interface ProjectFile {
596
618
  flow?: string;
597
619
  strings?: string;
598
620
  authoring?: string;
621
+ extensions?: string;
599
622
  };
600
623
  /** `bundle`: the compiled `.patterc` output path (relative to the project root,
601
624
  * or absolute); default `../patter-dist/<project-file-stem>.patterc`, beside the project (spec §11).
@@ -640,6 +663,14 @@ interface ProjectFile {
640
663
  * Absent = `DEFAULT_PAD_AFTER`. */
641
664
  padAfterDefault?: number;
642
665
  gameDataFields?: GameDataFields;
666
+ /**
667
+ * The Patter extensions this project takes, by id (ruled 2026-10-18: the project opts in), each pinned to a
668
+ * version, with its settings (the host fields its claims present, by setting name). The manifest itself
669
+ * sits in `extensions/<id>.extension.json`, and the data in `.patterextension` shards. Authoring config:
670
+ * the bundle carries each one's compiled declarations and values, never this block. An extension named
671
+ * here whose manifest is missing is a warning, and its data is left untouched.
672
+ */
673
+ extensions?: Record<string, ProjectExtension>;
643
674
  /** Ordered writing-status ladder (not-done -> done); default `DEFAULT_WRITING_STATUSES`. */
644
675
  writingStatuses?: WritingStatusDecl[];
645
676
  /** Ordered recording-status ladder (not-done -> done); default `DEFAULT_RECORDING_STATUSES`. */
@@ -943,6 +974,8 @@ interface CompiledSnippet {
943
974
  onExit?: CompiledEffect[];
944
975
  padAfterDefault?: number;
945
976
  gameData?: GameData;
977
+ /** This node's extension values, by extension id (editor-only fields stripped). Absent when it has none. */
978
+ extensions?: NodeExtensions;
946
979
  tags?: string[];
947
980
  jump?: Jump;
948
981
  secretUntilEligible?: boolean;
@@ -963,6 +996,8 @@ interface CompiledGroup {
963
996
  children: Array<CompiledGroup | CompiledSnippet>;
964
997
  padAfterDefault?: number;
965
998
  gameData?: GameData;
999
+ /** This node's extension values, by extension id (editor-only fields stripped). Absent when it has none. */
1000
+ extensions?: NodeExtensions;
966
1001
  tags?: string[];
967
1002
  /** Option-position fields (spec §5) - only when a direct child of a `choice`. */
968
1003
  prompt?: PromptBeat;
@@ -981,6 +1016,8 @@ interface CompiledBlock {
981
1016
  children: Array<CompiledGroup | CompiledSnippet>;
982
1017
  padAfterDefault?: number;
983
1018
  gameData?: GameData;
1019
+ /** This node's extension values, by extension id (editor-only fields stripped). Absent when it has none. */
1020
+ extensions?: NodeExtensions;
984
1021
  tags?: string[];
985
1022
  }
986
1023
  interface CompiledScene {
@@ -991,6 +1028,8 @@ interface CompiledScene {
991
1028
  gameId?: string;
992
1029
  padAfterDefault?: number;
993
1030
  gameData?: GameData;
1031
+ /** This node's extension values, by extension id (editor-only fields stripped). Absent when it has none. */
1032
+ extensions?: NodeExtensions;
994
1033
  tags?: string[];
995
1034
  onEntry?: CompiledEffect[];
996
1035
  sceneProps?: PropertyDecl[];
@@ -1031,6 +1070,13 @@ interface Bundle {
1031
1070
  * one. Absent when none. */
1032
1071
  externalScopes?: string[];
1033
1072
  gameDataFields?: GameDataFields;
1073
+ /**
1074
+ * The extensions the project takes, by id: each one's version and its compiled declarations by node kind,
1075
+ * every default resolved (what `extensionDecls` reads), and the project node's own values. A node's values
1076
+ * sit on the node, under `extensions`. Absent when the project takes none, so a bundle without them is
1077
+ * byte for byte what it was.
1078
+ */
1079
+ extensions?: Record<string, BundleExtension>;
1034
1080
  scenes: Record<string, CompiledScene>;
1035
1081
  /** locale -> (beatId -> text). In "embedded" localisation this carries every included locale; in "ids"
1036
1082
  * it is EMPTY (the runtime emits beat IDs), unless `localisation.sourceDebug` embedded the source locale
@@ -1203,24 +1249,109 @@ declare const DEFAULT_LAYOUT: {
1203
1249
  readonly flow: "scenes/";
1204
1250
  readonly strings: "loc/";
1205
1251
  readonly authoring: "authoring/";
1252
+ readonly extensions: "extensions/";
1206
1253
  };
1207
- /** A project's layout with the defaults filled in: the one place they are written down (the loader,
1208
- * validate, localisation, the scene scaffold, and Patterpad each had a copy). */
1209
- declare function projectLayout(project: {
1210
- layout?: {
1211
- flow?: string;
1212
- strings?: string;
1213
- authoring?: string;
1214
- };
1215
- }): {
1254
+ /** Where a project keeps each kind of shard, relative to the project root (`ProjectFile.layout`). */
1255
+ interface ProjectLayout {
1216
1256
  flow: string;
1217
1257
  strings: string;
1218
1258
  authoring: string;
1219
- };
1259
+ extensions: string;
1260
+ }
1261
+ /** A project's layout with the defaults filled in: the one place they are written down (the loader,
1262
+ * validate, localisation, the scene scaffold, and Patterpad each had a copy). */
1263
+ declare function projectLayout(project: {
1264
+ layout?: Partial<ProjectLayout>;
1265
+ }): ProjectLayout;
1220
1266
  /** The source shards' schema tags (spec §10), written by every tool that makes one. */
1221
1267
  declare const FLOW_SCHEMA = "patter/flow@0";
1222
1268
  declare const PROJECT_SCHEMA = "patter/project@0";
1223
1269
  declare const STRINGS_SCHEMA = "patter/strings@0";
1224
1270
  declare const AUTHORING_SCHEMA = "patter/authoring@0";
1271
+ /** An extension data shard's schema. */
1272
+ declare const EXTENSION_SCHEMA = "patter/extension@0";
1273
+ /** An extension data shard's file suffix: one per extension per scene
1274
+ * (`<layout.extensions>/<extension id>/<scene file stem>.patterextension`), and one for project-level
1275
+ * data (`<layout.extensions>/<extension id>/_project.patterextension`). Its own shard because a tool's
1276
+ * pass and a writer's edit must not collide on one file. */
1277
+ declare const EXTENSION_SHARD = ".patterextension";
1278
+ /** The stem of an extension's project-level data shard, beside its scene shards. */
1279
+ declare const PROJECT_EXTENSION_STEM = "_project";
1280
+ /**
1281
+ * An extension's manifest, as a project keeps it: `extensions/<id>.extension.json` at the project root
1282
+ * (`@wildwinter/toolkit/extensions`' `MANIFEST_FILE_SUFFIX`). Not a shard: JSON the extension ships, never
1283
+ * written by the tools nor put in the canonical form, but source all the same, so a pack carries it and a
1284
+ * project compiles from it.
1285
+ */
1286
+ declare const MANIFEST_EXTENSION = ".extension.json";
1287
+ /** Where a project keeps its extensions' manifests, relative to the root. Fixed, unlike the data shards'
1288
+ * folder, so a merge driver handed one shard's path can always find the manifest. */
1289
+ declare const EXTENSIONS_DIR = "extensions";
1290
+ /**
1291
+ * The extensions a project takes (`ProjectFile.extensions`), one each: the version it is pinned to and its
1292
+ * settings (the host fields its claims present, by setting name).
1293
+ */
1294
+ interface ProjectExtension {
1295
+ version: string;
1296
+ settings?: Record<string, string>;
1297
+ }
1298
+ /**
1299
+ * One extension's data for one scene, or for the project (`scene: "@project"`).
1300
+ *
1301
+ * `entries` is the extension's, by node kind (`scene`, `block`, `snippet`, `line`, `text`, `gameEvent`,
1302
+ * `option`, `project`) and then node id, and the canonical form never reorders anything inside it: its
1303
+ * lists are the extension's, in the extension's order. The values are raw, as a tool wrote them; the
1304
+ * extension's manifest says their types.
1305
+ */
1306
+ interface ExtensionShard {
1307
+ schema: typeof EXTENSION_SCHEMA;
1308
+ /** The extension's id: the same as the folder the shard sits in. */
1309
+ extension: string;
1310
+ /** The scene this shard's entries belong to, or `PROJECT_LOCALE_SCENE` (`"@project"`) for the project's. */
1311
+ scene: string;
1312
+ entries: Record<string, Record<string, Record<string, unknown>>>;
1313
+ }
1314
+ /**
1315
+ * The reserved key a merged shard carries an unresolved merge under, in every shard type
1316
+ * (`@wildwinter/toolkit/extensions`' `UNRESOLVED_KEY`, ruled 2026-10-18 in both formats): the clashes, each
1317
+ * with its path, its kind and the three values, ours being what the shard now holds. Validation, export
1318
+ * and pack refuse a shard carrying it; resolving removes it.
1319
+ */
1320
+ declare const UNRESOLVED_KEY = "unresolved";
1321
+ /** One extension as the bundle carries it. */
1322
+ interface BundleExtension {
1323
+ version: string;
1324
+ /** Compiled declarations by node kind: no editor-only fields, no editor words. */
1325
+ fields: Record<string, ExtensionFieldDecl[]>;
1326
+ /** The project node's values, raw, when it has any. */
1327
+ project?: Record<string, unknown>;
1328
+ }
1329
+ /**
1330
+ * A compiled extension field declaration (`@wildwinter/toolkit/extensions`' FieldDecl, less the editor's
1331
+ * words). Its `default` is always present. Structured types carry their parts: `fields` for a record,
1332
+ * `key` and `of` for a map, `of` and `id` for a list, `to` for a reference.
1333
+ */
1334
+ interface ExtensionFieldDecl {
1335
+ name: string;
1336
+ type: string;
1337
+ default: unknown;
1338
+ values?: string[];
1339
+ stages?: string[];
1340
+ to?: string;
1341
+ fields?: ExtensionFieldDecl[];
1342
+ key?: {
1343
+ type: string;
1344
+ to?: string;
1345
+ };
1346
+ of?: Omit<ExtensionFieldDecl, "name" | "default"> & {
1347
+ default?: unknown;
1348
+ };
1349
+ id?: string;
1350
+ min?: number;
1351
+ max?: number;
1352
+ required?: boolean;
1353
+ }
1354
+ /** A node's extension values in a bundle: extension id -> field -> value, raw. */
1355
+ type NodeExtensions = Record<string, Record<string, unknown>>;
1225
1356
 
1226
- export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleQualifier, COMMON_GENDERS, type CaptionDelimiters, type CastMember, type Comment, type CommentMessage, type CommentRange, type CompiledBlock, type CompiledEffect, type CompiledGroup, type CompiledScene, type CompiledSnippet, type CoverageDriver, DEFAULT_CAPTION_CHARACTER, DEFAULT_CAPTION_DELIMITERS, DEFAULT_DOCUMENTATION_CLASSES, DEFAULT_LAYOUT, DEFAULT_PAD_AFTER, DEFAULT_QUALIFIERS, DEFAULT_RECORDING_STATUSES, DEFAULT_SPEAKER_PATTERN, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type FlowSnapshotV2, type FlowState, type FlowValues, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type GrammaticalGender, type Group, HANDOFF_SCHEMA, type HandoffFile, type HandoffImport, type HandoffLine, type HandoffRow, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectFile, type PromptBeat, type PropertyDecl, type PropertyType, RERECORD_STATUS, RERECORD_STATUS_DECL, type RecordingFolder, type RecordingStatusDecl, SAVE_SCHEMA, SAVE_VERSION, SPEAKER_PATTERN_KEY, STRINGS_SCHEMA, type SaveEnvelope, type SaveGame, type SaveGameV2, type SavedChoice, type SavedChoiceOption, type SavedChoicePrompt, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SelectorSnapshot, type SequenceOptions, type Snippet, type SpeakerQualifier, type StackFrame, type Suggestion, type SuggestionSource, type TextBeat, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, formatSpeakerLabel, gameIdify, isContentlessBeat, isValidGameId, projectLayout, projectQualifiers, qualifierStringKey, speakerLabelKey, usedQualifiers, usedSpeakerPairs, walkNodes };
1357
+ export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleExtension, type BundleQualifier, COMMON_GENDERS, type CaptionDelimiters, type CastMember, type Comment, type CommentMessage, type CommentRange, type CompiledBlock, type CompiledEffect, type CompiledGroup, type CompiledScene, type CompiledSnippet, type CoverageDriver, DEFAULT_CAPTION_CHARACTER, DEFAULT_CAPTION_DELIMITERS, DEFAULT_DOCUMENTATION_CLASSES, DEFAULT_LAYOUT, DEFAULT_PAD_AFTER, DEFAULT_QUALIFIERS, DEFAULT_RECORDING_STATUSES, DEFAULT_SPEAKER_PATTERN, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, EXTENSIONS_DIR, EXTENSION_SCHEMA, EXTENSION_SHARD, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, type ExtensionFieldDecl, type ExtensionShard, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type FlowSnapshotV2, type FlowState, type FlowValues, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type GrammaticalGender, type Group, HANDOFF_SCHEMA, type HandoffFile, type HandoffImport, type HandoffLine, type HandoffRow, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, MANIFEST_EXTENSION, type NodeExtensions, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_EXTENSION_STEM, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectExtension, type ProjectFile, type ProjectLayout, type PromptBeat, type PropertyDecl, type PropertyType, RERECORD_STATUS, RERECORD_STATUS_DECL, type RecordingFolder, type RecordingStatusDecl, SAVE_SCHEMA, SAVE_VERSION, SPEAKER_PATTERN_KEY, STRINGS_SCHEMA, type SaveEnvelope, type SaveGame, type SaveGameV2, type SavedChoice, type SavedChoiceOption, type SavedChoicePrompt, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SelectorSnapshot, type SequenceOptions, type Snippet, type SpeakerQualifier, type StackFrame, type Suggestion, type SuggestionSource, type TextBeat, UNRESOLVED_KEY, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, formatSpeakerLabel, gameIdify, isContentlessBeat, isValidGameId, projectLayout, projectQualifiers, qualifierStringKey, speakerLabelKey, usedQualifiers, usedSpeakerPairs, walkNodes };
package/dist/index.js CHANGED
@@ -140,7 +140,7 @@ var DEFAULT_CAPTION_DELIMITERS = { open: "[", close: "]" };
140
140
  var DEFAULT_CAPTION_CHARACTER = "SFX";
141
141
  var SAVE_SCHEMA = "patter/save@0";
142
142
  var SAVE_VERSION = 3;
143
- var DEFAULT_LAYOUT = { flow: "scenes/", strings: "loc/", authoring: "authoring/" };
143
+ var DEFAULT_LAYOUT = { flow: "scenes/", strings: "loc/", authoring: "authoring/", extensions: "extensions/" };
144
144
  function projectLayout(project) {
145
145
  return { ...DEFAULT_LAYOUT, ...project.layout };
146
146
  }
@@ -148,6 +148,12 @@ var FLOW_SCHEMA = "patter/flow@0";
148
148
  var PROJECT_SCHEMA = "patter/project@0";
149
149
  var STRINGS_SCHEMA = "patter/strings@0";
150
150
  var AUTHORING_SCHEMA = "patter/authoring@0";
151
+ var EXTENSION_SCHEMA = "patter/extension@0";
152
+ var EXTENSION_SHARD = ".patterextension";
153
+ var PROJECT_EXTENSION_STEM = "_project";
154
+ var MANIFEST_EXTENSION = ".extension.json";
155
+ var EXTENSIONS_DIR = "extensions";
156
+ var UNRESOLVED_KEY = "unresolved";
151
157
  export {
152
158
  AUTHORING_SCHEMA,
153
159
  COMMON_GENDERS,
@@ -160,10 +166,15 @@ export {
160
166
  DEFAULT_RECORDING_STATUSES,
161
167
  DEFAULT_SPEAKER_PATTERN,
162
168
  DEFAULT_WRITING_STATUSES,
169
+ EXTENSIONS_DIR,
170
+ EXTENSION_SCHEMA,
171
+ EXTENSION_SHARD,
163
172
  FLOW_SCHEMA,
164
173
  HANDOFF_SCHEMA,
174
+ MANIFEST_EXTENSION,
165
175
  PAD_AFTER_MAX,
166
176
  PAD_AFTER_MIN,
177
+ PROJECT_EXTENSION_STEM,
167
178
  PROJECT_LOCALE_SCENE,
168
179
  PROJECT_SCHEMA,
169
180
  RERECORD_STATUS,
@@ -173,6 +184,7 @@ export {
173
184
  SAVE_VERSION,
174
185
  SPEAKER_PATTERN_KEY,
175
186
  STRINGS_SCHEMA,
187
+ UNRESOLVED_KEY,
176
188
  castStringKey,
177
189
  deriveRecordingFolders,
178
190
  effectiveGameId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/model",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Patter data-model types: source (scene/block/group/snippet/beat/jump), project, locale, authoring. The shape source-of-truth.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,6 +34,6 @@
34
34
  "build": "tsup src/index.ts --format esm,cjs --dts --clean"
35
35
  },
36
36
  "dependencies": {
37
- "@wildwinter/expr": "^0.5.0"
37
+ "@wildwinter/expr": "^0.6.0"
38
38
  }
39
39
  }