@enfocussw/switch-scripting-context 25.11.1-beta.4 → 25.11.1-beta.6
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/CHANGELOG.md +60 -0
- package/README.md +18 -9
- package/dist/init.d.ts +19 -1
- package/dist/init.js +114 -25
- package/docs/switch-api/api-versions.md +20 -9
- package/docs/switch-api/document-classes.md +11 -0
- package/docs/switch-api/entry-points.md +12 -0
- package/docs/switch-api/enums.md +17 -0
- package/docs/switch-api/execution-environment.md +45 -12
- package/docs/switch-api/flow-element.md +15 -2
- package/docs/switch-api/job-patterns.md +13 -0
- package/docs/switch-api/job.md +16 -1
- package/docs/switch-api/logging.md +15 -0
- package/docs/switch-api/switch.md +10 -0
- package/docs/switch-appstore/app-guidelines.md +19 -0
- package/docs/switch-project/node-versions.md +87 -0
- package/docs/switch-project/project-planning.md +25 -5
- package/docs/switch-project/property-editors.md +17 -3
- package/docs/switch-project/script-declaration.md +20 -4
- package/docs/switch-project/script-structure.md +22 -78
- package/docs/switch-project/tooling.md +12 -2
- package/docs/switch-project/vscode.md +3 -3
- package/docs/switch-scripting.md +7 -4
- package/package.json +1 -1
|
@@ -13,6 +13,18 @@ How the Node.js process a script runs in behaves, beyond the entry point API its
|
|
|
13
13
|
execution mode below, these are consequences of the host process model rather than things a script
|
|
14
14
|
configures — understanding them avoids subtle bugs around state persistence and error handling.
|
|
15
15
|
|
|
16
|
+
<!-- docs-index:contents begin -->
|
|
17
|
+
## Contents
|
|
18
|
+
|
|
19
|
+
- Execution modes
|
|
20
|
+
- Two independent concurrency tiers
|
|
21
|
+
- Don't rely on in-memory state between entry point calls
|
|
22
|
+
- Unhandled promise rejections end the run, not the process
|
|
23
|
+
- Third-party npm modules: file-based scripts only
|
|
24
|
+
- Native (binary) addons are not supported
|
|
25
|
+
- No explicit CPU or memory cap beyond the documented thresholds
|
|
26
|
+
<!-- docs-index:contents end -->
|
|
27
|
+
|
|
16
28
|
## Execution modes
|
|
17
29
|
|
|
18
30
|
Set in the XML declaration via SwitchScripter, or by editing it directly (see
|
|
@@ -50,19 +62,40 @@ them:
|
|
|
50
62
|
recycled (see
|
|
51
63
|
[Executor cleanup thresholds](job-patterns.md#executor-cleanup-thresholds)).
|
|
52
64
|
|
|
53
|
-
##
|
|
65
|
+
## Don't rely on in-memory state between entry point calls
|
|
66
|
+
|
|
67
|
+
Write every entry point so it neither depends on state from an earlier call nor assumes it starts
|
|
68
|
+
from a clean process.
|
|
69
|
+
|
|
70
|
+
The executor evaluates the whole script again before every entry point call. Top-level
|
|
71
|
+
`let`/`const`/`var` declarations and functions in the script file are recreated each time, so a
|
|
72
|
+
counter or cache held in one does not carry over to the next job.
|
|
73
|
+
|
|
74
|
+
Some in-memory state can still outlive a call, because executor processes are long-lived and
|
|
75
|
+
shared:
|
|
76
|
+
|
|
77
|
+
- Properties set on `global`/`globalThis`. One executor runs code for every script element pooled
|
|
78
|
+
onto it, so a global can be read or overwritten by a different script.
|
|
79
|
+
- Internal state of CommonJS packages loaded with `require()`. Node caches a required module for
|
|
80
|
+
the life of the process, so a singleton inside the package (a client, a connection pool, a
|
|
81
|
+
configured default) can be reused by a later call. ES-module packages that are bundled into the
|
|
82
|
+
script (see [Native (binary) addons](#native-binary-addons-are-not-supported)) are evaluated
|
|
83
|
+
again on each call like the script itself.
|
|
84
|
+
- Timers, sockets, and other handles a call leaves open keep running after the entry point returns.
|
|
85
|
+
|
|
86
|
+
Whether any of this reaches a given job depends on which executor the job lands on and whether that
|
|
87
|
+
executor was recycled in between. A script cannot control or predict either. This is a side effect
|
|
88
|
+
of the process model, not a supported feature, and it can change between Switch versions:
|
|
54
89
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
job, and don't assume it's shared with an *arbitrary* other job either — reset explicitly at the
|
|
62
|
-
start of an entry point if a fresh value is required per job.
|
|
90
|
+
- Don't use globals or package-level singletons to cache data or pass it between jobs. Store data
|
|
91
|
+
that must outlive a call with `s.setGlobalData()` (see [switch.md](switch.md)) or on the job
|
|
92
|
+
itself.
|
|
93
|
+
- Don't set globals. If a package keeps internal state, configure it explicitly at the start of
|
|
94
|
+
each entry point rather than assuming either a fresh or a previously configured instance.
|
|
95
|
+
- Close connections, clear timers, and release file handles before the entry point returns.
|
|
63
96
|
|
|
64
|
-
`getInitialize()`/`getFinalize()`
|
|
65
|
-
|
|
97
|
+
The executor also recognises `getInitialize()`/`getFinalize()` functions in a script. They are not
|
|
98
|
+
part of the public scripting API; don't define them.
|
|
66
99
|
|
|
67
100
|
## Unhandled promise rejections end the run, not the process
|
|
68
101
|
|
|
@@ -85,7 +118,7 @@ third-party modules from its own `node_modules` folder (see
|
|
|
85
118
|
|
|
86
119
|
Scripts with ESM dependencies are bundled before execution, when `SwitchVersion` in `manifest.xml`
|
|
87
120
|
is `24.0` or higher (see
|
|
88
|
-
[
|
|
121
|
+
[node-versions.md § Other effects of `SwitchVersion`](../switch-project/node-versions.md#other-effects-of-switchversion)); this bundling path does not support
|
|
89
122
|
native/binary Node addons (`.node` files). Stick to pure-JavaScript/TypeScript dependencies.
|
|
90
123
|
|
|
91
124
|
## No explicit CPU or memory cap beyond the documented thresholds
|
|
@@ -11,6 +11,19 @@ triggers:
|
|
|
11
11
|
|
|
12
12
|
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.
|
|
13
13
|
|
|
14
|
+
<!-- docs-index:contents begin -->
|
|
15
|
+
## Contents
|
|
16
|
+
|
|
17
|
+
- Identity
|
|
18
|
+
- Properties
|
|
19
|
+
- Connections
|
|
20
|
+
- Timer
|
|
21
|
+
- Logging
|
|
22
|
+
- Job creation (jobArrived / timerFired / httpRequestTriggeredAsync)
|
|
23
|
+
- Channels
|
|
24
|
+
- Utilities
|
|
25
|
+
<!-- docs-index:contents end -->
|
|
26
|
+
|
|
14
27
|
## Identity
|
|
15
28
|
|
|
16
29
|
```ts
|
|
@@ -81,12 +94,12 @@ flowElement.failProcess(message: string, messageParam?: string | number | boolea
|
|
|
81
94
|
```
|
|
82
95
|
Logs a fatal error and puts the element into the "problem process" state. `messageParam` substitutes `%1`; before Switch 22.1, always pass it, as a string (see [api-versions.md](api-versions.md#declaration-changes-to-existing-methods)). Throws `"Job routing is not allowed in this entry point."` if called from an entry point where routing isn't permitted.
|
|
83
96
|
|
|
84
|
-
## Job creation (jobArrived / timerFired)
|
|
97
|
+
## Job creation (jobArrived / timerFired / httpRequestTriggeredAsync)
|
|
85
98
|
|
|
86
99
|
```ts
|
|
87
100
|
flowElement.createJob(path: string): Promise<Job>
|
|
88
101
|
```
|
|
89
|
-
Creates a new job from an existing file/folder path. Valid
|
|
102
|
+
Creates a new job from an existing file/folder path. Valid in `jobArrived`, `timerFired`, and `httpRequestTriggeredAsync` (confirmed working there; the only webhook entry point that receives `flowElement`). The new job has default properties (no parent). The caller is responsible for cleaning up the source file after routing. See [job-patterns.md](job-patterns.md#temp-file-cleanup) for temp file cleanup rules and [job-patterns.md](job-patterns.md#sending-jobs) for routing constraints.
|
|
90
103
|
|
|
91
104
|
```ts
|
|
92
105
|
// Switch 21.0+
|
|
@@ -13,6 +13,19 @@ Common behavioral rules for file access, routing, temp file cleanup, and child j
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
<!-- docs-index:contents begin -->
|
|
17
|
+
## Contents
|
|
18
|
+
|
|
19
|
+
- File access semantics
|
|
20
|
+
- Temp file cleanup
|
|
21
|
+
- Platform-independent paths
|
|
22
|
+
- Sending jobs
|
|
23
|
+
- Dataset writes must precede child job creation
|
|
24
|
+
- processLater
|
|
25
|
+
- Executor cleanup thresholds
|
|
26
|
+
- Driving a third-party CLI application
|
|
27
|
+
<!-- docs-index:contents end -->
|
|
28
|
+
|
|
16
29
|
## File access semantics
|
|
17
30
|
|
|
18
31
|
`job.get(accessLevel)` and `job.getDataset(name, accessLevel)` behave differently depending on the access level:
|
package/docs/switch-api/job.md
CHANGED
|
@@ -11,6 +11,21 @@ triggers:
|
|
|
11
11
|
|
|
12
12
|
A `Job` represents a file or folder moving through the flow. It is passed to `jobArrived` and can be created via `flowElement.createJob()` or `job.createChild()`. Every job must be routed with a `sendTo*()` call or `fail()` before the entry point ends (or at a later time via `timerFired`).
|
|
13
13
|
> Jobs obtained via `flowElement.getJobs()` throw on `getPrivateData()`, `listDatasets()`, and `getDataset()` in the same entry point invocation — private data/metadata cannot be *read* back. `setPrivateData()`/`removePrivateData()` are not restricted this way.
|
|
14
|
+
|
|
15
|
+
<!-- docs-index:contents begin -->
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
- Identity
|
|
19
|
+
- Priority
|
|
20
|
+
- File access
|
|
21
|
+
- Routing
|
|
22
|
+
- Failure & logging
|
|
23
|
+
- Child jobs
|
|
24
|
+
- Private data
|
|
25
|
+
- Datasets (metadata)
|
|
26
|
+
- Variables & structured data
|
|
27
|
+
<!-- docs-index:contents end -->
|
|
28
|
+
|
|
14
29
|
## Identity
|
|
15
30
|
|
|
16
31
|
```ts
|
|
@@ -108,7 +123,7 @@ Creates a new job inheriting the processing history, metadata, and private data
|
|
|
108
123
|
|
|
109
124
|
## Private data
|
|
110
125
|
|
|
111
|
-
Private data is arbitrary key/value storage attached to a job and passed along with it. `EnfocusSwitchPrivateDataTag` needs Switch 21.0+; before 21.0, store string values only (see [api-versions.md](api-versions.md#declaration-changes-to-existing-methods)).
|
|
126
|
+
Private data is arbitrary key/value storage attached to a job and passed along with it. `EnfocusSwitchPrivateDataTag` needs Switch 21.0+; before 21.0, store string values only (see [api-versions.md](api-versions.md#declaration-changes-to-existing-methods)). For each tag's string value (`"EnfocusSwitch.<name>"`) and the release that added it, see [enums.md § EnfocusSwitchPrivateDataTag](enums.md#enfocusswitchprivatedatatag).
|
|
112
127
|
|
|
113
128
|
```ts
|
|
114
129
|
job.getPrivateData(tag: string | EnfocusSwitchPrivateDataTag): Promise<any>
|
|
@@ -15,6 +15,21 @@ Guidance for using `job.log()`, `flowElement.log()`, `job.fail()`, and `flowElem
|
|
|
15
15
|
listed in [enums.md](enums.md). Each log call has a small performance overhead, so where
|
|
16
16
|
and how often to log is a deliberate choice, not a default.
|
|
17
17
|
|
|
18
|
+
<!-- docs-index:contents begin -->
|
|
19
|
+
## Contents
|
|
20
|
+
|
|
21
|
+
- Log levels
|
|
22
|
+
- Don't add a verbosity property
|
|
23
|
+
- Logging in loops
|
|
24
|
+
- `console.log` does not work
|
|
25
|
+
- Don't double-log before a failure
|
|
26
|
+
- Message formatting and the `%` gotcha
|
|
27
|
+
- Signature gotcha: array vs. single value
|
|
28
|
+
- Placeholders instead of concatenation
|
|
29
|
+
- Message quality
|
|
30
|
+
- Log volume
|
|
31
|
+
<!-- docs-index:contents end -->
|
|
32
|
+
|
|
18
33
|
## Log levels
|
|
19
34
|
|
|
20
35
|
| Level | Use for |
|
|
@@ -11,6 +11,16 @@ triggers:
|
|
|
11
11
|
|
|
12
12
|
The `Switch` instance `s` is passed to every entry point. It provides global data storage, webhook subscription, abort handling, and server utilities.
|
|
13
13
|
|
|
14
|
+
<!-- docs-index:contents begin -->
|
|
15
|
+
## Contents
|
|
16
|
+
|
|
17
|
+
- Global data
|
|
18
|
+
- Webhooks
|
|
19
|
+
- Abort
|
|
20
|
+
- Server utilities
|
|
21
|
+
- Translation extraction rules
|
|
22
|
+
<!-- docs-index:contents end -->
|
|
23
|
+
|
|
14
24
|
## Global data
|
|
15
25
|
|
|
16
26
|
Global data is key/value storage shared across entry point invocations, scoped by `Scope`.
|
|
@@ -25,6 +25,25 @@ Items fall into three tiers, marked on each one:
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
+
<!-- docs-index:contents begin -->
|
|
29
|
+
## Contents
|
|
30
|
+
|
|
31
|
+
- Properties
|
|
32
|
+
- Entry points
|
|
33
|
+
- Sending jobs
|
|
34
|
+
- Logging
|
|
35
|
+
- Temp files and paths
|
|
36
|
+
- App-only
|
|
37
|
+
- Identity and versioning
|
|
38
|
+
- Top-level declaration properties
|
|
39
|
+
- Password protection
|
|
40
|
+
- Localization
|
|
41
|
+
- Icon
|
|
42
|
+
- Extra files
|
|
43
|
+
- Source and review hygiene
|
|
44
|
+
- What review does and doesn't cover
|
|
45
|
+
<!-- docs-index:contents end -->
|
|
46
|
+
|
|
28
47
|
## Properties
|
|
29
48
|
|
|
30
49
|
- [ ] **Apps required / scripts recommended.** Every property name (`LocalizedTagName`) is 30
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: node-versions
|
|
3
|
+
category: switch-project
|
|
4
|
+
order: 26
|
|
5
|
+
summary: "How `SwitchVersion` in `manifest.xml` selects the Node.js version, the version table per Switch release, which tools overwrite `SwitchVersion`, values missing from the table, other effects of `SwitchVersion`"
|
|
6
|
+
triggers:
|
|
7
|
+
- "Working out which Node.js version a script or app will run on, or what `SwitchVersion` in manifest.xml does"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Node.js version per Switch version
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
The Switch Server running the script picks its Node.js version by looking up `SwitchVersion` from
|
|
14
|
+
`manifest.xml` in a fixed table that ships with each Switch release:
|
|
15
|
+
|
|
16
|
+
| Switch version | `SwitchVersion` | Node.js used |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Switch 2020 Spring | `20.0` | 12 |
|
|
19
|
+
| Switch 2020 Fall | `20.1` | 12 |
|
|
20
|
+
| Switch 2021 Spring | `21.0` | 14 |
|
|
21
|
+
| Switch 2021 Fall | `21.1` | 16 |
|
|
22
|
+
| Switch 2022 Spring | `22.0` | 16 |
|
|
23
|
+
| Switch 2022 Fall | `22.1` | 16 |
|
|
24
|
+
| Switch 2023 Fall | `23.1` | 18 |
|
|
25
|
+
| Switch 2024 Spring | `24.0` | 18 |
|
|
26
|
+
| Switch 2024 Fall | `24.1` | 20 |
|
|
27
|
+
| Switch 25.11 | `25.11` | 20 |
|
|
28
|
+
| Switch 26.11 | `26.11` | 24 |
|
|
29
|
+
|
|
30
|
+
Switch 26.11 bundles Node.js 12.22.12, 14.21.3, 16.20.2, 18.20.8, 20.20.0, and 24.13.0.
|
|
31
|
+
|
|
32
|
+
`SwitchVersion` controls the Node.js version only. Which scripting API methods exist depends on the
|
|
33
|
+
Switch version the script runs on, see [api-versions.md](../switch-api/api-versions.md).
|
|
34
|
+
|
|
35
|
+
## What writes `SwitchVersion`
|
|
36
|
+
|
|
37
|
+
`SwitchVersion` is the version of whichever tool last wrote the manifest. It is not a setting the
|
|
38
|
+
author picks:
|
|
39
|
+
|
|
40
|
+
- `SwitchScriptTool --create` writes the tool's own version into the new script folder.
|
|
41
|
+
- `SwitchScriptTool --pack` writes the tool's own version into the packed `.sscript`, whatever the
|
|
42
|
+
script folder's manifest says. The script folder's manifest itself is left unchanged. With
|
|
43
|
+
SwitchScriptTool 26.11, a folder whose manifest says `21.0` packs to a `.sscript` whose manifest
|
|
44
|
+
says `26.11`, which moves the script from Node.js 14 to Node.js 24. This was verified with a
|
|
45
|
+
pre-release SwitchScriptTool build.
|
|
46
|
+
- SwitchScripter writes its own version on every save. Opening a script with a different
|
|
47
|
+
`SwitchVersion` shows a warning that saving updates the script to the current Node.js version.
|
|
48
|
+
|
|
49
|
+
**Gotcha:** a script folder tested directly in Switch and the `.sscript` packed from it can run on
|
|
50
|
+
different Node.js versions. A folder created with an older SwitchScriptTool keeps that tool's
|
|
51
|
+
version, but packing it with a newer SwitchScriptTool stamps the newer one. Test the packed
|
|
52
|
+
`.sscript`, not only the folder, before shipping it.
|
|
53
|
+
|
|
54
|
+
To target an older Node.js version, pack the script with the SwitchScriptTool of a Switch release
|
|
55
|
+
whose row in the table gives that version. For example, a script packed with SwitchScriptTool 25.11
|
|
56
|
+
gets `SwitchVersion` `25.11` and runs on Node.js 20, in Switch 26.11 too. Apps work the same way
|
|
57
|
+
with SwitchScripter, except that there is no SwitchScripter for Switch 25.11. An app that
|
|
58
|
+
needs Node.js 20 is built with the Switch 2024 Fall SwitchScripter (`SwitchVersion` `24.1`), see
|
|
59
|
+
[script-structure.md § SwitchScripter and Switch version compatibility](script-structure.md#switchscripter-and-switch-version-compatibility).
|
|
60
|
+
Editing `SwitchVersion` by hand doesn't last, because `--pack` and SwitchScripter overwrite it.
|
|
61
|
+
|
|
62
|
+
## Values missing from the table
|
|
63
|
+
|
|
64
|
+
Behavior of the Switch 26.11 Server:
|
|
65
|
+
|
|
66
|
+
- No `SwitchVersion` element: treated as `20.1`, so Node.js 12.
|
|
67
|
+
- Lower than the first entry: Node.js 12.
|
|
68
|
+
- Higher than the last entry, for example a package built by a newer tool than the Switch it runs
|
|
69
|
+
on: the newest Node.js that Switch bundles. The Server logs a debug message that the script
|
|
70
|
+
"might not be compatible with the current Switch version" and runs it anyway.
|
|
71
|
+
- **Known issue:** a value that isn't an exact entry but falls numerically between the first and
|
|
72
|
+
last entries matches no Node.js version, for example `25.1` instead of `25.11`, `24.10` instead of
|
|
73
|
+
`24.1`, or `23.0`. The Server then fails to build the path to a Node.js binary, so no executor
|
|
74
|
+
process starts for the script. This was confirmed by running the Switch 26.11 lookup code with
|
|
75
|
+
these values, not by running such a script in Switch, so the exact symptom in Switch hasn't been
|
|
76
|
+
checked. A value such as `26.3` works today only because it's higher than the last entry, and it
|
|
77
|
+
will break once a later release adds an entry above it. Never hand-edit `SwitchVersion`. If you
|
|
78
|
+
must, copy a value exactly as the table above spells it.
|
|
79
|
+
|
|
80
|
+
## Other effects of `SwitchVersion`
|
|
81
|
+
|
|
82
|
+
- npm dependencies that are ES modules are bundled with esbuild before execution only when
|
|
83
|
+
`SwitchVersion` is `24.0` or higher.
|
|
84
|
+
- A script expression has no manifest. It always runs on the newest Node.js the running Switch
|
|
85
|
+
bundles.
|
|
86
|
+
|
|
87
|
+
> **Mac (Apple Silicon):** For scripts whose `SwitchVersion` is `21.1` (Switch 2021 Fall) or later, Switch runs the native ARM build of Node.js on Apple Silicon. Scripts with an earlier `SwitchVersion` run under Rosetta, because the bundled Node.js 12 and 14 are Intel-only builds. If a script bundles platform-specific binaries, ship both Intel and ARM variants.
|
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
id: project-planning
|
|
3
3
|
category: switch-project
|
|
4
4
|
order: 1
|
|
5
|
-
summary: "
|
|
5
|
+
summary: "Planning checklist, run before scaffolding or on a project that is already scaffolded: Script vs App, job-processing approach, one script folder or several, target OS, Switch version baseline, concurrency, native/binary npm dependencies, Appstore competition risk"
|
|
6
6
|
triggers:
|
|
7
|
-
- "Starting a new script or app, before scaffolding any files"
|
|
7
|
+
- "Starting a new script or app, before scaffolding any files, or working in a script folder that is already scaffolded where Script vs App has not been settled"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Project Planning
|
|
11
11
|
|
|
12
|
-
A
|
|
13
|
-
creating any files.
|
|
12
|
+
A planning checklist for a new script or app. Work through this with the user before
|
|
13
|
+
creating any files.
|
|
14
|
+
|
|
15
|
+
A script folder that already exists (scaffolded by the user, SwitchScripter, or `SwitchScriptTool
|
|
16
|
+
--create`) does not skip the checklist. The manifest's `ScriptPackageType` is always `Script` in a
|
|
17
|
+
scaffolded folder, so it does not answer item 1. Ask Script or App anyway, and run the other
|
|
18
|
+
checks against the code that exists. The goal is to make a few decisions deliberately up front, because retrofitting
|
|
14
19
|
them after code exists is expensive: password protection and localization for an app, path handling
|
|
15
20
|
for a second target OS, synchronizing shared-resource access for concurrency, or restructuring entry
|
|
16
21
|
points around a job-processing approach chosen too casually.
|
|
@@ -19,6 +24,21 @@ Two items below are direct questions for the user. The rest are not questions to
|
|
|
19
24
|
are checks the agent runs against what's actually being built, raised only when they become
|
|
20
25
|
relevant.
|
|
21
26
|
|
|
27
|
+
<!-- docs-index:contents begin -->
|
|
28
|
+
## Contents
|
|
29
|
+
|
|
30
|
+
- Ask the user directly
|
|
31
|
+
- 1. Script or App?
|
|
32
|
+
- 2. What should job processing look like in their flow?
|
|
33
|
+
- Evaluate and flag, don't ask upfront
|
|
34
|
+
- One script folder or several
|
|
35
|
+
- Target OS
|
|
36
|
+
- Switch version baseline (apps only)
|
|
37
|
+
- Concurrency
|
|
38
|
+
- Native or binary npm dependencies
|
|
39
|
+
- Appstore competition risk (apps only)
|
|
40
|
+
<!-- docs-index:contents end -->
|
|
41
|
+
|
|
22
42
|
## Ask the user directly
|
|
23
43
|
|
|
24
44
|
### 1. Script or App?
|
|
@@ -86,7 +106,7 @@ This sets two separate limits:
|
|
|
86
106
|
|
|
87
107
|
- Which SwitchScripter build is needed to produce a compatible `.enfpack`, and so which Node.js
|
|
88
108
|
version the app runs on (see
|
|
89
|
-
[
|
|
109
|
+
[node-versions.md](node-versions.md)).
|
|
90
110
|
Write code for that Node.js version.
|
|
91
111
|
- Which scripting API methods the code may call. Methods follow the Switch version the app runs on,
|
|
92
112
|
so check every call against [api-versions.md](../switch-api/api-versions.md) for the oldest
|
|
@@ -27,6 +27,18 @@ the order they're offered. `Type` follows from which editor(s) are chosen — se
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
+
<!-- docs-index:contents begin -->
|
|
31
|
+
## Contents
|
|
32
|
+
|
|
33
|
+
- Inline editors
|
|
34
|
+
- Modal editors
|
|
35
|
+
- Literal editors
|
|
36
|
+
- Extra XML attributes for certain editors
|
|
37
|
+
- Common practices
|
|
38
|
+
- A property whose shape varies by a type selector
|
|
39
|
+
- Notes
|
|
40
|
+
<!-- docs-index:contents end -->
|
|
41
|
+
|
|
30
42
|
## Inline editors
|
|
31
43
|
|
|
32
44
|
The inline editor slot is always the single token `inline` in `Editor` — what differs is `Type`.
|
|
@@ -36,13 +48,15 @@ The inline editor slot is always the single token `inline` in `Editor` — what
|
|
|
36
48
|
| `Editor="inline" Type="string"` | Single-line text as entered |
|
|
37
49
|
| `Editor="inline" Type="password"` | Text as entered (displayed masked) |
|
|
38
50
|
| `Editor="inline" Type="number"` | Integer as a string |
|
|
39
|
-
| `Editor="inline" Type="rational"` | Decimal as a string |
|
|
40
51
|
| `Editor="inline" Type="time"` | `"hh:mm"` (zero-padded, e.g. `"09:05"`) |
|
|
41
52
|
| `Editor="inline" Type="date"` | Date as entered |
|
|
42
53
|
| `Editor="inline" Type="datetime"` | Date and time as entered |
|
|
43
54
|
| `Editor="inline" Type="bool"` | `"No"` or `"Yes"` |
|
|
44
55
|
| `Editor="inline" Type="enum:Item1;Item2;..."` | The selected item string (one of the declared values) |
|
|
45
56
|
|
|
57
|
+
> The `rational` (decimal) inline editor is not allowed in Node.js scripts. Don't declare
|
|
58
|
+
> `Type="rational"`.
|
|
59
|
+
|
|
46
60
|
---
|
|
47
61
|
|
|
48
62
|
## Modal editors
|
|
@@ -58,7 +72,7 @@ open a dialog when the user clicks the editor button. All have `Type="string"` u
|
|
|
58
72
|
| `filetype` | Filename pattern(s) for the selected file type |
|
|
59
73
|
| `types` (`Type="filefilter"`) | `string[]` — one pattern string per selected file type |
|
|
60
74
|
| `askplugin` | String value selected from the library dialog (calls `getLibraryForProperty`) — **requires the script to implement `getLibraryForProperty`** (`getLibraryForConnectionProperty` for a connection field), see [entry-points.md](../switch-api/entry-points.md#property-ui-callbacks) |
|
|
61
|
-
| `askplugin2` | `string[]` — one string per selected library item (calls `getLibraryForMultipleProperty`) — **requires a matching library-callback entry point**, same as `askplugin` above. ⚠ `getLibraryForMultipleProperty` isn't documented anywhere in [entry-points.md](../switch-api/entry-points.md#property-ui-callbacks), which only lists `getLibraryForProperty`/`getLibraryForConnectionProperty` — this may be the same entry point handling both single- and multi-select, or a genuinely separate, undocumented one
|
|
75
|
+
| `askplugin2` | `string[]` — one string per selected library item (calls `getLibraryForMultipleProperty`) — **requires a matching library-callback entry point**, same as `askplugin` above. ⚠ `getLibraryForMultipleProperty` isn't documented anywhere in [entry-points.md](../switch-api/entry-points.md#property-ui-callbacks), which only lists `getLibraryForProperty`/`getLibraryForConnectionProperty` — this may be the same entry point handling both single- and multi-select, or a genuinely separate, undocumented one. Unconfirmed, so treat the name as unverified. |
|
|
62
76
|
| `description` | Multi-line text as a single string (may include newlines) |
|
|
63
77
|
| `scriptexp` | Result of script expression evaluated in job context, as a string |
|
|
64
78
|
| `sltextwithvar` | Single-line text with variables substituted, as a string |
|
|
@@ -158,7 +172,7 @@ offer it as the property's only editor — pair it with a module-free option (e.
|
|
|
158
172
|
|
|
159
173
|
| Property kind | Recommended `Editor` chain | Why |
|
|
160
174
|
|---|---|---|
|
|
161
|
-
| Secret (password, token, API key) | `inline` only, with `Type="password"` | Masks the value and avoids leaking it via logs or variable inspection if a var/expression editor were combined. `password` is a `Type`, not an editor token — there is no `password` entry in the [modal editor](#modal-editors) list, so `Editor="password"` is wrong. |
|
|
175
|
+
| Secret (password, token, API key) | `inline` only, with `Type="password"` and **`Subtype=""`** (not `"inline"`) | Masks the value and avoids leaking it via logs or variable inspection if a var/expression editor were combined. `password` is a `Type`, not an editor token — there is no `password` entry in the [modal editor](#modal-editors) list, so `Editor="password"` is wrong. `Subtype="inline"` (the usual value for a single inline editor, per [script-declaration.md](script-declaration.md#custom-property-elements-elementfields)) is rejected at script load with "editor that is not supported in Node.js scripting"; Switch Designer itself saves `Type="password"` with `Subtype=""`. |
|
|
162
176
|
| Scalar job-specific value (job ID, copy count, company name, date, etc.) | `inline` (`Type` matching the value) + `sltextwithvar` + `scriptexp` | Hard-coded default, plus dynamic substitution and full expression evaluation. |
|
|
163
177
|
| Array/list job-specific value | `stringlist` + `mltextwithvar` + `scriptexp` | One item per line. **Caveat:** the intent is that every path returns a `string[]`, but the [modal editor table](#modal-editors) lists `mltextwithvar` as returning a single string, and which one holds for this chain is unverified. Handle both — split on newlines when a bare string comes back. |
|
|
164
178
|
| Structured blob text (XML/JSON/HTML, email body) | `description` + `mltextwithvar` + `scriptexp` | Free multi-line text suits a single blob of content better than a line-per-item list. |
|
|
@@ -13,6 +13,21 @@ The file `<ScriptID>.xml` defines the script's metadata, custom properties, conn
|
|
|
13
13
|
and execution configuration. It is normally written by **SwitchScripter**, but the format is fully
|
|
14
14
|
documented below — you can edit it directly.
|
|
15
15
|
|
|
16
|
+
<!-- docs-index:contents begin -->
|
|
17
|
+
## Contents
|
|
18
|
+
|
|
19
|
+
- Agent editing policy
|
|
20
|
+
- XML structure overview
|
|
21
|
+
- Built-in ElementFields
|
|
22
|
+
- Custom property elements (ElementFields)
|
|
23
|
+
- ConnectionFields
|
|
24
|
+
- ExtraProperties
|
|
25
|
+
- Type reference
|
|
26
|
+
- Escaped `ValueDescription` payloads
|
|
27
|
+
- Reserved tag names
|
|
28
|
+
- Annotated example
|
|
29
|
+
<!-- docs-index:contents end -->
|
|
30
|
+
|
|
16
31
|
## Agent editing policy
|
|
17
32
|
|
|
18
33
|
- Editing this file directly is safe as long as you follow the rules on this page exactly — the
|
|
@@ -68,7 +83,7 @@ value is non-string (see the table); an empty string field can omit `Type`.
|
|
|
68
83
|
| `Version` | number | Format `[1-9][0-9]*(\.(0\|[1-9][0-9]*))*`, e.g. `1`, `2.1`. SwitchScripter accepts a whole number only for a script and at most one minor level for an app (`2.1`); it resets anything else. Bump once per release, not per edit (see [Agent editing policy](#agent-editing-policy)). Each flow records the version of the element it uses; Switch compares that recorded value with `UpgradeMaximumVersion` to decide whether to show `FlowUpgradeWarning`. |
|
|
69
84
|
| `Keywords` | string | Space/comma/semicolon-separated. Elements pane search. |
|
|
70
85
|
| `Tooltip` | string | Elements pane tooltip. |
|
|
71
|
-
| `IncomingConnections` | `Yes` \| `No` | Add `RequireAtLeastOne="Yes"` when `Yes`. When `Yes`, the script must implement `jobArrived` (optionally `timerFired` too); when `No`, it must implement `timerFired` instead — see [entry-points.md](../switch-api/entry-points.md#job-processing). |
|
|
86
|
+
| `IncomingConnections` | `Yes` \| `No` | Add `RequireAtLeastOne="Yes"` when `Yes`. When `Yes`, the script must implement `jobArrived` (optionally `timerFired` too); when `No`, it must implement `timerFired` instead — see [entry-points.md](../switch-api/entry-points.md#job-processing). Setting `IncomingConnections="Yes" RequireAtLeastOne="No"` makes the incoming connection optional per instance: some instances can have zero incoming connections (e.g. a flow-starting webhook trigger) and others one wired in (e.g. a mid-flow processing step), with the script branching on both cases at runtime, typically gated by a property such as a mode enum. Confirmed working in Switch Designer. |
|
|
72
87
|
| `OutgoingConnections` | `No` \| `One` \| `Unlimited` | Add `RequireAtLeastOne="Yes"` when `One`/`Unlimited`. Always carries `DetailedInfo=""` (or a prose description of the connections, for generated docs). When not `No`, the script must call a `sendTo*()` method at least once for any job it actually routes (a purely timer/event-driven script that never touches a job — polling an API, rotating logs, etc. — is a legitimate exception); when `Unlimited`, never use `job.sendToSingle()` — see [job-patterns.md § Sending jobs](../switch-api/job-patterns.md#sending-jobs). |
|
|
73
88
|
| `ConnectionType` | `Move` \| `Filter` \| `TrafficLight` | Governs which `ConnectionFields` children are legal — see [ConnectionFields](#connectionfields). |
|
|
74
89
|
| `FunctionsNodeJSScript` | `;`-separated names | Entry point function names found in the script. Omit the element entirely if there is no Node.js program. |
|
|
@@ -120,7 +135,7 @@ Key attributes:
|
|
|
120
135
|
| `UserDefined` | Always `"true"` for custom properties. |
|
|
121
136
|
| `Type` | Value type discriminator — see [Type reference](#type-reference). |
|
|
122
137
|
| `Editor` | `;`-joined chain of editor tokens (inline editor first, then modal editors) — see [property-editors.md](property-editors.md). |
|
|
123
|
-
| `Subtype` | Which editor in the `Editor` chain is currently active for the stored value. For a single-editor property this equals that editor's token (e.g. `"inline"`); for a bare modal editor with no inline component, leave it `""`. This attribute is read by Switch's validator at runtime, not just bookkeeping — keep it consistent with `Editor`. |
|
|
138
|
+
| `Subtype` | Which editor in the `Editor` chain is currently active for the stored value. For a single-editor property this equals that editor's token (e.g. `"inline"`); for a bare modal editor with no inline component, leave it `""`. Exception: `Type="password"` takes `Subtype=""` even though `Editor="inline"` alone — see [property-editors.md](property-editors.md#common-practices). This attribute is read by Switch's validator at runtime, not just bookkeeping — keep it consistent with `Editor`. |
|
|
124
139
|
| `LocalizedTagName` | Display name shown in the Properties pane. **App guideline (mandatory for apps, recommended for scripts):** 30 characters max, sentence-style capitalization (`"Customer name"`, not `"Customer Name"`). |
|
|
125
140
|
| `Tooltip` | Tooltip shown in the Properties pane. **App guideline (mandatory for apps, recommended for scripts):** give every property a tooltip. The only exception is a property that's fully self-explanatory from its name alone with no extra constraints (e.g. "Number of copies" with no min/max) — if there's anything a user would need to know (units, valid range, format, an example value), put it in the tooltip. |
|
|
126
141
|
| `DetailedInfo` | Long-form description for generated docs. Always include the attribute (empty string `""` is fine) — omitting it makes the property non-editable in Switch Designer. |
|
|
@@ -258,7 +273,6 @@ If no OAuth property exists on the script, omit `ExtraProperties` entirely or le
|
|
|
258
273
|
|---|---|
|
|
259
274
|
| `string` | Text |
|
|
260
275
|
| `number` | Integer |
|
|
261
|
-
| `rational` | Decimal |
|
|
262
276
|
| `password` | Masked text |
|
|
263
277
|
| `bool` | `Yes`/`No` |
|
|
264
278
|
| `date`, `datetime`, `time` | `time` is hours-and-minutes |
|
|
@@ -272,11 +286,13 @@ This is the *authoring* vocabulary written into the XML `Type` attribute. It doe
|
|
|
272
286
|
onto the runtime `PropertyType` returned by `getPropertyType()` (see
|
|
273
287
|
[property-editors.md](property-editors.md) and [enums.md](../switch-api/enums.md)) — e.g. XML
|
|
274
288
|
`bool` becomes runtime `boolean`, XML `path` splits into runtime `filepath`/`folderpath`, and several
|
|
275
|
-
XML types (`
|
|
289
|
+
XML types (`stringlist`, `accountlist`, `enum:...`, `datetime`, `time`) have no direct
|
|
276
290
|
`PropertyType` counterpart while several runtime values (`filetype`, `folderpattern`, `regex`,
|
|
277
291
|
`oauthtoken`, `literal`) have no direct XML `Type` counterpart. Don't assume the two vocabularies are
|
|
278
292
|
interchangeable.
|
|
279
293
|
|
|
294
|
+
`rational` (decimal) is not a valid `Type` for Node.js scripts.
|
|
295
|
+
|
|
280
296
|
`Type` is not independently authored on properties with a modal editor chain — it follows from
|
|
281
297
|
which editor(s) you choose, per [property-editors.md](property-editors.md). When combining
|
|
282
298
|
an inline editor with modal editors (e.g. `Editor="inline;sltextwithvar;scriptexp"`), the inline
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
id: script-structure
|
|
3
3
|
category: switch-project
|
|
4
4
|
order: 10
|
|
5
|
-
summary: "What files a script folder contains, laying out several script folders side by side, `manifest.xml` format,
|
|
5
|
+
summary: "What files a script folder contains, laying out several script folders side by side, `manifest.xml` format, Script vs App, packing an app with SwitchScripter"
|
|
6
6
|
triggers:
|
|
7
|
-
- "Setting up a new script project
|
|
7
|
+
- "Setting up a new script project or converting a Script to an App"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Script Project Structure
|
|
@@ -13,6 +13,20 @@ A Switch script project lives in a **script folder** during development and is d
|
|
|
13
13
|
|
|
14
14
|
> **Agent editing policy**: Edit TypeScript/JavaScript source files (`main.ts`, `main.js`) and the XML declaration (`<ScriptID>.xml`) freely — see [script-declaration.md](script-declaration.md) for the declaration's rules, and [entry-points.md § Entry-point scanner constraints](../switch-api/entry-points.md#entry-point-scanner-constraints) before writing any string/regex literal or division, since certain shapes make Switch's entry-point scanner silently drop functions. Leave `manifest.xml` to the user unless explicitly asked.
|
|
15
15
|
|
|
16
|
+
<!-- docs-index:contents begin -->
|
|
17
|
+
## Contents
|
|
18
|
+
|
|
19
|
+
- Script folder files
|
|
20
|
+
- Several script folders side by side
|
|
21
|
+
- manifest.xml format
|
|
22
|
+
- Node.js version per Switch version
|
|
23
|
+
- Script vs App
|
|
24
|
+
- Packing an app
|
|
25
|
+
- SwitchScripter and Switch version compatibility
|
|
26
|
+
- App Store submission guidelines
|
|
27
|
+
- Execution modes
|
|
28
|
+
<!-- docs-index:contents end -->
|
|
29
|
+
|
|
16
30
|
## Script folder files
|
|
17
31
|
|
|
18
32
|
| File | Required | Notes |
|
|
@@ -80,78 +94,8 @@ my-collection/ the folder the user works in; AI agent config and docs liv
|
|
|
80
94
|
|
|
81
95
|
## Node.js version per Switch version
|
|
82
96
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
| Switch version | `SwitchVersion` | Node.js used |
|
|
87
|
-
|---|---|---|
|
|
88
|
-
| Switch 2020 Spring | `20.0` | 12 |
|
|
89
|
-
| Switch 2020 Fall | `20.1` | 12 |
|
|
90
|
-
| Switch 2021 Spring | `21.0` | 14 |
|
|
91
|
-
| Switch 2021 Fall | `21.1` | 16 |
|
|
92
|
-
| Switch 2022 Spring | `22.0` | 16 |
|
|
93
|
-
| Switch 2022 Fall | `22.1` | 16 |
|
|
94
|
-
| Switch 2023 Fall | `23.1` | 18 |
|
|
95
|
-
| Switch 2024 Spring | `24.0` | 18 |
|
|
96
|
-
| Switch 2024 Fall | `24.1` | 20 |
|
|
97
|
-
| Switch 25.07 | `25.07` | 20 |
|
|
98
|
-
| Switch 25.11 | `25.11` | 20 |
|
|
99
|
-
| Switch 26.03 | `26.03` | 24 |
|
|
100
|
-
| Switch 26.07 | `26.07` | 24 |
|
|
101
|
-
|
|
102
|
-
Switch 26.07 bundles Node.js 12.22.12, 14.21.3, 16.20.2, 18.20.8, 20.20.0, and 24.13.0.
|
|
103
|
-
|
|
104
|
-
`SwitchVersion` controls the Node.js version only. Which scripting API methods exist depends on the
|
|
105
|
-
Switch version the script runs on, see [api-versions.md](../switch-api/api-versions.md).
|
|
106
|
-
|
|
107
|
-
### What writes `SwitchVersion`
|
|
108
|
-
|
|
109
|
-
`SwitchVersion` is the version of whichever tool last wrote the manifest. It is not a setting the
|
|
110
|
-
author picks:
|
|
111
|
-
|
|
112
|
-
- `SwitchScriptTool --create` writes the tool's own version into the new script folder.
|
|
113
|
-
- `SwitchScriptTool --pack` writes the tool's own version into the packed `.sscript`, whatever the
|
|
114
|
-
script folder's manifest says. The script folder's manifest itself is left unchanged. Verified
|
|
115
|
-
with SwitchScriptTool 26.07: a folder whose manifest said `21.0` packed to a `.sscript` whose
|
|
116
|
-
manifest says `26.07`, which moves the script from Node.js 16 to Node.js 24.
|
|
117
|
-
- SwitchScripter writes its own version on every save. Opening a script with a different
|
|
118
|
-
`SwitchVersion` shows a warning that saving updates the script to the current Node.js version.
|
|
119
|
-
|
|
120
|
-
**Gotcha:** a script folder tested directly in Switch and the `.sscript` packed from it can run on
|
|
121
|
-
different Node.js versions. A folder created with an older SwitchScriptTool keeps that tool's
|
|
122
|
-
version, but packing it with a newer SwitchScriptTool stamps the newer one. Test the packed
|
|
123
|
-
`.sscript`, not only the folder, before shipping it.
|
|
124
|
-
|
|
125
|
-
To target an older Node.js version, build the package with the SwitchScriptTool or SwitchScripter
|
|
126
|
-
of that Switch release. Editing `SwitchVersion` by hand doesn't last, because `--pack` and
|
|
127
|
-
SwitchScripter overwrite it.
|
|
128
|
-
|
|
129
|
-
### Values missing from the table
|
|
130
|
-
|
|
131
|
-
Behavior of the Switch 26.07 Server:
|
|
132
|
-
|
|
133
|
-
- No `SwitchVersion` element: treated as `20.1`, so Node.js 12.
|
|
134
|
-
- Lower than the first entry: Node.js 12.
|
|
135
|
-
- Higher than the last entry, for example a package built by a newer tool than the Switch it runs
|
|
136
|
-
on: the newest Node.js that Switch bundles. The Server logs a debug message that the script
|
|
137
|
-
"might not be compatible with the current Switch version" and runs it anyway.
|
|
138
|
-
- **Known issue:** a value that isn't an exact entry but falls numerically between the first and
|
|
139
|
-
last entries matches no Node.js version, for example `25.7` instead of `25.07`, `24.10` instead of
|
|
140
|
-
`24.1`, or `23.0`. The Server then fails to build the path to a Node.js binary, so no executor
|
|
141
|
-
process starts for the script. This was confirmed by running the Switch 26.07 lookup code with
|
|
142
|
-
these values, not by running such a script in Switch, so the exact symptom in Switch hasn't been
|
|
143
|
-
checked. A value such as `26.3` works today only because it's higher than the last entry, and it
|
|
144
|
-
will break once a later release adds an entry above it. Never hand-edit `SwitchVersion`. If you
|
|
145
|
-
must, copy a value exactly as the table above spells it.
|
|
146
|
-
|
|
147
|
-
### Other effects of `SwitchVersion`
|
|
148
|
-
|
|
149
|
-
- npm dependencies that are ES modules are bundled with esbuild before execution only when
|
|
150
|
-
`SwitchVersion` is `24.0` or higher.
|
|
151
|
-
- A script expression has no manifest. It always runs on the newest Node.js the running Switch
|
|
152
|
-
bundles.
|
|
153
|
-
|
|
154
|
-
> **Mac (Apple Silicon):** For scripts whose `SwitchVersion` is `21.1` (Switch 2021 Fall) or later, Switch runs the native ARM build of Node.js on Apple Silicon. Scripts with an earlier `SwitchVersion` run under Rosetta, because the bundled Node.js 12 and 14 are Intel-only builds. If a script bundles platform-specific binaries, ship both Intel and ARM variants.
|
|
97
|
+
`SwitchVersion` in `manifest.xml` selects the Node.js version the script runs on, and SwitchScriptTool
|
|
98
|
+
and SwitchScripter overwrite it. See [node-versions.md](node-versions.md).
|
|
155
99
|
|
|
156
100
|
## Script vs App
|
|
157
101
|
|
|
@@ -186,14 +130,14 @@ joined up explicitly:
|
|
|
186
130
|
An app loads in the Switch version it was built with **or newer**, never older. So the SwitchScripter
|
|
187
131
|
used must be at or below the oldest Switch version you intend to support, and portable
|
|
188
132
|
SwitchScripters exist for older releases specifically to build backwards-compatible apps. There is
|
|
189
|
-
no SwitchScripter for Switch 25.
|
|
133
|
+
no SwitchScripter for Switch 25.11 — use the Switch 2024 Fall SwitchScripter for it.
|
|
190
134
|
Apps as a concept require Switch 13.1 or later.
|
|
191
135
|
|
|
192
136
|
The SwitchScripter version also fixes the app's Node.js version, see
|
|
193
|
-
[
|
|
137
|
+
[node-versions.md](node-versions.md). An app built with the
|
|
194
138
|
Switch 2024 Fall SwitchScripter has `SwitchVersion` `24.1`, so it runs on Node.js 20 in every Switch
|
|
195
|
-
version that loads it, including 26.x. Switch 26.
|
|
196
|
-
it runs on Node.js 24, and it loads only in Switch 26.
|
|
139
|
+
version that loads it, including 26.x. Switch 26.11 ships SwitchScripter 26.11. An app built with
|
|
140
|
+
it runs on Node.js 24, and it loads only in Switch 26.11 or newer.
|
|
197
141
|
|
|
198
142
|
An unsigned app loads only in Switch on the machine that created it. That's what allows local
|
|
199
143
|
testing before submission; it loads elsewhere only once Enfocus has signed it.
|