# @codaco/interview 中文文档

规范地址：https://npmcn.com/package/%40codaco/interview/

版本：11\.0\.0
npm 元数据更新时间：2026-10-21T14:53:51.136Z
中文译文更新时间：2026-11-10T06:20:41.848Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40codaco/interview
原文对照：https://npmcn.com/package/%40codaco/interview/?lang=original
源码仓库：https://github.com/complexdatacollective/network-canvas-monorepo

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：57.9 MB，585 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：21 个
- peer 依赖：react ^19\.3\.0，effect ^4\.0\.0，motion ^13\.4\.6，react-dom ^19\.3\.0，tailwindcss ^4\.3\.3，@codaco/fresco-ui ^8\.1\.0，@codaco/shared-consts ^6\.3\.0，@codaco/tailwind-config ^1\.5\.1，@codaco/protocol-validation ^15\.0\.0
- 首次发布：2026-05-05T08:36:32.462Z
- 最新版本发布：2026-10-21T14:53:50.793Z
- 近 30 天下载量：3765（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：47（近 12 个月 47 个）

## 中文简介

网络Canvas访谈引擎——壳牌组件,会话合同.

## 安装

```sh
npm install @codaco/interview
```

也可以使用：`pnpm add @codaco/interview`、`yarn add @codaco/interview`、`bun add @codaco/interview`

## README

# `@codaco/interview`

Network Canvas 采访引擎 — 已打包, 任何反应主机都可以嵌入 参与者与调查进行互动。 拥有 Redux 商店, 舞台 导航, 全部17个接口执行( NameGenerator, Socialgram, 分类Bin 地理空间 对话系统 土司系统 设计标志。 主机拥有网络呼叫( 同步、 完成、 资产) URL分辨率),以及参与者目前在协议中的位置.

预定主机为Fresco(Next.js)和新的建筑师预览 模式(Vite)——但软件包对两者都没有意识.

* * *

## 安装

```sh
pnpm add @codaco/interview
```

**同伴依赖关系** (你很可能已经拥有这些):

```jsonc
{
  "@codaco/fresco-ui": "^2.5.4",
  "@codaco/protocol-validation": "^11.5.0",
  "@codaco/shared-consts": "5.0.0",
  "@codaco/tailwind-config": "^1.0.0-alpha.11",
  "immer": "^11.1.4",
  "motion": "^12.38.0",
  "react": "^19.2.5",
  "react-dom": "^19.2.5",
  "tailwindcss": "^4.2.4",
}
```

### 尾风

软件包假定主机有一个有线的 Tailwind v4 构建插件 (单位:千美元)`@tailwindcss/vite` 对于维特, `@tailwindcss/postcss` 用于下一个.js/ 任意 (原始内容存档于2019-10-29). PostCSS protect. 链条中的每个CSS文件都有一个关切 消费者按下列顺序进口:

```css
/* styles/globals.css */
@import '@codaco/tailwind-config/fresco.css'; /* Tailwind v4 + theme + plugins + fonts */
@import '@codaco/fresco-ui/styles.css'; /* @source glue for fresco-ui's classes */
@import '@codaco/interview/styles.css'; /* @source glue for interview's classes */
```

**也别 `@import "tailwindcss"` 你自己来.** 那个进口活人 内部 `@codaco/tailwind-config/fresco.css`; 再次加载 尾风运行两次,产生重复/相冲突 公用事业.

每一个 `@codaco/fresco-ui/styles.css` 财务报告和审定财务报表 `@codaco/interview/styles.css` 是一个只包含 `@source "./**/*.{js,ts,tsx}"` 指令范围为软件包自己的模块文件。 光荣的结局 进口地点 `styles.css` 坐着——包裹 `src/` 从源头消耗时的树( 如工作空间主机所做的), 或 镜像 `dist/` 复制 npm 安装。 都进口了 尾风 类扫描仪同时行走软件包并发送每个工具类 组件参考。 主机不再需要写入 `@source '../node_modules/...'` 线条本身.

