Modules Data SourceScript

Script Bundle Module

A Script Bundle Module is a self-contained ZIP archive that provides all the logic required to expose a Script Data Source to Kubling.

A module defines:

  • How SQL operations are translated into executable logic
  • How external systems or APIs are invoked
  • How results and side effects are communicated back to the engine

This page documents the recommended module structure and the execution models supported by the engine.

The following tree illustrates a representative Script Bundle Module, based on a real integration with the JIRA API:

  • bundle-script-info.yaml
  • jira.ddl
  • init.js

Only one file is strictly required to be located at the root of the module:

  • bundle-script-info.yaml

All other paths follow conventions rather than hard constraints and may be adapted to the module’s needs.

📄 bundle-script-info.yaml

This file defines the module metadata and how it is wired into the engine.

Among other settings, it controls:

  • Which execution model is used
  • How scripts are discovered
  • How initialization and scheduled logic are registered

See the full schema here:
Script Module Bundle Information File

Table Handlers v26.1+

Table handlers define a table-centric execution model for Script Data Sources.

In this model, the engine resolves handlers dynamically based on table names and invokes operation-specific functions on demand. The association between SQL operations and script logic is expressed structurally, rather than through explicit delegate wiring.

Under the table handler model:

  • Each table is associated with a single JavaScript file
  • The file name determines which table it handles
  • SQL operations targeting that table are routed to functions defined in the file

Handlers do not return values.
They interact with the engine exclusively through exchange objects provided at invocation time.

💡

A concrete example of the table handler model can be found in the kubling-operaton repository

Handler Resolution

Handlers are discovered under a configured directory inside the module.

For each JavaScript file found:

  • The file name is mapped to a table name
  • The engine invokes functions on demand when operations target that table

Example layout:

handlers/
  ISSUE.js
  PROJECT.js

In this case, operations on tables ISSUE and PROJECT are routed to the corresponding files.

Handler Contract

A handler file may export any of the following functions (all lowercase):

  • select
  • insert
  • update
  • delete

Each function receives a single argument object composed of two distinct parts:

  • Informational input provided by the engine
  • Exchange objects used to communicate results back to the engine

The contract is:

// SELECT
select({ queryFilter, resultSet })
 
// INSERT
insert({ insertOp, affectedRows })
 
// UPDATE
update({ updateOp, affectedRows })
 
// DELETE
delete({ deleteOp, affectedRows })

Informational Inputs

Informational inputs describe what operation the engine is requesting:

These objects are provided by the engine and are treated as read-only by the handler.

Exchange Objects

Exchange objects represent the communication channel back to the engine:

  • resultSet is used to emit rows for SELECT operations
  • affectedRows is used to report affected row counts and generated keys

After handler execution, the engine inspects these objects to determine the outcome of the operation.

Execution Characteristics

The table handler model:

  • Makes table-level behavior explicit and localized
  • Eliminates explicit delegate wiring
  • Preserves a clear separation between intent (input) and effect (exchange)
  • Keeps execution deterministic from the engine’s perspective

Delegate Scripts

Delegate scripts represent an alternative execution model for Script Data Sources.

In this model, the engine invokes explicitly configured scripts for each operation type:

  • ResultSet delegate
  • Insert delegate
  • Update delegate
  • Delete delegate

Delegate-based wiring remains supported to preserve compatibility with existing modules.
However, new modules are encouraged to adopt the table handler model, as it results in simpler wiring and clearer execution semantics.

📄 Initialization Scripts

Initialization scripts allow a module to perform one-time setup logic required for correct operation.

Typical use cases include:

  • Authenticating with external systems
  • Fetching or refreshing credentials
  • Initializing shared state

If an initialization script is present, it must report its outcome to the engine.
A failure during initialization prevents the engine from starting.

The engine receives this information through the initResult context member:
initResult context member

Example:

import { generateAzureToken } from "../api/TokenGenerator";
 
try {
    generateAzureToken();
    initResult.initialized();
} catch (e) {
    initResult.error(e.message);
}

📄 Scheduled Scripts

Scheduled scripts are executed periodically based on a cron expression.

They are typically used for:

  • Refreshing credentials
  • Publishing state to external systems
  • Periodic reconciliation or notifications

Scheduled scripts should be used judiciously, as they consume engine resources.

Try to keep scheduled scripts as isolated from the rest of the module logic as possible.

In more advanced setups, scheduled scripts often interact with other systems through Data Sources, rather than calling external APIs directly.

In complex scenarios, systems such as Slack are commonly modeled as Data Sources.
A scheduled script would then perform an INSERT into a table, triggering the actual external interaction through the engine.