客户按 App、环境和宿主 externalUserId 唯一。Sandbox 与 Live 独立;控制台成员和 SDK 客户是不同身份。范围头只核对凭据,不授予权限或切换 App。游客由后端持有游客凭证,支付前完成真实认证。
App、环境与身份
理解组织、App、客户与两类凭据的边界。
SDK 0.0.1更新于
App、环境与客户
会话与登出
- Token 只存在 SDK 内存。过期后向自己的已认证后端刷新;SDK 不自动刷新。
- 登出或切换账号时先销毁展示并 disconnectSession,中止尚未完成的宿主会话请求,避免旧 Token 再次 identify。重试写入复用原 requestId / 幂等键。
- 宿主后端可以撤销单个会话或客户的全部会话;撤销签发 Key 后,客户 Token 在下次请求失效。
import { GrooveError } from "@tinkerlandai/indiana-sdk";import { grow } from "./groove-client"; // 向你自己的服务端取新 Token(会话接口需要你的登录态)let generation = 0;let pending: { controller: AbortController; promise: Promise<void> } | undefined; function refreshSession(): Promise<void> { if (pending) return pending.promise; const expected = generation; const controller = new AbortController(); const promise = (async () => { const response = await fetch("/api/groove-session", { signal: controller.signal, cache: "no-store", credentials: "same-origin", }); if (!response.ok) throw new Error("用户未登录"); const { customerToken } = await response.json(); if (expected !== generation) throw new GrooveError("IDENTITY_CHANGED", "Identity changed during refresh"); if (typeof customerToken !== "string") throw new Error("Invalid session"); grow.identify(customerToken); })(); pending = { controller, promise }; const clear = () => { if (pending?.promise === promise) pending = undefined; }; void promise.then(clear, clear); return promise;} // Token 过期(默认 10 分钟)时重新签发一次再重试export async function withSession<T>(call: () => Promise<T>) { const expected = generation; try { return await call(); } catch (error) { if (!(error instanceof GrooveError) || error.code !== "UNAUTHENTICATED") throw error; if (expected !== generation) throw new GrooveError("IDENTITY_CHANGED", "Identity changed"); await refreshSession(); if (expected !== generation) throw new GrooveError("IDENTITY_CHANGED", "Identity changed"); return call(); }} // 你的用户退出登录时:先丢弃 Token,再走你自己的登出export function disconnectSession() { generation++; pending?.controller.abort(); pending = undefined; grow.reset(); // 清除客户 Token,中止进行中的请求} export async function signOut() { disconnectSession(); await fetch("/api/logout", { method: "POST" });}