@al8b/io 0.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.
- package/README.md +21 -0
- package/dist/index.cjs +203 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +51 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +201 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @al8b/io
|
|
2
|
+
|
|
3
|
+
Storage and IO helpers for the L8B runtime. Today this package centers on persistent storage through a single service abstraction.
|
|
4
|
+
|
|
5
|
+
## Public API
|
|
6
|
+
|
|
7
|
+
- `StorageService`
|
|
8
|
+
|
|
9
|
+
## Notes
|
|
10
|
+
|
|
11
|
+
- Used by `@al8b/vm` and `@al8b/runtime`.
|
|
12
|
+
- Keep persistence concerns here rather than coupling them to VM or runtime orchestration.
|
|
13
|
+
|
|
14
|
+
## Scripts
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun run build
|
|
18
|
+
bun run test
|
|
19
|
+
bun run typecheck
|
|
20
|
+
bun run clean
|
|
21
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var diagnostics = require('@al8b/diagnostics');
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var StorageService = class {
|
|
8
|
+
static {
|
|
9
|
+
__name(this, "StorageService");
|
|
10
|
+
}
|
|
11
|
+
namespace;
|
|
12
|
+
cache = /* @__PURE__ */ new Map();
|
|
13
|
+
pendingWrites = /* @__PURE__ */ new Map();
|
|
14
|
+
writeTimer = null;
|
|
15
|
+
runtime;
|
|
16
|
+
constructor(namespace = "/l8b", preserve = false, runtime) {
|
|
17
|
+
this.namespace = namespace;
|
|
18
|
+
this.runtime = runtime;
|
|
19
|
+
if (!preserve && typeof localStorage !== "undefined") {
|
|
20
|
+
this.clear();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get value from storage
|
|
25
|
+
*/
|
|
26
|
+
get(name) {
|
|
27
|
+
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
28
|
+
diagnostics.reportRuntimeError(this.runtime?.listener, diagnostics.APIErrorCode.E7063, {
|
|
29
|
+
key: String(name)
|
|
30
|
+
});
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (this.cache.has(name)) {
|
|
34
|
+
return this.cache.get(name);
|
|
35
|
+
}
|
|
36
|
+
if (typeof localStorage !== "undefined") {
|
|
37
|
+
try {
|
|
38
|
+
const key = `${this.namespace}.${name}`;
|
|
39
|
+
const value = localStorage.getItem(key);
|
|
40
|
+
if (value !== null) {
|
|
41
|
+
const parsed = JSON.parse(value);
|
|
42
|
+
this.cache.set(name, parsed);
|
|
43
|
+
return parsed;
|
|
44
|
+
}
|
|
45
|
+
} catch (err) {
|
|
46
|
+
diagnostics.reportRuntimeError(this.runtime?.listener, diagnostics.APIErrorCode.E7062, {
|
|
47
|
+
error: `Get operation failed: ${String(err)}`
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Set value in storage (batched write)
|
|
55
|
+
*/
|
|
56
|
+
set(name, value) {
|
|
57
|
+
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
58
|
+
diagnostics.reportRuntimeError(this.runtime?.listener, diagnostics.APIErrorCode.E7063, {
|
|
59
|
+
key: String(name)
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
this.cache.set(name, value);
|
|
64
|
+
this.pendingWrites.set(name, value);
|
|
65
|
+
if (this.writeTimer === null) {
|
|
66
|
+
const schedule = typeof window !== "undefined" && typeof window.setTimeout === "function" ? window.setTimeout.bind(window) : setTimeout;
|
|
67
|
+
this.writeTimer = schedule(() => {
|
|
68
|
+
this.flush();
|
|
69
|
+
}, 100);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Flush pending writes to localStorage
|
|
74
|
+
*/
|
|
75
|
+
flush() {
|
|
76
|
+
if (this.writeTimer !== null) {
|
|
77
|
+
clearTimeout(this.writeTimer);
|
|
78
|
+
this.writeTimer = null;
|
|
79
|
+
}
|
|
80
|
+
if (typeof localStorage === "undefined") {
|
|
81
|
+
this.pendingWrites.clear();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
for (const [name, value] of this.pendingWrites) {
|
|
85
|
+
try {
|
|
86
|
+
const key = `${this.namespace}.${name}`;
|
|
87
|
+
const serialized = JSON.stringify(this.sanitize(value));
|
|
88
|
+
localStorage.setItem(key, serialized);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
if (err.name === "QuotaExceededError" || err.code === 22) {
|
|
91
|
+
diagnostics.reportRuntimeError(this.runtime?.listener, diagnostics.APIErrorCode.E7061, {});
|
|
92
|
+
} else {
|
|
93
|
+
diagnostics.reportRuntimeError(this.runtime?.listener, diagnostics.APIErrorCode.E7062, {
|
|
94
|
+
error: `Set operation failed: ${String(err)}`
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
this.pendingWrites.clear();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if there are pending writes and flush if needed
|
|
103
|
+
*/
|
|
104
|
+
check() {
|
|
105
|
+
if (this.pendingWrites.size > 0) {
|
|
106
|
+
this.flush();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Delete a single key from storage (cache + localStorage + any pending write)
|
|
111
|
+
*/
|
|
112
|
+
delete(name) {
|
|
113
|
+
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
114
|
+
diagnostics.reportRuntimeError(this.runtime?.listener, diagnostics.APIErrorCode.E7063, {
|
|
115
|
+
key: String(name)
|
|
116
|
+
});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.cache.delete(name);
|
|
120
|
+
this.pendingWrites.delete(name);
|
|
121
|
+
if (typeof localStorage !== "undefined") {
|
|
122
|
+
try {
|
|
123
|
+
localStorage.removeItem(`${this.namespace}.${name}`);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
diagnostics.reportRuntimeError(this.runtime?.listener, diagnostics.APIErrorCode.E7062, {
|
|
126
|
+
error: `Delete operation failed: ${String(err)}`
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Clear all storage for this namespace
|
|
133
|
+
*/
|
|
134
|
+
clear() {
|
|
135
|
+
if (typeof localStorage === "undefined") {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const prefix = `${this.namespace}.`;
|
|
139
|
+
const keysToRemove = [];
|
|
140
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
141
|
+
const key = localStorage.key(i);
|
|
142
|
+
if (key && key.startsWith(prefix)) {
|
|
143
|
+
keysToRemove.push(key);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const key of keysToRemove) {
|
|
147
|
+
localStorage.removeItem(key);
|
|
148
|
+
}
|
|
149
|
+
this.cache.clear();
|
|
150
|
+
this.pendingWrites.clear();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Sanitize value for JSON serialization
|
|
154
|
+
* Removes functions and handles circular references
|
|
155
|
+
*/
|
|
156
|
+
sanitize(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
157
|
+
if (value === null || value === void 0) {
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
if (typeof value !== "object") {
|
|
161
|
+
if (typeof value === "function") {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
if (seen.has(value)) {
|
|
167
|
+
return void 0;
|
|
168
|
+
}
|
|
169
|
+
seen.add(value);
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
return value.map((item) => this.sanitize(item, seen)).filter((item) => item !== void 0);
|
|
172
|
+
}
|
|
173
|
+
const result = {};
|
|
174
|
+
for (const key in value) {
|
|
175
|
+
if (Object.hasOwn(value, key)) {
|
|
176
|
+
const sanitized = this.sanitize(value[key], seen);
|
|
177
|
+
if (sanitized !== void 0) {
|
|
178
|
+
result[key] = sanitized;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
interfaceCache = null;
|
|
185
|
+
/**
|
|
186
|
+
* Get storage interface for game code
|
|
187
|
+
*/
|
|
188
|
+
getInterface() {
|
|
189
|
+
if (this.interfaceCache) {
|
|
190
|
+
return this.interfaceCache;
|
|
191
|
+
}
|
|
192
|
+
this.interfaceCache = {
|
|
193
|
+
set: /* @__PURE__ */ __name((name, value) => this.set(name, value), "set"),
|
|
194
|
+
get: /* @__PURE__ */ __name((name) => this.get(name), "get"),
|
|
195
|
+
delete: /* @__PURE__ */ __name((name) => this.delete(name), "delete")
|
|
196
|
+
};
|
|
197
|
+
return this.interfaceCache;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
exports.StorageService = StorageService;
|
|
202
|
+
//# sourceMappingURL=index.cjs.map
|
|
203
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/index.ts"],"names":["StorageService","namespace","cache","Map","pendingWrites","writeTimer","runtime","preserve","localStorage","clear","get","name","trim","reportRuntimeError","listener","APIErrorCode","E7063","key","String","has","value","getItem","parsed","JSON","parse","set","err","E7062","error","schedule","window","setTimeout","bind","flush","clearTimeout","serialized","stringify","sanitize","setItem","code","E7061","check","size","delete","removeItem","prefix","keysToRemove","i","length","startsWith","push","seen","WeakSet","undefined","add","Array","isArray","map","item","filter","result","Object","hasOwn","sanitized","interfaceCache","getInterface"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Storage service - localStorage wrapper with automatic serialization\n */\n\nimport { APIErrorCode, reportRuntimeError } from \"@al8b/diagnostics\";\n\nexport class StorageService {\n\tprivate namespace: string;\n\tprivate cache: Map<string, any> = new Map();\n\tprivate pendingWrites: Map<string, any> = new Map();\n\tprivate writeTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate runtime?: any;\n\n\tconstructor(namespace = \"/l8b\", preserve = false, runtime?: any) {\n\t\tthis.namespace = namespace;\n\t\tthis.runtime = runtime;\n\n\t\t// Clear storage if not preserving\n\t\tif (!preserve && typeof localStorage !== \"undefined\") {\n\t\t\tthis.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Get value from storage\n\t */\n\tget(name: string): any {\n\t\t// Validate storage key\n\t\tif (!name || typeof name !== \"string\" || name.trim() === \"\") {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, { key: String(name) });\n\t\t\treturn null;\n\t\t}\n\n\t\t// Check cache first\n\t\tif (this.cache.has(name)) {\n\t\t\treturn this.cache.get(name);\n\t\t}\n\n\t\t// Try localStorage\n\t\tif (typeof localStorage !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tconst key = `${this.namespace}.${name}`;\n\t\t\t\tconst value = localStorage.getItem(key);\n\t\t\t\tif (value !== null) {\n\t\t\t\t\tconst parsed = JSON.parse(value);\n\t\t\t\t\tthis.cache.set(name, parsed);\n\t\t\t\t\treturn parsed;\n\t\t\t\t}\n\t\t\t} catch (err: any) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, { error: `Get operation failed: ${String(err)}` });\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Set value in storage (batched write)\n\t */\n\tset(name: string, value: any): void {\n\t\t// Validate storage key\n\t\tif (!name || typeof name !== \"string\" || name.trim() === \"\") {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, { key: String(name) });\n\t\t\treturn;\n\t\t}\n\n\t\t// Update cache\n\t\tthis.cache.set(name, value);\n\n\t\t// Queue write\n\t\tthis.pendingWrites.set(name, value);\n\n\t\t// Schedule batch write\n\t\tif (this.writeTimer === null) {\n\t\t\tconst schedule =\n\t\t\t\ttypeof window !== \"undefined\" && typeof window.setTimeout === \"function\"\n\t\t\t\t\t? window.setTimeout.bind(window)\n\t\t\t\t\t: setTimeout;\n\n\t\t\tthis.writeTimer = schedule(() => {\n\t\t\t\tthis.flush();\n\t\t\t}, 100);\n\t\t}\n\t}\n\n\t/**\n\t * Flush pending writes to localStorage\n\t */\n\tflush(): void {\n\t\tif (this.writeTimer !== null) {\n\t\t\tclearTimeout(this.writeTimer);\n\t\t\tthis.writeTimer = null;\n\t\t}\n\n\t\tif (typeof localStorage === \"undefined\") {\n\t\t\tthis.pendingWrites.clear();\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const [name, value] of this.pendingWrites) {\n\t\t\ttry {\n\t\t\t\tconst key = `${this.namespace}.${name}`;\n\t\t\t\tconst serialized = JSON.stringify(this.sanitize(value));\n\t\t\t\tlocalStorage.setItem(key, serialized);\n\t\t\t} catch (err: any) {\n\t\t\t\t// Check for quota exceeded error\n\t\t\t\tif (err.name === \"QuotaExceededError\" || err.code === 22) {\n\t\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7061, {});\n\t\t\t\t} else {\n\t\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, { error: `Set operation failed: ${String(err)}` });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.pendingWrites.clear();\n\t}\n\n\t/**\n\t * Check if there are pending writes and flush if needed\n\t */\n\tcheck(): void {\n\t\tif (this.pendingWrites.size > 0) {\n\t\t\tthis.flush();\n\t\t}\n\t}\n\n\t/**\n\t * Delete a single key from storage (cache + localStorage + any pending write)\n\t */\n\tdelete(name: string): void {\n\t\tif (!name || typeof name !== \"string\" || name.trim() === \"\") {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, { key: String(name) });\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove from in-memory state\n\t\tthis.cache.delete(name);\n\t\tthis.pendingWrites.delete(name);\n\n\t\t// Remove from localStorage\n\t\tif (typeof localStorage !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tlocalStorage.removeItem(`${this.namespace}.${name}`);\n\t\t\t} catch (err: any) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, {\n\t\t\t\t\terror: `Delete operation failed: ${String(err)}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clear all storage for this namespace\n\t */\n\tclear(): void {\n\t\tif (typeof localStorage === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\n\t\tconst prefix = `${this.namespace}.`;\n\t\tconst keysToRemove: string[] = [];\n\n\t\t// Find all keys with this namespace\n\t\tfor (let i = 0; i < localStorage.length; i++) {\n\t\t\tconst key = localStorage.key(i);\n\t\t\tif (key && key.startsWith(prefix)) {\n\t\t\t\tkeysToRemove.push(key);\n\t\t\t}\n\t\t}\n\n\t\t// Remove them\n\t\tfor (const key of keysToRemove) {\n\t\t\tlocalStorage.removeItem(key);\n\t\t}\n\n\t\t// Clear cache\n\t\tthis.cache.clear();\n\t\tthis.pendingWrites.clear();\n\t}\n\n\t/**\n\t * Sanitize value for JSON serialization\n\t * Removes functions and handles circular references\n\t */\n\tprivate sanitize(value: any, seen = new WeakSet()): any {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn value;\n\t\t}\n\n\t\t// Primitives\n\t\tif (typeof value !== \"object\") {\n\t\t\t// Remove functions\n\t\t\tif (typeof value === \"function\") {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\n\t\t// Check for circular reference\n\t\tif (seen.has(value)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tseen.add(value);\n\n\t\t// Arrays\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value.map((item) => this.sanitize(item, seen)).filter((item) => item !== undefined);\n\t\t}\n\n\t\t// Objects\n\t\tconst result: any = {};\n\t\tfor (const key in value) {\n\t\t\tif (Object.hasOwn(value, key)) {\n\t\t\t\tconst sanitized = this.sanitize(value[key], seen);\n\t\t\t\tif (sanitized !== undefined) {\n\t\t\t\t\tresult[key] = sanitized;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate interfaceCache: {\n\t\tset: (name: string, value: unknown) => void;\n\t\tget: (name: string) => unknown;\n\t\tdelete: (name: string) => void;\n\t} | null = null;\n\n\t/**\n\t * Get storage interface for game code\n\t */\n\tgetInterface() {\n\t\tif (this.interfaceCache) {\n\t\t\treturn this.interfaceCache;\n\t\t}\n\t\tthis.interfaceCache = {\n\t\t\tset: (name: string, value: any) => this.set(name, value),\n\t\t\tget: (name: string) => this.get(name),\n\t\t\tdelete: (name: string) => this.delete(name),\n\t\t};\n\t\treturn this.interfaceCache;\n\t}\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage service - localStorage wrapper with automatic serialization
|
|
3
|
+
*/
|
|
4
|
+
declare class StorageService {
|
|
5
|
+
private namespace;
|
|
6
|
+
private cache;
|
|
7
|
+
private pendingWrites;
|
|
8
|
+
private writeTimer;
|
|
9
|
+
private runtime?;
|
|
10
|
+
constructor(namespace?: string, preserve?: boolean, runtime?: any);
|
|
11
|
+
/**
|
|
12
|
+
* Get value from storage
|
|
13
|
+
*/
|
|
14
|
+
get(name: string): any;
|
|
15
|
+
/**
|
|
16
|
+
* Set value in storage (batched write)
|
|
17
|
+
*/
|
|
18
|
+
set(name: string, value: any): void;
|
|
19
|
+
/**
|
|
20
|
+
* Flush pending writes to localStorage
|
|
21
|
+
*/
|
|
22
|
+
flush(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Check if there are pending writes and flush if needed
|
|
25
|
+
*/
|
|
26
|
+
check(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Delete a single key from storage (cache + localStorage + any pending write)
|
|
29
|
+
*/
|
|
30
|
+
delete(name: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Clear all storage for this namespace
|
|
33
|
+
*/
|
|
34
|
+
clear(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Sanitize value for JSON serialization
|
|
37
|
+
* Removes functions and handles circular references
|
|
38
|
+
*/
|
|
39
|
+
private sanitize;
|
|
40
|
+
private interfaceCache;
|
|
41
|
+
/**
|
|
42
|
+
* Get storage interface for game code
|
|
43
|
+
*/
|
|
44
|
+
getInterface(): {
|
|
45
|
+
set: (name: string, value: unknown) => void;
|
|
46
|
+
get: (name: string) => unknown;
|
|
47
|
+
delete: (name: string) => void;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { StorageService };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage service - localStorage wrapper with automatic serialization
|
|
3
|
+
*/
|
|
4
|
+
declare class StorageService {
|
|
5
|
+
private namespace;
|
|
6
|
+
private cache;
|
|
7
|
+
private pendingWrites;
|
|
8
|
+
private writeTimer;
|
|
9
|
+
private runtime?;
|
|
10
|
+
constructor(namespace?: string, preserve?: boolean, runtime?: any);
|
|
11
|
+
/**
|
|
12
|
+
* Get value from storage
|
|
13
|
+
*/
|
|
14
|
+
get(name: string): any;
|
|
15
|
+
/**
|
|
16
|
+
* Set value in storage (batched write)
|
|
17
|
+
*/
|
|
18
|
+
set(name: string, value: any): void;
|
|
19
|
+
/**
|
|
20
|
+
* Flush pending writes to localStorage
|
|
21
|
+
*/
|
|
22
|
+
flush(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Check if there are pending writes and flush if needed
|
|
25
|
+
*/
|
|
26
|
+
check(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Delete a single key from storage (cache + localStorage + any pending write)
|
|
29
|
+
*/
|
|
30
|
+
delete(name: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Clear all storage for this namespace
|
|
33
|
+
*/
|
|
34
|
+
clear(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Sanitize value for JSON serialization
|
|
37
|
+
* Removes functions and handles circular references
|
|
38
|
+
*/
|
|
39
|
+
private sanitize;
|
|
40
|
+
private interfaceCache;
|
|
41
|
+
/**
|
|
42
|
+
* Get storage interface for game code
|
|
43
|
+
*/
|
|
44
|
+
getInterface(): {
|
|
45
|
+
set: (name: string, value: unknown) => void;
|
|
46
|
+
get: (name: string) => unknown;
|
|
47
|
+
delete: (name: string) => void;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { StorageService };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { reportRuntimeError, APIErrorCode } from '@al8b/diagnostics';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
var StorageService = class {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "StorageService");
|
|
8
|
+
}
|
|
9
|
+
namespace;
|
|
10
|
+
cache = /* @__PURE__ */ new Map();
|
|
11
|
+
pendingWrites = /* @__PURE__ */ new Map();
|
|
12
|
+
writeTimer = null;
|
|
13
|
+
runtime;
|
|
14
|
+
constructor(namespace = "/l8b", preserve = false, runtime) {
|
|
15
|
+
this.namespace = namespace;
|
|
16
|
+
this.runtime = runtime;
|
|
17
|
+
if (!preserve && typeof localStorage !== "undefined") {
|
|
18
|
+
this.clear();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get value from storage
|
|
23
|
+
*/
|
|
24
|
+
get(name) {
|
|
25
|
+
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
26
|
+
reportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, {
|
|
27
|
+
key: String(name)
|
|
28
|
+
});
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (this.cache.has(name)) {
|
|
32
|
+
return this.cache.get(name);
|
|
33
|
+
}
|
|
34
|
+
if (typeof localStorage !== "undefined") {
|
|
35
|
+
try {
|
|
36
|
+
const key = `${this.namespace}.${name}`;
|
|
37
|
+
const value = localStorage.getItem(key);
|
|
38
|
+
if (value !== null) {
|
|
39
|
+
const parsed = JSON.parse(value);
|
|
40
|
+
this.cache.set(name, parsed);
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
} catch (err) {
|
|
44
|
+
reportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, {
|
|
45
|
+
error: `Get operation failed: ${String(err)}`
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Set value in storage (batched write)
|
|
53
|
+
*/
|
|
54
|
+
set(name, value) {
|
|
55
|
+
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
56
|
+
reportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, {
|
|
57
|
+
key: String(name)
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.cache.set(name, value);
|
|
62
|
+
this.pendingWrites.set(name, value);
|
|
63
|
+
if (this.writeTimer === null) {
|
|
64
|
+
const schedule = typeof window !== "undefined" && typeof window.setTimeout === "function" ? window.setTimeout.bind(window) : setTimeout;
|
|
65
|
+
this.writeTimer = schedule(() => {
|
|
66
|
+
this.flush();
|
|
67
|
+
}, 100);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Flush pending writes to localStorage
|
|
72
|
+
*/
|
|
73
|
+
flush() {
|
|
74
|
+
if (this.writeTimer !== null) {
|
|
75
|
+
clearTimeout(this.writeTimer);
|
|
76
|
+
this.writeTimer = null;
|
|
77
|
+
}
|
|
78
|
+
if (typeof localStorage === "undefined") {
|
|
79
|
+
this.pendingWrites.clear();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
for (const [name, value] of this.pendingWrites) {
|
|
83
|
+
try {
|
|
84
|
+
const key = `${this.namespace}.${name}`;
|
|
85
|
+
const serialized = JSON.stringify(this.sanitize(value));
|
|
86
|
+
localStorage.setItem(key, serialized);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
if (err.name === "QuotaExceededError" || err.code === 22) {
|
|
89
|
+
reportRuntimeError(this.runtime?.listener, APIErrorCode.E7061, {});
|
|
90
|
+
} else {
|
|
91
|
+
reportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, {
|
|
92
|
+
error: `Set operation failed: ${String(err)}`
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
this.pendingWrites.clear();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if there are pending writes and flush if needed
|
|
101
|
+
*/
|
|
102
|
+
check() {
|
|
103
|
+
if (this.pendingWrites.size > 0) {
|
|
104
|
+
this.flush();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Delete a single key from storage (cache + localStorage + any pending write)
|
|
109
|
+
*/
|
|
110
|
+
delete(name) {
|
|
111
|
+
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
112
|
+
reportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, {
|
|
113
|
+
key: String(name)
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.cache.delete(name);
|
|
118
|
+
this.pendingWrites.delete(name);
|
|
119
|
+
if (typeof localStorage !== "undefined") {
|
|
120
|
+
try {
|
|
121
|
+
localStorage.removeItem(`${this.namespace}.${name}`);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
reportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, {
|
|
124
|
+
error: `Delete operation failed: ${String(err)}`
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clear all storage for this namespace
|
|
131
|
+
*/
|
|
132
|
+
clear() {
|
|
133
|
+
if (typeof localStorage === "undefined") {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const prefix = `${this.namespace}.`;
|
|
137
|
+
const keysToRemove = [];
|
|
138
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
139
|
+
const key = localStorage.key(i);
|
|
140
|
+
if (key && key.startsWith(prefix)) {
|
|
141
|
+
keysToRemove.push(key);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const key of keysToRemove) {
|
|
145
|
+
localStorage.removeItem(key);
|
|
146
|
+
}
|
|
147
|
+
this.cache.clear();
|
|
148
|
+
this.pendingWrites.clear();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Sanitize value for JSON serialization
|
|
152
|
+
* Removes functions and handles circular references
|
|
153
|
+
*/
|
|
154
|
+
sanitize(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
155
|
+
if (value === null || value === void 0) {
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
if (typeof value !== "object") {
|
|
159
|
+
if (typeof value === "function") {
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
if (seen.has(value)) {
|
|
165
|
+
return void 0;
|
|
166
|
+
}
|
|
167
|
+
seen.add(value);
|
|
168
|
+
if (Array.isArray(value)) {
|
|
169
|
+
return value.map((item) => this.sanitize(item, seen)).filter((item) => item !== void 0);
|
|
170
|
+
}
|
|
171
|
+
const result = {};
|
|
172
|
+
for (const key in value) {
|
|
173
|
+
if (Object.hasOwn(value, key)) {
|
|
174
|
+
const sanitized = this.sanitize(value[key], seen);
|
|
175
|
+
if (sanitized !== void 0) {
|
|
176
|
+
result[key] = sanitized;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
interfaceCache = null;
|
|
183
|
+
/**
|
|
184
|
+
* Get storage interface for game code
|
|
185
|
+
*/
|
|
186
|
+
getInterface() {
|
|
187
|
+
if (this.interfaceCache) {
|
|
188
|
+
return this.interfaceCache;
|
|
189
|
+
}
|
|
190
|
+
this.interfaceCache = {
|
|
191
|
+
set: /* @__PURE__ */ __name((name, value) => this.set(name, value), "set"),
|
|
192
|
+
get: /* @__PURE__ */ __name((name) => this.get(name), "get"),
|
|
193
|
+
delete: /* @__PURE__ */ __name((name) => this.delete(name), "delete")
|
|
194
|
+
};
|
|
195
|
+
return this.interfaceCache;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export { StorageService };
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
201
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/index.ts"],"names":["StorageService","namespace","cache","Map","pendingWrites","writeTimer","runtime","preserve","localStorage","clear","get","name","trim","reportRuntimeError","listener","APIErrorCode","E7063","key","String","has","value","getItem","parsed","JSON","parse","set","err","E7062","error","schedule","window","setTimeout","bind","flush","clearTimeout","serialized","stringify","sanitize","setItem","code","E7061","check","size","delete","removeItem","prefix","keysToRemove","i","length","startsWith","push","seen","WeakSet","undefined","add","Array","isArray","map","item","filter","result","Object","hasOwn","sanitized","interfaceCache","getInterface"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Storage service - localStorage wrapper with automatic serialization\n */\n\nimport { APIErrorCode, reportRuntimeError } from \"@al8b/diagnostics\";\n\nexport class StorageService {\n\tprivate namespace: string;\n\tprivate cache: Map<string, any> = new Map();\n\tprivate pendingWrites: Map<string, any> = new Map();\n\tprivate writeTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate runtime?: any;\n\n\tconstructor(namespace = \"/l8b\", preserve = false, runtime?: any) {\n\t\tthis.namespace = namespace;\n\t\tthis.runtime = runtime;\n\n\t\t// Clear storage if not preserving\n\t\tif (!preserve && typeof localStorage !== \"undefined\") {\n\t\t\tthis.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Get value from storage\n\t */\n\tget(name: string): any {\n\t\t// Validate storage key\n\t\tif (!name || typeof name !== \"string\" || name.trim() === \"\") {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, { key: String(name) });\n\t\t\treturn null;\n\t\t}\n\n\t\t// Check cache first\n\t\tif (this.cache.has(name)) {\n\t\t\treturn this.cache.get(name);\n\t\t}\n\n\t\t// Try localStorage\n\t\tif (typeof localStorage !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tconst key = `${this.namespace}.${name}`;\n\t\t\t\tconst value = localStorage.getItem(key);\n\t\t\t\tif (value !== null) {\n\t\t\t\t\tconst parsed = JSON.parse(value);\n\t\t\t\t\tthis.cache.set(name, parsed);\n\t\t\t\t\treturn parsed;\n\t\t\t\t}\n\t\t\t} catch (err: any) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, { error: `Get operation failed: ${String(err)}` });\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Set value in storage (batched write)\n\t */\n\tset(name: string, value: any): void {\n\t\t// Validate storage key\n\t\tif (!name || typeof name !== \"string\" || name.trim() === \"\") {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, { key: String(name) });\n\t\t\treturn;\n\t\t}\n\n\t\t// Update cache\n\t\tthis.cache.set(name, value);\n\n\t\t// Queue write\n\t\tthis.pendingWrites.set(name, value);\n\n\t\t// Schedule batch write\n\t\tif (this.writeTimer === null) {\n\t\t\tconst schedule =\n\t\t\t\ttypeof window !== \"undefined\" && typeof window.setTimeout === \"function\"\n\t\t\t\t\t? window.setTimeout.bind(window)\n\t\t\t\t\t: setTimeout;\n\n\t\t\tthis.writeTimer = schedule(() => {\n\t\t\t\tthis.flush();\n\t\t\t}, 100);\n\t\t}\n\t}\n\n\t/**\n\t * Flush pending writes to localStorage\n\t */\n\tflush(): void {\n\t\tif (this.writeTimer !== null) {\n\t\t\tclearTimeout(this.writeTimer);\n\t\t\tthis.writeTimer = null;\n\t\t}\n\n\t\tif (typeof localStorage === \"undefined\") {\n\t\t\tthis.pendingWrites.clear();\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const [name, value] of this.pendingWrites) {\n\t\t\ttry {\n\t\t\t\tconst key = `${this.namespace}.${name}`;\n\t\t\t\tconst serialized = JSON.stringify(this.sanitize(value));\n\t\t\t\tlocalStorage.setItem(key, serialized);\n\t\t\t} catch (err: any) {\n\t\t\t\t// Check for quota exceeded error\n\t\t\t\tif (err.name === \"QuotaExceededError\" || err.code === 22) {\n\t\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7061, {});\n\t\t\t\t} else {\n\t\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, { error: `Set operation failed: ${String(err)}` });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.pendingWrites.clear();\n\t}\n\n\t/**\n\t * Check if there are pending writes and flush if needed\n\t */\n\tcheck(): void {\n\t\tif (this.pendingWrites.size > 0) {\n\t\t\tthis.flush();\n\t\t}\n\t}\n\n\t/**\n\t * Delete a single key from storage (cache + localStorage + any pending write)\n\t */\n\tdelete(name: string): void {\n\t\tif (!name || typeof name !== \"string\" || name.trim() === \"\") {\n\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7063, { key: String(name) });\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove from in-memory state\n\t\tthis.cache.delete(name);\n\t\tthis.pendingWrites.delete(name);\n\n\t\t// Remove from localStorage\n\t\tif (typeof localStorage !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tlocalStorage.removeItem(`${this.namespace}.${name}`);\n\t\t\t} catch (err: any) {\n\t\t\t\treportRuntimeError(this.runtime?.listener, APIErrorCode.E7062, {\n\t\t\t\t\terror: `Delete operation failed: ${String(err)}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clear all storage for this namespace\n\t */\n\tclear(): void {\n\t\tif (typeof localStorage === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\n\t\tconst prefix = `${this.namespace}.`;\n\t\tconst keysToRemove: string[] = [];\n\n\t\t// Find all keys with this namespace\n\t\tfor (let i = 0; i < localStorage.length; i++) {\n\t\t\tconst key = localStorage.key(i);\n\t\t\tif (key && key.startsWith(prefix)) {\n\t\t\t\tkeysToRemove.push(key);\n\t\t\t}\n\t\t}\n\n\t\t// Remove them\n\t\tfor (const key of keysToRemove) {\n\t\t\tlocalStorage.removeItem(key);\n\t\t}\n\n\t\t// Clear cache\n\t\tthis.cache.clear();\n\t\tthis.pendingWrites.clear();\n\t}\n\n\t/**\n\t * Sanitize value for JSON serialization\n\t * Removes functions and handles circular references\n\t */\n\tprivate sanitize(value: any, seen = new WeakSet()): any {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn value;\n\t\t}\n\n\t\t// Primitives\n\t\tif (typeof value !== \"object\") {\n\t\t\t// Remove functions\n\t\t\tif (typeof value === \"function\") {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\n\t\t// Check for circular reference\n\t\tif (seen.has(value)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tseen.add(value);\n\n\t\t// Arrays\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value.map((item) => this.sanitize(item, seen)).filter((item) => item !== undefined);\n\t\t}\n\n\t\t// Objects\n\t\tconst result: any = {};\n\t\tfor (const key in value) {\n\t\t\tif (Object.hasOwn(value, key)) {\n\t\t\t\tconst sanitized = this.sanitize(value[key], seen);\n\t\t\t\tif (sanitized !== undefined) {\n\t\t\t\t\tresult[key] = sanitized;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate interfaceCache: {\n\t\tset: (name: string, value: unknown) => void;\n\t\tget: (name: string) => unknown;\n\t\tdelete: (name: string) => void;\n\t} | null = null;\n\n\t/**\n\t * Get storage interface for game code\n\t */\n\tgetInterface() {\n\t\tif (this.interfaceCache) {\n\t\t\treturn this.interfaceCache;\n\t\t}\n\t\tthis.interfaceCache = {\n\t\t\tset: (name: string, value: any) => this.set(name, value),\n\t\t\tget: (name: string) => this.get(name),\n\t\t\tdelete: (name: string) => this.delete(name),\n\t\t};\n\t\treturn this.interfaceCache;\n\t}\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@al8b/io",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "IO and Storage utilities for L8B Engine",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist/**/*",
|
|
8
|
+
"package.json",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"clean": "bun --bun ../../../scripts/clean-package.mjs dist",
|
|
17
|
+
"test": "vitest run --passWithNoTests",
|
|
18
|
+
"typecheck": "tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@al8b/diagnostics": "workspace:*"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"tsup": "^8.0.0",
|
|
25
|
+
"typescript": "^5.9.3"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
}
|
|
30
|
+
}
|