@helioslx/core 0.2.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/CONTRIBUTING.md +83 -0
  3. package/LICENSE +256 -0
  4. package/README.md +159 -0
  5. package/SECURITY.md +67 -0
  6. package/dist/contracts-C4kpAdZq.d.ts +265 -0
  7. package/dist/contracts-C4kpAdZq.d.ts.map +1 -0
  8. package/dist/http.d.ts +643 -0
  9. package/dist/http.d.ts.map +1 -0
  10. package/dist/http.js +670 -0
  11. package/dist/http.js.map +1 -0
  12. package/dist/index.d.ts +40 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +270 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/node.d.ts +114 -0
  17. package/dist/node.d.ts.map +1 -0
  18. package/dist/node.js +334 -0
  19. package/dist/node.js.map +1 -0
  20. package/dist/redis.d.ts +46 -0
  21. package/dist/redis.d.ts.map +1 -0
  22. package/dist/redis.js +174 -0
  23. package/dist/redis.js.map +1 -0
  24. package/dist/source-DB1oq2GT.js +884 -0
  25. package/dist/source-DB1oq2GT.js.map +1 -0
  26. package/dist/source-D_XNWXS9.d.ts +76 -0
  27. package/dist/source-D_XNWXS9.d.ts.map +1 -0
  28. package/dist/testing.d.ts +38 -0
  29. package/dist/testing.d.ts.map +1 -0
  30. package/dist/testing.js +98 -0
  31. package/dist/testing.js.map +1 -0
  32. package/dist/validation-BdsVeyAE.js +151 -0
  33. package/dist/validation-BdsVeyAE.js.map +1 -0
  34. package/examples/embedded-elysia-http.ts +30 -0
  35. package/examples/full-frame-fade.ts +25 -0
  36. package/examples/graceful-shutdown.ts +19 -0
  37. package/examples/quickstart.ts +30 -0
  38. package/examples/redis.ts +30 -0
  39. package/examples/sparse-channels.ts +23 -0
  40. package/examples/viewer-subscription.ts +38 -0
  41. package/examples/viewer-tui.ts +201 -0
  42. package/package.json +108 -0
  43. package/src/contracts.ts +302 -0
  44. package/src/http.ts +1101 -0
  45. package/src/index.ts +61 -0
  46. package/src/memory-store.ts +45 -0
  47. package/src/node.ts +578 -0
  48. package/src/output-engine.ts +778 -0
  49. package/src/redis.ts +258 -0
  50. package/src/source.ts +502 -0
  51. package/src/testing.ts +139 -0
  52. package/src/validation.ts +328 -0
  53. package/src/viewer.ts +368 -0
