Skip to content

Conversation

tnaught
Copy link

@tnaught tnaught commented Aug 11, 2025

  • add coze-quickapp api for quickapp developers in realtime asr scene
  • add a quickapp example using coze-quickapp api

Summary by CodeRabbit

  • New Features

    • Introduced Quick App API SDK enabling WebSocket real-time speech transcription, speech synthesis, and chat, plus PCM recording/playback utilities.
    • Added a sample Quick App demonstrating real-time transcription with start/pause/resume/stop controls and a simple UI.
  • Documentation

    • Added package and example READMEs, usage instructions, and a changelog.
  • Tests

    • Added unit tests and test environment setup for core audio/WS components.
  • Chores

    • Added build tooling, lint configurations, and relaxed lint scripts in examples.
    • Registered new package and example in the monorepo configuration.

@CLAassistant
Copy link

CLAassistant commented Aug 11, 2025

CLA assistant check
All committers have signed the CLA.

Copy link

coderabbitai bot commented Aug 11, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Monorepo config & changelog
rush.json, common/changes/@coze/quickapp-api/feat-quickapp-api_2025-08-11-09-23.json
Registers new projects (@coze/quickapp-api, @coze/api-quickapp-example) and adds a changeset entry (minor) for @coze/quickapp-api.
Quick App example (Coze.js)
examples/coze-js-quickapp/*, examples/coze-js-quickapp/src/...
New Quick App example for real-time transcription: docs, config template, app/pages UX, shared styles, hook (use-transcription.js), package scripts, ESLint config, and ignores.
QuickApp API package: core code
packages/quickapp-api/src/index.ts, packages/quickapp-api/src/global.d.ts, packages/quickapp-api/src/ws-tools/...
Introduces ws-tools: Base client, WsTranscriptionClient, WsSpeechClient, WsChatClient, PcmRecorder, PcmStreamPlayer, and index re-exports plus custom event enum.
QuickApp API package: examples
packages/quickapp-api/examples/...
Provides example hooks for transcription, speech, and chat; barrel export.
QuickApp API package: build, config, docs, tests
packages/quickapp-api/package.json, CHANGELOG.md, README.md, .gitignore, eslint.config.js, tsconfig*.json, scripts/build.js, vitest.config.js, vitest.setup.js, src/ws-tools/__tests__/*
Adds package metadata/exports, documentation, lint/TS/test configs, build script, test setup/mocks, and unit tests for base client and PCM recorder.
ESLint updates in other examples
examples/fact-check-extension/eslint.config.cjs, examples/realtime-quickstart-vue/eslint.config.cjs, examples/realtime-websocket/eslint.config.cjs
Adds rules block disabling select TypeScript ESLint rules across examples.
Lint script adjustments
examples/fact-check-extension/package.json, examples/realtime-quickstart-vue/package.json, examples/realtime-websocket/package.json, examples/simult-extension/package.json
Simplifies lint scripts (removes --ext and/or --max-warnings 0; aligns to eslint . or similar).

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

A whisker twitch, I wire the streams,
WebSockets hum with vocal dreams.
I nibble code, transcribe the air,
Chatters, speeches, bytes to share.
Tap to start, I perk my ears—
Hop-stop-pause, the text appears.
Carrots cached; the build now cheers! 🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a 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 artifacts

Add 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.5

Dropping --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 CHANGELOG

Commented 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 ignores

For incremental builds, tsc emits tsbuildinfo. Consider ignoring it.

 # 临时文件
 .tmp/
 temp/
+*.tsbuildinfo

31-36: Consider excluding package manager artifacts to reduce noise

If 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*
+.npmrc
examples/simult-extension/package.json (1)

9-9: Maintains consistency with other examples; re-evaluate CI strictness

Good 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 negatives

Turning 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 it

To 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 examples

Same 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 example

RecordingStatus 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 clients

Hard-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 comments

Replace the Chinese comment with English for consistency across the codebase.

-      // 在测试环境中,使用全局模拟的record对象
+      // In test environment, use global mocked record object

150-150: Make recording duration configurable

The 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 logic

WebSocket 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 comments

Replace 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 comments

Replace Chinese comment with English for consistency.

 /**
- * 公共样式文件
+ * Common styles file
  */

