Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Generate types for integrations (#82)

* [types] Generate new types for integrations

* increase version for types

authored by

Juan Mrad and committed by
GitHub
21c87eb6 bdd9faf3

+260 -3
+20
types/index.ts
··· 276 276 277 277 export const ItemTypeKind = makeEnumLike(['CONTENT', 'THREAD', 'USER']); 278 278 export type ItemTypeKind = keyof typeof ItemTypeKind; 279 + 280 + // Integration plugin types (for third-party integrations and adopters' config) 281 + export type { 282 + CoopIntegrationConfigEntry, 283 + CoopIntegrationPlugin, 284 + CoopIntegrationsConfig, 285 + IntegrationCredentialField, 286 + IntegrationId, 287 + IntegrationManifest, 288 + ModelCard, 289 + ModelCardField, 290 + ModelCardSection, 291 + ModelCardSubsection, 292 + StoredIntegrationConfigPayload, 293 + } from './integration.js'; 294 + export { 295 + assertModelCardHasRequiredSections, 296 + isCoopIntegrationPlugin, 297 + REQUIRED_MODEL_CARD_SECTION_IDS, 298 + } from './integration.js';
+237
types/integration.ts
··· 1 + /** 2 + * Integration plugin types for COOP. 3 + * 4 + * These types define the contract that third-party integration packages 5 + * implement so adopters can install and configure them without adding 6 + * every integration to the main COOP repo. 7 + * 8 + * Integration packages export a CoopIntegrationPlugin; adopters register 9 + * them via an integrations config file (see CoopIntegrationsConfig). 10 + */ 11 + 12 + /** Unique identifier for the integration (e.g. "GOOGLE_CONTENT_SAFETY_API"). */ 13 + export type IntegrationId = string; 14 + 15 + // --------------------------------------------------------------------------- 16 + // Model card (optional, per-integration metadata for display in the UI) 17 + // --------------------------------------------------------------------------- 18 + 19 + /** 20 + * A single key-value row in a model card (e.g. "Release Date" -> "January 2026"). 21 + * Values are plain strings; the UI can linkify URLs or format as needed. 22 + */ 23 + export type ModelCardField = Readonly<{ 24 + label: string; 25 + value: string; 26 + }>; 27 + 28 + /** 29 + * A named group of fields within a section (e.g. "Basic Information" with 30 + * Model Name, Version, Release Date). Rendered as a bold subheading + key-value list. 31 + */ 32 + export type ModelCardSubsection = Readonly<{ 33 + title: string; 34 + fields: readonly ModelCardField[]; 35 + }>; 36 + 37 + /** 38 + * One collapsible section of a model card (e.g. "Model Details", "Training Data"). 39 + * Either subsections (with bold sub-headings) or top-level fields, or both. 40 + */ 41 + export type ModelCardSection = Readonly<{ 42 + /** Stable id for the section (e.g. "modelDetails", "trainingData"). */ 43 + id: string; 44 + /** Display title (e.g. "Model Details"). */ 45 + title: string; 46 + /** Optional grouped key-value blocks with their own titles. */ 47 + subsections?: readonly ModelCardSubsection[]; 48 + /** Optional flat key-value list when there are no subsections. */ 49 + fields?: readonly ModelCardField[]; 50 + }>; 51 + 52 + /** 53 + * Model card: structured, JSON-backed metadata for an integration, so the UI 54 + * can display it in a consistent but integration-specific way. 55 + * 56 + * Required: modelName and version (always shown). All sections are optional; 57 + * the UI renders only those present. Sections can have subsections (e.g. 58 + * "Basic Information", "Model Architecture") or flat fields. 59 + */ 60 + export type ModelCard = Readonly<{ 61 + /** Required. Display name of the model (e.g. "GPT-4"). */ 62 + modelName: string; 63 + /** Required. Version string (e.g. "1.0.0" or "v0.0"). */ 64 + version: string; 65 + /** Optional. Release date or similar (e.g. "January 2026"). */ 66 + releaseDate?: string; 67 + /** Optional. Ordered list of sections; each can be collapsed/expanded in the UI. */ 68 + sections?: readonly ModelCardSection[]; 69 + }>; 70 + 71 + /** 72 + * Section ids that every integration's model card must include. 73 + * Use assertModelCardHasRequiredSections() to validate at runtime. 74 + */ 75 + export const REQUIRED_MODEL_CARD_SECTION_IDS = [ 76 + 'modelDetails', 77 + 'technicalIntegration', 78 + ] as const; 79 + 80 + /** 81 + * Asserts that a model card has at least the required sections (basic information 82 + * and technical integration). Call when registering integration manifests. 83 + * @throws Error if any required section id is missing 84 + */ 85 + export function assertModelCardHasRequiredSections(card: ModelCard): void { 86 + const sectionIds = new Set((card.sections ?? []).map((s) => s.id)); 87 + for (const requiredId of REQUIRED_MODEL_CARD_SECTION_IDS) { 88 + if (!sectionIds.has(requiredId)) { 89 + throw new Error( 90 + `Model card must include a section with id "${requiredId}" (e.g. Basic Information / Model Details and Technical Integration).`, 91 + ); 92 + } 93 + } 94 + } 95 + 96 + /** 97 + * Describes a single credential field for integrations that require 98 + * API keys or other secrets. Used to generate or validate credential forms. 99 + */ 100 + export type IntegrationCredentialField = Readonly<{ 101 + /** Form field key (e.g. "apiKey", "labelerVersions"). */ 102 + key: string; 103 + /** Human-readable label for the field. */ 104 + label: string; 105 + /** Whether the field is required. */ 106 + required: boolean; 107 + /** Input type for the UI. */ 108 + inputType: 'text' | 'password' | 'json' | 'array'; 109 + /** Optional placeholder or hint. */ 110 + placeholder?: string; 111 + /** Optional description for the field. */ 112 + description?: string; 113 + }>; 114 + 115 + /** 116 + * Metadata and capability description for an integration. 117 + * This is the stable, structured information shown to users (name, docs, logos, etc.). 118 + */ 119 + export type IntegrationManifest = Readonly<{ 120 + /** Unique integration id. Must be UPPER_SNAKE_CASE to align with GraphQL enums when used in COOP. */ 121 + id: IntegrationId; 122 + /** Human-readable display name (e.g. "Google Content Safety API"). */ 123 + name: string; 124 + /** Semantic version of the integration plugin (e.g. "1.0.0"). */ 125 + version: string; 126 + /** Short description for listings and tooltips. */ 127 + description?: string; 128 + /** Link to documentation or product page. */ 129 + docsUrl?: string; 130 + /** Optional URL to a logo image (or asset key if using a bundler). */ 131 + logoUrl?: string; 132 + /** Optional URL to a logo variant (e.g. with background) for cards. */ 133 + logoWithBackgroundUrl?: string; 134 + /** Whether this integration requires the user to supply credentials (e.g. API key). */ 135 + requiresCredentials: boolean; 136 + /** 137 + * Schema for credential fields when requiresCredentials is true. 138 + * Enables UI generation and validation without hardcoding per-integration forms. 139 + */ 140 + credentialFields?: readonly IntegrationCredentialField[]; 141 + /** 142 + * Optional list of signal type ids this integration provides (e.g. "ZENTROPI_LABELER"). 143 + * Used by the platform to associate signals with this integration for display and gating. 144 + */ 145 + signalTypeIds?: readonly string[]; 146 + /** 147 + * Model card: structured metadata (model name, version, sections) for the UI. 148 + * When present, the integration detail page renders it. Built-in integrations 149 + * should always provide a model card with at least sections "modelDetails" and 150 + * "technicalIntegration"; use assertModelCardHasRequiredSections() when 151 + * registering. 152 + */ 153 + modelCard?: ModelCard; 154 + }>; 155 + 156 + /** 157 + * Plugin contract that third-party integration packages must implement. 158 + * Export this as the default export (or a named export) from the package. 159 + * 160 + * Example (in an integration package): 161 + * 162 + * const manifest: IntegrationManifest = { id: 'ACME_API', name: 'Acme API', ... }; 163 + * const plugin: CoopIntegrationPlugin = { manifest }; 164 + * export default plugin; 165 + */ 166 + export type CoopIntegrationPlugin = Readonly<{ 167 + manifest: IntegrationManifest; 168 + /** 169 + * Optional static config shape for this integration. 170 + * If present, adopters can pass non-secret config in the integrations config file. 171 + */ 172 + configSchema?: unknown; 173 + }>; 174 + 175 + /** 176 + * Single entry in the adopters' integrations config file. 177 + * Enables or disables a plugin and optionally passes static config. 178 + */ 179 + export type CoopIntegrationConfigEntry = Readonly<{ 180 + /** NPM package name (e.g. "@acme/coop-integration-acme") or path to a local module. */ 181 + package: string; 182 + /** Whether this integration is enabled. Default true if omitted. */ 183 + enabled?: boolean; 184 + /** Optional static config passed to the integration (no secrets here; use org credentials in-app). */ 185 + config?: Readonly<Record<string, unknown>>; 186 + }>; 187 + 188 + /** 189 + * Root type for the integrations config file that adopters use to register 190 + * plugin integrations. Can be JSON or a JS/TS module that exports this shape. 191 + * 192 + * Example integrations.config.json: 193 + * 194 + * { 195 + * "integrations": [ 196 + * { "package": "@acme/coop-integration-acme", "enabled": true }, 197 + * { "package": "./local-integrations/foo", "config": { "endpoint": "https://..." } } 198 + * ] 199 + * } 200 + */ 201 + export type CoopIntegrationsConfig = Readonly<{ 202 + integrations: readonly CoopIntegrationConfigEntry[]; 203 + }>; 204 + 205 + /** 206 + * Shape of the config stored in the database for each integration (per org). 207 + * Stored in a generic table as JSON: one row per (org_id, integration_id) with 208 + * config as a JSON-serializable object. Each integration defines its own required 209 + * fields via IntegrationManifest.credentialFields; the app validates and 210 + * serializes/deserializes to this type. 211 + * 212 + * Only JSON-serializable values (no functions, symbols, or BigInt) should be 213 + * included so the payload can be stored in a JSONB or TEXT column. 214 + */ 215 + export type StoredIntegrationConfigPayload = Readonly<Record<string, unknown>>; 216 + 217 + /** 218 + * Type guard for CoopIntegrationPlugin. 219 + */ 220 + export function isCoopIntegrationPlugin( 221 + value: unknown, 222 + ): value is CoopIntegrationPlugin { 223 + if (value == null || typeof value !== 'object') { 224 + return false; 225 + } 226 + const o = value as Record<string, unknown>; 227 + if (o.manifest == null || typeof o.manifest !== 'object') { 228 + return false; 229 + } 230 + const m = o.manifest as Record<string, unknown>; 231 + return ( 232 + typeof m.id === 'string' && 233 + typeof m.name === 'string' && 234 + typeof m.version === 'string' && 235 + typeof m.requiresCredentials === 'boolean' 236 + ); 237 + }
+2 -2
types/package-lock.json
··· 1 1 { 2 2 "name": "@roostorg/types", 3 - "version": "1.0.49", 3 + "version": "1.1.0", 4 4 "lockfileVersion": 2, 5 5 "requires": true, 6 6 "packages": { 7 7 "": { 8 8 "name": "@roostorg/types", 9 - "version": "1.0.49", 9 + "version": "1.1.0", 10 10 "license": "ISC", 11 11 "dependencies": { 12 12 "date-fns": "^2.29.3",
+1 -1
types/package.json
··· 1 1 { 2 2 "name": "@roostorg/types", 3 3 "type": "module", 4 - "version": "1.0.49", 4 + "version": "1.1.0", 5 5 "description": "Shared types across Coop services", 6 6 "module": "transpiled/index.js", 7 7 "typings": "./transpiled/index.d.ts",