web_plsql 1.2.0 → 1.3.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 (119) hide show
  1. package/README.md +103 -8
  2. package/dist/chunk-DQk6qfdC.mjs +18 -0
  3. package/dist/frontend/assets/main-C8GKnEhz.js +147 -0
  4. package/dist/frontend/assets/main-C8GKnEhz.js.br +0 -0
  5. package/dist/frontend/assets/main-C8GKnEhz.js.gz +0 -0
  6. package/dist/frontend/assets/main-bU4x_yEW.css +1 -0
  7. package/dist/frontend/assets/main-bU4x_yEW.css.br +0 -0
  8. package/dist/frontend/assets/main-bU4x_yEW.css.gz +0 -0
  9. package/{src/admin → dist/frontend}/index.html +12 -25
  10. package/dist/frontend/index.html.br +0 -0
  11. package/dist/frontend/index.html.gz +0 -0
  12. package/dist/index.d.mts +646 -0
  13. package/dist/index.mjs +3559 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/dist/oracledb-mock-Dn8aHtd7.mjs +68 -0
  16. package/dist/oracledb-mock-Dn8aHtd7.mjs.map +1 -0
  17. package/examples/config-apex.js +1 -1
  18. package/examples/config-docker.js +1 -2
  19. package/package.json +48 -32
  20. package/examples/config-native.js +0 -68
  21. package/src/admin/client/charts.ts +0 -598
  22. package/src/admin/client/main.js +0 -3
  23. package/src/admin/client/tailwind.css +0 -41
  24. package/src/admin/js/api.ts +0 -170
  25. package/src/admin/js/app.ts +0 -614
  26. package/src/admin/js/eslint.config.js +0 -74
  27. package/src/admin/js/schemas.ts +0 -215
  28. package/src/admin/js/templates/config.ts +0 -147
  29. package/src/admin/js/templates/errorRow.ts +0 -21
  30. package/src/admin/js/templates/index.ts +0 -4
  31. package/src/admin/js/templates/poolCard.ts +0 -61
  32. package/src/admin/js/templates/traceRow.ts +0 -24
  33. package/src/admin/js/tsconfig.json +0 -24
  34. package/src/admin/js/types.ts +0 -336
  35. package/src/admin/js/ui/components.ts +0 -44
  36. package/src/admin/js/ui/table.ts +0 -173
  37. package/src/admin/js/ui/theme.ts +0 -93
  38. package/src/admin/js/ui/views.ts +0 -427
  39. package/src/admin/js/util/format.ts +0 -46
  40. package/src/admin/js/util/metrics.ts +0 -24
  41. package/src/admin/lib/chart.bundle.css +0 -1
  42. package/src/admin/lib/chart.bundle.js +0 -146
  43. package/src/admin/style.css +0 -1437
  44. package/src/bin/load-test.js +0 -202
  45. package/src/handler/handlerAdmin.js +0 -293
  46. package/src/handler/handlerLogger.js +0 -21
  47. package/src/handler/handlerUpload.js +0 -26
  48. package/src/handler/plsql/cgi.js +0 -131
  49. package/src/handler/plsql/errorPage.js +0 -113
  50. package/src/handler/plsql/handlerPlSql.js +0 -90
  51. package/src/handler/plsql/owaPageStream.js +0 -93
  52. package/src/handler/plsql/parsePage.js +0 -213
  53. package/src/handler/plsql/procedure.js +0 -426
  54. package/src/handler/plsql/procedureError.js +0 -40
  55. package/src/handler/plsql/procedureNamed.js +0 -282
  56. package/src/handler/plsql/procedureSanitize.js +0 -286
  57. package/src/handler/plsql/procedureVariable.js +0 -56
  58. package/src/handler/plsql/request.js +0 -124
  59. package/src/handler/plsql/requestError.js +0 -23
  60. package/src/handler/plsql/sendResponse.js +0 -174
  61. package/src/handler/plsql/stream.js +0 -28
  62. package/src/handler/plsql/upload.js +0 -109
  63. package/src/index.js +0 -19
  64. package/src/server/adminContext.js +0 -23
  65. package/src/server/config.js +0 -57
  66. package/src/server/connections.js +0 -40
  67. package/src/server/server.js +0 -304
  68. package/src/types.js +0 -150
  69. package/src/util/cache.js +0 -123
  70. package/src/util/errorToString.js +0 -24
  71. package/src/util/file.js +0 -91
  72. package/src/util/html.js +0 -53
  73. package/src/util/jsonLogger.js +0 -47
  74. package/src/util/oracle.js +0 -80
  75. package/src/util/shutdown.js +0 -44
  76. package/src/util/statsManager.js +0 -368
  77. package/src/util/trace.js +0 -326
  78. package/src/util/traceManager.js +0 -68
  79. package/src/util/type.js +0 -6
  80. package/src/util/util.js +0 -66
  81. package/src/version.js +0 -5
  82. package/types/admin/js/schemas.d.ts +0 -384
  83. package/types/admin/js/types.d.ts +0 -312
  84. package/types/handler/handlerAdmin.d.ts +0 -79
  85. package/types/handler/handlerLogger.d.ts +0 -2
  86. package/types/handler/handlerUpload.d.ts +0 -7
  87. package/types/handler/plsql/cgi.d.ts +0 -4
  88. package/types/handler/plsql/errorPage.d.ts +0 -11
  89. package/types/handler/plsql/handlerPlSql.d.ts +0 -13
  90. package/types/handler/plsql/owaPageStream.d.ts +0 -28
  91. package/types/handler/plsql/parsePage.d.ts +0 -3
  92. package/types/handler/plsql/procedure.d.ts +0 -13
  93. package/types/handler/plsql/procedureError.d.ts +0 -23
  94. package/types/handler/plsql/procedureNamed.d.ts +0 -14
  95. package/types/handler/plsql/procedureSanitize.d.ts +0 -11
  96. package/types/handler/plsql/procedureVariable.d.ts +0 -10
  97. package/types/handler/plsql/request.d.ts +0 -9
  98. package/types/handler/plsql/requestError.d.ts +0 -8
  99. package/types/handler/plsql/sendResponse.d.ts +0 -6
  100. package/types/handler/plsql/upload.d.ts +0 -5
  101. package/types/index.d.ts +0 -10
  102. package/types/server/adminContext.d.ts +0 -17
  103. package/types/server/config.d.ts +0 -2
  104. package/types/server/server.d.ts +0 -57
  105. package/types/types.d.ts +0 -328
  106. package/types/util/cache.d.ts +0 -69
  107. package/types/util/errorToString.d.ts +0 -1
  108. package/types/util/file.d.ts +0 -6
  109. package/types/util/html.d.ts +0 -3
  110. package/types/util/jsonLogger.d.ts +0 -45
  111. package/types/util/oracle.d.ts +0 -5
  112. package/types/util/shutdown.d.ts +0 -2
  113. package/types/util/statsManager.d.ts +0 -395
  114. package/types/util/trace.d.ts +0 -22
  115. package/types/util/traceManager.d.ts +0 -35
  116. package/types/util/type.d.ts +0 -1
  117. package/types/util/util.d.ts +0 -3
  118. package/types/version.d.ts +0 -1
  119. /package/{src/admin/favicon.svg → dist/frontend/assets/favicon-mQAM4tVu.svg} +0 -0
@@ -1,113 +0,0 @@
1
- /*
2
- * Error handling
3
- */
4
-
5
- import {ProcedureError} from './procedureError.js';
6
- import {RequestError} from './requestError.js';
7
- import {getFormattedMessage, logToFile} from '../../util/trace.js';
8
- import {errorToString} from '../../util/errorToString.js';
9
- import {getHtmlPage} from '../../util/html.js';
10
- import {jsonLogger} from '../../util/jsonLogger.js';
11
-
12
- /**
13
- * @typedef {import('express').Request} Request
14
- * @typedef {import('express').Response} Response
15
- * @typedef {import('../../types.js').BindParameterConfig} BindParameterConfig
16
- * @typedef {import('../../types.js').environmentType} environmentType
17
- * @typedef {import('../../types.js').configPlSqlHandlerType} configPlSqlHandlerType
18
- * @typedef {{html: string; text: string}} outputType
19
- * @typedef {import('../../util/trace.js').messageType} messageType
20
- */
21
-
22
- /**
23
- * Get error data
24
- * @param {Request} req - The req object represents the HTTP request.
25
- * @param {unknown} error - The error.
26
- * @returns {messageType} - The output.
27
- */
28
- const getErrorData = (req, error) => {
29
- let timestamp = new Date();
30
- let message = '';
31
- /** @type {environmentType | null} */
32
- let environment = null;
33
- /** @type {string | null} */
34
- let sql = null;
35
- /** @type {BindParameterConfig | null} */
36
- let bind = null;
37
-
38
- // what type of Error did we receive
39
- if (error instanceof ProcedureError) {
40
- timestamp = error.timestamp;
41
- message = error.stack ?? '';
42
- environment = error.environment;
43
- sql = error.sql;
44
- bind = error.bind;
45
- } else if (error instanceof RequestError) {
46
- timestamp = error.timestamp;
47
- message = error.stack ?? '';
48
- } else if (error instanceof Error) {
49
- message = errorToString(error);
50
- } else {
51
- if (typeof error === 'string') {
52
- message = `${error}\n`;
53
- }
54
- /* v8 ignore start - unreachable code: creating Error without throwing */
55
- try {
56
- new Error();
57
- } catch (err) {
58
- message += errorToString(err);
59
- }
60
- /* v8 ignore stop */
61
- }
62
-
63
- return {type: 'error', timestamp, message, req, environment, sql, bind};
64
- };
65
-
66
- /**
67
- * Show an error page
68
- *
69
- * @param {Request} req - The req object represents the HTTP request.
70
- * @param {Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
71
- * @param {configPlSqlHandlerType} options - The configuration options.
72
- * @param {unknown} error - The error.
73
- */
74
- export const errorPage = (req, res, options, error) => {
75
- // get error data
76
- const errorData = getErrorData(req, error);
77
-
78
- // get formatted message
79
- const {html, text} = getFormattedMessage(errorData);
80
-
81
- // trace to file
82
- logToFile(text);
83
-
84
- // json log
85
- const firstLine = errorData.message.split('\n')[0];
86
- jsonLogger.log({
87
- timestamp: errorData.timestamp?.toISOString() ?? new Date().toISOString(),
88
- type: 'error',
89
- message: firstLine ?? '',
90
- req: {
91
- method: req.method,
92
- url: req.originalUrl,
93
- ip: req.ip ?? '',
94
- userAgent: req.get('user-agent') ?? '',
95
- },
96
- details: {
97
- fullMessage: errorData.message,
98
- sql: errorData.sql,
99
- bind: errorData.bind,
100
- environment: errorData.environment,
101
- },
102
- });
103
-
104
- // console
105
- console.error(text);
106
-
107
- // show page
108
- if (options.errorStyle === 'basic') {
109
- res.status(404).send('Page not found');
110
- } else {
111
- res.status(404).send(getHtmlPage(html));
112
- }
113
- };
@@ -1,90 +0,0 @@
1
- /*
2
- * Express middleware for Oracle PL/SQL
3
- */
4
-
5
- import debugModule from 'debug';
6
- const debug = debugModule('webplsql:handlerPlSql');
7
-
8
- import url from 'node:url';
9
- import {processRequest} from './request.js';
10
- import {RequestError} from './requestError.js';
11
- import {errorPage} from './errorPage.js';
12
- import {Cache} from '../../util/cache.js';
13
-
14
- /**
15
- * @typedef {import('express').RequestHandler} RequestHandler
16
- * @typedef {import('express').Request} Request
17
- * @typedef {import('express').Response} Response
18
- * @typedef {import('express').NextFunction} NextFunction
19
- * @typedef {import('oracledb').Pool} Pool
20
- * @typedef {import('../../types.js').environmentType} environmentType
21
- * @typedef {import('../../types.js').configPlSqlHandlerType} configPlSqlHandlerType
22
- */
23
-
24
- /**
25
- * express.Request handler
26
- * @param {Request} req - The req object represents the HTTP request.
27
- * @param {Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
28
- * @param {NextFunction} _next - The next function.
29
- * @param {Pool} connectionPool - The connection pool.
30
- * @param {configPlSqlHandlerType} options - the options for the middleware.
31
- * @param {Cache<string>} procedureNameCache - The procedure name cache.
32
- * @param {Cache<import('./procedureNamed.js').argsType>} argumentCache - The argument cache.
33
- */
34
- const requestHandler = async (req, res, _next, connectionPool, options, procedureNameCache, argumentCache) => {
35
- try {
36
- // should we switch to the default page if there is one defined
37
- if (typeof req.params.name !== 'string' || req.params.name.length === 0) {
38
- if (typeof options.defaultPage === 'string' && options.defaultPage.length > 0) {
39
- const newUrl = url.resolve(`${req.originalUrl}/${options.defaultPage}`, '');
40
- debug(`Redirect to the url "${newUrl}"`);
41
- res.redirect(newUrl);
42
- } else {
43
- errorPage(req, res, options, new RequestError('No procedure name given and no default page has been specified'));
44
- }
45
- } else {
46
- // request handler
47
- await processRequest(req, res, options, connectionPool, procedureNameCache, argumentCache);
48
- }
49
- } catch (err) {
50
- errorPage(req, res, options, err);
51
- }
52
- };
53
-
54
- /**
55
- * @typedef {import('express').RequestHandler & {
56
- * procedureNameCache: Cache<string>;
57
- * argumentCache: Cache<import('./procedureNamed.js').argsType>;
58
- * }} WebPlSqlRequestHandler
59
- */
60
-
61
- /**
62
- * Express middleware.
63
- *
64
- * @param {Pool} connectionPool - The connection pool.
65
- * @param {configPlSqlHandlerType} config - The configuration options.
66
- * @returns {WebPlSqlRequestHandler} - The handler.
67
- */
68
- export const handlerWebPlSql = (connectionPool, config) => {
69
- debug('options', config);
70
-
71
- /** @type {Cache<string>} */
72
- const procedureNameCache = new Cache();
73
- /** @type {Cache<import('./procedureNamed.js').argsType>} */
74
- const argumentCache = new Cache();
75
-
76
- /**
77
- * @param {Request} req - The request.
78
- * @param {Response} res - The response.
79
- * @param {NextFunction} next - The next function.
80
- */
81
- const handler = (req, res, next) => {
82
- void requestHandler(req, res, next, connectionPool, config, procedureNameCache, argumentCache);
83
- };
84
-
85
- // Expose caches for Admin Console
86
- handler.procedureNameCache = procedureNameCache;
87
- handler.argumentCache = argumentCache;
88
-
89
- return handler;
90
- };
@@ -1,93 +0,0 @@
1
- import {Readable} from 'node:stream';
2
- import oracledb from 'oracledb';
3
- import z from 'zod';
4
- import debugModule from 'debug';
5
- import {ProcedureError} from './procedureError.js';
6
- import {errorToString} from '../../util/errorToString.js';
7
-
8
- const debug = debugModule('webplsql:owaPageStream');
9
-
10
- /**
11
- * @typedef {import('oracledb').Connection} Connection
12
- * @typedef {import('../../types.js').BindParameterConfig} BindParameterConfig
13
- */
14
-
15
- export class OWAPageStream extends Readable {
16
- /**
17
- * @param {Connection} databaseConnection - The database connection.
18
- */
19
- constructor(databaseConnection) {
20
- super({highWaterMark: 16384}); // 16KB buffer
21
- this.databaseConnection = databaseConnection;
22
- this.chunkSize = 1000;
23
- this.isDone = false;
24
- }
25
-
26
- /**
27
- * Fetch a chunk of the page from the database.
28
- * @returns {Promise<string[]>} The array of lines fetched.
29
- */
30
- async fetchChunk() {
31
- if (this.isDone) return [];
32
-
33
- /** @type {BindParameterConfig} */
34
- const bindParameter = {
35
- lines: {dir: oracledb.BIND_OUT, type: oracledb.STRING, maxArraySize: this.chunkSize},
36
- irows: {dir: oracledb.BIND_INOUT, type: oracledb.NUMBER, val: this.chunkSize},
37
- };
38
-
39
- const sqlStatement = 'BEGIN owa.get_page(thepage=>:lines, irows=>:irows); END;';
40
-
41
- try {
42
- const result = await this.databaseConnection.execute(sqlStatement, bindParameter);
43
- const {lines, irows} = z.object({irows: z.number(), lines: z.array(z.string())}).parse(result.outBinds);
44
-
45
- debug(`fetched ${lines.length} lines (irows=${irows})`);
46
-
47
- // If we got fewer lines than requested, OR if we got 0 lines, we are done
48
- if (lines.length < this.chunkSize) {
49
- this.isDone = true;
50
- }
51
-
52
- return lines;
53
- } catch (err) {
54
- if (err instanceof ProcedureError) {
55
- throw err;
56
- }
57
- throw new ProcedureError(`OWAPageStream: error when getting page\n${errorToString(err)}`, {}, sqlStatement, bindParameter);
58
- }
59
- }
60
-
61
- /**
62
- * @override
63
- * @param {number} _size - The size hint (unused).
64
- * @returns {void}
65
- */
66
- _read(_size) {
67
- this.fetchChunk()
68
- .then((lines) => {
69
- if (lines.length > 0) {
70
- this.push(lines.join(''));
71
- }
72
-
73
- // After fetching, check if we're done
74
- if (this.isDone) {
75
- this.push(null);
76
- }
77
- })
78
- .catch((/** @type {unknown} */ err) => {
79
- this.destroy(err instanceof Error ? err : new Error(String(err)));
80
- });
81
- }
82
-
83
- /**
84
- * Add initial body content to the stream.
85
- * @param {string} content - The initial content to prepend.
86
- * @returns {void}
87
- */
88
- addBody(content) {
89
- if (content && content.length > 0) {
90
- this.push(content);
91
- }
92
- }
93
- }
@@ -1,213 +0,0 @@
1
- import debugModule from 'debug';
2
- const debug = debugModule('webplsql:parsePage');
3
-
4
- /**
5
- * @typedef {import('../../types.js').pageType} pageType
6
- * @typedef {import('../../types.js').cookieType} cookieType
7
- */
8
-
9
- /**
10
- * Parse the header and split it up into the individual components
11
- *
12
- * @param {string} text - The text returned from the PL/SQL procedure.
13
- * @returns {pageType} - The parsed page.
14
- */
15
- export const parsePage = (text) => {
16
- /** @type {pageType} */
17
- const page = {
18
- body: '',
19
- head: {
20
- cookies: [],
21
- otherHeaders: {},
22
- },
23
- file: {
24
- fileType: null,
25
- fileSize: null,
26
- fileBlob: null,
27
- },
28
- };
29
-
30
- //
31
- // 1) Split up the text in header and body
32
- //
33
-
34
- // Find the end of the header identified by \n\n
35
- let head = '';
36
- const headerEndPosition = text.indexOf('\n\n');
37
- if (headerEndPosition === -1) {
38
- head = text;
39
- } else {
40
- head = text.substring(0, headerEndPosition + 2);
41
- page.body = text.substring(headerEndPosition + 2);
42
- }
43
-
44
- //
45
- // 2) parse the headers
46
- //
47
-
48
- debug('parsing the headers received from PL/SQL');
49
- head.split('\n').forEach((line) => {
50
- const header = getHeader(line);
51
-
52
- if (header) {
53
- switch (header.name.toLowerCase()) {
54
- case 'set-cookie':
55
- {
56
- const cookie = parseCookie(header.value);
57
- debug(`oracle header "set-cookie" with value "${header.value}" was received has been parsed to ${JSON.stringify(cookie)}`);
58
- /* v8 ignore else - parseCookie validation ensures non-null */
59
- if (cookie !== null) {
60
- page.head.cookies.push(cookie);
61
- } else {
62
- throw new Error(`Unable to parse header "set-cookie" with value "${header.value}" received from PL/SQL`);
63
- }
64
- }
65
- break;
66
-
67
- case 'content-type':
68
- page.head.contentType = header.value;
69
- debug(`oracle header "content-type" with value "${page.head.contentType}" was parsed`);
70
- break;
71
-
72
- case 'x-db-content-length':
73
- {
74
- const contentLength = parseInt(header.value, 10);
75
- /* v8 ignore else - parseInt validation */
76
- if (!Number.isNaN(contentLength)) {
77
- page.head.contentLength = contentLength;
78
- debug(`oracle header "x-db-content-length" with value "${page.head.contentLength}" was parsed`);
79
- } else {
80
- throw new Error(`Unable to parse header "x-db-content-length" with value "${header.value}" received from PL/SQL`);
81
- }
82
- }
83
- break;
84
-
85
- case 'status':
86
- {
87
- const statusCode = parseInt(header.value, 10);
88
- /* v8 ignore else - parseInt validation */
89
- if (!Number.isNaN(statusCode)) {
90
- page.head.statusCode = statusCode;
91
- debug(`oracle header "status" with value "${page.head.statusCode}" was parsed`);
92
- const index = header.value.indexOf(' ');
93
- /* v8 ignore else - status code may not have description */
94
- if (index !== -1) {
95
- page.head.statusDescription = header.value.substring(index + 1);
96
- }
97
- } else {
98
- throw new Error(`Unable to parse header "status" with value "${header.value}" received from PL/SQL`);
99
- }
100
- }
101
- break;
102
-
103
- case 'location':
104
- page.head.redirectLocation = header.value;
105
- debug(`oracle header "location" with value "${page.head.statusCode}" was parsed`);
106
- break;
107
-
108
- case 'x-oracle-ignore':
109
- break;
110
-
111
- default:
112
- page.head.otherHeaders[header.name] = header.value;
113
- break;
114
- }
115
- }
116
- });
117
-
118
- return page;
119
- };
120
-
121
- /**
122
- * Get a header line
123
- * @param {string} line - The line
124
- * @returns {{name: string, value: string} | null} - The header.
125
- */
126
- const getHeader = (line) => {
127
- const index = line.indexOf(':');
128
-
129
- if (index !== -1) {
130
- return {
131
- name: line.substring(0, index).trim(),
132
- value: line.substring(index + 1).trim(),
133
- };
134
- }
135
-
136
- return null;
137
- };
138
-
139
- /**
140
- * Parses a cookie string
141
- * @param {string} text - The cookie string.
142
- * @returns {cookieType | null} - The parsed cookie.
143
- */
144
- const parseCookie = (text) => {
145
- // validate
146
- /* v8 ignore next - input validation */
147
- if (typeof text !== 'string' || text.trim().length === 0) {
148
- return null;
149
- }
150
-
151
- // split the cookie into it's parts
152
- let cookieElements = text.split(';');
153
-
154
- // trim cookie elements
155
- cookieElements = cookieElements.map((element) => element.trim());
156
-
157
- // get name and value
158
- const firstElement = cookieElements[0];
159
- if (!firstElement) {
160
- return null;
161
- }
162
- const index = firstElement.indexOf('=');
163
- /* v8 ignore next - cookie format validation */
164
- if (index <= 0) {
165
- // if the index is -1, there is no equal sign and if it's 0 the name is empty
166
- return null;
167
- }
168
-
169
- /** @type {cookieType} */
170
- const cookie = {
171
- name: firstElement.substring(0, index).trim(),
172
- value: firstElement.substring(index + 1).trim(),
173
- options: {},
174
- };
175
-
176
- // remove the first element
177
- cookieElements.shift();
178
-
179
- // get the other options
180
- cookieElements.forEach((element) => {
181
- if (element.startsWith('path=')) {
182
- cookie.options.path = element.substring(5);
183
- } else if (element.toLowerCase().startsWith('domain=')) {
184
- cookie.options.domain = element.substring(7);
185
- } else if (element.toLowerCase().startsWith('secure=')) {
186
- /* v8 ignore next - secure cookie attribute */
187
- cookie.options.secure = true;
188
- } else if (element.toLowerCase().startsWith('expires=')) {
189
- const date = tryDecodeDate(element.substring(8));
190
- if (date) {
191
- cookie.options.expires = date;
192
- }
193
- } else if (element.toLowerCase().startsWith('httponly')) {
194
- cookie.options.httpOnly = true;
195
- }
196
- });
197
-
198
- return cookie;
199
- };
200
-
201
- /**
202
- * Try to decode a date
203
- * @param {string} value - The value to decode.
204
- * @returns {Date | null} - The decoded date or null.
205
- */
206
- const tryDecodeDate = (value) => {
207
- try {
208
- return new Date(value);
209
- } catch (err) {
210
- /* v8 ignore next - invalid date format */
211
- return null;
212
- }
213
- };