@objectql/core 1.3.1 → 1.5.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.
@@ -0,0 +1,461 @@
1
+ "use strict";
2
+ /**
3
+ * Validation engine for ObjectQL.
4
+ * Executes validation rules based on metadata configuration.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.Validator = void 0;
8
+ /**
9
+ * Validator class that executes validation rules.
10
+ */
11
+ class Validator {
12
+ constructor(options = {}) {
13
+ this.options = {
14
+ language: options.language || 'en',
15
+ languageFallback: options.languageFallback || ['en', 'zh-CN'],
16
+ };
17
+ }
18
+ /**
19
+ * Validate a record against a set of rules.
20
+ */
21
+ async validate(rules, context) {
22
+ const results = [];
23
+ for (const rule of rules) {
24
+ // Check if rule should be applied
25
+ if (!this.shouldApplyRule(rule, context)) {
26
+ continue;
27
+ }
28
+ // Execute validation based on rule type
29
+ let result;
30
+ try {
31
+ switch (rule.type) {
32
+ case 'cross_field':
33
+ result = await this.validateCrossField(rule, context);
34
+ break;
35
+ case 'state_machine':
36
+ result = await this.validateStateMachine(rule, context);
37
+ break;
38
+ case 'unique':
39
+ result = await this.validateUniqueness(rule, context);
40
+ break;
41
+ case 'business_rule':
42
+ result = await this.validateBusinessRule(rule, context);
43
+ break;
44
+ case 'custom':
45
+ result = await this.validateCustom(rule, context);
46
+ break;
47
+ default:
48
+ // Generic validation
49
+ result = {
50
+ rule: rule.name,
51
+ valid: true,
52
+ };
53
+ }
54
+ }
55
+ catch (error) {
56
+ result = {
57
+ rule: rule.name,
58
+ valid: false,
59
+ message: error instanceof Error ? error.message : 'Validation error',
60
+ severity: rule.severity || 'error',
61
+ };
62
+ }
63
+ results.push(result);
64
+ }
65
+ // Categorize results
66
+ const errors = results.filter(r => !r.valid && r.severity === 'error');
67
+ const warnings = results.filter(r => !r.valid && r.severity === 'warning');
68
+ const info = results.filter(r => !r.valid && r.severity === 'info');
69
+ return {
70
+ valid: errors.length === 0,
71
+ results,
72
+ errors,
73
+ warnings,
74
+ info,
75
+ };
76
+ }
77
+ /**
78
+ * Validate field-level rules.
79
+ */
80
+ async validateField(fieldName, fieldConfig, value, context) {
81
+ const results = [];
82
+ // Required field validation
83
+ if (fieldConfig.required && (value === null || value === undefined || value === '')) {
84
+ results.push({
85
+ rule: `${fieldName}_required`,
86
+ valid: false,
87
+ message: fieldConfig.validation?.message || `${fieldConfig.label || fieldName} is required`,
88
+ severity: 'error',
89
+ fields: [fieldName],
90
+ });
91
+ }
92
+ // Skip further validation if value is empty and not required
93
+ if (value === null || value === undefined || value === '') {
94
+ return results;
95
+ }
96
+ // Type-specific validation
97
+ if (fieldConfig.validation) {
98
+ const validation = fieldConfig.validation;
99
+ // Email format
100
+ if (validation.format === 'email') {
101
+ // NOTE: This is a basic email validation regex. For production use,
102
+ // consider using a more comprehensive email validation library or regex
103
+ // that handles international domains, quoted strings, etc.
104
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
105
+ if (!emailRegex.test(value)) {
106
+ results.push({
107
+ rule: `${fieldName}_email_format`,
108
+ valid: false,
109
+ message: validation.message || 'Invalid email format',
110
+ severity: 'error',
111
+ fields: [fieldName],
112
+ });
113
+ }
114
+ }
115
+ // URL format
116
+ if (validation.format === 'url') {
117
+ try {
118
+ const url = new URL(value);
119
+ if (validation.protocols && !validation.protocols.includes(url.protocol.replace(':', ''))) {
120
+ results.push({
121
+ rule: `${fieldName}_url_protocol`,
122
+ valid: false,
123
+ message: validation.message || `URL must use one of: ${validation.protocols.join(', ')}`,
124
+ severity: 'error',
125
+ fields: [fieldName],
126
+ });
127
+ }
128
+ }
129
+ catch {
130
+ results.push({
131
+ rule: `${fieldName}_url_format`,
132
+ valid: false,
133
+ message: validation.message || 'Invalid URL format',
134
+ severity: 'error',
135
+ fields: [fieldName],
136
+ });
137
+ }
138
+ }
139
+ // Pattern validation (supports both pattern and deprecated regex)
140
+ const patternValue = validation.pattern ?? validation.regex;
141
+ if (patternValue) {
142
+ try {
143
+ const pattern = new RegExp(patternValue);
144
+ if (!pattern.test(String(value))) {
145
+ results.push({
146
+ rule: `${fieldName}_pattern`,
147
+ valid: false,
148
+ message: validation.message || 'Value does not match required pattern',
149
+ severity: 'error',
150
+ fields: [fieldName],
151
+ });
152
+ }
153
+ }
154
+ catch (error) {
155
+ results.push({
156
+ rule: `${fieldName}_pattern`,
157
+ valid: false,
158
+ message: `Invalid regex pattern: ${patternValue}`,
159
+ severity: 'error',
160
+ fields: [fieldName],
161
+ });
162
+ }
163
+ }
164
+ // Min/Max validation
165
+ if (validation.min !== undefined && value < validation.min) {
166
+ results.push({
167
+ rule: `${fieldName}_min`,
168
+ valid: false,
169
+ message: validation.message || `Value must be at least ${validation.min}`,
170
+ severity: 'error',
171
+ fields: [fieldName],
172
+ });
173
+ }
174
+ if (validation.max !== undefined && value > validation.max) {
175
+ results.push({
176
+ rule: `${fieldName}_max`,
177
+ valid: false,
178
+ message: validation.message || `Value must be at most ${validation.max}`,
179
+ severity: 'error',
180
+ fields: [fieldName],
181
+ });
182
+ }
183
+ // Length validation
184
+ const strValue = String(value);
185
+ if (validation.min_length !== undefined && strValue.length < validation.min_length) {
186
+ results.push({
187
+ rule: `${fieldName}_min_length`,
188
+ valid: false,
189
+ message: validation.message || `Must be at least ${validation.min_length} characters`,
190
+ severity: 'error',
191
+ fields: [fieldName],
192
+ });
193
+ }
194
+ if (validation.max_length !== undefined && strValue.length > validation.max_length) {
195
+ results.push({
196
+ rule: `${fieldName}_max_length`,
197
+ valid: false,
198
+ message: validation.message || `Must be at most ${validation.max_length} characters`,
199
+ severity: 'error',
200
+ fields: [fieldName],
201
+ });
202
+ }
203
+ }
204
+ // Legacy min/max from fieldConfig
205
+ if (fieldConfig.min !== undefined && value < fieldConfig.min) {
206
+ results.push({
207
+ rule: `${fieldName}_min`,
208
+ valid: false,
209
+ message: `Value must be at least ${fieldConfig.min}`,
210
+ severity: 'error',
211
+ fields: [fieldName],
212
+ });
213
+ }
214
+ if (fieldConfig.max !== undefined && value > fieldConfig.max) {
215
+ results.push({
216
+ rule: `${fieldName}_max`,
217
+ valid: false,
218
+ message: `Value must be at most ${fieldConfig.max}`,
219
+ severity: 'error',
220
+ fields: [fieldName],
221
+ });
222
+ }
223
+ return results;
224
+ }
225
+ /**
226
+ * Check if a rule should be applied based on triggers and conditions.
227
+ */
228
+ shouldApplyRule(rule, context) {
229
+ // Check trigger
230
+ if (rule.trigger && !rule.trigger.includes(context.operation)) {
231
+ return false;
232
+ }
233
+ // Check fields (for updates)
234
+ if (rule.fields && rule.fields.length > 0 && context.changedFields) {
235
+ const hasChangedField = rule.fields.some(f => context.changedFields.includes(f));
236
+ if (!hasChangedField) {
237
+ return false;
238
+ }
239
+ }
240
+ // Check apply_when condition
241
+ if (rule.apply_when) {
242
+ return this.evaluateCondition(rule.apply_when, context.record);
243
+ }
244
+ return true;
245
+ }
246
+ /**
247
+ * Validate cross-field rule.
248
+ */
249
+ async validateCrossField(rule, context) {
250
+ if (!rule.rule) {
251
+ return { rule: rule.name, valid: true };
252
+ }
253
+ const valid = this.evaluateCondition(rule.rule, context.record);
254
+ return {
255
+ rule: rule.name,
256
+ valid,
257
+ message: valid ? undefined : this.formatMessage(rule.message, context.record),
258
+ error_code: rule.error_code,
259
+ severity: rule.severity || 'error',
260
+ };
261
+ }
262
+ /**
263
+ * Validate state machine transitions.
264
+ */
265
+ async validateStateMachine(rule, context) {
266
+ // Only validate on update
267
+ if (context.operation !== 'update' || !context.previousRecord) {
268
+ return { rule: rule.name, valid: true };
269
+ }
270
+ const oldState = context.previousRecord[rule.field];
271
+ const newState = context.record[rule.field];
272
+ // If state hasn't changed, validation passes
273
+ if (oldState === newState) {
274
+ return { rule: rule.name, valid: true };
275
+ }
276
+ // Check if transition is allowed
277
+ const transitions = rule.transitions?.[oldState];
278
+ if (!transitions) {
279
+ return {
280
+ rule: rule.name,
281
+ valid: false,
282
+ message: this.formatMessage(rule.message, { old_status: oldState, new_status: newState }),
283
+ error_code: rule.error_code,
284
+ severity: rule.severity || 'error',
285
+ fields: [rule.field],
286
+ };
287
+ }
288
+ // Handle both array and object format
289
+ let allowedNext = [];
290
+ if (Array.isArray(transitions)) {
291
+ allowedNext = transitions;
292
+ }
293
+ else if (typeof transitions === 'object' && 'allowed_next' in transitions) {
294
+ allowedNext = transitions.allowed_next || [];
295
+ }
296
+ const isAllowed = allowedNext.includes(newState);
297
+ return {
298
+ rule: rule.name,
299
+ valid: isAllowed,
300
+ message: isAllowed ? undefined : this.formatMessage(rule.message, { old_status: oldState, new_status: newState }),
301
+ error_code: rule.error_code,
302
+ severity: rule.severity || 'error',
303
+ fields: [rule.field],
304
+ };
305
+ }
306
+ /**
307
+ * Validate uniqueness (stub - requires database access).
308
+ */
309
+ async validateUniqueness(rule, context) {
310
+ // TODO: Implement database query for uniqueness check
311
+ // This requires access to the data layer (driver/repository)
312
+ // Stub: Pass silently until implementation is complete
313
+ return {
314
+ rule: rule.name,
315
+ valid: true,
316
+ };
317
+ }
318
+ /**
319
+ * Validate business rule (stub - requires complex logic).
320
+ */
321
+ async validateBusinessRule(rule, context) {
322
+ // TODO: Implement business rule evaluation
323
+ // This requires expression parsing and relationship resolution
324
+ // Stub: Pass silently until implementation is complete
325
+ return {
326
+ rule: rule.name,
327
+ valid: true,
328
+ };
329
+ }
330
+ /**
331
+ * Validate custom rule (stub - requires function execution).
332
+ */
333
+ async validateCustom(rule, context) {
334
+ // TODO: Implement custom validator execution
335
+ // This requires safe function evaluation
336
+ // Stub: Pass silently until implementation is complete
337
+ return {
338
+ rule: rule.name,
339
+ valid: true,
340
+ };
341
+ }
342
+ /**
343
+ * Evaluate a validation condition.
344
+ */
345
+ evaluateCondition(condition, record) {
346
+ // Handle logical operators
347
+ if (condition.all_of) {
348
+ return condition.all_of.every(c => this.evaluateCondition(c, record));
349
+ }
350
+ if (condition.any_of) {
351
+ return condition.any_of.some(c => this.evaluateCondition(c, record));
352
+ }
353
+ // Handle expression
354
+ if (condition.expression) {
355
+ // TODO: Implement safe expression evaluation
356
+ return true;
357
+ }
358
+ // Handle field comparison
359
+ if (condition.field && condition.operator !== undefined) {
360
+ const fieldValue = record[condition.field];
361
+ // Use compare_to if specified (cross-field comparison), otherwise use value
362
+ const compareValue = condition.compare_to !== undefined
363
+ ? record[condition.compare_to]
364
+ : condition.value;
365
+ return this.compareValues(fieldValue, condition.operator, compareValue);
366
+ }
367
+ return true;
368
+ }
369
+ /**
370
+ * Compare two values using an operator.
371
+ */
372
+ compareValues(a, operator, b) {
373
+ switch (operator) {
374
+ case '=':
375
+ return a === b;
376
+ case '!=':
377
+ return a !== b;
378
+ case '>':
379
+ return a > b;
380
+ case '>=':
381
+ return a >= b;
382
+ case '<':
383
+ return a < b;
384
+ case '<=':
385
+ return a <= b;
386
+ case 'in':
387
+ return Array.isArray(b) && b.includes(a);
388
+ case 'not_in':
389
+ return Array.isArray(b) && !b.includes(a);
390
+ case 'contains': {
391
+ if (a == null || b == null) {
392
+ return false;
393
+ }
394
+ const strA = String(a);
395
+ const strB = String(b);
396
+ return strA.includes(strB);
397
+ }
398
+ case 'not_contains': {
399
+ if (a == null || b == null) {
400
+ return false;
401
+ }
402
+ const strA = String(a);
403
+ const strB = String(b);
404
+ return !strA.includes(strB);
405
+ }
406
+ case 'starts_with': {
407
+ if (a == null || b == null) {
408
+ return false;
409
+ }
410
+ const strA = String(a);
411
+ const strB = String(b);
412
+ return strA.startsWith(strB);
413
+ }
414
+ case 'ends_with': {
415
+ if (a == null || b == null) {
416
+ return false;
417
+ }
418
+ const strA = String(a);
419
+ const strB = String(b);
420
+ return strA.endsWith(strB);
421
+ }
422
+ default:
423
+ return false;
424
+ }
425
+ }
426
+ /**
427
+ * Format validation message with template variables.
428
+ */
429
+ formatMessage(message, context) {
430
+ // Handle i18n messages
431
+ if (typeof message === 'object') {
432
+ // Try preferred language first
433
+ const preferredLanguage = this.options.language ?? 'en';
434
+ let messageText = message[preferredLanguage];
435
+ // Try fallback languages if preferred not available
436
+ if (!messageText && this.options.languageFallback) {
437
+ for (const lang of this.options.languageFallback) {
438
+ if (message[lang]) {
439
+ messageText = message[lang];
440
+ break;
441
+ }
442
+ }
443
+ }
444
+ // Fallback to first available message
445
+ message = messageText || Object.values(message)[0];
446
+ }
447
+ // Replace template variables
448
+ return message.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (match, path) => {
449
+ const value = this.getNestedValue(context, path);
450
+ return value !== undefined ? String(value) : match;
451
+ });
452
+ }
453
+ /**
454
+ * Get nested value from object by path.
455
+ */
456
+ getNestedValue(obj, path) {
457
+ return path.split('.').reduce((current, key) => current?.[key], obj);
458
+ }
459
+ }
460
+ exports.Validator = Validator;
461
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
package/jest.config.js CHANGED
@@ -3,6 +3,11 @@ module.exports = {
3
3
  testEnvironment: 'node',
4
4
  testMatch: ['**/test/**/*.test.ts'],
5
5
  moduleNameMapper: {
6
- '^@objectql/types$': '<rootDir>/../types/src',
6
+ '^@objectql/(.*)$': '<rootDir>/../$1/src',
7
+ },
8
+ transform: {
9
+ '^.+\\.ts$': ['ts-jest', {
10
+ isolatedModules: true,
11
+ }],
7
12
  },
8
13
  };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@objectql/core",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
