@api-client/core 0.14.2 → 0.14.4

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 (86) 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 +1 -0
  4. package/build/src/browser.js.map +1 -1
  5. package/build/src/events/BaseEvents.d.ts +4 -0
  6. package/build/src/events/BaseEvents.d.ts.map +1 -1
  7. package/build/src/events/BaseEvents.js.map +1 -1
  8. package/build/src/index.d.ts +2 -1
  9. package/build/src/index.d.ts.map +1 -1
  10. package/build/src/index.js +2 -0
  11. package/build/src/index.js.map +1 -1
  12. package/build/src/modeling/ApiFile.d.ts +23 -0
  13. package/build/src/modeling/ApiFile.d.ts.map +1 -0
  14. package/build/src/modeling/ApiFile.js +44 -0
  15. package/build/src/modeling/ApiFile.js.map +1 -0
  16. package/build/src/modeling/ApiModel.d.ts +159 -0
  17. package/build/src/modeling/ApiModel.d.ts.map +1 -0
  18. package/build/src/modeling/ApiModel.js +237 -0
  19. package/build/src/modeling/ApiModel.js.map +1 -0
  20. package/build/src/modeling/DataDomain.d.ts +1 -1
  21. package/build/src/modeling/DataDomain.d.ts.map +1 -1
  22. package/build/src/modeling/DataDomain.js +1 -3
  23. package/build/src/modeling/DataDomain.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 +55 -8
  31. package/build/src/modeling/Semantics.d.ts.map +1 -1
  32. package/build/src/modeling/Semantics.js +62 -8
  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 +3 -0
  40. package/build/src/models/kinds.d.ts.map +1 -1
  41. package/build/src/models/kinds.js +3 -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/src/models/store/Group.d.ts +76 -2
  48. package/build/src/models/store/Group.d.ts.map +1 -1
  49. package/build/src/models/store/Group.js +84 -1
  50. package/build/src/models/store/Group.js.map +1 -1
  51. package/build/src/sdk/GroupsSdk.d.ts +41 -0
  52. package/build/src/sdk/GroupsSdk.d.ts.map +1 -0
  53. package/build/src/sdk/GroupsSdk.js +135 -0
  54. package/build/src/sdk/GroupsSdk.js.map +1 -0
  55. package/build/src/sdk/RouteBuilder.d.ts +2 -0
  56. package/build/src/sdk/RouteBuilder.d.ts.map +1 -1
  57. package/build/src/sdk/RouteBuilder.js +6 -0
  58. package/build/src/sdk/RouteBuilder.js.map +1 -1
  59. package/build/src/sdk/Sdk.d.ts +2 -0
  60. package/build/src/sdk/Sdk.d.ts.map +1 -1
  61. package/build/src/sdk/Sdk.js +5 -0
  62. package/build/src/sdk/Sdk.js.map +1 -1
  63. package/build/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +2 -3
  65. package/src/events/BaseEvents.ts +4 -0
  66. package/src/modeling/ApiFile.ts +53 -0
  67. package/src/modeling/ApiModel.ts +327 -0
  68. package/src/modeling/DataDomain.ts +1 -1
  69. package/src/modeling/DomainEntity.ts +1 -1
  70. package/src/modeling/DomainFile.ts +3 -40
  71. package/src/modeling/Semantics.ts +63 -8
  72. package/src/modeling/amf/ShapeGenerator.ts +1 -1
  73. package/src/modeling/types.ts +545 -0
  74. package/src/models/kinds.ts +3 -0
  75. package/src/models/store/File.ts +100 -13
  76. package/src/models/store/Group.ts +148 -2
  77. package/src/sdk/GroupsSdk.ts +150 -0
  78. package/src/sdk/RouteBuilder.ts +8 -0
  79. package/src/sdk/Sdk.ts +6 -0
  80. package/tests/unit/modeling/api_model.spec.ts +291 -0
  81. package/tests/unit/modeling/domain_entity.spec.ts +15 -15
  82. package/tests/unit/modeling/domain_file.spec.ts +1 -11
  83. package/tests/unit/modeling/domain_model_entities.spec.ts +2 -2
  84. package/tests/unit/modeling/semantics.spec.ts +8 -11
  85. package/tests/unit/models/File/constructor.spec.ts +3 -2
  86. package/tests/unit/models/File/shortcutTo.spec.ts +1 -1
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.14.2",
4
+ "version": "0.14.4",
5
5
  "license": "Apache-2.0",
