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

Configure Feed

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

Add new types for Signal to be exported by integration plugins (#83)

* Add new types for Signal to be exported by integration plugins

* rename to config over credentials as it may be more generic.

authored by

Juan Mrad and committed by
GitHub
1d9afe3c 21c87eb6

+94 -19
+3 -1
types/index.ts
··· 282 282 CoopIntegrationConfigEntry, 283 283 CoopIntegrationPlugin, 284 284 CoopIntegrationsConfig, 285 - IntegrationCredentialField, 285 + IntegrationConfigField, 286 286 IntegrationId, 287 287 IntegrationManifest, 288 288 ModelCard, 289 289 ModelCardField, 290 290 ModelCardSection, 291 291 ModelCardSubsection, 292 + PluginSignalContext, 293 + PluginSignalDescriptor, 292 294 StoredIntegrationConfigPayload, 293 295 } from './integration.js'; 294 296 export {
+88 -15
types/integration.ts
··· 94 94 } 95 95 96 96 /** 97 - * Describes a single credential field for integrations that require 98 - * API keys or other secrets. Used to generate or validate credential forms. 97 + * Describes a single configuration field for integrations that require 98 + * user-supplied config (e.g. API keys or other settings). Used to generate or validate config forms. 99 99 */ 100 - export type IntegrationCredentialField = Readonly<{ 101 - /** Form field key (e.g. "apiKey", "labelerVersions"). */ 100 + export type IntegrationConfigField = Readonly<{ 101 + /** Form field key (e.g. "apiKey", "truePercentage"). */ 102 102 key: string; 103 103 /** Human-readable label for the field. */ 104 104 label: string; ··· 119 119 export type IntegrationManifest = Readonly<{ 120 120 /** Unique integration id. Must be UPPER_SNAKE_CASE to align with GraphQL enums when used in COOP. */ 121 121 id: IntegrationId; 122 - /** Human-readable display name (e.g. "Google Content Safety API"). */ 122 + /** Human-readable display name shown in the UI (e.g. signal modal, integration cards). Exposed as Signal.integrationTitle. */ 123 123 name: string; 124 124 /** Semantic version of the integration plugin (e.g. "1.0.0"). */ 125 125 version: string; ··· 127 127 description?: string; 128 128 /** Link to documentation or product page. */ 129 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; 130 + /** Whether this integration requires the user to supply config (e.g. API key). */ 131 + requiresConfig: boolean; 136 132 /** 137 - * Schema for credential fields when requiresCredentials is true. 133 + * Schema for configuration fields when requiresConfig is true. 138 134 * Enables UI generation and validation without hardcoding per-integration forms. 139 135 */ 140 - credentialFields?: readonly IntegrationCredentialField[]; 136 + configurationFields?: readonly IntegrationConfigField[]; 141 137 /** 142 138 * Optional list of signal type ids this integration provides (e.g. "ZENTROPI_LABELER"). 143 139 * Used by the platform to associate signals with this integration for display and gating. ··· 151 147 * registering. 152 148 */ 153 149 modelCard?: ModelCard; 150 + /** 151 + * ------------------------------------------------------------ 152 + * LOGO/IMAGE SECTION: 153 + * ------------------------------------------------------------ 154 + * The following logo/image sections are optional. If none provided will use a fallback Coop logo. 155 + * 156 + * Provide either logoUrl and logoWithBackgroundUrl or logoPath and logoWithBackgroundPath. 157 + * 158 + * If you provide logoPath and logoWithBackgroundPath, the server will serve the files at 159 + * GET /api/v1/integration-logos/:integrationId and GET /api/v1/integration-logos/:integrationId/with-background 160 + * and set logoUrl and logoWithBackgroundUrl accordingly. 161 + * If you provide logoUrl and logoWithBackgroundUrl, the server will use those URLs directly. 162 + * Prefered size: ~180x180px for logoUrl and ~120x120px for logoWithBackgroundUrl. 163 + * Prefer a square or horizontal logo that scales well. 164 + */ 165 + logoUrl?: string; 166 + logoWithBackgroundUrl?: string; 167 + logoPath?: string; 168 + logoWithBackgroundPath?: string; 169 + }>; 170 + 171 + // --------------------------------------------------------------------------- 172 + // Plugin signals (for integrations that power routing/enforcement rules) 173 + // --------------------------------------------------------------------------- 174 + 175 + /** Context passed to plugin.createSignals() so the plugin can build signal instances with credential access. */ 176 + export type PluginSignalContext = Readonly<{ 177 + /** Integration id (e.g. "ACME_API") from the plugin manifest. */ 178 + integrationId: string; 179 + /** Get stored credential/config for an org. Resolves to the JSON stored for this integration. */ 180 + getCredential: (orgId: string) => Promise<Record<string, unknown>>; 181 + }>; 182 + 183 + /** Minimal signal descriptor returned by a plugin. The platform adapts this to its internal SignalBase. */ 184 + export type PluginSignalDescriptor = Readonly<{ 185 + /** Stable signal type id (e.g. "ACME_MODERATION_SIGNAL"). Must match one of manifest.signalTypeIds. */ 186 + id: Readonly<{ type: string }>; 187 + displayName: string; 188 + description: string; 189 + docsUrl: string | null; 190 + recommendedThresholds: Readonly<{ 191 + highPrecisionThreshold: string | number; 192 + highRecallThreshold: string | number; 193 + }> | null; 194 + supportedLanguages: readonly string[] | 'ALL'; 195 + pricingStructure: Readonly<{ type: 'FREE' | 'SUBSCRIPTION' }>; 196 + eligibleInputs: readonly string[]; 197 + outputType: Readonly<{ scalarType: string }>; 198 + getCost: () => number; 199 + /** Run the signal. Input shape is platform-defined; result must have outputType and score. */ 200 + run: (input: unknown) => Promise<unknown>; 201 + getDisabledInfo: (orgId: string) => Promise< 202 + | { disabled: false; disabledMessage?: string } 203 + | { disabled: true; disabledMessage: string } 204 + >; 205 + needsMatchingValues: boolean; 206 + eligibleSubcategories: ReadonlyArray<{ 207 + id: string; 208 + label: string; 209 + description?: string; 210 + childrenIds: readonly string[]; 211 + }>; 212 + needsActionPenalties: boolean; 213 + /** Integration id (same as context.integrationId). */ 214 + integration: string; 215 + allowedInAutomatedRules: boolean; 154 216 }>; 155 217 156 218 /** ··· 162 224 * const manifest: IntegrationManifest = { id: 'ACME_API', name: 'Acme API', ... }; 163 225 * const plugin: CoopIntegrationPlugin = { manifest }; 164 226 * export default plugin; 227 + * 228 + * To power routing/enforcement rules, also implement createSignals(context) and 229 + * return one descriptor per manifest.signalTypeIds entry. 165 230 */ 166 231 export type CoopIntegrationPlugin = Readonly<{ 167 232 manifest: IntegrationManifest; ··· 170 235 * If present, adopters can pass non-secret config in the integrations config file. 171 236 */ 172 237 configSchema?: unknown; 238 + /** 239 + * Optional. If this integration provides signals for use in rules, implement this. 240 + * Return one descriptor per signal type id listed in manifest.signalTypeIds. 241 + * The platform will register these so they appear in the rule builder and can be used in conditions. 242 + */ 243 + createSignals?: ( 244 + context: PluginSignalContext, 245 + ) => ReadonlyArray<Readonly<{ signalTypeId: string; signal: PluginSignalDescriptor }>>; 173 246 }>; 174 247 175 248 /** ··· 206 279 * Shape of the config stored in the database for each integration (per org). 207 280 * Stored in a generic table as JSON: one row per (org_id, integration_id) with 208 281 * config as a JSON-serializable object. Each integration defines its own required 209 - * fields via IntegrationManifest.credentialFields; the app validates and 282 + * fields via IntegrationManifest.configurationFields; the app validates and 210 283 * serializes/deserializes to this type. 211 284 * 212 285 * Only JSON-serializable values (no functions, symbols, or BigInt) should be ··· 232 305 typeof m.id === 'string' && 233 306 typeof m.name === 'string' && 234 307 typeof m.version === 'string' && 235 - typeof m.requiresCredentials === 'boolean' 308 + typeof m.requiresConfig === 'boolean' 236 309 ); 237 310 }
+2 -2
types/package-lock.json
··· 1 1 { 2 2 "name": "@roostorg/types", 3 - "version": "1.1.0", 3 + "version": "1.1.1", 4 4 "lockfileVersion": 2, 5 5 "requires": true, 6 6 "packages": { 7 7 "": { 8 8 "name": "@roostorg/types", 9 - "version": "1.1.0", 9 + "version": "1.1.1", 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.1.0", 4 + "version": "1.1.1", 5 5 "description": "Shared types across Coop services", 6 6 "module": "transpiled/index.js", 7 7 "typings": "./transpiled/index.d.ts",