oracledb 7.0.0 → 26.0.0

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 (84) hide show
  1. package/README.md +16 -6
  2. package/build/Release/oracledb-26.0.0-darwin-arm64.node +0 -0
  3. package/build/Release/oracledb-26.0.0-darwin-arm64.node-buildinfo.txt +1 -0
  4. package/build/Release/oracledb-26.0.0-js-buildinfo.txt +1 -0
  5. package/build/Release/oracledb-26.0.0-linux-arm64.node +0 -0
  6. package/build/Release/oracledb-26.0.0-linux-arm64.node-buildinfo.txt +1 -0
  7. package/build/Release/oracledb-26.0.0-linux-x64.node +0 -0
  8. package/build/Release/oracledb-26.0.0-linux-x64.node-buildinfo.txt +1 -0
  9. package/build/Release/oracledb-26.0.0-win32-x64.node +0 -0
  10. package/build/Release/oracledb-26.0.0-win32-x64.node-buildinfo.txt +1 -0
  11. package/lib/aqDeqOptions.js +4 -4
  12. package/lib/aqEnqOptions.js +2 -2
  13. package/lib/aqQueue.js +11 -6
  14. package/lib/connection.js +73 -18
  15. package/lib/constants.js +4 -0
  16. package/lib/endUserSecurityContext.js +30 -25
  17. package/lib/errors.js +92 -5
  18. package/lib/impl/connection.js +36 -0
  19. package/lib/impl/datahandlers/buffer.js +91 -18
  20. package/lib/impl/datahandlers/oson.js +3 -2
  21. package/lib/impl/parserHelpers.js +6 -2
  22. package/lib/impl/resultset.js +32 -11
  23. package/lib/oracledb.js +34 -0
  24. package/lib/pool.js +6 -2
  25. package/lib/queryStream.js +13 -5
  26. package/lib/resultset.js +10 -3
  27. package/lib/securityContextProvider.js +206 -0
  28. package/lib/thin/aq.js +2 -3
  29. package/lib/thin/connection.js +380 -24
  30. package/lib/thin/dbObject.js +20 -11
  31. package/lib/thin/pool.js +4 -3
  32. package/lib/thin/protocol/capabilities.js +37 -4
  33. package/lib/thin/protocol/constants.js +49 -1
  34. package/lib/thin/protocol/messages/aqArray.js +2 -2
  35. package/lib/thin/protocol/messages/aqBase.js +2 -2
  36. package/lib/thin/protocol/messages/auth.js +17 -6
  37. package/lib/thin/protocol/messages/base.js +75 -17
  38. package/lib/thin/protocol/messages/dataType.js +46 -0
  39. package/lib/thin/protocol/messages/directPathLoadStream.js +3 -2
  40. package/lib/thin/protocol/messages/execute.js +10 -4
  41. package/lib/thin/protocol/messages/index.js +4 -0
  42. package/lib/thin/protocol/messages/notification.js +281 -0
  43. package/lib/thin/protocol/messages/subscribe.js +181 -0
  44. package/lib/thin/protocol/messages/withData.js +23 -10
  45. package/lib/thin/protocol/packet.js +2 -4
  46. package/lib/thin/protocol/protocol.js +7 -4
  47. package/lib/thin/resultSet.js +40 -20
  48. package/lib/thin/sqlnet/ezConnectResolver.js +9 -2
  49. package/lib/thin/sqlnet/navNodes.js +7 -5
  50. package/lib/thin/sqlnet/networkSession.js +17 -11
  51. package/lib/thin/sqlnet/ntTcp.js +24 -8
  52. package/lib/thin/sqlnet/packet.js +24 -4
  53. package/lib/thin/sqlnet/sessionAtts.js +2 -2
  54. package/lib/thin/statement.js +6 -1
  55. package/lib/thin/statementCache.js +34 -6
  56. package/lib/thin/subscr.js +363 -0
  57. package/lib/transformer.js +60 -17
  58. package/lib/util.js +46 -8
  59. package/lib/version.js +1 -1
  60. package/package.json +1 -1
  61. package/plugins/configProviders/gcpstorage/index.js +121 -0
  62. package/plugins/configProviders/ociobject/index.js +1 -1
  63. package/plugins/token/cache.js +154 -0
  64. package/plugins/token/endUserSecurityProvider/index.js +377 -0
  65. package/plugins/token/extensionAzure/index.js +160 -28
  66. package/plugins/token/extensionOci/index.js +221 -16
  67. package/plugins/vectorsdk/OracleVecDB.js +346 -0
  68. package/plugins/vectorsdk/index.js +49 -0
  69. package/plugins/vectorsdk/utils.js +370 -0
  70. package/plugins/vectorsdk/vectorEmbedding.js +223 -0
  71. package/plugins/vectorsdk/vectorIndex.js +177 -0
  72. package/plugins/vectorsdk/vectorRecords.js +406 -0
  73. package/plugins/vectorsdk/vectorSchema.js +408 -0
  74. package/plugins/vectorsdk/vectorSearch.js +538 -0
  75. package/build/Release/oracledb-7.0.0-darwin-arm64.node +0 -0
  76. package/build/Release/oracledb-7.0.0-darwin-arm64.node-buildinfo.txt +0 -1
  77. package/build/Release/oracledb-7.0.0-js-buildinfo.txt +0 -1
  78. package/build/Release/oracledb-7.0.0-linux-arm64.node +0 -0
  79. package/build/Release/oracledb-7.0.0-linux-arm64.node-buildinfo.txt +0 -1
  80. package/build/Release/oracledb-7.0.0-linux-x64.node +0 -0
  81. package/build/Release/oracledb-7.0.0-linux-x64.node-buildinfo.txt +0 -1
  82. package/build/Release/oracledb-7.0.0-win32-x64.node +0 -0
  83. package/build/Release/oracledb-7.0.0-win32-x64.node-buildinfo.txt +0 -1
  84. package/eslint.config.js +0 -39
