openvibers/openvibe.contracts Commits

v0.12.0: OpenVibe.Search and OpenVibe.Sources are alpha — search.index-document@1, sources.source@1, sources.item@1; search.document.write, search.query.delegate, sources.source.manage|read, sources.item.read

OP OpenVibers committed 23 Sept 2026 · 96d82df465 · 22 files, +4196 −1


Changes

ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex 2e77bb70..e76f2c0e 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -117,6 +117,36 @@
"compatibility": "backward",
"status": "active",
"adr": "ADR-016"
++ },
++ {
++ "id": "search.index-document",
++ "version": "1.0.0",
++ "owner": "search",
++ "schema": "search/index-document.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-018"
++ },
++ {
++ "id": "sources.source",
++ "version": "1.0.0",
++ "owner": "sources",
++ "schema": "sources/source.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-017"
++ },
++ {
++ "id": "sources.item",
++ "version": "1.0.0",
++ "owner": "sources",
++ "schema": "sources/item.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-017"
}
]
}
ddiff --git a/contracts/search/index-document.v1.json b/contracts/search/index-document.v1.json
nnew file mode 100644
iindex 00000000..8a2fbb9d
---- /dev/null
++++ b/contracts/search/index-document.v1.json
@@@ -0,0 +1,112 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/search/index-document.v1.json",
++ "title": "IndexDocument",
++ "description": "What an owning service tells OpenVibe.Search about one of its resources (roadmap §15.12, §4.2 A). The owner stays the source of truth; Search keeps a revision-ordered, permission-aware copy for discovery. A document with deleted=true is a tombstone: it removes the resource from every result and wins over any upsert with the same or an older revision.",
++ "type": "object",
++ "required": ["owner", "type", "id", "revision"],
++ "additionalProperties": false,
++ "properties": {
++ "owner": {
++ "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$",
++ "description": "Owning service id (wiki, blog, news, community, sources…). Only that service may write the document."
++ },
++ "type": {
++ "type": "string", "pattern": "^[a-z][a-z0-9_]{1,39}$",
++ "description": "Resource type inside the owner (page, post, story, entity, offer, coupon, instrument, item…)."
++ },
++ "id": {
++ "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$",
++ "description": "Resource id inside owner+type. Stable across revisions."
++ },
++ "revision": {
++ "type": "integer", "minimum": 0, "maximum": 9007199254740991,
++ "description": "Owner's monotonic revision of the resource. An older revision never overwrites a newer one; at equal revision a deletion wins."
++ },
++ "deleted": {
++ "type": "boolean", "default": false,
++ "description": "Deletion marker. true = tombstone; every other content field is ignored."
++ },
++ "visibility": {
++ "enum": ["public", "unlisted", "members", "private", "draft"],
++ "description": "public: anyone. unlisted: never listed except to ACL matches; fetchable by exact id by a signed-in subject. members: subjects, groups or entitlements in acl. private: subjects in acl.subjects only. draft: never served."
++ },
++ "acl": {
++ "type": "object", "additionalProperties": false,
++ "description": "Who may see a non-public document. Ignored for public documents. Empty means nobody (for members/private) or direct-id only (for unlisted).",
++ "properties": {
++ "subjects": {
++ "type": "array", "maxItems": 200, "uniqueItems": true,
++ "items": { "type": "string", "pattern": "^(usr|gst)_[0-9A-HJKMNP-TV-Z]{26}$" }
++ },
++ "groups": {
++ "type": "array", "maxItems": 100, "uniqueItems": true,
++ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_.:-]{0,127}$" },
++ "description": "Opaque group keys owned by some service, e.g. role:admin (Network role), wiki.space:spc_…:member."
++ },
++ "entitlements": {
++ "type": "array", "maxItems": 100, "uniqueItems": true,
++ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_.:-]{0,127}$" },
++ "description": "Entitlement keys (VIP/Billing), e.g. vip.plan:pln_…. Only a first-party service that just resolved them may present them for a viewer."
++ }
++ }
++ },
++ "canonical_url": { "type": "string", "format": "uri", "pattern": "^https?://", "maxLength": 2048 },
++ "title": { "type": "string", "maxLength": 500 },
++ "summary": { "type": "string", "maxLength": 4000 },
++ "body": { "type": "string", "maxLength": 48000, "description": "Plain text for full-text search (no HTML). Keep it under the Events payload limit when publishing through Events." },
++ "facets": {
++ "type": "object", "maxProperties": 20,
++ "propertyNames": { "pattern": "^[a-z][a-z0-9_]{0,39}$" },
++ "additionalProperties": {
++ "oneOf": [
++ { "type": "string", "maxLength": 200 },
++ { "type": "number" },
++ { "type": "boolean" },
++ { "type": "array", "maxItems": 50, "items": { "type": "string", "maxLength": 200 } }
++ ]
++ },
++ "description": "Filterable values (category, tags, space, author subject, currency…). Filter with facet.<key>=<value>."
++ },
++ "language": { "type": "string", "pattern": "^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$", "description": "BCP 47 tag." },
++ "authorship": { "enum": ["human", "ai_assisted", "ai_generated", "imported"] },
++ "provenance": {
++ "type": "array", "maxItems": 50,
++ "description": "Where the content came from: typed references to source records (Sources items, citations, Media objects).",
++ "items": {
++ "type": "object", "required": ["service", "type", "id"], "additionalProperties": false,
++ "properties": {
++ "service": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" },
++ "type": { "type": "string", "pattern": "^[a-z][a-z0-9_]{1,39}$" },
++ "id": { "type": "string", "minLength": 1, "maxLength": 128 },
++ "revision": { "type": "integer", "minimum": 0 },
++ "label": { "type": "string", "maxLength": 200 },
++ "url": { "type": "string", "format": "uri", "pattern": "^https?://", "maxLength": 2048 },
++ "retrieved_at": { "type": "string", "format": "date-time" },
++ "stub": { "type": "boolean", "description": "true when the reference came from a stub provider; the document is then never indexable." }
++ }
++ }
++ },
++ "publication_state": {
++ "enum": ["draft", "scheduled", "published", "unpublished", "retracted", "archived"],
++ "description": "Only published documents are ever served."
++ },
++ "published_at": { "type": ["string", "null"], "format": "date-time" },
++ "updated_at": { "type": ["string", "null"], "format": "date-time" },
++ "indexability": {
++ "type": "object", "required": ["decision"], "additionalProperties": false,
++ "description": "The owner's deterministic indexability decision (publishing packages, roadmap §32.3). Search can only make it stricter.",
++ "properties": {
++ "decision": { "enum": ["index", "noindex"] },
++ "reasons": {
++ "type": "array", "maxItems": 20, "uniqueItems": true,
++ "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]{1,63}$" },
++ "description": "Known reasons: draft, private, members_only, unlisted, not_published, deleted, thin_content, duplicate_without_canonical, unsupported_claims, unsourced, stub_provider, sensitive_unreviewed, ai_unreviewed, missing_canonical_url, third_party_content, owner_decision."
++ }
++ }
++ }
++ },
++ "if": { "properties": { "deleted": { "const": true } }, "required": ["deleted"] },
++ "then": {},
++ "else": { "required": ["visibility", "publication_state", "title", "indexability"] }
++}
ddiff --git a/contracts/sources/item.v1.json b/contracts/sources/item.v1.json
nnew file mode 100644
iindex 00000000..1d76322b
---- /dev/null
++++ b/contracts/sources/item.v1.json
@@@ -0,0 +1,46 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/sources/item.v1.json",
++ "title": "SourceItem",
++ "description": "One thing a source said, with its provenance: source, canonical URL, retrieval time, content hash, raw-body hash, parser version and the terms it was ingested under. A field the source did not state is null — never guessed. Items change only through successful fetches (a new revision per content change) or an explicit, audited removal.",
++ "type": "object",
++ "required": ["id", "source_key", "category", "kind", "identity", "canonical_url", "title", "summary", "authors", "published_at", "source_updated_at", "fields", "revision", "provenance", "removed"],
++ "additionalProperties": false,
++ "properties": {
++ "id": { "type": "string", "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$" },
++ "source_key": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{1,63}$" },
++ "category": { "enum": ["news", "blog", "reviews", "deals", "coupons", "trade"] },
++ "kind": { "type": "string", "pattern": "^[a-z][a-z0-9_]{1,39}$", "description": "article | url | sitemap | product | offer | review | review_signal | record | coupon …" },
++ "identity": { "type": "string", "minLength": 1, "maxLength": 2048, "description": "The source's own id (guid, @id, API id) or the canonical URL." },
++ "canonical_url": { "type": ["string", "null"], "format": "uri" },
++ "title": { "type": ["string", "null"] },
++ "summary": { "type": ["string", "null"], "maxLength": 1000, "description": "Plain text, capped. Sources keeps metadata and short descriptions, not republishable full text." },
++ "authors": { "type": "array", "items": { "type": "string" } },
++ "published_at": { "type": ["string", "null"], "format": "date-time" },
++ "source_updated_at": { "type": ["string", "null"], "format": "date-time" },
++ "fields": { "type": "object", "description": "Adapter-specific parsed fields (offers, ratings, prices as stated, API extras)." },
++ "revision": { "type": "integer", "minimum": 1 },
++ "provenance": {
++ "type": "object",
++ "required": ["retrieved_at", "first_seen_at", "content_hash", "raw_body_hash", "parser_version", "fetch_run_id", "license_note", "terms_note", "entered_by"],
++ "additionalProperties": false,
++ "properties": {
++ "retrieved_at": { "type": "string", "format": "date-time", "description": "Last successful fetch that contained this item (observation time)." },
++ "first_seen_at": { "type": "string", "format": "date-time" },
++ "content_hash": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
++ "raw_body_hash": { "type": ["string", "null"], "pattern": "^[0-9a-f]{64}$" },
++ "parser_version": { "type": "string", "pattern": "^[a-z]+@\\d+$" },
++ "fetch_run_id": { "type": ["string", "null"], "pattern": "^frn_[0-9A-HJKMNP-TV-Z]{26}$" },
++ "license_note": { "type": ["string", "null"] },
++ "terms_note": { "type": ["string", "null"] },
++ "entered_by": { "type": ["string", "null"], "description": "Principal that entered a manual item." }
++ }
++ },
++ "removed": {
++ "type": ["object", "null"], "additionalProperties": false, "required": ["at", "reason"],
++ "properties": { "at": { "type": "string", "format": "date-time" }, "reason": { "type": "string" } }
++ },
++ "change_seq": { "type": "integer", "minimum": 1 },
++ "revisions": { "type": "array" }
++ }
++}
ddiff --git a/contracts/sources/source.v1.json b/contracts/sources/source.v1.json
nnew file mode 100644
iindex 00000000..9c62f530
---- /dev/null
++++ b/contracts/sources/source.v1.json
@@@ -0,0 +1,66 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/sources/source.v1.json",
++ "title": "Source",
++ "description": "A registered source (roadmap §4.2 B, Wave 14): where it is fetched from and on what terms. Credentials are referenced by environment-variable NAME only. A source's existence is never permission to fetch: robots and terms are verified before it is enabled, and the fetcher re-checks robots.txt on every run.",
++ "type": "object",
++ "required": ["key", "name", "type", "category", "endpoints", "auth", "min_interval_ms", "poll_interval_sec", "stale_after_sec", "max_items", "enabled", "review_required", "sensitivity", "default_indexability"],
++ "additionalProperties": false,
++ "properties": {
++ "key": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{1,63}$" },
++ "name": { "type": "string", "minLength": 1, "maxLength": 200 },
++ "type": { "enum": ["rss", "atom", "sitemap", "jsonld", "api", "manual"] },
++ "category": { "enum": ["news", "blog", "reviews", "deals", "coupons", "trade"] },
++ "homepage_url": { "type": ["string", "null"], "format": "uri" },
++ "endpoints": {
++ "type": "array", "maxItems": 20,
++ "items": {
++ "type": "object", "required": ["url"], "additionalProperties": false,
++ "properties": {
++ "url": { "type": "string", "format": "uri", "pattern": "^https?://" },
++ "format": { "enum": ["json", "xml"] },
++ "items_path": { "type": "string" },
++ "item_kind": { "type": "string", "pattern": "^[a-z][a-z0-9_]{1,39}$" },
++ "fields": { "type": "object", "additionalProperties": { "type": "string" } },
++ "extra": { "type": "object", "additionalProperties": { "type": "string" } }
++ }
++ }
++ },
++ "auth": {
++ "type": "object", "required": ["mode"], "additionalProperties": false,
++ "properties": {
++ "mode": { "enum": ["none", "header", "bearer", "query"] },
++ "env": { "type": "string", "pattern": "^SOURCES_CRED_[A-Z0-9_]{2,60}$", "description": "Name of the environment variable holding the credential. Never a value." },
++ "header": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9-]{0,63}$" },
++ "param": { "type": "string", "pattern": "^[A-Za-z0-9_.-]{1,64}$" }
++ }
++ },
++ "robots_note": { "type": ["string", "null"], "maxLength": 2000 },
++ "terms_note": { "type": ["string", "null"], "maxLength": 2000 },
++ "license_note": { "type": ["string", "null"], "maxLength": 2000 },
++ "min_interval_ms": { "type": "integer", "minimum": 0, "description": "Rate limit: the least time between two requests to this source (the per-host floor and robots Crawl-delay may raise it)." },
++ "poll_interval_sec": { "type": "integer", "minimum": 1 },
++ "stale_after_sec": { "type": "integer", "minimum": 1 },
++ "max_items": { "type": "integer", "minimum": 1 },
++ "enabled": { "type": "boolean" },
++ "review_required": { "type": "boolean", "description": "Products must review what they derive from this source before it can be indexable." },
++ "sensitivity": { "enum": ["none", "financial", "health", "political", "legal", "adult"] },
++ "default_indexability": { "enum": ["index", "noindex"], "description": "What a product's indexability gate starts from for content derived from this source." },
++ "search_visibility": { "type": ["string", "null"], "enum": ["members", null], "description": "members = raw items are indexed in OpenVibe.Search for staff only (never indexable)." },
++ "health": {
++ "type": "object", "additionalProperties": false,
++ "properties": {
++ "status": { "enum": ["healthy", "stale", "failing", "never_fetched", "disabled", "manual"] },
++ "stale": { "type": "boolean" },
++ "last_success_at": { "type": ["string", "null"], "format": "date-time" },
++ "last_run_at": { "type": ["string", "null"], "format": "date-time" },
++ "last_state": { "type": ["string", "null"] },
++ "consecutive_failures": { "type": "integer", "minimum": 0 },
++ "next_due_at": { "type": ["string", "null"], "format": "date-time" },
++ "stale_after_sec": { "type": "integer" }
++ }
++ },
++ "created_at": { "type": "string", "format": "date-time" },
++ "updated_at": { "type": "string", "format": "date-time" }
++ }
++}
ddiff --git a/fixtures/search.index-document/invalid/bad-visibility.json b/fixtures/search.index-document/invalid/bad-visibility.json
nnew file mode 100644
iindex 00000000..8332f36e
---- /dev/null
++++ b/fixtures/search.index-document/invalid/bad-visibility.json
@@@ -0,0 +1 @@
++{ "owner": "wiki", "type": "page", "id": "x", "revision": 1, "visibility": "everyone" }
ddiff --git a/fixtures/search.index-document/invalid/no-revision.json b/fixtures/search.index-document/invalid/no-revision.json
nnew file mode 100644
iindex 00000000..7ab23b7e
---- /dev/null
++++ b/fixtures/search.index-document/invalid/no-revision.json
@@@ -0,0 +1 @@
++{ "owner": "wiki", "type": "page", "id": "x" }
ddiff --git a/fixtures/search.index-document/valid/tombstone.json b/fixtures/search.index-document/valid/tombstone.json
nnew file mode 100644
iindex 00000000..d8304637
---- /dev/null
++++ b/fixtures/search.index-document/valid/tombstone.json
@@@ -0,0 +1 @@
++{ "owner": "blog", "type": "post", "id": "post-42", "revision": 7, "deleted": true }
ddiff --git a/fixtures/search.index-document/valid/wiki-page.json b/fixtures/search.index-document/valid/wiki-page.json
nnew file mode 100644
iindex 00000000..946cba13
---- /dev/null
++++ b/fixtures/search.index-document/valid/wiki-page.json
@@@ -0,0 +1,32 @@
++{
++ "owner": "wiki",
++ "type": "page",
++ "id": "pg_01J0000000000000000000000Z",
++ "revision": 3,
++ "visibility": "public",
++ "canonical_url": "https://openvibe.wiki/w/example",
++ "title": "Example page",
++ "summary": "A short summary.",
++ "body": "Plain text body.",
++ "facets": {
++ "space": "general",
++ "tags": [
++ "example"
++ ]
++ },
++ "language": "en",
++ "authorship": "human",
++ "publication_state": "published",
++ "published_at": "2026-09-22T12:00:00Z",
++ "indexability": {
++ "decision": "index",
++ "reasons": []
++ },
++ "provenance": [
++ {
++ "service": "sources",
++ "type": "item",
++ "id": "itm_01J0000000000000000000000Z"
++ }
++ ]
++}
ddiff --git a/fixtures/sources.item/invalid/no-provenance.json b/fixtures/sources.item/invalid/no-provenance.json
nnew file mode 100644
iindex 00000000..619ff143
---- /dev/null
++++ b/fixtures/sources.item/invalid/no-provenance.json
@@@ -0,0 +1 @@
++{ "id": "itm_01J0000000000000000000000Z", "source_key": "nasa-news", "category": "news", "kind": "article", "identity": "x", "canonical_url": null, "title": null, "summary": null, "authors": [], "published_at": null, "source_updated_at": null, "fields": {}, "revision": 1, "removed": null }
ddiff --git a/fixtures/sources.item/valid/article.json b/fixtures/sources.item/valid/article.json
nnew file mode 100644
iindex 00000000..e2e75f74
---- /dev/null
++++ b/fixtures/sources.item/valid/article.json
@@@ -0,0 +1,27 @@
++{
++ "id": "itm_01J0000000000000000000000Z",
++ "source_key": "nasa-news",
++ "category": "news",
++ "kind": "article",
++ "identity": "https://www.nasa.gov/news-release/example/",
++ "canonical_url": "https://www.nasa.gov/news-release/example/",
++ "title": "Example release",
++ "summary": null,
++ "authors": [],
++ "published_at": "2026-09-22T15:00:00Z",
++ "source_updated_at": null,
++ "fields": {},
++ "revision": 1,
++ "provenance": {
++ "retrieved_at": "2026-09-23T01:00:00Z",
++ "first_seen_at": "2026-09-23T01:00:00Z",
++ "content_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
++ "raw_body_hash": null,
++ "parser_version": "rss@1",
++ "fetch_run_id": "frn_01J0000000000000000000000Z",
++ "license_note": null,
++ "terms_note": "US government work",
++ "entered_by": null
++ },
++ "removed": null
++}
ddiff --git a/fixtures/sources.source/invalid/secret-in-auth.json b/fixtures/sources.source/invalid/secret-in-auth.json
nnew file mode 100644
iindex 00000000..cb97ff1e
---- /dev/null
++++ b/fixtures/sources.source/invalid/secret-in-auth.json
@@@ -0,0 +1,7 @@
++{
++ "key": "x-api", "name": "X", "type": "api", "category": "deals",
++ "endpoints": [{ "url": "https://example.com/api" }],
++ "auth": { "mode": "bearer", "env": "DATABASE_PASSWORD" },
++ "min_interval_ms": 1000, "poll_interval_sec": 60, "stale_after_sec": 600, "max_items": 10,
++ "enabled": false, "review_required": false, "sensitivity": "none", "default_indexability": "noindex"
++}
ddiff --git a/fixtures/sources.source/valid/rss.json b/fixtures/sources.source/valid/rss.json
nnew file mode 100644
iindex 00000000..c172903c
---- /dev/null
++++ b/fixtures/sources.source/valid/rss.json
@@@ -0,0 +1,20 @@
++{
++ "key": "nasa-news",
++ "name": "NASA news releases",
++ "type": "rss",
++ "category": "news",
++ "homepage_url": "https://www.nasa.gov/",
++ "endpoints": [{ "url": "https://www.nasa.gov/news-release/feed/" }],
++ "auth": { "mode": "none" },
++ "robots_note": "robots.txt allows the feed path (checked 2026-09-23)",
++ "terms_note": "US government work; verify before enabling",
++ "license_note": null,
++ "min_interval_ms": 60000,
++ "poll_interval_sec": 3600,
++ "stale_after_sec": 172800,
++ "max_items": 200,
++ "enabled": false,
++ "review_required": true,
++ "sensitivity": "none",
++ "default_indexability": "noindex"
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 073780ba..c9e6d5a3 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -866,6 +866,760 @@
}
}
}
++ },
++ "search.index-document": {
++ "version": "1.0.0",
++ "owner": "search",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/search/index-document.v1.json",
++ "title": "IndexDocument",
++ "description": "What an owning service tells OpenVibe.Search about one of its resources (roadmap §15.12, §4.2 A). The owner stays the source of truth; Search keeps a revision-ordered, permission-aware copy for discovery. A document with deleted=true is a tombstone: it removes the resource from every result and wins over any upsert with the same or an older revision.",
++ "type": "object",
++ "required": [
++ "owner",
++ "type",
++ "id",
++ "revision"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "owner": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]{1,39}$",
++ "description": "Owning service id (wiki, blog, news, community, sources…). Only that service may write the document."
++ },
++ "type": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_]{1,39}$",
++ "description": "Resource type inside the owner (page, post, story, entity, offer, coupon, instrument, item…)."
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:~-]{0,127}$",
++ "description": "Resource id inside owner+type. Stable across revisions."
++ },
++ "revision": {
++ "type": "integer",
++ "minimum": 0,
++ "maximum": 9007199254740991,
++ "description": "Owner's monotonic revision of the resource. An older revision never overwrites a newer one; at equal revision a deletion wins."
++ },
++ "deleted": {
++ "type": "boolean",
++ "default": false,
++ "description": "Deletion marker. true = tombstone; every other content field is ignored."
++ },
++ "visibility": {
++ "enum": [
++ "public",
++ "unlisted",
++ "members",
++ "private",
++ "draft"
++ ],
++ "description": "public: anyone. unlisted: never listed except to ACL matches; fetchable by exact id by a signed-in subject. members: subjects, groups or entitlements in acl. private: subjects in acl.subjects only. draft: never served."
++ },
++ "acl": {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "Who may see a non-public document. Ignored for public documents. Empty means nobody (for members/private) or direct-id only (for unlisted).",
++ "properties": {
++ "subjects": {
++ "type": "array",
++ "maxItems": 200,
++ "uniqueItems": true,
++ "items": {
++ "type": "string",
++ "pattern": "^(usr|gst)_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "groups": {
++ "type": "array",
++ "maxItems": 100,
++ "uniqueItems": true,
++ "items": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_.:-]{0,127}$"
++ },
++ "description": "Opaque group keys owned by some service, e.g. role:admin (Network role), wiki.space:spc_…:member."
++ },
++ "entitlements": {
++ "type": "array",
++ "maxItems": 100,
++ "uniqueItems": true,
++ "items": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_.:-]{0,127}$"
++ },
++ "description": "Entitlement keys (VIP/Billing), e.g. vip.plan:pln_…. Only a first-party service that just resolved them may present them for a viewer."
++ }
++ }
++ },
++ "canonical_url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https?://",
++ "maxLength": 2048
++ },
++ "title": {
++ "type": "string",
++ "maxLength": 500
++ },
++ "summary": {
++ "type": "string",
++ "maxLength": 4000
++ },
++ "body": {
++ "type": "string",
++ "maxLength": 48000,
++ "description": "Plain text for full-text search (no HTML). Keep it under the Events payload limit when publishing through Events."
++ },
++ "facets": {
++ "type": "object",
++ "maxProperties": 20,
++ "propertyNames": {
++ "pattern": "^[a-z][a-z0-9_]{0,39}$"
++ },
++ "additionalProperties": {
++ "oneOf": [
++ {
++ "type": "string",
++ "maxLength": 200
++ },
++ {
++ "type": "number"
++ },
++ {
++ "type": "boolean"
++ },
++ {
++ "type": "array",
++ "maxItems": 50,
++ "items": {
++ "type": "string",
++ "maxLength": 200
++ }
++ }
++ ]
++ },
++ "description": "Filterable values (category, tags, space, author subject, currency…). Filter with facet.<key>=<value>."
++ },
++ "language": {
++ "type": "string",
++ "pattern": "^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$",
++ "description": "BCP 47 tag."
++ },
++ "authorship": {
++ "enum": [
++ "human",
++ "ai_assisted",
++ "ai_generated",
++ "imported"
++ ]
++ },
++ "provenance": {
++ "type": "array",
++ "maxItems": 50,
++ "description": "Where the content came from: typed references to source records (Sources items, citations, Media objects).",
++ "items": {
++ "type": "object",
++ "required": [
++ "service",
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "service": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]{1,39}$"
++ },
++ "type": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_]{1,39}$"
++ },
++ "id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 128
++ },
++ "revision": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "label": {
++ "type": "string",
++ "maxLength": 200
++ },
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https?://",
++ "maxLength": 2048
++ },
++ "retrieved_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "stub": {
++ "type": "boolean",
++ "description": "true when the reference came from a stub provider; the document is then never indexable."
++ }
++ }
++ }
++ },
++ "publication_state": {
++ "enum": [
++ "draft",
++ "scheduled",
++ "published",
++ "unpublished",
++ "retracted",
++ "archived"
++ ],
++ "description": "Only published documents are ever served."
++ },
++ "published_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "indexability": {
++ "type": "object",
++ "required": [
++ "decision"
++ ],
++ "additionalProperties": false,
++ "description": "The owner's deterministic indexability decision (publishing packages, roadmap §32.3). Search can only make it stricter.",
++ "properties": {
++ "decision": {
++ "enum": [
++ "index",
++ "noindex"
++ ]
++ },
++ "reasons": {
++ "type": "array",
++ "maxItems": 20,
++ "uniqueItems": true,
++ "items": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_]{1,63}$"
++ },
++ "description": "Known reasons: draft, private, members_only, unlisted, not_published, deleted, thin_content, duplicate_without_canonical, unsupported_claims, unsourced, stub_provider, sensitive_unreviewed, ai_unreviewed, missing_canonical_url, third_party_content, owner_decision."
++ }
++ }
++ }
++ },
++ "if": {
++ "properties": {
++ "deleted": {
++ "const": true
++ }
++ },
++ "required": [
++ "deleted"
++ ]
++ },
++ "then": {},
++ "else": {
++ "required": [
++ "visibility",
++ "publication_state",
++ "title",
++ "indexability"
++ ]
++ }
++ }
++ },
++ "sources.source": {
++ "version": "1.0.0",
++ "owner": "sources",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/sources/source.v1.json",
++ "title": "Source",
++ "description": "A registered source (roadmap §4.2 B, Wave 14): where it is fetched from and on what terms. Credentials are referenced by environment-variable NAME only. A source's existence is never permission to fetch: robots and terms are verified before it is enabled, and the fetcher re-checks robots.txt on every run.",
++ "type": "object",
++ "required": [
++ "key",
++ "name",
++ "type",
++ "category",
++ "endpoints",
++ "auth",
++ "min_interval_ms",
++ "poll_interval_sec",
++ "stale_after_sec",
++ "max_items",
++ "enabled",
++ "review_required",
++ "sensitivity",
++ "default_indexability"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "key": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
++ },
++ "name": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200
++ },
++ "type": {
++ "enum": [
++ "rss",
++ "atom",
++ "sitemap",
++ "jsonld",
++ "api",
++ "manual"
++ ]
++ },
++ "category": {
++ "enum": [
++ "news",
++ "blog",
++ "reviews",
++ "deals",
++ "coupons",
++ "trade"
++ ]
++ },
++ "homepage_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri"
++ },
++ "endpoints": {
++ "type": "array",
++ "maxItems": 20,
++ "items": {
++ "type": "object",
++ "required": [
++ "url"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "url": {
++ "type": "string",
++ "format": "uri",
++ "pattern": "^https?://"
++ },
++ "format": {
++ "enum": [
++ "json",
++ "xml"
++ ]
++ },
++ "items_path": {
++ "type": "string"
++ },
++ "item_kind": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_]{1,39}$"
++ },
++ "fields": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "string"
++ }
++ },
++ "extra": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "string"
++ }
++ }
++ }
++ }
++ },
++ "auth": {
++ "type": "object",
++ "required": [
++ "mode"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "mode": {
++ "enum": [
++ "none",
++ "header",
++ "bearer",
++ "query"
++ ]
++ },
++ "env": {
++ "type": "string",
++ "pattern": "^SOURCES_CRED_[A-Z0-9_]{2,60}$",
++ "description": "Name of the environment variable holding the credential. Never a value."
++ },
++ "header": {
++ "type": "string",
++ "pattern": "^[A-Za-z][A-Za-z0-9-]{0,63}$"
++ },
++ "param": {
++ "type": "string",
++ "pattern": "^[A-Za-z0-9_.-]{1,64}$"
++ }
++ }
++ },
++ "robots_note": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 2000
++ },
++ "terms_note": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 2000
++ },
++ "license_note": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 2000
++ },
++ "min_interval_ms": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "Rate limit: the least time between two requests to this source (the per-host floor and robots Crawl-delay may raise it)."
++ },
++ "poll_interval_sec": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "stale_after_sec": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "max_items": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "enabled": {
++ "type": "boolean"
++ },
++ "review_required": {
++ "type": "boolean",
++ "description": "Products must review what they derive from this source before it can be indexable."
++ },
++ "sensitivity": {
++ "enum": [
++ "none",
++ "financial",
++ "health",
++ "political",
++ "legal",
++ "adult"
++ ]
++ },
++ "default_indexability": {
++ "enum": [
++ "index",
++ "noindex"
++ ],
++ "description": "What a product's indexability gate starts from for content derived from this source."
++ },
++ "search_visibility": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "enum": [
++ "members",
++ null
++ ],
++ "description": "members = raw items are indexed in OpenVibe.Search for staff only (never indexable)."
++ },
++ "health": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "status": {
++ "enum": [
++ "healthy",
++ "stale",
++ "failing",
++ "never_fetched",
++ "disabled",
++ "manual"
++ ]
++ },
++ "stale": {
++ "type": "boolean"
++ },
++ "last_success_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "last_run_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "last_state": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "consecutive_failures": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "next_due_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "stale_after_sec": {
++ "type": "integer"
++ }
++ }
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ },
++ "sources.item": {
++ "version": "1.0.0",
++ "owner": "sources",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/sources/item.v1.json",
++ "title": "SourceItem",
++ "description": "One thing a source said, with its provenance: source, canonical URL, retrieval time, content hash, raw-body hash, parser version and the terms it was ingested under. A field the source did not state is null — never guessed. Items change only through successful fetches (a new revision per content change) or an explicit, audited removal.",
++ "type": "object",
++ "required": [
++ "id",
++ "source_key",
++ "category",
++ "kind",
++ "identity",
++ "canonical_url",
++ "title",
++ "summary",
++ "authors",
++ "published_at",
++ "source_updated_at",
++ "fields",
++ "revision",
++ "provenance",
++ "removed"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^itm_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "source_key": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9-]{1,63}$"
++ },
++ "category": {
++ "enum": [
++ "news",
++ "blog",
++ "reviews",
++ "deals",
++ "coupons",
++ "trade"
++ ]
++ },
++ "kind": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9_]{1,39}$",
++ "description": "article | url | sitemap | product | offer | review | review_signal | record | coupon …"
++ },
++ "identity": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 2048,
++ "description": "The source's own id (guid, @id, API id) or the canonical URL."
++ },
++ "canonical_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri"
++ },
++ "title": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "summary": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 1000,
++ "description": "Plain text, capped. Sources keeps metadata and short descriptions, not republishable full text."
++ },
++ "authors": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ }
++ },
++ "published_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "source_updated_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "fields": {
++ "type": "object",
++ "description": "Adapter-specific parsed fields (offers, ratings, prices as stated, API extras)."
++ },
++ "revision": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "provenance": {
++ "type": "object",
++ "required": [
++ "retrieved_at",
++ "first_seen_at",
++ "content_hash",
++ "raw_body_hash",
++ "parser_version",
++ "fetch_run_id",
++ "license_note",
++ "terms_note",
++ "entered_by"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "retrieved_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "Last successful fetch that contained this item (observation time)."
++ },
++ "first_seen_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "content_hash": {
++ "type": "string",
++ "pattern": "^[0-9a-f]{64}$"
++ },
++ "raw_body_hash": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^[0-9a-f]{64}$"
++ },
++ "parser_version": {
++ "type": "string",
++ "pattern": "^[a-z]+@\\d+$"
++ },
++ "fetch_run_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^frn_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "license_note": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "terms_note": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "entered_by": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "Principal that entered a manual item."
++ }
++ }
++ },
++ "removed": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "at",
++ "reason"
++ ],
++ "properties": {
++ "at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "reason": {
++ "type": "string"
++ }
++ }
++ },
++ "change_seq": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "revisions": {
++ "type": "array"
++ }
++ }
++ }
}
}
}
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 42a9c934..b6e9649a 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -283,3 +283,2918 @@ export interface ReleaseManifest {
min_client_release: string | null;
mixed_version_window_hours: number;
}
++
++/** [email protected] (owner: search) */
++/**
++ * What an owning service tells OpenVibe.Search about one of its resources (roadmap §15.12, §4.2 A). The owner stays the source of truth; Search keeps a revision-ordered, permission-aware copy for discovery. A document with deleted=true is a tombstone: it removes the resource from every result and wins over any upsert with the same or an older revision.
++ */
++export interface IndexDocument {
++ /**
++ * Owning service id (wiki, blog, news, community, sources…). Only that service may write the document.
++ */
++ owner: string;
++ /**
++ * Resource type inside the owner (page, post, story, entity, offer, coupon, instrument, item…).
++ */
++ type: string;
++ /**
++ * Resource id inside owner+type. Stable across revisions.
++ */
++ id: string;
++ /**
++ * Owner's monotonic revision of the resource. An older revision never overwrites a newer one; at equal revision a deletion wins.
++ */
++ revision: number;
++ /**
++ * Deletion marker. true = tombstone; every other content field is ignored.
++ */
++ deleted?: boolean;
++ /**
++ * public: anyone. unlisted: never listed except to ACL matches; fetchable by exact id by a signed-in subject. members: subjects, groups or entitlements in acl. private: subjects in acl.subjects only. draft: never served.
++ */
++ visibility?: "public" | "unlisted" | "members" | "private" | "draft";
++ /**
++ * Who may see a non-public document. Ignored for public documents. Empty means nobody (for members/private) or direct-id only (for unlisted).
++ */
++ acl?: {
++ /**
++ * @maxItems 200
++ */
++ subjects?: string[];
++ /**
++ * Opaque group keys owned by some service, e.g. role:admin (Network role), wiki.space:spc_…:member.
++ *
++ * @maxItems 100
++ */
++ groups?: string[];
++ /**
++ * Entitlement keys (VIP/Billing), e.g. vip.plan:pln_…. Only a first-party service that just resolved them may present them for a viewer.
++ *
++ * @maxItems 100
++ */
++ entitlements?: string[];
++ };
++ canonical_url?: string;
++ title?: string;
++ summary?: string;
++ /**
++ * Plain text for full-text search (no HTML). Keep it under the Events payload limit when publishing through Events.
++ */
++ body?: string;
++ /**
++ * Filterable values (category, tags, space, author subject, currency…). Filter with facet.<key>=<value>.
++ */
++ facets?: {
++ [k: string]: (string | number | boolean | string[]) | undefined;
++ };
++ /**
++ * BCP 47 tag.
++ */
++ language?: string;
++ authorship?: "human" | "ai_assisted" | "ai_generated" | "imported";
++ /**
++ * Where the content came from: typed references to source records (Sources items, citations, Media objects).
++ *
++ * @maxItems 50
++ */
++ provenance?: {
++ service: string;
++ type: string;
++ id: string;
++ revision?: number;
++ label?: string;
++ url?: string;
++ retrieved_at?: string;
++ /**
++ * true when the reference came from a stub provider; the document is then never indexable.
++ */
++ stub?: boolean;
++ }[];
++ /**
++ * Only published documents are ever served.
++ */
++ publication_state?: "draft" | "scheduled" | "published" | "unpublished" | "retracted" | "archived";
++ published_at?: string | null;
++ updated_at?: string | null;
++ /**
++ * The owner's deterministic indexability decision (publishing packages, roadmap §32.3). Search can only make it stricter.
++ */
++ indexability?: {
++ decision: "index" | "noindex";
++ /**
++ * Known reasons: draft, private, members_only, unlisted, not_published, deleted, thin_content, duplicate_without_canonical, unsupported_claims, unsourced, stub_provider, sensitive_unreviewed, ai_unreviewed, missing_canonical_url, third_party_content, owner_decision.
++ *
++ * @maxItems 20
++ */
++ reasons?:
++ | []
++ | [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]
++ | [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,
++ 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,
++ 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
++ ];
++ };
++}
++
++/** [email protected] (owner: sources) */
++/**
++ * A registered source (roadmap §4.2 B, Wave 14): where it is fetched from and on what terms. Credentials are referenced by environment-variable NAME only. A source's existence is never permission to fetch: robots and terms are verified before it is enabled, and the fetcher re-checks robots.txt on every run.
++ */
++export interface Source {
++ key: string;
++ name: string;
++ type: "rss" | "atom" | "sitemap" | "jsonld" | "api" | "manual";
++ category: "news" | "blog" | "reviews" | "deals" | "coupons" | "trade";
++ homepage_url?: string | null;
++ /**
++ * @maxItems 20
++ */
++ endpoints:
++ | []
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ]
++ | [
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ },
++ {
++ url: string;
++ format?: "json" | "xml";
++ items_path?: string;
++ item_kind?: string;
++ fields?: {
++ [k: string]: string | undefined;
++ };
++ extra?: {
++ [k: string]: string | undefined;
++ };
++ }
++ ];
++ auth: {
++ mode: "none" | "header" | "bearer" | "query";
++ /**
++ * Name of the environment variable holding the credential. Never a value.
++ */
++ env?: string;
++ header?: string;
++ param?: string;
++ };
++ robots_note?: string | null;
++ terms_note?: string | null;
++ license_note?: string | null;
++ /**
++ * Rate limit: the least time between two requests to this source (the per-host floor and robots Crawl-delay may raise it).
++ */
++ min_interval_ms: number;
++ poll_interval_sec: number;
++ stale_after_sec: number;
++ max_items: number;
++ enabled: boolean;
++ /**
++ * Products must review what they derive from this source before it can be indexable.
++ */
++ review_required: boolean;
++ sensitivity: "none" | "financial" | "health" | "political" | "legal" | "adult";
++ /**
++ * What a product's indexability gate starts from for content derived from this source.
++ */
++ default_indexability: "index" | "noindex";
++ /**
++ * members = raw items are indexed in OpenVibe.Search for staff only (never indexable).
++ */
++ search_visibility?: "members" | null;
++ health?: {
++ status?: "healthy" | "stale" | "failing" | "never_fetched" | "disabled" | "manual";
++ stale?: boolean;
++ last_success_at?: string | null;
++ last_run_at?: string | null;
++ last_state?: string | null;
++ consecutive_failures?: number;
++ next_due_at?: string | null;
++ stale_after_sec?: number;
++ };
++ created_at?: string;
++ updated_at?: string;
++}
++
++/** [email protected] (owner: sources) */
++/**
++ * One thing a source said, with its provenance: source, canonical URL, retrieval time, content hash, raw-body hash, parser version and the terms it was ingested under. A field the source did not state is null — never guessed. Items change only through successful fetches (a new revision per content change) or an explicit, audited removal.
++ */
++export interface SourceItem {
++ id: string;
++ source_key: string;
++ category: "news" | "blog" | "reviews" | "deals" | "coupons" | "trade";
++ /**
++ * article | url | sitemap | product | offer | review | review_signal | record | coupon …
++ */
++ kind: string;
++ /**
++ * The source's own id (guid, @id, API id) or the canonical URL.
++ */
++ identity: string;
++ canonical_url: string | null;
++ title: string | null;
++ /**
++ * Plain text, capped. Sources keeps metadata and short descriptions, not republishable full text.
++ */
++ summary: string | null;
++ authors: string[];
++ published_at: string | null;
++ source_updated_at: string | null;
++ /**
++ * Adapter-specific parsed fields (offers, ratings, prices as stated, API extras).
++ */
++ fields: {};
++ revision: number;
++ provenance: {
++ /**
++ * Last successful fetch that contained this item (observation time).
++ */
++ retrieved_at: string;
++ first_seen_at: string;
++ content_hash: string;
++ raw_body_hash: string | null;
++ parser_version: string;
++ fetch_run_id: string | null;
++ license_note: string | null;
++ terms_note: string | null;
++ /**
++ * Principal that entered a manual item.
++ */
++ entered_by: string | null;
++ };
++ removed: {
++ at: string;
++ reason: string;
++ } | null;
++ change_seq?: number;
++ revisions?: unknown[];
++}
ddiff --git a/manifests/capabilities/search.document.write.json b/manifests/capabilities/search.document.write.json
nnew file mode 100644
iindex 00000000..db4255f2
---- /dev/null
++++ b/manifests/capabilities/search.document.write.json
@@@ -0,0 +1,26 @@
++{
++ "id": "search.document.write",
++ "version": "1.0",
++ "owner": "search",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Index, replace or tombstone the calling service's own documents (search.index-document@1) and read its own inventory for reconciliation. The owner in the path must be the principal's service slug (svc:wiki writes owner wiki only). Revision-ordered: an older revision never overwrites a newer one, and at equal revision a deletion wins.",
++ "inputSchema": "search.index-document@1",
++ "permissions": [
++ "search:write"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "search-write",
++ "events": [
++ "search.document.indexed",
++ "search.document.removed"
++ ],
++ "implementedBy": [
++ "PUT|DELETE /api/v1/documents/:owner/:type/:id",
++ "GET /api/v1/owners/:owner/documents",
++ "GET /api/v1/owners/:owner/documents/:type/:id",
++ "GET /api/v1/owners/:owner/rejections"
++ ]
++}
ddiff --git a/manifests/capabilities/search.query.delegate.json b/manifests/capabilities/search.query.delegate.json
nnew file mode 100644
iindex 00000000..fd4e9ea6
---- /dev/null
++++ b/manifests/capabilities/search.query.delegate.json
@@@ -0,0 +1,21 @@
++{
++ "id": "search.query.delegate",
++ "version": "1.0",
++ "owner": "search",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Query the index on behalf of a person: X-OV-Subject (usr_/gst_) plus the X-OV-Groups and X-OV-Entitlements the calling first-party service has just resolved for that person. Without X-OV-Subject the service sees public documents only. Drafts, unpublished and deleted documents are never returned to anyone.",
++ "permissions": [
++ "search:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "search-query",
++ "events": [],
++ "implementedBy": [
++ "GET /api/v1/search",
++ "GET /api/v1/suggest",
++ "GET /api/v1/documents/:owner/:type/:id"
++ ]
++}
ddiff --git a/manifests/capabilities/sources.item.read.json b/manifests/capabilities/sources.item.read.json
nnew file mode 100644
iindex 00000000..ef1c497e
---- /dev/null
++++ b/manifests/capabilities/sources.item.read.json
@@@ -0,0 +1,21 @@
++{
++ "id": "sources.item.read",
++ "version": "1.0",
++ "owner": "sources",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Read source items with provenance (source, canonical URL, retrieval time, content and raw-body hashes, parser version, terms) in change order, and one item with its revisions. Each page states the staleness of the sources it contains.",
++ "outputSchema": "sources.item@1",
++ "permissions": [
++ "sources:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "sources-read",
++ "events": [],
++ "implementedBy": [
++ "GET /api/v1/items",
++ "GET /api/v1/items/:id"
++ ]
++}
ddiff --git a/manifests/capabilities/sources.source.manage.json b/manifests/capabilities/sources.source.manage.json
nnew file mode 100644
iindex 00000000..0f2c0926
---- /dev/null
++++ b/manifests/capabilities/sources.source.manage.json
@@@ -0,0 +1,28 @@
++{
++ "id": "sources.source.manage",
++ "version": "1.0",
++ "owner": "sources",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Create, change, enable or disable and delete (only while it has no items) sources; trigger a fetch; add items to manual sources with an evidence URL; remove an item with a reason (sticky). Enabling needs a terms note, and a robots note for fetched sources.",
++ "permissions": [
++ "sources:admin"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "sources-admin",
++ "events": [
++ "sources.item.created",
++ "sources.item.updated",
++ "sources.item.removed",
++ "sources.fetch.failed"
++ ],
++ "implementedBy": [
++ "POST /api/v1/sources",
++ "PATCH|DELETE /api/v1/sources/:key",
++ "POST /api/v1/sources/:key/fetch",
++ "POST /api/v1/sources/:key/items",
++ "DELETE /api/v1/items/:id"
++ ]
++}
ddiff --git a/manifests/capabilities/sources.source.read.json b/manifests/capabilities/sources.source.read.json
nnew file mode 100644
iindex 00000000..f6c7b54c
---- /dev/null
++++ b/manifests/capabilities/sources.source.read.json
@@@ -0,0 +1,24 @@
++{
++ "id": "sources.source.read",
++ "version": "1.0",
++ "owner": "sources",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "List registered sources with health and staleness, their fetch runs (every attempt with an explicit state) and the health summary. Credential values are never part of any response; only the variable name is.",
++ "outputSchema": "sources.source@1",
++ "permissions": [
++ "sources:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "sources-read",
++ "events": [],
++ "implementedBy": [
++ "GET /api/v1/sources",
++ "GET /api/v1/sources/:key",
++ "GET /api/v1/sources/:key/runs",
++ "GET /api/v1/runs",
++ "GET /api/v1/health"
++ ]
++}
ddiff --git a/manifests/services/search.json b/manifests/services/search.json
nnew file mode 100644
iindex 00000000..d9ed0b08
---- /dev/null
++++ b/manifests/services/search.json
@@@ -0,0 +1,30 @@
++{
++ "version": "0.1.0",
++ "contractRanges": {
++ "openvibe-contracts": ">=0.7.0 <1.0.0"
++ },
++ "id": "search",
++ "name": "OpenVibe.Search",
++ "status": "alpha",
++ "repository": "OpenVibers/OpenVibe.Search",
++ "domains": [
++ "search.openvibe.network"
++ ],
++ "publicOrigin": "https://search.openvibe.network",
++ "health": "/api/health",
++ "ready": "/api/ready",
++ "capabilities": [
++ "search.document.write",
++ "search.query.delegate"
++ ],
++ "eventsProduced": [
++ "search.document.indexed",
++ "search.document.removed"
++ ],
++ "eventsConsumed": [
++ "*.index_document.upserted",
++ "*.index_document.deleted"
++ ],
++ "namespacesOwned": [],
++ "notes": "Canonical index document (search.index-document@1), event-fed and direct indexing with revision ordering and tombstones, permission-aware query over SQLite FTS5 (docs/adr-engine.md). Anonymous queries need no token."
++}
ddiff --git a/manifests/services/sources.json b/manifests/services/sources.json
nnew file mode 100644
iindex 00000000..a488c7f2
---- /dev/null
++++ b/manifests/services/sources.json
@@@ -0,0 +1,32 @@
++{
++ "version": "0.1.0",
++ "contractRanges": {
++ "openvibe-contracts": ">=0.7.0 <1.0.0"
++ },
++ "id": "sources",
++ "name": "OpenVibe.Sources",
++ "status": "alpha",
++ "repository": "OpenVibers/OpenVibe.Sources",
++ "domains": [
++ "sources.openvibe.network"
++ ],
++ "publicOrigin": "https://sources.openvibe.network",
++ "health": "/api/health",
++ "ready": "/api/ready",
++ "capabilities": [
++ "sources.source.read",
++ "sources.item.read",
++ "sources.source.manage"
++ ],
++ "eventsProduced": [
++ "sources.item.created",
++ "sources.item.updated",
++ "sources.item.removed",
++ "sources.fetch.failed",
++ "sources.index_document.upserted",
++ "sources.index_document.deleted"
++ ],
++ "eventsConsumed": [],
++ "namespacesOwned": [],
++ "notes": "Source registry and ingestion workers (RSS/Atom, sitemaps, JSON-LD, mapped official APIs, manual). Every item carries provenance; every fetch is a recorded run with an explicit state; failures never create or modify items."
++}
ddiff --git a/package.json b/package.json
iindex a227af8e..ea65dfa8 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.11.0",
++ "version": "0.12.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"types": "generated/typescript/index.d.ts",