@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.
- package/CHANGELOG.md +44 -0
- package/CONTRIBUTING.md +83 -0
- package/LICENSE +256 -0
- package/README.md +159 -0
- package/SECURITY.md +67 -0
- package/dist/contracts-C4kpAdZq.d.ts +265 -0
- package/dist/contracts-C4kpAdZq.d.ts.map +1 -0
- package/dist/http.d.ts +643 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +670 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +270 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +114 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +334 -0
- package/dist/node.js.map +1 -0
- package/dist/redis.d.ts +46 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +174 -0
- package/dist/redis.js.map +1 -0
- package/dist/source-DB1oq2GT.js +884 -0
- package/dist/source-DB1oq2GT.js.map +1 -0
- package/dist/source-D_XNWXS9.d.ts +76 -0
- package/dist/source-D_XNWXS9.d.ts.map +1 -0
- package/dist/testing.d.ts +38 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +98 -0
- package/dist/testing.js.map +1 -0
- package/dist/validation-BdsVeyAE.js +151 -0
- package/dist/validation-BdsVeyAE.js.map +1 -0
- package/examples/embedded-elysia-http.ts +30 -0
- package/examples/full-frame-fade.ts +25 -0
- package/examples/graceful-shutdown.ts +19 -0
- package/examples/quickstart.ts +30 -0
- package/examples/redis.ts +30 -0
- package/examples/sparse-channels.ts +23 -0
- package/examples/viewer-subscription.ts +38 -0
- package/examples/viewer-tui.ts +201 -0
- package/package.json +108 -0
- package/src/contracts.ts +302 -0
- package/src/http.ts +1101 -0
- package/src/index.ts +61 -0
- package/src/memory-store.ts +45 -0
- package/src/node.ts +578 -0
- package/src/output-engine.ts +778 -0
- package/src/redis.ts +258 -0
- package/src/source.ts +502 -0
- package/src/testing.ts +139 -0
- package/src/validation.ts +328 -0
- package/src/viewer.ts +368 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-DB1oq2GT.js","names":["#records","#outputs","#pendingMutations","#transport","#clock","#logger","#activeIntervalMs","#defaultIdleFps","#sendTimeoutMs","#shutdownTimeoutMs","#maxOutputs","#closeTransport","#createId","#lifecycle","#transportTelemetry","#assertOpen","#key","#createState","#cloneState","#wakeLoop","#running","#loopPromise","#loop","#getOrCreate","#updateTransitions","#markChanged","#snapshot","#beginSend","#closed","#loopIterations","#lastLoopStartedAt","#lastLoopCompletedAt","#applyOptions","#wallNow","#mode","#wait","#send","#sendWithTimeout","#interval","#wake","#source","#defaults","#store","#engine","#ownsStore","#logger","#onStart","#onStop","#onClose","#listeners","#universes","#closing","#closed","#enqueue","#startLocked","#running","#runHook","#emit","#ensureStartedLocked","#persist","#closePromise","#queue","#restored"],"sources":["../src/memory-store.ts","../src/output-engine.ts","../src/source.ts"],"sourcesContent":["import type {\n OutputAddress,\n OutputRecord,\n OutputStore,\n} from \"./contracts.js\";\n\nconst keyOf = ({ universe, priority }: Required<OutputAddress>): string =>\n `${universe}:${priority}`;\n\nconst cloneRecord = (record: OutputRecord): OutputRecord =>\n Object.freeze({\n ...record,\n target: Object.freeze([...record.target]),\n });\n\nexport class MemoryOutputStore implements OutputStore {\n readonly #records = new Map<string, OutputRecord>();\n\n async get(address: Required<OutputAddress>): Promise<OutputRecord | null> {\n const record = this.#records.get(keyOf(address));\n return record ? cloneRecord(record) : null;\n }\n\n async list(): Promise<readonly OutputRecord[]> {\n return Object.freeze(\n [...this.#records.values()]\n .sort(\n (left, right) =>\n left.universe - right.universe || left.priority - right.priority,\n )\n .map(cloneRecord),\n );\n }\n\n async remove(address: Required<OutputAddress>): Promise<void> {\n this.#records.delete(keyOf(address));\n }\n\n async save(record: OutputRecord): Promise<void> {\n this.#records.set(\n keyOf({ universe: record.universe, priority: record.priority }),\n cloneRecord(record),\n );\n }\n}\n","import {\n DEFAULT_ACTIVE_FPS,\n DEFAULT_IDLE_FPS,\n SLOT_COUNT,\n type ChannelWrite,\n type Clock,\n type EngineTelemetry,\n type FrameMode,\n type Logger,\n type OutputAddress,\n type OutputOptions,\n type OutputPacket,\n type OutputRecord,\n type OutputSnapshot,\n type OutputTransport,\n} from \"./contracts.js\";\nimport {\n SacnLifecycleError,\n TransportError,\n TransportTimeoutError,\n assertCid,\n assertFade,\n assertFps,\n assertSourceName,\n assertTimeout,\n normalizeAddress,\n toValidatedFrame,\n validateChannelWrites,\n} from \"./validation.js\";\n\ninterface Transition {\n readonly from: number;\n readonly to: number;\n readonly startedAt: number;\n readonly durationMs: number;\n}\n\ninterface OutputState {\n readonly address: Required<OutputAddress>;\n cid: string;\n idleFps: number;\n sourceName: string | null;\n readonly current: Uint8Array;\n readonly target: Uint8Array;\n readonly lastSent: Uint8Array;\n readonly transitions: Map<number, Transition>;\n sequence: number;\n lastSentAt: number | null;\n nextDueAt: number;\n dirty: boolean;\n lastError: string | null;\n consecutiveFailures: number;\n updatedAt: number;\n revision: number;\n sending: Promise<boolean> | null;\n transportPending: Promise<void> | null;\n}\n\nexport interface OutputMutationCheckpoint {\n readonly key: string;\n readonly state: OutputState | null;\n}\n\ninterface OutputEngineOptions {\n readonly transport: OutputTransport;\n readonly clock: Clock;\n readonly logger: Logger;\n readonly activeFps?: number;\n readonly idleFps?: number;\n readonly sendTimeoutMs?: number;\n readonly shutdownTimeoutMs?: number;\n readonly maxOutputs?: number;\n readonly closeTransport?: boolean;\n readonly createId: () => string;\n}\n\nconst framesEqual = (left: Uint8Array, right: Uint8Array): boolean => {\n for (let index = 0; index < SLOT_COUNT; index += 1) {\n if (left[index] !== right[index]) return false;\n }\n return true;\n};\n\nconst frozenFrame = (frame: Uint8Array): readonly number[] =>\n Object.freeze(Array.from(frame));\n\nconst errorMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\nexport class SystemClock implements Clock {\n now(): number {\n return performance.now();\n }\n\n wallNow(): number {\n return Date.now();\n }\n\n sleep(delayMs: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) {\n reject(signal.reason ?? new Error(\"Operation aborted.\"));\n return;\n }\n const handle = setTimeout(resolve, delayMs);\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(handle);\n reject(signal.reason ?? new Error(\"Operation aborted.\"));\n },\n { once: true },\n );\n });\n }\n}\n\nexport class OutputEngine {\n readonly #outputs = new Map<string, OutputState>();\n readonly #pendingMutations = new Set<string>();\n readonly #transport: OutputTransport;\n readonly #clock: Clock;\n readonly #logger: Logger;\n readonly #activeIntervalMs: number;\n readonly #defaultIdleFps: number;\n readonly #sendTimeoutMs: number;\n readonly #shutdownTimeoutMs: number;\n readonly #maxOutputs: number;\n readonly #closeTransport: boolean;\n readonly #createId: () => string;\n readonly #lifecycle = new AbortController();\n #running = false;\n #closed = false;\n #loopPromise: Promise<void> | null = null;\n #wake: (() => void) | null = null;\n #loopIterations = 0;\n #lastLoopStartedAt: number | null = null;\n #lastLoopCompletedAt: number | null = null;\n readonly #transportTelemetry = {\n sendAttempts: 0,\n sendFailures: 0,\n sendRetries: 0,\n sendSuccesses: 0,\n sendTimeouts: 0,\n };\n\n constructor(options: OutputEngineOptions) {\n const activeFps = options.activeFps ?? DEFAULT_ACTIVE_FPS;\n const idleFps = options.idleFps ?? DEFAULT_IDLE_FPS;\n const sendTimeoutMs = options.sendTimeoutMs ?? 1000;\n const shutdownTimeoutMs = options.shutdownTimeoutMs ?? 2000;\n const maxOutputs = options.maxOutputs ?? 1024;\n assertFps(activeFps, \"Active FPS\");\n assertFps(idleFps, \"Idle FPS\");\n assertTimeout(sendTimeoutMs);\n assertTimeout(shutdownTimeoutMs);\n if (!Number.isInteger(maxOutputs) || maxOutputs < 1) {\n throw new SacnLifecycleError(\n \"Maximum outputs must be a positive integer.\",\n );\n }\n this.#transport = options.transport;\n this.#clock = options.clock;\n this.#logger = options.logger;\n this.#activeIntervalMs = 1000 / activeFps;\n this.#defaultIdleFps = idleFps;\n this.#sendTimeoutMs = sendTimeoutMs;\n this.#shutdownTimeoutMs = shutdownTimeoutMs;\n this.#maxOutputs = maxOutputs;\n this.#closeTransport = options.closeTransport ?? true;\n this.#createId = options.createId;\n }\n\n restore(record: OutputRecord): void {\n this.#assertOpen();\n const address = normalizeAddress(record);\n assertFps(record.idleFps, \"Idle FPS\");\n if (this.#outputs.has(this.#key(address))) return;\n if (this.#outputs.size >= this.#maxOutputs) {\n throw new SacnLifecycleError(\n `Output limit of ${this.#maxOutputs} has been reached while restoring state.`,\n );\n }\n const state = this.#createState(record, toValidatedFrame(record.target));\n this.#outputs.set(this.#key(address), state);\n }\n\n async beginMutation(\n addressInput: OutputAddress,\n ): Promise<OutputMutationCheckpoint> {\n this.#assertOpen();\n const address = normalizeAddress(addressInput);\n const key = this.#key(address);\n if (this.#pendingMutations.has(key)) {\n throw new SacnLifecycleError(`Output ${key} already has a pending mutation.`);\n }\n const output = this.#outputs.get(key);\n if (output?.sending) await output.sending;\n if (output?.transportPending) await output.transportPending;\n this.#pendingMutations.add(key);\n return {\n key,\n state: output ? this.#cloneState(output) : null,\n };\n }\n\n commitMutation(checkpoint: OutputMutationCheckpoint): void {\n this.#pendingMutations.delete(checkpoint.key);\n this.#wakeLoop();\n }\n\n rollbackMutation(checkpoint: OutputMutationCheckpoint): void {\n if (checkpoint.state) {\n this.#outputs.set(checkpoint.key, checkpoint.state);\n } else {\n this.#outputs.delete(checkpoint.key);\n }\n this.#pendingMutations.delete(checkpoint.key);\n this.#wakeLoop();\n }\n\n start(): void {\n this.#assertOpen();\n if (this.#running) return;\n this.#running = true;\n this.#loopPromise = this.#loop();\n this.#logger.info?.(\"Output engine started.\", {\n activeFrameIntervalMs: this.#activeIntervalMs,\n idleFps: this.#defaultIdleFps,\n });\n }\n\n /**\n * Pauses the scheduler without tearing down outputs or the transport.\n * `start()` can resume the loop afterward.\n */\n async stop(): Promise<void> {\n this.#assertOpen();\n if (!this.#running) return;\n this.#running = false;\n this.#wakeLoop();\n if (this.#loopPromise) {\n await this.#loopPromise;\n this.#loopPromise = null;\n }\n this.#logger.info?.(\"Output engine stopped.\");\n }\n\n writeFrame(\n options: OutputOptions,\n frame: Uint8Array,\n durationMs: number,\n ): OutputSnapshot {\n this.#assertOpen();\n assertFade(durationMs);\n const output = this.#getOrCreate(options);\n const now = this.#clock.now();\n this.#updateTransitions(output, now);\n if (durationMs === 0) {\n output.current.set(frame);\n output.target.set(frame);\n output.transitions.clear();\n } else {\n for (let index = 0; index < SLOT_COUNT; index += 1) {\n const target = frame[index] ?? 0;\n if (target === output.current[index]) {\n output.target[index] = target;\n output.transitions.delete(index);\n } else {\n output.target[index] = target;\n output.transitions.set(index, {\n from: output.current[index] ?? 0,\n to: target,\n startedAt: now,\n durationMs,\n });\n }\n }\n }\n this.#markChanged(output, now);\n return this.#snapshot(output);\n }\n\n writeChannels(\n options: OutputOptions,\n writes: readonly ChannelWrite[],\n ): OutputSnapshot {\n this.#assertOpen();\n validateChannelWrites(writes);\n const output = this.#getOrCreate(options);\n const now = this.#clock.now();\n this.#updateTransitions(output, now);\n for (const write of writes) {\n const index = write.channel - 1;\n const durationMs = write.durationMs ?? 0;\n const from = output.current[index] ?? 0;\n output.target[index] = write.value;\n if (durationMs === 0 || from === write.value) {\n output.current[index] = write.value;\n output.transitions.delete(index);\n } else {\n output.transitions.set(index, {\n from,\n to: write.value,\n startedAt: now,\n durationMs,\n });\n }\n }\n this.#markChanged(output, now);\n return this.#snapshot(output);\n }\n\n getOutput(addressInput: OutputAddress): OutputSnapshot | null {\n this.#assertOpen();\n const address = normalizeAddress(addressInput);\n const output = this.#outputs.get(this.#key(address));\n if (!output) return null;\n this.#updateTransitions(output, this.#clock.now());\n return this.#snapshot(output);\n }\n\n listOutputs(): readonly OutputSnapshot[] {\n this.#assertOpen();\n const now = this.#clock.now();\n return Object.freeze(\n [...this.#outputs.values()]\n .sort(\n (left, right) =>\n left.address.universe - right.address.universe ||\n left.address.priority - right.address.priority,\n )\n .map((output) => {\n this.#updateTransitions(output, now);\n return this.#snapshot(output);\n }),\n );\n }\n\n async clearOutput(addressInput: OutputAddress): Promise<boolean> {\n this.#assertOpen();\n const address = normalizeAddress(addressInput);\n const output = this.#outputs.get(this.#key(address));\n if (!output) return false;\n if (output.sending) await output.sending;\n if (output.transportPending) await output.transportPending;\n const now = this.#clock.now();\n output.current.fill(0);\n output.target.fill(0);\n output.transitions.clear();\n this.#markChanged(output, now);\n const sent = await this.#beginSend(output, now);\n if (!sent) {\n throw new TransportError(\n `Failed to send blackout frame for ${this.#key(address)}.`,\n );\n }\n this.#outputs.delete(this.#key(address));\n try {\n await this.#transport.closeOutput?.(address);\n } catch (error) {\n throw new TransportError(`Failed to close output ${this.#key(address)}.`, error);\n }\n this.#wakeLoop();\n return true;\n }\n\n getTelemetry(): EngineTelemetry {\n const outputs = this.#closed\n ? Object.freeze([] as OutputSnapshot[])\n : this.listOutputs();\n return Object.freeze({\n closed: this.#closed,\n running: this.#running,\n outputCount: outputs.length,\n outputs,\n loopIterations: this.#loopIterations,\n lastLoopStartedAt: this.#lastLoopStartedAt,\n lastLoopCompletedAt: this.#lastLoopCompletedAt,\n lastLoopDurationMs:\n this.#lastLoopStartedAt !== null &&\n this.#lastLoopCompletedAt !== null\n ? Math.max(\n 0,\n this.#lastLoopCompletedAt - this.#lastLoopStartedAt,\n )\n : null,\n transport: Object.freeze({ ...this.#transportTelemetry }),\n });\n }\n\n async close(): Promise<void> {\n if (this.#closed) return;\n this.#closed = true;\n this.#running = false;\n this.#lifecycle.abort(new SacnLifecycleError(\"Output engine closed.\"));\n this.#wakeLoop();\n if (this.#loopPromise) {\n await this.#loopPromise;\n this.#loopPromise = null;\n }\n await Promise.allSettled(\n [...this.#outputs.values()]\n .map((output) => output.sending)\n .filter((send): send is Promise<boolean> => send !== null),\n );\n if (this.#closeTransport) {\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n this.#transport.close(),\n new Promise<never>((_resolve, reject) => {\n timeoutHandle = setTimeout(\n () =>\n reject(\n new TransportError(\n `Transport close timed out after ${this.#shutdownTimeoutMs}ms.`,\n ),\n ),\n this.#shutdownTimeoutMs,\n );\n }),\n ]);\n } finally {\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle);\n }\n }\n this.#outputs.clear();\n this.#logger.info?.(\"Output engine closed.\");\n }\n\n #getOrCreate(options: OutputOptions): OutputState {\n const address = normalizeAddress(options);\n const key = this.#key(address);\n const existing = this.#outputs.get(key);\n if (existing) {\n this.#applyOptions(existing, options);\n return existing;\n }\n if (this.#outputs.size >= this.#maxOutputs) {\n throw new SacnLifecycleError(\n `Output limit of ${this.#maxOutputs} has been reached.`,\n );\n }\n const now = this.#clock.now();\n const state = this.#createState(\n {\n ...address,\n cid: options.cid ?? this.#createId(),\n idleFps: options.idleFps ?? this.#defaultIdleFps,\n sourceName: options.sourceName ?? null,\n target: Array.from({ length: SLOT_COUNT }, () => 0),\n updatedAt: this.#wallNow(),\n },\n new Uint8Array(SLOT_COUNT),\n );\n this.#applyOptions(state, options);\n this.#outputs.set(key, state);\n return state;\n }\n\n #createState(record: OutputRecord, frame: Uint8Array): OutputState {\n if (frame.length !== SLOT_COUNT) {\n throw new Error(`Stored frame must contain exactly ${SLOT_COUNT} values.`);\n }\n return {\n address: normalizeAddress(record),\n cid: assertCid(record.cid),\n idleFps: record.idleFps,\n sourceName:\n record.sourceName === null ? null : assertSourceName(record.sourceName),\n current: frame.slice(),\n target: frame.slice(),\n lastSent: new Uint8Array(SLOT_COUNT),\n transitions: new Map(),\n sequence: 0,\n lastSentAt: null,\n nextDueAt: this.#clock.now(),\n dirty: true,\n lastError: null,\n consecutiveFailures: 0,\n updatedAt: record.updatedAt,\n revision: 0,\n sending: null,\n transportPending: null,\n };\n }\n\n #applyOptions(output: OutputState, options: OutputOptions): void {\n const cid =\n options.cid === undefined ? output.cid : assertCid(options.cid);\n const idleFps = options.idleFps ?? output.idleFps;\n if (options.idleFps !== undefined) {\n assertFps(options.idleFps, \"Idle FPS\");\n }\n const sourceName =\n options.sourceName === undefined\n ? output.sourceName\n : assertSourceName(options.sourceName);\n output.cid = cid;\n output.idleFps = idleFps;\n output.sourceName = sourceName;\n }\n\n #markChanged(output: OutputState, now: number): void {\n output.dirty = true;\n output.lastError = null;\n output.nextDueAt = now;\n output.updatedAt = this.#wallNow();\n output.revision += 1;\n this.#wakeLoop();\n }\n\n #updateTransitions(output: OutputState, now: number): boolean {\n let active = false;\n for (const [index, transition] of output.transitions) {\n const elapsed = now - transition.startedAt;\n if (elapsed >= transition.durationMs) {\n output.current[index] = transition.to;\n output.transitions.delete(index);\n continue;\n }\n const progress = Math.max(0, elapsed / transition.durationMs);\n output.current[index] = Math.round(\n transition.from + (transition.to - transition.from) * progress,\n );\n active = true;\n }\n return active;\n }\n\n #mode(output: OutputState): FrameMode {\n return output.dirty ||\n output.transitions.size > 0 ||\n !framesEqual(output.current, output.lastSent)\n ? \"active\"\n : \"idle\";\n }\n\n #interval(output: OutputState): number {\n return this.#mode(output) === \"active\"\n ? this.#activeIntervalMs\n : 1000 / output.idleFps;\n }\n\n #snapshot(output: OutputState): OutputSnapshot {\n return Object.freeze({\n universe: output.address.universe,\n priority: output.address.priority,\n cid: output.cid,\n sourceName: output.sourceName,\n idleFps: output.idleFps,\n current: frozenFrame(output.current),\n target: frozenFrame(output.target),\n lastSent: frozenFrame(output.lastSent),\n activeTransitions: output.transitions.size,\n frameMode: this.#mode(output),\n dirty: output.dirty,\n sequence: output.sequence,\n lastSentAt: output.lastSentAt,\n nextDueAt: output.nextDueAt,\n lastError: output.lastError,\n updatedAt: output.updatedAt,\n });\n }\n\n async #loop(): Promise<void> {\n while (this.#running && !this.#lifecycle.signal.aborted) {\n const now = this.#clock.now();\n this.#lastLoopStartedAt = now;\n this.#loopIterations += 1;\n const sends: Promise<boolean>[] = [];\n for (const output of this.#outputs.values()) {\n if (this.#pendingMutations.has(this.#key(output.address))) continue;\n this.#updateTransitions(output, now);\n if (\n !output.sending &&\n !output.transportPending &&\n (output.dirty || now >= output.nextDueAt)\n ) {\n sends.push(this.#beginSend(output, now));\n }\n }\n await Promise.allSettled(sends);\n this.#lastLoopCompletedAt = this.#clock.now();\n if (!this.#running) break;\n let nextDueAt = Number.POSITIVE_INFINITY;\n for (const output of this.#outputs.values()) {\n if (this.#pendingMutations.has(this.#key(output.address))) continue;\n nextDueAt = Math.min(nextDueAt, output.nextDueAt);\n }\n const delay = Number.isFinite(nextDueAt)\n ? Math.max(1, nextDueAt - this.#clock.now())\n : 1000;\n await this.#wait(delay);\n }\n }\n\n #beginSend(output: OutputState, now: number): Promise<boolean> {\n const send = this.#send(output, now);\n output.sending = send;\n void send.finally(() => {\n if (output.sending === send) output.sending = null;\n });\n return send;\n }\n\n async #send(output: OutputState, now: number): Promise<boolean> {\n const revision = output.revision;\n const wasTransitioning = output.transitions.size > 0;\n const sentFrame = output.current.slice();\n const packet: OutputPacket = Object.freeze({\n cid: output.cid,\n universe: output.address.universe,\n priority: output.address.priority,\n sequence: output.sequence,\n sourceName: output.sourceName,\n data: sentFrame.slice(),\n });\n if (output.consecutiveFailures > 0) {\n this.#transportTelemetry.sendRetries += 1;\n }\n this.#transportTelemetry.sendAttempts += 1;\n try {\n await this.#sendWithTimeout(packet, output);\n output.lastSent.set(sentFrame);\n output.lastSentAt = now;\n output.sequence = (output.sequence + 1) & 0xff;\n output.dirty = output.revision !== revision;\n output.lastError = null;\n output.consecutiveFailures = 0;\n output.nextDueAt =\n now +\n (wasTransitioning ? this.#activeIntervalMs : this.#interval(output));\n this.#transportTelemetry.sendSuccesses += 1;\n return true;\n } catch (error) {\n if (this.#closed) return false;\n output.lastError = errorMessage(error);\n output.dirty = true;\n output.consecutiveFailures += 1;\n const retryDelayMs = Math.min(\n 5000,\n this.#activeIntervalMs * 2 ** Math.min(output.consecutiveFailures - 1, 8),\n );\n output.nextDueAt = now + retryDelayMs;\n this.#transportTelemetry.sendFailures += 1;\n if (error instanceof TransportTimeoutError) {\n this.#transportTelemetry.sendTimeouts += 1;\n }\n this.#logger.error?.(\"Output transport send failed.\", {\n universe: output.address.universe,\n priority: output.address.priority,\n error: output.lastError,\n });\n return false;\n }\n }\n\n async #sendWithTimeout(\n packet: OutputPacket,\n output: OutputState,\n ): Promise<void> {\n const sendController = new AbortController();\n const timeoutController = new AbortController();\n let rejectOnClose: ((reason: unknown) => void) | undefined;\n const closed = new Promise<never>((_resolve, reject) => {\n rejectOnClose = reject;\n });\n const onClose = (): void => {\n const reason =\n this.#lifecycle.signal.reason ??\n new SacnLifecycleError(\"Output engine closed.\");\n sendController.abort(reason);\n rejectOnClose?.(reason);\n };\n this.#lifecycle.signal.addEventListener(\"abort\", onClose, { once: true });\n const timeoutError = new TransportTimeoutError(\n this.#sendTimeoutMs,\n packet.universe,\n packet.priority,\n );\n const timeout = this.#clock\n .sleep(this.#sendTimeoutMs, timeoutController.signal)\n .then(() => {\n sendController.abort(timeoutError);\n throw timeoutError;\n });\n const transportSend = Promise.resolve()\n .then(() => this.#transport.send(packet, sendController.signal))\n .catch((error: unknown) => {\n if (error instanceof TransportTimeoutError) throw error;\n if (this.#lifecycle.signal.aborted) throw error;\n throw new TransportError(\n `Transport send failed for output ${packet.universe}:${packet.priority}.`,\n error,\n );\n });\n const settlement = transportSend.then(\n () => undefined,\n () => undefined,\n );\n output.transportPending = settlement;\n void settlement.finally(() => {\n if (output.transportPending === settlement) {\n output.transportPending = null;\n this.#wakeLoop();\n }\n });\n try {\n await Promise.race([\n transportSend,\n timeout,\n closed,\n ]);\n } finally {\n timeoutController.abort();\n this.#lifecycle.signal.removeEventListener(\"abort\", onClose);\n }\n }\n\n async #wait(delayMs: number): Promise<void> {\n const controller = new AbortController();\n const onClose = (): void => controller.abort(this.#lifecycle.signal.reason);\n this.#lifecycle.signal.addEventListener(\"abort\", onClose, { once: true });\n const wake = new Promise<void>((resolve) => {\n this.#wake = resolve;\n });\n try {\n await Promise.race([\n this.#clock.sleep(delayMs, controller.signal),\n wake,\n ]);\n } catch {\n // Closing the engine aborts the scheduler wait.\n } finally {\n this.#wake = null;\n controller.abort();\n this.#lifecycle.signal.removeEventListener(\"abort\", onClose);\n }\n }\n\n #wakeLoop(): void {\n this.#wake?.();\n }\n\n #key(address: Required<OutputAddress>): string {\n return `${address.universe}:${address.priority}`;\n }\n\n #cloneState(output: OutputState): OutputState {\n return {\n ...output,\n address: Object.freeze({ ...output.address }),\n current: output.current.slice(),\n target: output.target.slice(),\n lastSent: output.lastSent.slice(),\n transitions: new Map(\n [...output.transitions].map(([index, transition]) => [\n index,\n { ...transition },\n ]),\n ),\n sending: null,\n transportPending: null,\n };\n }\n\n #wallNow(): number {\n return this.#clock.wallNow?.() ?? Date.now();\n }\n\n #assertOpen(): void {\n if (this.#closed) {\n throw new SacnLifecycleError(\"Output engine is closed.\");\n }\n }\n}\n","import {\n\ttype ChannelValues,\n\ttype ClearUniverseOptions,\n\ttype EngineTelemetry,\n\ttype FadeChannelsOptions,\n\ttype Logger,\n\ttype OutputOptions,\n\ttype OutputRecord,\n\ttype OutputSnapshot,\n\ttype OutputStore,\n\ttype SacnLifecycleHook,\n\ttype SacnSourceContract,\n\ttype SacnSourceEvent,\n\ttype SacnSourceOptions,\n\ttype TransitionWrite,\n\ttype UniverseContract,\n\ttype UniverseOptions,\n\ttype WriteFrameOptions,\n\tDEFAULT_PRIORITY,\n} from \"./contracts.js\";\nimport { MemoryOutputStore } from \"./memory-store.js\";\nimport { OutputEngine, SystemClock } from \"./output-engine.js\";\nimport {\n\tSacnLifecycleError,\n\tPersistenceError,\n\tassertFade,\n\tchannelValuesToWrites,\n\tnormalizeAddress,\n\ttoValidatedFrame,\n\tvalidateTransitionWrites,\n} from \"./validation.js\";\n\nconst noopLogger: Logger = Object.freeze({});\n\nconst defaultCreateId = (): string => {\n\tif (typeof globalThis.crypto?.randomUUID !== \"function\") {\n\t\tthrow new SacnLifecycleError(\n\t\t\t\"No UUID generator is available; inject createId in source options.\",\n\t\t);\n\t}\n\treturn globalThis.crypto.randomUUID();\n};\n\nconst toRecord = (snapshot: OutputSnapshot): OutputRecord =>\n\tObject.freeze({\n\t\tuniverse: snapshot.universe,\n\t\tpriority: snapshot.priority,\n\t\tcid: snapshot.cid,\n\t\tsourceName: snapshot.sourceName,\n\t\tidleFps: snapshot.idleFps,\n\t\ttarget: Object.freeze([...snapshot.target]),\n\t\tupdatedAt: snapshot.updatedAt,\n\t});\n\nconst outputOptionsFrom = (\n\tuniverse: number,\n\tpriority: number,\n\tdefaults: UniverseOptions,\n): OutputOptions => ({\n\tuniverse,\n\tpriority,\n\t...(defaults.cid === undefined ? {} : { cid: defaults.cid }),\n\t...(defaults.idleFps === undefined ? {} : { idleFps: defaults.idleFps }),\n\t...(defaults.sourceName === undefined\n\t\t? {}\n\t\t: { sourceName: defaults.sourceName }),\n});\n\n/**\n * Thin handle for a single `(universe, priority)` output address.\n */\nexport class Universe implements UniverseContract {\n\treadonly #source: SacnSource;\n\treadonly #defaults: UniverseOptions;\n\treadonly universe: number;\n\treadonly priority: number;\n\n\tconstructor(\n\t\tsource: SacnSource,\n\t\tuniverse: number,\n\t\tpriority: number,\n\t\tdefaults: UniverseOptions,\n\t) {\n\t\tthis.#source = source;\n\t\tthis.universe = universe;\n\t\tthis.priority = priority;\n\t\tthis.#defaults = defaults;\n\t}\n\n\tsetChannels(\n\t\tvalues: ChannelValues,\n\t\toptions: { readonly signal?: AbortSignal } = {},\n\t): Promise<OutputSnapshot> {\n\t\treturn this.#source.writeChannels(\n\t\t\toutputOptionsFrom(this.universe, this.priority, this.#defaults),\n\t\t\t() => channelValuesToWrites(values),\n\t\t\toptions.signal,\n\t\t);\n\t}\n\n\tfadeChannels(\n\t\tvalues: ChannelValues,\n\t\toptions: FadeChannelsOptions,\n\t): Promise<OutputSnapshot> {\n\t\treturn this.#source.writeChannels(\n\t\t\toutputOptionsFrom(this.universe, this.priority, this.#defaults),\n\t\t\t() => {\n\t\t\t\tassertFade(options.durationMs);\n\t\t\t\treturn channelValuesToWrites(values, options.durationMs);\n\t\t\t},\n\t\t\toptions.signal,\n\t\t);\n\t}\n\n\ttransition(\n\t\twrites: readonly TransitionWrite[],\n\t\toptions: { readonly signal?: AbortSignal } = {},\n\t): Promise<OutputSnapshot> {\n\t\treturn this.#source.writeChannels(\n\t\t\toutputOptionsFrom(this.universe, this.priority, this.#defaults),\n\t\t\t() => {\n\t\t\t\tvalidateTransitionWrites(writes);\n\t\t\t\treturn writes.map((write) => ({\n\t\t\t\t\tchannel: write.channel,\n\t\t\t\t\tvalue: write.value,\n\t\t\t\t\tdurationMs: write.durationMs,\n\t\t\t\t}));\n\t\t\t},\n\t\t\toptions.signal,\n\t\t);\n\t}\n\n\twrite(\n\t\tvalues: readonly number[] | Uint8Array,\n\t\toptions: WriteFrameOptions = {},\n\t): Promise<OutputSnapshot> {\n\t\treturn this.#source.writeFrame(\n\t\t\toutputOptionsFrom(this.universe, this.priority, this.#defaults),\n\t\t\tvalues,\n\t\t\toptions.durationMs ?? 0,\n\t\t\toptions.signal,\n\t\t);\n\t}\n\n\tget(): Promise<OutputSnapshot | null> {\n\t\treturn this.#source.getUniverse(this.universe, this.priority);\n\t}\n\n\tclear(options: ClearUniverseOptions = {}): Promise<boolean> {\n\t\treturn this.#source.clearUniverse(\n\t\t\tthis.universe,\n\t\t\tthis.priority,\n\t\t\toptions.signal,\n\t\t);\n\t}\n}\n\n/**\n * Runtime-neutral sACN output source over scheduling and storage.\n * Mutating requests are serialized so their returned and persisted snapshots\n * represent the same operation order.\n */\nexport class SacnSource implements SacnSourceContract {\n\treadonly #store: OutputStore;\n\treadonly #engine: OutputEngine;\n\treadonly #ownsStore: boolean;\n\treadonly #logger: Logger;\n\treadonly #onStart?: SacnLifecycleHook;\n\treadonly #onStop?: SacnLifecycleHook;\n\treadonly #onClose?: SacnLifecycleHook;\n\treadonly #listeners = new Set<(event: SacnSourceEvent) => void>();\n\treadonly #universes = new Map<string, Universe>();\n\t#queue: Promise<void> = Promise.resolve();\n\t#running = false;\n\t#restored = false;\n\t#closing = false;\n\t#closed = false;\n\t#closePromise: Promise<void> | null = null;\n\n\tconstructor(options: SacnSourceOptions) {\n\t\tthis.#store = options.store ?? new MemoryOutputStore();\n\t\tthis.#ownsStore = options.ownsStore ?? options.store === undefined;\n\t\tthis.#logger = options.logger ?? noopLogger;\n\t\tif (options.onStart !== undefined) this.#onStart = options.onStart;\n\t\tif (options.onStop !== undefined) this.#onStop = options.onStop;\n\t\tif (options.onClose !== undefined) this.#onClose = options.onClose;\n\t\tthis.#engine = new OutputEngine({\n\t\t\ttransport: options.transport,\n\t\t\tclock: options.clock ?? new SystemClock(),\n\t\t\tlogger: this.#logger,\n\t\t\tcreateId: options.createId ?? defaultCreateId,\n\t\t\t...(options.activeFps === undefined\n\t\t\t\t? {}\n\t\t\t\t: { activeFps: options.activeFps }),\n\t\t\t...(options.idleFps === undefined ? {} : { idleFps: options.idleFps }),\n\t\t\t...(options.sendTimeoutMs === undefined\n\t\t\t\t? {}\n\t\t\t\t: { sendTimeoutMs: options.sendTimeoutMs }),\n\t\t\t...(options.shutdownTimeoutMs === undefined\n\t\t\t\t? {}\n\t\t\t\t: { shutdownTimeoutMs: options.shutdownTimeoutMs }),\n\t\t\t...(options.maxOutputs === undefined\n\t\t\t\t? {}\n\t\t\t\t: { maxOutputs: options.maxOutputs }),\n\t\t\tcloseTransport: options.ownsTransport ?? false,\n\t\t});\n\t}\n\n\tuniverse(universe: number, options: UniverseOptions = {}): Universe {\n\t\tif (this.#closing || this.#closed) {\n\t\t\tthrow new SacnLifecycleError(\"Source is closed.\");\n\t\t}\n\t\tconst address = normalizeAddress({\n\t\t\tuniverse,\n\t\t\tpriority: options.priority ?? DEFAULT_PRIORITY,\n\t\t});\n\t\tconst key = `${address.universe}:${address.priority}`;\n\t\tconst existing = this.#universes.get(key);\n\t\tif (existing) return existing;\n\t\tconst handle = new Universe(this, address.universe, address.priority, {\n\t\t\t...(options.cid === undefined ? {} : { cid: options.cid }),\n\t\t\t...(options.idleFps === undefined ? {} : { idleFps: options.idleFps }),\n\t\t\t...(options.sourceName === undefined\n\t\t\t\t? {}\n\t\t\t\t: { sourceName: options.sourceName }),\n\t\t});\n\t\tthis.#universes.set(key, handle);\n\t\treturn handle;\n\t}\n\n\tstart(): Promise<void> {\n\t\treturn this.#enqueue(async () => {\n\t\t\tawait this.#startLocked();\n\t\t});\n\t}\n\n\tstop(): Promise<void> {\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#closed) {\n\t\t\t\tthrow new SacnLifecycleError(\"Source is closed.\");\n\t\t\t}\n\t\t\tif (!this.#running) return;\n\t\t\tawait this.#engine.stop();\n\t\t\tthis.#running = false;\n\t\t\tawait this.#runHook(this.#onStop);\n\t\t\tthis.#emit({ type: \"stopped\" });\n\t\t});\n\t}\n\n\t/** @internal Used by Universe handles. */\n\twriteFrame(\n\t\toptions: OutputOptions,\n\t\tvalues: readonly number[] | Uint8Array,\n\t\tdurationMs: number,\n\t\tsignal?: AbortSignal,\n\t): Promise<OutputSnapshot> {\n\t\treturn this.#enqueue(async () => {\n\t\t\tawait this.#ensureStartedLocked();\n\t\t\tconst frame = toValidatedFrame(values);\n\t\t\tassertFade(durationMs);\n\t\t\tconst checkpoint = await this.#engine.beginMutation(options);\n\t\t\ttry {\n\t\t\t\tconst snapshot = this.#engine.writeFrame(options, frame, durationMs);\n\t\t\t\tawait this.#persist(\"save output\", () =>\n\t\t\t\t\tthis.#store.save(toRecord(snapshot)),\n\t\t\t\t);\n\t\t\t\tthis.#engine.commitMutation(checkpoint);\n\t\t\t\tthis.#emit({ type: \"output-updated\", output: snapshot });\n\t\t\t\treturn snapshot;\n\t\t\t} catch (error) {\n\t\t\t\tthis.#engine.rollbackMutation(checkpoint);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}, signal);\n\t}\n\n\t/** @internal Used by Universe handles. */\n\twriteChannels(\n\t\toptions: OutputOptions,\n\t\tresolveChannels: () => ReturnType<typeof channelValuesToWrites>,\n\t\tsignal?: AbortSignal,\n\t): Promise<OutputSnapshot> {\n\t\treturn this.#enqueue(async () => {\n\t\t\tawait this.#ensureStartedLocked();\n\t\t\tconst channels = resolveChannels();\n\t\t\tconst checkpoint = await this.#engine.beginMutation(options);\n\t\t\ttry {\n\t\t\t\tconst snapshot = this.#engine.writeChannels(options, channels);\n\t\t\t\tawait this.#persist(\"save output\", () =>\n\t\t\t\t\tthis.#store.save(toRecord(snapshot)),\n\t\t\t\t);\n\t\t\t\tthis.#engine.commitMutation(checkpoint);\n\t\t\t\tthis.#emit({ type: \"output-updated\", output: snapshot });\n\t\t\t\treturn snapshot;\n\t\t\t} catch (error) {\n\t\t\t\tthis.#engine.rollbackMutation(checkpoint);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}, signal);\n\t}\n\n\t/** @internal Used by Universe handles. */\n\tgetUniverse(\n\t\tuniverse: number,\n\t\tpriority: number,\n\t): Promise<OutputSnapshot | null> {\n\t\treturn this.#enqueue(() =>\n\t\t\tPromise.resolve(\n\t\t\t\tthis.#engine.getOutput(normalizeAddress({ universe, priority })),\n\t\t\t),\n\t\t);\n\t}\n\n\tlistUniverses(): Promise<readonly OutputSnapshot[]> {\n\t\treturn this.#enqueue(() => Promise.resolve(this.#engine.listOutputs()));\n\t}\n\n\t/** @internal Used by Universe handles. */\n\tclearUniverse(\n\t\tuniverse: number,\n\t\tpriority: number,\n\t\tsignal?: AbortSignal,\n\t): Promise<boolean> {\n\t\treturn this.#enqueue(async () => {\n\t\t\tawait this.#ensureStartedLocked();\n\t\t\tconst address = normalizeAddress({ universe, priority });\n\t\t\tconst checkpoint = await this.#engine.beginMutation(address);\n\t\t\tconst previous = this.#engine.getOutput(address);\n\t\t\tif (!previous) {\n\t\t\t\tthis.#engine.commitMutation(checkpoint);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tlet persistenceRemoved = false;\n\t\t\ttry {\n\t\t\t\tawait this.#persist(\"remove output\", () => this.#store.remove(address));\n\t\t\t\tpersistenceRemoved = true;\n\t\t\t\tconst removed = await this.#engine.clearOutput(address);\n\t\t\t\tthis.#engine.commitMutation(checkpoint);\n\t\t\t\tthis.#emit({\n\t\t\t\t\ttype: \"output-cleared\",\n\t\t\t\t\taddress: Object.freeze({ ...address }),\n\t\t\t\t});\n\t\t\t\treturn removed;\n\t\t\t} catch (error) {\n\t\t\t\tthis.#engine.rollbackMutation(checkpoint);\n\t\t\t\tif (persistenceRemoved) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.#persist(\"restore output after failed clear\", () =>\n\t\t\t\t\t\t\tthis.#store.save(toRecord(previous)),\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch (rollbackError) {\n\t\t\t\t\t\tthrow new AggregateError(\n\t\t\t\t\t\t\t[error, rollbackError],\n\t\t\t\t\t\t\t\"Output clear and persistence rollback failed.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}, signal);\n\t}\n\n\tgetTelemetry(): EngineTelemetry {\n\t\treturn this.#engine.getTelemetry();\n\t}\n\n\tsubscribe(listener: (event: SacnSourceEvent) => void): () => void {\n\t\tif (this.#closing || this.#closed) {\n\t\t\tthrow new SacnLifecycleError(\"Source is closed.\");\n\t\t}\n\t\tthis.#listeners.add(listener);\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\tclose(): Promise<void> {\n\t\tif (this.#closePromise) return this.#closePromise;\n\t\tthis.#closing = true;\n\t\tconst closing = this.#queue.then(async () => {\n\t\t\tconst errors: unknown[] = [];\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.#engine.close();\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t\tif (this.#ownsStore) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.#persist(\"close output store\", async () => {\n\t\t\t\t\t\t\tawait this.#store.close?.();\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait this.#runHook(this.#onClose);\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t\tif (errors.length === 1) {\n\t\t\t\t\tthrow errors[0];\n\t\t\t\t}\n\t\t\t\tif (errors.length > 1) {\n\t\t\t\t\tthrow new AggregateError(errors, \"Source shutdown failed.\");\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tthis.#running = false;\n\t\t\t\tthis.#closed = true;\n\t\t\t\tthis.#closing = false;\n\t\t\t\tthis.#universes.clear();\n\t\t\t\tthis.#emit({ type: \"closed\" });\n\t\t\t\tthis.#listeners.clear();\n\t\t\t}\n\t\t});\n\t\tthis.#queue = closing.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\tthis.#closePromise = closing;\n\t\treturn closing;\n\t}\n\n\tasync #startLocked(): Promise<void> {\n\t\tif (this.#closed) {\n\t\t\tthrow new SacnLifecycleError(\"Source is closed.\");\n\t\t}\n\t\tif (this.#running) return;\n\t\tif (!this.#restored) {\n\t\t\tconst records = await this.#persist(\"load outputs\", () =>\n\t\t\t\tthis.#store.list(),\n\t\t\t);\n\t\t\tconst checkpoints = [];\n\t\t\ttry {\n\t\t\t\tfor (const record of records) {\n\t\t\t\t\tconst checkpoint = await this.#engine.beginMutation(record);\n\t\t\t\t\tcheckpoints.push(checkpoint);\n\t\t\t\t\tthis.#engine.restore(record);\n\t\t\t\t\tthis.#engine.commitMutation(checkpoint);\n\t\t\t\t}\n\t\t\t\tthis.#restored = true;\n\t\t\t} catch (error) {\n\t\t\t\tfor (const checkpoint of checkpoints.reverse()) {\n\t\t\t\t\tthis.#engine.rollbackMutation(checkpoint);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\tthis.#engine.start();\n\t\tthis.#running = true;\n\t\tawait this.#runHook(this.#onStart);\n\t\tthis.#emit({ type: \"started\" });\n\t}\n\n\tasync #ensureStartedLocked(): Promise<void> {\n\t\tif (!this.#running) {\n\t\t\tawait this.#startLocked();\n\t\t}\n\t}\n\n\tasync #runHook(hook: SacnLifecycleHook | undefined): Promise<void> {\n\t\tif (!hook) return;\n\t\tawait hook();\n\t}\n\n\t#enqueue<T>(operation: () => Promise<T>, signal?: AbortSignal): Promise<T> {\n\t\tif (this.#closing || this.#closed) {\n\t\t\treturn Promise.reject(new SacnLifecycleError(\"Source is closed.\"));\n\t\t}\n\t\tconst result = this.#queue.then(() => {\n\t\t\tsignal?.throwIfAborted();\n\t\t\treturn operation();\n\t\t});\n\t\tthis.#queue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tasync #persist<T>(action: string, operation: () => Promise<T>): Promise<T> {\n\t\ttry {\n\t\t\treturn await operation();\n\t\t} catch (error) {\n\t\t\tif (error instanceof PersistenceError) throw error;\n\t\t\tthrow new PersistenceError(`Failed to ${action}.`, error);\n\t\t}\n\t}\n\n\t#emit(event: SacnSourceEvent): void {\n\t\tconst frozenEvent = Object.freeze(event);\n\t\tfor (const listener of this.#listeners) {\n\t\t\ttry {\n\t\t\t\tlistener(frozenEvent);\n\t\t\t} catch (error) {\n\t\t\t\tthis.#logger.warn?.(\"Source event listener failed.\", { error });\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const createSacnSource = (options: SacnSourceOptions): SacnSource =>\n\tnew SacnSource(options);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { A as SacnSourceOptions, N as UniverseContract, O as SacnSourceContract, P as UniverseOptions, U as WriteFrameOptions, _ as OutputOptions, b as OutputSnapshot, c as EngineTelemetry, j as TransitionWrite, k as SacnSourceEvent, l as FadeChannelsOptions, n as ChannelWrite, r as ClearUniverseOptions, t as ChannelValues } from "./contracts-C4kpAdZq.js";
|
|
2
|
+
//#region src/validation.d.ts
|
|
3
|
+
type ValidationCode = "INVALID_CHANNEL" | "INVALID_CID" | "INVALID_FADE" | "INVALID_FPS" | "INVALID_FRAME" | "INVALID_PRIORITY" | "INVALID_PORT" | "INVALID_NAMESPACE" | "INVALID_SOURCE_NAME" | "INVALID_TIMEOUT" | "INVALID_UNIVERSE";
|
|
4
|
+
declare class SacnError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly cause?: unknown;
|
|
7
|
+
constructor(message: string, code: string, cause?: unknown);
|
|
8
|
+
}
|
|
9
|
+
declare class SacnValidationError extends SacnError {
|
|
10
|
+
constructor(message: string, code: ValidationCode);
|
|
11
|
+
}
|
|
12
|
+
declare class SacnLifecycleError extends SacnError {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
declare class TransportTimeoutError extends SacnError {
|
|
16
|
+
constructor(timeoutMs: number, universe: number, priority: number);
|
|
17
|
+
}
|
|
18
|
+
declare class TransportError extends SacnError {
|
|
19
|
+
constructor(message: string, cause?: unknown);
|
|
20
|
+
}
|
|
21
|
+
declare class PersistenceError extends SacnError {
|
|
22
|
+
constructor(message: string, cause?: unknown);
|
|
23
|
+
}
|
|
24
|
+
declare class DependencyUnavailableError extends SacnError {
|
|
25
|
+
constructor(message: string, cause?: unknown);
|
|
26
|
+
}
|
|
27
|
+
declare const channelValuesToWrites: (values: ChannelValues, durationMs?: number) => ChannelWrite[];
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/source.d.ts
|
|
30
|
+
/**
|
|
31
|
+
* Thin handle for a single `(universe, priority)` output address.
|
|
32
|
+
*/
|
|
33
|
+
declare class Universe implements UniverseContract {
|
|
34
|
+
#private;
|
|
35
|
+
readonly universe: number;
|
|
36
|
+
readonly priority: number;
|
|
37
|
+
constructor(source: SacnSource, universe: number, priority: number, defaults: UniverseOptions);
|
|
38
|
+
setChannels(values: ChannelValues, options?: {
|
|
39
|
+
readonly signal?: AbortSignal;
|
|
40
|
+
}): Promise<OutputSnapshot>;
|
|
41
|
+
fadeChannels(values: ChannelValues, options: FadeChannelsOptions): Promise<OutputSnapshot>;
|
|
42
|
+
transition(writes: readonly TransitionWrite[], options?: {
|
|
43
|
+
readonly signal?: AbortSignal;
|
|
44
|
+
}): Promise<OutputSnapshot>;
|
|
45
|
+
write(values: readonly number[] | Uint8Array, options?: WriteFrameOptions): Promise<OutputSnapshot>;
|
|
46
|
+
get(): Promise<OutputSnapshot | null>;
|
|
47
|
+
clear(options?: ClearUniverseOptions): Promise<boolean>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Runtime-neutral sACN output source over scheduling and storage.
|
|
51
|
+
* Mutating requests are serialized so their returned and persisted snapshots
|
|
52
|
+
* represent the same operation order.
|
|
53
|
+
*/
|
|
54
|
+
declare class SacnSource implements SacnSourceContract {
|
|
55
|
+
#private;
|
|
56
|
+
constructor(options: SacnSourceOptions);
|
|
57
|
+
universe(universe: number, options?: UniverseOptions): Universe;
|
|
58
|
+
start(): Promise<void>;
|
|
59
|
+
stop(): Promise<void>;
|
|
60
|
+
/** @internal Used by Universe handles. */
|
|
61
|
+
writeFrame(options: OutputOptions, values: readonly number[] | Uint8Array, durationMs: number, signal?: AbortSignal): Promise<OutputSnapshot>;
|
|
62
|
+
/** @internal Used by Universe handles. */
|
|
63
|
+
writeChannels(options: OutputOptions, resolveChannels: () => ReturnType<typeof channelValuesToWrites>, signal?: AbortSignal): Promise<OutputSnapshot>;
|
|
64
|
+
/** @internal Used by Universe handles. */
|
|
65
|
+
getUniverse(universe: number, priority: number): Promise<OutputSnapshot | null>;
|
|
66
|
+
listUniverses(): Promise<readonly OutputSnapshot[]>;
|
|
67
|
+
/** @internal Used by Universe handles. */
|
|
68
|
+
clearUniverse(universe: number, priority: number, signal?: AbortSignal): Promise<boolean>;
|
|
69
|
+
getTelemetry(): EngineTelemetry;
|
|
70
|
+
subscribe(listener: (event: SacnSourceEvent) => void): () => void;
|
|
71
|
+
close(): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
declare const createSacnSource: (options: SacnSourceOptions) => SacnSource;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { PersistenceError as a, SacnValidationError as c, ValidationCode as d, DependencyUnavailableError as i, TransportError as l, Universe as n, SacnError as o, createSacnSource as r, SacnLifecycleError as s, SacnSource as t, TransportTimeoutError as u };
|
|
76
|
+
//# sourceMappingURL=source-D_XNWXS9.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-D_XNWXS9.d.ts","names":[],"sources":["../src/validation.ts","../src/source.ts"],"mappings":";;KAaY;cAaC,kBAAkB;WAGlB;WACA;EAHX,YACE,iBACS,cACA;;cAOA,4BAA4B;EACvC,YAAY,iBAAiB,MAAM;;cAKxB,2BAA2B;EACtC,YAAY;;cAKD,8BAA8B;EACzC,YAAY,mBAAmB,kBAAkB;;cAQtC,uBAAuB;EAClC,YAAY,iBAAiB;;cAKlB,yBAAyB;EACpC,YAAY,iBAAiB;;cAKlB,mCAAmC;EAC9C,YAAY,iBAAiB;;cA4LlB,wBAAqB,QACxB,eAAa,wBAEpB;;;;;;cC/LU,oBAAoB;;WAGvB;WACA;EAET,YACC,QAAQ,YACR,kBACA,kBACA,UAAU;EAQX,YACC,QAAQ,eACR;aAAoB,SAAS;MAC3B,QAAQ;EAQX,aACC,QAAQ,eACR,SAAS,sBACP,QAAQ;EAWX,WACC,iBAAiB,mBACjB;aAAoB,SAAS;MAC3B,QAAQ;EAeX,MACC,4BAA4B,YAC5B,UAAS,oBACP,QAAQ;EASX,OAAO,QAAQ;EAIf,MAAM,UAAS,uBAA4B;;;;;;;cAc/B,sBAAsB;;EAiBlC,YAAY,SAAS;EA6BrB,SAAS,kBAAkB,UAAS,kBAAuB;EAsB3D,SAAS;EAMT,QAAQ;;EAcR,WACC,SAAS,eACT,4BAA4B,YAC5B,oBACA,SAAS,cACP,QAAQ;;EAsBX,cACC,SAAS,eACT,uBAAuB,kBAAkB,wBACzC,SAAS,cACP,QAAQ;;EAqBX,YACC,kBACA,mBACE,QAAQ;EAQX,iBAAiB,iBAAiB;;EAKlC,cACC,kBACA,kBACA,SAAS,cACP;EAwCH,gBAAgB;EAIhB,UAAU,WAAW,OAAO;EAQ5B,SAAS;;cA8HG,mBAAgB,SAAa,sBAAoB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { C as Receiver, S as OutputTransport, T as ReceiverPacketListener, g as OutputAddress, i as Clock, v as OutputPacket, w as ReceiverPacket } from "./contracts-C4kpAdZq.js";
|
|
2
|
+
//#region src/testing.d.ts
|
|
3
|
+
declare class FakeClock implements Clock {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(now?: number);
|
|
6
|
+
now(): number;
|
|
7
|
+
sleep(delayMs: number, signal: AbortSignal): Promise<void>;
|
|
8
|
+
advanceBy(milliseconds: number): void;
|
|
9
|
+
advanceTo(timestamp: number): void;
|
|
10
|
+
}
|
|
11
|
+
declare class RecordingTransport implements OutputTransport {
|
|
12
|
+
readonly packets: OutputPacket[];
|
|
13
|
+
readonly closedOutputs: Required<OutputAddress>[];
|
|
14
|
+
closeCalls: number;
|
|
15
|
+
sendImplementation?: (packet: OutputPacket, signal: AbortSignal) => Promise<void>;
|
|
16
|
+
send(packet: OutputPacket, signal: AbortSignal): Promise<void>;
|
|
17
|
+
closeOutput(address: Required<OutputAddress>): Promise<void>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
declare class FakeReceiver implements Receiver {
|
|
21
|
+
#private;
|
|
22
|
+
readonly universes: Set<number>;
|
|
23
|
+
closed: boolean;
|
|
24
|
+
addUniverse(universe: number): void;
|
|
25
|
+
removeUniverse(universe: number): void;
|
|
26
|
+
subscribe(listener: ReceiverPacketListener): () => void;
|
|
27
|
+
emit(packet: ReceiverPacket): void;
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
interface Deferred<T> {
|
|
31
|
+
readonly promise: Promise<T>;
|
|
32
|
+
readonly resolve: (value: T | PromiseLike<T>) => void;
|
|
33
|
+
readonly reject: (reason?: unknown) => void;
|
|
34
|
+
}
|
|
35
|
+
declare const createDeferred: <T = void>() => Deferred<T>;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { Deferred, FakeClock, FakeReceiver, RecordingTransport, createDeferred };
|
|
38
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing.ts"],"mappings":";;cAgBa,qBAAqB;;EAIhC,YAAY;EAIZ;EAIA,MAAM,iBAAiB,QAAQ,cAAc;EAsB7C,UAAU;EAIV,UAAU;;cAcC,8BAA8B;WAChC,SAAS;WACT,eAAe,SAAS;EACjC;EACA,sBACE,QAAQ,cACR,QAAQ,gBACL;EAEC,KAAK,QAAQ,cAAc,QAAQ,cAAc;EASjD,YAAY,SAAS,SAAS,iBAAiB;EAI/C,SAAS;;cAKJ,wBAAwB;;WAC1B,WAAS;EAElB;EAEA,YAAY;EAIZ,eAAe;EAIf,UAAU,UAAU;EAMpB,KAAK,QAAQ;EAIP,SAAS;;UAMA,SAAS;WACf,SAAS,QAAQ;WACjB,UAAU,OAAO,IAAI,YAAY;WACjC,SAAS;;cAGP,iBAAkB,eAAa,SAAS"}
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//#region src/testing.ts
|
|
2
|
+
var FakeClock = class {
|
|
3
|
+
#now;
|
|
4
|
+
#sleepers = /* @__PURE__ */ new Set();
|
|
5
|
+
constructor(now = 0) {
|
|
6
|
+
this.#now = now;
|
|
7
|
+
}
|
|
8
|
+
now() {
|
|
9
|
+
return this.#now;
|
|
10
|
+
}
|
|
11
|
+
sleep(delayMs, signal) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
if (signal.aborted) {
|
|
14
|
+
reject(signal.reason);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const sleeper = {
|
|
18
|
+
dueAt: this.#now + Math.max(0, delayMs),
|
|
19
|
+
resolve,
|
|
20
|
+
reject
|
|
21
|
+
};
|
|
22
|
+
this.#sleepers.add(sleeper);
|
|
23
|
+
signal.addEventListener("abort", () => {
|
|
24
|
+
if (this.#sleepers.delete(sleeper)) reject(signal.reason);
|
|
25
|
+
}, { once: true });
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
advanceBy(milliseconds) {
|
|
29
|
+
this.advanceTo(this.#now + milliseconds);
|
|
30
|
+
}
|
|
31
|
+
advanceTo(timestamp) {
|
|
32
|
+
if (!Number.isFinite(timestamp) || timestamp < this.#now) throw new RangeError("Fake clock cannot move backwards.");
|
|
33
|
+
this.#now = timestamp;
|
|
34
|
+
for (const sleeper of [...this.#sleepers]) if (sleeper.dueAt <= timestamp) {
|
|
35
|
+
this.#sleepers.delete(sleeper);
|
|
36
|
+
sleeper.resolve();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var RecordingTransport = class {
|
|
41
|
+
packets = [];
|
|
42
|
+
closedOutputs = [];
|
|
43
|
+
closeCalls = 0;
|
|
44
|
+
sendImplementation;
|
|
45
|
+
async send(packet, signal) {
|
|
46
|
+
const copy = Object.freeze({
|
|
47
|
+
...packet,
|
|
48
|
+
data: packet.data.slice()
|
|
49
|
+
});
|
|
50
|
+
this.packets.push(copy);
|
|
51
|
+
await this.sendImplementation?.(copy, signal);
|
|
52
|
+
}
|
|
53
|
+
async closeOutput(address) {
|
|
54
|
+
this.closedOutputs.push(Object.freeze({ ...address }));
|
|
55
|
+
}
|
|
56
|
+
async close() {
|
|
57
|
+
this.closeCalls += 1;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var FakeReceiver = class {
|
|
61
|
+
universes = /* @__PURE__ */ new Set();
|
|
62
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
63
|
+
closed = false;
|
|
64
|
+
addUniverse(universe) {
|
|
65
|
+
this.universes.add(universe);
|
|
66
|
+
}
|
|
67
|
+
removeUniverse(universe) {
|
|
68
|
+
this.universes.delete(universe);
|
|
69
|
+
}
|
|
70
|
+
subscribe(listener) {
|
|
71
|
+
if (this.closed) throw new Error("Fake receiver is closed.");
|
|
72
|
+
this.#listeners.add(listener);
|
|
73
|
+
return () => this.#listeners.delete(listener);
|
|
74
|
+
}
|
|
75
|
+
emit(packet) {
|
|
76
|
+
for (const listener of this.#listeners) listener(packet);
|
|
77
|
+
}
|
|
78
|
+
async close() {
|
|
79
|
+
this.closed = true;
|
|
80
|
+
this.#listeners.clear();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const createDeferred = () => {
|
|
84
|
+
let resolve;
|
|
85
|
+
let reject;
|
|
86
|
+
return {
|
|
87
|
+
promise: new Promise((resolvePromise, rejectPromise) => {
|
|
88
|
+
resolve = resolvePromise;
|
|
89
|
+
reject = rejectPromise;
|
|
90
|
+
}),
|
|
91
|
+
resolve,
|
|
92
|
+
reject
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
export { FakeClock, FakeReceiver, RecordingTransport, createDeferred };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","names":["#sleepers","#now","#listeners"],"sources":["../src/testing.ts"],"sourcesContent":["import type {\n Clock,\n OutputAddress,\n OutputPacket,\n OutputTransport,\n Receiver,\n ReceiverPacket,\n ReceiverPacketListener,\n} from \"./contracts.js\";\n\ninterface Sleeper {\n readonly dueAt: number;\n readonly resolve: () => void;\n readonly reject: (reason?: unknown) => void;\n}\n\nexport class FakeClock implements Clock {\n #now: number;\n readonly #sleepers = new Set<Sleeper>();\n\n constructor(now = 0) {\n this.#now = now;\n }\n\n now(): number {\n return this.#now;\n }\n\n sleep(delayMs: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) {\n reject(signal.reason);\n return;\n }\n const sleeper: Sleeper = {\n dueAt: this.#now + Math.max(0, delayMs),\n resolve,\n reject,\n };\n this.#sleepers.add(sleeper);\n signal.addEventListener(\n \"abort\",\n () => {\n if (this.#sleepers.delete(sleeper)) reject(signal.reason);\n },\n { once: true },\n );\n });\n }\n\n advanceBy(milliseconds: number): void {\n this.advanceTo(this.#now + milliseconds);\n }\n\n advanceTo(timestamp: number): void {\n if (!Number.isFinite(timestamp) || timestamp < this.#now) {\n throw new RangeError(\"Fake clock cannot move backwards.\");\n }\n this.#now = timestamp;\n for (const sleeper of [...this.#sleepers]) {\n if (sleeper.dueAt <= timestamp) {\n this.#sleepers.delete(sleeper);\n sleeper.resolve();\n }\n }\n }\n}\n\nexport class RecordingTransport implements OutputTransport {\n readonly packets: OutputPacket[] = [];\n readonly closedOutputs: Required<OutputAddress>[] = [];\n closeCalls = 0;\n sendImplementation?: (\n packet: OutputPacket,\n signal: AbortSignal,\n ) => Promise<void>;\n\n async send(packet: OutputPacket, signal: AbortSignal): Promise<void> {\n const copy: OutputPacket = Object.freeze({\n ...packet,\n data: packet.data.slice(),\n });\n this.packets.push(copy);\n await this.sendImplementation?.(copy, signal);\n }\n\n async closeOutput(address: Required<OutputAddress>): Promise<void> {\n this.closedOutputs.push(Object.freeze({ ...address }));\n }\n\n async close(): Promise<void> {\n this.closeCalls += 1;\n }\n}\n\nexport class FakeReceiver implements Receiver {\n readonly universes = new Set<number>();\n readonly #listeners = new Set<ReceiverPacketListener>();\n closed = false;\n\n addUniverse(universe: number): void {\n this.universes.add(universe);\n }\n\n removeUniverse(universe: number): void {\n this.universes.delete(universe);\n }\n\n subscribe(listener: ReceiverPacketListener): () => void {\n if (this.closed) throw new Error(\"Fake receiver is closed.\");\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n emit(packet: ReceiverPacket): void {\n for (const listener of this.#listeners) listener(packet);\n }\n\n async close(): Promise<void> {\n this.closed = true;\n this.#listeners.clear();\n }\n}\n\nexport interface Deferred<T> {\n readonly promise: Promise<T>;\n readonly resolve: (value: T | PromiseLike<T>) => void;\n readonly reject: (reason?: unknown) => void;\n}\n\nexport const createDeferred = <T = void>(): Deferred<T> => {\n let resolve!: (value: T | PromiseLike<T>) => void;\n let reject!: (reason?: unknown) => void;\n const promise = new Promise<T>((resolvePromise, rejectPromise) => {\n resolve = resolvePromise;\n reject = rejectPromise;\n });\n return { promise, resolve, reject };\n};\n"],"mappings":";AAgBA,IAAa,YAAb,MAAwC;CACtC;CACA,4BAAqB,IAAI,IAAa;CAEtC,YAAY,MAAM,GAAG;EACnB,KAAKC,OAAO;CACd;CAEA,MAAc;EACZ,OAAO,KAAKA;CACd;CAEA,MAAM,SAAiB,QAAoC;EACzD,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,IAAI,OAAO,SAAS;IAClB,OAAO,OAAO,MAAM;IACpB;GACF;GACA,MAAM,UAAmB;IACvB,OAAO,KAAKA,OAAO,KAAK,IAAI,GAAG,OAAO;IACtC;IACA;GACF;GACA,KAAKD,UAAU,IAAI,OAAO;GAC1B,OAAO,iBACL,eACM;IACJ,IAAI,KAAKA,UAAU,OAAO,OAAO,GAAG,OAAO,OAAO,MAAM;GAC1D,GACA,EAAE,MAAM,KAAK,CACf;EACF,CAAC;CACH;CAEA,UAAU,cAA4B;EACpC,KAAK,UAAU,KAAKC,OAAO,YAAY;CACzC;CAEA,UAAU,WAAyB;EACjC,IAAI,CAAC,OAAO,SAAS,SAAS,KAAK,YAAY,KAAKA,MAClD,MAAM,IAAI,WAAW,mCAAmC;EAE1D,KAAKA,OAAO;EACZ,KAAK,MAAM,WAAW,CAAC,GAAG,KAAKD,SAAS,GACtC,IAAI,QAAQ,SAAS,WAAW;GAC9B,KAAKA,UAAU,OAAO,OAAO;GAC7B,QAAQ,QAAQ;EAClB;CAEJ;AACF;AAEA,IAAa,qBAAb,MAA2D;CACzD,UAAmC,CAAC;CACpC,gBAAoD,CAAC;CACrD,aAAa;CACb;CAKA,MAAM,KAAK,QAAsB,QAAoC;EACnE,MAAM,OAAqB,OAAO,OAAO;GACvC,GAAG;GACH,MAAM,OAAO,KAAK,MAAM;EAC1B,CAAC;EACD,KAAK,QAAQ,KAAK,IAAI;EACtB,MAAM,KAAK,qBAAqB,MAAM,MAAM;CAC9C;CAEA,MAAM,YAAY,SAAiD;EACjE,KAAK,cAAc,KAAK,OAAO,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC;CACvD;CAEA,MAAM,QAAuB;EAC3B,KAAK,cAAc;CACrB;AACF;AAEA,IAAa,eAAb,MAA8C;CAC5C,4BAAqB,IAAI,IAAY;CACrC,6BAAsB,IAAI,IAA4B;CACtD,SAAS;CAET,YAAY,UAAwB;EAClC,KAAK,UAAU,IAAI,QAAQ;CAC7B;CAEA,eAAe,UAAwB;EACrC,KAAK,UAAU,OAAO,QAAQ;CAChC;CAEA,UAAU,UAA8C;EACtD,IAAI,KAAK,QAAQ,MAAM,IAAI,MAAM,0BAA0B;EAC3D,KAAKE,WAAW,IAAI,QAAQ;EAC5B,aAAa,KAAKA,WAAW,OAAO,QAAQ;CAC9C;CAEA,KAAK,QAA8B;EACjC,KAAK,MAAM,YAAY,KAAKA,YAAY,SAAS,MAAM;CACzD;CAEA,MAAM,QAAuB;EAC3B,KAAK,SAAS;EACd,KAAKA,WAAW,MAAM;CACxB;AACF;AAQA,MAAa,uBAA8C;CACzD,IAAI;CACJ,IAAI;CAKJ,OAAO;EAAE,SAAA,IAJW,SAAY,gBAAgB,kBAAkB;GAChE,UAAU;GACV,SAAS;EACX,CACe;EAAG;EAAS;CAAO;AACpC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
//#region src/contracts.ts
|
|
2
|
+
const SLOT_COUNT = 512;
|
|
3
|
+
const MIN_UNIVERSE = 1;
|
|
4
|
+
const MAX_UNIVERSE = 63999;
|
|
5
|
+
const MIN_PRIORITY = 0;
|
|
6
|
+
const MAX_PRIORITY = 200;
|
|
7
|
+
const DEFAULT_PRIORITY = 100;
|
|
8
|
+
const DEFAULT_ACTIVE_FPS = 44;
|
|
9
|
+
const DEFAULT_IDLE_FPS = 2;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/validation.ts
|
|
12
|
+
var SacnError = class extends Error {
|
|
13
|
+
code;
|
|
14
|
+
cause;
|
|
15
|
+
constructor(message, code, cause) {
|
|
16
|
+
super(message, cause === void 0 ? void 0 : { cause });
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.cause = cause;
|
|
19
|
+
this.name = new.target.name;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var SacnValidationError = class extends SacnError {
|
|
23
|
+
constructor(message, code) {
|
|
24
|
+
super(message, code);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var SacnLifecycleError = class extends SacnError {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message, "INVALID_LIFECYCLE");
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var TransportTimeoutError = class extends SacnError {
|
|
33
|
+
constructor(timeoutMs, universe, priority) {
|
|
34
|
+
super(`Transport send timed out after ${timeoutMs}ms for output ${universe}:${priority}.`, "TRANSPORT_TIMEOUT");
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var TransportError = class extends SacnError {
|
|
38
|
+
constructor(message, cause) {
|
|
39
|
+
super(message, "TRANSPORT_ERROR", cause);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var PersistenceError = class extends SacnError {
|
|
43
|
+
constructor(message, cause) {
|
|
44
|
+
super(message, "PERSISTENCE_ERROR", cause);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var DependencyUnavailableError = class extends SacnError {
|
|
48
|
+
constructor(message, cause) {
|
|
49
|
+
super(message, "DEPENDENCY_UNAVAILABLE", cause);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const integerInRange = (value, minimum, maximum, label, code) => {
|
|
53
|
+
if (!Number.isInteger(value) || value < minimum || value > maximum) throw new SacnValidationError(`${label} must be an integer between ${minimum} and ${maximum}.`, code);
|
|
54
|
+
};
|
|
55
|
+
const normalizeAddress = (address) => {
|
|
56
|
+
if (!address || typeof address !== "object") throw new SacnValidationError("Output address must be an object.", "INVALID_UNIVERSE");
|
|
57
|
+
integerInRange(address.universe, 1, MAX_UNIVERSE, "Universe", "INVALID_UNIVERSE");
|
|
58
|
+
const priority = address.priority ?? 100;
|
|
59
|
+
integerInRange(priority, 0, 200, "Priority", "INVALID_PRIORITY");
|
|
60
|
+
return {
|
|
61
|
+
universe: address.universe,
|
|
62
|
+
priority
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
const assertFps = (value, label = "FPS") => {
|
|
66
|
+
if (!Number.isFinite(value) || value <= 0 || value > 1e3) throw new SacnValidationError(`${label} must be a finite number greater than 0 and at most 1000.`, "INVALID_FPS");
|
|
67
|
+
};
|
|
68
|
+
const assertTimeout = (value) => {
|
|
69
|
+
if (!Number.isFinite(value) || value <= 0) throw new SacnValidationError("Send timeout must be a finite number greater than 0.", "INVALID_TIMEOUT");
|
|
70
|
+
};
|
|
71
|
+
const assertPort = (value) => {
|
|
72
|
+
integerInRange(value, 1, 65535, "UDP port", "INVALID_PORT");
|
|
73
|
+
};
|
|
74
|
+
const assertFade = (value) => {
|
|
75
|
+
if (!Number.isFinite(value) || value < 0) throw new SacnValidationError("Fade duration must be a finite number greater than or equal to 0.", "INVALID_FADE");
|
|
76
|
+
};
|
|
77
|
+
const assertSourceName = (value) => {
|
|
78
|
+
if (typeof value !== "string") throw new SacnValidationError("Source name must be a string.", "INVALID_SOURCE_NAME");
|
|
79
|
+
const normalized = value.trim();
|
|
80
|
+
if (normalized.length === 0 || normalized.length > 64) throw new SacnValidationError("Source name must contain between 1 and 64 characters.", "INVALID_SOURCE_NAME");
|
|
81
|
+
return normalized;
|
|
82
|
+
};
|
|
83
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
84
|
+
const assertCid = (value) => {
|
|
85
|
+
if (typeof value !== "string" || !UUID_PATTERN.test(value)) throw new SacnValidationError("CID must be a valid UUID.", "INVALID_CID");
|
|
86
|
+
return value.toLowerCase();
|
|
87
|
+
};
|
|
88
|
+
const assertChannelWrite = (write) => {
|
|
89
|
+
if (!write || typeof write !== "object") throw new SacnValidationError("Each channel write must be an object.", "INVALID_CHANNEL");
|
|
90
|
+
integerInRange(write.channel, 1, 512, "Channel", "INVALID_CHANNEL");
|
|
91
|
+
integerInRange(write.value, 0, 255, "Channel value", "INVALID_CHANNEL");
|
|
92
|
+
if (write.durationMs !== void 0) assertFade(write.durationMs);
|
|
93
|
+
};
|
|
94
|
+
const validateChannelWrites = (writes) => {
|
|
95
|
+
if (!Array.isArray(writes)) throw new SacnValidationError("Channel writes must be an array.", "INVALID_CHANNEL");
|
|
96
|
+
if (writes.length === 0) throw new SacnValidationError("At least one channel write is required.", "INVALID_CHANNEL");
|
|
97
|
+
const seen = /* @__PURE__ */ new Set();
|
|
98
|
+
for (const write of writes) {
|
|
99
|
+
assertChannelWrite(write);
|
|
100
|
+
if (seen.has(write.channel)) throw new SacnValidationError(`Channel ${write.channel} appears more than once.`, "INVALID_CHANNEL");
|
|
101
|
+
seen.add(write.channel);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const assertTransitionWrite = (write) => {
|
|
105
|
+
if (!write || typeof write !== "object") throw new SacnValidationError("Each transition write must be an object.", "INVALID_CHANNEL");
|
|
106
|
+
integerInRange(write.channel, 1, 512, "Channel", "INVALID_CHANNEL");
|
|
107
|
+
integerInRange(write.value, 0, 255, "Channel value", "INVALID_CHANNEL");
|
|
108
|
+
assertFade(write.durationMs);
|
|
109
|
+
};
|
|
110
|
+
const validateTransitionWrites = (writes) => {
|
|
111
|
+
if (!Array.isArray(writes)) throw new SacnValidationError("Transition writes must be an array.", "INVALID_CHANNEL");
|
|
112
|
+
if (writes.length === 0) throw new SacnValidationError("At least one transition write is required.", "INVALID_CHANNEL");
|
|
113
|
+
const seen = /* @__PURE__ */ new Set();
|
|
114
|
+
for (const write of writes) {
|
|
115
|
+
assertTransitionWrite(write);
|
|
116
|
+
if (seen.has(write.channel)) throw new SacnValidationError(`Channel ${write.channel} appears more than once.`, "INVALID_CHANNEL");
|
|
117
|
+
seen.add(write.channel);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const channelValuesToWrites = (values, durationMs) => {
|
|
121
|
+
if (!values || typeof values !== "object" || Array.isArray(values)) throw new SacnValidationError("Channel values must be an object map.", "INVALID_CHANNEL");
|
|
122
|
+
if (durationMs !== void 0) assertFade(durationMs);
|
|
123
|
+
const writes = [];
|
|
124
|
+
for (const [rawKey, rawValue] of Object.entries(values)) {
|
|
125
|
+
const channel = Number(rawKey);
|
|
126
|
+
if (!Number.isInteger(channel) || String(channel) !== rawKey) throw new SacnValidationError(`Channel key "${rawKey}" must be an integer between 1 and 512.`, "INVALID_CHANNEL");
|
|
127
|
+
if (typeof rawValue !== "number") throw new SacnValidationError(`Channel ${channel} value must be a number.`, "INVALID_CHANNEL");
|
|
128
|
+
writes.push({
|
|
129
|
+
channel,
|
|
130
|
+
value: rawValue,
|
|
131
|
+
...durationMs === void 0 ? {} : { durationMs }
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
validateChannelWrites(writes);
|
|
135
|
+
return writes;
|
|
136
|
+
};
|
|
137
|
+
const toValidatedFrame = (values) => {
|
|
138
|
+
if (!Array.isArray(values) && !(values instanceof Uint8Array)) throw new SacnValidationError("Frame values must be an array or Uint8Array.", "INVALID_FRAME");
|
|
139
|
+
if (values.length !== 512) throw new SacnValidationError(`Frame must contain exactly 512 values.`, "INVALID_FRAME");
|
|
140
|
+
const frame = /* @__PURE__ */ new Uint8Array(512);
|
|
141
|
+
for (let index = 0; index < 512; index += 1) {
|
|
142
|
+
const value = values[index];
|
|
143
|
+
if (value === void 0 || !Number.isInteger(value) || value < 0 || value > 255) throw new SacnValidationError(`Frame value at channel ${index + 1} must be an integer between 0 and 255.`, "INVALID_FRAME");
|
|
144
|
+
frame[index] = value;
|
|
145
|
+
}
|
|
146
|
+
return frame;
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
export { MAX_UNIVERSE as C, SLOT_COUNT as E, MAX_PRIORITY as S, MIN_UNIVERSE as T, validateChannelWrites as _, SacnValidationError as a, DEFAULT_IDLE_FPS as b, assertCid as c, assertPort as d, assertSourceName as f, toValidatedFrame as g, normalizeAddress as h, SacnLifecycleError as i, assertFade as l, channelValuesToWrites as m, PersistenceError as n, TransportError as o, assertTimeout as p, SacnError as r, TransportTimeoutError as s, DependencyUnavailableError as t, assertFps as u, validateTransitionWrites as v, MIN_PRIORITY as w, DEFAULT_PRIORITY as x, DEFAULT_ACTIVE_FPS as y };
|
|
150
|
+
|
|
151
|
+
//# sourceMappingURL=validation-BdsVeyAE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-BdsVeyAE.js","names":[],"sources":["../src/contracts.ts","../src/validation.ts"],"sourcesContent":["export const SLOT_COUNT = 512;\nexport const MIN_UNIVERSE = 1;\nexport const MAX_UNIVERSE = 63_999;\nexport const MIN_PRIORITY = 0;\nexport const MAX_PRIORITY = 200;\nexport const DEFAULT_PRIORITY = 100;\nexport const DEFAULT_ACTIVE_FPS = 44;\nexport const DEFAULT_IDLE_FPS = 2;\n\nexport interface OutputAddress {\n\treadonly universe: number;\n\treadonly priority?: number;\n}\n\nexport interface OutputOptions extends OutputAddress {\n\treadonly cid?: string;\n\treadonly idleFps?: number;\n\treadonly sourceName?: string;\n}\n\n/** One-based DMX channel → value map used by setChannels / fadeChannels. */\nexport type ChannelValues = Readonly<Record<number, number>>;\n\nexport interface ChannelWrite {\n\t/** One-based DMX slot number. */\n\treadonly channel: number;\n\treadonly value: number;\n\t/** Linear transition duration in milliseconds. Defaults to 0 (snap). */\n\treadonly durationMs?: number;\n}\n\nexport interface TransitionWrite {\n\treadonly channel: number;\n\treadonly value: number;\n\treadonly durationMs: number;\n}\n\nexport interface FadeChannelsOptions {\n\treadonly durationMs: number;\n\treadonly signal?: AbortSignal;\n}\n\nexport interface WriteFrameOptions {\n\treadonly durationMs?: number;\n\treadonly signal?: AbortSignal;\n}\n\nexport interface ClearUniverseOptions {\n\treadonly signal?: AbortSignal;\n}\n\nexport interface UniverseOptions {\n\treadonly priority?: number;\n\treadonly cid?: string;\n\treadonly idleFps?: number;\n\treadonly sourceName?: string;\n}\n\nexport type FrameMode = \"active\" | \"idle\";\n\nexport interface OutputSnapshot {\n\treadonly activeTransitions: number;\n\treadonly cid: string;\n\treadonly current: readonly number[];\n\treadonly dirty: boolean;\n\treadonly frameMode: FrameMode;\n\treadonly idleFps: number;\n\treadonly lastError: string | null;\n\treadonly lastSent: readonly number[];\n\treadonly lastSentAt: number | null;\n\treadonly nextDueAt: number;\n\treadonly priority: number;\n\treadonly sequence: number;\n\treadonly sourceName: string | null;\n\treadonly target: readonly number[];\n\treadonly universe: number;\n\treadonly updatedAt: number;\n}\n\nexport interface OutputRecord {\n\treadonly cid: string;\n\treadonly idleFps: number;\n\treadonly priority: number;\n\treadonly sourceName: string | null;\n\treadonly target: readonly number[];\n\treadonly universe: number;\n\treadonly updatedAt: number;\n}\n\nexport interface OutputStore {\n\tget(address: Required<OutputAddress>): Promise<OutputRecord | null>;\n\tlist(): Promise<readonly OutputRecord[]>;\n\tremove(address: Required<OutputAddress>): Promise<void>;\n\tsave(record: OutputRecord): Promise<void>;\n\tclose?(): Promise<void>;\n}\n\nexport interface OutputPacket {\n\treadonly cid: string;\n\treadonly data: Uint8Array;\n\treadonly priority: number;\n\treadonly sequence: number;\n\treadonly sourceName: string | null;\n\treadonly universe: number;\n}\n\nexport interface OutputTransport {\n\tsend(packet: OutputPacket, signal: AbortSignal): Promise<void>;\n\tcloseOutput?(address: Required<OutputAddress>): Promise<void>;\n\tclose(): Promise<void>;\n}\n\nexport interface Clock {\n\t/** Monotonic milliseconds used for scheduling. */\n\tnow(): number;\n\t/** Wall-clock Unix milliseconds used for public and persisted metadata. */\n\twallNow?(): number;\n\tsleep(delayMs: number, signal: AbortSignal): Promise<void>;\n}\n\nexport interface Logger {\n\tdebug?(message: string, context?: Readonly<Record<string, unknown>>): void;\n\tinfo?(message: string, context?: Readonly<Record<string, unknown>>): void;\n\twarn?(message: string, context?: Readonly<Record<string, unknown>>): void;\n\terror?(message: string, context?: Readonly<Record<string, unknown>>): void;\n}\n\nexport interface TransportTelemetry {\n\treadonly sendAttempts: number;\n\treadonly sendFailures: number;\n\treadonly sendRetries: number;\n\treadonly sendSuccesses: number;\n\treadonly sendTimeouts: number;\n}\n\nexport interface EngineTelemetry {\n\treadonly closed: boolean;\n\treadonly lastLoopCompletedAt: number | null;\n\treadonly lastLoopDurationMs: number | null;\n\treadonly lastLoopStartedAt: number | null;\n\treadonly loopIterations: number;\n\treadonly outputCount: number;\n\treadonly outputs: readonly OutputSnapshot[];\n\treadonly running: boolean;\n\treadonly transport: TransportTelemetry;\n}\n\nexport type SacnLifecycleHook = () => void | Promise<void>;\n\nexport interface SacnSourceOptions {\n\treadonly transport: OutputTransport;\n\treadonly store?: OutputStore;\n\treadonly clock?: Clock;\n\treadonly logger?: Logger;\n\treadonly activeFps?: number;\n\treadonly idleFps?: number;\n\treadonly sendTimeoutMs?: number;\n\t/** Maximum time spent awaiting transport shutdown. Defaults to 2 seconds. */\n\treadonly shutdownTimeoutMs?: number;\n\t/** Maximum number of active universe/priority outputs. Defaults to 1024. */\n\treadonly maxOutputs?: number;\n\t/** Close the injected transport with the source. Defaults to false. */\n\treadonly ownsTransport?: boolean;\n\t/** Defaults to true for the default memory store, false for an injected store. */\n\treadonly ownsStore?: boolean;\n\treadonly createId?: () => string;\n\treadonly onStart?: SacnLifecycleHook;\n\treadonly onStop?: SacnLifecycleHook;\n\treadonly onClose?: SacnLifecycleHook;\n}\n\nexport type SacnSourceEvent =\n\t| { readonly type: \"started\" }\n\t| { readonly type: \"stopped\" }\n\t| { readonly type: \"output-updated\"; readonly output: OutputSnapshot }\n\t| {\n\t\t\treadonly type: \"output-cleared\";\n\t\t\treadonly address: Required<OutputAddress>;\n\t }\n\t| { readonly type: \"closed\" };\n\nexport interface UniverseContract {\n\treadonly universe: number;\n\treadonly priority: number;\n\tsetChannels(\n\t\tvalues: ChannelValues,\n\t\toptions?: { readonly signal?: AbortSignal },\n\t): Promise<OutputSnapshot>;\n\tfadeChannels(\n\t\tvalues: ChannelValues,\n\t\toptions: FadeChannelsOptions,\n\t): Promise<OutputSnapshot>;\n\ttransition(\n\t\twrites: readonly TransitionWrite[],\n\t\toptions?: { readonly signal?: AbortSignal },\n\t): Promise<OutputSnapshot>;\n\twrite(\n\t\tvalues: readonly number[] | Uint8Array,\n\t\toptions?: WriteFrameOptions,\n\t): Promise<OutputSnapshot>;\n\tget(): Promise<OutputSnapshot | null>;\n\tclear(options?: ClearUniverseOptions): Promise<boolean>;\n}\n\nexport interface SacnSourceContract {\n\tuniverse(universe: number, options?: UniverseOptions): UniverseContract;\n\tstart(): Promise<void>;\n\tstop(): Promise<void>;\n\tlistUniverses(): Promise<readonly OutputSnapshot[]>;\n\tgetTelemetry(): EngineTelemetry;\n\tsubscribe(listener: (event: SacnSourceEvent) => void): () => void;\n\tclose(): Promise<void>;\n}\n\nexport interface ReceiverPacket {\n\treadonly cid: string;\n\treadonly priority: number;\n\treadonly sequence: number;\n\treadonly sourceAddress: string | null;\n\treadonly sourceName: string | null;\n\treadonly universe: number;\n\treadonly values: Uint8Array;\n}\n\nexport type ReceiverPacketListener = (packet: ReceiverPacket) => void;\n\n/** Runtime-neutral source of normalized sACN packets. */\nexport interface Receiver {\n\taddUniverse(universe: number): Promise<void> | void;\n\tremoveUniverse(universe: number): Promise<void> | void;\n\tsubscribe(listener: ReceiverPacketListener): () => void;\n\tclose(): Promise<void>;\n}\n\nexport interface ViewerSourceMetadata {\n\treadonly cid: string;\n\treadonly priority: number;\n\treadonly sequence: number;\n\treadonly sourceAddress: string | null;\n\treadonly sourceName: string | null;\n}\n\nexport interface ViewerPacket {\n\treadonly receivedAt: number;\n\treadonly source: ViewerSourceMetadata;\n\treadonly universe: number;\n\t/** Exactly 512 normalized values. */\n\treadonly values: readonly number[];\n}\n\nexport interface ViewerRecord {\n\treadonly selectedUniverses: readonly number[];\n\treadonly updatedAt: number;\n}\n\nexport interface ViewerStore {\n\tload(): Promise<ViewerRecord | null>;\n\tsave(record: ViewerRecord): Promise<void>;\n\tclose?(): Promise<void>;\n}\n\nexport interface ViewerTelemetry {\n\treadonly droppedUpdates: number;\n\treadonly packetsReceived: number;\n\treadonly selectedUniverses: readonly number[];\n\treadonly streamCount: number;\n}\n\nexport interface ViewerPacketStream extends AsyncIterable<ViewerPacket> {\n\tclose(): void;\n}\n\nexport interface ViewerServiceContract {\n\tstart(): Promise<void>;\n\tgetSelectedUniverses(): readonly number[];\n\tsetSelectedUniverses(\n\t\tuniverses: readonly number[],\n\t): Promise<readonly number[]>;\n\taddUniverse(universe: number): Promise<readonly number[]>;\n\tremoveUniverse(universe: number): Promise<readonly number[]>;\n\tsubscribe(listener: (packet: ViewerPacket) => void): () => void;\n\tpackets(capacity?: number): ViewerPacketStream;\n\tgetTelemetry(): ViewerTelemetry;\n\tclose(): Promise<void>;\n}\n\nexport interface ViewerServiceOptions {\n\treadonly receiver: Receiver;\n\treadonly store?: ViewerStore;\n\treadonly clock?: Pick<Clock, \"now\">;\n\treadonly logger?: Logger;\n\t/** Close the injected receiver with this service. Defaults to false. */\n\treadonly ownsReceiver?: boolean;\n\t/** Defaults to true for the default memory store, false for an injected store. */\n\treadonly ownsStore?: boolean;\n\t/** Default queue capacity for streams. Defaults to 32. */\n\treadonly streamCapacity?: number;\n\t/** Maximum number of selected universes. Defaults to 256. */\n\treadonly maxUniverses?: number;\n\t/** Maximum combined callback and stream subscribers. Defaults to 256. */\n\treadonly maxListeners?: number;\n}\n","import {\n DEFAULT_PRIORITY,\n MAX_PRIORITY,\n MAX_UNIVERSE,\n MIN_PRIORITY,\n MIN_UNIVERSE,\n SLOT_COUNT,\n type ChannelValues,\n type ChannelWrite,\n type OutputAddress,\n type TransitionWrite,\n} from \"./contracts.js\";\n\nexport type ValidationCode =\n | \"INVALID_CHANNEL\"\n | \"INVALID_CID\"\n | \"INVALID_FADE\"\n | \"INVALID_FPS\"\n | \"INVALID_FRAME\"\n | \"INVALID_PRIORITY\"\n | \"INVALID_PORT\"\n | \"INVALID_NAMESPACE\"\n | \"INVALID_SOURCE_NAME\"\n | \"INVALID_TIMEOUT\"\n | \"INVALID_UNIVERSE\";\n\nexport class SacnError extends Error {\n constructor(\n message: string,\n readonly code: string,\n readonly cause?: unknown,\n ) {\n super(message, cause === undefined ? undefined : { cause });\n this.name = new.target.name;\n }\n}\n\nexport class SacnValidationError extends SacnError {\n constructor(message: string, code: ValidationCode) {\n super(message, code);\n }\n}\n\nexport class SacnLifecycleError extends SacnError {\n constructor(message: string) {\n super(message, \"INVALID_LIFECYCLE\");\n }\n}\n\nexport class TransportTimeoutError extends SacnError {\n constructor(timeoutMs: number, universe: number, priority: number) {\n super(\n `Transport send timed out after ${timeoutMs}ms for output ${universe}:${priority}.`,\n \"TRANSPORT_TIMEOUT\",\n );\n }\n}\n\nexport class TransportError extends SacnError {\n constructor(message: string, cause?: unknown) {\n super(message, \"TRANSPORT_ERROR\", cause);\n }\n}\n\nexport class PersistenceError extends SacnError {\n constructor(message: string, cause?: unknown) {\n super(message, \"PERSISTENCE_ERROR\", cause);\n }\n}\n\nexport class DependencyUnavailableError extends SacnError {\n constructor(message: string, cause?: unknown) {\n super(message, \"DEPENDENCY_UNAVAILABLE\", cause);\n }\n}\n\nconst integerInRange = (\n value: number,\n minimum: number,\n maximum: number,\n label: string,\n code: ValidationCode,\n): void => {\n if (!Number.isInteger(value) || value < minimum || value > maximum) {\n throw new SacnValidationError(\n `${label} must be an integer between ${minimum} and ${maximum}.`,\n code,\n );\n }\n};\n\nexport const normalizeAddress = (\n address: OutputAddress,\n): Required<OutputAddress> => {\n if (!address || typeof address !== \"object\") {\n throw new SacnValidationError(\n \"Output address must be an object.\",\n \"INVALID_UNIVERSE\",\n );\n }\n integerInRange(\n address.universe,\n MIN_UNIVERSE,\n MAX_UNIVERSE,\n \"Universe\",\n \"INVALID_UNIVERSE\",\n );\n const priority = address.priority ?? DEFAULT_PRIORITY;\n integerInRange(\n priority,\n MIN_PRIORITY,\n MAX_PRIORITY,\n \"Priority\",\n \"INVALID_PRIORITY\",\n );\n return { universe: address.universe, priority };\n};\n\nexport const assertFps = (value: number, label = \"FPS\"): void => {\n if (!Number.isFinite(value) || value <= 0 || value > 1000) {\n throw new SacnValidationError(\n `${label} must be a finite number greater than 0 and at most 1000.`,\n \"INVALID_FPS\",\n );\n }\n};\n\nexport const assertTimeout = (value: number): void => {\n if (!Number.isFinite(value) || value <= 0) {\n throw new SacnValidationError(\n \"Send timeout must be a finite number greater than 0.\",\n \"INVALID_TIMEOUT\",\n );\n }\n};\n\nexport const assertPort = (value: number): void => {\n integerInRange(value, 1, 65_535, \"UDP port\", \"INVALID_PORT\");\n};\n\nexport const assertFade = (value: number): void => {\n if (!Number.isFinite(value) || value < 0) {\n throw new SacnValidationError(\n \"Fade duration must be a finite number greater than or equal to 0.\",\n \"INVALID_FADE\",\n );\n }\n};\n\n/** @deprecated Prefer assertFade; kept as a clearer alias for durationMs APIs. */\nexport const assertDuration = assertFade;\n\nexport const assertSourceName = (value: string): string => {\n if (typeof value !== \"string\") {\n throw new SacnValidationError(\n \"Source name must be a string.\",\n \"INVALID_SOURCE_NAME\",\n );\n }\n const normalized = value.trim();\n if (normalized.length === 0 || normalized.length > 64) {\n throw new SacnValidationError(\n \"Source name must contain between 1 and 64 characters.\",\n \"INVALID_SOURCE_NAME\",\n );\n }\n return normalized;\n};\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\nexport const assertCid = (value: string): string => {\n if (typeof value !== \"string\" || !UUID_PATTERN.test(value)) {\n throw new SacnValidationError(\"CID must be a valid UUID.\", \"INVALID_CID\");\n }\n return value.toLowerCase();\n};\n\nexport const assertChannelWrite = (write: ChannelWrite): void => {\n if (!write || typeof write !== \"object\") {\n throw new SacnValidationError(\n \"Each channel write must be an object.\",\n \"INVALID_CHANNEL\",\n );\n }\n integerInRange(write.channel, 1, SLOT_COUNT, \"Channel\", \"INVALID_CHANNEL\");\n integerInRange(write.value, 0, 255, \"Channel value\", \"INVALID_CHANNEL\");\n if (write.durationMs !== undefined) assertFade(write.durationMs);\n};\n\nexport const validateChannelWrites = (\n writes: readonly ChannelWrite[],\n): void => {\n if (!Array.isArray(writes)) {\n throw new SacnValidationError(\n \"Channel writes must be an array.\",\n \"INVALID_CHANNEL\",\n );\n }\n if (writes.length === 0) {\n throw new SacnValidationError(\n \"At least one channel write is required.\",\n \"INVALID_CHANNEL\",\n );\n }\n const seen = new Set<number>();\n for (const write of writes) {\n assertChannelWrite(write);\n if (seen.has(write.channel)) {\n throw new SacnValidationError(\n `Channel ${write.channel} appears more than once.`,\n \"INVALID_CHANNEL\",\n );\n }\n seen.add(write.channel);\n }\n};\n\nexport const assertTransitionWrite = (write: TransitionWrite): void => {\n if (!write || typeof write !== \"object\") {\n throw new SacnValidationError(\n \"Each transition write must be an object.\",\n \"INVALID_CHANNEL\",\n );\n }\n integerInRange(write.channel, 1, SLOT_COUNT, \"Channel\", \"INVALID_CHANNEL\");\n integerInRange(write.value, 0, 255, \"Channel value\", \"INVALID_CHANNEL\");\n assertFade(write.durationMs);\n};\n\nexport const validateTransitionWrites = (\n writes: readonly TransitionWrite[],\n): void => {\n if (!Array.isArray(writes)) {\n throw new SacnValidationError(\n \"Transition writes must be an array.\",\n \"INVALID_CHANNEL\",\n );\n }\n if (writes.length === 0) {\n throw new SacnValidationError(\n \"At least one transition write is required.\",\n \"INVALID_CHANNEL\",\n );\n }\n const seen = new Set<number>();\n for (const write of writes) {\n assertTransitionWrite(write);\n if (seen.has(write.channel)) {\n throw new SacnValidationError(\n `Channel ${write.channel} appears more than once.`,\n \"INVALID_CHANNEL\",\n );\n }\n seen.add(write.channel);\n }\n};\n\nexport const channelValuesToWrites = (\n values: ChannelValues,\n durationMs?: number,\n): ChannelWrite[] => {\n if (!values || typeof values !== \"object\" || Array.isArray(values)) {\n throw new SacnValidationError(\n \"Channel values must be an object map.\",\n \"INVALID_CHANNEL\",\n );\n }\n if (durationMs !== undefined) assertFade(durationMs);\n const writes: ChannelWrite[] = [];\n for (const [rawKey, rawValue] of Object.entries(values)) {\n const channel = Number(rawKey);\n if (!Number.isInteger(channel) || String(channel) !== rawKey) {\n throw new SacnValidationError(\n `Channel key \"${rawKey}\" must be an integer between 1 and ${SLOT_COUNT}.`,\n \"INVALID_CHANNEL\",\n );\n }\n if (typeof rawValue !== \"number\") {\n throw new SacnValidationError(\n `Channel ${channel} value must be a number.`,\n \"INVALID_CHANNEL\",\n );\n }\n writes.push({\n channel,\n value: rawValue,\n ...(durationMs === undefined ? {} : { durationMs }),\n });\n }\n validateChannelWrites(writes);\n return writes;\n};\n\nexport const toValidatedFrame = (\n values: readonly number[] | Uint8Array,\n): Uint8Array => {\n if (!Array.isArray(values) && !(values instanceof Uint8Array)) {\n throw new SacnValidationError(\n \"Frame values must be an array or Uint8Array.\",\n \"INVALID_FRAME\",\n );\n }\n if (values.length !== SLOT_COUNT) {\n throw new SacnValidationError(\n `Frame must contain exactly ${SLOT_COUNT} values.`,\n \"INVALID_FRAME\",\n );\n }\n const frame = new Uint8Array(SLOT_COUNT);\n for (let index = 0; index < SLOT_COUNT; index += 1) {\n const value = values[index];\n if (\n value === undefined ||\n !Number.isInteger(value) ||\n value < 0 ||\n value > 255\n ) {\n throw new SacnValidationError(\n `Frame value at channel ${index + 1} must be an integer between 0 and 255.`,\n \"INVALID_FRAME\",\n );\n }\n frame[index] = value;\n }\n return frame;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createSacnSource } from "@helioslx/core";
|
|
2
|
+
import { createSacnHttpAdapter } from "@helioslx/core/http";
|
|
3
|
+
import { RecordingTransport } from "@helioslx/core/testing";
|
|
4
|
+
import { Elysia } from "elysia";
|
|
5
|
+
|
|
6
|
+
const source = createSacnSource({
|
|
7
|
+
transport: new RecordingTransport(),
|
|
8
|
+
ownsTransport: true,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
await source.start();
|
|
12
|
+
|
|
13
|
+
const sacn = createSacnHttpAdapter({
|
|
14
|
+
source,
|
|
15
|
+
prefix: "/sacn",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const app = new Elysia().use(sacn).get("/", () => ({ ok: true }));
|
|
19
|
+
|
|
20
|
+
const port = Number(process.env.PORT ?? 3000);
|
|
21
|
+
app.listen(port);
|
|
22
|
+
console.info(`Listening on http://127.0.0.1:${port}`);
|
|
23
|
+
|
|
24
|
+
const shutdown = async (): Promise<void> => {
|
|
25
|
+
await app.stop();
|
|
26
|
+
await source.close();
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
process.once("SIGINT", () => void shutdown());
|
|
30
|
+
process.once("SIGTERM", () => void shutdown());
|