@sap/async-xsjs 1.0.2

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 (178) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +37 -0
  3. package/README.md +445 -0
  4. package/differences.md +162 -0
  5. package/docs/$.Application.html +262 -0
  6. package/docs/$.Session.html +674 -0
  7. package/docs/$.db.CallableStatement.html +2524 -0
  8. package/docs/$.db.Connection.html +511 -0
  9. package/docs/$.db.ParameterMetaData.html +805 -0
  10. package/docs/$.db.PreparedStatement.html +1796 -0
  11. package/docs/$.db.ResultSet.html +1308 -0
  12. package/docs/$.db.ResultSetMetaData.html +800 -0
  13. package/docs/$.db.SQLException.html +259 -0
  14. package/docs/$.db.html +773 -0
  15. package/docs/$.hdb.ColumnMetadata.html +438 -0
  16. package/docs/$.hdb.Connection.html +663 -0
  17. package/docs/$.hdb.ProcedureResult.html +280 -0
  18. package/docs/$.hdb.ResultSet.html +324 -0
  19. package/docs/$.hdb.ResultSetIterator.html +315 -0
  20. package/docs/$.hdb.ResultSetMetaData.html +259 -0
  21. package/docs/$.hdb.SQLException.html +259 -0
  22. package/docs/$.hdb.html +557 -0
  23. package/docs/$.html +471 -0
  24. package/docs/$.jobs.Job.html +783 -0
  25. package/docs/$.jobs.JobLog.html +380 -0
  26. package/docs/$.jobs.JobSchedules.html +852 -0
  27. package/docs/$.jobs.html +238 -0
  28. package/docs/$.net.Destination.html +304 -0
  29. package/docs/$.net.Mail.Part.html +510 -0
  30. package/docs/$.net.Mail.html +504 -0
  31. package/docs/$.net.SMTPConnection.html +347 -0
  32. package/docs/$.net.html +749 -0
  33. package/docs/$.net.http.Client.html +562 -0
  34. package/docs/$.net.http.Destination.html +237 -0
  35. package/docs/$.net.http.Request.html +567 -0
  36. package/docs/$.net.http.html +292 -0
  37. package/docs/$.security.AntiVirus.html +361 -0
  38. package/docs/$.security.Store.html +636 -0
  39. package/docs/$.security.crypto.html +414 -0
  40. package/docs/$.security.html +247 -0
  41. package/docs/$.security.x509.html +373 -0
  42. package/docs/$.text.analysis.Session.html +983 -0
  43. package/docs/$.text.analysis.html +242 -0
  44. package/docs/$.text.html +246 -0
  45. package/docs/$.text.mining.Session.html +2018 -0
  46. package/docs/$.text.mining.html +242 -0
  47. package/docs/$.trace.html +525 -0
  48. package/docs/$.util.SAXParser.html +955 -0
  49. package/docs/$.util.Zip.html +474 -0
  50. package/docs/$.util.codec.html +414 -0
  51. package/docs/$.util.compression.html +357 -0
  52. package/docs/$.util.html +325 -0
  53. package/docs/$.util.sql.html +290 -0
  54. package/docs/$.web.Body.html +333 -0
  55. package/docs/$.web.EntityList.html +296 -0
  56. package/docs/$.web.TupelList.html +496 -0
  57. package/docs/$.web.WebEntityRequest.html +393 -0
  58. package/docs/$.web.WebEntityResponse.html +392 -0
  59. package/docs/$.web.WebRequest.html +560 -0
  60. package/docs/$.web.WebResponse.html +609 -0
  61. package/docs/$.web.html +246 -0
  62. package/docs/Copyright-SAP.html +39 -0
  63. package/docs/Disclaimer-SAP.html +55 -0
  64. package/docs/index.html +232 -0
  65. package/docs/styles/jsdoc-default.css +382 -0
  66. package/lib/AppConfig.js +36 -0
  67. package/lib/AuditLogger.js +41 -0
  68. package/lib/cacert.js +26 -0
  69. package/lib/ctypes.js +153 -0
  70. package/lib/destinations/dest-provider.js +57 -0
  71. package/lib/index.js +235 -0
  72. package/lib/jobs/Action.js +40 -0
  73. package/lib/jobs/Job.js +100 -0
  74. package/lib/jobs/JobManager.js +150 -0
  75. package/lib/jobs/JobsRuntime.js +133 -0
  76. package/lib/jobs/SqlScriptJobRunner.js +36 -0
  77. package/lib/jobs/XsjsJobRunner.js +78 -0
  78. package/lib/jobs/index.js +11 -0
  79. package/lib/logging.js +16 -0
  80. package/lib/middleware.js +125 -0
  81. package/lib/odata/ODataService.js +125 -0
  82. package/lib/odata/index.js +7 -0
  83. package/lib/odata/service-factory.js +26 -0
  84. package/lib/passport-noauth.js +17 -0
  85. package/lib/routes.js +115 -0
  86. package/lib/runtime.js +740 -0
  87. package/lib/sandbox.js +40 -0
  88. package/lib/utils/XsJsFunctionRunner.js +57 -0
  89. package/lib/utils/XsJsLibFunctionRunner.js +57 -0
  90. package/lib/utils/buffer-utils.js +77 -0
  91. package/lib/utils/compression-utils.js +14 -0
  92. package/lib/utils/date-utils.js +104 -0
  93. package/lib/utils/errors/HttpError.js +20 -0
  94. package/lib/utils/errors/wrap-app-error.js +18 -0
  95. package/lib/utils/index.js +17 -0
  96. package/lib/utils/xs-function-runner.js +51 -0
  97. package/lib/utils/xs-types.js +21 -0
  98. package/lib/utils/xspath.js +36 -0
  99. package/lib/utils/xsstack.js +28 -0
  100. package/lib/views/error.html +28 -0
  101. package/lib/xsjs/Application.js +28 -0
  102. package/lib/xsjs/Locale.js +53 -0
  103. package/lib/xsjs/Session.js +31 -0
  104. package/lib/xsjs/constants.js +71 -0
  105. package/lib/xsjs/db/common/DbBase.js +85 -0
  106. package/lib/xsjs/db/common/DbOptions.js +163 -0
  107. package/lib/xsjs/db/common/arguments-validation.js +102 -0
  108. package/lib/xsjs/db/common/connection.js +12 -0
  109. package/lib/xsjs/db/common/enums.js +93 -0
  110. package/lib/xsjs/db/common/execute-batch.js +38 -0
  111. package/lib/xsjs/db/common/parse-time.js +139 -0
  112. package/lib/xsjs/db/dbapi/CallableStatement.js +192 -0
  113. package/lib/xsjs/db/dbapi/Connection.js +78 -0
  114. package/lib/xsjs/db/dbapi/DB.js +39 -0
  115. package/lib/xsjs/db/dbapi/ParameterMetaData.js +118 -0
  116. package/lib/xsjs/db/dbapi/PreparedStatement.js +78 -0
  117. package/lib/xsjs/db/dbapi/ResultSet.js +220 -0
  118. package/lib/xsjs/db/dbapi/ResultSetMetaData.js +116 -0
  119. package/lib/xsjs/db/dbapi/Statement.js +514 -0
  120. package/lib/xsjs/db/dbapi/conversions.js +113 -0
  121. package/lib/xsjs/db/dbapi/fetch-rows.js +32 -0
  122. package/lib/xsjs/db/hdbapi/Connection.js +525 -0
  123. package/lib/xsjs/db/hdbapi/HDB.js +32 -0
  124. package/lib/xsjs/db/hdbapi/ResultSetIterator.js +40 -0
  125. package/lib/xsjs/db/hdbapi/convert.js +77 -0
  126. package/lib/xsjs/db/hdbapi/table-string-parser.js +52 -0
  127. package/lib/xsjs/db/index.js +4 -0
  128. package/lib/xsjs/index.js +13 -0
  129. package/lib/xsjs/jobs/Job.js +228 -0
  130. package/lib/xsjs/jobs/Jobs.js +11 -0
  131. package/lib/xsjs/jobs/Logs.js +127 -0
  132. package/lib/xsjs/jobs/Schedule.js +110 -0
  133. package/lib/xsjs/jobs/Schedules.js +108 -0
  134. package/lib/xsjs/net/Destination.js +43 -0
  135. package/lib/xsjs/net/http/Client.js +220 -0
  136. package/lib/xsjs/net/http/HTTP.js +72 -0
  137. package/lib/xsjs/net/index.js +5 -0
  138. package/lib/xsjs/net/smtp/Mail.js +38 -0
  139. package/lib/xsjs/net/smtp/Part.js +30 -0
  140. package/lib/xsjs/net/smtp/SMTPConnection.js +39 -0
  141. package/lib/xsjs/net/smtp/index.js +18 -0
  142. package/lib/xsjs/net/smtp/nodemailer-util.js +77 -0
  143. package/lib/xsjs/require.js +39 -0
  144. package/lib/xsjs/security/AntiVirus.js +31 -0
  145. package/lib/xsjs/security/Store.js +119 -0
  146. package/lib/xsjs/security/crypto.js +23 -0
  147. package/lib/xsjs/security/index.js +5 -0
  148. package/lib/xsjs/security/x509.js +12 -0
  149. package/lib/xsjs/text/analysis/Session.js +128 -0
  150. package/lib/xsjs/text/index.js +30 -0
  151. package/lib/xsjs/text/mining/Session.js +82 -0
  152. package/lib/xsjs/trace/trace.js +41 -0
  153. package/lib/xsjs/util/SAXParser.js +174 -0
  154. package/lib/xsjs/util/Zip.js +220 -0
  155. package/lib/xsjs/util/codec.js +33 -0
  156. package/lib/xsjs/util/compression.js +24 -0
  157. package/lib/xsjs/util/index.js +22 -0
  158. package/lib/xsjs/web/BasicWebEntity.js +41 -0
  159. package/lib/xsjs/web/EntityList.js +11 -0
  160. package/lib/xsjs/web/TupelLists/CookiesTupelList.js +47 -0
  161. package/lib/xsjs/web/TupelLists/HeadersTupelList.js +55 -0
  162. package/lib/xsjs/web/TupelLists/ParametersTupelList.js +83 -0
  163. package/lib/xsjs/web/TupelLists/TupelListBase.js +45 -0
  164. package/lib/xsjs/web/WebBody.js +135 -0
  165. package/lib/xsjs/web/WebEntityRequest.js +40 -0
  166. package/lib/xsjs/web/WebEntityResponse.js +26 -0
  167. package/lib/xsjs/web/WebRequest.js +209 -0
  168. package/lib/xsjs/web/WebResponse.js +183 -0
  169. package/lib/xsjs/web/index.js +4 -0
  170. package/lib/xsjs/web/utils/HeadersParser.js +53 -0
  171. package/lib/xsjs/web/utils/HttpRequestParser.js +93 -0
  172. package/lib/xsjs/web/utils/MultipartParser.js +163 -0
  173. package/lib/xsjs/web/utils/MultipartResponseBuilder.js +73 -0
  174. package/lib/xsjs/web/utils/SetCookieParser.js +32 -0
  175. package/lib/xsjslib/TextBundleWrapper.js +46 -0
  176. package/lib/xsjslib/index.js +11 -0
  177. package/npm-shrinkwrap.json +11540 -0
  178. package/package.json +84 -0
