{"id":15290829,"url":"https://github.com/japlscript/japlscript","last_synced_at":"2026-03-05T16:51:24.466Z","repository":{"id":57727420,"uuid":"349969741","full_name":"japlscript/japlscript","owner":"japlscript","description":"Less than perfect bridge from Java to AppleScript and back","archived":false,"fork":false,"pushed_at":"2024-01-04T07:55:02.000Z","size":1313,"stargazers_count":9,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2025-03-27T01:35:54.963Z","etag":null,"topics":["ant","applescript","japlscript","java","maven","script"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/japlscript.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-03-21T10:52:15.000Z","updated_at":"2024-07-14T16:13:40.000Z","dependencies_parsed_at":"2022-09-09T10:00:20.882Z","dependency_job_id":null,"html_url":"https://github.com/japlscript/japlscript","commit_stats":null,"previous_names":["hendriks73/japlscript"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/japlscript%2Fjaplscript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/japlscript%2Fjaplscript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/japlscript%2Fjaplscript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/japlscript%2Fjaplscript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/japlscript","download_url":"https://codeload.github.com/japlscript/japlscript/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248695482,"owners_count":21146956,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ant","applescript","japlscript","java","maven","script"],"created_at":"2024-09-30T16:09:38.926Z","updated_at":"2026-03-05T16:51:24.172Z","avatar_url":"https://github.com/japlscript.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![LGPL 2.1](https://img.shields.io/badge/License-LGPL_2.1-blue.svg)](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.tagtraum/japlscript/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.tagtraum/japlscript)\n[![Build and Test](https://github.com/japlscript/japlscript/workflows/Build%20and%20Test/badge.svg)](https://github.com/japlscript/japlscript/actions)\n[![CodeCov](https://codecov.io/gh/japlscript/japlscript/branch/main/graph/badge.svg?token=H98FM0SKQL)](https://codecov.io/gh/japlscript/japlscript/branch/main)\n\n\n# JaplScript\n\n*JaplScript* is an imperfect bridge layer between Java and AppleScript.\nIt was created to serve a specific purpose and not to be a grand powerful library.\n\nThe overall approach is to\n\n1) Read `.sdef` files (exported with macOS's *Script Editor*).\n2) Generate annotated Java interfaces and enumerations for the defined AppleScript classes.\n3) Compile the interfaces/enums before runtime.\n4) Use them just like Java objects.\n\n\n## Installation\n               \nJaplScript is released via [Maven](https://maven.apache.org).\nYou can install it via the following dependency:\n\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.tagtraum\u003c/groupId\u003e\n        \u003cartifactId\u003ejaplscript-runtime\u003c/artifactId\u003e\n    \u003c/dependency\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.tagtraum\u003c/groupId\u003e\n        \u003cartifactId\u003ejaplscript-generator\u003c/artifactId\u003e\n        \u003c!-- the generator is not necessary during runtime --\u003e\n        \u003cscope\u003eprovided\u003c/scope\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n## Ant-based Interface Generation\n\nThe generator class is implemented as [Ant](https://ant.apache.org) task,\nso you can use it from any Ant file like this:\n\n```xml\n\u003cproject default=\"generate.interfaces\"\u003e\n    \u003ctarget name=\"generate.interfaces\"\u003e\n        \u003ctaskdef name=\"japlscript\"\n                 classname=\"com.tagtraum.japlscript.generation.GeneratorAntTask\"\n                 classpathref=\"your.reference\"/\u003e\n        \u003cjaplscript application=\"Music\"\n                    sdef=\"Music.sdef\"\n                    out=\"src/generated-sources\"\n                    packagePrefix=\"com.apple.music\"\u003e\n            \u003cexcludeclass name=\"rgb color\"/\u003e\n        \u003c/japlscript\u003e\n    \u003c/target\u003e\n\u003c/project\u003e\n```\n\nThe attribute `application` describes the application's name as used in a\nregular AppleScript `tell` command (which implies you can also use the bundle\nname).\n\nNote that the sample above uses an `\u003cexcludeclass/\u003e` tag, which simply means that\nJaplScript should not generate a Java interface for the given AppleScript \nclass or type (in this example: `rgb color`).\n\n                  \n## Maven-based Interface Generation\n\nFrom Maven, you can run a suitable Ant file using the\n[maven-antrun-plugin](https://maven.apache.org/plugins/maven-antrun-plugin/). If you do so,\nand have declared JaplScript as a dependency, you can set `classpathref=\"maven.compile.classpath\"`\nwhen you define the `japlscript` code generator task.\n\nSample Ant file `japlscript.xml`:\n\n```xml\n\u003cproject default=\"generate.interfaces\"\u003e\n    \u003ctarget name=\"generate.interfaces\"\u003e\n        \u003ctaskdef name=\"japlscript\"\n                 classname=\"com.tagtraum.japlscript.generation.GeneratorAntTask\"\n                 classpathref=\"maven.compile.classpath\"/\u003e\n        \u003cjaplscript application=\"Music\"\n                    sdef=\"Music.sdef\"\n                    out=\"${project.build.directory}/generated-sources/main/java\"\n                    packagePrefix=\"com.apple.music\"\u003e\n            \u003cexcludeclass name=\"rgb color\"/\u003e\n        \u003c/japlscript\u003e\n    \u003c/target\u003e\n\u003c/project\u003e\n```\n\nSample Maven `pom.xml` excerpt:\n\n```xml\n\u003cplugin\u003e\n    \u003cartifactId\u003emaven-antrun-plugin\u003c/artifactId\u003e\n    \u003cexecutions\u003e\n        \u003cexecution\u003e\n            \u003cconfiguration\u003e\n                \u003ctarget\u003e\n                    \u003c!--\n                    pass project.build.directory to ant, so you can use it when\n                    specifying the output folder, which could be\n                    ${project.build.directory}/generated-sources/main/java \n                     --\u003e\n                    \u003cproperty name=\"project.build.directory\" value=\"${project.build.directory}\" /\u003e\n                    \u003cant antfile=\"japlscript.xml\" inheritRefs=\"true\" /\u003e\n                \u003c/target\u003e\n                \u003csourceRoot\u003e${project.build.directory}/generated-sources/main/java\u003c/sourceRoot\u003e\n            \u003c/configuration\u003e\n            \u003cphase\u003egenerate-sources\u003c/phase\u003e\n            \u003cgoals\u003e\n                \u003cgoal\u003erun\u003c/goal\u003e\n            \u003c/goals\u003e\n        \u003c/execution\u003e\n    \u003c/executions\u003e\n\u003c/plugin\u003e\n```\n\n## Custom Type Mappings                                   \n\nTo introduce custom mappings from AppleScript classes to your own classes,\nyou can use the `\u003ctypemapping/\u003e` tag in your Ant file, for example:\n                                    \n```xml\n\u003cproject default=\"generate.interfaces\"\u003e\n    \u003ctarget name=\"generate.interfaces\"\u003e\n        \u003ctaskdef name=\"japlscript\"\n                 classname=\"com.tagtraum.japlscript.generation.GeneratorAntTask\"\n                 classpathref=\"maven.compile.classpath\"/\u003e\n        \u003cjaplscript application=\"Music\"\n                    sdef=\"Music.sdef\"\n                    out=\"${project.build.directory}/generated-sources/main/java\"\n                    packagePrefix=\"com.apple.music\"\u003e\n            \n            \u003c!-- mapping from \"file\" to \"com.apple.finder.File\" --\u003e\n            \u003ctypemapping applescript=\"file\" java=\"com.apple.finder.File\"/\u003e\n            \n        \u003c/japlscript\u003e\n    \u003c/target\u003e\n\u003c/project\u003e\n```\n\nNote that your custom Java types should implement the interface\n[Codec\u003cT\u003e](https://japlscript.github.io/japlscript/com/tagtraum/japlscript/Codec.html) to support encoding/decoding from\nan AppleScript object (specifier).\n\nIf your custom type is not a primitive, you probably also want to\nimplement the [Reference](https://japlscript.github.io/japlscript/com/tagtraum/japlscript/Reference.html)\ninterface.\n\n\n## Scripting Additions\n\nTo generate Java APIs for scripting additions, set the `scriptingAddition`\nattribute to `true`. Example:\n\n```xml\n\u003cproject default=\"generate.interfaces\"\u003e\n    \u003ctarget name=\"generate.interfaces\"\u003e\n        \u003ctaskdef name=\"japlscript\"\n                 classname=\"com.tagtraum.japlscript.generation.GeneratorAntTask\"\n                 classpathref=\"maven.compile.classpath\"/\u003e\n        \u003cjaplscript application=\"StandardAdditions\"\n                    scriptingAddition=\"true\"\n                    sdef=\"StandardAdditions.sdef\"\n                    out=\"${project.build.directory}/generated-sources/main/java\"\n                    packagePrefix=\"com.apple.macos\"/\u003e\n    \u003c/target\u003e\n\u003c/project\u003e\n```\n\nNote that typically the main class for an application is aptly named `Application.class`.\nFor scripting additions that is not the case—they are called `ScriptingAddition.class`\ninstead.\n\n\n## Usage\n\n### Getting Started...\n                           \nTo use the generated code, do something like this:\n\n```java\n// if you have generated classes for the Music.app\ncom.apple.music.Application app = com.apple.music.Application.getInstance();\n\n// then use app, for example, toggle playback (if a track is in the player)\napp.playpause();\n```\n\n### AppleScript Type System Support\n\nEvery JaplScript object that refers to an AppleScript counterpart\nimplements the interface [Reference](https://japlscript.github.io/japlscript/com/tagtraum/japlscript/Reference.html).\nAs such, you can `\u003cT\u003e T cast(java.lang.Class\u003cT\u003e klass)` an object to another\nJava type that in turn corresponds to another AppleScript type. Note that\ntype checks may be lazy, i.e. you might not get an exception right away, should\nthe cast not work.\n\nIf you want to check, whether a cast would be legitimate, you can call\n`boolean isInstanceOf(TypeClass typeClass)`. A\n[TypeClass](https://japlscript.github.io/japlscript/com/tagtraum/japlscript/language/TypeClass.html)\nis the Java-side pendant for an AppleScript class. Each of the generated interfaces\nexposes its `TypeClass` via it `CLASS` field. For example, if you have an instance of\nJava-interface `Track`, you can access `Track.CLASS` to retrieve its AppleScript type.\nThis means, you could ask an instance of `Track` whether its also an instance of the\nsub-class `FileTrack`:\n\n```java\nApplication application = Application.getInstance();\nTrack track = application.getCurrrentTrack();\n// check, whether the AppleScript object references by track\n// is actually a FileTrack and not just a Track. \nif (track.isInstanceOf(FileTrack.CLASS)) {\n    // cast the track Java instance to FileTrack. \n    FileTrack fileTrack = track.cast(FileTrack.class);\n    ...\n}\n```\n\nImplicitly, `isInstanceof(..)` uses the method `TypeClass getTypeClass()`, which\nlets you find out the actual type of the referenced AppleScript object. This could be\na subtype of the interface you are currently using.\n\nNote that using the AppleScript type system support is not always necessary.\nOftentimes, the regular Java type system works just as well (see example below),\nbut note that there is no strict guarantee.\n\n```java\nApplication application = Application.getInstance();\nTrack track = application.getCurrrentTrack();\nif (track instanceof FileTrack) {\n    FileTrack fileTrack = (FileTrack)track;\n    ...\n}\n```\n\n\n### Accessing Elements/Collections\n\nIn AppleScript, objects can have properties and elements. Elements are really just\ncollections, which can be accessed in JaplScript via generated methods.\nLet's assume you have a `PlayList` instance, which has a `Track` elements. Then\nJaplScript will generate the following standard methods:\n\n```java\nimport com.tagtraum.japlscript.Id;\n\npublic interface Playlist extends com.tagtraum.japlscript.Reference {\n\n    /**\n     * @return an array of all {@link Track}s\n     */\n    default Track[] getTracks() {\n        return getTracks(null);\n    }\n\n    /**\n     * @param filter AppleScript filter clause without the leading \u0026quot;whose\u0026quot; or \u0026quot;where\u0026quot;\n     * @return an array of all {@link Track}s\n     */\n    Track[] getTracks(java.lang.String filter);\n\n    /**\n     * @param index index into the element list (zero-based)\n     * @return the {@link Track} at the requested index\n     */\n    Track getTrack(int index);\n\n    /**\n     * @param id id of the item\n     * @return the {@link Track} with the requested id\n     */\n    Track getTrack(Id id);\n\n    /**\n     * @return number of all {@link Track}s\n     */\n    default int countTracks() {\n        return countTracks(null);\n    }\n\n    /**\n     * @param filter AppleScript filter clause without the leading \u0026quot;whose\u0026quot; or \u0026quot;where\u0026quot;\n     * @return the number of elements that pass the filter\n     */\n    int countTracks(String filter);\n}\n```\n\nThey will let you count the tracks and access them in bulk, by zero-based index and by id.\nAdditionally, they let you specify *filters*. These are just little AppleScript\nsnippets that you would usually use in an AppleScript `where` clause.\n\nFor example:\n\n```java\nint count = playlist.countTracks(\"year \u003e 1984\");\n```\n\nThis snippet counts all the tracks in the given playlist that have a year\ngreater than `1984`. Note that this assumes that the `Track` instance has a `year`\nproperty (AppleScript property name, not Java property name!).\nSimilar filters can be used in the other provided methods.\n\nNote that you have to pass well-formed AppleScripts, i.e., if you want to filter\nby a string value, you have to properly quote the string.\n\nFor example:\n\n```java\nint count = playlist.countTracks(\"persistent ID = \\\"0123456789abcde\\\"\");\n```\n\n\n### Creating new Objects\n\nCreating new AppleScript objects is sometimes not as straightforward as one might\nwish. For example, to create a new playlist in the Apple Music app (or iTunes),\nyou would use the application's `make()` command.\n\n```java\nApplication application = Application.getInstance();\nUserPlaylist userPlaylist = getApplication().make(UserPlaylist.class);\n```\n\nNote that using the Java class here is just a convenience. If you want to\nspecify additional arguments, like a parent playlist of folder, you would have\nto write something like this:\n\n```java\nReference reference = application.make(UserPlaylist.CLASS, someParentPlaylist, null);\nUserPlaylist userPlaylist = reference.cast(UserPlaylist.CLASS);\n```\n\n### Bulk Accessing Properties\n\nEvery JaplScript object has a method `java.util.Map\u003cString, Object\u003e getProperties()`,\nwhich lets you retrieve the object's properties in a convenient `java.util.Map`.\nNote that the keys correspond to the Java property names. The advantage of\nusing `getProperties()` instead of individually accessing properties one by\none is efficiency, since fewer AppleScript calls are needed.\n\n\n### Sessions\n\nWhen calling multiple setters in a row, JaplScript will translate each call\nto an AppleScript snippet and execute it. This of course is inefficient. It may make\nmore sense to first collect a bunch of calls and then execute them all at once.\nYou can achieve this kind of behavior by starting a [Session](https://japlscript.github.io/japlscript/com/tagtraum/japlscript/execution/Session.html):\n\n```java\nimport com.tagtraum.japlscript.execution.Session;\n\n[...]\n\nApplication application = Application.getInstance();\n// start session for the current thread\nSession session = Session.startSession();\n// call some setters\napplication.setThis(\"this\");\napplication.setThat(\"that\");\napplication.setOther(\"other\");\n// call commit in order to execute the combined AppleScript snippets\nsession.commit();\n```\n\n### Logging\n\nJaplScript uses `java.util.logging`. In order to see what scripts are being executed and when,\njust dial up the log level.\n\n\n### Artificial References\n\nUsually you will be able to obtain Java objects for your AppleScript objects\nusing the generated interfaces and their methods. But sometimes this can be awkward\nand you much rather just want to use an AppleScript snippet. This can easily be done\nby using a generic [ReferenceImpl](https://japlscript.github.io/japlscript/com/tagtraum/japlscript/language/ReferenceImpl.html).\n\nTo do so you have to understand that each `Reference` consists of two parts:\n\n1. An object reference, describing an object within an application's context\n2. An Application reference, describing the application context\n\nSo to create a Java object for an arbitrary AppleScript object, you can simply do\nsomething like this:\n\n```java\nApplication application = Application.getInstance();\nfinal String objectReference = \"(first source where kind is library)\";\nReference reference = new ReferenceImpl(objectReference, application.getApplicationReference());\n// cast to the Java interface that you know fits\nSource librarySource = reference.cast(Source.class); \n```\n\nThe snippet above allows you to create a Java instance for the first library source of\nsome application (think *Music.app* or *iTunes*) without executing a single line of\nAppleScript. Obviously, `objectReference` could also be some other random snippet\nof AppleScript that returns some object.\n\n\n## Sample Projects\n\n- [JaplSA](https://github.com/japlscript/japlsa) - Java API for AppleScript Standard Additions\n- [JaplSE](https://github.com/japlscript/japlse) - Java API for AppleScript System Events\n- [Japlphoto](https://github.com/japlscript/japlphoto) - Java API for Apple's Photos app\n- [Japlfind](https://github.com/japlscript/japlfind) - Java API for Apple's Finder app\n- [Japlcontact](https://github.com/japlscript/japlcontact) - Java API for Apple's Contacts app\n- [Obstunes](https://github.com/japlscript/obstunes) - Java API for iTunes \n- [Obstmusic](https://github.com/japlscript/obstmusic) - Java API for Apple's Music app\n- [Obstspot](https://github.com/japlscript/obstspot) - Java API for the Spotify app\n\nHave you generated an API stored in your repository? Open a PR to list it here.\n\nWant to have your API repository listed under https://github.com/japlscript, consider\ntransferring ownership to the *japlscript* GitHub organization.\n\n                \n## Java Module\n\nJaplScript is shipped as a Java module\n(see [JPMS](https://en.wikipedia.org/wiki/Java_Platform_Module_System))\nwith the name `tagtraum.japlscript`.\n                                  \nNote that module support is also possible for the generated code.\nIf you specify a module name during generation, the generated code will also\nbe a module. For example:\n\n```xml\n\u003cproject default=\"generate.interfaces\"\u003e\n    \u003ctarget name=\"generate.interfaces\"\u003e\n        \u003ctaskdef name=\"japlscript\"\n                 classname=\"com.tagtraum.japlscript.generation.GeneratorAntTask\"\n                 classpathref=\"maven.compile.classpath\"/\u003e\n        \u003cjaplscript application=\"Music\"\n                    module=\"tagtraum.music\"\n                    sdef=\"Music.sdef\"\n                    out=\"${project.build.directory}/generated-sources/main/java\"\n                    packagePrefix=\"com.apple.music\"/\u003e\n    \u003c/target\u003e\n\u003c/project\u003e\n```\n\nThis will create an appropriate `module-info.java` file exporting the module\nnamed `tagtraum.music`.\n\nNote that the generator requires Ant, which has not yet transitioned\nto modules, which may lead to problems. \n               \n\n## AppleScript Sandbox\n\nSince macOS 10.14 (Mojave), Apple imposed a sandbox on AppleScript. Therefore\nyou may see dialog boxes requesting authorization to perform certain actions.\nAfter a while, these boxes simply disappear and there does not seem to be an easy\nway to authorize your app. In this case, you need to open the system preferences,\nnavigate to *Security \u0026 Privacy*, *Privacy*, and then *Automation*, and make\nsure your app is allowed to remote control whatever app you are trying to remote\ncontrol (see also [this article](https://blog.beatunes.com/2018/10/beatunes-on-mojave-and-windows-10-dark.html)).\n\nIf you are shipping a real app with a UI and not just a command line tool, you\nneed to customize the sandbox permission dialog. You can do so by adding\nthe key `NSAppleEventsUsageDescription` to your app bundle's `/Contents/Info.plist`\nfile. For example: \n       \n    [...]\n    \u003ckey\u003eNSAppleEventsUsageDescription\u003c/key\u003e\n    \u003cstring\u003eSuperMusic uses AppleEvents to access your Music.app library,\n            e.g., to set BPM values or create playlists.\u003c/string\u003e\n    [...]\n\nApple's documentation for the keyword is [here](https://developer.apple.com/documentation/bundleresources/information_property_list/nsappleeventsusagedescription).\n                                    \n\n## Notarization and Hardened Runtime                    \n\nIf you would like to [notarize](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution)\nyour app, you must enable macOS's\n[Hardened Runtime](https://developer.apple.com/documentation/security/hardened_runtime).\nThis also means that applications that want to send Apple Events to other\napplications (automation) must be signed with the\n[`com.apple.security.automation.apple-events`](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_automation_apple-events)\nentitlement.\n\nHere's a sample `entitlements.plist` file:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003c!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\"\n        \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"\u003e\n\u003cplist version=\"1.0\"\u003e\n\u003cdict\u003e\n    \u003ckey\u003ecom.apple.security.automation.apple-events\u003c/key\u003e\n    \u003ctrue/\u003e\n\u003c/dict\u003e\n\u003c/plist\u003e\n```\n\nWhich is then used in your `codesign` call:\n\n```bash\ncodesign --entitlements entitlements.plist --options runtime \\\n   --deep -vvv -f --sign \"Developer ID Application: YOUR NAME\" Your.app\n```\n\n\n## Known Shortcomings\n\nNote that the generated interfaces may not always be perfect. This is especially\ntrue for complex AppleScript types and the cardinality of command return types.\nIn some cases, you may need to fix the generated Java interface manually\n(e.g. the cardinality of the return type of the Music.app's `search`-command).\n\nThere are also issues with generating *all* possible versions of overloaded\nAppleScript commands.\n\nAnt really should not be necessary during generation. Instead a simple Maven\nplugin should do the job.\n\n\n## API\n\nYou can find the complete [API here](https://japlscript.github.io/japlscript/).\n\n\n## Additional Resources\n\n- [AppleScript Language Guide](https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html)\n- [Raw AppleScript Event Codes](https://gist.github.com/ccstone/955a0461d0ba02289b0cef469862ec84)\n ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaplscript%2Fjaplscript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaplscript%2Fjaplscript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaplscript%2Fjaplscript/lists"}