openvibers/openvibe.contracts Commits
finish-f1: Changed: scripts/generate.js, test/run.js and README.md,…
Changes
@@@ -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 andregisters maturity in the ecosystem registry atomically. A placeholder is never counted as animplemented service.@@@ -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;++ };};}@@@ -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 } },@@@ -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(', ')}`);}
@@ -301,7 +301,8 @@ following exist here (plan §12.12):
@@ -301,7 +301,8 @@ following exist here (plan §12.12):
6. a migration/seed strategy, a security/threat review, and sitemap/robots/feed behaviour;
6. a migration/seed strategy, a security/threat review, and sitemap/robots/feed behaviour;
7. acceptance tests proving the advertised functionality.
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
registers maturity in the ecosystem registry atomically. A placeholder is never counted as an
implemented service.
implemented service.
@@ -296,283 +296,3726 @@ export interface ServiceManifest {
@@ -296,283 +296,3726 @@ export interface ServiceManifest {
* The catalog's description, verbatim.
* The catalog's description, verbatim.
*/
*/
description?: string;
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.
* Comma-separated search keywords, verbatim.
*/
*/
keywords?: string;
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) */
/** [email protected] (owner: network) */
@@ -29574,11 +33017,27 @@ export interface NetworkCreatorAnalyticsResult {
@@ -29574,11 +33017,27 @@ export interface NetworkCreatorAnalyticsResult {
creator: string;
creator: string;
days: number;
days: number;
full: boolean;
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
* @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
* @maxItems 100
*/
*/
@@ -29915,34 +33374,34 @@ export interface ProjectUsageResult {
@@ -29915,34 +33374,34 @@ export interface ProjectUsageResult {
* The whole range per service, capability, unit and environment.
* The whole range per service, capability, unit and environment.
*/
*/
totals: {
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.
* One row per UTC day, service, capability, dimension, unit and environment that saw usage; days without any are absent. Newest day first.
*/
*/
daily: {
daily: {
day: string;
day: string;
- service: Service;
- capability: Capability;
+ service: string;
+ capability: string;
dimension: string | null;
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).
* 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: {
quotas: {
- capability: Capability;
+ capability: string;
limit: number;
limit: number;
window: "minute" | "hour" | "day" | "month" | "total";
window: "minute" | "hour" | "day" | "month" | "total";
- unit: Unit;
+ unit: string;
/**
/**
* The audience of the service that enforces it (openvibe.<owner>).
* The audience of the service that enforces it (openvibe.<owner>).
*/
*/
@@ -29956,12 +33415,12 @@ export interface ProjectUsageResult {
@@ -29956,12 +33415,12 @@ export interface ProjectUsageResult {
note: string | null;
note: string | null;
}[];
}[];
errors: {
errors: {
- total: Count;
+ total: number;
by_code: {
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.
* The newest failures the rollups sampled, newest first.
@@ -29970,10 +33429,10 @@ export interface ProjectUsageResult {
@@ -29970,10 +33429,10 @@ export interface ProjectUsageResult {
*/
*/
recent: {
recent: {
at: string;
at: string;
- env: Env;
- service: Service;
- capability: Capability;
- code: Code;
+ env: "sandbox" | "production";
+ service: string;
+ capability: string;
+ code: string;
status: number | null;
status: number | null;
trace_id: string | null;
trace_id: string | null;
ref: string | null;
ref: string | null;
@@ -30615,10 +34074,6 @@ export type ToolsToolReadResult =
@@ -30615,10 +34074,6 @@ export type ToolsToolReadResult =
| {
| {
$schema: string;
$schema: string;
$id: string;
$id: string;
- $defs: {
- input: {} | boolean;
- output: {};
- };
};
};
/** [email protected] (owner: news) */
/** [email protected] (owner: news) */
@@ -43804,7 +47259,139 @@ export type ChatSendResult =
@@ -43804,7 +47259,139 @@ export type ChatSendResult =
export type ChatChannelModerationResult =
export type ChatChannelModerationResult =
| {
| {
ok: true;
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.
* The channel's moderators, oldest first (channel_moderators.created_at); empty when it has none.
*/
*/
@@ -47062,9 +50649,18 @@ export interface NetworkCoinsReadResult {
@@ -47062,9 +50649,18 @@ export interface NetworkCoinsReadResult {
holders: number;
holders: number;
transactions: number;
transactions: number;
recent: {
recent: {
- earned: Window;
- spent: Window;
- holders: Window;
+ earned: {
+ w: number;
+ pw: number;
+ };
+ spent: {
+ w: number;
+ pw: number;
+ };
+ holders: {
+ w: number;
+ pw: number;
+ };
};
};
}
}
@@ -17,6 +17,32 @@ const ROOT = path.join(__dirname, '..');
@@ -17,6 +17,32 @@ const ROOT = path.join(__dirname, '..');
const catalog = JSON.parse(fs.readFileSync(path.join(ROOT, 'contracts/catalog.json'), 'utf8')).contracts;
const catalog = JSON.parse(fs.readFileSync(path.join(ROOT, 'contracts/catalog.json'), 'utf8')).contracts;
const CHECK = process.argv.includes('--check');
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 () => {
(async () => {
const stale = [];
const stale = [];
const bundle = { _about: 'Generated by scripts/generate.js. Do not edit.', contracts: {} };
const bundle = { _about: 'Generated by scripts/generate.js. Do not edit.', contracts: {} };
@@ -25,7 +51,7 @@ const CHECK = process.argv.includes('--check');
@@ -25,7 +51,7 @@ const CHECK = process.argv.includes('--check');
const file = path.join(ROOT, 'contracts', entry.schema);
const file = path.join(ROOT, 'contracts', entry.schema);
const schema = JSON.parse(fs.readFileSync(file, 'utf8'));
const schema = JSON.parse(fs.readFileSync(file, 'utf8'));
bundle.contracts[entry.id] = { version: entry.version, owner: entry.owner, status: entry.status, schema };
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,
bannerComment: '', cwd: path.dirname(file), declareExternallyReferenced: false,
additionalProperties: false, strictIndexSignatures: true, format: true,
additionalProperties: false, strictIndexSignatures: true, format: true,
$refOptions: { resolve: { http: false } },
$refOptions: { resolve: { http: false } },
@@ -867,10 +867,13 @@ sub(path.join(__dirname, 'platform-fabric.test.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/generate.js'), '--check');
sub(path.join(ROOT, 'scripts/compat.js'));
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 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 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(', ')}`);
ok(dangling.length === 0, `generated types reference undeclared names: ${dangling.join(', ')}`);
}
}