@roostorg/types 1.0.49 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@roostorg/types",
3
3
  "type": "module",
4
- "version": "1.0.49",
5
- "description": "Shared types across Cove services",
4
+ "version": "1.1.1",
5
+ "description": "Shared types across Coop services",
6
6
  "module": "transpiled/index.js",
7
7
  "typings": "./transpiled/index.d.ts",
8
8
  "scripts": {
@@ -10,7 +10,7 @@
10
10
  "prepublishOnly": "npm run build",
11
11
  "test": "npm run build && node --test transpiled/test_scripts/"
12
12
  },
13
- "author": "",
13
+ "author": "Roostorg",
14
14
  "files": [
15
15
  "transpiled"
16
16
  ],
@@ -98,9 +98,6 @@ export type FieldScalarType<T extends FieldType> = T extends ScalarType ? T : Sc
98
98
  * ScalarTypes.GEOHASH are both represented as strings), so, without the label,
99
99
  * we wouldn't know unambiguously which ScalarType a value belongs to, which we
100
100
  * need sometimes (e.g., when deciding whether we can pass it to a signal).
101
- *
102
- * For why this type is written this way, see
103
- * https://github.com/protegoapi/protego/pull/20#discussion_r883974513
104
101
  */
105
102
  type EnumScalarType = ScalarTypes['STRING'] | ScalarTypes['NUMBER'];
