@medyll/idae-machine 0.123.0 → 0.125.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/dist/form/CreateUpdate.svelte +2 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.js +18 -11
- package/dist/main/machine/IDbCollection.d.ts +71 -0
- package/dist/main/machine/IDbCollection.js +102 -0
- package/dist/main/machine/IDbCollectionFieldForge.d.ts +42 -0
- package/dist/main/machine/IDbCollectionFieldForge.js +74 -0
- package/dist/main/machine/IDbCollectionFieldValues.d.ts +57 -0
- package/dist/main/machine/IDbCollectionFieldValues.js +82 -0
- package/dist/main/machine/IDbCollectionValues.d.ts +78 -0
- package/dist/main/machine/IDbCollectionValues.js +203 -0
- package/dist/main/machine/IDbError.d.ts +6 -0
- package/dist/main/machine/IDbError.js +61 -0
- package/dist/main/machine/IDbFormValidate.d.ts +55 -0
- package/dist/main/machine/IDbFormValidate.js +183 -0
- package/dist/main/machine/IDbValidationError.d.ts +19 -0
- package/dist/main/machine/IDbValidationError.js +24 -0
- package/dist/main/machineDb.d.ts +18 -222
- package/dist/main/machineDb.js +60 -653
- package/dist/main/machineForge.d.ts +57 -0
- package/dist/main/machineForge.js +105 -0
- package/dist/{form → ui}/CollectionButton.svelte +2 -2
- package/dist/{form → ui}/CollectionButton.svelte.d.ts +1 -1
- package/dist/{form → ui}/CollectionFks.svelte +3 -5
- package/dist/{form → ui}/CollectionList.svelte +1 -1
- package/dist/{form → ui}/CollectionListMenu.svelte +3 -2
- package/package.json +3 -3
- package/dist/form/DataList.svelte +0 -60
- package/dist/form/DataList.svelte.d.ts +0 -10
- /package/dist/{form → ui}/CollectionFieldGuess.svelte +0 -0
- /package/dist/{form → ui}/CollectionFieldGuess.svelte.d.ts +0 -0
- /package/dist/{form → ui}/CollectionFks.svelte.d.ts +0 -0
- /package/dist/{form → ui}/CollectionList.svelte.d.ts +0 -0
- /package/dist/{form → ui}/CollectionListMenu.svelte.d.ts +0 -0
- /package/dist/{form → ui}/CollectionReverseFks.svelte +0 -0
- /package/dist/{form → ui}/CollectionReverseFks.svelte.d.ts +0 -0
package/dist/main/machineDb.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
2
|
path: D:\boulot\python\wollama\src\lib\db\dbFields.ts
|
|
3
3
|
*/
|
|
4
|
-
import { schemeModel } from
|
|
4
|
+
import { schemeModel } from "../db/dbSchema.js";
|
|
5
|
+
import { IDbCollection } from "./machine/IDbCollection.js";
|
|
6
|
+
import { IDbError } from "./machine/IDbError.js";
|
|
7
|
+
import { MachineForge } from "./machineForge.js";
|
|
5
8
|
export var enumPrimitive;
|
|
6
9
|
(function (enumPrimitive) {
|
|
7
10
|
enumPrimitive["id"] = "id";
|
|
@@ -23,29 +26,7 @@ export var TplProperties;
|
|
|
23
26
|
TplProperties["readonly"] = "readonly";
|
|
24
27
|
TplProperties["required"] = "required";
|
|
25
28
|
})(TplProperties || (TplProperties = {}));
|
|
26
|
-
const a =
|
|
27
|
-
/*
|
|
28
|
-
renamed from DbCollectionError to IDbErrors
|
|
29
|
-
*/
|
|
30
|
-
class IDbError extends Error {
|
|
31
|
-
code;
|
|
32
|
-
constructor(message, code) {
|
|
33
|
-
super(message);
|
|
34
|
-
this.code = code;
|
|
35
|
-
this.name = 'DbCollectionError';
|
|
36
|
-
}
|
|
37
|
-
static throwError(message, code) {
|
|
38
|
-
throw new IDbError(message, code);
|
|
39
|
-
}
|
|
40
|
-
static handleError(error) {
|
|
41
|
-
if (error instanceof IDbError) {
|
|
42
|
-
console.error(`${error.name}: ${error.message} (Code: ${error.code})`);
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
console.error('Unexpected error:', error);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
29
|
+
const a = "any(private required readonly)";
|
|
49
30
|
/**
|
|
50
31
|
* Central class for parsing, introspecting, and extracting metadata from the database schema.
|
|
51
32
|
* Provides methods to access collections, templates, fields, foreign keys, and type information.
|
|
@@ -56,6 +37,7 @@ export class MachineDb {
|
|
|
56
37
|
* The database model (schema) used for introspection.
|
|
57
38
|
*/
|
|
58
39
|
model = schemeModel;
|
|
40
|
+
machineForge = new MachineForge();
|
|
59
41
|
#idbCollectionsList = {};
|
|
60
42
|
/**
|
|
61
43
|
* Create a new IDbBase instance.
|
|
@@ -63,6 +45,7 @@ export class MachineDb {
|
|
|
63
45
|
*/
|
|
64
46
|
constructor(model) {
|
|
65
47
|
this.model = model ?? schemeModel;
|
|
48
|
+
this.machineForge = new MachineForge();
|
|
66
49
|
}
|
|
67
50
|
/**
|
|
68
51
|
* Get an IDbCollection instance for a collection name.
|
|
@@ -73,30 +56,32 @@ export class MachineDb {
|
|
|
73
56
|
}
|
|
74
57
|
return this.#idbCollectionsList[collection];
|
|
75
58
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
*/
|
|
86
|
-
collectionValues(collection) {
|
|
87
|
-
return new IDbCollectionValues(collection, this);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Get an IDbCollectionFieldValues instance for a collection and data.
|
|
91
|
-
*/
|
|
92
|
-
collectionFieldValues(collection, data) {
|
|
93
|
-
return new IDbCollectionFieldValues(collection, data, this.collectionValues(collection));
|
|
59
|
+
fks(collection) {
|
|
60
|
+
const fks = this.collection(collection).getModelTemplateFks();
|
|
61
|
+
const out = {};
|
|
62
|
+
if (fks) {
|
|
63
|
+
Object.keys(fks).forEach((collection) => {
|
|
64
|
+
out[collection] = this.parseRawCollection(collection);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
94
68
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
69
|
+
reverseFks(targetCollection) {
|
|
70
|
+
const result = {};
|
|
71
|
+
Object.entries(this.model).forEach(([collectionName, collectionModel]) => {
|
|
72
|
+
const template = collectionModel.template;
|
|
73
|
+
if (template && template.fks) {
|
|
74
|
+
Object.entries(template.fks).forEach(([fkName, fkConfig]) => {
|
|
75
|
+
if (fkConfig?.code === targetCollection) {
|
|
76
|
+
if (!result[collectionName]) {
|
|
77
|
+
result[collectionName] = {};
|
|
78
|
+
}
|
|
79
|
+
result[collectionName][fkName] = fkConfig;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return result;
|
|
100
85
|
}
|
|
101
86
|
/**
|
|
102
87
|
* Parse all fields of a given collection.
|
|
@@ -118,37 +103,30 @@ export class MachineDb {
|
|
|
118
103
|
* Parse a single field of a collection and return its IDbForge metadata.
|
|
119
104
|
*/
|
|
120
105
|
parseCollectionFieldName(collection, fieldName) {
|
|
121
|
-
const field = this
|
|
106
|
+
const field = this.collection(collection).getFieldRule(fieldName);
|
|
122
107
|
if (!field) {
|
|
123
|
-
IDbError.throwError(`Field ${fieldName} not found in collection ${collection}`,
|
|
108
|
+
IDbError.throwError(`Field ${fieldName} not found in collection ${collection}`, "FIELD_NOT_FOUND");
|
|
124
109
|
return undefined;
|
|
125
110
|
}
|
|
126
|
-
const array = this.testIs(
|
|
127
|
-
const object = this.testIs(
|
|
128
|
-
const fk = this.testIs(
|
|
129
|
-
const primitive = this.testIs(
|
|
111
|
+
const array = this.machineForge.testIs("array", field);
|
|
112
|
+
const object = this.machineForge.testIs("object", field);
|
|
113
|
+
const fk = this.machineForge.testIs("fk", field);
|
|
114
|
+
const primitive = this.machineForge.testIs("primitive", field);
|
|
130
115
|
const fieldType = array ?? object ?? fk ?? primitive;
|
|
131
|
-
return this.forge({ collection, fieldName, ...fieldType });
|
|
132
|
-
}
|
|
133
|
-
// Toutes ces méthodes sont maintenant dans IDbCollection
|
|
134
|
-
#getTemplateFieldRule(collection, fieldName) {
|
|
135
|
-
// Utilise la nouvelle API orientée collection
|
|
136
|
-
return new IDbCollection(collection, this, this.model).getFieldRule(fieldName);
|
|
137
|
-
}
|
|
138
|
-
getFkFieldType(string) {
|
|
139
|
-
const [collection, field] = string.split('.');
|
|
140
|
-
let template = this.#getTemplateFieldRule(collection, field);
|
|
141
|
-
return template;
|
|
142
|
-
}
|
|
143
|
-
getFkTemplateFields(string) {
|
|
144
|
-
const [collection, field] = string.split('.');
|
|
145
|
-
return this.collection(collection).getTemplate()?.fields;
|
|
116
|
+
return this.machineForge.forge({ collection, fieldName, ...fieldType });
|
|
146
117
|
}
|
|
147
118
|
testIs(what, fieldRule) {
|
|
148
|
-
const typeMappings = {
|
|
119
|
+
const typeMappings = {
|
|
120
|
+
fk: "fk-",
|
|
121
|
+
array: "array-of-",
|
|
122
|
+
object: "object-",
|
|
123
|
+
primitive: "",
|
|
124
|
+
};
|
|
149
125
|
const prefix = typeMappings[what];
|
|
150
|
-
if (what ===
|
|
151
|
-
if (!fieldRule.startsWith(
|
|
126
|
+
if (what === "primitive") {
|
|
127
|
+
if (!fieldRule.startsWith("array-of-") &&
|
|
128
|
+
!fieldRule.startsWith("object-") &&
|
|
129
|
+
!fieldRule.startsWith("fk-")) {
|
|
152
130
|
return this.is(what, fieldRule);
|
|
153
131
|
}
|
|
154
132
|
return undefined;
|
|
@@ -163,15 +141,15 @@ export class MachineDb {
|
|
|
163
141
|
}
|
|
164
142
|
extract(type, fieldRule) {
|
|
165
143
|
function extractAfter(pattern, source) {
|
|
166
|
-
const reg = source?.split(
|
|
144
|
+
const reg = source?.split("(")?.[0];
|
|
167
145
|
return reg.split(pattern)[1];
|
|
168
146
|
}
|
|
169
147
|
function extractArgs(source) {
|
|
170
|
-
const [piece, remaining] = source.split(
|
|
148
|
+
const [piece, remaining] = source.split("(");
|
|
171
149
|
if (!remaining)
|
|
172
150
|
return { piece: piece.trim(), args: undefined };
|
|
173
|
-
const [central] = remaining?.split(
|
|
174
|
-
const args = central?.split(
|
|
151
|
+
const [central] = remaining?.split(")");
|
|
152
|
+
const args = central?.split(" ");
|
|
175
153
|
return { piece: piece.trim(), args };
|
|
176
154
|
}
|
|
177
155
|
let extractedArgs = extractArgs(fieldRule);
|
|
@@ -179,594 +157,23 @@ export class MachineDb {
|
|
|
179
157
|
let is = extractedArgs?.piece;
|
|
180
158
|
let fieldArgs = extractedArgs?.args;
|
|
181
159
|
switch (type) {
|
|
182
|
-
case
|
|
183
|
-
fieldType = extractAfter(
|
|
160
|
+
case "array":
|
|
161
|
+
fieldType = extractAfter("array-of-", fieldRule);
|
|
184
162
|
is = is ?? fieldType;
|
|
185
163
|
break;
|
|
186
|
-
case
|
|
187
|
-
fieldType = extractAfter(
|
|
164
|
+
case "object":
|
|
165
|
+
fieldType = extractAfter("object-", fieldRule);
|
|
188
166
|
is = is ?? fieldType;
|
|
189
167
|
break;
|
|
190
|
-
case
|
|
191
|
-
fieldType =
|
|
168
|
+
case "fk":
|
|
169
|
+
fieldType = "fk-" + extractAfter("fk-", fieldRule);
|
|
192
170
|
is = extractedArgs?.piece;
|
|
193
171
|
break;
|
|
194
|
-
case
|
|
172
|
+
case "primitive":
|
|
195
173
|
fieldType = extractedArgs?.piece;
|
|
196
174
|
is = is ?? fieldType;
|
|
197
175
|
break;
|
|
198
176
|
}
|
|
199
177
|
return { fieldType, fieldRule, fieldArgs, is: type };
|
|
200
178
|
}
|
|
201
|
-
fks(collection) {
|
|
202
|
-
let fks = this.collection(collection).getModelTemplateFks();
|
|
203
|
-
let out = {};
|
|
204
|
-
if (fks) {
|
|
205
|
-
Object.keys(fks).forEach((collection) => {
|
|
206
|
-
out[collection] = this.parseRawCollection(collection);
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
return out;
|
|
210
|
-
}
|
|
211
|
-
reverseFks(targetCollection) {
|
|
212
|
-
const result = {};
|
|
213
|
-
Object.entries(this.model).forEach(([collectionName, collectionModel]) => {
|
|
214
|
-
const template = collectionModel.template;
|
|
215
|
-
if (template && template.fks) {
|
|
216
|
-
Object.entries(template.fks).forEach(([fkName, fkConfig]) => {
|
|
217
|
-
if (fkConfig?.code === targetCollection) {
|
|
218
|
-
if (!result[collectionName]) {
|
|
219
|
-
result[collectionName] = {};
|
|
220
|
-
}
|
|
221
|
-
result[collectionName][fkName] = fkConfig;
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
return result;
|
|
227
|
-
}
|
|
228
|
-
iterateArrayField(collection, fieldName, data) {
|
|
229
|
-
const fieldInfo = this.parseCollectionFieldName(collection, fieldName);
|
|
230
|
-
if (fieldInfo?.is !== 'array' || !Array.isArray(data)) {
|
|
231
|
-
return [];
|
|
232
|
-
}
|
|
233
|
-
return data.map((_, idx) => ({ ...fieldInfo, fieldName: `${String(fieldName)}[${idx}]` }));
|
|
234
|
-
}
|
|
235
|
-
iterateObjectField(collection, fieldName, data) {
|
|
236
|
-
const fieldInfo = this.parseCollectionFieldName(collection, fieldName);
|
|
237
|
-
if (fieldInfo?.is !== 'object' || typeof data !== 'object' || data === null) {
|
|
238
|
-
return [];
|
|
239
|
-
}
|
|
240
|
-
return Object.keys(data).map((key) => ({ ...fieldInfo, fieldName: `${String(fieldName)}.${key}` }));
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Internal helper to construct an IDbForge object from its components.
|
|
244
|
-
*/
|
|
245
|
-
forge({ collection, fieldName, fieldType, fieldRule, fieldArgs, is }) {
|
|
246
|
-
return { collection, fieldName, fieldType, fieldRule, fieldArgs, is };
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
/* Single collection template relies on IDbBase */
|
|
250
|
-
export class IDbCollection {
|
|
251
|
-
collection;
|
|
252
|
-
#template;
|
|
253
|
-
#machineDb;
|
|
254
|
-
#model;
|
|
255
|
-
constructor(collection, idbBase, model) {
|
|
256
|
-
this.collection = collection;
|
|
257
|
-
this.#machineDb = idbBase;
|
|
258
|
-
this.#model = model[String(collection)];
|
|
259
|
-
this.#template = this.#model['template'];
|
|
260
|
-
}
|
|
261
|
-
get model() {
|
|
262
|
-
return this.#model;
|
|
263
|
-
}
|
|
264
|
-
get fields() {
|
|
265
|
-
return this.#template?.fields;
|
|
266
|
-
}
|
|
267
|
-
getPresentation() {
|
|
268
|
-
return this.#template?.presentation;
|
|
269
|
-
}
|
|
270
|
-
getFieldRule(fieldName) {
|
|
271
|
-
return this.fields[String(fieldName)];
|
|
272
|
-
}
|
|
273
|
-
getTemplate() {
|
|
274
|
-
return this.#template;
|
|
275
|
-
}
|
|
276
|
-
getModelTemplateFks() {
|
|
277
|
-
return this.#template?.fks;
|
|
278
|
-
}
|
|
279
|
-
getIndexName() {
|
|
280
|
-
return this.#template?.index;
|
|
281
|
-
}
|
|
282
|
-
collectionValues() {
|
|
283
|
-
return new IDbCollectionValues(this.collection, this.#machineDb);
|
|
284
|
-
}
|
|
285
|
-
collectionFieldValues(data) {
|
|
286
|
-
return new IDbCollectionFieldValues(this.collection, data, this.collectionValues());
|
|
287
|
-
}
|
|
288
|
-
fieldForge(fieldName, data) {
|
|
289
|
-
return new IDbCollectionFieldForge(this.collection, fieldName, data, this.collectionValues());
|
|
290
|
-
}
|
|
291
|
-
getFormValidate() {
|
|
292
|
-
return new IDbFormValidate(this.collection, this.#machineDb);
|
|
293
|
-
}
|
|
294
|
-
fks() {
|
|
295
|
-
return this.#machineDb.fks(this.collection);
|
|
296
|
-
}
|
|
297
|
-
reverseFks() {
|
|
298
|
-
return this.#machineDb.reverseFks(this.collection);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* IDbCollectionValues
|
|
303
|
-
*
|
|
304
|
-
* This class provides utilities to display, format, and introspect field values for a given collection, using the schema and provided data.
|
|
305
|
-
* It is designed for dynamic UI rendering, presentation logic, and metadata extraction for form generation in schema-driven applications.
|
|
306
|
-
*
|
|
307
|
-
* Main responsibilities:
|
|
308
|
-
* - Holds a reference to the collection name and the schema (IDbBase).
|
|
309
|
-
* - Provides methods to format field values according to their type (number, text, array, object, etc.).
|
|
310
|
-
* - Supplies presentation logic for displaying records (e.g., presentation string, index value).
|
|
311
|
-
* - Offers input attribute generation for forms (inputDataSet).
|
|
312
|
-
* - Supports iteration over array/object fields for advanced UI layouts.
|
|
313
|
-
* - Enables access to field metadata for validation and rendering.
|
|
314
|
-
*
|
|
315
|
-
* Usage:
|
|
316
|
-
* const values = new IDbCollectionValues('agents');
|
|
317
|
-
* const display = values.presentation(agentData); // formatted display string
|
|
318
|
-
* const index = values.indexValue(agentData); // index field value
|
|
319
|
-
* const formatted = values.format('name', agentData); // formatted field value
|
|
320
|
-
* const attrs = values.getInputDataSet('name', agentData); // input attributes for forms
|
|
321
|
-
*
|
|
322
|
-
* This class is typically used via IDbBase.getCollectionValues for shared instance management.
|
|
323
|
-
* @template T - The type of the data object for the collection.
|
|
324
|
-
*/
|
|
325
|
-
export class IDbCollectionValues {
|
|
326
|
-
/**
|
|
327
|
-
* The IDbBase instance used for schema introspection.
|
|
328
|
-
*/
|
|
329
|
-
idbBase;
|
|
330
|
-
/**
|
|
331
|
-
* The collection name this instance operates on.
|
|
332
|
-
*/
|
|
333
|
-
collectionName;
|
|
334
|
-
/**
|
|
335
|
-
* Create a new IDbCollectionValues instance for a given collection.
|
|
336
|
-
* @param collectionName The collection name.
|
|
337
|
-
*/
|
|
338
|
-
constructor(collectionName, idbBase) {
|
|
339
|
-
this.collectionName = collectionName;
|
|
340
|
-
this.idbBase = idbBase ?? new MachineDb();
|
|
341
|
-
}
|
|
342
|
-
presentation(data) {
|
|
343
|
-
try {
|
|
344
|
-
this.#checkError(!this.#checkAccess(), 'Access denied', 'ACCESS_DENIED');
|
|
345
|
-
const presentation = this.idbBase.collection(this.collectionName).getPresentation();
|
|
346
|
-
this.#checkError(!presentation, 'Presentation template not found', 'TEMPLATE_NOT_FOUND');
|
|
347
|
-
const fields = presentation.split(' ');
|
|
348
|
-
return fields
|
|
349
|
-
.map((field) => {
|
|
350
|
-
const value = data[field];
|
|
351
|
-
return value !== null && value !== undefined ? String(value) : '';
|
|
352
|
-
})
|
|
353
|
-
.join(' ');
|
|
354
|
-
}
|
|
355
|
-
catch (error) {
|
|
356
|
-
IDbError.handleError(error);
|
|
357
|
-
return '';
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Get the value of the index field for a data object.
|
|
362
|
-
* @param data The data object.
|
|
363
|
-
* @returns The value of the index field, or null if not found.
|
|
364
|
-
*/
|
|
365
|
-
indexValue(data) {
|
|
366
|
-
try {
|
|
367
|
-
this.#checkError(!this.#checkAccess(), 'Access denied', 'ACCESS_DENIED');
|
|
368
|
-
const indexName = this.idbBase.collection(this.collectionName).getIndexName();
|
|
369
|
-
this.#checkError(!indexName, 'Index not found for collection', 'INDEX_NOT_FOUND');
|
|
370
|
-
this.#checkError(!(indexName in data), `Index field ${indexName} not found in data`, 'FIELD_NOT_FOUND');
|
|
371
|
-
return data[indexName];
|
|
372
|
-
}
|
|
373
|
-
catch (error) {
|
|
374
|
-
IDbError.handleError(error);
|
|
375
|
-
return null;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Format a field value for display, using the field type and schema.
|
|
380
|
-
* @param fieldName The field name.
|
|
381
|
-
* @param data The data object.
|
|
382
|
-
* @returns The formatted value as a string.
|
|
383
|
-
*/
|
|
384
|
-
format(fieldName, data) {
|
|
385
|
-
try {
|
|
386
|
-
this.#checkError(!this.#checkAccess(), 'Access denied', 'ACCESS_DENIED');
|
|
387
|
-
this.#checkError(!(fieldName in data), `Field ${String(fieldName)} not found in data`, 'FIELD_NOT_FOUND');
|
|
388
|
-
const fieldInfo = this.idbBase.parseCollectionFieldName(this.collectionName, fieldName);
|
|
389
|
-
this.#checkError(!fieldInfo, `Field ${String(fieldName)} not found in collection`, 'FIELD_NOT_FOUND');
|
|
390
|
-
switch (fieldInfo?.fieldType) {
|
|
391
|
-
case 'number':
|
|
392
|
-
return this.#formatNumberField(data[fieldName]);
|
|
393
|
-
case 'text':
|
|
394
|
-
case 'text-tiny':
|
|
395
|
-
case 'text-short':
|
|
396
|
-
case 'text-medium':
|
|
397
|
-
case 'text-long':
|
|
398
|
-
case 'text-giant':
|
|
399
|
-
return this.#formatTextField(data[fieldName], fieldInfo.fieldType);
|
|
400
|
-
default:
|
|
401
|
-
return String(data[fieldName]);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
catch (error) {
|
|
405
|
-
IDbError.handleError(error);
|
|
406
|
-
return '';
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Get a set of data-* attributes for a field, for use in form generation or UI.
|
|
411
|
-
* @param fieldName The field name.
|
|
412
|
-
* @param data The data object.
|
|
413
|
-
* @returns An object with data-* attributes for the field.
|
|
414
|
-
*/
|
|
415
|
-
getInputDataSet(fieldName, data) {
|
|
416
|
-
const fieldInfo = this.idbBase.parseCollectionFieldName(this.collectionName, fieldName);
|
|
417
|
-
const fieldType = fieldInfo?.fieldType ?? '';
|
|
418
|
-
const fieldArgs = fieldInfo?.fieldArgs?.join(' ') ?? '';
|
|
419
|
-
const indexName = this.idbBase.collection(this.collectionName).getIndexName();
|
|
420
|
-
return {
|
|
421
|
-
'data-collection': this.collectionName,
|
|
422
|
-
'data-collectionId': indexName && data?.[indexName] !== undefined ? String(data?.[indexName]) : '',
|
|
423
|
-
'data-fieldName': String(fieldName),
|
|
424
|
-
'data-fieldType': fieldType,
|
|
425
|
-
'data-fieldArgs': fieldArgs
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* Iterate over an array field and return an array of IDbForge objects for each element.
|
|
430
|
-
* @param fieldName The field name.
|
|
431
|
-
* @param data The array data.
|
|
432
|
-
* @returns An array of IDbForge objects.
|
|
433
|
-
*/
|
|
434
|
-
iterateArrayField(fieldName, data) {
|
|
435
|
-
return this.idbBase.iterateArrayField(this.collectionName, fieldName, data);
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* Iterate over an object field and return an array of IDbForge objects for each property.
|
|
439
|
-
* @param fieldName The field name.
|
|
440
|
-
* @param data The object data.
|
|
441
|
-
* @returns An array of IDbForge objects.
|
|
442
|
-
*/
|
|
443
|
-
iterateObjectField(fieldName, data) {
|
|
444
|
-
return this.idbBase.iterateObjectField(this.collectionName, fieldName, data);
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Internal: Format a number field for display.
|
|
448
|
-
* @param value The number value.
|
|
449
|
-
* @returns The formatted string.
|
|
450
|
-
*/
|
|
451
|
-
#formatNumberField(value) {
|
|
452
|
-
// Implement number formatting logic here
|
|
453
|
-
return value.toString();
|
|
454
|
-
}
|
|
455
|
-
/**
|
|
456
|
-
* Internal: Format a text field for display, with length limits by type.
|
|
457
|
-
* @param value The string value.
|
|
458
|
-
* @param type The text type (e.g. 'text-short').
|
|
459
|
-
* @returns The formatted string.
|
|
460
|
-
*/
|
|
461
|
-
#formatTextField(value, type) {
|
|
462
|
-
const lengths = {
|
|
463
|
-
'text-tiny': 10,
|
|
464
|
-
'text-short': 20,
|
|
465
|
-
'text-medium': 30,
|
|
466
|
-
'text-long': 40,
|
|
467
|
-
'text-giant': 50
|
|
468
|
-
};
|
|
469
|
-
const str = typeof value === 'string' ? value : String(value ?? '');
|
|
470
|
-
const maxLength = lengths[type] || str.length;
|
|
471
|
-
return str.substring(0, maxLength);
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Internal: Check if access is allowed (override for custom logic).
|
|
475
|
-
* @returns True if access is allowed.
|
|
476
|
-
*/
|
|
477
|
-
#checkAccess() {
|
|
478
|
-
// Implement access check logic here
|
|
479
|
-
return true;
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Internal: Throw an error if a condition is met.
|
|
483
|
-
* @param condition The condition to check.
|
|
484
|
-
* @param message The error message.
|
|
485
|
-
* @param code The error code.
|
|
486
|
-
*/
|
|
487
|
-
#checkError(condition, message, code) {
|
|
488
|
-
if (condition) {
|
|
489
|
-
IDbError.throwError(message, code);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
export class IDbCollectionFieldValues {
|
|
494
|
-
#collection;
|
|
495
|
-
#collectionValues;
|
|
496
|
-
#data;
|
|
497
|
-
/**
|
|
498
|
-
* Returns the IDbForge metadata for a given field name.
|
|
499
|
-
* @param fieldName The field name to introspect.
|
|
500
|
-
*/
|
|
501
|
-
getForge(fieldName) {
|
|
502
|
-
return this.#collectionValues.idbBase.parseCollectionFieldName(this.#collection, String(fieldName));
|
|
503
|
-
}
|
|
504
|
-
constructor(collection, data, collectionValues) {
|
|
505
|
-
this.#collection = collection;
|
|
506
|
-
this.#collectionValues = collectionValues ?? new IDbCollectionValues(collection);
|
|
507
|
-
this.#data = data;
|
|
508
|
-
}
|
|
509
|
-
format(fieldName) {
|
|
510
|
-
const fieldInfo = this.#collectionValues.idbBase.parseCollectionFieldName(this.#collection, fieldName);
|
|
511
|
-
if (fieldInfo?.is === 'array') {
|
|
512
|
-
return this.iterateArray(String(fieldName), this.#data);
|
|
513
|
-
}
|
|
514
|
-
if (fieldInfo?.is === 'object') {
|
|
515
|
-
return this.iterateObject(String(fieldName), this.#data);
|
|
516
|
-
}
|
|
517
|
-
return this.#collectionValues.format(fieldName, this.#data);
|
|
518
|
-
}
|
|
519
|
-
getInputDataSet(fieldName) {
|
|
520
|
-
return this.#collectionValues.getInputDataSet(String(fieldName), this.#data);
|
|
521
|
-
}
|
|
522
|
-
// renamed from parseCollectionFieldName
|
|
523
|
-
// get forge(): IDbForge | undefined {
|
|
524
|
-
// return undefined; // Pas de #fieldName dans cette classe, getter non pertinent
|
|
525
|
-
// }
|
|
526
|
-
iterateArray(fieldName, data) {
|
|
527
|
-
return this.#collectionValues.iterateArrayField(fieldName, data);
|
|
528
|
-
}
|
|
529
|
-
iterateObject(fieldName, data) {
|
|
530
|
-
return this.#collectionValues.iterateObjectField(fieldName, data);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* IDbCollectionFieldForge
|
|
535
|
-
*
|
|
536
|
-
* This class provides advanced metadata and formatting for a single field of a collection, given a data object.
|
|
537
|
-
* It is designed for dynamic UI generation, form rendering, and introspection in schema-driven applications.
|
|
538
|
-
*
|
|
539
|
-
* Main responsibilities:
|
|
540
|
-
* - Holds references to the collection name, field name, and the data object for context.
|
|
541
|
-
* - Provides access to the parsed field metadata (IDbForge) for the field, including type, rules, and arguments.
|
|
542
|
-
* - Offers formatting utilities for the field value, adapting to type (number, text, array, object, etc.).
|
|
543
|
-
* - Supplies input attributes and type hints for form generation (e.g., htmlInputType, inputDataSet).
|
|
544
|
-
* - Supports iteration over array/object fields for complex form layouts.
|
|
545
|
-
* - Enables extraction of raw data and field arguments for validation and UI logic.
|
|
546
|
-
*
|
|
547
|
-
* Usage:
|
|
548
|
-
* const forge = new IDbCollectionFieldForge('agents', 'name', agentData);
|
|
549
|
-
* const formatted = forge.format; // formatted value for display
|
|
550
|
-
* const inputType = forge.htmlInputType; // e.g. 'text', 'area', 'email', etc.
|
|
551
|
-
* const meta = forge.forge; // IDbForge metadata for the field
|
|
552
|
-
*
|
|
553
|
-
* This class is typically used via IDbBase.getCollectionFieldForge for shared instance management.
|
|
554
|
-
*/
|
|
555
|
-
export class IDbCollectionFieldForge {
|
|
556
|
-
#collection;
|
|
557
|
-
#collectionValues;
|
|
558
|
-
#fieldName;
|
|
559
|
-
#data;
|
|
560
|
-
constructor(collection, fieldName, data, collectionValues) {
|
|
561
|
-
this.#collection = collection;
|
|
562
|
-
this.#collectionValues = collectionValues ?? new IDbCollectionValues(collection);
|
|
563
|
-
this.#fieldName = String(fieldName);
|
|
564
|
-
this.#data = data;
|
|
565
|
-
}
|
|
566
|
-
get format() {
|
|
567
|
-
return this.#collectionValues.format(String(this.#fieldName), this.#data);
|
|
568
|
-
}
|
|
569
|
-
get inputDataSet() {
|
|
570
|
-
return this.#collectionValues.getInputDataSet(String(this.#fieldName), this.#data);
|
|
571
|
-
}
|
|
572
|
-
// renamed from parseCollectionFieldName
|
|
573
|
-
get forge() {
|
|
574
|
-
return this.#collectionValues.idbBase.parseCollectionFieldName(this.#collection, String(this.#fieldName));
|
|
575
|
-
}
|
|
576
|
-
get fieldArgs() {
|
|
577
|
-
return this.forge?.fieldArgs;
|
|
578
|
-
}
|
|
579
|
-
get fieldType() {
|
|
580
|
-
return this.forge?.fieldType;
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* will return text.inputBase for ['url', 'email', 'number', 'date', 'time', 'datetime', 'phone', 'password']
|
|
584
|
-
*/
|
|
585
|
-
get htmlInputType() {
|
|
586
|
-
let variant = this?.fieldType?.split('text-')?.[1] ?? this.fieldType;
|
|
587
|
-
if (variant === 'area')
|
|
588
|
-
return variant;
|
|
589
|
-
if (this.forge?.fieldType?.startsWith('text-'))
|
|
590
|
-
return 'text';
|
|
591
|
-
if (['url', 'email', 'number', 'date', 'time', 'datetime', 'phone', 'password'].includes(this.forge?.fieldType ?? '')) {
|
|
592
|
-
return this.forge?.fieldType?.trim?.() ?? 'text';
|
|
593
|
-
}
|
|
594
|
-
return 'text';
|
|
595
|
-
}
|
|
596
|
-
get rawData() {
|
|
597
|
-
return this.#data;
|
|
598
|
-
}
|
|
599
|
-
iterateArray(fieldName, data) {
|
|
600
|
-
return this.#collectionValues.iterateArrayField(fieldName, data);
|
|
601
|
-
}
|
|
602
|
-
iterateObject(fieldName, data) {
|
|
603
|
-
return this.#collectionValues.iterateObjectField(fieldName, data);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
class IDbValidationError extends Error {
|
|
607
|
-
field;
|
|
608
|
-
code;
|
|
609
|
-
constructor(field, code, message) {
|
|
610
|
-
super(message);
|
|
611
|
-
this.field = field;
|
|
612
|
-
this.code = code;
|
|
613
|
-
this.name = 'IDbValidationError';
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
export class IDbFormValidate {
|
|
617
|
-
collection;
|
|
618
|
-
idbCollections;
|
|
619
|
-
constructor(collection, idbCollections) {
|
|
620
|
-
this.collection = collection;
|
|
621
|
-
this.idbCollections = idbCollections ?? new MachineDb();
|
|
622
|
-
}
|
|
623
|
-
validateField(fieldName, value) {
|
|
624
|
-
try {
|
|
625
|
-
const fieldInfo = this.idbCollections.parseCollectionFieldName(this.collection, fieldName);
|
|
626
|
-
if (!fieldInfo) {
|
|
627
|
-
return { isValid: false, error: `Field ${String(fieldName)} not found in collection` };
|
|
628
|
-
}
|
|
629
|
-
// Vérification du type
|
|
630
|
-
if (!this.#validateType(value, fieldInfo.fieldType)) {
|
|
631
|
-
return this.#returnError(fieldName, fieldInfo.fieldType);
|
|
632
|
-
}
|
|
633
|
-
// Vérification des arguments du champ (required, etc.)
|
|
634
|
-
if (fieldInfo.fieldArgs) {
|
|
635
|
-
for (const arg of fieldInfo.fieldArgs) {
|
|
636
|
-
if (arg === 'required' && (value === undefined || value === null || value === '')) {
|
|
637
|
-
return this.#returnError(fieldName, 'required');
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
// Validations spécifiques selon le type de champ
|
|
642
|
-
switch (fieldInfo.fieldType) {
|
|
643
|
-
case enumPrimitive.email:
|
|
644
|
-
if (!this.validateEmail(value)) {
|
|
645
|
-
return this.#returnError(fieldName, fieldInfo.fieldType);
|
|
646
|
-
}
|
|
647
|
-
break;
|
|
648
|
-
case enumPrimitive.url:
|
|
649
|
-
if (!this.validateUrl(value)) {
|
|
650
|
-
return this.#returnError(fieldName, fieldInfo.fieldType);
|
|
651
|
-
}
|
|
652
|
-
break;
|
|
653
|
-
case enumPrimitive.phone:
|
|
654
|
-
if (!this.validatePhone(value)) {
|
|
655
|
-
return this.#returnError(fieldName, fieldInfo.fieldType);
|
|
656
|
-
}
|
|
657
|
-
break;
|
|
658
|
-
case enumPrimitive.date:
|
|
659
|
-
case enumPrimitive.datetime:
|
|
660
|
-
case enumPrimitive.time:
|
|
661
|
-
if (!this.validateDateTime(value, fieldInfo.fieldType)) {
|
|
662
|
-
return this.#returnError(fieldName, fieldInfo.fieldType);
|
|
663
|
-
}
|
|
664
|
-
break;
|
|
665
|
-
// Ajoutez d'autres cas spécifiques ici
|
|
666
|
-
}
|
|
667
|
-
return { isValid: true };
|
|
668
|
-
}
|
|
669
|
-
catch (error) {
|
|
670
|
-
if (error instanceof IDbValidationError) {
|
|
671
|
-
return { isValid: false, error: error.message };
|
|
672
|
-
}
|
|
673
|
-
throw error;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Validate a single field value for a collection.
|
|
678
|
-
* @param fieldName The field name.
|
|
679
|
-
* @param value The value to validate.
|
|
680
|
-
* @returns True if valid, false otherwise.
|
|
681
|
-
*/
|
|
682
|
-
validateFieldValue(fieldName, value) {
|
|
683
|
-
const result = this.validateField(fieldName, value);
|
|
684
|
-
return !!result.isValid;
|
|
685
|
-
}
|
|
686
|
-
validateForm(formData, options = {}) {
|
|
687
|
-
const errors = {};
|
|
688
|
-
const invalidFields = [];
|
|
689
|
-
let isValid = true;
|
|
690
|
-
const fields = this.idbCollections.collection(this.collection).getTemplate().fields;
|
|
691
|
-
if (!fields) {
|
|
692
|
-
return {
|
|
693
|
-
isValid: false,
|
|
694
|
-
errors: { general: 'Collection template not found' },
|
|
695
|
-
invalidFields: ['general']
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
for (const [fieldName, fieldRule] of Object.entries(fields)) {
|
|
699
|
-
// Ignorer les champs spécifiés dans options.ignoreFields
|
|
700
|
-
if (options.ignoreFields && options.ignoreFields.includes(fieldName)) {
|
|
701
|
-
continue;
|
|
702
|
-
}
|
|
703
|
-
const result = this.validateField(fieldName, formData[fieldName]);
|
|
704
|
-
if (!result.isValid) {
|
|
705
|
-
errors[fieldName] = result.error || 'Invalid field';
|
|
706
|
-
invalidFields.push(fieldName);
|
|
707
|
-
isValid = false;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
return { isValid, errors, invalidFields };
|
|
711
|
-
}
|
|
712
|
-
#validateType(value, type) {
|
|
713
|
-
switch (type) {
|
|
714
|
-
case enumPrimitive.number:
|
|
715
|
-
return typeof value === 'number' && !isNaN(value);
|
|
716
|
-
case enumPrimitive.boolean:
|
|
717
|
-
return typeof value === 'boolean';
|
|
718
|
-
case enumPrimitive.text:
|
|
719
|
-
case enumPrimitive.email:
|
|
720
|
-
case enumPrimitive.url:
|
|
721
|
-
case enumPrimitive.phone:
|
|
722
|
-
case enumPrimitive.password:
|
|
723
|
-
return typeof value === 'string';
|
|
724
|
-
case enumPrimitive.date:
|
|
725
|
-
case enumPrimitive.datetime:
|
|
726
|
-
case enumPrimitive.time:
|
|
727
|
-
return value instanceof Date || typeof value === 'string';
|
|
728
|
-
case enumPrimitive.any:
|
|
729
|
-
return true;
|
|
730
|
-
default:
|
|
731
|
-
return true; // Pour les types non gérés, on considère que c'est valide
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
#returnError(fieldName, enumCode) {
|
|
735
|
-
throw new IDbValidationError(String(fieldName), enumCode ?? 'unknown', `Invalid format for field ${String(fieldName)}. Cause "${enumCode}" `);
|
|
736
|
-
}
|
|
737
|
-
validateEmail(email) {
|
|
738
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
739
|
-
return emailRegex.test(email);
|
|
740
|
-
}
|
|
741
|
-
validateUrl(url) {
|
|
742
|
-
try {
|
|
743
|
-
new URL(url);
|
|
744
|
-
return true;
|
|
745
|
-
}
|
|
746
|
-
catch {
|
|
747
|
-
return false;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
validatePhone(phone) {
|
|
751
|
-
// Ceci est un exemple simple. Vous pouvez ajuster selon vos besoins spécifiques
|
|
752
|
-
const phoneRegex = /^\+?[\d\s-]{10,}$/;
|
|
753
|
-
return phoneRegex.test(phone);
|
|
754
|
-
}
|
|
755
|
-
validateDateTime(value, type) {
|
|
756
|
-
const date = value instanceof Date ? value : new Date(value);
|
|
757
|
-
if (isNaN(date.getTime()))
|
|
758
|
-
return false;
|
|
759
|
-
switch (type) {
|
|
760
|
-
case enumPrimitive.date:
|
|
761
|
-
return true; // La conversion en Date a déjà validé le format
|
|
762
|
-
case enumPrimitive.time:
|
|
763
|
-
// Vérifiez si la chaîne contient uniquement l'heure
|
|
764
|
-
return /^([01]\d|2[0-3]):([0-5]\d)(:([0-5]\d))?$/.test(value);
|
|
765
|
-
case enumPrimitive.datetime:
|
|
766
|
-
return true; // La conversion en Date a déjà validé le format
|
|
767
|
-
default:
|
|
768
|
-
return false;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
179
|
}
|
|
772
|
-
// (fin de fichier)
|