Please check my understanding - JSX and JSContexts

Hi,
I am attempting to understand the inter application scripting landscape as it exists at the end of 2022. I have also found and read this topic On the horizon - QuarkXPress 2018 and other JavaScripts

  1. Am I correct that as Apple’s Javascript for Applications (JSX) uses Apple Events / OSA that scripts written in JSX can not run any faster than Apple Script versions except where some JavaScript data structures are more efficient?

  2. Given that both Apple Script and JSX appear to being given low priority by Apple suggests that learning JSX to write Automation Scripts is no more future proof than sticking with Apple Script.

  3. Applications such as those from the Omni Group have written their own bespoke Javascript syntax, known as a javascript context (JSC) and there is no agreed standard that will be common between applications.

  4. There are next to no tools like Script Debugger for writing and debugging either JSX or JSC.

I apologise for asking questions about javascript on an Applescript forum but I’m hoping that some of you will know the answers.

I have tried Omni’s javascript and even created a plugin and while I could probably live with the syntax the lack of any form of an IDE is rather a drag. Also, I have no idea if it is possible to write scripts that exchange data between desktop applications and even if it is the lack of a debugger will mean that development of any such script will be a painful experience.

Your thoughts welcome.

best wishes
Simon

→ JXA     (JSX)

(JSX is more commonly used to refer to a syntax used in the React web framework, JXA is the more frequently used abbreviation of macOS’s useful but never finished “JS for Automation”, for better or for worse)

Not a bespoke syntax. And JavaScriptCore is an Apple technology, not an Omni technology – Omni are just making use of it.

JXA and the application-embedded scripting environments like those of Omni JS, TaskPaper, Drafts etc all use their own copies of the same JavaScript interpreter that is used in Safari.

JavaScriptCore (JSC) is the framework which enables you use a copy of that interpreter outside Safari. It enables you to initialise a JSContext object in for example, Swift or ObjC, and make that available to users for the evaluation of JS scripts.

The idea of a “JXA ≠ JSC” distinction is just a slight misunderstanding. These various instances of JSC don’t differ in their JavaScript – they are all copies of exactly the same JS interpreter – they just differ in the automation libraries which are exposed in (made available to) their global context.

The JXA instance of JSC has a library for making use of the osascript (Apple Event) interface which is used by AppleScript.

The Omni applications, TaskPaper, Drafts etc embeddings of JSC have application-specific automation libraries.


On debugging, you can use the Safari JS debugger with JSContext instances.

More generally, you can reduce the need for debuggers by using constant, rather than mutable, name bindings : -)

(I personally haven’t used one (for debugging) in years, though SD is invaluable for probing the methods and properties of osascript interfaces)

Useful though, sometimes, to simply wrap one or mores expressions (perhaps a label string, followed by a comma and some other kind of raw or compound value) in a showLog function, perhaps defined as something like:

// showLog :: a -> IO ()
const showLog = (...args) =>
    // eslint-disable-next-line no-console
    console.log(
        args
        .map(JSON.stringify)
        .join(" -> ")
    );

and then see what appears at the JS console command line.

Many thanks Complexpoint for setting me straight with a very clear explanation.

So is it possible to write JXA script that accesses one application using OSA and another using JSC? For example read data from Apple Calendar and then pass it to OmniGraffle.

(I personally haven’t used one (for debugging) in years, though SD is invaluable for probing the methods and properties of osascript interfaces)

May I ask what you use?

Simon

In the case of the Omni applications, the osascript interface to their applications (which they still provide in addition to their JS Context interfaces, though they are not as actively maintained) includes an .evaluateJavascript method to which you can pass a JavaScript string:

  • from the osascript JXA JSContext, to
  • the separate Omni JSContext for evaluation there.

To be honest, since I more or less gave up variables, and switched to constant names and composable expressions, I haven’t experienced much need to scratch the head and trace the mutating meanings of variable names through a state history.

The showLog function above still turns out to be useful, sometimes, to check what value is emerging from a function application.

As a footnote, in the case of TaskPaper, you can:

  • pass values from the JXA context into the TaskPaper JS context, and
  • get values back into the JXA context.

