@storyshelf/core 0.3.2 → 0.5.4
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 +38 -31
- package/dist/adapter/capture-queue.d.mts +29 -1
- package/dist/adapter/capture-runner.d.mts +18 -2
- package/dist/adapter/capture-runner.mjs +12 -1
- package/dist/adapter/capture-runner.mjs.map +1 -0
- package/dist/adapter/database.d.mts +67 -17
- package/dist/adapter/database.mjs +22 -55
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/email-sender.d.mts +27 -0
- package/dist/adapter/email-sender.mjs +4 -0
- package/dist/adapter/metadata.d.mts +21 -15
- package/dist/adapter/notifier.d.mts +7 -0
- package/dist/adapter/notifier.mjs +6 -0
- package/dist/adapter/setup.d.mts +61 -0
- package/dist/adapter/setup.mjs +29 -0
- package/dist/adapter/setup.mjs.map +1 -0
- package/dist/adapter/webhook-events.d.mts +29 -0
- package/dist/adapter/webhook-events.mjs +83 -0
- package/dist/adapter/webhook-events.mjs.map +1 -0
- package/dist/adapters/notifier/events.d.mts +26 -0
- package/dist/adapters/notifier/events.mjs +66 -0
- package/dist/adapters/notifier/events.mjs.map +1 -0
- package/dist/adapters/notifier/format.d.mts +14 -0
- package/dist/adapters/notifier/format.mjs +96 -0
- package/dist/adapters/notifier/format.mjs.map +1 -0
- package/dist/adapters/notifier/provider.d.mts +32 -0
- package/dist/adapters/notifier/types.d.mts +56 -0
- package/dist/adapters/setup-runner.d.mts +23 -0
- package/dist/adapters/setup-runner.mjs +149 -0
- package/dist/adapters/setup-runner.mjs.map +1 -0
- package/dist/adapters/validate.d.mts +30 -0
- package/dist/adapters/validate.mjs +313 -0
- package/dist/adapters/validate.mjs.map +1 -0
- package/dist/auth.d.mts +94 -0
- package/dist/auth.mjs +4 -0
- package/dist/brand-packs.d.mts +16 -0
- package/dist/brand-packs.mjs +130 -0
- package/dist/brand-packs.mjs.map +1 -0
- package/dist/capture/adapter.d.mts +51 -1
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +16 -0
- package/dist/capture/affected.mjs +64 -0
- package/dist/capture/affected.mjs.map +1 -0
- package/dist/capture/diff-record.mjs +30 -0
- package/dist/capture/diff-record.mjs.map +1 -0
- package/dist/capture/dispatch.d.mts +36 -0
- package/dist/capture/dispatch.mjs +88 -31
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/guards.mjs +31 -0
- package/dist/capture/guards.mjs.map +1 -0
- package/dist/capture/orchestrator.d.mts +41 -6
- package/dist/capture/orchestrator.mjs +151 -28
- package/dist/capture/orchestrator.mjs.map +1 -1
- package/dist/capture/pipeline.d.mts +30 -16
- package/dist/capture/pipeline.mjs +143 -39
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/recovery.d.mts +21 -0
- package/dist/capture/recovery.mjs +83 -0
- package/dist/capture/recovery.mjs.map +1 -0
- package/dist/capture/rediff.d.mts +35 -0
- package/dist/capture/rediff.mjs +77 -0
- package/dist/capture/rediff.mjs.map +1 -0
- package/dist/capture/sizing.d.mts +58 -0
- package/dist/capture/sizing.mjs +119 -0
- package/dist/capture/sizing.mjs.map +1 -0
- package/dist/capture/skip-checks.mjs +3 -3
- package/dist/capture/skip-checks.mjs.map +1 -1
- package/dist/capture/statics.d.mts +38 -0
- package/dist/capture/statics.mjs +38 -4
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/status-fanout.mjs +7 -2
- package/dist/capture/status-fanout.mjs.map +1 -1
- package/dist/capture/storybook.d.mts +31 -2
- package/dist/capture/storybook.mjs +77 -2
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture.d.mts +10 -4
- package/dist/capture.mjs +7 -2
- package/dist/config.d.mts +121 -4
- package/dist/config.mjs +55 -8
- package/dist/config.mjs.map +1 -1
- package/dist/index.d.mts +5 -56
- package/dist/index.mjs +4 -284
- package/dist/logger.d.mts +26 -17
- package/dist/logger.mjs +10 -5
- package/dist/logger.mjs.map +1 -1
- package/dist/models/baseline-status.d.mts +29 -0
- package/dist/models/baseline-status.mjs +26 -0
- package/dist/models/baseline-status.mjs.map +1 -0
- package/dist/models/baseline.d.mts +33 -0
- package/dist/models/baseline.mjs +35 -13
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build.d.mts +87 -0
- package/dist/models/build.mjs +83 -35
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +40 -0
- package/dist/models/capture-attempt.mjs +85 -0
- package/dist/models/capture-attempt.mjs.map +1 -0
- package/dist/models/capture-log.d.mts +21 -0
- package/dist/models/capture-log.mjs +44 -0
- package/dist/models/capture-log.mjs.map +1 -0
- package/dist/models/comment.d.mts +29 -0
- package/dist/models/comment.mjs +11 -8
- package/dist/models/comment.mjs.map +1 -1
- package/dist/models/content-ref.d.mts +21 -0
- package/dist/models/content-ref.mjs +49 -0
- package/dist/models/content-ref.mjs.map +1 -0
- package/dist/models/credentials.d.mts +36 -0
- package/dist/models/credentials.mjs +103 -0
- package/dist/models/credentials.mjs.map +1 -0
- package/dist/models/label.d.mts +58 -0
- package/dist/models/label.mjs +32 -27
- package/dist/models/label.mjs.map +1 -1
- package/dist/models/member.d.mts +34 -0
- package/dist/models/member.mjs +20 -11
- package/dist/models/member.mjs.map +1 -1
- package/dist/models/notification-channel.d.mts +52 -0
- package/dist/models/notification-channel.mjs +93 -0
- package/dist/models/notification-channel.mjs.map +1 -0
- package/dist/models/notification-subscription.d.mts +44 -0
- package/dist/models/notification-subscription.mjs +89 -0
- package/dist/models/notification-subscription.mjs.map +1 -0
- package/dist/models/project-group-mapping.d.mts +30 -0
- package/dist/models/project-group-mapping.mjs +46 -0
- package/dist/models/project-group-mapping.mjs.map +1 -0
- package/dist/models/project.d.mts +54 -0
- package/dist/models/project.mjs +15 -11
- package/dist/models/project.mjs.map +1 -1
- package/dist/models/snapshot.d.mts +58 -0
- package/dist/models/snapshot.mjs +13 -7
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/status-config.d.mts +38 -0
- package/dist/models/status-config.mjs +12 -11
- package/dist/models/status-config.mjs.map +1 -1
- package/dist/models/token.d.mts +44 -0
- package/dist/models/token.mjs +18 -15
- package/dist/models/token.mjs.map +1 -1
- package/dist/models/user.d.mts +42 -0
- package/dist/models/user.mjs +65 -0
- package/dist/models/user.mjs.map +1 -0
- package/dist/models/webhook.d.mts +50 -0
- package/dist/models/webhook.mjs +12 -10
- package/dist/models/webhook.mjs.map +1 -1
- package/dist/models.d.mts +22 -0
- package/dist/models.mjs +23 -0
- package/dist/paths.mjs +29 -0
- package/dist/paths.mjs.map +1 -1
- package/dist/retention.d.mts +45 -0
- package/dist/retention.mjs +145 -0
- package/dist/retention.mjs.map +1 -0
- package/dist/schema/baseline.d.mts +1 -0
- package/dist/schema/build.d.mts +8 -0
- package/dist/schema/capture-attempt.d.mts +38 -0
- package/dist/schema/comment.d.mts +1 -1
- package/dist/schema/content-ref.d.mts +13 -0
- package/dist/schema/member.d.mts +2 -0
- package/dist/schema/notification.d.mts +35 -0
- package/dist/schema/project-group-mapping.d.mts +15 -0
- package/dist/schema/project.d.mts +4 -0
- package/dist/schema/snapshot.d.mts +7 -0
- package/dist/schema/token.d.mts +2 -0
- package/dist/schema/user.d.mts +11 -1
- package/dist/schema.d.mts +9 -46
- package/dist/schema.mjs +1 -55
- package/dist/test-helpers/create-project.d.mts +15 -0
- package/dist/test-helpers/create-project.mjs +28 -0
- package/dist/test-helpers/create-project.mjs.map +1 -0
- package/dist/test-helpers/database-contract.d.mts +13 -0
- package/dist/test-helpers/database-contract.mjs +58 -0
- package/dist/test-helpers/database-contract.mjs.map +1 -0
- package/dist/test-helpers/fake-adapters.d.mts +14 -0
- package/dist/test-helpers/fake-adapters.mjs +20 -0
- package/dist/test-helpers/fake-adapters.mjs.map +1 -0
- package/dist/test-helpers/fake-database.d.mts +11 -0
- package/dist/test-helpers/fake-database.mjs +104 -0
- package/dist/test-helpers/fake-database.mjs.map +1 -0
- package/dist/test-helpers/fake-storage.d.mts +14 -0
- package/dist/test-helpers/fake-storage.mjs +49 -0
- package/dist/test-helpers/fake-storage.mjs.map +1 -0
- package/dist/test-helpers/fake-tables.mjs +276 -0
- package/dist/test-helpers/fake-tables.mjs.map +1 -0
- package/dist/test-helpers/git-contract.d.mts +11 -0
- package/dist/test-helpers/git-contract.mjs +34 -0
- package/dist/test-helpers/git-contract.mjs.map +1 -0
- package/dist/test-helpers/notifier-contract.d.mts +11 -0
- package/dist/test-helpers/notifier-contract.mjs +34 -0
- package/dist/test-helpers/notifier-contract.mjs.map +1 -0
- package/dist/test-helpers/queue-contract.d.mts +11 -0
- package/dist/test-helpers/queue-contract.mjs +64 -0
- package/dist/test-helpers/queue-contract.mjs.map +1 -0
- package/dist/test-helpers/runner-contract.d.mts +11 -0
- package/dist/test-helpers/runner-contract.mjs +32 -0
- package/dist/test-helpers/runner-contract.mjs.map +1 -0
- package/dist/test-helpers/sql-chunks.mjs +138 -0
- package/dist/test-helpers/sql-chunks.mjs.map +1 -0
- package/dist/test-helpers/storage-contract.d.mts +11 -0
- package/dist/test-helpers/storage-contract.mjs +87 -0
- package/dist/test-helpers/storage-contract.mjs.map +1 -0
- package/dist/test-helpers.d.mts +13 -0
- package/dist/test-helpers.mjs +14 -0
- package/dist/tracing.mjs +63 -0
- package/dist/tracing.mjs.map +1 -0
- package/dist/types.d.mts +38 -5
- package/dist/types.mjs +18 -5
- package/dist/types.mjs.map +1 -1
- package/dist/urls.d.mts +12 -1
- package/dist/urls.mjs +22 -3
- package/dist/urls.mjs.map +1 -1
- package/dist/utils/encrypt.d.mts +48 -0
- package/dist/utils/encrypt.mjs +67 -13
- package/dist/utils/encrypt.mjs.map +1 -1
- package/dist/utils/hash.d.mts +17 -0
- package/dist/utils/hash.mjs +8 -2
- package/dist/utils/hash.mjs.map +1 -1
- package/dist/utils/http.d.mts +58 -0
- package/dist/utils/http.mjs +147 -0
- package/dist/utils/http.mjs.map +1 -0
- package/dist/utils/mime.d.mts +9 -0
- package/dist/utils/mime.mjs +33 -0
- package/dist/utils/mime.mjs.map +1 -0
- package/dist/utils/path-security.d.mts +12 -0
- package/dist/utils/path-security.mjs +24 -0
- package/dist/utils/path-security.mjs.map +1 -0
- package/dist/utils/redact.d.mts +17 -0
- package/dist/utils/redact.mjs +42 -0
- package/dist/utils/redact.mjs.map +1 -0
- package/dist/utils/timing.d.mts +19 -0
- package/dist/utils/timing.mjs +46 -0
- package/dist/utils/timing.mjs.map +1 -0
- package/dist/utils/ulid.d.mts +10 -0
- package/dist/utils/ulid.mjs +8 -8
- package/dist/utils/ulid.mjs.map +1 -1
- package/dist/utils.d.mts +12 -0
- package/dist/utils.mjs +13 -0
- package/package.json +69 -28
- package/dist/adapter/auth.d.mts +0 -50
- package/dist/adapter/auth.mjs +0 -10
- package/dist/adapter/auth.mjs.map +0 -1
- package/dist/adapter/init.d.mts +0 -58
- package/dist/adapter/init.mjs +0 -93
- package/dist/adapter/init.mjs.map +0 -1
- package/dist/adapters/webhook-events.mjs +0 -55
- package/dist/adapters/webhook-events.mjs.map +0 -1
- package/dist/ddl.d.mts +0 -8
- package/dist/ddl.mjs +0 -158
- package/dist/ddl.mjs.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/middleware/auth-gate.mjs +0 -36
- package/dist/middleware/auth-gate.mjs.map +0 -1
- package/dist/middleware/csrf.mjs +0 -40
- package/dist/middleware/csrf.mjs.map +0 -1
- package/dist/middleware/rate-limit.mjs +0 -39
- package/dist/middleware/rate-limit.mjs.map +0 -1
- package/dist/middleware/request-log.mjs +0 -28
- package/dist/middleware/request-log.mjs.map +0 -1
- package/dist/middleware/store-scope.mjs +0 -29
- package/dist/middleware/store-scope.mjs.map +0 -1
- package/dist/pages/build-detail.mjs +0 -339
- package/dist/pages/build-detail.mjs.map +0 -1
- package/dist/pages/build-diff-comments.mjs +0 -113
- package/dist/pages/build-diff-comments.mjs.map +0 -1
- package/dist/pages/build-diff-header.mjs +0 -163
- package/dist/pages/build-diff-header.mjs.map +0 -1
- package/dist/pages/build-diff-nav.mjs +0 -66
- package/dist/pages/build-diff-nav.mjs.map +0 -1
- package/dist/pages/build-diff-viewer.mjs +0 -197
- package/dist/pages/build-diff-viewer.mjs.map +0 -1
- package/dist/pages/build-diff.mjs +0 -90
- package/dist/pages/build-diff.mjs.map +0 -1
- package/dist/pages/compute-jobs.mjs +0 -198
- package/dist/pages/compute-jobs.mjs.map +0 -1
- package/dist/pages/label-detail.mjs +0 -228
- package/dist/pages/label-detail.mjs.map +0 -1
- package/dist/pages/login.mjs +0 -78
- package/dist/pages/login.mjs.map +0 -1
- package/dist/pages/project-builds.mjs +0 -231
- package/dist/pages/project-builds.mjs.map +0 -1
- package/dist/pages/project-create.mjs +0 -169
- package/dist/pages/project-create.mjs.map +0 -1
- package/dist/pages/project-settings.mjs +0 -102
- package/dist/pages/project-settings.mjs.map +0 -1
- package/dist/pages/projects.mjs +0 -184
- package/dist/pages/projects.mjs.map +0 -1
- package/dist/pages/root.mjs +0 -94
- package/dist/pages/root.mjs.map +0 -1
- package/dist/pages/settings-general.mjs +0 -192
- package/dist/pages/settings-general.mjs.map +0 -1
- package/dist/pages/settings-labels.mjs +0 -150
- package/dist/pages/settings-labels.mjs.map +0 -1
- package/dist/pages/settings-members.mjs +0 -125
- package/dist/pages/settings-members.mjs.map +0 -1
- package/dist/pages/settings-status.mjs +0 -152
- package/dist/pages/settings-status.mjs.map +0 -1
- package/dist/pages/settings-tests.mjs +0 -149
- package/dist/pages/settings-tests.mjs.map +0 -1
- package/dist/pages/settings-tokens.mjs +0 -96
- package/dist/pages/settings-tokens.mjs.map +0 -1
- package/dist/pages/settings-webhooks.mjs +0 -150
- package/dist/pages/settings-webhooks.mjs.map +0 -1
- package/dist/pages/storybook.mjs +0 -96
- package/dist/pages/storybook.mjs.map +0 -1
- package/dist/retention/purge.mjs +0 -70
- package/dist/retention/purge.mjs.map +0 -1
- package/dist/routers/admin.mjs +0 -42
- package/dist/routers/admin.mjs.map +0 -1
- package/dist/routers/assets.mjs +0 -25
- package/dist/routers/assets.mjs.map +0 -1
- package/dist/routers/auth.mjs +0 -78
- package/dist/routers/auth.mjs.map +0 -1
- package/dist/routers/builds.handlers.mjs +0 -184
- package/dist/routers/builds.handlers.mjs.map +0 -1
- package/dist/routers/builds.mjs +0 -185
- package/dist/routers/builds.mjs.map +0 -1
- package/dist/routers/comments.mjs +0 -90
- package/dist/routers/comments.mjs.map +0 -1
- package/dist/routers/health.mjs +0 -137
- package/dist/routers/health.mjs.map +0 -1
- package/dist/routers/helpers.mjs +0 -82
- package/dist/routers/helpers.mjs.map +0 -1
- package/dist/routers/htmx.mjs +0 -20
- package/dist/routers/htmx.mjs.map +0 -1
- package/dist/routers/labels.mjs +0 -110
- package/dist/routers/labels.mjs.map +0 -1
- package/dist/routers/media.mjs +0 -55
- package/dist/routers/media.mjs.map +0 -1
- package/dist/routers/members.mjs +0 -102
- package/dist/routers/members.mjs.map +0 -1
- package/dist/routers/projects.mjs +0 -122
- package/dist/routers/projects.mjs.map +0 -1
- package/dist/routers/schemas.mjs +0 -224
- package/dist/routers/schemas.mjs.map +0 -1
- package/dist/routers/settings-general.mjs +0 -95
- package/dist/routers/settings-general.mjs.map +0 -1
- package/dist/routers/settings-labels.mjs +0 -49
- package/dist/routers/settings-labels.mjs.map +0 -1
- package/dist/routers/settings-members.mjs +0 -30
- package/dist/routers/settings-members.mjs.map +0 -1
- package/dist/routers/settings-status.mjs +0 -73
- package/dist/routers/settings-status.mjs.map +0 -1
- package/dist/routers/settings-tokens.mjs +0 -40
- package/dist/routers/settings-tokens.mjs.map +0 -1
- package/dist/routers/settings-webhooks.mjs +0 -63
- package/dist/routers/settings-webhooks.mjs.map +0 -1
- package/dist/routers/settings.handlers.mjs +0 -93
- package/dist/routers/settings.handlers.mjs.map +0 -1
- package/dist/routers/settings.mjs +0 -23
- package/dist/routers/settings.mjs.map +0 -1
- package/dist/routers/snapshots.mjs +0 -144
- package/dist/routers/snapshots.mjs.map +0 -1
- package/dist/routers/status-configs.mjs +0 -101
- package/dist/routers/status-configs.mjs.map +0 -1
- package/dist/routers/storybook.mjs +0 -120
- package/dist/routers/storybook.mjs.map +0 -1
- package/dist/routers/tokens.mjs +0 -81
- package/dist/routers/tokens.mjs.map +0 -1
- package/dist/routers/ui.mjs +0 -165
- package/dist/routers/ui.mjs.map +0 -1
- package/dist/routers/webhooks.mjs +0 -90
- package/dist/routers/webhooks.mjs.map +0 -1
- package/dist/schema/baseline.mjs +0 -22
- package/dist/schema/baseline.mjs.map +0 -1
- package/dist/schema/build.mjs +0 -29
- package/dist/schema/build.mjs.map +0 -1
- package/dist/schema/comment.mjs +0 -26
- package/dist/schema/comment.mjs.map +0 -1
- package/dist/schema/label.mjs +0 -30
- package/dist/schema/label.mjs.map +0 -1
- package/dist/schema/member.mjs +0 -19
- package/dist/schema/member.mjs.map +0 -1
- package/dist/schema/project.mjs +0 -25
- package/dist/schema/project.mjs.map +0 -1
- package/dist/schema/snapshot.mjs +0 -34
- package/dist/schema/snapshot.mjs.map +0 -1
- package/dist/schema/status-config.mjs +0 -20
- package/dist/schema/status-config.mjs.map +0 -1
- package/dist/schema/token.mjs +0 -19
- package/dist/schema/token.mjs.map +0 -1
- package/dist/schema/user.mjs +0 -19
- package/dist/schema/user.mjs.map +0 -1
- package/dist/schema/webhook.mjs +0 -20
- package/dist/schema/webhook.mjs.map +0 -1
- package/dist/schema.mjs.map +0 -1
- package/dist/store.mjs +0 -20
- package/dist/store.mjs.map +0 -1
- package/dist/ui/document.mjs +0 -348
- package/dist/ui/document.mjs.map +0 -1
- package/dist/ui/feedback.mjs +0 -24
- package/dist/ui/feedback.mjs.map +0 -1
- package/dist/ui/forms.mjs +0 -117
- package/dist/ui/forms.mjs.map +0 -1
- package/dist/ui/styles.mjs +0 -174
- package/dist/ui/styles.mjs.map +0 -1
- package/dist/ui/theme.mjs +0 -44
- package/dist/ui/theme.mjs.map +0 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { decryptCredential, encrypt } from "../utils/encrypt.mjs";
|
|
5
|
+
import { ulid } from "../utils/ulid.mjs";
|
|
6
|
+
import { eq, getTableColumns, isNull } from "drizzle-orm";
|
|
7
|
+
//#region src/models/notification-channel.ts
|
|
8
|
+
/** Project and site-wide notification channel subscriptions. */
|
|
9
|
+
/** Data operations for notification channels. */
|
|
10
|
+
var NotificationChannelModel = class {
|
|
11
|
+
db;
|
|
12
|
+
tables;
|
|
13
|
+
secret;
|
|
14
|
+
/**
|
|
15
|
+
* @param db - Database adapter.
|
|
16
|
+
* @param tables - Table handles (defaults to the adapter's tables).
|
|
17
|
+
* @param secret - Server secret for channel-secret encryption.
|
|
18
|
+
*/
|
|
19
|
+
constructor(db, tables, secret) {
|
|
20
|
+
this.db = db;
|
|
21
|
+
this.tables = tables ?? { notificationChannels: db.tables.notificationChannels };
|
|
22
|
+
this.secret = secret;
|
|
23
|
+
}
|
|
24
|
+
/** List channels for a project. */
|
|
25
|
+
async list(projectId) {
|
|
26
|
+
return await this.db.list(this.tables.notificationChannels, { where: eq(this.column("projectId"), projectId) });
|
|
27
|
+
}
|
|
28
|
+
/** List site-wide admin channels (`project_id IS NULL`). */
|
|
29
|
+
async listSystem() {
|
|
30
|
+
return await this.db.list(this.tables.notificationChannels, { where: isNull(this.column("projectId")) });
|
|
31
|
+
}
|
|
32
|
+
/** Create a channel (secret encrypted at rest when provided). */
|
|
33
|
+
async create(input) {
|
|
34
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
35
|
+
return await this.db.insert(this.tables.notificationChannels, {
|
|
36
|
+
id: ulid(),
|
|
37
|
+
projectId: input.projectId,
|
|
38
|
+
provider: input.provider,
|
|
39
|
+
config: JSON.stringify(input.config),
|
|
40
|
+
secretEncrypted: input.secret ? encrypt(this.secret, input.secret) : null,
|
|
41
|
+
events: input.events && input.events.length > 0 ? JSON.stringify(input.events) : null,
|
|
42
|
+
enabled: input.enabled ?? true,
|
|
43
|
+
createdAt: now,
|
|
44
|
+
updatedAt: now
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Fetch a channel by id, or null. */
|
|
48
|
+
async get(id) {
|
|
49
|
+
return (await this.db.list(this.tables.notificationChannels, {
|
|
50
|
+
where: eq(this.column("id"), id),
|
|
51
|
+
limit: 1
|
|
52
|
+
}))[0] ?? null;
|
|
53
|
+
}
|
|
54
|
+
/** Remove a channel by id. */
|
|
55
|
+
async remove(id) {
|
|
56
|
+
await this.db.remove(this.tables.notificationChannels, id);
|
|
57
|
+
}
|
|
58
|
+
/** Decrypt the secret for a stored row (in memory only, at send time). */
|
|
59
|
+
decryptSecret(row) {
|
|
60
|
+
if (!row.secretEncrypted) throw new Error("Channel has no secret");
|
|
61
|
+
return decryptCredential(this.secret, row.secretEncrypted);
|
|
62
|
+
}
|
|
63
|
+
/** Decode the JSON-serialized event list of a channel. */
|
|
64
|
+
static eventsOf(row) {
|
|
65
|
+
return decodeEvents(row.events);
|
|
66
|
+
}
|
|
67
|
+
/** Decode the JSON-serialized config of a channel. */
|
|
68
|
+
static configOf(row) {
|
|
69
|
+
try {
|
|
70
|
+
const parsed = JSON.parse(row.config);
|
|
71
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
72
|
+
} catch {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
column(name) {
|
|
77
|
+
return getTableColumns(this.tables.notificationChannels)[name];
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/** Decode a JSON event list column (empty = all events). */
|
|
81
|
+
function decodeEvents(raw) {
|
|
82
|
+
if (!raw) return [];
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(raw);
|
|
85
|
+
return Array.isArray(parsed) ? parsed.filter((event) => typeof event === "string") : [];
|
|
86
|
+
} catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { NotificationChannelModel };
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=notification-channel.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-channel.mjs","names":[],"sources":["../../src/models/notification-channel.ts"],"sourcesContent":["/** Project and site-wide notification channel subscriptions. */\nimport { eq, getTableColumns, isNull } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { NotificationChannelRow } from \"../schema/notification.ts\";\nimport { decryptCredential, encrypt, type SecretInput } from \"../utils/encrypt.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link NotificationChannelModel}. */\nexport interface NotificationChannelTables {\n notificationChannels: Table;\n}\n\n/** Input for creating a notification channel. */\nexport interface NotificationChannelCreateInput {\n projectId: string | null;\n provider: string;\n config: Record<string, unknown>;\n secret?: string;\n events?: string[];\n enabled?: boolean;\n}\n\n/** Data operations for notification channels. */\nexport class NotificationChannelModel {\n private readonly tables: NotificationChannelTables;\n private readonly secret?: SecretInput;\n /**\n * @param db - Database adapter.\n * @param tables - Table handles (defaults to the adapter's tables).\n * @param secret - Server secret for channel-secret encryption.\n */\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: NotificationChannelTables,\n secret?: SecretInput,\n ) {\n this.tables = tables ?? { notificationChannels: db.tables.notificationChannels };\n this.secret = secret;\n }\n\n /** List channels for a project. */\n async list(projectId: string): Promise<NotificationChannelRow[]> {\n return (await this.db.list(this.tables.notificationChannels, {\n where: eq(this.column(\"projectId\"), projectId),\n })) as unknown as NotificationChannelRow[];\n }\n\n /** List site-wide admin channels (`project_id IS NULL`). */\n async listSystem(): Promise<NotificationChannelRow[]> {\n return (await this.db.list(this.tables.notificationChannels, {\n where: isNull(this.column(\"projectId\")),\n })) as unknown as NotificationChannelRow[];\n }\n\n /** Create a channel (secret encrypted at rest when provided). */\n async create(input: NotificationChannelCreateInput): Promise<NotificationChannelRow> {\n const now = new Date().toISOString();\n return (await this.db.insert(this.tables.notificationChannels, {\n id: ulid(),\n projectId: input.projectId,\n provider: input.provider,\n config: JSON.stringify(input.config),\n secretEncrypted: input.secret ? encrypt(this.secret, input.secret) : null,\n events: input.events && input.events.length > 0 ? JSON.stringify(input.events) : null,\n enabled: input.enabled ?? true,\n createdAt: now,\n updatedAt: now,\n })) as unknown as NotificationChannelRow;\n }\n\n /** Fetch a channel by id, or null. */\n async get(id: string): Promise<NotificationChannelRow | null> {\n const rows = (await this.db.list(this.tables.notificationChannels, {\n where: eq(this.column(\"id\"), id),\n limit: 1,\n })) as unknown as NotificationChannelRow[];\n return rows[0] ?? null;\n }\n\n /** Remove a channel by id. */\n async remove(id: string): Promise<void> {\n await this.db.remove(this.tables.notificationChannels, id);\n }\n\n /** Decrypt the secret for a stored row (in memory only, at send time). */\n decryptSecret(row: NotificationChannelRow): string {\n if (!row.secretEncrypted) {\n throw new Error(\"Channel has no secret\");\n }\n return decryptCredential(this.secret, row.secretEncrypted);\n }\n\n /** Decode the JSON-serialized event list of a channel. */\n static eventsOf(row: NotificationChannelRow): string[] {\n return decodeEvents(row.events);\n }\n\n /** Decode the JSON-serialized config of a channel. */\n static configOf(row: NotificationChannelRow): Record<string, unknown> {\n try {\n const parsed: unknown = JSON.parse(row.config);\n return typeof parsed === \"object\" && parsed !== null\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n\n private column(name: string): SQLWrapper {\n return getTableColumns(this.tables.notificationChannels)[name] as unknown as SQLWrapper;\n }\n}\n\n/** Decode a JSON event list column (empty = all events). */\nfunction decodeEvents(raw: string | null): string[] {\n if (!raw) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed)\n ? parsed.filter((event): event is string => typeof event === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n"],"mappings":";;;;;;;;;AAwBA,IAAa,2BAAb,MAAsC;CASjB;CARnB;CACA;;;;;;CAMA,YACE,IACA,QACA,QACA;EAHiB,KAAA,KAAA;EAIjB,KAAK,SAAS,UAAU,EAAE,sBAAsB,GAAG,OAAO,qBAAqB;EAC/E,KAAK,SAAS;CAChB;;CAGA,MAAM,KAAK,WAAsD;EAC/D,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,sBAAsB,EAC3D,OAAO,GAAG,KAAK,OAAO,WAAW,GAAG,SAAS,EAC/C,CAAC;CACH;;CAGA,MAAM,aAAgD;EACpD,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,sBAAsB,EAC3D,OAAO,OAAO,KAAK,OAAO,WAAW,CAAC,EACxC,CAAC;CACH;;CAGA,MAAM,OAAO,OAAwE;EACnF,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,sBAAsB;GAC7D,IAAI,KAAK;GACT,WAAW,MAAM;GACjB,UAAU,MAAM;GAChB,QAAQ,KAAK,UAAU,MAAM,MAAM;GACnC,iBAAiB,MAAM,SAAS,QAAQ,KAAK,QAAQ,MAAM,MAAM,IAAI;GACrE,QAAQ,MAAM,UAAU,MAAM,OAAO,SAAS,IAAI,KAAK,UAAU,MAAM,MAAM,IAAI;GACjF,SAAS,MAAM,WAAW;GAC1B,WAAW;GACX,WAAW;EACb,CAAC;CACH;;CAGA,MAAM,IAAI,IAAoD;EAK5D,QAAO,MAJa,KAAK,GAAG,KAAK,KAAK,OAAO,sBAAsB;GACjE,OAAO,GAAG,KAAK,OAAO,IAAI,GAAG,EAAE;GAC/B,OAAO;EACT,CAAC,EAAA,CACW,MAAM;CACpB;;CAGA,MAAM,OAAO,IAA2B;EACtC,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,sBAAsB,EAAE;CAC3D;;CAGA,cAAc,KAAqC;EACjD,IAAI,CAAC,IAAI,iBACP,MAAM,IAAI,MAAM,uBAAuB;EAEzC,OAAO,kBAAkB,KAAK,QAAQ,IAAI,eAAe;CAC3D;;CAGA,OAAO,SAAS,KAAuC;EACrD,OAAO,aAAa,IAAI,MAAM;CAChC;;CAGA,OAAO,SAAS,KAAsD;EACpE,IAAI;GACF,MAAM,SAAkB,KAAK,MAAM,IAAI,MAAM;GAC7C,OAAO,OAAO,WAAW,YAAY,WAAW,OAC3C,SACD,CAAC;EACP,QAAQ;GACN,OAAO,CAAC;EACV;CACF;CAEA,OAAe,MAA0B;EACvC,OAAO,gBAAgB,KAAK,OAAO,oBAAoB,CAAC,CAAC;CAC3D;AACF;;AAGA,SAAS,aAAa,KAA8B;CAClD,IAAI,CAAC,KACH,OAAO,CAAC;CAEV,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,GAAG;EACtC,OAAO,MAAM,QAAQ,MAAM,IACvB,OAAO,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IACnE,CAAC;CACP,QAAQ;EACN,OAAO,CAAC;CACV;AACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
|
+
import { NotificationSubscriptionRow } from "../schema/notification.mjs";
|
|
5
|
+
import { Table } from "drizzle-orm";
|
|
6
|
+
//#region src/models/notification-subscription.d.ts
|
|
7
|
+
/** Tables required by {@link NotificationSubscriptionModel}. */
|
|
8
|
+
interface NotificationSubscriptionTables {
|
|
9
|
+
notificationSubscriptions: Table;
|
|
10
|
+
}
|
|
11
|
+
/** Input for upserting a subscription (all fields optional except identity). */
|
|
12
|
+
interface NotificationSubscriptionInput {
|
|
13
|
+
events?: string[];
|
|
14
|
+
via?: string[];
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Data operations for per-user notification subscriptions. */
|
|
18
|
+
declare class NotificationSubscriptionModel {
|
|
19
|
+
private readonly db;
|
|
20
|
+
private readonly tables;
|
|
21
|
+
/**
|
|
22
|
+
* @param db - Database adapter.
|
|
23
|
+
* @param tables - Table handles (defaults to the adapter's tables).
|
|
24
|
+
*/
|
|
25
|
+
constructor(db: DatabaseAdapter, tables?: NotificationSubscriptionTables);
|
|
26
|
+
/** Fetch a user's subscription for a project, or null (null = silent). */
|
|
27
|
+
getFor(projectId: string, userId: string): Promise<NotificationSubscriptionRow | null>;
|
|
28
|
+
/** List all subscriptions for a project (admin audit). */
|
|
29
|
+
list(projectId: string): Promise<NotificationSubscriptionRow[]>;
|
|
30
|
+
/** List all subscriptions for a user (profile overview). */
|
|
31
|
+
listForUser(userId: string): Promise<NotificationSubscriptionRow[]>;
|
|
32
|
+
/** Create or replace a user's subscription for a project. */
|
|
33
|
+
upsert(projectId: string, userId: string, input: NotificationSubscriptionInput): Promise<NotificationSubscriptionRow>;
|
|
34
|
+
/** Remove a user's subscription (opt out). */
|
|
35
|
+
remove(projectId: string, userId: string): Promise<void>;
|
|
36
|
+
/** Decode the JSON-serialized event list of a subscription. */
|
|
37
|
+
static eventsOf(row: NotificationSubscriptionRow): string[];
|
|
38
|
+
/** Decode the JSON-serialized channel list of a subscription. */
|
|
39
|
+
static viaOf(row: NotificationSubscriptionRow): string[];
|
|
40
|
+
private column;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { NotificationSubscriptionInput, NotificationSubscriptionModel, NotificationSubscriptionTables };
|
|
44
|
+
//# sourceMappingURL=notification-subscription.d.mts.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { ulid } from "../utils/ulid.mjs";
|
|
5
|
+
import { and, eq, getTableColumns } from "drizzle-orm";
|
|
6
|
+
//#region src/models/notification-subscription.ts
|
|
7
|
+
/** Per-user opt-in rows for project notifications. */
|
|
8
|
+
/** Data operations for per-user notification subscriptions. */
|
|
9
|
+
var NotificationSubscriptionModel = class {
|
|
10
|
+
db;
|
|
11
|
+
tables;
|
|
12
|
+
/**
|
|
13
|
+
* @param db - Database adapter.
|
|
14
|
+
* @param tables - Table handles (defaults to the adapter's tables).
|
|
15
|
+
*/
|
|
16
|
+
constructor(db, tables) {
|
|
17
|
+
this.db = db;
|
|
18
|
+
this.tables = tables ?? { notificationSubscriptions: db.tables.notificationSubscriptions };
|
|
19
|
+
}
|
|
20
|
+
/** Fetch a user's subscription for a project, or null (null = silent). */
|
|
21
|
+
async getFor(projectId, userId) {
|
|
22
|
+
const projectScope = eq(this.column("projectId"), projectId);
|
|
23
|
+
const userScope = eq(this.column("userId"), userId);
|
|
24
|
+
return (await this.db.list(this.tables.notificationSubscriptions, {
|
|
25
|
+
where: and(projectScope, userScope),
|
|
26
|
+
limit: 1
|
|
27
|
+
}))[0] ?? null;
|
|
28
|
+
}
|
|
29
|
+
/** List all subscriptions for a project (admin audit). */
|
|
30
|
+
async list(projectId) {
|
|
31
|
+
return await this.db.list(this.tables.notificationSubscriptions, { where: eq(this.column("projectId"), projectId) });
|
|
32
|
+
}
|
|
33
|
+
/** List all subscriptions for a user (profile overview). */
|
|
34
|
+
async listForUser(userId) {
|
|
35
|
+
return await this.db.list(this.tables.notificationSubscriptions, { where: eq(this.column("userId"), userId) });
|
|
36
|
+
}
|
|
37
|
+
/** Create or replace a user's subscription for a project. */
|
|
38
|
+
async upsert(projectId, userId, input) {
|
|
39
|
+
const existing = await this.getFor(projectId, userId);
|
|
40
|
+
if (existing) return await this.db.update(this.tables.notificationSubscriptions, existing.id, {
|
|
41
|
+
events: encodeList(input.events),
|
|
42
|
+
via: encodeList(input.via),
|
|
43
|
+
enabled: input.enabled ?? true
|
|
44
|
+
});
|
|
45
|
+
return await this.db.insert(this.tables.notificationSubscriptions, {
|
|
46
|
+
id: ulid(),
|
|
47
|
+
projectId,
|
|
48
|
+
userId,
|
|
49
|
+
events: encodeList(input.events),
|
|
50
|
+
via: encodeList(input.via),
|
|
51
|
+
enabled: input.enabled ?? true,
|
|
52
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Remove a user's subscription (opt out). */
|
|
56
|
+
async remove(projectId, userId) {
|
|
57
|
+
const existing = await this.getFor(projectId, userId);
|
|
58
|
+
if (existing) await this.db.remove(this.tables.notificationSubscriptions, existing.id);
|
|
59
|
+
}
|
|
60
|
+
/** Decode the JSON-serialized event list of a subscription. */
|
|
61
|
+
static eventsOf(row) {
|
|
62
|
+
return decodeList(row.events);
|
|
63
|
+
}
|
|
64
|
+
/** Decode the JSON-serialized channel list of a subscription. */
|
|
65
|
+
static viaOf(row) {
|
|
66
|
+
return decodeList(row.via);
|
|
67
|
+
}
|
|
68
|
+
column(name) {
|
|
69
|
+
return getTableColumns(this.tables.notificationSubscriptions)[name];
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/** Encode an optional string list (undefined/empty = all, stored as null). */
|
|
73
|
+
function encodeList(values) {
|
|
74
|
+
return values && values.length > 0 ? JSON.stringify(values) : null;
|
|
75
|
+
}
|
|
76
|
+
/** Decode a JSON list column (null = all). */
|
|
77
|
+
function decodeList(raw) {
|
|
78
|
+
if (!raw) return [];
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(raw);
|
|
81
|
+
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
|
|
82
|
+
} catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { NotificationSubscriptionModel };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=notification-subscription.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-subscription.mjs","names":[],"sources":["../../src/models/notification-subscription.ts"],"sourcesContent":["/** Per-user opt-in rows for project notifications. */\nimport { and, eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { NotificationSubscriptionRow } from \"../schema/notification.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link NotificationSubscriptionModel}. */\nexport interface NotificationSubscriptionTables {\n notificationSubscriptions: Table;\n}\n\n/** Input for upserting a subscription (all fields optional except identity). */\nexport interface NotificationSubscriptionInput {\n events?: string[];\n via?: string[];\n enabled?: boolean;\n}\n\n/** Data operations for per-user notification subscriptions. */\nexport class NotificationSubscriptionModel {\n private readonly tables: NotificationSubscriptionTables;\n /**\n * @param db - Database adapter.\n * @param tables - Table handles (defaults to the adapter's tables).\n */\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: NotificationSubscriptionTables,\n ) {\n this.tables = tables ?? { notificationSubscriptions: db.tables.notificationSubscriptions };\n }\n\n /** Fetch a user's subscription for a project, or null (null = silent). */\n async getFor(projectId: string, userId: string): Promise<NotificationSubscriptionRow | null> {\n const projectScope = eq(this.column(\"projectId\"), projectId);\n const userScope = eq(this.column(\"userId\"), userId);\n const rows = (await this.db.list(this.tables.notificationSubscriptions, {\n where: and(projectScope, userScope),\n limit: 1,\n })) as unknown as NotificationSubscriptionRow[];\n return rows[0] ?? null;\n }\n\n /** List all subscriptions for a project (admin audit). */\n async list(projectId: string): Promise<NotificationSubscriptionRow[]> {\n return (await this.db.list(this.tables.notificationSubscriptions, {\n where: eq(this.column(\"projectId\"), projectId),\n })) as unknown as NotificationSubscriptionRow[];\n }\n\n /** List all subscriptions for a user (profile overview). */\n async listForUser(userId: string): Promise<NotificationSubscriptionRow[]> {\n return (await this.db.list(this.tables.notificationSubscriptions, {\n where: eq(this.column(\"userId\"), userId),\n })) as unknown as NotificationSubscriptionRow[];\n }\n\n /** Create or replace a user's subscription for a project. */\n async upsert(\n projectId: string,\n userId: string,\n input: NotificationSubscriptionInput,\n ): Promise<NotificationSubscriptionRow> {\n const existing = await this.getFor(projectId, userId);\n if (existing) {\n return (await this.db.update(this.tables.notificationSubscriptions, existing.id, {\n events: encodeList(input.events),\n via: encodeList(input.via),\n enabled: input.enabled ?? true,\n })) as unknown as NotificationSubscriptionRow;\n }\n return (await this.db.insert(this.tables.notificationSubscriptions, {\n id: ulid(),\n projectId,\n userId,\n events: encodeList(input.events),\n via: encodeList(input.via),\n enabled: input.enabled ?? true,\n createdAt: new Date().toISOString(),\n })) as unknown as NotificationSubscriptionRow;\n }\n\n /** Remove a user's subscription (opt out). */\n async remove(projectId: string, userId: string): Promise<void> {\n const existing = await this.getFor(projectId, userId);\n if (existing) {\n await this.db.remove(this.tables.notificationSubscriptions, existing.id);\n }\n }\n\n /** Decode the JSON-serialized event list of a subscription. */\n static eventsOf(row: NotificationSubscriptionRow): string[] {\n return decodeList(row.events);\n }\n\n /** Decode the JSON-serialized channel list of a subscription. */\n static viaOf(row: NotificationSubscriptionRow): string[] {\n return decodeList(row.via);\n }\n\n private column(name: string): SQLWrapper {\n return getTableColumns(this.tables.notificationSubscriptions)[name] as unknown as SQLWrapper;\n }\n}\n\n/** Encode an optional string list (undefined/empty = all, stored as null). */\nfunction encodeList(values: string[] | undefined): string | null {\n return values && values.length > 0 ? JSON.stringify(values) : null;\n}\n\n/** Decode a JSON list column (null = all). */\nfunction decodeList(raw: string | null): string[] {\n if (!raw) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed)\n ? parsed.filter((entry): entry is string => typeof entry === \"string\")\n : [];\n } catch {\n return [];\n }\n}\n"],"mappings":";;;;;;;;AAoBA,IAAa,gCAAb,MAA2C;CAOtB;CANnB;;;;;CAKA,YACE,IACA,QACA;EAFiB,KAAA,KAAA;EAGjB,KAAK,SAAS,UAAU,EAAE,2BAA2B,GAAG,OAAO,0BAA0B;CAC3F;;CAGA,MAAM,OAAO,WAAmB,QAA6D;EAC3F,MAAM,eAAe,GAAG,KAAK,OAAO,WAAW,GAAG,SAAS;EAC3D,MAAM,YAAY,GAAG,KAAK,OAAO,QAAQ,GAAG,MAAM;EAKlD,QAAO,MAJa,KAAK,GAAG,KAAK,KAAK,OAAO,2BAA2B;GACtE,OAAO,IAAI,cAAc,SAAS;GAClC,OAAO;EACT,CAAC,EAAA,CACW,MAAM;CACpB;;CAGA,MAAM,KAAK,WAA2D;EACpE,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,2BAA2B,EAChE,OAAO,GAAG,KAAK,OAAO,WAAW,GAAG,SAAS,EAC/C,CAAC;CACH;;CAGA,MAAM,YAAY,QAAwD;EACxE,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,2BAA2B,EAChE,OAAO,GAAG,KAAK,OAAO,QAAQ,GAAG,MAAM,EACzC,CAAC;CACH;;CAGA,MAAM,OACJ,WACA,QACA,OACsC;EACtC,MAAM,WAAW,MAAM,KAAK,OAAO,WAAW,MAAM;EACpD,IAAI,UACF,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,2BAA2B,SAAS,IAAI;GAC/E,QAAQ,WAAW,MAAM,MAAM;GAC/B,KAAK,WAAW,MAAM,GAAG;GACzB,SAAS,MAAM,WAAW;EAC5B,CAAC;EAEH,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,2BAA2B;GAClE,IAAI,KAAK;GACT;GACA;GACA,QAAQ,WAAW,MAAM,MAAM;GAC/B,KAAK,WAAW,MAAM,GAAG;GACzB,SAAS,MAAM,WAAW;GAC1B,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;;CAGA,MAAM,OAAO,WAAmB,QAA+B;EAC7D,MAAM,WAAW,MAAM,KAAK,OAAO,WAAW,MAAM;EACpD,IAAI,UACF,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,2BAA2B,SAAS,EAAE;CAE3E;;CAGA,OAAO,SAAS,KAA4C;EAC1D,OAAO,WAAW,IAAI,MAAM;CAC9B;;CAGA,OAAO,MAAM,KAA4C;EACvD,OAAO,WAAW,IAAI,GAAG;CAC3B;CAEA,OAAe,MAA0B;EACvC,OAAO,gBAAgB,KAAK,OAAO,yBAAyB,CAAC,CAAC;CAChE;AACF;;AAGA,SAAS,WAAW,QAA6C;CAC/D,OAAO,UAAU,OAAO,SAAS,IAAI,KAAK,UAAU,MAAM,IAAI;AAChE;;AAGA,SAAS,WAAW,KAA8B;CAChD,IAAI,CAAC,KACH,OAAO,CAAC;CAEV,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,GAAG;EACtC,OAAO,MAAM,QAAQ,MAAM,IACvB,OAAO,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IACnE,CAAC;CACP,QAAQ;EACN,OAAO,CAAC;CACV;AACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
|
+
import { ProjectRole } from "../types.mjs";
|
|
5
|
+
import { ProjectGroupMapping } from "../schema/project-group-mapping.mjs";
|
|
6
|
+
import { Table } from "drizzle-orm";
|
|
7
|
+
//#region src/models/project-group-mapping.d.ts
|
|
8
|
+
/** Tables required by {@link ProjectGroupMappingModel}. */
|
|
9
|
+
interface ProjectGroupMappingTables {
|
|
10
|
+
projectGroupMappings: Table;
|
|
11
|
+
}
|
|
12
|
+
/** Data operations for identity-provider group mappings. */
|
|
13
|
+
declare class ProjectGroupMappingModel {
|
|
14
|
+
private readonly db;
|
|
15
|
+
private readonly tables;
|
|
16
|
+
/**
|
|
17
|
+
* @param db - Database adapter.
|
|
18
|
+
* @param tables - Table handles.
|
|
19
|
+
*/
|
|
20
|
+
constructor(db: DatabaseAdapter, tables?: ProjectGroupMappingTables);
|
|
21
|
+
/** List all group mappings for a project. */
|
|
22
|
+
list(projectId: string): Promise<ProjectGroupMapping[]>;
|
|
23
|
+
/** Create a group mapping for a project. */
|
|
24
|
+
create(projectId: string, groupName: string, role: ProjectRole): Promise<ProjectGroupMapping>;
|
|
25
|
+
/** Remove a group mapping by id within a project. */
|
|
26
|
+
remove(projectId: string, id: string): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { ProjectGroupMappingModel, ProjectGroupMappingTables };
|
|
30
|
+
//# sourceMappingURL=project-group-mapping.d.mts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { ulid } from "../utils/ulid.mjs";
|
|
5
|
+
import { and, eq, getTableColumns } from "drizzle-orm";
|
|
6
|
+
//#region src/models/project-group-mapping.ts
|
|
7
|
+
/** Project group-to-role mappings for OIDC team sync. */
|
|
8
|
+
/** Data operations for identity-provider group mappings. */
|
|
9
|
+
var ProjectGroupMappingModel = class {
|
|
10
|
+
db;
|
|
11
|
+
tables;
|
|
12
|
+
/**
|
|
13
|
+
* @param db - Database adapter.
|
|
14
|
+
* @param tables - Table handles.
|
|
15
|
+
*/
|
|
16
|
+
constructor(db, tables) {
|
|
17
|
+
this.db = db;
|
|
18
|
+
this.tables = tables ?? { projectGroupMappings: db.tables.projectGroupMappings };
|
|
19
|
+
}
|
|
20
|
+
/** List all group mappings for a project. */
|
|
21
|
+
async list(projectId) {
|
|
22
|
+
return await this.db.list(this.tables.projectGroupMappings, { where: eq(getTableColumns(this.tables.projectGroupMappings)["projectId"], projectId) });
|
|
23
|
+
}
|
|
24
|
+
/** Create a group mapping for a project. */
|
|
25
|
+
async create(projectId, groupName, role) {
|
|
26
|
+
return await this.db.insert(this.tables.projectGroupMappings, {
|
|
27
|
+
id: ulid(),
|
|
28
|
+
projectId,
|
|
29
|
+
groupName,
|
|
30
|
+
role,
|
|
31
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/** Remove a group mapping by id within a project. */
|
|
35
|
+
async remove(projectId, id) {
|
|
36
|
+
const rows = await this.db.list(this.tables.projectGroupMappings, {
|
|
37
|
+
where: and(eq(getTableColumns(this.tables.projectGroupMappings)["id"], id), eq(getTableColumns(this.tables.projectGroupMappings)["projectId"], projectId)),
|
|
38
|
+
limit: 1
|
|
39
|
+
});
|
|
40
|
+
if (rows[0]) await this.db.remove(this.tables.projectGroupMappings, rows[0].id);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { ProjectGroupMappingModel };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=project-group-mapping.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-group-mapping.mjs","names":[],"sources":["../../src/models/project-group-mapping.ts"],"sourcesContent":["/** Project group-to-role mappings for OIDC team sync. */\nimport { and, eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { ProjectGroupMapping } from \"../schema/project-group-mapping.ts\";\nimport type { ProjectRole } from \"../types.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link ProjectGroupMappingModel}. */\nexport interface ProjectGroupMappingTables {\n projectGroupMappings: Table;\n}\n\n/** Data operations for identity-provider group mappings. */\nexport class ProjectGroupMappingModel {\n private readonly tables: ProjectGroupMappingTables;\n /**\n * @param db - Database adapter.\n * @param tables - Table handles.\n */\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: ProjectGroupMappingTables,\n ) {\n this.tables = tables ?? { projectGroupMappings: db.tables.projectGroupMappings };\n }\n\n /** List all group mappings for a project. */\n async list(projectId: string): Promise<ProjectGroupMapping[]> {\n return (await this.db.list(this.tables.projectGroupMappings, {\n where: eq(\n getTableColumns(this.tables.projectGroupMappings)[\"projectId\"] as unknown as SQLWrapper,\n projectId,\n ),\n })) as unknown as ProjectGroupMapping[];\n }\n\n /** Create a group mapping for a project. */\n async create(\n projectId: string,\n groupName: string,\n role: ProjectRole,\n ): Promise<ProjectGroupMapping> {\n return (await this.db.insert(this.tables.projectGroupMappings, {\n id: ulid(),\n projectId,\n groupName,\n role,\n createdAt: new Date().toISOString(),\n })) as unknown as ProjectGroupMapping;\n }\n\n /** Remove a group mapping by id within a project. */\n async remove(projectId: string, id: string): Promise<void> {\n const rows = (await this.db.list(this.tables.projectGroupMappings, {\n where: and(\n eq(getTableColumns(this.tables.projectGroupMappings)[\"id\"] as unknown as SQLWrapper, id),\n eq(\n getTableColumns(this.tables.projectGroupMappings)[\"projectId\"] as unknown as SQLWrapper,\n projectId,\n ),\n ),\n limit: 1,\n })) as unknown as ProjectGroupMapping[];\n if (rows[0]) {\n await this.db.remove(this.tables.projectGroupMappings, rows[0].id);\n }\n }\n}\n"],"mappings":";;;;;;;;AAcA,IAAa,2BAAb,MAAsC;CAOjB;CANnB;;;;;CAKA,YACE,IACA,QACA;EAFiB,KAAA,KAAA;EAGjB,KAAK,SAAS,UAAU,EAAE,sBAAsB,GAAG,OAAO,qBAAqB;CACjF;;CAGA,MAAM,KAAK,WAAmD;EAC5D,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,sBAAsB,EAC3D,OAAO,GACL,gBAAgB,KAAK,OAAO,oBAAoB,CAAC,CAAC,cAClD,SACF,EACF,CAAC;CACH;;CAGA,MAAM,OACJ,WACA,WACA,MAC8B;EAC9B,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,sBAAsB;GAC7D,IAAI,KAAK;GACT;GACA;GACA;GACA,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;;CAGA,MAAM,OAAO,WAAmB,IAA2B;EACzD,MAAM,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,sBAAsB;GACjE,OAAO,IACL,GAAG,gBAAgB,KAAK,OAAO,oBAAoB,CAAC,CAAC,OAAgC,EAAE,GACvF,GACE,gBAAgB,KAAK,OAAO,oBAAoB,CAAC,CAAC,cAClD,SACF,CACF;GACA,OAAO;EACT,CAAC;EACD,IAAI,KAAK,IACP,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,sBAAsB,KAAK,EAAE,CAAC,EAAE;CAErE;AACF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
|
+
import { Project } from "../schema/project.mjs";
|
|
5
|
+
import { Table } from "drizzle-orm";
|
|
6
|
+
//#region src/models/project.d.ts
|
|
7
|
+
/** Tables required by {@link ProjectModel}. */
|
|
8
|
+
interface ProjectTables {
|
|
9
|
+
projects: Table;
|
|
10
|
+
}
|
|
11
|
+
/** Data operations for project records. */
|
|
12
|
+
declare class ProjectModel {
|
|
13
|
+
private readonly db;
|
|
14
|
+
private readonly tables;
|
|
15
|
+
/**
|
|
16
|
+
* @param db - Database adapter.
|
|
17
|
+
* @param tables - Table handles (injected by the database adapter).
|
|
18
|
+
*/
|
|
19
|
+
constructor(db: DatabaseAdapter, tables?: ProjectTables);
|
|
20
|
+
/**
|
|
21
|
+
* Create a project with a unique slug.
|
|
22
|
+
*
|
|
23
|
+
* @param input - Project creation input.
|
|
24
|
+
* @returns The created project.
|
|
25
|
+
*/
|
|
26
|
+
create(input: ProjectCreateInput): Promise<Project>;
|
|
27
|
+
/** Fetch a project by id, or null if not found. */
|
|
28
|
+
get(id: string): Promise<Project | null>;
|
|
29
|
+
/** Fetch a project by its slug, or null if not found. */
|
|
30
|
+
getBySlug(slug: string): Promise<Project | null>;
|
|
31
|
+
/** List all projects. */
|
|
32
|
+
list(): Promise<Project[]>;
|
|
33
|
+
/** Update mutable fields of a project. */
|
|
34
|
+
update(id: string, patch: Partial<Omit<Project, "storybookMeta">> & {
|
|
35
|
+
storybookMeta?: unknown;
|
|
36
|
+
}): Promise<Project>;
|
|
37
|
+
/** Delete a project by id. */
|
|
38
|
+
remove(id: string): Promise<void>;
|
|
39
|
+
private uniqueSlug;
|
|
40
|
+
}
|
|
41
|
+
/** Input for creating a project. */
|
|
42
|
+
interface ProjectCreateInput {
|
|
43
|
+
name: string;
|
|
44
|
+
gitRepository?: string;
|
|
45
|
+
gitDefaultBranch?: string;
|
|
46
|
+
storybookMeta?: unknown;
|
|
47
|
+
/** Run Storybook `play` functions during capture (opt-in; default false). */
|
|
48
|
+
executePlay?: boolean;
|
|
49
|
+
/** Per-story play timeout in milliseconds (column default applies when omitted). */
|
|
50
|
+
playTimeoutMs?: number;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { ProjectCreateInput, ProjectModel, ProjectTables };
|
|
54
|
+
//# sourceMappingURL=project.d.mts.map
|
package/dist/models/project.mjs
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import { projects } from "../schema/project.mjs";
|
|
5
4
|
import { slugify, ulid } from "../utils/ulid.mjs";
|
|
6
|
-
import { eq } from "drizzle-orm";
|
|
5
|
+
import { eq, getTableColumns } from "drizzle-orm";
|
|
7
6
|
//#region src/models/project.ts
|
|
8
7
|
/** Project records and slug management. */
|
|
9
8
|
/** Data operations for project records. */
|
|
10
9
|
var ProjectModel = class {
|
|
11
10
|
db;
|
|
11
|
+
tables;
|
|
12
12
|
/**
|
|
13
13
|
* @param db - Database adapter.
|
|
14
|
+
* @param tables - Table handles (injected by the database adapter).
|
|
14
15
|
*/
|
|
15
|
-
constructor(db) {
|
|
16
|
+
constructor(db, tables) {
|
|
16
17
|
this.db = db;
|
|
18
|
+
this.tables = tables ?? { projects: db.tables.projects };
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Create a project with a unique slug.
|
|
@@ -24,48 +26,50 @@ var ProjectModel = class {
|
|
|
24
26
|
async create(input) {
|
|
25
27
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
26
28
|
const slug = await this.uniqueSlug(input.name);
|
|
27
|
-
return this.db.insert(projects, {
|
|
29
|
+
return await this.db.insert(this.tables.projects, {
|
|
28
30
|
id: ulid(),
|
|
29
31
|
name: input.name,
|
|
30
32
|
slug,
|
|
31
33
|
gitRepository: input.gitRepository,
|
|
32
34
|
gitDefaultBranch: input.gitDefaultBranch ?? "main",
|
|
33
35
|
storybookMeta: input.storybookMeta ? JSON.stringify(input.storybookMeta) : null,
|
|
36
|
+
...input.executePlay === void 0 ? {} : { executePlay: input.executePlay },
|
|
37
|
+
...input.playTimeoutMs === void 0 ? {} : { playTimeoutMs: input.playTimeoutMs },
|
|
34
38
|
createdAt: now,
|
|
35
39
|
updatedAt: now
|
|
36
40
|
});
|
|
37
41
|
}
|
|
38
42
|
/** Fetch a project by id, or null if not found. */
|
|
39
43
|
async get(id) {
|
|
40
|
-
return await this.db.get(projects, id);
|
|
44
|
+
return await this.db.get(this.tables.projects, id);
|
|
41
45
|
}
|
|
42
46
|
/** Fetch a project by its slug, or null if not found. */
|
|
43
47
|
async getBySlug(slug) {
|
|
44
|
-
return (await this.db.list(projects, {
|
|
45
|
-
where: eq(projects
|
|
48
|
+
return (await this.db.list(this.tables.projects, {
|
|
49
|
+
where: eq(getTableColumns(this.tables.projects)["slug"], slug),
|
|
46
50
|
limit: 1
|
|
47
51
|
}))[0] ?? null;
|
|
48
52
|
}
|
|
49
53
|
/** List all projects. */
|
|
50
54
|
async list() {
|
|
51
|
-
return await this.db.list(projects);
|
|
55
|
+
return await this.db.list(this.tables.projects);
|
|
52
56
|
}
|
|
53
57
|
/** Update mutable fields of a project. */
|
|
54
58
|
async update(id, patch) {
|
|
55
59
|
const normalized = { ...patch };
|
|
56
60
|
if (patch.storybookMeta !== void 0 && patch.storybookMeta !== null && typeof patch.storybookMeta !== "string") normalized.storybookMeta = JSON.stringify(patch.storybookMeta);
|
|
57
|
-
return await this.db.update(projects, id, {
|
|
61
|
+
return await this.db.update(this.tables.projects, id, {
|
|
58
62
|
...normalized,
|
|
59
63
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
60
64
|
});
|
|
61
65
|
}
|
|
62
66
|
/** Delete a project by id. */
|
|
63
67
|
async remove(id) {
|
|
64
|
-
await this.db.remove(projects, id);
|
|
68
|
+
await this.db.remove(this.tables.projects, id);
|
|
65
69
|
}
|
|
66
70
|
async uniqueSlug(name) {
|
|
67
71
|
const base = slugify(name) || "project";
|
|
68
|
-
const existing = await this.db.list(projects, { where: eq(projects
|
|
72
|
+
const existing = await this.db.list(this.tables.projects, { where: eq(getTableColumns(this.tables.projects)["slug"], base) });
|
|
69
73
|
if (existing.length === 0) return base;
|
|
70
74
|
const taken = new Set(existing.map((p) => p.slug));
|
|
71
75
|
let suffix = 2;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.mjs","names":[],"sources":["../../src/models/project.ts"],"sourcesContent":["/** Project records and slug management. */\nimport { eq } from \"drizzle-orm\";\nimport type {
|
|
1
|
+
{"version":3,"file":"project.mjs","names":[],"sources":["../../src/models/project.ts"],"sourcesContent":["/** Project records and slug management. */\nimport { eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport { slugify, ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link ProjectModel}. */\nexport interface ProjectTables {\n projects: Table;\n}\n\n/** Data operations for project records. */\nexport class ProjectModel {\n private readonly tables: ProjectTables;\n /**\n * @param db - Database adapter.\n * @param tables - Table handles (injected by the database adapter).\n */\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: ProjectTables,\n ) {\n this.tables = tables ?? { projects: db.tables.projects };\n }\n\n /**\n * Create a project with a unique slug.\n *\n * @param input - Project creation input.\n * @returns The created project.\n */\n async create(input: ProjectCreateInput): Promise<Project> {\n const now = new Date().toISOString();\n const slug = await this.uniqueSlug(input.name);\n return (await this.db.insert(this.tables.projects, {\n id: ulid(),\n name: input.name,\n slug,\n gitRepository: input.gitRepository,\n gitDefaultBranch: input.gitDefaultBranch ?? \"main\",\n storybookMeta: input.storybookMeta ? JSON.stringify(input.storybookMeta) : null,\n ...(input.executePlay === undefined ? {} : { executePlay: input.executePlay }),\n ...(input.playTimeoutMs === undefined ? {} : { playTimeoutMs: input.playTimeoutMs }),\n createdAt: now,\n updatedAt: now,\n })) as unknown as Project;\n }\n\n /** Fetch a project by id, or null if not found. */\n async get(id: string): Promise<Project | null> {\n return (await this.db.get(this.tables.projects, id)) as unknown as Project | null;\n }\n\n /** Fetch a project by its slug, or null if not found. */\n async getBySlug(slug: string): Promise<Project | null> {\n const rows = (await this.db.list(this.tables.projects, {\n where: eq(getTableColumns(this.tables.projects)[\"slug\"] as unknown as SQLWrapper, slug),\n limit: 1,\n })) as unknown as Project[];\n return rows[0] ?? null;\n }\n\n /** List all projects. */\n async list(): Promise<Project[]> {\n return (await this.db.list(this.tables.projects)) as unknown as Project[];\n }\n\n /** Update mutable fields of a project. */\n async update(\n id: string,\n patch: Partial<Omit<Project, \"storybookMeta\">> & {\n storybookMeta?: unknown;\n },\n ): Promise<Project> {\n const normalized: Partial<Project> & Record<string, unknown> = {\n ...patch,\n } as unknown as Partial<Project> & Record<string, unknown>;\n if (\n patch.storybookMeta !== undefined &&\n patch.storybookMeta !== null &&\n typeof patch.storybookMeta !== \"string\"\n ) {\n normalized.storybookMeta = JSON.stringify(patch.storybookMeta);\n }\n return (await this.db.update(this.tables.projects, id, {\n ...normalized,\n updatedAt: new Date().toISOString(),\n })) as unknown as Project;\n }\n\n /** Delete a project by id. */\n async remove(id: string): Promise<void> {\n await this.db.remove(this.tables.projects, id);\n }\n\n private async uniqueSlug(name: string): Promise<string> {\n const base = slugify(name) || \"project\";\n const existing = (await this.db.list(this.tables.projects, {\n where: eq(getTableColumns(this.tables.projects)[\"slug\"] as unknown as SQLWrapper, base),\n })) as unknown as Project[];\n if (existing.length === 0) {\n return base;\n }\n const taken = new Set(existing.map((p) => p.slug));\n let suffix = 2;\n while (taken.has(`${base}-${suffix}`)) {\n suffix += 1;\n }\n return `${base}-${suffix}`;\n }\n}\n\n/** Input for creating a project. */\nexport interface ProjectCreateInput {\n name: string;\n gitRepository?: string;\n gitDefaultBranch?: string;\n storybookMeta?: unknown;\n /** Run Storybook `play` functions during capture (opt-in; default false). */\n executePlay?: boolean;\n /** Per-story play timeout in milliseconds (column default applies when omitted). */\n playTimeoutMs?: number;\n}\n"],"mappings":";;;;;;;;AAaA,IAAa,eAAb,MAA0B;CAOL;CANnB;;;;;CAKA,YACE,IACA,QACA;EAFiB,KAAA,KAAA;EAGjB,KAAK,SAAS,UAAU,EAAE,UAAU,GAAG,OAAO,SAAS;CACzD;;;;;;;CAQA,MAAM,OAAO,OAA6C;EACxD,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,MAAM,OAAO,MAAM,KAAK,WAAW,MAAM,IAAI;EAC7C,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU;GACjD,IAAI,KAAK;GACT,MAAM,MAAM;GACZ;GACA,eAAe,MAAM;GACrB,kBAAkB,MAAM,oBAAoB;GAC5C,eAAe,MAAM,gBAAgB,KAAK,UAAU,MAAM,aAAa,IAAI;GAC3E,GAAI,MAAM,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,MAAM,YAAY;GAC5E,GAAI,MAAM,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,eAAe,MAAM,cAAc;GAClF,WAAW;GACX,WAAW;EACb,CAAC;CACH;;CAGA,MAAM,IAAI,IAAqC;EAC7C,OAAQ,MAAM,KAAK,GAAG,IAAI,KAAK,OAAO,UAAU,EAAE;CACpD;;CAGA,MAAM,UAAU,MAAuC;EAKrD,QAAO,MAJa,KAAK,GAAG,KAAK,KAAK,OAAO,UAAU;GACrD,OAAO,GAAG,gBAAgB,KAAK,OAAO,QAAQ,CAAC,CAAC,SAAkC,IAAI;GACtF,OAAO;EACT,CAAC,EAAA,CACW,MAAM;CACpB;;CAGA,MAAM,OAA2B;EAC/B,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,QAAQ;CACjD;;CAGA,MAAM,OACJ,IACA,OAGkB;EAClB,MAAM,aAAyD,EAC7D,GAAG,MACL;EACA,IACE,MAAM,kBAAkB,KAAA,KACxB,MAAM,kBAAkB,QACxB,OAAO,MAAM,kBAAkB,UAE/B,WAAW,gBAAgB,KAAK,UAAU,MAAM,aAAa;EAE/D,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU,IAAI;GACrD,GAAG;GACH,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;;CAGA,MAAM,OAAO,IAA2B;EACtC,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU,EAAE;CAC/C;CAEA,MAAc,WAAW,MAA+B;EACtD,MAAM,OAAO,QAAQ,IAAI,KAAK;EAC9B,MAAM,WAAY,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,UAAU,EACzD,OAAO,GAAG,gBAAgB,KAAK,OAAO,QAAQ,CAAC,CAAC,SAAkC,IAAI,EACxF,CAAC;EACD,IAAI,SAAS,WAAW,GACtB,OAAO;EAET,MAAM,QAAQ,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,IAAI,CAAC;EACjD,IAAI,SAAS;EACb,OAAO,MAAM,IAAI,GAAG,KAAK,GAAG,QAAQ,GAClC,UAAU;EAEZ,OAAO,GAAG,KAAK,GAAG;CACpB;AACF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
|
+
import { SnapshotStatus } from "../types.mjs";
|
|
5
|
+
import { Snapshot } from "../schema/snapshot.mjs";
|
|
6
|
+
import { Table } from "drizzle-orm";
|
|
7
|
+
//#region src/models/snapshot.d.ts
|
|
8
|
+
/** Tables required by {@link SnapshotModel}. */
|
|
9
|
+
interface SnapshotTables {
|
|
10
|
+
snapshots: Table;
|
|
11
|
+
}
|
|
12
|
+
/** Data operations for snapshot records. */
|
|
13
|
+
declare class SnapshotModel {
|
|
14
|
+
private readonly db;
|
|
15
|
+
private readonly tables;
|
|
16
|
+
/**
|
|
17
|
+
* @param db - Database adapter.
|
|
18
|
+
* @param tables - Table handles.
|
|
19
|
+
*/
|
|
20
|
+
constructor(db: DatabaseAdapter, tables?: SnapshotTables);
|
|
21
|
+
/**
|
|
22
|
+
* Create a snapshot for a story within a build.
|
|
23
|
+
*
|
|
24
|
+
* @param projectId - Project ID.
|
|
25
|
+
* @param buildId - Build ID.
|
|
26
|
+
* @param input - Snapshot creation input.
|
|
27
|
+
* @returns The created snapshot.
|
|
28
|
+
*/
|
|
29
|
+
create(projectId: string, buildId: string, input: SnapshotCreateInput): Promise<Snapshot>;
|
|
30
|
+
/** List all snapshots belonging to a build. */
|
|
31
|
+
listByBuild(buildId: string): Promise<Snapshot[]>;
|
|
32
|
+
/** Fetch a snapshot by id, or null if not found. */
|
|
33
|
+
get(id: string): Promise<Snapshot | null>;
|
|
34
|
+
/** Update mutable fields of a snapshot. */
|
|
35
|
+
update(id: string, patch: Partial<Snapshot>): Promise<Snapshot>;
|
|
36
|
+
/** Set the status of a snapshot. */
|
|
37
|
+
setStatus(id: string, status: SnapshotStatus): Promise<Snapshot>;
|
|
38
|
+
/** Record a reviewer's decision on a snapshot. */
|
|
39
|
+
review(id: string, status: SnapshotStatus, userId: string | null): Promise<Snapshot>;
|
|
40
|
+
}
|
|
41
|
+
/** Input for creating a snapshot. */
|
|
42
|
+
interface SnapshotCreateInput {
|
|
43
|
+
storyId: string;
|
|
44
|
+
storyName: string;
|
|
45
|
+
storyTitle: string;
|
|
46
|
+
storyImportPath?: string;
|
|
47
|
+
viewportName: string;
|
|
48
|
+
viewportWidth: number;
|
|
49
|
+
viewportHeight: number;
|
|
50
|
+
screenshotPath: string;
|
|
51
|
+
infraHash?: string | null;
|
|
52
|
+
inherited?: boolean;
|
|
53
|
+
baselineId?: string | null;
|
|
54
|
+
baselineVersion?: string | null;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { SnapshotCreateInput, SnapshotModel, SnapshotTables };
|
|
58
|
+
//# sourceMappingURL=snapshot.d.mts.map
|
package/dist/models/snapshot.mjs
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import { snapshots } from "../schema/snapshot.mjs";
|
|
5
4
|
import { ulid } from "../utils/ulid.mjs";
|
|
6
|
-
import { eq } from "drizzle-orm";
|
|
5
|
+
import { eq, getTableColumns } from "drizzle-orm";
|
|
7
6
|
//#region src/models/snapshot.ts
|
|
8
7
|
/** Snapshot records for captured stories within a build. */
|
|
9
8
|
/** Data operations for snapshot records. */
|
|
10
9
|
var SnapshotModel = class {
|
|
11
10
|
db;
|
|
11
|
+
tables;
|
|
12
12
|
/**
|
|
13
13
|
* @param db - Database adapter.
|
|
14
|
+
* @param tables - Table handles.
|
|
14
15
|
*/
|
|
15
|
-
constructor(db) {
|
|
16
|
+
constructor(db, tables) {
|
|
16
17
|
this.db = db;
|
|
18
|
+
this.tables = tables ?? { snapshots: db.tables.snapshots };
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Create a snapshot for a story within a build.
|
|
@@ -25,7 +27,7 @@ var SnapshotModel = class {
|
|
|
25
27
|
*/
|
|
26
28
|
async create(projectId, buildId, input) {
|
|
27
29
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
28
|
-
return await this.db.insert(snapshots, {
|
|
30
|
+
return await this.db.insert(this.tables.snapshots, {
|
|
29
31
|
id: ulid(),
|
|
30
32
|
projectId,
|
|
31
33
|
buildId,
|
|
@@ -37,6 +39,10 @@ var SnapshotModel = class {
|
|
|
37
39
|
viewportWidth: input.viewportWidth,
|
|
38
40
|
viewportHeight: input.viewportHeight,
|
|
39
41
|
screenshotPath: input.screenshotPath,
|
|
42
|
+
infraHash: input.infraHash ?? null,
|
|
43
|
+
inherited: input.inherited ?? false,
|
|
44
|
+
baselineId: input.baselineId ?? null,
|
|
45
|
+
baselineVersion: input.baselineVersion ?? null,
|
|
40
46
|
status: "pending",
|
|
41
47
|
createdAt: now,
|
|
42
48
|
updatedAt: now
|
|
@@ -44,15 +50,15 @@ var SnapshotModel = class {
|
|
|
44
50
|
}
|
|
45
51
|
/** List all snapshots belonging to a build. */
|
|
46
52
|
async listByBuild(buildId) {
|
|
47
|
-
return await this.db.list(snapshots, { where: eq(snapshots
|
|
53
|
+
return await this.db.list(this.tables.snapshots, { where: eq(getTableColumns(this.tables.snapshots)["buildId"], buildId) });
|
|
48
54
|
}
|
|
49
55
|
/** Fetch a snapshot by id, or null if not found. */
|
|
50
56
|
async get(id) {
|
|
51
|
-
return await this.db.get(snapshots, id);
|
|
57
|
+
return await this.db.get(this.tables.snapshots, id);
|
|
52
58
|
}
|
|
53
59
|
/** Update mutable fields of a snapshot. */
|
|
54
60
|
async update(id, patch) {
|
|
55
|
-
return await this.db.update(snapshots, id, {
|
|
61
|
+
return await this.db.update(this.tables.snapshots, id, {
|
|
56
62
|
...patch,
|
|
57
63
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
58
64
|
});
|