@api-client/core 0.18.24 → 0.18.25

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 (28) 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 +68 -0
  6. package/build/src/modeling/Semantics.d.ts.map +1 -1
  7. package/build/src/modeling/Semantics.js +217 -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.map +1 -1
  15. package/build/src/modeling/helpers/Intelisense.js +24 -58
  16. package/build/src/modeling/helpers/Intelisense.js.map +1 -1
  17. package/build/tsconfig.tsbuildinfo +1 -1
  18. package/data/models/example-generator-api.json +15 -15
  19. package/package.json +1 -1
  20. package/src/modeling/DomainEntity.ts +27 -5
  21. package/src/modeling/Semantics.ts +254 -0
  22. package/src/modeling/definitions/Email.ts +1 -1
  23. package/src/modeling/definitions/Password.ts +1 -3
  24. package/src/modeling/helpers/Intelisense.ts +26 -58
  25. package/tests/unit/modeling/definitions/password.spec.ts +0 -2
  26. package/tests/unit/modeling/domain_entity_parents.spec.ts +243 -0
  27. package/tests/unit/modeling/semantic_runtime.spec.ts +113 -0
  28. package/tests/unit/modeling/semantics.spec.ts +68 -0
@@ -42062,6 +42062,9 @@
42062
42062
  "@id": "#209"
42063
42063
  },
42064
42064
  {
42065
+ "@id": "#206"
42066
+ },
42067
+ {
42065
42068
  "@id": "#191"
42066
42069
  },
42067
42070
  {
@@ -42077,9 +42080,6 @@
42077
42080
  "@id": "#200"
42078
42081
  },
42079
42082
  {
42080
- "@id": "#206"
42081
- },
42082
- {
42083
42083
  "@id": "#209"
42084
42084
  }
42085
42085
  ],
@@ -43436,7 +43436,7 @@
43436
43436
  "doc:ExternalDomainElement",
43437
43437
  "doc:DomainElement"
43438
43438
  ],
43439
- "doc:raw": "countryCode: \"BE\"\ngraydonEnterpriseId: 1057155523\nregistrationId: \"0422319093\"\nvatNumber: \"BE0422319093\"\ngraydonCompanyId: \"0422319093\"\nisBranchOffice: false\n",
43439
+ "doc:raw": "addressType: 'REGISTERED-OFFICE-ADDRESS'\nstreetName: 'UITBREIDINGSTRAAT'\nhouseNumber: '84'\nhouseNumberAddition: '/1'\npostalCode: '2600'\ncity: 'BERCHEM (ANTWERPEN)'\ncountry: 'Belgium'\ncountryCode: 'BE'\nfullFormatedAddress: \"UITBREIDINGSTRAAT 84 /1, 2600 BERCHEM (ANTWERPEN), BELIUM\"\n",
43440
43440
  "core:mediaType": "application/yaml",
