@enfocussw/switch-scripting-context 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +504 -0
  2. package/README.md +192 -0
  3. package/bin/cli.js +8 -0
  4. package/dist/init.d.ts +78 -0
  5. package/dist/init.js +894 -0
  6. package/docs/switch-api/api-versions.md +127 -0
  7. package/docs/switch-api/connection.md +82 -0
  8. package/docs/switch-api/document-classes.md +189 -0
  9. package/docs/switch-api/entry-points.md +185 -0
  10. package/docs/switch-api/enums.md +181 -0
  11. package/docs/switch-api/execution-environment.md +143 -0
  12. package/docs/switch-api/flow-element.md +143 -0
  13. package/docs/switch-api/http.md +96 -0
  14. package/docs/switch-api/job-patterns.md +238 -0
  15. package/docs/switch-api/job.md +187 -0
  16. package/docs/switch-api/logging.md +117 -0
  17. package/docs/switch-api/switch.md +210 -0
  18. package/docs/switch-appstore/app-guidelines.md +281 -0
  19. package/docs/switch-appstore/app-manual.md +84 -0
  20. package/docs/switch-appstore/app-store-listing.md +69 -0
  21. package/docs/switch-appstore/app-store-submission.md +83 -0
  22. package/docs/switch-project/debugging.md +61 -0
  23. package/docs/switch-project/logs-and-dataroot.md +80 -0
  24. package/docs/switch-project/node-versions.md +87 -0
  25. package/docs/switch-project/project-planning.md +149 -0
  26. package/docs/switch-project/property-documentation.md +75 -0
  27. package/docs/switch-project/property-editors.md +249 -0
  28. package/docs/switch-project/script-declaration.md +407 -0
  29. package/docs/switch-project/script-structure.md +157 -0
  30. package/docs/switch-project/tooling.md +165 -0
  31. package/docs/switch-project/vscode.md +90 -0
  32. package/docs/switch-scripting.md +70 -0
  33. package/package.json +65 -0
