@verentis/sdk 0.2.2 → 0.2.3
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/dist/index.cjs +103 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -5
- package/dist/index.d.ts +43 -5
- package/dist/index.js +103 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,14 @@ var BridgeAuth = class {
|
|
|
30
30
|
_refreshTimer = null;
|
|
31
31
|
_pendingRefresh = null;
|
|
32
32
|
_messageHandler;
|
|
33
|
+
_requestId = null;
|
|
34
|
+
_refreshReject = null;
|
|
35
|
+
_requestTimer = null;
|
|
36
|
+
_destroyed = false;
|
|
37
|
+
setHostOrigin(origin) {
|
|
38
|
+
if (!origin || origin === "*" || origin === "null") throw new Error("A trusted host origin is required.");
|
|
39
|
+
this.hostOrigin = origin;
|
|
40
|
+
}
|
|
33
41
|
/** Set the initial token received from the host's `verentis:init` message */
|
|
34
42
|
setToken(info) {
|
|
35
43
|
this._token = info.accessToken;
|
|
@@ -44,7 +52,9 @@ var BridgeAuth = class {
|
|
|
44
52
|
return `Bearer ${this._token}`;
|
|
45
53
|
}
|
|
46
54
|
destroy() {
|
|
55
|
+
this._destroyed = true;
|
|
47
56
|
if (this._refreshTimer) clearTimeout(this._refreshTimer);
|
|
57
|
+
this.failRefresh("Authentication has been destroyed.");
|
|
48
58
|
if (typeof window !== "undefined") {
|
|
49
59
|
window.removeEventListener("message", this._messageHandler);
|
|
50
60
|
}
|
|
@@ -54,11 +64,16 @@ var BridgeAuth = class {
|
|
|
54
64
|
}
|
|
55
65
|
_refreshResolve = null;
|
|
56
66
|
requestRefresh() {
|
|
67
|
+
if (this._destroyed || this.hostOrigin === "*")
|
|
68
|
+
return Promise.reject(new Error("Authentication is not initialized."));
|
|
57
69
|
if (this._pendingRefresh) return this._pendingRefresh;
|
|
58
70
|
const requestId = crypto.randomUUID();
|
|
59
|
-
this.
|
|
71
|
+
this._requestId = requestId;
|
|
72
|
+
this._pendingRefresh = new Promise((resolve, reject) => {
|
|
60
73
|
this._refreshResolve = resolve;
|
|
74
|
+
this._refreshReject = reject;
|
|
61
75
|
});
|
|
76
|
+
this._requestTimer = setTimeout(() => this.failRefresh("Token refresh timed out."), 2e4);
|
|
62
77
|
const message = {
|
|
63
78
|
type: "verentis:token:refresh",
|
|
64
79
|
version: 1,
|
|
@@ -68,21 +83,35 @@ var BridgeAuth = class {
|
|
|
68
83
|
return this._pendingRefresh;
|
|
69
84
|
}
|
|
70
85
|
handleMessage(event) {
|
|
71
|
-
if (event.origin !== this.hostOrigin) return;
|
|
86
|
+
if (this._destroyed || event.origin !== this.hostOrigin || event.source !== window.parent) return;
|
|
72
87
|
const data = event.data;
|
|
73
|
-
if (data?.type !== "verentis:token:refreshed") return;
|
|
88
|
+
if (data?.version !== 1 || data?.type !== "verentis:token:refreshed" || !this._requestId || data.requestId !== this._requestId || typeof data.token?.accessToken !== "string" || !Number.isFinite(Date.parse(data.token.expiresAt)) || Date.parse(data.token.expiresAt) <= Date.now() + SKEW_MS) return;
|
|
89
|
+
if (this._requestTimer) clearTimeout(this._requestTimer);
|
|
90
|
+
this._requestTimer = null;
|
|
91
|
+
this._requestId = null;
|
|
74
92
|
this._token = data.token.accessToken;
|
|
75
93
|
this._expiresAt = new Date(data.token.expiresAt).getTime();
|
|
76
94
|
this._pendingRefresh = null;
|
|
77
95
|
this._refreshResolve?.();
|
|
78
96
|
this._refreshResolve = null;
|
|
97
|
+
this._refreshReject = null;
|
|
79
98
|
this.scheduleRefresh();
|
|
80
99
|
}
|
|
81
100
|
scheduleRefresh() {
|
|
82
101
|
if (this._refreshTimer) clearTimeout(this._refreshTimer);
|
|
83
102
|
if (this._expiresAt === 0) return;
|
|
84
103
|
const delay2 = Math.max(0, this._expiresAt - Date.now() - SKEW_MS);
|
|
85
|
-
this._refreshTimer = setTimeout(() => void this.requestRefresh()
|
|
104
|
+
this._refreshTimer = setTimeout(() => void this.requestRefresh().catch(() => {
|
|
105
|
+
}), delay2);
|
|
106
|
+
}
|
|
107
|
+
failRefresh(message) {
|
|
108
|
+
if (this._requestTimer) clearTimeout(this._requestTimer);
|
|
109
|
+
this._requestTimer = null;
|
|
110
|
+
this._requestId = null;
|
|
111
|
+
this._pendingRefresh = null;
|
|
112
|
+
this._refreshReject?.(new Error(message));
|
|
113
|
+
this._refreshReject = null;
|
|
114
|
+
this._refreshResolve = null;
|
|
86
115
|
}
|
|
87
116
|
};
|
|
88
117
|
|
|
@@ -754,8 +783,24 @@ function isHeaderAction(value) {
|
|
|
754
783
|
|
|
755
784
|
// src/transport/bridge.transport.ts
|
|
756
785
|
var Bridge = class {
|
|
786
|
+
constructor(expectedHostOrigin) {
|
|
787
|
+
this.expectedHostOrigin = expectedHostOrigin;
|
|
788
|
+
this._initPromise = new Promise((resolve, reject) => {
|
|
789
|
+
this._initResolve = resolve;
|
|
790
|
+
this._initReject = reject;
|
|
791
|
+
});
|
|
792
|
+
void this._initPromise.catch(() => {
|
|
793
|
+
});
|
|
794
|
+
this._messageHandler = this.handleMessage.bind(this);
|
|
795
|
+
if (typeof window !== "undefined") {
|
|
796
|
+
window.addEventListener("message", this._messageHandler);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
expectedHostOrigin;
|
|
757
800
|
listeners = /* @__PURE__ */ new Map();
|
|
758
801
|
_initResolve;
|
|
802
|
+
_initReject;
|
|
803
|
+
_initTimer;
|
|
759
804
|
_initPromise;
|
|
760
805
|
_hostOrigin = null;
|
|
761
806
|
_messageHandler;
|
|
@@ -763,15 +808,7 @@ var Bridge = class {
|
|
|
763
808
|
sessionId = null;
|
|
764
809
|
actions = /* @__PURE__ */ new Map();
|
|
765
810
|
requests = /* @__PURE__ */ new Set();
|
|
766
|
-
|
|
767
|
-
this._initPromise = new Promise((resolve) => {
|
|
768
|
-
this._initResolve = resolve;
|
|
769
|
-
});
|
|
770
|
-
this._messageHandler = this.handleMessage.bind(this);
|
|
771
|
-
if (typeof window !== "undefined") {
|
|
772
|
-
window.addEventListener("message", this._messageHandler);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
811
|
+
backendRequests = /* @__PURE__ */ new Map();
|
|
775
812
|
/** Post `verentis:ready` to the host to start the handshake */
|
|
776
813
|
sendReady(capabilities) {
|
|
777
814
|
console.info("[Verentis SDK] Sending verentis:ready to host");
|
|
@@ -784,6 +821,12 @@ var Bridge = class {
|
|
|
784
821
|
}
|
|
785
822
|
/** Wait for the host to send `verentis:init` with context and token */
|
|
786
823
|
waitForInit() {
|
|
824
|
+
if (!this._hostOrigin && !this._destroyed && !this._initTimer) {
|
|
825
|
+
this._initTimer = setTimeout(() => {
|
|
826
|
+
this._initReject(new Error("Workspace handshake timed out."));
|
|
827
|
+
this.destroy();
|
|
828
|
+
}, 2e4);
|
|
829
|
+
}
|
|
787
830
|
return this._initPromise;
|
|
788
831
|
}
|
|
789
832
|
/** Get the host origin (available after init) */
|
|
@@ -827,6 +870,28 @@ var Bridge = class {
|
|
|
827
870
|
setDirty(isDirty) {
|
|
828
871
|
this.postToHost({ type: "verentis:dirty", version: 1, isDirty });
|
|
829
872
|
}
|
|
873
|
+
/** The host, not the app, selects the installation, file, branch and permission ceiling. */
|
|
874
|
+
requestBackendCredential() {
|
|
875
|
+
if (this._destroyed || !this.sessionId || !this._hostOrigin)
|
|
876
|
+
return Promise.reject(new Error("An initialized workspace host is required."));
|
|
877
|
+
if (this.backendRequests.size >= 4)
|
|
878
|
+
return Promise.reject(new Error("A backend credential request is already pending."));
|
|
879
|
+
const requestId = crypto.randomUUID();
|
|
880
|
+
return new Promise((resolve, reject) => {
|
|
881
|
+
const timer = setTimeout(() => {
|
|
882
|
+
this.backendRequests.delete(requestId);
|
|
883
|
+
reject(new Error("Backend authorization timed out."));
|
|
884
|
+
}, 2e4);
|
|
885
|
+
this.backendRequests.set(requestId, { resolve, reject, timer });
|
|
886
|
+
try {
|
|
887
|
+
this.postToHost({ type: "verentis:backend:request", version: 1, requestId, sessionId: this.sessionId });
|
|
888
|
+
} catch {
|
|
889
|
+
clearTimeout(timer);
|
|
890
|
+
this.backendRequests.delete(requestId);
|
|
891
|
+
reject(new Error("The workspace host is unavailable."));
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
}
|
|
830
895
|
registerAction(descriptor, handler) {
|
|
831
896
|
if (!this.sessionId || this._destroyed) throw new Error("Header actions require an initialized workspace host.");
|
|
832
897
|
if (!isHeaderAction(descriptor)) throw new Error("Invalid header action.");
|
|
@@ -903,9 +968,17 @@ var Bridge = class {
|
|
|
903
968
|
/** Clean up event listeners */
|
|
904
969
|
destroy() {
|
|
905
970
|
this._destroyed = true;
|
|
971
|
+
clearTimeout(this._initTimer);
|
|
972
|
+
this._initTimer = void 0;
|
|
973
|
+
if (!this._hostOrigin) this._initReject(new Error("Bridge has been destroyed."));
|
|
906
974
|
this.actions.clear();
|
|
907
975
|
this.requests.clear();
|
|
908
976
|
this.listeners.clear();
|
|
977
|
+
for (const pending of this.backendRequests.values()) {
|
|
978
|
+
clearTimeout(pending.timer);
|
|
979
|
+
pending.reject(new Error("Bridge has been destroyed."));
|
|
980
|
+
}
|
|
981
|
+
this.backendRequests.clear();
|
|
909
982
|
if (typeof window !== "undefined") {
|
|
910
983
|
window.removeEventListener("message", this._messageHandler);
|
|
911
984
|
}
|
|
@@ -915,8 +988,12 @@ var Bridge = class {
|
|
|
915
988
|
const data = event.data;
|
|
916
989
|
if (event.source !== window.parent || data?.version !== 1 || typeof data?.type !== "string" || !data.type.startsWith("verentis:")) return;
|
|
917
990
|
if (this._hostOrigin && event.origin !== this._hostOrigin) return;
|
|
991
|
+
if (this.expectedHostOrigin && event.origin !== this.expectedHostOrigin) return;
|
|
918
992
|
if (data.type === "verentis:init") {
|
|
919
993
|
if (this._hostOrigin) return;
|
|
994
|
+
if (!data.context?.workspace?.id || !data.token || event.origin === "null") return;
|
|
995
|
+
clearTimeout(this._initTimer);
|
|
996
|
+
this._initTimer = void 0;
|
|
920
997
|
this._hostOrigin = event.origin;
|
|
921
998
|
this.sessionId = typeof data.sessionId === "string" ? data.sessionId : null;
|
|
922
999
|
console.info("[Verentis SDK] Received verentis:init from host", {
|
|
@@ -927,6 +1004,18 @@ var Bridge = class {
|
|
|
927
1004
|
const initData = data;
|
|
928
1005
|
this._initResolve({ context: initData.context, token: initData.token });
|
|
929
1006
|
}
|
|
1007
|
+
if (data.type === "verentis:backend:response") {
|
|
1008
|
+
if (!this._hostOrigin || data.sessionId !== this.sessionId) return;
|
|
1009
|
+
const pending = this.backendRequests.get(data.requestId);
|
|
1010
|
+
if (!pending) return;
|
|
1011
|
+
clearTimeout(pending.timer);
|
|
1012
|
+
this.backendRequests.delete(data.requestId);
|
|
1013
|
+
const value = data.credential;
|
|
1014
|
+
if (data.error || !value || typeof value.launchCredential !== "string" || value.launchCredential.length > 512 || !value.launchCredential || typeof value.delegationId !== "string" || typeof value.backendClientId !== "string" || !Number.isFinite(Date.parse(value.expiresAt)) || Date.parse(value.expiresAt) <= Date.now())
|
|
1015
|
+
pending.reject(new Error("Backend authorization was denied or is not configured."));
|
|
1016
|
+
else pending.resolve(value);
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
930
1019
|
if (data.type === "verentis:action:invoke") {
|
|
931
1020
|
void this.invokeAction(data);
|
|
932
1021
|
return;
|
|
@@ -1112,7 +1201,7 @@ var VerentisClient = class {
|
|
|
1112
1201
|
this.bridge.sendReady();
|
|
1113
1202
|
const { context, token } = await this.bridge.waitForInit();
|
|
1114
1203
|
const hostOrigin = this.bridge.hostOrigin;
|
|
1115
|
-
|
|
1204
|
+
bridgeAuth.setHostOrigin(hostOrigin);
|
|
1116
1205
|
bridgeAuth.setToken(token);
|
|
1117
1206
|
this.context.setFromAppContext(context);
|
|
1118
1207
|
const http = new HttpTransport(
|