@dawnlit/outlook-bridge 1.1.1 → 2.0.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.
- package/README.md +150 -183
- package/dist/backend.d.ts +151 -0
- package/dist/backend.js +3 -0
- package/dist/backend.js.map +1 -0
- package/dist/bridge.d.ts +56 -0
- package/dist/bridge.js +507 -0
- package/dist/bridge.js.map +1 -0
- package/dist/errors.d.ts +81 -43
- package/dist/errors.js +98 -52
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +25 -52
- package/dist/index.js.map +1 -1
- package/dist/mac/accounts.d.ts +7 -2
- package/dist/mac/accounts.js +23 -6
- package/dist/mac/accounts.js.map +1 -1
- package/dist/mac/attachments.d.ts +5 -25
- package/dist/mac/attachments.js +40 -99
- package/dist/mac/attachments.js.map +1 -1
- package/dist/mac/bounces.d.ts +12 -27
- package/dist/mac/bounces.js +95 -112
- package/dist/mac/bounces.js.map +1 -1
- package/dist/mac/cleanup.d.ts +11 -25
- package/dist/mac/cleanup.js +50 -76
- package/dist/mac/cleanup.js.map +1 -1
- package/dist/mac/drafts.d.ts +13 -34
- package/dist/mac/drafts.js +95 -169
- package/dist/mac/drafts.js.map +1 -1
- package/dist/mac/folders.d.ts +10 -21
- package/dist/mac/folders.js +33 -47
- package/dist/mac/folders.js.map +1 -1
- package/dist/mac/index.d.ts +2 -22
- package/dist/mac/index.js +19 -81
- package/dist/mac/index.js.map +1 -1
- package/dist/mac/profile.d.ts +13 -0
- package/dist/mac/profile.js +149 -0
- package/dist/mac/profile.js.map +1 -0
- package/dist/mac/read.d.ts +18 -38
- package/dist/mac/read.js +154 -210
- package/dist/mac/read.js.map +1 -1
- package/dist/mac/run.d.ts +35 -39
- package/dist/mac/run.js +97 -102
- package/dist/mac/run.js.map +1 -1
- package/dist/mac/scripts.d.ts +78 -86
- package/dist/mac/scripts.js +125 -115
- package/dist/mac/scripts.js.map +1 -1
- package/dist/mac/send.d.ts +10 -20
- package/dist/mac/send.js +64 -86
- package/dist/mac/send.js.map +1 -1
- package/dist/mac/signatures.d.ts +2 -2
- package/dist/mac/signatures.js +9 -11
- package/dist/mac/signatures.js.map +1 -1
- package/dist/mac/templates.d.ts +14 -26
- package/dist/mac/templates.js +75 -113
- package/dist/mac/templates.js.map +1 -1
- package/dist/mail.d.ts +77 -45
- package/dist/mail.js +166 -73
- package/dist/mail.js.map +1 -1
- package/dist/runtime.d.ts +76 -43
- package/dist/runtime.js +103 -48
- package/dist/runtime.js.map +1 -1
- package/dist/shared/args.d.ts +48 -0
- package/dist/shared/args.js +216 -0
- package/dist/shared/args.js.map +1 -0
- package/dist/shared/attachmentMatch.d.ts +28 -16
- package/dist/shared/attachmentMatch.js +60 -22
- package/dist/shared/attachmentMatch.js.map +1 -1
- package/dist/shared/bounceReport.d.ts +11 -0
- package/dist/shared/bounceReport.js +45 -0
- package/dist/shared/bounceReport.js.map +1 -0
- package/dist/shared/bounceRules.d.ts +38 -10
- package/dist/shared/bounceRules.js +50 -32
- package/dist/shared/bounceRules.js.map +1 -1
- package/dist/shared/exec.d.ts +21 -0
- package/dist/shared/exec.js +47 -0
- package/dist/shared/exec.js.map +1 -0
- package/dist/shared/json.d.ts +12 -6
- package/dist/shared/json.js +29 -23
- package/dist/shared/json.js.map +1 -1
- package/dist/shared/locate.d.ts +39 -0
- package/dist/shared/locate.js +129 -0
- package/dist/shared/locate.js.map +1 -0
- package/dist/shared/replyBody.d.ts +42 -32
- package/dist/shared/replyBody.js +84 -71
- package/dist/shared/replyBody.js.map +1 -1
- package/dist/templateBody.d.ts +63 -0
- package/dist/{outlookTemplateSections.js → templateBody.js} +72 -72
- package/dist/templateBody.js.map +1 -0
- package/dist/types.d.ts +360 -167
- package/dist/types.js +8 -9
- package/dist/types.js.map +1 -1
- package/dist/windows/accounts.d.ts +7 -12
- package/dist/windows/accounts.js +19 -34
- package/dist/windows/accounts.js.map +1 -1
- package/dist/windows/attachments.d.ts +9 -24
- package/dist/windows/attachments.js +94 -119
- package/dist/windows/attachments.js.map +1 -1
- package/dist/windows/bounces.d.ts +13 -26
- package/dist/windows/bounces.js +168 -200
- package/dist/windows/bounces.js.map +1 -1
- package/dist/windows/cleanup.d.ts +19 -23
- package/dist/windows/cleanup.js +92 -111
- package/dist/windows/cleanup.js.map +1 -1
- package/dist/windows/drafts.d.ts +16 -35
- package/dist/windows/drafts.js +167 -253
- package/dist/windows/drafts.js.map +1 -1
- package/dist/windows/folders.d.ts +8 -13
- package/dist/windows/folders.js +44 -52
- package/dist/windows/folders.js.map +1 -1
- package/dist/windows/index.d.ts +2 -27
- package/dist/windows/index.js +11 -66
- package/dist/windows/index.js.map +1 -1
- package/dist/windows/read.d.ts +24 -60
- package/dist/windows/read.js +261 -306
- package/dist/windows/read.js.map +1 -1
- package/dist/windows/run.d.ts +39 -39
- package/dist/windows/run.js +151 -111
- package/dist/windows/run.js.map +1 -1
- package/dist/windows/scripts.d.ts +65 -55
- package/dist/windows/scripts.js +109 -85
- package/dist/windows/scripts.js.map +1 -1
- package/dist/windows/send.d.ts +6 -11
- package/dist/windows/send.js +68 -88
- package/dist/windows/send.js.map +1 -1
- package/dist/windows/signatures.d.ts +4 -10
- package/dist/windows/signatures.js +30 -37
- package/dist/windows/signatures.js.map +1 -1
- package/dist/windows/templates.d.ts +12 -18
- package/dist/windows/templates.js +122 -149
- package/dist/windows/templates.js.map +1 -1
- package/package.json +4 -3
- package/dist/OutlookService.d.ts +0 -43
- package/dist/OutlookService.js +0 -137
- package/dist/OutlookService.js.map +0 -1
- package/dist/outlookTemplateSections.d.ts +0 -62
- package/dist/outlookTemplateSections.js.map +0 -1
- package/dist/shared/capabilities.d.ts +0 -3
- package/dist/shared/capabilities.js +0 -9
- package/dist/shared/capabilities.js.map +0 -1
package/dist/mac/run.d.ts
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
|
+
import { type RunBudget } from '../runtime';
|
|
1
2
|
/**
|
|
2
|
-
* Run
|
|
3
|
+
* Run a script from a temp file (no argument-length or quoting limits) and
|
|
4
|
+
* return what it printed, minus the newline osascript appends.
|
|
3
5
|
*
|
|
4
|
-
* `AS_HANDLERS` is prepended here rather than by each caller
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* `AS_HANDLERS` is prepended here rather than by each caller: a script missing
|
|
7
|
+
* a handler fails to compile as a WHOLE, with an error pointing nowhere near the
|
|
8
|
+
* omission, so it can't be left to each script to remember.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runOsaScript(source: string, budget: RunBudget): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Escape text for the inside of an AppleScript double-quoted literal.
|
|
13
|
+
* AppleScript strings never interpolate, so escaping the quote, the backslash
|
|
14
|
+
* and line breaks is the whole job.
|
|
12
15
|
*/
|
|
13
|
-
export declare function
|
|
14
|
-
/**
|
|
15
|
-
export declare function
|
|
16
|
+
export declare function asEscape(text: string): string;
|
|
17
|
+
/** Text as an AppleScript string literal. */
|
|
18
|
+
export declare function asString(text: string): string;
|
|
16
19
|
/** AppleScript's boolean literals. */
|
|
17
20
|
export declare function asBool(value: boolean): string;
|
|
21
|
+
/** A whole number for a script; anything else is refused before it becomes code. */
|
|
22
|
+
export declare function asInt(value: number): string;
|
|
23
|
+
/** Message ids (already validated as integers) as an AppleScript list literal. */
|
|
24
|
+
export declare function asIdList(ids: readonly string[]): string;
|
|
18
25
|
export declare const FIELD_SEP = "\u001F";
|
|
19
26
|
export declare const RECORD_SEP = "\u001E";
|
|
20
27
|
export declare const LIST_SEP = "\u001D";
|
|
@@ -23,42 +30,31 @@ export declare const AS_FIELD_SEP = "(character id 31)";
|
|
|
23
30
|
export declare const AS_RECORD_SEP = "(character id 30)";
|
|
24
31
|
export declare const AS_LIST_SEP = "(character id 29)";
|
|
25
32
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* rather than merely unlikely to break. A field holding a LIST must be built
|
|
31
|
-
* with `my sanitizeList(...)`, which cleans the elements instead — `sanitize`
|
|
32
|
-
* deliberately leaves the list separator alone.
|
|
33
|
+
* One record: the AppleScript expressions in `fields`, separated and terminated
|
|
34
|
+
* so `splitRecords`/`splitFields` can take them apart again. A field holding a
|
|
35
|
+
* LIST must be built with `my sanitizeList(...)`, which cleans the elements —
|
|
36
|
+
* `sanitize` deliberately leaves the list separator alone.
|
|
33
37
|
*/
|
|
34
38
|
export declare function asRow(fields: readonly string[]): string;
|
|
35
39
|
/**
|
|
36
|
-
* The
|
|
37
|
-
* inside a `tell` block.
|
|
40
|
+
* The handlers every script relies on, called with `my` from inside a `tell`.
|
|
38
41
|
*
|
|
39
|
-
* `isoDate` produces the zero-padded 'yyyy-MM-dd HH:mm' both platforms
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
+
* `isoDate` produces the zero-padded 'yyyy-MM-dd HH:mm' both platforms return;
|
|
43
|
+
* `sanitize` strips the framing characters; `joinList` and `replaceText` are the
|
|
44
|
+
* text-item-delimiter dance nobody should write twice.
|
|
42
45
|
*/
|
|
43
|
-
export declare const AS_HANDLERS = "\non pad2(n)\n set s to (n as integer) as string\n if (length of s) < 2 then set s to \"0\" & s\n return s\nend pad2\n\non isoDate(d)\n if d is missing value then return \"\"\n return (year of d as string) & \"-\" & pad2(month of d as integer) & \"-\" & pad2(day of d) & \" \" & pad2(hours of d) & \":\" & pad2(minutes of d)\nend isoDate\n\non joinList(lst, sep)\n set saved to AppleScript's text item delimiters\n set AppleScript's text item delimiters to sep\n set out to lst as string\n set AppleScript's text item delimiters to saved\n return out\nend joinList\n\non replaceText(t, findWhat, replaceWith)\n set saved to AppleScript's text item delimiters\n set AppleScript's text item delimiters to findWhat\n set parts to text items of t\n set AppleScript's text item delimiters to replaceWith\n set out to parts as string\n set AppleScript's text item delimiters to saved\n return out\nend replaceText\n\n-- Strips the FIELD and RECORD separators only. The LIST separator is left\n-- alone on purpose: a field may legitimately BE a list
|
|
44
|
-
/**
|
|
46
|
+
export declare const AS_HANDLERS = "\non pad2(n)\n set s to (n as integer) as string\n if (length of s) < 2 then set s to \"0\" & s\n return s\nend pad2\n\non isoDate(d)\n if d is missing value then return \"\"\n return (year of d as string) & \"-\" & pad2(month of d as integer) & \"-\" & pad2(day of d) & \" \" & pad2(hours of d) & \":\" & pad2(minutes of d)\nend isoDate\n\non joinList(lst, sep)\n set saved to AppleScript's text item delimiters\n set AppleScript's text item delimiters to sep\n set out to lst as string\n set AppleScript's text item delimiters to saved\n return out\nend joinList\n\non replaceText(t, findWhat, replaceWith)\n set saved to AppleScript's text item delimiters\n set AppleScript's text item delimiters to findWhat\n set parts to text items of t\n set AppleScript's text item delimiters to replaceWith\n set out to parts as string\n set AppleScript's text item delimiters to saved\n return out\nend replaceText\n\n-- Strips the FIELD and RECORD separators only. The LIST separator is left\n-- alone on purpose: a field may legitimately BE a list.\non sanitize(v)\n if v is missing value then return \"\"\n set t to v as string\n repeat with codePoint in {30, 31}\n set marker to (character id (codePoint as integer))\n if t contains marker then set t to my replaceText(t, marker, \" \")\n end repeat\n return t\nend sanitize\n\n-- A list field: each element loses all three separators, then they are joined\n-- with the LIST separator that splitList() takes them apart on.\non sanitizeList(lst)\n set cleaned to {}\n repeat with item_ in lst\n set t to my sanitize(item_)\n set marker to (character id 29)\n if t contains marker then set t to my replaceText(t, marker, \" \")\n set end of cleaned to t\n end repeat\n return my joinList(cleaned, (character id 29))\nend sanitizeList\n\non insertAboveQuoted(c, ins)\n if c is missing value then return ins\n set c to c as string\n if (length of c) is 0 then return ins\n -- A reply's own empty paragraph sits inside WordSection1; inserting straight\n -- after that tag puts the new text where the cursor would be. Anything else\n -- falls back to just inside <body>. ('at' is a reserved parameter name, so\n -- the cut point can't be called that.)\n set cutPoint to my tagEnd(c, \"WordSection1>\")\n if cutPoint is 0 then set cutPoint to my tagEnd(c, \"<body\")\n if cutPoint is 0 then return ins & c\n if cutPoint is (length of c) then return c & ins\n return (text 1 thru cutPoint of c) & ins & (text (cutPoint + 1) thru -1 of c)\nend insertAboveQuoted\n\n-- 'rest' is an AppleScript term, so the remainder can't be called that:\n-- assigning to it compiles and then fails at run time.\non tagEnd(c, marker)\n set ix to offset of marker in c\n if ix is 0 then return 0\n if marker ends with \">\" then return ix + (length of marker) - 1\n set tailText to text ix thru -1 of c\n set gt to offset of \">\" in tailText\n if gt is 0 then return 0\n return ix + gt - 1\nend tagEnd\n";
|
|
47
|
+
/** A script's output as records, without the trailing empty one. */
|
|
45
48
|
export declare function splitRecords(raw: string): string[];
|
|
46
|
-
/**
|
|
49
|
+
/** One record's fields. */
|
|
47
50
|
export declare function splitFields(record: string): string[];
|
|
48
|
-
/**
|
|
51
|
+
/** A field that carries a list. */
|
|
49
52
|
export declare function splitList(value: string | undefined): string[];
|
|
50
|
-
/** A field by position
|
|
53
|
+
/** A field by position; a missing field reads as ''. */
|
|
51
54
|
export declare function field(fields: readonly string[], index: number): string;
|
|
52
|
-
/**
|
|
53
|
-
* The fields of a script's ONE summary record — the shape every mutation here
|
|
54
|
-
* returns (counts first, then any per-item failures as further records).
|
|
55
|
-
*/
|
|
55
|
+
/** The fields of a script's first record — the summary a mutation returns ahead of any per-item rows. */
|
|
56
56
|
export declare function summaryFields(raw: string): string[];
|
|
57
|
-
/** A numeric field,
|
|
57
|
+
/** A numeric field, `fallback` when absent or unparsable. */
|
|
58
58
|
export declare function intField(fields: readonly string[], index: number, fallback?: number): number;
|
|
59
|
-
/**
|
|
60
|
-
* A boolean field. AppleScript renders booleans as the bare words `true`/`false`,
|
|
61
|
-
* so this is the counterpart of `shared/json.ts`'s coercions for the framed
|
|
62
|
-
* output — the Windows side gets those free from ConvertFrom-Json.
|
|
63
|
-
*/
|
|
59
|
+
/** A boolean field; AppleScript renders booleans as the bare words true/false. */
|
|
64
60
|
export declare function boolField(fields: readonly string[], index: number): boolean;
|
package/dist/mac/run.js
CHANGED
|
@@ -6,7 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.AS_HANDLERS = exports.AS_LIST_SEP = exports.AS_RECORD_SEP = exports.AS_FIELD_SEP = exports.LIST_SEP = exports.RECORD_SEP = exports.FIELD_SEP = void 0;
|
|
7
7
|
exports.runOsaScript = runOsaScript;
|
|
8
8
|
exports.asEscape = asEscape;
|
|
9
|
+
exports.asString = asString;
|
|
9
10
|
exports.asBool = asBool;
|
|
11
|
+
exports.asInt = asInt;
|
|
12
|
+
exports.asIdList = asIdList;
|
|
10
13
|
exports.asRow = asRow;
|
|
11
14
|
exports.splitRecords = splitRecords;
|
|
12
15
|
exports.splitFields = splitFields;
|
|
@@ -17,100 +20,104 @@ exports.intField = intField;
|
|
|
17
20
|
exports.boolField = boolField;
|
|
18
21
|
// Getting a generated AppleScript to osascript and its output back.
|
|
19
22
|
//
|
|
20
|
-
// The macOS counterpart of windows/run.ts:
|
|
21
|
-
// and the record framing every reader here shares.
|
|
22
|
-
const child_process_1 = require("child_process");
|
|
23
|
+
// The macOS counterpart of windows/run.ts: running a script, the escaping
|
|
24
|
+
// rules, and the record framing every reader here shares.
|
|
23
25
|
const fs_1 = __importDefault(require("fs"));
|
|
24
26
|
const runtime_1 = require("../runtime");
|
|
27
|
+
const exec_1 = require("../shared/exec");
|
|
25
28
|
const errors_1 = require("../errors");
|
|
26
29
|
const replyBody_1 = require("../shared/replyBody");
|
|
27
30
|
const RUNNER = 'osascript';
|
|
28
31
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
* osascript's report of a script error: "<file>:<start>:<end>: execution error:
|
|
33
|
+
* <message> (<number>)". Only the message is worth showing — these reach people
|
|
34
|
+
* in per-item failure lists — and -2700 is the number AppleScript gives every
|
|
35
|
+
* `error "…"` a script raises itself.
|
|
36
|
+
*/
|
|
37
|
+
function parseFailure(stderr) {
|
|
38
|
+
const message = stderr
|
|
39
|
+
.replace(/^(?:.*?:)?\d+:\d+:\s*execution error:\s*/m, '')
|
|
40
|
+
.replace(/\s*\(-2700\)\s*$/, '')
|
|
41
|
+
.trim();
|
|
42
|
+
return { message };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run a script from a temp file (no argument-length or quoting limits) and
|
|
46
|
+
* return what it printed, minus the newline osascript appends.
|
|
36
47
|
*
|
|
37
|
-
* `
|
|
38
|
-
*
|
|
48
|
+
* `AS_HANDLERS` is prepended here rather than by each caller: a script missing
|
|
49
|
+
* a handler fails to compile as a WHOLE, with an error pointing nowhere near the
|
|
50
|
+
* omission, so it can't be left to each script to remember.
|
|
39
51
|
*/
|
|
40
|
-
function runOsaScript(source,
|
|
41
|
-
// The full text is what osascript
|
|
42
|
-
//
|
|
52
|
+
async function runOsaScript(source, budget) {
|
|
53
|
+
// The full text is what osascript compiles, so it is also what error offsets
|
|
54
|
+
// refer to and what the debug hook and errors carry.
|
|
43
55
|
const script = exports.AS_HANDLERS + source;
|
|
44
|
-
const
|
|
45
|
-
fs_1.default.writeFileSync(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
catch { /* ignore */
|
|
56
|
-
}
|
|
57
|
-
if (error) {
|
|
58
|
-
// Running from a file, osascript prefixes the script path:
|
|
59
|
-
// "/tmp/outlook-bridge-osa-1.applescript:12:34: execution error:
|
|
60
|
-
// Microsoft Outlook got an error: … (-1728)". Keep just the human
|
|
61
|
-
// part — these strings reach the user in per-email failure lists.
|
|
62
|
-
const msg = (stderr || error.message)
|
|
63
|
-
.replace(/^(?:.*?:)?\d+:\d+:\s*execution error:\s*/m, '')
|
|
64
|
-
.trim();
|
|
65
|
-
const failure = (0, errors_1.classifyRunFailure)({
|
|
66
|
-
runner: RUNNER,
|
|
67
|
-
script,
|
|
68
|
-
stderr: msg,
|
|
69
|
-
durationMs,
|
|
70
|
-
nodeError: error,
|
|
71
|
-
timeoutMs: effectiveTimeout,
|
|
72
|
-
signal,
|
|
73
|
-
});
|
|
74
|
-
(0, runtime_1.reportRun)({ runner: RUNNER, script, durationMs, error: failure.message });
|
|
75
|
-
reject(failure);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
(0, runtime_1.reportRun)({ runner: RUNNER, script, durationMs });
|
|
79
|
-
resolve(stdout.replace(/\n$/, ''));
|
|
80
|
-
}
|
|
56
|
+
const file = (0, runtime_1.tempFile)('osa', 'applescript');
|
|
57
|
+
fs_1.default.writeFileSync(file, script, 'utf8');
|
|
58
|
+
try {
|
|
59
|
+
const { stdout } = await (0, exec_1.runScriptProcess)({
|
|
60
|
+
runner: RUNNER,
|
|
61
|
+
command: 'osascript',
|
|
62
|
+
args: [file],
|
|
63
|
+
script,
|
|
64
|
+
budget,
|
|
65
|
+
parseFailure,
|
|
81
66
|
});
|
|
82
|
-
|
|
67
|
+
return stdout.replace(/\n$/, '');
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
(0, runtime_1.removeTempFile)(file);
|
|
71
|
+
}
|
|
83
72
|
}
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Escape text for the inside of an AppleScript double-quoted literal.
|
|
75
|
+
* AppleScript strings never interpolate, so escaping the quote, the backslash
|
|
76
|
+
* and line breaks is the whole job.
|
|
77
|
+
*/
|
|
78
|
+
function asEscape(text) {
|
|
79
|
+
return text
|
|
87
80
|
.replace(/\\/g, '\\\\')
|
|
88
81
|
.replace(/"/g, '\\"')
|
|
89
82
|
.replace(/\r\n|\r|\n/g, '\\n');
|
|
90
83
|
}
|
|
84
|
+
/** Text as an AppleScript string literal. */
|
|
85
|
+
function asString(text) {
|
|
86
|
+
return `"${asEscape(text)}"`;
|
|
87
|
+
}
|
|
91
88
|
/** AppleScript's boolean literals. */
|
|
92
89
|
function asBool(value) {
|
|
93
90
|
return value ? 'true' : 'false';
|
|
94
91
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
/** A whole number for a script; anything else is refused before it becomes code. */
|
|
93
|
+
function asInt(value) {
|
|
94
|
+
if (!Number.isSafeInteger(value)) {
|
|
95
|
+
throw new errors_1.InvalidRequestError(`Expected a whole number, got ${String(value)}.`);
|
|
96
|
+
}
|
|
97
|
+
return String(value);
|
|
98
|
+
}
|
|
99
|
+
/** Message ids (already validated as integers) as an AppleScript list literal. */
|
|
100
|
+
function asIdList(ids) {
|
|
101
|
+
return `{${ids.map(id => asInt(Number(id))).join(', ')}}`;
|
|
102
|
+
}
|
|
103
|
+
// ── Record framing ───────────────────────────────────────────────────────
|
|
104
|
+
//
|
|
105
|
+
// Control characters separate fields, records and list items: subjects and
|
|
106
|
+
// bodies contain tabs, newlines and commas, but never these — and every emitted
|
|
107
|
+
// field passes through the `sanitize` handler, which makes the framing safe
|
|
108
|
+
// rather than merely unlikely to break.
|
|
109
|
+
exports.FIELD_SEP = '';
|
|
110
|
+
exports.RECORD_SEP = '';
|
|
111
|
+
exports.LIST_SEP = '';
|
|
102
112
|
/** The same three, as AppleScript expressions. */
|
|
103
113
|
exports.AS_FIELD_SEP = '(character id 31)';
|
|
104
114
|
exports.AS_RECORD_SEP = '(character id 30)';
|
|
105
115
|
exports.AS_LIST_SEP = '(character id 29)';
|
|
106
116
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* rather than merely unlikely to break. A field holding a LIST must be built
|
|
112
|
-
* with `my sanitizeList(...)`, which cleans the elements instead — `sanitize`
|
|
113
|
-
* deliberately leaves the list separator alone.
|
|
117
|
+
* One record: the AppleScript expressions in `fields`, separated and terminated
|
|
118
|
+
* so `splitRecords`/`splitFields` can take them apart again. A field holding a
|
|
119
|
+
* LIST must be built with `my sanitizeList(...)`, which cleans the elements —
|
|
120
|
+
* `sanitize` deliberately leaves the list separator alone.
|
|
114
121
|
*/
|
|
115
122
|
function asRow(fields) {
|
|
116
123
|
return fields
|
|
@@ -118,12 +125,11 @@ function asRow(fields) {
|
|
|
118
125
|
.join(` & ${exports.AS_FIELD_SEP} & `) + ` & ${exports.AS_RECORD_SEP}`;
|
|
119
126
|
}
|
|
120
127
|
/**
|
|
121
|
-
* The
|
|
122
|
-
* inside a `tell` block.
|
|
128
|
+
* The handlers every script relies on, called with `my` from inside a `tell`.
|
|
123
129
|
*
|
|
124
|
-
* `isoDate` produces the zero-padded 'yyyy-MM-dd HH:mm' both platforms
|
|
125
|
-
*
|
|
126
|
-
*
|
|
130
|
+
* `isoDate` produces the zero-padded 'yyyy-MM-dd HH:mm' both platforms return;
|
|
131
|
+
* `sanitize` strips the framing characters; `joinList` and `replaceText` are the
|
|
132
|
+
* text-item-delimiter dance nobody should write twice.
|
|
127
133
|
*/
|
|
128
134
|
exports.AS_HANDLERS = `
|
|
129
135
|
on pad2(n)
|
|
@@ -156,8 +162,7 @@ on replaceText(t, findWhat, replaceWith)
|
|
|
156
162
|
end replaceText
|
|
157
163
|
|
|
158
164
|
-- Strips the FIELD and RECORD separators only. The LIST separator is left
|
|
159
|
-
-- alone on purpose: a field may legitimately BE a list
|
|
160
|
-
-- collapsed every such field into one run-on string.
|
|
165
|
+
-- alone on purpose: a field may legitimately BE a list.
|
|
161
166
|
on sanitize(v)
|
|
162
167
|
if v is missing value then return ""
|
|
163
168
|
set t to v as string
|
|
@@ -174,10 +179,8 @@ on sanitizeList(lst)
|
|
|
174
179
|
set cleaned to {}
|
|
175
180
|
repeat with item_ in lst
|
|
176
181
|
set t to my sanitize(item_)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if t contains marker then set t to my replaceText(t, marker, " ")
|
|
180
|
-
end repeat
|
|
182
|
+
set marker to (character id 29)
|
|
183
|
+
if t contains marker then set t to my replaceText(t, marker, " ")
|
|
181
184
|
set end of cleaned to t
|
|
182
185
|
end repeat
|
|
183
186
|
return my joinList(cleaned, (character id 29))
|
|
@@ -187,11 +190,10 @@ on insertAboveQuoted(c, ins)
|
|
|
187
190
|
if c is missing value then return ins
|
|
188
191
|
set c to c as string
|
|
189
192
|
if (length of c) is 0 then return ins
|
|
190
|
-
--
|
|
191
|
-
--
|
|
192
|
-
--
|
|
193
|
-
--
|
|
194
|
-
-- called that however naturally it reads.
|
|
193
|
+
-- A reply's own empty paragraph sits inside WordSection1; inserting straight
|
|
194
|
+
-- after that tag puts the new text where the cursor would be. Anything else
|
|
195
|
+
-- falls back to just inside <body>. ('at' is a reserved parameter name, so
|
|
196
|
+
-- the cut point can't be called that.)
|
|
195
197
|
set cutPoint to my tagEnd(c, "${replyBody_1.WORD_SECTION_ANCHOR}")
|
|
196
198
|
if cutPoint is 0 then set cutPoint to my tagEnd(c, "<body")
|
|
197
199
|
if cutPoint is 0 then return ins & c
|
|
@@ -199,8 +201,8 @@ on insertAboveQuoted(c, ins)
|
|
|
199
201
|
return (text 1 thru cutPoint of c) & ins & (text (cutPoint + 1) thru -1 of c)
|
|
200
202
|
end insertAboveQuoted
|
|
201
203
|
|
|
202
|
-
-- 'rest' is an AppleScript term
|
|
203
|
-
--
|
|
204
|
+
-- 'rest' is an AppleScript term, so the remainder can't be called that:
|
|
205
|
+
-- assigning to it compiles and then fails at run time.
|
|
204
206
|
on tagEnd(c, marker)
|
|
205
207
|
set ix to offset of marker in c
|
|
206
208
|
if ix is 0 then return 0
|
|
@@ -211,39 +213,32 @@ on tagEnd(c, marker)
|
|
|
211
213
|
return ix + gt - 1
|
|
212
214
|
end tagEnd
|
|
213
215
|
`;
|
|
214
|
-
/**
|
|
216
|
+
/** A script's output as records, without the trailing empty one. */
|
|
215
217
|
function splitRecords(raw) {
|
|
216
218
|
return raw.split(exports.RECORD_SEP).filter(record => record.trim() !== '');
|
|
217
219
|
}
|
|
218
|
-
/**
|
|
220
|
+
/** One record's fields. */
|
|
219
221
|
function splitFields(record) {
|
|
220
222
|
return record.split(exports.FIELD_SEP);
|
|
221
223
|
}
|
|
222
|
-
/**
|
|
224
|
+
/** A field that carries a list. */
|
|
223
225
|
function splitList(value) {
|
|
224
226
|
return (value || '').split(exports.LIST_SEP).filter(Boolean);
|
|
225
227
|
}
|
|
226
|
-
/** A field by position
|
|
228
|
+
/** A field by position; a missing field reads as ''. */
|
|
227
229
|
function field(fields, index) {
|
|
228
230
|
return fields[index] ?? '';
|
|
229
231
|
}
|
|
230
|
-
/**
|
|
231
|
-
* The fields of a script's ONE summary record — the shape every mutation here
|
|
232
|
-
* returns (counts first, then any per-item failures as further records).
|
|
233
|
-
*/
|
|
232
|
+
/** The fields of a script's first record — the summary a mutation returns ahead of any per-item rows. */
|
|
234
233
|
function summaryFields(raw) {
|
|
235
234
|
return splitFields(splitRecords(raw)[0] ?? '');
|
|
236
235
|
}
|
|
237
|
-
/** A numeric field,
|
|
236
|
+
/** A numeric field, `fallback` when absent or unparsable. */
|
|
238
237
|
function intField(fields, index, fallback = 0) {
|
|
239
238
|
const parsed = Number.parseInt(field(fields, index), 10);
|
|
240
239
|
return Number.isNaN(parsed) ? fallback : parsed;
|
|
241
240
|
}
|
|
242
|
-
/**
|
|
243
|
-
* A boolean field. AppleScript renders booleans as the bare words `true`/`false`,
|
|
244
|
-
* so this is the counterpart of `shared/json.ts`'s coercions for the framed
|
|
245
|
-
* output — the Windows side gets those free from ConvertFrom-Json.
|
|
246
|
-
*/
|
|
241
|
+
/** A boolean field; AppleScript renders booleans as the bare words true/false. */
|
|
247
242
|
function boolField(fields, index) {
|
|
248
243
|
return field(fields, index).trim() === 'true';
|
|
249
244
|
}
|
package/dist/mac/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/mac/run.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/mac/run.ts"],"names":[],"mappings":";;;;;;AAkCA,oCAmBC;AAOD,4BAKC;AAGD,4BAEC;AAGD,wBAEC;AAGD,sBAKC;AAGD,4BAEC;AAuBD,sBAIC;AA6FD,oCAEC;AAGD,kCAEC;AAGD,8BAEC;AAGD,sBAEC;AAGD,sCAEC;AAGD,4BAGC;AAGD,8BAEC;AAjPD,oEAAoE;AACpE,EAAE;AACF,0EAA0E;AAC1E,0DAA0D;AAC1D,4CAAoB;AACpB,wCAAsE;AACtE,yCAAkD;AAClD,sCAAgD;AAChD,mDAA0D;AAE1D,MAAM,MAAM,GAAG,WAAoB,CAAC;AAEpC;;;;;GAKG;AACH,SAAS,YAAY,CAAC,MAAc;IAChC,MAAM,OAAO,GAAG,MAAM;SACjB,OAAO,CAAC,2CAA2C,EAAE,EAAE,CAAC;SACxD,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,IAAI,EAAE,CAAC;IACZ,OAAO,EAAC,OAAO,EAAC,CAAC;AACrB,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,MAAiB;IAChE,6EAA6E;IAC7E,qDAAqD;IACrD,MAAM,MAAM,GAAG,mBAAW,GAAG,MAAM,CAAC;IACpC,MAAM,IAAI,GAAG,IAAA,kBAAQ,EAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAC5C,YAAE,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC;QACD,MAAM,EAAC,MAAM,EAAC,GAAG,MAAM,IAAA,uBAAgB,EAAC;YACpC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,CAAC,IAAI,CAAC;YACZ,MAAM;YACN,MAAM;YACN,YAAY;SACf,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrC,CAAC;YAAS,CAAC;QACP,IAAA,wBAAc,EAAC,IAAI,CAAC,CAAC;IACzB,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAgB,QAAQ,CAAC,IAAY;IACjC,OAAO,IAAI;SACN,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,6CAA6C;AAC7C,SAAgB,QAAQ,CAAC,IAAY;IACjC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;AACjC,CAAC;AAED,sCAAsC;AACtC,SAAgB,MAAM,CAAC,KAAc;IACjC,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AACpC,CAAC;AAED,oFAAoF;AACpF,SAAgB,KAAK,CAAC,KAAa;IAC/B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,4BAAmB,CAAC,gCAAgC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,kFAAkF;AAClF,SAAgB,QAAQ,CAAC,GAAsB;IAC3C,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC9D,CAAC;AAED,4EAA4E;AAC5E,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAChF,4EAA4E;AAC5E,wCAAwC;AAC3B,QAAA,SAAS,GAAG,GAAG,CAAC;AAChB,QAAA,UAAU,GAAG,GAAG,CAAC;AACjB,QAAA,QAAQ,GAAG,GAAG,CAAC;AAE5B,kDAAkD;AACrC,QAAA,YAAY,GAAG,mBAAmB,CAAC;AACnC,QAAA,aAAa,GAAG,mBAAmB,CAAC;AACpC,QAAA,WAAW,GAAG,mBAAmB,CAAC;AAE/C;;;;;GAKG;AACH,SAAgB,KAAK,CAAC,MAAyB;IAC3C,OAAO,MAAM;SACR,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC;SAC7B,IAAI,CAAC,MAAM,oBAAY,KAAK,CAAC,GAAG,MAAM,qBAAa,EAAE,CAAC;AAC/D,CAAC;AAED;;;;;;GAMG;AACU,QAAA,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCA+DS,+BAAmB;;;;;;;;;;;;;;;;;;CAkBtD,CAAC;AAEF,oEAAoE;AACpE,SAAgB,YAAY,CAAC,GAAW;IACpC,OAAO,GAAG,CAAC,KAAK,CAAC,kBAAU,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,2BAA2B;AAC3B,SAAgB,WAAW,CAAC,MAAc;IACtC,OAAO,MAAM,CAAC,KAAK,CAAC,iBAAS,CAAC,CAAC;AACnC,CAAC;AAED,mCAAmC;AACnC,SAAgB,SAAS,CAAC,KAAyB;IAC/C,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACzD,CAAC;AAED,wDAAwD;AACxD,SAAgB,KAAK,CAAC,MAAyB,EAAE,KAAa;IAC1D,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,yGAAyG;AACzG,SAAgB,aAAa,CAAC,GAAW;IACrC,OAAO,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,6DAA6D;AAC7D,SAAgB,QAAQ,CAAC,MAAyB,EAAE,KAAa,EAAE,QAAQ,GAAG,CAAC;IAC3E,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACzD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;AACpD,CAAC;AAED,kFAAkF;AAClF,SAAgB,SAAS,CAAC,MAAyB,EAAE,KAAa;IAC9D,OAAO,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC;AAClD,CAAC","sourcesContent":["// Getting a generated AppleScript to osascript and its output back.\n//\n// The macOS counterpart of windows/run.ts: running a script, the escaping\n// rules, and the record framing every reader here shares.\nimport fs from 'fs';\nimport { removeTempFile, type RunBudget, tempFile } from '../runtime';\nimport { runScriptProcess } from '../shared/exec';\nimport { InvalidRequestError } from '../errors';\nimport { WORD_SECTION_ANCHOR } from '../shared/replyBody';\n\nconst RUNNER = 'osascript' as const;\n\n/**\n * osascript's report of a script error: \"<file>:<start>:<end>: execution error:\n * <message> (<number>)\". Only the message is worth showing — these reach people\n * in per-item failure lists — and -2700 is the number AppleScript gives every\n * `error \"…\"` a script raises itself.\n */\nfunction parseFailure(stderr: string): { message: string } {\n const message = stderr\n .replace(/^(?:.*?:)?\\d+:\\d+:\\s*execution error:\\s*/m, '')\n .replace(/\\s*\\(-2700\\)\\s*$/, '')\n .trim();\n return {message};\n}\n\n/**\n * Run a script from a temp file (no argument-length or quoting limits) and\n * return what it printed, minus the newline osascript appends.\n *\n * `AS_HANDLERS` is prepended here rather than by each caller: a script missing\n * a handler fails to compile as a WHOLE, with an error pointing nowhere near the\n * omission, so it can't be left to each script to remember.\n */\nexport async function runOsaScript(source: string, budget: RunBudget): Promise<string> {\n // The full text is what osascript compiles, so it is also what error offsets\n // refer to and what the debug hook and errors carry.\n const script = AS_HANDLERS + source;\n const file = tempFile('osa', 'applescript');\n fs.writeFileSync(file, script, 'utf8');\n try {\n const {stdout} = await runScriptProcess({\n runner: RUNNER,\n command: 'osascript',\n args: [file],\n script,\n budget,\n parseFailure,\n });\n return stdout.replace(/\\n$/, '');\n } finally {\n removeTempFile(file);\n }\n}\n\n/**\n * Escape text for the inside of an AppleScript double-quoted literal.\n * AppleScript strings never interpolate, so escaping the quote, the backslash\n * and line breaks is the whole job.\n */\nexport function asEscape(text: string): string {\n return text\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\r\\n|\\r|\\n/g, '\\\\n');\n}\n\n/** Text as an AppleScript string literal. */\nexport function asString(text: string): string {\n return `\"${asEscape(text)}\"`;\n}\n\n/** AppleScript's boolean literals. */\nexport function asBool(value: boolean): string {\n return value ? 'true' : 'false';\n}\n\n/** A whole number for a script; anything else is refused before it becomes code. */\nexport function asInt(value: number): string {\n if (!Number.isSafeInteger(value)) {\n throw new InvalidRequestError(`Expected a whole number, got ${String(value)}.`);\n }\n return String(value);\n}\n\n/** Message ids (already validated as integers) as an AppleScript list literal. */\nexport function asIdList(ids: readonly string[]): string {\n return `{${ids.map(id => asInt(Number(id))).join(', ')}}`;\n}\n\n// ── Record framing ───────────────────────────────────────────────────────\n//\n// Control characters separate fields, records and list items: subjects and\n// bodies contain tabs, newlines and commas, but never these — and every emitted\n// field passes through the `sanitize` handler, which makes the framing safe\n// rather than merely unlikely to break.\nexport const FIELD_SEP = '\u001f';\nexport const RECORD_SEP = '\u001e';\nexport const LIST_SEP = '\u001d';\n\n/** The same three, as AppleScript expressions. */\nexport const AS_FIELD_SEP = '(character id 31)';\nexport const AS_RECORD_SEP = '(character id 30)';\nexport const AS_LIST_SEP = '(character id 29)';\n\n/**\n * One record: the AppleScript expressions in `fields`, separated and terminated\n * so `splitRecords`/`splitFields` can take them apart again. A field holding a\n * LIST must be built with `my sanitizeList(...)`, which cleans the elements —\n * `sanitize` deliberately leaves the list separator alone.\n */\nexport function asRow(fields: readonly string[]): string {\n return fields\n .map(f => `my sanitize(${f})`)\n .join(` & ${AS_FIELD_SEP} & `) + ` & ${AS_RECORD_SEP}`;\n}\n\n/**\n * The handlers every script relies on, called with `my` from inside a `tell`.\n *\n * `isoDate` produces the zero-padded 'yyyy-MM-dd HH:mm' both platforms return;\n * `sanitize` strips the framing characters; `joinList` and `replaceText` are the\n * text-item-delimiter dance nobody should write twice.\n */\nexport const AS_HANDLERS = `\non pad2(n)\n set s to (n as integer) as string\n if (length of s) < 2 then set s to \"0\" & s\n return s\nend pad2\n\non isoDate(d)\n if d is missing value then return \"\"\n return (year of d as string) & \"-\" & pad2(month of d as integer) & \"-\" & pad2(day of d) & \" \" & pad2(hours of d) & \":\" & pad2(minutes of d)\nend isoDate\n\non joinList(lst, sep)\n set saved to AppleScript's text item delimiters\n set AppleScript's text item delimiters to sep\n set out to lst as string\n set AppleScript's text item delimiters to saved\n return out\nend joinList\n\non replaceText(t, findWhat, replaceWith)\n set saved to AppleScript's text item delimiters\n set AppleScript's text item delimiters to findWhat\n set parts to text items of t\n set AppleScript's text item delimiters to replaceWith\n set out to parts as string\n set AppleScript's text item delimiters to saved\n return out\nend replaceText\n\n-- Strips the FIELD and RECORD separators only. The LIST separator is left\n-- alone on purpose: a field may legitimately BE a list.\non sanitize(v)\n if v is missing value then return \"\"\n set t to v as string\n repeat with codePoint in {30, 31}\n set marker to (character id (codePoint as integer))\n if t contains marker then set t to my replaceText(t, marker, \" \")\n end repeat\n return t\nend sanitize\n\n-- A list field: each element loses all three separators, then they are joined\n-- with the LIST separator that splitList() takes them apart on.\non sanitizeList(lst)\n set cleaned to {}\n repeat with item_ in lst\n set t to my sanitize(item_)\n set marker to (character id 29)\n if t contains marker then set t to my replaceText(t, marker, \" \")\n set end of cleaned to t\n end repeat\n return my joinList(cleaned, (character id 29))\nend sanitizeList\n\non insertAboveQuoted(c, ins)\n if c is missing value then return ins\n set c to c as string\n if (length of c) is 0 then return ins\n -- A reply's own empty paragraph sits inside WordSection1; inserting straight\n -- after that tag puts the new text where the cursor would be. Anything else\n -- falls back to just inside <body>. ('at' is a reserved parameter name, so\n -- the cut point can't be called that.)\n set cutPoint to my tagEnd(c, \"${WORD_SECTION_ANCHOR}\")\n if cutPoint is 0 then set cutPoint to my tagEnd(c, \"<body\")\n if cutPoint is 0 then return ins & c\n if cutPoint is (length of c) then return c & ins\n return (text 1 thru cutPoint of c) & ins & (text (cutPoint + 1) thru -1 of c)\nend insertAboveQuoted\n\n-- 'rest' is an AppleScript term, so the remainder can't be called that:\n-- assigning to it compiles and then fails at run time.\non tagEnd(c, marker)\n set ix to offset of marker in c\n if ix is 0 then return 0\n if marker ends with \">\" then return ix + (length of marker) - 1\n set tailText to text ix thru -1 of c\n set gt to offset of \">\" in tailText\n if gt is 0 then return 0\n return ix + gt - 1\nend tagEnd\n`;\n\n/** A script's output as records, without the trailing empty one. */\nexport function splitRecords(raw: string): string[] {\n return raw.split(RECORD_SEP).filter(record => record.trim() !== '');\n}\n\n/** One record's fields. */\nexport function splitFields(record: string): string[] {\n return record.split(FIELD_SEP);\n}\n\n/** A field that carries a list. */\nexport function splitList(value: string | undefined): string[] {\n return (value || '').split(LIST_SEP).filter(Boolean);\n}\n\n/** A field by position; a missing field reads as ''. */\nexport function field(fields: readonly string[], index: number): string {\n return fields[index] ?? '';\n}\n\n/** The fields of a script's first record — the summary a mutation returns ahead of any per-item rows. */\nexport function summaryFields(raw: string): string[] {\n return splitFields(splitRecords(raw)[0] ?? '');\n}\n\n/** A numeric field, `fallback` when absent or unparsable. */\nexport function intField(fields: readonly string[], index: number, fallback = 0): number {\n const parsed = Number.parseInt(field(fields, index), 10);\n return Number.isNaN(parsed) ? fallback : parsed;\n}\n\n/** A boolean field; AppleScript renders booleans as the bare words true/false. */\nexport function boolField(fields: readonly string[], index: number): boolean {\n return field(fields, index).trim() === 'true';\n}\n"]}
|