UNPKG

n8n

Version:

n8n Workflow Automation Tool

223 lines 9.28 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.applyProviderShapeNormalizers = applyProviderShapeNormalizers; exports.findProviderShapeViolation = findProviderShapeViolation; const PLACEHOLDER_B64 = 'iVBORw0KGgo'; const DEFAULT_HUBSPOT_VID = 3234574; function isPlainObject(value) { return typeof value === 'object' && value !== null && !Array.isArray(value); } function nowUnix() { return Math.floor(Date.now() / 1000); } function isOpenAiImagesGeneration(path) { return path.endsWith('/images/generations'); } function isGeminiGenerate(host, path) { return host === 'generativelanguage.googleapis.com' && path.includes('generatecontent'); } function isRedditWrite(host, path, method) { if (method.toUpperCase() !== 'POST' || !host.includes('reddit.com')) return null; if (path.includes('api/comment')) return 'comment'; if (path.includes('api/submit')) return 'submit'; return null; } function isHubspotUpsert(host, path) { return host.includes('hubapi.com') && path.includes('/contacts/v1/contact/createorupdate'); } function isGoogleDocsBatchUpdate(host, path) { return (host === 'docs.googleapis.com' && path.includes('/documents/') && path.includes(':batchupdate')); } function extractAnswerText(body) { if (body === null || body === undefined) return ''; if (typeof body === 'string') return body; if (!isPlainObject(body)) return String(body); if (typeof body.text === 'string') return body.text; if (typeof body.content === 'string') return body.content; if (typeof body.output_text === 'string') return body.output_text; if (typeof body.message === 'string') return body.message; return JSON.stringify(body); } function normalizeOpenAiImages(spec) { const body = spec.body; let base = {}; let rawEntries; if (isPlainObject(body) && Array.isArray(body.data)) { base = body; rawEntries = body.data; } else if (Array.isArray(body)) { rawEntries = body; } else if (isPlainObject(body) && ('b64_json' in body || 'url' in body)) { rawEntries = [body]; } else if (isPlainObject(body)) { base = body; } const entries = rawEntries && rawEntries.length > 0 ? rawEntries : [{}]; const data = entries.map(coerceImageEntry); spec.body = { created: nowUnix(), ...base, data }; } function coerceImageEntry(entry) { if (typeof entry === 'string') return { b64_json: entry }; if (!isPlainObject(entry)) return { b64_json: PLACEHOLDER_B64 }; if (typeof entry.b64_json === 'string' && entry.b64_json.length > 0) return entry; return { ...entry, b64_json: PLACEHOLDER_B64 }; } function normalizeGemini(spec) { if (hasGeminiCandidates(spec.body)) return; const text = extractAnswerText(spec.body); spec.body = { candidates: [{ content: { parts: [{ text }], role: 'model' }, finishReason: 'STOP', index: 0 }], usageMetadata: { promptTokenCount: 0, candidatesTokenCount: 0, totalTokenCount: 0 }, }; } function hasGeminiCandidates(body) { if (!isPlainObject(body) || !Array.isArray(body.candidates) || body.candidates.length === 0) { return false; } const first = body.candidates[0]; if (!isPlainObject(first) || !isPlainObject(first.content)) return false; return Array.isArray(first.content.parts) && first.content.parts.length > 0; } function normalizeReddit(spec, kind) { const inner = extractRedditData(spec.body); const data = kind === 'comment' ? ensureRedditThings(inner) : inner; spec.body = { json: { errors: existingRedditErrors(spec.body), data } }; } function extractRedditData(body) { if (isPlainObject(body) && isPlainObject(body.json) && isPlainObject(body.json.data)) { return body.json.data; } if (isPlainObject(body) && isPlainObject(body.data)) return body.data; if (isPlainObject(body)) return body; return {}; } function existingRedditErrors(body) { if (isPlainObject(body) && isPlainObject(body.json) && Array.isArray(body.json.errors)) { return body.json.errors; } return []; } function ensureRedditThings(data) { if (Array.isArray(data.things) && isPlainObject(data.things[0]) && data.things[0].data) { return data; } return { things: [{ kind: 't1', data }] }; } function normalizeHubspotUpsert(spec) { const body = isPlainObject(spec.body) ? spec.body : {}; spec.body = { ...body, vid: resolveHubspotVid(body), isNew: typeof body.isNew === 'boolean' ? body.isNew : true, }; } function resolveHubspotVid(body) { for (const candidate of [body.vid, body['canonical-vid'], body.id]) { if (typeof candidate === 'number' && Number.isFinite(candidate)) return candidate; if (typeof candidate === 'string' && /^\d+$/.test(candidate)) return Number(candidate); } return DEFAULT_HUBSPOT_VID; } function normalizeGoogleDocsBatchUpdate(spec) { const body = isPlainObject(spec.body) ? spec.body : {}; const replies = Array.isArray(body.replies) && body.replies.length > 0 ? body.replies : [{}]; spec.body = { ...body, replies }; } function applyProviderShapeNormalizers(info, spec) { if (spec.type !== 'json') return; const path = info.pathname.toLowerCase(); const host = (info.hostname ?? '').toLowerCase(); if (isOpenAiImagesGeneration(path)) return normalizeOpenAiImages(spec); if (isGeminiGenerate(host, path)) return normalizeGemini(spec); const redditKind = isRedditWrite(host, path, info.method); if (redditKind) return normalizeReddit(spec, redditKind); if (isHubspotUpsert(host, path)) return normalizeHubspotUpsert(spec); if (isGoogleDocsBatchUpdate(host, path)) return normalizeGoogleDocsBatchUpdate(spec); } function findProviderShapeViolation(info, body) { const path = info.pathname.toLowerCase(); const host = (info.hostname ?? '').toLowerCase(); if (isOpenAiImagesGeneration(path)) return openAiImagesViolation(body); if (isGeminiGenerate(host, path)) return geminiViolation(body); const redditKind = isRedditWrite(host, path, info.method); if (redditKind) return redditViolation(body, redditKind); if (isHubspotUpsert(host, path)) return hubspotViolation(body); if (isGoogleDocsBatchUpdate(host, path)) return googleDocsViolation(body); return undefined; } function openAiImagesViolation(body) { const data = isPlainObject(body) ? body.data : undefined; if (!Array.isArray(data) || data.length === 0) { return 'Invalid: OpenAI /v1/images/generations must return a JSON object with a `data` ARRAY of image objects, e.g. `{ "created": <unix>, "data": [{ "b64_json": "..." }] }`. Resubmit with that envelope.'; } const brokenEntry = data.some((entry) => !isPlainObject(entry) || (typeof entry.b64_json !== 'string' && typeof entry.url !== 'string')); if (brokenEntry) { return 'Invalid: every OpenAI image `data[]` entry must carry a `b64_json` (base64 string) — the node decodes it. Resubmit with `data: [{ "b64_json": "..." }]`.'; } return undefined; } function geminiViolation(body) { if (hasGeminiCandidates(body)) return undefined; return 'Invalid: Google Gemini responses must use the full envelope `{ "candidates": [{ "content": { "parts": [{ "text": "..." }] } }] }` — the node reads candidates[].content.parts[].text. Resubmit wrapped in that shape.'; } function redditViolation(body, kind) { const data = isPlainObject(body) && isPlainObject(body.json) ? body.json.data : undefined; if (!isPlainObject(data)) { return 'Invalid: Reddit write responses must use the `{ "json": { "errors": [], "data": { ... } } }` envelope — the node reads response.json.data. Resubmit wrapped in that shape.'; } if (kind === 'comment') { const things = data.things; if (!Array.isArray(things) || !isPlainObject(things[0]) || !things[0].data) { return 'Invalid: Reddit api/comment responses put the comment under `json.data.things[0].data`. Resubmit with `{ "json": { "data": { "things": [{ "kind": "t1", "data": { ... } }] } } }`.'; } } return undefined; } function hubspotViolation(body) { const vid = isPlainObject(body) ? body.vid : undefined; if (typeof vid === 'number' || (typeof vid === 'string' && /^\d+$/.test(vid))) return undefined; return 'Invalid: HubSpot contacts/v1 createOrUpdate returns `{ "vid": <number>, "isNew": <bool> }` — the node reads response.vid. Resubmit with a numeric `vid`.'; } function googleDocsViolation(body) { const replies = isPlainObject(body) ? body.replies : undefined; if (Array.isArray(replies) && replies.length > 0) return undefined; return 'Invalid: Google Docs batchUpdate returns `{ "documentId": "...", "replies": [ ... ] }` with one reply per request — the node reads response.replies[0]. Resubmit with a non-empty `replies` array.'; } //# sourceMappingURL=provider-shapes.js.map