applescript-node 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ export interface CompileOptions {
2
+ language?: 'AppleScript' | 'JavaScript';
3
+ executeOnly?: boolean;
4
+ stayOpen?: boolean;
5
+ useStartupScreen?: boolean;
6
+ outputPath?: string;
7
+ bundleScript?: boolean;
8
+ }
9
+ export interface CompileResult {
10
+ success: boolean;
11
+ outputPath: string;
12
+ error?: string;
13
+ }
14
+ export declare function compileScript(script: string, options?: CompileOptions): Promise<CompileResult>;
15
+ export declare function compileScriptFile(filePath: string, options?: CompileOptions): Promise<CompileResult>;
@@ -0,0 +1,6 @@
1
+ export interface DecompileResult {
2
+ success: boolean;
3
+ source?: string;
4
+ error?: string;
5
+ }
6
+ export declare function decompileScript(scriptPath: string): Promise<DecompileResult>;
@@ -0,0 +1,97 @@
1
+ import type { OsaScriptOptions, ScriptExecutionResult } from './types.js';
2
+ /**
3
+ * ScriptExecutor - Core execution engine for AppleScript and JavaScript
4
+ *
5
+ * **IMPORTANT: Error Handling**
6
+ *
7
+ * This class implements robust error handling that is critical for production use:
8
+ *
9
+ * 1. **Type-Safe Error Extraction**: Errors from `child_process.exec` can be various types
10
+ * (Error objects, ErrnoException, etc.). The error handling properly extracts:
11
+ * - Error messages from Error instances or converts other types to strings
12
+ * - Exit codes from ErrnoException.code (which can be string or number)
13
+ * - Defaults to exit code 1 if code is missing or invalid
14
+ *
15
+ * 2. **Exit Code Handling**: The exit code extraction handles three cases:
16
+ * - String codes: Parsed with parseInt (e.g., "1" -> 1)
17
+ * - Number codes: Used directly
18
+ * - Missing/null codes: Defaults to 1
19
+ *
20
+ * 3. **Why This Matters**: Without proper error handling:
21
+ * - TypeScript would allow unsafe type assertions
22
+ * - Runtime errors could occur when accessing error.code
23
+ * - Exit codes might be undefined, causing downstream issues
24
+ *
25
+ * **Usage Example:**
26
+ * ```typescript
27
+ * const result = await ScriptExecutor.execute('tell app "Finder" to activate');
28
+ * if (!result.success) {
29
+ * console.error(`Script failed with exit code ${result.exitCode}: ${result.error}`);
30
+ * }
31
+ * ```
32
+ *
33
+ * **Testing Considerations:**
34
+ * - Always test both success and failure paths
35
+ * - Mock different error types (Error, ErrnoException, etc.)
36
+ * - Verify exit codes are correctly extracted and defaulted
37
+ * - Ensure error messages are properly extracted regardless of error type
38
+ *
39
+ * @see {@link ScriptExecutionResult} for the result structure
40
+ * @see {@link OsaScriptOptions} for execution options
41
+ */
42
+ export declare class ScriptExecutor {
43
+ private static buildFlags;
44
+ /**
45
+ * Execute an AppleScript or JavaScript string
46
+ *
47
+ * **Error Handling:**
48
+ * This method implements comprehensive error handling that safely extracts:
49
+ * - Error messages from any error type (Error, ErrnoException, etc.)
50
+ * - Exit codes with proper type checking and defaults
51
+ *
52
+ * **Important Notes:**
53
+ * - Script strings are automatically escaped for shell safety
54
+ * - Single quotes in scripts are escaped using the pattern: `'` -> `'"'"'`
55
+ * - Always returns a result object, never throws (errors are captured in result)
56
+ *
57
+ * @param script - The AppleScript or JavaScript code to execute
58
+ * @param options - Execution options (language, output format, etc.)
59
+ * @returns Promise resolving to execution result with success flag, output, error, and exit code
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const result = await ScriptExecutor.execute('tell app "Finder" to get name');
64
+ * if (result.success) {
65
+ * console.log(result.output); // "Finder"
66
+ * } else {
67
+ * console.error(`Failed: ${result.error} (exit code: ${result.exitCode})`);
68
+ * }
69
+ * ```
70
+ */
71
+ static execute<T = string>(script: string, options?: OsaScriptOptions): Promise<ScriptExecutionResult<T>>;
72
+ /**
73
+ * Execute an AppleScript or JavaScript file
74
+ *
75
+ * **Error Handling:**
76
+ * Uses the same robust error handling as `execute()` method.
77
+ * See {@link ScriptExecutor.execute} for details on error handling patterns.
78
+ *
79
+ * **Important Notes:**
80
+ * - File paths are passed directly to osascript (no shell escaping needed)
81
+ * - File must exist and be readable
82
+ * - Always returns a result object, never throws
83
+ *
84
+ * @param filePath - Path to the script file (.applescript, .scpt, etc.)
85
+ * @param options - Execution options (language, output format, etc.)
86
+ * @returns Promise resolving to execution result with success flag, output, error, and exit code
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const result = await ScriptExecutor.executeFile('./scripts/my-script.applescript');
91
+ * if (result.success) {
92
+ * console.log(result.output);
93
+ * }
94
+ * ```
95
+ */
96
+ static executeFile<T = string>(filePath: string, options?: OsaScriptOptions): Promise<ScriptExecutionResult<T>>;
97
+ }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * ExprBuilder provides a type-safe DSL for building AppleScript expressions.
3
+ * Instead of writing raw strings like: `if('counter > 10')`
4
+ * You can write: `if(expr => expr.gt('counter', 10))`
5
+ *
6
+ * This enables:
7
+ * - Type checking and autocomplete for variables in scope
8
+ * - Prevention of common syntax errors
9
+ * - Composable expressions
10
+ * - Self-documenting code
11
+ *
12
+ * @template TScope - Union of variable names available in the current scope.
13
+ * When used within loop callbacks like `forEach`, this will include loop variables
14
+ * like 'aPerson', 'aNote', etc., enabling autocomplete for them.
15
+ *
16
+ * @example
17
+ * // Basic usage with scoped variable
18
+ * .forEach('aPerson', 'every person', (b) => {
19
+ * b.ifThen(
20
+ * (e) => e.gt(e.count(e.property('aPerson', 'emails')), 0),
21
+ * // ^^^^^^^^ 'aPerson' gets autocomplete!
22
+ * (then_) => then_.setExpression('email', (e) =>
23
+ * e.valueOfItem(1, e.property('aPerson', 'emails'))
24
+ * )
25
+ * );
26
+ * })
27
+ *
28
+ * @example
29
+ * // Nested loops with multiple scoped variables
30
+ * .forEach('anAccount', 'every account', (outer) => {
31
+ * outer.forEach('aNote', 'notes of anAccount', (inner) => {
32
+ * // Both 'anAccount' and 'aNote' are in scope here
33
+ * inner.setExpression('accountName', (e) => e.property('anAccount', 'name'));
34
+ * inner.setExpression('noteName', (e) => e.property('aNote', 'name'));
35
+ * });
36
+ * })
37
+ */
38
+ export declare class ExprBuilder<TScope extends string = never> {
39
+ /**
40
+ * Greater than comparison: left > right
41
+ * @param left - Variable or expression. Scoped variables (from loops) get autocomplete.
42
+ * The type `TScope | (string & Record<never, never>)` provides autocomplete for scoped
43
+ * variables while still accepting any string expression.
44
+ * @param right - Value to compare against (string or number)
45
+ * @returns AppleScript expression: "left > right"
46
+ * @example
47
+ * e.gt('counter', 10) // => "counter > 10"
48
+ * e.gt('aPerson', 5) // => "aPerson > 5" (with autocomplete for 'aPerson' if in scope)
49
+ */
50
+ gt(left: TScope | (string & Record<never, never>), right: string | number): string;
51
+ /**
52
+ * Less than comparison: left < right
53
+ * @param left - Variable or expression (scoped variables get autocomplete)
54
+ */
55
+ lt(left: TScope | (string & Record<never, never>), right: string | number): string;
56
+ /**
57
+ * Greater than or equal: left >= right
58
+ * @param left - Variable or expression (scoped variables get autocomplete)
59
+ */
60
+ gte(left: TScope | (string & Record<never, never>), right: string | number): string;
61
+ /**
62
+ * Less than or equal: left <= right
63
+ * @param left - Variable or expression (scoped variables get autocomplete)
64
+ */
65
+ lte(left: TScope | (string & Record<never, never>), right: string | number): string;
66
+ /**
67
+ * Equality comparison: left = right
68
+ * @param left - Variable or expression (scoped variables get autocomplete)
69
+ */
70
+ eq(left: TScope | (string & Record<never, never>), right: string | number | boolean): string;
71
+ /**
72
+ * Inequality comparison: left is not equal to right
73
+ * @param left - Variable or expression (scoped variables get autocomplete)
74
+ */
75
+ ne(left: TScope | (string & Record<never, never>), right: string | number | boolean): string;
76
+ /**
77
+ * Logical AND: combines multiple conditions
78
+ * Example: expr.and(expr.gt('x', 5), expr.lt('x', 10))
79
+ */
80
+ and(...conditions: string[]): string;
81
+ /**
82
+ * Logical OR: combines multiple conditions
83
+ * Example: expr.or(expr.eq('status', 'done'), expr.eq('status', 'skipped'))
84
+ */
85
+ or(...conditions: string[]): string;
86
+ /**
87
+ * Logical NOT: negates a condition
88
+ * Example: expr.not(expr.eq('status', 'pending'))
89
+ */
90
+ not(condition: string): string;
91
+ /**
92
+ * String length: length of str
93
+ * Often used in conditions like: expr.gt(expr.length('name'), 5)
94
+ */
95
+ length(str: string): string;
96
+ /**
97
+ * Property access: prop of obj
98
+ * @param obj - Variable name. Scoped variables (from loops) get autocomplete.
99
+ * The type allows both scoped variables and arbitrary string expressions.
100
+ * @param prop - Property name to access
101
+ * @returns AppleScript expression: "prop of obj"
102
+ * @example
103
+ * e.property('aNote', 'name') // => "name of aNote"
104
+ * e.property('aPerson', 'emails') // => "emails of aPerson" (with autocomplete for 'aPerson')
105
+ */
106
+ property(obj: TScope | (string & Record<never, never>), prop: string): string;
107
+ /**
108
+ * Count: count of items
109
+ * Example: expr.gt(expr.count('notes'), 10)
110
+ */
111
+ count(items: string): string;
112
+ /**
113
+ * Existence check: exists item
114
+ * Example: expr.exists('window "Settings"')
115
+ */
116
+ exists(item: string): string;
117
+ /**
118
+ * String contains: haystack contains needle
119
+ * Example: expr.contains('name', '"test"')
120
+ */
121
+ contains(haystack: string, needle: string | number): string;
122
+ /**
123
+ * String starts with: str begins with prefix
124
+ * Example: expr.startsWith('name', '"John"')
125
+ */
126
+ startsWith(str: string, prefix: string): string;
127
+ /**
128
+ * String ends with: str ends with suffix
129
+ * Example: expr.endsWith('name', '"son"')
130
+ */
131
+ endsWith(str: string, suffix: string): string;
132
+ /**
133
+ * Type checking: the type of item is typeName
134
+ * Common types: 'text', 'number', 'list', 'record', 'boolean'
135
+ */
136
+ typeEquals(item: string, type: string): string;
137
+ /**
138
+ * Matches wildcard pattern
139
+ * Example: expr.matches('name', '"*Smith*"')
140
+ */
141
+ matches(str: string, pattern: string): string;
142
+ /**
143
+ * Parentheses for explicit grouping
144
+ * Useful when combining complex boolean expressions
145
+ * Example: expr.or(expr.paren(expr.and(...)), expr.eq(...))
146
+ */
147
+ paren(condition: string): string;
148
+ /**
149
+ * Create a comparison between two expressions
150
+ * Useful for comparing two computed properties
151
+ * Example: expr.compare('length of name1', '>', 'length of name2')
152
+ */
153
+ compare(left: string, operator: '>' | '<' | '>=' | '<=' | '=' | '!=', right: string): string;
154
+ /**
155
+ * Collection accessor: every element of container
156
+ * Example: expr.every('participant', 'aChat') => "every participant of aChat"
157
+ * Example: expr.every('note', 'folder "Notes"') => "every note of folder \"Notes\""
158
+ */
159
+ every(element: string, container: string): string;
160
+ /**
161
+ * Nested property accessor: chains multiple properties
162
+ * Example: expr.nestedProperty('aChat', 'account', 'id') => "id of account of aChat"
163
+ * Example: expr.nestedProperty('note', 'folder', 'name') => "name of folder of note"
164
+ * @param obj - Variable name (scoped variables get autocomplete)
165
+ */
166
+ nestedProperty(obj: TScope | (string & Record<never, never>), ...properties: string[]): string;
167
+ /**
168
+ * Type casting: expression as type
169
+ * Example: expr.asType('creation date of aNote', 'string') => "creation date of aNote as string"
170
+ * Example: expr.asType(expr.property('aNote', 'id'), 'text') => "id of aNote as text"
171
+ */
172
+ asType(expression: string, type: 'string' | 'text' | 'number' | 'integer' | 'list'): string;
173
+ /**
174
+ * Substring/range accessor: text start thru end of source
175
+ * Example: expr.text(1, 100, 'notePlaintext') => "text 1 thru 100 of notePlaintext"
176
+ * Example: expr.text(5, 10, 'myString') => "text 5 thru 10 of myString"
177
+ */
178
+ text(start: number, end: number, source: string): string;
179
+ /**
180
+ * Character accessor: character n of source
181
+ * Example: expr.character(1, 'myString') => "character 1 of myString"
182
+ */
183
+ character(index: number, source: string): string;
184
+ /**
185
+ * Item accessor: item n of collection
186
+ * Example: expr.item(1, 'myList') => "item 1 of myList"
187
+ * Example: expr.item('i', 'notes') => "item i of notes"
188
+ */
189
+ item(index: number | string, collection: string): string;
190
+ /**
191
+ * Items range: items start thru end of collection
192
+ * Example: expr.items(1, 5, 'myList') => "items 1 thru 5 of myList"
193
+ */
194
+ items(start: number, end: number, collection: string): string;
195
+ /**
196
+ * First item: first element of collection
197
+ * Example: expr.first('note', 'notes') => "first note of notes"
198
+ */
199
+ first(element: string, collection: string): string;
200
+ /**
201
+ * Last item: last element of collection
202
+ * Example: expr.last('note', 'notes') => "last note of notes"
203
+ */
204
+ last(element: string, collection: string): string;
205
+ /**
206
+ * Some: some element where condition
207
+ * Example: expr.some('note', 'notes', 'name contains "test"')
208
+ */
209
+ some(element: string, collection: string, condition: string): string;
210
+ /**
211
+ * Filter: every element where condition
212
+ * Example: expr.filter('note', 'notes', 'shared = true')
213
+ */
214
+ filter(element: string, collection: string, condition: string): string;
215
+ /**
216
+ * Concatenation: left & right
217
+ * Example: expr.concat('text 1 thru 50 of body', '"..."') => 'text 1 thru 50 of body & "..."'
218
+ */
219
+ concat(...parts: string[]): string;
220
+ /**
221
+ * Property of item accessor: property of item N of collection
222
+ * Example: expr.propertyOfItem('value', 1, 'emails of aPerson') => "value of item 1 of emails of aPerson"
223
+ * Example: expr.propertyOfItem('name', 1, 'contacts') => "name of item 1 of contacts"
224
+ */
225
+ propertyOfItem(property: string, index: number | string, collection: string): string;
226
+ /**
227
+ * Value of item accessor (common shorthand for propertyOfItem('value', ...))
228
+ * Example: expr.valueOfItem(1, 'emails of aPerson') => "value of item 1 of emails of aPerson"
229
+ * Example: expr.valueOfItem(1, 'phones of contact') => "value of item 1 of phones of contact"
230
+ */
231
+ valueOfItem(index: number | string, collection: string): string;
232
+ }
233
+ /**
234
+ * Factory function for creating an ExprBuilder with type-tracked scope.
235
+ * This is typically not needed as callbacks receive an ExprBuilder instance automatically.
236
+ *
237
+ * @template TScope - Union of variable names available in the current scope.
238
+ * TypeScript will infer this from loop contexts automatically.
239
+ * @returns ExprBuilder instance with scope tracking
240
+ *
241
+ * @example
242
+ * // Usually you don't need to call this - use callbacks instead:
243
+ * .forEach('aPerson', 'every person', (b) => {
244
+ * b.ifThen((e) => e.gt('counter', 10), ...) // 'e' provided automatically
245
+ * })
246
+ *
247
+ * @example
248
+ * // Manual usage (rare):
249
+ * const e = expr<'myVar' | 'anotherVar'>();
250
+ * e.property('myVar', 'name'); // 'myVar' gets autocomplete
251
+ */
252
+ export declare function expr<TScope extends string = never>(): ExprBuilder<TScope>;
@@ -0,0 +1,17 @@
1
+ import { AppleScriptBuilder } from './builder.js';
2
+ import type { OsaScriptOptions, Prettify, ScriptBuilder, ScriptExecutionResult } from './types.js';
3
+ export * from './builder.js';
4
+ export * from './compiler.js';
5
+ export * from './decompiler.js';
6
+ export * from './executor.js';
7
+ export * from './expressions.js';
8
+ export * from './languages.js';
9
+ export * from './loader.js';
10
+ export * from './sdef.js';
11
+ export * as sources from './sources/index.js';
12
+ export type { AppleScriptValue, ApplicationDictionary, ApplicationTarget, Class, Command, Element, Enumeration, Enumerator, OsaScriptOptions, Parameter, Prettify, ProcessInfo, Property, PropertyExtractor, ScriptBuilder, ScriptError, ScriptExecutionResult, Suite, TypeInfo, WindowInfo, } from './types.js';
13
+ export * from './validator.js';
14
+ export declare const createScript: () => AppleScriptBuilder;
15
+ export declare function runScript<TScope extends string, TReturn>(script: ScriptBuilder<TScope, TReturn>, options?: OsaScriptOptions): Promise<ScriptExecutionResult<Prettify<TReturn>>>;
16
+ export declare function runScript<T = string>(script: string, options?: OsaScriptOptions): Promise<ScriptExecutionResult<Prettify<T>>>;
17
+ export declare const runScriptFile: <T = string>(filePath: string, options?: OsaScriptOptions) => Promise<ScriptExecutionResult<T>>;