@api-client/core 0.18.25 → 0.18.27
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/Semantics.d.ts +193 -0
- package/build/src/modeling/Semantics.d.ts.map +1 -1
- package/build/src/modeling/Semantics.js +134 -0
- package/build/src/modeling/Semantics.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.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 +249 -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.d.ts.map +1 -1
- package/build/src/modeling/templates/verticals/technology-media/blog-domain.js +17 -9
- 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 +14 -14
- package/package.json +1 -1
- package/src/modeling/Semantics.ts +262 -0
- package/src/modeling/helpers/Intelisense.ts +7 -7
- 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 +286 -65
- package/src/modeling/templates/verticals/technology-media/blog-domain.ts +17 -9
- 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/username_semantic.spec.ts +81 -0
|
@@ -42062,9 +42062,6 @@
|
|
|
42062
42062
|
"@id": "#209"
|
|
42063
42063
|
},
|
|
42064
42064
|
{
|
|
42065
|
-
"@id": "#206"
|
|
42066
|
-
},
|
|
42067
|
-
{
|
|
42068
42065
|
"@id": "#191"
|
|
42069
42066
|
},
|
|
42070
42067
|
{
|
|
@@ -42074,10 +42071,13 @@
|
|
|
42074
42071
|
"@id": "#197"
|
|
42075
42072
|
},
|
|
42076
42073
|
{
|
|
42074
|
+
"@id": "#200"
|
|
42075
|
+
},
|
|
42076
|
+
{
|
|
42077
42077
|
"@id": "#203"
|
|
42078
42078
|
},
|
|
42079
42079
|
{
|
|
42080
|
-
"@id": "#
|
|
42080
|
+
"@id": "#206"
|
|
42081
42081
|
},
|
|
42082
42082
|
{
|
|
42083
42083
|
"@id": "#209"
|
|
@@ -43436,7 +43436,7 @@
|
|
|
43436
43436
|
"doc:ExternalDomainElement",
|
|
43437
43437
|
"doc:DomainElement"
|
|
43438
43438
|
],
|
|
43439
|
-
"doc:raw": "
|
|
43439
|
+
"doc:raw": "countryCode: \"BE\"\ngraydonEnterpriseId: 1057155523\nregistrationId: \"0422319093\"\nvatNumber: \"BE0422319093\"\ngraydonCompanyId: \"0422319093\"\nisBranchOffice: false\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": "
|
|
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",
|
|
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": "
|
|
43481
|
+
"doc:raw": "code: '5'\ndescription: 'Limited company'\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": "
|
|
43502
|
+
"doc:raw": "class: '3'\ndescription: '150 - 300'\nnumberOfFte: 5500\nnumberOfEmployees: 5232\n",
|
|
43503
43503
|
"core:mediaType": "application/yaml",
|
|
43504
43504
|
"sourcemaps:sources": [
|
|
43505
43505
|
{
|
|
@@ -43541,7 +43541,7 @@
|
|
|
43541
43541
|
"doc:ExternalDomainElement",
|
|
43542
43542
|
"doc:DomainElement"
|
|
43543
43543
|
],
|
|
43544
|
-
"doc:raw": "
|
|
43544
|
+
"doc:raw": "code: '7487'\ndescription: 'Financial and insurance activities'\ntype: \"PRIMARY\"\nclassificationCode: 'BE_NACEBEL2008'\nactivityGroupCode: 'ABCDE'\n",
|
|
43545
43545
|
"core:mediaType": "application/yaml",
|
|
43546
43546
|
"sourcemaps:sources": [
|
|
43547
43547
|
{
|
|
@@ -44756,22 +44756,22 @@
|
|
|
44756
44756
|
{
|
|
44757
44757
|
"@id": "#193/source-map/lexical/element_0",
|
|
44758
44758
|
"sourcemaps:element": "amf://id#193",
|
|
44759
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44759
|
+
"sourcemaps:value": "[(1,0)-(7,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)-(
|
|
44764
|
+
"sourcemaps:value": "[(1,0)-(10,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)-(
|
|
44769
|
+
"sourcemaps:value": "[(1,0)-(3,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)-(
|
|
44774
|
+
"sourcemaps:value": "[(1,0)-(5,0)]"
|
|
44775
44775
|
},
|
|
44776
44776
|
{
|
|
44777
44777
|
"@id": "#205/source-map/lexical/element_0",
|
|
@@ -44781,7 +44781,7 @@
|
|
|
44781
44781
|
{
|
|
44782
44782
|
"@id": "#208/source-map/lexical/element_0",
|
|
44783
44783
|
"sourcemaps:element": "amf://id#208",
|
|
44784
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44784
|
+
"sourcemaps:value": "[(1,0)-(6,0)]"
|
|
44785
44785
|
},
|
|
44786
44786
|
{
|
|
44787
44787
|
"@id": "#223/source-map/lexical/element_0",
|
package/package.json
CHANGED
|
@@ -26,6 +26,13 @@ export enum SemanticType {
|
|
|
26
26
|
* ensuring it is encrypted and not exposed in API responses.
|
|
27
27
|
*/
|
|
28
28
|
Password = 'Semantic#Password',
|
|
29
|
+
/**
|
|
30
|
+
* Annotates the field as the username for user authentication.
|
|
31
|
+
* This identifies which field should be used for login purposes.
|
|
32
|
+
* Can be applied to dedicated username fields or email fields that serve as usernames.
|
|
33
|
+
* The runtime uses this for authentication, password reset, and user lookup operations.
|
|
34
|
+
*/
|
|
35
|
+
Username = 'Semantic#Username',
|
|
29
36
|
/**
|
|
30
37
|
* Designates a Data Property as the `createdAt` timestamp of an entity.
|
|
31
38
|
* This is used to track when the entity was first created.
|
|
@@ -182,6 +189,12 @@ export enum SemanticType {
|
|
|
182
189
|
* Annotates a field as derived from other fields.
|
|
183
190
|
*/
|
|
184
191
|
Derived = 'Semantic#Derived',
|
|
192
|
+
/**
|
|
193
|
+
* Annotates a field that should automatically receive the client's IP address.
|
|
194
|
+
* The runtime automatically populates this field with the request's IP address
|
|
195
|
+
* when creating or updating records, providing audit trail and geolocation capabilities.
|
|
196
|
+
*/
|
|
197
|
+
ClientIPAddress = 'Semantic#ClientIPAddress',
|
|
185
198
|
|
|
186
199
|
//
|
|
187
200
|
// Association-Level Semantics
|
|
@@ -306,6 +319,64 @@ export enum SemanticOperation {
|
|
|
306
319
|
List = 'List',
|
|
307
320
|
}
|
|
308
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Defines the execution mode for a semantic.
|
|
324
|
+
*/
|
|
325
|
+
export enum SemanticExecutionMode {
|
|
326
|
+
/**
|
|
327
|
+
* Execute synchronously as part of the main operation
|
|
328
|
+
*/
|
|
329
|
+
Synchronous = 'Synchronous',
|
|
330
|
+
/**
|
|
331
|
+
* Execute asynchronously after the main operation
|
|
332
|
+
*/
|
|
333
|
+
Asynchronous = 'Asynchronous',
|
|
334
|
+
/**
|
|
335
|
+
* Execute in background/queue (fire and forget)
|
|
336
|
+
*/
|
|
337
|
+
Background = 'Background',
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Defines validation strategies for semantics.
|
|
342
|
+
*/
|
|
343
|
+
export enum SemanticValidationStrategy {
|
|
344
|
+
/**
|
|
345
|
+
* Fail the operation if semantic validation fails
|
|
346
|
+
*/
|
|
347
|
+
Strict = 'Strict',
|
|
348
|
+
/**
|
|
349
|
+
* Log warnings but continue operation
|
|
350
|
+
*/
|
|
351
|
+
Warning = 'Warning',
|
|
352
|
+
/**
|
|
353
|
+
* Skip validation entirely
|
|
354
|
+
*/
|
|
355
|
+
Skip = 'Skip',
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Defines caching strategies for computed semantics.
|
|
360
|
+
*/
|
|
361
|
+
export enum SemanticCacheStrategy {
|
|
362
|
+
/**
|
|
363
|
+
* No caching - always compute
|
|
364
|
+
*/
|
|
365
|
+
None = 'None',
|
|
366
|
+
/**
|
|
367
|
+
* Cache until dependent fields change
|
|
368
|
+
*/
|
|
369
|
+
Dependency = 'Dependency',
|
|
370
|
+
/**
|
|
371
|
+
* Cache with TTL expiration
|
|
372
|
+
*/
|
|
373
|
+
TimeToLive = 'TimeToLive',
|
|
374
|
+
/**
|
|
375
|
+
* Cache until manually invalidated
|
|
376
|
+
*/
|
|
377
|
+
Manual = 'Manual',
|
|
378
|
+
}
|
|
379
|
+
|
|
309
380
|
/**
|
|
310
381
|
* Configuration for when and how a semantic should execute at runtime.
|
|
311
382
|
*/
|
|
@@ -327,6 +398,134 @@ export interface SemanticRuntimeConfig {
|
|
|
327
398
|
* Whether this semantic can be disabled at the entity/property level.
|
|
328
399
|
*/
|
|
329
400
|
canDisable?: boolean
|
|
401
|
+
/**
|
|
402
|
+
* How the semantic should execute (sync/async/background).
|
|
403
|
+
* Default is Synchronous.
|
|
404
|
+
* @default Synchronous
|
|
405
|
+
*/
|
|
406
|
+
executionMode?: SemanticExecutionMode
|
|
407
|
+
/**
|
|
408
|
+
* Validation strategy when semantic processing fails.
|
|
409
|
+
* Default is Strict.
|
|
410
|
+
* @default Strict
|
|
411
|
+
*/
|
|
412
|
+
validationStrategy?: SemanticValidationStrategy
|
|
413
|
+
/**
|
|
414
|
+
* Maximum execution time in milliseconds before timeout.
|
|
415
|
+
* Default is 5000ms for sync, 30000ms for async.
|
|
416
|
+
* @default 5000 for sync, 30000 for async
|
|
417
|
+
*/
|
|
418
|
+
timeoutMs?: number
|
|
419
|
+
/**
|
|
420
|
+
* Number of retry attempts on failure.
|
|
421
|
+
* Default is 0 (no retries).
|
|
422
|
+
* @default 0
|
|
423
|
+
*/
|
|
424
|
+
retryAttempts?: number
|
|
425
|
+
/**
|
|
426
|
+
* Dependencies on other semantics that must execute first.
|
|
427
|
+
* Uses semantic IDs.
|
|
428
|
+
*/
|
|
429
|
+
dependencies?: SemanticType[]
|
|
430
|
+
/**
|
|
431
|
+
* Fields that this semantic depends on for computation.
|
|
432
|
+
* Used for cache invalidation and optimization.
|
|
433
|
+
*/
|
|
434
|
+
dependentFields?: string[]
|
|
435
|
+
/**
|
|
436
|
+
* Caching strategy for computed/derived values.
|
|
437
|
+
*/
|
|
438
|
+
cacheStrategy?: SemanticCacheStrategy
|
|
439
|
+
/**
|
|
440
|
+
* Cache TTL in seconds (only used with TimeToLive strategy).
|
|
441
|
+
*/
|
|
442
|
+
cacheTtlSeconds?: number
|
|
443
|
+
/**
|
|
444
|
+
* Whether this semantic should run in transactions.
|
|
445
|
+
* Default is true.
|
|
446
|
+
*/
|
|
447
|
+
transactional?: boolean
|
|
448
|
+
/**
|
|
449
|
+
* Rate limiting configuration for expensive operations.
|
|
450
|
+
*/
|
|
451
|
+
rateLimit?: SemanticRateLimit
|
|
452
|
+
/**
|
|
453
|
+
* Conditional execution based on data or context.
|
|
454
|
+
*/
|
|
455
|
+
conditions?: SemanticCondition[]
|
|
456
|
+
/**
|
|
457
|
+
* Audit and logging configuration.
|
|
458
|
+
*/
|
|
459
|
+
audit?: SemanticAudit
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export interface SemanticRateLimit {
|
|
463
|
+
/**
|
|
464
|
+
* Maximum executions per time window.
|
|
465
|
+
*/
|
|
466
|
+
maxExecutions: number
|
|
467
|
+
/**
|
|
468
|
+
* Time window in seconds.
|
|
469
|
+
*/
|
|
470
|
+
windowSeconds: number
|
|
471
|
+
/**
|
|
472
|
+
* Strategy when rate limit is exceeded.
|
|
473
|
+
*/
|
|
474
|
+
strategy: 'queue' | 'drop' | 'error'
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export interface SemanticCondition {
|
|
478
|
+
/**
|
|
479
|
+
* JEXL expression that must evaluate to true for execution.
|
|
480
|
+
* Has access to entity data, user context, and semantic field references.
|
|
481
|
+
*
|
|
482
|
+
* Available context variables:
|
|
483
|
+
* - `entity`: The entity data being processed
|
|
484
|
+
* - `user`: Current user context (if authenticated)
|
|
485
|
+
* - `operation`: The current operation (Create, Update, etc.)
|
|
486
|
+
* - `semantics`: Object with semantic field accessors
|
|
487
|
+
*
|
|
488
|
+
* Semantic field references use dot notation:
|
|
489
|
+
* - `semantics.CreatedTimestamp` - field with CreatedTimestamp semantic
|
|
490
|
+
* - `semantics.Password` - field with Password semantic
|
|
491
|
+
* - `semantics.User` - entity with User semantic (for associations)
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* // Only set timestamp if not already provided
|
|
495
|
+
* "semantics.CreatedTimestamp == null"
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* // Only apply to authenticated users
|
|
499
|
+
* "user != null && user.authenticated == true"
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* // Only hash password if it's a plain text (not already hashed)
|
|
503
|
+
* "entity[semantics.Password] != null && !entity[semantics.Password].startsWith('$')"
|
|
504
|
+
*
|
|
505
|
+
* @example
|
|
506
|
+
* // Only generate slug if title exists
|
|
507
|
+
* "entity[semantics.Title] != null && entity[semantics.Title].length > 0"
|
|
508
|
+
*/
|
|
509
|
+
expression: string
|
|
510
|
+
/**
|
|
511
|
+
* Description of when this semantic should run.
|
|
512
|
+
*/
|
|
513
|
+
description: string
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export interface SemanticAudit {
|
|
517
|
+
/**
|
|
518
|
+
* Whether to log semantic execution.
|
|
519
|
+
*/
|
|
520
|
+
logExecution: boolean
|
|
521
|
+
/**
|
|
522
|
+
* Whether to log input/output data.
|
|
523
|
+
*/
|
|
524
|
+
logData: boolean
|
|
525
|
+
/**
|
|
526
|
+
* Retention period for audit logs in days.
|
|
527
|
+
*/
|
|
528
|
+
retentionDays: number
|
|
330
529
|
}
|
|
331
530
|
|
|
332
531
|
/**
|
|
@@ -450,6 +649,29 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
450
649
|
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
451
650
|
priority: 10, // High priority for security
|
|
452
651
|
canDisable: false, // Security semantics cannot be disabled
|
|
652
|
+
timeoutMs: 2000, // Allow time for hashing
|
|
653
|
+
conditions: [
|
|
654
|
+
{
|
|
655
|
+
expression: 'entity[semantics.Password] != null && entity[semantics.Password].length > 0',
|
|
656
|
+
description: 'Only process when password field has a value',
|
|
657
|
+
},
|
|
658
|
+
],
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
[SemanticType.Username]: {
|
|
662
|
+
id: SemanticType.Username,
|
|
663
|
+
displayName: 'Username',
|
|
664
|
+
scope: SemanticScope.Property,
|
|
665
|
+
description: 'User authentication identifier',
|
|
666
|
+
category: SemanticCategory.Identity,
|
|
667
|
+
applicableDataTypes: ['string'],
|
|
668
|
+
hasConfig: false,
|
|
669
|
+
runtime: {
|
|
670
|
+
timing: SemanticTiming.Before,
|
|
671
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update, SemanticOperation.Read],
|
|
672
|
+
priority: 15, // High priority for authentication
|
|
673
|
+
canDisable: false, // Security semantics cannot be disabled
|
|
674
|
+
timeoutMs: 100, // Fast operation
|
|
453
675
|
},
|
|
454
676
|
},
|
|
455
677
|
[SemanticType.UserRole]: {
|
|
@@ -509,6 +731,13 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
509
731
|
timing: SemanticTiming.Before,
|
|
510
732
|
operations: [SemanticOperation.Create],
|
|
511
733
|
priority: 90,
|
|
734
|
+
timeoutMs: 100, // Very fast operation
|
|
735
|
+
conditions: [
|
|
736
|
+
{
|
|
737
|
+
expression: 'entity[semantics.CreatedTimestamp] == null',
|
|
738
|
+
description: 'Only set timestamp if not already provided',
|
|
739
|
+
},
|
|
740
|
+
],
|
|
512
741
|
},
|
|
513
742
|
},
|
|
514
743
|
[SemanticType.UpdatedTimestamp]: {
|
|
@@ -761,6 +990,27 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
761
990
|
priority: 40, // Validate URL format
|
|
762
991
|
},
|
|
763
992
|
},
|
|
993
|
+
[SemanticType.ClientIPAddress]: {
|
|
994
|
+
id: SemanticType.ClientIPAddress,
|
|
995
|
+
displayName: 'Client IP Address',
|
|
996
|
+
scope: SemanticScope.Property,
|
|
997
|
+
description: 'Automatically populated client IP address',
|
|
998
|
+
category: SemanticCategory.Contact,
|
|
999
|
+
applicableDataTypes: ['string'],
|
|
1000
|
+
hasConfig: false,
|
|
1001
|
+
runtime: {
|
|
1002
|
+
timing: SemanticTiming.Before,
|
|
1003
|
+
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
1004
|
+
priority: 95, // Low priority, populate after other validations
|
|
1005
|
+
timeoutMs: 100, // Very fast operation
|
|
1006
|
+
conditions: [
|
|
1007
|
+
{
|
|
1008
|
+
expression: 'entity[semantics.ClientIPAddress] == null',
|
|
1009
|
+
description: 'Only set IP address if not already provided',
|
|
1010
|
+
},
|
|
1011
|
+
],
|
|
1012
|
+
},
|
|
1013
|
+
},
|
|
764
1014
|
|
|
765
1015
|
//
|
|
766
1016
|
// Classification & Organization
|
|
@@ -778,6 +1028,18 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
|
778
1028
|
timing: SemanticTiming.Before,
|
|
779
1029
|
operations: [SemanticOperation.Create, SemanticOperation.Update],
|
|
780
1030
|
priority: 30, // Generate slug from title, validate uniqueness
|
|
1031
|
+
timeoutMs: 1000,
|
|
1032
|
+
conditions: [
|
|
1033
|
+
{
|
|
1034
|
+
expression: 'entity[semantics.PublicUniqueName] == null || entity[semantics.PublicUniqueName].length == 0',
|
|
1035
|
+
description: 'Only generate slug if not already provided',
|
|
1036
|
+
},
|
|
1037
|
+
// Let's not guess which field is marked as slug source in the `PublicUniqueName` configuration.
|
|
1038
|
+
// {
|
|
1039
|
+
// expression: 'entity[semantics.Title] != null && entity[semantics.Title].length > 0',
|
|
1040
|
+
// description: 'Only generate slug if title field exists and has content',
|
|
1041
|
+
// },
|
|
1042
|
+
],
|
|
781
1043
|
},
|
|
782
1044
|
},
|
|
783
1045
|
[SemanticType.Tags]: {
|
|
@@ -95,7 +95,7 @@ export function addAutoField(entity: DomainEntity, autoField: string): DomainEle
|
|
|
95
95
|
* @param entity The entity to which the ID field will be added.
|
|
96
96
|
* @returns The added primary field property.
|
|
97
97
|
*/
|
|
98
|
-
export function addIdField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
98
|
+
export function addIdField(entity: DomainEntity, info: Partial<IThing> = {}): DomainProperty {
|
|
99
99
|
const existing = findIdField(entity, true)
|
|
100
100
|
if (existing) {
|
|
101
101
|
// If the ID field already exists, return it.
|
|
@@ -121,7 +121,7 @@ export function addIdField(entity: DomainEntity, info: Partial<IThing> = {}): Do
|
|
|
121
121
|
* @param entity The entity to which the email field will be added.
|
|
122
122
|
* @returns The added email field property.
|
|
123
123
|
*/
|
|
124
|
-
export function addEmailField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
124
|
+
export function addEmailField(entity: DomainEntity, info: Partial<IThing> = {}): DomainProperty {
|
|
125
125
|
const existing = findEmailField(entity, true)
|
|
126
126
|
if (existing) {
|
|
127
127
|
return existing
|
|
@@ -144,7 +144,7 @@ export function addEmailField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
|
144
144
|
* @param info Additional information for the password field.
|
|
145
145
|
* @returns The added password field property.
|
|
146
146
|
*/
|
|
147
|
-
export function addPasswordField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
147
|
+
export function addPasswordField(entity: DomainEntity, info: Partial<IThing> = {}): DomainProperty {
|
|
148
148
|
const existing = findPasswordField(entity, true)
|
|
149
149
|
if (existing) {
|
|
150
150
|
return existing
|
|
@@ -164,7 +164,7 @@ export function addPasswordField(entity: DomainEntity, info: Partial<IThing> = {
|
|
|
164
164
|
* The version field is used to track the version of the entity.
|
|
165
165
|
* @returns The added version field property.
|
|
166
166
|
*/
|
|
167
|
-
export function addVersionField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
167
|
+
export function addVersionField(entity: DomainEntity, info: Partial<IThing> = {}): DomainProperty {
|
|
168
168
|
const existing = findVersionField(entity, true)
|
|
169
169
|
if (existing) {
|
|
170
170
|
// If the version field already exists, return it.
|
|
@@ -183,7 +183,7 @@ export function addVersionField(entity: DomainEntity, info: Partial<IThing> = {}
|
|
|
183
183
|
return prop
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
export function addNameField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
186
|
+
export function addNameField(entity: DomainEntity, info: Partial<IThing> = {}): DomainProperty {
|
|
187
187
|
const existing = findNameField(entity, true)
|
|
188
188
|
if (existing) {
|
|
189
189
|
return existing
|
|
@@ -214,7 +214,7 @@ export function addNameField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
|
214
214
|
* @param entity The entity to which the description field will be added.
|
|
215
215
|
* @returns The added description field property.
|
|
216
216
|
*/
|
|
217
|
-
export function addDescriptionField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
217
|
+
export function addDescriptionField(entity: DomainEntity, info: Partial<IThing> = {}): DomainProperty {
|
|
218
218
|
const existing = findDescriptionField(entity, true)
|
|
219
219
|
if (existing) {
|
|
220
220
|
return existing
|
|
@@ -246,7 +246,7 @@ export function addDescriptionField(entity: DomainEntity, info: Partial<IThing>
|
|
|
246
246
|
* @param info Additional information about the field.
|
|
247
247
|
* @returns The added status field property.
|
|
248
248
|
*/
|
|
249
|
-
export function addStatusField(entity: DomainEntity, info: Partial<IThing> = {}):
|
|
249
|
+
export function addStatusField(entity: DomainEntity, info: Partial<IThing> = {}): DomainProperty {
|
|
250
250
|
const existing = findStatusField(entity, true)
|
|
251
251
|
if (existing) {
|
|
252
252
|
return existing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"blog-publishing-platform","name":"Blog Publishing Platform","description":"A comprehensive content management and publishing platform for blogs, magazines, and digital publications. Includes content management, user roles, publishing workflow, and social features.","createdAt":"2025-07-27T21:14:57.328Z","updatedAt":"2025-07-27T21:14:57.328Z","version":"1.0.0","author":"API Now! Core Team","tags":["blog","cms","publishing","content","media","editorial"],"structure":{"domain":{"name":"Blog Publishing Platform","description":"A comprehensive content management and publishing platform for blogs, magazines, and digital publications","totalEntities":8,"totalProperties":68,"totalAssociations":14},"namespaces":[{"name":"ContentManagement","displayName":"Content Management","description":"Core content creation, editing, and organization features","modelCount":2,"entityCount":4,"models":[{"name":"Publications","displayName":"Publications Management","description":"Individual blogs, magazines, or publications within the platform","entityCount":1,"entities":[{"name":"publication","displayName":"Publication","description":"Individual blog or publication site","propertyCount":8,"associationCount":0,"properties":[{"name":"id","displayName":"Publication ID","description":"Unique identifier for the publication","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"name","displayName":"Publication Name","description":"Display name of the publication","type":"string","semantics":["Semantic#Title"]},{"name":"slug","displayName":"URL Slug","description":"URL-friendly identifier for the publication","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]},{"name":"description","displayName":"Description","description":"Publication description and tagline","type":"string","semantics":["Semantic#Description"]},{"name":"domain","displayName":"Custom Domain","description":"Custom domain name for the publication","type":"string","semantics":[]},{"name":"logo_url","displayName":"Logo URL","description":"URL to publication logo image","type":"string","semantics":["Semantic#ImageURL"]},{"name":"status","displayName":"Publication Status","description":"Current status of the publication","type":"string","required":true,"semantics":["Semantic#Status"],"enumValues":["active","suspended","archived"],"defaultValue":"active"},{"name":"created_at","displayName":"Created At","description":"When the publication was created","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[],"semantics":[]}]},{"name":"Content","displayName":"Content Management","description":"Posts, pages, and other content types","entityCount":3,"entities":[{"name":"category","displayName":"Content Category","description":"Content categorization for organization and navigation","propertyCount":5,"associationCount":2,"properties":[{"name":"id","displayName":"Category ID","description":"Unique identifier for the category","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"name","displayName":"Category Name","description":"Display name of the category","type":"string","semantics":["Semantic#Title"]},{"name":"slug","displayName":"URL Slug","description":"URL-friendly identifier for the category","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]},{"name":"description","displayName":"Description","description":"Category description","type":"string","semantics":["Semantic#Description"]},{"name":"color","displayName":"Color","description":"Theme color for the category","type":"string","semantics":[]}],"associations":[{"name":"publication","displayName":"Publication","description":"Publication this category belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"},{"name":"parentCategory","displayName":"Parent Category","description":"Parent category for hierarchical organization","required":false,"multiple":false,"targetEntities":["category"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]},{"name":"Tag","displayName":"Content Tag","description":"Tags for flexible content labeling and discovery","propertyCount":3,"associationCount":1,"properties":[{"name":"id","displayName":"Tag ID","description":"Unique identifier for the tag","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"name","displayName":"Tag Name","description":"Display name of the tag","type":"string","semantics":["Semantic#Title"]},{"name":"slug","displayName":"URL Slug","description":"URL-friendly identifier for the tag","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]}],"associations":[{"name":"publication","displayName":"Publication","description":"Publication this tag belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]},{"name":"Post","displayName":"Blog Post","description":"Individual blog posts and articles","propertyCount":16,"associationCount":4,"properties":[{"name":"id","displayName":"Post ID","description":"Unique identifier for the post","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"title","displayName":"Post Title","description":"Title of the blog post","type":"string","required":true,"semantics":["Semantic#Title"]},{"name":"slug","displayName":"URL Slug","description":"URL-friendly identifier for the post","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]},{"name":"excerpt","displayName":"Excerpt","description":"Brief summary or excerpt of the post","type":"string","semantics":["Semantic#Summary"]},{"name":"content","displayName":"Content","description":"Full content of the post in HTML or Markdown","type":"string","required":true,"semantics":["Semantic#HTML"]},{"name":"content_format","displayName":"Content Format","description":"Format of the content (HTML, Markdown, etc.)","type":"string","required":true,"semantics":[],"enumValues":["html","markdown","richtext"],"defaultValue":"markdown"},{"name":"featured_image_url","displayName":"Featured Image URL","description":"URL to featured image","type":"string","semantics":["Semantic#ImageURL"]},{"name":"status","displayName":"Post Status","description":"Current publishing status of the post","type":"string","required":true,"semantics":["Semantic#Status"],"enumValues":["draft","pending_review","scheduled","published","archived"],"defaultValue":"draft"},{"name":"published_at","displayName":"Published At","description":"When the post was published","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]},{"name":"scheduled_at","displayName":"Scheduled At","description":"When the post is scheduled to be published","type":"datetime","semantics":[]},{"name":"view_count","displayName":"View Count","description":"Number of times the post has been viewed","type":"number","required":true,"semantics":[]},{"name":"reading_time","displayName":"Reading Time","description":"Estimated reading time in minutes","type":"number","readOnly":true,"semantics":["Semantic#Calculated"]},{"name":"word_count","displayName":"Word Count","description":"Number of words in the post content","type":"number","readOnly":true,"semantics":["Semantic#Calculated"]},{"name":"meta_title","displayName":"Meta Title","description":"SEO meta title","type":"string","semantics":[]},{"name":"meta_description","displayName":"Meta Description","description":"SEO meta description","type":"string","semantics":[]},{"name":"updated_at","displayName":"Updated At","description":"When the post was last updated","type":"datetime","readOnly":true,"semantics":["Semantic#UpdatedTimestamp"]}],"associations":[{"name":"publication","displayName":"Publication","description":"Publication this post belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"},{"name":"categories","displayName":"Post Categories","description":"Categories this post belongs to","required":false,"multiple":true,"targetEntities":["category"],"semantics":["Semantic#Categories"],"cardinality":"One-to-Many"},{"name":"tags","displayName":"Post Tags","description":"Tags associated with this post","required":false,"multiple":true,"targetEntities":["Tag"],"semantics":["Semantic#Tags"],"cardinality":"One-to-Many"},{"name":"author","displayName":"Post Author","description":"Author who wrote this post","required":true,"multiple":false,"targetEntities":["user"],"semantics":["Semantic#ResourceOwnerIdentifier"],"cardinality":"One-to-One"}],"semantics":[]}]}]},{"name":"UserManagement","displayName":"User Management","description":"Authors, editors, subscribers, and user roles management","modelCount":1,"entityCount":1,"models":[{"name":"Users","displayName":"User Management","description":"User accounts and authentication","entityCount":1,"entities":[{"name":"user","displayName":"User Account","description":"User account for authors, editors, and subscribers","propertyCount":13,"associationCount":0,"properties":[{"name":"id","displayName":"User ID","description":"Unique identifier for the user","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"email","displayName":"Email Address","description":"User email address for login and communication","type":"string","required":true,"semantics":["Semantic#Email"]},{"name":"password","displayName":"Password","description":"Encrypted password for authentication","type":"string","required":true,"semantics":["Semantic#Password"]},{"name":"username","displayName":"Username","description":"Unique username for the user","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]},{"name":"display_name","displayName":"Display Name","description":"Public display name for the user","type":"string","required":true,"semantics":[]},{"name":"first_name","displayName":"First Name","description":"User first name","type":"string","semantics":[]},{"name":"last_name","displayName":"Last Name","description":"User last name","type":"string","semantics":[]},{"name":"bio","displayName":"Biography","description":"User biography and description","type":"string","semantics":["Semantic#Description"]},{"name":"avatar_url","displayName":"Avatar URL","description":"URL to user profile picture","type":"string","semantics":["Semantic#ImageURL"]},{"name":"website","displayName":"Website","description":"User personal website URL","type":"string","semantics":["Semantic#URL"]},{"name":"role","displayName":"User Role","description":"User role for permission management","type":"string","required":true,"semantics":["Semantic#Status"],"enumValues":["subscriber","author","editor","admin","super_admin"],"defaultValue":"subscriber"},{"name":"emailVerified","displayName":"Email Verified","description":"Whether the user has verified their email","type":"boolean","required":true,"semantics":[],"defaultValue":"false"},{"name":"created_at","displayName":"Created At","description":"When the user account was created","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[],"semantics":["Semantic#User"]}]}]},{"name":"SocialFeatures","displayName":"Social Features","description":"Comments, likes, shares, and social interactions","modelCount":1,"entityCount":1,"models":[{"name":"Comments","displayName":"Comment System","description":"Post comments and replies","entityCount":1,"entities":[{"name":"comment","displayName":"Comment","description":"User comments on posts","propertyCount":7,"associationCount":3,"properties":[{"name":"id","displayName":"Comment ID","description":"Unique identifier for the comment","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"content","displayName":"Comment Content","description":"Content of the comment","type":"string","required":true,"semantics":[]},{"name":"author_name","displayName":"Author Name","description":"Name of the comment author (for guest comments)","type":"string","semantics":[]},{"name":"author_email","displayName":"Author Email","description":"Email of the comment author (for guest comments)","type":"string","required":true,"semantics":["Semantic#Email"]},{"name":"status","displayName":"Comment Status","description":"Moderation status of the comment","type":"string","required":true,"semantics":["Semantic#Status"],"enumValues":["pending","approved","rejected","spam"],"defaultValue":"pending"},{"name":"user_agent","displayName":"User Agent","description":"Browser user agent string","type":"string","semantics":[]},{"name":"created_at","displayName":"Created At","description":"When the comment was created","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[{"name":"post","displayName":"Post","description":"Post this comment belongs to","required":true,"multiple":false,"targetEntities":["Post"],"semantics":[],"cardinality":"One-to-One"},{"name":"author","displayName":"Comment Author","description":"Registered user who wrote this comment","required":false,"multiple":false,"targetEntities":["user"],"semantics":["Semantic#ResourceOwnerIdentifier"],"cardinality":"One-to-One"},{"name":"parent_comment","displayName":"Parent Comment","description":"Parent comment for replies","required":false,"multiple":false,"targetEntities":["comment"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]}]}]},{"name":"Analytics","displayName":"Analytics & Tracking","description":"Analytics, metrics, and performance tracking","modelCount":1,"entityCount":1,"models":[{"name":"Analytics","displayName":"Content Analytics","description":"Content performance and user engagement metrics","entityCount":1,"entities":[{"name":"page_view","displayName":"Page View","description":"Individual page view tracking record","propertyCount":6,"associationCount":2,"properties":[{"name":"id","displayName":"Page View ID","description":"Unique identifier for the page view","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"path","displayName":"Page Path","description":"URL path of the viewed page","type":"string","required":true,"semantics":[]},{"name":"referrer","displayName":"Referrer","description":"Referring URL","type":"string","semantics":["Semantic#URL"]},{"name":"user_agent","displayName":"User Agent","description":"Browser user agent string","type":"string","semantics":[]},{"name":"session_id","displayName":"Session ID","description":"Visitor session identifier","type":"string","semantics":[]},{"name":"viewed_at","displayName":"Viewed At","description":"When the page was viewed","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[{"name":"post","displayName":"Viewed Post","description":"Post that was viewed (if applicable)","required":false,"multiple":false,"targetEntities":["Post"],"semantics":[],"cardinality":"One-to-One"},{"name":"publication","displayName":"Publication","description":"Publication this page view belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]}]}]},{"name":"MediaManagement","displayName":"Media Management","description":"Image, video, and file upload management","modelCount":1,"entityCount":1,"models":[{"name":"Media","displayName":"Media Library","description":"Uploaded files, images, and media assets","entityCount":1,"entities":[{"name":"media_file","displayName":"Media File","description":"Uploaded media files (images, videos, documents)","propertyCount":10,"associationCount":2,"properties":[{"name":"id","displayName":"Media File ID","description":"Unique identifier for the media file","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"filename","displayName":"File Name","description":"Original filename of the uploaded file","type":"string","required":true,"semantics":[]},{"name":"storage_key","displayName":"Storage Key","description":"Unique storage key for the file","type":"string","required":true,"unique":true,"semantics":[]},{"name":"url","displayName":"File URL","description":"Public URL to access the file","type":"string","required":true,"semantics":["Semantic#URL"]},{"name":"mime_type","displayName":"MIME Type","description":"MIME type of the file","type":"string","required":true,"semantics":[]},{"name":"file_size","displayName":"File Size","description":"File size in bytes","type":"number","required":true,"semantics":[]},{"name":"width","displayName":"Image Width","description":"Width in pixels (for images)","type":"number","required":true,"semantics":[]},{"name":"height","displayName":"Image Height","description":"Height in pixels (for images)","type":"number","required":true,"semantics":[]},{"name":"alt_text","displayName":"Alt Text","description":"Alternative text for accessibility","type":"string","semantics":[]},{"name":"uploaded_at","displayName":"Uploaded At","description":"When the file was uploaded","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[{"name":"uploader","displayName":"File Uploader","description":"User who uploaded this file","required":true,"multiple":false,"targetEntities":["user"],"semantics":["Semantic#ResourceOwnerIdentifier"],"cardinality":"One-to-One"},{"name":"publication","displayName":"Publication","description":"Publication this media file belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]}]}]}]}}
|
|
1
|
+
{"id":"blog-publishing-platform","name":"Blog Publishing Platform","description":"A comprehensive content management and publishing platform for blogs, magazines, and digital publications. Includes content management, user roles, publishing workflow, and social features.","createdAt":"2025-07-27T21:14:57.328Z","updatedAt":"2025-07-27T21:14:57.328Z","version":"1.0.0","author":"API Now! Core Team","tags":["blog","cms","publishing","content","media","editorial"],"structure":{"domain":{"name":"Blog Publishing Platform","description":"A comprehensive content management and publishing platform for blogs, magazines, and digital publications","totalEntities":8,"totalProperties":70,"totalAssociations":14},"namespaces":[{"name":"ContentManagement","displayName":"Content Management","description":"Core content creation, editing, and organization features","modelCount":2,"entityCount":4,"models":[{"name":"Publications","displayName":"Publications Management","description":"Individual blogs, magazines, or publications within the platform","entityCount":1,"entities":[{"name":"publication","displayName":"Publication","description":"Individual blog or publication site","propertyCount":8,"associationCount":0,"properties":[{"name":"id","displayName":"Publication ID","description":"Unique identifier for the publication","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"name","displayName":"Publication Name","description":"Display name of the publication","type":"string","semantics":["Semantic#Title"]},{"name":"slug","displayName":"URL Slug","description":"URL-friendly identifier for the publication","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]},{"name":"description","displayName":"Description","description":"Publication description and tagline","type":"string","semantics":["Semantic#Description"]},{"name":"domain","displayName":"Custom Domain","description":"Custom domain name for the publication","type":"string","semantics":[]},{"name":"logo_url","displayName":"Logo URL","description":"URL to publication logo image","type":"string","semantics":["Semantic#ImageURL"]},{"name":"status","displayName":"Publication Status","description":"Current status of the publication","type":"string","required":true,"semantics":["Semantic#Status"],"enumValues":["active","suspended","archived"],"defaultValue":"active"},{"name":"created_at","displayName":"Created At","description":"When the publication was created","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[],"semantics":[]}]},{"name":"Content","displayName":"Content Management","description":"Posts, pages, and other content types","entityCount":3,"entities":[{"name":"category","displayName":"Content Category","description":"Content categorization for organization and navigation","propertyCount":5,"associationCount":2,"properties":[{"name":"id","displayName":"Category ID","description":"Unique identifier for the category","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"name","displayName":"Category Name","description":"Display name of the category","type":"string","semantics":["Semantic#Title"]},{"name":"slug","displayName":"URL Slug","description":"URL-friendly identifier for the category","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]},{"name":"description","displayName":"Description","description":"Category description","type":"string","semantics":["Semantic#Description"]},{"name":"color","displayName":"Color","description":"Theme color for the category","type":"string","semantics":[]}],"associations":[{"name":"publication","displayName":"Publication","description":"Publication this category belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"},{"name":"parentCategory","displayName":"Parent Category","description":"Parent category for hierarchical organization","required":false,"multiple":false,"targetEntities":["category"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]},{"name":"Tag","displayName":"Content Tag","description":"Tags for flexible content labeling and discovery","propertyCount":3,"associationCount":1,"properties":[{"name":"id","displayName":"Tag ID","description":"Unique identifier for the tag","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"name","displayName":"Tag Name","description":"Display name of the tag","type":"string","semantics":["Semantic#Title"]},{"name":"slug","displayName":"URL Slug","description":"URL-friendly identifier for the tag","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]}],"associations":[{"name":"publication","displayName":"Publication","description":"Publication this tag belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]},{"name":"Post","displayName":"Blog Post","description":"Individual blog posts and articles","propertyCount":16,"associationCount":4,"properties":[{"name":"id","displayName":"Post ID","description":"Unique identifier for the post","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"title","displayName":"Post Title","description":"Title of the blog post","type":"string","required":true,"semantics":["Semantic#Title"]},{"name":"slug","displayName":"URL Slug","description":"URL-friendly identifier for the post","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]},{"name":"excerpt","displayName":"Excerpt","description":"Brief summary or excerpt of the post","type":"string","semantics":["Semantic#Summary"]},{"name":"content","displayName":"Content","description":"Full content of the post in HTML or Markdown","type":"string","required":true,"semantics":["Semantic#HTML"]},{"name":"content_format","displayName":"Content Format","description":"Format of the content (HTML, Markdown, etc.)","type":"string","required":true,"semantics":[],"enumValues":["html","markdown","richtext"],"defaultValue":"markdown"},{"name":"featured_image_url","displayName":"Featured Image URL","description":"URL to featured image","type":"string","semantics":["Semantic#ImageURL"]},{"name":"status","displayName":"Post Status","description":"Current publishing status of the post","type":"string","required":true,"semantics":["Semantic#Status"],"enumValues":["draft","pending_review","scheduled","published","archived"],"defaultValue":"draft"},{"name":"published_at","displayName":"Published At","description":"When the post was published","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]},{"name":"scheduled_at","displayName":"Scheduled At","description":"When the post is scheduled to be published","type":"datetime","semantics":[]},{"name":"view_count","displayName":"View Count","description":"Number of times the post has been viewed","type":"number","required":true,"semantics":[]},{"name":"reading_time","displayName":"Reading Time","description":"Estimated reading time in minutes","type":"number","readOnly":true,"semantics":["Semantic#Calculated"]},{"name":"word_count","displayName":"Word Count","description":"Number of words in the post content","type":"number","readOnly":true,"semantics":["Semantic#Calculated"]},{"name":"meta_title","displayName":"Meta Title","description":"SEO meta title","type":"string","semantics":[]},{"name":"meta_description","displayName":"Meta Description","description":"SEO meta description","type":"string","semantics":[]},{"name":"updated_at","displayName":"Updated At","description":"When the post was last updated","type":"datetime","readOnly":true,"semantics":["Semantic#UpdatedTimestamp"]}],"associations":[{"name":"publication","displayName":"Publication","description":"Publication this post belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"},{"name":"categories","displayName":"Post Categories","description":"Categories this post belongs to","required":false,"multiple":true,"targetEntities":["category"],"semantics":["Semantic#Categories"],"cardinality":"One-to-Many"},{"name":"tags","displayName":"Post Tags","description":"Tags associated with this post","required":false,"multiple":true,"targetEntities":["Tag"],"semantics":["Semantic#Tags"],"cardinality":"One-to-Many"},{"name":"author","displayName":"Post Author","description":"Author who wrote this post","required":true,"multiple":false,"targetEntities":["user"],"semantics":["Semantic#ResourceOwnerIdentifier"],"cardinality":"One-to-One"}],"semantics":[]}]}]},{"name":"UserManagement","displayName":"User Management","description":"Authors, editors, subscribers, and user roles management","modelCount":1,"entityCount":1,"models":[{"name":"Users","displayName":"User Management","description":"User accounts and authentication","entityCount":1,"entities":[{"name":"user","displayName":"User Account","description":"User account for authors, editors, and subscribers","propertyCount":14,"associationCount":0,"properties":[{"name":"id","displayName":"User ID","description":"Unique identifier for the user","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"email","displayName":"Email Address","description":"User email address for login and communication","type":"string","required":true,"semantics":["Semantic#Email"]},{"name":"password","displayName":"Password","description":"Encrypted password for authentication","type":"string","required":true,"semantics":["Semantic#Password"]},{"name":"username","displayName":"Username","description":"Unique username for the user","type":"string","required":true,"unique":true,"semantics":["Semantic#PublicUniqueName"]},{"name":"display_name","displayName":"Display Name","description":"Public display name for the user","type":"string","required":true,"semantics":[]},{"name":"first_name","displayName":"First Name","description":"User first name","type":"string","semantics":[]},{"name":"last_name","displayName":"Last Name","description":"User last name","type":"string","semantics":[]},{"name":"bio","displayName":"Biography","description":"User biography and description","type":"string","semantics":["Semantic#Description"]},{"name":"avatar_url","displayName":"Avatar URL","description":"URL to user profile picture","type":"string","semantics":["Semantic#ImageURL"]},{"name":"website","displayName":"Website","description":"User personal website URL","type":"string","semantics":["Semantic#URL"]},{"name":"role","displayName":"User Role","description":"User role for permission management","type":"string","required":true,"semantics":["Semantic#UserRole"],"enumValues":["subscriber","author","editor","admin","super_admin"],"defaultValue":"subscriber"},{"name":"status","displayName":"User Status","description":"Current status of the user account","type":"string","required":true,"semantics":["Semantic#Status"],"enumValues":["active","inactive","suspended","pending_verification"],"defaultValue":"active"},{"name":"emailVerified","displayName":"Email Verified","description":"Whether the user has verified their email","type":"boolean","required":true,"semantics":[],"defaultValue":"false"},{"name":"created_at","displayName":"Created At","description":"When the user account was created","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[],"semantics":["Semantic#User"]}]}]},{"name":"SocialFeatures","displayName":"Social Features","description":"Comments, likes, shares, and social interactions","modelCount":1,"entityCount":1,"models":[{"name":"Comments","displayName":"Comment System","description":"Post comments and replies","entityCount":1,"entities":[{"name":"comment","displayName":"Comment","description":"User comments on posts","propertyCount":7,"associationCount":3,"properties":[{"name":"id","displayName":"Comment ID","description":"Unique identifier for the comment","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"content","displayName":"Comment Content","description":"Content of the comment","type":"string","required":true,"semantics":[]},{"name":"author_name","displayName":"Author Name","description":"Name of the comment author (for guest comments)","type":"string","semantics":[]},{"name":"author_email","displayName":"Author Email","description":"Email of the comment author (for guest comments)","type":"string","required":true,"semantics":["Semantic#Email"]},{"name":"status","displayName":"Comment Status","description":"Moderation status of the comment","type":"string","required":true,"semantics":["Semantic#Status"],"enumValues":["pending","approved","rejected","spam"],"defaultValue":"pending"},{"name":"user_agent","displayName":"User Agent","description":"Browser user agent string","type":"string","semantics":[]},{"name":"created_at","displayName":"Created At","description":"When the comment was created","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[{"name":"post","displayName":"Post","description":"Post this comment belongs to","required":true,"multiple":false,"targetEntities":["Post"],"semantics":[],"cardinality":"One-to-One"},{"name":"author","displayName":"Comment Author","description":"Registered user who wrote this comment","required":false,"multiple":false,"targetEntities":["user"],"semantics":["Semantic#ResourceOwnerIdentifier"],"cardinality":"One-to-One"},{"name":"parent_comment","displayName":"Parent Comment","description":"Parent comment for replies","required":false,"multiple":false,"targetEntities":["comment"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]}]}]},{"name":"Analytics","displayName":"Analytics & Tracking","description":"Analytics, metrics, and performance tracking","modelCount":1,"entityCount":1,"models":[{"name":"Analytics","displayName":"Content Analytics","description":"Content performance and user engagement metrics","entityCount":1,"entities":[{"name":"page_view","displayName":"Page View","description":"Individual page view tracking record","propertyCount":7,"associationCount":2,"properties":[{"name":"id","displayName":"Page View ID","description":"Unique identifier for the page view","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"path","displayName":"Page Path","description":"URL path of the viewed page","type":"string","required":true,"semantics":[]},{"name":"referrer","displayName":"Referrer","description":"Referring URL","type":"string","semantics":["Semantic#URL"]},{"name":"ip_address","displayName":"IP Address","description":"Visitor IP address","type":"string","semantics":["Semantic#ClientIPAddress"]},{"name":"user_agent","displayName":"User Agent","description":"Browser user agent string","type":"string","semantics":[]},{"name":"session_id","displayName":"Session ID","description":"Visitor session identifier","type":"string","semantics":[]},{"name":"viewed_at","displayName":"Viewed At","description":"When the page was viewed","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[{"name":"post","displayName":"Viewed Post","description":"Post that was viewed (if applicable)","required":false,"multiple":false,"targetEntities":["Post"],"semantics":[],"cardinality":"One-to-One"},{"name":"publication","displayName":"Publication","description":"Publication this page view belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]}]}]},{"name":"MediaManagement","displayName":"Media Management","description":"Image, video, and file upload management","modelCount":1,"entityCount":1,"models":[{"name":"Media","displayName":"Media Library","description":"Uploaded files, images, and media assets","entityCount":1,"entities":[{"name":"media_file","displayName":"Media File","description":"Uploaded media files (images, videos, documents)","propertyCount":10,"associationCount":2,"properties":[{"name":"id","displayName":"Media File ID","description":"Unique identifier for the media file","type":"string","primary":true,"readOnly":true,"semantics":[]},{"name":"filename","displayName":"File Name","description":"Original filename of the uploaded file","type":"string","required":true,"semantics":[]},{"name":"storage_key","displayName":"Storage Key","description":"Unique storage key for the file","type":"string","required":true,"unique":true,"semantics":[]},{"name":"url","displayName":"File URL","description":"Public URL to access the file","type":"string","required":true,"semantics":["Semantic#URL"]},{"name":"mime_type","displayName":"MIME Type","description":"MIME type of the file","type":"string","required":true,"semantics":[]},{"name":"file_size","displayName":"File Size","description":"File size in bytes","type":"number","required":true,"semantics":[]},{"name":"width","displayName":"Image Width","description":"Width in pixels (for images)","type":"number","required":true,"semantics":[]},{"name":"height","displayName":"Image Height","description":"Height in pixels (for images)","type":"number","required":true,"semantics":[]},{"name":"alt_text","displayName":"Alt Text","description":"Alternative text for accessibility","type":"string","semantics":[]},{"name":"uploaded_at","displayName":"Uploaded At","description":"When the file was uploaded","type":"datetime","readOnly":true,"semantics":["Semantic#CreatedTimestamp"]}],"associations":[{"name":"uploader","displayName":"File Uploader","description":"User who uploaded this file","required":true,"multiple":false,"targetEntities":["user"],"semantics":["Semantic#ResourceOwnerIdentifier"],"cardinality":"One-to-One"},{"name":"publication","displayName":"Publication","description":"Publication this media file belongs to","required":true,"multiple":false,"targetEntities":["publication"],"semantics":[],"cardinality":"One-to-One"}],"semantics":[]}]}]}]}}
|