@@ -0,0 +1,407 @@
1
+ ---
2
+ id: script-declaration
3
+ category: switch-project
4
+ order: 14
5
+ summary: "XML declaration reference: properties, connections, execution config; agents may edit this file directly"
6
+ triggers:
7
+ - "Understanding, adding, or editing script properties/connections/execution config"
8
+ ---
9
+
10
+ # Script Declaration (XML)
11
+
12
+ The file `<ScriptID>.xml` defines the script's metadata, custom properties, connection properties,
13
+ and execution configuration. It is normally written by **SwitchScripter**, but the format is fully
14
+ documented below — you can edit it directly.
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
+
31
+ ## Agent editing policy
32
+
33
+ - Editing this file directly is safe as long as you follow the rules on this page exactly — the
34
+ format has no schema, so Switch will silently misbehave (not error) on a malformed attribute.
35
+ - Preserve every attribute you don't intend to change. Don't reformat, reorder attributes, or drop
36
+ fields you don't understand.
37
+ - `Name` is the script's stable ID — never change it on an existing script (it's used as the flow
38
+ element type and referenced by installed flows). `DisplayName`, tooltips, defaults, adding/removing
39
+ custom properties, and connection topology are all safe to change directly.
40
+ - `Version` identifies a released build, not an edit. Bump it only when the current value has
41
+ already been released (installed on a customer's or production Switch, or published on the
42
+ Appstore). If the current value was never released, keep editing under it; don't bump per
43
+ change. See the `Version` row below for format and what Switch does with it.
44
+ - Custom property tag names must match `^[A-Za-z][A-Za-z0-9]*$`, must not start with `xml`
45
+ (case-insensitive), and must not collide with a [reserved tag](#reserved-tag-names) below. Switch
46
+ does **not** sanitize or rewrite bad tag names on load — it just fails to work correctly.
47
+ - `manifest.xml` (declares the package's program file(s) and type) rarely needs edits; treat it as
48
+ low-risk to read but confirm with the user before changing `ScriptPackageType`,
49
+ `ScriptDeclarationFile`, or `ScriptProgramFiles`.
50
+ - Never add or edit `ApplicationPath` or `ApplicationLicense` — these are app-only properties that
51
+ point Switch at a third-party application's install location and license. They must be set by the
52
+ user from the Switch Scripter GUI after the script package has been packed; Switch silently drops
53
+ them if written into the declaration file directly.
54
+ - **Gotcha: reload the script element after changing properties.** When the declaration of a script
55
+ that is already used in a flow gains a property or a property changes, right-click the element in
56
+ Switch Designer and choose "Reload script" before activating the flow. Until then the element
57
+ keeps the old declaration: a new property doesn't appear in the element, and
58
+ `getPropertyStringValue()` on it throws `"Invalid tag: <tag>"`. Seen with a script folder on
59
+ Switch 26.07.
60
+
61
+ ---
62
+
63
+ ## XML structure overview
64
+
65
+ ```xml
66
+ <?xml version="1.0" encoding="UTF-8"?>
67
+ <Object>
68
+ <ElementFields> <!-- required: built-in fields + custom property elements -->
69
+ <ConnectionFields> <!-- optional: custom outgoing-connection property elements -->
70
+ <ExtraProperties> <!-- optional: OAuth 2.0 editor config only -->
71
+ </Object>
72
+ ```
73
+
74
+ `ElementFields` is required — Switch rejects the whole declaration without it. `ConnectionFields`
75
+ and `ExtraProperties` may be omitted or left empty.
76
+
77
+ ---
78
+
79
+ ## Built-in ElementFields
80
+
81
+ These configure the flow element (name, topology, execution, pane placement, docs). All carry
82
+ `Editor="hide"` and are never shown to the user directly. Add `Type="..."` to a field only when the
83
+ value is non-string (see the table); an empty string field can omit `Type`.
84
+
85
+ | Element | Values | Notes |
86
+ |---|---|---|
87
+ | `Name` | string | Stable script ID — **never change on an existing script**. Becomes the flow element `Type`. |
88
+ | `DisplayName` | string | Shown in Switch. Empty falls back to `Name`. A `~` splits "Vendor~Element" — Switch shows the part after `~` as the short name and the whole string (space-joined) as the full name. |
89
+ | `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`. |
90
+ | `Keywords` | string | Space/comma/semicolon-separated. Elements pane search. |
91
+ | `Tooltip` | string | Elements pane tooltip. |
92
+ | `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. |
93
+ | `OutgoingConnections` | `No` \| `One` \| `Unlimited` | Add `RequireAtLeastOne="Yes"` when `One`/`Unlimited`. `One` with `RequireAtLeastOne="No"` makes the outgoing connection optional: a flow with an instance that had no outgoing connection validated and activated (Switch 26.07). 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). |
94
+ | `ConnectionType` | `Move` \| `Filter` \| `TrafficLight` | Governs which `ConnectionFields` children are legal — see [ConnectionFields](#connectionfields). |
95
+ | `FunctionsNodeJSScript` | `;`-separated names | Entry point function names found in the script. Omit the element entirely if there is no Node.js program. |
96
+ | `ExecutionMode` | `Concurrent` \| `Serialized` | |
97
+ | `NumberOfSlots` | `Default` \| integer | Only meaningful when `Concurrent`. |
98
+ | `ExecutionGroup` | string | Only meaningful when `Serialized`. Empty falls back to `Name`. |
99
+ | `PerformanceTuning` | `Yes` \| `No` | When `Yes`, `IdleAfterJob` (and `NumberOfSlots` if concurrent) become visible/editable in Switch. |
100
+ | `IdleAfterJob` | number (seconds) | |
101
+ | `PositionInElementPane` | `Basics` \| `Tools` \| `Communication` \| `Apps` \| `Configurators` \| `Metadata` \| `Database` | Which section of the Elements pane the script appears under. |
102
+ | `SubcategoryInElementPane` | escaped XML, see [§ escaped payloads](#escaped-valuedescription-payloads) | Apps/Configurators only. |
103
+ | `DispositionInElementPane` | numeric string or empty | Sort position within the category. Empty = alphabetical. |
104
+ | `Description` | string | Long description. |
105
+ | `Compatibility` | string | Configurator only: third-party app compatibility notes. |
106
+ | `SupportInfo` | string | Configurator only: support email/URL. |
107
+ | `AppDiscovery` | string | Configurator only: how the app is located on disk. |
108
+ | `FlowUpgradeWarning` | string | Warning shown during flow upgrade. |
109
+ | `UpgradeMaximumVersion` | number | Previous version up to which the upgrade warning is shown. Optional. |
110
+ | `Connections` | string | Prose description of outgoing connections, for generated docs. |
111
+ | `SwitchModule` | `Configurator` \| `Metadata` \| `Scripting` \| `Database` \| `SwitchClient` \| `SwitchClientSDK` | Licensing module. |
112
+ | `ObsoleteProperties` | `;`/`,`/whitespace-separated tag names | Suppresses "unknown property" warnings when a flow already has properties you removed. Add old tag names here when removing a custom property. |
113
+ | `ObsoleteConnectionProperties` | same | Same, for removed connection properties. |
114
+
115
+ Do **not** add `ApplicationPath` or `ApplicationLicense` as element names — Switch silently drops
116
+ them. These app-only properties must be configured by the user from the Switch Scripter GUI after
117
+ the package is packed, not written into the declaration file.
118
+
119
+ ---
120
+
121
+ ## Custom property elements (ElementFields)
122
+
123
+ Each user-defined property is a child element of `<ElementFields>` with `UserDefined="true"`. The
124
+ element name is the property's **tag** (used as the `tag` argument in `getPropertyStringValue()`)
125
+ and must follow the [tag name rules](#reserved-tag-names).
126
+
127
+ The element must **never be self-closing**. Its text content holds the property's *current value*
128
+ — e.g. `<Count ...>8</Count>`, not `<Count ... Default="8" />`. A self-closing custom property is
129
+ non-editable in Switch Designer.
130
+
131
+ ```xml
132
+ <MyProperty UserDefined="true" Type="string" Editor="choosefolder" LocalizedTagName="My Property"
133
+ Tooltip="Select the output folder" DetailedInfo="" Validation="Standard" Default=""
134
+ Subtype="">Default value goes here</MyProperty>
135
+ ```
136
+
137
+ Key attributes:
138
+
139
+ | Attribute | Description |
140
+ |---|---|
141
+ | `UserDefined` | Always `"true"` for custom properties. |
142
+ | `Type` | Value type discriminator — see [Type reference](#type-reference). |
143
+ | `Editor` | `;`-joined chain of editor tokens (inline editor first, then modal editors) — see [property-editors.md](property-editors.md). |
144
+ | `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`. |
145
+ | `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"`). |
146
+ | `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. |
147
+ | `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. |
148
+ | `Validation` | `None` (skip validation) \| `Standard` (built-in validator) \| `Custom` (needs a `validateProperties` entry point) \| `Standard and custom` (both, custom only runs if standard passes). If any property uses `Custom` or `Standard and custom`, the script must implement `validateProperties` (or `validateConnectionProperties` for a connection field) — see [entry-points.md](../switch-api/entry-points.md#property-ui-callbacks). To let a property hold an empty value under `Standard` validation, add the `none` literal editor to the `Editor` chain (see [property-editors.md § Literal editors](property-editors.md#literal-editors)) instead of relaxing to `Validation="None"` just to permit emptiness. |
149
+ | `Default` | Default value string. Omit only if the value is emitted as CDATA instead. **App guideline (mandatory for apps, recommended for scripts):** always provide a default unless there's genuinely no sensible one; if a default truly isn't possible, give an example value in the `Tooltip` instead. |
150
+ | `Dependency` | Tag of the master property this one depends on. Written flat (not nested) — the dependent element is a sibling of its master, not a child. |
151
+ | `DependencyCondition` | One of: `Not-empty`, `Equals`, `Not-equals`, `Contains`, `Does not contain`, `Matches`, `Does not match`, `Starts with`, `Does not start with`. |
152
+ | `Dependencyvalue` | Value(s) to compare against — `;`-separated for multiple. Note the lowercase `v`. |
153
+
154
+ **Dependency example** — show `OutputFolder` only when `Mode` equals `"Custom"`:
155
+
156
+ ```xml
157
+ <Mode UserDefined="true" Type="enum:Default;Custom" Editor="inline" Subtype="inline"
158
+ LocalizedTagName="Mode" Tooltip="" DetailedInfo="" Validation="Standard"
159
+ Default="Default">Default</Mode>
160
+ <OutputFolder UserDefined="true" Type="string" Editor="choosefolder" Subtype=""
161
+ Dependency="Mode" DependencyCondition="Equals" Dependencyvalue="Custom"
162
+ LocalizedTagName="Output folder" Tooltip="" DetailedInfo=""
163
+ Validation="Standard" Default=""></OutputFolder>
164
+ ```
165
+
166
+ **Runtime pitfall:** when a dependent property is hidden (its master's current value doesn't satisfy
167
+ `DependencyCondition`/`Dependencyvalue`), Switch treats the tag as if it doesn't exist. Calling
168
+ `flowElement.getPropertyStringValue(tag)` on it throws `"Invalid tag: <tag>"` — see
169
+ [flow-element.md](../switch-api/flow-element.md#properties). Only fetch a dependent property once you know
170
+ its condition holds, either by checking the already-read master value yourself, or by calling
171
+ `flowElement.hasProperty(tag)` first (it returns `false` for a hidden dependent). This matters most
172
+ for an enum/drop-down master with several dependents keyed off different values — fetch only the
173
+ ones actually shown for the current selection, not all of them.
174
+
175
+ A script must never assume the set of fetchable dependents is fixed once at flow configuration
176
+ time: if the master's own value is dynamic (a Switch variable or script expression, resolved per
177
+ job in `jobArrived`), which dependents are shown can differ from job to job in the same flow
178
+ execution. Re-check the condition (or call `hasProperty()`) on every invocation rather than caching
179
+ an earlier "shown" determination. There is no way to read a currently-hidden dependent's value for
180
+ a given job — if a script genuinely needs data from more than one dependency group at once (e.g.
181
+ values entered while the master previously pointed elsewhere), this pattern cannot provide it, and
182
+ a different property structure (not `Dependency`-based hiding) is needed instead.
183
+
184
+ Attribute order in the file doesn't matter functionally — SwitchScripter's own output happens to be
185
+ alphabetical (QDom sorts attributes on write), but any order parses correctly.
186
+
187
+ ---
188
+
189
+ ## ConnectionFields
190
+
191
+ Custom outgoing-connection properties follow the same attribute pattern as element properties, with
192
+ one addition: `AvailableFor="Data"` or `AvailableFor="Log"` restricts which connection kind shows the
193
+ property; omit the attribute for both.
194
+
195
+ ```xml
196
+ <ConnectionFields>
197
+ <TargetFolder AvailableFor="Data" UserDefined="true" Type="string" Editor="choosefolder"
198
+ Subtype="" LocalizedTagName="Target folder" Tooltip="" DetailedInfo=""
199
+ Validation="Standard" Default=""></TargetFolder>
200
+ </ConnectionFields>
201
+ ```
202
+
203
+ Connection property values are read at runtime via `connection.getPropertyStringValue(tag)`.
204
+
205
+ **`ConnectionType` gates which built-in connection fields may appear here** — this is the single
206
+ most important thing to get right when hand-editing this section:
207
+
208
+ | `ConnectionType` | Legal built-in children |
209
+ |---|---|
210
+ | `Move` | none — `<ConnectionFields/>` must be empty of built-ins |
211
+ | `Filter` | `IncludeFolderMask`, `ExcludeFolderMask` (both or neither) |
212
+ | `TrafficLight` | `Success`, `Warning`, `Error`, each optionally with `AvailableFor="Data"` or `AvailableFor="Log"` (omit the attribute if available for both; omit the whole element if available for neither) |
213
+
214
+ ```xml
215
+ <!-- ConnectionType = TrafficLight -->
216
+ <ConnectionFields>
217
+ <Success AvailableFor="Data" LocalizedTagName="Success out" Type="bool" Editor="inline" Default="Yes">Yes</Success>
218
+ <Error AvailableFor="Data" LocalizedTagName="Error out" Type="bool" Editor="inline" Default="Yes">Yes</Error>
219
+ <!-- no Warning element: not available for Data or Log -->
220
+ </ConnectionFields>
221
+ ```
222
+
223
+ Custom (author-defined) connection properties may be added regardless of `ConnectionType`.
224
+
225
+ The declared connections and the script's `sendTo*()` calls must agree with each other — see
226
+ [job-patterns.md § Sending jobs](../switch-api/job-patterns.md#sending-jobs) for the exact rules per
227
+ `ConnectionType`. In particular, omit a `Success`/`Warning`/`Error` element entirely (as shown
228
+ above) if the script never routes to it, rather than leaving an unused connection declared.
229
+
230
+ **Caution — package-swap connection preservation:** Switch Designer decides whether existing flow
231
+ connections survive a script package replace by comparing the serialised `ConnectionFields` and
232
+ `IncomingConnections` text **byte-for-byte** between old and new declarations, and additionally
233
+ checks whether `ConnectionType`/`OutgoingConnections` changed. Don't make cosmetic whitespace or
234
+ attribute-order changes to those sections without expecting existing flows' outgoing connection
235
+ properties to reset on next package update.
236
+
237
+ ---
238
+
239
+ ## ExtraProperties
240
+
241
+ Only used for OAuth 2.0 (`Editor` containing `oauth`, see
242
+ [property-editors.md](property-editors.md)). One child element per OAuth property, named
243
+ identically to the property's tag, in `ElementFields` or `ConnectionFields`:
244
+
245
+ ```xml
246
+ <ElementFields>
247
+ <OAuth1 UserDefined="true" Type="string" Editor="oauth" Subtype="" LocalizedTagName="OAuth1"
248
+ Tooltip="" DetailedInfo="" Validation="Standard" Default=""></OAuth1>
249
+ </ElementFields>
250
+ <ExtraProperties>
251
+ <OAuth1 OAuthAuthorizationEndpoint="http://auth-url"
252
+ OAuthClientID="AppID1"
253
+ OAuthClientSecret="Application Password 123"
254
+ OAuthClientSecretEditor="inline"
255
+ OAuthRedirPorts="666"
256
+ OAuthRedirPortsEditor="inline"
257
+ OAuthScope="some-random-scope"
258
+ OAuthScopeEditor="inline"
259
+ OAuthTokenEndpoint="http://token-url"/>
260
+ </ExtraProperties>
261
+ ```
262
+
263
+ Attributes: `OAuthAuthorizationEndpoint`, `OAuthClientID`, `OAuthClientSecret`, `OAuthRedirPorts`,
264
+ `OAuthTokenEndpoint`, `OAuthScope`. A companion `<Attr>Editor` attribute is only needed when that
265
+ attribute has more than one valid input mode; otherwise omit it. If the package is
266
+ password-protected, `OAuthClientSecret` must be stored encrypted — leave OAuth secret edits to the
267
+ user rather than writing a plaintext secret into a password-protected package.
268
+
269
+ If no OAuth property exists on the script, omit `ExtraProperties` entirely or leave it empty
270
+ (`<ExtraProperties/>`).
271
+
272
+ ---
273
+
274
+ ## Type reference
275
+
276
+ `Type` is a single token, except for enums:
277
+
278
+ | Token | Meaning |
279
+ |---|---|
280
+ | `string` | Text |
281
+ | `number` | Integer |
282
+ | `password` | Masked text |
283
+ | `bool` | `Yes`/`No` |
284
+ | `date`, `datetime`, `time` | `time` is hours-and-minutes |
285
+ | `path` | Filesystem path |
286
+ | `enum:A;B;C` | Closed choice list, `;`-separated (a trailing `;` is tolerated) |
287
+ | `filefilter`, `folderfilter` | File / folder filter values |
288
+ | `stringlist` | List of strings |
289
+ | `accountlist` | Account list |
290
+
291
+ This is the *authoring* vocabulary written into the XML `Type` attribute. It does not map one-to-one
292
+ onto the runtime `PropertyType` returned by `getPropertyType()` (see
293
+ [property-editors.md](property-editors.md) and [enums.md](../switch-api/enums.md)) — e.g. XML
294
+ `bool` becomes runtime `boolean`, XML `path` splits into runtime `filepath`/`folderpath`, and several
295
+ XML types (`stringlist`, `accountlist`, `enum:...`, `datetime`, `time`) have no direct
296
+ `PropertyType` counterpart while several runtime values (`filetype`, `folderpattern`, `regex`,
297
+ `oauthtoken`, `literal`) have no direct XML `Type` counterpart. Don't assume the two vocabularies are
298
+ interchangeable.
299
+
300
+ `rational` (decimal) is not a valid `Type` for Node.js scripts.
301
+
302
+ `Type` is not independently authored on properties with a modal editor chain — it follows from
303
+ which editor(s) you choose, per [property-editors.md](property-editors.md). When combining
304
+ an inline editor with modal editors (e.g. `Editor="inline;sltextwithvar;scriptexp"`), the inline
305
+ editor's type wins: an inline Number editor plus those modal editors still yields `Type="number"`.
306
+
307
+ ---
308
+
309
+ ## Escaped `ValueDescription` payloads
310
+
311
+ `SubcategoryInElementPane` (and a couple of internal-only fields) store a nested XML fragment,
312
+ escaped into the text node:
313
+
314
+ ```xml
315
+ <SubcategoryInElementPane Editor="hide">&lt;ValueDescription Type="stringlist">
316
+ &lt;Value>Communication&lt;/Value>
317
+ &lt;/ValueDescription>
318
+ </SubcategoryInElementPane>
319
+ ```
320
+
321
+ Decodes to:
322
+
323
+ ```xml
324
+ <ValueDescription Type="stringlist">
325
+ <Value>Communication</Value>
326
+ </ValueDescription>
327
+ ```
328
+
329
+ One `Value` child per subcategory string. Escaping only `<` (not also `&quot;`) is fine — both forms
330
+ parse.
331
+
332
+ ---
333
+
334
+ ## Reserved tag names
335
+
336
+ A custom property or connection property tag must not collide with any built-in field name. Do not
337
+ use any of:
338
+
339
+ - Any [built-in `ElementFields` name](#built-in-elementfields) above.
340
+ - Any built-in `ConnectionFields` name: `IncludeFolderMask`, `ExcludeFolderMask`, `Success`,
341
+ `Warning`, `Error`.
342
+ - `Type`, `Category`, `ElementDescription`, `LogDebugMessages`, `SPSDebugMode`, `SPSDebugPort`,
343
+ `SPSDebugEntryPoints`, `AbortTimeoutMinutes`, `ScriptPackagePath`, `Xpos`, `Ypos`, `ElementIcon`,
344
+ `LastModified`, `Path`, `customExecutionMode`, `AdvancedTuningEnabled`, `Hold`, `TrafficType`,
345
+ `CorneringFactor`, `ElementType`.
346
+ - `ApplicationPath`, `ApplicationLicense` (legal as a tag, but Switch silently drops these two from
347
+ the flow element's property set, so don't use them). These are app-only properties, set by the
348
+ user from the Switch Scripter GUI after packing — never modify them directly.
349
+
350
+ Also required: tag matches `^[A-Za-z][A-Za-z0-9]*$` and does not start with `xml` (case-insensitive).
351
+
352
+ ---
353
+
354
+ ## Annotated example
355
+
356
+ Representative declaration with Move connections, custom properties, and a dependency chain:
357
+
358
+ ```xml
359
+ <Object>
360
+ <ElementFields>
361
+ <Name Editor="hide" Type="string">depositJob</Name>
362
+ <DisplayName Editor="hide" Type="string"></DisplayName>
363
+ <Version Editor="hide" Type="number">1</Version>
364
+ <Keywords Editor="hide" Type="string"></Keywords>
365
+ <Tooltip Editor="hide" Type="string"></Tooltip>
366
+ <IncomingConnections RequireAtLeastOne="Yes" Editor="hide">Yes</IncomingConnections>
367
+ <OutgoingConnections RequireAtLeastOne="Yes" DetailedInfo="" Editor="hide">Unlimited</OutgoingConnections>
368
+ <ConnectionType Editor="hide">Move</ConnectionType>
369
+ <FunctionsNodeJSScript Editor="hide">jobArrived</FunctionsNodeJSScript>
370
+ <ExecutionMode Editor="hide">Concurrent</ExecutionMode>
371
+ <NumberOfSlots Editor="hide">Default</NumberOfSlots>
372
+ <ExecutionGroup Editor="hide"></ExecutionGroup>
373
+ <PerformanceTuning Editor="hide">No</PerformanceTuning>
374
+ <IdleAfterJob Editor="hide" Type="number">0</IdleAfterJob>
375
+ <PositionInElementPane Editor="hide">Tools</PositionInElementPane>
376
+ <DispositionInElementPane Editor="hide" Type="number"></DispositionInElementPane>
377
+ <Description Editor="hide" Type="string"></Description>
378
+ <Connections Editor="hide" Type="string"></Connections>
379
+ <SwitchModule Editor="hide" Type="string">Scripting</SwitchModule>
380
+ <ObsoleteProperties Editor="hide" Type="string"></ObsoleteProperties>
381
+ <ObsoleteConnectionProperties Editor="hide" Type="string"></ObsoleteConnectionProperties>
382
+
383
+ <!-- Custom property: folder chooser -->
384
+ <DepositFolder UserDefined="true" Type="string" Editor="choosefolder" Subtype=""
385
+ LocalizedTagName="Deposit folder" Tooltip="" DetailedInfo=""
386
+ Validation="Standard" Default=""></DepositFolder>
387
+
388
+ <!-- Custom property: dropdown enum -->
389
+ <AttachAs UserDefined="true" Type="enum:Job;Dataset" Editor="inline" Subtype="inline"
390
+ LocalizedTagName="Attach as" Tooltip="" DetailedInfo="" Validation="Standard"
391
+ Default="Job">Job</AttachAs>
392
+
393
+ <!-- Custom property: bool, depends on AttachAs = "Dataset" -->
394
+ <IncludeMetadata UserDefined="true" Type="bool" Editor="inline" Subtype="inline"
395
+ Dependency="AttachAs" DependencyCondition="Equals" Dependencyvalue="Dataset"
396
+ LocalizedTagName="Include metadata" Tooltip="" DetailedInfo=""
397
+ Validation="Standard" Default="No">No</IncludeMetadata>
398
+
399
+ <!-- Custom property: literal "Default" editor combined with a modal editor -->
400
+ <FallbackFolder UserDefined="true" Type="string" Editor="default;choosefolder" Subtype=""
401
+ LocalizedTagName="Fallback folder" Tooltip="" DetailedInfo=""
402
+ Validation="Standard" Default="Default">Default</FallbackFolder>
403
+ </ElementFields>
404
+ <ConnectionFields/>
405
+ <ExtraProperties/>
406
+ </Object>
407
+ ```
@@ -0,0 +1,157 @@
1
+ ---
2
+ id: script-structure
3
+ category: switch-project
4
+ order: 10
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
+ triggers:
7
+ - "Setting up a new script project or converting a Script to an App"
8
+ ---
9
+
10
+ # Script Project Structure
11
+
12
+ A Switch script project lives in a **script folder** during development and is distributed as a `.sscript` package (ZIP).
13
+
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
+
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
+
30
+ ## Script folder files
31
+
32
+ | File | Required | Notes |
33
+ |---|---|---|
34
+ | `manifest.xml` | Yes | Switch internal use only. Do not edit manually. |
35
+ | `<ScriptID>.xml` | Yes | Script declaration (properties, connections, execution config). See [script-declaration.md](script-declaration.md). |
36
+ | `main.ts` | Yes (TypeScript) | Source file. Edit this. |
37
+ | `main.js` | Yes | Compiled output executed by Switch. Generated by transpiling `main.ts`. |
38
+ | `main.js.map` | No | Source map, generated alongside `main.js`. |
39
+ | `package.json` | Yes (TypeScript) | NPM config for type declarations. |
40
+ | `tsconfig.json` | Yes (TypeScript) | TypeScript transpilation options. |
41
+ | `<IconFileName>.png` | No | 32×32 px, RGB, not interlaced. Extension must be `.png`. |
42
+ | `node_modules/` | No | Local npm packages. |
43
+ | `Resources/` | No | Extra resource files bundled when packing with SwitchScriptTool. Never bundle an Oracle Java Runtime Environment here — licensing terms don't permit redistributing it, in a script or an app. |
44
+ | `<LanguageCode>.ts` | No | Translation files (Switch App SDK). |
45
+ | `.vscode/launch.json` | No | Debug config (created by SwitchScriptTool). |
46
+ | `.vscode/switch.code-snippets` | No | Entry point snippets (TypeScript only). |
47
+
48
+ > After editing `main.ts`, transpile with SwitchScriptTool to regenerate `main.js` before testing in Switch.
49
+
50
+ ## Several script folders side by side
51
+
52
+ One script folder produces one flow element. A design that needs several flow elements, such as
53
+ the apps in an app bundle, needs one script folder per element. Keep them in one parent folder, a
54
+ **script collection**:
55
+
56
+ ```
57
+ my-collection/ the folder the user works in; AI agent config and docs live here
58
+ ├── FirstElement/ script folder (manifest.xml, FirstElement.xml, main.ts, ...)
59
+ └── SecondElement/ script folder
60
+ ```
61
+
62
+ - Put each script folder directly under the collection, one level down. Don't nest a script folder
63
+ inside another script folder, and don't add grouping folders in between.
64
+ - Create each one with `SwitchScriptTool --create <ScriptID> <CollectionFolder>`. First check that
65
+ `<CollectionFolder>/<ScriptID>/` doesn't exist: `--create` deletes an existing target folder and
66
+ everything in it without asking.
67
+ - Never use `--create` to turn the collection folder itself into a script folder
68
+ (`--create <CollectionFolderName> <ParentFolder>`). That deletes the whole collection, including
69
+ `.git/` and AI agent config. If the user wants a script folder at the top level of the folder
70
+ they work in, or wants an existing top-level script folder moved down into a subfolder, stop and
71
+ leave that step to the user.
72
+ - Each script folder keeps its own `package.json`, `node_modules/` and `tsconfig.json`. Run npm,
73
+ transpile and pack per script folder.
74
+
75
+ ## manifest.xml format
76
+
77
+ ```xml
78
+ <Manifest>
79
+ <ScripterInstanceName>myScript</ScripterInstanceName>
80
+ <SwitchVersion>24.0</SwitchVersion>
81
+ <ScriptPackageFormatVersion>1.0</ScriptPackageFormatVersion>
82
+ <ScriptPasswordProtected>No</ScriptPasswordProtected>
83
+ <ScriptPackageType>Script</ScriptPackageType>
84
+ <ScriptDeclarationFile>myScript.xml</ScriptDeclarationFile>
85
+ <ScriptProgramFiles>
86
+ <ScriptProgramFile ScriptLanguage="NodeJSScript">main.ts</ScriptProgramFile>
87
+ </ScriptProgramFiles>
88
+ <ScriptIconFile>myScript_32.png</ScriptIconFile>
89
+ <LocalizationItems/>
90
+ </Manifest>
91
+ ```
92
+
93
+ `ScriptPackageType` is either `Script` (regular) or `App` (scripted plug-in).
94
+
95
+ ## Node.js version per Switch version
96
+
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).
99
+
100
+ ## Script vs App
101
+
102
+ | | Script | App |
103
+ |---|---|---|
104
+ | `ScriptPackageType` | `Script` | `App` |
105
+ | Appears in Elements pane | No (used via Script element) | Yes, as its own element |
106
+ | Distribution | Freely | Enfocus Appstore only |
107
+ | Position in pane | — | Configured via `PositionInElementPane` in XML |
108
+
109
+ ## Packing an app
110
+
111
+ An app is produced by SwitchScripter, not by SwitchScriptTool, and the two workflows have to be
112
+ joined up explicitly:
113
+
114
+ - **A script folder can only be typed `Script`.** To ship one as an app, pack it with
115
+ `SwitchScriptTool --pack` first, then open the resulting package in SwitchScripter and change the
116
+ type to `App` before creating the `.enfpack`. If "Create pack" is greyed out, the type is still
117
+ `Script`.
118
+ - **Extra files can't be managed while SwitchScripter is working with a script folder.** Convert to
119
+ a script package first. The conversion is reversible, so the `.pdesc` holding the extra-files
120
+ configuration can be round-tripped back into the script folder and kept in version control.
121
+ - **Creating a pack does not save the script.** Save first, or the pack is built from the last
122
+ saved state.
123
+ - **The pack ID is derived from the script ID and always starts with `com.enfocus.`**, whatever the
124
+ company name and domain in SwitchScripter preferences. To keep it stable across an update, open
125
+ the previous `.enfpack` rather than the `.sscript` — SwitchScripter carries the pack ID over from
126
+ the pack it opened, even if the script ID or domain has since changed.
127
+
128
+ ### SwitchScripter and Switch version compatibility
129
+
130
+ An app loads in the Switch version it was built with **or newer**, never older. So the SwitchScripter
131
+ used must be at or below the oldest Switch version you intend to support, and portable
132
+ SwitchScripters exist for older releases specifically to build backwards-compatible apps. There is
133
+ no SwitchScripter for Switch 25.11 — use the Switch 2024 Fall SwitchScripter for it.
134
+ Apps as a concept require Switch 13.1 or later.
135
+
136
+ The SwitchScripter version also fixes the app's Node.js version, see
137
+ [node-versions.md](node-versions.md). An app built with the
138
+ Switch 2024 Fall SwitchScripter has `SwitchVersion` `24.1`, so it runs on Node.js 20 in every Switch
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.
141
+
142
+ An unsigned app loads only in Switch on the machine that created it. That's what allows local
143
+ testing before submission; it loads elsewhere only once Enfocus has signed it.
144
+
145
+ ## App Store submission guidelines
146
+
147
+ App-specific submission rules (localization, universal/signed macOS binaries, immutable extra
148
+ files) live in [app-guidelines.md § App-only](../switch-appstore/app-guidelines.md#app-only), alongside the
149
+ rest of the pre-publish checklist.
150
+
151
+ ## Execution modes
152
+
153
+ `Concurrent`/`Serialized`, `ExecutionGroup` and `NumberOfSlots` are set in the XML declaration (see
154
+ [script-declaration.md](script-declaration.md#built-in-elementfields) for the attributes)
155
+ and documented in
156
+ [execution-environment.md § Execution modes](../switch-api/execution-environment.md#execution-modes),
157
+ which also explains why none of them maps to a count of Node.js processes.