@newhomestar/sdk 0.5.2 → 0.6.5
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/dist/credentials.d.ts +99 -0
- package/dist/credentials.js +754 -0
- package/dist/index.d.ts +107 -3
- package/dist/index.js +264 -17
- package/dist/integration.d.ts +398 -0
- package/dist/integration.js +416 -0
- package/dist/integrationSpec.d.ts +204 -0
- package/dist/integrationSpec.js +186 -0
- package/dist/workerSchema.d.ts +45 -2
- package/dist/workerSchema.js +28 -0
- package/package.json +2 -1
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
// nova-sdk – Integration Definition API
|
|
2
|
+
// =====================================================
|
|
3
|
+
// Code-first integration definitions with Zod schemas.
|
|
4
|
+
// Integrations are containerized services (like workers) that ALSO
|
|
5
|
+
// carry schema/event/function configuration synced to the platform DB.
|
|
6
|
+
//
|
|
7
|
+
// TWO API styles supported:
|
|
8
|
+
// Verbose: integrationSchema({ name, slug, schemaType, schema, version })
|
|
9
|
+
// Lean: schema("entity", z.object({...})) ← slug/name inferred from key
|
|
10
|
+
// -----------------------------------------------------------
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
/** Symbol used to tag lean schema/event wrappers (non-enumerable) */
|
|
13
|
+
const NOVA_SCHEMA = Symbol.for('nova.schema');
|
|
14
|
+
const NOVA_EVENT = Symbol.for('nova.event');
|
|
15
|
+
/**
|
|
16
|
+
* **Verbose** helper — define a schema with all metadata explicitly.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const WorkerProfile = integrationSchema({
|
|
21
|
+
* name: "Worker Profile",
|
|
22
|
+
* slug: "worker_profile",
|
|
23
|
+
* schemaType: "entity",
|
|
24
|
+
* schema: z.object({ workerId: z.string() }),
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function integrationSchema(cfg) {
|
|
29
|
+
return cfg;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* **Lean** helper — slug and name are inferred from the key in `schemas: {}`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const User = schema("entity", z.object({
|
|
37
|
+
* id: z.string(),
|
|
38
|
+
* email: z.string().email(),
|
|
39
|
+
* }));
|
|
40
|
+
*
|
|
41
|
+
* // In defineIntegration:
|
|
42
|
+
* schemas: { user: User }
|
|
43
|
+
* // → slug: "user", name: "User", schemaType: "entity"
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function schema(schemaType, zodSchema, opts) {
|
|
47
|
+
const def = {
|
|
48
|
+
name: '', // filled from key by defineIntegration()
|
|
49
|
+
slug: '', // filled from key by defineIntegration()
|
|
50
|
+
schemaType,
|
|
51
|
+
schema: zodSchema,
|
|
52
|
+
version: opts?.version ?? '1.0.0',
|
|
53
|
+
description: opts?.description,
|
|
54
|
+
shape: zodSchema, // convenience alias for direct Zod reference
|
|
55
|
+
};
|
|
56
|
+
Object.defineProperty(def, NOVA_SCHEMA, { value: true, enumerable: false });
|
|
57
|
+
return def;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* **Verbose** helper — define an event with all metadata explicitly.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const workerHire = integrationEvent({
|
|
65
|
+
* slug: "worker.hire",
|
|
66
|
+
* name: "Worker Hired",
|
|
67
|
+
* direction: "inbound",
|
|
68
|
+
* category: "lifecycle",
|
|
69
|
+
* severity: "info",
|
|
70
|
+
* payloadSchema: "new_hire_payload",
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function integrationEvent(cfg) {
|
|
75
|
+
return cfg;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* **Lean** helper — slug and name inferred from key in `events: {}`.
|
|
79
|
+
* Payload can reference a `schema()` result directly (type-safe, no strings).
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const User = schema("entity", z.object({ id: z.string() }));
|
|
84
|
+
*
|
|
85
|
+
* // In defineIntegration:
|
|
86
|
+
* events: {
|
|
87
|
+
* user_synced: event("outbound", { payload: User }),
|
|
88
|
+
* sync_failed: event("outbound", { severity: "error" }),
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export function event(direction, opts) {
|
|
93
|
+
const def = {
|
|
94
|
+
slug: '', // filled from key by defineIntegration()
|
|
95
|
+
name: '', // filled from key by defineIntegration()
|
|
96
|
+
direction,
|
|
97
|
+
severity: opts?.severity,
|
|
98
|
+
category: opts?.category,
|
|
99
|
+
description: opts?.description,
|
|
100
|
+
};
|
|
101
|
+
// Store payload reference for resolution during normalization
|
|
102
|
+
if (opts?.payload) {
|
|
103
|
+
Object.defineProperty(def, '__payloadRef', { value: opts.payload, enumerable: false });
|
|
104
|
+
}
|
|
105
|
+
Object.defineProperty(def, NOVA_EVENT, { value: true, enumerable: false });
|
|
106
|
+
return def;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Helper to define an integration function (API endpoint).
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const listWorkers = integrationFunction({
|
|
114
|
+
* slug: "list_workers",
|
|
115
|
+
* name: "List Workers",
|
|
116
|
+
* httpMethod: "GET",
|
|
117
|
+
* endpointPath: "/hr/v2/workers",
|
|
118
|
+
* responseSchema: "worker_profile",
|
|
119
|
+
* requiredScopes: ["api"],
|
|
120
|
+
* category: "workers",
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export function integrationFunction(cfg) {
|
|
125
|
+
return cfg;
|
|
126
|
+
}
|
|
127
|
+
/*─────────────────────────* Zod Validation Schema *──────────────────────────*/
|
|
128
|
+
const IntegrationSchemaDefSchema = z.object({
|
|
129
|
+
name: z.string(),
|
|
130
|
+
slug: z.string().regex(/^[a-z][a-z0-9_]*$/, 'Schema slug must be snake_case'),
|
|
131
|
+
description: z.string().optional(),
|
|
132
|
+
schemaType: z.enum(['request', 'response', 'entity', 'webhook_payload', 'configuration']),
|
|
133
|
+
schema: z.any(), // Zod schema instance
|
|
134
|
+
version: z.string().optional(),
|
|
135
|
+
});
|
|
136
|
+
const IntegrationEventDefSchema = z.object({
|
|
137
|
+
slug: z.string(),
|
|
138
|
+
name: z.string(),
|
|
139
|
+
direction: z.enum(['inbound', 'outbound', 'bidirectional']),
|
|
140
|
+
description: z.string().optional(),
|
|
141
|
+
category: z.string().optional(),
|
|
142
|
+
severity: z.enum(['info', 'warning', 'error', 'critical']).optional(),
|
|
143
|
+
payloadSchema: z.string().optional(),
|
|
144
|
+
});
|
|
145
|
+
const IntegrationFunctionDefSchema = z.object({
|
|
146
|
+
slug: z.string().regex(/^[a-z][a-z0-9_]*$/, 'Function slug must be snake_case'),
|
|
147
|
+
name: z.string(),
|
|
148
|
+
httpMethod: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
|
|
149
|
+
endpointPath: z.string(),
|
|
150
|
+
description: z.string().optional(),
|
|
151
|
+
requestSchema: z.string().optional(),
|
|
152
|
+
responseSchema: z.string().optional(),
|
|
153
|
+
requiredScopes: z.array(z.string()).optional(),
|
|
154
|
+
category: z.string().optional(),
|
|
155
|
+
capabilities: z.array(z.union([z.string(), z.record(z.string(), z.unknown())])).optional(),
|
|
156
|
+
});
|
|
157
|
+
export const IntegrationDefSchema = z.object({
|
|
158
|
+
slug: z.string().regex(/^[a-z][a-z0-9_]*$/, 'Integration slug must be snake_case'),
|
|
159
|
+
name: z.string(),
|
|
160
|
+
description: z.string().optional(),
|
|
161
|
+
integrationType: z.enum(['oidc', 'oauth2', 'api_key']),
|
|
162
|
+
category: z.string().optional(),
|
|
163
|
+
logoUrl: z.string().optional(),
|
|
164
|
+
color: z.string().optional(),
|
|
165
|
+
authorizationEndpoint: z.string().optional(),
|
|
166
|
+
tokenEndpoint: z.string().optional(),
|
|
167
|
+
userinfoEndpoint: z.string().optional(),
|
|
168
|
+
revocationEndpoint: z.string().optional(),
|
|
169
|
+
jwksUri: z.string().optional(),
|
|
170
|
+
baseUrl: z.string().optional(),
|
|
171
|
+
scopes: z.array(z.string()).optional(),
|
|
172
|
+
queue: z.string(),
|
|
173
|
+
displayName: z.string().optional(),
|
|
174
|
+
icon: z.string().optional(),
|
|
175
|
+
tags: z.array(z.string()).optional(),
|
|
176
|
+
ui: z.object({
|
|
177
|
+
category: z.string().optional(),
|
|
178
|
+
color: z.string().optional(),
|
|
179
|
+
}).optional(),
|
|
180
|
+
resources: z.object({
|
|
181
|
+
cpu: z.string(),
|
|
182
|
+
memory: z.string(),
|
|
183
|
+
}).optional(),
|
|
184
|
+
envSpec: z.array(z.object({
|
|
185
|
+
name: z.string(),
|
|
186
|
+
secret: z.boolean(),
|
|
187
|
+
default: z.string().optional(),
|
|
188
|
+
})).optional(),
|
|
189
|
+
actions: z.record(z.string(), z.any()),
|
|
190
|
+
schemas: z.record(z.string(), IntegrationSchemaDefSchema),
|
|
191
|
+
events: z.record(z.string(), IntegrationEventDefSchema),
|
|
192
|
+
functions: z.record(z.string(), IntegrationFunctionDefSchema),
|
|
193
|
+
});
|
|
194
|
+
/**
|
|
195
|
+
* Validate an integration definition before build or push.
|
|
196
|
+
*
|
|
197
|
+
* Performs conditional validation based on `integrationType`:
|
|
198
|
+
* - **Always required**: slug, name, queue, baseUrl, ≥1 action
|
|
199
|
+
* - **OAuth2/OIDC required**: authorizationEndpoint, tokenEndpoint
|
|
200
|
+
* - **OIDC warned**: jwksUri, userinfoEndpoint
|
|
201
|
+
* - **Warnings**: no schemas, no scopes, non-HTTPS URLs, no health action
|
|
202
|
+
*
|
|
203
|
+
* Called automatically during `nova integrations build` and `nova integrations push`.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* const result = validateIntegration(myIntegration);
|
|
208
|
+
* if (!result.valid) {
|
|
209
|
+
* result.errors.forEach(e => console.error(`❌ ${e}`));
|
|
210
|
+
* process.exit(1);
|
|
211
|
+
* }
|
|
212
|
+
* result.warnings.forEach(w => console.warn(`⚠️ ${w}`));
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export function validateIntegration(def) {
|
|
216
|
+
const errors = [];
|
|
217
|
+
const warnings = [];
|
|
218
|
+
// ── Always required ──
|
|
219
|
+
if (!def.slug)
|
|
220
|
+
errors.push("'slug' is required");
|
|
221
|
+
if (!def.name)
|
|
222
|
+
errors.push("'name' is required");
|
|
223
|
+
if (!def.queue)
|
|
224
|
+
errors.push("'queue' is required");
|
|
225
|
+
if (!def.baseUrl || def.baseUrl.trim() === '') {
|
|
226
|
+
errors.push("'baseUrl' is required — the base URL for API calls (e.g., https://api.example.com)");
|
|
227
|
+
}
|
|
228
|
+
if (!def.actions || Object.keys(def.actions).length === 0) {
|
|
229
|
+
errors.push("at least one action is required");
|
|
230
|
+
}
|
|
231
|
+
// ── OAuth2 / OIDC specific ──
|
|
232
|
+
if (def.integrationType === 'oauth2' || def.integrationType === 'oidc') {
|
|
233
|
+
if (!def.authorizationEndpoint || def.authorizationEndpoint.trim() === '') {
|
|
234
|
+
errors.push(`'authorizationEndpoint' is required for ${def.integrationType} integrations`);
|
|
235
|
+
}
|
|
236
|
+
if (!def.tokenEndpoint || def.tokenEndpoint.trim() === '') {
|
|
237
|
+
errors.push(`'tokenEndpoint' is required for ${def.integrationType} integrations`);
|
|
238
|
+
}
|
|
239
|
+
if (!def.scopes || def.scopes.length === 0) {
|
|
240
|
+
warnings.push(`no 'scopes' defined for ${def.integrationType} integration — most providers require at least one`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// ── OIDC specific ──
|
|
244
|
+
if (def.integrationType === 'oidc') {
|
|
245
|
+
if (!def.jwksUri) {
|
|
246
|
+
warnings.push("'jwksUri' not set for OIDC integration — required for token verification");
|
|
247
|
+
}
|
|
248
|
+
if (!def.userinfoEndpoint) {
|
|
249
|
+
warnings.push("'userinfoEndpoint' not set for OIDC integration");
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// ── URL format validation ──
|
|
253
|
+
const urlFields = [
|
|
254
|
+
['authorizationEndpoint', def.authorizationEndpoint],
|
|
255
|
+
['tokenEndpoint', def.tokenEndpoint],
|
|
256
|
+
['baseUrl', def.baseUrl],
|
|
257
|
+
['userinfoEndpoint', def.userinfoEndpoint],
|
|
258
|
+
['revocationEndpoint', def.revocationEndpoint],
|
|
259
|
+
['jwksUri', def.jwksUri],
|
|
260
|
+
];
|
|
261
|
+
for (const [fieldName, value] of urlFields) {
|
|
262
|
+
if (value && value.trim() !== '' && !value.startsWith('https://') && !value.startsWith('http://localhost')) {
|
|
263
|
+
warnings.push(`'${fieldName}' should use HTTPS: "${value}"`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// ── Structural checks ──
|
|
267
|
+
if (!def.schemas || Object.keys(def.schemas).length === 0) {
|
|
268
|
+
warnings.push("no schemas defined — consider adding at least one entity schema");
|
|
269
|
+
}
|
|
270
|
+
if (def.actions && !def.actions['health'] && !def.actions['healthCheck']) {
|
|
271
|
+
warnings.push("no 'health' action defined — recommended for all integrations");
|
|
272
|
+
}
|
|
273
|
+
if (!def.envSpec || def.envSpec.length === 0) {
|
|
274
|
+
warnings.push("no 'envSpec' defined — environment variables won't be validated at deploy time");
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
valid: errors.length === 0,
|
|
278
|
+
errors,
|
|
279
|
+
warnings,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/*─────────────────────────* defineIntegration() *──────────────────────────*/
|
|
283
|
+
/**
|
|
284
|
+
* Register an integration definition — the single source of truth for both
|
|
285
|
+
* the containerized runtime AND the platform configuration.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```ts
|
|
289
|
+
* export default defineIntegration({
|
|
290
|
+
* slug: "adp_workforce_now",
|
|
291
|
+
* name: "ADP Workforce Now",
|
|
292
|
+
* integrationType: "oidc",
|
|
293
|
+
* queue: "adp_integration_queue",
|
|
294
|
+
* schemas: { ... },
|
|
295
|
+
* events: { ... },
|
|
296
|
+
* functions: { ... },
|
|
297
|
+
* actions: {
|
|
298
|
+
* health: action({ ... }),
|
|
299
|
+
* handleWebhook: action({ ... }),
|
|
300
|
+
* },
|
|
301
|
+
* });
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
/** Convert a key like "user_profile" → "User Profile" */
|
|
305
|
+
function slugToName(slug) {
|
|
306
|
+
return slug
|
|
307
|
+
.replace(/_/g, ' ')
|
|
308
|
+
.replace(/\b\w/g, c => c.toUpperCase());
|
|
309
|
+
}
|
|
310
|
+
/** Convert camelCase key like "listUsers" → "list_users" */
|
|
311
|
+
function camelToSnake(str) {
|
|
312
|
+
return str.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
|
|
313
|
+
}
|
|
314
|
+
export function defineIntegration(def) {
|
|
315
|
+
// ── Phase 1: Normalize lean schemas ──
|
|
316
|
+
// Fill slug/name from the key when using schema() helper
|
|
317
|
+
for (const [key, schemaDef] of Object.entries(def.schemas)) {
|
|
318
|
+
if (!schemaDef.slug || schemaDef.slug === '') {
|
|
319
|
+
schemaDef.slug = key;
|
|
320
|
+
}
|
|
321
|
+
if (!schemaDef.name || schemaDef.name === '') {
|
|
322
|
+
schemaDef.name = slugToName(key);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// ── Phase 2: Normalize lean events ──
|
|
326
|
+
// Fill slug/name from the key and resolve payload references
|
|
327
|
+
for (const [key, eventDef] of Object.entries(def.events)) {
|
|
328
|
+
if (!eventDef.slug || eventDef.slug === '') {
|
|
329
|
+
eventDef.slug = key;
|
|
330
|
+
}
|
|
331
|
+
if (!eventDef.name || eventDef.name === '') {
|
|
332
|
+
eventDef.name = slugToName(key);
|
|
333
|
+
}
|
|
334
|
+
// Resolve direct payload reference → schema slug string
|
|
335
|
+
const payloadRef = eventDef.__payloadRef;
|
|
336
|
+
if (payloadRef && !eventDef.payloadSchema) {
|
|
337
|
+
for (const [schemaKey, schemaDef] of Object.entries(def.schemas)) {
|
|
338
|
+
if (schemaDef === payloadRef ||
|
|
339
|
+
schemaDef.shape === payloadRef ||
|
|
340
|
+
schemaDef.schema === payloadRef) {
|
|
341
|
+
eventDef.payloadSchema = schemaKey;
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// ── Phase 3: Auto-extract functions from ALL actions ──
|
|
348
|
+
// Every action is promoted to a function entry so it's synced to the DB
|
|
349
|
+
// and visible in the Service Mode tester. Actions with scopes get those
|
|
350
|
+
// recorded; actions without scopes (e.g., health, handleWebhook) are
|
|
351
|
+
// still registered so the admin UI can discover and invoke them.
|
|
352
|
+
if (!def.functions) {
|
|
353
|
+
def.functions = {};
|
|
354
|
+
}
|
|
355
|
+
for (const [key, actionDef] of Object.entries(def.actions)) {
|
|
356
|
+
const snakeKey = camelToSnake(key);
|
|
357
|
+
// Skip if already explicitly declared in functions map
|
|
358
|
+
if (def.functions[snakeKey])
|
|
359
|
+
continue;
|
|
360
|
+
const scopes = actionDef.scopes || actionDef.requiredScopes;
|
|
361
|
+
const fnEntry = {
|
|
362
|
+
slug: snakeKey,
|
|
363
|
+
name: slugToName(snakeKey),
|
|
364
|
+
httpMethod: (actionDef.method || 'POST').toUpperCase(),
|
|
365
|
+
endpointPath: actionDef.path || `/${snakeKey}`,
|
|
366
|
+
description: actionDef.description || '',
|
|
367
|
+
requiredScopes: scopes && Array.isArray(scopes) ? scopes : [],
|
|
368
|
+
category: actionDef.category || '',
|
|
369
|
+
capabilities: actionDef.capabilities || [],
|
|
370
|
+
};
|
|
371
|
+
def.functions[snakeKey] = fnEntry;
|
|
372
|
+
// Store Zod input/output as non-enumerable props for build step to
|
|
373
|
+
// convert to JSON Schema → inputSchema / outputSchema DB columns.
|
|
374
|
+
if (actionDef.input) {
|
|
375
|
+
Object.defineProperty(fnEntry, '__inputZod', {
|
|
376
|
+
value: actionDef.input,
|
|
377
|
+
enumerable: false,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
if (actionDef.output) {
|
|
381
|
+
Object.defineProperty(fnEntry, '__outputZod', {
|
|
382
|
+
value: actionDef.output,
|
|
383
|
+
enumerable: false,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
// Store params metadata for the CLI build step to embed
|
|
387
|
+
// x-nova-* extensions into JSON Schema output.
|
|
388
|
+
if (actionDef.params) {
|
|
389
|
+
Object.defineProperty(fnEntry, '__paramsMeta', {
|
|
390
|
+
value: actionDef.params,
|
|
391
|
+
enumerable: false,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// ── Phase 4: Validate with Zod ──
|
|
396
|
+
IntegrationDefSchema.parse(def);
|
|
397
|
+
// ── Phase 5: Cross-validate schema references in events ──
|
|
398
|
+
for (const [eventKey, eventDef] of Object.entries(def.events)) {
|
|
399
|
+
if (eventDef.payloadSchema && !def.schemas[eventDef.payloadSchema]) {
|
|
400
|
+
throw new Error(`Event '${eventKey}' references payloadSchema '${eventDef.payloadSchema}' ` +
|
|
401
|
+
`which is not defined in schemas. Available schemas: ${Object.keys(def.schemas).join(', ')}`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// ── Phase 6: Cross-validate schema references in functions ──
|
|
405
|
+
for (const [fnKey, fn] of Object.entries(def.functions)) {
|
|
406
|
+
if (fn.requestSchema && !def.schemas[fn.requestSchema]) {
|
|
407
|
+
throw new Error(`Function '${fnKey}' references requestSchema '${fn.requestSchema}' ` +
|
|
408
|
+
`which is not defined in schemas. Available schemas: ${Object.keys(def.schemas).join(', ')}`);
|
|
409
|
+
}
|
|
410
|
+
if (fn.responseSchema && !def.schemas[fn.responseSchema]) {
|
|
411
|
+
throw new Error(`Function '${fnKey}' references responseSchema '${fn.responseSchema}' ` +
|
|
412
|
+
`which is not defined in schemas. Available schemas: ${Object.keys(def.schemas).join(', ')}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return def;
|
|
416
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const IntegrationSpecSchema: z.ZodObject<{
|
|
3
|
+
apiVersion: z.ZodString;
|
|
4
|
+
kind: z.ZodLiteral<"Integration">;
|
|
5
|
+
metadata: z.ZodObject<{
|
|
6
|
+
slug: z.ZodString;
|
|
7
|
+
name: z.ZodString;
|
|
8
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
9
|
+
description: z.ZodOptional<z.ZodString>;
|
|
10
|
+
category: z.ZodOptional<z.ZodString>;
|
|
11
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
12
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
|
+
logoUrl: z.ZodOptional<z.ZodString>;
|
|
14
|
+
color: z.ZodOptional<z.ZodString>;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
spec: z.ZodObject<{
|
|
17
|
+
type: z.ZodEnum<{
|
|
18
|
+
api_key: "api_key";
|
|
19
|
+
oidc: "oidc";
|
|
20
|
+
oauth2: "oauth2";
|
|
21
|
+
}>;
|
|
22
|
+
endpoints: z.ZodOptional<z.ZodObject<{
|
|
23
|
+
authorization: z.ZodOptional<z.ZodString>;
|
|
24
|
+
token: z.ZodOptional<z.ZodString>;
|
|
25
|
+
userinfo: z.ZodOptional<z.ZodString>;
|
|
26
|
+
revocation: z.ZodOptional<z.ZodString>;
|
|
27
|
+
jwks: z.ZodOptional<z.ZodString>;
|
|
28
|
+
baseUrl: z.ZodOptional<z.ZodString>;
|
|
29
|
+
}, z.core.$strip>>;
|
|
30
|
+
scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
31
|
+
runtime: z.ZodObject<{
|
|
32
|
+
type: z.ZodString;
|
|
33
|
+
image: z.ZodString;
|
|
34
|
+
queue: z.ZodString;
|
|
35
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
command: z.ZodArray<z.ZodString>;
|
|
37
|
+
resources: z.ZodObject<{
|
|
38
|
+
cpu: z.ZodString;
|
|
39
|
+
memory: z.ZodString;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
envSpec: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
42
|
+
name: z.ZodString;
|
|
43
|
+
value: z.ZodOptional<z.ZodString>;
|
|
44
|
+
secret: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
+
default: z.ZodOptional<z.ZodString>;
|
|
46
|
+
}, z.core.$strip>>>;
|
|
47
|
+
}, z.core.$strip>;
|
|
48
|
+
actions: z.ZodArray<z.ZodObject<{
|
|
49
|
+
name: z.ZodString;
|
|
50
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
51
|
+
description: z.ZodOptional<z.ZodString>;
|
|
52
|
+
async: z.ZodOptional<z.ZodBoolean>;
|
|
53
|
+
input: z.ZodOptional<z.ZodUnknown>;
|
|
54
|
+
output: z.ZodOptional<z.ZodUnknown>;
|
|
55
|
+
schema: z.ZodOptional<z.ZodObject<{
|
|
56
|
+
input: z.ZodString;
|
|
57
|
+
output: z.ZodString;
|
|
58
|
+
}, z.core.$strip>>;
|
|
59
|
+
fga: z.ZodOptional<z.ZodObject<{
|
|
60
|
+
resourceType: z.ZodString;
|
|
61
|
+
relation: z.ZodString;
|
|
62
|
+
}, z.core.$strip>>;
|
|
63
|
+
capabilities: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
64
|
+
type: z.ZodLiteral<"webhook">;
|
|
65
|
+
eventTypes: z.ZodArray<z.ZodString>;
|
|
66
|
+
source: z.ZodString;
|
|
67
|
+
endpoint: z.ZodOptional<z.ZodString>;
|
|
68
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
69
|
+
authentication: z.ZodOptional<z.ZodObject<{
|
|
70
|
+
type: z.ZodEnum<{
|
|
71
|
+
bearer: "bearer";
|
|
72
|
+
basic: "basic";
|
|
73
|
+
api_key: "api_key";
|
|
74
|
+
signature: "signature";
|
|
75
|
+
}>;
|
|
76
|
+
config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
77
|
+
}, z.core.$strip>>;
|
|
78
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
79
|
+
type: z.ZodLiteral<"scheduled">;
|
|
80
|
+
cron: z.ZodString;
|
|
81
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
82
|
+
description: z.ZodOptional<z.ZodString>;
|
|
83
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
84
|
+
type: z.ZodLiteral<"queue">;
|
|
85
|
+
topics: z.ZodArray<z.ZodString>;
|
|
86
|
+
queueName: z.ZodOptional<z.ZodString>;
|
|
87
|
+
consumerGroup: z.ZodOptional<z.ZodString>;
|
|
88
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
89
|
+
type: z.ZodLiteral<"stream">;
|
|
90
|
+
streamName: z.ZodString;
|
|
91
|
+
eventTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
92
|
+
consumerGroup: z.ZodOptional<z.ZodString>;
|
|
93
|
+
}, z.core.$strip>]>>>;
|
|
94
|
+
}, z.core.$strip>>;
|
|
95
|
+
capabilities: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
96
|
+
type: z.ZodLiteral<"webhook">;
|
|
97
|
+
eventTypes: z.ZodArray<z.ZodString>;
|
|
98
|
+
source: z.ZodString;
|
|
99
|
+
endpoint: z.ZodOptional<z.ZodString>;
|
|
100
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
101
|
+
authentication: z.ZodOptional<z.ZodObject<{
|
|
102
|
+
type: z.ZodEnum<{
|
|
103
|
+
bearer: "bearer";
|
|
104
|
+
basic: "basic";
|
|
105
|
+
api_key: "api_key";
|
|
106
|
+
signature: "signature";
|
|
107
|
+
}>;
|
|
108
|
+
config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
109
|
+
}, z.core.$strip>>;
|
|
110
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
111
|
+
type: z.ZodLiteral<"scheduled">;
|
|
112
|
+
cron: z.ZodString;
|
|
113
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
114
|
+
description: z.ZodOptional<z.ZodString>;
|
|
115
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
116
|
+
type: z.ZodLiteral<"queue">;
|
|
117
|
+
topics: z.ZodArray<z.ZodString>;
|
|
118
|
+
queueName: z.ZodOptional<z.ZodString>;
|
|
119
|
+
consumerGroup: z.ZodOptional<z.ZodString>;
|
|
120
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
121
|
+
type: z.ZodLiteral<"stream">;
|
|
122
|
+
streamName: z.ZodString;
|
|
123
|
+
eventTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
124
|
+
consumerGroup: z.ZodOptional<z.ZodString>;
|
|
125
|
+
}, z.core.$strip>]>>>;
|
|
126
|
+
schemas: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
127
|
+
slug: z.ZodString;
|
|
128
|
+
name: z.ZodString;
|
|
129
|
+
type: z.ZodEnum<{
|
|
130
|
+
response: "response";
|
|
131
|
+
request: "request";
|
|
132
|
+
entity: "entity";
|
|
133
|
+
webhook_payload: "webhook_payload";
|
|
134
|
+
configuration: "configuration";
|
|
135
|
+
}>;
|
|
136
|
+
description: z.ZodOptional<z.ZodString>;
|
|
137
|
+
schema: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
|
|
138
|
+
version: z.ZodOptional<z.ZodString>;
|
|
139
|
+
fieldCount: z.ZodOptional<z.ZodNumber>;
|
|
140
|
+
}, z.core.$strip>>>;
|
|
141
|
+
events: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
142
|
+
slug: z.ZodString;
|
|
143
|
+
name: z.ZodString;
|
|
144
|
+
direction: z.ZodEnum<{
|
|
145
|
+
inbound: "inbound";
|
|
146
|
+
outbound: "outbound";
|
|
147
|
+
bidirectional: "bidirectional";
|
|
148
|
+
}>;
|
|
149
|
+
description: z.ZodOptional<z.ZodString>;
|
|
150
|
+
category: z.ZodOptional<z.ZodString>;
|
|
151
|
+
severity: z.ZodOptional<z.ZodEnum<{
|
|
152
|
+
error: "error";
|
|
153
|
+
info: "info";
|
|
154
|
+
warning: "warning";
|
|
155
|
+
critical: "critical";
|
|
156
|
+
}>>;
|
|
157
|
+
payloadSchema: z.ZodOptional<z.ZodString>;
|
|
158
|
+
}, z.core.$strip>>>;
|
|
159
|
+
functions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
160
|
+
slug: z.ZodString;
|
|
161
|
+
name: z.ZodString;
|
|
162
|
+
httpMethod: z.ZodEnum<{
|
|
163
|
+
GET: "GET";
|
|
164
|
+
POST: "POST";
|
|
165
|
+
PATCH: "PATCH";
|
|
166
|
+
DELETE: "DELETE";
|
|
167
|
+
PUT: "PUT";
|
|
168
|
+
}>;
|
|
169
|
+
endpointPath: z.ZodString;
|
|
170
|
+
description: z.ZodOptional<z.ZodString>;
|
|
171
|
+
requestSchema: z.ZodOptional<z.ZodString>;
|
|
172
|
+
responseSchema: z.ZodOptional<z.ZodString>;
|
|
173
|
+
requiredScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
174
|
+
category: z.ZodOptional<z.ZodString>;
|
|
175
|
+
}, z.core.$strip>>>;
|
|
176
|
+
}, z.core.$strip>;
|
|
177
|
+
build: z.ZodOptional<z.ZodObject<{
|
|
178
|
+
dockerfile: z.ZodString;
|
|
179
|
+
context: z.ZodString;
|
|
180
|
+
}, z.core.$strip>>;
|
|
181
|
+
ui: z.ZodOptional<z.ZodObject<{
|
|
182
|
+
category: z.ZodOptional<z.ZodString>;
|
|
183
|
+
color: z.ZodOptional<z.ZodString>;
|
|
184
|
+
}, z.core.$strip>>;
|
|
185
|
+
fga: z.ZodOptional<z.ZodObject<{
|
|
186
|
+
types: z.ZodArray<z.ZodObject<{
|
|
187
|
+
name: z.ZodString;
|
|
188
|
+
relations: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
189
|
+
computedUserset: z.ZodObject<{
|
|
190
|
+
object: z.ZodString;
|
|
191
|
+
relation: z.ZodString;
|
|
192
|
+
}, z.core.$strip>;
|
|
193
|
+
}, z.core.$strip>]>>;
|
|
194
|
+
}, z.core.$strip>>;
|
|
195
|
+
}, z.core.$strip>>;
|
|
196
|
+
}, z.core.$strip>;
|
|
197
|
+
export type IntegrationSpec = z.infer<typeof IntegrationSpecSchema>;
|
|
198
|
+
/**
|
|
199
|
+
* Parse nova-integration.yaml content and validate against the IntegrationSpec schema.
|
|
200
|
+
* @param yamlContent String contents of a nova-integration.yaml specification
|
|
201
|
+
* @returns Parsed IntegrationSpec object
|
|
202
|
+
* @throws Error with validation details if parsing or validation fail
|
|
203
|
+
*/
|
|
204
|
+
export declare function parseIntegrationSpec(yamlContent: string): IntegrationSpec;
|