s3db.js 12.1.0 → 12.2.1
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 +212 -196
- package/dist/s3db.cjs.js +1041 -1941
- package/dist/s3db.cjs.js.map +1 -1
- package/dist/s3db.es.js +1039 -1941
- package/dist/s3db.es.js.map +1 -1
- package/package.json +6 -1
- package/src/cli/index.js +954 -43
- package/src/cli/migration-manager.js +270 -0
- package/src/concerns/calculator.js +0 -4
- package/src/concerns/metadata-encoding.js +1 -21
- package/src/concerns/plugin-storage.js +17 -4
- package/src/concerns/typescript-generator.d.ts +171 -0
- package/src/concerns/typescript-generator.js +275 -0
- package/src/database.class.js +171 -28
- package/src/index.js +15 -9
- package/src/plugins/api/index.js +5 -2
- package/src/plugins/api/routes/resource-routes.js +86 -1
- package/src/plugins/api/server.js +79 -3
- package/src/plugins/api/utils/openapi-generator.js +195 -5
- package/src/plugins/backup/multi-backup-driver.class.js +0 -1
- package/src/plugins/backup.plugin.js +7 -14
- package/src/plugins/concerns/plugin-dependencies.js +73 -19
- package/src/plugins/eventual-consistency/analytics.js +0 -2
- package/src/plugins/eventual-consistency/consolidation.js +2 -13
- package/src/plugins/eventual-consistency/index.js +0 -1
- package/src/plugins/eventual-consistency/install.js +1 -1
- package/src/plugins/geo.plugin.js +5 -6
- package/src/plugins/importer/index.js +1 -1
- package/src/plugins/index.js +2 -1
- package/src/plugins/relation.plugin.js +11 -11
- package/src/plugins/replicator.plugin.js +12 -21
- package/src/plugins/s3-queue.plugin.js +4 -4
- package/src/plugins/scheduler.plugin.js +10 -12
- package/src/plugins/state-machine.plugin.js +8 -12
- package/src/plugins/tfstate/README.md +1 -1
- package/src/plugins/tfstate/errors.js +3 -3
- package/src/plugins/tfstate/index.js +41 -67
- package/src/plugins/ttl.plugin.js +3 -3
- package/src/resource.class.js +263 -61
- package/src/schema.class.js +0 -2
- package/src/testing/factory.class.js +286 -0
- package/src/testing/index.js +15 -0
- package/src/testing/seeder.class.js +183 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration Manager for s3db.js
|
|
3
|
+
* Handles database schema migrations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
export class MigrationManager {
|
|
10
|
+
constructor(database, migrationsDir = './migrations') {
|
|
11
|
+
this.database = database;
|
|
12
|
+
this.migrationsDir = migrationsDir;
|
|
13
|
+
this.migrationResource = null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Initialize migrations system
|
|
18
|
+
*/
|
|
19
|
+
async init() {
|
|
20
|
+
// Create migrations resource if it doesn't exist
|
|
21
|
+
const resources = await this.database.listResources();
|
|
22
|
+
const exists = resources.find(r => r.name === '_migrations');
|
|
23
|
+
|
|
24
|
+
if (!exists) {
|
|
25
|
+
this.migrationResource = await this.database.createResource({
|
|
26
|
+
name: '_migrations',
|
|
27
|
+
attributes: {
|
|
28
|
+
id: 'string|required',
|
|
29
|
+
name: 'string|required',
|
|
30
|
+
batch: 'number|default:1',
|
|
31
|
+
executedAt: 'string'
|
|
32
|
+
},
|
|
33
|
+
timestamps: true,
|
|
34
|
+
behavior: 'enforce-limits'
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
this.migrationResource = await this.database.resource('_migrations');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Ensure migrations directory exists
|
|
41
|
+
try {
|
|
42
|
+
await fs.mkdir(this.migrationsDir, { recursive: true });
|
|
43
|
+
} catch (err) {
|
|
44
|
+
// Directory exists
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate a new migration file
|
|
50
|
+
*/
|
|
51
|
+
async generate(name) {
|
|
52
|
+
const timestamp = new Date().toISOString().replace(/[-:]/g, '').split('.')[0];
|
|
53
|
+
const filename = `${timestamp}_${name}.js`;
|
|
54
|
+
const filepath = path.join(this.migrationsDir, filename);
|
|
55
|
+
|
|
56
|
+
const template = `/**
|
|
57
|
+
* Migration: ${name}
|
|
58
|
+
* Generated: ${new Date().toISOString()}
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
export async function up(database) {
|
|
62
|
+
// Add migration logic here
|
|
63
|
+
// Example:
|
|
64
|
+
// await database.createResource({
|
|
65
|
+
// name: 'users',
|
|
66
|
+
// attributes: {
|
|
67
|
+
// id: 'string|required',
|
|
68
|
+
// email: 'string|required|email',
|
|
69
|
+
// name: 'string|required'
|
|
70
|
+
// },
|
|
71
|
+
// timestamps: true
|
|
72
|
+
// });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function down(database) {
|
|
76
|
+
// Add rollback logic here
|
|
77
|
+
// Example:
|
|
78
|
+
// await database.deleteResource('users');
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
await fs.writeFile(filepath, template);
|
|
83
|
+
return { filename, filepath };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get all migration files
|
|
88
|
+
*/
|
|
89
|
+
async getMigrationFiles() {
|
|
90
|
+
try {
|
|
91
|
+
const files = await fs.readdir(this.migrationsDir);
|
|
92
|
+
return files
|
|
93
|
+
.filter(f => f.endsWith('.js'))
|
|
94
|
+
.sort();
|
|
95
|
+
} catch (err) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get executed migrations from database
|
|
102
|
+
*/
|
|
103
|
+
async getExecutedMigrations() {
|
|
104
|
+
try {
|
|
105
|
+
const migrations = await this.migrationResource.list();
|
|
106
|
+
return migrations.map(m => m.name);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get current batch number
|
|
114
|
+
*/
|
|
115
|
+
async getCurrentBatch() {
|
|
116
|
+
try {
|
|
117
|
+
const migrations = await this.migrationResource.list();
|
|
118
|
+
if (migrations.length === 0) return 0;
|
|
119
|
+
|
|
120
|
+
const batches = migrations.map(m => m.batch || 0);
|
|
121
|
+
return Math.max(...batches);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get pending migrations
|
|
129
|
+
*/
|
|
130
|
+
async getPendingMigrations() {
|
|
131
|
+
const allFiles = await this.getMigrationFiles();
|
|
132
|
+
const executed = await this.getExecutedMigrations();
|
|
133
|
+
|
|
134
|
+
return allFiles.filter(f => !executed.includes(f));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Run pending migrations
|
|
139
|
+
*/
|
|
140
|
+
async up(options = {}) {
|
|
141
|
+
const { step = null } = options;
|
|
142
|
+
const pending = await this.getPendingMigrations();
|
|
143
|
+
|
|
144
|
+
if (pending.length === 0) {
|
|
145
|
+
return { message: 'No pending migrations', migrations: [] };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const toRun = step ? pending.slice(0, step) : pending;
|
|
149
|
+
const batch = (await this.getCurrentBatch()) + 1;
|
|
150
|
+
const executed = [];
|
|
151
|
+
|
|
152
|
+
for (const filename of toRun) {
|
|
153
|
+
const filepath = path.join(process.cwd(), this.migrationsDir, filename);
|
|
154
|
+
const migration = await import(filepath);
|
|
155
|
+
|
|
156
|
+
// Execute up
|
|
157
|
+
await migration.up(this.database);
|
|
158
|
+
|
|
159
|
+
// Record migration
|
|
160
|
+
await this.migrationResource.insert({
|
|
161
|
+
id: filename,
|
|
162
|
+
name: filename,
|
|
163
|
+
batch,
|
|
164
|
+
executedAt: new Date().toISOString()
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
executed.push(filename);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { message: `Executed ${executed.length} migrations`, migrations: executed, batch };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Rollback migrations
|
|
175
|
+
*/
|
|
176
|
+
async down(options = {}) {
|
|
177
|
+
const { step = 1 } = options;
|
|
178
|
+
|
|
179
|
+
const allMigrations = await this.migrationResource.list();
|
|
180
|
+
if (allMigrations.length === 0) {
|
|
181
|
+
return { message: 'No migrations to rollback', migrations: [] };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Sort by batch descending, then by name descending
|
|
185
|
+
allMigrations.sort((a, b) => {
|
|
186
|
+
if (a.batch !== b.batch) return b.batch - a.batch;
|
|
187
|
+
return b.name.localeCompare(a.name);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const currentBatch = allMigrations[0].batch;
|
|
191
|
+
const toRollback = allMigrations
|
|
192
|
+
.filter(m => m.batch === currentBatch)
|
|
193
|
+
.slice(0, step);
|
|
194
|
+
|
|
195
|
+
const rolledBack = [];
|
|
196
|
+
|
|
197
|
+
for (const migration of toRollback) {
|
|
198
|
+
const filepath = path.join(process.cwd(), this.migrationsDir, migration.name);
|
|
199
|
+
const migrationModule = await import(filepath);
|
|
200
|
+
|
|
201
|
+
// Execute down
|
|
202
|
+
await migrationModule.down(this.database);
|
|
203
|
+
|
|
204
|
+
// Remove migration record
|
|
205
|
+
await this.migrationResource.delete(migration.id);
|
|
206
|
+
|
|
207
|
+
rolledBack.push(migration.name);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { message: `Rolled back ${rolledBack.length} migrations`, migrations: rolledBack };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Reset all migrations
|
|
215
|
+
*/
|
|
216
|
+
async reset() {
|
|
217
|
+
const allMigrations = await this.migrationResource.list();
|
|
218
|
+
|
|
219
|
+
// Sort in reverse order for rollback
|
|
220
|
+
allMigrations.sort((a, b) => {
|
|
221
|
+
if (a.batch !== b.batch) return b.batch - a.batch;
|
|
222
|
+
return b.name.localeCompare(a.name);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const rolledBack = [];
|
|
226
|
+
|
|
227
|
+
for (const migration of allMigrations) {
|
|
228
|
+
const filepath = path.join(process.cwd(), this.migrationsDir, migration.name);
|
|
229
|
+
const migrationModule = await import(filepath);
|
|
230
|
+
|
|
231
|
+
// Execute down
|
|
232
|
+
await migrationModule.down(this.database);
|
|
233
|
+
|
|
234
|
+
// Remove migration record
|
|
235
|
+
await this.migrationResource.delete(migration.id);
|
|
236
|
+
|
|
237
|
+
rolledBack.push(migration.name);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { message: `Reset ${rolledBack.length} migrations`, migrations: rolledBack };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get migration status
|
|
245
|
+
*/
|
|
246
|
+
async status() {
|
|
247
|
+
const allFiles = await this.getMigrationFiles();
|
|
248
|
+
const executed = await this.getExecutedMigrations();
|
|
249
|
+
const executedRecords = await this.migrationResource.list();
|
|
250
|
+
|
|
251
|
+
const executedMap = {};
|
|
252
|
+
executedRecords.forEach(m => {
|
|
253
|
+
executedMap[m.name] = m;
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return allFiles.map(filename => {
|
|
257
|
+
const isExecuted = executed.includes(filename);
|
|
258
|
+
const record = executedMap[filename];
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
name: filename,
|
|
262
|
+
status: isExecuted ? 'executed' : 'pending',
|
|
263
|
+
batch: record?.batch || null,
|
|
264
|
+
executedAt: record?.executedAt || null
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export default MigrationManager;
|
|
@@ -66,10 +66,6 @@ export function clearUTF8Memory() {
|
|
|
66
66
|
utf8BytesMemory.clear();
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
// Aliases for backward compatibility
|
|
70
|
-
export const clearUTF8Memo = clearUTF8Memory;
|
|
71
|
-
export const clearUTF8Cache = clearUTF8Memory;
|
|
72
|
-
|
|
73
69
|
/**
|
|
74
70
|
* Calculates the size in bytes of attribute names (mapped to digits)
|
|
75
71
|
* @param {Object} mappedObject - The object returned by schema.mapper()
|
|
@@ -464,24 +464,7 @@ export function metadataDecode(value) {
|
|
|
464
464
|
}
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
-
// No prefix - return as is
|
|
468
|
-
// Try to detect if it's base64 without prefix (legacy)
|
|
469
|
-
// OPTIMIZATION: Quick reject before expensive regex
|
|
470
|
-
const len = value.length;
|
|
471
|
-
if (len > 0 && len % 4 === 0) { // Base64 is always multiple of 4
|
|
472
|
-
if (/^[A-Za-z0-9+/]+=*$/.test(value)) {
|
|
473
|
-
try {
|
|
474
|
-
const decoded = Buffer.from(value, 'base64').toString('utf8');
|
|
475
|
-
// Verify it's valid UTF-8 with special chars
|
|
476
|
-
if (/[^\x00-\x7F]/.test(decoded) && Buffer.from(decoded, 'utf8').toString('base64') === value) {
|
|
477
|
-
return decoded;
|
|
478
|
-
}
|
|
479
|
-
} catch {
|
|
480
|
-
// Not base64, return as is
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
467
|
+
// No prefix - return as is
|
|
485
468
|
return value;
|
|
486
469
|
}
|
|
487
470
|
|
|
@@ -490,9 +473,6 @@ export function metadataDecode(value) {
|
|
|
490
473
|
* @param {string} value - Value to calculate size for
|
|
491
474
|
* @returns {Object} Size information
|
|
492
475
|
*/
|
|
493
|
-
// Backwards compatibility exports
|
|
494
|
-
export { metadataEncode as smartEncode, metadataDecode as smartDecode };
|
|
495
|
-
|
|
496
476
|
export function calculateEncodedSize(value) {
|
|
497
477
|
const analysis = analyzeString(value);
|
|
498
478
|
const originalSize = Buffer.byteLength(value, 'utf8');
|
|
@@ -134,11 +134,24 @@ export class PluginStorage {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
137
|
+
* Batch set multiple items
|
|
138
|
+
*
|
|
139
|
+
* @param {Array<{key: string, data: Object, options?: Object}>} items - Items to save
|
|
140
|
+
* @returns {Promise<Array<{ok: boolean, key: string, error?: Error}>>} Results
|
|
139
141
|
*/
|
|
140
|
-
async
|
|
141
|
-
|
|
142
|
+
async batchSet(items) {
|
|
143
|
+
const results = [];
|
|
144
|
+
|
|
145
|
+
for (const item of items) {
|
|
146
|
+
try {
|
|
147
|
+
await this.set(item.key, item.data, item.options || {});
|
|
148
|
+
results.push({ ok: true, key: item.key });
|
|
149
|
+
} catch (error) {
|
|
150
|
+
results.push({ ok: false, key: item.key, error });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return results;
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
/**
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Definition Generator for s3db.js
|
|
3
|
+
*
|
|
4
|
+
* Automatically generates type-safe interfaces from your s3db.js resources.
|
|
5
|
+
*
|
|
6
|
+
* @module s3db.js/typescript-generator
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { Database } from 's3db.js';
|
|
10
|
+
* import { generateTypes } from 's3db.js/typescript-generator';
|
|
11
|
+
*
|
|
12
|
+
* const db = new Database({ connectionString: '...' });
|
|
13
|
+
*
|
|
14
|
+
* await db.createResource({
|
|
15
|
+
* name: 'users',
|
|
16
|
+
* attributes: {
|
|
17
|
+
* name: 'string|required',
|
|
18
|
+
* email: 'string|required|email',
|
|
19
|
+
* age: 'number'
|
|
20
|
+
* }
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Generate TypeScript definitions
|
|
24
|
+
* await generateTypes(db, { outputPath: './types/database.d.ts' });
|
|
25
|
+
*
|
|
26
|
+
* // Now you get full autocomplete!
|
|
27
|
+
* import { Users } from './types/database';
|
|
28
|
+
* const user: Users = await db.resources.users.get('id');
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type { Database } from 's3db.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Options for generating TypeScript definitions
|
|
36
|
+
*/
|
|
37
|
+
export interface GenerateTypesOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Output path for the generated .d.ts file
|
|
40
|
+
* @example './types/database.d.ts'
|
|
41
|
+
*/
|
|
42
|
+
outputPath: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Module name to augment with resource types
|
|
46
|
+
* @default 's3db.js'
|
|
47
|
+
*/
|
|
48
|
+
moduleName?: string;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Include JSDoc comments in generated types
|
|
52
|
+
* @default true
|
|
53
|
+
*/
|
|
54
|
+
includeComments?: boolean;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Custom header comment to add to generated file
|
|
58
|
+
* @example 'Auto-generated types for MyApp database'
|
|
59
|
+
*/
|
|
60
|
+
header?: string;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Include resource metadata in generated types
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
includeMetadata?: boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Format style for generated code
|
|
70
|
+
* @default 'prettier'
|
|
71
|
+
*/
|
|
72
|
+
formatStyle?: 'prettier' | 'compact' | 'none';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Result of type generation
|
|
77
|
+
*/
|
|
78
|
+
export interface GenerateTypesResult {
|
|
79
|
+
/**
|
|
80
|
+
* Number of resource interfaces generated
|
|
81
|
+
*/
|
|
82
|
+
resourceCount: number;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* List of generated resource names
|
|
86
|
+
*/
|
|
87
|
+
resources: string[];
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Output file path
|
|
91
|
+
*/
|
|
92
|
+
outputPath: string;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Generated content (before writing to file)
|
|
96
|
+
*/
|
|
97
|
+
content: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generate TypeScript definitions from database resources
|
|
102
|
+
*
|
|
103
|
+
* Creates a .d.ts file with type-safe interfaces for all resources in the database.
|
|
104
|
+
* The generated file includes:
|
|
105
|
+
* - Interface for each resource with all fields typed
|
|
106
|
+
* - ResourceMap interface for type-safe db.resources access
|
|
107
|
+
* - Module augmentation for s3db.js Database class
|
|
108
|
+
*
|
|
109
|
+
* @param database - s3db.js Database instance with resources
|
|
110
|
+
* @param options - Generation options
|
|
111
|
+
* @returns Promise<void>
|
|
112
|
+
*
|
|
113
|
+
* @throws {Error} If database has no resources
|
|
114
|
+
* @throws {Error} If output directory doesn't exist
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* // Basic usage
|
|
119
|
+
* await generateTypes(db, { outputPath: './types/db.d.ts' });
|
|
120
|
+
*
|
|
121
|
+
* // With custom options
|
|
122
|
+
* await generateTypes(db, {
|
|
123
|
+
* outputPath: './types/db.d.ts',
|
|
124
|
+
* moduleName: 's3db.js',
|
|
125
|
+
* includeComments: true,
|
|
126
|
+
* header: 'Generated types for MyApp'
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export function generateTypes(
|
|
131
|
+
database: Database,
|
|
132
|
+
options: GenerateTypesOptions
|
|
133
|
+
): Promise<void>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Map s3db.js field types to TypeScript types
|
|
137
|
+
*
|
|
138
|
+
* @param fieldType - s3db.js field type string (e.g., 'string|required', 'number', 'embedding:1536')
|
|
139
|
+
* @returns TypeScript type string
|
|
140
|
+
*
|
|
141
|
+
* @internal
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* mapFieldTypeToTypeScript('string|required') // 'string'
|
|
146
|
+
* mapFieldTypeToTypeScript('number') // 'number'
|
|
147
|
+
* mapFieldTypeToTypeScript('embedding:1536') // 'number[] /* 1536 dimensions *\/'
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export function mapFieldTypeToTypeScript(fieldType: string): string;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check if a field is optional based on its validation rules
|
|
154
|
+
*
|
|
155
|
+
* @param fieldType - s3db.js field type string
|
|
156
|
+
* @returns True if field is optional (no 'required' rule)
|
|
157
|
+
*
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
160
|
+
export function isFieldOptional(fieldType: string): boolean;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Generate JSDoc comment for a field
|
|
164
|
+
*
|
|
165
|
+
* @param fieldName - Name of the field
|
|
166
|
+
* @param fieldType - s3db.js field type string
|
|
167
|
+
* @returns JSDoc comment string
|
|
168
|
+
*
|
|
169
|
+
* @internal
|
|
170
|
+
*/
|
|
171
|
+
export function generateFieldComment(fieldName: string, fieldType: string): string;
|