Commands, settings, SDK functions, anything in the docs.

Publish

Publishing checks

What the store checks in every upload, and how to fix what it refuses.

On this page

Every upload is checked twice: by the CLI before anything leaves your machine, and by the store, which publishes nothing until every one of its checks has passed.

Before the upload leaves your machine

developer upload stops before sending anything when:

  • ledable.json is not valid, or names a file outside the project's folder.
  • The project's @ledable/sdk is not a version this CLI builds. The message says whether to update the SDK in the project or the CLI.
  • The type check fails. It is the strict check developer check runs, and it cannot be turned off.
  • The listing does not fit the limits below: the message names each field that is too long.

developer build does all of this without uploading, so you can run it as often as you like.

What the store checks

A version number that has already passed is refused at once, with Version already published. Otherwise the store runs these checks in this order. Each one has the name the CLI prints.

  1. version: the version is newer than every version of the app that passed before.
  2. bundle_size: the built bundle is not empty and holds at most 1 MiB (1,048,576 bytes).
  3. bundle_pure: the bundle is one self-contained module that loads nothing at run time: no import or export … from, no import(), no require(). The CLI's build produces such a bundle, so this fails only when code you bundle loads modules itself.
  4. assets: every file the app ships is a .webp image or a .ledfont font that decodes, at most 4 MiB each and 8 MiB together. Its path inside the project is at most 256 characters of letters, digits, ., _ and -, and each folder or file name starts with a letter or a digit. An animated WebP must be made of frames that cover the whole image without blending, which is what images convert and the SDK write.
  5. manifest: the app_id in ledable.json is the app you uploaded to.
  6. category: ledable.json names one of the store's categories.
  7. sdk: the version was built with an @ledable/sdk the store still accepts. The check's detail names the oldest one it accepts; update the SDK in the project if yours is older.
  8. listing: the listing fits what every store client lays out (the limits are below).
  9. media: the icon and screenshots are WebP files uploaded with this version.

If all of these pass, the store runs the version once on LEDABLE's servers, the way displays will run it, with your secrets and the hosts your app declares. Each step has 10 seconds:

  1. settings: settings returns settings that every client can show. What it returns is kept with the version, and is used whenever settings fails later.
  2. preview: preview returns an image.
  3. render: one render with every setting at its default, for a 64×32 display, in English and in UTC, returns an image. A render that reports a failure, throws, or returns image: null fails this check. When a setting is required, it has no default to render with, so no render is made and the check passes with not rendered, needs and the settings' keys.
  4. credentials: if the app has a credential setting, its bundle exports verifyCredential.

If the version cannot be run at all, a single runtime check fails instead of the last four, with the reason.

Because the run is real, the services your app reads must answer while you upload, and every secret the version declares must have a value.

Listing

The store checks the listing against these limits, counted in characters. The CLI checks the same limits before it builds.

FieldLimit
name30 characters
description120 characters
The file readme names4000 characters
tags5 tags of at most 20 characters each
screenshots6 images

The fields themselves are described in The project and the ledable.json reference.

Reading the result

When every check passes, developer upload prints the published version with each check and what it found. Here it is for the example app of Accounts and credentials, shortened:

JSON
{
  "version": {
    "version": "1.0.0",
    "state": "ready",
    "bundle_size": 65366,
    "assets_size": 416625,
    "checks": [
      {
        "name": "version",
        "ok": true,
        "detail": "1.0.0 is the first version"
      },
      {
        "name": "bundle_size",
        "ok": true,
        "detail": "65366 bytes, limit 1048576"
      },
      {
        "name": "render",
        "ok": true,
        "detail": "not rendered, needs token"
      },
      {
        "name": "credentials",
        "ok": true,
        "detail": "checks token"
      }
    ],
    "created_at": 1791216000
  }
}

When a check fails, the command exits with status 1 and prints one JSON error on stderr, as every command does. Its message starts with Version failed publishing checks and then lists each check that ran, one per line, ok or FAIL with its detail:

Output
Version failed publishing checks
  FAIL version: 1.0.0 must be newer than 1.1.0
  ok   bundle_size: 65366 bytes, limit 1048576
  ok   bundle_pure: bundle must be a self-contained ESM module without imports
  ok   assets: 9 assets, 416625 bytes
  ok   manifest: GitLab To-Dos
  ok   category: productivity
  ok   sdk: @ledable/sdk 0.1.0, minimum 0.1.0
  ok   listing: fits
  ok   media: 0 listing images

When any of the first nine checks fails, the version is not run, so the last four do not appear. The store keeps every attempt: developer versions lists them with their checks, and developer status shows the failed checks of the latest upload under last_failure.

Fix and upload again

A version that failed was not published. Fix the problem and upload the same version number again; the new attempt replaces the failed one.

A version that passed is published for good and can never be uploaded again, even if you change nothing. To ship a change, raise version in ledable.json and upload that.