@mcpher/gas-fakes 2.3.5 → 2.3.7

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.
@@ -1,13 +1,24 @@
1
1
  import { Proxies } from '../../support/proxies.js';
2
2
  import { newFakeJdbcResultSetMetaData } from './fakejdbcresultsetmetadata.js';
3
+ import { FakeJdbcBigDecimal } from './fakejdbcbigdecimal.js';
4
+ import { newFakeJdbcBlob } from './fakejdbcblob.js';
5
+ import { newFakeJdbcClob } from './fakejdbcclob.js';
6
+ import { newFakeJdbcArray } from './fakejdbcarray.js';
7
+ import { newFakeInputStream, newFakeReader } from '../../support/fakeinputstream.js';
3
8
 
4
9
  class FakeJdbcResultSet {
5
- constructor(result) {
10
+ constructor(result, statement) {
6
11
  this.__fakeObjectType = 'JdbcResultSet';
7
12
  this._rows = result.rows || [];
8
13
  this._fields = result.fields || [];
9
14
  this._currentIndex = -1;
10
15
  this._isClosed = false;
16
+ this._statement = statement;
17
+ this._lastValue = null;
18
+ this._fetchSize = 0;
19
+ this._fetchDirection = 1000; // FETCH_FORWARD
20
+ this._type = 1003; // TYPE_FORWARD_ONLY
21
+ this._concurrency = 1007; // CONCUR_READ_ONLY
11
22
  }
12
23
 
13
24
  next() {
@@ -16,15 +27,96 @@ class FakeJdbcResultSet {
16
27
  return this._currentIndex < this._rows.length;
17
28
  }
18
29
 
19
- _getValue(columnIndex) {
30
+ _getValue(columnIdentifier) {
20
31
  if (this._isClosed) throw new Error('ResultSet is closed.');
21
32
  if (this._currentIndex < 0 || this._currentIndex >= this._rows.length) {
22
33
  throw new Error('No current row.');
23
34
  }
35
+
36
+ let index;
37
+ if (typeof columnIdentifier === 'number') {
38
+ index = columnIdentifier;
39
+ } else {
40
+ index = this.findColumn(columnIdentifier);
41
+ }
42
+
24
43
  // JDBC columns are 1-indexed
25
- const field = this._fields[columnIndex - 1];
26
- if (!field) throw new Error(`Invalid column index: ${columnIndex}`);
27
- return this._rows[this._currentIndex][field.name];
44
+ const field = this._fields[index - 1];
45
+ if (!field) throw new Error(`Invalid column identifier: ${columnIdentifier}`);
46
+ const val = this._rows[this._currentIndex][field.name];
47
+ this._lastValue = val;
48
+ return val;
49
+ }
50
+
51
+ wasNull() {
52
+ return this._lastValue === null || this._lastValue === undefined;
53
+ }
54
+
55
+ absolute(row) {
56
+ if (this._isClosed) throw new Error('ResultSet is closed.');
57
+ if (row === 0) throw new Error('Rows are 1-indexed.');
58
+ if (row > 0) {
59
+ this._currentIndex = row - 1;
60
+ } else {
61
+ this._currentIndex = this._rows.length + row;
62
+ }
63
+ return this._currentIndex >= 0 && this._currentIndex < this._rows.length;
64
+ }
65
+
66
+ afterLast() {
67
+ if (this._isClosed) throw new Error('ResultSet is closed.');
68
+ this._currentIndex = this._rows.length;
69
+ }
70
+
71
+ beforeFirst() {
72
+ if (this._isClosed) throw new Error('ResultSet is closed.');
73
+ this._currentIndex = -1;
74
+ }
75
+
76
+ first() {
77
+ return this.absolute(1);
78
+ }
79
+
80
+ last() {
81
+ return this.absolute(-1);
82
+ }
83
+
84
+ previous() {
85
+ if (this._isClosed) throw new Error('ResultSet is closed.');
86
+ if (this._currentIndex > -1) {
87
+ this._currentIndex--;
88
+ }
89
+ return this._currentIndex >= 0 && this._currentIndex < this._rows.length;
90
+ }
91
+
92
+ relative(rows) {
93
+ return this.absolute(this._currentIndex + 1 + rows);
94
+ }
95
+
96
+ isAfterLast() {
97
+ if (this._isClosed) throw new Error('ResultSet is closed.');
98
+ return this._currentIndex >= this._rows.length && this._rows.length > 0;
99
+ }
100
+
101
+ isBeforeFirst() {
102
+ if (this._isClosed) throw new Error('ResultSet is closed.');
103
+ return this._currentIndex < 0 && this._rows.length > 0;
104
+ }
105
+
106
+ isFirst() {
107
+ if (this._isClosed) throw new Error('ResultSet is closed.');
108
+ return this._currentIndex === 0 && this._rows.length > 0;
109
+ }
110
+
111
+ isLast() {
112
+ if (this._isClosed) throw new Error('ResultSet is closed.');
113
+ return this._currentIndex === this._rows.length - 1 && this._rows.length > 0;
114
+ }
115
+
116
+ getRow() {
117
+ if (this._isClosed) throw new Error('ResultSet is closed.');
118
+ if (this._currentIndex < 0 || this._currentIndex >= this._rows.length) return 0;
119
+ return this._currentIndex + 1;
28
120
  }
29
121
 
30
122
  getString(columnIndex) {
@@ -37,20 +129,102 @@ class FakeJdbcResultSet {
37
129
  return val !== null && val !== undefined ? parseInt(val, 10) : 0;
38
130
  }
39
131
 
132
+ getLong(columnIndex) {
133
+ return this.getInt(columnIndex);
134
+ }
135
+
136
+ getShort(columnIndex) {
137
+ return this.getInt(columnIndex);
138
+ }
139
+
140
+ getBoolean(columnIndex) {
141
+ const val = this._getValue(columnIndex);
142
+ return Boolean(val);
143
+ }
144
+
40
145
  getFloat(columnIndex) {
146
+ const val = this._getValue(columnIndex);
147
+ // GAS getFloat() returns IEEE 754 single-precision (32-bit float)
148
+ return val !== null && val !== undefined ? Math.fround(parseFloat(val)) : 0.0;
149
+ }
150
+
151
+ getDouble(columnIndex) {
152
+ // getDouble() returns full 64-bit precision
41
153
  const val = this._getValue(columnIndex);
42
154
  return val !== null && val !== undefined ? parseFloat(val) : 0.0;
43
155
  }
156
+
157
+ getBigDecimal(columnIndex) {
158
+ const val = this._getValue(columnIndex);
159
+ return val !== null && val !== undefined ? new FakeJdbcBigDecimal(val) : null;
160
+ }
44
161
 
45
162
  getDate(columnIndex) {
46
163
  const val = this._getValue(columnIndex);
47
164
  return val ? new Date(val) : null;
48
165
  }
49
166
 
167
+ getTimestamp(columnIndex) {
168
+ return this.getDate(columnIndex);
169
+ }
170
+
50
171
  getObject(columnIndex) {
51
172
  return this._getValue(columnIndex);
52
173
  }
53
174
 
175
+ getBytes(columnIndex) {
176
+ const val = this._getValue(columnIndex);
177
+ if (val === null || val === undefined) return null;
178
+ return Array.from(Buffer.from(val));
179
+ }
180
+
181
+ getBlob(columnIndex) {
182
+ const val = this._getValue(columnIndex);
183
+ return val !== null && val !== undefined ? newFakeJdbcBlob(val) : null;
184
+ }
185
+
186
+ getClob(columnIndex) {
187
+ const val = this._getValue(columnIndex);
188
+ return val !== null && val !== undefined ? newFakeJdbcClob(val) : null;
189
+ }
190
+
191
+ getArray(columnIndex) {
192
+ const val = this._getValue(columnIndex);
193
+ return val !== null && val !== undefined ? newFakeJdbcArray(val) : null;
194
+ }
195
+
196
+ getUnicodeStream(columnIndex) {
197
+ const val = this._getValue(columnIndex);
198
+ return val !== null && val !== undefined ? newFakeInputStream(Buffer.from(String(val), 'utf8')) : null;
199
+ }
200
+
201
+ getAsciiStream(columnIndex) {
202
+ const val = this._getValue(columnIndex);
203
+ return val !== null && val !== undefined ? newFakeInputStream(Buffer.from(String(val), 'ascii')) : null;
204
+ }
205
+
206
+ getBinaryStream(columnIndex) {
207
+ const val = this._getValue(columnIndex);
208
+ return val !== null && val !== undefined ? newFakeInputStream(Buffer.from(val)) : null;
209
+ }
210
+
211
+ getCharacterStream(columnIndex) {
212
+ const val = this._getValue(columnIndex);
213
+ return val !== null && val !== undefined ? newFakeReader(String(val)) : null;
214
+ }
215
+
216
+ getCursorName() {
217
+ if (this._isClosed) throw new Error('ResultSet is closed.');
218
+ return 'fake-cursor';
219
+ }
220
+
221
+ findColumn(columnLabel) {
222
+ if (this._isClosed) throw new Error('ResultSet is closed.');
223
+ const index = this._fields.findIndex(f => f.name.toLowerCase() === columnLabel.toLowerCase());
224
+ if (index === -1) throw new Error(`Column not found: ${columnLabel}`);
225
+ return index + 1;
226
+ }
227
+
54
228
  getMetaData() {
55
229
  if (this._isClosed) throw new Error('ResultSet is closed.');
56
230
  return newFakeJdbcResultSetMetaData(this._fields);
@@ -62,6 +236,54 @@ class FakeJdbcResultSet {
62
236
  this._fields = [];
63
237
  }
64
238
 
239
+ getByte(columnIndex) {
240
+ return this.getInt(columnIndex);
241
+ }
242
+
243
+ getTime(columnIndex) {
244
+ return this.getDate(columnIndex);
245
+ }
246
+
247
+ getURL(columnIndex) {
248
+ return this.getString(columnIndex);
249
+ }
250
+
251
+ getFetchDirection() {
252
+ return this._fetchDirection;
253
+ }
254
+
255
+ getFetchSize() {
256
+ return this._fetchSize;
257
+ }
258
+
259
+ getType() {
260
+ return this._type;
261
+ }
262
+
263
+ getConcurrency() {
264
+ return this._concurrency;
265
+ }
266
+
267
+ setFetchDirection(direction) {
268
+ this._fetchDirection = direction;
269
+ }
270
+
271
+ setFetchSize(rows) {
272
+ this._fetchSize = rows;
273
+ }
274
+
275
+ getStatement() {
276
+ return this._statement;
277
+ }
278
+
279
+ getWarnings() {
280
+ return null;
281
+ }
282
+
283
+ clearWarnings() {
284
+ // No-op for fake
285
+ }
286
+
65
287
  isClosed() {
66
288
  return this._isClosed;
67
289
  }
@@ -34,6 +34,14 @@ class FakeJdbcResultSetMetaData {
34
34
  if (!field) throw new Error(`Invalid column index: ${column}`);
35
35
  return `TYPE_${field.dataTypeID}`; // basic fallback
36
36
  }
37
+
38
+ getTableName(column) {
39
+ return "";
40
+ }
41
+
42
+ getSchemaName(column) {
43
+ return "";
44
+ }
37
45
  }
38
46
 
39
47
  export const newFakeJdbcResultSetMetaData = (...args) => Proxies.guard(new FakeJdbcResultSetMetaData(...args));
@@ -7,12 +7,12 @@ class FakeJdbcService {
7
7
  this.__fakeObjectType = 'Jdbc';
8
8
  }
9
9
 
10
- /**
10
+ /**
11
11
  * Connects to a JDBC database.
12
12
  * In gas-fakes, we primarily support postgres via pg module, mapping jdbc:postgresql to it.
13
13
  *
14
14
  * @param {string} url The URL of the database to connect to.
15
- * @param {string} user (optional) The user name to connect as.
15
+ * @param {string|object} user (optional) The user name to connect as, or an object of property/value pairs.
16
16
  * @param {string} password (optional) The password for the user.
17
17
  * @returns {JdbcConnection} A JDBC connection object.
18
18
  */
@@ -24,39 +24,49 @@ class FakeJdbcService {
24
24
  }
25
25
 
26
26
  // Explicitly merge user/password into the URL structure for gas-fakes processing
27
- if (user !== undefined && user !== null && password !== undefined && password !== null) {
28
- try {
29
- const cleanUrl = finalUrl.replace(/^jdbc:google:/, '').replace(/^jdbc:/, '');
30
- const urlObj = new URL(cleanUrl);
31
- urlObj.username = encodeURIComponent(String(user));
32
- urlObj.password = encodeURIComponent(String(password));
33
-
34
- // Restore the prefix
35
- let prefix = "jdbc:";
36
- if (finalUrl.startsWith("jdbc:google:")) prefix = "jdbc:google:";
37
- finalUrl = prefix + urlObj.toString();
38
- } catch (e) {
39
- // Fallback for malformed URLs
27
+ if (typeof user === 'object' && user !== null) {
28
+ // Handling info object
29
+ const info = user;
30
+ const u = info.user || info.userName;
31
+ const p = info.password;
32
+ if (u && p) {
33
+ finalUrl = this._mergeCredentials(finalUrl, u, p);
40
34
  }
35
+ // Note: other info properties aren't currently merged into the URL string,
36
+ // but Syncit.fxJdbcConnect will receive the info object anyway.
37
+ } else if (user !== undefined && user !== null && password !== undefined && password !== null) {
38
+ finalUrl = this._mergeCredentials(finalUrl, user, password);
41
39
  }
42
40
 
43
- // We intentionally pass undefined for user and password so the worker only sees finalUrl
44
- return newFakeJdbcConnection(finalUrl, undefined, undefined);
41
+ // Pass user (which might be the info object) and password to the connection
42
+ return newFakeJdbcConnection(finalUrl, user, password);
43
+ }
44
+
45
+ _mergeCredentials(url, user, password) {
46
+ try {
47
+ const cleanUrl = url.replace(/^jdbc:google:/, '').replace(/^jdbc:/, '');
48
+ const urlObj = new URL(cleanUrl);
49
+ urlObj.username = encodeURIComponent(String(user));
50
+ urlObj.password = encodeURIComponent(String(password));
51
+
52
+ // Restore the prefix
53
+ let prefix = "jdbc:";
54
+ if (url.startsWith("jdbc:google:")) prefix = "jdbc:google:";
55
+ return prefix + urlObj.toString();
56
+ } catch (e) {
57
+ return url; // Fallback for malformed URLs
58
+ }
45
59
  }
46
60
 
47
61
  /**
48
- * Connects to a Google Cloud SQL instance - but on gas-fakes its the same thing
62
+ * Connects to a Google Cloud SQL instance.
49
63
  * @param {string} url The URL of the database to connect to.
50
- * @param {string} user (optional) The user name to connect as.
64
+ * @param {string|object} user (optional) The user name to connect as, or an object of property/value pairs.
51
65
  * @param {string} password (optional) The password for the user.
52
66
  * @returns {JdbcConnection} A JDBC connection object.
53
67
  */
54
68
  getCloudSqlConnection(url, user, password) {
55
- return this.getConnection (url, user, password)
56
- }
57
-
58
- parseCsv(csv) {
59
- throw new Error('Not implemented: parseCsv');
69
+ return this.getConnection(url, user, password);
60
70
  }
61
71
 
62
72
  __useProxy (val) {
@@ -90,7 +100,7 @@ class FakeJdbcService {
90
100
 
91
101
  const cleanUrl = url.replace(/^jdbc:google:/, "").replace(/^jdbc:/, "");
92
102
 
93
- let scheme, user, pass, host, db;
103
+ let scheme, user, pass, host, db, searchString = "";
94
104
 
95
105
  try {
96
106
  const parsed = new URL(cleanUrl);
@@ -110,7 +120,7 @@ class FakeJdbcService {
110
120
  remainingParams.delete('user');
111
121
  remainingParams.delete('password');
112
122
 
113
- const searchString = remainingParams.toString();
123
+ searchString = remainingParams.toString();
114
124
  if (searchString) {
115
125
  db = `${db}?${searchString}`;
116
126
  }
@@ -181,7 +191,7 @@ class FakeJdbcService {
181
191
  // Live Apps Script Java JDBC requires properly encoded URI components to prevent "Connection URL is malformed"
182
192
  const gasAuthQuery = (encodedUser || encodedPass) ? `?user=${encodedUser}&password=${encodedPass}` : '';
183
193
 
184
- // --- GAS ---
194
+ // --- GAS --- (Rule 3: Strip ALL SSL/Tunneling parameters)
185
195
  let gasUrl, gasConnectionString, gasFullConnectionString;
186
196
  let isCloudSqlConnection = false;
187
197
 
@@ -192,8 +202,8 @@ class FakeJdbcService {
192
202
  isCloudSqlConnection = true;
193
203
  } else {
194
204
  gasUrl = `${protocol}${remoteHost}/${pureDb}`;
195
- gasConnectionString = `${protocol}${remoteHost}/${pureDb}`;
196
- // Drop all original query parameters, only append explicit unencoded user, password, and ssl
205
+ gasConnectionString = gasUrl;
206
+ // Single-argument getConnection is discouraged on GAS, but if used, only append credentials
197
207
  gasFullConnectionString = `${protocol}${remoteHost}/${pureDb}${gasAuthQuery}`;
198
208
  }
199
209
 
@@ -3,10 +3,39 @@ import { Syncit } from '../../support/syncit.js';
3
3
  import { newFakeJdbcResultSet } from './fakejdbcresultset.js';
4
4
 
5
5
  class FakeJdbcStatement {
6
- constructor(connectionId) {
6
+ constructor(connection, connectionId) {
7
7
  this.__fakeObjectType = 'JdbcStatement';
8
+ this._connection = connection;
8
9
  this._connectionId = connectionId;
9
10
  this._isClosed = false;
11
+ this._batch = [];
12
+ this._fetchSize = 0;
13
+ this._maxRows = 0;
14
+ this._queryTimeout = 0;
15
+ this._maxFieldSize = 0;
16
+ this._fetchDirection = 1000; // FETCH_FORWARD
17
+ this._isPoolable = false;
18
+ this._lastUpdateCount = -1;
19
+ }
20
+
21
+ addBatch(sql) {
22
+ if (this._isClosed) throw new Error('Statement is closed.');
23
+ this._batch.push(sql);
24
+ }
25
+
26
+ executeBatch() {
27
+ if (this._isClosed) throw new Error('Statement is closed.');
28
+ const results = [];
29
+ for (const sql of this._batch) {
30
+ const result = Syncit.fxJdbcQuery(this._connectionId, sql);
31
+ results.push(result.rowCount || 0);
32
+ }
33
+ this.clearBatch();
34
+ return results;
35
+ }
36
+
37
+ clearBatch() {
38
+ this._batch = [];
10
39
  }
11
40
 
12
41
  executeQuery(sql) {
@@ -14,21 +43,138 @@ class FakeJdbcStatement {
14
43
 
15
44
  // Fetch result synchronously from worker
16
45
  const result = Syncit.fxJdbcQuery(this._connectionId, sql);
17
-
18
- return newFakeJdbcResultSet(result);
46
+ this._lastUpdateCount = -1;
47
+ this._lastResultSet = newFakeJdbcResultSet(result, this);
48
+ return this._lastResultSet;
19
49
  }
20
50
 
21
- execute(sql) {
51
+ execute(sql, autoGeneratedKeysOrIndexesOrNames) {
22
52
  if (this._isClosed) throw new Error('Statement is closed.');
23
53
 
24
- // In actual JDBC, execute returns true if the first result is a ResultSet
25
- // node-postgres gives us an array of rows or a rowCount.
26
54
  const result = Syncit.fxJdbcQuery(this._connectionId, sql);
27
- return result.fields && result.fields.length > 0;
55
+ this._lastUpdateCount = result.rowCount || 0;
56
+ if (result.fields && result.fields.length > 0) {
57
+ this._lastResultSet = newFakeJdbcResultSet(result, this);
58
+ return true;
59
+ } else {
60
+ this._lastResultSet = null;
61
+ return false;
62
+ }
63
+ }
64
+
65
+ executeUpdate(sql, autoGeneratedKeysOrIndexesOrNames) {
66
+ if (this._isClosed) throw new Error('Statement is closed.');
67
+ const result = Syncit.fxJdbcQuery(this._connectionId, sql);
68
+ this._lastUpdateCount = result.rowCount;
69
+ this._lastResultSet = null;
70
+ return result.rowCount;
71
+ }
72
+
73
+ cancel() {
74
+ // Stub: No-op for fake
75
+ }
76
+
77
+ clearWarnings() {
78
+ // Stub: No-op for fake
79
+ }
80
+
81
+ getConnection() {
82
+ return this._connection;
83
+ }
84
+
85
+ getFetchDirection() {
86
+ return this._fetchDirection;
87
+ }
88
+
89
+ getFetchSize() {
90
+ return this._fetchSize;
91
+ }
92
+
93
+ getGeneratedKeys() {
94
+ // Return empty results as we don't currently track generated auto-increment IDs
95
+ return newFakeJdbcResultSet({ fields: [], rows: [] }, this);
96
+ }
97
+
98
+ getMaxFieldSize() {
99
+ return this._maxFieldSize;
100
+ }
101
+
102
+ getMaxRows() {
103
+ return this._maxRows;
104
+ }
105
+
106
+ getMoreResults(current) {
107
+ // We only support one result set at a time per statement in this fake
108
+ return false;
109
+ }
110
+
111
+ getQueryTimeout() {
112
+ return this._queryTimeout;
113
+ }
114
+
115
+ getResultSetConcurrency() {
116
+ return 1007; // CONCUR_READ_ONLY
117
+ }
118
+
119
+ getResultSetHoldability() {
120
+ return 1; // HOLD_CURSORS_OVER_COMMIT
121
+ }
122
+
123
+ getResultSetType() {
124
+ return 1003; // TYPE_FORWARD_ONLY
125
+ }
126
+
127
+ getWarnings() {
128
+ return null;
129
+ }
130
+
131
+ isPoolable() {
132
+ return this._isPoolable;
133
+ }
134
+
135
+ setCursorName(name) {
136
+ // Stub: No-op for fake
137
+ }
138
+
139
+ setEscapeProcessing(enable) {
140
+ // Stub: No-op for fake
141
+ }
142
+
143
+ setFetchDirection(direction) {
144
+ this._fetchDirection = direction;
145
+ }
146
+
147
+ setFetchSize(rows) {
148
+ this._fetchSize = rows;
149
+ }
150
+
151
+ setMaxFieldSize(max) {
152
+ this._maxFieldSize = max;
153
+ }
154
+
155
+ setMaxRows(max) {
156
+ this._maxRows = max;
157
+ }
158
+
159
+ setPoolable(poolable) {
160
+ this._isPoolable = poolable;
161
+ }
162
+
163
+ setQueryTimeout(seconds) {
164
+ this._queryTimeout = seconds;
165
+ }
166
+
167
+ getUpdateCount() {
168
+ return this._lastUpdateCount || 0;
169
+ }
170
+
171
+ getResultSet() {
172
+ return this._lastResultSet;
28
173
  }
29
174
 
30
175
  close() {
31
176
  this._isClosed = true;
177
+ this._lastResultSet = null;
32
178
  }
33
179
 
34
180
  isClosed() {
@@ -0,0 +1,42 @@
1
+ import { Proxies } from './proxies.js';
2
+
3
+ class FakeInputStream {
4
+ constructor(data) {
5
+ this.__fakeObjectType = 'InputStream';
6
+ this._data = data instanceof Uint8Array ? data : Buffer.from(data || '');
7
+ this._pos = 0;
8
+ }
9
+
10
+ read() {
11
+ if (this._pos >= this._data.length) return -1;
12
+ return this._data[this._pos++];
13
+ }
14
+
15
+ available() {
16
+ return this._data.length - this._pos;
17
+ }
18
+
19
+ close() {
20
+ this._pos = this._data.length;
21
+ }
22
+ }
23
+
24
+ class FakeReader {
25
+ constructor(data) {
26
+ this.__fakeObjectType = 'Reader';
27
+ this._data = String(data || '');
28
+ this._pos = 0;
29
+ }
30
+
31
+ read() {
32
+ if (this._pos >= this._data.length) return -1;
33
+ return this._data.charCodeAt(this._pos++);
34
+ }
35
+
36
+ close() {
37
+ this._pos = this._data.length;
38
+ }
39
+ }
40
+
41
+ export const newFakeInputStream = (...args) => Proxies.guard(new FakeInputStream(...args));
42
+ export const newFakeReader = (...args) => Proxies.guard(new FakeReader(...args));
@@ -0,0 +1,16 @@
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+
4
+ /**
5
+ * Initializes the global GasFakes metadata object.
6
+ * @param {string} packageJsonPath The relative path to package.json from this file.
7
+ * @returns {object} The GasFakes global object.
8
+ */
9
+ export function initMetadata(packageJsonPath = '../../package.json') {
10
+ // add the version number to gasfakes metadata
11
+ if (!globalThis.GasFakes) globalThis.GasFakes = {};
12
+ const pjson = require(packageJsonPath);
13
+ if (!globalThis.GasFakes.metadata) globalThis.GasFakes.metadata = {}
14
+ globalThis.GasFakes.metadata.version = pjson.version
15
+ return globalThis.GasFakes;
16
+ }