@opendatalabs/vana-sdk 4.4.0 → 4.5.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.
Files changed (106) hide show
  1. package/README.md +176 -0
  2. package/dist/connect/account-origin.cjs +72 -0
  3. package/dist/connect/account-origin.cjs.map +1 -0
  4. package/dist/connect/account-origin.d.ts +92 -0
  5. package/dist/connect/account-origin.js +47 -0
  6. package/dist/connect/account-origin.js.map +1 -0
  7. package/dist/connect/account-origin.test.d.ts +1 -0
  8. package/dist/connect/chrome.cjs +284 -0
  9. package/dist/connect/chrome.cjs.map +1 -0
  10. package/dist/connect/chrome.d.ts +42 -0
  11. package/dist/connect/chrome.js +256 -0
  12. package/dist/connect/chrome.js.map +1 -0
  13. package/dist/connect/http-transports.cjs +100 -0
  14. package/dist/connect/http-transports.cjs.map +1 -0
  15. package/dist/connect/http-transports.d.ts +77 -0
  16. package/dist/connect/http-transports.js +75 -0
  17. package/dist/connect/http-transports.js.map +1 -0
  18. package/dist/connect/http-transports.test.d.ts +1 -0
  19. package/dist/connect/launcher.cjs +232 -0
  20. package/dist/connect/launcher.cjs.map +1 -0
  21. package/dist/connect/launcher.d.ts +129 -0
  22. package/dist/connect/launcher.js +220 -0
  23. package/dist/connect/launcher.js.map +1 -0
  24. package/dist/connect/launcher.test.d.ts +1 -0
  25. package/dist/connect/protocol.cjs +259 -0
  26. package/dist/connect/protocol.cjs.map +1 -0
  27. package/dist/connect/protocol.d.ts +331 -0
  28. package/dist/connect/protocol.js +215 -0
  29. package/dist/connect/protocol.js.map +1 -0
  30. package/dist/connect/protocol.test.d.ts +1 -0
  31. package/dist/connect.cjs +55 -0
  32. package/dist/connect.cjs.map +1 -0
  33. package/dist/connect.d.ts +35 -0
  34. package/dist/connect.js +34 -0
  35. package/dist/connect.js.map +1 -0
  36. package/dist/direct/connect-flow-sheet.test.d.ts +1 -0
  37. package/dist/direct/connect-flow.cjs +211 -1
  38. package/dist/direct/connect-flow.cjs.map +1 -1
  39. package/dist/direct/connect-flow.d.ts +75 -0
  40. package/dist/direct/connect-flow.js +213 -1
  41. package/dist/direct/connect-flow.js.map +1 -1
  42. package/dist/direct/connect-handler.cjs +493 -0
  43. package/dist/direct/connect-handler.cjs.map +1 -0
  44. package/dist/direct/connect-handler.d.ts +246 -0
  45. package/dist/direct/connect-handler.js +476 -0
  46. package/dist/direct/connect-handler.js.map +1 -0
  47. package/dist/direct/connect-handler.test.d.ts +1 -0
  48. package/dist/direct/controller.cjs +86 -17
  49. package/dist/direct/controller.cjs.map +1 -1
  50. package/dist/direct/controller.d.ts +101 -6
  51. package/dist/direct/controller.js +86 -16
  52. package/dist/direct/controller.js.map +1 -1
  53. package/dist/direct/endpoints.cjs +5 -2
  54. package/dist/direct/endpoints.cjs.map +1 -1
  55. package/dist/direct/endpoints.js +5 -2
  56. package/dist/direct/endpoints.js.map +1 -1
  57. package/dist/direct/request-authorization.cjs +65 -0
  58. package/dist/direct/request-authorization.cjs.map +1 -0
  59. package/dist/direct/request-authorization.d.ts +65 -0
  60. package/dist/direct/request-authorization.js +39 -0
  61. package/dist/direct/request-authorization.js.map +1 -0
  62. package/dist/direct/request-authorization.test.d.ts +1 -0
  63. package/dist/direct/types.cjs.map +1 -1
  64. package/dist/direct/types.d.ts +6 -0
  65. package/dist/direct/types.js.map +1 -1
  66. package/dist/direct/use-direct-vana-connect.cjs +18 -1
  67. package/dist/direct/use-direct-vana-connect.cjs.map +1 -1
  68. package/dist/direct/use-direct-vana-connect.js +18 -1
  69. package/dist/direct/use-direct-vana-connect.js.map +1 -1
  70. package/dist/direct/use-vana-connect.cjs +84 -0
  71. package/dist/direct/use-vana-connect.cjs.map +1 -0
  72. package/dist/direct/use-vana-connect.d.ts +59 -0
  73. package/dist/direct/use-vana-connect.js +64 -0
  74. package/dist/direct/use-vana-connect.js.map +1 -0
  75. package/dist/direct/use-vana-connect.test.d.ts +1 -0
  76. package/dist/index.browser.d.ts +1 -0
  77. package/dist/index.browser.js +104 -0
  78. package/dist/index.browser.js.map +4 -4
  79. package/dist/index.node.cjs +112 -0
  80. package/dist/index.node.cjs.map +3 -3
  81. package/dist/index.node.d.ts +1 -0
  82. package/dist/index.node.js +104 -0
  83. package/dist/index.node.js.map +4 -4
  84. package/dist/protocol/fixtures/pdpp-scope-keys.json +133 -0
  85. package/dist/protocol/pdpp-scopes.cjs +134 -0
  86. package/dist/protocol/pdpp-scopes.cjs.map +1 -0
  87. package/dist/protocol/pdpp-scopes.d.ts +123 -0
  88. package/dist/protocol/pdpp-scopes.js +103 -0
  89. package/dist/protocol/pdpp-scopes.js.map +1 -0
  90. package/dist/protocol/pdpp-scopes.test.d.ts +1 -0
  91. package/dist/protocol/scopes.cjs +17 -0
  92. package/dist/protocol/scopes.cjs.map +1 -1
  93. package/dist/protocol/scopes.d.ts +1 -0
  94. package/dist/protocol/scopes.js +18 -0
  95. package/dist/protocol/scopes.js.map +1 -1
  96. package/dist/react.cjs +10 -2
  97. package/dist/react.cjs.map +1 -1
  98. package/dist/react.d.ts +4 -1
  99. package/dist/react.js +11 -1
  100. package/dist/react.js.map +1 -1
  101. package/dist/server.cjs +61 -2
  102. package/dist/server.cjs.map +1 -1
  103. package/dist/server.d.ts +16 -4
  104. package/dist/server.js +67 -3
  105. package/dist/server.js.map +1 -1
  106. package/package.json +11 -1
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Partner-side launcher for the Vana connect sheet.
3
+ *
4
+ * @remarks
5
+ * `connect.open({ clientId, appName, env, getRequest, onEvent, display? })` returns
6
+ * `{ close, setRequestStatus }`. Only launcher, chrome and message
7
+ * protocol live here. Identity, consent and signing live in the Vana-hosted
8
+ * iframe and Vana's servers; no token ever crosses into the partner page.
9
+ *
10
+ * Most apps should not call this directly: `createDirectConnectFlow` (and
11
+ * `useVanaConnect`) with `presentation: "sheet"` open it and wire it to the
12
+ * request, the status poll and the read.
13
+ *
14
+ * Ported from `@vana-unity/connect-embed` with two differences: the version
15
+ * handshake (`incompatible_version`), and no default Vana origin (pass `env`
16
+ * or `vanaOrigin`).
17
+ *
18
+ * `connected` means the person approved; it is not delivery. Treat the data
19
+ * as delivered only once your backend's status poll and read say so (the
20
+ * connect flow's `done`).
21
+ *
22
+ * @category Connect
23
+ * @module connect/launcher
24
+ */
25
+ import { type CloseReason } from "./chrome.js";
26
+ import { type ConnectCompletion, type ConnectDisplay, type ConnectProgressPhase, type ConnectRequest, type RequestStatus, type SheetErrorCode, type VanaConnectEnv } from "./protocol.js";
27
+ /**
28
+ * What the launcher reports to `onEvent`.
29
+ *
30
+ * @remarks
31
+ * `connected` is approval, not delivery: data is delivered only when the
32
+ * partner's backend reads it. The sheet sends no terminal "delivered" event
33
+ * today; the connect flow settles from the status poll instead.
34
+ */
35
+ export type ConnectEvent = {
36
+ type: "ready";
37
+ } | {
38
+ type: "signed-in";
39
+ } | {
40
+ completion: ConnectCompletion;
41
+ status: "approved";
42
+ type: "connected";
43
+ } | {
44
+ phase: ConnectProgressPhase;
45
+ type: "progress";
46
+ } | {
47
+ reason: ConnectCloseReason;
48
+ type: "close";
49
+ } | {
50
+ code: SheetErrorCode | "request_failed";
51
+ type: "error";
52
+ };
53
+ /** Every close says why, so a sheet never disappears unexplained. */
54
+ export type ConnectCloseReason = CloseReason | "api" | "escape" | "frame_blocked" | "incompatible_version" | "origin_not_registered" | "replaced" | "sheet";
55
+ /**
56
+ * Which Vana Account serves the sheet: an environment, or an exact origin
57
+ * (resolve preview origins with `resolveVanaAccountOrigin`). Required, so a
58
+ * dev app never silently frames production.
59
+ */
60
+ export type ConnectTarget = {
61
+ /** Vana deployment; the sheet comes from `VANA_ACCOUNT_ORIGINS[env]`. */
62
+ env: VanaConnectEnv;
63
+ vanaOrigin?: undefined;
64
+ } | {
65
+ /** Vana Account origin. */
66
+ vanaOrigin: string;
67
+ env?: undefined;
68
+ };
69
+ /** Options for {@link open}, besides the {@link ConnectTarget}. */
70
+ export interface ConnectOpenBaseOptions {
71
+ /** Chrome label only. Vana never trusts it; the sheet shows registered names. */
72
+ appName: string;
73
+ /** Registered embed client id. */
74
+ clientId: string;
75
+ /** Defaults to `document.body`. */
76
+ container?: HTMLElement;
77
+ /**
78
+ * `sheet` (the default) is a bottom sheet over your page. `fullscreen` fills
79
+ * this window, for a window that holds nothing else, such as a small popup
80
+ * you opened yourself: close that window on the `close` event.
81
+ */
82
+ display?: ConnectDisplay;
83
+ /**
84
+ * Called when the sheet asks for the request (after sign-in). The partner's
85
+ * backend creates the request or re-signs its short-lived read capability.
86
+ */
87
+ getRequest: () => Promise<ConnectRequest>;
88
+ onEvent?: (event: ConnectEvent) => void;
89
+ /**
90
+ * CSP-blocked frames still fire `load`, so only `vana:ready` proves the
91
+ * sheet runs. Without it by this deadline the sheet closes with
92
+ * `frame_blocked`.
93
+ */
94
+ readyTimeoutMs?: number;
95
+ }
96
+ /** Options for {@link open}. */
97
+ export type ConnectOpenOptions = ConnectOpenBaseOptions & ConnectTarget;
98
+ /** The open sheet. */
99
+ export interface ConnectHandle {
100
+ /** Close the sheet; emits `close` with reason `"api"`. */
101
+ close(): void;
102
+ /** Report the request outcome the partner's backend observed. */
103
+ setRequestStatus(status: RequestStatus): void;
104
+ }
105
+ /**
106
+ * Open the Vana connect sheet over the page. One sheet exists at a time;
107
+ * opening another closes the first with reason `"replaced"`.
108
+ *
109
+ * @param options - Client id, app name, the request source and event sink.
110
+ * @remarks
111
+ * Alongside framing, it asks Vana (`GET /api/embed/connect/client`, no
112
+ * credentials, no cache) whether this page's origin is registered for the
113
+ * client id, and closes with `origin_not_registered` on a definite no. If
114
+ * that check cannot be answered, the ready deadline decides.
115
+ *
116
+ * @param options - Client id, app name, Vana target, the request source and
117
+ * event sink.
118
+ * @returns A handle to close the sheet and report status.
119
+ * @throws TypeError when neither (or both) of `env` and `vanaOrigin` is
120
+ * given, `vanaOrigin` is not an http(s) origin, or `display` is not a known
121
+ * display.
122
+ */
123
+ export declare function open(options: ConnectOpenOptions): ConnectHandle;
124
+ /** Alias of {@link open}, for call sites where `open` would be ambiguous. */
125
+ export declare const openConnectSheet: typeof open;
126
+ /** Namespace form: `connect.open(...)`. */
127
+ export declare const connect: {
128
+ readonly open: typeof open;
129
+ };
@@ -0,0 +1,220 @@
1
+ import {
2
+ createSheetChrome
3
+ } from "./chrome.js";
4
+ import {
5
+ clientCheckUrl,
6
+ DEFAULT_CONNECT_DISPLAY,
7
+ MIN_SHEET_HEIGHT_PX,
8
+ normalizeOrigin,
9
+ PROTOCOL_VERSION,
10
+ parseClientCheck,
11
+ parseSheetMessage,
12
+ readConnectDisplay,
13
+ readIncompatibleVersion,
14
+ sheetUrl,
15
+ VANA_ACCOUNT_ORIGINS
16
+ } from "./protocol.js";
17
+ const DEFAULT_READY_TIMEOUT_MS = 1e4;
18
+ let closeCurrent = null;
19
+ function open(options) {
20
+ const vanaOrigin = resolveTarget(options);
21
+ const display = readConnectDisplay(
22
+ options.display ?? DEFAULT_CONNECT_DISPLAY
23
+ );
24
+ if (!display) {
25
+ throw new TypeError('display must be "sheet" or "fullscreen"');
26
+ }
27
+ closeCurrent?.("replaced");
28
+ const emit = (event) => options.onEvent?.(event);
29
+ const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
30
+ const previousOverflow = document.body.style.overflow;
31
+ let closed = false;
32
+ let ready = false;
33
+ let pendingStatus = null;
34
+ let chrome = null;
35
+ const post = (message) => {
36
+ chrome?.iframe.contentWindow?.postMessage(message, vanaOrigin);
37
+ };
38
+ const close = (reason) => {
39
+ if (closed) {
40
+ return;
41
+ }
42
+ closed = true;
43
+ window.clearTimeout(readyTimer);
44
+ window.removeEventListener("message", onMessage);
45
+ document.removeEventListener("keydown", onKeyDown);
46
+ chrome?.remove();
47
+ document.body.style.overflow = previousOverflow;
48
+ previousFocus?.focus();
49
+ if (closeCurrent === close) {
50
+ closeCurrent = null;
51
+ }
52
+ emit({ reason, type: "close" });
53
+ };
54
+ const requestFromPartner = () => {
55
+ options.getRequest().then(
56
+ (request) => {
57
+ if (!closed) {
58
+ post({ request, type: "vana:request", v: PROTOCOL_VERSION });
59
+ }
60
+ },
61
+ () => {
62
+ if (!closed) {
63
+ emit({ code: "request_failed", type: "error" });
64
+ }
65
+ }
66
+ );
67
+ };
68
+ const handleSheetMessage = (message) => {
69
+ switch (message.type) {
70
+ case "vana:ready":
71
+ if (ready) {
72
+ return;
73
+ }
74
+ window.clearTimeout(readyTimer);
75
+ ready = true;
76
+ if (pendingStatus) {
77
+ post({
78
+ status: pendingStatus,
79
+ type: "vana:request-status",
80
+ v: PROTOCOL_VERSION
81
+ });
82
+ pendingStatus = null;
83
+ }
84
+ emit({ type: "ready" });
85
+ return;
86
+ case "vana:resize":
87
+ if (message.height >= MIN_SHEET_HEIGHT_PX) {
88
+ chrome?.setContentHeight(message.height);
89
+ }
90
+ return;
91
+ case "vana:signed-in":
92
+ emit({ type: "signed-in" });
93
+ return;
94
+ case "vana:request-needed":
95
+ requestFromPartner();
96
+ return;
97
+ case "vana:connected":
98
+ emit({
99
+ completion: message.completion,
100
+ status: message.status,
101
+ type: "connected"
102
+ });
103
+ return;
104
+ case "vana:progress":
105
+ emit({ phase: message.phase, type: "progress" });
106
+ return;
107
+ case "vana:close":
108
+ close("sheet");
109
+ return;
110
+ case "vana:error":
111
+ window.clearTimeout(readyTimer);
112
+ emit({ code: message.code, type: "error" });
113
+ return;
114
+ default:
115
+ return;
116
+ }
117
+ };
118
+ function onMessage(event) {
119
+ if (event.origin !== vanaOrigin || !chrome || event.source !== chrome.iframe.contentWindow) {
120
+ return;
121
+ }
122
+ if (readIncompatibleVersion(event.data) !== null) {
123
+ emit({ code: "incompatible_version", type: "error" });
124
+ close("incompatible_version");
125
+ return;
126
+ }
127
+ const message = parseSheetMessage(event.data);
128
+ if (message) {
129
+ handleSheetMessage(message);
130
+ }
131
+ }
132
+ function onKeyDown(event) {
133
+ if (event.key === "Escape") {
134
+ close("escape");
135
+ }
136
+ }
137
+ const handle = {
138
+ close: () => {
139
+ close("api");
140
+ },
141
+ setRequestStatus(status) {
142
+ if (!ready) {
143
+ pendingStatus = status;
144
+ return;
145
+ }
146
+ post({ status, type: "vana:request-status", v: PROTOCOL_VERSION });
147
+ }
148
+ };
149
+ window.addEventListener("message", onMessage);
150
+ document.addEventListener("keydown", onKeyDown);
151
+ const readyTimer = window.setTimeout(() => {
152
+ emit({ code: "frame_blocked", type: "error" });
153
+ close("frame_blocked");
154
+ }, options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS);
155
+ checkClient(
156
+ clientCheckUrl({
157
+ clientId: options.clientId,
158
+ partnerOrigin: window.location.origin,
159
+ vanaOrigin
160
+ }),
161
+ () => {
162
+ if (!(closed || ready)) {
163
+ emit({ code: "origin_not_registered", type: "error" });
164
+ close("origin_not_registered");
165
+ }
166
+ }
167
+ );
168
+ chrome = createSheetChrome({
169
+ ariaLabel: `${options.appName} uses Vana`,
170
+ container: options.container ?? document.body,
171
+ display,
172
+ onClose: close,
173
+ src: sheetUrl({
174
+ clientId: options.clientId,
175
+ display,
176
+ partnerOrigin: window.location.origin,
177
+ vanaOrigin
178
+ })
179
+ });
180
+ document.body.style.overflow = "hidden";
181
+ chrome.show();
182
+ chrome.iframe.focus();
183
+ closeCurrent = close;
184
+ return handle;
185
+ }
186
+ function resolveTarget(target) {
187
+ const { env, vanaOrigin } = target;
188
+ if (env === void 0 === (vanaOrigin === void 0)) {
189
+ throw new TypeError("Pass exactly one of env and vanaOrigin");
190
+ }
191
+ if (env !== void 0) {
192
+ if (env !== "production" && env !== "dev") {
193
+ throw new TypeError('env must be "production" or "dev"');
194
+ }
195
+ return VANA_ACCOUNT_ORIGINS[env];
196
+ }
197
+ const origin = typeof vanaOrigin === "string" ? normalizeOrigin(vanaOrigin) : null;
198
+ if (!origin) {
199
+ throw new TypeError("vanaOrigin must be an http(s) origin");
200
+ }
201
+ return origin;
202
+ }
203
+ function checkClient(url, onRefused) {
204
+ if (typeof fetch !== "function") {
205
+ return;
206
+ }
207
+ fetch(url, { cache: "no-store", credentials: "omit" }).then((response) => response.ok ? response.json() : null).then((body) => {
208
+ if (parseClientCheck(body)?.registered === false) {
209
+ onRefused();
210
+ }
211
+ }).catch(() => void 0);
212
+ }
213
+ const openConnectSheet = open;
214
+ const connect = { open };
215
+ export {
216
+ connect,
217
+ open,
218
+ openConnectSheet
219
+ };
220
+ //# sourceMappingURL=launcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/connect/launcher.ts"],"sourcesContent":["/**\n * Partner-side launcher for the Vana connect sheet.\n *\n * @remarks\n * `connect.open({ clientId, appName, env, getRequest, onEvent, display? })` returns\n * `{ close, setRequestStatus }`. Only launcher, chrome and message\n * protocol live here. Identity, consent and signing live in the Vana-hosted\n * iframe and Vana's servers; no token ever crosses into the partner page.\n *\n * Most apps should not call this directly: `createDirectConnectFlow` (and\n * `useVanaConnect`) with `presentation: \"sheet\"` open it and wire it to the\n * request, the status poll and the read.\n *\n * Ported from `@vana-unity/connect-embed` with two differences: the version\n * handshake (`incompatible_version`), and no default Vana origin (pass `env`\n * or `vanaOrigin`).\n *\n * `connected` means the person approved; it is not delivery. Treat the data\n * as delivered only once your backend's status poll and read say so (the\n * connect flow's `done`).\n *\n * @category Connect\n * @module connect/launcher\n */\n\nimport {\n type CloseReason,\n createSheetChrome,\n type SheetChrome,\n} from \"./chrome\";\nimport {\n type ConnectCompletion,\n type ConnectDisplay,\n type ConnectProgressPhase,\n type ConnectRequest,\n clientCheckUrl,\n DEFAULT_CONNECT_DISPLAY,\n MIN_SHEET_HEIGHT_PX,\n normalizeOrigin,\n type PartnerMessage,\n PROTOCOL_VERSION,\n parseClientCheck,\n parseSheetMessage,\n readConnectDisplay,\n readIncompatibleVersion,\n type RequestStatus,\n type SheetErrorCode,\n type SheetMessage,\n sheetUrl,\n VANA_ACCOUNT_ORIGINS,\n type VanaConnectEnv,\n} from \"./protocol\";\n\nconst DEFAULT_READY_TIMEOUT_MS = 10_000;\n\n/**\n * What the launcher reports to `onEvent`.\n *\n * @remarks\n * `connected` is approval, not delivery: data is delivered only when the\n * partner's backend reads it. The sheet sends no terminal \"delivered\" event\n * today; the connect flow settles from the status poll instead.\n */\nexport type ConnectEvent =\n | { type: \"ready\" }\n | { type: \"signed-in\" }\n | { completion: ConnectCompletion; status: \"approved\"; type: \"connected\" }\n | { phase: ConnectProgressPhase; type: \"progress\" }\n | { reason: ConnectCloseReason; type: \"close\" }\n | { code: SheetErrorCode | \"request_failed\"; type: \"error\" };\n\n/** Every close says why, so a sheet never disappears unexplained. */\nexport type ConnectCloseReason =\n | CloseReason\n | \"api\"\n | \"escape\"\n | \"frame_blocked\"\n | \"incompatible_version\"\n | \"origin_not_registered\"\n | \"replaced\"\n | \"sheet\";\n\n/**\n * Which Vana Account serves the sheet: an environment, or an exact origin\n * (resolve preview origins with `resolveVanaAccountOrigin`). Required, so a\n * dev app never silently frames production.\n */\nexport type ConnectTarget =\n | {\n /** Vana deployment; the sheet comes from `VANA_ACCOUNT_ORIGINS[env]`. */\n env: VanaConnectEnv;\n vanaOrigin?: undefined;\n }\n | {\n /** Vana Account origin. */\n vanaOrigin: string;\n env?: undefined;\n };\n\n/** Options for {@link open}, besides the {@link ConnectTarget}. */\nexport interface ConnectOpenBaseOptions {\n /** Chrome label only. Vana never trusts it; the sheet shows registered names. */\n appName: string;\n /** Registered embed client id. */\n clientId: string;\n /** Defaults to `document.body`. */\n container?: HTMLElement;\n /**\n * `sheet` (the default) is a bottom sheet over your page. `fullscreen` fills\n * this window, for a window that holds nothing else, such as a small popup\n * you opened yourself: close that window on the `close` event.\n */\n display?: ConnectDisplay;\n /**\n * Called when the sheet asks for the request (after sign-in). The partner's\n * backend creates the request or re-signs its short-lived read capability.\n */\n getRequest: () => Promise<ConnectRequest>;\n onEvent?: (event: ConnectEvent) => void;\n /**\n * CSP-blocked frames still fire `load`, so only `vana:ready` proves the\n * sheet runs. Without it by this deadline the sheet closes with\n * `frame_blocked`.\n */\n readyTimeoutMs?: number;\n}\n\n/** Options for {@link open}. */\nexport type ConnectOpenOptions = ConnectOpenBaseOptions & ConnectTarget;\n\n/** The open sheet. */\nexport interface ConnectHandle {\n /** Close the sheet; emits `close` with reason `\"api\"`. */\n close(): void;\n /** Report the request outcome the partner's backend observed. */\n setRequestStatus(status: RequestStatus): void;\n}\n\n/** Closes the open sheet, if any. One sheet at a time. */\nlet closeCurrent: ((reason: ConnectCloseReason) => void) | null = null;\n\n/**\n * Open the Vana connect sheet over the page. One sheet exists at a time;\n * opening another closes the first with reason `\"replaced\"`.\n *\n * @param options - Client id, app name, the request source and event sink.\n * @remarks\n * Alongside framing, it asks Vana (`GET /api/embed/connect/client`, no\n * credentials, no cache) whether this page's origin is registered for the\n * client id, and closes with `origin_not_registered` on a definite no. If\n * that check cannot be answered, the ready deadline decides.\n *\n * @param options - Client id, app name, Vana target, the request source and\n * event sink.\n * @returns A handle to close the sheet and report status.\n * @throws TypeError when neither (or both) of `env` and `vanaOrigin` is\n * given, `vanaOrigin` is not an http(s) origin, or `display` is not a known\n * display.\n */\nexport function open(options: ConnectOpenOptions): ConnectHandle {\n const vanaOrigin = resolveTarget(options);\n const display = readConnectDisplay(\n options.display ?? DEFAULT_CONNECT_DISPLAY,\n );\n if (!display) {\n throw new TypeError('display must be \"sheet\" or \"fullscreen\"');\n }\n closeCurrent?.(\"replaced\");\n\n const emit = (event: ConnectEvent) => options.onEvent?.(event);\n const previousFocus =\n document.activeElement instanceof HTMLElement\n ? document.activeElement\n : null;\n const previousOverflow = document.body.style.overflow;\n let closed = false;\n let ready = false;\n /** Latest status reported before the sheet was ready, sent on ready. */\n let pendingStatus: RequestStatus | null = null;\n let chrome: SheetChrome | null = null;\n\n const post = (message: PartnerMessage) => {\n chrome?.iframe.contentWindow?.postMessage(message, vanaOrigin);\n };\n\n const close = (reason: ConnectCloseReason) => {\n if (closed) {\n return;\n }\n closed = true;\n window.clearTimeout(readyTimer);\n window.removeEventListener(\"message\", onMessage);\n document.removeEventListener(\"keydown\", onKeyDown);\n chrome?.remove();\n document.body.style.overflow = previousOverflow;\n previousFocus?.focus();\n if (closeCurrent === close) {\n closeCurrent = null;\n }\n emit({ reason, type: \"close\" });\n };\n\n const requestFromPartner = () => {\n options.getRequest().then(\n (request) => {\n if (!closed) {\n post({ request, type: \"vana:request\", v: PROTOCOL_VERSION });\n }\n },\n () => {\n if (!closed) {\n emit({ code: \"request_failed\", type: \"error\" });\n }\n },\n );\n };\n\n const handleSheetMessage = (message: SheetMessage) => {\n switch (message.type) {\n case \"vana:ready\":\n // A sheet that mounts twice (strict-mode dev builds) says ready twice.\n if (ready) {\n return;\n }\n window.clearTimeout(readyTimer);\n ready = true;\n if (pendingStatus) {\n post({\n status: pendingStatus,\n type: \"vana:request-status\",\n v: PROTOCOL_VERSION,\n });\n pendingStatus = null;\n }\n emit({ type: \"ready\" });\n return;\n case \"vana:resize\":\n if (message.height >= MIN_SHEET_HEIGHT_PX) {\n chrome?.setContentHeight(message.height);\n }\n return;\n case \"vana:signed-in\":\n emit({ type: \"signed-in\" });\n return;\n case \"vana:request-needed\":\n requestFromPartner();\n return;\n case \"vana:connected\":\n emit({\n completion: message.completion,\n status: message.status,\n type: \"connected\",\n });\n return;\n case \"vana:progress\":\n emit({ phase: message.phase, type: \"progress\" });\n return;\n case \"vana:close\":\n close(\"sheet\");\n return;\n case \"vana:error\":\n // The frame runs, so it is not blocked; the sheet stays open to say why.\n window.clearTimeout(readyTimer);\n emit({ code: message.code, type: \"error\" });\n return;\n default:\n return;\n }\n };\n\n function onMessage(event: MessageEvent) {\n if (\n event.origin !== vanaOrigin ||\n !chrome ||\n event.source !== chrome.iframe.contentWindow\n ) {\n return;\n }\n // A sheet at a version this launcher cannot speak is up but unusable:\n // say so, rather than waiting out the ready timeout as \"frame_blocked\".\n if (readIncompatibleVersion(event.data) !== null) {\n emit({ code: \"incompatible_version\", type: \"error\" });\n close(\"incompatible_version\");\n return;\n }\n const message = parseSheetMessage(event.data);\n if (message) {\n handleSheetMessage(message);\n }\n }\n\n function onKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n close(\"escape\");\n }\n }\n\n const handle: ConnectHandle = {\n close: () => {\n close(\"api\");\n },\n setRequestStatus(status) {\n // Before ready the frame still holds about:blank; posting would be lost.\n if (!ready) {\n pendingStatus = status;\n return;\n }\n post({ status, type: \"vana:request-status\", v: PROTOCOL_VERSION });\n },\n };\n\n window.addEventListener(\"message\", onMessage);\n document.addEventListener(\"keydown\", onKeyDown);\n const readyTimer = window.setTimeout(() => {\n emit({ code: \"frame_blocked\", type: \"error\" });\n close(\"frame_blocked\");\n }, options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS);\n\n checkClient(\n clientCheckUrl({\n clientId: options.clientId,\n partnerOrigin: window.location.origin,\n vanaOrigin,\n }),\n () => {\n if (!(closed || ready)) {\n emit({ code: \"origin_not_registered\", type: \"error\" });\n close(\"origin_not_registered\");\n }\n },\n );\n\n chrome = createSheetChrome({\n ariaLabel: `${options.appName} uses Vana`,\n container: options.container ?? document.body,\n display,\n onClose: close,\n src: sheetUrl({\n clientId: options.clientId,\n display,\n partnerOrigin: window.location.origin,\n vanaOrigin,\n }),\n });\n document.body.style.overflow = \"hidden\";\n chrome.show();\n chrome.iframe.focus();\n\n closeCurrent = close;\n return handle;\n}\n\nfunction resolveTarget(target: ConnectTarget): string {\n const { env, vanaOrigin } = target as {\n env?: unknown;\n vanaOrigin?: unknown;\n };\n if ((env === undefined) === (vanaOrigin === undefined)) {\n throw new TypeError(\"Pass exactly one of env and vanaOrigin\");\n }\n if (env !== undefined) {\n if (env !== \"production\" && env !== \"dev\") {\n throw new TypeError('env must be \"production\" or \"dev\"');\n }\n return VANA_ACCOUNT_ORIGINS[env];\n }\n const origin =\n typeof vanaOrigin === \"string\" ? normalizeOrigin(vanaOrigin) : null;\n if (!origin) {\n throw new TypeError(\"vanaOrigin must be an http(s) origin\");\n }\n return origin;\n}\n\n/**\n * Ask Vana whether this page may frame the sheet, alongside framing it, and\n * call `onRefused` only on a definite no. Any failure to ask leaves the ready\n * deadline to decide, so the check can only end the sheet sooner.\n */\nfunction checkClient(url: string, onRefused: () => void): void {\n if (typeof fetch !== \"function\") {\n return;\n }\n fetch(url, { cache: \"no-store\", credentials: \"omit\" })\n .then((response) => (response.ok ? response.json() : null))\n .then((body: unknown) => {\n if (parseClientCheck(body)?.registered === false) {\n onRefused();\n }\n })\n .catch(() => undefined);\n}\n\n/** Alias of {@link open}, for call sites where `open` would be ambiguous. */\nexport const openConnectSheet = open;\n\n/** Namespace form: `connect.open(...)`. */\nexport const connect = { open } as const;\n"],"mappings":"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","names":[]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var protocol_exports = {};
20
+ __export(protocol_exports, {
21
+ CLIENT_CHECK_PATH: () => CLIENT_CHECK_PATH,
22
+ CONNECT_DISPLAYS: () => CONNECT_DISPLAYS,
23
+ DEFAULT_CONNECT_DISPLAY: () => DEFAULT_CONNECT_DISPLAY,
24
+ DEFAULT_VANA_ORIGIN: () => DEFAULT_VANA_ORIGIN,
25
+ MIN_PROTOCOL_VERSION: () => MIN_PROTOCOL_VERSION,
26
+ MIN_SHEET_HEIGHT_PX: () => MIN_SHEET_HEIGHT_PX,
27
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
28
+ SHEET_PATH: () => SHEET_PATH,
29
+ SHEET_QUERY: () => SHEET_QUERY,
30
+ VANA_ACCOUNT_ORIGINS: () => VANA_ACCOUNT_ORIGINS,
31
+ clientCheckUrl: () => clientCheckUrl,
32
+ isRequestId: () => isRequestId,
33
+ normalizeOrigin: () => normalizeOrigin,
34
+ parseClientCheck: () => parseClientCheck,
35
+ parsePartnerMessage: () => parsePartnerMessage,
36
+ parseSheetMessage: () => parseSheetMessage,
37
+ readConnectDisplay: () => readConnectDisplay,
38
+ readConnectRequest: () => readConnectRequest,
39
+ readIncompatibleVersion: () => readIncompatibleVersion,
40
+ sheetUrl: () => sheetUrl,
41
+ vanaConnectCsp: () => vanaConnectCsp
42
+ });
43
+ module.exports = __toCommonJS(protocol_exports);
44
+ const PROTOCOL_VERSION = 1;
45
+ const MIN_PROTOCOL_VERSION = 1;
46
+ const SHEET_PATH = "/embed/connect";
47
+ const CLIENT_CHECK_PATH = "/api/embed/connect/client";
48
+ const SHEET_QUERY = {
49
+ clientId: "client_id",
50
+ display: "display",
51
+ origin: "origin",
52
+ version: "v"
53
+ };
54
+ const CONNECT_DISPLAYS = ["sheet", "fullscreen"];
55
+ const DEFAULT_CONNECT_DISPLAY = "sheet";
56
+ function readConnectDisplay(value) {
57
+ return CONNECT_DISPLAYS.find((known) => known === value) ?? null;
58
+ }
59
+ const VANA_ACCOUNT_ORIGINS = {
60
+ production: "https://account.vana.org",
61
+ dev: "https://account-dev.vana.org"
62
+ };
63
+ const DEFAULT_VANA_ORIGIN = VANA_ACCOUNT_ORIGINS.production;
64
+ const CONNECT_COMPLETIONS = [
65
+ "in_browser",
66
+ "remote_collect"
67
+ ];
68
+ const PROGRESS_PHASES = [
69
+ "collecting",
70
+ "writing"
71
+ ];
72
+ const MIN_SHEET_HEIGHT_PX = 80;
73
+ const REQUEST_STATUSES = [
74
+ "pending",
75
+ "approved",
76
+ "completed",
77
+ "denied",
78
+ "expired"
79
+ ];
80
+ const SHEET_ERROR_CODES = [
81
+ "frame_blocked",
82
+ "incompatible_version",
83
+ "origin_not_registered",
84
+ "personal_server_unsupported",
85
+ "request_unavailable",
86
+ "sheet_failed",
87
+ "vana_unavailable"
88
+ ];
89
+ const HEX_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
90
+ const HEX_SIGNATURE = /^0x[0-9a-fA-F]{130}$/;
91
+ const REQUEST_ID = /^[A-Za-z0-9_-]{1,128}$/;
92
+ const TIMESTAMP = /^[0-9]{10,16}$/;
93
+ function record(value) {
94
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
95
+ }
96
+ function isSupportedVersion(value) {
97
+ return typeof value === "number" && Number.isInteger(value) && value >= MIN_PROTOCOL_VERSION && value <= PROTOCOL_VERSION;
98
+ }
99
+ function isRequestId(value) {
100
+ return typeof value === "string" && REQUEST_ID.test(value);
101
+ }
102
+ function readAuthorization(value) {
103
+ const auth = record(value);
104
+ if (!auth) {
105
+ return null;
106
+ }
107
+ const { appAddress, signature, timestamp } = auth;
108
+ if (typeof appAddress !== "string" || !HEX_ADDRESS.test(appAddress) || typeof signature !== "string" || !HEX_SIGNATURE.test(signature) || typeof timestamp !== "string" || !TIMESTAMP.test(timestamp)) {
109
+ return null;
110
+ }
111
+ return {
112
+ appAddress,
113
+ signature,
114
+ timestamp
115
+ };
116
+ }
117
+ function readConnectRequest(value) {
118
+ const request = record(value);
119
+ if (!request || !isRequestId(request.id)) {
120
+ return null;
121
+ }
122
+ const authorization = readAuthorization(request.authorization);
123
+ return authorization ? { authorization, id: request.id } : null;
124
+ }
125
+ function readIncompatibleVersion(data) {
126
+ const message = record(data);
127
+ if (!message || message.type !== "vana:ready" && message.type !== "vana:error") {
128
+ return null;
129
+ }
130
+ const { v } = message;
131
+ if (typeof v !== "number" || !Number.isInteger(v) || isSupportedVersion(v)) {
132
+ return null;
133
+ }
134
+ return v;
135
+ }
136
+ function parseSheetMessage(data) {
137
+ const message = record(data);
138
+ if (!(message && isSupportedVersion(message.v))) {
139
+ return null;
140
+ }
141
+ const v = message.v;
142
+ switch (message.type) {
143
+ case "vana:ready":
144
+ case "vana:signed-in":
145
+ case "vana:request-needed":
146
+ case "vana:close":
147
+ return { type: message.type, v };
148
+ case "vana:resize": {
149
+ const { height } = message;
150
+ if (typeof height !== "number" || !Number.isFinite(height)) {
151
+ return null;
152
+ }
153
+ return { height, type: "vana:resize", v };
154
+ }
155
+ case "vana:connected": {
156
+ const completion = CONNECT_COMPLETIONS.find(
157
+ (known) => known === message.completion
158
+ );
159
+ return message.status === "approved" && completion ? { completion, status: "approved", type: "vana:connected", v } : null;
160
+ }
161
+ case "vana:progress": {
162
+ const phase = PROGRESS_PHASES.find((known) => known === message.phase);
163
+ return phase ? { phase, type: "vana:progress", v } : null;
164
+ }
165
+ case "vana:error": {
166
+ if (typeof message.code !== "string") {
167
+ return null;
168
+ }
169
+ const code = SHEET_ERROR_CODES.find((known) => known === message.code) ?? "sheet_failed";
170
+ return { code, type: "vana:error", v };
171
+ }
172
+ default:
173
+ return null;
174
+ }
175
+ }
176
+ function parsePartnerMessage(data) {
177
+ const message = record(data);
178
+ if (!(message && isSupportedVersion(message.v))) {
179
+ return null;
180
+ }
181
+ const v = message.v;
182
+ switch (message.type) {
183
+ case "vana:request": {
184
+ const request = readConnectRequest(message.request);
185
+ return request ? { request, type: "vana:request", v } : null;
186
+ }
187
+ case "vana:request-status": {
188
+ const status = REQUEST_STATUSES.find((known) => known === message.status);
189
+ return status ? { status, type: "vana:request-status", v } : null;
190
+ }
191
+ default:
192
+ return null;
193
+ }
194
+ }
195
+ function normalizeOrigin(value) {
196
+ try {
197
+ const url = new URL(value);
198
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
199
+ return null;
200
+ }
201
+ return url.origin;
202
+ } catch {
203
+ return null;
204
+ }
205
+ }
206
+ function clientCheckUrl(input) {
207
+ const url = new URL(CLIENT_CHECK_PATH, input.vanaOrigin);
208
+ url.searchParams.set(SHEET_QUERY.clientId, input.clientId);
209
+ url.searchParams.set(SHEET_QUERY.origin, input.partnerOrigin);
210
+ return url.toString();
211
+ }
212
+ function parseClientCheck(data) {
213
+ const body = record(data);
214
+ return body && typeof body.registered === "boolean" ? { registered: body.registered } : null;
215
+ }
216
+ function sheetUrl(input) {
217
+ const url = new URL(SHEET_PATH, input.vanaOrigin);
218
+ url.searchParams.set(SHEET_QUERY.clientId, input.clientId);
219
+ url.searchParams.set(SHEET_QUERY.origin, input.partnerOrigin);
220
+ url.searchParams.set(SHEET_QUERY.version, String(PROTOCOL_VERSION));
221
+ if (input.display && input.display !== DEFAULT_CONNECT_DISPLAY) {
222
+ url.searchParams.set(SHEET_QUERY.display, input.display);
223
+ }
224
+ return url.toString();
225
+ }
226
+ function vanaConnectCsp(target) {
227
+ const origin = target === "production" || target === "dev" ? VANA_ACCOUNT_ORIGINS[target] : typeof target === "object" && target !== null && typeof target.accountOrigin === "string" ? normalizeOrigin(target.accountOrigin) : null;
228
+ if (!origin) {
229
+ throw new TypeError(
230
+ 'vanaConnectCsp needs "production", "dev" or an http(s) accountOrigin'
231
+ );
232
+ }
233
+ return { "connect-src": [origin], "frame-src": [origin] };
234
+ }
235
+ // Annotate the CommonJS export names for ESM import in node:
236
+ 0 && (module.exports = {
237
+ CLIENT_CHECK_PATH,
238
+ CONNECT_DISPLAYS,
239
+ DEFAULT_CONNECT_DISPLAY,
240
+ DEFAULT_VANA_ORIGIN,
241
+ MIN_PROTOCOL_VERSION,
242
+ MIN_SHEET_HEIGHT_PX,
243
+ PROTOCOL_VERSION,
244
+ SHEET_PATH,
245
+ SHEET_QUERY,
246
+ VANA_ACCOUNT_ORIGINS,
247
+ clientCheckUrl,
248
+ isRequestId,
249
+ normalizeOrigin,
250
+ parseClientCheck,
251
+ parsePartnerMessage,
252
+ parseSheetMessage,
253
+ readConnectDisplay,
254
+ readConnectRequest,
255
+ readIncompatibleVersion,
256
+ sheetUrl,
257
+ vanaConnectCsp
258
+ });
259
+ //# sourceMappingURL=protocol.cjs.map