@floegence/redevplugin-ui 0.5.1 → 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/contracts.gen.d.ts +234 -108
- package/dist/contracts.gen.js +255 -108
- package/dist/error-codes.gen.d.ts +3 -3
- package/dist/error-codes.gen.js +13 -1
- package/dist/errors.d.ts +1 -1
- package/dist/http.d.ts +5 -1
- package/dist/http.js +37 -1
- package/dist/local-import.js +16 -10
- package/dist/openapi.gen.d.ts +428 -182
- package/dist/platform.d.ts +10 -8
- package/dist/platform.js +102 -70
- package/dist/surface-scope.d.ts +2 -1
- package/dist/surface-scope.js +34 -9
- package/dist/surface.js +67 -8
- package/package.json +4 -1
package/dist/platform.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type FetchLike } from "./http.js";
|
|
2
|
-
import type { components
|
|
2
|
+
import type { components } from "./openapi.gen.js";
|
|
3
3
|
import { type PluginConfirmationHandler, type PluginSurfaceHost, type PluginSurfaceOpeningProgress, type PluginSurfaceReloadLimiter, type PluginSurfaceSlot, type PluginTrustedMethodResult, type ReDevPluginSurfaceTransport } from "./surface.js";
|
|
4
4
|
import { type PluginSurfaceScope } from "./surface-scope.js";
|
|
5
5
|
export type PluginPlatformClientOptions = {
|
|
@@ -46,7 +46,9 @@ export type PluginOpenSurfaceInSlotOptions = PluginRequestOptions & {
|
|
|
46
46
|
onOpeningProgress?: (progress: PluginSurfaceOpeningProgress) => void;
|
|
47
47
|
onError?: (error: import("./errors.js").PluginBridgeError) => void;
|
|
48
48
|
};
|
|
49
|
-
export type
|
|
49
|
+
export type PluginSessionScopeRevokeCounts = Readonly<PlatformSchemas["SessionScopeRevokeCounts"]>;
|
|
50
|
+
export type PluginSessionScopeRevokeResult = Readonly<PlatformSchemas["SessionScopeRevokeCompleteResult"]>;
|
|
51
|
+
export type PluginSessionScopeState = PluginSessionScopeRevokeResult["state"];
|
|
50
52
|
export type PluginSurfaceBootstrapResult = PlatformSchemas["SurfaceBootstrap"];
|
|
51
53
|
export type PluginRuntimeStartRequest = PlatformSchemas["StartRuntimeRequest"];
|
|
52
54
|
export type PluginRuntimeTarget = NonNullable<PluginRuntimeStartRequest["target"]>;
|
|
@@ -72,15 +74,15 @@ export type PluginCapabilityContractPin = PlatformSchemas["HostCapabilityPinV1"]
|
|
|
72
74
|
export type PluginPublicOperationBinding = PlatformSchemas["PublicOperationBinding"];
|
|
73
75
|
export type PluginOperationRecord = PlatformSchemas["OperationRecord"];
|
|
74
76
|
export type PluginOperationList = PlatformSchemas["PluginOperationList"];
|
|
75
|
-
export type PluginOperationListOptions =
|
|
77
|
+
export type PluginOperationListOptions = PlatformSchemas["ListOperationsQueryRequest"];
|
|
76
78
|
export type PluginIntentRecord = PlatformSchemas["PluginIntentRecord"];
|
|
77
79
|
export type PluginIntentList = PlatformSchemas["PluginIntentList"];
|
|
78
|
-
export type PluginIntentListOptions =
|
|
80
|
+
export type PluginIntentListOptions = PlatformSchemas["ListIntentsQueryRequest"];
|
|
79
81
|
export type PluginIntentInvokeRequest = PlatformSchemas["InvokeIntentRequest"];
|
|
80
82
|
export type PluginPermissionGrant = PlatformSchemas["PluginPermissionGrant"];
|
|
81
83
|
export type PluginPermissionMutationResult = PlatformSchemas["PluginPermissionMutationResult"];
|
|
82
84
|
export type PluginPermissionList = PlatformSchemas["PluginPermissionList"];
|
|
83
|
-
export type PluginPermissionListOptions =
|
|
85
|
+
export type PluginPermissionListOptions = PlatformSchemas["ListPermissionsQueryRequest"];
|
|
84
86
|
export type PluginPermissionGrantRequest = PlatformSchemas["GrantPermissionRequest"];
|
|
85
87
|
export type PluginPermissionRevokeRequest = PlatformSchemas["RevokePermissionRequest"];
|
|
86
88
|
export type PluginSecurityPolicy = PlatformSchemas["PluginSecurityPolicy"];
|
|
@@ -94,7 +96,7 @@ export type PluginDataExportDeleteRequest = PlatformSchemas["DeleteDataExportReq
|
|
|
94
96
|
export type PluginDataExportDeleteResult = PlatformSchemas["PluginDataExportDeleteResult"];
|
|
95
97
|
export type PluginDataImportRequest = PlatformSchemas["ImportDataRequest"];
|
|
96
98
|
export type PluginDataBinding = PlatformSchemas["PluginDataBinding"];
|
|
97
|
-
export type PluginRetainedDataListOptions =
|
|
99
|
+
export type PluginRetainedDataListOptions = PlatformSchemas["ListRetainedDataQueryRequest"];
|
|
98
100
|
export type PluginRetainedDataList = PlatformSchemas["RetainedDataList"];
|
|
99
101
|
export type PluginRetainedDataDeleteRequest = PlatformSchemas["DeleteRetainedDataRequest"];
|
|
100
102
|
export type PluginRetainedDataBindRequest = PlatformSchemas["BindRetainedDataRequest"];
|
|
@@ -108,7 +110,7 @@ export type PluginDiagnosticEvent = PlatformSchemas["PluginDiagnosticEvent"];
|
|
|
108
110
|
export type PluginDiagnosticEventList = PlatformSchemas["PluginDiagnosticEventList"];
|
|
109
111
|
export type PluginDiagnosticDetails = PlatformSchemas["PluginDiagnosticDetails"];
|
|
110
112
|
export type PluginDiagnosticMutationOutcome = PlatformSchemas["DiagnosticMutationOutcome"];
|
|
111
|
-
export type PluginDiagnosticListOptions =
|
|
113
|
+
export type PluginDiagnosticListOptions = PlatformSchemas["ListDiagnosticsQueryRequest"];
|
|
112
114
|
export type PluginDiagnosticSeverity = NonNullable<PluginDiagnosticListOptions["severity"]>;
|
|
113
115
|
export declare class PluginPlatformClient {
|
|
114
116
|
#private;
|
|
@@ -123,7 +125,7 @@ export declare class PluginPlatformClient {
|
|
|
123
125
|
disablePlugin(request: PluginDisableRequest, options?: PluginRequestOptions): Promise<PluginRecord>;
|
|
124
126
|
uninstallPlugin(request: PluginUninstallRequest, options?: PluginRequestOptions): Promise<PluginRecord>;
|
|
125
127
|
openSurfaceInSlot(slot: PluginSurfaceSlot, request: PluginOpenSurfaceRequest, options?: PluginOpenSurfaceInSlotOptions): Promise<PluginSurfaceHost>;
|
|
126
|
-
|
|
128
|
+
revokeSessionScope(options?: PluginRequestOptions): Promise<PluginSessionScopeRevokeResult>;
|
|
127
129
|
startRuntime(request: PluginRuntimeStartRequest, options?: PluginRequestOptions): Promise<PluginRuntimeHealth>;
|
|
128
130
|
stopRuntime(options?: PluginRequestOptions): Promise<PluginRuntimeStopResult>;
|
|
129
131
|
refreshEnabledRuntimeState(options?: PluginRequestOptions): Promise<PluginRuntimeRefreshResult>;
|
package/dist/platform.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { assertMutationDispatchable, defaultFetch, dispatchMutationRequest, readMutationPlatformResponse, readPlatformResponse, trimTrailingSlash, } from "./http.js";
|
|
1
|
+
import { assertMutationDispatchable, defaultFetch, dispatchMutationRequest, dispatchQueryRequest, readMutationPlatformResponse, readPlatformResponse, trimTrailingSlash, } from "./http.js";
|
|
2
2
|
import { PluginBridgeError, PluginMutationLifecycleError, PluginTransportError, pluginMutationOutcome, } from "./errors.js";
|
|
3
3
|
import { createReDevPluginSurfaceTransport, openPluginSurfaceInSlot, } from "./surface.js";
|
|
4
|
-
import { defaultPluginSurfaceScope, disposePluginSurfaceScope, } from "./surface-scope.js";
|
|
4
|
+
import { defaultPluginSurfaceScope, disposePluginSurfaceScope, invalidatePluginSurfaceScope, } from "./surface-scope.js";
|
|
5
5
|
function toPluginSurfaceHostBootstrap(value) {
|
|
6
6
|
return {
|
|
7
7
|
pluginId: value.plugin_id,
|
|
@@ -33,9 +33,9 @@ export class PluginPlatformClient {
|
|
|
33
33
|
this.#surfaceTransport = options.surfaceTransport;
|
|
34
34
|
this.#onMutationOutcomeUnknown = options.onMutationOutcomeUnknown;
|
|
35
35
|
}
|
|
36
|
-
catalog(options = {}) { return this.#
|
|
37
|
-
features(options = {}) { return this.#
|
|
38
|
-
getCompatibility(options = {}) { return this.#
|
|
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
39
|
installReleaseRef(request, options = {}) {
|
|
40
40
|
return this.#requestMutation("POST", "/_redevplugin/api/plugins/install-release-ref", request, options);
|
|
41
41
|
}
|
|
@@ -89,16 +89,20 @@ export class PluginPlatformClient {
|
|
|
89
89
|
})),
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
|
-
async
|
|
92
|
+
async revokeSessionScope(options = {}) {
|
|
93
93
|
let result;
|
|
94
94
|
try {
|
|
95
|
-
|
|
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;
|
|
96
100
|
}
|
|
97
101
|
catch (error) {
|
|
98
|
-
await this.#
|
|
102
|
+
await this.#handleSessionRevokeFailure(error);
|
|
99
103
|
throw error;
|
|
100
104
|
}
|
|
101
|
-
await
|
|
105
|
+
await invalidatePluginSurfaceScope(this.#surfaceScope);
|
|
102
106
|
return result;
|
|
103
107
|
}
|
|
104
108
|
startRuntime(request, options = {}) {
|
|
@@ -110,41 +114,27 @@ export class PluginPlatformClient {
|
|
|
110
114
|
refreshEnabledRuntimeState(options = {}) {
|
|
111
115
|
return this.#requestMutation("POST", "/_redevplugin/api/plugins/runtime/refresh-enabled", {}, options);
|
|
112
116
|
}
|
|
113
|
-
runtimeHealth(options = {}) { return this.#
|
|
117
|
+
runtimeHealth(options = {}) { return this.#requestQuery("/_redevplugin/api/plugins/runtime/health/query", {}, options); }
|
|
114
118
|
getSettingsSchema(pluginInstanceId, scope, options = {}) {
|
|
115
|
-
return this.#
|
|
119
|
+
return this.#requestQuery(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings/schema/query`, { scope }, options);
|
|
116
120
|
}
|
|
117
121
|
getSettings(pluginInstanceId, scope, options = {}) {
|
|
118
|
-
return this.#
|
|
122
|
+
return this.#requestQuery(`/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings/query`, { scope }, options);
|
|
119
123
|
}
|
|
120
124
|
patchSettings(pluginInstanceId, request, options = {}) {
|
|
121
125
|
return this.#mutatePluginAt("PATCH", `/_redevplugin/api/plugins/${encodeURIComponent(pluginInstanceId)}/settings`, pluginInstanceId, request, options);
|
|
122
126
|
}
|
|
123
127
|
listOperations(options = {}, requestOptions = {}) {
|
|
124
|
-
|
|
125
|
-
if (options.plugin_instance_id)
|
|
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));
|
|
131
|
-
const query = params.toString();
|
|
132
|
-
return this.#getJSON(`/_redevplugin/api/plugins/operations${query ? `?${query}` : ""}`, requestOptions);
|
|
128
|
+
return this.#requestQuery("/_redevplugin/api/plugins/operations/query", options, requestOptions);
|
|
133
129
|
}
|
|
134
130
|
getOperation(operationId, options = {}) {
|
|
135
|
-
return this.#
|
|
131
|
+
return this.#requestQuery(`/_redevplugin/api/plugins/operations/${encodeURIComponent(operationId)}/query`, {}, options);
|
|
136
132
|
}
|
|
137
133
|
cancelOperation(operationId, reason, options = {}) {
|
|
138
134
|
return this.#requestMutation("POST", `/_redevplugin/api/plugins/operations/${encodeURIComponent(operationId)}/cancel`, reason ? { reason } : {}, options);
|
|
139
135
|
}
|
|
140
136
|
listIntents(options = {}, requestOptions = {}) {
|
|
141
|
-
|
|
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);
|
|
146
|
-
const query = params.toString();
|
|
147
|
-
return this.#getJSON(`/_redevplugin/api/plugins/intents${query ? `?${query}` : ""}`, requestOptions);
|
|
137
|
+
return this.#requestQuery("/_redevplugin/api/plugins/intents/query", options, requestOptions);
|
|
148
138
|
}
|
|
149
139
|
invokeIntent(request, options = {}) {
|
|
150
140
|
return this.#requestMutation("POST", "/_redevplugin/api/plugins/intents/invoke", request, options);
|
|
@@ -159,11 +149,7 @@ export class PluginPlatformClient {
|
|
|
159
149
|
return this.#mutatePluginAt("POST", "/_redevplugin/api/plugins/data/import", request.plugin_instance_id, request, options);
|
|
160
150
|
}
|
|
161
151
|
listRetainedData(options = {}, requestOptions = {}) {
|
|
162
|
-
|
|
163
|
-
if (options.plugin_instance_id)
|
|
164
|
-
params.set("plugin_instance_id", options.plugin_instance_id);
|
|
165
|
-
const query = params.toString();
|
|
166
|
-
return this.#getJSON(`/_redevplugin/api/plugins/retained-data${query ? `?${query}` : ""}`, requestOptions);
|
|
152
|
+
return this.#requestQuery("/_redevplugin/api/plugins/retained-data/query", options, requestOptions);
|
|
167
153
|
}
|
|
168
154
|
deleteRetainedData(request, options = {}) {
|
|
169
155
|
return this.#requestMutation("POST", "/_redevplugin/api/plugins/retained-data/delete", request, options);
|
|
@@ -175,13 +161,7 @@ export class PluginPlatformClient {
|
|
|
175
161
|
return this.#requestMutation("POST", "/_redevplugin/api/plugins/retained-data/cleanup-expired", request, options);
|
|
176
162
|
}
|
|
177
163
|
listPermissions(options = {}, requestOptions = {}) {
|
|
178
|
-
|
|
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);
|
|
164
|
+
return this.#requestQuery("/_redevplugin/api/plugins/permissions/query", options, requestOptions);
|
|
185
165
|
}
|
|
186
166
|
grantPermission(request, options = {}) {
|
|
187
167
|
return this.#mutatePlugin("/_redevplugin/api/plugins/permissions/grant", request, options);
|
|
@@ -190,10 +170,10 @@ export class PluginPlatformClient {
|
|
|
190
170
|
return this.#mutatePlugin("/_redevplugin/api/plugins/permissions/revoke", request, options);
|
|
191
171
|
}
|
|
192
172
|
listSecurityPolicies(options = {}) {
|
|
193
|
-
return this.#
|
|
173
|
+
return this.#requestQuery("/_redevplugin/api/plugins/security-policies/query", {}, options);
|
|
194
174
|
}
|
|
195
175
|
getSecurityPolicy(pluginInstanceId, options = {}) {
|
|
196
|
-
return this.#
|
|
176
|
+
return this.#requestQuery(`/_redevplugin/api/plugins/security-policies/${encodeURIComponent(pluginInstanceId)}/query`, {}, options);
|
|
197
177
|
}
|
|
198
178
|
putSecurityPolicy(pluginInstanceId, request, options = {}) {
|
|
199
179
|
return this.#mutatePluginAt("PUT", `/_redevplugin/api/plugins/security-policies/${encodeURIComponent(pluginInstanceId)}`, pluginInstanceId, request, options);
|
|
@@ -211,10 +191,7 @@ export class PluginPlatformClient {
|
|
|
211
191
|
return this.#requestMutation("POST", "/_redevplugin/api/plugins/secrets/delete", request, options);
|
|
212
192
|
}
|
|
213
193
|
listDiagnosticEvents(options = {}, requestOptions = {}) {
|
|
214
|
-
return this.#
|
|
215
|
-
}
|
|
216
|
-
#getJSON(path, options) {
|
|
217
|
-
return this.#requestJSON("GET", path, options);
|
|
194
|
+
return this.#requestQuery("/_redevplugin/api/plugins/diagnostics/query", options, requestOptions);
|
|
218
195
|
}
|
|
219
196
|
#mutatePlugin(path, request, options) {
|
|
220
197
|
return this.#mutatePluginAt("POST", path, request.plugin_instance_id, request, options);
|
|
@@ -232,7 +209,8 @@ export class PluginPlatformClient {
|
|
|
232
209
|
return result;
|
|
233
210
|
}
|
|
234
211
|
async #handleMutationFailure(error, pluginInstanceId, message) {
|
|
235
|
-
|
|
212
|
+
const outcome = pluginMutationOutcome(error);
|
|
213
|
+
if (outcome === "not_committed" || outcome === undefined)
|
|
236
214
|
return;
|
|
237
215
|
const lifecycleErrors = [];
|
|
238
216
|
try {
|
|
@@ -241,28 +219,58 @@ export class PluginPlatformClient {
|
|
|
241
219
|
catch (caught) {
|
|
242
220
|
lifecycleErrors.push(caught);
|
|
243
221
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
222
|
+
if (outcome === "unknown") {
|
|
223
|
+
try {
|
|
224
|
+
this.#onMutationOutcomeUnknown?.(pluginInstanceId);
|
|
225
|
+
}
|
|
226
|
+
catch (caught) {
|
|
227
|
+
lifecycleErrors.push(caught);
|
|
228
|
+
}
|
|
249
229
|
}
|
|
250
230
|
if (lifecycleErrors.length > 0)
|
|
251
231
|
throw new PluginMutationLifecycleError(message, error, lifecycleErrors);
|
|
252
232
|
}
|
|
253
|
-
async #
|
|
254
|
-
|
|
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;
|
|
255
261
|
try {
|
|
256
|
-
|
|
257
|
-
method,
|
|
258
|
-
headers: { "Accept": "application/json" },
|
|
259
|
-
credentials: "same-origin",
|
|
260
|
-
signal: options.signal,
|
|
261
|
-
});
|
|
262
|
+
encodedBody = JSON.stringify(body);
|
|
262
263
|
}
|
|
263
264
|
catch (cause) {
|
|
264
|
-
throw new PluginTransportError(`Plugin platform
|
|
265
|
+
throw new PluginTransportError(`Plugin platform query body serialization failed for ${operation}`, cause);
|
|
265
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);
|
|
266
274
|
return readPlatformResponse(response);
|
|
267
275
|
}
|
|
268
276
|
async #requestMutation(method, path, body, options) {
|
|
@@ -289,14 +297,38 @@ export class PluginPlatformClient {
|
|
|
289
297
|
return readMutationPlatformResponse(response);
|
|
290
298
|
}
|
|
291
299
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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]);
|
|
300
332
|
}
|
|
301
333
|
function surfaceTransportAPIBaseURL(value) {
|
|
302
334
|
if (!value || value.startsWith("/"))
|
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: () => Promise<void> | void): () => void;
|
|
7
|
+
export declare function registerPluginSurface(scope: PluginSurfaceScope, pluginInstanceId: string, dispose: () => Promise<void> | void, invalidate: () => Promise<void> | void): () => void;
|
|
8
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,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const scopes = new WeakMap();
|
|
2
2
|
function canonicalPluginInstanceId(pluginInstanceId) {
|
|
3
3
|
const canonical = typeof pluginInstanceId === "string" ? pluginInstanceId.trim() : "";
|
|
4
4
|
if (!canonical)
|
|
@@ -7,7 +7,7 @@ function canonicalPluginInstanceId(pluginInstanceId) {
|
|
|
7
7
|
}
|
|
8
8
|
export class PluginSurfaceScope {
|
|
9
9
|
constructor() {
|
|
10
|
-
|
|
10
|
+
scopes.set(this, { invalidated: false, registrations: new Map() });
|
|
11
11
|
}
|
|
12
12
|
static create() {
|
|
13
13
|
return new PluginSurfaceScope();
|
|
@@ -17,25 +17,34 @@ export function createPluginSurfaceScope() {
|
|
|
17
17
|
return PluginSurfaceScope.create();
|
|
18
18
|
}
|
|
19
19
|
export const defaultPluginSurfaceScope = createPluginSurfaceScope();
|
|
20
|
-
export function registerPluginSurface(scope, pluginInstanceId, dispose) {
|
|
21
|
-
const state =
|
|
20
|
+
export function registerPluginSurface(scope, pluginInstanceId, dispose, invalidate) {
|
|
21
|
+
const state = scopes.get(scope);
|
|
22
22
|
if (!state)
|
|
23
23
|
throw new TypeError("Plugin surface scope is invalid");
|
|
24
24
|
const canonicalPluginId = canonicalPluginInstanceId(pluginInstanceId);
|
|
25
25
|
const registration = Symbol(canonicalPluginId);
|
|
26
|
-
state.
|
|
27
|
-
|
|
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);
|
|
28
37
|
}
|
|
29
38
|
export async function disposePluginSurfaceScope(scope, pluginInstanceId) {
|
|
30
|
-
const state =
|
|
39
|
+
const state = scopes.get(scope);
|
|
31
40
|
if (!state)
|
|
32
41
|
throw new TypeError("Plugin surface scope is invalid");
|
|
33
42
|
const canonicalPluginId = pluginInstanceId === undefined
|
|
34
43
|
? undefined
|
|
35
44
|
: canonicalPluginInstanceId(pluginInstanceId);
|
|
36
|
-
const selected = [...state.entries()].filter(([, registration]) => canonicalPluginId === undefined || registration.pluginInstanceId === canonicalPluginId);
|
|
45
|
+
const selected = [...state.registrations.entries()].filter(([, registration]) => canonicalPluginId === undefined || registration.pluginInstanceId === canonicalPluginId);
|
|
37
46
|
for (const [key] of selected)
|
|
38
|
-
state.delete(key);
|
|
47
|
+
state.registrations.delete(key);
|
|
39
48
|
const results = await Promise.allSettled(selected.map(([, registration]) => Promise.resolve().then(() => registration.dispose())));
|
|
40
49
|
const failures = results
|
|
41
50
|
.filter((result) => result.status === "rejected")
|
|
@@ -45,3 +54,19 @@ export async function disposePluginSurfaceScope(scope, pluginInstanceId) {
|
|
|
45
54
|
if (failures.length > 1)
|
|
46
55
|
throw new AggregateError(failures, "Plugin surface scope teardown failed");
|
|
47
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");
|
|
72
|
+
}
|
package/dist/surface.js
CHANGED
|
@@ -2289,6 +2289,7 @@ class PluginSurfaceHostImplementation {
|
|
|
2289
2289
|
#closePromise;
|
|
2290
2290
|
#revokePromise;
|
|
2291
2291
|
#unregisterSurfaceScope;
|
|
2292
|
+
#onLocalInvalidation;
|
|
2292
2293
|
#opened = false;
|
|
2293
2294
|
#ready = false;
|
|
2294
2295
|
#disposed = false;
|
|
@@ -2325,7 +2326,7 @@ class PluginSurfaceHostImplementation {
|
|
|
2325
2326
|
this.#onOpeningProgress = options.onOpeningProgress;
|
|
2326
2327
|
this.#onError = options.onError;
|
|
2327
2328
|
hardenPluginSurfaceFrame(this.#iframe);
|
|
2328
|
-
this.#unregisterSurfaceScope = registerPluginSurface(options.surfaceScope ?? defaultPluginSurfaceScope, this.bootstrap.pluginInstanceId, () => this.#disposeLocal());
|
|
2329
|
+
this.#unregisterSurfaceScope = registerPluginSurface(options.surfaceScope ?? defaultPluginSurfaceScope, this.bootstrap.pluginInstanceId, () => this.#disposeLocal(), () => this.#invalidateLocal());
|
|
2329
2330
|
}
|
|
2330
2331
|
async open() {
|
|
2331
2332
|
this.#assertActive();
|
|
@@ -2447,6 +2448,11 @@ class PluginSurfaceHostImplementation {
|
|
|
2447
2448
|
this.#disposeLocal();
|
|
2448
2449
|
return revoke;
|
|
2449
2450
|
}
|
|
2451
|
+
observeLocalInvalidation(observer) {
|
|
2452
|
+
this.#onLocalInvalidation = observer;
|
|
2453
|
+
if (this.#disposed)
|
|
2454
|
+
observer();
|
|
2455
|
+
}
|
|
2450
2456
|
async #closeSurface() {
|
|
2451
2457
|
const startedAt = performance.now();
|
|
2452
2458
|
const quiesce = await this.#quiesceSurface();
|
|
@@ -2512,6 +2518,16 @@ class PluginSurfaceHostImplementation {
|
|
|
2512
2518
|
}
|
|
2513
2519
|
this.#iframe.srcdoc = "";
|
|
2514
2520
|
}
|
|
2521
|
+
#invalidateLocal() {
|
|
2522
|
+
this.#disposeLocal();
|
|
2523
|
+
this.#iframe.remove?.();
|
|
2524
|
+
try {
|
|
2525
|
+
this.#onLocalInvalidation?.();
|
|
2526
|
+
}
|
|
2527
|
+
catch {
|
|
2528
|
+
// Slot state observers cannot weaken local session containment.
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2515
2531
|
async #handlePortMessage(event) {
|
|
2516
2532
|
if (this.#disposed || !messageWithinLimit(event.data))
|
|
2517
2533
|
return;
|
|
@@ -3201,6 +3217,7 @@ function createPluginSurfaceOpeningLease(request) {
|
|
|
3201
3217
|
let unregister = () => undefined;
|
|
3202
3218
|
let revokePromise;
|
|
3203
3219
|
let removeAbortListener = () => undefined;
|
|
3220
|
+
let invalidationObserver = () => undefined;
|
|
3204
3221
|
const lease = {
|
|
3205
3222
|
options: Promise.resolve(undefined),
|
|
3206
3223
|
pluginInstanceId: request.pluginInstanceId,
|
|
@@ -3222,7 +3239,23 @@ function createPluginSurfaceOpeningLease(request) {
|
|
|
3222
3239
|
return Promise.resolve();
|
|
3223
3240
|
return lease.revoke();
|
|
3224
3241
|
},
|
|
3242
|
+
invalidate() {
|
|
3243
|
+
lease.cancelled = true;
|
|
3244
|
+
if (lease.owner === "host" || lease.owner === "invalidated")
|
|
3245
|
+
return;
|
|
3246
|
+
lease.owner = "invalidated";
|
|
3247
|
+
unregister();
|
|
3248
|
+
removeAbortListener();
|
|
3249
|
+
invalidationObserver();
|
|
3250
|
+
},
|
|
3251
|
+
observeInvalidation(observer) {
|
|
3252
|
+
invalidationObserver = observer;
|
|
3253
|
+
if (lease.owner === "invalidated")
|
|
3254
|
+
observer();
|
|
3255
|
+
},
|
|
3225
3256
|
revoke() {
|
|
3257
|
+
if (lease.owner === "invalidated")
|
|
3258
|
+
return Promise.resolve();
|
|
3226
3259
|
if (lease.owner === "host") {
|
|
3227
3260
|
throw new PluginBridgeError("PLUGIN_CONTRACT_MISMATCH", "Plugin surface ownership was already adopted");
|
|
3228
3261
|
}
|
|
@@ -3236,12 +3269,17 @@ function createPluginSurfaceOpeningLease(request) {
|
|
|
3236
3269
|
return revokePromise;
|
|
3237
3270
|
},
|
|
3238
3271
|
};
|
|
3239
|
-
unregister = registerPluginSurface(request.surfaceScope, request.pluginInstanceId, () => lease.cancel());
|
|
3240
|
-
|
|
3241
|
-
lease.options = Promise.
|
|
3272
|
+
unregister = registerPluginSurface(request.surfaceScope, request.pluginInstanceId, () => lease.cancel(), () => lease.invalidate());
|
|
3273
|
+
if (lease.owner === "invalidated") {
|
|
3274
|
+
lease.options = Promise.reject(new PluginBridgeError("PLUGIN_BRIDGE_DISPOSED", "Plugin surface scope is invalidated"));
|
|
3242
3275
|
}
|
|
3243
|
-
|
|
3244
|
-
|
|
3276
|
+
else {
|
|
3277
|
+
try {
|
|
3278
|
+
lease.options = Promise.resolve(request.open());
|
|
3279
|
+
}
|
|
3280
|
+
catch (error) {
|
|
3281
|
+
lease.options = Promise.reject(error);
|
|
3282
|
+
}
|
|
3245
3283
|
}
|
|
3246
3284
|
void lease.options.catch(() => {
|
|
3247
3285
|
if (lease.owner !== "opening")
|
|
@@ -3301,6 +3339,12 @@ export class PluginSurfaceSlot {
|
|
|
3301
3339
|
const controller = new AbortController();
|
|
3302
3340
|
this.#transitionController = controller;
|
|
3303
3341
|
this.#setState("opening");
|
|
3342
|
+
lease?.observeInvalidation(() => {
|
|
3343
|
+
if (this.#transitionController !== controller)
|
|
3344
|
+
return;
|
|
3345
|
+
controller.abort();
|
|
3346
|
+
this.#setState("empty");
|
|
3347
|
+
});
|
|
3304
3348
|
const transition = this.#tail.then(() => this.#openSurface(resolvedOptions, controller, lease), async (error) => {
|
|
3305
3349
|
await this.#revokeOpening(lease);
|
|
3306
3350
|
throw error;
|
|
@@ -3319,9 +3363,12 @@ export class PluginSurfaceSlot {
|
|
|
3319
3363
|
resolvedOptions = await options;
|
|
3320
3364
|
}
|
|
3321
3365
|
catch (error) {
|
|
3322
|
-
if (!this.#disposed && this.#transitionController === controller && !controller
|
|
3366
|
+
if (!this.#disposed && this.#transitionController === controller && !this.#openingCancelled(controller, lease)) {
|
|
3323
3367
|
this.#setState("error", toBridgeError(error, "PLUGIN_BRIDGE_HANDSHAKE_FAILED"));
|
|
3324
3368
|
}
|
|
3369
|
+
else {
|
|
3370
|
+
this.#setCancelledState(controller);
|
|
3371
|
+
}
|
|
3325
3372
|
throw error;
|
|
3326
3373
|
}
|
|
3327
3374
|
if (lease && resolvedOptions.bootstrap.pluginInstanceId !== lease.pluginInstanceId) {
|
|
@@ -3360,6 +3407,17 @@ export class PluginSurfaceSlot {
|
|
|
3360
3407
|
}
|
|
3361
3408
|
throw error;
|
|
3362
3409
|
}
|
|
3410
|
+
host.observeLocalInvalidation(() => {
|
|
3411
|
+
if (this.#active === host)
|
|
3412
|
+
this.#active = undefined;
|
|
3413
|
+
if (this.#opening === host)
|
|
3414
|
+
this.#opening = undefined;
|
|
3415
|
+
if (this.#transitionController === controller)
|
|
3416
|
+
controller.abort();
|
|
3417
|
+
host.element.remove();
|
|
3418
|
+
if (!this.#disposed)
|
|
3419
|
+
this.#setState("empty");
|
|
3420
|
+
});
|
|
3363
3421
|
this.#adoptOpening(lease);
|
|
3364
3422
|
this.#opening = host;
|
|
3365
3423
|
setSurfaceInteractive(host.element, false);
|
|
@@ -3788,7 +3846,8 @@ function isBridgeResponse(value) {
|
|
|
3788
3846
|
if (value.ok !== false || typeof value.error_code !== "string" || !pluginBridgeErrorCodeSet.has(value.error_code) ||
|
|
3789
3847
|
typeof value.error !== "string" || value.error.length > 4096 ||
|
|
3790
3848
|
!Object.keys(value).every((key) => ["type", "id", "ok", "error_code", "error", "error_details", "mutation_outcome"].includes(key)) ||
|
|
3791
|
-
(value.mutation_outcome !== undefined && value.mutation_outcome !== "
|
|
3849
|
+
(value.mutation_outcome !== undefined && value.mutation_outcome !== "committed" &&
|
|
3850
|
+
value.mutation_outcome !== "not_committed" && value.mutation_outcome !== "unknown")) {
|
|
3792
3851
|
return false;
|
|
3793
3852
|
}
|
|
3794
3853
|
if (value.error_code === "PLUGIN_CAPABILITY_ERROR")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floegence/redevplugin-ui",
|
|
3
|
-
"version": "0.5
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
"files": [
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@floegence/redevplugin-contracts": "0.6.5"
|
|
36
|
+
},
|
|
34
37
|
"publishConfig": {
|
|
35
38
|
"access": "public"
|
|
36
39
|
},
|