@verentis/sdk 0.2.3 → 0.2.4
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 +24 -0
- package/dist/index.cjs +65 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -5
- package/dist/index.d.ts +60 -5
- package/dist/index.js +65 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,6 +57,30 @@ Release the 0.2 SDK before deploying apps that require `^0.2.0`, together with t
|
|
|
57
57
|
Local sibling-repository development can link the built SDK; published applications must resolve
|
|
58
58
|
the released SDK from npm.
|
|
59
59
|
|
|
60
|
+
### WOPI editor launch
|
|
61
|
+
|
|
62
|
+
Signed WOPI applications use `Bridge.requestWopiLaunch()` after `waitForInit()`.
|
|
63
|
+
The authenticated workspace parent chooses the installed editor, document,
|
|
64
|
+
branch and action; the child cannot request arbitrary document identities.
|
|
65
|
+
The returned token is scoped to WOPI and is not a normal workspace API token.
|
|
66
|
+
Submit it using a form POST to the validated discovery action, never a URL.
|
|
67
|
+
|
|
68
|
+
`requestWopiSave(generation)`, `getWopiStatus()` and `closeWopiSession()` use the
|
|
69
|
+
same exact-parent, origin/session/request-correlated bridge. A matching durable
|
|
70
|
+
save receipt—not a CODE postMessage alone—is required before clearing dirty state.
|
|
71
|
+
When status reports `continuationAvailable`, `requestWopiContinuation()` asks
|
|
72
|
+
the parent to bind the target of a completed server-owned Save As operation.
|
|
73
|
+
It accepts no document or acquisition IDs. Its `WopiContinuation` result contains
|
|
74
|
+
only document metadata and the persisted target `accessTokenTtl`, not a token,
|
|
75
|
+
action URL or private control credential. The parent obtains the exact existing
|
|
76
|
+
control IDs from authenticated `/derived`; it does not reconstruct a launch,
|
|
77
|
+
including after signing-key rotation. Keep the initial launch/form separate from
|
|
78
|
+
active document metadata, retain CODE's iframe and existing token, and preserve
|
|
79
|
+
unverified edits until a matching target save receipt arrives.
|
|
80
|
+
These methods do not replace `requestBackendCredential()` or ordinary OAuth
|
|
81
|
+
authentication for apps that need workspace APIs. WOPI-only packages need no
|
|
82
|
+
per-editor OAuth client secret.
|
|
83
|
+
|
|
60
84
|
### Standalone mode (local development)
|
|
61
85
|
|
|
62
86
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -809,6 +809,7 @@ var Bridge = class {
|
|
|
809
809
|
actions = /* @__PURE__ */ new Map();
|
|
810
810
|
requests = /* @__PURE__ */ new Set();
|
|
811
811
|
backendRequests = /* @__PURE__ */ new Map();
|
|
812
|
+
wopiRequests = /* @__PURE__ */ new Map();
|
|
812
813
|
/** Post `verentis:ready` to the host to start the handshake */
|
|
813
814
|
sendReady(capabilities) {
|
|
814
815
|
console.info("[Verentis SDK] Sending verentis:ready to host");
|
|
@@ -817,7 +818,7 @@ var Bridge = class {
|
|
|
817
818
|
version: 1,
|
|
818
819
|
capabilities
|
|
819
820
|
};
|
|
820
|
-
window.parent.postMessage(message, "*");
|
|
821
|
+
window.parent.postMessage(message, this.expectedHostOrigin ?? "*");
|
|
821
822
|
}
|
|
822
823
|
/** Wait for the host to send `verentis:init` with context and token */
|
|
823
824
|
waitForInit() {
|
|
@@ -892,6 +893,53 @@ var Bridge = class {
|
|
|
892
893
|
}
|
|
893
894
|
});
|
|
894
895
|
}
|
|
896
|
+
/** The trusted parent selects the approved installation, document and action. */
|
|
897
|
+
requestWopiLaunch() {
|
|
898
|
+
return this.requestWopi("launch");
|
|
899
|
+
}
|
|
900
|
+
/** Recover only the target selected by a completed server-owned derived operation. */
|
|
901
|
+
requestWopiContinuation() {
|
|
902
|
+
return this.requestWopi("continue");
|
|
903
|
+
}
|
|
904
|
+
getWopiStatus() {
|
|
905
|
+
return this.requestWopi("status");
|
|
906
|
+
}
|
|
907
|
+
requestWopiSave(generation) {
|
|
908
|
+
if (!Number.isSafeInteger(generation) || generation < 0)
|
|
909
|
+
return Promise.reject(new Error("Invalid edit generation."));
|
|
910
|
+
return this.requestWopi("save", generation);
|
|
911
|
+
}
|
|
912
|
+
closeWopiSession() {
|
|
913
|
+
return this.requestWopi("close");
|
|
914
|
+
}
|
|
915
|
+
requestWopi(operation, generation) {
|
|
916
|
+
if (this._destroyed || !this.sessionId || !this._hostOrigin)
|
|
917
|
+
return Promise.reject(new Error("An initialized workspace host is required."));
|
|
918
|
+
if (this.wopiRequests.size >= 4)
|
|
919
|
+
return Promise.reject(new Error("Too many collaboration requests."));
|
|
920
|
+
const requestId = crypto.randomUUID();
|
|
921
|
+
return new Promise((resolve, reject) => {
|
|
922
|
+
const timer = setTimeout(() => {
|
|
923
|
+
this.wopiRequests.delete(requestId);
|
|
924
|
+
reject(new Error("Collaboration authorization timed out."));
|
|
925
|
+
}, 3e4);
|
|
926
|
+
this.wopiRequests.set(requestId, { resolve, reject, timer });
|
|
927
|
+
try {
|
|
928
|
+
this.postToHost({
|
|
929
|
+
type: "verentis:wopi:request",
|
|
930
|
+
version: 1,
|
|
931
|
+
requestId,
|
|
932
|
+
sessionId: this.sessionId,
|
|
933
|
+
operation,
|
|
934
|
+
...generation === void 0 ? {} : { generation }
|
|
935
|
+
});
|
|
936
|
+
} catch {
|
|
937
|
+
clearTimeout(timer);
|
|
938
|
+
this.wopiRequests.delete(requestId);
|
|
939
|
+
reject(new Error("The workspace host is unavailable."));
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
}
|
|
895
943
|
registerAction(descriptor, handler) {
|
|
896
944
|
if (!this.sessionId || this._destroyed) throw new Error("Header actions require an initialized workspace host.");
|
|
897
945
|
if (!isHeaderAction(descriptor)) throw new Error("Invalid header action.");
|
|
@@ -979,6 +1027,11 @@ var Bridge = class {
|
|
|
979
1027
|
pending.reject(new Error("Bridge has been destroyed."));
|
|
980
1028
|
}
|
|
981
1029
|
this.backendRequests.clear();
|
|
1030
|
+
for (const pending of this.wopiRequests.values()) {
|
|
1031
|
+
clearTimeout(pending.timer);
|
|
1032
|
+
pending.reject(new Error("Bridge has been destroyed."));
|
|
1033
|
+
}
|
|
1034
|
+
this.wopiRequests.clear();
|
|
982
1035
|
if (typeof window !== "undefined") {
|
|
983
1036
|
window.removeEventListener("message", this._messageHandler);
|
|
984
1037
|
}
|
|
@@ -1016,6 +1069,17 @@ var Bridge = class {
|
|
|
1016
1069
|
else pending.resolve(value);
|
|
1017
1070
|
return;
|
|
1018
1071
|
}
|
|
1072
|
+
if (data.type === "verentis:wopi:response") {
|
|
1073
|
+
if (!this._hostOrigin || data.sessionId !== this.sessionId) return;
|
|
1074
|
+
const pending = this.wopiRequests.get(data.requestId);
|
|
1075
|
+
if (!pending) return;
|
|
1076
|
+
clearTimeout(pending.timer);
|
|
1077
|
+
this.wopiRequests.delete(data.requestId);
|
|
1078
|
+
if (data.error || !Object.hasOwn(data, "result"))
|
|
1079
|
+
pending.reject(new Error("Collaboration authorization is unavailable."));
|
|
1080
|
+
else pending.resolve(data.result);
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1019
1083
|
if (data.type === "verentis:action:invoke") {
|
|
1020
1084
|
void this.invokeAction(data);
|
|
1021
1085
|
return;
|