From 27bb0ef07c8c5361f67005254096bdcb106d862c Mon Sep 17 00:00:00 2001 From: Christian Georgi Date: Tue, 12 Dec 2023 12:10:51 +0100 Subject: [PATCH] Small fixes and setup API extractor (#6) * Fix missing type by alias * Avoid `export {* as ql} from...` syntax * Skip release tags for now * Use type indirection for cds type * Fix TSDoc comments * Add missing param name * Fix fluent type, add test * Export more types --------- Co-authored-by: Daniel O'Grady <103028279+daogrady@users.noreply.github.com> --- .gitignore | 10 + api-extractor.json | 449 +++++++++++++++++++ apis/cds.d.ts | 12 +- apis/core.d.ts | 9 + apis/csn.d.ts | 4 +- apis/events.d.ts | 2 +- apis/linked.d.ts | 26 +- apis/log.d.ts | 33 +- apis/models.d.ts | 4 +- apis/server.d.ts | 40 +- apis/services.d.ts | 3 +- apis/test.d.ts | 24 +- package.json | 4 +- test/typescript/apis/project/cds-services.ts | 6 +- 14 files changed, 561 insertions(+), 65 deletions(-) create mode 100644 .gitignore create mode 100644 api-extractor.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..84e96210 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +node_modules/ +gen/ +out/ + +# API extractor + +dist/ +temp/ +etc/ +tsdoc-metadata.json diff --git a/api-extractor.json b/api-extractor.json new file mode 100644 index 00000000..8adb7bdc --- /dev/null +++ b/api-extractor.json @@ -0,0 +1,449 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + "projectFolder": ".", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "/apis/cds.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we can specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + */ + "bundledPackages": [], + + /** + * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files + * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. + * To use the OS's default newline kind, specify "os". + * + * DEFAULT VALUE: "crlf" + */ + // "newlineKind": "crlf", + + /** + * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the + * .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests. + * + * DEFAULT VALUE: "false" + */ + // "testMode": false, + + /** + * Specifies how API Extractor sorts members of an enum when generating the .api.json file. By default, the output + * files will be sorted alphabetically, which is "by-name". To keep the ordering in the source code, specify + * "preserve". + * + * DEFAULT VALUE: "by-name" + */ + // "enumMemberOrder": "by-name", + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + // "tsconfigFilePath": "/tsconfig.json", + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + "overrideTsconfig": { + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "types": [ + "node" + ], + "lib": [ + "es2021" + ], + }, + "exclude": [ + "node_modules", + "dist", + "test" + ] + }, + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": true + + /** + * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce + * a full file path. + * + * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: ".api.md" + */ + // "reportFileName": ".api.md", + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportFolder": "/temp/", + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/", + + /** + * Whether "forgotten exports" should be included in the API report file. Forgotten exports are declarations + * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to + * learn more. + * + * DEFAULT VALUE: "false" + */ + // , "includeForgottenExports": true + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": true + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + // "apiJsonFilePath": "/temp/.api.json", + + /** + * Whether "forgotten exports" should be included in the doc model file. Forgotten exports are declarations + * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to + * learn more. + * + * DEFAULT VALUE: "false" + */ + // "includeForgottenExports": false, + + /** + * The base URL where the project's source code can be viewed on a website such as GitHub or + * Azure DevOps. This URL path corresponds to the `` path on disk. + * + * This URL is concatenated with the file paths serialized to the doc model to produce URL file paths to individual API items. + * For example, if the `projectFolderUrl` is "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor" and an API + * item's file path is "api/ExtractorConfig.ts", the full URL file path would be + * "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor/api/ExtractorConfig.js". + * + * Can be omitted if you don't need source code links in your API documentation reference. + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "projectFolderUrl": "http://github.com/path/to/your/projectFolder" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": true + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + // "untrimmedFilePath": "/dist/.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. + * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "alphaTrimmedFilePath": "/dist/-alpha.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + // "enabled": true, + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // ,"addToApiReportFile": true + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + }, + // for now, everything is public + // FIXME use release tags + "ae-missing-release-tag": { + "logLevel": "none" + } + + // "ae-extra-release-tag": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/apis/cds.d.ts b/apis/cds.d.ts index 4f9da62c..8e60d050 100644 --- a/apis/cds.d.ts +++ b/apis/cds.d.ts @@ -6,11 +6,16 @@ export * from './services' export * from './events' export * from './utils' export { log, debug } from './log' -// FIXME: rename clashes to Linked.* -// export * from './csn' export { test } from './test' export * from './cqn' -export * as ql from './ql' + +// FIXME: sort out what needs to be exported from csn/linked and under which namespace +// export { Association, CSN, Definition, Extension, Element, EntityElements, FQN, kinds } from './csn' +// export { Definitions, LinkedCSN, LinkedDefinition, LinkedAssociation, LinkedEntity, Filter, Visitor } from './linked' + +// API extractor cannot handle export * as ql from './ql', so split it into an import and an export statement +import * as ql from './ql' +export { ql } export { QLExtensions } from './ql' // cds-ql.ts test tries to import this from top level? Correct? Or ql.QLExtensions? // trick to work around "delete" as reserved identifier @@ -18,7 +23,6 @@ import { Service } from './services' declare const delete_: Service['delete'] export { delete_ as delete } -import * as ql from './ql' declare global { // these provide the functionality from SELECT, INSERT, etc in the global facade const SELECT: ql.QL['SELECT'] diff --git a/apis/core.d.ts b/apis/core.d.ts index ea9f7e71..76691b8d 100644 --- a/apis/core.d.ts +++ b/apis/core.d.ts @@ -49,10 +49,13 @@ export const builtin: { /** * Add aspects to a given object, for example: * + * @example + * ```js * extend (Object.prototype) .with (class { * get foo() { return ... } * bar() {...} * }.prototype) + * ``` */ export function extend(target: T): { with(...ext: E): T & Intersect @@ -62,9 +65,12 @@ export function extend(target: T): { * Equip a given facade object with getters for lazy-loading modules instead * of static requires. Example: * + * @example + * ```js * const facade = lazify ({ * sub: lazy => require ('./sub-module') * }) + * ``` * * The first usage of `facade.sub` will load the sub module * using standard Node.js's `module.require` functions. @@ -75,10 +81,13 @@ export function lazify (target: T) : T * Prepare a node module for lazy-loading submodules instead * of static requires. Example: * + * @example + * ```js * require = lazify (module) //> turns require into a lazy one * const facade = module.exports = { * sub: require ('./sub-module') * }) + * ``` * * The first usage of `facade.sub` will load the sub module * using standard Node.js's `module.require` functions. diff --git a/apis/csn.d.ts b/apis/csn.d.ts index caf3fa16..825ea04b 100644 --- a/apis/csn.d.ts +++ b/apis/csn.d.ts @@ -46,7 +46,7 @@ export type Definition = context & service & type & struct & entity & Associatio // NOTE: If we use & instead of | CSN.definitions values would be reduced to /** - * Extensions capture extend Foo with { ... } directives. + * Extensions capture `extend Foo with { ... }` directives. */ export type Extension = { extend: FQN @@ -103,7 +103,7 @@ export interface Association extends type { type: 'cds.Association' | 'cds.Composition' target: FQN /** - * The specified cardinality. to-one = {max:1}, to-many = {max:'*'} + * The specified cardinality. to-one = `{max:1}`, to-many = `{max:'*'}` */ cardinality?: { src?: 1; min?: 1 | 0; max?: 1 | '*' } /** diff --git a/apis/events.d.ts b/apis/events.d.ts index c7d8237d..811171a7 100644 --- a/apis/events.d.ts +++ b/apis/events.d.ts @@ -37,7 +37,7 @@ export class Request extends Event { path: string target: LinkedDefinition /** - * Shortcut to {@link target.name} + * Shortcut to {@link Request.target | target (entity) name} * @see https://cap.cloud.sap/docs/node.js/events#req-entity */ entity: string diff --git a/apis/linked.d.ts b/apis/linked.d.ts index 03c3fe01..59a9a181 100644 --- a/apis/linked.d.ts +++ b/apis/linked.d.ts @@ -26,10 +26,12 @@ export interface LinkedCSN extends CSN { /** * Fetches definitions matching the given filter, returning an iterator on them. * @example - * let m = cds.reflect (aParsedModel) - * for (let d of m.each('entity')) console.log (d.kind, d.name) - * let entities = [...m.each('entity')] //> capture all - * let entities = m.all('entity') //> equivalent shortcut + * ```js + * let m = cds.reflect (aParsedModel) + * for (let d of m.each('entity')) console.log (d.kind, d.name) + * let entities = [...m.each('entity')] //> capture all + * let entities = m.all('entity') //> equivalent shortcut + * ``` */ each(x: Filter, defs?: Definitions): IterableIterator @@ -43,8 +45,8 @@ export interface LinkedCSN extends CSN { * Fetches definitions matching the given filter, returning the first match, if any. * @example * let service = model.find('service') - * @param {Filter} [x] the filter - * @param {Definitions} [defs] the definitions to fetch in, default: `this.definitions` + * @param x - the filter + * @param defs - the definitions to fetch in, default: `this.definitions` */ find(x: Filter, defs?: Definitions): any @@ -67,10 +69,12 @@ export interface LinkedCSN extends CSN { * It fetches all definitions whose fully-qualified names start with the parent's name. * Returns the found definitions as an object with the local names as keys. * @example - * let service = model.find ('service') - * let entities = m.childrenOf (service) - * @param parent either the parent itself or its fully-qualified name - * @param filter an optional filter to apply before picking a child + * ```js + * let service = model.find ('service') + * let entities = m.childrenOf (service) + * ``` + * @param parent - either the parent itself or its fully-qualified name + * @param filter - an optional filter to apply before picking a child */ childrenOf(parent: any | string, filter?: ((def: LinkedDefinition) => boolean)): Definitions @@ -80,6 +84,7 @@ export interface LinkedCSN extends CSN { * working with fully-qualified names as follows: * * @example + * ```js * let model = cds.reflect (cds.parse(` * namespace our.lovely.bookshop; * entity Books {...} @@ -87,6 +92,7 @@ export interface LinkedCSN extends CSN { * `)) * const {Books,Authors} = model.exports * SELECT.from (Books) .where ({ID:11}) + * ``` */ exports: Definitions & ((namespace: string) => Definitions) entities: Definitions & ((namespace: string) => Definitions) diff --git a/apis/log.d.ts b/apis/log.d.ts index 132d54d7..d1cc5800 100644 --- a/apis/log.d.ts +++ b/apis/log.d.ts @@ -7,13 +7,14 @@ export declare const log: LogFactory /** * Shortcut to `cds.log(...).debug`, returning `undefined` if `cds.log(...)._debug` is `false`. * Use like this: - * ``` + * @example + * ```js * const dbg = cds.debug('foo') * ... * dbg && dbg('message') * ``` * - * @param name logger name + * @param name - logger name */ export declare function debug(name: string): undefined | Log @@ -26,23 +27,23 @@ declare type LogFactory = { * * By default this logger would prefix all output with `[sql] - ` * You can change this by specifying another prefix in the options: - * - * ``` + * @example + * ```js * const LOG = cds.log('sql|db', { prefix: 'cds.ql' }) * ``` * * Call `cds.log()` for a given module again to dynamically change the log level * of all formerly created loggers, for example: - * - * ``` + * @example + * ```js * const LOG = cds.log('sql') * LOG.info ('this will show, as default level is info') * cds.log('sql', 'warn') * LOG.info('this will be suppressed now') * ``` * - * @param name logger name - * @param options level, label and prefix + * @param name - logger name + * @param options - level, label and prefix * @returns the logger * @see [capire](https://cap.cloud.sap/docs/node.js/cds-log) */ @@ -50,17 +51,17 @@ declare type LogFactory = { /** * Set a custom formatter function like that: - * ``` + * ```js * cds.log.format = (module, level, ...args) => [ '[', module, ']', ...args ] * ``` * * The formatter shall return an array of arguments, which are passed to the logger (for example, `console.log()`) */ format: Formatter - + /** * Set a custom logger. - * ``` + * ```js * cds.log.Logger = ... * ``` */ @@ -140,9 +141,9 @@ declare type Formatter = { /** * Custom format function * - * @param module logger name - * @param level log level - * @param args additional arguments + * @param module - logger name + * @param level - log level + * @param args - additional arguments * @returns an array of arguments, which are passed to the logger (for example, `console.log()`) */ (module: string, level: number, args: any[]): any[] @@ -152,8 +153,8 @@ declare type Log = { /** * Logs a message * - * @param message text to log - * @param optionalParams additional parameters, same as in `console.log(text, param1, ...)` + * @param message - text to log + * @param optionalParams - additional parameters, same as in `console.log(text, param1, ...)` */ (message?: any, ...optionalParams: any[]): void } diff --git a/apis/models.d.ts b/apis/models.d.ts index e9a3d7ab..9ebb225d 100644 --- a/apis/models.d.ts +++ b/apis/models.d.ts @@ -52,13 +52,13 @@ export const parse : { * Loads and parses models from the specified files. * Uses `cds.resolve` to fetch the respective models. * Essentially a shortcut for `cds.compile.to.csn(files)` - * @param {string} files - filenames of models or if folder containing models + * @param files - filenames of models or if folder containing models */ export function get (files: '*' | filename | filename[], o?:_options): Promise /** * Shortcut for `cds.get(files, 'inferred')` - * @param {string} files - filenames of models or if folder containing models + * @param files - filenames of models or if folder containing models */ export function load (files: '*' | filename | filename[], o?:_options): Promise diff --git a/apis/server.d.ts b/apis/server.d.ts index dc2db483..391db885 100644 --- a/apis/server.d.ts +++ b/apis/server.d.ts @@ -4,6 +4,8 @@ import * as http from "http" import * as cds from './cds' import { Application } from "express" +type _cds = typeof cds + export const connect: { /** * Connects to a specific datasource. @@ -21,7 +23,8 @@ import { Application } from "express" * Connects the primary datasource. * @see [capire](https://cap.cloud.sap/docs/node.js/cds-connect) */ - (options?: string | cds_connect_options): Promise //> cds.connect() + // API extractor cannot handle the direct usages of the cds namespace in typeof cds, so add an indirection. + (options?: string | cds_connect_options): Promise<_cds> //> cds.connect() } /** @@ -44,13 +47,12 @@ import { Application } from "express" */ // FIXME: this is actually supposed to be part of models.d.ts // but had to be moved here so export * would not clash their definitions - export function on (event : 'loaded', listener : (model : CSN) => void): typeof cds + export function on (event : 'loaded', listener : (model : CSN) => void): _cds - /** * Emitted whenever a specific service is connected for the first time. */ - export function on(event: 'connect', listener: (srv: Service) => void): typeof cds + export function on(event: 'connect', listener: (srv: Service) => void): _cds /** @@ -58,34 +60,34 @@ import { Application } from "express" * express application has been constructed but no middlewares or routes * added yet. */ - export function on (event : 'bootstrap', listener : (app : Application) => void) : typeof cds - export function once (event : 'bootstrap', listener : (app : Application) => void) : typeof cds + export function on (event : 'bootstrap', listener : (app : Application) => void) : _cds + export function once (event : 'bootstrap', listener : (app : Application) => void) : _cds /** * Emitted for each service served by cds.serve(). */ - export function on (event : 'serving', listener : (srv : Service) => void) : typeof cds + export function on (event : 'serving', listener : (srv : Service) => void) : _cds /** * Emitted by the default, built-in `server.js` when all services are * constructed and mounted by cds.serve(). */ - export function on (event : 'served', listener : (all : cds_services) => void) : typeof cds - export function once (event : 'served', listener : (all : cds_services) => void) : typeof cds + export function on (event : 'served', listener : (all : cds_services) => void) : _cds + export function once (event : 'served', listener : (all : cds_services) => void) : _cds /** * Emitted by the default, built-in `server.js` when the http server * is started and listening for incoming requests. */ - export function on (event : 'listening', listener : (args : { server: http.Server, url:string }) => void) : typeof cds - export function once (event : 'listening', listener : (args : { server: http.Server, url:string }) => void) : typeof cds + export function on (event : 'listening', listener : (args : { server: http.Server, url:string }) => void) : _cds + export function once (event : 'listening', listener : (args : { server: http.Server, url:string }) => void) : _cds /** * Emitted by the default, built-in `server.js` when the http server * is shutdown. */ - export function on (event : 'shutdown', listener : () => void) : typeof cds - export function once (event : 'shutdown', listener : () => void) : typeof cds + export function on (event : 'shutdown', listener : () => void) : _cds + export function once (event : 'shutdown', listener : () => void) : _cds /** * Dictionary of all services constructed and/or connected. @@ -112,7 +114,7 @@ export type service = { * Use that in modules to get IntelliSense. */ impl (impl: ServiceImpl) : typeof impl - // impl (srv:T, impl: ( typeof cds: T, srv: (T) ) => any) : typeof impl + // impl (srv:T, impl: ( _cds: T, srv: (T) ) => any) : typeof impl /** * Array of all services constructed. @@ -124,11 +126,11 @@ export type service = { type cds_services = { [name:string]: Service } interface cds_serve_fluent { - from (model : string | CSN) : typeof cds - to (protocol: string) : typeof cds - at (path: string) : typeof cds - in (app: Application) : typeof cds - with (impl: ServiceImpl | string) : typeof cds + from (model : string | CSN) : cds_serve_fluent + to (protocol: string) : cds_serve_fluent + at (path: string) : cds_serve_fluent + in (app: Application) : cds_serve_fluent + with (impl: ServiceImpl | string) : cds_serve_fluent // (req,res) : void } diff --git a/apis/services.d.ts b/apis/services.d.ts index c51fd406..2cf07972 100644 --- a/apis/services.d.ts +++ b/apis/services.d.ts @@ -1,10 +1,11 @@ import { SELECT, INSERT, UPDATE, DELETE, Query, ConstructedQuery, UPSERT } from './ql' import { Awaitable } from './ql' import { ArrayConstructable, Constructable } from './internal/inference' -import { LinkedCSN, LinkedDefinition, LinkedDefinitions, LinkedEntity } from './linked' +import { LinkedCSN, LinkedDefinition, Definitions as LinkedDefinitions, LinkedEntity } from './linked' import { CSN } from './csn' import { EventContext } from './events' import { Request } from './events' +import { ReadableStream } from 'node:stream/web' export class QueryAPI { diff --git a/apis/test.d.ts b/apis/test.d.ts index 10efe6aa..c2af5d46 100644 --- a/apis/test.d.ts +++ b/apis/test.d.ts @@ -2,6 +2,9 @@ import { AxiosInstance } from 'axios'; import chai from 'chai'; import * as http from 'http'; import { Service } from './services'; +import * as cds from './cds' + +type _cds = typeof cds declare class Axios { get axios(): AxiosInstance; @@ -24,7 +27,10 @@ declare class Axios { declare class DataUtil { delete(db?: Service): Promise; reset(db?: Service): Promise; - /** @deprecated */ autoReset(enabled: boolean): this; + /** + * @deprecated if needed, call `reset()`, considering test performance + */ + autoReset(enabled: boolean): this; } declare class Test extends Axios { @@ -34,13 +40,16 @@ declare class Test extends Axios { run(cmd: string, ...args: string[]): this; in(...paths: string[]): this; silent(): this; - /** @deprecated */ verbose(v: boolean): this; + /** + * @deprecated Server log is shown by default. Use `log()` to get control over it. + */ + verbose(v: boolean): this; get chai(): typeof chai; get expect(): typeof chai.expect; get assert(): typeof chai.assert; get data(): DataUtil; - get cds(): typeof import('./cds') + get cds(): _cds log() : { output: string @@ -66,12 +75,13 @@ declare class Test extends Axios { declare const test: { Test: typeof Test /** - * @see [capire docs](https://cap.cloud.sap/docs/node.js/cds-test?q=cds.test#run) + * @see [capire docs](https://cap.cloud.sap/docs/node.js/cds-test#class-cds-test-test) */ - (projectDir: string): Test; + (dirname: string): Test; /** - * @see [capire docs](https://cap.cloud.sap/docs/node.js/cds-test?q=cds.test#run-2) + * @see [capire docs](https://cap.cloud.sap/docs/node.js/cds-test#class-cds-test-test) */ (command: string, ...args: string[]): Test; - in (string) : Test + + in (dirname: string) : Test } diff --git a/package.json b/package.json index e70453e2..bb7317eb 100644 --- a/package.json +++ b/package.json @@ -18,13 +18,15 @@ "README.md" ], "scripts": { - "test": "jest --silent" + "test": "jest --silent", + "api-extractor": "api-extractor run --local --verbose" }, "peerDependencies": { "@sap/cds": ">=7" }, "devDependencies": { "@sap/cds": "^7.4.0", + "@types/node": "^20", "axios": "^1.6.2", "chai": "^4.3.10", "jest": "^29.7.0", diff --git a/test/typescript/apis/project/cds-services.ts b/test/typescript/apis/project/cds-services.ts index d8172e37..d2217ff2 100644 --- a/test/typescript/apis/project/cds-services.ts +++ b/test/typescript/apis/project/cds-services.ts @@ -34,6 +34,7 @@ cds.serve('SomeService', { service: '*' }) cds.serve('SomeService', { from: '*' }) cds.serve('SomeService', { service: '*', from: '*' }) cds.serve('SomeService', { someOtherOption: true }) +cds.serve('SomeService').at('path').from('model').in('dir').to('protocol') // CRUD await srv.read(Books, 'ID') @@ -228,13 +229,14 @@ cds.on('bootstrap', (app): void => { app.use(proxy({ port: process.env.PORT })) }) -cds.on('shutdown', () => { +.on('shutdown', () => { console.log("shutdown") }) -cds.once('shutdown', () => { +.once('shutdown', () => { console.log("shutdown") }) + // cds.context.http if (cds.context?.http) { const { req , res } = cds.context.http