fix: simplify custom widget package workbench
This commit is contained in:
1 parent
9a52a035aa
commit
48042a08ff
37 files changed
+1155
-608
No files matched your search
@@ -90,6 +90,8 @@ export const widgetCollectionContentsSchema = z
|
||||
else if (!shared) issue(["connections", name], `collection slot '${slot}' is undeclared`);
|
||||
else if (!isWidgetConnectionCompatible(requirement, shared))
|
||||
issue(["connections", name], `slot '${slot}' requires compatible ${requirement.kind} settings`);
|
||||
else if (Boolean(requirement.multiple) !== Boolean(shared.multiple))
|
||||
issue(["connections", name], `slot '${slot}' must use the same repeatable connection setting`);
|
||||
else if (!requirement.optional && shared.optional)
|
||||
issue(["connections", name], `slot '${slot}' must be required because this widget requires it`);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,17 @@ export class WidgetPackageProcess {
|
||||
public get stopped() {
|
||||
return this.dead;
|
||||
}
|
||||
public inspect() {
|
||||
let state: "running" | "stopped" = "running";
|
||||
if (this.dead) state = "stopped";
|
||||
return {
|
||||
state,
|
||||
pid: this.child.pid ?? null,
|
||||
activeRequests: this.pending.size,
|
||||
queuedRequests: this.queued,
|
||||
lastUsed: this.lastUsed,
|
||||
};
|
||||
}
|
||||
public waitUntilReady() {
|
||||
return this.ready;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,9 @@ export const widgetPackageConnectionSchema = z
|
||||
label: z.string().trim().min(1).max(128),
|
||||
kind: z.enum(["http", "integration", "service"]),
|
||||
serviceType: identifier.optional(),
|
||||
integrationKind: identifier.optional(),
|
||||
// Optional fields preserve the digest of existing package archives.
|
||||
multiple: z.boolean().optional(),
|
||||
optional: z.boolean().default(false),
|
||||
description: z.string().max(2048).optional(),
|
||||
})
|
||||
@@ -89,14 +92,41 @@ export const widgetPackageConnectionSchema = z
|
||||
|
||||
/** Shared by binding pickers, activation and portable collection imports. */
|
||||
export function isWidgetConnectionCompatible(
|
||||
requirement: { kind: string; serviceType?: string },
|
||||
configuration: { kind?: unknown; serviceType?: unknown },
|
||||
requirement: { kind: string; serviceType?: string; integrationKind?: string },
|
||||
configuration: { kind?: unknown; serviceType?: unknown; integrationKind?: unknown },
|
||||
) {
|
||||
if (requirement.kind !== configuration.kind) return false;
|
||||
if (requirement.kind === "service") return requirement.serviceType === configuration.serviceType;
|
||||
if (requirement.kind === "integration" && requirement.integrationKind)
|
||||
return requirement.integrationKind === configuration.integrationKind;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Repeatable connections retain the original named binding and add stable, namespaced members.
|
||||
* A colon cannot appear in a manifest connection name, so groups cannot overlap.
|
||||
*/
|
||||
export function getWidgetConnectionBindingNames(
|
||||
name: string,
|
||||
requirement: { multiple?: boolean },
|
||||
bindings: Record<string, string>,
|
||||
) {
|
||||
return Object.keys(bindings).filter(
|
||||
(key) => Boolean(bindings[key]) && (key === name || (requirement.multiple && key.startsWith(`${name}:`))),
|
||||
);
|
||||
}
|
||||
|
||||
export function getWidgetConnectionRequirement<T extends { multiple?: boolean }>(
|
||||
requirements: Record<string, T>,
|
||||
name: string,
|
||||
) {
|
||||
const exact = requirements[name];
|
||||
if (exact) return exact;
|
||||
const prefix = name.split(":")[0];
|
||||
const group = prefix && requirements[prefix];
|
||||
if (group && group.multiple) return group;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const customWidgetPackageSchema = z
|
||||
.strictObject({
|
||||
$schema: z.literal(CUSTOM_WIDGET_PACKAGE_SCHEMA),
|
||||
|
||||
@@ -100,6 +100,17 @@ export class CustomWidgetPackageSupervisor {
|
||||
if (pending) (await pending.catch(() => undefined))?.stop();
|
||||
}
|
||||
|
||||
/** Read only: inspecting an inactive artifact must never start its server. */
|
||||
public async inspect(digest: string) {
|
||||
const pending = this.processes.get(digest);
|
||||
if (!pending) return { state: "idle" as const };
|
||||
try {
|
||||
return (await pending).inspect();
|
||||
} catch (error) {
|
||||
return { state: "failed" as const, error: error instanceof Error ? error.message : String(error) };
|
||||
}
|
||||
}
|
||||
|
||||
public async cancelInstances(instanceIds: string[], options: { actions?: boolean } = {}): Promise<void> {
|
||||
for (const id of instanceIds) this.instanceGenerations.set(id, (this.instanceGenerations.get(id) ?? 0) + 1);
|
||||
const ids = new Set(instanceIds);
|
||||
|
||||
Reference in new issue
Block a user