5-125: Consider using CSS variables for colors

Hardcoded 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-pattern

Both 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 portability

Allow 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 flow

No 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 path

Current src is /common/logo.png. README shows both common/images/ and a top-level common/logo.png. Ensure the actual file location matches the reference. If the logo is under src/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 format

Quick App routing typically uses pages/<name> and resolves index.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 code

The tree lists both common/images/ and common/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 VCS

Since 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 config

If 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 copy

Copying 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 visibility

You assert client.config and client.listeners directly. If these are marked protected in BaseWsTranscriptionClient, consider exposing readonly accessors for testability; otherwise keep casting.

If desired, I can add getConfig() and getListenersSize() accessors to avoid (client as any) in tests.

packages/quickapp-api/src/ws-tools/index.ts (1)

8-13: Validate CustomEventTypes against server-side protocol

Ensure 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 coverage

Consider 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 flag

Console 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 error

You 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 callbacks

Optional: 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 growth

Optional: 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 ws

If 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 error

Clear 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 sync

Consider 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 comments

These comments merely translate what the import statements already clearly indicate and add no additional value.

-// 导入基础转写客户端类
-
-// 导入PCM录音机
-

42-56: Consider using a UUID library if available

The 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 typo

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3068887 and c1ac7f4.

⛔ 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 release

Entry 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 hooks

onCreate/onDestroy hooks are wired correctly with safe logging.

examples/coze-js-quickapp/src/manifest.json (2)

7-7: Icon path verified
Found examples/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
The system.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 consistent

Verified that in ws-tools:

  • speechClient.destroy() is async and examples use await.
  • transcriptionClient.destroy() and chatClient.destroy() are synchronous and examples call them without await.

No changes needed.

packages/quickapp-api/src/index.ts (1)

6-7: Public re-export looks good

Top-level export of ws-tools is clear and keeps the surface consistent.

packages/quickapp-api/tsconfig.build.json (1)

1-15: Build tsconfig is appropriate

Includes only src, excludes tests and dist. Matches typical package build needs.

packages/quickapp-api/tsconfig.json (1)

1-21: Base tsconfig is sensible

Strict 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 good

Centralized re-exports are clean and helpful for consumers. No issues.

rush.json (1)

422-425: 🛑 rush.json Is Valid JSON—Ignore the Biome Configuration Suggestion

Our 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 fix

Event 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 good

Exports 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 well

examples/coze-js-quickapp/src/common/js/use-transcription.js (1)

242-242: Module format vs consumer import

This 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 config

The 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.

Comment on lines +6 to +17
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',
},
Copy link

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 as const/let are preferred in modern JavaScript

For example code that genuinely needs exceptions, use inline eslint-disable comments with explanations rather than blanket disabling.

Comment on lines +1 to +36
{
"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"
}
}
Copy link

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 (no index.js in the example).
  • Move @coze-infra/eslint-config from dependencies into devDependencies.
  • 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.

Suggested change
{
"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.

Comment on lines +31 to +35
"aiot-toolkit": "^1.2.0-alpha.5",
"babel-eslint": "^10.0.1",
"eslint": "9.14.0",
"eslint-plugin-ux": "^0.0.4"
}
Copy link

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:


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 the parser.
  • 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 with fixupPluginRules() 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.
🤖 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.

Comment on lines +107 to +125
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);
}
};
Copy link

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.

Suggested change
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.

Comment on lines +83 to +86
.button-row {
margin: 10px 30px;
width: 100%;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
.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.

Comment on lines +75 to +80
this.ws = websocket.create({
url: 'wss://ws.coze.cn/v1/audio/transcriptions',
header: {
Authorization: `Bearer ${this.config.token}`,
},
});
Copy link

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.

Suggested change
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.

Comment on lines +1 to +11
import { defineConfig } from '@coze-infra/vitest-config';

export default defineConfig({
dirname: __dirname,
preset: 'default',
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./vitest.setup.js'],
},
});
Copy link

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

    1. Rename vitest.config.jsvitest.config.cjs
    2. 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'],
         },
       });
🤖 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,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

__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.

Comment on lines +47 to +69
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);
};
}
Copy link

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.

Comment on lines +71 to +82
// 模拟 @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(),
}),
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants