@sylphx/lens-server 1.11.3 → 2.1.0

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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * @sylphx/lens-server - Storage Types
3
+ *
4
+ * Storage adapter interface for opLog plugin.
5
+ * Enables serverless support by abstracting state/version/patch storage.
6
+ */
7
+
8
+ import type { PatchOperation } from "@sylphx/lens-core";
9
+
10
+ /**
11
+ * Entity state stored in the operation log.
12
+ */
13
+ export interface StoredEntityState {
14
+ /** Canonical state data */
15
+ data: Record<string, unknown>;
16
+ /** Current version */
17
+ version: number;
18
+ /** Timestamp of last update */
19
+ updatedAt: number;
20
+ }
21
+
22
+ /**
23
+ * Operation log entry stored in storage.
24
+ */
25
+ export interface StoredPatchEntry {
26
+ /** Version this patch creates */
27
+ version: number;
28
+ /** Patch operations */
29
+ patch: PatchOperation[];
30
+ /** Timestamp when patch was created */
31
+ timestamp: number;
32
+ }
33
+
34
+ /**
35
+ * Result from emit operation.
36
+ */
37
+ export interface EmitResult {
38
+ /** New version after emit */
39
+ version: number;
40
+ /** Computed patch (null if first emit) */
41
+ patch: PatchOperation[] | null;
42
+ /** Whether state actually changed */
43
+ changed: boolean;
44
+ }
45
+
46
+ /**
47
+ * Storage adapter interface for opLog.
48
+ *
49
+ * Implementations:
50
+ * - `memoryStorage()` - In-memory (default, for long-running servers)
51
+ * - `redisStorage()` - Redis/Upstash (for serverless)
52
+ * - `kvStorage()` - Cloudflare KV, Vercel KV
53
+ *
54
+ * All methods are async to support external storage.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Default (in-memory)
59
+ * const app = createApp({
60
+ * router,
61
+ * plugins: [opLog()],
62
+ * });
63
+ *
64
+ * // With Redis for serverless
65
+ * const app = createApp({
66
+ * router,
67
+ * plugins: [opLog({
68
+ * storage: redisStorage({ url: process.env.REDIS_URL }),
69
+ * })],
70
+ * });
71
+ * ```
72
+ */
73
+ export interface OpLogStorage {
74
+ /**
75
+ * Emit new state for an entity.
76
+ * This is an atomic operation that:
77
+ * 1. Computes patch from previous state (if exists)
78
+ * 2. Stores new state with incremented version
79
+ * 3. Appends patch to operation log
80
+ *
81
+ * @param entity - Entity type name
82
+ * @param entityId - Entity ID
83
+ * @param data - New state data
84
+ * @returns Emit result with version, patch, and changed flag
85
+ */
86
+ emit(entity: string, entityId: string, data: Record<string, unknown>): Promise<EmitResult>;
87
+
88
+ /**
89
+ * Get current canonical state for an entity.
90
+ *
91
+ * @param entity - Entity type name
92
+ * @param entityId - Entity ID
93
+ * @returns State data or null if not found
94
+ */
95
+ getState(entity: string, entityId: string): Promise<Record<string, unknown> | null>;
96
+
97
+ /**
98
+ * Get current version for an entity.
99
+ * Returns 0 if entity doesn't exist.
100
+ *
101
+ * @param entity - Entity type name
102
+ * @param entityId - Entity ID
103
+ * @returns Current version (0 if not found)
104
+ */
105
+ getVersion(entity: string, entityId: string): Promise<number>;
106
+
107
+ /**
108
+ * Get the latest patch for an entity.
109
+ * Returns null if no patches available.
110
+ *
111
+ * @param entity - Entity type name
112
+ * @param entityId - Entity ID
113
+ * @returns Latest patch or null
114
+ */
115
+ getLatestPatch(entity: string, entityId: string): Promise<PatchOperation[] | null>;
116
+
117
+ /**
118
+ * Get all patches since a given version.
119
+ * Used for reconnection to bring client up to date.
120
+ *
121
+ * @param entity - Entity type name
122
+ * @param entityId - Entity ID
123
+ * @param sinceVersion - Client's current version
124
+ * @returns Array of patches (one per version), or null if too old
125
+ */
126
+ getPatchesSince(
127
+ entity: string,
128
+ entityId: string,
129
+ sinceVersion: number,
130
+ ): Promise<PatchOperation[][] | null>;
131
+
132
+ /**
133
+ * Check if entity exists in storage.
134
+ *
135
+ * @param entity - Entity type name
136
+ * @param entityId - Entity ID
137
+ * @returns True if entity exists
138
+ */
139
+ has(entity: string, entityId: string): Promise<boolean>;
140
+
141
+ /**
142
+ * Delete an entity from storage.
143
+ * Removes state, version, and all patches.
144
+ *
145
+ * @param entity - Entity type name
146
+ * @param entityId - Entity ID
147
+ */
148
+ delete(entity: string, entityId: string): Promise<void>;
149
+
150
+ /**
151
+ * Clear all data from storage.
152
+ * Used for testing.
153
+ */
154
+ clear(): Promise<void>;
155
+
156
+ /**
157
+ * Dispose storage resources.
158
+ * Called when shutting down.
159
+ */
160
+ dispose?(): Promise<void>;
161
+ }
162
+
163
+ /**
164
+ * Configuration for operation log storage.
165
+ */
166
+ export interface OpLogStorageConfig {
167
+ /**
168
+ * Maximum number of patches to keep per entity.
169
+ * Older patches are evicted when limit is reached.
170
+ * @default 1000
171
+ */
172
+ maxPatchesPerEntity?: number;
173
+
174
+ /**
175
+ * Maximum age of patches in milliseconds.
176
+ * Patches older than this are evicted.
177
+ * @default 300000 (5 minutes)
178
+ */
179
+ maxPatchAge?: number;
180
+
181
+ /**
182
+ * Cleanup interval in milliseconds.
183
+ * Set to 0 to disable automatic cleanup.
184
+ * @default 60000 (1 minute)
185
+ */
186
+ cleanupInterval?: number;
187
+
188
+ /**
189
+ * Maximum retries for emit operations on version conflict.
190
+ * Only applies to external storage (Redis, Upstash, Vercel KV).
191
+ * Set to 0 to disable retries (fail immediately on conflict).
192
+ * @default 3
193
+ */
194
+ maxRetries?: number;
195
+ }
196
+
197
+ /**
198
+ * Default storage configuration.
199
+ */
200
+ export const DEFAULT_STORAGE_CONFIG: Required<OpLogStorageConfig> = {
201
+ maxPatchesPerEntity: 1000,
202
+ maxPatchAge: 5 * 60 * 1000, // 5 minutes
203
+ cleanupInterval: 60 * 1000, // 1 minute
204
+ maxRetries: 3,
205
+ };