Files
v10/dist/dev/core/composition/create-composition.js
T

85 lines
3.0 KiB
JavaScript

import { signal } from "../signals/primitives.js";
//#region src/core/composition/create-composition.ts
/**
* Create a composition from a set of behaviors.
*
* Composition unions the behaviors' declared `stateKeys` / `contextKeys`
* to know which signals to create. Each signal is seeded from
* `initialState` / `initialContext` when supplied, defaulting to
* `undefined`. Behaviors are responsible for writing their own slots
* once their preconditions are met.
*
* Cross-behavior type conflicts (e.g. two behaviors disagreeing on a
* field's type) surface as a compose-time type error via
* `ValidateComposition`.
*
* @example
* ```ts
* const composition = createComposition([resolvePresentation, switchVideoTrack], {
* config: { parsePresentation: parseMultivariantPlaylist, initialBandwidth: 2_000_000 },
* initialState: { bandwidthState: { fastEstimate: 0, ... } },
* });
* ```
*/
/**
* Create a typed signal map for a given set of keys, seeded from an
* optional partial initial value.
*
* Pipeline: `Set` dedupes the iterable (insertion order preserved, so
* first occurrence wins) → `Object.fromEntries` materializes one
* `signal()` per unique key, seeded from `initial[key]` or `undefined`.
*
* Per-key value types live in TypeScript only — at runtime every signal
* is `Signal<unknown>`. The boundary cast at the return narrows the wide
* `Record<PropertyKey, Signal<unknown>>` shape to the caller's expected
* per-key types from `S`.
*
* Used by `createComposition` to derive engine state/context maps from
* the union of behaviors' declared `stateKeys` / `contextKeys`.
*
* @example
* ```ts
* interface State { count?: number; label?: string }
* const state = buildSignalMap<State>(['count', 'label'], { count: 5 });
* state.count.get(); // 5
* state.label.get(); // undefined
* ```
*/
function buildSignalMap(keys, initial) {
const init = initial;
const uniqueKeys = new Set(keys);
return Object.fromEntries([...uniqueKeys].map((key) => [key, signal(init[key])]));
}
function createComposition(behaviors, options) {
const validBehaviors = behaviors;
const state = buildSignalMap(validBehaviors.flatMap((b) => b.stateKeys), options?.initialState ?? {});
const context = buildSignalMap(validBehaviors.flatMap((b) => b.contextKeys), options?.initialContext ?? {});
const deps = {
state,
context,
config: options?.config ?? {}
};
const cleanups = validBehaviors.map((behavior) => behavior.setup(deps));
return {
state,
context,
async destroy() {
const results = [];
for (const cleanup of cleanups) {
if (cleanup == null) continue;
if (typeof cleanup === "function") results.push(cleanup());
else if ("destroy" in cleanup) results.push(cleanup.destroy());
}
await Promise.all(results);
for (const sig of Object.values(state)) sig.set(void 0);
for (const sig of Object.values(context)) sig.set(void 0);
}
};
}
function defineBehavior(behavior) {
return behavior;
}
//#endregion
export { buildSignalMap, createComposition, defineBehavior };
//# sourceMappingURL=create-composition.js.map