-
Notifications
You must be signed in to change notification settings - Fork 80
feat: add quickapp api using coze in websocket realtime asr scene #295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a new @coze/quickapp-api package with WebSocket tools (transcription, speech, chat, PCM recorder/player), build/test configs, and examples. Introduces a Quick App demo showcasing real-time transcription. Updates Rush to include the new package and example. Relaxes ESLint rules and lint scripts across several existing examples. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant TranscriptionPage
participant useTranscription
participant WsTranscriptionClient
participant PcmRecorder
participant WS as Transcription WS Server
User->>TranscriptionPage: Tap "Start Recording"
TranscriptionPage->>useTranscription: startRecording()
useTranscription->>WsTranscriptionClient: start()
WsTranscriptionClient->>WS: Connect (Authorization)
WsTranscriptionClient->>WS: Send config (PCM 48kHz/mono/16-bit)
WsTranscriptionClient->>PcmRecorder: start()
PcmRecorder-->>WsTranscriptionClient: PCM frames
WsTranscriptionClient->>WS: Send audio chunks (base64)
WS-->>WsTranscriptionClient: transcriptions.message.update
WsTranscriptionClient-->>useTranscription: Update(text, isFinal=false)
useTranscription-->>TranscriptionPage: onTranscriptionUpdate(text)
WS-->>WsTranscriptionClient: transcriptions.message.completed
WsTranscriptionClient-->>useTranscription: Update(text, isFinal=true)
useTranscription-->>TranscriptionPage: onTranscriptionUpdate(text)
User->>TranscriptionPage: Tap "End Recording"
TranscriptionPage->>useTranscription: stopRecording()
useTranscription->>WsTranscriptionClient: stop()
WsTranscriptionClient->>PcmRecorder: stop()
WsTranscriptionClient->>WS: audio buffer complete, close
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 37
🧹 Nitpick comments (47)
examples/coze-js-quickapp/.gitignore (1)
26-27
: Good: local config excluded; consider ignoring additional Quick App artifactsAdd common Quick App packaging outputs and local variants to avoid accidental commits.
# 配置文件 src/config.js +src/config.local.js + +# 打包产物 +*.rpk +sign/ +release/examples/realtime-websocket/package.json (1)
8-8
: Confirm lint CI strictness and plugin compatibility
- File: examples/realtime-websocket/package.json (lint script at line 8 changed to
"eslint ."
).- Dev dependencies now:
• ESLint 9.14.0
• eslint-plugin-react-hooks 5.1.0-beta-26f2496093-20240514
• eslint-plugin-react-refresh ^0.4.5Dropping
--max-warnings 0
means lint warnings will no longer fail CI. If you need strict enforcement, re-add that flag or enforce warning thresholds in your CI job.Also, please verify that ESLint 9.14.0 is fully compatible with your plugins (react-hooks beta and react-refresh) and update versions or peer dependencies accordingly.
packages/quickapp-api/CHANGELOG.md (1)
12-15
: Avoid commented-out bullets in CHANGELOGCommented items imply future scope. Prefer removing them or adding a “Planned” or “Roadmap” section to avoid confusion for consumers scanning the changelog.
- <!-- - `WsSpeechClient`: 用于语音合成的WebSocket客户端 - - `WsChatClient`: 用于聊天的WebSocket客户端 - - `PcmRecorder`: 用于PCM格式录音的工具 - - `PcmStreamPlayer`: 用于PCM流播放的工具 --> + - (计划)`WsSpeechClient`: 用于语音合成的WebSocket客户端 + - (计划)`WsChatClient`: 用于聊天的WebSocket客户端 + - (计划)`PcmRecorder`: 用于PCM格式录音的工具 + - (计划)`PcmStreamPlayer`: 用于PCM流播放的工具examples/fact-check-extension/package.json (1)
9-9
: Lint scope and strictness changed
- Removing
--ext ts,tsx
will lint JS files too (defaults). If this is intentional, great; if not, reintroduce--ext
.--max-warnings 0
removal means warnings won’t fail CI anymore.Optionally keep strictness:
- "lint": "eslint . --report-unused-disable-directives", + "lint": "eslint . --report-unused-disable-directives --max-warnings 0"Ensure the local ESLint config excludes dist/build to avoid unnecessary linting time.
packages/quickapp-api/.gitignore (2)
24-30
: Add TypeScript build metadata to ignoresFor incremental builds, tsc emits tsbuildinfo. Consider ignoring it.
# 临时文件 .tmp/ temp/ +*.tsbuildinfo
31-36
: Consider excluding package manager artifacts to reduce noiseIf pnpm/npm caches are used locally, you may want to ignore their logs/caches at package level.
# 环境变量 .env .env.local .env.development.local .env.test.local .env.production.local + +# 包管理器缓存(可选) +.pnpm-debug.log* +.npmrcexamples/simult-extension/package.json (1)
9-9
: Maintains consistency with other examples; re-evaluate CI strictnessGood to standardize on
eslint . --report-unused-disable-directives
. If you still want warnings to gate CI, add--max-warnings 0
.- "lint": "eslint . --report-unused-disable-directives", + "lint": "eslint . --report-unused-disable-directives --max-warnings 0"examples/realtime-quickstart-vue/eslint.config.cjs (1)
6-10
: Avoid blanket disables; refine no-unused-vars to reduce false negativesTurning off these rules repo-wide in the example can hide issues. Consider softening no-unused-vars instead of disabling it.
Apply this minimal tweak:
rules: { '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['warn', { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true + }], '@typescript-eslint/no-require-imports': 'off', },Optionally, scope these relaxations only to this example (and to JS/TS/Vue files) using overrides:
overrides: [ { files: ['**/*.{js,ts,vue}'], rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', ignoreRestSiblings: true }], '@typescript-eslint/no-require-imports': 'off', }, }, ]examples/fact-check-extension/eslint.config.cjs (1)
6-10
: Refine no-unused-vars instead of disabling itTo prevent masking accidental leftovers in example code, prefer a softer rule configuration.
rules: { '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['warn', { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true + }], '@typescript-eslint/no-require-imports': 'off', },examples/realtime-websocket/eslint.config.cjs (1)
6-10
: Prefer softer no-unused-vars to keep useful warnings in examplesSame rationale as other examples.
rules: { '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['warn', { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true + }], '@typescript-eslint/no-require-imports': 'off', },packages/quickapp-api/README.md (2)
28-28
: Remove unused import in the exampleRecordingStatus isn’t used in this snippet; keeping the import may encourage unused code patterns.
-import { WsTranscriptionClient, RecordingStatus } from '@coze/quickapp-api/ws-tools'; +import { WsTranscriptionClient } from '@coze/quickapp-api/ws-tools';
31-34
: Security: avoid bundling long‑lived tokens in clientsHard-coding API tokens in client apps (even examples) risks leakage. Prefer short‑lived, scoped, server‑issued credentials fetched at runtime over HTTPS.
Suggested doc note:
- Retrieve an ephemeral token from your backend right before connecting.
- Scope it to the minimum permissions and TTL.
- Always use WSS endpoints; pin allowed origins/domains in the app manifest.
packages/quickapp-api/src/ws-tools/pcm-recorder.ts (2)
88-88
: Use English for code commentsReplace the Chinese comment with English for consistency across the codebase.
- // 在测试环境中,使用全局模拟的record对象 + // In test environment, use global mocked record object
150-150
: Make recording duration configurableThe hardcoded 10-minute duration limit may be restrictive. Consider making this configurable through
PcmRecorderConfig
.Add to the interface:
export interface PcmRecorderConfig { // ... existing fields /** * Maximum recording duration in milliseconds * @default 600000 (10 minutes) */ maxDuration?: number; }Then use it in the start method:
- duration: 600000, + duration: this.config.maxDuration || 600000,packages/quickapp-api/src/ws-tools/chat.ts (1)
88-94
: Consider adding automatic reconnection logicWebSocket connections can drop due to network issues. Consider implementing automatic reconnection with exponential backoff to improve reliability.
Example approach:
- Add reconnection configuration (max attempts, delays)
- Implement exponential backoff for reconnection attempts
- Queue messages during disconnection and send on reconnect
- Emit reconnection events for UI updates
packages/quickapp-api/vitest.setup.js (1)
2-3
: Use English for code commentsReplace Chinese comments with English for consistency.
/** - * Vitest 测试设置文件 - * 用于配置全局测试环境 + * Vitest test setup file + * Configures global test environment */examples/coze-js-quickapp/src/common/styles/common.css (2)
2-3
: Use English for code commentsReplace Chinese comment with English for consistency.
/** - * 公共样式文件 + * Common styles file */
5-125
: Consider using CSS variables for colorsHardcoded color values throughout the file make theme changes difficult. Consider extracting colors to CSS variables for better maintainability.
Example approach:
:root { --color-primary: #2196f3; --color-danger: #f44336; --color-warning: #ff9900; --color-text: #333333; --color-text-muted: #999999; --color-background: #ffffff; --color-background-secondary: #f5f5f5; } .primary-button { background-color: var(--color-primary); /* ... */ }packages/quickapp-api/src/ws-tools/pcm-stream-player.ts (3)
60-90
: Avoid async + new Promise anti-patternBoth methods already construct and return a Promise; remove the redundant async for clarity.
Apply this diff:
- private async writeToFile(data: ArrayBuffer): Promise<void> { + private writeToFile(data: ArrayBuffer): Promise<void> { return new Promise((resolve, reject) => { try { this.file.writeArrayBuffer({- private async clearFile(): Promise<void> { + private clearFile(): Promise<void> { return new Promise((resolve, reject) => { try { this.file.delete({Also applies to: 97-126
36-52
: Consider dependency injection for system modules to improve testability and portabilityAllow providing audio/file modules via constructor to ease testing and future host variations.
Example:
- constructor(options: { debug?: boolean } = {}) { - this.debug = options.debug || false; - // Import required modules from Quick App - this.audio = require('@system.audio'); - this.file = require('@system.file'); + constructor(options: { debug?: boolean; audio?: any; file?: any } = {}) { + this.debug = options.debug || false; + // Allow DI to simplify testing and host customization + this.audio = options.audio ?? require('@system.audio'); + this.file = options.file ?? require('@system.file');
1-5
: Add basic unit tests for feed → play → stop/destroy flowNo tests for PcmStreamPlayer were found. Cover init, write ordering, prepare/play, stop/pause/stop idempotency, and cleanup.
I can scaffold vitest tests exercising:
- Resolving of initPlayer with/without prepare()
- Ordered writes under concurrent feed() calls
- Stop/reset/destroy cleanup paths
examples/coze-js-quickapp/src/config.example.js (1)
6-12
: Config sample is clear; consider safer defaults and ignore rules
- Suggest defaulting debug to false in the example to discourage shipping verbose logs in prod.
- Ensure
src/config.js
is git-ignored to avoid leaking tokens (if not already in .gitignore).Proposed tweak:
export default { // 语音转写API令牌 transcriptionToken: 'YOUR_API_TOKEN_HERE', // 调试模式 - debug: true, + debug: false, };examples/coze-js-quickapp/src/pages/index/index.ux (2)
7-9
: Verify logo asset pathCurrent src is
/common/logo.png
. README shows bothcommon/images/
and a top-levelcommon/logo.png
. Ensure the actual file location matches the reference. If the logo is undersrc/common/images/logo.png
, update:- <image src="/common/logo.png" class="logo"></image> + <image src="/common/images/logo.png" class="logo"></image>
30-34
: Confirm router URI formatQuick App routing typically uses
pages/<name>
and resolvesindex.ux
. Using a leading slash is sometimes unnecessary. If navigation fails, try:- router.push({ uri: '/pages/transcription' }); + router.push({ uri: 'pages/transcription' });examples/coze-js-quickapp/README.md (2)
14-31
: Align asset paths with codeThe tree lists both
common/images/
andcommon/logo.png
. Index page uses/common/logo.png
. Please standardize one location and update both README and code to match to avoid broken references.
46-58
: Add a note to keep API tokens out of VCSSince users will create
src/config.js
with real tokens, add a line noting that this file is ignored by.gitignore
and should not be committed.Proposed note:
- 请确保
src/config.js
已在.gitignore
中忽略,避免将真实令牌提交到版本库。packages/quickapp-api/scripts/build.js (2)
22-29
: Use explicit build configIf
tsconfig.build.json
exists, call tsc with it to avoid picking up IDE configs.- execSync('tsc', { cwd: rootDir, stdio: 'inherit' }); + execSync('tsc -p tsconfig.build.json', { cwd: rootDir, stdio: 'inherit' });
46-55
: Guard optional docs copyCopying README/CHANGELOG will throw if files are missing. Add existence checks.
-console.log('复制README.md和CHANGELOG.md到dist目录...'); -fs.copyFileSync( - path.join(rootDir, 'README.md'), - path.join(rootDir, 'dist', 'README.md'), -); -fs.copyFileSync( - path.join(rootDir, 'CHANGELOG.md'), - path.join(rootDir, 'dist', 'CHANGELOG.md'), -); +console.log('复制README.md和CHANGELOG.md到dist目录...'); +const readmePath = path.join(rootDir, 'README.md'); +const changelogPath = path.join(rootDir, 'CHANGELOG.md'); +if (fs.existsSync(readmePath)) { + fs.copyFileSync(readmePath, path.join(rootDir, 'dist', 'README.md')); +} +if (fs.existsSync(changelogPath)) { + fs.copyFileSync(changelogPath, path.join(rootDir, 'dist', 'CHANGELOG.md')); +}packages/quickapp-api/src/ws-tools/__tests__/base.test.ts (2)
2-2
: Remove unused import
WebsocketsEventType
isn’t used in this test; drop the import to satisfy linters.-import { WebsocketsEventType } from '@coze/api';
22-30
: Config assertion visibilityYou assert
client.config
andclient.listeners
directly. If these are markedprotected
inBaseWsTranscriptionClient
, consider exposing readonly accessors for testability; otherwise keep casting.If desired, I can add
getConfig()
andgetListenersSize()
accessors to avoid(client as any)
in tests.packages/quickapp-api/src/ws-tools/index.ts (1)
8-13
: Validate CustomEventTypes against server-side protocolEnsure these string literals match the backend’s expected event names. If they’re public API, consider documenting them in README to avoid drift.
packages/quickapp-api/src/ws-tools/__tests__/pcm-recorder.test.ts (1)
139-165
: Destroy cleans up and prevents further callbacks — good coverageConsider adding small extras:
- Start called twice is idempotent (no double start).
- stop/pause when already INACTIVE/PAUSED are no-ops.
examples/coze-js-quickapp/src/common/js/use-transcription.js (3)
50-66
: Reduce noisy logs; gate them behind debug flagConsole logs in update/completed handlers are unconditional. Gate them by config.debug to keep examples clean.
- console.log( - '[useTranscription] TRANSCRIPTIONS_MESSAGE_UPDATE:', - JSON.stringify(data), - ); + if (config.debug) { + console.log( + '[useTranscription] TRANSCRIPTIONS_MESSAGE_UPDATE:', + JSON.stringify(data), + ); + } @@ - console.log( - '[useTranscription] TRANSCRIPTIONS_MESSAGE_COMPLETED:', - JSON.stringify(data), - ); + if (config.debug) { + console.log( + '[useTranscription] TRANSCRIPTIONS_MESSAGE_COMPLETED:', + JSON.stringify(data), + ); + }Also applies to: 87-101
87-95
: Error logging: keep UX clean, still forward errorYou already propagate error via onError; consider logging only when debug.
- transcriptionClient.on(WebsocketsEventType.ERROR, error => { - console.error('Transcription error:', error); + transcriptionClient.on(WebsocketsEventType.ERROR, error => { + if (config.debug) console.error('Transcription error:', error);
185-192
: Destroy should also reset local state and callbacksOptional: clear text and callbacks to prevent leaks in the example layer.
const destroy = () => { if (transcriptionClient) { transcriptionClient.destroy(); transcriptionClient = null; } isRecording = false; isPaused = false; + transcriptionText = ''; + errorMessage = ''; + onStatusChangeCallback = null; + onTranscriptionUpdateCallback = null; + onErrorCallback = null; };packages/quickapp-api/src/ws-tools/base.ts (2)
53-65
: Clean up empty listener arrays to prevent map growthOptional: delete the event key if no listeners remain.
off(event: string, callback: Function): void { if (!this.listeners.has(event)) { return; } const callbacks = this.listeners.get(event); if (callbacks) { const index = callbacks.indexOf(callback); if (index !== -1) { callbacks.splice(index, 1); + if (callbacks.length === 0) { + this.listeners.delete(event); + } } } }
119-121
: Base destroy can opportunistically close wsIf derived classes set ws, safely close it here to reduce duplication.
destroy(): void { + try { + (this.ws as any)?.close?.(); + } catch (_) { + /* noop */ + } this.listeners.clear(); }examples/coze-js-quickapp/src/pages/transcription/index.ux (2)
102-108
: Make start handler async and clear stale errorClear UI error before starting and await the async operation to catch immediate failures.
- handleStartRecording() { + async handleStartRecording() { if (this.isRecording && !this.isPaused) { return; // 已经在录音中 } - this.transcription.startRecording(); + this.errorMessage = ''; + try { + await this.transcription.startRecording(); + } catch (e) { + // 错误会通过 onError 回调兜底;这里仅在调试时打印 + if (this.transcription?.getStatus?.().debug) { + console.error(e); + } + } },
83-87
: Optional: keep UI error text in syncConsider clearing errorMessage on successful stop/resume/pause toggles as needed to avoid stale errors lingering.
Also applies to: 118-119, 130-133
packages/quickapp-api/src/ws-tools/transcription.ts (3)
10-13
: Remove redundant Chinese commentsThese comments merely translate what the import statements already clearly indicate and add no additional value.
-// 导入基础转写客户端类 - -// 导入PCM录音机 -
42-56
: Consider using a UUID library if availableThe UUID generation implementation is correct. However, if QuickApp environment supports it, consider using a lightweight UUID library for better maintainability.
112-121
: Fix debug log message typoThe debug message contains "error1" which appears to be a typo.
this.ws.onerror = (error: any) => { if (this.config.debug) { console.error( - '[WsTranscriptionClient] WebSocket error1:', + '[WsTranscriptionClient] WebSocket error:', JSON.stringify(error), ); } this.handleError(error); reject(error); };packages/quickapp-api/package.json (5)
44-48
: Include CHANGELOG in published files (if present).If you maintain a CHANGELOG for this package, add it to the "files" whitelist so it ships with the published package.
"files": [ "dist", "LICENSE", - "README.md" + "README.md", + "CHANGELOG.md" ],
49-57
: Add a prepack/prepublishOnly to ensure dist is built when publishing standalone.Monorepo workflows often handle builds, but adding a guard helps if someone publishes this package independently.
"scripts": { "build": "tsc -b tsconfig.build.json -f", "clean": "rimraf dist", "dev": "tsc -b tsconfig.build.json -w", "lint": "eslint src", "lint:fix": "eslint src --fix", "test": "vitest run", - "test:watch": "vitest" + "test:watch": "vitest", + "prepack": "npm run build" },
61-70
: Declare Node engine constraints to match your tooling.ESLint 9 and Vitest 2 require modern Node versions (ESLint 9 needs Node >= 18.18). Add an engines field to prevent installs on unsupported Node versions.
Add outside this block:
+ "engines": { + "node": ">=18.18" + },Also consider bumping rimraf to ^5 if you’re on Node 18+:
- "rimraf": "^3.0.2", + "rimraf": "^5.0.0",Please confirm your CI/CD Node version aligns (Rush pipeline). If needed, update CI images to Node >= 18.18.
26-26
: Fill in or remove the empty "author" field.Either provide the correct author/maintainers or drop the field to avoid publishing with an empty value.
27-30
: Optional: Add "sideEffects": false for better tree-shaking (if safe).If all exports are side-effect free (no global setup at import time), declare:
+ "sideEffects": false,
Skip this if imports perform required side effects (e.g., polyfills or environment detection).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
common/config/subspaces/default/pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
examples/coze-js-quickapp/package-lock.json
is excluded by!**/package-lock.json
examples/coze-js-quickapp/src/common/logo.png
is excluded by!**/*.png
examples/coze-js-quickapp/src/common/logo.svg
is excluded by!**/*.svg
📒 Files selected for processing (45)
common/changes/@coze/quickapp-api/feat-quickapp-api_2025-08-11-09-23.json
(1 hunks)examples/coze-js-quickapp/.gitignore
(1 hunks)examples/coze-js-quickapp/README.md
(1 hunks)examples/coze-js-quickapp/eslint.config.js
(1 hunks)examples/coze-js-quickapp/package.json
(1 hunks)examples/coze-js-quickapp/src/app.ux
(1 hunks)examples/coze-js-quickapp/src/common/js/use-transcription.js
(1 hunks)examples/coze-js-quickapp/src/common/styles/common.css
(1 hunks)examples/coze-js-quickapp/src/config.example.js
(1 hunks)examples/coze-js-quickapp/src/manifest.json
(1 hunks)examples/coze-js-quickapp/src/pages/index/index.ux
(1 hunks)examples/coze-js-quickapp/src/pages/transcription/index.ux
(1 hunks)examples/fact-check-extension/eslint.config.cjs
(1 hunks)examples/fact-check-extension/package.json
(1 hunks)examples/realtime-quickstart-vue/eslint.config.cjs
(1 hunks)examples/realtime-quickstart-vue/package.json
(1 hunks)examples/realtime-websocket/eslint.config.cjs
(1 hunks)examples/realtime-websocket/package.json
(1 hunks)examples/simult-extension/package.json
(1 hunks)packages/quickapp-api/.gitignore
(1 hunks)packages/quickapp-api/CHANGELOG.md
(1 hunks)packages/quickapp-api/README.md
(1 hunks)packages/quickapp-api/eslint.config.js
(1 hunks)packages/quickapp-api/examples/index.js
(1 hunks)packages/quickapp-api/examples/use-chat.js
(1 hunks)packages/quickapp-api/examples/use-speech.js
(1 hunks)packages/quickapp-api/examples/use-transcription.js
(1 hunks)packages/quickapp-api/package.json
(1 hunks)packages/quickapp-api/scripts/build.js
(1 hunks)packages/quickapp-api/src/global.d.ts
(1 hunks)packages/quickapp-api/src/index.ts
(1 hunks)packages/quickapp-api/src/ws-tools/__tests__/base.test.ts
(1 hunks)packages/quickapp-api/src/ws-tools/__tests__/pcm-recorder.test.ts
(1 hunks)packages/quickapp-api/src/ws-tools/base.ts
(1 hunks)packages/quickapp-api/src/ws-tools/chat.ts
(1 hunks)packages/quickapp-api/src/ws-tools/index.ts
(1 hunks)packages/quickapp-api/src/ws-tools/pcm-recorder.ts
(1 hunks)packages/quickapp-api/src/ws-tools/pcm-stream-player.ts
(1 hunks)packages/quickapp-api/src/ws-tools/speech.ts
(1 hunks)packages/quickapp-api/src/ws-tools/transcription.ts
(1 hunks)packages/quickapp-api/tsconfig.build.json
(1 hunks)packages/quickapp-api/tsconfig.json
(1 hunks)packages/quickapp-api/vitest.config.js
(1 hunks)packages/quickapp-api/vitest.setup.js
(1 hunks)rush.json
(2 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-07-31T07:01:34.010Z
Learnt from: CR
PR: coze-dev/coze-js#0
File: .cursor/rules/coze-js-rule.mdc:0-0
Timestamp: 2025-07-31T07:01:34.010Z
Learning: Use Vitest as the testing framework
Applied to files:
packages/quickapp-api/vitest.config.js
📚 Learning: 2025-07-31T07:01:34.010Z
Learnt from: CR
PR: coze-dev/coze-js#0
File: .cursor/rules/coze-js-rule.mdc:0-0
Timestamp: 2025-07-31T07:01:34.010Z
Learning: Applies to **/*.spec.ts : Mock global objects to test environment-specific code
Applied to files:
packages/quickapp-api/vitest.setup.js
📚 Learning: 2025-07-31T07:01:34.010Z
Learnt from: CR
PR: coze-dev/coze-js#0
File: .cursor/rules/coze-js-rule.mdc:0-0
Timestamp: 2025-07-31T07:01:34.010Z
Learning: Applies to **/*.spec.ts : External dependencies should be mocked (`vi.mock()`)
Applied to files:
packages/quickapp-api/vitest.setup.js
📚 Learning: 2025-07-31T07:01:34.010Z
Learnt from: CR
PR: coze-dev/coze-js#0
File: .cursor/rules/coze-js-rule.mdc:0-0
Timestamp: 2025-07-31T07:01:34.010Z
Learning: Applies to **/*.spec.ts : Use `vi.clearAllMocks()` and `vi.resetAllMocks()` to ensure test isolation
Applied to files:
packages/quickapp-api/vitest.setup.js
📚 Learning: 2025-07-31T07:01:34.010Z
Learnt from: CR
PR: coze-dev/coze-js#0
File: .cursor/rules/coze-js-rule.mdc:0-0
Timestamp: 2025-07-31T07:01:34.010Z
Learning: Applies to **/*.spec.ts : Internal modules can be mocked to isolate test units
Applied to files:
packages/quickapp-api/vitest.setup.js
📚 Learning: 2025-02-24T03:34:17.077Z
Learnt from: DingGao-Devin
PR: coze-dev/coze-js#104
File: packages/chat-sdk/src/pages/chat/index.tsx:37-41
Timestamp: 2025-02-24T03:34:17.077Z
Learning: The file `packages/chat-sdk/src/pages/chat/index.tsx` is a test implementation and doesn't require production-level improvements.
Applied to files:
packages/quickapp-api/examples/use-chat.js
🧬 Code Graph Analysis (15)
packages/quickapp-api/eslint.config.js (6)
examples/coze-js-quickapp/eslint.config.js (1)
require
(1-1)examples/fact-check-extension/eslint.config.cjs (1)
require
(1-1)examples/realtime-quickstart-vue/eslint.config.cjs (1)
require
(1-1)examples/realtime-websocket/eslint.config.cjs (1)
require
(1-1)packages/quickapp-api/scripts/build.js (1)
require
(8-8)examples/fact-check-extension/scripts/postbuild.js (1)
__dirname
(6-6)
examples/coze-js-quickapp/src/common/js/use-transcription.js (5)
packages/quickapp-api/examples/use-transcription.js (4)
transcriptionClient
(18-21)isRecording
(24-24)isPaused
(25-25)transcriptionText
(26-26)packages/quickapp-api/src/ws-tools/index.ts (2)
WsTranscriptionClient
(16-16)WebsocketsEventType
(6-6)packages/quickapp-api/src/ws-tools/transcription.ts (5)
WsTranscriptionClient
(18-616)destroy
(590-615)onStatusChange
(568-573)onTranscriptionUpdate
(532-541)onError
(579-585)packages/quickapp-api/src/ws-tools/base.ts (1)
destroy
(119-121)packages/quickapp-api/src/ws-tools/pcm-recorder.ts (2)
destroy
(295-309)onError
(288-290)
packages/quickapp-api/scripts/build.js (1)
examples/fact-check-extension/scripts/postbuild.js (1)
__dirname
(6-6)
packages/quickapp-api/examples/use-transcription.js (5)
examples/coze-js-quickapp/src/common/js/use-transcription.js (7)
transcriptionClient
(26-26)isRecording
(29-29)isPaused
(30-30)transcriptionText
(32-32)destroy
(185-192)onTranscriptionUpdate
(206-208)onError
(214-216)packages/quickapp-api/src/ws-tools/transcription.ts (3)
destroy
(590-615)onTranscriptionUpdate
(532-541)onError
(579-585)packages/quickapp-api/examples/use-speech.js (3)
isPaused
(25-25)statusChangeCallback
(27-27)errorCallback
(29-29)packages/quickapp-api/examples/use-chat.js (1)
errorCallback
(28-28)packages/quickapp-api/src/ws-tools/base.ts (1)
destroy
(119-121)
packages/quickapp-api/vitest.config.js (1)
examples/fact-check-extension/scripts/postbuild.js (1)
__dirname
(6-6)
packages/quickapp-api/src/ws-tools/transcription.ts (2)
packages/quickapp-api/src/ws-tools/base.ts (1)
BaseWsTranscriptionClient
(8-122)packages/quickapp-api/src/ws-tools/pcm-recorder.ts (1)
PcmRecorder
(44-310)
packages/quickapp-api/src/ws-tools/__tests__/base.test.ts (1)
packages/quickapp-api/src/ws-tools/base.ts (1)
BaseWsTranscriptionClient
(8-122)
packages/quickapp-api/src/ws-tools/__tests__/pcm-recorder.test.ts (1)
packages/quickapp-api/src/ws-tools/pcm-recorder.ts (1)
PcmRecorder
(44-310)
packages/quickapp-api/examples/use-speech.js (7)
packages/quickapp-api/src/ws-tools/index.ts (1)
WsSpeechClient
(25-25)packages/quickapp-api/src/ws-tools/speech.ts (6)
WsSpeechClient
(11-424)speak
(246-294)stop
(299-309)pause
(314-322)resume
(327-335)destroy
(398-423)examples/coze-js-quickapp/src/common/js/use-transcription.js (5)
isPaused
(30-30)destroy
(185-192)onStatusChange
(198-200)onError
(214-216)getStatus
(222-227)packages/quickapp-api/examples/use-chat.js (1)
errorCallback
(28-28)packages/quickapp-api/src/ws-tools/transcription.ts (6)
stop
(433-464)pause
(469-495)resume
(500-526)destroy
(590-615)onStatusChange
(568-573)onError
(579-585)packages/quickapp-api/src/ws-tools/base.ts (1)
destroy
(119-121)packages/quickapp-api/src/ws-tools/chat.ts (1)
destroy
(274-281)
packages/quickapp-api/src/ws-tools/base.ts (2)
packages/quickapp-api/src/ws-tools/index.ts (2)
BaseWsTranscriptionClient
(22-22)WebsocketsEventType
(6-6)examples/coze-js-quickapp/src/common/js/use-transcription.js (1)
config
(19-23)
packages/quickapp-api/src/ws-tools/chat.ts (2)
packages/quickapp-api/src/ws-tools/index.ts (2)
WsChatClient
(29-29)WebsocketsEventType
(6-6)examples/coze-js-quickapp/src/common/js/use-transcription.js (2)
config
(19-23)require
(5-8)
packages/quickapp-api/src/ws-tools/pcm-recorder.ts (1)
packages/quickapp-api/src/ws-tools/index.ts (2)
RecordingStatus
(19-19)PcmRecorder
(19-19)
packages/quickapp-api/src/ws-tools/speech.ts (3)
packages/quickapp-api/src/ws-tools/index.ts (2)
WsSpeechClient
(25-25)PcmStreamPlayer
(26-26)packages/quickapp-api/src/ws-tools/pcm-stream-player.ts (1)
PcmStreamPlayer
(5-312)examples/coze-js-quickapp/src/common/js/use-transcription.js (1)
config
(19-23)
packages/quickapp-api/src/ws-tools/pcm-stream-player.ts (1)
packages/quickapp-api/src/ws-tools/index.ts (1)
PcmStreamPlayer
(26-26)
packages/quickapp-api/examples/use-chat.js (2)
packages/quickapp-api/src/ws-tools/index.ts (1)
WsChatClient
(29-29)packages/quickapp-api/src/ws-tools/chat.ts (3)
WsChatClient
(8-282)connect
(52-121)destroy
(274-281)
🪛 Biome (2.1.2)
rush.json
[error] 422-425: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 425-425: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 482-482: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
🔇 Additional comments (33)
common/changes/@coze/quickapp-api/feat-quickapp-api_2025-08-11-09-23.json (1)
3-7
: Confirm semver bump for @coze/quickapp-api (1.0.0 → 1.1.0)Rush change entry is structurally correct. Running:
jq '.name,.version' packages/quickapp-api/package.json # "@coze/quickapp-api" # "1.0.0"means a “minor” bump will move you to 1.1.0. Please verify this matches your intent:
- File under review:
common/changes/@coze/quickapp-api/feat-quickapp-api_2025-08-11-09-23.json- Change snippet:
{ "packageName": "@coze/quickapp-api", "comment": "add quickapp-api for websocket and asr and quickapp-examples", "type": "minor" }- If this is the very first publish and you intend to ship version 1.0.0, adjust the change type (e.g.
none
or per your policy) or remove the change file.- Otherwise, for a backward-compatible feature addition, “minor” is appropriate.
packages/quickapp-api/CHANGELOG.md (1)
5-9
: Version/date may confuse readers; align with actual releaseEntry shows 0.1.0 (2023-11-01) while this PR lands in 2025. Consider:
- Using the real release date when publishing; or
- Adding an “Unreleased” section for current changes, moving the date to publish time.
If package.json is at 0.1.x and this PR is a minor bump, ensure CHANGELOG reflects the next version appropriately.
examples/coze-js-quickapp/src/app.ux (1)
5-20
: LGTM: minimal and correct Quick App lifecycle hooksonCreate/onDestroy hooks are wired correctly with safe logging.
examples/coze-js-quickapp/src/manifest.json (2)
7-7
: Icon path verified
Foundexamples/coze-js-quickapp/src/common/logo.png
, which matches the manifest’s/common/logo.png
reference. Packaging will succeed.
6-6
: minPlatformVersion 1070 already covers system.websocketfactory
Thesystem.websocketfactory
API is supported from Quick App platform v1020 onward, so setting"minPlatformVersion": 1070
is sufficient. No changes needed.packages/quickapp-api/README.md (1)
74-79
: Destroy/cleanup semantics are already consistentVerified that in
ws-tools
:
speechClient.destroy()
isasync
and examples useawait
.transcriptionClient.destroy()
andchatClient.destroy()
are synchronous and examples call them withoutawait
.No changes needed.
packages/quickapp-api/src/index.ts (1)
6-7
: Public re-export looks goodTop-level export of ws-tools is clear and keeps the surface consistent.
packages/quickapp-api/tsconfig.build.json (1)
1-15
: Build tsconfig is appropriateIncludes only src, excludes tests and dist. Matches typical package build needs.
packages/quickapp-api/tsconfig.json (1)
1-21
: Base tsconfig is sensibleStrict mode, declarations, and DOM libs for tests are reasonable. CommonJS output aligns with Node consumption.
packages/quickapp-api/examples/index.js (1)
6-13
: Barrel exports look goodCentralized re-exports are clean and helpful for consumers. No issues.
rush.json (1)
422-425
: 🛑 rush.json Is Valid JSON—Ignore the Biome Configuration SuggestionOur verification shows that rush.json contains no comment tokens (// or /* */) and no disallowed trailing commas. It already conforms to strict JSON, so Biome should parse it without errors. You can safely disregard the previous recommendation to ignore rush.json in your Biome config.
If you’re still seeing parse errors from Biome, please share the exact error messages and the surrounding lines so we can pinpoint the real issue.
Likely an incorrect or invalid review comment.
packages/quickapp-api/src/ws-tools/__tests__/base.test.ts (1)
32-43
: Overall tests are solid after access fixEvent subscription, removal, multi-listener handling, and destroy behavior are covered well.
Also applies to: 45-56, 58-70, 72-82
packages/quickapp-api/src/ws-tools/index.ts (1)
16-29
: Aggregator looks goodExports are cohesive and easy to consume. No issues.
packages/quickapp-api/src/ws-tools/__tests__/pcm-recorder.test.ts (5)
57-66
: Config and initial status assertions look correct
68-86
: Start flow and onframerecorded piping correctly validated
88-94
: Stop flow assertions are solid
96-116
: Pause/resume behavior aligned with platform limitations
118-137
: Error mapping from fail callback to Error is validated wellexamples/coze-js-quickapp/src/common/js/use-transcription.js (1)
242-242
: Module format vs consumer importThis module exports via CommonJS (module.exports). The .ux page imports it as a named ESM export. To avoid interop issues, either:
- Switch this file to ESM exports, or
- Change the page to import the default CJS namespace and destructure.
See page review for a concrete import change.
packages/quickapp-api/examples/use-speech.js (1)
40-74
: LGTM! Well-structured speech synthesis initiation.The function properly validates input, maintains state consistency, and handles errors appropriately.
packages/quickapp-api/src/ws-tools/transcription.ts (13)
14-40
: LGTM!Well-structured class definition with proper TypeScript typing and correct inheritance pattern.
193-234
: LGTM!Audio streaming implementation is correct with proper state checks and error handling.
236-277
: LGTM!Correctly signals the end of audio stream with appropriate event type.
279-309
: LGTM!Message handling is well-structured with proper event delegation.
311-354
: LGTM!Correct manual Base64 implementation necessary for QuickApp environment where
btoa
is not available.
356-384
: LGTM!Recorder initialization with matching audio configuration and proper callback setup.
386-428
: LGTM!Well-structured start sequence with proper initialization order and error handling.
430-464
: LGTM!Proper stop sequence with correct order of operations.
466-526
: LGTM!Pause and resume methods correctly manage state and delegate to recorder.
528-585
: LGTM!Event callback registration methods are well-implemented with proper data extraction.
587-615
: LGTM!Comprehensive cleanup with proper resource disposal sequence.
617-619
: LGTM!Proper module exports providing both named and default export options.
150-163
: Ignore event type change—TRANSCRIPTIONS_UPDATE is correct for initial audio configThe WebSocket protocol defines TranscriptionsUpdateEvent (event_type=TRANSCRIPTIONS_UPDATE) with optional input_audio config, which the client sends to update its audio settings. All transcription clients (quickapp-api, coze-js, uniapp, examples) use this same event for the initial PCM configuration. No dedicated “INIT” or “CONFIG” event exists, so no change is needed.
Likely an incorrect or invalid review comment.
rules: { | ||
'@typescript-eslint/no-explicit-any': 'off', | ||
'@typescript-eslint/no-unused-vars': 'off', | ||
'@typescript-eslint/no-require-imports': 'off', | ||
'@typescript-eslint/no-magic-numbers': 'off', | ||
'@typescript-eslint/naming-convention': 'off', | ||
'@typescript-eslint/no-useless-constructor': 'off', | ||
'@typescript-eslint/no-shadow': 'off', | ||
'security/detect-object-injection': 'off', | ||
'no-inner-declarations': 'off', | ||
'no-var': 'off', | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Reconsider disabling critical linting rules
Disabling important TypeScript and security rules reduces code quality and safety. Consider:
- Keep
@typescript-eslint/no-explicit-any
enabled to maintain type safety - Keep
security/detect-object-injection
enabled to prevent potential security vulnerabilities - Keep
no-var
enabled asconst
/let
are preferred in modern JavaScript
For example code that genuinely needs exceptions, use inline eslint-disable comments with explanations rather than blanket disabling.
{ | ||
"name": "@coze/api-quickapp-example", | ||
"version": "1.0.0", | ||
"description": "Coze.js Quick App Example - Speech to Text Transcription", | ||
"keywords": [ | ||
"coze", | ||
"quickapp", | ||
"speech-to-text", | ||
"transcription" | ||
], | ||
"license": "MIT", | ||
"author": "Coze.js Team", | ||
"main": "index.js", | ||
"scripts": { | ||
"build": "aiot build", | ||
"lint": "eslint --fix src/", | ||
"release": "aiot release", | ||
"server": "aiot server", | ||
"start": "aiot server --watch", | ||
"watch": "aiot watch" | ||
}, | ||
"browserslist": [ | ||
"chrome 65" | ||
], | ||
"dependencies": { | ||
"@coze-infra/eslint-config": "workspace:*", | ||
"@coze/api": "workspace:*", | ||
"@coze/quickapp-api": "workspace:*" | ||
}, | ||
"devDependencies": { | ||
"aiot-toolkit": "^1.2.0-alpha.5", | ||
"babel-eslint": "^10.0.1", | ||
"eslint": "9.14.0", | ||
"eslint-plugin-ux": "^0.0.4" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
❓ Verification inconclusive
Harden package metadata for an example app and align ESLint tooling
- Mark example as private to avoid accidental publish; remove unused "main".
- Move linter config to devDependencies.
- Replace deprecated babel-eslint with @babel/eslint-parser; verify eslint-plugin-ux compatibility with ESLint 9.
Run to check presence of main entry and confirm CJS/ESM needs:
Suggested changes:
{
"name": "@coze/api-quickapp-example",
"version": "1.0.0",
"description": "Coze.js Quick App Example - Speech to Text Transcription",
@@
- "author": "Coze.js Team",
- "main": "index.js",
+ "author": "Coze.js Team",
+ "private": true,
"scripts": {
@@
- "dependencies": {
- "@coze-infra/eslint-config": "workspace:*",
- "@coze/api": "workspace:*",
- "@coze/quickapp-api": "workspace:*"
- },
+ "dependencies": {
+ "@coze/api": "workspace:*",
+ "@coze/quickapp-api": "workspace:*"
+ },
"devDependencies": {
"aiot-toolkit": "^1.2.0-alpha.5",
- "babel-eslint": "^10.0.1",
- "eslint": "9.14.0",
- "eslint-plugin-ux": "^0.0.4"
+ "@babel/eslint-parser": "^7.24.0",
+ "eslint": "9.14.0",
+ "eslint-plugin-ux": "^0.0.4",
+ "@coze-infra/eslint-config": "workspace:*"
}
}
🏁 Script executed:
#!/bin/bash
# Does index.js exist? If not, "main" can be removed.
fd -t f -a '^index\.js$' examples/coze-js-quickapp || true
Length of output: 52
Let’s re-verify whether index.js
exists using a more reliable file search:
🏁 Script executed:
#!/bin/bash
# Check for index.js at the root of the example
find examples/coze-js-quickapp -maxdepth 1 -type f -name 'index.js' || true
Length of output: 67
Harden package metadata and align ESLint tooling
- Add
"private": true
and remove the unused"main": "index.js"
field (noindex.js
in the example). - Move
@coze-infra/eslint-config
fromdependencies
intodevDependencies
. - Replace deprecated
babel-eslint
with@babel/eslint-parser@^7.24.0
. - Remove any trailing commas in JSON lists/objects.
- Verify that
eslint-plugin-ux@^0.0.4
is fully compatible with ESLint 9 before merging.
Suggested package.json
changes:
{
"name": "@coze/api-quickapp-example",
"version": "1.0.0",
"description": "Coze.js Quick App Example - Speech to Text Transcription",
@@
- "author": "Coze.js Team",
- "main": "index.js",
+ "author": "Coze.js Team",
+ "private": true,
"scripts": {
@@
- "dependencies": {
- "@coze-infra/eslint-config": "workspace:*",
- "@coze/api": "workspace:*",
- "@coze/quickapp-api": "workspace:*"
- },
+ "dependencies": {
+ "@coze/api": "workspace:*",
+ "@coze/quickapp-api": "workspace:*"
+ },
"devDependencies": {
- "aiot-toolkit": "^1.2.0-alpha.5",
- "babel-eslint": "^10.0.1",
- "eslint": "9.14.0",
- "eslint-plugin-ux": "^0.0.4"
+ "aiot-toolkit": "^1.2.0-alpha.5",
+ "@babel/eslint-parser": "^7.24.0",
+ "eslint": "9.14.0",
+ "eslint-plugin-ux": "^0.0.4",
+ "@coze-infra/eslint-config": "workspace:*"
}
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
{ | |
"name": "@coze/api-quickapp-example", | |
"version": "1.0.0", | |
"description": "Coze.js Quick App Example - Speech to Text Transcription", | |
"keywords": [ | |
"coze", | |
"quickapp", | |
"speech-to-text", | |
"transcription" | |
], | |
"license": "MIT", | |
"author": "Coze.js Team", | |
"main": "index.js", | |
"scripts": { | |
"build": "aiot build", | |
"lint": "eslint --fix src/", | |
"release": "aiot release", | |
"server": "aiot server", | |
"start": "aiot server --watch", | |
"watch": "aiot watch" | |
}, | |
"browserslist": [ | |
"chrome 65" | |
], | |
"dependencies": { | |
"@coze-infra/eslint-config": "workspace:*", | |
"@coze/api": "workspace:*", | |
"@coze/quickapp-api": "workspace:*" | |
}, | |
"devDependencies": { | |
"aiot-toolkit": "^1.2.0-alpha.5", | |
"babel-eslint": "^10.0.1", | |
"eslint": "9.14.0", | |
"eslint-plugin-ux": "^0.0.4" | |
} | |
} | |
{ | |
"name": "@coze/api-quickapp-example", | |
"version": "1.0.0", | |
"description": "Coze.js Quick App Example - Speech to Text Transcription", | |
"keywords": [ | |
"coze", | |
"quickapp", | |
"speech-to-text", | |
"transcription" | |
], | |
"license": "MIT", | |
"author": "Coze.js Team", | |
"private": true, | |
"scripts": { | |
"build": "aiot build", | |
"lint": "eslint --fix src/", | |
"release": "aiot release", | |
"server": "aiot server", | |
"start": "aiot server --watch", | |
"watch": "aiot watch" | |
}, | |
"browserslist": [ | |
"chrome 65" | |
], | |
"dependencies": { | |
"@coze/api": "workspace:*", | |
"@coze/quickapp-api": "workspace:*" | |
}, | |
"devDependencies": { | |
"aiot-toolkit": "^1.2.0-alpha.5", | |
"@babel/eslint-parser": "^7.24.0", | |
"eslint": "9.14.0", | |
"eslint-plugin-ux": "^0.0.4", | |
"@coze-infra/eslint-config": "workspace:*" | |
} | |
} |
🤖 Prompt for AI Agents
In examples/coze-js-quickapp/package.json around lines 1 to 36, strengthen
package metadata and adjust dependencies: add "private": true and remove the
unused "main": "index.js" entry; move "@coze-infra/eslint-config" from
dependencies into devDependencies; replace deprecated "babel-eslint" with
"@babel/eslint-parser" at version ^7.24.0 in devDependencies; ensure there are
no trailing commas anywhere in the JSON; and confirm eslint-plugin-ux@^0.0.4 is
compatible with ESLint 9 before merging.
"aiot-toolkit": "^1.2.0-alpha.5", | ||
"babel-eslint": "^10.0.1", | ||
"eslint": "9.14.0", | ||
"eslint-plugin-ux": "^0.0.4" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
babel-eslint is deprecated; verify plugin compatibility with ESLint v9
Use @babel/eslint-parser instead. Also confirm eslint-plugin-ux supports ESLint 9.
🌐 Web query:
Is "babel-eslint" deprecated and what is the recommended replacement? Also, is "eslint-plugin-ux" compatible with ESLint v9?
💡 Result:
Yes. The babel-eslint package was deprecated on August 18, 2021; its last stable release (10.1.0) was in February 2020[1]. The recommended replacement is to use the modern Babel-maintained parser and plugin: @babel/eslint-parser (parser) and, if needed, @babel/eslint-plugin for related rules[9][7].
Regarding eslint-plugin-ux and ESLint v9: there is no public confirmation that eslint-plugin-ux is updated for ESLint v9’s new rule API and flat config. ESLint v9 introduced breaking changes requiring plugins to be updated; many plugins that haven’t migrated will throw errors like “context.getScope is not a function.” ESLint provides @eslint/compat to wrap older plugins so they can run on v9 until they’re updated[2][8]. So:
- If eslint-plugin-ux hasn’t been updated for v9, you can often make it work by wrapping it with fixupPluginRules from @eslint/compat in eslint.config.js[2].
- Full native compatibility depends on the plugin’s own updates; check its release notes. ESLint’s tracking shows that v9 support varies by plugin and requires flat config readiness[5][8].
Practical steps:
- Replace babel-eslint with @babel/eslint-parser in your config[9].
- If you must use eslint-plugin-ux on ESLint v9 and it errors, install @eslint/compat and wrap the plugin with fixupPluginRules()[2].
Citations:
- 1: https://www.xeol.io/explorer/package/npm/babel-eslint
- 2: https://eslint.org/blog/2024/05/eslint-compatibility-utilities/
- 3: https://gitlab.com/gitlab-org/customers-gitlab-com/-/issues/9381
- 4: https://station.railway.com/questions/issues-deploying-react-builds-17f4138a
- 5: 📈 Tracking: ESLint v9 support eslint/eslint#18391
- 6: https://eslint.org/docs/latest/use/getting-started
- 7: https://classic.yarnpkg.com/en/package/@babel/eslint-plugin
- 8: https://eslint.org/docs/latest/use/migrate-to-9.0.0
- 9: https://socket.dev/npm/package/@babel/eslint-parser
- 10: https://docs.trunk.io/code-quality/linters/supported/eslint
Replace deprecated babel-eslint and ensure ESLint-v9 compatibility
- In examples/coze-js-quickapp/package.json (lines 31–35), swap out the deprecated
babel-eslint
dependency for the modern parser (and optional plugin):- "babel-eslint": "^10.0.1", + "@babel/eslint-parser": "^latest", + "@babel/eslint-plugin": "^latest" // if you rely on Babel-specific lint rules
- Update your ESLint configuration to use
@babel/eslint-parser
as theparser
. - Confirm whether eslint-plugin-ux supports ESLint 9’s new rule API:
- If you hit errors (e.g.
context.getScope is not a function
), install@eslint/compat
and wrap the plugin rules withfixupPluginRules()
in your flat config:import { fixupPluginRules } from "@eslint/compat"; import ux from "eslint-plugin-ux"; export default [ // ... fixupPluginRules(ux), ];
- For full native support, check the plugin’s release notes or issue tracker for explicit ESLint 9/flat-config compatibility.
- If you hit errors (e.g.
🤖 Prompt for AI Agents
In examples/coze-js-quickapp/package.json around lines 31 to 35, replace the
deprecated "babel-eslint" dependency with the modern parser
"@babel/eslint-parser" (and add it to devDependencies), then update the project
ESLint configuration to set parser to "@babel/eslint-parser"; if you encounter
runtime errors from "eslint-plugin-ux" under ESLint v9 (e.g. context.getScope),
install "@eslint/compat" and in your flat config import and wrap the ux plugin
rules with fixupPluginRules() before exporting, and otherwise verify the
plugin’s release notes for native ESLint 9 / flat-config support and update or
remove the plugin accordingly.
const startRecording = async () => { | ||
errorMessage = ''; | ||
|
||
try { | ||
if (!transcriptionClient) { | ||
initClient(); | ||
} | ||
|
||
await transcriptionClient.start(); | ||
isRecording = true; | ||
isPaused = false; | ||
onStatusChangeCallback && | ||
onStatusChangeCallback({ isRecording, isPaused }); | ||
} catch (error) { | ||
console.error('Failed to start recording:', error); | ||
errorMessage = `启动失败: ${error.message || '未知错误'}`; | ||
onErrorCallback && onErrorCallback(errorMessage); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Guard against missing token and concurrent starts
- Validate token before starting to avoid opaque connection failures.
- Prevent double-start by guarding concurrent calls.
- const startRecording = async () => {
+ let isStarting = false;
+ const startRecording = async () => {
errorMessage = '';
try {
+ if (!config.token) {
+ errorMessage = '未配置转写 Token';
+ onErrorCallback && onErrorCallback(errorMessage);
+ return;
+ }
+ if (isStarting) return;
+ isStarting = true;
if (!transcriptionClient) {
initClient();
}
await transcriptionClient.start();
isRecording = true;
isPaused = false;
onStatusChangeCallback &&
onStatusChangeCallback({ isRecording, isPaused });
} catch (error) {
- console.error('Failed to start recording:', error);
+ if (config.debug) console.error('Failed to start recording:', error);
errorMessage = `启动失败: ${error.message || '未知错误'}`;
onErrorCallback && onErrorCallback(errorMessage);
+ } finally {
+ isStarting = false;
}
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const startRecording = async () => { | |
errorMessage = ''; | |
try { | |
if (!transcriptionClient) { | |
initClient(); | |
} | |
await transcriptionClient.start(); | |
isRecording = true; | |
isPaused = false; | |
onStatusChangeCallback && | |
onStatusChangeCallback({ isRecording, isPaused }); | |
} catch (error) { | |
console.error('Failed to start recording:', error); | |
errorMessage = `启动失败: ${error.message || '未知错误'}`; | |
onErrorCallback && onErrorCallback(errorMessage); | |
} | |
}; | |
let isStarting = false; | |
const startRecording = async () => { | |
errorMessage = ''; | |
try { | |
if (!config.token) { | |
errorMessage = '未配置转写 Token'; | |
onErrorCallback && onErrorCallback(errorMessage); | |
return; | |
} | |
if (isStarting) return; | |
isStarting = true; | |
if (!transcriptionClient) { | |
initClient(); | |
} | |
await transcriptionClient.start(); | |
isRecording = true; | |
isPaused = false; | |
onStatusChangeCallback && | |
onStatusChangeCallback({ isRecording, isPaused }); | |
} catch (error) { | |
if (config.debug) console.error('Failed to start recording:', error); | |
errorMessage = `启动失败: ${error.message || '未知错误'}`; | |
onErrorCallback && onErrorCallback(errorMessage); | |
} finally { | |
isStarting = false; | |
} | |
}; |
🤖 Prompt for AI Agents
In examples/coze-js-quickapp/src/common/js/use-transcription.js around lines 107
to 125, the startRecording function needs a pre-check for a missing auth token
and protection against concurrent starts: validate that the token exists before
calling or initializing the client and call onErrorCallback with a clear message
if it's missing; add a transient isStarting (or similar) guard that returns
early if a start is already in progress, set isStarting=true before awaiting
transcriptionClient.start(), and ensure isStarting is reset (and isRecording
only set true) in a finally block so concurrent calls are prevented and state
remains consistent even on error.
.button-row { | ||
margin: 10px 30px; | ||
width: 100%; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix potential horizontal overflow in button-row
Setting width: 100%
with horizontal margins causes the element to exceed its container width (100% + 60px total).
.button-row {
margin: 10px 30px;
- width: 100%;
+ width: calc(100% - 60px);
}
Alternatively, remove the width property and let the element size naturally within its margins.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
.button-row { | |
margin: 10px 30px; | |
width: 100%; | |
} | |
.button-row { | |
margin: 10px 30px; | |
width: calc(100% - 60px); | |
} |
🤖 Prompt for AI Agents
In examples/coze-js-quickapp/src/common/styles/common.css around lines 83 to 86,
the .button-row rule sets width: 100% while also using horizontal margins, which
can cause horizontal overflow; remove the width property (or change it to width:
auto or box-sizing: border-box) so the element sizes naturally within its
margins and no longer exceeds its container.
this.ws = websocket.create({ | ||
url: 'wss://ws.coze.cn/v1/audio/transcriptions', | ||
header: { | ||
Authorization: `Bearer ${this.config.token}`, | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Make WebSocket URL configurable
The WebSocket URL is hardcoded. Consider making it configurable through the options to support different environments (dev, staging, production).
this.ws = websocket.create({
- url: 'wss://ws.coze.cn/v1/audio/transcriptions',
+ url: this.config.wsUrl || 'wss://ws.coze.cn/v1/audio/transcriptions',
header: {
Authorization: `Bearer ${this.config.token}`,
},
});
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
this.ws = websocket.create({ | |
url: 'wss://ws.coze.cn/v1/audio/transcriptions', | |
header: { | |
Authorization: `Bearer ${this.config.token}`, | |
}, | |
}); | |
this.ws = websocket.create({ | |
- url: 'wss://ws.coze.cn/v1/audio/transcriptions', | |
+ url: this.config.wsUrl || 'wss://ws.coze.cn/v1/audio/transcriptions', | |
header: { | |
Authorization: `Bearer ${this.config.token}`, | |
}, | |
}); |
🤖 Prompt for AI Agents
In packages/quickapp-api/src/ws-tools/transcription.ts around lines 75 to 80,
the WebSocket URL is hardcoded to 'wss://ws.coze.cn/v1/audio/transcriptions';
change this to accept a configurable url via the class/options (or fallback to
the current URL as a default), update the constructor signature and any
instantiation sites to pass the environment-specific URL, validate the provided
URL (non-empty string) before using it, and document that it can be sourced from
config or an environment variable so dev/staging/prod can use different
endpoints.
import { defineConfig } from '@coze-infra/vitest-config'; | ||
|
||
export default defineConfig({ | ||
dirname: __dirname, | ||
preset: 'default', | ||
test: { | ||
environment: 'jsdom', | ||
globals: true, | ||
setupFiles: ['./vitest.setup.js'], | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Ensure this config’s module system matches package.json; convert to CJS if the package is not ESM
Using import in a .js file requires "type": "module" at the package or repo root. Otherwise, Node will throw a SyntaxError.
Run this to check module type and spot mixed configs:
If CJS is preferred, switch to require/module.exports:
-import { defineConfig } from '@coze-infra/vitest-config';
-
-export default defineConfig({
+const { defineConfig } = require('@coze-infra/vitest-config');
+module.exports = defineConfig({
preset: 'default',
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./vitest.setup.js'],
},
});
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check package-level type
if [ -f packages/quickapp-api/package.json ]; then
echo "quickapp-api/package.json type:"
cat packages/quickapp-api/package.json | jq -r '.type // "<not set>"'
fi
# Find other vitest configs to mirror their style
fd -i -a 'vitest.config.*' | sed 's/^/found: /'
# Print first line of this config to confirm syntax
head -n 1 packages/quickapp-api/vitest.config.js
Length of output: 1062
Align Vitest Config Module System with package.json
The packages/quickapp-api/vitest.config.js
is written as ESM (using import
/export
) but its nearest package.json has no "type": "module"
(defaulting to CommonJS). Node will throw a SyntaxError when loading this config. You have two options:
-
Add ESM support
• In packages/quickapp-api/package.json, add:{ "type": "module", … }
• Keep your existing
vitest.config.js
. -
Or convert to CommonJS
- Rename
vitest.config.js
→vitest.config.cjs
- Update contents:
-import { defineConfig } from '@coze-infra/vitest-config'; - -export default defineConfig({ +const { defineConfig } = require('@coze-infra/vitest-config'); +module.exports = defineConfig({ dirname: __dirname, preset: 'default', test: { environment: 'jsdom', globals: true, setupFiles: ['./vitest.setup.js'], }, });
- Rename
🤖 Prompt for AI Agents
In packages/quickapp-api/vitest.config.js (lines 1-11): the file uses ESM
import/export but the package.json for packages/quickapp-api lacks
"type":"module", causing Node to throw a SyntaxError; fix by either adding
"type":"module" to packages/quickapp-api/package.json to enable ESM (keep the
file as-is) or convert the config to CommonJS by renaming vitest.config.js →
vitest.config.cjs and replacing ESM syntax with CommonJS equivalents (use
require to load '@coze-infra/vitest-config' and module.exports =
defineConfig(...)), then run tests to confirm the config is loadable.
import { defineConfig } from '@coze-infra/vitest-config'; | ||
|
||
export default defineConfig({ | ||
dirname: __dirname, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__dirname is undefined in ESM; compute it from import.meta.url or drop it
If this file is treated as ESM (it uses import), __dirname will be undefined.
Two options:
- Keep ESM and compute dirname:
-import { defineConfig } from '@coze-infra/vitest-config';
+import { defineConfig } from '@coze-infra/vitest-config';
+import { fileURLToPath } from 'node:url';
+import { dirname as pathDirname } from 'node:path';
+
+const __dirname = pathDirname(fileURLToPath(import.meta.url));
- Or remove dirname if not required by your preset:
-export default defineConfig({
- dirname: __dirname,
+export default defineConfig({
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/quickapp-api/vitest.config.js around line 4, __dirname is referenced
but in ESM it is undefined; either remove the dirname property if your preset
doesn't need it, or compute it from import.meta.url: import file URL to path
(fileURLToPath) and derive directory with path.dirname (or use new URL('.',
import.meta.url).pathname) and assign that value to dirname; update exports to
use the computed dirname and ensure required imports (path and
url/fileURLToPath) are added at top.
global.require = vi.fn(module => { | ||
if (module === '@system.audio') { | ||
return audioMock; | ||
} | ||
if (module === '@system.record') { | ||
return recordMock; | ||
} | ||
throw new Error(`Module ${module} not mocked`); | ||
}); | ||
|
||
// 在测试环境中模拟 require('@system.audio') 和 require('@system.record') | ||
const originalRequire = typeof require !== 'undefined' ? require : null; | ||
if (originalRequire && typeof originalRequire === 'function') { | ||
global.require = function (id) { | ||
if (id === '@system.audio') { | ||
return audioMock; | ||
} | ||
if (id === '@system.record') { | ||
return recordMock; | ||
} | ||
return originalRequire(id); | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove duplicate require mocking logic
There are two blocks of code that mock the require
function. The second implementation (lines 58-69) is more complete as it handles fallback to the original require. Consider removing the first mock (lines 47-55).
-// 模拟 require 函数
-global.require = vi.fn(module => {
- if (module === '@system.audio') {
- return audioMock;
- }
- if (module === '@system.record') {
- return recordMock;
- }
- throw new Error(`Module ${module} not mocked`);
-});
-
-// 在测试环境中模拟 require('@system.audio') 和 require('@system.record')
+// Mock require function for Quick App modules in test environment
const originalRequire = typeof require !== 'undefined' ? require : null;
-if (originalRequire && typeof originalRequire === 'function') {
- global.require = function (id) {
- if (id === '@system.audio') {
- return audioMock;
- }
- if (id === '@system.record') {
- return recordMock;
- }
+
+global.require = function (id) {
+ if (id === '@system.audio') {
+ return audioMock;
+ }
+ if (id === '@system.record') {
+ return recordMock;
+ }
+ if (originalRequire && typeof originalRequire === 'function') {
return originalRequire(id);
- };
-}
+ }
+ throw new Error(`Module ${id} not mocked`);
+};
🤖 Prompt for AI Agents
packages/quickapp-api/vitest.setup.js lines 47 to 69: there are two duplicate
mocks of global.require; remove the first mock block (lines 47-55) and keep the
more complete fallback implementation (lines 58-69) so the file only defines the
require override that returns audioMock/recordMock for those ids and falls back
to the original require for all others.
// 模拟 @system.websocket | ||
global.system.websocket = { | ||
create: () => ({ | ||
connect: vi.fn(), | ||
send: vi.fn(), | ||
close: vi.fn(), | ||
onopen: vi.fn(), | ||
onmessage: vi.fn(), | ||
onerror: vi.fn(), | ||
onclose: vi.fn(), | ||
}), | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing mock for @system.websocketfactory
The WsChatClient
in chat.ts
requires @system.websocketfactory
, but this module is not mocked in the require function. Add it to ensure tests work correctly.
Add to the global require mock:
if (id === '@system.websocketfactory') {
return {
create: () => ({
connect: vi.fn(),
send: vi.fn(),
close: vi.fn(),
onopen: vi.fn(),
onmessage: vi.fn(),
onerror: vi.fn(),
onclose: vi.fn(),
})
};
}
🤖 Prompt for AI Agents
In packages/quickapp-api/vitest.setup.js around lines 71 to 82, the global
require mock does not return a stub for '@system.websocketfactory' used by
WsChatClient; add a branch in the require mock that returns an object with a
create() method which returns the same mocked websocket API (connect, send,
close, onopen, onmessage, onerror, onclose implemented as vi.fn()) so tests that
import '@system.websocketfactory' receive the expected mock.
Summary by CodeRabbit
New Features
Documentation
Tests
Chores