model: add onSuccess/onFailure lifecycle hooks to modelRegister
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
|
||||
import { reactive } from './reactivity.js';
|
||||
|
||||
/** Registered models: name → { model, subsystem, fetch } */
|
||||
/** Registered models: name → { model, subsystem, fetch, onSuccess?, onFailure? } */
|
||||
const _models = new Map();
|
||||
|
||||
/** In-flight fetch promises for dedup: name → Promise */
|
||||
@@ -29,6 +29,13 @@ const _fetchPromises = new Map();
|
||||
* @param {string} definition.subsystem - WS topic to listen for ('*' = all)
|
||||
* @param {function} definition.fetch - async (signal?, param?) => Promise<data>
|
||||
* @param {any} [definition.defaultData] - Initial data value (default: null)
|
||||
* @param {function} [definition.onSuccess] - (name, data, param?) => void.
|
||||
* Called after model.data is assigned. Also fires when data === null —
|
||||
* only a thrown fetch error skips it. Hook errors are caught and logged
|
||||
* (console.warn) and never affect the fetch promise or model state.
|
||||
* @param {function} [definition.onFailure] - (name, error) => void.
|
||||
* Called after model.error is assigned, only on a real throw from fetch.
|
||||
* Hook errors are caught and logged and never affect the fetch promise.
|
||||
* @returns {object} reactive model
|
||||
*/
|
||||
export function modelRegister(name, definition) {
|
||||
@@ -43,6 +50,8 @@ export function modelRegister(name, definition) {
|
||||
model,
|
||||
subsystem: definition.subsystem,
|
||||
fetch: definition.fetch,
|
||||
onSuccess: definition.onSuccess,
|
||||
onFailure: definition.onFailure,
|
||||
});
|
||||
|
||||
return model;
|
||||
@@ -59,9 +68,13 @@ export function getModel(name) {
|
||||
return entry.model;
|
||||
}
|
||||
|
||||
/** Build dedup key from model name and optional param. */
|
||||
/**
|
||||
* Build dedup key from model name and optional param.
|
||||
* Objects (e.g. `{ action: 'refresh' }`) are JSON-serialized so distinct
|
||||
* param objects get distinct keys; strings/undefined keep bare behavior.
|
||||
*/
|
||||
function _dedupKey(name, param) {
|
||||
return param !== undefined ? `${name}:${String(param)}` : name;
|
||||
return param !== undefined ? name + ':' + JSON.stringify(param) : name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,8 +110,20 @@ export function modelFetch(name, signalOrParam, signal) {
|
||||
try {
|
||||
const data = await entry.fetch(actualSignal, param);
|
||||
model.data = data;
|
||||
// Fires for any resolved result, including data === null. Hook
|
||||
// errors must not clobber model state or the returned promise.
|
||||
try {
|
||||
entry.onSuccess?.(name, data, param);
|
||||
} catch (err) {
|
||||
console.warn('Model onSuccess hook (' + name + ') threw:', err);
|
||||
}
|
||||
} catch (e) {
|
||||
model.error = e.message || 'Fetch failed';
|
||||
try {
|
||||
entry.onFailure?.(name, e);
|
||||
} catch (err) {
|
||||
console.warn('Model onFailure hook (' + name + ') threw:', err);
|
||||
}
|
||||
} finally {
|
||||
model.loading = false;
|
||||
model.refreshing = false;
|
||||
|
||||
Reference in New Issue
Block a user