106
103
  export type TaggedScalar<T extends ScalarType> = {
@@ -159,9 +156,6 @@ export declare function isMediaValue<T extends ScalarType>(it: TaggedScalar<T>):
159
156
  * legal values in the enum like.
160
157
  */
161
158
  export declare function makeEnumLike<T extends string>(strings: readonly T[]): { [K in T]: K; };
162
- export declare const hiveProjectNames: readonly ["Visual", "Text", "Speech", "OCR", "Demographic"];
163
- export type HiveProjectName = (typeof hiveProjectNames)[number];
164
- export declare function isHiveProjectName(name: string): name is HiveProjectName;
165
159
  export type SignalSubcategory = {
166
160
  id: string;
167
161
  label: string;
@@ -196,4 +190,5 @@ export declare const ItemTypeKind: {
196
190
  USER: "USER";
197
191
  };
198
192
  export type ItemTypeKind = keyof typeof ItemTypeKind;
199
- export {};
193
+ export type { CoopIntegrationConfigEntry, CoopIntegrationPlugin, CoopIntegrationsConfig, IntegrationConfigField, IntegrationId, IntegrationManifest, ModelCard, ModelCardField, ModelCardSection, ModelCardSubsection, PluginSignalContext, PluginSignalDescriptor, StoredIntegrationConfigPayload, } from './integration.js';
194
+ export { assertModelCardHasRequiredSections, isCoopIntegrationPlugin, REQUIRED_MODEL_CARD_SECTION_IDS, } from './integration.js';
@@ -3,7 +3,7 @@ import { isValid, parseJSON } from 'date-fns';
3
3
  // type is probably not ideal.
4
4
  //
5
5
  // NB: the ID type refers to ids for any type of entity, but USER_ID should be
6
- // used instead for fields that hold ids of users protego might know about. E.g.,
6
+ // used instead for fields that hold ids of users coop might know about. E.g.,
7
7
  // imagine a `Message` content type. Both the `to` and `from` fields could hold
8
8
  // ScalarTypes.USER_IDs, and then a rule could flag the message if _either_ the
9
9
  // sender or the recipient satisfies some condition (after passing the user id
@@ -84,16 +84,6 @@ export function isMediaValue(it) {
84
84
  export function makeEnumLike(strings) {
85
85
  return Object.fromEntries(strings.map((it) => [it, it]));
86
86
  }
87
- export const hiveProjectNames = [
88
- 'Visual',
89
- 'Text',
90
- 'Speech',
91
- 'OCR',
92
- 'Demographic',
93
- ];
94
- export function isHiveProjectName(name) {
95
- return hiveProjectNames.includes(name);
96
- }
97
87
  export function parseDateString(it) {
98
88
  return new Date(it);
99
89
  }
@@ -110,3 +100,4 @@ export function makeDateString(it) {
110
100
  : undefined;
111
101
  }
112
102
  export const ItemTypeKind = makeEnumLike(['CONTENT', 'THREAD', 'USER']);
103
+ export { assertModelCardHasRequiredSections, isCoopIntegrationPlugin, REQUIRED_MODEL_CARD_SECTION_IDS, } from './integration.js';
@@ -0,0 +1,267 @@
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
+ /** Unique identifier for the integration (e.g. "GOOGLE_CONTENT_SAFETY_API"). */
12
+ export type IntegrationId = string;
13
+ /**
14
+ * A single key-value row in a model card (e.g. "Release Date" -> "January 2026").
15
+ * Values are plain strings; the UI can linkify URLs or format as needed.
16
+ */
17
+ export type ModelCardField = Readonly<{
18
+ label: string;
19
+ value: string;
20
+ }>;
21
+ /**
22
+ * A named group of fields within a section (e.g. "Basic Information" with
23
+ * Model Name, Version, Release Date). Rendered as a bold subheading + key-value list.
24
+ */
25
+ export type ModelCardSubsection = Readonly<{
26
+ title: string;
27
+ fields: readonly ModelCardField[];
28
+ }>;
29
+ /**
30
+ * One collapsible section of a model card (e.g. "Model Details", "Training Data").
31
+ * Either subsections (with bold sub-headings) or top-level fields, or both.
32
+ */
33
+ export type ModelCardSection = Readonly<{
34
+ /** Stable id for the section (e.g. "modelDetails", "trainingData"). */
35
+ id: string;
36
+ /** Display title (e.g. "Model Details"). */
37
+ title: string;
38
+ /** Optional grouped key-value blocks with their own titles. */
39
+ subsections?: readonly ModelCardSubsection[];
40
+ /** Optional flat key-value list when there are no subsections. */
41
+ fields?: readonly ModelCardField[];
42
+ }>;
43
+ /**
44
+ * Model card: structured, JSON-backed metadata for an integration, so the UI
45
+ * can display it in a consistent but integration-specific way.
46
+ *
47
+ * Required: modelName and version (always shown). All sections are optional;
48
+ * the UI renders only those present. Sections can have subsections (e.g.
49
+ * "Basic Information", "Model Architecture") or flat fields.
50
+ */
51
+ export type ModelCard = Readonly<{
52
+ /** Required. Display name of the model (e.g. "GPT-4"). */
53
+ modelName: string;
54
+ /** Required. Version string (e.g. "1.0.0" or "v0.0"). */
55
+ version: string;
56
+ /** Optional. Release date or similar (e.g. "January 2026"). */
57
+ releaseDate?: string;
58
+ /** Optional. Ordered list of sections; each can be collapsed/expanded in the UI. */
59
+ sections?: readonly ModelCardSection[];
60
+ }>;
61
+ /**
62
+ * Section ids that every integration's model card must include.
63
+ * Use assertModelCardHasRequiredSections() to validate at runtime.
64
+ */
65
+ export declare const REQUIRED_MODEL_CARD_SECTION_IDS: readonly ["modelDetails", "technicalIntegration"];
66
+ /**
67
+ * Asserts that a model card has at least the required sections (basic information
68
+ * and technical integration). Call when registering integration manifests.
69
+ * @throws Error if any required section id is missing
70
+ */
71
+ export declare function assertModelCardHasRequiredSections(card: ModelCard): void;
72
+ /**
73
+ * Describes a single configuration field for integrations that require
74
+ * user-supplied config (e.g. API keys or other settings). Used to generate or validate config forms.
75
+ */
76
+ export type IntegrationConfigField = Readonly<{
77
+ /** Form field key (e.g. "apiKey", "truePercentage"). */
78
+ key: string;
79
+ /** Human-readable label for the field. */
80
+ label: string;
81
+ /** Whether the field is required. */
82
+ required: boolean;
83
+ /** Input type for the UI. */
84
+ inputType: 'text' | 'password' | 'json' | 'array';
85
+ /** Optional placeholder or hint. */
86
+ placeholder?: string;
87
+ /** Optional description for the field. */
88
+ description?: string;
89
+ }>;
90
+ /**
91
+ * Metadata and capability description for an integration.
92
+ * This is the stable, structured information shown to users (name, docs, logos, etc.).
93
+ */
94
+ export type IntegrationManifest = Readonly<{
95
+ /** Unique integration id. Must be UPPER_SNAKE_CASE to align with GraphQL enums when used in COOP. */
96
+ id: IntegrationId;
97
+ /** Human-readable display name shown in the UI (e.g. signal modal, integration cards). Exposed as Signal.integrationTitle. */
98
+ name: string;
99
+ /** Semantic version of the integration plugin (e.g. "1.0.0"). */
100
+ version: string;
101
+ /** Short description for listings and tooltips. */
102
+ description?: string;
103
+ /** Link to documentation or product page. */
104
+ docsUrl?: string;
105
+ /** Whether this integration requires the user to supply config (e.g. API key). */
106
+ requiresConfig: boolean;
107
+ /**
108
+ * Schema for configuration fields when requiresConfig is true.
109
+ * Enables UI generation and validation without hardcoding per-integration forms.
110
+ */
111
+ configurationFields?: readonly IntegrationConfigField[];
112
+ /**
113
+ * Optional list of signal type ids this integration provides (e.g. "ZENTROPI_LABELER").
114
+ * Used by the platform to associate signals with this integration for display and gating.
115
+ */
116
+ signalTypeIds?: readonly string[];
117
+ /**
118
+ * Model card: structured metadata (model name, version, sections) for the UI.
119
+ * When present, the integration detail page renders it. Built-in integrations
120
+ * should always provide a model card with at least sections "modelDetails" and
121
+ * "technicalIntegration"; use assertModelCardHasRequiredSections() when
122
+ * registering.
123
+ */
124
+ modelCard?: ModelCard;
125
+ /**
126
+ * ------------------------------------------------------------
127
+ * LOGO/IMAGE SECTION:
128
+ * ------------------------------------------------------------
129
+ * The following logo/image sections are optional. If none provided will use a fallback Coop logo.
130
+ *
131
+ * Provide either logoUrl and logoWithBackgroundUrl or logoPath and logoWithBackgroundPath.
132
+ *
133
+ * If you provide logoPath and logoWithBackgroundPath, the server will serve the files at
134
+ * GET /api/v1/integration-logos/:integrationId and GET /api/v1/integration-logos/:integrationId/with-background
135
+ * and set logoUrl and logoWithBackgroundUrl accordingly.
136
+ * If you provide logoUrl and logoWithBackgroundUrl, the server will use those URLs directly.
137
+ * Prefered size: ~180x180px for logoUrl and ~120x120px for logoWithBackgroundUrl.
138
+ * Prefer a square or horizontal logo that scales well.
139
+ */
140
+ logoUrl?: string;
141
+ logoWithBackgroundUrl?: string;
142
+ logoPath?: string;
143
+ logoWithBackgroundPath?: string;
144
+ }>;
145
+ /** Context passed to plugin.createSignals() so the plugin can build signal instances with credential access. */
146
+ export type PluginSignalContext = Readonly<{
147
+ /** Integration id (e.g. "ACME_API") from the plugin manifest. */
148
+ integrationId: string;
149
+ /** Get stored credential/config for an org. Resolves to the JSON stored for this integration. */
150
+ getCredential: (orgId: string) => Promise<Record<string, unknown>>;
151
+ }>;
152
+ /** Minimal signal descriptor returned by a plugin. The platform adapts this to its internal SignalBase. */
153
+ export type PluginSignalDescriptor = Readonly<{
154
+ /** Stable signal type id (e.g. "ACME_MODERATION_SIGNAL"). Must match one of manifest.signalTypeIds. */
155
+ id: Readonly<{
156
+ type: string;
157
+ }>;
158
+ displayName: string;
159
+ description: string;
160
+ docsUrl: string | null;
161
+ recommendedThresholds: Readonly<{
162
+ highPrecisionThreshold: string | number;
163
+ highRecallThreshold: string | number;
164
+ }> | null;
165
+ supportedLanguages: readonly string[] | 'ALL';
166
+ pricingStructure: Readonly<{
167
+ type: 'FREE' | 'SUBSCRIPTION';
168
+ }>;
169
+ eligibleInputs: readonly string[];
170
+ outputType: Readonly<{
171
+ scalarType: string;
172
+ }>;
173
+ getCost: () => number;
174
+ /** Run the signal. Input shape is platform-defined; result must have outputType and score. */
175
+ run: (input: unknown) => Promise<unknown>;
176
+ getDisabledInfo: (orgId: string) => Promise<{
177
+ disabled: false;
178
+ disabledMessage?: string;
179
+ } | {
180
+ disabled: true;
181
+ disabledMessage: string;
182
+ }>;
183
+ needsMatchingValues: boolean;
184
+ eligibleSubcategories: ReadonlyArray<{
185
+ id: string;
186
+ label: string;
187
+ description?: string;
188
+ childrenIds: readonly string[];
189
+ }>;
190
+ needsActionPenalties: boolean;
191
+ /** Integration id (same as context.integrationId). */
192
+ integration: string;
193
+ allowedInAutomatedRules: boolean;
194
+ }>;
195
+ /**
196
+ * Plugin contract that third-party integration packages must implement.
197
+ * Export this as the default export (or a named export) from the package.
198
+ *
199
+ * Example (in an integration package):
200
+ *
201
+ * const manifest: IntegrationManifest = { id: 'ACME_API', name: 'Acme API', ... };
202
+ * const plugin: CoopIntegrationPlugin = { manifest };
203
+ * export default plugin;
204
+ *
205
+ * To power routing/enforcement rules, also implement createSignals(context) and
206
+ * return one descriptor per manifest.signalTypeIds entry.
207
+ */
208
+ export type CoopIntegrationPlugin = Readonly<{
209
+ manifest: IntegrationManifest;
210
+ /**
211
+ * Optional static config shape for this integration.
212
+ * If present, adopters can pass non-secret config in the integrations config file.
213
+ */
214
+ configSchema?: unknown;
215
+ /**
216
+ * Optional. If this integration provides signals for use in rules, implement this.
217
+ * Return one descriptor per signal type id listed in manifest.signalTypeIds.
218
+ * The platform will register these so they appear in the rule builder and can be used in conditions.
219
+ */
220
+ createSignals?: (context: PluginSignalContext) => ReadonlyArray<Readonly<{
221
+ signalTypeId: string;
222
+ signal: PluginSignalDescriptor;
223
+ }>>;
224
+ }>;
225
+ /**
226
+ * Single entry in the adopters' integrations config file.
227
+ * Enables or disables a plugin and optionally passes static config.
228
+ */
229
+ export type CoopIntegrationConfigEntry = Readonly<{
230
+ /** NPM package name (e.g. "@acme/coop-integration-acme") or path to a local module. */
231
+ package: string;
232
+ /** Whether this integration is enabled. Default true if omitted. */
233
+ enabled?: boolean;
234
+ /** Optional static config passed to the integration (no secrets here; use org credentials in-app). */
235
+ config?: Readonly<Record<string, unknown>>;
236
+ }>;
237
+ /**
238
+ * Root type for the integrations config file that adopters use to register
239
+ * plugin integrations. Can be JSON or a JS/TS module that exports this shape.
240
+ *
241
+ * Example integrations.config.json:
242
+ *
243
+ * {
244
+ * "integrations": [
245
+ * { "package": "@acme/coop-integration-acme", "enabled": true },
246
+ * { "package": "./local-integrations/foo", "config": { "endpoint": "https://..." } }
247
+ * ]
248
+ * }
249
+ */
250
+ export type CoopIntegrationsConfig = Readonly<{
251
+ integrations: readonly CoopIntegrationConfigEntry[];
252
+ }>;
253
+ /**
254
+ * Shape of the config stored in the database for each integration (per org).
255
+ * Stored in a generic table as JSON: one row per (org_id, integration_id) with
256
+ * config as a JSON-serializable object. Each integration defines its own required
257
+ * fields via IntegrationManifest.configurationFields; the app validates and
258
+ * serializes/deserializes to this type.
259
+ *
260
+ * Only JSON-serializable values (no functions, symbols, or BigInt) should be
261
+ * included so the payload can be stored in a JSONB or TEXT column.
262
+ */
263
+ export type StoredIntegrationConfigPayload = Readonly<Record<string, unknown>>;
264
+ /**
265
+ * Type guard for CoopIntegrationPlugin.
266
+ */
267
+ export declare function isCoopIntegrationPlugin(value: unknown): value is CoopIntegrationPlugin;
@@ -0,0 +1,48 @@
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
+ * Section ids that every integration's model card must include.
13
+ * Use assertModelCardHasRequiredSections() to validate at runtime.
14
+ */
15
+ export const REQUIRED_MODEL_CARD_SECTION_IDS = [
16
+ 'modelDetails',
17
+ 'technicalIntegration',
18
+ ];
19
+ /**
20
+ * Asserts that a model card has at least the required sections (basic information
21
+ * and technical integration). Call when registering integration manifests.
22
+ * @throws Error if any required section id is missing
23
+ */
24
+ export function assertModelCardHasRequiredSections(card) {
25
+ const sectionIds = new Set((card.sections ?? []).map((s) => s.id));
26
+ for (const requiredId of REQUIRED_MODEL_CARD_SECTION_IDS) {
27
+ if (!sectionIds.has(requiredId)) {
28
+ throw new Error(`Model card must include a section with id "${requiredId}" (e.g. Basic Information / Model Details and Technical Integration).`);
29
+ }
30
+ }
31
+ }
32
+ /**
33
+ * Type guard for CoopIntegrationPlugin.
34
+ */
35
+ export function isCoopIntegrationPlugin(value) {
36
+ if (value == null || typeof value !== 'object') {
37
+ return false;
38
+ }
39
+ const o = value;
40
+ if (o.manifest == null || typeof o.manifest !== 'object') {
41
+ return false;
42
+ }
43
+ const m = o.manifest;
44
+ return (typeof m.id === 'string' &&
45
+ typeof m.name === 'string' &&
46
+ typeof m.version === 'string' &&
47
+ typeof m.requiresConfig === 'boolean');
48
+ }