CLose story 1.2
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Visibility Matrix contract.
|
||||
*
|
||||
* Canonical shape: { _version: 1, matrix: { [userId: string]: VisibilityState } }
|
||||
* where VisibilityState ∈ VISIBILITY_STATES.
|
||||
*
|
||||
* StateStore is the sole writer of this structure. All other modules treat it as read-only.
|
||||
*
|
||||
* @module contracts/visibility-matrix
|
||||
*/
|
||||
|
||||
/** @typedef {'active'|'hidden'|'self-muted'|'offline'|'cam-lost'|'reconnecting'|'never-connected'|'ghost'} VisibilityState */
|
||||
|
||||
/**
|
||||
* @typedef {Object} VisibilityMatrix
|
||||
* @property {1} _version - Schema version; always 1 for v1.
|
||||
* @property {Object.<string, VisibilityState>} matrix - userId → VisibilityState map.
|
||||
*/
|
||||
|
||||
export const VISIBILITY_STATES = Object.freeze([
|
||||
"active",
|
||||
"hidden",
|
||||
"self-muted",
|
||||
"offline",
|
||||
"cam-lost",
|
||||
"reconnecting",
|
||||
"never-connected",
|
||||
"ghost",
|
||||
]);
|
||||
|
||||
export const VISIBILITY_MATRIX_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Creates a new VisibilityMatrix with an optional initial matrix.
|
||||
* @param {Object.<string, VisibilityState>} [matrix={}] - Initial userId→state entries.
|
||||
* @returns {VisibilityMatrix}
|
||||
*/
|
||||
export function createVisibilityMatrix(matrix = {}) {
|
||||
return { _version: VISIBILITY_MATRIX_VERSION, matrix: { ...matrix } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a VisibilityMatrix DTO. Throws TypeError on any violation.
|
||||
* @param {unknown} data - Value to validate.
|
||||
* @returns {VisibilityMatrix} The validated matrix.
|
||||
* @throws {TypeError} If data fails validation.
|
||||
*/
|
||||
export function isValidVisibilityMatrix(data) {
|
||||
if (data === null || typeof data !== "object") {
|
||||
throw new TypeError("VisibilityMatrix: must be an object");
|
||||
}
|
||||
const obj = /** @type {Record<string, unknown>} */ (data);
|
||||
const { _version, matrix, ...rest } = obj;
|
||||
if (Object.keys(rest).length > 0) {
|
||||
throw new TypeError(`VisibilityMatrix: unknown keys: ${Object.keys(rest).join(", ")}`);
|
||||
}
|
||||
if (_version !== VISIBILITY_MATRIX_VERSION) {
|
||||
throw new TypeError(`VisibilityMatrix: _version must be ${VISIBILITY_MATRIX_VERSION}, got ${_version}`);
|
||||
}
|
||||
if (matrix === null || typeof matrix !== "object" || Array.isArray(matrix)) {
|
||||
throw new TypeError("VisibilityMatrix: matrix must be a plain object");
|
||||
}
|
||||
const matrixObj = /** @type {Record<string, unknown>} */ (matrix);
|
||||
for (const [userId, state] of Object.entries(matrixObj)) {
|
||||
if (typeof userId !== "string" || userId.length === 0) {
|
||||
throw new TypeError(`VisibilityMatrix: userId must be a non-empty string, got "${userId}"`);
|
||||
}
|
||||
if (!VISIBILITY_STATES.includes(/** @type {any} */ (state))) {
|
||||
throw new TypeError(`VisibilityMatrix: invalid state "${state}" for userId "${userId}"`);
|
||||
}
|
||||
}
|
||||
return /** @type {VisibilityMatrix} */ (data);
|
||||
}
|
||||
Reference in New Issue
Block a user