@api-client/core 0.14.1 → 0.14.3

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 (96) hide show
  1. package/build/src/browser.d.ts +1 -1
  2. package/build/src/browser.d.ts.map +1 -1
  3. package/build/src/browser.js.map +1 -1
  4. package/build/src/index.d.ts +2 -1
  5. package/build/src/index.d.ts.map +1 -1
  6. package/build/src/index.js +1 -0
  7. package/build/src/index.js.map +1 -1
  8. package/build/src/modeling/ApiFile.d.ts +23 -0
  9. package/build/src/modeling/ApiFile.d.ts.map +1 -0
  10. package/build/src/modeling/ApiFile.js +44 -0
  11. package/build/src/modeling/ApiFile.js.map +1 -0
  12. package/build/src/modeling/ApiModel.d.ts +159 -0
  13. package/build/src/modeling/ApiModel.d.ts.map +1 -0
  14. package/build/src/modeling/ApiModel.js +237 -0
  15. package/build/src/modeling/ApiModel.js.map +1 -0
  16. package/build/src/modeling/DataDomain.d.ts +1 -1
  17. package/build/src/modeling/DataDomain.d.ts.map +1 -1
  18. package/build/src/modeling/DataDomain.js +1 -3
  19. package/build/src/modeling/DataDomain.js.map +1 -1
  20. package/build/src/modeling/DomainAssociation.d.ts +35 -0
  21. package/build/src/modeling/DomainAssociation.d.ts.map +1 -1
  22. package/build/src/modeling/DomainAssociation.js +42 -5
  23. package/build/src/modeling/DomainAssociation.js.map +1 -1
  24. package/build/src/modeling/DomainEntity.js +1 -1
  25. package/build/src/modeling/DomainEntity.js.map +1 -1
  26. package/build/src/modeling/DomainFile.d.ts +1 -2
  27. package/build/src/modeling/DomainFile.d.ts.map +1 -1
  28. package/build/src/modeling/DomainFile.js +3 -41
  29. package/build/src/modeling/DomainFile.js.map +1 -1
  30. package/build/src/modeling/Semantics.d.ts +62 -7
  31. package/build/src/modeling/Semantics.d.ts.map +1 -1
  32. package/build/src/modeling/Semantics.js +76 -7
  33. package/build/src/modeling/Semantics.js.map +1 -1
  34. package/build/src/modeling/amf/ShapeGenerator.d.ts.map +1 -1
  35. package/build/src/modeling/amf/ShapeGenerator.js.map +1 -1
  36. package/build/src/modeling/types.d.ts +491 -0
  37. package/build/src/modeling/types.d.ts.map +1 -1
  38. package/build/src/modeling/types.js.map +1 -1
  39. package/build/src/models/kinds.d.ts +2 -0
  40. package/build/src/models/kinds.d.ts.map +1 -1
  41. package/build/src/models/kinds.js +2 -0
  42. package/build/src/models/kinds.js.map +1 -1
  43. package/build/src/models/store/File.d.ts +19 -2
  44. package/build/src/models/store/File.d.ts.map +1 -1
  45. package/build/src/models/store/File.js +100 -13
  46. package/build/src/models/store/File.js.map +1 -1
  47. package/build/tsconfig.tsbuildinfo +1 -1
  48. package/data/models/APIC-187.json +3 -3
  49. package/data/models/APIC-188.json +3 -3
  50. package/data/models/APIC-233.json +1 -1
  51. package/data/models/APIC-391.json +2 -2
  52. package/data/models/APIC-483.json +1 -1
  53. package/data/models/APIC-487.json +1 -1
  54. package/data/models/APIC-655.json +1 -1
  55. package/data/models/APIC-689.json +1 -1
  56. package/data/models/APIC-690.json +5 -5
  57. package/data/models/SE-10469.json +1 -1
  58. package/data/models/SE-13092.json +5 -5
  59. package/data/models/SE-22063.json +12 -2
  60. package/data/models/amf-helper-api.json +154 -14
  61. package/data/models/arc-demo-api.json +95 -15
  62. package/data/models/async-api.json +1 -1
  63. package/data/models/example-generator-api.json +361 -21
  64. package/data/models/expanded-api.json +1 -1
  65. package/data/models/flattened-api.json +1 -1
  66. package/data/models/multiple-servers.json +1 -1
  67. package/data/models/oas-3-api.json +1 -1
  68. package/data/models/oas-date.json +1 -1
  69. package/data/models/oas-types.json +1 -1
  70. package/data/models/oas-unions.json +1 -1
  71. package/data/models/petstore.json +1 -1
  72. package/data/models/raml-date.json +1 -1
  73. package/data/models/recursive.json +1 -1
  74. package/data/models/schema-api.json +62 -2
  75. package/data/models/secured-api.json +16 -16
  76. package/data/models/tracked-to-linked.json +4 -4
  77. package/package.json +3 -4
  78. package/src/modeling/ApiFile.ts +53 -0
  79. package/src/modeling/ApiModel.ts +327 -0
  80. package/src/modeling/DataDomain.ts +1 -1
  81. package/src/modeling/DomainAssociation.ts +56 -0
  82. package/src/modeling/DomainEntity.ts +1 -1
  83. package/src/modeling/DomainFile.ts +3 -40
  84. package/src/modeling/Semantics.ts +79 -7
  85. package/src/modeling/amf/ShapeGenerator.ts +1 -1
  86. package/src/modeling/types.ts +545 -0
  87. package/src/models/kinds.ts +2 -0
  88. package/src/models/store/File.ts +100 -13
  89. package/tests/unit/modeling/api_model.spec.ts +291 -0
  90. package/tests/unit/modeling/domain_asociation.spec.ts +92 -2
  91. package/tests/unit/modeling/domain_entity.spec.ts +15 -15
  92. package/tests/unit/modeling/domain_file.spec.ts +1 -11
  93. package/tests/unit/modeling/domain_model_entities.spec.ts +2 -2
  94. package/tests/unit/modeling/semantics.spec.ts +146 -0
  95. package/tests/unit/models/File/constructor.spec.ts +3 -2
  96. package/tests/unit/models/File/shortcutTo.spec.ts +1 -1
