jenkins b2cfbc94e8 hermes(webui): track the ignored HUX-04 artifacts card models
The artifacts/ directory matched a repo-wide ignore rule and was left
out of the UI card commit; its suites only passed locally because the
files existed on disk. Force-track the complete card so the source
push carries every module the tests import.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
2026-08-24 04:19:03 -03:00

95 lines
4.1 KiB
TypeScript

/** Same-origin endpoint descriptions for a future HUX-04 backend adapter. */
import { artifactsEnabled } from "./model.ts";
import { isOpaqueId, normalizeIdentity, scopedPath } from "./security.ts";
import type {
ArtifactEndpoint,
ArtifactEndpointContract,
ArtifactScope,
HuxIdentity,
} from "./types.ts";
interface FoundationClient {
apiVersion: string;
identity: HuxIdentity;
endpoint(path: string): string;
}
function endpoint(method: ArtifactEndpoint["method"], path: string, responseSchema: string,
requiredBody?: readonly string[]): ArtifactEndpoint {
return Object.freeze({method, path, responseSchema, ...(requiredBody ? {requiredBody} : {})});
}
function versionNumber(value: number): string {
if (!Number.isSafeInteger(value) || value < 1) throw new TypeError("Artifact version is invalid");
return String(value);
}
/**
* The server derives identity from the authenticated session and verifies the
* project/conversation path binding. Mutation bodies MUST carry
* expected_current_version and return 409 on stale state. Existing versions
* are never PATCHed or deleted; edits append a new content_ref-backed version.
*/
export function createArtifactEndpointContract(client: FoundationClient, scope: ArtifactScope,
flags?: Iterable<string>): ArtifactEndpointContract | null {
if (!artifactsEnabled(flags)) return null;
if (client.apiVersion !== "hux.v1") throw new TypeError("HUX v1 client required");
normalizeIdentity({tenant_ref: client.identity.tenantRef, user_ref: client.identity.userRef,
surface: client.identity.surface});
const collection = scopedPath("/projects", scope);
function itemPath(artifactId: string): string {
if (!isOpaqueId(artifactId, "art")) throw new TypeError("Opaque artifact id required");
return scopedPath("/projects", scope, artifactId);
}
function itemEndpoint(artifactId: string, suffix = ""): string {
return client.endpoint(`${itemPath(artifactId)}${suffix}`);
}
return Object.freeze({
list: endpoint("GET", client.endpoint(collection), "hux.artifact_workspace.v1"),
item(artifactId) {
return endpoint("GET", itemEndpoint(artifactId), "hux.artifact_response.v1");
},
preview(artifactId, version) {
return endpoint("GET", itemEndpoint(artifactId, `/versions/${versionNumber(version)}/preview`),
"hux.artifact_preview.v1");
},
diff(artifactId, fromVersion, toVersion) {
if (fromVersion >= toVersion) throw new TypeError("Artifact diff range is invalid");
return endpoint("POST", itemEndpoint(artifactId, "/diffs"), "hux.artifact_diff.v1",
Object.freeze(["from_version", "to_version", "expected_current_version"]));
},
appendVersion(artifactId) {
return endpoint("POST", itemEndpoint(artifactId, "/versions"), "hux.artifact_response.v1",
Object.freeze(["content_ref", "expected_current_version"]));
},
continueEdit(artifactId) {
return endpoint("POST", itemEndpoint(artifactId, "/continue"), "hux.artifact_edit_intent.v1",
Object.freeze(["version", "expected_current_version"]));
},
authorize(artifactId, action) {
if (action !== "download" && action !== "share") throw new TypeError("Artifact action is invalid");
return endpoint("POST", itemEndpoint(artifactId, `/authorizations/${action}`),
"hux.artifact_authorization.v1", Object.freeze(["expected_current_version"]));
},
download(artifactId, version) {
return endpoint("GET", itemEndpoint(artifactId, `/versions/${versionNumber(version)}/download`),
"hux.artifact_content.v1");
},
share(artifactId, version) {
return endpoint("POST", itemEndpoint(artifactId, `/versions/${versionNumber(version)}/shares`),
"hux.artifact_share.v1", Object.freeze(["expected_current_version"]));
},
promote(artifactId) {
return endpoint("POST", itemEndpoint(artifactId, "/promotions"), "hux.artifact_response.v1",
Object.freeze(["target_project_id", "version", "expected_current_version"]));
},
sources(artifactId) {
return endpoint("GET", itemEndpoint(artifactId, "/sources"), "hux.artifact_sources.v1");
},
});
}