@storylet-studio/model 0.1.0 → 0.3.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
@@ -390,6 +390,15 @@ interface PropertyDecl {
390
390
  * declaration, and inserting a stage mid-ladder is the design's whole point.
391
391
  */
392
392
  stages?: string[];
393
+ /**
394
+ * `@world` only. `false` makes the property read-only TO THE STORY: a
395
+ * condition may read it, an outcome that writes it is a compile error. The
396
+ * game still moves it through its resolver; this is the story's statement
397
+ * of intent, not the game's policy. Mirrors Patter's `HostScopeDecl.writable`
398
+ * name for name (Reboot.md 10, ruled 2026-09-03). Ignored on every other
399
+ * scope. Absent = writable.
400
+ */
401
+ writable?: boolean;
393
402
  /**
394
403
  * The sharing axis (design/flows.md, Patter's flag adopted): is this
395
404
  * property's value one world value across all flows, or a copy per flow?
@@ -400,6 +409,25 @@ interface PropertyDecl {
400
409
  * the game's own state, always engine-level, never per-flow.
401
410
  */
402
411
  shared?: boolean;
412
+ /**
413
+ * The durability axis (design/engine-server.md 4.2), valid wherever `shared`
414
+ * is valid and orthogonal to it: `shared` says whose value this is WITHIN a
415
+ * run, `durable` says whether the value survives the run at all. A durable
416
+ * shared property is the installation's memory ("trolls defeated since we
417
+ * opened"); a durable per-flow one is the player's pocket (visits,
418
+ * allegiance, what they earned).
419
+ *
420
+ * INERT TO THE RUNTIME. The engine partitions by `shared` alone and never
421
+ * reads this. Durability is what the SERVER does at a run boundary: it reads
422
+ * the declarations, lifts the durable values out of the partitions before the
423
+ * world restarts, and writes them back into the fresh engine afterwards,
424
+ * entirely through `getProperty` / `setProperty`.
425
+ *
426
+ * On a `@world` declaration the flag is a validation error, for the reason
427
+ * `shared` is: @world is the game's own state, and how long the game keeps it
428
+ * is the game's business.
429
+ */
430
+ durable?: boolean;
403
431
  purpose?: string;
404
432
  }
