From 39e5039ff5d9cef12bbad89dbb8237db3109d05f Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 19 Jun 2024 09:35:26 -0700 Subject: [PATCH 01/49] Upgrade package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29ebc6b..36e8c7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.5", + "version": "0.0.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.5", + "version": "0.0.6", "license": "MIT", "dependencies": { "@typescript-eslint/eslint-plugin": "^6.0.0", From e1cd602c4db89fde8b996aa7f62743fd2bfc10d0 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 19 Jun 2024 10:42:47 -0700 Subject: [PATCH 02/49] Remake this to a Typescript project. --- dbos-rules.test.js => dbos-rules.test.ts | 4 +++- dbos-rules.js => dbos-rules.ts | 13 ++++++------ package-lock.json | 26 ++++++++++++++++++------ package.json | 14 +++++++------ tsconfig.json | 25 +++++++++++++++++++++++ 5 files changed, 62 insertions(+), 20 deletions(-) rename dbos-rules.test.js => dbos-rules.test.ts (98%) rename dbos-rules.js => dbos-rules.ts (95%) create mode 100644 tsconfig.json diff --git a/dbos-rules.test.js b/dbos-rules.test.ts similarity index 98% rename from dbos-rules.test.js rename to dbos-rules.test.ts index 2d20958..57412b0 100644 --- a/dbos-rules.test.js +++ b/dbos-rules.test.ts @@ -58,12 +58,14 @@ ruleTester.run( code: "const foo = bcrypt.hash('xxx', 10);", //output: 'const foo = *NEED SUGGESTION*;', errors: 1, - }], + }] + /* invalid: [{ code: "const foo = bcrypt.compare('xxx', pass);", //output: 'const foo = *NEED SUGGESTION*;', errors: 1, }], + */ } ); diff --git a/dbos-rules.js b/dbos-rules.ts similarity index 95% rename from dbos-rules.js rename to dbos-rules.ts index fa6b168..6e570a7 100644 --- a/dbos-rules.js +++ b/dbos-rules.ts @@ -82,9 +82,9 @@ module.exports = { }, schema: [], }, - create: function (context) { + create: function (context: any) { return { - CallExpression(node) { + CallExpression(node: any) { //console.log(node.callee.type+JSON.stringify(node)); if (node.callee.type === 'MemberExpression' && node.callee.object.name === 'bcrypt' && @@ -108,9 +108,9 @@ module.exports = { }, schema: [], }, - create: function (context) { + create: function (context: any) { return { - CallExpression(node) { + CallExpression(node: any) { //console.log(node.callee.type+JSON.stringify(node)); if (node.callee.type === 'MemberExpression' && node.callee.object.name === 'Math' && @@ -142,9 +142,9 @@ module.exports = { }, schema: [], }, - create: function (context) { + create: function (context: any) { return { - NewExpression(node) { + NewExpression(node: any) { if (node.callee.name === 'Date') { context.report({ node: node, @@ -167,4 +167,3 @@ module.exports = { dbosExtendedConfig: extConfig, } }; - diff --git a/package-lock.json b/package-lock.json index 36e8c7d..3728f69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,13 @@ "version": "0.0.6", "license": "MIT", "dependencies": { + "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.10.0", - "eslint": "^8.56.0", + "eslint": ">=8.0.0", "eslint-plugin-no-secrets": "^0.8.9", - "eslint-plugin-security": "^2.1.0" + "eslint-plugin-security": "^2.1.0", + "typescript": "^5.4.5" }, "peerDependencies": { "eslint": ">=8.0.0" @@ -146,6 +148,14 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "node_modules/@types/node": { + "version": "20.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", + "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/semver": { "version": "7.5.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", @@ -1440,10 +1450,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "peer": true, + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1452,6 +1461,11 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index 8566440..dbdf278 100644 --- a/package.json +++ b/package.json @@ -7,20 +7,22 @@ "type": "git", "url": "https://github.com/dbos-inc/eslint-plugin" }, - "main": "dbos-rules.js", + "main": "dbos-rules.ts", "homepage": "https://docs.dbos.dev/", "scripts": { - "test": "node dbos-rules.test.js" + "test": "tsc && node dist/dbos-rules.test.js" }, "peerDependencies": { "eslint": ">=8.0.0" }, "dependencies": { - "eslint": "^8.56.0", - "eslint-plugin-security": "^2.1.0", - "eslint-plugin-no-secrets": "^0.8.9", + "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.10.0" + "@typescript-eslint/parser": "^6.10.0", + "eslint": ">=8.0.0", + "eslint-plugin-no-secrets": "^0.8.9", + "eslint-plugin-security": "^2.1.0", + "typescript": "^5.4.5" }, "keywords": [ "eslint", diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..729300e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +/* Visit https://aka.ms/tsconfig to read more about this file */ +{ + "compilerOptions": { + "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + "module": "commonjs", /* Specify what module code is generated. */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declarationMap": true, /* Create sourcemaps for d.ts files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "newLine": "lf", /* Set the newline character for emitting files. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "strict": true, /* Enable all strict type-checking options. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": [ /* Specifies an array of filenames or patterns to include in the program. */ + "dbos-rules.ts", + "dbos-rules.test.ts" + ], + "exclude": [ + "dist" + ] +} From 0910f5bbc8832298e5e351dd9b2da9db6fc89557 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 19 Jun 2024 10:44:30 -0700 Subject: [PATCH 03/49] Upgrade the minor version of package.json --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3728f69..93f805d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.6", + "version": "0.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.6", + "version": "0.0.7", "license": "MIT", "dependencies": { "@types/node": "^20.14.6", diff --git a/package.json b/package.json index dbdf278..67d699b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.6", + "version": "0.0.7", "description": "eslint plugin for DBOS SDK", "license": "MIT", "repository": { From 6c530ea7f50ea1abb1680bd82cefa9df3320d909 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 19 Jun 2024 10:51:10 -0700 Subject: [PATCH 04/49] Add a rule for building the project --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 67d699b..dd2da0a 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "main": "dbos-rules.ts", "homepage": "https://docs.dbos.dev/", "scripts": { + "build": "tsc", "test": "tsc && node dist/dbos-rules.test.js" }, "peerDependencies": { From 6195c79a96e00fca07c3b5e11f0b08f857fa6b42 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 19 Jun 2024 10:57:56 -0700 Subject: [PATCH 05/49] Change the main file to a js file (and include a field for types there as well) --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index dd2da0a..f97479c 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "type": "git", "url": "https://github.com/dbos-inc/eslint-plugin" }, - "main": "dbos-rules.ts", + "main": "dist/dbos-rules.js", + "types": "dist/dbos-rules.d.ts", "homepage": "https://docs.dbos.dev/", "scripts": { "build": "tsc", From 644c43f7674e179b4e65593b3aa812f0b63fe255 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 19 Jun 2024 10:59:48 -0700 Subject: [PATCH 06/49] Remove a build rule --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index f97479c..596e472 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "types": "dist/dbos-rules.d.ts", "homepage": "https://docs.dbos.dev/", "scripts": { - "build": "tsc", "test": "tsc && node dist/dbos-rules.test.js" }, "peerDependencies": { From e372ffce9aa83fdd1f4002f1a4370f68ca16d252 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 19 Jun 2024 11:42:37 -0700 Subject: [PATCH 07/49] Change a version string --- dbos-rules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 6e570a7..6543b68 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -70,7 +70,7 @@ const extConfig = module.exports = { meta: { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.6", + "version": "0.0.7", }, rules: { 'detect-native-code': { From 2c46da2798a3a2f5b0c02ecf9f664545d25ad433 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Thu, 20 Jun 2024 13:46:55 -0700 Subject: [PATCH 08/49] Changes: - Replace Chuck's old rules with my new `ts-morph`-based rules - Comments out the tests for now (will add them back later) --- dbos-rules.test.ts | 9 +- dbos-rules.ts | 414 ++++++++++++++++++++++++++++++++------------- package-lock.json | 267 ++++++++++++++++++++++++----- package.json | 2 + 4 files changed, 534 insertions(+), 158 deletions(-) diff --git a/dbos-rules.test.ts b/dbos-rules.test.ts index 57412b0..598c494 100644 --- a/dbos-rules.test.ts +++ b/dbos-rules.test.ts @@ -1,11 +1,14 @@ const {RuleTester} = require("eslint"); -const ruleUnderTest = require("./dbos-rules.js"); +const ruleUnderTest = require("./dbos-rules"); const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); +// TODO: get the tests to start running again (they should be able to pass, but something's up with `parserServices`...) + // Throws error if the tests in ruleTester.run() do not pass +/* ruleTester.run( "detect-nondeterministic-calls", // rule name ruleUnderTest.rules['detect-nondeterministic-calls'], // rule code @@ -59,15 +62,13 @@ ruleTester.run( //output: 'const foo = *NEED SUGGESTION*;', errors: 1, }] - /* invalid: [{ code: "const foo = bcrypt.compare('xxx', pass);", //output: 'const foo = *NEED SUGGESTION*;', errors: 1, }], - */ } ); - +*/ console.log("All tests passed!"); diff --git a/dbos-rules.ts b/dbos-rules.ts index 6543b68..1e377cd 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -1,40 +1,294 @@ -const tslintPlugin = require("@typescript-eslint/eslint-plugin"); +import { TypeChecker } from "typescript"; +import * as tslintPlugin from "@typescript-eslint/eslint-plugin"; +import { ESLintUtils, ParserServicesWithTypeInformation } from "@typescript-eslint/utils"; + +import { + createWrappedNode, Node, Expression, FunctionDeclaration, + ConstructorDeclaration, ClassDeclaration, MethodDeclaration +} from "ts-morph"; + const secPlugin = require("eslint-plugin-security"); const noSecrets = require("eslint-plugin-no-secrets"); -const baseConfig = -{ +//////////////////////////////////////////////////////////////////////////////////////////////////// Here is my `ts-morph` linting code: + +////////// These are some shared types and values used throughout the code + +// TODO: support `FunctionExpression` and `ArrowFunction` too +type FunctionOrMethod = FunctionDeclaration | MethodDeclaration | ConstructorDeclaration; + +// This returns `undefined` if there is no error message to emit +type DetChecker = (node: Node, fn: FunctionOrMethod, isLocal: (name: string) => boolean) => string | undefined; + +// TODO: stop this globalness (make some class, perhaps, and include some methods with these as internal fields?) +let globalEslintContext: any | undefined = undefined; +let globalParserServices: ParserServicesWithTypeInformation | undefined = undefined; +let globalTypeChecker: TypeChecker | undefined = undefined; + +const DETERMINISTIC_DECORATOR_NAMES = new Set(["Workflow", "Transaction"]); +const TYPES_ALLOWED_TO_AWAIT_WITH = new Set(["WorkflowContext", "TransactionContext"]); + +////////// These are some utility functions + +// This reduces `f.x.y.z` or `f.y().z.w()` into `f` (the leftmost term). This term need not be an identifier. +function reduceExprToLeftmostTerm(node: Expression): Node { + while (Node.isPropertyAccessExpression(node) || Node.isCallExpression(node)) { + node = node.getExpression(); + } + + return node; +} + +function evaluateClassForDeterminism(theClass: ClassDeclaration) { + theClass.getConstructors().forEach(evaluateFunctionForDeterminism); + theClass.getMethods().forEach(evaluateFunctionForDeterminism); +} + +function functionShouldBeDeterministic(fnDecl: FunctionOrMethod): boolean { + return fnDecl.getModifiers().some((modifier) => + Node.isDecorator(modifier) && DETERMINISTIC_DECORATOR_NAMES.has(modifier.getName()) + ); +} + +// Bijectivity is preseved for TSMorph <-> TSC <-> ESTree, as far as I can tell! +function makeTsMorphNode(eslintNode: any): Node { + const compilerNode = globalParserServices!.esTreeNodeToTSNodeMap.get(eslintNode); + + const options = { // TODO: should I pass some compiler options in too, and if so, how? + compilerOptions: undefined, sourceFile: compilerNode.getSourceFile(), typeChecker: globalTypeChecker + }; + + return createWrappedNode(compilerNode, options); +} + +function makeEslintNode(tsMorphNode: Node): any { + const compilerNode = tsMorphNode.compilerNode; + return globalParserServices!.tsNodeToESTreeNodeMap.get(compilerNode); +} + +function getTypeForTsMorphNode(tsMorphNode: Node): string { + /* We need to use the typechecker to check the type, instead of `expr.getType()`, + since type information is lost when creating `ts-morph` nodes from Typescript compiler + nodes, which in turn come from ESTree nodes (which are the nodes that ESLint uses + for its AST). */ + + const type = globalTypeChecker!.getTypeAtLocation(tsMorphNode.compilerNode); + const name = type.getSymbol()?.getName(); + + if (name === undefined) { + throw new Error("Unable to extract a type from the TSMorph node!"); + } + else { + return name; + } +} + +////////// These functions are the determinism heuristics that I've written + +const mutatesGlobalVariable: DetChecker = (node, _fn, isLocal) => { + if (Node.isExpressionStatement(node)) { + const subexpr = node.getExpression(); + + if (Node.isBinaryExpression(subexpr)) { + const lhs = reduceExprToLeftmostTerm(subexpr.getLeft()); + + if (Node.isIdentifier(lhs) && !isLocal(lhs.getText())) { + return "This is a global modification relative to the workflow/transaction declaration."; + } + + /* TODO: warn about these types of assignment too: `[a, b] = [b, a]`, and `b = [a, a = b][0]`. + Could I solve that by checking for equals signs, and then a variable, or array with variables in it, + on the lefthand side? */ + } + } +} + +/* TODO: should I ban IO functions, like `fetch`, `console.log`, +and mutating global arrays via functions like `push`, etc.? */ +const callsBannedFunction: DetChecker = (node, _fn, _isLocal) => { + const makeDateMessage = (variantEnd: string) => `Calling \`Date${variantEnd}()\` is banned (consider using \`@dbos-inc/communicator-datetime\` for consistency and testability)`; + + const bcryptMessage = "Avoid using `bcrypt`, which contains native code. Instead, use `bcryptjs`. \ +Also, some `bcrypt` functions generate random data and should only be called from communicators" + + const bannedFunctionsWithValidArgCountsAndMessages: Map, string]> = new Map([ + ["Date", [new Set([0]), makeDateMessage("")]], // This covers `new Date()` as well + ["Date.now", [new Set([0]), makeDateMessage(".now")]], + ["Math.random", [new Set([0]), "Avoid calling Math.random() directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`"]], + ["setTimeout", [new Set([1, 2]), "Avoid calling `setTimeout()` directly; it can lead to undesired behavior when debugging"]], + ["bcrypt.hash", [new Set([3]), bcryptMessage]], + ["bcrypt.compare", [new Set([3]), bcryptMessage]] + ]); + + ////////// + + if (Node.isCallExpression(node) || Node.isNewExpression(node)) { + const text = node.getExpression().getText(); // TODO: make this work for cases like `Math. random()`! + const validArgCountsAndMessage = bannedFunctionsWithValidArgCountsAndMessages.get(text); + + if (validArgCountsAndMessage !== undefined) { + const [validArgCounts, customMessage] = validArgCountsAndMessage; + const argCount = node.getArguments().length; + + if (validArgCounts.has(argCount)) { + return customMessage; + } + } + } +} + +const awaitsOnAllowedType: DetChecker = (node, _fn, _isLocal) => { + // TODO: match against `.then` as well (with a promise object preceding it) + if (Node.isAwaitExpression(node)) { + let expr = reduceExprToLeftmostTerm(node.getExpression()); + + // In this case, we are awaiting on a literal value, which doesn't make a ton of sense + if (!Node.isIdentifier(expr)) { + if (!Node.isLiteralExpression(expr)) { + throw new Error("Hm, what could this expression be?"); + } + else { + return; // Don't check literals + } + } + + const typeName = getTypeForTsMorphNode(expr); + + if (!TYPES_ALLOWED_TO_AWAIT_WITH.has(typeName)) { // TODO: test this + return `This function should not await with a leftmost variable of type \`${typeName}\` (allowed types: ${JSON.stringify(TYPES_ALLOWED_TO_AWAIT_WITH)})`; + } + } +} + +////////// This is the main function that recurs on the `ts-morph` AST + +function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { + const body = fn.getBody(); + + if (body === undefined) { + throw new Error("When would a function not have a body?"); + } + + const stack: Set[] = [new Set()]; + const getCurrentFrame = () => stack[stack.length - 1]; + const pushFrame = () => stack.push(new Set()); + const popFrame = () => stack.pop(); + const isLocal = (name: string) => stack.some((frame) => frame.has(name)); + + const detCheckers: DetChecker[] = [mutatesGlobalVariable, callsBannedFunction, awaitsOnAllowedType]; + + function checkNodeForGlobalVarUsage(node: Node) { + const locals = getCurrentFrame(); + + if (Node.isClassDeclaration(node)) { + evaluateClassForDeterminism(node); + return; + } + else if (Node.isFunctionDeclaration(node)) { // || Node.isArrowFunction(node)) { + /* Not checking if this function should be deterministic + strictly, since it might have nondeterministic subfunctions */ + evaluateFunctionForDeterminism(node); + return; + } + else if (Node.isBlock(node)) { + pushFrame(); + node.forEachChild(checkNodeForGlobalVarUsage); + popFrame(); + return; + } + else if (Node.isVariableDeclaration(node)) { + locals.add(node.getName()); + } + else if (functionShouldBeDeterministic(fn)) { + + detCheckers.forEach((detChecker) => { + const maybe_error_string = detChecker(node, fn, isLocal); + + if (maybe_error_string !== undefined) { + const correspondingEslintNode = makeEslintNode!(node); + globalEslintContext.report({node: correspondingEslintNode, message: maybe_error_string}); + } + }); + + // console.log(`Not accounted for (det function, ${node.getKindName()})... (${node.print()})`); + } + else { + // console.log("Not accounted for (nondet function)..."); + } + + node.forEachChild(checkNodeForGlobalVarUsage); + } + + body.forEachChild(checkNodeForGlobalVarUsage); +} + +////////// This is the entrypoint for running the determinism analysis with `ts-morph` + +export function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContextParam: any) { + // TODO: should I really do this global setting? It's pretty nasty... + globalEslintContext = eslintContextParam; + globalParserServices = ESLintUtils.getParserServices(globalEslintContext); + globalTypeChecker = globalParserServices.program.getTypeChecker(); + + const tsMorphNode = makeTsMorphNode(estreeNode); + + try { + if (Node.isSourceFile(tsMorphNode)) { + tsMorphNode.getFunctions().forEach(evaluateFunctionForDeterminism); + tsMorphNode.getClasses().forEach(evaluateClassForDeterminism); + } + else { + throw new Error("Was expecting a source file to be passed to `analyzeSourceNodeForDeterminism`!"); + } + } + finally { + // Not keeping these globals around after failure + globalEslintContext = undefined; + globalParserServices = undefined; + globalTypeChecker = undefined; + } +} + +/* Other TODO: +- Take a look at these functions: +isArrowFunction, isFunctionExpression, isObjectBindingPattern, isPropertyAssignment, isQualifiedName +- Check function expressions and arrow functions for mutation (and interfaces?) +- Check for recursive global mutation for expected-to-be-deterministic functions +*/ + +//////////////////////////////////////////////////////////////////////////////////////////////////// Here is the ESLint plugin code (mostly boilerplate): + +const baseConfig = { plugins: [ "@typescript-eslint", "security", - "no-secrets", + "no-secrets" ], - env: { - "node" : true - }, + + env: { "node" : true }, + rules: { "no-eval": "error", "@typescript-eslint/no-implied-eval": "error", "no-console": "error", "security/detect-unsafe-regex": "error", "no-secrets/no-secrets": "error", - "@dbos-inc/detect-nondeterministic-calls": "error", - "@dbos-inc/detect-new-date": "error", - "@dbos-inc/detect-native-code": "error", + "@dbos-inc/unexpected-nondeterminism": "error" }, - "extends": [ - ], + + "extends": [] }; -const recConfig = -{ +const recConfig = { ...baseConfig, - "extends" : [ + + "extends": [ ...baseConfig.extends, "plugin:@typescript-eslint/recommended-requiring-type-checking", "eslint:recommended", - "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended" ], + rules: { ...baseConfig.rules, "@typescript-eslint/no-unnecessary-type-assertion": "off", @@ -45,125 +299,57 @@ const recConfig = "@typescript-eslint/no-floating-promises": "error", "eqeqeq": ["error", "always"], "@typescript-eslint/no-for-in-array": "error", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }] + } +}; - "@typescript-eslint/no-unused-vars": [ - "error", - { "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_" } - ], - }, -} - -const extConfig = -{ +const extConfig = { ...recConfig, - "extends" : [ - ...recConfig.extends, - ], + + "extends" : [...recConfig.extends], + rules: { ...recConfig.rules, - "@typescript-eslint/no-shadow": "error", + "@typescript-eslint/no-shadow": "error" }, -} - +}; module.exports = { meta: { "name": "@dbos-inc/eslint-plugin", "version": "0.0.7", }, + rules: { - 'detect-native-code': { - // Rule configuration for detection of libraries based on native code + "unexpected-nondeterminism": { meta: { - type: 'suggestion', - docs: { - description: 'Detect calls to libraries with native functions like bcrypt, which should be replaced with native JS', - }, - schema: [], - }, - create: function (context: any) { - return { - CallExpression(node: any) { - //console.log(node.callee.type+JSON.stringify(node)); - if (node.callee.type === 'MemberExpression' && - node.callee.object.name === 'bcrypt' && - (node.callee.property.name === 'compare' || node.callee.property.name === 'hash')) - { - context.report({ - node: node, - message: "Avoid using the 'bcrypt' library, which contains native code. Instead, use 'bcryptjs'. Also, note that some bcrypt functions generate random data and should only be called from DBOS communicators, such as `@dbos-inc/communicator-bcrypt`.", - }); - } - }, - }; - }, - }, - 'detect-nondeterministic-calls': { - // Rule configuration for Math.random() detection - meta: { - type: 'suggestion', - docs: { - description: 'Detect calls to nondeterministic functions like Math.random(), which should be called via DBOS rather than directly', - }, - schema: [], - }, - create: function (context: any) { - return { - CallExpression(node: any) { - //console.log(node.callee.type+JSON.stringify(node)); - if (node.callee.type === 'MemberExpression' && - node.callee.object.name === 'Math' && - node.callee.property.name === 'random') - { - context.report({ - node: node, - message: 'Avoid calling Math.random() directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`' - }); - } - if (node.callee.type === 'Identifier' && - node.callee.name === 'setTimeout') - { - context.report({ - node: node, - message: 'Avoid calling setTimeout() directly; it can lead to undesired behavior when debugging.', - }); - } - }, - }; - }, - }, - 'detect-new-date': { - // Rule configuration for new Date() detection - meta: { - type: 'suggestion', - docs: { - description: 'Detect calls to new Date(), which should be called via DBOS rather than directly', - }, - schema: [], + type: "suggestion", + docs: { description: "Detect nondeterminism in cases where functions should act deterministically" }, + schema: [] }, + create: function (context: any) { return { - NewExpression(node: any) { - if (node.callee.name === 'Date') { - context.report({ - node: node, - message: 'Avoid using new Date(); consider using the DBOS SDK functions or `@dbos-inc/communicator-datetime` for consistency and testability.', - }); - } - }, - }; - }, - }, + /* Note: I am working with ts-morph because it has + stronger typing, and it's easier to work with the AST + than ESTree's limited tree navigation. */ + Program(node: any) { + analyzeEstreeNodeForDeterminism(node, context); + } + } + } + } }, + plugins: { - "@typescript-eslint" : tslintPlugin, - "security" : secPlugin, - "no-secrets" : noSecrets, + "@typescript-eslint": tslintPlugin, + "security": secPlugin, + "no-secrets": noSecrets }, + configs: { dbosBaseConfig: baseConfig, dbosRecommendedConfig: recConfig, - dbosExtendedConfig: extConfig, + dbosExtendedConfig: extConfig } }; diff --git a/package-lock.json b/package-lock.json index 93f805d..3c833fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,11 @@ "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.10.0", + "@typescript-eslint/utils": "^7.13.1", "eslint": ">=8.0.0", "eslint-plugin-no-secrets": "^0.8.9", "eslint-plugin-security": "^2.1.0", + "ts-morph": "^22.0.0", "typescript": "^5.4.5" }, "peerDependencies": { @@ -143,6 +145,39 @@ "node": ">= 8" } }, + "node_modules/@ts-morph/common": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", + "integrity": "sha512-m7Lllj9n/S6sOkCkRftpM7L24uvmfXQFedlW/4hENcuJH1HHm9u5EgxZb9uVjQSCGrbBWBkOGgcTxNg36r6ywA==", + "dependencies": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.3", + "mkdirp": "^3.0.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -157,9 +192,9 @@ } }, "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==" + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.17.0", @@ -195,6 +230,30 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", + "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/typescript-estree": "6.17.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, "node_modules/@typescript-eslint/parser": { "version": "6.17.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz", @@ -264,6 +323,30 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", + "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/typescript-estree": "6.17.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, "node_modules/@typescript-eslint/types": { "version": "6.17.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz", @@ -326,27 +409,117 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", - "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", + "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/typescript-estree": "6.17.0", - "semver": "^7.5.4" + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/typescript-estree": "7.13.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", + "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", + "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", + "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", + "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/visitor-keys": { @@ -487,6 +660,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/code-block-writer": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.1.tgz", + "integrity": "sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1064,17 +1242,6 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1106,6 +1273,20 @@ "node": "*" } }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1179,6 +1360,11 @@ "node": ">=6" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1327,12 +1513,9 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -1417,16 +1600,25 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" } }, + "node_modules/ts-morph": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz", + "integrity": "sha512-M9MqFGZREyeb5fTl6gNHKZLqBQA0TjA1lea+CR48R8EBTDuWrNqW6ccC5QvjNR4s6wDumD3LTCjOFSp9iwlzaw==", + "dependencies": { + "@ts-morph/common": "~0.23.0", + "code-block-writer": "^13.0.1" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1493,11 +1685,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 596e472..10ad92a 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,11 @@ "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.10.0", + "@typescript-eslint/utils": "^7.13.1", "eslint": ">=8.0.0", "eslint-plugin-no-secrets": "^0.8.9", "eslint-plugin-security": "^2.1.0", + "ts-morph": "^22.0.0", "typescript": "^5.4.5" }, "keywords": [ From 8e8f9d28db2749b3b776a492e2d2dffe7b2db870 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Thu, 20 Jun 2024 14:40:27 -0700 Subject: [PATCH 09/49] Fix some linting bugs. Tweak some diagnostic messages. --- dbos-rules.ts | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 1e377cd..a79a068 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -3,7 +3,7 @@ import * as tslintPlugin from "@typescript-eslint/eslint-plugin"; import { ESLintUtils, ParserServicesWithTypeInformation } from "@typescript-eslint/utils"; import { - createWrappedNode, Node, Expression, FunctionDeclaration, + createWrappedNode, Node, FunctionDeclaration, ConstructorDeclaration, ClassDeclaration, MethodDeclaration } from "ts-morph"; @@ -30,13 +30,17 @@ const TYPES_ALLOWED_TO_AWAIT_WITH = new Set(["WorkflowContext", "TransactionCont ////////// These are some utility functions -// This reduces `f.x.y.z` or `f.y().z.w()` into `f` (the leftmost term). This term need not be an identifier. -function reduceExprToLeftmostTerm(node: Expression): Node { - while (Node.isPropertyAccessExpression(node) || Node.isCallExpression(node)) { - node = node.getExpression(); - } +// This reduces `f.x.y.z` or `f.y().z.w()` into `f` (the leftmost child). This term need not be an identifier. +function reduceNodeToLeftmostLeaf(node: Node): Node { + while (true) { + let value = node.getFirstChild(); + + if (value === undefined) { + return node; + } - return node; + node = value; + } } function evaluateClassForDeterminism(theClass: ClassDeclaration) { @@ -66,21 +70,15 @@ function makeEslintNode(tsMorphNode: Node): any { return globalParserServices!.tsNodeToESTreeNodeMap.get(compilerNode); } -function getTypeForTsMorphNode(tsMorphNode: Node): string { +// If the returned name is undefined, then there is no associated type (e.g. a never-defined but used variable) +function getTypeNameForTsMorphNode(tsMorphNode: Node): string | undefined { /* We need to use the typechecker to check the type, instead of `expr.getType()`, since type information is lost when creating `ts-morph` nodes from Typescript compiler nodes, which in turn come from ESTree nodes (which are the nodes that ESLint uses for its AST). */ const type = globalTypeChecker!.getTypeAtLocation(tsMorphNode.compilerNode); - const name = type.getSymbol()?.getName(); - - if (name === undefined) { - throw new Error("Unable to extract a type from the TSMorph node!"); - } - else { - return name; - } + return type.getSymbol()?.getName(); } ////////// These functions are the determinism heuristics that I've written @@ -90,7 +88,7 @@ const mutatesGlobalVariable: DetChecker = (node, _fn, isLocal) => { const subexpr = node.getExpression(); if (Node.isBinaryExpression(subexpr)) { - const lhs = reduceExprToLeftmostTerm(subexpr.getLeft()); + const lhs = reduceNodeToLeftmostLeaf(subexpr.getLeft()); if (Node.isIdentifier(lhs) && !isLocal(lhs.getText())) { return "This is a global modification relative to the workflow/transaction declaration."; @@ -140,22 +138,25 @@ Also, some `bcrypt` functions generate random data and should only be called fro const awaitsOnAllowedType: DetChecker = (node, _fn, _isLocal) => { // TODO: match against `.then` as well (with a promise object preceding it) if (Node.isAwaitExpression(node)) { - let expr = reduceExprToLeftmostTerm(node.getExpression()); + let expr = reduceNodeToLeftmostLeaf(node.getExpression()); // In this case, we are awaiting on a literal value, which doesn't make a ton of sense if (!Node.isIdentifier(expr)) { - if (!Node.isLiteralExpression(expr)) { - throw new Error("Hm, what could this expression be?"); + if (Node.isLiteralExpression(expr)) { + return; // Don't check literals (that's invalid code, and that will be handled by something else) } - else { - return; // Don't check literals + else if (!Node.isThisExpression(expr)) { // Don't fail on `this` (since it may have a type too) + throw new Error(`Hm, what could this expression be? (${expr.getKindName()}, ${expr.print()})`); } } - const typeName = getTypeForTsMorphNode(expr); + const typeName = getTypeNameForTsMorphNode(expr); - if (!TYPES_ALLOWED_TO_AWAIT_WITH.has(typeName)) { // TODO: test this - return `This function should not await with a leftmost variable of type \`${typeName}\` (allowed types: ${JSON.stringify(TYPES_ALLOWED_TO_AWAIT_WITH)})`; + /* If the typename is undefined, there's no associated typename (so possibly a + variable is being used that was never defined; that error will be handled elsewhere) */ + if (typeName !== undefined && !TYPES_ALLOWED_TO_AWAIT_WITH.has(typeName)) { + const allowedAsString = [...TYPES_ALLOWED_TO_AWAIT_WITH].join(", "); + return `This function should not await with a leftmost value of type \`${typeName}\` (name = \`${expr.print()}\`, allowed types = {${allowedAsString}})`; } } } From 437f8eb45b26a8fdb03d07fccdd81ea18ae5ee83 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Thu, 20 Jun 2024 14:43:38 -0700 Subject: [PATCH 10/49] Add backticks to a series of typenames --- dbos-rules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index a79a068..53eb374 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -155,7 +155,7 @@ const awaitsOnAllowedType: DetChecker = (node, _fn, _isLocal) => { /* If the typename is undefined, there's no associated typename (so possibly a variable is being used that was never defined; that error will be handled elsewhere) */ if (typeName !== undefined && !TYPES_ALLOWED_TO_AWAIT_WITH.has(typeName)) { - const allowedAsString = [...TYPES_ALLOWED_TO_AWAIT_WITH].join(", "); + const allowedAsString = [...TYPES_ALLOWED_TO_AWAIT_WITH].map((name) => `\`${name}\``).join(", "); return `This function should not await with a leftmost value of type \`${typeName}\` (name = \`${expr.print()}\`, allowed types = {${allowedAsString}})`; } } From fb3cfe566dbf32685a729557bdc47f3d6411d591 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Thu, 20 Jun 2024 15:06:27 -0700 Subject: [PATCH 11/49] Now only checking for determinism in 'Workflow's --- dbos-rules.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 53eb374..2718a57 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -25,8 +25,9 @@ let globalEslintContext: any | undefined = undefined; let globalParserServices: ParserServicesWithTypeInformation | undefined = undefined; let globalTypeChecker: TypeChecker | undefined = undefined; -const DETERMINISTIC_DECORATOR_NAMES = new Set(["Workflow", "Transaction"]); -const TYPES_ALLOWED_TO_AWAIT_WITH = new Set(["WorkflowContext", "TransactionContext"]); +// These included `Transaction` and `TransactionContext` respectively before! +const DETERMINISTIC_DECORATORS = new Set(["Workflow"]); +const TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS = new Set(["WorkflowContext"]); ////////// These are some utility functions @@ -50,7 +51,7 @@ function evaluateClassForDeterminism(theClass: ClassDeclaration) { function functionShouldBeDeterministic(fnDecl: FunctionOrMethod): boolean { return fnDecl.getModifiers().some((modifier) => - Node.isDecorator(modifier) && DETERMINISTIC_DECORATOR_NAMES.has(modifier.getName()) + Node.isDecorator(modifier) && DETERMINISTIC_DECORATORS.has(modifier.getName()) ); } @@ -150,12 +151,15 @@ const awaitsOnAllowedType: DetChecker = (node, _fn, _isLocal) => { } } + /* If the typename is undefined, there's no associated typename (so possibly a + variable is being used that was never defined; that error will be handled elsewhere). */ const typeName = getTypeNameForTsMorphNode(expr); + if (typeName === undefined) return; - /* If the typename is undefined, there's no associated typename (so possibly a - variable is being used that was never defined; that error will be handled elsewhere) */ - if (typeName !== undefined && !TYPES_ALLOWED_TO_AWAIT_WITH.has(typeName)) { - const allowedAsString = [...TYPES_ALLOWED_TO_AWAIT_WITH].map((name) => `\`${name}\``).join(", "); + const validSet = TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS; + + if (!validSet.has(typeName)) { + const allowedAsString = [...validSet].map((name) => `\`${name}\``).join(", "); return `This function should not await with a leftmost value of type \`${typeName}\` (name = \`${expr.print()}\`, allowed types = {${allowedAsString}})`; } } From 411e5758869525a5d7ad385aa1b81203ef7371e1 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Thu, 20 Jun 2024 16:29:57 -0700 Subject: [PATCH 12/49] Wrap all of the global usage in one object (less chance of getting undefined values that way) --- dbos-rules.ts | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 2718a57..f9592ec 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -20,10 +20,7 @@ type FunctionOrMethod = FunctionDeclaration | MethodDeclaration | ConstructorDec // This returns `undefined` if there is no error message to emit type DetChecker = (node: Node, fn: FunctionOrMethod, isLocal: (name: string) => boolean) => string | undefined; -// TODO: stop this globalness (make some class, perhaps, and include some methods with these as internal fields?) -let globalEslintContext: any | undefined = undefined; -let globalParserServices: ParserServicesWithTypeInformation | undefined = undefined; -let globalTypeChecker: TypeChecker | undefined = undefined; +let globalTools: {eslintContext: any, parserServices: ParserServicesWithTypeInformation, typeChecker: TypeChecker} | undefined = undefined; // These included `Transaction` and `TransactionContext` respectively before! const DETERMINISTIC_DECORATORS = new Set(["Workflow"]); @@ -57,10 +54,10 @@ function functionShouldBeDeterministic(fnDecl: FunctionOrMethod): boolean { // Bijectivity is preseved for TSMorph <-> TSC <-> ESTree, as far as I can tell! function makeTsMorphNode(eslintNode: any): Node { - const compilerNode = globalParserServices!.esTreeNodeToTSNodeMap.get(eslintNode); + const compilerNode = globalTools!.parserServices.esTreeNodeToTSNodeMap.get(eslintNode); const options = { // TODO: should I pass some compiler options in too, and if so, how? - compilerOptions: undefined, sourceFile: compilerNode.getSourceFile(), typeChecker: globalTypeChecker + compilerOptions: undefined, sourceFile: compilerNode.getSourceFile(), typeChecker: globalTools!.typeChecker }; return createWrappedNode(compilerNode, options); @@ -68,7 +65,7 @@ function makeTsMorphNode(eslintNode: any): Node { function makeEslintNode(tsMorphNode: Node): any { const compilerNode = tsMorphNode.compilerNode; - return globalParserServices!.tsNodeToESTreeNodeMap.get(compilerNode); + return globalTools!.parserServices.tsNodeToESTreeNodeMap.get(compilerNode); } // If the returned name is undefined, then there is no associated type (e.g. a never-defined but used variable) @@ -78,7 +75,7 @@ function getTypeNameForTsMorphNode(tsMorphNode: Node): string | undefined { nodes, which in turn come from ESTree nodes (which are the nodes that ESLint uses for its AST). */ - const type = globalTypeChecker!.getTypeAtLocation(tsMorphNode.compilerNode); + const type = globalTools!.typeChecker.getTypeAtLocation(tsMorphNode.compilerNode); return type.getSymbol()?.getName(); } @@ -136,7 +133,7 @@ Also, some `bcrypt` functions generate random data and should only be called fro } } -const awaitsOnAllowedType: DetChecker = (node, _fn, _isLocal) => { +const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { // TODO: match against `.then` as well (with a promise object preceding it) if (Node.isAwaitExpression(node)) { let expr = reduceNodeToLeftmostLeaf(node.getExpression()); @@ -180,7 +177,7 @@ function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { const popFrame = () => stack.pop(); const isLocal = (name: string) => stack.some((frame) => frame.has(name)); - const detCheckers: DetChecker[] = [mutatesGlobalVariable, callsBannedFunction, awaitsOnAllowedType]; + const detCheckers: DetChecker[] = [mutatesGlobalVariable, callsBannedFunction, awaitsOnNotAllowedType]; function checkNodeForGlobalVarUsage(node: Node) { const locals = getCurrentFrame(); @@ -211,7 +208,7 @@ function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { if (maybe_error_string !== undefined) { const correspondingEslintNode = makeEslintNode!(node); - globalEslintContext.report({node: correspondingEslintNode, message: maybe_error_string}); + globalTools!.eslintContext.report({node: correspondingEslintNode, message: maybe_error_string}); } }); @@ -229,11 +226,14 @@ function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { ////////// This is the entrypoint for running the determinism analysis with `ts-morph` -export function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContextParam: any) { - // TODO: should I really do this global setting? It's pretty nasty... - globalEslintContext = eslintContextParam; - globalParserServices = ESLintUtils.getParserServices(globalEslintContext); - globalTypeChecker = globalParserServices.program.getTypeChecker(); +export function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContext: any) { + const parserServices = ESLintUtils.getParserServices(eslintContext); + + globalTools = { + eslintContext: eslintContext, + parserServices: parserServices, + typeChecker: parserServices.program.getTypeChecker() + }; const tsMorphNode = makeTsMorphNode(estreeNode); @@ -247,10 +247,8 @@ export function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContextPa } } finally { - // Not keeping these globals around after failure - globalEslintContext = undefined; - globalParserServices = undefined; - globalTypeChecker = undefined; + // Not keeping the tools around after failure + globalTools = undefined; } } From f2dd9f517520fd2ea9561b5e210daa2f89483ea1 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Thu, 20 Jun 2024 16:31:48 -0700 Subject: [PATCH 13/49] Format an object over 3 lines, instead of 1 --- dbos-rules.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index f9592ec..dcc0e83 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -57,7 +57,9 @@ function makeTsMorphNode(eslintNode: any): Node { const compilerNode = globalTools!.parserServices.esTreeNodeToTSNodeMap.get(eslintNode); const options = { // TODO: should I pass some compiler options in too, and if so, how? - compilerOptions: undefined, sourceFile: compilerNode.getSourceFile(), typeChecker: globalTools!.typeChecker + compilerOptions: undefined, + sourceFile: compilerNode.getSourceFile(), + typeChecker: globalTools!.typeChecker }; return createWrappedNode(compilerNode, options); From 87e6eff6f499b54744bd40384248e25cb38934f8 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Thu, 20 Jun 2024 17:17:28 -0700 Subject: [PATCH 14/49] Fix a TODO, and add another --- dbos-rules.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index dcc0e83..e22a129 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -121,7 +121,12 @@ Also, some `bcrypt` functions generate random data and should only be called fro ////////// if (Node.isCallExpression(node) || Node.isNewExpression(node)) { - const text = node.getExpression().getText(); // TODO: make this work for cases like `Math. random()`! + /* Doing this to make syntax like `Math. random` be reduced to `Math.random` + (although this might not work for more complicated function call layouts) */ + const expr = node.getExpression(); + const kids = expr.getChildren(); + const text = (kids.length === 0) ? expr.getText() : kids.map((node) => node.getText()).join(""); + const validArgCountsAndMessage = bannedFunctionsWithValidArgCountsAndMessages.get(text); if (validArgCountsAndMessage !== undefined) { @@ -135,6 +140,7 @@ Also, some `bcrypt` functions generate random data and should only be called fro } } +// TODO: make such awaits acceptable if the rightmost function you're calling is being passed an allowed type const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { // TODO: match against `.then` as well (with a promise object preceding it) if (Node.isAwaitExpression(node)) { From 4e9c66c0cc3ebeb9459d660c4a8ad7b472b3c344 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Fri, 21 Jun 2024 11:19:40 -0700 Subject: [PATCH 15/49] Now not giving nondeterminism warnings when you call a helper function that's passed a WorkflowContext --- dbos-rules.ts | 72 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index e22a129..c7f08da 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -4,7 +4,8 @@ import { ESLintUtils, ParserServicesWithTypeInformation } from "@typescript-esli import { createWrappedNode, Node, FunctionDeclaration, - ConstructorDeclaration, ClassDeclaration, MethodDeclaration + CallExpression, ConstructorDeclaration, ClassDeclaration, + MethodDeclaration } from "ts-morph"; const secPlugin = require("eslint-plugin-security"); @@ -26,6 +27,24 @@ let globalTools: {eslintContext: any, parserServices: ParserServicesWithTypeInfo const DETERMINISTIC_DECORATORS = new Set(["Workflow"]); const TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS = new Set(["WorkflowContext"]); +/* Typically, awaiting on something in a workflow function is not allowed, +since awaiting usually indicates IO, which may be nondeterministic. The only exception +is awaiting on a call hinging on a `WorkflowContext`, e.g. for some code like this +(where `ctxt` is a `WorkflowContext` object): + +`const user = await ctxt.client('users').select("password").where({ username }).first();` + +But there's a common pattern of awaiting upon a function that doesn't have a leftmost `ctxt` there, +but rather upon a function where you just pass that context in as a parameter. Some hypothetical code +for that would look like this: + +`const user = await getUser(ctxt, username);` + +While this seems nondeterministic, it's likely to be deterministic, since the `getUser` function +probably just does the snippet above, but in an abstracted manner (so `getUser` would be a helper function). +So, setting this flag means that determinism warnings will be disabled for awaits in this situation. */ +const ignoreAwaitsForCallsWithAContextParam = true; + ////////// These are some utility functions // This reduces `f.x.y.z` or `f.y().z.w()` into `f` (the leftmost child). This term need not be an identifier. @@ -139,33 +158,54 @@ Also, some `bcrypt` functions generate random data and should only be called fro } } } - -// TODO: make such awaits acceptable if the rightmost function you're calling is being passed an allowed type const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { // TODO: match against `.then` as well (with a promise object preceding it) + + ////////// This is a little utility function used below + + // If the valid type set and arg type set intersect, then there's a valid type in the args + function validTypeExistsInFunctionCallParams(functionCall: CallExpression, validTypes: Set): boolean { + const argTypes = functionCall.getArguments().map(getTypeNameForTsMorphNode); + return argTypes.some((argType) => argType !== undefined && validTypes.has(argType)); + } + + ////////// + if (Node.isAwaitExpression(node)) { - let expr = reduceNodeToLeftmostLeaf(node.getExpression()); + const functionCall = node.getExpression(); + if (!Node.isCallExpression(functionCall)) return; // Wouldn't make sense otherwise + + let lhs = reduceNodeToLeftmostLeaf(functionCall); - // In this case, we are awaiting on a literal value, which doesn't make a ton of sense - if (!Node.isIdentifier(expr)) { - if (Node.isLiteralExpression(expr)) { - return; // Don't check literals (that's invalid code, and that will be handled by something else) + if (!Node.isIdentifier(lhs) && !Node.isThisExpression(lhs)) { // `this` may have a type too + if (Node.isLiteralExpression(lhs)) { + return; // Doesn't make sense to await on literals (that will be reported by something else) } - else if (!Node.isThisExpression(expr)) { // Don't fail on `this` (since it may have a type too) - throw new Error(`Hm, what could this expression be? (${expr.getKindName()}, ${expr.print()})`); + else { // Throwing an error here, since I want to catch what this could be, and maybe revise the code below + throw new Error(`Hm, what could this expression be? Examine... (${lhs.getKindName()}, ${lhs.print()})`); } } - /* If the typename is undefined, there's no associated typename (so possibly a - variable is being used that was never defined; that error will be handled elsewhere). */ - const typeName = getTypeNameForTsMorphNode(expr); + /* If the typename is undefined, there's no associated typename + (so possibly a variable is being used that was never defined; + that error will be handled elsewhere). */ + const typeName = getTypeNameForTsMorphNode(lhs); if (typeName === undefined) return; const validSet = TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS; + const awaitingOnAllowedType = validSet.has(typeName); + + if (!awaitingOnAllowedType) { + /* We should be allowed to await if we call a function that passes + an allowed type, since that probably means that that function is + a helper function which is deterministic and uses our allowed type. */ + if (ignoreAwaitsForCallsWithAContextParam && validTypeExistsInFunctionCallParams(functionCall, validSet)) { + return; + // return `Not warning about this await, since it seems that the called function is being passed an awaitable type, so it's probably a helper function`; + } - if (!validSet.has(typeName)) { const allowedAsString = [...validSet].map((name) => `\`${name}\``).join(", "); - return `This function should not await with a leftmost value of type \`${typeName}\` (name = \`${expr.print()}\`, allowed types = {${allowedAsString}})`; + return `This function should not await with a leftmost value of type \`${typeName}\` (name = \`${lhs.print()}\`, allowed types = {${allowedAsString}})`; } } } @@ -234,7 +274,7 @@ function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { ////////// This is the entrypoint for running the determinism analysis with `ts-morph` -export function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContext: any) { +function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContext: any) { const parserServices = ESLintUtils.getParserServices(eslintContext); globalTools = { From 62db7ff012613f918fd8f7b4842613856f883d51 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Fri, 21 Jun 2024 11:27:48 -0700 Subject: [PATCH 16/49] Add a comment --- dbos-rules.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/dbos-rules.ts b/dbos-rules.ts index c7f08da..531af89 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -165,6 +165,7 @@ const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { // If the valid type set and arg type set intersect, then there's a valid type in the args function validTypeExistsInFunctionCallParams(functionCall: CallExpression, validTypes: Set): boolean { + // I'd like to use `isDisjointFrom` here, but it doesn't seem to be available, for some reason const argTypes = functionCall.getArguments().map(getTypeNameForTsMorphNode); return argTypes.some((argType) => argType !== undefined && validTypes.has(argType)); } From 8f5537b463ea2731f6952671fe6fbb800799371a Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Fri, 21 Jun 2024 12:21:40 -0700 Subject: [PATCH 17/49] Change a var name --- dbos-rules.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 531af89..75b317e 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -149,11 +149,11 @@ Also, some `bcrypt` functions generate random data and should only be called fro const validArgCountsAndMessage = bannedFunctionsWithValidArgCountsAndMessages.get(text); if (validArgCountsAndMessage !== undefined) { - const [validArgCounts, customMessage] = validArgCountsAndMessage; + const [validArgCounts, message] = validArgCountsAndMessage; const argCount = node.getArguments().length; if (validArgCounts.has(argCount)) { - return customMessage; + return message; } } } From 520bb5c9c171e078f649c09e00000db378106842 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Fri, 21 Jun 2024 12:31:57 -0700 Subject: [PATCH 18/49] Bump some version numbers of eslint stuff --- package-lock.json | 273 ++++++++++------------------------------------ package.json | 4 +- 2 files changed, 62 insertions(+), 215 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c833fe..756341d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "MIT", "dependencies": { "@types/node": "^20.14.6", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.10.0", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", "@typescript-eslint/utils": "^7.13.1", "eslint": ">=8.0.0", "eslint-plugin-no-secrets": "^0.8.9", @@ -178,11 +178,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, "node_modules/@types/node": { "version": "20.14.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", @@ -191,38 +186,31 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" - }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz", - "integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==", - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/type-utils": "6.17.0", - "@typescript-eslint/utils": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", - "debug": "^4.3.4", + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", + "integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/type-utils": "7.13.1", + "@typescript-eslint/utils": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -230,50 +218,26 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", - "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/typescript-estree": "6.17.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz", - "integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==", - "dependencies": { - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/typescript-estree": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz", + "integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==", + "dependencies": { + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/typescript-estree": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -282,15 +246,15 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz", - "integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==", + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", + "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", "dependencies": { - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0" + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -298,24 +262,24 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz", - "integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==", + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz", + "integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==", "dependencies": { - "@typescript-eslint/typescript-estree": "6.17.0", - "@typescript-eslint/utils": "6.17.0", + "@typescript-eslint/typescript-estree": "7.13.1", + "@typescript-eslint/utils": "7.13.1", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -323,36 +287,12 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", - "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/typescript-estree": "6.17.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, "node_modules/@typescript-eslint/types": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz", - "integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==", + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", + "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -360,21 +300,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz", - "integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==", + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", + "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", "dependencies": { - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -395,9 +335,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -429,62 +369,7 @@ "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", - "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", - "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", - "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", - "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", - "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/visitor-keys": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", @@ -500,44 +385,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz", - "integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==", - "dependencies": { - "@typescript-eslint/types": "6.17.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -1093,9 +940,9 @@ } }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "engines": { "node": ">= 4" } diff --git a/package.json b/package.json index 10ad92a..0f9f561 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ }, "dependencies": { "@types/node": "^20.14.6", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.10.0", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", "@typescript-eslint/utils": "^7.13.1", "eslint": ">=8.0.0", "eslint-plugin-no-secrets": "^0.8.9", From b2ca30ef06621bf13582dc5aafa9592ad95e10ad Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Mon, 24 Jun 2024 09:45:19 -0700 Subject: [PATCH 19/49] Add a TODO --- dbos-rules.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/dbos-rules.ts b/dbos-rules.ts index 75b317e..29a89ac 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -8,6 +8,7 @@ import { MethodDeclaration } from "ts-morph"; +// TODO: find Typescript variants of these const secPlugin = require("eslint-plugin-security"); const noSecrets = require("eslint-plugin-no-secrets"); From 472a6f1ab2bc7e7c2a862a3587b662d7ad79db81 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Mon, 24 Jun 2024 09:47:07 -0700 Subject: [PATCH 20/49] Add another TODO --- dbos-rules.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 29a89ac..6e13ad8 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -22,7 +22,9 @@ type FunctionOrMethod = FunctionDeclaration | MethodDeclaration | ConstructorDec // This returns `undefined` if there is no error message to emit type DetChecker = (node: Node, fn: FunctionOrMethod, isLocal: (name: string) => boolean) => string | undefined; -let globalTools: {eslintContext: any, parserServices: ParserServicesWithTypeInformation, typeChecker: TypeChecker} | undefined = undefined; +// TODO: figure out how to make the `any` types around here typed +type GlobalTools = {eslintContext: any, parserServices: ParserServicesWithTypeInformation, typeChecker: TypeChecker}; +let globalTools: GlobalTools | undefined = undefined; // These included `Transaction` and `TransactionContext` respectively before! const DETERMINISTIC_DECORATORS = new Set(["Workflow"]); From 1d021395cc9fc7885228528d45bf915567aef5d0 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Mon, 24 Jun 2024 18:43:30 -0700 Subject: [PATCH 21/49] So many changes: - Move the rules + the tests into their own directories - Write some tests (will switch the testing framework to jest later) --- dbos-rules.test.ts | 74 --- package-lock.json | 705 ++++++++++++++++++++++++++++- package.json | 15 +- dbos-rules.ts => src/dbos-rules.ts | 114 +++-- test/test.ts | 142 ++++++ tsconfig.json | 9 +- 6 files changed, 916 insertions(+), 143 deletions(-) delete mode 100644 dbos-rules.test.ts rename dbos-rules.ts => src/dbos-rules.ts (81%) create mode 100644 test/test.ts diff --git a/dbos-rules.test.ts b/dbos-rules.test.ts deleted file mode 100644 index 598c494..0000000 --- a/dbos-rules.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -const {RuleTester} = require("eslint"); -const ruleUnderTest = require("./dbos-rules"); - -const ruleTester = new RuleTester({ - parserOptions: { ecmaVersion: 2015 } -}); - -// TODO: get the tests to start running again (they should be able to pass, but something's up with `parserServices`...) - -// Throws error if the tests in ruleTester.run() do not pass -/* -ruleTester.run( - "detect-nondeterministic-calls", // rule name - ruleUnderTest.rules['detect-nondeterministic-calls'], // rule code - { // checks - // 'valid' checks cases that should pass - valid: [{ - code: "const foo = 'bar';", - }], - // 'invalid' checks cases that should not pass - invalid: [{ - code: "const foo = Math.random();", - //output: 'const foo = *NEED SUGGESTION*;', - errors: 1, - }, - { - code: "setTimeout(1000).then();", - //output: 'const foo = *NEED SUGGESTION*;', - errors: 1, - }], - } -); - -ruleTester.run( - "detect-new-date", // rule name - ruleUnderTest.rules['detect-new-date'], // rule code - { // checks - // 'valid' checks cases that should pass - valid: [{ - code: "const foo = 'bar';", - }], - // 'invalid' checks cases that should not pass - invalid: [{ - code: "const foo = new Date();", - //output: 'const foo = *NEED SUGGESTION*;', - errors: 1, - }], - } -); - -ruleTester.run( - "detect-native-code", // rule name - ruleUnderTest.rules['detect-native-code'], // rule code - { // checks - // 'valid' checks cases that should pass - valid: [{ - code: "const foo = 'bar';", - }], - // 'invalid' checks cases that should not pass - invalid: [{ - code: "const foo = bcrypt.hash('xxx', 10);", - //output: 'const foo = *NEED SUGGESTION*;', - errors: 1, - }] - invalid: [{ - code: "const foo = bcrypt.compare('xxx', pass);", - //output: 'const foo = *NEED SUGGESTION*;', - errors: 1, - }], - } -); -*/ - -console.log("All tests passed!"); diff --git a/package-lock.json b/package-lock.json index 756341d..845c5cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,25 @@ "version": "0.0.7", "license": "MIT", "dependencies": { + "@types/mocha": "^10.0.7", "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", + "@typescript-eslint/rule-tester": "^7.13.1", "@typescript-eslint/utils": "^7.13.1", - "eslint": ">=8.0.0", "eslint-plugin-no-secrets": "^0.8.9", "eslint-plugin-security": "^2.1.0", + "mocha": "^10.4.0", + "ts-mocha": "^10.0.0", "ts-morph": "^22.0.0", "typescript": "^5.4.5" - }, - "peerDependencies": { - "eslint": ">=8.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -57,6 +58,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -79,6 +81,7 @@ "version": "8.56.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -87,6 +90,7 @@ "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "peer": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", @@ -100,6 +104,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "peer": true, "engines": { "node": ">=12.22" }, @@ -111,7 +116,8 @@ "node_modules/@humanwhocodes/object-schema": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "peer": true }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -178,6 +184,17 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "optional": true + }, + "node_modules/@types/mocha": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", + "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==" + }, "node_modules/@types/node": { "version": "20.14.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", @@ -245,6 +262,30 @@ } } }, + "node_modules/@typescript-eslint/rule-tester": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/rule-tester/-/rule-tester-7.13.1.tgz", + "integrity": "sha512-rM55VW1cWHLKys8cKKa2RjRUasB5k3hniuwixZXX28oayk9QCiAhbKTJjkbYAkXjOOyfEN+ReVFTvvTNWGOvDA==", + "dependencies": { + "@typescript-eslint/typescript-estree": "7.13.1", + "@typescript-eslint/utils": "7.13.1", + "ajv": "^6.12.6", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "4.6.2", + "semver": "^7.6.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@eslint/eslintrc": ">=2", + "eslint": "^8.56.0" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", @@ -388,12 +429,14 @@ "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "peer": true }, "node_modules/acorn": { "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -405,6 +448,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -424,6 +468,14 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -446,6 +498,18 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -459,15 +523,35 @@ "node": ">=8" } }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -484,14 +568,36 @@ "node": ">=8" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "peer": true, "engines": { "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -507,6 +613,53 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/code-block-writer": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.1.tgz", @@ -531,12 +684,14 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -562,10 +717,30 @@ } } }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "peer": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "engines": { + "node": ">=0.3.1" + } }, "node_modules/dir-glob": { "version": "3.0.1", @@ -582,6 +757,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -589,6 +765,19 @@ "node": ">=6.0.0" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -604,6 +793,7 @@ "version": "8.56.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -678,6 +868,7 @@ "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -704,6 +895,7 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "peer": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -720,6 +912,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -731,6 +924,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -742,6 +936,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "peer": true, "engines": { "node": ">=4.0" } @@ -750,6 +945,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -793,7 +989,8 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "peer": true }, "node_modules/fastq": { "version": "1.16.0", @@ -807,6 +1004,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "peer": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -840,10 +1038,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -856,17 +1063,40 @@ "node_modules/flatted": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "peer": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -886,6 +1116,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -897,6 +1128,7 @@ "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "peer": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -939,6 +1171,14 @@ "node": ">=8" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -951,6 +1191,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "peer": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -966,6 +1207,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "peer": true, "engines": { "node": ">=0.8.19" } @@ -984,6 +1226,17 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -992,6 +1245,14 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1015,14 +1276,35 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "engines": { "node": ">=8" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "peer": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -1038,7 +1320,8 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "peer": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -1050,10 +1333,23 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -1062,6 +1358,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -1089,6 +1386,26 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1113,6 +1430,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1120,6 +1438,14 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", @@ -1134,6 +1460,97 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mocha": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "8.1.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1144,6 +1561,14 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1156,6 +1581,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "peer": true, "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -1200,6 +1626,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -1224,6 +1651,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -1232,6 +1660,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "peer": true, "engines": { "node": ">=8" } @@ -1259,6 +1688,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -1290,6 +1720,25 @@ } ] }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -1298,10 +1747,19 @@ "regexp-tree": "bin/regexp-tree" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "peer": true, "engines": { "node": ">=4" } @@ -1319,6 +1777,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -1351,6 +1810,25 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", @@ -1370,10 +1848,19 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1385,6 +1872,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "peer": true, "engines": { "node": ">=8" } @@ -1397,6 +1885,36 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -1408,6 +1926,15 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "optional": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1433,7 +1960,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "peer": true }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -1457,6 +1985,26 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-mocha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz", + "integrity": "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==", + "dependencies": { + "ts-node": "7.0.1" + }, + "bin": { + "ts-mocha": "bin/ts-mocha" + }, + "engines": { + "node": ">= 6.X.X" + }, + "optionalDependencies": { + "tsconfig-paths": "^3.5.0" + }, + "peerDependencies": { + "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X" + } + }, "node_modules/ts-morph": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz", @@ -1466,10 +2014,63 @@ "code-block-writer": "^13.0.1" } }, + "node_modules/ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dependencies": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ts-node/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -1481,6 +2082,7 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "peer": true, "engines": { "node": ">=10" }, @@ -1517,6 +2119,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -1527,11 +2130,87 @@ "node": ">= 8" } }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 0f9f561..f06c20f 100644 --- a/package.json +++ b/package.json @@ -7,23 +7,24 @@ "type": "git", "url": "https://github.com/dbos-inc/eslint-plugin" }, - "main": "dist/dbos-rules.js", - "types": "dist/dbos-rules.d.ts", + "main": "dist/src/dbos-rules.js", + "types": "dist/src/dbos-rules.d.ts", "homepage": "https://docs.dbos.dev/", "scripts": { - "test": "tsc && node dist/dbos-rules.test.js" - }, - "peerDependencies": { - "eslint": ">=8.0.0" + "build": "tsc", + "test": "tsc && ts-mocha test/test.ts" }, "dependencies": { + "@types/mocha": "^10.0.7", "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", + "@typescript-eslint/rule-tester": "^7.13.1", "@typescript-eslint/utils": "^7.13.1", - "eslint": ">=8.0.0", "eslint-plugin-no-secrets": "^0.8.9", "eslint-plugin-security": "^2.1.0", + "mocha": "^10.4.0", + "ts-mocha": "^10.0.0", "ts-morph": "^22.0.0", "typescript": "^5.4.5" }, diff --git a/dbos-rules.ts b/src/dbos-rules.ts similarity index 81% rename from dbos-rules.ts rename to src/dbos-rules.ts index 6e13ad8..ed118bc 100644 --- a/dbos-rules.ts +++ b/src/dbos-rules.ts @@ -19,16 +19,42 @@ const noSecrets = require("eslint-plugin-no-secrets"); // TODO: support `FunctionExpression` and `ArrowFunction` too type FunctionOrMethod = FunctionDeclaration | MethodDeclaration | ConstructorDeclaration; -// This returns `undefined` if there is no error message to emit +// This returns `undefined` if there is no error message to emit; otherwise, it returns a key to the `ERROR_MESSAGES` map type DetChecker = (node: Node, fn: FunctionOrMethod, isLocal: (name: string) => boolean) => string | undefined; // TODO: figure out how to make the `any` types around here typed type GlobalTools = {eslintContext: any, parserServices: ParserServicesWithTypeInformation, typeChecker: TypeChecker}; -let globalTools: GlobalTools | undefined = undefined; +let GLOBAL_TOOLS: GlobalTools | undefined = undefined; // These included `Transaction` and `TransactionContext` respectively before! const DETERMINISTIC_DECORATORS = new Set(["Workflow"]); const TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS = new Set(["WorkflowContext"]); +const ERROR_MESSAGES = makeErrorMessageSet(); + +////////// This is the set of error messages that can be emitted + +function makeErrorMessageSet(): Map { + const makeDateMessage = (variantEnd: string) => `Calling \`Date${variantEnd}()\` is banned (consider using \`@dbos-inc/communicator-datetime\` for consistency and testability)`; + + const bcryptMessage = "Avoid using `bcrypt`, which contains native code. Instead, use `bcryptjs`. \ +Also, some `bcrypt` functions generate random data and should only be called from communicators"; + + const validTypeSetString: string = [...TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS].map((name) => `\`${name}\``).join(", "); + + // The keys are the ids, and the values are the messages themselves + return new Map([ + ["globalModification", "This is a global modification relative to the workflow declaration."], + ["awaitingOnNotAllowedType", `This function (expected to be deterministic) should not await with a leftmost value of this type (allowed set: ${validTypeSetString})`], + ["Date", makeDateMessage("")], + ["Date.now", makeDateMessage(".now")], + ["Math.random", "Avoid calling Math.random() directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`"], + ["setTimeout", "Avoid calling `setTimeout()` directly; it can lead to undesired behavior when debugging"], + ["bcrypt.hash", bcryptMessage], + ["bcrypt.compare", bcryptMessage] + ]); +} + +////////// /* Typically, awaiting on something in a workflow function is not allowed, since awaiting usually indicates IO, which may be nondeterministic. The only exception @@ -48,6 +74,12 @@ probably just does the snippet above, but in an abstracted manner (so `getUser` So, setting this flag means that determinism warnings will be disabled for awaits in this situation. */ const ignoreAwaitsForCallsWithAContextParam = true; +/* +TODO (Harry's request): +Peter asked me to add a config setting for @StoredProcedure methods to enable them to run locally. +How hard is it to add a linter rule to always warn the user of this config setting is enabled?` +*/ + ////////// These are some utility functions // This reduces `f.x.y.z` or `f.y().z.w()` into `f` (the leftmost child). This term need not be an identifier. @@ -76,20 +108,13 @@ function functionShouldBeDeterministic(fnDecl: FunctionOrMethod): boolean { // Bijectivity is preseved for TSMorph <-> TSC <-> ESTree, as far as I can tell! function makeTsMorphNode(eslintNode: any): Node { - const compilerNode = globalTools!.parserServices.esTreeNodeToTSNodeMap.get(eslintNode); - - const options = { // TODO: should I pass some compiler options in too, and if so, how? - compilerOptions: undefined, - sourceFile: compilerNode.getSourceFile(), - typeChecker: globalTools!.typeChecker - }; - - return createWrappedNode(compilerNode, options); + const compilerNode = GLOBAL_TOOLS!.parserServices.esTreeNodeToTSNodeMap.get(eslintNode); + return createWrappedNode(compilerNode); } function makeEslintNode(tsMorphNode: Node): any { const compilerNode = tsMorphNode.compilerNode; - return globalTools!.parserServices.tsNodeToESTreeNodeMap.get(compilerNode); + return GLOBAL_TOOLS!.parserServices.tsNodeToESTreeNodeMap.get(compilerNode); } // If the returned name is undefined, then there is no associated type (e.g. a never-defined but used variable) @@ -99,7 +124,7 @@ function getTypeNameForTsMorphNode(tsMorphNode: Node): string | undefined { nodes, which in turn come from ESTree nodes (which are the nodes that ESLint uses for its AST). */ - const type = globalTools!.typeChecker.getTypeAtLocation(tsMorphNode.compilerNode); + const type = GLOBAL_TOOLS!.typeChecker.getTypeAtLocation(tsMorphNode.compilerNode); return type.getSymbol()?.getName(); } @@ -113,7 +138,7 @@ const mutatesGlobalVariable: DetChecker = (node, _fn, isLocal) => { const lhs = reduceNodeToLeftmostLeaf(subexpr.getLeft()); if (Node.isIdentifier(lhs) && !isLocal(lhs.getText())) { - return "This is a global modification relative to the workflow/transaction declaration."; + return "globalModification"; } /* TODO: warn about these types of assignment too: `[a, b] = [b, a]`, and `b = [a, a = b][0]`. @@ -126,18 +151,14 @@ const mutatesGlobalVariable: DetChecker = (node, _fn, isLocal) => { /* TODO: should I ban IO functions, like `fetch`, `console.log`, and mutating global arrays via functions like `push`, etc.? */ const callsBannedFunction: DetChecker = (node, _fn, _isLocal) => { - const makeDateMessage = (variantEnd: string) => `Calling \`Date${variantEnd}()\` is banned (consider using \`@dbos-inc/communicator-datetime\` for consistency and testability)`; - - const bcryptMessage = "Avoid using `bcrypt`, which contains native code. Instead, use `bcryptjs`. \ -Also, some `bcrypt` functions generate random data and should only be called from communicators" - - const bannedFunctionsWithValidArgCountsAndMessages: Map, string]> = new Map([ - ["Date", [new Set([0]), makeDateMessage("")]], // This covers `new Date()` as well - ["Date.now", [new Set([0]), makeDateMessage(".now")]], - ["Math.random", [new Set([0]), "Avoid calling Math.random() directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`"]], - ["setTimeout", [new Set([1, 2]), "Avoid calling `setTimeout()` directly; it can lead to undesired behavior when debugging"]], - ["bcrypt.hash", [new Set([3]), bcryptMessage]], - ["bcrypt.compare", [new Set([3]), bcryptMessage]] + // All of these function names are also keys in `ERROR_MESSAGES` above + const bannedFunctionsWithValidArgCounts: Map> = new Map([ + ["Date", new Set([0])], + ["Date.now", new Set([0])], + ["Math.random", new Set([0])], + ["setTimeout", new Set([1, 2])], + ["bcrypt.hash", new Set([3])], + ["bcrypt.compare", new Set([3])] ]); ////////// @@ -149,14 +170,13 @@ Also, some `bcrypt` functions generate random data and should only be called fro const kids = expr.getChildren(); const text = (kids.length === 0) ? expr.getText() : kids.map((node) => node.getText()).join(""); - const validArgCountsAndMessage = bannedFunctionsWithValidArgCountsAndMessages.get(text); + const validArgCounts = bannedFunctionsWithValidArgCounts.get(text); - if (validArgCountsAndMessage !== undefined) { - const [validArgCounts, message] = validArgCountsAndMessage; + if (validArgCounts !== undefined) { const argCount = node.getArguments().length; if (validArgCounts.has(argCount)) { - return message; + return text; // Returning the function name key } } } @@ -194,7 +214,10 @@ const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { (so possibly a variable is being used that was never defined; that error will be handled elsewhere). */ const typeName = getTypeNameForTsMorphNode(lhs); - if (typeName === undefined) return; + + if (typeName === undefined) { + return; + } const validSet = TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS; const awaitingOnAllowedType = validSet.has(typeName); @@ -204,12 +227,10 @@ const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { an allowed type, since that probably means that that function is a helper function which is deterministic and uses our allowed type. */ if (ignoreAwaitsForCallsWithAContextParam && validTypeExistsInFunctionCallParams(functionCall, validSet)) { - return; - // return `Not warning about this await, since it seems that the called function is being passed an awaitable type, so it's probably a helper function`; + return; } - const allowedAsString = [...validSet].map((name) => `\`${name}\``).join(", "); - return `This function should not await with a leftmost value of type \`${typeName}\` (name = \`${lhs.print()}\`, allowed types = {${allowedAsString}})`; + return "awaitingOnNotAllowedType"; } } } @@ -250,17 +271,18 @@ function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { popFrame(); return; } + // Note: parameters are not considered to be locals here (modifying them is not allowed, currently!) else if (Node.isVariableDeclaration(node)) { locals.add(node.getName()); } else if (functionShouldBeDeterministic(fn)) { detCheckers.forEach((detChecker) => { - const maybe_error_string = detChecker(node, fn, isLocal); + const messageKey = detChecker(node, fn, isLocal); - if (maybe_error_string !== undefined) { + if (messageKey !== undefined) { const correspondingEslintNode = makeEslintNode!(node); - globalTools!.eslintContext.report({node: correspondingEslintNode, message: maybe_error_string}); + GLOBAL_TOOLS!.eslintContext.report({ node: correspondingEslintNode, messageId: messageKey }); } }); @@ -281,7 +303,7 @@ function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContext: any) { const parserServices = ESLintUtils.getParserServices(eslintContext); - globalTools = { + GLOBAL_TOOLS = { eslintContext: eslintContext, parserServices: parserServices, typeChecker: parserServices.program.getTypeChecker() @@ -300,7 +322,7 @@ function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContext: any) { } finally { // Not keeping the tools around after failure - globalTools = undefined; + GLOBAL_TOOLS = undefined; } } @@ -331,13 +353,13 @@ const baseConfig = { "@dbos-inc/unexpected-nondeterminism": "error" }, - "extends": [] + extends: [] }; const recConfig = { ...baseConfig, - "extends": [ + extends: [ ...baseConfig.extends, "plugin:@typescript-eslint/recommended-requiring-type-checking", "eslint:recommended", @@ -361,7 +383,7 @@ const recConfig = { const extConfig = { ...recConfig, - "extends" : [...recConfig.extends], + extends: [...recConfig.extends], rules: { ...recConfig.rules, @@ -371,8 +393,8 @@ const extConfig = { module.exports = { meta: { - "name": "@dbos-inc/eslint-plugin", - "version": "0.0.7", + name: "@dbos-inc/eslint-plugin", + version: "0.0.7" }, rules: { @@ -380,7 +402,7 @@ module.exports = { meta: { type: "suggestion", docs: { description: "Detect nondeterminism in cases where functions should act deterministically" }, - schema: [] + messages: Object.fromEntries(ERROR_MESSAGES) }, create: function (context: any) { diff --git a/test/test.ts b/test/test.ts new file mode 100644 index 0000000..f206442 --- /dev/null +++ b/test/test.ts @@ -0,0 +1,142 @@ +import { after as mochaAfter } from "mocha"; // TODO: switch to `jest` or `vitest` +import { RuleTester } from "@typescript-eslint/rule-tester"; +const rulesUnderTest = require("../dist/src/dbos-rules.js"); // TODO: import my rules normally + +RuleTester.afterAll = mochaAfter; + +////////// + +// https://stackoverflow.com/questions/51851677/how-to-get-argument-types-from-function-in-typescript +type ArgumentTypes = F extends (...args: infer A) => any ? A : never; + +type TestTypes = ArgumentTypes[2]; +type ValidTests = TestTypes["valid"]; +type InvalidTests = TestTypes["invalid"]; + +type TestSet = [string, ValidTests, InvalidTests][]; + +function doTest(title: string, valid: ValidTests, invalid: InvalidTests) { + const ruleName = "unexpected-nondeterminism"; + tester.run(title, rulesUnderTest.rules[ruleName], { valid: valid, invalid: invalid }); +} + +////////// + +const tester = new RuleTester({ + parser: "@typescript-eslint/parser", + parserOptions: { project: "tsconfig.json" }, + defaultFilenames: { ts: "test/test.ts", tsx: "test/this_file_doesnt_exist.tsx" } +}); + +function makeExpectedDetCode(code: string, params: string = "", aboveClass: string = ""): string { + return ` + ${aboveClass} + class Foo { + @Workflow + bar(${params}) { + ${code} + } + } + `; +} + +// TODO: give this a better return type +function makeCaseForModification(numErrors: number, code: string): any { + return { code: code, errors: Array(numErrors).fill({ messageId: "globalModification" }) }; +} + +// TODO: this too +function makeCaseForOkayCall(call: string): any { + return { code: makeExpectedDetCode(`const x = ${call};`) }; +} + +// TODO: this too +function makeCaseForBannedCall(prefix: string, functionName: string, params: string): any { + return { code: makeExpectedDetCode(`const x = ${prefix} ${functionName}(${params});`), errors: [{ messageId: functionName }] } +} + +function makeCaseForOkayAwaitCall(params: string, awaitedUpon: string): any { + const code = makeExpectedDetCode(`const x = await ${awaitedUpon};`, params, "class WorkflowContext {}"); + return { code: code }; +} + +// TODO: this too +function makeCaseForBannedAwaitCall(params: string, awaitedUpon: string): any { + const code = makeExpectedDetCode(`const x = await ${awaitedUpon};`, params, "class FooBar {}"); + return { code: code, errors: [{ messageId: "awaitingOnNotAllowedType" }]}; +} + +const testSet: TestSet = [ + ["global modifications", [], [makeCaseForModification(5, +` +let x = 3; +let y = {a: 1, b: 2}; + +class Foo { + @Workflow + foo() { + x = 4; // Not allowed + y.a += 1; // Not allowed + + let y = {a: 3, b: 4}; // Aliases the global 'y' + y.a = 1; // Not a global modification anymore + } + + bar() { + y.b += 2; + let z = 8; + + class Bar { + @Workflow + w() { + z = 9; // Not allowed + } + } + } + + @Workflow + baz() { + x *= 5; // Not allowed + y.b += y.a; // Not allowed + + function bazbaz() { + x -= 6; + y.b += y.a; + } + } +}`)] + ], + + ["banned functions", + [ + makeCaseForOkayCall("foo()"), + makeCaseForOkayCall("Date('December 17, 1995 03:24:00')"), + makeCaseForOkayCall("new Date('December 17, 1995 03:24:00')") + ], + + [ + makeCaseForBannedCall("", "Date", ""), + makeCaseForBannedCall("new", "Date", ""), + makeCaseForBannedCall("", "Math.random", ""), + makeCaseForBannedCall("", "setTimeout", "a, b"), + makeCaseForBannedCall("", "bcrypt.hash", "a, b, c"), + makeCaseForBannedCall("", "bcrypt.compare", "a, b, c") + ] + ], + + ["allowed awaits", + [ + makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.foo()"), + makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), + makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.client('users').select('password').where({ username }).first();") + ], + + [ + makeCaseForBannedAwaitCall("", "fetch('https://www.google.com')"), + makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), + makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.foo()") + ] + ] +]; + +testSet.forEach((test) => doTest(...test)); diff --git a/tsconfig.json b/tsconfig.json index 729300e..0ddf1e9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,8 @@ "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - "module": "commonjs", /* Specify what module code is generated. */ + "module": "nodenext", /* Specify what module code is generated. */ + "moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */ "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ "declarationMap": true, /* Create sourcemaps for d.ts files. */ "sourceMap": true, /* Create source map files for emitted JavaScript files. */ @@ -15,10 +16,12 @@ "strict": true, /* Enable all strict type-checking options. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, + "include": [ /* Specifies an array of filenames or patterns to include in the program. */ - "dbos-rules.ts", - "dbos-rules.test.ts" + "src/dbos-rules.ts", + "test/test.ts" ], + "exclude": [ "dist" ] From e2ffb5cfeb6f890d2a14d72d9cfcbe54c79e17d7 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Mon, 24 Jun 2024 18:46:41 -0700 Subject: [PATCH 22/49] Remove a type annotation --- src/dbos-rules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dbos-rules.ts b/src/dbos-rules.ts index ed118bc..42c6494 100644 --- a/src/dbos-rules.ts +++ b/src/dbos-rules.ts @@ -39,7 +39,7 @@ function makeErrorMessageSet(): Map { const bcryptMessage = "Avoid using `bcrypt`, which contains native code. Instead, use `bcryptjs`. \ Also, some `bcrypt` functions generate random data and should only be called from communicators"; - const validTypeSetString: string = [...TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS].map((name) => `\`${name}\``).join(", "); + const validTypeSetString = [...TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS].map((name) => `\`${name}\``).join(", "); // The keys are the ids, and the values are the messages themselves return new Map([ From 256442cc770003750466a1ca7a2297334192d241 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 10:22:34 -0700 Subject: [PATCH 23/49] Add more types to everything --- src/dbos-rules.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/dbos-rules.ts b/src/dbos-rules.ts index 42c6494..2fa774d 100644 --- a/src/dbos-rules.ts +++ b/src/dbos-rules.ts @@ -12,6 +12,11 @@ import { const secPlugin = require("eslint-plugin-security"); const noSecrets = require("eslint-plugin-no-secrets"); +// https://stackoverflow.com/questions/51851677/how-to-get-argument-types-from-function-in-typescript +type ArgumentTypes = F extends (...args: infer A) => any ? A : never; +type EslintContext = ArgumentTypes[0]; // TODO: stop using this construct +type EslintNode = any; // TODO: type this + //////////////////////////////////////////////////////////////////////////////////////////////////// Here is my `ts-morph` linting code: ////////// These are some shared types and values used throughout the code @@ -22,8 +27,7 @@ type FunctionOrMethod = FunctionDeclaration | MethodDeclaration | ConstructorDec // This returns `undefined` if there is no error message to emit; otherwise, it returns a key to the `ERROR_MESSAGES` map type DetChecker = (node: Node, fn: FunctionOrMethod, isLocal: (name: string) => boolean) => string | undefined; -// TODO: figure out how to make the `any` types around here typed -type GlobalTools = {eslintContext: any, parserServices: ParserServicesWithTypeInformation, typeChecker: TypeChecker}; +type GlobalTools = {eslintContext: EslintContext, parserServices: ParserServicesWithTypeInformation, typeChecker: TypeChecker}; let GLOBAL_TOOLS: GlobalTools | undefined = undefined; // These included `Transaction` and `TransactionContext` respectively before! @@ -107,12 +111,12 @@ function functionShouldBeDeterministic(fnDecl: FunctionOrMethod): boolean { } // Bijectivity is preseved for TSMorph <-> TSC <-> ESTree, as far as I can tell! -function makeTsMorphNode(eslintNode: any): Node { +function makeTsMorphNode(eslintNode: EslintNode): Node { const compilerNode = GLOBAL_TOOLS!.parserServices.esTreeNodeToTSNodeMap.get(eslintNode); return createWrappedNode(compilerNode); } -function makeEslintNode(tsMorphNode: Node): any { +function makeEslintNode(tsMorphNode: Node): EslintNode { const compilerNode = tsMorphNode.compilerNode; return GLOBAL_TOOLS!.parserServices.tsNodeToESTreeNodeMap.get(compilerNode); } @@ -300,7 +304,7 @@ function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { ////////// This is the entrypoint for running the determinism analysis with `ts-morph` -function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContext: any) { +function analyzeRootNodeForDeterminism(eslintNode: EslintNode, eslintContext: EslintContext) { const parserServices = ESLintUtils.getParserServices(eslintContext); GLOBAL_TOOLS = { @@ -309,7 +313,7 @@ function analyzeEstreeNodeForDeterminism(estreeNode: any, eslintContext: any) { typeChecker: parserServices.program.getTypeChecker() }; - const tsMorphNode = makeTsMorphNode(estreeNode); + const tsMorphNode = makeTsMorphNode(eslintNode); try { if (Node.isSourceFile(tsMorphNode)) { @@ -405,13 +409,13 @@ module.exports = { messages: Object.fromEntries(ERROR_MESSAGES) }, - create: function (context: any) { + create: function (context: EslintContext) { return { /* Note: I am working with ts-morph because it has stronger typing, and it's easier to work with the AST than ESTree's limited tree navigation. */ - Program(node: any) { - analyzeEstreeNodeForDeterminism(node, context); + Program(node: EslintNode) { + analyzeRootNodeForDeterminism(node, context); } } } From 6e99680f3ced064114d9757aef3b827906a16d5c Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 10:39:29 -0700 Subject: [PATCH 24/49] Make everything more strongly typed --- src/dbos-rules.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/dbos-rules.ts b/src/dbos-rules.ts index 2fa774d..f78dce9 100644 --- a/src/dbos-rules.ts +++ b/src/dbos-rules.ts @@ -1,6 +1,6 @@ import { TypeChecker } from "typescript"; import * as tslintPlugin from "@typescript-eslint/eslint-plugin"; -import { ESLintUtils, ParserServicesWithTypeInformation } from "@typescript-eslint/utils"; +import { ESLintUtils, TSESLint, TSESTree, ParserServicesWithTypeInformation } from "@typescript-eslint/utils"; import { createWrappedNode, Node, FunctionDeclaration, @@ -12,10 +12,8 @@ import { const secPlugin = require("eslint-plugin-security"); const noSecrets = require("eslint-plugin-no-secrets"); -// https://stackoverflow.com/questions/51851677/how-to-get-argument-types-from-function-in-typescript -type ArgumentTypes = F extends (...args: infer A) => any ? A : never; -type EslintContext = ArgumentTypes[0]; // TODO: stop using this construct -type EslintNode = any; // TODO: type this +type EslintNode = TSESTree.Node; +type EslintContext = TSESLint.RuleContext; //////////////////////////////////////////////////////////////////////////////////////////////////// Here is my `ts-morph` linting code: @@ -128,6 +126,7 @@ function getTypeNameForTsMorphNode(tsMorphNode: Node): string | undefined { nodes, which in turn come from ESTree nodes (which are the nodes that ESLint uses for its AST). */ + // TODO: use `getSymbolAtLocation` instead const type = GLOBAL_TOOLS!.typeChecker.getTypeAtLocation(tsMorphNode.compilerNode); return type.getSymbol()?.getName(); } From 7a159278780b2599b962dd21acc6837ff44ced9a Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 11:29:12 -0700 Subject: [PATCH 25/49] Add some more test cases. Change some test case subset names. Remove a TODO. Change some error messages. --- src/dbos-rules.ts | 17 ++++++++--------- test/test.ts | 10 +++++++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/dbos-rules.ts b/src/dbos-rules.ts index f78dce9..476e6dd 100644 --- a/src/dbos-rules.ts +++ b/src/dbos-rules.ts @@ -36,7 +36,8 @@ const ERROR_MESSAGES = makeErrorMessageSet(); ////////// This is the set of error messages that can be emitted function makeErrorMessageSet(): Map { - const makeDateMessage = (variantEnd: string) => `Calling \`Date${variantEnd}()\` is banned (consider using \`@dbos-inc/communicator-datetime\` for consistency and testability)`; + const makeDateMessage = (bannedCall: string) => `Calling ${bannedCall} is banned \ +(consider using \`@dbos-inc/communicator-datetime\` for consistency and testability)`; const bcryptMessage = "Avoid using `bcrypt`, which contains native code. Instead, use `bcryptjs`. \ Also, some `bcrypt` functions generate random data and should only be called from communicators"; @@ -45,11 +46,11 @@ Also, some `bcrypt` functions generate random data and should only be called fro // The keys are the ids, and the values are the messages themselves return new Map([ - ["globalModification", "This is a global modification relative to the workflow declaration."], - ["awaitingOnNotAllowedType", `This function (expected to be deterministic) should not await with a leftmost value of this type (allowed set: ${validTypeSetString})`], - ["Date", makeDateMessage("")], - ["Date.now", makeDateMessage(".now")], - ["Math.random", "Avoid calling Math.random() directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`"], + ["globalModification", "This is a global modification relative to the workflow declaration"], + ["awaitingOnNotAllowedType", `This function (expected to be deterministic) should not await with a leftmost value of this type (allowed set: \{${validTypeSetString}\})`], + ["Date", makeDateMessage("`Date()` or `new Date()`")], + ["Date.now", makeDateMessage("`Date.now()`")], + ["Math.random", "Avoid calling `Math.random()` directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`"], ["setTimeout", "Avoid calling `setTimeout()` directly; it can lead to undesired behavior when debugging"], ["bcrypt.hash", bcryptMessage], ["bcrypt.compare", bcryptMessage] @@ -126,9 +127,7 @@ function getTypeNameForTsMorphNode(tsMorphNode: Node): string | undefined { nodes, which in turn come from ESTree nodes (which are the nodes that ESLint uses for its AST). */ - // TODO: use `getSymbolAtLocation` instead - const type = GLOBAL_TOOLS!.typeChecker.getTypeAtLocation(tsMorphNode.compilerNode); - return type.getSymbol()?.getName(); + return GLOBAL_TOOLS!.typeChecker.getTypeAtLocation(tsMorphNode.compilerNode).getSymbol()?.getName(); } ////////// These functions are the determinism heuristics that I've written diff --git a/test/test.ts b/test/test.ts index f206442..fb2d373 100644 --- a/test/test.ts +++ b/test/test.ts @@ -107,7 +107,7 @@ class Foo { }`)] ], - ["banned functions", + ["banned/not banned functions", [ makeCaseForOkayCall("foo()"), makeCaseForOkayCall("Date('December 17, 1995 03:24:00')"), @@ -124,8 +124,9 @@ class Foo { ] ], - ["allowed awaits", + ["allowed/not allowed awaits", [ + makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "({}).foo()"), // TODO: probably make this not allowed makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.foo()"), makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.client('users').select('password').where({ username }).first();") @@ -134,9 +135,12 @@ class Foo { [ makeCaseForBannedAwaitCall("", "fetch('https://www.google.com')"), makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), - makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.foo()") + makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.foo()"), + makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.client('users').select('password').where({ username }).first();") ] ] ]; testSet.forEach((test) => doTest(...test)); + +// TODO: test the 1 other await case From a3f527d0cf8c6cad131dee0e3062e9c64da151f1 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 11:30:00 -0700 Subject: [PATCH 26/49] Swap 2 test cases --- test/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.ts b/test/test.ts index fb2d373..20a8585 100644 --- a/test/test.ts +++ b/test/test.ts @@ -134,8 +134,8 @@ class Foo { [ makeCaseForBannedAwaitCall("", "fetch('https://www.google.com')"), - makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.foo()"), + makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.client('users').select('password').where({ username }).first();") ] ] From 5a74ae28823b65df3d1c85aa42858b8565a3ba9a Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 11:30:35 -0700 Subject: [PATCH 27/49] Reformat a TODO --- src/dbos-rules.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dbos-rules.ts b/src/dbos-rules.ts index 476e6dd..bd6b609 100644 --- a/src/dbos-rules.ts +++ b/src/dbos-rules.ts @@ -328,8 +328,7 @@ function analyzeRootNodeForDeterminism(eslintNode: EslintNode, eslintContext: Es } } -/* Other TODO: -- Take a look at these functions: +/* Take a look at these functions later on: isArrowFunction, isFunctionExpression, isObjectBindingPattern, isPropertyAssignment, isQualifiedName - Check function expressions and arrow functions for mutation (and interfaces?) - Check for recursive global mutation for expected-to-be-deterministic functions From 0082c13c73a2713d34a46802d560c8502e5da6fb Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 11:44:06 -0700 Subject: [PATCH 28/49] Test some more cases --- test/test.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/test.ts b/test/test.ts index 20a8585..2b1ab0d 100644 --- a/test/test.ts +++ b/test/test.ts @@ -126,7 +126,12 @@ class Foo { ["allowed/not allowed awaits", [ + makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "new Set()"), // TODO: definitely make this not allowed makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "({}).foo()"), // TODO: probably make this not allowed + + // When you don't await on a `WorkflowContext`, but you pass a param into the function you're calling, it's okay + makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "foo(ctxt); async function foo(bar: WorkflowContext) {return bar.baz();} "), + makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.foo()"), makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.client('users').select('password').where({ username }).first();") @@ -134,13 +139,13 @@ class Foo { [ makeCaseForBannedAwaitCall("", "fetch('https://www.google.com')"), + makeCaseForBannedAwaitCall("", "foo(); async function foo() {return 5;} "), makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.foo()"), makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), - makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.client('users').select('password').where({ username }).first();") + makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.client('users').select('password').where({ username }).first();"), + makeCaseForBannedAwaitCall("ctxt: object", "5; const y = new Set(); await y.foo()") ] ] ]; testSet.forEach((test) => doTest(...test)); - -// TODO: test the 1 other await case From 7b0bad7faa955c65de9e8c7655f8c24155b2ce45 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 11:44:34 -0700 Subject: [PATCH 29/49] Remove some params --- test/test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test.ts b/test/test.ts index 2b1ab0d..da9f130 100644 --- a/test/test.ts +++ b/test/test.ts @@ -126,8 +126,8 @@ class Foo { ["allowed/not allowed awaits", [ - makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "new Set()"), // TODO: definitely make this not allowed - makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "({}).foo()"), // TODO: probably make this not allowed + makeCaseForOkayAwaitCall("", "new Set()"), // TODO: definitely make this not allowed + makeCaseForOkayAwaitCall("", "({}).foo()"), // TODO: probably make this not allowed // When you don't await on a `WorkflowContext`, but you pass a param into the function you're calling, it's okay makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "foo(ctxt); async function foo(bar: WorkflowContext) {return bar.baz();} "), From 9317aac6a2834c70355d21587bd5c981eb9faf16 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 11:50:10 -0700 Subject: [PATCH 30/49] Make a bunch of functions more strongly typed --- test/test.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/test.ts b/test/test.ts index da9f130..8b416a5 100644 --- a/test/test.ts +++ b/test/test.ts @@ -9,11 +9,16 @@ RuleTester.afterAll = mochaAfter; // https://stackoverflow.com/questions/51851677/how-to-get-argument-types-from-function-in-typescript type ArgumentTypes = F extends (...args: infer A) => any ? A : never; +// https://stackoverflow.com/questions/41253310/typescript-retrieve-element-type-information-from-array-type +type ArrayElementType = ArrayType extends readonly (infer T)[] ? T : never; + type TestTypes = ArgumentTypes[2]; type ValidTests = TestTypes["valid"]; type InvalidTests = TestTypes["invalid"]; type TestSet = [string, ValidTests, InvalidTests][]; +type ValidTest = ArrayElementType; +type InvalidTest = ArrayElementType; function doTest(title: string, valid: ValidTests, invalid: InvalidTests) { const ruleName = "unexpected-nondeterminism"; @@ -40,30 +45,25 @@ function makeExpectedDetCode(code: string, params: string = "", aboveClass: stri `; } -// TODO: give this a better return type -function makeCaseForModification(numErrors: number, code: string): any { +function makeCaseForModification(numErrors: number, code: string): InvalidTest { return { code: code, errors: Array(numErrors).fill({ messageId: "globalModification" }) }; } -// TODO: this too -function makeCaseForOkayCall(call: string): any { +function makeCaseForOkayCall(call: string): ValidTest { return { code: makeExpectedDetCode(`const x = ${call};`) }; } -// TODO: this too -function makeCaseForBannedCall(prefix: string, functionName: string, params: string): any { +function makeCaseForBannedCall(prefix: string, functionName: string, params: string): InvalidTest { return { code: makeExpectedDetCode(`const x = ${prefix} ${functionName}(${params});`), errors: [{ messageId: functionName }] } } -function makeCaseForOkayAwaitCall(params: string, awaitedUpon: string): any { - const code = makeExpectedDetCode(`const x = await ${awaitedUpon};`, params, "class WorkflowContext {}"); - return { code: code }; +function makeCaseForOkayAwaitCall(params: string, awaitedUpon: string): ValidTest { + return { code: makeExpectedDetCode(`const x = await ${awaitedUpon};`, params, "class WorkflowContext {}") }; } -// TODO: this too -function makeCaseForBannedAwaitCall(params: string, awaitedUpon: string): any { +function makeCaseForBannedAwaitCall(params: string, awaitedUpon: string): InvalidTest { const code = makeExpectedDetCode(`const x = await ${awaitedUpon};`, params, "class FooBar {}"); - return { code: code, errors: [{ messageId: "awaitingOnNotAllowedType" }]}; + return { code: code, errors: [{ messageId: "awaitingOnNotAllowedType" }] }; } const testSet: TestSet = [ From 1742952a2090577c449419b899df7f9b8c739d7f Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 13:29:23 -0700 Subject: [PATCH 31/49] Switch from mocha to vitest --- package-lock.json | 1841 ++++++++++++++++++-------- package.json | 8 +- test/{test.ts => dbos-rules.test.ts} | 11 +- tsconfig.json | 2 +- 4 files changed, 1301 insertions(+), 561 deletions(-) rename test/{test.ts => dbos-rules.test.ts} (93%) diff --git a/package-lock.json b/package-lock.json index 845c5cb..01dff5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "0.0.7", "license": "MIT", "dependencies": { - "@types/mocha": "^10.0.7", "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", @@ -17,10 +16,9 @@ "@typescript-eslint/utils": "^7.13.1", "eslint-plugin-no-secrets": "^0.8.9", "eslint-plugin-security": "^2.1.0", - "mocha": "^10.4.0", - "ts-mocha": "^10.0.0", "ts-morph": "^22.0.0", - "typescript": "^5.4.5" + "typescript": "^5.4.5", + "vitest": "^1.6.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -32,6 +30,351 @@ "node": ">=0.10.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -119,6 +462,22 @@ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "peer": true }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -151,6 +510,203 @@ "node": ">= 8" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, "node_modules/@ts-morph/common": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", @@ -184,16 +740,10 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "optional": true - }, - "node_modules/@types/mocha": { - "version": "10.0.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", - "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==" + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/node": { "version": "20.14.6", @@ -426,17 +976,105 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "peer": true - }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "peer": true + }, + "node_modules/@vitest/expect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "dependencies": { + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "dependencies": { + "@vitest/utils": "1.6.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "peer": true, + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", + "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", "bin": { "acorn": "bin/acorn" }, @@ -453,6 +1091,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -468,18 +1117,11 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "peer": true, "engines": { "node": ">=8" } @@ -488,6 +1130,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -498,22 +1141,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "peer": true }, "node_modules/array-union": { "version": "2.1.0", @@ -523,12 +1155,12 @@ "node": ">=8" } }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "engines": { - "node": ">=0.10.0" + "node": "*" } }, "node_modules/balanced-match": { @@ -536,17 +1168,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -568,15 +1189,13 @@ "node": ">=8" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "engines": { + "node": ">=8" + } }, "node_modules/callsites": { "version": "3.1.0", @@ -587,21 +1206,28 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -613,51 +1239,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dependencies": { - "is-glob": "^4.0.1" + "get-func-name": "^2.0.2" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "node": "*" } }, "node_modules/code-block-writer": { @@ -669,6 +1259,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -679,7 +1270,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "peer": true }, "node_modules/concat-map": { "version": "0.0.1", @@ -687,11 +1279,15 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "peer": true }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -717,15 +1313,15 @@ } } }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "engines": { - "node": ">=10" + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dependencies": { + "type-detect": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=6" } }, "node_modules/deep-is": { @@ -734,12 +1330,12 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "peer": true }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "engines": { - "node": ">=0.3.1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/dir-glob": { @@ -765,23 +1361,48 @@ "node": ">=6.0.0" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { - "node": ">=6" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "peer": true, "engines": { "node": ">=10" }, @@ -941,6 +1562,14 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -950,6 +1579,28 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1027,6 +1678,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -1038,14 +1690,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -1069,7 +1713,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "peer": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -1084,12 +1729,23 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/glob": { @@ -1167,16 +1823,17 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "peer": true, "engines": { "node": ">=8" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "engines": { + "node": ">=16.17.0" } }, "node_modules/ignore": { @@ -1216,6 +1873,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1224,18 +1882,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "peer": true }, "node_modules/is-extglob": { "version": "2.1.1", @@ -1245,14 +1893,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1281,20 +1921,12 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1303,13 +1935,18 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "peer": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -1333,18 +1970,6 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "optional": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -1367,10 +1992,26 @@ "node": ">= 0.8.0" } }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -1386,25 +2027,26 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "get-func-name": "^2.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "node_modules/merge2": { "version": "1.4.1", @@ -1426,6 +2068,17 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1438,14 +2091,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", @@ -1460,123 +2105,92 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", - "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "8.1.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/mlly": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", "dependencies": { - "balanced-match": "^1.0.0" + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" } }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dependencies": { - "brace-expansion": "^2.0.1" + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=10" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dependencies": { - "has-flag": "^4.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "peer": true, "dependencies": { "wrappy": "1" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -1598,6 +2212,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -1612,6 +2227,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -1643,6 +2259,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "peer": true, "engines": { "node": ">=8" } @@ -1660,7 +2277,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "peer": true, "engines": { "node": ">=8" } @@ -1673,6 +2289,24 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1684,6 +2318,43 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz", + "integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==", + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.0", + "pathe": "^1.1.2" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1693,6 +2364,30 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1720,24 +2415,10 @@ } ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/regexp-tree": { "version": "0.1.27", @@ -1747,14 +2428,6 @@ "regexp-tree": "bin/regexp-tree" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1788,6 +2461,40 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -1810,25 +2517,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/safe-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", @@ -1848,19 +2536,10 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1872,11 +2551,26 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "peer": true, "engines": { "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -1885,40 +2579,29 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "engines": { "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==" }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1926,19 +2609,22 @@ "node": ">=8" } }, - "node_modules/strip-bom": { + "node_modules/strip-final-newline": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "optional": true, + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "peer": true, "engines": { "node": ">=8" }, @@ -1946,10 +2632,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dependencies": { + "js-tokens": "^9.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -1963,6 +2661,27 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "peer": true }, + "node_modules/tinybench": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", + "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==" + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1985,26 +2704,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz", - "integrity": "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==", - "dependencies": { - "ts-node": "7.0.1" - }, - "bin": { - "ts-mocha": "bin/ts-mocha" - }, - "engines": { - "node": ">= 6.X.X" - }, - "optionalDependencies": { - "tsconfig-paths": "^3.5.0" - }, - "peerDependencies": { - "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X" - } - }, "node_modules/ts-morph": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz", @@ -2014,58 +2713,6 @@ "code-block-writer": "^13.0.1" } }, - "node_modules/ts-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", - "dependencies": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" - }, - "bin": { - "ts-node": "dist/bin.js" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/ts-node/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "optional": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2078,6 +2725,14 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -2102,6 +2757,11 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -2115,106 +2775,185 @@ "punycode": "^2.1.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "peer": true, + "node_modules/vite": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz", + "integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==", "dependencies": { - "isexe": "^2.0.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.38", + "rollup": "^4.13.0" }, "bin": { - "node-which": "bin/node-which" + "vite": "bin/vite.js" }, "engines": { - "node": ">= 8" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/vite-node": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" }, "engines": { - "node": ">=10" + "node": "^18.0.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" + "url": "https://opencollective.com/vitest" } }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/vitest": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": ">=10" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, "engines": { - "node": ">=10" + "node": ">= 8" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", - "engines": { - "node": ">=4" - } + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "peer": true }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "peer": true, "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index f06c20f..8708ddf 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,9 @@ "homepage": "https://docs.dbos.dev/", "scripts": { "build": "tsc", - "test": "tsc && ts-mocha test/test.ts" + "test": "tsc && vitest test/dbos-rules.test.ts" }, "dependencies": { - "@types/mocha": "^10.0.7", "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", @@ -23,10 +22,9 @@ "@typescript-eslint/utils": "^7.13.1", "eslint-plugin-no-secrets": "^0.8.9", "eslint-plugin-security": "^2.1.0", - "mocha": "^10.4.0", - "ts-mocha": "^10.0.0", "ts-morph": "^22.0.0", - "typescript": "^5.4.5" + "typescript": "^5.4.5", + "vitest": "^1.6.0" }, "keywords": [ "eslint", diff --git a/test/test.ts b/test/dbos-rules.test.ts similarity index 93% rename from test/test.ts rename to test/dbos-rules.test.ts index 8b416a5..e7b33d2 100644 --- a/test/test.ts +++ b/test/dbos-rules.test.ts @@ -1,8 +1,11 @@ -import { after as mochaAfter } from "mocha"; // TODO: switch to `jest` or `vitest` +import * as vitest from "vitest"; import { RuleTester } from "@typescript-eslint/rule-tester"; -const rulesUnderTest = require("../dist/src/dbos-rules.js"); // TODO: import my rules normally +const rulesUnderTest = require("../dist/src/dbos-rules.js"); // TODO: import my rules normally (and no `tsc` before the test too) -RuleTester.afterAll = mochaAfter; +RuleTester.afterAll = vitest.afterAll; +RuleTester.it = vitest.it; +RuleTester.itOnly = vitest.it.only; +RuleTester.describe = vitest.describe; ////////// @@ -30,7 +33,7 @@ function doTest(title: string, valid: ValidTests, invalid: InvalidTests) { const tester = new RuleTester({ parser: "@typescript-eslint/parser", parserOptions: { project: "tsconfig.json" }, - defaultFilenames: { ts: "test/test.ts", tsx: "test/this_file_doesnt_exist.tsx" } + defaultFilenames: { ts: "test/dbos-rules.test.ts", tsx: "test/this_file_doesnt_exist.tsx" } }); function makeExpectedDetCode(code: string, params: string = "", aboveClass: string = ""): string { diff --git a/tsconfig.json b/tsconfig.json index 0ddf1e9..797c7e4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,7 @@ "include": [ /* Specifies an array of filenames or patterns to include in the program. */ "src/dbos-rules.ts", - "test/test.ts" + "test/dbos-rules.test.ts" ], "exclude": [ From dcd118074fa119392980fb4d383c99ce4b9c9b7e Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 13:31:30 -0700 Subject: [PATCH 32/49] Shorten a command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8708ddf..0448829 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "homepage": "https://docs.dbos.dev/", "scripts": { "build": "tsc", - "test": "tsc && vitest test/dbos-rules.test.ts" + "test": "tsc && vitest" }, "dependencies": { "@types/node": "^20.14.6", From e6697d1992e37cf782f923d4d27c219bfbd166d3 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 14:12:32 -0700 Subject: [PATCH 33/49] Move all of the testing dependencies to 'devDependencies' --- package-lock.json | 268 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 10 +- 2 files changed, 253 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01dff5b..be25c98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,12 +12,14 @@ "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", - "@typescript-eslint/rule-tester": "^7.13.1", "@typescript-eslint/utils": "^7.13.1", "eslint-plugin-no-secrets": "^0.8.9", "eslint-plugin-security": "^2.1.0", "ts-morph": "^22.0.0", - "typescript": "^5.4.5", + "typescript": "^5.4.5" + }, + "devDependencies": { + "@typescript-eslint/rule-tester": "^7.14.1", "vitest": "^1.6.0" } }, @@ -37,6 +39,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "aix" @@ -52,6 +55,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -67,6 +71,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -82,6 +87,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" @@ -97,6 +103,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -112,6 +119,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -127,6 +135,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -142,6 +151,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -157,6 +167,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -172,6 +183,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -187,6 +199,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" @@ -202,6 +215,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -217,6 +231,7 @@ "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" @@ -232,6 +247,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -247,6 +263,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -262,6 +279,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -277,6 +295,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -292,6 +311,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -307,6 +327,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -322,6 +343,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" @@ -337,6 +359,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -352,6 +375,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -367,6 +391,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -466,6 +491,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -476,7 +502,8 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -517,6 +544,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -529,6 +557,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -541,6 +570,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -553,6 +583,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -565,6 +596,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -577,6 +609,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -589,6 +622,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -601,6 +635,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -613,6 +648,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -625,6 +661,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -637,6 +674,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -649,6 +687,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -661,6 +700,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -673,6 +713,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -685,6 +726,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -697,6 +739,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -705,7 +748,8 @@ "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true }, "node_modules/@ts-morph/common": { "version": "0.23.0", @@ -743,7 +787,8 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true }, "node_modules/@types/node": { "version": "20.14.6", @@ -813,12 +858,13 @@ } }, "node_modules/@typescript-eslint/rule-tester": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/rule-tester/-/rule-tester-7.13.1.tgz", - "integrity": "sha512-rM55VW1cWHLKys8cKKa2RjRUasB5k3hniuwixZXX28oayk9QCiAhbKTJjkbYAkXjOOyfEN+ReVFTvvTNWGOvDA==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/rule-tester/-/rule-tester-7.14.1.tgz", + "integrity": "sha512-REmxvlD8pXOxlxfaoxAgqDEU8AFTAoCCIKdpe5y8Jxs3QXqLPiP7pl8Gn073LS2KgFrzEddDiDmBLwXCVJFeBg==", + "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/utils": "7.13.1", + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/utils": "7.14.1", "ajv": "^6.12.6", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "4.6.2", @@ -836,6 +882,127 @@ "eslint": "^8.56.0" } }, + "node_modules/@typescript-eslint/rule-tester/node_modules/@typescript-eslint/scope-manager": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.14.1.tgz", + "integrity": "sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/rule-tester/node_modules/@typescript-eslint/types": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.14.1.tgz", + "integrity": "sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/rule-tester/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.14.1.tgz", + "integrity": "sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/rule-tester/node_modules/@typescript-eslint/utils": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.14.1.tgz", + "integrity": "sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/rule-tester/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.14.1.tgz", + "integrity": "sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/rule-tester/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/rule-tester/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", @@ -986,6 +1153,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "dev": true, "dependencies": { "@vitest/spy": "1.6.0", "@vitest/utils": "1.6.0", @@ -999,6 +1167,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "dev": true, "dependencies": { "@vitest/utils": "1.6.0", "p-limit": "^5.0.0", @@ -1012,6 +1181,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, "dependencies": { "yocto-queue": "^1.0.0" }, @@ -1026,6 +1196,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, "engines": { "node": ">=12.20" }, @@ -1037,6 +1208,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "dev": true, "dependencies": { "magic-string": "^0.30.5", "pathe": "^1.1.1", @@ -1050,6 +1222,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "dev": true, "dependencies": { "tinyspy": "^2.2.0" }, @@ -1061,6 +1234,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "dev": true, "dependencies": { "diff-sequences": "^29.6.3", "estree-walker": "^3.0.3", @@ -1095,6 +1269,7 @@ "version": "8.3.3", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, "dependencies": { "acorn": "^8.11.0" }, @@ -1159,6 +1334,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, "engines": { "node": "*" } @@ -1193,6 +1369,7 @@ "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, "engines": { "node": ">=8" } @@ -1210,6 +1387,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -1243,6 +1421,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, "dependencies": { "get-func-name": "^2.0.2" }, @@ -1282,7 +1461,8 @@ "node_modules/confbox": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", - "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==" + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -1317,6 +1497,7 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, "dependencies": { "type-detect": "^4.0.0" }, @@ -1334,6 +1515,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -1365,6 +1547,7 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -1566,6 +1749,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, "dependencies": { "@types/estree": "^1.0.0" } @@ -1583,6 +1767,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", @@ -1720,6 +1905,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -1733,6 +1919,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, "engines": { "node": "*" } @@ -1741,6 +1928,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, "engines": { "node": ">=16" }, @@ -1832,6 +2020,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, "engines": { "node": ">=16.17.0" } @@ -1925,6 +2114,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -1940,7 +2130,8 @@ "node_modules/js-tokens": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", - "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==" + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -1996,6 +2187,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, "dependencies": { "mlly": "^1.4.2", "pkg-types": "^1.0.3" @@ -2031,6 +2223,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, "dependencies": { "get-func-name": "^2.0.1" } @@ -2039,6 +2232,7 @@ "version": "0.30.10", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -2046,7 +2240,8 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "node_modules/merge2": { "version": "1.4.1", @@ -2072,6 +2267,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, "engines": { "node": ">=12" }, @@ -2109,6 +2305,7 @@ "version": "1.7.1", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "dev": true, "dependencies": { "acorn": "^8.11.3", "pathe": "^1.1.2", @@ -2125,6 +2322,7 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, "funding": [ { "type": "github", @@ -2147,6 +2345,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, "dependencies": { "path-key": "^4.0.0" }, @@ -2161,6 +2360,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, "engines": { "node": ">=12" }, @@ -2181,6 +2381,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, "dependencies": { "mimic-fn": "^4.0.0" }, @@ -2292,12 +2493,14 @@ "node_modules/pathe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, "engines": { "node": "*" } @@ -2305,7 +2508,8 @@ "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -2322,6 +2526,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz", "integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==", + "dev": true, "dependencies": { "confbox": "^0.1.7", "mlly": "^1.7.0", @@ -2332,6 +2537,7 @@ "version": "8.4.38", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2368,6 +2574,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -2381,6 +2588,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -2418,7 +2626,8 @@ "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true }, "node_modules/regexp-tree": { "version": "0.1.27", @@ -2465,6 +2674,7 @@ "version": "4.18.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dev": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -2558,12 +2768,14 @@ "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==" + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { "node": ">=14" }, @@ -2583,6 +2795,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2590,12 +2803,14 @@ "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==" + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true }, "node_modules/std-env": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true }, "node_modules/strip-ansi": { "version": "6.0.1", @@ -2613,6 +2828,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, "engines": { "node": ">=12" }, @@ -2636,6 +2852,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dev": true, "dependencies": { "js-tokens": "^9.0.0" }, @@ -2664,12 +2881,14 @@ "node_modules/tinybench": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", - "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==" + "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "dev": true }, "node_modules/tinypool": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, "engines": { "node": ">=14.0.0" } @@ -2678,6 +2897,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, "engines": { "node": ">=14.0.0" } @@ -2729,6 +2949,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, "engines": { "node": ">=4" } @@ -2760,7 +2981,8 @@ "node_modules/ufo": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", - "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==" + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true }, "node_modules/undici-types": { "version": "5.26.5", @@ -2779,6 +3001,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz", "integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==", + "dev": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.38", @@ -2833,6 +3056,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "dev": true, "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", @@ -2854,6 +3078,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "dev": true, "dependencies": { "@vitest/expect": "1.6.0", "@vitest/runner": "1.6.0", @@ -2932,6 +3157,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" diff --git a/package.json b/package.json index 0448829..ccc78d7 100644 --- a/package.json +++ b/package.json @@ -18,16 +18,18 @@ "@types/node": "^20.14.6", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", - "@typescript-eslint/rule-tester": "^7.13.1", "@typescript-eslint/utils": "^7.13.1", "eslint-plugin-no-secrets": "^0.8.9", "eslint-plugin-security": "^2.1.0", "ts-morph": "^22.0.0", - "typescript": "^5.4.5", - "vitest": "^1.6.0" + "typescript": "^5.4.5" }, "keywords": [ "eslint", "eslintplugin" - ] + ], + "devDependencies": { + "@typescript-eslint/rule-tester": "^7.14.1", + "vitest": "^1.6.0" + } } From daf422fe5cf20f08234477c89645f0b21114ddcf Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 14:38:24 -0700 Subject: [PATCH 34/49] Move the rules and the tests out of their little directories. Now importing the rules in a sane manner in the testing file. --- test/dbos-rules.test.ts => dbos-rules.test.ts | 8 ++++---- src/dbos-rules.ts => dbos-rules.ts | 2 ++ package.json | 4 ++-- tsconfig.json | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) rename test/dbos-rules.test.ts => dbos-rules.test.ts (94%) rename src/dbos-rules.ts => dbos-rules.ts (99%) diff --git a/test/dbos-rules.test.ts b/dbos-rules.test.ts similarity index 94% rename from test/dbos-rules.test.ts rename to dbos-rules.test.ts index e7b33d2..0e920da 100644 --- a/test/dbos-rules.test.ts +++ b/dbos-rules.test.ts @@ -1,11 +1,11 @@ import * as vitest from "vitest"; import { RuleTester } from "@typescript-eslint/rule-tester"; -const rulesUnderTest = require("../dist/src/dbos-rules.js"); // TODO: import my rules normally (and no `tsc` before the test too) +import { dbosRulesPerName } from "./dbos-rules"; -RuleTester.afterAll = vitest.afterAll; RuleTester.it = vitest.it; RuleTester.itOnly = vitest.it.only; RuleTester.describe = vitest.describe; +RuleTester.afterAll = vitest.afterAll; ////////// @@ -25,7 +25,7 @@ type InvalidTest = ArrayElementType; function doTest(title: string, valid: ValidTests, invalid: InvalidTests) { const ruleName = "unexpected-nondeterminism"; - tester.run(title, rulesUnderTest.rules[ruleName], { valid: valid, invalid: invalid }); + tester.run(title, dbosRulesPerName[ruleName], { valid: valid, invalid: invalid }); } ////////// @@ -33,7 +33,7 @@ function doTest(title: string, valid: ValidTests, invalid: InvalidTests) { const tester = new RuleTester({ parser: "@typescript-eslint/parser", parserOptions: { project: "tsconfig.json" }, - defaultFilenames: { ts: "test/dbos-rules.test.ts", tsx: "test/this_file_doesnt_exist.tsx" } + defaultFilenames: { ts: "dbos-rules.test.ts", tsx: "this_file_doesnt_exist.tsx" } }); function makeExpectedDetCode(code: string, params: string = "", aboveClass: string = ""): string { diff --git a/src/dbos-rules.ts b/dbos-rules.ts similarity index 99% rename from src/dbos-rules.ts rename to dbos-rules.ts index bd6b609..1bb08a5 100644 --- a/src/dbos-rules.ts +++ b/dbos-rules.ts @@ -431,3 +431,5 @@ module.exports = { dbosExtendedConfig: extConfig } }; + +export const dbosRulesPerName: any = module.exports.rules; diff --git a/package.json b/package.json index ccc78d7..e346b54 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "type": "git", "url": "https://github.com/dbos-inc/eslint-plugin" }, - "main": "dist/src/dbos-rules.js", - "types": "dist/src/dbos-rules.d.ts", + "main": "dist/dbos-rules.js", + "types": "dist/dbos-rules.d.ts", "homepage": "https://docs.dbos.dev/", "scripts": { "build": "tsc", diff --git a/tsconfig.json b/tsconfig.json index 797c7e4..5948a77 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,8 +18,8 @@ }, "include": [ /* Specifies an array of filenames or patterns to include in the program. */ - "src/dbos-rules.ts", - "test/dbos-rules.test.ts" + "dbos-rules.ts", + "dbos-rules.test.ts" ], "exclude": [ From 58050219dcf9e22a65a688b8c06486dfdbe8bc6a Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 14:40:22 -0700 Subject: [PATCH 35/49] Make some function syntax more succinct --- dbos-rules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 1bb08a5..292a83a 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -406,7 +406,7 @@ module.exports = { messages: Object.fromEntries(ERROR_MESSAGES) }, - create: function (context: EslintContext) { + create: (context: EslintContext) => { return { /* Note: I am working with ts-morph because it has stronger typing, and it's easier to work with the AST From a959e31cefc443b4827f21c92b33fa4b22ce54b4 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 14:49:40 -0700 Subject: [PATCH 36/49] Rename the determinism rule to its original name --- dbos-rules.test.ts | 2 +- dbos-rules.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dbos-rules.test.ts b/dbos-rules.test.ts index 0e920da..43ca504 100644 --- a/dbos-rules.test.ts +++ b/dbos-rules.test.ts @@ -24,7 +24,7 @@ type ValidTest = ArrayElementType; type InvalidTest = ArrayElementType; function doTest(title: string, valid: ValidTests, invalid: InvalidTests) { - const ruleName = "unexpected-nondeterminism"; + const ruleName = "detect-nondeterministic-calls"; tester.run(title, dbosRulesPerName[ruleName], { valid: valid, invalid: invalid }); } diff --git a/dbos-rules.ts b/dbos-rules.ts index 292a83a..1362b6a 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -351,7 +351,7 @@ const baseConfig = { "no-console": "error", "security/detect-unsafe-regex": "error", "no-secrets/no-secrets": "error", - "@dbos-inc/unexpected-nondeterminism": "error" + "@dbos-inc/detect-nondeterministic-calls": "error" }, extends: [] @@ -399,7 +399,7 @@ module.exports = { }, rules: { - "unexpected-nondeterminism": { + "detect-nondeterministic-calls": { meta: { type: "suggestion", docs: { description: "Detect nondeterminism in cases where functions should act deterministically" }, From 8fd184af043139a6aebbfe4fccc478bee18dc499 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 14:52:23 -0700 Subject: [PATCH 37/49] Add some to the README --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 68b2e5c..7a76c06 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ -# DBOS eslint plugin -eslint plugin for DBOS sdk +# DBOS typescript-eslint plugin +typescript-eslint plugin for DBOS sdk The [DBOS SDK](https://github.com/dbos-inc/dbos-ts) (from [DBOS, Inc.](https://dbos.dev)) is a **Typescript framework built on the database** that helps you develop transactional backend applications. This [eslint](https://eslint.org) plugin assists in the following aspects of coding: - Correct use of the DBOS SDK - Conformance to TypeScript best practices -- Identification of code that may contain security vulnerabilities -- +- Identification of code that may contain security vulnerabilities and unexpected nondeterminism ## Getting Started From 44f0c00f2fc7fec31e0f18475ac58e6408604ce6 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 15:10:29 -0700 Subject: [PATCH 38/49] Change some spelling --- dbos-rules.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 1362b6a..245c281 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -8,7 +8,7 @@ import { MethodDeclaration } from "ts-morph"; -// TODO: find Typescript variants of these +// Should I find TypeScript variants of these? const secPlugin = require("eslint-plugin-security"); const noSecrets = require("eslint-plugin-no-secrets"); @@ -123,7 +123,7 @@ function makeEslintNode(tsMorphNode: Node): EslintNode { // If the returned name is undefined, then there is no associated type (e.g. a never-defined but used variable) function getTypeNameForTsMorphNode(tsMorphNode: Node): string | undefined { /* We need to use the typechecker to check the type, instead of `expr.getType()`, - since type information is lost when creating `ts-morph` nodes from Typescript compiler + since type information is lost when creating `ts-morph` nodes from TypeScript compiler nodes, which in turn come from ESTree nodes (which are the nodes that ESLint uses for its AST). */ From 1260909e182bfdafb8d80e24695f03f110e0fea7 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 15:51:53 -0700 Subject: [PATCH 39/49] Try to make a workflow for publishing to NPM (mostly copied over from dbos-transact) --- .github/workflows/publish_npm.yml | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/publish_npm.yml diff --git a/.github/workflows/publish_npm.yml b/.github/workflows/publish_npm.yml new file mode 100644 index 0000000..a28c535 --- /dev/null +++ b/.github/workflows/publish_npm.yml @@ -0,0 +1,57 @@ +name: Publish current branch to npm + +on: + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + strategy: + matrix: + package: [ + . + ] + steps: + - name: Checkout + uses: actions/checkout@v4.1.1 + with: + fetch-depth: 0 # fetch-depth 0 needed for NBGV + - name: Use Node.js 20 + uses: actions/setup-node@v4.0.2 + with: + node-version: 20 + - name: Nerdbank.GitVersioning + id: nbgv + uses: dotnet/nbgv@v0.4.2 + with: + stamp: ${{ matrix.package }}/package.json + - run: npm ci + - run: cd ${{ matrix.package }}; npm run build + - name: Publish release to npm + uses: JS-DevTools/npm-publish@v3 + id: npmrelease + # boolean properties from NBGV step appears to be converted into *capitalized* strings + # so explicitly string compare PublicRelease output value + if: ${{ steps.nbgv.outputs.PublicRelease == 'True'}} + with: + token: ${{ secrets.NPM_PUBLISH }} + registry: https://registry.npmjs.org/ + tag: ${{ steps.nbgv.outputs.PrereleaseVersion == '' && 'latest' || 'preview' }} # Assign a 'preview' tag to versions end with '-preview'. Otherwise, assign a 'latest' tag to the latest release. + access: public + package: ${{ matrix.package }} + - name: Publish test package to npm + uses: JS-DevTools/npm-publish@v3 + id: npmtest + if: ${{ steps.nbgv.outputs.PublicRelease == 'False'}} + with: + token: ${{ secrets.NPM_PUBLISH }} + registry: https://registry.npmjs.org/ + tag: 'test' + access: public + package: ${{ matrix.package }} + - if: ${{ steps.npmrelease.outputs.type }} + run: echo "Published a new release package!" + - if: ${{ steps.npmtest.outputs.type }} + run: echo "Published a new test package!" From 1beea51bc890578cab46dd0120b9789f8dacfa08 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 16:09:07 -0700 Subject: [PATCH 40/49] Make the version upgrade to 1.0.0 --- dbos-rules.ts | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 245c281..e250260 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -395,7 +395,7 @@ const extConfig = { module.exports = { meta: { name: "@dbos-inc/eslint-plugin", - version: "0.0.7" + version: "1.0.0" }, rules: { diff --git a/package-lock.json b/package-lock.json index be25c98..b582ce1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.7", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.7", + "version": "1.0.0", "license": "MIT", "dependencies": { "@types/node": "^20.14.6", diff --git a/package.json b/package.json index e346b54..b61bf47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dbos-inc/eslint-plugin", - "version": "0.0.7", + "version": "1.0.0", "description": "eslint plugin for DBOS SDK", "license": "MIT", "repository": { From d048825f49c2bd076f27a98b860d37f594a7126b Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 16:14:54 -0700 Subject: [PATCH 41/49] Shorten the package.json testing rule --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b61bf47..acc8efb 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "homepage": "https://docs.dbos.dev/", "scripts": { "build": "tsc", - "test": "tsc && vitest" + "test": "vitest" }, "dependencies": { "@types/node": "^20.14.6", From 56ec5ea7175d00ec7bbb500da7c84f4da87397df Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 16:59:31 -0700 Subject: [PATCH 42/49] Change an error message slightly. Add a TODO. Change a bit of formatting too. --- dbos-rules.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index e250260..7a574ab 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -47,7 +47,7 @@ Also, some `bcrypt` functions generate random data and should only be called fro // The keys are the ids, and the values are the messages themselves return new Map([ ["globalModification", "This is a global modification relative to the workflow declaration"], - ["awaitingOnNotAllowedType", `This function (expected to be deterministic) should not await with a leftmost value of this type (allowed set: \{${validTypeSetString}\})`], + ["awaitingOnNotAllowedType", `This function should not await with the current leftmost type (allowed set: \{${validTypeSetString}\})`], ["Date", makeDateMessage("`Date()` or `new Date()`")], ["Date.now", makeDateMessage("`Date.now()`")], ["Math.random", "Avoid calling `Math.random()` directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`"], @@ -199,7 +199,10 @@ const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { if (Node.isAwaitExpression(node)) { const functionCall = node.getExpression(); - if (!Node.isCallExpression(functionCall)) return; // Wouldn't make sense otherwise + + if (!Node.isCallExpression(functionCall)) { + return; // Wouldn't make sense otherwise + } let lhs = reduceNodeToLeftmostLeaf(functionCall); @@ -214,7 +217,9 @@ const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { /* If the typename is undefined, there's no associated typename (so possibly a variable is being used that was never defined; - that error will be handled elsewhere). */ + that error will be handled elsewhere). TODO: figure out what's + happening when the Typescript compiler can't get type info out + of the LHS (that happens in some very rare cases). */ const typeName = getTypeNameForTsMorphNode(lhs); if (typeName === undefined) { @@ -229,10 +234,10 @@ const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { an allowed type, since that probably means that that function is a helper function which is deterministic and uses our allowed type. */ if (ignoreAwaitsForCallsWithAContextParam && validTypeExistsInFunctionCallParams(functionCall, validSet)) { - return; + return; } - return "awaitingOnNotAllowedType"; + return "awaitingOnNotAllowedType"; } } } From b4159b732a52461e277814749263c9d9375513db Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Tue, 25 Jun 2024 17:28:55 -0700 Subject: [PATCH 43/49] Add an idea about a possible future point of failure --- dbos-rules.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dbos-rules.ts b/dbos-rules.ts index 7a574ab..2462f9f 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -251,6 +251,8 @@ function evaluateFunctionForDeterminism(fn: FunctionOrMethod) { throw new Error("When would a function not have a body?"); } + /* Could some stack contents stay around if an error + was thrown, and the appropriate frame was never popped? */ const stack: Set[] = [new Set()]; const getCurrentFrame = () => stack[stack.length - 1]; const pushFrame = () => stack.push(new Set()); From 975d5999dc4f3176978fa0841eea5ac5c343f5eb Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 26 Jun 2024 16:37:16 -0700 Subject: [PATCH 44/49] Make the tests much easier to read --- dbos-rules.test.ts | 235 +++++++++++++++++++++++++++++---------------- 1 file changed, 150 insertions(+), 85 deletions(-) diff --git a/dbos-rules.test.ts b/dbos-rules.test.ts index 43ca504..cdd597f 100644 --- a/dbos-rules.test.ts +++ b/dbos-rules.test.ts @@ -16,16 +16,16 @@ type ArgumentTypes = F extends (...args: infer A) => any ? A type ArrayElementType = ArrayType extends readonly (infer T)[] ? T : never; type TestTypes = ArgumentTypes[2]; -type ValidTests = TestTypes["valid"]; -type InvalidTests = TestTypes["invalid"]; +type SuccessTests = TestTypes["valid"]; +type FailureTests = TestTypes["invalid"]; -type TestSet = [string, ValidTests, InvalidTests][]; -type ValidTest = ArrayElementType; -type InvalidTest = ArrayElementType; +type TestSet = [string, SuccessTests, FailureTests][]; +type SuccessTest = ArrayElementType; +type FailureTest = ArrayElementType; -function doTest(title: string, valid: ValidTests, invalid: InvalidTests) { +function doTest(title: string, successTests: SuccessTests, failureTests: FailureTests) { const ruleName = "detect-nondeterministic-calls"; - tester.run(title, dbosRulesPerName[ruleName], { valid: valid, invalid: invalid }); + tester.run(title, dbosRulesPerName[ruleName], { valid: successTests, invalid: failureTests }); } ////////// @@ -36,117 +36,182 @@ const tester = new RuleTester({ defaultFilenames: { ts: "dbos-rules.test.ts", tsx: "this_file_doesnt_exist.tsx" } }); -function makeExpectedDetCode(code: string, params: string = "", aboveClass: string = ""): string { +////////// These functions build different types of test cases with some primitive code structure around them. + +function makeExpectedDetCode( + code: string, + codeAboveClass: string, + enclosingFunctionParams: string): string { + return ` - ${aboveClass} + ${codeAboveClass} + class Foo { @Workflow - bar(${params}) { + foo(${enclosingFunctionParams}) { ${code} } } `; } -function makeCaseForModification(numErrors: number, code: string): InvalidTest { - return { code: code, errors: Array(numErrors).fill({ messageId: "globalModification" }) }; -} +function makeExpectedSuccessTest(code: string, + { codeAboveClass, enclosingFunctionParams } = { codeAboveClass: "", enclosingFunctionParams: "" }): SuccessTest { -function makeCaseForOkayCall(call: string): ValidTest { - return { code: makeExpectedDetCode(`const x = ${call};`) }; + return { code: makeExpectedDetCode(code, codeAboveClass, enclosingFunctionParams) }; } -function makeCaseForBannedCall(prefix: string, functionName: string, params: string): InvalidTest { - return { code: makeExpectedDetCode(`const x = ${prefix} ${functionName}(${params});`), errors: [{ messageId: functionName }] } -} +function makeExpectedFailureTest(code: string, expectedErrorIds: string[], + { codeAboveClass, enclosingFunctionParams } = { codeAboveClass: "", enclosingFunctionParams: "" }): FailureTest { -function makeCaseForOkayAwaitCall(params: string, awaitedUpon: string): ValidTest { - return { code: makeExpectedDetCode(`const x = await ${awaitedUpon};`, params, "class WorkflowContext {}") }; + const inObjectFormat = expectedErrorIds.map((id) => { return { messageId: id }; }); + return { code: makeExpectedDetCode(code, codeAboveClass, enclosingFunctionParams), errors: inObjectFormat }; } -function makeCaseForBannedAwaitCall(params: string, awaitedUpon: string): InvalidTest { - const code = makeExpectedDetCode(`const x = await ${awaitedUpon};`, params, "class FooBar {}"); - return { code: code, errors: [{ messageId: "awaitingOnNotAllowedType" }] }; -} +////////// const testSet: TestSet = [ - ["global modifications", [], [makeCaseForModification(5, -` -let x = 3; -let y = {a: 1, b: 2}; - -class Foo { - @Workflow - foo() { - x = 4; // Not allowed - y.a += 1; // Not allowed - - let y = {a: 3, b: 4}; // Aliases the global 'y' - y.a = 1; // Not a global modification anymore - } - - bar() { - y.b += 2; - let z = 8; - - class Bar { - @Workflow - w() { - z = 9; // Not allowed - } - } - } - - @Workflow - baz() { - x *= 5; // Not allowed - y.b += y.a; // Not allowed - - function bazbaz() { - x -= 6; - y.b += y.a; - } - } -}`)] + ["global mutations", [], + + [makeExpectedFailureTest( + ` + let x = 3; + let y = {a: 1, b: 2}; + + class Bar { + @Workflow + foo() { + x = 4; // Not allowed + y.a += 1; // Not allowed + + let y = {a: 3, b: 4}; // Aliases the global 'y' + y.a = 1; // Not a global modification anymore + } + + bar() { + y.b += 2; + let z = 8; + + class Bar { + @Workflow + w() { + z = 9; // Not allowed + } + } + } + + @Workflow + baz() { + x *= 5; // Not allowed + y.b += y.a; // Not allowed + + function bazbaz() { + x -= 6; + y.b += y.a; + } + } + }`, + Array(5).fill("globalModification") // Expecting 5 errors + )] ], ["banned/not banned functions", [ - makeCaseForOkayCall("foo()"), - makeCaseForOkayCall("Date('December 17, 1995 03:24:00')"), - makeCaseForOkayCall("new Date('December 17, 1995 03:24:00')") + makeExpectedSuccessTest("foo();"), // Calling these `Date` variants is allowed + makeExpectedSuccessTest("Date('December 17, 1995 03:24:00');"), + makeExpectedSuccessTest("new Date('December 17, 1995 03:24:00');") ], [ - makeCaseForBannedCall("", "Date", ""), - makeCaseForBannedCall("new", "Date", ""), - makeCaseForBannedCall("", "Math.random", ""), - makeCaseForBannedCall("", "setTimeout", "a, b"), - makeCaseForBannedCall("", "bcrypt.hash", "a, b, c"), - makeCaseForBannedCall("", "bcrypt.compare", "a, b, c") + /* The secondary args here are the expected error + IDs (which line up with the banned functions tested) */ + makeExpectedFailureTest("Date();", ["Date"]), + makeExpectedFailureTest("new Date();", ["Date"]), + makeExpectedFailureTest("Math.random();", ["Math.random"]), + makeExpectedFailureTest("setTimeout(a, b);", ["setTimeout"]), + makeExpectedFailureTest("bcrypt.hash(a, b, c);", ["bcrypt.hash"]), + makeExpectedFailureTest("bcrypt.compare(a, b, c);", ["bcrypt.compare"]) ] ], ["allowed/not allowed awaits", [ - makeCaseForOkayAwaitCall("", "new Set()"), // TODO: definitely make this not allowed - makeCaseForOkayAwaitCall("", "({}).foo()"), // TODO: probably make this not allowed - - // When you don't await on a `WorkflowContext`, but you pass a param into the function you're calling, it's okay - makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "foo(ctxt); async function foo(bar: WorkflowContext) {return bar.baz();} "), - makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.foo()"), - makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), - makeCaseForOkayAwaitCall("ctxt: WorkflowContext", "ctxt.client('users').select('password').where({ username }).first();") + makeExpectedSuccessTest("await ({}).foo();"), // TODO: probably make this not allowed + makeExpectedSuccessTest("await new Set();"), // TODO: definitely make this not allowed + + // Awaiting on a method with a leftmost `WorkflowContext`, #1 + makeExpectedSuccessTest( + "await ctxt.foo();", + { codeAboveClass: "class WorkflowContext {}", enclosingFunctionParams: "ctxt: WorkflowContext" } + ), + + // Awaiting on a method with a leftmost `WorkflowContext`, #2 + makeExpectedSuccessTest( + "await ctxt.invoke(ShopUtilities).retrieveOrder(order_id);", + { codeAboveClass: "class WorkflowContext {}", enclosingFunctionParams: "ctxt: WorkflowContext" } + ), + + // Awaiting on a method with a leftmost `WorkflowContext`, #3 + makeExpectedSuccessTest( + "await ctxt.client('users').select('password').where({ username }).first();", + { codeAboveClass: "class WorkflowContext {}", enclosingFunctionParams: "ctxt: WorkflowContext" } + ), + + // Awaiting on a leftmost non-`WorkflowContext` type, but you pass a `WorkflowContext` in + makeExpectedSuccessTest( + ` + async function workflowHelperFunction(ctxt: WorkflowContext) { + return await ctxt.baz(); + } + + await workflowHelperFunction(ctxt); + `, + { codeAboveClass: "class WorkflowContext {}", enclosingFunctionParams: "ctxt: WorkflowContext" } + ) ], [ - makeCaseForBannedAwaitCall("", "fetch('https://www.google.com')"), - makeCaseForBannedAwaitCall("", "foo(); async function foo() {return 5;} "), - makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.foo()"), - makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.invoke(ShopUtilities).retrieveOrder(order_id)"), - makeCaseForBannedAwaitCall("ctxt: FooBar", "ctxt.client('users').select('password').where({ username }).first();"), - makeCaseForBannedAwaitCall("ctxt: object", "5; const y = new Set(); await y.foo()") + // Awaiting on a not-allowed function, #1 + makeExpectedFailureTest("await fetch('https://www.google.com');", ["awaitingOnNotAllowedType"]), + + // Awaiting on a not-allowed function, #2 + makeExpectedFailureTest(` + async function foo() { + return 5; + } + + await foo(); + `, + ["awaitingOnNotAllowedType"] + ), + + // Awaiting on a not-allowed class, #1 + makeExpectedFailureTest( + "const x = new Set(); await x.foo();", + ["awaitingOnNotAllowedType"] + ), + + // Awaiting on a not-allowed class, #2 + makeExpectedFailureTest( + "await fooBar.foo();", + ["awaitingOnNotAllowedType"], + { codeAboveClass: "class FooBar {}", enclosingFunctionParams: "fooBar: FooBar" } + ), + + // Awaiting on a not-allowed class, #3 + makeExpectedFailureTest( + "await fooBar.invoke(ShopUtilities).retrieveOrder(order_id);", + ["awaitingOnNotAllowedType"], + { codeAboveClass: "class FooBar {}", enclosingFunctionParams: "fooBar: FooBar" } + ), + + // Awaiting on a not-allowed class, #4 + makeExpectedFailureTest( + "await fooBar.client('users').select('password').where({ username }).first();", + ["awaitingOnNotAllowedType"], + { codeAboveClass: "class FooBar {}", enclosingFunctionParams: "fooBar: FooBar" } + ), ] ] ]; From da93f875eacebb4074d625c40700b3a51daff18c Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 26 Jun 2024 16:42:54 -0700 Subject: [PATCH 45/49] Decapitalize some names --- dbos-rules.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 2462f9f..dfb5e68 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -29,9 +29,9 @@ type GlobalTools = {eslintContext: EslintContext, parserServices: ParserServices let GLOBAL_TOOLS: GlobalTools | undefined = undefined; // These included `Transaction` and `TransactionContext` respectively before! -const DETERMINISTIC_DECORATORS = new Set(["Workflow"]); -const TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS = new Set(["WorkflowContext"]); -const ERROR_MESSAGES = makeErrorMessageSet(); +const deterministicDecorators = new Set(["Workflow"]); +const typesYouCanAwaitUponInDeterministicFunctions = new Set(["WorkflowContext"]); +const errorMessages = makeErrorMessageSet(); ////////// This is the set of error messages that can be emitted @@ -42,7 +42,7 @@ function makeErrorMessageSet(): Map { const bcryptMessage = "Avoid using `bcrypt`, which contains native code. Instead, use `bcryptjs`. \ Also, some `bcrypt` functions generate random data and should only be called from communicators"; - const validTypeSetString = [...TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS].map((name) => `\`${name}\``).join(", "); + const validTypeSetString = [...typesYouCanAwaitUponInDeterministicFunctions].map((name) => `\`${name}\``).join(", "); // The keys are the ids, and the values are the messages themselves return new Map([ @@ -105,7 +105,7 @@ function evaluateClassForDeterminism(theClass: ClassDeclaration) { function functionShouldBeDeterministic(fnDecl: FunctionOrMethod): boolean { return fnDecl.getModifiers().some((modifier) => - Node.isDecorator(modifier) && DETERMINISTIC_DECORATORS.has(modifier.getName()) + Node.isDecorator(modifier) && deterministicDecorators.has(modifier.getName()) ); } @@ -226,7 +226,7 @@ const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { return; } - const validSet = TYPES_YOU_CAN_AWAIT_UPON_IN_DETERERMINISTIC_FUNCTIONS; + const validSet = typesYouCanAwaitUponInDeterministicFunctions; const awaitingOnAllowedType = validSet.has(typeName); if (!awaitingOnAllowedType) { @@ -410,7 +410,7 @@ module.exports = { meta: { type: "suggestion", docs: { description: "Detect nondeterminism in cases where functions should act deterministically" }, - messages: Object.fromEntries(ERROR_MESSAGES) + messages: Object.fromEntries(errorMessages) }, create: (context: EslintContext) => { From fb564cf04f5221f4fe1c9a3d92ea91ae0668ceae Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 26 Jun 2024 16:56:27 -0700 Subject: [PATCH 46/49] Rename some vars and error messages. --- dbos-rules.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index dfb5e68..1160b5a 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -30,7 +30,7 @@ let GLOBAL_TOOLS: GlobalTools | undefined = undefined; // These included `Transaction` and `TransactionContext` respectively before! const deterministicDecorators = new Set(["Workflow"]); -const typesYouCanAwaitUponInDeterministicFunctions = new Set(["WorkflowContext"]); +const awaitableTypes = new Set(["WorkflowContext"]); // Awaitable in deterministic functions, to be specific const errorMessages = makeErrorMessageSet(); ////////// This is the set of error messages that can be emitted @@ -39,15 +39,17 @@ function makeErrorMessageSet(): Map { const makeDateMessage = (bannedCall: string) => `Calling ${bannedCall} is banned \ (consider using \`@dbos-inc/communicator-datetime\` for consistency and testability)`; + // TODO: update this message if more types are added in the future to `deterministicDecorators` or `awaitableTypes` + const awaitMessage = `The enclosing workflow makes an asynchronous call to a non-DBOS function. \ +Please verify that this call is deterministic or it may lead to non-reproducible behavior`; + const bcryptMessage = "Avoid using `bcrypt`, which contains native code. Instead, use `bcryptjs`. \ Also, some `bcrypt` functions generate random data and should only be called from communicators"; - const validTypeSetString = [...typesYouCanAwaitUponInDeterministicFunctions].map((name) => `\`${name}\``).join(", "); - // The keys are the ids, and the values are the messages themselves return new Map([ - ["globalModification", "This is a global modification relative to the workflow declaration"], - ["awaitingOnNotAllowedType", `This function should not await with the current leftmost type (allowed set: \{${validTypeSetString}\})`], + ["globalModification", "Deterministic DBOS operations (e.g. workflow code) should not mutate global variables; it can lead to non-reproducible behavior"], + ["awaitingOnNotAllowedType", awaitMessage], ["Date", makeDateMessage("`Date()` or `new Date()`")], ["Date.now", makeDateMessage("`Date.now()`")], ["Math.random", "Avoid calling `Math.random()` directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`"], @@ -226,14 +228,13 @@ const awaitsOnNotAllowedType: DetChecker = (node, _fn, _isLocal) => { return; } - const validSet = typesYouCanAwaitUponInDeterministicFunctions; - const awaitingOnAllowedType = validSet.has(typeName); + const awaitingOnAllowedType = awaitableTypes.has(typeName); if (!awaitingOnAllowedType) { /* We should be allowed to await if we call a function that passes an allowed type, since that probably means that that function is a helper function which is deterministic and uses our allowed type. */ - if (ignoreAwaitsForCallsWithAContextParam && validTypeExistsInFunctionCallParams(functionCall, validSet)) { + if (ignoreAwaitsForCallsWithAContextParam && validTypeExistsInFunctionCallParams(functionCall, awaitableTypes)) { return; } From 2ab05740e723e8bb7181c23474112fa7c22616a6 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 26 Jun 2024 17:14:34 -0700 Subject: [PATCH 47/49] Now defining ranges for arg counts, instead of a set of allowed arg counts (this works better for variadic functions, like 'console.log' and 'setTimeout') --- dbos-rules.test.ts | 3 ++- dbos-rules.ts | 26 ++++++++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/dbos-rules.test.ts b/dbos-rules.test.ts index cdd597f..d82d9bf 100644 --- a/dbos-rules.test.ts +++ b/dbos-rules.test.ts @@ -128,6 +128,7 @@ const testSet: TestSet = [ makeExpectedFailureTest("Date();", ["Date"]), makeExpectedFailureTest("new Date();", ["Date"]), makeExpectedFailureTest("Math.random();", ["Math.random"]), + makeExpectedFailureTest("console.log(\"Hello!\");", ["console.log"]), makeExpectedFailureTest("setTimeout(a, b);", ["setTimeout"]), makeExpectedFailureTest("bcrypt.hash(a, b, c);", ["bcrypt.hash"]), makeExpectedFailureTest("bcrypt.compare(a, b, c);", ["bcrypt.compare"]) @@ -211,7 +212,7 @@ const testSet: TestSet = [ "await fooBar.client('users').select('password').where({ username }).first();", ["awaitingOnNotAllowedType"], { codeAboveClass: "class FooBar {}", enclosingFunctionParams: "fooBar: FooBar" } - ), + ) ] ] ]; diff --git a/dbos-rules.ts b/dbos-rules.ts index 1160b5a..73f26bd 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -53,6 +53,7 @@ Also, some `bcrypt` functions generate random data and should only be called fro ["Date", makeDateMessage("`Date()` or `new Date()`")], ["Date.now", makeDateMessage("`Date.now()`")], ["Math.random", "Avoid calling `Math.random()` directly; it can lead to non-reproducible behavior. See `@dbos-inc/communicator-random`"], + ["console.log", "Avoid calling `console.log` directly; the DBOS logger, `ctxt.logger.info`, is recommended."], ["setTimeout", "Avoid calling `setTimeout()` directly; it can lead to undesired behavior when debugging"], ["bcrypt.hash", bcryptMessage], ["bcrypt.compare", bcryptMessage] @@ -156,13 +157,18 @@ const mutatesGlobalVariable: DetChecker = (node, _fn, isLocal) => { and mutating global arrays via functions like `push`, etc.? */ const callsBannedFunction: DetChecker = (node, _fn, _isLocal) => { // All of these function names are also keys in `ERROR_MESSAGES` above - const bannedFunctionsWithValidArgCounts: Map> = new Map([ - ["Date", new Set([0])], - ["Date.now", new Set([0])], - ["Math.random", new Set([0])], - ["setTimeout", new Set([1, 2])], - ["bcrypt.hash", new Set([3])], - ["bcrypt.compare", new Set([3])] + + const AS_MANY_ARGS_AS_YOU_WANT = 99999; + type ArgCountRange = {min: number, max: number}; // This range is inclusive + + const bannedFunctionsWithArgCountRanges: Map = new Map([ + ["Date", {min: 0, max: 0}], + ["Date.now", {min: 0, max: 0}], + ["Math.random", {min: 0, max: 0}], + ["console.log", {min: 0, max: AS_MANY_ARGS_AS_YOU_WANT}], + ["setTimeout", {min: 1, max: AS_MANY_ARGS_AS_YOU_WANT}], + ["bcrypt.hash", {min: 3, max: 3}], + ["bcrypt.compare", {min: 3, max: 3}] ]); ////////// @@ -174,12 +180,12 @@ const callsBannedFunction: DetChecker = (node, _fn, _isLocal) => { const kids = expr.getChildren(); const text = (kids.length === 0) ? expr.getText() : kids.map((node) => node.getText()).join(""); - const validArgCounts = bannedFunctionsWithValidArgCounts.get(text); + const argCountRange = bannedFunctionsWithArgCountRanges.get(text); - if (validArgCounts !== undefined) { + if (argCountRange !== undefined) { const argCount = node.getArguments().length; - if (validArgCounts.has(argCount)) { + if (argCount >= argCountRange.min && argCount <= argCountRange.max) { return text; // Returning the function name key } } From 2c0465c5a1158212d827986d68723f46b1a44fc5 Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 26 Jun 2024 17:16:03 -0700 Subject: [PATCH 48/49] Change some comment wording slightly --- dbos-rules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbos-rules.ts b/dbos-rules.ts index 73f26bd..c059fb2 100644 --- a/dbos-rules.ts +++ b/dbos-rules.ts @@ -153,7 +153,7 @@ const mutatesGlobalVariable: DetChecker = (node, _fn, isLocal) => { } } -/* TODO: should I ban IO functions, like `fetch`, `console.log`, +/* TODO: should I ban more IO functions, like `fetch`, and mutating global arrays via functions like `push`, etc.? */ const callsBannedFunction: DetChecker = (node, _fn, _isLocal) => { // All of these function names are also keys in `ERROR_MESSAGES` above From 71b0f3bdd7a5f65043dcb2e91bbfabc31062b9bc Mon Sep 17 00:00:00 2001 From: Caspian Ahlberg Date: Wed, 26 Jun 2024 17:27:15 -0700 Subject: [PATCH 49/49] Add some parens to some workflow decorators --- dbos-rules.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dbos-rules.test.ts b/dbos-rules.test.ts index d82d9bf..2177c35 100644 --- a/dbos-rules.test.ts +++ b/dbos-rules.test.ts @@ -47,7 +47,7 @@ function makeExpectedDetCode( ${codeAboveClass} class Foo { - @Workflow + @Workflow() foo(${enclosingFunctionParams}) { ${code} } @@ -79,7 +79,7 @@ const testSet: TestSet = [ let y = {a: 1, b: 2}; class Bar { - @Workflow + @Workflow() foo() { x = 4; // Not allowed y.a += 1; // Not allowed @@ -93,14 +93,14 @@ const testSet: TestSet = [ let z = 8; class Bar { - @Workflow + @Workflow() w() { z = 9; // Not allowed } } } - @Workflow + @Workflow() baz() { x *= 5; // Not allowed y.b += y.a; // Not allowed