`@codaco/tailwind-config/fresco.css` 捆绑默认和 访谈主题变体. 贝壳制造 `<main data-theme-interview>` 带有传送门容器,所以对话框/接通部分留在单元内 子树——不需要主机侧设置. 见 _主题 & DOM 范围_ 见下文.

### 阴道/jsdom

节点没有CSS装载器,所以任何从 `@codaco/interview` (即使是计划) 需要维特处理 包裹,否则你得到 _"TypeError:未知的文件扩展名 `.css`" (中文(简体) )_内置软件包 每个使用 jsdom 的项目 :

```ts
// vitest.config.ts
export default defineConfig({
  test: {
    server: {
      deps: { inline: ['@codaco/interview'] },
    },
  },
});
```

* * *

## 壳牌部分

```tsx
import {
  createDebouncedSyncHandler,
  Shell,
  type InterviewPayload,
} from '@codaco/interview';
```

完整的最小主机( TypeScript / Next.js App 路由器样式 – the 形状与任何其他反应框架相同:

```tsx
'use client';

import {
  createDebouncedSyncHandler,
  Shell,
  type AssetRequestHandler,
  type FinishHandler,
  type InterviewPayload,
  type SyncHandler,
} from '@codaco/interview';
import { useCallback, useMemo, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';

export default function InterviewClient({
  payload,
  resolveAssetUrl,
}: {
  payload: InterviewPayload;
  resolveAssetUrl: (assetId: string) => Promise<string>;
}) {
  const router = useRouter();
  const params = useSearchParams();

  // currentStep is HOST state — the package never owns it. Keep it in
  // useState, nuqs, a context, the URL, wherever fits the host. The
  // package re-renders when the prop changes.
  const [currentStep, setCurrentStep] = useState<number>(() =>
    Number(params.get('step') ?? payload.session.currentStep),
  );

  const onStepChange = useCallback(
    (step: number) => {
      setCurrentStep(step);
      const next = new URLSearchParams(params.toString());
      next.set('step', String(step));
      router.replace(`?${next.toString()}`);
    },
    [params, router],
  );

  // Persist state on every reducer commit. Receives the full session
  // payload — POST it to your server, write to IndexedDB, anything.
  //
  // The engine offers every change as it happens and never batches on your
  // behalf: only you know what one write costs. A local write can take them
  // all; a network request usually should not, so wrap it in
  // `createDebouncedSyncHandler` (below) rather than posting per answer.
  const onSync: SyncHandler = useMemo(
    () =>
      createDebouncedSyncHandler(
        async (interviewId, session) => {
          await fetch(`/interview/${interviewId}/sync`, {
            method: 'POST',
            body: JSON.stringify(session),
          });
        },
        { waitMs: 3000 },
      ),
    [],
  );

  // Called when the participant clicks Finish on the FinishSession
  // stage. Receives an AbortSignal so you can cancel any in-flight work
  // if the user backs out.
  const onFinish: FinishHandler = useCallback(
    async (interviewId, signal) => {
      await fetch(`/interview/${interviewId}/finish`, {
        method: 'POST',
        signal,
      });
      router.push(`/interview/${interviewId}/complete`);
    },
    [router],
  );

  // Stages reference protocol assets by ID. The package calls this
  // exactly when a stage needs a URL — return a same-origin URL,
  // pre-signed S3 URL, blob: URL, whatever your storage uses.
  const onRequestAsset: AssetRequestHandler = useCallback(
    (assetId) => resolveAssetUrl(assetId),
    [resolveAssetUrl],
  );

  return (
    <Shell
      payload={payload}
      currentStep={currentStep}
      onStepChange={onStepChange}
      onSync={onSync}
      onFinish={onFinish}
      onRequestAsset={onRequestAsset}
    />
  );
}
```

### 为什么 `currentStep` 主办国?

所以东道主可以驾车 无论它喜欢什么 `nuqs` URL 参数, 浏览器 历史, 阶梯 UI, 来自电子邮件的深层链接, 服务器发送的初始 立场。 软件包读取 `currentStep` 发送 `onStepChange` 页:1 每个导航,它不保存自己的副本.

这也是允许主机挂起的 `Shell` 一次但让 不重新创建 Redux 商店的不同阶段: `currentStep` 中间的道具变化 .

### 果壳道具

| Prop | 类型 | 需求 | 页:1 |
| --- | --- | --- | --- |
| `payload` | `InterviewPayload` | 对 | `{ session, protocol }` ——见外形类型. 每个商店创建一次 `payload.session.id`;通过稳定的引用. |
| `currentStep` | `number` | 对 | 参与者的舞台指数。 宿主拥有. |
| `onStepChange` | `(step: number) => void` | 对 | 每当参与者导航时就被解雇。 主机应该反射 `step` 进入自己的状态. |
| `onSync` | `(id, session, opts) => Promise` | 对 | 在每个 Redux 承诺后呼叫——引擎不批量. 随你喜欢的坚持; 包入 `createDebouncedSyncHandler` 如果写作昂贵. `opts.immediate` 刻出不可推迟的标记(退出、完成); `opts.unloading` 附加标记文件可能无法存活的(隐藏,页码). |
| `onFinish` | `(id, AbortSignal) => Promise` | 对 | 从会场叫来的 如果用户在飞行中途导航,信号会中止. |
| `onRequestAsset` | `(assetId) => Promise<url>` | 对 | 将协议资产解决为 URL 。 被称为懒惰的阶段上架. |
| `analytics` | `InterviewAnalyticsMetadata` | 对 | 主机元数据作为每个事件的超级属性: `installationId` (匿名主机UUID), `hostApp` (例如:. `"Fresco"`, (中文(简体) ), `hostVersion?`。 。 。 . |
| `posthogClient` | 邮购客户端( 需要) `capture`, (中文(简体) ), `captureException`, (中文(简体) ), `register`(中文(简体) ) | 没有 | 已初始化的 PostHog 客户端 。 当提供时,包会通过它发出事件而不修改其配置. 缺席时, 包会懒惰地初始化它自己命名的实例 `ph-relay.networkcanvas.com`。 。 。 . |
| `disableAnalytics` | `boolean` | 没有 | 何时 `true`,所有事件排放均被抑制(否) `posthog-js` 导入)。 默认 `false`。用于E2E和合成访问运行. |
| `finishConfirmationDescription` | `ReactNode` | 没有 | 完成确认对话框中显示的特定主机解释 。 不承诺响应的局部中性引导的默认是不可改变的. 一个已订阅的消息组件可以保持一个主机覆盖,以响应语言变化. |
| `requestedLocale` | `string | readonly string[] | null` | 没有 | 用户偏好,解决了主机地块,或者命令了地块请求. 该软件包会与其自己支持的界面语言进行谈判;未匹配的请求使用英语. |
| `localePreference` | `string | null` | 没有 | 可选的控制菜单选择, 配对 `onLocaleChange`。字符串选择最佳支持的匹配; `null` 如下 `requestedLocale`。关闭它以保留菜单选择到软件包的地方. |
| `onLocaleChange` | `(locale: string | null) => void` | 没有 | 在菜单选择后调用, 这样主机就可以坚持它 . `null` 表示再次遵循主机请求. |
| `catalog` | `InterviewCatalog` | 没有 | 界面语言的消息来自 `loadInterviewCatalog`,基于同样的 `requestedLocale` 财务报告和审定财务报表 `localePreference`. 让访谈渲染出水合物,而不等待该语言的下载。 只在符合谈判语言时使用. |
| `allowLanguageSelection` | `boolean` | 没有 | 在设置菜单中显示界面语言选择器 。 默认为 `true`。 。 。 . |
| `flags` | `{ isE2E?, isDevelopment? }` | 没有 | `isE2E: true` 曝光情况 `window.__interviewStore` 用于 Playwright 固定装置. `isDevelopment: true` 启用 redux-logger . |

软件包替换前一个 `onError` 内部调用 `posthog.captureException` 调用; 通过解析分析客户端报告错误和资产负载故障( 或在 `disableAnalytics` 实值 `true`) (中文(简体) ). 主机不需要自导出错误槽 .

#### 界面语言

传递用户的偏好或您的主机已经谈判过的地址为 `requestedLocale`。软件包拥有其登记册和信件:目前 支持 `en`, (中文(简体) ), `en-GB`, (中文(简体) ), `es`, (中文(简体) ), `zh-Hans`, (中文(简体) ), `zh-Hant`, (中文(简体) ), `de`, (中文(简体) ), `nl`, (中文(简体) ), `pt-BR`, (中文(简体) ), `it`,以及 `fr`,匹配区域请求 例如 `es-MX` 改为 `es`, (中文(简体) ), `zh-CN` 财务报告和审定财务报表 `zh-SG` 改为 `zh-Hans`, (中文(简体) ), `zh-TW`, (中文(简体) ), `zh-HK` 财务报告和审定财务报表 `zh-MO` 改为 `zh-Hant`, (中文(简体) ), `de-AT` 改为 `de`, (中文(简体) ), `nl-BE` 改为 `nl`, (中文(简体) ), `pt` 或者说 `pt-PT` 改为 `pt-BR`, (中文(简体) ), `it-CH` 改为 `it`,以及 `fr-CA` 改为 `fr`,然后掉到 `en` 对不支持或错误的请求。 一个阵列 以优惠顺序表达请求。 不需要主机提供者或目录 . 每种语言的信息都是单独的块, 第一次采访时会加载 显示语言; 英语不需要。 以语言装入的贝壳 还没有装入 显示一个旋转器 在采访的表面,直到它得到了, 而不是先制作英文,而后开关保持当前 在屏幕上使用语言直到新的语言准备好。 每个离线主机都保留 预留每块建筑的可用语言,作为PWA服务 工人们会的.

一个主机可以把下载 从采访的路径 `loadInterviewCatalog(requestedLocale, localePreference)` 从 `@codaco/interview/catalog`,它的谈判方式是 `Shell` 是否和 决定: `{ locale, messages }`。条目没有反应,所以是一个服务器 可以导入它: 服务器委托主机等待它并传递结果为 `catalog`,而访谈使该语言水合物没有 旋转而无所求. 一个客户端在不等待的情况下打电话 准备有效载荷;然后壳牌发现已经装入的语言.

设置控制包提供的按钮、菜单、验证、访问 标签、帮助和舞台控制。 协议授权的标题、提示、标签、, 选项、研究值和识别符没有变化。 议定书 内容本地化是一个单独的计划.

菜单可以暂时覆盖主机请求. 它的自动选项清除 此覆盖; 新的主机请求也立即生效 。 一个东道主 可使用的持续菜单更改 `onLocaleChange` 并通过新解决的地方 恢复为 `requestedLocale`。以在 菜单,也可以通过 `localePreference`: 字符串优先并匹配 相对于软件包注册;未匹配或格式不当的偏好下降 通过至 `requestedLocale`。 。 。 . `null` 如下 `requestedLocale`,并省略其内容 保持软件包本地菜单状态。 这样可以自动清除保存的首选项 直接在重新装入后。 软件包本身不读取浏览器偏好或 存储。 语言变化保留挂载的访谈,等待格式输入, 导航和答案。 软件包 `lang` 财务报告和审定财务报表 `dir` b. 本区域和本区域 将主机文档的语言留给主机.

主机在外部提供导出控制 `Shell`,例如内线 `ProtocolField` 预览,可以使用 `InterviewI18nProvider` 从 `@codaco/interview` 同样的 `requestedLocale` 合同和软件包目录。 与 `Shell`,它没有带来它自己的悬浮边界:它的首次渲染在 尚未加载的语句暂停,所以将其置于其中。 主办者 已经拥有一个提供者可以添加 `interviewCatalogLoaders` 从 `@codaco/interview/locales` 他们的应用程序目录来源。 没有供应商, 独立控件使用他们的英语默认值.

#### 分析

采访包直接显示PostHog事件 `onError`\- 典型的主桥。 三种操作模式:

1.  **主机提供客户端** - 通过 `posthogClient`; 软件包通过主机实例发送,并 `distinct_id` 每起事件都被否决到采访ID。 主机实例配置(自动捕获,识别,会话记录)是主机的责任.
2.  **亲身经历** ——省略 `posthogClient`; 软件包懒惰导入 `posthog-js/dist/module.no-external` 构建( 没有远程脚本加载器, 因此它运行在 `script-src 'self'` 并输入一个已命名的实例`'@codaco/interview'`反对 `https://ph-relay.networkcanvas.com`。 。 。 .
3.  **已禁用** - 通过 `disableAnalytics={true}`; 没有发布事件, 没有导入后 hog-js .

PII合同:事件从不包括协议-网络数据,协议-作者内容(阶段标签,即时文本,代码簿标签,资产名称),或参与者输入(形式值,自由文本,更改标签,搜索查询,密码句). 事件只包括结构标识符(阶段类型/索引,即时索引,随机节点/对接UUID),代码簿 **内部标识** (例如:. `"person"`, (中文(简体) ), `"friend"`),计数,持续时间,以及包定义的歧视者.

整个事件分类学生活在 [`docs/superpowers/specs/2026-05-05-interview-analytics-design.md`](https://npmcn.com/package/docs/superpowers/specs/2026-05-05-interview-analytics-design.md)。 。 。 .

### 烤箱视图

`Shell` 将自己的 UI 烤面包供应商和取景器安装在原地, 本地化采访区域。 主办方不需要再访问取景口. 这使得通知文本和可访问姓名能够对访谈作出回应 菜单的语言,包括主机使用不同语言时.

每个壳牌公司保持其验证通知独立于其他挂载 面试。 应用级的土司供应商仍可单独供主机使用 在面谈之外发出通知.

* * *

## 建设 `InterviewPayload` 服务器侧

软件包从未进入主机数据库. 您的服务器手 彻底解决了 `{ session, protocol }` 对象, 全部 `asset://` 已映射到 ID 的 URL `onRequestAsset` 将会是 与电话.

```ts
import {
  type InterviewPayload,
  type ProtocolPayload,
  isValidAssetType,
} from '@codaco/interview';
import { hashProtocol } from '@codaco/protocol-validation';

export async function loadInterviewPayload(
  interviewId: string,
): Promise<InterviewPayload> {
  const interview = await db.interview.findUniqueOrThrow({
    where: { id: interviewId },
    include: { protocol: true },
  });

  // protocol assets come from your DB / object store — flatten to the
  // shape the package consumes
  const assets = interview.protocol.assets
    .filter((a) => isValidAssetType(a.type))
    .map((a) => ({
      assetId: a.id,
      name: a.name,
      type: a.type,
      ...(a.value ? { value: a.value } : {}), // apikey assets only
    }));

  const protocol: ProtocolPayload = {
    ...interview.protocol,
    // Content hash of { codebook, stages }. Computed at protocol-import time
    // by `hashProtocol` from @codaco/protocol-validation; forwarded here as a
    // super-property on every analytics event.
    hash: interview.protocol.hash ?? hashProtocol(interview.protocol),
    importedAt: interview.protocol.importedAt.toISOString(),
    assets,
  };

  return {
    session: {
      id: interview.id,
      // … the rest of SessionPayload — see the type for the full shape
      network: interview.network ?? createInitialNetwork(),
      currentStep: interview.currentStep ?? 0,
      stageMetadata: interview.stageMetadata,
      // …
    },
    protocol,
  };
}
```

`createInitialNetwork()` 返回带有自负的空洞网络 已关闭的节点——当您创建新访问时拨打一次 记录之后的负载会通过计划验证.

持有有效协议文件本身的主机,而不是 平整数据库记录,用 `currentProtocolToPayload(protocol, { id, importedAt })` 从 `@codaco/interview/contract`。通过相同的 `id` 财务报告和审定财务报表 `importedAt` 每一次 同一协议已载入.

* * *

## Sparse 实体属性流

访谈存储实体属性为稀有记录. 自己的属性密钥 总是有定义 `VariableValue`; 缺少密钥表示变量是 未设. `false`, (中文(简体) ), `0`, (中文(简体) ), `''`,以及 `[]` 答复是明确的,并一直在场.

```mermaid
flowchart LR
  Form[Mounted form fields] --> Adapter[Coerce values and build patch]
  Direct[Direct participant edit] --> Patch[AttributePatch: set and unset]
  Adapter --> Patch
  Patch --> Validate[Validate keys and overlap]
  Validate --> Apply[Apply attributes and secure metadata]
  Protocol[Protocol-derived prompt attributes] --> Apply
  Apply --> Network[Sparse session.network]
  Network --> Sync[onSync session]
  Sync --> Persistence[Host persistence]
  Persistence --> HostParse[Host read via NcNetworkSchema]
  HostParse --> Payload[Shell payload]
  Payload --> Network
  Persistence --> ExportParse[Exporter parse via NcNetworkSchema]
  ExportParse --> Output[CSV and GraphML]
```

内部 `AttributePatch` 合同将设定定义值与 删除属性 :

```ts
type AttributePatch = Readonly<{
  set: Readonly<Record<string, VariableValue>>;
  unset: readonly string[];
}>;
```

格式提交首先强制其协议变量类型的值。 这个 适配器然后考虑确切的挂载字段名称:每个定义值去 改为 `set`,每个 `undefined` 值转到 `unset`字段 被忽略 。 如果挂载定义值不是 `VariableValue`转换 整个失败, 没有发送部分补丁 .

创建和编辑时有意使用该结果的不同部分:

-   只创建实体写入 `set`,所以未解字段被省略.
-   编辑两者都适用 `set` 财务报告和审定财务报表 `unset`,所以清除挂载字段删除 键,而表单以外的属性保持不变.
-   直接参与者编辑, 如 bin 和布局控件, 生成相同的 补丁形状, 而不是写入无效的哨兵 .

节点、边缘和自我更新 thunks 验证参与者编辑的补丁密钥 属于适用的代码簿定义,并拒绝在 两者 `set` 财务报告和审定财务报表 `unset`。节点和边缘创建对它们的 初始属性;可明确控制名册和小类进口 允许外部节点属性。 验证在完成之前完成 减号可以突变会话状态 。 即时会员补丁直接导出 从协议的 `additionalAttributes`,因此减少者应用信任的 键而不重复补丁验证.

共享应用器为两者制作新的属性和安全元数据地图 验证和协议衍生的补丁 。 一个 `unset` 删除属性并 它的匹配 `_secureAttributes` 在同一缩小的过渡中输入;空 安全元数据映射崩溃到 `undefined`。 。 。 .

网络作曲器撤消历史对存在敏感 。 对于每一个触摸到的钥匙,一个 倒数补丁恢复先前的自身值 `set`,但恢复先前的缺席 与 `unset`当定义的空值,例如 `[]` 被编辑后撤销.

`onSync` 手握在减员后短短的会话。 主机解析 存储或接收的网络 `NcNetworkSchema` 在构建下一个之前 `Shell` 有效载荷;这接受无效的输入项,但输出很少. `@codaco/network-exporters` 重复在格式化前分析每个会话, 因此,持久性和导出不会重新产生无效属性值.

* * *

## 公共 API 参考

下面的一切都是从 `'@codaco/interview'`更多公众 子路径曝光合同,协议的 schema 版本, locate catalogs, the 面试目录加载器(`@codaco/interview/catalog`)和样式;主机代码 不应进入包件内部.

### 构成部分

-   `Shell` - 运行时间
-   `InterviewI18nProvider` – 所使用的导出控件的软件包地盘边界 外部 `Shell`

### Schemas + 帮助者

-   `StageMetadataSchema` - Zod计划 `session.stageMetadata`。使用这个 以校验您数据库中恢复的状态,然后将其递回 进入有效载荷.
-   `createInitialNetwork()` ——自负所生的空网络. 打电话一次 每一次新面试.
-   `isValidAssetType(type)` - 上游类型 `ResolvedAsset.type`。 。 。 .
-   `getNodeLabelAttribute(variables, attributes)` 选择变量 其值应显示为节点的标签。 给兄弟们用的 软件包(例如). `@codaco/network-exporters`因此,出口使用相同的 uI 的标签逻辑 .

### 合成数据

-   `generateNetwork(codebook, stages, options?)` ——决定性 网络生成器,可以走协议的阶段并产生节点, 边缘 自我属性 和阶段元数据 用来写故事 固定装置,负载测试,以及合成预览模式.
-   `GenerateNetworkOptions`, (中文(简体) ), `GenerateNetworkResult` - 伙伴类型.

### 公共类型

```ts
type InterviewPayload = { session: SessionPayload; protocol: ProtocolPayload };

type SessionPayload = SessionState; // the Redux session shape
type ProtocolPayload = Omit<CurrentProtocol, 'assetManifest'> & {
  id: string;
  importedAt: string; // ISO
  assets: ResolvedAsset[];
};

type ResolvedAsset = {
  assetId: string;
  name: string;
  type: 'image' | 'video' | 'audio' | 'network' | 'geojson' | 'apikey';
  value?: string; // apikey only
  source?: string; // the file's name in the protocol, used for MIME type and CSV/JSON decisions
};

type SyncOptions = {
  // Do not defer this write.
  immediate: boolean;
  // The document is being hidden or unloaded and may never run script again:
  // use a transport that outlives it, and do not queue behind a request that
  // will die with it. Always accompanied by `immediate`.
  unloading: boolean;
};
type SyncHandler = (
  interviewId: string,
  session: SessionPayload,
  options: SyncOptions,
) => Promise<void>;

// Batching is the host's decision. This wraps a handler so ordinary changes
// are rate-limited to one write per `waitMs` carrying the newest state, while
// `immediate` writes go out at once.
function createDebouncedSyncHandler(
  write: SyncHandler,
  options: { waitMs: number },
): SyncHandler;
type FinishHandler = (
  interviewId: string,
  signal: AbortSignal,
) => Promise<void>;
type AssetRequestHandler = (assetId: string) => Promise<string>;
type ErrorHandler = (error: Error, ctx?: Record<string, unknown>) => void;
type StepChangeHandler = (step: number) => void;

type InterviewerFlags = {
  isE2E?: boolean;
  isDevelopment?: boolean;
};
```

* * *

## 主题 & DOM 范围

`Shell` 制作单曲 `<main data-theme-interview>` 键。 这既是测试/e2e固定装置的稳定选择器,也是激活采访主题的包装器:后人拿起深色调色板,努尼托打字机,以及响应的根字体大小自动.

`Shell` 并提供一个门户容器(通过 `<PortalContainerProvider>` 从 `@codaco/fresco-ui/PortalContainer`)所以对话,弹出,倒放,工具提示,土司,选择,以及从采访内部打开的组合框都变成了节点 _内部_ 他们自动继承访谈调色板而不是传送到 `document.body`。 。 。 .

如果你让访谈主题UI **外部** 联 合 国 `Shell` (例如采访结束后的"谢谢"一页),将UI包装为: `<ThemedRegion theme="interview">` 从 `@codaco/fresco-ui/ThemedRegion`数字 :

```tsx
import { ThemedRegion } from '@codaco/fresco-ui/ThemedRegion';

<ThemedRegion theme="interview">
  <ThankYouPage />
</ThemedRegion>;
```

`<ThemedRegion>` 及其祖先,直到 `<body>` 一定没有 `transform`, (中文(简体) ), `filter`, (中文(简体) ), `perspective`,或 `contain` 设定——这些为固定位置的后人创建了新的包含区块,并会打破模式/流行定位.

* * *

## 测试整合

### 单位/组件测试(Vitest + jsdom)

见 _阴道/jsdom_ 页:1 `@codaco/interview` 内 您的配置使得 CSS 的副作用 解决干净.

### 端对端( Playwright)

对于 e2e , 您通常想要对主机的数据进行确定性立方 层。 软件包通过 `isE2E` 旗帜,哪个 曝光 Redux 现场商店 `window.__interviewStore` 这样固定 可以在不点击数据库的情况下读取网络状态 :

```ts
<Shell {...props} flags={{ isE2E: true }} />
```

软件包自己的e2e套件(在此repo中)使用Vite主机 执行 `window.__test` 钩子 `installProtocol` 编号 : `createInterview` 编号 : `reset`,运行在官方 `mcr.microsoft.com/playwright` 图像 表示反对每阶段截图 `e2e/visual-snapshots/{chromium,firefox,webkit}-matrix/`。用作参考 用于连接您的 e2e 设置 — 见 [`e2e/README.md`](https://npmcn.com/package/%40codaco/interview/e2e/README.md) 联 合 国 版式和测试授权模式.

对于交互式调试( 无试跑器) , 运行 **`pnpm dev:host`** 到启动 vite 主机 + 资产服务器, 准备仓储协议并打开浏览器 页:1.

* * *

## 什么活在这个包里,什么没有

在:

-   redux 商店 + 每个压缩器 / 选择器 / tunk
-   所有17个相位接口和导航铬
-   对话框系统、土司系统和相位错误边界
-   合成网络生成器
-   合同类型和主机序列图

输出 :

-   所有触及数据库、会话饼干或网络的东西
-   协议解析和验证(使用) `@codaco/protocol-validation`(中文(简体) )
-   导出为 GraphML / CSV( 使用) `@codaco/network-exporters`(中文(简体) )
-   网络过滤/查询DSL(使用) `@codaco/network-query`(中文(简体) )

当有疑问时:如果将它嵌入一个 非弗雷斯科主机(Architect的预览模式,一个CLI,一个Electron app), 它 属于这里。 如果它和弗雷斯科的Postgres 或它的认证层说话,它 没有.

* * *

## 许可证

MIT — 见寄存器根.

## 常见问题

### @codaco/interview 是什么？

网络Canvas访谈引擎——壳牌组件,会话合同\.

### 如何安装 @codaco/interview？

在项目目录运行 npm install @codaco/interview，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：react（^19\.3\.0）、effect（^4\.0\.0）、motion（^13\.4\.6）、react-dom（^19\.3\.0）、tailwindcss（^4\.3\.3）、@codaco/fresco-ui（^8\.1\.0）、@codaco/shared-consts（^6\.3\.0）、@codaco/tailwind-config（^1\.5\.1）、@codaco/protocol-validation（^15\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @codaco/interview 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @codaco/interview 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @codaco/interview 可以商用吗？

@codaco/interview 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @codaco/interview 的下载量有多少？

根据 npm 官方下载统计，@codaco/interview 在 2026/10/10 – 2026/11/08 这 30 天内被下载 3,765 次，日均约 126 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @codaco/interview 还在维护吗？

最新版本 11\.0\.0 发布于 2026/10/21（19 天前）；至今共发布 47 个版本，其中过去 12 个月发布了 47 个；首次发布于 2026/05/05。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

