@sentry/api 1.27.0 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/api-client/api-path-scope.d.ts +25 -0
- package/dist/api-client/api-path.d.ts +1 -20
- package/dist/api-client/index.js +30 -28
- package/dist/client/index.d.ts +1 -1
- package/dist/client/types.gen.d.ts +22 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +53 -0
- package/dist/types.gen.d.ts +870 -0
- package/dist/valibot.gen.d.ts +1195 -349
- package/dist/valibot.js +347 -347
- package/dist/zod.gen.d.ts +2 -2
- package/dist/zod.js +838 -258
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -176,6 +176,29 @@ const sentry = createSentryClient(
|
|
|
176
176
|
const { data, error } = await sentry.listOrganizations();
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
+
### Routing requests by organization
|
|
180
|
+
|
|
181
|
+
Set `resolveBaseUrl` to pick each request's base URL from its path before the
|
|
182
|
+
URL is built, such as the region that serves the organization. It receives the
|
|
183
|
+
configured `baseUrl` (`''` when unset), the path and query that follow it, and
|
|
184
|
+
the organization and project the path targets (`parseApiPathScope` from
|
|
185
|
+
`@sentry/api/client`; absent for unscoped paths). Its result replaces `baseUrl`
|
|
186
|
+
for that request only; trailing slashes are removed.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
const sentry = createSentryClient({
|
|
190
|
+
baseUrl: "https://sentry.io",
|
|
191
|
+
resolveBaseUrl: async ({ baseUrl, scope }) =>
|
|
192
|
+
scope ? await regionUrlFor(scope.organization) : baseUrl,
|
|
193
|
+
});
|
|
194
|
+
// Sent to the organization's region, such as https://us.sentry.io.
|
|
195
|
+
await sentry.listOrganizationIssues({ path: { organization_id_or_slug: "acme" } });
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
It runs once per request, including `prepareRequest`, after `requestValidator`
|
|
199
|
+
and before request interceptors. A per-call `resolveBaseUrl` replaces the
|
|
200
|
+
configured one. A throw rejects the request unchanged, before `fetch` is called.
|
|
201
|
+
|
|
179
202
|
### Preparing a raw request
|
|
180
203
|
|
|
181
204
|
Use `sentry.client.prepareRequest` when a consumer needs to inspect a request
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the organization and project an API path targets. No imports, so
|
|
3
|
+
* the generated client can use it without pulling in `@sentry/api/client`.
|
|
4
|
+
*/
|
|
5
|
+
/** The organization, and project when present, an API path targets. */
|
|
6
|
+
export type ApiPathScope = {
|
|
7
|
+
organization: string;
|
|
8
|
+
project?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The organization, and project when present, a Sentry API request targets,
|
|
12
|
+
* read from its path: `/organizations/{org}/`, `/projects/{org}/{project}/`,
|
|
13
|
+
* `/teams/{org}/`, and `/organizations/{org}/projects/{project}/`.
|
|
14
|
+
*
|
|
15
|
+
* Accepts a full URL, whose path must contain `/api/0/` (a self-hosted
|
|
16
|
+
* install may serve it under a prefix), or a path relative to `/api/0/` with
|
|
17
|
+
* or without that prefix. Undefined for unscoped paths, list endpoints such
|
|
18
|
+
* as `/organizations/`, and schema placeholders.
|
|
19
|
+
*
|
|
20
|
+
* Hosts with several credentials or regions use it to pick the one that
|
|
21
|
+
* serves the organization: `createApiTransport` passes it to
|
|
22
|
+
* `CredentialProvider.current`, and the generated client to
|
|
23
|
+
* `Config.resolveBaseUrl`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseApiPathScope(urlOrPath: string): ApiPathScope | undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { type ApiPathScope, parseApiPathScope } from "./api-path-scope.js";
|
|
1
2
|
/**
|
|
2
3
|
* Encode one Sentry API path identifier without letting it become a URL path.
|
|
3
4
|
*
|
|
@@ -30,25 +31,5 @@ export declare function buildSentryApiUrl(baseUrl: string, endpoint: string): st
|
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
33
|
export declare function apiPath(strings: TemplateStringsArray, ...values: (string | number)[]): string;
|
|
33
|
-
/** The organization, and project when present, an API path targets. */
|
|
34
|
-
export type ApiPathScope = {
|
|
35
|
-
organization: string;
|
|
36
|
-
project?: string;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* The organization, and project when present, a Sentry API request targets,
|
|
40
|
-
* read from its path: `/organizations/{org}/`, `/projects/{org}/{project}/`,
|
|
41
|
-
* `/teams/{org}/`, and `/organizations/{org}/projects/{project}/`.
|
|
42
|
-
*
|
|
43
|
-
* Accepts a full URL, whose path must contain `/api/0/` (a self-hosted
|
|
44
|
-
* install may serve it under a prefix), or a path relative to `/api/0/` with
|
|
45
|
-
* or without that prefix. Undefined for unscoped paths, list endpoints such
|
|
46
|
-
* as `/organizations/`, and schema placeholders.
|
|
47
|
-
*
|
|
48
|
-
* Hosts with several credentials use it to pick the one that can reach the
|
|
49
|
-
* organization; {@link createApiTransport} passes it to
|
|
50
|
-
* `CredentialProvider.current`.
|
|
51
|
-
*/
|
|
52
|
-
export declare function parseApiPathScope(urlOrPath: string): ApiPathScope | undefined;
|
|
53
34
|
/** `path` with `query` appended, or `path` alone when `query` is empty. */
|
|
54
35
|
export declare function withQuery(path: string, query: URLSearchParams): string;
|
package/dist/api-client/index.js
CHANGED
|
@@ -212,34 +212,7 @@ function isApiAuthenticationErrorDeep(error) {
|
|
|
212
212
|
return false;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
// src/api-client/api-path.ts
|
|
216
|
-
function encodeApiPathSegment(value) {
|
|
217
|
-
const text = String(value);
|
|
218
|
-
return text === "." || text === ".." ? null : encodeURIComponent(text);
|
|
219
|
-
}
|
|
220
|
-
function buildSentryApiUrl(baseUrl, endpoint) {
|
|
221
|
-
const path = endpoint.startsWith("/") ? endpoint.slice(1) : endpoint;
|
|
222
|
-
let end = baseUrl.length;
|
|
223
|
-
while (end > 0 && baseUrl.charAt(end - 1) === "/") {
|
|
224
|
-
end -= 1;
|
|
225
|
-
}
|
|
226
|
-
return `${baseUrl.slice(0, end)}/api/0/${path}`;
|
|
227
|
-
}
|
|
228
|
-
function apiPath(strings, ...values) {
|
|
229
|
-
return strings.reduce((acc, literal, index) => {
|
|
230
|
-
if (index >= values.length) {
|
|
231
|
-
return acc + literal;
|
|
232
|
-
}
|
|
233
|
-
const value = String(values[index]);
|
|
234
|
-
const encoded = encodeApiPathSegment(value);
|
|
235
|
-
if (encoded === null) {
|
|
236
|
-
throw new UserInputError(
|
|
237
|
-
`Invalid identifier "${value}": relative path segments are not allowed.`
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
return acc + literal + encoded;
|
|
241
|
-
}, "");
|
|
242
|
-
}
|
|
215
|
+
// src/api-client/api-path-scope.ts
|
|
243
216
|
var API_PATH_IDENTIFIER = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
244
217
|
function isApiPathIdentifier(segment) {
|
|
245
218
|
return segment !== void 0 && API_PATH_IDENTIFIER.test(segment);
|
|
@@ -275,6 +248,35 @@ function parseApiPathScope(urlOrPath) {
|
|
|
275
248
|
}
|
|
276
249
|
return;
|
|
277
250
|
}
|
|
251
|
+
|
|
252
|
+
// src/api-client/api-path.ts
|
|
253
|
+
function encodeApiPathSegment(value) {
|
|
254
|
+
const text = String(value);
|
|
255
|
+
return text === "." || text === ".." ? null : encodeURIComponent(text);
|
|
256
|
+
}
|
|
257
|
+
function buildSentryApiUrl(baseUrl, endpoint) {
|
|
258
|
+
const path = endpoint.startsWith("/") ? endpoint.slice(1) : endpoint;
|
|
259
|
+
let end = baseUrl.length;
|
|
260
|
+
while (end > 0 && baseUrl.charAt(end - 1) === "/") {
|
|
261
|
+
end -= 1;
|
|
262
|
+
}
|
|
263
|
+
return `${baseUrl.slice(0, end)}/api/0/${path}`;
|
|
264
|
+
}
|
|
265
|
+
function apiPath(strings, ...values) {
|
|
266
|
+
return strings.reduce((acc, literal, index) => {
|
|
267
|
+
if (index >= values.length) {
|
|
268
|
+
return acc + literal;
|
|
269
|
+
}
|
|
270
|
+
const value = String(values[index]);
|
|
271
|
+
const encoded = encodeApiPathSegment(value);
|
|
272
|
+
if (encoded === null) {
|
|
273
|
+
throw new UserInputError(
|
|
274
|
+
`Invalid identifier "${value}": relative path segments are not allowed.`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
return acc + literal + encoded;
|
|
278
|
+
}, "");
|
|
279
|
+
}
|
|
278
280
|
function withQuery(path, query) {
|
|
279
281
|
const queryString = query.toString();
|
|
280
282
|
return queryString ? `${path}?${queryString}` : path;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen';
|
|
|
6
6
|
export type { ServerSentEventsResult } from '../core/serverSentEvents.gen';
|
|
7
7
|
export { createClient } from './client.gen';
|
|
8
8
|
export type { PreparedClient } from './client.gen';
|
|
9
|
-
export type { Client, ClientOptions, Config, CreateClientConfig, Options, RequestOptions, RequestResult, ResolvedRequestOptions, ResponseStyle, TDataShape, } from './types.gen';
|
|
9
|
+
export type { BaseUrlRequest, BaseUrlResolver, Client, ClientOptions, Config, CreateClientConfig, Options, RequestOptions, RequestResult, ResolvedRequestOptions, ResponseStyle, TDataShape, } from './types.gen';
|
|
10
10
|
export { createConfig, mergeHeaders } from './utils.gen';
|
|
@@ -3,6 +3,21 @@ import type { Auth } from '../core/auth.gen';
|
|
|
3
3
|
import type { ServerSentEventsOptions, ServerSentEventsResult } from '../core/serverSentEvents.gen';
|
|
4
4
|
import type { Client as CoreClient, Config as CoreConfig } from '../core/types.gen';
|
|
5
5
|
import type { Middleware } from './utils.gen';
|
|
6
|
+
import type { ApiPathScope } from '../api-client/api-path-scope';
|
|
7
|
+
/** The request a base URL is picked for; see {@link Config.resolveBaseUrl}. */
|
|
8
|
+
export type BaseUrlRequest = {
|
|
9
|
+
/** The configured base URL, or `''` when none is set. */
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
/** The request's path and query, as they follow the base URL. */
|
|
12
|
+
url: string;
|
|
13
|
+
/**
|
|
14
|
+
* The organization, and project when present, the path targets
|
|
15
|
+
* (`parseApiPathScope` in `@sentry/api/client`). Absent for unscoped paths.
|
|
16
|
+
*/
|
|
17
|
+
scope?: ApiPathScope;
|
|
18
|
+
};
|
|
19
|
+
/** Picks the base URL for one request; see {@link Config.resolveBaseUrl}. */
|
|
20
|
+
export type BaseUrlResolver = (request: BaseUrlRequest) => string | Promise<string>;
|
|
6
21
|
export type ResponseStyle = 'fields';
|
|
7
22
|
export interface Config<T extends ClientOptions = ClientOptions> extends Omit<RequestInit, 'body' | 'headers' | 'method'>, CoreConfig {
|
|
8
23
|
/**
|
|
@@ -16,6 +31,13 @@ export interface Config<T extends ClientOptions = ClientOptions> extends Omit<Re
|
|
|
16
31
|
* @default globalThis.fetch
|
|
17
32
|
*/
|
|
18
33
|
fetch?: typeof fetch;
|
|
34
|
+
/**
|
|
35
|
+
* Pick the base URL for each request, before its URL is built, such as the
|
|
36
|
+
* region that serves the organization the path targets. Called once per
|
|
37
|
+
* request, after `requestValidator` and before request interceptors; its
|
|
38
|
+
* result replaces `baseUrl` for that request.
|
|
39
|
+
*/
|
|
40
|
+
resolveBaseUrl?: BaseUrlResolver;
|
|
19
41
|
/**
|
|
20
42
|
* Please don't use the Fetch client for Next.js applications. The `next`
|
|
21
43
|
* options won't have any effect.
|
package/dist/index.d.ts
CHANGED
|
@@ -9,4 +9,4 @@ export type * from './query-enums.gen.ts';
|
|
|
9
9
|
export * from './pagination.gen.ts';
|
|
10
10
|
export { createSentryClient } from './sentry-client.gen.ts';
|
|
11
11
|
export type { SentryClient } from './sentry-client.gen.ts';
|
|
12
|
-
export type { Config } from './client/index.ts';
|
|
12
|
+
export type { BaseUrlRequest, BaseUrlResolver, Config } from './client/index.ts';
|
package/dist/index.js
CHANGED
|
@@ -443,6 +443,43 @@ function getValidRequestBody(options) {
|
|
|
443
443
|
return void 0;
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
+
// src/api-client/api-path-scope.ts
|
|
447
|
+
var API_PATH_IDENTIFIER = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
448
|
+
function isApiPathIdentifier(segment) {
|
|
449
|
+
return segment !== void 0 && API_PATH_IDENTIFIER.test(segment);
|
|
450
|
+
}
|
|
451
|
+
function apiPathSegments(urlOrPath) {
|
|
452
|
+
const isUrl = URL.canParse(urlOrPath);
|
|
453
|
+
const path = isUrl ? new URL(urlOrPath).pathname : urlOrPath.split(/[?#]/, 1)[0] ?? "";
|
|
454
|
+
const segments = path.split("/").filter((segment) => segment.length > 0);
|
|
455
|
+
for (let index = 0; index < segments.length - 1; index += 1) {
|
|
456
|
+
if (segments[index] === "api" && segments[index + 1] === "0") {
|
|
457
|
+
return segments.slice(index + 2);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return isUrl ? void 0 : segments;
|
|
461
|
+
}
|
|
462
|
+
function parseApiPathScope(urlOrPath) {
|
|
463
|
+
const segments = apiPathSegments(urlOrPath);
|
|
464
|
+
if (!segments) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const [resource, organization, next, project] = segments;
|
|
468
|
+
if (!isApiPathIdentifier(organization)) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (resource === "organizations") {
|
|
472
|
+
return next === "projects" && isApiPathIdentifier(project) ? { organization, project } : { organization };
|
|
473
|
+
}
|
|
474
|
+
if (resource === "projects") {
|
|
475
|
+
return isApiPathIdentifier(next) ? { organization, project: next } : { organization };
|
|
476
|
+
}
|
|
477
|
+
if (resource === "teams") {
|
|
478
|
+
return { organization };
|
|
479
|
+
}
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
446
483
|
// src/core/auth.gen.ts
|
|
447
484
|
var getAuthToken = async (auth, callback) => {
|
|
448
485
|
const token = typeof callback === "function" ? await callback(auth) : callback;
|
|
@@ -677,6 +714,15 @@ var assertFields = (config) => {
|
|
|
677
714
|
throw new TypeError('@sentry/api only supports responseStyle: "fields". Read result.data instead.');
|
|
678
715
|
}
|
|
679
716
|
};
|
|
717
|
+
var resolveBaseUrl = async (resolve, baseUrl, url) => {
|
|
718
|
+
const scope = parseApiPathScope(url);
|
|
719
|
+
const resolved = await resolve(scope ? { baseUrl, url, scope } : { baseUrl, url });
|
|
720
|
+
let end = resolved.length;
|
|
721
|
+
while (end > 0 && resolved.charAt(end - 1) === "/") {
|
|
722
|
+
end -= 1;
|
|
723
|
+
}
|
|
724
|
+
return resolved.slice(0, end);
|
|
725
|
+
};
|
|
680
726
|
var createClient = (config = {}) => {
|
|
681
727
|
assertFields(config);
|
|
682
728
|
let _config = mergeConfigs(createConfig(), config);
|
|
@@ -709,6 +755,13 @@ var createClient = (config = {}) => {
|
|
|
709
755
|
opts.headers.delete("Content-Type");
|
|
710
756
|
}
|
|
711
757
|
const resolvedOpts = opts;
|
|
758
|
+
if (resolvedOpts.resolveBaseUrl) {
|
|
759
|
+
resolvedOpts.baseUrl = await resolveBaseUrl(
|
|
760
|
+
resolvedOpts.resolveBaseUrl,
|
|
761
|
+
resolvedOpts.baseUrl ?? "",
|
|
762
|
+
buildUrl({ ...resolvedOpts, baseUrl: "" })
|
|
763
|
+
);
|
|
764
|
+
}
|
|
712
765
|
const url = buildUrl(resolvedOpts);
|
|
713
766
|
return { opts: resolvedOpts, url };
|
|
714
767
|
};
|