An options argument (most easily used if it takes the form of a key/value dictionary, i.e. a JS Object) facilitates this.

Note also that this pattern of passing code from the outside into an application-embedded JS Context is simplified by the fact that any JS function definition is an object which can be converted to a source string.

(either with the .toString() method, or by wrapping it in a JS template literal string)

e.g.

Expand disclosure triangle to view JS source
(() => {
    "use strict";


    // ---------- TASKPAPER JS CONTEXT -----------

    const tp3Context = (editor, options) => {
        const outline = editor.outline;

        // Taskpaper JS Context code in here. ...

        return `${outline}`;
    };

    // ------------- JXA EVALUATION CONTEXT --------------

    // main :: IO ()
    const main = () => {
        const
            doc = Application("TaskPaper")
            .documents.at(0);

        return doc.exists() ? (
            doc.evaluate({
                script: `${tp3Context}`,
                withOptions: {}
            })
        ) : "No document open in TaskPaper.";
    };

    // MAIN ---
    return main();
})();

I’m not sure if my particular perspective is useful for you, but as someone who has written scripts for Adobe Creative Cloud software in both AppleScript and JavaScript, I can share a little bit of my experience with that.

First of all, it’s a terrible thing that Adobe let their ExtendScript Toolkit software languish in 32 bit software hell (Adobe uses an older version of JavaScript and calls it ExtendScript). It will not run on 64 bit machines AT ALL. You now need to use a plug-in for Microsoft Visual Studio to debug JavasScript for Adobe CC. It does work. I used it once, just to see if I could get some of my old JavaScripts to run, but it’s not as clear or simple (though some of that could just be my unfamiliarity with Visual Studio) as ExtendScript Toolkit was. The only reason I was even using JavaScript was because my employer at the time was doing all of their print production work on PCs and I didn’t have the opportunity to use AppleScript. It was hell, but I made it work (and actually, when I went back to AppleScript at another job later, I had developed some new techniques for AppleScript from that experience).

There is no better tool out there for automation on the Mac than Script Debugger and AppleScript.

The ability to look into the various properties and settings of all of the various objects unleashes the capacity for nearly total control over the documents when scripting. It’s like having the skeleton key to every single tool in the Adobe graphic library.

Also, the seamlessness of addressing multiple apps from tell statements is LIGHT YEARS easier than the weird data structures that you have to build between apps to get any interactivity from JavaScript. They were ridiculously complicated and difficult to implement.

JavaScript has some additional functionalities for things like text manipulation, being a more traditional programming tool than AppleScript, but for all of AppleScript’s limitations, I have yet to find anything that allows me to do my development as quickly or as simply as AppleScript, from simple basic scripts to complex suites of software than run and process things over networks and through multiple user’s machines.

If Apple kills AppleScript, I will be miserable and desperate. The only other language that I really find amenable to my software development style is Python. I’ve stayed kind of current on that, just in case I eventually have to make the switch.

As a graphic production designer and programmer, who has worked on automation software for The Pokémon Company International, Wizards of the Coast, Ravensburger North America (I’m the only person that has written graphic production software for all 3 top tier trading card games: Pokémon, Magic: The Gathering, and Disney’s new one, Lorcana) and many others, I don’t see anything else out there that would give me the power and control, and most of all, the informal fluidity, of AppleScript. It could have been a tool to bring programming to people like me that learned on the machines of the 70s and the 80s and get bogged down by the environments and IDEs of a lot of other hopelessly abstract “modern” programming languages.

I’m not ever going to write a device driver. I don’t need clean, perfectly compiled code. What I need is a tool to direct my software to intelligently process my graphic production documents WAY faster than a human. Productivity is not always higher just because a program runs faster. There are trade-offs, and I’ve learned how much I can accomplish with software that is slower than optimized software, but still thousands of times faster than human beings.

Programmers really need to learn this lesson, I believe. There is a place for a lot of different approaches, and this endlessly convoluted labyrinth of impenetrable programming languages isn’t the only way. Simple, generally applicable tools have an important place and it’s being completely neglected by the industry for the most part. If Apple kills AppleScript, I hope someone eventually resurrects its spirit as an open-source tool.

