Webhooks
Webhooks let you receive notifications when packages are published or
From 44324c35d2e5467d56572d7f65fe91dffe1519a9 Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Thu, 1 Jan 2026 21:04:39 +0100
Subject: [PATCH 08/17] fixes
---
api/src/db/database.rs | 16 ++++++++--------
api/src/db/models.rs | 1 +
api/src/publish.rs | 9 ++-------
api/src/tasks.rs | 1 +
4 files changed, 12 insertions(+), 15 deletions(-)
diff --git a/api/src/db/database.rs b/api/src/db/database.rs
index 4e27a204c..6d3e572ba 100644
--- a/api/src/db/database.rs
+++ b/api/src/db/database.rs
@@ -3518,6 +3518,7 @@ impl Database {
Ok(task)
}
+
#[instrument(
name = "Database::process_webhooks_for_publish",
skip(self),
@@ -3525,21 +3526,20 @@ impl Database {
)]
pub async fn process_webhooks_for_publish(
&self,
- scope: &ScopeName,
- name: &PackageName,
- version: &Version,
+ task: &PublishingTask,
) -> Result> {
let mut tx = self.pool.begin().await?;
let webhook_deliveries = insert_webhook_event(
&mut tx,
- scope,
- Some(name),
+ &task.package_scope,
+ Some(&task.package_name),
WebhookEventKind::PackageVersionPublished,
WebhookPayload::PackageVersionPublished {
- scope: scope.clone(),
- package: name.clone(),
- version: version.clone(),
+ scope: task.package_scope.clone(),
+ package: task.package_name.clone(),
+ version: task.package_version.clone(),
+ user_id: task.user_id.clone(),
},
)
.await?;
diff --git a/api/src/db/models.rs b/api/src/db/models.rs
index 28d0e2637..2a07d87b1 100644
--- a/api/src/db/models.rs
+++ b/api/src/db/models.rs
@@ -1114,6 +1114,7 @@ pub enum WebhookPayload {
scope: ScopeName,
package: PackageName,
version: Version,
+ user_id: Option,
},
PackageVersionYanked {
scope: ScopeName,
diff --git a/api/src/publish.rs b/api/src/publish.rs
index 4b8fae06e..48d2951ac 100644
--- a/api/src/publish.rs
+++ b/api/src/publish.rs
@@ -142,13 +142,8 @@ pub async fn publish_task(
}
PublishingTaskStatus::Failure => return Ok(()),
PublishingTaskStatus::Success => {
- let webhook_deliveries = db
- .process_webhooks_for_publish(
- &publishing_task.package_scope,
- &publishing_task.package_name,
- &publishing_task.package_version,
- )
- .await?;
+ let webhook_deliveries =
+ db.process_webhooks_for_publish(&publishing_task).await?;
crate::tasks::enqueue_webhook_dispatches(
&webhook_dispatch_queue,
diff --git a/api/src/tasks.rs b/api/src/tasks.rs
index 8e37f92f6..36a362c75 100644
--- a/api/src/tasks.rs
+++ b/api/src/tasks.rs
@@ -552,6 +552,7 @@ async fn dispatch_webhook(
scope,
package,
version,
+ user_id: _,
} => ProviderEmbed {
color: GREEN,
title: "Package version published",
From 4d91711122666e6b0695c457820999ab109a45a3 Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Thu, 15 Jan 2026 09:25:41 +0100
Subject: [PATCH 09/17] fixes
---
frontend/routes/package/settings/index.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/routes/package/settings/index.tsx b/frontend/routes/package/settings/index.tsx
index 391d9215b..fcca4dfe9 100644
--- a/frontend/routes/package/settings/index.tsx
+++ b/frontend/routes/package/settings/index.tsx
@@ -325,7 +325,7 @@ function FeaturePackage(props: { package: Package }) {
function Webhooks({ webhooks }: { webhooks: WebhookEndpoint[] }) {
return (
-
+
Webhooks
Webhooks let you receive notifications when packages are published or
@@ -350,7 +350,7 @@ function Webhooks({ webhooks }: { webhooks: WebhookEndpoint[] }) {
)}
-
+
Create
From 1eb3b04859d4caff5468b7c7f2970131e422a008 Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Fri, 23 Jan 2026 17:25:35 +0100
Subject: [PATCH 10/17] Update api/src/db/database.rs
Co-authored-by: KnorpelSenf
---
api/src/db/database.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/api/src/db/database.rs b/api/src/db/database.rs
index 6d3e572ba..ef0a0b2e7 100644
--- a/api/src/db/database.rs
+++ b/api/src/db/database.rs
@@ -5538,7 +5538,7 @@ async fn audit_log(
Ok(())
}
-pub async fn insert_webhook_event(
+async fn insert_webhook_event(
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
scope: &ScopeName,
package: Option<&PackageName>,
From 3470b7164542bac9581fac48b89c0568ba9ee828 Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Sun, 1 Feb 2026 14:37:58 +0100
Subject: [PATCH 11/17] fixes, docs, and npm tarball event
---
api/migrations/20251221141049_webhook.sql | 1 +
api/src/api/errors.rs | 5 +
api/src/db/database.rs | 30 +++
api/src/db/models.rs | 8 +-
api/src/tasks.rs | 23 +-
frontend/components/Help.tsx | 11 +
frontend/docs/toc.ts | 5 +
frontend/docs/webhooks.md | 216 ++++++++++++++++++
frontend/islands/WebhookEdit.tsx | 15 +-
frontend/routes/@[scope]/~/settings/index.tsx | 5 +-
frontend/routes/package/settings/index.tsx | 5 +-
frontend/utils/api_types.ts | 1 +
12 files changed, 317 insertions(+), 8 deletions(-)
create mode 100644 frontend/components/Help.tsx
create mode 100644 frontend/docs/webhooks.md
diff --git a/api/migrations/20251221141049_webhook.sql b/api/migrations/20251221141049_webhook.sql
index 9a14d26cb..92e9d0e15 100644
--- a/api/migrations/20251221141049_webhook.sql
+++ b/api/migrations/20251221141049_webhook.sql
@@ -1,4 +1,5 @@
CREATE TYPE webhook_event_kind AS ENUM (
+ 'package_version_npm_tarball_ready',
'package_version_published',
'package_version_yanked',
'package_version_deleted',
diff --git a/api/src/api/errors.rs b/api/src/api/errors.rs
index 52affa89b..918c07dc6 100644
--- a/api/src/api/errors.rs
+++ b/api/src/api/errors.rs
@@ -253,6 +253,11 @@ errors!(
status: BAD_REQUEST,
"The metadata for the ticket is not in a valid format, should be a key-value of strings.",
},
+ WebhookResponseFailure {
+ status: BAD_REQUEST,
+ fields: { status: reqwest::StatusCode },
+ ({ status }) => "The webhook target responded with status {status}.",
+ },
);
pub fn map_unique_violation(err: sqlx::Error, new_err: ApiError) -> ApiError {
diff --git a/api/src/db/database.rs b/api/src/db/database.rs
index ef0a0b2e7..68537f528 100644
--- a/api/src/db/database.rs
+++ b/api/src/db/database.rs
@@ -2464,6 +2464,36 @@ impl Database {
.fetch_one(&self.pool)
.await
}
+ #[instrument(
+ name = "Database::process_webhooks_for_npm_tarball",
+ skip(self),
+ err
+ )]
+ pub async fn process_webhooks_for_npm_tarball(
+ &self,
+ package_scope: &ScopeName,
+ package_name: &PackageName,
+ package_version: &Version,
+ ) -> Result> {
+ let mut tx = self.pool.begin().await?;
+
+ let webhook_deliveries = insert_webhook_event(
+ &mut tx,
+ package_scope,
+ Some(package_name),
+ WebhookEventKind::PackageVersionNpmTarballReady,
+ WebhookPayload::PackageVersionNpmTarballReady {
+ scope: package_scope.clone(),
+ package: package_name.clone(),
+ version: package_version.clone(),
+ },
+ )
+ .await?;
+
+ tx.commit().await?;
+
+ Ok(webhook_deliveries)
+ }
#[instrument(name = "Database::get_scope_member", skip(self), err)]
pub async fn get_scope_member(
diff --git a/api/src/db/models.rs b/api/src/db/models.rs
index 2a07d87b1..74c1bd344 100644
--- a/api/src/db/models.rs
+++ b/api/src/db/models.rs
@@ -1046,6 +1046,7 @@ impl FromRow<'_, sqlx::postgres::PgRow> for AuditLog {
#[serde(rename_all = "snake_case")]
#[sqlx(type_name = "webhook_event_kind", rename_all = "snake_case")]
pub enum WebhookEventKind {
+ PackageVersionNpmTarballReady,
PackageVersionPublished,
PackageVersionYanked,
PackageVersionDeleted,
@@ -1110,10 +1111,15 @@ pub struct UpdateWebhookEndpoint {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event", rename_all = "snake_case")]
pub enum WebhookPayload {
- PackageVersionPublished {
+ PackageVersionNpmTarballReady {
scope: ScopeName,
package: PackageName,
version: Version,
+ },
+ PackageVersionPublished {
+ version: Version,
+ scope: ScopeName,
+ package: PackageName,
user_id: Option,
},
PackageVersionYanked {
diff --git a/api/src/tasks.rs b/api/src/tasks.rs
index 36a362c75..6549118ab 100644
--- a/api/src/tasks.rs
+++ b/api/src/tasks.rs
@@ -214,6 +214,9 @@ pub async fn npm_tarball_build_handler(
)
.await?;
+ db.process_webhooks_for_npm_tarball(&job.scope, &job.name, &job.version)
+ .await?;
+
Ok(())
}
@@ -548,6 +551,18 @@ async fn dispatch_webhook(
let url = ®istry_url.0;
match payload {
+ WebhookPayload::PackageVersionNpmTarballReady {
+ scope,
+ package,
+ version,
+ } => ProviderEmbed {
+ color: GREEN,
+ title: "Package version NPM tarball ready",
+ url: format!("{url}@{scope}/{package}/{version}"),
+ description: format!(
+ "NPM tarball for @{scope}/{package}/{version} is ready"
+ ),
+ },
WebhookPayload::PackageVersionPublished {
scope,
package,
@@ -740,8 +755,9 @@ async fn dispatch_webhook(
}
};
- let success = response.status().is_success();
- let response_http_status = response.status().as_u16() as i32;
+ let status = response.status();
+ let success = status.is_success();
+ let response_http_status = status.as_u16() as i32;
let response_headers =
serde_json::to_value(headers_to_map(response.headers()))?;
let response_body = response.text().await.map_err(anyhow::Error::from)?;
@@ -764,8 +780,7 @@ async fn dispatch_webhook(
.await?;
if !success {
- todo!("error");
- Ok(())
+ Err(ApiError::WebhookResponseFailure { status })
} else {
Ok(())
}
diff --git a/frontend/components/Help.tsx b/frontend/components/Help.tsx
new file mode 100644
index 000000000..588af0231
--- /dev/null
+++ b/frontend/components/Help.tsx
@@ -0,0 +1,11 @@
+// Copyright 2024 the JSR authors. All rights reserved. MIT license.
+
+import HelpIcon from "tb-icons/TbHelp";
+
+export function Help({ href }: { href: string }) {
+ return (
+
+
+
+ );
+}
diff --git a/frontend/docs/toc.ts b/frontend/docs/toc.ts
index 035922c3a..81083a48d 100644
--- a/frontend/docs/toc.ts
+++ b/frontend/docs/toc.ts
@@ -103,6 +103,11 @@ export default [
id: "trust",
group: "Reference",
},
+ {
+ title: "Webhooks",
+ id: "webhooks",
+ group: "Reference",
+ },
{
title: "Badges",
id: "badges",
diff --git a/frontend/docs/webhooks.md b/frontend/docs/webhooks.md
new file mode 100644
index 000000000..d33c14bd5
--- /dev/null
+++ b/frontend/docs/webhooks.md
@@ -0,0 +1,216 @@
+---
+title: Webhooks
+description: Webhooks allow you to receive HTTP notifications when events occur in your scope or packages.
+---
+
+Webhooks allow you to receive real-time HTTP notifications when events occur in
+your JSR scope or packages. You can use webhooks to trigger CI/CD pipelines,
+send notifications to chat services, or integrate with other tools.
+
+## Creating a webhook
+
+Webhooks can be created at two levels:
+
+- **Scope-level webhooks**: Receive notifications for all events in a scope and
+ its packages.
+- **Package-level webhooks**: Receive notifications only for events related to a
+ specific package.
+
+To create a webhook:
+
+1. Navigate to your scope or package settings
+2. Go to the "Webhooks" section
+3. Click "Create webhook"
+4. Configure the webhook URL, events, and optional secret
+
+## Events
+
+### Package events
+
+These events are triggered for specific packages. For scope-level webhooks,
+these events are triggered for all packages in the scope.
+
+| Event | Description |
+| ----------------------------------- | ------------------------------------------------- |
+| `package_version_published` | A new version of a package was published |
+| `package_version_yanked` | A package version was yanked or unyanked |
+| `package_version_deleted` | A package version was deleted |
+| `package_version_npm_tarball_ready` | The npm-compatible tarball for a version is ready |
+
+### Scope events
+
+These events are triggered at the scope level and are only available for
+scope-level webhooks.
+
+| Event | Description |
+| ------------------------ | -------------------------------------- |
+| `scope_package_created` | A new package was created in the scope |
+| `scope_package_deleted` | A package was deleted from the scope |
+| `scope_package_archived` | A package was archived or unarchived |
+| `scope_member_added` | A new member was added to the scope |
+| `scope_member_removed` | A member was removed from the scope |
+
+## Payload format
+
+Webhooks support three payload formats:
+
+- **JSON**: Standard JSON payload
+- **Discord**: Pre-formatted for Discord webhook endpoints
+- **Slack**: Pre-formatted for Slack webhook endpoints
+
+### JSON payloads
+
+All JSON payloads include an `event` field that identifies the event type. The
+remaining fields vary by event.
+
+#### `package_version_published`
+
+```json
+{
+ "event": "package_version_published",
+ "scope": "myorg",
+ "package": "mylib",
+ "version": "1.0.0",
+ "user_id": "550e8400-e29b-41d4-a716-446655440000"
+}
+```
+
+The `user_id` field contains the UUID of the user who published the version, or
+`null` if published via CI without user context.
+
+#### `package_version_yanked`
+
+```json
+{
+ "event": "package_version_yanked",
+ "scope": "myorg",
+ "package": "mylib",
+ "version": "1.0.0",
+ "yanked": true
+}
+```
+
+The `yanked` field is `true` when a version is yanked, and `false` when a
+version is unyanked.
+
+#### `package_version_deleted`
+
+```json
+{
+ "event": "package_version_deleted",
+ "scope": "myorg",
+ "package": "mylib",
+ "version": "1.0.0"
+}
+```
+
+#### `package_version_npm_tarball_ready`
+
+```json
+{
+ "event": "package_version_npm_tarball_ready",
+ "scope": "myorg",
+ "package": "mylib",
+ "version": "1.0.0"
+}
+```
+
+This event is triggered after a version is published and the npm-compatible
+tarball has been built. Use this event if you need to wait for npm compatibility
+before taking action.
+
+#### `scope_package_created`
+
+```json
+{
+ "event": "scope_package_created",
+ "scope": "myorg",
+ "package": "mylib"
+}
+```
+
+#### `scope_package_deleted`
+
+```json
+{
+ "event": "scope_package_deleted",
+ "scope": "myorg",
+ "package": "mylib"
+}
+```
+
+#### `scope_package_archived`
+
+```json
+{
+ "event": "scope_package_archived",
+ "scope": "myorg",
+ "package": "mylib",
+ "archived": true
+}
+```
+
+The `archived` field is `true` when a package is archived, and `false` when a
+package is unarchived.
+
+#### `scope_member_added`
+
+```json
+{
+ "event": "scope_member_added",
+ "scope": "myorg",
+ "user_id": "550e8400-e29b-41d4-a716-446655440000"
+}
+```
+
+#### `scope_member_removed`
+
+```json
+{
+ "event": "scope_member_removed",
+ "scope": "myorg",
+ "user_id": "550e8400-e29b-41d4-a716-446655440000"
+}
+```
+
+## HTTP headers
+
+Each webhook request includes the following HTTP headers:
+
+| Header | Description |
+| ----------------- | ----------------------------------------------------- |
+| `X-JSR-Event` | The event type (e.g., `"package_version_published"`) |
+| `X-JSR-Event-Id` | Unique identifier for this event |
+| `X-JSR-Signature` | HMAC signature of the request body (if secret is set) |
+
+## Secrets and signature verification
+
+Webhook secrets allow you to verify that incoming webhook requests genuinely
+originate from JSR and have not been tampered with in transit.
+
+### How secrets work
+
+When you configure a secret for your webhook:
+
+1. JSR computes an HMAC-SHA256 signature of the request body using your secret
+2. The signature is included in the `X-JSR-Signature` header
+3. Your server can verify the signature to authenticate the request
+
+The signature format is: `sha256=`
+
+### Verifying signatures
+
+To verify a webhook signature:
+
+1. Extract the signature from the `X-JSR-Signature` header
+2. Compute an HMAC-SHA256 hash of the raw request body using your secret
+3. Compare the computed hash with the signature from the header
+
+## Delivery and retries
+
+JSR delivers webhooks with the following behavior:
+
+- Webhooks are delivered asynchronously after events occur
+- Failed deliveries (non-2xx responses) are retried up to 3 times
+- You can view delivery history and debug failed deliveries in the webhook
+ settings
diff --git a/frontend/islands/WebhookEdit.tsx b/frontend/islands/WebhookEdit.tsx
index 2dfb587d1..fdb479ea3 100644
--- a/frontend/islands/WebhookEdit.tsx
+++ b/frontend/islands/WebhookEdit.tsx
@@ -2,6 +2,7 @@
import { WebhookEndpoint, WebhookEventKind } from "../utils/api_types.ts";
import { useSignal } from "@preact/signals";
import { api, path } from "../utils/api.ts";
+import { Help } from "../components/Help.tsx";
export const WEBHOOK_EVENTS: Array<{
id: WebhookEventKind;
@@ -9,6 +10,12 @@ export const WEBHOOK_EVENTS: Array<{
description: string;
packageLevel: boolean;
}> = [
+ {
+ id: "package_version_npm_tarball_ready",
+ name: "Package version NPM tarball ready",
+ description: "A NPM tarball for a published version is available.",
+ packageLevel: true,
+ },
{
id: "package_version_published",
name: "Package version published",
@@ -169,6 +176,9 @@ export function WebhookEdit(
className="input-container input select w-full max-w-lg block px-3 py-2 text-sm mt-3"
required
disabled={processing}
+ onChange={(e) =>
+ payloadFormat.value = e.currentTarget
+ .value as WebhookEndpoint["payloadFormat"]}
>
JSON
@@ -188,7 +198,10 @@ export function WebhookEdit(
- Secret
+
+ Secret{" "}
+
+
{webhook?.hasSecret && (
secret.value === null
? (
diff --git a/frontend/routes/@[scope]/~/settings/index.tsx b/frontend/routes/@[scope]/~/settings/index.tsx
index 9c75a79db..04a49aa3b 100644
--- a/frontend/routes/@[scope]/~/settings/index.tsx
+++ b/frontend/routes/@[scope]/~/settings/index.tsx
@@ -17,6 +17,7 @@ import { QuotaCard } from "../../../../components/QuotaCard.tsx";
import { scopeIAM } from "../../../../utils/iam.ts";
import { TicketModal } from "../../../../islands/TicketModal.tsx";
import { ListDisplay } from "../../../../components/List.tsx";
+import { Help } from "../../../../components/Help.tsx";
export default define.page(function ScopeSettingsPage(
{ data, state },
@@ -233,7 +234,9 @@ function Webhooks(
) {
return (
-
Webhooks
+
+ Webhooks
+
Webhooks let you receive notifications when packages are published or
other events happen in the scope.
diff --git a/frontend/routes/package/settings/index.tsx b/frontend/routes/package/settings/index.tsx
index fcca4dfe9..02ddcbe75 100644
--- a/frontend/routes/package/settings/index.tsx
+++ b/frontend/routes/package/settings/index.tsx
@@ -15,6 +15,7 @@ import { PackageDescriptionEditor } from "../(_islands)/PackageDescriptionEditor
import { RUNTIME_COMPAT_KEYS } from "../../../components/RuntimeCompatIndicator.tsx";
import { scopeIAM } from "../../../utils/iam.ts";
import { ListDisplay } from "../../../components/List.tsx";
+import { Help } from "../../../components/Help.tsx";
export default define.page(
function Settings({ data, params }) {
@@ -326,7 +327,9 @@ function FeaturePackage(props: { package: Package }) {
function Webhooks({ webhooks }: { webhooks: WebhookEndpoint[] }) {
return (
-
Webhooks
+
+ Webhooks
+
Webhooks let you receive notifications when packages are published or
other events happen in the scope.
diff --git a/frontend/utils/api_types.ts b/frontend/utils/api_types.ts
index 41a3ebb6e..1a2ab5da4 100644
--- a/frontend/utils/api_types.ts
+++ b/frontend/utils/api_types.ts
@@ -403,6 +403,7 @@ export interface PackageDownloadsRecentVersion {
}
export type WebhookEventKind =
+ | "package_version_npm_tarball_ready"
| "package_version_published"
| "package_version_yanked"
| "package_version_deleted"
From 05c7eb44c82f27f81e0a0679b11e9ba2aa437221 Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Sun, 1 Feb 2026 15:04:12 +0100
Subject: [PATCH 12/17] fixes
---
frontend/docs/webhooks.md | 9 ++++-----
frontend/islands/WebhookEdit.tsx | 3 ++-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/frontend/docs/webhooks.md b/frontend/docs/webhooks.md
index d33c14bd5..4efd4556b 100644
--- a/frontend/docs/webhooks.md
+++ b/frontend/docs/webhooks.md
@@ -190,11 +190,10 @@ originate from JSR and have not been tampered with in transit.
### How secrets work
-When you configure a secret for your webhook:
-
-1. JSR computes an HMAC-SHA256 signature of the request body using your secret
-2. The signature is included in the `X-JSR-Signature` header
-3. Your server can verify the signature to authenticate the request
+When you configure a secret for your webhook, JSR computes an HMAC-SHA256
+signature of the request body using your secret which in the `X-JSR-Signature`
+header. Your server can verify this signature to validate that the request is
+genuine.
The signature format is: `sha256=`
diff --git a/frontend/islands/WebhookEdit.tsx b/frontend/islands/WebhookEdit.tsx
index fdb479ea3..656141860 100644
--- a/frontend/islands/WebhookEdit.tsx
+++ b/frontend/islands/WebhookEdit.tsx
@@ -169,7 +169,8 @@ export function WebhookEdit(
- Payload format
+ Payload format {" "}
+
Date: Sun, 1 Feb 2026 16:22:10 +0100
Subject: [PATCH 13/17] fixes
---
.../package/settings/webhooks/[delivery].tsx | 89 +++++++++++++++++++
.../package/settings/webhooks/[webhook].tsx | 22 ++++-
2 files changed, 108 insertions(+), 3 deletions(-)
create mode 100644 frontend/routes/package/settings/webhooks/[delivery].tsx
diff --git a/frontend/routes/package/settings/webhooks/[delivery].tsx b/frontend/routes/package/settings/webhooks/[delivery].tsx
new file mode 100644
index 000000000..00f24e2cf
--- /dev/null
+++ b/frontend/routes/package/settings/webhooks/[delivery].tsx
@@ -0,0 +1,89 @@
+// Copyright 2024 the JSR authors. All rights reserved. MIT license.
+import { HttpError, RouteConfig } from "fresh";
+import { define } from "../../../../util.ts";
+import { PackageHeader } from "../../(_components)/PackageHeader.tsx";
+import { PackageNav, Params } from "../../(_components)/PackageNav.tsx";
+import type {
+ WebhookDelivery as ApiWebhookDelivery,
+ WebhookEndpoint,
+} from "../../../../utils/api_types.ts";
+import { packageData } from "../../../../utils/data.ts";
+import { path } from "../../../../utils/api.ts";
+import { scopeIAM } from "../../../../utils/iam.ts";
+import { WebhookDelivery } from "../../../../components/WebhookDelivery.tsx";
+
+export default define.page(function ScopeSettingsPage(
+ { data, params },
+) {
+ return (
+
+ );
+});
+
+export const handler = define.handlers({
+ async GET(ctx) {
+ const [user, data, webhookResp, webhookDeliveryResp] = await Promise.all([
+ ctx.state.userPromise,
+ packageData(ctx.state, ctx.params.scope, ctx.params.package),
+ ctx.state.api.get(
+ path`/scopes/${ctx.params.scope}/packages/${ctx.params.package}/webhooks/${ctx.params.webhook}`,
+ ),
+ ctx.state.api.get(
+ path`/scopes/${ctx.params.scope}/packages/${ctx.params.package}/webhooks/${ctx.params.webhook}/deliveries/${ctx.params.delivery}`,
+ ),
+ ]);
+ if (user instanceof Response) return user;
+ if (data === null) throw new HttpError(404, "The scope was not found.");
+
+ const { pkg, scopeMember, downloads } = data;
+ const iam = scopeIAM(ctx.state, scopeMember, user);
+ if (!iam.canAdmin) throw new HttpError(404, "This package was not found.");
+
+ if (!webhookResp.ok) {
+ if (webhookResp.code === "webhookNotFound") {
+ throw new HttpError(404, "The webhook was not found.");
+ }
+ throw webhookResp; // graceful handle errors
+ }
+ if (!webhookDeliveryResp.ok) {
+ if (webhookDeliveryResp.code === "webhookNotFound") {
+ throw new HttpError(404, "The webhook was not found.");
+ }
+ throw webhookDeliveryResp; // graceful handle errors
+ }
+
+ ctx.state.meta = {
+ title: `Webhook Settings - @${pkg.scope}/${pkg.name} - JSR`,
+ };
+ return {
+ data: {
+ package: pkg,
+ downloads,
+ webhook: webhookResp.data,
+ webhookDelivery: webhookDeliveryResp.data,
+ iam,
+ },
+ };
+ },
+});
+
+export const config: RouteConfig = {
+ routeOverride: "/@:scope/:package/settings/webhooks/:webhook/deliveries/:delivery",
+};
diff --git a/frontend/routes/package/settings/webhooks/[webhook].tsx b/frontend/routes/package/settings/webhooks/[webhook].tsx
index 143d81143..7e77333c7 100644
--- a/frontend/routes/package/settings/webhooks/[webhook].tsx
+++ b/frontend/routes/package/settings/webhooks/[webhook].tsx
@@ -2,7 +2,10 @@
import { HttpError, RouteConfig } from "fresh";
import { define } from "../../../../util.ts";
import { WebhookEdit } from "../../../../islands/WebhookEdit.tsx";
-import { WebhookEndpoint } from "../../../../utils/api_types.ts";
+import {
+ WebhookDelivery,
+ WebhookEndpoint,
+} from "../../../../utils/api_types.ts";
import { packageData } from "../../../../utils/data.ts";
import { path } from "../../../../utils/api.ts";
import { scopeIAM } from "../../../../utils/iam.ts";
@@ -38,19 +41,25 @@ export default define.page(function ScopeSettingsPage(
package={data.package.name}
/>
-
+
);
});
export const handler = define.handlers({
async GET(ctx) {
- const [user, data, webhookResp] = await Promise.all([
+ const [user, data, webhookResp, webhookDeliveriesResp] = await Promise.all([
ctx.state.userPromise,
packageData(ctx.state, ctx.params.scope, ctx.params.package),
ctx.state.api.get(
path`/scopes/${ctx.params.scope}/packages/${ctx.params.package}/webhooks/${ctx.params.webhook}`,
),
+ ctx.state.api.get(
+ path`/scopes/${ctx.params.scope}/webhooks/${ctx.params.webhook}/deliveries`,
+ ),
]);
if (user instanceof Response) return user;
if (data === null) throw new HttpError(404, "The scope was not found.");
@@ -65,6 +74,12 @@ export const handler = define.handlers({
}
throw webhookResp; // graceful handle errors
}
+ if (!webhookDeliveriesResp.ok) {
+ if (webhookDeliveriesResp.code === "webhookNotFound") {
+ throw new HttpError(404, "The webhook was not found.");
+ }
+ throw webhookDeliveriesResp; // graceful handle errors
+ }
ctx.state.meta = {
title: `Webhook Settings - @${pkg.scope}/${pkg.name} - JSR`,
@@ -79,6 +94,7 @@ export const handler = define.handlers({
downloads,
iam,
webhook: webhookResp.data,
+ webhookDeliveries: webhookDeliveriesResp.data,
},
};
},
From 80aad4981640842225fc6272da4d90a348bbd432 Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Sun, 1 Feb 2026 16:27:00 +0100
Subject: [PATCH 14/17] fmt
---
frontend/routes/package/settings/webhooks/[delivery].tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/frontend/routes/package/settings/webhooks/[delivery].tsx b/frontend/routes/package/settings/webhooks/[delivery].tsx
index 00f24e2cf..fd55f32b9 100644
--- a/frontend/routes/package/settings/webhooks/[delivery].tsx
+++ b/frontend/routes/package/settings/webhooks/[delivery].tsx
@@ -85,5 +85,6 @@ export const handler = define.handlers({
});
export const config: RouteConfig = {
- routeOverride: "/@:scope/:package/settings/webhooks/:webhook/deliveries/:delivery",
+ routeOverride:
+ "/@:scope/:package/settings/webhooks/:webhook/deliveries/:delivery",
};
From a474e53257aaf9dcd7414c8a33cc3e187f9f9c1d Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Thu, 5 Feb 2026 12:33:41 +0100
Subject: [PATCH 15/17] sqlx
---
...1897f68b0462ad93a29ce20bb652bbbb83420.json | 99 ---------------
...b8dfe51e9edc309c346baaea94470cace57af.json | 76 ------------
...d6c41dc7080e695249ff4a5a415df05139ece.json | 94 ---------------
...03f8903efbb5467a9f5ddcf09564ebaf3368f.json | 1 +
...46082c2ef7e1ef2b6694ce6e64a7859da7ff9.json | 1 +
...8b93a12da06bb610173c1e39f9997f97e9610.json | 48 --------
...a43b7d34ab53ad7bc638460faa748dfaa9b8f.json | 113 ------------------
...2a35c41672736f94acea3afe9ad369b9f53b5.json | 69 -----------
...d27ee4ced0cd03477ae28bb5d771bc7b5f42a.json | 1 +
...2dd858c5b403cd4315ad0de5706e1ad5f0166.json | 1 +
...e608dbd3b65e43086a172151e0eba0ef1ffff.json | 1 +
...c8e4d983080a59b6105cf43ffa3c42c8cef0a.json | 2 +
...423231bb548d6558610b649455941a5e0b135.json | 1 +
...afabc2574dae3f240befb41e4fb4e74280528.json | 14 ---
...4e912aaccf5f610465408f9a49ce925b838f9.json | 1 +
...73dea89d8e91de9054f48cee1471cca07bedf.json | 2 +
16 files changed, 11 insertions(+), 513 deletions(-)
delete mode 100644 api/.sqlx/query-050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420.json
delete mode 100644 api/.sqlx/query-0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af.json
delete mode 100644 api/.sqlx/query-0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece.json
delete mode 100644 api/.sqlx/query-35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610.json
delete mode 100644 api/.sqlx/query-46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f.json
delete mode 100644 api/.sqlx/query-51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5.json
delete mode 100644 api/.sqlx/query-af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528.json
diff --git a/api/.sqlx/query-050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420.json b/api/.sqlx/query-050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420.json
deleted file mode 100644
index b9c4edadc..000000000
--- a/api/.sqlx/query-050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420.json
+++ /dev/null
@@ -1,99 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO users (name, email, avatar_url, github_id, is_blocked, is_staff)\n VALUES ($1, $2, $3, $4, $5, $6)\n RETURNING id, name, email, avatar_url, updated_at, created_at, github_id, is_blocked, is_staff, scope_limit,\n (SELECT COUNT(created_at) FROM scope_invites WHERE target_user_id = id) as \"invite_count!\",\n (SELECT COUNT(created_at) FROM scopes WHERE creator = id) as \"scope_usage!\",\n (CASE WHEN users.is_staff THEN (\n SELECT count(tickets.created_at) FROM tickets WHERE closed = false AND EXISTS (\n SELECT 1 FROM ticket_messages as tm WHERE tm.ticket_id = tickets.id AND tm.author = tickets.creator AND tm.created_at = (\n SELECT MAX(ticket_messages.created_at) FROM ticket_messages WHERE ticket_messages.ticket_id = tickets.id\n )\n )\n ) ELSE (\n SELECT COUNT(created_at) FROM tickets WHERE closed = false AND tickets.creator = users.id AND EXISTS (\n SELECT 1 FROM ticket_messages as tm WHERE tm.ticket_id = tickets.id AND tm.author != users.id AND tm.created_at > (\n SELECT MAX(tm2.created_at) FROM ticket_messages as tm2 WHERE tm2.ticket_id = tm.ticket_id AND tm2.author = users.id\n )\n )\n ) END) as \"newer_ticket_messages_count!\"\n ",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "id",
- "type_info": "Uuid"
- },
- {
- "ordinal": 1,
- "name": "name",
- "type_info": "Text"
- },
- {
- "ordinal": 2,
- "name": "email",
- "type_info": "Varchar"
- },
- {
- "ordinal": 3,
- "name": "avatar_url",
- "type_info": "Text"
- },
- {
- "ordinal": 4,
- "name": "updated_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 5,
- "name": "created_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 6,
- "name": "github_id",
- "type_info": "Int8"
- },
- {
- "ordinal": 7,
- "name": "is_blocked",
- "type_info": "Bool"
- },
- {
- "ordinal": 8,
- "name": "is_staff",
- "type_info": "Bool"
- },
- {
- "ordinal": 9,
- "name": "scope_limit",
- "type_info": "Int4"
- },
- {
- "ordinal": 10,
- "name": "invite_count!",
- "type_info": "Int8"
- },
- {
- "ordinal": 11,
- "name": "scope_usage!",
- "type_info": "Int8"
- },
- {
- "ordinal": 12,
- "name": "newer_ticket_messages_count!",
- "type_info": "Int8"
- }
- ],
- "parameters": {
- "Left": [
- "Text",
- "Varchar",
- "Text",
- "Int8",
- "Bool",
- "Bool"
- ]
- },
- "nullable": [
- false,
- false,
- true,
- false,
- false,
- false,
- true,
- false,
- false,
- false,
- null,
- null,
- null
- ]
- },
- "hash": "050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420"
-}
diff --git a/api/.sqlx/query-0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af.json b/api/.sqlx/query-0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af.json
deleted file mode 100644
index 902544274..000000000
--- a/api/.sqlx/query-0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af.json
+++ /dev/null
@@ -1,76 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "SELECT\n scope as \"scope: ScopeName\",\n description as \"description: ScopeDescription\",\n creator,\n package_limit,\n new_package_per_week_limit,\n publish_attempts_per_week_limit,\n verify_oidc_actor,\n require_publishing_from_ci,\n updated_at,\n created_at\n FROM scopes WHERE creator = $1\n ORDER BY scope ASC",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "scope: ScopeName",
- "type_info": "Text"
- },
- {
- "ordinal": 1,
- "name": "description: ScopeDescription",
- "type_info": "Text"
- },
- {
- "ordinal": 2,
- "name": "creator",
- "type_info": "Uuid"
- },
- {
- "ordinal": 3,
- "name": "package_limit",
- "type_info": "Int4"
- },
- {
- "ordinal": 4,
- "name": "new_package_per_week_limit",
- "type_info": "Int4"
- },
- {
- "ordinal": 5,
- "name": "publish_attempts_per_week_limit",
- "type_info": "Int4"
- },
- {
- "ordinal": 6,
- "name": "verify_oidc_actor",
- "type_info": "Bool"
- },
- {
- "ordinal": 7,
- "name": "require_publishing_from_ci",
- "type_info": "Bool"
- },
- {
- "ordinal": 8,
- "name": "updated_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 9,
- "name": "created_at",
- "type_info": "Timestamptz"
- }
- ],
- "parameters": {
- "Left": [
- "Uuid"
- ]
- },
- "nullable": [
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false
- ]
- },
- "hash": "0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af"
-}
diff --git a/api/.sqlx/query-0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece.json b/api/.sqlx/query-0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece.json
deleted file mode 100644
index de37c9f85..000000000
--- a/api/.sqlx/query-0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece.json
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "DELETE FROM users\n WHERE id = $1\n RETURNING id, name, email, avatar_url, updated_at, created_at, github_id, is_blocked, is_staff, scope_limit,\n (SELECT COUNT(created_at) FROM scope_invites WHERE target_user_id = id) as \"invite_count!\",\n (SELECT COUNT(created_at) FROM scopes WHERE creator = id) as \"scope_usage!\",\n (CASE WHEN users.is_staff THEN (\n SELECT count(tickets.created_at) FROM tickets WHERE closed = false AND EXISTS (\n SELECT 1 FROM ticket_messages as tm WHERE tm.ticket_id = tickets.id AND tm.author = tickets.creator AND tm.created_at = (\n SELECT MAX(ticket_messages.created_at) FROM ticket_messages WHERE ticket_messages.ticket_id = tickets.id\n )\n )\n ) ELSE (\n SELECT COUNT(created_at) FROM tickets WHERE closed = false AND tickets.creator = users.id AND EXISTS (\n SELECT 1 FROM ticket_messages as tm WHERE tm.ticket_id = tickets.id AND tm.author != users.id AND tm.created_at > (\n SELECT MAX(tm2.created_at) FROM ticket_messages as tm2 WHERE tm2.ticket_id = tm.ticket_id AND tm2.author = users.id\n )\n )\n ) END) as \"newer_ticket_messages_count!\"\n ",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "id",
- "type_info": "Uuid"
- },
- {
- "ordinal": 1,
- "name": "name",
- "type_info": "Text"
- },
- {
- "ordinal": 2,
- "name": "email",
- "type_info": "Varchar"
- },
- {
- "ordinal": 3,
- "name": "avatar_url",
- "type_info": "Text"
- },
- {
- "ordinal": 4,
- "name": "updated_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 5,
- "name": "created_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 6,
- "name": "github_id",
- "type_info": "Int8"
- },
- {
- "ordinal": 7,
- "name": "is_blocked",
- "type_info": "Bool"
- },
- {
- "ordinal": 8,
- "name": "is_staff",
- "type_info": "Bool"
- },
- {
- "ordinal": 9,
- "name": "scope_limit",
- "type_info": "Int4"
- },
- {
- "ordinal": 10,
- "name": "invite_count!",
- "type_info": "Int8"
- },
- {
- "ordinal": 11,
- "name": "scope_usage!",
- "type_info": "Int8"
- },
- {
- "ordinal": 12,
- "name": "newer_ticket_messages_count!",
- "type_info": "Int8"
- }
- ],
- "parameters": {
- "Left": [
- "Uuid"
- ]
- },
- "nullable": [
- false,
- false,
- true,
- false,
- false,
- false,
- true,
- false,
- false,
- false,
- null,
- null,
- null
- ]
- },
- "hash": "0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece"
-}
diff --git a/api/.sqlx/query-10136f30939a45abc06f1d1aba703f8903efbb5467a9f5ddcf09564ebaf3368f.json b/api/.sqlx/query-10136f30939a45abc06f1d1aba703f8903efbb5467a9f5ddcf09564ebaf3368f.json
index 138b535cb..23906f801 100644
--- a/api/.sqlx/query-10136f30939a45abc06f1d1aba703f8903efbb5467a9f5ddcf09564ebaf3368f.json
+++ b/api/.sqlx/query-10136f30939a45abc06f1d1aba703f8903efbb5467a9f5ddcf09564ebaf3368f.json
@@ -45,6 +45,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
diff --git a/api/.sqlx/query-2741c92898bfa9147c1ec3a23f346082c2ef7e1ef2b6694ce6e64a7859da7ff9.json b/api/.sqlx/query-2741c92898bfa9147c1ec3a23f346082c2ef7e1ef2b6694ce6e64a7859da7ff9.json
index 71ca3f3c1..7235ec9ab 100644
--- a/api/.sqlx/query-2741c92898bfa9147c1ec3a23f346082c2ef7e1ef2b6694ce6e64a7859da7ff9.json
+++ b/api/.sqlx/query-2741c92898bfa9147c1ec3a23f346082c2ef7e1ef2b6694ce6e64a7859da7ff9.json
@@ -18,6 +18,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
diff --git a/api/.sqlx/query-35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610.json b/api/.sqlx/query-35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610.json
deleted file mode 100644
index 8dd84463c..000000000
--- a/api/.sqlx/query-35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "\n SELECT time_bucket, kind as \"kind: DownloadKind\", count\n FROM version_download_counts_4h\n WHERE scope = $1 AND package = $2 AND version = $3 AND time_bucket >= $4 AND time_bucket < $5\n ORDER BY time_bucket ASC\n ",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "time_bucket",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 1,
- "name": "kind: DownloadKind",
- "type_info": {
- "Custom": {
- "name": "download_kind",
- "kind": {
- "Enum": [
- "npm_tgz",
- "jsr_meta"
- ]
- }
- }
- }
- },
- {
- "ordinal": 2,
- "name": "count",
- "type_info": "Int4"
- }
- ],
- "parameters": {
- "Left": [
- "Text",
- "Text",
- "Text",
- "Timestamptz",
- "Timestamptz"
- ]
- },
- "nullable": [
- false,
- false,
- false
- ]
- },
- "hash": "35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610"
-}
diff --git a/api/.sqlx/query-46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f.json b/api/.sqlx/query-46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f.json
deleted file mode 100644
index 5ea339b0a..000000000
--- a/api/.sqlx/query-46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f.json
+++ /dev/null
@@ -1,113 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO package_versions (scope, name, version, user_id, readme_path, exports, uses_npm, meta)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n RETURNING scope as \"scope: ScopeName\", name as \"name: PackageName\", version as \"version: Version\", user_id, readme_path as \"readme_path: PackagePath\", exports as \"exports: ExportsMap\", is_yanked, uses_npm, meta as \"meta: PackageVersionMeta\", updated_at, created_at, rekor_log_id, license,\n (SELECT COUNT(*)\n FROM package_versions AS pv\n WHERE pv.scope = package_versions.scope\n AND pv.name = package_versions.name\n AND pv.version > package_versions.version\n AND pv.version NOT LIKE '%-%'\n AND pv.is_yanked = false) as \"newer_versions_count!\",\n (SELECT COALESCE(SUM(dl.count), 0)\n FROM version_download_counts_24h as dl\n WHERE dl.scope = package_versions.scope\n AND dl.package = package_versions.name\n AND dl.version = package_versions.version) as \"lifetime_download_count!\"",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "scope: ScopeName",
- "type_info": "Text"
- },
- {
- "ordinal": 1,
- "name": "name: PackageName",
- "type_info": "Text"
- },
- {
- "ordinal": 2,
- "name": "version: Version",
- "type_info": "Text"
- },
- {
- "ordinal": 3,
- "name": "user_id",
- "type_info": "Uuid"
- },
- {
- "ordinal": 4,
- "name": "readme_path: PackagePath",
- "type_info": "Text"
- },
- {
- "ordinal": 5,
- "name": "exports: ExportsMap",
- "type_info": "Jsonb"
- },
- {
- "ordinal": 6,
- "name": "is_yanked",
- "type_info": "Bool"
- },
- {
- "ordinal": 7,
- "name": "uses_npm",
- "type_info": "Bool"
- },
- {
- "ordinal": 8,
- "name": "meta: PackageVersionMeta",
- "type_info": "Jsonb"
- },
- {
- "ordinal": 9,
- "name": "updated_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 10,
- "name": "created_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 11,
- "name": "rekor_log_id",
- "type_info": "Text"
- },
- {
- "ordinal": 12,
- "name": "license",
- "type_info": "Text"
- },
- {
- "ordinal": 13,
- "name": "newer_versions_count!",
- "type_info": "Int8"
- },
- {
- "ordinal": 14,
- "name": "lifetime_download_count!",
- "type_info": "Int8"
- }
- ],
- "parameters": {
- "Left": [
- "Text",
- "Text",
- "Text",
- "Uuid",
- "Text",
- "Jsonb",
- "Bool",
- "Jsonb"
- ]
- },
- "nullable": [
- false,
- false,
- false,
- true,
- true,
- false,
- false,
- false,
- false,
- false,
- false,
- true,
- true,
- null,
- null
- ]
- },
- "hash": "46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f"
-}
diff --git a/api/.sqlx/query-51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5.json b/api/.sqlx/query-51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5.json
deleted file mode 100644
index bd69526a4..000000000
--- a/api/.sqlx/query-51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5.json
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO package_files (scope, name, version, path, size, checksum)\n VALUES ($1, $2, $3, $4, $5, $6)\n RETURNING scope as \"scope: ScopeName\", name as \"name: PackageName\", version as \"version: Version\", path as \"path: PackagePath\", size, checksum, updated_at, created_at",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "scope: ScopeName",
- "type_info": "Text"
- },
- {
- "ordinal": 1,
- "name": "name: PackageName",
- "type_info": "Text"
- },
- {
- "ordinal": 2,
- "name": "version: Version",
- "type_info": "Text"
- },
- {
- "ordinal": 3,
- "name": "path: PackagePath",
- "type_info": "Text"
- },
- {
- "ordinal": 4,
- "name": "size",
- "type_info": "Int4"
- },
- {
- "ordinal": 5,
- "name": "checksum",
- "type_info": "Text"
- },
- {
- "ordinal": 6,
- "name": "updated_at",
- "type_info": "Timestamptz"
- },
- {
- "ordinal": 7,
- "name": "created_at",
- "type_info": "Timestamptz"
- }
- ],
- "parameters": {
- "Left": [
- "Text",
- "Text",
- "Text",
- "Text",
- "Int4",
- "Text"
- ]
- },
- "nullable": [
- false,
- false,
- false,
- false,
- false,
- true,
- false,
- false
- ]
- },
- "hash": "51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5"
-}
diff --git a/api/.sqlx/query-5e1e0196eefc009cc3c400e84e7d27ee4ced0cd03477ae28bb5d771bc7b5f42a.json b/api/.sqlx/query-5e1e0196eefc009cc3c400e84e7d27ee4ced0cd03477ae28bb5d771bc7b5f42a.json
index bf3ce600a..282e43361 100644
--- a/api/.sqlx/query-5e1e0196eefc009cc3c400e84e7d27ee4ced0cd03477ae28bb5d771bc7b5f42a.json
+++ b/api/.sqlx/query-5e1e0196eefc009cc3c400e84e7d27ee4ced0cd03477ae28bb5d771bc7b5f42a.json
@@ -98,6 +98,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
diff --git a/api/.sqlx/query-7f3c9b05c2366415a18b97f1a3c2dd858c5b403cd4315ad0de5706e1ad5f0166.json b/api/.sqlx/query-7f3c9b05c2366415a18b97f1a3c2dd858c5b403cd4315ad0de5706e1ad5f0166.json
index 0ff6302f4..6948175ee 100644
--- a/api/.sqlx/query-7f3c9b05c2366415a18b97f1a3c2dd858c5b403cd4315ad0de5706e1ad5f0166.json
+++ b/api/.sqlx/query-7f3c9b05c2366415a18b97f1a3c2dd858c5b403cd4315ad0de5706e1ad5f0166.json
@@ -45,6 +45,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
diff --git a/api/.sqlx/query-8197bee3c5fe91b17dcfa40a7f5e608dbd3b65e43086a172151e0eba0ef1ffff.json b/api/.sqlx/query-8197bee3c5fe91b17dcfa40a7f5e608dbd3b65e43086a172151e0eba0ef1ffff.json
index 406af33b4..48a88872c 100644
--- a/api/.sqlx/query-8197bee3c5fe91b17dcfa40a7f5e608dbd3b65e43086a172151e0eba0ef1ffff.json
+++ b/api/.sqlx/query-8197bee3c5fe91b17dcfa40a7f5e608dbd3b65e43086a172151e0eba0ef1ffff.json
@@ -19,6 +19,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
diff --git a/api/.sqlx/query-a8e8bfc9567dfeacaf3da1bc354c8e4d983080a59b6105cf43ffa3c42c8cef0a.json b/api/.sqlx/query-a8e8bfc9567dfeacaf3da1bc354c8e4d983080a59b6105cf43ffa3c42c8cef0a.json
index 1cee4896e..516392d58 100644
--- a/api/.sqlx/query-a8e8bfc9567dfeacaf3da1bc354c8e4d983080a59b6105cf43ffa3c42c8cef0a.json
+++ b/api/.sqlx/query-a8e8bfc9567dfeacaf3da1bc354c8e4d983080a59b6105cf43ffa3c42c8cef0a.json
@@ -45,6 +45,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
@@ -110,6 +111,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
diff --git a/api/.sqlx/query-a951281e9bd7c33e5068faeaab8423231bb548d6558610b649455941a5e0b135.json b/api/.sqlx/query-a951281e9bd7c33e5068faeaab8423231bb548d6558610b649455941a5e0b135.json
index ad121e188..e060d279e 100644
--- a/api/.sqlx/query-a951281e9bd7c33e5068faeaab8423231bb548d6558610b649455941a5e0b135.json
+++ b/api/.sqlx/query-a951281e9bd7c33e5068faeaab8423231bb548d6558610b649455941a5e0b135.json
@@ -16,6 +16,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
diff --git a/api/.sqlx/query-af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528.json b/api/.sqlx/query-af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528.json
deleted file mode 100644
index c1da7eb2b..000000000
--- a/api/.sqlx/query-af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO bad_words (word) VALUES ($1)",
- "describe": {
- "columns": [],
- "parameters": {
- "Left": [
- "Text"
- ]
- },
- "nullable": []
- },
- "hash": "af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528"
-}
diff --git a/api/.sqlx/query-cdf070f5a93e4748f7a166bf7094e912aaccf5f610465408f9a49ce925b838f9.json b/api/.sqlx/query-cdf070f5a93e4748f7a166bf7094e912aaccf5f610465408f9a49ce925b838f9.json
index 6f3b636fa..26ccb23fa 100644
--- a/api/.sqlx/query-cdf070f5a93e4748f7a166bf7094e912aaccf5f610465408f9a49ce925b838f9.json
+++ b/api/.sqlx/query-cdf070f5a93e4748f7a166bf7094e912aaccf5f610465408f9a49ce925b838f9.json
@@ -98,6 +98,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
diff --git a/api/.sqlx/query-dd4a183981823896391889dddbf73dea89d8e91de9054f48cee1471cca07bedf.json b/api/.sqlx/query-dd4a183981823896391889dddbf73dea89d8e91de9054f48cee1471cca07bedf.json
index 353e611c6..649ef2852 100644
--- a/api/.sqlx/query-dd4a183981823896391889dddbf73dea89d8e91de9054f48cee1471cca07bedf.json
+++ b/api/.sqlx/query-dd4a183981823896391889dddbf73dea89d8e91de9054f48cee1471cca07bedf.json
@@ -45,6 +45,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
@@ -109,6 +110,7 @@
"name": "webhook_event_kind",
"kind": {
"Enum": [
+ "package_version_npm_tarball_ready",
"package_version_published",
"package_version_yanked",
"package_version_deleted",
From 9d890e4af51bcd131e1079a9813344fdebd35edc Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Thu, 5 Feb 2026 12:43:57 +0100
Subject: [PATCH 16/17] fix sqlx again
---
...1897f68b0462ad93a29ce20bb652bbbb83420.json | 99 +++++++++++++++
...b8dfe51e9edc309c346baaea94470cace57af.json | 76 ++++++++++++
...d6c41dc7080e695249ff4a5a415df05139ece.json | 94 +++++++++++++++
...8b93a12da06bb610173c1e39f9997f97e9610.json | 48 ++++++++
...e9761bc495a34441ddf2a128fec6364be6a62.json | 48 ++++++++
...a43b7d34ab53ad7bc638460faa748dfaa9b8f.json | 113 ++++++++++++++++++
...2a35c41672736f94acea3afe9ad369b9f53b5.json | 69 +++++++++++
...afabc2574dae3f240befb41e4fb4e74280528.json | 14 +++
8 files changed, 561 insertions(+)
create mode 100644 api/.sqlx/query-050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420.json
create mode 100644 api/.sqlx/query-0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af.json
create mode 100644 api/.sqlx/query-0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece.json
create mode 100644 api/.sqlx/query-35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610.json
create mode 100644 api/.sqlx/query-3f118b22cfc900a7f6e3e483f97e9761bc495a34441ddf2a128fec6364be6a62.json
create mode 100644 api/.sqlx/query-46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f.json
create mode 100644 api/.sqlx/query-51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5.json
create mode 100644 api/.sqlx/query-af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528.json
diff --git a/api/.sqlx/query-050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420.json b/api/.sqlx/query-050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420.json
new file mode 100644
index 000000000..b9c4edadc
--- /dev/null
+++ b/api/.sqlx/query-050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420.json
@@ -0,0 +1,99 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO users (name, email, avatar_url, github_id, is_blocked, is_staff)\n VALUES ($1, $2, $3, $4, $5, $6)\n RETURNING id, name, email, avatar_url, updated_at, created_at, github_id, is_blocked, is_staff, scope_limit,\n (SELECT COUNT(created_at) FROM scope_invites WHERE target_user_id = id) as \"invite_count!\",\n (SELECT COUNT(created_at) FROM scopes WHERE creator = id) as \"scope_usage!\",\n (CASE WHEN users.is_staff THEN (\n SELECT count(tickets.created_at) FROM tickets WHERE closed = false AND EXISTS (\n SELECT 1 FROM ticket_messages as tm WHERE tm.ticket_id = tickets.id AND tm.author = tickets.creator AND tm.created_at = (\n SELECT MAX(ticket_messages.created_at) FROM ticket_messages WHERE ticket_messages.ticket_id = tickets.id\n )\n )\n ) ELSE (\n SELECT COUNT(created_at) FROM tickets WHERE closed = false AND tickets.creator = users.id AND EXISTS (\n SELECT 1 FROM ticket_messages as tm WHERE tm.ticket_id = tickets.id AND tm.author != users.id AND tm.created_at > (\n SELECT MAX(tm2.created_at) FROM ticket_messages as tm2 WHERE tm2.ticket_id = tm.ticket_id AND tm2.author = users.id\n )\n )\n ) END) as \"newer_ticket_messages_count!\"\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Uuid"
+ },
+ {
+ "ordinal": 1,
+ "name": "name",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 2,
+ "name": "email",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 3,
+ "name": "avatar_url",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 4,
+ "name": "updated_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 5,
+ "name": "created_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 6,
+ "name": "github_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 7,
+ "name": "is_blocked",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 8,
+ "name": "is_staff",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 9,
+ "name": "scope_limit",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 10,
+ "name": "invite_count!",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 11,
+ "name": "scope_usage!",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 12,
+ "name": "newer_ticket_messages_count!",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text",
+ "Varchar",
+ "Text",
+ "Int8",
+ "Bool",
+ "Bool"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ null,
+ null,
+ null
+ ]
+ },
+ "hash": "050b47e8c4f5300bdb04ae9f42a1897f68b0462ad93a29ce20bb652bbbb83420"
+}
diff --git a/api/.sqlx/query-0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af.json b/api/.sqlx/query-0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af.json
new file mode 100644
index 000000000..902544274
--- /dev/null
+++ b/api/.sqlx/query-0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af.json
@@ -0,0 +1,76 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT\n scope as \"scope: ScopeName\",\n description as \"description: ScopeDescription\",\n creator,\n package_limit,\n new_package_per_week_limit,\n publish_attempts_per_week_limit,\n verify_oidc_actor,\n require_publishing_from_ci,\n updated_at,\n created_at\n FROM scopes WHERE creator = $1\n ORDER BY scope ASC",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "scope: ScopeName",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 1,
+ "name": "description: ScopeDescription",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 2,
+ "name": "creator",
+ "type_info": "Uuid"
+ },
+ {
+ "ordinal": 3,
+ "name": "package_limit",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 4,
+ "name": "new_package_per_week_limit",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 5,
+ "name": "publish_attempts_per_week_limit",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 6,
+ "name": "verify_oidc_actor",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 7,
+ "name": "require_publishing_from_ci",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 8,
+ "name": "updated_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 9,
+ "name": "created_at",
+ "type_info": "Timestamptz"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Uuid"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "0ddb2cf66ee7e7d2c7341713b39b8dfe51e9edc309c346baaea94470cace57af"
+}
diff --git a/api/.sqlx/query-0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece.json b/api/.sqlx/query-0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece.json
new file mode 100644
index 000000000..de37c9f85
--- /dev/null
+++ b/api/.sqlx/query-0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece.json
@@ -0,0 +1,94 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "DELETE FROM users\n WHERE id = $1\n RETURNING id, name, email, avatar_url, updated_at, created_at, github_id, is_blocked, is_staff, scope_limit,\n (SELECT COUNT(created_at) FROM scope_invites WHERE target_user_id = id) as \"invite_count!\",\n (SELECT COUNT(created_at) FROM scopes WHERE creator = id) as \"scope_usage!\",\n (CASE WHEN users.is_staff THEN (\n SELECT count(tickets.created_at) FROM tickets WHERE closed = false AND EXISTS (\n SELECT 1 FROM ticket_messages as tm WHERE tm.ticket_id = tickets.id AND tm.author = tickets.creator AND tm.created_at = (\n SELECT MAX(ticket_messages.created_at) FROM ticket_messages WHERE ticket_messages.ticket_id = tickets.id\n )\n )\n ) ELSE (\n SELECT COUNT(created_at) FROM tickets WHERE closed = false AND tickets.creator = users.id AND EXISTS (\n SELECT 1 FROM ticket_messages as tm WHERE tm.ticket_id = tickets.id AND tm.author != users.id AND tm.created_at > (\n SELECT MAX(tm2.created_at) FROM ticket_messages as tm2 WHERE tm2.ticket_id = tm.ticket_id AND tm2.author = users.id\n )\n )\n ) END) as \"newer_ticket_messages_count!\"\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Uuid"
+ },
+ {
+ "ordinal": 1,
+ "name": "name",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 2,
+ "name": "email",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 3,
+ "name": "avatar_url",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 4,
+ "name": "updated_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 5,
+ "name": "created_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 6,
+ "name": "github_id",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 7,
+ "name": "is_blocked",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 8,
+ "name": "is_staff",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 9,
+ "name": "scope_limit",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 10,
+ "name": "invite_count!",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 11,
+ "name": "scope_usage!",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 12,
+ "name": "newer_ticket_messages_count!",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Uuid"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ null,
+ null,
+ null
+ ]
+ },
+ "hash": "0e8e4c876ea49c287f77dc33a1dd6c41dc7080e695249ff4a5a415df05139ece"
+}
diff --git a/api/.sqlx/query-35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610.json b/api/.sqlx/query-35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610.json
new file mode 100644
index 000000000..8dd84463c
--- /dev/null
+++ b/api/.sqlx/query-35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610.json
@@ -0,0 +1,48 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "\n SELECT time_bucket, kind as \"kind: DownloadKind\", count\n FROM version_download_counts_4h\n WHERE scope = $1 AND package = $2 AND version = $3 AND time_bucket >= $4 AND time_bucket < $5\n ORDER BY time_bucket ASC\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "time_bucket",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 1,
+ "name": "kind: DownloadKind",
+ "type_info": {
+ "Custom": {
+ "name": "download_kind",
+ "kind": {
+ "Enum": [
+ "npm_tgz",
+ "jsr_meta"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "ordinal": 2,
+ "name": "count",
+ "type_info": "Int4"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text",
+ "Text",
+ "Text",
+ "Timestamptz",
+ "Timestamptz"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "35632bc48d97695339900965cf98b93a12da06bb610173c1e39f9997f97e9610"
+}
diff --git a/api/.sqlx/query-3f118b22cfc900a7f6e3e483f97e9761bc495a34441ddf2a128fec6364be6a62.json b/api/.sqlx/query-3f118b22cfc900a7f6e3e483f97e9761bc495a34441ddf2a128fec6364be6a62.json
new file mode 100644
index 000000000..e019b4269
--- /dev/null
+++ b/api/.sqlx/query-3f118b22cfc900a7f6e3e483f97e9761bc495a34441ddf2a128fec6364be6a62.json
@@ -0,0 +1,48 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO scope_members (scope, user_id, is_admin)\n VALUES ($1, $2, $3)\n RETURNING scope as \"scope: ScopeName\", user_id, is_admin, updated_at, created_at",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "scope: ScopeName",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 1,
+ "name": "user_id",
+ "type_info": "Uuid"
+ },
+ {
+ "ordinal": 2,
+ "name": "is_admin",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 3,
+ "name": "updated_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 4,
+ "name": "created_at",
+ "type_info": "Timestamptz"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text",
+ "Uuid",
+ "Bool"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "3f118b22cfc900a7f6e3e483f97e9761bc495a34441ddf2a128fec6364be6a62"
+}
diff --git a/api/.sqlx/query-46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f.json b/api/.sqlx/query-46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f.json
new file mode 100644
index 000000000..5ea339b0a
--- /dev/null
+++ b/api/.sqlx/query-46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f.json
@@ -0,0 +1,113 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO package_versions (scope, name, version, user_id, readme_path, exports, uses_npm, meta)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n RETURNING scope as \"scope: ScopeName\", name as \"name: PackageName\", version as \"version: Version\", user_id, readme_path as \"readme_path: PackagePath\", exports as \"exports: ExportsMap\", is_yanked, uses_npm, meta as \"meta: PackageVersionMeta\", updated_at, created_at, rekor_log_id, license,\n (SELECT COUNT(*)\n FROM package_versions AS pv\n WHERE pv.scope = package_versions.scope\n AND pv.name = package_versions.name\n AND pv.version > package_versions.version\n AND pv.version NOT LIKE '%-%'\n AND pv.is_yanked = false) as \"newer_versions_count!\",\n (SELECT COALESCE(SUM(dl.count), 0)\n FROM version_download_counts_24h as dl\n WHERE dl.scope = package_versions.scope\n AND dl.package = package_versions.name\n AND dl.version = package_versions.version) as \"lifetime_download_count!\"",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "scope: ScopeName",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 1,
+ "name": "name: PackageName",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 2,
+ "name": "version: Version",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 3,
+ "name": "user_id",
+ "type_info": "Uuid"
+ },
+ {
+ "ordinal": 4,
+ "name": "readme_path: PackagePath",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 5,
+ "name": "exports: ExportsMap",
+ "type_info": "Jsonb"
+ },
+ {
+ "ordinal": 6,
+ "name": "is_yanked",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 7,
+ "name": "uses_npm",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 8,
+ "name": "meta: PackageVersionMeta",
+ "type_info": "Jsonb"
+ },
+ {
+ "ordinal": 9,
+ "name": "updated_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 10,
+ "name": "created_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 11,
+ "name": "rekor_log_id",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 12,
+ "name": "license",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 13,
+ "name": "newer_versions_count!",
+ "type_info": "Int8"
+ },
+ {
+ "ordinal": 14,
+ "name": "lifetime_download_count!",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text",
+ "Text",
+ "Text",
+ "Uuid",
+ "Text",
+ "Jsonb",
+ "Bool",
+ "Jsonb"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ true,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ true,
+ null,
+ null
+ ]
+ },
+ "hash": "46f22b0a1095e370760d3f58454a43b7d34ab53ad7bc638460faa748dfaa9b8f"
+}
diff --git a/api/.sqlx/query-51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5.json b/api/.sqlx/query-51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5.json
new file mode 100644
index 000000000..bd69526a4
--- /dev/null
+++ b/api/.sqlx/query-51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5.json
@@ -0,0 +1,69 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO package_files (scope, name, version, path, size, checksum)\n VALUES ($1, $2, $3, $4, $5, $6)\n RETURNING scope as \"scope: ScopeName\", name as \"name: PackageName\", version as \"version: Version\", path as \"path: PackagePath\", size, checksum, updated_at, created_at",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "scope: ScopeName",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 1,
+ "name": "name: PackageName",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 2,
+ "name": "version: Version",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 3,
+ "name": "path: PackagePath",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 4,
+ "name": "size",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 5,
+ "name": "checksum",
+ "type_info": "Text"
+ },
+ {
+ "ordinal": 6,
+ "name": "updated_at",
+ "type_info": "Timestamptz"
+ },
+ {
+ "ordinal": 7,
+ "name": "created_at",
+ "type_info": "Timestamptz"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text",
+ "Text",
+ "Text",
+ "Text",
+ "Int4",
+ "Text"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false
+ ]
+ },
+ "hash": "51fd48d86ae305c6fc527fd5b062a35c41672736f94acea3afe9ad369b9f53b5"
+}
diff --git a/api/.sqlx/query-af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528.json b/api/.sqlx/query-af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528.json
new file mode 100644
index 000000000..c1da7eb2b
--- /dev/null
+++ b/api/.sqlx/query-af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528.json
@@ -0,0 +1,14 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO bad_words (word) VALUES ($1)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Text"
+ ]
+ },
+ "nullable": []
+ },
+ "hash": "af37a947380c91b98ef36a7f2bcafabc2574dae3f240befb41e4fb4e74280528"
+}
From 872abab61e542994e7cd8e5245a70cb8693223ab Mon Sep 17 00:00:00 2001
From: Leo Kettmeir
Date: Thu, 5 Feb 2026 15:21:39 +0100
Subject: [PATCH 17/17] fixes
---
api/src/db/database.rs | 6 +++---
api/src/publish.rs | 10 +++++++++-
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/api/src/db/database.rs b/api/src/db/database.rs
index b2d3e4b2d..790a040fb 100644
--- a/api/src/db/database.rs
+++ b/api/src/db/database.rs
@@ -2721,7 +2721,7 @@ impl Database {
WebhookEventKind::ScopeMemberAdded,
WebhookPayload::ScopeMemberAdded {
scope: scope.clone(),
- user_id: target_user_id.clone(),
+ user_id: *target_user_id,
},
)
.await?;
@@ -3053,7 +3053,7 @@ impl Database {
let webhook_deliveries = insert_webhook_event(
&mut tx,
- &scope,
+ scope,
None,
WebhookEventKind::ScopeMemberRemoved,
WebhookPayload::ScopeMemberRemoved {
@@ -3528,7 +3528,7 @@ impl Database {
scope: task.package_scope.clone(),
package: task.package_name.clone(),
version: task.package_version.clone(),
- user_id: task.user_id.clone(),
+ user_id: task.user_id,
},
)
.await?;
diff --git a/api/src/publish.rs b/api/src/publish.rs
index 80fe5b006..bc282b6fe 100644
--- a/api/src/publish.rs
+++ b/api/src/publish.rs
@@ -78,9 +78,17 @@ pub async fn publish_handler(mut req: Request) -> ApiResult<()> {
Ok(())
}
+#[allow(clippy::too_many_arguments)]
#[instrument(
name = "publish_task",
- skip(buckets, db, license_store, registry_url, webhook_dispatch_queue, orama_client),
+ skip(
+ buckets,
+ db,
+ license_store,
+ registry_url,
+ webhook_dispatch_queue,
+ orama_client
+ ),
err
)]
pub async fn publish_task(