@@ -0,0 +1,327 @@
1
+ import { nanoid } from '../nanoid.js'
2
+ import { ApiModelKind } from '../models/kinds.js'
3
+ import { type IThing, Thing } from '../models/Thing.js'
4
+ import type {
5
+ AccessRule,
6
+ AuthenticationConfiguration,
7
+ AuthorizationConfiguration,
8
+ ExposedEntity,
9
+ ForeignDomainDependency,
10
+ RateLimitingConfiguration,
11
+ SessionConfiguration,
12
+ } from './types.js'
13
+ import { DataDomain, type DataDomainSchema } from './DataDomain.js'
14
+
15
+ export interface ApiModelSchema {
16
+ /**
17
+ * The data domain kind recognizable by the ecosystem.
18
+ */
19
+ kind: typeof ApiModelKind
20
+ /**
21
+ * The unique key of the data domain schema.
22
+ * This is a stable identifier that does not change across versions.
23
+ */
24
+ key: string
25
+ /**
26
+ * Contains the name, display name, description, and the version of the data domain schema.
27
+ */
28
+ info: IThing
29
+
30
+ /**
31
+ * The designated Data Entity that represents a "User".
32
+ * This entity must be marked with the "User" semantic in the Data Modeler.
33
+ *
34
+ * This property is required to publish the API.
35
+ */
36
+ userKey?: string
37
+
38
+ /**
39
+ * Reference to the stable, version-controlled data definition from the
40
+ * Data Catalog. When not set, the model cannot be published.
41
+ */
42
+ domain?: ForeignDomainDependency
43
+
44
+ /**
45
+ * Configuration for how users prove their identity.
46
+ * The API model is invalid if this is not set.
47
+ */
48
+ authentication?: AuthenticationConfiguration
49
+
50
+ /**
51
+ * Configuration for what authenticated users are allowed to do.
52
+ * The API model is invalid if this is not set.
53
+ */
54
+ authorization?: AuthorizationConfiguration
55
+
56
+ /**
57
+ * Configuration for the transport and payload of the user session.
58
+ * The API model is invalid if this is not set.
59
+ */
60
+ session?: SessionConfiguration
61
+ /**
62
+ * The specific subset of Data Entities to be exposed by this API.
63
+ * These are the entities that are included in the data domain schema.
64
+ */
65
+ exposes: ExposedEntity[]
66
+
67
+ /**
68
+ * Optional array of access rules that define the access control policies
69
+ * for the API. These rules are used to enforce security and permissions
70
+ * on the exposed entities.
71
+ *
72
+ * These rules apply to all exposed entities and actions. An API action
73
+ * can declare its own access rules, which will override these.
74
+ */
75
+ accessRule?: AccessRule[]
76
+ /**
77
+ * Optional configuration for API-wide rate limiting and throttling.
78
+ * Defines rules to protect the API from overuse.
79
+ */
80
+ rateLimiting?: RateLimitingConfiguration
81
+ }
82
+
83
+ export class ApiModel extends EventTarget {
84
+ /**
85
+ * The data domain kind recognizable by the ecosystem.
86
+ */
87
+ kind: typeof ApiModelKind
88
+ /**
89
+ * The unique key of the data domain schema.
90
+ * This is a stable identifier that does not change across versions.
91
+ */
92
+ key: string
93
+
94
+ /**
95
+ * The description of the domain property.
96
+ */
97
+ info: Thing
98
+ /**
99
+ * The designated Data Entity that represents a "User".
100
+ * This entity must be marked with the "User" semantic in the Data Modeler.
101
+ *
102
+ * This property is required to publish the API.
103
+ */
104
+ userKey?: string
105
+
106
+ /**
107
+ * Reference to the stable, version-controlled data definition from the
108
+ * Data Catalog. When not set, the model cannot be published.
109
+ */
110
+ domain?: ForeignDomainDependency
111
+
112
+ /**
113
+ * Configuration for how users prove their identity.
114
+ * The API model is invalid if this is not set.
115
+ */
116
+ authentication?: AuthenticationConfiguration
117
+
118
+ /**
119
+ * Configuration for what authenticated users are allowed to do.
120
+ * The API model is invalid if this is not set.
121
+ */
122
+ authorization?: AuthorizationConfiguration
123
+
124
+ /**
125
+ * Configuration for the transport and payload of the user session.
126
+ * The API model is invalid if this is not set.
127
+ */
128
+ session?: SessionConfiguration
129
+ /**
130
+ * The specific subset of Data Entities to be exposed by this API.
131
+ * These are the entities that are included in the data domain schema.
132
+ */
133
+ exposes: ExposedEntity[]
134
+ /**
135
+ * Optional array of access rules that define the access control policies
136
+ * for the API. These rules are used to enforce security and permissions
137
+ * on the exposed entities.
138
+ *
139
+ * These rules apply to all exposed entities and actions. An API action
140
+ * can declare its own access rules, which will override these.
141
+ */
142
+ accessRule?: AccessRule[]
143
+ /**
144
+ * Optional configuration for API-wide rate limiting and throttling.
145
+ * Defines rules to protect the API from overuse.
146
+ */
147
+ rateLimiting?: RateLimitingConfiguration
148
+
149
+ /**
150
+ * When the initializing flag is set to true,
151
+ * the domain is not notified of changes.
152
+ */
153
+ #initializing = true
154
+
155
+ /**
156
+ * When the notifying flag is set to true,
157
+ * the domain is pending a notification.
158
+ * No other notifications will be sent until
159
+ * the current notification is sent.
160
+ */
161
+ #notifying = false
162
+
163
+ /**
164
+ * A reference to the published data domain.
165
+ */
166
+ dataDomain?: DataDomain
167
+
168
+ static createSchema(input: Partial<ApiModelSchema> = {}): ApiModelSchema {
169
+ const { key = nanoid(), exposes = [] } = input
170
+ const info = Thing.fromJSON(input.info, { name: 'Unnamed API' }).toJSON()
171
+ const result: ApiModelSchema = {
172
+ kind: ApiModelKind,
173
+ key,
174
+ info,
175
+ exposes,
176
+ }
177
+ if (input.userKey) {
178
+ result.userKey = input.userKey
179
+ }
180
+ if (input.domain) {
181
+ result.domain = input.domain
182
+ }
183
+ if (input.authentication) {
184
+ result.authentication = input.authentication
185
+ }
186
+ if (input.authorization) {
187
+ result.authorization = input.authorization
188
+ }
189
+ if (input.session) {
190
+ result.session = input.session
191
+ }
192
+ if (input.accessRule) {
193
+ result.accessRule = input.accessRule
194
+ }
195
+ if (input.rateLimiting) {
196
+ result.rateLimiting = input.rateLimiting
197
+ }
198
+ return result
199
+ }
200
+
201
+ constructor(state?: Partial<ApiModelSchema>, domain?: DataDomainSchema) {
202
+ super()
203
+ const init = ApiModel.createSchema(state)
204
+ this.kind = init.kind
205
+ this.key = init.key
206
+ this.info = new Thing(init.info)
207
+ this.userKey = init.userKey
208
+ if (init.domain) {
209
+ this.domain = structuredClone(init.domain)
210
+ }
211
+ if (init.authentication) {
212
+ this.authentication = structuredClone(init.authentication)
213
+ }
214
+ if (init.authorization) {
215
+ this.authorization = structuredClone(init.authorization)
216
+ }
217
+ if (init.session) {
218
+ this.session = structuredClone(init.session)
219
+ }
220
+ if (Array.isArray(init.exposes)) {
221
+ this.exposes = structuredClone(init.exposes)
222
+ } else {
223
+ this.exposes = []
224
+ }
225
+ if (init.accessRule) {
226
+ this.accessRule = structuredClone(init.accessRule)
227
+ }
228
+ if (init.rateLimiting) {
229
+ this.rateLimiting = structuredClone(init.rateLimiting)
230
+ }
231
+ if (domain) {
232
+ this.dataDomain = new DataDomain(domain)
233
+ }
234
+ this.#initializing = false
235
+ this.info.addEventListener('change', () => {
236
+ this.notifyChange()
237
+ })
238
+ }
239
+
240
+ toJSON(): ApiModelSchema {
241
+ const result: ApiModelSchema = {
242
+ kind: this.kind,
243
+ key: this.key,
244
+ info: this.info.toJSON(),
245
+ exposes: structuredClone(this.exposes),
246
+ }
247
+ if (this.userKey) {
248
+ result.userKey = this.userKey
249
+ }
250
+ if (this.domain) {
251
+ result.domain = structuredClone(this.domain)
252
+ }
253
+ if (this.authentication) {
254
+ result.authentication = structuredClone(this.authentication)
255
+ }
256
+ if (this.authorization) {
257
+ result.authorization = structuredClone(this.authorization)
258
+ }
259
+ if (this.session) {
260
+ result.session = structuredClone(this.session)
261
+ }
262
+ if (this.accessRule) {
263
+ result.accessRule = structuredClone(this.accessRule)
264
+ }
265
+ if (this.rateLimiting) {
266
+ result.rateLimiting = structuredClone(this.rateLimiting)
267
+ }
268
+ return result
269
+ }
270
+
271
+ /**
272
+ * This function is used internally by all domain elements to notify that something has changed.
273
+ * Since we want to notify listeners after the operation commits, we use microtask
274
+ * to ensure that the event is dispatched after the current operation.
275
+ */
276
+ notifyChange() {
277
+ if (this.#notifying || this.#initializing) {
278
+ return
279
+ }
280
+ this.#notifying = true
281
+ queueMicrotask(() => {
282
+ this.#notifying = false
283
+ const event = new Event('change')
284
+ this.dispatchEvent(event)
285
+ })
286
+ }
287
+
288
+ /**
289
+ * Exposes a new entity in the API model.
290
+ * If the entity already exists, it returns the existing one.
291
+ * @param entityKey The key of the entity to expose.
292
+ * @returns The exposed entity.
293
+ */
294
+ exposeEntity(entityKey: string): ExposedEntity {
295
+ const existing = this.exposes.find((e) => e.key === entityKey)
296
+ if (existing) {
297
+ return existing
298
+ }
299
+ const newEntity: ExposedEntity = {
300
+ key: entityKey,
301
+ actions: [],
302
+ }
303
+ this.exposes.push(newEntity)
304
+ this.notifyChange()
305
+ return newEntity
306
+ }
307
+
308
+ /**
309
+ * Removes an entity from the API model.
310
+ * @param entityKey The key of the entity to remove.
311
+ */
312
+ removeEntity(entityKey: string): void {
313
+ const index = this.exposes.findIndex((e) => e.key === entityKey)
314
+ if (index !== -1) {
315
+ this.exposes.splice(index, 1)
316
+ this.notifyChange()
317
+ }
318
+ }
319
+ /**
320
+ * Returns the exposed entity by its key.
321
+ * @param entityKey The key of the entity to find.
322
+ * @returns The exposed entity or undefined if not found.
323
+ */
324
+ getExposedEntity(entityKey: string): ExposedEntity | undefined {
325
+ return this.exposes.find((e) => e.key === entityKey)
326
+ }
327
+ }
@@ -134,7 +134,7 @@ export class DataDomain extends EventTarget {
134
134
  /**
135
135
  * The description of the domain property.
136
136
  */
137
- accessor info: Thing
137
+ info: Thing
138
138
 
139
139
  /**
140
140
  * When the initializing flag is set to true,
@@ -12,6 +12,26 @@ import type { AssociationTarget, DomainGraphEdge } from './types.js'
12
12
  import { ShapeGenerator } from './amf/ShapeGenerator.js'
13
13
  import { DataSemantics, isAssociationSemantic, type SemanticType, type AppliedDataSemantic } from './Semantics.js'
14
14
 
15
+ /**
16
+ * Defines the behavior when a parent entity in an association is deleted.
17
+ *
18
+ * - `restrict`: Prevents the deletion of a parent entity if it has any associated child entities.
19
+ * The generated API should return a clear and specific error message (e.g., 409 Conflict).
20
+ * - _Example_: Do not allow a Department to be deleted if it still has Employees.
21
+ * - `cascade`: Automatically deletes all associated child entities when the parent entity is deleted.
22
+ * - _Example_: Deleting a User will also delete all their associated Posts and Comments.
23
+ * - `setNull`: Sets the foreign key of the associated child entities to NULL. This is only valid if the
24
+ * association property on the child entity is nullable.
25
+ * - _Example_: When a `Project` is deleted, the `project_id` on associated `Tasks` is set to NULL,
26
+ * making them unassigned but not deleting them.
27
+ * - `doNothing`: No action is taken on the associated child entities when the parent entity is deleted.
28
+ * - _Example_: Deleting a `Category` does not affect associated `Products`, which remain in the database
29
+ * but may become orphaned.
30
+ * This is useful when the association is optional or when child entities should not be deleted
31
+ * or modified upon the deletion of a parent entity.
32
+ */
33
+ export type OnDeleteRule = 'restrict' | 'cascade' | 'setNull' | 'doNothing'
34
+
15
35
  export interface DomainAssociationSchema extends DomainElementSchema {
16
36
  kind: typeof DomainAssociationKind
17
37
  /**
@@ -54,6 +74,14 @@ export interface DomainAssociationSchema extends DomainElementSchema {
54
74
  * describe the association in more detail.
55
75
  */
56
76
  semantics?: AppliedDataSemantic[]
77
+ /**
78
+ * Defines the behavior when a parent entity in an association is deleted.
79
+ */
80
+ onDelete?: OnDeleteRule
81
+ /**
82
+ * Whether the association is read-only.
83
+ */
84
+ readOnly?: boolean
57
85
  }
58
86
 
59
87
  /**
@@ -153,6 +181,16 @@ export class DomainAssociation extends DomainElement {
153
181
  */
154
182
  @observed({ deep: true }) accessor semantics: AppliedDataSemantic[] = []
155
183
 
184
+ /**
185
+ * Defines the behavior when a parent entity in an association is deleted.
186
+ */
187
+ @observed() accessor onDelete: OnDeleteRule | undefined
188
+
189
+ /**
190
+ * Whether the association is read-only.
191
+ */
192
+ @observed() accessor readOnly: boolean | undefined
193
+
156
194
  /**
157
195
  * Creates a full data association schema with defaults.
158
196
  *
@@ -170,6 +208,12 @@ export class DomainAssociation extends DomainElement {
170
208
  if (Array.isArray(semantics)) {
171
209
  result.semantics = [...semantics]
172
210
  }
211
+ if (input.onDelete) {
212
+ result.onDelete = input.onDelete
213
+ }
214
+ if (typeof input.readOnly === 'boolean') {
215
+ result.readOnly = input.readOnly
216
+ }
173
217
  if (input.schema) {
174
218
  result.schema = structuredClone(input.schema)
175
219
  }
@@ -227,6 +271,12 @@ export class DomainAssociation extends DomainElement {
227
271
  } else {
228
272
  this.semantics = []
229
273
  }
274
+ if (init.onDelete) {
275
+ this.onDelete = init.onDelete
276
+ }
277
+ if (typeof init.readOnly === 'boolean') {
278
+ this.readOnly = init.readOnly
279
+ }
230
280
  }
231
281
 
232
282
  /**
@@ -258,6 +308,12 @@ export class DomainAssociation extends DomainElement {
258
308
  if (Array.isArray(this.semantics) && this.semantics.length) {
259
309
  result.semantics = toRaw(this, this.semantics)?.map((i) => structuredClone(i))
260
310
  }
311
+ if (this.onDelete) {
312
+ result.onDelete = this.onDelete
313
+ }
314
+ if (typeof this.readOnly === 'boolean' && this.readOnly) {
315
+ result.readOnly = this.readOnly
316
+ }
261
317
  return result
262
318
  }
263
319
 
@@ -165,7 +165,7 @@ export class DomainEntity extends DomainElement {
165
165
  */
166
166
  static createSchema(input: Partial<DomainEntitySchema> = {}): DomainEntitySchema {
167
167
  const { key = nanoid(), tags, semantics, fields, deprecated } = input
168
- const info = Thing.fromJSON(input.info, { name: 'New entity' }).toJSON()
168
+ const info = Thing.fromJSON(input.info, { name: 'new_entity' }).toJSON()
169
169
  const result: DomainEntitySchema = {
170
170
  kind: DomainEntityKind,
171
171
  key,
@@ -1,6 +1,4 @@
1
- import { nanoid } from '../nanoid.js'
2
1
  import { File, type IFile } from '../models/store/File.js'
3
- import { Thing } from '../models/Thing.js'
4
2
  import type { DataDomain, DataDomainSchema } from './DataDomain.js'
5
3
  import { DomainFileKind } from '../models/kinds.js'
6
4
 
@@ -30,46 +28,11 @@ export class DomainFile extends File {
30
28
  } else {
31
29
  final = input as DataDomainSchema
32
30
  }
33
- const init: IDomainFile = {
34
- kind: DomainFileKind,
35
- key: final.key,
36
- info: { ...final.info },
37
- lastModified: { user: '', time: 0, byMe: false },
38
- parents: [],
39
- permissionIds: [],
40
- permissions: [],
41
- }
42
- return new DomainFile(init)
31
+ return new DomainFile({ key: final.key, info: final.info })
43
32
  }
44
33
 
45
- constructor(input?: string | IDomainFile) {
46
- super()
47
- let init: IDomainFile
48
- if (typeof input === 'string') {
49
- init = JSON.parse(input)
50
- } else if (typeof input === 'object') {
51
- init = input
52
- } else {
53
- init = {
54
- kind: DomainFileKind,
55
- key: nanoid(),
56
- info: Thing.fromName('').toJSON(),
57
- parents: [],
58
- permissionIds: [],
59
- permissions: [],
60
- lastModified: { user: '', time: 0, byMe: false },
61
- }
62
- }
63
- this.new(init)
64
- }
65
-
66
- override new(init: IDomainFile): this {
67
- if (!DomainFile.isDomainFile(init)) {
68
- throw new Error(`Not a data file.`)
69
- }
70
- super.new(init)
71
- this.kind = DomainFileKind
72
- return this
34
+ constructor(state: Partial<IDomainFile> = {}) {
35
+ super({ ...state, kind: DomainFileKind })
73
36
  }
74
37
 
75
38
  static isDomainFile(input: unknown): boolean {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-len */
1
2
  import type { DomainPropertyType } from './DataFormat.js'
2
3
 
3
4
  /**
@@ -9,24 +10,79 @@ export enum SemanticType {
9
10
  /**
10
11
  * Designates a Data Entity that represents users of the system.
11
12
  */
12
- User = 'https://apinow.app/semantics/entities/#User',
13
+ User = 'Semantic#User',
13
14
 
14
15
  // Property-Level Semantics
15
- CreatedTimestamp = 'https://apinow.app/semantics/properties/#CreatedTimestamp',
16
- UpdatedTimestamp = 'https://apinow.app/semantics/properties/#UpdatedTimestamp',
17
- DeletedTimestamp = 'https://apinow.app/semantics/properties/#DeletedTimestamp',
18
- DeletedFlag = 'https://apinow.app/semantics/properties/#DeletedFlag',
19
- PublicUniqueName = 'https://apinow.app/semantics/properties/#PublicUniqueName',
16
+ /**
17
+ * Annotates the field as the user password.
18
+ * The runtime should treat this field with special care,
19
+ * ensuring it is encrypted and not exposed in API responses.
20
+ */
21
+ Password = 'Semantic#Password',
22
+ /**
23
+ * Designates a Data Property as the `createdAt` timestamp of an entity.
24
+ * This is used to track when the entity was first created.
25
+ */
26
+ CreatedTimestamp = 'Semantic#CreatedTimestamp',
27
+ /**
28
+ * Designates a Data Property as the `updatedAt` timestamp of an entity.
29
+ * This is used to track when the entity was last modified.
30
+ */
31
+ UpdatedTimestamp = 'Semantic#UpdatedTimestamp',
32
+ /**
33
+ * Designates a Data Property as the `deletedAt` timestamp of an entity.
34
+ * This is used to track when the entity was soft-deleted.
35
+ * Soft-deletion means the entity is not physically removed from the database,
36
+ * but marked as deleted for logical deletion purposes.
37
+ */
38
+ DeletedTimestamp = 'Semantic#DeletedTimestamp',
39
+ /**
40
+ * Designates a Data Property as a boolean flag indicating whether the entity is deleted.
41
+ * This is used for soft-deletion, where the entity is not physically removed from the database,
42
+ * but marked as deleted.
43
+ */
44
+ DeletedFlag = 'Semantic#DeletedFlag',
45
+ /**
46
+ * Designates a Data Property as a unique public identifier for a resource.
47
+ * This is often used in URLs to provide a user-friendly way to access the resource.
48
+ * For example, a blog post might have a public unique name like "my-first-post".
49
+ */
50
+ PublicUniqueName = 'Semantic#PublicUniqueName',
51
+ /**
52
+ * Designates a Data Property as the `role` of a user within the system.
53
+ * This is used to define the user's permissions and access levels.
54
+ * For example, a user with the role of "admin" would have elevated permissions
55
+ * compared to a user with the role of "guest".
56
+ * Roles are defined on the entity as enums, or as a string property with a controlled vocabulary.
57
+ */
58
+ UserRole = 'Semantic#UserRole',
20
59
  // Association-Level Semantics
21
- ResourceOwnerIdentifier = 'https://apinow.app/semantics/associations/#ResourceOwnerIdentifier',
60
+ /**
61
+ * Designates an association that links a resource to a "User" entity instance.
62
+ * This is used to indicate ownership of the resource for access control purposes.
63
+ * For example, a blog post might have a resource owner identifier that points to the user who created it.
64
+ */
65
+ ResourceOwnerIdentifier = 'Semantic#ResourceOwnerIdentifier',
22
66
  }
23
67
 
24
68
  /**
25
69
  * Defines the scope at which a semantic can be applied.
26
70
  */
27
71
  export enum SemanticScope {
72
+ /**
73
+ * The semantic applies to an entire Data Entity.
74
+ * This is used for semantics that provide context or constraints at the entity level.
75
+ */
28
76
  Entity = 'Entity',
77
+ /**
78
+ * The semantic applies to a single Data Property.
79
+ * This is used for semantics that provide context or constraints at the property level.
80
+ */
29
81
  Property = 'Property',
82
+ /**
83
+ * The semantic applies to an Association between Data Entities.
84
+ * This is used for semantics that provide context or constraints at the association level.
85
+ */
30
86
  Association = 'Association',
31
87
  }
32
88
 
@@ -118,6 +174,14 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
118
174
  },
119
175
 
120
176
  // Property-Level Definitions
177
+ [SemanticType.Password]: {
178
+ id: SemanticType.Password,
179
+ displayName: 'User Password',
180
+ scope: SemanticScope.Property,
181
+ description:
182
+ '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.',
183
+ applicableDataTypes: ['string'],
184
+ },
121
185
  [SemanticType.CreatedTimestamp]: {
122
186
  id: SemanticType.CreatedTimestamp,
123
187
  displayName: 'Creation Timestamp',
@@ -159,6 +223,14 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
159
223
  description: 'A user-friendly, unique public identifier for a resource, often used in URLs.',
160
224
  applicableDataTypes: ['string'],
161
225
  },
226
+ [SemanticType.UserRole]: {
227
+ id: SemanticType.UserRole,
228
+ displayName: 'User Role Field',
229
+ scope: SemanticScope.Property,
230
+ description:
231
+ 'A text field that is recognized by the runtime as a role of a user in the API. Used with the role-based authorization strategy.',
232
+ applicableDataTypes: ['string'],
233
+ },
162
234
  }
163
235
 
164
236
  /**
@@ -107,7 +107,7 @@ export class ShapeGenerator {
107
107
  * const amfShape = generator.entity(myDomainEntity);
108
108
  * ```
109
109
  */
110
- entity(input: DomainEntity, visited: Set<string> = new Set<string>()): IApiNodeShape | IApiRecursiveShape {
110
+ entity(input: DomainEntity, visited = new Set<string>()): IApiNodeShape | IApiRecursiveShape {
111
111
  if (visited.has(input.key)) {
112
112
  // create a recursive shape.
113
113
  return this.createRecursiveShape(input)