@sap/cds 7.4.0 → 7.4.1
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/CHANGELOG.md +11 -0
- package/apis/core.d.ts +7 -7
- package/apis/events.d.ts +2 -1
- package/apis/linked.d.ts +2 -0
- package/apis/ql.d.ts +8 -0
- package/apis/server.d.ts +3 -3
- package/apis/services.d.ts +5 -5
- package/bin/serve.js +1 -1
- package/lib/compile/etc/csv.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/TrustedResourceJsonSerializer.js +8 -1
- package/libx/_runtime/common/utils/resolveView.js +8 -8
- package/libx/_runtime/db/expand/expandCQNToJoin.js +4 -4
- package/libx/_runtime/db/expand/rawToExpanded.js +4 -4
- package/libx/_runtime/fiori/lean-draft.js +30 -21
- package/libx/_runtime/remote/utils/client.js +2 -2
- package/libx/odata/afterburner.js +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## Version 7.4.1 - 2023-11-23
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Add dynamic properties to result when experimental feature `cds.env.features.okra_skip_query_options` is active
|
|
12
|
+
- Allow negative integers in new parser
|
|
13
|
+
- Allow deletion of instances outside the draft tree
|
|
14
|
+
- Tenant lookup in OData metadata requests
|
|
15
|
+
- `cds.parse.csv` and `cds deploy` correctly parse CSV files with Windows file endings (CRLF) and quoted values
|
|
16
|
+
- Typescript Typings
|
|
17
|
+
|
|
7
18
|
## Version 7.4.0 - 2023-11-13
|
|
8
19
|
|
|
9
20
|
### Added
|
package/apis/core.d.ts
CHANGED
|
@@ -3,13 +3,13 @@ import * as csn from './csn'
|
|
|
3
3
|
import { service } from './server'
|
|
4
4
|
|
|
5
5
|
// These are classes actually -> using the new() => interface trick
|
|
6
|
-
export type Association = new() => LinkedAssociation
|
|
7
|
-
export type Composition = new() => LinkedAssociation
|
|
8
|
-
export type entity = new() => LinkedEntity
|
|
9
|
-
export type event = new() => linked & csn.struct
|
|
10
|
-
export type type = new() => linked & csn.type
|
|
11
|
-
export type array = new() => linked & csn.type
|
|
12
|
-
export type struct = new() => linked & csn.struct
|
|
6
|
+
export type Association = new(_?:object) => LinkedAssociation
|
|
7
|
+
export type Composition = new(_?:object) => LinkedAssociation
|
|
8
|
+
export type entity = new(_?:object) => LinkedEntity
|
|
9
|
+
export type event = new(_?:object) => linked & csn.struct
|
|
10
|
+
export type type = new(_?:object) => linked & csn.type
|
|
11
|
+
export type array = new(_?:object) => linked & csn.type
|
|
12
|
+
export type struct = new(_?:object) => linked & csn.struct
|
|
13
13
|
|
|
14
14
|
export default class cds {
|
|
15
15
|
// infer (query : cqn, model : csn) : LinkedDefinition
|
package/apis/events.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export default class cds {
|
|
|
34
34
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/events)
|
|
35
35
|
*/
|
|
36
36
|
export class EventContext {
|
|
37
|
+
constructor(properties:{event:string, data?:object, query?:object, headers:object});
|
|
37
38
|
http?: {req: express.Request, res: express.Response}
|
|
38
39
|
tenant: string
|
|
39
40
|
user: User
|
|
@@ -48,7 +49,7 @@ export class EventContext {
|
|
|
48
49
|
export class Event extends EventContext {
|
|
49
50
|
event: string
|
|
50
51
|
data: any
|
|
51
|
-
headers:
|
|
52
|
+
headers: any
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
/**
|
package/apis/linked.d.ts
CHANGED
package/apis/ql.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CSN, Definition, EntityElements } from "./csn"
|
|
2
2
|
import * as CQN from "./cqn"
|
|
3
3
|
import { Constructable, ArrayConstructable, SingularType } from "./internal/inference"
|
|
4
|
+
import { LinkedEntity } from "./linked"
|
|
4
5
|
|
|
5
6
|
export type Query = CQN.Query
|
|
6
7
|
|
|
@@ -206,6 +207,7 @@ TaggedTemplateQueryPart<Awaitable<SELECT<unknown>, InstanceType<any>>>
|
|
|
206
207
|
=> Awaitable<SELECT<SingularType<T>>, SingularType<T>>)
|
|
207
208
|
|
|
208
209
|
& ((entity: Definition | string, primaryKey? : PK, projection? : Projection<unknown>) => SELECT<any>)
|
|
210
|
+
& ((entity: LinkedEntity | string, primaryKey? : PK, projection? : Projection<unknown>) => SELECT<any>)
|
|
209
211
|
& (<T> (entity: T[], projection? : Projection<T>) => Awaitable<SELECT<T>, T>)
|
|
210
212
|
& (<T> (entity: T[], primaryKey : PK, projection? : Projection<T>) => Awaitable<SELECT<T>, T>)
|
|
211
213
|
& (<T> (entity: {new():T}, projection? : Projection<T>) => Awaitable<SELECT<T>, T>)
|
|
@@ -225,6 +227,7 @@ type SELECT_from =
|
|
|
225
227
|
=> Awaitable<SELECT<SingularType<T>>, InstanceType<SingularType<T>>>) // when specifying a key, we expect a single element as result
|
|
226
228
|
// calling with definition
|
|
227
229
|
& ((entity: Definition | string, primaryKey? : PK, projection? : Projection<unknown>) => SELECT<any>)
|
|
230
|
+
& ((entity: LinkedEntity | string, primaryKey? : PK, projection? : Projection<unknown>) => SELECT<any>)
|
|
228
231
|
// calling with concrete list
|
|
229
232
|
& (<T> (entity: T[], projection? : Projection<T>) => SELECT<T> & Promise<T[]>)
|
|
230
233
|
& (<T> (entity: T[], primaryKey : PK, projection? : Projection<T>) => Awaitable<SELECT<T>, T>)
|
|
@@ -234,6 +237,7 @@ export class INSERT<T> extends ConstructedQuery {
|
|
|
234
237
|
static into : (<T extends ArrayConstructable<any>> (entity:T, entries? : object | object[]) => INSERT<SingularType<T>>)
|
|
235
238
|
& (TaggedTemplateQueryPart<INSERT<unknown>>)
|
|
236
239
|
& ((entity : Definition | string, entries? : object | object[]) => INSERT<any>)
|
|
240
|
+
& ((entity : LinkedEntity | string, entries? : object | object[]) => INSERT<any>)
|
|
237
241
|
& (<T> (entity:Constructable<T>, entries? : object | object[]) => INSERT<T>)
|
|
238
242
|
& (<T> (entity:T, entries? : T | object | object[]) => INSERT<T>)
|
|
239
243
|
|
|
@@ -255,6 +259,7 @@ export class UPSERT<T> extends ConstructedQuery {
|
|
|
255
259
|
static into : (<T extends ArrayConstructable<any>> (entity:T, entries? : object | object[]) => UPSERT<SingularType<T>>)
|
|
256
260
|
& (TaggedTemplateQueryPart<UPSERT<unknown>>)
|
|
257
261
|
& ((entity : Definition | string, entries? : object | object[]) => UPSERT<any>)
|
|
262
|
+
& ((entity : LinkedEntity | string, entries? : object | object[]) => UPSERT<any>)
|
|
258
263
|
& (<T> (entity:Constructable<T>, entries? : object | object[]) => UPSERT<T>)
|
|
259
264
|
& (<T> (entity:T, entries? : T | object | object[]) => UPSERT<T>)
|
|
260
265
|
|
|
@@ -289,6 +294,7 @@ export class UPDATE<T> extends ConstructedQuery {
|
|
|
289
294
|
static entity <T extends ArrayConstructable<any>> (entity:T, primaryKey? : PK) : UPDATE<SingularType<T>>
|
|
290
295
|
|
|
291
296
|
static entity (entity : Definition | string, primaryKey? : PK) : UPDATE<any>
|
|
297
|
+
static entity (entity : LinkedEntity | string, primaryKey? : PK) : UPDATE<any>
|
|
292
298
|
static entity <T> (entity:Constructable<T>, primaryKey? : PK) : UPDATE<T>
|
|
293
299
|
static entity <T> (entity:T, primaryKey? : PK) : UPDATE<T>
|
|
294
300
|
byKey (primaryKey? : PK) : this
|
|
@@ -307,10 +313,12 @@ export class UPDATE<T> extends ConstructedQuery {
|
|
|
307
313
|
|
|
308
314
|
export class CREATE<T> extends ConstructedQuery {
|
|
309
315
|
static entity (entity : Definition | string) : CREATE<any>
|
|
316
|
+
static entity (entity : LinkedEntity | string) : CREATE<any>
|
|
310
317
|
CREATE : CQN.CREATE["CREATE"]
|
|
311
318
|
}
|
|
312
319
|
|
|
313
320
|
export class DROP<T> extends ConstructedQuery {
|
|
314
321
|
static entity (entity : Definition | string) : DROP<any>
|
|
322
|
+
static entity (entity : LinkedEntity | string) : DROP<any>
|
|
315
323
|
DROP : CQN.DROP["DROP"]
|
|
316
324
|
}
|
package/apis/server.d.ts
CHANGED
|
@@ -100,18 +100,18 @@ export default class cds {
|
|
|
100
100
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
export type service =
|
|
103
|
+
export type service = {
|
|
104
104
|
/**
|
|
105
105
|
* Dummy wrapper for service implementation functions.
|
|
106
106
|
* Use that in modules to get IntelliSense.
|
|
107
107
|
*/
|
|
108
108
|
impl (impl: ServiceImpl) : typeof impl
|
|
109
|
-
impl <T> (srv:T, impl: ( this: T, srv: (T) ) => any) : typeof impl
|
|
109
|
+
// impl <T> (srv:T, impl: ( this: T, srv: (T) ) => any) : typeof impl
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Array of all services constructed.
|
|
113
113
|
*/
|
|
114
|
-
providers : Service
|
|
114
|
+
providers : Service[]
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
|
package/apis/services.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SELECT, INSERT, UPDATE, DELETE, Query, ConstructedQuery, UPSERT } from './ql'
|
|
2
2
|
import { Awaitable } from './ql'
|
|
3
3
|
import { ArrayConstructable, Constructable } from './internal/inference'
|
|
4
|
-
import { LinkedCSN, LinkedDefinition, Definitions } from './linked'
|
|
4
|
+
import { LinkedCSN, LinkedDefinition, Definitions, LinkedEntity } from './linked'
|
|
5
5
|
import { CSN } from './csn'
|
|
6
6
|
import { EventContext } from './events'
|
|
7
7
|
import { Request } from './events'
|
|
@@ -120,9 +120,9 @@ export class QueryAPI {
|
|
|
120
120
|
*/
|
|
121
121
|
export class Service extends QueryAPI {
|
|
122
122
|
constructor(
|
|
123
|
-
name
|
|
124
|
-
model
|
|
125
|
-
options
|
|
123
|
+
name?: string,
|
|
124
|
+
model?: CSN,
|
|
125
|
+
options?: {
|
|
126
126
|
kind: string
|
|
127
127
|
impl: string | ServiceImpl
|
|
128
128
|
}
|
|
@@ -380,5 +380,5 @@ declare namespace types {
|
|
|
380
380
|
| 'NEW' | 'EDIT' | 'PATCH' | 'SAVE'
|
|
381
381
|
| 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE'
|
|
382
382
|
| 'COMMIT' | 'ROLLBACK'
|
|
383
|
-
type target = string | LinkedDefinition | ArrayConstructable<any>
|
|
383
|
+
type target = string | LinkedDefinition | LinkedEntity | (string | LinkedDefinition | LinkedEntity)[] | ArrayConstructable<any>
|
|
384
384
|
}
|
package/bin/serve.js
CHANGED
|
@@ -192,7 +192,7 @@ async function serve (all=[], o={}) {
|
|
|
192
192
|
const server = await cds_server(o)
|
|
193
193
|
|
|
194
194
|
// increase keep-alive timeout for CF (gorouter wants >90s)
|
|
195
|
-
if (process.env.
|
|
195
|
+
if (process.env.CF_INSTANCE_GUID) server.keepAliveTimeout = 91 * 1000
|
|
196
196
|
|
|
197
197
|
// return a promise which resolves to the created http server when listening
|
|
198
198
|
return cds.server.listening = new Promise ((_resolve,_reject) => {
|
package/lib/compile/etc/csv.js
CHANGED
|
@@ -12,7 +12,8 @@ function read (res) {
|
|
|
12
12
|
function parse (csv) {
|
|
13
13
|
if (csv[0] === BOM) csv = csv.slice(1)
|
|
14
14
|
let sep
|
|
15
|
-
|
|
15
|
+
// this also means that \r\n within quotes is NOT retained but normalized to \n. We accept this for now.
|
|
16
|
+
const lines = csv.split(/\r?\n/)
|
|
16
17
|
const rows = [], headers = []
|
|
17
18
|
|
|
18
19
|
let val, values=[]
|
|
@@ -21,7 +21,7 @@ const mpSupportsEmptyLocale = () => {
|
|
|
21
21
|
const metadata = service => {
|
|
22
22
|
return async (odataReq, odataRes, next) => {
|
|
23
23
|
const req = odataReq.getIncomingRequest()
|
|
24
|
-
const tenant = req.
|
|
24
|
+
const tenant = req.tenant ?? req.user?.tenant
|
|
25
25
|
// REVISIT: can we take locale from user, or is there some odata special wrt metadata?
|
|
26
26
|
const locale = odataRes.getContract().getLocale()
|
|
27
27
|
|
|
@@ -212,7 +212,14 @@ class TrustedResourceJsonSerializer {
|
|
|
212
212
|
_serializeEntity (result, entityType, data, expandItems, odataPath, structurePath) {
|
|
213
213
|
this._serializeAnnotations(result, data, MetaProperties.ETAG, MetaProperties.CONTEXT)
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
const { okra_skip_query_options, odata_new_parser } = cds.env.features
|
|
216
|
+
if (okra_skip_query_options && odata_new_parser) {
|
|
217
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
218
|
+
if (!key.startsWith('*')) result[key] = value
|
|
219
|
+
})
|
|
220
|
+
} else {
|
|
221
|
+
this._serializeStructure(result, entityType, data, expandItems, odataPath, structurePath || [])
|
|
222
|
+
}
|
|
216
223
|
|
|
217
224
|
// Add annotation '"@odata.id": null' for an entity of a transient type if some properties have been
|
|
218
225
|
// aggregated away or if not all of the base type's properties have been serialized.
|
|
@@ -782,14 +782,14 @@ const findQueryTarget = q => {
|
|
|
782
782
|
return q.SELECT && q.SELECT._transitions
|
|
783
783
|
? q.SELECT._transitions[q.SELECT._transitions.length - 1].target
|
|
784
784
|
: q.INSERT
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
785
|
+
? q.INSERT._transitions[q.INSERT._transitions.length - 1].target
|
|
786
|
+
: q.UPDATE
|
|
787
|
+
? q.UPDATE._transitions[q.UPDATE._transitions.length - 1].target
|
|
788
|
+
: q.UPSERT
|
|
789
|
+
? q.UPSERT._transitions[q.UPSERT._transitions.length - 1].target
|
|
790
|
+
: q.DELETE
|
|
791
|
+
? q.DELETE._transitions[q.DELETE._transitions.length - 1].target
|
|
792
|
+
: undefined
|
|
793
793
|
}
|
|
794
794
|
|
|
795
795
|
module.exports = {
|
|
@@ -1282,10 +1282,10 @@ class JoinCQNFromExpanded {
|
|
|
1282
1282
|
element.ref[0] === alias
|
|
1283
1283
|
? [...element.ref]
|
|
1284
1284
|
: element.ref.length === 1
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1285
|
+
? [alias, element.ref[0]]
|
|
1286
|
+
: this._isPathExpressionToOne(element.ref, expandedEntity)
|
|
1287
|
+
? [alias, ...element.ref]
|
|
1288
|
+
: [alias, element.ref[1]]
|
|
1289
1289
|
|
|
1290
1290
|
return (sort && { ref, sort }) || { ref }
|
|
1291
1291
|
})
|
|
@@ -97,10 +97,10 @@ class RawToExpanded {
|
|
|
97
97
|
? null
|
|
98
98
|
: !!entry[mappings.IsActiveEntity]
|
|
99
99
|
: 'IsActiveEntity' in entry
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
? entry.IsActiveEntity === null
|
|
101
|
+
? null
|
|
102
|
+
: !!entry.IsActiveEntity
|
|
103
|
+
: null
|
|
104
104
|
: null
|
|
105
105
|
|
|
106
106
|
// A raw row contains more elements than the config. Iterating over config is faster.
|
|
@@ -112,8 +112,8 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
112
112
|
const _etagValidationType = req.headers['if-match']
|
|
113
113
|
? 'if-match'
|
|
114
114
|
: req.headers['if-none-match']
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
? 'if-none-match'
|
|
116
|
+
: undefined
|
|
117
117
|
|
|
118
118
|
const query = _cleansed(req.query, this.model)
|
|
119
119
|
_cleanseParams(req.params, req.target)
|
|
@@ -181,23 +181,23 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
181
181
|
const read = req.query._target.name.endsWith('.drafts')
|
|
182
182
|
? Read.ownDrafts
|
|
183
183
|
: draftParams.IsActiveEntity === false && draftParams.SiblingEntity_IsActiveEntity === null
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
184
|
+
? Read.all
|
|
185
|
+
: draftParams.IsActiveEntity === true &&
|
|
186
|
+
draftParams.SiblingEntity_IsActiveEntity === null &&
|
|
187
|
+
(draftParams.DraftAdministrativeData_InProcessByUser === 'not null' ||
|
|
188
|
+
draftParams.DraftAdministrativeData_InProcessByUser === 'not ')
|
|
189
|
+
? Read.lockedByAnotherUser
|
|
190
|
+
: draftParams.IsActiveEntity === true &&
|
|
191
|
+
draftParams.SiblingEntity_IsActiveEntity === null &&
|
|
192
|
+
draftParams.DraftAdministrativeData_InProcessByUser === ''
|
|
193
|
+
? Read.unsavedChangesByAnotherUser
|
|
194
|
+
: draftParams.IsActiveEntity === true && draftParams.HasDraftEntity === false
|
|
195
|
+
? Read.unchanged
|
|
196
|
+
: draftParams.IsActiveEntity === true
|
|
197
|
+
? Read.onlyActives
|
|
198
|
+
: draftParams.IsActiveEntity === false
|
|
199
|
+
? Read.ownDrafts
|
|
200
|
+
: Read.onlyActives
|
|
201
201
|
const result = await read(run, query)
|
|
202
202
|
return result
|
|
203
203
|
}
|
|
@@ -218,18 +218,27 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
218
218
|
|
|
219
219
|
if (req.event === 'DELETE' && draftParams.IsActiveEntity) {
|
|
220
220
|
const draftsRef = _redirectRefToDrafts(query.DELETE.from.ref, this.model)
|
|
221
|
-
const
|
|
221
|
+
const draftQuery = SELECT.one.from({ ref: draftsRef }).columns([
|
|
222
222
|
{ ref: ['DraftAdministrativeData_DraftUUID'] },
|
|
223
223
|
{
|
|
224
224
|
ref: ['DraftAdministrativeData'],
|
|
225
225
|
expand: [_inProcessByUserXpr(_lock.shiftedNow)]
|
|
226
226
|
}
|
|
227
227
|
])
|
|
228
|
+
|
|
229
|
+
// Deletion of active instance outside draft tree, no need to check for draft
|
|
230
|
+
if (!draftQuery.target?.isDraft) {
|
|
231
|
+
await run(query)
|
|
232
|
+
return req.data
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Deletion of active instance inside draft tree, need to check that no draft exists
|
|
236
|
+
const draft = await draftQuery
|
|
228
237
|
const inProcessByUser = draft?.DraftAdministrativeData?.InProcessByUser
|
|
229
238
|
if (inProcessByUser && inProcessByUser !== cds.context.user.id)
|
|
230
239
|
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [inProcessByUser])
|
|
231
240
|
if (draft) req.reject(403, 'DRAFT_ACTIVE_DELETE_FORBIDDEN_DRAFT_EXISTS')
|
|
232
|
-
await run(
|
|
241
|
+
await run(query)
|
|
233
242
|
return req.data
|
|
234
243
|
}
|
|
235
244
|
|
|
@@ -387,8 +387,8 @@ const getReqOptions = (req, query, service) => {
|
|
|
387
387
|
typeof query === 'object'
|
|
388
388
|
? _cqnToReqOptions(query, service, req)
|
|
389
389
|
: typeof query === 'string'
|
|
390
|
-
|
|
391
|
-
|
|
390
|
+
? _stringToReqOptions(query, req.data, req.target)
|
|
391
|
+
: _pathToReqOptions(req.method, req.path, req.data, req.target, service.name)
|
|
392
392
|
|
|
393
393
|
if (service.kind === 'odata-v2' && req.event === 'READ' && reqOptions.url?.match(/(\/any\()|(\/all\()/)) {
|
|
394
394
|
req.reject(501, 'Lambda expressions are not supported in OData v2')
|
|
@@ -173,14 +173,15 @@ function _processWhere(where, entity) {
|
|
|
173
173
|
function _convertVal(element, value) {
|
|
174
174
|
if (value === null) return value
|
|
175
175
|
switch (element._type) {
|
|
176
|
-
case 'cds.Integer':
|
|
177
176
|
case 'cds.UInt8':
|
|
177
|
+
case 'cds.Integer':
|
|
178
178
|
case 'cds.Int16':
|
|
179
179
|
case 'cds.Int32':
|
|
180
|
-
if (
|
|
180
|
+
if (!/^-?\d+$/.test(value)) throw new Error('Not a valid integer')
|
|
181
181
|
// eslint-disable-next-line no-case-declarations
|
|
182
182
|
const n = Number(value)
|
|
183
183
|
if (!Number.isSafeInteger(n)) throw new Error('Not a valid integer')
|
|
184
|
+
if (element._type === 'cds.UInt8' && n < 0) throw new Error('Not a positive integer')
|
|
184
185
|
return n
|
|
185
186
|
|
|
186
187
|
case 'cds.String':
|