@codaco/interview

中文文档
11.0.0发布于 2026/10/21MITTypeScriptES Module构建来源已验证近 30 天下载 3765

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

中文使用文档
文档目录20 节
  1. 安装
  2. 尾风
  3. 阴道/jsdom
  4. 壳牌部分
  5. 为什么 currentStep 主办国?
  6. 果壳道具
  7. 烤箱视图
  8. 建设 InterviewPayload 服务器侧
  9. Sparse 实体属性流
  10. 公共 API 参考
  11. 构成部分
  12. Schemas + 帮助者
  13. 合成数据
  14. 公共类型
  15. 主题 & DOM 范围
  16. 测试整合
  17. 单位/组件测试(Vitest + jsdom)
  18. 端对端( Playwright)
  19. 什么活在这个包里,什么没有
  20. 许可证

@codaco/interview

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

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


安装

pnpm add @codaco/interview

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

{
  "@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文件都有一个关切 消费者按下列顺序进口:

/* 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 的项目 :

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

壳牌部分

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

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

'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
payloadInterviewPayload对{ session, protocol } ——见外形类型. 每个商店创建一次 payload.session.id;通过稳定的引用.
currentStepnumber对参与者的舞台指数。 宿主拥有.
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 。 被称为懒惰的阶段上架.
analyticsInterviewAnalyticsMetadata对主机元数据作为每个事件的超级属性: installationId (匿名主机UUID), hostApp (例如:. "Fresco", (中文(简体) ), hostVersion?。 。 。 .
posthogClient邮购客户端( 需要) capture, (中文(简体) ), captureException, (中文(简体) ), register(中文(简体) )没有已初始化的 PostHog 客户端 。 当提供时,包会通过它发出事件而不修改其配置. 缺席时, 包会懒惰地初始化它自己命名的实例 ph-relay.networkcanvas.com。 。 。 .
disableAnalyticsboolean没有何时 true,所有事件排放均被抑制(否) posthog-js 导入)。 默认 false。用于E2E和合成访问运行.
finishConfirmationDescriptionReactNode没有完成确认对话框中显示的特定主机解释 。 不承诺响应的局部中性引导的默认是不可改变的. 一个已订阅的消息组件可以保持一个主机覆盖,以响应语言变化.
requestedLocalestring | readonly string[] | null没有用户偏好,解决了主机地块,或者命令了地块请求. 该软件包会与其自己支持的界面语言进行谈判;未匹配的请求使用英语.
localePreferencestring | null没有可选的控制菜单选择, 配对 onLocaleChange。字符串选择最佳支持的匹配; null 如下 requestedLocale。关闭它以保留菜单选择到软件包的地方.
onLocaleChange(locale: string | null) => void没有在菜单选择后调用, 这样主机就可以坚持它 . null 表示再次遵循主机请求.
catalogInterviewCatalog没有界面语言的消息来自 loadInterviewCatalog,基于同样的 requestedLocale 财务报告和审定财务报表 localePreference. 让访谈渲染出水合物,而不等待该语言的下载。 只在符合谈判语言时使用.
allowLanguageSelectionboolean没有在设置菜单中显示界面语言选择器 。 默认为 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。 。 。 .

烤箱视图

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

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


建设 InterviewPayload 服务器侧

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

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, (中文(简体) ), '',以及 [] 答复是明确的,并一直在场.

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 合同将设定定义值与 删除属性 :

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 - 伙伴类型.

公共类型

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数字 :

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 这样固定 可以在不点击数据库的情况下读取网络状态 :

<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 联 合 国 版式和测试授权模式.

对于交互式调试( 无试跑器) , 运行 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 共发布 47 个版本,首次发布于 2026/05/05;过去 12 个月发布了 47 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
11.0.0latest2026/10/2119 天前
10.0.02026/10/1327 天前
9.1.02026/09/291 个月前
9.0.12026/09/022 个月前
9.0.02026/08/312 个月前
8.0.02026/08/112 个月前

其他发布通道:alpha → 1.0.0-alpha.17。

关于 @codaco/interview 的常见问题

@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 和提交记录判断。