Barest bones of a illustrative sample script:
Simply listing all links, and selecting one or more from a bare script-generated menu.
Can be run from Script Editor, with language selector at top left to ‘JavaScript’ rather than ‘AppleScript’.
(() => {
'use strict';
ObjC.import('sqlite3');
// HOOK: SIMPLE EXAMPLE OF TABLE-READING FROM JAVASCRIPT FOR AUTOMATION
// Rob Trew 2019
const main = () => {
const multipleSelections = true;
return either(
msg => msg,
ks => {
const sa = standardSEAdditions();
return ks.map(k => (sa.openLocation(k), k)).join('\n');
},
showMenuLR(
multipleSelections,
'Hook links',
linksFromHooKDBPath(
'~/Library/Application Support/' +
'com.cogsciapps.hook/hook.sqlite'
)
)
);
};
// HOOK.APP - SIMPLEST LISTING OF LINKS VIA SQLITE
// linksFromHooKDBPath :: FilePath -> [String]
const linksFromHooKDBPath = strDBPath => {
const
SQLITE_OK = parseInt($.SQLITE_OK, 10),
SQLITE_ROW = parseInt($.SQLITE_ROW, 10),
ppDb = Ref(),
strSQL = 'SELECT src FROM link ORDER by src';
return bindLR(
bindLR(
SQLITE_OK !== $.sqlite3_open(filePath(strDBPath), ppDb) ? (
Left($.sqlite3_errmsg(ppDb[0]))
) : Right(ppDb[0]),
db => {
const ppStmt = Ref();
return SQLITE_OK !== $.sqlite3_prepare_v2(
db, strSQL, -1, ppStmt, Ref()
) ? (
Left($.sqlite3_errmsg(db))
) : Right(Tuple(db, ppStmt[0]));
}
),
// Accumulation of all available rows in the table:
tpl => unfoldr(
stmt => SQLITE_ROW !== $.sqlite3_step(stmt) ? (
$.sqlite3_finalize(stmt),
$.sqlite3_close(tpl[0]),
Nothing()
) : Just(Tuple(
$.sqlite3_column_text(stmt, 0),
stmt
)),
tpl[1]
)
);
};
// JXA ------------------------------------------------
// showMenuLR :: Bool -> String -> [String] -> Either String [String]
const showMenuLR = (blnMult, title, xs) =>
0 < xs.length ? (() => {
const sa = standardSEAdditions();
sa.activate();
const v = sa.chooseFromList(xs, {
withTitle: title,
withPrompt: 'Select' + (
blnMult ? ' one or more of ' +
xs.length.toString() : ':'
),
defaultItems: xs[0],
okButtonName: 'OK',
cancelButtonName: 'Cancel',
multipleSelectionsAllowed: blnMult,
emptySelectionAllowed: false
});
return Array.isArray(v) ? (
Right(v)
) : Left('User cancelled ' + title + ' menu.');
})() : Left(title + ': No items to choose from.');
// standardSEAdditions :: () -> Application
const standardSEAdditions = () =>
Object.assign(Application('System Events'), {
includeStandardAdditions: true
});
// GENERIC FUNCTIONS ----------------------------------
// https://github.com/RobTrew/prelude-jxa
// Just :: a -> Maybe a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
// Left :: a -> Either a b
const Left = x => ({
type: 'Either',
Left: x
});
// Nothing :: Maybe a
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
// Right :: b -> Either a b
const Right = x => ({
type: 'Either',
Right: x
});
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
const bindLR = (m, mf) =>
undefined !== m.Left ? (
m
) : mf(m.Right);
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = (fl, fr, e) =>
'Either' === e.type ? (
undefined !== e.Left ? (
fl(e.Left)
) : fr(e.Right)
) : undefined;
// filePath :: String -> FilePath
const filePath = s =>
ObjC.unwrap(ObjC.wrap(s)
.stringByStandardizingPath);
// showJSON :: a -> String
const showJSON = x => JSON.stringify(x, null, 2);
// unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
const unfoldr = (f, v) => {
let
xr = [v, v],
xs = [];
while (true) {
const mb = f(xr[1]);
if (mb.Nothing) {
return xs
} else {
xr = mb.Just;
xs.push(xr[0])
}
}
};
// MAIN ---
return main();
})();