6
6
  "exports": {
7
7
  "./browser.js": {
@@ -135,7 +135,7 @@
135
135
  "oauth2-mock-server": "^8.0.0",
136
136
  "playwright": "^1.50.1",
137
137
  "prettier": "^3.5.1",
138
- "sinon": "^20.0.0",
138
+ "sinon": "^21.0.0",
139
139
  "ts-lit-plugin": "^2.0.2",
140
140
  "ts-node-maintained": "^10.9.5",
141
141
  "typescript": "^5.5.2",
@@ -157,7 +157,6 @@
157
157
  "test:coverage": "wireit",
158
158
  "test:node": "node --import ts-node-maintained/register/esm --enable-source-maps bin/test.ts",
159
159
  "test:node:coverage": "c8 --reporter lcov --reporter text node --import ts-node-maintained/register/esm --enable-source-maps bin/test.ts",
160
- "test:node:watch": "node --import ts-node-maintained/register/esm --enable-source-maps bin/test.ts --watch",
161
160
  "build:api-models": "node data/model.js",
162
161
  "copy:assets": "cp -f ./oauth-popup.html ./build/oauth-popup.html",
163
162
  "start": "echo \"Use the npm run dev instead\"",
@@ -439,6 +439,10 @@ export interface ContextListOptions {
439
439
  * Some specific list operations might use this directly if not part of the main path.
440
440
  */
441
441
  oid?: string
442
+ /**
443
+ * Optional user id to filter results by the user
444
+ */
445
+ uid?: string
442
446
  /**
443
447
  * A structured filter object for advanced querying.
444
448
  */
@@ -0,0 +1,53 @@
1
+ import { File, type IFile } from '../models/store/File.js'
2
+ import { ApiFileKind } from '../models/kinds.js'
3
+ import { ApiModel, ApiModelSchema } from './ApiModel.js'
4
+
5
+ export interface IApiFile extends IFile {
6
+ kind: typeof ApiFileKind
7
+ }
8
+
9
+ /**
10
+ * Used by the store. A file definition for the ApiModel.
11
+ */
12
+ export class ApiFile extends File {
13
+ override kind = ApiFileKind
14
+
15
+ static override fromName(name: string): ApiFile {
16
+ return super.fromName(name, ApiFileKind) as ApiFile
17
+ }
18
+
19
+ /**
20
+ * Creates the file definition for a DomainNamespace contents.
21
+ *
22
+ * @param input The data namespace instance or schema.
23
+ */
24
+ static fromApiModel(input: ApiModel | ApiModelSchema): ApiFile {
25
+ let final: ApiModelSchema
26
+ if (typeof (input as ApiModel).toJSON === 'function') {
27
+ final = (input as ApiModel).toJSON()
28
+ } else {
29
+ final = input as ApiModelSchema
30
+ }
31
+ return new ApiFile({ key: final.key, info: final.info })
32
+ }
33
+
34
+ constructor(state: Partial<IApiFile> = {}) {
35
+ super({ ...state, kind: ApiFileKind })
36
+ }
37
+
38
+ static isApiFile(input: unknown): boolean {
39
+ const typed = input as IApiFile
40
+ if (!input || typed.kind !== ApiFileKind) {
41
+ return false
42
+ }
43
+ return true
44
+ }
45
+
46
+ override toJSON(): IApiFile {
47
+ const result: IApiFile = {
48
+ ...super.toJSON(),
49
+ kind: ApiFileKind,
50
+ }
51
+ return result
52
+ }
53
+ }
@@ -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,
@@ -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 {
@@ -10,14 +10,44 @@ export enum SemanticType {
10
10
  /**
11
11
  * Designates a Data Entity that represents users of the system.
12
12
  */
13
- User = 'https://apinow.app/semantics/entities/#User',
13
+ User = 'Semantic#User',
14
14
 
15
15
  // Property-Level Semantics
16
- CreatedTimestamp = 'https://apinow.app/semantics/properties/#CreatedTimestamp',
17
- UpdatedTimestamp = 'https://apinow.app/semantics/properties/#UpdatedTimestamp',
18
- DeletedTimestamp = 'https://apinow.app/semantics/properties/#DeletedTimestamp',
19
- DeletedFlag = 'https://apinow.app/semantics/properties/#DeletedFlag',
20
- 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',
21
51
  /**
22
52
  * Designates a Data Property as the `role` of a user within the system.
23
53
  * This is used to define the user's permissions and access levels.
@@ -25,17 +55,34 @@ export enum SemanticType {
25
55
  * compared to a user with the role of "guest".
26
56
  * Roles are defined on the entity as enums, or as a string property with a controlled vocabulary.
27
57
  */
28
- UserRole = 'https://apinow.app/semantics/entities/#UserRole',
58
+ UserRole = 'Semantic#UserRole',
29
59
  // Association-Level Semantics
30
- 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',
31
66
  }
32
67
 
33
68
  /**
34
69
  * Defines the scope at which a semantic can be applied.
35
70
  */
36
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
+ */
37
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
+ */
38
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
+ */
39
86
  Association = 'Association',
40
87
  }
41
88
 
@@ -127,6 +174,14 @@ export const DataSemantics: Record<SemanticType, DataSemantic> = {
127
174
  },
128
175
 
129
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
+ },
130
185
  [SemanticType.CreatedTimestamp]: {
131
186
  id: SemanticType.CreatedTimestamp,
132
187
  displayName: 'Creation Timestamp',
@@ -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)