package/README.md CHANGED
@@ -1,22 +1,30 @@
1
- # node-oracledb version 7.0.0 <img src="https://img.shields.io/npm/v/oracledb.svg" alt="oracledb"/> <img src="https://img.shields.io/npm/dm/oracledb.svg" alt="oracledb"/>
1
+ # node-oracledb version 26.0.0 <img src="https://img.shields.io/npm/v/oracledb.svg" alt="oracledb"/> <img src="https://img.shields.io/npm/dm/oracledb.svg" alt="oracledb"/>
2
2
 
3
3
  The node-oracledb add-on for Node.js powers high performance Oracle Database
4
4
  applications. Applications can be written in TypeScript, or directly in
5
5
  JavaScript.
6
6
 
7
- Use node-oracledb 7.0.0 to connect Node.js 14.17, or later, to Oracle
7
+ Use node-oracledb 26.0.0 to connect Node.js 14.17, or later, to Oracle
8
8
  Database. Older versions of node-oracledb may work with older versions of
9
9
  Node.js.
10
10
 
11
11
  Node-oracledb supports basic and advanced features of Oracle Database
12
- and Oracle Client. See the [homepage][4] for a list.
12
+ and Oracle Client. See the [homepage][4] for a list. It is used by many
13
+ JavaScript and TypeScript frameworks, SQL generators, ORMs, and libraries.
14
+
15
+ Node-oracledb has a rich feature set which is easy to use. It gives you
16
+ control over SQL and PL/SQL statement execution, fast data ingestion, support
17
+ for calling NoSQL-style document APIs, message queueing,database notifications
18
+ and for starting and stopping the database.
19
+ It also has high availability and security features. Database operations
20
+ can optionally be [pipelined][16].
13
21
 
14
22
  The node-oracledb module is open source and maintained by Oracle Corp.
15
23
  It is stable, well documented, and has a comprehensive test suite.
16
24
 
17
25
  ## Installation
18
26
 
19
- Run `npm install oracledb`
27
+ Run `npm install oracledb`.
20
28
 
21
29
  See [Getting Started with Node-oracledb][1] and [Quick Start Node-oracledb Installation][2].
22
30
 
