SideSource is a JavaScript/TypeScript framework for creating SideStore sources. It also has easy to use Cloudflare workers integration.
Web docs are available at https://sidestore.io/SideSource.
Changelog is available at https://github.com/SideStore/SideSource/blob/main/CHANGELOG.md.
Table of Contents:
apps.sidestore.io is powered by SideSource, and you can view the configs and source code at https://github.com/SideStore/SideSource/tree/main/example. It has multiple different sources for each of the release channels (stable, beta and nightly) and uses custom functions to parse GitHub releases for changelog and other info.
Warning
SideSource's caching abilities will only work on a custom domain (apex domain or subdomain will work). Caching will greatly reduce the amount of time responses take to complete. This is a limitation of Cloudflare Workers; any requests to workers at
*.workers.dev
will be unable to use the Cache API. Please see the Cloudflare Workers documentation for more info.
First, ensure that you have node.js installed. You can either download it from the official website, or if you don't want to do a system-wide install, you can use a version manager such as nvm.
Next, install wrangler, the CLI used for Cloudflare Workers:
npm install -g wrangler
If you have a Cloudflare account, you can login with it using:
wrangler login
However, this is not required until you want to deploy your worker.
Create a new worker:
wrangler init <worker name>
Use these options:
Then, cd <worker name>
.
You now have 2 options:
First, install the required dependencies:
npm i sidesource
Next, open src/index.ts
and change it to this:
import { makeSourceHandler } from "sidesource";
export default makeSourceHandler({ <config> });
This will allow SideSource to handle all requests to the worker.
Next, configure your source.
First, install the required dependencies:
npm i sidesource itty-router
Next, open src/index.ts
and change it to this:
import { Router } from "itty-router";
import { makeSourceHandler } from "sidesource";
const router = Router();
const stable = makeSourceHandler({ <stable config> });
router.all("/stable", stable.handle);
router.all("/preview/stable/:key", stable.handle);
router.all("/reset-cache/stable/:key", stable.resetCache(["/stable"])); // Make sure to include array of routes to reset cache for
// See for info on preview and reset-cache: https://sidestore.io/SideSource/#4-setting-up-a-key-for-preview-and-caching-resetting-functionality
const beta = makeSourceHandler({ <beta config> });
router.all("/beta", beta.handle);
router.all("/preview/beta/:key", beta.handle);
router.all("/reset-cache/beta/:key", beta.resetCache(["/beta"])); // Make sure to include array of routes to reset cache for
// 404 fallback (must be added last so it doesn't overwrite other routes)
router.all("*", () => new Response("404 Not Found", { status: 404 }));
export default { fetch: router.handle };
This will use itty-router to create 2 sources:
/stable
, preview at /preview/stable/<key>
, reset cache at /reset-cache/stable/<key>
/beta
, preview at /preview/beta/<key>
, reset cache at /reset-cache/beta/<key>
If you change the routes that the sources are available at, make sure to update the reset cache route with the routes that it should reset the cache for.
You can also make a source available at the base with the /
route.
The sources can have separate configs, and therefore produce 2 different sources.
Next, configure your source.
Possible values for a config are available at https://sidestore.io/SideSource/interfaces/Config.html. By default, a config expects a configURL, which is used to resolve a remote config.
Example for a config hosted in a GitHub repository:
makeSourceHandler({
configURL: "github:SideStore/SideSource/example/config/stable.json",
});
Warning
If you are not using a custom domain (aka you are not using caching), you should not use any
github:
URLs.github:
URLs resolve using GitHub's API, which is limited to 60 requests per hour without an access token, which will be exceeded very quickly without caching. Instead, use the URL that your browser goes to when viewing the "raw" contents of a file. (the raw equivalent of the above example URL would be https://github.com/SideStore/SideSource/raw/main/example/config/stable.json)
However, if you want to use the config you provide when calling makeSourceHandler
and you don't want to use a remote config, you can set remoteConfig
to false
. This is not recommended because
you will need to re-deploy your Cloudflare Worker every time you want to change the config. If you use a remote config, you can just
reset the cache to fetch the latest config.
If you haven't already, you will probably want to upload your configs as JSON files to a github repository to use as remote configs. (JSON5 is supported.) There is also a JSON schema available:
{
"$schema": "https://github.com/SideStore/SideSource/raw/main/dist/schema.json"
}
If you have multiple release channels, you can reduce duplication configuration with a base config. Base configs are specified similarly to remote configs, and the same rules apply to them.
makeSourceHandler({
configURL: "github:SideStore/SideSource/example/config/beta.json",
baseConfigURL: "github:SideStore/SideSource/example/config/stable.json",
});
If your base config has this in it:
{
"source": {
"name": "Example Source",
"identifier": "com.example"
}
}
And your normal config has this in it:
{
"source": {
"name": "Example Source 2"
}
}
It will result in this:
{
"source": {
"name": "Example Source 2",
"identifier": "com.example"
}
}
A secret key is required for the preview and caching setting functionality. It doesn't have to be very unique, but you probably don't want people knowing it because they could use it to use up the 60 github API requests you get per hour, and then cause inputs (or your whole source) to not work. A good way to get a secret key is by using a UUID, or just using the first 8 characters of one.
Now that you have your key, you need to allow the worker to see it. Create a .dev.vars
file (you will want this to be in your .gitignore):
KEY=<your key>
You also need to give it to wrangler:
echo <your key> | wrangler secret put KEY
You should now be able to use the preview and reset cache functionality.
/preview/<your key>
(the route may be different if you are using a multi source setup). This will give you the source while bypassing cache. This is a good way to test before
resetting the cache. You can tell if a source is bypassing the cache because if it is, the response will have a header named X-Skipping-Cache
./reset-cache/<your key>
(the route may be different if you are using a multi source setup). This will reset the cache. If you have a GitHub Actions workflow you use for
releasing your app, you can make a step that runs curl <source url>/reset-cache/${{ secrets.KEY }}
after uploading a release. (make sure to add your key as a secret in the github actions
settings)To locally test your source, run:
wrangler dev --local
Your worker will start running and you can use the URL it gives you to test it.
To deploy your worker, run:
wrangler publish
If you want to use a custom domain for your worker (and allow SideSource to cache results), please see Cloudflare's documentation.
Functions allow for dynamic logic that isn't possible to specify in a JSON config.
As with any function, built in functions can be used with the format function:<function name>
. Example of using makeUnc0verApp
in a custom input:
{
"type": "custom",
"functionName": "function:makeUnc0verApp"
}
This input would result in an unc0ver app being added into the source.
ipaAssetUpdatedAtToSourceDate
This function only works for GitHub input lambdas, and it parses the ipaAsset's updated_at property and turns it into a date compatible with source versions.
This function is currently the default for dateLambda
makeUnc0verApp
This function only works for custom inputs, and it parses https://unc0ver.dev/releases.json and creates an app from it.
Example functions: https://github.com/SideStore/SideSource/blob/main/example/src/functions.ts
Example usage of example functions: https://github.com/SideStore/SideSource/blob/main/example/config/stable.json#L13
You can pass your own functions as the second parameter to makeSourceHandler
and makeSource
. Example:
makeSourceHandler({
<your config>
}, {
myFunction: () => "example"
});
Then, you can use this function in properties that allow it as function:myFunction
.
Some properties will give arguments to functions it runs. For example, GitHub input lambdas will provide 2 arguments:
release
: The release object from the GitHub API.ipaAsset
: The asset object that was picked using assetRegex.Please check the documentation for more info on the specific arguments properties give.
Generated using TypeDoc