diff --git a/CHANGELOG.md b/CHANGELOG.md index fa4739ae0..a16810bf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,6 +127,14 @@ Cleanup and renaming - Change command `PTBK_NAME` to `PROMPTBOOK_NAME` _(but keep backward compatibility and preserve alias `PTBK`)_ - Rename `runRemoteServer` -> `startRemoteServer` and return `Destroyable` object +## !!! This branch !!! + +- Implement `JavascriptExecutionTools` +- Deprecate `JavascriptEvalExecutionTools` +- Do not allow to use `JavascriptEvalExecutionTools` in node.js environment + +--- + ## Upcomming - Rename "natural" -> "llm" diff --git a/samples/usage/logging/logging.ts.todo b/samples/usage/logging/logging.ts.todo index 5e49213a2..4ff13c86a 100644 --- a/samples/usage/logging/logging.ts.todo +++ b/samples/usage/logging/logging.ts.todo @@ -34,7 +34,7 @@ async function main() { openAiApiKey: process.env.OPENAI_API_KEY, }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, }), ], diff --git a/samples/usage/remote/index.html b/samples/usage/remote/index.html index fa25a662b..5d99a9d32 100644 --- a/samples/usage/remote/index.html +++ b/samples/usage/remote/index.html @@ -52,7 +52,7 @@

esting remote server of PromptBook

