@hardlydifficult/state-tracker 2.0.4 → 2.0.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.
Files changed (2) hide show
  1. package/README.md +62 -12
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @hardlydifficult/state-tracker
2
2
 
3
- Atomic JSON state persistence with sync and async APIs, auto-save, and graceful degradation.
3
+ Atomic JSON state persistence with sync/async APIs, auto-save, and graceful degradation for TypeScript applications.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,11 +8,7 @@ Atomic JSON state persistence with sync and async APIs, auto-save, and graceful
8
8
  npm install @hardlydifficult/state-tracker
9
9
  ```
10
10
 
11
- ## Usage
12
-
13
- ### Server context (async with auto-save)
14
-
15
- For long-running servers, use `loadAsync()` + `autoSaveMs` + `update()`. State degrades gracefully to in-memory if disk is unavailable.
11
+ ## Quick Start
16
12
 
17
13
  ```typescript
18
14
  import { StateTracker } from "@hardlydifficult/state-tracker";
@@ -38,11 +34,17 @@ store.set({ requestCount: 0, lastActiveAt: new Date().toISOString() }); // full
38
34
  await store.saveAsync(); // force immediate save
39
35
  ```
40
36
 
41
- ### Simple context (sync)
37
+ ## State Persistence
38
+
39
+ The `StateTracker` class provides atomic JSON state persistence using file-based storage with graceful fallback to in-memory mode when disk access fails.
40
+
41
+ ### Sync API
42
42
 
43
- For tools and scripts, use the sync `load()`/`save()` API.
43
+ For tools and scripts, use the synchronous API:
44
44
 
45
45
  ```typescript
46
+ import { StateTracker } from "@hardlydifficult/state-tracker";
47
+
46
48
  const store = new StateTracker<number>({
47
49
  key: "counter",
48
50
  default: 0,
@@ -52,6 +54,23 @@ const count = store.load();
52
54
  store.save(count + 1);
53
55
  ```
54
56
 
57
+ ### Async API
58
+
59
+ For long-running servers, use the async API with auto-save:
60
+
61
+ ```typescript
62
+ const store = new StateTracker<AppState>({
63
+ key: "app",
64
+ default: { version: 1 },
65
+ stateDirectory: "/var/state",
66
+ autoSaveMs: 5000,
67
+ });
68
+
69
+ await store.loadAsync();
70
+ store.set({ version: 2 });
71
+ await store.saveAsync(); // Force immediate save
72
+ ```
73
+
55
74
  ## Options
56
75
 
57
76
  | Option | Type | Description |
@@ -60,7 +79,7 @@ store.save(count + 1);
60
79
  | `default` | `T` | Default value when no state file exists (required) |
61
80
  | `stateDirectory` | `string` | Directory for state files (default: `$STATE_TRACKER_DIR` or `~/.app-state`) |
62
81
  | `autoSaveMs` | `number` | Auto-save interval after `update()`/`set()`/`reset()` (default: 0 = disabled) |
63
- | `onEvent` | `function` | Event callback for logging (`{ level, message, context }`) |
82
+ | `onEvent` | `(event: StateTrackerEvent) => void` | Event callback for logging (`{ level, message, context }`) |
64
83
 
65
84
  ## Properties
66
85
 
@@ -76,16 +95,47 @@ store.save(count + 1);
76
95
  | `loadAsync()` | Async load with graceful degradation (safe to call multiple times) |
77
96
  | `saveAsync()` | Async atomic save (temp file + rename) |
78
97
  | `load()` | Sync load |
79
- | `save(value)` | Sync save |
98
+ | `save(value)` | Sync save (overwrites entire state) |
80
99
  | `update(changes)` | Shallow merge for object state, triggers auto-save |
81
100
  | `set(newState)` | Replace entire state, triggers auto-save |
82
101
  | `reset()` | Restore to defaults, triggers auto-save |
102
+ | `getFilePath()` | Returns the full path to the state file |
103
+
104
+ ## Event Handling
105
+
106
+ Events are emitted for key lifecycle operations with configurable logging:
107
+
108
+ ```typescript
109
+ const store = new StateTracker<AppState>({
110
+ key: "app",
111
+ default: { version: 1 },
112
+ onEvent: ({ level, message, context }) => {
113
+ console.log(`[${level}] ${message}`, context);
114
+ },
115
+ });
116
+ ```
117
+
118
+ Event levels: `"debug"`, `"info"`, `"warn"`, `"error"`
83
119
 
84
120
  ## Features
85
121
 
86
122
  - **Type inference** from the default value
87
123
  - **Atomic writes** via temp file + rename to prevent corruption
88
- - **Key sanitization** to prevent path traversal
124
+ - **Key sanitization** to prevent path traversal (alphanumeric, hyphens, underscores only)
89
125
  - **Graceful degradation** — runs in-memory when disk is unavailable
90
126
  - **Auto-save** — debounced saves after state mutations
91
- - **Legacy format support** — reads both v1 envelope and raw formats
127
+ - **Legacy format support** — reads both v1 envelope format and raw PersistentStore formats
128
+
129
+ ## Legacy Format Migration
130
+
131
+ The library transparently handles migration from legacy formats:
132
+
133
+ ```typescript
134
+ // If disk contains legacy format: { count: 42 }
135
+ // Load merges with defaults: { count: 42, extra: true }
136
+ await store.loadAsync();
137
+
138
+ // Subsequent save writes new envelope format:
139
+ // { value: { count: 42, extra: true }, lastUpdated: "..." }
140
+ await store.saveAsync();
141
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardlydifficult/state-tracker",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [