fix(core): serialize state mutations, fix batch flushing/reentrancy, handle falsy initial values, dispose old StatePart on force, and improve notification/error handling

This commit is contained in:
2026-02-28 08:52:41 +00:00
parent 2f0b39ae41
commit 9312b8908c
8 changed files with 383 additions and 120 deletions

View File

@@ -14,6 +14,7 @@ export class Smartstate<StatePartNameType extends string> {
// Batch support
private batchDepth = 0;
private isFlushing = false;
private pendingNotifications = new Set<StatePart<any, any>>();
constructor() {}
@@ -41,11 +42,18 @@ export class Smartstate<StatePartNameType extends string> {
await updateFn();
} finally {
this.batchDepth--;
if (this.batchDepth === 0) {
const pending = [...this.pendingNotifications];
this.pendingNotifications.clear();
for (const sp of pending) {
await sp.notifyChange();
if (this.batchDepth === 0 && !this.isFlushing) {
this.isFlushing = true;
try {
while (this.pendingNotifications.size > 0) {
const pending = [...this.pendingNotifications];
this.pendingNotifications.clear();
for (const sp of pending) {
await sp.notifyChange();
}
}
} finally {
this.isFlushing = false;
}
}
}
@@ -84,6 +92,7 @@ export class Smartstate<StatePartNameType extends string> {
`State part '${statePartNameArg}' already exists, but initMode is 'mandatory'`
);
case 'force':
existingStatePart.dispose();
break;
case 'soft':
case 'persistent':
@@ -91,7 +100,7 @@ export class Smartstate<StatePartNameType extends string> {
return existingStatePart as StatePart<StatePartNameType, PayloadType>;
}
} else {
if (!initialArg) {
if (initialArg === undefined) {
throw new Error(
`State part '${statePartNameArg}' does not exist and no initial state provided`
);