clientId, }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, }), ], diff --git a/samples/usage/simple-script/simple-script.js b/samples/usage/simple-script/simple-script.js index 1c1398f83..5df27f8f6 100644 --- a/samples/usage/simple-script/simple-script.js +++ b/samples/usage/simple-script/simple-script.js @@ -1,7 +1,7 @@ #!/usr/bin/env node import { PromptbookLibrary } from '@promptbook/core'; -import { JavascriptEvalExecutionTools } from '@promptbook/execute-javascript'; +import { JavascriptExecutionTools } from '@promptbook/execute-javascript'; import { OpenAiExecutionTools } from '@promptbook/openai'; import { assertsExecutionSuccessful, executionReportJsonToString } from '@promptbook/utils'; import chalk from 'chalk'; @@ -38,7 +38,7 @@ async function main() { openAiApiKey: process.env.OPENAI_API_KEY, }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, }), ], diff --git a/src/execution/plugins/script-execution-tools/custom-function-async.test.ts.test.ts b/src/execution/plugins/script-execution-tools/custom-function-async.test.ts.test.ts index 5931635cf..2f6e4903c 100644 --- a/src/execution/plugins/script-execution-tools/custom-function-async.test.ts.test.ts +++ b/src/execution/plugins/script-execution-tools/custom-function-async.test.ts.test.ts @@ -36,7 +36,7 @@ describe('createPromptbookExecutor + custom async function ', () => { tools: { natural: new MockedEchoNaturalExecutionTools({ isVerbose: true }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, // Note: [🕎] diff --git a/src/execution/plugins/script-execution-tools/custom-function-missing.test.ts b/src/execution/plugins/script-execution-tools/custom-function-missing.test.ts index 8ae55a561..2c051c87f 100644 --- a/src/execution/plugins/script-execution-tools/custom-function-missing.test.ts +++ b/src/execution/plugins/script-execution-tools/custom-function-missing.test.ts @@ -6,7 +6,7 @@ import { assertsExecutionSuccessful } from '../../assertsExecutionSuccessful'; import { createPromptbookExecutor } from '../../createPromptbookExecutor'; import { MockedEchoNaturalExecutionTools } from '../natural-execution-tools/mocked/MockedEchoNaturalExecutionTools'; import { CallbackInterfaceTools } from '../user-interface-execution-tools/callback/CallbackInterfaceTools'; -import { JavascriptEvalExecutionTools } from './javascript/JavascriptEvalExecutionTools'; +import { JavascriptExecutionTools } from './javascript/JavascriptExecutionTools'; describe('createPromptbookExecutor + missing custom function', () => { const promptbook = promptbookStringToJson( @@ -36,7 +36,7 @@ describe('createPromptbookExecutor + missing custom function', () => { tools: { natural: new MockedEchoNaturalExecutionTools({ isVerbose: true }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, // Note: [🕎] diff --git a/src/execution/plugins/script-execution-tools/custom-function-with-dependencies.test.ts b/src/execution/plugins/script-execution-tools/custom-function-with-dependencies.test.ts index dc6464afa..35375a33b 100644 --- a/src/execution/plugins/script-execution-tools/custom-function-with-dependencies.test.ts +++ b/src/execution/plugins/script-execution-tools/custom-function-with-dependencies.test.ts @@ -6,7 +6,7 @@ import { PromptbookString } from '../../../types/PromptbookString'; import { createPromptbookExecutor } from '../../createPromptbookExecutor'; import { MockedEchoNaturalExecutionTools } from '../natural-execution-tools/mocked/MockedEchoNaturalExecutionTools'; import { CallbackInterfaceTools } from '../user-interface-execution-tools/callback/CallbackInterfaceTools'; -import { JavascriptEvalExecutionTools } from './javascript/JavascriptEvalExecutionTools'; +import { JavascriptExecutionTools } from './javascript/JavascriptExecutionTools'; describe('createPromptbookExecutor + custom function with dependencies', () => { const promptbook = promptbookStringToJson( @@ -37,7 +37,7 @@ describe('createPromptbookExecutor + custom function with dependencies', () => { tools: { natural: new MockedEchoNaturalExecutionTools({ isVerbose: true }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, // Note: [🕎] diff --git a/src/execution/plugins/script-execution-tools/custom-function.test.ts b/src/execution/plugins/script-execution-tools/custom-function.test.ts index 10f3ce71b..c0aff58eb 100644 --- a/src/execution/plugins/script-execution-tools/custom-function.test.ts +++ b/src/execution/plugins/script-execution-tools/custom-function.test.ts @@ -5,7 +5,7 @@ import { PromptbookString } from '../../../types/PromptbookString'; import { createPromptbookExecutor } from '../../createPromptbookExecutor'; import { MockedEchoNaturalExecutionTools } from '../natural-execution-tools/mocked/MockedEchoNaturalExecutionTools'; import { CallbackInterfaceTools } from '../user-interface-execution-tools/callback/CallbackInterfaceTools'; -import { JavascriptEvalExecutionTools } from './javascript/JavascriptEvalExecutionTools'; +import { JavascriptExecutionTools } from './javascript/JavascriptExecutionTools'; describe('createPromptbookExecutor + custom function without dependencies', () => { const promptbook = promptbookStringToJson( @@ -35,7 +35,7 @@ describe('createPromptbookExecutor + custom function without dependencies', () = tools: { natural: new MockedEchoNaturalExecutionTools({ isVerbose: true }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, // Note: [🕎] diff --git a/src/execution/plugins/script-execution-tools/javascript/JavascriptEvalExecutionTools.test.ts b/src/execution/plugins/script-execution-tools/javascript/JavascriptEvalExecutionTools.test.ts deleted file mode 100644 index c7fb0da9a..000000000 --- a/src/execution/plugins/script-execution-tools/javascript/JavascriptEvalExecutionTools.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import spaceTrim from 'spacetrim'; -import { JavascriptEvalExecutionTools } from './JavascriptEvalExecutionTools'; - -describe('JavascriptEvalExecutionTools', () => { - const javascriptEvalExecutionTools = new JavascriptEvalExecutionTools({ - isVerbose: true, - // Note: [🕎] Custom functions are tested elsewhere - }); - - it('should evaluate supersimple statement', () => { - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'javascript', - parameters: { - animal: 'cat', - }, - script: 'animal', - }), - ).resolves.toEqual('cat'); - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'javascript', - parameters: { - animal: 'cat', - }, - script: 'return animal', - }), - ).resolves.toEqual('cat'); - }); - - it('should evaluate single statement', () => { - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'javascript', - parameters: { - animal: 'cat', - }, - script: "return animal.split('').reverse().join('-')", - }), - ).resolves.toEqual('t-a-c'); - }); - - it('should evaluate build-in function', () => { - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'javascript', - parameters: { - animal: '"cat"', - }, - script: 'return removeQuotes(animal)', - }), - ).resolves.toEqual('cat'); - - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'javascript', - parameters: { - animal: 'The animal is: "dog"', - }, - script: 'return unwrapResult(animal)', - }), - ).resolves.toEqual('dog'); - }); - - it('should evaluate multiple statements', () => { - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'javascript', - parameters: { - animal: 'cat', - sound: 'meow', - }, - script: spaceTrim(` - const sentence1 = animal + ' makes ' + sound + '.'; - const sentence2 = \`Two \${animal}s makes \${sound} \${sound}.\`; - const sentence3 = \`Three \${animal}s makes \${sound} \${sound} \${sound}.\`; - return spaceTrim(\` - \${sentence1} - \${sentence2} - \${sentence3} - \`); - `), - }), - ).resolves.toEqual( - spaceTrim(` - cat makes meow. - Two cats makes meow meow. - Three cats makes meow meow meow. - `), - ); - }); - - it('should throw error from script', () => { - () => - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'javascript', - parameters: {}, - script: "throw new Error('Some error')", - }), - ).rejects.toThrowError('Some error'); - }); - - it('should evaluate custom function', () => { - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'javascript', - parameters: { - animal: 'cat', - sound: 'meow', - }, - script: spaceTrim(` - function makeSentence(animal, sound) { - return animal + ' makes ' + sound + '.'; - } - return makeSentence(animal, sound); - `), - }), - ).resolves.toEqual('cat makes meow.'); - }); - - it('should fail on python script', () => { - expect( - javascriptEvalExecutionTools.execute({ - scriptLanguage: 'python', - parameters: { - animal: 'cat', - }, - script: spaceTrim(` - print(animal); - `), - }), - ).rejects.toThrowError(/not supported/i); - }); -}); - -/** - * TODO: !! Make shared test between JavascriptEvalExecutionTools and JavascriptExecutionTools to test the same functionality when implemented via vm2 - */ diff --git a/src/execution/plugins/script-execution-tools/javascript/JavascriptEvalExecutionTools.ts b/src/execution/plugins/script-execution-tools/javascript/JavascriptEvalExecutionTools.ts index e2b54e3ae..6cbf7ebbe 100644 --- a/src/execution/plugins/script-execution-tools/javascript/JavascriptEvalExecutionTools.ts +++ b/src/execution/plugins/script-execution-tools/javascript/JavascriptEvalExecutionTools.ts @@ -13,6 +13,7 @@ import { parseKeywordsFromString, } from 'n12'; import { spaceTrim as _spaceTrim } from 'spacetrim'; +import { isRunningInNode } from '../../../../utils/isRunningInWhatever'; import { prettifyMarkdown as _prettifyMarkdown } from '../../../../utils/markdown/prettifyMarkdown'; import { removeEmojis as _removeEmojis } from '../../../../utils/removeEmojis'; import { removeQuotes as _removeQuotes } from '../../../../utils/removeQuotes'; @@ -20,17 +21,24 @@ import { trimCodeBlock as _trimCodeBlock } from '../../../../utils/trimCodeBlock import { trimEndOfCodeBlock as _trimEndOfCodeBlock } from '../../../../utils/trimEndOfCodeBlock'; import { unwrapResult as _unwrapResult } from '../../../../utils/unwrapResult'; import { ScriptExecutionTools, ScriptExecutionToolsExecuteOptions } from '../../../ScriptExecutionTools'; -import { JavascriptExecutionToolsOptions } from './JavascriptExecutionToolsOptions'; +import type { JavascriptExecutionToolsOptions } from './JavascriptExecutionToolsOptions'; import { preserve } from './utils/preserve'; /** * ScriptExecutionTools for JavaScript implemented via eval * + * @deprecated Use `JavascriptExecutionTools` instead * Warning: It is used for testing and mocking * **NOT intended to use in the production** due to its unsafe nature, use `JavascriptExecutionTools` instead. */ export class JavascriptEvalExecutionTools implements ScriptExecutionTools { - public constructor(private readonly options: JavascriptExecutionToolsOptions) {} + + public constructor(private readonly options: JavascriptExecutionToolsOptions) { + if (isRunningInNode()) { + throw new Error(`JavascriptEvalExecutionTools is not intended to be used in Node.js environment`); + } + } + /** * Executes a JavaScript diff --git a/src/execution/plugins/script-execution-tools/javascript/JavascriptExecutionTools.test.ts b/src/execution/plugins/script-execution-tools/javascript/JavascriptExecutionTools.test.ts new file mode 100644 index 000000000..ee92c471f --- /dev/null +++ b/src/execution/plugins/script-execution-tools/javascript/JavascriptExecutionTools.test.ts @@ -0,0 +1,148 @@ +import { describe, expect, it } from '@jest/globals'; +import spaceTrim from 'spacetrim'; +import { JavascriptEvalExecutionTools } from './JavascriptEvalExecutionTools'; +import { JavascriptExecutionTools } from './JavascriptExecutionTools'; + +describe('JavascriptEvalExecutionTools', () => { + const javascriptEvalExecutionTools = new JavascriptEvalExecutionTools({ + isVerbose: true, + // Note: [🕎] Custom functions are tested elsewhere + }); + + const javascriptExecutionTools = new JavascriptExecutionTools({ + isVerbose: true, + // Note: [🕎] Custom functions are tested elsewhere + }); + + for (const [name, executionTools] of Object.entries({ javascriptEvalExecutionTools, javascriptExecutionTools })) { + it(`should evaluate supersimple statement in ${name}`, () => { + expect( + executionTools.execute({ + scriptLanguage: 'javascript', + parameters: { + animal: 'cat', + }, + script: 'animal', + }), + ).resolves.toEqual('cat'); + expect( + executionTools.execute({ + scriptLanguage: 'javascript', + parameters: { + animal: 'cat', + }, + script: 'return animal', + }), + ).resolves.toEqual('cat'); + }); + + it(`should evaluate single statement in ${name}`, () => { + expect( + executionTools.execute({ + scriptLanguage: 'javascript', + parameters: { + animal: 'cat', + }, + script: "return animal.split('').reverse().join('-')", + }), + ).resolves.toEqual('t-a-c'); + }); + + it(`should evaluate build-in function in ${name}`, () => { + expect( + executionTools.execute({ + scriptLanguage: 'javascript', + parameters: { + animal: '"cat"', + }, + script: 'return removeQuotes(animal)', + }), + ).resolves.toEqual('cat'); + + expect( + executionTools.execute({ + scriptLanguage: 'javascript', + parameters: { + animal: 'The animal is: "dog"', + }, + script: 'return unwrapResult(animal)', + }), + ).resolves.toEqual('dog'); + }); + + it(`should evaluate multiple statements in ${name}`, () => { + expect( + executionTools.execute({ + scriptLanguage: 'javascript', + parameters: { + animal: 'cat', + sound: 'meow', + }, + script: spaceTrim(` + const sentence1 = animal + ' makes ' + sound + '.'; + const sentence2 = \`Two \${animal}s makes \${sound} \${sound}.\`; + const sentence3 = \`Three \${animal}s makes \${sound} \${sound} \${sound}.\`; + return spaceTrim(\` + \${sentence1} + \${sentence2} + \${sentence3} + \`); + `), + }), + ).resolves.toEqual( + spaceTrim(` + cat makes meow. + Two cats makes meow meow. + Three cats makes meow meow meow. + `), + ); + }); + + it(`should throw error from script in ${name}`, () => { + () => + expect( + executionTools.execute({ + scriptLanguage: 'javascript', + parameters: {}, + script: "throw new Error('Some error')", + }), + ).rejects.toThrowError('Some error'); + }); + + it(`should evaluate custom function in ${name}`, () => { + expect( + executionTools.execute({ + scriptLanguage: 'javascript', + parameters: { + animal: 'cat', + sound: 'meow', + }, + script: spaceTrim(` + function makeSentence(animal, sound) { + return animal + ' makes ' + sound + '.'; + } + return makeSentence(animal, sound); + `), + }), + ).resolves.toEqual('cat makes meow.'); + }); + + it(`should fail on python script in ${name}`, () => { + expect( + executionTools.execute({ + scriptLanguage: 'python', + parameters: { + animal: 'cat', + }, + script: spaceTrim(` + print(animal); + `), + }), + ).rejects.toThrowError(/not supported/i); + }); + } +}); + +/** + * TODO: !! Make shared test between JavascriptEvalExecutionTools and JavascriptExecutionTools to test the same functionality when implemented via vm2 + */ diff --git a/src/execution/plugins/script-execution-tools/postprocessing.test.ts b/src/execution/plugins/script-execution-tools/postprocessing.test.ts index 5d6964b5a..a38f4bb9b 100644 --- a/src/execution/plugins/script-execution-tools/postprocessing.test.ts +++ b/src/execution/plugins/script-execution-tools/postprocessing.test.ts @@ -5,7 +5,7 @@ import { PromptbookString } from '../../../types/PromptbookString'; import { createPromptbookExecutor } from '../../createPromptbookExecutor'; import { MockedEchoNaturalExecutionTools } from '../natural-execution-tools/mocked/MockedEchoNaturalExecutionTools'; import { CallbackInterfaceTools } from '../user-interface-execution-tools/callback/CallbackInterfaceTools'; -import { JavascriptEvalExecutionTools } from './javascript/JavascriptEvalExecutionTools'; +import { JavascriptExecutionTools } from './javascript/JavascriptExecutionTools'; describe('createPromptbookExecutor + postprocessing', () => { const promptbook = promptbookStringToJson( @@ -38,7 +38,7 @@ describe('createPromptbookExecutor + postprocessing', () => { tools: { natural: new MockedEchoNaturalExecutionTools({ isVerbose: true }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, // Note: [🕎] Custom functions are tested elsewhere }), diff --git a/src/execution/plugins/script-execution-tools/script-execution-errors.test.ts b/src/execution/plugins/script-execution-tools/script-execution-errors.test.ts index 171308119..08d01abc2 100644 --- a/src/execution/plugins/script-execution-tools/script-execution-errors.test.ts +++ b/src/execution/plugins/script-execution-tools/script-execution-errors.test.ts @@ -6,7 +6,7 @@ import { assertsExecutionSuccessful } from '../../assertsExecutionSuccessful'; import { createPromptbookExecutor } from '../../createPromptbookExecutor'; import { MockedEchoNaturalExecutionTools } from '../natural-execution-tools/mocked/MockedEchoNaturalExecutionTools'; import { CallbackInterfaceTools } from '../user-interface-execution-tools/callback/CallbackInterfaceTools'; -import { JavascriptEvalExecutionTools } from './javascript/JavascriptEvalExecutionTools'; +import { JavascriptExecutionTools } from './javascript/JavascriptExecutionTools'; describe('createPromptbookExecutor + executing scripts in promptbook', () => { const promptbook = promptbookStringToJson( @@ -37,7 +37,7 @@ describe('createPromptbookExecutor + executing scripts in promptbook', () => { tools: { natural: new MockedEchoNaturalExecutionTools({ isVerbose: true }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, // Note: [🕎] Custom functions are tested elsewhere }), diff --git a/src/execution/plugins/script-execution-tools/script-execution-tools.test.ts b/src/execution/plugins/script-execution-tools/script-execution-tools.test.ts index e7e596d2c..63936bc10 100644 --- a/src/execution/plugins/script-execution-tools/script-execution-tools.test.ts +++ b/src/execution/plugins/script-execution-tools/script-execution-tools.test.ts @@ -6,7 +6,7 @@ import { assertsExecutionSuccessful } from '../../assertsExecutionSuccessful'; import { createPromptbookExecutor } from '../../createPromptbookExecutor'; import { MockedEchoNaturalExecutionTools } from '../natural-execution-tools/mocked/MockedEchoNaturalExecutionTools'; import { CallbackInterfaceTools } from '../user-interface-execution-tools/callback/CallbackInterfaceTools'; -import { JavascriptEvalExecutionTools } from './javascript/JavascriptEvalExecutionTools'; +import { JavascriptExecutionTools } from './javascript/JavascriptEvalExecutionTools'; describe('createPromptbookExecutor + executing scripts in promptbook', () => { const promptbook = promptbookStringToJson( @@ -34,7 +34,7 @@ describe('createPromptbookExecutor + executing scripts in promptbook', () => { tools: { natural: new MockedEchoNaturalExecutionTools({ isVerbose: true }), script: [ - new JavascriptEvalExecutionTools({ + new JavascriptExecutionTools({ isVerbose: true, // Note: [🕎] Custom functions are tested elsewhere }),