@sadhaka/loom-engine 0.13.0 → 0.14.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/director/ai/ai-plugin-registry.d.ts +20 -0
- package/dist/director/ai/ai-plugin-registry.d.ts.map +1 -0
- package/dist/director/ai/ai-plugin-registry.js +232 -0
- package/dist/director/ai/ai-plugin-registry.js.map +1 -0
- package/dist/director/ai/mock-ai-plugin.d.ts +24 -0
- package/dist/director/ai/mock-ai-plugin.d.ts.map +1 -0
- package/dist/director/ai/mock-ai-plugin.js +83 -0
- package/dist/director/ai/mock-ai-plugin.js.map +1 -0
- package/dist/director/ai/plugin-context.d.ts +27 -0
- package/dist/director/ai/plugin-context.d.ts.map +1 -0
- package/dist/director/ai/plugin-context.js +152 -0
- package/dist/director/ai/plugin-context.js.map +1 -0
- package/dist/director/ai/plugin.d.ts +57 -0
- package/dist/director/ai/plugin.d.ts.map +1 -0
- package/dist/director/ai/plugin.js +32 -0
- package/dist/director/ai/plugin.js.map +1 -0
- package/dist/director/index.d.ts +27 -0
- package/dist/director/index.d.ts.map +1 -0
- package/dist/director/index.js +26 -0
- package/dist/director/index.js.map +1 -0
- package/dist/director/zone/mock-zone-bridge.d.ts +22 -0
- package/dist/director/zone/mock-zone-bridge.d.ts.map +1 -0
- package/dist/director/zone/mock-zone-bridge.js +107 -0
- package/dist/director/zone/mock-zone-bridge.js.map +1 -0
- package/dist/director/zone/sse-zone-bridge.d.ts +40 -0
- package/dist/director/zone/sse-zone-bridge.d.ts.map +1 -0
- package/dist/director/zone/sse-zone-bridge.js +164 -0
- package/dist/director/zone/sse-zone-bridge.js.map +1 -0
- package/dist/director/zone/zone-event-bridge.d.ts +21 -0
- package/dist/director/zone/zone-event-bridge.d.ts.map +1 -0
- package/dist/director/zone/zone-event-bridge.js +24 -0
- package/dist/director/zone/zone-event-bridge.js.map +1 -0
- package/dist/director/zone/zone-event-envelope.d.ts +90 -0
- package/dist/director/zone/zone-event-envelope.d.ts.map +1 -0
- package/dist/director/zone/zone-event-envelope.js +104 -0
- package/dist/director/zone/zone-event-envelope.js.map +1 -0
- package/dist/director/zone/zone-event-log.d.ts +17 -0
- package/dist/director/zone/zone-event-log.d.ts.map +1 -0
- package/dist/director/zone/zone-event-log.js +46 -0
- package/dist/director/zone/zone-event-log.js.map +1 -0
- package/dist/director/zone/zone-event-system.d.ts +14 -0
- package/dist/director/zone/zone-event-system.d.ts.map +1 -0
- package/dist/director/zone/zone-event-system.js +179 -0
- package/dist/director/zone/zone-event-system.js.map +1 -0
- package/dist/director/zone/zone-state-resource.d.ts +15 -0
- package/dist/director/zone/zone-state-resource.d.ts.map +1 -0
- package/dist/director/zone/zone-state-resource.js +60 -0
- package/dist/director/zone/zone-state-resource.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +33 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// IAIPlugin - server-side plugin SPI for the Director.
|
|
2
|
+
//
|
|
3
|
+
// Per LOOM-DIRECTOR-PROTOCOL-V2 Section 5: the engine no longer
|
|
4
|
+
// hardcodes a single Anthropic flow. Consumers register any number of
|
|
5
|
+
// IAIPlugin implementations against the AIPluginRegistry, and the
|
|
6
|
+
// runtime dispatches lifecycle hooks (tick, peer join/leave, zone
|
|
7
|
+
// enter, player action). Each hook returns EmittedEvents - either
|
|
8
|
+
// per-character v1 envelopes, v2 zone events, or both.
|
|
9
|
+
//
|
|
10
|
+
// This module is server-only. The browser bundle never imports it.
|
|
11
|
+
// It is exposed only via the `@sadhaka/loom-engine/server` entry
|
|
12
|
+
// point so consumers can wire LLM-backed plugins (Anthropic, OpenAI,
|
|
13
|
+
// local models, deterministic state machines, ...) on the Node side
|
|
14
|
+
// while the browser engine stays small.
|
|
15
|
+
//
|
|
16
|
+
// `ZoneEvent` (LOOM-DIRECTOR-PROTOCOL-V2 §3) is defined in
|
|
17
|
+
// `src/director/zone/zone-event-envelope.ts` (Track A, merged into
|
|
18
|
+
// 0.14.0 alongside this module). The registry carries zone events
|
|
19
|
+
// opaquely - it never dereferences fields beyond the discriminated
|
|
20
|
+
// union - so the only consumer of the strict type is plugin authors
|
|
21
|
+
// constructing ZoneEvent values in their hook returns.
|
|
22
|
+
//
|
|
23
|
+
// Spec invariants preserved:
|
|
24
|
+
// - All hooks are async; they return EmittedEvents.
|
|
25
|
+
// - Hooks not implemented by a plugin are simply omitted; the
|
|
26
|
+
// registry checks `typeof plugin.onX === 'function'` before call.
|
|
27
|
+
// - Plugins are pure-ish: given a context, they return events.
|
|
28
|
+
// State mutation is the engine's job (open question 8.2 resolved).
|
|
29
|
+
// - Plugin-direct event emission outside its returned EmittedEvents
|
|
30
|
+
// is not supported; the registry is the single funnel.
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/director/ai/plugin.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,EAAE;AACF,gEAAgE;AAChE,sEAAsE;AACtE,kEAAkE;AAClE,kEAAkE;AAClE,kEAAkE;AAClE,uDAAuD;AACvD,EAAE;AACF,mEAAmE;AACnE,iEAAiE;AACjE,qEAAqE;AACrE,oEAAoE;AACpE,wCAAwC;AACxC,EAAE;AACF,2DAA2D;AAC3D,mEAAmE;AACnE,kEAAkE;AAClE,mEAAmE;AACnE,oEAAoE;AACpE,uDAAuD;AACvD,EAAE;AACF,6BAA6B;AAC7B,sDAAsD;AACtD,gEAAgE;AAChE,sEAAsE;AACtE,iEAAiE;AACjE,uEAAuE;AACvE,sEAAsE;AACtE,2DAA2D"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type { EventEnvelope, DirectorEvent, DirectorEventType, DirectorEventDataMap, EventPriority, EncounterSpawnData, EncounterTickData, EncounterEndData, EncounterLootData, KnotContextData, KnotPaletteHex, KnotMood, VeBudgetUpdateData, VeilTier, SceneTransitionData, SceneTransitionKind, NarratorLineData, NarratorVoice, SystemHeartbeatData, SystemReplayCompleteData, SystemSnapshotRequiredData, MobSpec, BossSpec, DropSpec, } from './event-envelope.js';
|
|
2
|
+
export { parseEnvelope, parseEnvelopeJson, priorityFor, EventEnvelopeParseError, } from './event-envelope.js';
|
|
3
|
+
export type { IDirectorBridge, DirectorBridgeStatus, DirectorBridgeStats, } from './director-bridge.js';
|
|
4
|
+
export { RESOURCE_DIRECTOR_BRIDGE, RESOURCE_KNOT_CONTEXT, } from './director-bridge.js';
|
|
5
|
+
export { MockDirectorBridge } from './mock-director-bridge.js';
|
|
6
|
+
export type { SSEDirectorBridgeOptions } from './sse-director-bridge.js';
|
|
7
|
+
export { SSEDirectorBridge } from './sse-director-bridge.js';
|
|
8
|
+
export type { KnotPaletteRgba } from './knot-context-resource.js';
|
|
9
|
+
export { KnotContextResource } from './knot-context-resource.js';
|
|
10
|
+
export type { DirectorEventLog } from './director-system.js';
|
|
11
|
+
export { DirectorSystem, RESOURCE_DIRECTOR_LOG, createDirectorEventLog, } from './director-system.js';
|
|
12
|
+
export type { DirectorEncounterSystemOptions } from './director-encounter-system.js';
|
|
13
|
+
export { DirectorEncounterSystem } from './director-encounter-system.js';
|
|
14
|
+
export type { ZoneEventEnvelope, ZoneEvent, ZoneEventType, ZoneEventDataMap, ZoneBossSpec, ZoneBossSpawnData, ZoneBossTickData, ZoneBossEndData, ZoneBossOutcome, ZoneBossHit, ZoneNarratorData, ZoneKnotData, ZoneStateData, ZoneSnapshotData, ZoneStateChange, } from './zone/zone-event-envelope.js';
|
|
15
|
+
export { parseZoneEnvelope, parseZoneEnvelopeJson, priorityFor as zonePriorityFor, ZoneEventEnvelopeParseError, } from './zone/zone-event-envelope.js';
|
|
16
|
+
export type { IZoneEventBridge, ZoneEventBridgeStatus, ZoneEventBridgeStats, } from './zone/zone-event-bridge.js';
|
|
17
|
+
export { RESOURCE_ZONE_EVENT_BRIDGE } from './zone/zone-event-bridge.js';
|
|
18
|
+
export { MockZoneBridge } from './zone/mock-zone-bridge.js';
|
|
19
|
+
export type { SSEZoneBridgeOptions, SSEZoneBridgeEventSource, } from './zone/sse-zone-bridge.js';
|
|
20
|
+
export { SSEZoneBridge } from './zone/sse-zone-bridge.js';
|
|
21
|
+
export type { ZoneEventLog, ZoneEventLogEntry, } from './zone/zone-event-log.js';
|
|
22
|
+
export { RESOURCE_ZONE_EVENT_LOG, ZONE_RING_SIZE, createZoneEventLog, getOrCreateZoneEntry, pushZoneEvent, } from './zone/zone-event-log.js';
|
|
23
|
+
export type { DirectorZoneStateResource } from './zone/zone-state-resource.js';
|
|
24
|
+
export { RESOURCE_DIRECTOR_ZONE_STATE, createDirectorZoneStateResource, getOrCreateZoneStateMap, applyZoneStateChanges, replaceZoneStateFromSnapshot, } from './zone/zone-state-resource.js';
|
|
25
|
+
export type { ZoneEventSystemOptions } from './zone/zone-event-system.js';
|
|
26
|
+
export { ZoneEventSystem } from './zone/zone-event-system.js';
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/director/index.ts"],"names":[],"mappings":"AAaA,YAAY,EACV,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,QAAQ,EACR,kBAAkB,EAClB,QAAQ,EACR,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,EAC1B,OAAO,EACP,QAAQ,EACR,QAAQ,GACT,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,YAAY,EACV,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,WAAW,IAAI,eAAe,EAC9B,2BAA2B,GAC5B,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,YAAY,EACV,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,YAAY,EACV,YAAY,EACZ,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,GACd,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,EAC/B,uBAAuB,EACvB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Director module barrel - re-exports the v1 (Phase 6) surface plus
|
|
2
|
+
// the v2 (Phase 16) zone-event surface.
|
|
3
|
+
//
|
|
4
|
+
// v1 lives at the top level of src/director/. v2 lives under
|
|
5
|
+
// src/director/zone/. Track B's AI Plugin SPI lives under
|
|
6
|
+
// src/director/ai/ and is re-exported via @sadhaka/loom-engine/server,
|
|
7
|
+
// not from here. We do not pull AI plugin types into this barrel so
|
|
8
|
+
// the browser bundle stays small.
|
|
9
|
+
//
|
|
10
|
+
// Engine-level public re-exports happen in ../index.ts. Internal
|
|
11
|
+
// callers can import from this barrel for convenience.
|
|
12
|
+
export { parseEnvelope, parseEnvelopeJson, priorityFor, EventEnvelopeParseError, } from './event-envelope.js';
|
|
13
|
+
export { RESOURCE_DIRECTOR_BRIDGE, RESOURCE_KNOT_CONTEXT, } from './director-bridge.js';
|
|
14
|
+
export { MockDirectorBridge } from './mock-director-bridge.js';
|
|
15
|
+
export { SSEDirectorBridge } from './sse-director-bridge.js';
|
|
16
|
+
export { KnotContextResource } from './knot-context-resource.js';
|
|
17
|
+
export { DirectorSystem, RESOURCE_DIRECTOR_LOG, createDirectorEventLog, } from './director-system.js';
|
|
18
|
+
export { DirectorEncounterSystem } from './director-encounter-system.js';
|
|
19
|
+
export { parseZoneEnvelope, parseZoneEnvelopeJson, priorityFor as zonePriorityFor, ZoneEventEnvelopeParseError, } from './zone/zone-event-envelope.js';
|
|
20
|
+
export { RESOURCE_ZONE_EVENT_BRIDGE } from './zone/zone-event-bridge.js';
|
|
21
|
+
export { MockZoneBridge } from './zone/mock-zone-bridge.js';
|
|
22
|
+
export { SSEZoneBridge } from './zone/sse-zone-bridge.js';
|
|
23
|
+
export { RESOURCE_ZONE_EVENT_LOG, ZONE_RING_SIZE, createZoneEventLog, getOrCreateZoneEntry, pushZoneEvent, } from './zone/zone-event-log.js';
|
|
24
|
+
export { RESOURCE_DIRECTOR_ZONE_STATE, createDirectorZoneStateResource, getOrCreateZoneStateMap, applyZoneStateChanges, replaceZoneStateFromSnapshot, } from './zone/zone-state-resource.js';
|
|
25
|
+
export { ZoneEventSystem } from './zone/zone-event-system.js';
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/director/index.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,wCAAwC;AACxC,EAAE;AACF,6DAA6D;AAC7D,0DAA0D;AAC1D,uEAAuE;AACvE,oEAAoE;AACpE,kCAAkC;AAClC,EAAE;AACF,iEAAiE;AACjE,uDAAuD;AA6BvD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EACL,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAoBzE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,WAAW,IAAI,eAAe,EAC9B,2BAA2B,GAC5B,MAAM,+BAA+B,CAAC;AAMvC,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAK5D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK1D,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,GACd,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,EAC/B,uBAAuB,EACvB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ZoneEvent } from './zone-event-envelope.js';
|
|
2
|
+
import { type IZoneEventBridge, type ZoneEventBridgeStatus, type ZoneEventBridgeStats } from './zone-event-bridge.js';
|
|
3
|
+
export declare class MockZoneBridge implements IZoneEventBridge {
|
|
4
|
+
private queue;
|
|
5
|
+
private statusValue;
|
|
6
|
+
private readonly lastEventIdByZone;
|
|
7
|
+
private readonly statsValue;
|
|
8
|
+
start(): void;
|
|
9
|
+
stop(): void;
|
|
10
|
+
status(): ZoneEventBridgeStatus;
|
|
11
|
+
isConnected(): boolean;
|
|
12
|
+
getLastEventId(zone: string): number;
|
|
13
|
+
pollEvents(): ZoneEvent[];
|
|
14
|
+
stats(): Readonly<ZoneEventBridgeStats>;
|
|
15
|
+
enqueueIncoming(event: ZoneEvent): void;
|
|
16
|
+
enqueueIncomingJson(json: string): boolean;
|
|
17
|
+
enqueueAll(events: ReadonlyArray<ZoneEvent>): void;
|
|
18
|
+
bumpReconnect(): void;
|
|
19
|
+
setServerDrops(p1: number, p2: number): void;
|
|
20
|
+
pending(): number;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=mock-zone-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-zone-bridge.d.ts","sourceRoot":"","sources":["../../../src/director/zone/mock-zone-bridge.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,wBAAwB,CAAC;AAEhC,qBAAa,cAAe,YAAW,gBAAgB;IACrD,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IACpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAYzB;IAEF,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI;IAIZ,MAAM,IAAI,qBAAqB;IAI/B,WAAW,IAAI,OAAO;IAItB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIpC,UAAU,IAAI,SAAS,EAAE;IAOzB,KAAK,IAAI,QAAQ,CAAC,oBAAoB,CAAC;IAmBvC,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAavC,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAQ1C,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,IAAI;IAQlD,aAAa,IAAI,IAAI;IAKrB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAM5C,OAAO,IAAI,MAAM;CAGlB"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// MockZoneBridge - in-memory v2 zone-event source for tests + offline
|
|
2
|
+
// demos.
|
|
3
|
+
//
|
|
4
|
+
// Tests inject envelopes via enqueueIncoming() and let ZoneEventSystem
|
|
5
|
+
// drain them. The demo path can also use this when running without a
|
|
6
|
+
// backend connection. pollEvents() drains the queue in FIFO order.
|
|
7
|
+
//
|
|
8
|
+
// Tracks per-zone lastEventId so the IZoneEventBridge contract is
|
|
9
|
+
// satisfied. Does not reconnect, does not validate envelopes when fed
|
|
10
|
+
// objects directly - that's parseZoneEnvelope's job upstream of
|
|
11
|
+
// enqueueIncoming(). enqueueIncomingJson() is a convenience that runs
|
|
12
|
+
// the parser first and silently drops malformed payloads (mirrors how
|
|
13
|
+
// SSEZoneBridge would handle a bad frame).
|
|
14
|
+
import { parseZoneEnvelopeJson } from './zone-event-envelope.js';
|
|
15
|
+
export class MockZoneBridge {
|
|
16
|
+
queue = [];
|
|
17
|
+
statusValue = 'idle';
|
|
18
|
+
lastEventIdByZone = new Map();
|
|
19
|
+
statsValue = {
|
|
20
|
+
eventsReceived: 0,
|
|
21
|
+
reconnects: 0,
|
|
22
|
+
outOfOrderEvents: 0,
|
|
23
|
+
serverDropsP1: 0,
|
|
24
|
+
serverDropsP2: 0,
|
|
25
|
+
};
|
|
26
|
+
start() {
|
|
27
|
+
this.statusValue = 'connected';
|
|
28
|
+
}
|
|
29
|
+
stop() {
|
|
30
|
+
this.statusValue = 'closed';
|
|
31
|
+
}
|
|
32
|
+
status() {
|
|
33
|
+
return this.statusValue;
|
|
34
|
+
}
|
|
35
|
+
isConnected() {
|
|
36
|
+
return this.statusValue === 'connected';
|
|
37
|
+
}
|
|
38
|
+
getLastEventId(zone) {
|
|
39
|
+
return this.lastEventIdByZone.get(zone) ?? 0;
|
|
40
|
+
}
|
|
41
|
+
pollEvents() {
|
|
42
|
+
if (this.queue.length === 0)
|
|
43
|
+
return [];
|
|
44
|
+
const out = this.queue;
|
|
45
|
+
this.queue = [];
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
stats() {
|
|
49
|
+
// Allocate the read-only view fresh each call so mutations after
|
|
50
|
+
// the call don't leak through. lastEventIdByZone is wrapped in a
|
|
51
|
+
// shallow clone; spec calls this rare so cost is fine.
|
|
52
|
+
return {
|
|
53
|
+
eventsReceived: this.statsValue.eventsReceived,
|
|
54
|
+
reconnects: this.statsValue.reconnects,
|
|
55
|
+
outOfOrderEvents: this.statsValue.outOfOrderEvents,
|
|
56
|
+
serverDropsP1: this.statsValue.serverDropsP1,
|
|
57
|
+
serverDropsP2: this.statsValue.serverDropsP2,
|
|
58
|
+
lastEventIdByZone: new Map(this.lastEventIdByZone),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// ----- Mock-only injection helpers -----
|
|
62
|
+
// Enqueue a parsed envelope as if the server pushed it. Out-of-order
|
|
63
|
+
// injection is allowed; the consumer's per-zone gap detection should
|
|
64
|
+
// handle it.
|
|
65
|
+
enqueueIncoming(event) {
|
|
66
|
+
this.queue.push(event);
|
|
67
|
+
this.statsValue.eventsReceived++;
|
|
68
|
+
const prev = this.lastEventIdByZone.get(event.zone_id) ?? 0;
|
|
69
|
+
if (event.id > prev) {
|
|
70
|
+
this.lastEventIdByZone.set(event.zone_id, event.id);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
this.statsValue.outOfOrderEvents++;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Convenience: enqueue from a JSON string. Silently drops malformed
|
|
77
|
+
// payloads, matching the SSE bridge's parse-error behaviour.
|
|
78
|
+
enqueueIncomingJson(json) {
|
|
79
|
+
const ev = parseZoneEnvelopeJson(json);
|
|
80
|
+
if (!ev)
|
|
81
|
+
return false;
|
|
82
|
+
this.enqueueIncoming(ev);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
// Convenience: bulk enqueue.
|
|
86
|
+
enqueueAll(events) {
|
|
87
|
+
for (let i = 0; i < events.length; i++) {
|
|
88
|
+
const e = events[i];
|
|
89
|
+
if (e)
|
|
90
|
+
this.enqueueIncoming(e);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Mock-only: simulate the server crediting a reconnect.
|
|
94
|
+
bumpReconnect() {
|
|
95
|
+
this.statsValue.reconnects++;
|
|
96
|
+
}
|
|
97
|
+
// Mock-only: simulate server-side drop counters from a heartbeat.
|
|
98
|
+
setServerDrops(p1, p2) {
|
|
99
|
+
this.statsValue.serverDropsP1 = p1;
|
|
100
|
+
this.statsValue.serverDropsP2 = p2;
|
|
101
|
+
}
|
|
102
|
+
// Inspect-only: how many events are buffered waiting for a poll.
|
|
103
|
+
pending() {
|
|
104
|
+
return this.queue.length;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=mock-zone-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-zone-bridge.js","sourceRoot":"","sources":["../../../src/director/zone/mock-zone-bridge.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,SAAS;AACT,EAAE;AACF,uEAAuE;AACvE,qEAAqE;AACrE,mEAAmE;AACnE,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,gEAAgE;AAChE,sEAAsE;AACtE,sEAAsE;AACtE,2CAA2C;AAG3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAOjE,MAAM,OAAO,cAAc;IACjB,KAAK,GAAgB,EAAE,CAAC;IACxB,WAAW,GAA0B,MAAM,CAAC;IACnC,iBAAiB,GAAwB,IAAI,GAAG,EAAE,CAAC;IACnD,UAAU,GAMvB;QACF,cAAc,EAAE,CAAC;QACjB,UAAU,EAAE,CAAC;QACb,gBAAgB,EAAE,CAAC;QACnB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;KACjB,CAAC;IAEF,KAAK;QACH,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,IAAI;QACF,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;IAC9B,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,WAAW,KAAK,WAAW,CAAC;IAC1C,CAAC;IAED,cAAc,CAAC,IAAY;QACzB,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK;QACH,iEAAiE;QACjE,iEAAiE;QACjE,uDAAuD;QACvD,OAAO;YACL,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,cAAc;YAC9C,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU;YACtC,gBAAgB,EAAE,IAAI,CAAC,UAAU,CAAC,gBAAgB;YAClD,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,aAAa;YAC5C,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,aAAa;YAC5C,iBAAiB,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,0CAA0C;IAE1C,qEAAqE;IACrE,qEAAqE;IACrE,aAAa;IACb,eAAe,CAAC,KAAgB;QAC9B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,IAAI,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,KAAK,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAC;QACrC,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,6DAA6D;IAC7D,mBAAmB,CAAC,IAAY;QAC9B,MAAM,EAAE,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QACtB,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6BAA6B;IAC7B,UAAU,CAAC,MAAgC;QACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,CAAC;gBAAE,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,aAAa;QACX,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IAC/B,CAAC;IAED,kEAAkE;IAClE,cAAc,CAAC,EAAU,EAAE,EAAU;QACnC,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,EAAE,CAAC;QACnC,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,EAAE,CAAC;IACrC,CAAC;IAED,iEAAiE;IACjE,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ZoneEvent } from './zone-event-envelope.js';
|
|
2
|
+
import { type IZoneEventBridge, type ZoneEventBridgeStatus, type ZoneEventBridgeStats } from './zone-event-bridge.js';
|
|
3
|
+
export interface SSEZoneBridgeEventSource {
|
|
4
|
+
readonly readyState: number;
|
|
5
|
+
addEventListener(type: string, listener: (event: {
|
|
6
|
+
data?: unknown;
|
|
7
|
+
}) => void): void;
|
|
8
|
+
removeEventListener?(type: string, listener: (event: {
|
|
9
|
+
data?: unknown;
|
|
10
|
+
}) => void): void;
|
|
11
|
+
}
|
|
12
|
+
export interface SSEZoneBridgeOptions {
|
|
13
|
+
eventSource: SSEZoneBridgeEventSource;
|
|
14
|
+
characterId: string;
|
|
15
|
+
currentZone: () => string;
|
|
16
|
+
eventName?: string;
|
|
17
|
+
filterAtReceive?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare class SSEZoneBridge implements IZoneEventBridge {
|
|
20
|
+
private readonly es;
|
|
21
|
+
private readonly characterId;
|
|
22
|
+
private readonly currentZone;
|
|
23
|
+
private readonly eventName;
|
|
24
|
+
private readonly filterAtReceive;
|
|
25
|
+
private listener;
|
|
26
|
+
private queue;
|
|
27
|
+
private statusValue;
|
|
28
|
+
private readonly lastEventIdByZone;
|
|
29
|
+
private readonly statsValue;
|
|
30
|
+
constructor(opts: SSEZoneBridgeOptions);
|
|
31
|
+
start(): void;
|
|
32
|
+
stop(): void;
|
|
33
|
+
status(): ZoneEventBridgeStatus;
|
|
34
|
+
isConnected(): boolean;
|
|
35
|
+
getLastEventId(zone: string): number;
|
|
36
|
+
pollEvents(): ZoneEvent[];
|
|
37
|
+
stats(): Readonly<ZoneEventBridgeStats>;
|
|
38
|
+
private handleRaw;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=sse-zone-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-zone-bridge.d.ts","sourceRoot":"","sources":["../../../src/director/zone/sse-zone-bridge.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,wBAAwB,CAAC;AAIhC,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAC5C,IAAI,CAAC;IACR,mBAAmB,CAAC,CAClB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAC5C,IAAI,CAAC;CAGT;AAED,MAAM,WAAW,oBAAoB;IAInC,WAAW,EAAE,wBAAwB,CAAC;IAItC,WAAW,EAAE,MAAM,CAAC;IAMpB,WAAW,EAAE,MAAM,MAAM,CAAC;IAG1B,SAAS,CAAC,EAAE,MAAM,CAAC;IAInB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAOD,qBAAa,aAAc,YAAW,gBAAgB;IACpD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA2B;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAE1C,OAAO,CAAC,QAAQ,CAAsD;IACtE,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IACpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAYzB;gBAEU,IAAI,EAAE,oBAAoB;IAWtC,KAAK,IAAI,IAAI;IAeb,IAAI,IAAI,IAAI;IAQZ,MAAM,IAAI,qBAAqB;IAe/B,WAAW,IAAI,OAAO;IAItB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIpC,UAAU,IAAI,SAAS,EAAE;IAOzB,KAAK,IAAI,QAAQ,CAAC,oBAAoB,CAAC;IAavC,OAAO,CAAC,SAAS;CA6BlB"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// SSEZoneBridge - multiplexes v2 zone-event frames onto an EXISTING
|
|
2
|
+
// presence EventSource (per LOOM-DIRECTOR-PROTOCOL-V2 §2 + §4.2).
|
|
3
|
+
//
|
|
4
|
+
// CRITICAL: this bridge does NOT open its own EventSource. The
|
|
5
|
+
// presence transport (15.x SSEMultiplayerBridge) already has a live
|
|
6
|
+
// connection per peer; we just attach a listener for SSE frames whose
|
|
7
|
+
// `event:` line is `zone.event`. Per spec §1.1: "Transport reuses
|
|
8
|
+
// presence SSE. Same channel as 15.x presence updates, multiplexed by
|
|
9
|
+
// event topic. No second EventSource per peer."
|
|
10
|
+
//
|
|
11
|
+
// Wire shape per frame:
|
|
12
|
+
// event: zone.event
|
|
13
|
+
// data: <ZoneEventEnvelope JSON>
|
|
14
|
+
//
|
|
15
|
+
// Local-zone filter: the system layer (ZoneEventSystem) is the
|
|
16
|
+
// authoritative filter on "is this event for the local player's
|
|
17
|
+
// zone". The bridge buffers ALL zone events it receives so the system
|
|
18
|
+
// can log other-zone events for diagnostics if it chooses, then drop
|
|
19
|
+
// them before applying. The currentZone() callback is provided so the
|
|
20
|
+
// bridge can additionally short-circuit obvious-other-zone events at
|
|
21
|
+
// receive time when stats matter (e.g. a high-volume foreign zone).
|
|
22
|
+
// The default behaviour is to keep everything and let the system
|
|
23
|
+
// decide.
|
|
24
|
+
//
|
|
25
|
+
// Browser-only (constructor accepts EventSource which is a DOM API).
|
|
26
|
+
// In Node tests we exercise this via an injected fake EventSource
|
|
27
|
+
// (pattern parallel to SSEDirectorBridge.eventSourceFactory).
|
|
28
|
+
import { parseZoneEnvelopeJson } from './zone-event-envelope.js';
|
|
29
|
+
// SSE EventSource readyState constants. Mirrored locally to avoid
|
|
30
|
+
// pulling DOM types into the engine's strict Node test config.
|
|
31
|
+
const ES_OPEN = 1;
|
|
32
|
+
const ES_CLOSED = 2;
|
|
33
|
+
export class SSEZoneBridge {
|
|
34
|
+
es;
|
|
35
|
+
characterId;
|
|
36
|
+
currentZone;
|
|
37
|
+
eventName;
|
|
38
|
+
filterAtReceive;
|
|
39
|
+
listener = null;
|
|
40
|
+
queue = [];
|
|
41
|
+
statusValue = 'idle';
|
|
42
|
+
lastEventIdByZone = new Map();
|
|
43
|
+
statsValue = {
|
|
44
|
+
eventsReceived: 0,
|
|
45
|
+
reconnects: 0,
|
|
46
|
+
outOfOrderEvents: 0,
|
|
47
|
+
serverDropsP1: 0,
|
|
48
|
+
serverDropsP2: 0,
|
|
49
|
+
};
|
|
50
|
+
constructor(opts) {
|
|
51
|
+
this.es = opts.eventSource;
|
|
52
|
+
this.characterId = opts.characterId;
|
|
53
|
+
this.currentZone = opts.currentZone;
|
|
54
|
+
this.eventName = opts.eventName ?? 'zone.event';
|
|
55
|
+
this.filterAtReceive = opts.filterAtReceive ?? false;
|
|
56
|
+
// characterId is currently used for diagnostics only - reserved
|
|
57
|
+
// for future emitter_id-based UX hooks (e.g. local-cause cues).
|
|
58
|
+
void this.characterId;
|
|
59
|
+
}
|
|
60
|
+
start() {
|
|
61
|
+
if (this.listener)
|
|
62
|
+
return;
|
|
63
|
+
this.listener = (e) => { this.handleRaw(e); };
|
|
64
|
+
this.es.addEventListener(this.eventName, this.listener);
|
|
65
|
+
// The presence EventSource owns connect lifecycle. We mirror its
|
|
66
|
+
// current readyState so consumers can reason about isConnected().
|
|
67
|
+
if (this.es.readyState === ES_OPEN) {
|
|
68
|
+
this.statusValue = 'connected';
|
|
69
|
+
}
|
|
70
|
+
else if (this.es.readyState === ES_CLOSED) {
|
|
71
|
+
this.statusValue = 'closed';
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.statusValue = 'connecting';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
stop() {
|
|
78
|
+
this.statusValue = 'closed';
|
|
79
|
+
if (this.listener && this.es.removeEventListener) {
|
|
80
|
+
this.es.removeEventListener(this.eventName, this.listener);
|
|
81
|
+
}
|
|
82
|
+
this.listener = null;
|
|
83
|
+
}
|
|
84
|
+
status() {
|
|
85
|
+
// Refresh from the underlying ES if we're attached - the presence
|
|
86
|
+
// layer handles reconnects, so our cached status can lag.
|
|
87
|
+
if (this.listener) {
|
|
88
|
+
if (this.es.readyState === ES_OPEN) {
|
|
89
|
+
if (this.statusValue === 'connecting' || this.statusValue === 'reconnecting') {
|
|
90
|
+
this.statusValue = 'connected';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (this.es.readyState === ES_CLOSED) {
|
|
94
|
+
this.statusValue = 'closed';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return this.statusValue;
|
|
98
|
+
}
|
|
99
|
+
isConnected() {
|
|
100
|
+
return this.status() === 'connected';
|
|
101
|
+
}
|
|
102
|
+
getLastEventId(zone) {
|
|
103
|
+
return this.lastEventIdByZone.get(zone) ?? 0;
|
|
104
|
+
}
|
|
105
|
+
pollEvents() {
|
|
106
|
+
if (this.queue.length === 0)
|
|
107
|
+
return [];
|
|
108
|
+
const out = this.queue;
|
|
109
|
+
this.queue = [];
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
stats() {
|
|
113
|
+
return {
|
|
114
|
+
eventsReceived: this.statsValue.eventsReceived,
|
|
115
|
+
reconnects: this.statsValue.reconnects,
|
|
116
|
+
outOfOrderEvents: this.statsValue.outOfOrderEvents,
|
|
117
|
+
serverDropsP1: this.statsValue.serverDropsP1,
|
|
118
|
+
serverDropsP2: this.statsValue.serverDropsP2,
|
|
119
|
+
lastEventIdByZone: new Map(this.lastEventIdByZone),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// ----- Internal -----
|
|
123
|
+
handleRaw(e) {
|
|
124
|
+
const dataStr = typeof e.data === 'string' ? e.data : '';
|
|
125
|
+
const ev = parseZoneEnvelopeJson(dataStr);
|
|
126
|
+
if (!ev) {
|
|
127
|
+
// Malformed envelope - drop. The presence layer's EventSource
|
|
128
|
+
// keeps flowing; one bad frame doesn't kill the channel.
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (this.filterAtReceive) {
|
|
132
|
+
// Optional optimization: drop foreign-zone events before they
|
|
133
|
+
// hit the queue. The system would have done it anyway.
|
|
134
|
+
const localZone = safeCurrentZone(this.currentZone);
|
|
135
|
+
if (localZone && ev.zone_id !== localZone) {
|
|
136
|
+
// Still track the highest id for that foreign zone so the
|
|
137
|
+
// bridge's per-zone last-id map is honest about what arrived.
|
|
138
|
+
const prev = this.lastEventIdByZone.get(ev.zone_id) ?? 0;
|
|
139
|
+
if (ev.id > prev)
|
|
140
|
+
this.lastEventIdByZone.set(ev.zone_id, ev.id);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
this.statsValue.eventsReceived++;
|
|
145
|
+
const prev = this.lastEventIdByZone.get(ev.zone_id) ?? 0;
|
|
146
|
+
if (ev.id > prev) {
|
|
147
|
+
this.lastEventIdByZone.set(ev.zone_id, ev.id);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
this.statsValue.outOfOrderEvents++;
|
|
151
|
+
}
|
|
152
|
+
this.queue.push(ev);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function safeCurrentZone(fn) {
|
|
156
|
+
try {
|
|
157
|
+
const z = fn();
|
|
158
|
+
return typeof z === 'string' && z.length > 0 ? z : null;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=sse-zone-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-zone-bridge.js","sourceRoot":"","sources":["../../../src/director/zone/sse-zone-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ZoneEvent } from './zone-event-envelope.js';
|
|
2
|
+
export type ZoneEventBridgeStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'snapshot-required' | 'closed';
|
|
3
|
+
export interface ZoneEventBridgeStats {
|
|
4
|
+
eventsReceived: number;
|
|
5
|
+
reconnects: number;
|
|
6
|
+
outOfOrderEvents: number;
|
|
7
|
+
serverDropsP1: number;
|
|
8
|
+
serverDropsP2: number;
|
|
9
|
+
lastEventIdByZone: ReadonlyMap<string, number>;
|
|
10
|
+
}
|
|
11
|
+
export interface IZoneEventBridge {
|
|
12
|
+
start(): void;
|
|
13
|
+
stop(): void;
|
|
14
|
+
status(): ZoneEventBridgeStatus;
|
|
15
|
+
isConnected(): boolean;
|
|
16
|
+
getLastEventId(zone: string): number;
|
|
17
|
+
pollEvents(): ZoneEvent[];
|
|
18
|
+
stats(): Readonly<ZoneEventBridgeStats>;
|
|
19
|
+
}
|
|
20
|
+
export declare const RESOURCE_ZONE_EVENT_BRIDGE = "zone_event_bridge";
|
|
21
|
+
//# sourceMappingURL=zone-event-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone-event-bridge.d.ts","sourceRoot":"","sources":["../../../src/director/zone/zone-event-bridge.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,MAAM,qBAAqB,GAC7B,MAAM,GACN,YAAY,GACZ,WAAW,GACX,cAAc,GACd,mBAAmB,GACnB,QAAQ,CAAC;AAEb,MAAM,WAAW,oBAAoB;IAEnC,cAAc,EAAE,MAAM,CAAC;IAEvB,UAAU,EAAE,MAAM,CAAC;IAEnB,gBAAgB,EAAE,MAAM,CAAC;IAGzB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IAGtB,iBAAiB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;IACb,MAAM,IAAI,qBAAqB,CAAC;IAChC,WAAW,IAAI,OAAO,CAAC;IAEvB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAErC,UAAU,IAAI,SAAS,EAAE,CAAC;IAC1B,KAAK,IAAI,QAAQ,CAAC,oBAAoB,CAAC,CAAC;CACzC;AAGD,eAAO,MAAM,0BAA0B,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// IZoneEventBridge - abstraction over the v2 zone-event source.
|
|
2
|
+
//
|
|
3
|
+
// Concrete implementations:
|
|
4
|
+
// MockZoneBridge - in-process. enqueueIncoming(event) simulates
|
|
5
|
+
// server pushes for tests + offline demo.
|
|
6
|
+
// SSEZoneBridge - multiplexes onto an EXISTING presence
|
|
7
|
+
// EventSource. Listens for SSE frames whose
|
|
8
|
+
// `event:` line is `zone.event` (per spec §2.1).
|
|
9
|
+
//
|
|
10
|
+
// The ZoneEventSystem (PHASE_INPUT, AFTER DirectorSystem and
|
|
11
|
+
// PeerPresenceSystem) calls pollEvents() once per tick to drain queued
|
|
12
|
+
// events. Bridges buffer events between polls so the system's per-tick
|
|
13
|
+
// read is bounded.
|
|
14
|
+
//
|
|
15
|
+
// Per-zone monotonic id semantics (spec §8.1):
|
|
16
|
+
// - Each zone has its own id sequence starting at 1.
|
|
17
|
+
// - getLastEventId(zone) returns the highest id observed for that
|
|
18
|
+
// zone, or 0 if no events seen.
|
|
19
|
+
// - Out-of-order events are tracked in stats, but bridge-level
|
|
20
|
+
// reorder buffering lives in concrete impls (the contract here is
|
|
21
|
+
// the polled queue is best-effort ordered).
|
|
22
|
+
// Resource keys.
|
|
23
|
+
export const RESOURCE_ZONE_EVENT_BRIDGE = 'zone_event_bridge';
|
|
24
|
+
//# sourceMappingURL=zone-event-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone-event-bridge.js","sourceRoot":"","sources":["../../../src/director/zone/zone-event-bridge.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,4BAA4B;AAC5B,kEAAkE;AAClE,6DAA6D;AAC7D,2DAA2D;AAC3D,+DAA+D;AAC/D,oEAAoE;AACpE,EAAE;AACF,6DAA6D;AAC7D,uEAAuE;AACvE,uEAAuE;AACvE,mBAAmB;AACnB,EAAE;AACF,+CAA+C;AAC/C,uDAAuD;AACvD,oEAAoE;AACpE,oCAAoC;AACpC,iEAAiE;AACjE,sEAAsE;AACtE,gDAAgD;AAwChD,iBAAiB;AACjB,MAAM,CAAC,MAAM,0BAA0B,GAAG,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { DropSpec, EventPriority, KnotMood, KnotPaletteHex, NarratorVoice } from '../event-envelope.js';
|
|
2
|
+
export interface ZoneEventEnvelope<T extends ZoneEventType = ZoneEventType> {
|
|
3
|
+
id: number;
|
|
4
|
+
ts: number;
|
|
5
|
+
type: T;
|
|
6
|
+
zone_id: string;
|
|
7
|
+
emitter_id: string | null;
|
|
8
|
+
priority?: EventPriority;
|
|
9
|
+
data: ZoneEventDataMap[T];
|
|
10
|
+
}
|
|
11
|
+
export interface ZoneBossSpec {
|
|
12
|
+
boss_id: string;
|
|
13
|
+
type: string;
|
|
14
|
+
name: string;
|
|
15
|
+
hp_max: number;
|
|
16
|
+
hp_current: number;
|
|
17
|
+
dmg: number;
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
knot_flavor: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ZoneBossSpawnData {
|
|
23
|
+
boss: ZoneBossSpec;
|
|
24
|
+
narrator_line: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface ZoneBossHit {
|
|
27
|
+
from_character_id: string;
|
|
28
|
+
amount: number;
|
|
29
|
+
ts_ms: number;
|
|
30
|
+
}
|
|
31
|
+
export interface ZoneBossTickData {
|
|
32
|
+
boss_id: string;
|
|
33
|
+
hp_current: number;
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
recent_hits: ReadonlyArray<ZoneBossHit>;
|
|
37
|
+
}
|
|
38
|
+
export type ZoneBossOutcome = 'killed' | 'despawned' | 'fled';
|
|
39
|
+
export interface ZoneBossEndData {
|
|
40
|
+
boss_id: string;
|
|
41
|
+
outcome: ZoneBossOutcome;
|
|
42
|
+
killer_character_id: string | null;
|
|
43
|
+
loot: ReadonlyArray<DropSpec>;
|
|
44
|
+
duration_ms: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ZoneNarratorData {
|
|
47
|
+
line: string;
|
|
48
|
+
voice: NarratorVoice;
|
|
49
|
+
ttl_ms: number;
|
|
50
|
+
}
|
|
51
|
+
export interface ZoneKnotData {
|
|
52
|
+
knot: string;
|
|
53
|
+
palette: KnotPaletteHex;
|
|
54
|
+
mood: KnotMood;
|
|
55
|
+
fade_ms: number;
|
|
56
|
+
}
|
|
57
|
+
export interface ZoneStateChange {
|
|
58
|
+
key: string;
|
|
59
|
+
value: unknown;
|
|
60
|
+
}
|
|
61
|
+
export interface ZoneStateData {
|
|
62
|
+
changes: ReadonlyArray<ZoneStateChange>;
|
|
63
|
+
}
|
|
64
|
+
export interface ZoneSnapshotData {
|
|
65
|
+
active_boss: ZoneBossSpec | null;
|
|
66
|
+
knot: ZoneKnotData | null;
|
|
67
|
+
state: ReadonlyArray<ZoneStateChange>;
|
|
68
|
+
last_event_id: number;
|
|
69
|
+
}
|
|
70
|
+
export type ZoneEventType = 'zone.boss.spawn' | 'zone.boss.tick' | 'zone.boss.end' | 'zone.narrator' | 'zone.knot' | 'zone.state' | 'zone.snapshot';
|
|
71
|
+
export interface ZoneEventDataMap {
|
|
72
|
+
'zone.boss.spawn': ZoneBossSpawnData;
|
|
73
|
+
'zone.boss.tick': ZoneBossTickData;
|
|
74
|
+
'zone.boss.end': ZoneBossEndData;
|
|
75
|
+
'zone.narrator': ZoneNarratorData;
|
|
76
|
+
'zone.knot': ZoneKnotData;
|
|
77
|
+
'zone.state': ZoneStateData;
|
|
78
|
+
'zone.snapshot': ZoneSnapshotData;
|
|
79
|
+
}
|
|
80
|
+
export type ZoneEvent = {
|
|
81
|
+
[K in ZoneEventType]: ZoneEventEnvelope<K>;
|
|
82
|
+
}[ZoneEventType];
|
|
83
|
+
export declare function priorityFor(type: ZoneEventType): EventPriority;
|
|
84
|
+
export declare class ZoneEventEnvelopeParseError extends Error {
|
|
85
|
+
readonly raw: unknown;
|
|
86
|
+
constructor(message: string, raw: unknown);
|
|
87
|
+
}
|
|
88
|
+
export declare function parseZoneEnvelope(raw: unknown): ZoneEvent;
|
|
89
|
+
export declare function parseZoneEnvelopeJson(json: string): ZoneEvent | null;
|
|
90
|
+
//# sourceMappingURL=zone-event-envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone-event-envelope.d.ts","sourceRoot":"","sources":["../../../src/director/zone/zone-event-envelope.ts"],"names":[],"mappings":"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"}
|