@bubblelab/bubble-core 0.1.10 → 0.1.11
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/bubble-bundle.d.ts +759 -350
- package/dist/bubble-factory.d.ts.map +1 -1
- package/dist/bubble-factory.js +125 -35
- package/dist/bubble-factory.js.map +1 -1
- package/dist/bubble-flow/bubble-flow-class.d.ts +5 -0
- package/dist/bubble-flow/bubble-flow-class.d.ts.map +1 -1
- package/dist/bubble-flow/bubble-flow-class.js +20 -0
- package/dist/bubble-flow/bubble-flow-class.js.map +1 -1
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.d.ts.map +1 -1
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.js +6 -3
- package/dist/bubble-flow/sample/simplified-data-analysis.flow.js.map +1 -1
- package/dist/bubbles/service-bubble/agi-inc.d.ts +1121 -0
- package/dist/bubbles/service-bubble/agi-inc.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/agi-inc.js +730 -0
- package/dist/bubbles/service-bubble/agi-inc.js.map +1 -0
- package/dist/bubbles/service-bubble/ai-agent.d.ts +277 -65
- package/dist/bubbles/service-bubble/ai-agent.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/ai-agent.js +533 -399
- package/dist/bubbles/service-bubble/ai-agent.js.map +1 -1
- package/dist/bubbles/service-bubble/airtable.d.ts +1753 -0
- package/dist/bubbles/service-bubble/airtable.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/airtable.js +1173 -0
- package/dist/bubbles/service-bubble/airtable.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/google-maps-scraper.d.ts +240 -0
- package/dist/bubbles/service-bubble/apify/actors/google-maps-scraper.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/google-maps-scraper.js +119 -0
- package/dist/bubbles/service-bubble/apify/actors/google-maps-scraper.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/instagram-hashtag-scraper.d.ts +4 -4
- package/dist/bubbles/service-bubble/apify/actors/instagram-scraper.d.ts +14 -14
- package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.d.ts +137 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.js +81 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/linkedin-posts-search.d.ts +6 -6
- package/dist/bubbles/service-bubble/apify/actors/linkedin-profile-posts.d.ts +32 -32
- package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.d.ts +488 -0
- package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.js +463 -0
- package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.d.ts +262 -0
- package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.js +291 -0
- package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.js.map +1 -0
- package/dist/bubbles/service-bubble/apify/actors/youtube-scraper.d.ts +12 -12
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.d.ts +1301 -170
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.js +32 -0
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.js.map +1 -1
- package/dist/bubbles/service-bubble/apify/apify.d.ts +162 -17
- package/dist/bubbles/service-bubble/apify/apify.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/apify/apify.js +205 -32
- package/dist/bubbles/service-bubble/apify/apify.js.map +1 -1
- package/dist/bubbles/service-bubble/eleven-labs.d.ts +421 -0
- package/dist/bubbles/service-bubble/eleven-labs.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/eleven-labs.js +479 -0
- package/dist/bubbles/service-bubble/eleven-labs.js.map +1 -0
- package/dist/bubbles/service-bubble/firecrawl.d.ts +37748 -0
- package/dist/bubbles/service-bubble/firecrawl.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/firecrawl.js +1489 -0
- package/dist/bubbles/service-bubble/firecrawl.js.map +1 -0
- package/dist/bubbles/service-bubble/followupboss.d.ts +6822 -0
- package/dist/bubbles/service-bubble/followupboss.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/followupboss.js +1394 -0
- package/dist/bubbles/service-bubble/followupboss.js.map +1 -0
- package/dist/bubbles/service-bubble/github.d.ts +2399 -0
- package/dist/bubbles/service-bubble/github.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/github.js +1052 -0
- package/dist/bubbles/service-bubble/github.js.map +1 -0
- package/dist/bubbles/service-bubble/gmail.d.ts +180 -180
- package/dist/bubbles/service-bubble/google-calendar.d.ts +60 -60
- package/dist/bubbles/service-bubble/google-drive.d.ts +37 -36
- package/dist/bubbles/service-bubble/google-drive.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/google-drive.js +35 -3
- package/dist/bubbles/service-bubble/google-drive.js.map +1 -1
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.d.ts +943 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.d.ts +31 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.js +184 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.integration.flow.js.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.js +401 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.js.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.schema.d.ts +1024 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.schema.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/{google-sheets.js → google-sheets/google-sheets.schema.js} +45 -409
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.schema.js.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.utils.d.ts +38 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.utils.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.utils.js +183 -0
- package/dist/bubbles/service-bubble/google-sheets/google-sheets.utils.js.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets/index.d.ts +4 -0
- package/dist/bubbles/service-bubble/google-sheets/index.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/google-sheets/index.js +4 -0
- package/dist/bubbles/service-bubble/google-sheets/index.js.map +1 -0
- package/dist/bubbles/service-bubble/hello-world.d.ts +4 -4
- package/dist/bubbles/service-bubble/http.d.ts +4 -4
- package/dist/bubbles/service-bubble/http.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/http.js +7 -1
- package/dist/bubbles/service-bubble/http.js.map +1 -1
- package/dist/bubbles/service-bubble/insforge-db.d.ts +140 -0
- package/dist/bubbles/service-bubble/insforge-db.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/insforge-db.js +260 -0
- package/dist/bubbles/service-bubble/insforge-db.js.map +1 -0
- package/dist/bubbles/service-bubble/notion/index.d.ts +3 -0
- package/dist/bubbles/service-bubble/notion/index.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/notion/index.js +3 -0
- package/dist/bubbles/service-bubble/notion/index.js.map +1 -0
- package/dist/bubbles/service-bubble/notion/notion.d.ts +35405 -0
- package/dist/bubbles/service-bubble/notion/notion.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/notion/notion.js +1492 -0
- package/dist/bubbles/service-bubble/notion/notion.js.map +1 -0
- package/dist/bubbles/service-bubble/notion/property-schemas.d.ts +1148 -0
- package/dist/bubbles/service-bubble/notion/property-schemas.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/notion/property-schemas.js +341 -0
- package/dist/bubbles/service-bubble/notion/property-schemas.js.map +1 -0
- package/dist/bubbles/service-bubble/postgresql.d.ts +12 -12
- package/dist/bubbles/service-bubble/resend.d.ts +34 -13
- package/dist/bubbles/service-bubble/resend.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/resend.js +133 -2
- package/dist/bubbles/service-bubble/resend.js.map +1 -1
- package/dist/bubbles/service-bubble/slack.d.ts +241 -241
- package/dist/bubbles/service-bubble/slack.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/slack.js +2 -2
- package/dist/bubbles/service-bubble/slack.js.map +1 -1
- package/dist/bubbles/service-bubble/storage.d.ts +25 -21
- package/dist/bubbles/service-bubble/storage.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/storage.js +43 -2
- package/dist/bubbles/service-bubble/storage.js.map +1 -1
- package/dist/bubbles/service-bubble/telegram.d.ts +7742 -0
- package/dist/bubbles/service-bubble/telegram.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/telegram.js +1132 -0
- package/dist/bubbles/service-bubble/telegram.js.map +1 -0
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts +76 -20
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.js +12 -0
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/chart-js-tool.d.ts +14 -14
- package/dist/bubbles/tool-bubble/code-edit-tool.d.ts +188 -0
- package/dist/bubbles/tool-bubble/code-edit-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/code-edit-tool.js +321 -0
- package/dist/bubbles/tool-bubble/code-edit-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts +8 -4
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.js +115 -10
- package/dist/bubbles/tool-bubble/get-bubble-details-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/google-maps-tool.d.ts +455 -0
- package/dist/bubbles/tool-bubble/google-maps-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/google-maps-tool.js +205 -0
- package/dist/bubbles/tool-bubble/google-maps-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/instagram-tool.d.ts +36 -36
- package/dist/bubbles/tool-bubble/instagram-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/instagram-tool.js +2 -2
- package/dist/bubbles/tool-bubble/instagram-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/linkedin-tool.d.ts +824 -447
- package/dist/bubbles/tool-bubble/linkedin-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/linkedin-tool.js +232 -12
- package/dist/bubbles/tool-bubble/linkedin-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/list-bubbles-tool.d.ts +4 -4
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts +66 -66
- package/dist/bubbles/tool-bubble/research-agent-tool.d.ts +17 -16
- package/dist/bubbles/tool-bubble/research-agent-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/research-agent-tool.js +26 -16
- package/dist/bubbles/tool-bubble/research-agent-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/sql-query-tool.d.ts +8 -8
- package/dist/bubbles/tool-bubble/tiktok-tool.d.ts +485 -0
- package/dist/bubbles/tool-bubble/tiktok-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/tiktok-tool.js +226 -0
- package/dist/bubbles/tool-bubble/tiktok-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/tool-template.d.ts +8 -8
- package/dist/bubbles/tool-bubble/twitter-tool.d.ts +947 -0
- package/dist/bubbles/tool-bubble/twitter-tool.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/twitter-tool.js +494 -0
- package/dist/bubbles/tool-bubble/twitter-tool.js.map +1 -0
- package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts +22 -16
- package/dist/bubbles/tool-bubble/web-crawl-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/web-crawl-tool.js +58 -59
- package/dist/bubbles/tool-bubble/web-crawl-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/web-extract-tool.d.ts +8 -8
- package/dist/bubbles/tool-bubble/web-extract-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/web-extract-tool.js +17 -17
- package/dist/bubbles/tool-bubble/web-extract-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts +15 -107
- package/dist/bubbles/tool-bubble/web-scrape-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/web-scrape-tool.js +51 -72
- package/dist/bubbles/tool-bubble/web-scrape-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/web-search-tool.d.ts +20 -9
- package/dist/bubbles/tool-bubble/web-search-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/web-search-tool.js +45 -35
- package/dist/bubbles/tool-bubble/web-search-tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/youtube-tool.d.ts +25 -25
- package/dist/bubbles/tool-bubble/youtube-tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/youtube-tool.js +5 -5
- package/dist/bubbles/tool-bubble/youtube-tool.js.map +1 -1
- package/dist/bubbles/workflow-bubble/database-analyzer.workflow.d.ts +4 -4
- package/dist/bubbles/workflow-bubble/generate-document.workflow.d.ts +30 -30
- package/dist/bubbles/workflow-bubble/generate-document.workflow.js +1 -1
- package/dist/bubbles/workflow-bubble/parse-document.workflow.d.ts +22 -22
- package/dist/bubbles/workflow-bubble/parse-document.workflow.js +1 -1
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts +54 -54
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.js +4 -4
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.d.ts +36 -36
- package/dist/bubbles/workflow-bubble/pdf-ocr.workflow.js +1 -1
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts +14 -14
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js +6 -6
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.js.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts +26 -26
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.js +24 -4
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.js.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts +19 -19
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.d.ts.map +1 -1
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js +8 -8
- package/dist/bubbles/workflow-bubble/slack-notifier.workflow.js.map +1 -1
- package/dist/bubbles.json +220 -72
- package/dist/index.d.ts +23 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -3
- package/dist/index.js.map +1 -1
- package/dist/logging/BubbleLogger.d.ts +45 -16
- package/dist/logging/BubbleLogger.d.ts.map +1 -1
- package/dist/logging/BubbleLogger.js +178 -77
- package/dist/logging/BubbleLogger.js.map +1 -1
- package/dist/logging/StreamingBubbleLogger.d.ts +13 -1
- package/dist/logging/StreamingBubbleLogger.d.ts.map +1 -1
- package/dist/logging/StreamingBubbleLogger.js +51 -8
- package/dist/logging/StreamingBubbleLogger.js.map +1 -1
- package/dist/logging/WebhookStreamLogger.d.ts +66 -0
- package/dist/logging/WebhookStreamLogger.d.ts.map +1 -0
- package/dist/logging/WebhookStreamLogger.js +291 -0
- package/dist/logging/WebhookStreamLogger.js.map +1 -0
- package/dist/types/available-tools.d.ts +1 -1
- package/dist/types/available-tools.d.ts.map +1 -1
- package/dist/types/available-tools.js +1 -0
- package/dist/types/available-tools.js.map +1 -1
- package/dist/types/base-bubble-class.d.ts +6 -4
- package/dist/types/base-bubble-class.d.ts.map +1 -1
- package/dist/types/base-bubble-class.js +35 -20
- package/dist/types/base-bubble-class.js.map +1 -1
- package/dist/types/bubble.d.ts +2 -0
- package/dist/types/bubble.d.ts.map +1 -1
- package/dist/types/service-bubble-class.d.ts +1 -1
- package/dist/types/service-bubble-class.d.ts.map +1 -1
- package/dist/types/service-bubble-class.js +2 -2
- package/dist/types/service-bubble-class.js.map +1 -1
- package/dist/types/tool-bubble-class.d.ts +1 -1
- package/dist/types/tool-bubble-class.d.ts.map +1 -1
- package/dist/types/tool-bubble-class.js +2 -2
- package/dist/types/tool-bubble-class.js.map +1 -1
- package/dist/types/workflow-bubble-class.d.ts +1 -1
- package/dist/types/workflow-bubble-class.d.ts.map +1 -1
- package/dist/types/workflow-bubble-class.js +2 -2
- package/dist/types/workflow-bubble-class.js.map +1 -1
- package/dist/utils/agent-formatter.d.ts +14 -2
- package/dist/utils/agent-formatter.d.ts.map +1 -1
- package/dist/utils/agent-formatter.js +174 -26
- package/dist/utils/agent-formatter.js.map +1 -1
- package/dist/utils/bubbleflow-validation.d.ts +7 -0
- package/dist/utils/bubbleflow-validation.d.ts.map +1 -1
- package/dist/utils/bubbleflow-validation.js +171 -6
- package/dist/utils/bubbleflow-validation.js.map +1 -1
- package/dist/utils/json-parsing.d.ts.map +1 -1
- package/dist/utils/json-parsing.js +146 -0
- package/dist/utils/json-parsing.js.map +1 -1
- package/dist/utils/safe-gemini-chat.d.ts +31 -0
- package/dist/utils/safe-gemini-chat.d.ts.map +1 -0
- package/dist/utils/safe-gemini-chat.js +93 -0
- package/dist/utils/safe-gemini-chat.js.map +1 -0
- package/dist/utils/schema-comparison.d.ts +92 -0
- package/dist/utils/schema-comparison.d.ts.map +1 -0
- package/dist/utils/schema-comparison.js +716 -0
- package/dist/utils/schema-comparison.js.map +1 -0
- package/dist/utils/zod-schema.d.ts +24 -0
- package/dist/utils/zod-schema.d.ts.map +1 -0
- package/dist/utils/zod-schema.js +56 -0
- package/dist/utils/zod-schema.js.map +1 -0
- package/package.json +5 -4
- package/dist/bubbles/service-bubble/google-sheets.d.ts +0 -1811
- package/dist/bubbles/service-bubble/google-sheets.d.ts.map +0 -1
- package/dist/bubbles/service-bubble/google-sheets.js.map +0 -1
- package/dist/bubbles/service-bubble/x-twitter.d.ts +0 -814
- package/dist/bubbles/service-bubble/x-twitter.d.ts.map +0 -1
- package/dist/bubbles/service-bubble/x-twitter.js +0 -445
- package/dist/bubbles/service-bubble/x-twitter.js.map +0 -1
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts +0 -125
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.d.ts.map +0 -1
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js +0 -808
- package/dist/bubbles/workflow-bubble/bubbleflow-generator.workflow.js.map +0 -1
- package/dist/test-gm.d.ts +0 -10
- package/dist/test-gm.d.ts.map +0 -1
- package/dist/test-gm.js +0 -95
- package/dist/test-gm.js.map +0 -1
- package/dist/utils/param-helper.d.ts +0 -2
- package/dist/utils/param-helper.d.ts.map +0 -1
- package/dist/utils/param-helper.js +0 -5
- package/dist/utils/param-helper.js.map +0 -1
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Get the type name of a value for display purposes
|
|
4
|
+
*/
|
|
5
|
+
function getTypeName(value) {
|
|
6
|
+
if (value === null)
|
|
7
|
+
return 'null';
|
|
8
|
+
if (value === undefined)
|
|
9
|
+
return 'undefined';
|
|
10
|
+
if (Array.isArray(value))
|
|
11
|
+
return 'array';
|
|
12
|
+
return typeof value;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Extract field names and their optionality from a Zod object schema
|
|
16
|
+
*/
|
|
17
|
+
function extractSchemaFields(schema) {
|
|
18
|
+
const fields = new Map();
|
|
19
|
+
// Unwrap the schema to get to the core type
|
|
20
|
+
let currentSchema = schema;
|
|
21
|
+
// Handle ZodEffects (transforms, refinements)
|
|
22
|
+
while (currentSchema instanceof z.ZodEffects) {
|
|
23
|
+
currentSchema = currentSchema._def.schema;
|
|
24
|
+
}
|
|
25
|
+
// Handle ZodOptional
|
|
26
|
+
if (currentSchema instanceof z.ZodOptional) {
|
|
27
|
+
currentSchema = currentSchema._def.innerType;
|
|
28
|
+
}
|
|
29
|
+
// Handle ZodNullable
|
|
30
|
+
if (currentSchema instanceof z.ZodNullable) {
|
|
31
|
+
currentSchema = currentSchema._def.innerType;
|
|
32
|
+
}
|
|
33
|
+
// Handle ZodDefault
|
|
34
|
+
if (currentSchema instanceof z.ZodDefault) {
|
|
35
|
+
currentSchema = currentSchema._def.innerType;
|
|
36
|
+
}
|
|
37
|
+
// Now check if it's an object
|
|
38
|
+
if (currentSchema instanceof z.ZodObject) {
|
|
39
|
+
const shape = currentSchema._def.shape();
|
|
40
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
41
|
+
const zodSchema = fieldSchema;
|
|
42
|
+
const isOptional = zodSchema.isOptional() || zodSchema instanceof z.ZodDefault;
|
|
43
|
+
fields.set(key, { schema: zodSchema, isOptional });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return fields;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get expected type description from a Zod schema
|
|
50
|
+
*/
|
|
51
|
+
function getExpectedType(schema) {
|
|
52
|
+
// Unwrap optional/nullable/default
|
|
53
|
+
let currentSchema = schema;
|
|
54
|
+
const wrappers = [];
|
|
55
|
+
if (currentSchema instanceof z.ZodOptional) {
|
|
56
|
+
wrappers.push('optional');
|
|
57
|
+
currentSchema = currentSchema._def.innerType;
|
|
58
|
+
}
|
|
59
|
+
if (currentSchema instanceof z.ZodNullable) {
|
|
60
|
+
wrappers.push('nullable');
|
|
61
|
+
currentSchema = currentSchema._def.innerType;
|
|
62
|
+
}
|
|
63
|
+
if (currentSchema instanceof z.ZodDefault) {
|
|
64
|
+
wrappers.push('default');
|
|
65
|
+
currentSchema = currentSchema._def.innerType;
|
|
66
|
+
}
|
|
67
|
+
let baseType;
|
|
68
|
+
if (currentSchema instanceof z.ZodString) {
|
|
69
|
+
baseType = 'string';
|
|
70
|
+
}
|
|
71
|
+
else if (currentSchema instanceof z.ZodNumber) {
|
|
72
|
+
baseType = 'number';
|
|
73
|
+
}
|
|
74
|
+
else if (currentSchema instanceof z.ZodBoolean) {
|
|
75
|
+
baseType = 'boolean';
|
|
76
|
+
}
|
|
77
|
+
else if (currentSchema instanceof z.ZodArray) {
|
|
78
|
+
baseType = 'array';
|
|
79
|
+
}
|
|
80
|
+
else if (currentSchema instanceof z.ZodObject) {
|
|
81
|
+
baseType = 'object';
|
|
82
|
+
}
|
|
83
|
+
else if (currentSchema instanceof z.ZodEnum) {
|
|
84
|
+
baseType = `enum(${currentSchema._def.values.join('|')})`;
|
|
85
|
+
}
|
|
86
|
+
else if (currentSchema instanceof z.ZodUnion) {
|
|
87
|
+
baseType = 'union';
|
|
88
|
+
}
|
|
89
|
+
else if (currentSchema instanceof z.ZodLiteral) {
|
|
90
|
+
baseType = `literal(${JSON.stringify(currentSchema._def.value)})`;
|
|
91
|
+
}
|
|
92
|
+
else if (currentSchema instanceof z.ZodAny) {
|
|
93
|
+
baseType = 'any';
|
|
94
|
+
}
|
|
95
|
+
else if (currentSchema instanceof z.ZodUnknown) {
|
|
96
|
+
baseType = 'unknown';
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
baseType = 'unknown';
|
|
100
|
+
}
|
|
101
|
+
if (wrappers.length > 0) {
|
|
102
|
+
return `${wrappers.join(' ')} ${baseType}`;
|
|
103
|
+
}
|
|
104
|
+
return baseType;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Compare data against a Zod schema and find all differences
|
|
108
|
+
*
|
|
109
|
+
* @param schema - The Zod schema to compare against
|
|
110
|
+
* @param data - The actual data to compare
|
|
111
|
+
* @param basePath - Base path for nested field names (used internally)
|
|
112
|
+
* @returns Detailed comparison results showing all differences
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const schema = z.object({
|
|
117
|
+
* name: z.string(),
|
|
118
|
+
* age: z.number().optional(),
|
|
119
|
+
* });
|
|
120
|
+
*
|
|
121
|
+
* const data = {
|
|
122
|
+
* name: 'John',
|
|
123
|
+
* extraField: 'unexpected',
|
|
124
|
+
* };
|
|
125
|
+
*
|
|
126
|
+
* const diff = compareWithSchema(schema, data);
|
|
127
|
+
* // diff.extraFields = [{ path: 'extraField', value: 'unexpected', type: 'string' }]
|
|
128
|
+
* // diff.missingOptionalFields = [{ path: 'age' }]
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export function compareWithSchema(schema, data, basePath = '') {
|
|
132
|
+
const result = {
|
|
133
|
+
extraFields: [],
|
|
134
|
+
missingRequiredFields: [],
|
|
135
|
+
missingOptionalFields: [],
|
|
136
|
+
typeMismatches: [],
|
|
137
|
+
nestedDifferences: {},
|
|
138
|
+
summary: {
|
|
139
|
+
totalExtraFields: 0,
|
|
140
|
+
totalMissingRequired: 0,
|
|
141
|
+
totalMissingOptional: 0,
|
|
142
|
+
totalTypeMismatches: 0,
|
|
143
|
+
isCompatible: true,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
// If data is not an object, we can't compare fields
|
|
147
|
+
if (typeof data !== 'object' || data === null) {
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
const dataObj = data;
|
|
151
|
+
const schemaFields = extractSchemaFields(schema);
|
|
152
|
+
const dataKeys = new Set(Object.keys(dataObj));
|
|
153
|
+
// Find extra fields (in data but not in schema)
|
|
154
|
+
for (const key of dataKeys) {
|
|
155
|
+
if (!schemaFields.has(key)) {
|
|
156
|
+
const fullPath = basePath ? `${basePath}.${key}` : key;
|
|
157
|
+
result.extraFields.push({
|
|
158
|
+
path: fullPath,
|
|
159
|
+
value: dataObj[key],
|
|
160
|
+
type: getTypeName(dataObj[key]),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Find missing fields and check types
|
|
165
|
+
for (const [key, fieldInfo] of schemaFields) {
|
|
166
|
+
const fullPath = basePath ? `${basePath}.${key}` : key;
|
|
167
|
+
const hasField = dataKeys.has(key);
|
|
168
|
+
const value = dataObj[key];
|
|
169
|
+
if (!hasField || value === undefined) {
|
|
170
|
+
if (fieldInfo.isOptional) {
|
|
171
|
+
result.missingOptionalFields.push({ path: fullPath });
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
result.missingRequiredFields.push({ path: fullPath });
|
|
175
|
+
}
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
// Check for type mismatches using safeParse on the field
|
|
179
|
+
const fieldResult = fieldInfo.schema.safeParse(value);
|
|
180
|
+
if (!fieldResult.success) {
|
|
181
|
+
// Unwrap the schema to get base type for comparison
|
|
182
|
+
let baseSchema = fieldInfo.schema;
|
|
183
|
+
if (baseSchema instanceof z.ZodOptional) {
|
|
184
|
+
baseSchema = baseSchema._def.innerType;
|
|
185
|
+
}
|
|
186
|
+
if (baseSchema instanceof z.ZodNullable) {
|
|
187
|
+
baseSchema = baseSchema._def.innerType;
|
|
188
|
+
}
|
|
189
|
+
if (baseSchema instanceof z.ZodDefault) {
|
|
190
|
+
baseSchema = baseSchema._def.innerType;
|
|
191
|
+
}
|
|
192
|
+
const expectedBaseType = getExpectedType(baseSchema);
|
|
193
|
+
const actualType = getTypeName(value);
|
|
194
|
+
// Only report as type mismatch if the base types are actually different
|
|
195
|
+
// For objects/arrays, structural issues will be caught in nested differences
|
|
196
|
+
const isStructuralMismatch = (baseSchema instanceof z.ZodObject && actualType === 'object') ||
|
|
197
|
+
(baseSchema instanceof z.ZodArray && actualType === 'array');
|
|
198
|
+
if (!isStructuralMismatch) {
|
|
199
|
+
result.typeMismatches.push({
|
|
200
|
+
path: fullPath,
|
|
201
|
+
expectedType: expectedBaseType,
|
|
202
|
+
actualType: actualType,
|
|
203
|
+
actualValue: value,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Recursively check nested objects
|
|
208
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
209
|
+
// Unwrap the schema to check if it's an object
|
|
210
|
+
let innerSchema = fieldInfo.schema;
|
|
211
|
+
if (innerSchema instanceof z.ZodOptional) {
|
|
212
|
+
innerSchema = innerSchema._def.innerType;
|
|
213
|
+
}
|
|
214
|
+
if (innerSchema instanceof z.ZodNullable) {
|
|
215
|
+
innerSchema = innerSchema._def.innerType;
|
|
216
|
+
}
|
|
217
|
+
if (innerSchema instanceof z.ZodDefault) {
|
|
218
|
+
innerSchema = innerSchema._def.innerType;
|
|
219
|
+
}
|
|
220
|
+
if (innerSchema instanceof z.ZodObject) {
|
|
221
|
+
const nestedDiff = compareWithSchema(innerSchema, value, fullPath);
|
|
222
|
+
if (nestedDiff.extraFields.length > 0 ||
|
|
223
|
+
nestedDiff.missingRequiredFields.length > 0 ||
|
|
224
|
+
nestedDiff.typeMismatches.length > 0) {
|
|
225
|
+
result.nestedDifferences[key] = nestedDiff;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Check array items if it's an array schema
|
|
230
|
+
if (Array.isArray(value)) {
|
|
231
|
+
let innerSchema = fieldInfo.schema;
|
|
232
|
+
if (innerSchema instanceof z.ZodOptional) {
|
|
233
|
+
innerSchema = innerSchema._def.innerType;
|
|
234
|
+
}
|
|
235
|
+
if (innerSchema instanceof z.ZodNullable) {
|
|
236
|
+
innerSchema = innerSchema._def.innerType;
|
|
237
|
+
}
|
|
238
|
+
if (innerSchema instanceof z.ZodArray) {
|
|
239
|
+
const itemSchema = innerSchema._def.type;
|
|
240
|
+
// Check first few items for differences
|
|
241
|
+
for (let i = 0; i < Math.min(value.length, 3); i++) {
|
|
242
|
+
const item = value[i];
|
|
243
|
+
if (typeof item === 'object' && item !== null) {
|
|
244
|
+
const itemPath = `${fullPath}[${i}]`;
|
|
245
|
+
const itemDiff = compareWithSchema(itemSchema, item, itemPath);
|
|
246
|
+
if (itemDiff.extraFields.length > 0 ||
|
|
247
|
+
itemDiff.missingRequiredFields.length > 0 ||
|
|
248
|
+
itemDiff.typeMismatches.length > 0) {
|
|
249
|
+
result.nestedDifferences[`${key}[${i}]`] = itemDiff;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Calculate summary
|
|
257
|
+
result.summary.totalExtraFields = countAllExtraFields(result);
|
|
258
|
+
result.summary.totalMissingRequired = countAllMissingRequired(result);
|
|
259
|
+
result.summary.totalMissingOptional = countAllMissingOptional(result);
|
|
260
|
+
result.summary.totalTypeMismatches = countAllTypeMismatches(result);
|
|
261
|
+
result.summary.isCompatible =
|
|
262
|
+
result.summary.totalMissingRequired === 0 &&
|
|
263
|
+
result.summary.totalTypeMismatches === 0;
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
function countAllExtraFields(diff) {
|
|
267
|
+
let count = diff.extraFields.length;
|
|
268
|
+
for (const nested of Object.values(diff.nestedDifferences)) {
|
|
269
|
+
count += countAllExtraFields(nested);
|
|
270
|
+
}
|
|
271
|
+
return count;
|
|
272
|
+
}
|
|
273
|
+
function countAllMissingRequired(diff) {
|
|
274
|
+
let count = diff.missingRequiredFields.length;
|
|
275
|
+
for (const nested of Object.values(diff.nestedDifferences)) {
|
|
276
|
+
count += countAllMissingRequired(nested);
|
|
277
|
+
}
|
|
278
|
+
return count;
|
|
279
|
+
}
|
|
280
|
+
function countAllMissingOptional(diff) {
|
|
281
|
+
let count = diff.missingOptionalFields.length;
|
|
282
|
+
for (const nested of Object.values(diff.nestedDifferences)) {
|
|
283
|
+
count += countAllMissingOptional(nested);
|
|
284
|
+
}
|
|
285
|
+
return count;
|
|
286
|
+
}
|
|
287
|
+
function countAllTypeMismatches(diff) {
|
|
288
|
+
let count = diff.typeMismatches.length;
|
|
289
|
+
for (const nested of Object.values(diff.nestedDifferences)) {
|
|
290
|
+
count += countAllTypeMismatches(nested);
|
|
291
|
+
}
|
|
292
|
+
return count;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Format schema differences as a human-readable string
|
|
296
|
+
*/
|
|
297
|
+
export function formatSchemaDifference(diff, indent = 0) {
|
|
298
|
+
const pad = ' '.repeat(indent);
|
|
299
|
+
const lines = [];
|
|
300
|
+
if (diff.extraFields.length > 0) {
|
|
301
|
+
lines.push(`${pad}Extra fields (not in schema):`);
|
|
302
|
+
for (const field of diff.extraFields) {
|
|
303
|
+
const valuePreview = typeof field.value === 'object'
|
|
304
|
+
? JSON.stringify(field.value).slice(0, 100)
|
|
305
|
+
: String(field.value).slice(0, 50);
|
|
306
|
+
lines.push(`${pad} - ${field.path}: ${field.type} = ${valuePreview}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (diff.missingRequiredFields.length > 0) {
|
|
310
|
+
lines.push(`${pad}Missing required fields:`);
|
|
311
|
+
for (const field of diff.missingRequiredFields) {
|
|
312
|
+
lines.push(`${pad} - ${field.path}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (diff.missingOptionalFields.length > 0) {
|
|
316
|
+
lines.push(`${pad}Missing optional fields:`);
|
|
317
|
+
for (const field of diff.missingOptionalFields) {
|
|
318
|
+
lines.push(`${pad} - ${field.path}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (diff.typeMismatches.length > 0) {
|
|
322
|
+
lines.push(`${pad}Type mismatches:`);
|
|
323
|
+
for (const mismatch of diff.typeMismatches) {
|
|
324
|
+
lines.push(`${pad} - ${mismatch.path}: expected ${mismatch.expectedType}, got ${mismatch.actualType}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
for (const [key, nestedDiff] of Object.entries(diff.nestedDifferences)) {
|
|
328
|
+
lines.push(`${pad}Nested differences in '${key}':`);
|
|
329
|
+
lines.push(formatSchemaDifference(nestedDiff, indent + 1));
|
|
330
|
+
}
|
|
331
|
+
if (lines.length === 0) {
|
|
332
|
+
lines.push(`${pad}No differences found - data matches schema perfectly`);
|
|
333
|
+
}
|
|
334
|
+
return lines.join('\n');
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Compare multiple data items against a schema and aggregate differences
|
|
338
|
+
* Useful for analyzing API responses with multiple items
|
|
339
|
+
*/
|
|
340
|
+
export function compareMultipleWithSchema(schema, items, maxItems = 10) {
|
|
341
|
+
const allExtraFields = [];
|
|
342
|
+
const allMissingRequired = [];
|
|
343
|
+
const allMissingOptional = [];
|
|
344
|
+
const allTypeMismatches = new Map();
|
|
345
|
+
const sampleDifferences = [];
|
|
346
|
+
const itemsToCheck = items.slice(0, maxItems);
|
|
347
|
+
for (let index = 0; index < itemsToCheck.length; index++) {
|
|
348
|
+
const item = itemsToCheck[index];
|
|
349
|
+
const diff = compareWithSchema(schema, item);
|
|
350
|
+
sampleDifferences.push(diff);
|
|
351
|
+
// Collect extra fields
|
|
352
|
+
collectExtraFieldsWithIndex(diff, allExtraFields, index);
|
|
353
|
+
// Collect missing required
|
|
354
|
+
for (const field of diff.missingRequiredFields) {
|
|
355
|
+
allMissingRequired.push({
|
|
356
|
+
fieldName: field.path,
|
|
357
|
+
index,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
// Collect missing optional
|
|
361
|
+
for (const field of diff.missingOptionalFields) {
|
|
362
|
+
allMissingOptional.push({
|
|
363
|
+
fieldName: field.path,
|
|
364
|
+
index,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
// Collect type mismatches
|
|
368
|
+
for (const mismatch of diff.typeMismatches) {
|
|
369
|
+
if (!allTypeMismatches.has(mismatch.path)) {
|
|
370
|
+
allTypeMismatches.set(mismatch.path, new Set());
|
|
371
|
+
}
|
|
372
|
+
allTypeMismatches
|
|
373
|
+
.get(mismatch.path)
|
|
374
|
+
.add(`expected ${mismatch.expectedType}, got ${mismatch.actualType}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// Determine PASS/FAIL status
|
|
378
|
+
// Only FAIL if there are missing REQUIRED fields or type mismatches
|
|
379
|
+
// Missing optional fields are informational only
|
|
380
|
+
const status = allMissingRequired.length === 0 && allTypeMismatches.size === 0
|
|
381
|
+
? 'PASS'
|
|
382
|
+
: 'FAIL';
|
|
383
|
+
const result = {
|
|
384
|
+
itemCount: items.length,
|
|
385
|
+
status,
|
|
386
|
+
allExtraFields,
|
|
387
|
+
allMissingRequired,
|
|
388
|
+
allMissingOptional,
|
|
389
|
+
allTypeMismatches,
|
|
390
|
+
sampleDifferences,
|
|
391
|
+
summary: '', // Will be populated below
|
|
392
|
+
};
|
|
393
|
+
// Generate the summary
|
|
394
|
+
result.summary = generateSummary({
|
|
395
|
+
...result,
|
|
396
|
+
schema,
|
|
397
|
+
sampleDifferences,
|
|
398
|
+
items,
|
|
399
|
+
});
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Generate expected schema structure as a string
|
|
404
|
+
*/
|
|
405
|
+
function generateExpectedSchemaStructure(schema, indent = 0) {
|
|
406
|
+
const lines = [];
|
|
407
|
+
const pad = ' '.repeat(indent);
|
|
408
|
+
const schemaFields = extractSchemaFields(schema);
|
|
409
|
+
if (schemaFields.size === 0) {
|
|
410
|
+
return '';
|
|
411
|
+
}
|
|
412
|
+
for (const [fieldName, fieldInfo] of schemaFields) {
|
|
413
|
+
const expectedType = getExpectedType(fieldInfo.schema);
|
|
414
|
+
const marker = fieldInfo.isOptional ? '(optional)' : '(required)';
|
|
415
|
+
// Check if it's a nested object
|
|
416
|
+
let innerSchema = fieldInfo.schema;
|
|
417
|
+
if (innerSchema instanceof z.ZodOptional) {
|
|
418
|
+
innerSchema = innerSchema._def.innerType;
|
|
419
|
+
}
|
|
420
|
+
if (innerSchema instanceof z.ZodNullable) {
|
|
421
|
+
innerSchema = innerSchema._def.innerType;
|
|
422
|
+
}
|
|
423
|
+
if (innerSchema instanceof z.ZodDefault) {
|
|
424
|
+
innerSchema = innerSchema._def.innerType;
|
|
425
|
+
}
|
|
426
|
+
if (innerSchema instanceof z.ZodObject) {
|
|
427
|
+
lines.push(`${pad}${fieldName}: object ${marker} {`);
|
|
428
|
+
lines.push(generateExpectedSchemaStructure(innerSchema, indent + 1));
|
|
429
|
+
lines.push(`${pad}}`);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
lines.push(`${pad}${fieldName}: ${expectedType} ${marker}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return lines.join('\n');
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Infer schema structure from actual data samples
|
|
439
|
+
*/
|
|
440
|
+
function inferSchemaFromData(items, maxSamples = 10) {
|
|
441
|
+
const fieldTypes = new Map();
|
|
442
|
+
const samplesToCheck = items.slice(0, maxSamples);
|
|
443
|
+
for (const item of samplesToCheck) {
|
|
444
|
+
if (typeof item !== 'object' || item === null) {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const collectFieldsRecursive = (obj, prefix = '') => {
|
|
448
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
449
|
+
const fieldPath = prefix ? `${prefix}.${key}` : key;
|
|
450
|
+
if (!fieldTypes.has(fieldPath)) {
|
|
451
|
+
fieldTypes.set(fieldPath, new Set());
|
|
452
|
+
}
|
|
453
|
+
const typeName = getTypeName(value);
|
|
454
|
+
fieldTypes.get(fieldPath).add(typeName);
|
|
455
|
+
// Recursively process nested objects
|
|
456
|
+
if (typeof value === 'object' &&
|
|
457
|
+
value !== null &&
|
|
458
|
+
!Array.isArray(value)) {
|
|
459
|
+
collectFieldsRecursive(value, fieldPath);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
collectFieldsRecursive(item);
|
|
464
|
+
}
|
|
465
|
+
return fieldTypes;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Generate actual data structure from samples with proper nesting
|
|
469
|
+
*/
|
|
470
|
+
function generateActualSchemaStructure(items, maxSamples = 10) {
|
|
471
|
+
const fieldTypes = inferSchemaFromData(items, maxSamples);
|
|
472
|
+
if (fieldTypes.size === 0) {
|
|
473
|
+
return ' (no fields found)';
|
|
474
|
+
}
|
|
475
|
+
const lines = [];
|
|
476
|
+
const root = {
|
|
477
|
+
name: '',
|
|
478
|
+
types: new Set(),
|
|
479
|
+
children: new Map(),
|
|
480
|
+
isObject: true,
|
|
481
|
+
};
|
|
482
|
+
// Build the tree
|
|
483
|
+
for (const [fieldPath, types] of fieldTypes) {
|
|
484
|
+
const parts = fieldPath.split('.');
|
|
485
|
+
let currentNode = root;
|
|
486
|
+
for (let i = 0; i < parts.length; i++) {
|
|
487
|
+
const part = parts[i];
|
|
488
|
+
if (!currentNode.children.has(part)) {
|
|
489
|
+
currentNode.children.set(part, {
|
|
490
|
+
name: part,
|
|
491
|
+
types: new Set(),
|
|
492
|
+
children: new Map(),
|
|
493
|
+
isObject: false,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
const node = currentNode.children.get(part);
|
|
497
|
+
// If this is the last part, set the types
|
|
498
|
+
if (i === parts.length - 1) {
|
|
499
|
+
node.types = types;
|
|
500
|
+
// Check if any child fields exist for this path
|
|
501
|
+
const hasChildren = Array.from(fieldTypes.keys()).some((key) => key.startsWith(fieldPath + '.') && key !== fieldPath);
|
|
502
|
+
node.isObject = hasChildren || types.has('object');
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
node.isObject = true;
|
|
506
|
+
}
|
|
507
|
+
currentNode = node;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
// Render the tree
|
|
511
|
+
function renderNode(node, indent, parentPath = '') {
|
|
512
|
+
const pad = ' '.repeat(indent);
|
|
513
|
+
const currentPath = parentPath ? `${parentPath}.${node.name}` : node.name;
|
|
514
|
+
if (node.name === '') {
|
|
515
|
+
// Root node, just render children
|
|
516
|
+
for (const child of node.children.values()) {
|
|
517
|
+
renderNode(child, indent, '');
|
|
518
|
+
}
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
if (node.children.size > 0) {
|
|
522
|
+
// This is an object with nested fields
|
|
523
|
+
const typeStr = node.types.size > 0 ? Array.from(node.types).join(' | ') : 'object';
|
|
524
|
+
lines.push(`${pad}${node.name}: ${typeStr} {`);
|
|
525
|
+
// Render children
|
|
526
|
+
for (const child of Array.from(node.children.values()).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
527
|
+
renderNode(child, indent + 1, currentPath);
|
|
528
|
+
}
|
|
529
|
+
lines.push(`${pad}}`);
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
// Leaf node
|
|
533
|
+
const typeStr = node.types.size > 0 ? Array.from(node.types).join(' | ') : 'unknown';
|
|
534
|
+
lines.push(`${pad}${node.name}: ${typeStr}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
renderNode(root, 1);
|
|
538
|
+
return lines.join('\n');
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Generate a human-readable diff-style summary
|
|
542
|
+
*/
|
|
543
|
+
function generateSummary(result) {
|
|
544
|
+
const lines = [];
|
|
545
|
+
// First line shows PASS/FAIL status prominently
|
|
546
|
+
lines.push(`[${result.status}] Schema Comparison Summary (${result.itemCount} items)`);
|
|
547
|
+
lines.push('='.repeat(60));
|
|
548
|
+
lines.push('');
|
|
549
|
+
// Show expected schema structure
|
|
550
|
+
lines.push('Expected Schema:');
|
|
551
|
+
lines.push(generateExpectedSchemaStructure(result.schema));
|
|
552
|
+
lines.push('');
|
|
553
|
+
// Show actual schema from data
|
|
554
|
+
lines.push('Actual Schema:');
|
|
555
|
+
lines.push(generateActualSchemaStructure(result.items));
|
|
556
|
+
lines.push('');
|
|
557
|
+
lines.push('='.repeat(60));
|
|
558
|
+
lines.push('');
|
|
559
|
+
if (result.allMissingRequired.length > 0) {
|
|
560
|
+
lines.push('Missing REQUIRED fields:');
|
|
561
|
+
const grouped = new Map();
|
|
562
|
+
for (const field of result.allMissingRequired) {
|
|
563
|
+
if (!grouped.has(field.fieldName)) {
|
|
564
|
+
grouped.set(field.fieldName, []);
|
|
565
|
+
}
|
|
566
|
+
grouped.get(field.fieldName).push(field.index);
|
|
567
|
+
}
|
|
568
|
+
for (const [fieldName, indices] of grouped) {
|
|
569
|
+
const indexList = indices.length > 5
|
|
570
|
+
? `[${indices.slice(0, 5).join(', ')}... and ${indices.length - 5} more]`
|
|
571
|
+
: `[${indices.join(', ')}]`;
|
|
572
|
+
lines.push(` - ${fieldName} ${indexList}`);
|
|
573
|
+
}
|
|
574
|
+
lines.push('');
|
|
575
|
+
}
|
|
576
|
+
if (result.allMissingOptional.length > 0) {
|
|
577
|
+
lines.push('Missing optional fields:');
|
|
578
|
+
const grouped = new Map();
|
|
579
|
+
for (const field of result.allMissingOptional) {
|
|
580
|
+
if (!grouped.has(field.fieldName)) {
|
|
581
|
+
grouped.set(field.fieldName, []);
|
|
582
|
+
}
|
|
583
|
+
grouped.get(field.fieldName).push(field.index);
|
|
584
|
+
}
|
|
585
|
+
for (const [fieldName, indices] of grouped) {
|
|
586
|
+
const indexList = indices.length > 5
|
|
587
|
+
? `[${indices.slice(0, 5).join(', ')}... and ${indices.length - 5} more]`
|
|
588
|
+
: `[${indices.join(', ')}]`;
|
|
589
|
+
lines.push(` - ${fieldName} ${indexList}`);
|
|
590
|
+
}
|
|
591
|
+
lines.push('');
|
|
592
|
+
}
|
|
593
|
+
if (result.allExtraFields.length > 0) {
|
|
594
|
+
lines.push('Extra fields (not in schema): Permitted, if these fields are not relevant to the functionality of the bubble');
|
|
595
|
+
const grouped = new Map();
|
|
596
|
+
for (const field of result.allExtraFields) {
|
|
597
|
+
if (!grouped.has(field.fieldName)) {
|
|
598
|
+
grouped.set(field.fieldName, []);
|
|
599
|
+
}
|
|
600
|
+
grouped.get(field.fieldName).push(field.index);
|
|
601
|
+
}
|
|
602
|
+
for (const [fieldName, indices] of grouped) {
|
|
603
|
+
const indexList = indices.length > 5
|
|
604
|
+
? `[${indices.slice(0, 5).join(', ')}... and ${indices.length - 5} more]`
|
|
605
|
+
: `[${indices.join(', ')}]`;
|
|
606
|
+
lines.push(` + ${fieldName} ${indexList}`);
|
|
607
|
+
}
|
|
608
|
+
lines.push('');
|
|
609
|
+
}
|
|
610
|
+
if (result.allTypeMismatches.size > 0) {
|
|
611
|
+
lines.push('Type mismatches:');
|
|
612
|
+
for (const [path, mismatches] of result.allTypeMismatches) {
|
|
613
|
+
lines.push(` ! ${path}: ${[...mismatches].join(', ')}`);
|
|
614
|
+
}
|
|
615
|
+
lines.push('');
|
|
616
|
+
}
|
|
617
|
+
if (result.status === 'PASS') {
|
|
618
|
+
lines.push('✓ All items match the schema perfectly');
|
|
619
|
+
}
|
|
620
|
+
return lines.join('\n');
|
|
621
|
+
}
|
|
622
|
+
function collectExtraFieldsWithIndex(diff, collection, index) {
|
|
623
|
+
for (const field of diff.extraFields) {
|
|
624
|
+
collection.push({
|
|
625
|
+
fieldName: field.path,
|
|
626
|
+
index,
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
for (const nested of Object.values(diff.nestedDifferences)) {
|
|
630
|
+
collectExtraFieldsWithIndex(nested, collection, index);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Format aggregated differences from multiple items in a git-diff style
|
|
635
|
+
*/
|
|
636
|
+
export function formatAggregatedDifferences(result) {
|
|
637
|
+
const lines = [];
|
|
638
|
+
lines.push(`Schema Comparison Summary (${result.itemCount} items)`);
|
|
639
|
+
lines.push('='.repeat(50));
|
|
640
|
+
lines.push('');
|
|
641
|
+
if (result.allMissingRequired.length > 0) {
|
|
642
|
+
lines.push('Missing REQUIRED fields:');
|
|
643
|
+
for (const field of result.allMissingRequired) {
|
|
644
|
+
lines.push(` - ${field.fieldName} [missing at index ${field.index}]`);
|
|
645
|
+
}
|
|
646
|
+
lines.push('');
|
|
647
|
+
}
|
|
648
|
+
if (result.allMissingOptional.length > 0) {
|
|
649
|
+
lines.push('Missing optional fields:');
|
|
650
|
+
for (const field of result.allMissingOptional) {
|
|
651
|
+
lines.push(` - ${field.fieldName} [missing at index ${field.index}]`);
|
|
652
|
+
}
|
|
653
|
+
lines.push('');
|
|
654
|
+
}
|
|
655
|
+
if (result.allExtraFields.length > 0) {
|
|
656
|
+
lines.push('Extra fields (not in schema):');
|
|
657
|
+
for (const field of result.allExtraFields) {
|
|
658
|
+
lines.push(` + ${field.fieldName} [found at index ${field.index}]`);
|
|
659
|
+
}
|
|
660
|
+
lines.push('');
|
|
661
|
+
}
|
|
662
|
+
if (result.allTypeMismatches.size > 0) {
|
|
663
|
+
lines.push('Type mismatches:');
|
|
664
|
+
for (const [path, mismatches] of result.allTypeMismatches) {
|
|
665
|
+
lines.push(` ! ${path}: ${[...mismatches].join(', ')}`);
|
|
666
|
+
}
|
|
667
|
+
lines.push('');
|
|
668
|
+
}
|
|
669
|
+
if (result.allExtraFields.length === 0 &&
|
|
670
|
+
result.allMissingRequired.length === 0 &&
|
|
671
|
+
result.allTypeMismatches.size === 0) {
|
|
672
|
+
lines.push('✓ All items match the schema perfectly');
|
|
673
|
+
}
|
|
674
|
+
return lines.join('\n');
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Format aggregated differences with per-item breakdown
|
|
678
|
+
*/
|
|
679
|
+
export function formatAggregatedDifferencesDetailed(result, schema) {
|
|
680
|
+
const lines = [];
|
|
681
|
+
const schemaFields = extractSchemaFields(schema);
|
|
682
|
+
lines.push(`Schema Comparison Summary (${result.itemCount} items)`);
|
|
683
|
+
lines.push('='.repeat(50));
|
|
684
|
+
lines.push('');
|
|
685
|
+
// Show per-item breakdown
|
|
686
|
+
for (let i = 0; i < result.sampleDifferences.length; i++) {
|
|
687
|
+
const diff = result.sampleDifferences[i];
|
|
688
|
+
lines.push(`Item ${i}:`);
|
|
689
|
+
// Show all schema fields with their status
|
|
690
|
+
for (const [fieldName, fieldInfo] of schemaFields) {
|
|
691
|
+
const isMissing = diff.missingRequiredFields.some((f) => f.path === fieldName) ||
|
|
692
|
+
diff.missingOptionalFields.some((f) => f.path === fieldName);
|
|
693
|
+
const hasTypeMismatch = diff.typeMismatches.some((m) => m.path === fieldName);
|
|
694
|
+
const fieldType = fieldInfo.isOptional ? 'optional' : 'required';
|
|
695
|
+
if (isMissing) {
|
|
696
|
+
lines.push(` - ${fieldName} (${fieldType}) MISSING`);
|
|
697
|
+
}
|
|
698
|
+
else if (hasTypeMismatch) {
|
|
699
|
+
const mismatch = diff.typeMismatches.find((m) => m.path === fieldName);
|
|
700
|
+
lines.push(` ! ${fieldName} (${fieldType}) TYPE MISMATCH: ${mismatch?.expectedType} vs ${mismatch?.actualType}`);
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
lines.push(` + ${fieldName} (${fieldType}) ✓`);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
// Show extra fields
|
|
707
|
+
if (diff.extraFields.length > 0) {
|
|
708
|
+
for (const extra of diff.extraFields) {
|
|
709
|
+
lines.push(` + ${extra.path} (extra field)`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
lines.push('');
|
|
713
|
+
}
|
|
714
|
+
return lines.join('\n');
|
|
715
|
+
}
|
|
716
|
+
//# sourceMappingURL=schema-comparison.js.map
|