@api-client/core 0.18.24 → 0.18.26
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/build/src/modeling/DomainEntity.d.ts +6 -1
- package/build/src/modeling/DomainEntity.d.ts.map +1 -1
- package/build/src/modeling/DomainEntity.js +24 -5
- package/build/src/modeling/DomainEntity.js.map +1 -1
- package/build/src/modeling/Semantics.d.ts +254 -0
- package/build/src/modeling/Semantics.d.ts.map +1 -1
- package/build/src/modeling/Semantics.js +328 -0
- package/build/src/modeling/Semantics.js.map +1 -1
- package/build/src/modeling/definitions/Email.js +1 -1
- package/build/src/modeling/definitions/Email.js.map +1 -1
- package/build/src/modeling/definitions/Password.d.ts.map +1 -1
- package/build/src/modeling/definitions/Password.js +1 -3
- package/build/src/modeling/definitions/Password.js.map +1 -1
- package/build/src/modeling/helpers/Intelisense.d.ts +7 -7
- package/build/src/modeling/helpers/Intelisense.d.ts.map +1 -1
- package/build/src/modeling/helpers/Intelisense.js +24 -58
- package/build/src/modeling/helpers/Intelisense.js.map +1 -1
- package/build/src/modeling/templates/meta/blog-publishing-platform.json +1 -1
- package/build/src/modeling/templates/meta/financial-services-platform.json +1 -1
- package/build/src/modeling/templates/meta/index.d.ts +1 -1
- package/build/src/modeling/templates/meta/index.js +1 -1
- package/build/src/modeling/templates/meta/index.js.map +1 -1
- package/build/src/modeling/templates/meta/iot-smart-home-platform.json +1 -1
- package/build/src/modeling/templates/verticals/business-services/financial-services-domain.d.ts.map +1 -1
- package/build/src/modeling/templates/verticals/business-services/financial-services-domain.js +248 -63
- package/build/src/modeling/templates/verticals/business-services/financial-services-domain.js.map +1 -1
- package/build/src/modeling/templates/verticals/technology-media/blog-domain.js +5 -5
- package/build/src/modeling/templates/verticals/technology-media/blog-domain.js.map +1 -1
- package/build/src/modeling/templates/verticals/technology-media/iot-smart-home-domain.d.ts.map +1 -1
- package/build/src/modeling/templates/verticals/technology-media/iot-smart-home-domain.js +2 -0
- package/build/src/modeling/templates/verticals/technology-media/iot-smart-home-domain.js.map +1 -1
- package/build/src/modeling/validation/postgresql.d.ts.map +1 -1
- package/build/src/modeling/validation/postgresql.js +0 -1
- package/build/src/modeling/validation/postgresql.js.map +1 -1
- package/build/src/runtime/modeling/Semantics.d.ts +84 -0
- package/build/src/runtime/modeling/Semantics.d.ts.map +1 -0
- package/build/src/runtime/modeling/Semantics.js +124 -0
- package/build/src/runtime/modeling/Semantics.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +8 -8
- package/package.json +1 -1
- package/src/modeling/DomainEntity.ts +27 -5
- package/src/modeling/Semantics.ts +493 -0
- package/src/modeling/definitions/Email.ts +1 -1
- package/src/modeling/definitions/Password.ts +1 -3
- package/src/modeling/helpers/Intelisense.ts +33 -65
- package/src/modeling/templates/meta/blog-publishing-platform.json +1 -1
- package/src/modeling/templates/meta/financial-services-platform.json +1 -1
- package/src/modeling/templates/meta/iot-smart-home-platform.json +1 -1
- package/src/modeling/templates/verticals/business-services/financial-services-domain.ts +285 -65
- package/src/modeling/templates/verticals/technology-media/blog-domain.ts +5 -5
- package/src/modeling/templates/verticals/technology-media/iot-smart-home-domain.ts +2 -0
- package/src/modeling/validation/postgresql.ts +0 -1
- package/src/runtime/modeling/Semantics.ts +196 -0
- package/tests/unit/modeling/client_ip_address_semantic.spec.ts +71 -0
- package/tests/unit/modeling/definitions/password.spec.ts +0 -2
- package/tests/unit/modeling/domain_entity_parents.spec.ts +243 -0
- package/tests/unit/modeling/semantic_runtime.spec.ts +113 -0
- package/tests/unit/modeling/semantics.spec.ts +68 -0
|
@@ -182,6 +182,12 @@ export enum SemanticType {
|
|
|
182
182
|
* Annotates a field as derived from other fields.
|
|
183
183
|
*/
|
|
184
184
|
Derived = 'Semantic#Derived',
|
|
185
|
+
/**
|
|
186
|
+
* Annotates a field that should automatically receive the client's IP address.
|
|
187
|
+
* The runtime automatically populates this field with the request's IP address
|
|
188
|
+
* when creating or updating records, providing audit trail and geolocation capabilities.
|
|
189
|
+
*/
|
|
190
|
+
ClientIPAddress = 'Semantic#ClientIPAddress',
|
|
185
191
|
|
|
186
192
|
//
|
|
187
193
|
// Association-Level Semantics
|
|
@@ -270,6 +276,251 @@ export enum SemanticCategory {
|
|
|
270
276
|
Computed = 'Computed Values',
|
|
271
277
|
}
|
|
272
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Defines when a semantic should execute in relation to database operations.
|
|
281
|
+
*/
|
|
282
|
+
export enum SemanticTiming {
|
|
283
|
+
/**
|
|
284
|
+
* Execute before the database operation (validation, preprocessing)
|
|
285
|
+
*/
|
|
286
|
+
Before = 'Before',
|
|
287
|
+
/**
|
|
288
|
+
* Execute after the database operation (cleanup, notifications, derived values)
|
|
289
|
+
*/
|
|
290
|
+
After = 'After',
|
|
291
|
+
/**
|
|
292
|
+
* Execute both before and after the operation
|
|
293
|
+
*/
|
|
294
|
+
Both = 'Both',
|
|
295
|
+
/**
|
|
296
|
+
* No automatic execution (manual/on-demand only)
|
|
297
|
+
*/
|
|
298
|
+
None = 'None',
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Defines which database operations can trigger a semantic.
|
|
303
|
+
*/
|
|
304
|
+
export enum SemanticOperation {
|
|
305
|
+
Create = 'Create',
|
|
306
|
+
Read = 'Read',
|
|
307
|
+
Update = 'Update',
|
|
308
|
+
Delete = 'Delete',
|
|
309
|
+
/**
|
|
310
|
+
* Special operation for list/query operations
|
|
311
|
+
*/
|
|
312
|
+
List = 'List',
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Defines the execution mode for a semantic.
|
|
317
|
+
*/
|
|
318
|
+
export enum SemanticExecutionMode {
|
|
319
|
+
/**
|
|
320
|
+
* Execute synchronously as part of the main operation
|
|
321
|
+
*/
|
|
322
|
+
Synchronous = 'Synchronous',
|
|
323
|
+
/**
|
|
324
|
+
* Execute asynchronously after the main operation
|
|
325
|
+
*/
|
|
326
|
+
Asynchronous = 'Asynchronous',
|
|
327
|
+
/**
|
|
328
|
+
* Execute in background/queue (fire and forget)
|
|
329
|
+
*/
|
|
330
|
+
Background = 'Background',
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Defines validation strategies for semantics.
|
|
335
|
+
*/
|
|
336
|
+
export enum SemanticValidationStrategy {
|
|
337
|
+
/**
|
|
338
|
+
* Fail the operation if semantic validation fails
|
|
339
|
+
*/
|
|
340
|
+
Strict = 'Strict',
|
|
341
|
+
/**
|
|
342
|
+
* Log warnings but continue operation
|
|
343
|
+
*/
|
|
344
|
+
Warning = 'Warning',
|
|
345
|
+
/**
|
|
346
|
+
* Skip validation entirely
|
|
347
|
+
*/
|
|
348
|
+
Skip = 'Skip',
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Defines caching strategies for computed semantics.
|
|
353
|
+
*/
|
|
354
|
+
export enum SemanticCacheStrategy {
|
|
355
|
+
/**
|
|
356
|
+
* No caching - always compute
|
|
357
|
+
*/
|
|
358
|
+
None = 'None',
|
|
359
|
+
/**
|
|
360
|
+
* Cache until dependent fields change
|
|
361
|
+
*/
|
|
362
|
+
Dependency = 'Dependency',
|
|
363
|
+
/**
|
|
364
|
+
* Cache with TTL expiration
|
|
365
|
+
*/
|
|
366
|
+
TimeToLive = 'TimeToLive',
|
|
367
|
+
/**
|
|
368
|
+
* Cache until manually invalidated
|
|
369
|
+
*/
|
|
370
|
+
Manual = 'Manual',
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Configuration for when and how a semantic should execute at runtime.
|
|
375
|
+
*/
|
|
376
|
+
export interface SemanticRuntimeConfig {
|
|
377
|
+
/**
|
|
378
|
+
* When the semantic should execute relative to the database operation.
|
|
379
|
+
*/
|
|
380
|
+
timing: SemanticTiming
|
|
381
|
+
/**
|
|
382
|
+
* Which database operations should trigger this semantic.
|
|
383
|
+
*/
|
|
384
|
+
operations: SemanticOperation[]
|
|
385
|
+
/**
|
|
386
|
+
* Priority order for execution when multiple semantics apply.
|
|
387
|
+
* Lower numbers execute first. Default is 100.
|
|
388
|
+
*/
|
|
389
|
+
priority?: number
|
|
390
|
+
/**
|
|
391
|
+
* Whether this semantic can be disabled at the entity/property level.
|
|
392
|
+
*/
|
|
393
|
+
canDisable?: boolean
|
|
394
|
+
/**
|
|
395
|
+
* How the semantic should execute (sync/async/background).
|
|
396
|
+
* Default is Synchronous.
|
|
397
|
+
* @default Synchronous
|
|
398
|
+
*/
|
|
399
|
+
executionMode?: SemanticExecutionMode
|
|
400
|
+
/**
|
|
401
|
+
* Validation strategy when semantic processing fails.
|
|
402
|
+
* Default is Strict.
|
|
403
|
+
* @default Strict
|
|
404
|
+
*/
|
|
405
|
+
validationStrategy?: SemanticValidationStrategy
|
|
406
|
+
/**
|
|
407
|
+
* Maximum execution time in milliseconds before timeout.
|
|
408
|
+
* Default is 5000ms for sync, 30000ms for async.
|
|
409
|
+
* @default 5000 for sync, 30000 for async
|
|
410
|
+
*/
|
|
411
|
+
timeoutMs?: number
|
|
412
|
+
/**
|
|
413
|
+
* Number of retry attempts on failure.
|
|
414
|
+
* Default is 0 (no retries).
|
|
415
|
+
* @default 0
|
|
416
|
+
*/
|
|
417
|
+
retryAttempts?: number
|
|
418
|
+
/**
|
|
419
|
+
* Dependencies on other semantics that must execute first.
|
|
420
|
+
* Uses semantic IDs.
|
|
421
|
+
*/
|
|
422
|
+
dependencies?: SemanticType[]
|
|
423
|
+
/**
|
|
424
|
+
* Fields that this semantic depends on for computation.
|
|
425
|
+
* Used for cache invalidation and optimization.
|
|
426
|
+
*/
|
|
427
|
+
dependentFields?: string[]
|
|
428
|
+
/**
|
|
429
|
+
* Caching strategy for computed/derived values.
|
|
430
|
+
*/
|
|
431
|
+
cacheStrategy?: SemanticCacheStrategy
|
|
432
|
+
/**
|
|
433
|
+
* Cache TTL in seconds (only used with TimeToLive strategy).
|
|
434
|
+
*/
|
|
435
|
+
cacheTtlSeconds?: number
|
|
436
|
+
/**
|
|
437
|
+
* Whether this semantic should run in transactions.
|
|
438
|
+
* Default is true.
|
|
439
|
+
*/
|
|
440
|
+
transactional?: boolean
|
|
441
|
+
/**
|
|
442
|
+
* Rate limiting configuration for expensive operations.
|
|
443
|
+
*/
|
|
444
|
+
rateLimit?: SemanticRateLimit
|
|
445
|
+
/**
|
|
446
|
+
* Conditional execution based on data or context.
|
|
447
|
+
*/
|
|
448
|
+
conditions?: SemanticCondition[]
|
|
449
|
+
/**
|
|
450
|
+
* Audit and logging configuration.
|
|
451
|
+
*/
|
|
452
|
+
audit?: SemanticAudit
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export interface SemanticRateLimit {
|
|
456
|
+
/**
|
|
457
|
+
* Maximum executions per time window.
|
|
458
|
+
*/
|
|
459
|
+
maxExecutions: number
|
|
460
|
+
/**
|
|
461
|
+
* Time window in seconds.
|
|
462
|
+
*/
|
|
463
|
+
windowSeconds: number
|
|
464
|
+
/**
|
|
465
|
+
* Strategy when rate limit is exceeded.
|
|
466
|
+
*/
|
|
467
|
+
strategy: 'queue' | 'drop' | 'error'
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export interface SemanticCondition {
|
|
471
|
+
/**
|
|
472
|
+
* JEXL expression that must evaluate to true for execution.
|
|
473
|
+
* Has access to entity data, user context, and semantic field references.
|
|
474
|
+
*
|
|
475
|
+
* Available context variables:
|
|
476
|
+
* - `entity`: The entity data being processed
|
|
477
|
+
* - `user`: Current user context (if authenticated)
|
|
478
|
+
* - `operation`: The current operation (Create, Update, etc.)
|
|
479
|
+
* - `semantics`: Object with semantic field accessors
|
|
480
|
+
*
|
|
481
|
+
* Semantic field references use dot notation:
|
|
482
|
+
* - `semantics.CreatedTimestamp` - field with CreatedTimestamp semantic
|
|
483
|
+
* - `semantics.Password` - field with Password semantic
|
|
484
|
+
* - `semantics.User` - entity with User semantic (for associations)
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* // Only set timestamp if not already provided
|
|
488
|
+
* "semantics.CreatedTimestamp == null"
|
|
489
|
+
*
|
|
490
|
+
* @example
|
|
491
|
+
* // Only apply to authenticated users
|
|
492
|
+
* "user != null && user.authenticated == true"
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* // Only hash password if it's a plain text (not already hashed)
|
|
496
|
+
* "entity[semantics.Password] != null && !entity[semantics.Password].startsWith('$')"
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* // Only generate slug if title exists
|
|
500
|
+
* "entity[semantics.Title] != null && entity[semantics.Title].length > 0"
|
|
501
|
+
*/
|
|
502
|
+
expression: string
|
|
503
|
+
/**
|
|
504
|
+
* Description of when this semantic should run.
|
|
505
|
+
*/
|
|
506
|
+
description: string
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export interface SemanticAudit {
|
|
510
|
+
/**
|
|
511
|
+
* Whether to log semantic execution.
|
|
512
|
+
*/
|
|
513
|
+
logExecution: boolean
|
|
514
|
+
/**
|
|
515
|
+
* Whether to log input/output data.
|
|
516
|
+
*/
|
|
517
|
+
logData: boolean
|
|
518
|
+
/**
|
|
519
|
+
* Retention period for audit logs in days.
|
|
520
|
+
*/
|
|
521
|
+
retentionDays: number
|
|
522
|
+
}
|
|
523
|
+
|
|
273
524
|
/**
|
|
274
525
|
* A base interface for all Data Semantics, containing common properties.
|
|
275
526
|
* A semantic is an annotation applied to a Data Entity, Property, or Association
|
|
@@ -302,6 +553,10 @@ interface BaseDataSemantic {
|
|
|
302
553
|
* This is used to determine if the semantic requires additional setup or configuration.
|
|
303
554
|
*/
|
|
304
555
|
hasConfig: boolean
|
|
556
|
+
/**
|
|
557
|
+
* Runtime execution configuration for this semantic.
|
|
558
|
+
*/
|
|
559
|
+
runtime: SemanticRuntimeConfig
|
|
305
560
|
}
|
|
306
561
|
|
|
307
562
|
/**
|
|
@@ -369,6 +624,10 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
369
624
|
description: 'System users and accounts',
|
|
370
625
|
category: SemanticCategory.Identity,
|
|
371
626
|
hasConfig: false,
|
|
627
|
+
runtime: {
|
|
628
|
+
timing: SemanticTiming.None,
|
|
629
|
+
operations: [],
|
|
630
|
+
},
|
|
372
631
|
},
|
|
373
632
|
[SemanticType.Password]: {
|
|
374
633
|
id: SemanticType.Password,
|
|
@@ -378,6 +637,19 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
378
637
|
category: SemanticCategory.Identity,
|
|
379
638
|
applicableDataTypes: ['string'],
|
|
380
639
|
hasConfig: true,
|
|
640
|
+
runtime: {
|
|
641
|
+
timing: SemanticTiming.Before,
|
|
642
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
643
|
+
priority: 10, // High priority for security
|
|
644
|
+
canDisable: false, // Security semantics cannot be disabled
|
|
645
|
+
timeoutMs: 2000, // Allow time for hashing
|
|
646
|
+
conditions: [
|
|
647
|
+
{
|
|
648
|
+
expression: 'entity[semantics.Password] != null && entity[semantics.Password].length > 0',
|
|
649
|
+
description: 'Only process when password field has a value',
|
|
650
|
+
},
|
|
651
|
+
],
|
|
652
|
+
},
|
|
381
653
|
},
|
|
382
654
|
[SemanticType.UserRole]: {
|
|
383
655
|
id: SemanticType.UserRole,
|
|
@@ -387,6 +659,17 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
387
659
|
category: SemanticCategory.Identity,
|
|
388
660
|
applicableDataTypes: ['string'],
|
|
389
661
|
hasConfig: false,
|
|
662
|
+
runtime: {
|
|
663
|
+
timing: SemanticTiming.Before,
|
|
664
|
+
operations: [
|
|
665
|
+
SemanticOperation.Create,
|
|
666
|
+
SemanticOperation.Read,
|
|
667
|
+
SemanticOperation.Update,
|
|
668
|
+
SemanticOperation.Delete,
|
|
669
|
+
SemanticOperation.List,
|
|
670
|
+
],
|
|
671
|
+
priority: 20,
|
|
672
|
+
},
|
|
390
673
|
},
|
|
391
674
|
[SemanticType.ResourceOwnerIdentifier]: {
|
|
392
675
|
id: SemanticType.ResourceOwnerIdentifier,
|
|
@@ -395,6 +678,18 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
395
678
|
description: 'Links record to owner user',
|
|
396
679
|
category: SemanticCategory.Identity,
|
|
397
680
|
hasConfig: false,
|
|
681
|
+
runtime: {
|
|
682
|
+
timing: SemanticTiming.Before,
|
|
683
|
+
operations: [
|
|
684
|
+
SemanticOperation.Create,
|
|
685
|
+
SemanticOperation.Read,
|
|
686
|
+
SemanticOperation.Update,
|
|
687
|
+
SemanticOperation.Delete,
|
|
688
|
+
SemanticOperation.List,
|
|
689
|
+
],
|
|
690
|
+
priority: 5, // Very high priority for access control
|
|
691
|
+
canDisable: false,
|
|
692
|
+
},
|
|
398
693
|
},
|
|
399
694
|
|
|
400
695
|
//
|
|
@@ -409,6 +704,18 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
409
704
|
category: SemanticCategory.Lifecycle,
|
|
410
705
|
applicableDataTypes: ['datetime'],
|
|
411
706
|
hasConfig: false,
|
|
707
|
+
runtime: {
|
|
708
|
+
timing: SemanticTiming.Before,
|
|
709
|
+
operations: [SemanticOperation.Create],
|
|
710
|
+
priority: 90,
|
|
711
|
+
timeoutMs: 100, // Very fast operation
|
|
712
|
+
conditions: [
|
|
713
|
+
{
|
|
714
|
+
expression: 'entity[semantics.CreatedTimestamp] == null',
|
|
715
|
+
description: 'Only set timestamp if not already provided',
|
|
716
|
+
},
|
|
717
|
+
],
|
|
718
|
+
},
|
|
412
719
|
},
|
|
413
720
|
[SemanticType.UpdatedTimestamp]: {
|
|
414
721
|
id: SemanticType.UpdatedTimestamp,
|
|
@@ -418,6 +725,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
418
725
|
category: SemanticCategory.Lifecycle,
|
|
419
726
|
applicableDataTypes: ['datetime'],
|
|
420
727
|
hasConfig: false,
|
|
728
|
+
runtime: {
|
|
729
|
+
timing: SemanticTiming.Before,
|
|
730
|
+
operations: [SemanticOperation.Update],
|
|
731
|
+
priority: 90,
|
|
732
|
+
},
|
|
421
733
|
},
|
|
422
734
|
[SemanticType.DeletedTimestamp]: {
|
|
423
735
|
id: SemanticType.DeletedTimestamp,
|
|
@@ -427,6 +739,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
427
739
|
category: SemanticCategory.Lifecycle,
|
|
428
740
|
applicableDataTypes: ['datetime'],
|
|
429
741
|
hasConfig: false,
|
|
742
|
+
runtime: {
|
|
743
|
+
timing: SemanticTiming.Before,
|
|
744
|
+
operations: [SemanticOperation.Delete],
|
|
745
|
+
priority: 80,
|
|
746
|
+
},
|
|
430
747
|
},
|
|
431
748
|
[SemanticType.DeletedFlag]: {
|
|
432
749
|
id: SemanticType.DeletedFlag,
|
|
@@ -436,6 +753,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
436
753
|
category: SemanticCategory.Lifecycle,
|
|
437
754
|
applicableDataTypes: ['boolean'],
|
|
438
755
|
hasConfig: false,
|
|
756
|
+
runtime: {
|
|
757
|
+
timing: SemanticTiming.Before,
|
|
758
|
+
operations: [SemanticOperation.Delete, SemanticOperation.Read, SemanticOperation.List],
|
|
759
|
+
priority: 80,
|
|
760
|
+
},
|
|
439
761
|
},
|
|
440
762
|
[SemanticType.Version]: {
|
|
441
763
|
id: SemanticType.Version,
|
|
@@ -445,6 +767,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
445
767
|
category: SemanticCategory.Lifecycle,
|
|
446
768
|
applicableDataTypes: ['number'],
|
|
447
769
|
hasConfig: false,
|
|
770
|
+
runtime: {
|
|
771
|
+
timing: SemanticTiming.Before,
|
|
772
|
+
operations: [SemanticOperation.Update],
|
|
773
|
+
priority: 85,
|
|
774
|
+
},
|
|
448
775
|
},
|
|
449
776
|
|
|
450
777
|
//
|
|
@@ -459,6 +786,10 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
459
786
|
category: SemanticCategory.Content,
|
|
460
787
|
applicableDataTypes: ['string'],
|
|
461
788
|
hasConfig: false,
|
|
789
|
+
runtime: {
|
|
790
|
+
timing: SemanticTiming.None,
|
|
791
|
+
operations: [],
|
|
792
|
+
},
|
|
462
793
|
},
|
|
463
794
|
[SemanticType.Description]: {
|
|
464
795
|
id: SemanticType.Description,
|
|
@@ -468,6 +799,10 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
468
799
|
category: SemanticCategory.Content,
|
|
469
800
|
applicableDataTypes: ['string'],
|
|
470
801
|
hasConfig: false,
|
|
802
|
+
runtime: {
|
|
803
|
+
timing: SemanticTiming.None,
|
|
804
|
+
operations: [],
|
|
805
|
+
},
|
|
471
806
|
},
|
|
472
807
|
[SemanticType.Summary]: {
|
|
473
808
|
id: SemanticType.Summary,
|
|
@@ -477,6 +812,10 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
477
812
|
category: SemanticCategory.Content,
|
|
478
813
|
applicableDataTypes: ['string'],
|
|
479
814
|
hasConfig: false,
|
|
815
|
+
runtime: {
|
|
816
|
+
timing: SemanticTiming.None,
|
|
817
|
+
operations: [],
|
|
818
|
+
},
|
|
480
819
|
},
|
|
481
820
|
[SemanticType.Markdown]: {
|
|
482
821
|
id: SemanticType.Markdown,
|
|
@@ -486,6 +825,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
486
825
|
category: SemanticCategory.Content,
|
|
487
826
|
applicableDataTypes: ['string'],
|
|
488
827
|
hasConfig: true,
|
|
828
|
+
runtime: {
|
|
829
|
+
timing: SemanticTiming.Before,
|
|
830
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
831
|
+
priority: 50, // Process before storage
|
|
832
|
+
},
|
|
489
833
|
},
|
|
490
834
|
[SemanticType.HTML]: {
|
|
491
835
|
id: SemanticType.HTML,
|
|
@@ -495,6 +839,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
495
839
|
category: SemanticCategory.Content,
|
|
496
840
|
applicableDataTypes: ['string'],
|
|
497
841
|
hasConfig: true,
|
|
842
|
+
runtime: {
|
|
843
|
+
timing: SemanticTiming.Before,
|
|
844
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
845
|
+
priority: 50, // Process before storage for sanitization
|
|
846
|
+
},
|
|
498
847
|
},
|
|
499
848
|
[SemanticType.ImageURL]: {
|
|
500
849
|
id: SemanticType.ImageURL,
|
|
@@ -504,6 +853,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
504
853
|
category: SemanticCategory.Content,
|
|
505
854
|
applicableDataTypes: ['string'],
|
|
506
855
|
hasConfig: false,
|
|
856
|
+
runtime: {
|
|
857
|
+
timing: SemanticTiming.Before,
|
|
858
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
859
|
+
priority: 60, // Validate URLs before storage
|
|
860
|
+
},
|
|
507
861
|
},
|
|
508
862
|
[SemanticType.FileURL]: {
|
|
509
863
|
id: SemanticType.FileURL,
|
|
@@ -513,6 +867,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
513
867
|
category: SemanticCategory.Content,
|
|
514
868
|
applicableDataTypes: ['string'],
|
|
515
869
|
hasConfig: false,
|
|
870
|
+
runtime: {
|
|
871
|
+
timing: SemanticTiming.Before,
|
|
872
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
873
|
+
priority: 60, // Validate URLs before storage
|
|
874
|
+
},
|
|
516
875
|
},
|
|
517
876
|
|
|
518
877
|
//
|
|
@@ -527,6 +886,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
527
886
|
category: SemanticCategory.Business,
|
|
528
887
|
applicableDataTypes: ['string'],
|
|
529
888
|
hasConfig: true,
|
|
889
|
+
runtime: {
|
|
890
|
+
timing: SemanticTiming.Both,
|
|
891
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Read, SemanticOperation.List],
|
|
892
|
+
priority: 30, // Validate state transitions before, filter by state after
|
|
893
|
+
},
|
|
530
894
|
},
|
|
531
895
|
[SemanticType.Currency]: {
|
|
532
896
|
id: SemanticType.Currency,
|
|
@@ -536,6 +900,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
536
900
|
category: SemanticCategory.Business,
|
|
537
901
|
applicableDataTypes: ['number', 'string'],
|
|
538
902
|
hasConfig: true,
|
|
903
|
+
runtime: {
|
|
904
|
+
timing: SemanticTiming.Before,
|
|
905
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
906
|
+
priority: 70, // Validate currency format and precision
|
|
907
|
+
},
|
|
539
908
|
},
|
|
540
909
|
[SemanticType.SKU]: {
|
|
541
910
|
id: SemanticType.SKU,
|
|
@@ -545,6 +914,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
545
914
|
category: SemanticCategory.Business,
|
|
546
915
|
applicableDataTypes: ['string'],
|
|
547
916
|
hasConfig: true,
|
|
917
|
+
runtime: {
|
|
918
|
+
timing: SemanticTiming.Before,
|
|
919
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
920
|
+
priority: 25, // High priority for uniqueness validation
|
|
921
|
+
},
|
|
548
922
|
},
|
|
549
923
|
|
|
550
924
|
//
|
|
@@ -559,6 +933,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
559
933
|
category: SemanticCategory.Contact,
|
|
560
934
|
applicableDataTypes: ['string'],
|
|
561
935
|
hasConfig: true,
|
|
936
|
+
runtime: {
|
|
937
|
+
timing: SemanticTiming.Before,
|
|
938
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
939
|
+
priority: 40, // Validate email format
|
|
940
|
+
},
|
|
562
941
|
},
|
|
563
942
|
[SemanticType.Phone]: {
|
|
564
943
|
id: SemanticType.Phone,
|
|
@@ -568,6 +947,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
568
947
|
category: SemanticCategory.Contact,
|
|
569
948
|
applicableDataTypes: ['string'],
|
|
570
949
|
hasConfig: true,
|
|
950
|
+
runtime: {
|
|
951
|
+
timing: SemanticTiming.Before,
|
|
952
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
953
|
+
priority: 40, // Validate phone format
|
|
954
|
+
},
|
|
571
955
|
},
|
|
572
956
|
[SemanticType.URL]: {
|
|
573
957
|
id: SemanticType.URL,
|
|
@@ -577,6 +961,32 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
577
961
|
category: SemanticCategory.Contact,
|
|
578
962
|
applicableDataTypes: ['string'],
|
|
579
963
|
hasConfig: true,
|
|
964
|
+
runtime: {
|
|
965
|
+
timing: SemanticTiming.Before,
|
|
966
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
967
|
+
priority: 40, // Validate URL format
|
|
968
|
+
},
|
|
969
|
+
},
|
|
970
|
+
[SemanticType.ClientIPAddress]: {
|
|
971
|
+
id: SemanticType.ClientIPAddress,
|
|
972
|
+
displayName: 'Client IP Address',
|
|
973
|
+
scope: SemanticScope.Property,
|
|
974
|
+
description: 'Automatically populated client IP address',
|
|
975
|
+
category: SemanticCategory.Contact,
|
|
976
|
+
applicableDataTypes: ['string'],
|
|
977
|
+
hasConfig: false,
|
|
978
|
+
runtime: {
|
|
979
|
+
timing: SemanticTiming.Before,
|
|
980
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
981
|
+
priority: 95, // Low priority, populate after other validations
|
|
982
|
+
timeoutMs: 100, // Very fast operation
|
|
983
|
+
conditions: [
|
|
984
|
+
{
|
|
985
|
+
expression: 'entity[semantics.ClientIPAddress] == null',
|
|
986
|
+
description: 'Only set IP address if not already provided',
|
|
987
|
+
},
|
|
988
|
+
],
|
|
989
|
+
},
|
|
580
990
|
},
|
|
581
991
|
|
|
582
992
|
//
|
|
@@ -591,6 +1001,23 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
591
1001
|
category: SemanticCategory.Organization,
|
|
592
1002
|
applicableDataTypes: ['string'],
|
|
593
1003
|
hasConfig: true,
|
|
1004
|
+
runtime: {
|
|
1005
|
+
timing: SemanticTiming.Before,
|
|
1006
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
1007
|
+
priority: 30, // Generate slug from title, validate uniqueness
|
|
1008
|
+
timeoutMs: 1000,
|
|
1009
|
+
conditions: [
|
|
1010
|
+
{
|
|
1011
|
+
expression: 'entity[semantics.PublicUniqueName] == null || entity[semantics.PublicUniqueName].length == 0',
|
|
1012
|
+
description: 'Only generate slug if not already provided',
|
|
1013
|
+
},
|
|
1014
|
+
// Let's not guess which field is marked as slug source in the `PublicUniqueName` configuration.
|
|
1015
|
+
// {
|
|
1016
|
+
// expression: 'entity[semantics.Title] != null && entity[semantics.Title].length > 0',
|
|
1017
|
+
// description: 'Only generate slug if title field exists and has content',
|
|
1018
|
+
// },
|
|
1019
|
+
],
|
|
1020
|
+
},
|
|
594
1021
|
},
|
|
595
1022
|
[SemanticType.Tags]: {
|
|
596
1023
|
id: SemanticType.Tags,
|
|
@@ -599,6 +1026,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
599
1026
|
description: 'Enable tagging functionality',
|
|
600
1027
|
category: SemanticCategory.Organization,
|
|
601
1028
|
hasConfig: true,
|
|
1029
|
+
runtime: {
|
|
1030
|
+
timing: SemanticTiming.After,
|
|
1031
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Delete],
|
|
1032
|
+
priority: 200, // Process after main operation
|
|
1033
|
+
},
|
|
602
1034
|
},
|
|
603
1035
|
[SemanticType.Categories]: {
|
|
604
1036
|
id: SemanticType.Categories,
|
|
@@ -607,6 +1039,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
607
1039
|
description: 'Enable categorization functionality',
|
|
608
1040
|
category: SemanticCategory.Organization,
|
|
609
1041
|
hasConfig: true,
|
|
1042
|
+
runtime: {
|
|
1043
|
+
timing: SemanticTiming.After,
|
|
1044
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Delete],
|
|
1045
|
+
priority: 200, // Process after main operation
|
|
1046
|
+
},
|
|
610
1047
|
},
|
|
611
1048
|
|
|
612
1049
|
//
|
|
@@ -621,6 +1058,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
621
1058
|
category: SemanticCategory.Location,
|
|
622
1059
|
applicableDataTypes: ['string'],
|
|
623
1060
|
hasConfig: true,
|
|
1061
|
+
runtime: {
|
|
1062
|
+
timing: SemanticTiming.Both,
|
|
1063
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Read, SemanticOperation.List],
|
|
1064
|
+
priority: 60, // Validate coordinates before, enable spatial queries after
|
|
1065
|
+
},
|
|
624
1066
|
},
|
|
625
1067
|
|
|
626
1068
|
//
|
|
@@ -636,6 +1078,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
636
1078
|
category: SemanticCategory.Computed,
|
|
637
1079
|
applicableDataTypes: ['string', 'number', 'boolean', 'date', 'datetime', 'time', 'binary'],
|
|
638
1080
|
hasConfig: true,
|
|
1081
|
+
runtime: {
|
|
1082
|
+
timing: SemanticTiming.Both,
|
|
1083
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Read],
|
|
1084
|
+
priority: 150, // Calculate after other validations, recalculate on read if needed
|
|
1085
|
+
},
|
|
639
1086
|
},
|
|
640
1087
|
[SemanticType.Derived]: {
|
|
641
1088
|
id: SemanticType.Derived,
|
|
@@ -645,9 +1092,55 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
645
1092
|
category: SemanticCategory.Computed,
|
|
646
1093
|
applicableDataTypes: ['string'],
|
|
647
1094
|
hasConfig: true,
|
|
1095
|
+
runtime: {
|
|
1096
|
+
timing: SemanticTiming.After,
|
|
1097
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
1098
|
+
priority: 180, // Derive after all other processing
|
|
1099
|
+
},
|
|
648
1100
|
},
|
|
649
1101
|
}
|
|
650
1102
|
|
|
1103
|
+
/**
|
|
1104
|
+
* Get all semantics that should run for a specific operation and timing
|
|
1105
|
+
*/
|
|
1106
|
+
export function getSemanticsForOperation(
|
|
1107
|
+
semantics: AppliedDataSemantic[],
|
|
1108
|
+
operation: SemanticOperation,
|
|
1109
|
+
timing: SemanticTiming
|
|
1110
|
+
): AppliedDataSemantic[] {
|
|
1111
|
+
return semantics
|
|
1112
|
+
.filter((semantic) => {
|
|
1113
|
+
const definition = DataSemantics[semantic.id]
|
|
1114
|
+
if (!definition?.runtime) {
|
|
1115
|
+
return false
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
const { timing: semanticTiming, operations } = definition.runtime
|
|
1119
|
+
|
|
1120
|
+
// Check if timing matches
|
|
1121
|
+
const timingMatches = semanticTiming === timing || semanticTiming === SemanticTiming.Both
|
|
1122
|
+
|
|
1123
|
+
// Check if operation is included
|
|
1124
|
+
const operationMatches = operations.includes(operation)
|
|
1125
|
+
|
|
1126
|
+
return timingMatches && operationMatches
|
|
1127
|
+
})
|
|
1128
|
+
.sort((a, b) => {
|
|
1129
|
+
// Sort by priority (lower number = higher priority)
|
|
1130
|
+
const priorityA = DataSemantics[a.id]?.runtime?.priority ?? 100
|
|
1131
|
+
const priorityB = DataSemantics[b.id]?.runtime?.priority ?? 100
|
|
1132
|
+
return priorityA - priorityB
|
|
1133
|
+
})
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Check if a specific semantic can be disabled at runtime
|
|
1138
|
+
*/
|
|
1139
|
+
export function canSemanticBeDisabled(semanticType: SemanticType): boolean {
|
|
1140
|
+
const definition = DataSemantics[semanticType]
|
|
1141
|
+
return definition?.runtime?.canDisable !== false // Default to true if not specified
|
|
1142
|
+
}
|
|
1143
|
+
|
|
651
1144
|
export function getSemanticsByCategory(): Record<SemanticCategory, DataSemantic[]>
|
|
652
1145
|
export function getSemanticsByCategory(scope: SemanticScope.Entity): Record<SemanticCategory, EntitySemantic[]>
|
|
653
1146
|
export function getSemanticsByCategory(scope: SemanticScope.Property): Record<SemanticCategory, PropertySemantic[]>
|
|
@@ -79,7 +79,7 @@ export const createEmailSemantic = (config: EmailConfig = {}): AppliedEmailSeman
|
|
|
79
79
|
* Default configuration for Email semantic.
|
|
80
80
|
*/
|
|
81
81
|
export const DEFAULT_EMAIL_CONFIG: EmailConfig = {
|
|
82
|
-
requireVerification:
|
|
82
|
+
requireVerification: true,
|
|
83
83
|
allowSubaddressing: true,
|
|
84
84
|
allowInternational: true,
|
|
85
85
|
}
|