diff --git a/.github/scripts/schema_guard.py b/.github/scripts/schema_guard.py index 4051259..4d8a184 100755 --- a/.github/scripts/schema_guard.py +++ b/.github/scripts/schema_guard.py @@ -14,9 +14,32 @@ """ import sys +import json +import re import pathlib import yaml +# Every lerd since the package layer fetches each entry of the index's +# `packages` on `lerd update` and refuses a name composer could not publish, so +# an entry that is not a composer name fails a refresh on every install. Other +# package kinds take an index key of their own, which those versions never read. +COMPOSER_NAME = re.compile(r"^[a-z0-9][a-z0-9._-]*/[a-z0-9][a-z0-9._-]*$") + + +def index_failures(candidate_dir): + path = pathlib.Path(candidate_dir) / "frameworks" / "index.json" + try: + index = json.loads(path.read_text(encoding="utf-8")) + except (OSError, ValueError): + return [] + return [ + f"frameworks/index.json: `{entry.get('name')}` under `packages` is not a composer name; " + "every lerd since the package layer fetches each entry there on update and refuses it. " + "List other package kinds under their own key, such as `npm_packages`" + for entry in index.get("packages", []) + if not COMPOSER_NAME.match(str(entry.get("name", ""))) + ] + # A key whose published values number no more than this across the whole store # is treated as a closed set, so a value outside it is worth a second look. CLOSED_SET_MAX = 8 @@ -97,7 +120,7 @@ def main(): candidate = profile(candidate_dir, pattern) enums = closed_sets(published) - failures, warnings = [], [] + failures, warnings = index_failures(candidate_dir), [] for rel, entry in published.items(): if rel not in candidate: diff --git a/README.md b/README.md index 0ca2b79..cffddd5 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,11 @@ list meaning every framework. The package wins a name collision with the version file, which is what lets an entry move here without being shadowed by the copy it left behind. List the package under `packages` in `frameworks/index.json` as `{"name": "vendor/package"}`, which is where lerd reads the set from; a file -nothing lists is never fetched. +nothing lists is never fetched. Only composer names belong there: every lerd +since the package layer fetches each entry of `packages` on `lerd update` and +refuses a name composer could not publish, so anything else, an npm package +say, gets an index key of its own that those versions never read. The schema +guard refuses a non-composer name under `packages`. A capture seam names a method the Debug window should report, for a library whose own call is the event rather than the start of one. It belongs to the @@ -263,6 +267,33 @@ removes: doctor: [horizon_supervisor] ``` +#### Browser events + +A frontend library reports what goes wrong through DOM events of its own, which +lerd's browser capture records once it knows their names. The package that ships +the library declares them in a `browser:` block, and lerd offers them as a preset +for any project that requires the package: + +```yaml +package: "@inertiajs/vue3" +type: npm # optional: composer (the default) or npm +browser: + preset: inertia # packages sharing a preset are offered as one + label: Inertia.js + events: + - event: inertia:invalid + label: Inertia invalid response + message: detail.response # where in the event the message is +``` + +An npm package lives in `packages/npm--.yaml`, its scope's `@` +dropped (`npm-inertiajs-vue3.yaml`, `npm-vite.yaml`), and is listed in the index +under `npm_packages` rather than `packages`: a lerd that predates npm packages +fetches every entry of `packages` on `lerd update` and would count an npm name +as a failure, while it never reads a key it does not know. It also reads a +composer package's file without the keys it does not know, so a `browser:` +block reaches only the versions that use it. + The copies a package was lifted out of stay in the version files that already shipped them: an install whose binary predates the package layer still reads those, and deleting them would take the worker away from it. The package file is diff --git a/frameworks/index.json b/frameworks/index.json index 62007a4..1ee55e7 100644 --- a/frameworks/index.json +++ b/frameworks/index.json @@ -190,10 +190,21 @@ {"name": "symfony/panther"}, {"name": "symfony/redis-messenger"}, {"name": "symfony/scheduler"}, + {"name": "symfony/ux-turbo"}, {"name": "tempest/command-bus"}, {"name": "tempest/database"}, {"name": "typesense/typesense-php"}, {"name": "typo3/cms-scheduler"}, {"name": "vladimir-yuldashev/laravel-queue-rabbitmq"} + ], + "npm_packages": [ + {"name": "@hotwired/turbo"}, + {"name": "@hotwired/turbo-rails"}, + {"name": "@inertiajs/inertia"}, + {"name": "@inertiajs/react"}, + {"name": "@inertiajs/svelte"}, + {"name": "@inertiajs/vue3"}, + {"name": "htmx.org"}, + {"name": "vite"} ] } diff --git a/packages/inertiajs-inertia-laravel.yaml b/packages/inertiajs-inertia-laravel.yaml index 20c697b..a32de58 100644 --- a/packages/inertiajs-inertia-laravel.yaml +++ b/packages/inertiajs-inertia-laravel.yaml @@ -15,3 +15,14 @@ workers: # exists once someone has run it. check: file: bootstrap/ssr/ssr.js + +browser: + preset: inertia + label: Inertia.js + events: + - event: inertia:invalid + label: Inertia invalid response + message: detail.response + - event: inertia:exception + label: Inertia exception + message: detail.exception.message diff --git a/packages/npm-hotwired-turbo-rails.yaml b/packages/npm-hotwired-turbo-rails.yaml new file mode 100644 index 0000000..fc2fb56 --- /dev/null +++ b/packages/npm-hotwired-turbo-rails.yaml @@ -0,0 +1,13 @@ +package: "@hotwired/turbo-rails" +type: npm + +browser: + preset: turbo + label: Hotwire Turbo + events: + - event: turbo:fetch-request-error + label: Turbo request failed + message: detail.error.message + - event: turbo:frame-missing + label: Turbo frame missing + message: detail.response.status diff --git a/packages/npm-hotwired-turbo.yaml b/packages/npm-hotwired-turbo.yaml new file mode 100644 index 0000000..080225b --- /dev/null +++ b/packages/npm-hotwired-turbo.yaml @@ -0,0 +1,13 @@ +package: "@hotwired/turbo" +type: npm + +browser: + preset: turbo + label: Hotwire Turbo + events: + - event: turbo:fetch-request-error + label: Turbo request failed + message: detail.error.message + - event: turbo:frame-missing + label: Turbo frame missing + message: detail.response.status diff --git a/packages/npm-htmx.org.yaml b/packages/npm-htmx.org.yaml new file mode 100644 index 0000000..6b24b3d --- /dev/null +++ b/packages/npm-htmx.org.yaml @@ -0,0 +1,14 @@ +package: "htmx.org" +type: npm + +browser: + preset: htmx + label: htmx + events: + - event: htmx:responseError + label: htmx error response + message: detail.xhr.status + - event: htmx:sendError + label: htmx request not sent + - event: htmx:swapError + label: htmx swap failed diff --git a/packages/npm-inertiajs-inertia.yaml b/packages/npm-inertiajs-inertia.yaml new file mode 100644 index 0000000..37efa6f --- /dev/null +++ b/packages/npm-inertiajs-inertia.yaml @@ -0,0 +1,13 @@ +package: "@inertiajs/inertia" +type: npm + +browser: + preset: inertia + label: Inertia.js + events: + - event: inertia:invalid + label: Inertia invalid response + message: detail.response + - event: inertia:exception + label: Inertia exception + message: detail.exception.message diff --git a/packages/npm-inertiajs-react.yaml b/packages/npm-inertiajs-react.yaml new file mode 100644 index 0000000..2ccbead --- /dev/null +++ b/packages/npm-inertiajs-react.yaml @@ -0,0 +1,13 @@ +package: "@inertiajs/react" +type: npm + +browser: + preset: inertia + label: Inertia.js + events: + - event: inertia:invalid + label: Inertia invalid response + message: detail.response + - event: inertia:exception + label: Inertia exception + message: detail.exception.message diff --git a/packages/npm-inertiajs-svelte.yaml b/packages/npm-inertiajs-svelte.yaml new file mode 100644 index 0000000..e2e9828 --- /dev/null +++ b/packages/npm-inertiajs-svelte.yaml @@ -0,0 +1,13 @@ +package: "@inertiajs/svelte" +type: npm + +browser: + preset: inertia + label: Inertia.js + events: + - event: inertia:invalid + label: Inertia invalid response + message: detail.response + - event: inertia:exception + label: Inertia exception + message: detail.exception.message diff --git a/packages/npm-inertiajs-vue3.yaml b/packages/npm-inertiajs-vue3.yaml new file mode 100644 index 0000000..fceb01c --- /dev/null +++ b/packages/npm-inertiajs-vue3.yaml @@ -0,0 +1,13 @@ +package: "@inertiajs/vue3" +type: npm + +browser: + preset: inertia + label: Inertia.js + events: + - event: inertia:invalid + label: Inertia invalid response + message: detail.response + - event: inertia:exception + label: Inertia exception + message: detail.exception.message diff --git a/packages/npm-vite.yaml b/packages/npm-vite.yaml new file mode 100644 index 0000000..e61cf1a --- /dev/null +++ b/packages/npm-vite.yaml @@ -0,0 +1,10 @@ +package: "vite" +type: npm + +browser: + preset: vite + label: Vite + events: + - event: vite:preloadError + label: Vite chunk failed to load + message: payload.message diff --git a/packages/symfony-ux-turbo.yaml b/packages/symfony-ux-turbo.yaml new file mode 100644 index 0000000..2944c1c --- /dev/null +++ b/packages/symfony-ux-turbo.yaml @@ -0,0 +1,12 @@ +package: symfony/ux-turbo + +browser: + preset: turbo + label: Hotwire Turbo + events: + - event: turbo:fetch-request-error + label: Turbo request failed + message: detail.error.message + - event: turbo:frame-missing + label: Turbo frame missing + message: detail.response.status