@storyshelf/app 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -4
- package/dist/app-types.d.mts +1 -3
- package/dist/asset-manifest.mjs +13 -5
- package/dist/asset-manifest.mjs.map +1 -1
- package/dist/assets/{htmx.min.js → htmx.mjs} +10 -1
- package/dist/assets/htmx.mjs.map +1 -0
- package/dist/assets/mark.mjs +10 -0
- package/dist/assets/mark.mjs.map +1 -0
- package/dist/boot-tasks.mjs +80 -0
- package/dist/boot-tasks.mjs.map +1 -0
- package/dist/build-state-flags.mjs +18 -0
- package/dist/build-state-flags.mjs.map +1 -0
- package/dist/build-state-setup.mjs +75 -0
- package/dist/build-state-setup.mjs.map +1 -0
- package/dist/capture-alerts.mjs +46 -0
- package/dist/capture-alerts.mjs.map +1 -0
- package/dist/capture-process.mjs +59 -0
- package/dist/capture-process.mjs.map +1 -0
- package/dist/capture-setup.mjs +95 -117
- package/dist/capture-setup.mjs.map +1 -1
- package/dist/connections.mjs +34 -0
- package/dist/connections.mjs.map +1 -0
- package/dist/consumer-setup.mjs +58 -0
- package/dist/consumer-setup.mjs.map +1 -0
- package/dist/credentials.mjs +3 -2
- package/dist/credentials.mjs.map +1 -1
- package/dist/git-status.mjs +46 -0
- package/dist/git-status.mjs.map +1 -0
- package/dist/hidden-data.mjs +24 -0
- package/dist/hidden-data.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +202 -124
- package/dist/index.mjs.map +1 -1
- package/dist/insights/budget.mjs +49 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/deps.mjs +21 -0
- package/dist/insights/deps.mjs.map +1 -0
- package/dist/insights/errors.mjs +22 -0
- package/dist/insights/errors.mjs.map +1 -0
- package/dist/insights/health-panel.mjs +40 -0
- package/dist/insights/health-panel.mjs.map +1 -0
- package/dist/insights/job.mjs +103 -0
- package/dist/insights/job.mjs.map +1 -0
- package/dist/insights/load-health.mjs +59 -0
- package/dist/insights/load-health.mjs.map +1 -0
- package/dist/insights/load-triage.mjs +98 -0
- package/dist/insights/load-triage.mjs.map +1 -0
- package/dist/insights/panel.mjs +28 -0
- package/dist/insights/panel.mjs.map +1 -0
- package/dist/insights/request.mjs +113 -0
- package/dist/insights/request.mjs.map +1 -0
- package/dist/insights/stale.mjs +21 -0
- package/dist/insights/stale.mjs.map +1 -0
- package/dist/insights/usage-report.mjs +60 -0
- package/dist/insights/usage-report.mjs.map +1 -0
- package/dist/insights/view.mjs +48 -0
- package/dist/insights/view.mjs.map +1 -0
- package/dist/job-canceller.mjs +14 -0
- package/dist/job-canceller.mjs.map +1 -0
- package/dist/job-queue.mjs +51 -0
- package/dist/job-queue.mjs.map +1 -0
- package/dist/jobs-setup.mjs +132 -0
- package/dist/jobs-setup.mjs.map +1 -0
- package/dist/lifecycle.mjs +109 -19
- package/dist/lifecycle.mjs.map +1 -1
- package/dist/middleware/auth-gate.mjs +4 -2
- package/dist/middleware/auth-gate.mjs.map +1 -1
- package/dist/middleware/rate-limit.mjs +41 -33
- package/dist/middleware/rate-limit.mjs.map +1 -1
- package/dist/middleware/request-log.mjs +3 -2
- package/dist/middleware/request-log.mjs.map +1 -1
- package/dist/middleware/require-review.mjs +51 -0
- package/dist/middleware/require-review.mjs.map +1 -0
- package/dist/middleware/require-testing.mjs +41 -0
- package/dist/middleware/require-testing.mjs.map +1 -0
- package/dist/middleware/setup-gate.mjs.map +1 -1
- package/dist/middleware/store-scope.mjs +12 -2
- package/dist/middleware/store-scope.mjs.map +1 -1
- package/dist/notifications-pause.mjs +33 -0
- package/dist/notifications-pause.mjs.map +1 -0
- package/dist/notify.d.mts +2 -1
- package/dist/notify.mjs +78 -39
- package/dist/notify.mjs.map +1 -1
- package/dist/pages/a11y-violations.mjs +116 -0
- package/dist/pages/a11y-violations.mjs.map +1 -0
- package/dist/pages/admin-ai.mjs +78 -0
- package/dist/pages/admin-ai.mjs.map +1 -0
- package/dist/pages/admin-system-origin.mjs +123 -0
- package/dist/pages/admin-system-origin.mjs.map +1 -0
- package/dist/pages/admin-system-targets.mjs +53 -0
- package/dist/pages/admin-system-targets.mjs.map +1 -0
- package/dist/pages/admin-system.mjs +63 -13
- package/dist/pages/admin-system.mjs.map +1 -1
- package/dist/pages/build-detail.mjs +104 -76
- package/dist/pages/build-detail.mjs.map +1 -1
- package/dist/pages/build-diff-actions.mjs +87 -33
- package/dist/pages/build-diff-actions.mjs.map +1 -1
- package/dist/pages/build-diff-comments.mjs +16 -14
- package/dist/pages/build-diff-comments.mjs.map +1 -1
- package/dist/pages/build-diff-environment.mjs +82 -0
- package/dist/pages/build-diff-environment.mjs.map +1 -0
- package/dist/pages/build-diff-header.mjs +22 -4
- package/dist/pages/build-diff-header.mjs.map +1 -1
- package/dist/pages/build-diff-insight.mjs +113 -0
- package/dist/pages/build-diff-insight.mjs.map +1 -0
- package/dist/pages/build-diff-model.mjs +55 -10
- package/dist/pages/build-diff-model.mjs.map +1 -1
- package/dist/pages/build-diff-nav.mjs +28 -12
- package/dist/pages/build-diff-nav.mjs.map +1 -1
- package/dist/pages/build-diff-shortcuts.mjs +5 -1
- package/dist/pages/build-diff-shortcuts.mjs.map +1 -1
- package/dist/pages/build-diff-usage.mjs +63 -0
- package/dist/pages/build-diff-usage.mjs.map +1 -0
- package/dist/pages/build-diff-viewer.mjs +62 -5
- package/dist/pages/build-diff-viewer.mjs.map +1 -1
- package/dist/pages/build-diff.mjs +34 -13
- package/dist/pages/build-diff.mjs.map +1 -1
- package/dist/pages/build-tests.mjs +74 -0
- package/dist/pages/build-tests.mjs.map +1 -0
- package/dist/pages/component-detail.mjs +249 -0
- package/dist/pages/component-detail.mjs.map +1 -0
- package/dist/pages/component-stories.mjs +168 -0
- package/dist/pages/component-stories.mjs.map +1 -0
- package/dist/pages/compute-jobs.mjs +13 -26
- package/dist/pages/compute-jobs.mjs.map +1 -1
- package/dist/pages/design-links.mjs +182 -0
- package/dist/pages/design-links.mjs.map +1 -0
- package/dist/pages/environment-change.mjs +53 -0
- package/dist/pages/environment-change.mjs.map +1 -0
- package/dist/pages/health-panel.mjs +126 -0
- package/dist/pages/health-panel.mjs.map +1 -0
- package/dist/pages/library-grid.mjs +105 -0
- package/dist/pages/library-grid.mjs.map +1 -0
- package/dist/pages/library-model.mjs +47 -0
- package/dist/pages/library-model.mjs.map +1 -0
- package/dist/pages/library-snapshots.mjs +130 -0
- package/dist/pages/library-snapshots.mjs.map +1 -0
- package/dist/pages/library.mjs +112 -218
- package/dist/pages/library.mjs.map +1 -1
- package/dist/pages/notifications-paused.mjs +22 -0
- package/dist/pages/notifications-paused.mjs.map +1 -0
- package/dist/pages/profile.mjs +53 -48
- package/dist/pages/profile.mjs.map +1 -1
- package/dist/pages/project-builds-table.mjs +2 -2
- package/dist/pages/project-builds-table.mjs.map +1 -1
- package/dist/pages/project-builds.mjs +51 -21
- package/dist/pages/project-builds.mjs.map +1 -1
- package/dist/pages/project-settings.mjs +51 -12
- package/dist/pages/project-settings.mjs.map +1 -1
- package/dist/pages/projects-card.mjs +14 -1
- package/dist/pages/projects-card.mjs.map +1 -1
- package/dist/pages/projects.mjs +6 -2
- package/dist/pages/projects.mjs.map +1 -1
- package/dist/pages/review-changeset.mjs +116 -0
- package/dist/pages/review-changeset.mjs.map +1 -0
- package/dist/pages/review-comments.mjs +103 -0
- package/dist/pages/review-comments.mjs.map +1 -0
- package/dist/pages/review-verdict.mjs +102 -0
- package/dist/pages/review-verdict.mjs.map +1 -0
- package/dist/pages/settings-ai.mjs +69 -0
- package/dist/pages/settings-ai.mjs.map +1 -0
- package/dist/pages/settings-capture.mjs +124 -0
- package/dist/pages/settings-capture.mjs.map +1 -0
- package/dist/pages/settings-design-credential.mjs +99 -0
- package/dist/pages/settings-design-credential.mjs.map +1 -0
- package/dist/pages/settings-design-links.mjs +179 -0
- package/dist/pages/settings-design-links.mjs.map +1 -0
- package/dist/pages/settings-design.mjs +48 -0
- package/dist/pages/settings-design.mjs.map +1 -0
- package/dist/pages/settings-general.mjs +46 -21
- package/dist/pages/settings-general.mjs.map +1 -1
- package/dist/pages/settings-labels.mjs +43 -2
- package/dist/pages/settings-labels.mjs.map +1 -1
- package/dist/pages/settings-links.mjs +112 -0
- package/dist/pages/settings-links.mjs.map +1 -0
- package/dist/pages/settings-notifications-git.mjs +128 -0
- package/dist/pages/settings-notifications-git.mjs.map +1 -0
- package/dist/pages/settings-notifications.mjs +79 -42
- package/dist/pages/settings-notifications.mjs.map +1 -1
- package/dist/pages/settings-tests.mjs +33 -2
- package/dist/pages/settings-tests.mjs.map +1 -1
- package/dist/pages/target-choices.mjs +39 -0
- package/dist/pages/target-choices.mjs.map +1 -0
- package/dist/publish-setup.mjs +89 -0
- package/dist/publish-setup.mjs.map +1 -0
- package/dist/retention-timer.mjs +2 -1
- package/dist/retention-timer.mjs.map +1 -1
- package/dist/review-events.mjs +48 -0
- package/dist/review-events.mjs.map +1 -0
- package/dist/routers/admin-pages.mjs +45 -3
- package/dist/routers/admin-pages.mjs.map +1 -1
- package/dist/routers/admin-public-origin.mjs +85 -0
- package/dist/routers/admin-public-origin.mjs.map +1 -0
- package/dist/routers/assets.mjs +9 -2
- package/dist/routers/assets.mjs.map +1 -1
- package/dist/routers/auth.mjs +2 -2
- package/dist/routers/auth.mjs.map +1 -1
- package/dist/routers/baseline-guard.mjs +2 -2
- package/dist/routers/baseline-guard.mjs.map +1 -1
- package/dist/routers/baselines.handlers.mjs +64 -0
- package/dist/routers/baselines.handlers.mjs.map +1 -0
- package/dist/routers/baselines.mjs +125 -0
- package/dist/routers/baselines.mjs.map +1 -0
- package/dist/routers/build-cancel.mjs +68 -0
- package/dist/routers/build-cancel.mjs.map +1 -0
- package/dist/routers/build-changeset.mjs +70 -0
- package/dist/routers/build-changeset.mjs.map +1 -0
- package/dist/routers/build-reviews.handlers.mjs +86 -0
- package/dist/routers/build-reviews.handlers.mjs.map +1 -0
- package/dist/routers/build-reviews.mjs +74 -0
- package/dist/routers/build-reviews.mjs.map +1 -0
- package/dist/routers/builds.handlers.mjs +46 -55
- package/dist/routers/builds.handlers.mjs.map +1 -1
- package/dist/routers/builds.mjs +71 -17
- package/dist/routers/builds.mjs.map +1 -1
- package/dist/routers/capture-config.mjs +35 -0
- package/dist/routers/capture-config.mjs.map +1 -0
- package/dist/routers/comments.mjs +41 -15
- package/dist/routers/comments.mjs.map +1 -1
- package/dist/routers/component-catalog.handlers.mjs +71 -0
- package/dist/routers/component-catalog.handlers.mjs.map +1 -0
- package/dist/routers/component.handlers.mjs +121 -0
- package/dist/routers/component.handlers.mjs.map +1 -0
- package/dist/routers/connections.mjs +118 -0
- package/dist/routers/connections.mjs.map +1 -0
- package/dist/routers/design-connect.handlers.mjs +44 -0
- package/dist/routers/design-connect.handlers.mjs.map +1 -0
- package/dist/routers/design.handlers.mjs +114 -0
- package/dist/routers/design.handlers.mjs.map +1 -0
- package/dist/routers/health-report.mjs +2 -3
- package/dist/routers/health-report.mjs.map +1 -1
- package/dist/routers/health.mjs +4 -3
- package/dist/routers/health.mjs.map +1 -1
- package/dist/routers/helpers.mjs +19 -3
- package/dist/routers/helpers.mjs.map +1 -1
- package/dist/routers/insights-ui.mjs +64 -0
- package/dist/routers/insights-ui.mjs.map +1 -0
- package/dist/routers/insights.handlers.mjs +45 -0
- package/dist/routers/insights.handlers.mjs.map +1 -0
- package/dist/routers/insights.mjs +189 -0
- package/dist/routers/insights.mjs.map +1 -0
- package/dist/routers/links.handlers.mjs +80 -0
- package/dist/routers/links.handlers.mjs.map +1 -0
- package/dist/routers/links.mjs +139 -0
- package/dist/routers/links.mjs.map +1 -0
- package/dist/routers/media.mjs +18 -4
- package/dist/routers/media.mjs.map +1 -1
- package/dist/routers/members.mjs +2 -1
- package/dist/routers/members.mjs.map +1 -1
- package/dist/routers/notification-channels.mjs +17 -23
- package/dist/routers/notification-channels.mjs.map +1 -1
- package/dist/routers/oauth-state.mjs +45 -0
- package/dist/routers/oauth-state.mjs.map +1 -0
- package/dist/routers/preview-base.mjs +38 -0
- package/dist/routers/preview-base.mjs.map +1 -0
- package/dist/routers/preview-token.mjs +51 -0
- package/dist/routers/preview-token.mjs.map +1 -0
- package/dist/routers/profile.mjs +4 -2
- package/dist/routers/profile.mjs.map +1 -1
- package/dist/routers/projects.mjs +17 -1
- package/dist/routers/projects.mjs.map +1 -1
- package/dist/routers/publish-upload.mjs +78 -0
- package/dist/routers/publish-upload.mjs.map +1 -0
- package/dist/routers/published-access.mjs +52 -0
- package/dist/routers/published-access.mjs.map +1 -0
- package/dist/routers/published-files.mjs +52 -0
- package/dist/routers/published-files.mjs.map +1 -0
- package/dist/routers/review-forms.mjs +72 -0
- package/dist/routers/review-forms.mjs.map +1 -0
- package/dist/routers/review-section.handlers.mjs +85 -0
- package/dist/routers/review-section.handlers.mjs.map +1 -0
- package/dist/routers/review-switch-off.mjs +29 -0
- package/dist/routers/review-switch-off.mjs.map +1 -0
- package/dist/routers/schemas.mjs +140 -22
- package/dist/routers/schemas.mjs.map +1 -1
- package/dist/routers/scoped-models.mjs +22 -0
- package/dist/routers/scoped-models.mjs.map +1 -0
- package/dist/routers/settings-ai.mjs +35 -0
- package/dist/routers/settings-ai.mjs.map +1 -0
- package/dist/routers/settings-capture.mjs +77 -0
- package/dist/routers/settings-capture.mjs.map +1 -0
- package/dist/routers/settings-design.mjs +103 -0
- package/dist/routers/settings-design.mjs.map +1 -0
- package/dist/routers/settings-general.mjs +55 -10
- package/dist/routers/settings-general.mjs.map +1 -1
- package/dist/routers/settings-git-channel.mjs +77 -0
- package/dist/routers/settings-git-channel.mjs.map +1 -0
- package/dist/routers/settings-labels.mjs +27 -1
- package/dist/routers/settings-labels.mjs.map +1 -1
- package/dist/routers/settings-links.mjs +63 -0
- package/dist/routers/settings-links.mjs.map +1 -0
- package/dist/routers/settings-members.mjs +4 -7
- package/dist/routers/settings-members.mjs.map +1 -1
- package/dist/routers/settings-notifications.mjs +15 -10
- package/dist/routers/settings-notifications.mjs.map +1 -1
- package/dist/routers/settings-tokens.mjs +2 -5
- package/dist/routers/settings-tokens.mjs.map +1 -1
- package/dist/routers/settings-webhooks.mjs +7 -24
- package/dist/routers/settings-webhooks.mjs.map +1 -1
- package/dist/routers/settings.handlers.mjs +107 -26
- package/dist/routers/settings.handlers.mjs.map +1 -1
- package/dist/routers/settings.mjs +16 -4
- package/dist/routers/settings.mjs.map +1 -1
- package/dist/routers/signed-storybook.mjs +53 -0
- package/dist/routers/signed-storybook.mjs.map +1 -0
- package/dist/routers/snapshot-groups.mjs +63 -0
- package/dist/routers/snapshot-groups.mjs.map +1 -0
- package/dist/routers/snapshots.mjs +103 -5
- package/dist/routers/snapshots.mjs.map +1 -1
- package/dist/routers/storybook.mjs +37 -88
- package/dist/routers/storybook.mjs.map +1 -1
- package/dist/routers/tokens.mjs +2 -5
- package/dist/routers/tokens.mjs.map +1 -1
- package/dist/routers/ui.mjs +34 -5
- package/dist/routers/ui.mjs.map +1 -1
- package/dist/routers/webhooks.mjs +5 -11
- package/dist/routers/webhooks.mjs.map +1 -1
- package/dist/routers/well-known.mjs +11 -4
- package/dist/routers/well-known.mjs.map +1 -1
- package/dist/routes/api-routes.mjs +93 -0
- package/dist/routes/api-routes.mjs.map +1 -0
- package/dist/routes/classification.mjs +38 -0
- package/dist/routes/classification.mjs.map +1 -0
- package/dist/routes/openapi-classes.mjs +22 -0
- package/dist/routes/openapi-classes.mjs.map +1 -0
- package/dist/routes/page-routes.mjs +104 -0
- package/dist/routes/page-routes.mjs.map +1 -0
- package/dist/runtime.mjs +18 -13
- package/dist/runtime.mjs.map +1 -1
- package/dist/serverless.mjs +5 -0
- package/dist/store.mjs +19 -1
- package/dist/store.mjs.map +1 -1
- package/dist/targets.mjs +46 -0
- package/dist/targets.mjs.map +1 -0
- package/dist/ui/buttons.mjs +16 -3
- package/dist/ui/buttons.mjs.map +1 -1
- package/dist/ui/design-reference.mjs +97 -0
- package/dist/ui/design-reference.mjs.map +1 -0
- package/dist/ui/document.mjs +1 -2
- package/dist/ui/document.mjs.map +1 -1
- package/dist/ui/forms.mjs +6 -5
- package/dist/ui/forms.mjs.map +1 -1
- package/dist/ui/icons/paths.mjs +1 -0
- package/dist/ui/icons/paths.mjs.map +1 -1
- package/dist/ui/preview-embed.mjs +82 -0
- package/dist/ui/preview-embed.mjs.map +1 -0
- package/dist/ui/shell/client-script.mjs +85 -0
- package/dist/ui/shell/client-script.mjs.map +1 -1
- package/dist/ui/shell/nav.mjs +12 -8
- package/dist/ui/shell/nav.mjs.map +1 -1
- package/dist/ui/shell/review-script.mjs +8 -0
- package/dist/ui/shell/review-script.mjs.map +1 -1
- package/dist/ui/shell/sidebar.mjs +2 -2
- package/dist/ui/shell/sidebar.mjs.map +1 -1
- package/package.json +11 -6
- package/dist/asset-paths.mjs +0 -23
- package/dist/asset-paths.mjs.map +0 -1
- package/dist/assets/mark.svg +0 -1
- package/dist/pages/settings-status.mjs +0 -137
- package/dist/pages/settings-status.mjs.map +0 -1
- package/dist/routers/settings-status.mjs +0 -77
- package/dist/routers/settings-status.mjs.map +0 -1
- package/dist/routers/status-configs.mjs +0 -108
- package/dist/routers/status-configs.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,18 +1,56 @@
|
|
|
1
1
|
# @storyshelf/app
|
|
2
2
|
|
|
3
|
-
The StoryShelf HTTP app: `createShelfApp()` composes database, storage, capture, auth, and git
|
|
3
|
+
The StoryShelf HTTP app: `createShelfApp()` composes database, storage, capture, auth, and notifier (chat, email, git) adapters (all from `@storyshelf/core`) into a complete Hono application — JSON API under `/api/v1` plus server-rendered HTML pages (hono/jsx + HTMX, no client framework).
|
|
4
4
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { createShelfApp } from "@storyshelf/app";
|
|
9
9
|
|
|
10
|
-
const app = createShelfApp({ database, storage, captureRunner });
|
|
11
|
-
|
|
10
|
+
const app = createShelfApp({ database, storage, testing: { runner: captureRunner } });
|
|
11
|
+
// Leave `testing` out to host Storybooks only: createShelfApp({ database, storage })
|
|
12
|
+
await app.lifecycle.setup();
|
|
12
13
|
|
|
13
14
|
serve({ fetch: app.fetch, port: 3000 });
|
|
14
15
|
```
|
|
15
16
|
|
|
17
|
+
Adapter `init` hooks kick off in the background when the app is created. Await them before serving for fail-fast startup (or let the first request gate on settlement), and close adapters on shutdown:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
await app.lifecycle.setup();
|
|
21
|
+
|
|
22
|
+
const server = serve({ fetch: app.fetch, port: 3000 });
|
|
23
|
+
process.on("SIGTERM", () => {
|
|
24
|
+
app.lifecycle
|
|
25
|
+
.teardown()
|
|
26
|
+
.then(() => server.close())
|
|
27
|
+
.catch(() => {});
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Logging
|
|
32
|
+
|
|
33
|
+
Three tiers, pick the shallowest that fits:
|
|
34
|
+
|
|
35
|
+
1. **Defaults** — omit `logger` and import nothing. The app creates its own instance; `LOG_LEVEL` env sets the level (`debug`, `info`, …; default `"info"`).
|
|
36
|
+
2. **Configured app** — `createShelfLogger` (re-exported from `@storyshelf/app`, canonical home `@storyshelf/core/logger`), pass it in, and read it back via `app.lifecycle.logger` for your own logs:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { createShelfLogger, createShelfApp } from "@storyshelf/app";
|
|
40
|
+
|
|
41
|
+
const app = createShelfApp({ database, storage, logger: createShelfLogger() });
|
|
42
|
+
await app.lifecycle.setup();
|
|
43
|
+
const logger = app.lifecycle.logger;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
3. **A process that only consumes jobs or runs forked capture** — import `createShelfLogger` from `@storyshelf/core/logger` directly, never the router barrel (it must not pull Hono).
|
|
47
|
+
|
|
48
|
+
## Health
|
|
49
|
+
|
|
50
|
+
Two-tier, neither gated by adapter init: `GET /api/v1/health` is open liveness for orchestrators; `POST /api/v1/health` is site-admin deep readiness with per-adapter states.
|
|
51
|
+
|
|
52
|
+
Choose the default adapters in [deployment](../../guides/deployment/) or see the [CLI guide](../../guides/cli/) for the packaged server entry point.
|
|
53
|
+
|
|
16
54
|
## How it fits in
|
|
17
55
|
|
|
18
|
-
`app` owns everything HTTP: `routers/`, `pages/`, `ui/`, `middleware/`, the request `store`, and vendored assets. All domain logic — adapter interfaces, models, schema, capture pipeline, retention, diff — lives in `@storyshelf/core` and is imported here through its granular subpaths (never the core barrel, which is now domain-only).
|
|
56
|
+
`app` owns everything HTTP: `routers/`, `pages/`, `ui/`, `middleware/`, the request `store`, and vendored assets. All domain logic — adapter interfaces, models, schema, capture pipeline, retention, diff — lives in `@storyshelf/core` and is imported here through its granular subpaths (never the core barrel, which is now domain-only). The capture consumer and the forked capture child live in core too, so they pull zero HTTP modules.
|
package/dist/app-types.d.mts
CHANGED
|
@@ -2,13 +2,12 @@ import __tsdown_shims_path from 'node:path';
|
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
4
4
|
import { AdapterSetupResult } from "@storyshelf/core/adapter/setup";
|
|
5
|
+
import { ShelfConfig, UIConfig } from "@storyshelf/core/config";
|
|
5
6
|
import { NotifierProvider } from "@storyshelf/core/adapter/notifier";
|
|
6
7
|
import { AuthUser } from "@storyshelf/core/types";
|
|
7
|
-
import { ShelfConfig, UIConfig } from "@storyshelf/core/config";
|
|
8
8
|
import { Logger, createShelfLogger } from "@storyshelf/core/logger";
|
|
9
9
|
import { CaptureQueue } from "@storyshelf/core/adapter/capture-queue";
|
|
10
10
|
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
11
|
-
import { GitHostProvider } from "@storyshelf/core/adapter/git-host";
|
|
12
11
|
import { StorageAdapter } from "@storyshelf/core/adapter/storage";
|
|
13
12
|
//#region src/app-types.d.ts
|
|
14
13
|
/** Per-request shelf context (adapters, config, user, capture queue). */
|
|
@@ -23,7 +22,6 @@ interface ShelfContext {
|
|
|
23
22
|
authEnabled: boolean;
|
|
24
23
|
enqueueCapture?: (buildId: string, reqId?: string) => Promise<void>;
|
|
25
24
|
captureQueue: CaptureQueue | null;
|
|
26
|
-
gitHosts: GitHostProvider[];
|
|
27
25
|
notifiers: NotifierProvider[];
|
|
28
26
|
}
|
|
29
27
|
/** Hono router type carrying the shelf context variables. */
|
package/dist/asset-manifest.mjs
CHANGED
|
@@ -2,23 +2,31 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { contentHash, iconSprite, iconSpriteHash, iconSpriteHref } from "./ui/icons/sprite.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { htmxSource } from "./assets/htmx.mjs";
|
|
6
|
+
import { markSource } from "./assets/mark.mjs";
|
|
7
|
+
import { clientScript } from "./ui/shell/client-script.mjs";
|
|
7
8
|
//#region src/asset-manifest.ts
|
|
8
9
|
/**
|
|
9
10
|
* Static assets with content-addressed URLs. A URL that embeds the content
|
|
10
11
|
* hash may be cached forever (`immutable`): changing the file changes the
|
|
11
|
-
* URL.
|
|
12
|
+
* URL. The sources are modules under `assets/` (embedded in the bundle, nothing to locate at
|
|
13
|
+
* runtime) and are hashed once at load so the page shell can reference the URLs.
|
|
12
14
|
*/
|
|
13
|
-
const htmxSource = readFileSync(findAsset("htmx.min.js", import.meta.url), "utf8");
|
|
14
15
|
const htmxHash = contentHash(htmxSource);
|
|
15
16
|
/**
|
|
16
17
|
* The StoryShelf brand mark. Serves as the enforced favicon and as the default
|
|
17
18
|
* sidebar logo when `ui.logo` is not set.
|
|
18
19
|
*/
|
|
19
|
-
const markSource = readFileSync(findAsset("mark.svg", import.meta.url), "utf8");
|
|
20
20
|
const markHash = contentHash(markSource);
|
|
21
|
+
/** The shell's interactive behaviour; static, so it is served as a cacheable file. */
|
|
22
|
+
const clientSource = clientScript();
|
|
23
|
+
const clientHash = contentHash(clientSource);
|
|
21
24
|
const assetManifest = {
|
|
25
|
+
client: {
|
|
26
|
+
source: clientSource,
|
|
27
|
+
hash: clientHash,
|
|
28
|
+
href: `/assets/client-${clientHash}.js`
|
|
29
|
+
},
|
|
22
30
|
htmx: {
|
|
23
31
|
source: htmxSource,
|
|
24
32
|
hash: htmxHash,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-manifest.mjs","names":[],"sources":["../src/asset-manifest.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"asset-manifest.mjs","names":[],"sources":["../src/asset-manifest.ts"],"sourcesContent":["import { htmxSource } from \"./assets/htmx.ts\";\nimport { markSource } from \"./assets/mark.ts\";\nimport { contentHash, iconSprite, iconSpriteHash, iconSpriteHref } from \"./ui/icons/sprite.ts\";\nimport { clientScript } from \"./ui/shell/client-script.ts\";\n\n/**\n * Static assets with content-addressed URLs. A URL that embeds the content\n * hash may be cached forever (`immutable`): changing the file changes the\n * URL. The sources are modules under `assets/` (embedded in the bundle, nothing to locate at\n * runtime) and are hashed once at load so the page shell can reference the URLs.\n */\nconst htmxHash = contentHash(htmxSource);\n\n/**\n * The StoryShelf brand mark. Serves as the enforced favicon and as the default\n * sidebar logo when `ui.logo` is not set.\n */\nconst markHash = contentHash(markSource);\n\n/** The shell's interactive behaviour; static, so it is served as a cacheable file. */\nconst clientSource = clientScript();\nconst clientHash = contentHash(clientSource);\n\nexport const assetManifest = {\n client: { source: clientSource, hash: clientHash, href: `/assets/client-${clientHash}.js` },\n htmx: { source: htmxSource, hash: htmxHash, href: `/assets/htmx-${htmxHash}.js` },\n icons: { source: iconSprite, hash: iconSpriteHash, href: iconSpriteHref },\n mark: { source: markSource, hash: markHash, href: `/assets/mark-${markHash}.svg` },\n} as const;\n"],"mappings":";;;;;;;;;;;;;;AAWA,MAAM,WAAW,YAAY,UAAU;;;;;AAMvC,MAAM,WAAW,YAAY,UAAU;;AAGvC,MAAM,eAAe,aAAa;AAClC,MAAM,aAAa,YAAY,YAAY;AAE3C,MAAa,gBAAgB;CAC3B,QAAQ;EAAE,QAAQ;EAAc,MAAM;EAAY,MAAM,kBAAkB,WAAW;CAAK;CAC1F,MAAM;EAAE,QAAQ;EAAY,MAAM;EAAU,MAAM,gBAAgB,SAAS;CAAK;CAChF,OAAO;EAAE,QAAQ;EAAY,MAAM;EAAgB,MAAM;CAAe;CACxE,MAAM;EAAE,QAAQ;EAAY,MAAM;EAAU,MAAM,gBAAgB,SAAS;CAAM;AACnF"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/assets/htmx.ts
|
|
5
|
+
const htmxSource = String.raw`(function (e, t) {
|
|
2
6
|
if (typeof define === "function" && define.amd) {
|
|
3
7
|
define([], t);
|
|
4
8
|
} else if (typeof module === "object" && module.exports) {
|
|
@@ -3302,3 +3306,8 @@
|
|
|
3302
3306
|
return Q;
|
|
3303
3307
|
})();
|
|
3304
3308
|
});
|
|
3309
|
+
`;
|
|
3310
|
+
//#endregion
|
|
3311
|
+
export { htmxSource };
|
|
3312
|
+
|
|
3313
|
+
//# sourceMappingURL=htmx.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"htmx.mjs","names":[],"sources":["../../src/assets/htmx.ts"],"sourcesContent":["/**\n * htmx 1.9.12, vendored as shipped in `htmx.org@1.9.12/dist/htmx.min.js` (formatted by oxfmt).\n *\n * Embedded as a string so it travels inside the compiled JavaScript: there is no file to find\n * at runtime, whether the app runs from source, from the library build or inside a bundle.\n * To upgrade, run `nub packages/app/scripts/embed-htmx.ts <path to the new htmx.min.js>`.\n */\nexport const htmxVersion = \"1.9.12\";\n\nexport const htmxSource = String.raw`(function (e, t) {\n if (typeof define === \"function\" && define.amd) {\n define([], t);\n } else if (typeof module === \"object\" && module.exports) {\n module.exports = t();\n } else {\n e.htmx = e.htmx || t();\n }\n})(typeof self !== \"undefined\" ? self : this, function () {\n return (function () {\n \"use strict\";\n var Q = {\n onLoad: F,\n process: zt,\n on: de,\n off: ge,\n trigger: ce,\n ajax: Nr,\n find: C,\n findAll: f,\n closest: v,\n values: function (e, t) {\n var r = dr(e, t || \"post\");\n return r.values;\n },\n remove: _,\n addClass: z,\n removeClass: n,\n toggleClass: $,\n takeClass: W,\n defineExtension: Ur,\n removeExtension: Br,\n logAll: V,\n logNone: j,\n logger: null,\n config: {\n historyEnabled: true,\n historyCacheSize: 10,\n refreshOnHistoryMiss: false,\n defaultSwapStyle: \"innerHTML\",\n defaultSwapDelay: 0,\n defaultSettleDelay: 20,\n includeIndicatorStyles: true,\n indicatorClass: \"htmx-indicator\",\n requestClass: \"htmx-request\",\n addedClass: \"htmx-added\",\n settlingClass: \"htmx-settling\",\n swappingClass: \"htmx-swapping\",\n allowEval: true,\n allowScriptTags: true,\n inlineScriptNonce: \"\",\n attributesToSettle: [\"class\", \"style\", \"width\", \"height\"],\n withCredentials: false,\n timeout: 0,\n wsReconnectDelay: \"full-jitter\",\n wsBinaryType: \"blob\",\n disableSelector: \"[hx-disable], [data-hx-disable]\",\n useTemplateFragments: false,\n scrollBehavior: \"smooth\",\n defaultFocusScroll: false,\n getCacheBusterParam: false,\n globalViewTransitions: false,\n methodsThatUseUrlParams: [\"get\"],\n selfRequestsOnly: false,\n ignoreTitle: false,\n scrollIntoViewOnBoost: true,\n triggerSpecsCache: null,\n },\n parseInterval: d,\n _: t,\n createEventSource: function (e) {\n return new EventSource(e, { withCredentials: true });\n },\n createWebSocket: function (e) {\n var t = new WebSocket(e, []);\n t.binaryType = Q.config.wsBinaryType;\n return t;\n },\n version: \"1.9.12\",\n };\n var r = {\n addTriggerHandler: Lt,\n bodyContains: se,\n canAccessLocalStorage: U,\n findThisElement: xe,\n filterValues: yr,\n hasAttribute: o,\n getAttributeValue: te,\n getClosestAttributeValue: ne,\n getClosestMatch: c,\n getExpressionVars: Hr,\n getHeaders: xr,\n getInputValues: dr,\n getInternalData: ae,\n getSwapSpecification: wr,\n getTriggerSpecs: it,\n getTarget: ye,\n makeFragment: l,\n mergeObjects: le,\n makeSettleInfo: T,\n oobSwap: Ee,\n querySelectorExt: ue,\n selectAndSwap: je,\n settleImmediately: nr,\n shouldCancel: ut,\n triggerEvent: ce,\n triggerErrorEvent: fe,\n withExtensions: R,\n };\n var w = [\"get\", \"post\", \"put\", \"delete\", \"patch\"];\n var i = w\n .map(function (e) {\n return \"[hx-\" + e + \"], [data-hx-\" + e + \"]\";\n })\n .join(\", \");\n var S = e(\"head\"),\n q = e(\"title\"),\n H = e(\"svg\", true);\n function e(e, t) {\n return new RegExp(\"<\" + e + \"(\\\\s[^>]*>|>)([\\\\s\\\\S]*?)<\\\\/\" + e + \">\", !!t ? \"gim\" : \"im\");\n }\n function d(e) {\n if (e == undefined) {\n return undefined;\n }\n let t = NaN;\n if (e.slice(-2) == \"ms\") {\n t = parseFloat(e.slice(0, -2));\n } else if (e.slice(-1) == \"s\") {\n t = parseFloat(e.slice(0, -1)) * 1e3;\n } else if (e.slice(-1) == \"m\") {\n t = parseFloat(e.slice(0, -1)) * 1e3 * 60;\n } else {\n t = parseFloat(e);\n }\n return isNaN(t) ? undefined : t;\n }\n function ee(e, t) {\n return e.getAttribute && e.getAttribute(t);\n }\n function o(e, t) {\n return e.hasAttribute && (e.hasAttribute(t) || e.hasAttribute(\"data-\" + t));\n }\n function te(e, t) {\n return ee(e, t) || ee(e, \"data-\" + t);\n }\n function u(e) {\n return e.parentElement;\n }\n function re() {\n return document;\n }\n function c(e, t) {\n while (e && !t(e)) {\n e = u(e);\n }\n return e ? e : null;\n }\n function L(e, t, r) {\n var n = te(t, r);\n var i = te(t, \"hx-disinherit\");\n if (e !== t && i && (i === \"*\" || i.split(\" \").indexOf(r) >= 0)) {\n return \"unset\";\n } else {\n return n;\n }\n }\n function ne(t, r) {\n var n = null;\n c(t, function (e) {\n return (n = L(t, e, r));\n });\n if (n !== \"unset\") {\n return n;\n }\n }\n function h(e, t) {\n var r =\n e.matches ||\n e.matchesSelector ||\n e.msMatchesSelector ||\n e.mozMatchesSelector ||\n e.webkitMatchesSelector ||\n e.oMatchesSelector;\n return r && r.call(e, t);\n }\n function A(e) {\n var t = /<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i;\n var r = t.exec(e);\n if (r) {\n return r[1].toLowerCase();\n } else {\n return \"\";\n }\n }\n function s(e, t) {\n var r = new DOMParser();\n var n = r.parseFromString(e, \"text/html\");\n var i = n.body;\n while (t > 0) {\n t--;\n i = i.firstChild;\n }\n if (i == null) {\n i = re().createDocumentFragment();\n }\n return i;\n }\n function N(e) {\n return /<body/.test(e);\n }\n function l(e) {\n var t = !N(e);\n var r = A(e);\n var n = e;\n if (r === \"head\") {\n n = n.replace(S, \"\");\n }\n if (Q.config.useTemplateFragments && t) {\n var i = s(\"<body><template>\" + n + \"</template></body>\", 0);\n var a = i.querySelector(\"template\").content;\n if (Q.config.allowScriptTags) {\n oe(a.querySelectorAll(\"script\"), function (e) {\n if (Q.config.inlineScriptNonce) {\n e.nonce = Q.config.inlineScriptNonce;\n }\n e.htmxExecuted = navigator.userAgent.indexOf(\"Firefox\") === -1;\n });\n } else {\n oe(a.querySelectorAll(\"script\"), function (e) {\n _(e);\n });\n }\n return a;\n }\n switch (r) {\n case \"thead\":\n case \"tbody\":\n case \"tfoot\":\n case \"colgroup\":\n case \"caption\":\n return s(\"<table>\" + n + \"</table>\", 1);\n case \"col\":\n return s(\"<table><colgroup>\" + n + \"</colgroup></table>\", 2);\n case \"tr\":\n return s(\"<table><tbody>\" + n + \"</tbody></table>\", 2);\n case \"td\":\n case \"th\":\n return s(\"<table><tbody><tr>\" + n + \"</tr></tbody></table>\", 3);\n case \"script\":\n case \"style\":\n return s(\"<div>\" + n + \"</div>\", 1);\n default:\n return s(n, 0);\n }\n }\n function ie(e) {\n if (e) {\n e();\n }\n }\n function I(e, t) {\n return Object.prototype.toString.call(e) === \"[object \" + t + \"]\";\n }\n function k(e) {\n return I(e, \"Function\");\n }\n function P(e) {\n return I(e, \"Object\");\n }\n function ae(e) {\n var t = \"htmx-internal-data\";\n var r = e[t];\n if (!r) {\n r = e[t] = {};\n }\n return r;\n }\n function M(e) {\n var t = [];\n if (e) {\n for (var r = 0; r < e.length; r++) {\n t.push(e[r]);\n }\n }\n return t;\n }\n function oe(e, t) {\n if (e) {\n for (var r = 0; r < e.length; r++) {\n t(e[r]);\n }\n }\n }\n function X(e) {\n var t = e.getBoundingClientRect();\n var r = t.top;\n var n = t.bottom;\n return r < window.innerHeight && n >= 0;\n }\n function se(e) {\n if (e.getRootNode && e.getRootNode() instanceof window.ShadowRoot) {\n return re().body.contains(e.getRootNode().host);\n } else {\n return re().body.contains(e);\n }\n }\n function D(e) {\n return e.trim().split(/\\s+/);\n }\n function le(e, t) {\n for (var r in t) {\n if (t.hasOwnProperty(r)) {\n e[r] = t[r];\n }\n }\n return e;\n }\n function E(e) {\n try {\n return JSON.parse(e);\n } catch (e) {\n b(e);\n return null;\n }\n }\n function U() {\n var e = \"htmx:localStorageTest\";\n try {\n localStorage.setItem(e, e);\n localStorage.removeItem(e);\n return true;\n } catch (e) {\n return false;\n }\n }\n function B(t) {\n try {\n var e = new URL(t);\n if (e) {\n t = e.pathname + e.search;\n }\n if (!/^\\/$/.test(t)) {\n t = t.replace(/\\/+$/, \"\");\n }\n return t;\n } catch (e) {\n return t;\n }\n }\n function t(e) {\n return Tr(re().body, function () {\n return eval(e);\n });\n }\n function F(t) {\n var e = Q.on(\"htmx:load\", function (e) {\n t(e.detail.elt);\n });\n return e;\n }\n function V() {\n Q.logger = function (e, t, r) {\n if (console) {\n console.log(t, e, r);\n }\n };\n }\n function j() {\n Q.logger = null;\n }\n function C(e, t) {\n if (t) {\n return e.querySelector(t);\n } else {\n return C(re(), e);\n }\n }\n function f(e, t) {\n if (t) {\n return e.querySelectorAll(t);\n } else {\n return f(re(), e);\n }\n }\n function _(e, t) {\n e = p(e);\n if (t) {\n setTimeout(function () {\n _(e);\n e = null;\n }, t);\n } else {\n e.parentElement.removeChild(e);\n }\n }\n function z(e, t, r) {\n e = p(e);\n if (r) {\n setTimeout(function () {\n z(e, t);\n e = null;\n }, r);\n } else {\n e.classList && e.classList.add(t);\n }\n }\n function n(e, t, r) {\n e = p(e);\n if (r) {\n setTimeout(function () {\n n(e, t);\n e = null;\n }, r);\n } else {\n if (e.classList) {\n e.classList.remove(t);\n if (e.classList.length === 0) {\n e.removeAttribute(\"class\");\n }\n }\n }\n }\n function $(e, t) {\n e = p(e);\n e.classList.toggle(t);\n }\n function W(e, t) {\n e = p(e);\n oe(e.parentElement.children, function (e) {\n n(e, t);\n });\n z(e, t);\n }\n function v(e, t) {\n e = p(e);\n if (e.closest) {\n return e.closest(t);\n } else {\n do {\n if (e == null || h(e, t)) {\n return e;\n }\n } while ((e = e && u(e)));\n return null;\n }\n }\n function g(e, t) {\n return e.substring(0, t.length) === t;\n }\n function G(e, t) {\n return e.substring(e.length - t.length) === t;\n }\n function J(e) {\n var t = e.trim();\n if (g(t, \"<\") && G(t, \"/>\")) {\n return t.substring(1, t.length - 2);\n } else {\n return t;\n }\n }\n function Z(e, t) {\n if (t.indexOf(\"closest \") === 0) {\n return [v(e, J(t.substr(8)))];\n } else if (t.indexOf(\"find \") === 0) {\n return [C(e, J(t.substr(5)))];\n } else if (t === \"next\") {\n return [e.nextElementSibling];\n } else if (t.indexOf(\"next \") === 0) {\n return [K(e, J(t.substr(5)))];\n } else if (t === \"previous\") {\n return [e.previousElementSibling];\n } else if (t.indexOf(\"previous \") === 0) {\n return [Y(e, J(t.substr(9)))];\n } else if (t === \"document\") {\n return [document];\n } else if (t === \"window\") {\n return [window];\n } else if (t === \"body\") {\n return [document.body];\n } else {\n return re().querySelectorAll(J(t));\n }\n }\n var K = function (e, t) {\n var r = re().querySelectorAll(t);\n for (var n = 0; n < r.length; n++) {\n var i = r[n];\n if (i.compareDocumentPosition(e) === Node.DOCUMENT_POSITION_PRECEDING) {\n return i;\n }\n }\n };\n var Y = function (e, t) {\n var r = re().querySelectorAll(t);\n for (var n = r.length - 1; n >= 0; n--) {\n var i = r[n];\n if (i.compareDocumentPosition(e) === Node.DOCUMENT_POSITION_FOLLOWING) {\n return i;\n }\n }\n };\n function ue(e, t) {\n if (t) {\n return Z(e, t)[0];\n } else {\n return Z(re().body, e)[0];\n }\n }\n function p(e) {\n if (I(e, \"String\")) {\n return C(e);\n } else {\n return e;\n }\n }\n function ve(e, t, r) {\n if (k(t)) {\n return { target: re().body, event: e, listener: t };\n } else {\n return { target: p(e), event: t, listener: r };\n }\n }\n function de(t, r, n) {\n jr(function () {\n var e = ve(t, r, n);\n e.target.addEventListener(e.event, e.listener);\n });\n var e = k(r);\n return e ? r : n;\n }\n function ge(t, r, n) {\n jr(function () {\n var e = ve(t, r, n);\n e.target.removeEventListener(e.event, e.listener);\n });\n return k(r) ? r : n;\n }\n var pe = re().createElement(\"output\");\n function me(e, t) {\n var r = ne(e, t);\n if (r) {\n if (r === \"this\") {\n return [xe(e, t)];\n } else {\n var n = Z(e, r);\n if (n.length === 0) {\n b('The selector \"' + r + '\" on ' + t + \" returned no matches!\");\n return [pe];\n } else {\n return n;\n }\n }\n }\n }\n function xe(e, t) {\n return c(e, function (e) {\n return te(e, t) != null;\n });\n }\n function ye(e) {\n var t = ne(e, \"hx-target\");\n if (t) {\n if (t === \"this\") {\n return xe(e, \"hx-target\");\n } else {\n return ue(e, t);\n }\n } else {\n var r = ae(e);\n if (r.boosted) {\n return re().body;\n } else {\n return e;\n }\n }\n }\n function be(e) {\n var t = Q.config.attributesToSettle;\n for (var r = 0; r < t.length; r++) {\n if (e === t[r]) {\n return true;\n }\n }\n return false;\n }\n function we(t, r) {\n oe(t.attributes, function (e) {\n if (!r.hasAttribute(e.name) && be(e.name)) {\n t.removeAttribute(e.name);\n }\n });\n oe(r.attributes, function (e) {\n if (be(e.name)) {\n t.setAttribute(e.name, e.value);\n }\n });\n }\n function Se(e, t) {\n var r = Fr(t);\n for (var n = 0; n < r.length; n++) {\n var i = r[n];\n try {\n if (i.isInlineSwap(e)) {\n return true;\n }\n } catch (e) {\n b(e);\n }\n }\n return e === \"outerHTML\";\n }\n function Ee(e, i, a) {\n var t = \"#\" + ee(i, \"id\");\n var o = \"outerHTML\";\n if (e === \"true\") {\n } else if (e.indexOf(\":\") > 0) {\n o = e.substr(0, e.indexOf(\":\"));\n t = e.substr(e.indexOf(\":\") + 1, e.length);\n } else {\n o = e;\n }\n var r = re().querySelectorAll(t);\n if (r) {\n oe(r, function (e) {\n var t;\n var r = i.cloneNode(true);\n t = re().createDocumentFragment();\n t.appendChild(r);\n if (!Se(o, e)) {\n t = r;\n }\n var n = { shouldSwap: true, target: e, fragment: t };\n if (!ce(e, \"htmx:oobBeforeSwap\", n)) return;\n e = n.target;\n if (n[\"shouldSwap\"]) {\n Fe(o, e, e, t, a);\n }\n oe(a.elts, function (e) {\n ce(e, \"htmx:oobAfterSwap\", n);\n });\n });\n i.parentNode.removeChild(i);\n } else {\n i.parentNode.removeChild(i);\n fe(re().body, \"htmx:oobErrorNoTarget\", { content: i });\n }\n return e;\n }\n function Ce(e, t, r) {\n var n = ne(e, \"hx-select-oob\");\n if (n) {\n var i = n.split(\",\");\n for (var a = 0; a < i.length; a++) {\n var o = i[a].split(\":\", 2);\n var s = o[0].trim();\n if (s.indexOf(\"#\") === 0) {\n s = s.substring(1);\n }\n var l = o[1] || \"true\";\n var u = t.querySelector(\"#\" + s);\n if (u) {\n Ee(l, u, r);\n }\n }\n }\n oe(f(t, \"[hx-swap-oob], [data-hx-swap-oob]\"), function (e) {\n var t = te(e, \"hx-swap-oob\");\n if (t != null) {\n Ee(t, e, r);\n }\n });\n }\n function Re(e) {\n oe(f(e, \"[hx-preserve], [data-hx-preserve]\"), function (e) {\n var t = te(e, \"id\");\n var r = re().getElementById(t);\n if (r != null) {\n e.parentNode.replaceChild(r, e);\n }\n });\n }\n function Te(o, e, s) {\n oe(e.querySelectorAll(\"[id]\"), function (e) {\n var t = ee(e, \"id\");\n if (t && t.length > 0) {\n var r = t.replace(\"'\", \"\\\\'\");\n var n = e.tagName.replace(\":\", \"\\\\:\");\n var i = o.querySelector(n + \"[id='\" + r + \"']\");\n if (i && i !== o) {\n var a = e.cloneNode();\n we(e, i);\n s.tasks.push(function () {\n we(e, a);\n });\n }\n }\n });\n }\n function Oe(e) {\n return function () {\n n(e, Q.config.addedClass);\n zt(e);\n Nt(e);\n qe(e);\n ce(e, \"htmx:load\");\n };\n }\n function qe(e) {\n var t = \"[autofocus]\";\n var r = h(e, t) ? e : e.querySelector(t);\n if (r != null) {\n r.focus();\n }\n }\n function a(e, t, r, n) {\n Te(e, r, n);\n while (r.childNodes.length > 0) {\n var i = r.firstChild;\n z(i, Q.config.addedClass);\n e.insertBefore(i, t);\n if (i.nodeType !== Node.TEXT_NODE && i.nodeType !== Node.COMMENT_NODE) {\n n.tasks.push(Oe(i));\n }\n }\n }\n function He(e, t) {\n var r = 0;\n while (r < e.length) {\n t = ((t << 5) - t + e.charCodeAt(r++)) | 0;\n }\n return t;\n }\n function Le(e) {\n var t = 0;\n if (e.attributes) {\n for (var r = 0; r < e.attributes.length; r++) {\n var n = e.attributes[r];\n if (n.value) {\n t = He(n.name, t);\n t = He(n.value, t);\n }\n }\n }\n return t;\n }\n function Ae(e) {\n var t = ae(e);\n if (t.onHandlers) {\n for (var r = 0; r < t.onHandlers.length; r++) {\n const n = t.onHandlers[r];\n e.removeEventListener(n.event, n.listener);\n }\n delete t.onHandlers;\n }\n }\n function Ne(e) {\n var t = ae(e);\n if (t.timeout) {\n clearTimeout(t.timeout);\n }\n if (t.webSocket) {\n t.webSocket.close();\n }\n if (t.sseEventSource) {\n t.sseEventSource.close();\n }\n if (t.listenerInfos) {\n oe(t.listenerInfos, function (e) {\n if (e.on) {\n e.on.removeEventListener(e.trigger, e.listener);\n }\n });\n }\n Ae(e);\n oe(Object.keys(t), function (e) {\n delete t[e];\n });\n }\n function m(e) {\n ce(e, \"htmx:beforeCleanupElement\");\n Ne(e);\n if (e.children) {\n oe(e.children, function (e) {\n m(e);\n });\n }\n }\n function Ie(t, e, r) {\n if (t.tagName === \"BODY\") {\n return Ue(t, e, r);\n } else {\n var n;\n var i = t.previousSibling;\n a(u(t), t, e, r);\n if (i == null) {\n n = u(t).firstChild;\n } else {\n n = i.nextSibling;\n }\n r.elts = r.elts.filter(function (e) {\n return e != t;\n });\n while (n && n !== t) {\n if (n.nodeType === Node.ELEMENT_NODE) {\n r.elts.push(n);\n }\n n = n.nextElementSibling;\n }\n m(t);\n u(t).removeChild(t);\n }\n }\n function ke(e, t, r) {\n return a(e, e.firstChild, t, r);\n }\n function Pe(e, t, r) {\n return a(u(e), e, t, r);\n }\n function Me(e, t, r) {\n return a(e, null, t, r);\n }\n function Xe(e, t, r) {\n return a(u(e), e.nextSibling, t, r);\n }\n function De(e, t, r) {\n m(e);\n return u(e).removeChild(e);\n }\n function Ue(e, t, r) {\n var n = e.firstChild;\n a(e, n, t, r);\n if (n) {\n while (n.nextSibling) {\n m(n.nextSibling);\n e.removeChild(n.nextSibling);\n }\n m(n);\n e.removeChild(n);\n }\n }\n function Be(e, t, r) {\n var n = r || ne(e, \"hx-select\");\n if (n) {\n var i = re().createDocumentFragment();\n oe(t.querySelectorAll(n), function (e) {\n i.appendChild(e);\n });\n t = i;\n }\n return t;\n }\n function Fe(e, t, r, n, i) {\n switch (e) {\n case \"none\":\n return;\n case \"outerHTML\":\n Ie(r, n, i);\n return;\n case \"afterbegin\":\n ke(r, n, i);\n return;\n case \"beforebegin\":\n Pe(r, n, i);\n return;\n case \"beforeend\":\n Me(r, n, i);\n return;\n case \"afterend\":\n Xe(r, n, i);\n return;\n case \"delete\":\n De(r, n, i);\n return;\n default:\n var a = Fr(t);\n for (var o = 0; o < a.length; o++) {\n var s = a[o];\n try {\n var l = s.handleSwap(e, r, n, i);\n if (l) {\n if (typeof l.length !== \"undefined\") {\n for (var u = 0; u < l.length; u++) {\n var f = l[u];\n if (f.nodeType !== Node.TEXT_NODE && f.nodeType !== Node.COMMENT_NODE) {\n i.tasks.push(Oe(f));\n }\n }\n }\n return;\n }\n } catch (e) {\n b(e);\n }\n }\n if (e === \"innerHTML\") {\n Ue(r, n, i);\n } else {\n Fe(Q.config.defaultSwapStyle, t, r, n, i);\n }\n }\n }\n function Ve(e) {\n if (e.indexOf(\"<title\") > -1) {\n var t = e.replace(H, \"\");\n var r = t.match(q);\n if (r) {\n return r[2];\n }\n }\n }\n function je(e, t, r, n, i, a) {\n i.title = Ve(n);\n var o = l(n);\n if (o) {\n Ce(r, o, i);\n o = Be(r, o, a);\n Re(o);\n return Fe(e, r, t, o, i);\n }\n }\n function _e(e, t, r) {\n var n = e.getResponseHeader(t);\n if (n.indexOf(\"{\") === 0) {\n var i = E(n);\n for (var a in i) {\n if (i.hasOwnProperty(a)) {\n var o = i[a];\n if (!P(o)) {\n o = { value: o };\n }\n ce(r, a, o);\n }\n }\n } else {\n var s = n.split(\",\");\n for (var l = 0; l < s.length; l++) {\n ce(r, s[l].trim(), []);\n }\n }\n }\n var ze = /\\s/;\n var x = /[\\s,]/;\n var $e = /[_$a-zA-Z]/;\n var We = /[_$a-zA-Z0-9]/;\n var Ge = ['\"', \"'\", \"/\"];\n var Je = /[^\\s]/;\n var Ze = /[{(]/;\n var Ke = /[})]/;\n function Ye(e) {\n var t = [];\n var r = 0;\n while (r < e.length) {\n if ($e.exec(e.charAt(r))) {\n var n = r;\n while (We.exec(e.charAt(r + 1))) {\n r++;\n }\n t.push(e.substr(n, r - n + 1));\n } else if (Ge.indexOf(e.charAt(r)) !== -1) {\n var i = e.charAt(r);\n var n = r;\n r++;\n while (r < e.length && e.charAt(r) !== i) {\n if (e.charAt(r) === \"\\\\\") {\n r++;\n }\n r++;\n }\n t.push(e.substr(n, r - n + 1));\n } else {\n var a = e.charAt(r);\n t.push(a);\n }\n r++;\n }\n return t;\n }\n function Qe(e, t, r) {\n return (\n $e.exec(e.charAt(0)) &&\n e !== \"true\" &&\n e !== \"false\" &&\n e !== \"this\" &&\n e !== r &&\n t !== \".\"\n );\n }\n function et(e, t, r) {\n if (t[0] === \"[\") {\n t.shift();\n var n = 1;\n var i = \" return (function(\" + r + \"){ return (\";\n var a = null;\n while (t.length > 0) {\n var o = t[0];\n if (o === \"]\") {\n n--;\n if (n === 0) {\n if (a === null) {\n i = i + \"true\";\n }\n t.shift();\n i += \")})\";\n try {\n var s = Tr(\n e,\n function () {\n return Function(i)();\n },\n function () {\n return true;\n },\n );\n s.source = i;\n return s;\n } catch (e) {\n fe(re().body, \"htmx:syntax:error\", { error: e, source: i });\n return null;\n }\n }\n } else if (o === \"[\") {\n n++;\n }\n if (Qe(o, a, r)) {\n i += \"((\" + r + \".\" + o + \") ? (\" + r + \".\" + o + \") : (window.\" + o + \"))\";\n } else {\n i = i + o;\n }\n a = t.shift();\n }\n }\n }\n function y(e, t) {\n var r = \"\";\n while (e.length > 0 && !t.test(e[0])) {\n r += e.shift();\n }\n return r;\n }\n function tt(e) {\n var t;\n if (e.length > 0 && Ze.test(e[0])) {\n e.shift();\n t = y(e, Ke).trim();\n e.shift();\n } else {\n t = y(e, x);\n }\n return t;\n }\n var rt = \"input, textarea, select\";\n function nt(e, t, r) {\n var n = [];\n var i = Ye(t);\n do {\n y(i, Je);\n var a = i.length;\n var o = y(i, /[,\\[\\s]/);\n if (o !== \"\") {\n if (o === \"every\") {\n var s = { trigger: \"every\" };\n y(i, Je);\n s.pollInterval = d(y(i, /[,\\[\\s]/));\n y(i, Je);\n var l = et(e, i, \"event\");\n if (l) {\n s.eventFilter = l;\n }\n n.push(s);\n } else if (o.indexOf(\"sse:\") === 0) {\n n.push({ trigger: \"sse\", sseEvent: o.substr(4) });\n } else {\n var u = { trigger: o };\n var l = et(e, i, \"event\");\n if (l) {\n u.eventFilter = l;\n }\n while (i.length > 0 && i[0] !== \",\") {\n y(i, Je);\n var f = i.shift();\n if (f === \"changed\") {\n u.changed = true;\n } else if (f === \"once\") {\n u.once = true;\n } else if (f === \"consume\") {\n u.consume = true;\n } else if (f === \"delay\" && i[0] === \":\") {\n i.shift();\n u.delay = d(y(i, x));\n } else if (f === \"from\" && i[0] === \":\") {\n i.shift();\n if (Ze.test(i[0])) {\n var c = tt(i);\n } else {\n var c = y(i, x);\n if (c === \"closest\" || c === \"find\" || c === \"next\" || c === \"previous\") {\n i.shift();\n var h = tt(i);\n if (h.length > 0) {\n c += \" \" + h;\n }\n }\n }\n u.from = c;\n } else if (f === \"target\" && i[0] === \":\") {\n i.shift();\n u.target = tt(i);\n } else if (f === \"throttle\" && i[0] === \":\") {\n i.shift();\n u.throttle = d(y(i, x));\n } else if (f === \"queue\" && i[0] === \":\") {\n i.shift();\n u.queue = y(i, x);\n } else if (f === \"root\" && i[0] === \":\") {\n i.shift();\n u[f] = tt(i);\n } else if (f === \"threshold\" && i[0] === \":\") {\n i.shift();\n u[f] = y(i, x);\n } else {\n fe(e, \"htmx:syntax:error\", { token: i.shift() });\n }\n }\n n.push(u);\n }\n }\n if (i.length === a) {\n fe(e, \"htmx:syntax:error\", { token: i.shift() });\n }\n y(i, Je);\n } while (i[0] === \",\" && i.shift());\n if (r) {\n r[t] = n;\n }\n return n;\n }\n function it(e) {\n var t = te(e, \"hx-trigger\");\n var r = [];\n if (t) {\n var n = Q.config.triggerSpecsCache;\n r = (n && n[t]) || nt(e, t, n);\n }\n if (r.length > 0) {\n return r;\n } else if (h(e, \"form\")) {\n return [{ trigger: \"submit\" }];\n } else if (h(e, 'input[type=\"button\"], input[type=\"submit\"]')) {\n return [{ trigger: \"click\" }];\n } else if (h(e, rt)) {\n return [{ trigger: \"change\" }];\n } else {\n return [{ trigger: \"click\" }];\n }\n }\n function at(e) {\n ae(e).cancelled = true;\n }\n function ot(e, t, r) {\n var n = ae(e);\n n.timeout = setTimeout(function () {\n if (se(e) && n.cancelled !== true) {\n if (!ct(r, e, Wt(\"hx:poll:trigger\", { triggerSpec: r, target: e }))) {\n t(e);\n }\n ot(e, t, r);\n }\n }, r.pollInterval);\n }\n function st(e) {\n return location.hostname === e.hostname && ee(e, \"href\") && ee(e, \"href\").indexOf(\"#\") !== 0;\n }\n function lt(t, r, e) {\n if (\n (t.tagName === \"A\" && st(t) && (t.target === \"\" || t.target === \"_self\")) ||\n t.tagName === \"FORM\"\n ) {\n r.boosted = true;\n var n, i;\n if (t.tagName === \"A\") {\n n = \"get\";\n i = ee(t, \"href\");\n } else {\n var a = ee(t, \"method\");\n n = a ? a.toLowerCase() : \"get\";\n if (n === \"get\") {\n }\n i = ee(t, \"action\");\n }\n e.forEach(function (e) {\n ht(\n t,\n function (e, t) {\n if (v(e, Q.config.disableSelector)) {\n m(e);\n return;\n }\n he(n, i, e, t);\n },\n r,\n e,\n true,\n );\n });\n }\n }\n function ut(e, t) {\n if (e.type === \"submit\" || e.type === \"click\") {\n if (t.tagName === \"FORM\") {\n return true;\n }\n if (h(t, 'input[type=\"submit\"], button') && v(t, \"form\") !== null) {\n return true;\n }\n if (\n t.tagName === \"A\" &&\n t.href &&\n (t.getAttribute(\"href\") === \"#\" || t.getAttribute(\"href\").indexOf(\"#\") !== 0)\n ) {\n return true;\n }\n }\n return false;\n }\n function ft(e, t) {\n return ae(e).boosted && e.tagName === \"A\" && t.type === \"click\" && (t.ctrlKey || t.metaKey);\n }\n function ct(e, t, r) {\n var n = e.eventFilter;\n if (n) {\n try {\n return n.call(t, r) !== true;\n } catch (e) {\n fe(re().body, \"htmx:eventFilter:error\", { error: e, source: n.source });\n return true;\n }\n }\n return false;\n }\n function ht(a, o, e, s, l) {\n var u = ae(a);\n var t;\n if (s.from) {\n t = Z(a, s.from);\n } else {\n t = [a];\n }\n if (s.changed) {\n t.forEach(function (e) {\n var t = ae(e);\n t.lastValue = e.value;\n });\n }\n oe(t, function (n) {\n var i = function (e) {\n if (!se(a)) {\n n.removeEventListener(s.trigger, i);\n return;\n }\n if (ft(a, e)) {\n return;\n }\n if (l || ut(e, a)) {\n e.preventDefault();\n }\n if (ct(s, a, e)) {\n return;\n }\n var t = ae(e);\n t.triggerSpec = s;\n if (t.handledFor == null) {\n t.handledFor = [];\n }\n if (t.handledFor.indexOf(a) < 0) {\n t.handledFor.push(a);\n if (s.consume) {\n e.stopPropagation();\n }\n if (s.target && e.target) {\n if (!h(e.target, s.target)) {\n return;\n }\n }\n if (s.once) {\n if (u.triggeredOnce) {\n return;\n } else {\n u.triggeredOnce = true;\n }\n }\n if (s.changed) {\n var r = ae(n);\n if (r.lastValue === n.value) {\n return;\n }\n r.lastValue = n.value;\n }\n if (u.delayed) {\n clearTimeout(u.delayed);\n }\n if (u.throttle) {\n return;\n }\n if (s.throttle > 0) {\n if (!u.throttle) {\n o(a, e);\n u.throttle = setTimeout(function () {\n u.throttle = null;\n }, s.throttle);\n }\n } else if (s.delay > 0) {\n u.delayed = setTimeout(function () {\n o(a, e);\n }, s.delay);\n } else {\n ce(a, \"htmx:trigger\");\n o(a, e);\n }\n }\n };\n if (e.listenerInfos == null) {\n e.listenerInfos = [];\n }\n e.listenerInfos.push({ trigger: s.trigger, listener: i, on: n });\n n.addEventListener(s.trigger, i);\n });\n }\n var vt = false;\n var dt = null;\n function gt() {\n if (!dt) {\n dt = function () {\n vt = true;\n };\n window.addEventListener(\"scroll\", dt);\n setInterval(function () {\n if (vt) {\n vt = false;\n oe(\n re().querySelectorAll(\"[hx-trigger='revealed'],[data-hx-trigger='revealed']\"),\n function (e) {\n pt(e);\n },\n );\n }\n }, 200);\n }\n }\n function pt(t) {\n if (!o(t, \"data-hx-revealed\") && X(t)) {\n t.setAttribute(\"data-hx-revealed\", \"true\");\n var e = ae(t);\n if (e.initHash) {\n ce(t, \"revealed\");\n } else {\n t.addEventListener(\n \"htmx:afterProcessNode\",\n function (e) {\n ce(t, \"revealed\");\n },\n { once: true },\n );\n }\n }\n }\n function mt(e, t, r) {\n var n = D(r);\n for (var i = 0; i < n.length; i++) {\n var a = n[i].split(/:(.+)/);\n if (a[0] === \"connect\") {\n xt(e, a[1], 0);\n }\n if (a[0] === \"send\") {\n bt(e);\n }\n }\n }\n function xt(s, r, n) {\n if (!se(s)) {\n return;\n }\n if (r.indexOf(\"/\") == 0) {\n var e = location.hostname + (location.port ? \":\" + location.port : \"\");\n if (location.protocol == \"https:\") {\n r = \"wss://\" + e + r;\n } else if (location.protocol == \"http:\") {\n r = \"ws://\" + e + r;\n }\n }\n var t = Q.createWebSocket(r);\n t.onerror = function (e) {\n fe(s, \"htmx:wsError\", { error: e, socket: t });\n yt(s);\n };\n t.onclose = function (e) {\n if ([1006, 1012, 1013].indexOf(e.code) >= 0) {\n var t = wt(n);\n setTimeout(function () {\n xt(s, r, n + 1);\n }, t);\n }\n };\n t.onopen = function (e) {\n n = 0;\n };\n ae(s).webSocket = t;\n t.addEventListener(\"message\", function (e) {\n if (yt(s)) {\n return;\n }\n var t = e.data;\n R(s, function (e) {\n t = e.transformResponse(t, null, s);\n });\n var r = T(s);\n var n = l(t);\n var i = M(n.children);\n for (var a = 0; a < i.length; a++) {\n var o = i[a];\n Ee(te(o, \"hx-swap-oob\") || \"true\", o, r);\n }\n nr(r.tasks);\n });\n }\n function yt(e) {\n if (!se(e)) {\n ae(e).webSocket.close();\n return true;\n }\n }\n function bt(u) {\n var f = c(u, function (e) {\n return ae(e).webSocket != null;\n });\n if (f) {\n u.addEventListener(it(u)[0].trigger, function (e) {\n var t = ae(f).webSocket;\n var r = xr(u, f);\n var n = dr(u, \"post\");\n var i = n.errors;\n var a = n.values;\n var o = Hr(u);\n var s = le(a, o);\n var l = yr(s, u);\n l[\"HEADERS\"] = r;\n if (i && i.length > 0) {\n ce(u, \"htmx:validation:halted\", i);\n return;\n }\n t.send(JSON.stringify(l));\n if (ut(e, u)) {\n e.preventDefault();\n }\n });\n } else {\n fe(u, \"htmx:noWebSocketSourceError\");\n }\n }\n function wt(e) {\n var t = Q.config.wsReconnectDelay;\n if (typeof t === \"function\") {\n return t(e);\n }\n if (t === \"full-jitter\") {\n var r = Math.min(e, 6);\n var n = 1e3 * Math.pow(2, r);\n return n * Math.random();\n }\n b('htmx.config.wsReconnectDelay must either be a function or the string \"full-jitter\"');\n }\n function St(e, t, r) {\n var n = D(r);\n for (var i = 0; i < n.length; i++) {\n var a = n[i].split(/:(.+)/);\n if (a[0] === \"connect\") {\n Et(e, a[1]);\n }\n if (a[0] === \"swap\") {\n Ct(e, a[1]);\n }\n }\n }\n function Et(t, e) {\n var r = Q.createEventSource(e);\n r.onerror = function (e) {\n fe(t, \"htmx:sseError\", { error: e, source: r });\n Tt(t);\n };\n ae(t).sseEventSource = r;\n }\n function Ct(a, o) {\n var s = c(a, Ot);\n if (s) {\n var l = ae(s).sseEventSource;\n var u = function (e) {\n if (Tt(s)) {\n return;\n }\n if (!se(a)) {\n l.removeEventListener(o, u);\n return;\n }\n var t = e.data;\n R(a, function (e) {\n t = e.transformResponse(t, null, a);\n });\n var r = wr(a);\n var n = ye(a);\n var i = T(a);\n je(r.swapStyle, n, a, t, i);\n nr(i.tasks);\n ce(a, \"htmx:sseMessage\", e);\n };\n ae(a).sseListener = u;\n l.addEventListener(o, u);\n } else {\n fe(a, \"htmx:noSSESourceError\");\n }\n }\n function Rt(e, t, r) {\n var n = c(e, Ot);\n if (n) {\n var i = ae(n).sseEventSource;\n var a = function () {\n if (!Tt(n)) {\n if (se(e)) {\n t(e);\n } else {\n i.removeEventListener(r, a);\n }\n }\n };\n ae(e).sseListener = a;\n i.addEventListener(r, a);\n } else {\n fe(e, \"htmx:noSSESourceError\");\n }\n }\n function Tt(e) {\n if (!se(e)) {\n ae(e).sseEventSource.close();\n return true;\n }\n }\n function Ot(e) {\n return ae(e).sseEventSource != null;\n }\n function qt(e, t, r, n) {\n var i = function () {\n if (!r.loaded) {\n r.loaded = true;\n t(e);\n }\n };\n if (n > 0) {\n setTimeout(i, n);\n } else {\n i();\n }\n }\n function Ht(t, i, e) {\n var a = false;\n oe(w, function (r) {\n if (o(t, \"hx-\" + r)) {\n var n = te(t, \"hx-\" + r);\n a = true;\n i.path = n;\n i.verb = r;\n e.forEach(function (e) {\n Lt(t, e, i, function (e, t) {\n if (v(e, Q.config.disableSelector)) {\n m(e);\n return;\n }\n he(r, n, e, t);\n });\n });\n }\n });\n return a;\n }\n function Lt(n, e, t, r) {\n if (e.sseEvent) {\n Rt(n, r, e.sseEvent);\n } else if (e.trigger === \"revealed\") {\n gt();\n ht(n, r, t, e);\n pt(n);\n } else if (e.trigger === \"intersect\") {\n var i = {};\n if (e.root) {\n i.root = ue(n, e.root);\n }\n if (e.threshold) {\n i.threshold = parseFloat(e.threshold);\n }\n var a = new IntersectionObserver(function (e) {\n for (var t = 0; t < e.length; t++) {\n var r = e[t];\n if (r.isIntersecting) {\n ce(n, \"intersect\");\n break;\n }\n }\n }, i);\n a.observe(n);\n ht(n, r, t, e);\n } else if (e.trigger === \"load\") {\n if (!ct(e, n, Wt(\"load\", { elt: n }))) {\n qt(n, r, t, e.delay);\n }\n } else if (e.pollInterval > 0) {\n t.polling = true;\n ot(n, r, e);\n } else {\n ht(n, r, t, e);\n }\n }\n function At(e) {\n if (\n !e.htmxExecuted &&\n Q.config.allowScriptTags &&\n (e.type === \"text/javascript\" || e.type === \"module\" || e.type === \"\")\n ) {\n var t = re().createElement(\"script\");\n oe(e.attributes, function (e) {\n t.setAttribute(e.name, e.value);\n });\n t.textContent = e.textContent;\n t.async = false;\n if (Q.config.inlineScriptNonce) {\n t.nonce = Q.config.inlineScriptNonce;\n }\n var r = e.parentElement;\n try {\n r.insertBefore(t, e);\n } catch (e) {\n b(e);\n } finally {\n if (e.parentElement) {\n e.parentElement.removeChild(e);\n }\n }\n }\n }\n function Nt(e) {\n if (h(e, \"script\")) {\n At(e);\n }\n oe(f(e, \"script\"), function (e) {\n At(e);\n });\n }\n function It(e) {\n var t = e.attributes;\n if (!t) {\n return false;\n }\n for (var r = 0; r < t.length; r++) {\n var n = t[r].name;\n if (g(n, \"hx-on:\") || g(n, \"data-hx-on:\") || g(n, \"hx-on-\") || g(n, \"data-hx-on-\")) {\n return true;\n }\n }\n return false;\n }\n function kt(e) {\n var t = null;\n var r = [];\n if (It(e)) {\n r.push(e);\n }\n if (document.evaluate) {\n var n = document.evaluate(\n './/*[@*[ starts-with(name(), \"hx-on:\") or starts-with(name(), \"data-hx-on:\") or' +\n ' starts-with(name(), \"hx-on-\") or starts-with(name(), \"data-hx-on-\") ]]',\n e,\n );\n while ((t = n.iterateNext())) r.push(t);\n } else if (typeof e.getElementsByTagName === \"function\") {\n var i = e.getElementsByTagName(\"*\");\n for (var a = 0; a < i.length; a++) {\n if (It(i[a])) {\n r.push(i[a]);\n }\n }\n }\n return r;\n }\n function Pt(e) {\n if (e.querySelectorAll) {\n var t = \", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]\";\n var r = e.querySelectorAll(\n i +\n t +\n \", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws],\" +\n \" [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]\",\n );\n return r;\n } else {\n return [];\n }\n }\n function Mt(e) {\n var t = v(e.target, \"button, input[type='submit']\");\n var r = Dt(e);\n if (r) {\n r.lastButtonClicked = t;\n }\n }\n function Xt(e) {\n var t = Dt(e);\n if (t) {\n t.lastButtonClicked = null;\n }\n }\n function Dt(e) {\n var t = v(e.target, \"button, input[type='submit']\");\n if (!t) {\n return;\n }\n var r = p(\"#\" + ee(t, \"form\")) || v(t, \"form\");\n if (!r) {\n return;\n }\n return ae(r);\n }\n function Ut(e) {\n e.addEventListener(\"click\", Mt);\n e.addEventListener(\"focusin\", Mt);\n e.addEventListener(\"focusout\", Xt);\n }\n function Bt(e) {\n var t = Ye(e);\n var r = 0;\n for (var n = 0; n < t.length; n++) {\n const i = t[n];\n if (i === \"{\") {\n r++;\n } else if (i === \"}\") {\n r--;\n }\n }\n return r;\n }\n function Ft(t, e, r) {\n var n = ae(t);\n if (!Array.isArray(n.onHandlers)) {\n n.onHandlers = [];\n }\n var i;\n var a = function (e) {\n return Tr(t, function () {\n if (!i) {\n i = new Function(\"event\", r);\n }\n i.call(t, e);\n });\n };\n t.addEventListener(e, a);\n n.onHandlers.push({ event: e, listener: a });\n }\n function Vt(e) {\n var t = te(e, \"hx-on\");\n if (t) {\n var r = {};\n var n = t.split(\"\\n\");\n var i = null;\n var a = 0;\n while (n.length > 0) {\n var o = n.shift();\n var s = o.match(/^\\s*([a-zA-Z:\\-\\.]+:)(.*)/);\n if (a === 0 && s) {\n o.split(\":\");\n i = s[1].slice(0, -1);\n r[i] = s[2];\n } else {\n r[i] += o;\n }\n a += Bt(o);\n }\n for (var l in r) {\n Ft(e, l, r[l]);\n }\n }\n }\n function jt(e) {\n Ae(e);\n for (var t = 0; t < e.attributes.length; t++) {\n var r = e.attributes[t].name;\n var n = e.attributes[t].value;\n if (g(r, \"hx-on\") || g(r, \"data-hx-on\")) {\n var i = r.indexOf(\"-on\") + 3;\n var a = r.slice(i, i + 1);\n if (a === \"-\" || a === \":\") {\n var o = r.slice(i + 1);\n if (g(o, \":\")) {\n o = \"htmx\" + o;\n } else if (g(o, \"-\")) {\n o = \"htmx:\" + o.slice(1);\n } else if (g(o, \"htmx-\")) {\n o = \"htmx:\" + o.slice(5);\n }\n Ft(e, o, n);\n }\n }\n }\n }\n function _t(t) {\n if (v(t, Q.config.disableSelector)) {\n m(t);\n return;\n }\n var r = ae(t);\n if (r.initHash !== Le(t)) {\n Ne(t);\n r.initHash = Le(t);\n Vt(t);\n ce(t, \"htmx:beforeProcessNode\");\n if (t.value) {\n r.lastValue = t.value;\n }\n var e = it(t);\n var n = Ht(t, r, e);\n if (!n) {\n if (ne(t, \"hx-boost\") === \"true\") {\n lt(t, r, e);\n } else if (o(t, \"hx-trigger\")) {\n e.forEach(function (e) {\n Lt(t, e, r, function () {});\n });\n }\n }\n if (t.tagName === \"FORM\" || (ee(t, \"type\") === \"submit\" && o(t, \"form\"))) {\n Ut(t);\n }\n var i = te(t, \"hx-sse\");\n if (i) {\n St(t, r, i);\n }\n var a = te(t, \"hx-ws\");\n if (a) {\n mt(t, r, a);\n }\n ce(t, \"htmx:afterProcessNode\");\n }\n }\n function zt(e) {\n e = p(e);\n if (v(e, Q.config.disableSelector)) {\n m(e);\n return;\n }\n _t(e);\n oe(Pt(e), function (e) {\n _t(e);\n });\n oe(kt(e), jt);\n }\n function $t(e) {\n return e.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n }\n function Wt(e, t) {\n var r;\n if (window.CustomEvent && typeof window.CustomEvent === \"function\") {\n r = new CustomEvent(e, { bubbles: true, cancelable: true, detail: t });\n } else {\n r = re().createEvent(\"CustomEvent\");\n r.initCustomEvent(e, true, true, t);\n }\n return r;\n }\n function fe(e, t, r) {\n ce(e, t, le({ error: t }, r));\n }\n function Gt(e) {\n return e === \"htmx:afterProcessNode\";\n }\n function R(e, t) {\n oe(Fr(e), function (e) {\n try {\n t(e);\n } catch (e) {\n b(e);\n }\n });\n }\n function b(e) {\n if (console.error) {\n console.error(e);\n } else if (console.log) {\n console.log(\"ERROR: \", e);\n }\n }\n function ce(e, t, r) {\n e = p(e);\n if (r == null) {\n r = {};\n }\n r[\"elt\"] = e;\n var n = Wt(t, r);\n if (Q.logger && !Gt(t)) {\n Q.logger(e, t, r);\n }\n if (r.error) {\n b(r.error);\n ce(e, \"htmx:error\", { errorInfo: r });\n }\n var i = e.dispatchEvent(n);\n var a = $t(t);\n if (i && a !== t) {\n var o = Wt(a, n.detail);\n i = i && e.dispatchEvent(o);\n }\n R(e, function (e) {\n i = i && e.onEvent(t, n) !== false && !n.defaultPrevented;\n });\n return i;\n }\n var Jt = location.pathname + location.search;\n function Zt() {\n var e = re().querySelector(\"[hx-history-elt],[data-hx-history-elt]\");\n return e || re().body;\n }\n function Kt(e, t, r, n) {\n if (!U()) {\n return;\n }\n if (Q.config.historyCacheSize <= 0) {\n localStorage.removeItem(\"htmx-history-cache\");\n return;\n }\n e = B(e);\n var i = E(localStorage.getItem(\"htmx-history-cache\")) || [];\n for (var a = 0; a < i.length; a++) {\n if (i[a].url === e) {\n i.splice(a, 1);\n break;\n }\n }\n var o = { url: e, content: t, title: r, scroll: n };\n ce(re().body, \"htmx:historyItemCreated\", { item: o, cache: i });\n i.push(o);\n while (i.length > Q.config.historyCacheSize) {\n i.shift();\n }\n while (i.length > 0) {\n try {\n localStorage.setItem(\"htmx-history-cache\", JSON.stringify(i));\n break;\n } catch (e) {\n fe(re().body, \"htmx:historyCacheError\", { cause: e, cache: i });\n i.shift();\n }\n }\n }\n function Yt(e) {\n if (!U()) {\n return null;\n }\n e = B(e);\n var t = E(localStorage.getItem(\"htmx-history-cache\")) || [];\n for (var r = 0; r < t.length; r++) {\n if (t[r].url === e) {\n return t[r];\n }\n }\n return null;\n }\n function Qt(e) {\n var t = Q.config.requestClass;\n var r = e.cloneNode(true);\n oe(f(r, \".\" + t), function (e) {\n n(e, t);\n });\n return r.innerHTML;\n }\n function er() {\n var e = Zt();\n var t = Jt || location.pathname + location.search;\n var r;\n try {\n r = re().querySelector('[hx-history=\"false\" i],[data-hx-history=\"false\" i]');\n } catch (e) {\n r = re().querySelector('[hx-history=\"false\"],[data-hx-history=\"false\"]');\n }\n if (!r) {\n ce(re().body, \"htmx:beforeHistorySave\", { path: t, historyElt: e });\n Kt(t, Qt(e), re().title, window.scrollY);\n }\n if (Q.config.historyEnabled)\n history.replaceState({ htmx: true }, re().title, window.location.href);\n }\n function tr(e) {\n if (Q.config.getCacheBusterParam) {\n e = e.replace(/org\\.htmx\\.cache-buster=[^&]*&?/, \"\");\n if (G(e, \"&\") || G(e, \"?\")) {\n e = e.slice(0, -1);\n }\n }\n if (Q.config.historyEnabled) {\n history.pushState({ htmx: true }, \"\", e);\n }\n Jt = e;\n }\n function rr(e) {\n if (Q.config.historyEnabled) history.replaceState({ htmx: true }, \"\", e);\n Jt = e;\n }\n function nr(e) {\n oe(e, function (e) {\n e.call();\n });\n }\n function ir(a) {\n var e = new XMLHttpRequest();\n var o = { path: a, xhr: e };\n ce(re().body, \"htmx:historyCacheMiss\", o);\n e.open(\"GET\", a, true);\n e.setRequestHeader(\"HX-Request\", \"true\");\n e.setRequestHeader(\"HX-History-Restore-Request\", \"true\");\n e.setRequestHeader(\"HX-Current-URL\", re().location.href);\n e.onload = function () {\n if (this.status >= 200 && this.status < 400) {\n ce(re().body, \"htmx:historyCacheMissLoad\", o);\n var e = l(this.response);\n e = e.querySelector(\"[hx-history-elt],[data-hx-history-elt]\") || e;\n var t = Zt();\n var r = T(t);\n var n = Ve(this.response);\n if (n) {\n var i = C(\"title\");\n if (i) {\n i.innerHTML = n;\n } else {\n window.document.title = n;\n }\n }\n Ue(t, e, r);\n nr(r.tasks);\n Jt = a;\n ce(re().body, \"htmx:historyRestore\", {\n path: a,\n cacheMiss: true,\n serverResponse: this.response,\n });\n } else {\n fe(re().body, \"htmx:historyCacheMissLoadError\", o);\n }\n };\n e.send();\n }\n function ar(e) {\n er();\n e = e || location.pathname + location.search;\n var t = Yt(e);\n if (t) {\n var r = l(t.content);\n var n = Zt();\n var i = T(n);\n Ue(n, r, i);\n nr(i.tasks);\n document.title = t.title;\n setTimeout(function () {\n window.scrollTo(0, t.scroll);\n }, 0);\n Jt = e;\n ce(re().body, \"htmx:historyRestore\", { path: e, item: t });\n } else {\n if (Q.config.refreshOnHistoryMiss) {\n window.location.reload(true);\n } else {\n ir(e);\n }\n }\n }\n function or(e) {\n var t = me(e, \"hx-indicator\");\n if (t == null) {\n t = [e];\n }\n oe(t, function (e) {\n var t = ae(e);\n t.requestCount = (t.requestCount || 0) + 1;\n e.classList[\"add\"].call(e.classList, Q.config.requestClass);\n });\n return t;\n }\n function sr(e) {\n var t = me(e, \"hx-disabled-elt\");\n if (t == null) {\n t = [];\n }\n oe(t, function (e) {\n var t = ae(e);\n t.requestCount = (t.requestCount || 0) + 1;\n e.setAttribute(\"disabled\", \"\");\n });\n return t;\n }\n function lr(e, t) {\n oe(e, function (e) {\n var t = ae(e);\n t.requestCount = (t.requestCount || 0) - 1;\n if (t.requestCount === 0) {\n e.classList[\"remove\"].call(e.classList, Q.config.requestClass);\n }\n });\n oe(t, function (e) {\n var t = ae(e);\n t.requestCount = (t.requestCount || 0) - 1;\n if (t.requestCount === 0) {\n e.removeAttribute(\"disabled\");\n }\n });\n }\n function ur(e, t) {\n for (var r = 0; r < e.length; r++) {\n var n = e[r];\n if (n.isSameNode(t)) {\n return true;\n }\n }\n return false;\n }\n function fr(e) {\n if (e.name === \"\" || e.name == null || e.disabled || v(e, \"fieldset[disabled]\")) {\n return false;\n }\n if (\n e.type === \"button\" ||\n e.type === \"submit\" ||\n e.tagName === \"image\" ||\n e.tagName === \"reset\" ||\n e.tagName === \"file\"\n ) {\n return false;\n }\n if (e.type === \"checkbox\" || e.type === \"radio\") {\n return e.checked;\n }\n return true;\n }\n function cr(e, t, r) {\n if (e != null && t != null) {\n var n = r[e];\n if (n === undefined) {\n r[e] = t;\n } else if (Array.isArray(n)) {\n if (Array.isArray(t)) {\n r[e] = n.concat(t);\n } else {\n n.push(t);\n }\n } else {\n if (Array.isArray(t)) {\n r[e] = [n].concat(t);\n } else {\n r[e] = [n, t];\n }\n }\n }\n }\n function hr(t, r, n, e, i) {\n if (e == null || ur(t, e)) {\n return;\n } else {\n t.push(e);\n }\n if (fr(e)) {\n var a = ee(e, \"name\");\n var o = e.value;\n if (e.multiple && e.tagName === \"SELECT\") {\n o = M(e.querySelectorAll(\"option:checked\")).map(function (e) {\n return e.value;\n });\n }\n if (e.files) {\n o = M(e.files);\n }\n cr(a, o, r);\n if (i) {\n vr(e, n);\n }\n }\n if (h(e, \"form\")) {\n var s = e.elements;\n oe(s, function (e) {\n hr(t, r, n, e, i);\n });\n }\n }\n function vr(e, t) {\n if (e.willValidate) {\n ce(e, \"htmx:validation:validate\");\n if (!e.checkValidity()) {\n t.push({ elt: e, message: e.validationMessage, validity: e.validity });\n ce(e, \"htmx:validation:failed\", { message: e.validationMessage, validity: e.validity });\n }\n }\n }\n function dr(e, t) {\n var r = [];\n var n = {};\n var i = {};\n var a = [];\n var o = ae(e);\n if (o.lastButtonClicked && !se(o.lastButtonClicked)) {\n o.lastButtonClicked = null;\n }\n var s = (h(e, \"form\") && e.noValidate !== true) || te(e, \"hx-validate\") === \"true\";\n if (o.lastButtonClicked) {\n s = s && o.lastButtonClicked.formNoValidate !== true;\n }\n if (t !== \"get\") {\n hr(r, i, a, v(e, \"form\"), s);\n }\n hr(r, n, a, e, s);\n if (\n o.lastButtonClicked ||\n e.tagName === \"BUTTON\" ||\n (e.tagName === \"INPUT\" && ee(e, \"type\") === \"submit\")\n ) {\n var l = o.lastButtonClicked || e;\n var u = ee(l, \"name\");\n cr(u, l.value, i);\n }\n var f = me(e, \"hx-include\");\n oe(f, function (e) {\n hr(r, n, a, e, s);\n if (!h(e, \"form\")) {\n oe(e.querySelectorAll(rt), function (e) {\n hr(r, n, a, e, s);\n });\n }\n });\n n = le(n, i);\n return { errors: a, values: n };\n }\n function gr(e, t, r) {\n if (e !== \"\") {\n e += \"&\";\n }\n if (String(r) === \"[object Object]\") {\n r = JSON.stringify(r);\n }\n var n = encodeURIComponent(r);\n e += encodeURIComponent(t) + \"=\" + n;\n return e;\n }\n function pr(e) {\n var t = \"\";\n for (var r in e) {\n if (e.hasOwnProperty(r)) {\n var n = e[r];\n if (Array.isArray(n)) {\n oe(n, function (e) {\n t = gr(t, r, e);\n });\n } else {\n t = gr(t, r, n);\n }\n }\n }\n return t;\n }\n function mr(e) {\n var t = new FormData();\n for (var r in e) {\n if (e.hasOwnProperty(r)) {\n var n = e[r];\n if (Array.isArray(n)) {\n oe(n, function (e) {\n t.append(r, e);\n });\n } else {\n t.append(r, n);\n }\n }\n }\n return t;\n }\n function xr(e, t, r) {\n var n = {\n \"HX-Request\": \"true\",\n \"HX-Trigger\": ee(e, \"id\"),\n \"HX-Trigger-Name\": ee(e, \"name\"),\n \"HX-Target\": te(t, \"id\"),\n \"HX-Current-URL\": re().location.href,\n };\n Rr(e, \"hx-headers\", false, n);\n if (r !== undefined) {\n n[\"HX-Prompt\"] = r;\n }\n if (ae(e).boosted) {\n n[\"HX-Boosted\"] = \"true\";\n }\n return n;\n }\n function yr(t, e) {\n var r = ne(e, \"hx-params\");\n if (r) {\n if (r === \"none\") {\n return {};\n } else if (r === \"*\") {\n return t;\n } else if (r.indexOf(\"not \") === 0) {\n oe(r.substr(4).split(\",\"), function (e) {\n e = e.trim();\n delete t[e];\n });\n return t;\n } else {\n var n = {};\n oe(r.split(\",\"), function (e) {\n e = e.trim();\n n[e] = t[e];\n });\n return n;\n }\n } else {\n return t;\n }\n }\n function br(e) {\n return ee(e, \"href\") && ee(e, \"href\").indexOf(\"#\") >= 0;\n }\n function wr(e, t) {\n var r = t ? t : ne(e, \"hx-swap\");\n var n = {\n swapStyle: ae(e).boosted ? \"innerHTML\" : Q.config.defaultSwapStyle,\n swapDelay: Q.config.defaultSwapDelay,\n settleDelay: Q.config.defaultSettleDelay,\n };\n if (Q.config.scrollIntoViewOnBoost && ae(e).boosted && !br(e)) {\n n[\"show\"] = \"top\";\n }\n if (r) {\n var i = D(r);\n if (i.length > 0) {\n for (var a = 0; a < i.length; a++) {\n var o = i[a];\n if (o.indexOf(\"swap:\") === 0) {\n n[\"swapDelay\"] = d(o.substr(5));\n } else if (o.indexOf(\"settle:\") === 0) {\n n[\"settleDelay\"] = d(o.substr(7));\n } else if (o.indexOf(\"transition:\") === 0) {\n n[\"transition\"] = o.substr(11) === \"true\";\n } else if (o.indexOf(\"ignoreTitle:\") === 0) {\n n[\"ignoreTitle\"] = o.substr(12) === \"true\";\n } else if (o.indexOf(\"scroll:\") === 0) {\n var s = o.substr(7);\n var l = s.split(\":\");\n var u = l.pop();\n var f = l.length > 0 ? l.join(\":\") : null;\n n[\"scroll\"] = u;\n n[\"scrollTarget\"] = f;\n } else if (o.indexOf(\"show:\") === 0) {\n var c = o.substr(5);\n var l = c.split(\":\");\n var h = l.pop();\n var f = l.length > 0 ? l.join(\":\") : null;\n n[\"show\"] = h;\n n[\"showTarget\"] = f;\n } else if (o.indexOf(\"focus-scroll:\") === 0) {\n var v = o.substr(\"focus-scroll:\".length);\n n[\"focusScroll\"] = v == \"true\";\n } else if (a == 0) {\n n[\"swapStyle\"] = o;\n } else {\n b(\"Unknown modifier in hx-swap: \" + o);\n }\n }\n }\n }\n return n;\n }\n function Sr(e) {\n return (\n ne(e, \"hx-encoding\") === \"multipart/form-data\" ||\n (h(e, \"form\") && ee(e, \"enctype\") === \"multipart/form-data\")\n );\n }\n function Er(t, r, n) {\n var i = null;\n R(r, function (e) {\n if (i == null) {\n i = e.encodeParameters(t, n, r);\n }\n });\n if (i != null) {\n return i;\n } else {\n if (Sr(r)) {\n return mr(n);\n } else {\n return pr(n);\n }\n }\n }\n function T(e) {\n return { tasks: [], elts: [e] };\n }\n function Cr(e, t) {\n var r = e[0];\n var n = e[e.length - 1];\n if (t.scroll) {\n var i = null;\n if (t.scrollTarget) {\n i = ue(r, t.scrollTarget);\n }\n if (t.scroll === \"top\" && (r || i)) {\n i = i || r;\n i.scrollTop = 0;\n }\n if (t.scroll === \"bottom\" && (n || i)) {\n i = i || n;\n i.scrollTop = i.scrollHeight;\n }\n }\n if (t.show) {\n var i = null;\n if (t.showTarget) {\n var a = t.showTarget;\n if (t.showTarget === \"window\") {\n a = \"body\";\n }\n i = ue(r, a);\n }\n if (t.show === \"top\" && (r || i)) {\n i = i || r;\n i.scrollIntoView({ block: \"start\", behavior: Q.config.scrollBehavior });\n }\n if (t.show === \"bottom\" && (n || i)) {\n i = i || n;\n i.scrollIntoView({ block: \"end\", behavior: Q.config.scrollBehavior });\n }\n }\n }\n function Rr(e, t, r, n) {\n if (n == null) {\n n = {};\n }\n if (e == null) {\n return n;\n }\n var i = te(e, t);\n if (i) {\n var a = i.trim();\n var o = r;\n if (a === \"unset\") {\n return null;\n }\n if (a.indexOf(\"javascript:\") === 0) {\n a = a.substr(11);\n o = true;\n } else if (a.indexOf(\"js:\") === 0) {\n a = a.substr(3);\n o = true;\n }\n if (a.indexOf(\"{\") !== 0) {\n a = \"{\" + a + \"}\";\n }\n var s;\n if (o) {\n s = Tr(\n e,\n function () {\n return Function(\"return (\" + a + \")\")();\n },\n {},\n );\n } else {\n s = E(a);\n }\n for (var l in s) {\n if (s.hasOwnProperty(l)) {\n if (n[l] == null) {\n n[l] = s[l];\n }\n }\n }\n }\n return Rr(u(e), t, r, n);\n }\n function Tr(e, t, r) {\n if (Q.config.allowEval) {\n return t();\n } else {\n fe(e, \"htmx:evalDisallowedError\");\n return r;\n }\n }\n function Or(e, t) {\n return Rr(e, \"hx-vars\", true, t);\n }\n function qr(e, t) {\n return Rr(e, \"hx-vals\", false, t);\n }\n function Hr(e) {\n return le(Or(e), qr(e));\n }\n function Lr(t, r, n) {\n if (n !== null) {\n try {\n t.setRequestHeader(r, n);\n } catch (e) {\n t.setRequestHeader(r, encodeURIComponent(n));\n t.setRequestHeader(r + \"-URI-AutoEncoded\", \"true\");\n }\n }\n }\n function Ar(t) {\n if (t.responseURL && typeof URL !== \"undefined\") {\n try {\n var e = new URL(t.responseURL);\n return e.pathname + e.search;\n } catch (e) {\n fe(re().body, \"htmx:badResponseUrl\", { url: t.responseURL });\n }\n }\n }\n function O(e, t) {\n return t.test(e.getAllResponseHeaders());\n }\n function Nr(e, t, r) {\n e = e.toLowerCase();\n if (r) {\n if (r instanceof Element || I(r, \"String\")) {\n return he(e, t, null, null, { targetOverride: p(r), returnPromise: true });\n } else {\n return he(e, t, p(r.source), r.event, {\n handler: r.handler,\n headers: r.headers,\n values: r.values,\n targetOverride: p(r.target),\n swapOverride: r.swap,\n select: r.select,\n returnPromise: true,\n });\n }\n } else {\n return he(e, t, null, null, { returnPromise: true });\n }\n }\n function Ir(e) {\n var t = [];\n while (e) {\n t.push(e);\n e = e.parentElement;\n }\n return t;\n }\n function kr(e, t, r) {\n var n;\n var i;\n if (typeof URL === \"function\") {\n i = new URL(t, document.location.href);\n var a = document.location.origin;\n n = a === i.origin;\n } else {\n i = t;\n n = g(t, document.location.origin);\n }\n if (Q.config.selfRequestsOnly) {\n if (!n) {\n return false;\n }\n }\n return ce(e, \"htmx:validateUrl\", le({ url: i, sameHost: n }, r));\n }\n function he(t, r, n, i, a, e) {\n var o = null;\n var s = null;\n a = a != null ? a : {};\n if (a.returnPromise && typeof Promise !== \"undefined\") {\n var l = new Promise(function (e, t) {\n o = e;\n s = t;\n });\n }\n if (n == null) {\n n = re().body;\n }\n var M = a.handler || Mr;\n var X = a.select || null;\n if (!se(n)) {\n ie(o);\n return l;\n }\n var u = a.targetOverride || ye(n);\n if (u == null || u == pe) {\n fe(n, \"htmx:targetError\", { target: te(n, \"hx-target\") });\n ie(s);\n return l;\n }\n var f = ae(n);\n var c = f.lastButtonClicked;\n if (c) {\n var h = ee(c, \"formaction\");\n if (h != null) {\n r = h;\n }\n var v = ee(c, \"formmethod\");\n if (v != null) {\n if (v.toLowerCase() !== \"dialog\") {\n t = v;\n }\n }\n }\n var d = ne(n, \"hx-confirm\");\n if (e === undefined) {\n var D = function (e) {\n return he(t, r, n, i, a, !!e);\n };\n var U = {\n target: u,\n elt: n,\n path: r,\n verb: t,\n triggeringEvent: i,\n etc: a,\n issueRequest: D,\n question: d,\n };\n if (ce(n, \"htmx:confirm\", U) === false) {\n ie(o);\n return l;\n }\n }\n var g = n;\n var p = ne(n, \"hx-sync\");\n var m = null;\n var x = false;\n if (p) {\n var B = p.split(\":\");\n var F = B[0].trim();\n if (F === \"this\") {\n g = xe(n, \"hx-sync\");\n } else {\n g = ue(n, F);\n }\n p = (B[1] || \"drop\").trim();\n f = ae(g);\n if (p === \"drop\" && f.xhr && f.abortable !== true) {\n ie(o);\n return l;\n } else if (p === \"abort\") {\n if (f.xhr) {\n ie(o);\n return l;\n } else {\n x = true;\n }\n } else if (p === \"replace\") {\n ce(g, \"htmx:abort\");\n } else if (p.indexOf(\"queue\") === 0) {\n var V = p.split(\" \");\n m = (V[1] || \"last\").trim();\n }\n }\n if (f.xhr) {\n if (f.abortable) {\n ce(g, \"htmx:abort\");\n } else {\n if (m == null) {\n if (i) {\n var y = ae(i);\n if (y && y.triggerSpec && y.triggerSpec.queue) {\n m = y.triggerSpec.queue;\n }\n }\n if (m == null) {\n m = \"last\";\n }\n }\n if (f.queuedRequests == null) {\n f.queuedRequests = [];\n }\n if (m === \"first\" && f.queuedRequests.length === 0) {\n f.queuedRequests.push(function () {\n he(t, r, n, i, a);\n });\n } else if (m === \"all\") {\n f.queuedRequests.push(function () {\n he(t, r, n, i, a);\n });\n } else if (m === \"last\") {\n f.queuedRequests = [];\n f.queuedRequests.push(function () {\n he(t, r, n, i, a);\n });\n }\n ie(o);\n return l;\n }\n }\n var b = new XMLHttpRequest();\n f.xhr = b;\n f.abortable = x;\n var w = function () {\n f.xhr = null;\n f.abortable = false;\n if (f.queuedRequests != null && f.queuedRequests.length > 0) {\n var e = f.queuedRequests.shift();\n e();\n }\n };\n var j = ne(n, \"hx-prompt\");\n if (j) {\n var S = prompt(j);\n if (S === null || !ce(n, \"htmx:prompt\", { prompt: S, target: u })) {\n ie(o);\n w();\n return l;\n }\n }\n if (d && !e) {\n if (!confirm(d)) {\n ie(o);\n w();\n return l;\n }\n }\n var E = xr(n, u, S);\n if (t !== \"get\" && !Sr(n)) {\n E[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n }\n if (a.headers) {\n E = le(E, a.headers);\n }\n var _ = dr(n, t);\n var C = _.errors;\n var R = _.values;\n if (a.values) {\n R = le(R, a.values);\n }\n var z = Hr(n);\n var $ = le(R, z);\n var T = yr($, n);\n if (Q.config.getCacheBusterParam && t === \"get\") {\n T[\"org.htmx.cache-buster\"] = ee(u, \"id\") || \"true\";\n }\n if (r == null || r === \"\") {\n r = re().location.href;\n }\n var O = Rr(n, \"hx-request\");\n var W = ae(n).boosted;\n var q = Q.config.methodsThatUseUrlParams.indexOf(t) >= 0;\n var H = {\n boosted: W,\n useUrlParams: q,\n parameters: T,\n unfilteredParameters: $,\n headers: E,\n target: u,\n verb: t,\n errors: C,\n withCredentials: a.credentials || O.credentials || Q.config.withCredentials,\n timeout: a.timeout || O.timeout || Q.config.timeout,\n path: r,\n triggeringEvent: i,\n };\n if (!ce(n, \"htmx:configRequest\", H)) {\n ie(o);\n w();\n return l;\n }\n r = H.path;\n t = H.verb;\n E = H.headers;\n T = H.parameters;\n C = H.errors;\n q = H.useUrlParams;\n if (C && C.length > 0) {\n ce(n, \"htmx:validation:halted\", H);\n ie(o);\n w();\n return l;\n }\n var G = r.split(\"#\");\n var J = G[0];\n var L = G[1];\n var A = r;\n if (q) {\n A = J;\n var Z = Object.keys(T).length !== 0;\n if (Z) {\n if (A.indexOf(\"?\") < 0) {\n A += \"?\";\n } else {\n A += \"&\";\n }\n A += pr(T);\n if (L) {\n A += \"#\" + L;\n }\n }\n }\n if (!kr(n, A, H)) {\n fe(n, \"htmx:invalidPath\", H);\n ie(s);\n return l;\n }\n b.open(t.toUpperCase(), A, true);\n b.overrideMimeType(\"text/html\");\n b.withCredentials = H.withCredentials;\n b.timeout = H.timeout;\n if (O.noHeaders) {\n } else {\n for (var N in E) {\n if (E.hasOwnProperty(N)) {\n var K = E[N];\n Lr(b, N, K);\n }\n }\n }\n var I = {\n xhr: b,\n target: u,\n requestConfig: H,\n etc: a,\n boosted: W,\n select: X,\n pathInfo: { requestPath: r, finalRequestPath: A, anchor: L },\n };\n b.onload = function () {\n try {\n var e = Ir(n);\n I.pathInfo.responsePath = Ar(b);\n M(n, I);\n lr(k, P);\n ce(n, \"htmx:afterRequest\", I);\n ce(n, \"htmx:afterOnLoad\", I);\n if (!se(n)) {\n var t = null;\n while (e.length > 0 && t == null) {\n var r = e.shift();\n if (se(r)) {\n t = r;\n }\n }\n if (t) {\n ce(t, \"htmx:afterRequest\", I);\n ce(t, \"htmx:afterOnLoad\", I);\n }\n }\n ie(o);\n w();\n } catch (e) {\n fe(n, \"htmx:onLoadError\", le({ error: e }, I));\n throw e;\n }\n };\n b.onerror = function () {\n lr(k, P);\n fe(n, \"htmx:afterRequest\", I);\n fe(n, \"htmx:sendError\", I);\n ie(s);\n w();\n };\n b.onabort = function () {\n lr(k, P);\n fe(n, \"htmx:afterRequest\", I);\n fe(n, \"htmx:sendAbort\", I);\n ie(s);\n w();\n };\n b.ontimeout = function () {\n lr(k, P);\n fe(n, \"htmx:afterRequest\", I);\n fe(n, \"htmx:timeout\", I);\n ie(s);\n w();\n };\n if (!ce(n, \"htmx:beforeRequest\", I)) {\n ie(o);\n w();\n return l;\n }\n var k = or(n);\n var P = sr(n);\n oe([\"loadstart\", \"loadend\", \"progress\", \"abort\"], function (t) {\n oe([b, b.upload], function (e) {\n e.addEventListener(t, function (e) {\n ce(n, \"htmx:xhr:\" + t, {\n lengthComputable: e.lengthComputable,\n loaded: e.loaded,\n total: e.total,\n });\n });\n });\n });\n ce(n, \"htmx:beforeSend\", I);\n var Y = q ? null : Er(b, n, T);\n b.send(Y);\n return l;\n }\n function Pr(e, t) {\n var r = t.xhr;\n var n = null;\n var i = null;\n if (O(r, /HX-Push:/i)) {\n n = r.getResponseHeader(\"HX-Push\");\n i = \"push\";\n } else if (O(r, /HX-Push-Url:/i)) {\n n = r.getResponseHeader(\"HX-Push-Url\");\n i = \"push\";\n } else if (O(r, /HX-Replace-Url:/i)) {\n n = r.getResponseHeader(\"HX-Replace-Url\");\n i = \"replace\";\n }\n if (n) {\n if (n === \"false\") {\n return {};\n } else {\n return { type: i, path: n };\n }\n }\n var a = t.pathInfo.finalRequestPath;\n var o = t.pathInfo.responsePath;\n var s = ne(e, \"hx-push-url\");\n var l = ne(e, \"hx-replace-url\");\n var u = ae(e).boosted;\n var f = null;\n var c = null;\n if (s) {\n f = \"push\";\n c = s;\n } else if (l) {\n f = \"replace\";\n c = l;\n } else if (u) {\n f = \"push\";\n c = o || a;\n }\n if (c) {\n if (c === \"false\") {\n return {};\n }\n if (c === \"true\") {\n c = o || a;\n }\n if (t.pathInfo.anchor && c.indexOf(\"#\") === -1) {\n c = c + \"#\" + t.pathInfo.anchor;\n }\n return { type: f, path: c };\n } else {\n return {};\n }\n }\n function Mr(l, u) {\n var f = u.xhr;\n var c = u.target;\n var e = u.etc;\n var t = u.requestConfig;\n var h = u.select;\n if (!ce(l, \"htmx:beforeOnLoad\", u)) return;\n if (O(f, /HX-Trigger:/i)) {\n _e(f, \"HX-Trigger\", l);\n }\n if (O(f, /HX-Location:/i)) {\n er();\n var r = f.getResponseHeader(\"HX-Location\");\n var v;\n if (r.indexOf(\"{\") === 0) {\n v = E(r);\n r = v[\"path\"];\n delete v[\"path\"];\n }\n Nr(\"GET\", r, v).then(function () {\n tr(r);\n });\n return;\n }\n var n = O(f, /HX-Refresh:/i) && \"true\" === f.getResponseHeader(\"HX-Refresh\");\n if (O(f, /HX-Redirect:/i)) {\n location.href = f.getResponseHeader(\"HX-Redirect\");\n n && location.reload();\n return;\n }\n if (n) {\n location.reload();\n return;\n }\n if (O(f, /HX-Retarget:/i)) {\n if (f.getResponseHeader(\"HX-Retarget\") === \"this\") {\n u.target = l;\n } else {\n u.target = ue(l, f.getResponseHeader(\"HX-Retarget\"));\n }\n }\n var d = Pr(l, u);\n var i = f.status >= 200 && f.status < 400 && f.status !== 204;\n var g = f.response;\n var a = f.status >= 400;\n var p = Q.config.ignoreTitle;\n var o = le({ shouldSwap: i, serverResponse: g, isError: a, ignoreTitle: p }, u);\n if (!ce(c, \"htmx:beforeSwap\", o)) return;\n c = o.target;\n g = o.serverResponse;\n a = o.isError;\n p = o.ignoreTitle;\n u.target = c;\n u.failed = a;\n u.successful = !a;\n if (o.shouldSwap) {\n if (f.status === 286) {\n at(l);\n }\n R(l, function (e) {\n g = e.transformResponse(g, f, l);\n });\n if (d.type) {\n er();\n }\n var s = e.swapOverride;\n if (O(f, /HX-Reswap:/i)) {\n s = f.getResponseHeader(\"HX-Reswap\");\n }\n var v = wr(l, s);\n if (v.hasOwnProperty(\"ignoreTitle\")) {\n p = v.ignoreTitle;\n }\n c.classList.add(Q.config.swappingClass);\n var m = null;\n var x = null;\n var y = function () {\n try {\n var e = document.activeElement;\n var t = {};\n try {\n t = { elt: e, start: e ? e.selectionStart : null, end: e ? e.selectionEnd : null };\n } catch (e) {}\n var r;\n if (h) {\n r = h;\n }\n if (O(f, /HX-Reselect:/i)) {\n r = f.getResponseHeader(\"HX-Reselect\");\n }\n if (d.type) {\n ce(re().body, \"htmx:beforeHistoryUpdate\", le({ history: d }, u));\n if (d.type === \"push\") {\n tr(d.path);\n ce(re().body, \"htmx:pushedIntoHistory\", { path: d.path });\n } else {\n rr(d.path);\n ce(re().body, \"htmx:replacedInHistory\", { path: d.path });\n }\n }\n var n = T(c);\n je(v.swapStyle, c, l, g, n, r);\n if (t.elt && !se(t.elt) && ee(t.elt, \"id\")) {\n var i = document.getElementById(ee(t.elt, \"id\"));\n var a = {\n preventScroll:\n v.focusScroll !== undefined ? !v.focusScroll : !Q.config.defaultFocusScroll,\n };\n if (i) {\n if (t.start && i.setSelectionRange) {\n try {\n i.setSelectionRange(t.start, t.end);\n } catch (e) {}\n }\n i.focus(a);\n }\n }\n c.classList.remove(Q.config.swappingClass);\n oe(n.elts, function (e) {\n if (e.classList) {\n e.classList.add(Q.config.settlingClass);\n }\n ce(e, \"htmx:afterSwap\", u);\n });\n if (O(f, /HX-Trigger-After-Swap:/i)) {\n var o = l;\n if (!se(l)) {\n o = re().body;\n }\n _e(f, \"HX-Trigger-After-Swap\", o);\n }\n var s = function () {\n oe(n.tasks, function (e) {\n e.call();\n });\n oe(n.elts, function (e) {\n if (e.classList) {\n e.classList.remove(Q.config.settlingClass);\n }\n ce(e, \"htmx:afterSettle\", u);\n });\n if (u.pathInfo.anchor) {\n var e = re().getElementById(u.pathInfo.anchor);\n if (e) {\n e.scrollIntoView({ block: \"start\", behavior: \"auto\" });\n }\n }\n if (n.title && !p) {\n var t = C(\"title\");\n if (t) {\n t.innerHTML = n.title;\n } else {\n window.document.title = n.title;\n }\n }\n Cr(n.elts, v);\n if (O(f, /HX-Trigger-After-Settle:/i)) {\n var r = l;\n if (!se(l)) {\n r = re().body;\n }\n _e(f, \"HX-Trigger-After-Settle\", r);\n }\n ie(m);\n };\n if (v.settleDelay > 0) {\n setTimeout(s, v.settleDelay);\n } else {\n s();\n }\n } catch (e) {\n fe(l, \"htmx:swapError\", u);\n ie(x);\n throw e;\n }\n };\n var b = Q.config.globalViewTransitions;\n if (v.hasOwnProperty(\"transition\")) {\n b = v.transition;\n }\n if (\n b &&\n ce(l, \"htmx:beforeTransition\", u) &&\n typeof Promise !== \"undefined\" &&\n document.startViewTransition\n ) {\n var w = new Promise(function (e, t) {\n m = e;\n x = t;\n });\n var S = y;\n y = function () {\n document.startViewTransition(function () {\n S();\n return w;\n });\n };\n }\n if (v.swapDelay > 0) {\n setTimeout(y, v.swapDelay);\n } else {\n y();\n }\n }\n if (a) {\n fe(\n l,\n \"htmx:responseError\",\n le(\n { error: \"Response Status Error Code \" + f.status + \" from \" + u.pathInfo.requestPath },\n u,\n ),\n );\n }\n }\n var Xr = {};\n function Dr() {\n return {\n init: function (e) {\n return null;\n },\n onEvent: function (e, t) {\n return true;\n },\n transformResponse: function (e, t, r) {\n return e;\n },\n isInlineSwap: function (e) {\n return false;\n },\n handleSwap: function (e, t, r, n) {\n return false;\n },\n encodeParameters: function (e, t, r) {\n return null;\n },\n };\n }\n function Ur(e, t) {\n if (t.init) {\n t.init(r);\n }\n Xr[e] = le(Dr(), t);\n }\n function Br(e) {\n delete Xr[e];\n }\n function Fr(e, r, n) {\n if (e == undefined) {\n return r;\n }\n if (r == undefined) {\n r = [];\n }\n if (n == undefined) {\n n = [];\n }\n var t = te(e, \"hx-ext\");\n if (t) {\n oe(t.split(\",\"), function (e) {\n e = e.replace(/ /g, \"\");\n if (e.slice(0, 7) == \"ignore:\") {\n n.push(e.slice(7));\n return;\n }\n if (n.indexOf(e) < 0) {\n var t = Xr[e];\n if (t && r.indexOf(t) < 0) {\n r.push(t);\n }\n }\n });\n }\n return Fr(u(e), r, n);\n }\n var Vr = false;\n re().addEventListener(\"DOMContentLoaded\", function () {\n Vr = true;\n });\n function jr(e) {\n if (Vr || re().readyState === \"complete\") {\n e();\n } else {\n re().addEventListener(\"DOMContentLoaded\", e);\n }\n }\n function _r() {\n if (Q.config.includeIndicatorStyles !== false) {\n re().head.insertAdjacentHTML(\n \"beforeend\",\n \"<style> .\" +\n Q.config.indicatorClass +\n \"{opacity:0} .\" +\n Q.config.requestClass +\n \" .\" +\n Q.config.indicatorClass +\n \"{opacity:1; transition: opacity 200ms ease-in;} .\" +\n Q.config.requestClass +\n \".\" +\n Q.config.indicatorClass +\n \"{opacity:1; transition: opacity 200ms ease-in;} </style>\",\n );\n }\n }\n function zr() {\n var e = re().querySelector('meta[name=\"htmx-config\"]');\n if (e) {\n return E(e.content);\n } else {\n return null;\n }\n }\n function $r() {\n var e = zr();\n if (e) {\n Q.config = le(Q.config, e);\n }\n }\n jr(function () {\n $r();\n _r();\n var e = re().body;\n zt(e);\n var t = re().querySelectorAll(\"[hx-trigger='restored'],[data-hx-trigger='restored']\");\n e.addEventListener(\"htmx:abort\", function (e) {\n var t = e.target;\n var r = ae(t);\n if (r && r.xhr) {\n r.xhr.abort();\n }\n });\n const r = window.onpopstate ? window.onpopstate.bind(window) : null;\n window.onpopstate = function (e) {\n if (e.state && e.state.htmx) {\n ar();\n oe(t, function (e) {\n ce(e, \"htmx:restored\", { document: re(), triggerEvent: ce });\n });\n } else {\n if (r) {\n r(e);\n }\n }\n };\n setTimeout(function () {\n ce(e, \"htmx:load\", {});\n e = null;\n }, 0);\n });\n return Q;\n })();\n});\n`;\n"],"mappings":";;;;AASA,MAAa,aAAa,OAAO,GAAG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/assets/mark.ts
|
|
5
|
+
/** The StoryShelf brand mark (an SVG): the favicon and the default sidebar logo. */
|
|
6
|
+
const markSource = String.raw`<svg xmlns='http://www.w3.org/2000/svg' width='64' height='64' viewBox='0 0 64 64' role='img' aria-label='StoryShelf'><rect width='64' height='64' rx='14' fill='#1d5fcf'/><path d='M15 11 H49 A4 4 0 0 1 53 15 V15 A2 2 0 0 1 51 17 H13 A2 2 0 0 1 11 15 V15 A4 4 0 0 1 15 11 Z' fill='#ffffff'/><path d='M13 29 H51 A2 2 0 0 1 53 31 V33 A2 2 0 0 1 51 35 H13 A2 2 0 0 1 11 33 V31 A2 2 0 0 1 13 29 Z' fill='#ffffff'/><path d='M13 47 H51 A2 2 0 0 1 53 49 V49 A4 4 0 0 1 49 53 H15 A4 4 0 0 1 11 49 V49 A2 2 0 0 1 13 47 Z' fill='#ffffff'/><path d='M16 19 H16 A2 2 0 0 1 18 21 V27 A2 2 0 0 1 16 29 H16 A2 2 0 0 1 14 27 V21 A2 2 0 0 1 16 19 Z' fill='#ffffff'/><path d='M22 21 H22 A2 2 0 0 1 24 23 V27 A2 2 0 0 1 22 29 H22 A2 2 0 0 1 20 27 V23 A2 2 0 0 1 22 21 Z' fill='#ffffff'/><path d='M28 23 H28 A2 2 0 0 1 30 25 V27 A2 2 0 0 1 28 29 H28 A2 2 0 0 1 26 27 V25 A2 2 0 0 1 28 23 Z' fill='#ffffff'/><path d='M48 37 H48 A2 2 0 0 1 50 39 V45 A2 2 0 0 1 48 47 H48 A2 2 0 0 1 46 45 V39 A2 2 0 0 1 48 37 Z' fill='#ffffff'/><path d='M42 39 H42 A2 2 0 0 1 44 41 V45 A2 2 0 0 1 42 47 H42 A2 2 0 0 1 40 45 V41 A2 2 0 0 1 42 39 Z' fill='#ffffff'/><path d='M36 41 H36 A2 2 0 0 1 38 43 V45 A2 2 0 0 1 36 47 H36 A2 2 0 0 1 34 45 V43 A2 2 0 0 1 36 41 Z' fill='#ffffff'/></svg>`;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { markSource };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=mark.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mark.mjs","names":[],"sources":["../../src/assets/mark.ts"],"sourcesContent":["/** The StoryShelf brand mark (an SVG): the favicon and the default sidebar logo. */\nexport const markSource = String.raw`<svg xmlns='http://www.w3.org/2000/svg' width='64' height='64' viewBox='0 0 64 64' role='img' aria-label='StoryShelf'><rect width='64' height='64' rx='14' fill='#1d5fcf'/><path d='M15 11 H49 A4 4 0 0 1 53 15 V15 A2 2 0 0 1 51 17 H13 A2 2 0 0 1 11 15 V15 A4 4 0 0 1 15 11 Z' fill='#ffffff'/><path d='M13 29 H51 A2 2 0 0 1 53 31 V33 A2 2 0 0 1 51 35 H13 A2 2 0 0 1 11 33 V31 A2 2 0 0 1 13 29 Z' fill='#ffffff'/><path d='M13 47 H51 A2 2 0 0 1 53 49 V49 A4 4 0 0 1 49 53 H15 A4 4 0 0 1 11 49 V49 A2 2 0 0 1 13 47 Z' fill='#ffffff'/><path d='M16 19 H16 A2 2 0 0 1 18 21 V27 A2 2 0 0 1 16 29 H16 A2 2 0 0 1 14 27 V21 A2 2 0 0 1 16 19 Z' fill='#ffffff'/><path d='M22 21 H22 A2 2 0 0 1 24 23 V27 A2 2 0 0 1 22 29 H22 A2 2 0 0 1 20 27 V23 A2 2 0 0 1 22 21 Z' fill='#ffffff'/><path d='M28 23 H28 A2 2 0 0 1 30 25 V27 A2 2 0 0 1 28 29 H28 A2 2 0 0 1 26 27 V25 A2 2 0 0 1 28 23 Z' fill='#ffffff'/><path d='M48 37 H48 A2 2 0 0 1 50 39 V45 A2 2 0 0 1 48 47 H48 A2 2 0 0 1 46 45 V39 A2 2 0 0 1 48 37 Z' fill='#ffffff'/><path d='M42 39 H42 A2 2 0 0 1 44 41 V45 A2 2 0 0 1 42 47 H42 A2 2 0 0 1 40 45 V41 A2 2 0 0 1 42 39 Z' fill='#ffffff'/><path d='M36 41 H36 A2 2 0 0 1 38 43 V45 A2 2 0 0 1 36 47 H36 A2 2 0 0 1 34 45 V43 A2 2 0 0 1 36 41 Z' fill='#ffffff'/></svg>`;\n"],"mappings":";;;;;AACA,MAAa,aAAa,OAAO,GAAG"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { NOTIFICATIONS_PAUSED_TEXT } from "./notifications-pause.mjs";
|
|
5
|
+
import { startConsumer, trackBackground } from "./lifecycle.mjs";
|
|
6
|
+
import { checkCredentialsAfterSetup } from "./credentials.mjs";
|
|
7
|
+
import { logHostingOnlyMode } from "./hidden-data.mjs";
|
|
8
|
+
import { requestOpenBuildStates, sweepOrphanedScratch } from "@storyshelf/core/capture";
|
|
9
|
+
//#region src/boot-tasks.ts
|
|
10
|
+
/**
|
|
11
|
+
* Boot-time work that runs once adapters are ready: the job consumer starts (there is no boot sweep
|
|
12
|
+
* of builds left `capturing`: leases expire and jobs are redelivered, so a second instance can
|
|
13
|
+
* start without touching a first one's builds), the credential state is checked, and the git state
|
|
14
|
+
* of open builds is asked for again (see {@link replayGitStateAfterSetup}).
|
|
15
|
+
*/
|
|
16
|
+
function runBootTasks(cell, scoped, runtime, buildState) {
|
|
17
|
+
startConsumerAfterSetup(cell, runtime.logger);
|
|
18
|
+
sweepScratchOnBoot(cell, runtime.config.scratchDir, runtime.logger);
|
|
19
|
+
checkCredentialsAfterSetup(cell, scoped.database, runtime.config, runtime.logger);
|
|
20
|
+
logHostingOnlyAfterSetup(cell, scoped, runtime.logger);
|
|
21
|
+
logPausedAfterSetup(cell, runtime);
|
|
22
|
+
if (buildState) replayGitStateAfterSetup(cell, scoped, runtime, buildState);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A restart can lose a git status or comment: a delivery in flight on a host with no durable
|
|
26
|
+
* queue, a request made while no origin resolved. Once adapters are ready, with notifiers wired
|
|
27
|
+
* and a public origin resolved, the open builds (newest 25 per project) are asked for again, once
|
|
28
|
+
* (ADR 0014, decision 11). Only git state is replayed: chat, email and alerts are not.
|
|
29
|
+
*/
|
|
30
|
+
function replayGitStateAfterSetup(cell, scoped, runtime, buildState) {
|
|
31
|
+
if (runtime.notifiers.length === 0) return;
|
|
32
|
+
trackBackground(cell, cell.ready.then(async (setup) => {
|
|
33
|
+
if (setup.ok && await runtime.publicOrigin.url() !== void 0) {
|
|
34
|
+
const asked = await requestOpenBuildStates({
|
|
35
|
+
db: scoped.database,
|
|
36
|
+
logger: runtime.logger
|
|
37
|
+
}, buildState);
|
|
38
|
+
runtime.logger.info({ asked }, "re-requested the git state of open builds at boot");
|
|
39
|
+
}
|
|
40
|
+
}).catch((error) => {
|
|
41
|
+
runtime.logger.warn({ err: error }, "could not replay the git state of open builds");
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
/** With notifiers wired but no public origin resolved, say at boot that notifications are paused. */
|
|
45
|
+
function logPausedAfterSetup(cell, runtime) {
|
|
46
|
+
if (runtime.notifiers.length === 0) return;
|
|
47
|
+
trackBackground(cell, cell.ready.then(async (setup) => {
|
|
48
|
+
if (setup.ok && await runtime.publicOrigin.url() === void 0) runtime.logger.warn({}, NOTIFICATIONS_PAUSED_TEXT);
|
|
49
|
+
}).catch((error) => {
|
|
50
|
+
runtime.logger.warn({ err: error }, "could not check the public origin for notifications");
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
/** Without `testing`, once adapters are ready, log the mode and any snapshots it hides. */
|
|
54
|
+
function logHostingOnlyAfterSetup(cell, scoped, logger) {
|
|
55
|
+
if (scoped.testing !== void 0) return;
|
|
56
|
+
trackBackground(cell, cell.ready.then(async (setup) => {
|
|
57
|
+
if (setup.ok) await logHostingOnlyMode(scoped.database, logger);
|
|
58
|
+
}).catch((error) => {
|
|
59
|
+
logger.warn({ err: error }, "could not count the snapshots hosting-only mode hides");
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
/** Once adapters are ready, start the consumer, so a host that never calls `setup()` still runs jobs. */
|
|
63
|
+
function startConsumerAfterSetup(cell, logger) {
|
|
64
|
+
trackBackground(cell, cell.ready.then((setup) => {
|
|
65
|
+
if (setup.ok) startConsumer(cell, logger);
|
|
66
|
+
}).catch((error) => {
|
|
67
|
+
logger.error({ err: error }, "job consumer failed to start");
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
/** Remove scratch dirs a crash left behind; filesystem only, so it never waits on adapters. */
|
|
71
|
+
function sweepScratchOnBoot(cell, scratchDir, logger) {
|
|
72
|
+
if (!scratchDir) return;
|
|
73
|
+
trackBackground(cell, sweepOrphanedScratch(scratchDir, { logger }).then(() => {}, (error) => {
|
|
74
|
+
logger.warn({ err: error }, "scratch sweep failed");
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { runBootTasks };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=boot-tasks.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot-tasks.mjs","names":[],"sources":["../src/boot-tasks.ts"],"sourcesContent":["import {\n requestOpenBuildStates,\n sweepOrphanedScratch,\n type BuildStatePublisher,\n} from \"@storyshelf/core/capture\";\nimport type { ShelfOptions } from \"@storyshelf/core/config\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { checkCredentialsAfterSetup } from \"./credentials.ts\";\nimport { logHostingOnlyMode } from \"./hidden-data.ts\";\nimport { startConsumer, trackBackground, type LifecycleCell } from \"./lifecycle.ts\";\nimport { NOTIFICATIONS_PAUSED_TEXT } from \"./notifications-pause.ts\";\nimport type { ServerRuntime } from \"./runtime.ts\";\n\n/**\n * Boot-time work that runs once adapters are ready: the job consumer starts (there is no boot sweep\n * of builds left `capturing`: leases expire and jobs are redelivered, so a second instance can\n * start without touching a first one's builds), the credential state is checked, and the git state\n * of open builds is asked for again (see {@link replayGitStateAfterSetup}).\n */\nexport function runBootTasks(\n cell: LifecycleCell,\n scoped: ShelfOptions,\n runtime: ServerRuntime,\n buildState?: BuildStatePublisher,\n): void {\n startConsumerAfterSetup(cell, runtime.logger);\n sweepScratchOnBoot(cell, runtime.config.scratchDir, runtime.logger);\n checkCredentialsAfterSetup(cell, scoped.database, runtime.config, runtime.logger);\n logHostingOnlyAfterSetup(cell, scoped, runtime.logger);\n logPausedAfterSetup(cell, runtime);\n if (buildState) {\n replayGitStateAfterSetup(cell, scoped, runtime, buildState);\n }\n}\n\n/**\n * A restart can lose a git status or comment: a delivery in flight on a host with no durable\n * queue, a request made while no origin resolved. Once adapters are ready, with notifiers wired\n * and a public origin resolved, the open builds (newest 25 per project) are asked for again, once\n * (ADR 0014, decision 11). Only git state is replayed: chat, email and alerts are not.\n */\nfunction replayGitStateAfterSetup(\n cell: LifecycleCell,\n scoped: ShelfOptions,\n runtime: ServerRuntime,\n buildState: BuildStatePublisher,\n): void {\n if (runtime.notifiers.length === 0) {\n return;\n }\n trackBackground(\n cell,\n cell.ready\n .then(async (setup) => {\n if (setup.ok && (await runtime.publicOrigin.url()) !== undefined) {\n const asked = await requestOpenBuildStates(\n { db: scoped.database, logger: runtime.logger },\n buildState,\n );\n runtime.logger.info({ asked }, \"re-requested the git state of open builds at boot\");\n }\n })\n .catch((error: unknown) => {\n runtime.logger.warn({ err: error }, \"could not replay the git state of open builds\");\n }),\n );\n}\n\n/** With notifiers wired but no public origin resolved, say at boot that notifications are paused. */\nfunction logPausedAfterSetup(cell: LifecycleCell, runtime: ServerRuntime): void {\n if (runtime.notifiers.length === 0) {\n return;\n }\n trackBackground(\n cell,\n cell.ready\n .then(async (setup) => {\n if (setup.ok && (await runtime.publicOrigin.url()) === undefined) {\n runtime.logger.warn({}, NOTIFICATIONS_PAUSED_TEXT);\n }\n })\n .catch((error: unknown) => {\n runtime.logger.warn({ err: error }, \"could not check the public origin for notifications\");\n }),\n );\n}\n\n/** Without `testing`, once adapters are ready, log the mode and any snapshots it hides. */\nfunction logHostingOnlyAfterSetup(cell: LifecycleCell, scoped: ShelfOptions, logger: Logger): void {\n if (scoped.testing !== undefined) {\n return;\n }\n trackBackground(\n cell,\n cell.ready\n .then(async (setup) => {\n if (setup.ok) {\n await logHostingOnlyMode(scoped.database, logger);\n }\n })\n .catch((error: unknown) => {\n logger.warn({ err: error }, \"could not count the snapshots hosting-only mode hides\");\n }),\n );\n}\n\n/** Once adapters are ready, start the consumer, so a host that never calls `setup()` still runs jobs. */\nfunction startConsumerAfterSetup(cell: LifecycleCell, logger: Logger): void {\n trackBackground(\n cell,\n cell.ready\n .then((setup) => {\n if (setup.ok) {\n startConsumer(cell, logger);\n }\n })\n .catch((error: unknown) => {\n logger.error({ err: error }, \"job consumer failed to start\");\n }),\n );\n}\n\n/** Remove scratch dirs a crash left behind; filesystem only, so it never waits on adapters. */\nfunction sweepScratchOnBoot(\n cell: LifecycleCell,\n scratchDir: string | undefined,\n logger: Logger,\n): void {\n if (!scratchDir) {\n return;\n }\n trackBackground(\n cell,\n sweepOrphanedScratch(scratchDir, { logger }).then(\n () => {},\n (error: unknown) => {\n logger.warn({ err: error }, \"scratch sweep failed\");\n },\n ),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmBA,SAAgB,aACd,MACA,QACA,SACA,YACM;CACN,wBAAwB,MAAM,QAAQ,MAAM;CAC5C,mBAAmB,MAAM,QAAQ,OAAO,YAAY,QAAQ,MAAM;CAClE,2BAA2B,MAAM,OAAO,UAAU,QAAQ,QAAQ,QAAQ,MAAM;CAChF,yBAAyB,MAAM,QAAQ,QAAQ,MAAM;CACrD,oBAAoB,MAAM,OAAO;CACjC,IAAI,YACF,yBAAyB,MAAM,QAAQ,SAAS,UAAU;AAE9D;;;;;;;AAQA,SAAS,yBACP,MACA,QACA,SACA,YACM;CACN,IAAI,QAAQ,UAAU,WAAW,GAC/B;CAEF,gBACE,MACA,KAAK,MACF,KAAK,OAAO,UAAU;EACrB,IAAI,MAAM,MAAO,MAAM,QAAQ,aAAa,IAAI,MAAO,KAAA,GAAW;GAChE,MAAM,QAAQ,MAAM,uBAClB;IAAE,IAAI,OAAO;IAAU,QAAQ,QAAQ;GAAO,GAC9C,UACF;GACA,QAAQ,OAAO,KAAK,EAAE,MAAM,GAAG,mDAAmD;EACpF;CACF,CAAC,CAAC,CACD,OAAO,UAAmB;EACzB,QAAQ,OAAO,KAAK,EAAE,KAAK,MAAM,GAAG,+CAA+C;CACrF,CAAC,CACL;AACF;;AAGA,SAAS,oBAAoB,MAAqB,SAA8B;CAC9E,IAAI,QAAQ,UAAU,WAAW,GAC/B;CAEF,gBACE,MACA,KAAK,MACF,KAAK,OAAO,UAAU;EACrB,IAAI,MAAM,MAAO,MAAM,QAAQ,aAAa,IAAI,MAAO,KAAA,GACrD,QAAQ,OAAO,KAAK,CAAC,GAAG,yBAAyB;CAErD,CAAC,CAAC,CACD,OAAO,UAAmB;EACzB,QAAQ,OAAO,KAAK,EAAE,KAAK,MAAM,GAAG,qDAAqD;CAC3F,CAAC,CACL;AACF;;AAGA,SAAS,yBAAyB,MAAqB,QAAsB,QAAsB;CACjG,IAAI,OAAO,YAAY,KAAA,GACrB;CAEF,gBACE,MACA,KAAK,MACF,KAAK,OAAO,UAAU;EACrB,IAAI,MAAM,IACR,MAAM,mBAAmB,OAAO,UAAU,MAAM;CAEpD,CAAC,CAAC,CACD,OAAO,UAAmB;EACzB,OAAO,KAAK,EAAE,KAAK,MAAM,GAAG,uDAAuD;CACrF,CAAC,CACL;AACF;;AAGA,SAAS,wBAAwB,MAAqB,QAAsB;CAC1E,gBACE,MACA,KAAK,MACF,MAAM,UAAU;EACf,IAAI,MAAM,IACR,cAAc,MAAM,MAAM;CAE9B,CAAC,CAAC,CACD,OAAO,UAAmB;EACzB,OAAO,MAAM,EAAE,KAAK,MAAM,GAAG,8BAA8B;CAC7D,CAAC,CACL;AACF;;AAGA,SAAS,mBACP,MACA,YACA,QACM;CACN,IAAI,CAAC,YACH;CAEF,gBACE,MACA,qBAAqB,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,WACrC,CAAC,IACN,UAAmB;EAClB,OAAO,KAAK,EAAE,KAAK,MAAM,GAAG,sBAAsB;CACpD,CACF,CACF;AACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/build-state-flags.ts
|
|
5
|
+
/**
|
|
6
|
+
* What decides which statuses a project's builds show: the instance's `testing` and the project's
|
|
7
|
+
* review switch (a row without the column, from a hand-built table, counts as on, the default).
|
|
8
|
+
*/
|
|
9
|
+
function buildStateFlags(testing, project) {
|
|
10
|
+
return {
|
|
11
|
+
testing,
|
|
12
|
+
review: project.reviewEnabled !== false
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { buildStateFlags };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=build-state-flags.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-state-flags.mjs","names":[],"sources":["../src/build-state-flags.ts"],"sourcesContent":["import type { BuildStateFlags } from \"@storyshelf/core/adapter/notifier\";\nimport type { Project } from \"@storyshelf/core/schema\";\n\n/**\n * What decides which statuses a project's builds show: the instance's `testing` and the project's\n * review switch (a row without the column, from a hand-built table, counts as on, the default).\n */\nexport function buildStateFlags(\n testing: boolean,\n project: Pick<Project, \"reviewEnabled\">,\n): BuildStateFlags {\n return { testing, review: project.reviewEnabled !== false };\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,gBACd,SACA,SACiB;CACjB,OAAO;EAAE;EAAS,QAAQ,QAAQ,kBAAkB;CAAM;AAC5D"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { credentialKeys } from "./credential-keys.mjs";
|
|
5
|
+
import { notifySystemWith } from "./notify.mjs";
|
|
6
|
+
import { buildStateFlags } from "./build-state-flags.mjs";
|
|
7
|
+
import { createBuildStatePublisher, createNotifyEnqueuer, createNotifyHandler, notifyExhausted } from "@storyshelf/core/capture";
|
|
8
|
+
import { ProjectModel } from "@storyshelf/core/models";
|
|
9
|
+
//#region src/build-state-setup.ts
|
|
10
|
+
/**
|
|
11
|
+
* Build-state wiring (ADR 0014, decisions 5 and 7): the publisher every build transition calls and
|
|
12
|
+
* the `notify` job kind behind it. On a host that polls the queue, a transition queues one durable
|
|
13
|
+
* `notify` job per git channel and the handler delivers with retries; a host with no consumer
|
|
14
|
+
* (serverless) delivers inline, without durability. The consumer is built in `jobs-setup.ts`.
|
|
15
|
+
*/
|
|
16
|
+
/** Wire build-state delivery for `options`; `jobs` is the `notify` queue, null on a host with no consumer. */
|
|
17
|
+
function setupBuildState(options, runtime, jobs) {
|
|
18
|
+
const deps = {
|
|
19
|
+
db: options.database,
|
|
20
|
+
storage: options.storage,
|
|
21
|
+
notifiers: runtime.notifiers,
|
|
22
|
+
secret: credentialKeys(runtime.config),
|
|
23
|
+
config: runtime.config,
|
|
24
|
+
publicOrigin: async () => await runtime.publicOrigin.url(),
|
|
25
|
+
flags: (project) => buildStateFlags(options.testing !== void 0, project),
|
|
26
|
+
logger: runtime.logger.child({ component: "build-state" })
|
|
27
|
+
};
|
|
28
|
+
if (jobs === null) return {
|
|
29
|
+
publisher: createBuildStatePublisher({ deps }),
|
|
30
|
+
handler: null
|
|
31
|
+
};
|
|
32
|
+
const handlerOptions = {
|
|
33
|
+
deps,
|
|
34
|
+
onFailed: async (failure) => {
|
|
35
|
+
await alertAdmins(options, runtime, failure);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
jobs.exhausted.notify = notifyExhausted(handlerOptions);
|
|
39
|
+
return {
|
|
40
|
+
publisher: createBuildStatePublisher({
|
|
41
|
+
deps,
|
|
42
|
+
enqueue: createNotifyEnqueuer(deps, { enqueueJob: async (job) => await jobs.get().enqueueJob(job) })
|
|
43
|
+
}),
|
|
44
|
+
handler: createNotifyHandler(handlerOptions)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Raise `sys:notify-failed` to the site-wide channels. Git channels are project-scoped and
|
|
49
|
+
* site-wide channels deliver messages, which are not retried through `notify` jobs, so a failing
|
|
50
|
+
* alert can never raise another one.
|
|
51
|
+
*/
|
|
52
|
+
async function alertAdmins(options, runtime, failure) {
|
|
53
|
+
const project = failure.projectId === null ? null : await new ProjectModel(options.database).get(failure.projectId);
|
|
54
|
+
await notifySystemWith({
|
|
55
|
+
db: options.database,
|
|
56
|
+
config: runtime.config,
|
|
57
|
+
ui: runtime.ui,
|
|
58
|
+
logger: runtime.logger,
|
|
59
|
+
notifiers: runtime.notifiers,
|
|
60
|
+
notificationsConfig: options.notifications?.config,
|
|
61
|
+
publicOrigin: runtime.publicOrigin
|
|
62
|
+
}, "sys:notify-failed", {
|
|
63
|
+
channelId: failure.channelId,
|
|
64
|
+
provider: failure.provider,
|
|
65
|
+
projectId: failure.projectId,
|
|
66
|
+
projectSlug: project?.slug,
|
|
67
|
+
buildId: failure.buildId,
|
|
68
|
+
error: failure.error,
|
|
69
|
+
message: `${failure.provider} channel of ${project?.slug ?? "a project"} could not show build ${failure.buildId}: ${failure.error}`
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { setupBuildState };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=build-state-setup.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-state-setup.mjs","names":[],"sources":["../src/build-state-setup.ts"],"sourcesContent":["/**\n * Build-state wiring (ADR 0014, decisions 5 and 7): the publisher every build transition calls and\n * the `notify` job kind behind it. On a host that polls the queue, a transition queues one durable\n * `notify` job per git channel and the handler delivers with retries; a host with no consumer\n * (serverless) delivers inline, without durability. The consumer is built in `jobs-setup.ts`.\n */\nimport {\n createBuildStatePublisher,\n createNotifyEnqueuer,\n createNotifyHandler,\n notifyExhausted,\n type BuildStateDeps,\n type BuildStatePublisher,\n type KindHandler,\n type NotifyFailure,\n} from \"@storyshelf/core/capture\";\nimport type { ShelfOptions } from \"@storyshelf/core/config\";\nimport { ProjectModel } from \"@storyshelf/core/models\";\nimport { buildStateFlags } from \"./build-state-flags.ts\";\nimport { credentialKeys } from \"./credential-keys.ts\";\nimport type { JobQueue } from \"./job-queue.ts\";\nimport { notifySystemWith } from \"./notify.ts\";\nimport type { ServerRuntime } from \"./runtime.ts\";\n\n/** What requests and the consumer use of build-state delivery. */\nexport interface BuildStateWiring {\n /** Asks for a build's current state to be shown on its git channels. */\n publisher: BuildStatePublisher;\n /** Runs `notify` jobs; null when this host polls none (it delivers inline). */\n handler: KindHandler | null;\n}\n\n/** Wire build-state delivery for `options`; `jobs` is the `notify` queue, null on a host with no consumer. */\nexport function setupBuildState(\n options: ShelfOptions,\n runtime: ServerRuntime,\n jobs: JobQueue | null,\n): BuildStateWiring {\n const deps: BuildStateDeps = {\n db: options.database,\n storage: options.storage,\n notifiers: runtime.notifiers,\n secret: credentialKeys(runtime.config),\n config: runtime.config,\n publicOrigin: async () => await runtime.publicOrigin.url(),\n flags: (project) => buildStateFlags(options.testing !== undefined, project),\n logger: runtime.logger.child({ component: \"build-state\" }),\n };\n if (jobs === null) {\n return { publisher: createBuildStatePublisher({ deps }), handler: null };\n }\n const handlerOptions = {\n deps,\n onFailed: async (failure: NotifyFailure): Promise<void> => {\n await alertAdmins(options, runtime, failure);\n },\n };\n jobs.exhausted.notify = notifyExhausted(handlerOptions);\n return {\n publisher: createBuildStatePublisher({\n deps,\n enqueue: createNotifyEnqueuer(deps, {\n enqueueJob: async (job) => await jobs.get().enqueueJob(job),\n }),\n }),\n handler: createNotifyHandler(handlerOptions),\n };\n}\n\n/**\n * Raise `sys:notify-failed` to the site-wide channels. Git channels are project-scoped and\n * site-wide channels deliver messages, which are not retried through `notify` jobs, so a failing\n * alert can never raise another one.\n */\nasync function alertAdmins(\n options: ShelfOptions,\n runtime: ServerRuntime,\n failure: NotifyFailure,\n): Promise<void> {\n const project =\n failure.projectId === null\n ? null\n : await new ProjectModel(options.database).get(failure.projectId);\n await notifySystemWith(\n {\n db: options.database,\n config: runtime.config,\n ui: runtime.ui,\n logger: runtime.logger,\n notifiers: runtime.notifiers,\n notificationsConfig: options.notifications?.config,\n publicOrigin: runtime.publicOrigin,\n },\n \"sys:notify-failed\",\n {\n channelId: failure.channelId,\n provider: failure.provider,\n projectId: failure.projectId,\n projectSlug: project?.slug,\n buildId: failure.buildId,\n error: failure.error,\n // The formatter prints `message` in an admin alert.\n message: `${failure.provider} channel of ${project?.slug ?? \"a project\"} could not show build ${failure.buildId}: ${failure.error}`,\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiCA,SAAgB,gBACd,SACA,SACA,MACkB;CAClB,MAAM,OAAuB;EAC3B,IAAI,QAAQ;EACZ,SAAS,QAAQ;EACjB,WAAW,QAAQ;EACnB,QAAQ,eAAe,QAAQ,MAAM;EACrC,QAAQ,QAAQ;EAChB,cAAc,YAAY,MAAM,QAAQ,aAAa,IAAI;EACzD,QAAQ,YAAY,gBAAgB,QAAQ,YAAY,KAAA,GAAW,OAAO;EAC1E,QAAQ,QAAQ,OAAO,MAAM,EAAE,WAAW,cAAc,CAAC;CAC3D;CACA,IAAI,SAAS,MACX,OAAO;EAAE,WAAW,0BAA0B,EAAE,KAAK,CAAC;EAAG,SAAS;CAAK;CAEzE,MAAM,iBAAiB;EACrB;EACA,UAAU,OAAO,YAA0C;GACzD,MAAM,YAAY,SAAS,SAAS,OAAO;EAC7C;CACF;CACA,KAAK,UAAU,SAAS,gBAAgB,cAAc;CACtD,OAAO;EACL,WAAW,0BAA0B;GACnC;GACA,SAAS,qBAAqB,MAAM,EAClC,YAAY,OAAO,QAAQ,MAAM,KAAK,IAAI,CAAC,CAAC,WAAW,GAAG,EAC5D,CAAC;EACH,CAAC;EACD,SAAS,oBAAoB,cAAc;CAC7C;AACF;;;;;;AAOA,eAAe,YACb,SACA,SACA,SACe;CACf,MAAM,UACJ,QAAQ,cAAc,OAClB,OACA,MAAM,IAAI,aAAa,QAAQ,QAAQ,CAAC,CAAC,IAAI,QAAQ,SAAS;CACpE,MAAM,iBACJ;EACE,IAAI,QAAQ;EACZ,QAAQ,QAAQ;EAChB,IAAI,QAAQ;EACZ,QAAQ,QAAQ;EAChB,WAAW,QAAQ;EACnB,qBAAqB,QAAQ,eAAe;EAC5C,cAAc,QAAQ;CACxB,GACA,qBACA;EACE,WAAW,QAAQ;EACnB,UAAU,QAAQ;EAClB,WAAW,QAAQ;EACnB,aAAa,SAAS;EACtB,SAAS,QAAQ;EACjB,OAAO,QAAQ;EAEf,SAAS,GAAG,QAAQ,SAAS,cAAc,SAAS,QAAQ,YAAY,wBAAwB,QAAQ,QAAQ,IAAI,QAAQ;CAC9H,CACF;AACF"}
|