@floegence/redevplugin-ui 0.4.3 → 0.6.5
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/bridge-capability.d.ts +7 -0
- package/dist/bridge-capability.js +8 -0
- package/dist/capability-client.d.ts +27 -7
- package/dist/capability-client.js +25 -19
- package/dist/contracts.gen.d.ts +261 -117
- package/dist/contracts.gen.js +286 -118
- package/dist/error-codes.gen.d.ts +4 -0
- package/dist/error-codes.gen.js +218 -0
- package/dist/errors.d.ts +22 -4
- package/dist/errors.js +42 -60
- package/dist/http.d.ts +20 -8
- package/dist/http.js +187 -15
- package/dist/local-import.d.ts +6 -11
- package/dist/local-import.js +64 -13
- package/dist/opaque-surface-policy.gen.d.ts +4 -3
- package/dist/opaque-surface-policy.gen.js +4 -2
- package/dist/openapi.gen.d.ts +3991 -0
- package/dist/openapi.gen.js +5 -0
- package/dist/platform.d.ts +150 -529
- package/dist/platform.js +303 -102
- package/dist/plugin.d.ts +2 -2
- package/dist/surface-scope.d.ts +3 -2
- package/dist/surface-scope.js +54 -13
- package/dist/surface.d.ts +64 -31
- package/dist/surface.js +1466 -203
- package/dist/trusted-parent.d.ts +3 -3
- package/dist/trusted-parent.js +1 -1
- package/dist/ui-patch-validator.d.ts +67 -0
- package/dist/ui-patch-validator.js +125 -0
- package/dist/ui-reconciler.d.ts +10 -0
- package/dist/ui-reconciler.js +283 -0
- package/package.json +4 -1
package/dist/platform.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { defaultFetch,
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { assertMutationDispatchable, defaultFetch, dispatchMutationRequest, dispatchQueryRequest, readMutationPlatformResponse, readPlatformResponse, trimTrailingSlash, } from "./http.js";
|
|
2
|
+
import { PluginBridgeError, PluginMutationLifecycleError, PluginTransportError, pluginMutationOutcome, } from "./errors.js";
|
|
3
|
+
import { createReDevPluginSurfaceTransport, openPluginSurfaceInSlot, } from "./surface.js";
|
|
4
|
+
import { defaultPluginSurfaceScope, disposePluginSurfaceScope, invalidatePluginSurfaceScope, } from "./surface-scope.js";
|
|
5
|
+
function toPluginSurfaceHostBootstrap(value) {
|
|
4
6
|
return {
|
|
5
7
|
pluginId: value.plugin_id,
|
|
6
8
|
pluginInstanceId: value.plugin_instance_id,
|
|
@@ -13,7 +15,7 @@ export function toPluginSurfaceHostBootstrap(value) {
|
|
|
13
15
|
entrySHA256: value.entry_sha256,
|
|
14
16
|
assetTicket: value.asset_ticket,
|
|
15
17
|
assetSessionNonce: value.asset_session_nonce,
|
|
16
|
-
|
|
18
|
+
managementRevision: value.management_revision,
|
|
17
19
|
revokeEpoch: value.revoke_epoch,
|
|
18
20
|
runtimeGenerationId: value.runtime_generation_id,
|
|
19
21
|
};
|
|
@@ -22,121 +24,320 @@ export class PluginPlatformClient {
|
|
|
22
24
|
#fetch;
|
|
23
25
|
#apiBaseURL;
|
|
24
26
|
#surfaceScope;
|
|
27
|
+
#surfaceTransport;
|
|
28
|
+
#onMutationOutcomeUnknown;
|
|
25
29
|
constructor(options = {}) {
|
|
26
30
|
this.#fetch = options.fetch ?? defaultFetch();
|
|
27
31
|
this.#apiBaseURL = trimTrailingSlash(options.apiBaseURL ?? "");
|
|
28
32
|
this.#surfaceScope = options.surfaceScope ?? defaultPluginSurfaceScope;
|
|
33
|
+
this.#surfaceTransport = options.surfaceTransport;
|
|
34
|
+
this.#onMutationOutcomeUnknown = options.onMutationOutcomeUnknown;
|
|
29
35
|
}
|
|
30
|
-
catalog() { return this.#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
catalog(options = {}) { return this.#requestQuery("/_redevplugin/api/plugins/catalog/query", {}, options); }
|
|
37
|
+
features(options = {}) { return this.#requestQuery("/_redevplugin/api/plugins/features/query", {}, options); }
|
|
38
|
+
getCompatibility(options = {}) { return this.#requestQuery("/_redevplugin/api/plugins/platform/compatibility/query", {}, options); }
|
|
39
|
+
installReleaseRef(request, options = {}) {
|
|
40
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/install-release-ref", request, options);
|
|
41
|
+
}
|
|
42
|
+
updateReleaseRef(request, options = {}) {
|
|
43
|
+
return this.#mutatePlugin("/_redevplugin/api/plugins/update-release-ref", request, options);
|
|
44
|
+
}
|
|
45
|
+
downgradePlugin(request, options = {}) {
|
|
46
|
+
return this.#mutatePlugin("/_redevplugin/api/plugins/downgrade", request, options);
|
|
47
|
+
}
|
|
48
|
+
enablePlugin(request, options = {}) {
|
|
49
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/enable", request, options);
|
|
50
|
+
}
|
|
51
|
+
disablePlugin(request, options = {}) {
|
|
52
|
+
return this.#mutatePlugin("/_redevplugin/api/plugins/disable", request, options);
|
|
53
|
+
}
|
|
54
|
+
uninstallPlugin(request, options = {}) {
|
|
55
|
+
return this.#mutatePlugin("/_redevplugin/api/plugins/uninstall", request, options);
|
|
56
|
+
}
|
|
57
|
+
#openSurface(request) {
|
|
58
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/surfaces/open", request, {});
|
|
59
|
+
}
|
|
60
|
+
openSurfaceInSlot(slot, request, options = {}) {
|
|
61
|
+
const { signal, ...hostOptions } = options;
|
|
62
|
+
const pluginInstanceId = typeof request.plugin_instance_id === "string"
|
|
63
|
+
? request.plugin_instance_id.trim()
|
|
64
|
+
: "";
|
|
65
|
+
if (!pluginInstanceId) {
|
|
66
|
+
return Promise.reject(new PluginBridgeError("PLUGIN_INVALID_REQUEST", "Plugin surface opening requires a canonical plugin instance identifier"));
|
|
67
|
+
}
|
|
68
|
+
if (signal?.aborted) {
|
|
69
|
+
return Promise.reject(new PluginTransportError("Plugin surface opening was aborted before dispatch", signal.reason, "not_committed"));
|
|
70
|
+
}
|
|
71
|
+
const surfaceTransport = this.#surfaceTransport ??= createReDevPluginSurfaceTransport({
|
|
72
|
+
fetch: this.#fetch,
|
|
73
|
+
apiBaseURL: surfaceTransportAPIBaseURL(this.#apiBaseURL),
|
|
74
|
+
});
|
|
75
|
+
const canonicalRequest = {
|
|
76
|
+
...request,
|
|
77
|
+
plugin_instance_id: pluginInstanceId,
|
|
78
|
+
};
|
|
79
|
+
return openPluginSurfaceInSlot(slot, {
|
|
80
|
+
pluginInstanceId,
|
|
81
|
+
surfaceScope: this.#surfaceScope,
|
|
82
|
+
signal,
|
|
83
|
+
abortError: () => new PluginTransportError("Plugin surface opening was aborted after dispatch", signal?.reason, "unknown"),
|
|
84
|
+
open: () => this.#openSurface(canonicalRequest).then((result) => ({
|
|
85
|
+
...hostOptions,
|
|
86
|
+
bootstrap: toPluginSurfaceHostBootstrap(result),
|
|
87
|
+
hostTransport: surfaceTransport,
|
|
88
|
+
surfaceScope: this.#surfaceScope,
|
|
89
|
+
})),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async revokeSessionScope(options = {}) {
|
|
93
|
+
let result;
|
|
94
|
+
try {
|
|
95
|
+
const raw = await this.#requestMutation("POST", "/_redevplugin/api/plugins/session/revoke-scope", {}, options);
|
|
96
|
+
if (!isPluginSessionScopeRevokeResult(raw)) {
|
|
97
|
+
throw new PluginTransportError("Plugin session scope revocation returned an invalid result", new PluginBridgeError("PLUGIN_CONTRACT_MISMATCH", "Invalid session scope revocation result"), "unknown");
|
|
98
|
+
}
|
|
99
|
+
result = raw;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
await this.#handleSessionRevokeFailure(error);
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
await invalidatePluginSurfaceScope(this.#surfaceScope);
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
startRuntime(request, options = {}) {
|
|
109
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/runtime/start", request, options);
|
|
110
|
+
}
|
|
111
|
+
stopRuntime(options = {}) {
|
|
112
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/runtime/stop", {}, options);
|
|
113
|
+
}
|
|
114
|
+
refreshEnabledRuntimeState(options = {}) {
|
|
115
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/runtime/refresh-enabled", {}, options);
|
|
116
|
+
}
|
|
117
|
+
runtimeHealth(options = {}) { return this.#requestQuery("/_redevplugin/api/plugins/runtime/health/query", {}, options); }
|
|
118
|
+
getSettingsSchema(pluginInstanceId, scope, options = {}) {
|
|
119
|
+
return this.#requestQuery(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings/schema/query`, { scope }, options);
|
|
120
|
+
}
|
|
121
|
+
getSettings(pluginInstanceId, scope, options = {}) {
|
|
122
|
+
return this.#requestQuery(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings/query`, { scope }, options);
|
|
123
|
+
}
|
|
124
|
+
patchSettings(pluginInstanceId, request, options = {}) {
|
|
125
|
+
return this.#mutatePluginAt("PATCH", `/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings`, pluginInstanceId, request, options);
|
|
126
|
+
}
|
|
127
|
+
listOperations(options = {}, requestOptions = {}) {
|
|
128
|
+
return this.#requestQuery("/_redevplugin/api/plugins/operations/query", options, requestOptions);
|
|
129
|
+
}
|
|
130
|
+
getOperation(operationId, options = {}) {
|
|
131
|
+
return this.#requestQuery(`/_redevplugin/api/plugins/operations/${encodeURIComponent(operationId)}/query`, {}, options);
|
|
132
|
+
}
|
|
133
|
+
cancelOperation(operationId, reason, options = {}) {
|
|
134
|
+
return this.#requestMutation("POST", `/_redevplugin/api/plugins/operations/${encodeURIComponent(operationId)}/cancel`, reason ? { reason } : {}, options);
|
|
135
|
+
}
|
|
136
|
+
listIntents(options = {}, requestOptions = {}) {
|
|
137
|
+
return this.#requestQuery("/_redevplugin/api/plugins/intents/query", options, requestOptions);
|
|
138
|
+
}
|
|
139
|
+
invokeIntent(request, options = {}) {
|
|
140
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/intents/invoke", request, options);
|
|
141
|
+
}
|
|
142
|
+
exportData(request, options = {}) {
|
|
143
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/data/export", request, options);
|
|
144
|
+
}
|
|
145
|
+
deleteDataExport(request, options = {}) {
|
|
146
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/data/export/delete", request, options);
|
|
147
|
+
}
|
|
148
|
+
importData(request, options = {}) {
|
|
149
|
+
return this.#mutatePluginAt("POST", "/_redevplugin/api/plugins/data/import", request.plugin_instance_id, request, options);
|
|
150
|
+
}
|
|
151
|
+
listRetainedData(options = {}, requestOptions = {}) {
|
|
152
|
+
return this.#requestQuery("/_redevplugin/api/plugins/retained-data/query", options, requestOptions);
|
|
153
|
+
}
|
|
154
|
+
deleteRetainedData(request, options = {}) {
|
|
155
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/retained-data/delete", request, options);
|
|
156
|
+
}
|
|
157
|
+
bindRetainedData(request, options = {}) {
|
|
158
|
+
return this.#mutatePluginAt("POST", "/_redevplugin/api/plugins/retained-data/bind", request.target_plugin_instance_id, request, options);
|
|
159
|
+
}
|
|
160
|
+
cleanupExpiredRetainedData(request = {}, options = {}) {
|
|
161
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/retained-data/cleanup-expired", request, options);
|
|
162
|
+
}
|
|
163
|
+
listPermissions(options = {}, requestOptions = {}) {
|
|
164
|
+
return this.#requestQuery("/_redevplugin/api/plugins/permissions/query", options, requestOptions);
|
|
165
|
+
}
|
|
166
|
+
grantPermission(request, options = {}) {
|
|
167
|
+
return this.#mutatePlugin("/_redevplugin/api/plugins/permissions/grant", request, options);
|
|
168
|
+
}
|
|
169
|
+
revokePermission(request, options = {}) {
|
|
170
|
+
return this.#mutatePlugin("/_redevplugin/api/plugins/permissions/revoke", request, options);
|
|
171
|
+
}
|
|
172
|
+
listSecurityPolicies(options = {}) {
|
|
173
|
+
return this.#requestQuery("/_redevplugin/api/plugins/security-policies/query", {}, options);
|
|
174
|
+
}
|
|
175
|
+
getSecurityPolicy(pluginInstanceId, options = {}) {
|
|
176
|
+
return this.#requestQuery(`/_redevplugin/api/plugins/security-policies/${encodeURIComponent(pluginInstanceId)}/query`, {}, options);
|
|
177
|
+
}
|
|
178
|
+
putSecurityPolicy(pluginInstanceId, request, options = {}) {
|
|
179
|
+
return this.#mutatePluginAt("PUT", `/_redevplugin/api/plugins/security-policies/${encodeURIComponent(pluginInstanceId)}`, pluginInstanceId, request, options);
|
|
180
|
+
}
|
|
181
|
+
deleteSecurityPolicy(pluginInstanceId, request, options = {}) {
|
|
182
|
+
return this.#mutatePluginAt("DELETE", `/_redevplugin/api/plugins/security-policies/${encodeURIComponent(pluginInstanceId)}`, pluginInstanceId, request, options);
|
|
183
|
+
}
|
|
184
|
+
bindSecret(request, options = {}) {
|
|
185
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/secrets/bind", request, options);
|
|
186
|
+
}
|
|
187
|
+
testSecret(request, options = {}) {
|
|
188
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/secrets/test", request, options);
|
|
189
|
+
}
|
|
190
|
+
deleteSecret(request, options = {}) {
|
|
191
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/secrets/delete", request, options);
|
|
192
|
+
}
|
|
193
|
+
listDiagnosticEvents(options = {}, requestOptions = {}) {
|
|
194
|
+
return this.#requestQuery("/_redevplugin/api/plugins/diagnostics/query", options, requestOptions);
|
|
195
|
+
}
|
|
196
|
+
#mutatePlugin(path, request, options) {
|
|
197
|
+
return this.#mutatePluginAt("POST", path, request.plugin_instance_id, request, options);
|
|
198
|
+
}
|
|
199
|
+
async #mutatePluginAt(method, path, pluginInstanceId, request, options) {
|
|
200
|
+
let result;
|
|
40
201
|
try {
|
|
41
|
-
|
|
202
|
+
result = await this.#requestMutation(method, path, request, options);
|
|
42
203
|
}
|
|
43
|
-
|
|
44
|
-
|
|
204
|
+
catch (error) {
|
|
205
|
+
await this.#handleMutationFailure(error, pluginInstanceId, "Plugin mutation and local surface teardown failed");
|
|
206
|
+
throw error;
|
|
45
207
|
}
|
|
208
|
+
await disposePluginSurfaceScope(this.#surfaceScope, pluginInstanceId);
|
|
209
|
+
return result;
|
|
46
210
|
}
|
|
47
|
-
|
|
48
|
-
|
|
211
|
+
async #handleMutationFailure(error, pluginInstanceId, message) {
|
|
212
|
+
const outcome = pluginMutationOutcome(error);
|
|
213
|
+
if (outcome === "not_committed" || outcome === undefined)
|
|
214
|
+
return;
|
|
215
|
+
const lifecycleErrors = [];
|
|
49
216
|
try {
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
listPermissions(pluginInstanceId, activeOnly) {
|
|
95
|
-
const params = new URLSearchParams();
|
|
96
|
-
if (pluginInstanceId)
|
|
97
|
-
params.set("plugin_instance_id", pluginInstanceId);
|
|
98
|
-
if (activeOnly != null)
|
|
99
|
-
params.set("active_only", activeOnly ? "true" : "false");
|
|
100
|
-
const query = params.toString();
|
|
101
|
-
return this.#getJSON(`/_redevplugin/api/plugins/permissions${query ? `?${query}` : ""}`);
|
|
102
|
-
}
|
|
103
|
-
grantPermission(request) { return this.#postJSON("/_redevplugin/api/plugins/permissions/grant", request); }
|
|
104
|
-
revokePermission(request) { return this.#postJSON("/_redevplugin/api/plugins/permissions/revoke", request); }
|
|
105
|
-
bindSecret(request) { return this.#postJSON("/_redevplugin/api/plugins/secrets/bind", request); }
|
|
106
|
-
testSecret(request) { return this.#postJSON("/_redevplugin/api/plugins/secrets/test", request); }
|
|
107
|
-
deleteSecret(request) { return this.#postJSON("/_redevplugin/api/plugins/secrets/delete", request); }
|
|
108
|
-
listAuditEvents(options = {}) { return this.#getJSON(`/_redevplugin/api/plugins/audit${queryString(options)}`); }
|
|
109
|
-
listDiagnosticEvents(options = {}) { return this.#getJSON(`/_redevplugin/api/plugins/diagnostics${queryString(options)}`); }
|
|
110
|
-
#getJSON(path) { return this.#requestJSON("GET", path); }
|
|
111
|
-
#postJSON(path, body) { return this.#requestJSON("POST", path, body); }
|
|
112
|
-
#patchJSON(path, body) { return this.#requestJSON("PATCH", path, body); }
|
|
113
|
-
async #mutatePlugin(path, request) {
|
|
217
|
+
await disposePluginSurfaceScope(this.#surfaceScope, pluginInstanceId);
|
|
218
|
+
}
|
|
219
|
+
catch (caught) {
|
|
220
|
+
lifecycleErrors.push(caught);
|
|
221
|
+
}
|
|
222
|
+
if (outcome === "unknown") {
|
|
223
|
+
try {
|
|
224
|
+
this.#onMutationOutcomeUnknown?.(pluginInstanceId);
|
|
225
|
+
}
|
|
226
|
+
catch (caught) {
|
|
227
|
+
lifecycleErrors.push(caught);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (lifecycleErrors.length > 0)
|
|
231
|
+
throw new PluginMutationLifecycleError(message, error, lifecycleErrors);
|
|
232
|
+
}
|
|
233
|
+
async #handleSessionRevokeFailure(error) {
|
|
234
|
+
const outcome = pluginMutationOutcome(error);
|
|
235
|
+
if (outcome === "not_committed")
|
|
236
|
+
return;
|
|
237
|
+
const lifecycleErrors = [];
|
|
238
|
+
if (outcome === "committed" || outcome === "unknown") {
|
|
239
|
+
try {
|
|
240
|
+
await invalidatePluginSurfaceScope(this.#surfaceScope);
|
|
241
|
+
}
|
|
242
|
+
catch (caught) {
|
|
243
|
+
lifecycleErrors.push(caught);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (outcome === "unknown") {
|
|
247
|
+
try {
|
|
248
|
+
this.#onMutationOutcomeUnknown?.();
|
|
249
|
+
}
|
|
250
|
+
catch (caught) {
|
|
251
|
+
lifecycleErrors.push(caught);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (lifecycleErrors.length > 0) {
|
|
255
|
+
throw new PluginMutationLifecycleError("Plugin session scope revocation and local invalidation failed", error, lifecycleErrors);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async #requestQuery(path, body, options) {
|
|
259
|
+
const operation = `POST ${path}`;
|
|
260
|
+
let encodedBody;
|
|
114
261
|
try {
|
|
115
|
-
|
|
262
|
+
encodedBody = JSON.stringify(body);
|
|
116
263
|
}
|
|
117
|
-
|
|
118
|
-
|
|
264
|
+
catch (cause) {
|
|
265
|
+
throw new PluginTransportError(`Plugin platform query body serialization failed for ${operation}`, cause);
|
|
119
266
|
}
|
|
267
|
+
const response = await dispatchQueryRequest(this.#fetch, this.#apiBaseURL + path, {
|
|
268
|
+
method: "POST",
|
|
269
|
+
headers: { "Accept": "application/json", "Content-Type": "application/json" },
|
|
270
|
+
body: encodedBody,
|
|
271
|
+
credentials: "same-origin",
|
|
272
|
+
signal: options.signal,
|
|
273
|
+
}, operation);
|
|
274
|
+
return readPlatformResponse(response);
|
|
120
275
|
}
|
|
121
|
-
async #
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
276
|
+
async #requestMutation(method, path, body, options) {
|
|
277
|
+
const operation = `${method} ${path}`;
|
|
278
|
+
assertMutationDispatchable(options.signal, operation);
|
|
279
|
+
const headers = {
|
|
280
|
+
"Accept": "application/json",
|
|
281
|
+
"Content-Type": "application/json",
|
|
282
|
+
};
|
|
283
|
+
let encodedBody;
|
|
284
|
+
try {
|
|
285
|
+
encodedBody = JSON.stringify(body);
|
|
286
|
+
}
|
|
287
|
+
catch (cause) {
|
|
288
|
+
throw new PluginTransportError(`Plugin platform request body serialization failed for ${operation}`, cause, "not_committed");
|
|
289
|
+
}
|
|
290
|
+
const response = await dispatchMutationRequest(this.#fetch, this.#apiBaseURL + path, {
|
|
126
291
|
method,
|
|
127
292
|
headers,
|
|
128
|
-
body:
|
|
293
|
+
body: encodedBody,
|
|
129
294
|
credentials: "same-origin",
|
|
130
|
-
|
|
131
|
-
|
|
295
|
+
signal: options.signal,
|
|
296
|
+
}, operation);
|
|
297
|
+
return readMutationPlatformResponse(response);
|
|
132
298
|
}
|
|
133
299
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
300
|
+
const sessionScopeCountKeys = [
|
|
301
|
+
"surfaces",
|
|
302
|
+
"asset_tickets",
|
|
303
|
+
"asset_sessions",
|
|
304
|
+
"plugin_gateway_tokens",
|
|
305
|
+
"confirmation_tokens",
|
|
306
|
+
"stream_tickets",
|
|
307
|
+
"handle_grants",
|
|
308
|
+
"confirmations",
|
|
309
|
+
"operations",
|
|
310
|
+
"streams",
|
|
311
|
+
"runtime_executions",
|
|
312
|
+
"active_network_requests",
|
|
313
|
+
"sockets",
|
|
314
|
+
"network_streams",
|
|
315
|
+
"storage_hostcalls",
|
|
316
|
+
];
|
|
317
|
+
function isPluginSessionScopeRevokeResult(value) {
|
|
318
|
+
if (!isExactRecord(value, ["state", "fenced", "complete", "counts"]) ||
|
|
319
|
+
value.state !== "complete" || value.fenced !== true || value.complete !== true)
|
|
320
|
+
return false;
|
|
321
|
+
const counts = value.counts;
|
|
322
|
+
if (!isExactRecord(counts, sessionScopeCountKeys))
|
|
323
|
+
return false;
|
|
324
|
+
return sessionScopeCountKeys.every((key) => Number.isSafeInteger(counts[key]) && Number(counts[key]) >= 0);
|
|
325
|
+
}
|
|
326
|
+
function isExactRecord(value, keys) {
|
|
327
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
328
|
+
return false;
|
|
329
|
+
const actual = Object.keys(value).sort();
|
|
330
|
+
const expected = [...keys].sort();
|
|
331
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
332
|
+
}
|
|
333
|
+
function surfaceTransportAPIBaseURL(value) {
|
|
334
|
+
if (!value || value.startsWith("/"))
|
|
335
|
+
return value;
|
|
336
|
+
const parsed = new URL(value);
|
|
337
|
+
const currentOrigin = globalThis.location?.origin;
|
|
338
|
+
if (!currentOrigin || currentOrigin === "null" || parsed.origin !== currentOrigin ||
|
|
339
|
+
parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") {
|
|
340
|
+
throw new TypeError("Plugin surface transport apiBaseURL must be same-origin");
|
|
341
|
+
}
|
|
342
|
+
return trimTrailingSlash(parsed.pathname);
|
|
142
343
|
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { PluginBridgeClient } from "./surface.js";
|
|
2
2
|
export { PluginBridgeError } from "./errors.js";
|
|
3
3
|
export { callCapabilityOperation, callCapabilityStream, callCapabilitySync, isCapabilityBusinessError, } from "./capability-client.js";
|
|
4
|
-
export type { BridgeLifecycleEvent, MessagePortLike, PluginBridgeClientOptions, PluginCanvasAccessibilityState, PluginCanvasInputEvent, PluginCanvasKeyEvent, PluginCanvasPointerEvent, PluginCanvasResizeEvent, PluginCanvasSurface, PluginJSONObject, PluginJSONValue, PluginMethodResult, PluginStreamTerminalStatus, PluginUIActionEvent, PluginUIAttributeValue, PluginUIVNode, } from "./surface.js";
|
|
5
|
-
export type { PluginCapabilitySchema, PluginCapabilityStreamEvent, PluginCapabilityStreamReadResult, PluginOperation, PluginStream, } from "./capability-client.js";
|
|
4
|
+
export type { BridgeLifecycleEvent, MessagePortLike, PluginBridgeClientOptions, PluginBridgeRequestOptions, PluginCanvasAccessibilityState, PluginCanvasInputEvent, PluginCanvasKeyEvent, PluginCanvasPointerEvent, PluginCanvasResizeEvent, PluginCanvasSurface, PluginJSONObject, PluginJSONValue, PluginMethodResult, PluginStreamTerminalStatus, PluginUIActionEvent, PluginUIAttributeValue, PluginUIElementVNode, PluginUIPatchOperation, PluginUIVNode, } from "./surface.js";
|
|
5
|
+
export type { PluginCapabilitySchema, PluginCapabilityEffect, PluginCapabilityOperationContract, PluginCapabilityStreamContract, PluginCapabilitySyncContract, PluginCapabilityStreamEvent, PluginCapabilityStreamReadResult, PluginOperation, PluginStream, } from "./capability-client.js";
|
package/dist/surface-scope.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export declare class PluginSurfaceScope {
|
|
|
4
4
|
}
|
|
5
5
|
export declare function createPluginSurfaceScope(): PluginSurfaceScope;
|
|
6
6
|
export declare const defaultPluginSurfaceScope: PluginSurfaceScope;
|
|
7
|
-
export declare function registerPluginSurface(scope: PluginSurfaceScope, pluginInstanceId: string, dispose: () => void): () => void;
|
|
8
|
-
export declare function disposePluginSurfaceScope(scope: PluginSurfaceScope, pluginInstanceId?: string): void
|
|
7
|
+
export declare function registerPluginSurface(scope: PluginSurfaceScope, pluginInstanceId: string, dispose: () => Promise<void> | void, invalidate: () => Promise<void> | void): () => void;
|
|
8
|
+
export declare function disposePluginSurfaceScope(scope: PluginSurfaceScope, pluginInstanceId?: string): Promise<void>;
|
|
9
|
+
export declare function invalidatePluginSurfaceScope(scope: PluginSurfaceScope): Promise<void>;
|
package/dist/surface-scope.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
const
|
|
1
|
+
const scopes = new WeakMap();
|
|
2
|
+
function canonicalPluginInstanceId(pluginInstanceId) {
|
|
3
|
+
const canonical = typeof pluginInstanceId === "string" ? pluginInstanceId.trim() : "";
|
|
4
|
+
if (!canonical)
|
|
5
|
+
throw new TypeError("Plugin instance identifier must be a non-empty string");
|
|
6
|
+
return canonical;
|
|
7
|
+
}
|
|
2
8
|
export class PluginSurfaceScope {
|
|
3
9
|
constructor() {
|
|
4
|
-
|
|
10
|
+
scopes.set(this, { invalidated: false, registrations: new Map() });
|
|
5
11
|
}
|
|
6
12
|
static create() {
|
|
7
13
|
return new PluginSurfaceScope();
|
|
@@ -11,21 +17,56 @@ export function createPluginSurfaceScope() {
|
|
|
11
17
|
return PluginSurfaceScope.create();
|
|
12
18
|
}
|
|
13
19
|
export const defaultPluginSurfaceScope = createPluginSurfaceScope();
|
|
14
|
-
export function registerPluginSurface(scope, pluginInstanceId, dispose) {
|
|
15
|
-
const state =
|
|
20
|
+
export function registerPluginSurface(scope, pluginInstanceId, dispose, invalidate) {
|
|
21
|
+
const state = scopes.get(scope);
|
|
16
22
|
if (!state)
|
|
17
23
|
throw new TypeError("Plugin surface scope is invalid");
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
const canonicalPluginId = canonicalPluginInstanceId(pluginInstanceId);
|
|
25
|
+
const registration = Symbol(canonicalPluginId);
|
|
26
|
+
if (state.invalidated) {
|
|
27
|
+
try {
|
|
28
|
+
void Promise.resolve(invalidate()).catch(() => undefined);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// A fenced session remains invalid even when a local observer fails.
|
|
32
|
+
}
|
|
33
|
+
return () => undefined;
|
|
34
|
+
}
|
|
35
|
+
state.registrations.set(registration, { pluginInstanceId: canonicalPluginId, dispose, invalidate });
|
|
36
|
+
return () => state.registrations.delete(registration);
|
|
21
37
|
}
|
|
22
|
-
export function disposePluginSurfaceScope(scope, pluginInstanceId) {
|
|
23
|
-
const state =
|
|
38
|
+
export async function disposePluginSurfaceScope(scope, pluginInstanceId) {
|
|
39
|
+
const state = scopes.get(scope);
|
|
24
40
|
if (!state)
|
|
25
41
|
throw new TypeError("Plugin surface scope is invalid");
|
|
26
|
-
const
|
|
42
|
+
const canonicalPluginId = pluginInstanceId === undefined
|
|
43
|
+
? undefined
|
|
44
|
+
: canonicalPluginInstanceId(pluginInstanceId);
|
|
45
|
+
const selected = [...state.registrations.entries()].filter(([, registration]) => canonicalPluginId === undefined || registration.pluginInstanceId === canonicalPluginId);
|
|
27
46
|
for (const [key] of selected)
|
|
28
|
-
state.delete(key);
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
state.registrations.delete(key);
|
|
48
|
+
const results = await Promise.allSettled(selected.map(([, registration]) => Promise.resolve().then(() => registration.dispose())));
|
|
49
|
+
const failures = results
|
|
50
|
+
.filter((result) => result.status === "rejected")
|
|
51
|
+
.map((result) => result.reason);
|
|
52
|
+
if (failures.length === 1)
|
|
53
|
+
throw failures[0];
|
|
54
|
+
if (failures.length > 1)
|
|
55
|
+
throw new AggregateError(failures, "Plugin surface scope teardown failed");
|
|
56
|
+
}
|
|
57
|
+
export async function invalidatePluginSurfaceScope(scope) {
|
|
58
|
+
const state = scopes.get(scope);
|
|
59
|
+
if (!state)
|
|
60
|
+
throw new TypeError("Plugin surface scope is invalid");
|
|
61
|
+
state.invalidated = true;
|
|
62
|
+
const selected = [...state.registrations.values()];
|
|
63
|
+
state.registrations.clear();
|
|
64
|
+
const results = await Promise.allSettled(selected.map((registration) => Promise.resolve().then(() => registration.invalidate())));
|
|
65
|
+
const failures = results
|
|
66
|
+
.filter((result) => result.status === "rejected")
|
|
67
|
+
.map((result) => result.reason);
|
|
68
|
+
if (failures.length === 1)
|
|
69
|
+
throw failures[0];
|
|
70
|
+
if (failures.length > 1)
|
|
71
|
+
throw new AggregateError(failures, "Plugin surface scope invalidation failed");
|
|
31
72
|
}
|