@@ -40,7 +48,8 @@ See [Getting Started with Node-oracledb][1] and [Quick Start Node-oracledb Insta
40
48
  available when optional Oracle Client libraries are loaded by
41
49
  node-oracledb. Libraries are available in the free [Oracle Instant
42
50
  Client][15] packages. Node-oracledb can use Oracle Client libraries version
43
- 19 and later.
51
+ 19 or later. Older node-oracledb versions supported older Client
52
+ versions.
44
53
 
45
54
  - Oracle Database
46
55
 
@@ -118,4 +127,5 @@ limitations under the License.
118
127
  [12]: https://github.com/oracle/node-oracledb/blob/main/CONTRIBUTING.md
119
128
  [13]: https://github.com/oracle/node-oracledb/blob/main/SECURITY.md
120
129
  [14]: https://www.npmjs.com/package/oracledb
121
- [15]: https://www.oracle.com/database/technologies/instant-client.html
130
+ [15]: https://www.oracle.com/database/technologies/instant-client.html
131
+ [16]: https://node-oracledb.readthedocs.io/en/latest/user_guide/pipeline.html
@@ -0,0 +1 @@
1
+ oracledb-26.0.0-darwin-arm64.node v24.11.1 fdba9345f92e83a5f0e66f01a5953ecfc358e053 1c8cabceb27b2e7a64737fb3de66f3d0014eed5f Mon, 28 Sep 2026 10:55:10 GMT
@@ -0,0 +1 @@
1
+ fdba9345f92e83a5f0e66f01a5953ecfc358e053
@@ -0,0 +1 @@
1
+ oracledb-26.0.0-linux-arm64.node v24.19.0 fdba9345f92e83a5f0e66f01a5953ecfc358e053 1c8cabceb27b2e7a64737fb3de66f3d0014eed5f Mon, 28 Sep 2026 10:49:51 GMT
@@ -0,0 +1 @@
1
+ oracledb-26.0.0-linux-x64.node v22.19.0 fdba9345f92e83a5f0e66f01a5953ecfc358e053 1c8cabceb27b2e7a64737fb3de66f3d0014eed5f Mon, 28 Sep 2026 10:48:56 GMT
@@ -0,0 +1 @@
1
+ oracledb-26.0.0-win32-x64.node v22.17.1 fdba9345f92e83a5f0e66f01a5953ecfc358e053 1c8cabceb27b2e7a64737fb3de66f3d0014eed5f Mon, 28 Sep 2026 10:54:04 GMT
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2019, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2019, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -56,7 +56,7 @@ class AqDeqOptions {
56
56
  }
57
57
 
58
58
  set consumerName(value) {
59
- errors.assertPropValue(typeof value === 'string', "consumerName");
59
+ errors.assertPropLength(value, "string", 128, "consumerName");
60
60
  this._impl.setConsumerName(value);
61
61
  }
62
62
 
@@ -70,7 +70,7 @@ class AqDeqOptions {
70
70
  }
71
71
 
72
72
  set correlation(value) {
73
- errors.assertPropValue(typeof value === 'string', "correlation");
73
+ errors.assertPropLength(value, "string", 128, "correlation");
74
74
  this._impl.setCorrelation(value);
75
75
  }
76
76
 
@@ -148,7 +148,7 @@ class AqDeqOptions {
148
148
  }
149
149
 
150
150
  set transformation(value) {
151
- errors.assertPropValue(typeof value === 'string', "transformation");
151
+ errors.assertPropLength(value, "string", 128, "transformation");
152
152
  this._impl.setTransformation(value);
153
153
  }
154
154
 
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2019, 2022, Oracle and/or its affiliates.
1
+ // Copyright (c) 2019, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -57,7 +57,7 @@ class AqEnqOptions {
57
57
  }
58
58
 
59
59
  set transformation(value) {
60
- errors.assertPropValue(typeof value === 'string', "transformation");
60
+ errors.assertPropLength(value, "string", 128, "transformation");
61
61
  this._impl.setTransformation(value);
62
62
  }
63
63
 