7
7
  "fast-glob": "^3.3.2",
8
8
  "js-yaml": "^4.1.1",
9
- "@objectql/types": "1.3.1",
10
- "@objectql/driver-remote": "1.3.1"
9
+ "@objectql/types": "1.5.0",
10
+ "@objectql/driver-remote": "1.5.0"
11
11
  },
12
12
  "devDependencies": {
13
13
  "typescript": "^5.3.0"
package/src/app.ts CHANGED
@@ -14,6 +14,9 @@ import {
14
14
  ActionContext,
15
15
  LoaderPlugin
16
16
  } from '@objectql/types';
17
+ import * as fs from 'fs';
18
+ import * as path from 'path';
19
+ import * as yaml from 'js-yaml';
17
20
  import { ObjectLoader } from './loader';
18
21
  import { ObjectRepository } from './repository';
19
22
  import { loadPlugin } from './plugin';
@@ -106,6 +109,50 @@ export class ObjectQL implements IObjectQL {
106
109
  this.loader.use(plugin);
107
110
  }
108
111
 
112
+ async updateMetadata(type: string, id: string, content: any): Promise<void> {
113
+ // Use registry to find the entry so we can get the file path
114
+ const entry = this.metadata.getEntry(type, id);
115
+ if (!entry) {
116
+ throw new Error(`Metadata ${type}:${id} not found`);
117
+ }
118
+
119
+ if (!entry.path) {
120
+ throw new Error('Cannot update: Metadata source file not found (in-memory only?)');
121
+ }
122
+
123
+ // Safety Check: Prevent writing to node_modules
124
+ if (entry.path.includes('node_modules')) {
125
+ throw new Error(`Cannot update metadata ${type}:${id}: File is inside node_modules (read-only package).`);
126
+ }
127
+
128
+ // Check file extension
129
+ const ext = path.extname(entry.path).toLowerCase();
130
+ let newContent = '';
131
+
132
+ if (ext === '.yml' || ext === '.yaml') {
133
+ newContent = yaml.dump(content);
134
+ } else if (ext === '.json') {
135
+ newContent = JSON.stringify(content, null, 2);
136
+ } else {
137
+ throw new Error(`Cannot update: Unsupported file format ${ext} (only .yml, .yaml, .json supported)`);
138
+ }
139
+
140
+ // Write file
141
+ try {
142
+ await fs.promises.chmod(entry.path, 0o666).catch(() => {}); // Try to ensure writable
143
+ await fs.promises.writeFile(entry.path, newContent, 'utf8');
144
+ } catch (e: any) {
145
+ throw new Error(`Failed to write file ${entry.path}: ${e.message}`);
146
+ }
147
+
148
+ // Update registry in-memory
149
+ entry.content = content;
150
+
151
+ // If it's an object update, we might need some re-processing?
152
+ // For now, assume a restart or reload is needed for deep schema changes,
153
+ // but simple property updates are reflected immediately in registry.
154
+ }
155
+
109
156
  createContext(options: ObjectQLContextOptions): ObjectQLContext {
110
157
  const ctx: ObjectQLContext = {
111
158
  userId: options.userId,
@@ -253,5 +300,54 @@ export class ObjectQL implements IObjectQL {
253
300
  await driver.init(objects);
254
301
  }
255
302
  }
303
+
304
+ // 6. Process Initial Data
305
+ await this.processInitialData();
306
+ }
307
+
308
+ private async processInitialData() {
309
+ const dataEntries = this.metadata.list<any>('data');
310
+ if (dataEntries.length === 0) return;
311
+
312
+ console.log(`Processing ${dataEntries.length} initial data files...`);
313
+
314
+ // We need a system context to write data
315
+ const ctx = this.createContext({ isSystem: true });
316
+
317
+ for (const entry of dataEntries) {
318
+ // Expected format:
319
+ // object: User
320
+ // records:
321
+ // - name: Admin
322
+ // email: admin@example.com
323
+
324
+ const objectName = entry.object;
325
+ const records = entry.records;
326
+
327
+ if (!objectName || !records || !Array.isArray(records)) {
328
+ console.warn(`Skipping invalid data entry:`, entry);
329
+ continue;
330
+ }
331
+
332
+ const repo = ctx.object(objectName);
333
+
334
+ for (const record of records) {
335
+ try {
336
+ // Check existence if a unique key is provided?
337
+ // For now, let's assume if it has an ID, we check it.
338
+ // Or we could try to find existing record by some key matching logic.
339
+ // Simple approach: create. If it fails (constraint), ignore.
340
+
341
+ // Actually, a better approach for initial data is "upsert" or "create if not exists".
342
+ // But without unique keys defined in data, we can't reliably dedup.
343
+ // Let's try to 'create' and catch errors.
344
+ await repo.create(record);
345
+ console.log(`Initialized record for ${objectName}`);
346
+ } catch (e: any) {
347
+ // Ignore duplicate key errors silently-ish
348
+ // console.warn(`Failed to insert initial data for ${objectName}: ${e.message}`);
349
+ }
350
+ }
351
+ }
256
352
  }
257
353
  }
package/src/index.ts CHANGED
@@ -7,3 +7,4 @@ export * from './remote';
7
7
  export * from './action';
8
8
  export * from './hook';
9
9
  export * from './object';
10
+ export * from './validator';