{"version":3,"file":"analyzer.mjs","names":["analyze","issues"],"sources":["../../src/transaction-analyzer/analyzer.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Transaction } from '@mysten/sui/transactions';\nimport type { Defined, Simplify, UnionToIntersection } from '../util.js';\n\n/**\n * Declare a dependency on `analyzer` that does NOT short-circuit the parent when\n * it fails: the parent always runs and receives the dependency's full\n * {@link AnalyzerResult} (so it can inspect `status`/`issues`) rather than the\n * unwrapped result. Sugar for `{ analyzer, required: false, transform: (r) => r }`\n * — see {@link Dependency} for the general (required + custom `transform`) form.\n */\nexport function optional<T extends Defined, Options, Analysis extends Record<string, Defined>>(\n\tanalyzer: Analyzer<T, Options, Analysis>,\n): Dependency<T, AnalyzerResult<T>, Options, Analysis> {\n\treturn {\n\t\tanalyzer,\n\t\trequired: false,\n\t\ttransform: (result) => result,\n\t};\n}\n\nexport function createAnalyzer<\n\tT extends Defined,\n\tDeps extends Record<string, AnalyzerDependency> = {},\n\tOptions = object,\n>({\n\tcacheKey,\n\tdependencies,\n\tanalyze,\n}: {\n\tcacheKey?: unknown;\n\tdependencies?: Deps;\n\tanalyze: (\n\t\toptions: Options,\n\t\ttransaction: Transaction,\n\t) => (analysis: {\n\t\t[k in keyof Deps]: DependencyResult<Deps[k]>;\n\t}) => Promise<AnalyzerOutput<T>> | AnalyzerOutput<T>;\n}) {\n\treturn {\n\t\tcacheKey,\n\t\tdependencies,\n\t\tanalyze: analyze,\n\t} as Analyzer<\n\t\tT,\n\t\tSimplify<\n\t\t\tUnionToIntersection<\n\t\t\t\t| Options\n\t\t\t\t| {\n\t\t\t\t\t\t[k in keyof Deps]: DependencyOptions<Deps[k]>;\n\t\t\t\t  }[keyof Deps]\n\t\t\t>\n\t\t>,\n\t\t{\n\t\t\t[k in keyof Deps]: DependencyResult<Deps[k]>;\n\t\t}\n\t>;\n}\n\ntype OptionsFromAnalyzers<T extends Record<string, Analyzer<Defined, any, any>>> = Simplify<\n\t{\n\t\t[K in keyof T]: T[K] extends Analyzer<Defined, infer O, any> ? O : never;\n\t}[keyof T] & {\n\t\ttransaction: string | Uint8Array;\n\t}\n>;\n\n/** A dependency edge, normalized to its three intrinsic parts. */\ninterface NormalizedDependency {\n\tanalyzer: Analyzer<Defined>;\n\t/** Required deps short-circuit the parent (→ `skipped`) when they produce no result. */\n\trequired: boolean;\n\t/** Maps the dependency's full result to the value the parent sees. */\n\ttransform: (result: AnalyzerResult) => Defined;\n}\n\n/** The unwrapping transform applied to bare-analyzer dependencies by default. */\nfunction unwrapResult(result: AnalyzerResult): Defined {\n\treturn (result as { result?: Defined }).result as Defined;\n}\n\n/** The identity transform applied to optional dependency objects by default. */\nfunction identityResult(result: AnalyzerResult): Defined {\n\treturn result;\n}\n\nfunction isDependency(dep: AnalyzerDependency): dep is Dependency<Defined, Defined, any, any> {\n\treturn 'analyzer' in dep;\n}\n\nfunction normalizeDependency(dep: AnalyzerDependency): NormalizedDependency {\n\tif (isDependency(dep)) {\n\t\tconst required = dep.required ?? true;\n\t\treturn {\n\t\t\tanalyzer: dep.analyzer,\n\t\t\trequired,\n\t\t\ttransform: (dep.transform ?? (required ? unwrapResult : identityResult)) as (\n\t\t\t\tresult: AnalyzerResult,\n\t\t\t) => Defined,\n\t\t};\n\t}\n\n\treturn { analyzer: dep as Analyzer<Defined>, required: true, transform: unwrapResult };\n}\n\nexport async function analyze<T extends Record<string, Analyzer<Defined, any, any>>>(\n\tanalyzers: T,\n\t{ transaction, ...options }: OptionsFromAnalyzers<T>,\n) {\n\tif (Object.prototype.hasOwnProperty.call(analyzers, 'status')) {\n\t\tthrow new Error('Analyzer key \"status\" is reserved for the top-level analysis status');\n\t}\n\n\tconst tx = Transaction.from(transaction);\n\tconst analyzerMap = new Map<\n\t\tunknown,\n\t\t(analysis: object) => AnalyzerOutput | Promise<AnalyzerOutput>\n\t>();\n\n\tfunction initializeAnalyzer(analyzer: Analyzer<Defined>) {\n\t\tconst cacheKey = analyzer.cacheKey ?? analyzer;\n\n\t\tif (!analyzerMap.has(cacheKey)) {\n\t\t\tconst deps: Record<string, AnalyzerDependency> = analyzer.dependencies || {};\n\t\t\tanalyzerMap.set(cacheKey, analyzer.analyze(options, tx));\n\n\t\t\tObject.values(deps).forEach((dep) => initializeAnalyzer(normalizeDependency(dep).analyzer));\n\t\t}\n\n\t\treturn analyzerMap.get(cacheKey)!;\n\t}\n\n\tObject.values(analyzers).forEach((analyzer) => initializeAnalyzer(analyzer));\n\n\tconst analysisMap = new Map<unknown, Promise<AnalyzerResult>>();\n\n\tasync function runAnalyzer(analyzer: Analyzer<Defined>): Promise<AnalyzerResult> {\n\t\tconst deps = await Promise.all(\n\t\t\tObject.entries((analyzer.dependencies || {}) as Record<string, AnalyzerDependency>).map(\n\t\t\t\tasync ([key, dep]) => {\n\t\t\t\t\tconst normalized = normalizeDependency(dep);\n\t\t\t\t\treturn [key, normalized, await getAnalysis(normalized.analyzer)] as const;\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\n\t\tconst analysis: Record<string, Defined> = {};\n\t\tconst inherited = new Set<TransactionAnalysisIssue>();\n\t\tlet missingRequiredDependency = false;\n\n\t\ttry {\n\t\t\tfor (const [key, { required, transform }, result] of deps) {\n\t\t\t\t// Optional deps never gate and never inherit issues: the parent owns the full\n\t\t\t\t// result (default `transform` for `optional()` and explicit optional objects is\n\t\t\t\t// identity) and decides itself.\n\t\t\t\tif (!required) {\n\t\t\t\t\tanalysis[key] = transform(result);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (result.issues) {\n\t\t\t\t\tresult.issues.forEach((issue) => inherited.add(issue));\n\t\t\t\t}\n\n\t\t\t\t// A required dep that produced no result (`failed`/`skipped`) short-circuits the\n\t\t\t\t// parent. `partial` still has a result, so it proceeds — `partial` is only\n\t\t\t\t// meaningful at the outer `analyze()` layer, not at a dependency boundary.\n\t\t\t\tif (result.status === 'success' || result.status === 'partial') {\n\t\t\t\t\tanalysis[key] = transform(result);\n\t\t\t\t} else {\n\t\t\t\t\tmissingRequiredDependency = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingRequiredDependency) {\n\t\t\t\treturn { status: 'skipped', issues: [...inherited], ownIssues: [] };\n\t\t\t}\n\n\t\t\tconst output = await analyzerMap.get(analyzer.cacheKey ?? analyzer)!(analysis);\n\t\t\tconst ownIssues = output.issues ? [...output.issues] : [];\n\t\t\tconst issues = [...new Set([...inherited, ...ownIssues])];\n\n\t\t\t// Intentional `!== undefined` (not truthiness): falsy results (`0`, `''`,\n\t\t\t// `false`, `null`) are valid and must stay `success`/`partial`. Do not refactor\n\t\t\t// to `if (output.result)` — that would silently reclassify them as `failed`.\n\t\t\tif (output.result !== undefined) {\n\t\t\t\tif (issues.length) {\n\t\t\t\t\treturn { status: 'partial', result: output.result, issues, ownIssues };\n\t\t\t\t}\n\t\t\t\treturn { status: 'success', result: output.result };\n\t\t\t}\n\n\t\t\treturn { status: 'failed', issues, ownIssues };\n\t\t} catch (error) {\n\t\t\tconst issue = {\n\t\t\t\tmessage: `Unexpected error while analyzing transaction: ${(error as Error).message}`,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tstatus: 'failed',\n\t\t\t\tissues: [...new Set([...inherited, issue])],\n\t\t\t\townIssues: [issue],\n\t\t\t};\n\t\t}\n\t}\n\n\tfunction getAnalysis(analyzer: Analyzer<Defined>): Promise<AnalyzerResult> {\n\t\tconst cacheKey = analyzer.cacheKey ?? analyzer;\n\n\t\tif (!analysisMap.has(cacheKey)) {\n\t\t\tanalysisMap.set(cacheKey, runAnalyzer(analyzer));\n\t\t}\n\n\t\treturn analysisMap.get(cacheKey)!;\n\t}\n\n\tconst perAnalyzer = Object.fromEntries(\n\t\tawait Promise.all(\n\t\t\tObject.entries(analyzers).map(async ([key, analyzer]) => [key, await getAnalysis(analyzer)]),\n\t\t),\n\t) as {\n\t\t[k in keyof T]: T[k] extends Analyzer<infer R, any, any> ? AnalyzerResult<R> : never;\n\t};\n\n\tconst issues: TransactionAnalysisIssue[] = [];\n\tfor (const pending of analysisMap.values()) {\n\t\tconst result = await pending;\n\t\tif (result.ownIssues) issues.push(...result.ownIssues);\n\t}\n\n\tconst topLevelResults = Object.values(perAnalyzer);\n\tconst hasSuccessfulResult = topLevelResults.some(\n\t\t(result) => result.status === 'success' || result.status === 'partial',\n\t);\n\tconst status: AnalyzeStatus = topLevelResults.every((result) => result.status === 'success')\n\t\t? 'complete'\n\t\t: hasSuccessfulResult\n\t\t\t? 'partial'\n\t\t\t: 'failed';\n\n\treturn { ...perAnalyzer, issues, status };\n}\n\n/**\n * A dependency edge. A bare {@link Analyzer} is sugar for a required edge whose\n * value is the unwrapped result. The explicit object form lets you declare an\n * edge `required: false` (the parent never short-circuits on it) and/or map the\n * dependency's full {@link AnalyzerResult} to a custom value via `transform`.\n * An optional edge without a `transform` receives the full {@link AnalyzerResult}.\n */\nexport interface Dependency<\n\tR extends Defined,\n\tV extends Defined = R,\n\tOptions = object,\n\tAnalysis extends Record<string, Defined> = {},\n> {\n\tanalyzer: Analyzer<R, Options, Analysis>;\n\t/** Whether a failed/skipped dependency short-circuits the parent. Default: `true`. */\n\trequired?: boolean;\n\t/**\n\t * Maps the dependency's full result to the value the parent sees. Default:\n\t * `(r) => r.result` for required edges and `(r) => r` for optional edges.\n\t */\n\ttransform?: (result: AnalyzerResult<R>) => V;\n}\n\nexport type AnalyzerDependency = Analyzer<Defined, any, any> | Dependency<any, Defined, any, any>;\n\ntype DependencyResult<T extends AnalyzerDependency> = T extends {\n\ttransform: (result: AnalyzerResult<any>) => infer V;\n}\n\t? V\n\t: T extends { analyzer: Analyzer<infer R, any, any>; required: false; transform?: undefined }\n\t\t? AnalyzerResult<R>\n\t\t: T extends Dependency<any, infer V, any, any>\n\t\t\t? V\n\t\t\t: T extends Analyzer<infer R, any, any>\n\t\t\t\t? R\n\t\t\t\t: never;\n\ntype DependencyOptions<T extends AnalyzerDependency> =\n\tT extends Dependency<any, any, infer O, any>\n\t\t? O\n\t\t: T extends Analyzer<any, infer O, any>\n\t\t\t? O\n\t\t\t: never;\n\nexport type AnalyzerStatus = 'success' | 'partial' | 'failed' | 'skipped';\n\nexport type AnalyzeStatus = 'complete' | 'partial' | 'failed';\n\nexport type Analyzer<\n\tT extends Defined,\n\tOptions = object,\n\tAnalysis extends Record<string, Defined> = {},\n> = {\n\tcacheKey?: unknown;\n\tdependencies: AnalyzerDependencies<Analysis>;\n\tanalyze: (\n\t\toptions: Options,\n\t\ttransaction: Transaction,\n\t) => (analysis: Analysis) => AnalyzerOutput<T> | Promise<AnalyzerOutput<T>>;\n};\n\ntype IsAny<T> = 0 extends 1 & T ? true : false;\ntype AnalyzerDependencies<Analysis extends Record<string, Defined>> =\n\tIsAny<Analysis> extends true\n\t\t? Record<string, AnalyzerDependency>\n\t\t: { [K in keyof Analysis]: AnalyzerDependency } & Record<string, AnalyzerDependency>;\n\nexport type AnalyzerOutput<T extends Defined = Defined> =\n\t| {\n\t\t\tresult: T;\n\t\t\tissues?: TransactionAnalysisIssue[];\n\t  }\n\t| {\n\t\t\tissues: TransactionAnalysisIssue[];\n\t\t\tresult?: never;\n\t  };\n\nexport type AnalyzerResult<T extends Defined = Defined> =\n\t| {\n\t\t\tstatus: 'success';\n\t\t\tresult: T;\n\t\t\tissues?: never;\n\t\t\townIssues?: never;\n\t  }\n\t| {\n\t\t\tstatus: 'partial';\n\t\t\tresult: T;\n\t\t\tissues: TransactionAnalysisIssue[];\n\t\t\townIssues: TransactionAnalysisIssue[];\n\t  }\n\t| {\n\t\t\tstatus: 'failed';\n\t\t\tresult?: never;\n\t\t\tissues: TransactionAnalysisIssue[];\n\t\t\townIssues: TransactionAnalysisIssue[];\n\t  }\n\t| {\n\t\t\tstatus: 'skipped';\n\t\t\tresult?: never;\n\t\t\tissues: TransactionAnalysisIssue[];\n\t\t\townIssues: [];\n\t  };\n\nexport interface TransactionAnalysisIssue {\n\tmessage: string;\n\terror?: Error;\n}\n"],"mappings":"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"}