@enfocussw/switch-scripting-context 25.11.0-beta.10

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.
@@ -0,0 +1,51 @@
1
+ # Debugging Scripts
2
+
3
+ Debugging requires configuring the Script element in Switch Designer — the agent cannot do this directly but can guide the user through the steps.
4
+
5
+ ## Constraints
6
+
7
+ - Only works with script folders and non-password-protected packages
8
+ - Only one script can be in debug mode at a time
9
+ - Execution mode is forced to Serialized while debug is enabled
10
+ - Only these four entry points can be debugged: `jobArrived`, `timerFired`, `httpRequestTriggeredAsync`, `findExternalEditorPath`
11
+
12
+ ## Enabling debug mode in Switch Designer
13
+
14
+ In the Script element properties, once a script folder or package is selected, three extra properties appear:
15
+
16
+ | Property | Value |
17
+ |---|---|
18
+ | Enable debug mode | Yes |
19
+ | Port | 9229 (default) |
20
+ | Debug entry points | Select from the supported entry points above |
21
+
22
+ ## Attaching VS Code
23
+
24
+ **Script folder:** Open the script folder in VS Code and press F5. The `.vscode/launch.json` created by `SwitchScriptTool --create` is pre-configured for attach mode.
25
+
26
+ **Package (no `launch.json`):** Create `launch.json` manually:
27
+
28
+ ```json
29
+ {
30
+ "version": "0.2.0",
31
+ "configurations": [
32
+ {
33
+ "type": "node",
34
+ "request": "attach",
35
+ "name": "Attach",
36
+ "port": 9229,
37
+ "skipFiles": ["<node_internals>/**"]
38
+ }
39
+ ]
40
+ }
41
+ ```
42
+
43
+ Use the port number set in the Script element properties.
44
+
45
+ After attaching, VS Code stops in internal Switch code first — press F5 once more to reach the script entry point. Set breakpoints anywhere in the entry point being debugged.
46
+
47
+ ## Critical warning
48
+
49
+ **Disable debug mode in Switch Designer when the debug session is finished.**
50
+
51
+ If left enabled, the flow hangs silently — `jobArrived` leaves jobs stuck in the input folder, other entry points hang with no visible error or indication.
@@ -0,0 +1,167 @@
1
+ # Document Classes
2
+
3
+ Read-only document introspection utilities. All methods may throw — wrap in `try/catch`. Load this file when working with PDF, image, XML, or XMP documents.
4
+
5
+ ## PdfDocument
6
+
7
+ Open a PDF to inspect metadata and page geometry.
8
+
9
+ ```ts
10
+ PdfDocument.open(path: string): PdfDocument
11
+ ```
12
+ Opens the PDF. Call `close()` when done.
13
+
14
+ ```ts
15
+ doc.close(): void
16
+ ```
17
+
18
+ ```ts
19
+ doc.getNumberOfPages(): number
20
+ PdfDocument.getNumberOfPages(path: string): number
21
+ ```
22
+
23
+ ```ts
24
+ doc.getPDFVersion(): string // e.g. "1.6"
25
+ PdfDocument.getPDFVersion(path: string): string
26
+
27
+ doc.getPDFXVersion(): string // empty string if not PDF/X
28
+ PdfDocument.getPDFXVersion(path: string): string
29
+
30
+ doc.getSecurityMethod(): string
31
+ PdfDocument.getSecurityMethod(path: string): string
32
+
33
+ doc.getPage(pageNumber?: number): PdfPage // 1-based; defaults to page 1
34
+ doc.getXMP(): XmpDocument
35
+ ```
36
+
37
+ ### Static page dimension methods
38
+
39
+ All take `(path: string, pageNumber?: number, effective?: boolean)` — page numbers are 1-based, `effective` defaults to `true` (applies rotation and scaling). All return `number` in points.
40
+
41
+ | Method | Description |
42
+ |---|---|
43
+ | `PdfDocument.getPageHeight` | Alias for media box height |
44
+ | `PdfDocument.getPageWidth` | Alias for media box width |
45
+ | `PdfDocument.getPageMediaBoxHeight` / `Width` | Media box |
46
+ | `PdfDocument.getPageCropBoxHeight` / `Width` | Crop box |
47
+ | `PdfDocument.getPageBleedBoxHeight` / `Width` | Bleed box |
48
+ | `PdfDocument.getPageTrimBoxHeight` / `Width` | Trim box |
49
+ | `PdfDocument.getPageArtBoxHeight` / `Width` | Art box — **known issue:** currently returns the crop box value instead (not expected to be fixed soon) |
50
+
51
+ ```ts
52
+ PdfDocument.getPageRotation(path: string, pageNumber?: number): number // degrees
53
+ PdfDocument.getPageScaling(path: string, pageNumber?: number): number
54
+ PdfDocument.getPageLabel(path: string, pageNumber?: number): string // empty if none
55
+ ```
56
+
57
+ ---
58
+
59
+ ## PdfPage
60
+
61
+ Obtained via `PdfDocument.getPage()`. Instance-level page geometry. All dimensions in points.
62
+
63
+ ```ts
64
+ page.getHeight(effective?: boolean): number // alias for getMediaBoxHeight
65
+ page.getWidth(effective?: boolean): number // alias for getMediaBoxWidth
66
+
67
+ page.getMediaBoxHeight(effective?: boolean): number
68
+ page.getMediaBoxWidth(effective?: boolean): number
69
+ page.getCropBoxHeight(effective?: boolean): number
70
+ page.getCropBoxWidth(effective?: boolean): number
71
+ page.getBleedBoxHeight(effective?: boolean): number
72
+ page.getBleedBoxWidth(effective?: boolean): number
73
+ page.getTrimBoxHeight(effective?: boolean): number
74
+ page.getTrimBoxWidth(effective?: boolean): number
75
+ page.getArtBoxHeight(effective?: boolean): number // known issue: currently returns the crop box value
76
+ page.getArtBoxWidth(effective?: boolean): number // known issue: currently returns the crop box value
77
+
78
+ page.getRotation(): number // degrees
79
+ page.getScaling(): number
80
+ page.getPageLabel(): string // empty if none
81
+ ```
82
+
83
+ `effective` defaults to `true` — applies rotation and scaling.
84
+
85
+ ---
86
+
87
+ ## ImageDocument
88
+
89
+ Supported formats: JPEG, TIFF, PNG. Reads values from EXIF and XMP.
90
+
91
+ **`open` is async** — use `await`:
92
+
93
+ ```ts
94
+ const img = await ImageDocument.open(path);
95
+ // ...
96
+ img.close();
97
+ ```
98
+
99
+ All static methods also return `Promise<T>`.
100
+
101
+ ```ts
102
+ ImageDocument.open(path: string): Promise<ImageDocument>
103
+ img.close(): void
104
+
105
+ img.getWidth(): number
106
+ ImageDocument.getWidth(path: string): Promise<number>
107
+
108
+ img.getHeight(): number
109
+ ImageDocument.getHeight(path: string): Promise<number>
110
+
111
+ img.getColorMode(): ImageDocument.ColorMode
112
+ ImageDocument.getColorMode(path: string): Promise<ImageDocument.ColorMode>
113
+
114
+ img.getColorSpace(): ImageDocument.ColorSpace
115
+ ImageDocument.getColorSpace(path: string): Promise<ImageDocument.ColorSpace>
116
+
117
+ img.getICCProfile(): string // Photoshop, falling back to the EXIF ProfileDescription if absent
118
+ ImageDocument.getICCProfile(path: string): Promise<string>
119
+
120
+ img.getSamplesPerPixel(): number
121
+ ImageDocument.getSamplesPerPixel(path: string): Promise<number>
122
+ ```
123
+
124
+ See [api-enums.md](api-enums.md) for `ImageDocument.ColorMode` and `ImageDocument.ColorSpace` values.
125
+
126
+ ---
127
+
128
+ ## XmlDocument
129
+
130
+ XPath 1.0 queries against XML files or strings.
131
+
132
+ ```ts
133
+ XmlDocument.open(path: string): XmlDocument
134
+ XmlDocument.parse(xmlString: string): XmlDocument
135
+
136
+ doc.evaluate(xpath: string, prefixMap?: { [prefix: string]: string }): boolean | number | string | object | undefined
137
+ doc.getDefaultNSMap(): { [prefix: string]: string }
138
+ ```
139
+
140
+ XPath does not support default namespaces — always use a prefix. `getDefaultNSMap()` returns all namespace mappings occurring in the document; the default namespace (if any) is accessible via the special prefixes `default_switch_ns`, `dn`, and `jdf`. `evaluate()` can also return an `object` (e.g. a node-set result), not just the primitive types.
141
+
142
+ ```ts
143
+ const doc = XmlDocument.open(path);
144
+ const map = doc.getDefaultNSMap();
145
+ const result = doc.evaluate('/dn:root/dn:item/@id', map);
146
+ ```
147
+
148
+ ---
149
+
150
+ ## XmpDocument
151
+
152
+ Evaluate XMP location paths (not XPath) against XMP-only files. Obtained from `PdfDocument.getXMP()` or opened directly.
153
+
154
+ > **Note**: `open()` requires an XMP-only file — do not pass a PDF or image path.
155
+
156
+ ```ts
157
+ XmpDocument.open(path: string): XmpDocument
158
+
159
+ doc.evaluate(
160
+ xmpLocationPath: string,
161
+ additionalPrefixMap?: { [prefix: string]: string }
162
+ ): boolean | number | string | undefined
163
+
164
+ doc.save(path: string): void
165
+ ```
166
+
167
+ If `additionalPrefixMap` is omitted, the default prefix map (all standard XMP namespaces plus any extras in the file) is used.
@@ -0,0 +1,82 @@
1
+ # Switch Script Entry Points
2
+
3
+ Entry points are top-level `async` functions with specific names. They are **not exported**. Switch calls them automatically based on flow events.
4
+
5
+ ## Flow lifecycle
6
+
7
+ ```ts
8
+ async function flowStartTriggered(s: Switch, flowElement: FlowElement): Promise<void>
9
+ ```
10
+ Called when the flow starts. Use to subscribe to webhooks (`s.httpRequestSubscribe`) or channels (`flowElement.subscribeToChannel`). Cannot coexist with `timerFired` or `jobArrived` as the sole entry point. May execute in parallel for concurrent elements. Not available for debugging.
11
+
12
+ ```ts
13
+ async function flowStopTriggered(s: Switch, flowElement: FlowElement): Promise<void>
14
+ ```
15
+ Called when the flow stops. May execute in parallel for concurrent elements. Not available for debugging.
16
+
17
+ ## Timer
18
+
19
+ ```ts
20
+ async function timerFired(s: Switch, flowElement: FlowElement): Promise<void>
21
+ ```
22
+ Called on a recurring timer. First fires after `flowStartTriggered`. Set the interval via `flowElement.setTimerInterval(seconds)`. Cannot coexist with `flowStartTriggered` as the sole entry point.
23
+
24
+ ## Job processing
25
+
26
+ ```ts
27
+ async function jobArrived(s: Switch, flowElement: FlowElement, job: Job): Promise<void>
28
+ ```
29
+ Called when a job arrives. Must eventually call a `job.sendTo*()` or `job.fail()`. Can run concurrently if configured in the script XML declaration.
30
+
31
+ ```ts
32
+ async function abort(s: Switch, flowElement: FlowElement, job: Job, abortData: any): Promise<void>
33
+ ```
34
+ Called when an entry point exceeds its configured timeout. The `abortData` value was set beforehand via `s.setAbortData()`. Maximum execution: 60 seconds, after which the script executor is killed. Applies to: `jobArrived`, `timerFired`, `httpRequestTriggeredSync`, `httpRequestTriggeredAsync`, `flowStartTriggered`, `flowStopTriggered`.
35
+
36
+ ## Webhooks
37
+
38
+ ```ts
39
+ async function httpRequestTriggeredSync(request: HttpRequest, args: any[], response: HttpResponse, s: Switch): Promise<void>
40
+ ```
41
+ Synchronous webhook handler. A response **must** be sent before the function returns. Registered via `s.httpRequestSubscribe()` in `flowStartTriggered`. Executes in concurrent mode. Request body limit: 1 MB (HTTP 413). Queue limit: 10,000 pending requests (HTTP 429). Execution timeout: 1 minute (HTTP 524). Default response if none set: HTTP 200 with `{"status": true}`.
42
+
43
+ ```ts
44
+ async function httpRequestTriggeredAsync(request: HttpRequest, args: any[], s: Switch, flowElement: FlowElement): Promise<void>
45
+ ```
46
+ Asynchronous webhook handler. No response is required. Registered via `s.httpRequestSubscribe()` in `flowStartTriggered`. Only invoked if `httpRequestTriggeredSync` is not defined, or if the sync handler returned a 2xx status.
47
+
48
+ ## Script expression
49
+
50
+ ```ts
51
+ async function calculateScriptExpression(s: Switch, flowElement: FlowElement, job: Job): Promise<string | number | boolean>
52
+ ```
53
+ Called to evaluate a script expression for the flow element. The return value is used as the expression result.
54
+
55
+ ## Property UI callbacks
56
+
57
+ Not available for debugging.
58
+
59
+ ```ts
60
+ async function getLibraryForProperty(s: Switch, flowElement: FlowElement, tag: string): Promise<string[]>
61
+ ```
62
+ Returns a dynamic list of values for a property's library dropdown.
63
+
64
+ ```ts
65
+ async function getLibraryForConnectionProperty(s: Switch, flowElement: FlowElement, c: Connection, tag: string): Promise<string[]>
66
+ ```
67
+ Returns a dynamic list of values for a connection property's library dropdown.
68
+
69
+ ```ts
70
+ async function validateProperties(s: Switch, flowElement: FlowElement, tags: string[]): Promise<{ tag: string, valid: boolean }[]>
71
+ ```
72
+ Validates one or more property values. Return one result object per tag.
73
+
74
+ ```ts
75
+ async function validateConnectionProperties(s: Switch, flowElement: FlowElement, c: Connection, tags: string[]): Promise<{ tag: string, valid: boolean }[]>
76
+ ```
77
+ Validates one or more connection property values.
78
+
79
+ ```ts
80
+ async function findExternalEditorPath(s: Switch, flowElement: FlowElement, tag: string): Promise<string>
81
+ ```
82
+ Resolves the path to an external editor for a property.
@@ -0,0 +1,153 @@
1
+ # Switch Scripting Enums
2
+
3
+ All enums are available globally in `main.ts`. They are also accessible via `EnfocusSwitch.*` when used outside `main.ts` (e.g. in imported modules).
4
+
5
+ ## LogLevel
6
+
7
+ Used with `job.log()` and `flowElement.log()`.
8
+
9
+ | Value | String |
10
+ |---|---|
11
+ | `LogLevel.Info` | `"info"` |
12
+ | `LogLevel.Warning` | `"warning"` |
13
+ | `LogLevel.Error` | `"error"` |
14
+ | `LogLevel.Debug` | `"debug"` |
15
+
16
+ ## AccessLevel
17
+
18
+ Used with `job.get()` and `job.getDataset()`.
19
+
20
+ | Value | String |
21
+ |---|---|
22
+ | `AccessLevel.ReadOnly` | `"readOnly"` |
23
+ | `AccessLevel.ReadWrite` | `"readWrite"` |
24
+
25
+ ## DatasetModel
26
+
27
+ Used with `job.createDataset()`, `job.sendToLog()`, and `job.listDatasets()`.
28
+
29
+ | Value | String |
30
+ |---|---|
31
+ | `DatasetModel.Opaque` | `"Opaque"` |
32
+ | `DatasetModel.XML` | `"XML"` |
33
+ | `DatasetModel.XMP` | `"XMP"` |
34
+ | `DatasetModel.JDF` | `"JDF"` |
35
+ | `DatasetModel.JSON` | `"JSON"` |
36
+
37
+ ## Scope
38
+
39
+ Used with `s.getGlobalData()`, `s.setGlobalData()`, `s.removeGlobalData()`.
40
+
41
+ | Value | String |
42
+ |---|---|
43
+ | `Scope.Element` | `"element"` |
44
+ | `Scope.Flow` | `"flow"` |
45
+ | `Scope.FlowElement` | `"flowElement"` |
46
+ | `Scope.FlowElements` | `"flowElements"` |
47
+ | `Scope.Global` | `"global"` |
48
+
49
+ ## Priority
50
+
51
+ Used with `job.setPriority()` / `job.getPriority()`.
52
+
53
+ | Value | Number |
54
+ |---|---|
55
+ | `Priority.Low` | `-100000` |
56
+ | `Priority.BelowNormal` | `-10000` |
57
+ | `Priority.Normal` | `0` |
58
+ | `Priority.AboveNormal` | `10000` |
59
+ | `Priority.High` | `100000` |
60
+
61
+ ## Connection.Level
62
+
63
+ Used with `job.sendToData()` and `job.sendToLog()` for traffic light connections.
64
+
65
+ | Value | String |
66
+ |---|---|
67
+ | `Connection.Level.Success` | `"success"` |
68
+ | `Connection.Level.Warning` | `"warning"` |
69
+ | `Connection.Level.Error` | `"error"` |
70
+
71
+ Also available as `EnfocusSwitch.Connection.Level.*` outside `main.ts`.
72
+
73
+ ## HttpRequest.Method
74
+
75
+ Used with `s.httpRequestSubscribe()` and `s.httpRequestUnsubscribe()`.
76
+
77
+ | Value | String |
78
+ |---|---|
79
+ | `HttpRequest.Method.POST` | `"POST"` |
80
+ | `HttpRequest.Method.PUT` | `"PUT"` |
81
+ | `HttpRequest.Method.DELETE` | `"DELETE"` |
82
+
83
+ Also available as `EnfocusSwitch.HttpRequest.Method.*` outside `main.ts`.
84
+
85
+ ## PropertyType
86
+
87
+ Returned by `flowElement.getPropertyType()` and `connection.getPropertyType()`.
88
+
89
+ | Value | String |
90
+ |---|---|
91
+ | `PropertyType.Literal` | `"literal"` |
92
+ | `PropertyType.Number` | `"number"` |
93
+ | `PropertyType.Date` | `"date"` |
94
+ | `PropertyType.HoursAndMinutes` | `"hoursandminutes"` |
95
+ | `PropertyType.Boolean` | `"boolean"` |
96
+ | `PropertyType.String` | `"string"` |
97
+ | `PropertyType.FilePath` | `"filepath"` |
98
+ | `PropertyType.FolderPath` | `"folderpath"` |
99
+ | `PropertyType.FileType` | `"filetype"` |
100
+ | `PropertyType.FolderPattern` | `"folderpattern"` |
101
+ | `PropertyType.Regex` | `"regex"` |
102
+ | `PropertyType.OAuthToken` | `"oauthtoken"` |
103
+
104
+ ## NoYesListPropertyStringValue
105
+
106
+ Convenience for `Boolean`-typed properties returned as strings.
107
+
108
+ | Value | String |
109
+ |---|---|
110
+ | `NoYesListPropertyStringValue.No` | `"No"` |
111
+ | `NoYesListPropertyStringValue.Yes` | `"Yes"` |
112
+
113
+ ## EnfocusSwitchPrivateDataTag
114
+
115
+ Well-known tags for `job.getPrivateData()` / `job.setPrivateData()`.
116
+
117
+ | Value | String |
118
+ |---|---|
119
+ | `EnfocusSwitchPrivateDataTag.hierarchy` | `"EnfocusSwitch.hierarchy"` |
120
+ | `EnfocusSwitchPrivateDataTag.emailAddresses` | `"EnfocusSwitch.emailAddresses"` |
121
+ | `EnfocusSwitchPrivateDataTag.emailBody` | `"EnfocusSwitch.emailBody"` |
122
+ | `EnfocusSwitchPrivateDataTag.userName` | `"EnfocusSwitch.userName"` |
123
+ | `EnfocusSwitchPrivateDataTag.userFullName` | `"EnfocusSwitch.userFullName"` |
124
+ | `EnfocusSwitchPrivateDataTag.userEmail` | `"EnfocusSwitch.userEmail"` |
125
+ | `EnfocusSwitchPrivateDataTag.origin` | `"EnfocusSwitch.origin"` |
126
+ | `EnfocusSwitchPrivateDataTag.initiated` | `"EnfocusSwitch.initiated"` |
127
+ | `EnfocusSwitchPrivateDataTag.submittedTo` | `"EnfocusSwitch.submittedTo"` |
128
+ | `EnfocusSwitchPrivateDataTag.state` | `"EnfocusSwitch.state"` |
129
+
130
+ ## ImageDocument.ColorMode
131
+
132
+ Used with `ImageDocument.getColorMode()`. Available as `EnfocusSwitch.ImageDocument.ColorMode.*` outside `main.ts`.
133
+
134
+ | Value | String |
135
+ |---|---|
136
+ | `ImageDocument.ColorMode.Bitmap` | `"Bitmap"` |
137
+ | `ImageDocument.ColorMode.Gray` | `"Gray"` |
138
+ | `ImageDocument.ColorMode.IndexedColor` | `"Indexed color"` |
139
+ | `ImageDocument.ColorMode.RGB` | `"RGB"` |
140
+ | `ImageDocument.ColorMode.CMYK` | `"CMYK"` |
141
+ | `ImageDocument.ColorMode.Multichannel` | `"Multichannel"` |
142
+ | `ImageDocument.ColorMode.Duotone` | `"Duotone"` |
143
+ | `ImageDocument.ColorMode.LabColor` | `"Lab color"` |
144
+ | `ImageDocument.ColorMode.Unknown` | `"Unknown"` |
145
+
146
+ ## ImageDocument.ColorSpace
147
+
148
+ Used with `ImageDocument.getColorSpace()`. Available as `EnfocusSwitch.ImageDocument.ColorSpace.*` outside `main.ts`.
149
+
150
+ | Value | String |
151
+ |---|---|
152
+ | `ImageDocument.ColorSpace.SRGB` | `"sRGB"` |
153
+ | `ImageDocument.ColorSpace.Uncalibrated` | `"uncalibrated"` |
@@ -0,0 +1,70 @@
1
+ # Execution Environment
2
+
3
+ How the Node.js process a script runs in behaves, beyond the entry point API itself. These are
4
+ consequences of the host process model, not things a script configures — understanding them avoids
5
+ subtle bugs around state persistence and error handling.
6
+
7
+ ## Two independent concurrency tiers
8
+
9
+ `NumberOfSlots`/`ExecutionGroup` (see
10
+ [Execution modes](api-script-structure.md#execution-modes)) and the pool of Node.js processes that
11
+ actually run script code are governed completely separately — there is no one-to-one mapping between
12
+ them:
13
+
14
+ - **`NumberOfSlots`** is a job-admission throttle on the Switch Server, scoped to that one flow
15
+ element instance (keyed by flow + element id): it limits how many jobs may be dispatched to that
16
+ instance at once, falling back to a server-wide default if left as `Default`. **`ExecutionGroup`**
17
+ (meaningful only for `Serialized` mode) is a true cross-flow-element named lock — every flow
18
+ element that declares the same group string, anywhere on the server, contends for one shared lock.
19
+ Neither is a count of OS processes.
20
+ - Separately, a shared pool of long-lived Node.js executor processes actually runs the script code
21
+ for every script element on the server, sized by a single server-wide concurrency setting — not
22
+ one process per slot, per instance, or per script. A job dispatched by the Server is handed to
23
+ whichever pooled executor is free (or a new one is spawned if none are); executors are reused
24
+ across many jobs and across different flow elements running the same Node.js version, until
25
+ recycled (see
26
+ [Executor cleanup thresholds](api-job-patterns.md#executor-cleanup-thresholds)).
27
+
28
+ ## Module-level state persists across jobs on the same executor
29
+
30
+ Because executor processes are long-lived and reused, any state held outside an entry point function
31
+ — module-level `let`/`const`/`var`, an in-memory cache, a counter — can survive from one job
32
+ invocation to the next if the same executor happens to service both. It is **not** reset before each
33
+ `jobArrived`/`timerFired`/etc. call, and which executor services a given job is not something a
34
+ script controls or can predict. This is useful for caching (e.g. a parsed config file, a pooled
35
+ connection) but also a common source of bugs: don't assume module-level state starts fresh for every
36
+ job, and don't assume it's shared with an *arbitrary* other job either — reset explicitly at the
37
+ start of an entry point if a fresh value is required per job.
38
+
39
+ `getInitialize()`/`getFinalize()` (if the script uses them) run once per script per process
40
+ lifetime, not once per job — the same caveat applies to anything they set up.
41
+
42
+ ## Unhandled promise rejections end the run, not the process
43
+
44
+ An unhandled promise rejection inside an entry point call ends that job's/run's execution — but the
45
+ underlying executor process is **not** killed and continues serving subsequent job invocations,
46
+ including for other flow elements pooled onto the same executor. Don't rely on an unhandled
47
+ rejection to surface as a hard failure of the whole executor; always catch and handle errors
48
+ explicitly (e.g. via `job.fail()`/`flowElement.failProcess()` — see
49
+ [api-logging.md](api-logging.md)) rather than letting a promise reject unhandled.
50
+
51
+ ## Third-party npm modules: file-based scripts only
52
+
53
+ A script **expression** (entered inline in a flow element property, not a file-based script/app —
54
+ see [Script expression](api-entry-points.md#script-expression)) cannot use third-party npm modules
55
+ at all; only Node.js built-ins are available via `require`. A file-based script/app can use
56
+ third-party modules from its own `node_modules` folder (see
57
+ [Script folder files](api-script-structure.md#script-folder-files)).
58
+
59
+ ## Native (binary) addons are not supported
60
+
61
+ Scripts with ESM dependencies are bundled before execution; this bundling path does not support
62
+ native/binary Node addons (`.node` files). Stick to pure-JavaScript/TypeScript dependencies.
63
+
64
+ ## No explicit CPU or memory cap beyond the documented thresholds
65
+
66
+ Beyond the entry-point abort timeout and executor recycling thresholds already documented (see
67
+ [Job processing](api-entry-points.md#job-processing) and
68
+ [Executor cleanup thresholds](api-job-patterns.md#executor-cleanup-thresholds)), there is no
69
+ additional CPU-time or memory limit enforced on a script's own code. A runaway loop or leak is
70
+ bounded only by those thresholds, not stopped proactively.
@@ -0,0 +1,112 @@
1
+ # FlowElement Class
2
+
3
+ The `FlowElement` instance `flowElement` is passed to every entry point that operates on a flow element. It provides access to properties, connections, job creation, and logging.
4
+
5
+ ## Identity
6
+
7
+ ```ts
8
+ flowElement.getName(): string
9
+ ```
10
+ Returns the name of this flow element.
11
+
12
+ ```ts
13
+ flowElement.getFlowName(): string
14
+ ```
15
+ Returns the name of the flow containing this element.
16
+
17
+ ## Properties
18
+
19
+ Properties are configured by the user in the Switch canvas and declared in the script's XML declaration.
20
+
21
+ ```ts
22
+ flowElement.getPropertyStringValue(tag: string): Promise<string | string[]>
23
+ ```
24
+ Returns the property value as a string (or array for multi-value properties). For `OAuthToken` properties, returns a valid refreshed token. Throws `"Invalid tag: <tag>"` if the tag is unknown or a hidden dependent property. See [api-property-editors.md](api-property-editors.md) for editor types and their return value formats.
25
+
26
+ > **Dynamic values (Switch variables and script expressions) are only resolved when `getPropertyStringValue()` is called from `jobArrived`.** In any other entry point (e.g. `timerFired`) the raw unresolved string is returned.
27
+ >
28
+ > **Workaround for deferred processing:** Read property values in `jobArrived` and store them with `s.setGlobalData()`. In `timerFired`, retrieve the stored values and fetch the waiting jobs with `flowElement.getJobs(ids)`.
29
+
30
+ ```ts
31
+ flowElement.getPropertyType(tag: string): PropertyType
32
+ ```
33
+ Returns the `PropertyType` of the property — required to correctly interpret the string value when multiple input types are possible.
34
+
35
+ ```ts
36
+ flowElement.getPropertyDisplayName(tag: string): string
37
+ ```
38
+ Returns the English display name of the property (for use in log messages).
39
+
40
+ ```ts
41
+ flowElement.hasProperty(tag: string): boolean
42
+ ```
43
+ Returns `true` if the property exists and is visible for the current configuration.
44
+
45
+ ## Connections
46
+
47
+ ```ts
48
+ flowElement.getOutConnections(): Connection[]
49
+ ```
50
+ Returns all outgoing connections (arbitrary order). Empty array if none.
51
+
52
+ ## Timer
53
+
54
+ ```ts
55
+ flowElement.setTimerInterval(seconds: number): void
56
+ ```
57
+ Sets the interval between `timerFired` invocations. Default is 300 s. The actual interval may be longer under load.
58
+
59
+ ## Logging
60
+
61
+ See [api-logging.md](api-logging.md) for log level semantics and logging practice.
62
+
63
+ ```ts
64
+ flowElement.log(level: LogLevel, message: string, messageParams?: (string | number | boolean)[]): Promise<void>
65
+ ```
66
+ Logs a message for the flow element. Use `%1`, `%2`, etc. in the message string for substitution via `messageParams`.
67
+
68
+ ```ts
69
+ flowElement.failProcess(message: string, messageParam?: string | number | boolean): void
70
+ ```
71
+ Logs a fatal error and puts the element into the "problem process" state. `messageParam` substitutes `%1`. Throws `"Job routing is not allowed in this entry point."` if called from an entry point where routing isn't permitted.
72
+
73
+ ## Job creation (jobArrived / timerFired)
74
+
75
+ ```ts
76
+ flowElement.createJob(path: string): Promise<Job>
77
+ ```
78
+ Creates a new job from an existing file/folder path. Valid only in `jobArrived` and `timerFired`. The new job has default properties (no parent). The caller is responsible for cleaning up the source file after routing. See [api-job-patterns.md](api-job-patterns.md) for temp file cleanup rules and routing constraints.
79
+
80
+ ```ts
81
+ flowElement.getJobs(ids: string[]): Promise<Job[]>
82
+ ```
83
+ Returns up to 10,000 waiting jobs by ID. Throws if `ids` is not an array, is empty, or exceeds 10,000 entries. Call at most once per entry point. Not available in concurrent `jobArrived`. Private data and metadata of returned jobs cannot be read in the same invocation.
84
+
85
+ ## Channels
86
+
87
+ ```ts
88
+ flowElement.subscribeToChannel(channelId: string, backingFolderPath: string): void
89
+ ```
90
+ Subscribes to a channel to receive jobs from it. Only one subscriber per channel at a time. Must be called inside `flowStartTriggered`.
91
+
92
+ ## Utilities
93
+
94
+ ```ts
95
+ flowElement.createPathWithName(name: string, createFolder: boolean): Promise<any>
96
+ ```
97
+ Creates a temporary path (optionally as a folder — `createFolder` is required, not optional). Returns an empty string only if creating the folder throws (e.g. a permissions error); it does not check whether the path already exists first.
98
+
99
+ ```ts
100
+ flowElement.getFileCount(nested?: boolean): Promise<number>
101
+ ```
102
+ Returns the number of files in the active flow. If `nested` is `false`, counts only direct children; if `true` (the default, so it can be omitted), counts recursively (folders themselves are not counted).
103
+
104
+ ```ts
105
+ flowElement.getScriptDataPath(): string
106
+ ```
107
+ Returns the path to the ScriptData folder for this element.
108
+
109
+ ```ts
110
+ flowElement.getPluginResourcesPath(): string
111
+ ```
112
+ Returns the script resources folder path. For scripted plug-ins and apps, returns the resources folder. For script folders, returns the folder path. **For a regular script package (`.sscript`) running locally**, returns the parent folder of the deployed package file — external resources are not bundled inside the package itself, so anything the script needs at runtime must be placed in that parent folder alongside the `.sscript`, not referenced from inside it. **Throws** `"flowElement.getPluginResourcesPath() is supported only in App or Configurator."` for a non-local (server-deployed) `.sscript` package.