package/lib/aqQueue.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2019, 2025, Oracle and/or its affiliates.
1
+ // Copyright (c) 2019, 2026, Oracle and/or its affiliates.
2
2
 
3
3
  //-----------------------------------------------------------------------------
4
4
  //
@@ -104,15 +104,15 @@ class AqQueue {
104
104
 
105
105
  // validate options, if applicable
106
106
  if (message.correlation !== undefined) {
107
- errors.assertParamPropValue(typeof message.correlation === 'string', 1,
107
+ errors.assertPropLength(message.correlation, "string", 128,
108
108
  "correlation");
109
109
  }
110
110
  if (message.delay !== undefined) {
111
111
  errors.assertParamPropValue(Number.isInteger(message.delay), 1, "delay");
112
112
  }
113
113
  if (message.exceptionQueue !== undefined) {
114
- errors.assertParamPropValue(typeof message.exceptionQueue === 'string',
115
- 1, "exceptionQueue");
114
+ errors.assertPropLength(message.exceptionQueue, "string", 128,
115
+ "exceptionQueue");
116
116
  }
117
117
  if (message.expiration !== undefined) {
118
118
  errors.assertParamPropValue(Number.isInteger(message.expiration), 1,
@@ -123,8 +123,13 @@ class AqQueue {
123
123
  "priority");
124
124
  }
125
125
  if (message.recipients !== undefined) {
126
- errors.assertParamPropValue(nodbUtil.isArrayOfStrings(message.recipients),
127
- 1, "recipients");
126
+ // Check recipients length and type
127
+ errors.assertPropLength(message.recipients, "array", 1024, "recipients");
128
+ // Check value and length of each recipient
129
+ message.recipients.forEach((recipient, index) => {
130
+ errors.assertPropLength(recipient, "string", 128,
131
+ `recipient at index ${index}`);
132
+ });
128
133
  }
129
134
 
130
135
  return message;
package/lib/connection.js CHANGED
@@ -55,6 +55,19 @@ const APP_CONTEXT_VALUE_MAX_BYTES = 4 * 1000;
55
55
  // and destroyed with another!
56
56
  const _subscriptions = new Map();
57
57
 
58
+ //---------------------------------------------------------------------------
59
+ // _removeSubscription()
60
+ //
61
+ // Remove a subscription only when it is still the subscription currently
62
+ // registered for the name. A terminal old subscription must not remove a newer
63
+ // subscription that reused the same name.
64
+ //---------------------------------------------------------------------------
65
+ function _removeSubscription(name, subscription) {
66
+ if (_subscriptions.get(name) === subscription) {
67
+ _subscriptions.delete(name);
68
+ }
69
+ }
70
+
58
71
  // default closure for NUMBER type.
59
72
  const defaultNumberConverter = (v) => (v === null) ? null : parseFloat(v);
60
73
 
@@ -83,9 +96,8 @@ function _determineDbObjTypeConverter(metadata, options) {
83
96
  }
84
97
  if ([types.DB_TYPE_CLOB, types.DB_TYPE_NCLOB, types.DB_TYPE_BLOB,
85
98
  types.DB_TYPE_BFILE].includes(metadata.type)) {
86
- // converters for LOB's are not supported.
87
- return errors.throwErr(errors.ERR_NOT_IMPLEMENTED,
88
- 'DbObjConverter for LOBs');
99
+ // converters for LOBs are not supported.
100
+ return errors.throwNotImplemented('DbObjConverter for LOBs');
89
101
  }
90
102
  metadata.converter = result.converter;
91
103
  }
@@ -905,6 +917,23 @@ class Connection extends EventEmitter {
905
917
  this._impl.setCurrentSchema(value);
906
918
  }
907
919
 
920
+ //---------------------------------------------------------------------------
921
+ // databaseOpenTelemetryTracing
922
+ //
923
+ // Property for enabling database OpenTelemetry trace propagation.
924
+ //---------------------------------------------------------------------------
925
+ get databaseOpenTelemetryTracing() {
926
+ if (this._impl)
927
+ return this._impl.getDatabaseOpenTelemetryTracing();
928
+ return undefined;
929
+ }
930
+
931
+ set databaseOpenTelemetryTracing(value) {
932
+ errors.assertPropValue(typeof value === 'boolean', "databaseOpenTelemetryTracing");
933
+ errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
934
+ this._impl.setDatabaseOpenTelemetryTracing(value);
935
+ }
936
+
908
937
  //---------------------------------------------------------------------------
909
938
  // dbOp
910
939
  //
@@ -1406,7 +1435,7 @@ class Connection extends EventEmitter {
1406
1435
  errors.assertArgCount(arguments, 1, 1);
1407
1436
  errors.assertParamValue(typeof sql === 'string', 1);
1408
1437
  errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1409
- const info = await this._impl.getStatementInfo(sql);
1438
+ const info = await this._impl.getStatementInfo(sql, { connection: this });
1410
1439
  if (info.metaData) {
1411
1440
  for (let i = 0; i < info.metaData.length; i++) {
1412
1441
  const m = info.metaData[i];
@@ -1564,6 +1593,21 @@ class Connection extends EventEmitter {
1564
1593
  return this._impl && this._impl.getTransactionInProgress();
1565
1594
  }
1566
1595
 
1596
+ //---------------------------------------------------------------------------
1597
+ // txnPriority
1598
+ //
1599
+ // Property for transaction priority associated with the connection.
1600
+ //---------------------------------------------------------------------------
1601
+ get txnPriority() {
1602
+ return this._impl && this._impl.getTxnPriority();
1603
+ }
1604
+
1605
+ set txnPriority(value) {
1606
+ errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1607
+ errors.assertTxnPriority(value);
1608
+ this._impl.setTxnPriority(value);
1609
+ }
1610
+
1567
1611
  //---------------------------------------------------------------------------
1568
1612
  // ping()
1569
1613
  //
@@ -1590,9 +1634,11 @@ class Connection extends EventEmitter {
1590
1634
  } else {
1591
1635
  options = {};
1592
1636
  }
1637
+ errors.assertParamPropUnsignedIntNonZero(options, 3, "rowsPerDataEvent");
1638
+ const rowsPerDataEvent = options.rowsPerDataEvent;
1593
1639
  options.resultSet = true;
1594
1640
 
1595
- const stream = new QueryStream();
1641
+ const stream = new QueryStream(undefined, rowsPerDataEvent);
1596
1642
 
1597
1643
  // calling execute() via nextTick to ensure that handlers are registered
1598
1644
  // prior to the events being emitted
@@ -1732,15 +1778,22 @@ class Connection extends EventEmitter {
1732
1778
  errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
1733
1779
 
1734
1780
  const inSubscr = _subscriptions.get(name);
1781
+ if (this.thin) {
1782
+ options._onRetire = (subscription) =>
1783
+ _removeSubscription(name, subscription);
1784
+ }
1735
1785
  const outValue = await this._impl.subscribe(inSubscr, options);
1736
1786
  let subscription;
1737
1787
  if (options.namespace === constants.SUBSCR_NAMESPACE_DBCHANGE) {
1738
1788
  subscription = outValue.subscription;
1739
1789
  delete outValue.subscription;
1790
+ outValue.regId = Number(outValue.regId);
1740
1791
  } else {
1741
1792
  subscription = outValue;
1742
1793
  }
1743
- _subscriptions.set(name, subscription);
1794
+ if (!this.thin || subscription._registered) {
1795
+ _subscriptions.set(name, subscription);
1796
+ }
1744
1797
  return outValue;
1745
1798
  }
1746
1799
 
@@ -1988,11 +2041,14 @@ class Connection extends EventEmitter {
1988
2041
 
1989
2042
  // Force a ResultSet and then fetch rows according to the requested
1990
2043
  // fetch variant in non-pipeline flow.
1991
- op.options.resultSet = true;
1992
- op.options.fetchTypeHandler = this._createFetchTypeHandler(op.fetchLobs);
2044
+ const execOptions = {
2045
+ ...op.options,
2046
+ resultSet: true,
2047
+ fetchTypeHandler: this._createFetchTypeHandler(op.fetchLobs)
2048
+ };
1993
2049
  let shouldClose = false;
1994
2050
  try {
1995
- res = await this._execute(op.statement, op.parameters, op.options);
2051
+ res = await this._execute(op.statement, op.parameters, execOptions);
1996
2052
  if (opType === 'fetchOne') {
1997
2053
  const row = await res.resultSet._getRow();
1998
2054
  res.rows = row ? [row] : [];
@@ -2095,12 +2151,18 @@ class Connection extends EventEmitter {
2095
2151
  errors.assertParamValue(typeof name === 'string', 1);
2096
2152
  errors.assert(this._impl, errors.ERR_INVALID_CONNECTION);
2097
2153
  errors.assert(_subscriptions.has(name), errors.ERR_INVALID_SUBSCR);
2098
- await this._impl.unsubscribe(_subscriptions.get(name));
2154
+ const subscription = _subscriptions.get(name);
2155
+ await this._impl.unsubscribe(subscription);
2156
+
2157
+ if (this.thin) {
2158
+ _removeSubscription(name, subscription);
2159
+ return;
2160
+ }
2099
2161
 
2100
2162
  // Delay decreasing the reference count
2101
2163
  // as NJS layer starts cleanup (uv_close) of async handle asynchronously.
2102
2164
  setTimeout(() => {
2103
- _subscriptions.delete(name);
2165
+ _removeSubscription(name, subscription);
2104
2166
  }, 0);
2105
2167
  }
2106
2168
 
@@ -2116,13 +2178,6 @@ class Connection extends EventEmitter {
2116
2178
  errors.throwErr(errors.ERR_APP_CONTEXT_EMPTY_NAMESPACE);
2117
2179
  }
2118
2180
 
2119
- // Passing null/undefined/[] currently leaves existing entries intact in
2120
- // thick mode and triggers an error in thin mode.
2121
- // It does not clear the context; use clearAppContext() explicitly
2122
- // when you need to remove keys.
2123
- if (keyValues === null || keyValues === undefined) {
2124
- keyValues = [];
2125
- }
2126
2181
  errors.assertParamValue(Array.isArray(keyValues), 2);
2127
2182
 
2128
2183
  for (const entry of keyValues) {
package/lib/constants.js CHANGED
@@ -207,4 +207,8 @@ module.exports = {
207
207
  VECTOR_FORMAT_INT8: 4,
208
208
  VECTOR_FORMAT_BINARY: 5,
209
209
 
210
+ TXN_PRIORITY_HIGH: 'HIGH',
211
+ TXN_PRIORITY_MEDIUM: 'MEDIUM',
212
+ TXN_PRIORITY_LOW: 'LOW',
213
+ TXN_PRIORITY_DEFAULT: '',
210
214
  };
@@ -40,16 +40,17 @@ function hasNonEmptyText(value) {
40
40
  }
41
41
 
42
42
  /*
43
- Represents the credential set an application associates with an end user.
43
+ Represents a Deep Data Security credential set for an end user or, for direct
44
+ application logon, the application identity encoded by its database token.
44
45
 
45
46
  Required properties
46
47
  - databaseAccessToken: non-empty string accepted by the database driver.
47
- - endUserToken or endUserName: at least one must be supplied as a string.
48
- - key: required whenever endUserName is supplied; otherwise optional.
48
+ - endUserToken or endUserName: exactly one must be supplied as a string for an
49
+ end-user context. Neither is supplied for direct application logon.
49
50
 
50
51
  Optional properties
51
52
  - key: look-up identifier the database maps to stored context attributes; may be
52
- omitted only when identifying users via endUserToken.
53
+ supplied only with endUserName.
53
54
  - dataRoles: data roles enabled/disabled by the application logic, rather
54
55
  than a database security policy.
55
56
  - attributes: Attributes are contained in JSON objects conforming
@@ -72,29 +73,31 @@ class EndUserSecurityContext {
72
73
  dataRoles,
73
74
  attributes
74
75
  } = options;
75
-
76
- // The database access token is always required, and the end user must be
77
- // identified either by token or by name.
76
+ const hasEndUserToken = endUserToken !== undefined && endUserToken !== null;
77
+ const hasEndUserName = endUserName !== undefined && endUserName !== null;
78
+ const hasKey = key !== undefined && key !== null;
79
+
80
+ // The database access token is always required. An end-user context has
81
+ // exactly one identity mechanism. A context with neither token nor name
82
+ // is an application-direct logon: the database maps the client identity
83
+ // in databaseAccessToken to its registered application identity.
78
84
  errors.assertParamValue(hasNonEmptyText(databaseAccessToken), 1);
79
85
  errors.assertParamValue(
80
- (endUserToken !== undefined && endUserToken !== null) ||
81
- (endUserName !== undefined && endUserName !== null),
82
- 1
83
- );
86
+ (hasEndUserToken && !hasEndUserName && !hasKey) ||
87
+ (!hasEndUserToken && hasEndUserName) ||
88
+ (!hasEndUserToken && !hasEndUserName && !hasKey), 1);
84
89
 
85
90
  // When supplied, token/name/key values must contain non-whitespace text.
86
- if (endUserToken !== undefined && endUserToken !== null) {
91
+ if (hasEndUserToken) {
87
92
  errors.assertParamValue(hasNonEmptyText(endUserToken), 1);
88
93
  }
89
94
 
90
- // Name-based contexts require a key so the database can locate the stored
91
- // end user context attributes.
92
- if (endUserName !== undefined && endUserName !== null) {
95
+ if (hasEndUserName) {
93
96
  errors.assertParamValue(hasNonEmptyText(endUserName), 1);
94
- errors.assertParamValue(key !== undefined && key !== null, 1);
95
97
  }
96
98
 
97
- if (key !== undefined && key !== null) {
99
+ if (hasKey) {
100
+ errors.assertParamValue(hasEndUserName, 1);
98
101
  errors.assertParamValue(hasNonEmptyText(key), 1);
99
102
  }
100
103
 
@@ -117,7 +120,10 @@ class EndUserSecurityContext {
117
120
 
118
121
  // Data roles are optional, but must be passed as an array when present.
119
122
  if (dataRoles != null) {
120
- errors.assertParamValue(Array.isArray(dataRoles), 1);
123
+ errors.assertParamValue(
124
+ Array.isArray(dataRoles) && dataRoles.every(hasNonEmptyText),
125
+ 1
126
+ );
121
127
  payload.data_roles = Array.from(dataRoles);
122
128
  }
123
129
 
@@ -128,17 +134,21 @@ class EndUserSecurityContext {
128
134
  1
129
135
  );
130
136
  payload.attributes = Object.entries(attributes).map(
131
- ([name, values]) => ({ name, values }));
137
+ ([name, values]) => ({ name, values: values ?? null }));
132
138
  }
133
139
 
134
140
  this._payloadStore = new ObfuscatedValue();
135
141
  this._encodeAndStorePayload(payload);
136
142
  }
137
143
 
144
+ // Encode EUSC payloads with strict validation of unsupported JSON values and
145
+ // field names.
138
146
  _encodePayload(value) {
139
147
  const encoder = new oson.OsonEncoder();
140
148
  return encoder.encode(
141
- transformer.transformJsonValue(value),
149
+ transformer.transformJsonValue(value, {
150
+ rejectUnsupportedTypes: true
151
+ }),
142
152
  DEFAULT_MAX_FIELD_NAME_SIZE
143
153
  );
144
154
  }
@@ -159,7 +169,6 @@ class EndUserSecurityContext {
159
169
  payloadLength
160
170
  );
161
171
  }
162
- this.clearEncodedPayload();
163
172
  this._payloadStore.set(encodedPayload, { returnBuffer: true });
164
173
  encodedPayload.fill(0);
165
174
  }
@@ -167,10 +176,6 @@ class EndUserSecurityContext {
167
176
  getDeobfuscatedValue() {
168
177
  return this._payloadStore.get({ returnBuffer: true });
169
178
  }
170
-
171
- clearEncodedPayload() {
172
- this._payloadStore.clear();
173
- }
174
179
  }
175
180
 
176
181
  module.exports = EndUserSecurityContext;