@storyshelf/app 0.7.0 → 0.9.0-next-d2cc082
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/dist/app-types.d.mts +1 -3
- package/dist/asset-manifest.mjs +1 -2
- package/dist/assets/htmx.mjs +1 -2
- package/dist/assets/mark.mjs +1 -2
- package/dist/boot-tasks.mjs +1 -2
- package/dist/build-state-flags.mjs +1 -2
- package/dist/build-state-setup.mjs +1 -2
- package/dist/capture-alerts.mjs +1 -2
- package/dist/capture-process.mjs +1 -2
- package/dist/capture-setup.mjs +1 -2
- package/dist/connections.mjs +1 -2
- package/dist/consumer-setup.mjs +1 -2
- package/dist/credential-keys.mjs +1 -2
- package/dist/credentials.mjs +1 -2
- package/dist/design-events.mjs +71 -0
- package/dist/design-events.mjs.map +1 -0
- package/dist/git-status.mjs +1 -2
- package/dist/hidden-data.mjs +1 -2
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +2 -3
- package/dist/insights/budget.mjs +1 -2
- package/dist/insights/deps.mjs +1 -2
- package/dist/insights/errors.mjs +1 -2
- package/dist/insights/health-panel.mjs +0 -1
- package/dist/insights/job.mjs +1 -2
- package/dist/insights/load-health.mjs +1 -2
- package/dist/insights/load-triage.mjs +1 -2
- package/dist/insights/panel.mjs +1 -2
- package/dist/insights/request.mjs +1 -2
- package/dist/insights/stale.mjs +1 -2
- package/dist/insights/usage-report.mjs +1 -2
- package/dist/insights/view.mjs +1 -2
- package/dist/job-canceller.mjs +1 -2
- package/dist/job-queue.mjs +1 -2
- package/dist/jobs-setup.mjs +1 -2
- package/dist/lifecycle.mjs +2 -3
- package/dist/lifecycle.mjs.map +1 -1
- package/dist/middleware/auth-gate.mjs +1 -2
- package/dist/middleware/csrf.mjs +1 -2
- package/dist/middleware/rate-limit.mjs +1 -2
- package/dist/middleware/request-log.mjs +1 -2
- package/dist/middleware/require-review.mjs +1 -2
- package/dist/middleware/require-testing.mjs +1 -2
- package/dist/middleware/server-timing.mjs +1 -2
- package/dist/middleware/setup-gate.mjs +1 -2
- package/dist/middleware/store-scope.mjs +1 -2
- package/dist/notifications-pause.mjs +1 -2
- package/dist/notify.d.mts +1 -3
- package/dist/notify.mjs +5 -3
- package/dist/notify.mjs.map +1 -1
- package/dist/pages/a11y-violations.mjs +1 -2
- package/dist/pages/admin-ai.mjs +1 -2
- package/dist/pages/admin-system-origin.mjs +19 -10
- package/dist/pages/admin-system-origin.mjs.map +1 -1
- package/dist/pages/admin-system-targets.mjs +1 -2
- package/dist/pages/admin-system.mjs +1 -2
- package/dist/pages/build-detail.mjs +1 -2
- package/dist/pages/build-diff-actions.mjs +1 -2
- package/dist/pages/build-diff-comments.mjs +1 -2
- package/dist/pages/build-diff-default.mjs +53 -0
- package/dist/pages/build-diff-default.mjs.map +1 -0
- package/dist/pages/build-diff-environment.mjs +34 -5
- package/dist/pages/build-diff-environment.mjs.map +1 -1
- package/dist/pages/build-diff-header.mjs +1 -2
- package/dist/pages/build-diff-insight.mjs +1 -2
- package/dist/pages/build-diff-model.mjs +20 -3
- package/dist/pages/build-diff-model.mjs.map +1 -1
- package/dist/pages/build-diff-nav.mjs +1 -2
- package/dist/pages/build-diff-reference.mjs +84 -0
- package/dist/pages/build-diff-reference.mjs.map +1 -0
- package/dist/pages/build-diff-shortcuts.mjs +1 -2
- package/dist/pages/build-diff-stale.mjs +1 -2
- package/dist/pages/build-diff-usage.mjs +1 -2
- package/dist/pages/build-diff-viewer.mjs +38 -12
- package/dist/pages/build-diff-viewer.mjs.map +1 -1
- package/dist/pages/build-diff.mjs +6 -12
- package/dist/pages/build-diff.mjs.map +1 -1
- package/dist/pages/build-tests.mjs +1 -2
- package/dist/pages/component-detail.mjs +1 -2
- package/dist/pages/component-stories.mjs +1 -2
- package/dist/pages/compute-jobs.mjs +1 -2
- package/dist/pages/design-links.mjs +1 -2
- package/dist/pages/environment-change.mjs +16 -4
- package/dist/pages/environment-change.mjs.map +1 -1
- package/dist/pages/health-panel.mjs +1 -2
- package/dist/pages/invite.mjs +1 -2
- package/dist/pages/label-detail.mjs +1 -2
- package/dist/pages/library-grid.mjs +1 -2
- package/dist/pages/library-model.mjs +1 -2
- package/dist/pages/library-snapshots.mjs +1 -2
- package/dist/pages/library.mjs +1 -2
- package/dist/pages/login.mjs +1 -2
- package/dist/pages/notifications-paused.mjs +1 -2
- package/dist/pages/passkey-ceremony.mjs +1 -2
- package/dist/pages/profile.mjs +1 -2
- package/dist/pages/project-builds-table.mjs +1 -2
- package/dist/pages/project-builds.mjs +1 -2
- package/dist/pages/project-create.mjs +1 -2
- package/dist/pages/project-settings.mjs +1 -2
- package/dist/pages/projects-card.mjs +1 -2
- package/dist/pages/projects.mjs +1 -2
- package/dist/pages/review-changeset.mjs +1 -2
- package/dist/pages/review-comments.mjs +1 -2
- package/dist/pages/review-verdict.mjs +1 -2
- package/dist/pages/root.mjs +1 -2
- package/dist/pages/settings-ai.mjs +1 -2
- package/dist/pages/settings-capture.mjs +1 -2
- package/dist/pages/settings-design-credential.mjs +1 -2
- package/dist/pages/settings-design-links.mjs +1 -2
- package/dist/pages/settings-design.mjs +1 -2
- package/dist/pages/settings-general.mjs +1 -2
- package/dist/pages/settings-labels.mjs +1 -2
- package/dist/pages/settings-links.mjs +1 -2
- package/dist/pages/settings-members.mjs +1 -2
- package/dist/pages/settings-notifications-git.mjs +1 -2
- package/dist/pages/settings-notifications.mjs +1 -2
- package/dist/pages/settings-tests.mjs +1 -2
- package/dist/pages/settings-tokens.mjs +1 -2
- package/dist/pages/settings-webhooks.mjs +1 -2
- package/dist/pages/storybook.mjs +1 -2
- package/dist/pages/target-choices.mjs +1 -2
- package/dist/publish-setup.mjs +1 -2
- package/dist/retention-timer.mjs +10 -11
- package/dist/retention-timer.mjs.map +1 -1
- package/dist/review-events.mjs +1 -2
- package/dist/routers/admin-pages.mjs +1 -2
- package/dist/routers/admin-public-origin.mjs +10 -3
- package/dist/routers/admin-public-origin.mjs.map +1 -1
- package/dist/routers/admin.mjs +8 -4
- package/dist/routers/admin.mjs.map +1 -1
- package/dist/routers/assets.mjs +1 -2
- package/dist/routers/attempts.mjs +1 -2
- package/dist/routers/auth-sync.mjs +1 -2
- package/dist/routers/auth.mjs +1 -2
- package/dist/routers/baseline-guard.mjs +2 -3
- package/dist/routers/baseline-guard.mjs.map +1 -1
- package/dist/routers/baseline-reference.mjs +72 -0
- package/dist/routers/baseline-reference.mjs.map +1 -0
- package/dist/routers/baselines.handlers.mjs +1 -2
- package/dist/routers/baselines.mjs +1 -2
- package/dist/routers/build-cancel.mjs +1 -2
- package/dist/routers/build-changeset.mjs +1 -2
- package/dist/routers/build-reviews.handlers.mjs +1 -2
- package/dist/routers/build-reviews.mjs +1 -2
- package/dist/routers/builds.handlers.mjs +23 -9
- package/dist/routers/builds.handlers.mjs.map +1 -1
- package/dist/routers/builds.mjs +7 -3
- package/dist/routers/builds.mjs.map +1 -1
- package/dist/routers/capture-cache.mjs +29 -0
- package/dist/routers/capture-cache.mjs.map +1 -0
- package/dist/routers/capture-config.mjs +1 -2
- package/dist/routers/comments.mjs +1 -2
- package/dist/routers/comparison-image.mjs +29 -0
- package/dist/routers/comparison-image.mjs.map +1 -0
- package/dist/routers/component-catalog.handlers.mjs +1 -2
- package/dist/routers/component.handlers.mjs +1 -2
- package/dist/routers/connections.mjs +1 -2
- package/dist/routers/design-connect.handlers.mjs +1 -2
- package/dist/routers/design.handlers.mjs +8 -2
- package/dist/routers/design.handlers.mjs.map +1 -1
- package/dist/routers/flash.mjs +1 -2
- package/dist/routers/health-report.mjs +1 -2
- package/dist/routers/health.mjs +1 -2
- package/dist/routers/helpers.mjs +1 -2
- package/dist/routers/htmx.mjs +1 -2
- package/dist/routers/insights-ui.mjs +1 -2
- package/dist/routers/insights.handlers.mjs +1 -2
- package/dist/routers/insights.mjs +1 -2
- package/dist/routers/labels.mjs +1 -2
- package/dist/routers/links.handlers.mjs +1 -2
- package/dist/routers/links.mjs +1 -2
- package/dist/routers/media.mjs +14 -5
- package/dist/routers/media.mjs.map +1 -1
- package/dist/routers/members.mjs +1 -2
- package/dist/routers/notification-channels.mjs +1 -2
- package/dist/routers/notification-subscriptions.mjs +1 -2
- package/dist/routers/oauth-state.mjs +1 -2
- package/dist/routers/preview-base.mjs +1 -2
- package/dist/routers/preview-token.mjs +1 -2
- package/dist/routers/profile.mjs +1 -2
- package/dist/routers/projects.mjs +1 -2
- package/dist/routers/publish-upload.mjs +19 -12
- package/dist/routers/publish-upload.mjs.map +1 -1
- package/dist/routers/published-access.mjs +1 -2
- package/dist/routers/published-compression.mjs +109 -0
- package/dist/routers/published-compression.mjs.map +1 -0
- package/dist/routers/published-files.mjs +26 -9
- package/dist/routers/published-files.mjs.map +1 -1
- package/dist/routers/review-forms.mjs +1 -2
- package/dist/routers/review-next.mjs +1 -2
- package/dist/routers/review-section.handlers.mjs +1 -2
- package/dist/routers/review-switch-off.mjs +1 -2
- package/dist/routers/schemas.mjs +1 -2
- package/dist/routers/scoped-models.mjs +1 -2
- package/dist/routers/settings-ai.mjs +1 -2
- package/dist/routers/settings-capture.mjs +1 -2
- package/dist/routers/settings-design.mjs +1 -2
- package/dist/routers/settings-general.mjs +1 -2
- package/dist/routers/settings-git-channel.mjs +1 -2
- package/dist/routers/settings-labels.mjs +1 -2
- package/dist/routers/settings-links.mjs +1 -2
- package/dist/routers/settings-members.mjs +1 -2
- package/dist/routers/settings-notifications.mjs +1 -2
- package/dist/routers/settings-tokens.mjs +1 -2
- package/dist/routers/settings-webhooks.mjs +1 -2
- package/dist/routers/settings.handlers.mjs +1 -2
- package/dist/routers/settings.mjs +1 -2
- package/dist/routers/signed-storybook.mjs +5 -3
- package/dist/routers/signed-storybook.mjs.map +1 -1
- package/dist/routers/snapshot-groups.mjs +1 -2
- package/dist/routers/snapshots.mjs +1 -2
- package/dist/routers/storybook.mjs +21 -17
- package/dist/routers/storybook.mjs.map +1 -1
- package/dist/routers/tokens.mjs +1 -2
- package/dist/routers/ui.mjs +4 -2
- package/dist/routers/ui.mjs.map +1 -1
- package/dist/routers/webhooks.mjs +1 -2
- package/dist/routers/well-known.mjs +1 -2
- package/dist/routes/api-routes.mjs +1 -2
- package/dist/routes/classification.mjs +1 -2
- package/dist/routes/openapi-classes.mjs +1 -2
- package/dist/routes/page-routes.mjs +2 -3
- package/dist/routes/page-routes.mjs.map +1 -1
- package/dist/runtime.mjs +1 -2
- package/dist/serverless.mjs +1 -2
- package/dist/store.mjs +1 -2
- package/dist/targets.mjs +1 -2
- package/dist/ui/avatar.mjs +1 -2
- package/dist/ui/buttons.mjs +1 -2
- package/dist/ui/code-block.mjs +1 -2
- package/dist/ui/compare-stage.mjs +86 -54
- package/dist/ui/compare-stage.mjs.map +1 -1
- package/dist/ui/csrf-field.mjs +1 -2
- package/dist/ui/css.mjs +1 -2
- package/dist/ui/design-reference.mjs +1 -2
- package/dist/ui/document.mjs +1 -2
- package/dist/ui/dropdown.mjs +1 -2
- package/dist/ui/feedback.mjs +1 -2
- package/dist/ui/filter-input.mjs +1 -2
- package/dist/ui/forms.mjs +1 -2
- package/dist/ui/icons/icon.mjs +1 -2
- package/dist/ui/icons/paths.mjs +1 -2
- package/dist/ui/icons/sprite.mjs +1 -2
- package/dist/ui/kbd.mjs +1 -2
- package/dist/ui/layout.mjs +1 -2
- package/dist/ui/preview-embed.mjs +1 -2
- package/dist/ui/progress.mjs +1 -2
- package/dist/ui/segmented.mjs +1 -2
- package/dist/ui/shell/account.mjs +1 -2
- package/dist/ui/shell/bare.mjs +1 -2
- package/dist/ui/shell/boot-script.mjs +1 -2
- package/dist/ui/shell/client-script.mjs +1 -2
- package/dist/ui/shell/nav.mjs +1 -2
- package/dist/ui/shell/review-script.mjs +17 -3
- package/dist/ui/shell/review-script.mjs.map +1 -1
- package/dist/ui/shell/sidebar.mjs +1 -2
- package/dist/ui/shell/topbar.mjs +1 -2
- package/dist/ui/stacks.mjs +1 -2
- package/dist/ui/styles/base.mjs +1 -2
- package/dist/ui/styles/behavior.mjs +1 -2
- package/dist/ui/styles/review-layout.mjs +1 -2
- package/dist/ui/styles/review.mjs +1 -2
- package/dist/ui/styles/tokens.mjs +1 -2
- package/dist/ui/styles.mjs +1 -2
- package/dist/ui/subnav.mjs +1 -2
- package/dist/ui/table.mjs +1 -2
- package/dist/ui/theme.mjs +1 -2
- package/dist/ui/thumbnail.mjs +1 -2
- package/dist/ui/time.mjs +1 -2
- package/package.json +9 -18
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { credentialKeys } from "../credential-keys.mjs";
|
|
5
5
|
import { getStore } from "../store.mjs";
|
|
6
6
|
import { NotificationChannelModel, WebhookModel } from "@storyshelf/core/models";
|
|
7
|
-
|
|
7
|
+
// src/routers/scoped-models.ts
|
|
8
8
|
/** Models that encrypt secrets at rest, bound to the current request's db and server secret. */
|
|
9
9
|
/** Webhook model for the current request. */
|
|
10
10
|
function webhookModel() {
|
|
@@ -16,7 +16,6 @@ function notificationChannelModel() {
|
|
|
16
16
|
const { db, config } = getStore();
|
|
17
17
|
return new NotificationChannelModel(db, void 0, credentialKeys(config));
|
|
18
18
|
}
|
|
19
|
-
//#endregion
|
|
20
19
|
export { notificationChannelModel, webhookModel };
|
|
21
20
|
|
|
22
21
|
//# sourceMappingURL=scoped-models.mjs.map
|
|
@@ -7,7 +7,7 @@ import { hxRedirect } from "./htmx.mjs";
|
|
|
7
7
|
import { flash } from "./flash.mjs";
|
|
8
8
|
import { asString, findProject, renderSettingsPage } from "./settings.handlers.mjs";
|
|
9
9
|
import { ProjectModel } from "@storyshelf/core/models";
|
|
10
|
-
|
|
10
|
+
// src/routers/settings-ai.ts
|
|
11
11
|
/** Register the project AI settings tab (site admins set the profile gate). */
|
|
12
12
|
function registerAiSettings(app) {
|
|
13
13
|
app.get("/projects/:slug/settings/ai", async (c) => c.html(await renderSettingsPage(c, "ai")));
|
|
@@ -29,7 +29,6 @@ async function handleAiUpdate(c) {
|
|
|
29
29
|
flash(c, requested === "" ? "AI turned off for this project" : "AI profile saved");
|
|
30
30
|
return hxRedirect(c, `/projects/${project.slug}/settings/ai`);
|
|
31
31
|
}
|
|
32
|
-
//#endregion
|
|
33
32
|
export { registerAiSettings };
|
|
34
33
|
|
|
35
34
|
//# sourceMappingURL=settings-ai.mjs.map
|
|
@@ -12,7 +12,7 @@ import { resolveBrowsers } from "@storyshelf/core/capture";
|
|
|
12
12
|
import { ProjectModel } from "@storyshelf/core/models";
|
|
13
13
|
import { HTTPException } from "hono/http-exception";
|
|
14
14
|
import { isCaptureTarget } from "@storyshelf/core/adapter/capture-runner";
|
|
15
|
-
|
|
15
|
+
// src/routers/settings-capture.ts
|
|
16
16
|
/** Capture settings: browsers (primary + extras), the mode allowlist and the signed URL lifetime. */
|
|
17
17
|
function registerCaptureSettings(app) {
|
|
18
18
|
app.get("/projects/:slug/settings/capture", async (c) => c.html(await renderSettingsPage(c, "capture")));
|
|
@@ -71,7 +71,6 @@ async function handleCaptureUpdate(c) {
|
|
|
71
71
|
flash(c, "Capture settings saved");
|
|
72
72
|
return hxRedirect(c, `/projects/${project.slug}/settings/capture`);
|
|
73
73
|
}
|
|
74
|
-
//#endregion
|
|
75
74
|
export { registerCaptureSettings };
|
|
76
75
|
|
|
77
76
|
//# sourceMappingURL=settings-capture.mjs.map
|
|
@@ -9,7 +9,7 @@ import { flash } from "./flash.mjs";
|
|
|
9
9
|
import { asString, renderSettingsPage } from "./settings.handlers.mjs";
|
|
10
10
|
import { DesignCredentialModel, DesignModel } from "@storyshelf/core/models";
|
|
11
11
|
import { InvalidDesignUrlError } from "@storyshelf/core/adapter/design";
|
|
12
|
-
|
|
12
|
+
// src/routers/settings-design.ts
|
|
13
13
|
/** Design settings (project design files, component mapping, provider credentials). */
|
|
14
14
|
function registerDesignSettings(app) {
|
|
15
15
|
const base = "/projects/:slug/settings/design";
|
|
@@ -97,7 +97,6 @@ async function handleRemoveToken(c) {
|
|
|
97
97
|
await credentialModel().remove(project.id, provider.metadata.kind);
|
|
98
98
|
return done(c, project.slug, `${provider.metadata.name} token removed`);
|
|
99
99
|
}
|
|
100
|
-
//#endregion
|
|
101
100
|
export { registerDesignSettings };
|
|
102
101
|
|
|
103
102
|
//# sourceMappingURL=settings-design.mjs.map
|
|
@@ -10,7 +10,7 @@ import { runSwitchOffPass } from "./review-switch-off.mjs";
|
|
|
10
10
|
import { asString, findProject, renderSettingsPage } from "./settings.handlers.mjs";
|
|
11
11
|
import { resolveBrowsers } from "@storyshelf/core/capture";
|
|
12
12
|
import { ProjectModel } from "@storyshelf/core/models";
|
|
13
|
-
|
|
13
|
+
// src/routers/settings-general.ts
|
|
14
14
|
/** General project settings (name, repo, thresholds) and danger-zone delete. */
|
|
15
15
|
function registerGeneralSettings(app) {
|
|
16
16
|
app.get("/projects/:slug/settings", async (c) => c.html(await renderSettingsPage(c, "general")));
|
|
@@ -143,7 +143,6 @@ function approvalsOf(form, project) {
|
|
|
143
143
|
function reviewSavedMessage(accepted) {
|
|
144
144
|
return accepted > 0 ? `Review settings saved; accepted ${accepted} pending build${accepted === 1 ? "" : "s"}` : "Review settings saved";
|
|
145
145
|
}
|
|
146
|
-
//#endregion
|
|
147
146
|
export { registerGeneralSettings };
|
|
148
147
|
|
|
149
148
|
//# sourceMappingURL=settings-general.mjs.map
|
|
@@ -6,7 +6,7 @@ import { flash } from "./flash.mjs";
|
|
|
6
6
|
import { notificationChannelModel } from "./scoped-models.mjs";
|
|
7
7
|
import { asString, renderSettingsPage } from "./settings.handlers.mjs";
|
|
8
8
|
import { NotificationChannelModel } from "@storyshelf/core/models";
|
|
9
|
-
|
|
9
|
+
// src/routers/settings-git-channel.ts
|
|
10
10
|
/** Create a git (`state`) channel from the settings form: repository, status prefix and token. */
|
|
11
11
|
const CONFIG_FIELDS = [
|
|
12
12
|
"owner",
|
|
@@ -71,7 +71,6 @@ function gitChannelProblem(project, provider, config, secret) {
|
|
|
71
71
|
});
|
|
72
72
|
return checked.ok ? null : fieldErrors(secret.secretField, checked);
|
|
73
73
|
}
|
|
74
|
-
//#endregion
|
|
75
74
|
export { createGitChannel };
|
|
76
75
|
|
|
77
76
|
//# sourceMappingURL=settings-git-channel.mjs.map
|
|
@@ -6,7 +6,7 @@ import { hxRedirect } from "./htmx.mjs";
|
|
|
6
6
|
import { flash } from "./flash.mjs";
|
|
7
7
|
import { asString, findProject, renderSettingsPage } from "./settings.handlers.mjs";
|
|
8
8
|
import { LabelModel } from "@storyshelf/core/models";
|
|
9
|
-
|
|
9
|
+
// src/routers/settings-labels.ts
|
|
10
10
|
/** Label-type settings (create custom types, delete removable ones). */
|
|
11
11
|
function registerLabelSettings(app) {
|
|
12
12
|
app.get("/projects/:slug/settings/labels", async (c) => c.html(await renderSettingsPage(c, "labels", { editKey: c.req.query("edit") })));
|
|
@@ -72,7 +72,6 @@ async function handleDeleteLabelType(c) {
|
|
|
72
72
|
flash(c, "Label type deleted");
|
|
73
73
|
return hxRedirect(c, `/projects/${project.slug}/settings/labels`);
|
|
74
74
|
}
|
|
75
|
-
//#endregion
|
|
76
75
|
export { registerLabelSettings };
|
|
77
76
|
|
|
78
77
|
//# sourceMappingURL=settings-labels.mjs.map
|
|
@@ -6,7 +6,7 @@ import { hxRedirect } from "./htmx.mjs";
|
|
|
6
6
|
import { flash } from "./flash.mjs";
|
|
7
7
|
import { asString, findProject, renderSettingsPage } from "./settings.handlers.mjs";
|
|
8
8
|
import { PackageUsageModel, ProjectModel } from "@storyshelf/core/models";
|
|
9
|
-
|
|
9
|
+
// src/routers/settings-links.ts
|
|
10
10
|
/** Package-link settings (link a consumed package to its upstream project, or unlink it). */
|
|
11
11
|
function registerLinkSettings(app) {
|
|
12
12
|
app.get("/projects/:slug/settings/links", async (c) => c.html(await renderSettingsPage(c, "links")));
|
|
@@ -57,7 +57,6 @@ async function handleRemoveLink(c) {
|
|
|
57
57
|
flash(c, "Link removed");
|
|
58
58
|
return hxRedirect(c, `/projects/${project.slug}/settings/links`);
|
|
59
59
|
}
|
|
60
|
-
//#endregion
|
|
61
60
|
export { registerLinkSettings };
|
|
62
61
|
|
|
63
62
|
//# sourceMappingURL=settings-links.mjs.map
|
|
@@ -7,7 +7,7 @@ import { flash } from "./flash.mjs";
|
|
|
7
7
|
import { asString, findProject, renderSettingsPage } from "./settings.handlers.mjs";
|
|
8
8
|
import { MemberModel, ProjectGroupMappingModel } from "@storyshelf/core/models";
|
|
9
9
|
import { isProjectRole } from "@storyshelf/core/types";
|
|
10
|
-
|
|
10
|
+
// src/routers/settings-members.ts
|
|
11
11
|
/** Project member settings (assign roles, remove members). */
|
|
12
12
|
function registerMemberSettings(app) {
|
|
13
13
|
app.get("/projects/:slug/settings/members", async (c) => c.html(await renderSettingsPage(c, "members")));
|
|
@@ -49,7 +49,6 @@ function registerGroupMappingSettings(app) {
|
|
|
49
49
|
return hxRedirect(c, `/projects/${project.slug}/settings/members`);
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
-
//#endregion
|
|
53
52
|
export { registerMemberSettings };
|
|
54
53
|
|
|
55
54
|
//# sourceMappingURL=settings-members.mjs.map
|
|
@@ -7,7 +7,7 @@ import { notificationChannelModel } from "./scoped-models.mjs";
|
|
|
7
7
|
import { asString, findProject, renderSettingsPage } from "./settings.handlers.mjs";
|
|
8
8
|
import { createGitChannel } from "./settings-git-channel.mjs";
|
|
9
9
|
import { NotificationChannelModel, NotificationSubscriptionModel } from "@storyshelf/core/models";
|
|
10
|
-
|
|
10
|
+
// src/routers/settings-notifications.ts
|
|
11
11
|
/** Notifications settings (message and git channels plus my preferences). */
|
|
12
12
|
function registerNotificationSettings(app) {
|
|
13
13
|
app.get("/projects/:slug/settings/notifications", async (c) => c.html(await renderSettingsPage(c, "notifications")));
|
|
@@ -107,7 +107,6 @@ async function saveSubscription(project, userId, form) {
|
|
|
107
107
|
}
|
|
108
108
|
await model.remove(project.id, userId);
|
|
109
109
|
}
|
|
110
|
-
//#endregion
|
|
111
110
|
export { registerNotificationSettings };
|
|
112
111
|
|
|
113
112
|
//# sourceMappingURL=settings-notifications.mjs.map
|
|
@@ -6,7 +6,7 @@ import { hxRedirect } from "./htmx.mjs";
|
|
|
6
6
|
import { flash } from "./flash.mjs";
|
|
7
7
|
import { asString, findProject, renderSettingsPage } from "./settings.handlers.mjs";
|
|
8
8
|
import { TokenModel } from "@storyshelf/core/models";
|
|
9
|
-
|
|
9
|
+
// src/routers/settings-tokens.ts
|
|
10
10
|
/** CI token settings (create with one-time display, delete). */
|
|
11
11
|
function registerTokenSettings(app) {
|
|
12
12
|
app.get("/projects/:slug/settings/tokens", async (c) => c.html(await renderSettingsPage(c, "tokens")));
|
|
@@ -31,7 +31,6 @@ async function handleDeleteToken(c) {
|
|
|
31
31
|
flash(c, "Token deleted");
|
|
32
32
|
return hxRedirect(c, `/projects/${project.slug}/settings/tokens`);
|
|
33
33
|
}
|
|
34
|
-
//#endregion
|
|
35
34
|
export { registerTokenSettings };
|
|
36
35
|
|
|
37
36
|
//# sourceMappingURL=settings-tokens.mjs.map
|
|
@@ -7,7 +7,7 @@ import { flash } from "./flash.mjs";
|
|
|
7
7
|
import { webhookModel } from "./scoped-models.mjs";
|
|
8
8
|
import { asString, findProject, renderSettingsPage } from "./settings.handlers.mjs";
|
|
9
9
|
import { WebhookModel } from "@storyshelf/core/models";
|
|
10
|
-
|
|
10
|
+
// src/routers/settings-webhooks.ts
|
|
11
11
|
/** Webhook settings (create with one-time secret display, delete). */
|
|
12
12
|
function registerWebhookSettings(app) {
|
|
13
13
|
app.get("/projects/:slug/settings/webhooks", async (c) => c.html(await renderSettingsPage(c, "webhooks")));
|
|
@@ -44,7 +44,6 @@ async function handleDeleteWebhook(c) {
|
|
|
44
44
|
flash(c, "Webhook deleted");
|
|
45
45
|
return hxRedirect(c, `/projects/${project.slug}/settings/webhooks`);
|
|
46
46
|
}
|
|
47
|
-
//#endregion
|
|
48
47
|
export { registerWebhookSettings };
|
|
49
48
|
|
|
50
49
|
//# sourceMappingURL=settings-webhooks.mjs.map
|
|
@@ -9,7 +9,7 @@ import { runnerTargets } from "../targets.mjs";
|
|
|
9
9
|
import { isSiteAdmin } from "./insights.handlers.mjs";
|
|
10
10
|
import { renderProjectSettingsPage } from "../pages/project-settings.mjs";
|
|
11
11
|
import { DesignCredentialModel, DesignModel, LabelModel, MemberModel, NotificationChannelModel, NotificationSubscriptionModel, PackageUsageModel, ProjectGroupMappingModel, ProjectModel, TokenModel, WebhookModel, credentialDaysLeft } from "@storyshelf/core/models";
|
|
12
|
-
|
|
12
|
+
// src/routers/settings.handlers.ts
|
|
13
13
|
/** Tokens without hashes for the settings UI. */
|
|
14
14
|
async function loadTokenSummaries(db, projectId) {
|
|
15
15
|
return (await new TokenModel(db).list(projectId)).map(({ hash: _hash, ...rest }) => rest);
|
|
@@ -224,7 +224,6 @@ async function findProject(slug) {
|
|
|
224
224
|
function asString(value) {
|
|
225
225
|
return typeof value === "string" ? value.trim() : void 0;
|
|
226
226
|
}
|
|
227
|
-
//#endregion
|
|
228
227
|
export { asString, findProject, renderSettingsPage };
|
|
229
228
|
|
|
230
229
|
//# sourceMappingURL=settings.handlers.mjs.map
|
|
@@ -11,7 +11,7 @@ import { registerMemberSettings } from "./settings-members.mjs";
|
|
|
11
11
|
import { registerNotificationSettings } from "./settings-notifications.mjs";
|
|
12
12
|
import { registerTokenSettings } from "./settings-tokens.mjs";
|
|
13
13
|
import { registerWebhookSettings } from "./settings-webhooks.mjs";
|
|
14
|
-
|
|
14
|
+
// src/routers/settings.ts
|
|
15
15
|
/** Register the server-rendered project settings pages and their form handlers. */
|
|
16
16
|
function registerSettingsPages(app) {
|
|
17
17
|
registerProjectSettings(app);
|
|
@@ -31,7 +31,6 @@ function registerCatalogSettings(app) {
|
|
|
31
31
|
registerLinkSettings(app);
|
|
32
32
|
registerDesignSettings(app);
|
|
33
33
|
}
|
|
34
|
-
//#endregion
|
|
35
34
|
export { registerSettingsPages };
|
|
36
35
|
|
|
37
36
|
//# sourceMappingURL=settings.mjs.map
|
|
@@ -7,13 +7,15 @@ import { publishedAccess } from "./published-access.mjs";
|
|
|
7
7
|
import { SIGNED_URL_HEADERS, publishedRelativePath, servePublishedFile } from "./published-files.mjs";
|
|
8
8
|
import { STORYBOOK_TOKEN_PREFIX, storybookTokenPath, verifyStorybookToken } from "@storyshelf/core/utils";
|
|
9
9
|
import { BuildModel, ProjectModel } from "@storyshelf/core/models";
|
|
10
|
-
|
|
10
|
+
// src/routers/signed-storybook.ts
|
|
11
11
|
/**
|
|
12
12
|
* Signed Storybook URLs (ADR 0007, decision 5): `GET /_s/<token>/<file>` serves one build's static
|
|
13
13
|
* Storybook files to a remote browser, with no session. The HMAC token is the only credential; it
|
|
14
14
|
* names one project and one build and expires. The file itself is served by the same handler and
|
|
15
15
|
* access policy as the other published routes (`published-files.ts`, `published-access.ts`).
|
|
16
16
|
* Nothing else lives under `/_s/`: no wrapper page, no API, no other build, no listing.
|
|
17
|
+
* Answers are `private, no-store`, except a success for a capture-purpose grant when the server
|
|
18
|
+
* opted in (`captureAssets.cacheMaxAgeSeconds`, see `capture-cache.ts`).
|
|
17
19
|
*/
|
|
18
20
|
/** Register the signed-URL file route. */
|
|
19
21
|
function registerSignedStorybook(app) {
|
|
@@ -39,7 +41,8 @@ async function serveGranted(c, token, rel) {
|
|
|
39
41
|
buildId,
|
|
40
42
|
rel,
|
|
41
43
|
access,
|
|
42
|
-
cacheControl: ""
|
|
44
|
+
cacheControl: "",
|
|
45
|
+
grant: verified.claims
|
|
43
46
|
});
|
|
44
47
|
}
|
|
45
48
|
/** The one answer for every refusal, so a caller cannot tell a bad signature from an expired token. */
|
|
@@ -47,7 +50,6 @@ function refuse(c, reason) {
|
|
|
47
50
|
getStore().logger.debug({ reason }, "signed storybook request refused");
|
|
48
51
|
return c.text("Not found", 404, SIGNED_URL_HEADERS);
|
|
49
52
|
}
|
|
50
|
-
//#endregion
|
|
51
53
|
export { registerSignedStorybook };
|
|
52
54
|
|
|
53
55
|
//# sourceMappingURL=signed-storybook.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signed-storybook.mjs","names":[],"sources":["../../src/routers/signed-storybook.ts"],"sourcesContent":["/**\n * Signed Storybook URLs (ADR 0007, decision 5): `GET /_s/<token>/<file>` serves one build's static\n * Storybook files to a remote browser, with no session. The HMAC token is the only credential; it\n * names one project and one build and expires. The file itself is served by the same handler and\n * access policy as the other published routes (`published-files.ts`, `published-access.ts`).\n * Nothing else lives under `/_s/`: no wrapper page, no API, no other build, no listing.\n */\nimport { BuildModel, ProjectModel } from \"@storyshelf/core/models\";\nimport {\n STORYBOOK_TOKEN_PREFIX,\n storybookTokenPath,\n verifyStorybookToken,\n} from \"@storyshelf/core/utils\";\nimport type { Context } from \"hono\";\nimport type { ShelfRouter } from \"../app-types.ts\";\nimport { credentialKeys } from \"../credential-keys.ts\";\nimport { getStore } from \"../store.ts\";\nimport { publishedAccess } from \"./published-access.ts\";\nimport {\n publishedRelativePath,\n servePublishedFile,\n SIGNED_URL_HEADERS,\n} from \"./published-files.ts\";\n\n/** Register the signed-URL file route. */\nexport function registerSignedStorybook(app: ShelfRouter): void {\n app.get(`${STORYBOOK_TOKEN_PREFIX}:token/*`, async (c) => await serveSigned(c));\n}\n\nasync function serveSigned(c: Context): Promise<Response> {\n const token = c.req.param(\"token\") ?? \"\";\n const rel = publishedRelativePath(c.req.path, storybookTokenPath(token));\n if (!rel) {\n return refuse(c, \"bad path\");\n }\n return (await serveGranted(c, token, rel)) ?? refuse(c, \"no such file\");\n}\n\n/** The file the token grants as a response, or null (for the debug log only: why not). */\nasync function serveGranted(c: Context, token: string, rel: string): Promise<Response | null> {\n const { config, db } = getStore();\n const verified = verifyStorybookToken(token, credentialKeys(config));\n if (!verified.ok) {\n return refuse(c, verified.reason);\n }\n const { projectId, buildId } = verified.claims;\n const [project, build] = await Promise.all([\n new ProjectModel(db).get(projectId),\n new BuildModel(db).get(buildId),\n ]);\n const access = project && build ? await publishedAccess(build, project, verified.claims) : null;\n if (access !== \"token\") {\n return refuse(c, \"unknown build\");\n }\n return await servePublishedFile(c, {
|
|
1
|
+
{"version":3,"file":"signed-storybook.mjs","names":[],"sources":["../../src/routers/signed-storybook.ts"],"sourcesContent":["/**\n * Signed Storybook URLs (ADR 0007, decision 5): `GET /_s/<token>/<file>` serves one build's static\n * Storybook files to a remote browser, with no session. The HMAC token is the only credential; it\n * names one project and one build and expires. The file itself is served by the same handler and\n * access policy as the other published routes (`published-files.ts`, `published-access.ts`).\n * Nothing else lives under `/_s/`: no wrapper page, no API, no other build, no listing.\n * Answers are `private, no-store`, except a success for a capture-purpose grant when the server\n * opted in (`captureAssets.cacheMaxAgeSeconds`, see `capture-cache.ts`).\n */\nimport { BuildModel, ProjectModel } from \"@storyshelf/core/models\";\nimport {\n STORYBOOK_TOKEN_PREFIX,\n storybookTokenPath,\n verifyStorybookToken,\n} from \"@storyshelf/core/utils\";\nimport type { Context } from \"hono\";\nimport type { ShelfRouter } from \"../app-types.ts\";\nimport { credentialKeys } from \"../credential-keys.ts\";\nimport { getStore } from \"../store.ts\";\nimport { publishedAccess } from \"./published-access.ts\";\nimport {\n publishedRelativePath,\n servePublishedFile,\n SIGNED_URL_HEADERS,\n} from \"./published-files.ts\";\n\n/** Register the signed-URL file route. */\nexport function registerSignedStorybook(app: ShelfRouter): void {\n app.get(`${STORYBOOK_TOKEN_PREFIX}:token/*`, async (c) => await serveSigned(c));\n}\n\nasync function serveSigned(c: Context): Promise<Response> {\n const token = c.req.param(\"token\") ?? \"\";\n const rel = publishedRelativePath(c.req.path, storybookTokenPath(token));\n if (!rel) {\n return refuse(c, \"bad path\");\n }\n return (await serveGranted(c, token, rel)) ?? refuse(c, \"no such file\");\n}\n\n/** The file the token grants as a response, or null (for the debug log only: why not). */\nasync function serveGranted(c: Context, token: string, rel: string): Promise<Response | null> {\n const { config, db } = getStore();\n const verified = verifyStorybookToken(token, credentialKeys(config));\n if (!verified.ok) {\n return refuse(c, verified.reason);\n }\n const { projectId, buildId } = verified.claims;\n const [project, build] = await Promise.all([\n new ProjectModel(db).get(projectId),\n new BuildModel(db).get(buildId),\n ]);\n const access = project && build ? await publishedAccess(build, project, verified.claims) : null;\n if (access !== \"token\") {\n return refuse(c, \"unknown build\");\n }\n return await servePublishedFile(c, {\n projectId,\n buildId,\n rel,\n access,\n cacheControl: \"\",\n grant: verified.claims,\n });\n}\n\n/** The one answer for every refusal, so a caller cannot tell a bad signature from an expired token. */\nfunction refuse(c: Context, reason: string): Response {\n getStore().logger.debug({ reason }, \"signed storybook request refused\");\n return c.text(\"Not found\", 404, SIGNED_URL_HEADERS);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,wBAAwB,KAAwB;CAC9D,IAAI,IAAI,GAAG,uBAAuB,WAAW,OAAO,MAAM,MAAM,YAAY,CAAC,CAAC;AAChF;AAEA,eAAe,YAAY,GAA+B;CACxD,MAAM,QAAQ,EAAE,IAAI,MAAM,OAAO,KAAK;CACtC,MAAM,MAAM,sBAAsB,EAAE,IAAI,MAAM,mBAAmB,KAAK,CAAC;CACvE,IAAI,CAAC,KACH,OAAO,OAAO,GAAG,UAAU;CAE7B,OAAQ,MAAM,aAAa,GAAG,OAAO,GAAG,KAAM,OAAO,GAAG,cAAc;AACxE;;AAGA,eAAe,aAAa,GAAY,OAAe,KAAuC;CAC5F,MAAM,EAAE,QAAQ,OAAO,SAAS;CAChC,MAAM,WAAW,qBAAqB,OAAO,eAAe,MAAM,CAAC;CACnE,IAAI,CAAC,SAAS,IACZ,OAAO,OAAO,GAAG,SAAS,MAAM;CAElC,MAAM,EAAE,WAAW,YAAY,SAAS;CACxC,MAAM,CAAC,SAAS,SAAS,MAAM,QAAQ,IAAI,CACzC,IAAI,aAAa,EAAE,CAAC,CAAC,IAAI,SAAS,GAClC,IAAI,WAAW,EAAE,CAAC,CAAC,IAAI,OAAO,CAChC,CAAC;CACD,MAAM,SAAS,WAAW,QAAQ,MAAM,gBAAgB,OAAO,SAAS,SAAS,MAAM,IAAI;CAC3F,IAAI,WAAW,SACb,OAAO,OAAO,GAAG,eAAe;CAElC,OAAO,MAAM,mBAAmB,GAAG;EACjC;EACA;EACA;EACA;EACA,cAAc;EACd,OAAO,SAAS;CAClB,CAAC;AACH;;AAGA,SAAS,OAAO,GAAY,QAA0B;CACpD,SAAS,CAAC,CAAC,OAAO,MAAM,EAAE,OAAO,GAAG,kCAAkC;CACtE,OAAO,EAAE,KAAK,aAAa,KAAK,kBAAkB;AACpD"}
|
|
@@ -6,7 +6,7 @@ import { notFound } from "./helpers.mjs";
|
|
|
6
6
|
import { baselineChanged, findDriftedSnapshots, isOpenSnapshot } from "./baseline-guard.mjs";
|
|
7
7
|
import { approveSnapshot, refreshBuild } from "./builds.handlers.mjs";
|
|
8
8
|
import { SnapshotModel } from "@storyshelf/core/models";
|
|
9
|
-
|
|
9
|
+
// src/routers/snapshot-groups.ts
|
|
10
10
|
/** Shared (story, mode) review: one decision covers every browser's snapshot. */
|
|
11
11
|
/** Identity of a review group: the same story and mode across engines. */
|
|
12
12
|
function groupKey(snapshot) {
|
|
@@ -57,7 +57,6 @@ function splitByDrift(open, drifted) {
|
|
|
57
57
|
skipped: open.filter((s) => blocked.has(groupKey(s)))
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
-
//#endregion
|
|
61
60
|
export { approveGroup, groupKey, rejectGroup, splitByDrift };
|
|
62
61
|
|
|
63
62
|
//# sourceMappingURL=snapshot-groups.mjs.map
|
|
@@ -14,7 +14,7 @@ import { createRoute, z } from "@hono/zod-openapi";
|
|
|
14
14
|
import { REDIFFABLE_BUILD_STATUSES, rediffBuild } from "@storyshelf/core/capture";
|
|
15
15
|
import { SnapshotModel } from "@storyshelf/core/models";
|
|
16
16
|
import { HTTPException } from "hono/http-exception";
|
|
17
|
-
|
|
17
|
+
// src/routers/snapshots.ts
|
|
18
18
|
const listSnapshotsRoute = createRoute({
|
|
19
19
|
method: "get",
|
|
20
20
|
tags: ["Review"],
|
|
@@ -332,7 +332,6 @@ function registerSnapshots(app) {
|
|
|
332
332
|
return c.json({ ok: true });
|
|
333
333
|
});
|
|
334
334
|
}
|
|
335
|
-
//#endregion
|
|
336
335
|
export { registerSnapshots };
|
|
337
336
|
|
|
338
337
|
//# sourceMappingURL=snapshots.mjs.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "node:path";
|
|
1
|
+
import { posix } from "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { getStore } from "../store.mjs";
|
|
@@ -8,8 +8,7 @@ import { publishedRelativePath, servePublishedFile } from "./published-files.mjs
|
|
|
8
8
|
import { renderStorybookPage, renderStorybookPreparingPage } from "../pages/storybook.mjs";
|
|
9
9
|
import { storybookDir } from "@storyshelf/core/utils";
|
|
10
10
|
import { BuildModel, LabelModel, ProjectModel } from "@storyshelf/core/models";
|
|
11
|
-
|
|
12
|
-
//#region src/routers/storybook.ts
|
|
11
|
+
// src/routers/storybook.ts
|
|
13
12
|
/** Check whether a build's extracted statics are available for serving. */
|
|
14
13
|
async function staticsReady(projectId, buildId) {
|
|
15
14
|
if (await getStore().storage.exists(posix.join(storybookDir(projectId, buildId), "manifest.json"))) return true;
|
|
@@ -28,19 +27,6 @@ function registerStorybook(app) {
|
|
|
28
27
|
if (!await publishedAccess(build, project)) return c.redirect("/auth/login", 302);
|
|
29
28
|
return c.redirect(`/projects/${slug}/storybook/build/${build.id}/`, 302);
|
|
30
29
|
});
|
|
31
|
-
app.get("/projects/:slug/storybook/:key/:value", async (c) => {
|
|
32
|
-
const slug = c.req.param("slug");
|
|
33
|
-
const key = c.req.param("key");
|
|
34
|
-
if (key === "build") return c.notFound();
|
|
35
|
-
const value = c.req.param("value");
|
|
36
|
-
const project = await new ProjectModel(getStore().db).getBySlug(slug);
|
|
37
|
-
if (!project) notFound("Project not found");
|
|
38
|
-
const buildId = await new LabelModel(getStore().db).latestBuildId(project.id, key, value);
|
|
39
|
-
if (!buildId) notFound("No build carries that label");
|
|
40
|
-
const build = await new BuildModel(getStore().db).get(buildId);
|
|
41
|
-
if (!build || !await publishedAccess(build, project)) return c.redirect("/auth/login", 302);
|
|
42
|
-
return c.redirect(`/projects/${slug}/storybook/build/${buildId}/`, 302);
|
|
43
|
-
});
|
|
44
30
|
app.get("/_/:id", async (c) => {
|
|
45
31
|
const id = c.req.param("id");
|
|
46
32
|
if (id.length === 26 && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(id)) {
|
|
@@ -108,13 +94,31 @@ function registerStorybook(app) {
|
|
|
108
94
|
cacheControl: "public, max-age=31536000, immutable"
|
|
109
95
|
}) ?? c.notFound();
|
|
110
96
|
});
|
|
97
|
+
app.get("/projects/:slug/storybook/:key/*", async (c) => {
|
|
98
|
+
const slug = c.req.param("slug");
|
|
99
|
+
const key = c.req.param("key");
|
|
100
|
+
if (key === "build") return c.notFound();
|
|
101
|
+
let value;
|
|
102
|
+
try {
|
|
103
|
+
value = decodeURIComponent(c.req.path.split("/").slice(5).join("/"));
|
|
104
|
+
} catch {
|
|
105
|
+
return c.notFound();
|
|
106
|
+
}
|
|
107
|
+
if (!value) return c.notFound();
|
|
108
|
+
const project = await new ProjectModel(getStore().db).getBySlug(slug);
|
|
109
|
+
if (!project) notFound("Project not found");
|
|
110
|
+
const buildId = await new LabelModel(getStore().db).latestBuildId(project.id, key, value);
|
|
111
|
+
if (!buildId) notFound("No build carries that label");
|
|
112
|
+
const build = await new BuildModel(getStore().db).get(buildId);
|
|
113
|
+
if (!build || !await publishedAccess(build, project)) return c.redirect("/auth/login", 302);
|
|
114
|
+
return c.redirect(`/projects/${slug}/storybook/build/${buildId}/`, 302);
|
|
115
|
+
});
|
|
111
116
|
}
|
|
112
117
|
/** Cache policy of the short link: a ULID names one build forever; a slug follows the latest. */
|
|
113
118
|
function shortLinkCache(isUlid26, isRoot) {
|
|
114
119
|
if (isUlid26) return "public, max-age=31536000, immutable";
|
|
115
120
|
return isRoot ? "public, max-age=60" : "public, max-age=3600";
|
|
116
121
|
}
|
|
117
|
-
//#endregion
|
|
118
122
|
export { registerStorybook };
|
|
119
123
|
|
|
120
124
|
//# sourceMappingURL=storybook.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storybook.mjs","names":[],"sources":["../../src/routers/storybook.ts"],"sourcesContent":["import { BuildModel } from \"@storyshelf/core/models\";\nimport { LabelModel } from \"@storyshelf/core/models\";\nimport { ProjectModel } from \"@storyshelf/core/models\";\nimport type { Build } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport { storybookDir } from \"@storyshelf/core/utils\";\nimport { posix } from \"node:path\";\nimport type { ShelfRouter } from \"../app-types.ts\";\nimport { renderStorybookPage, renderStorybookPreparingPage } from \"../pages/storybook.tsx\";\nimport { getStore } from \"../store.ts\";\nimport { notFound } from \"./helpers.ts\";\nimport { publishedAccess } from \"./published-access.ts\";\nimport { publishedRelativePath, servePublishedFile } from \"./published-files.ts\";\n\n/** Check whether a build's extracted statics are available for serving. */\nasync function staticsReady(projectId: string, buildId: string): Promise<boolean> {\n // New path: manifest.json indicates content-addressed storage; fallback to legacy iframe.html\n if (\n await getStore().storage.exists(posix.join(storybookDir(projectId, buildId), \"manifest.json\"))\n ) {\n return true;\n }\n return await getStore().storage.exists(\n posix.join(storybookDir(projectId, buildId), \"iframe.html\"),\n );\n}\n\n/** Register the published Storybook resolver and static-asset routes. */\nexport function registerStorybook(app: ShelfRouter): void {\n app.get(\"/_\", (c) => c.redirect(\"/\", 302));\n app.get(\"/_/\", (c) => c.redirect(\"/\", 302));\n\n // Resolver: latest approved build on the default branch (never feature branches).\n app.get(\"/projects/:slug/storybook\", async (c) => {\n const slug = c.req.param(\"slug\");\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n notFound(\"Project not found\");\n }\n const build = await new BuildModel(getStore().db).latestApprovedDefault(project);\n if (!build) {\n notFound(\"No approved build on the default branch yet\");\n }\n if (!(await publishedAccess(build, project))) {\n return c.redirect(\"/auth/login\", 302);\n }\n return c.redirect(`/projects/${slug}/storybook/build/${build.id}/`, 302);\n });\n\n // Resolver: latest build bearing a label (`build` is a reserved label key).\n app.get(\"/projects/:slug/storybook/:key/:value\", async (c) => {\n const slug = c.req.param(\"slug\");\n const key = c.req.param(\"key\");\n if (key === \"build\") {\n return c.notFound();\n }\n const value = c.req.param(\"value\");\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n notFound(\"Project not found\");\n }\n const buildId = await new LabelModel(getStore().db).latestBuildId(project.id, key, value);\n if (!buildId) {\n notFound(\"No build carries that label\");\n }\n const build = await new BuildModel(getStore().db).get(buildId);\n if (!build || !(await publishedAccess(build, project))) {\n return c.redirect(\"/auth/login\", 302);\n }\n return c.redirect(`/projects/${slug}/storybook/build/${buildId}/`, 302);\n });\n\n // Short link canonical is /_/:id/ with trailing slash (so relative ./sb-manager/... resolves).\n // 26-char ULID → that exact build, whatever its status (visibility check only).\n // Any other id → project slug → latest approved build on the default branch, else 404\n // (no fallback to feature-branch or unreviewed builds). No fall-through on ULID.\n app.get(\"/_/:id\", async (c) => {\n const id = c.req.param(\"id\");\n const isUlid26 = id.length === 26 && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(id);\n if (isUlid26) {\n const build = await new BuildModel(getStore().db).get(id);\n if (!build) notFound();\n } else {\n const project = await new ProjectModel(getStore().db).getBySlug(id);\n if (!project) notFound();\n }\n return c.redirect(`/_/${id}/`, 302);\n });\n\n app.get(\"/_/:id/*\", async (c) => {\n const id = c.req.param(\"id\");\n const isUlid26 = id.length === 26 && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(id);\n const rest = publishedRelativePath(c.req.path, `/_/${id}/`);\n if (rest === null) return c.notFound();\n let project: Project | null = null;\n let build: Build | null = null;\n if (isUlid26) {\n build = await new BuildModel(getStore().db).get(id);\n if (!build) return c.notFound();\n project = await new ProjectModel(getStore().db).get(build.projectId);\n if (!project) return c.notFound();\n } else {\n project = await new ProjectModel(getStore().db).getBySlug(id);\n if (!project) return c.notFound();\n build = await new BuildModel(getStore().db).latestApprovedDefault(project);\n if (!build) return c.notFound();\n }\n const access = await publishedAccess(build, project);\n if (!access) return c.redirect(\"/auth/login\", 302);\n if (rest === \"\" && !(await staticsReady(project.id, build.id))) {\n return c.html(renderStorybookPreparingPage(project, build, project.slug), 200);\n }\n const fallback = isUlid26\n ? `/projects/${project.slug}/builds/${build.id}`\n : `/projects/${project.slug}`;\n const served = await servePublishedFile(c, {\n projectId: project.id,\n buildId: build.id,\n rel: rest === \"\" ? \"index.html\" : rest,\n access,\n cacheControl: shortLinkCache(isUlid26, rest === \"\"),\n });\n if (served) return served;\n return rest === \"\" || rest === \"index.html\" ? c.redirect(fallback, 302) : c.notFound();\n });\n\n // Canonical build route: serves the static Storybook and the landing page.\n app.get(\"/projects/:slug/storybook/build/:buildId/*\", async (c) => {\n const slug = c.req.param(\"slug\");\n const buildId = c.req.param(\"buildId\");\n // Hono's bare `/*` wildcard is not exposed through `param()`, so derive the\n // remainder from the raw request path (un-encoded) and decode it ourselves.\n const rest = publishedRelativePath(c.req.path, `/projects/${slug}/storybook/build/${buildId}/`);\n if (rest === null) {\n return c.notFound();\n }\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n notFound(\"Project not found\");\n }\n const build = await new BuildModel(getStore().db).get(buildId);\n if (!build || build.projectId !== project.id) {\n notFound(\"Build not found\");\n }\n const access = await publishedAccess(build, project);\n if (!access) {\n return c.redirect(\"/auth/login\", 302);\n }\n\n if (rest === \"\") {\n if (!(await staticsReady(project.id, build.id))) {\n return c.html(renderStorybookPreparingPage(project, build, slug), 200);\n }\n const storyId = c.req.query(\"storyId\") ?? c.req.query(\"id\") ?? undefined;\n const viewMode = c.req.query(\"viewMode\") === \"docs\" ? \"docs\" : undefined;\n return c.html(renderStorybookPage(project, build, slug, { storyId, viewMode }), 200);\n }\n\n return (\n (await servePublishedFile(c, {\n projectId: project.id,\n buildId: build.id,\n rel: rest,\n access,\n cacheControl: \"public, max-age=31536000, immutable\",\n })) ?? c.notFound()\n );\n });\n}\n\n/** Cache policy of the short link: a ULID names one build forever; a slug follows the latest. */\nfunction shortLinkCache(isUlid26: boolean, isRoot: boolean): string {\n if (isUlid26) {\n return \"public, max-age=31536000, immutable\";\n }\n return isRoot ? \"public, max-age=60\" : \"public, max-age=3600\";\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"storybook.mjs","names":[],"sources":["../../src/routers/storybook.ts"],"sourcesContent":["import { BuildModel } from \"@storyshelf/core/models\";\nimport { LabelModel } from \"@storyshelf/core/models\";\nimport { ProjectModel } from \"@storyshelf/core/models\";\nimport type { Build } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport { storybookDir } from \"@storyshelf/core/utils\";\nimport { posix } from \"node:path\";\nimport type { ShelfRouter } from \"../app-types.ts\";\nimport { renderStorybookPage, renderStorybookPreparingPage } from \"../pages/storybook.tsx\";\nimport { getStore } from \"../store.ts\";\nimport { notFound } from \"./helpers.ts\";\nimport { publishedAccess } from \"./published-access.ts\";\nimport { publishedRelativePath, servePublishedFile } from \"./published-files.ts\";\n\n/** Check whether a build's extracted statics are available for serving. */\nasync function staticsReady(projectId: string, buildId: string): Promise<boolean> {\n // New path: manifest.json indicates content-addressed storage; fallback to legacy iframe.html\n if (\n await getStore().storage.exists(posix.join(storybookDir(projectId, buildId), \"manifest.json\"))\n ) {\n return true;\n }\n return await getStore().storage.exists(\n posix.join(storybookDir(projectId, buildId), \"iframe.html\"),\n );\n}\n\n/** Register the published Storybook resolver and static-asset routes. */\nexport function registerStorybook(app: ShelfRouter): void {\n app.get(\"/_\", (c) => c.redirect(\"/\", 302));\n app.get(\"/_/\", (c) => c.redirect(\"/\", 302));\n\n // Resolver: latest approved build on the default branch (never feature branches).\n app.get(\"/projects/:slug/storybook\", async (c) => {\n const slug = c.req.param(\"slug\");\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n notFound(\"Project not found\");\n }\n const build = await new BuildModel(getStore().db).latestApprovedDefault(project);\n if (!build) {\n notFound(\"No approved build on the default branch yet\");\n }\n if (!(await publishedAccess(build, project))) {\n return c.redirect(\"/auth/login\", 302);\n }\n return c.redirect(`/projects/${slug}/storybook/build/${build.id}/`, 302);\n });\n\n // Short link canonical is /_/:id/ with trailing slash (so relative ./sb-manager/... resolves).\n // 26-char ULID → that exact build, whatever its status (visibility check only).\n // Any other id → project slug → latest approved build on the default branch, else 404\n // (no fallback to feature-branch or unreviewed builds). No fall-through on ULID.\n app.get(\"/_/:id\", async (c) => {\n const id = c.req.param(\"id\");\n const isUlid26 = id.length === 26 && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(id);\n if (isUlid26) {\n const build = await new BuildModel(getStore().db).get(id);\n if (!build) notFound();\n } else {\n const project = await new ProjectModel(getStore().db).getBySlug(id);\n if (!project) notFound();\n }\n return c.redirect(`/_/${id}/`, 302);\n });\n\n app.get(\"/_/:id/*\", async (c) => {\n const id = c.req.param(\"id\");\n const isUlid26 = id.length === 26 && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(id);\n const rest = publishedRelativePath(c.req.path, `/_/${id}/`);\n if (rest === null) return c.notFound();\n let project: Project | null = null;\n let build: Build | null = null;\n if (isUlid26) {\n build = await new BuildModel(getStore().db).get(id);\n if (!build) return c.notFound();\n project = await new ProjectModel(getStore().db).get(build.projectId);\n if (!project) return c.notFound();\n } else {\n project = await new ProjectModel(getStore().db).getBySlug(id);\n if (!project) return c.notFound();\n build = await new BuildModel(getStore().db).latestApprovedDefault(project);\n if (!build) return c.notFound();\n }\n const access = await publishedAccess(build, project);\n if (!access) return c.redirect(\"/auth/login\", 302);\n if (rest === \"\" && !(await staticsReady(project.id, build.id))) {\n return c.html(renderStorybookPreparingPage(project, build, project.slug), 200);\n }\n const fallback = isUlid26\n ? `/projects/${project.slug}/builds/${build.id}`\n : `/projects/${project.slug}`;\n const served = await servePublishedFile(c, {\n projectId: project.id,\n buildId: build.id,\n rel: rest === \"\" ? \"index.html\" : rest,\n access,\n cacheControl: shortLinkCache(isUlid26, rest === \"\"),\n });\n if (served) return served;\n return rest === \"\" || rest === \"index.html\" ? c.redirect(fallback, 302) : c.notFound();\n });\n\n // Canonical build route: serves the static Storybook and the landing page.\n app.get(\"/projects/:slug/storybook/build/:buildId/*\", async (c) => {\n const slug = c.req.param(\"slug\");\n const buildId = c.req.param(\"buildId\");\n // Hono's bare `/*` wildcard is not exposed through `param()`, so derive the\n // remainder from the raw request path (un-encoded) and decode it ourselves.\n const rest = publishedRelativePath(c.req.path, `/projects/${slug}/storybook/build/${buildId}/`);\n if (rest === null) {\n return c.notFound();\n }\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n notFound(\"Project not found\");\n }\n const build = await new BuildModel(getStore().db).get(buildId);\n if (!build || build.projectId !== project.id) {\n notFound(\"Build not found\");\n }\n const access = await publishedAccess(build, project);\n if (!access) {\n return c.redirect(\"/auth/login\", 302);\n }\n\n if (rest === \"\") {\n if (!(await staticsReady(project.id, build.id))) {\n return c.html(renderStorybookPreparingPage(project, build, slug), 200);\n }\n const storyId = c.req.query(\"storyId\") ?? c.req.query(\"id\") ?? undefined;\n const viewMode = c.req.query(\"viewMode\") === \"docs\" ? \"docs\" : undefined;\n return c.html(renderStorybookPage(project, build, slug, { storyId, viewMode }), 200);\n }\n\n return (\n (await servePublishedFile(c, {\n projectId: project.id,\n buildId: build.id,\n rel: rest,\n access,\n cacheControl: \"public, max-age=31536000, immutable\",\n })) ?? c.notFound()\n );\n });\n\n // Resolver: latest build bearing a label (`build` is a reserved label key).\n app.get(\"/projects/:slug/storybook/:key/*\", async (c) => {\n const slug = c.req.param(\"slug\");\n const key = c.req.param(\"key\");\n if (key === \"build\") {\n return c.notFound();\n }\n let value: string;\n try {\n value = decodeURIComponent(c.req.path.split(\"/\").slice(5).join(\"/\"));\n } catch {\n return c.notFound();\n }\n if (!value) return c.notFound();\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n notFound(\"Project not found\");\n }\n const buildId = await new LabelModel(getStore().db).latestBuildId(project.id, key, value);\n if (!buildId) {\n notFound(\"No build carries that label\");\n }\n const build = await new BuildModel(getStore().db).get(buildId);\n if (!build || !(await publishedAccess(build, project))) {\n return c.redirect(\"/auth/login\", 302);\n }\n return c.redirect(`/projects/${slug}/storybook/build/${buildId}/`, 302);\n });\n}\n\n/** Cache policy of the short link: a ULID names one build forever; a slug follows the latest. */\nfunction shortLinkCache(isUlid26: boolean, isRoot: boolean): string {\n if (isUlid26) {\n return \"public, max-age=31536000, immutable\";\n }\n return isRoot ? \"public, max-age=60\" : \"public, max-age=3600\";\n}\n"],"mappings":"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"}
|
package/dist/routers/tokens.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { notFound, resolveAuthorizedProject } from "./helpers.mjs";
|
|
|
6
6
|
import { notFound as notFound$1, tokenCreateSchema, tokenCreatedSchema, tokenPublicSchema, unauthorized } from "./schemas.mjs";
|
|
7
7
|
import { createRoute, z } from "@hono/zod-openapi";
|
|
8
8
|
import { TokenModel } from "@storyshelf/core/models";
|
|
9
|
-
|
|
9
|
+
// src/routers/tokens.ts
|
|
10
10
|
const ADMIN_ROLES = ["admin"];
|
|
11
11
|
const listTokensRoute = createRoute({
|
|
12
12
|
method: "get",
|
|
@@ -82,7 +82,6 @@ function registerTokens(app) {
|
|
|
82
82
|
return c.body(null, 204);
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
|
-
//#endregion
|
|
86
85
|
export { registerTokens };
|
|
87
86
|
|
|
88
87
|
//# sourceMappingURL=tokens.mjs.map
|
package/dist/routers/ui.mjs
CHANGED
|
@@ -19,10 +19,11 @@ import { renderProjectBuildsPage } from "../pages/project-builds.mjs";
|
|
|
19
19
|
import { renderProjectCreatePage } from "../pages/project-create.mjs";
|
|
20
20
|
import { renderProjectsPage } from "../pages/projects.mjs";
|
|
21
21
|
import { renderRootPage } from "../pages/root.mjs";
|
|
22
|
+
import { loadBaselineReference, loadDefaultComparison } from "./baseline-reference.mjs";
|
|
22
23
|
import { loadComponentPage } from "./component.handlers.mjs";
|
|
23
24
|
import { registerSettingsPages } from "./settings.mjs";
|
|
24
25
|
import { BuildModel, CommentModel, LabelModel, ProjectModel, SnapshotModel } from "@storyshelf/core/models";
|
|
25
|
-
|
|
26
|
+
// src/routers/ui.ts
|
|
26
27
|
function asString(value) {
|
|
27
28
|
return typeof value === "string" ? value.trim() : void 0;
|
|
28
29
|
}
|
|
@@ -145,6 +146,8 @@ function registerUiPages(app) {
|
|
|
145
146
|
canReview,
|
|
146
147
|
hasBaseline,
|
|
147
148
|
drifted,
|
|
149
|
+
reference: selected ? await loadBaselineReference(project, build, selected) : void 0,
|
|
150
|
+
defaultComparison: selected ? await loadDefaultComparison(project, build, selected) : null,
|
|
148
151
|
insight: await loadInsightPanel(project, build),
|
|
149
152
|
design,
|
|
150
153
|
usage: await linkedUsageFor(c, project, build.id, selectedSnapshot(snapshots, snapshotId)?.storyImportPath ?? null).catch(() => []),
|
|
@@ -189,7 +192,6 @@ async function loadBuildUsage(c, buildId) {
|
|
|
189
192
|
return /* @__PURE__ */ new Map();
|
|
190
193
|
}
|
|
191
194
|
}
|
|
192
|
-
//#endregion
|
|
193
195
|
export { registerUiPages };
|
|
194
196
|
|
|
195
197
|
//# sourceMappingURL=ui.mjs.map
|
package/dist/routers/ui.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.mjs","names":[],"sources":["../../src/routers/ui.ts"],"sourcesContent":["import { BuildModel } from \"@storyshelf/core/models\";\nimport { CommentModel } from \"@storyshelf/core/models\";\nimport { LabelModel } from \"@storyshelf/core/models\";\nimport { ProjectModel } from \"@storyshelf/core/models\";\nimport { SnapshotModel } from \"@storyshelf/core/models\";\nimport type { ProjectRole } from \"@storyshelf/core/types\";\nimport type { Context } from \"hono\";\nimport type { ShelfRouter } from \"../app-types.ts\";\nimport { loadInsightPanel } from \"../insights/panel.ts\";\nimport { renderBuildDetailPage } from \"../pages/build-detail.tsx\";\nimport type { LinkedUsageItem } from \"../pages/build-diff-usage.tsx\";\nimport { renderBuildDiffPage, selectedSnapshot } from \"../pages/build-diff.tsx\";\nimport { renderComponentPage } from \"../pages/component-detail.tsx\";\nimport { renderComputeJobsPage, renderActiveQueue } from \"../pages/compute-jobs.tsx\";\nimport { renderLabelDetailPage, renderLabelsPage } from \"../pages/label-detail.tsx\";\nimport { renderLibraryPage } from \"../pages/library.tsx\";\nimport { renderProjectBuildsPage } from \"../pages/project-builds.tsx\";\nimport { renderProjectCreatePage } from \"../pages/project-create.tsx\";\nimport { renderProjectsPage } from \"../pages/projects.tsx\";\nimport { renderRootPage } from \"../pages/root.tsx\";\nimport { getStore } from \"../store.ts\";\nimport { loadBaselineView } from \"./baseline-guard.ts\";\nimport { loadComponentPage } from \"./component.handlers.ts\";\nimport {\n designUrlsFor,\n loadDesignResolutions,\n loadReviewDesign,\n returnPathOf,\n} from \"./design.handlers.ts\";\nimport { flash } from \"./flash.ts\";\nimport { currentProjectRole } from \"./helpers.ts\";\nimport { hxRedirect } from \"./htmx.ts\";\nimport { linkedUsageByStory, linkedUsageFor } from \"./links.handlers.ts\";\nimport { registerSettingsPages } from \"./settings.ts\";\nfunction asString(value: FormDataEntryValue | null): string | undefined {\n return typeof value === \"string\" ? value.trim() : undefined;\n}\n\nfunction decodeRest(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return \"\";\n }\n}\n\ninterface QueueView {\n buildId: string;\n status: string;\n queuedAt: string;\n startedAt?: string;\n finishedAt?: string;\n error?: string;\n}\n\nasync function getQueueView(): Promise<QueueView[]> {\n const queue = getStore().captureQueue;\n if (!queue) {\n return [];\n }\n const active = await queue.active();\n return active.map((entry) => ({\n buildId: entry.buildId,\n status: entry.status,\n queuedAt: entry.queuedAt,\n startedAt: entry.startedAt,\n finishedAt: entry.finishedAt,\n error: entry.error,\n }));\n}\n\nasync function canManageJobs(projectId: string): Promise<boolean> {\n const { authEnabled } = getStore();\n if (!authEnabled) {\n return true;\n }\n const roles: readonly ProjectRole[] = [\"developer\", \"approver\", \"admin\"];\n const role = await currentProjectRole(projectId);\n return Boolean(role && roles.includes(role));\n}\n\n/** Register the server-rendered HTML pages (projects, builds, diffs, labels, settings). */\nexport function registerUiPages(app: ShelfRouter): void {\n // eslint-disable-next-line promise-function-async -- renderRootPage returns RenderedContent (string | Promise<string>)\n app.get(\"/\", async (c) => c.html(await renderRootPage()));\n app.get(\"/projects\", async (c) => c.html(await renderProjectsPage()));\n // eslint-disable-next-line promise-function-async -- renderProjectCreatePage returns RenderedContent (string | Promise<string>)\n app.get(\"/projects/new\", async (c) => c.html(await renderProjectCreatePage()));\n\n app.post(\"/projects/new\", async (c) => {\n const form = await c.req.formData();\n const name = asString(form.get(\"name\"));\n const gitRepository = asString(form.get(\"gitRepository\"));\n const gitDefaultBranch = asString(form.get(\"gitDefaultBranch\"));\n if (!name) {\n return c.html(\n renderProjectCreatePage({\n values: { name, gitRepository, gitDefaultBranch },\n errors: { name: \"Name is required\" },\n }),\n 400,\n );\n }\n try {\n const project = await new ProjectModel(getStore().db).create({\n name,\n gitRepository,\n gitDefaultBranch,\n });\n await new LabelModel(getStore().db).seedFor(project.id);\n flash(c, `Created ${project.name}`);\n return hxRedirect(c, `/projects/${project.slug}/library`);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to create project\";\n return c.html(\n renderProjectCreatePage({\n values: { name, gitRepository, gitDefaultBranch },\n globalError: message,\n }),\n 400,\n );\n }\n });\n\n app.get(\"/projects/:slug\", (c) => c.redirect(`/projects/${c.req.param(\"slug\")}/library`, 302));\n\n app.get(\"/projects/:slug/builds\", async (c) => {\n const html = await renderProjectBuildsPage(c.req.param(\"slug\"), {\n status: c.req.query(\"status\"),\n branch: c.req.query(\"branch\"),\n });\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/builds/:buildId\", async (c) => {\n const buildId = c.req.param(\"buildId\");\n const html = await renderBuildDetailPage(buildId, await loadBuildUsage(c, buildId));\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/jobs\", async (c) => {\n const slug = c.req.param(\"slug\");\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n return c.notFound();\n }\n const queueView = await getQueueView();\n const canRetry = await canManageJobs(project.id);\n if (c.req.query(\"partial\") === \"queue\") {\n return c.html(renderActiveQueue(slug, queueView));\n }\n const html = await renderComputeJobsPage(slug, queueView, canRetry);\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/builds/:buildId/diff\", async (c) => {\n const slug = c.req.param(\"slug\");\n const buildId = c.req.param(\"buildId\");\n const snapshotId = c.req.query(\"snapshot\");\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n return c.notFound();\n }\n const build = await new BuildModel(getStore().db).get(buildId);\n if (!build || build.projectId !== project.id) {\n return c.notFound();\n }\n const snapshots = await new SnapshotModel(getStore().db).listByBuild(build.id);\n const comments = await new CommentModel(getStore().db).listByBuild(build.id);\n const { hasBaseline, drifted } = await loadBaselineView(project, build, snapshots);\n const { user, authEnabled } = getStore();\n const canReview = !authEnabled || Boolean(user);\n const selected = selectedSnapshot(snapshots, snapshotId);\n const design = selected\n ? await loadReviewDesign(\n project,\n selected,\n designUrlsFor(await loadDesignResolutions(project, snapshots), selected),\n returnPathOf(c),\n ).catch(() => [])\n : [];\n return c.html(\n renderBuildDiffPage({\n project,\n build,\n snapshots,\n comments,\n selectedId: snapshotId,\n canReview,\n hasBaseline,\n drifted,\n insight: await loadInsightPanel(project, build),\n design,\n usage: await linkedUsageFor(\n c,\n project,\n build.id,\n selectedSnapshot(snapshots, snapshotId)?.storyImportPath ?? null,\n ).catch(() => []),\n publishedBaseDomain: getStore().config.publishedBaseDomain,\n }),\n );\n });\n\n app.get(\"/projects/:slug/labels\", async (c) => {\n const html = await renderLabelsPage(c.req.param(\"slug\"));\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/labels/:key/*\", async (c) => {\n const slug = c.req.param(\"slug\");\n const key = c.req.param(\"key\");\n // Hono's bare `/*` wildcard is not exposed through `param()`, so derive the\n // label value from the raw request path and decode it ourselves.\n const base = `/projects/${slug}/labels/${key}/`;\n const value = c.req.path.startsWith(base) ? decodeRest(c.req.path.slice(base.length)) : \"\";\n const html = await renderLabelDetailPage(slug, key, value);\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/components/*\", async (c) => {\n const slug = c.req.param(\"slug\");\n const base = `/projects/${slug}/components/`;\n const path = c.req.path.startsWith(base) ? decodeRest(c.req.path.slice(base.length)) : \"\";\n const data = await loadComponentPage(c, slug, path, c.req.query(\"tab\"), c.req.query(\"story\"));\n return data ? c.html(renderComponentPage(data)) : c.notFound();\n });\n\n app.get(\"/projects/:slug/library\", async (c) => {\n const html = await renderLibraryPage(c.req.param(\"slug\"), c.req.query(\"branch\"));\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n registerSettingsPages(app);\n}\n\n/** Linked-package usage for every story of a build; empty on any failure so the page still renders. */\nasync function loadBuildUsage(\n c: Context,\n buildId: string,\n): Promise<Map<string, LinkedUsageItem[]>> {\n try {\n const build = await new BuildModel(getStore().db).get(buildId);\n const project = build ? await new ProjectModel(getStore().db).get(build.projectId) : null;\n return build && project ? await linkedUsageByStory(c, project, build.id) : new Map();\n } catch {\n return new Map();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ui.mjs","names":[],"sources":["../../src/routers/ui.ts"],"sourcesContent":["import { BuildModel } from \"@storyshelf/core/models\";\nimport { CommentModel } from \"@storyshelf/core/models\";\nimport { LabelModel } from \"@storyshelf/core/models\";\nimport { ProjectModel } from \"@storyshelf/core/models\";\nimport { SnapshotModel } from \"@storyshelf/core/models\";\nimport type { ProjectRole } from \"@storyshelf/core/types\";\nimport type { Context } from \"hono\";\nimport type { ShelfRouter } from \"../app-types.ts\";\nimport { loadInsightPanel } from \"../insights/panel.ts\";\nimport { renderBuildDetailPage } from \"../pages/build-detail.tsx\";\nimport type { LinkedUsageItem } from \"../pages/build-diff-usage.tsx\";\nimport { renderBuildDiffPage, selectedSnapshot } from \"../pages/build-diff.tsx\";\nimport { renderComponentPage } from \"../pages/component-detail.tsx\";\nimport { renderComputeJobsPage, renderActiveQueue } from \"../pages/compute-jobs.tsx\";\nimport { renderLabelDetailPage, renderLabelsPage } from \"../pages/label-detail.tsx\";\nimport { renderLibraryPage } from \"../pages/library.tsx\";\nimport { renderProjectBuildsPage } from \"../pages/project-builds.tsx\";\nimport { renderProjectCreatePage } from \"../pages/project-create.tsx\";\nimport { renderProjectsPage } from \"../pages/projects.tsx\";\nimport { renderRootPage } from \"../pages/root.tsx\";\nimport { getStore } from \"../store.ts\";\nimport { loadBaselineView } from \"./baseline-guard.ts\";\nimport { loadBaselineReference, loadDefaultComparison } from \"./baseline-reference.ts\";\nimport { loadComponentPage } from \"./component.handlers.ts\";\nimport {\n designUrlsFor,\n loadDesignResolutions,\n loadReviewDesign,\n returnPathOf,\n} from \"./design.handlers.ts\";\nimport { flash } from \"./flash.ts\";\nimport { currentProjectRole } from \"./helpers.ts\";\nimport { hxRedirect } from \"./htmx.ts\";\nimport { linkedUsageByStory, linkedUsageFor } from \"./links.handlers.ts\";\nimport { registerSettingsPages } from \"./settings.ts\";\nfunction asString(value: FormDataEntryValue | null): string | undefined {\n return typeof value === \"string\" ? value.trim() : undefined;\n}\n\nfunction decodeRest(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return \"\";\n }\n}\n\ninterface QueueView {\n buildId: string;\n status: string;\n queuedAt: string;\n startedAt?: string;\n finishedAt?: string;\n error?: string;\n}\n\nasync function getQueueView(): Promise<QueueView[]> {\n const queue = getStore().captureQueue;\n if (!queue) {\n return [];\n }\n const active = await queue.active();\n return active.map((entry) => ({\n buildId: entry.buildId,\n status: entry.status,\n queuedAt: entry.queuedAt,\n startedAt: entry.startedAt,\n finishedAt: entry.finishedAt,\n error: entry.error,\n }));\n}\n\nasync function canManageJobs(projectId: string): Promise<boolean> {\n const { authEnabled } = getStore();\n if (!authEnabled) {\n return true;\n }\n const roles: readonly ProjectRole[] = [\"developer\", \"approver\", \"admin\"];\n const role = await currentProjectRole(projectId);\n return Boolean(role && roles.includes(role));\n}\n\n/** Register the server-rendered HTML pages (projects, builds, diffs, labels, settings). */\nexport function registerUiPages(app: ShelfRouter): void {\n // eslint-disable-next-line promise-function-async -- renderRootPage returns RenderedContent (string | Promise<string>)\n app.get(\"/\", async (c) => c.html(await renderRootPage()));\n app.get(\"/projects\", async (c) => c.html(await renderProjectsPage()));\n // eslint-disable-next-line promise-function-async -- renderProjectCreatePage returns RenderedContent (string | Promise<string>)\n app.get(\"/projects/new\", async (c) => c.html(await renderProjectCreatePage()));\n\n app.post(\"/projects/new\", async (c) => {\n const form = await c.req.formData();\n const name = asString(form.get(\"name\"));\n const gitRepository = asString(form.get(\"gitRepository\"));\n const gitDefaultBranch = asString(form.get(\"gitDefaultBranch\"));\n if (!name) {\n return c.html(\n renderProjectCreatePage({\n values: { name, gitRepository, gitDefaultBranch },\n errors: { name: \"Name is required\" },\n }),\n 400,\n );\n }\n try {\n const project = await new ProjectModel(getStore().db).create({\n name,\n gitRepository,\n gitDefaultBranch,\n });\n await new LabelModel(getStore().db).seedFor(project.id);\n flash(c, `Created ${project.name}`);\n return hxRedirect(c, `/projects/${project.slug}/library`);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to create project\";\n return c.html(\n renderProjectCreatePage({\n values: { name, gitRepository, gitDefaultBranch },\n globalError: message,\n }),\n 400,\n );\n }\n });\n\n app.get(\"/projects/:slug\", (c) => c.redirect(`/projects/${c.req.param(\"slug\")}/library`, 302));\n\n app.get(\"/projects/:slug/builds\", async (c) => {\n const html = await renderProjectBuildsPage(c.req.param(\"slug\"), {\n status: c.req.query(\"status\"),\n branch: c.req.query(\"branch\"),\n });\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/builds/:buildId\", async (c) => {\n const buildId = c.req.param(\"buildId\");\n const html = await renderBuildDetailPage(buildId, await loadBuildUsage(c, buildId));\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/jobs\", async (c) => {\n const slug = c.req.param(\"slug\");\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n return c.notFound();\n }\n const queueView = await getQueueView();\n const canRetry = await canManageJobs(project.id);\n if (c.req.query(\"partial\") === \"queue\") {\n return c.html(renderActiveQueue(slug, queueView));\n }\n const html = await renderComputeJobsPage(slug, queueView, canRetry);\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/builds/:buildId/diff\", async (c) => {\n const slug = c.req.param(\"slug\");\n const buildId = c.req.param(\"buildId\");\n const snapshotId = c.req.query(\"snapshot\");\n const project = await new ProjectModel(getStore().db).getBySlug(slug);\n if (!project) {\n return c.notFound();\n }\n const build = await new BuildModel(getStore().db).get(buildId);\n if (!build || build.projectId !== project.id) {\n return c.notFound();\n }\n const snapshots = await new SnapshotModel(getStore().db).listByBuild(build.id);\n const comments = await new CommentModel(getStore().db).listByBuild(build.id);\n const { hasBaseline, drifted } = await loadBaselineView(project, build, snapshots);\n const { user, authEnabled } = getStore();\n const canReview = !authEnabled || Boolean(user);\n const selected = selectedSnapshot(snapshots, snapshotId);\n const design = selected\n ? await loadReviewDesign(\n project,\n selected,\n designUrlsFor(await loadDesignResolutions(project, snapshots), selected),\n returnPathOf(c),\n ).catch(() => [])\n : [];\n return c.html(\n renderBuildDiffPage({\n project,\n build,\n snapshots,\n comments,\n selectedId: snapshotId,\n canReview,\n hasBaseline,\n drifted,\n reference: selected ? await loadBaselineReference(project, build, selected) : undefined,\n defaultComparison: selected ? await loadDefaultComparison(project, build, selected) : null,\n insight: await loadInsightPanel(project, build),\n design,\n usage: await linkedUsageFor(\n c,\n project,\n build.id,\n selectedSnapshot(snapshots, snapshotId)?.storyImportPath ?? null,\n ).catch(() => []),\n publishedBaseDomain: getStore().config.publishedBaseDomain,\n }),\n );\n });\n\n app.get(\"/projects/:slug/labels\", async (c) => {\n const html = await renderLabelsPage(c.req.param(\"slug\"));\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/labels/:key/*\", async (c) => {\n const slug = c.req.param(\"slug\");\n const key = c.req.param(\"key\");\n // Hono's bare `/*` wildcard is not exposed through `param()`, so derive the\n // label value from the raw request path and decode it ourselves.\n const base = `/projects/${slug}/labels/${key}/`;\n const value = c.req.path.startsWith(base) ? decodeRest(c.req.path.slice(base.length)) : \"\";\n const html = await renderLabelDetailPage(slug, key, value);\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n app.get(\"/projects/:slug/components/*\", async (c) => {\n const slug = c.req.param(\"slug\");\n const base = `/projects/${slug}/components/`;\n const path = c.req.path.startsWith(base) ? decodeRest(c.req.path.slice(base.length)) : \"\";\n const data = await loadComponentPage(c, slug, path, c.req.query(\"tab\"), c.req.query(\"story\"));\n return data ? c.html(renderComponentPage(data)) : c.notFound();\n });\n\n app.get(\"/projects/:slug/library\", async (c) => {\n const html = await renderLibraryPage(c.req.param(\"slug\"), c.req.query(\"branch\"));\n if (!html) {\n return c.notFound();\n }\n return c.html(html);\n });\n\n registerSettingsPages(app);\n}\n\n/** Linked-package usage for every story of a build; empty on any failure so the page still renders. */\nasync function loadBuildUsage(\n c: Context,\n buildId: string,\n): Promise<Map<string, LinkedUsageItem[]>> {\n try {\n const build = await new BuildModel(getStore().db).get(buildId);\n const project = build ? await new ProjectModel(getStore().db).get(build.projectId) : null;\n return build && project ? await linkedUsageByStory(c, project, build.id) : new Map();\n } catch {\n return new Map();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAS,SAAS,OAAsD;CACtE,OAAO,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI,KAAA;AACpD;AAEA,SAAS,WAAW,OAAuB;CACzC,IAAI;EACF,OAAO,mBAAmB,KAAK;CACjC,QAAQ;EACN,OAAO;CACT;AACF;AAWA,eAAe,eAAqC;CAClD,MAAM,QAAQ,SAAS,CAAC,CAAC;CACzB,IAAI,CAAC,OACH,OAAO,CAAC;CAGV,QAAO,MADc,MAAM,OAAO,EAAA,CACpB,KAAK,WAAW;EAC5B,SAAS,MAAM;EACf,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,YAAY,MAAM;EAClB,OAAO,MAAM;CACf,EAAE;AACJ;AAEA,eAAe,cAAc,WAAqC;CAChE,MAAM,EAAE,gBAAgB,SAAS;CACjC,IAAI,CAAC,aACH,OAAO;CAET,MAAM,QAAgC;EAAC;EAAa;EAAY;CAAO;CACvE,MAAM,OAAO,MAAM,mBAAmB,SAAS;CAC/C,OAAO,QAAQ,QAAQ,MAAM,SAAS,IAAI,CAAC;AAC7C;;AAGA,SAAgB,gBAAgB,KAAwB;CAEtD,IAAI,IAAI,KAAK,OAAO,MAAM,EAAE,KAAK,MAAM,eAAe,CAAC,CAAC;CACxD,IAAI,IAAI,aAAa,OAAO,MAAM,EAAE,KAAK,MAAM,mBAAmB,CAAC,CAAC;CAEpE,IAAI,IAAI,iBAAiB,OAAO,MAAM,EAAE,KAAK,MAAM,wBAAwB,CAAC,CAAC;CAE7E,IAAI,KAAK,iBAAiB,OAAO,MAAM;EACrC,MAAM,OAAO,MAAM,EAAE,IAAI,SAAS;EAClC,MAAM,OAAO,SAAS,KAAK,IAAI,MAAM,CAAC;EACtC,MAAM,gBAAgB,SAAS,KAAK,IAAI,eAAe,CAAC;EACxD,MAAM,mBAAmB,SAAS,KAAK,IAAI,kBAAkB,CAAC;EAC9D,IAAI,CAAC,MACH,OAAO,EAAE,KACP,wBAAwB;GACtB,QAAQ;IAAE;IAAM;IAAe;GAAiB;GAChD,QAAQ,EAAE,MAAM,mBAAmB;EACrC,CAAC,GACD,GACF;EAEF,IAAI;GACF,MAAM,UAAU,MAAM,IAAI,aAAa,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO;IAC3D;IACA;IACA;GACF,CAAC;GACD,MAAM,IAAI,WAAW,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;GACtD,MAAM,GAAG,WAAW,QAAQ,MAAM;GAClC,OAAO,WAAW,GAAG,aAAa,QAAQ,KAAK,SAAS;EAC1D,SAAS,OAAO;GACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;GACzD,OAAO,EAAE,KACP,wBAAwB;IACtB,QAAQ;KAAE;KAAM;KAAe;IAAiB;IAChD,aAAa;GACf,CAAC,GACD,GACF;EACF;CACF,CAAC;CAED,IAAI,IAAI,oBAAoB,MAAM,EAAE,SAAS,aAAa,EAAE,IAAI,MAAM,MAAM,EAAE,WAAW,GAAG,CAAC;CAE7F,IAAI,IAAI,0BAA0B,OAAO,MAAM;EAC7C,MAAM,OAAO,MAAM,wBAAwB,EAAE,IAAI,MAAM,MAAM,GAAG;GAC9D,QAAQ,EAAE,IAAI,MAAM,QAAQ;GAC5B,QAAQ,EAAE,IAAI,MAAM,QAAQ;EAC9B,CAAC;EACD,IAAI,CAAC,MACH,OAAO,EAAE,SAAS;EAEpB,OAAO,EAAE,KAAK,IAAI;CACpB,CAAC;CAED,IAAI,IAAI,mCAAmC,OAAO,MAAM;EACtD,MAAM,UAAU,EAAE,IAAI,MAAM,SAAS;EACrC,MAAM,OAAO,MAAM,sBAAsB,SAAS,MAAM,eAAe,GAAG,OAAO,CAAC;EAClF,IAAI,CAAC,MACH,OAAO,EAAE,SAAS;EAEpB,OAAO,EAAE,KAAK,IAAI;CACpB,CAAC;CAED,IAAI,IAAI,wBAAwB,OAAO,MAAM;EAC3C,MAAM,OAAO,EAAE,IAAI,MAAM,MAAM;EAC/B,MAAM,UAAU,MAAM,IAAI,aAAa,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,IAAI;EACpE,IAAI,CAAC,SACH,OAAO,EAAE,SAAS;EAEpB,MAAM,YAAY,MAAM,aAAa;EACrC,MAAM,WAAW,MAAM,cAAc,QAAQ,EAAE;EAC/C,IAAI,EAAE,IAAI,MAAM,SAAS,MAAM,SAC7B,OAAO,EAAE,KAAK,kBAAkB,MAAM,SAAS,CAAC;EAElD,MAAM,OAAO,MAAM,sBAAsB,MAAM,WAAW,QAAQ;EAClE,IAAI,CAAC,MACH,OAAO,EAAE,SAAS;EAEpB,OAAO,EAAE,KAAK,IAAI;CACpB,CAAC;CAED,IAAI,IAAI,wCAAwC,OAAO,MAAM;EAC3D,MAAM,OAAO,EAAE,IAAI,MAAM,MAAM;EAC/B,MAAM,UAAU,EAAE,IAAI,MAAM,SAAS;EACrC,MAAM,aAAa,EAAE,IAAI,MAAM,UAAU;EACzC,MAAM,UAAU,MAAM,IAAI,aAAa,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,IAAI;EACpE,IAAI,CAAC,SACH,OAAO,EAAE,SAAS;EAEpB,MAAM,QAAQ,MAAM,IAAI,WAAW,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO;EAC7D,IAAI,CAAC,SAAS,MAAM,cAAc,QAAQ,IACxC,OAAO,EAAE,SAAS;EAEpB,MAAM,YAAY,MAAM,IAAI,cAAc,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,MAAM,EAAE;EAC7E,MAAM,WAAW,MAAM,IAAI,aAAa,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,MAAM,EAAE;EAC3E,MAAM,EAAE,aAAa,YAAY,MAAM,iBAAiB,SAAS,OAAO,SAAS;EACjF,MAAM,EAAE,MAAM,gBAAgB,SAAS;EACvC,MAAM,YAAY,CAAC,eAAe,QAAQ,IAAI;EAC9C,MAAM,WAAW,iBAAiB,WAAW,UAAU;EACvD,MAAM,SAAS,WACX,MAAM,iBACJ,SACA,UACA,cAAc,MAAM,sBAAsB,SAAS,SAAS,GAAG,QAAQ,GACvE,aAAa,CAAC,CAChB,CAAC,CAAC,YAAY,CAAC,CAAC,IAChB,CAAC;EACL,OAAO,EAAE,KACP,oBAAoB;GAClB;GACA;GACA;GACA;GACA,YAAY;GACZ;GACA;GACA;GACA,WAAW,WAAW,MAAM,sBAAsB,SAAS,OAAO,QAAQ,IAAI,KAAA;GAC9E,mBAAmB,WAAW,MAAM,sBAAsB,SAAS,OAAO,QAAQ,IAAI;GACtF,SAAS,MAAM,iBAAiB,SAAS,KAAK;GAC9C;GACA,OAAO,MAAM,eACX,GACA,SACA,MAAM,IACN,iBAAiB,WAAW,UAAU,CAAC,EAAE,mBAAmB,IAC9D,CAAC,CAAC,YAAY,CAAC,CAAC;GAChB,qBAAqB,SAAS,CAAC,CAAC,OAAO;EACzC,CAAC,CACH;CACF,CAAC;CAED,IAAI,IAAI,0BAA0B,OAAO,MAAM;EAC7C,MAAM,OAAO,MAAM,iBAAiB,EAAE,IAAI,MAAM,MAAM,CAAC;EACvD,IAAI,CAAC,MACH,OAAO,EAAE,SAAS;EAEpB,OAAO,EAAE,KAAK,IAAI;CACpB,CAAC;CAED,IAAI,IAAI,iCAAiC,OAAO,MAAM;EACpD,MAAM,OAAO,EAAE,IAAI,MAAM,MAAM;EAC/B,MAAM,MAAM,EAAE,IAAI,MAAM,KAAK;EAG7B,MAAM,OAAO,aAAa,KAAK,UAAU,IAAI;EAC7C,MAAM,QAAQ,EAAE,IAAI,KAAK,WAAW,IAAI,IAAI,WAAW,EAAE,IAAI,KAAK,MAAM,KAAK,MAAM,CAAC,IAAI;EACxF,MAAM,OAAO,MAAM,sBAAsB,MAAM,KAAK,KAAK;EACzD,IAAI,CAAC,MACH,OAAO,EAAE,SAAS;EAEpB,OAAO,EAAE,KAAK,IAAI;CACpB,CAAC;CAED,IAAI,IAAI,gCAAgC,OAAO,MAAM;EACnD,MAAM,OAAO,EAAE,IAAI,MAAM,MAAM;EAC/B,MAAM,OAAO,aAAa,KAAK;EAC/B,MAAM,OAAO,EAAE,IAAI,KAAK,WAAW,IAAI,IAAI,WAAW,EAAE,IAAI,KAAK,MAAM,KAAK,MAAM,CAAC,IAAI;EACvF,MAAM,OAAO,MAAM,kBAAkB,GAAG,MAAM,MAAM,EAAE,IAAI,MAAM,KAAK,GAAG,EAAE,IAAI,MAAM,OAAO,CAAC;EAC5F,OAAO,OAAO,EAAE,KAAK,oBAAoB,IAAI,CAAC,IAAI,EAAE,SAAS;CAC/D,CAAC;CAED,IAAI,IAAI,2BAA2B,OAAO,MAAM;EAC9C,MAAM,OAAO,MAAM,kBAAkB,EAAE,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI,MAAM,QAAQ,CAAC;EAC/E,IAAI,CAAC,MACH,OAAO,EAAE,SAAS;EAEpB,OAAO,EAAE,KAAK,IAAI;CACpB,CAAC;CAED,sBAAsB,GAAG;AAC3B;;AAGA,eAAe,eACb,GACA,SACyC;CACzC,IAAI;EACF,MAAM,QAAQ,MAAM,IAAI,WAAW,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO;EAC7D,MAAM,UAAU,QAAQ,MAAM,IAAI,aAAa,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,SAAS,IAAI;EACrF,OAAO,SAAS,UAAU,MAAM,mBAAmB,GAAG,SAAS,MAAM,EAAE,oBAAI,IAAI,IAAI;CACrF,QAAQ;EACN,uBAAO,IAAI,IAAI;CACjB;AACF"}
|
|
@@ -6,7 +6,7 @@ import { notFound as notFound$1, unauthorized, webhookCreateSchema, webhookCreat
|
|
|
6
6
|
import { webhookModel } from "./scoped-models.mjs";
|
|
7
7
|
import { createRoute, z } from "@hono/zod-openapi";
|
|
8
8
|
import { WebhookModel } from "@storyshelf/core/models";
|
|
9
|
-
|
|
9
|
+
// src/routers/webhooks.ts
|
|
10
10
|
const ADMIN_ROLES = ["admin"];
|
|
11
11
|
const listWebhooksRoute = createRoute({
|
|
12
12
|
method: "get",
|
|
@@ -85,7 +85,6 @@ function registerWebhooks(app) {
|
|
|
85
85
|
return c.body(null, 204);
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
|
-
//#endregion
|
|
89
88
|
export { registerWebhooks };
|
|
90
89
|
|
|
91
90
|
//# sourceMappingURL=webhooks.mjs.map
|