@sharpee/channel-service 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.
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ /**
3
+ * `renderToString` and `renderStatusLine` — flatten an `ITextBlock[]`
4
+ * to a single display string.
5
+ *
6
+ * Owner context: `@sharpee/channel-service`. Block-flattening helpers
7
+ * consumed by transcript tooling, chat overlays, and dev scripts.
8
+ * Channel-service is downstream of engine and upstream of clients —
9
+ * the right dependency position for a helper that walks ITextBlock[]
10
+ * for display.
11
+ *
12
+ * Public interface:
13
+ * - `renderToString(blocks, options?)` — flatten blocks to a joined
14
+ * string with smart separators between same-key vs different-key
15
+ * block transitions. Decorations translate to ANSI codes when
16
+ * `options.ansi === true`, or to bracket-stripped plain text
17
+ * otherwise.
18
+ * - `renderStatusLine(blocks, options?)` — render `status.*` blocks
19
+ * to a single pipe-separated line for status-bar display.
20
+ * - `CLIRenderOptions` — option shape (ansi, blockSeparator,
21
+ * colors, includeStatus). Name preserved from the prior
22
+ * `@sharpee/text-service` home; not CLI-specific in practice
23
+ * (zifmia uses `renderToString` for browser chat bubbles).
24
+ *
25
+ * Ported from `@sharpee/text-service/src/cli-renderer.ts` per ADR-174
26
+ * Phase 2 (OQ-1 resolution, 2026-05-10). The original file remains
27
+ * compilable in text-service through Phase 2 for zifmia's sake; Phase
28
+ * 3 deletes the package.
29
+ *
30
+ * @see ADR-174 — Decoration Architecture and Engine-Internal Prose Pipeline
31
+ * @see ADR-163 — Channel-Service Platform
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.renderToString = renderToString;
35
+ exports.renderStatusLine = renderStatusLine;
36
+ const text_blocks_1 = require("../text-blocks/index.js");
37
+ /**
38
+ * ANSI escape codes
39
+ */
40
+ const ANSI = {
41
+ RESET: '\x1b[0m',
42
+ BOLD: '\x1b[1m',
43
+ ITALIC: '\x1b[3m',
44
+ UNDERLINE: '\x1b[4m',
45
+ STRIKETHROUGH: '\x1b[9m',
46
+ // Colors
47
+ BLACK: '\x1b[30m',
48
+ RED: '\x1b[31m',
49
+ GREEN: '\x1b[32m',
50
+ YELLOW: '\x1b[33m',
51
+ BLUE: '\x1b[34m',
52
+ MAGENTA: '\x1b[35m',
53
+ CYAN: '\x1b[36m',
54
+ WHITE: '\x1b[37m',
55
+ // Bright colors
56
+ BRIGHT_RED: '\x1b[91m',
57
+ BRIGHT_GREEN: '\x1b[92m',
58
+ BRIGHT_YELLOW: '\x1b[93m',
59
+ BRIGHT_BLUE: '\x1b[94m',
60
+ BRIGHT_MAGENTA: '\x1b[95m',
61
+ BRIGHT_CYAN: '\x1b[96m',
62
+ BRIGHT_WHITE: '\x1b[97m',
63
+ };
64
+ /**
65
+ * Map decoration class names to ANSI codes. Keys are the final
66
+ * `IDecoration.className` strings emitted by the parser — the
67
+ * `sharpee-`-prefixed platform vocabulary names plus a couple of
68
+ * legacy bare names retained for transitional safety.
69
+ */
70
+ const DECORATION_ANSI = {
71
+ 'sharpee-em': ANSI.ITALIC,
72
+ 'sharpee-strong': ANSI.BOLD,
73
+ 'sharpee-item': ANSI.CYAN,
74
+ 'sharpee-room': ANSI.YELLOW,
75
+ 'sharpee-npc': ANSI.MAGENTA,
76
+ 'sharpee-command': ANSI.GREEN,
77
+ 'sharpee-direction': ANSI.WHITE,
78
+ 'sharpee-u': ANSI.UNDERLINE,
79
+ 'sharpee-st': ANSI.STRIKETHROUGH,
80
+ 'sharpee-code': ANSI.WHITE,
81
+ };
82
+ /**
83
+ * Approximate hex colors to ANSI by extracting the dominant channel
84
+ * (or detecting a balanced mix) and mapping to the closest ANSI hue.
85
+ */
86
+ function hexToAnsi(hex) {
87
+ const r = Number.parseInt(hex.slice(1, 3), 16);
88
+ const g = Number.parseInt(hex.slice(3, 5), 16);
89
+ const b = Number.parseInt(hex.slice(5, 7), 16);
90
+ return pickPrimaryAnsi(r, g, b) ?? pickMixedAnsi(r, g, b) ?? ANSI.WHITE;
91
+ }
92
+ /** One channel dominates the other two — return its hue, or null. */
93
+ function pickPrimaryAnsi(r, g, b) {
94
+ if (r > g && r > b)
95
+ return r > 180 ? ANSI.BRIGHT_RED : ANSI.RED;
96
+ if (g > r && g > b)
97
+ return g > 180 ? ANSI.BRIGHT_GREEN : ANSI.GREEN;
98
+ if (b > r && b > g)
99
+ return b > 180 ? ANSI.BRIGHT_BLUE : ANSI.BLUE;
100
+ return null;
101
+ }
102
+ /** Two or three channels are roughly equal and bright — pick the mix. */
103
+ function pickMixedAnsi(r, g, b) {
104
+ if (r > 200 && g > 200)
105
+ return ANSI.BRIGHT_YELLOW;
106
+ if (r > 200 && b > 200)
107
+ return ANSI.BRIGHT_MAGENTA;
108
+ if (g > 200 && b > 200)
109
+ return ANSI.BRIGHT_CYAN;
110
+ if (r > 180 && g > 180 && b > 180)
111
+ return ANSI.BRIGHT_WHITE;
112
+ return null;
113
+ }
114
+ /**
115
+ * Render TextContent to string
116
+ */
117
+ function renderContent(content, options) {
118
+ return content
119
+ .map((item) => {
120
+ if (typeof item === 'string') {
121
+ return item;
122
+ }
123
+ // IDecoration
124
+ return renderDecoration(item, options);
125
+ })
126
+ .join('');
127
+ }
128
+ /**
129
+ * Render a decoration to string
130
+ */
131
+ function renderDecoration(decoration, options) {
132
+ const innerText = renderContent(decoration.content, options);
133
+ if (!options.ansi) {
134
+ // Plain text fallback
135
+ if (decoration.className === 'sharpee-em')
136
+ return `*${innerText}*`;
137
+ if (decoration.className === 'sharpee-strong')
138
+ return `**${innerText}**`;
139
+ return innerText;
140
+ }
141
+ // ANSI rendering
142
+ let ansiCode = DECORATION_ANSI[decoration.className];
143
+ // Check for story-defined color, keyed on the final class name.
144
+ if (!ansiCode && options.colors?.[decoration.className]) {
145
+ const hex = options.colors[decoration.className];
146
+ if (hex.startsWith('#')) {
147
+ ansiCode = hexToAnsi(hex);
148
+ }
149
+ }
150
+ if (ansiCode) {
151
+ return `${ansiCode}${innerText}${ANSI.RESET}`;
152
+ }
153
+ // Unknown decoration class name - render without styling
154
+ return innerText;
155
+ }
156
+ /**
157
+ * Render a single block to string
158
+ */
159
+ function renderBlock(block, options) {
160
+ const text = renderContent(block.content, options);
161
+ // Apply block-level styling
162
+ if (options.ansi) {
163
+ if (block.key === 'room.name') {
164
+ return `${ANSI.BOLD}${ANSI.YELLOW}${text}${ANSI.RESET}`;
165
+ }
166
+ if (block.key === 'action.blocked' || block.key === 'error') {
167
+ return `${ANSI.RED}${text}${ANSI.RESET}`;
168
+ }
169
+ }
170
+ return text;
171
+ }
172
+ /**
173
+ * Render `ITextBlock[]` to string for display.
174
+ *
175
+ * Uses smart joining: single newline between consecutive blocks of the
176
+ * same key, double newline (or `options.blockSeparator`) between blocks
177
+ * of different keys. This keeps related output together (e.g., multiple
178
+ * "Taken." messages) while separating distinct sections.
179
+ *
180
+ * @example
181
+ * const output = renderToString(blocks, { ansi: true });
182
+ * console.log(output);
183
+ */
184
+ function renderToString(blocks, options = {}) {
185
+ const opts = {
186
+ ansi: false,
187
+ blockSeparator: '\n\n',
188
+ includeStatus: false,
189
+ ...options,
190
+ };
191
+ // Filter out status blocks unless explicitly included
192
+ const filteredBlocks = opts.includeStatus
193
+ ? blocks
194
+ : blocks.filter((b) => !(0, text_blocks_1.isStatusBlock)(b));
195
+ // Render each block and pair with its key for smart joining
196
+ const rendered = filteredBlocks
197
+ .map((block) => ({ key: block.key, text: renderBlock(block, opts) }))
198
+ .filter((item) => item.text.trim());
199
+ if (rendered.length === 0)
200
+ return '';
201
+ // Smart join: \n between same block types, \n\n between different types
202
+ const parts = [rendered[0].text];
203
+ for (let i = 1; i < rendered.length; i++) {
204
+ const prev = rendered[i - 1];
205
+ const curr = rendered[i];
206
+ // Use single newline for consecutive same-type blocks, double for different
207
+ const separator = prev.key === curr.key ? '\n' : opts.blockSeparator;
208
+ parts.push(separator + curr.text);
209
+ }
210
+ return parts.join('');
211
+ }
212
+ /**
213
+ * Render status blocks to a single line (for status bar display).
214
+ *
215
+ * @example
216
+ * const status = renderStatusLine(blocks);
217
+ * // "West of House | Score: 0 | Turns: 1"
218
+ */
219
+ function renderStatusLine(blocks, options = {}) {
220
+ const statusBlocks = blocks.filter(text_blocks_1.isStatusBlock);
221
+ const parts = [];
222
+ // Render in specific order
223
+ const roomBlock = statusBlocks.find((b) => b.key === 'status.room');
224
+ if (roomBlock) {
225
+ parts.push(renderContent(roomBlock.content, options));
226
+ }
227
+ const scoreBlock = statusBlocks.find((b) => b.key === 'status.score');
228
+ if (scoreBlock) {
229
+ parts.push(`Score: ${renderContent(scoreBlock.content, options)}`);
230
+ }
231
+ const turnsBlock = statusBlocks.find((b) => b.key === 'status.turns');
232
+ if (turnsBlock) {
233
+ parts.push(`Turns: ${renderContent(turnsBlock.content, options)}`);
234
+ }
235
+ // Add any custom status blocks
236
+ const knownKeys = new Set(['status.room', 'status.score', 'status.turns']);
237
+ const customBlocks = statusBlocks.filter((b) => !knownKeys.has(b.key));
238
+ for (const block of customBlocks) {
239
+ parts.push(renderContent(block.content, options));
240
+ }
241
+ return parts.join(' | ');
242
+ }
243
+ //# sourceMappingURL=render-to-string.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-to-string.js","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/channel-service/src/render-to-string.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @sharpee/channel-service/renderer — public surface.
3
+ *
4
+ * Owner context: consumer-side dispatcher. Re-exports the `Renderer`
5
+ * host, the `ChannelRenderer` plug-in contract, and the small helpers
6
+ * needed by concrete renderer hosts (browser, CLI, multi-user web
7
+ * client) per ADR-165 §1, §2, §3, §5, §7.
8
+ */
9
+ export type { ChannelRenderer, Renderer as IRenderer, ChannelStateStore, SlotHandle, } from './types';
10
+ export { Renderer, createRenderer, type RendererOptions } from './renderer';
11
+ export { createJsonTreeFallbackFactory, type FallbackOutputSink, type FallbackWarningSink, } from './json-tree-fallback';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/channel-service/src/renderer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EACV,eAAe,EACf,QAAQ,IAAI,SAAS,EACrB,iBAAiB,EACjB,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EACL,6BAA6B,EAC7B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /**
3
+ * @sharpee/channel-service/renderer — public surface.
4
+ *
5
+ * Owner context: consumer-side dispatcher. Re-exports the `Renderer`
6
+ * host, the `ChannelRenderer` plug-in contract, and the small helpers
7
+ * needed by concrete renderer hosts (browser, CLI, multi-user web
8
+ * client) per ADR-165 §1, §2, §3, §5, §7.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.createJsonTreeFallbackFactory = exports.createRenderer = exports.Renderer = void 0;
12
+ var renderer_1 = require("./renderer");
13
+ Object.defineProperty(exports, "Renderer", { enumerable: true, get: function () { return renderer_1.Renderer; } });
14
+ Object.defineProperty(exports, "createRenderer", { enumerable: true, get: function () { return renderer_1.createRenderer; } });
15
+ var json_tree_fallback_1 = require("./json-tree-fallback");
16
+ Object.defineProperty(exports, "createJsonTreeFallbackFactory", { enumerable: true, get: function () { return json_tree_fallback_1.createJsonTreeFallbackFactory; } });
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/channel-service/src/renderer/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AASH,uCAA4E;AAAnE,oGAAA,QAAQ,OAAA;AAAE,0GAAA,cAAc,OAAA;AAEjC,2DAI8B;AAH5B,mIAAA,6BAA6B,OAAA"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @sharpee/channel-service/renderer — generic JSON-tree fallback.
3
+ *
4
+ * Owner context: consumer-side dispatcher. Per ADR-165 §3, a channel
5
+ * id that appears in the CMGT manifest but has no registered
6
+ * `ChannelRenderer` falls back to a generic JSON-tree view. This
7
+ * keeps unknown story channels visible-and-debuggable rather than
8
+ * silently dropped.
9
+ *
10
+ * The fallback this module ships logs a one-time warning per channel
11
+ * id and writes a JSON-stringified line to the console. Concrete
12
+ * platform consumers (browser, CLI) override the warning sink and
13
+ * the rendering surface — for now the default is enough to satisfy
14
+ * AC-3.
15
+ *
16
+ * @see ADR-165 — Renderer Architecture — §3, AC-3
17
+ */
18
+ import type { ChannelRenderer } from './types';
19
+ /**
20
+ * Sink for warnings emitted by the fallback. Defaults to
21
+ * `console.warn`. Tests inject a recording sink; concrete platform
22
+ * consumers can route to a logger.
23
+ */
24
+ export type FallbackWarningSink = (message: string) => void;
25
+ /**
26
+ * Sink for the rendered JSON-tree output. Defaults to `console.log`.
27
+ * Concrete consumers redirect to a debug pane or stdout.
28
+ */
29
+ export type FallbackOutputSink = (channelId: string, json: string) => void;
30
+ /**
31
+ * Construct a JSON-tree fallback renderer factory.
32
+ *
33
+ * The returned function builds a `ChannelRenderer` for a specific
34
+ * channel id; the dispatcher caches one per unrendered id so the
35
+ * "one-time warning" behavior is preserved across emissions.
36
+ *
37
+ * @param warn — sink for the one-time warning. Default `console.warn`.
38
+ * @param output — sink for the rendered JSON line. Default
39
+ * `console.log`.
40
+ */
41
+ export declare function createJsonTreeFallbackFactory(opts?: {
42
+ warn?: FallbackWarningSink;
43
+ output?: FallbackOutputSink;
44
+ }): (channelId: string) => ChannelRenderer;
45
+ //# sourceMappingURL=json-tree-fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-tree-fallback.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/channel-service/src/renderer/json-tree-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAE5D;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAE3E;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,CAAC,EAAE;IACnD,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,eAAe,CAkBzC"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ /**
3
+ * @sharpee/channel-service/renderer — generic JSON-tree fallback.
4
+ *
5
+ * Owner context: consumer-side dispatcher. Per ADR-165 §3, a channel
6
+ * id that appears in the CMGT manifest but has no registered
7
+ * `ChannelRenderer` falls back to a generic JSON-tree view. This
8
+ * keeps unknown story channels visible-and-debuggable rather than
9
+ * silently dropped.
10
+ *
11
+ * The fallback this module ships logs a one-time warning per channel
12
+ * id and writes a JSON-stringified line to the console. Concrete
13
+ * platform consumers (browser, CLI) override the warning sink and
14
+ * the rendering surface — for now the default is enough to satisfy
15
+ * AC-3.
16
+ *
17
+ * @see ADR-165 — Renderer Architecture — §3, AC-3
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.createJsonTreeFallbackFactory = createJsonTreeFallbackFactory;
21
+ /**
22
+ * Construct a JSON-tree fallback renderer factory.
23
+ *
24
+ * The returned function builds a `ChannelRenderer` for a specific
25
+ * channel id; the dispatcher caches one per unrendered id so the
26
+ * "one-time warning" behavior is preserved across emissions.
27
+ *
28
+ * @param warn — sink for the one-time warning. Default `console.warn`.
29
+ * @param output — sink for the rendered JSON line. Default
30
+ * `console.log`.
31
+ */
32
+ function createJsonTreeFallbackFactory(opts) {
33
+ const warn = opts?.warn ?? defaultWarn;
34
+ const output = opts?.output ?? defaultOutput;
35
+ const warned = new Set();
36
+ return (channelId) => ({
37
+ onValue(value, channel) {
38
+ if (!warned.has(channelId)) {
39
+ warned.add(channelId);
40
+ warn(`[channel-service] No renderer registered for channel '${channelId}' ` +
41
+ `(contentType=${channel.contentType}, mode=${channel.mode}). ` +
42
+ `Falling back to JSON-tree view; values will be JSON-stringified.`);
43
+ }
44
+ output(channelId, safeStringify(value));
45
+ },
46
+ });
47
+ }
48
+ function defaultWarn(message) {
49
+ // Console only — no alerting, no throwing. The dispatcher routes
50
+ // the JSON-tree output separately.
51
+ // eslint-disable-next-line no-console
52
+ console.warn(message);
53
+ }
54
+ function defaultOutput(channelId, json) {
55
+ // eslint-disable-next-line no-console
56
+ console.log(`[${channelId}] ${json}`);
57
+ }
58
+ /**
59
+ * `JSON.stringify` with a circular-reference fallback. Renderer
60
+ * fallback should never throw — bad data is debuggable, but a
61
+ * thrown stringifier breaks dispatch.
62
+ */
63
+ function safeStringify(value) {
64
+ try {
65
+ return JSON.stringify(value);
66
+ }
67
+ catch {
68
+ return '[unserializable]';
69
+ }
70
+ }
71
+ //# sourceMappingURL=json-tree-fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-tree-fallback.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/channel-service/src/renderer/json-tree-fallback.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;AA6BH,sEAqBC;AAhCD;;;;;;;;;;GAUG;AACH,SAAgB,6BAA6B,CAAC,IAG7C;IACC,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,WAAW,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,IAAI,aAAa,CAAC;IAC7C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IAEjC,OAAO,CAAC,SAAiB,EAAmB,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,KAAc,EAAE,OAA0B;YAChD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACtB,IAAI,CACF,yDAAyD,SAAS,IAAI;oBACpE,gBAAgB,OAAO,CAAC,WAAW,UAAU,OAAO,CAAC,IAAI,KAAK;oBAC9D,kEAAkE,CACrE,CAAC;YACJ,CAAC;YACD,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1C,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,iEAAiE;IACjE,mCAAmC;IACnC,sCAAsC;IACtC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,aAAa,CAAC,SAAiB,EAAE,IAAY;IACpD,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,kBAAkB,CAAC;IAC5B,CAAC;AACH,CAAC"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @sharpee/channel-service/renderer — `Renderer` host implementation.
3
+ *
4
+ * Owner context: consumer-side dispatcher. Drives `CmgtPacket` and
5
+ * `TurnPacket` instances into registered `ChannelRenderer` plug-ins,
6
+ * holds the channel state store, and pumps commands back to the
7
+ * engine.
8
+ *
9
+ * Public interface (per ADR-165 §2, §3, §4, §5, §7):
10
+ * - `Renderer` class implementing the same-named interface.
11
+ * - `createRenderer(opts)` factory for ergonomic instantiation.
12
+ *
13
+ * @see ADR-165 — Renderer Architecture
14
+ */
15
+ import type { CmgtPacket, CommandPacket, TurnPacket } from "../../if-domain/index";
16
+ import type { ChannelRenderer, ChannelStateStore, Renderer as RendererInterface, SlotHandle } from './types';
17
+ import { type FallbackOutputSink, type FallbackWarningSink } from './json-tree-fallback';
18
+ /**
19
+ * Optional construction options for the `Renderer`.
20
+ */
21
+ export interface RendererOptions {
22
+ /**
23
+ * Sink for warnings (unrendered channels, payload keys not in the
24
+ * manifest). Defaults to `console.warn`.
25
+ */
26
+ warn?: (message: string) => void;
27
+ /**
28
+ * Sink for unhandled-channel JSON-tree output. Defaults to
29
+ * `console.log` — concrete consumers redirect to their debug surface.
30
+ */
31
+ fallbackOutput?: FallbackOutputSink;
32
+ /**
33
+ * Override the warning sink the fallback uses. Defaults to
34
+ * `opts.warn ?? console.warn`. Exposed as a separate option so
35
+ * tests can record fallback warnings independently of dispatch
36
+ * warnings.
37
+ */
38
+ fallbackWarn?: FallbackWarningSink;
39
+ }
40
+ /**
41
+ * Concrete `Renderer` host (ADR-165 §2).
42
+ *
43
+ * One instance per session. Holds:
44
+ * - the registered `ChannelRenderer`s (last-write-wins per channel id),
45
+ * - the channel state store (replace-latest, append-accumulating,
46
+ * event-no-entry — ADR-165 §5),
47
+ * - the slot handles (`getSlot` / `registerSlot` per §7),
48
+ * - the current `CmgtPacket` (so `onValue` can pass the matching
49
+ * `ChannelDefinition` to the renderer),
50
+ * - the command-handler subscriber list.
51
+ *
52
+ * No DOM, no transport, no engine. Concrete consumers compose this
53
+ * with their host platform.
54
+ */
55
+ export declare class Renderer implements RendererInterface {
56
+ private renderers;
57
+ private fallbackRenderers;
58
+ private state;
59
+ private slots;
60
+ private commandHandlers;
61
+ private currentManifest?;
62
+ private readonly warn;
63
+ private readonly buildFallback;
64
+ constructor(opts?: RendererOptions);
65
+ /**
66
+ * Register a `ChannelRenderer` (ADR-165 §3). Last-write-wins —
67
+ * re-registering replaces the prior renderer for that id.
68
+ */
69
+ registerRenderer(channelId: string, renderer: ChannelRenderer): void;
70
+ /**
71
+ * Register a slot (ADR-165 §7). Stories that replace the
72
+ * platform-default layout call this for each region.
73
+ */
74
+ registerSlot(name: string, handle: SlotHandle): void;
75
+ /**
76
+ * Resolve a slot handle, or `null` if not registered.
77
+ */
78
+ getSlot(name: string): SlotHandle | null;
79
+ /**
80
+ * Subscribe to commands emitted by channel renderers.
81
+ */
82
+ onCommand(handler: (cmd: CommandPacket) => void): void;
83
+ /**
84
+ * Emit a command from a `ChannelRenderer` (UI-gesture origin).
85
+ * All registered handlers receive the packet synchronously.
86
+ */
87
+ emitCommand(text: string): void;
88
+ /**
89
+ * Apply a CMGT packet (ADR-165 §4 lifecycle).
90
+ *
91
+ * 1. If a prior manifest exists, run `onDestroy` for each of its
92
+ * renderers (in registration order for determinism).
93
+ * 2. Reset the state store.
94
+ * 3. Update the current manifest.
95
+ * 4. Run `onCmgt` for each new-manifest renderer in registration
96
+ * order.
97
+ */
98
+ applyCmgt(packet: CmgtPacket): void;
99
+ /**
100
+ * Dispatch a turn packet (ADR-165 §4 dispatch contract).
101
+ *
102
+ * Iterates the current manifest in registration order. For each
103
+ * manifest entry that has a payload value, updates the state
104
+ * store per mode and invokes `onValue`. Handles the `clear`
105
+ * channel specially — empties append-mode state stores and fires
106
+ * `onClear` hooks.
107
+ */
108
+ applyTurnPacket(packet: TurnPacket): void;
109
+ /**
110
+ * Snapshot of the channel state store (deep-cloned) — for tests
111
+ * and ADR-163 AC-12 round-trip.
112
+ */
113
+ getStateSnapshot(): ChannelStateStore;
114
+ /**
115
+ * Update the state store per mode and invoke the renderer's
116
+ * `onValue`.
117
+ */
118
+ private applyChannelValue;
119
+ /**
120
+ * Handle a `clear` channel emission (ADR-165 §4 step 2).
121
+ *
122
+ * `value.target` (when set) names a specific append-mode channel
123
+ * to clear; an empty / missing target clears every append-mode
124
+ * channel currently registered in the manifest.
125
+ */
126
+ private handleClear;
127
+ /**
128
+ * Resolve the renderer for a channel id. Returns the registered
129
+ * renderer when present; otherwise lazily creates and caches a
130
+ * JSON-tree fallback (so the one-time warning fires once per
131
+ * channel id, not once per emission).
132
+ */
133
+ private resolveRenderer;
134
+ }
135
+ /**
136
+ * Convenience factory for `new Renderer(opts)`.
137
+ */
138
+ export declare function createRenderer(opts?: RendererOptions): Renderer;
139
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/channel-service/src/renderer/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAEV,UAAU,EACV,aAAa,EACb,UAAU,EACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EACjB,QAAQ,IAAI,iBAAiB,EAC7B,UAAU,EACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAE9B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;;OAGG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAS,YAAW,iBAAiB;IAChD,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,iBAAiB,CAAsC;IAC/D,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,eAAe,CAA2C;IAClE,OAAO,CAAC,eAAe,CAAC,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;gBAE3D,IAAI,GAAE,eAAoB;IAetC;;;OAGG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI;IAIpE;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAIpD;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAQxC;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAItD;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAW/B;;;;;;;;;OASG;IACH,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAoBnC;;;;;;;;OAQG;IACH,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IA8BzC;;;OAGG;IACH,gBAAgB,IAAI,iBAAiB;IAQrC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAuBzB;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IAoBnB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;CAUxB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAE,eAAoB,GAAG,QAAQ,CAEnE"}