package/lib/runtime.js ADDED
@@ -0,0 +1,740 @@
1
+ 'use strict';
2
+
3
+ var assert = require('assert');
4
+ var fs = require('fs');
5
+ var fsp = require('fs').promises;
6
+ var path = require('path');
7
+ var vm = require('vm');
8
+ var _ = require('lodash');
9
+ var jobs = require('./jobs');
10
+ var logger = require('./logging').logger;
11
+ var sandbox = require('./sandbox');
12
+ var xs = require('./xsjs');
13
+ var utils = require('./utils');
14
+ var util = require('util');
15
+ var VError = require('verror');
16
+ var AppConfig = require('./AppConfig');
17
+ var suppLibs = require('./xsjslib');
18
+ var destProvider = require('./destinations/dest-provider');
19
+ var Store = require('./xsjs/security/Store');
20
+ var SAPPassport = require('@sap/e2e-trace').Passport;
21
+ var DbOptions = require('./xsjs/db/common/DbOptions');
22
+
23
+ var globalLibraryCache = {
24
+ store: function (libName, library, location) {
25
+ if (location === 'global') {
26
+ this.global[libName] = library;
27
+ } else {
28
+ logger.warn('Unsupported library cache mode: ' + location);
29
+ }
30
+ },
31
+ restore: function (context) {
32
+ var parentPackage = context;
33
+ Object.keys(globalLibraryCache.global).forEach(function (libName) {
34
+ libName.split('.').forEach(function (name, index, array) {
35
+ if (index === array.length - 1) {
36
+ context.libs[libName] = parentPackage[name] = globalLibraryCache.global[libName];
37
+ } else {
38
+ parentPackage = parentPackage[name] = parentPackage[name] || {};
39
+ }
40
+ });
41
+ });
42
+ },
43
+ global: {}
44
+ };
45
+
46
+ // # section Exports
47
+
48
+ module.exports.createRuntime = createRuntime;
49
+ module.exports.Runtime = Runtime;
50
+
51
+ // # Runtime class definition {
52
+
53
+ function Runtime() {
54
+ this.xsapp = {};
55
+ this.xsaccess = {};
56
+ this.xsjs = {};
57
+ this.xsjslib = {};
58
+ this.xsjobs = {};
59
+ this.xscfgd = {};
60
+ this.xshttpdest = {};
61
+ this.xsprivileges = {};
62
+ this.xsodata = [];
63
+ this._rejectedFiles = {};
64
+ this.rewriteRules = [];
65
+ this.redirectRules = [];
66
+ this.staticDirectories = [];
67
+ this.settings = {};
68
+ this._sandBoxHook = null;
69
+ }
70
+
71
+ Runtime.prototype.get = function (key) {
72
+ if (typeof key === 'undefined') {
73
+ return this.settings;
74
+ }
75
+ return this.settings[key];
76
+ };
77
+
78
+ Runtime.prototype.set = function (key, value) {
79
+ if (!value && key && typeof key === 'object') {
80
+ _.extend(this.settings, key);
81
+ } else {
82
+ this.settings[key] = value;
83
+ }
84
+ };
85
+
86
+ /*
87
+ Runtime.prototype.getScript = function (pathname) {
88
+ return this.xsjs[pathname];
89
+ };
90
+ */
91
+
92
+ Runtime.prototype.getXsjsModule = function (pathname) {
93
+ return this.xsjs[pathname];
94
+ };
95
+
96
+
97
+ Runtime.prototype.getLibraryModule = function (libId) {
98
+ return this.xsjslib[libId];
99
+ };
100
+
101
+ /**
102
+ * Executes XSJS script by getting it by <code>pathname</code> from the list of prepared
103
+ * scripts collected in <code>this.xsjs</code>
104
+ *
105
+ * @param pathname XSJS path - something like /my/script/location/file.xsjs
106
+ * @param xsruntime context that will be attached to the global context under '$' property
107
+ * @returns result context after execution
108
+ */
109
+ Runtime.prototype.runXsjs = async function (pathname, xsruntime) {
110
+ var ecmaModule = this.getXsjsModule(pathname);
111
+
112
+ assert(ecmaModule, util.format('missing module for XSJS on path "%s"', pathname));
113
+ return await this._runModule(ecmaModule, xsruntime);
114
+ };
115
+
116
+ /**
117
+ * Executes XSJSLIB script by getting it by <code>pathname</code> from the list of prepared
118
+ * library scripts collected in <code>this.xsjslib</code>
119
+ *
120
+ * @param libId XSJSLIB package and name according to XS1 - something like my.script.location.file.xsjslib
121
+ * @param xsruntime context that will be attached to the global context under '$' property
122
+ * @returns result context after execution
123
+ */
124
+ Runtime.prototype.runXsjslib = async function (libId, xsruntime) {
125
+ var ecmaModule = this.getLibraryModule(libId);
126
+ assert(ecmaModule, util.format('missing module for XSJSLIB with packagename "%s"', libId));
127
+ return await this._runModule(ecmaModule, xsruntime);
128
+ };
129
+
130
+ Runtime.prototype._runModule = async function (ecmaModule, xsruntime) {
131
+ var context = this.createSandbox(xsruntime);
132
+ var module = await ecmaModule.getModule(context);
133
+ await module.evaluate();
134
+ return module;
135
+ };
136
+
137
+ /**
138
+ * This method is provided for internal runtime stakeholders like XSUnit and provides
139
+ * possibility for consumers to enrich the sandbox with additional API provided in the global
140
+ * context.
141
+ *
142
+ * How it works: while executing some of run* methods like <code>runXsjslib</code>, <code>createSandbox</code> is called
143
+ * which prepares the base sandbox, calls the hook (if provided) and expects the hook to return valid sandbox.
144
+ * Finally script is executed using returned sandbox.
145
+ *
146
+ * @param hook function that will be called while sandbox is prepared
147
+ * @throws TypeError in case provided <code>hook</code> is not a function
148
+ */
149
+ Runtime.prototype.setSandboxHook = function (hook) {
150
+ assert(hook, 'valid hook function expected');
151
+ if (!_.isFunction(hook)) {
152
+ throw new TypeError('valid sandbox hook expected');
153
+ }
154
+ this._sandBoxHook = hook;
155
+ };
156
+
157
+ Runtime.prototype.createSandbox = function (xsruntime) {
158
+ var sbox = sandbox.create(xsruntime, this.get('context'));
159
+ if (this._sandBoxHook) {
160
+ sbox = this._sandBoxHook(sbox);
161
+ assert(sbox, 'valid sandbox should be returned by the sandbox hook');
162
+ }
163
+
164
+ return sbox;
165
+ };
166
+
167
+ Runtime.prototype.importLibrary = async function (packagename, objectname, xsruntime) {
168
+ var libId;
169
+ if (objectname) {
170
+ libId = packagename + '.' + objectname;
171
+ } else if (packagename) {
172
+ if (path.extname(packagename) !== '.xsjslib') {
173
+ throw new VError('Library "%s" does not have ".xsjslib" extention', packagename);
174
+ }
175
+ var libPath;
176
+ if (packagename[0] === '/') {
177
+ libPath = packagename;
178
+ } else {
179
+ var callingXsjs = utils.getCallingXsjsRelativePath(this.get('rootDirs'));
180
+ var dir = path.dirname(callingXsjs);
181
+ libPath = path.normalize('/' + dir + '/' + packagename);
182
+ }
183
+ libId = utils.toResourceId(libPath);
184
+ } else {
185
+ throw new VError('Import library failed. No name specified.');
186
+ }
187
+
188
+ // check library cache
189
+ if (xsruntime.libs[libId]) {
190
+ return xsruntime.libs[libId];
191
+ }
192
+
193
+ var ecmaModule = this.getLibraryModule(libId);
194
+ if (!ecmaModule) {
195
+ var xsjslib = suppLibs(this)[libId];
196
+ if (xsjslib) {
197
+ return xsjslib;
198
+ }
199
+ throw new VError('Library "%s" not found', libId);
200
+ }
201
+
202
+ // create package structure on sandbox
203
+ var sandbox = this.createSandbox(xsruntime);
204
+ var library = {};
205
+
206
+ attachLibraryToDollarObject(sandbox.$, libId, library);
207
+ await loadLibrary(sandbox, ecmaModule, library);
208
+
209
+ if (this.settings.libraryCache[libId]) {
210
+ globalLibraryCache.store(libId, library, this.settings.libraryCache[libId]);
211
+ }
212
+ return library;
213
+ };
214
+
215
+ function attachLibraryToDollarObject(dollarObj, libId, library) {
216
+ // cache library
217
+ dollarObj.libs[libId] = library;
218
+
219
+ // attach library
220
+ var parent = dollarObj;
221
+ libId.split('.').forEach(function (name, idx, arr) {
222
+ if (idx + 1 === arr.length) {
223
+ parent[name] = library;
224
+ } else {
225
+ parent = parent[name] = parent[name] || {};
226
+ }
227
+ });
228
+ }
229
+
230
+ async function loadLibrary(sandbox, ecmaModule, library) {
231
+ var module = await ecmaModule.getModule(sandbox);
232
+ await module.evaluate();
233
+ _.extend(library, module.namespace.default);
234
+ }
235
+
236
+ Runtime.prototype.getDestination = function (packagename, objectname) {
237
+ return this.xshttpdest[packagename + '.' + objectname];
238
+ };
239
+
240
+ Runtime.prototype.getConfig = function (packagename, objectname) {
241
+ return this.xscfgd[packagename + '.' + objectname];
242
+ };
243
+
244
+ Runtime.prototype.encodeUser = function (user) {
245
+ var buffer = Buffer.from([user.id, user.pass].join(':'), 'utf8');
246
+ return buffer.toString('base64');
247
+ };
248
+
249
+ Runtime.prototype.decodeUser = function (encodedUser) {
250
+ var auth = Buffer.from(encodedUser, 'base64').toString('utf8').split(':');
251
+ return {
252
+ id: auth[0],
253
+ pass: auth[1]
254
+ };
255
+ };
256
+
257
+ // # Creating and attaching runtime context
258
+ Runtime.prototype.attachContext = function (req, res, traceOptions) {
259
+ var locale = res.locals.locale;
260
+ var webRequest = new xs.web.WebRequest(req, locale);
261
+ adjustRequestBody(req, webRequest);
262
+ var baseContext = this.createBaseContext(req, locale, traceOptions);
263
+ var webResponse = new xs.web.WebResponse(res, { runtime: this, context: baseContext, loggingContext: req.loggingContext, traceOptions: traceOptions, locale: locale, req: req });
264
+
265
+ req.$ = _.extend(baseContext, {
266
+ application: new xs.Application(locale, webResponse),
267
+ request: webRequest,
268
+ response: webResponse
269
+ });
270
+ };
271
+
272
+ function adjustRequestBody(req, webRequest) {
273
+ // only applicable for $.request
274
+ if (!req.body || !req.body.length) {
275
+ webRequest.body = undefined;
276
+ }
277
+ }
278
+
279
+ Runtime.prototype.createBaseContext = function (req, locale, traceOptions) {
280
+ traceOptions.req = req;
281
+ var self = this;
282
+ var sapPassport = req.headers[SAPPassport.HEADER_NAME];
283
+ var traceObject = xs.trace.createTrace(traceOptions);
284
+ var context = {
285
+ import: async function (packagename, objectname) {
286
+ return await self.importLibrary(packagename, objectname, this);
287
+ },
288
+ require: function (packagename) {
289
+ return xs.require(packagename, self.get('rootDirs'));
290
+ },
291
+ trace: traceObject,
292
+ config: {
293
+ getObject: function (packagename, objectname) {
294
+ return self.getConfig(packagename, objectname);
295
+ }
296
+ },
297
+ net: {
298
+ http: new xs.net.HTTP(self.xshttpdest, destProvider.defineDestinationProviderFunction(self), sapPassport)
299
+ },
300
+ web: {
301
+ WebRequest: xs.web.WebRequest,
302
+ TupelList: xs.web.TupelList,
303
+ Body: xs.web.WebBody
304
+ },
305
+ libs: {}, // library cache
306
+ security: xs.security,
307
+ session: new xs.Session(req, locale),
308
+ util: xs.util
309
+ };
310
+ var mailOptions = this.get('mail');
311
+ if (mailOptions) {
312
+ context.net.Mail = xs.net.createMail(mailOptions);
313
+ context.net.SMTPConnection = xs.net.createSMTPConnection(mailOptions);
314
+ }
315
+
316
+ var hanaDbOptions = this.get('hanaDbOptions');
317
+ if (hanaDbOptions) {
318
+ var dbReqOptions = hanaDbOptions.forRequest(req, locale);
319
+ context.db = new xs.db.DB(dbReqOptions);
320
+ context.hdb = new xs.db.HDB(dbReqOptions, traceObject._tracer);
321
+ context.text = xs.text.create(context.db);
322
+ }
323
+
324
+ var secStoreDbOptions = this.get('secureStoreDbOptions');
325
+ if (secStoreDbOptions) {
326
+ var ssDbOptions = secStoreDbOptions.forRequest(req, locale);
327
+ context.security.Store = Store.createStore(ssDbOptions);
328
+ }
329
+
330
+ if (this.get('jobs')) {
331
+ context.jobs = new xs.Jobs(self);
332
+ }
333
+
334
+ globalLibraryCache.restore(context);
335
+
336
+ return context;
337
+ };
338
+
339
+ Runtime.prototype.cleanupContext = function (jsContext) {
340
+
341
+ if (jsContext.db) {
342
+ jsContext.db._closeAllConnections();
343
+ }
344
+ if (jsContext.hdb) {
345
+ jsContext.hdb._closeAllConnections();
346
+ }
347
+ if (jsContext.security.Store) {
348
+ jsContext.security.Store._closeAllConnections();
349
+ }
350
+ };
351
+
352
+ Runtime.prototype.triggerGc = function () {
353
+ if (global.gc) {
354
+ global.gc();
355
+ }
356
+ };
357
+
358
+ // # Jobs handling
359
+
360
+ Runtime.prototype.getJob = function (pathname) {
361
+ return this.xsjobs[pathname];
362
+ };
363
+
364
+ Runtime.prototype.getJobName = function (jobPath) {
365
+ var appConfig = this.get('appConfig');
366
+ return jobs.Job.buildName(appConfig.host, jobPath);
367
+ };
368
+ // } Runtime class definition
369
+
370
+ function normalizeFormData(formData) {
371
+ var result = _.defaults({}, formData, {
372
+ maxFilesSizeInBytes: 10485760 // default: 10MB
373
+ });
374
+ assert(typeof result.maxFilesSizeInBytes === 'number' && result.maxFilesSizeInBytes > 0);
375
+ return result;
376
+ }
377
+
378
+ function getSecStoreDbOptions(options, xsApplicationUser) {
379
+ if (options) {
380
+ delete options.connectWithLoggedUser;
381
+ delete options.sqlcc;
382
+ options.plan = 'securestore';
383
+ return new DbOptions(options, xsApplicationUser);
384
+ }
385
+ }
386
+
387
+ async function* getAllFiles(dir) {
388
+ const dirents = await fsp.readdir(dir, { withFileTypes: true });
389
+ for (const dirent of dirents) {
390
+ const res = path.resolve(dir, dirent.name);
391
+ if (dirent.isDirectory()) {
392
+ yield* getAllFiles(res);
393
+ } else {
394
+ yield res;
395
+ }
396
+ }
397
+ }
398
+
399
+ async function createRuntime(options) {
400
+ var rt = new Runtime();
401
+
402
+ options = options || {};
403
+ var appConfig = new AppConfig(options.port);
404
+
405
+ rt.set('hanaDbOptions', options.hana && new DbOptions(options.hana, options.xsApplicationUser));
406
+ rt.set('jobs', options.jobs);
407
+ rt.set('appConfig', appConfig);
408
+ rt.set('compression', options.compression === undefined || !!options.compression);
409
+ rt.set('libraryCache', options.libraryCache || {});
410
+ rt.set('mail', options.mail || {});
411
+ rt.set('formData', normalizeFormData(options.formData));
412
+ rt.set('destinationProvider', options.destinationProvider);
413
+ rt.set('secureStoreDbOptions', getSecStoreDbOptions(options.secureStore, options.xsApplicationUser));
414
+ rt.set('context', options.context);
415
+
416
+ if (options.redirectUrl) {
417
+ rt.redirectRules.push({
418
+ pathname: '/',
419
+ location: options.redirectUrl
420
+ });
421
+ }
422
+
423
+ var directories = options.rootDirs || [];
424
+ directories = directories.filter(function (dirPath) {
425
+ return !!dirPath;
426
+ }).map(function (unresolvedPath) {
427
+ return path.resolve(unresolvedPath);
428
+ });
429
+ if (!directories.length) {
430
+ directories.push(path.resolve(options.rootDir || 'lib'));
431
+ }
432
+ rt.set('rootDirs', directories);
433
+
434
+ for (var directory of directories) {
435
+ for await (const file of getAllFiles(directory)) {
436
+ var rel = path.relative(directory, file).replaceAll('\\', '/');
437
+ var pathname = '/' + rel;
438
+ var basename = path.basename(file);
439
+ if (basename === '.xsapp') {
440
+ xsapp.call(rt, pathname, file);
441
+ } else if (basename === '.xsaccess') {
442
+ xsaccess.call(rt, pathname, file);
443
+ } else if (basename === '.xsprivileges') {
444
+ xsprivileges.call(rt, pathname, file);
445
+ } else if (/\.xsjs$/.test(basename)) {
446
+ await xsjs.call(rt, pathname, file);
447
+ } else if (/\.xsjslib$/.test(basename)) {
448
+ await xsjslib.call(rt, pathname, file);
449
+ } else if (/\.xsodata$/.test(basename)) {
450
+ xsodata.call(rt, pathname, file);
451
+ } else if (/\.xscfgd$/.test(basename)) {
452
+ xscfgd.call(rt, pathname, file);
453
+ } else if (/\.xshttpdest$/.test(basename)) {
454
+ xshttpdest.call(rt, pathname, file);
455
+ } else if (/\.xsjob$/.test(basename)) {
456
+ xsjob.call(rt, pathname, file);
457
+ }
458
+ }
459
+ }
460
+
461
+ // all files have been processed, check jobs can be executed
462
+ validateJobs.call(rt);
463
+
464
+ if (!_.isEmpty(rt._rejectedFiles)) {
465
+ _.forEach(rt._rejectedFiles, function (err, filename) {
466
+ logger.error(err, 'File "%s" rejected', filename);
467
+ });
468
+ throw new Error('Some of the files in the working dir(s) were rejected. Check the logs.');
469
+ }
470
+
471
+ return rt;
472
+ }
473
+
474
+ function readFile(filename) {
475
+ return fs.readFileSync(filename, 'utf8');
476
+ }
477
+
478
+ function readJson(filename) {
479
+ try {
480
+ return JSON.parse(readFile(filename));
481
+ } catch (err) {
482
+ this._rejectedFiles[filename] = err;
483
+ }
484
+ }
485
+
486
+ function readJsonThatCanBeEmpty(filename) {
487
+ try {
488
+ var content = readFile(filename);
489
+ if (_.isEmpty(_.trim(content))) {
490
+ return;
491
+ }
492
+ return JSON.parse(content);
493
+ } catch (err) {
494
+ this._rejectedFiles[filename] = err;
495
+ }
496
+ }
497
+
498
+ function readXScfgd(filename) {
499
+ var sConfig = readFile(filename);
500
+ var lines = sConfig.split(/;\s*[\r\n]/);
501
+
502
+ if (/implements \S+/.test(lines[0])) {
503
+ lines.shift();
504
+ }
505
+
506
+ var sandbox = {};
507
+ var it;
508
+
509
+ for (var lnn in lines) {
510
+ var line = lines[lnn];
511
+ line = line.trim();
512
+ if (line === '') {
513
+ continue;
514
+ }
515
+ if (line[0] === '/' && line[1] === '/') {
516
+ continue;
517
+ }
518
+
519
+ var aProp = line.split('=');
520
+ if (aProp.length > 1) {
521
+ var key = aProp[0].trim();
522
+ var akey = key.split('.');
523
+ akey.pop();
524
+
525
+ var o = sandbox;
526
+ for (it = 0; it < akey.length; ++it) {
527
+ o = o[akey[it]] = o[akey[it]] || {};
528
+ }
529
+ }
530
+ }
531
+
532
+ var newScript = lines.join(';\r\n');
533
+ try {
534
+ var script = new vm.Script(newScript);
535
+ script.runInNewContext(sandbox);
536
+
537
+ return sandbox;
538
+ } catch (err) {
539
+ this._rejectedFiles[filename] = err;
540
+ }
541
+ }
542
+
543
+ function readHTTPDestination(filename) {
544
+ var sandbox = {
545
+ none: null,
546
+ basic: 'basic',
547
+ AssertionTicket: 'AssertionTicket'
548
+ };
549
+
550
+ var properties = Object.getOwnPropertyNames(sandbox);
551
+ try {
552
+ var script = new vm.Script(readFile(filename));
553
+ script.runInNewContext(sandbox);
554
+
555
+ properties.forEach(function (property) {
556
+ delete sandbox[property];
557
+ });
558
+
559
+ return sandbox;
560
+ } catch (err) {
561
+ this._rejectedFiles[filename] = err;
562
+ }
563
+ }
564
+
565
+ function xsapp(pathname, filename) {
566
+ pathname = path.dirname(pathname);
567
+ if (this.xsapp[pathname]) {
568
+ return warnIgnoredFile(pathname, filename);
569
+ }
570
+ var data = readJsonThatCanBeEmpty.call(this, filename);
571
+ if (!data) {
572
+ return;
573
+ }
574
+ this.xsapp[pathname] = data;
575
+ }
576
+
577
+ function xsaccess(pathname, filename) {
578
+ pathname = path.dirname(pathname);
579
+ if (this.xsaccess[pathname]) {
580
+ return warnIgnoredFile(pathname, filename);
581
+ }
582
+ var data = readJsonThatCanBeEmpty.call(this, filename);
583
+ if (!data) {
584
+ return;
585
+ }
586
+ this.xsaccess[pathname] = data;
587
+ var dirname = path.dirname(filename);
588
+ if (data.exposed) {
589
+ logger.info('Adding static handler for "%s" in "%s"', pathname,
590
+ dirname);
591
+ this.staticDirectories.push({
592
+ pathname: pathname,
593
+ dirname: dirname
594
+ });
595
+ }
596
+ var rewriteRules = data.rewrite_rules;
597
+ if (rewriteRules) {
598
+ this.rewriteRules[pathname] = [];
599
+ rewriteRules.forEach(function (rule) {
600
+ logger.info('Adding rewrite rule from "%s" to "%s"', rule.source, rule.target);
601
+ this.rewriteRules[pathname].push({
602
+ regex: new RegExp(rule.source + '$'),
603
+ replacement: rule.target
604
+ });
605
+ }, this);
606
+ }
607
+ if (data.authentication) {
608
+ logger.warn('Authentication not yet supported');
609
+ }
610
+ }
611
+
612
+ function xsprivileges(pathname, filename) {
613
+ pathname = path.dirname(pathname);
614
+ if (this.xsprivileges[pathname]) {
615
+ return warnIgnoredFile(pathname, filename);
616
+ }
617
+ var data = readJson.call(this, filename);
618
+ if (!data) {
619
+ return;
620
+ }
621
+ this.xsprivileges[pathname] = data;
622
+ }
623
+
624
+ function xsodata(pathname, filename) {
625
+ if (this.xsodata[pathname]) {
626
+ return warnIgnoredFile(pathname, filename);
627
+ }
628
+ this.xsodata[pathname] = filename;
629
+ }
630
+
631
+ async function xsjs(pathname, filename) {
632
+ if (this.xsjs[pathname]) {
633
+ return warnIgnoredFile(pathname, filename);
634
+ }
635
+ let ecmaModule = new EcmaModule(filename);
636
+ try {
637
+ await ecmaModule.getModule(vm.createContext({'$': {}}));
638
+ } catch (err) {
639
+ this._rejectedFiles[filename] = err;
640
+ }
641
+ this.xsjs[pathname] = ecmaModule; // readScript.call(this, filename);
642
+ }
643
+
644
+ async function xsjslib(pathname, filename) {
645
+ if (this.xsjslib[utils.toResourceId(pathname)]) {
646
+ return warnIgnoredFile(pathname, filename);
647
+ }
648
+ let ecmaModule = new EcmaModule(filename);
649
+ try {
650
+ await ecmaModule.getModule(vm.createContext({'$': {}}));
651
+ } catch (err) {
652
+ this._rejectedFiles[filename] = err;
653
+ }
654
+ this.xsjslib[utils.toResourceId(pathname)] = ecmaModule; // readScript.call(this, filename);
655
+ }
656
+
657
+ function xscfgd(pathname, filename) {
658
+ if (this.xscfgd[utils.toResourceId(pathname)]) {
659
+ return warnIgnoredFile(pathname, filename);
660
+ }
661
+ this.xscfgd[utils.toResourceId(pathname)] = readXScfgd.call(this, filename);
662
+ }
663
+
664
+ function xshttpdest(pathname, filename) {
665
+ if (this.xshttpdest[utils.toResourceId(pathname)]) {
666
+ return warnIgnoredFile(pathname, filename);
667
+ }
668
+ this.xshttpdest[utils.toResourceId(pathname)] = readHTTPDestination.call(this, filename);
669
+ }
670
+
671
+ function xsjob(pathname, filename) {
672
+ if (this.xsjobs[pathname]) {
673
+ return warnIgnoredFile(pathname, filename);
674
+ }
675
+ try {
676
+ var data = readJson.call(this, filename);
677
+ if (!data) {
678
+ return;
679
+ }
680
+ this.xsjobs[pathname] = new jobs.Job(data, pathname, this.get('rootDirs'));
681
+ } catch (err) {
682
+ this._rejectedFiles[filename] = err;
683
+ }
684
+ }
685
+
686
+ function warnIgnoredFile(pathname, filename) {
687
+ logger.warn(util.format('Ignoring %s - file with the same path (%s) is present in an already loaded rootDir', filename, pathname));
688
+ }
689
+
690
+ function validateJobs() {
691
+ var self = this;
692
+ Object.keys(self.xsjobs).forEach(function (key) {
693
+ var job = self.xsjobs[key];
694
+ job.active = true;
695
+ if (!job.action.isJavaScript()) {
696
+ return;
697
+ }
698
+ var scriptPath = job.action.getScriptPath();
699
+ if (!self.getXsjsModule(scriptPath)) {
700
+ job.active = false;
701
+ var message = util.format('XSJS file "%s" required by job "%s" was not found', scriptPath, job.urlPath);
702
+ self._rejectedFiles[key] = new Error(message);
703
+ }
704
+ });
705
+ }
706
+
707
+
708
+ class EcmaModule {
709
+ constructor(fileName) {
710
+ this.fileName = fileName;
711
+ this.moduleSource = null;
712
+ this.cachedData = undefined;
713
+ }
714
+
715
+ async getModule(context) {
716
+ if (!this.moduleSource) {
717
+ let xsjsfh;
718
+ try {
719
+ xsjsfh = await fsp.open(this.fileName);
720
+ this.moduleSource = await xsjsfh.readFile({encoding: 'utf-8'});
721
+ } finally {
722
+ if (xsjsfh) {
723
+ await xsjsfh.close();
724
+ }
725
+ }
726
+
727
+ }
728
+ // var moduleContext = vm.createContext(context);
729
+ var module = new vm.SourceTextModule(this.moduleSource, {
730
+ identifier: this.fileName,
731
+ context: context,
732
+ cachedData: this.cachedData
733
+ });
734
+ if (!this.cachedData) {
735
+ this.cachedData = module.createCachedData();
736
+ }
737
+ await module.link(() => {}); // TODO - check if we need linker
738
+ return module;
739
+ }
740
+ }