@storylet-studio/runtime 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.cjs +532 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +218 -23
- package/dist/index.d.ts +218 -23
- package/dist/index.js +532 -80
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ScalarValue, ScopeResolver, ExprNode } from '@wildwinter/expr';
|
|
2
2
|
export { Prng, makePrng, shuffleInPlace } from '@wildwinter/expr';
|
|
3
|
-
import { Bundle, Card, Expression, Deck, Box, Hand, HandTemplate, TagGroup, PropertyDecl,
|
|
3
|
+
import { Bundle, FlowSave, LoadReport, Card, Expression, Deck, Box, Hand, HandTemplate, TagGroup, PropertyDecl, SaveEnvelope, PropertyType, ScalarValue as ScalarValue$1 } from '@storylet-studio/model';
|
|
4
4
|
import { PropertyBag, PropertyRow } from '@wildwinter/scoperegistry';
|
|
5
5
|
export { PropertyRow } from '@wildwinter/scoperegistry';
|
|
6
6
|
|
|
@@ -38,6 +38,25 @@ interface EngineOptions {
|
|
|
38
38
|
interface OpenFlowOptions {
|
|
39
39
|
/** Seed for this flow's PRNG (defaults to the engine's `seed`). */
|
|
40
40
|
seed?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Open this flow AS IT WAS: a blob from `saveFlow`, applied to the freshly
|
|
43
|
+
* opened (or replaced) flow before the handle comes back
|
|
44
|
+
* (design/engine-server.md 4.1).
|
|
45
|
+
*
|
|
46
|
+
* An option on `openFlow` rather than a `Flow.restore` verb on purpose:
|
|
47
|
+
* restoring INTO a running flow is the trap hosts keep falling into
|
|
48
|
+
* (openFlow REPLACES), and "open this flow as it was" is one act. Drift is
|
|
49
|
+
* tolerated exactly as `loadGame` tolerates it, with one addition, because
|
|
50
|
+
* this restore lands in a LIVE engine: a shared card whose world copies are
|
|
51
|
+
* all held by the OTHER open flows is not put back, and is reported as
|
|
52
|
+
* `claimed-elsewhere`. Ask `previewFlowRestore` first to see that coming.
|
|
53
|
+
*/
|
|
54
|
+
restore?: FlowSave;
|
|
55
|
+
/** Handed the `restore`'s LoadReport as it happens - the same report
|
|
56
|
+
* `previewFlowRestore` returns for the same blob. Ignored without
|
|
57
|
+
* `restore`; the report has nowhere else to go, since `openFlow` returns
|
|
58
|
+
* the handle. */
|
|
59
|
+
onRestoreReport?: (report: LoadReport) => void;
|
|
41
60
|
}
|
|
42
61
|
/** A card view in a dealt hand or a peeked list. Carries NO outcome
|
|
43
62
|
* availability - ask `outcomes()` for current truth (schema 5). */
|
|
@@ -53,6 +72,9 @@ interface OutcomeView {
|
|
|
53
72
|
gameId: string;
|
|
54
73
|
title?: string;
|
|
55
74
|
purpose?: string;
|
|
75
|
+
/** The outcome's fields, exactly as the bundle carries them: game data
|
|
76
|
+
* declared by the box's `outcomeFields`, never read by the engine. */
|
|
77
|
+
fields?: Record<string, ScalarValue>;
|
|
56
78
|
/** Evaluated against CURRENT state at the moment of the ask. */
|
|
57
79
|
available: boolean;
|
|
58
80
|
}
|
|
@@ -63,7 +85,8 @@ interface RankedList {
|
|
|
63
85
|
cards: DealtCard[];
|
|
64
86
|
}
|
|
65
87
|
interface PlayOptions {
|
|
66
|
-
/** Turn advance override; default settings.playAdvancesTurns
|
|
88
|
+
/** Turn advance override; default settings.playAdvancesTurns, or 0 when the
|
|
89
|
+
* card's box is timed (design/engine-server.md 4.8). */
|
|
67
90
|
advanceTurns?: number;
|
|
68
91
|
}
|
|
69
92
|
/** Why a card did or did not make an ask, in availability order (schema 3.1). */
|
|
@@ -71,11 +94,18 @@ type TraceVerdict = "dealt" | "capped" | "cooldown" | "deck-gate" | "tags" | "co
|
|
|
71
94
|
/** One event on the deal/play log - "why did Ambush at the ford get dealt
|
|
72
95
|
* here?" is answered by the ask event's per-card verdicts and keys. The
|
|
73
96
|
* verb is the event type, so a peek is distinguishable from a deal when
|
|
74
|
-
* reading a run back.
|
|
97
|
+
* reading a run back.
|
|
98
|
+
*
|
|
99
|
+
* IDENTITY IS BY GAMEID throughout (design/engine-server.md 4.4). It was
|
|
100
|
+
* mixed until then: `deal.hand` and `peek.box` were gameIds while
|
|
101
|
+
* `evict.hand`, `play.card` and every `cards[].id` were internal ids, so
|
|
102
|
+
* every consumer outside the engine - the Board, the four examiners, the
|
|
103
|
+
* Live Link, a wire a kiosk reads - mapped one to the other itself. */
|
|
75
104
|
type TraceEvent = {
|
|
76
105
|
type: "deal";
|
|
77
106
|
/** Hand gameId. */
|
|
78
107
|
hand: string;
|
|
108
|
+
/** `id` is the card's GAMEID (design/engine-server.md 4.4). */
|
|
79
109
|
cards: {
|
|
80
110
|
id: string;
|
|
81
111
|
verdict: TraceVerdict;
|
|
@@ -87,24 +117,31 @@ type TraceEvent = {
|
|
|
87
117
|
/** Box gameId. */
|
|
88
118
|
box: string;
|
|
89
119
|
criteria: Record<string, string>;
|
|
120
|
+
/** `id` is the card's GAMEID (design/engine-server.md 4.4). */
|
|
90
121
|
cards: {
|
|
91
122
|
id: string;
|
|
92
123
|
verdict: TraceVerdict;
|
|
93
124
|
priority?: number;
|
|
94
125
|
specificity?: number;
|
|
95
126
|
}[];
|
|
96
|
-
}
|
|
127
|
+
}
|
|
128
|
+
/** Hand and card gameIds. A card the build no longer has (`vanished`) has
|
|
129
|
+
* no gameId left and is named by the id the board carried. */
|
|
130
|
+
| {
|
|
97
131
|
type: "evict";
|
|
98
132
|
hand: string;
|
|
99
133
|
card: string;
|
|
100
134
|
reason: TraceVerdict | "hand-condition" | "vanished";
|
|
101
|
-
}
|
|
135
|
+
}
|
|
136
|
+
/** Card and outcome gameIds. */
|
|
137
|
+
| {
|
|
102
138
|
type: "play";
|
|
103
139
|
card: string;
|
|
104
140
|
outcome: string;
|
|
105
141
|
turn: number;
|
|
106
142
|
}
|
|
107
|
-
/** One landed outcome change; `path` is the resolved store location
|
|
143
|
+
/** One landed outcome change; `path` is the resolved store location, in the
|
|
144
|
+
* address grammar `getProperty` takes - the owner segment is its gameId (a
|
|
108
145
|
* routed @hand write shows where it actually went, schema 3.6). `prev`
|
|
109
146
|
* is the value it replaced, so a log can read "0 -> 1". */
|
|
110
147
|
| {
|
|
@@ -153,8 +190,8 @@ interface CardEntry {
|
|
|
153
190
|
deck: Deck<Expression>;
|
|
154
191
|
box: Box<Expression>;
|
|
155
192
|
}
|
|
156
|
-
/** One kernel bag with its store path prefix (story / box.<
|
|
157
|
-
* / hand.<
|
|
193
|
+
/** One kernel bag with its store path prefix (story / box.<gameId> / deck.<gameId>
|
|
194
|
+
* / hand.<gameId> / value.<gameId>): the state logger's mount surface
|
|
158
195
|
* (design/engine-runtimes.md 3.4 - the logger builds on the PropertyBag
|
|
159
196
|
* audit hook, so it needs the bags themselves, not just their rows).
|
|
160
197
|
* The Engine lists the shared bags, a Flow its own; the @world container
|
|
@@ -172,6 +209,50 @@ interface BoxView {
|
|
|
172
209
|
title?: string;
|
|
173
210
|
turn: number;
|
|
174
211
|
}
|
|
212
|
+
/** One side's five declaration lists, keyed by owner id where the scope has
|
|
213
|
+
* owners. The bags are built from these; so is the load report's answer to
|
|
214
|
+
* "what does this build declare that the save does not carry". */
|
|
215
|
+
interface DeclSet {
|
|
216
|
+
story: PropertyDecl[];
|
|
217
|
+
box: Map<string, PropertyDecl[]>;
|
|
218
|
+
deck: Map<string, PropertyDecl[]>;
|
|
219
|
+
hand: Map<string, PropertyDecl[]>;
|
|
220
|
+
value: Map<string, PropertyDecl[]>;
|
|
221
|
+
}
|
|
222
|
+
/** The four owned property scopes: the ones whose address carries an owner
|
|
223
|
+
* segment. `story` has no owner and `world` is the host's. */
|
|
224
|
+
type OwnedScope = "box" | "deck" | "hand" | "value";
|
|
225
|
+
/** The owner segment of a property address, both ways round
|
|
226
|
+
* (design/engine-server.md 4.4).
|
|
227
|
+
*
|
|
228
|
+
* `gameId` is the segment the ADDRESS uses; `id` is the internal id everything
|
|
229
|
+
* inside the engine is keyed by - the bags, the save envelope, the ladders.
|
|
230
|
+
* Both maps are built in bundle order and a repeated gameId does NOT
|
|
231
|
+
* overwrite the first.
|
|
232
|
+
*
|
|
233
|
+
* Box, deck, hand and card gameIds are unique bundle-wide, so for three of the
|
|
234
|
+
* four scopes the segment is simply the gameId. A TAG's is unique only within
|
|
235
|
+
* its group, and a group's only within its box, so two boxes may each name a
|
|
236
|
+
* tag "docks": the value scope's segment is box-qualified,
|
|
237
|
+
* `value.<boxGameId>/<tagGameId>.<name>`, wherever a gameId repeats, and the
|
|
238
|
+
* short form is REFUSED there rather than resolved to the first in bundle
|
|
239
|
+
* order. `valueAddresses` in the model is the one definition of that rule -
|
|
240
|
+
* the Board draws these addresses from the bundle while the engine builds them
|
|
241
|
+
* from this index, and the two have to agree - and `repeated` is what it
|
|
242
|
+
* found, so a refusal can name the candidates.
|
|
243
|
+
*
|
|
244
|
+
* Two GROUPS in one box naming the same tag is the case the box qualifier
|
|
245
|
+
* cannot separate, and it is closing at the source rather than here (question
|
|
246
|
+
* 16, ruled 2026-09-06): the compiler warns that a tag gameId must be unique
|
|
247
|
+
* within its box, and refuses it from the next release. Until then the first
|
|
248
|
+
* in bundle order answers, as it always did.
|
|
249
|
+
*/
|
|
250
|
+
interface OwnerIndex {
|
|
251
|
+
gameId: Map<string, string>;
|
|
252
|
+
id: Map<string, string>;
|
|
253
|
+
repeated: Map<string, string[]>;
|
|
254
|
+
}
|
|
255
|
+
type OwnerIndexes = Record<OwnedScope, OwnerIndex>;
|
|
175
256
|
/** One side's five stores (shared on the engine, per-flow on each flow). */
|
|
176
257
|
interface Partition {
|
|
177
258
|
story: PropertyBag;
|
|
@@ -199,6 +280,8 @@ interface Internals {
|
|
|
199
280
|
hand: Hand<Expression>;
|
|
200
281
|
box: Box<Expression>;
|
|
201
282
|
}>;
|
|
283
|
+
/** The owner segment of a property address, both ways round (4.4). */
|
|
284
|
+
owners: OwnerIndexes;
|
|
202
285
|
templatesById: Map<string, HandTemplate<Expression>>;
|
|
203
286
|
groupsById: Map<string, {
|
|
204
287
|
group: TagGroup;
|
|
@@ -223,17 +306,26 @@ interface Internals {
|
|
|
223
306
|
hasShared: boolean;
|
|
224
307
|
/** The per-flow halves of every declaration list, precomputed once: each
|
|
225
308
|
* new flow builds its bags from these. */
|
|
226
|
-
flowDecls:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
};
|
|
309
|
+
flowDecls: DeclSet;
|
|
310
|
+
/** The shared halves, the same way. Not used to build anything - the shared
|
|
311
|
+
* bags are built straight from the bundle - but a load report has to say
|
|
312
|
+
* what the shared side WOULD hold without building a bag, which is what
|
|
313
|
+
* makes previewLoad pure. */
|
|
314
|
+
sharedDecls: DeclSet;
|
|
233
315
|
/** The shared stores. Reassigned wholesale by loadGame/reset. */
|
|
234
316
|
shared: Partition;
|
|
235
317
|
/** @world: the host's resolver, or the self-backed bag's. */
|
|
236
318
|
worldResolver: ScopeResolver;
|
|
319
|
+
/** The @world WRITE seam. `host` says the caller is the GAME's own surface -
|
|
320
|
+
* setProperty, the coverage harness, the CLI's --set - which the shared
|
|
321
|
+
* kernel lets past a `writable: false` (scoperegistry 0.6.0): that flag is
|
|
322
|
+
* the story's promise, not the game's. The story's refusal is the
|
|
323
|
+
* worldReadOnly table below, consulted before this seam is reached. A BOUND
|
|
324
|
+
* resolver is opaque - it takes a name and a value and keeps whatever rule
|
|
325
|
+
* the game has - so the flag only ever reaches the self-backed bag.
|
|
326
|
+
* Undefined when @world cannot be written at all (a resolver bound with no
|
|
327
|
+
* `set`). */
|
|
328
|
+
worldSet?: (name: string, value: ScalarValue, host?: boolean) => void;
|
|
237
329
|
/** @world names declared `writable: false`: the story's promise, kept at
|
|
238
330
|
* runtime as the compiler keeps it at publish (Reboot.md 10). */
|
|
239
331
|
worldReadOnly: Set<string>;
|
|
@@ -307,15 +399,24 @@ declare class Engine {
|
|
|
307
399
|
* Derived, which is what makes closeFlow and the openFlow replace release
|
|
308
400
|
* what a flow was holding: its board leaves the map with it. */
|
|
309
401
|
sharedClaims(): Map<string, number>;
|
|
402
|
+
/** The same ledger with one name left out: what the REST of the world
|
|
403
|
+
* holds, which is the question a resume under that name has to ask. */
|
|
404
|
+
private sharedClaimsExcept;
|
|
310
405
|
/**
|
|
311
406
|
* Read shared state by path: "world.x", "story.gold" (when shared),
|
|
312
|
-
* "box.
|
|
407
|
+
* "box.village.heat" (when shared) - the owner segment is its GAMEID
|
|
408
|
+
* (design/engine-server.md 4.4). A ref that resolves PER-FLOW throws,
|
|
313
409
|
* naming the fix - silently answering with some flow's copy (or a junk
|
|
314
410
|
* default) was the bug Patter's engine.getProperty guard exists to stop.
|
|
315
411
|
*/
|
|
316
412
|
getProperty(path: string): ScalarValue;
|
|
317
413
|
setProperty(path: string, value: ScalarValue): void;
|
|
318
414
|
private resolveShared;
|
|
415
|
+
/** The engine's own surface has no flow, so an engine-level diagnostic
|
|
416
|
+
* carries the empty flow id - the same way a LoadReport's shared half
|
|
417
|
+
* carries no flow. It reaches the run log and the engine tap; there is
|
|
418
|
+
* nowhere else for it to go, and it fires only on a legacy address. */
|
|
419
|
+
private diagnose;
|
|
319
420
|
/** The shared surface as examiner rows: @world (read through the
|
|
320
421
|
* resolver) then the shared partitions. Per-flow rows live on each Flow. */
|
|
321
422
|
listProperties(): PropertyRow[];
|
|
@@ -329,10 +430,40 @@ declare class Engine {
|
|
|
329
430
|
* every live flow keyed by its id. @world is NEVER here - the host
|
|
330
431
|
* saves its container, each engine saves its own envelope. */
|
|
331
432
|
saveGame(): SaveEnvelope;
|
|
433
|
+
/** ONE flow's blob, to park a visit that is walking away: the same shape
|
|
434
|
+
* the envelope carries per flow, and the same shape `openFlow`'s `restore`
|
|
435
|
+
* option takes back (design/engine-server.md 4.1). Saving the whole
|
|
436
|
+
* envelope to park one of four hundred players is wrong in cost and in
|
|
437
|
+
* meaning. Throws for a name that is not open - a closed flow has nothing
|
|
438
|
+
* left to save. */
|
|
439
|
+
saveFlow(id: string): FlowSave;
|
|
440
|
+
/** What `loadGame(envelope)` would do that is not a plain restore, without
|
|
441
|
+
* doing any of it (design/engine-server.md 4.9). Pure: nothing on this
|
|
442
|
+
* engine moves. A project mismatch is refused here exactly as `loadGame`
|
|
443
|
+
* refuses it - it is the one thing neither call will tolerate. */
|
|
444
|
+
previewLoad(envelope: SaveEnvelope): LoadReport;
|
|
445
|
+
/** What `openFlow(id, { restore: saved })` would do to a flow of that name,
|
|
446
|
+
* without doing it: the same report shape, since a visit parked under one
|
|
447
|
+
* build and resumed under the next raises the same questions. Pure. */
|
|
448
|
+
previewFlowRestore(id: string, saved: FlowSave): LoadReport;
|
|
332
449
|
/** Restore: shared state once, then every flow REBUILT from its blob.
|
|
333
450
|
* Handles held from before the load are closed and inert (Patter's
|
|
334
|
-
* rule); take fresh ones from getFlow()/flows().
|
|
335
|
-
|
|
451
|
+
* rule); take fresh ones from getFlow()/flows().
|
|
452
|
+
*
|
|
453
|
+
* Returns the report `previewLoad` would have given for this envelope: the
|
|
454
|
+
* drift tolerance that makes a load forgiving is what hides its cost, so
|
|
455
|
+
* the cost comes back with the load whether or not anybody looked first. */
|
|
456
|
+
loadGame(envelope: SaveEnvelope): LoadReport;
|
|
457
|
+
private assertSameProject;
|
|
458
|
+
/** The whole-envelope walk: the report, and the cleaned state the apply
|
|
459
|
+
* half writes. Nothing here touches the engine, which is what lets
|
|
460
|
+
* previewLoad and loadGame share it. */
|
|
461
|
+
private planLoad;
|
|
462
|
+
/** One flow's walk. `otherClaims` is the rest of the world's shared ledger
|
|
463
|
+
* and is present only for a SINGLE-flow restore into a live engine: a
|
|
464
|
+
* whole-envelope load rebuilds every flow from one consistent moment, so
|
|
465
|
+
* there is nobody else to compete with. */
|
|
466
|
+
private planFlowRestore;
|
|
336
467
|
}
|
|
337
468
|
declare class Flow {
|
|
338
469
|
readonly id: string;
|
|
@@ -432,6 +563,23 @@ declare class Flow {
|
|
|
432
563
|
/** A peek's ask: raw criteria ({group gameId: tag gameId}), bindings only,
|
|
433
564
|
* no condition slot (schema 3.1; the boundary, Reboot 4). */
|
|
434
565
|
private askForPeek;
|
|
566
|
+
/**
|
|
567
|
+
* Fill one hole from the property its value names: the hand that moves
|
|
568
|
+
* (design/engine-server.md 4.6).
|
|
569
|
+
*
|
|
570
|
+
* The semantics are `bindStateGroups`' below, word for word, applied per
|
|
571
|
+
* HOLE instead of per group: resolved at ask time, and a value naming no tag
|
|
572
|
+
* leaves the hole UNBOUND (a wildcard) with a diagnostic rather than dealing
|
|
573
|
+
* a silently empty hand. What is added is the `@hand` scope - the asking
|
|
574
|
+
* hand's OWN declared state, read here from the flow's merged view (the
|
|
575
|
+
* shared half under the flow's own, so a `shared: true` declaration moves
|
|
576
|
+
* the hole for every flow and a per-flow one moves it for this flow alone).
|
|
577
|
+
*
|
|
578
|
+
* Read BEFORE tag composition, which is the whole reason it is safe: the
|
|
579
|
+
* @hand bag a card sees is built from the bound tags, so resolving a hole
|
|
580
|
+
* from it would be circular. A hand's own declarations are not, so they are.
|
|
581
|
+
*/
|
|
582
|
+
private fillHoleFromProperty;
|
|
435
583
|
/**
|
|
436
584
|
* Bind every `boundBy` group in the box from the property it names.
|
|
437
585
|
*
|
|
@@ -477,7 +625,9 @@ declare class Flow {
|
|
|
477
625
|
* null for available. `trace` (when a subscriber exists) collects the
|
|
478
626
|
* per-card verdicts. */
|
|
479
627
|
private runAsk;
|
|
480
|
-
/** Flip eligible-but-not-taken trace entries to "capped".
|
|
628
|
+
/** Flip eligible-but-not-taken trace entries to "capped". `taken` is keyed
|
|
629
|
+
* by GAMEID, as the trace rows are (4.4): the two must move together or
|
|
630
|
+
* every dealt card silently reads as capped. */
|
|
481
631
|
private capTrace;
|
|
482
632
|
private view;
|
|
483
633
|
private handCapacity;
|
|
@@ -512,6 +662,8 @@ declare class Flow {
|
|
|
512
662
|
* board (you never play a card from inside the deck). Throws before any
|
|
513
663
|
* mutation on a gated-shut outcome or a bad write target. */
|
|
514
664
|
play(cardId: string, outcomeGameId: string, from: string, opts?: PlayOptions): void;
|
|
665
|
+
/** One owned property's address, owner segment and all (4.4). */
|
|
666
|
+
private address;
|
|
515
667
|
/** Land one change in whichever partition declares the name: the flow's
|
|
516
668
|
* bag when the property is per-flow, the shared bag when it is shared -
|
|
517
669
|
* the union/partition invariant made executable. */
|
|
@@ -536,9 +688,14 @@ declare class Flow {
|
|
|
536
688
|
* Bundle order: world, story, then per-box / per-deck / per-hand /
|
|
537
689
|
* per-tag stores. */
|
|
538
690
|
listProperties(): PropertyRow[];
|
|
539
|
-
/** Read by path: "world.x", "story.gold", "value.
|
|
540
|
-
* "box.
|
|
541
|
-
* merged view, routed by the declaration's sharing.
|
|
691
|
+
/** Read by path: "world.x", "story.gold", "value.docks.danger",
|
|
692
|
+
* "box.village.heat", "deck.wares.n", "hand.the-elder.zone" - the flow's
|
|
693
|
+
* merged view, routed by the declaration's sharing.
|
|
694
|
+
*
|
|
695
|
+
* The owner segment is the entity's GAMEID, the name it is called by
|
|
696
|
+
* everywhere else (4.4). Its internal id is accepted for this release and
|
|
697
|
+
* earns a `diagnostic` naming the address to move to; the next lockstep
|
|
698
|
+
* release refuses it. */
|
|
542
699
|
getProperty(path: string): ScalarValue;
|
|
543
700
|
setProperty(path: string, value: ScalarValue): void;
|
|
544
701
|
private resolvePath;
|
|
@@ -562,6 +719,13 @@ interface BundleIdentity {
|
|
|
562
719
|
/** "full" | "stripped": whether authoring metadata (titles) survived. */
|
|
563
720
|
metadata: string;
|
|
564
721
|
}
|
|
722
|
+
/** One hole this hand fills from a property rather than with a tag: the hand
|
|
723
|
+
* MOVES when that property is written (design/engine-server.md 4.6). `group`
|
|
724
|
+
* is the tag group's gameId, `from` the reference exactly as authored. */
|
|
725
|
+
interface MovableHole {
|
|
726
|
+
group: string;
|
|
727
|
+
from: string;
|
|
728
|
+
}
|
|
565
729
|
/** One hand: the deal() surface. `gameId` is the name deal() is called with. */
|
|
566
730
|
interface HandSummary {
|
|
567
731
|
gameId: string;
|
|
@@ -573,6 +737,16 @@ interface HandSummary {
|
|
|
573
737
|
slots: number | "unbounded";
|
|
574
738
|
/** The hand template's gameId; absent for a standalone (inline-rule) hand. */
|
|
575
739
|
template?: string;
|
|
740
|
+
/**
|
|
741
|
+
* The holes filled from a property, in bundle order. Absent when the hand
|
|
742
|
+
* has none, which is the ordinary case.
|
|
743
|
+
*
|
|
744
|
+
* Reported because it is the one thing about a hand an integrator cannot see
|
|
745
|
+
* from its name: a movable hole means writing that property MOVES the hand,
|
|
746
|
+
* so it is the difference between a fixed kiosk and a performer who walks
|
|
747
|
+
* about. `setProperty` is the whole verb; there is no other.
|
|
748
|
+
*/
|
|
749
|
+
movable?: MovableHole[];
|
|
576
750
|
}
|
|
577
751
|
/** One tag group and its tags, by gameId: the peek() criteria surface (a
|
|
578
752
|
* criteria entry is `{ [group gameId]: tag gameId }`). */
|
|
@@ -588,6 +762,19 @@ interface BoxSummary {
|
|
|
588
762
|
ranking: {
|
|
589
763
|
specificity: boolean;
|
|
590
764
|
};
|
|
765
|
+
/** Present on a TIMED box (design/engine-server.md 4.8): how long one of
|
|
766
|
+
* its turns lasts. An integrator reading a bundle needs it to know which
|
|
767
|
+
* boxes their host must tick, and how often. Absent is the ordinary box. */
|
|
768
|
+
turn?: {
|
|
769
|
+
seconds: number;
|
|
770
|
+
};
|
|
771
|
+
/** How many cards in this box are DURABLE (design/engine-server.md 4.2):
|
|
772
|
+
* their `redraw: "never"` spend outlives the run, and a server has to lift
|
|
773
|
+
* and restore it. A count rather than a list, like every other number here:
|
|
774
|
+
* an integrator needs to know whether this box has any such cards at all,
|
|
775
|
+
* and which ones is the authoring tool's question. Absent when there are
|
|
776
|
+
* none, which is the ordinary bundle. */
|
|
777
|
+
durableCards?: number;
|
|
591
778
|
tagGroups: TagGroupSummary[];
|
|
592
779
|
counts: {
|
|
593
780
|
decks: number;
|
|
@@ -604,6 +791,11 @@ interface PropertySummary {
|
|
|
604
791
|
default: ScalarValue$1;
|
|
605
792
|
/** Enum / flags options, where declared. */
|
|
606
793
|
values?: string[];
|
|
794
|
+
/** Declared DURABLE (design/engine-server.md 4.2): the value survives a run,
|
|
795
|
+
* and a server lifts and restores it across one. The engine never reads it;
|
|
796
|
+
* it is reported because it is the difference between a value an integrator
|
|
797
|
+
* may reset and one somebody is going to expect back. Absent = run-scoped. */
|
|
798
|
+
durable?: true;
|
|
607
799
|
purpose?: string;
|
|
608
800
|
}
|
|
609
801
|
/** The scope a declaration block belongs to. `tag` declarations compose into
|
|
@@ -634,6 +826,9 @@ interface MapSummary {
|
|
|
634
826
|
group: string;
|
|
635
827
|
zones: number;
|
|
636
828
|
backgrounds: number;
|
|
829
|
+
/** Placed hands standing on this map (design/engine-server.md 4.3): where the
|
|
830
|
+
* kiosks are, in a bundle that carries geometry at all. */
|
|
831
|
+
sites: number;
|
|
637
832
|
}
|
|
638
833
|
/** What a bundle offers a host, read from the asset alone. */
|
|
639
834
|
interface BundleDescription {
|
|
@@ -663,4 +858,4 @@ interface BundleDescription {
|
|
|
663
858
|
* throughout; the same shape every runtime returns. */
|
|
664
859
|
declare function describeBundle(bundle: Bundle): BundleDescription;
|
|
665
860
|
|
|
666
|
-
export { type BagMount, type BoxSummary, type BoxView, type BundleDescription, type BundleIdentity, type DealtCard, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, Flow, type HandSummary, type LogEntry, type MapSummary, type OpenFlowOptions, type OutcomeView, type PlayOptions, type PropertyScopeKind, type PropertyScopeSummary, type PropertySummary, type RankedList, type TagGroupSummary, type TraceEvent, type TraceHandler, type TraceVerdict, describeBundle };
|
|
861
|
+
export { type BagMount, type BoxSummary, type BoxView, type BundleDescription, type BundleIdentity, type DealtCard, Engine, type EngineLogEntry, type EngineOptions, type EngineTraceHandler, Flow, type HandSummary, type LogEntry, type MapSummary, type MovableHole, type OpenFlowOptions, type OutcomeView, type PlayOptions, type PropertyScopeKind, type PropertyScopeSummary, type PropertySummary, type RankedList, type TagGroupSummary, type TraceEvent, type TraceHandler, type TraceVerdict, describeBundle };
|