Perhaps these things don’t really turn on language war polemics ?

  1. The quality of a language is just its fit for for a particular user and job.
  2. The issue here is simply that the Apple Event model doesn’t apply to the phone and tablet operating systems – now used on the bulk of Apple hardware – the main source of its revenues.

It’s like everything – carpe diem – enjoy AppleScript while its here.

It’s not going to vanish in the next 10 minutes. If it does eventually become unworkable, there will be other things to choose from, and life’s main challenges will still be elsewhere :slight_smile:

You both make excellent points. I am still unclear if Javascript in any of its forms allows the control of several applications from within the one script.

I think I shall be sticking with Applescript rather than Javascript because I am invested in it, it still works and I just don’t like looking at scripts that are littered with punctuation characters. I thought it was accepted in computer science that you spend more time reading code than writing it yet languages littered with punctuation are everywhere :
const main = () => {

Chris, have you ever looked at Livecode? It is a hypercard like language and allows Applescript to be called from within its own application scripts and its can produce applications that run on desktops and mobiles.

best wishes
Simon

The question is not whether the language ‘allows’ it, but whether the available automation libraries support it.

If you use a JSContext to which the osascript Automation library is exposed (i.e. if you use JavaScript for Automation), you can address as many applications as you like, in exactly the same way as you do in AS.

The limitation, in JS as in AppleScript, is that you are doing so through the osascript Apple Event interface, which is macOS-only.

Another macOS inter-app pattern is the use of evaluateJavaScript methods – see Omni apps, TaskPaper – which allow you to pass a JS script from the osascript evaluation context into an app-embedded JSContext, and also get a value back from it.

The application-embedded JSContexts, those of the Omni apps, Drafts, etc work on iOS precisely because they are embedded with in an application, rather than driving it from outside.

  • The key strength of that approach is cross-platform applicability (execution speed too, for those that like that sort of thing)
  • The key limit of driving an application from within (using an app-embedded scripting interpreter) is that while you can get access to external resources (local files, remote urls etc) you can’t get direct access to other applications.

There are, of course, inter-app workarounds (much seen on iOS, to some extent on macOS too) involving custom url schemes.


The Omni applications, in particular, support a scripting exchange between applications (on both macOS and iOS) using script urls.

So am I correct in understanding the following?

  1. Inter app exchanges rely on OSA and can’t run on iOS.
  2. JXA dies when OSA dies as does Applescript.
  3. Its not possible to use two application embedded JSContexts from within a single script. e.g. an omni JS plugin can not work with TaskPaper without going via OSA.

On macOS, inter-app scripting is either:

  1. through osascript (Apple Events) or
  2. through custom URL schemes (including custom url schemes which allow for URL-encoded JS script source, as well as simple non-code values, to be passed back and forth )

On iOS etc, only the custom url scheme interchange (2. above) is possible between applications.

Yes, AppleScript and the JXA JSContext both talk to your machine and applications through the osascript interface.

See for example, the man page on the osascript command line.

They have no other conversation with applications, or with the operating system generally.

You can write interactions between two Omni JSContexts using their url-scheme, see script urls.

In that particular macOS case yes, you would need to use osascript to:

  1. send script source for evaluation to the Omni JSContext, and get values back from it, and
  2. ditto with the TaskPaper JSContext.

(The osascript component, written in AppleScript or JS for Automation, would need to be the intermediary, or, to put it another way, would provide the evaluation space in which the conversation was conducted)

I haven’t used Livecode yet. I’ll have to take a look at that.

As someone who’s worked professionally in the graphic production automation space for more than 10 years now, I find that there’s no other programming tool that fits this niche quite like AppleScript. When I worked for Starbucks last year, on their menu automation project, I tried to make a case for AppleScript on the Adobe application side, because it allowed for a lot of customization and flexibility for handling the data that was getting passed off to InDesign through XML. I wrote a custom XML parser that converted the data into labeled lists so that it could ingest that data and then act on it, based on various elements of the data. As it turns out, no one wants to use AppleScript for massive, mission-critical projects for obvious reasons. They ended up using my XML style mapping techniques for the InDesign templates (however, because there was no ability to handle the data with conditionals inside of InDesign, they had to process it all through custom-written XSLTs), but it was all driven on the back end by JavaScript in a “headless” version of InDesign server running on a PC. Mind you, building the system required a team of 5 advanced tech coders, at exorbitant salaries, which is still, well over a year later, still yet to develop a fully working system. Kanban and sprints and scrums, oh my! Bogged down in tech bureaucracy. When they eventually get it built out, it’ll work great and be way more future-proof than AppleScript, but look at the cost and time of development. I have written entire production suites for graphic production for manufacturing companies in less than 3 months that were more complicated, and then able to maintain and update the code by myself easily. It’s kind of exasperating to see these kinds of potentials with a technology and feeling like the entire industry has written it off because most people never knew how to use it to its full potential. I kind of feel like the next stage in my programming career is almost certainly to be using Python to program GIS software (my undergraduate degree is in geography). I do feel some of the same freedom in Python that I do in AppleScript, and I honestly can’t say that about most of the other options out there. I agree with you. I don’t want to be reading languages littered with punctuation. I’m also that irritating guy that complains about object orientation a lot. I’m glad I didn’t go into programming as a career per se. That year at Starbucks in all of those tech meetings turned me off to it forever.

Just a bit of correction:

  • AppleScript and JXA communicate with other applications using AppleEvents
  • osascript is a tool for interacting with AppleScript or JXA from the command line

Thanks – yes – the interface used by the osascript command line tool, with its language option set either to AppleScript or to Javascript.

The OSA interface.

(We might call it metonomy :slight_smile: )

For the command line incantation itself:

NAME
osascript — execute OSA scripts ( AppleScript, JavaScript, etc. )

SYNOPSIS
osascript [ −l language] [ −i] [ −s flags] [ −e statement | programfile] [argument ...]

DESCRIPTION
osascript executes the given OSA script, which may be plain text or a compiled script (.scpt) created by Script Editor or osacompile(1). By default, osascript treats plain text as AppleScript, but you can change this using the −l option. To get a list of the OSA languages installed on your system, use osalang(1).

I feel your pain. The fact is that the members of this forum plus a few others who use Applescript are not as valued as much as the producers of Apps to run on iOS. As Complexpoint suggests they won’t kill it but will probably just let it wither away. This thread has taught me that javascript is not a replacement for Applescript so I have no need to learn its curious ways. Ultimately the filing system can be used to exchange data, slowly, between applications with former Applescript users relying on built in macro languages, which may be javascript driven, to do limited automation.

Generally, I have never understood why so many languages aim for terseness. For example I’m struggling with an Objective-C method used in an add-in for Livecode that I am trying to write. The issue is that it uses a callback which to my mind is just an unnecessary complication that saves one or two lines of code. The joke is that the documentation supplied by Apple is equally terse requiring the reader to be in the know. I also don’t understand why there are so many different languages, nut then I’m not a programmer just a retired tester.

I’ve not tried Python yet but I have purchased some books and watched a few Youtube videos (see Python Programmer). I’m impressed by the number of tools that are available free to download. In one video the author creates a server to serve files on his local network to an old ipad, I think the code was less than ten lines long.

I’m not totally sure what counts as GIS software but I once used Livecode to write an Android application to plot mobile tracking beacons on mapping downloaded from Google Maps so I don’t know if that counts.

As Churchill would say “KBO”

S

AppleScript will always be limited option when Apple do not allow it to extend with new feature. No matter if that is from Apple or 3rd party the developers know that so why would they invest in a technology that Apple have locked.

Users who do scripts are not necessary programmer or computer engineers who could approach the task in best ways. Like everything else in life it takes practice to get knowledge in anything.

I have in the past years learn JavaScript because I’m addicted to Node-Red and embedded micro controllers. There are plenty of Automation solutions for Python and JavaScript and sometimes its interesting to learn something new instead of doing something old.

Home Assistant Core is made in Python, Node-Red use Node.js

And I do not think Apple knows how many people without programming knowledge use Apple’s frameworks who could become future Swift developers. The reason I say that many AppleScript users learned ASObjC because they needed new features that was very complicated or almost impossible to do in AS.