接入 Prototype 體驗工具
本能力自 19.0.0-alpha.139 提供。消費端須先升級並 pin 具體版本,再接入自己的
fixture 與產品流程。既有 mockAuthPlugin() 的未採用者維持原行為。
1. 定義應用的情境與獨立 fixture
在 Vite config 旁的 scripts/ 組裝 PrototypeDefinition,factory 可放在 src/mocks/data/。
每次呼叫建立全新的資料與資源,不讀寫 browser seed 或共享 module Map。框架不提供產品情境。
import type { PrototypeDefinition, PrototypeFixture } from '@appfuse/appfuse-web/vite'
declare function createFixture(context: Parameters<PrototypeDefinition['createFixture']>[0]): Promise<PrototypeFixture>
export const definition: PrototypeDefinition = {
id: 'example-app',
initialScenario: 'initial',
scenarios: [{ id: 'initial', label: 'Initial', description: 'Only the adopted example domain is reset.' }],
faults: [], // 無故障能力時 fixture 不提供 applyFault
links: [{ id: 'entry', label: 'Open example', href: '/example' }],
createFixture,
}
fixture 必須有 dispatch 與冪等、可重試的 dispose;可選 mailbox/applyFault 的能力集合須跨
情境一致。故障清冊不放保留的 normal;非空 faults 必須有 applyFault,normal 清故障但保留資料。
dispatch 接收 {domain,action,data},回 {status,body};await 後的重要寫入前核對
lease.assertCurrent(),並回應 lease.signal。factory 的 now 是自然時間,不改 UI 時鐘。
2. 在 DEV+既有 MSW 模式組裝 Node plugin
import { defineConfig, loadEnv } from 'vite'
import { prototypeToolsPlugin } from '@appfuse/appfuse-web/vite'
import { definition } from './scripts/prototype-definition'
export default defineConfig(({ command, mode }) => ({
plugins: command === 'serve' && loadEnv(mode, process.cwd(), '').VITE_MSW === 'true'
? [prototypeToolsPlugin(definition)] : [],
}))
與既有 React、alias、proxy 等 plugin/設定合併;同一 server 不能再註冊 mockAuthPlugin。 不新增 HTTP 或 Prototype 環境旗標。browser src 不匯入 scripts 或 Node 入口。 Vite base 與既有 environ.basename 須一致;首版支援由英數、底線、連字號 path segment 組成的 base。 代理須保留瀏覽器 Host 與 scheme,不信任任意 X-Forwarded-*。
3. 啟動 MSW 前確定 backend
import { createPrototypeClient } from '@appfuse/appfuse-web/prototype'
export const prototypeClient = createPrototypeClient()
// 由 App 在 import.meta.env.DEV && 既有 MSW enabled 分支動態載入本模組。
const result = await prototypeClient.initialize()
// enabled → 已接域使用 Node adapter;disabled → 明確選既有 adapter,再啟動 worker。
先 configure environ,再 initialize。只有直接 404/200 text/html 才是 disabled;其餘 discovery 錯誤須顯示可重試失敗,不能當 plugin 缺席。啟用後也不能因 409/503 退回全域 seed/Session store。 固定時鐘回歸沿既有 adapter,須測 disabled 分支;自然時間體驗沿 Node 容器。
已接域的 MSW handler 使用 client.fetch 呼叫 fixture endpoint,產品 service 仍走 apiClient 的正式 產品 URL,不認識工具協定。例如 handler 將產品查詢轉為:
const response = await prototypeClient.fetch('/api/__prototype/fixture', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ domain: 'example', action: 'read', data: {} }),
})
// 保留 response.status/body,不能將錯誤當成空資料。
client 自動附加初始化時的 generation,只允許同源 fixture 路徑。未接域沿用原 browser seed, 在情境描述揭露工具涵蓋範圍;不要同時維護同一已接域的兩份權威資料。
4. 掛入 Router 與清理回呼
已啟用才由 DEV lazy branch 載入工具 surface,Provider 放在 Router 內:
import { Outlet } from 'react-router'
import { PrototypeProvider, PrototypeToolsEntry, PrototypeToolsPage, PrototypeToolsLink } from '@appfuse/appfuse-web/prototype'
function Surface() {
return (
<PrototypeProvider client={prototypeClient} diagnostics={coordinator.diagnostics} onExperienceChanged={clearKnownRuntime}>
<Outlet />
<PrototypeToolsEntry />
</PrototypeProvider>
)
}
// 將 /_prototype/tools 對應至 <PrototypeToolsPage />。
// 登入 dialog 內可放 <PrototypeToolsLink />,使入口位於 dialog 的 focus scope。
工具 route 應放在應用既有 Layout 的內容區。PrototypeToolsPage 只提供可嵌入內容,
寬度、留白、背景與頁面高度由應用外框決定;框架不另加最大寬度、置中、外層內距或視窗最小高度。
獨立 route 由應用自行提供外層頁面容器,仍使用 <PrototypeToolsPage />,無需設定模式或覆寫框架內部 CSS。
clearKnownRuntime 使用穩定回呼,清除已知 runtime Session、query cache 與流程 locator;不做全域 storage.clear()。未接入 Session coordinator 的應用省略 diagnostics;不可從 hostname 推算假的降級狀態。 框架透過既有 i18n 的 t() 顯示通用文案、錯誤碼與應用 label/description,詞彙依應用流程載入。
Provider 負責可見頁輪詢、focus 重查與 BroadcastChannel 失效通知。換 generation 清理後 reload, 只換 revision 更新摘要;不重送舊操作。工具入口開具名分頁,Alt+Shift+P 忽略 IME/repeat。 原產品表單不因開工具而消失,工具只準備 Given/取碼,使用者仍回產品頁驗證及完成操作。
5. 沿用 Native Session 接縫
將 manifest.sessionScope 的 cookieName/cookiePath 與同源 apiOrigin 傳給既有
createSessionCoordinator,將 client.nativeBaseURL() 傳給 createMockAccessSessionClient。
產品先驗證 proof,再在既有 lease 中 issue/refresh/revoke;工具沒有免驗證登入。
Refresh 只經 HttpOnly Cookie,Access 只留 runtime memory。
安全來源維持 Web Locks;開發模式 LAN HTTP 沿用既有自動相容政策, 不增加設定。降級只保證單 context 佇列,跨分頁競爭可能失敗;UI 的提示依 coordinator.diagnostics。
6. 驗證生命週期與正式排除
下列 Session 期限協調與保持登入的 Scope Cookie 支援自 alpha.140 提供; alpha.139 的首版容器固定兩小時並使用工作階段 Scope Cookie,不能用升版前的結果宣稱已具此能力。
- 同 context 共用,同 Email 的不同 context 仍隔離;重設不清除其他體驗。
- 重建成功才替換,新世代 revision 0;準備失敗保留舊體驗,舊請求/Cookie 不污染新世代。
- 自然時間,匿名體驗無壓力時閒置 120 分鐘;登入後依原 Native Session 有效期限保留。 保持登入時 Scope Cookie 同步持久保存,初始化不降回工作階段 Cookie;一般登入不跨瀏覽器關閉保存。 登出/撤銷後回到匿名期限。不新增設定,不改原 Native 固定/滑動期限;查詢與工具不額外延長登入資格。 上限仍為 128 份(含候選及待清理資源),容量壓力可能提早逐出;Vite 重啟仍重置記憶體 Mock。 先逐出未互動者,再依 LRU;全 busy 暫時 503+Retry-After: 1。state/信箱/poll/focus/Native 自動 refresh/inspect 不續命,可信互動及有效業務操作才續命。
- 409 experience-unavailable 顯示「已到期或因資源回收,請重新開始」;revision-conflict 重讀狀態; fixture/容量/來源/協定錯誤保留各自 errorCode,不統一偽裝成服務故障。
- 信箱更新失敗保留前次資料並標 stale;無有效 Clipboard 仍能手動複製,不替人登入。
- MSW=false 不發 discovery;production/preview 無工具 route、監聽、工具 API。掃描 production JS 及 source-map sources,確認沒有 Prototype 工具來源模組。
- promote 移除 scripts,將 Vite prototypeToolsPlugin 接線最小轉換為 mockAuthPlugin,沿用 DEV/MSW 條件與產品 service 的真 API 接縫。先在暫存投影驗證型別、build 及 DEV+MSW 登入/重載,再依既有作者權流程投影,不直接改配對 web 的業務碼。
API 與型別以套件 lib/prototype/README.md、lib/vite/README.md 為準;架構理由見
ADR-011。