tiny-server-state 0.1.2
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/README.md +269 -0
- package/dist/Store.d.ts +46 -0
- package/dist/index.cjs +145 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
@@ -0,0 +1,269 @@
|
|
1
|
+
# π§ tiny-server-state
|
2
|
+
|
3
|
+
A **lightweight, reactive in-memory state engine** for Node.js servers. It offers simple key-value storage with optional TTL (Time-To-Live), real-time subscriptions, and a React-like API for easier state management.
|
4
|
+
|
5
|
+
> Ideal for handling temporary, shared, or cacheable state without a database.
|
6
|
+
|
7
|
+
---
|
8
|
+
|
9
|
+
## π Features
|
10
|
+
|
11
|
+
- β
**Simple and minimal** state engine
|
12
|
+
- π **TTL-based expiration** (auto-deletes expired keys)
|
13
|
+
- π§ **React-style `useState`** for intuitive state access
|
14
|
+
- π‘ **Subscriptions** for real-time updates
|
15
|
+
- π **Automatic Garbage Collection (GC)**
|
16
|
+
- π οΈ **No external dependencies**
|
17
|
+
- π **TypeScript support** out of the box
|
18
|
+
|
19
|
+
---
|
20
|
+
|
21
|
+
## π¦ Installation
|
22
|
+
|
23
|
+
```bash
|
24
|
+
npm install tiny-server-state
|
25
|
+
```
|
26
|
+
|
27
|
+
## π Quick Start
|
28
|
+
|
29
|
+
```typescript
|
30
|
+
import { createServerState } from "tiny-server-state";
|
31
|
+
|
32
|
+
// Create a new state store instance
|
33
|
+
const store = createServerState();
|
34
|
+
|
35
|
+
// Use React-like state management
|
36
|
+
const [count, setCount] = store.useState("count", 0);
|
37
|
+
console.log(count); // 0
|
38
|
+
|
39
|
+
// Update state with a new value
|
40
|
+
setCount(5);
|
41
|
+
|
42
|
+
// Or use a function to update based on previous value
|
43
|
+
setCount(prev => prev + 1);
|
44
|
+
```
|
45
|
+
|
46
|
+
## π API Reference
|
47
|
+
|
48
|
+
### Creating a Store
|
49
|
+
|
50
|
+
```typescript
|
51
|
+
const store = createServerState(sweepInterval?: number);
|
52
|
+
```
|
53
|
+
|
54
|
+
- `sweepInterval`: Optional interval (in ms) for garbage collection (default: 60000)
|
55
|
+
|
56
|
+
### Core Methods
|
57
|
+
|
58
|
+
#### `get<T>(key: string): T | undefined`
|
59
|
+
Retrieve a value from the store.
|
60
|
+
|
61
|
+
```typescript
|
62
|
+
const value = store.get("myKey");
|
63
|
+
```
|
64
|
+
|
65
|
+
#### `set<T>(key: string, value: T, options?: StateOptions): void`
|
66
|
+
Set a value in the store.
|
67
|
+
|
68
|
+
```typescript
|
69
|
+
// Simple set
|
70
|
+
store.set("myKey", "myValue");
|
71
|
+
|
72
|
+
// Set with TTL (expires after 5 seconds)
|
73
|
+
store.set("temporaryKey", "value", { ttl: 5000 });
|
74
|
+
```
|
75
|
+
|
76
|
+
#### `useState<T>(key: string, initial: T | (() => T), options?: StateOptions)`
|
77
|
+
React-style state management.
|
78
|
+
|
79
|
+
```typescript
|
80
|
+
// Basic usage
|
81
|
+
const [value, setValue] = store.useState("key", "initial");
|
82
|
+
|
83
|
+
// With initializer function
|
84
|
+
const [user, setUser] = store.useState("user", () => ({ name: "John" }));
|
85
|
+
|
86
|
+
// With TTL
|
87
|
+
const [token, setToken] = store.useState("token", "abc123", { ttl: 3600000 }); // 1 hour
|
88
|
+
|
89
|
+
// Update examples
|
90
|
+
setValue("new value"); // Direct update
|
91
|
+
setValue(prev => prev + " updated"); // Function update
|
92
|
+
```
|
93
|
+
|
94
|
+
#### `subscribe<T>(key: string, callback: (value: T) => void)`
|
95
|
+
Subscribe to state changes.
|
96
|
+
|
97
|
+
```typescript
|
98
|
+
// Subscribe to changes
|
99
|
+
const unsubscribe = store.subscribe("user", (newValue) => {
|
100
|
+
console.log("User updated:", newValue);
|
101
|
+
});
|
102
|
+
|
103
|
+
// Later: cleanup subscription
|
104
|
+
unsubscribe();
|
105
|
+
```
|
106
|
+
|
107
|
+
#### `useEffect(effect: () => void | (() => void), deps?: string[], effectId?: string)`
|
108
|
+
React-style effect management for side effects when state changes.
|
109
|
+
|
110
|
+
```typescript
|
111
|
+
// Run effect when dependencies change
|
112
|
+
const cleanup = store.useEffect(
|
113
|
+
() => {
|
114
|
+
console.log("User or settings changed");
|
115
|
+
|
116
|
+
// Optional cleanup function
|
117
|
+
return () => {
|
118
|
+
console.log("Effect cleanup");
|
119
|
+
};
|
120
|
+
},
|
121
|
+
["user", "settings"] // Dependencies
|
122
|
+
);
|
123
|
+
|
124
|
+
// Run effect only once (no dependencies)
|
125
|
+
const onceCleanup = store.useEffect(() => {
|
126
|
+
console.log("This runs once");
|
127
|
+
});
|
128
|
+
|
129
|
+
// Later: cleanup effect
|
130
|
+
cleanup();
|
131
|
+
```
|
132
|
+
|
133
|
+
#### `cleanupAllEffects(): void`
|
134
|
+
Cleanup all active effects (useful for testing or shutdown).
|
135
|
+
|
136
|
+
```typescript
|
137
|
+
// Cleanup all effects at once
|
138
|
+
store.cleanupAllEffects();
|
139
|
+
```
|
140
|
+
|
141
|
+
## π― Use Cases
|
142
|
+
|
143
|
+
### 1. Session Management
|
144
|
+
```typescript
|
145
|
+
// Store session data with automatic expiration
|
146
|
+
const [session, setSession] = store.useState("session", null, {
|
147
|
+
ttl: 30 * 60 * 1000 // 30 minutes
|
148
|
+
});
|
149
|
+
|
150
|
+
// Update session
|
151
|
+
setSession({ userId: "123", lastActive: Date.now() });
|
152
|
+
```
|
153
|
+
|
154
|
+
### 2. Caching
|
155
|
+
```typescript
|
156
|
+
// Cache expensive operation results
|
157
|
+
async function getCachedData(id: string) {
|
158
|
+
const [data, setData] = store.useState(`cache:${id}`, null, {
|
159
|
+
ttl: 5 * 60 * 1000 // 5 minutes
|
160
|
+
});
|
161
|
+
|
162
|
+
if (!data) {
|
163
|
+
const newData = await fetchExpensiveData(id);
|
164
|
+
setData(newData);
|
165
|
+
return newData;
|
166
|
+
}
|
167
|
+
|
168
|
+
return data;
|
169
|
+
}
|
170
|
+
```
|
171
|
+
|
172
|
+
### 3. Real-time Updates and Side Effects
|
173
|
+
```typescript
|
174
|
+
// Broadcast changes to all subscribers
|
175
|
+
const [status, setStatus] = store.useState("systemStatus", "operational");
|
176
|
+
const [metrics, setMetrics] = store.useState("metrics", { cpu: 0, memory: 0 });
|
177
|
+
|
178
|
+
// React to status and metrics changes
|
179
|
+
store.useEffect(() => {
|
180
|
+
const currentMetrics = store.get("metrics");
|
181
|
+
const currentStatus = store.get("systemStatus");
|
182
|
+
|
183
|
+
// Notify admins when system is stressed
|
184
|
+
if (currentMetrics.cpu > 90 || currentMetrics.memory > 90) {
|
185
|
+
notifyAdmins(`High resource usage detected! CPU: ${currentMetrics.cpu}%, Memory: ${currentMetrics.memory}%`);
|
186
|
+
setStatus("stressed");
|
187
|
+
}
|
188
|
+
|
189
|
+
// Log all status changes
|
190
|
+
console.log(`System status: ${currentStatus}`);
|
191
|
+
|
192
|
+
// Cleanup function (runs before next effect or on cleanup)
|
193
|
+
return () => {
|
194
|
+
console.log(`Previous status: ${currentStatus}`);
|
195
|
+
};
|
196
|
+
}, ["systemStatus", "metrics"]);
|
197
|
+
|
198
|
+
// Update status
|
199
|
+
setStatus("maintenance");
|
200
|
+
|
201
|
+
// Update metrics
|
202
|
+
setMetrics({ cpu: 95, memory: 85 }); // This will trigger the effect and change status to "stressed"
|
203
|
+
```
|
204
|
+
|
205
|
+
## π§ Configuration
|
206
|
+
|
207
|
+
### StateOptions Interface
|
208
|
+
|
209
|
+
```typescript
|
210
|
+
interface StateOptions {
|
211
|
+
ttl?: number; // Time-to-live in milliseconds
|
212
|
+
}
|
213
|
+
```
|
214
|
+
|
215
|
+
### Garbage Collection
|
216
|
+
|
217
|
+
The store automatically removes expired entries at regular intervals. You can configure the sweep interval when creating the store:
|
218
|
+
|
219
|
+
```typescript
|
220
|
+
// Custom sweep interval (e.g., every 5 minutes)
|
221
|
+
const store = createServerState(5 * 60 * 1000);
|
222
|
+
```
|
223
|
+
|
224
|
+
## π€ Contributing
|
225
|
+
|
226
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
227
|
+
|
228
|
+
## π License
|
229
|
+
|
230
|
+
MIT License - feel free to use this in your own projects.
|
231
|
+
|
232
|
+
---
|
233
|
+
|
234
|
+
## π TypeScript Support
|
235
|
+
|
236
|
+
This package includes TypeScript definitions out of the box. You'll get full type safety and autocompletion in your TypeScript projects.
|
237
|
+
|
238
|
+
```typescript
|
239
|
+
// Type-safe state management
|
240
|
+
const [user, setUser] = store.useState<{
|
241
|
+
id: string;
|
242
|
+
name: string;
|
243
|
+
age: number;
|
244
|
+
}>("user", {
|
245
|
+
id: "1",
|
246
|
+
name: "John",
|
247
|
+
age: 30
|
248
|
+
});
|
249
|
+
|
250
|
+
// TypeScript will ensure type safety
|
251
|
+
setUser(prev => ({
|
252
|
+
...prev,
|
253
|
+
age: prev.age + 1
|
254
|
+
}));
|
255
|
+
```
|
256
|
+
|
257
|
+
## β‘ Performance Considerations
|
258
|
+
|
259
|
+
- The store uses an in-memory Map, so be mindful of memory usage when storing large objects
|
260
|
+
- TTL and garbage collection help manage memory automatically
|
261
|
+
- Consider using shorter TTLs for frequently changing data
|
262
|
+
- For large-scale applications, consider using a proper database instead
|
263
|
+
|
264
|
+
## π Security Notes
|
265
|
+
|
266
|
+
- Data is stored in memory and will be lost when the server restarts
|
267
|
+
- Don't store sensitive information without proper encryption
|
268
|
+
- Be cautious with TTL values to prevent memory leaks
|
269
|
+
- Consider implementing rate limiting for state updates if exposed to client requests
|
package/dist/Store.d.ts
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
import { EventEmitter } from "node:events";
|
2
|
+
export interface StateOptions {
|
3
|
+
ttl?: number;
|
4
|
+
}
|
5
|
+
export declare class ServerState {
|
6
|
+
private sweepEvery;
|
7
|
+
private cache;
|
8
|
+
private events;
|
9
|
+
private effects;
|
10
|
+
constructor(sweepEvery?: number);
|
11
|
+
/** Internal GC */
|
12
|
+
private sweep;
|
13
|
+
/** Get value (or undefined if missing / expired) */
|
14
|
+
get<T>(key: string): T | undefined;
|
15
|
+
/** Set value + optional TTL */
|
16
|
+
set<T>(key: string, value: T, opts?: StateOptions): void;
|
17
|
+
/**
|
18
|
+
* React-like helper.
|
19
|
+
* ```ts
|
20
|
+
* const [count, setCount] = store.useState("count", 0, { ttl: 30_000 });
|
21
|
+
* ```
|
22
|
+
*/
|
23
|
+
useState<T>(key: string, initial: T | (() => T), opts?: StateOptions): [T, (v: T | ((prev: T) => T), o?: StateOptions) => void];
|
24
|
+
/** Subscribe; returns an unsubscribe fn */
|
25
|
+
subscribe<T>(key: string, cb: (v: T) => void): () => EventEmitter<[never]>;
|
26
|
+
/**
|
27
|
+
* React-like useEffect helper.
|
28
|
+
* Runs effect when dependencies change, supports cleanup.
|
29
|
+
* ```ts
|
30
|
+
* // Run effect when 'user' or 'settings' change
|
31
|
+
* const cleanup = store.useEffect(
|
32
|
+
* () => {
|
33
|
+
* console.log('User or settings changed');
|
34
|
+
* return () => console.log('Cleanup');
|
35
|
+
* },
|
36
|
+
* ['user', 'settings']
|
37
|
+
* );
|
38
|
+
* ```
|
39
|
+
*/
|
40
|
+
useEffect(effect: () => void | (() => void), deps?: string[], effectId?: string): () => void;
|
41
|
+
/**
|
42
|
+
* Cleanup all effects (useful for testing or shutdown)
|
43
|
+
*/
|
44
|
+
cleanupAllEffects(): void;
|
45
|
+
}
|
46
|
+
export declare const createServerState: (sweep?: number) => ServerState;
|
package/dist/index.cjs
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var node_events = require('node:events');
|
4
|
+
|
5
|
+
// [tiny-server-state] in-memory state engine
|
6
|
+
class ServerState {
|
7
|
+
constructor(sweepEvery = 60000 /* 1 min */) {
|
8
|
+
this.sweepEvery = sweepEvery;
|
9
|
+
this.cache = new Map();
|
10
|
+
this.events = new node_events.EventEmitter();
|
11
|
+
this.effects = new Map(); // Track effect cleanup functions
|
12
|
+
setInterval(() => this.sweep(), sweepEvery).unref();
|
13
|
+
}
|
14
|
+
// βββββββββββββββββββββββββ core helpers βββββββββββββββββββββββββ
|
15
|
+
/** Internal GC */
|
16
|
+
sweep() {
|
17
|
+
const now = Date.now();
|
18
|
+
for (const [k, v] of this.cache)
|
19
|
+
if (v.expiresAt && v.expiresAt <= now)
|
20
|
+
this.cache.delete(k);
|
21
|
+
}
|
22
|
+
/** Get value (or undefined if missing / expired) */
|
23
|
+
get(key) {
|
24
|
+
const hit = this.cache.get(key);
|
25
|
+
if (!hit)
|
26
|
+
return undefined;
|
27
|
+
if (hit.expiresAt && hit.expiresAt <= Date.now()) {
|
28
|
+
this.cache.delete(key);
|
29
|
+
return undefined;
|
30
|
+
}
|
31
|
+
return hit.value;
|
32
|
+
}
|
33
|
+
/** Set value + optional TTL */
|
34
|
+
set(key, value, opts = {}) {
|
35
|
+
const expiresAt = opts.ttl ? Date.now() + opts.ttl : undefined;
|
36
|
+
this.cache.set(key, { value, expiresAt });
|
37
|
+
this.events.emit(key, value); // notify subscribers
|
38
|
+
}
|
39
|
+
// βββββββββββββββββββββ React-flavoured sugar βββββββββββββββββββββ
|
40
|
+
/**
|
41
|
+
* React-like helper.
|
42
|
+
* ```ts
|
43
|
+
* const [count, setCount] = store.useState("count", 0, { ttl: 30_000 });
|
44
|
+
* ```
|
45
|
+
*/
|
46
|
+
useState(key, initial, opts = {}) {
|
47
|
+
let current = this.get(key);
|
48
|
+
if (current === undefined) {
|
49
|
+
current = typeof initial === "function" ? initial() : initial;
|
50
|
+
this.set(key, current, opts);
|
51
|
+
}
|
52
|
+
const setter = (v, override = {}) => {
|
53
|
+
const prev = this.get(key);
|
54
|
+
const next = typeof v === "function" ? v(prev) : v;
|
55
|
+
this.set(key, next, { ...opts, ...override });
|
56
|
+
};
|
57
|
+
return [current, setter];
|
58
|
+
}
|
59
|
+
/** Subscribe; returns an unsubscribe fn */
|
60
|
+
subscribe(key, cb) {
|
61
|
+
this.events.on(key, cb);
|
62
|
+
return () => this.events.off(key, cb);
|
63
|
+
}
|
64
|
+
/**
|
65
|
+
* React-like useEffect helper.
|
66
|
+
* Runs effect when dependencies change, supports cleanup.
|
67
|
+
* ```ts
|
68
|
+
* // Run effect when 'user' or 'settings' change
|
69
|
+
* const cleanup = store.useEffect(
|
70
|
+
* () => {
|
71
|
+
* console.log('User or settings changed');
|
72
|
+
* return () => console.log('Cleanup');
|
73
|
+
* },
|
74
|
+
* ['user', 'settings']
|
75
|
+
* );
|
76
|
+
* ```
|
77
|
+
*/
|
78
|
+
useEffect(effect, deps = [], effectId) {
|
79
|
+
const id = effectId || `effect_${Date.now()}_${Math.random()}`;
|
80
|
+
// Cleanup previous effect if it exists
|
81
|
+
const existingCleanup = this.effects.get(id);
|
82
|
+
if (existingCleanup) {
|
83
|
+
existingCleanup();
|
84
|
+
this.effects.delete(id);
|
85
|
+
}
|
86
|
+
let cleanup;
|
87
|
+
const unsubscribers = [];
|
88
|
+
// Run effect initially
|
89
|
+
const runEffect = () => {
|
90
|
+
// Cleanup previous run
|
91
|
+
if (cleanup)
|
92
|
+
cleanup();
|
93
|
+
// Run the effect
|
94
|
+
const result = effect();
|
95
|
+
cleanup = typeof result === 'function' ? result : undefined;
|
96
|
+
};
|
97
|
+
// If no dependencies, run once and return cleanup
|
98
|
+
if (deps.length === 0) {
|
99
|
+
runEffect();
|
100
|
+
const effectCleanup = () => {
|
101
|
+
if (cleanup)
|
102
|
+
cleanup();
|
103
|
+
this.effects.delete(id);
|
104
|
+
};
|
105
|
+
this.effects.set(id, effectCleanup);
|
106
|
+
return effectCleanup;
|
107
|
+
}
|
108
|
+
// Subscribe to all dependencies
|
109
|
+
deps.forEach(dep => {
|
110
|
+
const unsub = this.subscribe(dep, () => {
|
111
|
+
runEffect();
|
112
|
+
});
|
113
|
+
unsubscribers.push(unsub);
|
114
|
+
});
|
115
|
+
// Run effect initially
|
116
|
+
runEffect();
|
117
|
+
// Return cleanup function
|
118
|
+
const effectCleanup = () => {
|
119
|
+
// Cleanup effect
|
120
|
+
if (cleanup)
|
121
|
+
cleanup();
|
122
|
+
// Unsubscribe from all dependencies
|
123
|
+
unsubscribers.forEach(unsub => unsub());
|
124
|
+
// Remove from effects map
|
125
|
+
this.effects.delete(id);
|
126
|
+
};
|
127
|
+
this.effects.set(id, effectCleanup);
|
128
|
+
return effectCleanup;
|
129
|
+
}
|
130
|
+
/**
|
131
|
+
* Cleanup all effects (useful for testing or shutdown)
|
132
|
+
*/
|
133
|
+
cleanupAllEffects() {
|
134
|
+
for (const cleanup of this.effects.values()) {
|
135
|
+
cleanup();
|
136
|
+
}
|
137
|
+
this.effects.clear();
|
138
|
+
}
|
139
|
+
}
|
140
|
+
// factory (so callers donβt import the class directly)
|
141
|
+
const createServerState = (sweep) => new ServerState(sweep);
|
142
|
+
|
143
|
+
exports.ServerState = ServerState;
|
144
|
+
exports.createServerState = createServerState;
|
145
|
+
//# sourceMappingURL=index.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/Store.ts"],"sourcesContent":["// [tiny-server-state] in-memory state engine\nimport { EventEmitter } from \"node:events\";\n\nexport interface StateOptions { ttl?: number /* ms */; }\n\ninterface Entry<T = unknown> {\n value: T;\n /** epoch in ms when the value dies */\n expiresAt?: number;\n}\n\nexport class ServerState {\n private cache = new Map<string, Entry>();\n private events = new EventEmitter();\n private effects = new Map<string, () => void>(); // Track effect cleanup functions\n\n constructor(private sweepEvery = 60_000 /* 1 min */) {\n setInterval(() => this.sweep(), sweepEvery).unref();\n }\n\n // βββββββββββββββββββββββββ core helpers βββββββββββββββββββββββββ\n\n /** Internal GC */\n private sweep() {\n const now = Date.now();\n for (const [k, v] of this.cache) if (v.expiresAt && v.expiresAt <= now) this.cache.delete(k);\n }\n\n /** Get value (or undefined if missing / expired) */\n get<T>(key: string): T | undefined {\n const hit = this.cache.get(key);\n if (!hit) return undefined;\n if (hit.expiresAt && hit.expiresAt <= Date.now()) {\n this.cache.delete(key);\n return undefined;\n }\n return hit.value as T;\n }\n\n /** Set value + optional TTL */\n set<T>(key: string, value: T, opts: StateOptions = {}): void {\n const expiresAt = opts.ttl ? Date.now() + opts.ttl : undefined;\n this.cache.set(key, { value, expiresAt });\n this.events.emit(key, value); // notify subscribers\n }\n\n // βββββββββββββββββββββ React-flavoured sugar βββββββββββββββββββββ\n\n /**\n * React-like helper.\n * ```ts\n * const [count, setCount] = store.useState(\"count\", 0, { ttl: 30_000 });\n * ```\n */\n useState<T>(\n key: string,\n initial: T | (() => T),\n opts: StateOptions = {}\n ): [T, (v: T | ((prev: T) => T), o?: StateOptions) => void] {\n let current = this.get<T>(key);\n if (current === undefined) {\n current = typeof initial === \"function\" ? (initial as () => T)() : initial;\n this.set(key, current, opts);\n }\n const setter = (v: T | ((prev: T) => T), override: StateOptions = {}) => {\n const prev = this.get<T>(key) as T;\n const next = typeof v === \"function\" ? (v as (p: T) => T)(prev) : v;\n this.set(key, next, { ...opts, ...override });\n };\n return [current, setter];\n }\n\n /** Subscribe; returns an unsubscribe fn */\n subscribe<T>(key: string, cb: (v: T) => void) {\n this.events.on(key, cb);\n return () => this.events.off(key, cb);\n }\n\n /**\n * React-like useEffect helper.\n * Runs effect when dependencies change, supports cleanup.\n * ```ts\n * // Run effect when 'user' or 'settings' change\n * const cleanup = store.useEffect(\n * () => {\n * console.log('User or settings changed');\n * return () => console.log('Cleanup');\n * },\n * ['user', 'settings']\n * );\n * ```\n */\n useEffect(\n effect: () => void | (() => void),\n deps: string[] = [],\n effectId?: string\n ): () => void {\n const id = effectId || `effect_${Date.now()}_${Math.random()}`;\n \n // Cleanup previous effect if it exists\n const existingCleanup = this.effects.get(id);\n if (existingCleanup) {\n existingCleanup();\n this.effects.delete(id);\n }\n\n let cleanup: (() => void) | undefined;\n const unsubscribers: (() => void)[] = [];\n\n // Run effect initially\n const runEffect = () => {\n // Cleanup previous run\n if (cleanup) cleanup();\n \n // Run the effect\n const result = effect();\n cleanup = typeof result === 'function' ? result : undefined;\n };\n\n // If no dependencies, run once and return cleanup\n if (deps.length === 0) {\n runEffect();\n const effectCleanup = () => {\n if (cleanup) cleanup();\n this.effects.delete(id);\n };\n this.effects.set(id, effectCleanup);\n return effectCleanup;\n }\n\n // Subscribe to all dependencies\n deps.forEach(dep => {\n const unsub = this.subscribe(dep, () => {\n runEffect();\n });\n unsubscribers.push(unsub);\n });\n\n // Run effect initially\n runEffect();\n\n // Return cleanup function\n const effectCleanup = () => {\n // Cleanup effect\n if (cleanup) cleanup();\n \n // Unsubscribe from all dependencies\n unsubscribers.forEach(unsub => unsub());\n \n // Remove from effects map\n this.effects.delete(id);\n };\n\n this.effects.set(id, effectCleanup);\n return effectCleanup;\n }\n\n /**\n * Cleanup all effects (useful for testing or shutdown)\n */\n cleanupAllEffects(): void {\n for (const cleanup of this.effects.values()) {\n cleanup();\n }\n this.effects.clear();\n }\n}\n\n// factory (so callers donβt import the class directly)\nexport const createServerState = (sweep?: number) => new ServerState(sweep);\n"],"names":["EventEmitter"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./Store";
|
package/dist/index.js
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
2
|
+
|
3
|
+
// [tiny-server-state] in-memory state engine
|
4
|
+
class ServerState {
|
5
|
+
constructor(sweepEvery = 60000 /* 1 min */) {
|
6
|
+
this.sweepEvery = sweepEvery;
|
7
|
+
this.cache = new Map();
|
8
|
+
this.events = new EventEmitter();
|
9
|
+
this.effects = new Map(); // Track effect cleanup functions
|
10
|
+
setInterval(() => this.sweep(), sweepEvery).unref();
|
11
|
+
}
|
12
|
+
// βββββββββββββββββββββββββ core helpers βββββββββββββββββββββββββ
|
13
|
+
/** Internal GC */
|
14
|
+
sweep() {
|
15
|
+
const now = Date.now();
|
16
|
+
for (const [k, v] of this.cache)
|
17
|
+
if (v.expiresAt && v.expiresAt <= now)
|
18
|
+
this.cache.delete(k);
|
19
|
+
}
|
20
|
+
/** Get value (or undefined if missing / expired) */
|
21
|
+
get(key) {
|
22
|
+
const hit = this.cache.get(key);
|
23
|
+
if (!hit)
|
24
|
+
return undefined;
|
25
|
+
if (hit.expiresAt && hit.expiresAt <= Date.now()) {
|
26
|
+
this.cache.delete(key);
|
27
|
+
return undefined;
|
28
|
+
}
|
29
|
+
return hit.value;
|
30
|
+
}
|
31
|
+
/** Set value + optional TTL */
|
32
|
+
set(key, value, opts = {}) {
|
33
|
+
const expiresAt = opts.ttl ? Date.now() + opts.ttl : undefined;
|
34
|
+
this.cache.set(key, { value, expiresAt });
|
35
|
+
this.events.emit(key, value); // notify subscribers
|
36
|
+
}
|
37
|
+
// βββββββββββββββββββββ React-flavoured sugar βββββββββββββββββββββ
|
38
|
+
/**
|
39
|
+
* React-like helper.
|
40
|
+
* ```ts
|
41
|
+
* const [count, setCount] = store.useState("count", 0, { ttl: 30_000 });
|
42
|
+
* ```
|
43
|
+
*/
|
44
|
+
useState(key, initial, opts = {}) {
|
45
|
+
let current = this.get(key);
|
46
|
+
if (current === undefined) {
|
47
|
+
current = typeof initial === "function" ? initial() : initial;
|
48
|
+
this.set(key, current, opts);
|
49
|
+
}
|
50
|
+
const setter = (v, override = {}) => {
|
51
|
+
const prev = this.get(key);
|
52
|
+
const next = typeof v === "function" ? v(prev) : v;
|
53
|
+
this.set(key, next, { ...opts, ...override });
|
54
|
+
};
|
55
|
+
return [current, setter];
|
56
|
+
}
|
57
|
+
/** Subscribe; returns an unsubscribe fn */
|
58
|
+
subscribe(key, cb) {
|
59
|
+
this.events.on(key, cb);
|
60
|
+
return () => this.events.off(key, cb);
|
61
|
+
}
|
62
|
+
/**
|
63
|
+
* React-like useEffect helper.
|
64
|
+
* Runs effect when dependencies change, supports cleanup.
|
65
|
+
* ```ts
|
66
|
+
* // Run effect when 'user' or 'settings' change
|
67
|
+
* const cleanup = store.useEffect(
|
68
|
+
* () => {
|
69
|
+
* console.log('User or settings changed');
|
70
|
+
* return () => console.log('Cleanup');
|
71
|
+
* },
|
72
|
+
* ['user', 'settings']
|
73
|
+
* );
|
74
|
+
* ```
|
75
|
+
*/
|
76
|
+
useEffect(effect, deps = [], effectId) {
|
77
|
+
const id = effectId || `effect_${Date.now()}_${Math.random()}`;
|
78
|
+
// Cleanup previous effect if it exists
|
79
|
+
const existingCleanup = this.effects.get(id);
|
80
|
+
if (existingCleanup) {
|
81
|
+
existingCleanup();
|
82
|
+
this.effects.delete(id);
|
83
|
+
}
|
84
|
+
let cleanup;
|
85
|
+
const unsubscribers = [];
|
86
|
+
// Run effect initially
|
87
|
+
const runEffect = () => {
|
88
|
+
// Cleanup previous run
|
89
|
+
if (cleanup)
|
90
|
+
cleanup();
|
91
|
+
// Run the effect
|
92
|
+
const result = effect();
|
93
|
+
cleanup = typeof result === 'function' ? result : undefined;
|
94
|
+
};
|
95
|
+
// If no dependencies, run once and return cleanup
|
96
|
+
if (deps.length === 0) {
|
97
|
+
runEffect();
|
98
|
+
const effectCleanup = () => {
|
99
|
+
if (cleanup)
|
100
|
+
cleanup();
|
101
|
+
this.effects.delete(id);
|
102
|
+
};
|
103
|
+
this.effects.set(id, effectCleanup);
|
104
|
+
return effectCleanup;
|
105
|
+
}
|
106
|
+
// Subscribe to all dependencies
|
107
|
+
deps.forEach(dep => {
|
108
|
+
const unsub = this.subscribe(dep, () => {
|
109
|
+
runEffect();
|
110
|
+
});
|
111
|
+
unsubscribers.push(unsub);
|
112
|
+
});
|
113
|
+
// Run effect initially
|
114
|
+
runEffect();
|
115
|
+
// Return cleanup function
|
116
|
+
const effectCleanup = () => {
|
117
|
+
// Cleanup effect
|
118
|
+
if (cleanup)
|
119
|
+
cleanup();
|
120
|
+
// Unsubscribe from all dependencies
|
121
|
+
unsubscribers.forEach(unsub => unsub());
|
122
|
+
// Remove from effects map
|
123
|
+
this.effects.delete(id);
|
124
|
+
};
|
125
|
+
this.effects.set(id, effectCleanup);
|
126
|
+
return effectCleanup;
|
127
|
+
}
|
128
|
+
/**
|
129
|
+
* Cleanup all effects (useful for testing or shutdown)
|
130
|
+
*/
|
131
|
+
cleanupAllEffects() {
|
132
|
+
for (const cleanup of this.effects.values()) {
|
133
|
+
cleanup();
|
134
|
+
}
|
135
|
+
this.effects.clear();
|
136
|
+
}
|
137
|
+
}
|
138
|
+
// factory (so callers donβt import the class directly)
|
139
|
+
const createServerState = (sweep) => new ServerState(sweep);
|
140
|
+
|
141
|
+
export { ServerState, createServerState };
|
142
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/Store.ts"],"sourcesContent":["// [tiny-server-state] in-memory state engine\nimport { EventEmitter } from \"node:events\";\n\nexport interface StateOptions { ttl?: number /* ms */; }\n\ninterface Entry<T = unknown> {\n value: T;\n /** epoch in ms when the value dies */\n expiresAt?: number;\n}\n\nexport class ServerState {\n private cache = new Map<string, Entry>();\n private events = new EventEmitter();\n private effects = new Map<string, () => void>(); // Track effect cleanup functions\n\n constructor(private sweepEvery = 60_000 /* 1 min */) {\n setInterval(() => this.sweep(), sweepEvery).unref();\n }\n\n // βββββββββββββββββββββββββ core helpers βββββββββββββββββββββββββ\n\n /** Internal GC */\n private sweep() {\n const now = Date.now();\n for (const [k, v] of this.cache) if (v.expiresAt && v.expiresAt <= now) this.cache.delete(k);\n }\n\n /** Get value (or undefined if missing / expired) */\n get<T>(key: string): T | undefined {\n const hit = this.cache.get(key);\n if (!hit) return undefined;\n if (hit.expiresAt && hit.expiresAt <= Date.now()) {\n this.cache.delete(key);\n return undefined;\n }\n return hit.value as T;\n }\n\n /** Set value + optional TTL */\n set<T>(key: string, value: T, opts: StateOptions = {}): void {\n const expiresAt = opts.ttl ? Date.now() + opts.ttl : undefined;\n this.cache.set(key, { value, expiresAt });\n this.events.emit(key, value); // notify subscribers\n }\n\n // βββββββββββββββββββββ React-flavoured sugar βββββββββββββββββββββ\n\n /**\n * React-like helper.\n * ```ts\n * const [count, setCount] = store.useState(\"count\", 0, { ttl: 30_000 });\n * ```\n */\n useState<T>(\n key: string,\n initial: T | (() => T),\n opts: StateOptions = {}\n ): [T, (v: T | ((prev: T) => T), o?: StateOptions) => void] {\n let current = this.get<T>(key);\n if (current === undefined) {\n current = typeof initial === \"function\" ? (initial as () => T)() : initial;\n this.set(key, current, opts);\n }\n const setter = (v: T | ((prev: T) => T), override: StateOptions = {}) => {\n const prev = this.get<T>(key) as T;\n const next = typeof v === \"function\" ? (v as (p: T) => T)(prev) : v;\n this.set(key, next, { ...opts, ...override });\n };\n return [current, setter];\n }\n\n /** Subscribe; returns an unsubscribe fn */\n subscribe<T>(key: string, cb: (v: T) => void) {\n this.events.on(key, cb);\n return () => this.events.off(key, cb);\n }\n\n /**\n * React-like useEffect helper.\n * Runs effect when dependencies change, supports cleanup.\n * ```ts\n * // Run effect when 'user' or 'settings' change\n * const cleanup = store.useEffect(\n * () => {\n * console.log('User or settings changed');\n * return () => console.log('Cleanup');\n * },\n * ['user', 'settings']\n * );\n * ```\n */\n useEffect(\n effect: () => void | (() => void),\n deps: string[] = [],\n effectId?: string\n ): () => void {\n const id = effectId || `effect_${Date.now()}_${Math.random()}`;\n \n // Cleanup previous effect if it exists\n const existingCleanup = this.effects.get(id);\n if (existingCleanup) {\n existingCleanup();\n this.effects.delete(id);\n }\n\n let cleanup: (() => void) | undefined;\n const unsubscribers: (() => void)[] = [];\n\n // Run effect initially\n const runEffect = () => {\n // Cleanup previous run\n if (cleanup) cleanup();\n \n // Run the effect\n const result = effect();\n cleanup = typeof result === 'function' ? result : undefined;\n };\n\n // If no dependencies, run once and return cleanup\n if (deps.length === 0) {\n runEffect();\n const effectCleanup = () => {\n if (cleanup) cleanup();\n this.effects.delete(id);\n };\n this.effects.set(id, effectCleanup);\n return effectCleanup;\n }\n\n // Subscribe to all dependencies\n deps.forEach(dep => {\n const unsub = this.subscribe(dep, () => {\n runEffect();\n });\n unsubscribers.push(unsub);\n });\n\n // Run effect initially\n runEffect();\n\n // Return cleanup function\n const effectCleanup = () => {\n // Cleanup effect\n if (cleanup) cleanup();\n \n // Unsubscribe from all dependencies\n unsubscribers.forEach(unsub => unsub());\n \n // Remove from effects map\n this.effects.delete(id);\n };\n\n this.effects.set(id, effectCleanup);\n return effectCleanup;\n }\n\n /**\n * Cleanup all effects (useful for testing or shutdown)\n */\n cleanupAllEffects(): void {\n for (const cleanup of this.effects.values()) {\n cleanup();\n }\n this.effects.clear();\n }\n}\n\n// factory (so callers donβt import the class directly)\nexport const createServerState = (sweep?: number) => new ServerState(sweep);\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
{
|
2
|
+
"name": "tiny-server-state",
|
3
|
+
"version": "0.1.2",
|
4
|
+
"description": "Lightweight TTL-enabled in-memory state for Node servers",
|
5
|
+
"main": "dist/index.cjs",
|
6
|
+
"module": "dist/index.js",
|
7
|
+
"types": "dist/index.d.ts",
|
8
|
+
"exports": {
|
9
|
+
"import": "./dist/index.js",
|
10
|
+
"require": "./dist/index.cjs"
|
11
|
+
},
|
12
|
+
"files": [
|
13
|
+
"dist"
|
14
|
+
],
|
15
|
+
"keywords": [
|
16
|
+
"state",
|
17
|
+
"cache",
|
18
|
+
"ttl",
|
19
|
+
"node",
|
20
|
+
"server"
|
21
|
+
],
|
22
|
+
"license": "MIT",
|
23
|
+
"scripts": {
|
24
|
+
"build": "rollup -c",
|
25
|
+
"clean": "rimraf dist",
|
26
|
+
"prepublishOnly": "npm run clean && npm run build",
|
27
|
+
"test": "node test/basic.test.js"
|
28
|
+
},
|
29
|
+
"devDependencies": {
|
30
|
+
"@rollup/plugin-typescript": "^11.1.3",
|
31
|
+
"@types/node": "^22.15.21",
|
32
|
+
"rimraf": "^5.0.5",
|
33
|
+
"rollup": "^4.41.0",
|
34
|
+
"typescript": "^5.4.5"
|
35
|
+
},
|
36
|
+
"dependencies": {
|
37
|
+
"tslib": "^2.8.1"
|
38
|
+
}
|
39
|
+
}
|