@sap/cds-dk 10.1.0 → 10.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/bin/bind.js +4 -0
  3. package/bin/build.js +1 -0
  4. package/bin/compile/index.js +12 -11
  5. package/bin/completion/index.js +21 -10
  6. package/bin/completion/scripts/cds.fish +179 -127
  7. package/bin/completion/scripts/cds.ps1 +88 -14
  8. package/bin/completion/scripts/cds.sh +71 -5
  9. package/bin/completion/scripts/cds.zsh +81 -15
  10. package/bin/completion/templates/cds-tmpl.ps1 +15 -6
  11. package/bin/deploy/index.js +7 -3
  12. package/bin/env.js +4 -1
  13. package/bin/export.js +9 -1
  14. package/bin/import.js +4 -1
  15. package/bin/init.js +1 -0
  16. package/bin/inspect.js +3 -1
  17. package/bin/lint.js +5 -3
  18. package/bin/minify.js +6 -1
  19. package/bin/repl.js +11 -2
  20. package/bin/run.js +12 -1
  21. package/bin/serve.js +13 -2
  22. package/bin/up.js +4 -1
  23. package/bin/util/containerize.js +2 -0
  24. package/bin/watch/index.js +8 -1
  25. package/lib/build/util.js +2 -4
  26. package/lib/init/constants.js +1 -1
  27. package/lib/init/template/data/as-json.js +3 -0
  28. package/node_modules/@cap-js/db-service/CHANGELOG.md +17 -0
  29. package/node_modules/@cap-js/db-service/lib/common/generic-pool.js +4 -7
  30. package/node_modules/@cap-js/db-service/lib/cqn2pql.js +1 -1
  31. package/node_modules/@cap-js/db-service/lib/cqn2sql.js +15 -11
  32. package/node_modules/@cap-js/db-service/lib/cqn4sql.js +119 -19
  33. package/node_modules/@cap-js/db-service/lib/infer/index.js +27 -16
  34. package/node_modules/@cap-js/db-service/lib/recursion-guard.js +40 -0
  35. package/node_modules/@cap-js/db-service/lib/search.js +1 -1
  36. package/node_modules/@cap-js/db-service/package.json +1 -1
  37. package/node_modules/@cap-js/sqlite/CHANGELOG.md +19 -0
  38. package/node_modules/@cap-js/sqlite/lib/SQLiteService.js +110 -14
  39. package/node_modules/@cap-js/sqlite/package.json +3 -3
  40. package/node_modules/@sap/cds-compiler/CHANGELOG.md +15 -0
  41. package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +7 -3
  42. package/node_modules/@sap/cds-compiler/lib/model/csnUtils.js +2 -0
  43. package/node_modules/@sap/cds-compiler/lib/transform/db/applyTransformations.js +12 -0
  44. package/node_modules/@sap/cds-compiler/lib/transform/db/associations.js +5 -3
  45. package/node_modules/@sap/cds-compiler/lib/transform/db/flattening.js +4 -3
  46. package/node_modules/@sap/cds-compiler/lib/transform/effective/flattening.js +17 -1
  47. package/node_modules/@sap/cds-compiler/lib/transform/effective/main.js +19 -0
  48. package/node_modules/@sap/cds-compiler/lib/transform/fioriTreeViews.js +12 -1
  49. package/node_modules/@sap/cds-compiler/lib/transform/forRelationalDB.js +1 -1
  50. package/node_modules/@sap/cds-compiler/lib/transform/odata/adaptAnnotationRefs.js +11 -5
  51. package/node_modules/@sap/cds-compiler/lib/transform/translateAssocsToJoins.js +29 -2
  52. package/node_modules/@sap/cds-compiler/package.json +1 -1
  53. package/node_modules/@sap/cds-mtxs/CHANGELOG.md +34 -0
  54. package/node_modules/@sap/cds-mtxs/bin/cds-mtx-migrate.js +26 -5
  55. package/node_modules/@sap/cds-mtxs/bin/cds-mtx.js +3 -0
  56. package/node_modules/@sap/cds-mtxs/lib/utils.js +5 -4
  57. package/node_modules/@sap/cds-mtxs/package.json +1 -1
  58. package/node_modules/@sap/cds-mtxs/srv/cf/abstract-provisioning-service.js +62 -7
  59. package/node_modules/@sap/cds-mtxs/srv/cf/saas-provisioning-service.js +1 -1
  60. package/node_modules/@sap/cds-mtxs/srv/cf/sms-provisioning-service.js +23 -3
  61. package/node_modules/@sap/cds-mtxs/srv/extensibility/compat/update.js +15 -0
  62. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/annotations.js +2 -2
  63. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/config.js +2 -0
  64. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/index.js +2 -0
  65. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/protected-namespace.js +45 -0
  66. package/node_modules/@sap/cds-mtxs/srv/extensibility-service.js +8 -3
  67. package/node_modules/@sap/cds-mtxs/srv/jobs-service.js +49 -10
  68. package/node_modules/@sap/cds-mtxs/srv/model-provider.js +11 -2
  69. package/node_modules/@sap/cds-mtxs/srv/plugins/common.js +19 -12
  70. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/hdi-ctnr-mgr.js +6 -3
  71. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/serverless.js +271 -0
  72. package/node_modules/@sap/cds-mtxs/srv/plugins/hana.js +3 -2
  73. package/node_modules/@sap/cds-mtxs/srv/worker/job-worker-pool.js +212 -0
  74. package/node_modules/@sap/cds-mtxs/srv/worker/job-worker.js +61 -0
  75. package/package.json +2 -2
  76. /package/node_modules/@sap/cds-mtxs/srv/worker/{pool.js → model-worker-pool.js} +0 -0
  77. /package/node_modules/@sap/cds-mtxs/srv/worker/{load.js → model-worker.js} +0 -0
