@hvakr/firestate 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2160,6 +2160,37 @@ const shallow = (a, b) => {
2160
2160
 
2161
2161
  //#endregion
2162
2162
  //#region src/utils/undo.ts
2163
+ const getGroupedActions = (action) => action.actions ?? [action];
2164
+ /**
2165
+ * Apply a group in the required direction. If a member fails, compensate the
2166
+ * members already applied so a failed undo/redo does not leave the group
2167
+ * partially applied (provided the compensating actions themselves succeed).
2168
+ */
2169
+ const applyGroup = async (actions, operation) => {
2170
+ const ordered = operation === "undo" ? [...actions].reverse() : actions;
2171
+ const applied = [];
2172
+ try {
2173
+ for (const action of ordered) {
2174
+ await action[operation]();
2175
+ applied.push(action);
2176
+ }
2177
+ } catch (error) {
2178
+ const compensate = operation === "undo" ? "redo" : "undo";
2179
+ for (const action of applied.reverse()) await action[compensate]();
2180
+ throw error;
2181
+ }
2182
+ };
2183
+ const mergeGroupedActions = (older, newer) => {
2184
+ const actions = [...getGroupedActions(older), newer];
2185
+ return {
2186
+ undo: () => applyGroup(actions, "undo"),
2187
+ redo: () => applyGroup(actions, "redo"),
2188
+ groupId: newer.groupId,
2189
+ path: newer.path ?? older.path,
2190
+ description: newer.description ?? older.description,
2191
+ actions
2192
+ };
2193
+ };
2163
2194
  /**
2164
2195
  * Create an undo manager instance.
2165
2196
  * This is a standalone, framework-agnostic implementation.
@@ -2179,7 +2210,7 @@ const shallow = (a, b) => {
2179
2210
  * ```
2180
2211
  */
2181
2212
  const createUndoManager = (config = {}) => {
2182
- const { maxLength = 20, onNavigate } = config;
2213
+ const { maxLength = 20, onNavigate, onUndo, onRedo, onError } = config;
2183
2214
  let undoStack = [];
2184
2215
  let redoStack = [];
2185
2216
  const subscribers = /* @__PURE__ */ new Set();
@@ -2203,19 +2234,7 @@ const createUndoManager = (config = {}) => {
2203
2234
  const last = undoStack[undoStack.length - 1];
2204
2235
  if (last?.groupId === action.groupId) {
2205
2236
  undoStack.pop();
2206
- undoStack.push({
2207
- undo: async () => {
2208
- await action.undo();
2209
- await last.undo();
2210
- },
2211
- redo: async () => {
2212
- await last.redo();
2213
- await action.redo();
2214
- },
2215
- groupId: action.groupId,
2216
- path: action.path ?? last.path,
2217
- description: action.description ?? last.description
2218
- });
2237
+ undoStack.push(mergeGroupedActions(last, action));
2219
2238
  redoStack = [];
2220
2239
  notify();
2221
2240
  return;
@@ -2229,31 +2248,35 @@ const createUndoManager = (config = {}) => {
2229
2248
  const undo = async () => {
2230
2249
  const action = undoStack.pop();
2231
2250
  if (!action) return;
2232
- if (action.path && onNavigate) onNavigate(action.path);
2233
2251
  try {
2252
+ if (action.path && onNavigate) onNavigate(action.path);
2234
2253
  await action.undo();
2235
- redoStack.push(action);
2236
2254
  } catch (error) {
2237
2255
  undoStack.push(action);
2238
- console.error("Undo failed:", error);
2256
+ if (onError) onError(error, action, "undo");
2257
+ else console.error("Undo failed:", error);
2239
2258
  throw error;
2240
2259
  }
2260
+ redoStack.push(action);
2241
2261
  notify();
2262
+ onUndo?.(action);
2242
2263
  };
2243
2264
  const redo = async () => {
2244
2265
  const action = redoStack.pop();
2245
2266
  if (!action) return;
2246
- if (action.path && onNavigate) onNavigate(action.path);
2247
2267
  try {
2268
+ if (action.path && onNavigate) onNavigate(action.path);
2248
2269
  await action.redo();
2249
- undoStack.push(action);
2250
- if (undoStack.length > maxLength) undoStack.shift();
2251
2270
  } catch (error) {
2252
2271
  redoStack.push(action);
2253
- console.error("Redo failed:", error);
2272
+ if (onError) onError(error, action, "redo");
2273
+ else console.error("Redo failed:", error);
2254
2274
  throw error;
2255
2275
  }
2276
+ undoStack.push(action);
2277
+ if (undoStack.length > maxLength) undoStack.shift();
2256
2278
  notify();
2279
+ onRedo?.(action);
2257
2280
  };
2258
2281
  const clear = () => {
2259
2282
  undoStack = [];
@@ -2311,9 +2334,22 @@ const createStore = (config) => {
2311
2334
  const { firestore, autosave = 1e3, minLoadTime = 0, maxUndoLength = 20 } = config;
2312
2335
  let onError = config.onError;
2313
2336
  let onNavigate = config.onNavigate;
2337
+ let onUndo = config.onUndo;
2338
+ let onRedo = config.onRedo;
2314
2339
  const undoManager = createUndoManager({
2315
2340
  maxLength: maxUndoLength,
2316
- onNavigate: (path) => onNavigate?.(path)
2341
+ onNavigate: (path) => onNavigate?.(path),
2342
+ onUndo: (action) => onUndo?.(action),
2343
+ onRedo: (action) => onRedo?.(action),
2344
+ onError: (error, action, operation) => {
2345
+ const context = {
2346
+ type: "undo",
2347
+ path: action.path ?? "undo",
2348
+ operation
2349
+ };
2350
+ if (onError) onError(error, context);
2351
+ else console.error(`Firestate error in ${context.type} ${context.path} during ${context.operation}:`, error);
2352
+ }
2317
2353
  });
2318
2354
  const syncStates = /* @__PURE__ */ new Map();
2319
2355
  const syncSubscribers = /* @__PURE__ */ new Set();
@@ -2340,6 +2376,12 @@ const createStore = (config) => {
2340
2376
  setOnNavigate: (handler) => {
2341
2377
  onNavigate = handler;
2342
2378
  },
2379
+ setOnUndo: (handler) => {
2380
+ onUndo = handler;
2381
+ },
2382
+ setOnRedo: (handler) => {
2383
+ onRedo = handler;
2384
+ },
2343
2385
  subscribeToSyncState: (fn) => {
2344
2386
  syncSubscribers.add(fn);
2345
2387
  return () => syncSubscribers.delete(fn);
@@ -2386,14 +2428,16 @@ const createStore = (config) => {
2386
2428
  * }
2387
2429
  * ```
2388
2430
  */
2389
- const FirestateProvider = ({ firestore, autosave = 1e3, minLoadTime = 0, maxUndoLength = 20, onError, onNavigate, children }) => {
2431
+ const FirestateProvider = ({ firestore, autosave = 1e3, minLoadTime = 0, maxUndoLength = 20, onError, onNavigate, onUndo, onRedo, children }) => {
2390
2432
  const store = useMemo(() => createStore({
2391
2433
  firestore,
2392
2434
  autosave,
2393
2435
  minLoadTime,
2394
2436
  maxUndoLength,
2395
2437
  onError,
2396
- onNavigate
2438
+ onNavigate,
2439
+ onUndo,
2440
+ onRedo
2397
2441
  }), [
2398
2442
  firestore,
2399
2443
  autosave,
@@ -2406,6 +2450,12 @@ const FirestateProvider = ({ firestore, autosave = 1e3, minLoadTime = 0, maxUndo
2406
2450
  useEffect(() => {
2407
2451
  store.setOnNavigate(onNavigate);
2408
2452
  }, [store, onNavigate]);
2453
+ useEffect(() => {
2454
+ store.setOnUndo(onUndo);
2455
+ }, [store, onUndo]);
2456
+ useEffect(() => {
2457
+ store.setOnRedo(onRedo);
2458
+ }, [store, onRedo]);
2409
2459
  return /* @__PURE__ */ jsx(FirestateContext.Provider, {
2410
2460
  value: store,
2411
2461
  children