From 7037365d04318901f52ec979c0863ce3260f1f4c Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Thu, 21 Aug 2025 08:01:44 +0200 Subject: [PATCH 01/12] Add typescript --- package.json | 7 +++++-- tsconfig.json | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tsconfig.json diff --git a/package.json b/package.json index 327d5be..a7a2bf8 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ ], "scripts": { "test": "npx jest --silent", - "lint": "npx eslint ." + "lint": "npx eslint .", + "check:types": "npx tsc --noEmit" }, "dependencies": { "pluralize": "^8.0.0" @@ -31,7 +32,9 @@ "@sap/cds": ">=7.6" }, "devDependencies": { + "@types/node": "^24.3.0", + "eslint": "^8.56.0", "jest": ">=29", - "eslint": "^8.56.0" + "typescript": "^5.9.2" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..50ebd8b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,32 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + //"rootDir": "./lib/compile", + "module": "nodenext", + "target": "esnext", + "lib": ["esnext"], + "types": ["node"], + "allowJs": true, + "checkJs": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noImplicitOverride": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noPropertyAccessFromIndexSignature": true, + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + // enable after first round + "noImplicitReturns": false, + "noImplicitAny": false, + }, + "exclude": [ + "**/*.test.js" + ] +} From 2f195af85fd376e41f9cfeb7ae425718cbf84bcb Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Thu, 21 Aug 2025 13:01:38 +0200 Subject: [PATCH 02/12] Types for schema --- lib/compile/csdl2openapi.js | 98 ++++++++++++++++++++----------------- lib/compile/types.d.ts | 41 ++++++++++++++++ 2 files changed, 95 insertions(+), 44 deletions(-) create mode 100644 lib/compile/types.d.ts diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index d09d545..42cb984 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -3,7 +3,7 @@ */ const cds = require('@sap/cds'); var pluralize = require('pluralize') -const DEBUG = cds.debug('openapi'); // Initialize cds.debug with the 'openapi' +const DEBUG = cds.debug('openapi'); // Initialize cds.debug with the 'openapi' //TODO @@ -104,7 +104,8 @@ module.exports.csdl2openapi = function ( const namespace = { 'Edm': 'Edm' }; const namespaceUrl = {}; const voc = {}; - const requiredSchemas = { list: [], used: {} }; + /** @type {{ list: { namespace:string, name: string, suffix: string }[], used: Record }} */ + const requiredSchemas = { list: [], used: {} }; preProcess(csdl, boundOverloads, derivedTypes, alias, namespace, namespaceUrl, voc); @@ -171,14 +172,14 @@ module.exports.csdl2openapi = function ( } let extensionEnums = { "x-sap-compliance-level": {allowedValues: ["sap:base:v1", "sap:core:v1", "sap:core:v2" ] } , - "x-sap-api-type": {allowedValues: [ "ODATA", "ODATAV4", "REST" , "SOAP"] }, + "x-sap-api-type": {allowedValues: [ "ODATA", "ODATAV4", "REST" , "SOAP"] }, "x-sap-direction": {allowedValues: ["inbound", "outbound", "mixed"] , default : "inbound" }, "x-sap-dpp-entity-semantics": {allowedValues: ["sap:DataSubject", "sap:DataSubjectDetails", "sap:Other"] }, "x-sap-dpp-field-semantics": {allowedValues: ["sap:DataSubjectID", "sap:ConsentID", "sap:PurposeID", "sap:ContractRelatedID", "sap:LegalEntityID", "sap:DataControllerID", "sap:UserID", "sap:EndOfBusinessDate", "sap:BlockingDate", "sap:EndOfRetentionDate"] }, }; checkForExtentionEnums(extensionObj, extensionEnums); - let extenstionSchema = { + let extenstionSchema = { "x-sap-stateInfo": ['state', 'deprecationDate', 'decomissionedDate', 'link'], "x-sap-ext-overview": ['name', 'values'], "x-sap-deprecated-operation" : ['deprecationDate', 'successorOperationRef', "successorOperationId"], @@ -223,9 +224,9 @@ module.exports.csdl2openapi = function ( if (resObj[openapiProperty] === undefined) { resObj[openapiProperty] = {}; } - + let node = resObj[openapiProperty]; - + // traverse the annotation property and define the objects if they're not defined for (let nestedIndex = 1; nestedIndex < keys.length - 1; nestedIndex++) { const nestedElement = keys[nestedIndex]; @@ -234,11 +235,11 @@ module.exports.csdl2openapi = function ( } node = node[nestedElement]; } - + // set value annotation property node[keys[keys.length - 1]] = value; } - + if (!csdl.$EntityContainer) { delete openapi.servers; delete openapi.tags; @@ -2241,17 +2242,19 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Construct Schema Object for model object referencing a type - * @param {object} modelElement referencing a type + * @param {object} element referencing a type * @return {object} Schema Object */ function getSchema(element, suffix = '', forParameter = false, forFunction = false) { + + /** @type {import('./types').Schema} */ let s = {}; switch (element.$Type) { case 'Edm.AnnotationPath': case 'Edm.ModelElementPath': case 'Edm.NavigationPropertyPath': case 'Edm.PropertyPath': - s.type = 'string'; + s = { type: 'string' }; break; case 'Edm.Binary': s = { @@ -2261,7 +2264,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot if (element.$MaxLength) s.maxLength = Math.ceil(4 * element.$MaxLength / 3); break; case 'Edm.Boolean': - s.type = 'boolean'; + s = { type: 'boolean' }; break; case 'Edm.Byte': s = { @@ -2284,9 +2287,10 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot example: '2017-04-13T15:51:04' + (isNaN(element.$Precision) || element.$Precision === 0 ? '' : '.' + '0'.repeat(element.$Precision)) + 'Z' }; break; - case 'Edm.Decimal': + case 'Edm.Decimal': { + const preDecimal = /** @type {const}*/({ type: 'number', format: 'decimal' }) s = { - anyOf: [{ type: 'number', format: 'decimal' }, { type: 'string' }], + anyOf: [preDecimal, { type: 'string' }], example: 0 }; if (!isNaN(element.$Precision)) s['x-sap-precision'] = element.$Precision; @@ -2294,19 +2298,20 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot // eslint-disable-next-line no-case-declarations let scale = !isNaN(element.$Scale) ? element.$Scale : null; if (scale !== null) { - // Node.js 12.13.0 has problems with negative exponents, 10 ** -5 --> 0.000009999999999999999 + // Node.js 24 has problems with negative exponents, 10 ** -5 --> 0.000009999999999999999 if (scale <= 0) - s.anyOf[0].multipleOf = 10 ** -scale; + preDecimal.multipleOf = 10 ** -scale; else - s.anyOf[0].multipleOf = 1 / 10 ** scale; + preDecimal.multipleOf = 1 / 10 ** scale; } if (element.$Precision < 16) { let limit = 10 ** (element.$Precision - scale); let delta = 10 ** -scale; - s.anyOf[0].maximum = limit - delta; - s.anyOf[0].minimum = -s.anyOf[0].maximum; + preDecimal.maximum = limit - delta; + preDecimal.minimum = -preDecimal.maximum; } break; + } case 'Edm.Double': s = { anyOf: [{ type: 'number', format: 'double' }, { type: 'string' }], @@ -2383,11 +2388,13 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot } break; case 'Edm.String': - case undefined: - s.type = 'string'; + case undefined: { + s = { type: 'string' }; if (element.$MaxLength) s.maxLength = element.$MaxLength; - getPattern(s, element); + const pattern = element[voc.Validation.Pattern]; + if (pattern) s.pattern = pattern; break; + } case 'Edm.TimeOfDay': s = { type: 'string', @@ -2428,41 +2435,55 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot s.example = element[voc.Core.Example].Value; } + /** @returns {s is import('./types.d.ts').StringSchema} */ + const isStringSchema = s => s?.type === 'string' + /** @returns {s is import('./types.d.ts').NumberSchema} */ + const isNumberSchema = s => s?.type === 'number' || s?.type === 'integer' + /** @returns {s is import('./types.d.ts').AnyOf} */ + const isAnyOfSchema = s => Boolean(s?.anyOf) + if (forFunction) { if (s.example && typeof s.example === "string") { s.example = `${pathValuePrefix(element.$Type)}${s.example }${pathValueSuffix(element.$Type)} `; } - if (s.pattern) { - const pre = pathValuePrefix(element.$Type); - const suf = pathValueSuffix(element.$Type); - s.pattern = s.pattern.replace(/^\^/, `^ ${pre} (`); - s.pattern = s.pattern.replace(/\$$/, `)${suf} $`); - } else if (!element.$Type || element.$Type === "Edm.String") { - s.pattern = "^'([^']|'')*'$"; + if (isStringSchema(s)) { + if (s.pattern) { + const pre = pathValuePrefix(element.$Type); + const suf = pathValueSuffix(element.$Type); + s.pattern = s.pattern.replace(/^\^/, `^ ${pre} (`); + s.pattern = s.pattern.replace(/\$$/, `)${suf} $`); + } else if (!element.$Type || element.$Type === "Edm.String") { + s.pattern = "^'([^']|'')*'$"; + } } if (element.$Nullable) { s.default = "null"; - if (s.pattern) { - s.pattern = s.pattern.replace(/^\^/, "^(null|"); - s.pattern = s.pattern.replace(/\$$/, ")$"); + if (isStringSchema(s) && s.pattern) { + s.pattern = s.pattern + .replace(/^\^/, "^(null|") + .replace(/\$$/, ")$"); } } } if (element[voc.Validation.Maximum] != undefined) { if (s.$ref) s = { allOf: [s] }; - if (s.anyOf) { + if (isAnyOfSchema(s) && isNumberSchema(s.anyOf[0])) { s.anyOf[0].maximum = element[voc.Validation.Maximum]; } + // TODO: this implies that we could be handling an AnyOfSchema here. So exclusiveMinimum is attach to that? Or to its first element, as above? + // @ts-expect-error if (element[voc.Validation.Maximum + voc.Validation.Exclusive]) s.exclusiveMaximum = true; } if (element[voc.Validation.Minimum] != undefined) { if (s.$ref) s = { allOf: [s] }; - if (s.anyOf) { + if (isAnyOfSchema(s) && isNumberSchema(s.anyOf[0])) { s.anyOf[0].minimum = element[voc.Validation.Minimum]; } + // TODO: see above + // @ts-expect-error if (element[voc.Validation.Minimum + voc.Validation.Exclusive]) s.exclusiveMinimum = true; } @@ -2500,16 +2521,6 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot if (values) schema.enum = values.map(record => record.Value); } - /** - * Add pattern to Schema Object for string-like model element - * @param {object} schema Schema Object to augment - * @param {object} element Model element - */ - function getPattern(schema, element) { - const pattern = element[voc.Validation.Pattern]; - if (pattern) schema.pattern = pattern; - } - /** * Construct Reference Object for a type * @param {string} typename Qualified name of referenced type @@ -2664,5 +2675,4 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot function isIdentifier(name) { return !name.startsWith('$') && !name.includes('@'); } - }; diff --git a/lib/compile/types.d.ts b/lib/compile/types.d.ts new file mode 100644 index 0000000..1694d2b --- /dev/null +++ b/lib/compile/types.d.ts @@ -0,0 +1,41 @@ +type StringSchema = { + type: 'string' + format?: 'base64url' | 'uuid' | 'time' | 'date' | 'date-time' | 'duration' + maxLength?: number + example?: string + pattern?: string +} + +type NumberSchema = { + type: 'number' | 'integer' + format?: 'float' | 'double' | 'decimal' | 'uint8' | 'int8' | 'int16' | 'int32' | 'int64' + multipleOf?: number + example?: number, + minimum?: number + maximum?: number + exclusiveMinimum?: boolean + exclusiveMaximum?: boolean +} + +type BooleanSchema = { + type: 'boolean' +} + +type ArraySchema = { + type: 'array', + items: Schema +} + +type SingleSchema = StringSchema | NumberSchema | BooleanSchema | ArraySchema + +type AnyOf = { anyOf: Schema[] } +type AllOf = { allOf: Schema[] } +type MultiSchema = AnyOf | AllOf + +export type Schema = (SingleSchema | MultiSchema) & { + nullable?: boolean + default?: unknown + example?: string | number, + description?: string + '$ref'?: unknown +} \ No newline at end of file From 4c7e9a550248e97e5dfff3f58c898d223691b6a0 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Thu, 21 Aug 2025 13:23:26 +0200 Subject: [PATCH 03/12] More types --- lib/compile/csdl2openapi.js | 31 +++++++++++++++---------------- lib/compile/types.d.ts | 18 ++++++++++-------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 42cb984..0a8db9f 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -675,7 +675,7 @@ module.exports.csdl2openapi = function ( * @param {string} sourceName Name of path source * @param {string} targetName Name of path target * @param {string} target Target container child of path - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @param {string} navigationPath Path for finding navigation restrictions */ function pathItems(paths, prefix, prefixParameters, element, root, sourceName, targetName, target, level, navigationPath) { @@ -755,7 +755,7 @@ module.exports.csdl2openapi = function ( * @param {string} sourceName Name of path source * @param {string} targetName Name of path target * @param {string} target Target container child of path - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @param {string} navigationPath Path for finding navigation restrictions * @param {object} restrictions Navigation property restrictions of navigation segment * @param {array} nonExpandable Non-expandable navigation properties @@ -794,7 +794,7 @@ module.exports.csdl2openapi = function ( * @param {string} sourceName Name of path source * @param {string} targetName Name of path target * @param {string} target Target container child of path - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @param {object} restrictions Navigation property restrictions of navigation segment */ function operationCreate(pathItem, element, name, sourceName, targetName, target, level, restrictions) { @@ -837,7 +837,7 @@ module.exports.csdl2openapi = function ( * @param {string} operation Operation (verb) * @param {string} name Name of navigation segment * @param {string} sourceName Name of path source - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @param {boolean} collection Access a collection * @param {boolean} byKey Access by key * @return {string} Operation Text @@ -862,7 +862,7 @@ module.exports.csdl2openapi = function ( * @param {string} sourceName Name of path source * @param {string} targetName Name of path target * @param {string} target Target container child of path - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @param {object} restrictions Navigation property restrictions of navigation segment * @param {boolean} byKey Read by key * @param {array} nonExpandable Non-expandable navigation properties @@ -1016,7 +1016,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * Collect navigation paths of a navigation segment and its potentially structured components * @param {object} element Model element of navigation segment * @param {string} prefix Navigation prefix - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @return {Array} Array of navigation property paths */ function navigationPaths(element, prefix = '', level = 0) { @@ -1321,7 +1321,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * @param {string} name Name of navigation segment * @param {string} sourceName Name of path source * @param {string} target Target container child of path - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @param {object} restrictions Navigation property restrictions of navigation segment * @param {boolean} byKey Update by key */ @@ -1358,7 +1358,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * @param {string} name Name of navigation segment * @param {string} sourceName Name of path source * @param {string} target Target container child of path - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @param {object} restrictions Navigation property restrictions of navigation segment * @param {boolean} byKey Delete by key */ @@ -1383,7 +1383,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * @param {Array} prefixParameters Parameter Objects for prefix * @param {object} type Entity type object of navigation segment * @param {string} sourceName Name of path source - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @param {string} navigationPrefix Path for finding navigation restrictions */ function pathItemsWithNavigation(paths, prefix, prefixParameters, type, root, sourceName, level, navigationPrefix) { @@ -1417,7 +1417,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * @param {object} type Structured type * @param {object} map Map of navigation property paths and their types * @param {string} prefix Navigation prefix - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @return {object} Map of navigation property paths and their types */ function navigationPathMap(type, map = {}, prefix = '', level = 0) { @@ -1468,7 +1468,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Key for path item * @param {object} entityType Entity Type object - * @param {integer} level Number of navigation segments so far + * @param {number} level Number of navigation segments so far * @return {object} key: Key segment, parameters: key parameters */ function entityKey(entityType, level) { @@ -1794,7 +1794,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Construct Responses Object - * @param {string} code HTTP response code + * @param {string | number} code HTTP response code * @param {string} description Description * @param {object} type Response type object * @param {array} errors Array of operation-specific status codes with descriptions @@ -2175,7 +2175,6 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Construct OData error response - * @return {object} Error response schema */ function error() { const err = { @@ -2219,8 +2218,9 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot }, required: ['lang', 'value'] }; - delete err.properties.error.properties.details; - delete err.properties.error.properties.target; + // explicit cast required, as .details and .target are no inferred as optional above + delete /**@type{any}*/(err.properties.error.properties).details; + delete /**@type{any}*/(err.properties.error.properties).target; } return err; @@ -2228,7 +2228,6 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Construct OData count response - * @return {object} Count response schema */ function count() { return { diff --git a/lib/compile/types.d.ts b/lib/compile/types.d.ts index 1694d2b..1de8c56 100644 --- a/lib/compile/types.d.ts +++ b/lib/compile/types.d.ts @@ -26,16 +26,18 @@ type ArraySchema = { items: Schema } -type SingleSchema = StringSchema | NumberSchema | BooleanSchema | ArraySchema - -type AnyOf = { anyOf: Schema[] } -type AllOf = { allOf: Schema[] } -type MultiSchema = AnyOf | AllOf - -export type Schema = (SingleSchema | MultiSchema) & { +type Meta = { nullable?: boolean default?: unknown example?: string | number, description?: string '$ref'?: unknown -} \ No newline at end of file +} + +type SingleSchema = (StringSchema | NumberSchema | BooleanSchema | ArraySchema) & Meta + +type AnyOf = { anyOf: Schema[] } & Meta +type AllOf = { allOf: Schema[] } & Meta +type MultiSchema = AnyOf | AllOf + +export type Schema = (SingleSchema | MultiSchema) \ No newline at end of file From 3e8f3862ee9fc731ddf1190b088fb9248f4dfb88 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Thu, 21 Aug 2025 14:47:45 +0200 Subject: [PATCH 04/12] Add target restriction types --- lib/compile/csdl2openapi.js | 108 ++++++++++++++++++------------------ lib/compile/types.d.ts | 9 ++- 2 files changed, 62 insertions(+), 55 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 0a8db9f..66ddb76 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -158,10 +158,10 @@ module.exports.csdl2openapi = function ( for (const [key, value] of Object.entries(containerSchema)) { if (key.startsWith('@OpenAPI.Extensions')) { - const annotationProperties = key.split('@OpenAPI.Extensions.')[1]; + const annotationProperties = key.split('@OpenAPI.Extensions.')[1] ?? '' const keys = annotationProperties.split('.'); - if (!keys[0].startsWith("x-sap-")) { - keys[0] = (keys[0].startsWith("sap-") ? "x-" : "x-sap-") + keys[0]; + if (!keys[0]?.startsWith("x-sap-")) { + keys[0] = (keys[0]?.startsWith("sap-") ? "x-" : "x-sap-") + keys[0]; } if (keys.length === 1) { extensionObj[keys[0]] = value; @@ -241,8 +241,9 @@ module.exports.csdl2openapi = function ( } if (!csdl.$EntityContainer) { - delete openapi.servers; - delete openapi.tags; + // explicit cast required as .servers and .tags are not declared as optional + delete /**@type{any}*/(openapi).servers; + delete /**@type{any}*/(openapi).tags; } security(openapi, entityContainer); @@ -302,13 +303,14 @@ module.exports.csdl2openapi = function ( Object.keys(schema.$Annotations || {}).forEach(target => { const annotations = schema.$Annotations[target]; const segments = target.split('/'); - const open = segments[0].indexOf('('); + const firstSegment = /**@type{string}*/(segments[0]); + const open = firstSegment.indexOf('('); let element; if (open == -1) { - element = modelElement(segments[0]); + element = modelElement(firstSegment); } else { - element = modelElement(segments[0].substring(0, open)); - let args = segments[0].substring(open + 1, segments[0].length - 1); + element = modelElement(firstSegment.substring(0, open)); + let args = firstSegment.substring(open + 1, firstSegment.length - 1); element = element.find( (overload) => (overload.$Kind == "Action" && @@ -338,23 +340,21 @@ module.exports.csdl2openapi = function ( case 1: Object.assign(element, annotations); break; - case 2: + case 2: { + const secondSegment = /**@type{string}*/(segments[1]) if (['Action', 'Function'].includes(element.$Kind)) { - if (segments[1] == '$ReturnType') { + if (secondSegment === '$ReturnType') { if (element.$ReturnType) Object.assign(element.$ReturnType, annotations); } else { - const parameter = element.$Parameter.find(p => p.$Name == segments[1]); + const parameter = element.$Parameter.find(p => p.$Name == secondSegment); Object.assign(parameter, annotations); } - } else { - if (element[segments[1]]) { - Object.assign(element[segments[1]], annotations); - } else { - // DEBUG?.(`Invalid annotation target '${target}'`) - } + } else if (element[secondSegment]) { + Object.assign(element[secondSegment], annotations); } break; + } default: DEBUG?.('More than two annotation target path segments'); } @@ -674,7 +674,7 @@ module.exports.csdl2openapi = function ( * @param {object} root Root model element * @param {string} sourceName Name of path source * @param {string} targetName Name of path target - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path * @param {number} level Number of navigation segments so far * @param {string} navigationPath Path for finding navigation restrictions */ @@ -754,7 +754,7 @@ module.exports.csdl2openapi = function ( * @param {object} root Root model element * @param {string} sourceName Name of path source * @param {string} targetName Name of path target - * @param {string} target Target container child of path + * @param {null | object} target Target container child of path * @param {number} level Number of navigation segments so far * @param {string} navigationPath Path for finding navigation restrictions * @param {object} restrictions Navigation property restrictions of navigation segment @@ -793,13 +793,13 @@ module.exports.csdl2openapi = function ( * @param {string} name Name of navigation segment * @param {string} sourceName Name of path source * @param {string} targetName Name of path target - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path * @param {number} level Number of navigation segments so far * @param {object} restrictions Navigation property restrictions of navigation segment */ function operationCreate(pathItem, element, name, sourceName, targetName, target, level, restrictions) { - const insertRestrictions = restrictions.InsertRestrictions || target && target[voc.Capabilities.InsertRestrictions] || {}; - let countRestrictions = target && (target[voc.Capabilities.CountRestrictions]?.Countable === false); // count property will be added if CountRestrictions is false + const insertRestrictions = restrictions.InsertRestrictions || target?.[voc.Capabilities.InsertRestrictions] || {}; + let countRestrictions = target?.[voc.Capabilities.CountRestrictions]?.Countable === false // count property will be added if CountRestrictions is false if (insertRestrictions.Insertable !== false) { const lname = pluralize.singular(splitName(name)); const type = modelElement(element.$Type); @@ -861,7 +861,7 @@ module.exports.csdl2openapi = function ( * @param {string} name Name of navigation segment * @param {string} sourceName Name of path source * @param {string} targetName Name of path target - * @param {string} target Target container child of path + * @param {null |import('./types').TargetRestrictions[]} target Target container child of path * @param {number} level Number of navigation segments so far * @param {object} restrictions Navigation property restrictions of navigation segment * @param {boolean} byKey Read by key @@ -961,10 +961,10 @@ module.exports.csdl2openapi = function ( /** * Add parameter for query option $count * @param {Array} parameters Array of parameters to augment - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path */ function optionCount(parameters, target) { - const targetRestrictions = target && target[voc.Capabilities.CountRestrictions]; + const targetRestrictions = target?.[voc.Capabilities.CountRestrictions]; const targetCountable = target == null || targetRestrictions == null || targetRestrictions.Countable !== false; @@ -981,11 +981,11 @@ module.exports.csdl2openapi = function ( * Add parameter for query option $expand * @param {Array} parameters Array of parameters to augment * @param {object} element Model element of navigation segment - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path * @param {array} nonExpandable Non-expandable navigation properties */ function optionExpand(parameters, element, target, nonExpandable) { - const targetRestrictions = target && target[voc.Capabilities.ExpandRestrictions]; + const targetRestrictions = target?.[voc.Capabilities.ExpandRestrictions]; const supported = targetRestrictions == null || targetRestrictions.Expandable != false; if (supported) { const expandItems = ['*'].concat(navigationPaths(element).filter(path => !nonExpandable.includes(path))); @@ -1036,11 +1036,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Add parameter for query option $filter * @param {Array} parameters Array of parameters to augment - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions} target Target container child of path * @param {object} restrictions Navigation property restrictions of navigation segment */ function optionFilter(parameters, target, restrictions) { - const filterRestrictions = restrictions.FilterRestrictions || target && target[voc.Capabilities.FilterRestrictions] || {}; + const filterRestrictions = restrictions.FilterRestrictions || target?.[voc.Capabilities.FilterRestrictions] || {}; if (filterRestrictions.Filterable !== false) { const filter = { @@ -1068,11 +1068,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * Add parameter for query option $orderby * @param {Array} parameters Array of parameters to augment * @param {object} element Model element of navigation segment - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path * @param {object} restrictions Navigation property restrictions of navigation segment */ function optionOrderBy(parameters, element, target, restrictions) { - const sortRestrictions = restrictions.SortRestrictions || target && target[voc.Capabilities.SortRestrictions] || {}; + const sortRestrictions = restrictions.SortRestrictions || target?.[voc.Capabilities.SortRestrictions] || {}; if (sortRestrictions.Sortable !== false) { const nonSortable = {}; @@ -1106,7 +1106,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Unpack EnumMember value if it uses CSDL JSON CS01 style, like CAP does - * @param {string or object} path Qualified name of referenced type + * @param {string | object} member Qualified name of referenced type * @return {object} Reference Object */ function enumMember(member) { @@ -1118,7 +1118,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Unpack NavigationPropertyPath value if it uses CSDL JSON CS01 style, like CAP does - * @param {string or object} path Qualified name of referenced type + * @param {string | object} path Qualified name of referenced type * @return {object} Reference Object */ function navigationPropertyPath(path) { @@ -1130,7 +1130,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Unpack PropertyPath value if it uses CSDL JSON CS01 style, like CAP does - * @param {string or object} path Qualified name of referenced type + * @param {string | object} path Qualified name of referenced type * @return {object} Reference Object */ function propertyPath(path) { @@ -1222,11 +1222,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Add parameter for query option $search * @param {Array} parameters Array of parameters to augment - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path * @param {object} restrictions Navigation property restrictions of navigation segment */ function optionSearch(parameters, target, restrictions) { - const searchRestrictions = restrictions.SearchRestrictions || target && target[voc.Capabilities.SearchRestrictions] || {}; + const searchRestrictions = restrictions.SearchRestrictions ?? target?.[voc.Capabilities.SearchRestrictions] ?? {}; if (searchRestrictions.Searchable !== false) { if (searchRestrictions[voc.Core.Description]) { @@ -1246,11 +1246,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * Add parameter for query option $select * @param {Array} parameters Array of parameters to augment * @param {object} element Model element of navigation segment - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path * @param {object} restrictions Navigation property restrictions of navigation segment */ function optionSelect(parameters, element, target, restrictions) { - const selectSupport = restrictions.SelectSupport || target && target[voc.Capabilities.SelectSupport] || {}; + const selectSupport = restrictions.SelectSupport ?? target?.[voc.Capabilities.SelectSupport] ?? {}; if (selectSupport.Supported !== false) { const type = modelElement(element.$Type) || {}; @@ -1281,13 +1281,13 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Add parameter for query option $skip * @param {Array} parameters Array of parameters to augment - * @param {string} target Target container child of path + * @param {Record} target Target container child of path FIXME: this seems to be an incorrect use of TargetRestrictions * @param {object} restrictions Navigation property restrictions of navigation segment */ function optionSkip(parameters, target, restrictions) { const supported = restrictions.SkipSupported !== undefined ? restrictions.SkipSupported - : target == null || target[voc.Capabilities.SkipSupported] !== false; + : target == null || target?.[voc.Capabilities.SkipSupported] !== false; if (supported) { parameters.push({ @@ -1299,13 +1299,13 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Add parameter for query option $top * @param {Array} parameters Array of parameters to augment - * @param {string} target Target container child of path + * @param {Record} target Target container child of path FIXME: this seems to be an incorrect use of TargetRestrictions * @param {object} restrictions Navigation property restrictions of navigation segment */ function optionTop(parameters, target, restrictions) { const supported = restrictions.TopSupported !== undefined ? restrictions.TopSupported - : target == null || target[voc.Capabilities.TopSupported] !== false; + : target == null || target?.[voc.Capabilities.TopSupported] !== false; if (supported) { parameters.push({ @@ -1320,14 +1320,14 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * @param {object} element Model element of navigation segment * @param {string} name Name of navigation segment * @param {string} sourceName Name of path source - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path * @param {number} level Number of navigation segments so far * @param {object} restrictions Navigation property restrictions of navigation segment * @param {boolean} byKey Update by key */ - function operationUpdate(pathItem, element, name, sourceName, target, level, restrictions, byKey) { - const updateRestrictions = restrictions.UpdateRestrictions || target && target[voc.Capabilities.UpdateRestrictions] || {}; - let countRestrictions = target && (target[voc.Capabilities.CountRestrictions]?.Countable === false); + function operationUpdate(pathItem, element, name, sourceName, target, level, restrictions, byKey = false) { + const updateRestrictions = restrictions.UpdateRestrictions || target?.[voc.Capabilities.UpdateRestrictions] || {}; + let countRestrictions = target?.[voc.Capabilities.CountRestrictions]?.Countable === false; if (updateRestrictions.Updatable !== false) { const type = modelElement(element.$Type); const operation = { @@ -1357,14 +1357,14 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * @param {object} element Model element of navigation segment * @param {string} name Name of navigation segment * @param {string} sourceName Name of path source - * @param {string} target Target container child of path + * @param {null | import('./types').TargetRestrictions[]} target Target container child of path * @param {number} level Number of navigation segments so far * @param {object} restrictions Navigation property restrictions of navigation segment * @param {boolean} byKey Delete by key */ - function operationDelete(pathItem, element, name, sourceName, target, level, restrictions, byKey) { - const deleteRestrictions = restrictions.DeleteRestrictions || target && target[voc.Capabilities.DeleteRestrictions] || {}; - let countRestrictions = target && (target[voc.Capabilities.CountRestrictions]?.Countable === false); + function operationDelete(pathItem, element, name, sourceName, target, level, restrictions, byKey = false) { + const deleteRestrictions = restrictions.DeleteRestrictions || target?.[voc.Capabilities.DeleteRestrictions] || {}; + let countRestrictions = target?.[voc.Capabilities.CountRestrictions]?.Countable === false if (deleteRestrictions.Deletable !== false) { pathItem.delete = { summary: deleteRestrictions.Description || operationSummary('Deletes', name, sourceName, level, element.$Collection, byKey), @@ -1387,7 +1387,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * @param {string} navigationPrefix Path for finding navigation restrictions */ function pathItemsWithNavigation(paths, prefix, prefixParameters, type, root, sourceName, level, navigationPrefix) { - const navigationRestrictions = root[voc.Capabilities.NavigationRestrictions] || {}; + const navigationRestrictions = root[voc.Capabilities.NavigationRestrictions] ?? {}; const rootNavigable = level == 0 && enumMember(navigationRestrictions.Navigability) != 'None' || level == 1 && enumMember(navigationRestrictions.Navigability) != 'Single' || level > 1; @@ -1517,7 +1517,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Prefix for key value in key segment - * @param {typename} Qualified name of key property type + * @param {string} typename Qualified name of key property type * @return {string} value prefix */ function pathValuePrefix(typename) { @@ -1530,7 +1530,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Suffix for key value in key segment - * @param {typename} Qualified name of key property type + * @param {string} typename Qualified name of key property type * @return {string} value prefix */ function pathValueSuffix(typename) { diff --git a/lib/compile/types.d.ts b/lib/compile/types.d.ts index 1de8c56..1a8f7b9 100644 --- a/lib/compile/types.d.ts +++ b/lib/compile/types.d.ts @@ -40,4 +40,11 @@ type AnyOf = { anyOf: Schema[] } & Meta type AllOf = { allOf: Schema[] } & Meta type MultiSchema = AnyOf | AllOf -export type Schema = (SingleSchema | MultiSchema) \ No newline at end of file +export type Schema = (SingleSchema | MultiSchema) + + + +export type TargetRestrictions = { + Countable?: boolean + Expandable?: boolean +} \ No newline at end of file From f5792418f636dc368817cd5c7aa87b7270798a50 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Thu, 21 Aug 2025 15:03:14 +0200 Subject: [PATCH 05/12] Fix remaining types --- lib/compile/csdl2openapi.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 66ddb76..cdb71f6 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -902,9 +902,12 @@ module.exports.csdl2openapi = function ( customParameters(operation, byKey ? readByKeyRestrictions || readRestrictions : readRestrictions); if (collection) { + // @ts-expect-error - see FIXME in optionTop and optionSkip optionTop(operation.parameters, target, restrictions); + // @ts-expect-error optionSkip(operation.parameters, target, restrictions); if (csdl.$Version >= '4.0') optionSearch(operation.parameters, target, restrictions); + // @ts-expect-error optionFilter(operation.parameters, target, restrictions); optionCount(operation.parameters, target); optionOrderBy(operation.parameters, element, target, restrictions); @@ -1171,7 +1174,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot .map(entryToProperty({ path: prefix, typeRefChain: [] })); for (let i = 0; i < properties.length; i++) { - const property = properties[i]; + const property = /** @type{NonNullable} */(properties[i]); if (!property.isComplex) { paths.push(property.path); continue; @@ -1492,6 +1495,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot property = properties[key]; } else { parameter = Object.keys(key)[0]; + // @ts-expect-error - this can be asserted to be attached once we stricten types for getKey, etc const segments = key[parameter].split('/'); property = properties[segments[0]]; for (let i = 1; i < segments.length; i++) { @@ -1583,7 +1587,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot * @param {string} actionName Qualified name of function * @param {object} overload Function overload * @param {string} sourceName Name of path source - * @param {string} actionImport Action import + * @param {object} actionImport Action import */ function pathItemAction(paths, prefix, prefixParameters, actionName, overload, sourceName, actionImport = {}) { const name = actionName.indexOf('.') === -1 ? actionName : nameParts(actionName).name; From d1aba18c199636e2adc2fce765791e8c054e0359 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Thu, 21 Aug 2025 15:04:51 +0200 Subject: [PATCH 06/12] Add types in index.js --- lib/compile/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/compile/index.js b/lib/compile/index.js index 9c522b3..4f6cdf5 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -184,4 +184,5 @@ function _servicePath(csdl, csn, protocols) { return paths; } + return {} } \ No newline at end of file From 3118ebb5417fa72a4072f58b4efd7b8528b968dc Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Thu, 21 Aug 2025 15:11:30 +0200 Subject: [PATCH 07/12] Add workflow --- .github/workflows/type-check.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/type-check.yml diff --git a/.github/workflows/type-check.yml b/.github/workflows/type-check.yml new file mode 100644 index 0000000..9e3b21e --- /dev/null +++ b/.github/workflows/type-check.yml @@ -0,0 +1,23 @@ +name: Type Check + +on: + pull_request: + branches: + - main +jobs: + typecheck: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install dependencies + run: npm i + + - name: Run type check + run: npm run check:types \ No newline at end of file From 0d63275a0bc9f77566500fc0eca73c819e1bb3db Mon Sep 17 00:00:00 2001 From: Daniel O'Grady <103028279+daogrady@users.noreply.github.com> Date: Thu, 21 Aug 2025 17:00:59 +0200 Subject: [PATCH 08/12] Update lib/compile/csdl2openapi.js --- lib/compile/csdl2openapi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index cdb71f6..9f1d977 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -2301,7 +2301,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot // eslint-disable-next-line no-case-declarations let scale = !isNaN(element.$Scale) ? element.$Scale : null; if (scale !== null) { - // Node.js 24 has problems with negative exponents, 10 ** -5 --> 0.000009999999999999999 + // Node.js 20 has problems with negative exponents, 10 ** -5 --> 0.000009999999999999999 if (scale <= 0) preDecimal.multipleOf = 10 ** -scale; else From 02d2a342910ae19eadefe7d6933860947ce82dd2 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Thu, 21 Aug 2025 21:34:46 +0200 Subject: [PATCH 09/12] Typo --- lib/compile/csdl2openapi.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 9f1d977..7ef1b5d 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -177,20 +177,20 @@ module.exports.csdl2openapi = function ( "x-sap-dpp-entity-semantics": {allowedValues: ["sap:DataSubject", "sap:DataSubjectDetails", "sap:Other"] }, "x-sap-dpp-field-semantics": {allowedValues: ["sap:DataSubjectID", "sap:ConsentID", "sap:PurposeID", "sap:ContractRelatedID", "sap:LegalEntityID", "sap:DataControllerID", "sap:UserID", "sap:EndOfBusinessDate", "sap:BlockingDate", "sap:EndOfRetentionDate"] }, }; - checkForExtentionEnums(extensionObj, extensionEnums); + checkForExtensionEnums(extensionObj, extensionEnums); - let extenstionSchema = { + let extensionSchema = { "x-sap-stateInfo": ['state', 'deprecationDate', 'decomissionedDate', 'link'], "x-sap-ext-overview": ['name', 'values'], "x-sap-deprecated-operation" : ['deprecationDate', 'successorOperationRef', "successorOperationId"], "x-sap-odm-semantic-key" : ['name', 'values'], }; - checkForExtentionSchema(extensionObj, extenstionSchema); + checkForExtentionSchema(extensionObj, extensionSchema); return extensionObj; } - function checkForExtentionEnums(extensionObj, extensionEnums){ + function checkForExtensionEnums(extensionObj, extensionEnums){ for (const [key, value] of Object.entries(extensionObj)) { if(extensionEnums[key] && extensionEnums[key].allowedValues && !extensionEnums[key].allowedValues.includes(value)){ if(extensionEnums[key].default){ @@ -203,14 +203,14 @@ module.exports.csdl2openapi = function ( } } - function checkForExtentionSchema(extensionObj, extenstionSchema) { + function checkForExtentionSchema(extensionObj, extensionSchema) { for (const [key, value] of Object.entries(extensionObj)) { - if (extenstionSchema[key]) { + if (extensionSchema[key]) { if (Array.isArray(value)) { - extensionObj[key] = value.filter((v) => extenstionSchema[key].includes(v)); + extensionObj[key] = value.filter((v) => extensionSchema[key].includes(v)); } else if (typeof value === "object" && value !== null) { for (const field in value) { - if (!extenstionSchema[key].includes(field)) { + if (!extensionSchema[key].includes(field)) { delete extensionObj[key][field]; } } From 642d76d45740ce2b8c9adabb92fb5163739fbb91 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Fri, 22 Aug 2025 09:14:25 +0200 Subject: [PATCH 10/12] Remove noUncheckedIndexAccess config --- lib/compile/csdl2openapi.js | 8 ++++---- tsconfig.json | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 7ef1b5d..8b0505e 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -160,8 +160,8 @@ module.exports.csdl2openapi = function ( if (key.startsWith('@OpenAPI.Extensions')) { const annotationProperties = key.split('@OpenAPI.Extensions.')[1] ?? '' const keys = annotationProperties.split('.'); - if (!keys[0]?.startsWith("x-sap-")) { - keys[0] = (keys[0]?.startsWith("sap-") ? "x-" : "x-sap-") + keys[0]; + if (!keys[0].startsWith("x-sap-")) { + keys[0] = (keys[0].startsWith("sap-") ? "x-" : "x-sap-") + keys[0]; } if (keys.length === 1) { extensionObj[keys[0]] = value; @@ -303,7 +303,7 @@ module.exports.csdl2openapi = function ( Object.keys(schema.$Annotations || {}).forEach(target => { const annotations = schema.$Annotations[target]; const segments = target.split('/'); - const firstSegment = /**@type{string}*/(segments[0]); + const firstSegment = segments[0]; const open = firstSegment.indexOf('('); let element; if (open == -1) { @@ -1290,7 +1290,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot function optionSkip(parameters, target, restrictions) { const supported = restrictions.SkipSupported !== undefined ? restrictions.SkipSupported - : target == null || target?.[voc.Capabilities.SkipSupported] !== false; + : target == null || target[voc.Capabilities.SkipSupported] !== false; if (supported) { parameters.push({ diff --git a/tsconfig.json b/tsconfig.json index 50ebd8b..c14df73 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,13 @@ { // Visit https://aka.ms/tsconfig to read more about this file "compilerOptions": { - //"rootDir": "./lib/compile", "module": "nodenext", "target": "esnext", "lib": ["esnext"], "types": ["node"], "allowJs": true, "checkJs": true, - "noUncheckedIndexedAccess": true, + "noUncheckedIndexedAccess": false, "exactOptionalPropertyTypes": true, "noImplicitOverride": true, "noUnusedLocals": true, From eb89bc8494e036f568c296304f1ec1640c0d4306 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Fri, 22 Aug 2025 09:17:10 +0200 Subject: [PATCH 11/12] Remove obsolete typing --- lib/compile/csdl2openapi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 8b0505e..2ad46eb 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -1174,7 +1174,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot .map(entryToProperty({ path: prefix, typeRefChain: [] })); for (let i = 0; i < properties.length; i++) { - const property = /** @type{NonNullable} */(properties[i]); + const property = properties[i]; if (!property.isComplex) { paths.push(property.path); continue; From 63faeba8daabd925b83b89bc37047a5cc20a2dd9 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Fri, 22 Aug 2025 09:26:00 +0200 Subject: [PATCH 12/12] Remove obsolete ignore-error --- lib/compile/csdl2openapi.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 2ad46eb..50518f0 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -1495,7 +1495,6 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot property = properties[key]; } else { parameter = Object.keys(key)[0]; - // @ts-expect-error - this can be asserted to be attached once we stricten types for getKey, etc const segments = key[parameter].split('/'); property = properties[segments[0]]; for (let i = 1; i < segments.length; i++) {