@@ -0,0 +1,212 @@
1
+ 'use strict'
2
+
3
+ const { Worker } = require('worker_threads')
4
+ const path = require('path')
5
+ const cds = require('@sap/cds')
6
+
7
+ const LOG = cds.log('mtx')
8
+ const WORKER_PATH = path.join(__dirname, 'job-worker.js')
9
+
10
+ const IDLE = 'idle'
11
+ const BUSY = 'busy'
12
+ const BOOTING = 'booting'
13
+
14
+ class DeploymentPool {
15
+
16
+ constructor(size) {
17
+ this.size = size
18
+ this.workers = [] // { worker, state, pending: Map<id, {resolve,reject}> }
19
+ this.waitQueue = [] // queued acquire requests: { resolve, reject }
20
+ this._idCounter = 0
21
+ this._shuttingDown = false
22
+ }
23
+
24
+ /**
25
+ * Spawn a new worker, bootstrap it, and add it to the pool.
26
+ * Returns a promise that resolves when the worker is ready.
27
+ */
28
+ _spawn() {
29
+ return new Promise((resolve, reject) => {
30
+ const worker = new Worker(WORKER_PATH, { env: process.env, workerData: { cdsEnv: cds.env, cdsRoot: cds.root } })
31
+ const entry = { worker, state: BOOTING, pending: new Map() }
32
+ this.workers.push(entry)
33
+
34
+ const onReady = (msg) => {
35
+ if (msg.ready) {
36
+ worker.off('message', onReady)
37
+ worker.off('error', onError)
38
+ entry.state = IDLE
39
+ worker.on('message', (msg) => this._onMessage(entry, msg))
40
+ worker.on('error', (err) => this._onError(entry, err))
41
+ worker.on('exit', (code) => this._onExit(entry, code))
42
+ resolve(entry)
43
+ } else if (msg.error) {
44
+ worker.off('message', onReady)
45
+ worker.off('error', onError)
46
+ this.workers = this.workers.filter(e => e !== entry)
47
+ reject(new Error(msg.error))
48
+ }
49
+ }
50
+
51
+ const onError = (err) => {
52
+ worker.off('message', onReady)
53
+ this.workers = this.workers.filter(e => e !== entry)
54
+ reject(err)
55
+ }
56
+
57
+ worker.on('message', onReady)
58
+ worker.on('error', onError)
59
+ })
60
+ }
61
+
62
+ /**
63
+ * Handle messages from a worker (command responses).
64
+ */
65
+ _onMessage(entry, { id, result, error }) {
66
+ const pending = entry.pending.get(id)
67
+ if (!pending) return
68
+ entry.pending.delete(id)
69
+ if (error) pending.reject(new Error(error))
70
+ else pending.resolve(result)
71
+ }
72
+
73
+ /**
74
+ * Handle worker errors.
75
+ */
76
+ _onError(entry, err) {
77
+ LOG.error('Deployment worker error:', err.message)
78
+ // Reject all pending tasks on this worker
79
+ for (const [, { reject }] of entry.pending) {
80
+ reject(new Error(`Worker error: ${err.message}`))
81
+ }
82
+ entry.pending.clear()
83
+ }
84
+
85
+ /**
86
+ * Handle worker exit — reject in-flight tasks and replace the worker.
87
+ */
88
+ _onExit(entry, code) {
89
+ this.workers = this.workers.filter(e => e !== entry)
90
+
91
+ // Reject any in-flight tasks
92
+ for (const [, { reject }] of entry.pending) {
93
+ reject(new Error(`Worker exited unexpectedly with code ${code}`))
94
+ }
95
+ entry.pending.clear()
96
+
97
+ // Do not replace workers during shutdown
98
+ if (this._shuttingDown) return
99
+
100
+ // Immediately replace the crashed worker
101
+ LOG.warn('Deployment worker exited with code', code, '- spawning replacement')
102
+ this._spawn().then(newEntry => {
103
+ // If there are queued requests waiting, serve them
104
+ if (this.waitQueue.length > 0 && newEntry.state === IDLE) {
105
+ newEntry.state = BUSY
106
+ const { resolve } = this.waitQueue.shift()
107
+ resolve(newEntry)
108
+ }
109
+ }).catch(err => {
110
+ LOG.error('Failed to spawn replacement worker:', err.message)
111
+ // If there are queued requests waiting, reject the first one
112
+ if (this.waitQueue.length > 0) {
113
+ const { reject } = this.waitQueue.shift()
114
+ reject(err)
115
+ }
116
+ })
117
+ }
118
+
119
+ /**
120
+ * Acquire an idle worker. If none available and pool not full, spawns a new one.
121
+ * If all workers are busy and pool is full, queues the request.
122
+ */
123
+ async acquire() {
124
+ // Try to find an idle worker
125
+ const idle = this.workers.find(e => e.state === IDLE)
126
+ if (idle) {
127
+ idle.state = BUSY
128
+ return idle
129
+ }
130
+
131
+ // Pool not full — spawn a new worker
132
+ if (this.workers.length < this.size) {
133
+ const entry = await this._spawn()
134
+ entry.state = BUSY
135
+ return entry
136
+ }
137
+
138
+ // All busy — wait in queue
139
+ return new Promise((resolve, reject) => {
140
+ this.waitQueue.push({ resolve, reject })
141
+ })
142
+ }
143
+
144
+ /**
145
+ * Release a worker back to idle state. Serves the next queued request if any.
146
+ */
147
+ release(entry) {
148
+ if (this.waitQueue.length > 0) {
149
+ entry.state = BUSY
150
+ const { resolve } = this.waitQueue.shift()
151
+ resolve(entry)
152
+ } else {
153
+ entry.state = IDLE
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Execute a service action on a worker.
159
+ * Acquires a worker, sends the command, waits for the result, releases the worker.
160
+ */
161
+ async exec(service, op, tenant, args) {
162
+ const entry = await this.acquire()
163
+ const id = ++this._idCounter
164
+ try {
165
+ const result = await new Promise((resolve, reject) => {
166
+ entry.pending.set(id, { resolve, reject })
167
+ entry.worker.postMessage({ id, service, op, tenant, args, cdsEnv: cds.env })
168
+ })
169
+ return result
170
+ } finally {
171
+ this.release(entry)
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Shut down all workers. Sends a shutdown message to each worker and waits for them to exit.
177
+ */
178
+ async shutdown() {
179
+ this._shuttingDown = true
180
+
181
+ // Reject all queued acquire requests
182
+ for (const { reject } of this.waitQueue) {
183
+ reject(new Error('Worker pool is shutting down'))
184
+ }
185
+ this.waitQueue = []
186
+
187
+ // Send shutdown message to all workers and wait for them to exit
188
+ await Promise.all(this.workers.map(entry => {
189
+ for (const [, { reject }] of entry.pending) {
190
+ reject(new Error('Worker pool is shutting down'))
191
+ }
192
+ entry.pending.clear()
193
+ entry.worker.postMessage({ shutdown: true })
194
+ return new Promise(resolve => entry.worker.once('exit', resolve))
195
+ }))
196
+ this.workers = []
197
+ }
198
+ }
199
+
200
+ let _pool
201
+
202
+ /**
203
+ * Returns the singleton DeploymentPool instance.
204
+ * @param {number} size - Pool size (number of workers)
205
+ */
206
+ module.exports = function getDeploymentPool(size) {
207
+ if (!_pool) {
208
+ _pool = new DeploymentPool(size)
209
+ cds.on('shutdown', async () => _pool.shutdown())
210
+ }
211
+ return _pool
212
+ }
@@ -0,0 +1,61 @@
1
+ 'use strict'
2
+
3
+ const { parentPort, workerData } = require('worker_threads')
4
+ const cds = require('@sap/cds')
5
+ const { _import, isfile, path } = cds.utils
6
+
7
+ if (workerData?.cdsRoot) cds.root = workerData.cdsRoot
8
+ if (workerData?.cdsEnv) cds.env.add(workerData.cdsEnv)
9
+
10
+ async function bootstrap() {
11
+ const services = [
12
+ '@sap/cds-mtxs/srv/deployment-service',
13
+ '@sap/cds-mtxs/srv/model-provider',
14
+ '@sap/cds-mtxs/srv/cf/saas-provisioning-service',
15
+ '@sap/cds-mtxs/srv/extensibility-service',
16
+ ]
17
+ await _local_cli_js()
18
+ cds.emit('bootstrap')
19
+ const model = await cds.load([...services, '@sap/cds-mtxs/db/extensions'])
20
+ cds.model = cds.compile.for.nodejs(model)
21
+ const unserved = !cds.services['cds.xt.DeploymentService']
22
+ if (unserved) await cds.serve(services)
23
+ await cds.plugins
24
+ await cds.emit('served', cds.services)
25
+ parentPort.postMessage({ ready: true })
26
+ }
27
+
28
+ async function _local_cli_js() {
29
+ const _local = file => isfile(file) || isfile(path.join(cds.env.folders.srv, file))
30
+ const cli_js = process.env.CDS_TYPESCRIPT && (_local('cli.ts') || _local('server.ts')) || _local('cli.js') || _local('server.js')
31
+ if (cli_js) await _import(cli_js)
32
+ }
33
+
34
+ parentPort.on('message', async (msg) => {
35
+ if (msg.shutdown) {
36
+ await cds.shutdown()
37
+ process.exit(0)
38
+ }
39
+ const { id, service, op, tenant, args, cdsEnv } = msg
40
+ if (cdsEnv) cds.env.add(cdsEnv)
41
+ const srv = cds.services[service]
42
+ if (!srv) {
43
+ parentPort.postMessage({ id, error: `Service ${service} not available` })
44
+ return
45
+ }
46
+ try {
47
+ const result = await srv[op](tenant, ...Object.values(args))
48
+ parentPort.postMessage({ id, result })
49
+ } catch (e) {
50
+ const error = e.message ?? 'Unknown error'
51
+ parentPort.postMessage({ id, error })
52
+ } finally {
53
+ // cds.db.disconnect(cds.env.requires.multitenancy.t0 ?? 't0')
54
+ // cds.db.disconnect(tenant)
55
+ }
56
+ })
57
+
58
+ bootstrap().catch(e => {
59
+ parentPort.postMessage({ error: `Bootstrap failed: ${e.message}` })
60
+ process.exit(1)
61
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "10.1.0",
3
+ "version": "10.1.1",
4
4
  "description": "Command line client and development toolkit for the SAP Cloud Application Programming Model",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
@@ -21,7 +21,7 @@
21
21
  "@cap-js/openapi": "^1.0.0",
22
22
  "@cap-js/sqlite": "^3",
23
23
  "@sap/cds": "^10",
24
- "@sap/cds-mtxs": "4.0.3",
24
+ "@sap/cds-mtxs": "^4",
25
25
  "@sap/hdi-deploy": "^5",
26
26
  "express": "^5",
27
27
  "hdb": "^2.0.0",