@viceme-ai/sdk 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -45
- package/dist/browser-events.d.ts +49 -0
- package/dist/browser-events.d.ts.map +1 -0
- package/dist/capability-mount.d.ts +15 -0
- package/dist/capability-mount.d.ts.map +1 -0
- package/dist/chunks/{client-DKsaEtwq.js → client-Cbldd7F2.js} +321 -313
- package/dist/chunks/client-Cbldd7F2.js.map +1 -0
- package/dist/chunks/{version-C3KVoLNb.js → errors-B-Qv_azy.js} +4 -7
- package/dist/chunks/errors-B-Qv_azy.js.map +1 -0
- package/dist/chunks/version-C8DeBm39.js +6 -0
- package/dist/chunks/version-C8DeBm39.js.map +1 -0
- package/dist/core/build-endpoints.d.ts +2 -0
- package/dist/core/build-endpoints.d.ts.map +1 -1
- package/dist/core/capabilities.d.ts +2 -2
- package/dist/core/capabilities.d.ts.map +1 -1
- package/dist/core/client.d.ts +4 -2
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/lifecycle.d.ts +2 -4
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/presentation.d.ts +7 -2
- package/dist/core/presentation.d.ts.map +1 -1
- package/dist/danmaku/index.d.ts +1 -0
- package/dist/danmaku/index.d.ts.map +1 -1
- package/dist/danmaku/mount.d.ts +1 -1
- package/dist/danmaku/mount.d.ts.map +1 -1
- package/dist/danmaku.js +127 -121
- package/dist/danmaku.js.map +1 -1
- package/dist/generated/public-contract.d.ts +54 -410
- package/dist/generated/public-contract.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -21
- package/dist/index.js.map +1 -1
- package/dist/loader/attributes.d.ts +3 -2
- package/dist/loader/attributes.d.ts.map +1 -1
- package/dist/loader/auto-loader.d.ts +1 -1
- package/dist/loader/auto-loader.d.ts.map +1 -1
- package/dist/loader/events.d.ts +1 -44
- package/dist/loader/events.d.ts.map +1 -1
- package/dist/loader/registry.d.ts +2 -1
- package/dist/loader/registry.d.ts.map +1 -1
- package/dist/manifest.json +41 -28
- package/dist/session/session.d.ts +7 -6
- package/dist/session/session.d.ts.map +1 -1
- package/dist/testing/test-adapter.d.ts +1 -1
- package/dist/testing/test-adapter.d.ts.map +1 -1
- package/dist/testing.js +7 -6
- package/dist/testing.js.map +1 -1
- package/dist/tip/index.d.ts +22 -0
- package/dist/tip/index.d.ts.map +1 -0
- package/dist/tip/mount.d.ts +5 -0
- package/dist/tip/mount.d.ts.map +1 -0
- package/dist/tip.js +137 -0
- package/dist/tip.js.map +1 -0
- package/dist/transport/transport.d.ts +3 -1
- package/dist/transport/transport.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/viceme.min.js +2 -22
- package/dist/viceme.min.js.map +1 -1
- package/package.json +6 -2
- package/dist/chunks/client-DKsaEtwq.js.map +0 -1
- package/dist/chunks/version-C3KVoLNb.js.map +0 -1
- package/dist/loader/mount-handle.d.ts +0 -25
- package/dist/loader/mount-handle.d.ts.map +0 -1
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sources":["../src/testing/test-adapter.ts"],"sourcesContent":["/**\n * `@viceme-ai/sdk/testing` — explicit test adapter.\n *\n * Deterministic fixture helpers for SDK consumers and capability authors.\n * This code must never be imported from production core, loader, or capability\n * bundles; it is excluded from them at build time and only exported via the\n * `./testing` subpath.\n *\n * The test client runs the exact same config, lifecycle, error, and contract\n * validation as production `createViceMe()` — it is not a bypass mock.\n */\n\nimport { configInvalid } from '../core/errors.ts';\nimport { isValidWorkKey, isValidRegion } from '../core/config.ts';\nimport { ViceMeClientImpl } from '../core/client.ts';\nimport type { ViceMeClient } from '../core/client.ts';\nimport type { ViceMeRegion } from '../core/config.ts';\nimport type { AccessPresenter } from '../core/presentation.ts';\nimport type { Transport, TransportRequest, TransportResponse } from '../transport/transport.ts';\n\nexport interface MemoryTransportWorkFixture {\n key: string;\n capabilities: string[];\n token?: string;\n expiresAt?: number;\n}\n\nexport interface MemoryTransportOptions {\n /** Work descriptor served for `POST /v1/public/
|
|
1
|
+
{"version":3,"file":"testing.js","sources":["../src/testing/test-adapter.ts"],"sourcesContent":["/**\n * `@viceme-ai/sdk/testing` — explicit test adapter.\n *\n * Deterministic fixture helpers for SDK consumers and capability authors.\n * This code must never be imported from production core, loader, or capability\n * bundles; it is excluded from them at build time and only exported via the\n * `./testing` subpath.\n *\n * The test client runs the exact same config, lifecycle, error, and contract\n * validation as production `createViceMe()` — it is not a bypass mock.\n */\n\nimport { configInvalid } from '../core/errors.ts';\nimport { isValidWorkKey, isValidRegion } from '../core/config.ts';\nimport { ViceMeClientImpl } from '../core/client.ts';\nimport type { ViceMeClient } from '../core/client.ts';\nimport type { ViceMeRegion } from '../core/config.ts';\nimport type { AccessPresenter } from '../core/presentation.ts';\nimport type { Transport, TransportRequest, TransportResponse } from '../transport/transport.ts';\n\nexport interface MemoryTransportWorkFixture {\n key: string;\n capabilities: string[];\n token?: string;\n expiresAt?: number;\n}\n\nexport interface MemoryTransportOptions {\n /** Work descriptor served for `POST /v1/public/work-sdk/sessions`. */\n work: MemoryTransportWorkFixture;\n /** Artificial response latency in milliseconds (default 0). */\n latencyMs?: number;\n /**\n * Queue of failures thrown before the fixture response is served; each\n * entry is consumed by one session request. Use for retry/timeout tests.\n */\n sessionFailures?: Array<Error | { status: number; code: string; message?: string }>;\n}\n\nexport interface MemoryTransport extends Transport {\n /** Every request seen so far, for assertions. */\n readonly requests: TransportRequest[];\n}\n\nexport function createMemoryTransport(options: MemoryTransportOptions): MemoryTransport {\n const requests: TransportRequest[] = [];\n const failures = [...(options.sessionFailures ?? [])];\n const latencyMs = options.latencyMs ?? 0;\n\n return {\n requests,\n async request(request: TransportRequest): Promise<TransportResponse> {\n // A pre-aborted signal never counts as an issued request.\n if (request.signal?.aborted) {\n throw new DOMException('Aborted', 'AbortError');\n }\n requests.push(request);\n await new Promise<void>((resolve, reject) => {\n const signal = request.signal;\n const timer = setTimeout(() => resolve(), latencyMs);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n if (request.path === '/v1/public/work-sdk/sessions' && request.method === 'POST') {\n const failure = failures.shift();\n if (failure) {\n if (failure instanceof Error) throw failure;\n return {\n status: failure.status,\n body: {\n error: { code: failure.code, message: failure.message ?? 'Simulated failure.' },\n },\n };\n }\n return {\n status: 201,\n body: {\n workKey: options.work.key,\n capabilities: [...options.work.capabilities],\n ...(options.work.token !== undefined ? { token: options.work.token } : {}),\n ...(options.work.expiresAt !== undefined\n ? { expiresAt: new Date(options.work.expiresAt).toISOString() }\n : {}),\n },\n requestId: `test-${requests.length}`,\n };\n }\n return {\n status: 404,\n body: { error: { code: 'CONFIG_INVALID', message: `No fixture for ${request.path}.` } },\n };\n },\n };\n}\n\nexport interface CreateTestViceMeOptions {\n workKey: string;\n region: ViceMeRegion;\n /** Mock transport serving the fixture contract. */\n transport: Transport;\n signal?: AbortSignal;\n presenter?: AccessPresenter;\n /** Stable virtual clock for deterministic time-based assertions. */\n now?: () => number;\n}\n\n/**\n * Create a client backed by an injected transport. Runs identical validation\n * and lifecycle logic to `createViceMe()`.\n */\nexport function createTestViceMe(options: CreateTestViceMeOptions): ViceMeClient {\n if (!isValidWorkKey(options.workKey)) {\n throw configInvalid('Test client requires a valid workKey (\"wrk_…\").');\n }\n if (!isValidRegion(options.region)) {\n throw configInvalid('Test client requires region \"cn\" or \"global\".');\n }\n if (typeof options.transport?.request !== 'function') {\n throw configInvalid('Test client requires a transport with a request() method.');\n }\n return new ViceMeClientImpl({\n config: {\n workKey: options.workKey,\n region: options.region,\n signal: options.signal,\n },\n transport: options.transport,\n presenter: options.presenter,\n ...(options.now !== undefined ? { now: options.now } : {}),\n });\n}\n\n/** Standard fixture work used across repo tests and examples. */\nexport const FIXTURE_WORK: MemoryTransportWorkFixture = {\n key: 'wrk_test',\n capabilities: ['fixture'],\n token: 'test-session-token',\n};\n"],"names":["createMemoryTransport","options","requests","failures","latencyMs","request","resolve","reject","signal","timer","failure","createTestViceMe","isValidWorkKey","configInvalid","isValidRegion","ViceMeClientImpl","FIXTURE_WORK"],"mappings":";;AA4CO,SAASA,EAAsBC,GAAkD;AACtF,QAAMC,IAA+B,CAAA,GAC/BC,IAAW,CAAC,GAAIF,EAAQ,mBAAmB,CAAA,CAAG,GAC9CG,IAAYH,EAAQ,aAAa;AAEvC,SAAO;AAAA,IACL,UAAAC;AAAA,IACA,MAAM,QAAQG,GAAuD;AAEnE,UAAIA,EAAQ,QAAQ;AAClB,cAAM,IAAI,aAAa,WAAW,YAAY;AAehD,UAbAH,EAAS,KAAKG,CAAO,GACrB,MAAM,IAAI,QAAc,CAACC,GAASC,MAAW;AAC3C,cAAMC,IAASH,EAAQ,QACjBI,IAAQ,WAAW,MAAMH,EAAA,GAAWF,CAAS;AACnD,QAAAI,GAAQ;AAAA,UACN;AAAA,UACA,MAAM;AACJ,yBAAaC,CAAK,GAClBF,EAAO,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,UAClD;AAAA,UACA,EAAE,MAAM,GAAA;AAAA,QAAK;AAAA,MAEjB,CAAC,GACGF,EAAQ,SAAS,kCAAkCA,EAAQ,WAAW,QAAQ;AAChF,cAAMK,IAAUP,EAAS,MAAA;AACzB,YAAIO,GAAS;AACX,cAAIA,aAAmB,MAAO,OAAMA;AACpC,iBAAO;AAAA,YACL,QAAQA,EAAQ;AAAA,YAChB,MAAM;AAAA,cACJ,OAAO,EAAE,MAAMA,EAAQ,MAAM,SAASA,EAAQ,WAAW,qBAAA;AAAA,YAAqB;AAAA,UAChF;AAAA,QAEJ;AACA,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,YACJ,SAAST,EAAQ,KAAK;AAAA,YACtB,cAAc,CAAC,GAAGA,EAAQ,KAAK,YAAY;AAAA,YAC3C,GAAIA,EAAQ,KAAK,UAAU,SAAY,EAAE,OAAOA,EAAQ,KAAK,MAAA,IAAU,CAAA;AAAA,YACvE,GAAIA,EAAQ,KAAK,cAAc,SAC3B,EAAE,WAAW,IAAI,KAAKA,EAAQ,KAAK,SAAS,EAAE,YAAA,EAAY,IAC1D,CAAA;AAAA,UAAC;AAAA,UAEP,WAAW,QAAQC,EAAS,MAAM;AAAA,QAAA;AAAA,MAEtC;AACA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,SAAS,kBAAkBG,EAAQ,IAAI,IAAA,EAAI;AAAA,MAAE;AAAA,IAE1F;AAAA,EAAA;AAEJ;AAiBO,SAASM,EAAiBV,GAAgD;AAC/E,MAAI,CAACW,EAAeX,EAAQ,OAAO;AACjC,UAAMY,EAAc,iDAAiD;AAEvE,MAAI,CAACC,EAAcb,EAAQ,MAAM;AAC/B,UAAMY,EAAc,+CAA+C;AAErE,MAAI,OAAOZ,EAAQ,WAAW,WAAY;AACxC,UAAMY,EAAc,2DAA2D;AAEjF,SAAO,IAAIE,EAAiB;AAAA,IAC1B,QAAQ;AAAA,MACN,SAASd,EAAQ;AAAA,MACjB,QAAQA,EAAQ;AAAA,MAChB,QAAQA,EAAQ;AAAA,IAAA;AAAA,IAElB,WAAWA,EAAQ;AAAA,IACnB,WAAWA,EAAQ;AAAA,IACnB,GAAIA,EAAQ,QAAQ,SAAY,EAAE,KAAKA,EAAQ,QAAQ,CAAA;AAAA,EAAC,CACzD;AACH;AAGO,MAAMe,IAA2C;AAAA,EACtD,KAAK;AAAA,EACL,cAAc,CAAC,SAAS;AAAA,EACxB,OAAO;AACT;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ViceMeClient } from '../core/client.ts';
|
|
2
|
+
export interface TipMountOptions {
|
|
3
|
+
target: Element;
|
|
4
|
+
theme: 'light' | 'dark' | 'auto';
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}
|
|
7
|
+
export interface TipMountHandle {
|
|
8
|
+
readonly capability: 'tip';
|
|
9
|
+
destroy(): void;
|
|
10
|
+
}
|
|
11
|
+
export interface TipWidgetCloseDetail {
|
|
12
|
+
workId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TipPaidDetail {
|
|
15
|
+
workId: string;
|
|
16
|
+
orderNo: string;
|
|
17
|
+
status: 'PAID';
|
|
18
|
+
amountCents: number;
|
|
19
|
+
}
|
|
20
|
+
/** Explicit ESM/npm entry point; the CDN loader calls the same mount function. */
|
|
21
|
+
export declare function mountTip(client: ViceMeClient, options: TipMountOptions): Promise<TipMountHandle>;
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tip/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;IAC3B,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAgChG"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ViceMeClient } from '../core/client.ts';
|
|
2
|
+
import type { CapabilityMountHandle, CapabilityMountOptions } from '../capability-mount.ts';
|
|
3
|
+
export declare const FRAME_READY_TIMEOUT_MS = 8000;
|
|
4
|
+
export declare function mount(client: ViceMeClient, options: CapabilityMountOptions): Promise<CapabilityMountHandle>;
|
|
5
|
+
//# sourceMappingURL=mount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/tip/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD,OAAO,KAAK,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAS5F,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAM5C,wBAAsB,KAAK,CACzB,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAsLhC"}
|
package/dist/tip.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { c as p, V as b, B as N } from "./chunks/errors-B-Qv_azy.js";
|
|
2
|
+
const M = /* @__PURE__ */ new Map([
|
|
3
|
+
["viceme:ready", /* @__PURE__ */ new Set(["clientKey", "workKey", "capabilities", "version"])],
|
|
4
|
+
["viceme:capability-ready", /* @__PURE__ */ new Set(["clientKey", "instanceKey", "capability", "version"])],
|
|
5
|
+
[
|
|
6
|
+
"viceme:error",
|
|
7
|
+
/* @__PURE__ */ new Set(["clientKey", "instanceKey", "capability", "code", "retryable", "requestId"])
|
|
8
|
+
],
|
|
9
|
+
["viceme:destroyed", /* @__PURE__ */ new Set(["clientKey", "instanceKey", "capability"])],
|
|
10
|
+
["viceme:widget-close", /* @__PURE__ */ new Set(["workId"])],
|
|
11
|
+
["viceme:tip-paid", /* @__PURE__ */ new Set(["workId", "orderNo", "status", "amountCents"])]
|
|
12
|
+
]);
|
|
13
|
+
function D(i, t) {
|
|
14
|
+
const a = M.get(i), n = {};
|
|
15
|
+
for (const [s, o] of Object.entries(t))
|
|
16
|
+
a.has(s) && o !== void 0 && (n[s] = o);
|
|
17
|
+
return n;
|
|
18
|
+
}
|
|
19
|
+
function k(i, t, a) {
|
|
20
|
+
const n = new CustomEvent(t, {
|
|
21
|
+
detail: D(t, a),
|
|
22
|
+
bubbles: !0,
|
|
23
|
+
composed: !0,
|
|
24
|
+
cancelable: !1
|
|
25
|
+
});
|
|
26
|
+
i.dispatchEvent(n);
|
|
27
|
+
}
|
|
28
|
+
const R = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, S = /^VT\d{14}[0-9a-f]{12}$/, C = 100, O = 2e4, K = 2048, P = 8e3;
|
|
29
|
+
function x(i) {
|
|
30
|
+
return typeof i == "object" && i !== null;
|
|
31
|
+
}
|
|
32
|
+
async function U(i, t) {
|
|
33
|
+
if (t.signal?.aborted) throw p();
|
|
34
|
+
if (await i.ready(), t.signal?.aborted) throw p();
|
|
35
|
+
if (!i.hasCapability("tip"))
|
|
36
|
+
throw new b({
|
|
37
|
+
code: "CAPABILITY_DISABLED",
|
|
38
|
+
message: "Tip is not available in this SDK build.",
|
|
39
|
+
retryable: !1,
|
|
40
|
+
capability: "tip"
|
|
41
|
+
});
|
|
42
|
+
const a = t.target.ownerDocument, n = a.defaultView;
|
|
43
|
+
if (!n)
|
|
44
|
+
throw new b({
|
|
45
|
+
code: "CONFIG_INVALID",
|
|
46
|
+
message: "Tip requires a browser document.",
|
|
47
|
+
retryable: !1,
|
|
48
|
+
capability: "tip"
|
|
49
|
+
});
|
|
50
|
+
const s = N[i.region], o = t.theme === "auto" ? n.matchMedia("(prefers-color-scheme: dark)") : void 0;
|
|
51
|
+
let l = t.theme === "auto" ? o?.matches ? "dark" : "light" : t.theme, y = !1, m, u;
|
|
52
|
+
const c = a.createElement("div");
|
|
53
|
+
c.dataset.vicemeTip = "mounted", c.style.all = "initial", c.style.display = "block", c.style.width = "100%", c.style.maxWidth = "100%", c.style.contain = "layout style";
|
|
54
|
+
const L = c.attachShadow({ mode: "open" }), r = a.createElement("iframe"), h = new URL(`/widget/tip/${encodeURIComponent(i.workKey)}`, s);
|
|
55
|
+
h.searchParams.set("appearance", l), r.title = "ViceMe Tip", r.src = h.toString(), r.loading = "eager", r.referrerPolicy = "strict-origin", r.setAttribute(
|
|
56
|
+
"sandbox",
|
|
57
|
+
"allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
|
58
|
+
), r.setAttribute("allow", "payment"), r.style.boxSizing = "border-box", r.style.display = "block", r.style.width = "100%", r.style.height = "0px", r.style.margin = "0", r.style.border = "0", r.style.background = "transparent", r.style.colorScheme = l, r.style.pointerEvents = "none", L.append(r);
|
|
59
|
+
let v, I;
|
|
60
|
+
const _ = new Promise((d, e) => {
|
|
61
|
+
v = d, I = e, m = n.setTimeout(() => {
|
|
62
|
+
e(
|
|
63
|
+
new b({
|
|
64
|
+
code: "INTERNAL_ERROR",
|
|
65
|
+
message: "Hosted Tip widget did not become ready in time.",
|
|
66
|
+
retryable: !0,
|
|
67
|
+
capability: "tip"
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
}, P);
|
|
71
|
+
}), E = (d) => {
|
|
72
|
+
if (d.origin !== s || d.source !== r.contentWindow) return;
|
|
73
|
+
const e = d.data;
|
|
74
|
+
if (!(!x(e) || typeof e.type != "string")) {
|
|
75
|
+
if (e.type === "viceme:widget-resize") {
|
|
76
|
+
if (typeof e.workId != "string" || !R.test(e.workId) || typeof e.height != "number" || !Number.isInteger(e.height) || e.height < 1 || e.height > K || u !== void 0 && e.workId !== u)
|
|
77
|
+
return;
|
|
78
|
+
r.style.height = `${e.height}px`, u === void 0 && (u = e.workId, r.style.pointerEvents = "auto", m !== void 0 && (n.clearTimeout(m), m = void 0), v?.());
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!(typeof e.workId != "string" || e.workId !== u)) {
|
|
82
|
+
if (e.type === "viceme:widget-close") {
|
|
83
|
+
k(t.target, "viceme:widget-close", { workId: e.workId });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
e.type === "viceme:tip-paid" && typeof e.orderNo == "string" && S.test(e.orderNo) && e.status === "PAID" && typeof e.amountCents == "number" && Number.isInteger(e.amountCents) && e.amountCents >= C && e.amountCents <= O && k(t.target, "viceme:tip-paid", {
|
|
87
|
+
workId: e.workId,
|
|
88
|
+
orderNo: e.orderNo,
|
|
89
|
+
status: "PAID",
|
|
90
|
+
amountCents: e.amountCents
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}, g = (d) => {
|
|
95
|
+
l = d.matches ? "dark" : "light", r.style.colorScheme = l, r.contentWindow?.postMessage(
|
|
96
|
+
{ type: "viceme:widget-appearance", appearance: l },
|
|
97
|
+
s
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
let w;
|
|
101
|
+
const T = () => {
|
|
102
|
+
I?.(p()), f();
|
|
103
|
+
}, f = () => {
|
|
104
|
+
y || (y = !0, m !== void 0 && n.clearTimeout(m), n.removeEventListener("message", E), w?.(), t.signal?.removeEventListener("abort", T), c.remove());
|
|
105
|
+
};
|
|
106
|
+
try {
|
|
107
|
+
if (n.addEventListener("message", E), o && (typeof o.addEventListener == "function" ? (o.addEventListener("change", g), w = () => o.removeEventListener("change", g)) : (o.addListener(g), w = () => o.removeListener(g))), t.signal?.addEventListener("abort", T, { once: !0 }), t.signal?.aborted) throw p();
|
|
108
|
+
t.target.appendChild(c), await _;
|
|
109
|
+
} catch (d) {
|
|
110
|
+
throw f(), d;
|
|
111
|
+
}
|
|
112
|
+
return { capability: "tip", destroy: f };
|
|
113
|
+
}
|
|
114
|
+
const A = /* @__PURE__ */ new WeakMap();
|
|
115
|
+
function V(i, t) {
|
|
116
|
+
let a = A.get(i);
|
|
117
|
+
a || (a = /* @__PURE__ */ new WeakMap(), A.set(i, a));
|
|
118
|
+
const n = a.get(t.target);
|
|
119
|
+
if (n) return n;
|
|
120
|
+
const s = U(i, t).then((o) => {
|
|
121
|
+
let l = !1;
|
|
122
|
+
const y = {
|
|
123
|
+
capability: "tip",
|
|
124
|
+
destroy() {
|
|
125
|
+
l || (l = !0, t.signal?.removeEventListener("abort", y.destroy), a.get(t.target) === s && a.delete(t.target), o.destroy());
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
return t.signal?.addEventListener("abort", y.destroy, { once: !0 }), t.signal?.aborted && y.destroy(), y;
|
|
129
|
+
}).catch((o) => {
|
|
130
|
+
throw a.get(t.target) === s && a.delete(t.target), o;
|
|
131
|
+
});
|
|
132
|
+
return a.set(t.target, s), s;
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
V as mountTip
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=tip.js.map
|
package/dist/tip.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tip.js","sources":["../src/browser-events.ts","../src/tip/mount.ts","../src/tip/index.ts"],"sourcesContent":["/** Stable, allowlisted `viceme:*` DOM events shared by hosted capabilities. */\n\nimport type { ViceMeErrorCode } from './core/errors.ts';\n\nexport interface VicemeReadyDetail {\n clientKey: string;\n workKey: string;\n capabilities: string[];\n version: string;\n}\n\nexport interface VicemeCapabilityReadyDetail {\n clientKey: string;\n instanceKey: string;\n capability: string;\n version: string;\n}\n\nexport interface VicemeErrorDetail {\n clientKey?: string;\n instanceKey?: string;\n capability?: string;\n code: ViceMeErrorCode;\n retryable: boolean;\n requestId?: string;\n}\n\nexport interface VicemeDestroyedDetail {\n clientKey: string;\n instanceKey: string;\n capability: string;\n}\n\nexport interface VicemeWidgetCloseDetail {\n workId: string;\n}\n\nexport interface VicemeTipPaidDetail {\n workId: string;\n orderNo: string;\n status: 'PAID';\n amountCents: number;\n}\n\nexport type VicemeEventType =\n | 'viceme:ready'\n | 'viceme:capability-ready'\n | 'viceme:error'\n | 'viceme:destroyed'\n | 'viceme:widget-close'\n | 'viceme:tip-paid';\n\nexport type VicemeEventDetailMap = {\n 'viceme:ready': VicemeReadyDetail;\n 'viceme:capability-ready': VicemeCapabilityReadyDetail;\n 'viceme:error': VicemeErrorDetail;\n 'viceme:destroyed': VicemeDestroyedDetail;\n 'viceme:widget-close': VicemeWidgetCloseDetail;\n 'viceme:tip-paid': VicemeTipPaidDetail;\n};\n\nconst ALLOWED_KEYS: ReadonlyMap<VicemeEventType, ReadonlySet<string>> = new Map([\n ['viceme:ready', new Set(['clientKey', 'workKey', 'capabilities', 'version'])],\n ['viceme:capability-ready', new Set(['clientKey', 'instanceKey', 'capability', 'version'])],\n [\n 'viceme:error',\n new Set(['clientKey', 'instanceKey', 'capability', 'code', 'retryable', 'requestId']),\n ],\n ['viceme:destroyed', new Set(['clientKey', 'instanceKey', 'capability'])],\n ['viceme:widget-close', new Set(['workId'])],\n ['viceme:tip-paid', new Set(['workId', 'orderNo', 'status', 'amountCents'])],\n]);\n\n/** Strip anything outside the documented allowlist before dispatch. */\nexport function sanitizeDetail(\n type: VicemeEventType,\n detail: VicemeEventDetailMap[VicemeEventType],\n): VicemeEventDetailMap[VicemeEventType] {\n const allowed = ALLOWED_KEYS.get(type)!;\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(detail)) {\n if (allowed.has(key) && value !== undefined) result[key] = value;\n }\n return result as unknown as VicemeEventDetailMap[VicemeEventType];\n}\n\nexport function dispatchViceMeEvent<K extends VicemeEventType>(\n target: EventTarget,\n type: K,\n detail: VicemeEventDetailMap[K],\n): void {\n const event = new CustomEvent(type, {\n detail: sanitizeDetail(type, detail),\n bubbles: true,\n composed: true,\n cancelable: false,\n });\n target.dispatchEvent(event);\n}\n","import type { ViceMeClient } from '../core/client.ts';\nimport { BUILD_WIDGET_ORIGINS } from '../core/build-endpoints.ts';\nimport { clientDestroyed, ViceMeError } from '../core/errors.ts';\nimport { dispatchViceMeEvent } from '../browser-events.ts';\nimport type { CapabilityMountHandle, CapabilityMountOptions } from '../capability-mount.ts';\n\ntype WidgetAppearance = 'light' | 'dark';\n\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\nconst TIP_ORDER_NO_PATTERN = /^VT\\d{14}[0-9a-f]{12}$/;\nconst TIP_AMOUNT_MIN = 100;\nconst TIP_AMOUNT_MAX = 20_000;\nconst WIDGET_HEIGHT_MAX = 2_048;\nexport const FRAME_READY_TIMEOUT_MS = 8_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nexport async function mount(\n client: ViceMeClient,\n options: CapabilityMountOptions,\n): Promise<CapabilityMountHandle> {\n if (options.signal?.aborted) throw clientDestroyed();\n await client.ready();\n if (options.signal?.aborted) throw clientDestroyed();\n if (!client.hasCapability('tip')) {\n throw new ViceMeError({\n code: 'CAPABILITY_DISABLED',\n message: 'Tip is not available in this SDK build.',\n retryable: false,\n capability: 'tip',\n });\n }\n\n const documentObject = options.target.ownerDocument;\n const windowObject = documentObject.defaultView;\n if (!windowObject) {\n throw new ViceMeError({\n code: 'CONFIG_INVALID',\n message: 'Tip requires a browser document.',\n retryable: false,\n capability: 'tip',\n });\n }\n\n const widgetOrigin = BUILD_WIDGET_ORIGINS[client.region];\n const mediaQuery =\n options.theme === 'auto' ? windowObject.matchMedia('(prefers-color-scheme: dark)') : undefined;\n let appearance: WidgetAppearance =\n options.theme === 'auto' ? (mediaQuery?.matches ? 'dark' : 'light') : options.theme;\n let destroyed = false;\n let readyTimer: number | undefined;\n let boundWorkId: string | undefined;\n\n const portal = documentObject.createElement('div');\n portal.dataset.vicemeTip = 'mounted';\n portal.style.all = 'initial';\n portal.style.display = 'block';\n portal.style.width = '100%';\n portal.style.maxWidth = '100%';\n portal.style.contain = 'layout style';\n\n const shadow = portal.attachShadow({ mode: 'open' });\n const frame = documentObject.createElement('iframe');\n const frameUrl = new URL(`/widget/tip/${encodeURIComponent(client.workKey)}`, widgetOrigin);\n frameUrl.searchParams.set('appearance', appearance);\n frame.title = 'ViceMe Tip';\n frame.src = frameUrl.toString();\n frame.loading = 'eager';\n frame.referrerPolicy = 'strict-origin';\n frame.setAttribute(\n 'sandbox',\n 'allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts',\n );\n frame.setAttribute('allow', 'payment');\n frame.style.boxSizing = 'border-box';\n frame.style.display = 'block';\n frame.style.width = '100%';\n frame.style.height = '0px';\n frame.style.margin = '0';\n frame.style.border = '0';\n frame.style.background = 'transparent';\n frame.style.colorScheme = appearance;\n frame.style.pointerEvents = 'none';\n shadow.append(frame);\n\n let resolveReady: (() => void) | undefined;\n let rejectReady: ((error: ViceMeError) => void) | undefined;\n const readiness = new Promise<void>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n readyTimer = windowObject.setTimeout(() => {\n reject(\n new ViceMeError({\n code: 'INTERNAL_ERROR',\n message: 'Hosted Tip widget did not become ready in time.',\n retryable: true,\n capability: 'tip',\n }),\n );\n }, FRAME_READY_TIMEOUT_MS);\n });\n\n const onMessage = (event: MessageEvent<unknown>): void => {\n if (event.origin !== widgetOrigin || event.source !== frame.contentWindow) return;\n const message = event.data;\n if (!isRecord(message) || typeof message.type !== 'string') return;\n\n if (message.type === 'viceme:widget-resize') {\n if (\n typeof message.workId !== 'string' ||\n !UUID_PATTERN.test(message.workId) ||\n typeof message.height !== 'number' ||\n !Number.isInteger(message.height) ||\n message.height < 1 ||\n message.height > WIDGET_HEIGHT_MAX ||\n (boundWorkId !== undefined && message.workId !== boundWorkId)\n ) {\n return;\n }\n frame.style.height = `${message.height}px`;\n if (boundWorkId === undefined) {\n boundWorkId = message.workId;\n frame.style.pointerEvents = 'auto';\n if (readyTimer !== undefined) {\n windowObject.clearTimeout(readyTimer);\n readyTimer = undefined;\n }\n resolveReady?.();\n }\n return;\n }\n\n if (typeof message.workId !== 'string' || message.workId !== boundWorkId) return;\n if (message.type === 'viceme:widget-close') {\n dispatchViceMeEvent(options.target, 'viceme:widget-close', { workId: message.workId });\n return;\n }\n if (\n message.type === 'viceme:tip-paid' &&\n typeof message.orderNo === 'string' &&\n TIP_ORDER_NO_PATTERN.test(message.orderNo) &&\n message.status === 'PAID' &&\n typeof message.amountCents === 'number' &&\n Number.isInteger(message.amountCents) &&\n message.amountCents >= TIP_AMOUNT_MIN &&\n message.amountCents <= TIP_AMOUNT_MAX\n ) {\n dispatchViceMeEvent(options.target, 'viceme:tip-paid', {\n workId: message.workId,\n orderNo: message.orderNo,\n status: 'PAID',\n amountCents: message.amountCents,\n });\n }\n };\n\n const onAppearanceChange = (event: MediaQueryListEvent): void => {\n appearance = event.matches ? 'dark' : 'light';\n frame.style.colorScheme = appearance;\n frame.contentWindow?.postMessage(\n { type: 'viceme:widget-appearance', appearance },\n widgetOrigin,\n );\n };\n\n let removeAppearanceListener: (() => void) | undefined;\n const onAbort = (): void => {\n rejectReady?.(clientDestroyed());\n cleanup();\n };\n const cleanup = (): void => {\n if (destroyed) return;\n destroyed = true;\n if (readyTimer !== undefined) windowObject.clearTimeout(readyTimer);\n windowObject.removeEventListener('message', onMessage);\n removeAppearanceListener?.();\n options.signal?.removeEventListener('abort', onAbort);\n portal.remove();\n };\n\n try {\n windowObject.addEventListener('message', onMessage);\n if (mediaQuery) {\n if (typeof mediaQuery.addEventListener === 'function') {\n mediaQuery.addEventListener('change', onAppearanceChange);\n removeAppearanceListener = () =>\n mediaQuery.removeEventListener('change', onAppearanceChange);\n } else {\n mediaQuery.addListener(onAppearanceChange);\n removeAppearanceListener = () => mediaQuery.removeListener(onAppearanceChange);\n }\n }\n options.signal?.addEventListener('abort', onAbort, { once: true });\n if (options.signal?.aborted) throw clientDestroyed();\n options.target.appendChild(portal);\n await readiness;\n } catch (error) {\n cleanup();\n throw error;\n }\n\n return { capability: 'tip', destroy: cleanup };\n}\n","import type { ViceMeClient } from '../core/client.ts';\nimport { mount } from './mount.ts';\n\nexport interface TipMountOptions {\n target: Element;\n theme: 'light' | 'dark' | 'auto';\n signal?: AbortSignal;\n}\n\nexport interface TipMountHandle {\n readonly capability: 'tip';\n destroy(): void;\n}\n\nexport interface TipWidgetCloseDetail {\n workId: string;\n}\n\nexport interface TipPaidDetail {\n workId: string;\n orderNo: string;\n status: 'PAID';\n amountCents: number;\n}\n\nconst mounts = new WeakMap<ViceMeClient, WeakMap<Element, Promise<TipMountHandle>>>();\n\n/** Explicit ESM/npm entry point; the CDN loader calls the same mount function. */\nexport function mountTip(client: ViceMeClient, options: TipMountOptions): Promise<TipMountHandle> {\n let byTarget = mounts.get(client);\n if (!byTarget) {\n byTarget = new WeakMap();\n mounts.set(client, byTarget);\n }\n const existing = byTarget.get(options.target);\n if (existing) return existing;\n\n const pending: Promise<TipMountHandle> = mount(client, options)\n .then((raw) => {\n let destroyed = false;\n const handle: TipMountHandle = {\n capability: 'tip' as const,\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n options.signal?.removeEventListener('abort', handle.destroy);\n if (byTarget.get(options.target) === pending) byTarget.delete(options.target);\n raw.destroy();\n },\n };\n options.signal?.addEventListener('abort', handle.destroy, { once: true });\n if (options.signal?.aborted) handle.destroy();\n return handle;\n })\n .catch((error: unknown) => {\n if (byTarget.get(options.target) === pending) byTarget.delete(options.target);\n throw error;\n });\n byTarget.set(options.target, pending);\n return pending;\n}\n"],"names":["ALLOWED_KEYS","sanitizeDetail","type","detail","allowed","result","key","value","dispatchViceMeEvent","target","event","UUID_PATTERN","TIP_ORDER_NO_PATTERN","TIP_AMOUNT_MIN","TIP_AMOUNT_MAX","WIDGET_HEIGHT_MAX","FRAME_READY_TIMEOUT_MS","isRecord","mount","client","options","clientDestroyed","ViceMeError","documentObject","windowObject","widgetOrigin","BUILD_WIDGET_ORIGINS","mediaQuery","appearance","destroyed","readyTimer","boundWorkId","portal","shadow","frame","frameUrl","resolveReady","rejectReady","readiness","resolve","reject","onMessage","message","onAppearanceChange","removeAppearanceListener","onAbort","cleanup","error","mounts","mountTip","byTarget","existing","pending","raw","handle"],"mappings":"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"}
|
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export interface TransportRequest {
|
|
10
10
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
11
|
-
/** Absolute path under the public API base
|
|
11
|
+
/** Absolute path under the public API base. */
|
|
12
12
|
path: string;
|
|
13
13
|
/** JSON-serializable request body. */
|
|
14
14
|
body?: unknown;
|
|
15
15
|
/** Work-session token. It is never persisted by the transport. */
|
|
16
16
|
authorization?: string;
|
|
17
|
+
/** Optional memory-only signed-in widget token. */
|
|
18
|
+
userAuthorization?: string;
|
|
17
19
|
signal?: AbortSignal;
|
|
18
20
|
/** Per-request timeout; defaults to the transport default. */
|
|
19
21
|
timeoutMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC1C
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC1C,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;CAClC;AAqED,qBAAa,cAAe,YAAW,SAAS;;gBAMlC,OAAO,EAAE,qBAAqB;IAOpC,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA0GrE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,SAAS,CAE9E"}
|
package/dist/version.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Kept in sync with `packages/sdk/package.json#version`; a unit test enforces
|
|
5
5
|
* the match so the loader, release manifest, and npm metadata can never drift.
|
|
6
6
|
*/
|
|
7
|
-
export declare const SDK_VERSION = "0.
|
|
7
|
+
export declare const SDK_VERSION = "0.4.0";
|
|
8
8
|
/** Public API major carried by this build (loader namespace `v1`). */
|
|
9
9
|
export declare const API_MAJOR = 1;
|
|
10
10
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/viceme.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(){"use strict";const
|
|
1
|
+
(function(){"use strict";const re=new Set(["RATE_LIMITED","NETWORK_TIMEOUT","CHECKOUT_UNAVAILABLE","INTERNAL_ERROR"]);class w extends Error{code;retryable;requestId;capability;constructor(e){super(e.message),this.name="ViceMeError",this.code=e.code,this.retryable=e.retryable??re.has(e.code),this.requestId=e.requestId,this.capability=e.capability}toJSON(){const e={code:this.code,retryable:this.retryable};return this.requestId!==void 0&&(e.requestId=this.requestId),this.capability!==void 0&&(e.capability=this.capability),e}}function ie(t){return t instanceof w}function p(t){return new w({code:"CONFIG_INVALID",message:t,retryable:!1})}function _(){return new w({code:"CLIENT_DESTROYED",message:"ViceMe client has been destroyed.",retryable:!1})}const B={cn:"https://api.viceme.cn",global:"https://api.viceme.ai"},ne={cn:"https://viceme.cn",global:"https://viceme.ai"},ae=new Set(["cn","global"]),oe={cn:B.cn,global:B.global};function se(t){return oe[t]}function V(t){return typeof t=="string"&&/^wrk_[A-Za-z0-9_-]{4,124}$/.test(t)}function W(t){return typeof t=="string"&&ae.has(t)}function ce(t){if(typeof t!="object"||t===null)throw p("createViceMe expects a configuration object.");const e=t,r=new Set(["workKey","region","signal"]);for(const i of Object.keys(e))if(!r.has(i))throw p(`Unknown configuration field "${i}".`);if(!V(e.workKey))throw p('Configuration field "workKey" must be a public work key ("wrk_…").');if(!W(e.region))throw p('Configuration field "region" must be "cn" or "global".');if(e.signal!==void 0&&!(e.signal instanceof AbortSignal))throw p('Configuration field "signal" must be an AbortSignal.');return{workKey:e.workKey,region:e.region,signal:e.signal}}const le=new Set(["data-viceme-work","data-viceme-region","data-viceme-features","data-viceme-target","data-viceme-theme","data-viceme-loader"]),de=new Set(["light","dark","auto"]),G=["danmaku","tip"];function ue(t){for(const g of t.getAttributeNames())if(g.startsWith("data-viceme-")&&!le.has(g))throw p(`Unknown loader attribute "${g}".`);const e=t.getAttribute("data-viceme-work");if(!V(e))throw p('Loader attribute "data-viceme-work" must be a public work key ("wrk_…").');const r=t.getAttribute("data-viceme-region");if(!W(r))throw p('Loader attribute "data-viceme-region" must be "cn" or "global".');const i=t.getAttribute("data-viceme-features"),a=i?.split(",")??[],u=new Set(a);if(!i||/\s/.test(i)||a.length!==u.size||a.some(g=>!G.includes(g)))throw p('Loader attribute "data-viceme-features" must contain each of "danmaku" and "tip" at most once.');const n=G.filter(g=>u.has(g)),o=t.getAttribute("data-viceme-target");if(o==="")throw p('Loader attribute "data-viceme-target" must be a CSS selector.');if(o===null)throw p('Loader attribute "data-viceme-target" is required when features are declared.');try{t.matches(o)}catch{throw p('Loader attribute "data-viceme-target" is not a valid CSS selector.')}const l=t.getAttribute("data-viceme-theme");if(l!==null&&!de.has(l))throw p('Loader attribute "data-viceme-theme" must be "light", "dark", or "auto".');return{workKey:e,region:r,features:n,target:o,theme:l??"auto"}}function K(t,e,r){return`v${t}+${e}+${r}`}class fe{#e=new Map;#t=new Map;#r=new Map;#i=0;getClient(e){return this.#e.get(e)}registerClient(e){this.#e.set(e.clientKey,e)}unregisterClient(e){this.#e.delete(e)}getInstance(e){return this.#t.get(e)}findInstance(e,r,i){return this.#r.get(`${e}::${r}`)?.get(i)}registerInstance(e,r,i,a){const u=`${e}::${r}`;let n=this.#r.get(u);n===void 0&&(n=new WeakMap,this.#r.set(u,n));const o=n.get(i);if(o)return o;this.#i+=1;const l={instanceKey:`${e}::${r}::el${this.#i}`,clientKey:e,capability:r,host:i,raw:a,destroyed:!1};return this.#t.set(l.instanceKey,l),n.set(i,l),l}removeInstance(e){const r=this.#t.get(e);if(r)return this.#t.delete(e),this.#r.get(`${r.clientKey}::${r.capability}`)?.delete(r.host),r}instancesForClient(e){return[...this.#t.values()].filter(r=>r.clientKey===e)}}const me=new Map([["viceme:ready",new Set(["clientKey","workKey","capabilities","version"])],["viceme:capability-ready",new Set(["clientKey","instanceKey","capability","version"])],["viceme:error",new Set(["clientKey","instanceKey","capability","code","retryable","requestId"])],["viceme:destroyed",new Set(["clientKey","instanceKey","capability"])],["viceme:widget-close",new Set(["workId"])],["viceme:tip-paid",new Set(["workId","orderNo","status","amountCents"])]]);function he(t,e){const r=me.get(t),i={};for(const[a,u]of Object.entries(e))r.has(a)&&u!==void 0&&(i[a]=u);return i}function x(t,e,r){const i=new CustomEvent(e,{detail:he(e,r),bubbles:!0,composed:!0,cancelable:!1});t.dispatchEvent(i)}const ye={CREATED:["READY","DESTROYED"],READY:["DEGRADED","DESTROYED"],DEGRADED:["DESTROYED"],DESTROYED:[]};class pe{#e="CREATED";#t=new Set;get state(){return this.#e}get destroyed(){return this.#e==="DESTROYED"}transition(e){if(e!==this.#e){if(!ye[this.#e].includes(e))throw new w({code:"INTERNAL_ERROR",message:`Illegal lifecycle transition ${this.#e} -> ${e}.`,retryable:!1});this.#e=e;for(const r of this.#t)r(e)}}assertAlive(){if(this.destroyed)throw _()}subscribe(e){return this.#t.add(e),()=>{this.#t.delete(e)}}clearListeners(){this.#t.clear()}}function we(t,e){if(typeof t!="object"||t===null)throw D();const r=t;if(typeof r.workKey!="string"||!Array.isArray(r.capabilities)||!r.capabilities.every(a=>typeof a=="string"))throw D();if(r.workKey!==e)throw new w({code:"WORK_NOT_FOUND",message:"Work-session response did not match the requested work key.",retryable:!1});const i={work:{key:r.workKey,capabilities:r.capabilities}};if(typeof r.token=="string"&&(i.token=r.token),typeof r.expiresAt=="string"){const a=new Date(r.expiresAt).getTime();Number.isNaN(a)||(i.expiresAt=a)}return i}function D(){return new w({code:"INTERNAL_ERROR",message:"Malformed work-session response.",retryable:!0})}class ge{#e;#t;#r;#i;constructor(e){this.#e=e,this.#t=e.now??(()=>Date.now())}get snapshot(){return this.#r}#n(){const e=this.#r?.expiresAt;return e!==void 0&&e<=this.#t()}establish(){if(this.#r){if(!this.#n())return Promise.resolve(this.#r);this.#r=void 0}return this.#i??=this.#e.transport.request({method:"POST",path:"/v1/public/work-sdk/sessions",body:{workKey:this.#e.workKey},signal:this.#e.signal,timeoutMs:this.#e.timeoutMs}).then(e=>{const r=we(e.body,this.#e.workKey);return this.#r=r,r}).finally(()=>{this.#i=void 0}),this.#i}async request(e){const r=await this.establish();if(!r.token)throw D();try{return await this.#e.transport.request({...e,authorization:r.token,userAuthorization:r.userToken,signal:this.#e.signal})}catch(i){if(!(i instanceof w)||i.code!=="SESSION_EXPIRED")throw i;this.#r===r&&this.invalidate();const a=await this.establish();if(!a.token)throw D();return this.#e.transport.request({...e,authorization:a.token,userAuthorization:a.userToken,signal:this.#e.signal})}}authenticate(e){if(!this.#r)throw D();this.#r={...this.#r,userToken:e.userToken,user:e.user}}async signOut(){this.invalidate(),await this.establish()}invalidate(){this.#r=void 0}destroy(){this.invalidate(),this.#i=void 0}}const H="0.4.0",U=1,P="viceme-access-layer",q=50;function be(t){switch(t){case"SIGN_IN":return{description:"",label:"授权"};case"FOLLOW":return{description:"",label:"关注"};case"CHECKOUT":return{description:"",label:"打开支付"}}}function ve(t){if(!ie(t))return"操作未完成,请重试。";switch(t.code){case"CONFIG_INVALID":return"微信授权配置无效,请稍后重试。";case"AUTH_CANCELLED":return"微信授权已取消,请重试。";case"SESSION_EXPIRED":return"授权会话已过期,请重试。";case"NETWORK_TIMEOUT":return"网络连接超时,请重试。";default:return t.requestId?`操作未完成,请重试。请求 ID:${t.requestId}`:"操作未完成,请重试。"}}function Ee(){if(customElements.get(P))return;class t extends HTMLElement{interaction;connectedCallback(){const r=be(this.interaction.action),i=this.attachShadow({mode:"open"});i.innerHTML=`
|
|
2
2
|
<style>
|
|
3
3
|
:host {
|
|
4
4
|
position: fixed;
|
|
@@ -104,25 +104,6 @@
|
|
|
104
104
|
font-size: 0.875rem;
|
|
105
105
|
}
|
|
106
106
|
[data-viceme='profile-header'] { min-width: 0; }
|
|
107
|
-
[data-viceme='profile-covers'] {
|
|
108
|
-
display: none;
|
|
109
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
110
|
-
gap: 0.75rem;
|
|
111
|
-
margin-top: 1.25rem;
|
|
112
|
-
border-radius: 0.875rem;
|
|
113
|
-
padding: 0.75rem;
|
|
114
|
-
background: #f4f4f5;
|
|
115
|
-
}
|
|
116
|
-
[data-viceme='profile-covers'][data-visible='true'] { display: grid; }
|
|
117
|
-
[data-viceme='profile-cover'] {
|
|
118
|
-
display: block;
|
|
119
|
-
width: 100%;
|
|
120
|
-
aspect-ratio: 16 / 9;
|
|
121
|
-
border: 1px solid #e4e4e7;
|
|
122
|
-
border-radius: 0.625rem;
|
|
123
|
-
object-fit: cover;
|
|
124
|
-
background: #ffffff;
|
|
125
|
-
}
|
|
126
107
|
[data-viceme='panel'][data-action='SIGN_IN'] [data-viceme='description'] { display: none; }
|
|
127
108
|
[data-viceme='panel'][data-action='SIGN_IN'] [data-viceme='profile'][data-visible='true'] {
|
|
128
109
|
display: block;
|
|
@@ -220,7 +201,6 @@
|
|
|
220
201
|
</div>
|
|
221
202
|
</div>
|
|
222
203
|
<p data-viceme="profile-description"></p>
|
|
223
|
-
<div data-viceme="profile-covers" aria-label="作品封面"></div>
|
|
224
204
|
</section>
|
|
225
205
|
<p data-viceme="error" role="alert" aria-live="polite"></p>
|
|
226
206
|
<div data-viceme="actions">
|
|
@@ -229,5 +209,5 @@
|
|
|
229
209
|
</div>
|
|
230
210
|
<iframe data-viceme="frame" title="" referrerpolicy="no-referrer" allow="payment"></iframe>
|
|
231
211
|
</section>
|
|
232
|
-
`;const n=i.querySelector("[data-viceme='panel']");n.dataset.action=this.interaction.action,n.dataset.frame=String(this.interaction.action==="CHECKOUT");const l=i.querySelector("[data-viceme='description']");l.textContent=r.description;const a=i.querySelector("[data-viceme='action']"),c=a.parentElement,d=i.querySelector("[data-viceme='close']"),I=i.querySelector("[data-viceme='backdrop']"),k=i.querySelector("[data-viceme='error']"),o=i.querySelector("[data-viceme='frame']"),N=i.querySelector("[data-viceme='profile']"),s=i.querySelector("[data-viceme='avatar']"),u=i.querySelector("[data-viceme='avatar-fallback']"),f=i.querySelector("[data-viceme='profile-name']"),p=i.querySelector("[data-viceme='profile-description']"),h=i.querySelector("[data-viceme='profile-stats']"),A=i.querySelector("[data-viceme='profile-covers']");a.textContent=r.label,a.hidden=this.interaction.action==="CHECKOUT",c.dataset.single="true";const S=r.label;o.title=this.interaction.action==="SIGN_IN"?"微信授权":"支付";const b=this.interaction.followTarget;if(b){n.setAttribute("aria-label",this.interaction.action==="SIGN_IN"?`授权 ${b.displayName}`:`关注 ${b.displayName}`),N.dataset.visible="true",f.textContent=b.displayName;const m=[...b.description??""];if(p.textContent=m.length>j?`${m.slice(0,j).join("")}…`:m.join(""),m.length>j&&(p.title=b.description),h.textContent=`${b.workCount} 个作品`,b.coverUrls.length===0)A.remove();else{for(const[M,L]of b.coverUrls.entries()){const D=document.createElement("img");D.dataset.viceme="profile-cover",D.src=L,D.alt=`${b.displayName}的作品封面 ${M+1}`,A.append(D)}A.dataset.visible="true"}u.textContent=b.displayName.trim().slice(0,1)||"V",b.avatarUrl&&(s.src=b.avatarUrl,s.alt=`${b.displayName}的头像`,s.hidden=!1,u.hidden=!0)}let R=null;const q=m=>{this.dispatchEvent(new CustomEvent("viceme:access-layer-close",{detail:m}))},C=()=>{R?.cancel(),q("dismissed")};I.addEventListener("click",C),d.addEventListener("click",C),this.addEventListener("keydown",m=>{if(m.key==="Escape"&&C(),m.key==="Tab"){if(m.preventDefault(),R){o.focus();return}const L=[d,...a.hidden?[]:[a]].filter(ie=>!ie.disabled&&!ie.hidden);if(L.length===0)return;const D=L.indexOf(i.activeElement),Qe=m.shiftKey?-1:1;L[(D+Qe+L.length)%L.length]?.focus()}});const O=async()=>{a.disabled=!0,a.setAttribute("aria-busy","true"),a.textContent="正在打开…",k.textContent="";try{const m=await this.interaction.perform();if(m.type==="frame"){if(R=m,this.interaction.action==="SIGN_IN"){const M=n.getBoundingClientRect().height;M>0&&(n.style.height=`${M}px`)}n.dataset.frame="true",o.src=m.url,o.focus(),await m.completion}q("acted")}catch(m){R?.cancel(),R=null,n.dataset.frame="false",o.removeAttribute("src"),k.textContent=Ee(m),a.hidden=!1,a.disabled=!1,a.removeAttribute("aria-busy"),a.textContent=S,a.focus()}};a.addEventListener("click",O),this.interaction.action==="CHECKOUT"?queueMicrotask(()=>{O()}):queueMicrotask(()=>a.focus())}}customElements.define(F,t)}const $=async t=>{if(typeof document>"u"||typeof customElements>"u")return"dismissed";Ie();const e=document.createElement(F);e.interaction=t;const r=document.documentElement.style.overflow,i=document.activeElement instanceof HTMLElement?document.activeElement:null;document.documentElement.style.overflow="hidden",document.body.append(e);try{return await new Promise(n=>{e.addEventListener("viceme:access-layer-close",l=>n(l.detail),{once:!0})})}finally{e.remove(),document.documentElement.style.overflow=r,i?.focus()}};function E(t){if(typeof t!="object"||t===null)throw w();return t}function w(){return new v({code:"INTERNAL_ERROR",message:"Malformed public API response.",retryable:!0})}function Ae(t){const e=E(t);if(typeof e.subject!="string"||!(typeof e.nickname=="string"||e.nickname===null)||!(typeof e.avatarUrl=="string"||e.avatarUrl===null))throw w();return{subject:e.subject,nickname:e.nickname,avatarUrl:e.avatarUrl}}function z(t){const e=E(t);if(typeof e.following!="boolean"||!(typeof e.followedAt=="string"||e.followedAt===null))throw w();const r=E(e.target);if(!(r.kind==="CREATOR"||r.kind==="USER")||typeof r.displayName!="string"||!(typeof r.avatarUrl=="string"||r.avatarUrl===null)||!(typeof r.description=="string"||r.description===null)||!Number.isInteger(r.workCount)||r.workCount<0||!Array.isArray(r.coverUrls)||r.coverUrls.length>2||!r.coverUrls.every(i=>typeof i=="string"))throw w();return{following:e.following,followedAt:e.followedAt,target:{kind:r.kind,displayName:r.displayName,avatarUrl:r.avatarUrl,description:r.description,workCount:r.workCount,coverUrls:r.coverUrls}}}function ke(t){const e=E(t),r=new Set(["OWNER","FOLLOWING","PURCHASED","ENTITLED","AUTH_REQUIRED","FOLLOW_REQUIRED","PURCHASE_REQUIRED","FEATURE_NOT_FOUND","FEATURE_DISABLED","POLICY_UNSUPPORTED"]),i=new Set([null,"SIGN_IN","FOLLOW","CHECKOUT"]);if(typeof e.allowed!="boolean"||typeof e.reason!="string"||!r.has(e.reason)||!i.has(e.nextAction))throw w();return{allowed:e.allowed,reason:e.reason,nextAction:e.nextAction}}function Se(t){const e=E(t),r=E(e.policy);if(typeof e.featureKey!="string"||!/^[a-z][a-z0-9_-]{1,63}$/.test(e.featureKey)||typeof e.title!="string"||e.title.trim().length===0||e.title.length>120||!(r.type==="PUBLIC"||r.type==="FOLLOW_OWNER"||r.type==="WORK_ENTITLEMENT"))throw w();const i=e.price===null?null:E(e.price);if(i!==null&&(!Number.isInteger(i.amountCents)||i.amountCents<=0||i.currency!=="CNY")||r.type==="WORK_ENTITLEMENT"!=(i!==null))throw w();return{featureKey:e.featureKey,title:e.title,policy:{type:r.type},price:i===null?null:{amountCents:i.amountCents,currency:"CNY"}}}function Q(t){if(typeof t!="string")throw w();try{const e=new URL(t).origin;if(e!==t)throw w();return e}catch(e){throw e instanceof v?e:w()}}function Z(){const t=crypto.getRandomValues(new Uint8Array(32));return Te(t)}function Te(t){let e="";for(const r of t)e+=String.fromCharCode(r);return btoa(e).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"")}function Ne(){return typeof document<"u"&&document.documentElement.lang.toLowerCase()==="en-us"?"en-US":"zh-CN"}function Re(t){const e=async(s,u)=>{const f=E((await t.session.request({method:"POST",path:"/v1/public/v1/auth/exchange",body:{code:s,codeVerifier:u}})).body);if(typeof f.token!="string"||typeof f.expiresAt!="number")throw w();const p=Ae(f.user);return t.session.authenticate({token:f.token,expiresAt:f.expiresAt,user:p}),{authenticated:!0,user:p}},r=()=>new v({code:"AUTH_CANCELLED",message:"The interactive action was cancelled.",retryable:!1}),i=(s,u,f,p,h)=>{if(typeof window>"u")throw new v({code:"CONFIG_INVALID",message:"Interactive access requires a browser window.",retryable:!1});let A=!0,S,b;const R=()=>{A&&(A=!1,window.removeEventListener("message",C))},q=new Promise((O,m)=>{S=O,b=m}),C=O=>{if(!A||O.origin!==u||typeof O.data!="object"||O.data===null)return;const m=O.data;if(!(m.workKey!==t.workKey||m.channel!==f)){if(m.type===`viceme:${p}:cancelled`){R(),b(r());return}m.type===`viceme:${p}:complete`&&(R(),h(m).then(S,b))}};return window.addEventListener("message",C),{type:"frame",url:s,completion:q,cancel(){R(),S()}}},n=async s=>{await t.ready();const u=Z(),f=await t.session.request({method:"POST",path:"/v1/public/v1/auth/wechat/authorize",body:{channel:u,clientType:"pc",locale:Ne()}}),p=E(f.body);if(typeof p.authorizationUrl!="string")throw w();return i(p.authorizationUrl,Q(p.completionOrigin),u,"auth",async h=>{if(typeof h.code!="string"||typeof h.codeVerifier!="string")throw w();await e(h.code,h.codeVerifier),await s?.()})},l=async()=>(await t.ready(),z((await t.session.request({method:"GET",path:"/v1/public/v1/follow"})).body)),a=async s=>(await t.ready(),z((await t.session.request({method:s,path:"/v1/public/v1/follow"})).body)),c={async getState(){await t.ready();const s=t.session.snapshot?.user??null;return{authenticated:s!==null,user:s}},async signIn(){const s=t.presenter??$,u=(await l()).target;if(await s({featureKey:"auth",reason:"AUTH_REQUIRED",action:"SIGN_IN",followTarget:u,perform:()=>n(async()=>{await a("PUT")})})==="dismissed")throw r();return this.getState()},async signOut(){await t.ready(),await t.session.signOut()}},d={getState:l,follow:()=>a("PUT"),unfollow:()=>a("DELETE")},I=async s=>{await t.ready();const u=E((await t.session.request({method:"POST",path:"/v1/public/v1/access/check",body:{featureKeys:s}})).body),f=E(u.decisions);return Object.fromEntries(Object.entries(f).map(([p,h])=>[p,ke(h)]))},k=async(s,u={})=>{await t.ready();const f=Z(),p={featureKey:s,locale:u.locale??"zh-CN",channel:f},h=E((await t.session.request({method:"POST",path:"/v1/public/v1/checkout/sessions",body:p})).body);if(typeof h.checkoutUrl!="string"||typeof h.alreadyOwned!="boolean")throw w();const A=Q(h.completionOrigin),S={checkoutUrl:h.checkoutUrl,alreadyOwned:h.alreadyOwned};return{result:S,action:S.alreadyOwned?{type:"completed"}:i(S.checkoutUrl,A,f,"checkout",async()=>{})}};return{auth:c,follow:d,access:{async getFeatures(){await t.ready();const s=E((await t.session.request({method:"GET",path:"/v1/public/v1/access/features"})).body);if(!Array.isArray(s.features)||s.features.length>100)throw w();return s.features.map(Se)},async check(s){const f=(await I([s]))[s];if(!f)throw w();return f},checkMany:I,async require(s){let u=await this.check(s);const f=t.presenter??$;for(let p=0;!u.allowed&&u.nextAction&&p<3;p+=1){const h=u.nextAction;if(h==="FOLLOW"){await d.follow(),u=await this.check(s);continue}const A=h==="SIGN_IN"?(await d.getState()).target:void 0;if(await f({featureKey:s,reason:u.reason,action:h,...A?{followTarget:A}:{},perform:async()=>h==="SIGN_IN"?n(async()=>{await d.follow()}):(await k(s)).action})==="dismissed")return u;u=await this.check(s)}return u},async refresh(){await t.ready()}},checkout:{async open(s){const u=t.presenter??$;let f;if(await u({featureKey:s.featureKey,reason:"PURCHASE_REQUIRED",action:"CHECKOUT",perform:async()=>{const h=await k(s.featureKey,s);return f=h.result,h.action}})==="dismissed"||!f)throw r();return f}}}}function X(t){t instanceof J&&t.markDegraded()}class Oe extends DOMException{constructor(){super("ViceMe client destroyed.","AbortError")}}class J{#e=new ge;#t;#r;#i=new AbortController;#a;auth;access;checkout;#n;constructor(e){this.#r=e.config,this.#t=new we({workKey:e.config.workKey,transport:e.transport,signal:this.#i.signal,...e.now!==void 0?{now:e.now}:{}});const r=Re({session:this.#t,workKey:e.config.workKey,presenter:e.presenter,ready:()=>this.ready()});this.auth=r.auth,this.access=r.access,this.checkout=r.checkout;const i=e.config.signal;if(i){const n=()=>{this.#i.abort(i.reason instanceof Error?i.reason:new DOMException("Caller signal aborted.","AbortError"))};i.aborted?n():(i.addEventListener("abort",n,{once:!0}),this.#n=()=>{i.removeEventListener("abort",n)})}}get version(){return Y}get workKey(){return this.#r.workKey}get region(){return this.#r.region}get state(){return this.#e.state}get apiMajor(){return x}ready(){return this.#e.destroyed?Promise.reject(_()):(this.#a??=this.#o(),this.#a)}async#o(){if(this.#e.destroyed)throw _();this.#e.transition("INITIALIZING");try{if(await this.#t.establish(),this.#e.destroyed)throw _();this.#e.transition("READY")}catch(e){throw this.#e.destroyed?_():(this.#e.transition("FAILED"),this.#a=void 0,this.#t.invalidate(),e)}}hasCapability(e){if(this.#e.destroyed)return!1;const r=this.#t.snapshot?.work.capabilities;return r!==void 0&&r.includes(e)}get capabilities(){return this.#t.snapshot?.work.capabilities??[]}markDegraded(){this.#e.state==="READY"&&this.#e.transition("DEGRADED")}get sessionSnapshot(){return this.#t.snapshot}destroy(){this.#e.destroyed||(this.#e.transition("DESTROYED"),this.#i.abort(new Oe),this.#t.destroy(),this.#e.clearListeners(),this.#a=void 0,this.#n?.(),this.#n=void 0)}}const Le=1e4,Ce=new Map([[400,"CONFIG_INVALID"],[401,"SESSION_EXPIRED"],[403,"CAPABILITY_DISABLED"],[404,"WORK_NOT_FOUND"],[409,"CONFIG_INVALID"],[422,"CONFIG_INVALID"],[429,"RATE_LIMITED"]]),De=new Set(["CONFIG_INVALID","WORK_NOT_FOUND","CAPABILITY_DISABLED","SESSION_EXPIRED","AUTH_REQUIRED","AUTH_CANCELLED","RETURN_URL_NOT_ALLOWED","RATE_LIMITED","NETWORK_TIMEOUT","CHECKOUT_UNAVAILABLE","INTERNAL_ERROR"]);function _e(t){if(typeof t!="object"||t===null)return{};const e=t,r=typeof e.error=="object"&&e.error!==null?e.error:e,i={};return typeof r.code=="string"&&De.has(r.code)&&(i.code=r.code),typeof r.message=="string"&&(i.message=r.message.slice(0,200)),typeof r.retryable=="boolean"&&(i.retryable=r.retryable),typeof r.requestId=="string"&&(i.requestId=r.requestId),i}function Ue(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`req-${Math.random().toString(36).slice(2)}`}class xe{#e;#t;#r;#i;constructor(e){this.#e=e.apiBaseUrl.replace(/\/+$/,""),this.#t=e.fetchImpl??globalThis.fetch.bind(globalThis),this.#r=e.defaultTimeoutMs??Le,this.#i=e.generateRequestId??Ue}async request(e){if(e.signal?.aborted)throw this.#a(e.signal);const r=e.timeoutMs??this.#r,i=new AbortController;let n=!1;const l=setTimeout(()=>{n=!0,i.abort(new ee)},r),a=()=>i.abort(new ee(!1));e.signal?.addEventListener("abort",a,{once:!0});const c=this.#i();try{const d=await this.#t(`${this.#e}${e.path}`,{method:e.method,credentials:"omit",mode:"cors",headers:{"content-type":"application/json","x-client-request-id":c,...e.authorization!==void 0?{authorization:`Bearer ${e.authorization}`}:{}},body:e.body!==void 0?JSON.stringify(e.body):void 0,signal:i.signal});let I;try{I=await d.json()}catch(o){if(e.signal?.aborted||n)throw o;I=void 0}const k=d.headers.get("x-request-id")??void 0;if(!d.ok){const o=_e(I),N=o.code??Ce.get(d.status)??"INTERNAL_ERROR";throw new v({code:N,message:o.message??"Public API request failed.",retryable:o.retryable,requestId:o.requestId??k})}return{status:d.status,body:I,requestId:k}}catch(d){throw e.signal?.aborted?this.#a(e.signal):n?new v({code:"NETWORK_TIMEOUT",message:"Public API request timed out.",retryable:!0,requestId:c}):d instanceof v?d:d instanceof DOMException&&d.name==="AbortError"?new DOMException("ViceMe request aborted by caller.","AbortError"):new v({code:"INTERNAL_ERROR",message:"Public API request failed.",retryable:!0,requestId:c})}finally{clearTimeout(l),e.signal?.removeEventListener("abort",a)}}#a(e){return e.reason instanceof Error?e.reason:new DOMException("ViceMe request aborted by caller.","AbortError")}}function Me(t){return new xe(t)}class ee extends DOMException{constructor(e=!0){super(e?"ViceMe request timed out.":"ViceMe request aborted.","AbortError")}}function Ke(t){const e=le(t),r=ce(e.region),i=Me({apiBaseUrl:r});return new J({config:e,transport:i})}const qe=8e3,Pe="danmaku.js",te=Symbol.for("viceme.loader.state.v1");function g(){const e=globalThis[te];if(e)return e;const r={registry:new me,queue:{current:Promise.resolve()}};return Object.defineProperty(globalThis,te,{value:r,writable:!0,configurable:!0,enumerable:!1}),r}function G(t){if(!t.destroyed){t.destroyed=!0,g().registry.removeInstance(t.instanceKey);try{t.raw.destroy()}finally{K(t.host,"viceme:destroyed",{clientKey:t.clientKey,instanceKey:t.instanceKey,capability:t.capability})}}}function Fe(t){return{instanceKey:t.instanceKey,capability:t.capability,destroy:()=>G(t)}}function V(t,e,r){Object.defineProperty(t,e,{value:r,writable:!0,configurable:!0,enumerable:!1})}function je(t){let r=globalThis.ViceMe;r||(r={},V(globalThis,"ViceMe",r));let i=r.versions;i||(i={},V(r,"versions",i));const n=i[`v${x}`];if(n)return n;const l={version:t,whenReady(a){const c=g().registry.getClient(a);return c?c.ready.then(()=>c.client):Promise.reject(y(`Unknown clientKey "${a}".`))},getInstance(a){const c=g().registry.getInstance(a);return c?Fe(c):void 0},destroyInstance(a){const c=g().registry.getInstance(a);c&&G(c)},destroyClient(a){for(const d of g().registry.instancesForClient(a))G(d);const c=g().registry.getClient(a);g().registry.unregisterClient(a),c?.client.destroy()}};return V(i,`v${x}`,l),l}function $e(){return document.readyState!=="loading"?Promise.resolve():new Promise(t=>{document.addEventListener("DOMContentLoaded",()=>t(),{once:!0})})}async function Ge(t){let e;try{e=await Ve(t)}catch{e={ok:!1,status:0}}return{manifest:await Be(e),baseUrl:t}}function Ve(t){const e=typeof AbortSignal<"u"&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(qe):void 0;return fetch(t,{credentials:"omit",signal:e})}async function Be(t){if(!t.ok)throw y(`Release manifest is not available (HTTP ${t.status}).`);const e=await t.json();if(typeof e!="object"||e===null||typeof e.version!="string"||typeof e.apiMajor!="number"||typeof e.features!="object"||e.features===null)throw y("Release manifest is malformed.");const r=e;if(r.version!==Y)throw y("Release manifest version does not match this loader.");if(r.apiMajor!==x)throw y("Release manifest major version does not match this loader.");const i=Object.keys(r.features);if(i.length!==1||i[0]!=="danmaku"||r.features.danmaku!==Pe)throw y("Release manifest features must be exactly danmaku.js.");return r}const He=new Set(["CONFIG_INVALID","CAPABILITY_DISABLED","CLIENT_DESTROYED","INTERNAL_ERROR"]);function We(t){if(typeof t=="object"&&t!==null){const e=t;if(typeof e.code=="string"&&He.has(e.code)&&typeof e.retryable=="boolean"){const r={code:e.code,retryable:e.retryable};return typeof e.requestId=="string"&&(r.requestId=e.requestId),typeof e.capability=="string"&&(r.capability=e.capability),r}}return{code:"INTERNAL_ERROR",retryable:!0}}function T(t,e,r={}){K(t,"viceme:error",{...We(e),...r})}async function Ye(t){let e;try{e=fe(t)}catch(o){T(document,o);return}if(!t.src){T(document,y("Loader script requires a src URL."));return}const r=new URL("manifest.json",t.src);let i,n;try{({manifest:i,baseUrl:n}=await Ge(r))}catch(o){T(document,o,{clientKey:P(x,e.region,e.workKey)});return}await $e();let l;try{const o=document.querySelectorAll(e.target);if(o.length===0)throw y(`Loader target "${e.target}" matched no element; no node was created.`);if(o.length>1)throw y(`Loader target "${e.target}" matched ${o.length} elements; exactly one is required.`);l=o[0]}catch(o){T(document,o,{clientKey:P(i.apiMajor,e.region,e.workKey)});return}const a=P(i.apiMajor,e.region,e.workKey);je(i.version);let c=g().registry.getClient(a),d;if(c)d=c.client;else{try{d=Ke({workKey:e.workKey,region:e.region})}catch(N){T(l,N,{clientKey:a});return}const o=d.ready();o.catch(()=>{}),c={clientKey:a,client:d,ready:o},g().registry.registerClient(c)}try{await c.ready}catch(o){g().registry.unregisterClient(a),d.destroy(),T(l,o,{clientKey:a});return}if(g().registry.getClient(a)!==c)return;let I=!1;const k=[];for(const o of e.features){if(g().registry.findInstance(a,o,l))continue;const N=i.features[o];if(typeof N!="string"){X(d),T(l,new v({code:"CAPABILITY_DISABLED",message:`Capability "${o}" is not part of this release.`,retryable:!1,capability:o}),{clientKey:a,capability:o});continue}try{const u=await import(new URL(N,n).href);if(typeof u.mountDanmaku!="function")throw y(`Capability chunk "${o}" does not export mountDanmaku().`);if(g().registry.getClient(a)!==c)return;const f=await u.mountDanmaku(d,{target:l,theme:e.theme});if(g().registry.getClient(a)!==c){f.destroy();return}const p=g().registry.registerInstance(a,o,l,f);I=!0,k.push(o),K(l,"viceme:capability-ready",{clientKey:a,instanceKey:p.instanceKey,capability:o,version:i.version})}catch(s){if(g().registry.getClient(a)!==c)return;X(d),T(l,s,{clientKey:a,capability:o})}}I&&g().registry.getClient(a)===c&&K(l,"viceme:ready",{clientKey:a,workKey:e.workKey,capabilities:k,version:i.version})}function ze(t){const{queue:e}=g(),r=e.current.then(()=>Ye(t));return e.current=r.catch(()=>{}),r}function re(){if(typeof document>"u")return;let t=document.currentScript instanceof HTMLScriptElement?document.currentScript:null;if(!t){const e=document.querySelectorAll("script[data-viceme-loader]");e.length===1&&(t=e[0]??null)}t&&ze(t).catch(e=>{T(document,e)})}re();const Xe=re})();
|
|
212
|
+
`;const a=i.querySelector("[data-viceme='panel']");a.dataset.action=this.interaction.action,a.dataset.frame="false";const u=i.querySelector("[data-viceme='description']");u.textContent=r.description;const n=i.querySelector("[data-viceme='action']"),o=n.parentElement,l=i.querySelector("[data-viceme='close']"),g=i.querySelector("[data-viceme='backdrop']"),S=i.querySelector("[data-viceme='error']"),s=i.querySelector("[data-viceme='frame']"),L=i.querySelector("[data-viceme='profile']"),c=i.querySelector("[data-viceme='avatar']"),d=i.querySelector("[data-viceme='avatar-fallback']"),m=i.querySelector("[data-viceme='profile-name']"),f=i.querySelector("[data-viceme='profile-description']"),v=i.querySelector("[data-viceme='profile-stats']");n.textContent=r.label,n.hidden=!1,o.dataset.single="true";const T=r.label;s.title=this.interaction.action==="SIGN_IN"?"登录授权":"支付";const h=this.interaction.followTarget;if(h){a.setAttribute("aria-label",this.interaction.action==="SIGN_IN"?`授权 ${h.displayName}`:`关注 ${h.displayName}`),L.dataset.visible="true",m.textContent=h.displayName;const y=[...h.description??""];f.textContent=y.length>q?`${y.slice(0,q).join("")}…`:y.join(""),y.length>q&&(f.title=h.description),v.textContent=`${h.workCount} 个作品`,d.textContent=h.displayName.trim().slice(0,1)||"V",h.avatarUrl&&(c.src=h.avatarUrl,c.alt=`${h.displayName}的头像`,c.hidden=!1,d.hidden=!0)}let E=null;const C=y=>{this.dispatchEvent(new CustomEvent("viceme:access-layer-close",{detail:y}))},k=()=>{E?.cancel(),C("dismissed")};g.addEventListener("click",k),l.addEventListener("click",k),this.addEventListener("keydown",y=>{if(y.key==="Escape"&&k(),y.key==="Tab"){if(y.preventDefault(),E?.type==="frame"){s.focus();return}const A=[l,...n.hidden?[]:[n]].filter(te=>!te.disabled&&!te.hidden);if(A.length===0)return;const Je=A.indexOf(i.activeElement),Ze=y.shiftKey?-1:1;A[(Je+Ze+A.length)%A.length]?.focus()}});const M=async()=>{n.disabled=!0,n.setAttribute("aria-busy","true"),n.textContent="正在打开…",S.textContent="";try{const y=await this.interaction.perform();y.type==="frame"?(E=y,a.dataset.frame="true",s.src=y.url,s.focus(),await y.completion):y.type==="external"&&(E=y,await y.completion),C("acted")}catch(y){E?.cancel(),E=null,a.dataset.frame="false",s.removeAttribute("src"),S.textContent=ve(y),n.hidden=!1,n.disabled=!1,n.removeAttribute("aria-busy"),n.textContent=T,n.focus()}};n.addEventListener("click",M),queueMicrotask(()=>n.focus())}}customElements.define(P,t)}const F=async t=>{if(typeof document>"u"||typeof customElements>"u")return"dismissed";Ee();const e=document.createElement(P);e.interaction=t;const r=document.documentElement.style.overflow,i=document.activeElement instanceof HTMLElement?document.activeElement:null;document.documentElement.style.overflow="hidden",document.body.append(e);try{return await new Promise(a=>{e.addEventListener("viceme:access-layer-close",u=>a(u.detail),{once:!0})})}finally{e.remove(),document.documentElement.style.overflow=r,i?.focus()}};function N(t){if(typeof t!="object"||t===null)throw I();return t}function I(){return new w({code:"INTERNAL_ERROR",message:"Malformed public API response.",retryable:!0})}function Ie(t){const e=N(t);if(typeof e.id!="string"||!(typeof e.displayName=="string"||e.displayName===null)||!(typeof e.avatarUrl=="string"||e.avatarUrl===null))throw I();return{subject:e.id,nickname:e.displayName,avatarUrl:e.avatarUrl}}function z(t){const e=N(t);if(typeof e.following!="boolean"||!(typeof e.followedAt=="string"||e.followedAt===null))throw I();const r=N(e.creator);if(typeof r.displayName!="string"||!(typeof r.avatarUrl=="string"||r.avatarUrl===null)||!(typeof r.description=="string"||r.description===null)||!Number.isInteger(r.publishedWorkCount)||r.publishedWorkCount<0)throw I();return{following:e.following,followedAt:e.followedAt,target:{kind:"CREATOR",displayName:r.displayName,avatarUrl:r.avatarUrl,description:r.description,workCount:r.publishedWorkCount}}}function ke(t){const e=N(t),r=new Set(["PUBLIC","OWNER","FOLLOWING","ENTITLED","AUTH_REQUIRED","FOLLOW_REQUIRED","PURCHASE_REQUIRED","FEATURE_NOT_FOUND","FEATURE_DISABLED"]),i=new Set([null,"SIGN_IN","FOLLOW","CHECKOUT"]);if(typeof e.allowed!="boolean"||typeof e.reason!="string"||!r.has(e.reason)||!i.has(e.nextAction))throw I();return{allowed:e.allowed,reason:e.reason,nextAction:e.nextAction}}function Ae(t){const e=N(t);if(typeof e.featureKey!="string"||!/^[a-z][a-z0-9_-]{1,63}$/.test(e.featureKey)||typeof e.title!="string"||e.title.trim().length===0||e.title.length>120||!(e.policyType==="PUBLIC"||e.policyType==="FOLLOW_OWNER"||e.policyType==="WORK_ENTITLEMENT"))throw I();const r=e.price===null?null:N(e.price);if(r!==null&&(!Number.isInteger(r.amountCents)||r.amountCents<=0||r.currency!=="CNY")||e.policyType==="WORK_ENTITLEMENT"!=(r!==null))throw I();return{featureKey:e.featureKey,title:e.title,policy:{type:e.policyType},price:r===null?null:{amountCents:r.amountCents,currency:"CNY"}}}function Te(){const t=crypto.getRandomValues(new Uint8Array(32));return Se(t)}function Se(t){let e="";for(const r of t)e+=String.fromCharCode(r);return btoa(e).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"")}function Ne(){return typeof document<"u"&&document.documentElement.lang.toLowerCase()==="en-us"?"en-US":"zh-CN"}function Re(t){const e=()=>new w({code:"AUTH_CANCELLED",message:"The interactive action was cancelled.",retryable:!1}),r=(c,d,m,f,v,T)=>{if(typeof window>"u")throw new w({code:"CONFIG_INVALID",message:"Interactive access requires a browser window.",retryable:!1});let h=!0,E,C;const k=()=>{h&&(h=!1,window.removeEventListener("message",y))},M=new Promise((R,A)=>{E=R,C=A}),y=R=>{if(!h||R.origin!==d||typeof R.data!="object"||R.data===null)return;const A=R.data;if(!(A.workKey!==t.workKey||A.channel!==m)){if(A.type===`viceme:${f}:ready`){R.source&&"postMessage"in R.source&&R.source.postMessage({type:`viceme:${f}:init`,workKey:t.workKey,channel:m,...v},d);return}if(A.type===`viceme:${f}:cancelled`){k(),C(e());return}A.type===`viceme:${f}:complete`&&(k(),T(A).then(E,C))}};return window.addEventListener("message",y),{type:"frame",url:c,completion:M,cancel(){k(),E()}}},i=async()=>{if(await t.ready(),typeof window>"u")throw new w({code:"CONFIG_INVALID",message:"Interactive access requires a browser window.",retryable:!1});const c=Te(),d=new URL("/sdk/login",t.widgetOrigin);d.search=new URLSearchParams({workKey:t.workKey,channel:c,parentOrigin:window.location.origin,locale:Ne()}).toString();const m=t.session.snapshot?.token;if(!m)throw I();return r(d.toString(),t.widgetOrigin,c,"auth",{workSessionToken:m},async f=>{if(typeof f.userToken!="string")throw I();t.session.authenticate({userToken:f.userToken,user:Ie(f.user)})})},a=async()=>(await t.ready(),z((await t.session.request({method:"GET",path:"/v1/public/work-sdk/follow"})).body)),u=async()=>(await t.ready(),z((await t.session.request({method:"PUT",path:"/v1/public/work-sdk/follow"})).body)),n={async getState(){await t.ready();const c=t.session.snapshot?.user??null;return{authenticated:c!==null,user:c}},async signIn(){if(await(t.presenter??F)({featureKey:"auth",reason:"AUTH_REQUIRED",action:"SIGN_IN",perform:i})==="dismissed")throw e();return this.getState()},async signOut(){await t.ready(),await t.session.signOut()}},o={getState:a,follow:u,unfollow:async()=>{throw new w({code:"CAPABILITY_DISABLED",message:"Unfollow is not exposed by website access.",retryable:!1})}},l=async c=>{await t.ready();const d=N((await t.session.request({method:"POST",path:"/v1/public/work-sdk/access/check",body:{featureKeys:c}})).body),m=N(d.decisions);return Object.fromEntries(Object.entries(m).map(([f,v])=>[f,ke(v)]))},g=async(c,d={})=>{await t.ready();const m={featureKey:c,locale:d.locale??"zh-CN"},f=N((await t.session.request({method:"POST",path:"/v1/public/work-sdk/checkout",body:m})).body);if(typeof f.checkoutUrl!="string"||typeof f.alreadyOwned!="boolean")throw I();try{new URL(f.checkoutUrl)}catch{throw I()}return{checkoutUrl:f.checkoutUrl,alreadyOwned:f.alreadyOwned}},S=(c,d)=>{if(c.alreadyOwned)return{type:"completed"};if(typeof window>"u")throw new w({code:"CONFIG_INVALID",message:"Hosted checkout requires a browser window.",retryable:!1});const m=window.open(c.checkoutUrl,"_blank","popup,width=520,height=760");if(!m)throw new w({code:"AUTH_CANCELLED",message:"The hosted checkout window was blocked.",retryable:!1});let f=!0,v,T,h;const E=new Promise((k,M)=>{T=k,h=M}),C=async()=>{if(f)try{const k=(await l([d]))[d];if(!k)throw I();if(k.allowed){f=!1,m.close(),T();return}if(m.closed){f=!1,h(e());return}v=setTimeout(()=>{C()},1500)}catch(k){f=!1,h(k)}};return v=setTimeout(()=>{C()},1500),{type:"external",completion:E,cancel(){f&&(f=!1,v&&clearTimeout(v),m.close(),T())}}};return{auth:n,follow:o,access:{async getFeatures(){await t.ready();const c=N((await t.session.request({method:"GET",path:"/v1/public/work-sdk/access/features"})).body);if(!Array.isArray(c.features)||c.features.length>100)throw I();return c.features.map(Ae)},async check(c){const m=(await l([c]))[c];if(!m)throw I();return m},checkMany:l,async require(c){let d=await this.check(c);const m=t.presenter??F;for(let f=0;!d.allowed&&d.nextAction&&f<3;f+=1){const v=d.nextAction,T=v==="FOLLOW"?(await o.getState()).target:void 0,h=v==="CHECKOUT"?await g(c):void 0;if(h?.alreadyOwned){d=await this.check(c);continue}if(await m({featureKey:c,reason:d.reason,action:v,...T?{followTarget:T}:{},perform:async()=>v==="SIGN_IN"?i():v==="FOLLOW"?(await o.follow(),{type:"completed"}):S(h,c)})==="dismissed")return d;d=await this.check(c)}return d},async refresh(){await t.ready()}},checkout:{async open(c){const d=await g(c.featureKey,c);if(d.alreadyOwned)return d;if(await(t.presenter??F)({featureKey:c.featureKey,reason:"PURCHASE_REQUIRED",action:"CHECKOUT",perform:async()=>S(d,c.featureKey)})==="dismissed")throw e();return d}}}}function Y(t){t instanceof Q&&t.markDegraded()}class Oe extends DOMException{constructor(){super("ViceMe client destroyed.","AbortError")}}class Q{#e=new pe;#t;#r;#i=new AbortController;#n;auth;access;checkout;#a;constructor(e){this.#r=e.config,this.#t=new ge({workKey:e.config.workKey,transport:e.transport,signal:this.#i.signal,...e.now!==void 0?{now:e.now}:{}});const r=Re({session:this.#t,workKey:e.config.workKey,widgetOrigin:ne[e.config.region],presenter:e.presenter,ready:()=>this.#o()});this.auth=r.auth,this.access=r.access,this.checkout=r.checkout;const i=e.config.signal;if(i){const a=()=>{this.#i.abort(i.reason instanceof Error?i.reason:new DOMException("Caller signal aborted.","AbortError"))};i.aborted?a():(i.addEventListener("abort",a,{once:!0}),this.#a=()=>{i.removeEventListener("abort",a)})}}get version(){return H}get workKey(){return this.#r.workKey}get region(){return this.#r.region}get state(){return this.#e.state}get apiMajor(){return U}ready(){return this.#e.destroyed?Promise.reject(_()):(this.#n||(this.#e.transition("READY"),this.#n=Promise.resolve()),this.#n)}async#o(){if(this.#e.destroyed)throw _();await this.ready();try{if(await this.#t.establish(),this.#e.destroyed)throw _()}catch(e){throw this.#e.destroyed?_():(this.#t.invalidate(),e)}}hasCapability(e){if(this.#e.destroyed)return!1;const r=this.#t.snapshot?.work.capabilities;return e==="danmaku"||e==="tip"||r?.includes(e)===!0}get capabilities(){return this.#t.snapshot?.work.capabilities??[]}markDegraded(){this.#e.state==="READY"&&this.#e.transition("DEGRADED")}get sessionSnapshot(){return this.#t.snapshot}destroy(){this.#e.destroyed||(this.#e.transition("DESTROYED"),this.#i.abort(new Oe),this.#t.destroy(),this.#e.clearListeners(),this.#n=void 0,this.#a?.(),this.#a=void 0)}}const Le=1e4,Ce=new Map([[400,"CONFIG_INVALID"],[401,"SESSION_EXPIRED"],[403,"CAPABILITY_DISABLED"],[404,"WORK_NOT_FOUND"],[409,"CONFIG_INVALID"],[422,"CONFIG_INVALID"],[429,"RATE_LIMITED"]]),_e=new Set(["CONFIG_INVALID","WORK_NOT_FOUND","CAPABILITY_DISABLED","SESSION_EXPIRED","AUTH_REQUIRED","AUTH_CANCELLED","RETURN_URL_NOT_ALLOWED","RATE_LIMITED","NETWORK_TIMEOUT","CHECKOUT_UNAVAILABLE","INTERNAL_ERROR"]);function De(t){if(typeof t!="object"||t===null)return{};const e=t,r=typeof e.error=="object"&&e.error!==null?e.error:e,i={};return typeof r.code=="string"&&_e.has(r.code)&&(i.code=r.code),typeof r.message=="string"&&(i.message=r.message.slice(0,200)),typeof r.retryable=="boolean"&&(i.retryable=r.retryable),typeof r.requestId=="string"&&(i.requestId=r.requestId),i}function Ue(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`req-${Math.random().toString(36).slice(2)}`}class Me{#e;#t;#r;#i;constructor(e){this.#e=e.apiBaseUrl.replace(/\/+$/,""),this.#t=e.fetchImpl??globalThis.fetch.bind(globalThis),this.#r=e.defaultTimeoutMs??Le,this.#i=e.generateRequestId??Ue}async request(e){if(e.signal?.aborted)throw this.#n(e.signal);const r=e.timeoutMs??this.#r,i=new AbortController;let a=!1;const u=setTimeout(()=>{a=!0,i.abort(new X)},r),n=()=>i.abort(new X(!1));e.signal?.addEventListener("abort",n,{once:!0});const o=this.#i();try{const l=await this.#t(`${this.#e}${e.path}`,{method:e.method,credentials:"omit",mode:"cors",headers:{"content-type":"application/json","x-client-request-id":o,...e.authorization!==void 0?{authorization:`Bearer ${e.authorization}`}:{},...e.userAuthorization!==void 0?{"x-viceme-user-token":e.userAuthorization}:{}},body:e.body!==void 0?JSON.stringify(e.body):void 0,signal:i.signal});let g;try{g=await l.json()}catch(s){if(e.signal?.aborted||a)throw s;g=void 0}const S=l.headers.get("x-request-id")??void 0;if(!l.ok){const s=De(g),L=s.code??Ce.get(l.status)??"INTERNAL_ERROR";throw new w({code:L,message:s.message??"Public API request failed.",retryable:s.retryable,requestId:s.requestId??S})}return{status:l.status,body:g,requestId:S}}catch(l){throw e.signal?.aborted?this.#n(e.signal):a?new w({code:"NETWORK_TIMEOUT",message:"Public API request timed out.",retryable:!0,requestId:o}):l instanceof w?l:l instanceof DOMException&&l.name==="AbortError"?new DOMException("ViceMe request aborted by caller.","AbortError"):new w({code:"INTERNAL_ERROR",message:"Public API request failed.",retryable:!0,requestId:o})}finally{clearTimeout(u),e.signal?.removeEventListener("abort",n)}}#n(e){return e.reason instanceof Error?e.reason:new DOMException("ViceMe request aborted by caller.","AbortError")}}function xe(t){return new Me(t)}class X extends DOMException{constructor(e=!0){super(e?"ViceMe request timed out.":"ViceMe request aborted.","AbortError")}}function Ke(t){const e=ce(t),r=se(e.region),i=xe({apiBaseUrl:r});return new Q({config:e,transport:i})}const Pe=8e3,qe=8e3,J={danmaku:"danmaku.js",tip:"tip.js"},Fe={danmaku:"mountDanmaku",tip:"mountTip"},Z=Symbol.for("viceme.loader.state.v1");function b(){const e=globalThis[Z];if(e)return e;const r={registry:new fe,queue:{current:Promise.resolve()}};return Object.defineProperty(globalThis,Z,{value:r,writable:!0,configurable:!0,enumerable:!1}),r}function $(t){if(!t.destroyed){t.destroyed=!0,b().registry.removeInstance(t.instanceKey);try{t.raw.destroy()}finally{x(t.host,"viceme:destroyed",{clientKey:t.clientKey,instanceKey:t.instanceKey,capability:t.capability})}}}function $e(t){return{instanceKey:t.instanceKey,capability:t.capability,destroy:()=>$(t)}}function j(t,e,r){Object.defineProperty(t,e,{value:r,writable:!0,configurable:!0,enumerable:!1})}function je(t){let r=globalThis.ViceMe;r||(r={},j(globalThis,"ViceMe",r));let i=r.versions;i||(i={},j(r,"versions",i));const a=i[`v${U}`];if(a)return a;const u={version:t,whenReady(n){const o=b().registry.getClient(n);return o?o.ready.then(()=>o.client):Promise.reject(p(`Unknown clientKey "${n}".`))},getInstance(n){const o=b().registry.getInstance(n);return o?$e(o):void 0},destroyInstance(n){const o=b().registry.getInstance(n);o&&$(o)},destroyClient(n){for(const l of b().registry.instancesForClient(n))$(l);const o=b().registry.getClient(n);for(const l of o?.pendingMounts??[])l.abort();b().registry.unregisterClient(n),o?.client.destroy()}};return j(i,`v${U}`,u),u}function Be(){return document.readyState!=="loading"?Promise.resolve():new Promise(t=>{document.addEventListener("DOMContentLoaded",()=>t(),{once:!0})})}async function Ve(t){let e;try{e=await We(t)}catch{e={ok:!1,status:0}}return{manifest:await Ge(e),baseUrl:t}}function We(t){const e=typeof AbortSignal<"u"&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(Pe):void 0;return fetch(t,{credentials:"omit",signal:e})}async function Ge(t){if(!t.ok)throw p(`Release manifest is not available (HTTP ${t.status}).`);const e=await t.json();if(typeof e!="object"||e===null||typeof e.version!="string"||typeof e.apiMajor!="number"||typeof e.features!="object"||e.features===null)throw p("Release manifest is malformed.");const r=e;if(r.version!==H)throw p("Release manifest version does not match this loader.");if(r.apiMajor!==U)throw p("Release manifest major version does not match this loader.");const i=Object.keys(r.features).sort();if(i.length!==2||i[0]!=="danmaku"||i[1]!=="tip"||r.features.danmaku!==J.danmaku||r.features.tip!==J.tip)throw p("Release manifest features must be exactly danmaku.js and tip.js.");return r}const He=new Set(["CONFIG_INVALID","CAPABILITY_DISABLED","CLIENT_DESTROYED","INTERNAL_ERROR"]);function ze(t){if(typeof t=="object"&&t!==null){const e=t;if(typeof e.code=="string"&&He.has(e.code)&&typeof e.retryable=="boolean"){const r={code:e.code,retryable:e.retryable};return typeof e.requestId=="string"&&(r.requestId=e.requestId),typeof e.capability=="string"&&(r.capability=e.capability),r}}return{code:"INTERNAL_ERROR",retryable:!0}}function O(t,e,r={}){x(t,"viceme:error",{...ze(e),...r})}function Ye(t,e,r){let i,a;const u=new Promise((n,o)=>{if(a=()=>o(_()),e.signal.aborted){a();return}e.signal.addEventListener("abort",a,{once:!0}),i=globalThis.setTimeout(()=>{o(new w({code:"INTERNAL_ERROR",message:`Capability "${r}" did not load and mount in time.`,retryable:!0,capability:r})),e.abort()},qe)});return Promise.race([t,u]).finally(()=>{i!==void 0&&globalThis.clearTimeout(i),a&&e.signal.removeEventListener("abort",a)})}async function Qe(t){let e;try{e=ue(t)}catch(s){O(document,s);return}if(!t.src){O(document,p("Loader script requires a src URL."));return}const r=new URL("manifest.json",t.src);let i,a;try{({manifest:i,baseUrl:a}=await Ve(r))}catch(s){O(document,s,{clientKey:K(U,e.region,e.workKey)});return}await Be();let u;try{const s=document.querySelectorAll(e.target);if(s.length===0)throw p(`Loader target "${e.target}" matched no element; no node was created.`);if(s.length>1)throw p(`Loader target "${e.target}" matched ${s.length} elements; exactly one is required.`);u=s[0]}catch(s){O(document,s,{clientKey:K(i.apiMajor,e.region,e.workKey)});return}const n=K(i.apiMajor,e.region,e.workKey);je(i.version);let o=b().registry.getClient(n),l;if(o)l=o.client;else{try{l=Ke({workKey:e.workKey,region:e.region})}catch(L){O(u,L,{clientKey:n});return}const s=l.ready();s.catch(()=>{}),o={clientKey:n,client:l,ready:s,pendingMounts:new Set},b().registry.registerClient(o)}try{await o.ready}catch(s){b().registry.unregisterClient(n),l.destroy(),O(u,s,{clientKey:n});return}if(b().registry.getClient(n)!==o)return;const g=async s=>{if(b().registry.findInstance(n,s,u))return;const L=i.features[s];if(typeof L!="string"){Y(l),O(u,new w({code:"CAPABILITY_DISABLED",message:`Capability "${s}" is not part of this release.`,retryable:!1,capability:s}),{clientKey:n,capability:s});return}const c=new AbortController;o.pendingMounts.add(c);try{const d=(async()=>{const T=await import(new URL(L,a).href),h=Fe[s],E=T[h];if(typeof E!="function")throw p(`Capability chunk "${s}" does not export ${h}().`);if(b().registry.getClient(n)!==o)throw _();return await E(l,{target:u,theme:e.theme,signal:c.signal})})(),m=await Ye(d,c,s);if(b().registry.getClient(n)!==o){m.destroy();return}const f=b().registry.registerInstance(n,s,u,m);return x(u,"viceme:capability-ready",{clientKey:n,instanceKey:f.instanceKey,capability:s,version:i.version}),s}catch(d){if(b().registry.getClient(n)!==o)return;Y(l),O(u,d,{clientKey:n,capability:s});return}finally{o.pendingMounts.delete(c)}},S=(await Promise.all(e.features.map(g))).filter(s=>s!==void 0);S.length>0&&b().registry.getClient(n)===o&&x(u,"viceme:ready",{clientKey:n,workKey:e.workKey,capabilities:S,version:i.version})}function Xe(t){const{queue:e}=b(),r=e.current.then(()=>Qe(t));return e.current=r.catch(()=>{}),r}function ee(){if(typeof document>"u")return;let t=document.currentScript instanceof HTMLScriptElement?document.currentScript:null;if(!t){const e=document.querySelectorAll("script[data-viceme-loader]");e.length===1&&(t=e[0]??null)}t&&Xe(t).catch(e=>{O(document,e)})}ee();const et=ee})();
|
|
233
213
|
//# sourceMappingURL=viceme.min.js.map
|