writing-scripts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<!-- TIER:1 -->
<!-- TIER:1 -->

Writing Scripts

编写脚本

A script is a JavaScript function that runs at a specific hook point in the Celigo data pipeline. Scripts handle logic that expressions, filters, and visual mappings cannot -- complex conditionals, cross-record calculations, API calls within the pipeline, and custom routing.
Concerns when writing a script:
  • Choosing the right hook point -- which function type matches what you're trying to accomplish
  • Input/output contracts -- what
    options
    contains and what the function must return (array length rules are strict)
  • Expression alternative -- filter, transform, and output filter have expression-based alternatives that don't require a script; prefer expressions when possible
  • Available modules -- scripts can
    import
    three built-in modules:
    integrator-api
    (call Celigo APIs),
    dayjs
    (date/time manipulation), and
    sjcl
    (Stanford JavaScript Crypto Library for hashing/encryption)
  • One script, many functions -- a single script resource can contain multiple exported functions, each wired independently to different hook points
Used across flows, APIs, and tools.
脚本是一种JavaScript函数,会在Celigo数据管道中的特定钩子点运行。脚本可处理表达式、过滤器和可视化映射无法实现的逻辑——复杂条件判断、跨记录计算、管道内API调用以及自定义路由。
编写脚本时需要注意的事项:
  • 选择合适的钩子点——哪种函数类型与你的目标匹配
  • 输入/输出约定——
    options
    包含的内容以及函数必须返回的值(数组长度规则严格)
  • 表达式替代方案——filter、transform和output filter有基于表达式的替代方案,无需使用脚本;尽可能优先使用表达式
  • 可用模块——脚本可以
    import
    三个内置模块:
    integrator-api
    (调用Celigo APIs)、
    dayjs
    (日期/时间处理)和
    sjcl
    (用于哈希/加密的Stanford JavaScript Crypto Library)
  • 单脚本多函数——单个脚本资源可包含多个导出函数,每个函数可独立关联到不同的钩子点
适用于流程、API和各类工具。

Hook Points

钩子点

Every script function runs at a specific point in the pipeline. Choose based on when you need to act and what data you need access to.
每个脚本函数都会在管道中的特定点运行。根据你需要执行操作的时机和需要访问的数据来选择。

Data Pipeline Hooks

数据管道钩子

HookRuns onWhenInputMust return
preSavePage
ExportAfter retrieval, before pipeline
options.data[]
,
errors[]
,
files[]
,
retryData{}
{ data[], errors[], abort, newErrorsAndRetryData[] }
preMap
ImportBefore field mapping
options.data[]
(unmapped records)
Array matching
data.length
:
{ data }
,
{ errors }
, or
{}
to skip
postMap
ImportAfter field mapping, before submit
options.preMapData[]
,
postMapData[]
Array matching
postMapData.length
:
{ data }
,
{ errors }
, or
{}
to skip
postSubmit
ImportAfter destination submission
options.preMapData[]
,
postMapData[]
,
responseData[]
responseData[]
(same length, modified)
postAggregate
ImportAfter file aggregation upload
options.postAggregateData: { success, _json, code, message }
void
钩子运行场景触发时机输入必须返回
preSavePage
导出数据检索后、管道处理前
options.data[]
,
errors[]
,
files[]
,
retryData{}
{ data[], errors[], abort, newErrorsAndRetryData[] }
preMap
导入字段映射前
options.data[]
(未映射记录)
data.length
匹配的数组:
{ data }
{ errors }
{}
(跳过)
postMap
导入字段映射后、提交前
options.preMapData[]
,
postMapData[]
postMapData.length
匹配的数组:
{ data }
{ errors }
{}
(跳过)
postSubmit
导入目标端提交后
options.preMapData[]
,
postMapData[]
,
responseData[]
responseData[]
(长度相同,可修改)
postAggregate
导入文件聚合上传后
options.postAggregateData: { success, _json, code, message }
void

Record-Level Processors (on export or import)

记录级处理器(导出或导入时)

HookWhenInputMust return
filter
Per-record, before processing
options.record
boolean
(true = process)
input_filter
Per-record on lookup exports
options.record
boolean
(true = include)
transform
Per-record, reshaping before mapping
options.record
Transformed record
filter and transform have expression-based alternatives. Only use a script when the logic is too complex for an expression (multi-field conditionals, date math, external lookups).
钩子触发时机输入必须返回
filter
每条记录处理前
options.record
boolean
(true = 处理)
input_filter
查找导出时的每条记录
options.record
boolean
(true = 包含)
transform
每条记录处理前、映射前重构
options.record
转换后的记录
filter和transform有基于表达式的替代方案。只有当逻辑过于复杂(多字段条件、日期运算、外部查找)无法用表达式实现时,才使用脚本。

Flow-Level Hook

流程级钩子

