@api-client/core 0.19.19 → 0.19.21
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/authorization/Utils.js +3 -3
- package/build/src/authorization/Utils.js.map +1 -1
- package/build/src/modeling/ApiModel.d.ts +16 -5
- package/build/src/modeling/ApiModel.d.ts.map +1 -1
- package/build/src/modeling/ApiModel.js +17 -2
- package/build/src/modeling/ApiModel.js.map +1 -1
- package/build/src/modeling/ApiValidation.d.ts.map +1 -1
- package/build/src/modeling/ApiValidation.js +2 -1
- package/build/src/modeling/ApiValidation.js.map +1 -1
- package/build/src/modeling/DomainProperty.d.ts +12 -0
- package/build/src/modeling/DomainProperty.d.ts.map +1 -1
- package/build/src/modeling/DomainProperty.js +23 -28
- package/build/src/modeling/DomainProperty.js.map +1 -1
- package/build/src/modeling/DomainSerialization.js +1 -1
- package/build/src/modeling/DomainSerialization.js.map +1 -1
- package/build/src/modeling/ExposedEntity.d.ts +15 -1
- package/build/src/modeling/ExposedEntity.d.ts.map +1 -1
- package/build/src/modeling/ExposedEntity.js +42 -4
- package/build/src/modeling/ExposedEntity.js.map +1 -1
- package/build/src/modeling/actions/Action.d.ts.map +1 -1
- package/build/src/modeling/actions/Action.js +1 -0
- package/build/src/modeling/actions/Action.js.map +1 -1
- package/build/src/modeling/actions/ListAction.d.ts +3 -17
- package/build/src/modeling/actions/ListAction.d.ts.map +1 -1
- package/build/src/modeling/actions/ListAction.js +18 -38
- package/build/src/modeling/actions/ListAction.js.map +1 -1
- package/build/src/modeling/actions/SearchAction.d.ts +4 -4
- package/build/src/modeling/actions/SearchAction.d.ts.map +1 -1
- package/build/src/modeling/actions/SearchAction.js +16 -13
- package/build/src/modeling/actions/SearchAction.js.map +1 -1
- package/build/src/modeling/generators/oas_312/OasGenerator.d.ts +32 -0
- package/build/src/modeling/generators/oas_312/OasGenerator.d.ts.map +1 -0
- package/build/src/modeling/generators/oas_312/OasGenerator.js +1452 -0
- package/build/src/modeling/generators/oas_312/OasGenerator.js.map +1 -0
- package/build/src/modeling/generators/oas_312/OasSchemaGenerator.d.ts +27 -0
- package/build/src/modeling/generators/oas_312/OasSchemaGenerator.d.ts.map +1 -0
- package/build/src/modeling/generators/oas_312/OasSchemaGenerator.js +295 -0
- package/build/src/modeling/generators/oas_312/OasSchemaGenerator.js.map +1 -0
- package/build/src/modeling/generators/oas_312/types.d.ts +1010 -0
- package/build/src/modeling/generators/oas_312/types.d.ts.map +1 -0
- package/build/src/modeling/generators/oas_312/types.js +2 -0
- package/build/src/modeling/generators/oas_312/types.js.map +1 -0
- package/build/src/modeling/generators/oas_320/OasGenerator.d.ts +16 -0
- package/build/src/modeling/generators/oas_320/OasGenerator.d.ts.map +1 -0
- package/build/src/modeling/generators/oas_320/OasGenerator.js +306 -0
- package/build/src/modeling/generators/oas_320/OasGenerator.js.map +1 -0
- package/build/src/modeling/generators/oas_320/OasSchemaGenerator.d.ts +25 -0
- package/build/src/modeling/generators/oas_320/OasSchemaGenerator.d.ts.map +1 -0
- package/build/src/modeling/generators/oas_320/OasSchemaGenerator.js +237 -0
- package/build/src/modeling/generators/oas_320/OasSchemaGenerator.js.map +1 -0
- package/build/src/modeling/generators/oas_320/types.d.ts +1219 -0
- package/build/src/modeling/generators/oas_320/types.d.ts.map +1 -0
- package/build/src/modeling/generators/oas_320/types.js +2 -0
- package/build/src/modeling/generators/oas_320/types.js.map +1 -0
- package/build/src/modeling/helpers/Intelisense.d.ts +1 -1
- package/build/src/modeling/helpers/Intelisense.d.ts.map +1 -1
- package/build/src/modeling/helpers/Intelisense.js +4 -2
- package/build/src/modeling/helpers/Intelisense.js.map +1 -1
- package/build/src/modeling/types.d.ts +50 -13
- package/build/src/modeling/types.d.ts.map +1 -1
- package/build/src/modeling/types.js.map +1 -1
- package/build/src/modeling/validation/api_model_rules.d.ts +1 -0
- package/build/src/modeling/validation/api_model_rules.d.ts.map +1 -1
- package/build/src/modeling/validation/api_model_rules.js +105 -29
- package/build/src/modeling/validation/api_model_rules.js.map +1 -1
- package/build/src/models/ProjectRequest.d.ts.map +1 -1
- package/build/src/models/ProjectRequest.js +0 -4
- package/build/src/models/ProjectRequest.js.map +1 -1
- package/build/src/models/transformers/ArcDexieTransformer.d.ts.map +1 -1
- package/build/src/models/transformers/ArcDexieTransformer.js +0 -4
- package/build/src/models/transformers/ArcDexieTransformer.js.map +1 -1
- package/build/src/models/transformers/ImportUtils.js +1 -1
- package/build/src/models/transformers/ImportUtils.js.map +1 -1
- package/build/src/models/transformers/PostmanBackupTransformer.d.ts.map +1 -1
- package/build/src/models/transformers/PostmanBackupTransformer.js +0 -4
- package/build/src/models/transformers/PostmanBackupTransformer.js.map +1 -1
- package/build/src/runtime/constants.d.ts +7 -0
- package/build/src/runtime/constants.d.ts.map +1 -0
- package/build/src/runtime/constants.js +8 -0
- package/build/src/runtime/constants.js.map +1 -0
- package/build/src/runtime/http-engine/ntlm/Des.d.ts.map +1 -1
- package/build/src/runtime/http-engine/ntlm/Des.js +1 -0
- package/build/src/runtime/http-engine/ntlm/Des.js.map +1 -1
- package/build/src/runtime/variables/EvalFunctions.d.ts.map +1 -1
- package/build/src/runtime/variables/EvalFunctions.js +0 -1
- package/build/src/runtime/variables/EvalFunctions.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/eslint.config.js +6 -0
- package/package.json +3 -1
- package/src/authorization/Utils.ts +3 -3
- package/src/modeling/ApiModel.ts +23 -8
- package/src/modeling/ApiValidation.ts +2 -0
- package/src/modeling/DomainProperty.ts +22 -18
- package/src/modeling/DomainSerialization.ts +1 -1
- package/src/modeling/ExposedEntity.ts +44 -4
- package/src/modeling/actions/Action.ts +1 -0
- package/src/modeling/actions/ListAction.ts +12 -30
- package/src/modeling/actions/SearchAction.ts +11 -8
- package/src/modeling/generators/oas_312/OasGenerator.ts +1685 -0
- package/src/modeling/generators/oas_312/OasSchemaGenerator.ts +322 -0
- package/src/modeling/generators/oas_312/types.ts +1052 -0
- package/src/modeling/generators/oas_320/OasGenerator.ts +359 -0
- package/src/modeling/generators/oas_320/OasSchemaGenerator.ts +255 -0
- package/src/modeling/generators/oas_320/types.ts +1259 -0
- package/src/modeling/helpers/Intelisense.ts +4 -2
- package/src/modeling/types.ts +55 -22
- package/src/modeling/validation/api_model_rules.ts +103 -32
- package/src/models/ProjectRequest.ts +0 -4
- package/src/models/transformers/ArcDexieTransformer.ts +0 -4
- package/src/models/transformers/ImportUtils.ts +1 -1
- package/src/models/transformers/PostmanBackupTransformer.ts +0 -5
- package/src/runtime/constants.ts +9 -0
- package/src/runtime/http-engine/ntlm/Des.ts +1 -0
- package/src/runtime/variables/EvalFunctions.ts +0 -1
- package/tests/test-utils.ts +6 -2
- package/tests/unit/decorators/observed.spec.ts +8 -24
- package/tests/unit/decorators/observed_recursive.spec.ts +0 -1
- package/tests/unit/events/EventsTestHelpers.ts +0 -1
- package/tests/unit/events/events_polyfills.ts +0 -1
- package/tests/unit/legacy-transformers/DataTestHelper.ts +0 -2
- package/tests/unit/legacy-transformers/LegacyExportProcessor.spec.ts +0 -1
- package/tests/unit/modeling/actions/ListAction.spec.ts +9 -69
- package/tests/unit/modeling/actions/SearchAction.spec.ts +9 -35
- package/tests/unit/modeling/api_model.spec.ts +28 -0
- package/tests/unit/modeling/definitions/sku.spec.ts +0 -2
- package/tests/unit/modeling/domain_property.spec.ts +20 -1
- package/tests/unit/modeling/exposed_entity.spec.ts +71 -0
- package/tests/unit/modeling/generators/OasGenerator.spec.ts +302 -0
- package/tests/unit/modeling/helpers/intellisense.spec.ts +1 -1
- package/tests/unit/modeling/validation/api_model_rules.spec.ts +113 -15
package/eslint.config.js
CHANGED
|
@@ -3,6 +3,7 @@ import pluginJs from '@eslint/js'
|
|
|
3
3
|
import tseslint from 'typescript-eslint'
|
|
4
4
|
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
|
|
5
5
|
import noOnlyTests from 'eslint-plugin-no-only-tests'
|
|
6
|
+
import jsdoc from 'eslint-plugin-jsdoc'
|
|
6
7
|
// import eslintPluginPrettier from 'eslint-plugin-prettier'
|
|
7
8
|
|
|
8
9
|
// import eslintConfigPrettier from 'eslint-config-prettier'
|
|
@@ -40,6 +41,7 @@ export default [
|
|
|
40
41
|
// eslintConfigPrettier,
|
|
41
42
|
eslintPluginPrettierRecommended,
|
|
42
43
|
{
|
|
44
|
+
...jsdoc.configs['flat/recommended-typescript'],
|
|
43
45
|
files: ['**/*.ts'],
|
|
44
46
|
languageOptions: {
|
|
45
47
|
globals: {
|
|
@@ -69,6 +71,9 @@ export default [
|
|
|
69
71
|
'no-console': ['error'],
|
|
70
72
|
'no-redeclare': ['error'],
|
|
71
73
|
},
|
|
74
|
+
plugins: {
|
|
75
|
+
jsdoc,
|
|
76
|
+
},
|
|
72
77
|
},
|
|
73
78
|
{
|
|
74
79
|
files: [
|
|
@@ -106,6 +111,7 @@ export default [
|
|
|
106
111
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
107
112
|
'@typescript-eslint/no-empty-function': 'off',
|
|
108
113
|
'no-only-tests/no-only-tests': 'error',
|
|
114
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
109
115
|
'no-console': ['warn'],
|
|
110
116
|
},
|
|
111
117
|
},
|
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.19.
|
|
4
|
+
"version": "0.19.21",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./browser.js": {
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"console-table-printer": "^2.11.2",
|
|
98
98
|
"dompurify": "^3.2.6",
|
|
99
99
|
"jsdom": "^29.0.0",
|
|
100
|
+
"json-schema-typed": "^8.0.2",
|
|
100
101
|
"nanoid": "^5.1.5",
|
|
101
102
|
"tslog": "^4.9.3",
|
|
102
103
|
"ws": "^8.12.0",
|
|
@@ -129,6 +130,7 @@
|
|
|
129
130
|
"cors": "^2.8.5",
|
|
130
131
|
"eslint": "^10.0.1",
|
|
131
132
|
"eslint-config-prettier": "^10.0.1",
|
|
133
|
+
"eslint-plugin-jsdoc": "^62.9.0",
|
|
132
134
|
"eslint-plugin-no-only-tests": "^3.3.0",
|
|
133
135
|
"eslint-plugin-prettier": "^5.2.3",
|
|
134
136
|
"express": "^5.1.0",
|
|
@@ -32,20 +32,20 @@ export function sanityCheck(settings: IOAuth2Authorization): void {
|
|
|
32
32
|
try {
|
|
33
33
|
checkUrl(settings.authorizationUri as string)
|
|
34
34
|
} catch (e) {
|
|
35
|
-
throw new Error(`authorizationUri: ${(e as Error).message}
|
|
35
|
+
throw new Error(`authorizationUri: ${(e as Error).message}`, { cause: e })
|
|
36
36
|
}
|
|
37
37
|
if (settings.accessTokenUri) {
|
|
38
38
|
try {
|
|
39
39
|
checkUrl(settings.accessTokenUri)
|
|
40
40
|
} catch (e) {
|
|
41
|
-
throw new Error(`accessTokenUri: ${(e as Error).message}
|
|
41
|
+
throw new Error(`accessTokenUri: ${(e as Error).message}`, { cause: e })
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
} else if (settings.accessTokenUri) {
|
|
45
45
|
try {
|
|
46
46
|
checkUrl(settings.accessTokenUri)
|
|
47
47
|
} catch (e) {
|
|
48
|
-
throw new Error(`accessTokenUri: ${(e as Error).message}
|
|
48
|
+
throw new Error(`accessTokenUri: ${(e as Error).message}`, { cause: e })
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|
package/src/modeling/ApiModel.ts
CHANGED
|
@@ -3,13 +3,15 @@ import { ApiModelKind, DataDomainKind, ExposedEntityKind } from '../models/kinds
|
|
|
3
3
|
import { type ThingSchema, Thing } from '../models/Thing.js'
|
|
4
4
|
import type {
|
|
5
5
|
AssociationTarget,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
AuthenticationStrategy,
|
|
7
|
+
AuthorizationStrategy,
|
|
8
8
|
ExposedEntitySchema,
|
|
9
9
|
RolesBasedAccessControl,
|
|
10
10
|
SessionConfiguration,
|
|
11
11
|
UsernamePasswordConfiguration,
|
|
12
12
|
ExposeOptions,
|
|
13
|
+
OffsetPaginationStrategy,
|
|
14
|
+
CursorPaginationStrategy,
|
|
13
15
|
} from './types.js'
|
|
14
16
|
import { DataDomain } from './DataDomain.js'
|
|
15
17
|
import { DependentModel, type DependentModelSchema, type DomainDependency } from './DependentModel.js'
|
|
@@ -67,7 +69,6 @@ export interface ApiModelSchema extends DependentModelSchema {
|
|
|
67
69
|
* Contains the name, display name, description, and the version of the API model schema.
|
|
68
70
|
*/
|
|
69
71
|
info: ThingSchema
|
|
70
|
-
|
|
71
72
|
/**
|
|
72
73
|
* The designated Data Entity that represents a "User".
|
|
73
74
|
* This entity should be marked with the "User" semantic in the Data Modeler.
|
|
@@ -80,13 +81,13 @@ export interface ApiModelSchema extends DependentModelSchema {
|
|
|
80
81
|
* Configuration for how users prove their identity.
|
|
81
82
|
* The API model is invalid if this is not set.
|
|
82
83
|
*/
|
|
83
|
-
authentication?:
|
|
84
|
+
authentication?: AuthenticationStrategy
|
|
84
85
|
|
|
85
86
|
/**
|
|
86
87
|
* Configuration for what authenticated users are allowed to do.
|
|
87
88
|
* The API model is invalid if this is not set.
|
|
88
89
|
*/
|
|
89
|
-
authorization?:
|
|
90
|
+
authorization?: AuthorizationStrategy
|
|
90
91
|
|
|
91
92
|
/**
|
|
92
93
|
* Configuration for the transport and payload of the user session.
|
|
@@ -125,6 +126,12 @@ export interface ApiModelSchema extends DependentModelSchema {
|
|
|
125
126
|
* The license information for the API.
|
|
126
127
|
*/
|
|
127
128
|
license?: ApiLicense
|
|
129
|
+
/**
|
|
130
|
+
* The pagination strategy used by all endpoints in this API. The configuration
|
|
131
|
+
* is shared across all endpoints to ensure consistency and security.
|
|
132
|
+
* This defines how the results are paginated when retrieving a collection of resources.
|
|
133
|
+
*/
|
|
134
|
+
pagination: CursorPaginationStrategy | OffsetPaginationStrategy
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
export class ApiModel extends DependentModel {
|
|
@@ -154,13 +161,13 @@ export class ApiModel extends DependentModel {
|
|
|
154
161
|
* Configuration for how users prove their identity.
|
|
155
162
|
* The API model is invalid if this is not set.
|
|
156
163
|
*/
|
|
157
|
-
authentication?:
|
|
164
|
+
authentication?: AuthenticationStrategy
|
|
158
165
|
|
|
159
166
|
/**
|
|
160
167
|
* Configuration for what authenticated users are allowed to do.
|
|
161
168
|
* The API model is invalid if this is not set.
|
|
162
169
|
*/
|
|
163
|
-
authorization?:
|
|
170
|
+
authorization?: AuthorizationStrategy
|
|
164
171
|
|
|
165
172
|
/**
|
|
166
173
|
* Configuration for the transport and payload of the user session.
|
|
@@ -200,6 +207,11 @@ export class ApiModel extends DependentModel {
|
|
|
200
207
|
* The license information for the API.
|
|
201
208
|
*/
|
|
202
209
|
@observed({ deep: true }) accessor license: ApiLicense | undefined
|
|
210
|
+
/**
|
|
211
|
+
* The pagination strategy used by all endpoints in this API.
|
|
212
|
+
* This defines how the results are paginated when retrieving a collection of resources.
|
|
213
|
+
*/
|
|
214
|
+
@observed({ deep: true }) accessor pagination: CursorPaginationStrategy | OffsetPaginationStrategy
|
|
203
215
|
|
|
204
216
|
/**
|
|
205
217
|
* When the initializing flag is set to true,
|
|
@@ -232,13 +244,14 @@ export class ApiModel extends DependentModel {
|
|
|
232
244
|
}
|
|
233
245
|
|
|
234
246
|
static createSchema(input: Partial<ApiModelSchema> = {}): ApiModelSchema {
|
|
235
|
-
const { key = nanoid(), exposes = [] } = input
|
|
247
|
+
const { key = nanoid(), exposes = [], pagination } = input
|
|
236
248
|
const info = Thing.fromJSON(input.info, { name: 'Unnamed API' }).toJSON()
|
|
237
249
|
const result: ApiModelSchema = {
|
|
238
250
|
kind: ApiModelKind,
|
|
239
251
|
key,
|
|
240
252
|
info,
|
|
241
253
|
exposes,
|
|
254
|
+
pagination: pagination ? structuredClone(pagination) : { kind: 'cursor' },
|
|
242
255
|
}
|
|
243
256
|
if (input.user) {
|
|
244
257
|
result.user = structuredClone(input.user)
|
|
@@ -298,6 +311,7 @@ export class ApiModel extends DependentModel {
|
|
|
298
311
|
this.key = init.key
|
|
299
312
|
this.info = new Thing(init.info)
|
|
300
313
|
this.user = init.user
|
|
314
|
+
this.pagination = init.pagination ? structuredClone(init.pagination) : { kind: 'cursor' }
|
|
301
315
|
if (init.authentication) {
|
|
302
316
|
this.authentication = structuredClone(init.authentication)
|
|
303
317
|
}
|
|
@@ -341,6 +355,7 @@ export class ApiModel extends DependentModel {
|
|
|
341
355
|
key: this.key,
|
|
342
356
|
info: this.info.toJSON(),
|
|
343
357
|
exposes: Array.from(this.exposes.values()).map((e) => e.toJSON()),
|
|
358
|
+
pagination: structuredClone(toRaw(this, this.pagination)) as CursorPaginationStrategy | OffsetPaginationStrategy,
|
|
344
359
|
}
|
|
345
360
|
if (this.user) {
|
|
346
361
|
result.user = { ...this.user }
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
validateApiModelSecurity,
|
|
8
8
|
validateApiModelMetadata,
|
|
9
9
|
validateExposedEntity,
|
|
10
|
+
validateApiPagination,
|
|
10
11
|
} from './validation/api_model_rules.js'
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -58,6 +59,7 @@ export class ApiValidation {
|
|
|
58
59
|
this.report.push(...validateApiModelInfo(this.model))
|
|
59
60
|
this.report.push(...validateApiModelDependency(this.model))
|
|
60
61
|
this.report.push(...validateApiModelSecurity(this.model))
|
|
62
|
+
this.report.push(...validateApiPagination(this.model))
|
|
61
63
|
this.report.push(...validateApiModelMetadata(this.model))
|
|
62
64
|
|
|
63
65
|
if (!this.model.exposes || this.model.exposes.size === 0) {
|
|
@@ -50,8 +50,14 @@ export interface DomainPropertySchema extends DomainElementSchema {
|
|
|
50
50
|
unique?: boolean
|
|
51
51
|
/**
|
|
52
52
|
* Whether this property describes an indexed property of the entity.
|
|
53
|
+
* This enables a standard B-Tree index.
|
|
53
54
|
*/
|
|
54
55
|
index?: boolean
|
|
56
|
+
/**
|
|
57
|
+
* Whether this property describes a search index of the entity.
|
|
58
|
+
* This enables a specialized Inverted Index (e.g., a GIN or `pg_trgm` index in PostgreSQL).
|
|
59
|
+
*/
|
|
60
|
+
search?: boolean
|
|
55
61
|
/**
|
|
56
62
|
* Whether the property is read only in the schema.
|
|
57
63
|
*/
|
|
@@ -170,8 +176,14 @@ export class DomainProperty extends DomainElement {
|
|
|
170
176
|
|
|
171
177
|
/**
|
|
172
178
|
* Whether this property describes an indexed property of the entity.
|
|
179
|
+
* This enables a standard B-Tree index.
|
|
173
180
|
*/
|
|
174
181
|
@observed() accessor index: boolean | undefined
|
|
182
|
+
/**
|
|
183
|
+
* Whether this property describes a search index of the entity.
|
|
184
|
+
* This enables a specialized Inverted Index (e.g., a GIN or `pg_trgm` index in PostgreSQL).
|
|
185
|
+
*/
|
|
186
|
+
@observed() accessor search: boolean | undefined
|
|
175
187
|
|
|
176
188
|
/**
|
|
177
189
|
* Whether the property is read only in the schema.
|
|
@@ -239,6 +251,7 @@ export class DomainProperty extends DomainElement {
|
|
|
239
251
|
required,
|
|
240
252
|
type = DomainPropertyList.string,
|
|
241
253
|
index,
|
|
254
|
+
search,
|
|
242
255
|
primary,
|
|
243
256
|
unique,
|
|
244
257
|
readOnly,
|
|
@@ -270,6 +283,9 @@ export class DomainProperty extends DomainElement {
|
|
|
270
283
|
if (typeof index === 'boolean') {
|
|
271
284
|
result.index = index
|
|
272
285
|
}
|
|
286
|
+
if (typeof search === 'boolean') {
|
|
287
|
+
result.search = search
|
|
288
|
+
}
|
|
273
289
|
if (typeof primary === 'boolean') {
|
|
274
290
|
result.primary = primary
|
|
275
291
|
}
|
|
@@ -330,43 +346,30 @@ export class DomainProperty extends DomainElement {
|
|
|
330
346
|
|
|
331
347
|
if (typeof init.multiple === 'boolean') {
|
|
332
348
|
this.multiple = init.multiple
|
|
333
|
-
} else {
|
|
334
|
-
this.multiple = undefined
|
|
335
349
|
}
|
|
336
350
|
if (typeof init.required === 'boolean') {
|
|
337
351
|
this.required = init.required
|
|
338
|
-
} else {
|
|
339
|
-
this.required = undefined
|
|
340
352
|
}
|
|
341
353
|
if (typeof init.index === 'boolean') {
|
|
342
354
|
this.index = init.index
|
|
343
|
-
}
|
|
344
|
-
|
|
355
|
+
}
|
|
356
|
+
if (typeof init.search === 'boolean') {
|
|
357
|
+
this.search = init.search
|
|
345
358
|
}
|
|
346
359
|
if (typeof init.deprecated === 'boolean') {
|
|
347
360
|
this.deprecated = init.deprecated
|
|
348
|
-
} else {
|
|
349
|
-
this.deprecated = undefined
|
|
350
361
|
}
|
|
351
362
|
if (typeof init.primary === 'boolean') {
|
|
352
363
|
this.primary = init.primary
|
|
353
|
-
} else {
|
|
354
|
-
this.primary = undefined
|
|
355
364
|
}
|
|
356
365
|
if (typeof init.unique === 'boolean') {
|
|
357
366
|
this.unique = init.unique
|
|
358
|
-
} else {
|
|
359
|
-
this.unique = undefined
|
|
360
367
|
}
|
|
361
368
|
if (typeof init.readOnly === 'boolean') {
|
|
362
369
|
this.readOnly = init.readOnly
|
|
363
|
-
} else {
|
|
364
|
-
this.readOnly = undefined
|
|
365
370
|
}
|
|
366
371
|
if (typeof init.writeOnly === 'boolean') {
|
|
367
372
|
this.writeOnly = init.writeOnly
|
|
368
|
-
} else {
|
|
369
|
-
this.writeOnly = undefined
|
|
370
373
|
}
|
|
371
374
|
if (Array.isArray(init.tags)) {
|
|
372
375
|
this.tags = [...init.tags]
|
|
@@ -380,8 +383,6 @@ export class DomainProperty extends DomainElement {
|
|
|
380
383
|
}
|
|
381
384
|
if (init.schema) {
|
|
382
385
|
this.schema = structuredClone(init.schema)
|
|
383
|
-
} else {
|
|
384
|
-
this.schema = undefined
|
|
385
386
|
}
|
|
386
387
|
if (Array.isArray(init.bindings)) {
|
|
387
388
|
this.bindings = init.bindings.map((i) => structuredClone(i))
|
|
@@ -418,6 +419,9 @@ export class DomainProperty extends DomainElement {
|
|
|
418
419
|
if (typeof this.index === 'boolean') {
|
|
419
420
|
result.index = this.index
|
|
420
421
|
}
|
|
422
|
+
if (typeof this.search === 'boolean') {
|
|
423
|
+
result.search = this.search
|
|
424
|
+
}
|
|
421
425
|
if (typeof this.deprecated === 'boolean') {
|
|
422
426
|
result.deprecated = this.deprecated
|
|
423
427
|
}
|
|
@@ -513,7 +513,7 @@ export function deserialize(root: DataDomain, options: DeserializeOptions = {}):
|
|
|
513
513
|
const message = `Failed to merge dependency "${dependency.key}": ${error instanceof Error ? error.message : String(error)}`
|
|
514
514
|
|
|
515
515
|
if (mode === 'strict') {
|
|
516
|
-
throw new Error(message)
|
|
516
|
+
throw new Error(message, { cause: error })
|
|
517
517
|
}
|
|
518
518
|
|
|
519
519
|
const issue: DeserializationIssue = {
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { observed } from '../decorators/observed.js'
|
|
1
|
+
import { observed, toRaw } from '../decorators/observed.js'
|
|
2
2
|
import { ExposedEntityKind } from '../models/kinds.js'
|
|
3
3
|
import { nanoid } from '../nanoid.js'
|
|
4
4
|
import { Action } from './actions/Action.js'
|
|
5
|
-
import { restoreAction } from './actions/index.js'
|
|
5
|
+
import { type ApiActionSchema, restoreAction } from './actions/index.js'
|
|
6
6
|
import type { ApiModel } from './ApiModel.js'
|
|
7
7
|
import { ensureLeadingSlash, joinPaths } from './helpers/endpointHelpers.js'
|
|
8
8
|
import { AccessRule } from './rules/AccessRule.js'
|
|
9
9
|
import { type RateLimitRule, restoreAccessRule } from './rules/index.js'
|
|
10
10
|
import { RateLimitingConfiguration } from './rules/RateLimitingConfiguration.js'
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
AssociationTarget,
|
|
13
|
+
ExposeOptions,
|
|
14
|
+
ExposeParentRef,
|
|
15
|
+
ExposedEntitySchema,
|
|
16
|
+
PaginationContract,
|
|
17
|
+
} from './types.js'
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
20
|
* A class that specializes in representing an exposed Data Entity within an API Model.
|
|
@@ -100,6 +106,14 @@ export class ExposedEntity extends EventTarget {
|
|
|
100
106
|
*/
|
|
101
107
|
@observed() accessor rateLimiting: RateLimitingConfiguration | undefined
|
|
102
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Pagination contract for this exposure.
|
|
111
|
+
* Defines a list of fields that can be used for filtering, sorting, and searching.
|
|
112
|
+
* The pagination contract is only valid for that specific exposure. It cannot be inherited
|
|
113
|
+
* by other exposures.
|
|
114
|
+
*/
|
|
115
|
+
@observed({ deep: true }) accessor paginationContract: PaginationContract | undefined
|
|
116
|
+
|
|
103
117
|
/**
|
|
104
118
|
* When true, generation for this exposure hit configured limits
|
|
105
119
|
*
|
|
@@ -138,6 +152,7 @@ export class ExposedEntity extends EventTarget {
|
|
|
138
152
|
accessRule,
|
|
139
153
|
rateLimiting,
|
|
140
154
|
truncated,
|
|
155
|
+
paginationContract,
|
|
141
156
|
} = input
|
|
142
157
|
const result: ExposedEntitySchema = {
|
|
143
158
|
kind: ExposedEntityKind,
|
|
@@ -168,6 +183,9 @@ export class ExposedEntity extends EventTarget {
|
|
|
168
183
|
if (truncated !== undefined) {
|
|
169
184
|
result.truncated = truncated
|
|
170
185
|
}
|
|
186
|
+
if (paginationContract !== undefined) {
|
|
187
|
+
result.paginationContract = structuredClone(paginationContract)
|
|
188
|
+
}
|
|
171
189
|
return result
|
|
172
190
|
}
|
|
173
191
|
|
|
@@ -186,8 +204,13 @@ export class ExposedEntity extends EventTarget {
|
|
|
186
204
|
this.exposeOptions = init.exposeOptions
|
|
187
205
|
this.actions = init.actions ? init.actions.map((a) => restoreAction(this, a)) : []
|
|
188
206
|
this.accessRule = init.accessRule ? init.accessRule.map((ar) => restoreAccessRule(ar)) : []
|
|
189
|
-
|
|
207
|
+
if (init.rateLimiting) {
|
|
208
|
+
this.rateLimiting = new RateLimitingConfiguration(init.rateLimiting)
|
|
209
|
+
}
|
|
190
210
|
this.truncated = init.truncated
|
|
211
|
+
if (init.paginationContract) {
|
|
212
|
+
this.paginationContract = structuredClone(init.paginationContract)
|
|
213
|
+
}
|
|
191
214
|
this.#initializing = false
|
|
192
215
|
}
|
|
193
216
|
|
|
@@ -233,6 +256,9 @@ export class ExposedEntity extends EventTarget {
|
|
|
233
256
|
if (this.truncated !== undefined) {
|
|
234
257
|
result.truncated = this.truncated
|
|
235
258
|
}
|
|
259
|
+
if (this.paginationContract) {
|
|
260
|
+
result.paginationContract = structuredClone(toRaw(this, this.paginationContract))
|
|
261
|
+
}
|
|
236
262
|
return result
|
|
237
263
|
}
|
|
238
264
|
|
|
@@ -434,4 +460,18 @@ export class ExposedEntity extends EventTarget {
|
|
|
434
460
|
}
|
|
435
461
|
return rules
|
|
436
462
|
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Adds an action to the exposure.
|
|
466
|
+
* @param schema The schema of the action to add.
|
|
467
|
+
* @returns The added action.
|
|
468
|
+
*/
|
|
469
|
+
addAction(schema: ApiActionSchema): Action {
|
|
470
|
+
if (this.actions.some((action) => action.kind === schema.kind)) {
|
|
471
|
+
throw new Error(`Action of kind "${schema.kind}" already exists for this exposure`)
|
|
472
|
+
}
|
|
473
|
+
const action = restoreAction(this, schema)
|
|
474
|
+
this.actions.push(action)
|
|
475
|
+
return action
|
|
476
|
+
}
|
|
437
477
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { PaginationStrategy } from '../types.js'
|
|
2
1
|
import { Action, type ActionSchema } from './Action.js'
|
|
3
|
-
import { observed, toRaw } from '../../decorators/observed.js'
|
|
4
2
|
import type { ExposedEntity } from '../ExposedEntity.js'
|
|
3
|
+
import { observed } from '../../decorators/observed.js'
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Enables retrieving a collection of resources.
|
|
@@ -10,20 +9,9 @@ import type { ExposedEntity } from '../ExposedEntity.js'
|
|
|
10
9
|
export interface ListActionSchema extends ActionSchema {
|
|
11
10
|
kind: 'list'
|
|
12
11
|
/**
|
|
13
|
-
* The
|
|
14
|
-
* This defines how the results are paginated when retrieving a collection of resources.
|
|
15
|
-
* It can be either 'cursor' or 'offset'.
|
|
12
|
+
* The time to live for the cache in seconds.
|
|
16
13
|
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Fields from the entity that can be used for filtering.
|
|
20
|
-
* Must be marked as "indexable" in the Data Model.
|
|
21
|
-
*/
|
|
22
|
-
filterableFields: string[]
|
|
23
|
-
/**
|
|
24
|
-
* Fields from the entity that can be used for sorting.
|
|
25
|
-
*/
|
|
26
|
-
sortableFields: string[]
|
|
14
|
+
cacheTtl?: number
|
|
27
15
|
}
|
|
28
16
|
|
|
29
17
|
/**
|
|
@@ -32,29 +20,23 @@ export interface ListActionSchema extends ActionSchema {
|
|
|
32
20
|
*/
|
|
33
21
|
export class ListAction extends Action implements ListActionSchema {
|
|
34
22
|
@observed() override accessor kind: 'list'
|
|
35
|
-
@observed(
|
|
36
|
-
@observed({ deep: true }) accessor filterableFields: string[] = []
|
|
37
|
-
@observed({ deep: true }) accessor sortableFields: string[] = []
|
|
23
|
+
@observed() accessor cacheTtl: number | undefined
|
|
38
24
|
|
|
39
25
|
constructor(parent: ExposedEntity, state: Partial<ListActionSchema> = {}) {
|
|
40
26
|
super(parent, state)
|
|
41
|
-
this.kind =
|
|
42
|
-
this.
|
|
43
|
-
? { ...state.pagination }
|
|
44
|
-
: {
|
|
45
|
-
kind: 'offset',
|
|
46
|
-
}
|
|
47
|
-
this.filterableFields = state.filterableFields ? [...state.filterableFields] : []
|
|
48
|
-
this.sortableFields = state.sortableFields ? [...state.sortableFields] : []
|
|
27
|
+
this.kind = 'list'
|
|
28
|
+
this.cacheTtl = state.cacheTtl
|
|
49
29
|
}
|
|
50
30
|
|
|
51
31
|
override toJSON(): ListActionSchema {
|
|
52
|
-
|
|
32
|
+
const result: ListActionSchema = {
|
|
53
33
|
...(super.toJSON() as ListActionSchema),
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
34
|
+
kind: 'list',
|
|
35
|
+
}
|
|
36
|
+
if (this.cacheTtl !== undefined) {
|
|
37
|
+
result.cacheTtl = this.cacheTtl
|
|
57
38
|
}
|
|
39
|
+
return result
|
|
58
40
|
}
|
|
59
41
|
|
|
60
42
|
static isListAction(action: Action): action is ListAction {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Action, type ActionSchema } from './Action.js'
|
|
2
|
-
import { observed, toRaw } from '../../decorators/observed.js'
|
|
3
2
|
import type { ExposedEntity } from '../ExposedEntity.js'
|
|
3
|
+
import { observed } from '../../decorators/observed.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Enables keyword-based search across specified fields.
|
|
@@ -9,10 +9,10 @@ import type { ExposedEntity } from '../ExposedEntity.js'
|
|
|
9
9
|
export interface SearchActionSchema extends ActionSchema {
|
|
10
10
|
kind: 'search'
|
|
11
11
|
/**
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
12
|
+
* The maximum depth of the Abstract Syntax Tree (AST) that will be used to parse the search query.
|
|
13
|
+
* This is used to prevent denial of service attacks.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
maxAstDepth?: number
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -21,20 +21,23 @@ export interface SearchActionSchema extends ActionSchema {
|
|
|
21
21
|
*/
|
|
22
22
|
export class SearchAction extends Action implements SearchActionSchema {
|
|
23
23
|
@observed() override accessor kind: 'search'
|
|
24
|
-
@observed(
|
|
24
|
+
@observed() accessor maxAstDepth: number | undefined
|
|
25
25
|
|
|
26
26
|
constructor(parent: ExposedEntity, state: Partial<SearchActionSchema> = {}) {
|
|
27
27
|
super(parent, state)
|
|
28
28
|
this.kind = 'search'
|
|
29
|
-
this.
|
|
29
|
+
this.maxAstDepth = state.maxAstDepth
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
override toJSON(): SearchActionSchema {
|
|
33
|
-
|
|
33
|
+
const result: SearchActionSchema = {
|
|
34
34
|
...(super.toJSON() as SearchActionSchema),
|
|
35
35
|
kind: 'search',
|
|
36
|
-
fields: structuredClone(toRaw(this, this.fields)) as string[],
|
|
37
36
|
}
|
|
37
|
+
if (this.maxAstDepth !== undefined) {
|
|
38
|
+
result.maxAstDepth = this.maxAstDepth
|
|
39
|
+
}
|
|
40
|
+
return result
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
static isSearchActionSchema(schema: ActionSchema): schema is SearchActionSchema {
|