Export local provider catalog coverage
This commit is contained in:
parent
9dc4e0ef81
commit
e063828c2c
8 changed files with 346 additions and 2 deletions
|
|
@ -811,7 +811,11 @@ Recently cut:
|
|||
advertisement-backed entries for Aetheria, Repixelizer, and Sai through the
|
||||
same `loadProviderAdvertisement` path used by the provider picker. Fixture-only
|
||||
surfaces remain explicit local catalog entries until they gain provider
|
||||
advertisements or move into their owner packs.
|
||||
advertisements or move into their owner packs. The fallback catalog is now
|
||||
schema-backed as `gamecult.eve.local_provider_catalog.v1`, copied into the
|
||||
conformance export, and indexed as `localProviderCatalogs[]` so external
|
||||
consumers can verify which provider advertisements the browser reference uses
|
||||
without importing Eve's source layout.
|
||||
- Provider advertisements can now name sidecar plugin requirements per surface
|
||||
with `surfaces[].requiresPlugins[]`. The parity harness validates those
|
||||
requirements against known plugin manifests, exports them in the conformance
|
||||
|
|
@ -947,7 +951,10 @@ Recently cut:
|
|||
- The surface contract now states nested Norn and TeX placement as optional
|
||||
composition, not Sai custody. The plugin-owner consumer can assert
|
||||
provider-plugin requirement availability, so `sai.vn` remains required while
|
||||
`norn.graph` and `tex.math` stay optional nested sidecar plugins.
|
||||
`norn.graph` and `tex.math` stay optional nested sidecar plugins. The browser
|
||||
fallback catalog only advertises the Sai provider surface; independent Norn
|
||||
and TeX availability is discovered through that provider's plugin
|
||||
requirements and the separate plugin advertisements.
|
||||
- Sai, Norn, and TeX now have plugin ABI fixtures for describe, validate,
|
||||
project, lower, measure, and apply behavior. The parity harness validates
|
||||
those fixtures against plugin manifests, so Eve proves the ABI shape without
|
||||
|
|
|
|||
|
|
@ -170,6 +170,15 @@ The conformance export includes runtime `capabilityManifestPath` and
|
|||
`capabilityManifestErrors` fields so external consumers can distinguish a
|
||||
generic projection runtime from a runtime that can project plugin-declared
|
||||
capabilities.
|
||||
Browser reference fallback discovery is exported as `localProviderCatalogs[]`.
|
||||
Each record names the runtime, source catalog path, exported catalog copy,
|
||||
schema, purpose, provider ids, advertisement paths, surface count, status, and
|
||||
validation errors. This proves which local provider/plugin advertisements the
|
||||
web picker is consuming without turning the fallback file into live discovery
|
||||
truth. Consumer smokes can assert entries with
|
||||
`--expect-local-provider-catalog <runtimeId:status:path-substring>`,
|
||||
`--expect-local-provider-catalog-provider <runtimeId:providerId>`, and
|
||||
`--expect-local-provider-catalog-advertisement <runtimeId:advertisement-path-substring>`.
|
||||
|
||||
Plugin handoff paths are exported as `plugins[].handoffPath` for owner repos
|
||||
such as Sai, Norn, and EvePlugins. These paths are incubation evidence for
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"providerPluginRequirementCoverage",
|
||||
"runtimePluginProjectionCoverage",
|
||||
"providerRuntimePluginProjectionCoverage",
|
||||
"localProviderCatalogs",
|
||||
"interactiveWorldSurfaces",
|
||||
"worldSurfaceLoweringCoverage",
|
||||
"commandBoundaryCoverage",
|
||||
|
|
@ -352,6 +353,68 @@
|
|||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"localProviderCatalogs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"runtimeId",
|
||||
"ownerRepo",
|
||||
"catalogPath",
|
||||
"catalogExportPath",
|
||||
"schema",
|
||||
"purpose",
|
||||
"status",
|
||||
"providerIds",
|
||||
"advertisementPaths",
|
||||
"surfaceCount",
|
||||
"errors"
|
||||
],
|
||||
"properties": {
|
||||
"runtimeId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"ownerRepo": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"catalogPath": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"catalogExportPath": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"purpose": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"providerIds": {
|
||||
"type": "array"
|
||||
},
|
||||
"advertisementPaths": {
|
||||
"type": "array"
|
||||
},
|
||||
"surfaceCount": {
|
||||
"type": "number"
|
||||
},
|
||||
"errors": {
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"splitTargetBlockers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
|
|||
95
schemas/gamecult.eve.local_provider_catalog.v1.schema.json
Normal file
95
schemas/gamecult.eve.local_provider_catalog.v1.schema.json
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "gamecult.eve.local_provider_catalog.v1",
|
||||
"title": "Eve Local Provider Catalog",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema",
|
||||
"purpose",
|
||||
"providers"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"const": "gamecult.eve.local_provider_catalog.v1"
|
||||
},
|
||||
"purpose": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"providers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"providerId",
|
||||
"title",
|
||||
"kind",
|
||||
"surfaces"
|
||||
],
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"aliases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"advertisement": {
|
||||
"type": "string"
|
||||
},
|
||||
"freshness": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"state"
|
||||
],
|
||||
"properties": {
|
||||
"state": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"surfaces": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"transport",
|
||||
"surfaceId"
|
||||
],
|
||||
"properties": {
|
||||
"transport": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
|
@ -45,10 +45,18 @@ try {
|
|||
--expect-schema gamecult.eve.runtime_split_handoff.v1 `
|
||||
--expect-schema gamecult.eve.plugin_handoff.v1 `
|
||||
--expect-schema gamecult.eve.provider_handoff.v1 `
|
||||
--expect-schema gamecult.eve.local_provider_catalog.v1 `
|
||||
--expect-schema gamecult.eve.electron_shell_projection.v1 `
|
||||
--expect-schema gamecult.eve.tui_grid.v1 `
|
||||
--expect-schema gamecult.eve.web_layout_probe.v1 `
|
||||
--expect-capability-gap runtime:Fensalir:direct2d:capture:missing `
|
||||
--expect-local-provider-catalog web:valid:local-provider-catalog.json `
|
||||
--expect-local-provider-catalog-provider web:aetheria `
|
||||
--expect-local-provider-catalog-provider web:repixelizer `
|
||||
--expect-local-provider-catalog-provider web:gamecult.home.vn `
|
||||
--expect-local-provider-catalog-advertisement web:aetheria.provider-advertisement.json `
|
||||
--expect-local-provider-catalog-advertisement web:repixelizer.provider-advertisement.json `
|
||||
--expect-local-provider-catalog-advertisement web:sai-vn.provider-advertisement.json `
|
||||
--expect-world-lowering-coverage aetheria:aetheria.daemon.game:web-reference:claimed:Eve:web `
|
||||
--expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-uitoolkit:claimed:EveUnity:unity-uitoolkit `
|
||||
--expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-scene:claimed:EveUnity:unity-scene `
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ if (!exportDirectory) {
|
|||
" --expect-runtime-plugin-projection <runtimeId:pluginId:status:ownerRepo>",
|
||||
" --expect-provider-runtime-plugin-projection <providerId:surfaceId:runtimeId:pluginId:status:runtimeOwnerRepo>",
|
||||
" --expect-runtime-plugin-gap <runtimeId:pluginId:ownerRepo>",
|
||||
" --expect-local-provider-catalog <runtimeId:status:path-substring>",
|
||||
" --expect-local-provider-catalog-provider <runtimeId:providerId>",
|
||||
" --expect-local-provider-catalog-advertisement <runtimeId:advertisement-path-substring>",
|
||||
" --expect-world-lowering-coverage <providerId:surfaceId:targetId:status:ownerRepo:runtimeId>",
|
||||
" --expect-command-boundary-coverage <providerId:surfaceId:targetId:status:ownerRepo:runtimeId>",
|
||||
" --expect-world-lowering-gap <providerId:surfaceId:targetId:ownerRepo:runtimeId>",
|
||||
|
|
@ -143,6 +146,7 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
const runtimePluginProjectionCoverage = Array.isArray(index.runtimePluginProjectionCoverage) ? index.runtimePluginProjectionCoverage : [];
|
||||
const providerRuntimePluginProjectionCoverage = Array.isArray(index.providerRuntimePluginProjectionCoverage) ? index.providerRuntimePluginProjectionCoverage : [];
|
||||
const runtimePluginProjectionGaps = Array.isArray(index.runtimePluginProjectionGaps) ? index.runtimePluginProjectionGaps : [];
|
||||
const localProviderCatalogs = Array.isArray(index.localProviderCatalogs) ? index.localProviderCatalogs : [];
|
||||
const interactiveWorldSurfaces = Array.isArray(index.interactiveWorldSurfaces) ? index.interactiveWorldSurfaces : [];
|
||||
const worldSurfaceLoweringCoverage = Array.isArray(index.worldSurfaceLoweringCoverage) ? index.worldSurfaceLoweringCoverage : [];
|
||||
const commandBoundaryCoverage = Array.isArray(index.commandBoundaryCoverage) ? index.commandBoundaryCoverage : [];
|
||||
|
|
@ -174,6 +178,7 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
validateRuntimePluginProjectionCoverage(index.runtimePluginProjectionCoverage, errors);
|
||||
validateProviderRuntimePluginProjectionCoverage(index.providerRuntimePluginProjectionCoverage, errors);
|
||||
validateRuntimePluginProjectionGaps(index.runtimePluginProjectionGaps, errors);
|
||||
validateLocalProviderCatalogs(index.localProviderCatalogs, directory, errors);
|
||||
validateSplitTargetBlockers(index.splitTargetBlockers, errors);
|
||||
validateSplitHandoffMoveCoverage(index.splitHandoffMoveCoverage, errors);
|
||||
validatePluginHandoffMoveCoverage(index.pluginHandoffMoveCoverage, errors);
|
||||
|
|
@ -234,6 +239,39 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
errors.push(`runtimePluginProjectionGaps:${expectation.runtimeId}:${expectation.pluginId}:reason:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.localProviderCatalogs) {
|
||||
const catalog = localProviderCatalogs.find(candidate => candidate.runtimeId === expectation.runtimeId);
|
||||
if (!catalog) {
|
||||
errors.push(`localProviderCatalogs:${expectation.runtimeId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (catalog.status !== expectation.status) {
|
||||
errors.push(`localProviderCatalogs:${expectation.runtimeId}:status:expected ${expectation.status} got ${catalog.status || ""}`);
|
||||
}
|
||||
if (!catalog.catalogPath?.includes(expectation.pathSubstring) && !catalog.catalogExportPath?.includes(expectation.pathSubstring)) {
|
||||
errors.push(`localProviderCatalogs:${expectation.runtimeId}:path:expected substring ${expectation.pathSubstring}`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.localProviderCatalogProviders) {
|
||||
const catalog = localProviderCatalogs.find(candidate => candidate.runtimeId === expectation.runtimeId);
|
||||
if (!catalog) {
|
||||
errors.push(`localProviderCatalogs:${expectation.runtimeId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (!Array.isArray(catalog.providerIds) || !catalog.providerIds.includes(expectation.providerId)) {
|
||||
errors.push(`localProviderCatalogs:${expectation.runtimeId}:provider:${expectation.providerId}:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.localProviderCatalogAdvertisements) {
|
||||
const catalog = localProviderCatalogs.find(candidate => candidate.runtimeId === expectation.runtimeId);
|
||||
if (!catalog) {
|
||||
errors.push(`localProviderCatalogs:${expectation.runtimeId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (!Array.isArray(catalog.advertisementPaths) || !catalog.advertisementPaths.some(candidate => candidate.includes(expectation.pathSubstring))) {
|
||||
errors.push(`localProviderCatalogs:${expectation.runtimeId}:advertisement:${expectation.pathSubstring}:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.worldLoweringCoverage) {
|
||||
const coverage = worldSurfaceLoweringCoverage.find(candidate =>
|
||||
candidate.providerId === expectation.providerId &&
|
||||
|
|
@ -1070,6 +1108,25 @@ function validateProviderPluginRequirementCoverage(records, errors) {
|
|||
}
|
||||
}
|
||||
|
||||
function validateLocalProviderCatalogs(catalogs, directory, errors) {
|
||||
if (!Array.isArray(catalogs)) {
|
||||
errors.push("localProviderCatalogs:missing");
|
||||
return;
|
||||
}
|
||||
for (const [index, catalog] of catalogs.entries()) {
|
||||
for (const field of ["runtimeId", "ownerRepo", "catalogPath", "catalogExportPath", "schema", "purpose", "status"]) {
|
||||
if (!catalog?.[field]) errors.push(`localProviderCatalogs:${index}:${field}:missing`);
|
||||
}
|
||||
for (const field of ["providerIds", "advertisementPaths", "errors"]) {
|
||||
if (!Array.isArray(catalog?.[field])) errors.push(`localProviderCatalogs:${index}:${field}:expected array`);
|
||||
}
|
||||
if (typeof catalog?.surfaceCount !== "number") errors.push(`localProviderCatalogs:${index}:surfaceCount:expected number`);
|
||||
if (catalog?.catalogExportPath && !existsSync(path.join(directory, catalog.catalogExportPath))) {
|
||||
errors.push(`localProviderCatalogs:${index}:catalogExportPath:${catalog.catalogExportPath}:missing`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateInteractiveWorldSurfaces(surfaces, errors) {
|
||||
if (!Array.isArray(surfaces)) {
|
||||
errors.push("interactiveWorldSurfaces:missing");
|
||||
|
|
@ -1197,6 +1254,9 @@ function parseArguments(args) {
|
|||
runtimePluginProjectionCoverage: [],
|
||||
providerRuntimePluginProjectionCoverage: [],
|
||||
runtimePluginGaps: [],
|
||||
localProviderCatalogs: [],
|
||||
localProviderCatalogProviders: [],
|
||||
localProviderCatalogAdvertisements: [],
|
||||
worldLoweringCoverage: [],
|
||||
commandBoundaryCoverage: [],
|
||||
worldLoweringGaps: [],
|
||||
|
|
@ -1251,6 +1311,9 @@ function parseArguments(args) {
|
|||
["--expect-runtime-plugin-projection", expectations.runtimePluginProjectionCoverage],
|
||||
["--expect-provider-runtime-plugin-projection", expectations.providerRuntimePluginProjectionCoverage],
|
||||
["--expect-runtime-plugin-gap", expectations.runtimePluginGaps],
|
||||
["--expect-local-provider-catalog", expectations.localProviderCatalogs],
|
||||
["--expect-local-provider-catalog-provider", expectations.localProviderCatalogProviders],
|
||||
["--expect-local-provider-catalog-advertisement", expectations.localProviderCatalogAdvertisements],
|
||||
["--expect-world-lowering-coverage", expectations.worldLoweringCoverage],
|
||||
["--expect-command-boundary-coverage", expectations.commandBoundaryCoverage],
|
||||
["--expect-world-lowering-gap", expectations.worldLoweringGaps],
|
||||
|
|
@ -1361,6 +1424,12 @@ function parseArguments(args) {
|
|||
target.push(parseRuntimePluginProjectionCoverageExpectation(value));
|
||||
} else if (option === "--expect-provider-runtime-plugin-projection") {
|
||||
target.push(parseProviderRuntimePluginProjectionCoverageExpectation(value));
|
||||
} else if (option === "--expect-local-provider-catalog") {
|
||||
target.push(parseLocalProviderCatalogExpectation(value));
|
||||
} else if (option === "--expect-local-provider-catalog-provider") {
|
||||
target.push(parseLocalProviderCatalogProviderExpectation(value));
|
||||
} else if (option === "--expect-local-provider-catalog-advertisement") {
|
||||
target.push(parseLocalProviderCatalogAdvertisementExpectation(value));
|
||||
} else {
|
||||
target.push(value);
|
||||
}
|
||||
|
|
@ -1384,6 +1453,43 @@ function parseScreenshotMetricExpectation(value) {
|
|||
};
|
||||
}
|
||||
|
||||
function parseLocalProviderCatalogExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length !== 3 || parts.some(part => !part)) {
|
||||
console.error(`Expected local provider catalog in <runtimeId:status:path-substring> form, got: ${value}`);
|
||||
process.exit(2);
|
||||
}
|
||||
return {
|
||||
runtimeId: parts[0],
|
||||
status: parts[1],
|
||||
pathSubstring: parts[2],
|
||||
};
|
||||
}
|
||||
|
||||
function parseLocalProviderCatalogProviderExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length !== 2 || parts.some(part => !part)) {
|
||||
console.error(`Expected local provider catalog provider in <runtimeId:providerId> form, got: ${value}`);
|
||||
process.exit(2);
|
||||
}
|
||||
return {
|
||||
runtimeId: parts[0],
|
||||
providerId: parts[1],
|
||||
};
|
||||
}
|
||||
|
||||
function parseLocalProviderCatalogAdvertisementExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length !== 2 || parts.some(part => !part)) {
|
||||
console.error(`Expected local provider catalog advertisement in <runtimeId:advertisement-path-substring> form, got: ${value}`);
|
||||
process.exit(2);
|
||||
}
|
||||
return {
|
||||
runtimeId: parts[0],
|
||||
pathSubstring: parts[1],
|
||||
};
|
||||
}
|
||||
|
||||
function parseRuntimeCaptureArtifactExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length !== 5 || parts.some(part => !part)) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
"gamecult.eve.web_layout_probe.v1": "schemas/gamecult.eve.web_layout_probe.v1.schema.json",
|
||||
"gamecult.eve.provider_scenario.v1": "schemas/gamecult.eve.provider_scenario.v1.schema.json",
|
||||
"gamecult.eve.provider_advertisement.v1": "schemas/gamecult.eve.provider_advertisement.v1.schema.json",
|
||||
"gamecult.eve.local_provider_catalog.v1": "schemas/gamecult.eve.local_provider_catalog.v1.schema.json",
|
||||
"gamecult.eve.provider_handoff.v1": "schemas/gamecult.eve.provider_handoff.v1.schema.json",
|
||||
"gamecult.eve.conformance_handoff.v1": "schemas/gamecult.eve.conformance_handoff.v1.schema.json",
|
||||
"gamecult.eve.electron_shell_projection.v1": "schemas/gamecult.eve.electron_shell_projection.v1.schema.json",
|
||||
|
|
|
|||
|
|
@ -502,6 +502,14 @@ async function validateRuntimeLocalProviderCatalog(runtime) {
|
|||
return [`${catalogPath}:invalid-json:${error instanceof Error ? error.message : String(error)}`];
|
||||
}
|
||||
|
||||
errors.push(...await validateJsonDocument(
|
||||
manifest.schemas?.["gamecult.eve.local_provider_catalog.v1"],
|
||||
catalogPath,
|
||||
{
|
||||
schema: "gamecult.eve.local_provider_catalog.v1",
|
||||
},
|
||||
));
|
||||
|
||||
const providers = Array.isArray(catalog.providers) ? catalog.providers : [];
|
||||
const providerIds = new Set(providers.map(provider => provider.providerId).filter(Boolean));
|
||||
for (const providerId of catalogConfig.requiredProviders || []) {
|
||||
|
|
@ -1990,6 +1998,7 @@ function buildConformanceExport(report) {
|
|||
runtimePluginProjectionCoverage: collectRuntimePluginProjectionCoverage(report),
|
||||
runtimePluginProjectionGaps: collectRuntimePluginProjectionGaps(report),
|
||||
providerRuntimePluginProjectionCoverage: collectProviderRuntimePluginProjectionCoverage(report),
|
||||
localProviderCatalogs: collectLocalProviderCatalogs(report),
|
||||
interactiveWorldSurfaces: collectInteractiveWorldSurfaces(report),
|
||||
worldSurfaceLoweringCoverage: collectWorldSurfaceLoweringCoverage(report),
|
||||
commandBoundaryCoverage: collectCommandBoundaryCoverage(report),
|
||||
|
|
@ -2073,6 +2082,44 @@ function buildSchemaCatalog() {
|
|||
.sort((left, right) => left.schemaId.localeCompare(right.schemaId));
|
||||
}
|
||||
|
||||
function collectLocalProviderCatalogs(report) {
|
||||
return (report.runtimes || [])
|
||||
.filter(runtime => runtime.localProviderCatalog?.path)
|
||||
.map(runtime => {
|
||||
const catalogPath = runtime.localProviderCatalog.path;
|
||||
let catalog = {};
|
||||
try {
|
||||
catalog = JSON.parse(readFileSync(path.join(repoRoot, catalogPath), "utf8"));
|
||||
} catch {
|
||||
catalog = {};
|
||||
}
|
||||
const providers = Array.isArray(catalog.providers) ? catalog.providers : [];
|
||||
const catalogDirectory = path.posix.dirname(normalizePath(catalogPath));
|
||||
return {
|
||||
runtimeId: runtime.id,
|
||||
ownerRepo: runtime.ownerRepo || "",
|
||||
catalogPath,
|
||||
catalogExportPath: makeLocalProviderCatalogExportPath(runtime.id, catalogPath),
|
||||
schema: catalog.schema || "",
|
||||
purpose: catalog.purpose || "",
|
||||
status: (runtime.localProviderCatalogErrors || []).length ? "invalid" : "valid",
|
||||
providerIds: providers.map(provider => provider.providerId).filter(Boolean).sort(),
|
||||
advertisementPaths: providers
|
||||
.map(provider => resolveCatalogPath(catalogDirectory, provider.advertisement || ""))
|
||||
.filter(Boolean)
|
||||
.sort(),
|
||||
surfaceCount: providers.reduce((count, provider) => count + (Array.isArray(provider.surfaces) ? provider.surfaces.length : 0), 0),
|
||||
errors: runtime.localProviderCatalogErrors || [],
|
||||
};
|
||||
})
|
||||
.sort((left, right) => left.runtimeId.localeCompare(right.runtimeId));
|
||||
}
|
||||
|
||||
function makeLocalProviderCatalogExportPath(runtimeId, catalogPath) {
|
||||
if (!runtimeId || !catalogPath) return "";
|
||||
return `catalogs/${runtimeId}/${path.basename(catalogPath)}`;
|
||||
}
|
||||
|
||||
function buildCapabilityMatrix(report) {
|
||||
const packs = (report.conformancePacks || []).map(pack => {
|
||||
const fixtures = (report.fixtures || []).filter(fixture => fixture.pack === pack.id);
|
||||
|
|
@ -3121,6 +3168,14 @@ async function writeConformanceExport(conformanceExport, directory) {
|
|||
await mkdir(path.dirname(destinationPath), { recursive: true });
|
||||
await writeFile(destinationPath, await readFile(sourcePath, "utf8"));
|
||||
}
|
||||
for (const catalog of conformanceExport.localProviderCatalogs || []) {
|
||||
if (!catalog.catalogPath || !catalog.catalogExportPath) continue;
|
||||
const sourcePath = path.join(repoRoot, catalog.catalogPath);
|
||||
if (!existsSync(sourcePath)) continue;
|
||||
const destinationPath = path.join(directory, catalog.catalogExportPath);
|
||||
await mkdir(path.dirname(destinationPath), { recursive: true });
|
||||
await writeFile(destinationPath, await readFile(sourcePath, "utf8"));
|
||||
}
|
||||
}
|
||||
|
||||
function collectConformanceHandoffs(conformanceExport) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue