@caoscompanybr/merlin 3.5.0
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/.claude/CLAUDE.md +216 -0
- package/.claude/hooks/README-license-gate.md +45 -0
- package/.claude/hooks/auto-summarize.js +47 -0
- package/.claude/hooks/context-monitor.js +60 -0
- package/.claude/hooks/doc-sync.js +111 -0
- package/.claude/hooks/license-gate.cjs +59 -0
- package/.claude/hooks/session-reset.js +27 -0
- package/.claude/hooks/thoughts-indexer.js +80 -0
- package/.claude/rules/merlin-constitution.md +27 -0
- package/.merlin-core/commands/README.md +19 -0
- package/.merlin-core/commands/founder-mode.md +51 -0
- package/.merlin-core/commands/git/commit.md +35 -0
- package/.merlin-core/commands/git/describe-pr.md +43 -0
- package/.merlin-core/commands/git/safe-commit.md +182 -0
- package/.merlin-core/commands/implementation/implement-plan.md +129 -0
- package/.merlin-core/commands/implementation/oneshot.md +63 -0
- package/.merlin-core/commands/implementation/tdd.md +152 -0
- package/.merlin-core/commands/planning/create-plan.md +184 -0
- package/.merlin-core/commands/planning/iterate-plan.md +45 -0
- package/.merlin-core/commands/planning/validate-plan.md +48 -0
- package/.merlin-core/commands/research/analyze-issue.md +155 -0
- package/.merlin-core/commands/research/research-codebase.md +157 -0
- package/.merlin-core/commands/review/adversarial-review.md +112 -0
- package/.merlin-core/commands/review/check.md +91 -0
- package/.merlin-core/commands/review/debug.md +135 -0
- package/.merlin-core/commands/review/doubts.md +178 -0
- package/.merlin-core/commands/review/engineering-audit.md +87 -0
- package/.merlin-core/commands/review/local-review.md +48 -0
- package/.merlin-core/commands/review/verify-goals.md +83 -0
- package/.merlin-core/commands/session/capture-feedback.md +74 -0
- package/.merlin-core/commands/session/capture-learning.md +155 -0
- package/.merlin-core/commands/session/check-objectives.md +85 -0
- package/.merlin-core/commands/session/conclude.md +125 -0
- package/.merlin-core/commands/session/create-handoff.md +88 -0
- package/.merlin-core/commands/session/create-objective.md +111 -0
- package/.merlin-core/commands/session/create-process.md +105 -0
- package/.merlin-core/commands/session/create-reminder.md +86 -0
- package/.merlin-core/commands/session/fast-start.md +261 -0
- package/.merlin-core/commands/session/recall-learnings.md +79 -0
- package/.merlin-core/commands/session/recall-processes.md +74 -0
- package/.merlin-core/commands/session/resume-handoff.md +51 -0
- package/.merlin-core/commands/session/run-process.md +53 -0
- package/.merlin-core/commands/special/beauty.md +89 -0
- package/.merlin-core/commands/special/common-ground.md +114 -0
- package/.merlin-core/commands/special/elicit.md +98 -0
- package/.merlin-core/commands/special/party.md +66 -0
- package/.merlin-core/commands/special/scrape.md +78 -0
- package/.merlin-core/commands/special/skill-audit.md +128 -0
- package/.merlin-core/commands/special/start-here.md +132 -0
- package/.merlin-core/constitution.md +442 -0
- package/.merlin-core/core/README.md +19 -0
- package/.merlin-core/core/alkimia/README.md +20 -0
- package/.merlin-core/core/alkimia/context/context-tracker.js +209 -0
- package/.merlin-core/core/alkimia/domain/domain-loader.js +215 -0
- package/.merlin-core/core/alkimia/engine.js +284 -0
- package/.merlin-core/core/alkimia/layers/l0-constitution.js +47 -0
- package/.merlin-core/core/alkimia/layers/l1-global.js +58 -0
- package/.merlin-core/core/alkimia/layers/l2-agent.js +58 -0
- package/.merlin-core/core/alkimia/layers/l3-workflow.js +54 -0
- package/.merlin-core/core/alkimia/layers/l4-task.js +45 -0
- package/.merlin-core/core/alkimia/layers/l5-squad.js +161 -0
- package/.merlin-core/core/alkimia/layers/l6-skill.js +520 -0
- package/.merlin-core/core/alkimia/layers/l7-star-command.js +87 -0
- package/.merlin-core/core/alkimia/layers/layer-processor.js +78 -0
- package/.merlin-core/core/alkimia/mandate.js +46 -0
- package/.merlin-core/core/alkimia/memory/doc-sync.js +201 -0
- package/.merlin-core/core/alkimia/memory/document-sharder.js +272 -0
- package/.merlin-core/core/alkimia/memory/git-history-retriever.js +225 -0
- package/.merlin-core/core/alkimia/memory/memory-bridge.js +97 -0
- package/.merlin-core/core/alkimia/memory/session-analyzer.js +400 -0
- package/.merlin-core/core/alkimia/memory/thoughts-indexer.js +477 -0
- package/.merlin-core/core/alkimia/memory/thoughts-provider.js +603 -0
- package/.merlin-core/core/alkimia/output/formatter.js +464 -0
- package/.merlin-core/core/alkimia/security/content-sanitizer.js +140 -0
- package/.merlin-core/core/alkimia/skill-importer.js +440 -0
- package/.merlin-core/core/alkimia/squads/default/.synapse/manifest +17 -0
- package/.merlin-core/core/alkimia/utils/frontmatter.js +321 -0
- package/.merlin-core/core/alkimia/utils/tokens.js +24 -0
- package/.merlin-core/core/approval/README.md +16 -0
- package/.merlin-core/core/approval/approval-engine.js +380 -0
- package/.merlin-core/core/approval/channels/cli-channel.js +50 -0
- package/.merlin-core/core/config/README.md +17 -0
- package/.merlin-core/core/config/config-cache.js +182 -0
- package/.merlin-core/core/config/config-loader.js +279 -0
- package/.merlin-core/core/config/config-resolver.js +411 -0
- package/.merlin-core/core/config/env-interpolator.js +123 -0
- package/.merlin-core/core/config/merge-utils.js +102 -0
- package/.merlin-core/core/config/schemas/core-config.schema.json +41 -0
- package/.merlin-core/core/config/schemas/framework-config.schema.json +24 -0
- package/.merlin-core/core/config/schemas/local-config.schema.json +23 -0
- package/.merlin-core/core/config/schemas/project-config.schema.json +189 -0
- package/.merlin-core/core/docs-consistency.js +140 -0
- package/.merlin-core/core/events/event-bus.js +344 -0
- package/.merlin-core/core/events/hook-handler.js +419 -0
- package/.merlin-core/core/execution/README.md +17 -0
- package/.merlin-core/core/execution/attempt-journal.js +380 -0
- package/.merlin-core/core/execution/autonomous-build-loop.js +637 -0
- package/.merlin-core/core/execution/build-orchestrator.js +296 -0
- package/.merlin-core/core/execution/build-state-manager.js +196 -0
- package/.merlin-core/core/execution/context-injector.js +204 -0
- package/.merlin-core/core/execution/cron-engine.js +247 -0
- package/.merlin-core/core/execution/cron-expression.js +148 -0
- package/.merlin-core/core/execution/env-preflight.js +423 -0
- package/.merlin-core/core/execution/guardrail-engine.js +745 -0
- package/.merlin-core/core/execution/heartbeat-engine.js +198 -0
- package/.merlin-core/core/execution/model-router.js +282 -0
- package/.merlin-core/core/execution/parallel-executor.js +378 -0
- package/.merlin-core/core/execution/parallel-monitor.js +201 -0
- package/.merlin-core/core/execution/party-session.js +311 -0
- package/.merlin-core/core/execution/rate-limit-manager.js +152 -0
- package/.merlin-core/core/execution/result-aggregator.js +215 -0
- package/.merlin-core/core/execution/semantic-merge-engine.js +320 -0
- package/.merlin-core/core/execution/subagent-dispatcher.js +721 -0
- package/.merlin-core/core/execution/success-verifier.js +227 -0
- package/.merlin-core/core/execution/task-metadata.js +105 -0
- package/.merlin-core/core/execution/team-executor.js +195 -0
- package/.merlin-core/core/execution/two-tier-editor.js +290 -0
- package/.merlin-core/core/execution/version-snapshot.js +294 -0
- package/.merlin-core/core/execution/wave-executor.js +224 -0
- package/.merlin-core/core/health-check/health-engine.js +415 -0
- package/.merlin-core/core/licensing/activation.js +281 -0
- package/.merlin-core/core/licensing/crc.js +103 -0
- package/.merlin-core/core/licensing/entitlement.js +99 -0
- package/.merlin-core/core/licensing/fingerprint.js +104 -0
- package/.merlin-core/core/licensing/gate.js +133 -0
- package/.merlin-core/core/licensing/hmac.js +42 -0
- package/.merlin-core/core/licensing/key.js +144 -0
- package/.merlin-core/core/licensing/license.js +212 -0
- package/.merlin-core/core/mcp/README.md +16 -0
- package/.merlin-core/core/mcp/browser-capability.js +191 -0
- package/.merlin-core/core/mcp/capability-mapper.js +92 -0
- package/.merlin-core/core/mcp/mcp-connector.js +278 -0
- package/.merlin-core/core/mcp/mcp-registry.js +101 -0
- package/.merlin-core/core/orchestration/README.md +17 -0
- package/.merlin-core/core/orchestration/agent-invoker.js +456 -0
- package/.merlin-core/core/orchestration/condition-evaluator.js +250 -0
- package/.merlin-core/core/orchestration/decision-tree.js +192 -0
- package/.merlin-core/core/orchestration/executor-assignment.js +372 -0
- package/.merlin-core/core/orchestration/gate-evaluator.js +653 -0
- package/.merlin-core/core/orchestration/intent-classifier.js +579 -0
- package/.merlin-core/core/orchestration/lock-manager.js +308 -0
- package/.merlin-core/core/orchestration/master-orchestrator.js +363 -0
- package/.merlin-core/core/orchestration/phase-tool-masks.js +194 -0
- package/.merlin-core/core/orchestration/recovery-handler.js +402 -0
- package/.merlin-core/core/orchestration/reflect-checkpoint.js +431 -0
- package/.merlin-core/core/orchestration/session-state.js +430 -0
- package/.merlin-core/core/orchestration/skill-dispatcher.js +255 -0
- package/.merlin-core/core/orchestration/step-loader.js +226 -0
- package/.merlin-core/core/orchestration/workflow-executor.js +864 -0
- package/.merlin-core/core/process/executor.js +231 -0
- package/.merlin-core/core/process/process-file.js +50 -0
- package/.merlin-core/core/process/secret-scan.js +86 -0
- package/.merlin-core/core/process/signature.js +77 -0
- package/.merlin-core/core/quality-gates/README.md +17 -0
- package/.merlin-core/core/quality-gates/layer1-precommit.js +110 -0
- package/.merlin-core/core/quality-gates/layer2-pr-automation.js +116 -0
- package/.merlin-core/core/quality-gates/layer3-human-review.js +133 -0
- package/.merlin-core/core/registry/service-registry.js +140 -0
- package/.merlin-core/core-config.yaml +159 -0
- package/.merlin-core/development/README.md +17 -0
- package/.merlin-core/development/agents/README.md +16 -0
- package/.merlin-core/development/agents/analyst.md +214 -0
- package/.merlin-core/development/agents/architect.md +166 -0
- package/.merlin-core/development/agents/data-engineer.md +154 -0
- package/.merlin-core/development/agents/dev.md +203 -0
- package/.merlin-core/development/agents/devops.md +236 -0
- package/.merlin-core/development/agents/grimorio.md +125 -0
- package/.merlin-core/development/agents/merlin-master.md +173 -0
- package/.merlin-core/development/agents/meta.md +190 -0
- package/.merlin-core/development/agents/pm.md +145 -0
- package/.merlin-core/development/agents/po.md +172 -0
- package/.merlin-core/development/agents/qa.md +275 -0
- package/.merlin-core/development/agents/researcher.md +218 -0
- package/.merlin-core/development/agents/scout.md +179 -0
- package/.merlin-core/development/agents/sm.md +148 -0
- package/.merlin-core/development/agents/ux.md +169 -0
- package/.merlin-core/development/agents/web-researcher.md +203 -0
- package/.merlin-core/development/checklists/adversarial-review-checklist.md +70 -0
- package/.merlin-core/development/checklists/operations-ci-checklist.md +40 -0
- package/.merlin-core/development/checklists/operations-deploy-checklist.md +54 -0
- package/.merlin-core/development/checklists/operations-publish-checklist.md +47 -0
- package/.merlin-core/development/checklists/source-verification-checklist.md +38 -0
- package/.merlin-core/development/templates/HEARTBEAT-template.md +46 -0
- package/.merlin-core/development/templates/ears-requirements-template.md +93 -0
- package/.merlin-core/development/templates/handoff-template.md +50 -0
- package/.merlin-core/development/templates/prd-template.md +62 -0
- package/.merlin-core/development/templates/research-template.md +53 -0
- package/.merlin-core/development/templates/spec-template.md +84 -0
- package/.merlin-core/development/workflows/brownfield-discovery.yaml +166 -0
- package/.merlin-core/development/workflows/brownfield-service.yaml +52 -0
- package/.merlin-core/development/workflows/development-cycle.yaml +57 -0
- package/.merlin-core/development/workflows/epic-orchestration.yaml +47 -0
- package/.merlin-core/development/workflows/folloni-funnel.yaml +177 -0
- package/.merlin-core/development/workflows/greenfield-fullstack.yaml +167 -0
- package/.merlin-core/development/workflows/greenfield-service.yaml +56 -0
- package/.merlin-core/development/workflows/qa-loop.yaml +115 -0
- package/.merlin-core/development/workflows/spec-pipeline.yaml +185 -0
- package/.merlin-core/development/workflows/steps/folloni-01-research.yaml +35 -0
- package/.merlin-core/development/workflows/steps/folloni-02-architecture.yaml +41 -0
- package/.merlin-core/development/workflows/steps/folloni-03-implementation.yaml +52 -0
- package/.merlin-core/development/workflows/story-development-cycle.yaml +67 -0
- package/.merlin-core/docs/GUIDE.md +413 -0
- package/.merlin-core/docs/merlin-commands-guide-pt.md +183 -0
- package/.merlin-core/framework-config.yaml +148 -0
- package/.merlin-core/hooks/README.md +16 -0
- package/.merlin-core/hooks/precompact-memory-flush.js +69 -0
- package/.merlin-core/hooks/pretooluse-remote-approve.js +113 -0
- package/.merlin-core/hooks/spikes/spike-b-hook.js +70 -0
- package/.merlin-core/hooks/spikes/spike-b-stub.js +70 -0
- package/.merlin-core/index.js +91 -0
- package/.merlin-core/local-config.yaml.template +31 -0
- package/.merlin-core/mcp-servers/lsp-bridge/index.js +397 -0
- package/.merlin-core/modules/scraping/module.json +23 -0
- package/.merlin-core/project-config.yaml +89 -0
- package/.merlin-core/schemas/README.md +18 -0
- package/.merlin-core/schemas/agent-hook-schema.json +152 -0
- package/.merlin-core/schemas/agent-schema.json +31 -0
- package/.merlin-core/schemas/command-schema.json +18 -0
- package/.merlin-core/schemas/feedback-schema.json +36 -0
- package/.merlin-core/schemas/handoff-schema.json +19 -0
- package/.merlin-core/schemas/learning-schema.json +51 -0
- package/.merlin-core/schemas/module.schema.json +124 -0
- package/.merlin-core/schemas/must-haves-schema.json +95 -0
- package/.merlin-core/schemas/objective-schema.json +23 -0
- package/.merlin-core/schemas/plan-schema.json +20 -0
- package/.merlin-core/schemas/process-schema.json +82 -0
- package/.merlin-core/schemas/reminder-schema.json +20 -0
- package/.merlin-core/schemas/skill-eval-schema.json +92 -0
- package/.merlin-core/schemas/skill-schema.json +77 -0
- package/.merlin-core/schemas/workflow-schema.json +38 -0
- package/.merlin-core/skills/README.md +16 -0
- package/.merlin-core/skills/domain/azure-cloud/SKILL.md +211 -0
- package/.merlin-core/skills/domain/azure-cloud/references/appinsights-instrumentation.md +63 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-compliance.md +99 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-cost-optimization.md +419 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-deploy.md +82 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-diagnostics.md +130 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-prepare.md +134 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-quotas.md +290 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-rbac.md +11 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-resource-lookup.md +97 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-resource-visualizer.md +178 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-storage.md +91 -0
- package/.merlin-core/skills/domain/azure-cloud/references/azure-validate.md +58 -0
- package/.merlin-core/skills/domain/azure-cloud/references/entra-app-registration.md +192 -0
- package/.merlin-core/skills/domain/browser-automation/SKILL.md +311 -0
- package/.merlin-core/skills/domain/browser-automation/references/agent-browser-skill.md +632 -0
- package/.merlin-core/skills/domain/browser-automation/references/authentication.md +308 -0
- package/.merlin-core/skills/domain/browser-automation/references/commands.md +266 -0
- package/.merlin-core/skills/domain/browser-automation/references/profiling.md +120 -0
- package/.merlin-core/skills/domain/browser-automation/references/proxy-support.md +194 -0
- package/.merlin-core/skills/domain/browser-automation/references/session-management.md +194 -0
- package/.merlin-core/skills/domain/browser-automation/references/snapshot-refs.md +196 -0
- package/.merlin-core/skills/domain/browser-automation/references/video-recording.md +173 -0
- package/.merlin-core/skills/domain/browser-automation/templates/authenticated-session.sh +105 -0
- package/.merlin-core/skills/domain/browser-automation/templates/capture-workflow.sh +69 -0
- package/.merlin-core/skills/domain/browser-automation/templates/form-automation.sh +62 -0
- package/.merlin-core/skills/domain/digital-marketing/SKILL.md +292 -0
- package/.merlin-core/skills/domain/digital-marketing/references/content-strategy.md +320 -0
- package/.merlin-core/skills/domain/digital-marketing/references/copy-formats.md +298 -0
- package/.merlin-core/skills/domain/digital-marketing/references/copy-methodology.md +180 -0
- package/.merlin-core/skills/domain/digital-marketing/references/email-sequences.md +135 -0
- package/.merlin-core/skills/domain/digital-marketing/references/launch-strategy.md +213 -0
- package/.merlin-core/skills/domain/digital-marketing/references/pricing-strategy.md +160 -0
- package/.merlin-core/skills/domain/digital-marketing/references/programmatic-seo.md +237 -0
- package/.merlin-core/skills/domain/digital-marketing/references/revops-lifecycle.md +170 -0
- package/.merlin-core/skills/domain/digital-marketing/references/revops-operations.md +167 -0
- package/.merlin-core/skills/domain/digital-marketing/references/schema-markup.md +190 -0
- package/.merlin-core/skills/domain/digital-marketing/references/strategy-frameworks.md +324 -0
- package/.merlin-core/skills/domain/digital-marketing/references/traffic-management.md +350 -0
- package/.merlin-core/skills/domain/expo-native-ui/SKILL.md +348 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/animations.md +220 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/api-routes.md +361 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/cicd-workflows.md +84 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/controls.md +266 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/data-fetching.md +553 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/deployment-stores.md +1353 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/deployment.md +183 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/dev-client.md +166 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/dom-components.md +410 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/form-sheet.md +253 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/gradients.md +117 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/icons.md +218 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/media.md +245 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/platform-native.md +75 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/route-structure.md +229 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/search.md +249 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/storage.md +121 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/tabs.md +433 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/tailwind-native.md +473 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/toolbar-and-headers.md +284 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/upgrading-guides.md +674 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/upgrading.md +127 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/visual-effects.md +199 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/webgpu-three.md +605 -0
- package/.merlin-core/skills/domain/expo-native-ui/references/zoom-transitions.md +161 -0
- package/.merlin-core/skills/domain/marketing-ops/SKILL.md +117 -0
- package/.merlin-core/skills/domain/marketing-ops/references/_index.md +78 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ad-creative/references/generative-tools.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ad-creative/references/platform-specs.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ad-creative.md +251 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ads/references/ad-copy-templates.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ads/references/audience-targeting.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ads/references/conversion-tracking.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ads/references/platform-setup-checklists.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ads.md +322 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ai-seo/references/content-patterns.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ai-seo/references/content-types.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ai-seo/references/platform-ranking-factors.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/ai-seo.md +388 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/aso/references/apple-specs.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/aso/references/benchmarks.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/aso/references/google-play-specs.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/aso/references/report-template.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/aso/references/scoring-criteria.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/aso.md +316 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/co-marketing.md +305 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/community-marketing.md +169 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/competitor-profiling/references/templates.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/competitor-profiling/references/tool-reference.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/competitor-profiling.md +442 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/competitors/references/content-architecture.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/competitors/references/templates.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/competitors.md +281 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/content-strategy.md +16 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/directory-submissions/references/directory-list.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/directory-submissions/references/positioning-variations.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/directory-submissions/references/submission-tracker-template.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/directory-submissions.md +396 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/free-tools/references/tool-types.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/free-tools.md +196 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/image/references/ai-image-prompting.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/image.md +352 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/launch.md +18 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/lead-magnets/references/benchmarks.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/lead-magnets/references/format-guide.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/lead-magnets.md +333 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/programmatic-seo.md +16 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/schema.md +16 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/seo-audit/references/ai-writing-detection.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/seo-audit/references/international-seo.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/seo-audit.md +546 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/site-architecture/references/mermaid-templates.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/site-architecture/references/navigation-patterns.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/site-architecture/references/site-type-templates.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/site-architecture.md +371 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/social/references/platform-limits.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/social/references/platforms.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/social/references/post-templates.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/social/references/reverse-engineering.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/social/references/short-form-video.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/social.md +431 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/video/references/ai-video-prompting.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/acquire/video.md +353 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/ab-testing/references/sample-size-guide.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/ab-testing/references/test-templates.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/ab-testing.md +379 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/analytics/references/event-library.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/analytics/references/ga4-implementation.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/analytics/references/gtm-implementation.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/analytics.md +323 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/copy-editing.md +18 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/copywriting.md +18 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/cro/references/experiments.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/cro/references/form.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/cro.md +211 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/emails.md +18 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/paywalls/references/experiments.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/paywalls.md +255 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/popups.md +518 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/pricing.md +18 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/sales-enablement/references/deck-frameworks.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/sales-enablement/references/demo-scripts.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/sales-enablement/references/objection-library.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/sales-enablement/references/one-pager-templates.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/sales-enablement.md +371 -0
- package/.merlin-core/skills/domain/marketing-ops/references/activate/signup.md +406 -0
- package/.merlin-core/skills/domain/marketing-ops/references/expand/co-marketing.md +18 -0
- package/.merlin-core/skills/domain/marketing-ops/references/expand/community-marketing.md +18 -0
- package/.merlin-core/skills/domain/marketing-ops/references/expand/referrals/references/affiliate-programs.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/expand/referrals/references/program-examples.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/expand/referrals.md +278 -0
- package/.merlin-core/skills/domain/marketing-ops/references/foundation/customer-research/references/source-guides.md +425 -0
- package/.merlin-core/skills/domain/marketing-ops/references/foundation/customer-research.md +284 -0
- package/.merlin-core/skills/domain/marketing-ops/references/foundation/marketing-ideas/references/ideas-by-category.md +216 -0
- package/.merlin-core/skills/domain/marketing-ops/references/foundation/marketing-ideas.md +188 -0
- package/.merlin-core/skills/domain/marketing-ops/references/foundation/marketing-psychology.md +532 -0
- package/.merlin-core/skills/domain/marketing-ops/references/foundation/product-marketing.md +276 -0
- package/.merlin-core/skills/domain/marketing-ops/references/retain/churn-prevention/references/cancel-flow-patterns.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/retain/churn-prevention/references/dunning-playbook.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/retain/churn-prevention.md +442 -0
- package/.merlin-core/skills/domain/marketing-ops/references/retain/onboarding/references/experiments.md +19 -0
- package/.merlin-core/skills/domain/marketing-ops/references/retain/onboarding.md +243 -0
- package/.merlin-core/skills/domain/marketing-ops/references/retain/revops-lifecycle.md +18 -0
- package/.merlin-core/skills/domain/marketing-ops/references/retain/revops-operations.md +18 -0
- package/.merlin-core/skills/domain/n8n-automation/SKILL.md +149 -0
- package/.merlin-core/skills/domain/n8n-automation/references/code-javascript.md +3744 -0
- package/.merlin-core/skills/domain/n8n-automation/references/code-python.md +3293 -0
- package/.merlin-core/skills/domain/n8n-automation/references/expression-syntax.md +1662 -0
- package/.merlin-core/skills/domain/n8n-automation/references/mcp-tools-expert.md +2111 -0
- package/.merlin-core/skills/domain/n8n-automation/references/node-configuration.md +2523 -0
- package/.merlin-core/skills/domain/n8n-automation/references/validation-expert.md +2491 -0
- package/.merlin-core/skills/domain/n8n-automation/references/workflow-patterns.md +4624 -0
- package/.merlin-core/skills/domain/ops-manual/SKILL.md +225 -0
- package/.merlin-core/skills/domain/ops-manual/references/elicitation-questions.md +141 -0
- package/.merlin-core/skills/domain/ops-manual/references/external-skills-registry.md +63 -0
- package/.merlin-core/skills/domain/ops-manual/references/operations-template.yaml +132 -0
- package/.merlin-core/skills/domain/remotion-best-practices/SKILL.md +99 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/3d.md +86 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/animations.md +27 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/assets.md +78 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/audio.md +172 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/calculate-metadata.md +131 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/can-decode.md +75 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/charts.md +68 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/compositions.md +154 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/display-captions.md +126 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/extract-frames.md +229 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/fonts.md +152 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/gifs.md +144 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/images.md +134 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/lottie.md +70 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/maps.md +414 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/measuring-text.md +143 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/parameters.md +109 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/sequencing.md +118 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/tailwind.md +11 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/text-animations.md +20 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/timing.md +179 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/transitions.md +137 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/transparent-videos.md +106 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/trimming.md +51 -0
- package/.merlin-core/skills/domain/remotion-best-practices/rules/videos.md +171 -0
- package/.merlin-core/skills/domain/resend-email/SKILL.md +377 -0
- package/.merlin-core/skills/general/adversarial-review/SKILL.md +144 -0
- package/.merlin-core/skills/general/api-design/SKILL.md +513 -0
- package/.merlin-core/skills/general/apify-scrape/SKILL.md +137 -0
- package/.merlin-core/skills/general/apify-scrape/scripts/apify-scrape.sh +68 -0
- package/.merlin-core/skills/general/backup/SKILL.md +87 -0
- package/.merlin-core/skills/general/blkskrn/SKILL.md +392 -0
- package/.merlin-core/skills/general/blkskrn/references/animation-patterns.md +521 -0
- package/.merlin-core/skills/general/blkskrn/references/design-system.md +637 -0
- package/.merlin-core/skills/general/blkskrn/references/html-templates.md +440 -0
- package/.merlin-core/skills/general/blkskrn/references/presenter-template.md +45 -0
- package/.merlin-core/skills/general/blkskrn/references/slide-types.md +424 -0
- package/.merlin-core/skills/general/blkskrn/scripts/canvas-manager.js +502 -0
- package/.merlin-core/skills/general/blkskrn/scripts/presenter.js +90 -0
- package/.merlin-core/skills/general/blkskrn/templates/presenter.html +273 -0
- package/.merlin-core/skills/general/blkskrn/templates/slide-base.html +277 -0
- package/.merlin-core/skills/general/blkskrn/templates/viewer.html +165 -0
- package/.merlin-core/skills/general/browser-takeover/SKILL.md +53 -0
- package/.merlin-core/skills/general/claude-api/SKILL.md +90 -0
- package/.merlin-core/skills/general/code-javascript/SKILL.md +268 -0
- package/.merlin-core/skills/general/code-python/SKILL.md +424 -0
- package/.merlin-core/skills/general/code-style/SKILL.md +97 -0
- package/.merlin-core/skills/general/code-typescript/SKILL.md +361 -0
- package/.merlin-core/skills/general/cold-email/SKILL.md +164 -0
- package/.merlin-core/skills/general/cold-email/references/benchmarks.md +18 -0
- package/.merlin-core/skills/general/cold-email/references/follow-up-sequences.md +18 -0
- package/.merlin-core/skills/general/cold-email/references/frameworks.md +18 -0
- package/.merlin-core/skills/general/cold-email/references/personalization.md +18 -0
- package/.merlin-core/skills/general/cold-email/references/subject-lines.md +18 -0
- package/.merlin-core/skills/general/container-security/SKILL.md +462 -0
- package/.merlin-core/skills/general/context-management/SKILL.md +79 -0
- package/.merlin-core/skills/general/copy-editing/SKILL.md +501 -0
- package/.merlin-core/skills/general/copy-editing/references/checklist.md +18 -0
- package/.merlin-core/skills/general/copy-editing/references/content-refresh.md +18 -0
- package/.merlin-core/skills/general/copy-editing/references/plain-english-alternatives.md +18 -0
- package/.merlin-core/skills/general/copywriting/SKILL.md +294 -0
- package/.merlin-core/skills/general/copywriting/references/copy-frameworks.md +392 -0
- package/.merlin-core/skills/general/copywriting/references/natural-transitions.md +276 -0
- package/.merlin-core/skills/general/database/SKILL.md +561 -0
- package/.merlin-core/skills/general/database/references/postgres-concurrency.md +182 -0
- package/.merlin-core/skills/general/database/references/postgres-connections.md +97 -0
- package/.merlin-core/skills/general/database/references/postgres-data-patterns.md +159 -0
- package/.merlin-core/skills/general/database/references/postgres-monitoring.md +136 -0
- package/.merlin-core/skills/general/database/references/postgres-rls.md +140 -0
- package/.merlin-core/skills/general/database-provision/SKILL.md +56 -0
- package/.merlin-core/skills/general/deploy/SKILL.md +65 -0
- package/.merlin-core/skills/general/design-inspiration/SKILL.md +146 -0
- package/.merlin-core/skills/general/design-palette/SKILL.md +99 -0
- package/.merlin-core/skills/general/design-palette/references/full-palettes.md +144 -0
- package/.merlin-core/skills/general/design-system/SKILL.md +94 -0
- package/.merlin-core/skills/general/design-typography/SKILL.md +115 -0
- package/.merlin-core/skills/general/design-typography/references/full-pairings.md +144 -0
- package/.merlin-core/skills/general/design-ux-patterns/SKILL.md +155 -0
- package/.merlin-core/skills/general/design-ux-patterns/references/charts-data-guidelines.md +197 -0
- package/.merlin-core/skills/general/design-ux-patterns/references/landing-patterns.md +199 -0
- package/.merlin-core/skills/general/design-ux-patterns/references/professional-ui-checklist.md +56 -0
- package/.merlin-core/skills/general/design-ux-patterns/references/style-catalog.md +89 -0
- package/.merlin-core/skills/general/design-ux-patterns/references/ux-guidelines.md +837 -0
- package/.merlin-core/skills/general/discover-cloud/SKILL.md +108 -0
- package/.merlin-core/skills/general/doc-sync/SKILL.md +52 -0
- package/.merlin-core/skills/general/document-sharding/SKILL.md +53 -0
- package/.merlin-core/skills/general/docx/SKILL.md +418 -0
- package/.merlin-core/skills/general/docx/references/windows-setup.md +27 -0
- package/.merlin-core/skills/general/docx/scripts/__init__.py +1 -0
- package/.merlin-core/skills/general/docx/scripts/accept_changes.py +135 -0
- package/.merlin-core/skills/general/docx/scripts/comment.py +318 -0
- package/.merlin-core/skills/general/docx/scripts/office/__init__.py +0 -0
- package/.merlin-core/skills/general/docx/scripts/office/helpers/__init__.py +0 -0
- package/.merlin-core/skills/general/docx/scripts/office/helpers/merge_runs.py +199 -0
- package/.merlin-core/skills/general/docx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/.merlin-core/skills/general/docx/scripts/office/pack.py +159 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/.merlin-core/skills/general/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/.merlin-core/skills/general/docx/scripts/office/soffice.py +183 -0
- package/.merlin-core/skills/general/docx/scripts/office/unpack.py +132 -0
- package/.merlin-core/skills/general/docx/scripts/office/validate.py +111 -0
- package/.merlin-core/skills/general/docx/scripts/office/validators/__init__.py +15 -0
- package/.merlin-core/skills/general/docx/scripts/office/validators/base.py +847 -0
- package/.merlin-core/skills/general/docx/scripts/office/validators/docx.py +446 -0
- package/.merlin-core/skills/general/docx/scripts/office/validators/pptx.py +275 -0
- package/.merlin-core/skills/general/docx/scripts/office/validators/redlining.py +247 -0
- package/.merlin-core/skills/general/docx/scripts/templates/comments.xml +3 -0
- package/.merlin-core/skills/general/docx/scripts/templates/commentsExtended.xml +3 -0
- package/.merlin-core/skills/general/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/.merlin-core/skills/general/docx/scripts/templates/commentsIds.xml +3 -0
- package/.merlin-core/skills/general/docx/scripts/templates/people.xml +3 -0
- package/.merlin-core/skills/general/elicitation/SKILL.md +188 -0
- package/.merlin-core/skills/general/engineering-audit/SKILL.md +122 -0
- package/.merlin-core/skills/general/find-and-edit/SKILL.md +102 -0
- package/.merlin-core/skills/general/first-party-docs/SKILL.md +51 -0
- package/.merlin-core/skills/general/frontend-design/SKILL.md +204 -0
- package/.merlin-core/skills/general/guardrails/SKILL.md +144 -0
- package/.merlin-core/skills/general/image-gen/SKILL.md +49 -0
- package/.merlin-core/skills/general/learning-capture/SKILL.md +192 -0
- package/.merlin-core/skills/general/lgpd-compliance-audit/SKILL.md +448 -0
- package/.merlin-core/skills/general/load-testing/SKILL.md +114 -0
- package/.merlin-core/skills/general/load-testing/docker/Dockerfile.dashboard +21 -0
- package/.merlin-core/skills/general/load-testing/docker/docker-compose.locust.yml +39 -0
- package/.merlin-core/skills/general/load-testing/requirements.txt +1 -0
- package/.merlin-core/skills/general/load-testing/scripts/compare_baseline.py +172 -0
- package/.merlin-core/skills/general/load-testing/scripts/run_local.py +245 -0
- package/.merlin-core/skills/general/load-testing/templates/load_shape_stepped.py +35 -0
- package/.merlin-core/skills/general/load-testing/templates/locustfile_dashboard.py +47 -0
- package/.merlin-core/skills/general/load-testing/templates/threshold_hook.py +36 -0
- package/.merlin-core/skills/general/mage-beauty/SKILL.md +89 -0
- package/.merlin-core/skills/general/mage-beauty/references/anti-patterns.md +148 -0
- package/.merlin-core/skills/general/mage-beauty/references/color-and-contrast.md +87 -0
- package/.merlin-core/skills/general/mage-beauty/references/interaction-design.md +99 -0
- package/.merlin-core/skills/general/mage-beauty/references/motion-design.md +90 -0
- package/.merlin-core/skills/general/mage-beauty/references/remotion-bridge.md +187 -0
- package/.merlin-core/skills/general/mage-beauty/references/responsive-and-multi-format.md +98 -0
- package/.merlin-core/skills/general/mage-beauty/references/spatial-design.md +88 -0
- package/.merlin-core/skills/general/mage-beauty/references/typography.md +60 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-adapt.md +102 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-animate.md +97 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-audit.md +99 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-bolder.md +94 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-cinematic.md +128 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-clarify.md +107 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-colorize.md +106 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-critique.md +88 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-delight.md +98 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-distill.md +97 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-harden.md +79 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-layout.md +104 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-onboard.md +98 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-optimize.md +124 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-overdrive.md +105 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-polish.md +91 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-quieter.md +95 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-rebrand.md +127 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-shape.md +160 -0
- package/.merlin-core/skills/general/mage-beauty/references/verb-typeset.md +109 -0
- package/.merlin-core/skills/general/mage-beauty/references/voice-and-microcopy.md +137 -0
- package/.merlin-core/skills/general/mcp-builder/SKILL.md +92 -0
- package/.merlin-core/skills/general/network-debug/SKILL.md +51 -0
- package/.merlin-core/skills/general/next-best-practices/SKILL.md +177 -0
- package/.merlin-core/skills/general/next-best-practices/references/async-patterns.md +87 -0
- package/.merlin-core/skills/general/next-best-practices/references/bundling.md +182 -0
- package/.merlin-core/skills/general/next-best-practices/references/data-patterns.md +306 -0
- package/.merlin-core/skills/general/next-best-practices/references/debug-tricks.md +125 -0
- package/.merlin-core/skills/general/next-best-practices/references/directives.md +74 -0
- package/.merlin-core/skills/general/next-best-practices/references/error-handling.md +232 -0
- package/.merlin-core/skills/general/next-best-practices/references/file-conventions.md +141 -0
- package/.merlin-core/skills/general/next-best-practices/references/font.md +257 -0
- package/.merlin-core/skills/general/next-best-practices/references/functions.md +108 -0
- package/.merlin-core/skills/general/next-best-practices/references/hydration-error.md +88 -0
- package/.merlin-core/skills/general/next-best-practices/references/image.md +179 -0
- package/.merlin-core/skills/general/next-best-practices/references/metadata.md +296 -0
- package/.merlin-core/skills/general/next-best-practices/references/parallel-routes.md +298 -0
- package/.merlin-core/skills/general/next-best-practices/references/route-handlers.md +146 -0
- package/.merlin-core/skills/general/next-best-practices/references/rsc-boundaries.md +164 -0
- package/.merlin-core/skills/general/next-best-practices/references/runtime-selection.md +40 -0
- package/.merlin-core/skills/general/next-best-practices/references/scripts.md +141 -0
- package/.merlin-core/skills/general/next-best-practices/references/self-hosting.md +384 -0
- package/.merlin-core/skills/general/next-best-practices/references/suspense-boundaries.md +67 -0
- package/.merlin-core/skills/general/next-steps/SKILL.md +43 -0
- package/.merlin-core/skills/general/party-mode/SKILL.md +57 -0
- package/.merlin-core/skills/general/pdf/SKILL.md +298 -0
- package/.merlin-core/skills/general/pdf/references/forms.md +312 -0
- package/.merlin-core/skills/general/pdf/references/reference.md +640 -0
- package/.merlin-core/skills/general/pdf/references/windows-setup.md +40 -0
- package/.merlin-core/skills/general/pdf/scripts/check_bounding_boxes.py +65 -0
- package/.merlin-core/skills/general/pdf/scripts/check_fillable_fields.py +11 -0
- package/.merlin-core/skills/general/pdf/scripts/convert_pdf_to_images.py +33 -0
- package/.merlin-core/skills/general/pdf/scripts/create_validation_image.py +37 -0
- package/.merlin-core/skills/general/pdf/scripts/extract_form_field_info.py +122 -0
- package/.merlin-core/skills/general/pdf/scripts/extract_form_structure.py +115 -0
- package/.merlin-core/skills/general/pdf/scripts/fill_fillable_fields.py +98 -0
- package/.merlin-core/skills/general/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/.merlin-core/skills/general/pptx/SKILL.md +133 -0
- package/.merlin-core/skills/general/pptx/references/editing.md +213 -0
- package/.merlin-core/skills/general/pptx/references/pptxgenjs.md +581 -0
- package/.merlin-core/skills/general/pptx/references/windows-setup.md +27 -0
- package/.merlin-core/skills/general/pptx/scripts/__init__.py +0 -0
- package/.merlin-core/skills/general/pptx/scripts/add_slide.py +195 -0
- package/.merlin-core/skills/general/pptx/scripts/clean.py +286 -0
- package/.merlin-core/skills/general/pptx/scripts/thumbnail.py +289 -0
- package/.merlin-core/skills/general/property-testing/SKILL.md +214 -0
- package/.merlin-core/skills/general/purge-leaked-secret/SKILL.md +383 -0
- package/.merlin-core/skills/general/reflection/SKILL.md +100 -0
- package/.merlin-core/skills/general/secret-safe-commit/SKILL.md +246 -0
- package/.merlin-core/skills/general/secret-safe-commit/templates/.gitleaks.toml +91 -0
- package/.merlin-core/skills/general/secret-safe-commit/templates/.pre-commit-config.yaml +57 -0
- package/.merlin-core/skills/general/secret-safe-commit/templates/secret-scan.yml +48 -0
- package/.merlin-core/skills/general/semantic-search/SKILL.md +79 -0
- package/.merlin-core/skills/general/skill-creator/SKILL.md +342 -0
- package/.merlin-core/skills/general/skill-creator/agents/analyzer.md +283 -0
- package/.merlin-core/skills/general/skill-creator/agents/comparator.md +211 -0
- package/.merlin-core/skills/general/skill-creator/agents/grader.md +227 -0
- package/.merlin-core/skills/general/skill-creator/assets/eval_review.html +146 -0
- package/.merlin-core/skills/general/skill-creator/eval-viewer/generate_review.py +471 -0
- package/.merlin-core/skills/general/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.merlin-core/skills/general/skill-creator/references/schemas.md +439 -0
- package/.merlin-core/skills/general/skill-creator/scripts/__init__.py +0 -0
- package/.merlin-core/skills/general/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/.merlin-core/skills/general/skill-creator/scripts/generate_report.py +326 -0
- package/.merlin-core/skills/general/skill-creator/scripts/improve_description.py +247 -0
- package/.merlin-core/skills/general/skill-creator/scripts/package_skill.py +136 -0
- package/.merlin-core/skills/general/skill-creator/scripts/quick_validate.py +103 -0
- package/.merlin-core/skills/general/skill-creator/scripts/run_eval.py +310 -0
- package/.merlin-core/skills/general/skill-creator/scripts/run_loop.py +328 -0
- package/.merlin-core/skills/general/skill-creator/scripts/utils.py +47 -0
- package/.merlin-core/skills/general/start-here/SKILL.md +63 -0
- package/.merlin-core/skills/general/start-here/recipes.json +758 -0
- package/.merlin-core/skills/general/start-here/recipes.schema.json +57 -0
- package/.merlin-core/skills/general/static-analysis/SKILL.md +151 -0
- package/.merlin-core/skills/general/tailwind-design-system/SKILL.md +201 -0
- package/.merlin-core/skills/general/tailwind-design-system/references/advanced-v4.md +152 -0
- package/.merlin-core/skills/general/tailwind-design-system/references/component-patterns.md +353 -0
- package/.merlin-core/skills/general/teach-method/SKILL.md +86 -0
- package/.merlin-core/skills/general/team-execution/SKILL.md +67 -0
- package/.merlin-core/skills/general/testing/SKILL.md +412 -0
- package/.merlin-core/skills/general/token-economy/SKILL.md +55 -0
- package/.merlin-core/skills/general/vps-security-hardening/SKILL.md +406 -0
- package/.merlin-core/skills/general/web-quality/SKILL.md +180 -0
- package/.merlin-core/skills/general/webapp-testing/SKILL.md +153 -0
- package/.merlin-core/skills/general/webapp-testing/scripts/screenshot_compare.py +72 -0
- package/.merlin-core/skills/general/webapp-testing/scripts/with_server.py +103 -0
- package/.merlin-core/skills/general/xlsx/SKILL.md +167 -0
- package/.merlin-core/skills/general/xlsx/references/nodejs-sheetjs-styled-reports.md +141 -0
- package/.merlin-core/skills/general/xlsx/references/windows-setup.md +17 -0
- package/.merlin-core/skills/general/xlsx/scripts/recalc.py +184 -0
- package/.merlin-core/skills/general/xlsx/scripts/styled-report.js +130 -0
- package/.merlin-core/skills/general/yolo-mode/SKILL.md +60 -0
- package/.merlin-core/skills/general/youtube-transcript/SKILL.md +177 -0
- package/.merlin-core/skills/general/youtube-transcript/scripts/fetch_transcript.py +188 -0
- package/.merlin-core/skills/general/youtube-transcript/scripts/gladia_transcribe.mjs +230 -0
- package/.merlin-core/tools/commands/activate.js +72 -0
- package/.merlin-core/tools/commands/archive-thoughts.js +181 -0
- package/.merlin-core/tools/commands/backup.js +156 -0
- package/.merlin-core/tools/commands/certify-process.js +196 -0
- package/.merlin-core/tools/commands/convert.js +87 -0
- package/.merlin-core/tools/commands/cron.js +147 -0
- package/.merlin-core/tools/commands/disable.js +73 -0
- package/.merlin-core/tools/commands/doc-sync.js +127 -0
- package/.merlin-core/tools/commands/eval-skill.js +193 -0
- package/.merlin-core/tools/commands/frontmatter.js +49 -0
- package/.merlin-core/tools/commands/heartbeat.js +43 -0
- package/.merlin-core/tools/commands/index-thoughts.js +35 -0
- package/.merlin-core/tools/commands/install-remote-approve.js +184 -0
- package/.merlin-core/tools/commands/install.js +81 -0
- package/.merlin-core/tools/commands/lib/__verify__/diff-reports.js +170 -0
- package/.merlin-core/tools/commands/lib/fs-safe.js +186 -0
- package/.merlin-core/tools/commands/lib/preflight.js +607 -0
- package/.merlin-core/tools/commands/lib/preserve.js +232 -0
- package/.merlin-core/tools/commands/lib/project-config.template.yaml +69 -0
- package/.merlin-core/tools/commands/lib/report.js +231 -0
- package/.merlin-core/tools/commands/lib/settings-merge.js +134 -0
- package/.merlin-core/tools/commands/license.js +52 -0
- package/.merlin-core/tools/commands/list.js +125 -0
- package/.merlin-core/tools/commands/migrate-alkimia.js +271 -0
- package/.merlin-core/tools/commands/modules.js +68 -0
- package/.merlin-core/tools/commands/provision.js +83 -0
- package/.merlin-core/tools/commands/prune-feedback.js +114 -0
- package/.merlin-core/tools/commands/run-process.js +28 -0
- package/.merlin-core/tools/commands/state.js +79 -0
- package/.merlin-core/tools/commands/sync-bridges.js +197 -0
- package/.merlin-core/tools/commands/upgrade.js +1135 -0
- package/.merlin-core/tools/commands/validate-recipes.js +218 -0
- package/.merlin-core/tools/commands/validate.js +159 -0
- package/.merlin-core/tools/commands/yolo.js +82 -0
- package/.merlin-core/tools/compose-rules.mjs +179 -0
- package/.merlin-core/tools/disable-module.mjs +150 -0
- package/.merlin-core/tools/lib/deployer.mjs +131 -0
- package/.merlin-core/tools/lib/modules-activation.mjs +225 -0
- package/.merlin-core/tools/merlin-tools.js +153 -0
- package/.merlin-core/tools/migrate-frontmatter-v3.js +192 -0
- package/.merlin-core/tools/modules-catalog.mjs +174 -0
- package/.merlin-core/tools/provision-module.mjs +191 -0
- package/.merlin-core/tools/verify-module.mjs +99 -0
- package/.merlin-core/tools/vps-security-audit.sh +234 -0
- package/INSTALL.md +312 -0
- package/LICENSE +118 -0
- package/PRIVACY-LICENSING.md +65 -0
- package/README.md +391 -0
- package/bin/README.md +15 -0
- package/bin/convert-to-merlin.sh +109 -0
- package/bin/fleet-patch-hooks.sh +144 -0
- package/bin/fleet-patch-v3-fixes.sh +127 -0
- package/bin/merlin-init.js +232 -0
- package/bin/merlin.js +321 -0
- package/package.json +127 -0
|
@@ -0,0 +1,3744 @@
|
|
|
1
|
+
<!-- Adapted from czlonkowski/n8n-skills (MIT). Source files: SKILL.md, DATA_ACCESS.md, BUILTIN_FUNCTIONS.md, COMMON_PATTERNS.md, ERROR_PATTERNS.md -->
|
|
2
|
+
|
|
3
|
+
# JavaScript Code Node — Consolidated Reference
|
|
4
|
+
|
|
5
|
+
Expert guidance for writing JavaScript code in n8n Code nodes.
|
|
6
|
+
|
|
7
|
+
> **Cross-references:**
|
|
8
|
+
>
|
|
9
|
+
> - [expression-syntax.md](expression-syntax.md) — Expression `{{ }}` syntax is for OTHER nodes; never use `{{ }}` inside Code nodes!
|
|
10
|
+
> - [code-python.md](code-python.md) — Python alternative for Code nodes
|
|
11
|
+
> - [validation-expert.md](validation-expert.md) — Validate Code node configuration and auto-fix common issues
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
// Basic template for Code nodes
|
|
19
|
+
const items = $input.all();
|
|
20
|
+
|
|
21
|
+
// Process data
|
|
22
|
+
const processed = items.map((item) => ({
|
|
23
|
+
json: {
|
|
24
|
+
...item.json,
|
|
25
|
+
processed: true,
|
|
26
|
+
timestamp: new Date().toISOString(),
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
return processed;
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Essential Rules
|
|
34
|
+
|
|
35
|
+
1. **Choose "Run Once for All Items" mode** (recommended for most use cases)
|
|
36
|
+
2. **Access data**: `$input.all()`, `$input.first()`, or `$input.item`
|
|
37
|
+
3. **CRITICAL**: Must return `[{json: {...}}]` format
|
|
38
|
+
4. **CRITICAL**: Webhook data is under `$json.body` (not `$json` directly)
|
|
39
|
+
5. **Built-ins available**: $helpers.httpRequest(), DateTime (Luxon), $jmespath()
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Mode Selection Guide
|
|
44
|
+
|
|
45
|
+
The Code node offers two execution modes. Choose based on your use case:
|
|
46
|
+
|
|
47
|
+
### Run Once for All Items (Recommended - Default)
|
|
48
|
+
|
|
49
|
+
**Use this mode for:** 95% of use cases
|
|
50
|
+
|
|
51
|
+
- **How it works**: Code executes **once** regardless of input count
|
|
52
|
+
- **Data access**: `$input.all()` or `items` array
|
|
53
|
+
- **Best for**: Aggregation, filtering, batch processing, transformations, API calls with all data
|
|
54
|
+
- **Performance**: Faster for multiple items (single execution)
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// Example: Calculate total from all items
|
|
58
|
+
const allItems = $input.all();
|
|
59
|
+
const total = allItems.reduce((sum, item) => sum + (item.json.amount || 0), 0);
|
|
60
|
+
|
|
61
|
+
return [
|
|
62
|
+
{
|
|
63
|
+
json: {
|
|
64
|
+
total,
|
|
65
|
+
count: allItems.length,
|
|
66
|
+
average: total / allItems.length,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**When to use:**
|
|
73
|
+
|
|
74
|
+
- Comparing items across the dataset
|
|
75
|
+
- Calculating totals, averages, or statistics
|
|
76
|
+
- Sorting or ranking items
|
|
77
|
+
- Deduplication
|
|
78
|
+
- Building aggregated reports
|
|
79
|
+
- Combining data from multiple items
|
|
80
|
+
|
|
81
|
+
### Run Once for Each Item
|
|
82
|
+
|
|
83
|
+
**Use this mode for:** Specialized cases only
|
|
84
|
+
|
|
85
|
+
- **How it works**: Code executes **separately** for each input item
|
|
86
|
+
- **Data access**: `$input.item` or `$item`
|
|
87
|
+
- **Best for**: Item-specific logic, independent operations, per-item validation
|
|
88
|
+
- **Performance**: Slower for large datasets (multiple executions)
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
// Example: Add processing timestamp to each item
|
|
92
|
+
const item = $input.item;
|
|
93
|
+
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
json: {
|
|
97
|
+
...item.json,
|
|
98
|
+
processed: true,
|
|
99
|
+
processedAt: new Date().toISOString(),
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**When to use:**
|
|
106
|
+
|
|
107
|
+
- Each item needs independent API call
|
|
108
|
+
- Per-item validation with different error handling
|
|
109
|
+
- Item-specific transformations based on item properties
|
|
110
|
+
- When items must be processed separately for business logic
|
|
111
|
+
|
|
112
|
+
**Decision Shortcut:**
|
|
113
|
+
|
|
114
|
+
- **Need to look at multiple items?** -> Use "All Items" mode
|
|
115
|
+
- **Each item completely independent?** -> Use "Each Item" mode
|
|
116
|
+
- **Not sure?** -> Use "All Items" mode (you can always loop inside)
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Return Format Requirements
|
|
121
|
+
|
|
122
|
+
**CRITICAL RULE**: Always return array of objects with `json` property
|
|
123
|
+
|
|
124
|
+
### Correct Return Formats
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
// Single result
|
|
128
|
+
return [
|
|
129
|
+
{
|
|
130
|
+
json: {
|
|
131
|
+
field1: value1,
|
|
132
|
+
field2: value2,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
// Multiple results
|
|
138
|
+
return [
|
|
139
|
+
{ json: { id: 1, data: "first" } },
|
|
140
|
+
{ json: { id: 2, data: "second" } },
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
// Transformed array
|
|
144
|
+
const transformed = $input
|
|
145
|
+
.all()
|
|
146
|
+
.filter((item) => item.json.valid)
|
|
147
|
+
.map((item) => ({
|
|
148
|
+
json: {
|
|
149
|
+
id: item.json.id,
|
|
150
|
+
processed: true,
|
|
151
|
+
},
|
|
152
|
+
}));
|
|
153
|
+
return transformed;
|
|
154
|
+
|
|
155
|
+
// Empty result (when no data to return)
|
|
156
|
+
return [];
|
|
157
|
+
|
|
158
|
+
// Conditional return
|
|
159
|
+
if (shouldProcess) {
|
|
160
|
+
return [{ json: processedData }];
|
|
161
|
+
} else {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Incorrect Return Formats
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
// WRONG: Object without array wrapper
|
|
170
|
+
return {
|
|
171
|
+
json: { field: value },
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// WRONG: Array without json wrapper
|
|
175
|
+
return [{ field: value }];
|
|
176
|
+
|
|
177
|
+
// WRONG: Plain string
|
|
178
|
+
return "processed";
|
|
179
|
+
|
|
180
|
+
// WRONG: Raw data without mapping
|
|
181
|
+
return $input.all(); // Missing .map()
|
|
182
|
+
|
|
183
|
+
// WRONG: Incomplete structure
|
|
184
|
+
return [{ data: value }]; // Should be {json: value}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Why it matters**: Next nodes expect array format. Incorrect format causes workflow execution to fail.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Data Access Patterns
|
|
192
|
+
|
|
193
|
+
In n8n Code nodes, you access data from previous nodes using built-in variables and methods. Understanding which method to use is critical for correct workflow execution.
|
|
194
|
+
|
|
195
|
+
**Data Access Priority** (by common usage):
|
|
196
|
+
|
|
197
|
+
1. **`$input.all()`** - Most common - Batch operations, aggregations
|
|
198
|
+
2. **`$input.first()`** - Very common - Single item operations
|
|
199
|
+
3. **`$input.item`** - Common - Each Item mode only
|
|
200
|
+
4. **`$node["NodeName"].json`** - Specific node references
|
|
201
|
+
5. **`$json`** - Direct current item (legacy, use `$input` instead)
|
|
202
|
+
|
|
203
|
+
### Pattern 1: $input.all() - Process All Items
|
|
204
|
+
|
|
205
|
+
**Usage**: Most common pattern for batch processing
|
|
206
|
+
|
|
207
|
+
**When to use:**
|
|
208
|
+
|
|
209
|
+
- Processing multiple records
|
|
210
|
+
- Aggregating data (sum, count, average)
|
|
211
|
+
- Filtering arrays
|
|
212
|
+
- Transforming datasets
|
|
213
|
+
- Comparing items
|
|
214
|
+
- Sorting or ranking
|
|
215
|
+
|
|
216
|
+
#### Basic Usage
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
// Get all items from previous node
|
|
220
|
+
const allItems = $input.all();
|
|
221
|
+
|
|
222
|
+
// allItems is an array of objects like:
|
|
223
|
+
// [
|
|
224
|
+
// {json: {id: 1, name: "Alice"}},
|
|
225
|
+
// {json: {id: 2, name: "Bob"}}
|
|
226
|
+
// ]
|
|
227
|
+
|
|
228
|
+
console.log(`Received ${allItems.length} items`);
|
|
229
|
+
|
|
230
|
+
return allItems;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### Example 1: Filter Active Items
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
const allItems = $input.all();
|
|
237
|
+
|
|
238
|
+
// Filter only active items
|
|
239
|
+
const activeItems = allItems.filter((item) => item.json.status === "active");
|
|
240
|
+
|
|
241
|
+
return activeItems;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### Example 2: Transform All Items
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
const allItems = $input.all();
|
|
248
|
+
|
|
249
|
+
// Map to new structure
|
|
250
|
+
const transformed = allItems.map((item) => ({
|
|
251
|
+
json: {
|
|
252
|
+
id: item.json.id,
|
|
253
|
+
fullName: `${item.json.firstName} ${item.json.lastName}`,
|
|
254
|
+
email: item.json.email,
|
|
255
|
+
processedAt: new Date().toISOString(),
|
|
256
|
+
},
|
|
257
|
+
}));
|
|
258
|
+
|
|
259
|
+
return transformed;
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### Example 3: Aggregate Data
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
const allItems = $input.all();
|
|
266
|
+
|
|
267
|
+
// Calculate total
|
|
268
|
+
const total = allItems.reduce((sum, item) => {
|
|
269
|
+
return sum + (item.json.amount || 0);
|
|
270
|
+
}, 0);
|
|
271
|
+
|
|
272
|
+
return [
|
|
273
|
+
{
|
|
274
|
+
json: {
|
|
275
|
+
total,
|
|
276
|
+
count: allItems.length,
|
|
277
|
+
average: total / allItems.length,
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
];
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### Example 4: Sort and Limit
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
const allItems = $input.all();
|
|
287
|
+
|
|
288
|
+
// Get top 5 by score
|
|
289
|
+
const topFive = allItems
|
|
290
|
+
.sort((a, b) => (b.json.score || 0) - (a.json.score || 0))
|
|
291
|
+
.slice(0, 5);
|
|
292
|
+
|
|
293
|
+
return topFive.map((item) => ({ json: item.json }));
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### Example 5: Group By Category
|
|
297
|
+
|
|
298
|
+
```javascript
|
|
299
|
+
const allItems = $input.all();
|
|
300
|
+
|
|
301
|
+
// Group items by category
|
|
302
|
+
const grouped = {};
|
|
303
|
+
|
|
304
|
+
for (const item of allItems) {
|
|
305
|
+
const category = item.json.category || "Uncategorized";
|
|
306
|
+
|
|
307
|
+
if (!grouped[category]) {
|
|
308
|
+
grouped[category] = [];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
grouped[category].push(item.json);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Convert to array format
|
|
315
|
+
return Object.entries(grouped).map(([category, items]) => ({
|
|
316
|
+
json: {
|
|
317
|
+
category,
|
|
318
|
+
items,
|
|
319
|
+
count: items.length,
|
|
320
|
+
},
|
|
321
|
+
}));
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### Example 6: Deduplicate by ID
|
|
325
|
+
|
|
326
|
+
```javascript
|
|
327
|
+
const allItems = $input.all();
|
|
328
|
+
|
|
329
|
+
// Remove duplicates by ID
|
|
330
|
+
const seen = new Set();
|
|
331
|
+
const unique = [];
|
|
332
|
+
|
|
333
|
+
for (const item of allItems) {
|
|
334
|
+
const id = item.json.id;
|
|
335
|
+
|
|
336
|
+
if (!seen.has(id)) {
|
|
337
|
+
seen.add(id);
|
|
338
|
+
unique.push(item);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return unique;
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Pattern 2: $input.first() - Get First Item
|
|
346
|
+
|
|
347
|
+
**Usage**: Very common for single-item operations
|
|
348
|
+
|
|
349
|
+
**When to use:**
|
|
350
|
+
|
|
351
|
+
- Previous node returns single object
|
|
352
|
+
- Working with API responses
|
|
353
|
+
- Getting initial/first data point
|
|
354
|
+
- Configuration or metadata access
|
|
355
|
+
|
|
356
|
+
#### Basic Usage
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
// Get first item from previous node
|
|
360
|
+
const firstItem = $input.first();
|
|
361
|
+
|
|
362
|
+
// Access the JSON data
|
|
363
|
+
const data = firstItem.json;
|
|
364
|
+
|
|
365
|
+
console.log("First item:", data);
|
|
366
|
+
|
|
367
|
+
return [{ json: data }];
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
#### Example 1: Process Single API Response
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
// Get API response (typically single object)
|
|
374
|
+
const response = $input.first().json;
|
|
375
|
+
|
|
376
|
+
// Extract what you need
|
|
377
|
+
return [
|
|
378
|
+
{
|
|
379
|
+
json: {
|
|
380
|
+
userId: response.data.user.id,
|
|
381
|
+
userName: response.data.user.name,
|
|
382
|
+
status: response.status,
|
|
383
|
+
fetchedAt: new Date().toISOString(),
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
];
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### Example 2: Transform Single Object
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
const data = $input.first().json;
|
|
393
|
+
|
|
394
|
+
// Transform structure
|
|
395
|
+
return [
|
|
396
|
+
{
|
|
397
|
+
json: {
|
|
398
|
+
id: data.id,
|
|
399
|
+
contact: {
|
|
400
|
+
email: data.email,
|
|
401
|
+
phone: data.phone,
|
|
402
|
+
},
|
|
403
|
+
address: {
|
|
404
|
+
street: data.street,
|
|
405
|
+
city: data.city,
|
|
406
|
+
zip: data.zip,
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
];
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### Example 3: Validate Single Item
|
|
414
|
+
|
|
415
|
+
```javascript
|
|
416
|
+
const item = $input.first().json;
|
|
417
|
+
|
|
418
|
+
// Validation logic
|
|
419
|
+
const isValid = item.email && item.email.includes("@");
|
|
420
|
+
|
|
421
|
+
return [
|
|
422
|
+
{
|
|
423
|
+
json: {
|
|
424
|
+
...item,
|
|
425
|
+
valid: isValid,
|
|
426
|
+
validatedAt: new Date().toISOString(),
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
];
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Example 4: Extract Nested Data
|
|
433
|
+
|
|
434
|
+
```javascript
|
|
435
|
+
const response = $input.first().json;
|
|
436
|
+
|
|
437
|
+
// Navigate nested structure
|
|
438
|
+
const users = response.data?.users || [];
|
|
439
|
+
|
|
440
|
+
return users.map((user) => ({
|
|
441
|
+
json: {
|
|
442
|
+
id: user.id,
|
|
443
|
+
name: user.profile?.name || "Unknown",
|
|
444
|
+
email: user.contact?.email || "no-email",
|
|
445
|
+
},
|
|
446
|
+
}));
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
#### Example 5: Combine with Other Methods
|
|
450
|
+
|
|
451
|
+
```javascript
|
|
452
|
+
// Get first item's data
|
|
453
|
+
const firstData = $input.first().json;
|
|
454
|
+
|
|
455
|
+
// Use it to filter all items
|
|
456
|
+
const allItems = $input.all();
|
|
457
|
+
const matching = allItems.filter(
|
|
458
|
+
(item) => item.json.category === firstData.targetCategory,
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
return matching;
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Pattern 3: $input.item - Current Item (Each Item Mode)
|
|
465
|
+
|
|
466
|
+
**Usage**: Common in "Run Once for Each Item" mode
|
|
467
|
+
|
|
468
|
+
**When to use:**
|
|
469
|
+
|
|
470
|
+
- Mode is set to "Run Once for Each Item"
|
|
471
|
+
- Need to process items independently
|
|
472
|
+
- Per-item API calls or validations
|
|
473
|
+
- Item-specific error handling
|
|
474
|
+
|
|
475
|
+
**IMPORTANT**: Only use in "Each Item" mode. Will be undefined in "All Items" mode.
|
|
476
|
+
|
|
477
|
+
#### Basic Usage
|
|
478
|
+
|
|
479
|
+
```javascript
|
|
480
|
+
// In "Run Once for Each Item" mode
|
|
481
|
+
const currentItem = $input.item;
|
|
482
|
+
const data = currentItem.json;
|
|
483
|
+
|
|
484
|
+
console.log("Processing item:", data.id);
|
|
485
|
+
|
|
486
|
+
return [
|
|
487
|
+
{
|
|
488
|
+
json: {
|
|
489
|
+
...data,
|
|
490
|
+
processed: true,
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
];
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
#### Example 1: Add Processing Metadata
|
|
497
|
+
|
|
498
|
+
```javascript
|
|
499
|
+
const item = $input.item;
|
|
500
|
+
|
|
501
|
+
return [
|
|
502
|
+
{
|
|
503
|
+
json: {
|
|
504
|
+
...item.json,
|
|
505
|
+
processed: true,
|
|
506
|
+
processedAt: new Date().toISOString(),
|
|
507
|
+
processingDuration: Math.random() * 1000, // Simulated duration
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
];
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
#### Example 2: Per-Item Validation
|
|
514
|
+
|
|
515
|
+
```javascript
|
|
516
|
+
const item = $input.item;
|
|
517
|
+
const data = item.json;
|
|
518
|
+
|
|
519
|
+
// Validate this specific item
|
|
520
|
+
const errors = [];
|
|
521
|
+
|
|
522
|
+
if (!data.email) errors.push("Email required");
|
|
523
|
+
if (!data.name) errors.push("Name required");
|
|
524
|
+
if (data.age && data.age < 18) errors.push("Must be 18+");
|
|
525
|
+
|
|
526
|
+
return [
|
|
527
|
+
{
|
|
528
|
+
json: {
|
|
529
|
+
...data,
|
|
530
|
+
valid: errors.length === 0,
|
|
531
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
];
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### Example 3: Item-Specific API Call
|
|
538
|
+
|
|
539
|
+
```javascript
|
|
540
|
+
const item = $input.item;
|
|
541
|
+
const userId = item.json.userId;
|
|
542
|
+
|
|
543
|
+
// Make API call specific to this item
|
|
544
|
+
const response = await $helpers.httpRequest({
|
|
545
|
+
method: "GET",
|
|
546
|
+
url: `https://api.example.com/users/${userId}/details`,
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
return [
|
|
550
|
+
{
|
|
551
|
+
json: {
|
|
552
|
+
...item.json,
|
|
553
|
+
details: response,
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
];
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
#### Example 4: Conditional Processing
|
|
560
|
+
|
|
561
|
+
```javascript
|
|
562
|
+
const item = $input.item;
|
|
563
|
+
const data = item.json;
|
|
564
|
+
|
|
565
|
+
// Process based on item type
|
|
566
|
+
if (data.type === "premium") {
|
|
567
|
+
return [
|
|
568
|
+
{
|
|
569
|
+
json: {
|
|
570
|
+
...data,
|
|
571
|
+
discount: 0.2,
|
|
572
|
+
tier: "premium",
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
];
|
|
576
|
+
} else {
|
|
577
|
+
return [
|
|
578
|
+
{
|
|
579
|
+
json: {
|
|
580
|
+
...data,
|
|
581
|
+
discount: 0.05,
|
|
582
|
+
tier: "standard",
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
];
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Pattern 4: $node - Reference Other Nodes
|
|
590
|
+
|
|
591
|
+
**Usage**: Less common, but powerful for specific scenarios
|
|
592
|
+
|
|
593
|
+
**When to use:**
|
|
594
|
+
|
|
595
|
+
- Need data from specific named node
|
|
596
|
+
- Combining data from multiple nodes
|
|
597
|
+
- Accessing metadata about workflow execution
|
|
598
|
+
|
|
599
|
+
#### Basic Usage
|
|
600
|
+
|
|
601
|
+
```javascript
|
|
602
|
+
// Get output from specific node
|
|
603
|
+
const webhookData = $node["Webhook"].json;
|
|
604
|
+
const apiData = $node["HTTP Request"].json;
|
|
605
|
+
|
|
606
|
+
return [
|
|
607
|
+
{
|
|
608
|
+
json: {
|
|
609
|
+
fromWebhook: webhookData,
|
|
610
|
+
fromAPI: apiData,
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
];
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
#### Example 1: Combine Multiple Sources
|
|
617
|
+
|
|
618
|
+
```javascript
|
|
619
|
+
// Reference multiple nodes
|
|
620
|
+
const webhook = $node["Webhook"].json;
|
|
621
|
+
const database = $node["Postgres"].json;
|
|
622
|
+
const api = $node["HTTP Request"].json;
|
|
623
|
+
|
|
624
|
+
return [
|
|
625
|
+
{
|
|
626
|
+
json: {
|
|
627
|
+
combined: {
|
|
628
|
+
webhook: webhook.body,
|
|
629
|
+
dbRecords: database.length,
|
|
630
|
+
apiResponse: api.status,
|
|
631
|
+
},
|
|
632
|
+
processedAt: new Date().toISOString(),
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
];
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
#### Example 2: Compare Across Nodes
|
|
639
|
+
|
|
640
|
+
```javascript
|
|
641
|
+
const oldData = $node["Get Old Data"].json;
|
|
642
|
+
const newData = $node["Get New Data"].json;
|
|
643
|
+
|
|
644
|
+
// Compare
|
|
645
|
+
const changes = {
|
|
646
|
+
added: newData.filter((n) => !oldData.find((o) => o.id === n.id)),
|
|
647
|
+
removed: oldData.filter((o) => !newData.find((n) => n.id === o.id)),
|
|
648
|
+
modified: newData.filter((n) => {
|
|
649
|
+
const old = oldData.find((o) => o.id === n.id);
|
|
650
|
+
return old && JSON.stringify(old) !== JSON.stringify(n);
|
|
651
|
+
}),
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
return [
|
|
655
|
+
{
|
|
656
|
+
json: {
|
|
657
|
+
changes,
|
|
658
|
+
summary: {
|
|
659
|
+
added: changes.added.length,
|
|
660
|
+
removed: changes.removed.length,
|
|
661
|
+
modified: changes.modified.length,
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
];
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
#### Example 3: Access Node Metadata
|
|
669
|
+
|
|
670
|
+
```javascript
|
|
671
|
+
// Get data from specific execution path
|
|
672
|
+
const ifTrueBranch = $node["IF True"].json;
|
|
673
|
+
const ifFalseBranch = $node["IF False"].json;
|
|
674
|
+
|
|
675
|
+
// Use whichever branch executed
|
|
676
|
+
const result = ifTrueBranch || ifFalseBranch || {};
|
|
677
|
+
|
|
678
|
+
return [{ json: result }];
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Critical: Webhook Data Structure
|
|
682
|
+
|
|
683
|
+
**MOST COMMON MISTAKE**: Forgetting webhook data is nested under `.body`
|
|
684
|
+
|
|
685
|
+
#### The Problem
|
|
686
|
+
|
|
687
|
+
Webhook node wraps all incoming data under a `body` property. This catches many developers by surprise.
|
|
688
|
+
|
|
689
|
+
#### Structure
|
|
690
|
+
|
|
691
|
+
```javascript
|
|
692
|
+
// Webhook node output structure:
|
|
693
|
+
{
|
|
694
|
+
"headers": {
|
|
695
|
+
"content-type": "application/json",
|
|
696
|
+
"user-agent": "...",
|
|
697
|
+
// ... other headers
|
|
698
|
+
},
|
|
699
|
+
"params": {},
|
|
700
|
+
"query": {},
|
|
701
|
+
"body": {
|
|
702
|
+
// YOUR DATA IS HERE
|
|
703
|
+
"name": "Alice",
|
|
704
|
+
"email": "alice@example.com",
|
|
705
|
+
"message": "Hello!"
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
#### Wrong vs Right
|
|
711
|
+
|
|
712
|
+
```javascript
|
|
713
|
+
// WRONG: Trying to access directly
|
|
714
|
+
const name = $json.name; // undefined
|
|
715
|
+
const email = $json.email; // undefined
|
|
716
|
+
|
|
717
|
+
// CORRECT: Access via .body
|
|
718
|
+
const name = $json.body.name; // "Alice"
|
|
719
|
+
const email = $json.body.email; // "alice@example.com"
|
|
720
|
+
|
|
721
|
+
// CORRECT: Extract body first
|
|
722
|
+
const webhookData = $json.body;
|
|
723
|
+
const name = webhookData.name; // "Alice"
|
|
724
|
+
const email = webhookData.email; // "alice@example.com"
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
#### Example: Full Webhook Processing
|
|
728
|
+
|
|
729
|
+
```javascript
|
|
730
|
+
// Get webhook data from previous node
|
|
731
|
+
const webhookOutput = $input.first().json;
|
|
732
|
+
|
|
733
|
+
// Access the actual payload
|
|
734
|
+
const payload = webhookOutput.body;
|
|
735
|
+
|
|
736
|
+
// Access headers if needed
|
|
737
|
+
const contentType = webhookOutput.headers["content-type"];
|
|
738
|
+
|
|
739
|
+
// Access query parameters if needed
|
|
740
|
+
const apiKey = webhookOutput.query.api_key;
|
|
741
|
+
|
|
742
|
+
// Process the actual data
|
|
743
|
+
return [
|
|
744
|
+
{
|
|
745
|
+
json: {
|
|
746
|
+
// Data from webhook body
|
|
747
|
+
userName: payload.name,
|
|
748
|
+
userEmail: payload.email,
|
|
749
|
+
message: payload.message,
|
|
750
|
+
|
|
751
|
+
// Metadata
|
|
752
|
+
receivedAt: new Date().toISOString(),
|
|
753
|
+
contentType: contentType,
|
|
754
|
+
authenticated: !!apiKey,
|
|
755
|
+
},
|
|
756
|
+
},
|
|
757
|
+
];
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
#### POST Data, Query Params, and Headers
|
|
761
|
+
|
|
762
|
+
```javascript
|
|
763
|
+
const webhook = $input.first().json;
|
|
764
|
+
|
|
765
|
+
return [
|
|
766
|
+
{
|
|
767
|
+
json: {
|
|
768
|
+
// POST body data
|
|
769
|
+
formData: webhook.body,
|
|
770
|
+
|
|
771
|
+
// Query parameters (?key=value)
|
|
772
|
+
queryParams: webhook.query,
|
|
773
|
+
|
|
774
|
+
// HTTP headers
|
|
775
|
+
userAgent: webhook.headers["user-agent"],
|
|
776
|
+
contentType: webhook.headers["content-type"],
|
|
777
|
+
|
|
778
|
+
// Request metadata
|
|
779
|
+
method: webhook.method, // POST, GET, etc.
|
|
780
|
+
url: webhook.url,
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
];
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
#### Common Webhook Scenarios
|
|
787
|
+
|
|
788
|
+
```javascript
|
|
789
|
+
// Scenario 1: Form submission
|
|
790
|
+
const formData = $json.body;
|
|
791
|
+
const name = formData.name;
|
|
792
|
+
const email = formData.email;
|
|
793
|
+
|
|
794
|
+
// Scenario 2: JSON API webhook
|
|
795
|
+
const apiPayload = $json.body;
|
|
796
|
+
const eventType = apiPayload.event;
|
|
797
|
+
const data = apiPayload.data;
|
|
798
|
+
|
|
799
|
+
// Scenario 3: Query parameters
|
|
800
|
+
const apiKey = $json.query.api_key;
|
|
801
|
+
const userId = $json.query.user_id;
|
|
802
|
+
|
|
803
|
+
// Scenario 4: Headers
|
|
804
|
+
const authorization = $json.headers["authorization"];
|
|
805
|
+
const signature = $json.headers["x-signature"];
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
### Choosing the Right Data Access Pattern
|
|
809
|
+
|
|
810
|
+
#### Decision Tree
|
|
811
|
+
|
|
812
|
+
```
|
|
813
|
+
Do you need ALL items from previous node?
|
|
814
|
+
|- YES -> Use $input.all()
|
|
815
|
+
|
|
|
816
|
+
\- NO -> Do you need just the FIRST item?
|
|
817
|
+
|- YES -> Use $input.first()
|
|
818
|
+
|
|
|
819
|
+
\- NO -> Are you in "Each Item" mode?
|
|
820
|
+
|- YES -> Use $input.item
|
|
821
|
+
|
|
|
822
|
+
\- NO -> Do you need specific node data?
|
|
823
|
+
|- YES -> Use $node["NodeName"]
|
|
824
|
+
\- NO -> Use $input.first() (default)
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
#### Quick Reference Table
|
|
828
|
+
|
|
829
|
+
| Scenario | Use This | Example |
|
|
830
|
+
| -------------------------- | ---------------- | ----------------------------------------------------- |
|
|
831
|
+
| Sum all amounts | `$input.all()` | `allItems.reduce((sum, i) => sum + i.json.amount, 0)` |
|
|
832
|
+
| Get API response | `$input.first()` | `$input.first().json.data` |
|
|
833
|
+
| Process each independently | `$input.item` | `$input.item.json` (Each Item mode) |
|
|
834
|
+
| Combine two nodes | `$node["Name"]` | `$node["API"].json` |
|
|
835
|
+
| Filter array | `$input.all()` | `allItems.filter(i => i.json.active)` |
|
|
836
|
+
| Transform single object | `$input.first()` | `{...input.first().json, new: true}` |
|
|
837
|
+
| Webhook data | `$input.first()` | `$input.first().json.body` |
|
|
838
|
+
|
|
839
|
+
### Common Data Access Mistakes
|
|
840
|
+
|
|
841
|
+
#### Mistake 1: Using $json Without Context
|
|
842
|
+
|
|
843
|
+
```javascript
|
|
844
|
+
// WRONG: $json is ambiguous
|
|
845
|
+
const value = $json.field;
|
|
846
|
+
|
|
847
|
+
// CORRECT: Be explicit
|
|
848
|
+
const value = $input.first().json.field;
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
#### Mistake 2: Forgetting .json Property
|
|
852
|
+
|
|
853
|
+
```javascript
|
|
854
|
+
// WRONG: Trying to access fields on item object
|
|
855
|
+
const items = $input.all();
|
|
856
|
+
const names = items.map((item) => item.name); // undefined
|
|
857
|
+
|
|
858
|
+
// CORRECT: Access via .json
|
|
859
|
+
const names = items.map((item) => item.json.name);
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
#### Mistake 3: Using $input.item in All Items Mode
|
|
863
|
+
|
|
864
|
+
```javascript
|
|
865
|
+
// WRONG: $input.item is undefined in "All Items" mode
|
|
866
|
+
const data = $input.item.json; // Error!
|
|
867
|
+
|
|
868
|
+
// CORRECT: Use appropriate method
|
|
869
|
+
const data = $input.first().json; // Or $input.all()
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
#### Mistake 4: Not Handling Empty Arrays
|
|
873
|
+
|
|
874
|
+
```javascript
|
|
875
|
+
// WRONG: Crashes if no items
|
|
876
|
+
const first = $input.all()[0].json;
|
|
877
|
+
|
|
878
|
+
// CORRECT: Check length first
|
|
879
|
+
const items = $input.all();
|
|
880
|
+
if (items.length === 0) {
|
|
881
|
+
return [];
|
|
882
|
+
}
|
|
883
|
+
const first = items[0].json;
|
|
884
|
+
|
|
885
|
+
// ALSO CORRECT: Use $input.first()
|
|
886
|
+
const first = $input.first().json; // Built-in safety
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
#### Mistake 5: Modifying Original Data
|
|
890
|
+
|
|
891
|
+
```javascript
|
|
892
|
+
// RISKY: Mutating original
|
|
893
|
+
const items = $input.all();
|
|
894
|
+
items[0].json.modified = true; // Modifies original
|
|
895
|
+
return items;
|
|
896
|
+
|
|
897
|
+
// SAFE: Create new objects
|
|
898
|
+
const items = $input.all();
|
|
899
|
+
return items.map((item) => ({
|
|
900
|
+
json: {
|
|
901
|
+
...item.json,
|
|
902
|
+
modified: true,
|
|
903
|
+
},
|
|
904
|
+
}));
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
### Advanced Data Access Patterns
|
|
908
|
+
|
|
909
|
+
#### Pattern: Pagination Handling
|
|
910
|
+
|
|
911
|
+
```javascript
|
|
912
|
+
const currentPage = $input.all();
|
|
913
|
+
const pageNumber = $node["Set Page"].json.page || 1;
|
|
914
|
+
|
|
915
|
+
// Combine with previous pages
|
|
916
|
+
const allPreviousPages = $node["Accumulator"]?.json.accumulated || [];
|
|
917
|
+
|
|
918
|
+
return [
|
|
919
|
+
{
|
|
920
|
+
json: {
|
|
921
|
+
accumulated: [...allPreviousPages, ...currentPage],
|
|
922
|
+
currentPage: pageNumber,
|
|
923
|
+
totalItems: allPreviousPages.length + currentPage.length,
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
];
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
#### Pattern: Conditional Node Reference
|
|
930
|
+
|
|
931
|
+
```javascript
|
|
932
|
+
// Access different nodes based on condition
|
|
933
|
+
const condition = $input.first().json.type;
|
|
934
|
+
|
|
935
|
+
let data;
|
|
936
|
+
if (condition === "api") {
|
|
937
|
+
data = $node["API Response"].json;
|
|
938
|
+
} else if (condition === "database") {
|
|
939
|
+
data = $node["Database"].json;
|
|
940
|
+
} else {
|
|
941
|
+
data = $node["Default"].json;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
return [{ json: data }];
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
#### Pattern: Multi-Node Aggregation
|
|
948
|
+
|
|
949
|
+
```javascript
|
|
950
|
+
// Collect data from multiple named nodes
|
|
951
|
+
const sources = ["Source1", "Source2", "Source3"];
|
|
952
|
+
const allData = [];
|
|
953
|
+
|
|
954
|
+
for (const source of sources) {
|
|
955
|
+
const nodeData = $node[source]?.json;
|
|
956
|
+
if (nodeData) {
|
|
957
|
+
allData.push({
|
|
958
|
+
source,
|
|
959
|
+
data: nodeData,
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
return allData.map((item) => ({ json: item }));
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
---
|
|
968
|
+
|
|
969
|
+
## Built-in Functions & Helpers
|
|
970
|
+
|
|
971
|
+
n8n Code nodes provide powerful built-in functions beyond standard JavaScript:
|
|
972
|
+
|
|
973
|
+
1. **$helpers.httpRequest()** - Make HTTP requests
|
|
974
|
+
2. **DateTime (Luxon)** - Advanced date/time operations
|
|
975
|
+
3. **$jmespath()** - Query JSON structures
|
|
976
|
+
4. **$getWorkflowStaticData()** - Persistent storage
|
|
977
|
+
5. **Standard JavaScript Globals** - Math, JSON, console, etc.
|
|
978
|
+
6. **Available Node.js Modules** - crypto, Buffer, URL
|
|
979
|
+
|
|
980
|
+
### 1. $helpers.httpRequest() - HTTP Requests
|
|
981
|
+
|
|
982
|
+
Make HTTP requests directly from Code nodes without using HTTP Request node.
|
|
983
|
+
|
|
984
|
+
#### Basic Usage
|
|
985
|
+
|
|
986
|
+
```javascript
|
|
987
|
+
const response = await $helpers.httpRequest({
|
|
988
|
+
method: "GET",
|
|
989
|
+
url: "https://api.example.com/users",
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
return [{ json: { data: response } }];
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
#### Complete Options
|
|
996
|
+
|
|
997
|
+
```javascript
|
|
998
|
+
const response = await $helpers.httpRequest({
|
|
999
|
+
method: "POST", // GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
|
|
1000
|
+
url: "https://api.example.com/users",
|
|
1001
|
+
headers: {
|
|
1002
|
+
Authorization: "Bearer token123",
|
|
1003
|
+
"Content-Type": "application/json",
|
|
1004
|
+
"User-Agent": "n8n-workflow",
|
|
1005
|
+
},
|
|
1006
|
+
body: {
|
|
1007
|
+
name: "John Doe",
|
|
1008
|
+
email: "john@example.com",
|
|
1009
|
+
},
|
|
1010
|
+
qs: {
|
|
1011
|
+
// Query string parameters
|
|
1012
|
+
page: 1,
|
|
1013
|
+
limit: 10,
|
|
1014
|
+
},
|
|
1015
|
+
timeout: 10000, // Milliseconds (default: no timeout)
|
|
1016
|
+
json: true, // Auto-parse JSON response (default: true)
|
|
1017
|
+
simple: false, // Don't throw on HTTP errors (default: true)
|
|
1018
|
+
resolveWithFullResponse: false, // Return only body (default: false)
|
|
1019
|
+
});
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
#### GET Request
|
|
1023
|
+
|
|
1024
|
+
```javascript
|
|
1025
|
+
// Simple GET
|
|
1026
|
+
const users = await $helpers.httpRequest({
|
|
1027
|
+
method: "GET",
|
|
1028
|
+
url: "https://api.example.com/users",
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
return [{ json: { users } }];
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
```javascript
|
|
1035
|
+
// GET with query parameters
|
|
1036
|
+
const results = await $helpers.httpRequest({
|
|
1037
|
+
method: "GET",
|
|
1038
|
+
url: "https://api.example.com/search",
|
|
1039
|
+
qs: {
|
|
1040
|
+
q: "javascript",
|
|
1041
|
+
page: 1,
|
|
1042
|
+
per_page: 50,
|
|
1043
|
+
},
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
return [{ json: results }];
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
#### POST Request
|
|
1050
|
+
|
|
1051
|
+
```javascript
|
|
1052
|
+
// POST with JSON body
|
|
1053
|
+
const newUser = await $helpers.httpRequest({
|
|
1054
|
+
method: "POST",
|
|
1055
|
+
url: "https://api.example.com/users",
|
|
1056
|
+
headers: {
|
|
1057
|
+
"Content-Type": "application/json",
|
|
1058
|
+
Authorization: "Bearer " + $env.API_KEY,
|
|
1059
|
+
},
|
|
1060
|
+
body: {
|
|
1061
|
+
name: $json.body.name,
|
|
1062
|
+
email: $json.body.email,
|
|
1063
|
+
role: "user",
|
|
1064
|
+
},
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
return [{ json: newUser }];
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
#### PUT/PATCH Request
|
|
1071
|
+
|
|
1072
|
+
```javascript
|
|
1073
|
+
// Update resource
|
|
1074
|
+
const updated = await $helpers.httpRequest({
|
|
1075
|
+
method: "PATCH",
|
|
1076
|
+
url: `https://api.example.com/users/${userId}`,
|
|
1077
|
+
body: {
|
|
1078
|
+
name: "Updated Name",
|
|
1079
|
+
status: "active",
|
|
1080
|
+
},
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
return [{ json: updated }];
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
#### DELETE Request
|
|
1087
|
+
|
|
1088
|
+
```javascript
|
|
1089
|
+
// Delete resource
|
|
1090
|
+
await $helpers.httpRequest({
|
|
1091
|
+
method: "DELETE",
|
|
1092
|
+
url: `https://api.example.com/users/${userId}`,
|
|
1093
|
+
headers: {
|
|
1094
|
+
Authorization: "Bearer " + $env.API_KEY,
|
|
1095
|
+
},
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
return [{ json: { deleted: true, userId } }];
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
#### Authentication Patterns
|
|
1102
|
+
|
|
1103
|
+
```javascript
|
|
1104
|
+
// Bearer Token
|
|
1105
|
+
const response = await $helpers.httpRequest({
|
|
1106
|
+
url: "https://api.example.com/data",
|
|
1107
|
+
headers: {
|
|
1108
|
+
Authorization: `Bearer ${$env.API_TOKEN}`,
|
|
1109
|
+
},
|
|
1110
|
+
});
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
```javascript
|
|
1114
|
+
// API Key in Header
|
|
1115
|
+
const response = await $helpers.httpRequest({
|
|
1116
|
+
url: "https://api.example.com/data",
|
|
1117
|
+
headers: {
|
|
1118
|
+
"X-API-Key": $env.API_KEY,
|
|
1119
|
+
},
|
|
1120
|
+
});
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
```javascript
|
|
1124
|
+
// Basic Auth (manual)
|
|
1125
|
+
const credentials = Buffer.from(`${username}:${password}`).toString("base64");
|
|
1126
|
+
|
|
1127
|
+
const response = await $helpers.httpRequest({
|
|
1128
|
+
url: "https://api.example.com/data",
|
|
1129
|
+
headers: {
|
|
1130
|
+
Authorization: `Basic ${credentials}`,
|
|
1131
|
+
},
|
|
1132
|
+
});
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
#### Error Handling
|
|
1136
|
+
|
|
1137
|
+
```javascript
|
|
1138
|
+
// Handle HTTP errors gracefully
|
|
1139
|
+
try {
|
|
1140
|
+
const response = await $helpers.httpRequest({
|
|
1141
|
+
method: "GET",
|
|
1142
|
+
url: "https://api.example.com/users",
|
|
1143
|
+
simple: false, // Don't throw on 4xx/5xx
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
1147
|
+
return [{ json: { success: true, data: response.body } }];
|
|
1148
|
+
} else {
|
|
1149
|
+
return [
|
|
1150
|
+
{
|
|
1151
|
+
json: {
|
|
1152
|
+
success: false,
|
|
1153
|
+
status: response.statusCode,
|
|
1154
|
+
error: response.body,
|
|
1155
|
+
},
|
|
1156
|
+
},
|
|
1157
|
+
];
|
|
1158
|
+
}
|
|
1159
|
+
} catch (error) {
|
|
1160
|
+
return [
|
|
1161
|
+
{
|
|
1162
|
+
json: {
|
|
1163
|
+
success: false,
|
|
1164
|
+
error: error.message,
|
|
1165
|
+
},
|
|
1166
|
+
},
|
|
1167
|
+
];
|
|
1168
|
+
}
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
#### Full Response Access
|
|
1172
|
+
|
|
1173
|
+
```javascript
|
|
1174
|
+
// Get full response including headers and status
|
|
1175
|
+
const response = await $helpers.httpRequest({
|
|
1176
|
+
url: "https://api.example.com/data",
|
|
1177
|
+
resolveWithFullResponse: true,
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
return [
|
|
1181
|
+
{
|
|
1182
|
+
json: {
|
|
1183
|
+
statusCode: response.statusCode,
|
|
1184
|
+
headers: response.headers,
|
|
1185
|
+
body: response.body,
|
|
1186
|
+
rateLimit: response.headers["x-ratelimit-remaining"],
|
|
1187
|
+
},
|
|
1188
|
+
},
|
|
1189
|
+
];
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
### 2. DateTime (Luxon) - Date & Time Operations
|
|
1193
|
+
|
|
1194
|
+
n8n includes Luxon for powerful date/time handling. Access via `DateTime` global.
|
|
1195
|
+
|
|
1196
|
+
#### Current Date/Time
|
|
1197
|
+
|
|
1198
|
+
```javascript
|
|
1199
|
+
// Current time
|
|
1200
|
+
const now = DateTime.now();
|
|
1201
|
+
|
|
1202
|
+
// Current time in specific timezone
|
|
1203
|
+
const nowTokyo = DateTime.now().setZone("Asia/Tokyo");
|
|
1204
|
+
|
|
1205
|
+
// Today at midnight
|
|
1206
|
+
const today = DateTime.now().startOf("day");
|
|
1207
|
+
|
|
1208
|
+
return [
|
|
1209
|
+
{
|
|
1210
|
+
json: {
|
|
1211
|
+
iso: now.toISO(), // "2025-01-20T15:30:00.000Z"
|
|
1212
|
+
formatted: now.toFormat("yyyy-MM-dd HH:mm:ss"), // "2025-01-20 15:30:00"
|
|
1213
|
+
unix: now.toSeconds(), // Unix timestamp
|
|
1214
|
+
millis: now.toMillis(), // Milliseconds since epoch
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
];
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
#### Formatting Dates
|
|
1221
|
+
|
|
1222
|
+
```javascript
|
|
1223
|
+
const now = DateTime.now();
|
|
1224
|
+
|
|
1225
|
+
return [
|
|
1226
|
+
{
|
|
1227
|
+
json: {
|
|
1228
|
+
isoFormat: now.toISO(), // ISO 8601: "2025-01-20T15:30:00.000Z"
|
|
1229
|
+
sqlFormat: now.toSQL(), // SQL: "2025-01-20 15:30:00.000"
|
|
1230
|
+
httpFormat: now.toHTTP(), // HTTP: "Mon, 20 Jan 2025 15:30:00 GMT"
|
|
1231
|
+
|
|
1232
|
+
// Custom formats
|
|
1233
|
+
dateOnly: now.toFormat("yyyy-MM-dd"), // "2025-01-20"
|
|
1234
|
+
timeOnly: now.toFormat("HH:mm:ss"), // "15:30:00"
|
|
1235
|
+
readable: now.toFormat("MMMM dd, yyyy"), // "January 20, 2025"
|
|
1236
|
+
compact: now.toFormat("yyyyMMdd"), // "20250120"
|
|
1237
|
+
withDay: now.toFormat("EEEE, MMMM dd, yyyy"), // "Monday, January 20, 2025"
|
|
1238
|
+
custom: now.toFormat("dd/MM/yy HH:mm"), // "20/01/25 15:30"
|
|
1239
|
+
},
|
|
1240
|
+
},
|
|
1241
|
+
];
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
#### Parsing Dates
|
|
1245
|
+
|
|
1246
|
+
```javascript
|
|
1247
|
+
// From ISO string
|
|
1248
|
+
const dt1 = DateTime.fromISO("2025-01-20T15:30:00");
|
|
1249
|
+
|
|
1250
|
+
// From specific format
|
|
1251
|
+
const dt2 = DateTime.fromFormat("01/20/2025", "MM/dd/yyyy");
|
|
1252
|
+
|
|
1253
|
+
// From SQL
|
|
1254
|
+
const dt3 = DateTime.fromSQL("2025-01-20 15:30:00");
|
|
1255
|
+
|
|
1256
|
+
// From Unix timestamp
|
|
1257
|
+
const dt4 = DateTime.fromSeconds(1737384600);
|
|
1258
|
+
|
|
1259
|
+
// From milliseconds
|
|
1260
|
+
const dt5 = DateTime.fromMillis(1737384600000);
|
|
1261
|
+
|
|
1262
|
+
return [{ json: { parsed: dt1.toISO() } }];
|
|
1263
|
+
```
|
|
1264
|
+
|
|
1265
|
+
#### Date Arithmetic
|
|
1266
|
+
|
|
1267
|
+
```javascript
|
|
1268
|
+
const now = DateTime.now();
|
|
1269
|
+
|
|
1270
|
+
return [
|
|
1271
|
+
{
|
|
1272
|
+
json: {
|
|
1273
|
+
// Adding time
|
|
1274
|
+
tomorrow: now.plus({ days: 1 }).toISO(),
|
|
1275
|
+
nextWeek: now.plus({ weeks: 1 }).toISO(),
|
|
1276
|
+
nextMonth: now.plus({ months: 1 }).toISO(),
|
|
1277
|
+
inTwoHours: now.plus({ hours: 2 }).toISO(),
|
|
1278
|
+
|
|
1279
|
+
// Subtracting time
|
|
1280
|
+
yesterday: now.minus({ days: 1 }).toISO(),
|
|
1281
|
+
lastWeek: now.minus({ weeks: 1 }).toISO(),
|
|
1282
|
+
lastMonth: now.minus({ months: 1 }).toISO(),
|
|
1283
|
+
twoHoursAgo: now.minus({ hours: 2 }).toISO(),
|
|
1284
|
+
|
|
1285
|
+
// Complex operations
|
|
1286
|
+
in90Days: now.plus({ days: 90 }).toFormat("yyyy-MM-dd"),
|
|
1287
|
+
in6Months: now.plus({ months: 6 }).toFormat("yyyy-MM-dd"),
|
|
1288
|
+
},
|
|
1289
|
+
},
|
|
1290
|
+
];
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
#### Time Comparisons
|
|
1294
|
+
|
|
1295
|
+
```javascript
|
|
1296
|
+
const now = DateTime.now();
|
|
1297
|
+
const targetDate = DateTime.fromISO("2025-12-31");
|
|
1298
|
+
|
|
1299
|
+
return [
|
|
1300
|
+
{
|
|
1301
|
+
json: {
|
|
1302
|
+
// Comparisons
|
|
1303
|
+
isFuture: targetDate > now,
|
|
1304
|
+
isPast: targetDate < now,
|
|
1305
|
+
isEqual: targetDate.equals(now),
|
|
1306
|
+
|
|
1307
|
+
// Differences
|
|
1308
|
+
daysUntil: targetDate.diff(now, "days").days,
|
|
1309
|
+
hoursUntil: targetDate.diff(now, "hours").hours,
|
|
1310
|
+
monthsUntil: targetDate.diff(now, "months").months,
|
|
1311
|
+
|
|
1312
|
+
// Detailed difference
|
|
1313
|
+
detailedDiff: targetDate
|
|
1314
|
+
.diff(now, ["months", "days", "hours"])
|
|
1315
|
+
.toObject(),
|
|
1316
|
+
},
|
|
1317
|
+
},
|
|
1318
|
+
];
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
#### Timezone Operations
|
|
1322
|
+
|
|
1323
|
+
```javascript
|
|
1324
|
+
const now = DateTime.now();
|
|
1325
|
+
|
|
1326
|
+
return [
|
|
1327
|
+
{
|
|
1328
|
+
json: {
|
|
1329
|
+
// Current timezone
|
|
1330
|
+
local: now.toISO(),
|
|
1331
|
+
|
|
1332
|
+
// Convert to different timezone
|
|
1333
|
+
tokyo: now.setZone("Asia/Tokyo").toISO(),
|
|
1334
|
+
newYork: now.setZone("America/New_York").toISO(),
|
|
1335
|
+
london: now.setZone("Europe/London").toISO(),
|
|
1336
|
+
utc: now.toUTC().toISO(),
|
|
1337
|
+
|
|
1338
|
+
// Get timezone info
|
|
1339
|
+
timezone: now.zoneName, // "America/Los_Angeles"
|
|
1340
|
+
offset: now.offset, // Offset in minutes
|
|
1341
|
+
offsetFormatted: now.toFormat("ZZ"), // "+08:00"
|
|
1342
|
+
},
|
|
1343
|
+
},
|
|
1344
|
+
];
|
|
1345
|
+
```
|
|
1346
|
+
|
|
1347
|
+
#### Start/End of Period
|
|
1348
|
+
|
|
1349
|
+
```javascript
|
|
1350
|
+
const now = DateTime.now();
|
|
1351
|
+
|
|
1352
|
+
return [
|
|
1353
|
+
{
|
|
1354
|
+
json: {
|
|
1355
|
+
startOfDay: now.startOf("day").toISO(),
|
|
1356
|
+
endOfDay: now.endOf("day").toISO(),
|
|
1357
|
+
startOfWeek: now.startOf("week").toISO(),
|
|
1358
|
+
endOfWeek: now.endOf("week").toISO(),
|
|
1359
|
+
startOfMonth: now.startOf("month").toISO(),
|
|
1360
|
+
endOfMonth: now.endOf("month").toISO(),
|
|
1361
|
+
startOfYear: now.startOf("year").toISO(),
|
|
1362
|
+
endOfYear: now.endOf("year").toISO(),
|
|
1363
|
+
},
|
|
1364
|
+
},
|
|
1365
|
+
];
|
|
1366
|
+
```
|
|
1367
|
+
|
|
1368
|
+
#### Weekday & Month Info
|
|
1369
|
+
|
|
1370
|
+
```javascript
|
|
1371
|
+
const now = DateTime.now();
|
|
1372
|
+
|
|
1373
|
+
return [
|
|
1374
|
+
{
|
|
1375
|
+
json: {
|
|
1376
|
+
// Day info
|
|
1377
|
+
weekday: now.weekday, // 1 = Monday, 7 = Sunday
|
|
1378
|
+
weekdayShort: now.weekdayShort, // "Mon"
|
|
1379
|
+
weekdayLong: now.weekdayLong, // "Monday"
|
|
1380
|
+
isWeekend: now.weekday > 5, // Saturday or Sunday
|
|
1381
|
+
|
|
1382
|
+
// Month info
|
|
1383
|
+
month: now.month, // 1-12
|
|
1384
|
+
monthShort: now.monthShort, // "Jan"
|
|
1385
|
+
monthLong: now.monthLong, // "January"
|
|
1386
|
+
|
|
1387
|
+
// Year info
|
|
1388
|
+
year: now.year, // 2025
|
|
1389
|
+
quarter: now.quarter, // 1-4
|
|
1390
|
+
daysInMonth: now.daysInMonth, // 28-31
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1393
|
+
];
|
|
1394
|
+
```
|
|
1395
|
+
|
|
1396
|
+
### 3. $jmespath() - JSON Querying
|
|
1397
|
+
|
|
1398
|
+
Query and transform JSON structures using JMESPath syntax.
|
|
1399
|
+
|
|
1400
|
+
#### Basic Queries
|
|
1401
|
+
|
|
1402
|
+
```javascript
|
|
1403
|
+
const data = $input.first().json;
|
|
1404
|
+
|
|
1405
|
+
// Extract specific field
|
|
1406
|
+
const names = $jmespath(data, "users[*].name");
|
|
1407
|
+
|
|
1408
|
+
// Filter array
|
|
1409
|
+
const adults = $jmespath(data, "users[?age >= `18`]");
|
|
1410
|
+
|
|
1411
|
+
// Get specific index
|
|
1412
|
+
const firstUser = $jmespath(data, "users[0]");
|
|
1413
|
+
|
|
1414
|
+
return [{ json: { names, adults, firstUser } }];
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
#### Advanced Queries
|
|
1418
|
+
|
|
1419
|
+
```javascript
|
|
1420
|
+
const data = $input.first().json;
|
|
1421
|
+
|
|
1422
|
+
// Sort and slice
|
|
1423
|
+
const top5 = $jmespath(data, "users | sort_by(@, &score) | reverse(@) | [0:5]");
|
|
1424
|
+
|
|
1425
|
+
// Extract nested fields
|
|
1426
|
+
const emails = $jmespath(data, "users[*].contact.email");
|
|
1427
|
+
|
|
1428
|
+
// Multi-field extraction
|
|
1429
|
+
const simplified = $jmespath(
|
|
1430
|
+
data,
|
|
1431
|
+
"users[*].{name: name, email: contact.email}",
|
|
1432
|
+
);
|
|
1433
|
+
|
|
1434
|
+
// Conditional filtering
|
|
1435
|
+
const premium = $jmespath(data, "users[?subscription.tier == `premium`]");
|
|
1436
|
+
|
|
1437
|
+
return [{ json: { top5, emails, simplified, premium } }];
|
|
1438
|
+
```
|
|
1439
|
+
|
|
1440
|
+
#### Common JMESPath Patterns
|
|
1441
|
+
|
|
1442
|
+
```javascript
|
|
1443
|
+
// Pattern 1: Filter and project
|
|
1444
|
+
const query1 = $jmespath(
|
|
1445
|
+
data,
|
|
1446
|
+
"products[?price > `100`].{name: name, price: price}",
|
|
1447
|
+
);
|
|
1448
|
+
|
|
1449
|
+
// Pattern 2: Aggregate functions
|
|
1450
|
+
const query2 = $jmespath(data, "sum(products[*].price)");
|
|
1451
|
+
const query3 = $jmespath(data, "max(products[*].price)");
|
|
1452
|
+
const query4 = $jmespath(data, "length(products)");
|
|
1453
|
+
|
|
1454
|
+
// Pattern 3: Nested filtering
|
|
1455
|
+
const query5 = $jmespath(data, "categories[*].products[?inStock == `true`]");
|
|
1456
|
+
|
|
1457
|
+
return [{ json: { query1, query2, query3, query4, query5 } }];
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
### 4. $getWorkflowStaticData() - Persistent Storage
|
|
1461
|
+
|
|
1462
|
+
Store data that persists across workflow executions.
|
|
1463
|
+
|
|
1464
|
+
#### Basic Usage
|
|
1465
|
+
|
|
1466
|
+
```javascript
|
|
1467
|
+
// Get static data storage
|
|
1468
|
+
const staticData = $getWorkflowStaticData();
|
|
1469
|
+
|
|
1470
|
+
// Initialize counter if doesn't exist
|
|
1471
|
+
if (!staticData.counter) {
|
|
1472
|
+
staticData.counter = 0;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// Increment counter
|
|
1476
|
+
staticData.counter++;
|
|
1477
|
+
|
|
1478
|
+
return [
|
|
1479
|
+
{
|
|
1480
|
+
json: {
|
|
1481
|
+
executionCount: staticData.counter,
|
|
1482
|
+
},
|
|
1483
|
+
},
|
|
1484
|
+
];
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
#### Use Cases
|
|
1488
|
+
|
|
1489
|
+
```javascript
|
|
1490
|
+
// Use Case 1: Rate limiting
|
|
1491
|
+
const staticData = $getWorkflowStaticData();
|
|
1492
|
+
const now = Date.now();
|
|
1493
|
+
|
|
1494
|
+
if (!staticData.lastRun) {
|
|
1495
|
+
staticData.lastRun = now;
|
|
1496
|
+
staticData.runCount = 1;
|
|
1497
|
+
} else {
|
|
1498
|
+
const timeSinceLastRun = now - staticData.lastRun;
|
|
1499
|
+
|
|
1500
|
+
if (timeSinceLastRun < 60000) {
|
|
1501
|
+
// Less than 1 minute
|
|
1502
|
+
return [{ json: { error: "Rate limit: wait 1 minute between runs" } }];
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
staticData.lastRun = now;
|
|
1506
|
+
staticData.runCount++;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
return [{ json: { allowed: true, totalRuns: staticData.runCount } }];
|
|
1510
|
+
```
|
|
1511
|
+
|
|
1512
|
+
```javascript
|
|
1513
|
+
// Use Case 2: Tracking last processed ID
|
|
1514
|
+
const staticData = $getWorkflowStaticData();
|
|
1515
|
+
const currentItems = $input.all();
|
|
1516
|
+
|
|
1517
|
+
// Get last processed ID
|
|
1518
|
+
const lastId = staticData.lastProcessedId || 0;
|
|
1519
|
+
|
|
1520
|
+
// Filter only new items
|
|
1521
|
+
const newItems = currentItems.filter((item) => item.json.id > lastId);
|
|
1522
|
+
|
|
1523
|
+
// Update last processed ID
|
|
1524
|
+
if (newItems.length > 0) {
|
|
1525
|
+
staticData.lastProcessedId = Math.max(
|
|
1526
|
+
...newItems.map((item) => item.json.id),
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
return newItems;
|
|
1531
|
+
```
|
|
1532
|
+
|
|
1533
|
+
```javascript
|
|
1534
|
+
// Use Case 3: Accumulating results
|
|
1535
|
+
const staticData = $getWorkflowStaticData();
|
|
1536
|
+
|
|
1537
|
+
if (!staticData.accumulated) {
|
|
1538
|
+
staticData.accumulated = [];
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// Add current items to accumulated list
|
|
1542
|
+
const currentData = $input.all().map((item) => item.json);
|
|
1543
|
+
staticData.accumulated.push(...currentData);
|
|
1544
|
+
|
|
1545
|
+
return [
|
|
1546
|
+
{
|
|
1547
|
+
json: {
|
|
1548
|
+
currentBatch: currentData.length,
|
|
1549
|
+
totalAccumulated: staticData.accumulated.length,
|
|
1550
|
+
allData: staticData.accumulated,
|
|
1551
|
+
},
|
|
1552
|
+
},
|
|
1553
|
+
];
|
|
1554
|
+
```
|
|
1555
|
+
|
|
1556
|
+
### 5. Standard JavaScript Globals
|
|
1557
|
+
|
|
1558
|
+
#### Math Object
|
|
1559
|
+
|
|
1560
|
+
```javascript
|
|
1561
|
+
return [
|
|
1562
|
+
{
|
|
1563
|
+
json: {
|
|
1564
|
+
// Rounding
|
|
1565
|
+
rounded: Math.round(3.7), // 4
|
|
1566
|
+
floor: Math.floor(3.7), // 3
|
|
1567
|
+
ceil: Math.ceil(3.2), // 4
|
|
1568
|
+
|
|
1569
|
+
// Min/Max
|
|
1570
|
+
max: Math.max(1, 5, 3, 9, 2), // 9
|
|
1571
|
+
min: Math.min(1, 5, 3, 9, 2), // 1
|
|
1572
|
+
|
|
1573
|
+
// Random
|
|
1574
|
+
random: Math.random(), // 0-1
|
|
1575
|
+
randomInt: Math.floor(Math.random() * 100), // 0-99
|
|
1576
|
+
|
|
1577
|
+
// Other
|
|
1578
|
+
abs: Math.abs(-5), // 5
|
|
1579
|
+
sqrt: Math.sqrt(16), // 4
|
|
1580
|
+
pow: Math.pow(2, 3), // 8
|
|
1581
|
+
},
|
|
1582
|
+
},
|
|
1583
|
+
];
|
|
1584
|
+
```
|
|
1585
|
+
|
|
1586
|
+
#### JSON Object
|
|
1587
|
+
|
|
1588
|
+
```javascript
|
|
1589
|
+
// Parse JSON string
|
|
1590
|
+
const jsonString = '{"name": "John", "age": 30}';
|
|
1591
|
+
const parsed = JSON.parse(jsonString);
|
|
1592
|
+
|
|
1593
|
+
// Stringify object
|
|
1594
|
+
const obj = { name: "John", age: 30 };
|
|
1595
|
+
const stringified = JSON.stringify(obj);
|
|
1596
|
+
|
|
1597
|
+
// Pretty print
|
|
1598
|
+
const pretty = JSON.stringify(obj, null, 2);
|
|
1599
|
+
|
|
1600
|
+
return [{ json: { parsed, stringified, pretty } }];
|
|
1601
|
+
```
|
|
1602
|
+
|
|
1603
|
+
#### console Object
|
|
1604
|
+
|
|
1605
|
+
```javascript
|
|
1606
|
+
// Debug logging (appears in browser console, press F12)
|
|
1607
|
+
console.log("Processing items:", $input.all().length);
|
|
1608
|
+
console.log("First item:", $input.first().json);
|
|
1609
|
+
|
|
1610
|
+
// Other console methods
|
|
1611
|
+
console.error("Error message");
|
|
1612
|
+
console.warn("Warning message");
|
|
1613
|
+
console.info("Info message");
|
|
1614
|
+
|
|
1615
|
+
// Continues to return data
|
|
1616
|
+
return [{ json: { processed: true } }];
|
|
1617
|
+
```
|
|
1618
|
+
|
|
1619
|
+
#### Object Methods
|
|
1620
|
+
|
|
1621
|
+
```javascript
|
|
1622
|
+
const obj = { name: "John", age: 30, city: "NYC" };
|
|
1623
|
+
|
|
1624
|
+
return [
|
|
1625
|
+
{
|
|
1626
|
+
json: {
|
|
1627
|
+
keys: Object.keys(obj), // ["name", "age", "city"]
|
|
1628
|
+
values: Object.values(obj), // ["John", 30, "NYC"]
|
|
1629
|
+
entries: Object.entries(obj), // [["name", "John"], ...]
|
|
1630
|
+
|
|
1631
|
+
// Check property
|
|
1632
|
+
hasName: "name" in obj, // true
|
|
1633
|
+
|
|
1634
|
+
// Merge objects
|
|
1635
|
+
merged: Object.assign({}, obj, { country: "USA" }),
|
|
1636
|
+
},
|
|
1637
|
+
},
|
|
1638
|
+
];
|
|
1639
|
+
```
|
|
1640
|
+
|
|
1641
|
+
#### Array Methods
|
|
1642
|
+
|
|
1643
|
+
```javascript
|
|
1644
|
+
const arr = [1, 2, 3, 4, 5];
|
|
1645
|
+
|
|
1646
|
+
return [
|
|
1647
|
+
{
|
|
1648
|
+
json: {
|
|
1649
|
+
mapped: arr.map((x) => x * 2), // [2, 4, 6, 8, 10]
|
|
1650
|
+
filtered: arr.filter((x) => x > 2), // [3, 4, 5]
|
|
1651
|
+
reduced: arr.reduce((sum, x) => sum + x, 0), // 15
|
|
1652
|
+
some: arr.some((x) => x > 3), // true
|
|
1653
|
+
every: arr.every((x) => x > 0), // true
|
|
1654
|
+
find: arr.find((x) => x > 3), // 4
|
|
1655
|
+
includes: arr.includes(3), // true
|
|
1656
|
+
joined: arr.join(", "), // "1, 2, 3, 4, 5"
|
|
1657
|
+
},
|
|
1658
|
+
},
|
|
1659
|
+
];
|
|
1660
|
+
```
|
|
1661
|
+
|
|
1662
|
+
### 6. Available Node.js Modules
|
|
1663
|
+
|
|
1664
|
+
#### crypto Module
|
|
1665
|
+
|
|
1666
|
+
```javascript
|
|
1667
|
+
const crypto = require("crypto");
|
|
1668
|
+
|
|
1669
|
+
// Hash functions
|
|
1670
|
+
const hash = crypto.createHash("sha256").update("my secret text").digest("hex");
|
|
1671
|
+
|
|
1672
|
+
// MD5 hash
|
|
1673
|
+
const md5 = crypto.createHash("md5").update("my text").digest("hex");
|
|
1674
|
+
|
|
1675
|
+
// Random values
|
|
1676
|
+
const randomBytes = crypto.randomBytes(16).toString("hex");
|
|
1677
|
+
|
|
1678
|
+
return [{ json: { hash, md5, randomBytes } }];
|
|
1679
|
+
```
|
|
1680
|
+
|
|
1681
|
+
#### Buffer (built-in)
|
|
1682
|
+
|
|
1683
|
+
```javascript
|
|
1684
|
+
// Base64 encoding
|
|
1685
|
+
const encoded = Buffer.from("Hello World").toString("base64");
|
|
1686
|
+
|
|
1687
|
+
// Base64 decoding
|
|
1688
|
+
const decoded = Buffer.from(encoded, "base64").toString();
|
|
1689
|
+
|
|
1690
|
+
// Hex encoding
|
|
1691
|
+
const hex = Buffer.from("Hello").toString("hex");
|
|
1692
|
+
|
|
1693
|
+
return [{ json: { encoded, decoded, hex } }];
|
|
1694
|
+
```
|
|
1695
|
+
|
|
1696
|
+
#### URL / URLSearchParams
|
|
1697
|
+
|
|
1698
|
+
```javascript
|
|
1699
|
+
// Parse URL
|
|
1700
|
+
const url = new URL("https://example.com/path?param1=value1¶m2=value2");
|
|
1701
|
+
|
|
1702
|
+
// Build query string
|
|
1703
|
+
const params = new URLSearchParams({
|
|
1704
|
+
search: "query",
|
|
1705
|
+
page: 1,
|
|
1706
|
+
limit: 10,
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
return [
|
|
1710
|
+
{
|
|
1711
|
+
json: {
|
|
1712
|
+
host: url.host,
|
|
1713
|
+
pathname: url.pathname,
|
|
1714
|
+
search: url.search,
|
|
1715
|
+
queryString: params.toString(), // "search=query&page=1&limit=10"
|
|
1716
|
+
},
|
|
1717
|
+
},
|
|
1718
|
+
];
|
|
1719
|
+
```
|
|
1720
|
+
|
|
1721
|
+
### What's NOT Available
|
|
1722
|
+
|
|
1723
|
+
**External npm packages are NOT available:**
|
|
1724
|
+
|
|
1725
|
+
- axios
|
|
1726
|
+
- lodash
|
|
1727
|
+
- moment (use DateTime/Luxon instead)
|
|
1728
|
+
- request
|
|
1729
|
+
- Any other npm package
|
|
1730
|
+
|
|
1731
|
+
**Workaround**: Use $helpers.httpRequest() for HTTP, or add data to workflow via HTTP Request node.
|
|
1732
|
+
|
|
1733
|
+
---
|
|
1734
|
+
|
|
1735
|
+
## 10 Common Patterns
|
|
1736
|
+
|
|
1737
|
+
Production-tested patterns for n8n Code nodes. Each pattern includes use case, key techniques, complete example, and variations.
|
|
1738
|
+
|
|
1739
|
+
**Pattern Categories:**
|
|
1740
|
+
|
|
1741
|
+
- Data Aggregation (Patterns 1, 5, 10)
|
|
1742
|
+
- Content Processing (Patterns 2, 3)
|
|
1743
|
+
- Data Validation & Comparison (Patterns 4)
|
|
1744
|
+
- Data Transformation (Patterns 5, 6, 7)
|
|
1745
|
+
- Output Formatting (Pattern 8)
|
|
1746
|
+
- Filtering & Ranking (Pattern 9)
|
|
1747
|
+
|
|
1748
|
+
### Pattern 1: Multi-Source Data Aggregation
|
|
1749
|
+
|
|
1750
|
+
**Use Case**: Combining data from multiple APIs, RSS feeds, webhooks, or databases
|
|
1751
|
+
|
|
1752
|
+
**When to use:**
|
|
1753
|
+
|
|
1754
|
+
- Collecting data from multiple services
|
|
1755
|
+
- Normalizing different API response formats
|
|
1756
|
+
- Merging data sources into unified structure
|
|
1757
|
+
- Building aggregated reports
|
|
1758
|
+
|
|
1759
|
+
**Key Techniques**: Loop iteration, conditional parsing, data normalization
|
|
1760
|
+
|
|
1761
|
+
#### Complete Example
|
|
1762
|
+
|
|
1763
|
+
```javascript
|
|
1764
|
+
// Process and structure data collected from multiple sources
|
|
1765
|
+
const allItems = $input.all();
|
|
1766
|
+
let processedArticles = [];
|
|
1767
|
+
|
|
1768
|
+
// Handle different source formats
|
|
1769
|
+
for (const item of allItems) {
|
|
1770
|
+
const sourceName = item.json.name || "Unknown";
|
|
1771
|
+
const sourceData = item.json;
|
|
1772
|
+
|
|
1773
|
+
// Parse source-specific structure - Hacker News format
|
|
1774
|
+
if (sourceName === "Hacker News" && sourceData.hits) {
|
|
1775
|
+
for (const hit of sourceData.hits) {
|
|
1776
|
+
processedArticles.push({
|
|
1777
|
+
title: hit.title,
|
|
1778
|
+
url: hit.url,
|
|
1779
|
+
summary: hit.story_text || "No summary",
|
|
1780
|
+
source: "Hacker News",
|
|
1781
|
+
score: hit.points || 0,
|
|
1782
|
+
fetchedAt: new Date().toISOString(),
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
// Parse source-specific structure - Reddit format
|
|
1788
|
+
else if (sourceName === "Reddit" && sourceData.data?.children) {
|
|
1789
|
+
for (const post of sourceData.data.children) {
|
|
1790
|
+
processedArticles.push({
|
|
1791
|
+
title: post.data.title,
|
|
1792
|
+
url: post.data.url,
|
|
1793
|
+
summary: post.data.selftext || "No summary",
|
|
1794
|
+
source: "Reddit",
|
|
1795
|
+
score: post.data.score || 0,
|
|
1796
|
+
fetchedAt: new Date().toISOString(),
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// Parse source-specific structure - RSS feed format
|
|
1802
|
+
else if (sourceName === "RSS" && sourceData.items) {
|
|
1803
|
+
for (const rssItem of sourceData.items) {
|
|
1804
|
+
processedArticles.push({
|
|
1805
|
+
title: rssItem.title,
|
|
1806
|
+
url: rssItem.link,
|
|
1807
|
+
summary: rssItem.description || "No summary",
|
|
1808
|
+
source: "RSS Feed",
|
|
1809
|
+
score: 0,
|
|
1810
|
+
fetchedAt: new Date().toISOString(),
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// Sort by score (highest first)
|
|
1817
|
+
processedArticles.sort((a, b) => b.score - a.score);
|
|
1818
|
+
|
|
1819
|
+
return processedArticles.map((article) => ({ json: article }));
|
|
1820
|
+
```
|
|
1821
|
+
|
|
1822
|
+
#### Variations
|
|
1823
|
+
|
|
1824
|
+
```javascript
|
|
1825
|
+
// Variation 1: Add source weighting
|
|
1826
|
+
for (const article of processedArticles) {
|
|
1827
|
+
const weights = {
|
|
1828
|
+
"Hacker News": 1.5,
|
|
1829
|
+
Reddit: 1.0,
|
|
1830
|
+
"RSS Feed": 0.8,
|
|
1831
|
+
};
|
|
1832
|
+
|
|
1833
|
+
article.weightedScore = article.score * (weights[article.source] || 1.0);
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
// Variation 2: Filter by minimum score
|
|
1837
|
+
processedArticles = processedArticles.filter((article) => article.score >= 10);
|
|
1838
|
+
|
|
1839
|
+
// Variation 3: Deduplicate by URL
|
|
1840
|
+
const seen = new Set();
|
|
1841
|
+
processedArticles = processedArticles.filter((article) => {
|
|
1842
|
+
if (seen.has(article.url)) {
|
|
1843
|
+
return false;
|
|
1844
|
+
}
|
|
1845
|
+
seen.add(article.url);
|
|
1846
|
+
return true;
|
|
1847
|
+
});
|
|
1848
|
+
```
|
|
1849
|
+
|
|
1850
|
+
### Pattern 2: Regex Filtering & Pattern Matching
|
|
1851
|
+
|
|
1852
|
+
**Use Case**: Content analysis, keyword extraction, mention tracking, text parsing
|
|
1853
|
+
|
|
1854
|
+
**When to use:**
|
|
1855
|
+
|
|
1856
|
+
- Extracting mentions or tags from text
|
|
1857
|
+
- Finding patterns in unstructured data
|
|
1858
|
+
- Counting keyword occurrences
|
|
1859
|
+
- Validating formats (emails, phone numbers)
|
|
1860
|
+
|
|
1861
|
+
**Key Techniques**: Regex matching, object aggregation, sorting/ranking
|
|
1862
|
+
|
|
1863
|
+
#### Complete Example
|
|
1864
|
+
|
|
1865
|
+
```javascript
|
|
1866
|
+
// Extract and track mentions using regex patterns
|
|
1867
|
+
const etfPattern = /\b([A-Z]{2,5})\b/g;
|
|
1868
|
+
const knownETFs = ["VOO", "VTI", "VT", "SCHD", "QYLD", "VXUS", "SPY", "QQQ"];
|
|
1869
|
+
|
|
1870
|
+
const etfMentions = {};
|
|
1871
|
+
|
|
1872
|
+
for (const item of $input.all()) {
|
|
1873
|
+
const data = item.json.data;
|
|
1874
|
+
|
|
1875
|
+
// Skip if no data or children
|
|
1876
|
+
if (!data?.children) continue;
|
|
1877
|
+
|
|
1878
|
+
for (const post of data.children) {
|
|
1879
|
+
// Combine title and body text
|
|
1880
|
+
const title = post.data.title || "";
|
|
1881
|
+
const body = post.data.selftext || "";
|
|
1882
|
+
const combinedText = (title + " " + body).toUpperCase();
|
|
1883
|
+
|
|
1884
|
+
// Find all matches
|
|
1885
|
+
const matches = combinedText.match(etfPattern);
|
|
1886
|
+
|
|
1887
|
+
if (matches) {
|
|
1888
|
+
for (const match of matches) {
|
|
1889
|
+
// Only count known ETFs
|
|
1890
|
+
if (knownETFs.includes(match)) {
|
|
1891
|
+
if (!etfMentions[match]) {
|
|
1892
|
+
etfMentions[match] = {
|
|
1893
|
+
count: 0,
|
|
1894
|
+
totalScore: 0,
|
|
1895
|
+
posts: [],
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
etfMentions[match].count++;
|
|
1900
|
+
etfMentions[match].totalScore += post.data.score || 0;
|
|
1901
|
+
etfMentions[match].posts.push({
|
|
1902
|
+
title: post.data.title,
|
|
1903
|
+
url: post.data.url,
|
|
1904
|
+
score: post.data.score,
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// Convert to array and sort by mention count
|
|
1913
|
+
return Object.entries(etfMentions)
|
|
1914
|
+
.map(([etf, data]) => ({
|
|
1915
|
+
json: {
|
|
1916
|
+
etf,
|
|
1917
|
+
mentions: data.count,
|
|
1918
|
+
totalScore: data.totalScore,
|
|
1919
|
+
averageScore: data.totalScore / data.count,
|
|
1920
|
+
topPosts: data.posts.sort((a, b) => b.score - a.score).slice(0, 3),
|
|
1921
|
+
},
|
|
1922
|
+
}))
|
|
1923
|
+
.sort((a, b) => b.json.mentions - a.json.mentions);
|
|
1924
|
+
```
|
|
1925
|
+
|
|
1926
|
+
#### Variations
|
|
1927
|
+
|
|
1928
|
+
```javascript
|
|
1929
|
+
// Variation 1: Email extraction
|
|
1930
|
+
const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
|
|
1931
|
+
const emails = text.match(emailPattern) || [];
|
|
1932
|
+
|
|
1933
|
+
// Variation 2: Phone number extraction
|
|
1934
|
+
const phonePattern = /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g;
|
|
1935
|
+
const phones = text.match(phonePattern) || [];
|
|
1936
|
+
|
|
1937
|
+
// Variation 3: Hashtag extraction
|
|
1938
|
+
const hashtagPattern = /#(\w+)/g;
|
|
1939
|
+
const hashtags = [];
|
|
1940
|
+
let match;
|
|
1941
|
+
while ((match = hashtagPattern.exec(text)) !== null) {
|
|
1942
|
+
hashtags.push(match[1]);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
// Variation 4: URL extraction
|
|
1946
|
+
const urlPattern = /https?:\/\/[^\s]+/g;
|
|
1947
|
+
const urls = text.match(urlPattern) || [];
|
|
1948
|
+
```
|
|
1949
|
+
|
|
1950
|
+
### Pattern 3: Markdown Parsing & Structured Data Extraction
|
|
1951
|
+
|
|
1952
|
+
**Use Case**: Parsing formatted text, extracting structured fields, content transformation
|
|
1953
|
+
|
|
1954
|
+
**When to use:**
|
|
1955
|
+
|
|
1956
|
+
- Parsing markdown or HTML
|
|
1957
|
+
- Extracting data from structured text
|
|
1958
|
+
- Converting formatted content to JSON
|
|
1959
|
+
- Processing documentation or articles
|
|
1960
|
+
|
|
1961
|
+
**Key Techniques**: Regex grouping, helper functions, data normalization, while loops for iteration
|
|
1962
|
+
|
|
1963
|
+
#### Complete Example
|
|
1964
|
+
|
|
1965
|
+
```javascript
|
|
1966
|
+
// Parse markdown and extract structured information
|
|
1967
|
+
const markdown = $input.first().json.data.markdown;
|
|
1968
|
+
const adRegex = /##\s*(.*?)\n(.*?)(?=\n##|\n---|$)/gs;
|
|
1969
|
+
|
|
1970
|
+
const ads = [];
|
|
1971
|
+
let match;
|
|
1972
|
+
|
|
1973
|
+
// Helper function to parse time strings to minutes
|
|
1974
|
+
function parseTimeToMinutes(timeStr) {
|
|
1975
|
+
if (!timeStr) return 999999; // Sort unparseable times last
|
|
1976
|
+
|
|
1977
|
+
const hourMatch = timeStr.match(/(\d+)\s*hour/);
|
|
1978
|
+
const dayMatch = timeStr.match(/(\d+)\s*day/);
|
|
1979
|
+
const minMatch = timeStr.match(/(\d+)\s*min/);
|
|
1980
|
+
|
|
1981
|
+
let totalMinutes = 0;
|
|
1982
|
+
if (dayMatch) totalMinutes += parseInt(dayMatch[1]) * 1440; // 24 * 60
|
|
1983
|
+
if (hourMatch) totalMinutes += parseInt(hourMatch[1]) * 60;
|
|
1984
|
+
if (minMatch) totalMinutes += parseInt(minMatch[1]);
|
|
1985
|
+
|
|
1986
|
+
return totalMinutes;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
// Extract all job postings from markdown
|
|
1990
|
+
while ((match = adRegex.exec(markdown)) !== null) {
|
|
1991
|
+
const title = match[1]?.trim() || "No title";
|
|
1992
|
+
const content = match[2]?.trim() || "";
|
|
1993
|
+
|
|
1994
|
+
// Extract structured fields from content
|
|
1995
|
+
const districtMatch = content.match(/\*\*District:\*\*\s*(.*?)(?:\n|$)/);
|
|
1996
|
+
const salaryMatch = content.match(/\*\*Salary:\*\*\s*(.*?)(?:\n|$)/);
|
|
1997
|
+
const timeMatch = content.match(/Posted:\s*(.*?)\*/);
|
|
1998
|
+
|
|
1999
|
+
ads.push({
|
|
2000
|
+
title: title,
|
|
2001
|
+
district: districtMatch?.[1].trim() || "Unknown",
|
|
2002
|
+
salary: salaryMatch?.[1].trim() || "Not specified",
|
|
2003
|
+
postedTimeAgo: timeMatch?.[1] || "Unknown",
|
|
2004
|
+
timeInMinutes: parseTimeToMinutes(timeMatch?.[1]),
|
|
2005
|
+
fullContent: content,
|
|
2006
|
+
extractedAt: new Date().toISOString(),
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
// Sort by recency (posted time)
|
|
2011
|
+
ads.sort((a, b) => a.timeInMinutes - b.timeInMinutes);
|
|
2012
|
+
|
|
2013
|
+
return ads.map((ad) => ({ json: ad }));
|
|
2014
|
+
```
|
|
2015
|
+
|
|
2016
|
+
#### Variations
|
|
2017
|
+
|
|
2018
|
+
````javascript
|
|
2019
|
+
// Variation 1: Parse HTML table to JSON
|
|
2020
|
+
const tableRegex = /<tr>(.*?)<\/tr>/gs;
|
|
2021
|
+
const cellRegex = /<td>(.*?)<\/td>/g;
|
|
2022
|
+
|
|
2023
|
+
const rows = [];
|
|
2024
|
+
let tableMatch;
|
|
2025
|
+
|
|
2026
|
+
while ((tableMatch = tableRegex.exec(htmlTable)) !== null) {
|
|
2027
|
+
const cells = [];
|
|
2028
|
+
let cellMatch;
|
|
2029
|
+
|
|
2030
|
+
while ((cellMatch = cellRegex.exec(tableMatch[1])) !== null) {
|
|
2031
|
+
cells.push(cellMatch[1].trim());
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
if (cells.length > 0) {
|
|
2035
|
+
rows.push(cells);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
// Variation 2: Extract code blocks from markdown
|
|
2040
|
+
const codeBlockRegex = /```(\w+)?\n(.*?)```/gs;
|
|
2041
|
+
const codeBlocks = [];
|
|
2042
|
+
|
|
2043
|
+
while ((match = codeBlockRegex.exec(markdown)) !== null) {
|
|
2044
|
+
codeBlocks.push({
|
|
2045
|
+
language: match[1] || "plain",
|
|
2046
|
+
code: match[2].trim(),
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
// Variation 3: Parse YAML frontmatter
|
|
2051
|
+
const frontmatterRegex = /^---\n(.*?)\n---/s;
|
|
2052
|
+
const frontmatterMatch = content.match(frontmatterRegex);
|
|
2053
|
+
|
|
2054
|
+
if (frontmatterMatch) {
|
|
2055
|
+
const yamlLines = frontmatterMatch[1].split("\n");
|
|
2056
|
+
const metadata = {};
|
|
2057
|
+
|
|
2058
|
+
for (const line of yamlLines) {
|
|
2059
|
+
const [key, ...valueParts] = line.split(":");
|
|
2060
|
+
if (key && valueParts.length > 0) {
|
|
2061
|
+
metadata[key.trim()] = valueParts.join(":").trim();
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
````
|
|
2066
|
+
|
|
2067
|
+
### Pattern 4: JSON Comparison & Validation
|
|
2068
|
+
|
|
2069
|
+
**Use Case**: Workflow versioning, configuration validation, change detection, data integrity
|
|
2070
|
+
|
|
2071
|
+
**When to use:**
|
|
2072
|
+
|
|
2073
|
+
- Comparing two versions of data
|
|
2074
|
+
- Detecting changes in configurations
|
|
2075
|
+
- Validating data consistency
|
|
2076
|
+
- Checking for differences
|
|
2077
|
+
|
|
2078
|
+
**Key Techniques**: JSON ordering, base64 decoding, deep comparison, object manipulation
|
|
2079
|
+
|
|
2080
|
+
> **See also**: [validation-expert.md](validation-expert.md) for structured validation workflows
|
|
2081
|
+
|
|
2082
|
+
#### Complete Example
|
|
2083
|
+
|
|
2084
|
+
```javascript
|
|
2085
|
+
// Compare and validate JSON objects from different sources
|
|
2086
|
+
const orderJsonKeys = (jsonObj) => {
|
|
2087
|
+
const ordered = {};
|
|
2088
|
+
Object.keys(jsonObj)
|
|
2089
|
+
.sort()
|
|
2090
|
+
.forEach((key) => {
|
|
2091
|
+
ordered[key] = jsonObj[key];
|
|
2092
|
+
});
|
|
2093
|
+
return ordered;
|
|
2094
|
+
};
|
|
2095
|
+
|
|
2096
|
+
const allItems = $input.all();
|
|
2097
|
+
|
|
2098
|
+
// Assume first item is base64-encoded original, second is current
|
|
2099
|
+
const origWorkflow = JSON.parse(
|
|
2100
|
+
Buffer.from(allItems[0].json.content, "base64").toString(),
|
|
2101
|
+
);
|
|
2102
|
+
const currentWorkflow = allItems[1].json;
|
|
2103
|
+
|
|
2104
|
+
// Order keys for consistent comparison
|
|
2105
|
+
const orderedOriginal = orderJsonKeys(origWorkflow);
|
|
2106
|
+
const orderedCurrent = orderJsonKeys(currentWorkflow);
|
|
2107
|
+
|
|
2108
|
+
// Deep comparison
|
|
2109
|
+
const isSame =
|
|
2110
|
+
JSON.stringify(orderedOriginal) === JSON.stringify(orderedCurrent);
|
|
2111
|
+
|
|
2112
|
+
// Find differences
|
|
2113
|
+
const differences = [];
|
|
2114
|
+
for (const key of Object.keys(orderedOriginal)) {
|
|
2115
|
+
if (
|
|
2116
|
+
JSON.stringify(orderedOriginal[key]) !== JSON.stringify(orderedCurrent[key])
|
|
2117
|
+
) {
|
|
2118
|
+
differences.push({
|
|
2119
|
+
field: key,
|
|
2120
|
+
original: orderedOriginal[key],
|
|
2121
|
+
current: orderedCurrent[key],
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
// Check for new keys
|
|
2127
|
+
for (const key of Object.keys(orderedCurrent)) {
|
|
2128
|
+
if (!(key in orderedOriginal)) {
|
|
2129
|
+
differences.push({
|
|
2130
|
+
field: key,
|
|
2131
|
+
original: null,
|
|
2132
|
+
current: orderedCurrent[key],
|
|
2133
|
+
status: "new",
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
return [
|
|
2139
|
+
{
|
|
2140
|
+
json: {
|
|
2141
|
+
identical: isSame,
|
|
2142
|
+
differenceCount: differences.length,
|
|
2143
|
+
differences: differences,
|
|
2144
|
+
original: orderedOriginal,
|
|
2145
|
+
current: orderedCurrent,
|
|
2146
|
+
comparedAt: new Date().toISOString(),
|
|
2147
|
+
},
|
|
2148
|
+
},
|
|
2149
|
+
];
|
|
2150
|
+
```
|
|
2151
|
+
|
|
2152
|
+
#### Variations
|
|
2153
|
+
|
|
2154
|
+
```javascript
|
|
2155
|
+
// Variation 1: Simple equality check
|
|
2156
|
+
const isEqual = JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
2157
|
+
|
|
2158
|
+
// Variation 2: Deep diff with detailed changes
|
|
2159
|
+
function deepDiff(obj1, obj2, path = "") {
|
|
2160
|
+
const changes = [];
|
|
2161
|
+
|
|
2162
|
+
for (const key in obj1) {
|
|
2163
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
2164
|
+
|
|
2165
|
+
if (!(key in obj2)) {
|
|
2166
|
+
changes.push({ type: "removed", path: currentPath, value: obj1[key] });
|
|
2167
|
+
} else if (typeof obj1[key] === "object" && typeof obj2[key] === "object") {
|
|
2168
|
+
changes.push(...deepDiff(obj1[key], obj2[key], currentPath));
|
|
2169
|
+
} else if (obj1[key] !== obj2[key]) {
|
|
2170
|
+
changes.push({
|
|
2171
|
+
type: "modified",
|
|
2172
|
+
path: currentPath,
|
|
2173
|
+
from: obj1[key],
|
|
2174
|
+
to: obj2[key],
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
for (const key in obj2) {
|
|
2180
|
+
if (!(key in obj1)) {
|
|
2181
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
2182
|
+
changes.push({ type: "added", path: currentPath, value: obj2[key] });
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
return changes;
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
// Variation 3: Schema validation
|
|
2190
|
+
function validateSchema(data, schema) {
|
|
2191
|
+
const errors = [];
|
|
2192
|
+
|
|
2193
|
+
for (const field of schema.required || []) {
|
|
2194
|
+
if (!(field in data)) {
|
|
2195
|
+
errors.push(`Missing required field: ${field}`);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
for (const [field, type] of Object.entries(schema.types || {})) {
|
|
2200
|
+
if (field in data && typeof data[field] !== type) {
|
|
2201
|
+
errors.push(
|
|
2202
|
+
`Field ${field} should be ${type}, got ${typeof data[field]}`,
|
|
2203
|
+
);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
return {
|
|
2208
|
+
valid: errors.length === 0,
|
|
2209
|
+
errors,
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
```
|
|
2213
|
+
|
|
2214
|
+
### Pattern 5: CRM Data Transformation
|
|
2215
|
+
|
|
2216
|
+
**Use Case**: Lead enrichment, data normalization, API preparation, form data processing
|
|
2217
|
+
|
|
2218
|
+
**When to use:**
|
|
2219
|
+
|
|
2220
|
+
- Processing form submissions
|
|
2221
|
+
- Preparing data for CRM APIs
|
|
2222
|
+
- Normalizing contact information
|
|
2223
|
+
- Enriching lead data
|
|
2224
|
+
|
|
2225
|
+
**Key Techniques**: Object destructuring, data mapping, format conversion, field splitting
|
|
2226
|
+
|
|
2227
|
+
#### Complete Example
|
|
2228
|
+
|
|
2229
|
+
```javascript
|
|
2230
|
+
// Transform form data into CRM-compatible format
|
|
2231
|
+
const item = $input.all()[0];
|
|
2232
|
+
const { name, email, phone, company, course_interest, message, timestamp } =
|
|
2233
|
+
item.json;
|
|
2234
|
+
|
|
2235
|
+
// Split name into first and last
|
|
2236
|
+
const nameParts = name.split(" ");
|
|
2237
|
+
const firstName = nameParts[0] || "";
|
|
2238
|
+
const lastName = nameParts.slice(1).join(" ") || "Unknown";
|
|
2239
|
+
|
|
2240
|
+
// Format phone number
|
|
2241
|
+
const cleanPhone = phone.replace(/[^\d]/g, ""); // Remove non-digits
|
|
2242
|
+
|
|
2243
|
+
// Build CRM data structure
|
|
2244
|
+
const crmData = {
|
|
2245
|
+
data: {
|
|
2246
|
+
type: "Contact",
|
|
2247
|
+
attributes: {
|
|
2248
|
+
first_name: firstName,
|
|
2249
|
+
last_name: lastName,
|
|
2250
|
+
email1: email,
|
|
2251
|
+
phone_work: cleanPhone,
|
|
2252
|
+
account_name: company,
|
|
2253
|
+
description: `Course Interest: ${course_interest}\n\nMessage: ${message}\n\nSubmitted: ${timestamp}`,
|
|
2254
|
+
lead_source: "Website Form",
|
|
2255
|
+
status: "New",
|
|
2256
|
+
},
|
|
2257
|
+
},
|
|
2258
|
+
metadata: {
|
|
2259
|
+
original_submission: timestamp,
|
|
2260
|
+
processed_at: new Date().toISOString(),
|
|
2261
|
+
},
|
|
2262
|
+
};
|
|
2263
|
+
|
|
2264
|
+
return [
|
|
2265
|
+
{
|
|
2266
|
+
json: {
|
|
2267
|
+
...item.json,
|
|
2268
|
+
crmData,
|
|
2269
|
+
processed: true,
|
|
2270
|
+
},
|
|
2271
|
+
},
|
|
2272
|
+
];
|
|
2273
|
+
```
|
|
2274
|
+
|
|
2275
|
+
#### Variations
|
|
2276
|
+
|
|
2277
|
+
```javascript
|
|
2278
|
+
// Variation 1: Multiple contact processing
|
|
2279
|
+
const contacts = $input.all();
|
|
2280
|
+
|
|
2281
|
+
return contacts.map((item) => {
|
|
2282
|
+
const data = item.json;
|
|
2283
|
+
const [firstName, ...lastNameParts] = data.name.split(" ");
|
|
2284
|
+
|
|
2285
|
+
return {
|
|
2286
|
+
json: {
|
|
2287
|
+
firstName,
|
|
2288
|
+
lastName: lastNameParts.join(" ") || "Unknown",
|
|
2289
|
+
email: data.email.toLowerCase(),
|
|
2290
|
+
phone: data.phone.replace(/[^\d]/g, ""),
|
|
2291
|
+
tags: [data.source, data.interest_level].filter(Boolean),
|
|
2292
|
+
},
|
|
2293
|
+
};
|
|
2294
|
+
});
|
|
2295
|
+
|
|
2296
|
+
// Variation 2: Field validation and normalization
|
|
2297
|
+
function normalizePContact(raw) {
|
|
2298
|
+
return {
|
|
2299
|
+
first_name: raw.firstName?.trim() || "",
|
|
2300
|
+
last_name: raw.lastName?.trim() || "Unknown",
|
|
2301
|
+
email: raw.email?.toLowerCase().trim() || "",
|
|
2302
|
+
phone: raw.phone?.replace(/[^\d]/g, "") || "",
|
|
2303
|
+
company: raw.company?.trim() || "Unknown",
|
|
2304
|
+
title: raw.title?.trim() || "",
|
|
2305
|
+
valid: Boolean(raw.email && raw.firstName),
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
// Variation 3: Lead scoring
|
|
2310
|
+
function calculateLeadScore(data) {
|
|
2311
|
+
let score = 0;
|
|
2312
|
+
|
|
2313
|
+
if (data.email) score += 10;
|
|
2314
|
+
if (data.phone) score += 10;
|
|
2315
|
+
if (data.company) score += 15;
|
|
2316
|
+
if (data.title?.toLowerCase().includes("director")) score += 20;
|
|
2317
|
+
if (data.title?.toLowerCase().includes("manager")) score += 15;
|
|
2318
|
+
if (data.message?.length > 100) score += 10;
|
|
2319
|
+
|
|
2320
|
+
return score;
|
|
2321
|
+
}
|
|
2322
|
+
```
|
|
2323
|
+
|
|
2324
|
+
### Pattern 6: Release Information Processing
|
|
2325
|
+
|
|
2326
|
+
**Use Case**: Version management, changelog parsing, release notes generation, GitHub API processing
|
|
2327
|
+
|
|
2328
|
+
**When to use:**
|
|
2329
|
+
|
|
2330
|
+
- Processing GitHub releases
|
|
2331
|
+
- Filtering stable versions
|
|
2332
|
+
- Generating changelog summaries
|
|
2333
|
+
- Extracting version information
|
|
2334
|
+
|
|
2335
|
+
**Key Techniques**: Array filtering, conditional field extraction, date formatting, string manipulation
|
|
2336
|
+
|
|
2337
|
+
#### Complete Example
|
|
2338
|
+
|
|
2339
|
+
```javascript
|
|
2340
|
+
// Extract and filter stable releases from GitHub API
|
|
2341
|
+
const allReleases = $input.first().json;
|
|
2342
|
+
|
|
2343
|
+
const stableReleases = allReleases
|
|
2344
|
+
.filter((release) => !release.prerelease && !release.draft)
|
|
2345
|
+
.slice(0, 10)
|
|
2346
|
+
.map((release) => {
|
|
2347
|
+
// Extract highlights section from changelog
|
|
2348
|
+
const body = release.body || "";
|
|
2349
|
+
let highlights = "No highlights available";
|
|
2350
|
+
|
|
2351
|
+
if (body.includes("## Highlights:")) {
|
|
2352
|
+
highlights = body.split("## Highlights:")[1]?.split("##")[0]?.trim();
|
|
2353
|
+
} else {
|
|
2354
|
+
// Fallback to first 500 chars
|
|
2355
|
+
highlights = body.substring(0, 500) + "...";
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
return {
|
|
2359
|
+
tag: release.tag_name,
|
|
2360
|
+
name: release.name,
|
|
2361
|
+
published: release.published_at,
|
|
2362
|
+
publishedDate: new Date(release.published_at).toLocaleDateString(),
|
|
2363
|
+
author: release.author.login,
|
|
2364
|
+
url: release.html_url,
|
|
2365
|
+
changelog: body,
|
|
2366
|
+
highlights: highlights,
|
|
2367
|
+
assetCount: release.assets.length,
|
|
2368
|
+
assets: release.assets.map((asset) => ({
|
|
2369
|
+
name: asset.name,
|
|
2370
|
+
size: asset.size,
|
|
2371
|
+
downloadCount: asset.download_count,
|
|
2372
|
+
downloadUrl: asset.browser_download_url,
|
|
2373
|
+
})),
|
|
2374
|
+
};
|
|
2375
|
+
});
|
|
2376
|
+
|
|
2377
|
+
return stableReleases.map((release) => ({ json: release }));
|
|
2378
|
+
```
|
|
2379
|
+
|
|
2380
|
+
#### Variations
|
|
2381
|
+
|
|
2382
|
+
```javascript
|
|
2383
|
+
// Variation 1: Version comparison
|
|
2384
|
+
function compareVersions(v1, v2) {
|
|
2385
|
+
const parts1 = v1.replace("v", "").split(".").map(Number);
|
|
2386
|
+
const parts2 = v2.replace("v", "").split(".").map(Number);
|
|
2387
|
+
|
|
2388
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
2389
|
+
const num1 = parts1[i] || 0;
|
|
2390
|
+
const num2 = parts2[i] || 0;
|
|
2391
|
+
|
|
2392
|
+
if (num1 > num2) return 1;
|
|
2393
|
+
if (num1 < num2) return -1;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
return 0;
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
// Variation 2: Breaking change detection
|
|
2400
|
+
function hasBreakingChanges(changelog) {
|
|
2401
|
+
const breakingKeywords = [
|
|
2402
|
+
"BREAKING CHANGE",
|
|
2403
|
+
"breaking change",
|
|
2404
|
+
"BC:",
|
|
2405
|
+
"\u{1F4A5}",
|
|
2406
|
+
];
|
|
2407
|
+
|
|
2408
|
+
return breakingKeywords.some((keyword) => changelog.includes(keyword));
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
// Variation 3: Extract version numbers
|
|
2412
|
+
const versionPattern = /v?(\d+)\.(\d+)\.(\d+)/;
|
|
2413
|
+
const match = tagName.match(versionPattern);
|
|
2414
|
+
|
|
2415
|
+
if (match) {
|
|
2416
|
+
const [_, major, minor, patch] = match;
|
|
2417
|
+
const version = {
|
|
2418
|
+
major: parseInt(major),
|
|
2419
|
+
minor: parseInt(minor),
|
|
2420
|
+
patch: parseInt(patch),
|
|
2421
|
+
};
|
|
2422
|
+
}
|
|
2423
|
+
```
|
|
2424
|
+
|
|
2425
|
+
### Pattern 7: Array Transformation with Context
|
|
2426
|
+
|
|
2427
|
+
**Use Case**: Quick data transformation, field mapping, adding computed fields
|
|
2428
|
+
|
|
2429
|
+
**When to use:**
|
|
2430
|
+
|
|
2431
|
+
- Transforming arrays with additional context
|
|
2432
|
+
- Adding calculated fields
|
|
2433
|
+
- Simplifying complex objects
|
|
2434
|
+
- Pluralization logic
|
|
2435
|
+
|
|
2436
|
+
**Key Techniques**: Array methods chaining, ternary operators, computed properties
|
|
2437
|
+
|
|
2438
|
+
#### Complete Example
|
|
2439
|
+
|
|
2440
|
+
```javascript
|
|
2441
|
+
// Transform releases with contextual information
|
|
2442
|
+
const releases = $input
|
|
2443
|
+
.first()
|
|
2444
|
+
.json.filter((release) => !release.prerelease && !release.draft)
|
|
2445
|
+
.slice(0, 10)
|
|
2446
|
+
.map((release) => ({
|
|
2447
|
+
version: release.tag_name,
|
|
2448
|
+
assetCount: release.assets.length,
|
|
2449
|
+
assetsCountText: release.assets.length === 1 ? "file" : "files",
|
|
2450
|
+
downloadUrl: release.html_url,
|
|
2451
|
+
isRecent:
|
|
2452
|
+
new Date(release.published_at) >
|
|
2453
|
+
new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
|
|
2454
|
+
age: Math.floor(
|
|
2455
|
+
(Date.now() - new Date(release.published_at)) / (24 * 60 * 60 * 1000),
|
|
2456
|
+
),
|
|
2457
|
+
ageText: `${Math.floor((Date.now() - new Date(release.published_at)) / (24 * 60 * 60 * 1000))} days ago`,
|
|
2458
|
+
}));
|
|
2459
|
+
|
|
2460
|
+
return releases.map((release) => ({ json: release }));
|
|
2461
|
+
```
|
|
2462
|
+
|
|
2463
|
+
#### Variations
|
|
2464
|
+
|
|
2465
|
+
```javascript
|
|
2466
|
+
// Variation 1: Add ranking
|
|
2467
|
+
const items = $input
|
|
2468
|
+
.all()
|
|
2469
|
+
.sort((a, b) => b.json.score - a.json.score)
|
|
2470
|
+
.map((item, index) => ({
|
|
2471
|
+
json: {
|
|
2472
|
+
...item.json,
|
|
2473
|
+
rank: index + 1,
|
|
2474
|
+
medal: index < 3 ? ["\u{1F947}", "\u{1F948}", "\u{1F949}"][index] : "",
|
|
2475
|
+
},
|
|
2476
|
+
}));
|
|
2477
|
+
|
|
2478
|
+
// Variation 2: Add percentage calculations
|
|
2479
|
+
const total = $input.all().reduce((sum, item) => sum + item.json.value, 0);
|
|
2480
|
+
|
|
2481
|
+
const itemsWithPercentage = $input.all().map((item) => ({
|
|
2482
|
+
json: {
|
|
2483
|
+
...item.json,
|
|
2484
|
+
percentage: ((item.json.value / total) * 100).toFixed(2) + "%",
|
|
2485
|
+
},
|
|
2486
|
+
}));
|
|
2487
|
+
|
|
2488
|
+
// Variation 3: Add category labels
|
|
2489
|
+
const categorize = (value) => {
|
|
2490
|
+
if (value > 100) return "High";
|
|
2491
|
+
if (value > 50) return "Medium";
|
|
2492
|
+
return "Low";
|
|
2493
|
+
};
|
|
2494
|
+
|
|
2495
|
+
const categorized = $input.all().map((item) => ({
|
|
2496
|
+
json: {
|
|
2497
|
+
...item.json,
|
|
2498
|
+
category: categorize(item.json.value),
|
|
2499
|
+
},
|
|
2500
|
+
}));
|
|
2501
|
+
```
|
|
2502
|
+
|
|
2503
|
+
### Pattern 8: Slack Block Kit Formatting
|
|
2504
|
+
|
|
2505
|
+
**Use Case**: Chat notifications, rich message formatting, interactive messages
|
|
2506
|
+
|
|
2507
|
+
**When to use:**
|
|
2508
|
+
|
|
2509
|
+
- Sending formatted Slack messages
|
|
2510
|
+
- Creating interactive notifications
|
|
2511
|
+
- Building rich content for chat platforms
|
|
2512
|
+
- Status reports and alerts
|
|
2513
|
+
|
|
2514
|
+
**Key Techniques**: Template literals, nested objects, Block Kit syntax, date formatting
|
|
2515
|
+
|
|
2516
|
+
#### Complete Example
|
|
2517
|
+
|
|
2518
|
+
```javascript
|
|
2519
|
+
// Create Slack-formatted message with structured blocks
|
|
2520
|
+
const date = new Date().toISOString().split("T")[0];
|
|
2521
|
+
const data = $input.first().json;
|
|
2522
|
+
|
|
2523
|
+
return [
|
|
2524
|
+
{
|
|
2525
|
+
json: {
|
|
2526
|
+
text: `Daily Report - ${date}`, // Fallback text
|
|
2527
|
+
blocks: [
|
|
2528
|
+
{
|
|
2529
|
+
type: "header",
|
|
2530
|
+
text: {
|
|
2531
|
+
type: "plain_text",
|
|
2532
|
+
text: `\u{1F4CA} Daily Security Report - ${date}`,
|
|
2533
|
+
},
|
|
2534
|
+
},
|
|
2535
|
+
{
|
|
2536
|
+
type: "section",
|
|
2537
|
+
text: {
|
|
2538
|
+
type: "mrkdwn",
|
|
2539
|
+
text: `*Status:* ${data.status === "ok" ? "\u2705 All Clear" : "\u26A0\uFE0F Issues Detected"}\n*Alerts:* ${data.alertCount || 0}\n*Updated:* ${new Date().toLocaleString()}`,
|
|
2540
|
+
},
|
|
2541
|
+
},
|
|
2542
|
+
{
|
|
2543
|
+
type: "divider",
|
|
2544
|
+
},
|
|
2545
|
+
{
|
|
2546
|
+
type: "section",
|
|
2547
|
+
fields: [
|
|
2548
|
+
{
|
|
2549
|
+
type: "mrkdwn",
|
|
2550
|
+
text: `*Failed Logins:*\n${data.failedLogins || 0}`,
|
|
2551
|
+
},
|
|
2552
|
+
{
|
|
2553
|
+
type: "mrkdwn",
|
|
2554
|
+
text: `*API Errors:*\n${data.apiErrors || 0}`,
|
|
2555
|
+
},
|
|
2556
|
+
{
|
|
2557
|
+
type: "mrkdwn",
|
|
2558
|
+
text: `*Uptime:*\n${data.uptime || "100%"}`,
|
|
2559
|
+
},
|
|
2560
|
+
{
|
|
2561
|
+
type: "mrkdwn",
|
|
2562
|
+
text: `*Response Time:*\n${data.avgResponseTime || "N/A"}ms`,
|
|
2563
|
+
},
|
|
2564
|
+
],
|
|
2565
|
+
},
|
|
2566
|
+
{
|
|
2567
|
+
type: "context",
|
|
2568
|
+
elements: [
|
|
2569
|
+
{
|
|
2570
|
+
type: "mrkdwn",
|
|
2571
|
+
text: `Report generated automatically by n8n workflow`,
|
|
2572
|
+
},
|
|
2573
|
+
],
|
|
2574
|
+
},
|
|
2575
|
+
],
|
|
2576
|
+
},
|
|
2577
|
+
},
|
|
2578
|
+
];
|
|
2579
|
+
```
|
|
2580
|
+
|
|
2581
|
+
#### Variations
|
|
2582
|
+
|
|
2583
|
+
```javascript
|
|
2584
|
+
// Variation 1: Interactive buttons
|
|
2585
|
+
const blocksWithButtons = [
|
|
2586
|
+
{
|
|
2587
|
+
type: "section",
|
|
2588
|
+
text: {
|
|
2589
|
+
type: "mrkdwn",
|
|
2590
|
+
text: "Would you like to approve this request?",
|
|
2591
|
+
},
|
|
2592
|
+
accessory: {
|
|
2593
|
+
type: "button",
|
|
2594
|
+
text: {
|
|
2595
|
+
type: "plain_text",
|
|
2596
|
+
text: "Approve",
|
|
2597
|
+
},
|
|
2598
|
+
style: "primary",
|
|
2599
|
+
value: "approve",
|
|
2600
|
+
action_id: "approve_button",
|
|
2601
|
+
},
|
|
2602
|
+
},
|
|
2603
|
+
];
|
|
2604
|
+
|
|
2605
|
+
// Variation 2: List formatting
|
|
2606
|
+
const items = ["Item 1", "Item 2", "Item 3"];
|
|
2607
|
+
const formattedList = items.map((item, i) => `${i + 1}. ${item}`).join("\n");
|
|
2608
|
+
|
|
2609
|
+
// Variation 3: Status indicators
|
|
2610
|
+
function getStatusEmoji(status) {
|
|
2611
|
+
const statusMap = {
|
|
2612
|
+
success: "\u2705",
|
|
2613
|
+
warning: "\u26A0\uFE0F",
|
|
2614
|
+
error: "\u274C",
|
|
2615
|
+
info: "\u2139\uFE0F",
|
|
2616
|
+
};
|
|
2617
|
+
|
|
2618
|
+
return statusMap[status] || "\u2022";
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
// Variation 4: Truncate long messages
|
|
2622
|
+
function truncate(text, maxLength = 3000) {
|
|
2623
|
+
if (text.length <= maxLength) return text;
|
|
2624
|
+
return text.substring(0, maxLength - 3) + "...";
|
|
2625
|
+
}
|
|
2626
|
+
```
|
|
2627
|
+
|
|
2628
|
+
### Pattern 9: Top N Filtering & Ranking
|
|
2629
|
+
|
|
2630
|
+
**Use Case**: RAG pipelines, ranking algorithms, result filtering, leaderboards
|
|
2631
|
+
|
|
2632
|
+
**When to use:**
|
|
2633
|
+
|
|
2634
|
+
- Getting top results by score
|
|
2635
|
+
- Filtering best/worst performers
|
|
2636
|
+
- Building leaderboards
|
|
2637
|
+
- Relevance ranking
|
|
2638
|
+
|
|
2639
|
+
**Key Techniques**: Sorting, slicing, null coalescing, score calculations
|
|
2640
|
+
|
|
2641
|
+
#### Complete Example
|
|
2642
|
+
|
|
2643
|
+
```javascript
|
|
2644
|
+
// Filter and rank by similarity score, return top results
|
|
2645
|
+
const ragResponse = $input.item.json;
|
|
2646
|
+
const chunks = ragResponse.chunks || [];
|
|
2647
|
+
|
|
2648
|
+
// Sort by similarity (highest first)
|
|
2649
|
+
const topChunks = chunks
|
|
2650
|
+
.sort((a, b) => (b.similarity || 0) - (a.similarity || 0))
|
|
2651
|
+
.slice(0, 6);
|
|
2652
|
+
|
|
2653
|
+
return [
|
|
2654
|
+
{
|
|
2655
|
+
json: {
|
|
2656
|
+
query: ragResponse.query,
|
|
2657
|
+
topChunks: topChunks,
|
|
2658
|
+
count: topChunks.length,
|
|
2659
|
+
maxSimilarity: topChunks[0]?.similarity || 0,
|
|
2660
|
+
minSimilarity: topChunks[topChunks.length - 1]?.similarity || 0,
|
|
2661
|
+
averageSimilarity:
|
|
2662
|
+
topChunks.reduce((sum, chunk) => sum + (chunk.similarity || 0), 0) /
|
|
2663
|
+
topChunks.length,
|
|
2664
|
+
},
|
|
2665
|
+
},
|
|
2666
|
+
];
|
|
2667
|
+
```
|
|
2668
|
+
|
|
2669
|
+
#### Variations
|
|
2670
|
+
|
|
2671
|
+
```javascript
|
|
2672
|
+
// Variation 1: Top N with minimum threshold
|
|
2673
|
+
const threshold = 0.7;
|
|
2674
|
+
const topItems = $input
|
|
2675
|
+
.all()
|
|
2676
|
+
.filter((item) => item.json.score >= threshold)
|
|
2677
|
+
.sort((a, b) => b.json.score - a.json.score)
|
|
2678
|
+
.slice(0, 10);
|
|
2679
|
+
|
|
2680
|
+
// Variation 2: Bottom N (worst performers)
|
|
2681
|
+
const bottomItems = $input
|
|
2682
|
+
.all()
|
|
2683
|
+
.sort((a, b) => a.json.score - b.json.score) // Ascending
|
|
2684
|
+
.slice(0, 5);
|
|
2685
|
+
|
|
2686
|
+
// Variation 3: Top N by multiple criteria
|
|
2687
|
+
const ranked = $input
|
|
2688
|
+
.all()
|
|
2689
|
+
.map((item) => ({
|
|
2690
|
+
...item,
|
|
2691
|
+
compositeScore: item.json.relevance * 0.6 + item.json.recency * 0.4,
|
|
2692
|
+
}))
|
|
2693
|
+
.sort((a, b) => b.compositeScore - a.compositeScore)
|
|
2694
|
+
.slice(0, 10);
|
|
2695
|
+
|
|
2696
|
+
// Variation 4: Percentile filtering
|
|
2697
|
+
const allScores = $input
|
|
2698
|
+
.all()
|
|
2699
|
+
.map((item) => item.json.score)
|
|
2700
|
+
.sort((a, b) => b - a);
|
|
2701
|
+
const percentile95 = allScores[Math.floor(allScores.length * 0.05)];
|
|
2702
|
+
|
|
2703
|
+
const topPercentile = $input
|
|
2704
|
+
.all()
|
|
2705
|
+
.filter((item) => item.json.score >= percentile95);
|
|
2706
|
+
```
|
|
2707
|
+
|
|
2708
|
+
### Pattern 10: String Aggregation & Reporting
|
|
2709
|
+
|
|
2710
|
+
**Use Case**: Report generation, log aggregation, content concatenation, summary creation
|
|
2711
|
+
|
|
2712
|
+
**When to use:**
|
|
2713
|
+
|
|
2714
|
+
- Combining multiple text outputs
|
|
2715
|
+
- Generating reports from data
|
|
2716
|
+
- Aggregating logs or messages
|
|
2717
|
+
- Creating formatted summaries
|
|
2718
|
+
|
|
2719
|
+
**Key Techniques**: Array joining, string concatenation, template literals, timestamp handling
|
|
2720
|
+
|
|
2721
|
+
#### Complete Example
|
|
2722
|
+
|
|
2723
|
+
```javascript
|
|
2724
|
+
// Aggregate multiple text inputs into formatted report
|
|
2725
|
+
const allItems = $input.all();
|
|
2726
|
+
|
|
2727
|
+
// Collect all messages
|
|
2728
|
+
const messages = allItems.map((item) => item.json.message);
|
|
2729
|
+
|
|
2730
|
+
// Build report
|
|
2731
|
+
const header = `\u{1F3AF} **Daily Summary Report**\n\u{1F4C5} ${new Date().toLocaleString()}\n\u{1F4CA} Total Items: ${messages.length}\n\n`;
|
|
2732
|
+
const divider = "\n\n---\n\n";
|
|
2733
|
+
const footer = `\n\n---\n\n\u2705 Report generated at ${new Date().toISOString()}`;
|
|
2734
|
+
|
|
2735
|
+
const finalReport = header + messages.join(divider) + footer;
|
|
2736
|
+
|
|
2737
|
+
return [
|
|
2738
|
+
{
|
|
2739
|
+
json: {
|
|
2740
|
+
report: finalReport,
|
|
2741
|
+
messageCount: messages.length,
|
|
2742
|
+
generatedAt: new Date().toISOString(),
|
|
2743
|
+
reportLength: finalReport.length,
|
|
2744
|
+
},
|
|
2745
|
+
},
|
|
2746
|
+
];
|
|
2747
|
+
```
|
|
2748
|
+
|
|
2749
|
+
#### Variations
|
|
2750
|
+
|
|
2751
|
+
```javascript
|
|
2752
|
+
// Variation 1: Numbered list
|
|
2753
|
+
const numberedReport = allItems
|
|
2754
|
+
.map(
|
|
2755
|
+
(item, index) =>
|
|
2756
|
+
`${index + 1}. ${item.json.title}\n ${item.json.description}`,
|
|
2757
|
+
)
|
|
2758
|
+
.join("\n\n");
|
|
2759
|
+
|
|
2760
|
+
// Variation 2: Markdown table
|
|
2761
|
+
const headers = "| Name | Status | Score |\n|------|--------|-------|\n";
|
|
2762
|
+
const rows = allItems
|
|
2763
|
+
.map(
|
|
2764
|
+
(item) =>
|
|
2765
|
+
`| ${item.json.name} | ${item.json.status} | ${item.json.score} |`,
|
|
2766
|
+
)
|
|
2767
|
+
.join("\n");
|
|
2768
|
+
|
|
2769
|
+
const table = headers + rows;
|
|
2770
|
+
|
|
2771
|
+
// Variation 3: HTML report
|
|
2772
|
+
const htmlReport = `
|
|
2773
|
+
<!DOCTYPE html>
|
|
2774
|
+
<html>
|
|
2775
|
+
<head><title>Report</title></head>
|
|
2776
|
+
<body>
|
|
2777
|
+
<h1>Report - ${new Date().toLocaleDateString()}</h1>
|
|
2778
|
+
<ul>
|
|
2779
|
+
${allItems.map((item) => `<li>${item.json.title}: ${item.json.value}</li>`).join("\n ")}
|
|
2780
|
+
</ul>
|
|
2781
|
+
</body>
|
|
2782
|
+
</html>
|
|
2783
|
+
`;
|
|
2784
|
+
|
|
2785
|
+
// Variation 4: JSON summary
|
|
2786
|
+
const summary = {
|
|
2787
|
+
generated: new Date().toISOString(),
|
|
2788
|
+
totalItems: allItems.length,
|
|
2789
|
+
items: allItems.map((item) => item.json),
|
|
2790
|
+
statistics: {
|
|
2791
|
+
total: allItems.reduce((sum, item) => sum + (item.json.value || 0), 0),
|
|
2792
|
+
average:
|
|
2793
|
+
allItems.reduce((sum, item) => sum + (item.json.value || 0), 0) /
|
|
2794
|
+
allItems.length,
|
|
2795
|
+
max: Math.max(...allItems.map((item) => item.json.value || 0)),
|
|
2796
|
+
min: Math.min(...allItems.map((item) => item.json.value || 0)),
|
|
2797
|
+
},
|
|
2798
|
+
};
|
|
2799
|
+
```
|
|
2800
|
+
|
|
2801
|
+
### Choosing the Right Pattern
|
|
2802
|
+
|
|
2803
|
+
| Your Goal | Use Pattern |
|
|
2804
|
+
| ------------------------------ | ------------------------------------ |
|
|
2805
|
+
| Combine multiple API responses | Pattern 1 (Multi-source Aggregation) |
|
|
2806
|
+
| Extract mentions or keywords | Pattern 2 (Regex Filtering) |
|
|
2807
|
+
| Parse formatted text | Pattern 3 (Markdown Parsing) |
|
|
2808
|
+
| Detect changes in data | Pattern 4 (JSON Comparison) |
|
|
2809
|
+
| Prepare form data for CRM | Pattern 5 (CRM Transformation) |
|
|
2810
|
+
| Process GitHub releases | Pattern 6 (Release Processing) |
|
|
2811
|
+
| Add computed fields | Pattern 7 (Array Transformation) |
|
|
2812
|
+
| Format Slack messages | Pattern 8 (Block Kit Formatting) |
|
|
2813
|
+
| Get top results | Pattern 9 (Top N Filtering) |
|
|
2814
|
+
| Create text reports | Pattern 10 (String Aggregation) |
|
|
2815
|
+
|
|
2816
|
+
### Combining Patterns
|
|
2817
|
+
|
|
2818
|
+
Many real workflows combine multiple patterns:
|
|
2819
|
+
|
|
2820
|
+
```javascript
|
|
2821
|
+
// Example: Multi-source aggregation + Top N filtering
|
|
2822
|
+
const allItems = $input.all();
|
|
2823
|
+
const aggregated = [];
|
|
2824
|
+
|
|
2825
|
+
// Pattern 1: Aggregate from different sources
|
|
2826
|
+
for (const item of allItems) {
|
|
2827
|
+
// ... aggregation logic
|
|
2828
|
+
aggregated.push(normalizedItem);
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
// Pattern 9: Get top 10 by score
|
|
2832
|
+
const top10 = aggregated.sort((a, b) => b.score - a.score).slice(0, 10);
|
|
2833
|
+
|
|
2834
|
+
// Pattern 10: Generate report
|
|
2835
|
+
const report = `Top 10 Items:\n\n${top10.map((item, i) => `${i + 1}. ${item.title} (${item.score})`).join("\n")}`;
|
|
2836
|
+
|
|
2837
|
+
return [{ json: { report, items: top10 } }];
|
|
2838
|
+
```
|
|
2839
|
+
|
|
2840
|
+
---
|
|
2841
|
+
|
|
2842
|
+
## Error Patterns & Prevention
|
|
2843
|
+
|
|
2844
|
+
This section covers the **top 5 error patterns** encountered in n8n Code nodes. Understanding and avoiding these errors will save significant debugging time.
|
|
2845
|
+
|
|
2846
|
+
> **See also**: [validation-expert.md](validation-expert.md) for automated validation of Code node configurations
|
|
2847
|
+
|
|
2848
|
+
**Error Frequency**:
|
|
2849
|
+
|
|
2850
|
+
1. Empty Code / Missing Return - **38% of failures**
|
|
2851
|
+
2. Expression Syntax Confusion - **8% of failures**
|
|
2852
|
+
3. Incorrect Return Wrapper - **5% of failures**
|
|
2853
|
+
4. Unmatched Expression Brackets - **6% of failures**
|
|
2854
|
+
5. Missing Null Checks - **Common runtime error**
|
|
2855
|
+
|
|
2856
|
+
### Error #1: Empty Code or Missing Return Statement
|
|
2857
|
+
|
|
2858
|
+
**Frequency**: Most common error (38% of all validation failures)
|
|
2859
|
+
|
|
2860
|
+
**What Happens**:
|
|
2861
|
+
|
|
2862
|
+
- Workflow execution fails
|
|
2863
|
+
- Next nodes receive no data
|
|
2864
|
+
- Error: "Code cannot be empty" or "Code must return data"
|
|
2865
|
+
|
|
2866
|
+
#### The Problem
|
|
2867
|
+
|
|
2868
|
+
```javascript
|
|
2869
|
+
// ERROR: No code at all
|
|
2870
|
+
// (Empty code field)
|
|
2871
|
+
```
|
|
2872
|
+
|
|
2873
|
+
```javascript
|
|
2874
|
+
// ERROR: Code executes but doesn't return anything
|
|
2875
|
+
const items = $input.all();
|
|
2876
|
+
|
|
2877
|
+
// Process items
|
|
2878
|
+
for (const item of items) {
|
|
2879
|
+
console.log(item.json.name);
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
// Forgot to return!
|
|
2883
|
+
```
|
|
2884
|
+
|
|
2885
|
+
```javascript
|
|
2886
|
+
// ERROR: Early return path exists, but not all paths return
|
|
2887
|
+
const items = $input.all();
|
|
2888
|
+
|
|
2889
|
+
if (items.length === 0) {
|
|
2890
|
+
return []; // This path returns
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
// Process items
|
|
2894
|
+
const processed = items.map((item) => ({ json: item.json }));
|
|
2895
|
+
|
|
2896
|
+
// Forgot to return processed!
|
|
2897
|
+
```
|
|
2898
|
+
|
|
2899
|
+
#### The Solution
|
|
2900
|
+
|
|
2901
|
+
```javascript
|
|
2902
|
+
// CORRECT: Always return data
|
|
2903
|
+
const items = $input.all();
|
|
2904
|
+
|
|
2905
|
+
// Process items
|
|
2906
|
+
const processed = items.map((item) => ({
|
|
2907
|
+
json: {
|
|
2908
|
+
...item.json,
|
|
2909
|
+
processed: true,
|
|
2910
|
+
},
|
|
2911
|
+
}));
|
|
2912
|
+
|
|
2913
|
+
return processed; // Return statement present
|
|
2914
|
+
```
|
|
2915
|
+
|
|
2916
|
+
```javascript
|
|
2917
|
+
// CORRECT: Return empty array if no items
|
|
2918
|
+
const items = $input.all();
|
|
2919
|
+
|
|
2920
|
+
if (items.length === 0) {
|
|
2921
|
+
return []; // Valid: empty array when no data
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
// Process and return
|
|
2925
|
+
return items.map((item) => ({ json: item.json }));
|
|
2926
|
+
```
|
|
2927
|
+
|
|
2928
|
+
```javascript
|
|
2929
|
+
// CORRECT: All code paths return
|
|
2930
|
+
const items = $input.all();
|
|
2931
|
+
|
|
2932
|
+
if (items.length === 0) {
|
|
2933
|
+
return [];
|
|
2934
|
+
} else if (items.length === 1) {
|
|
2935
|
+
return [{ json: { single: true, data: items[0].json } }];
|
|
2936
|
+
} else {
|
|
2937
|
+
return items.map((item) => ({ json: item.json }));
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
// All paths covered
|
|
2941
|
+
```
|
|
2942
|
+
|
|
2943
|
+
#### Checklist
|
|
2944
|
+
|
|
2945
|
+
- [ ] Code field is not empty
|
|
2946
|
+
- [ ] Return statement exists
|
|
2947
|
+
- [ ] ALL code paths return data (if/else branches)
|
|
2948
|
+
- [ ] Return format is correct (`[{json: {...}}]`)
|
|
2949
|
+
- [ ] Return happens even on errors (use try-catch)
|
|
2950
|
+
|
|
2951
|
+
### Error #2: Expression Syntax Confusion
|
|
2952
|
+
|
|
2953
|
+
**Frequency**: 8% of validation failures
|
|
2954
|
+
|
|
2955
|
+
**What Happens**:
|
|
2956
|
+
|
|
2957
|
+
- Syntax error in code execution
|
|
2958
|
+
- Error: "Unexpected token" or "Expression syntax is not valid in Code nodes"
|
|
2959
|
+
- Template variables not evaluated
|
|
2960
|
+
|
|
2961
|
+
> **IMPORTANT**: Expression `{{ }}` syntax belongs in other nodes (Set, IF, HTTP Request). Code nodes use plain JavaScript. See [expression-syntax.md](expression-syntax.md) for when to use expressions vs code.
|
|
2962
|
+
|
|
2963
|
+
#### The Problem
|
|
2964
|
+
|
|
2965
|
+
n8n has TWO distinct syntaxes:
|
|
2966
|
+
|
|
2967
|
+
1. **Expression syntax** `{{ }}` - Used in OTHER nodes (Set, IF, HTTP Request)
|
|
2968
|
+
2. **JavaScript** - Used in CODE nodes (no `{{ }}`)
|
|
2969
|
+
|
|
2970
|
+
Many developers mistakenly use expression syntax inside Code nodes.
|
|
2971
|
+
|
|
2972
|
+
```javascript
|
|
2973
|
+
// WRONG: Using n8n expression syntax in Code node
|
|
2974
|
+
const userName = "{{ $json.name }}";
|
|
2975
|
+
const userEmail = "{{ $json.body.email }}";
|
|
2976
|
+
|
|
2977
|
+
return [
|
|
2978
|
+
{
|
|
2979
|
+
json: {
|
|
2980
|
+
name: userName,
|
|
2981
|
+
email: userEmail,
|
|
2982
|
+
},
|
|
2983
|
+
},
|
|
2984
|
+
];
|
|
2985
|
+
|
|
2986
|
+
// Result: Literal string "{{ $json.name }}", NOT the value!
|
|
2987
|
+
```
|
|
2988
|
+
|
|
2989
|
+
```javascript
|
|
2990
|
+
// WRONG: Trying to evaluate expressions
|
|
2991
|
+
const value = "{{ $now.toFormat('yyyy-MM-dd') }}";
|
|
2992
|
+
```
|
|
2993
|
+
|
|
2994
|
+
#### The Solution
|
|
2995
|
+
|
|
2996
|
+
```javascript
|
|
2997
|
+
// CORRECT: Use JavaScript directly (no {{ }})
|
|
2998
|
+
const userName = $json.name;
|
|
2999
|
+
const userEmail = $json.body.email;
|
|
3000
|
+
|
|
3001
|
+
return [
|
|
3002
|
+
{
|
|
3003
|
+
json: {
|
|
3004
|
+
name: userName,
|
|
3005
|
+
email: userEmail,
|
|
3006
|
+
},
|
|
3007
|
+
},
|
|
3008
|
+
];
|
|
3009
|
+
```
|
|
3010
|
+
|
|
3011
|
+
```javascript
|
|
3012
|
+
// CORRECT: JavaScript template literals (use backticks)
|
|
3013
|
+
const message = `Hello, ${$json.name}! Your email is ${$json.email}`;
|
|
3014
|
+
|
|
3015
|
+
return [
|
|
3016
|
+
{
|
|
3017
|
+
json: {
|
|
3018
|
+
greeting: message,
|
|
3019
|
+
},
|
|
3020
|
+
},
|
|
3021
|
+
];
|
|
3022
|
+
```
|
|
3023
|
+
|
|
3024
|
+
```javascript
|
|
3025
|
+
// CORRECT: Direct variable access
|
|
3026
|
+
const item = $input.first().json;
|
|
3027
|
+
|
|
3028
|
+
return [
|
|
3029
|
+
{
|
|
3030
|
+
json: {
|
|
3031
|
+
name: item.name,
|
|
3032
|
+
email: item.email,
|
|
3033
|
+
timestamp: new Date().toISOString(), // JavaScript Date, not {{ }}
|
|
3034
|
+
},
|
|
3035
|
+
},
|
|
3036
|
+
];
|
|
3037
|
+
```
|
|
3038
|
+
|
|
3039
|
+
#### Comparison Table
|
|
3040
|
+
|
|
3041
|
+
| Context | Syntax | Example |
|
|
3042
|
+
| --------------------- | --------------------- | --------------------------- |
|
|
3043
|
+
| Set node | `{{ }}` expressions | `{{ $json.name }}` |
|
|
3044
|
+
| IF node | `{{ }}` expressions | `{{ $json.age > 18 }}` |
|
|
3045
|
+
| HTTP Request URL | `{{ }}` expressions | `{{ $json.userId }}` |
|
|
3046
|
+
| **Code node** | **JavaScript** | `$json.name` |
|
|
3047
|
+
| **Code node strings** | **Template literals** | `` `Hello ${$json.name}` `` |
|
|
3048
|
+
|
|
3049
|
+
#### Quick Fix Guide
|
|
3050
|
+
|
|
3051
|
+
```javascript
|
|
3052
|
+
// WRONG -> RIGHT conversions
|
|
3053
|
+
|
|
3054
|
+
// "{{ $json.field }}"
|
|
3055
|
+
// -> $json.field
|
|
3056
|
+
|
|
3057
|
+
// "{{ $now }}"
|
|
3058
|
+
// -> new Date().toISOString()
|
|
3059
|
+
|
|
3060
|
+
// "{{ $node['HTTP Request'].json.data }}"
|
|
3061
|
+
// -> $node["HTTP Request"].json.data
|
|
3062
|
+
|
|
3063
|
+
// `{{ $json.firstName }} {{ $json.lastName }}`
|
|
3064
|
+
// -> `${$json.firstName} ${$json.lastName}`
|
|
3065
|
+
```
|
|
3066
|
+
|
|
3067
|
+
### Error #3: Incorrect Return Wrapper Format
|
|
3068
|
+
|
|
3069
|
+
**Frequency**: 5% of validation failures
|
|
3070
|
+
|
|
3071
|
+
**What Happens**:
|
|
3072
|
+
|
|
3073
|
+
- Error: "Return value must be an array of objects"
|
|
3074
|
+
- Error: "Each item must have a json property"
|
|
3075
|
+
- Next nodes receive malformed data
|
|
3076
|
+
|
|
3077
|
+
#### The Problem
|
|
3078
|
+
|
|
3079
|
+
Code nodes MUST return:
|
|
3080
|
+
|
|
3081
|
+
- **Array** of objects
|
|
3082
|
+
- Each object MUST have a **`json` property**
|
|
3083
|
+
|
|
3084
|
+
```javascript
|
|
3085
|
+
// WRONG: Returning object instead of array
|
|
3086
|
+
return {
|
|
3087
|
+
json: {
|
|
3088
|
+
result: "success",
|
|
3089
|
+
},
|
|
3090
|
+
};
|
|
3091
|
+
// Missing array wrapper []
|
|
3092
|
+
```
|
|
3093
|
+
|
|
3094
|
+
```javascript
|
|
3095
|
+
// WRONG: Returning array without json wrapper
|
|
3096
|
+
return [
|
|
3097
|
+
{ id: 1, name: "Alice" },
|
|
3098
|
+
{ id: 2, name: "Bob" },
|
|
3099
|
+
];
|
|
3100
|
+
// Missing json property
|
|
3101
|
+
```
|
|
3102
|
+
|
|
3103
|
+
```javascript
|
|
3104
|
+
// WRONG: Returning plain value
|
|
3105
|
+
return "processed";
|
|
3106
|
+
```
|
|
3107
|
+
|
|
3108
|
+
```javascript
|
|
3109
|
+
// WRONG: Returning items without mapping
|
|
3110
|
+
return $input.all();
|
|
3111
|
+
// Works if items already have json property, but not guaranteed
|
|
3112
|
+
```
|
|
3113
|
+
|
|
3114
|
+
```javascript
|
|
3115
|
+
// WRONG: Incomplete structure
|
|
3116
|
+
return [{ data: { result: "success" } }];
|
|
3117
|
+
// Should be {json: {...}}, not {data: {...}}
|
|
3118
|
+
```
|
|
3119
|
+
|
|
3120
|
+
#### The Solution
|
|
3121
|
+
|
|
3122
|
+
```javascript
|
|
3123
|
+
// CORRECT: Single result
|
|
3124
|
+
return [
|
|
3125
|
+
{
|
|
3126
|
+
json: {
|
|
3127
|
+
result: "success",
|
|
3128
|
+
timestamp: new Date().toISOString(),
|
|
3129
|
+
},
|
|
3130
|
+
},
|
|
3131
|
+
];
|
|
3132
|
+
```
|
|
3133
|
+
|
|
3134
|
+
```javascript
|
|
3135
|
+
// CORRECT: Multiple results
|
|
3136
|
+
return [
|
|
3137
|
+
{ json: { id: 1, name: "Alice" } },
|
|
3138
|
+
{ json: { id: 2, name: "Bob" } },
|
|
3139
|
+
{ json: { id: 3, name: "Carol" } },
|
|
3140
|
+
];
|
|
3141
|
+
```
|
|
3142
|
+
|
|
3143
|
+
```javascript
|
|
3144
|
+
// CORRECT: Transforming array
|
|
3145
|
+
const items = $input.all();
|
|
3146
|
+
|
|
3147
|
+
return items.map((item) => ({
|
|
3148
|
+
json: {
|
|
3149
|
+
id: item.json.id,
|
|
3150
|
+
name: item.json.name,
|
|
3151
|
+
processed: true,
|
|
3152
|
+
},
|
|
3153
|
+
}));
|
|
3154
|
+
```
|
|
3155
|
+
|
|
3156
|
+
```javascript
|
|
3157
|
+
// CORRECT: Empty result
|
|
3158
|
+
return [];
|
|
3159
|
+
// Valid when no data to return
|
|
3160
|
+
```
|
|
3161
|
+
|
|
3162
|
+
```javascript
|
|
3163
|
+
// CORRECT: Conditional returns
|
|
3164
|
+
if (shouldProcess) {
|
|
3165
|
+
return [{ json: { result: "processed" } }];
|
|
3166
|
+
} else {
|
|
3167
|
+
return [];
|
|
3168
|
+
}
|
|
3169
|
+
```
|
|
3170
|
+
|
|
3171
|
+
#### Return Format Checklist
|
|
3172
|
+
|
|
3173
|
+
- [ ] Return value is an **array** `[...]`
|
|
3174
|
+
- [ ] Each array element has **`json` property**
|
|
3175
|
+
- [ ] Structure is `[{json: {...}}]` or `[{json: {...}}, {json: {...}}]`
|
|
3176
|
+
- [ ] NOT `{json: {...}}` (missing array wrapper)
|
|
3177
|
+
- [ ] NOT `[{...}]` (missing json property)
|
|
3178
|
+
|
|
3179
|
+
#### Common Scenarios
|
|
3180
|
+
|
|
3181
|
+
```javascript
|
|
3182
|
+
// Scenario 1: Single object from API
|
|
3183
|
+
const response = $input.first().json;
|
|
3184
|
+
|
|
3185
|
+
// CORRECT
|
|
3186
|
+
return [{ json: response }];
|
|
3187
|
+
|
|
3188
|
+
// WRONG
|
|
3189
|
+
return { json: response };
|
|
3190
|
+
|
|
3191
|
+
// Scenario 2: Array of objects
|
|
3192
|
+
const users = $input.all();
|
|
3193
|
+
|
|
3194
|
+
// CORRECT
|
|
3195
|
+
return users.map((user) => ({ json: user.json }));
|
|
3196
|
+
|
|
3197
|
+
// WRONG
|
|
3198
|
+
return users; // Risky - depends on existing structure
|
|
3199
|
+
|
|
3200
|
+
// Scenario 3: Computed result
|
|
3201
|
+
const total = $input.all().reduce((sum, item) => sum + item.json.amount, 0);
|
|
3202
|
+
|
|
3203
|
+
// CORRECT
|
|
3204
|
+
return [{ json: { total } }];
|
|
3205
|
+
|
|
3206
|
+
// WRONG
|
|
3207
|
+
return { total };
|
|
3208
|
+
|
|
3209
|
+
// Scenario 4: No results
|
|
3210
|
+
// CORRECT
|
|
3211
|
+
return [];
|
|
3212
|
+
|
|
3213
|
+
// WRONG
|
|
3214
|
+
return null;
|
|
3215
|
+
```
|
|
3216
|
+
|
|
3217
|
+
### Error #4: Unmatched Expression Brackets
|
|
3218
|
+
|
|
3219
|
+
**Frequency**: 6% of validation failures
|
|
3220
|
+
|
|
3221
|
+
**What Happens**:
|
|
3222
|
+
|
|
3223
|
+
- Parsing error during save
|
|
3224
|
+
- Error: "Unmatched expression brackets"
|
|
3225
|
+
- Code appears correct but fails validation
|
|
3226
|
+
|
|
3227
|
+
#### The Problem
|
|
3228
|
+
|
|
3229
|
+
This error typically occurs when:
|
|
3230
|
+
|
|
3231
|
+
1. Strings contain unbalanced quotes
|
|
3232
|
+
2. Multi-line strings with special characters
|
|
3233
|
+
3. Template literals with nested brackets
|
|
3234
|
+
|
|
3235
|
+
```javascript
|
|
3236
|
+
// WRONG: Unescaped quote in string
|
|
3237
|
+
const message = "It's a nice day";
|
|
3238
|
+
// Single quote breaks string
|
|
3239
|
+
```
|
|
3240
|
+
|
|
3241
|
+
```javascript
|
|
3242
|
+
// WRONG: Unbalanced brackets in regex
|
|
3243
|
+
const pattern = /\{(\w+)\}/; // JSON storage issue
|
|
3244
|
+
```
|
|
3245
|
+
|
|
3246
|
+
```javascript
|
|
3247
|
+
// WRONG: Multi-line string with quotes
|
|
3248
|
+
const html = "
|
|
3249
|
+
<div class="container">
|
|
3250
|
+
<p>Hello</p>
|
|
3251
|
+
</div>
|
|
3252
|
+
";
|
|
3253
|
+
// Quote balance issues
|
|
3254
|
+
```
|
|
3255
|
+
|
|
3256
|
+
#### The Solution
|
|
3257
|
+
|
|
3258
|
+
```javascript
|
|
3259
|
+
// CORRECT: Escape quotes
|
|
3260
|
+
const message = "It\\'s a nice day";
|
|
3261
|
+
// Or use different quotes
|
|
3262
|
+
const message = "It's a nice day"; // Double quotes work
|
|
3263
|
+
```
|
|
3264
|
+
|
|
3265
|
+
```javascript
|
|
3266
|
+
// CORRECT: Escape regex properly
|
|
3267
|
+
const pattern = /\\{(\\w+)\\}/;
|
|
3268
|
+
```
|
|
3269
|
+
|
|
3270
|
+
```javascript
|
|
3271
|
+
// CORRECT: Template literals for multi-line
|
|
3272
|
+
const html = `
|
|
3273
|
+
<div class="container">
|
|
3274
|
+
<p>Hello</p>
|
|
3275
|
+
</div>
|
|
3276
|
+
`;
|
|
3277
|
+
// Backticks handle multi-line and quotes
|
|
3278
|
+
```
|
|
3279
|
+
|
|
3280
|
+
```javascript
|
|
3281
|
+
// CORRECT: Escape backslashes
|
|
3282
|
+
const path = "C:\\\\Users\\\\Documents\\\\file.txt";
|
|
3283
|
+
```
|
|
3284
|
+
|
|
3285
|
+
#### Escaping Guide
|
|
3286
|
+
|
|
3287
|
+
| Character | Escape As | Example |
|
|
3288
|
+
| ------------------------------------ | --------- | ------------------------ |
|
|
3289
|
+
| Single quote in single-quoted string | `\\'` | `'It\\'s working'` |
|
|
3290
|
+
| Double quote in double-quoted string | `\\"` | `"She said \\"hello\\""` |
|
|
3291
|
+
| Backslash | `\\\\` | `"C:\\\\path"` |
|
|
3292
|
+
| Newline | `\\n` | `"Line 1\\nLine 2"` |
|
|
3293
|
+
| Tab | `\\t` | `"Column1\\tColumn2"` |
|
|
3294
|
+
|
|
3295
|
+
#### Best Practices
|
|
3296
|
+
|
|
3297
|
+
```javascript
|
|
3298
|
+
// BEST: Use template literals for complex strings
|
|
3299
|
+
const message = `User ${name} said: "Hello!"`;
|
|
3300
|
+
|
|
3301
|
+
// BEST: Use template literals for HTML
|
|
3302
|
+
const html = `
|
|
3303
|
+
<div class="${className}">
|
|
3304
|
+
<h1>${title}</h1>
|
|
3305
|
+
<p>${content}</p>
|
|
3306
|
+
</div>
|
|
3307
|
+
`;
|
|
3308
|
+
|
|
3309
|
+
// BEST: Use template literals for JSON
|
|
3310
|
+
const jsonString = `{
|
|
3311
|
+
"name": "${name}",
|
|
3312
|
+
"email": "${email}"
|
|
3313
|
+
}`;
|
|
3314
|
+
```
|
|
3315
|
+
|
|
3316
|
+
### Error #5: Missing Null Checks / Undefined Access
|
|
3317
|
+
|
|
3318
|
+
**Frequency**: Very common runtime error
|
|
3319
|
+
|
|
3320
|
+
**What Happens**:
|
|
3321
|
+
|
|
3322
|
+
- Workflow execution stops
|
|
3323
|
+
- Error: "Cannot read property 'X' of undefined"
|
|
3324
|
+
- Error: "Cannot read property 'X' of null"
|
|
3325
|
+
- Crashes on missing data
|
|
3326
|
+
|
|
3327
|
+
#### The Problem
|
|
3328
|
+
|
|
3329
|
+
```javascript
|
|
3330
|
+
// WRONG: No null check - crashes if user doesn't exist
|
|
3331
|
+
const email = item.json.user.email;
|
|
3332
|
+
```
|
|
3333
|
+
|
|
3334
|
+
```javascript
|
|
3335
|
+
// WRONG: Assumes array has items
|
|
3336
|
+
const firstItem = $input.all()[0].json;
|
|
3337
|
+
```
|
|
3338
|
+
|
|
3339
|
+
```javascript
|
|
3340
|
+
// WRONG: Assumes nested property exists
|
|
3341
|
+
const city = $json.address.city;
|
|
3342
|
+
```
|
|
3343
|
+
|
|
3344
|
+
```javascript
|
|
3345
|
+
// WRONG: No validation before array operations
|
|
3346
|
+
const names = $json.users.map((user) => user.name);
|
|
3347
|
+
```
|
|
3348
|
+
|
|
3349
|
+
#### The Solution
|
|
3350
|
+
|
|
3351
|
+
```javascript
|
|
3352
|
+
// CORRECT: Optional chaining
|
|
3353
|
+
const email = item.json?.user?.email || "no-email@example.com";
|
|
3354
|
+
```
|
|
3355
|
+
|
|
3356
|
+
```javascript
|
|
3357
|
+
// CORRECT: Check array length
|
|
3358
|
+
const items = $input.all();
|
|
3359
|
+
|
|
3360
|
+
if (items.length === 0) {
|
|
3361
|
+
return [];
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
const firstItem = items[0].json;
|
|
3365
|
+
```
|
|
3366
|
+
|
|
3367
|
+
```javascript
|
|
3368
|
+
// CORRECT: Guard clauses
|
|
3369
|
+
const data = $input.first().json;
|
|
3370
|
+
|
|
3371
|
+
if (!data.address) {
|
|
3372
|
+
return [{ json: { error: "No address provided" } }];
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
const city = data.address.city;
|
|
3376
|
+
```
|
|
3377
|
+
|
|
3378
|
+
```javascript
|
|
3379
|
+
// CORRECT: Default values
|
|
3380
|
+
const users = $json.users || [];
|
|
3381
|
+
const names = users.map((user) => user.name || "Unknown");
|
|
3382
|
+
```
|
|
3383
|
+
|
|
3384
|
+
```javascript
|
|
3385
|
+
// CORRECT: Try-catch for risky operations
|
|
3386
|
+
try {
|
|
3387
|
+
const email = item.json.user.email.toLowerCase();
|
|
3388
|
+
return [{ json: { email } }];
|
|
3389
|
+
} catch (error) {
|
|
3390
|
+
return [
|
|
3391
|
+
{
|
|
3392
|
+
json: {
|
|
3393
|
+
error: "Invalid user data",
|
|
3394
|
+
details: error.message,
|
|
3395
|
+
},
|
|
3396
|
+
},
|
|
3397
|
+
];
|
|
3398
|
+
}
|
|
3399
|
+
```
|
|
3400
|
+
|
|
3401
|
+
#### Safe Access Patterns
|
|
3402
|
+
|
|
3403
|
+
```javascript
|
|
3404
|
+
// Pattern 1: Optional chaining (modern, recommended)
|
|
3405
|
+
const value = data?.nested?.property?.value;
|
|
3406
|
+
|
|
3407
|
+
// Pattern 2: Logical OR with default
|
|
3408
|
+
const value = data.property || "default";
|
|
3409
|
+
|
|
3410
|
+
// Pattern 3: Ternary check
|
|
3411
|
+
const value = data.property ? data.property : "default";
|
|
3412
|
+
|
|
3413
|
+
// Pattern 4: Guard clause
|
|
3414
|
+
if (!data.property) {
|
|
3415
|
+
return [];
|
|
3416
|
+
}
|
|
3417
|
+
const value = data.property;
|
|
3418
|
+
|
|
3419
|
+
// Pattern 5: Try-catch
|
|
3420
|
+
try {
|
|
3421
|
+
const value = data.nested.property.value;
|
|
3422
|
+
} catch (error) {
|
|
3423
|
+
const value = "default";
|
|
3424
|
+
}
|
|
3425
|
+
```
|
|
3426
|
+
|
|
3427
|
+
#### Webhook Data Safety
|
|
3428
|
+
|
|
3429
|
+
```javascript
|
|
3430
|
+
// Webhook data requires extra safety
|
|
3431
|
+
|
|
3432
|
+
// RISKY: Assumes all fields exist
|
|
3433
|
+
const name = $json.body.user.name;
|
|
3434
|
+
const email = $json.body.user.email;
|
|
3435
|
+
|
|
3436
|
+
// SAFE: Check each level
|
|
3437
|
+
const body = $json.body || {};
|
|
3438
|
+
const user = body.user || {};
|
|
3439
|
+
const name = user.name || "Unknown";
|
|
3440
|
+
const email = user.email || "no-email";
|
|
3441
|
+
|
|
3442
|
+
// BETTER: Optional chaining
|
|
3443
|
+
const name = $json.body?.user?.name || "Unknown";
|
|
3444
|
+
const email = $json.body?.user?.email || "no-email";
|
|
3445
|
+
```
|
|
3446
|
+
|
|
3447
|
+
#### Array Safety
|
|
3448
|
+
|
|
3449
|
+
```javascript
|
|
3450
|
+
// RISKY: No length check
|
|
3451
|
+
const items = $input.all();
|
|
3452
|
+
const firstId = items[0].json.id;
|
|
3453
|
+
|
|
3454
|
+
// SAFE: Check length
|
|
3455
|
+
const items = $input.all();
|
|
3456
|
+
|
|
3457
|
+
if (items.length > 0) {
|
|
3458
|
+
const firstId = items[0].json.id;
|
|
3459
|
+
} else {
|
|
3460
|
+
// Handle empty case
|
|
3461
|
+
return [];
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
// BETTER: Use $input.first()
|
|
3465
|
+
const firstItem = $input.first();
|
|
3466
|
+
const firstId = firstItem.json.id; // Built-in safety
|
|
3467
|
+
```
|
|
3468
|
+
|
|
3469
|
+
#### Object Property Safety
|
|
3470
|
+
|
|
3471
|
+
```javascript
|
|
3472
|
+
// RISKY: Direct access
|
|
3473
|
+
const config = $json.settings.advanced.timeout;
|
|
3474
|
+
|
|
3475
|
+
// SAFE: Step by step with defaults
|
|
3476
|
+
const settings = $json.settings || {};
|
|
3477
|
+
const advanced = settings.advanced || {};
|
|
3478
|
+
const timeout = advanced.timeout || 30000;
|
|
3479
|
+
|
|
3480
|
+
// BETTER: Optional chaining
|
|
3481
|
+
const timeout = $json.settings?.advanced?.timeout ?? 30000;
|
|
3482
|
+
// Note: ?? (nullish coalescing) vs || (logical OR)
|
|
3483
|
+
```
|
|
3484
|
+
|
|
3485
|
+
### Quick Error Reference
|
|
3486
|
+
|
|
3487
|
+
| Error Message | Likely Cause | Fix |
|
|
3488
|
+
| ------------------------------------- | --------------------------------- | ------------------------------ |
|
|
3489
|
+
| "Code cannot be empty" | Empty code field | Add meaningful code |
|
|
3490
|
+
| "Code must return data" | Missing return statement | Add `return [...]` |
|
|
3491
|
+
| "Return value must be an array" | Returning object instead of array | Wrap in `[...]` |
|
|
3492
|
+
| "Each item must have json property" | Missing `json` wrapper | Use `{json: {...}}` |
|
|
3493
|
+
| "Unexpected token" | Expression syntax `{{ }}` in code | Remove `{{ }}`, use JavaScript |
|
|
3494
|
+
| "Cannot read property X of undefined" | Missing null check | Use optional chaining `?.` |
|
|
3495
|
+
| "Cannot read property X of null" | Null value access | Add guard clause or default |
|
|
3496
|
+
| "Unmatched expression brackets" | Quote/bracket imbalance | Check string escaping |
|
|
3497
|
+
|
|
3498
|
+
### Debugging Tips
|
|
3499
|
+
|
|
3500
|
+
#### 1. Use console.log()
|
|
3501
|
+
|
|
3502
|
+
```javascript
|
|
3503
|
+
const items = $input.all();
|
|
3504
|
+
console.log("Items count:", items.length);
|
|
3505
|
+
console.log("First item:", items[0]);
|
|
3506
|
+
|
|
3507
|
+
// Check browser console (F12) for output
|
|
3508
|
+
```
|
|
3509
|
+
|
|
3510
|
+
#### 2. Return Intermediate Results
|
|
3511
|
+
|
|
3512
|
+
```javascript
|
|
3513
|
+
// Debug by returning current state
|
|
3514
|
+
const items = $input.all();
|
|
3515
|
+
const processed = items.map((item) => ({ json: item.json }));
|
|
3516
|
+
|
|
3517
|
+
// Return to see what you have
|
|
3518
|
+
return processed;
|
|
3519
|
+
```
|
|
3520
|
+
|
|
3521
|
+
#### 3. Try-Catch for Troubleshooting
|
|
3522
|
+
|
|
3523
|
+
```javascript
|
|
3524
|
+
try {
|
|
3525
|
+
// Your code here
|
|
3526
|
+
const result = riskyOperation();
|
|
3527
|
+
return [{ json: { result } }];
|
|
3528
|
+
} catch (error) {
|
|
3529
|
+
// See what failed
|
|
3530
|
+
return [
|
|
3531
|
+
{
|
|
3532
|
+
json: {
|
|
3533
|
+
error: error.message,
|
|
3534
|
+
stack: error.stack,
|
|
3535
|
+
},
|
|
3536
|
+
},
|
|
3537
|
+
];
|
|
3538
|
+
}
|
|
3539
|
+
```
|
|
3540
|
+
|
|
3541
|
+
#### 4. Validate Input Structure
|
|
3542
|
+
|
|
3543
|
+
```javascript
|
|
3544
|
+
const items = $input.all();
|
|
3545
|
+
|
|
3546
|
+
// Check what you received
|
|
3547
|
+
console.log("Input structure:", JSON.stringify(items[0], null, 2));
|
|
3548
|
+
|
|
3549
|
+
// Then process
|
|
3550
|
+
```
|
|
3551
|
+
|
|
3552
|
+
---
|
|
3553
|
+
|
|
3554
|
+
## Error Prevention Checklist
|
|
3555
|
+
|
|
3556
|
+
Use this checklist before deploying Code nodes:
|
|
3557
|
+
|
|
3558
|
+
### Code Structure
|
|
3559
|
+
|
|
3560
|
+
- [ ] Code field is not empty
|
|
3561
|
+
- [ ] Return statement exists
|
|
3562
|
+
- [ ] All code paths return data
|
|
3563
|
+
|
|
3564
|
+
### Return Format
|
|
3565
|
+
|
|
3566
|
+
- [ ] Returns array: `[...]`
|
|
3567
|
+
- [ ] Each item has `json` property: `{json: {...}}`
|
|
3568
|
+
- [ ] Format is `[{json: {...}}]`
|
|
3569
|
+
|
|
3570
|
+
### Syntax
|
|
3571
|
+
|
|
3572
|
+
- [ ] No `{{ }}` expression syntax (use JavaScript) — see [expression-syntax.md](expression-syntax.md)
|
|
3573
|
+
- [ ] Template literals use backticks: `` `${variable}` ``
|
|
3574
|
+
- [ ] All quotes and brackets balanced
|
|
3575
|
+
- [ ] Strings properly escaped
|
|
3576
|
+
|
|
3577
|
+
### Data Safety
|
|
3578
|
+
|
|
3579
|
+
- [ ] Null checks for optional properties
|
|
3580
|
+
- [ ] Array length checks before access
|
|
3581
|
+
- [ ] Webhook data accessed via `.body`
|
|
3582
|
+
- [ ] Try-catch for risky operations
|
|
3583
|
+
- [ ] Default values for missing data
|
|
3584
|
+
|
|
3585
|
+
### Testing
|
|
3586
|
+
|
|
3587
|
+
- [ ] Test with empty input
|
|
3588
|
+
- [ ] Test with missing fields
|
|
3589
|
+
- [ ] Test with unexpected data types
|
|
3590
|
+
- [ ] Check browser console for errors
|
|
3591
|
+
|
|
3592
|
+
---
|
|
3593
|
+
|
|
3594
|
+
## Best Practices
|
|
3595
|
+
|
|
3596
|
+
### 1. Always Validate Input Data
|
|
3597
|
+
|
|
3598
|
+
```javascript
|
|
3599
|
+
const items = $input.all();
|
|
3600
|
+
|
|
3601
|
+
// Check if data exists
|
|
3602
|
+
if (!items || items.length === 0) {
|
|
3603
|
+
return [];
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
// Validate structure
|
|
3607
|
+
if (!items[0].json) {
|
|
3608
|
+
return [{ json: { error: "Invalid input format" } }];
|
|
3609
|
+
}
|
|
3610
|
+
|
|
3611
|
+
// Continue processing...
|
|
3612
|
+
```
|
|
3613
|
+
|
|
3614
|
+
### 2. Use Try-Catch for Error Handling
|
|
3615
|
+
|
|
3616
|
+
```javascript
|
|
3617
|
+
try {
|
|
3618
|
+
const response = await $helpers.httpRequest({
|
|
3619
|
+
url: "https://api.example.com/data",
|
|
3620
|
+
});
|
|
3621
|
+
|
|
3622
|
+
return [{ json: { success: true, data: response } }];
|
|
3623
|
+
} catch (error) {
|
|
3624
|
+
return [
|
|
3625
|
+
{
|
|
3626
|
+
json: {
|
|
3627
|
+
success: false,
|
|
3628
|
+
error: error.message,
|
|
3629
|
+
},
|
|
3630
|
+
},
|
|
3631
|
+
];
|
|
3632
|
+
}
|
|
3633
|
+
```
|
|
3634
|
+
|
|
3635
|
+
### 3. Prefer Array Methods Over Loops
|
|
3636
|
+
|
|
3637
|
+
```javascript
|
|
3638
|
+
// GOOD: Functional approach
|
|
3639
|
+
const processed = $input
|
|
3640
|
+
.all()
|
|
3641
|
+
.filter((item) => item.json.valid)
|
|
3642
|
+
.map((item) => ({ json: { id: item.json.id } }));
|
|
3643
|
+
|
|
3644
|
+
// SLOWER: Manual loop
|
|
3645
|
+
const processed = [];
|
|
3646
|
+
for (const item of $input.all()) {
|
|
3647
|
+
if (item.json.valid) {
|
|
3648
|
+
processed.push({ json: { id: item.json.id } });
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
```
|
|
3652
|
+
|
|
3653
|
+
### 4. Filter Early, Process Late
|
|
3654
|
+
|
|
3655
|
+
```javascript
|
|
3656
|
+
// GOOD: Filter first to reduce processing
|
|
3657
|
+
const processed = $input
|
|
3658
|
+
.all()
|
|
3659
|
+
.filter((item) => item.json.status === "active") // Reduce dataset first
|
|
3660
|
+
.map((item) => expensiveTransformation(item)); // Then transform
|
|
3661
|
+
|
|
3662
|
+
// WASTEFUL: Transform everything, then filter
|
|
3663
|
+
const processed = $input
|
|
3664
|
+
.all()
|
|
3665
|
+
.map((item) => expensiveTransformation(item)) // Wastes CPU
|
|
3666
|
+
.filter((item) => item.json.status === "active");
|
|
3667
|
+
```
|
|
3668
|
+
|
|
3669
|
+
### 5. Use Descriptive Variable Names
|
|
3670
|
+
|
|
3671
|
+
```javascript
|
|
3672
|
+
// GOOD: Clear intent
|
|
3673
|
+
const activeUsers = $input.all().filter((item) => item.json.active);
|
|
3674
|
+
const totalRevenue = activeUsers.reduce(
|
|
3675
|
+
(sum, user) => sum + user.json.revenue,
|
|
3676
|
+
0,
|
|
3677
|
+
);
|
|
3678
|
+
|
|
3679
|
+
// BAD: Unclear purpose
|
|
3680
|
+
const a = $input.all().filter((item) => item.json.active);
|
|
3681
|
+
const t = a.reduce((s, u) => s + u.json.revenue, 0);
|
|
3682
|
+
```
|
|
3683
|
+
|
|
3684
|
+
### 6. Debug with console.log()
|
|
3685
|
+
|
|
3686
|
+
```javascript
|
|
3687
|
+
// Debug statements appear in browser console
|
|
3688
|
+
const items = $input.all();
|
|
3689
|
+
console.log(`Processing ${items.length} items`);
|
|
3690
|
+
|
|
3691
|
+
for (const item of items) {
|
|
3692
|
+
console.log("Item data:", item.json);
|
|
3693
|
+
// Process...
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
return result;
|
|
3697
|
+
```
|
|
3698
|
+
|
|
3699
|
+
---
|
|
3700
|
+
|
|
3701
|
+
## When to Use Code Node
|
|
3702
|
+
|
|
3703
|
+
Use Code node when:
|
|
3704
|
+
|
|
3705
|
+
- Complex transformations requiring multiple steps
|
|
3706
|
+
- Custom calculations or business logic
|
|
3707
|
+
- Recursive operations
|
|
3708
|
+
- API response parsing with complex structure
|
|
3709
|
+
- Multi-step conditionals
|
|
3710
|
+
- Data aggregation across items
|
|
3711
|
+
|
|
3712
|
+
Consider other nodes when:
|
|
3713
|
+
|
|
3714
|
+
- Simple field mapping -> Use **Set** node
|
|
3715
|
+
- Basic filtering -> Use **Filter** node
|
|
3716
|
+
- Simple conditionals -> Use **IF** or **Switch** node
|
|
3717
|
+
- HTTP requests only -> Use **HTTP Request** node
|
|
3718
|
+
|
|
3719
|
+
**Code node excels at**: Complex logic that would require chaining many simple nodes
|
|
3720
|
+
|
|
3721
|
+
---
|
|
3722
|
+
|
|
3723
|
+
## Quick Reference Checklist
|
|
3724
|
+
|
|
3725
|
+
Before deploying Code nodes, verify:
|
|
3726
|
+
|
|
3727
|
+
- [ ] **Code is not empty** - Must have meaningful logic
|
|
3728
|
+
- [ ] **Return statement exists** - Must return array of objects
|
|
3729
|
+
- [ ] **Proper return format** - Each item: `{json: {...}}`
|
|
3730
|
+
- [ ] **Data access correct** - Using `$input.all()`, `$input.first()`, or `$input.item`
|
|
3731
|
+
- [ ] **No n8n expressions** - Use JavaScript template literals: `` `${value}` `` — see [expression-syntax.md](expression-syntax.md)
|
|
3732
|
+
- [ ] **Error handling** - Guard clauses for null/undefined inputs
|
|
3733
|
+
- [ ] **Webhook data** - Access via `.body` if from webhook
|
|
3734
|
+
- [ ] **Mode selection** - "All Items" for most cases
|
|
3735
|
+
- [ ] **Performance** - Prefer map/filter over manual loops
|
|
3736
|
+
- [ ] **Output consistent** - All code paths return same structure
|
|
3737
|
+
|
|
3738
|
+
---
|
|
3739
|
+
|
|
3740
|
+
## n8n Documentation
|
|
3741
|
+
|
|
3742
|
+
- Code Node Guide: https://docs.n8n.io/code/code-node/
|
|
3743
|
+
- Built-in Methods: https://docs.n8n.io/code-examples/methods-variables-reference/
|
|
3744
|
+
- Luxon Documentation: https://moment.github.io/luxon/
|