@floegence/redevplugin-ui 0.4.2 → 0.5.1
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 +84 -66
- package/dist/contracts.gen.js +87 -66
- package/dist/error-codes.gen.d.ts +4 -0
- package/dist/error-codes.gen.js +206 -0
- package/dist/errors.d.ts +22 -4
- package/dist/errors.js +42 -60
- package/dist/http.d.ts +16 -8
- package/dist/http.js +151 -15
- package/dist/local-import.d.ts +6 -11
- package/dist/local-import.js +58 -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 +3745 -0
- package/dist/openapi.gen.js +5 -0
- package/dist/platform.d.ts +148 -529
- package/dist/platform.js +250 -81
- package/dist/plugin.d.ts +2 -2
- package/dist/surface-scope.d.ts +2 -2
- package/dist/surface-scope.js +22 -6
- package/dist/surface.d.ts +64 -31
- package/dist/surface.js +1430 -200
- 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 +1 -1
package/dist/platform.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { defaultFetch,
|
|
1
|
+
import { assertMutationDispatchable, defaultFetch, dispatchMutationRequest, readMutationPlatformResponse, readPlatformResponse, trimTrailingSlash, } from "./http.js";
|
|
2
|
+
import { PluginBridgeError, PluginMutationLifecycleError, PluginTransportError, pluginMutationOutcome, } from "./errors.js";
|
|
3
|
+
import { createReDevPluginSurfaceTransport, openPluginSurfaceInSlot, } from "./surface.js";
|
|
2
4
|
import { defaultPluginSurfaceScope, disposePluginSurfaceScope, } from "./surface-scope.js";
|
|
3
|
-
|
|
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,288 @@ 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.#getJSON("/_redevplugin/api/plugins/catalog"); }
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
catalog(options = {}) { return this.#getJSON("/_redevplugin/api/plugins/catalog", options); }
|
|
37
|
+
features(options = {}) { return this.#getJSON("/_redevplugin/api/plugins/features", options); }
|
|
38
|
+
getCompatibility(options = {}) { return this.#getJSON("/_redevplugin/api/plugins/platform/compatibility", 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"));
|
|
42
67
|
}
|
|
43
|
-
|
|
44
|
-
|
|
68
|
+
if (signal?.aborted) {
|
|
69
|
+
return Promise.reject(new PluginTransportError("Plugin surface opening was aborted before dispatch", signal.reason, "not_committed"));
|
|
45
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
|
+
});
|
|
46
91
|
}
|
|
47
|
-
|
|
48
|
-
|
|
92
|
+
async revokeSurfaceScope(options = {}) {
|
|
93
|
+
let result;
|
|
49
94
|
try {
|
|
50
|
-
|
|
95
|
+
result = await this.#requestMutation("POST", "/_redevplugin/api/plugins/surfaces/revoke-scope", {}, options);
|
|
51
96
|
}
|
|
52
|
-
|
|
53
|
-
|
|
97
|
+
catch (error) {
|
|
98
|
+
await this.#handleMutationFailure(error, undefined, "Plugin surface scope revocation and local teardown failed");
|
|
99
|
+
throw error;
|
|
54
100
|
}
|
|
101
|
+
await disposePluginSurfaceScope(this.#surfaceScope);
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
startRuntime(request, options = {}) {
|
|
105
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/runtime/start", request, options);
|
|
106
|
+
}
|
|
107
|
+
stopRuntime(options = {}) {
|
|
108
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/runtime/stop", {}, options);
|
|
109
|
+
}
|
|
110
|
+
refreshEnabledRuntimeState(options = {}) {
|
|
111
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/runtime/refresh-enabled", {}, options);
|
|
112
|
+
}
|
|
113
|
+
runtimeHealth(options = {}) { return this.#getJSON("/_redevplugin/api/plugins/runtime/health", options); }
|
|
114
|
+
getSettingsSchema(pluginInstanceId, scope, options = {}) {
|
|
115
|
+
return this.#getJSON(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings/schema?scope=${encodeURIComponent(scope)}`, options);
|
|
116
|
+
}
|
|
117
|
+
getSettings(pluginInstanceId, scope, options = {}) {
|
|
118
|
+
return this.#getJSON(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings?scope=${encodeURIComponent(scope)}`, options);
|
|
119
|
+
}
|
|
120
|
+
patchSettings(pluginInstanceId, request, options = {}) {
|
|
121
|
+
return this.#mutatePluginAt("PATCH", `/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings`, pluginInstanceId, request, options);
|
|
55
122
|
}
|
|
56
|
-
|
|
57
|
-
runtimeHealth() { return this.#getJSON("/_redevplugin/api/plugins/runtime/health"); }
|
|
58
|
-
getSettingsSchema(pluginInstanceId) { return this.#getJSON(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings/schema`); }
|
|
59
|
-
getSettings(pluginInstanceId) { return this.#getJSON(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings`); }
|
|
60
|
-
patchSettings(pluginInstanceId, values) { return this.#patchJSON(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings`, { values }); }
|
|
61
|
-
listOperations(pluginInstanceId) { return this.#getJSON(`/_redevplugin/api/plugins/operations${pluginInstanceId ? `?plugin_instance_id=${encodeURIComponent(pluginInstanceId)}` : ""}`); }
|
|
62
|
-
getOperation(operationId) { return this.#getJSON(`/_redevplugin/api/plugins/operations/${encodeURIComponent(operationId)}`); }
|
|
63
|
-
cancelOperation(operationId, reason) { return this.#postJSON(`/_redevplugin/api/plugins/operations/${encodeURIComponent(operationId)}/cancel`, reason ? { reason } : {}); }
|
|
64
|
-
listIntents(options = {}) {
|
|
123
|
+
listOperations(options = {}, requestOptions = {}) {
|
|
65
124
|
const params = new URLSearchParams();
|
|
66
|
-
if (options.intent_id)
|
|
67
|
-
params.set("intent_id", options.intent_id);
|
|
68
125
|
if (options.plugin_instance_id)
|
|
69
126
|
params.set("plugin_instance_id", options.plugin_instance_id);
|
|
127
|
+
if (options.cursor)
|
|
128
|
+
params.set("cursor", options.cursor);
|
|
129
|
+
if (options.limit !== undefined)
|
|
130
|
+
params.set("limit", String(options.limit));
|
|
70
131
|
const query = params.toString();
|
|
71
|
-
return this.#getJSON(`/_redevplugin/api/plugins/
|
|
132
|
+
return this.#getJSON(`/_redevplugin/api/plugins/operations${query ? `?${query}` : ""}`, requestOptions);
|
|
133
|
+
}
|
|
134
|
+
getOperation(operationId, options = {}) {
|
|
135
|
+
return this.#getJSON(`/_redevplugin/api/plugins/operations/${encodeURIComponent(operationId)}`, options);
|
|
72
136
|
}
|
|
73
|
-
|
|
74
|
-
return this.#
|
|
137
|
+
cancelOperation(operationId, reason, options = {}) {
|
|
138
|
+
return this.#requestMutation("POST", `/_redevplugin/api/plugins/operations/${encodeURIComponent(operationId)}/cancel`, reason ? { reason } : {}, options);
|
|
75
139
|
}
|
|
76
|
-
|
|
77
|
-
importData(request) { return this.#postJSON("/_redevplugin/api/plugins/data/import", request); }
|
|
78
|
-
listRetainedData(options = {}) {
|
|
140
|
+
listIntents(options = {}, requestOptions = {}) {
|
|
79
141
|
const params = new URLSearchParams();
|
|
80
|
-
if (options.
|
|
81
|
-
params.set("
|
|
82
|
-
if (options.
|
|
83
|
-
params.set("
|
|
84
|
-
if (options.source_plugin_instance_id)
|
|
85
|
-
params.set("source_plugin_instance_id", options.source_plugin_instance_id);
|
|
86
|
-
if (options.state)
|
|
87
|
-
params.set("state", options.state);
|
|
142
|
+
if (options.intent_id !== undefined)
|
|
143
|
+
params.set("intent_id", options.intent_id);
|
|
144
|
+
if (options.plugin_instance_id !== undefined)
|
|
145
|
+
params.set("plugin_instance_id", options.plugin_instance_id);
|
|
88
146
|
const query = params.toString();
|
|
89
|
-
return this.#getJSON(`/_redevplugin/api/plugins/
|
|
147
|
+
return this.#getJSON(`/_redevplugin/api/plugins/intents${query ? `?${query}` : ""}`, requestOptions);
|
|
148
|
+
}
|
|
149
|
+
invokeIntent(request, options = {}) {
|
|
150
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/intents/invoke", request, options);
|
|
151
|
+
}
|
|
152
|
+
exportData(request, options = {}) {
|
|
153
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/data/export", request, options);
|
|
154
|
+
}
|
|
155
|
+
deleteDataExport(request, options = {}) {
|
|
156
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/data/export/delete", request, options);
|
|
157
|
+
}
|
|
158
|
+
importData(request, options = {}) {
|
|
159
|
+
return this.#mutatePluginAt("POST", "/_redevplugin/api/plugins/data/import", request.plugin_instance_id, request, options);
|
|
90
160
|
}
|
|
91
|
-
|
|
92
|
-
bindRetainedData(request) { return this.#postJSON("/_redevplugin/api/plugins/retained-data/bind", request); }
|
|
93
|
-
cleanupExpiredRetainedData(request = {}) { return this.#postJSON("/_redevplugin/api/plugins/retained-data/cleanup-expired", request); }
|
|
94
|
-
listPermissions(pluginInstanceId, activeOnly) {
|
|
161
|
+
listRetainedData(options = {}, requestOptions = {}) {
|
|
95
162
|
const params = new URLSearchParams();
|
|
96
|
-
if (
|
|
97
|
-
params.set("plugin_instance_id",
|
|
98
|
-
if (activeOnly != null)
|
|
99
|
-
params.set("active_only", activeOnly ? "true" : "false");
|
|
163
|
+
if (options.plugin_instance_id)
|
|
164
|
+
params.set("plugin_instance_id", options.plugin_instance_id);
|
|
100
165
|
const query = params.toString();
|
|
101
|
-
return this.#getJSON(`/_redevplugin/api/plugins/
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
166
|
+
return this.#getJSON(`/_redevplugin/api/plugins/retained-data${query ? `?${query}` : ""}`, requestOptions);
|
|
167
|
+
}
|
|
168
|
+
deleteRetainedData(request, options = {}) {
|
|
169
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/retained-data/delete", request, options);
|
|
170
|
+
}
|
|
171
|
+
bindRetainedData(request, options = {}) {
|
|
172
|
+
return this.#mutatePluginAt("POST", "/_redevplugin/api/plugins/retained-data/bind", request.target_plugin_instance_id, request, options);
|
|
173
|
+
}
|
|
174
|
+
cleanupExpiredRetainedData(request = {}, options = {}) {
|
|
175
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/retained-data/cleanup-expired", request, options);
|
|
176
|
+
}
|
|
177
|
+
listPermissions(options = {}, requestOptions = {}) {
|
|
178
|
+
const params = new URLSearchParams();
|
|
179
|
+
if (options.plugin_instance_id !== undefined)
|
|
180
|
+
params.set("plugin_instance_id", options.plugin_instance_id);
|
|
181
|
+
if (options.active_only !== undefined)
|
|
182
|
+
params.set("active_only", options.active_only ? "true" : "false");
|
|
183
|
+
const query = params.toString();
|
|
184
|
+
return this.#getJSON(`/_redevplugin/api/plugins/permissions${query ? `?${query}` : ""}`, requestOptions);
|
|
185
|
+
}
|
|
186
|
+
grantPermission(request, options = {}) {
|
|
187
|
+
return this.#mutatePlugin("/_redevplugin/api/plugins/permissions/grant", request, options);
|
|
188
|
+
}
|
|
189
|
+
revokePermission(request, options = {}) {
|
|
190
|
+
return this.#mutatePlugin("/_redevplugin/api/plugins/permissions/revoke", request, options);
|
|
191
|
+
}
|
|
192
|
+
listSecurityPolicies(options = {}) {
|
|
193
|
+
return this.#getJSON("/_redevplugin/api/plugins/security-policies", options);
|
|
194
|
+
}
|
|
195
|
+
getSecurityPolicy(pluginInstanceId, options = {}) {
|
|
196
|
+
return this.#getJSON(`/_redevplugin/api/plugins/security-policies/${encodeURIComponent(pluginInstanceId)}`, options);
|
|
197
|
+
}
|
|
198
|
+
putSecurityPolicy(pluginInstanceId, request, options = {}) {
|
|
199
|
+
return this.#mutatePluginAt("PUT", `/_redevplugin/api/plugins/security-policies/${encodeURIComponent(pluginInstanceId)}`, pluginInstanceId, request, options);
|
|
200
|
+
}
|
|
201
|
+
deleteSecurityPolicy(pluginInstanceId, request, options = {}) {
|
|
202
|
+
return this.#mutatePluginAt("DELETE", `/_redevplugin/api/plugins/security-policies/${encodeURIComponent(pluginInstanceId)}`, pluginInstanceId, request, options);
|
|
203
|
+
}
|
|
204
|
+
bindSecret(request, options = {}) {
|
|
205
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/secrets/bind", request, options);
|
|
206
|
+
}
|
|
207
|
+
testSecret(request, options = {}) {
|
|
208
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/secrets/test", request, options);
|
|
209
|
+
}
|
|
210
|
+
deleteSecret(request, options = {}) {
|
|
211
|
+
return this.#requestMutation("POST", "/_redevplugin/api/plugins/secrets/delete", request, options);
|
|
212
|
+
}
|
|
213
|
+
listDiagnosticEvents(options = {}, requestOptions = {}) {
|
|
214
|
+
return this.#getJSON(`/_redevplugin/api/plugins/diagnostics${queryString(options)}`, requestOptions);
|
|
215
|
+
}
|
|
216
|
+
#getJSON(path, options) {
|
|
217
|
+
return this.#requestJSON("GET", path, options);
|
|
218
|
+
}
|
|
219
|
+
#mutatePlugin(path, request, options) {
|
|
220
|
+
return this.#mutatePluginAt("POST", path, request.plugin_instance_id, request, options);
|
|
221
|
+
}
|
|
222
|
+
async #mutatePluginAt(method, path, pluginInstanceId, request, options) {
|
|
223
|
+
let result;
|
|
114
224
|
try {
|
|
115
|
-
|
|
225
|
+
result = await this.#requestMutation(method, path, request, options);
|
|
116
226
|
}
|
|
117
|
-
|
|
118
|
-
|
|
227
|
+
catch (error) {
|
|
228
|
+
await this.#handleMutationFailure(error, pluginInstanceId, "Plugin mutation and local surface teardown failed");
|
|
229
|
+
throw error;
|
|
119
230
|
}
|
|
231
|
+
await disposePluginSurfaceScope(this.#surfaceScope, pluginInstanceId);
|
|
232
|
+
return result;
|
|
120
233
|
}
|
|
121
|
-
async #
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
234
|
+
async #handleMutationFailure(error, pluginInstanceId, message) {
|
|
235
|
+
if (pluginMutationOutcome(error) === "not_committed")
|
|
236
|
+
return;
|
|
237
|
+
const lifecycleErrors = [];
|
|
238
|
+
try {
|
|
239
|
+
await disposePluginSurfaceScope(this.#surfaceScope, pluginInstanceId);
|
|
240
|
+
}
|
|
241
|
+
catch (caught) {
|
|
242
|
+
lifecycleErrors.push(caught);
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
this.#onMutationOutcomeUnknown?.(pluginInstanceId);
|
|
246
|
+
}
|
|
247
|
+
catch (caught) {
|
|
248
|
+
lifecycleErrors.push(caught);
|
|
249
|
+
}
|
|
250
|
+
if (lifecycleErrors.length > 0)
|
|
251
|
+
throw new PluginMutationLifecycleError(message, error, lifecycleErrors);
|
|
252
|
+
}
|
|
253
|
+
async #requestJSON(method, path, options) {
|
|
254
|
+
let response;
|
|
255
|
+
try {
|
|
256
|
+
response = await this.#fetch(this.#apiBaseURL + path, {
|
|
257
|
+
method,
|
|
258
|
+
headers: { "Accept": "application/json" },
|
|
259
|
+
credentials: "same-origin",
|
|
260
|
+
signal: options.signal,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
catch (cause) {
|
|
264
|
+
throw new PluginTransportError(`Plugin platform request failed for ${method} ${path}`, cause);
|
|
265
|
+
}
|
|
266
|
+
return readPlatformResponse(response);
|
|
267
|
+
}
|
|
268
|
+
async #requestMutation(method, path, body, options) {
|
|
269
|
+
const operation = `${method} ${path}`;
|
|
270
|
+
assertMutationDispatchable(options.signal, operation);
|
|
271
|
+
const headers = {
|
|
272
|
+
"Accept": "application/json",
|
|
273
|
+
"Content-Type": "application/json",
|
|
274
|
+
};
|
|
275
|
+
let encodedBody;
|
|
276
|
+
try {
|
|
277
|
+
encodedBody = JSON.stringify(body);
|
|
278
|
+
}
|
|
279
|
+
catch (cause) {
|
|
280
|
+
throw new PluginTransportError(`Plugin platform request body serialization failed for ${operation}`, cause, "not_committed");
|
|
281
|
+
}
|
|
282
|
+
const response = await dispatchMutationRequest(this.#fetch, this.#apiBaseURL + path, {
|
|
126
283
|
method,
|
|
127
284
|
headers,
|
|
128
|
-
body:
|
|
285
|
+
body: encodedBody,
|
|
129
286
|
credentials: "same-origin",
|
|
130
|
-
|
|
131
|
-
|
|
287
|
+
signal: options.signal,
|
|
288
|
+
}, operation);
|
|
289
|
+
return readMutationPlatformResponse(response);
|
|
132
290
|
}
|
|
133
291
|
}
|
|
134
292
|
function queryString(values) {
|
|
135
293
|
const params = new URLSearchParams();
|
|
136
294
|
for (const [key, value] of Object.entries(values)) {
|
|
137
|
-
if (value
|
|
295
|
+
if (value !== undefined)
|
|
138
296
|
params.set(key, String(value));
|
|
139
297
|
}
|
|
140
298
|
const query = params.toString();
|
|
141
299
|
return query ? `?${query}` : "";
|
|
142
300
|
}
|
|
301
|
+
function surfaceTransportAPIBaseURL(value) {
|
|
302
|
+
if (!value || value.startsWith("/"))
|
|
303
|
+
return value;
|
|
304
|
+
const parsed = new URL(value);
|
|
305
|
+
const currentOrigin = globalThis.location?.origin;
|
|
306
|
+
if (!currentOrigin || currentOrigin === "null" || parsed.origin !== currentOrigin ||
|
|
307
|
+
parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") {
|
|
308
|
+
throw new TypeError("Plugin surface transport apiBaseURL must be same-origin");
|
|
309
|
+
}
|
|
310
|
+
return trimTrailingSlash(parsed.pathname);
|
|
311
|
+
}
|
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,5 @@ 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): () => void;
|
|
8
|
+
export declare function disposePluginSurfaceScope(scope: PluginSurfaceScope, pluginInstanceId?: string): Promise<void>;
|
package/dist/surface-scope.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
const registrations = 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
|
registrations.set(this, new Map());
|
|
@@ -15,17 +21,27 @@ export function registerPluginSurface(scope, pluginInstanceId, dispose) {
|
|
|
15
21
|
const state = registrations.get(scope);
|
|
16
22
|
if (!state)
|
|
17
23
|
throw new TypeError("Plugin surface scope is invalid");
|
|
18
|
-
const
|
|
19
|
-
|
|
24
|
+
const canonicalPluginId = canonicalPluginInstanceId(pluginInstanceId);
|
|
25
|
+
const registration = Symbol(canonicalPluginId);
|
|
26
|
+
state.set(registration, { pluginInstanceId: canonicalPluginId, dispose });
|
|
20
27
|
return () => state.delete(registration);
|
|
21
28
|
}
|
|
22
|
-
export function disposePluginSurfaceScope(scope, pluginInstanceId) {
|
|
29
|
+
export async function disposePluginSurfaceScope(scope, pluginInstanceId) {
|
|
23
30
|
const state = registrations.get(scope);
|
|
24
31
|
if (!state)
|
|
25
32
|
throw new TypeError("Plugin surface scope is invalid");
|
|
26
|
-
const
|
|
33
|
+
const canonicalPluginId = pluginInstanceId === undefined
|
|
34
|
+
? undefined
|
|
35
|
+
: canonicalPluginInstanceId(pluginInstanceId);
|
|
36
|
+
const selected = [...state.entries()].filter(([, registration]) => canonicalPluginId === undefined || registration.pluginInstanceId === canonicalPluginId);
|
|
27
37
|
for (const [key] of selected)
|
|
28
38
|
state.delete(key);
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
const results = await Promise.allSettled(selected.map(([, registration]) => Promise.resolve().then(() => registration.dispose())));
|
|
40
|
+
const failures = results
|
|
41
|
+
.filter((result) => result.status === "rejected")
|
|
42
|
+
.map((result) => result.reason);
|
|
43
|
+
if (failures.length === 1)
|
|
44
|
+
throw failures[0];
|
|
45
|
+
if (failures.length > 1)
|
|
46
|
+
throw new AggregateError(failures, "Plugin surface scope teardown failed");
|
|
31
47
|
}
|
package/dist/surface.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { PluginBridgeError } from "./errors.js";
|
|
1
|
+
import { PluginBridgeError, type PluginMutationOutcome } from "./errors.js";
|
|
2
2
|
import { pluginUIProtocolVersion } from "./contracts.gen.js";
|
|
3
|
-
import { type OpaqueSurfaceAllowedTag } from "./opaque-surface-policy.gen.js";
|
|
4
3
|
import { type FetchLike } from "./http.js";
|
|
5
4
|
import { type PluginSurfaceScope } from "./surface-scope.js";
|
|
6
|
-
|
|
5
|
+
import { type PluginUIVNode } from "./ui-reconciler.js";
|
|
6
|
+
export type { PluginUIAttributeValue, PluginUIElementVNode, PluginUIPatchOperation, PluginUIVNode, } from "./ui-reconciler.js";
|
|
7
|
+
export declare const opaqueSurfaceDocumentSchemaVersion: "redevplugin.opaque_surface_document.v3";
|
|
7
8
|
export declare const pluginRiskPlanSchemaVersion: "redevplugin.capability.risk_plan.v1";
|
|
8
9
|
export type PluginJSONValue = null | boolean | number | string | PluginJSONValue[] | PluginJSONObject;
|
|
9
10
|
export type PluginJSONObject = {
|
|
@@ -26,7 +27,7 @@ export type TrustedParentBridgeHandshake = {
|
|
|
26
27
|
active_fingerprint: string;
|
|
27
28
|
bridge_nonce: string;
|
|
28
29
|
asset_session_nonce: string;
|
|
29
|
-
|
|
30
|
+
management_revision: number;
|
|
30
31
|
revoke_epoch: number;
|
|
31
32
|
ui_protocol_version: typeof pluginUIProtocolVersion;
|
|
32
33
|
};
|
|
@@ -53,6 +54,7 @@ export type PluginBridgeResponse = {
|
|
|
53
54
|
error_code: string;
|
|
54
55
|
error: string;
|
|
55
56
|
error_details?: PluginJSONObject;
|
|
57
|
+
mutation_outcome?: PluginMutationOutcome;
|
|
56
58
|
};
|
|
57
59
|
export type PluginBridgeCancelMessage = {
|
|
58
60
|
type: "redevplugin.bridge.cancel";
|
|
@@ -70,6 +72,9 @@ export type PluginBridgeLifecycleAckMessage = {
|
|
|
70
72
|
export type PluginUIActionEvent = {
|
|
71
73
|
action: string;
|
|
72
74
|
event: "click" | "input" | "change" | "submit" | "escape";
|
|
75
|
+
targetKey: string;
|
|
76
|
+
editRevision: number;
|
|
77
|
+
isComposing: boolean;
|
|
73
78
|
value?: string;
|
|
74
79
|
checked?: boolean;
|
|
75
80
|
form_data?: Record<string, string>;
|
|
@@ -117,13 +122,6 @@ export type PluginCanvasPointerEvent = {
|
|
|
117
122
|
pressure: number;
|
|
118
123
|
};
|
|
119
124
|
export type PluginCanvasInputEvent = PluginCanvasFocusEvent | PluginCanvasResizeEvent | PluginCanvasKeyEvent | PluginCanvasPointerEvent;
|
|
120
|
-
export type PluginUIAttributeValue = string | number | boolean;
|
|
121
|
-
export type PluginUIVNode = string | {
|
|
122
|
-
type: "element";
|
|
123
|
-
tag: OpaqueSurfaceAllowedTag;
|
|
124
|
-
attributes?: Record<string, PluginUIAttributeValue>;
|
|
125
|
-
children?: PluginUIVNode[];
|
|
126
|
-
};
|
|
127
125
|
export type PluginMethodResult<T = unknown> = {
|
|
128
126
|
data: T;
|
|
129
127
|
operation_id?: string;
|
|
@@ -227,16 +225,19 @@ export type PluginBridgeClientOptions = {
|
|
|
227
225
|
port?: MessagePortLike;
|
|
228
226
|
surfaceHandle?: string;
|
|
229
227
|
};
|
|
228
|
+
export type PluginBridgeRequestOptions = {
|
|
229
|
+
signal?: AbortSignal;
|
|
230
|
+
};
|
|
230
231
|
export declare class PluginBridgeClient {
|
|
231
232
|
#private;
|
|
232
233
|
readonly surfaceHandle: string;
|
|
233
234
|
readonly timeoutMs: number;
|
|
234
235
|
constructor(options?: PluginBridgeClientOptions);
|
|
235
236
|
ready(): Promise<void>;
|
|
236
|
-
call<T = unknown>(method: string, params?: PluginJSONObject): Promise<T>;
|
|
237
|
-
readStream(streamHandle: string): Promise<PluginStreamReadResult>;
|
|
238
|
-
cancelOperation(operationID: string, reason?: string): Promise<void>;
|
|
239
|
-
render(tree: PluginUIVNode
|
|
237
|
+
call<T = unknown>(method: string, params?: PluginJSONObject, options?: PluginBridgeRequestOptions): Promise<T>;
|
|
238
|
+
readStream(streamHandle: string, options?: PluginBridgeRequestOptions): Promise<PluginStreamReadResult>;
|
|
239
|
+
cancelOperation(operationID: string, reason?: string, options?: PluginBridgeRequestOptions): Promise<void>;
|
|
240
|
+
render(tree: PluginUIVNode): Promise<void>;
|
|
240
241
|
openCanvas(canvasId: string): Promise<PluginCanvasSurface>;
|
|
241
242
|
updateCanvasAccessibility(canvasId: string, state: PluginCanvasAccessibilityState): Promise<void>;
|
|
242
243
|
loadImageAsset(assetId: string): Promise<ImageBitmap>;
|
|
@@ -322,7 +323,7 @@ export type PluginSurfacePreparationResult = {
|
|
|
322
323
|
asset_session_nonce: string;
|
|
323
324
|
entry_path: string;
|
|
324
325
|
entry_sha256: string;
|
|
325
|
-
|
|
326
|
+
management_revision: number;
|
|
326
327
|
revoke_epoch: number;
|
|
327
328
|
issued_at: string;
|
|
328
329
|
expires_at: string;
|
|
@@ -340,7 +341,7 @@ export type PluginSurfaceHostBootstrap = {
|
|
|
340
341
|
entrySHA256: string;
|
|
341
342
|
assetTicket: string;
|
|
342
343
|
assetSessionNonce: string;
|
|
343
|
-
|
|
344
|
+
managementRevision: number;
|
|
344
345
|
revokeEpoch: number;
|
|
345
346
|
runtimeGenerationId: string;
|
|
346
347
|
};
|
|
@@ -348,6 +349,22 @@ export type PluginSurfaceOpeningProgress = {
|
|
|
348
349
|
phase: "opening";
|
|
349
350
|
elapsedMs: number;
|
|
350
351
|
};
|
|
352
|
+
export type PluginSurfaceHost = {
|
|
353
|
+
readonly element: HTMLIFrameElement;
|
|
354
|
+
readonly surfaceInstanceId: string;
|
|
355
|
+
sendLifecycle(event: Exclude<BridgeLifecycleEvent, {
|
|
356
|
+
type: "ready" | "dispose";
|
|
357
|
+
}>): void;
|
|
358
|
+
close(): Promise<PluginSurfaceCloseResult>;
|
|
359
|
+
dispose(): Promise<void>;
|
|
360
|
+
};
|
|
361
|
+
export type PreparedPluginSurfaceHost = PluginSurfaceHost & {
|
|
362
|
+
readonly bootstrap: PluginSurfaceHostBootstrap;
|
|
363
|
+
readonly bridgeChannelId: string;
|
|
364
|
+
readonly frameGenerationId: string;
|
|
365
|
+
readonly surfaceHandle: string;
|
|
366
|
+
open(): Promise<void>;
|
|
367
|
+
};
|
|
351
368
|
export type PluginSurfaceHostOptions = {
|
|
352
369
|
bootstrap: PluginSurfaceHostBootstrap;
|
|
353
370
|
hostTransport: ReDevPluginSurfaceTransport;
|
|
@@ -361,6 +378,13 @@ export type PluginSurfaceHostOptions = {
|
|
|
361
378
|
onOpeningProgress?: (progress: PluginSurfaceOpeningProgress) => void;
|
|
362
379
|
onError?: (error: PluginBridgeError) => void;
|
|
363
380
|
};
|
|
381
|
+
type PluginSurfaceOpeningRequest = {
|
|
382
|
+
pluginInstanceId: string;
|
|
383
|
+
surfaceScope: PluginSurfaceScope;
|
|
384
|
+
signal?: AbortSignal;
|
|
385
|
+
abortError: () => Error;
|
|
386
|
+
open: () => Promise<PluginSurfaceHostOptions>;
|
|
387
|
+
};
|
|
364
388
|
declare const redevPluginSurfaceTransportBrand: unique symbol;
|
|
365
389
|
export type ReDevPluginSurfaceTransport = {
|
|
366
390
|
readonly [redevPluginSurfaceTransportBrand]: true;
|
|
@@ -373,21 +397,30 @@ export declare function createReDevPluginSurfaceTransport(options?: ReDevPluginS
|
|
|
373
397
|
export type OpaquePluginBootstrapHTMLOptions = {
|
|
374
398
|
scriptNonce?: string;
|
|
375
399
|
};
|
|
400
|
+
export type PluginSurfaceQuiesceResult = {
|
|
401
|
+
outcome: "acknowledged" | "not_ready" | "timed_out";
|
|
402
|
+
durationMs: number;
|
|
403
|
+
};
|
|
404
|
+
export type PluginSurfaceCloseResult = {
|
|
405
|
+
quiesce: PluginSurfaceQuiesceResult;
|
|
406
|
+
revokeDurationMs: number;
|
|
407
|
+
totalDurationMs: number;
|
|
408
|
+
};
|
|
376
409
|
export declare function createOpaquePluginBootstrapHTML(options?: OpaquePluginBootstrapHTMLOptions): string;
|
|
377
|
-
export declare
|
|
410
|
+
export declare function createPreparedPluginSurfaceHost(options: PluginSurfaceHostOptions): PreparedPluginSurfaceHost;
|
|
411
|
+
export type PluginSurfaceSlotState = "empty" | "opening" | "ready" | "error" | "disposed";
|
|
412
|
+
export type PluginSurfaceSlotOptions = {
|
|
413
|
+
stage: HTMLElement;
|
|
414
|
+
onStateChange?: (state: PluginSurfaceSlotState, error?: PluginBridgeError) => void;
|
|
415
|
+
onSurfaceClosed?: (result: PluginSurfaceCloseResult) => void;
|
|
416
|
+
};
|
|
417
|
+
export declare function openPluginSurfaceInSlot(slot: PluginSurfaceSlot, request: PluginSurfaceOpeningRequest): Promise<PluginSurfaceHost>;
|
|
418
|
+
export declare function openPreparedPluginSurfaceInSlot(slot: PluginSurfaceSlot, options: PluginSurfaceHostOptions | PromiseLike<PluginSurfaceHostOptions>): Promise<PluginSurfaceHost>;
|
|
419
|
+
export declare class PluginSurfaceSlot {
|
|
378
420
|
#private;
|
|
379
|
-
readonly element:
|
|
380
|
-
|
|
381
|
-
readonly bridgeChannelId: string;
|
|
382
|
-
readonly frameGenerationId: string;
|
|
383
|
-
readonly surfaceHandle: string;
|
|
384
|
-
static create(options: PluginSurfaceHostOptions): PluginSurfaceHost;
|
|
421
|
+
readonly element: HTMLElement;
|
|
422
|
+
static create(options: PluginSurfaceSlotOptions): PluginSurfaceSlot;
|
|
385
423
|
private constructor();
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
type: "ready" | "dispose";
|
|
389
|
-
}>): void;
|
|
390
|
-
close(): Promise<void>;
|
|
391
|
-
dispose(): void;
|
|
424
|
+
close(): Promise<PluginSurfaceCloseResult | undefined>;
|
|
425
|
+
dispose(): Promise<void>;
|
|
392
426
|
}
|
|
393
|
-
export {};
|