From 0bce00c4768d98bf9d63f4a14e428065752227a7 Mon Sep 17 00:00:00 2001 From: James Sumners Date: Wed, 26 Jun 2024 14:41:40 -0400 Subject: [PATCH 1/2] chore: resolves locally referenced package exports --- index.js | 33 +++++++++++++++++++++++++++++++-- package.json | 1 + test/mapped-exports.js | 24 ++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 966b9c9..1aa1210 100644 --- a/index.js +++ b/index.js @@ -226,11 +226,40 @@ function Hook (modules, options, onrequire) { let matchFound = false if (hasWhitelist) { - if (!id.startsWith('.') && modules.includes(id)) { + if (id.startsWith('.') === true && modules.includes(id) === false) { + // Try to resolve a package export relative to the base package. + // If found, and the package export is in the allow list, return + // success. Basically, we take a required module like + // `../dist/foo/bar.cjs` and narrow it down to what is likely present + // in the package exports. + let doWork = true + const parts = id.split('/') + const fileName = parts[parts.length - 1] + parts[parts.length - 1] = path.basename(fileName, path.extname(fileName)) + do { + try { + parts.shift() + if (parts.length === 0) { + // We couldn't find anything, so let the rest of the algorithm + // play out. + doWork = false + continue + } + + const exportName = moduleName + '/' + parts.join('/') + require.resolve(exportName) + moduleName = exportName + if (modules.includes(exportName) === true) { + matchFound = true + } + doWork = false + } catch {} + } while (doWork === true) + } else if (!id.startsWith('.') && modules.includes(id)) { // Not starting with '.' means `id` is identifying a module path, // as opposed to a local file path. (Note: I'm not sure about // absolute paths, but those are handled above.) - // If this `id` is in `modules`, then this could be a match to an + // If this `id` is in `modules`, then this could be a match to a // package "exports" entry point that wouldn't otherwise match below. moduleName = id matchFound = true diff --git a/package.json b/package.json index cf28608..5ee0ba1 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@babel/preset-env": "^7.9.5", "@babel/preset-typescript": "^7.9.0", "@babel/register": "^7.9.0", + "@langchain/core": "^0.1.57", "ipp-printer": "^1.0.0", "patterns": "^1.0.3", "roundround": "^0.2.0", diff --git a/test/mapped-exports.js b/test/mapped-exports.js index d9a302d..b18b52b 100644 --- a/test/mapped-exports.js +++ b/test/mapped-exports.js @@ -42,3 +42,27 @@ test('handles mapped exports: mapped-exports/bar', { skip: nodeSupportsExports } hook.unhook() }) + +test('handles mapped exports: picks up allow listed resolved module', { skip: nodeSupportsExports }, function (t) { + t.plan(3) + + let hookHit = false + const hook = new Hook(['@langchain/core/callbacks/manager'], function (exports, name) { + t.equal(name, '@langchain/core/callbacks/manager', 'hook name matches') + hookHit = true + return exports + }) + + const { Tool } = require('@langchain/core/tools') + const MyTool = class MyTool extends Tool { + _call () { + t.pass('tool was executed successfully') + } + } + + const tool = new MyTool() + tool.call('foo', [() => {}]) + t.equal(hookHit, true, 'hook was hit successfully') + + hook.unhook() +}) From d46e3da359653e004f767f9bc1ce0c61e3d44891 Mon Sep 17 00:00:00 2001 From: James Sumners Date: Wed, 17 Jul 2024 08:01:45 -0400 Subject: [PATCH 2/2] improve skip logic --- test/mapped-exports.js | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/test/mapped-exports.js b/test/mapped-exports.js index b18b52b..148b4ee 100644 --- a/test/mapped-exports.js +++ b/test/mapped-exports.js @@ -6,6 +6,7 @@ const { Hook } = require('../') // Mapped export tests require Node.js >=12.17.0 for "exports" support in package.json. const nodeSupportsExports = semver.lt(process.version, '12.17.0') +const node18AndAbove = semver.gte(process.version, '18.0.0') test('handles mapped exports: mapped-exports/foo', { skip: nodeSupportsExports }, function (t) { t.plan(2) @@ -43,26 +44,30 @@ test('handles mapped exports: mapped-exports/bar', { skip: nodeSupportsExports } hook.unhook() }) -test('handles mapped exports: picks up allow listed resolved module', { skip: nodeSupportsExports }, function (t) { - t.plan(3) +test( + 'handles mapped exports: picks up allow listed resolved module', + { skip: nodeSupportsExports || node18AndAbove === false }, + function (t) { + t.plan(3) - let hookHit = false - const hook = new Hook(['@langchain/core/callbacks/manager'], function (exports, name) { - t.equal(name, '@langchain/core/callbacks/manager', 'hook name matches') - hookHit = true - return exports - }) + let hookHit = false + const hook = new Hook(['@langchain/core/callbacks/manager'], function (exports, name) { + t.equal(name, '@langchain/core/callbacks/manager', 'hook name matches') + hookHit = true + return exports + }) - const { Tool } = require('@langchain/core/tools') - const MyTool = class MyTool extends Tool { - _call () { - t.pass('tool was executed successfully') + const { Tool } = require('@langchain/core/tools') + const MyTool = class MyTool extends Tool { + _call () { + t.pass('tool was executed successfully') + } } - } - const tool = new MyTool() - tool.call('foo', [() => {}]) - t.equal(hookHit, true, 'hook was hit successfully') + const tool = new MyTool() + tool.call('foo', [() => {}]) + t.equal(hookHit, true, 'hook was hit successfully') - hook.unhook() -}) + hook.unhook() + } +)