@scriptdb/system-modules 1.0.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 +397 -0
- package/dist/index.js +320 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# @scriptdb/system-modules
|
|
2
|
+
|
|
3
|
+
System module resolver for the script database, providing built-in modules for creating, updating, removing, and saving code snippets.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dynamic module loading**: Load system modules at runtime
|
|
8
|
+
- **Code snippet management**: Create, update, remove, and save code snippets
|
|
9
|
+
- **Module registry**: Maintain a registry of all available system modules
|
|
10
|
+
- **Require-like functionality**: Access modules using a require-like API
|
|
11
|
+
- **Import functionality**: Access modules using an import-like API
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun add @scriptdb/system-modules
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { SystemModuleResolver } from '@scriptdb/system-modules';
|
|
23
|
+
|
|
24
|
+
// Get the system modules context
|
|
25
|
+
const context = await SystemModuleResolver();
|
|
26
|
+
|
|
27
|
+
// Access the built-in modules
|
|
28
|
+
const create = context.create;
|
|
29
|
+
const update = context.update;
|
|
30
|
+
const remove = context.remove;
|
|
31
|
+
const save = context.save;
|
|
32
|
+
|
|
33
|
+
// Create a new code snippet
|
|
34
|
+
create('myDatabase', 'function myFunc() { return "Hello, World!"; }');
|
|
35
|
+
|
|
36
|
+
// Update an existing function
|
|
37
|
+
update('myDatabase', 'myFunc', () => { return "Updated function!"; });
|
|
38
|
+
|
|
39
|
+
// Save entire code to a database
|
|
40
|
+
save('myDatabase', 'export const PI = 3.14159;');
|
|
41
|
+
|
|
42
|
+
// Remove a function
|
|
43
|
+
remove('myDatabase', 'myFunc');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## API Reference
|
|
47
|
+
|
|
48
|
+
### SystemModuleResolver()
|
|
49
|
+
|
|
50
|
+
Resolves and loads all system modules, creating a context with built-in modules.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
await SystemModuleResolver(): Promise<Record<string, any>>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Returns a promise that resolves with a context object containing all system modules.
|
|
57
|
+
|
|
58
|
+
### System Modules
|
|
59
|
+
|
|
60
|
+
#### create(dbName, code)
|
|
61
|
+
|
|
62
|
+
Creates a new code snippet in the specified database file.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
create(dbName: string, code: string | Function): void
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- `dbName` (string): Name of the database file (without extension)
|
|
69
|
+
- `code` (string | Function): Code content of the snippet
|
|
70
|
+
|
|
71
|
+
#### update(dbName, fnName, code)
|
|
72
|
+
|
|
73
|
+
Updates an existing code snippet in the specified database file.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
update(dbName: string, fnName: string, code: string | Function): string
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `dbName` (string): Name of the database file
|
|
80
|
+
- `fnName` (string): Name of the function to update
|
|
81
|
+
- `code` (string | Function): New code content
|
|
82
|
+
|
|
83
|
+
Returns a string indicating the result of the operation.
|
|
84
|
+
|
|
85
|
+
#### remove(dbName, fnName?)
|
|
86
|
+
|
|
87
|
+
Removes a code snippet from the specified database file.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
remove(dbName: string, fnName?: string): string | boolean
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- `dbName` (string): Name of the database file
|
|
94
|
+
- `fnName` (string, optional): Name of the function to remove. If not provided, removes the entire file.
|
|
95
|
+
|
|
96
|
+
Returns a string or boolean indicating the result of the operation.
|
|
97
|
+
|
|
98
|
+
#### save(dbName, code)
|
|
99
|
+
|
|
100
|
+
Saves code content to the specified database file, overwriting any existing content.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
save(dbName: string, code: string | Function | any): void
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- `dbName` (string): Name of the database file
|
|
107
|
+
- `code` (string | Function | any): Code content to save
|
|
108
|
+
|
|
109
|
+
### Context Methods
|
|
110
|
+
|
|
111
|
+
#### require(moduleName)
|
|
112
|
+
|
|
113
|
+
Loads a system module by name.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
context.require(moduleName: string): any
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- `moduleName` (string): The name of the module to load
|
|
120
|
+
|
|
121
|
+
Returns the module's default export if available, otherwise the module itself.
|
|
122
|
+
|
|
123
|
+
#### import(moduleName)
|
|
124
|
+
|
|
125
|
+
Asynchronously loads a system module by name.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
context.import(moduleName: string): Promise<{ default: any }>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `moduleName` (string): The name of the module to load
|
|
132
|
+
|
|
133
|
+
Returns a promise that resolves with an object containing the module's default export.
|
|
134
|
+
|
|
135
|
+
## Examples
|
|
136
|
+
|
|
137
|
+
### Creating and Managing Code Snippets
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { SystemModuleResolver } from '@scriptdb/system-modules';
|
|
141
|
+
|
|
142
|
+
const context = await SystemModuleResolver();
|
|
143
|
+
|
|
144
|
+
// Create a new function in a database
|
|
145
|
+
context.create('math', `
|
|
146
|
+
function add(a: number, b: number): number {
|
|
147
|
+
return a + b;
|
|
148
|
+
}
|
|
149
|
+
`);
|
|
150
|
+
|
|
151
|
+
// Update an existing function
|
|
152
|
+
context.update('math', 'add', (a: number, b: number) => {
|
|
153
|
+
console.log(`Adding ${a} and ${b}`);
|
|
154
|
+
return a + b;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Add a new function to the same database
|
|
158
|
+
context.create('math', `
|
|
159
|
+
function multiply(a: number, b: number): number {
|
|
160
|
+
return a * b;
|
|
161
|
+
}
|
|
162
|
+
`);
|
|
163
|
+
|
|
164
|
+
// Save a complete module
|
|
165
|
+
context.save('utils', `
|
|
166
|
+
export const formatDate = (date: Date): string => {
|
|
167
|
+
return date.toISOString().split('T')[0];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const capitalize = (str: string): string => {
|
|
171
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
172
|
+
}
|
|
173
|
+
`);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Using the Module Context
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { SystemModuleResolver } from '@scriptdb/system-modules';
|
|
180
|
+
|
|
181
|
+
// Get the system modules context
|
|
182
|
+
const context = await SystemModuleResolver();
|
|
183
|
+
|
|
184
|
+
// Access modules directly
|
|
185
|
+
const create = context.create;
|
|
186
|
+
const update = context.update;
|
|
187
|
+
|
|
188
|
+
// Create a new module
|
|
189
|
+
create('userManagement', `
|
|
190
|
+
class UserManager {
|
|
191
|
+
private users: Array<{ id: number; name: string }> = [];
|
|
192
|
+
|
|
193
|
+
addUser(name: string): void {
|
|
194
|
+
const id = this.users.length + 1;
|
|
195
|
+
this.users.push({ id, name });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
getUser(id: number): { id: number; name: string } | undefined {
|
|
199
|
+
return this.users.find(user => user.id === id);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
listUsers(): Array<{ id: number; name: string }> {
|
|
203
|
+
return [...this.users];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default UserManager;
|
|
208
|
+
`);
|
|
209
|
+
|
|
210
|
+
// Use the require-like functionality
|
|
211
|
+
const userModule = context.require('userManagement');
|
|
212
|
+
// In this case, userModule will contain the user management code
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Working with Different Module Types
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { SystemModuleResolver } from '@scriptdb/system-modules';
|
|
219
|
+
|
|
220
|
+
const context = await SystemModuleResolver();
|
|
221
|
+
|
|
222
|
+
// Create a class-based module
|
|
223
|
+
context.create('database', `
|
|
224
|
+
class SimpleDatabase {
|
|
225
|
+
private data: Record<string, any> = {};
|
|
226
|
+
|
|
227
|
+
set(key: string, value: any): void {
|
|
228
|
+
this.data[key] = value;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
get(key: string): any {
|
|
232
|
+
return this.data[key];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
has(key: string): boolean {
|
|
236
|
+
return key in this.data;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
delete(key: string): boolean {
|
|
240
|
+
if (this.has(key)) {
|
|
241
|
+
delete this.data[key];
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
clear(): void {
|
|
248
|
+
this.data = {};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export default SimpleDatabase;
|
|
253
|
+
`);
|
|
254
|
+
|
|
255
|
+
// Create a functional module
|
|
256
|
+
context.create('stringUtils', `
|
|
257
|
+
export const reverse = (str: string): string => {
|
|
258
|
+
return str.split('').reverse().join('');
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export const truncate = (str: string, length: number): string => {
|
|
262
|
+
return str.length > length ? str.slice(0, length) + '...' : str;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
export const slugify = (str: string): string => {
|
|
266
|
+
return str
|
|
267
|
+
.toLowerCase()
|
|
268
|
+
.replace(/[^a-z0-9 -]/g, '')
|
|
269
|
+
.replace(/\s+/g, '-')
|
|
270
|
+
.replace(/-+/g, '-');
|
|
271
|
+
};
|
|
272
|
+
`);
|
|
273
|
+
|
|
274
|
+
// Update a specific function in a module
|
|
275
|
+
context.update('stringUtils', 'reverse', (str: string) => {
|
|
276
|
+
return Array.from(str).reverse().join('');
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Managing Database Files
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { SystemModuleResolver } from '@scriptdb/system-modules';
|
|
284
|
+
|
|
285
|
+
const context = await SystemModuleResolver();
|
|
286
|
+
|
|
287
|
+
// Create a new database file with multiple functions
|
|
288
|
+
context.save('api', `
|
|
289
|
+
import { fetch } from 'bun';
|
|
290
|
+
|
|
291
|
+
// Function to make GET requests
|
|
292
|
+
export async function get(url: string): Promise<any> {
|
|
293
|
+
const response = await fetch(url);
|
|
294
|
+
return await response.json();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Function to make POST requests
|
|
298
|
+
export async function post(url: string, data: any): Promise<any> {
|
|
299
|
+
const response = await fetch(url, {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: { 'Content-Type': 'application/json' },
|
|
302
|
+
body: JSON.stringify(data)
|
|
303
|
+
});
|
|
304
|
+
return await response.json();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Function to make PUT requests
|
|
308
|
+
export async function put(url: string, data: any): Promise<any> {
|
|
309
|
+
const response = await fetch(url, {
|
|
310
|
+
method: 'PUT',
|
|
311
|
+
headers: { 'Content-Type': 'application/json' },
|
|
312
|
+
body: JSON.stringify(data)
|
|
313
|
+
});
|
|
314
|
+
return await response.json();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Function to make DELETE requests
|
|
318
|
+
export async function del(url: string): Promise<any> {
|
|
319
|
+
const response = await fetch(url, {
|
|
320
|
+
method: 'DELETE'
|
|
321
|
+
});
|
|
322
|
+
return await response.json();
|
|
323
|
+
}
|
|
324
|
+
`);
|
|
325
|
+
|
|
326
|
+
// Add a new function to the existing database
|
|
327
|
+
context.create('api', `
|
|
328
|
+
export async function patch(url: string, data: any): Promise<any> {
|
|
329
|
+
const response = await fetch(url, {
|
|
330
|
+
method: 'PATCH',
|
|
331
|
+
headers: { 'Content-Type': 'application/json' },
|
|
332
|
+
body: JSON.stringify(data)
|
|
333
|
+
});
|
|
334
|
+
return await response.json();
|
|
335
|
+
}
|
|
336
|
+
`);
|
|
337
|
+
|
|
338
|
+
// Update an existing function
|
|
339
|
+
context.update('api', 'get', async (url: string) => {
|
|
340
|
+
const response = await fetch(url);
|
|
341
|
+
return await response.json();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Remove a specific function
|
|
345
|
+
context.remove('api', 'del');
|
|
346
|
+
|
|
347
|
+
// Remove the entire database file (after creating a backup)
|
|
348
|
+
context.remove('api');
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Error Handling
|
|
352
|
+
|
|
353
|
+
The System Module Resolver will throw an error if:
|
|
354
|
+
|
|
355
|
+
- A module is not found when using require() or import()
|
|
356
|
+
- There are issues with file operations
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { SystemModuleResolver } from '@scriptdb/system-modules';
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const context = await SystemModuleResolver();
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
const module = context.require('non-existent-module');
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error('Module not found:', error.message);
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error('Failed to load system modules:', error.message);
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## File Structure
|
|
375
|
+
|
|
376
|
+
System modules are stored in TypeScript files in the `./databases/system-modules` directory:
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
./databases/system-modules/
|
|
380
|
+
├── myDatabase.ts # Database file for code snippets
|
|
381
|
+
├── math.ts # Mathematical functions
|
|
382
|
+
├── api.ts # API-related functions
|
|
383
|
+
└── utils.ts # Utility functions
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Each file can contain TypeScript code, including functions, classes, and exports.
|
|
387
|
+
|
|
388
|
+
## Security Considerations
|
|
389
|
+
|
|
390
|
+
- Validate code content before saving to prevent injection attacks
|
|
391
|
+
- Consider implementing a sandboxed environment for executing code snippets
|
|
392
|
+
- Limit file system access to prevent unauthorized access to sensitive files
|
|
393
|
+
- Regularly backup your databases to prevent data loss
|
|
394
|
+
|
|
395
|
+
## License
|
|
396
|
+
|
|
397
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// src/modules/update.ts
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
|
|
6
|
+
// src/utils/isIdentifier.ts
|
|
7
|
+
function isIdentifier(key) {
|
|
8
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/utils/valueToCode.ts
|
|
12
|
+
function valueToCode(val, depth = 0) {
|
|
13
|
+
const indentUnit = " ";
|
|
14
|
+
const indent = indentUnit.repeat(depth);
|
|
15
|
+
const indentInner = indentUnit.repeat(depth + 1);
|
|
16
|
+
if (val === null)
|
|
17
|
+
return "null";
|
|
18
|
+
const t = typeof val;
|
|
19
|
+
if (t === "string")
|
|
20
|
+
return JSON.stringify(val);
|
|
21
|
+
if (t === "number" || t === "boolean")
|
|
22
|
+
return String(val);
|
|
23
|
+
if (t === "function")
|
|
24
|
+
return val.toString();
|
|
25
|
+
if (Array.isArray(val)) {
|
|
26
|
+
if (val.length === 0)
|
|
27
|
+
return "[]";
|
|
28
|
+
const items = val.map((v) => valueToCode(v, depth + 1));
|
|
29
|
+
return `[
|
|
30
|
+
` + items.map((it) => indentInner + it).join(`,
|
|
31
|
+
`) + `
|
|
32
|
+
` + indent + "]";
|
|
33
|
+
}
|
|
34
|
+
if (t === "object") {
|
|
35
|
+
const keys = Object.keys(val);
|
|
36
|
+
if (keys.length === 0)
|
|
37
|
+
return "{}";
|
|
38
|
+
const entries = keys.map((k) => {
|
|
39
|
+
const keyPart = isIdentifier(k) ? k : JSON.stringify(k);
|
|
40
|
+
const v = valueToCode(val[k], depth + 1);
|
|
41
|
+
return indentInner + keyPart + ": " + v;
|
|
42
|
+
});
|
|
43
|
+
return `{
|
|
44
|
+
` + entries.join(`,
|
|
45
|
+
`) + `
|
|
46
|
+
` + indent + "}";
|
|
47
|
+
}
|
|
48
|
+
return String(val);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/modules/update.ts
|
|
52
|
+
function update(dbName, fnName, code) {
|
|
53
|
+
const DIR = "databases";
|
|
54
|
+
const basePath = path.join(os.homedir(), ".scriptdb");
|
|
55
|
+
const baseDir = path.resolve(basePath, DIR);
|
|
56
|
+
const dbPath = path.join(baseDir, `${dbName}.ts`);
|
|
57
|
+
let src;
|
|
58
|
+
if (!fs.existsSync(dbPath)) {
|
|
59
|
+
try {
|
|
60
|
+
fs.writeFileSync(dbPath, "", "utf8");
|
|
61
|
+
return `Created new database file: ${dbPath}`;
|
|
62
|
+
} catch (e) {
|
|
63
|
+
return `Failed to create dbPath file: ${dbPath}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
src = fs.readFileSync(dbPath, "utf8");
|
|
67
|
+
const originalSrc = src;
|
|
68
|
+
const escaped = fnName.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
69
|
+
const startRe = new RegExp(`function\\s+${escaped}\\s*\\(|\\bclass\\s+${escaped}\\b|\\b(?:const|let|var)\\s+${escaped}\\s*=\\s*(?:function\\b|class\\b|\\(|\\{|\\[)`, "m");
|
|
70
|
+
const startMatch = src.match(startRe);
|
|
71
|
+
let declKind = null;
|
|
72
|
+
if (startMatch) {
|
|
73
|
+
let startIdx = startMatch.index;
|
|
74
|
+
const snippet = src.slice(startIdx, startIdx + 80);
|
|
75
|
+
if (/^function\b/.test(snippet))
|
|
76
|
+
declKind = "functionDecl";
|
|
77
|
+
else if (/^class\b/.test(snippet))
|
|
78
|
+
declKind = "classDecl";
|
|
79
|
+
else if (/^\b(?:const|let|var)\b/.test(snippet))
|
|
80
|
+
declKind = "varAssign";
|
|
81
|
+
}
|
|
82
|
+
let newCode;
|
|
83
|
+
if (typeof code === "function") {
|
|
84
|
+
const fnStr = code.toString();
|
|
85
|
+
if (declKind === "functionDecl") {
|
|
86
|
+
if (/^function\s+\w+/.test(fnStr))
|
|
87
|
+
newCode = fnStr;
|
|
88
|
+
else
|
|
89
|
+
newCode = `function ${fnName}${fnStr.replace(/^function\s*\(/, "(")}`;
|
|
90
|
+
} else if (declKind === "classDecl") {
|
|
91
|
+
if (/^class\s+\w+/.test(fnStr))
|
|
92
|
+
newCode = fnStr;
|
|
93
|
+
else if (/^class\s*\{/.test(fnStr))
|
|
94
|
+
newCode = fnStr.replace(/^class\s*\{/, `class ${fnName} {`);
|
|
95
|
+
else
|
|
96
|
+
newCode = `const ${fnName} = ${fnStr};`;
|
|
97
|
+
} else {
|
|
98
|
+
newCode = `const ${fnName} = ${fnStr};`;
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
newCode = `const ${fnName} = ${valueToCode(code, 0)};`;
|
|
102
|
+
}
|
|
103
|
+
if (startMatch) {
|
|
104
|
+
const startIdx = startMatch.index;
|
|
105
|
+
const idxCurly = src.indexOf("{", startIdx);
|
|
106
|
+
const idxBracket = src.indexOf("[", startIdx);
|
|
107
|
+
let braceOpen = -1;
|
|
108
|
+
if (idxCurly === -1)
|
|
109
|
+
braceOpen = idxBracket;
|
|
110
|
+
else if (idxBracket === -1)
|
|
111
|
+
braceOpen = idxCurly;
|
|
112
|
+
else
|
|
113
|
+
braceOpen = Math.min(idxCurly, idxBracket);
|
|
114
|
+
if (braceOpen === -1) {
|
|
115
|
+
const exportRe = new RegExp(`export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`, "m");
|
|
116
|
+
if (exportRe.test(src)) {
|
|
117
|
+
src = src.replace(exportRe, `${newCode}
|
|
118
|
+
|
|
119
|
+
export const ${fnName}: any = ${fnName};`);
|
|
120
|
+
} else {
|
|
121
|
+
src = src + `
|
|
122
|
+
|
|
123
|
+
${newCode}
|
|
124
|
+
|
|
125
|
+
export const ${fnName}: any = ${fnName};`;
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
const openingChar = src[braceOpen];
|
|
129
|
+
const closingChar = openingChar === "[" ? "]" : "}";
|
|
130
|
+
let i = braceOpen + 1;
|
|
131
|
+
let depth = 1;
|
|
132
|
+
const len = src.length;
|
|
133
|
+
while (i < len && depth > 0) {
|
|
134
|
+
const ch = src[i];
|
|
135
|
+
if (ch === openingChar)
|
|
136
|
+
depth++;
|
|
137
|
+
else if (ch === closingChar)
|
|
138
|
+
depth--;
|
|
139
|
+
i++;
|
|
140
|
+
}
|
|
141
|
+
let braceClose = i;
|
|
142
|
+
let endIdx = braceClose;
|
|
143
|
+
if (src.slice(braceClose, braceClose + 1) === ";")
|
|
144
|
+
endIdx = braceClose + 1;
|
|
145
|
+
const before = src.slice(0, startIdx);
|
|
146
|
+
const after = src.slice(endIdx);
|
|
147
|
+
src = before + newCode + after;
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
const exportRe = new RegExp(`export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`, "m");
|
|
151
|
+
if (exportRe.test(src)) {
|
|
152
|
+
src = src.replace(exportRe, `${newCode}
|
|
153
|
+
|
|
154
|
+
export const ${fnName}: any = ${fnName};`);
|
|
155
|
+
} else {
|
|
156
|
+
src = src + `
|
|
157
|
+
|
|
158
|
+
${newCode}
|
|
159
|
+
|
|
160
|
+
export const ${fnName}: any = ${fnName};`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
fs.writeFileSync(dbPath, src, "utf8");
|
|
164
|
+
if (src === originalSrc) {
|
|
165
|
+
return `Saved ${fnName} to database ${dbName}.`;
|
|
166
|
+
} else {
|
|
167
|
+
return `Updated ${dbName} with ${fnName}.`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/modules/remove.ts
|
|
172
|
+
import path2 from "node:path";
|
|
173
|
+
import fs2 from "node:fs";
|
|
174
|
+
import * as os2 from "os";
|
|
175
|
+
function remove(dbName, fnName) {
|
|
176
|
+
const DIR = "databases";
|
|
177
|
+
const basePath = path2.join(os2.homedir(), ".scriptdb");
|
|
178
|
+
const baseDir = path2.resolve(basePath, DIR);
|
|
179
|
+
const dbPath = path2.join(baseDir, `${dbName}.ts`);
|
|
180
|
+
if (!fs2.existsSync(dbPath))
|
|
181
|
+
return false;
|
|
182
|
+
if (!fnName) {
|
|
183
|
+
const bak2 = `${dbPath}.bak`;
|
|
184
|
+
try {
|
|
185
|
+
fs2.copyFileSync(dbPath, bak2);
|
|
186
|
+
} catch (e) {}
|
|
187
|
+
try {
|
|
188
|
+
fs2.unlinkSync(dbPath);
|
|
189
|
+
return "Removed successfully";
|
|
190
|
+
} catch (e) {
|
|
191
|
+
return "Removed failed";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const bak = `${dbPath}.bak`;
|
|
195
|
+
try {
|
|
196
|
+
fs2.copyFileSync(dbPath, bak);
|
|
197
|
+
} catch (e) {}
|
|
198
|
+
let src = fs2.readFileSync(dbPath, "utf8");
|
|
199
|
+
const escaped = fnName.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
200
|
+
const startRe = new RegExp(`function\\s+${escaped}\\s*\\(|\\bclass\\s+${escaped}\\b|\\b(?:const|let|var)\\s+${escaped}\\s*=\\s*(?:function\\b|class\\b|\\(|\\{|\\[)`, "m");
|
|
201
|
+
const startMatch = src.match(startRe);
|
|
202
|
+
if (startMatch) {
|
|
203
|
+
const startIdx = startMatch.index;
|
|
204
|
+
const len = src.length;
|
|
205
|
+
const idxCurly = src.indexOf("{", startIdx);
|
|
206
|
+
const idxBracket = src.indexOf("[", startIdx);
|
|
207
|
+
let braceOpen = -1;
|
|
208
|
+
if (idxCurly === -1)
|
|
209
|
+
braceOpen = idxBracket;
|
|
210
|
+
else if (idxBracket === -1)
|
|
211
|
+
braceOpen = idxCurly;
|
|
212
|
+
else
|
|
213
|
+
braceOpen = Math.min(idxCurly, idxBracket);
|
|
214
|
+
if (braceOpen !== -1) {
|
|
215
|
+
const openingChar = src[braceOpen];
|
|
216
|
+
const closingChar = openingChar === "[" ? "]" : "}";
|
|
217
|
+
let i = braceOpen + 1;
|
|
218
|
+
let depth = 1;
|
|
219
|
+
while (i < len && depth > 0) {
|
|
220
|
+
const ch = src[i];
|
|
221
|
+
if (ch === openingChar)
|
|
222
|
+
depth++;
|
|
223
|
+
else if (ch === closingChar)
|
|
224
|
+
depth--;
|
|
225
|
+
i++;
|
|
226
|
+
}
|
|
227
|
+
let braceClose = i;
|
|
228
|
+
let endIdx = braceClose;
|
|
229
|
+
if (src.slice(braceClose, braceClose + 1) === ";")
|
|
230
|
+
endIdx = braceClose + 1;
|
|
231
|
+
const before = src.slice(0, startIdx);
|
|
232
|
+
const after = src.slice(endIdx);
|
|
233
|
+
src = before + after;
|
|
234
|
+
} else {
|
|
235
|
+
const semi = src.indexOf(";", startIdx);
|
|
236
|
+
let endIdx = semi !== -1 ? semi + 1 : src.indexOf(`
|
|
237
|
+
|
|
238
|
+
`, startIdx);
|
|
239
|
+
if (endIdx === -1)
|
|
240
|
+
endIdx = len;
|
|
241
|
+
src = src.slice(0, startIdx) + src.slice(endIdx);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const exportRe = new RegExp(`export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`, "g");
|
|
245
|
+
src = src.replace(exportRe, "");
|
|
246
|
+
src = src.replace(/\n{3,}/g, `
|
|
247
|
+
|
|
248
|
+
`);
|
|
249
|
+
fs2.writeFileSync(dbPath, src, "utf8");
|
|
250
|
+
return `Removed ${fnName} from database ${dbName}.`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/modules/create.ts
|
|
254
|
+
import fs3 from "node:fs";
|
|
255
|
+
import path3 from "node:path";
|
|
256
|
+
import * as os3 from "os";
|
|
257
|
+
function create(dbName, code) {
|
|
258
|
+
const DIR = "databases";
|
|
259
|
+
const basePath = path3.join(os3.homedir(), ".scriptdb");
|
|
260
|
+
const baseDir = path3.resolve(basePath, DIR);
|
|
261
|
+
const dbPath = path3.join(baseDir, `${dbName}.ts`);
|
|
262
|
+
fs3.appendFileSync(dbPath, code.toString(), "utf8");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/modules/save.ts
|
|
266
|
+
import * as fs4 from "fs";
|
|
267
|
+
import * as path4 from "path";
|
|
268
|
+
import * as os4 from "os";
|
|
269
|
+
function save(dbName, code) {
|
|
270
|
+
const DIR = "databases";
|
|
271
|
+
const basePath = path4.join(os4.homedir(), ".scriptdb");
|
|
272
|
+
const baseDir = path4.resolve(basePath, DIR);
|
|
273
|
+
const dbPath = path4.join(baseDir, `${dbName}.ts`);
|
|
274
|
+
let fileContent = typeof code === "function" ? code.toString() : code;
|
|
275
|
+
fs4.writeFileSync(dbPath, fileContent, "utf8");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/index.ts
|
|
279
|
+
var update2 = update;
|
|
280
|
+
var remove2 = remove;
|
|
281
|
+
var create2 = create;
|
|
282
|
+
var save2 = save;
|
|
283
|
+
async function SystemModuleResolver() {
|
|
284
|
+
const moduleRegistry = new Map;
|
|
285
|
+
moduleRegistry.set("update", update2);
|
|
286
|
+
moduleRegistry.set("remove", remove2);
|
|
287
|
+
moduleRegistry.set("create", create2);
|
|
288
|
+
moduleRegistry.set("save", save2);
|
|
289
|
+
const context = {
|
|
290
|
+
require: (moduleName) => {
|
|
291
|
+
const module = moduleRegistry.get(moduleName);
|
|
292
|
+
if (!module) {
|
|
293
|
+
throw new Error(`Module '${moduleName}' not found`);
|
|
294
|
+
}
|
|
295
|
+
return module.default || module;
|
|
296
|
+
},
|
|
297
|
+
import: async (moduleName) => {
|
|
298
|
+
const module = moduleRegistry.get(moduleName);
|
|
299
|
+
if (!module) {
|
|
300
|
+
throw new Error(`Module '${moduleName}' not found`);
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
default: module.default || module
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
for (const [name, moduleExports] of moduleRegistry) {
|
|
308
|
+
context[name] = moduleExports.default || moduleExports;
|
|
309
|
+
}
|
|
310
|
+
return context;
|
|
311
|
+
}
|
|
312
|
+
var src_default = SystemModuleResolver;
|
|
313
|
+
export {
|
|
314
|
+
update2 as update,
|
|
315
|
+
save2 as save,
|
|
316
|
+
remove2 as remove,
|
|
317
|
+
src_default as default,
|
|
318
|
+
create2 as create,
|
|
319
|
+
SystemModuleResolver
|
|
320
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scriptdb/system-modules",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "System module resolver for script database",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "bun --watch src/index.ts",
|
|
20
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm --splitting",
|
|
21
|
+
"build:cjs": "bun build src/index.ts --outdir dist --target node --format cjs --outfile dist/index.js",
|
|
22
|
+
"build:types": "tsc --emitDeclarationOnly",
|
|
23
|
+
"build:all": "bun run build && bun run build:cjs && bun run build:types",
|
|
24
|
+
"test": "bun test",
|
|
25
|
+
"lint": "bun run lint:src",
|
|
26
|
+
"lint:src": "eslint src --ext .ts,.tsx",
|
|
27
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"clean": "rm -rf dist"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/bun": "^1.3.2",
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
35
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
36
|
+
"bun-types": "latest",
|
|
37
|
+
"eslint": "^8.0.0",
|
|
38
|
+
"typescript": "^5.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|