@rcrsr/rill 0.8.0 → 0.8.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 +28 -0
- package/dist/error-registry.d.ts.map +1 -1
- package/dist/error-registry.js +164 -0
- package/dist/error-registry.js.map +1 -1
- package/dist/ext/crypto/index.d.ts +32 -0
- package/dist/ext/crypto/index.d.ts.map +1 -0
- package/dist/ext/crypto/index.js +143 -0
- package/dist/ext/crypto/index.js.map +1 -0
- package/dist/ext/exec/index.d.ts +45 -0
- package/dist/ext/exec/index.d.ts.map +1 -0
- package/dist/ext/exec/index.js +168 -0
- package/dist/ext/exec/index.js.map +1 -0
- package/dist/ext/exec/runner.d.ts +62 -0
- package/dist/ext/exec/runner.d.ts.map +1 -0
- package/dist/ext/exec/runner.js +168 -0
- package/dist/ext/exec/runner.js.map +1 -0
- package/dist/ext/fetch/index.d.ts +68 -0
- package/dist/ext/fetch/index.d.ts.map +1 -0
- package/dist/ext/fetch/index.js +259 -0
- package/dist/ext/fetch/index.js.map +1 -0
- package/dist/ext/fetch/request.d.ts +90 -0
- package/dist/ext/fetch/request.d.ts.map +1 -0
- package/dist/ext/fetch/request.js +413 -0
- package/dist/ext/fetch/request.js.map +1 -0
- package/dist/ext/fs/index.d.ts +39 -0
- package/dist/ext/fs/index.d.ts.map +1 -0
- package/dist/ext/fs/index.js +560 -0
- package/dist/ext/fs/index.js.map +1 -0
- package/dist/ext/fs/sandbox.d.ts +78 -0
- package/dist/ext/fs/sandbox.d.ts.map +1 -0
- package/dist/ext/fs/sandbox.js +208 -0
- package/dist/ext/fs/sandbox.js.map +1 -0
- package/dist/ext/kv/index.d.ts +46 -0
- package/dist/ext/kv/index.d.ts.map +1 -0
- package/dist/ext/kv/index.js +215 -0
- package/dist/ext/kv/index.js.map +1 -0
- package/dist/ext/kv/store.d.ts +46 -0
- package/dist/ext/kv/store.d.ts.map +1 -0
- package/dist/ext/kv/store.js +256 -0
- package/dist/ext/kv/store.js.map +1 -0
- package/dist/generated/version-data.d.ts +1 -1
- package/dist/generated/version-data.js +2 -2
- package/package.json +37 -11
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fs Extension Sandbox Module
|
|
3
|
+
*
|
|
4
|
+
* Path resolution and validation implementing 9-step security sequence.
|
|
5
|
+
* Prevents path traversal and symlink attacks via realpath() defense.
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'node:fs/promises';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { RuntimeError } from '../../error-classes.js';
|
|
10
|
+
// ============================================================
|
|
11
|
+
// PATH RESOLUTION
|
|
12
|
+
// ============================================================
|
|
13
|
+
/**
|
|
14
|
+
* Resolves and validates path within mount boundaries.
|
|
15
|
+
*
|
|
16
|
+
* 9-step path resolution sequence (spec lines 331-342):
|
|
17
|
+
* 1. Resolve mount name to MountConfig
|
|
18
|
+
* 2. Use mount's resolved physical path (from creation time)
|
|
19
|
+
* 3. Join resolved mount base with script's relative path argument
|
|
20
|
+
* 4. Normalize with path.resolve() to collapse .. segments
|
|
21
|
+
* 5. Resolve final path with fs.realpath() (symlink defense)
|
|
22
|
+
* 6. Verify resolved path starts with mount's resolved base (startsWith())
|
|
23
|
+
* 7. If glob set, verify filename matches pattern
|
|
24
|
+
* 8. Check mode permits operation
|
|
25
|
+
* 9. Return validated path for node:fs operation
|
|
26
|
+
*
|
|
27
|
+
* @param mountName - Mount identifier from script
|
|
28
|
+
* @param relativePath - Script-provided path relative to mount
|
|
29
|
+
* @param mounts - Mount configuration map
|
|
30
|
+
* @param operation - Operation type for mode validation
|
|
31
|
+
* @param createMode - For write operations creating new files (checks parent dir)
|
|
32
|
+
* @returns Validated absolute path
|
|
33
|
+
* @throws RuntimeError - EC-1 (unknown mount), EC-2 (path escape), EC-3 (glob), EC-4 (mode), EC-7 (permission)
|
|
34
|
+
*/
|
|
35
|
+
export async function resolvePath(mountName, relativePath, mounts, operation, createMode = false) {
|
|
36
|
+
// Step 1: Resolve mount name to MountConfig
|
|
37
|
+
// EC-1: Unknown mount name
|
|
38
|
+
const mount = mounts[mountName];
|
|
39
|
+
if (!mount) {
|
|
40
|
+
throw new RuntimeError('RILL-R017', `mount "${mountName}" not configured`, undefined, { mountName });
|
|
41
|
+
}
|
|
42
|
+
// Step 2: Use mount's resolved physical path (set at creation time)
|
|
43
|
+
const mountBase = mount.resolvedPath;
|
|
44
|
+
if (!mountBase) {
|
|
45
|
+
throw new RuntimeError('RILL-R017', `mount "${mountName}" not initialized (missing resolvedPath)`, undefined, { mountName });
|
|
46
|
+
}
|
|
47
|
+
// Step 3: Join resolved mount base with script's relative path
|
|
48
|
+
const joined = path.join(mountBase, relativePath);
|
|
49
|
+
// Step 4: Normalize with path.resolve() to collapse .. segments
|
|
50
|
+
const normalized = path.resolve(joined);
|
|
51
|
+
// Step 6 (early check): Verify normalized path starts with mount base before realpath
|
|
52
|
+
// This catches path traversal attempts even when the target doesn't exist
|
|
53
|
+
// EC-2: Path escapes boundary
|
|
54
|
+
if (!normalized.startsWith(mountBase + path.sep) &&
|
|
55
|
+
normalized !== mountBase) {
|
|
56
|
+
throw new RuntimeError('RILL-R018', 'path escapes mount boundary', undefined, { mountName, path: relativePath, normalized, mountBase });
|
|
57
|
+
}
|
|
58
|
+
// Step 5: Resolve final path with fs.realpath() (symlink defense)
|
|
59
|
+
// For write operations creating new files, resolve parent directory instead
|
|
60
|
+
let resolvedPath;
|
|
61
|
+
try {
|
|
62
|
+
if (createMode) {
|
|
63
|
+
// New file write: resolve parent directory
|
|
64
|
+
const parentDir = path.dirname(normalized);
|
|
65
|
+
const resolvedParent = await fs.realpath(parentDir);
|
|
66
|
+
const filename = path.basename(normalized);
|
|
67
|
+
resolvedPath = path.join(resolvedParent, filename);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Existing file: resolve full path
|
|
71
|
+
resolvedPath = await fs.realpath(normalized);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// EC-7: Permission denied or ENOENT
|
|
76
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
77
|
+
const code = error.code;
|
|
78
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
79
|
+
throw new RuntimeError('RILL-R021', `permission denied: ${normalized}`, undefined, { path: normalized, code });
|
|
80
|
+
}
|
|
81
|
+
// For ENOENT on createMode, this is expected (new file)
|
|
82
|
+
// For ENOENT on read mode, propagate as path doesn't exist
|
|
83
|
+
if (code === 'ENOENT') {
|
|
84
|
+
if (createMode) {
|
|
85
|
+
// Parent directory doesn't exist for new file write
|
|
86
|
+
throw new RuntimeError('RILL-R021', `parent directory does not exist: ${path.dirname(normalized)}`, undefined, { path: normalized });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// File doesn't exist for read
|
|
90
|
+
throw new RuntimeError('RILL-R021', `file not found: ${normalized}`, undefined, { path: normalized });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
// Step 6 (post-realpath): Verify resolved path still within mount (symlink defense)
|
|
97
|
+
// EC-2: Path escapes boundary via symlink
|
|
98
|
+
if (!resolvedPath.startsWith(mountBase + path.sep) &&
|
|
99
|
+
resolvedPath !== mountBase) {
|
|
100
|
+
throw new RuntimeError('RILL-R018', 'path escapes mount boundary', undefined, { mountName, path: relativePath, resolvedPath, mountBase });
|
|
101
|
+
}
|
|
102
|
+
// Step 7: If glob set, verify filename matches pattern
|
|
103
|
+
// EC-3: Glob mismatch
|
|
104
|
+
if (mount.glob) {
|
|
105
|
+
const filename = path.basename(resolvedPath);
|
|
106
|
+
if (!matchesGlob(filename, mount.glob)) {
|
|
107
|
+
throw new RuntimeError('RILL-R019', `file type not permitted in mount "${mountName}"`, undefined, { mountName, glob: mount.glob, filename });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Step 8: Check mode permits operation
|
|
111
|
+
// EC-4: Mode violation
|
|
112
|
+
if (!checkMode(mount.mode, operation)) {
|
|
113
|
+
throw new RuntimeError('RILL-R020', `mount "${mountName}" does not permit ${operation}`, undefined, { mountName, mode: mount.mode, operation });
|
|
114
|
+
}
|
|
115
|
+
// Step 9: Return validated path for node:fs operation
|
|
116
|
+
return resolvedPath;
|
|
117
|
+
}
|
|
118
|
+
// ============================================================
|
|
119
|
+
// GLOB MATCHING
|
|
120
|
+
// ============================================================
|
|
121
|
+
/**
|
|
122
|
+
* Simple glob pattern matching (spec lines 346-354).
|
|
123
|
+
*
|
|
124
|
+
* Supported patterns:
|
|
125
|
+
* - *.csv - Files ending in .csv
|
|
126
|
+
* - *.{json,yaml} - Files ending in .json or .yaml
|
|
127
|
+
* - * - All files (default when omitted)
|
|
128
|
+
* - **\/*.csv - CSV files at any depth (for find() only)
|
|
129
|
+
*
|
|
130
|
+
* No third-party glob library. Uses path.extname() checks.
|
|
131
|
+
*
|
|
132
|
+
* @param filename - Filename to match (basename only)
|
|
133
|
+
* @param pattern - Glob pattern
|
|
134
|
+
* @returns true if filename matches pattern
|
|
135
|
+
*/
|
|
136
|
+
export function matchesGlob(filename, pattern) {
|
|
137
|
+
// Pattern: * (all files)
|
|
138
|
+
if (pattern === '*') {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
// Pattern: *.ext (single extension)
|
|
142
|
+
if (pattern.startsWith('*.') && !pattern.includes('{')) {
|
|
143
|
+
const ext = pattern.slice(1); // Remove leading *
|
|
144
|
+
return filename.endsWith(ext);
|
|
145
|
+
}
|
|
146
|
+
// Pattern: *.{ext1,ext2} (multiple extensions)
|
|
147
|
+
if (pattern.startsWith('*.{') && pattern.endsWith('}')) {
|
|
148
|
+
const extensionsStr = pattern.slice(3, -1); // Extract between *.{ and }
|
|
149
|
+
const extensions = extensionsStr.split(',').map((e) => `.${e.trim()}`);
|
|
150
|
+
return extensions.some((ext) => filename.endsWith(ext));
|
|
151
|
+
}
|
|
152
|
+
// Pattern: **/*.ext (recursive, any depth)
|
|
153
|
+
if (pattern.startsWith('**/')) {
|
|
154
|
+
const subPattern = pattern.slice(3); // Remove leading **/
|
|
155
|
+
return matchesGlob(filename, subPattern);
|
|
156
|
+
}
|
|
157
|
+
// Unknown pattern: no match (conservative)
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
// ============================================================
|
|
161
|
+
// MODE VALIDATION
|
|
162
|
+
// ============================================================
|
|
163
|
+
/**
|
|
164
|
+
* Checks if mount mode permits operation.
|
|
165
|
+
*
|
|
166
|
+
* @param mode - Mount access mode
|
|
167
|
+
* @param operation - Operation type
|
|
168
|
+
* @returns true if operation permitted
|
|
169
|
+
*/
|
|
170
|
+
export function checkMode(mode, operation) {
|
|
171
|
+
if (mode === 'read-write')
|
|
172
|
+
return true;
|
|
173
|
+
if (mode === 'read' && operation === 'read')
|
|
174
|
+
return true;
|
|
175
|
+
if (mode === 'write' && operation === 'write')
|
|
176
|
+
return true;
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
// ============================================================
|
|
180
|
+
// MOUNT INITIALIZATION
|
|
181
|
+
// ============================================================
|
|
182
|
+
/**
|
|
183
|
+
* Resolves mount path at creation time (Step 2 of sequence).
|
|
184
|
+
*
|
|
185
|
+
* Mutates MountConfig to set resolvedPath field.
|
|
186
|
+
*
|
|
187
|
+
* @param mount - Mount configuration
|
|
188
|
+
* @throws RuntimeError - If mount path invalid or inaccessible
|
|
189
|
+
*/
|
|
190
|
+
export async function initializeMount(mount) {
|
|
191
|
+
try {
|
|
192
|
+
// Resolve mount path with fs.realpath() at creation time (spec line 333)
|
|
193
|
+
mount.resolvedPath = await fs.realpath(mount.path);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
197
|
+
const code = error.code;
|
|
198
|
+
if (code === 'ENOENT') {
|
|
199
|
+
throw new RuntimeError('RILL-R017', `mount path does not exist: ${mount.path}`, undefined, { path: mount.path });
|
|
200
|
+
}
|
|
201
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
202
|
+
throw new RuntimeError('RILL-R021', `permission denied: ${mount.path}`, undefined, { path: mount.path, code });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../src/ext/fs/sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KV Extension Factory
|
|
3
|
+
*
|
|
4
|
+
* Provides key-value store operations with JSON persistence and schema validation.
|
|
5
|
+
* Supports both open mode (any key/value) and declared mode (schema-defined keys only).
|
|
6
|
+
*/
|
|
7
|
+
import type { ExtensionResult } from '../../runtime/ext/extensions.js';
|
|
8
|
+
import { type SchemaEntry } from './store.js';
|
|
9
|
+
/** KV extension configuration */
|
|
10
|
+
export interface KvConfig {
|
|
11
|
+
/** Path to store file */
|
|
12
|
+
store: string;
|
|
13
|
+
/** Schema definitions (optional, enables declared mode) */
|
|
14
|
+
schema?: Record<string, SchemaEntry> | undefined;
|
|
15
|
+
/** Maximum entries (default: 10000) */
|
|
16
|
+
maxEntries?: number | undefined;
|
|
17
|
+
/** Maximum value size in bytes (default: 102400 = 100KB) */
|
|
18
|
+
maxValueSize?: number | undefined;
|
|
19
|
+
/** Maximum store size in bytes (default: 10485760 = 10MB) */
|
|
20
|
+
maxStoreSize?: number | undefined;
|
|
21
|
+
/** Write policy: 'dispose' (default) or 'immediate' */
|
|
22
|
+
writePolicy?: 'dispose' | 'immediate' | undefined;
|
|
23
|
+
}
|
|
24
|
+
export type { SchemaEntry };
|
|
25
|
+
/**
|
|
26
|
+
* Create KV extension with persistent storage.
|
|
27
|
+
*
|
|
28
|
+
* Initializes store by loading existing file or creating with schema defaults.
|
|
29
|
+
* Returns 8 functions: get, set, delete, keys, has, clear, getAll, schema.
|
|
30
|
+
*
|
|
31
|
+
* @param config - Store configuration
|
|
32
|
+
* @returns ExtensionResult with 8 KV functions and dispose handler
|
|
33
|
+
* @throws RuntimeError if store file is corrupt (EC-25)
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const kvExt = createKvExtension({
|
|
38
|
+
* store: './data/state.json',
|
|
39
|
+
* schema: {
|
|
40
|
+
* count: { type: 'number', default: 0 }
|
|
41
|
+
* }
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function createKvExtension(config: KvConfig): ExtensionResult;
|
|
46
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ext/kv/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAM3D,iCAAiC;AACjC,MAAM,WAAW,QAAQ;IACvB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,SAAS,CAAC;IACjD,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,uDAAuD;IACvD,WAAW,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;CACnD;AAGD,YAAY,EAAE,WAAW,EAAE,CAAC;AAM5B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,CA2MnE"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KV Extension Factory
|
|
3
|
+
*
|
|
4
|
+
* Provides key-value store operations with JSON persistence and schema validation.
|
|
5
|
+
* Supports both open mode (any key/value) and declared mode (schema-defined keys only).
|
|
6
|
+
*/
|
|
7
|
+
import { createStore } from './store.js';
|
|
8
|
+
// ============================================================
|
|
9
|
+
// FACTORY
|
|
10
|
+
// ============================================================
|
|
11
|
+
/**
|
|
12
|
+
* Create KV extension with persistent storage.
|
|
13
|
+
*
|
|
14
|
+
* Initializes store by loading existing file or creating with schema defaults.
|
|
15
|
+
* Returns 8 functions: get, set, delete, keys, has, clear, getAll, schema.
|
|
16
|
+
*
|
|
17
|
+
* @param config - Store configuration
|
|
18
|
+
* @returns ExtensionResult with 8 KV functions and dispose handler
|
|
19
|
+
* @throws RuntimeError if store file is corrupt (EC-25)
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const kvExt = createKvExtension({
|
|
24
|
+
* store: './data/state.json',
|
|
25
|
+
* schema: {
|
|
26
|
+
* count: { type: 'number', default: 0 }
|
|
27
|
+
* }
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function createKvExtension(config) {
|
|
32
|
+
// Apply defaults
|
|
33
|
+
const maxEntries = config.maxEntries ?? 10000;
|
|
34
|
+
const maxValueSize = config.maxValueSize ?? 102400; // 100KB
|
|
35
|
+
const maxStoreSize = config.maxStoreSize ?? 10485760; // 10MB
|
|
36
|
+
const writePolicy = config.writePolicy ?? 'dispose';
|
|
37
|
+
// Store will be initialized lazily on first operation
|
|
38
|
+
let storePromise = null;
|
|
39
|
+
let storeInstance = null;
|
|
40
|
+
// Helper to get or create store
|
|
41
|
+
const getStore = async () => {
|
|
42
|
+
if (storeInstance)
|
|
43
|
+
return storeInstance;
|
|
44
|
+
if (!storePromise) {
|
|
45
|
+
storePromise = createStore({
|
|
46
|
+
store: config.store,
|
|
47
|
+
schema: config.schema,
|
|
48
|
+
maxEntries,
|
|
49
|
+
maxValueSize,
|
|
50
|
+
maxStoreSize,
|
|
51
|
+
writePolicy,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
storeInstance = await storePromise;
|
|
55
|
+
return storeInstance;
|
|
56
|
+
};
|
|
57
|
+
// ============================================================
|
|
58
|
+
// FUNCTIONS
|
|
59
|
+
// ============================================================
|
|
60
|
+
/**
|
|
61
|
+
* Get value or schema default.
|
|
62
|
+
* IR-15, EC-20 (key not in schema)
|
|
63
|
+
* Returns empty string for missing keys in open mode.
|
|
64
|
+
*/
|
|
65
|
+
const get = async (args) => {
|
|
66
|
+
const store = await getStore();
|
|
67
|
+
const key = args[0];
|
|
68
|
+
const value = store.get(key);
|
|
69
|
+
// In rill, functions cannot return undefined - return empty string for missing keys in open mode
|
|
70
|
+
return value !== undefined ? value : '';
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Set value with validation.
|
|
74
|
+
* IR-16, EC-20-24
|
|
75
|
+
*/
|
|
76
|
+
const set = async (args) => {
|
|
77
|
+
const store = await getStore();
|
|
78
|
+
const key = args[0];
|
|
79
|
+
const value = args[1];
|
|
80
|
+
await store.set(key, value);
|
|
81
|
+
return true;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Delete key.
|
|
85
|
+
* IR-17
|
|
86
|
+
*/
|
|
87
|
+
const deleteKey = async (args) => {
|
|
88
|
+
const store = await getStore();
|
|
89
|
+
const key = args[0];
|
|
90
|
+
return store.delete(key);
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Get all keys.
|
|
94
|
+
* IR-18
|
|
95
|
+
*/
|
|
96
|
+
const keys = async () => {
|
|
97
|
+
const store = await getStore();
|
|
98
|
+
return store.keys();
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Check key existence.
|
|
102
|
+
* IR-19
|
|
103
|
+
*/
|
|
104
|
+
const has = async (args) => {
|
|
105
|
+
const store = await getStore();
|
|
106
|
+
const key = args[0];
|
|
107
|
+
return store.has(key);
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Clear all keys (restores schema defaults if declared mode).
|
|
111
|
+
* IR-20
|
|
112
|
+
*/
|
|
113
|
+
const clear = async () => {
|
|
114
|
+
const store = await getStore();
|
|
115
|
+
store.clear();
|
|
116
|
+
return true;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Get all entries as dict.
|
|
120
|
+
* IR-21
|
|
121
|
+
*/
|
|
122
|
+
const getAll = async () => {
|
|
123
|
+
const store = await getStore();
|
|
124
|
+
return store.getAll();
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Get schema information (empty list in open mode).
|
|
128
|
+
* IR-22
|
|
129
|
+
*/
|
|
130
|
+
const schema = async () => {
|
|
131
|
+
if (!config.schema) {
|
|
132
|
+
return []; // Open mode - no schema
|
|
133
|
+
}
|
|
134
|
+
// Declared mode - return schema entries as list of dicts
|
|
135
|
+
const result = [];
|
|
136
|
+
for (const [key, entry] of Object.entries(config.schema)) {
|
|
137
|
+
result.push({
|
|
138
|
+
key,
|
|
139
|
+
type: entry.type,
|
|
140
|
+
description: entry.description ?? '',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
};
|
|
145
|
+
// ============================================================
|
|
146
|
+
// DISPOSE
|
|
147
|
+
// ============================================================
|
|
148
|
+
/**
|
|
149
|
+
* Flush state to disk on dispose.
|
|
150
|
+
*/
|
|
151
|
+
const dispose = async () => {
|
|
152
|
+
if (storeInstance) {
|
|
153
|
+
await storeInstance.flush();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
// ============================================================
|
|
157
|
+
// EXTENSION RESULT
|
|
158
|
+
// ============================================================
|
|
159
|
+
const result = {
|
|
160
|
+
get: {
|
|
161
|
+
params: [{ name: 'key', type: 'string', description: 'Key to retrieve' }],
|
|
162
|
+
fn: get,
|
|
163
|
+
description: 'Get value or schema default',
|
|
164
|
+
returnType: 'any',
|
|
165
|
+
},
|
|
166
|
+
set: {
|
|
167
|
+
params: [
|
|
168
|
+
{ name: 'key', type: 'string', description: 'Key to set' },
|
|
169
|
+
{ name: 'value', type: 'string', description: 'Value to store' },
|
|
170
|
+
],
|
|
171
|
+
fn: set,
|
|
172
|
+
description: 'Set value with validation',
|
|
173
|
+
returnType: 'bool',
|
|
174
|
+
},
|
|
175
|
+
delete: {
|
|
176
|
+
params: [{ name: 'key', type: 'string', description: 'Key to delete' }],
|
|
177
|
+
fn: deleteKey,
|
|
178
|
+
description: 'Delete key',
|
|
179
|
+
returnType: 'bool',
|
|
180
|
+
},
|
|
181
|
+
keys: {
|
|
182
|
+
params: [],
|
|
183
|
+
fn: keys,
|
|
184
|
+
description: 'Get all keys',
|
|
185
|
+
returnType: 'list',
|
|
186
|
+
},
|
|
187
|
+
has: {
|
|
188
|
+
params: [{ name: 'key', type: 'string', description: 'Key to check' }],
|
|
189
|
+
fn: has,
|
|
190
|
+
description: 'Check key existence',
|
|
191
|
+
returnType: 'bool',
|
|
192
|
+
},
|
|
193
|
+
clear: {
|
|
194
|
+
params: [],
|
|
195
|
+
fn: clear,
|
|
196
|
+
description: 'Clear all keys',
|
|
197
|
+
returnType: 'bool',
|
|
198
|
+
},
|
|
199
|
+
getAll: {
|
|
200
|
+
params: [],
|
|
201
|
+
fn: getAll,
|
|
202
|
+
description: 'Get all entries as dict',
|
|
203
|
+
returnType: 'dict',
|
|
204
|
+
},
|
|
205
|
+
schema: {
|
|
206
|
+
params: [],
|
|
207
|
+
fn: schema,
|
|
208
|
+
description: 'Get schema information',
|
|
209
|
+
returnType: 'list',
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
result.dispose = dispose;
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ext/kv/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KV Store Implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides JSON-based key-value persistence with schema validation.
|
|
5
|
+
* Lifecycle: Load (read store file) -> Execute (in-memory operations) -> Flush (atomic write on dispose)
|
|
6
|
+
*/
|
|
7
|
+
import type { RillValue } from '../../runtime/core/values.js';
|
|
8
|
+
/** Schema entry defining type and default for a key */
|
|
9
|
+
export interface SchemaEntry {
|
|
10
|
+
type: 'string' | 'number' | 'bool' | 'list' | 'dict';
|
|
11
|
+
default: RillValue;
|
|
12
|
+
description?: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
/** Store configuration */
|
|
15
|
+
export interface StoreConfig {
|
|
16
|
+
/** Path to store file */
|
|
17
|
+
store: string;
|
|
18
|
+
/** Schema definitions (optional, enables declared mode) */
|
|
19
|
+
schema?: Record<string, SchemaEntry> | undefined;
|
|
20
|
+
/** Maximum entries (default: 10000) */
|
|
21
|
+
maxEntries?: number | undefined;
|
|
22
|
+
/** Maximum value size in bytes (default: 102400 = 100KB) */
|
|
23
|
+
maxValueSize?: number | undefined;
|
|
24
|
+
/** Maximum store size in bytes (default: 10485760 = 10MB) */
|
|
25
|
+
maxStoreSize?: number | undefined;
|
|
26
|
+
/** Write policy: 'dispose' (default) or 'immediate' */
|
|
27
|
+
writePolicy?: 'dispose' | 'immediate' | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create KV store with JSON persistence.
|
|
31
|
+
*
|
|
32
|
+
* @param config - Store configuration
|
|
33
|
+
* @returns Store operations object
|
|
34
|
+
* @throws RuntimeError if store file is corrupt (EC-25)
|
|
35
|
+
*/
|
|
36
|
+
export declare function createStore(config: StoreConfig): Promise<{
|
|
37
|
+
get: (key: string) => RillValue | undefined;
|
|
38
|
+
set: (key: string, value: RillValue) => Promise<void>;
|
|
39
|
+
delete: (key: string) => boolean;
|
|
40
|
+
keys: () => string[];
|
|
41
|
+
has: (key: string) => boolean;
|
|
42
|
+
clear: () => void;
|
|
43
|
+
getAll: () => Record<string, RillValue>;
|
|
44
|
+
flush: () => Promise<void>;
|
|
45
|
+
}>;
|
|
46
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/ext/kv/store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAM9D,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACrD,OAAO,EAAE,SAAS,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,0BAA0B;AAC1B,MAAM,WAAW,WAAW;IAC1B,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,SAAS,CAAC;IACjD,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,uDAAuD;IACvD,WAAW,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;CACnD;AAMD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC;IAC9D,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,CAAC;IAC5C,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,IAAI,EAAE,MAAM,MAAM,EAAE,CAAC;IACrB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC,CAmTD"}
|