fix: harden invalidation registry governance
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import type {
|
||||
CommandEffectDescriptor,
|
||||
InstalledContractContribution,
|
||||
InstalledHttpContract,
|
||||
RuntimeValidator,
|
||||
} from "../../src/contracts/external-contract-runtime.ts";
|
||||
|
||||
function zodValidator<T>(
|
||||
schemaId: string,
|
||||
schema: z.ZodType<T>,
|
||||
): RuntimeValidator<T> {
|
||||
return Object.freeze({
|
||||
schemaId,
|
||||
safeParse(value: unknown) {
|
||||
const result = schema.safeParse(value);
|
||||
if (result.success) {
|
||||
return Object.freeze({ success: true as const, data: result.data });
|
||||
}
|
||||
return Object.freeze({
|
||||
success: false as const,
|
||||
issues: Object.freeze(
|
||||
result.error.issues.map((issue) =>
|
||||
Object.freeze({
|
||||
path: Object.freeze(
|
||||
issue.path.map((segment) =>
|
||||
typeof segment === "number" ? segment : String(segment),
|
||||
),
|
||||
),
|
||||
code: String(issue.code),
|
||||
}),
|
||||
),
|
||||
),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const entitySchema = z
|
||||
.object({
|
||||
id: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
})
|
||||
.strip();
|
||||
|
||||
const problemSchema = z
|
||||
.object({
|
||||
type: z.string().min(1),
|
||||
title: z.string().min(1),
|
||||
status: z.int().min(100).max(599),
|
||||
})
|
||||
.strip();
|
||||
|
||||
type TestProblem = z.output<typeof problemSchema>;
|
||||
|
||||
const problemValidator = zodValidator("TestProblem", problemSchema);
|
||||
const readPolicy = Object.freeze({
|
||||
policyId: "TEST_READ_V1",
|
||||
requestByteLimit: 0,
|
||||
responseByteLimit: 32_768,
|
||||
totalDeadlineMs: 10_000,
|
||||
retryBudget: 2 as const,
|
||||
authProfileId: "TEST_AUTH",
|
||||
diagnosticsOperation: "test.read",
|
||||
});
|
||||
|
||||
export const TEST_LIST_HTTP_CONTRACT: InstalledHttpContract<
|
||||
Readonly<{ limit: number }>,
|
||||
readonly z.output<typeof entitySchema>[],
|
||||
TestProblem
|
||||
> = Object.freeze({
|
||||
contract: Object.freeze({
|
||||
operationId: "TEST_LIST_ENTITIES",
|
||||
method: "GET" as const,
|
||||
pathTemplate: "/api/test-entities",
|
||||
inputValidator: zodValidator(
|
||||
"TestEntityListQuery",
|
||||
z
|
||||
.object({
|
||||
limit: z.coerce.number().int().min(1).max(100).default(20),
|
||||
})
|
||||
.strict(),
|
||||
),
|
||||
outputValidator: zodValidator(
|
||||
"TestEntityListPayload",
|
||||
z.array(entitySchema).max(100),
|
||||
),
|
||||
problemValidator,
|
||||
acceptedStatuses: Object.freeze([200]),
|
||||
emptyBodyStatuses: Object.freeze([]),
|
||||
retrySemantics: "SAFE" as const,
|
||||
requestBody: "NONE" as const,
|
||||
responseBody: "REQUIRED_JSON" as const,
|
||||
commandRecovery: null,
|
||||
commandEffect: null,
|
||||
projectRequest(input: Readonly<{ limit: number }>) {
|
||||
return Object.freeze({
|
||||
pathValues: Object.freeze({}),
|
||||
queryEntries: Object.freeze([
|
||||
Object.freeze(["limit", String(input.limit)] as const),
|
||||
]),
|
||||
body: null,
|
||||
});
|
||||
},
|
||||
}),
|
||||
frontend: readPolicy,
|
||||
});
|
||||
|
||||
export const TEST_DETAIL_HTTP_CONTRACT: InstalledHttpContract<
|
||||
Readonly<{ entityId: string }>,
|
||||
z.output<typeof entitySchema>,
|
||||
TestProblem
|
||||
> = Object.freeze({
|
||||
contract: Object.freeze({
|
||||
operationId: "TEST_GET_ENTITY",
|
||||
method: "GET" as const,
|
||||
pathTemplate: "/api/test-entities/{entityId}",
|
||||
inputValidator: zodValidator(
|
||||
"TestEntityParams",
|
||||
z.object({ entityId: z.string().min(1) }).strict(),
|
||||
),
|
||||
outputValidator: zodValidator("TestEntityPayload", entitySchema),
|
||||
problemValidator,
|
||||
acceptedStatuses: Object.freeze([200]),
|
||||
emptyBodyStatuses: Object.freeze([]),
|
||||
retrySemantics: "SAFE" as const,
|
||||
requestBody: "NONE" as const,
|
||||
responseBody: "REQUIRED_JSON" as const,
|
||||
commandRecovery: null,
|
||||
commandEffect: null,
|
||||
projectRequest(input: Readonly<{ entityId: string }>) {
|
||||
return Object.freeze({
|
||||
pathValues: Object.freeze({ entityId: input.entityId }),
|
||||
queryEntries: Object.freeze([]),
|
||||
body: null,
|
||||
});
|
||||
},
|
||||
}),
|
||||
frontend: Object.freeze({
|
||||
...readPolicy,
|
||||
policyId: "TEST_DETAIL_V1",
|
||||
diagnosticsOperation: "test.detail",
|
||||
}),
|
||||
});
|
||||
|
||||
const commandEffect: CommandEffectDescriptor<TestProblem> = Object.freeze({
|
||||
successEffect: "APPLIED_CONFIRMED" as const,
|
||||
classifyProblem({
|
||||
status,
|
||||
}: Readonly<{ status: number; problem: TestProblem }>) {
|
||||
return status === 400 ? "NOT_APPLIED" : "MAYBE_APPLIED";
|
||||
},
|
||||
});
|
||||
|
||||
export const TEST_CREATE_HTTP_CONTRACT: InstalledHttpContract<
|
||||
Readonly<{ name: string }>,
|
||||
z.output<typeof entitySchema>,
|
||||
TestProblem
|
||||
> = Object.freeze({
|
||||
contract: Object.freeze({
|
||||
operationId: "TEST_CREATE_ENTITY",
|
||||
method: "POST" as const,
|
||||
pathTemplate: "/api/test-entities",
|
||||
inputValidator: zodValidator(
|
||||
"TestCreateEntityCommand",
|
||||
z.object({ name: z.string().trim().min(1) }).strict(),
|
||||
),
|
||||
outputValidator: zodValidator("TestEntityPayload", entitySchema),
|
||||
problemValidator,
|
||||
acceptedStatuses: Object.freeze([201]),
|
||||
emptyBodyStatuses: Object.freeze([]),
|
||||
retrySemantics: "KEYED" as const,
|
||||
requestBody: "JSON" as const,
|
||||
responseBody: "REQUIRED_JSON" as const,
|
||||
commandRecovery: Object.freeze({
|
||||
mode: "IDEMPOTENCY_REPLAY" as const,
|
||||
operationIdentityField: "idempotencyKey",
|
||||
}),
|
||||
commandEffect,
|
||||
projectRequest(input: Readonly<{ name: string }>) {
|
||||
return Object.freeze({
|
||||
pathValues: Object.freeze({}),
|
||||
queryEntries: Object.freeze([]),
|
||||
body: Object.freeze({ ...input }),
|
||||
});
|
||||
},
|
||||
}),
|
||||
frontend: Object.freeze({
|
||||
policyId: "TEST_CREATE_V1",
|
||||
requestByteLimit: 32_768,
|
||||
responseByteLimit: 32_768,
|
||||
totalDeadlineMs: 10_000,
|
||||
retryBudget: 0 as const,
|
||||
authProfileId: "TEST_AUTH",
|
||||
diagnosticsOperation: "test.create",
|
||||
}),
|
||||
});
|
||||
|
||||
export const TEST_CONTRACT_CONTRIBUTION: InstalledContractContribution =
|
||||
Object.freeze({
|
||||
contributionId: "test-http-v1",
|
||||
featureId: "test-feature",
|
||||
source: Object.freeze({
|
||||
kind: "EXTERNAL_PACKAGE" as const,
|
||||
package: Object.freeze({
|
||||
packageId: "@test/contracts",
|
||||
version: "1.0.0",
|
||||
digest: `sha256:${"a".repeat(64)}`,
|
||||
runtimeProtocolVersion: 1 as const,
|
||||
sourceRevision: "abcdef1",
|
||||
}),
|
||||
}),
|
||||
http: Object.freeze([
|
||||
TEST_LIST_HTTP_CONTRACT,
|
||||
TEST_DETAIL_HTTP_CONTRACT,
|
||||
TEST_CREATE_HTTP_CONTRACT,
|
||||
]) as readonly InstalledHttpContract<unknown, unknown, unknown>[],
|
||||
events: Object.freeze([]),
|
||||
});
|
||||
Reference in New Issue
Block a user