@api-client/core 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.16.0",
4
+ "version": "0.17.0",
5
5
  "license": "Apache-2.0",
6
6
  "exports": {
7
7
  "./browser.js": {
@@ -227,6 +227,44 @@ export enum SemanticScope {
227
227
  Association = 'Association',
228
228
  }
229
229
 
230
+ /**
231
+ * Defines categories for organizing semantics in the UI.
232
+ */
233
+ export enum SemanticCategory {
234
+ /**
235
+ * User management, authentication, and access control
236
+ */
237
+ Identity = 'Identity & Authentication',
238
+ /**
239
+ * Timestamps, versioning, and record lifecycle
240
+ */
241
+ Lifecycle = 'Timestamps & Versioning',
242
+ /**
243
+ * Text content, media, and rich content types
244
+ */
245
+ Content = 'Content & Media',
246
+ /**
247
+ * Business-specific data like pricing, inventory, status
248
+ */
249
+ Business = 'Business Data',
250
+ /**
251
+ * Contact information and communication
252
+ */
253
+ Contact = 'Contact Information',
254
+ /**
255
+ * Organization, categorization, and tagging
256
+ */
257
+ Organization = 'Classification & Organization',
258
+ /**
259
+ * Location and geographical data
260
+ */
261
+ Location = 'Location & Geography',
262
+ /**
263
+ * Calculated and derived values
264
+ */
265
+ Computed = 'Computed Values',
266
+ }
267
+
230
268
  /**
231
269
  * A base interface for all Data Semantics, containing common properties.
232
270
  * A semantic is an annotation applied to a Data Entity, Property, or Association
@@ -250,6 +288,10 @@ interface BaseDataSemantic {
250
288
  * Specifies whether the semantic applies to an Entity, Property, or Association.
251
289
  */
252
290
  scope: SemanticScope
291
+ /**
292
+ * The category this semantic belongs to for UI organization.
293
+ */
294
+ category: SemanticCategory
253
295
  }
254
296
 
255
297
  /**
@@ -307,213 +349,296 @@ export type DataSemantic = EntitySemantic | PropertySemantic | AssociationSemant
307
349
  */
308
350
  export const DataSemantics: Record<SemanticType, DataSemantic> = {
309
351
  //
310
- // Entity-Level Definitions
352
+ // Identity & Authentication
311
353
  //
312
354
 
313
355
  [SemanticType.User]: {
314
356
  id: SemanticType.User,
315
357
  displayName: 'User Entity',
316
358
  scope: SemanticScope.Entity,
317
- description: 'Designates an entity that represents system users, crucial for authentication and authorization.',
359
+ description: 'System users and accounts',
360
+ category: SemanticCategory.Identity,
318
361
  },
319
-
320
- //
321
- // Property-Level Definitions
322
- //
323
-
324
362
  [SemanticType.Password]: {
325
363
  id: SemanticType.Password,
326
364
  displayName: 'User Password',
327
365
  scope: SemanticScope.Property,
328
- description:
329
- 'Annotates the field as the user password. The runtime should treat this field with special care, ensuring it is encrypted and not exposed in API responses.',
366
+ description: 'Secure password field',
367
+ category: SemanticCategory.Identity,
368
+ applicableDataTypes: ['string'],
369
+ },
370
+ [SemanticType.UserRole]: {
371
+ id: SemanticType.UserRole,
372
+ displayName: 'User Role Field',
373
+ scope: SemanticScope.Property,
374
+ description: 'User permissions and access level',
375
+ category: SemanticCategory.Identity,
330
376
  applicableDataTypes: ['string'],
331
377
  },
378
+ [SemanticType.ResourceOwnerIdentifier]: {
379
+ id: SemanticType.ResourceOwnerIdentifier,
380
+ displayName: 'Resource Owner Identifier',
381
+ scope: SemanticScope.Association,
382
+ description: 'Links record to owner user',
383
+ category: SemanticCategory.Identity,
384
+ },
385
+
386
+ //
387
+ // Timestamps & Versioning
388
+ //
389
+
332
390
  [SemanticType.CreatedTimestamp]: {
333
391
  id: SemanticType.CreatedTimestamp,
334
392
  displayName: 'Creation Timestamp',
335
393
  scope: SemanticScope.Property,
336
- description: "Marks a field as the one that contains the object's creation timestamp.",
394
+ description: 'When record was created',
395
+ category: SemanticCategory.Lifecycle,
337
396
  applicableDataTypes: ['datetime'],
338
397
  },
339
398
  [SemanticType.UpdatedTimestamp]: {
340
399
  id: SemanticType.UpdatedTimestamp,
341
400
  displayName: 'Update Timestamp',
342
401
  scope: SemanticScope.Property,
343
- description: "Marks a field as the field that contains object's last modification timestamp.",
402
+ description: 'When record was last modified',
403
+ category: SemanticCategory.Lifecycle,
344
404
  applicableDataTypes: ['datetime'],
345
405
  },
346
406
  [SemanticType.DeletedTimestamp]: {
347
407
  id: SemanticType.DeletedTimestamp,
348
408
  displayName: 'Soft Delete Timestamp',
349
409
  scope: SemanticScope.Property,
350
- description: "Marks a field as the field that contains object's deletion timestamp.",
410
+ description: 'When record was marked deleted',
411
+ category: SemanticCategory.Lifecycle,
351
412
  applicableDataTypes: ['datetime'],
352
413
  },
353
- [SemanticType.PublicUniqueName]: {
354
- id: SemanticType.PublicUniqueName,
355
- displayName: 'Public Unique Name (Slug)',
414
+ [SemanticType.DeletedFlag]: {
415
+ id: SemanticType.DeletedFlag,
416
+ displayName: 'Soft Delete Flag',
356
417
  scope: SemanticScope.Property,
357
- description: 'A user-friendly, unique public identifier for a resource, often used in URLs.',
358
- applicableDataTypes: ['string'],
418
+ description: 'Mark record as deleted',
419
+ category: SemanticCategory.Lifecycle,
420
+ applicableDataTypes: ['boolean'],
421
+ },
422
+ [SemanticType.Version]: {
423
+ id: SemanticType.Version,
424
+ displayName: 'Version Number',
425
+ scope: SemanticScope.Property,
426
+ description: 'Auto-incrementing version counter',
427
+ category: SemanticCategory.Lifecycle,
428
+ applicableDataTypes: ['number'],
359
429
  },
430
+
431
+ //
432
+ // Content & Media
433
+ //
434
+
360
435
  [SemanticType.Title]: {
361
436
  id: SemanticType.Title,
362
437
  displayName: 'Record Title',
363
438
  scope: SemanticScope.Property,
364
- description: 'A title for the record. Used as a source for the PublicUniqueName semantic.',
439
+ description: 'Main title or heading',
440
+ category: SemanticCategory.Content,
365
441
  applicableDataTypes: ['string'],
366
442
  },
367
- [SemanticType.UserRole]: {
368
- id: SemanticType.UserRole,
369
- displayName: 'User Role Field',
443
+ [SemanticType.Description]: {
444
+ id: SemanticType.Description,
445
+ displayName: 'Description',
370
446
  scope: SemanticScope.Property,
371
- description: "A text or enum field that defines the user's role for role-based authorization.",
447
+ description: 'Detailed description text',
448
+ category: SemanticCategory.Content,
372
449
  applicableDataTypes: ['string'],
373
450
  },
374
- [SemanticType.Status]: {
375
- id: SemanticType.Status,
376
- displayName: 'Record Status',
451
+ [SemanticType.Summary]: {
452
+ id: SemanticType.Summary,
453
+ displayName: 'Summary',
377
454
  scope: SemanticScope.Property,
378
- description: 'A text or enum field to represent the state of a record.',
455
+ description: 'Brief summary text',
456
+ category: SemanticCategory.Content,
379
457
  applicableDataTypes: ['string'],
380
458
  },
381
- [SemanticType.Version]: {
382
- id: SemanticType.Version,
383
- displayName: 'Version Number',
459
+ [SemanticType.Markdown]: {
460
+ id: SemanticType.Markdown,
461
+ displayName: 'Markdown Content',
384
462
  scope: SemanticScope.Property,
385
- description: 'An integer field that automatically increments on every update.',
386
- applicableDataTypes: ['number'],
463
+ description: 'Formatted text content',
464
+ category: SemanticCategory.Content,
465
+ applicableDataTypes: ['string'],
466
+ },
467
+ [SemanticType.HTML]: {
468
+ id: SemanticType.HTML,
469
+ displayName: 'HTML Content',
470
+ scope: SemanticScope.Property,
471
+ description: 'Rich HTML content',
472
+ category: SemanticCategory.Content,
473
+ applicableDataTypes: ['string'],
387
474
  },
388
475
  [SemanticType.ImageURL]: {
389
476
  id: SemanticType.ImageURL,
390
477
  displayName: 'Image URL',
391
478
  scope: SemanticScope.Property,
392
- description: 'Annotates a field that holds a reference to an image data via an URL.',
479
+ description: 'Link to image file',
480
+ category: SemanticCategory.Content,
393
481
  applicableDataTypes: ['string'],
394
482
  },
395
483
  [SemanticType.FileURL]: {
396
484
  id: SemanticType.FileURL,
397
485
  displayName: 'File URL',
398
486
  scope: SemanticScope.Property,
399
- description: 'Annotates a field that holds a reference to a file object via an URL (non-image binary data).',
487
+ description: 'Link to file attachment',
488
+ category: SemanticCategory.Content,
400
489
  applicableDataTypes: ['string'],
401
490
  },
402
- [SemanticType.DeletedFlag]: {
403
- id: SemanticType.DeletedFlag,
404
- displayName: 'Soft Delete Flag',
405
- scope: SemanticScope.Property,
406
- description: 'A boolean property that marks the object as deleted without physically removing it.',
407
- applicableDataTypes: ['boolean'],
408
- },
409
- [SemanticType.Markdown]: {
410
- id: SemanticType.Markdown,
411
- displayName: 'Markdown Content',
491
+
492
+ //
493
+ // Business Data
494
+ //
495
+
496
+ [SemanticType.Status]: {
497
+ id: SemanticType.Status,
498
+ displayName: 'Record Status',
412
499
  scope: SemanticScope.Property,
413
- description: 'A text field that contains markdown content.',
500
+ description: 'Current state of record',
501
+ category: SemanticCategory.Business,
414
502
  applicableDataTypes: ['string'],
415
503
  },
416
- [SemanticType.HTML]: {
417
- id: SemanticType.HTML,
418
- displayName: 'HTML Content',
504
+ [SemanticType.Price]: {
505
+ id: SemanticType.Price,
506
+ displayName: 'Price',
419
507
  scope: SemanticScope.Property,
420
- description: 'Annotates a field that contains HTML content.',
421
- applicableDataTypes: ['string'],
508
+ description: 'Monetary value with currency',
509
+ category: SemanticCategory.Business,
510
+ applicableDataTypes: ['number', 'string'],
422
511
  },
423
- [SemanticType.GeospatialCoordinates]: {
424
- id: SemanticType.GeospatialCoordinates,
425
- displayName: 'Geospatial Coordinates',
512
+ [SemanticType.SKU]: {
513
+ id: SemanticType.SKU,
514
+ displayName: 'SKU',
426
515
  scope: SemanticScope.Property,
427
- description: 'Annotates a field that holds geospatial coordinate data (latitude/longitude).',
516
+ description: 'Product identification code',
517
+ category: SemanticCategory.Business,
428
518
  applicableDataTypes: ['string'],
429
519
  },
520
+
521
+ //
522
+ // Contact Information
523
+ //
524
+
430
525
  [SemanticType.Email]: {
431
526
  id: SemanticType.Email,
432
527
  displayName: 'Email',
433
528
  scope: SemanticScope.Property,
434
- description: 'Annotates a field as an email address with validation and verification options.',
529
+ description: 'Email address',
530
+ category: SemanticCategory.Contact,
435
531
  applicableDataTypes: ['string'],
436
532
  },
437
533
  [SemanticType.Phone]: {
438
534
  id: SemanticType.Phone,
439
535
  displayName: 'Phone',
440
536
  scope: SemanticScope.Property,
441
- description: 'Annotates a field as a phone number with validation and formatting options.',
537
+ description: 'Phone number',
538
+ category: SemanticCategory.Contact,
442
539
  applicableDataTypes: ['string'],
443
540
  },
444
- [SemanticType.Price]: {
445
- id: SemanticType.Price,
446
- displayName: 'Price',
447
- scope: SemanticScope.Property,
448
- description: 'Annotates a field as a monetary value with currency support and precision control.',
449
- applicableDataTypes: ['number', 'string'],
450
- },
451
541
  [SemanticType.URL]: {
452
542
  id: SemanticType.URL,
453
543
  displayName: 'URL',
454
544
  scope: SemanticScope.Property,
455
- description: 'Annotates a field as a URL with validation and allowed protocols.',
545
+ description: 'Web address or link',
546
+ category: SemanticCategory.Contact,
456
547
  applicableDataTypes: ['string'],
457
548
  },
458
- [SemanticType.SKU]: {
459
- id: SemanticType.SKU,
460
- displayName: 'SKU',
549
+
550
+ //
551
+ // Classification & Organization
552
+ //
553
+
554
+ [SemanticType.PublicUniqueName]: {
555
+ id: SemanticType.PublicUniqueName,
556
+ displayName: 'Public Unique Name (Slug)',
461
557
  scope: SemanticScope.Property,
462
- description:
463
- 'Annotates a field as a Stock Keeping Unit (SKU). Enforces uniqueness at the database level, critical for product catalogs. Provides automatic validation and formatting for product identification codes.',
558
+ description: 'URL-friendly unique identifier',
559
+ category: SemanticCategory.Organization,
464
560
  applicableDataTypes: ['string'],
465
561
  },
466
- [SemanticType.Description]: {
467
- id: SemanticType.Description,
468
- displayName: 'Description',
469
- scope: SemanticScope.Property,
470
- description: 'Annotates a field as a long-form description.',
471
- applicableDataTypes: ['string'],
562
+ [SemanticType.Tags]: {
563
+ id: SemanticType.Tags,
564
+ displayName: 'Tags',
565
+ scope: SemanticScope.Association,
566
+ description: 'Enable tagging functionality',
567
+ category: SemanticCategory.Organization,
472
568
  },
473
- [SemanticType.Summary]: {
474
- id: SemanticType.Summary,
475
- displayName: 'Summary',
569
+ [SemanticType.Categories]: {
570
+ id: SemanticType.Categories,
571
+ displayName: 'Categories',
572
+ scope: SemanticScope.Association,
573
+ description: 'Enable categorization functionality',
574
+ category: SemanticCategory.Organization,
575
+ },
576
+
577
+ //
578
+ // Location & Geography
579
+ //
580
+
581
+ [SemanticType.GeospatialCoordinates]: {
582
+ id: SemanticType.GeospatialCoordinates,
583
+ displayName: 'Geospatial Coordinates',
476
584
  scope: SemanticScope.Property,
477
- description: 'Annotates a field as a short summary.',
585
+ description: 'Location coordinates',
586
+ category: SemanticCategory.Location,
478
587
  applicableDataTypes: ['string'],
479
588
  },
589
+
590
+ //
591
+ // Computed Values
592
+ //
593
+
480
594
  [SemanticType.Calculated]: {
481
595
  id: SemanticType.Calculated,
482
596
  displayName: 'Calculated',
483
597
  scope: SemanticScope.Property,
484
- description: 'Annotates a field as a calculated value based on a formula.',
598
+ description: 'Auto-calculated field value',
599
+ category: SemanticCategory.Computed,
485
600
  applicableDataTypes: ['string'],
486
601
  },
487
602
  [SemanticType.Derived]: {
488
603
  id: SemanticType.Derived,
489
604
  displayName: 'Derived',
490
605
  scope: SemanticScope.Property,
491
- description: 'Annotates a field as derived from other fields.',
606
+ description: 'Value derived from other fields',
607
+ category: SemanticCategory.Computed,
492
608
  applicableDataTypes: ['string'],
493
609
  },
610
+ }
494
611
 
495
- //
496
- // Association-Level Definitions
497
- //
612
+ /**
613
+ * Helper function to get all semantics grouped by category.
614
+ * Useful for organizing semantics in UI dropdowns and forms.
615
+ */
616
+ export const getSemanticsByCategory = (): Record<SemanticCategory, DataSemantic[]> => {
617
+ const result: Record<SemanticCategory, DataSemantic[]> = {
618
+ [SemanticCategory.Identity]: [],
619
+ [SemanticCategory.Lifecycle]: [],
620
+ [SemanticCategory.Content]: [],
621
+ [SemanticCategory.Business]: [],
622
+ [SemanticCategory.Contact]: [],
623
+ [SemanticCategory.Organization]: [],
624
+ [SemanticCategory.Location]: [],
625
+ [SemanticCategory.Computed]: [],
626
+ }
498
627
 
499
- [SemanticType.ResourceOwnerIdentifier]: {
500
- id: SemanticType.ResourceOwnerIdentifier,
501
- displayName: 'Resource Owner Identifier',
502
- scope: SemanticScope.Association,
503
- description: 'Links a resource to a "User" entity instance, indicating ownership for access control.',
504
- },
505
- [SemanticType.Tags]: {
506
- id: SemanticType.Tags,
507
- displayName: 'Tags',
508
- scope: SemanticScope.Association,
509
- description: 'Annotates an association as supporting tag functionality.',
510
- },
511
- [SemanticType.Categories]: {
512
- id: SemanticType.Categories,
513
- displayName: 'Categories',
514
- scope: SemanticScope.Association,
515
- description: 'Annotates an association as supporting category functionality.',
516
- },
628
+ Object.values(DataSemantics).forEach((semantic) => {
629
+ result[semantic.category].push(semantic)
630
+ })
631
+
632
+ return result
633
+ }
634
+
635
+ /**
636
+ * Helper function to get semantics for a specific category.
637
+ * @param category The category to filter by
638
+ * @returns Array of semantics in the specified category
639
+ */
640
+ export const getSemanticsByCategoryType = (category: SemanticCategory): DataSemantic[] => {
641
+ return Object.values(DataSemantics).filter((semantic) => semantic.category === category)
517
642
  }
518
643
 
519
644
  /**
@@ -2,10 +2,13 @@ import { test } from '@japa/runner'
2
2
  import {
3
3
  SemanticType,
4
4
  SemanticScope,
5
+ SemanticCategory,
5
6
  isEntitySemantic,
6
7
  isPropertySemantic,
7
8
  isAssociationSemantic,
8
9
  DataSemantics,
10
+ getSemanticsByCategory,
11
+ getSemanticsByCategoryType,
9
12
  type EntitySemantic,
10
13
  type PropertySemantic,
11
14
  type AssociationSemantic,
@@ -45,18 +48,21 @@ test.group('Semantics', () => {
45
48
  displayName: 'User Entity',
46
49
  description: 'Test',
47
50
  scope: SemanticScope.Entity,
51
+ category: SemanticCategory.Identity,
48
52
  }
49
53
  const propertySemantic: PropertySemantic = {
50
54
  id: SemanticType.CreatedTimestamp,
51
55
  displayName: 'Creation Timestamp',
52
56
  description: 'Test',
53
57
  scope: SemanticScope.Property,
58
+ category: SemanticCategory.Lifecycle,
54
59
  }
55
60
  const associationSemantic: AssociationSemantic = {
56
61
  id: SemanticType.ResourceOwnerIdentifier,
57
62
  displayName: 'Resource Owner Identifier',
58
63
  description: 'Test',
59
64
  scope: SemanticScope.Association,
65
+ category: SemanticCategory.Identity,
60
66
  }
61
67
 
62
68
  assert.isTrue(isEntitySemantic(entitySemantic))
@@ -70,18 +76,21 @@ test.group('Semantics', () => {
70
76
  displayName: 'User Entity',
71
77
  description: 'Test',
72
78
  scope: SemanticScope.Entity,
79
+ category: SemanticCategory.Identity,
73
80
  }
74
81
  const propertySemantic: PropertySemantic = {
75
82
  id: SemanticType.CreatedTimestamp,
76
83
  displayName: 'Creation Timestamp',
77
84
  description: 'Test',
78
85
  scope: SemanticScope.Property,
86
+ category: SemanticCategory.Lifecycle,
79
87
  }
80
88
  const associationSemantic: AssociationSemantic = {
81
89
  id: SemanticType.ResourceOwnerIdentifier,
82
90
  displayName: 'Resource Owner Identifier',
83
91
  description: 'Test',
84
92
  scope: SemanticScope.Association,
93
+ category: SemanticCategory.Identity,
85
94
  }
86
95
 
87
96
  assert.isFalse(isPropertySemantic(entitySemantic))
@@ -95,18 +104,21 @@ test.group('Semantics', () => {
95
104
  displayName: 'User Entity',
96
105
  description: 'Test',
97
106
  scope: SemanticScope.Entity,
107
+ category: SemanticCategory.Identity,
98
108
  }
99
109
  const propertySemantic: PropertySemantic = {
100
110
  id: SemanticType.CreatedTimestamp,
101
111
  displayName: 'Creation Timestamp',
102
112
  description: 'Test',
103
113
  scope: SemanticScope.Property,
114
+ category: SemanticCategory.Lifecycle,
104
115
  }
105
116
  const associationSemantic: AssociationSemantic = {
106
117
  id: SemanticType.ResourceOwnerIdentifier,
107
118
  displayName: 'Resource Owner Identifier',
108
119
  description: 'Test',
109
120
  scope: SemanticScope.Association,
121
+ category: SemanticCategory.Identity,
110
122
  }
111
123
 
112
124
  assert.isFalse(isAssociationSemantic(entitySemantic))
@@ -128,6 +140,11 @@ test.group('Semantics', () => {
128
140
  assert.isNotEmpty(semantic.description, `description for ${type} should not be empty`)
129
141
 
130
142
  assert.oneOf(semantic.scope, Object.values(SemanticScope), `scope for ${type} should be a valid SemanticScope`)
143
+ assert.oneOf(
144
+ semantic.category,
145
+ Object.values(SemanticCategory),
146
+ `category for ${type} should be a valid SemanticCategory`
147
+ )
131
148
 
132
149
  if (isPropertySemantic(semantic)) {
133
150
  if (semantic.applicableDataTypes) {
@@ -142,15 +159,18 @@ test.group('Semantics', () => {
142
159
  // Specific checks for some semantics
143
160
  const userSemantic = DataSemantics[SemanticType.User]
144
161
  assert.equal(userSemantic.scope, SemanticScope.Entity)
162
+ assert.equal(userSemantic.category, SemanticCategory.Identity)
145
163
 
146
164
  const createdTimestampSemantic = DataSemantics[SemanticType.CreatedTimestamp]
147
165
  assert.equal(createdTimestampSemantic.scope, SemanticScope.Property)
166
+ assert.equal(createdTimestampSemantic.category, SemanticCategory.Lifecycle)
148
167
  if (isPropertySemantic(createdTimestampSemantic)) {
149
168
  assert.deepEqual(createdTimestampSemantic.applicableDataTypes, ['datetime'])
150
169
  }
151
170
 
152
171
  const resourceOwnerSemantic = DataSemantics[SemanticType.ResourceOwnerIdentifier]
153
172
  assert.equal(resourceOwnerSemantic.scope, SemanticScope.Association)
173
+ assert.equal(resourceOwnerSemantic.category, SemanticCategory.Identity)
154
174
  })
155
175
 
156
176
  test('GeospatialCoordinates semantic should have correct definition', ({ assert }) => {
@@ -193,6 +213,58 @@ test.group('Semantics', () => {
193
213
  const description = geospatialSemantic.description
194
214
 
195
215
  // Check that the description mentions key features
196
- assert.equal(description, 'Annotates a field that holds geospatial coordinate data (latitude/longitude).')
216
+ assert.equal(description, 'Location coordinates')
217
+ })
218
+
219
+ test('getSemanticsByCategory should group semantics correctly', ({ assert }) => {
220
+ const semanticsByCategory = getSemanticsByCategory()
221
+
222
+ // Check that all categories are present
223
+ const expectedCategories = Object.values(SemanticCategory)
224
+ expectedCategories.forEach((category) => {
225
+ assert.isDefined(semanticsByCategory[category], `Category ${category} should be present`)
226
+ assert.isArray(semanticsByCategory[category], `Category ${category} should be an array`)
227
+ })
228
+
229
+ // Check that User semantic is in Identity category
230
+ const identitySemantics = semanticsByCategory[SemanticCategory.Identity]
231
+ const userSemantic = identitySemantics.find((s) => s.id === SemanticType.User)
232
+ assert.isDefined(userSemantic, 'User semantic should be in Identity category')
233
+
234
+ // Check that CreatedTimestamp is in Lifecycle category
235
+ const lifecycleSemantics = semanticsByCategory[SemanticCategory.Lifecycle]
236
+ const createdTimestampSemantic = lifecycleSemantics.find((s) => s.id === SemanticType.CreatedTimestamp)
237
+ assert.isDefined(createdTimestampSemantic, 'CreatedTimestamp semantic should be in Lifecycle category')
238
+
239
+ // Check that Title is in Content category
240
+ const contentSemantics = semanticsByCategory[SemanticCategory.Content]
241
+ const titleSemantic = contentSemantics.find((s) => s.id === SemanticType.Title)
242
+ assert.isDefined(titleSemantic, 'Title semantic should be in Content category')
243
+
244
+ // Verify all semantics are categorized (total count should match)
245
+ const totalSemantics = Object.values(semanticsByCategory).flat().length
246
+ const allSemantics = Object.values(DataSemantics).length
247
+ assert.equal(totalSemantics, allSemantics, 'All semantics should be categorized')
248
+ })
249
+
250
+ test('getSemanticsByCategoryType should filter semantics by category', ({ assert }) => {
251
+ const identitySemantics = getSemanticsByCategoryType(SemanticCategory.Identity)
252
+
253
+ // All returned semantics should be in Identity category
254
+ identitySemantics.forEach((semantic) => {
255
+ assert.equal(semantic.category, SemanticCategory.Identity, 'All semantics should be in Identity category')
256
+ })
257
+
258
+ // Should include User semantic
259
+ const userSemantic = identitySemantics.find((s) => s.id === SemanticType.User)
260
+ assert.isDefined(userSemantic, 'Should include User semantic')
261
+
262
+ // Should include Password semantic
263
+ const passwordSemantic = identitySemantics.find((s) => s.id === SemanticType.Password)
264
+ assert.isDefined(passwordSemantic, 'Should include Password semantic')
265
+
266
+ // Test empty result for a category with no semantics (if any)
267
+ const locationSemantics = getSemanticsByCategoryType(SemanticCategory.Location)
268
+ assert.isArray(locationSemantics, 'Should return an array even if empty')
197
269
  })
198
270
  })