External JXA smart rule script not working - but internal works

I’ve got a simple script on DT3.8.5:

function performsmartrule(records) {
	var app = Application("DEVONthink 3");
	app.includeStandardAdditions = true;

	records.forEach (r => {
		app.openWindowFor({record: r, force: true});
	})
}

When I try to run this script with a smart rule Execute Script - External option - it fails with the error: “Error: can’t convert types”. When running Execute Script - Javascript (internal) option it works as expected. Is there any reason why this script would fail? @chrillek @cgrunenberg

EDIT: the code below seems to work as a workaround, although I don’t think this is the ‘neat’ way as I’m ignore the records parameter. This also means that the “Apply rule” action does not work (as there might be no records selected when clicking that option).

function performsmartrule(records) {
	const app = Application("DEVONthink 3");
	app.includeStandardAdditions = true;

	app.selectedRecords().forEach(r => {
		app.openWindowFor({record: r, force: true});
	});
}

Also it seems @chrillek and @cgrunenberg have discussed some of this here before: Javascript: Get Custom Metadata to rename file - #21 by cgrunenberg but I can’t find an answer to the possible problem that records is not “working” in the script above…

EDIT2: Here’s another bit of script to test in a Smart Rule as an external script. The first openWindowFor succeeds, the second one fails. It works fine as an internal Javascript with the Execute script action.

function performsmartrule(records) {
	const app = Application("DEVONthink 3");
	app.includeStandardAdditions = true;
	
	let srecords = app.selectedRecords()
	app.logMessage(srecords.length.toString());
	app.logMessage(srecords[0].name());
	app.openWindowFor({record: srecords[0], force: true});
	
	app.logMessage(records.length.toString());
	app.logMessage(records[0].name());
	app.openWindowFor({record: records[0], force: true});

}

EDIT3: Here’s a very ugly workaround for my initial problem - not sure what the performance impact is though… (besides that it’s ugly as hell):

function performsmartrule(records) {
	const app = Application("DEVONthink 3");
	app.includeStandardAdditions = true;

	records.forEach(r => {
		r = app.getRecordWithUuid(r.uuid());
		app.openWindowFor({record: r, force: true});
	});
}

I really have no idea. But I’ve seen similar problems in the past occasionally, especially with record as the first parameter. @cgrunenberg to the rescue, please.

Aside: is selectedRecords really the same as records inside performsmartrule?

And: you don’t need toString() to log numbers.

Some more detail, now that I’m back home. This

function performsmartrule(records) {
	const app = Application("DEVONthink 3");
	records.forEach(r => {
		app.openWindowFor({record: r}, { force: true});
	});
}

works fine as internal script in a smart rule. However, it again throws a “types can’t be converted” error if run as an external script. Note: I passed in the record as a separate parameter, which doesn’t do anything different from passing it in a single object with the force parameter, as you did. So far, we have

  • openWindowFor({record: r, force: true}) - works in an internal script, fails in an external one
  • openWindowFor({record: r}, {record: r, force: true}) - works in an internal script, fails in an external one

In both cases, record is the current element of the list passed into performsmartrule.

Wouldn’t it be far easier if you simply use AppleScript? *duck and cover*

5 Likes

For opening a window for a record - sure.

But then there are all the other things: array methods (missing in AS), string methods (missing in AS), scoped variables (mostly missing in AS), objects and their methods (mostly missing in AS).

You’re of course right: Every programming problem can be solved in every programming language. Provided the language and its tools are implemented correctly…

Yes, very likely. But this statement is obviously part of a bigger script which I first wrote in AppleScript before rewriting it in JS. For all the reasons @chrillek mentions the script is much better to maintain and offers much more flexibility in JS.

I do agree that unfortunately there seem to be quite a lot of idiosyncracies with JXA which definitely doesn’t make it easy. Here’s to hoping @cgrunenberg is willing to help out…

No, it definitely is not. DT gives records as input e.g. when clicking Apply Rule on a Smart Rule, which does not align with selectedRecords(). I just have no idea why records seem to adhere to some ideas of a record object (e.g. giving the expected result with r.name() but not with others e.g. the openWindowFor call.

That is correct as records matched by a smart rule (or a smart group or a search) aren’t by their nature selected.

In fact, in a smart rule we suggest you don’t use any references to selected records as a rule is expected to run headlessly and transparently under the usual conditions.

In fact, using selected records would only be useful if you were using an On Demand event trigger. In that case, you could actually select records to act on.

Well, @mdbraber only did that stuff with selectedRecords to show when the external script works. The problem here is that an internal JXA script behaves differently than an external one.

Yep, I know that. Just passing along comments and suggestions regarding his response.

Tnx for the suggestions @BLUEFROG - appreciated.

I’m now also finding out that I’m running into similar problems with app.delete({record: r}). Even after my r = app.getRecordWithUuid(r.uuid()) hack it mentions: “Error: Can’t get object”. Also app.delete(r) does not work (Error: Parameter is missing).

I’m just a bit dumfounded what exactly is happening here…

I suppose it’s all related. There were problems with methods whose first parameter is record in JXA. @cgrunenberg has fixed them, or so it seemed. There re probably some kinks left to be ironed out…

Thanks for clarifying Chris - I’ll take some weeks of for a holiday and will look into it after I get back again. Hopefully @cgrunenberg is able to find what’s causing this (or maybe better: he’s also enjoying a holiday and will only get to this later :slight_smile: )

The last issue reported by you was actually none of DEVONthink, just wrong usage/syntax in the script :slight_smile:

1 Like

External scripts are compiled & executed without any adjustments currently, internal scripts include one of the many JXA workarounds and therefore are not affected in this case.

3 Likes

Thanks for clarifying Criss. Does this mean execution of external scripts will stay as-is, or will workarounds also be implemented?

A workaround might be added but in the meantime I would recommended internal ones.

Just to confirm, I’ve been able to update my (external) JS script by using this hack. It would be great if a future update of DT would be able to make commands with {record: r} work as intended (e.g. also the delete command needed this hack) @cgrunenberg.

Thanks for thinking along!