Socket
Socket
Sign inDemoInstall

@sap/cds

Package Overview
Dependencies
Maintainers
1
Versions
183
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap/cds - npm Package Compare versions

Comparing version 4.4.10 to 4.5.1

apis/core.d.ts

2

apis/cds.d.ts
declare type cds_facade =
typeof import ('@sap/cds-reflect')
typeof import ('./core')
& import ('./models')

@@ -4,0 +4,0 @@ & import ('./connect')

import edm from "@sap/cds-compiler/lib/edm/edm"
import { Query, expr, _xpr } from "@sap/cds-reflect/apis/cqn"
import { CSN } from "@sap/cds-reflect/apis/csn"
import { Query, expr, _xpr } from "./cqn"
import { CSN } from "./csn"

@@ -5,0 +5,0 @@ type csn = CSN

@@ -1,3 +0,3 @@

import {Definition} from "@sap/cds-reflect/apis/csn"
import * as CQN from "@sap/cds-reflect/apis/cqn"
import {Definition} from "./csn"
import * as CQN from "./cqn"
import { STATUS_CODES } from "http"

@@ -4,0 +4,0 @@

import { Service, ServiceImpl } from "./services"
import { LinkedDefinition } from "@sap/cds-reflect/apis/reflected"
import { csn } from "@sap/cds-reflect/apis/csn"
import { LinkedDefinition } from "./reflect"
import { csn } from "./csn"
import * as http from "http";

@@ -5,0 +5,0 @@

import { SELECT, INSERT, UPDATE, DELETE, Query, ConstructedQuery } from './ql'
import { LinkedModel, Definition, Definitions } from '@sap/cds-reflect/apis/reflected'
import { csn, type } from "@sap/cds-reflect/apis/csn"
import { LinkedModel, Definition, Definitions } from './reflect'
import { csn, type } from "./csn"
// import { Service } from './cds'

@@ -53,7 +53,10 @@

// Messaging API
emit (eve: Events, entity: Target, data?: object) : this
emit (eve: Events, data?: object) : this
async emit (eve: Events, data?: object, headers?: object) : this
// Http API
async send (eve: Events, entity: Target, data?: object, headers?: object) : this
async send (eve: Events, data?: object, headers?: object) : this
// Provider API
prepend (fn: ServiceImpl): this
async prepend (fn: ServiceImpl): this
on (eve: Events, entity: Target, handler: OnEventHandler): this

@@ -60,0 +63,0 @@ on (eve: Events, handler: OnEventHandler): this

@@ -1,74 +0,78 @@