405
433
  /** A card-template field (box-defined). Data for the host; the engine never
@@ -424,6 +452,32 @@ declare function effectiveGameId(entity: {
424
452
  title?: string;
425
453
  id: string;
426
454
  }): string;
455
+ /** The three answers a value address needs, all derived from the bundle. */
456
+ interface ValueAddresses {
457
+ /** Tag internal id -> the owner segment an address PRINTS for it. */
458
+ print: Map<string, string>;
459
+ /** Every owner segment a value address ACCEPTS -> the tag's internal id.
460
+ * Holds the qualified form for every tag and the short form only for a
461
+ * gameId no other tag shares. */
462
+ accept: Map<string, string>;
463
+ /** A tag gameId more than one box uses -> its qualified forms, in bundle
464
+ * order. Empty for the overwhelming majority of projects, and what a
465
+ * refusal lists. */
466
+ repeated: Map<string, string[]>;
467
+ }
468
+ /** The owner segment of every tag in the bundle, both ways round. */
469
+ declare function valueAddresses(bundle: {
470
+ boxes: readonly {
471
+ id: string;
472
+ gameId?: string;
473
+ title?: string;
474
+ tagGroups: readonly TagGroup[];
475
+ }[];
476
+ }): ValueAddresses;
477
+ /** What an ambiguous short-form value address is told: the candidates, in
478
+ * full, because "that names two tags" without them leaves a host reading a
479
+ * bundle it did not write to find out which boxes. */
480
+ declare function ambiguousValueAddressMessage(segment: string, name: string, candidates: readonly string[]): string;
427
481
  /**
428
482
  * The first free gameId of the form `base`, `base-2`, `base-3`, ... not already
429
483
  * in `taken`.
@@ -464,6 +518,17 @@ declare function byDisplayOrder<T extends {
464
518
  order?: number;
465
519
  }>(items: readonly T[]): T[];
466
520
  declare function freeTitle(base: string, taken: ReadonlySet<string>): string;
521
+ /**
522
+ * A count of a TIMED box's turns, said as time (design/engine-server.md 4.8):
523
+ * `turnSpan(30, 60)` is "30 min", and `turnSpan(30, 60, true)` is "30 minutes".
524
+ *
525
+ * One definition, because the conversion appears wherever a designer might
526
+ * otherwise have to do it in their head: the card editor's Redraw field, the
527
+ * box page, the Board's advance buttons, and the coverage report's turn
528
+ * budget. Two of those want the unit spelled out and two want it short, which
529
+ * is the whole of `long`.
530
+ */
531
+ declare function turnSpan(turns: number, seconds: number, long?: boolean): string;
467
532
  interface Outcome<E> {
468
533
  id: string;
469
534
  gameId?: string;
@@ -519,6 +584,21 @@ interface Card<E> {
519
584
  * common case writes one number and "five in the world, one to a customer"
520
585
  * is `copies: 1, sharedCopies: 5`. */
521
586
  sharedCopies?: number;
587
+ /** Does this card's `redraw: "never"` spend survive the run
588
+ * (design/engine-server.md 4.2)? Absent takes the deck's flag, set here it
589
+ * overrides the deck, exactly as `shared` does. `shared` decides who a
590
+ * spend counts for WITHIN a run; this decides whether it outlives one.
591
+ *
592
+ * Only `"never"` crosses the run boundary, for the reason only `"never"`
593
+ * crosses the flow boundary (shared-scarcity 9.3.2): a finite cooldown is
594
+ * an absolute turn of a box clock, and the clock resets with the run. On
595
+ * any other redraw the flag is a compile warning.
596
+ *
597
+ * INERT TO THE RUNTIME, like the declaration flag: the server lifts the
598
+ * durable spends at run end (per-flow ones from the flow's `never`
599
+ * cooldowns, shared ones from the engine's spent set) and puts them back
600
+ * through `openFlow(id, { restore })` and `markTaken`. */
601
+ durable?: boolean;
522
602
  /** Card-template data: field name -> value, validated at publish. */
523
603
  fields?: Record<string, ScalarValue>;
524
604
  outcomes: Outcome<E>[];
@@ -534,6 +614,11 @@ interface Deck<E> {
534
614
  * in it is shared unless the card says otherwise. The container is where
535
615
  * Patter puts its own shared-memory flag, and the deck is our container. */
536
616
  shared?: boolean;
617
+ /** Every `redraw: "never"` card in this pile is spent for good, past the end
618
+ * of the run, unless the card says otherwise (design/engine-server.md 4.2).
619
+ * The container carries the flag for the reason `shared` is carried here:
620
+ * a pile is what an author reaches for when a rule is true of all of it. */
621
+ durable?: boolean;
537
622
  properties: PropertyDecl[];
538
623
  cards: Card<E>[];
539
624
  }
@@ -629,6 +714,30 @@ interface TagGroup {
629
714
  * half of that answer. `home` was a metaphor an author had to learn, and it
630
715
  * leaked into hand-edited shards and the docs. */
631
716
  declare const PLACE_GROUP = "place";
717
+ /** The scopes a movable hole may be filled from (design/engine-server.md 4.6):
718
+ * the two `boundBy` already allows, plus `@hand` - the asking hand's OWN
719
+ * declared property, resolved before tag composition so a movable hole can
720
+ * never depend on the tags it is choosing. */
721
+ type HoleRefScope = "hand" | "story" | "world";
722
+ /** A parsed hole reference: `@hand.zone` -> `{ scope: "hand", name: "zone" }`. */
723
+ interface HoleRef {
724
+ scope: HoleRefScope;
725
+ name: string;
726
+ }
727
+ /**
728
+ * Is this `chosen` / binding value MEANT as a property reference rather than a
729
+ * tag id?
730
+ *
731
+ * The test is the leading `@` alone, deliberately: a value that starts with
732
+ * one and does not parse is a mistyped reference, which the compiler should
733
+ * name as such, not a tag id that happens to look odd. Tag ids never begin
734
+ * with `@`.
735
+ */
736
+ declare const isHoleRef: (value: string) => boolean;
737
+ /** Parse a hole reference, or undefined when it is not one. The on-disk form
738
+ * stays a plain string, so the canonical serialiser and the shard merge need
739
+ * no change at all: a hole is still one group name against one value. */
740
+ declare const parseHoleRef: (value: string) => HoleRef | undefined;
632
741
  /** A declared kind of hand (schema 2.6): live-inherited, author-side only,
633
742
  * never called from game code. One condition governs every instance. */
634
743
  interface HandTemplate<E> {
@@ -640,9 +749,12 @@ interface HandTemplate<E> {
640
749
  * position). Merges as a per-item value, so id-sorted storage stays
641
750
  * merge-clean (Reboot 7.4). */
642
751
  order?: number;
643
- /** Fixed tag bindings: tag group id -> tag id. */
752
+ /** Fixed tag bindings: tag group id -> tag id. Literal tags only: what a
753
+ * template FIXES is the same for every instance, and a hole that moves is
754
+ * the instance's own business (`Hand.chosen`, 4.6). */
644
755
  bindings?: Record<string, string>;
645
- /** The holes: tag group ids each instance fills (one tag each). */
756
+ /** The holes: tag group ids each instance fills (one tag each, or one
757
+ * property reference: 4.6). */
646
758
  chooses?: string[];
647
759
  /** Shared availability condition, ANDed in (schema 3.1); evaluated per
648
760
  * instance against that instance's composed @hand. */
@@ -654,6 +766,15 @@ interface HandTemplate<E> {
654
766
  }
655
767
  /** A standalone hand's inline rule (schema 2.6): owned by the hand. */
656
768
  interface HandRule<E> {
769
+ /**
770
+ * Tag group id -> tag id, or a PROPERTY REFERENCE (`"@hand.zone"`,
771
+ * `"@story.where"`, `"@world.place"`) the runtime resolves at ask time
772
+ * (design/engine-server.md 4.6, the hand that moves). Still a plain string
773
+ * on disk, so the canonical serialiser and the merge are untouched; what
774
+ * widened is the meaning, and `parseHoleRef` is where it is read.
775
+ *
776
+ * `place` is never fillable this way: it is the hand's own name, not an axis.
777
+ */
657
778
  bindings?: Record<string, string>;
658
779
  condition?: E;
659
780
  slots: number | "unbounded";
@@ -670,7 +791,17 @@ interface Hand<E> {
670
791
  purpose?: string;
671
792
  /** Hand template id (not gameId). */
672
793
  template?: string;
673
- /** Template instances: tag group id -> tag id, one per `chooses` hole. */
794
+ /**
795
+ * Template instances: tag group id -> tag id, one per `chooses` hole.
796
+ *
797
+ * A value may instead be a PROPERTY REFERENCE (`"@hand.zone"`,
798
+ * `"@story.where"`, `"@world.place"`), which makes the hole MOVABLE: the
799
+ * runtime resolves the reference at ask time and binds the hole to the tag
800
+ * the value names, so moving the Elder to the forest is `setProperty` and
801
+ * nothing else (design/engine-server.md 4.6). Still a plain string on disk,
802
+ * so the canonical serialiser and the shard merge need no change; read it
803
+ * with `parseHoleRef`.
804
+ */
674
805
  chosen?: Record<string, string>;
675
806
  /** Standalone hands: the inline rule. */
676
807
  rule?: HandRule<E>;
@@ -695,6 +826,25 @@ interface Box<E> {
695
826
  ranking: {
696
827
  specificity: boolean;
697
828
  };
829
+ /**
830
+ * A TIMED box: its clock counts real time, one turn every `seconds` of the
831
+ * run (design/engine-server.md 4.8). Absent is the ordinary box, whose turn
832
+ * is a play.
833
+ *
834
+ * Two things follow, and only two. In the ENGINE, a play in this box
835
+ * defaults to advancing nothing: `settings.playAdvancesTurns` does not
836
+ * apply, so a designer cannot declare the convention and then forget to
837
+ * switch play-advance off. Everywhere else it is what the tools SAY: the
838
+ * host ticks the box (the runtime has no clock and gains none here), and a
839
+ * card's `redraw: N` reads as N x `seconds`, which the editors, the bundle
840
+ * inspectors and the coverage report spell out rather than leaving a
841
+ * designer to know that 30 meant minutes.
842
+ *
843
+ * The number itself is inert to the runtime, which never reads it.
844
+ */
845
+ turn?: {
846
+ seconds: number;
847
+ };
698
848
  /** The card template: what every card in this box carries. */
699
849
  fields: FieldDecl[];
700
850
  properties: PropertyDecl[];
@@ -714,6 +864,30 @@ interface BundleContent {
714
864
  interface BundleSettings {
715
865
  playAdvancesTurns: number;
716
866
  }
867
+ /**
868
+ * The play ladder (design/engine-server.md 4.10): how much of itself
869
+ * Storyletter shows this project, in one setting with three rungs rather than
870
+ * a set of toggles, because the features nest.
871
+ *
872
+ * solo one player, one flow: no sharing, no durability, no venue features
873
+ * shared several players over one world: sharing appears
874
+ * venue a production: nothing is hidden
875
+ *
876
+ * EDITOR-SIDE ONLY. It stays in the project shard beside `coverage` and
877
+ * `export` and is never compiled: a solo project plays on the same Engine as a
878
+ * venue one. Hidden is hidden rather than disabled, so going DOWN a rung is
879
+ * refused when the project already contains what the rung would hide, and a
880
+ * hand-edited shard above its rung is a compile warning.
881
+ */
882
+ type PlayRung = "solo" | "shared" | "venue";
883
+ /** The default rung: a project shard that says nothing is a solo game. */
884
+ declare const DEFAULT_PLAY_RUNG: PlayRung;
885
+ /** The project shard's settings block: what the bundle carries, plus the
886
+ * authoring-side play rung that it does not. */
887
+ interface ProjectSettings extends BundleSettings {
888
+ /** The play ladder rung (see `PlayRung`). Absent = "solo". */
889
+ play?: PlayRung;
890
+ }
717
891
  /**
718
892
  * A map that a bundle was asked to carry: one spatial tag group's geometry,
719
893
  * flattened for a host to draw (design/graphical-views.md 2, "The map MAY ship").
@@ -728,9 +902,16 @@ interface BundleSettings {
728
902
  * names it passes to `peek`. There is nothing here to strip either, which is why
729
903
  * `metadata: "stripped"` needs no special case: no titles, no purposes.
730
904
  *
731
- * Sites are deliberately NOT here. A site is where an author parked a hand while
732
- * working, held in the view sidecar precisely because it is not content, and a
733
- * host that wants to place a hand already has its zone from the compiled binding.
905
+ * SITES ARE HERE, which reverses a ruling. Until 2026-09-05 this comment said
906
+ * they were deliberately not: a site was where an author parked a hand while
907
+ * working, held in the view sidecar precisely because it was not content, and a
908
+ * host that wanted to place a hand had its zone from the compiled binding. That
909
+ * held for a game, where a hand's zone is its only real-world meaning. It does
910
+ * not hold for a physical experience (design/engine-server.md 4.3), where the
911
+ * position IS content: it is where the kiosk stands, and a producer's map is
912
+ * simply wrong without it. The alternative was a second file beside the bundle,
913
+ * which would cost a format the inspectors do not read and would put the view
914
+ * sidecar in the shipping path by the back door.
734
915
  */
735
916
  interface BundleMap {
736
917
  /** The owning box, by gameId (tag groups are box-scoped). */
@@ -746,6 +927,16 @@ interface BundleMap {
746
927
  /** Background pictures, back to front, as bundle-relative paths. Hidden ones
747
928
  * do not ship: what an author put away is not something to spring on a host. */
748
929
  backgrounds?: BundleBackground[];
930
+ /** Where the placed hands stand on this map, by hand gameId, sorted by that
931
+ * gameId so the bytes do not depend on authoring order. A hand nobody has
932
+ * placed has no entry, and a map with no placed hand has no key at all. The
933
+ * zone a site sits in is NOT repeated here: the hand's own binding is what
934
+ * the runtime deals from, and a second copy could only go on to disagree. */
935
+ sites?: {
936
+ hand: string;
937
+ x: number;
938
+ y: number;
939
+ }[];
749
940
  }
750
941
  /** One shipped picture. `locked` and `hidden` are authoring state and do not
751
942
  * travel; the draw order is the array order. */
@@ -835,6 +1026,75 @@ interface SaveEnvelope {
835
1026
  shared: SharedSave;
836
1027
  flows: Record<string, FlowSave>;
837
1028
  }
1029
+ /** One card that a restore refused to put back on the board.
1030
+ *
1031
+ * `vanished` and `hand-vanished` are the edit's doing (the card, or the hand
1032
+ * it sat in, is no longer in the bundle). `claimed-elsewhere` is only ever a
1033
+ * single-flow restore into a LIVE engine: the card is shared, and the other
1034
+ * open flows already hold every copy the world has. */
1035
+ interface LoadEviction {
1036
+ flow: string;
1037
+ hand: string;
1038
+ card: string;
1039
+ reason: "vanished" | "hand-vanished" | "claimed-elsewhere";
1040
+ }
1041
+ /** One property the restore could not put back as it was. `flow` names the
1042
+ * flow whose half it belongs to; absent, it is the shared half.
1043
+ *
1044
+ * `path` is the engine's property address, spelled exactly as
1045
+ * `Flow.listProperties()` / `Engine.listProperties()` print it and exactly as
1046
+ * `getProperty` and `setProperty` accept it: `story.<name>` for the story
1047
+ * scope, `<scope>.<ownerGameId>.<name>` for the box, deck, hand and tag
1048
+ * scopes. No `@`, which belongs to the expression language and not to an
1049
+ * address.
1050
+ *
1051
+ * The owner segment is its GAMEID (design/engine-server.md 4.4), the name it
1052
+ * is called by everywhere else, so an operator reading a hot-swap report can
1053
+ * paste the address straight into `setProperty`. An owner the build no longer
1054
+ * has keeps the id the save carried: there is no gameId left to give it,
1055
+ * which is the rule the eviction list above has always used. */
1056
+ interface LoadProperty {
1057
+ flow?: string;
1058
+ path: string;
1059
+ }
1060
+ /** What a load or a flow restore would do that is not a plain restore
1061
+ * (design/engine-server.md 4.9). Arrays are sorted, so two runtimes given the
1062
+ * same save and bundle produce the same bytes; `flows` alone keeps the
1063
+ * envelope's own order, because a caller re-takes its handles in it. */
1064
+ interface LoadReport {
1065
+ /** No drift and nothing dropped, defaulted or retyped: the save goes back
1066
+ * exactly as it was. `flows` is not a divergence and does not count. */
1067
+ exact: boolean;
1068
+ project: string;
1069
+ /** Drift when the two differ; reported, never refused. */
1070
+ version: {
1071
+ saved: string;
1072
+ bundle: string;
1073
+ };
1074
+ /** Drift when the two differ; reported, never refused. */
1075
+ hash: {
1076
+ saved: string;
1077
+ bundle: string;
1078
+ };
1079
+ /** The flows this restores, in the order it restores them. */
1080
+ flows: string[];
1081
+ evicted: LoadEviction[];
1082
+ /** Cooldowns held for cards the bundle no longer has. */
1083
+ droppedCooldowns: {
1084
+ flow: string;
1085
+ card: string;
1086
+ }[];
1087
+ /** Shared `redraw: "never"` entries for cards the bundle no longer has. */
1088
+ droppedSpent: string[];
1089
+ /** In the save, not declared any more. */
1090
+ droppedProperties: LoadProperty[];
1091
+ /** Declared, not in the save: it takes the declaration's default. */
1092
+ defaultedProperties: LoadProperty[];
1093
+ /** In the save, still declared, but the saved value no longer fits the
1094
+ * declaration (its type changed, or an enum value / quality stage was
1095
+ * edited away). It takes the declaration's default. */
1096
+ retypedProperties: LoadProperty[];
1097
+ }
838
1098
  /** The .storyletsave FILE: the HOST's file, not the engine's - the engine's
839
1099
  * envelope plus, when the host keeps one, its @world container. This is
840
1100
  * "host saves its container once, each engine saves its own envelope"
@@ -879,7 +1139,18 @@ declare const SHARD_EXTENSIONS: {
879
1139
  * file and were retired: `purpose` already says why a thing exists, and
880
1140
  * Patterpad's typed routing has no destination here. */
881
1141
  readonly notes: ".storyletnotes";
1142
+ /** An installation contract: what a VENUE depends on, one file per
1143
+ * installation in `contracts/` at the project root
1144
+ * (design/engine-server.md 4.11). Its own shard, and its own folder, for the
1145
+ * walkthrough's reason (Reboot 7.5, S4): a different owner, a different
1146
+ * change rate, and a merge that must never collide with the author's edits,
1147
+ * since the server always wins its own file. */
1148
+ readonly contract: ".storyletcontract";
882
1149
  };
1150
+ /** Where the installation contracts live, relative to the project root. The
1151
+ * directory is the registry, as it is for a box's decks: a contract exists
1152
+ * because its file exists. */
1153
+ declare const CONTRACTS_DIR = "contracts";
883
1154
  declare const PROJECT_SCHEMA = "storylets/project@0";
884
1155
  declare const BOX_SCHEMA = "storylets/box@0";
885
1156
  declare const TAGS_SCHEMA = "storylets/tags@0";
@@ -889,6 +1160,68 @@ declare const VIEW_SCHEMA = "storylets/view@0";
889
1160
  /** The comment sidecar's schema. Still called "notes" on disk: the file already
890
1161
  * held both, and renaming it would break every project for no gain. */
891
1162
  declare const NOTES_SCHEMA = "storylets/notes@0";
1163
+ declare const CONTRACT_SCHEMA = "storylets/contract@0";
1164
+ /**
1165
+ * What one installation depends on, written by the venue's server and read by
1166
+ * `validate` (design/engine-server.md 4.11).
1167
+ *
1168
+ * NOT THE AUTHOR'S FILE. A venue is provisioned against names - the hands its
1169
+ * stations deal, the boxes its scheduler ticks, the properties its clocks drive,
1170
+ * the fields its crew read - and the server writes them out so the tools that
1171
+ * already gate a build can refuse a rename before it reaches the venue. A
1172
+ * project playing at two venues has two of these. The author never edits one,
1173
+ * and today, with no server built, a project either receives one or has none.
1174
+ *
1175
+ * NEVER COMPILED. It is project-side config like `coverage` and `export`: the
1176
+ * server does not need its own contract handed back, it needs the bundle to
1177
+ * still honour it.
1178
+ *
1179
+ * BY GAMEID throughout, because a gameId is the name that crosses the project's
1180
+ * border and an internal id is authoring identity.
1181
+ */
1182
+ interface ContractShard {
1183
+ schema: typeof CONTRACT_SCHEMA;
1184
+ /** The installation this contract speaks for. One file per installation, and
1185
+ * two files naming the same one is an error. */
1186
+ installation: string;
1187
+ /** Who wrote it, for a human reading the file ("Storylet Server 0.1.0"). */
1188
+ by?: string;
1189
+ /** The server's revision when it wrote this. */
1190
+ revision?: number;
1191
+ /** Hands a station is bound to, by gameId: they may not be renamed or
1192
+ * removed. */
1193
+ hands?: string[];
1194
+ /** Timed boxes the venue's scheduler ticks, by box gameId, with the turn unit
1195
+ * in SECONDS it was provisioned against. A box whose unit changed means every
1196
+ * rest on its cards changed meaning. */
1197
+ boxes?: Record<string, {
1198
+ turn: number;
1199
+ }>;
1200
+ /** Property paths the venue reads or drives, in the engine's own address
1201
+ * grammar with no `@` ("world.time_wall", "story.visits"), which is how
1202
+ * `listProperties()` prints them. */
1203
+ properties?: ContractProperty[];
1204
+ /** Card-template field names the crew and the bridges read. */
1205
+ fields?: string[];
1206
+ }
1207
+ /**
1208
+ * One contracted property.
1209
+ *
1210
+ * A bare path is the common form and the one the spec's example writes. The
1211
+ * object form adds the TYPE the venue was provisioned against, which is the only
1212
+ * way `validate` can catch the break that costs a producer most: a property that
1213
+ * still exists under the same name and now holds something else. A server that
1214
+ * knows the type should write the object form; a hand-written contract may say
1215
+ * only the path and get the existence check alone.
1216
+ */
1217
+ type ContractProperty = string | {
1218
+ path: string;
1219
+ type?: PropertyType;
1220
+ };
1221
+ /** The path a contracted property names, whichever form it was written in. */
1222
+ declare const contractPropertyPath: (p: ContractProperty) => string;
1223
+ /** The type a contracted property was provisioned against, when it says. */
1224
+ declare const contractPropertyType: (p: ContractProperty) => PropertyType | undefined;
892
1225
  /** A point in a canvas's own coordinates. */
893
1226
  interface ViewPoint {
894
1227
  x: number;
@@ -1008,7 +1341,7 @@ interface ProjectShard {
1008
1341
  name: string;
1009
1342
  version: string;
1010
1343
  };
1011
- settings: BundleSettings;
1344
+ settings: ProjectSettings;
1012
1345
  /** Coverage drivers + argument domains (authoring/testing config; stays
1013
1346
  * out of the compiled bundle). */
1014
1347
  coverage?: CoverageConfig;
@@ -1078,6 +1411,10 @@ interface BoxShard {
1078
1411
  ranking: {
1079
1412
  specificity: boolean;
1080
1413
  };
1414
+ /** Declares a timed box (see `Box.turn`); compiled through unchanged. */
1415
+ turn?: {
1416
+ seconds: number;
1417
+ };
1081
1418
  fields: FieldDecl[];
1082
1419
  properties: PropertyDecl[];
1083
1420
  };
@@ -1103,6 +1440,8 @@ interface DeckShard {
1103
1440
  condition?: string;
1104
1441
  /** Scarce across flows: see Deck.shared. */
1105
1442
  shared?: boolean;
1443
+ /** Its `redraw: "never"` cards are spent past the run: see Deck.durable. */
1444
+ durable?: boolean;
1106
1445
  /** Authored display order within the box (sparse; see BoxShard). */
1107
1446
  order?: number;
1108
1447
  properties: PropertyDecl[];
@@ -1110,4 +1449,4 @@ interface DeckShard {
1110
1449
  cards: Card<string>[];
1111
1450
  }
1112
1451
 
1113
- export { BOX_SCHEMA, BUNDLE_EXTENSION, BUNDLE_SCHEMA, type Box, type BoxMap, type BoxShard, type Bundle, type BundleBackground, type BundleContent, type BundleMap, type BundleSettings, type CanvasFurniture, type Card, type Comment, type CommentMark, type CommentMessage, type CoverageConfig, type CoverageDriver, DECK_SCHEMA, type Deck, type DeckCanvas, type DeckShard, FURNITURE_COLOURS, type FieldDecl, type FlowSave, type Frame, type FurnitureColour, HANDS_SCHEMA, type Hand, type HandBinding, type HandRule, type HandTemplate, type HandsShard, NOTES_SCHEMA, type NotesShard, type Outcome, PLACE_GROUP, PROJECT_FOLDER_EXTENSION, PROJECT_SCHEMA, type PlayRecord, type Polygon, type ProjectShard, type PropertyBag, type PropertyDecl, type PropertyType, type PropsPartition, type Rect, type RedrawPolicy, SAVEFILE_SCHEMA, SAVE_SCHEMA, SHARD_EXTENSIONS, SPATIAL, type SaveEnvelope, type SaveFile, type SharedSave, type SpatialBackground, type SpatialGroup, type StackMove, type Stacked, TAGS_SCHEMA, type Tag, type TagGroup, type TagsShard, VIEW_SCHEMA, type ViewPoint, type ViewShard, backgroundsOf, bindHand, bundleAssetPath, byDisplayOrder, centroid, commentsOf, droppedRect, effectiveGameId, framesOf, freeGameId, freeTitle, gameIdify, handBinding, inferDeclFromWrite, isSpatial, isValidGameId, labelPoint, markOf, marksOn, openThreadCounts, pointInPolygon, polygonBounds, polygonOf, restack, spatialOf, stacked, threadsFor, unbindHand, withBackgrounds, withPolygon, withSpatialGroup, withZ, zOf, zoneAt, zonesAt };
1452
+ export { BOX_SCHEMA, BUNDLE_EXTENSION, BUNDLE_SCHEMA, type Box, type BoxMap, type BoxShard, type Bundle, type BundleBackground, type BundleContent, type BundleMap, type BundleSettings, CONTRACTS_DIR, CONTRACT_SCHEMA, type CanvasFurniture, type Card, type Comment, type CommentMark, type CommentMessage, type ContractProperty, type ContractShard, type CoverageConfig, type CoverageDriver, DECK_SCHEMA, DEFAULT_PLAY_RUNG, type Deck, type DeckCanvas, type DeckShard, FURNITURE_COLOURS, type FieldDecl, type FlowSave, type Frame, type FurnitureColour, HANDS_SCHEMA, type Hand, type HandBinding, type HandRule, type HandTemplate, type HandsShard, type HoleRef, type HoleRefScope, type LoadEviction, type LoadProperty, type LoadReport, NOTES_SCHEMA, type NotesShard, type Outcome, PLACE_GROUP, PROJECT_FOLDER_EXTENSION, PROJECT_SCHEMA, type PlayRecord, type PlayRung, type Polygon, type ProjectSettings, type ProjectShard, type PropertyBag, type PropertyDecl, type PropertyType, type PropsPartition, type Rect, type RedrawPolicy, SAVEFILE_SCHEMA, SAVE_SCHEMA, SHARD_EXTENSIONS, SPATIAL, type SaveEnvelope, type SaveFile, type SharedSave, type SpatialBackground, type SpatialGroup, type StackMove, type Stacked, TAGS_SCHEMA, type Tag, type TagGroup, type TagsShard, VIEW_SCHEMA, type ValueAddresses, type ViewPoint, type ViewShard, ambiguousValueAddressMessage, backgroundsOf, bindHand, bundleAssetPath, byDisplayOrder, centroid, commentsOf, contractPropertyPath, contractPropertyType, droppedRect, effectiveGameId, framesOf, freeGameId, freeTitle, gameIdify, handBinding, inferDeclFromWrite, isHoleRef, isSpatial, isValidGameId, labelPoint, markOf, marksOn, openThreadCounts, parseHoleRef, pointInPolygon, polygonBounds, polygonOf, restack, spatialOf, stacked, threadsFor, turnSpan, unbindHand, valueAddresses, withBackgrounds, withPolygon, withSpatialGroup, withZ, zOf, zoneAt, zonesAt };