@enfocussw/switch-scripting-context 25.11.1-beta.2 → 25.11.1-beta.4
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 +41 -0
- package/README.md +14 -9
- package/dist/init.d.ts +11 -0
- package/dist/init.js +90 -9
- package/docs/switch-api/logging.md +7 -0
- package/docs/switch-appstore/app-guidelines.md +8 -4
- package/docs/switch-project/property-editors.md +46 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,47 @@ All notable changes to this package are documented here. Format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [25.11.1-beta.4] - 2026-09-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Guidance in `docs/switch-project/property-editors.md` for a property whose shape varies by a
|
|
13
|
+
type selector. Declare an enum master and one dependent per shape instead of encoding JSON in a
|
|
14
|
+
single property, so each shape gets a native editor and most parsing disappears. Includes the
|
|
15
|
+
two constraints: the master must be static, and a hidden dependent throws when read.
|
|
16
|
+
- A rule in `docs/switch-api/logging.md` against adding a script-level verbosity property. Switch
|
|
17
|
+
already gates `Debug` behind a preference, so the property duplicates a control the user has and
|
|
18
|
+
costs a pane row and translated strings.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- `init` and `remove` now write to a docs folder named `docs-for-agents` instead of
|
|
23
|
+
`switch-docs`. Re-running `init` after an upgrade deletes the old folder, drops its gitignore
|
|
24
|
+
line, and repoints the generated AI config files, so no stale copy of the docs is left for an
|
|
25
|
+
agent to load. Pass `--docs-dir switch-docs` to keep the old name.
|
|
26
|
+
- The `DetailedInfo` item in `docs/switch-appstore/app-guidelines.md` no longer reads as requiring
|
|
27
|
+
it on every property and connection. It is optional, matching what the property documentation
|
|
28
|
+
and app manual guidance already said, and points at the app manual as the surface users read.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- The secret property row in `docs/switch-project/property-editors.md` listed `password` as an
|
|
33
|
+
editor chain. It is a `Type`, and there is no such editor token, so following the row produced an
|
|
34
|
+
invalid declaration.
|
|
35
|
+
- The array property row in `docs/switch-project/property-editors.md` claimed the chain always
|
|
36
|
+
returns a `string[]`, which the modal editor table contradicts. Both files now say the behaviour
|
|
37
|
+
is unverified and that code should handle a bare string too.
|
|
38
|
+
|
|
39
|
+
## [25.11.1-beta.3] - 2026-09-17
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- `init` now records the version that produced the docs on the first line of
|
|
44
|
+
`switch-scripting.md`, as an HTML comment reading `switch-scripting-context <version>`. A tool
|
|
45
|
+
that installs the docs can read it to tell which version a project has, including a checkout
|
|
46
|
+
someone else set up. Re-running `init` rewrites the line, and `--dry-run` prints it without
|
|
47
|
+
writing.
|
|
48
|
+
|
|
8
49
|
## [25.11.1-beta.2] - 2026-09-17
|
|
9
50
|
|
|
10
51
|
### Added
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ AI coding assistant context for [Enfocus Switch](https://www.enfocus.com/en/swit
|
|
|
4
4
|
|
|
5
5
|
Installs curated API reference docs and generates config files for 8 AI coding agents (Claude Code, GitHub Copilot, Cursor, Codex CLI/OpenCode, Gemini CLI, Windsurf, Zed, and Cline) so AI assistants understand the Switch scripting API out of the box.
|
|
6
6
|
|
|
7
|
-
See [CHANGELOG.md](https://cdn.jsdelivr.net/npm/@enfocussw/switch-scripting-context@25.11.1-beta.
|
|
7
|
+
See [CHANGELOG.md](https://cdn.jsdelivr.net/npm/@enfocussw/switch-scripting-context@25.11.1-beta.4/CHANGELOG.md) (also included in this package) for what's changed between versions.
|
|
8
8
|
|
|
9
9
|
## Usage
|
|
10
10
|
|
|
@@ -45,20 +45,25 @@ so the beta line precedes GA and leaves `25.11.0` free for the first published r
|
|
|
45
45
|
|
|
46
46
|
## What it does
|
|
47
47
|
|
|
48
|
-
1. Copies the Switch API reference docs to `
|
|
48
|
+
1. Copies the Switch API reference docs to `docs-for-agents/` in your project
|
|
49
49
|
2. Generates AI config files for each tool (see below)
|
|
50
|
-
3. Appends `
|
|
50
|
+
3. Appends `docs-for-agents/` to `.gitignore`
|
|
51
|
+
|
|
52
|
+
Versions before this one used `switch-docs/` as the folder name. Re-running `init` deletes that
|
|
53
|
+
folder and its `.gitignore` line, so your agents do not find a second, stale copy of the docs. It
|
|
54
|
+
only deletes a `switch-docs/` folder that `init` itself wrote. Pass `--docs-dir switch-docs` to
|
|
55
|
+
stay on the old name.
|
|
51
56
|
|
|
52
57
|
### Generated files
|
|
53
58
|
|
|
54
59
|
| Tool | File | How context is loaded |
|
|
55
60
|
|---|---|---|
|
|
56
|
-
| Claude Code / ClawCode | `CLAUDE.md` | `@
|
|
61
|
+
| Claude Code / ClawCode | `CLAUDE.md` | `@docs-for-agents/switch-scripting.md` import; hub file routes to specific API docs on demand |
|
|
57
62
|
| GitHub Copilot | `.github/copilot-instructions.md` | `#file:` reference to hub |
|
|
58
63
|
| GitHub Copilot (scoped) | `.github/instructions/switch-scripting.instructions.md` | `applyTo: "**/*.ts"`; auto-attaches to every TypeScript file edit |
|
|
59
64
|
| Cursor | `.cursor/rules/switch-scripting.mdc` | Inline key rules + full API file path list |
|
|
60
65
|
| Codex CLI / OpenCode | `AGENTS.md` | Inline key rules + full API file path list |
|
|
61
|
-
| Gemini CLI | `GEMINI.md` | `@
|
|
66
|
+
| Gemini CLI | `GEMINI.md` | `@docs-for-agents/switch-scripting.md` import |
|
|
62
67
|
| Windsurf | `.windsurfrules` | Inline key rules + full API file path list |
|
|
63
68
|
| Zed | `.rules` | Inline key rules + full API file path list |
|
|
64
69
|
| Cline | `.clinerules` | Inline key rules + full API file path list |
|
|
@@ -84,7 +89,7 @@ A few things this gets you without asking for them by name:
|
|
|
84
89
|
properties (app path/licence) are off-limits and left to SwitchScripter's GUI instead.
|
|
85
90
|
|
|
86
91
|
Re-run `init` after upgrading this package so the copied docs and generated config files catch up.
|
|
87
|
-
See [CHANGELOG.md](https://cdn.jsdelivr.net/npm/@enfocussw/switch-scripting-context@25.11.1-beta.
|
|
92
|
+
See [CHANGELOG.md](https://cdn.jsdelivr.net/npm/@enfocussw/switch-scripting-context@25.11.1-beta.4/CHANGELOG.md) for what changed.
|
|
88
93
|
|
|
89
94
|
## Options
|
|
90
95
|
|
|
@@ -94,7 +99,7 @@ npx switch-scripting-context init [options]
|
|
|
94
99
|
--tools <list> Tools to configure. Default: all
|
|
95
100
|
IDs: claude, copilot, cursor, codex, gemini, windsurf, zed, cline
|
|
96
101
|
Aliases: opencode (→ codex), clawcode (→ claude)
|
|
97
|
-
--docs-dir <dir> Destination folder for docs. Default:
|
|
102
|
+
--docs-dir <dir> Destination folder for docs. Default: docs-for-agents
|
|
98
103
|
--force Overwrite existing AI config files instead of merging
|
|
99
104
|
--dry-run Print what would happen without writing any files
|
|
100
105
|
```
|
|
@@ -124,7 +129,7 @@ To undo `init`, run `remove`. By default it deletes the docs folder, the Switch
|
|
|
124
129
|
npx switch-scripting-context remove [options]
|
|
125
130
|
|
|
126
131
|
--tools <list> Only remove these tools' config. Default: remove everything
|
|
127
|
-
--docs-dir <dir> Docs folder to remove. Default:
|
|
132
|
+
--docs-dir <dir> Docs folder to remove. Default: docs-for-agents
|
|
128
133
|
--dry-run Print what would happen without writing any files
|
|
129
134
|
```
|
|
130
135
|
|
|
@@ -143,7 +148,7 @@ npm install --save-dev "https://github.com/enfocus-switch/types-switch-scripting
|
|
|
143
148
|
|
|
144
149
|
## Included docs
|
|
145
150
|
|
|
146
|
-
The `
|
|
151
|
+
The `docs-for-agents/` folder contains:
|
|
147
152
|
|
|
148
153
|
- `switch-scripting.md`: master index with execution environment rules and "load when" routing table
|
|
149
154
|
<!-- docs-index:readme begin -->
|
package/dist/init.d.ts
CHANGED
|
@@ -31,4 +31,15 @@ export declare function generateZedRules(docsDir: string): string;
|
|
|
31
31
|
export declare function generateClineRules(docsDir: string): string;
|
|
32
32
|
export declare function buildIdMap(registry: ToolDefinition[]): Map<string, string>;
|
|
33
33
|
export declare function findMarkerRange(content: string): [number, number] | null;
|
|
34
|
+
/** The running package's own version, so a git-clone install stamps what actually ran. */
|
|
35
|
+
export declare function packageVersion(root?: string): string;
|
|
36
|
+
/** The comment init writes as the first line of the copied hub file. */
|
|
37
|
+
export declare function versionStamp(version: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Drop the `docsDir/` line init added, together with the comment line directly above it.
|
|
40
|
+
* The comment and the path are matched as one unit so that a project carrying entries for
|
|
41
|
+
* two docs folders (one left by an older version, one current) keeps the comment belonging
|
|
42
|
+
* to the entry that stays.
|
|
43
|
+
*/
|
|
44
|
+
export declare function stripGitignoreEntry(content: string, docsDir: string): string;
|
|
34
45
|
export declare function run(argv?: string[]): Promise<void>;
|
package/dist/init.js
CHANGED
|
@@ -42,6 +42,9 @@ exports.generateZedRules = generateZedRules;
|
|
|
42
42
|
exports.generateClineRules = generateClineRules;
|
|
43
43
|
exports.buildIdMap = buildIdMap;
|
|
44
44
|
exports.findMarkerRange = findMarkerRange;
|
|
45
|
+
exports.packageVersion = packageVersion;
|
|
46
|
+
exports.versionStamp = versionStamp;
|
|
47
|
+
exports.stripGitignoreEntry = stripGitignoreEntry;
|
|
45
48
|
exports.run = run;
|
|
46
49
|
const fs = __importStar(require("fs"));
|
|
47
50
|
const path = __importStar(require("path"));
|
|
@@ -90,6 +93,13 @@ const INLINE_CORE_RULES = `## Core rules
|
|
|
90
93
|
- After editing \`main.ts\`, transpile with \`SwitchScriptTool --transpile <folder>\` — do not use \`tsc\` directly.
|
|
91
94
|
- Never use a script folder in production — pack with \`SwitchScriptTool --pack\` first; folders are excluded from flow exports.`;
|
|
92
95
|
// ─── Content generators ──────────────────────────────────────────────────────
|
|
96
|
+
// Claude Code and Gemini CLI resolve @file imports themselves, so their generated
|
|
97
|
+
// block is just a pointer to switch-scripting.md; the routing table and key rules
|
|
98
|
+
// living there are the actual instructions. Codex, Windsurf, Zed, and Cline don't
|
|
99
|
+
// reliably follow that import syntax, so their generators (generateInlineBlock)
|
|
100
|
+
// paste INLINE_CORE_RULES and the routing table directly into the generated file
|
|
101
|
+
// instead. Don't collapse these into one shared generator; the split is deliberate,
|
|
102
|
+
// not duplication.
|
|
93
103
|
function generateClaudeMd(docsDir) {
|
|
94
104
|
return `${MARKER_BEGIN}
|
|
95
105
|
## Switch Scripting (Enfocus Switch)
|
|
@@ -365,6 +375,61 @@ function copyDocs(packageRoot, destDir, dryRun) {
|
|
|
365
375
|
});
|
|
366
376
|
console.log(` docs → ${path.relative(process.cwd(), destDir)}/`);
|
|
367
377
|
}
|
|
378
|
+
// ─── Version stamp ───────────────────────────────────────────────────────────
|
|
379
|
+
/** The hub file in the copied docs folder that carries the version stamp. */
|
|
380
|
+
const HUB_FILE = 'switch-scripting.md';
|
|
381
|
+
/** The running package's own version, so a git-clone install stamps what actually ran. */
|
|
382
|
+
function packageVersion(root = packageRoot()) {
|
|
383
|
+
return JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version;
|
|
384
|
+
}
|
|
385
|
+
/** The comment init writes as the first line of the copied hub file. */
|
|
386
|
+
function versionStamp(version) {
|
|
387
|
+
return `<!-- switch-scripting-context ${version} -->`;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Records which version produced the copied docs, so a consumer can tell what is installed
|
|
391
|
+
* without having recorded it at install time. Only the copy is stamped: the repo's own
|
|
392
|
+
* docs/switch-scripting.md is generated output and a stamp there would fight the generator.
|
|
393
|
+
* copyDocs wipes destDir on every run, so there is never a stale stamp to remove.
|
|
394
|
+
*/
|
|
395
|
+
function stampDocsVersion(packageRoot, destDir, dryRun) {
|
|
396
|
+
const stamp = versionStamp(packageVersion(packageRoot));
|
|
397
|
+
const hubPath = path.join(destDir, HUB_FILE);
|
|
398
|
+
const rel = path.relative(process.cwd(), hubPath);
|
|
399
|
+
if (dryRun) {
|
|
400
|
+
console.log(` [dry-run] ${rel} → first line ${stamp}`);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
fs.writeFileSync(hubPath, `${stamp}\n${fs.readFileSync(hubPath, 'utf8')}`, 'utf8');
|
|
404
|
+
console.log(` ${rel} → stamped ${stamp}`);
|
|
405
|
+
}
|
|
406
|
+
// ─── Legacy docs folder ──────────────────────────────────────────────────────
|
|
407
|
+
/** Docs folder name written by versions before the rename to docs-for-agents. */
|
|
408
|
+
const LEGACY_DOCS_DIR = 'switch-docs';
|
|
409
|
+
/**
|
|
410
|
+
* Delete the docs folder an older version wrote under its old name.
|
|
411
|
+
*
|
|
412
|
+
* init regenerates every AI config block in place, so after an upgrade the configs point at
|
|
413
|
+
* the new folder and nothing reads the old one. It is gitignored, so nothing surfaces it
|
|
414
|
+
* either, but an agent grepping the project still finds and loads the stale copy. The folder
|
|
415
|
+
* is only removed when it holds the hub file init writes, so a folder of the same name that
|
|
416
|
+
* this tool did not create is left alone.
|
|
417
|
+
*/
|
|
418
|
+
function removeLegacyDocsDir(targetRoot, docsDir, dryRun) {
|
|
419
|
+
if (docsDir === LEGACY_DOCS_DIR)
|
|
420
|
+
return;
|
|
421
|
+
const legacyDir = path.join(targetRoot, LEGACY_DOCS_DIR);
|
|
422
|
+
if (!fs.existsSync(path.join(legacyDir, HUB_FILE)))
|
|
423
|
+
return;
|
|
424
|
+
if (dryRun) {
|
|
425
|
+
console.log(` [dry-run] ${LEGACY_DOCS_DIR}/ → deleted (docs folder from an earlier version)`);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
fs.rmSync(legacyDir, { recursive: true, force: true });
|
|
429
|
+
console.log(` ${LEGACY_DOCS_DIR}/ → deleted (docs folder from an earlier version)`);
|
|
430
|
+
}
|
|
431
|
+
removeGitignoreEntry(targetRoot, LEGACY_DOCS_DIR, dryRun);
|
|
432
|
+
}
|
|
368
433
|
// ─── .gitignore update ───────────────────────────────────────────────────────
|
|
369
434
|
function updateGitignore(targetRoot, docsDir, dryRun) {
|
|
370
435
|
const gitignorePath = path.join(targetRoot, '.gitignore');
|
|
@@ -378,7 +443,10 @@ function updateGitignore(targetRoot, docsDir, dryRun) {
|
|
|
378
443
|
console.log(` [dry-run] .gitignore → append ${docsDir}/ entry`);
|
|
379
444
|
return;
|
|
380
445
|
}
|
|
381
|
-
|
|
446
|
+
// entry opens with its own blank-line separator, so normalise what the file already
|
|
447
|
+
// ends with. Without this, appending after an entry that was just removed leaves a
|
|
448
|
+
// widening gap in the file on every upgrade.
|
|
449
|
+
fs.writeFileSync(gitignorePath, content.replace(/\n*$/, '\n') + entry, 'utf8');
|
|
382
450
|
console.log(` .gitignore → appended ${docsDir}/ entry`);
|
|
383
451
|
}
|
|
384
452
|
/** AI config files that init may have created, for all tools or only the given tool ids. */
|
|
@@ -442,15 +510,25 @@ function removeAiConfig(filePath, file, docsDir, targetRoot, dryRun) {
|
|
|
442
510
|
console.log(` ${rel} → removed Switch section`);
|
|
443
511
|
}
|
|
444
512
|
}
|
|
513
|
+
function escapeRegExp(s) {
|
|
514
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Drop the `docsDir/` line init added, together with the comment line directly above it.
|
|
518
|
+
* The comment and the path are matched as one unit so that a project carrying entries for
|
|
519
|
+
* two docs folders (one left by an older version, one current) keeps the comment belonging
|
|
520
|
+
* to the entry that stays.
|
|
521
|
+
*/
|
|
522
|
+
function stripGitignoreEntry(content, docsDir) {
|
|
523
|
+
const pattern = new RegExp(`(\n# switch-scripting-context \\(AI context docs[^\n]*\\))?\n${escapeRegExp(docsDir)}/\n`, 'g');
|
|
524
|
+
return content.replace(pattern, '\n');
|
|
525
|
+
}
|
|
445
526
|
function removeGitignoreEntry(targetRoot, docsDir, dryRun) {
|
|
446
527
|
const gitignorePath = path.join(targetRoot, '.gitignore');
|
|
447
528
|
if (!fs.existsSync(gitignorePath))
|
|
448
529
|
return;
|
|
449
530
|
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
450
|
-
|
|
451
|
-
const updated = content
|
|
452
|
-
.replace(/\n# switch-scripting-context \(AI context docs[^\n]*\)\n/g, '\n')
|
|
453
|
-
.replace(new RegExp(`\n${docsDir}/\n`, 'g'), '\n');
|
|
531
|
+
const updated = stripGitignoreEntry(content, docsDir);
|
|
454
532
|
if (updated === content)
|
|
455
533
|
return;
|
|
456
534
|
if (dryRun) {
|
|
@@ -491,6 +569,7 @@ function remove(options) {
|
|
|
491
569
|
else {
|
|
492
570
|
console.log(` ${options.docsDir}/ → not found, skipping`);
|
|
493
571
|
}
|
|
572
|
+
removeLegacyDocsDir(targetRoot, options.docsDir, options.dryRun);
|
|
494
573
|
// 2. Remove AI config sections / files
|
|
495
574
|
console.log('\nCleaning AI config files...');
|
|
496
575
|
for (const { filePath, file } of aiConfigFiles(targetRoot, options.docsDir)) {
|
|
@@ -510,7 +589,9 @@ function init(options) {
|
|
|
510
589
|
console.log('\nDry run — no files will be written.\n');
|
|
511
590
|
// 1. Copy docs
|
|
512
591
|
console.log('Copying docs...');
|
|
592
|
+
removeLegacyDocsDir(targetRoot, options.docsDir, options.dryRun);
|
|
513
593
|
copyDocs(packageRoot, docsDestDir, options.dryRun);
|
|
594
|
+
stampDocsVersion(packageRoot, docsDestDir, options.dryRun);
|
|
514
595
|
// 2. Generate AI config files
|
|
515
596
|
const toolActions = [];
|
|
516
597
|
for (const toolDef of TOOL_REGISTRY) {
|
|
@@ -542,12 +623,12 @@ function parseArgs(argv) {
|
|
|
542
623
|
let toolsExplicit = false;
|
|
543
624
|
const initOptions = {
|
|
544
625
|
tools: TOOL_REGISTRY.map(t => t.id),
|
|
545
|
-
docsDir: '
|
|
626
|
+
docsDir: 'docs-for-agents',
|
|
546
627
|
force: false,
|
|
547
628
|
dryRun: false,
|
|
548
629
|
};
|
|
549
630
|
const removeOptions = {
|
|
550
|
-
docsDir: '
|
|
631
|
+
docsDir: 'docs-for-agents',
|
|
551
632
|
dryRun: false,
|
|
552
633
|
};
|
|
553
634
|
for (let i = 1; i < args.length; i++) {
|
|
@@ -627,7 +708,7 @@ Options (init):
|
|
|
627
708
|
IDs: claude, copilot, cursor, codex, gemini, windsurf, zed, cline
|
|
628
709
|
Aliases: opencode (→ codex), clawcode (→ claude)
|
|
629
710
|
Default: all tools
|
|
630
|
-
--docs-dir <dir> Destination folder for docs in target project (default:
|
|
711
|
+
--docs-dir <dir> Destination folder for docs in target project (default: docs-for-agents)
|
|
631
712
|
--force Overwrite existing AI config files rather than merging
|
|
632
713
|
--dry-run Print what would happen without writing any files
|
|
633
714
|
--help Show this help message
|
|
@@ -635,7 +716,7 @@ Options (init):
|
|
|
635
716
|
Options (remove):
|
|
636
717
|
--tools <list> Only remove these tools' config; keeps the docs folder and other tools
|
|
637
718
|
Default: remove everything, same as listing every tool
|
|
638
|
-
--docs-dir <dir> Docs folder to remove (default:
|
|
719
|
+
--docs-dir <dir> Docs folder to remove (default: docs-for-agents)
|
|
639
720
|
--dry-run Print what would happen without writing any files
|
|
640
721
|
|
|
641
722
|
Examples:
|
|
@@ -24,6 +24,13 @@ and how often to log is a deliberate choice, not a default.
|
|
|
24
24
|
| `Info` | Normal, expected checkpoints worth surfacing to a flow operator without them needing debug mode (e.g. "processed 40 records", "output written to X"). |
|
|
25
25
|
| `Debug` | Verbose detail for diagnosing execution. Many users leave debug logging enabled in production to self-diagnose issues and to help app developers and Enfocus Support — leave meaningful checkpoints in, but trim noisy or no-longer-useful debug logs before shipping. **`Debug` messages only reach the log database if the Switch preference "Log debug messages" is turned on** (off by default) — see [logs-and-dataroot.md](../switch-project/logs-and-dataroot.md#retention-and-the-debug-level-gate). |
|
|
26
26
|
|
|
27
|
+
## Don't add a verbosity property
|
|
28
|
+
|
|
29
|
+
Switch already has the switch: `Debug` messages only reach the log database when the preference
|
|
30
|
+
"Log debug messages" is on. A script-level "log detail" or "verbose logging" property duplicates a
|
|
31
|
+
control the user already has, adds a row to the property pane, and (for an app) adds strings to
|
|
32
|
+
translate. Put verbose detail behind `LogLevel.Debug` and let the preference govern it.
|
|
33
|
+
|
|
27
34
|
## Logging in loops
|
|
28
35
|
|
|
29
36
|
Don't categorically avoid logging inside loops — per-item logs can matter (e.g. a per-job error).
|
|
@@ -162,11 +162,15 @@ Unlike the sections above, these are documented in full here rather than deferri
|
|
|
162
162
|
approval. Note that an unsigned app always loads under the **Custom** subcategory regardless of
|
|
163
163
|
what it declares — the declared subcategory only takes effect once Enfocus has signed the app, and
|
|
164
164
|
review may change it.
|
|
165
|
-
- [ ] **`DetailedInfo` is
|
|
166
|
-
Switch Designer can export a *flow's* HTML
|
|
167
|
-
|
|
165
|
+
- [ ] **`DetailedInfo` is written wherever it adds something a `Tooltip` can't carry** — not
|
|
166
|
+
mechanically on every property. It is optional: Switch Designer can export a *flow's* HTML
|
|
167
|
+
documentation from every element in it, so an empty `DetailedInfo` produces a blank entry there
|
|
168
|
+
if a flow builder ever generates one, which is a reason to fill it in where the property warrants
|
|
169
|
+
it, not a reason to pad out every trivial one. See
|
|
168
170
|
[property-documentation.md](../switch-project/property-documentation.md) for what to write in it, and when
|
|
169
|
-
it's fine to leave blank.
|
|
171
|
+
it's fine to leave blank. The [app manual](app-manual.md#flow-elements-properties) is the surface
|
|
172
|
+
users are far more likely to read, and it needs real per-property content regardless of what
|
|
173
|
+
`DetailedInfo` holds.
|
|
170
174
|
- [ ] **`Description`, `Compatibility`, `SupportInfo` and `AppDiscovery` are filled in.** These are
|
|
171
175
|
required to create the `.enfpack` at all, and `SupportInfo` must point at *your* support channel,
|
|
172
176
|
never Enfocus Support — see [app-store-listing.md](app-store-listing.md) for what to write
|
|
@@ -158,9 +158,9 @@ offer it as the property's only editor — pair it with a module-free option (e.
|
|
|
158
158
|
|
|
159
159
|
| Property kind | Recommended `Editor` chain | Why |
|
|
160
160
|
|---|---|---|
|
|
161
|
-
| Secret (password, token, API key) | `
|
|
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. |
|
|
162
162
|
| 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
|
-
| Array/list job-specific value | `stringlist` + `mltextwithvar` + `scriptexp` | One item per line
|
|
163
|
+
| 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
164
|
| 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. |
|
|
165
165
|
| Boolean used as a `Dependency` master | `inline` only | Other properties' visibility depends on a value known at design time. |
|
|
166
166
|
| Boolean consumed directly by script logic (not a `Dependency` master) | `inline` + `conditionwithvar` + `scriptexp` | Safe to toggle dynamically since nothing else depends on it for visibility. Also covers booleans that are themselves a `Dependency` *dependent* (not a master). |
|
|
@@ -174,6 +174,50 @@ offer it as the property's only editor — pair it with a module-free option (e.
|
|
|
174
174
|
combined with `sltextwithvar`/`scriptexp` too, but this is uncommon in practice and not covered by a
|
|
175
175
|
dedicated row above.
|
|
176
176
|
|
|
177
|
+
### A property whose shape varies by a type selector
|
|
178
|
+
|
|
179
|
+
When what a property must hold depends on another property's value — a map for one type, an
|
|
180
|
+
ordered list for another, a pair of labelled strings for a third — don't reach for one property
|
|
181
|
+
holding JSON or a hand-rolled line format. Declare an enum master and one dependent per shape, each
|
|
182
|
+
with the editor that natively fits it, and read only the dependent the master currently shows.
|
|
183
|
+
|
|
184
|
+
The shapes then need no parsing at all in the common cases, and the property pane shows one
|
|
185
|
+
type-appropriate editor with a label that explains itself instead of a text box whose format lives
|
|
186
|
+
in the documentation:
|
|
187
|
+
|
|
188
|
+
```xml
|
|
189
|
+
<!-- master: enum, inline only, so the value is known at flow-configuration time -->
|
|
190
|
+
<QType UserDefined="true" Type="enum:(not used);List;Ordered;Pair" Editor="inline" Subtype="inline"
|
|
191
|
+
LocalizedTagName="Type" Tooltip="" DetailedInfo="" Validation="Standard"
|
|
192
|
+
Default="(not used)">(not used)</QType>
|
|
193
|
+
|
|
194
|
+
<!-- one dependent per shape; each is hidden unless its condition holds -->
|
|
195
|
+
<QList UserDefined="true" Type="stringlist" Editor="stringlist" Subtype="stringlist"
|
|
196
|
+
Dependency="QType" DependencyCondition="Equals" Dependencyvalue="List"
|
|
197
|
+
LocalizedTagName="Items" Tooltip="" DetailedInfo="" Validation="Standard"></QList>
|
|
198
|
+
<QLow UserDefined="true" Type="string" Editor="description" Subtype=""
|
|
199
|
+
Dependency="QType" DependencyCondition="Equals" Dependencyvalue="Pair"
|
|
200
|
+
LocalizedTagName="What a low value means" Tooltip="" DetailedInfo=""
|
|
201
|
+
Validation="Standard" Default=""></QLow>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Adding `(not used)` to the master's items makes it double as the group's on/off switch, so the
|
|
205
|
+
group needs no separate boolean.
|
|
206
|
+
|
|
207
|
+
Two constraints come with the pattern:
|
|
208
|
+
|
|
209
|
+
- The master must be an enum or a boolean with `inline` only, per the rows above. A dynamic master
|
|
210
|
+
makes the visible set differ per job, which the dependents' own editors cannot account for.
|
|
211
|
+
- Reading a hidden dependent throws `"Invalid tag: <tag>"`. Check the master's value, or call
|
|
212
|
+
`hasProperty(tag)`, before every read — see
|
|
213
|
+
[script-declaration.md § Dependency](script-declaration.md#custom-property-elements-elementfields).
|
|
214
|
+
This bites hardest in a loop over jobs or connections, where the throw repeats on every
|
|
215
|
+
invocation.
|
|
216
|
+
|
|
217
|
+
A shape the master cannot enumerate, or one that must vary per job, is the case this pattern does
|
|
218
|
+
not cover; that is where a single `description` property holding a structured blob is the honest
|
|
219
|
+
answer.
|
|
220
|
+
|
|
177
221
|
---
|
|
178
222
|
|
|
179
223
|
## Notes
|