@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.
Files changed (59) hide show
  1. package/build/src/modeling/DomainEntity.d.ts +6 -1
  2. package/build/src/modeling/DomainEntity.d.ts.map +1 -1
  3. package/build/src/modeling/DomainEntity.js +24 -5
  4. package/build/src/modeling/DomainEntity.js.map +1 -1
  5. package/build/src/modeling/Semantics.d.ts +254 -0
  6. package/build/src/modeling/Semantics.d.ts.map +1 -1
  7. package/build/src/modeling/Semantics.js +328 -0
  8. package/build/src/modeling/Semantics.js.map +1 -1
  9. package/build/src/modeling/definitions/Email.js +1 -1
  10. package/build/src/modeling/definitions/Email.js.map +1 -1
  11. package/build/src/modeling/definitions/Password.d.ts.map +1 -1
  12. package/build/src/modeling/definitions/Password.js +1 -3
  13. package/build/src/modeling/definitions/Password.js.map +1 -1
  14. package/build/src/modeling/helpers/Intelisense.d.ts +7 -7
  15. package/build/src/modeling/helpers/Intelisense.d.ts.map +1 -1
  16. package/build/src/modeling/helpers/Intelisense.js +24 -58
  17. package/build/src/modeling/helpers/Intelisense.js.map +1 -1
  18. package/build/src/modeling/templates/meta/blog-publishing-platform.json +1 -1
  19. package/build/src/modeling/templates/meta/financial-services-platform.json +1 -1
  20. package/build/src/modeling/templates/meta/index.d.ts +1 -1
  21. package/build/src/modeling/templates/meta/index.js +1 -1
  22. package/build/src/modeling/templates/meta/index.js.map +1 -1
  23. package/build/src/modeling/templates/meta/iot-smart-home-platform.json +1 -1
  24. package/build/src/modeling/templates/verticals/business-services/financial-services-domain.d.ts.map +1 -1
  25. package/build/src/modeling/templates/verticals/business-services/financial-services-domain.js +248 -63
  26. package/build/src/modeling/templates/verticals/business-services/financial-services-domain.js.map +1 -1
  27. package/build/src/modeling/templates/verticals/technology-media/blog-domain.js +5 -5
  28. package/build/src/modeling/templates/verticals/technology-media/blog-domain.js.map +1 -1
  29. package/build/src/modeling/templates/verticals/technology-media/iot-smart-home-domain.d.ts.map +1 -1
  30. package/build/src/modeling/templates/verticals/technology-media/iot-smart-home-domain.js +2 -0
  31. package/build/src/modeling/templates/verticals/technology-media/iot-smart-home-domain.js.map +1 -1
  32. package/build/src/modeling/validation/postgresql.d.ts.map +1 -1
  33. package/build/src/modeling/validation/postgresql.js +0 -1
  34. package/build/src/modeling/validation/postgresql.js.map +1 -1
  35. package/build/src/runtime/modeling/Semantics.d.ts +84 -0
  36. package/build/src/runtime/modeling/Semantics.d.ts.map +1 -0
  37. package/build/src/runtime/modeling/Semantics.js +124 -0
  38. package/build/src/runtime/modeling/Semantics.js.map +1 -0
  39. package/build/tsconfig.tsbuildinfo +1 -1
  40. package/data/models/example-generator-api.json +8 -8
  41. package/package.json +1 -1
  42. package/src/modeling/DomainEntity.ts +27 -5
  43. package/src/modeling/Semantics.ts +493 -0
  44. package/src/modeling/definitions/Email.ts +1 -1
  45. package/src/modeling/definitions/Password.ts +1 -3
  46. package/src/modeling/helpers/Intelisense.ts +33 -65
  47. package/src/modeling/templates/meta/blog-publishing-platform.json +1 -1
  48. package/src/modeling/templates/meta/financial-services-platform.json +1 -1
  49. package/src/modeling/templates/meta/iot-smart-home-platform.json +1 -1
  50. package/src/modeling/templates/verticals/business-services/financial-services-domain.ts +285 -65
  51. package/src/modeling/templates/verticals/technology-media/blog-domain.ts +5 -5
  52. package/src/modeling/templates/verticals/technology-media/iot-smart-home-domain.ts +2 -0
  53. package/src/modeling/validation/postgresql.ts +0 -1
  54. package/src/runtime/modeling/Semantics.ts +196 -0
  55. package/tests/unit/modeling/client_ip_address_semantic.spec.ts +71 -0
  56. package/tests/unit/modeling/definitions/password.spec.ts +0 -2
  57. package/tests/unit/modeling/domain_entity_parents.spec.ts +243 -0
  58. package/tests/unit/modeling/semantic_runtime.spec.ts +113 -0
  59. 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: false,
82
+ requireVerification: true,
83
83
  allowSubaddressing: true,
84
84
  allowInternational: true,
85
85
  }