@signaltree/core 6.0.13 → 6.0.14

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.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { signalTree } from './lib/signal-tree.js';
2
2
  export { ENHANCER_META, entityMap } from './lib/types.js';
3
3
  export { composeEnhancers, createLazySignalTree, isAnySignal, isNodeAccessor, toWritableSignal } from './lib/utils.js';
4
+ export { createEditSession } from './lib/edit-session.js';
4
5
  export { getPathNotifier } from './lib/path-notifier.js';
5
6
  export { SecurityPresets, SecurityValidator } from './lib/security/security-validator.js';
6
7
  export { createEnhancer, resolveEnhancerOrder } from './enhancers/index.js';
@@ -0,0 +1,84 @@
1
+ import { signal } from '@angular/core';
2
+ import { deepEqual } from '../deep-equal.js';
3
+
4
+ function clone(value) {
5
+ try {
6
+ return globalThis.structuredClone ? globalThis.structuredClone(value) : JSON.parse(JSON.stringify(value));
7
+ } catch {
8
+ return JSON.parse(JSON.stringify(value));
9
+ }
10
+ }
11
+ function createEditSession(initial) {
12
+ const original = signal(clone(initial));
13
+ const present = signal(clone(initial));
14
+ const pastCount = signal(0);
15
+ const futureCount = signal(0);
16
+ let past = [];
17
+ let future = [];
18
+ function updateCounts() {
19
+ pastCount.set(past.length);
20
+ futureCount.set(future.length);
21
+ }
22
+ const canUndo = () => pastCount() > 0;
23
+ const canRedo = () => futureCount() > 0;
24
+ const isDirty = () => !deepEqual(original(), present());
25
+ function setOriginal(value) {
26
+ const v = clone(value);
27
+ original.set(v);
28
+ present.set(clone(v));
29
+ past = [];
30
+ future = [];
31
+ updateCounts();
32
+ }
33
+ function applyChanges(valueOrUpdater) {
34
+ const current = present();
35
+ const next = typeof valueOrUpdater === 'function' ? valueOrUpdater(clone(current)) : valueOrUpdater;
36
+ if (deepEqual(current, next)) return;
37
+ past.push(clone(current));
38
+ present.set(clone(next));
39
+ future = [];
40
+ updateCounts();
41
+ }
42
+ function undo() {
43
+ if (past.length === 0) return;
44
+ const prev = past.pop();
45
+ future.push(clone(present()));
46
+ present.set(clone(prev));
47
+ updateCounts();
48
+ }
49
+ function redo() {
50
+ if (future.length === 0) return;
51
+ const next = future.pop();
52
+ past.push(clone(present()));
53
+ present.set(clone(next));
54
+ updateCounts();
55
+ }
56
+ function reset() {
57
+ present.set(clone(original()));
58
+ past = [];
59
+ future = [];
60
+ updateCounts();
61
+ }
62
+ function getHistory() {
63
+ return {
64
+ past: past.map(p => clone(p)),
65
+ present: clone(present()),
66
+ future: future.map(f => clone(f))
67
+ };
68
+ }
69
+ return {
70
+ original,
71
+ modified: present,
72
+ canUndo,
73
+ canRedo,
74
+ isDirty,
75
+ setOriginal,
76
+ applyChanges,
77
+ undo,
78
+ redo,
79
+ reset,
80
+ getHistory
81
+ };
82
+ }
83
+
84
+ export { createEditSession, createEditSession as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaltree/core",
3
- "version": "6.0.13",
3
+ "version": "6.0.14",
4
4
  "description": "Lightweight, type-safe signal-based state management for Angular. Core package providing hierarchical signal trees, basic entity management, and async actions.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
package/src/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { signalTree } from './lib/signal-tree';
2
2
  export type { ISignalTree, SignalTree, SignalTreeBase, FullSignalTree, ProdSignalTree, TreeNode, CallableWritableSignal, AccessibleNode, NodeAccessor, Primitive, NotFn, TreeConfig, TreePreset, Enhancer, EnhancerMeta, EnhancerWithMeta, EntitySignal, EntityMapMarker, EntityConfig, MutationOptions, AddOptions, AddManyOptions, TimeTravelEntry, TimeTravelMethods, } from './lib/types';
3
3
  export { entityMap } from './lib/types';
4
4
  export { equal, deepEqual, isNodeAccessor, isAnySignal, toWritableSignal, parsePath, composeEnhancers, isBuiltInObject, createLazySignalTree, } from './lib/utils';
5
+ export { createEditSession, type EditSession, type UndoRedoHistory, } from './lib/edit-session';
5
6
  export { getPathNotifier } from './lib/path-notifier';
6
7
  export { SecurityValidator, SecurityPresets, type SecurityEvent, type SecurityEventType, type SecurityValidatorConfig, } from './lib/security/security-validator';
7
8
  export { createEnhancer, resolveEnhancerOrder } from './enhancers/index';
@@ -0,0 +1,21 @@
1
+ import { WritableSignal } from '@angular/core';
2
+ export type UndoRedoHistory<T> = {
3
+ past: T[];
4
+ present: T;
5
+ future: T[];
6
+ };
7
+ export interface EditSession<T> {
8
+ readonly original: WritableSignal<T>;
9
+ readonly modified: WritableSignal<T>;
10
+ readonly canUndo: () => boolean;
11
+ readonly canRedo: () => boolean;
12
+ readonly isDirty: () => boolean;
13
+ setOriginal(value: T): void;
14
+ applyChanges(valueOrUpdater: T | ((current: T) => T)): void;
15
+ undo(): void;
16
+ redo(): void;
17
+ reset(): void;
18
+ getHistory(): UndoRedoHistory<T>;
19
+ }
20
+ export declare function createEditSession<T>(initial: T): EditSession<T>;
21
+ export default createEditSession;