mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
build(spf): build26 from 45504a2b
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
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
|
||||
Reference in New Issue
Block a user