From b6ca17e790ffc9a02432997b0699135623b9afc4 Mon Sep 17 00:00:00 2001 From: Utkarsh Maheshwari Date: Wed, 28 Feb 2024 14:58:19 +0530 Subject: [PATCH 1/2] Implement pm.require (#976) --- CHANGELOG.yaml | 4 + lib/postman-sandbox.js | 2 + lib/sandbox/execute.js | 14 +- lib/sandbox/pm-require.js | 176 ++++++ lib/sandbox/pmapi.js | 13 +- package-lock.json | 12 +- package.json | 4 +- .../unit/sandbox-libraries/pm-require.test.js | 508 ++++++++++++++++++ types/index.d.ts | 75 ++- types/sandbox/prerequest.d.ts | 11 +- types/sandbox/test.d.ts | 11 +- 11 files changed, 812 insertions(+), 18 deletions(-) create mode 100644 lib/sandbox/pm-require.js create mode 100644 test/unit/sandbox-libraries/pm-require.test.js diff --git a/CHANGELOG.yaml b/CHANGELOG.yaml index a7533a66..bb28e8d4 100644 --- a/CHANGELOG.yaml +++ b/CHANGELOG.yaml @@ -1,3 +1,7 @@ +unreleased: + new features: + - GH-976 Add `pm.require` API to use packages inside scripts + 4.4.0: date: 2023-11-18 new features: diff --git a/lib/postman-sandbox.js b/lib/postman-sandbox.js index 2b7b9c45..50568ae7 100644 --- a/lib/postman-sandbox.js +++ b/lib/postman-sandbox.js @@ -93,6 +93,7 @@ class PostmanSandbox extends UniversalVM { executionEventName = 'execution.result.' + id, executionTimeout = _.get(options, 'timeout', this.executionTimeout), cursor = _.clone(_.get(options, 'cursor', {})), // clone the cursor as it travels through IPC for mutation + resolvedPackages = _.get(options, 'resolvedPackages'), debugMode = _.has(options, 'debug') ? options.debug : this.debug; let waiting; @@ -126,6 +127,7 @@ class PostmanSandbox extends UniversalVM { cursor: cursor, debug: debugMode, timeout: executionTimeout, + resolvedPackages: resolvedPackages, legacy: _.get(options, 'legacy') }); } diff --git a/lib/sandbox/execute.js b/lib/sandbox/execute.js index ac2260fe..4e7b8ee6 100644 --- a/lib/sandbox/execute.js +++ b/lib/sandbox/execute.js @@ -9,6 +9,7 @@ const _ = require('lodash'), PostmanTimers = require('./timers'), PostmanAPI = require('./pmapi'), PostmanCookieStore = require('./cookie-store'), + createPostmanRequire = require('./pm-require'), EXECUTION_RESULT_EVENT_BASE = 'execution.result.', EXECUTION_REQUEST_EVENT_BASE = 'execution.request.', @@ -120,6 +121,10 @@ module.exports = function (bridge, glob) { // create the execution object execution = new Execution(id, event, context, { ...options, initializeExecution }), + disabledAPIs = [ + ...(initializationOptions.disabledAPIs || []) + ], + /** * Dispatch assertions from `pm.test` or legacy `test` API. * @@ -205,6 +210,10 @@ module.exports = function (bridge, glob) { timers.clearEvent(id, err, res); }); + if (!options.resolvedPackages) { + disabledAPIs.push('require'); + } + // send control to the function that executes the context and prepares the scope executeContext(scope, code, execution, // if a console is sent, we use it. otherwise this also prevents erroneous referencing to any console @@ -228,9 +237,8 @@ module.exports = function (bridge, glob) { }, dispatchAssertions, new PostmanCookieStore(id, bridge, timers), - { - disabledAPIs: initializationOptions.disabledAPIs - }) + createPostmanRequire(options.resolvedPackages, scope), + { disabledAPIs }) ), dispatchAssertions, { disableLegacyAPIs: initializationOptions.disableLegacyAPIs }); diff --git a/lib/sandbox/pm-require.js b/lib/sandbox/pm-require.js new file mode 100644 index 00000000..5d2c55ee --- /dev/null +++ b/lib/sandbox/pm-require.js @@ -0,0 +1,176 @@ +const MODULE_KEY = '__module_obj', // why not use `module`? + MODULE_WRAPPER = [ + '(function (exports, module) {\n', + `\n})(${MODULE_KEY}.exports, ${MODULE_KEY});` + ]; + +/** + * Cache of all files that are available to be required. + * + * @typedef {Object.} FileCache + */ + +class PostmanRequireStore { + /** + * @param {FileCache} fileCache - fileCache + */ + constructor (fileCache) { + if (!fileCache) { + throw new Error('File cache is required'); + } + + this.fileCache = fileCache; + } + + /** + * Check if the file is available in the cache. + * + * @param {string} path - path + * @returns {boolean} + */ + hasFile (path) { + return Boolean(this.getFile(path)); + } + + /** + * Get the file from the cache. + * + * @param {string} path - path + * @returns {Object|undefined} - file + */ + getFile (path) { + return this.fileCache[path]; + } + + /** + * Get the resolved path for the file. + * + * @param {string} path - path + * @returns {string|undefined} - resolved path + */ + getResolvedPath (path) { + if (this.hasFile(path)) { + return path; + } + } + + /** + * Get the file data. + * + * @param {string} path - path + * @returns {string|undefined} + */ + getFileData (path) { + return this.hasFile(path) && this.getFile(path).data; + } + + /** + * Check if the file has an error. + * + * @param {string} path - path + * @returns {boolean} + */ + hasError (path) { + return this.hasFile(path) && Boolean(this.getFile(path).error); + } + + /** + * Get the file error. + * + * @param {string} path - path + * @returns {string|undefined} + */ + getFileError (path) { + return this.hasError(path) && this.getFile(path).error; + } +} + +/** + * @param {FileCache} fileCache - fileCache + * @param {Object} scope - scope + * @returns {Function} - postmanRequire + * @example + * const fileCache = { + * 'path/to/file.js': { + * data: 'module.exports = { foo: "bar" };' + * } + * }; + * + * const postmanRequire = createPostmanRequire(fileCache, scope); + * + * const module = postmanRequire('path/to/file.js'); + * console.log(module.foo); // bar + */ +function createPostmanRequire (fileCache, scope) { + const store = new PostmanRequireStore(fileCache || {}), + cache = {}; + + /** + * @param {string} name - name + * @returns {any} - module + */ + function postmanRequire (name) { + const path = store.getResolvedPath(name); + + if (!path) { + // Error should contain the name exactly as the user specified, + // and not the resolved path. + throw new Error(`Cannot find module '${name}'`); + } + + if (store.hasError(path)) { + throw new Error(`Error while loading module '${name}': ${store.getFileError(path)}`); + } + + // Any module should not be evaluated twice, so we use it from the + // cache. If there's a circular dependency, the partially evaluated + // module will be returned from the cache. + if (cache[path]) { + // Always use the resolved path as the ID of the module. This + // ensures that relative paths are handled correctly. + return cache[path].exports; + } + + /* eslint-disable-next-line one-var */ + const file = store.getFileData(path), + moduleObj = { + id: path, + exports: {} + }; + + // Add to cache before executing. This ensures that any dependency + // that tries to import it's parent/ancestor gets the cached + // version and not end up in infinite loop. + cache[moduleObj.id] = moduleObj; + + /* eslint-disable-next-line one-var */ + const wrappedModule = MODULE_WRAPPER[0] + file + MODULE_WRAPPER[1]; + + scope.import({ + [MODULE_KEY]: moduleObj + }); + + // Note: We're executing the code in the same scope as the one + // which called the `pm.require` function. This is because we want + // to share the global scope across all the required modules. Any + // locals are available inside the required modules and any locals + // created inside the required modules are available to the parent. + // + // Why `async` = true? + // - We want to allow execution of async code like setTimeout etc. + scope.exec(wrappedModule, true, (err) => { + // Bubble up the error to be caught as execution error + if (err) { + throw err; + } + }); + + scope.unset(MODULE_KEY); + + return moduleObj.exports; + } + + return postmanRequire; +} + +module.exports = createPostmanRequire; diff --git a/lib/sandbox/pmapi.js b/lib/sandbox/pmapi.js index db514c82..95bdc1dc 100644 --- a/lib/sandbox/pmapi.js +++ b/lib/sandbox/pmapi.js @@ -47,10 +47,11 @@ const _ = require('lodash'), * @param {Function} onSkipRequest - callback to execute when pm.execution.skipRequest() called * @param {Function} onAssertion - callback to execute when pm.expect() called * @param {Object} cookieStore - cookie store + * @param {Function} requireFn - requireFn * @param {Object} [options] - options * @param {Array.} [options.disabledAPIs] - list of disabled APIs */ -function Postman (execution, onRequest, onSkipRequest, onAssertion, cookieStore, options = {}) { +function Postman (execution, onRequest, onSkipRequest, onAssertion, cookieStore, requireFn, options = {}) { // @todo - ensure runtime passes data in a scope format let iterationData = new VariableScope(); @@ -291,6 +292,16 @@ function Postman (execution, onRequest, onSkipRequest, onAssertion, cookieStore, */ current: execution.legacy._eventItemName }) + }, + + /** + * Imports a package in the script. + * + * @param {String} name - name of the module + * @returns {any} - exports from the module + */ + require: function (name) { + return requireFn(name); } }, options.disabledAPIs); diff --git a/package-lock.json b/package-lock.json index 446ff81d..d0cb854a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5167,9 +5167,9 @@ } }, "postman-collection": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.3.0.tgz", - "integrity": "sha512-QpmNOw1JhAVQTFWRz443/qpKs4/3T1MFrKqDZ84RS1akxOzhXXr15kD8+/+jeA877qyy9rfMsrFgLe2W7aCPjw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.4.0.tgz", + "integrity": "sha512-2BGDFcUwlK08CqZFUlIC8kwRJueVzPjZnnokWPtJCd9f2J06HBQpGL7t2P1Ud1NEsK9NHq9wdipUhWLOPj5s/Q==", "requires": { "@faker-js/faker": "5.5.3", "file-type": "3.9.0", @@ -6310,9 +6310,9 @@ "dev": true }, "uniscope": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/uniscope/-/uniscope-2.0.1.tgz", - "integrity": "sha512-qjEYNHjlJUbP87C8nVhAVFDrg2tt46Nqitkl+mOCukNz5tLtQCY0ifLR46VDyo7coaq+cD+BZi9CE/0WDjuWOg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uniscope/-/uniscope-2.1.0.tgz", + "integrity": "sha512-nFkkwVnj/sLUgLxffkJXTHEOCY7uuVHItzmEyEibL9OdtTrXRhd8uHBAS6kgTUrmDPpKZlz/Ts/CdEgqj6o+Tg==", "dev": true }, "universalify": { diff --git a/package.json b/package.json index a3fef1dd..da680202 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ }, "dependencies": { "lodash": "4.17.21", - "postman-collection": "4.3.0", + "postman-collection": "4.4.0", "teleport-javascript": "1.0.0", "uvm": "2.1.1" }, @@ -92,7 +92,7 @@ "terser": "^5.24.0", "tsd-jsdoc": "^2.5.0", "tv4": "1.3.0", - "uniscope": "2.0.1", + "uniscope": "2.1.0", "watchify": "^4.0.0", "xml2js": "0.4.23" }, diff --git a/test/unit/sandbox-libraries/pm-require.test.js b/test/unit/sandbox-libraries/pm-require.test.js new file mode 100644 index 00000000..164a248e --- /dev/null +++ b/test/unit/sandbox-libraries/pm-require.test.js @@ -0,0 +1,508 @@ +describe('sandbox library - pm.require api', function () { + this.timeout(1000 * 60); + var Sandbox = require('../../../'), + + sampleContextData = { + globals: [{ + key: 'var1', + value: 'one' + }, { + key: 'var2', + value: 2, + type: 'number' + }], + environment: [{ + key: 'var1', + value: 'one-env' + }, { + key: 'var2', + value: 2.5, + type: 'number' + }], + collectionVariables: [{ + key: 'var1', + value: 'collection-var1', + type: 'string' + }, { + key: 'var2', + value: 2.9, + type: 'number' + }], + data: { + var1: 'one-data' + } + }, + context; + + beforeEach(function (done) { + Sandbox.createContext({ debug: true }, function (err, ctx) { + context = ctx; + done(err); + }); + }); + + afterEach(function () { + context.dispose(); + context = null; + }); + + it('should be an exposed function', function (done) { + context.execute(` + var assert = require('assert'); + assert.strictEqual((typeof pm.require), 'function'); + `, { + context: sampleContextData, + resolvedPackages: {} + }, done); + }); + + it('should not be a function if resolvedPackages is not present', function (done) { + context.execute(` + var assert = require('assert'); + assert.strictEqual((typeof pm.require), 'undefined'); + `, { + context: sampleContextData + }, done); + }); + + it('should return the exports from the file', function (done) { + context.execute(` + var assert = require('assert'); + var mod = pm.require('mod1'); + assert.strictEqual(mod.a, 123); + assert.strictEqual(mod.b, 456); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: 'module.exports = { a: 123, b: 456 }' + } + } + }, done); + }); + + it('should throw error if required file is not found', function (done) { + context.execute(` + var assert = require('assert'); + try { + pm.require('mod1'); + } + catch (e) { + assert.strictEqual(e.message, "Cannot find module 'mod1'"); + } + `, { + context: sampleContextData, + resolvedPackages: {} + }, done); + }); + + it('should throw custom error if required file has error', function (done) { + context.execute(` + var assert = require('assert'); + try { + pm.require('mod1'); + throw new Error('should not reach here'); + } + catch (e) { + assert.strictEqual(e.message, "Error while loading module 'mod1': my error"); + } + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + error: 'my error' + } + } + }, done); + }); + + it('should throw error if required file throws error', function (done) { + context.execute(` + var assert = require('assert'); + try { + pm.require('mod1'); + throw new Error('expected to throw'); + } + catch (e) { + assert.strictEqual(e.message, "my error"); + } + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + name: 'mod1', + data: 'throw new Error("my error");' + } + } + }, done); + }); + + it('should not throw error if file is empty', function (done) { + context.execute(` + var assert = require('assert'); + var mod = pm.require('mod1'); + assert.deepEqual(mod, {}); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: '' + } + } + }, done); + }); + + it('should allow required files to access globals', function (done) { + context.execute(` + var assert = require('assert'); + var1 = 123; // global + var mod = pm.require('mod1'); + assert.strictEqual(mod.a, 123); + assert.strictEqual(mod.b, 456); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + var2 = 456; + module.exports = { a: var1, b: pm.require('mod2') }; + ` + }, + mod2: { + data: ` + module.exports = var2; + ` + } + } + }, done); + }); + + it('should allow setting globals from required files', function (done) { + context.execute(` + var assert = require('assert'); + pm.require('mod1'); + assert.strictEqual(var1, 123); + assert.strictEqual(var2, 456); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + var1 = 123; + var2 = 456; + ` + } + } + }, done); + }); + + it('should allow required files to access pm.* apis', function (done) { + context.execute(` + var assert = require('assert'); + var mod = pm.require('mod1'); + assert.strictEqual(mod.a, "one-env"); + assert.strictEqual(mod.b, 2); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + module.exports = { + a: pm.environment.get("var1"), + b: pm.globals.get("var2") + }; + ` + } + } + }, done); + }); + + it('should allow required files to require other files', function (done) { + context.execute(` + var assert = require('assert'); + var mod = pm.require('mod1'); + assert.strictEqual(mod.a, 123); + assert.strictEqual(mod.b, 345); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + module.exports = { + a: 123, + b: pm.require("mod2") + } + ` + }, + mod2: { + data: ` + module.exports = 345; + ` + } + } + }, done); + }); + + it('should not evaluate the same file twice', function (done) { + context.execute(` + var assert = require('assert'); + var mod1 = pm.require('mod1'); + var mod2 = pm.require('mod1'); + assert.strictEqual(mod1, mod2); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + module.exports = { a: 123 }; + ` + } + } + }, done); + }); + + it('should allow circular dependencies', function (done) { + context.execute(` + var assert = require('assert'); + var mod1 = pm.require('mod1'); + assert.strictEqual(mod1.a, 123); + assert.strictEqual(mod1.b, 123); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + module.exports.a = 123; + module.exports.b = pm.require("mod2"); + ` + }, + mod2: { + data: ` + module.exports = pm.require("mod1").a; + ` + } + } + }, done); + }); + + it('should allow required file to require itself', function (done) { + context.execute(` + var assert = require('assert'); + var mod1 = pm.require('mod1'); + assert.strictEqual(mod1.a, 123); + assert.strictEqual(mod1.b, 123); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + module.exports.a = 123; + module.exports.b = pm.require("mod1").a; + ` + } + } + }, done); + }); + + // TODO: fixit + it.skip('should not have access to __module_obj', function (done) { + context.execute(` + var assert = require('assert'); + try { + const val = pm.require('mod1'); + throw new Error('should not reach here'); + } + catch (e) { + assert.strictEqual(e.message, "__module_obj is not defined"); + } + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + module.exports = __module_obj; // __module_obj should not be defined + ` + } + } + }, done); + }); + + it('should allow async operations', function (done) { + const errorSpy = sinon.stub(); + + context.on('execution.error', errorSpy); + context.execute(` + const assert = require('assert'); + const mod1 = pm.require('mod1'); + + assert.strictEqual(mod1.a, 123); + assert.strictEqual(mod1.b, undefined); // should not be updated yet + + setTimeout(() => { + assert.strictEqual(mod1.a, 123); + assert.strictEqual(mod1.b, 456); + }, 10); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + module.exports.a = 123; + setTimeout(function () { + module.exports.b = 456; + }, 10); + ` + } + } + }, function (err) { + if (err) { + return done(err); + } + + expect(errorSpy, 'there was an error in the script').to.not.have.been.called; + done(); + }); + }); + + it('should catch errors in async code', function (done) { + const errorSpy = sinon.stub(); + + context.on('execution.error', errorSpy); + context.execute(` + pm.require('mod1'); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + setTimeout(function () { + pm.require('mod2'); + }, 10); + ` + }, + mod2: { + data: ` + setTimeout(function () { + throw new Error('my async error'); + }, 10); + ` + } + } + }, function (err) { + if (err) { + return done(err); + } + + expect(errorSpy, 'there was no error in the script').to.have.been.calledOnce; + expect(errorSpy.firstCall.args[1]).to.have.property('message', 'my async error'); + done(); + }); + }); + + it('should keep the references for the locals instead of values', function (done) { + const errorSpy = sinon.stub(); + + context.on('execution.error', errorSpy); + context.execute(` + const assert = require('assert'); + + a = 1; + b = 1; + obj = { + a: 1, + b: 1 + }; + + pm.require('mod1'); + pm.require('mod2'); + + assert(a === 2); + assert(obj.a === 2); + + setTimeout(function () { + assert(b === 2); + assert(obj.b === 2); + }, 3); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + const assert = require('assert'); + + assert(a === 1); + assert(obj.a === 1); + + a = 2; + obj.a = 2; + + setTimeout(function () { + assert(b === 1); + assert(obj.b === 1); + + b = 2; + obj.b = 2; + }, 1); + ` + }, + mod2: { + data: ` + const assert = require('assert'); + + assert.strictEqual(obj.a, 2, 'sync variables by reference not updated'); + assert.strictEqual(a, 2, 'sync variables by value not updated'); + + setTimeout(function () { + assert.strictEqual(obj.b, 2, 'async variables by reference not updated'); + assert.strictEqual(b, 2, 'async variables by value not updated'); + }, 2); + ` + } + } + }, function (err) { + if (err) { + return done(err); + } + + expect(errorSpy).to.not.have.been.called; + done(); + }); + }); + + it('should make "module" available in the required file', function (done) { + context.execute(` + pm.require('mod1'); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + var assert = require('assert'); + assert.ok(module); + assert.ok(module.exports); + assert.strictEqual(module.exports, exports); + assert.strictEqual(module.id, 'mod1'); + ` + } + } + }, done); + }); + + it('should not have access to cache', function (done) { + context.execute(` + var assert = require('assert'); + var mod1 = pm.require('mod1'); + + assert.strictEqual(pm.require.cache, undefined); + `, { + context: sampleContextData, + resolvedPackages: { + mod1: { + data: ` + module.exports = { a: 123 }; + ` + } + } + }, done); + }); +}); diff --git a/types/index.d.ts b/types/index.d.ts index 11ab88fb..6eb931e8 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for postman-sandbox 4.3.0 +// Type definitions for postman-sandbox 4.4.0 // Project: https://github.com/postmanlabs/postman-sandbox // Definitions by: PostmanLabs // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -86,17 +86,82 @@ declare type Return = { nextRequest: any; }; +/** + * Cache of all files that are available to be required. + */ +declare type FileCache = { + [key: string]: { data: string; } | { error: string; }; +}; + +/** + * @param fileCache - fileCache + */ +declare class PostmanRequireStore { + constructor(fileCache: FileCache); + /** + * Check if the file is available in the cache. + * @param path - path + */ + hasFile(path: string): boolean; + /** + * Get the file from the cache. + * @param path - path + * @returns - file + */ + getFile(path: string): any | undefined; + /** + * Get the resolved path for the file. + * @param path - path + * @returns - resolved path + */ + getResolvedPath(path: string): string | undefined; + /** + * Get the file data. + * @param path - path + */ + getFileData(path: string): string | undefined; + /** + * Check if the file has an error. + * @param path - path + */ + hasError(path: string): boolean; + /** + * Get the file error. + * @param path - path + */ + getFileError(path: string): string | undefined; +} + +/** + * @example + * const fileCache = { + * 'path/to/file.js': { + * data: 'module.exports = { foo: "bar" };' + * } + * }; + * + * const postmanRequire = createPostmanRequire(fileCache, scope); + * + * const module = postmanRequire('path/to/file.js'); + * console.log(module.foo); // bar + * @param fileCache - fileCache + * @param scope - scope + * @returns - postmanRequire + */ +declare function createPostmanRequire(fileCache: FileCache, scope: any): (...params: any[]) => any; + /** * @param execution - execution context * @param onRequest - callback to execute when pm.sendRequest() called * @param onSkipRequest - callback to execute when pm.execution.skipRequest() called * @param onAssertion - callback to execute when pm.expect() called * @param cookieStore - cookie store + * @param requireFn - requireFn * @param [options] - options * @param [options.disabledAPIs] - list of disabled APIs */ declare class Postman { - constructor(execution: Execution, onRequest: (...params: any[]) => any, onSkipRequest: (...params: any[]) => any, onAssertion: (...params: any[]) => any, cookieStore: any, options?: { + constructor(execution: Execution, onRequest: (...params: any[]) => any, onSkipRequest: (...params: any[]) => any, onAssertion: (...params: any[]) => any, cookieStore: any, requireFn: (...params: any[]) => any, options?: { disabledAPIs?: string[]; }); /** @@ -140,6 +205,12 @@ declare class Postman { * Exposes handlers to control or access execution state */ execution: Execution; + /** + * Imports a package in the script. + * @param name - name of the module + * @returns - exports from the module + */ + require(name: string): any; expect: Chai.ExpectStatic; } diff --git a/types/sandbox/prerequest.d.ts b/types/sandbox/prerequest.d.ts index af7bf282..d8946e5a 100644 --- a/types/sandbox/prerequest.d.ts +++ b/types/sandbox/prerequest.d.ts @@ -1,4 +1,4 @@ -// Type definitions for postman-sandbox 4.3.0 +// Type definitions for postman-sandbox 4.4.0 // Project: https://github.com/postmanlabs/postman-sandbox // Definitions by: PostmanLabs // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -20,11 +20,12 @@ declare interface PostmanLegacy { * @param onSkipRequest - callback to execute when pm.execution.skipRequest() called * @param onAssertion - callback to execute when pm.expect() called * @param cookieStore - cookie store + * @param requireFn - requireFn * @param [options] - options * @param [options.disabledAPIs] - list of disabled APIs */ declare class Postman { - constructor(execution: Execution, onRequest: (...params: any[]) => any, onSkipRequest: (...params: any[]) => any, onAssertion: (...params: any[]) => any, cookieStore: any, options?: { + constructor(execution: Execution, onRequest: (...params: any[]) => any, onSkipRequest: (...params: any[]) => any, onAssertion: (...params: any[]) => any, cookieStore: any, requireFn: (...params: any[]) => any, options?: { disabledAPIs?: string[]; }); /** @@ -63,6 +64,12 @@ declare class Postman { * Exposes handlers to control or access execution state */ execution: Execution; + /** + * Imports a package in the script. + * @param name - name of the module + * @returns - exports from the module + */ + require(name: string): any; expect: Chai.ExpectStatic; } diff --git a/types/sandbox/test.d.ts b/types/sandbox/test.d.ts index 42924417..16d8177e 100644 --- a/types/sandbox/test.d.ts +++ b/types/sandbox/test.d.ts @@ -1,4 +1,4 @@ -// Type definitions for postman-sandbox 4.3.0 +// Type definitions for postman-sandbox 4.4.0 // Project: https://github.com/postmanlabs/postman-sandbox // Definitions by: PostmanLabs // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -20,11 +20,12 @@ declare interface PostmanLegacy { * @param onSkipRequest - callback to execute when pm.execution.skipRequest() called * @param onAssertion - callback to execute when pm.expect() called * @param cookieStore - cookie store + * @param requireFn - requireFn * @param [options] - options * @param [options.disabledAPIs] - list of disabled APIs */ declare class Postman { - constructor(execution: Execution, onRequest: (...params: any[]) => any, onSkipRequest: (...params: any[]) => any, onAssertion: (...params: any[]) => any, cookieStore: any, options?: { + constructor(execution: Execution, onRequest: (...params: any[]) => any, onSkipRequest: (...params: any[]) => any, onAssertion: (...params: any[]) => any, cookieStore: any, requireFn: (...params: any[]) => any, options?: { disabledAPIs?: string[]; }); /** @@ -69,6 +70,12 @@ declare class Postman { * Exposes handlers to control or access execution state */ execution: Execution; + /** + * Imports a package in the script. + * @param name - name of the module + * @returns - exports from the module + */ + require(name: string): any; expect: Chai.ExpectStatic; } From 4393b9d06edb18b0f4b6b2bea48aa2d2398a7f72 Mon Sep 17 00:00:00 2001 From: Udit Vasu Date: Wed, 28 Feb 2024 15:00:20 +0530 Subject: [PATCH 2/2] Release v4.5.0 --- CHANGELOG.yaml | 3 ++- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.yaml b/CHANGELOG.yaml index bb28e8d4..6a7f4f7c 100644 --- a/CHANGELOG.yaml +++ b/CHANGELOG.yaml @@ -1,4 +1,5 @@ -unreleased: +4.5.0: + date: 2024-02-28 new features: - GH-976 Add `pm.require` API to use packages inside scripts diff --git a/package-lock.json b/package-lock.json index d0cb854a..fe0983ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "postman-sandbox", - "version": "4.4.0", + "version": "4.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index da680202..2e635574 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postman-sandbox", - "version": "4.4.0", + "version": "4.5.0", "description": "Sandbox for Postman Scripts to run in Node.js or browser", "author": "Postman Inc.", "license": "Apache-2.0",