const { app, env, service:{providers} } = require('../../lib')
const cds = require('../../lib')
cds.on('served', ()=>{
const mountPoint = '/$fiori-preview'
const appID = 'preview-app'
const _appURL = (srv, entity) => `${mountPoint}/${srv}/${entity}#${appID}`
const _componentURL = (srv, entity) => `${mountPoint}/${srv}/${entity}/app`
const { app, env, service:{providers} } = cds
function _manifest(serviceName, entityName) {
const [serviceProv, serviceInfo] = _validate(serviceName, entityName)
const manifest = {
_version: '1.8.0',
'sap.app': {
id: 'preview',
type: 'application',
title: `Preview ‒ List of ${serviceProv.name}.${entityName}`,
description: 'Preview Application',
dataSources: {
mainService: {
uri: `${serviceProv.path}/`,
type: 'OData',
settings: {
odataVersion: '4.0'
const mountPoint = '/$fiori-preview'
const appID = 'preview-app'
const _appURL = (srv, entity) => `${mountPoint}/${srv}/${entity}#${appID}`
const _componentURL = (srv, entity) => `${mountPoint}/${srv}/${entity}/app`
function _manifest(serviceName, entityName) {
const [serviceProv, serviceInfo] = _validate(serviceName, entityName)
const manifest = {
_version: '1.8.0',
'sap.app': {
id: 'preview',
type: 'application',
title: `Preview ‒ List of ${serviceProv.name}.${entityName}`,
description: 'Preview Application',
dataSources: {
mainService: {
uri: `${serviceProv.path}/`,
type: 'OData',
settings: {
odataVersion: '4.0'
}
}
}
},
},
},
'sap.ui5': {
dependencies: {
libs: {
'sap.fe.templates': {}
}
},
models: {
'': {
dataSource: 'mainService',
settings: {
synchronizationMode: 'None',
operationMode: 'Server',
autoExpandSelect: true,
earlyRequests: true,
groupProperties: {
default: {
submit: 'Auto'
'sap.ui5': {
dependencies: {
libs: {
'sap.fe.templates': {}
}
},
models: {
'': {
dataSource: 'mainService',
settings: {
synchronizationMode: 'None',
operationMode: 'Server',
autoExpandSelect: true,
earlyRequests: true,
groupProperties: {
default: {
submit: 'Auto'
}
}
}
}
}
},
routing: {
routes: [
{
name: `${entityName}ListRoute`,
target: `${entityName}ListTarget`,
pattern: ':?query:',
},
{
name: `${entityName}DetailsRoute`,
target: `${entityName}DetailsTarget`,
pattern: `${entityName}({key}):?query:`,
}
],
targets: {
[`${entityName}ListTarget`]: {
type: 'Component',
id: `${entityName}ListTarget`,
name: 'sap.fe.templates.ListReport',
options: {
settings: {
entitySet: `${entityName}`,
navigation: {
[`${entityName}`]: {
detail: {
route: `${entityName}DetailsRoute`
},
routing: {
routes: [
{
name: `${entityName}ListRoute`,
target: `${entityName}ListTarget`,
pattern: ':?query:',
},
{
name: `${entityName}DetailsRoute`,
target: `${entityName}DetailsTarget`,
pattern: `${entityName}({key}):?query:`,
}
],
targets: {
[`${entityName}ListTarget`]: {
type: 'Component',
id: `${entityName}ListTarget`,
name: 'sap.fe.templates.ListReport',
options: {
settings: {
entitySet: `${entityName}`,
navigation: {
[`${entityName}`]: {
detail: {
route: `${entityName}DetailsRoute`
}
}

@@ -78,181 +82,183 @@ }

}
}
},
[`${entityName}DetailsTarget`]: {
type: 'Component',
id: `${entityName}DetailsTarget`,
name: 'sap.fe.templates.ObjectPage',
options: {
settings: {
entitySet: `${entityName}`,
navigation: {}
},
[`${entityName}DetailsTarget`]: {
type: 'Component',
id: `${entityName}DetailsTarget`,
name: 'sap.fe.templates.ObjectPage',
options: {
settings: {
entitySet: `${entityName}`,
navigation: {}
}
}
}
}
}
},
},
},
contentDensities: {
compact: true,
cozy: true
},
'sap.ui': {
technology: 'UI5',
fullWidth: true
},
'sap.fiori': {
registrationIds: [],
archeType: 'transactional'
},
}
contentDensities: {
compact: true,
cozy: true
},
'sap.ui': {
technology: 'UI5',
fullWidth: true
},
'sap.fiori': {
registrationIds: [],
archeType: 'transactional'
},
}
const { routing } = manifest['sap.ui5']
for (const {navProperty, targetEntity} of serviceInfo) {
// add a route for the navigation property
routing.routes.push(
{
name: `${navProperty}Route`,
target: `${navProperty}Target`,
pattern: `${entityName}({key})/${navProperty}({key2}):?query:`,
const { routing } = manifest['sap.ui5']
for (const {navProperty, targetEntity} of serviceInfo) {
// add a route for the navigation property
routing.routes.push(
{
name: `${navProperty}Route`,
target: `${navProperty}Target`,
pattern: `${entityName}({key})/${navProperty}({key2}):?query:`,
}
)
// add a route target leading to the target entity
routing.targets[`${navProperty}Target`] = {
type: 'Component',
id: `${navProperty}Target`,
name: 'sap.fe.templates.ObjectPage',
options: {
settings: {
entitySet: targetEntity
}
}
}
)
// add a route target leading to the target entity
routing.targets[`${navProperty}Target`] = {
type: 'Component',
id: `${navProperty}Target`,
name: 'sap.fe.templates.ObjectPage',
options: {
settings: {
entitySet: targetEntity
// wire the new route from the source entity's navigation (see above)
routing.targets[`${entityName}DetailsTarget`].options.settings.navigation[navProperty] = {
detail: {
route: `${navProperty}Route`
}
}
}
// wire the new route from the source entity's navigation (see above)
routing.targets[`${entityName}DetailsTarget`].options.settings.navigation[navProperty] = {
detail: {
route: `${navProperty}Route`
}
}
return manifest
}
return manifest
}
function _html(serviceName, entityName) {
_validate(serviceName, entityName)
let ui5Version = (env.preview && env.preview.ui5 && env.preview.ui5.version) || ''
let ui5Host = (env.preview && env.preview.ui5 && env.preview.ui5.host) || `https://sapui5.hana.ondemand.com/${ui5Version}`
if (!ui5Host.endsWith('/')) ui5Host += '/'
function _html(serviceName, entityName) {
_validate(serviceName, entityName)
let ui5Version = (env.preview && env.preview.ui5 && env.preview.ui5.version) || ''
let ui5Host = (env.preview && env.preview.ui5 && env.preview.ui5.host) || `https://sapui5.hana.ondemand.com/${ui5Version}`
if (!ui5Host.endsWith('/')) ui5Host += '/'
// copied from UI5's test-resources/sap/ushell/shells/sandbox/fioriSandbox.html
return `
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Preview for ${serviceName}.${entityName}</title>
<script>
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
applications: {
"${appID}": {
title: "Browse ${entityName}",
description: "from ${serviceName}",
additionalInformation: "SAPUI5.Component=app",
applicationType : "URL",
url: "${_componentURL(serviceName, entityName)}",
navigationMode: "embedded"
// copied from UI5's test-resources/sap/ushell/shells/sandbox/fioriSandbox.html
return `
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Preview for ${serviceName}.${entityName}</title>
<script>
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
applications: {
"${appID}": {
title: "Browse ${entityName}",
description: "from ${serviceName}",
additionalInformation: "SAPUI5.Component=app",
applicationType : "URL",
url: "${_componentURL(serviceName, entityName)}",
navigationMode: "embedded"
}
}
}
}
</script>
<script id="sap-ushell-bootstrap" src="${ui5Host}test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" src="${ui5Host}resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout" data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3" data-sap-ui-frameOptions="allow">
</script>
<script src="${ui5Host}test-resources/sap/ushell/bootstrap/standalone.js"></script>
<script>
// load and register Fiori2 icon font
jQuery.sap.require("sap.ushell.iconfonts");
jQuery.sap.require("sap.ushell.services.AppConfiguration");
sap.ushell.iconfonts.registerFiori2IconFont();
sap.ui.getCore().attachInit(function() { sap.ushell.Container.createRenderer().placeAt("content") })
</script>
</head>
<body class="sapUiBody sapUShellFullHeight" id="content"></body>
</html>
`
}
</script>
<script id="sap-ushell-bootstrap" src="${ui5Host}test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" src="${ui5Host}resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout" data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3" data-sap-ui-frameOptions="allow">
</script>
<script src="${ui5Host}test-resources/sap/ushell/bootstrap/standalone.js"></script>
<script>
// load and register Fiori2 icon font
jQuery.sap.require("sap.ushell.iconfonts");
jQuery.sap.require("sap.ushell.services.AppConfiguration");
sap.ushell.iconfonts.registerFiori2IconFont();
sap.ui.getCore().attachInit(function() { sap.ushell.Container.createRenderer().placeAt("content") })
</script>
</head>
<body class="sapUiBody sapUShellFullHeight" id="content"></body>
</html>
`
}
function _componentJs(serviceName, entityName) {
const manifest = _manifest(serviceName, entityName)
return `sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("preview.Component", {
metadata: { manifest: ${JSON.stringify(manifest, null, 2)} }
});
});`
}
function _componentJs(serviceName, entityName) {
const manifest = _manifest(serviceName, entityName)
return `sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("preview.Component", {
metadata: { manifest: ${JSON.stringify(manifest, null, 2)} }
});
});`
}
function _validate(serviceName, entityName) {
const serviceProv = providers.find (s => s.name === serviceName)
if (!serviceProv) throw _badRequest (`No such service '${serviceName}'. Available: [${providers.map(p => p.name)}]`)
return _serviceInfo (serviceProv, entityName)
}
function _validate(serviceName, entityName) {
const serviceProv = providers.find (s => s.name === serviceName)
if (!serviceProv) throw _badRequest (`No such service '${serviceName}'. Available: [${providers.map(p => p.name)}]`)
return _serviceInfo (serviceProv, entityName)
}
function _serviceInfo (serviceProv, entityName) {
const entities = serviceProv.model.entities(serviceProv.name)
const entity = entities[entityName]
if (!entity) throw _badRequest (`No such entity '${entityName}' in service '${serviceProv.name}'`)
return [serviceProv, serviceProv.model.all ('Association', entity.elements)
.filter (a =>
!a.target.endsWith('_texts') &&
!a.target.endsWith('DraftAdministrativeData') &&
a.name !== 'SiblingEntity')
.map (a => { return { navProperty: a.name, targetEntity: a.target.split('.')[1] } })
]
}
function _serviceInfo (serviceProv, entityName) {
const entities = serviceProv.model.entities(serviceProv.name)
const entity = entities[entityName]
if (!entity) throw _badRequest (`No such entity '${entityName}' in service '${serviceProv.name}'`)
return [serviceProv, serviceProv.model.all ('Association', entity.elements)
.filter (a =>
!a.target.endsWith('_texts') &&
!a.target.endsWith('DraftAdministrativeData') &&
a.name !== 'SiblingEntity')
.map (a => { return { navProperty: a.name, targetEntity: a.target.split('.')[1] } })
]
}
const _badRequest = (message) => { const err = new Error (message); err.statusCode = 400; return err}
const _badRequest = (message) => { const err = new Error (message); err.statusCode = 400; return err}
// fetch and instrument all OData providers
const any = providers.filter (srv =>
srv._adapters [Object.keys(srv._adapters) .find (a => a.startsWith ('odata'))]
)
.map(srv => {
// called from ../index.js to provide the data for the HTML link
const link = linkProvider(srv)
srv.$linkProviders ? srv.$linkProviders.push (link) : srv.$linkProviders = [link]
return link
})
.length
// install middlewares once
if (any) {
const router = require('express').Router()
// UI5 component
router.get ('/:service/:entity/app/Component.js', ({ params }, resp) => resp.send(_componentJs(params.service, params.entity)))
// html
router.get ('/:service/:entity', ({ params }, resp) => resp.send(_html(params.service, params.entity)))
// legacy URL pattern from cds 3.x
router.get ('/', ({ query }, resp, next) => {
if (query.service && query.entity) resp.redirect (308, _appURL(query.service, query.entity))
else next()
// fetch and instrument all OData providers
const any = providers.filter (srv =>
srv._adapters [Object.keys(srv._adapters) .find (a => a.startsWith ('odata'))]
)
.map(srv => {
// called from ../index.js to provide the data for the HTML link
const link = linkProvider(srv)
srv.$linkProviders ? srv.$linkProviders.push (link) : srv.$linkProviders = [link]
return link
})
app.use(mountPoint.replace('$','\\$'), router)
}
.length
function linkProvider(service) {
return (entity) => {
if (!entity) return
return {
href: _appURL(service.name, entity),
title: 'Preview in Fiori elements',
name: 'Fiori'
// install middlewares once
if (any) {
const router = require('express').Router()
// UI5 component
router.get ('/:service/:entity/app/Component.js', ({ params }, resp) => resp.send(_componentJs(params.service, params.entity)))
// html
router.get ('/:service/:entity', ({ params }, resp) => resp.send(_html(params.service, params.entity)))
// legacy URL pattern from cds 3.x
router.get ('/', ({ query }, resp, next) => {
if (query.service && query.entity) resp.redirect (308, _appURL(query.service, query.entity))
else next()
})
app.use(mountPoint.replace('$','\\$'), router)
}
function linkProvider(service) {
return (entity) => {
if (!entity) return
return {
href: _appURL(service.name, entity),
title: 'Preview in Fiori elements',
name: 'Fiori'
}
}
}
}
})

@@ -6,10 +6,15 @@

const { find, path, readFileSync } = cds.utils, {app} = cds.env.folders
const htmls = find (app, ['*.html', '*/*.html', '*/*/*.html']).map(file => path.relative(app, file).replace (/\\/g,'/'))
const odata = srv => Object.keys(srv._adapters).find (a => a.startsWith ('odata'))
const metadata = srv => odata(srv) ? ` / <a href="${srv.path}/$metadata">$metadata</a>` : ``
const html = readFileSync(path.join(__dirname,'index.html'),'utf-8') .replace (
'{{content}}',
htmls.map(html => `\n<span><a href="${html}">/${html}</a></span>`).join(',') +
cds.service.providers.map (srv => `
const html = readFileSync(path.join(__dirname,'index.html'),'utf-8')
// .replace ('{{subtitle}}', 'Version ' + cds.version)
.replace (/{{package}}/g, _project())
.replace (/{{app}}/g, app.replace(/*trailing slash*/ /\/$/, ''))
.replace ('{{apps}}', find (app, ['*.html', '*/*.html', '*/*/*.html']).map(
file => path.relative(app, file).replace (/\\/g,'/')).map(
html => `\n<li><a href="${html}">/${html}</a></li>`
).join('\n') || '— none —'
)
.replace ('{{services}}', cds.service.providers.map (srv => `
<h3>

@@ -21,3 +26,3 @@ <a href="${srv.path}">${srv.path}</a>${metadata(srv)} ${_moreLinks(srv)}

<li>
<a href="${srv.path}/${e}">${e}</a> ${_moreLinks(srv, e)}
<a href="${srv.path}/${e}?$top=11">${e}</a> ${_moreLinks(srv, e)}
</li>`}).join('')}

@@ -49,4 +54,14 @@ </ul>

.sort ((l1, l2) => l1.name.localeCompare(l2))
.map (l => ` <a class="preview" href="${l.href}" title="${l.title||l.name}"> &hellip;in ${l.name}</a>`)
.map (l => ` <a class="preview" href="${l.href}" title="${l.title||l.name}"> &rarr; ${l.name} preview </a>`)
.join (' ')
}
function _project(){
const cwd = process.cwd()
try {
const pj = require(cwd+'/package.json')
return `${pj.name} ${pj.version}`
} catch(e) {
return `${cwd}`
}
}

@@ -124,4 +124,2 @@ module.exports = Object.assign ( _serve, {

const is_in_memory = o => o && o.credentials && o.credentials.database === ':memory:'
const production = process.env.NODE_ENV === 'production'
const {existsSync:exists} = require ('fs')

@@ -173,29 +171,23 @@ const {dirname,resolve} = require ('path')

if (o.watch) return _watch (o.project,o) // cds serve --watch <project>
if (o.project) { // cds serve --in <project>
process.env._original_cwd = process.cwd()
try { process.chdir (o.project) }
catch(e){
try { process.chdir (dirname (require.resolve(o.project+'/package.json'))) }
catch(_){ throw e }
}
process.on('exit', ()=> process.chdir (process.env._original_cwd))
}
if (o.project) _in_project (o.project) // cds run --project <project>
// IMPORTANT: never load any @sap/cds modules before the chdir above happened!
const cds = _prepare_cds()
const cds = _prepare_cds (o,require('../lib'))
// handle --in-memory resp. --in-memory? (requires cds.env)
o.in_memory = o['in-memory'] || !production && o['in-memory?'] && !cds.requires.db
if (o.in_memory) cds.env.add ({requires: { db: {
kind:'sqlite', ...cds.requires.sqlite,
credentials:{database:':memory:'}
}}})
else o.in_memory = is_in_memory(cds.requires.db)
// The following things are meant for dev mode, which can be overruled by feature flagse...
const {features} = cds.env
{
// load service bindings when mocking or asked to
if (features.mocked_bindings && o.mocked || o['with-bindings']) await cds.service.bindings
// load service bindings when mocking or asked to
if (o.mocked || o['with-bindings']) await cds.service.bindings
// handle --in-memory resp. --in-memory? (requires cds.env)
if (features.in_memory_db) o.in_memory = _in_memory (o,cds.env)
// add dev helper for Fiori URLs
if (process.env.NODE_ENV!=='production') require('../app/fiori/routes')
// add dev helper for Fiori URLs
if (features.fiori_routes) require('../app/fiori/routes')
// add fiori preview links to default index.html
if (features.fiori_preview) require('../app/fiori/preview')
}
// bootstrap server from project-local server.js or from @sap/cds/server.js

@@ -205,13 +197,10 @@ const server_js = _local('server.js') || _local(cds.env.folders.srv,'server.js') || cds.server

// add fiori preview links to default index.html
if (cds.env.features.fiori_preview) require('../app/fiori/preview')
// synchronize with the server events
return new Promise((resolve, reject) => {
server.on('error', e => reject(e)) // startup errors like EADDRINUSE
const done = ()=> {
cds.emit('listening', { server, url: `http://localhost:${server.address().port}` })
resolve(server)
server.once ('listening',done); if (server.listening) done()
server.on ('error', e => reject(e)) // startup errors like EADDRINUSE
function done () {
cds.emit ('listening', { server, url: `http://localhost:${server.address().port}` })
resolve (server)
}
server.listening ? done() : server.on('listening',done)
})

@@ -221,6 +210,5 @@ }

function _prepare_cds () { // NOSONAR
/** @param {import('../lib')} cds */
function _prepare_cds (o,cds) { // NOSONAR
const cds = require('../lib')
const cds_requires = Object.create(cds.requires)

@@ -247,2 +235,3 @@ for (let entry of Object.values(cds.requires)) {

console.log ('\x1b[0m')
if (o.mocked) require('../lib/db/deploy').include_external_entities_in(model)
})

@@ -274,8 +263,3 @@

function _local (...path) {
const file = resolve(...path)
if (exists(file)) return require (file)
}
/** handles --watch option */
function _watch (project,o) {

@@ -296,3 +280,43 @@ o.args = process.argv.slice(2) .filter (a => a !== '--watch' && a !== '-w')

// mascades password-like strings, also reducing clutter in output
/** handles --project option */
function _in_project (project) {
// save the former process.cwd()
const former = process.env._original_cwd = process.cwd()
try { // using the given project as dirname, e.g. './bookshop'
process.chdir (project)
} catch(e) {
try { // using the given project as a node package name, e.g. '@capire/bookshop'
process.chdir (dirname (require.resolve(project+'/package.json')))
} catch(_){ throw e }
}
// restore the former process.cwd()
process.on('exit', ()=> process.chdir (former))
}
/** handles --in-memory option */
function _in_memory (o,env) {
const db = env.requires.db
if (o['in-memory'] || o['in-memory?'] && !db) {
env.add ({ requires: { db: {
kind:'sqlite', ...env.requires.sqlite,
credentials:{database:':memory:'}
}}})
return true
}
if (db && db.credentials && db.credentials.database === ':memory:') {
return true
}
}
/** used to load local server.js */
function _local (...path) {
const file = resolve(...path)
if (exists(file)) return require (file)
}
/** mascades password-like strings, also reducing clutter in output */
function _redacted (cred) {

@@ -299,0 +323,0 @@ const secrets = /(password)|(certificate)|(ca)/i // 'certificate' and 'ca' on HANA

@@ -1,3 +0,2 @@

/* eslint-disable no-console */
const Severities = ['Error', 'Warning', 'Info', 'Debug']
const { sortMessagesSeverityAware, deduplicateMessages } = require('@sap/cds-compiler')

@@ -12,6 +11,7 @@ // sorts, filters, and writes compilation messages to console

messages.forEach (m => { if (!m.severity) m.severity = 'Error' })
messages = messages.filter (m => level.includes (m.severity))
messages = _sortUnique (messages)
deduplicateMessages(messages)
messages = sortMessagesSeverityAware (messages)
for (let m of messages) {

@@ -25,32 +25,2 @@ // show stack for resolution issues since there the requiring code location is in the stack

function _sortUnique (a, comparator=_compareCompilationMessage) {
let arr = [];
for (let i = 0; i < a.length; i++) {
const hasDup = arr.some(v => comparator(v, a[i]) === 0)
if (!hasDup) arr.push(a[i])
}
return arr.sort (comparator)
}
function _compareCompilationMessage (a, b) {
let rc = Severities.indexOf (a.severity) - Severities.indexOf (b.severity); if (rc !== 0) return rc
rc = eq( a.message, b.message ); if (rc !== 0) return rc
if (a.location && b.location) {
let aend = a.location.end || a.location.start;
let bend = b.location.end || b.location.start;
return ( eq( a.location.filename, b.location.filename ) ||
eq( a.location.start.line, b.location.start.line ) ||
eq( a.location.start.column, b.location.start.column ) ||
eq( aend.line, bend.line ) ||
eq( aend.column, bend.column ))
}
else
return (!a.location ? (!b.location ? 0 : 1) : -1)
}
function eq(x, y) {
return (x === y) ? 0 : (x > y) ? 1 : -1
}
function _effectiveLogLevel (options) {

@@ -71,1 +41,3 @@ const logLevel = options && options['log-level'] || require('../../lib').env["log-level"]

}
/* eslint no-console:off */

@@ -7,2 +7,31 @@ # Change Log

## Version 4.5.1 - 2021-02-01
### Fixed
- Update `@sap/cds-runtime` dependency
## Version 4.5.0 - 2021-02-01
### Added
- `cds.server` provides an option to switch off automatically generated `index.html` served at `/`:
Do that in a custom `server.js`:
```js
const cds = require('@sap/cds')
// ...
module.exports = (o) => cds.server({ ...o, index:false })
```
- The default `index.html` now honors the system's setting for dark mode.
- Former package `@sap/cds-reflect` is now embedded in `@sap/cds`
### Changed
- Fiori preview is now disabled if `NODE_ENV` is `production`, to avoid any runtime overhead there. You can enable it with configuration `cds.features.fiori_preview: true`.
### Fixed
- `cds build` now correctly supports multitenant applications defining multiple database modules, e.g. one database for tenant related data and one for shared data.
- `cds deploy --to hana` does no longer fail with an invalid service name error if '.' is used in the MTA ID.
## Version 4.4.10 - 2021-01-18

@@ -19,2 +48,3 @@

## Version 4.4.8 - 2021-01-07

@@ -105,2 +135,8 @@

## Version 4.3.2 - 2020-12-18
### Fixed
- use `@sap/cds-runtime~2.6`
## Version 4.3.1 - 2020-11-20

@@ -107,0 +143,0 @@

@@ -49,4 +49,3 @@ const cds = require('..')

const name = localized_(locale, each)
const source = localized_(locale, d.source)
const x = {__proto__:d, name, source }
const x = {__proto__:d, name }
Object.defineProperty (m.definitions, name, {value:x})

@@ -53,0 +52,0 @@ pass2.push ([x,locale])

@@ -6,3 +6,3 @@ const fs = require('@sap/cds-foss')('fs-extra')

const DEBUG = process.env.DEBUG
const HDI_CONTAINER_TYPE = 'com.sap.xs.hdi-container'
const HDI_CONTAINER_TYPES = ['com.sap.xs.hdi-container']
const UTF_8 = 'utf-8'

@@ -47,3 +47,3 @@ const MTA_YAML = 'mta.yaml'

if (mta.ID) {
details.name = mta.ID.replace('.', '-')
details.name = mta.ID.replace(/\./g, '-')
}

@@ -148,3 +148,3 @@ }

if (mta && Array.isArray(mta.resources)) {
const hdiResources = mta.resources.filter(resource => resource.type === HDI_CONTAINER_TYPE)
const hdiResources = mta.resources.filter(resource => HDI_CONTAINER_TYPES.includes(resource.type))

@@ -151,0 +151,0 @@ if (hdiResources.length > 0) {

@@ -6,4 +6,4 @@ /* eslint-disable no-empty */

const BuildTaskFactory = require('../buildTaskFactory')
const { setProperty, BuildMessage, BuildError } = require('../util')
const { BUILD_TASK_HANA, BUILD_TASK_MTX, FOLDER_GEN, SEVERITY_WARNING } = require('../constants')
const { BuildMessage, BuildError } = require('../util')
const { BUILD_TASK_HANA, FOLDER_GEN, SEVERITY_WARNING } = require('../constants')
const DEBUG = process.env.DEBUG

@@ -29,21 +29,3 @@

async build() {
// custom build tasks for srv and db modules might be defined
let tasks = await this._getTasks()
if (tasks.length === 0) {
this.logger.log("[cds] - no modules containing cds model files found, skipping build")
return
}
const hanaTask = tasks.find(task => task.for === BUILD_TASK_HANA)
const models = tasks.reduce((acc, task) => {
if (task.options) {
if (Array.isArray(task.options.model)) {
task.options.model.forEach(model => acc.add(model))
} else if (task.options.model) {
acc.add(task.options.model)
}
}
return acc
}, new Set())
const modelPaths = this.cds.resolve(Array.from(models), this.buildOptions)
const modelPaths = this.resolveModel()
if (!modelPaths || modelPaths.length === 0) {

@@ -59,4 +41,7 @@ this.logger.log("[cds] - no model found, skipping build")

// custom build tasks for srv and db modules might be defined
const tenantDbPath = await this._getHanaTenantDbPath(modelPaths)
// validate extension whitlists defined for this SaaS application
this._validateExtensionWhitelists(model, hanaTask)
this._validateExtensionWhitelists(model, tenantDbPath)

@@ -81,3 +66,3 @@ // copy base model sources

// copy native hana content and templates
await this._copyNativeContent(this.task.src, this.task.dest, hanaTask)
await this._copyNativeContent(this.task.src, this.task.dest, tenantDbPath)
}

@@ -99,3 +84,3 @@

async _copyNativeContent(src, dest, hanaTask) {
async _copyNativeContent(src, dest, tenantDbPath) {
// copying tmplates

@@ -108,9 +93,6 @@ const tplSrc = path.join(src, FOLDER_TEMPLATES)

if (hanaTask) {
// copy hana related content from db module
const dbSrc = path.resolve(src, hanaTask.src)
const dbDest = path.resolve(dest, path.relative(this.buildOptions.root, dbSrc))
await this._copyNativeHanaContent(dbSrc, dbDest)
} else {
this.logger.warn('[cds] - no db module found, skip copying native HANA artefacts')
if (tenantDbPath) {
// copy any static HANA artefacts, e.g. .csv files
const dbDest = path.resolve(dest, path.relative(this.buildOptions.root, tenantDbPath))
await this._copyNativeHanaContent(tenantDbPath, dbDest)
}

@@ -147,3 +129,3 @@ }

*/
_validateExtensionWhitelists(csn, hanaTask) {
_validateExtensionWhitelists(csn, tenantDbPath) {
let entityWhitelist = this.env.get("mtx.entity-whitelist")

@@ -153,5 +135,4 @@ let serviceWhitelist = this.env.get("mtx.service-whitelist")

// for java projects mtx configuration is part of sidecar config
if (!entityWhitelist && !serviceWhitelist) {
const dbPath = path.resolve(this.buildOptions.root, hanaTask ? hanaTask.src : this.env.folders.db)
const dbEnv = this.env.for("cds", dbPath)
if (!entityWhitelist && !serviceWhitelist && tenantDbPath) {
const dbEnv = this.env.for("cds", tenantDbPath)
if (dbEnv && dbEnv.get("mtx")) {

@@ -194,39 +175,34 @@ entityWhitelist = dbEnv.get("mtx.entity-whitelist")

* A build task of type 'hana' is enforced in order to copy existing native hana artefacts later on.
*
* @returns {Array<Object>} the array of build tasks.
*
* @param {Array<string>} modelPaths - the .cds root model files defined by this build task's model options
* @returns {string} the src folder of the tenant db module
*/
async _getTasks() {
async _getHanaTenantDbPath(modelPaths) {
let tasks = this.buildOptions.tasks || []
tasks = tasks.filter(task => task.for !== BUILD_TASK_MTX)
if (tasks.length === 0 || !tasks.find(task => task.for === BUILD_TASK_HANA)) {
const dbKind = this.env.get("requires.db.kind")
// ensure that the hana task is contained even if this mtx task has been executed solely using "cds build --for mtx"
if (!tasks.find(task => task.for === BUILD_TASK_HANA)) {
//mtx task might have been executed as separate task
const buildTaskFactory = new BuildTaskFactory(this.logger, this.cds)
let derivedTasks = []
try {
if (dbKind !== "hana") {
// temporarily switch db.kind to 'hana' to make sure a hana build task
// is created as mtx is only supporting hana
setProperty(this.env, "requires.db.kind", "hana")
}
derivedTasks = await buildTaskFactory._createTasksFromConfig(this.buildOptions)
}
finally {
if (dbKind !== "hana") {
setProperty(this.env, "requires.db.kind", dbKind)
}
}
tasks = buildTaskFactory._getExistingTasks(this.buildOptions)
if (tasks.length === 0) {
// no custom tasks, use derived tasks
tasks = derivedTasks
tasks = await buildTaskFactory._createTasksFromConfig(this.buildOptions)
}
else {
// merge hana task into custom tasks
// assuming that custom tasks have been defined for service modules
tasks = tasks.concat(derivedTasks.find(task => task.for === BUILD_TASK_HANA))
}
// the SaaS app might use a tenant aware db as well as a shared db deployed using static hdi-deployer
// pick the hana build task refering to the tenant aware db - the src path has to be contained in this build task's model options
const hanaDbPaths = tasks.filter(task => task.for === BUILD_TASK_HANA).map(hanaTask => path.resolve(this.buildOptions.root, hanaTask.src || this.env.folders.db))
let tenantDbPath = hanaDbPaths.find(hanaDbPath => hanaDbPaths.length === 1 || modelPaths.some(modelPath => path.dirname(modelPath) === hanaDbPath))
if (!tenantDbPath) {
tenantDbPath = path.join(this.buildOptions.root, this.env.folders.db)
if (hanaDbPaths.length === 0) {
this.pushMessages(new BuildMessage(`[cds] - no 'hana' build task found, use default location '${tenantDbPath}'`, SEVERITY_WARNING))
} else {
this.pushMessages(new BuildMessage(`[cds] - no 'hana' build task found matching model scope '${this.task.options.model}', use default location '${tenantDbPath}'`, SEVERITY_WARNING))
}
}
return tasks
return tenantDbPath
}
}
module.exports = MtxModuleBuilder

@@ -6,3 +6,3 @@ const cdsc = require ('@sap/cds-compiler')

if (typeof _o === 'string') _o = {version:_o}
const o = _4odata ? { ...env.effective.cdsc(_o), ..._o, ...env.cdsc } : { ..._o, ...env.cdsc }
const o = _4odata ? { ..._o, ...env.effective.cdsc(_o) } : { ..._o, ...env.cdsc }
const names = o.names || o.sql_mapping || env.sql.names

@@ -9,0 +9,0 @@ if (names !== 'plain') o.sqlMapping = names

@@ -1,2 +0,2 @@

const cds = require ('@sap/cds-reflect'), {lazified} = cds
const {lazify:lazified} = require ('../core/lazy')
require = lazified (module) // eslint-disable-line

@@ -70,5 +70,5 @@

if (!cds.env.features.snapi) {
if (!global.cds.env.features.snapi) {
if (!global.it) console.warn ('[cds] - features.snapi not set -> using old compiler...') // eslint-disable-line no-console
require('./old')(compile)
}

@@ -56,4 +56,3 @@ const cdsc = require ('@sap/cds-compiler')

// adjust for existing/former implementation
const {snapi} = require('..').env.features
if (!snapi) {
if (!global.cds.env.features.snapi) {
const _parse = Object.assign ((...args) => _parse.cdl(...args), parse, {

@@ -60,0 +59,0 @@ cdl: (...args) => (_parse.cdl = require('./cdsv').parse_only) (...args),

@@ -1,16 +0,24 @@

exports.for = (cds,v) => { if (cds.env.features.cls) {
exports.for = (cds,v) => {
const [,major,minor] = /v(\d+)\.(\d+)/.exec(process.version)
if (major > 12 || major == 12 && minor >= 18) {
if (cds.env.features.cls) {
const { executionAsyncResource:current, createHook } = module.require ('async_hooks')
const _context = Symbol('cds.req.context')
const _context = Symbol('cds.context')
createHook ({ init(id,t,tid, res) {
res[_context] = current()[_context]
const cr = current(); if (!cr) return
const ctx = cr[_context]
if (ctx) res[_context] = ctx
}}).enable()
Reflect.defineProperty (cds,'context',{ enumerable:1,
set(v) { current()[_context] = v && v.context || v },
get:()=> current()[_context],
set(v) {
const cr = current()
if (cr) cr[_context] = v && v.context || v
},
get() {
const cr = current()
if (cr) return cr[_context]
else return undefined
},
})

@@ -29,2 +37,2 @@

}}
}

@@ -53,31 +53,36 @@ const cds = require('..')

exports.include_external_entities_in = function (model) {
if (model._mocked) return model; else Object.defineProperty(model,'_mocked',{value:true})
for (let each in model.definitions) {
const def = model.definitions[each]
if (def['@cds.persistence.mock'] === false) continue
if (def['@cds.persistence.skip'] === true) {
delete def['@cds.persistence.skip']
if (model._xsn) delete model._xsn.definitions[each]['@cds.persistence.skip']
}
const def = model.definitions[each]
if (def['@cds.persistence.mock'] === false) continue
if (def['@cds.persistence.skip'] === true) {
DEBUG && DEBUG ('including mocked', each)
delete def['@cds.persistence.skip']
if (model._xsn) delete model._xsn.definitions[each]['@cds.persistence.skip']
}
}
exports.exclude_external_entities_in (model,'if-bound')
return model
}
exports.exclude_external_entities_in = function (model) { // NOSONAR
for (let [each,{service=each}] of Object.entries (cds.requires)) {
if (!each.model) continue //> not for internal services like cds.requires.odata
exports.exclude_external_entities_in = function (csn,_bound) { // NOSONAR
for (let [each,{service=each,model,credentials}] of Object.entries (cds.requires)) {
if (!model) continue //> not for internal services like cds.requires.odata
if (_bound && !credentials) continue
DEBUG && DEBUG ('excluding external entities for', service, '...')
const prefix = service+'.'
for (let each in model.definitions) if (each.startsWith(prefix)) {
for (let each in csn.definitions) if (each.startsWith(prefix)) {
DEBUG && DEBUG ('excluding external entity', each)
_exclude (each)
}
}
return model
return csn
function _exclude (each) {
const def = model.definitions[each]; if (def.kind !== 'entity') return
const def = csn.definitions[each]; if (def.kind !== 'entity') return
def['@cds.persistence.skip'] = true
if (model._xsn) model._xsn.definitions[each]['@cds.persistence.skip'] = {val:true}
DEBUG && DEBUG ('excluded imported', each)
if (csn._xsn) csn._xsn.definitions[each]['@cds.persistence.skip'] = {val:true}
// propagate to all views...
for (let other in model.definitions) {
const q = model.definitions[other].query
for (let other in csn.definitions) {
const q = csn.definitions[other].query
if (q && q.SELECT && q.SELECT.from && q.SELECT.from.ref && q.SELECT.from.ref[0] === each)

@@ -84,0 +89,0 @@ _exclude (other)

@@ -0,9 +1,15 @@

const [,major,minor] = /v(\d+)\.(\d+)/.exec(process.version)
const production = process.env.NODE_ENV === 'production'
module.exports = {
features: {
fiori_preview: true,
cls: major > 12 || major == 12 && minor >= 18,
fiori_preview: !production,
fiori_routes: !production,
in_memory_db: !production,
mocked_bindings: !production,
skip_unused: true,
one_model: true,
snapi: true,
cls: true,
localized: true,

@@ -116,3 +122,3 @@ skip_reclassification: false,

// proxies:false,
},
},
v4: {

@@ -129,3 +135,5 @@ version: 'v4',

structs:true,
refs:false, //> proxies:false,
refs:false,
proxies:false,
xrefs:false,
safe_ax:true,

@@ -137,3 +145,5 @@ },

structs:true,
refs:true, //> proxies:true,
refs:true,
proxies:true,
xrefs:true,
safe_ax:true,

@@ -140,0 +150,0 @@ },

@@ -352,25 +352,7 @@ const DEFAULTS = require('./defaults'), defaults = require.resolve ('./defaults')

if (flavor) odata.version = flavor.version
/////////////////////////////////////////////////
// Interims compatibility to cds-runtime...
if (odata.format === 'structured') {
odata.structs = true
if (odata._foreignKeys === undefined) odata.refs = true
}
if (odata._foreignKeys !== undefined) odata.refs = !odata._foreignKeys
if (odata.__proxies !== undefined) odata.proxies = odata.__proxies
/////////////////////////////////////////////////
if (odata.structs && odata.refs === undefined) odata.refs = false //> decouple .structs and .refs
if (odata.refs && odata.proxies === undefined) odata.proxies = false //> decouple .refs and .proxies
// if (odata.structs && odata.refs === undefined) odata.refs = false //> decouple .structs and .refs
// if (odata.refs && odata.proxies === undefined) odata.proxies = false //> decouple .refs and .proxies
// if (odata.refs && odata.xrefs === undefined) odata.xrefs = false //> decouple .refs and .xrefs
delete odata.flavors
delete odata.flavor
/////////////////////////////////////////////////
// Interims compatibility to cds-runtime...
odata.format = odata.structs ? 'structured' : 'flat'
odata._foreignKeys = !odata.refs
odata.__proxies = odata.proxies
/////////////////////////////////////////////////
return odata

@@ -387,5 +369,7 @@ }

if ((x = odata.refs) !== undefined && !('odataForeignKeys' in cdsc)) cdsc.odataForeignKeys = !x
if ((x = odata.proxies) !== undefined) {
if (!('odataProxies' in cdsc)) cdsc.odataProxies = x
if (!cdsc.beta || !('odataProxies' in cdsc.beta)) (cdsc.beta || (cdsc.beta={})).odataProxies = x
if ((x = odata.xrefs) !== undefined && !('odataXServiceRefs' in cdsc)) cdsc.odataXServiceRefs = x
if ((x = odata.proxies) !== undefined && !('odataProxies' in cdsc)) cdsc.odataProxies = x
if (compiler_v1) {
if (cdsc.odataProxies || cdsc.odataXServiceRefs) (cdsc.beta || (cdsc.beta={})).odataProxies = true
if (cdsc.odataXServiceRefs) delete cdsc.odataProxies
}

@@ -395,4 +379,6 @@ return cdsc

const compiler_v1 = require('@sap/cds-compiler/package.json').version.startsWith('1.')
/** @type Config & typeof DEFAULTS */
module.exports = Config.prototype.for ('cds', process_cwd)
/* eslint no-console:0 */
if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
const core = require ('@sap/cds-reflect/lib'), { extend, lazified } = core
const { extend, lazify, lazify:lazified } = require ('./core/lazy')
const core = new class cds extends require('events'){}
const c = lazy => cds.builtin.classes [lazy]
require = lazified (module) // eslint-disable-line

@@ -9,2 +11,21 @@

// Builtin types and classes
builtin: require ('./core/csn'),
service: lazy => extend (cds.builtin.classes.service) .with (lazified ({
bindings: require ('./srv/bindings'),
factory: require('./srv/factory'),
providers:[],
impl: x=>x,
})),
Association:c, Composition:c,
entity:c,
type:c,
array:c,
struct:c,
// Model Reflection
reflect: require ('./core/reflect'),
linked: require ('./core/linked'),
infer: require ('./core/infer'),
// Loading and Compiling Models

@@ -18,4 +39,3 @@ model: undefined,

// Providing and Consuming Services
services: new core.Iterable,
service: require ('./srv'),
services: new class Iterable { *[Symbol.iterator]() {for (let e in this) yield this[e]}},
serve: require ('./serve'),

@@ -43,4 +63,5 @@ server: require ('../server'),

test: require ('./utils/tests'),
log: require ('./utils/logging'),
debug (..._) { const L = this.log(..._); return L._debug && L.debug },
log: require ('./utils/logging'), debug: lazy => cds.log.debug,
clone: m => JSON.parse (JSON.stringify(m)),
lazified, lazify, extend,

@@ -47,0 +68,0 @@ // Configuration & Information

@@ -385,3 +385,10 @@ /* eslint-disable no-inner-declarations */

module.exports = Object.assign (()=>exports, lib.statements, {
module.exports = Object.assign (function(){
// eslint-disable-next-line no-console
console.trace(`
'const { SELECT, ... } = srv.ql(req)' is deprecated and superceded by 'cds.context'.
Please use 'cds.tx(req).run( <query> )' instead.
`)
return module.exports
}, lib.statements, {
SELECT: SELECT._api(),

@@ -394,11 +401,11 @@ INSERT: INSERT._api(),

// if (cds.env.features.cls || cds.env.features.debug_queries) {
// const { AsyncResource } = require('async_hooks')
// const { then } = Query.prototype
// cds.extend (Query) .with (class {
// get then(){
// const q = new AsyncResource('cds.Query')
// return (r,e) => q.runInAsyncScope (then,this,r,e)
// }
// })
// }
if (cds.env.features.cls && cds.env.features.debug_queries) {
const { AsyncResource } = require('async_hooks')
const { then } = Query.prototype
cds.extend (Query) .with (class {
get then(){
const q = new AsyncResource('cds.Query')
return (r,e) => q.runInAsyncScope (then,this,r,e)
}
})
}

@@ -0,4 +1,4 @@

const {env:{features}} = require('..')
const async_events = { succeeded:1, failed:1, done:1 }
const { EventEmitter } = require('events')
const { inspect } = require('util')
const User = require ('./user')

@@ -14,7 +14,20 @@

constructor(_) { Object.assign (this,this._=_||{}) }
toString() { return `${this.event} ${this.path}` }
[inspect.custom]() { return `${this.constructor.name} ${inspect(this._)}` }
constructor(_={}) {
Object.assign (this, this._set('_',_))
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// REVISIT: temporary workarounds until https://github.wdf.sap.corp/cdx/cds-runtime/pull/964 is released
// Remove this getter when https://github.wdf.sap.corp/cdx/cds-runtime/pull/964 is released
get statements(){ return global.cds.ql }
// Keep method _set, but remove 'enumerable:true' when https://github.wdf.sap.corp/cdx/cds-runtime/pull/964 is released
_set (property, value) {
Object.defineProperty (this, property, {value,writable:true,enumerable:true})
return value
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//

@@ -25,3 +38,3 @@ // Emitting and listening to succeeded / failed / done events

/** @returns {EventEmitter} */ get emitter() {
return this._emitter || (this._emitter = this.context ? this.context.emitter : new EventEmitter)
return this._emitter || (this._emitter = this._propagated('emitter') || new EventEmitter)
}

@@ -56,2 +69,10 @@

set context(c) { if (c) super.context = c }
get context() { return this }
set id(c) { if (c) super.id = c }
get id() {
return this.id = this._propagated('id') || this.headers[ 'x-correlation-id' ]
}
set tenant(t) {

@@ -61,3 +82,3 @@ if (t) super.tenant = this.user.tenant = t

get tenant() {
return this.tenant = this.context ? this.context.tenant : this.user.tenant
return this.tenant = this._propagated('tenant') || this.user.tenant
}

@@ -71,3 +92,3 @@

get user() {
return this.user = this.context ? this.context.user : new User
return this.user = this._propagated('user') || new User
}

@@ -79,7 +100,7 @@

get locale() {
return this.locale = this.context ? this.context.locale : this.user.locale
return this.locale = this._propagated('locale') || this.user.locale
}
get timestamp() {
return super.timestamp = this.context ? this.context.timestamp : Date.now()
return super.timestamp = this._propagated('timestamp') || Date.now()
}

@@ -92,4 +113,3 @@

} else {
const headers = {}
const outer = this.context && this.context.headers
const headers={}, outer = this._propagated('headers')
if (outer) for (let each of EventContext.propagateHeaders) {

@@ -107,2 +127,7 @@ if (each in outer) headers[each] = outer[each]

_propagated (p) {
const ctx = this.context
if (ctx !== this) return ctx[p]
}
set _tx(tx) {

@@ -114,5 +139,8 @@ Object.defineProperty (this,'_tx',{value:tx}) //> allowed only once!

// REVISIT: Eliminate req._children
const reqs = ctx._children || (ctx._children = {})
const all = reqs[tx.name] || (reqs[tx.name] = [])
all.push(this)
// only collect children if integrity checks are active
if (features.assert_integrity !== false) {
const reqs = ctx._children || (ctx._children = {})
const all = reqs[tx.name] || (reqs[tx.name] = [])
all.push(this)
}
}

@@ -123,4 +151,6 @@ }

/** REVISIT: remove -> @deprecated */
get _model() { return super._model = this._tx && this._tx.model || this.context && this.context._model }
set _model(m){ super._model = m }
get _model() {
return super._model = this._tx && this._tx.model || this._propagated('_model')
}
}

@@ -127,0 +157,0 @@

@@ -7,5 +7,10 @@ const EventContext = require ('./context')

*/
class EventMessage extends EventContext {}
class EventMessage extends EventContext {
static for (eve) {
if (eve instanceof this) return eve
if (typeof eve === 'object') return new this(eve)
}
}
module.exports = exports = EventMessage
exports.Context = EventContext

@@ -12,3 +12,3 @@ const { Responses, Errors } = require('./res')

get method() {
return super.method = Crud2Http[this.event] || this.event
return this._set ('method', Crud2Http[this.event] || this.event)
}

@@ -18,17 +18,13 @@

get event() {
if (this._.method) return super.event = Http2Crud[this._.method] || this._.method
if (this.query) return super.event = Query2Crud(this.query)
return super.event = undefined
if (this._.method) return this._set ('event', Http2Crud[this._.method] || this._.method)
if (this.query) return this._set ('event', Query2Crud(this.query))
return this._set ('event', undefined)
}
set entity(e) {
if (e) super.entity = e.name ? (this.target = e).name : e
}
set entity(e) { if (e) super.entity = e.name ? (this.target = e).name : e }
get entity() {
return super.entity = this.target && this.target.name
return this._set ('entity', this.target && this.target.name)
}
set path(p) {
if (p) super.path = p.startsWith('/') ? p.slice(1) : p
}
set path(p) { if (p) super.path = p.startsWith('/') ? p.slice(1) : p }
get path() {

@@ -38,19 +34,21 @@ const {_} = this

const q = this.query
if (q.SELECT) return super.path = _path4 (q.SELECT,'from')
if (q.INSERT) return super.path = _path4 (q.INSERT,'into')
if (q.UPDATE) return super.path = _path4 (q.UPDATE,'entity')
if (q.DELETE) return super.path = _path4 (q.DELETE,'from')
if (q.SELECT) return this._set ('path', _path4 (q.SELECT,'from'))
if (q.INSERT) return this._set ('path', _path4 (q.INSERT,'into'))
if (q.UPDATE) return this._set ('path', _path4 (q.UPDATE,'entity'))
if (q.DELETE) return this._set ('path', _path4 (q.DELETE,'from'))
}
if (_.target) return super.path = _.target.name
if (_.entity) return super.path = _.entity.name || _.entity
else return super.path = undefined
if (_.target) return this._set ('path', _.target.name)
if (_.entity) return this._set ('path', _.entity.name || _.entity)
else return this._set ('path', undefined)
}
set data(d) { if(d) super.data = d } //> undefined data is frequently passed in via ._
set data(d) { if (d) super.data = d }
get data() {
const q = this.query; if (!q) return super.data = undefined
const { INSERT:I, UPDATE:U } = q
if (I) return super.data = I.rows || I.values || I.entries && (I.entries.length > 1 ? I.entries : I.entries[0]) ||{}
if (U) return super.data = U.data ||{}
return super.data = {}
const q = this.query
if (!q) return this._set ('data', undefined)
const I = q.INSERT
if (I) return this._set ('data', I.rows || I.values || I.entries && (I.entries.length > 1 ? I.entries : I.entries[0]) ||{})
const U = q.UPDATE
if (U) return this._set ('data', U.data ||{})
return this._set ('data', {})
}

@@ -69,9 +67,10 @@

// Lazily create message collectors for .errors and .messages
/** @private */ get _messages() { return this.messages = super._messages = new Responses }
/** @private */ get _errors() { return this.errors = super._errors = new Errors }
/** @private */ get _messages() { return this.messages = this._set ('_messages', new Responses) }
/** @private */ get _errors() { return this.errors = this._set ('_errors', new Errors) }
// REVISIT: Used for request logging in cds.server
// REVISIT: _.odataReq stuff should go into subclass ODataRequest
get _path() { return super._path = this._.odataReq ? this._.odataReq._url.pathname : this._.req && this._.req.path }
get _query() { return super._query = this._.odataReq ? this._.odataReq._queryOptions : this._.req && this._.req.query }
get _path() { return this._set ('_path', this._.odataReq ? this._.odataReq._url.pathname : this._.req && this._.req.path) }
get _query() { return this._set ('_query', this._.odataReq ? this._.odataReq._queryOptions : this._.req && this._.req.query) }
}

@@ -103,12 +102,15 @@

const SQL2Crud = {
SELECT: 'READ',
INSERT: 'CREATE',
UPDATE: 'UPDATE',
DELETE: 'DELETE',
CREATE: 'CREATE ENTITY',
DROP: 'DROP ENTITY',
SELECT: 'READ',
INSERT: 'CREATE',
UPDATE: 'UPDATE',
DELETE: 'DELETE',
BEGIN: 'BEGIN',
COMMIT: 'COMMIT',
ROLLBACK: 'ROLLBACK',
CREATE: 'CREATE ENTITY',
DROP: 'DROP ENTITY',
}
const Query2Crud = (q) => {
if (typeof q === 'string') return q.match(/^\s*(\w+)/) && SQL2Crud[RegExp.$1] || q
if (typeof q === 'string') return SQL2Crud[q] || /^\s*(\w+)/.test(q) && SQL2Crud[RegExp.$1] || q
else for (let each in q) if (each in SQL2Crud) return SQL2Crud[each]

@@ -136,8 +138,2 @@ }

// REVISIT: remove after req.statements isn't used anymore in @sap/cds-runtime
statements: {
get() { return super.statements = global.cds.ql },
set(ql) { super.statements = ql },
},
})

@@ -42,4 +42,2 @@ const { ProtocolAdapter } = require('./srv/adapters')

return cds.load(from)
// const cached = cds_serve, key = `${from}`
// return cached[key] || (cached[key] = cds.load(from))
})

@@ -79,3 +77,3 @@

// 5) Pass 2: Finalize service bootrapping by calling their impl functions.
// 5) Pass 2: Finalize service bootstrapping by calling their impl functions.
// Note: doing that in a second pass guarantees all own services are in

@@ -82,0 +80,0 @@ // cds.services, so they'll be found when they cds.connect to each others.

@@ -15,3 +15,3 @@ const add_methods_to = require ('./Service-methods')

/**
* Subclasses may override this to prepare the given model
* Subclasses may override this to prepare the given model appropriately
*/

@@ -24,35 +24,17 @@ set model(m) {

/**
* Subclasses may override this to free additional resources
* Messaging API to emit asynchronous event messages, i.e. instances of `cds.Event`.
*/
disconnect (tenant) { // eslint-disable-line no-unused-vars
if (this === cds.db) cds.db = undefined //> REVISIT: should go into DatabaseService
delete cds.services[this.name]
}
/**
* Emits asynchronous event messages, i.e. instances of `cds.Event`, constructed from given arguments.
*/
emit (event, data, headers) {
if (event.query) return this.send (event) // REVISIT: Remove that when new runtime & tests are released
if (event in sync_events) return this.send ({ event, data, headers }) // REVISIT: Remove that when new runtime & tests are released
if (event instanceof cds.Event) return this.dispatch (event)
if (typeof event === 'object') {
if ((event.event || event.method) in sync_events) return this.dispatch (new cds.Request(event))
return this.dispatch (new cds.Event(event))
}
else return this.dispatch (new cds.Event ({ event, data, headers }))
const res = this._compat_sync (event, data, headers); if (res) return res
const eve = cds.Event.for(event) || new cds.Event({ event, data, headers })
return this.dispatch (eve)
}
/**
* Sends synchronous requests, i.e. instances of `cds.Request`, constructed from given arguments.
* REST-style API to send synchronous requests...
*/
send (method, path, data, headers) {
if (method instanceof cds.Request) return this.dispatch (method)
if (typeof method === 'object') return this.dispatch (new cds.Request(method))
else return this.dispatch (new cds.Request ({ method, path, data, headers }))
const req = cds.Request.for(method) || new cds.Request({ method, path, data, headers })
return this.dispatch (req)
}
/**
* REST-style API...
*/
get (path, data) { return is_rest(path) ? this.send('GET', path,data) : this.read (path, data) }

@@ -65,5 +47,8 @@ put (path, data) { return is_rest(path) ? this.send('PUT', path,data) : this.update (path, data) }

/**
* Querying API...
* Querying API to send synchronous requests...
*/
run (query,data) { return this.send (data ? { query, data } : { query }) }
run (query, data) {
const req = new cds.Request ({ query, data })
return this.dispatch (req)
}
read (...args) { return SELECT.from(...args).bind(this) }

@@ -73,2 +58,8 @@ insert (...args) { return INSERT(...args).bind(this) }

update (...args) { return UPDATE.entity(...args).bind(this) }
/**
* Streaming API variant of .run(). Subclasses should override this to support real streaming.
* The default implementation doesn't stream, but simply invokes the callback on each row.
* The callback function is invoked with (row, index).
*/
foreach (query, data, callback) {

@@ -89,5 +80,13 @@ if (!callback) [ data, callback ] = [ undefined, data ]

|| this.model && this.model.namespace
|| this.name !== 'db' && !/[^\w.]/.test(this.name) && this.name //> REVISIT: should go into DatabaseService
|| this.name !== 'db' && !/\W/.test(this.name) && this.name || undefined
}
/**
* Subclasses may override this to free private resources
*/
disconnect (tenant) { // eslint-disable-line no-unused-vars
if (this === cds.db) cds.db = undefined //> REVISIT: should go into DatabaseService
delete cds.services[this.name]
}
}

@@ -97,2 +96,3 @@ module.exports = Service

Service.prototype.dispatch = require('./Service-dispatch')
Service.prototype._compat_sync = require('./Service-compat')
Service.prototype._implicit_next = cds.env.features.implicit_next

@@ -105,2 +105,1 @@ Service.prototype._is_service_instance = Service._is_service_class = true //> for factory

const is_rest = x => x && typeof x === 'string' && x[0] === '/'
const sync_events = { CREATE:1, READ:1, UPDATE:1, DELETE:1, INSERT:1, GET:1, PUT:1, POST:1, PATCH:1, BEGIN:1, COMMIT:1, ROLLBACK:1 }

@@ -7,3 +7,3 @@ /**

*/
module.exports = async function srv_dispatch (req) { //NOSONAR
module.exports = async function dispatch (req) { //NOSONAR

@@ -14,4 +14,6 @@ // Ensure we are in a proper transaction

if (txc) return this.tx(txc).dispatch(req)
const tx = this.tx(req) //> else start a new top-level tx, which we need to commit/rollback
return tx.dispatch(req) .then (tx.commit, tx.rollback)
else try { //> start a new top-level tx, which we need to commit/rollback
const tx = cds.context = this.tx(req)
return tx.dispatch(req) .then (tx.commit, tx.rollback)
} finally { cds.context = txc }
}

@@ -89,3 +91,4 @@ // `this` is a tx from now on...

if (srv.namespace) { // ensure fully-qualified names
if (req._.path) _ensure_fqn (req,'path',srv)
const p = req._.path
if (p) _ensure_fqn (req,'path',srv, p.startsWith('/') ? p.slice(1) : p)
else if (q.SELECT) _ensure_fqn (q.SELECT,'from',srv)

@@ -102,4 +105,3 @@ else if (q.INSERT) _ensure_fqn (q.INSERT,'into',srv)

const _ensure_fqn = (x,p,srv) => {
const name = x[p]
const _ensure_fqn = (x,p,srv, name = x[p]) => {
if (typeof name === 'string') {

@@ -106,0 +108,0 @@ if (srv.model && name in srv.model.definitions) return

@@ -6,3 +6,3 @@ const cds = require('..')

constructor (name) {
this._handlers = { on:[], before:[], after:[], _initial:[] }
this._handlers = { on:[], before:[], after:[], _initial:[] } //, _error:[] }
this.name = name

@@ -16,3 +16,3 @@ }

const handler = (req) => req.reject(
405, `Event "${event}" not allowed${req.target ? ` for entity "${req.target.name}"`: ''}.`
405, `Event "${req.event}" not allowed${req.target ? ` for entity "${req.target.name}"`: ''}.`
)

@@ -24,3 +24,3 @@ handler._initial = true

async prepend (...impl_functions) {
const {_handlers} = this, _new = this._handlers = { on:[], before:[], after:[], _initial:[] }
const {_handlers} = this, _new = this._handlers = { on:[], before:[], after:[], _initial:[] } //, _error:[] }
await Promise.all (impl_functions.map (fn => is_impl(fn) && fn.call (this,this)))

@@ -71,3 +71,6 @@ for (let each in _new) if (_new[each].length) _handlers[each] = [ ..._new[each], ..._handlers[each] ]

if (handler._initial) phase = '_initial'
srv.subscribe (phase, event, path, handler)
// REVISIT + PARKED: Should we add srv.on('error',(err)=>...)?
// else if (event === 'error') phase = '_error'
else if (phase === 'on') cds.emit('subscribe',srv,event)
srv._handlers[phase].push ({ event, path, handler })
}

@@ -77,8 +80,2 @@ return srv

// REVISIT: This exists for messaging impl only -> we should get rid of it and re-inline it to _register
EventHandlers.prototype.subscribe = function (phase, event, path, handler) {
this._handlers[phase].push ({ event, path, handler })
if (phase === 'on') cds.emit('subscribe',this,event)
}
const _expected = (arg,type) => cds.error(`Expected ${type} for argument '${Object.keys(arg)[0]}' but got: ${Object.values(arg)[0]}`)

@@ -85,0 +82,0 @@ const is_class = x => typeof x === 'function' && x.prototype && /^class\b/.test(x)

@@ -9,3 +9,3 @@ const cds = require('../index'), { Context } = cds.Request

*/
module.exports = function (req) { const srv = this
module.exports = function tx (req) { const srv = this
if (srv.context) return srv

@@ -18,3 +18,3 @@ if (!req) {

// called for a nested req -> nested tx
if (req.context) return NestedTransaction.for (srv, req.context)
if (req.context !== req) return NestedTransaction.for (srv, req.context)
// called for a req with a root tx -> nested tx

@@ -42,8 +42,6 @@ if (req._tx) return NestedTransaction.for (srv, req)

static for (srv,root) {
const txs = root.transactions || (root.transactions = new Map)
let txs = root.transactions
if (!txs) Object.defineProperty(root, 'transactions', {value: txs = new Map})
let tx = txs.get (srv)
if (!tx) {
tx = new this (srv,root)
txs.set (srv,tx)
}
if (!tx) txs.set (srv, tx = new this (srv,root))
return tx

@@ -53,9 +51,6 @@ }

constructor (srv,root) {
const tx = _init ({ __proto__:srv, context:root })
const proto = new.target.prototype
const tx = Object.create(srv)
if ('begin' in srv) tx.dispatch = tx.start = Transaction.prototype.start
tx.commit = proto.commit.bind(tx)
tx.rollback = proto.rollback.bind(tx)
tx.context = root
tx.ready = false
return tx

@@ -65,17 +60,4 @@ }

/**
* Used before the first srv.dispatch in a transaction, to ensure the
* service's implementation of .begin is called appropriately.
*/
async start (req) {
if (req.query || req.event !== 'BEGIN') {
if (!this.ready) this.ready = this.begin()
await this.ready //> parallel .dispatch events queue here
delete this.dispatch
}
return this.__proto__.dispatch.call (this,req)
}
/**
* In addition to srv.commit, resets the transaction to initial state,
* in order to re-.start on subsequently .dispatched events.
* in order to re-start on subsequently dispatched events.
*/

@@ -85,4 +67,3 @@ async commit (res) {

if (this.__proto__.commit) await this.__proto__.commit.call (this,res)
this.dispatch = this.start
this.ready = false
_init (this)
}

@@ -94,9 +75,13 @@ return res

* In addition to srv.rollback, resets the transaction to initial state,
* in order to re-.start on subsequently .dispatched events.
* in order to re-start on subsequently dispatched events.
*/
async rollback (err) {
// REVISIT + PARKED: Should we add srv.on('error',(err)=>...)?
// const err_handlers = this._handlers._error
// if (err_handlers.length) {
// await Promise.all (err_handlers.map (each => each.handler.call (this,err)))
// }
if (this.ready) { //> nothing to do if no transaction started at all
if (this.__proto__.rollback) await this.__proto__.rollback.call (this,err)
this.dispatch = this.start
this.ready = false
_init (this)
}

@@ -157,1 +142,20 @@ if (err) throw err

}
/**
* Ensure the service's implementation of .begin is called appropriately
* before any .dispatch.
*/
const _init = (tx) => {
if ('begin' in tx) tx.dispatch = begin_and_dispatch
tx.ready = false
return tx
}
async function begin_and_dispatch (req) {
if (!req.query && req.method === 'BEGIN') // IMPORTANT: !req.query is to exclude batch requests
return this.ready = this.__proto__.dispatch.call (this,req)
if (!this.ready) this.ready = this.begin()
await this.ready
delete this.dispatch
return this.dispatch (req)
}

@@ -18,6 +18,11 @@ /**

* @param {string} [module] the module for which a logger is requested
* @param {string|number} [level] the log level to enable -> 0=off, 1=error, 2=warn, 3=info, 4=debug, 5=trace
* @param {string|number|{ level, prefix }} [level] the log level to enable -> 0=off, 1=error, 2=warn, 3=info, 4=debug, 5=trace
* @param {string} [prefix] a prefix to prepend to each log output, default: '[cds.<module>] -'
*/
module.exports = exports = function cds_log (module, level, prefix = module ? `[cds.${module}] -` : `[cds] -`) { // NOSONAR
module.exports = exports = function cds_log (module, level, prefix) { // NOSONAR
if (typeof level === 'object') {
prefix = level.prefix
level = level.level
}
if (!prefix) prefix = module ? `[cds.${module.match(/^[^|]+/)}] -` : `[cds] -`
if (!level) {

@@ -42,2 +47,13 @@ const regex = RegExp(`\\b(y|all|${module||'any'})\\b`)

/**
* Shortcut to `cds.log(...).debug`, returning undefined if `cds.log(...)._debug` is false.
* @param {string} [module] the module for which a logger is requested
* @param {string|{ level, prefix }} [prefix] a prefix to prepend to each log output, default: '[cds.<module>] -'
*/
exports.debug = function cds_debug (module, prefix) {
const L = this.log (module, typeof prefix === 'object' ? prefix : {prefix})
return L._debug && L.debug
}
/**
* Constructs a new Logger with the method signature of `{ debug, log, info, warn, error }`

@@ -58,5 +74,5 @@ * from console. The default implementation actually maps it to `global.console`.

trace: level < TRACE ? ()=>{} : (...args) => console.trace (prefix, ...args),
debug: level < DEBUG ? ()=>{} : (...args) => console.debug (prefix, ...args),
log: level < INFO ? ()=>{} : (...args) => console.log (prefix, ...args),
info: level < INFO ? ()=>{} : (...args) => console.info (prefix, ...args),
debug: level < DEBUG ? ()=>{} : (...args) => console.warn (prefix, ...args),
log: level < INFO ? ()=>{} : (...args) => console.warn (prefix, ...args),
info: level < INFO ? ()=>{} : (...args) => console.warn (prefix, ...args),
warn: level < WARN ? ()=>{} : (...args) => console.warn (prefix, ...args),

@@ -63,0 +79,0 @@ error: level < ERROR ? ()=>{} : (...args) => console.error (prefix, ...args),

{
"name": "@sap/cds",
"version": "4.4.10",
"version": "4.5.1",
"description": "SAP Cloud Application Programming Model - CDS for Node.js",

@@ -29,7 +29,6 @@ "homepage": "https://cap.cloud.sap/",

"dependencies": {
"@sap/cds-compiler": "^1.43.1",
"@sap/cds-reflect": "^2.13.0",
"@sap/cds-runtime": "~2.7.0",
"@sap/cds-compiler": "^1.49.0",
"@sap/cds-runtime": "~2.8.0",
"@sap/cds-foss": "^2"
}
}

@@ -21,3 +21,3 @@ const express = require('express')

* @param {express.Application} options.app - filenames of models to load; default: '*'
* @param {express.Handler} options.index_html - custom handler for /
* @param {express.Handler} options.index - custom handler for /
* @param {express.Handler} options.favicon - custom handler for /favicon.ico

@@ -30,29 +30,25 @@ * @param {express.Handler} options.logger - custom request logger middleware

const app = cds.app = o.app || express()
cds.emit('bootstrap',app) // hook for project-local server.js
cds.emit ('bootstrap',app) //> hook for project-local server.js
// mount static resources and common middlewares...
// mount static resources and logger middleware
app.use (express.static (cds.env.folders.app)) //> defaults to ./app
app.use ('/favicon.ico', o.favicon) //> if none in ./app
app.get ('/', o.index_html) //> if none in ./app
app.use (o.logger) //> basic request logging
if (o.index) app.get ('/',o.index) //> if none in ./app
if (o.logger) app.use (o.logger) //> basic request logging
// load specified models or all in project
let model = cds.model = await cds.load (o.from)
const csn = await cds.load (o.from||'*')
cds.model = cds.linked (cds.compile.for.odata(csn))
// bootstrap --in-memory db if requested
if (o.in_memory) await cds.deploy (model,o)
// connect to essential framework services if required
if (cds.requires.messaging) await cds.connect.to ('messaging')
if (cds.requires.db) cds.db = await cds.connect.to ('db')
// pre-unfold models to minimize memory consumption
if (cds.env.features.snapi) model = cds.model = cds.linked(cds.compile.for.odata(model))
// bootstrap in-memory db if requested
if (o.in_memory) await cds.deploy (csn).to (cds.db,o)
// connect to primary database if required
if (cds.requires.db) cds.db = await cds.connect.to('db')
// serve all services declared in models
await cds.serve (o.service,o).in (app)
cds.emit ('served', cds.services) //> hook for listeners
// bootstrap messaging service if required
if (cds.requires.messaging) await cds.connect.to('messaging')
// construct and mount modelled services
/* cds.services = */ await cds.serve(o).from (model) .in (app)
cds.emit ('served', cds.services)
// start http server

@@ -70,3 +66,3 @@ return app.listen (o.port || process.env.PORT || 4004)

// default generic index.html page
get index_html() {
get index() {
const index = require ('./app/index.js')

@@ -98,2 +94,2 @@ return (_,res) => res.send (index.html)

// -------------------------------------------------------------------------
if (!module.parent) module.exports (process.argv[2])
if (!module.parent) module.exports ({from:process.argv[2]})

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc