diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index c32fc9d0cb..73b1b5c078 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -77,6 +77,8 @@ jobs: with: files: | web/** + e2e/** + sdks/nodejs-client/** packages/** package.json pnpm-lock.yaml @@ -112,7 +114,7 @@ jobs: - name: Web type check if: steps.changed-files.outputs.any_changed == 'true' - working-directory: ./web + working-directory: . run: vp run type-check - name: Web dead code check diff --git a/.vite-hooks/pre-commit b/.vite-hooks/pre-commit index 13bbd81cf6..b78fa886e4 100755 --- a/.vite-hooks/pre-commit +++ b/.vite-hooks/pre-commit @@ -64,36 +64,8 @@ if $web_modified; then echo "Running ESLint on web module" - if git diff --cached --quiet -- 'web/**/*.ts' 'web/**/*.tsx'; then - web_ts_modified=false - else - ts_diff_status=$? - if [ $ts_diff_status -eq 1 ]; then - web_ts_modified=true - else - echo "Unable to determine staged TypeScript changes (git exit code: $ts_diff_status)." - exit $ts_diff_status - fi - fi - cd ./web || exit 1 vp staged - if $web_ts_modified; then - echo "Running TypeScript type-check:tsgo" - if ! npm run type-check:tsgo; then - echo "Type check failed. Please run 'npm run type-check:tsgo' to fix the errors." - exit 1 - fi - else - echo "No staged TypeScript changes detected, skipping type-check:tsgo" - fi - - echo "Running knip" - if ! npm run knip; then - echo "Knip check failed. Please run 'npm run knip' to fix the errors." - exit 1 - fi - cd ../ fi diff --git a/e2e/package.json b/e2e/package.json index 925418f223..fb0705ab79 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -11,9 +11,11 @@ "e2e:install": "playwright install --with-deps chromium", "e2e:middleware:down": "tsx ./scripts/setup.ts middleware-down", "e2e:middleware:up": "tsx ./scripts/setup.ts middleware-up", - "e2e:reset": "tsx ./scripts/setup.ts reset" + "e2e:reset": "tsx ./scripts/setup.ts reset", + "type-check": "tsc" }, "devDependencies": { + "@dify/tsconfig": "workspace:*", "@cucumber/cucumber": "catalog:", "@playwright/test": "catalog:", "@types/node": "catalog:", diff --git a/e2e/scripts/run-cucumber.ts b/e2e/scripts/run-cucumber.ts index 39e9157916..1d5229742c 100644 --- a/e2e/scripts/run-cucumber.ts +++ b/e2e/scripts/run-cucumber.ts @@ -17,12 +17,10 @@ const parseArgs = (argv: string[]): RunOptions => { let headed = false const forwardArgs: string[] = [] - for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index] - + for (const [index, arg] of argv.entries()) { if (arg === '--') { forwardArgs.push(...argv.slice(index + 1)) - break + return { forwardArgs, full, headed } } if (arg === '--full') { @@ -38,11 +36,7 @@ const parseArgs = (argv: string[]): RunOptions => { forwardArgs.push(arg) } - return { - forwardArgs, - full, - headed, - } + return { forwardArgs, full, headed } } const hasCustomTags = (forwardArgs: string[]) => diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json index 3976c12b66..fc729f0ad0 100644 --- a/e2e/tsconfig.json +++ b/e2e/tsconfig.json @@ -1,16 +1,9 @@ { + "extends": "@dify/tsconfig/node.json", "compilerOptions": { - "target": "ES2023", "lib": ["ES2023", "DOM"], - "module": "ESNext", - "moduleResolution": "Bundler", "allowJs": false, - "resolveJsonModule": true, - "noEmit": true, - "strict": true, - "skipLibCheck": true, "types": ["node", "@playwright/test", "@cucumber/cucumber"], - "isolatedModules": true, "verbatimModuleSyntax": true }, "include": ["./**/*.ts"], diff --git a/package.json b/package.json index 736a354ef7..47c8c170e5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "dify", "private": true, "scripts": { - "prepare": "vp config" + "prepare": "vp config", + "type-check": "vp run -r type-check" }, "devDependencies": { "vite": "catalog:", diff --git a/packages/dify-ui/package.json b/packages/dify-ui/package.json index d8314a6be3..ef731aec89 100644 --- a/packages/dify-ui/package.json +++ b/packages/dify-ui/package.json @@ -19,6 +19,11 @@ "tailwind-merge": "catalog:" }, "devDependencies": { - "tailwindcss": "catalog:" + "@dify/tsconfig": "workspace:*", + "tailwindcss": "catalog:", + "typescript": "catalog:" + }, + "scripts": { + "type-check": "tsc" } } diff --git a/packages/dify-ui/tsconfig.json b/packages/dify-ui/tsconfig.json index 3e912baba0..243fc436d2 100644 --- a/packages/dify-ui/tsconfig.json +++ b/packages/dify-ui/tsconfig.json @@ -1,11 +1,7 @@ { + "extends": "@dify/tsconfig/base.json", "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, + "noEmit": false, "declaration": true, "declarationMap": true, "sourceMap": true, diff --git a/packages/migrate-no-unchecked-indexed-access/package.json b/packages/migrate-no-unchecked-indexed-access/package.json index 781e88b0e8..cea9dcde60 100644 --- a/packages/migrate-no-unchecked-indexed-access/package.json +++ b/packages/migrate-no-unchecked-indexed-access/package.json @@ -7,12 +7,14 @@ "migrate-no-unchecked-indexed-access": "./bin/migrate-no-unchecked-indexed-access.js" }, "scripts": { - "build": "vp pack" + "build": "vp pack", + "type-check": "tsc" }, "dependencies": { "typescript": "catalog:" }, "devDependencies": { + "@dify/tsconfig": "workspace:*", "@types/node": "catalog:", "vite": "catalog:", "vite-plus": "catalog:" diff --git a/packages/migrate-no-unchecked-indexed-access/src/cli.ts b/packages/migrate-no-unchecked-indexed-access/src/cli.ts index fa52510905..99142c388f 100644 --- a/packages/migrate-no-unchecked-indexed-access/src/cli.ts +++ b/packages/migrate-no-unchecked-indexed-access/src/cli.ts @@ -34,7 +34,8 @@ let exitCode = 0 try { await main() - exitCode = process.exitCode ?? 0 + const currentExitCode = process.exitCode + exitCode = typeof currentExitCode === 'number' ? currentExitCode : 0 } catch (error) { console.error(error instanceof Error ? error.message : error) diff --git a/packages/migrate-no-unchecked-indexed-access/tsconfig.json b/packages/migrate-no-unchecked-indexed-access/tsconfig.json new file mode 100644 index 0000000000..aeb24e1df5 --- /dev/null +++ b/packages/migrate-no-unchecked-indexed-access/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@dify/tsconfig/node.json" +} diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json new file mode 100644 index 0000000000..707f1aff56 --- /dev/null +++ b/packages/tsconfig/base.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "verbatimModuleSyntax": true, + + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + "module": "preserve", + "noEmit": true + } +} diff --git a/packages/tsconfig/nextjs.json b/packages/tsconfig/nextjs.json new file mode 100644 index 0000000000..81c6436a97 --- /dev/null +++ b/packages/tsconfig/nextjs.json @@ -0,0 +1,10 @@ +{ + "extends": "./web.json", + "compilerOptions": { + "plugins": [ + { + "name": "next" + } + ] + } +} diff --git a/packages/tsconfig/node.json b/packages/tsconfig/node.json new file mode 100644 index 0000000000..832dab2b09 --- /dev/null +++ b/packages/tsconfig/node.json @@ -0,0 +1,7 @@ +{ + "extends": "./base.json", + "compilerOptions": { + "lib": ["es2022"], + "types": ["node"] + } +} diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json new file mode 100644 index 0000000000..52cafc5bb3 --- /dev/null +++ b/packages/tsconfig/package.json @@ -0,0 +1,11 @@ +{ + "name": "@dify/tsconfig", + "version": "0.0.0-private", + "private": true, + "exports": { + "./base.json": "./base.json", + "./nextjs.json": "./nextjs.json", + "./node.json": "./node.json", + "./web.json": "./web.json" + } +} diff --git a/packages/tsconfig/web.json b/packages/tsconfig/web.json new file mode 100644 index 0000000000..9f3ba7c121 --- /dev/null +++ b/packages/tsconfig/web.json @@ -0,0 +1,7 @@ +{ + "extends": "./base.json", + "compilerOptions": { + "jsx": "react-jsx", + "lib": ["es2022", "dom", "dom.iterable"] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdf5001e2d..2301b5b281 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -586,6 +586,9 @@ importers: '@cucumber/cucumber': specifier: 'catalog:' version: 12.8.0 + '@dify/tsconfig': + specifier: workspace:* + version: link:../packages/tsconfig '@playwright/test': specifier: 'catalog:' version: 1.59.1 @@ -614,9 +617,15 @@ importers: specifier: 'catalog:' version: 3.5.0 devDependencies: + '@dify/tsconfig': + specifier: workspace:* + version: link:../tsconfig tailwindcss: specifier: 'catalog:' version: 4.2.2 + typescript: + specifier: 'catalog:' + version: 6.0.2 packages/iconify-collections: devDependencies: @@ -630,6 +639,9 @@ importers: specifier: 'catalog:' version: 6.0.2 devDependencies: + '@dify/tsconfig': + specifier: workspace:* + version: link:../tsconfig '@types/node': specifier: 'catalog:' version: 25.6.0 @@ -640,8 +652,13 @@ importers: specifier: 'catalog:' version: 0.1.18(@types/node@25.6.0)(@vitest/coverage-v8@4.1.4)(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(happy-dom@20.9.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3) + packages/tsconfig: {} + sdks/nodejs-client: devDependencies: + '@dify/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig '@eslint/js': specifier: 'catalog:' version: 10.0.1(eslint@10.2.0(jiti@2.6.1)) @@ -991,6 +1008,9 @@ importers: '@dify/iconify-collections': specifier: workspace:* version: link:../packages/iconify-collections + '@dify/tsconfig': + specifier: workspace:* + version: link:../packages/tsconfig '@egoist/tailwindcss-icons': specifier: 'catalog:' version: 1.9.2(tailwindcss@4.2.2) diff --git a/sdks/nodejs-client/package.json b/sdks/nodejs-client/package.json index e058edb0ca..28ebcb89c2 100644 --- a/sdks/nodejs-client/package.json +++ b/sdks/nodejs-client/package.json @@ -48,13 +48,14 @@ "build": "vp pack", "lint": "eslint", "lint:fix": "eslint --fix", - "type-check": "tsc -p tsconfig.json --noEmit", + "type-check": "tsc", "test": "vp test", "test:coverage": "vp test --coverage", "publish:check": "./scripts/publish.sh --dry-run", "publish:npm": "./scripts/publish.sh" }, "devDependencies": { + "@dify/tsconfig": "workspace:*", "@eslint/js": "catalog:", "@types/node": "catalog:", "@typescript-eslint/eslint-plugin": "catalog:", diff --git a/sdks/nodejs-client/src/http/sse.test.ts b/sdks/nodejs-client/src/http/sse.test.ts index 70cd11007d..83cde28de3 100644 --- a/sdks/nodejs-client/src/http/sse.test.ts +++ b/sdks/nodejs-client/src/http/sse.test.ts @@ -14,8 +14,8 @@ describe("sse parsing", () => { events.push(event); } expect(events).toHaveLength(1); - expect(events[0].event).toBe("message"); - expect(events[0].data).toEqual({ answer: "hi" }); + expect(events[0]!.event).toBe("message"); + expect(events[0]!.data).toEqual({ answer: "hi" }); }); it("handles multi-line data payloads", async () => { @@ -24,8 +24,8 @@ describe("sse parsing", () => { for await (const event of parseSseStream(stream)) { events.push(event); } - expect(events[0].raw).toBe("line1\nline2"); - expect(events[0].data).toBe("line1\nline2"); + expect(events[0]!.raw).toBe("line1\nline2"); + expect(events[0]!.data).toBe("line1\nline2"); }); it("ignores comments and flushes the last event without a trailing separator", async () => { diff --git a/sdks/nodejs-client/src/index.test.ts b/sdks/nodejs-client/src/index.test.ts index d194680379..8d56b994c4 100644 --- a/sdks/nodejs-client/src/index.test.ts +++ b/sdks/nodejs-client/src/index.test.ts @@ -99,7 +99,7 @@ describe("File uploads", () => { super(); } - _read() {} + override _read() {} append() {} diff --git a/sdks/nodejs-client/tsconfig.json b/sdks/nodejs-client/tsconfig.json index 46055447be..1e55007ed0 100644 --- a/sdks/nodejs-client/tsconfig.json +++ b/sdks/nodejs-client/tsconfig.json @@ -1,18 +1,14 @@ { + "extends": "@dify/tsconfig/node.json", "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "Bundler", + "lib": ["ES2023", "DOM", "DOM.Iterable"], "rootDir": ".", "outDir": "dist", + "noEmit": false, "declaration": true, "declarationMap": true, "sourceMap": true, - "strict": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true, "types": ["node"] }, - "include": ["src/**/*.ts", "tests/**/*.ts"] + "include": ["src/**/*.ts", "tests/**/*.ts", "vite.config.ts"] } diff --git a/web/__tests__/instrumentation-client.spec.ts b/web/__tests__/instrumentation-client.spec.ts deleted file mode 100644 index f767b80826..0000000000 --- a/web/__tests__/instrumentation-client.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Tests for Array.prototype.toSpliced polyfill - */ - -describe('toSpliced polyfill', () => { - let originalToSpliced: typeof Array.prototype.toSpliced - - beforeEach(() => { - // Save original method - originalToSpliced = Array.prototype.toSpliced - }) - - afterEach(() => { - // Restore original method - // eslint-disable-next-line no-extend-native - Array.prototype.toSpliced = originalToSpliced - }) - - const applyPolyfill = () => { - // @ts-expect-error - intentionally deleting for test - delete Array.prototype.toSpliced - - if (!Array.prototype.toSpliced) { - // eslint-disable-next-line no-extend-native - Array.prototype.toSpliced = function (this: T[], start: number, deleteCount?: number, ...items: T[]): T[] { - const copy = this.slice() - copy.splice(start, deleteCount ?? copy.length - start, ...items) - return copy - } - } - } - - it('should add toSpliced method when not available', () => { - applyPolyfill() - expect(typeof Array.prototype.toSpliced).toBe('function') - }) - - it('should return a new array without modifying the original', () => { - applyPolyfill() - const arr = [1, 2, 3, 4, 5] - const result = arr.toSpliced(1, 2) - - expect(result).toEqual([1, 4, 5]) - expect(arr).toEqual([1, 2, 3, 4, 5]) // original unchanged - }) - - it('should insert items at the specified position', () => { - applyPolyfill() - const arr: (number | string)[] = [1, 2, 3] - const result = arr.toSpliced(1, 0, 'a', 'b') - - expect(result).toEqual([1, 'a', 'b', 2, 3]) - }) - - it('should replace items at the specified position', () => { - applyPolyfill() - const arr: (number | string)[] = [1, 2, 3, 4, 5] - const result = arr.toSpliced(1, 2, 'a', 'b') - - expect(result).toEqual([1, 'a', 'b', 4, 5]) - }) - - it('should handle negative start index', () => { - applyPolyfill() - const arr = [1, 2, 3, 4, 5] - const result = arr.toSpliced(-2, 1) - - expect(result).toEqual([1, 2, 3, 5]) - }) - - it('should delete to end when deleteCount is omitted', () => { - applyPolyfill() - const arr = [1, 2, 3, 4, 5] - const result = arr.toSpliced(2) - - expect(result).toEqual([1, 2]) - }) -}) diff --git a/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx b/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx index 40c32b7dd6..ae555a872b 100644 --- a/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx @@ -258,7 +258,8 @@ describe('CreateFromDSLModal', () => { expect(getCreateButton())!.toBeDisabled() }) - ahooksMocks.handlers.findLast(item => Array.isArray(item.keys))?.handler() + const latestHandlerAfterRemove = [...ahooksMocks.handlers].reverse().find(item => Array.isArray(item.keys)) + latestHandlerAfterRemove?.handler() expect(mockImportDSL).not.toHaveBeenCalled() }) @@ -387,7 +388,8 @@ describe('CreateFromDSLModal', () => { ) expect(screen.getByText('apps-full'))!.toBeInTheDocument() - ahooksMocks.handlers.findLast(item => Array.isArray(item.keys))?.handler() + const latestPlanLimitHandler = [...ahooksMocks.handlers].reverse().find(item => Array.isArray(item.keys)) + latestPlanLimitHandler?.handler() expect(mockImportDSL).toHaveBeenCalledTimes(1) }) diff --git a/web/app/components/base/error-boundary/index.tsx b/web/app/components/base/error-boundary/index.tsx index 607494eab5..21395a57c9 100644 --- a/web/app/components/base/error-boundary/index.tsx +++ b/web/app/components/base/error-boundary/index.tsx @@ -67,7 +67,7 @@ class ErrorBoundaryInner extends React.Component< } } - componentDidCatch(error: Error, errorInfo: ErrorInfo) { + override componentDidCatch(error: Error, errorInfo: ErrorInfo) { if (IS_DEV) { console.error('ErrorBoundary caught an error:', error) console.error('Error Info:', errorInfo) @@ -82,7 +82,7 @@ class ErrorBoundaryInner extends React.Component< this.props.onError(error, errorInfo) } - componentDidUpdate(prevProps: any) { + override componentDidUpdate(prevProps: any) { const { resetKeys, resetOnPropsChange } = this.props const { hasError } = this.state @@ -98,7 +98,7 @@ class ErrorBoundaryInner extends React.Component< this.props.onResetKeysChange(prevProps.resetKeys) } - render() { + override render() { const { hasError, error, errorInfo, errorCount } = this.state const { fallback, diff --git a/web/app/components/base/markdown/error-boundary.tsx b/web/app/components/base/markdown/error-boundary.tsx index fd11af5b1c..8331e72622 100644 --- a/web/app/components/base/markdown/error-boundary.tsx +++ b/web/app/components/base/markdown/error-boundary.tsx @@ -17,12 +17,12 @@ export default class ErrorBoundary extends Component { this.state = { hasError: false } } - componentDidCatch(error: any, errorInfo: any) { + override componentDidCatch(error: any, errorInfo: any) { this.setState({ hasError: true }) console.error(error, errorInfo) } - render() { + override render() { // eslint-disable-next-line ts/ban-ts-comment // @ts-expect-error if (this.state.hasError) { diff --git a/web/app/components/base/prompt-editor/plugins/context-block/node.tsx b/web/app/components/base/prompt-editor/plugins/context-block/node.tsx index 958415d843..6e67ee0e7d 100644 --- a/web/app/components/base/prompt-editor/plugins/context-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/context-block/node.tsx @@ -10,15 +10,15 @@ export class ContextBlockNode extends DecoratorNode { __onAddContext: () => void __canNotAddContext: boolean - static getType(): string { + static override getType(): string { return 'context-block' } - static clone(node: ContextBlockNode): ContextBlockNode { + static override clone(node: ContextBlockNode): ContextBlockNode { return new ContextBlockNode(node.__datasets, node.__onAddContext, node.getKey(), node.__canNotAddContext) } - isInline(): boolean { + override isInline(): boolean { return true } @@ -30,17 +30,17 @@ export class ContextBlockNode extends DecoratorNode { this.__canNotAddContext = canNotAddContext || false } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'items-center', 'align-middle') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return ( { return self.__canNotAddContext } - static importJSON(serializedNode: SerializedNode): ContextBlockNode { + static override importJSON(serializedNode: SerializedNode): ContextBlockNode { const node = $createContextBlockNode(serializedNode.datasets, serializedNode.onAddContext, serializedNode.canNotAddContext) return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { return { type: 'context-block', version: 1, @@ -85,7 +85,7 @@ export class ContextBlockNode extends DecoratorNode { } } - getTextContent(): string { + override getTextContent(): string { return '{{#context#}}' } } diff --git a/web/app/components/base/prompt-editor/plugins/current-block/node.tsx b/web/app/components/base/prompt-editor/plugins/current-block/node.tsx index 916e3b5169..f206c48cee 100644 --- a/web/app/components/base/prompt-editor/plugins/current-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/current-block/node.tsx @@ -7,15 +7,15 @@ type SerializedNode = SerializedLexicalNode & { generatorType: GeneratorType } export class CurrentBlockNode extends DecoratorNode { __generatorType: GeneratorType - static getType(): string { + static override getType(): string { return 'current-block' } - static clone(node: CurrentBlockNode): CurrentBlockNode { + static override clone(node: CurrentBlockNode): CurrentBlockNode { return new CurrentBlockNode(node.__generatorType, node.getKey()) } - isInline(): boolean { + override isInline(): boolean { return true } @@ -25,17 +25,17 @@ export class CurrentBlockNode extends DecoratorNode { this.__generatorType = generatorType } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'items-center', 'align-middle') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return ( { return self.__generatorType } - static importJSON(serializedNode: SerializedNode): CurrentBlockNode { + static override importJSON(serializedNode: SerializedNode): CurrentBlockNode { const node = $createCurrentBlockNode(serializedNode.generatorType) return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { return { type: 'current-block', version: 1, @@ -63,7 +63,7 @@ export class CurrentBlockNode extends DecoratorNode { } } - getTextContent(): string { + override getTextContent(): string { return '{{#current#}}' } } diff --git a/web/app/components/base/prompt-editor/plugins/custom-text/node.tsx b/web/app/components/base/prompt-editor/plugins/custom-text/node.tsx index 29c2d4ddb4..e4f774f04e 100644 --- a/web/app/components/base/prompt-editor/plugins/custom-text/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/custom-text/node.tsx @@ -2,11 +2,11 @@ import type { EditorConfig, SerializedTextNode } from 'lexical' import { $createTextNode, TextNode } from 'lexical' export class CustomTextNode extends TextNode { - static getType() { + static override getType() { return 'custom-text' } - static clone(node: CustomTextNode) { + static override clone(node: CustomTextNode) { return new CustomTextNode(node.__text, node.__key) } @@ -14,12 +14,12 @@ export class CustomTextNode extends TextNode { // super(text, key) // } - createDOM(config: EditorConfig) { + override createDOM(config: EditorConfig) { const dom = super.createDOM(config) return dom } - static importJSON(serializedNode: SerializedTextNode): TextNode { + static override importJSON(serializedNode: SerializedTextNode): TextNode { const node = $createTextNode(serializedNode.text) node.setFormat(serializedNode.format) node.setDetail(serializedNode.detail) @@ -28,7 +28,7 @@ export class CustomTextNode extends TextNode { return node } - exportJSON(): SerializedTextNode { + override exportJSON(): SerializedTextNode { return { detail: this.getDetail(), format: this.getFormat(), @@ -40,7 +40,7 @@ export class CustomTextNode extends TextNode { } } - isSimpleText() { + override isSimpleText() { return ( (this.__type === 'text' || this.__type === 'custom-text') && this.__mode === 0) } diff --git a/web/app/components/base/prompt-editor/plugins/error-message-block/node.tsx b/web/app/components/base/prompt-editor/plugins/error-message-block/node.tsx index d253cf7ff3..86d5a4103a 100644 --- a/web/app/components/base/prompt-editor/plugins/error-message-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/error-message-block/node.tsx @@ -5,15 +5,15 @@ import ErrorMessageBlockComponent from './component' type SerializedNode = SerializedLexicalNode export class ErrorMessageBlockNode extends DecoratorNode { - static getType(): string { + static override getType(): string { return 'error-message-block' } - static clone(node: ErrorMessageBlockNode): ErrorMessageBlockNode { + static override clone(node: ErrorMessageBlockNode): ErrorMessageBlockNode { return new ErrorMessageBlockNode(node.getKey()) } - isInline(): boolean { + override isInline(): boolean { return true } @@ -21,17 +21,17 @@ export class ErrorMessageBlockNode extends DecoratorNode { super(key) } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'items-center', 'align-middle') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return ( { ) } - static importJSON(): ErrorMessageBlockNode { + static override importJSON(): ErrorMessageBlockNode { const node = $createErrorMessageBlockNode() return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { return { type: 'error-message-block', version: 1, } } - getTextContent(): string { + override getTextContent(): string { return '{{#error_message#}}' } } diff --git a/web/app/components/base/prompt-editor/plugins/history-block/node.tsx b/web/app/components/base/prompt-editor/plugins/history-block/node.tsx index 85a4de2696..9a363303bf 100644 --- a/web/app/components/base/prompt-editor/plugins/history-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/history-block/node.tsx @@ -9,11 +9,11 @@ export class HistoryBlockNode extends DecoratorNode { __roleName: RoleName __onEditRole: () => void - static getType(): string { + static override getType(): string { return 'history-block' } - static clone(node: HistoryBlockNode): HistoryBlockNode { + static override clone(node: HistoryBlockNode): HistoryBlockNode { return new HistoryBlockNode(node.__roleName, node.__onEditRole, node.__key) } @@ -24,21 +24,21 @@ export class HistoryBlockNode extends DecoratorNode { this.__onEditRole = onEditRole } - isInline(): boolean { + override isInline(): boolean { return true } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'items-center', 'align-middle') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return ( { return self.__onEditRole } - static importJSON(serializedNode: SerializedNode): HistoryBlockNode { + static override importJSON(serializedNode: SerializedNode): HistoryBlockNode { const node = $createHistoryBlockNode(serializedNode.roleName, serializedNode.onEditRole) return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { return { type: 'history-block', version: 1, @@ -75,7 +75,7 @@ export class HistoryBlockNode extends DecoratorNode { } } - getTextContent(): string { + override getTextContent(): string { return '{{#histories#}}' } } diff --git a/web/app/components/base/prompt-editor/plugins/hitl-input-block/node.tsx b/web/app/components/base/prompt-editor/plugins/hitl-input-block/node.tsx index 9f2d25b446..592ecf1f6c 100644 --- a/web/app/components/base/prompt-editor/plugins/hitl-input-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/hitl-input-block/node.tsx @@ -37,7 +37,7 @@ export class HITLInputNode extends DecoratorNode { __ragVariables?: Var[] __readonly?: boolean - isIsolated(): boolean { + override isIsolated(): boolean { return true // This is necessary for drag-and-drop to work correctly } @@ -45,7 +45,7 @@ export class HITLInputNode extends DecoratorNode { return true // This is necessary for drag-and-drop to work correctly } - static getType(): string { + static override getType(): string { return 'hitl-input-block' } @@ -109,7 +109,7 @@ export class HITLInputNode extends DecoratorNode { return self.__readonly || false } - static clone(node: HITLInputNode): HITLInputNode { + static override clone(node: HITLInputNode): HITLInputNode { return new HITLInputNode( node.__variableName, node.__nodeId, @@ -127,7 +127,7 @@ export class HITLInputNode extends DecoratorNode { ) } - isInline(): boolean { + override isInline(): boolean { return true } @@ -162,17 +162,17 @@ export class HITLInputNode extends DecoratorNode { this.__readonly = readonly } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'w-[calc(100%-1px)]', 'items-center', 'align-middle', 'support-drag') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return ( { ) } - static importJSON(serializedNode: SerializedNode): HITLInputNode { + static override importJSON(serializedNode: SerializedNode): HITLInputNode { const node = $createHITLInputNode( serializedNode.variableName, serializedNode.nodeId, @@ -211,7 +211,7 @@ export class HITLInputNode extends DecoratorNode { return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { return { type: 'hitl-input-block', version: 1, @@ -230,7 +230,7 @@ export class HITLInputNode extends DecoratorNode { } } - getTextContent(): string { + override getTextContent(): string { return `{{#$output.${this.getVariableName()}#}}` } } diff --git a/web/app/components/base/prompt-editor/plugins/last-run-block/node.tsx b/web/app/components/base/prompt-editor/plugins/last-run-block/node.tsx index 0606adbe2e..a968638958 100644 --- a/web/app/components/base/prompt-editor/plugins/last-run-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/last-run-block/node.tsx @@ -5,15 +5,15 @@ import LastRunBlockComponent from './component' type SerializedNode = SerializedLexicalNode export class LastRunBlockNode extends DecoratorNode { - static getType(): string { + static override getType(): string { return 'last-run-block' } - static clone(node: LastRunBlockNode): LastRunBlockNode { + static override clone(node: LastRunBlockNode): LastRunBlockNode { return new LastRunBlockNode(node.getKey()) } - isInline(): boolean { + override isInline(): boolean { return true } @@ -21,17 +21,17 @@ export class LastRunBlockNode extends DecoratorNode { super(key) } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'items-center', 'align-middle') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return ( { ) } - static importJSON(): LastRunBlockNode { + static override importJSON(): LastRunBlockNode { const node = $createLastRunBlockNode() return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { return { type: 'last-run-block', version: 1, } } - getTextContent(): string { + override getTextContent(): string { return '{{#last_run#}}' } } diff --git a/web/app/components/base/prompt-editor/plugins/query-block/node.tsx b/web/app/components/base/prompt-editor/plugins/query-block/node.tsx index 519d6bab92..394970d625 100644 --- a/web/app/components/base/prompt-editor/plugins/query-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/query-block/node.tsx @@ -5,46 +5,46 @@ import QueryBlockComponent from './component' type SerializedNode = SerializedLexicalNode export class QueryBlockNode extends DecoratorNode { - static getType(): string { + static override getType(): string { return 'query-block' } - static clone(): QueryBlockNode { + static override clone(): QueryBlockNode { return new QueryBlockNode() } - isInline(): boolean { + override isInline(): boolean { return true } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'items-center', 'align-middle') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return } - static importJSON(): QueryBlockNode { + static override importJSON(): QueryBlockNode { const node = $createQueryBlockNode() return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { return { type: 'query-block', version: 1, } } - getTextContent(): string { + override getTextContent(): string { return '{{#query#}}' } } diff --git a/web/app/components/base/prompt-editor/plugins/request-url-block/node.tsx b/web/app/components/base/prompt-editor/plugins/request-url-block/node.tsx index b24e653bbc..e3a8e9dd29 100644 --- a/web/app/components/base/prompt-editor/plugins/request-url-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/request-url-block/node.tsx @@ -5,46 +5,46 @@ import RequestURLBlockComponent from './component' type SerializedNode = SerializedLexicalNode export class RequestURLBlockNode extends DecoratorNode { - static getType(): string { + static override getType(): string { return 'request-url-block' } - static clone(node: RequestURLBlockNode): RequestURLBlockNode { + static override clone(node: RequestURLBlockNode): RequestURLBlockNode { return new RequestURLBlockNode(node.__key) } - isInline(): boolean { + override isInline(): boolean { return true } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'items-center', 'align-middle') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return } - static importJSON(): RequestURLBlockNode { + static override importJSON(): RequestURLBlockNode { const node = $createRequestURLBlockNode() return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { return { type: 'request-url-block', version: 1, } } - getTextContent(): string { + override getTextContent(): string { return '{{#url#}}' } } diff --git a/web/app/components/base/prompt-editor/plugins/variable-value-block/node.tsx b/web/app/components/base/prompt-editor/plugins/variable-value-block/node.tsx index e83dd22fd4..4fd2ef7239 100644 --- a/web/app/components/base/prompt-editor/plugins/variable-value-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/variable-value-block/node.tsx @@ -9,11 +9,11 @@ import { } from 'lexical' export class VariableValueBlockNode extends TextNode { - static getType(): string { + static override getType(): string { return 'variable-value-block' } - static clone(node: VariableValueBlockNode): VariableValueBlockNode { + static override clone(node: VariableValueBlockNode): VariableValueBlockNode { return new VariableValueBlockNode(node.__text, node.__key) } @@ -21,13 +21,13 @@ export class VariableValueBlockNode extends TextNode { // super(text, key) // } - createDOM(config: EditorConfig): HTMLElement { + override createDOM(config: EditorConfig): HTMLElement { const element = super.createDOM(config) element.classList.add('inline-flex', 'items-center', 'px-0.5', 'h-[22px]', 'text-text-accent', 'rounded-[5px]', 'align-middle') return element } - static importJSON(serializedNode: SerializedTextNode): TextNode { + static override importJSON(serializedNode: SerializedTextNode): TextNode { const node = $createVariableValueBlockNode(serializedNode.text) node.setFormat(serializedNode.format) node.setDetail(serializedNode.detail) @@ -36,7 +36,7 @@ export class VariableValueBlockNode extends TextNode { return node } - exportJSON(): SerializedTextNode { + override exportJSON(): SerializedTextNode { return { detail: this.getDetail(), format: this.getFormat(), @@ -48,7 +48,7 @@ export class VariableValueBlockNode extends TextNode { } } - canInsertTextBefore(): boolean { + override canInsertTextBefore(): boolean { return false } } diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx index 2d13627b20..20fc7c6e79 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx @@ -19,11 +19,11 @@ export class WorkflowVariableBlockNode extends DecoratorNode __getVarType?: GetVarType __availableVariables?: NodeOutPutVar[] - static getType(): string { + static override getType(): string { return 'workflow-variable-block' } - static clone(node: WorkflowVariableBlockNode): WorkflowVariableBlockNode { + static override clone(node: WorkflowVariableBlockNode): WorkflowVariableBlockNode { return new WorkflowVariableBlockNode( node.__variables, node.__workflowNodesMap, @@ -33,7 +33,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode ) } - isInline(): boolean { + override isInline(): boolean { return true } @@ -52,17 +52,17 @@ export class WorkflowVariableBlockNode extends DecoratorNode this.__availableVariables = availableVariables } - createDOM(): HTMLElement { + override createDOM(): HTMLElement { const div = document.createElement('div') div.classList.add('inline-flex', 'items-center', 'align-middle') return div } - updateDOM(): false { + override updateDOM(): false { return false } - decorate(): React.JSX.Element { + override decorate(): React.JSX.Element { return ( ) } - static importJSON(serializedNode: SerializedNode): WorkflowVariableBlockNode { + static override importJSON(serializedNode: SerializedNode): WorkflowVariableBlockNode { const node = $createWorkflowVariableBlockNode( serializedNode.variables, serializedNode.workflowNodesMap, @@ -85,7 +85,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode return node } - exportJSON(): SerializedNode { + override exportJSON(): SerializedNode { const json: SerializedNode = { type: 'workflow-variable-block', version: 1, @@ -119,7 +119,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode return self.__availableVariables } - getTextContent(): string { + override getTextContent(): string { return `{{#${this.getVariables().join('.')}#}}` } } diff --git a/web/app/components/workflow/__tests__/update-dsl-modal.spec.tsx b/web/app/components/workflow/__tests__/update-dsl-modal.spec.tsx index d56cf80779..b435091647 100644 --- a/web/app/components/workflow/__tests__/update-dsl-modal.spec.tsx +++ b/web/app/components/workflow/__tests__/update-dsl-modal.spec.tsx @@ -230,7 +230,7 @@ describe('UpdateDSLModal', () => { it('should show an error when the selected file content is invalid for the current app mode', async () => { class InvalidDSLFileReader extends MockFileReader { - readAsText(_file: Blob) { + override readAsText(_file: Blob) { const event = { target: { result: 'workflow:\n graph:\n nodes:\n - data:\n type: answer\n' } } as unknown as ProgressEvent this.onload?.call(this as unknown as FileReader, event) } diff --git a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx index d43efc1b30..a80d0b1ee2 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx @@ -55,7 +55,7 @@ const OutputVarList: FC = ({ replaceSpaceWithUnderscoreInVarNameInput(e.target) const newKey = e.target.value - validateVarInput(list.toSpliced(index, 1), newKey) + validateVarInput(list.filter((_, itemIndex) => itemIndex !== index), newKey) const newOutputs = produce(outputs, (draft) => { draft[newKey] = draft[oldKey]! diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx index a0add3e7fd..b8ec03eb93 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx @@ -67,7 +67,7 @@ const VarList: FC = ({ const newKey = e.target.value - validateVarInput(list.toSpliced(index, 1), newKey) + validateVarInput(list.filter((_, itemIndex) => itemIndex !== index), newKey) onVarNameChange?.(list[index]!.variable, newKey) const newList = produce(list, (draft) => { diff --git a/web/app/components/workflow/run/utils/format-log/parallel/index.ts b/web/app/components/workflow/run/utils/format-log/parallel/index.ts index 4ebc6ab60e..588858d576 100644 --- a/web/app/components/workflow/run/utils/format-log/parallel/index.ts +++ b/web/app/components/workflow/run/utils/format-log/parallel/index.ts @@ -1,6 +1,15 @@ import type { NodeTracing } from '@/types/workflow' import { BlockEnum } from '@/app/components/workflow/types' +function findLastIndex(list: T[], predicate: (item: T) => boolean): number { + for (let index = list.length - 1; index >= 0; index--) { + if (predicate(list[index]!)) + return index + } + + return -1 +} + function printNodeStructure(node: NodeTracing, depth: number) { const indent = ' '.repeat(depth) console.log(`${indent}${node.title}`) @@ -107,7 +116,7 @@ const format = (list: NodeTracing[], t: any, isPrint?: boolean): NodeTracing[] = } } if (parentParallelStartNode!.parallelDetail.children) { - const sameBranchNodesLastIndex = parentParallelStartNode.parallelDetail.children.findLastIndex((node) => { + const sameBranchNodesLastIndex = findLastIndex(parentParallelStartNode.parallelDetail.children, (node) => { const currStartNodeId = node.parallel_start_node_id ?? node.execution_metadata?.parallel_start_node_id ?? null return currStartNodeId === parentParallelBranchStartNodeId }) @@ -124,7 +133,7 @@ const format = (list: NodeTracing[], t: any, isPrint?: boolean): NodeTracing[] = const parallelStartNode = result.find(item => parallel_start_node_id === item.node_id) if (parallelStartNode && parallelStartNode.parallelDetail && parallelStartNode!.parallelDetail!.children) { - const sameBranchNodesLastIndex = parallelStartNode.parallelDetail.children.findLastIndex((node) => { + const sameBranchNodesLastIndex = findLastIndex(parallelStartNode.parallelDetail.children, (node) => { const currStartNodeId = node.parallel_start_node_id ?? node.execution_metadata?.parallel_start_node_id ?? null return currStartNodeId === branchStartNodeId }) diff --git a/web/i18n-config/index.ts b/web/i18n-config/index.ts index ff8bd4ff02..016cc2ba2d 100644 --- a/web/i18n-config/index.ts +++ b/web/i18n-config/index.ts @@ -10,7 +10,7 @@ export const i18n = { locales: LanguagesSupported, } as const -export { Locale } +export type { Locale } export const setLocaleOnClient = async (locale: Locale, reloadPage = true) => { Cookies.set(LOCALE_COOKIE_NAME, locale, { expires: 365 }) diff --git a/web/instrumentation-client.ts b/web/instrumentation-client.ts index 4c9e6a10e7..7d61502bc0 100644 --- a/web/instrumentation-client.ts +++ b/web/instrumentation-client.ts @@ -2,20 +2,6 @@ import { IS_DEV } from '@/config' import { env } from '@/env' async function main() { - // Polyfill for Array.prototype.toSpliced (ES2023, Chrome 110+) - if (!Array.prototype.toSpliced) { - // eslint-disable-next-line no-extend-native - Array.prototype.toSpliced = function (this: T[], start: number, deleteCount?: number, ...items: T[]): T[] { - const copy = this.slice() - // When deleteCount is undefined (omitted), delete to end; otherwise let splice handle coercion - if (deleteCount === undefined) - copy.splice(start, copy.length - start, ...items) - else - copy.splice(start, deleteCount, ...items) - return copy - } - } - if (!('localStorage' in globalThis) || !('sessionStorage' in globalThis)) { class StorageMock { data: Record diff --git a/web/package.json b/web/package.json index 72af940479..eeb3c94460 100644 --- a/web/package.json +++ b/web/package.json @@ -48,8 +48,8 @@ "test": "vp test", "test:coverage": "vp test --coverage", "test:watch": "vp test --watch", - "type-check": "tsc --noEmit", - "type-check:tsgo": "tsgo --noEmit", + "type-check": "tsc", + "type-check:tsgo": "tsgo", "uglify-embed": "node ./bin/uglify-embed" }, "dependencies": { @@ -160,6 +160,7 @@ "@antfu/eslint-config": "catalog:", "@chromatic-com/storybook": "catalog:", "@dify/iconify-collections": "workspace:*", + "@dify/tsconfig": "workspace:*", "@egoist/tailwindcss-icons": "catalog:", "@eslint-react/eslint-plugin": "catalog:", "@hono/node-server": "catalog:", diff --git a/web/tsconfig.json b/web/tsconfig.json index 003da18aa2..cf4110bbb5 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -1,15 +1,7 @@ { + "extends": "@dify/tsconfig/nextjs.json", "compilerOptions": { "incremental": true, - "target": "es2022", - "jsx": "react-jsx", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "module": "esnext", - "moduleResolution": "bundler", "paths": { "@/*": [ "./*" @@ -23,19 +15,7 @@ "vitest/globals", "node" ], - "allowJs": true, - "strict": true, - "noUncheckedIndexedAccess": true, - "noEmit": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "skipLibCheck": true, - "plugins": [ - { - "name": "next" - } - ] + "allowJs": true }, "include": [ "next-env.d.ts",