Normalize C# provider advertisements at Eve boundary
This commit is contained in:
parent
4338a42c6e
commit
cdea1c8f9e
4 changed files with 175 additions and 2 deletions
6
packages/eve-contracts/dist/index.d.ts
vendored
6
packages/eve-contracts/dist/index.d.ts
vendored
|
|
@ -52,3 +52,9 @@ export declare const parseEveCommandInvocation: (value: unknown) => EveCommandIn
|
||||||
export declare const parseEveCommandReceipt: (value: unknown) => EveCommandReceipt;
|
export declare const parseEveCommandReceipt: (value: unknown) => EveCommandReceipt;
|
||||||
export declare const parseEveCommandResult: (value: unknown) => EveCommandResult;
|
export declare const parseEveCommandResult: (value: unknown) => EveCommandResult;
|
||||||
export declare const parseEveInputCapability: (value: unknown) => EveInputCapability;
|
export declare const parseEveInputCapability: (value: unknown) => EveInputCapability;
|
||||||
|
/**
|
||||||
|
* Converts the canonical positional MessagePack representation used by the C#
|
||||||
|
* Eve package into the JSON-shaped provider contract used at browser/model
|
||||||
|
* boundaries. This is serialization lowering, not a second advertisement DTO.
|
||||||
|
*/
|
||||||
|
export declare function normalizeEveProviderAdvertisement(value: unknown): unknown;
|
||||||
|
|
|
||||||
73
packages/eve-contracts/dist/index.js
vendored
73
packages/eve-contracts/dist/index.js
vendored
|
|
@ -44,7 +44,7 @@ export const isEveCommandInvocation = (value) => isEveContract("commandInvocatio
|
||||||
export const isEveCommandReceipt = (value) => isEveContract("commandReceipt", value);
|
export const isEveCommandReceipt = (value) => isEveContract("commandReceipt", value);
|
||||||
export const isEveCommandResult = (value) => isEveContract("commandResult", value);
|
export const isEveCommandResult = (value) => isEveContract("commandResult", value);
|
||||||
export const isEveInputCapability = (value) => isEveContract("inputCapability", value);
|
export const isEveInputCapability = (value) => isEveContract("inputCapability", value);
|
||||||
export const parseEveProviderAdvertisement = (value) => parseEveContract("providerAdvertisement", value);
|
export const parseEveProviderAdvertisement = (value) => parseEveContract("providerAdvertisement", normalizeEveProviderAdvertisement(value));
|
||||||
export const parseEveSurfaceDocument = (value) => parseEveContract("surface", value);
|
export const parseEveSurfaceDocument = (value) => parseEveContract("surface", value);
|
||||||
export const parseEveCommandDescriptor = (value) => parseEveContract("commandDescriptor", value);
|
export const parseEveCommandDescriptor = (value) => parseEveContract("commandDescriptor", value);
|
||||||
export const parseEveCommandInvocation = (value) => parseEveContract("commandInvocation", value);
|
export const parseEveCommandInvocation = (value) => parseEveContract("commandInvocation", value);
|
||||||
|
|
@ -62,3 +62,74 @@ function requiredValidator(schemaId) {
|
||||||
throw new Error(`Eve contract schema ${schemaId} was not registered.`);
|
throw new Error(`Eve contract schema ${schemaId} was not registered.`);
|
||||||
return validator;
|
return validator;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Converts the canonical positional MessagePack representation used by the C#
|
||||||
|
* Eve package into the JSON-shaped provider contract used at browser/model
|
||||||
|
* boundaries. This is serialization lowering, not a second advertisement DTO.
|
||||||
|
*/
|
||||||
|
export function normalizeEveProviderAdvertisement(value) {
|
||||||
|
if (!Array.isArray(value))
|
||||||
|
return value;
|
||||||
|
if (value.length < 13)
|
||||||
|
return value;
|
||||||
|
const [schema, providerId, serviceId, verseId, title, kind, cultMeshAddress, updatedAtUtc, freshness, schemas, witnesses, surfaces, commands, authorizedBodyProducerIds] = value;
|
||||||
|
return {
|
||||||
|
schema,
|
||||||
|
providerId,
|
||||||
|
serviceId,
|
||||||
|
verseId,
|
||||||
|
title,
|
||||||
|
kind,
|
||||||
|
cultMeshAddress,
|
||||||
|
updatedAtUtc,
|
||||||
|
freshness: normalizeFreshness(freshness),
|
||||||
|
schemas,
|
||||||
|
witnesses: Array.isArray(witnesses) ? witnesses.map(normalizeWitness) : witnesses,
|
||||||
|
surfaces: Array.isArray(surfaces) ? surfaces.map(normalizeSurfaceAdvertisement) : surfaces,
|
||||||
|
commands: Array.isArray(commands) ? commands.map(normalizeAdvertisedCommand) : commands,
|
||||||
|
...(Array.isArray(authorizedBodyProducerIds) ? { authorizedBodyProducerIds } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function normalizeFreshness(value) {
|
||||||
|
return Array.isArray(value)
|
||||||
|
? { state: value[0], lastSeenAtUtc: value[1], maxAgeMs: value[2] }
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
function normalizeWitness(value) {
|
||||||
|
return Array.isArray(value)
|
||||||
|
? { kind: value[0], ref: value[1], summary: value[2] }
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
function normalizeSurfaceAdvertisement(value) {
|
||||||
|
if (!Array.isArray(value))
|
||||||
|
return value;
|
||||||
|
return {
|
||||||
|
surfaceId: value[0],
|
||||||
|
schema: value[1],
|
||||||
|
recordRef: value[2],
|
||||||
|
transport: value[3],
|
||||||
|
status: value[4],
|
||||||
|
surfaceKind: value[5],
|
||||||
|
...(value[6] ? { worldInteraction: normalizeWorldInteraction(value[6]) } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function normalizeWorldInteraction(value) {
|
||||||
|
if (!Array.isArray(value))
|
||||||
|
return value;
|
||||||
|
return {
|
||||||
|
projectionKind: value[0],
|
||||||
|
stateSchemas: value[1],
|
||||||
|
commandBoundary: value[2],
|
||||||
|
commandRecordRef: value[3],
|
||||||
|
receiptSchema: value[4],
|
||||||
|
receiptRecordRef: value[5],
|
||||||
|
assetManifestRecordRef: value[6],
|
||||||
|
loweringTargets: value[7],
|
||||||
|
ownership: value[8],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function normalizeAdvertisedCommand(value) {
|
||||||
|
return Array.isArray(value)
|
||||||
|
? { command: value[0], surfaceId: value[1], transport: value[2], summary: value[3] }
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ export const isEveInputCapability = (value: unknown): value is EveInputCapabilit
|
||||||
isEveContract("inputCapability", value);
|
isEveContract("inputCapability", value);
|
||||||
|
|
||||||
export const parseEveProviderAdvertisement = (value: unknown): EveProviderAdvertisement =>
|
export const parseEveProviderAdvertisement = (value: unknown): EveProviderAdvertisement =>
|
||||||
parseEveContract("providerAdvertisement", value);
|
parseEveContract("providerAdvertisement", normalizeEveProviderAdvertisement(value));
|
||||||
export const parseEveSurfaceDocument = (value: unknown): EveSurfaceDocument =>
|
export const parseEveSurfaceDocument = (value: unknown): EveSurfaceDocument =>
|
||||||
parseEveContract("surface", value);
|
parseEveContract("surface", value);
|
||||||
export const parseEveCommandDescriptor = (value: unknown): EveCommandDescriptor =>
|
export const parseEveCommandDescriptor = (value: unknown): EveCommandDescriptor =>
|
||||||
|
|
@ -114,3 +114,77 @@ function requiredValidator(schemaId: string): ValidateFunction {
|
||||||
if (!validator) throw new Error(`Eve contract schema ${schemaId} was not registered.`);
|
if (!validator) throw new Error(`Eve contract schema ${schemaId} was not registered.`);
|
||||||
return validator;
|
return validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the canonical positional MessagePack representation used by the C#
|
||||||
|
* Eve package into the JSON-shaped provider contract used at browser/model
|
||||||
|
* boundaries. This is serialization lowering, not a second advertisement DTO.
|
||||||
|
*/
|
||||||
|
export function normalizeEveProviderAdvertisement(value: unknown): unknown {
|
||||||
|
if (!Array.isArray(value)) return value;
|
||||||
|
if (value.length < 13) return value;
|
||||||
|
const [schema, providerId, serviceId, verseId, title, kind, cultMeshAddress, updatedAtUtc,
|
||||||
|
freshness, schemas, witnesses, surfaces, commands, authorizedBodyProducerIds] = value;
|
||||||
|
return {
|
||||||
|
schema,
|
||||||
|
providerId,
|
||||||
|
serviceId,
|
||||||
|
verseId,
|
||||||
|
title,
|
||||||
|
kind,
|
||||||
|
cultMeshAddress,
|
||||||
|
updatedAtUtc,
|
||||||
|
freshness: normalizeFreshness(freshness),
|
||||||
|
schemas,
|
||||||
|
witnesses: Array.isArray(witnesses) ? witnesses.map(normalizeWitness) : witnesses,
|
||||||
|
surfaces: Array.isArray(surfaces) ? surfaces.map(normalizeSurfaceAdvertisement) : surfaces,
|
||||||
|
commands: Array.isArray(commands) ? commands.map(normalizeAdvertisedCommand) : commands,
|
||||||
|
...(Array.isArray(authorizedBodyProducerIds) ? { authorizedBodyProducerIds } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeFreshness(value: unknown): unknown {
|
||||||
|
return Array.isArray(value)
|
||||||
|
? { state: value[0], lastSeenAtUtc: value[1], maxAgeMs: value[2] }
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeWitness(value: unknown): unknown {
|
||||||
|
return Array.isArray(value)
|
||||||
|
? { kind: value[0], ref: value[1], summary: value[2] }
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSurfaceAdvertisement(value: unknown): unknown {
|
||||||
|
if (!Array.isArray(value)) return value;
|
||||||
|
return {
|
||||||
|
surfaceId: value[0],
|
||||||
|
schema: value[1],
|
||||||
|
recordRef: value[2],
|
||||||
|
transport: value[3],
|
||||||
|
status: value[4],
|
||||||
|
surfaceKind: value[5],
|
||||||
|
...(value[6] ? { worldInteraction: normalizeWorldInteraction(value[6]) } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeWorldInteraction(value: unknown): unknown {
|
||||||
|
if (!Array.isArray(value)) return value;
|
||||||
|
return {
|
||||||
|
projectionKind: value[0],
|
||||||
|
stateSchemas: value[1],
|
||||||
|
commandBoundary: value[2],
|
||||||
|
commandRecordRef: value[3],
|
||||||
|
receiptSchema: value[4],
|
||||||
|
receiptRecordRef: value[5],
|
||||||
|
assetManifestRecordRef: value[6],
|
||||||
|
loweringTargets: value[7],
|
||||||
|
ownership: value[8],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeAdvertisedCommand(value: unknown): unknown {
|
||||||
|
return Array.isArray(value)
|
||||||
|
? { command: value[0], surfaceId: value[1], transport: value[2], summary: value[3] }
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,28 @@ test("validates an existing canonical Eve surface fixture", async () => {
|
||||||
assert.equal(parseEveSurfaceDocument(fixture).providerId, "eve.world-smoke");
|
assert.equal(parseEveSurfaceDocument(fixture).providerId, "eve.world-smoke");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("provider parser lowers the canonical C# MessagePack tuple without app-specific decoding", () => {
|
||||||
|
const advertisement = parseEveProviderAdvertisement([
|
||||||
|
"gamecult.eve.provider_advertisement.v1",
|
||||||
|
"aetheria",
|
||||||
|
"aetheria-daemon",
|
||||||
|
"aetheria.local",
|
||||||
|
"Aetheria",
|
||||||
|
"game.runtime",
|
||||||
|
"cultmesh://aetheria",
|
||||||
|
"2026-08-22T00:00:00Z",
|
||||||
|
["fresh", "2026-08-22T00:00:00Z", 15_000],
|
||||||
|
["gamecult.eve.surface.v1"],
|
||||||
|
[["cultcache", "aetheria.cc", "typed state"]],
|
||||||
|
[["aetheria.hangar", "gamecult.eve.surface.v1", "eve:surface:aetheria.hangar", "cultmesh-record", "active", "interactive-world",
|
||||||
|
["provider-authored-world-surface", ["aetheria.frame.v1"], "aetheria.daemon.commands", "eve:commands", "gamecult.eve.command_receipt.v1", "eve:receipts", "eve:assets", ["web-reference"], "provider owns truth"]]],
|
||||||
|
[["hangar.select_verse", "aetheria.hangar", "cultmesh", "Select Verse"]],
|
||||||
|
["aetheria-daemon.soa"],
|
||||||
|
]);
|
||||||
|
assert.equal(advertisement.surfaces[0].worldInteraction.commandBoundary, "aetheria.daemon.commands");
|
||||||
|
assert.equal(advertisement.witnesses[0].ref, "aetheria.cc");
|
||||||
|
});
|
||||||
|
|
||||||
test("command results validate persisted receipts and transient Eve projections together", () => {
|
test("command results validate persisted receipts and transient Eve projections together", () => {
|
||||||
const result = parseEveCommandResult({
|
const result = parseEveCommandResult({
|
||||||
schema: "gamecult.eve.command_result.v1",
|
schema: "gamecult.eve.command_result.v1",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue