mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
build(store): build26 from 45504a2b
This commit is contained in:
+16
@@ -0,0 +1,16 @@
|
||||
//#region src/core/abort-controller-registry.d.ts
|
||||
type SignalKey = PropertyKey;
|
||||
declare class AbortControllerRegistry {
|
||||
#private;
|
||||
/** The attach-scoped signal. Aborts on detach or reattach. */
|
||||
get base(): AbortSignal;
|
||||
/** Clears all keyed signals, leaving base intact. */
|
||||
clear(): void;
|
||||
/** Resets base and clears all keyed signals. */
|
||||
reset(): void;
|
||||
/** Creates a new signal for the key, superseding any previous signal. */
|
||||
supersede(key: SignalKey): AbortSignal;
|
||||
}
|
||||
//#endregion
|
||||
export { AbortControllerRegistry, SignalKey };
|
||||
//# sourceMappingURL=abort-controller-registry.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"abort-controller-registry.d.ts","names":[],"sources":["../../../src/core/abort-controller-registry.ts"],"mappings":";KAEY,YAAY;cAEX;;;MAKP,QAAQ;;EAKZ;;EAQA;;EAOA,UAAU,KAAK,YAAY"}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { anyAbortSignal } from "@videojs/utils/events";
|
||||
//#region src/core/abort-controller-registry.ts
|
||||
var AbortControllerRegistry = class {
|
||||
#base = new AbortController();
|
||||
#keys = /* @__PURE__ */ new Map();
|
||||
/** The attach-scoped signal. Aborts on detach or reattach. */
|
||||
get base() {
|
||||
return this.#base.signal;
|
||||
}
|
||||
/** Clears all keyed signals, leaving base intact. */
|
||||
clear() {
|
||||
for (const controller of this.#keys.values()) controller.abort();
|
||||
this.#keys.clear();
|
||||
}
|
||||
/** Resets base and clears all keyed signals. */
|
||||
reset() {
|
||||
this.clear();
|
||||
this.#base.abort();
|
||||
this.#base = new AbortController();
|
||||
}
|
||||
/** Creates a new signal for the key, superseding any previous signal. */
|
||||
supersede(key) {
|
||||
this.#keys.get(key)?.abort();
|
||||
const controller = new AbortController();
|
||||
this.#keys.set(key, controller);
|
||||
return anyAbortSignal([this.#base.signal, controller.signal]);
|
||||
}
|
||||
};
|
||||
//#endregion
|
||||
export { AbortControllerRegistry };
|
||||
|
||||
//# sourceMappingURL=abort-controller-registry.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"abort-controller-registry.js","names":["#base","#keys"],"sources":["../../../src/core/abort-controller-registry.ts"],"sourcesContent":["import { anyAbortSignal } from '@videojs/utils/events';\n\nexport type SignalKey = PropertyKey;\n\nexport class AbortControllerRegistry {\n #base = new AbortController();\n #keys = new Map<SignalKey, AbortController>();\n\n /** The attach-scoped signal. Aborts on detach or reattach. */\n get base(): AbortSignal {\n return this.#base.signal;\n }\n\n /** Clears all keyed signals, leaving base intact. */\n clear(): void {\n for (const controller of this.#keys.values()) {\n controller.abort();\n }\n this.#keys.clear();\n }\n\n /** Resets base and clears all keyed signals. */\n reset(): void {\n this.clear();\n this.#base.abort();\n this.#base = new AbortController();\n }\n\n /** Creates a new signal for the key, superseding any previous signal. */\n supersede(key: SignalKey): AbortSignal {\n this.#keys.get(key)?.abort();\n const controller = new AbortController();\n this.#keys.set(key, controller);\n return anyAbortSignal([this.#base.signal, controller.signal]);\n }\n}\n"],"mappings":";;AAIA,IAAa,0BAAb,MAAqC;CACnC,QAAQ,IAAI,gBAAgB;CAC5B,wBAAQ,IAAI,IAAgC;;CAG5C,IAAI,OAAoB;EACtB,OAAO,KAAKA,MAAM;CACpB;;CAGA,QAAc;EACZ,KAAK,MAAM,cAAc,KAAKC,MAAM,OAAO,GACzC,WAAW,MAAM;EAEnB,KAAKA,MAAM,MAAM;CACnB;;CAGA,QAAc;EACZ,KAAK,MAAM;EACX,KAAKD,MAAM,MAAM;EACjB,KAAKA,QAAQ,IAAI,gBAAgB;CACnC;;CAGA,UAAU,KAA6B;EACrC,KAAKC,MAAM,IAAI,GAAG,CAAC,EAAE,MAAM;EAC3B,MAAM,aAAa,IAAI,gBAAgB;EACvC,KAAKA,MAAM,IAAI,KAAK,UAAU;EAC9B,OAAO,eAAe,CAAC,KAAKD,MAAM,QAAQ,WAAW,MAAM,CAAC;CAC9D;AACF"}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
import { Slice, UnionSliceState } from "./slice.js";
|
||||
//#region src/core/combine.d.ts
|
||||
/**
|
||||
* Combines multiple slices into a single slice.
|
||||
*
|
||||
* @param slices - The slices to combine.
|
||||
* @returns A new slice that represents the combination of the input slices.
|
||||
*/
|
||||
declare function combine<Target, const Slices extends Slice<Target, any>[]>(...slices: Slices): Slice<Target, UnionSliceState<Slices>>;
|
||||
//#endregion
|
||||
export { combine };
|
||||
//# sourceMappingURL=combine.d.ts.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"combine.d.ts","names":[],"sources":["../../../src/core/combine.ts"],"mappings":";;;;;;;;iBAQgB,QAAQ,cAAc,eAAe,MAAM,mBACtD,QAAQ,SACV,MAAM,QAAQ,gBAAgB"}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
//#region src/core/combine.ts
|
||||
/**
|
||||
* Combines multiple slices into a single slice.
|
||||
*
|
||||
* @param slices - The slices to combine.
|
||||
* @returns A new slice that represents the combination of the input slices.
|
||||
*/
|
||||
function combine(...slices) {
|
||||
return {
|
||||
state: (ctx) => {
|
||||
const states = slices.map((slice) => slice.state(ctx));
|
||||
{
|
||||
const seen = /* @__PURE__ */ new Set();
|
||||
for (const state of states) for (const key of Object.keys(state)) {
|
||||
if (seen.has(key)) console.warn(`[vjs-store] combine(): duplicate state key "${key}" — later slice overwrites earlier one`);
|
||||
seen.add(key);
|
||||
}
|
||||
}
|
||||
return Object.assign({}, ...states);
|
||||
},
|
||||
attach: (ctx) => {
|
||||
for (const slice of slices) try {
|
||||
slice.attach?.(ctx);
|
||||
} catch (err) {
|
||||
ctx.reportError(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
//#endregion
|
||||
export { combine };
|
||||
|
||||
//# sourceMappingURL=combine.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"combine.js","names":[],"sources":["../../../src/core/combine.ts"],"sourcesContent":["import type { AttachContext, InferSliceState, Slice, StateContext, UnionSliceState } from './slice';\n\n/**\n * Combines multiple slices into a single slice.\n *\n * @param slices - The slices to combine.\n * @returns A new slice that represents the combination of the input slices.\n */\nexport function combine<Target, const Slices extends Slice<Target, any>[]>(\n ...slices: Slices\n): Slice<Target, UnionSliceState<Slices>> {\n return {\n state: (ctx: StateContext<Target>) => {\n const states = slices.map((slice) => slice.state(ctx));\n\n if (__DEV__) {\n const seen = new Set<string>();\n for (const state of states) {\n for (const key of Object.keys(state as object)) {\n if (seen.has(key)) {\n console.warn(`[vjs-store] combine(): duplicate state key \"${key}\" — later slice overwrites earlier one`);\n }\n seen.add(key);\n }\n }\n }\n\n return Object.assign({}, ...states) as UnionSliceState<Slices>;\n },\n\n attach: (ctx: AttachContext<Target, UnionSliceState<Slices>>) => {\n for (const slice of slices) {\n try {\n slice.attach?.(ctx as AttachContext<Target, InferSliceState<typeof slice>>);\n } catch (err) {\n ctx.reportError(err);\n }\n }\n },\n };\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,QACd,GAAG,QACqC;CACxC,OAAO;EACL,QAAQ,QAA8B;GACpC,MAAM,SAAS,OAAO,KAAK,UAAU,MAAM,MAAM,GAAG,CAAC;GAExC;IACX,MAAM,uBAAO,IAAI,IAAY;IAC7B,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,OAAO,OAAO,KAAK,KAAe,GAAG;KAC9C,IAAI,KAAK,IAAI,GAAG,GACd,QAAQ,KAAK,+CAA+C,IAAI,uCAAuC;KAEzG,KAAK,IAAI,GAAG;IACd;GAEJ;GAEA,OAAO,OAAO,OAAO,CAAC,GAAG,GAAG,MAAM;EACpC;EAEA,SAAS,QAAwD;GAC/D,KAAK,MAAM,SAAS,QAClB,IAAI;IACF,MAAM,SAAS,GAA2D;GAC5E,SAAS,KAAK;IACZ,IAAI,YAAY,GAAG;GACrB;EAEJ;CACF;AACF"}
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
import { Store } from "./store.js";
|
||||
//#region src/core/config.d.ts
|
||||
interface StoreCallbacks<Target, State> {
|
||||
onSetup?: (ctx: StoreSetupContext<Target, State>) => void;
|
||||
onAttach?: (ctx: StoreAttachContext<Target, State>) => void;
|
||||
onError?: (ctx: StoreErrorContext<Target, State>) => void;
|
||||
}
|
||||
interface StoreSetupContext<Target, State> {
|
||||
store: Store<Target, State>;
|
||||
signal: AbortSignal;
|
||||
}
|
||||
interface StoreAttachContext<Target, State> {
|
||||
store: Store<Target, State>;
|
||||
target: Target;
|
||||
signal: AbortSignal;
|
||||
}
|
||||
interface StoreErrorContext<Target, State> {
|
||||
store: Store<Target, State>;
|
||||
error: unknown;
|
||||
}
|
||||
//#endregion
|
||||
export { StoreAttachContext, StoreCallbacks, StoreErrorContext, StoreSetupContext };
|
||||
//# sourceMappingURL=config.d.ts.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"config.d.ts","names":[],"sources":["../../../src/core/config.ts"],"mappings":";;UAEiB,eAAe,QAAQ;EACtC,WAAW,KAAK,kBAAkB,QAAQ;EAC1C,YAAY,KAAK,mBAAmB,QAAQ;EAC5C,WAAW,KAAK,kBAAkB,QAAQ;;UAG3B,kBAAkB,QAAQ;EACzC,OAAO,MAAM,QAAQ;EACrB,QAAQ;;UAGO,mBAAmB,QAAQ;EAC1C,OAAO,MAAM,QAAQ;EACrB,QAAQ;EACR,QAAQ;;UAGO,kBAAkB,QAAQ;EACzC,OAAO,MAAM,QAAQ;EACrB"}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
//#region src/core/errors.d.ts
|
||||
type StoreErrorCode =
|
||||
/** Store was destroyed. */
|
||||
'DESTROYED' |
|
||||
/** No target is attached to the store. */
|
||||
'NO_TARGET';
|
||||
interface StoreErrorOptions {
|
||||
cause?: unknown;
|
||||
message?: string;
|
||||
}
|
||||
declare class StoreError extends Error {
|
||||
readonly code: StoreErrorCode;
|
||||
cause?: unknown;
|
||||
constructor(code: StoreErrorCode, options?: StoreErrorOptions);
|
||||
}
|
||||
declare function isStoreError(error: unknown): error is StoreError;
|
||||
declare function throwNoTargetError(): never;
|
||||
declare function throwDestroyedError(): never;
|
||||
//#endregion
|
||||
export { StoreError, StoreErrorCode, StoreErrorOptions, isStoreError, throwDestroyedError, throwNoTargetError };
|
||||
//# sourceMappingURL=errors.d.ts.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../../src/core/errors.ts"],"mappings":";KAAY;;;;;UAMK;EACf;EACA;;cAGW,mBAAmB;WACrB,MAAM;EACf;EAEA,YAAY,MAAM,gBAAgB,UAAU;;iBAQ9B,aAAa,iBAAiB,SAAS;iBAIvC;iBAIA"}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
//#region src/core/errors.ts
|
||||
var StoreError = class extends Error {
|
||||
code;
|
||||
cause;
|
||||
constructor(code, options) {
|
||||
super(options?.message ?? code);
|
||||
this.name = "StoreError";
|
||||
this.code = code;
|
||||
this.cause = options?.cause;
|
||||
}
|
||||
};
|
||||
function isStoreError(error) {
|
||||
return error instanceof StoreError;
|
||||
}
|
||||
function throwNoTargetError() {
|
||||
throw new StoreError("NO_TARGET");
|
||||
}
|
||||
function throwDestroyedError() {
|
||||
throw new StoreError("DESTROYED");
|
||||
}
|
||||
//#endregion
|
||||
export { StoreError, isStoreError, throwDestroyedError, throwNoTargetError };
|
||||
|
||||
//# sourceMappingURL=errors.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"errors.js","names":[],"sources":["../../../src/core/errors.ts"],"sourcesContent":["export type StoreErrorCode =\n /** Store was destroyed. */\n | 'DESTROYED'\n /** No target is attached to the store. */\n | 'NO_TARGET';\n\nexport interface StoreErrorOptions {\n cause?: unknown;\n message?: string;\n}\n\nexport class StoreError extends Error {\n readonly code: StoreErrorCode;\n cause?: unknown;\n\n constructor(code: StoreErrorCode, options?: StoreErrorOptions) {\n super(options?.message ?? code);\n this.name = 'StoreError';\n this.code = code;\n this.cause = options?.cause;\n }\n}\n\nexport function isStoreError(error: unknown): error is StoreError {\n return error instanceof StoreError;\n}\n\nexport function throwNoTargetError(): never {\n throw new StoreError('NO_TARGET');\n}\n\nexport function throwDestroyedError(): never {\n throw new StoreError('DESTROYED');\n}\n"],"mappings":";AAWA,IAAa,aAAb,cAAgC,MAAM;CACpC;CACA;CAEA,YAAY,MAAsB,SAA6B;EAC7D,MAAM,SAAS,WAAW,IAAI;EAC9B,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,QAAQ,SAAS;CACxB;AACF;AAEA,SAAgB,aAAa,OAAqC;CAChE,OAAO,iBAAiB;AAC1B;AAEA,SAAgB,qBAA4B;CAC1C,MAAM,IAAI,WAAW,WAAW;AAClC;AAEA,SAAgB,sBAA6B;CAC3C,MAAM,IAAI,WAAW,WAAW;AAClC"}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
import { AnySlice, InferSliceState } from "./slice.js";
|
||||
import { Selector } from "./shallow-equal.js";
|
||||
//#region src/core/selector.d.ts
|
||||
/**
|
||||
* Create a type-safe selector for a slice's state.
|
||||
*
|
||||
* The selector returns the slice's state, or `undefined` if the slice
|
||||
* is not configured in the store.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const selectPlayback = createSelector(playbackSlice);
|
||||
* selectPlayback(store.state); // { paused, play, pause, ... } | undefined
|
||||
* selectPlayback.displayName; // 'playback' (from slice name)
|
||||
* ```
|
||||
*
|
||||
* @param slice - The slice to create a selector for.
|
||||
*/
|
||||
declare function createSelector<S extends AnySlice>(slice: S): Selector<object, InferSliceState<S> | undefined>;
|
||||
//#endregion
|
||||
export { createSelector };
|
||||
//# sourceMappingURL=selector.d.ts.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"selector.d.ts","names":[],"sources":["../../../src/core/selector.ts"],"mappings":";;;;;;;;;;;;;;;;;;iBA4BgB,eAAe,UAAU,UAAU,OAAO,IAAI,iBAAiB,gBAAgB"}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
import { AbortControllerRegistry } from "./abort-controller-registry.js";
|
||||
import { throwNoTargetError } from "./errors.js";
|
||||
import { pick } from "@videojs/utils/object";
|
||||
//#region src/core/selector.ts
|
||||
const stateContext = {
|
||||
target: throwNoTargetError,
|
||||
signals: new AbortControllerRegistry(),
|
||||
get: throwNoTargetError,
|
||||
set: throwNoTargetError
|
||||
};
|
||||
/**
|
||||
* Create a type-safe selector for a slice's state.
|
||||
*
|
||||
* The selector returns the slice's state, or `undefined` if the slice
|
||||
* is not configured in the store.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const selectPlayback = createSelector(playbackSlice);
|
||||
* selectPlayback(store.state); // { paused, play, pause, ... } | undefined
|
||||
* selectPlayback.displayName; // 'playback' (from slice name)
|
||||
* ```
|
||||
*
|
||||
* @param slice - The slice to create a selector for.
|
||||
*/
|
||||
function createSelector(slice) {
|
||||
const initialState = slice.state(stateContext);
|
||||
const keys = Object.keys(initialState);
|
||||
const firstKey = keys[0];
|
||||
if (!firstKey) return Object.assign(() => void 0, { displayName: slice.name });
|
||||
return Object.assign((state) => {
|
||||
if (!(firstKey in state)) return void 0;
|
||||
return pick(state, keys);
|
||||
}, { displayName: slice.name });
|
||||
}
|
||||
//#endregion
|
||||
export { createSelector };
|
||||
|
||||
//# sourceMappingURL=selector.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"selector.js","names":[],"sources":["../../../src/core/selector.ts"],"sourcesContent":["import { pick } from '@videojs/utils/object';\nimport { AbortControllerRegistry } from './abort-controller-registry';\nimport { throwNoTargetError } from './errors';\nimport type { Selector } from './shallow-equal';\nimport type { AnySlice, InferSliceState, StateContext } from './slice';\n\nconst stateContext: StateContext<unknown> = {\n target: throwNoTargetError,\n signals: new AbortControllerRegistry(),\n get: throwNoTargetError,\n set: throwNoTargetError,\n};\n\n/**\n * Create a type-safe selector for a slice's state.\n *\n * The selector returns the slice's state, or `undefined` if the slice\n * is not configured in the store.\n *\n * @example\n * ```ts\n * const selectPlayback = createSelector(playbackSlice);\n * selectPlayback(store.state); // { paused, play, pause, ... } | undefined\n * selectPlayback.displayName; // 'playback' (from slice name)\n * ```\n *\n * @param slice - The slice to create a selector for.\n */\nexport function createSelector<S extends AnySlice>(slice: S): Selector<object, InferSliceState<S> | undefined> {\n const initialState = slice.state(stateContext);\n const keys = Object.keys(initialState as object);\n\n const firstKey = keys[0];\n\n if (!firstKey) {\n return Object.assign(() => undefined, { displayName: slice.name });\n }\n\n return Object.assign(\n (state: object) => {\n // WARN: Could be the source of a bug if two slices have overlapping state keys\n if (!(firstKey in state)) return undefined;\n return pick(state as Record<string, unknown>, keys) as InferSliceState<S>;\n },\n { displayName: slice.name }\n );\n}\n"],"mappings":";;;;AAMA,MAAM,eAAsC;CAC1C,QAAQ;CACR,SAAS,IAAI,wBAAwB;CACrC,KAAK;CACL,KAAK;AACP;;;;;;;;;;;;;;;;AAiBA,SAAgB,eAAmC,OAA4D;CAC7G,MAAM,eAAe,MAAM,MAAM,YAAY;CAC7C,MAAM,OAAO,OAAO,KAAK,YAAsB;CAE/C,MAAM,WAAW,KAAK;CAEtB,IAAI,CAAC,UACH,OAAO,OAAO,aAAa,KAAA,GAAW,EAAE,aAAa,MAAM,KAAK,CAAC;CAGnE,OAAO,OAAO,QACX,UAAkB;EAEjB,IAAI,EAAE,YAAY,QAAQ,OAAO,KAAA;EACjC,OAAO,KAAK,OAAkC,IAAI;CACpD,GACA,EAAE,aAAa,MAAM,KAAK,CAC5B;AACF"}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import { shallowEqual } from "@videojs/utils/object";
|
||||
//#region src/core/shallow-equal.d.ts
|
||||
interface Selector<State, Result> {
|
||||
(state: State): Result;
|
||||
displayName?: string | undefined;
|
||||
}
|
||||
type Comparator<T> = (a: T, b: T) => boolean;
|
||||
//#endregion
|
||||
export { Comparator, Selector, shallowEqual };
|
||||
//# sourceMappingURL=shallow-equal.d.ts.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"shallow-equal.d.ts","names":[],"sources":["../../../src/core/shallow-equal.ts"],"mappings":";;UAEiB,SAAS,OAAO;GAC9B,OAAO,QAAQ;EAChB;;KAGU,WAAW,MAAM,GAAG,GAAG,GAAG"}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import { shallowEqual } from "@videojs/utils/object";
|
||||
export { shallowEqual };
|
||||
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
import { AbortControllerRegistry } from "./abort-controller-registry.js";
|
||||
import { UnknownState } from "./state.js";
|
||||
import { Simplify, UnionToIntersection } from "@videojs/utils/types";
|
||||
//#region src/core/slice.d.ts
|
||||
type Attach<Target, State> = (ctx: AttachContext<Target, State>) => void;
|
||||
interface AttachStore {
|
||||
readonly state: UnknownState;
|
||||
subscribe: (callback: () => void) => () => void;
|
||||
}
|
||||
interface AttachContext<Target, State> {
|
||||
target: Target;
|
||||
signal: AbortSignal;
|
||||
store: AttachStore;
|
||||
get: () => Readonly<State>;
|
||||
set: (partial: Partial<State>) => void;
|
||||
reportError: (error: unknown) => void;
|
||||
}
|
||||
interface StateContext<Target> {
|
||||
/** Returns the current target. Throws if not attached. */
|
||||
target: () => Target;
|
||||
/**
|
||||
* Cancellation signals for async operations.
|
||||
*
|
||||
* - `signals.base` — Aborts on detach or reattach. Use for cleanup.
|
||||
* - `signals.supersede(key)` — Returns a signal that aborts when the same key
|
||||
* is superseded or when base aborts. Use for operations that should cancel
|
||||
* previous in-flight work (e.g., seek superseding seek).
|
||||
* - `signals.clear()` — Aborts all keyed signals. Use when starting fresh
|
||||
* (e.g., loading a new source cancels pending seeks).
|
||||
*/
|
||||
signals: AbortControllerRegistry;
|
||||
/** Read current slice state. Safe to use inside action closures (not during `state()` init). */
|
||||
get: () => Readonly<Record<string, unknown>>;
|
||||
/** Patch the slice state. Safe to use inside action closures (not during `state()` init). */
|
||||
set: (partial: Record<string, unknown>) => void;
|
||||
}
|
||||
interface SliceConfig<Target, State> {
|
||||
/** Debug label. Used as `displayName` on selectors created from this slice. */
|
||||
name?: string;
|
||||
state: (ctx: StateContext<Target>) => State;
|
||||
attach?: (ctx: AttachContext<Target, State>) => void;
|
||||
}
|
||||
type Slice<Target, State> = SliceConfig<Target, State>;
|
||||
type AnySlice<Target = any> = Slice<Target, any>;
|
||||
type SliceFactory<Target> = <State>(config: SliceConfig<Target, State>) => Slice<Target, State>;
|
||||
declare function defineSlice<Target>(): SliceFactory<Target>;
|
||||
type InferSliceTarget<S> = S extends Slice<infer Target, any> ? Target : never;
|
||||
type InferSliceState<S> = S extends Slice<any, infer State> ? State : never;
|
||||
type UnionSliceState<Slices extends AnySlice[]> = Simplify<UnionToIntersection<InferSliceState<Slices[number]>>>;
|
||||
//#endregion
|
||||
export { AnySlice, Attach, AttachContext, AttachStore, InferSliceState, InferSliceTarget, Slice, SliceConfig, SliceFactory, StateContext, UnionSliceState, defineSlice };
|
||||
//# sourceMappingURL=slice.d.ts.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"slice.d.ts","names":[],"sources":["../../../src/core/slice.ts"],"mappings":";;;;KAQY,OAAO,QAAQ,UAAU,KAAK,cAAc,QAAQ;UAE/C;WACN,OAAO;EAChB,YAAY;;UAGG,cAAc,QAAQ;EACrC,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,WAAW,SAAS;EACpB,MAAM,SAAS,QAAQ;EACvB,cAAc;;UAOC,aAAa;;EAE5B,cAAc;;;;;;;;;;;EAWd,SAAS;;EAET,WAAW,SAAS;;EAEpB,MAAM,SAAS;;UAOA,YAAY,QAAQ;;EAEnC;EACA,QAAQ,KAAK,aAAa,YAAY;EACtC,UAAU,KAAK,cAAc,QAAQ;;KAG3B,MAAM,QAAQ,SAAS,YAAY,QAAQ;KAE3C,SAAS,gBAAgB,MAAM;KAM/B,aAAa,WAAW,OAAO,QAAQ,YAAY,QAAQ,WAAW,MAAM,QAAQ;iBAEhF,YAAY,WAAW,aAAa;KAQxC,iBAAiB,KAAK,UAAU,YAAY,eAAe;KAE3D,gBAAgB,KAAK,UAAU,iBAAiB,SAAS;KAEzD,gBAAgB,eAAe,cAAc,SAAS,oBAAoB,gBAAgB"}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
//#region src/core/slice.ts
|
||||
function defineSlice() {
|
||||
return (config) => config;
|
||||
}
|
||||
//#endregion
|
||||
export { defineSlice };
|
||||
|
||||
//# sourceMappingURL=slice.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"slice.js","names":[],"sources":["../../../src/core/slice.ts"],"sourcesContent":["import type { Simplify, UnionToIntersection } from '@videojs/utils/types';\nimport type { AbortControllerRegistry } from './abort-controller-registry';\nimport type { UnknownState } from './state';\n\n// ----------------------------------------\n// Attach\n// ----------------------------------------\n\nexport type Attach<Target, State> = (ctx: AttachContext<Target, State>) => void;\n\nexport interface AttachStore {\n readonly state: UnknownState;\n subscribe: (callback: () => void) => () => void;\n}\n\nexport interface AttachContext<Target, State> {\n target: Target;\n signal: AbortSignal;\n store: AttachStore;\n get: () => Readonly<State>;\n set: (partial: Partial<State>) => void;\n reportError: (error: unknown) => void;\n}\n\n// ----------------------------------------\n// State Context\n// ----------------------------------------\n\nexport interface StateContext<Target> {\n /** Returns the current target. Throws if not attached. */\n target: () => Target;\n /**\n * Cancellation signals for async operations.\n *\n * - `signals.base` — Aborts on detach or reattach. Use for cleanup.\n * - `signals.supersede(key)` — Returns a signal that aborts when the same key\n * is superseded or when base aborts. Use for operations that should cancel\n * previous in-flight work (e.g., seek superseding seek).\n * - `signals.clear()` — Aborts all keyed signals. Use when starting fresh\n * (e.g., loading a new source cancels pending seeks).\n */\n signals: AbortControllerRegistry;\n /** Read current slice state. Safe to use inside action closures (not during `state()` init). */\n get: () => Readonly<Record<string, unknown>>;\n /** Patch the slice state. Safe to use inside action closures (not during `state()` init). */\n set: (partial: Record<string, unknown>) => void;\n}\n\n// ----------------------------------------\n// Slice\n// ----------------------------------------\n\nexport interface SliceConfig<Target, State> {\n /** Debug label. Used as `displayName` on selectors created from this slice. */\n name?: string;\n state: (ctx: StateContext<Target>) => State;\n attach?: (ctx: AttachContext<Target, State>) => void;\n}\n\nexport type Slice<Target, State> = SliceConfig<Target, State>;\n\nexport type AnySlice<Target = any> = Slice<Target, any>;\n\n// ----------------------------------------\n// Factory\n// ----------------------------------------\n\nexport type SliceFactory<Target> = <State>(config: SliceConfig<Target, State>) => Slice<Target, State>;\n\nexport function defineSlice<Target>(): SliceFactory<Target> {\n return (config) => config;\n}\n\n// ----------------------------------------\n// Inference\n// ----------------------------------------\n\nexport type InferSliceTarget<S> = S extends Slice<infer Target, any> ? Target : never;\n\nexport type InferSliceState<S> = S extends Slice<any, infer State> ? State : never;\n\nexport type UnionSliceState<Slices extends AnySlice[]> = Simplify<UnionToIntersection<InferSliceState<Slices[number]>>>;\n"],"mappings":";AAqEA,SAAgB,cAA4C;CAC1D,QAAQ,WAAW;AACrB"}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
//#region src/core/state.d.ts
|
||||
type StateChange = () => void;
|
||||
type UnknownState = Record<string, unknown>;
|
||||
interface SubscribeOptions {
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
interface State<T> {
|
||||
readonly current: Readonly<T>;
|
||||
subscribe(callback: StateChange, options?: SubscribeOptions): () => void;
|
||||
}
|
||||
interface WritableState<T> extends State<T> {
|
||||
patch: (partial: Partial<T>) => void;
|
||||
}
|
||||
declare function flush(): void;
|
||||
declare function createState<T>(initial: T): WritableState<T>;
|
||||
declare function isState(value: unknown): value is State<object>;
|
||||
//#endregion
|
||||
export { State, StateChange, SubscribeOptions, UnknownState, WritableState, createState, flush, isState };
|
||||
//# sourceMappingURL=state.d.ts.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"state.d.ts","names":[],"sources":["../../../src/core/state.ts"],"mappings":";KAEY;KAEA,eAAe;UAEV;EACf,SAAS;;UAGM,MAAM;WACZ,SAAS,SAAS;EAC3B,UAAU,UAAU,aAAa,UAAU;;UAG5B,cAAc,WAAW,MAAM;EAC9C,QAAQ,SAAS,QAAQ;;iBAYX;iBA2EA,YAAY,GAAG,SAAS,IAAI,cAAc;iBAI1C,QAAQ,iBAAiB,SAAS"}
|
||||
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
import { noop } from "@videojs/utils/function";
|
||||
//#region src/core/state.ts
|
||||
let isFlushScheduled = false;
|
||||
function scheduleFlush() {
|
||||
if (isFlushScheduled) return;
|
||||
isFlushScheduled = true;
|
||||
queueMicrotask(flush);
|
||||
}
|
||||
const pendingContainers = /* @__PURE__ */ new Set();
|
||||
function flush() {
|
||||
isFlushScheduled = false;
|
||||
for (const container of pendingContainers) container.flush();
|
||||
pendingContainers.clear();
|
||||
}
|
||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var StateContainer = class {
|
||||
#current;
|
||||
#listeners = /* @__PURE__ */ new Set();
|
||||
#pending = false;
|
||||
constructor(initial) {
|
||||
this.#current = Object.freeze({ ...initial });
|
||||
}
|
||||
get current() {
|
||||
return this.#current;
|
||||
}
|
||||
patch(partial) {
|
||||
const next = { ...this.#current };
|
||||
let changed = false;
|
||||
for (const key in partial) {
|
||||
if (!hasOwnProp.call(partial, key)) continue;
|
||||
const value = partial[key];
|
||||
if (!Object.is(this.#current[key], value)) {
|
||||
next[key] = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
this.#current = Object.freeze(next);
|
||||
this.#markPending();
|
||||
}
|
||||
}
|
||||
subscribe(callback, options) {
|
||||
const signal = options?.signal;
|
||||
if (signal?.aborted) return noop;
|
||||
this.#listeners.add(callback);
|
||||
if (!signal) return () => this.#listeners.delete(callback);
|
||||
const onAbort = () => this.#listeners.delete(callback);
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
return () => {
|
||||
signal.removeEventListener("abort", onAbort);
|
||||
this.#listeners.delete(callback);
|
||||
};
|
||||
}
|
||||
flush() {
|
||||
if (!this.#pending) return;
|
||||
this.#pending = false;
|
||||
for (const fn of this.#listeners) fn();
|
||||
}
|
||||
#markPending() {
|
||||
this.#pending = true;
|
||||
pendingContainers.add(this);
|
||||
scheduleFlush();
|
||||
}
|
||||
};
|
||||
function createState(initial) {
|
||||
return new StateContainer(initial);
|
||||
}
|
||||
function isState(value) {
|
||||
return value instanceof StateContainer;
|
||||
}
|
||||
//#endregion
|
||||
export { createState, flush, isState };
|
||||
|
||||
//# sourceMappingURL=state.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"state.js","names":["#current","#markPending","#listeners","#pending"],"sources":["../../../src/core/state.ts"],"sourcesContent":["import { noop } from '@videojs/utils/function';\n\nexport type StateChange = () => void;\n\nexport type UnknownState = Record<string, unknown>;\n\nexport interface SubscribeOptions {\n signal?: AbortSignal;\n}\n\nexport interface State<T> {\n readonly current: Readonly<T>;\n subscribe(callback: StateChange, options?: SubscribeOptions): () => void;\n}\n\nexport interface WritableState<T> extends State<T> {\n patch: (partial: Partial<T>) => void;\n}\n\nlet isFlushScheduled = false;\nfunction scheduleFlush(): void {\n if (isFlushScheduled) return;\n isFlushScheduled = true;\n queueMicrotask(flush);\n}\n\nconst pendingContainers = new Set<StateContainer<any>>();\n\nexport function flush(): void {\n isFlushScheduled = false;\n for (const container of pendingContainers) container.flush();\n pendingContainers.clear();\n}\n\nconst hasOwnProp = Object.prototype.hasOwnProperty;\n\nclass StateContainer<T> implements WritableState<T> {\n #current: T;\n #listeners = new Set<StateChange>();\n #pending = false;\n\n constructor(initial: T) {\n this.#current = Object.freeze({ ...initial });\n }\n\n get current(): Readonly<T> {\n return this.#current;\n }\n\n patch(partial: Partial<T>): void {\n const next = { ...this.#current };\n\n let changed = false;\n\n for (const key in partial) {\n if (!hasOwnProp.call(partial, key)) continue;\n\n const value = partial[key];\n\n if (!Object.is(this.#current[key], value)) {\n next[key] = value!;\n changed = true;\n }\n }\n\n if (changed) {\n this.#current = Object.freeze(next);\n this.#markPending();\n }\n }\n\n subscribe(callback: StateChange, options?: SubscribeOptions): () => void {\n const signal = options?.signal;\n if (signal?.aborted) return noop;\n\n this.#listeners.add(callback);\n\n if (!signal) {\n return () => this.#listeners.delete(callback);\n }\n\n const onAbort = () => this.#listeners.delete(callback);\n signal.addEventListener('abort', onAbort, { once: true });\n\n return () => {\n signal.removeEventListener('abort', onAbort);\n this.#listeners.delete(callback);\n };\n }\n\n flush(): void {\n if (!this.#pending) return;\n this.#pending = false;\n for (const fn of this.#listeners) fn();\n }\n\n #markPending(): void {\n this.#pending = true;\n pendingContainers.add(this);\n scheduleFlush();\n }\n}\n\nexport function createState<T>(initial: T): WritableState<T> {\n return new StateContainer(initial);\n}\n\nexport function isState(value: unknown): value is State<object> {\n return value instanceof StateContainer;\n}\n"],"mappings":";;AAmBA,IAAI,mBAAmB;AACvB,SAAS,gBAAsB;CAC7B,IAAI,kBAAkB;CACtB,mBAAmB;CACnB,eAAe,KAAK;AACtB;AAEA,MAAM,oCAAoB,IAAI,IAAyB;AAEvD,SAAgB,QAAc;CAC5B,mBAAmB;CACnB,KAAK,MAAM,aAAa,mBAAmB,UAAU,MAAM;CAC3D,kBAAkB,MAAM;AAC1B;AAEA,MAAM,aAAa,OAAO,UAAU;AAEpC,IAAM,iBAAN,MAAoD;CAClD;CACA,6BAAa,IAAI,IAAiB;CAClC,WAAW;CAEX,YAAY,SAAY;EACtB,KAAKA,WAAW,OAAO,OAAO,EAAE,GAAG,QAAQ,CAAC;CAC9C;CAEA,IAAI,UAAuB;EACzB,OAAO,KAAKA;CACd;CAEA,MAAM,SAA2B;EAC/B,MAAM,OAAO,EAAE,GAAG,KAAKA,SAAS;EAEhC,IAAI,UAAU;EAEd,KAAK,MAAM,OAAO,SAAS;GACzB,IAAI,CAAC,WAAW,KAAK,SAAS,GAAG,GAAG;GAEpC,MAAM,QAAQ,QAAQ;GAEtB,IAAI,CAAC,OAAO,GAAG,KAAKA,SAAS,MAAM,KAAK,GAAG;IACzC,KAAK,OAAO;IACZ,UAAU;GACZ;EACF;EAEA,IAAI,SAAS;GACX,KAAKA,WAAW,OAAO,OAAO,IAAI;GAClC,KAAKC,aAAa;EACpB;CACF;CAEA,UAAU,UAAuB,SAAwC;EACvE,MAAM,SAAS,SAAS;EACxB,IAAI,QAAQ,SAAS,OAAO;EAE5B,KAAKC,WAAW,IAAI,QAAQ;EAE5B,IAAI,CAAC,QACH,aAAa,KAAKA,WAAW,OAAO,QAAQ;EAG9C,MAAM,gBAAgB,KAAKA,WAAW,OAAO,QAAQ;EACrD,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EAExD,aAAa;GACX,OAAO,oBAAoB,SAAS,OAAO;GAC3C,KAAKA,WAAW,OAAO,QAAQ;EACjC;CACF;CAEA,QAAc;EACZ,IAAI,CAAC,KAAKC,UAAU;EACpB,KAAKA,WAAW;EAChB,KAAK,MAAM,MAAM,KAAKD,YAAY,GAAG;CACvC;CAEA,eAAqB;EACnB,KAAKC,WAAW;EAChB,kBAAkB,IAAI,IAAI;EAC1B,cAAc;CAChB;AACF;AAEA,SAAgB,YAAe,SAA8B;CAC3D,OAAO,IAAI,eAAe,OAAO;AACnC;AAEA,SAAgB,QAAQ,OAAwC;CAC9D,OAAO,iBAAiB;AAC1B"}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
import { State, StateChange, SubscribeOptions, UnknownState } from "./state.js";
|
||||
import { Slice } from "./slice.js";
|
||||
import { StoreCallbacks } from "./config.js";
|
||||
//#region src/core/store.d.ts
|
||||
interface StoreOptions<Target, State> extends StoreCallbacks<Target, State> {}
|
||||
declare function createStore<Target = unknown>(): <State>(slice: Slice<Target, State>, options?: StoreOptions<Target, State>) => Store<Target, State>;
|
||||
declare function isStore(value: unknown): value is AnyStore;
|
||||
interface BaseStore<Target = unknown, State$1 = UnknownState> {
|
||||
[key: string]: unknown;
|
||||
readonly $state: State<State$1>;
|
||||
readonly target: Target | null;
|
||||
readonly destroyed: boolean;
|
||||
readonly state: State$1;
|
||||
attach(target: Target): () => void;
|
||||
destroy(): void;
|
||||
subscribe(callback: StateChange, options?: SubscribeOptions): () => void;
|
||||
}
|
||||
type Store<Target = unknown, State = UnknownState> = BaseStore<Target, State> & State;
|
||||
type AnyStore<Target = any> = BaseStore<Target, object>;
|
||||
type UnknownStore<Target = unknown> = Store<Target, UnknownState>;
|
||||
type InferStoreTarget<S extends AnyStore> = S extends Store<infer T, any> ? T : never;
|
||||
type InferStoreState<S extends AnyStore> = S extends Store<any, infer State> ? State : never;
|
||||
//#endregion
|
||||
export { AnyStore, BaseStore, InferStoreState, InferStoreTarget, Store, StoreOptions, UnknownStore, createStore, isStore };
|
||||
//# sourceMappingURL=store.d.ts.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"store.d.ts","names":[],"sources":["../../../src/core/store.ts"],"mappings":";;;;UAUiB,aAAa,QAAQ,eAAe,eAAe,QAAQ;iBAE5D,YAAY,sBAAsB,OAChD,OAAO,MAAM,QAAQ,QACrB,UAAU,aAAa,QAAQ,WAC5B,MAAM,QAAQ;iBAsIH,QAAQ,iBAAiB,SAAS;UAQjC,UAAU,kBAAkB,UAAQ;GAClD;WACQ,QAAQ,MAAe;WACvB,QAAQ;WACR;WACA,OAAO;EAChB,OAAO,QAAQ;EACf;EACA,UAAU,UAAU,aAAa,UAAU;;KAGjC,MAAM,kBAAkB,QAAQ,gBAAgB,UAAU,QAAQ,SAAS;KAE3E,SAAS,gBAAgB,UAAU;KAEnC,aAAa,oBAAoB,MAAM,QAAQ;KAE/C,iBAAiB,UAAU,YAAY,UAAU,YAAY,UAAU;KAEvE,gBAAgB,UAAU,YAAY,UAAU,iBAAiB,SAAS"}
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
import { AbortControllerRegistry } from "./abort-controller-registry.js";
|
||||
import { throwDestroyedError, throwNoTargetError } from "./errors.js";
|
||||
import { createState } from "./state.js";
|
||||
import { isNull, isObject } from "@videojs/utils/predicate";
|
||||
//#region src/core/store.ts
|
||||
const STORE_SYMBOL = Symbol.for("@videojs/store");
|
||||
function createStore() {
|
||||
return (slice, options = {}) => {
|
||||
let target = null;
|
||||
let destroyed = false;
|
||||
const setupAbort = new AbortController();
|
||||
const signals = new AbortControllerRegistry();
|
||||
let state;
|
||||
function validate() {
|
||||
if (destroyed) throwDestroyedError();
|
||||
if (!target) throwNoTargetError();
|
||||
}
|
||||
const initialState = slice.state({
|
||||
target: () => {
|
||||
validate();
|
||||
return target;
|
||||
},
|
||||
signals,
|
||||
get: () => state.current,
|
||||
set: (partial) => state.patch(partial)
|
||||
});
|
||||
state = createState(initialState);
|
||||
const store = {
|
||||
[STORE_SYMBOL]: true,
|
||||
get $state() {
|
||||
return state;
|
||||
},
|
||||
get target() {
|
||||
return target;
|
||||
},
|
||||
get destroyed() {
|
||||
return destroyed;
|
||||
},
|
||||
get state() {
|
||||
return state.current;
|
||||
},
|
||||
attach,
|
||||
destroy,
|
||||
subscribe
|
||||
};
|
||||
for (const key of Object.keys(initialState)) Object.defineProperty(store, key, {
|
||||
get: () => state.current[key],
|
||||
enumerable: true
|
||||
});
|
||||
try {
|
||||
options.onSetup?.({
|
||||
store,
|
||||
signal: setupAbort.signal
|
||||
});
|
||||
} catch (error) {
|
||||
reportError(error);
|
||||
}
|
||||
return store;
|
||||
function attach(newTarget) {
|
||||
if (destroyed) throwDestroyedError();
|
||||
signals.reset();
|
||||
target = newTarget;
|
||||
const attachContext = {
|
||||
target: newTarget,
|
||||
signal: signals.base,
|
||||
get: () => state.current,
|
||||
set: (partial) => state.patch(partial),
|
||||
reportError,
|
||||
store: {
|
||||
get state() {
|
||||
return state.current;
|
||||
},
|
||||
subscribe
|
||||
}
|
||||
};
|
||||
try {
|
||||
slice.attach?.(attachContext);
|
||||
} catch (error) {
|
||||
reportError(error);
|
||||
}
|
||||
try {
|
||||
options.onAttach?.({
|
||||
store,
|
||||
target: newTarget,
|
||||
signal: signals.base
|
||||
});
|
||||
} catch (error) {
|
||||
reportError(error);
|
||||
}
|
||||
return detach;
|
||||
}
|
||||
function detach() {
|
||||
if (isNull(target)) return;
|
||||
signals.reset();
|
||||
target = null;
|
||||
state.patch(initialState);
|
||||
}
|
||||
function destroy() {
|
||||
if (destroyed) return;
|
||||
destroyed = true;
|
||||
detach();
|
||||
setupAbort.abort();
|
||||
}
|
||||
function subscribe(callback, options) {
|
||||
return state.subscribe(callback, options);
|
||||
}
|
||||
function reportError(error) {
|
||||
if (options.onError) options.onError({
|
||||
store,
|
||||
error
|
||||
});
|
||||
else console.error("[vjs-store]", error);
|
||||
}
|
||||
};
|
||||
}
|
||||
function isStore(value) {
|
||||
return isObject(value) && STORE_SYMBOL in value;
|
||||
}
|
||||
//#endregion
|
||||
export { createStore, isStore };
|
||||
|
||||
//# sourceMappingURL=store.js.map
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user