@@ -0,0 +1,302 @@
1
+ export const SLOT_COUNT = 512;
2
+ export const MIN_UNIVERSE = 1;
3
+ export const MAX_UNIVERSE = 63_999;
4
+ export const MIN_PRIORITY = 0;
5
+ export const MAX_PRIORITY = 200;
6
+ export const DEFAULT_PRIORITY = 100;
7
+ export const DEFAULT_ACTIVE_FPS = 44;
8
+ export const DEFAULT_IDLE_FPS = 2;
9
+
10
+ export interface OutputAddress {
11
+ readonly universe: number;
12
+ readonly priority?: number;
13
+ }
14
+
15
+ export interface OutputOptions extends OutputAddress {
16
+ readonly cid?: string;
17
+ readonly idleFps?: number;
18
+ readonly sourceName?: string;
19
+ }
20
+
21
+ /** One-based DMX channel → value map used by setChannels / fadeChannels. */
22
+ export type ChannelValues = Readonly<Record<number, number>>;
23
+
24
+ export interface ChannelWrite {
25
+ /** One-based DMX slot number. */
26
+ readonly channel: number;
27
+ readonly value: number;
28
+ /** Linear transition duration in milliseconds. Defaults to 0 (snap). */
29
+ readonly durationMs?: number;
30
+ }
31
+
32
+ export interface TransitionWrite {
33
+ readonly channel: number;
34
+ readonly value: number;
35
+ readonly durationMs: number;
36
+ }
37
+
38
+ export interface FadeChannelsOptions {
39
+ readonly durationMs: number;
40
+ readonly signal?: AbortSignal;
41
+ }
42
+
43
+ export interface WriteFrameOptions {
44
+ readonly durationMs?: number;
45
+ readonly signal?: AbortSignal;
46
+ }
47
+
48
+ export interface ClearUniverseOptions {
49
+ readonly signal?: AbortSignal;
50
+ }
51
+
52
+ export interface UniverseOptions {
53
+ readonly priority?: number;
54
+ readonly cid?: string;
55
+ readonly idleFps?: number;
56
+ readonly sourceName?: string;
57
+ }
58
+
59
+ export type FrameMode = "active" | "idle";
60
+
61
+ export interface OutputSnapshot {
62
+ readonly activeTransitions: number;
63
+ readonly cid: string;
64
+ readonly current: readonly number[];
65
+ readonly dirty: boolean;
66
+ readonly frameMode: FrameMode;
67
+ readonly idleFps: number;
68
+ readonly lastError: string | null;
69
+ readonly lastSent: readonly number[];
70
+ readonly lastSentAt: number | null;
71
+ readonly nextDueAt: number;
72
+ readonly priority: number;
73
+ readonly sequence: number;
74
+ readonly sourceName: string | null;
75
+ readonly target: readonly number[];
76
+ readonly universe: number;
77
+ readonly updatedAt: number;
78
+ }
79
+
80
+ export interface OutputRecord {
81
+ readonly cid: string;
82
+ readonly idleFps: number;
83
+ readonly priority: number;
84
+ readonly sourceName: string | null;
85
+ readonly target: readonly number[];
86
+ readonly universe: number;
87
+ readonly updatedAt: number;
88
+ }
89
+
90
+ export interface OutputStore {
91
+ get(address: Required<OutputAddress>): Promise<OutputRecord | null>;
92
+ list(): Promise<readonly OutputRecord[]>;
93
+ remove(address: Required<OutputAddress>): Promise<void>;
94
+ save(record: OutputRecord): Promise<void>;
95
+ close?(): Promise<void>;
96
+ }
97
+
98
+ export interface OutputPacket {
99
+ readonly cid: string;
100
+ readonly data: Uint8Array;
101
+ readonly priority: number;
102
+ readonly sequence: number;
103
+ readonly sourceName: string | null;
104
+ readonly universe: number;
105
+ }
106
+
107
+ export interface OutputTransport {
108
+ send(packet: OutputPacket, signal: AbortSignal): Promise<void>;
109
+ closeOutput?(address: Required<OutputAddress>): Promise<void>;
110
+ close(): Promise<void>;
111
+ }
112
+
113
+ export interface Clock {
114
+ /** Monotonic milliseconds used for scheduling. */
115
+ now(): number;
116
+ /** Wall-clock Unix milliseconds used for public and persisted metadata. */
117
+ wallNow?(): number;
118
+ sleep(delayMs: number, signal: AbortSignal): Promise<void>;
119
+ }
120
+
121
+ export interface Logger {
122
+ debug?(message: string, context?: Readonly<Record<string, unknown>>): void;
123
+ info?(message: string, context?: Readonly<Record<string, unknown>>): void;
124
+ warn?(message: string, context?: Readonly<Record<string, unknown>>): void;
125
+ error?(message: string, context?: Readonly<Record<string, unknown>>): void;
126
+ }
127
+
128
+ export interface TransportTelemetry {
129
+ readonly sendAttempts: number;
130
+ readonly sendFailures: number;
131
+ readonly sendRetries: number;
132
+ readonly sendSuccesses: number;
133
+ readonly sendTimeouts: number;
134
+ }
135
+
136
+ export interface EngineTelemetry {
137
+ readonly closed: boolean;
138
+ readonly lastLoopCompletedAt: number | null;
139
+ readonly lastLoopDurationMs: number | null;
140
+ readonly lastLoopStartedAt: number | null;
141
+ readonly loopIterations: number;
142
+ readonly outputCount: number;
143
+ readonly outputs: readonly OutputSnapshot[];
144
+ readonly running: boolean;
145
+ readonly transport: TransportTelemetry;
146
+ }
147
+
148
+ export type SacnLifecycleHook = () => void | Promise<void>;
149
+
150
+ export interface SacnSourceOptions {
151
+ readonly transport: OutputTransport;
152
+ readonly store?: OutputStore;
153
+ readonly clock?: Clock;
154
+ readonly logger?: Logger;
155
+ readonly activeFps?: number;
156
+ readonly idleFps?: number;
157
+ readonly sendTimeoutMs?: number;
158
+ /** Maximum time spent awaiting transport shutdown. Defaults to 2 seconds. */
159
+ readonly shutdownTimeoutMs?: number;
160
+ /** Maximum number of active universe/priority outputs. Defaults to 1024. */
161
+ readonly maxOutputs?: number;
162
+ /** Close the injected transport with the source. Defaults to false. */
163
+ readonly ownsTransport?: boolean;
164
+ /** Defaults to true for the default memory store, false for an injected store. */
165
+ readonly ownsStore?: boolean;
166
+ readonly createId?: () => string;
167
+ readonly onStart?: SacnLifecycleHook;
168
+ readonly onStop?: SacnLifecycleHook;
169
+ readonly onClose?: SacnLifecycleHook;
170
+ }
171
+
172
+ export type SacnSourceEvent =
173
+ | { readonly type: "started" }
174
+ | { readonly type: "stopped" }
175
+ | { readonly type: "output-updated"; readonly output: OutputSnapshot }
176
+ | {
177
+ readonly type: "output-cleared";
178
+ readonly address: Required<OutputAddress>;
179
+ }
180
+ | { readonly type: "closed" };
181
+
182
+ export interface UniverseContract {
183
+ readonly universe: number;
184
+ readonly priority: number;
185
+ setChannels(
186
+ values: ChannelValues,
187
+ options?: { readonly signal?: AbortSignal },
188
+ ): Promise<OutputSnapshot>;
189
+ fadeChannels(
190
+ values: ChannelValues,
191
+ options: FadeChannelsOptions,
192
+ ): Promise<OutputSnapshot>;
193
+ transition(
194
+ writes: readonly TransitionWrite[],
195
+ options?: { readonly signal?: AbortSignal },
196
+ ): Promise<OutputSnapshot>;
197
+ write(
198
+ values: readonly number[] | Uint8Array,
199
+ options?: WriteFrameOptions,
200
+ ): Promise<OutputSnapshot>;
201
+ get(): Promise<OutputSnapshot | null>;
202
+ clear(options?: ClearUniverseOptions): Promise<boolean>;
203
+ }
204
+
205
+ export interface SacnSourceContract {
206
+ universe(universe: number, options?: UniverseOptions): UniverseContract;
207
+ start(): Promise<void>;
208
+ stop(): Promise<void>;
209
+ listUniverses(): Promise<readonly OutputSnapshot[]>;
210
+ getTelemetry(): EngineTelemetry;
211
+ subscribe(listener: (event: SacnSourceEvent) => void): () => void;
212
+ close(): Promise<void>;
213
+ }
214
+
215
+ export interface ReceiverPacket {
216
+ readonly cid: string;
217
+ readonly priority: number;
218
+ readonly sequence: number;
219
+ readonly sourceAddress: string | null;
220
+ readonly sourceName: string | null;
221
+ readonly universe: number;
222
+ readonly values: Uint8Array;
223
+ }
224
+
225
+ export type ReceiverPacketListener = (packet: ReceiverPacket) => void;
226
+
227
+ /** Runtime-neutral source of normalized sACN packets. */
228
+ export interface Receiver {
229
+ addUniverse(universe: number): Promise<void> | void;
230
+ removeUniverse(universe: number): Promise<void> | void;
231
+ subscribe(listener: ReceiverPacketListener): () => void;
232
+ close(): Promise<void>;
233
+ }
234
+
235
+ export interface ViewerSourceMetadata {
236
+ readonly cid: string;
237
+ readonly priority: number;
238
+ readonly sequence: number;
239
+ readonly sourceAddress: string | null;
240
+ readonly sourceName: string | null;
241
+ }
242
+
243
+ export interface ViewerPacket {
244
+ readonly receivedAt: number;
245
+ readonly source: ViewerSourceMetadata;
246
+ readonly universe: number;
247
+ /** Exactly 512 normalized values. */
248
+ readonly values: readonly number[];
249
+ }
250
+
251
+ export interface ViewerRecord {
252
+ readonly selectedUniverses: readonly number[];
253
+ readonly updatedAt: number;
254
+ }
255
+
256
+ export interface ViewerStore {
257
+ load(): Promise<ViewerRecord | null>;
258
+ save(record: ViewerRecord): Promise<void>;
259
+ close?(): Promise<void>;
260
+ }
261
+
262
+ export interface ViewerTelemetry {
263
+ readonly droppedUpdates: number;
264
+ readonly packetsReceived: number;
265
+ readonly selectedUniverses: readonly number[];
266
+ readonly streamCount: number;
267
+ }
268
+
269
+ export interface ViewerPacketStream extends AsyncIterable<ViewerPacket> {
270
+ close(): void;
271
+ }
272
+
273
+ export interface ViewerServiceContract {
274
+ start(): Promise<void>;
275
+ getSelectedUniverses(): readonly number[];
276
+ setSelectedUniverses(
277
+ universes: readonly number[],
278
+ ): Promise<readonly number[]>;
279
+ addUniverse(universe: number): Promise<readonly number[]>;
280
+ removeUniverse(universe: number): Promise<readonly number[]>;
281
+ subscribe(listener: (packet: ViewerPacket) => void): () => void;
282
+ packets(capacity?: number): ViewerPacketStream;
283
+ getTelemetry(): ViewerTelemetry;
284
+ close(): Promise<void>;
285
+ }
286
+
287
+ export interface ViewerServiceOptions {
288
+ readonly receiver: Receiver;
289
+ readonly store?: ViewerStore;
290
+ readonly clock?: Pick<Clock, "now">;
291
+ readonly logger?: Logger;
292
+ /** Close the injected receiver with this service. Defaults to false. */
293
+ readonly ownsReceiver?: boolean;
294
+ /** Defaults to true for the default memory store, false for an injected store. */
295
+ readonly ownsStore?: boolean;
296
+ /** Default queue capacity for streams. Defaults to 32. */
297
+ readonly streamCapacity?: number;
298
+ /** Maximum number of selected universes. Defaults to 256. */
299
+ readonly maxUniverses?: number;
300
+ /** Maximum combined callback and stream subscribers. Defaults to 256. */
301
+ readonly maxListeners?: number;
302
+ }