openvibers/openvibe.contracts Commits

finish-f1: Changed: scripts/generate.js, test/run.js and README.md,…

OP OpenVibers committed 1 Oct 2026 · bf8216e562 · 4 files, +3918 −292

Handed to the local harness by the cloud job pr-OpenVibe.Contracts-2; done by Claude opus.

Changes

ddiff --git a/README.md b/README.md
iindex 8f945d83..46529b36 100644
---- a/README.md
++++ b/README.md
@@@ -301,7 +301,8 @@ following exist here (plan §12.12):
6. a migration/seed strategy, a security/threat review, and sitemap/robots/feed behaviour;
7. acceptance tests proving the advertised functionality.
--The launch release removes the domain from `OpenVibe.Sites/sites.json`, switches routing and
++The launch release moves the domain's catalog entry from `manifests/products/<domain>.json` to its
++service manifest's `site` (`registry.service-manifest` 1.1.0, without `launch`), switches routing and
registers maturity in the ecosystem registry atomically. A placeholder is never counted as an
implemented service.
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex ad0864d6..bb2f4472 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -296,283 +296,3726 @@ export interface ServiceManifest {
* The catalog's description, verbatim.
*/
description?: string;
-- pillars?: Pillars;
-- faq?: Faq;
++ /**
++ * Feature pillars, in display order.
++ *
++ * @maxItems 8
++ */
++ pillars?:
++ | []
++ | [
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ];
++ /**
++ * @maxItems 20
++ */
++ faq?:
++ | []
++ | [
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ];
/**
* Comma-separated search keywords, verbatim.
*/
keywords?: string;
-- vision?: Vision;
-- highlight?: Highlight;
/**
-- * What a person still has to do or decide before the product launches. Absent once nothing blocks it.
++ * The product page's vision section (badge, call to action, design preview, steps, choices, features, connections), kept verbatim from the catalog: a presentation payload the showcase renders as it is, not a domain contract.
++ */
++ vision?: {
++ [k: string]: unknown | undefined;
++ };
++ /**
++ * A featured block on the product page.
++ */
++ highlight?: {
++ title: string;
++ text: string;
++ /**
++ * @maxItems 10
++ */
++ points?:
++ | []
++ | [string]
++ | [string, string]
++ | [string, string, string]
++ | [string, string, string, string]
++ | [string, string, string, string, string]
++ | [string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string, string, string, string];
++ cta?: {
++ label: string;
++ url: string;
++ note?: string;
++ };
++ more?: {
++ label: string;
++ url: string;
++ note?: string;
++ };
++ };
++ /**
++ * What a person still has to do or decide before the product launches. Absent once nothing blocks it.
++ */
++ launch?: string;
++ /**
++ * How the product relates to services, repositories and other pages.
++ */
++ relationships?: {
++ /**
++ * The service manifest (manifests/services/<id>.json) that serves or will serve this product, when one exists.
++ */
++ service?: string;
++ /**
++ * The repository planned or used for the product (under github.com/OpenVibers).
++ */
++ plannedRepo?: string;
++ /**
++ * No repository exists for the product yet.
++ */
++ noRepo?: true;
++ /**
++ * The sentence introducing links.
++ */
++ linksLead?: string;
++ /**
++ * Where to go instead (a closed, moved or status address).
++ *
++ * @maxItems 10
++ */
++ links?:
++ | []
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ];
++ /**
++ * What already works elsewhere on the network while the product is not launched.
++ *
++ * @maxItems 10
++ */
++ meanwhile?:
++ | []
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ];
++ /**
++ * The status API a status page reads.
++ */
++ statusApi?: string;
++ };
++ };
++ capabilities: string[];
++ eventsProduced: string[];
++ eventsConsumed: string[];
++ namespacesOwned: string[];
++ contractRanges?: {
++ [k: string]: string | undefined;
++ };
++ /**
++ * How the service starts, stops, recovers and is rolled back (roadmap WS-P task 1), taken from its code and its systemd units, never from intent. A part that does not apply is { "none": "<why>" }. `ovhost validate` refuses a host service whose lifecycle lacks a field, whose shutdown deadline exceeds its unit's TimeoutStopSec, or whose installed openvibe-contracts is outside contracts.range.
++ */
++ lifecycle?: {
++ /**
++ * The endpoint that says the process is alive, and what a 200 from it proves. Readiness (can it serve?) is `ready`.
++ */
++ liveness:
++ | {
++ /**
++ * A path on internalOrigin; the same as `health`.
++ */
++ endpoint: string;
++ /**
++ * What a 200 proves, and what it does not check.
++ */
++ means: string;
++ }
++ | {
++ /**
++ * Why there is none.
++ */
++ none: string;
++ };
++ /**
++ * What happens between the stop signal and the process exiting.
++ */
++ shutdown:
++ | {
++ /**
++ * The signal the unit sends (KillSignal=, systemd's default SIGTERM) and the process handles.
++ */
++ signal: "SIGTERM" | "SIGINT" | "SIGQUIT" | "SIGHUP";
++ /**
++ * The longest the process takes from the signal to exiting, as its own code bounds it (a forced-exit timer); 0 when it exits at once. Must not exceed the unit's TimeoutStopSec, after which systemd kills it mid-drain.
++ */
++ deadlineSeconds: number;
++ /**
++ * What it stops, finishes, flushes or closes before exiting, in order.
++ *
++ * @minItems 1
++ */
++ drains: [string, ...string[]];
++ /**
++ * Worker units the service owns that drain on their own (ovhost never restarts them): how long a stop signal lets them run.
++ */
++ workers?: {
++ /**
++ * The longest a worker keeps running after the stop signal, as its code bounds it.
++ */
++ deadlineSeconds: number;
++ /**
++ * What a worker keeps serving, and what it ends, before exiting.
++ *
++ * @minItems 1
++ */
++ drains: [string, ...string[]];
++ };
++ /**
++ * Anything the fields above do not say (a gap the code has, a handler that is missing).
++ */
++ note?: string;
++ }
++ | {
++ /**
++ * Why there is none.
++ */
++ none: string;
++ };
++ /**
++ * Work a stop or a crash interrupted that the next start picks up.
++ */
++ startupRecovery:
++ | {
++ /**
++ * Each thing the process resumes at boot.
++ *
++ * @minItems 1
++ */
++ resumes: [
++ {
++ /**
++ * outbox: unsent events; jobs: queued or interrupted background work; sessions: client connections or media sessions; consumer: an event or change feed redelivered or read from a stored cursor; schedule: timed work that fell due; state: in-memory state rebuilt from storage.
++ */
++ kind: "outbox" | "jobs" | "sessions" | "consumer" | "schedule" | "state";
++ /**
++ * What is resumed, and how.
++ */
++ what: string;
++ },
++ ...{
++ /**
++ * outbox: unsent events; jobs: queued or interrupted background work; sessions: client connections or media sessions; consumer: an event or change feed redelivered or read from a stored cursor; schedule: timed work that fell due; state: in-memory state rebuilt from storage.
++ */
++ kind: "outbox" | "jobs" | "sessions" | "consumer" | "schedule" | "state";
++ /**
++ * What is resumed, and how.
++ */
++ what: string;
++ }[]
++ ];
++ }
++ | {
++ /**
++ * Why there is none.
++ */
++ none: string;
++ };
++ /**
++ * When a release is rolled back, for how long that happens by itself, and what makes going back unsafe.
++ */
++ rollback:
++ | {
++ /**
++ * What triggers a rollback: automatic (readiness not reached after the restart) and manual.
++ *
++ * @minItems 1
++ */
++ conditions: [string, ...string[]];
++ /**
++ * How long after a deploy the rollback is automatic, and what bounds a manual one.
++ */
++ window: string;
++ /**
++ * Changes a previous release cannot live with (forward-only migrations, table rebuilds, authority switches), or { none } with the reason.
++ */
++ blockers:
++ | [string, ...string[]]
++ | {
++ /**
++ * Why there is none.
++ */
++ none: string;
++ };
++ }
++ | {
++ /**
++ * Why there is none.
++ */
++ none: string;
++ };
++ /**
++ * The openvibe-contracts versions the service accepts.
++ */
++ contracts:
++ | {
++ /**
++ * The accepted versions, the same range as contractRanges['openvibe-contracts'].
++ */
++ range: string;
++ /**
++ * Where the range comes from, when it is not the obvious one.
++ */
++ note?: string;
++ }
++ | {
++ /**
++ * Why there is none.
++ */
++ none: string;
++ };
++ /**
++ * What the service claims so that one owner acts at a time, and how a stale owner is fenced.
++ */
++ leases:
++ | {
++ /**
++ * Each thing claimed, with its holder, expiry and fencing.
++ *
++ * @minItems 1
++ */
++ claims: [
++ {
++ /**
++ * What is claimed.
++ */
++ what: string;
++ /**
++ * Who holds the claim (a process, a worker id, a row).
++ */
++ holder: string;
++ /**
++ * When the claim lapses and how it is renewed.
++ */
++ expires: string;
++ /**
++ * How a stale owner is kept from acting after its claim lapsed.
++ */
++ fencing: string;
++ },
++ ...{
++ /**
++ * What is claimed.
++ */
++ what: string;
++ /**
++ * Who holds the claim (a process, a worker id, a row).
++ */
++ holder: string;
++ /**
++ * When the claim lapses and how it is renewed.
++ */
++ expires: string;
++ /**
++ * How a stale owner is kept from acting after its claim lapsed.
++ */
++ fencing: string;
++ }[]
++ ];
++ }
++ | {
++ /**
++ * Why there is none.
++ */
++ none: string;
++ };
++ };
++ notes?: string;
++}
++
++/** [email protected] (owner: network) */
++/**
++ * A product domain of the network's catalog (manifests/products/<domain>.json) that has no service manifest presenting it as a site: a planned product, a sub-domain of a service, a brand domain of a multi-domain service, a closed or moved address. Carries what the network's showcase pages need, copied from the OpenVibe.Sites catalog (plan T11 lane D) so Sites can be deleted. A product whose service manifest has a site block carries the same fields there (registry.service-manifest 1.1.0 site); every catalog domain has exactly one of the two homes. The $defs are shared with that site block.
++ */
++export interface Product {
++ /**
++ * The product's host, unique across the catalog.
++ */
++ domain: string;
++ /**
++ * The product's short name used everywhere (frame service names, /shared paths), e.g. food for openvibe.food. Not unique: a moved product keeps it on both addresses.
++ */
++ tld: string;
++ /**
++ * Full name, e.g. OpenVibe.Food.
++ */
++ name: string;
++ /**
++ * Icon name of the showcase pages (a Font Awesome name such as fa-utensils).
++ */
++ icon: string;
++ /**
++ * Brand colour, lower-case hex.
++ */
++ accent: string;
++ tagline: string;
++ /**
++ * The catalog's description, verbatim.
++ */
++ description: string;
++ /**
++ * Which legal wording its /terms, /privacy and /dmca use (openvibe-shared/legal), as in a service manifest's site.
++ */
++ legalProfile?: "streaming" | "tools" | "ugc" | "games" | "hosting" | "account" | "info";
++ /**
++ * Absent for a product page. closed: the address was decided against and points elsewhere; moved: the product lives at another address; status: the address shows the network's status. relationships.links say where.
++ */
++ kind?: "closed" | "moved" | "status";
++ /**
++ * Feature pillars, in display order.
++ *
++ * @maxItems 8
++ */
++ pillars?:
++ | []
++ | [
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ]
++ | [
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ },
++ {
++ title: string;
++ text: string;
++ }
++ ];
++ /**
++ * @maxItems 20
++ */
++ faq?:
++ | []
++ | [
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ]
++ | [
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ },
++ {
++ question: string;
++ answer: string;
++ }
++ ];
++ /**
++ * Comma-separated search keywords, verbatim.
++ */
++ keywords: string;
++ /**
++ * The product page's vision section (badge, call to action, design preview, steps, choices, features, connections), kept verbatim from the catalog: a presentation payload the showcase renders as it is, not a domain contract.
++ */
++ vision?: {
++ [k: string]: unknown | undefined;
++ };
++ /**
++ * A featured block on the product page.
++ */
++ highlight?: {
++ title: string;
++ text: string;
++ /**
++ * @maxItems 10
*/
-- launch?: string;
-- relationships?: Relationships;
-- };
-- capabilities: string[];
-- eventsProduced: string[];
-- eventsConsumed: string[];
-- namespacesOwned: string[];
-- contractRanges?: {
-- [k: string]: string | undefined;
++ points?:
++ | []
++ | [string]
++ | [string, string]
++ | [string, string, string]
++ | [string, string, string, string]
++ | [string, string, string, string, string]
++ | [string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string, string, string, string];
++ cta?: {
++ label: string;
++ url: string;
++ note?: string;
++ };
++ more?: {
++ label: string;
++ url: string;
++ note?: string;
++ };
};
/**
-- * How the service starts, stops, recovers and is rolled back (roadmap WS-P task 1), taken from its code and its systemd units, never from intent. A part that does not apply is { "none": "<why>" }. `ovhost validate` refuses a host service whose lifecycle lacks a field, whose shutdown deadline exceeds its unit's TimeoutStopSec, or whose installed openvibe-contracts is outside contracts.range.
++ * What a person still has to do or decide before the product launches. Absent once nothing blocks it.
*/
-- lifecycle?: {
++ launch?: string;
++ /**
++ * How the product relates to services, repositories and other pages.
++ */
++ relationships?: {
/**
-- * The endpoint that says the process is alive, and what a 200 from it proves. Readiness (can it serve?) is `ready`.
++ * The service manifest (manifests/services/<id>.json) that serves or will serve this product, when one exists.
*/
-- liveness:
-- | {
-- /**
-- * A path on internalOrigin; the same as `health`.
-- */
-- endpoint: string;
-- /**
-- * What a 200 proves, and what it does not check.
-- */
-- means: string;
-- }
-- | {
-- /**
-- * Why there is none.
-- */
-- none: string;
-- };
++ service?: string;
/**
-- * What happens between the stop signal and the process exiting.
++ * The repository planned or used for the product (under github.com/OpenVibers).
*/
-- shutdown:
-- | {
-- /**
-- * The signal the unit sends (KillSignal=, systemd's default SIGTERM) and the process handles.
-- */
-- signal: "SIGTERM" | "SIGINT" | "SIGQUIT" | "SIGHUP";
-- /**
-- * The longest the process takes from the signal to exiting, as its own code bounds it (a forced-exit timer); 0 when it exits at once. Must not exceed the unit's TimeoutStopSec, after which systemd kills it mid-drain.
-- */
-- deadlineSeconds: number;
-- /**
-- * What it stops, finishes, flushes or closes before exiting, in order.
-- *
-- * @minItems 1
-- */
-- drains: [string, ...string[]];
-- /**
-- * Worker units the service owns that drain on their own (ovhost never restarts them): how long a stop signal lets them run.
-- */
-- workers?: {
-- /**
-- * The longest a worker keeps running after the stop signal, as its code bounds it.
-- */
-- deadlineSeconds: number;
-- /**
-- * What a worker keeps serving, and what it ends, before exiting.
-- *
-- * @minItems 1
-- */
-- drains: [string, ...string[]];
-- };
-- /**
-- * Anything the fields above do not say (a gap the code has, a handler that is missing).
-- */
-- note?: string;
-- }
-- | {
-- /**
-- * Why there is none.
-- */
-- none: string;
-- };
++ plannedRepo?: string;
/**
-- * Work a stop or a crash interrupted that the next start picks up.
++ * No repository exists for the product yet.
*/
-- startupRecovery:
-- | {
-- /**
-- * Each thing the process resumes at boot.
-- *
-- * @minItems 1
-- */
-- resumes: [
-- {
-- /**
-- * outbox: unsent events; jobs: queued or interrupted background work; sessions: client connections or media sessions; consumer: an event or change feed redelivered or read from a stored cursor; schedule: timed work that fell due; state: in-memory state rebuilt from storage.
-- */
-- kind: "outbox" | "jobs" | "sessions" | "consumer" | "schedule" | "state";
-- /**
-- * What is resumed, and how.
-- */
-- what: string;
-- },
-- ...{
-- /**
-- * outbox: unsent events; jobs: queued or interrupted background work; sessions: client connections or media sessions; consumer: an event or change feed redelivered or read from a stored cursor; schedule: timed work that fell due; state: in-memory state rebuilt from storage.
-- */
-- kind: "outbox" | "jobs" | "sessions" | "consumer" | "schedule" | "state";
-- /**
-- * What is resumed, and how.
-- */
-- what: string;
-- }[]
-- ];
-- }
-- | {
-- /**
-- * Why there is none.
-- */
-- none: string;
-- };
++ noRepo?: true;
/**
-- * When a release is rolled back, for how long that happens by itself, and what makes going back unsafe.
++ * The sentence introducing links.
*/
-- rollback:
-- | {
-- /**
-- * What triggers a rollback: automatic (readiness not reached after the restart) and manual.
-- *
-- * @minItems 1
-- */
-- conditions: [string, ...string[]];
-- /**
-- * How long after a deploy the rollback is automatic, and what bounds a manual one.
-- */
-- window: string;
-- /**
-- * Changes a previous release cannot live with (forward-only migrations, table rebuilds, authority switches), or { none } with the reason.
-- */
-- blockers:
-- | [string, ...string[]]
-- | {
-- /**
-- * Why there is none.
-- */
-- none: string;
-- };
-- }
-- | {
-- /**
-- * Why there is none.
-- */
-- none: string;
-- };
++ linksLead?: string;
/**
-- * The openvibe-contracts versions the service accepts.
++ * Where to go instead (a closed, moved or status address).
++ *
++ * @maxItems 10
*/
-- contracts:
-- | {
-- /**
-- * The accepted versions, the same range as contractRanges['openvibe-contracts'].
-- */
-- range: string;
-- /**
-- * Where the range comes from, when it is not the obvious one.
-- */
-- note?: string;
-- }
-- | {
-- /**
-- * Why there is none.
-- */
-- none: string;
-- };
++ links?:
++ | []
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ];
/**
-- * What the service claims so that one owner acts at a time, and how a stale owner is fenced.
++ * What already works elsewhere on the network while the product is not launched.
++ *
++ * @maxItems 10
*/
-- leases:
-- | {
-- /**
-- * Each thing claimed, with its holder, expiry and fencing.
-- *
-- * @minItems 1
-- */
-- claims: [
-- {
-- /**
-- * What is claimed.
-- */
-- what: string;
-- /**
-- * Who holds the claim (a process, a worker id, a row).
-- */
-- holder: string;
-- /**
-- * When the claim lapses and how it is renewed.
-- */
-- expires: string;
-- /**
-- * How a stale owner is kept from acting after its claim lapsed.
-- */
-- fencing: string;
-- },
-- ...{
-- /**
-- * What is claimed.
-- */
-- what: string;
-- /**
-- * Who holds the claim (a process, a worker id, a row).
-- */
-- holder: string;
-- /**
-- * When the claim lapses and how it is renewed.
-- */
-- expires: string;
-- /**
-- * How a stale owner is kept from acting after its claim lapsed.
-- */
-- fencing: string;
-- }[]
-- ];
-- }
-- | {
-- /**
-- * Why there is none.
-- */
-- none: string;
-- };
++ meanwhile?:
++ | []
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ]
++ | [
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ },
++ {
++ label: string;
++ url: string;
++ note?: string;
++ }
++ ];
++ /**
++ * The status API a status page reads.
++ */
++ statusApi?: string;
};
-- notes?: string;
--}
--
--/** [email protected] (owner: network) */
--/**
-- * A product domain of the network's catalog (manifests/products/<domain>.json) that has no service manifest presenting it as a site: a planned product, a sub-domain of a service, a brand domain of a multi-domain service, a closed or moved address. Carries what the network's showcase pages need, copied from the OpenVibe.Sites catalog (plan T11 lane D) so Sites can be deleted. A product whose service manifest has a site block carries the same fields there (registry.service-manifest 1.1.0 site); every catalog domain has exactly one of the two homes. The $defs are shared with that site block.
-- */
--export interface Product {
-- /**
-- * The product's host, unique across the catalog.
-- */
-- domain: string;
-- tld: Tld;
-- /**
-- * Full name, e.g. OpenVibe.Food.
-- */
-- name: string;
-- /**
-- * Icon name of the showcase pages (a Font Awesome name such as fa-utensils).
-- */
-- icon: string;
-- accent: Accent;
-- tagline: string;
-- description: Description;
-- /**
-- * Which legal wording its /terms, /privacy and /dmca use (openvibe-shared/legal), as in a service manifest's site.
-- */
-- legalProfile?: "streaming" | "tools" | "ugc" | "games" | "hosting" | "account" | "info";
-- /**
-- * Absent for a product page. closed: the address was decided against and points elsewhere; moved: the product lives at another address; status: the address shows the network's status. relationships.links say where.
-- */
-- kind?: "closed" | "moved" | "status";
-- pillars?: Pillars;
-- faq?: Faq;
-- keywords: Keywords;
-- vision?: Vision;
-- highlight?: Highlight;
-- launch?: Launch;
-- relationships?: Relationships;
}
/** [email protected] (owner: network) */
@@@ -29574,11 +33017,27 @@ export interface NetworkCreatorAnalyticsResult {
creator: string;
days: number;
full: boolean;
-- totals: Counts;
++ totals: {
++ streams?: number;
++ stream_seconds?: number;
++ peak_viewers?: number;
++ avg_viewers?: number;
++ unique_chatters?: number;
++ messages?: number;
++ watch_minutes?: number;
++ };
/**
* @maxItems 365
*/
-- daily: Counts[];
++ daily: {
++ streams?: number;
++ stream_seconds?: number;
++ peak_viewers?: number;
++ avg_viewers?: number;
++ unique_chatters?: number;
++ messages?: number;
++ watch_minutes?: number;
++ }[];
/**
* @maxItems 100
*/
@@@ -29915,34 +33374,34 @@ export interface ProjectUsageResult {
* The whole range per service, capability, unit and environment.
*/
totals: {
-- service: Service;
-- capability: Capability;
-- unit: Unit;
-- env: Env;
-- quantity: Count;
-- errors: Count;
++ service: string;
++ capability: string;
++ unit: string;
++ env: "sandbox" | "production";
++ quantity: number;
++ errors: number;
}[];
/**
* One row per UTC day, service, capability, dimension, unit and environment that saw usage; days without any are absent. Newest day first.
*/
daily: {
day: string;
-- service: Service;
-- capability: Capability;
++ service: string;
++ capability: string;
dimension: string | null;
-- unit: Unit;
-- env: Env;
-- quantity: Count;
-- errors: Count;
++ unit: string;
++ env: "sandbox" | "production";
++ quantity: number;
++ errors: number;
}[];
/**
* The project's recorded quotas (GET /api/v1/projects/:project/quotas), each with what its current window has used, in every environment together. used is null where the rollups cannot tell (a minute or hour window, or a unit no service reports for that capability).
*/
quotas: {
-- capability: Capability;
++ capability: string;
limit: number;
window: "minute" | "hour" | "day" | "month" | "total";
-- unit: Unit;
++ unit: string;
/**
* The audience of the service that enforces it (openvibe.<owner>).
*/
@@@ -29956,12 +33415,12 @@ export interface ProjectUsageResult {
note: string | null;
}[];
errors: {
-- total: Count;
++ total: number;
by_code: {
-- service: Service;
-- capability: Capability;
-- code: Code;
-- count: Count;
++ service: string;
++ capability: string;
++ code: string;
++ count: number;
}[];
/**
* The newest failures the rollups sampled, newest first.
@@@ -29970,10 +33429,10 @@ export interface ProjectUsageResult {
*/
recent: {
at: string;
-- env: Env;
-- service: Service;
-- capability: Capability;
-- code: Code;
++ env: "sandbox" | "production";
++ service: string;
++ capability: string;
++ code: string;
status: number | null;
trace_id: string | null;
ref: string | null;
@@@ -30615,10 +34074,6 @@ export type ToolsToolReadResult =
| {
$schema: string;
$id: string;
-- $defs: {
-- input: {} | boolean;
-- output: {};
-- };
};
/** [email protected] (owner: news) */
@@@ -43804,7 +47259,139 @@ export type ChatSendResult =
export type ChatChannelModerationResult =
| {
ok: true;
-- settings: Settings;
++ /**
++ * One row of channel_moderation_settings, or Live's defaults for a channel that has no row. Every column is present; the switches are 0/1.
++ */
++ settings: {
++ channel_id: number;
++ slow_mode_seconds: number;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ followers_only: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ emote_only: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ allow_anonymous: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ links_allowed: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ gifs_enabled: 0 | 1;
++ account_age_gate_hours: number;
++ /**
++ * Percent of a message that may be capitals; 0 means off.
++ */
++ caps_percentage_limit: number;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ aggressive_filter: 0 | 1;
++ max_message_length: number;
++ tts_max_length: number;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ slur_filter_enabled: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ slur_filter_use_builtin: 0 | 1;
++ /**
++ * Extra terms, comma separated; a channel's own filter list, never a network-wide secret.
++ */
++ slur_filter_terms: string;
++ /**
++ * Extra patterns, one per line.
++ */
++ slur_filter_regexes: string;
++ slur_filter_nudge_message: string;
++ /**
++ * A JSON array string of disabled built-in categories ('[]' when none).
++ */
++ slur_filter_disabled_categories: string;
++ /**
++ * 0 off, 1 approve before chatting, 2 approved addresses only.
++ */
++ ip_approval_mode: number;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ soundboard_enabled: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ soundboard_allow_pitch: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ soundboard_allow_speed: 0 | 1;
++ /**
++ * Banned channel sound ids, comma separated.
++ */
++ soundboard_banned_ids: string;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ viewer_auto_delete_enabled: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ viewer_delete_all_enabled: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ custom_emotes_enabled: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ custom_sounds_enabled: 0 | 1;
++ max_sound_seconds: number;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ uploads_mods_only: 0 | 1;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ mods_can_edit_about: 0 | 1;
++ /**
++ * Emote display size in chat, percent of the base height (100 is the default).
++ */
++ emote_scale: number;
++ emote_size_min: number;
++ emote_size_max: number;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ sounds_mods_only: 0 | 1;
++ sound_min_speed: number;
++ sound_max_speed: number;
++ sound_min_pitch_cents: number;
++ sound_max_pitch_cents: number;
++ /**
++ * A switch as the SQLite table stores it.
++ */
++ sub_only: 0 | 1;
++ /**
++ * Optional alert sounds set by the streamer; absent or null when unset.
++ */
++ donation_sound_url?: string | null;
++ donation_sound_mime?: string | null;
++ goal_sound_url?: string | null;
++ goal_sound_mime?: string | null;
++ /**
++ * When the row was last written; absent or null when the channel still has its defaults.
++ */
++ updated_at?: string | null;
++ };
/**
* The channel's moderators, oldest first (channel_moderators.created_at); empty when it has none.
*/
@@@ -47062,9 +50649,18 @@ export interface NetworkCoinsReadResult {
holders: number;
transactions: number;
recent: {
-- earned: Window;
-- spent: Window;
-- holders: Window;
++ earned: {
++ w: number;
++ pw: number;
++ };
++ spent: {
++ w: number;
++ pw: number;
++ };
++ holders: {
++ w: number;
++ pw: number;
++ };
};
}
ddiff --git a/scripts/generate.js b/scripts/generate.js
iindex 70d0f0c1..84ea9e20 100644
---- a/scripts/generate.js
++++ b/scripts/generate.js
@@@ -17,6 +17,32 @@ const ROOT = path.join(__dirname, '..');
const catalog = JSON.parse(fs.readFileSync(path.join(ROOT, 'contracts/catalog.json'), 'utf8')).contracts;
const CHECK = process.argv.includes('--check');
++// json-schema-to-typescript names a $ref to a $defs entry (Tld, Service...) and, with
++// declareExternallyReferenced false, never declares it: inline every $defs ref (local or
++// another file's, e.g. product.v1.json#/$defs/tld) before compiling. Cycles stay refs.
++function inlineDefs(schema, file) {
++ const docs = new Map([[file, schema]]);
++ const load = (f) => docs.get(f) || docs.set(f, JSON.parse(fs.readFileSync(f, 'utf8'))).get(f);
++ const walk = (node, base, seen) => {
++ if (Array.isArray(node)) return node.map(v => walk(v, base, seen));
++ if (!node || typeof node !== 'object') return node;
++ const m = typeof node.$ref === 'string' && node.$ref.match(/^([^#]*)#\/\$defs\/([^/]+)$/);
++ if (m && !/^[a-z]+:/.test(m[1])) {
++ const target = m[1] ? path.resolve(path.dirname(base), m[1]) : base;
++ const key = `${target}#${m[2]}`;
++ const def = load(target).$defs?.[m[2]];
++ if (def !== undefined && !seen.has(key)) {
++ const { $ref, ...rest } = node;
++ return walk({ ...def, ...rest }, target, new Set([...seen, key]));
++ }
++ }
++ const out = {};
++ for (const [k, v] of Object.entries(node)) if (k !== '$defs') out[k] = walk(v, base, seen);
++ return out;
++ };
++ return walk(schema, file, new Set());
++}
++
(async () => {
const stale = [];
const bundle = { _about: 'Generated by scripts/generate.js. Do not edit.', contracts: {} };
@@@ -25,7 +51,7 @@ const CHECK = process.argv.includes('--check');
const file = path.join(ROOT, 'contracts', entry.schema);
const schema = JSON.parse(fs.readFileSync(file, 'utf8'));
bundle.contracts[entry.id] = { version: entry.version, owner: entry.owner, status: entry.status, schema };
-- const ts = await compile(schema, schema.title, {
++ const ts = await compile(inlineDefs(schema, file), schema.title, {
bannerComment: '', cwd: path.dirname(file), declareExternallyReferenced: false,
additionalProperties: false, strictIndexSignatures: true, format: true,
$refOptions: { resolve: { http: false } },
ddiff --git a/test/run.js b/test/run.js
iindex 87df3086..a955bd8d 100644
---- a/test/run.js
++++ b/test/run.js
@@@ -867,10 +867,13 @@ sub(path.join(__dirname, 'platform-fabric.test.js'));
sub(path.join(ROOT, 'scripts/generate.js'), '--check');
sub(path.join(ROOT, 'scripts/compat.js'));
{
-- // A $ref with sibling keywords makes json-schema-to-typescript emit SubjectRef1 & co. without declaring them.
++ // A $ref with sibling keywords (SubjectRef1 & co.) or to a $defs entry (Tld, Service...) makes
++ // json-schema-to-typescript emit a type name it never declares; every name used as a type must be declared.
const dts = fs.readFileSync(path.join(ROOT, 'generated/typescript/index.d.ts'), 'utf8');
const declared = new Set([...dts.matchAll(/^export (?:interface|type) (\w+)/gm)].map(m => m[1]));
-- const dangling = [...new Set([...dts.replace(/\/\*[\s\S]*?\*\//g, '').matchAll(/\b([A-Z][A-Za-z]*\d+)\b/g)].map(m => m[1]))].filter(n => !declared.has(n));
++ const code = dts.replace(/\/\*[\s\S]*?\*\//g, '').replace(/"(?:[^"\\]|\\.)*"/g, '""');
++ const used = [...code.matchAll(/(?:[:|&=<(]|\bextends)\s*([A-Z]\w*)\b(?!\s*\??:)/g)].map(m => m[1]);
++ const dangling = [...new Set(used)].filter(n => !declared.has(n));
ok(dangling.length === 0, `generated types reference undeclared names: ${dangling.join(', ')}`);
}