Files
workinf_Blender_Wasm/web/protocol/project-action-mutex.ts
mes123456 7c16b279ae
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance M7 workflows and release operations
2026-08-15 17:43:53 -04:00

80 lines
2.9 KiB
TypeScript

export const PROJECT_ACTION_KINDS = ["OPEN", "SAVE", "CLOSE"] as const;
export type ProjectActionKind = typeof PROJECT_ACTION_KINDS[number];
export interface ProjectActionIdentity {
actionId: string;
kind: ProjectActionKind;
}
export interface ProjectActionMutexState {
owner: ProjectActionIdentity | null;
}
export type ProjectActionConflictReason =
| "REPEATED_OPEN"
| "CONCURRENT_SAVE"
| "CLOSE_DURING_SAVE"
| "PROJECT_ACTION_EXCLUSIVE";
export interface ProjectActionConflict {
code: "USER_ACTION_CONFLICT";
reason: ProjectActionConflictReason;
requested: ProjectActionIdentity;
owner: ProjectActionIdentity;
}
export type ProjectActionAcquireResult =
| { granted: true; state: ProjectActionMutexState; conflict: null }
| { granted: false; state: ProjectActionMutexState; conflict: ProjectActionConflict };
export type ProjectActionReleaseResult =
| { released: true; state: ProjectActionMutexState; errorCode: null }
| {
released: false;
state: ProjectActionMutexState;
errorCode: "PROJECT_ACTION_LOCK_NOT_HELD" | "PROJECT_ACTION_LOCK_IDENTITY_MISMATCH";
};
export const PROJECT_ACTION_CONFLICT_MATRIX: Readonly<Record<ProjectActionKind, Readonly<Record<ProjectActionKind, boolean>>>> = {
OPEN: { OPEN: true, SAVE: true, CLOSE: true },
SAVE: { OPEN: true, SAVE: true, CLOSE: true },
CLOSE: { OPEN: true, SAVE: true, CLOSE: true },
};
export function createProjectActionMutexState(): ProjectActionMutexState {
return { owner: null };
}
function conflictReason(requested: ProjectActionKind, owner: ProjectActionKind): ProjectActionConflictReason {
if (requested === "OPEN" && owner === "OPEN") return "REPEATED_OPEN";
if (requested === "SAVE" && owner === "SAVE") return "CONCURRENT_SAVE";
if (requested === "CLOSE" && owner === "SAVE") return "CLOSE_DURING_SAVE";
return "PROJECT_ACTION_EXCLUSIVE";
}
export function acquireProjectAction(state: ProjectActionMutexState, requested: ProjectActionIdentity): ProjectActionAcquireResult {
const owner = state.owner;
if (!owner) return { granted: true, state: { owner: requested }, conflict: null };
if (!PROJECT_ACTION_CONFLICT_MATRIX[requested.kind][owner.kind]) {
return { granted: true, state: { owner: requested }, conflict: null };
}
return {
granted: false,
state,
conflict: {
code: "USER_ACTION_CONFLICT",
reason: conflictReason(requested.kind, owner.kind),
requested,
owner,
},
};
}
export function releaseProjectAction(state: ProjectActionMutexState, identity: ProjectActionIdentity): ProjectActionReleaseResult {
if (!state.owner) return { released: false, state, errorCode: "PROJECT_ACTION_LOCK_NOT_HELD" };
if (state.owner.kind !== identity.kind || state.owner.actionId !== identity.actionId) {
return { released: false, state, errorCode: "PROJECT_ACTION_LOCK_IDENTITY_MISMATCH" };
}
return { released: true, state: createProjectActionMutexState(), errorCode: null };
}