43441
43441
  "sourcemaps:sources": [
43442
43442
  {
@@ -43457,7 +43457,7 @@
43457
43457
  "doc:ExternalDomainElement",
43458
43458
  "doc:DomainElement"
43459
43459
  ],
43460
- "doc:raw": "addressType: 'REGISTERED-OFFICE-ADDRESS'\nstreetName: 'UITBREIDINGSTRAAT'\nhouseNumber: '84'\nhouseNumberAddition: '/1'\npostalCode: '2600'\ncity: 'BERCHEM (ANTWERPEN)'\ncountry: 'Belgium'\ncountryCode: 'BE'\nfullFormatedAddress: \"UITBREIDINGSTRAAT 84 /1, 2600 BERCHEM (ANTWERPEN), BELIUM\"\n",
43460
+ "doc:raw": "code: '5'\ndescription: 'Limited company'\n",
43461
43461
  "core:mediaType": "application/yaml",
43462
43462
  "sourcemaps:sources": [
43463
43463
  {
@@ -43478,7 +43478,7 @@
43478
43478
  "doc:ExternalDomainElement",
43479
43479
  "doc:DomainElement"
43480
43480
  ],
43481
- "doc:raw": "code: '5'\ndescription: 'Limited company'\n",
43481
+ "doc:raw": "class: '3'\ndescription: '150 - 300'\nnumberOfFte: 5500\nnumberOfEmployees: 5232\n",
43482
43482
  "core:mediaType": "application/yaml",
43483
43483
  "sourcemaps:sources": [
43484
43484
  {
@@ -43499,7 +43499,7 @@
43499
43499
  "doc:ExternalDomainElement",
43500
43500
  "doc:DomainElement"
43501
43501
  ],
43502
- "doc:raw": "code: 'J'\ndescription: 'Information and communication'\n",
43502
+ "doc:raw": "code: '7487'\ndescription: 'Financial and insurance activities'\ntype: \"PRIMARY\"\nclassificationCode: 'BE_NACEBEL2008'\nactivityGroupCode: 'ABCDE'\n",
43503
43503
  "core:mediaType": "application/yaml",
43504
43504
  "sourcemaps:sources": [
43505
43505
  {
@@ -43520,7 +43520,7 @@
43520
43520
  "doc:ExternalDomainElement",
43521
43521
  "doc:DomainElement"
43522
43522
  ],
43523
- "doc:raw": "class: '3'\ndescription: '150 - 300'\nnumberOfFte: 5500\nnumberOfEmployees: 5232\n",
43523
+ "doc:raw": "code: 'J'\ndescription: 'Information and communication'\n",
43524
43524
  "core:mediaType": "application/yaml",
43525
43525
  "sourcemaps:sources": [
43526
43526
  {
@@ -43541,7 +43541,7 @@
43541
43541
  "doc:ExternalDomainElement",
43542
43542
  "doc:DomainElement"
43543
43543
  ],
43544
- "doc:raw": "code: '7487'\ndescription: 'Financial and insurance activities'\ntype: \"PRIMARY\"\nclassificationCode: 'BE_NACEBEL2008'\nactivityGroupCode: 'ABCDE'\n",
43544
+ "doc:raw": "countryCode: \"BE\"\ngraydonEnterpriseId: 1057155523\nregistrationId: \"0422319093\"\nvatNumber: \"BE0422319093\"\ngraydonCompanyId: \"0422319093\"\nisBranchOffice: false\n",
43545
43545
  "core:mediaType": "application/yaml",
43546
43546
  "sourcemaps:sources": [
43547
43547
  {
@@ -44756,32 +44756,32 @@
44756
44756
  {
44757
44757
  "@id": "#193/source-map/lexical/element_0",
44758
44758
  "sourcemaps:element": "amf://id#193",
44759
- "sourcemaps:value": "[(1,0)-(7,0)]"
44759
+ "sourcemaps:value": "[(1,0)-(10,0)]"
44760
44760
  },
44761
44761
  {
44762
44762
  "@id": "#196/source-map/lexical/element_0",
44763
44763
  "sourcemaps:element": "amf://id#196",
44764
- "sourcemaps:value": "[(1,0)-(10,0)]"
44764
+ "sourcemaps:value": "[(1,0)-(3,0)]"
44765
44765
  },
44766
44766
  {
44767
44767
  "@id": "#199/source-map/lexical/element_0",
44768
44768
  "sourcemaps:element": "amf://id#199",
44769
- "sourcemaps:value": "[(1,0)-(3,0)]"
44769
+ "sourcemaps:value": "[(1,0)-(5,0)]"
44770
44770
  },
44771
44771
  {
44772
44772
  "@id": "#202/source-map/lexical/element_0",
44773
44773
  "sourcemaps:element": "amf://id#202",
44774
- "sourcemaps:value": "[(1,0)-(3,0)]"
44774
+ "sourcemaps:value": "[(1,0)-(6,0)]"
44775
44775
  },
44776
44776
  {
44777
44777
  "@id": "#205/source-map/lexical/element_0",
44778
44778
  "sourcemaps:element": "amf://id#205",
44779
- "sourcemaps:value": "[(1,0)-(5,0)]"
44779
+ "sourcemaps:value": "[(1,0)-(3,0)]"
44780
44780
  },
44781
44781
  {
44782
44782
  "@id": "#208/source-map/lexical/element_0",
44783
44783
  "sourcemaps:element": "amf://id#208",
44784
- "sourcemaps:value": "[(1,0)-(6,0)]"
44784
+ "sourcemaps:value": "[(1,0)-(7,0)]"
44785
44785
  },
44786
44786
  {
44787
44787
  "@id": "#223/source-map/lexical/element_0",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@api-client/core",
3
3
  "description": "The API Client's core client library. Works in NodeJS and in a ES enabled browser.",
4
- "version": "0.18.24",
4
+ "version": "0.18.25",
5
5
  "license": "Apache-2.0",
6
6
  "exports": {
7
7
  "./browser.js": {
@@ -412,7 +412,7 @@ export class DomainEntity extends DomainElement {
412
412
  * ```
413
413
  */
414
414
  addParent(key: string, domain?: string): this {
415
- const effectiveKey = domain ? `${domain}:${key}` : key
415
+ const effectiveKey = this.domain.buildReferenceKey(key, domain)
416
416
  // Prevent adding self as parent
417
417
  if (effectiveKey === this.key) {
418
418
  const message = 'Entity cannot be a parent of itself'
@@ -486,19 +486,41 @@ export class DomainEntity extends DomainElement {
486
486
  * Removes a parent from this entity.
487
487
  *
488
488
  * @param key The key of the parent entity to remove.
489
+ * @param domain Optional domain key if the parent belongs to a foreign domain.
489
490
  * @returns `this` for chaining.
490
491
  * @throws Error When the parent does not exist.
491
492
  * @example
492
493
  * ```typescript
494
+ * // Remove local parent
493
495
  * entity.removeParent('baseEntity');
496
+ *
497
+ * // Remove foreign domain parent
498
+ * entity.removeParent('baseEntity', 'externalDomain');
494
499
  * ```
495
500
  */
496
- removeParent(key: string): this {
501
+ removeParent(key: string, domain?: string): this {
497
502
  const { graph } = this.root
498
- if (!graph.hasEdge(this.key, key)) {
499
- throw new Error(`Trying to remove a parent ${key} from ${this.key}, but it doesn't exist`)
503
+ const effectiveKey = this.domain.buildReferenceKey(key, domain)
504
+
505
+ if (!graph.hasEdge(this.key, effectiveKey)) {
506
+ let message = `Trying to remove a parent ${key} from ${this.key}, but it doesn't exist`
507
+ if (domain) {
508
+ message = `Trying to remove a foreign parent ${key} from domain "${domain}" from ${this.key}, but it doesn't exist`
509
+ }
510
+ throw new Error(message)
500
511
  }
501
- graph.removeEdge(this.key, key)
512
+
513
+ // Verify it's actually a parent edge
514
+ const edge = graph.edge(this.key, effectiveKey)
515
+ if (!edge || edge.type !== 'parent') {
516
+ let message = `Edge between ${this.key} and ${key} exists but is not a parent relationship`
517
+ if (domain) {
518
+ message = `Edge between ${this.key} and ${key} in domain "${domain}" exists but is not a parent relationship`
519
+ }
520
+ throw new Error(message)
521
+ }
522
+
523
+ graph.removeEdge(this.key, effectiveKey)
502
524
  this.root.notifyChange()
503
525
  return this
504
526
  }
@@ -270,6 +270,65 @@ export enum SemanticCategory {
270
270
  Computed = 'Computed Values',
271
271
  }
272
272
 
273
+ /**
274
+ * Defines when a semantic should execute in relation to database operations.
275
+ */
276
+ export enum SemanticTiming {
277
+ /**
278
+ * Execute before the database operation (validation, preprocessing)
279
+ */
280
+ Before = 'Before',
281
+ /**
282
+ * Execute after the database operation (cleanup, notifications, derived values)
283
+ */
284
+ After = 'After',
285
+ /**
286
+ * Execute both before and after the operation
287
+ */
288
+ Both = 'Both',
289
+ /**
290
+ * No automatic execution (manual/on-demand only)
291
+ */
292
+ None = 'None',
293
+ }
294
+
295
+ /**
296
+ * Defines which database operations can trigger a semantic.
297
+ */
298
+ export enum SemanticOperation {
299
+ Create = 'Create',
300
+ Read = 'Read',
301
+ Update = 'Update',
302
+ Delete = 'Delete',
303
+ /**
304
+ * Special operation for list/query operations
305
+ */
306
+ List = 'List',
307
+ }
308
+
309
+ /**
310
+ * Configuration for when and how a semantic should execute at runtime.
311
+ */
312
+ export interface SemanticRuntimeConfig {
313
+ /**
314
+ * When the semantic should execute relative to the database operation.
315
+ */
316
+ timing: SemanticTiming
317
+ /**
318
+ * Which database operations should trigger this semantic.
319
+ */
320
+ operations: SemanticOperation[]
321
+ /**
322
+ * Priority order for execution when multiple semantics apply.
323
+ * Lower numbers execute first. Default is 100.
324
+ */
325
+ priority?: number
326
+ /**
327
+ * Whether this semantic can be disabled at the entity/property level.
328
+ */
329
+ canDisable?: boolean
330
+ }
331
+
273
332
  /**
274
333
  * A base interface for all Data Semantics, containing common properties.
275
334
  * A semantic is an annotation applied to a Data Entity, Property, or Association
@@ -302,6 +361,10 @@ interface BaseDataSemantic {
302
361
  * This is used to determine if the semantic requires additional setup or configuration.
303
362
  */
304
363
  hasConfig: boolean
364
+ /**
365
+ * Runtime execution configuration for this semantic.
366
+ */
367
+ runtime: SemanticRuntimeConfig
305
368
  }
306
369
 
307
370
  /**
@@ -369,6 +432,10 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
369
432
  description: 'System users and accounts',
370
433
  category: SemanticCategory.Identity,
371
434
  hasConfig: false,
435
+ runtime: {
436
+ timing: SemanticTiming.None,
437
+ operations: [],
438
+ },
372
439
  },
373
440
  [SemanticType.Password]: {
374
441
  id: SemanticType.Password,
@@ -378,6 +445,12 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
378
445
  category: SemanticCategory.Identity,
379
446
  applicableDataTypes: ['string'],
380
447
  hasConfig: true,
448
+ runtime: {
449
+ timing: SemanticTiming.Before,
450
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
451
+ priority: 10, // High priority for security
452
+ canDisable: false, // Security semantics cannot be disabled
453
+ },
381
454
  },
382
455
  [SemanticType.UserRole]: {
383
456
  id: SemanticType.UserRole,
@@ -387,6 +460,17 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
387
460
  category: SemanticCategory.Identity,
388
461
  applicableDataTypes: ['string'],
389
462
  hasConfig: false,
463
+ runtime: {
464
+ timing: SemanticTiming.Before,
465
+ operations: [
466
+ SemanticOperation.Create,
467
+ SemanticOperation.Read,
468
+ SemanticOperation.Update,
469
+ SemanticOperation.Delete,
470
+ SemanticOperation.List,
471
+ ],
472
+ priority: 20,
473
+ },
390
474
  },
391
475
  [SemanticType.ResourceOwnerIdentifier]: {
392
476
  id: SemanticType.ResourceOwnerIdentifier,
@@ -395,6 +479,18 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
395
479
  description: 'Links record to owner user',
396
480
  category: SemanticCategory.Identity,
397
481
  hasConfig: false,
482
+ runtime: {
483
+ timing: SemanticTiming.Before,
484
+ operations: [
485
+ SemanticOperation.Create,
486
+ SemanticOperation.Read,
487
+ SemanticOperation.Update,
488
+ SemanticOperation.Delete,
489
+ SemanticOperation.List,
490
+ ],
491
+ priority: 5, // Very high priority for access control
492
+ canDisable: false,
493
+ },
398
494
  },
399
495
 
400
496
  //
@@ -409,6 +505,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
409
505
  category: SemanticCategory.Lifecycle,
410
506
  applicableDataTypes: ['datetime'],
411
507
  hasConfig: false,
508
+ runtime: {
509
+ timing: SemanticTiming.Before,
510
+ operations: [SemanticOperation.Create],
511
+ priority: 90,
512
+ },
412
513
  },
413
514
  [SemanticType.UpdatedTimestamp]: {
414
515
  id: SemanticType.UpdatedTimestamp,
@@ -418,6 +519,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
418
519
  category: SemanticCategory.Lifecycle,
419
520
  applicableDataTypes: ['datetime'],
420
521
  hasConfig: false,
522
+ runtime: {
523
+ timing: SemanticTiming.Before,
524
+ operations: [SemanticOperation.Update],
525
+ priority: 90,
526
+ },
421
527
  },
422
528
  [SemanticType.DeletedTimestamp]: {
423
529
  id: SemanticType.DeletedTimestamp,
@@ -427,6 +533,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
427
533
  category: SemanticCategory.Lifecycle,
428
534
  applicableDataTypes: ['datetime'],
429
535
  hasConfig: false,
536
+ runtime: {
537
+ timing: SemanticTiming.Before,
538
+ operations: [SemanticOperation.Delete],
539
+ priority: 80,
540
+ },
430
541
  },
431
542
  [SemanticType.DeletedFlag]: {
432
543
  id: SemanticType.DeletedFlag,
@@ -436,6 +547,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
436
547
  category: SemanticCategory.Lifecycle,
437
548
  applicableDataTypes: ['boolean'],
438
549
  hasConfig: false,
550
+ runtime: {
551
+ timing: SemanticTiming.Before,
552
+ operations: [SemanticOperation.Delete, SemanticOperation.Read, SemanticOperation.List],
553
+ priority: 80,
554
+ },
439
555
  },
440
556
  [SemanticType.Version]: {
441
557
  id: SemanticType.Version,
@@ -445,6 +561,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
445
561
  category: SemanticCategory.Lifecycle,
446
562
  applicableDataTypes: ['number'],
447
563
  hasConfig: false,
564
+ runtime: {
565
+ timing: SemanticTiming.Before,
566
+ operations: [SemanticOperation.Update],
567
+ priority: 85,
568
+ },
448
569
  },
449
570
 
450
571
  //
@@ -459,6 +580,10 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
459
580
  category: SemanticCategory.Content,
460
581
  applicableDataTypes: ['string'],
461
582
  hasConfig: false,
583
+ runtime: {
584
+ timing: SemanticTiming.None,
585
+ operations: [],
586
+ },
462
587
  },
463
588
  [SemanticType.Description]: {
464
589
  id: SemanticType.Description,
@@ -468,6 +593,10 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
468
593
  category: SemanticCategory.Content,
469
594
  applicableDataTypes: ['string'],
470
595
  hasConfig: false,
596
+ runtime: {
597
+ timing: SemanticTiming.None,
598
+ operations: [],
599
+ },
471
600
  },
472
601
  [SemanticType.Summary]: {
473
602
  id: SemanticType.Summary,
@@ -477,6 +606,10 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
477
606
  category: SemanticCategory.Content,
478
607
  applicableDataTypes: ['string'],
479
608
  hasConfig: false,
609
+ runtime: {
610
+ timing: SemanticTiming.None,
611
+ operations: [],
612
+ },
480
613
  },
481
614
  [SemanticType.Markdown]: {
482
615
  id: SemanticType.Markdown,
@@ -486,6 +619,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
486
619
  category: SemanticCategory.Content,
487
620
  applicableDataTypes: ['string'],
488
621
  hasConfig: true,
622
+ runtime: {
623
+ timing: SemanticTiming.Before,
624
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
625
+ priority: 50, // Process before storage
626
+ },
489
627
  },
490
628
  [SemanticType.HTML]: {
491
629
  id: SemanticType.HTML,
@@ -495,6 +633,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
495
633
  category: SemanticCategory.Content,
496
634
  applicableDataTypes: ['string'],
497
635
  hasConfig: true,
636
+ runtime: {
637
+ timing: SemanticTiming.Before,
638
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
639
+ priority: 50, // Process before storage for sanitization
640
+ },
498
641
  },
499
642
  [SemanticType.ImageURL]: {
500
643
  id: SemanticType.ImageURL,
@@ -504,6 +647,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
504
647
  category: SemanticCategory.Content,
505
648
  applicableDataTypes: ['string'],
506
649
  hasConfig: false,
650
+ runtime: {
651
+ timing: SemanticTiming.Before,
652
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
653
+ priority: 60, // Validate URLs before storage
654
+ },
507
655
  },
508
656
  [SemanticType.FileURL]: {
509
657
  id: SemanticType.FileURL,
@@ -513,6 +661,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
513
661
  category: SemanticCategory.Content,
514
662
  applicableDataTypes: ['string'],
515
663
  hasConfig: false,
664
+ runtime: {
665
+ timing: SemanticTiming.Before,
666
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
667
+ priority: 60, // Validate URLs before storage
668
+ },
516
669
  },
517
670
 
518
671
  //
@@ -527,6 +680,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
527
680
  category: SemanticCategory.Business,
528
681
  applicableDataTypes: ['string'],
529
682
  hasConfig: true,
683
+ runtime: {
684
+ timing: SemanticTiming.Both,
685
+ operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Read, SemanticOperation.List],
686
+ priority: 30, // Validate state transitions before, filter by state after
687
+ },
530
688
  },
531
689
  [SemanticType.Currency]: {
532
690
  id: SemanticType.Currency,
@@ -536,6 +694,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
536
694
  category: SemanticCategory.Business,
537
695
  applicableDataTypes: ['number', 'string'],
538
696
  hasConfig: true,
697
+ runtime: {
698
+ timing: SemanticTiming.Before,
699
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
700
+ priority: 70, // Validate currency format and precision
701
+ },
539
702
  },
540
703
  [SemanticType.SKU]: {
541
704
  id: SemanticType.SKU,
@@ -545,6 +708,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
545
708
  category: SemanticCategory.Business,
546
709
  applicableDataTypes: ['string'],
547
710
  hasConfig: true,
711
+ runtime: {
712
+ timing: SemanticTiming.Before,
713
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
714
+ priority: 25, // High priority for uniqueness validation
715
+ },
548
716
  },
549
717
 
550
718
  //
@@ -559,6 +727,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
559
727
  category: SemanticCategory.Contact,
560
728
  applicableDataTypes: ['string'],
561
729
  hasConfig: true,
730
+ runtime: {
731
+ timing: SemanticTiming.Before,
732
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
733
+ priority: 40, // Validate email format
734
+ },
562
735
  },
563
736
  [SemanticType.Phone]: {
564
737
  id: SemanticType.Phone,
@@ -568,6 +741,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
568
741
  category: SemanticCategory.Contact,
569
742
  applicableDataTypes: ['string'],
570
743
  hasConfig: true,
744
+ runtime: {
745
+ timing: SemanticTiming.Before,
746
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
747
+ priority: 40, // Validate phone format
748
+ },
571
749
  },
572
750
  [SemanticType.URL]: {
573
751
  id: SemanticType.URL,
@@ -577,6 +755,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
577
755
  category: SemanticCategory.Contact,
578
756
  applicableDataTypes: ['string'],
579
757
  hasConfig: true,
758
+ runtime: {
759
+ timing: SemanticTiming.Before,
760
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
761
+ priority: 40, // Validate URL format
762
+ },
580
763
  },
581
764
 
582
765
  //
@@ -591,6 +774,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
591
774
  category: SemanticCategory.Organization,
592
775
  applicableDataTypes: ['string'],
593
776
  hasConfig: true,
777
+ runtime: {
778
+ timing: SemanticTiming.Before,
779
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
780
+ priority: 30, // Generate slug from title, validate uniqueness
781
+ },
594
782
  },
595
783
  [SemanticType.Tags]: {
596
784
  id: SemanticType.Tags,
@@ -599,6 +787,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
599
787
  description: 'Enable tagging functionality',
600
788
  category: SemanticCategory.Organization,
601
789
  hasConfig: true,
790
+ runtime: {
791
+ timing: SemanticTiming.After,
792
+ operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Delete],
793
+ priority: 200, // Process after main operation
794
+ },
602
795
  },
603
796
  [SemanticType.Categories]: {
604
797
  id: SemanticType.Categories,
@@ -607,6 +800,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
607
800
  description: 'Enable categorization functionality',
608
801
  category: SemanticCategory.Organization,
609
802
  hasConfig: true,
803
+ runtime: {
804
+ timing: SemanticTiming.After,
805
+ operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Delete],
806
+ priority: 200, // Process after main operation
807
+ },
610
808
  },
611
809
 
612
810
  //
@@ -621,6 +819,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
621
819
  category: SemanticCategory.Location,
622
820
  applicableDataTypes: ['string'],
623
821
  hasConfig: true,
822
+ runtime: {
823
+ timing: SemanticTiming.Both,
824
+ operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Read, SemanticOperation.List],
825
+ priority: 60, // Validate coordinates before, enable spatial queries after
826
+ },
624
827
  },
625
828
 
626
829
  //
@@ -636,6 +839,11 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
636
839
  category: SemanticCategory.Computed,
637
840
  applicableDataTypes: ['string', 'number', 'boolean', 'date', 'datetime', 'time', 'binary'],
638
841
  hasConfig: true,
842
+ runtime: {
843
+ timing: SemanticTiming.Both,
844
+ operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Read],
845
+ priority: 150, // Calculate after other validations, recalculate on read if needed
846
+ },
639
847
  },
640
848
  [SemanticType.Derived]: {
641
849
  id: SemanticType.Derived,
@@ -645,9 +853,55 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
645
853
  category: SemanticCategory.Computed,
646
854
  applicableDataTypes: ['string'],
647
855
  hasConfig: true,
856
+ runtime: {
857
+ timing: SemanticTiming.After,
858
+ operations: [SemanticOperation.Create, SemanticOperation.Update],
859
+ priority: 180, // Derive after all other processing
860
+ },
648
861
  },
649
862
  }
650
863
 
864
+ /**
865
+ * Get all semantics that should run for a specific operation and timing
866
+ */
867
+ export function getSemanticsForOperation(
868
+ semantics: AppliedDataSemantic[],
869
+ operation: SemanticOperation,
870
+ timing: SemanticTiming
871
+ ): AppliedDataSemantic[] {
872
+ return semantics
873
+ .filter((semantic) => {
874
+ const definition = DataSemantics[semantic.id]
875
+ if (!definition?.runtime) {
876
+ return false
877
+ }
878
+
879
+ const { timing: semanticTiming, operations } = definition.runtime
880
+
881
+ // Check if timing matches
882
+ const timingMatches = semanticTiming === timing || semanticTiming === SemanticTiming.Both
883
+
884
+ // Check if operation is included
885
+ const operationMatches = operations.includes(operation)
886
+
887
+ return timingMatches && operationMatches
888
+ })
889
+ .sort((a, b) => {
890
+ // Sort by priority (lower number = higher priority)
891
+ const priorityA = DataSemantics[a.id]?.runtime?.priority ?? 100
892
+ const priorityB = DataSemantics[b.id]?.runtime?.priority ?? 100
893
+ return priorityA - priorityB
894
+ })
895
+ }
896
+
897
+ /**
898
+ * Check if a specific semantic can be disabled at runtime
899
+ */
900
+ export function canSemanticBeDisabled(semanticType: SemanticType): boolean {
901
+ const definition = DataSemantics[semanticType]
902
+ return definition?.runtime?.canDisable !== false // Default to true if not specified
903
+ }
904
+
651
905
  export function getSemanticsByCategory(): Record<SemanticCategory, DataSemantic[]>
652
906
  export function getSemanticsByCategory(scope: SemanticScope.Entity): Record<SemanticCategory, EntitySemantic[]>
653
907
  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
  }
@@ -147,9 +147,7 @@ export const DEFAULT_PASSWORD_CONFIG: PasswordConfig = {
147
147
  requireNumbers: true,
148
148
  requireUppercase: true,
149
149
  requireLowercase: true,
150
- encryptionAlgorithm: PasswordEncryptionAlgorithm.Bcrypt,
151
- saltRounds: 12,
152
- maxAge: undefined,
150
+ minLength: 8,
153
151
  preventReuse: false,
154
152
  preventReuseCount: 5,
155
153
  allowCommonPasswords: false,