@shipfox/api-integration-sentry 10.2.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +25 -0
- package/README.md +1 -1
- package/dist/api/client.js +1 -1
- package/dist/api/client.js.map +1 -1
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/errors.js +1 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/install.js +1 -1
- package/dist/core/install.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/api/client.ts +1 -1
- package/src/core/errors.ts +1 -1
- package/src/core/install.test.ts +4 -4
- package/src/core/install.ts +1 -1
- package/src/presentation/routes/install.test.ts +3 -3
- package/test/factories/sentry-installation-webhook.ts +1 -1
- package/test/factories/sentry-issue-webhook.ts +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/api-integration-sentry",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "12.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
"drizzle-orm": "^0.45.2",
|
|
23
23
|
"ky": "^2.0.0",
|
|
24
24
|
"zod": "^4.4.3",
|
|
25
|
-
"@shipfox/api-auth-context": "
|
|
26
|
-
"@shipfox/api-integration-spi": "0.
|
|
27
|
-
"@shipfox/api-integration-sentry-dto": "
|
|
25
|
+
"@shipfox/api-auth-context": "12.0.0",
|
|
26
|
+
"@shipfox/api-integration-spi": "1.0.0",
|
|
27
|
+
"@shipfox/api-integration-sentry-dto": "12.0.0",
|
|
28
28
|
"@shipfox/config": "1.2.4",
|
|
29
|
-
"@shipfox/node-drizzle": "0.3.
|
|
30
|
-
"@shipfox/node-fastify": "0.4.
|
|
31
|
-
"@shipfox/node-module": "1.0.
|
|
29
|
+
"@shipfox/node-drizzle": "0.3.5",
|
|
30
|
+
"@shipfox/node-fastify": "0.4.1",
|
|
31
|
+
"@shipfox/node-module": "1.0.5",
|
|
32
32
|
"@shipfox/node-opentelemetry": "0.6.3",
|
|
33
|
-
"@shipfox/node-postgres": "0.
|
|
33
|
+
"@shipfox/node-postgres": "0.5.0"
|
|
34
34
|
},
|
|
35
35
|
"imports": {
|
|
36
36
|
"#*": "./dist/*"
|
package/src/api/client.ts
CHANGED
|
@@ -103,7 +103,7 @@ export function createSentryApiClient(): SentryApiClient {
|
|
|
103
103
|
|
|
104
104
|
// The typed error deliberately drops Sentry's status and body so no token, code,
|
|
105
105
|
// or client secret can leak to the client or the logged error chain. That also
|
|
106
|
-
// strips the one detail a self-hoster needs to fix a misconfigured Sentry app
|
|
106
|
+
// strips the one detail a self-hoster needs to fix a misconfigured Sentry app.
|
|
107
107
|
// e.g. a 403 on `get-installation` means the app is missing "Organization: Read"
|
|
108
108
|
// (see README "Required permissions"). So we log the upstream status
|
|
109
109
|
// here, the only place it survives, keyed by the operation that failed.
|
package/src/core/errors.ts
CHANGED
|
@@ -41,7 +41,7 @@ export class SentryVerificationInProgressError extends Error {
|
|
|
41
41
|
/**
|
|
42
42
|
* Base for issue deliveries we received and authenticated but deliberately do
|
|
43
43
|
* not publish. The webhook layer records them for dedup and acknowledges with a
|
|
44
|
-
* 204 rather than treating them as failures
|
|
44
|
+
* 204 rather than treating them as failures: a sustained non-2xx can make Sentry
|
|
45
45
|
* disable the webhook. Carries no HTTP concerns so workers/jobs can reuse it.
|
|
46
46
|
*/
|
|
47
47
|
export class SentryIssueDroppedError extends Error {}
|
package/src/core/install.test.ts
CHANGED
|
@@ -121,7 +121,7 @@ function run(options: RunOptions = {}) {
|
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
describe('handleSentryConnect
|
|
124
|
+
describe('handleSentryConnect: browser-first (no row)', () => {
|
|
125
125
|
it('exchanges, persists the unclaimed row, then binds and verifies last', async () => {
|
|
126
126
|
const sentry = sentryClient();
|
|
127
127
|
const {connectSentryInstallation, persistVerifiedUnclaimedInstallation, result} = run({sentry});
|
|
@@ -179,7 +179,7 @@ describe('handleSentryConnect — browser-first (no row)', () => {
|
|
|
179
179
|
});
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
-
describe('handleSentryConnect
|
|
182
|
+
describe('handleSentryConnect: verified, unclaimed row exists (webhook-first)', () => {
|
|
183
183
|
it('binds via same-code race when the exchange is already used and the hash matches', async () => {
|
|
184
184
|
const sentry = sentryClient({
|
|
185
185
|
exchangeAuthorizationCode: vi.fn(() =>
|
|
@@ -237,7 +237,7 @@ describe('handleSentryConnect — verified, unclaimed row exists (webhook-first)
|
|
|
237
237
|
});
|
|
238
238
|
});
|
|
239
239
|
|
|
240
|
-
describe('handleSentryConnect
|
|
240
|
+
describe('handleSentryConnect: already claimed / tombstoned', () => {
|
|
241
241
|
it('is idempotent for an install already claimed to the same workspace', async () => {
|
|
242
242
|
const existing = connection({workspaceId: WORKSPACE_ID});
|
|
243
243
|
const {sentry, connectSentryInstallation, result} = run({
|
|
@@ -272,7 +272,7 @@ describe('handleSentryConnect — already claimed / tombstoned', () => {
|
|
|
272
272
|
});
|
|
273
273
|
});
|
|
274
274
|
|
|
275
|
-
describe('handleSentryConnect
|
|
275
|
+
describe('handleSentryConnect: simultaneous race', () => {
|
|
276
276
|
it('returns a retryable error when the code is already used and no row is visible yet', async () => {
|
|
277
277
|
const sentry = sentryClient({
|
|
278
278
|
exchangeAuthorizationCode: vi.fn(() =>
|
package/src/core/install.ts
CHANGED
|
@@ -236,7 +236,7 @@ async function claimBrowserFirst(
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
// The browser-first exchange got "already used" with no row visible at lookup: a
|
|
239
|
-
// concurrent webhook won the exchange. Re-read once
|
|
239
|
+
// concurrent webhook won the exchange. Re-read once: if its verified row is now
|
|
240
240
|
// visible we reconcile through the same proof rules; if it got claimed we resolve
|
|
241
241
|
// it; if it was tombstoned we surface that terminally (matching the top-level
|
|
242
242
|
// check); otherwise it is still mid-flight, so the claim is retryable.
|
|
@@ -94,7 +94,7 @@ async function createTestApp(options: CreateTestAppOptions = {}): Promise<Fastif
|
|
|
94
94
|
updatedAt: new Date(),
|
|
95
95
|
}),
|
|
96
96
|
),
|
|
97
|
-
// Webhook receiver dependencies
|
|
97
|
+
// Webhook receiver dependencies: install/connect tests don't exercise them.
|
|
98
98
|
coreDb: vi.fn() as never,
|
|
99
99
|
publishIntegrationEventReceived: vi.fn(() => Promise.resolve({published: false})),
|
|
100
100
|
recordDeliveryOnly: vi.fn(() => Promise.resolve()),
|
|
@@ -109,7 +109,7 @@ async function createTestApp(options: CreateTestAppOptions = {}): Promise<Fastif
|
|
|
109
109
|
function connectPayload(options: {authorize?: boolean} = {}) {
|
|
110
110
|
const workspaceId = crypto.randomUUID();
|
|
111
111
|
if (options.authorize !== false) {
|
|
112
|
-
authenticatedMemberships = [{workspaceId, role: 'admin'}];
|
|
112
|
+
authenticatedMemberships = [{workspaceId, role: 'admin', workspaceStatus: 'active'}];
|
|
113
113
|
}
|
|
114
114
|
return {
|
|
115
115
|
workspace_id: workspaceId,
|
|
@@ -143,7 +143,7 @@ describe('Sentry integration routes', () => {
|
|
|
143
143
|
it('returns the external-install URL for a member', async () => {
|
|
144
144
|
const app = await createTestApp();
|
|
145
145
|
const workspaceId = crypto.randomUUID();
|
|
146
|
-
authenticatedMemberships = [{workspaceId, role: 'admin'}];
|
|
146
|
+
authenticatedMemberships = [{workspaceId, role: 'admin', workspaceStatus: 'active'}];
|
|
147
147
|
|
|
148
148
|
const res = await app.inject({
|
|
149
149
|
method: 'POST',
|
|
@@ -4,7 +4,7 @@ import {Factory} from 'fishery';
|
|
|
4
4
|
// over the wire: the lifecycle data nests under `data.installation` and carries
|
|
5
5
|
// the single-use authorization `code`. Tests serialize the built object to form a
|
|
6
6
|
// signed request body; the route parses and validates it with the production Zod
|
|
7
|
-
// schema. Build-only
|
|
7
|
+
// schema. Build-only: the payload is never persisted, so there is no onCreate
|
|
8
8
|
// handler.
|
|
9
9
|
export interface SentryInstallationWebhookPayload {
|
|
10
10
|
action: string;
|
|
@@ -2,7 +2,7 @@ import {Factory} from 'fishery';
|
|
|
2
2
|
|
|
3
3
|
// Raw Sentry issue webhook payload, shaped exactly as Sentry delivers it over
|
|
4
4
|
// the wire. Tests serialize the built object to form a signed request body; the
|
|
5
|
-
// route parses and validates it with the production Zod schema. Build-only
|
|
5
|
+
// route parses and validates it with the production Zod schema. Build-only: the
|
|
6
6
|
// payload is never persisted, so there is no onCreate handler.
|
|
7
7
|
export interface SentryIssueWebhookPayload {
|
|
8
8
|
action: string;
|