HookRuns onWhenInputMust return
postResponseMap
Page processor (flow/API/tool)After response mapping merges results
options.postResponseMapData[]
,
responseData[]
postResponseMapData[]
(same length)
Configured on the flow's
pageProcessors[]
entry, not on the export/import. Plan this hook when building the resource, but wire it at the flow level.
钩子运行场景触发时机输入必须返回
postResponseMap
页面处理器(流程/API/工具)响应映射合并结果后
options.postResponseMapData[]
,
responseData[]
postResponseMapData[]
(长度相同)
配置在流程的
pageProcessors[]
条目上,而非导出/导入步骤。在构建资源时规划该钩子,但在流程层面关联配置。

Routing and Handlers

路由与处理器

HookRuns onWhenInputMust return
branching
RouterPer-record routing decision
options.record
,
settings
number[]
(branch indices, e.g.,
[0, 2]
)
handleRequest
API resourceIncoming HTTP request (script-mode API)
options.method
,
headers
,
queryString
,
body
,
rawBody
{ statusCode, headers?, body }
contentBasedFlowRouter
AS2 connectionEDI message routing
options.httpHeaders
,
mimeHeaders
,
rawMessageBody
{ _flowId, _exportId }
钩子运行场景触发时机输入必须返回
branching
路由器每条记录的路由决策
options.record
,
settings
number[]
(分支索引,例如
[0, 2]
handleRequest
API资源传入的HTTP请求(脚本模式API)
options.method
,
headers
,
queryString
,
body
,
rawBody
{ statusCode, headers?, body }
contentBasedFlowRouter
AS2连接EDI消息路由
options.httpHeaders
,
mimeHeaders
,
rawMessageBody
{ _flowId, _exportId }

Quick Reference

快速参考

Hook Point Decision Matrix

钩子点决策矩阵

When you need to...Use hookConfigured onInput / Output
Transform or filter a batch after retrieval
preSavePage
ExportReceives pages of records, returns pages (with optional errors)
Filter individual records before processing
filter
Export or importReceives single record, returns boolean (true = keep)
Filter records entering a lookup export
input_filter
Export (lookup)Receives single record, returns boolean (true = include)
Reshape records before mapping
transform
Export or importReceives single record, returns transformed record
Transform records before field mapping
preMap
ImportReceives unmapped records array, returns array (same length)
Transform records after field mapping
postMap
ImportReceives pre-map + post-map arrays, returns array (same length)
Process API responses after submission
postSubmit
ImportReceives pre-map, post-map, and response arrays, returns response array
Handle results after file aggregation
postAggregate
Import (file)Receives aggregation result, returns void
Post-response processing (merge lookup/import results)
postResponseMap
Flow
pageProcessors[]
entry
Receives merged records + response data, returns merged records (same length)
Route records to branches
branching
Router in flow/toolReceives single record + settings, returns branch indices array
Handle incoming HTTP requests (script-mode API)
handleRequest
API resourceReceives method, headers, query, body; returns
{ statusCode, headers?, body }
Route EDI messages to flows
contentBasedFlowRouter
AS2 connectionReceives HTTP/MIME headers + raw body, returns
{ _flowId, _exportId }
当你需要...使用钩子配置位置输入/输出
检索后转换或过滤批量数据
preSavePage
导出接收记录分页,返回分页结果(可包含错误信息)
处理前过滤单条记录
filter
导出或导入接收单条记录,返回布尔值(true = 保留)
过滤进入查找导出的记录
input_filter
导出(查找)接收单条记录,返回布尔值(true = 包含)
映射前重构记录
transform
导出或导入接收单条记录,返回转换后的记录
字段映射前转换记录
preMap
导入接收未映射记录数组,返回长度相同的数组
字段映射后转换记录
postMap
导入接收映射前+映射后数组,返回长度相同的数组
提交后处理API响应
postSubmit
导入接收映射前、映射后和响应数组,返回响应数组
文件聚合后处理结果
postAggregate
导入(文件)接收聚合结果,无返回值
响应后处理(合并查找/导入结果)
postResponseMap
流程
pageProcessors[]
条目
接收合并记录+响应数据,返回长度相同的合并记录
将路由记录到分支
branching
流程/工具中的路由器接收单条记录+设置,返回分支索引数组
处理传入的HTTP请求(脚本模式API)
handleRequest
API资源接收方法、头信息、查询参数、请求体;返回
{ statusCode, headers?, body }
将EDI消息路由到流程
contentBasedFlowRouter
AS2连接接收HTTP/MIME头信息+原始请求体,返回
{ _flowId, _exportId }

Minimum Required Fields

必填字段

A script resource needs only two fields:
  • name
    -- descriptive name (convention:
    <System> - <step> - <hookType>
    , e.g.,
    "Salesforce - getBatchRecords - postResponseMap"
    )
  • content
    -- the JavaScript source code as a string
See references/schemas/request.yml for the full create/update schema.
脚本资源仅需两个字段:
  • name
    ——描述性名称(惯例:
    <系统> - <步骤> - <钩子类型>
    ,例如
    "Salesforce - getBatchRecords - postResponseMap"
  • content
    ——字符串格式的JavaScript源代码
完整的创建/更新 schema 请参考references/schemas/request.yml

Related Skills

相关技能

  • configuring-exports > Quick Reference -- export configuration, where
    preSavePage
    ,
    filter
    ,
    transform
    , and
    input_filter
    hooks are wired
  • configuring-imports > Quick Reference -- import configuration, where
    preMap
    ,
    postMap
    ,
    postSubmit
    , and
    postAggregate
    hooks are wired
  • building-flows > How to Build a Flow -- flow construction, where
    postResponseMap
    and
    branching
    hooks are wired
  • writing-handlebars > Quick Reference -- Handlebars expressions for dynamic values in scripts and hook configurations
<!-- TIER:2 -->
  • configuring-exports > Quick Reference——导出配置,
    preSavePage
    filter
    transform
    input_filter
    钩子在此关联
  • configuring-imports > Quick Reference——导入配置,
    preMap
    postMap
    postSubmit
    postAggregate
    钩子在此关联
  • building-flows > How to Build a Flow——流程构建,
    postResponseMap
    branching
    钩子在此关联
  • writing-handlebars > Quick Reference——用于脚本和钩子配置中动态值的Handlebars表达式
<!-- TIER:2 -->

Common Options Available to All Hooks

所有钩子通用的选项

Most hooks receive these context fields in
options
:
  • _flowId
    ,
    _integrationId
    ,
    _apiId
    ,
    _parentIntegrationId
    -- execution context IDs
  • _exportId
    or
    _importId
    -- the step's resource ID
  • _connectionId
    -- the connection in use
  • settings
    -- custom settings in scope for the resource
  • testMode
    -- boolean, whether running in test/preview mode
  • job
    -- the current job object
大多数钩子的
options
中会包含以下上下文字段:
  • _flowId
    ,
    _integrationId
    ,
    _apiId
    ,
    _parentIntegrationId
    ——执行上下文ID
  • _exportId
    _importId
    ——步骤的资源ID
  • _connectionId
    ——正在使用的连接ID
  • settings
    ——资源范围内的自定义设置
  • testMode
    ——布尔值,是否处于测试/预览模式
  • job
    ——当前作业对象

Function Point Categories

函数点分类

Scripts run at twelve function points, grouped into four categories. The Hook Points tables above give each one's input/output contract; this is the mental model for which kind of point you're wiring and whether a non-script alternative exists.
  • Step-level pipeline hooks (on the export or import) --
    preSavePage
    ,
    preMap
    ,
    postMap
    ,
    postSubmit
    ,
    postAggregate
  • Parent-level response hook (on the flow/API/tool
    pageProcessors[]
    entry, not the step) --
    postResponseMap
  • Script-mode replacements for declarative slots --
    filter
    ,
    input_filter
    ,
    transform
    ,
    branching
  • Resource-specific function points --
    contentBasedFlowRouter
    (on an AS2 connection) and
    handleRequest
    (on a script-mode API)
Script-only points have no declarative equivalent:
postSubmit
,
postResponseMap
,
postAggregate
,
contentBasedFlowRouter
, and
handleRequest
. On those slots a script is the only option. The four script-mode slots (
filter
,
input_filter
,
transform
,
branching
) each hold either a declarative rule tree or a script -- never both -- so prefer the declarative path there unless the logic genuinely can't be expressed as rules (see Declarative vs Script Mode).
脚本在12个函数点运行,分为4类。上述钩子点表格列出了每个函数点的输入/输出约定;以下是关于你要关联的函数点类型以及是否存在非脚本替代方案的思维模型。
  • 步骤级管道钩子(在导出或导入步骤)——
    preSavePage
    preMap
    postMap
    postSubmit
    postAggregate
  • 父级响应钩子(在流程/API/工具的
    pageProcessors[]
    条目,而非步骤)——
    postResponseMap
  • 声明式插槽的脚本模式替代方案——
    filter
    input_filter
    transform
    branching
  • 资源特定函数点——
    contentBasedFlowRouter
    (在AS2连接)和
    handleRequest
    (在脚本模式API)
仅脚本可用的点没有声明式等效方案
postSubmit
postResponseMap
postAggregate
contentBasedFlowRouter
handleRequest
。在这些插槽中,脚本是唯一选项。四个脚本模式插槽(
filter
input_filter
transform
branching
)每个只能容纳声明式规则树或脚本——不能同时存在,因此除非逻辑确实无法用规则表达(请参阅声明式 vs 脚本模式),否则优先选择声明式方式。

Declarative vs Script Mode

声明式 vs 脚本模式

The four mode-switchable slots --
filter
,
input_filter
,
transform
, and
branching
-- hold a declarative rule tree or a script reference at any one moment, not both. Because the slot's contents change, switching modes is a two-part operation.
From script mode to declarative mode (the common direction -- prototype with a script, then clean up):
  1. Clear the script from the slot. The slot reverts to declarative mode by default.
  2. Author the declarative rule for that slot (rules-engine filter, Mapper 2.0 transform, or router input-filter rule).
From declarative mode to script mode (rarer -- the rules engine couldn't express what you need):
  1. Wire a script into the slot. The declarative rules already there are replaced by the script reference automatically.
Wiring a script and clearing it are mirror operations on the same slot. Recognize the mode-swap in phrasing like "switch the filter to rules", "convert this transform back to expressions", or "use a script for this filter instead of rules".
四个可切换模式的插槽——
filter
input_filter
transform
branching
——在任何时候只能容纳声明式规则树或脚本引用,不能同时存在。由于插槽内容会变化,切换模式需要两步操作。
从脚本模式切换到声明式模式(常见方向——用脚本原型开发,然后优化):
  1. 清除插槽中的脚本。插槽默认恢复为声明式模式。
  2. 为该插槽编写声明式规则(规则引擎过滤器、Mapper 2.0转换器或路由器输入过滤规则)。
从声明式模式切换到脚本模式(较少见——规则引擎无法表达需求):
  1. 将脚本关联到插槽。已有的声明式规则会自动被脚本引用替换。
关联脚本和清除脚本是同一插槽的镜像操作。可以通过以下表述识别模式切换:"将过滤器切换为规则""将此转换器转换回表达式""使用脚本替代规则实现此过滤器"

How to Write a Script

如何编写脚本

1. Determine what you need to accomplish

1. 明确目标

Map your goal to the right hook point using the Hook Point Decision Matrix above.
使用上述钩子点决策矩阵将你的目标映射到合适的钩子点。

2. Check if an expression can handle it

2. 检查是否可用表达式实现

Filter, transform, and output filter all have expression-based alternatives. Expressions are simpler to maintain and don't require a script resource. Use a script only when you need:
  • Multi-step logic or loops
  • Cross-record calculations (totals, deduplication)
  • External API calls via
    integrator-api
  • Error handling with retry data
  • Access to
    preMapData
    alongside
    postMapData
Filter、transform和output filter都有基于表达式的替代方案。表达式更易于维护,且无需脚本资源。仅当你需要以下功能时才使用脚本:
  • 多步骤逻辑或循环
  • 跨记录计算(总计、去重)
  • 通过
    integrator-api
    调用外部API
  • 带重试数据的错误处理
  • 同时访问
    preMapData
    postMapData

3. Check for existing scripts in the account

3. 检查账户中已有的脚本

bash
celigo scripts list
celigo scripts get <id>   # content is only returned on individual GET
bash
celigo scripts list
celigo scripts get <id>   # 仅单个GET请求会返回content内容

4. Create the script resource

4. 创建脚本资源

Build the script with the correct function name matching the hook point. A single script can contain multiple functions.
See references/schemas/request.yml for the create/update schema and references/schemas/response.yml for the response shape.
Key fields:
  • name
    -- descriptive name (convention:
    <System> - <step> - <hookType>
    , e.g.,
    "Salesforce - getBatchRecords - postResponseMap"
    )
  • content
    -- the JavaScript source code
编写脚本时,使用与钩子点匹配的正确函数名。单个脚本可包含多个函数。
创建/更新 schema 请参考references/schemas/request.yml,响应结构请参考references/schemas/response.yml
关键字段:
  • name
    ——描述性名称(惯例:
    <系统> - <步骤> - <钩子类型>
    ,例如
    "Salesforce - getBatchRecords - postResponseMap"
  • content
    ——JavaScript源代码

5. Wire the script to the resource

5. 将脚本关联到资源

Wiring depends on the hook type:
HookWiring patternWhere
preSavePage
,
preMap
,
postMap
,
postSubmit
,
postAggregate
hooks.{hookType}: { _scriptId, function }
Export or import resource
filter
,
input_filter
,
transform
{field}: { type: "script", script: { _scriptId, function } }
Export or import resource
postResponseMap
hooks.postResponseMap: { _scriptId, function }
Flow
pageProcessors[]
entry
branching
routeRecordsUsing: "script"
+ script reference
Router in flow
handleRequest
script: { _scriptId, function }
+
type: "script"
API resource
contentBasedFlowRouter
as2.contentBasedFlowRouter: { _scriptId, function }
AS2 connection
Hook-based attachment (preSavePage, preMap, etc.) is additive -- adding a hook doesn't remove existing config. Replace-based attachment (filter, transform) replaces the existing filter/transform expression.
关联方式取决于钩子类型:
钩子关联模式位置
preSavePage
,
preMap
,
postMap
,
postSubmit
,
postAggregate
hooks.{hookType}: { _scriptId, function }
导出或导入资源
filter
,
input_filter
,
transform
{field}: { type: "script", script: { _scriptId, function } }
导出或导入资源
postResponseMap
hooks.postResponseMap: { _scriptId, function }
流程
pageProcessors[]
条目
branching
routeRecordsUsing: "script"
+ 脚本引用
流程中的路由器
handleRequest
script: { _scriptId, function }
+
type: "script"
API资源
contentBasedFlowRouter
as2.contentBasedFlowRouter: { _scriptId, function }
AS2连接
基于钩子的关联(preSavePage、preMap等)是叠加式的——添加钩子不会移除现有配置。替代式关联(filter、transform)会替换现有的过滤器/转换表达式。

6. Test and iterate

6. 测试与迭代

bash
undefined
bash
undefined

Enable debug logging on the script

启用脚本的调试日志

celigo scripts enable-debug <script-id>
celigo scripts enable-debug <script-id>

Run the flow or API that triggers the script

运行触发脚本的流程或API

celigo flows run <flow-id> -y
celigo flows run <flow-id> -y

Check debug logs

查看调试日志

celigo scripts debug-logs <script-id> --since 30
celigo scripts debug-logs <script-id> --since 30

Check execution logs

查看执行日志

celigo scripts debug-logs <script-id> --level error --limit 20
celigo scripts debug-logs <script-id> --level error --limit 20

Disable debug when done

测试完成后禁用调试

celigo scripts disable-debug <script-id>
undefined
celigo scripts disable-debug <script-id>
undefined

Available Modules

可用模块

Scripts can import three built-in modules:
脚本可以导入三个内置模块:

integrator-api

integrator-api

Call Celigo APIs from within the script -- run exports, read connections, trigger imports.
javascript
import { exports, imports, connections } from 'integrator-api'

const result = exports.run({ _id: 'exportId' })
const conn = connections.get({ _id: 'connectionId' })
Useful in
preSavePage
for enrichment,
handleRequest
for orchestration, and
postSubmit
for triggering downstream processes.
在脚本内调用Celigo APIs——运行导出、读取连接、触发导入。
javascript
import { exports, imports, connections } from 'integrator-api'

const result = exports.run({ _id: 'exportId' })
const conn = connections.get({ _id: 'connectionId' })
适用于
preSavePage
中的数据增强、
handleRequest
中的编排以及
postSubmit
中的下游流程触发。

dayjs

dayjs

Date and time manipulation. Handles parsing, formatting, diffing, and timezone conversions without manual date math.
javascript
import dayjs from 'dayjs'

const formatted = dayjs(record.createdAt).format('YYYY-MM-DD')
const isRecent = dayjs().diff(dayjs(record.updatedAt), 'day') < 7
日期和时间处理。无需手动日期运算即可处理解析、格式化、差值计算和时区转换。
javascript
import dayjs from 'dayjs'

const formatted = dayjs(record.createdAt).format('YYYY-MM-DD')
const isRecent = dayjs().diff(dayjs(record.updatedAt), 'day') < 7

sjcl

sjcl

Stanford JavaScript Crypto Library for hashing, encryption, and HMAC generation.
javascript
import sjcl from 'sjcl'

const hash = sjcl.hash.sha256.hash(payload)
const hexDigest = sjcl.codec.hex.fromBits(hash)
用于哈希、加密和HMAC生成的Stanford JavaScript Crypto Library。
javascript
import sjcl from 'sjcl'

const hash = sjcl.hash.sha256.hash(payload)
const hexDigest = sjcl.codec.hex.fromBits(hash)

CLI Commands

CLI命令

CRUD

CRUD操作

bash
celigo scripts list
celigo scripts get <id>
celigo scripts create < script.json
celigo scripts update <id> < script.json
celigo scripts set <id> name="New Name"
celigo scripts delete <id>
bash
celigo scripts list
celigo scripts get <id>
celigo scripts create < script.json
celigo scripts update <id> < script.json
celigo scripts set <id> name="New Name"
celigo scripts delete <id>

Logs and Debugging

日志与调试

bash
celigo scripts debug-logs <id> [--limit N] [--offset N] [--level error|warn|info|debug] [--start-date ISO] [--end-date ISO]
celigo scripts enable-debug <id> [--duration <minutes>]
celigo scripts disable-debug <id>
celigo scripts debug-logs <id> [--since <minutes>] [--flow-id <id>]
bash
celigo scripts debug-logs <id> [--limit N] [--offset N] [--level error|warn|info|debug] [--start-date ISO] [--end-date ISO]
celigo scripts enable-debug <id> [--duration <minutes>]
celigo scripts disable-debug <id>
celigo scripts debug-logs <id> [--since <minutes>] [--flow-id <id>]

Authoring Against Sample Data

基于示例数据编写脚本

Script logic is runtime-dependent -- it only works against the specific shape of data it handles -- so a script is written and validated against a sample input. The sample comes from the step's recent runs, a
test
/
run
capture on the parent flow, or a JSON example you supply. A script written without sample data is written blind.
Treat authoring as a loop, not a one-shot:
  1. Generate or edit the function against the sample input.
  2. Run it against that sample.
  3. Check the output for errors or obviously-wrong results.
  4. Iterate -- refine and re-run until it passes.
A script that fails on the first pass isn't a failure; it's the first turn of the loop -- the runtime error and the code are both visible, so the next pass is informed by what went wrong. When no sample is available (the step has never run and no parent provided records), supply a JSON example before writing the script; a user-supplied sample plays the same validation role as captured runtime data.
脚本逻辑依赖运行时环境——仅能处理特定结构的数据——因此脚本需基于示例输入编写和验证。示例数据来自步骤的近期运行记录、父流程的
test
/
run
捕获结果,或你提供的JSON示例。未基于示例数据编写的脚本相当于盲写。
将编写过程视为循环,而非一次性操作:
  1. 生成或编辑针对示例输入的函数。
  2. 运行脚本处理该示例数据。
  3. 检查输出是否存在错误或明显错误的结果。
  4. 迭代——优化并重新运行,直到通过测试。
首次运行失败的脚本并非失败;这只是循环的第一轮——运行时错误和代码都可见,因此下一轮可以根据错误信息进行优化。当没有可用示例数据时(步骤从未运行且父流程未提供记录),编写脚本前先提供JSON示例;用户提供的示例与捕获的运行时数据具有相同的验证作用。

Execution Logs and the Debug Window

执行日志与调试窗口

Scripts write to a per-script execution log using standard
console
methods. What gets captured depends on the level:
  • console.error()
    ,
    console.warn()
    ,
    console.info()
    , and
    console.log()
    are always captured -- no setup, no toggle (
    info
    and
    log
    are equivalent).
  • console.debug()
    is gated: its output is persisted only while a time-bounded debug window is open on the script. When the window is closed,
    console.debug()
    still runs but its output is dropped.
"Debugging a script" here means exactly this -- turning on
console.debug()
capture for a window. It is not breakpoint-style debugging; there is no pausing or stepping through code. Open a window only when you need
console.debug()
output; for "why did this fail?" / "what errors happened?", the always-captured error/warn/info/log entries are usually enough.
The debug window is time-bounded and expires automatically -- it defaults to a short window (15 minutes) and is opened with
celigo scripts enable-debug <id> [--duration <minutes>]
. There's no need to close it manually, though
celigo scripts disable-debug <id>
ends it early.
Each log entry records its time, level (
INFO
/
WARN
/
ERROR
/
DEBUG
), the message, and two locating fields:
  • functionType
    -- which hook produced the entry (
    preMap
    ,
    postSubmit
    , etc.)
  • _resourceId
    -- the export or import that ran the hook
Because one script can carry many functions across many hook sites, an unfiltered log stream interleaves entries from every consumer. Filter aggressively when reading -- by flow (
--flow-id
), by time (
--since
/
--start-date
/
--end-date
), and by level (
--level
). The practical query is "logs for this script, in this flow, on this step, during this window."
<!-- TIER:3 -->
脚本使用标准
console
方法写入每个脚本专属的执行日志。捕获的内容取决于日志级别:
  • console.error()
    console.warn()
    console.info()
    console.log()
    始终会被捕获——无需设置或切换(
    info
    log
    等效)。
  • console.debug()
    受限制:仅当脚本的限时调试窗口开启时,其输出才会被持久化。窗口关闭后,
    console.debug()
    仍会执行,但输出会被丢弃。
此处的"调试脚本"特指——为脚本开启
console.debug()
捕获窗口。这并非断点式调试;无法暂停或单步执行代码。仅当需要
console.debug()
输出时才开启窗口;对于"为什么失败?" / "发生了什么错误?",始终捕获的error/warn/info/log条目通常足够。
调试窗口是限时的,会自动过期——默认短窗口(15分钟),可通过
celigo scripts enable-debug <id> [--duration <minutes>]
开启。无需手动关闭,不过
celigo scripts disable-debug <id>
可提前结束。
每条日志条目会记录时间、级别(
INFO
/
WARN
/
ERROR
/
DEBUG
)、消息以及两个定位字段:
  • functionType
    ——生成条目的钩子类型(
    preMap
    postSubmit
    等)
  • _resourceId
    ——运行该钩子的导出或导入步骤ID
由于一个脚本可包含多个函数并用于多个钩子点,未过滤的日志流会交错显示所有使用该脚本的条目。读取时需严格过滤——按流程(
--flow-id
)、时间(
--since
/
--start-date
/
--end-date
)和级别(
--level
)。常用查询为"该脚本在指定流程、指定步骤、指定时间范围内的日志"。
<!-- TIER:3 -->

Pre-Submit Checklist

提交前检查清单

Before creating or updating a script, verify:
  • Hook point is correct -- the function name matches the hook type being wired (e.g.,
    preSavePage
    function for a
    hooks.preSavePage
    reference)
  • Return value matches contract -- batch hooks (
    preMap
    ,
    postMap
    ,
    postSubmit
    ,
    postResponseMap
    ) return arrays that match the input array length exactly
  • Error handling uses return pattern, not throw -- per-record errors use
    { errors: [...] }
    return values, not thrown exceptions (which fail the entire page)
  • Expression alternative considered -- filter, transform, and output filter can use expressions; only use a script when expressions cannot handle the logic
  • content
    field is included on PUT
    -- omitting
    content
    on update erases the code; always GET first, modify, then PUT
  • Debug mode is disabled after testing --
    celigo scripts disable-debug <id>
    to avoid log noise in production
创建或更新脚本前,请验证:
  • 钩子点正确——函数名与要关联的钩子类型匹配(例如
    preSavePage
    函数对应
    hooks.preSavePage
    引用)
  • 返回值符合约定——批量钩子(
    preMap
    postMap
    postSubmit
    postResponseMap
    )返回的数组必须与输入数组长度完全一致
  • 错误处理使用返回模式而非抛出异常——单条记录的错误使用
    { errors: [...] }
    返回值,而非抛出异常(会导致整个分页失败)
  • 已考虑表达式替代方案——filter、transform和output filter可使用表达式;仅当表达式无法处理逻辑时才使用脚本
  • PUT请求包含
    content
    字段
    ——更新时省略
    content
    会清除代码;始终先GET、修改,再PUT
  • 测试后已禁用调试模式——执行
    celigo scripts disable-debug <id>
    避免生产环境日志冗余

Gotchas

注意事项

  1. Array length contracts are strict.
    preMap
    ,
    postMap
    , and
    postResponseMap
    return arrays MUST match the input array length. Returning fewer or more elements fails the entire page silently or with cryptic errors.
  2. abort: true
    stops pagination, not the flow.
    In
    preSavePage
    , setting
    abort: true
    tells the export to stop generating new pages. It does NOT stop the flow or cancel processing of the current page's records.
  3. Script
    content
    is not returned in list responses.
    celigo scripts list
    shows metadata only. You must
    celigo scripts get <id>
    to see the actual JavaScript code.
  4. PUT erases
    content
    if omitted.
    Always GET the script first, modify, then PUT the complete object. The
    set
    command handles this automatically.
  5. One script resource can contain multiple functions. A single script with both
    preSavePage
    and
    preMap
    functions can be wired to different resources by specifying the
    function
    name in each hook reference.
  6. Throwing an exception fails the entire page. In batch hooks (preSavePage, preMap, postMap, postSubmit), an unhandled exception fails ALL records on that page, not just one. Use the error return pattern (
    { errors: [...] }
    ) for per-record errors.
  7. postResponseMap
    lives on the flow, not the resource.
    The hook is configured on the
    pageProcessors[]
    entry in the flow/API/tool, even though it processes export or import response data.
  8. filter/transform scripts replace expression-based alternatives. Wiring a script filter replaces any existing expression filter. They cannot coexist on the same resource.
  9. console.log()
    output goes to script logs, not stdout.
    Use
    celigo scripts debug-logs
    to see output. Logs require debug mode to be enabled for debug-level messages.
  10. Only
    console.debug()
    needs the debug window.
    error
    /
    warn
    /
    info
    /
    log
    are always captured;
    debug
    output is persisted only while a time-bounded debug window is open (
    celigo scripts enable-debug
    ). A closed window silently drops
    console.debug()
    output.
  11. Shared-script logs interleave across hook sites. One script can hold many functions used by many exports/imports, so its log stream mixes entries from every consumer. Filter by flow, level, and time when reading; each entry's
    functionType
    and
    _resourceId
    identify where it came from.
  12. Clearing a script-mode filter/transform reverts the slot to declarative mode. The four mode-switchable slots (
    filter
    ,
    input_filter
    ,
    transform
    ,
    branching
    ) hold a rule tree or a script, never both -- removing the script drops the slot back to rules, and wiring a script replaces the rules.
  1. 数组长度约定严格
    preMap
    postMap
    postResponseMap
    返回的数组必须与输入数组长度一致。返回元素过多或过少会导致整个分页静默失败或出现模糊错误。
  2. abort: true
    停止分页而非流程
    。在
    preSavePage
    中设置
    abort: true
    会告知导出停止生成新分页。但不会停止流程或取消当前分页记录的处理。
  3. 脚本
    content
    不会在列表响应中返回
    celigo scripts list
    仅显示元数据。必须执行
    celigo scripts get <id>
    才能查看实际JavaScript代码。
  4. PUT请求若省略
    content
    会清除内容
    。始终先GET脚本、修改,再PUT完整对象。
    set
    命令会自动处理此操作。
  5. 单个脚本资源可包含多个函数。同时包含
    preSavePage
    preMap
    函数的单个脚本可通过在每个钩子引用中指定
    function
    名称,关联到不同资源。
  6. 抛出异常会导致整个分页失败。在批量钩子(preSavePage、preMap、postMap、postSubmit)中,未处理的异常会导致该分页所有记录失败,而非单条记录。使用错误返回模式(
    { errors: [...] }
    )处理单条记录错误。
  7. postResponseMap
    属于流程而非资源
    。该钩子配置在流程/API/工具的
    pageProcessors[]
    条目上,尽管它处理的是导出或导入的响应数据。
  8. filter/transform脚本会替代基于表达式的方案。关联脚本过滤器会替换任何现有的表达式过滤器。它们无法在同一资源上共存。
  9. console.log()
    输出会写入脚本日志而非stdout
    。使用
    celigo scripts debug-logs
    查看输出。调试级消息需要启用调试模式才能记录。
  10. console.debug()
    需要调试窗口
    error
    /
    warn
    /
    info
    /
    log
    始终会被捕获;
    debug
    输出仅在限时调试窗口开启时(
    celigo scripts enable-debug
    )才会被持久化。窗口关闭后,
    console.debug()
    输出会被静默丢弃。
  11. 共享脚本的日志会在多个钩子点交错显示。一个脚本可包含多个函数并用于多个导出/导入步骤,因此其日志流会混合所有使用该脚本的条目。读取时按流程、级别和时间过滤;每条条目的
    functionType
    _resourceId
    可标识其来源。
  12. 清除脚本模式的filter/transform会将插槽恢复为声明式模式。四个可切换模式的插槽(
    filter
    input_filter
    transform
    branching
    )只能容纳规则树或脚本,不能同时存在——移除脚本会将插槽恢复为规则模式,关联脚本会替换规则。

Common Errors

常见错误

Error / SymptomCauseFix
"The number of elements in the return value must match the input"Batch hook return array length differs from inputEnsure return array has exactly
data.length
(preMap) or
postMapData.length
(postMap) elements; use
{}
for skipped records
All records on a page fail with no per-record detailUnhandled exception thrown in batch hookWrap logic in try/catch; return
{ errors: [...] }
per record instead of throwing
Script content is empty after updatePUT omitted the
content
field
Always GET first, modify, then PUT the complete object (or use
celigo scripts set
)
abort: true
set but flow keeps running
abort
only stops pagination; current page still processes
This is expected behavior; use error returns or filter to skip individual records
Script not executing / no logsScript not wired to any resource, or debug mode not enabledVerify
_scriptId
+
function
reference on the export/import/flow; enable debug with
celigo scripts enable-debug
"Function not found" or similar
function
name in hook reference doesn't match an exported function in the script
Check the function name matches exactly (case-sensitive) between the hook config and the script's
export
Filter always returns all/no recordsFilter function returns truthy/falsy value instead of strict booleanReturn explicit
true
or
false
; avoid returning objects or undefined
postResponseMap
not firing
Hook wired on the import/export instead of the flow's
pageProcessors[]
entry
Move the hook config to the
pageProcessors[]
entry in the flow, not the resource
console.debug()
lines missing from logs
No debug window was open while the script ranOpen a window first (
celigo scripts enable-debug <id>
), then reproduce; error/warn/info/log don't require it
Log stream is a confusing mix of unrelated entriesScript is shared across many hooks/flows and the query is unfilteredFilter by
--flow-id
,
--level
, and date range; use each entry's
functionType
/
_resourceId
to identify the origin
错误/症状原因修复方案
"返回值中的元素数量必须与输入匹配"批量钩子返回的数组长度与输入不同确保返回数组的长度恰好等于
data.length
(preMap)或
postMapData.length
(postMap);使用
{}
跳过记录
分页中所有记录失败且无单条记录详情批量钩子中抛出未处理的异常将逻辑包裹在try/catch中;使用
{ errors: [...] }
返回单条记录错误,而非抛出异常
更新后脚本内容为空PUT请求省略了
content
字段
始终先GET、修改,再PUT完整对象(或使用
celigo scripts set
设置
abort: true
但流程仍继续运行
abort
仅停止分页;当前分页仍会处理
这是预期行为;使用错误返回或过滤器跳过单条记录
脚本未执行/无日志脚本未关联到任何资源,或未启用调试模式验证导出/导入/流程上的
_scriptId
+
function
引用;使用
celigo scripts enable-debug
启用调试
"未找到函数"或类似错误钩子引用中的
function
名称与脚本中的导出函数不匹配
检查钩子配置与脚本
export
中的函数名称是否完全匹配(区分大小写)
过滤器始终返回所有/无记录过滤器函数返回真值/假值而非严格布尔值返回明确的
true
false
;避免返回对象或undefined
postResponseMap
未触发
钩子关联到了导入/导出而非流程的
pageProcessors[]
条目
将钩子配置移至流程的
pageProcessors[]
条目,而非资源
console.debug()
行未出现在日志中
脚本运行时未开启调试窗口先开启窗口(
celigo scripts enable-debug <id>
),再重现场景;error/warn/info/log无需开启窗口
日志流混杂无关条目脚本在多个钩子/流程中共享且查询未过滤
--flow-id
--level
和日期范围过滤;使用每条条目的
functionType
/
_resourceId
识别来源