@sharpee/engine 0.9.113 → 1.0.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/action-context-factory.d.ts +10 -2
- package/action-context-factory.d.ts.map +1 -1
- package/action-context-factory.js +29 -5
- package/action-context-factory.js.map +1 -1
- package/command-executor.d.ts +2 -1
- package/command-executor.d.ts.map +1 -1
- package/command-executor.js +3 -3
- package/command-executor.js.map +1 -1
- package/game-engine.d.ts +87 -4
- package/game-engine.d.ts.map +1 -1
- package/game-engine.js +239 -8
- package/game-engine.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +2 -0
- package/index.js.map +1 -1
- package/package.json +12 -12
- package/prose-pipeline/assemble.d.ts +77 -0
- package/prose-pipeline/assemble.d.ts.map +1 -0
- package/prose-pipeline/assemble.js +96 -0
- package/prose-pipeline/assemble.js.map +1 -0
- package/prose-pipeline/decorations/index.d.ts +18 -0
- package/prose-pipeline/decorations/index.d.ts.map +1 -0
- package/prose-pipeline/decorations/index.js +24 -0
- package/prose-pipeline/decorations/index.js.map +1 -0
- package/prose-pipeline/decorations/parser.d.ts +34 -0
- package/prose-pipeline/decorations/parser.d.ts.map +1 -0
- package/prose-pipeline/decorations/parser.js +177 -0
- package/prose-pipeline/decorations/parser.js.map +1 -0
- package/prose-pipeline/decorations/platform-vocabulary.d.ts +27 -0
- package/prose-pipeline/decorations/platform-vocabulary.d.ts.map +1 -0
- package/prose-pipeline/decorations/platform-vocabulary.js +65 -0
- package/prose-pipeline/decorations/platform-vocabulary.js.map +1 -0
- package/prose-pipeline/decorations/resolver.d.ts +28 -0
- package/prose-pipeline/decorations/resolver.d.ts.map +1 -0
- package/prose-pipeline/decorations/resolver.js +39 -0
- package/prose-pipeline/decorations/resolver.js.map +1 -0
- package/prose-pipeline/decorations/types.d.ts +16 -0
- package/prose-pipeline/decorations/types.d.ts.map +1 -0
- package/prose-pipeline/decorations/types.js +17 -0
- package/prose-pipeline/decorations/types.js.map +1 -0
- package/prose-pipeline/handlers/about.d.ts +31 -0
- package/prose-pipeline/handlers/about.d.ts.map +1 -0
- package/prose-pipeline/handlers/about.js +50 -0
- package/prose-pipeline/handlers/about.js.map +1 -0
- package/prose-pipeline/handlers/audibility.d.ts +46 -0
- package/prose-pipeline/handlers/audibility.d.ts.map +1 -0
- package/prose-pipeline/handlers/audibility.js +79 -0
- package/prose-pipeline/handlers/audibility.js.map +1 -0
- package/prose-pipeline/handlers/banner.d.ts +44 -0
- package/prose-pipeline/handlers/banner.d.ts.map +1 -0
- package/prose-pipeline/handlers/banner.js +88 -0
- package/prose-pipeline/handlers/banner.js.map +1 -0
- package/prose-pipeline/handlers/client-query.d.ts +22 -0
- package/prose-pipeline/handlers/client-query.d.ts.map +1 -0
- package/prose-pipeline/handlers/client-query.js +55 -0
- package/prose-pipeline/handlers/client-query.js.map +1 -0
- package/prose-pipeline/handlers/command-failed.d.ts +24 -0
- package/prose-pipeline/handlers/command-failed.d.ts.map +1 -0
- package/prose-pipeline/handlers/command-failed.js +46 -0
- package/prose-pipeline/handlers/command-failed.js.map +1 -0
- package/prose-pipeline/handlers/domain-message.d.ts +31 -0
- package/prose-pipeline/handlers/domain-message.d.ts.map +1 -0
- package/prose-pipeline/handlers/domain-message.js +66 -0
- package/prose-pipeline/handlers/domain-message.js.map +1 -0
- package/prose-pipeline/handlers/game.d.ts +31 -0
- package/prose-pipeline/handlers/game.d.ts.map +1 -0
- package/prose-pipeline/handlers/game.js +35 -0
- package/prose-pipeline/handlers/game.js.map +1 -0
- package/prose-pipeline/handlers/generic.d.ts +27 -0
- package/prose-pipeline/handlers/generic.d.ts.map +1 -0
- package/prose-pipeline/handlers/generic.js +67 -0
- package/prose-pipeline/handlers/generic.js.map +1 -0
- package/prose-pipeline/handlers/help.d.ts +21 -0
- package/prose-pipeline/handlers/help.d.ts.map +1 -0
- package/prose-pipeline/handlers/help.js +68 -0
- package/prose-pipeline/handlers/help.js.map +1 -0
- package/prose-pipeline/handlers/implicit-take.d.ts +20 -0
- package/prose-pipeline/handlers/implicit-take.d.ts.map +1 -0
- package/prose-pipeline/handlers/implicit-take.js +26 -0
- package/prose-pipeline/handlers/implicit-take.js.map +1 -0
- package/prose-pipeline/handlers/index.d.ts +24 -0
- package/prose-pipeline/handlers/index.d.ts.map +1 -0
- package/prose-pipeline/handlers/index.js +40 -0
- package/prose-pipeline/handlers/index.js.map +1 -0
- package/prose-pipeline/handlers/revealed.d.ts +24 -0
- package/prose-pipeline/handlers/revealed.d.ts.map +1 -0
- package/prose-pipeline/handlers/revealed.js +54 -0
- package/prose-pipeline/handlers/revealed.js.map +1 -0
- package/prose-pipeline/handlers/room.d.ts +25 -0
- package/prose-pipeline/handlers/room.d.ts.map +1 -0
- package/prose-pipeline/handlers/room.js +78 -0
- package/prose-pipeline/handlers/room.js.map +1 -0
- package/prose-pipeline/handlers/types.d.ts +47 -0
- package/prose-pipeline/handlers/types.d.ts.map +1 -0
- package/prose-pipeline/handlers/types.js +15 -0
- package/prose-pipeline/handlers/types.js.map +1 -0
- package/prose-pipeline/index.d.ts +20 -0
- package/prose-pipeline/index.d.ts.map +1 -0
- package/prose-pipeline/index.js +50 -0
- package/prose-pipeline/index.js.map +1 -0
- package/prose-pipeline/pipeline.d.ts +58 -0
- package/prose-pipeline/pipeline.d.ts.map +1 -0
- package/prose-pipeline/pipeline.js +120 -0
- package/prose-pipeline/pipeline.js.map +1 -0
- package/prose-pipeline/stages/filter.d.ts +26 -0
- package/prose-pipeline/stages/filter.d.ts.map +1 -0
- package/prose-pipeline/stages/filter.js +38 -0
- package/prose-pipeline/stages/filter.js.map +1 -0
- package/prose-pipeline/stages/index.d.ts +13 -0
- package/prose-pipeline/stages/index.d.ts.map +1 -0
- package/prose-pipeline/stages/index.js +19 -0
- package/prose-pipeline/stages/index.js.map +1 -0
- package/prose-pipeline/stages/sort.d.ts +38 -0
- package/prose-pipeline/stages/sort.d.ts.map +1 -0
- package/prose-pipeline/stages/sort.js +94 -0
- package/prose-pipeline/stages/sort.js.map +1 -0
- package/prose-pipeline/types.d.ts +45 -0
- package/prose-pipeline/types.d.ts.map +1 -0
- package/prose-pipeline/types.js +23 -0
- package/prose-pipeline/types.js.map +1 -0
- package/sound/dispatcher.d.ts +75 -0
- package/sound/dispatcher.d.ts.map +1 -0
- package/sound/dispatcher.js +122 -0
- package/sound/dispatcher.js.map +1 -0
- package/sound/index.d.ts +26 -0
- package/sound/index.d.ts.map +1 -0
- package/sound/index.js +34 -0
- package/sound/index.js.map +1 -0
- package/sound/propagation.d.ts +69 -0
- package/sound/propagation.d.ts.map +1 -0
- package/sound/propagation.js +219 -0
- package/sound/propagation.js.map +1 -0
- package/story.d.ts +28 -1
- package/story.d.ts.map +1 -1
- package/story.js.map +1 -1
- package/test-helpers/mock-prose-pipeline.d.ts +22 -0
- package/test-helpers/mock-prose-pipeline.d.ts.map +1 -0
- package/test-helpers/mock-prose-pipeline.js +56 -0
- package/test-helpers/mock-prose-pipeline.js.map +1 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Event sorting stage — orders events within a turn for correct prose
|
|
4
|
+
* sequence: lifecycle first, then per-transaction implicit-take →
|
|
5
|
+
* room-description → action.* → others, finally by chain depth.
|
|
6
|
+
*
|
|
7
|
+
* Public interface: `sortEventsForProse`, `getChainMetadata`. Used
|
|
8
|
+
* internally by the prose pipeline as the second per-turn stage,
|
|
9
|
+
* after filtering.
|
|
10
|
+
*
|
|
11
|
+
* Owner context: `@sharpee/engine` — internal prose pipeline.
|
|
12
|
+
*
|
|
13
|
+
* @see ADR-094 Event Chaining (preserved; the sort responsibility
|
|
14
|
+
* ports from `@sharpee/text-service`)
|
|
15
|
+
* @see ADR-174 §Engine-internal prose pipeline
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.sortEventsForProse = sortEventsForProse;
|
|
19
|
+
exports.getChainMetadata = getChainMetadata;
|
|
20
|
+
const LIFECYCLE_EVENTS = new Set([
|
|
21
|
+
'game.started',
|
|
22
|
+
'game.starting',
|
|
23
|
+
'game.loading',
|
|
24
|
+
'game.loaded',
|
|
25
|
+
'game.initialized',
|
|
26
|
+
]);
|
|
27
|
+
/**
|
|
28
|
+
* Sort events for correct prose order within transactions.
|
|
29
|
+
*
|
|
30
|
+
* Stable sort — preserves cross-transaction order while applying
|
|
31
|
+
* within-transaction rules.
|
|
32
|
+
*/
|
|
33
|
+
function sortEventsForProse(events) {
|
|
34
|
+
return [...events].sort((a, b) => {
|
|
35
|
+
const aData = a.data;
|
|
36
|
+
const bData = b.data;
|
|
37
|
+
const aTxnId = aData?._transactionId;
|
|
38
|
+
const bTxnId = bData?._transactionId;
|
|
39
|
+
// Game lifecycle events come first, before everything.
|
|
40
|
+
// Ensures the banner displays before the first room description.
|
|
41
|
+
// Note: matches specific lifecycle events only, NOT game.message.
|
|
42
|
+
const aIsGameLifecycle = LIFECYCLE_EVENTS.has(a.type);
|
|
43
|
+
const bIsGameLifecycle = LIFECYCLE_EVENTS.has(b.type);
|
|
44
|
+
if (aIsGameLifecycle && !bIsGameLifecycle)
|
|
45
|
+
return -1;
|
|
46
|
+
if (!aIsGameLifecycle && bIsGameLifecycle)
|
|
47
|
+
return 1;
|
|
48
|
+
// Different transactions or no transaction: maintain original order.
|
|
49
|
+
if (aTxnId !== bTxnId)
|
|
50
|
+
return 0;
|
|
51
|
+
// Implicit take events come first, before the main action result:
|
|
52
|
+
// "first taking the X" before "X reads:..."
|
|
53
|
+
const aIsImplicitTake = a.type === 'if.event.implicit_take';
|
|
54
|
+
const bIsImplicitTake = b.type === 'if.event.implicit_take';
|
|
55
|
+
if (aIsImplicitTake && !bIsImplicitTake)
|
|
56
|
+
return -1;
|
|
57
|
+
if (!aIsImplicitTake && bIsImplicitTake)
|
|
58
|
+
return 1;
|
|
59
|
+
// Room description before action.success (for contents list):
|
|
60
|
+
// "Room Name\nDescription" before "You see X here."
|
|
61
|
+
const aIsRoomDesc = a.type === 'if.event.room.description' ||
|
|
62
|
+
a.type === 'if.event.room_description';
|
|
63
|
+
const bIsRoomDesc = b.type === 'if.event.room.description' ||
|
|
64
|
+
b.type === 'if.event.room_description';
|
|
65
|
+
if (aIsRoomDesc && !bIsRoomDesc)
|
|
66
|
+
return -1;
|
|
67
|
+
if (!aIsRoomDesc && bIsRoomDesc)
|
|
68
|
+
return 1;
|
|
69
|
+
// Within transaction: action.* first (after room description).
|
|
70
|
+
const aIsAction = a.type.startsWith('action.');
|
|
71
|
+
const bIsAction = b.type.startsWith('action.');
|
|
72
|
+
if (aIsAction && !bIsAction)
|
|
73
|
+
return -1;
|
|
74
|
+
if (!aIsAction && bIsAction)
|
|
75
|
+
return 1;
|
|
76
|
+
// Then by chain depth (lower depth first).
|
|
77
|
+
const aDepth = aData?._chainDepth ?? 0;
|
|
78
|
+
const bDepth = bData?._chainDepth ?? 0;
|
|
79
|
+
return aDepth - bDepth;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Extract chain metadata from event data.
|
|
84
|
+
*/
|
|
85
|
+
function getChainMetadata(event) {
|
|
86
|
+
const data = event.data;
|
|
87
|
+
return {
|
|
88
|
+
_transactionId: data?._transactionId,
|
|
89
|
+
_chainDepth: data?._chainDepth,
|
|
90
|
+
_chainedFrom: data?._chainedFrom,
|
|
91
|
+
_chainSourceId: data?._chainSourceId,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=sort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort.js","sourceRoot":"","sources":["../../../../../../../../repos/sharpee/packages/engine/src/prose-pipeline/stages/sort.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prose pipeline service interface — engine-internal home for the
|
|
3
|
+
* `ITextService` contract.
|
|
4
|
+
*
|
|
5
|
+
* Per ADR-174, the responsibility for translating events into blocks
|
|
6
|
+
* moves into `@sharpee/engine`. The transitional `ITextService` name
|
|
7
|
+
* is preserved here so engine code already typed against it (the
|
|
8
|
+
* three `processTurn` call sites in `game-engine.ts` and the
|
|
9
|
+
* `getTextService` / `setTextService` accessors) can keep compiling
|
|
10
|
+
* during sub-phase 1.6's import swap. A follow-up cleanup may rename
|
|
11
|
+
* this to `IProsePipeline` once the legacy alias is no longer
|
|
12
|
+
* imported anywhere.
|
|
13
|
+
*
|
|
14
|
+
* Public interface: `ITextService`. Implemented by `ProsePipeline`
|
|
15
|
+
* (and by `MockProsePipeline` in tests).
|
|
16
|
+
*
|
|
17
|
+
* Owner context: `@sharpee/engine` — internal prose pipeline.
|
|
18
|
+
*
|
|
19
|
+
* @see ADR-174 §Internal interfaces
|
|
20
|
+
*/
|
|
21
|
+
import type { ISemanticEvent } from "../../core/index";
|
|
22
|
+
import type { ITextBlock } from "../../text-blocks/index";
|
|
23
|
+
/**
|
|
24
|
+
* Per-turn prose translator.
|
|
25
|
+
*
|
|
26
|
+
* Stateless transformer: takes the events emitted during a turn,
|
|
27
|
+
* returns the structured `ITextBlock[]` the channel layer hands off
|
|
28
|
+
* to renderers. Engine constructs an implementation once during
|
|
29
|
+
* `setStory()` and calls `processTurn` per turn (and per
|
|
30
|
+
* meta-command / restart).
|
|
31
|
+
*/
|
|
32
|
+
export interface ITextService {
|
|
33
|
+
/**
|
|
34
|
+
* Process turn events and produce TextBlocks.
|
|
35
|
+
*
|
|
36
|
+
* Called by Engine after each turn completes — and again on the
|
|
37
|
+
* meta-command path (restart, restore) where the same per-turn
|
|
38
|
+
* shape applies.
|
|
39
|
+
*
|
|
40
|
+
* @param events All events from this turn, including chained ones.
|
|
41
|
+
* @returns Blocks in render order.
|
|
42
|
+
*/
|
|
43
|
+
processTurn(events: ISemanticEvent[]): ITextBlock[];
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/engine/src/prose-pipeline/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;OASG;IACH,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,UAAU,EAAE,CAAC;CACrD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Prose pipeline service interface — engine-internal home for the
|
|
4
|
+
* `ITextService` contract.
|
|
5
|
+
*
|
|
6
|
+
* Per ADR-174, the responsibility for translating events into blocks
|
|
7
|
+
* moves into `@sharpee/engine`. The transitional `ITextService` name
|
|
8
|
+
* is preserved here so engine code already typed against it (the
|
|
9
|
+
* three `processTurn` call sites in `game-engine.ts` and the
|
|
10
|
+
* `getTextService` / `setTextService` accessors) can keep compiling
|
|
11
|
+
* during sub-phase 1.6's import swap. A follow-up cleanup may rename
|
|
12
|
+
* this to `IProsePipeline` once the legacy alias is no longer
|
|
13
|
+
* imported anywhere.
|
|
14
|
+
*
|
|
15
|
+
* Public interface: `ITextService`. Implemented by `ProsePipeline`
|
|
16
|
+
* (and by `MockProsePipeline` in tests).
|
|
17
|
+
*
|
|
18
|
+
* Owner context: `@sharpee/engine` — internal prose pipeline.
|
|
19
|
+
*
|
|
20
|
+
* @see ADR-174 §Internal interfaces
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/engine/src/prose-pipeline/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-turn audibility dispatcher (ADR-172 Phase 6).
|
|
3
|
+
*
|
|
4
|
+
* Closes the loop between `emitSound` (authoring API; Step 6.1) and the
|
|
5
|
+
* `audibility` channel (Phase 5). For each `ISound` buffered during a
|
|
6
|
+
* turn, the dispatcher walks every entity carrying `ListenerTrait`,
|
|
7
|
+
* calls `propagate(sound, listenerId, world, timestamp)`, and emits one
|
|
8
|
+
* `sound.audibility.heard` `ISemanticEvent` per (sound × listener) pair
|
|
9
|
+
* that produced a non-null `IAudibilityEvent`.
|
|
10
|
+
*
|
|
11
|
+
* The dispatcher is pure: same buffer + same world state + same
|
|
12
|
+
* timestamp → same event array. Listeners are processed in entity-id
|
|
13
|
+
* sort order for deterministic event ordering across turns and runs.
|
|
14
|
+
*
|
|
15
|
+
* Owner context: `@sharpee/engine` — runtime / sound subsystem.
|
|
16
|
+
*
|
|
17
|
+
* Public interface:
|
|
18
|
+
* - `class SoundDispatcher` — the dispatcher itself.
|
|
19
|
+
* - `SoundDispatcher.dispatch(buffer, world, timestamp)` — produces
|
|
20
|
+
* the `sound.audibility.heard` events for the turn.
|
|
21
|
+
*
|
|
22
|
+
* @see ADR-172 — Spatial Sound Propagation
|
|
23
|
+
* @see ADR-163 — Channel-Service Platform (audibility channel)
|
|
24
|
+
*/
|
|
25
|
+
import { type ISemanticEvent } from "../../core/index";
|
|
26
|
+
import type { IAudibilityEvent, ISound } from "../../if-domain/index";
|
|
27
|
+
import { type WorldModel } from "../../world-model/index";
|
|
28
|
+
/**
|
|
29
|
+
* Semantic-event type fired by the dispatcher when a listener perceives
|
|
30
|
+
* a propagated sound. Mirrors `SOUND_EVENT_TYPES.AUDIBILITY_HEARD` in
|
|
31
|
+
* `@sharpee/stdlib/channels/sound-events`. The constant is duplicated
|
|
32
|
+
* here as a string literal so the engine package does not depend on
|
|
33
|
+
* stdlib at compile time (engine → stdlib is the existing dependency
|
|
34
|
+
* direction; the inverse would be a cycle). The string value is the
|
|
35
|
+
* contract — both sides must agree.
|
|
36
|
+
*/
|
|
37
|
+
export declare const AUDIBILITY_HEARD_EVENT_TYPE = "sound.audibility.heard";
|
|
38
|
+
/**
|
|
39
|
+
* Per-turn audibility dispatcher.
|
|
40
|
+
*
|
|
41
|
+
* The class shape (rather than a free function) leaves room for a
|
|
42
|
+
* future propagate-injection point in tests and for caching listener
|
|
43
|
+
* lookups across multi-action turns. For Phase 6 the dispatcher is a
|
|
44
|
+
* thin wrapper around `propagate()`; the class structure is the
|
|
45
|
+
* extension seam for L2's "NPC voice profile" layer.
|
|
46
|
+
*/
|
|
47
|
+
export declare class SoundDispatcher {
|
|
48
|
+
/**
|
|
49
|
+
* The propagation function the dispatcher uses. Defaulted to the
|
|
50
|
+
* production `propagate` from `./propagation`; tests may inject a
|
|
51
|
+
* fake to isolate dispatcher behavior from edge-graph + Dijkstra
|
|
52
|
+
* complexity.
|
|
53
|
+
*/
|
|
54
|
+
private readonly propagate;
|
|
55
|
+
constructor(propagateFn?: (sound: ISound, listenerId: string, world: WorldModel, timestamp: number) => IAudibilityEvent | null);
|
|
56
|
+
/**
|
|
57
|
+
* Dispatch every buffered sound to every listener.
|
|
58
|
+
*
|
|
59
|
+
* @param buffer The per-turn sound buffer; iterated in insertion
|
|
60
|
+
* order. May be empty (quiet turn).
|
|
61
|
+
* @param world The world model the propagation function reads
|
|
62
|
+
* from. Must be the same instance the actions
|
|
63
|
+
* mutated during the turn.
|
|
64
|
+
* @param timestamp The turn-sequence integer the engine assigns to
|
|
65
|
+
* this turn. Used as the `IAudibilityEvent.timestamp`
|
|
66
|
+
* for ordering across multi-emission turns.
|
|
67
|
+
* @returns Array of `sound.audibility.heard` events, one per
|
|
68
|
+
* (sound × listener) where `propagate()` returned
|
|
69
|
+
* non-null. Order: outer iteration over the buffer in
|
|
70
|
+
* emission order, inner iteration over listeners
|
|
71
|
+
* sorted by entity id ascending.
|
|
72
|
+
*/
|
|
73
|
+
dispatch(buffer: readonly ISound[], world: WorldModel, timestamp: number): ISemanticEvent[];
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/engine/src/sound/dispatcher.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGjF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AAEpE;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAKG;gBAG3B,WAAW,GAAE,CACX,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,KACd,gBAAgB,GAAG,IAAuB;IAKjD;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CACN,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,GAChB,cAAc,EAAE;CAsBpB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// packages/engine/src/sound/dispatcher.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SoundDispatcher = exports.AUDIBILITY_HEARD_EVENT_TYPE = void 0;
|
|
5
|
+
/**
|
|
6
|
+
* Per-turn audibility dispatcher (ADR-172 Phase 6).
|
|
7
|
+
*
|
|
8
|
+
* Closes the loop between `emitSound` (authoring API; Step 6.1) and the
|
|
9
|
+
* `audibility` channel (Phase 5). For each `ISound` buffered during a
|
|
10
|
+
* turn, the dispatcher walks every entity carrying `ListenerTrait`,
|
|
11
|
+
* calls `propagate(sound, listenerId, world, timestamp)`, and emits one
|
|
12
|
+
* `sound.audibility.heard` `ISemanticEvent` per (sound × listener) pair
|
|
13
|
+
* that produced a non-null `IAudibilityEvent`.
|
|
14
|
+
*
|
|
15
|
+
* The dispatcher is pure: same buffer + same world state + same
|
|
16
|
+
* timestamp → same event array. Listeners are processed in entity-id
|
|
17
|
+
* sort order for deterministic event ordering across turns and runs.
|
|
18
|
+
*
|
|
19
|
+
* Owner context: `@sharpee/engine` — runtime / sound subsystem.
|
|
20
|
+
*
|
|
21
|
+
* Public interface:
|
|
22
|
+
* - `class SoundDispatcher` — the dispatcher itself.
|
|
23
|
+
* - `SoundDispatcher.dispatch(buffer, world, timestamp)` — produces
|
|
24
|
+
* the `sound.audibility.heard` events for the turn.
|
|
25
|
+
*
|
|
26
|
+
* @see ADR-172 — Spatial Sound Propagation
|
|
27
|
+
* @see ADR-163 — Channel-Service Platform (audibility channel)
|
|
28
|
+
*/
|
|
29
|
+
const core_1 = require("../../core/index.js");
|
|
30
|
+
const world_model_1 = require("../../world-model/index.js");
|
|
31
|
+
const propagation_1 = require("./propagation");
|
|
32
|
+
/**
|
|
33
|
+
* Semantic-event type fired by the dispatcher when a listener perceives
|
|
34
|
+
* a propagated sound. Mirrors `SOUND_EVENT_TYPES.AUDIBILITY_HEARD` in
|
|
35
|
+
* `@sharpee/stdlib/channels/sound-events`. The constant is duplicated
|
|
36
|
+
* here as a string literal so the engine package does not depend on
|
|
37
|
+
* stdlib at compile time (engine → stdlib is the existing dependency
|
|
38
|
+
* direction; the inverse would be a cycle). The string value is the
|
|
39
|
+
* contract — both sides must agree.
|
|
40
|
+
*/
|
|
41
|
+
exports.AUDIBILITY_HEARD_EVENT_TYPE = 'sound.audibility.heard';
|
|
42
|
+
/**
|
|
43
|
+
* Per-turn audibility dispatcher.
|
|
44
|
+
*
|
|
45
|
+
* The class shape (rather than a free function) leaves room for a
|
|
46
|
+
* future propagate-injection point in tests and for caching listener
|
|
47
|
+
* lookups across multi-action turns. For Phase 6 the dispatcher is a
|
|
48
|
+
* thin wrapper around `propagate()`; the class structure is the
|
|
49
|
+
* extension seam for L2's "NPC voice profile" layer.
|
|
50
|
+
*/
|
|
51
|
+
class SoundDispatcher {
|
|
52
|
+
/**
|
|
53
|
+
* The propagation function the dispatcher uses. Defaulted to the
|
|
54
|
+
* production `propagate` from `./propagation`; tests may inject a
|
|
55
|
+
* fake to isolate dispatcher behavior from edge-graph + Dijkstra
|
|
56
|
+
* complexity.
|
|
57
|
+
*/
|
|
58
|
+
propagate;
|
|
59
|
+
constructor(propagateFn = propagation_1.propagate) {
|
|
60
|
+
this.propagate = propagateFn;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Dispatch every buffered sound to every listener.
|
|
64
|
+
*
|
|
65
|
+
* @param buffer The per-turn sound buffer; iterated in insertion
|
|
66
|
+
* order. May be empty (quiet turn).
|
|
67
|
+
* @param world The world model the propagation function reads
|
|
68
|
+
* from. Must be the same instance the actions
|
|
69
|
+
* mutated during the turn.
|
|
70
|
+
* @param timestamp The turn-sequence integer the engine assigns to
|
|
71
|
+
* this turn. Used as the `IAudibilityEvent.timestamp`
|
|
72
|
+
* for ordering across multi-emission turns.
|
|
73
|
+
* @returns Array of `sound.audibility.heard` events, one per
|
|
74
|
+
* (sound × listener) where `propagate()` returned
|
|
75
|
+
* non-null. Order: outer iteration over the buffer in
|
|
76
|
+
* emission order, inner iteration over listeners
|
|
77
|
+
* sorted by entity id ascending.
|
|
78
|
+
*/
|
|
79
|
+
dispatch(buffer, world, timestamp) {
|
|
80
|
+
if (buffer.length === 0)
|
|
81
|
+
return [];
|
|
82
|
+
const listeners = world
|
|
83
|
+
.findByTrait(world_model_1.TraitType.LISTENER)
|
|
84
|
+
.slice()
|
|
85
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
86
|
+
if (listeners.length === 0)
|
|
87
|
+
return [];
|
|
88
|
+
const events = [];
|
|
89
|
+
for (const sound of buffer) {
|
|
90
|
+
for (const listener of listeners) {
|
|
91
|
+
const audibility = this.propagate(sound, listener.id, world, timestamp);
|
|
92
|
+
if (!audibility)
|
|
93
|
+
continue;
|
|
94
|
+
events.push(buildAudibilityEvent(audibility, listener.id));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return events;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.SoundDispatcher = SoundDispatcher;
|
|
101
|
+
/**
|
|
102
|
+
* Build the `sound.audibility.heard` semantic event from a propagation
|
|
103
|
+
* result and the listener that perceived it.
|
|
104
|
+
*
|
|
105
|
+
* `entities` mapping:
|
|
106
|
+
* - `actor` → `sourceEntityId` (who emitted the sound)
|
|
107
|
+
* - `location` → `sourceRoomId` (where the sound came from)
|
|
108
|
+
* - `target` → listenerId (who heard it)
|
|
109
|
+
*
|
|
110
|
+
* The `audibility` channel projection (ADR-163, ADR-172 Phase 5) reads
|
|
111
|
+
* the `IAudibilityEvent` from `event.data`; `entities` is provided for
|
|
112
|
+
* downstream filters and handlers that want a fast "who heard / from
|
|
113
|
+
* where" lookup without re-reading `data`.
|
|
114
|
+
*/
|
|
115
|
+
function buildAudibilityEvent(audibility, listenerId) {
|
|
116
|
+
return (0, core_1.createEvent)(exports.AUDIBILITY_HEARD_EVENT_TYPE, audibility, {
|
|
117
|
+
actor: audibility.sourceEntityId,
|
|
118
|
+
location: audibility.sourceRoomId,
|
|
119
|
+
target: listenerId,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/engine/src/sound/dispatcher.ts"],"names":[],"mappings":";AAAA,0CAA0C;;;AAE1C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,wCAAiE;AAEjE,sDAAiF;AACjF,+CAA8D;AAE9D;;;;;;;;GAQG;AACU,QAAA,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAa,eAAe;IAC1B;;;;;OAKG;IACc,SAAS,CAKG;IAE7B,YACE,cAK+B,uBAAgB;QAE/C,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CACN,MAAyB,EACzB,KAAiB,EACjB,SAAiB;QAEjB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEnC,MAAM,SAAS,GAAG,KAAK;aACpB,WAAW,CAAC,uBAAS,CAAC,QAAQ,CAAC;aAC/B,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,CAAW,EAAE,CAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEtC,MAAM,MAAM,GAAqB,EAAE,CAAC;QAEpC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;gBACxE,IAAI,CAAC,UAAU;oBAAE,SAAS;gBAC1B,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AApED,0CAoEC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,oBAAoB,CAC3B,UAA4B,EAC5B,UAAkB;IAElB,OAAO,IAAA,kBAAW,EAChB,mCAA2B,EAC3B,UAAgD,EAChD;QACE,KAAK,EAAE,UAAU,CAAC,cAAc;QAChC,QAAQ,EAAE,UAAU,CAAC,YAAY;QACjC,MAAM,EAAE,UAAU;KACnB,CACF,CAAC;AACJ,CAAC"}
|
package/sound/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sharpee/engine/sound — spatial sound propagation runtime (ADR-172).
|
|
3
|
+
*
|
|
4
|
+
* Owner context: engine layer. The propagation function lives here
|
|
5
|
+
* because it consumes world-model state, doesn't fit in a pure
|
|
6
|
+
* domain-types layer, and is invoked from the action pipeline (Phase
|
|
7
|
+
* 6) which is engine-adjacent.
|
|
8
|
+
*
|
|
9
|
+
* Public interface:
|
|
10
|
+
* - `propagate(sound, listenerId, world, timestamp)` — produce an
|
|
11
|
+
* `AudibilityEvent` (or null) for a single listener.
|
|
12
|
+
* - `clarityToTier(clarity)` — exposed for testability and for
|
|
13
|
+
* composition layers that need to query "what tier does this
|
|
14
|
+
* clarity value map to?".
|
|
15
|
+
* - `SoundDispatcher` — per-turn fan-out: for each buffered sound,
|
|
16
|
+
* propagate to every `ListenerTrait` entity and emit one
|
|
17
|
+
* `sound.audibility.heard` semantic event per non-silent result
|
|
18
|
+
* (Phase 6 Step 6.2).
|
|
19
|
+
* - `AUDIBILITY_HEARD_EVENT_TYPE` — the semantic-event type string
|
|
20
|
+
* fired by the dispatcher.
|
|
21
|
+
*
|
|
22
|
+
* @see ADR-172 — Spatial Sound Propagation
|
|
23
|
+
*/
|
|
24
|
+
export { propagate, clarityToTier } from './propagation';
|
|
25
|
+
export { SoundDispatcher, AUDIBILITY_HEARD_EVENT_TYPE } from './dispatcher';
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/engine/src/sound/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
|
package/sound/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// packages/engine/src/sound/index.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.AUDIBILITY_HEARD_EVENT_TYPE = exports.SoundDispatcher = exports.clarityToTier = exports.propagate = void 0;
|
|
5
|
+
/**
|
|
6
|
+
* @sharpee/engine/sound — spatial sound propagation runtime (ADR-172).
|
|
7
|
+
*
|
|
8
|
+
* Owner context: engine layer. The propagation function lives here
|
|
9
|
+
* because it consumes world-model state, doesn't fit in a pure
|
|
10
|
+
* domain-types layer, and is invoked from the action pipeline (Phase
|
|
11
|
+
* 6) which is engine-adjacent.
|
|
12
|
+
*
|
|
13
|
+
* Public interface:
|
|
14
|
+
* - `propagate(sound, listenerId, world, timestamp)` — produce an
|
|
15
|
+
* `AudibilityEvent` (or null) for a single listener.
|
|
16
|
+
* - `clarityToTier(clarity)` — exposed for testability and for
|
|
17
|
+
* composition layers that need to query "what tier does this
|
|
18
|
+
* clarity value map to?".
|
|
19
|
+
* - `SoundDispatcher` — per-turn fan-out: for each buffered sound,
|
|
20
|
+
* propagate to every `ListenerTrait` entity and emit one
|
|
21
|
+
* `sound.audibility.heard` semantic event per non-silent result
|
|
22
|
+
* (Phase 6 Step 6.2).
|
|
23
|
+
* - `AUDIBILITY_HEARD_EVENT_TYPE` — the semantic-event type string
|
|
24
|
+
* fired by the dispatcher.
|
|
25
|
+
*
|
|
26
|
+
* @see ADR-172 — Spatial Sound Propagation
|
|
27
|
+
*/
|
|
28
|
+
var propagation_1 = require("./propagation");
|
|
29
|
+
Object.defineProperty(exports, "propagate", { enumerable: true, get: function () { return propagation_1.propagate; } });
|
|
30
|
+
Object.defineProperty(exports, "clarityToTier", { enumerable: true, get: function () { return propagation_1.clarityToTier; } });
|
|
31
|
+
var dispatcher_1 = require("./dispatcher");
|
|
32
|
+
Object.defineProperty(exports, "SoundDispatcher", { enumerable: true, get: function () { return dispatcher_1.SoundDispatcher; } });
|
|
33
|
+
Object.defineProperty(exports, "AUDIBILITY_HEARD_EVENT_TYPE", { enumerable: true, get: function () { return dispatcher_1.AUDIBILITY_HEARD_EVENT_TYPE; } });
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/engine/src/sound/index.ts"],"names":[],"mappings":";AAAA,qCAAqC;;;AAErC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,6CAAyD;AAAhD,wGAAA,SAAS,OAAA;AAAE,4GAAA,aAAa,OAAA;AACjC,2CAA4E;AAAnE,6GAAA,eAAe,OAAA;AAAE,yHAAA,2BAA2B,OAAA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spatial sound propagation function (ADR-172 Phase 3).
|
|
3
|
+
*
|
|
4
|
+
* Pure logic: given a `Sound` emission, a listener entity-id, and the
|
|
5
|
+
* world-model, returns the `AudibilityEvent` the listener perceives —
|
|
6
|
+
* or `null` if the sound is silent at the listener's location.
|
|
7
|
+
*
|
|
8
|
+
* The function is structured around three pieces:
|
|
9
|
+
*
|
|
10
|
+
* 1. **Edge-graph construction** (`getAcousticEdges`) — for any room,
|
|
11
|
+
* enumerate the rooms it's acoustically connected to, with each
|
|
12
|
+
* edge's cost. Today's edge sources are exits-with-doors and walls
|
|
13
|
+
* (per ADR-173). Future acoustic conduits ride on the same shape
|
|
14
|
+
* without changes here.
|
|
15
|
+
*
|
|
16
|
+
* 2. **Path search** (`findShortestAcousticPath`) — Dijkstra from
|
|
17
|
+
* source room to listener's room. Path cost = sum of edge costs +
|
|
18
|
+
* 1 unit per intermediate room. Wall edges traversed are recorded
|
|
19
|
+
* so the resulting `AudibilityEvent` can name a wall when the path
|
|
20
|
+
* crosses exactly one.
|
|
21
|
+
*
|
|
22
|
+
* 3. **Clarity → tier mapping** (`clarityToTier`) — the ADR-172
|
|
23
|
+
* audibility-tier table.
|
|
24
|
+
*
|
|
25
|
+
* The propagation function does *not* enumerate listeners — that is
|
|
26
|
+
* Phase 5/6's dispatcher. This file is `propagate(sound, listenerId,
|
|
27
|
+
* world, timestamp) → event | null`, intended to be called per
|
|
28
|
+
* listener.
|
|
29
|
+
*
|
|
30
|
+
* Owner context: `@sharpee/engine` — runtime / sound subsystem.
|
|
31
|
+
*
|
|
32
|
+
* @see ADR-172 — Spatial Sound Propagation
|
|
33
|
+
* @see ADR-173 — Wall Adjacency Primitive (substrate)
|
|
34
|
+
*/
|
|
35
|
+
import type { EntityId } from "../../core/index";
|
|
36
|
+
import { type AudibilityTier, type IAudibilityEvent, type ISound } from "../../if-domain/index";
|
|
37
|
+
import { WorldModel } from "../../world-model/index";
|
|
38
|
+
/**
|
|
39
|
+
* Propagate a sound emission to a single listener.
|
|
40
|
+
*
|
|
41
|
+
* Returns an `AudibilityEvent` for the listener if the sound reaches
|
|
42
|
+
* them at any tier above `silent`; returns `null` if the sound is
|
|
43
|
+
* silent at the listener's location (no reachable path, cost too high
|
|
44
|
+
* for the volume budget, or the listener has no resolvable room).
|
|
45
|
+
*
|
|
46
|
+
* Same-room emissions short-circuit to `full` audibility regardless of
|
|
47
|
+
* intervening boundaries (degenerate case from ADR-172 §Propagation
|
|
48
|
+
* function step 5).
|
|
49
|
+
*
|
|
50
|
+
* @param sound The emission shape.
|
|
51
|
+
* @param listenerId The entity id of the listener.
|
|
52
|
+
* @param world The world-model carrying rooms, walls, doors,
|
|
53
|
+
* and obstructors.
|
|
54
|
+
* @param timestamp Engine-provided turn-sequence integer for event
|
|
55
|
+
* ordering. Phase 6's dispatcher threads this from
|
|
56
|
+
* the turn manager.
|
|
57
|
+
*/
|
|
58
|
+
export declare function propagate(sound: ISound, listenerId: EntityId, world: WorldModel, timestamp: number): IAudibilityEvent | null;
|
|
59
|
+
/**
|
|
60
|
+
* Map a clarity value (volume budget − accumulated path cost) to the
|
|
61
|
+
* ADR-172 audibility tier table.
|
|
62
|
+
*
|
|
63
|
+
* Exposed for testability and so that future composition layers can
|
|
64
|
+
* reuse the mapping (e.g., a conversation-choreography layer that
|
|
65
|
+
* wants to show a "what would the audibility be at this volume from
|
|
66
|
+
* here?" debug overlay).
|
|
67
|
+
*/
|
|
68
|
+
export declare function clarityToTier(clarity: number): AudibilityTier;
|
|
69
|
+
//# sourceMappingURL=propagation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propagation.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/engine/src/sound/propagation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAUL,UAAU,EACX,MAAM,sBAAsB,CAAC;AAM9B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,QAAQ,EACpB,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,GAChB,gBAAgB,GAAG,IAAI,CAuBzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAM7D"}
|