app-settings-js 0.1.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.
@@ -0,0 +1,222 @@
1
+ import { type FetchLike, type RequestOptions, type TransportOptions } from "./http.js";
2
+ import { SettingsSnapshot } from "./snapshot.js";
3
+ import type { ApiKey, CreatedApiKey, Environment, Group, GroupMember, Health, IntermediateValue, PersonalValue, Platform, Role, Scope, ServerValue, Setting, SettingScope, SettingType, SettingValue, TypeConfig, WhoAmI } from "./types.js";
4
+ /** How to reach the server, and what to assume when a call does not say. */
5
+ export interface ClientOptions extends TransportOptions {
6
+ /**
7
+ * The environment used by calls that require one. Resolution always needs an
8
+ * environment, so setting it here means most calls take no options at all.
9
+ */
10
+ environment?: string;
11
+ /** The platform filter applied to resolution and to setting lookups. */
12
+ platform?: string | string[];
13
+ }
14
+ /** Options shared by both resolution calls. */
15
+ export interface ResolveOptions extends RequestOptions {
16
+ /** Overrides the client's environment. Required if the client has none. */
17
+ environment?: string;
18
+ /** Restricts the resolution to these platforms. Capped by the key's own fence. */
19
+ platform?: string | string[];
20
+ /** Resolves as this role instead of the key's. It may not outrank the key. */
21
+ role?: string;
22
+ }
23
+ /** Resolution for one user. */
24
+ export interface ResolveUserOptions extends ResolveOptions {
25
+ /**
26
+ * Groups to apply without storing membership, which is how an ad-hoc group
27
+ * is used. Saved memberships apply regardless.
28
+ */
29
+ groupId?: string | string[];
30
+ }
31
+ /** Filters for listing setting definitions. */
32
+ export interface ListSettingsOptions extends RequestOptions {
33
+ environment?: string;
34
+ platform?: string;
35
+ scope?: SettingScope;
36
+ }
37
+ /** A new setting definition. Type, scope, platform and environment are fixed at creation. */
38
+ export interface CreateSettingInput {
39
+ name: string;
40
+ type: SettingType;
41
+ scope: SettingScope;
42
+ platform: string;
43
+ /** Defaults to the client's environment. */
44
+ environment?: string;
45
+ description?: string;
46
+ /** The rules for this type. See {@link TypeConfig}. */
47
+ typeConfig?: TypeConfig;
48
+ /** Defaults to the calling key's own role. */
49
+ role?: string;
50
+ /** Used when no layer supplies a value. */
51
+ defaultValue?: SettingValue;
52
+ }
53
+ /** The parts of a definition that are safe to change after creation. */
54
+ export interface UpdateSettingInput {
55
+ description?: string;
56
+ typeConfig?: TypeConfig;
57
+ role?: string;
58
+ defaultValue?: SettingValue;
59
+ }
60
+ /** Options for writing a group override. */
61
+ export interface GroupValueOptions extends RequestOptions {
62
+ /** Whether the user is meant to observe the override. Defaults to true. */
63
+ visible?: boolean;
64
+ /** Whether the override beats the user's own value. Defaults to false. */
65
+ enforced?: boolean;
66
+ }
67
+ /** A new group. */
68
+ export interface CreateGroupInput {
69
+ name: string;
70
+ description?: string;
71
+ /** Omit to span every environment, which a fenced key may not do. */
72
+ environment?: string | null;
73
+ /** Higher priority wins when two groups override the same setting. */
74
+ priority?: number;
75
+ /** Marks an ad-hoc group so operators can prune it later. */
76
+ ephemeral?: boolean;
77
+ /** Seeds membership in the same request. */
78
+ members?: string[];
79
+ }
80
+ /** A new API key. It can never reach further than the key that mints it. */
81
+ export interface CreateKeyInput {
82
+ name: string;
83
+ scopes: Scope[];
84
+ /** Empty inherits the creating key's fence rather than granting everything. */
85
+ environments?: string[];
86
+ platforms?: string[];
87
+ /** Defaults to the lowest-ranked role. May not outrank the creating key. */
88
+ role?: string;
89
+ /** Supply at most one of these. */
90
+ expiresAt?: Date | string;
91
+ /** A Go duration such as `"720h"`. */
92
+ expiresIn?: string;
93
+ }
94
+ /**
95
+ * A client for the App Settings API.
96
+ *
97
+ * One instance is cheap and holds no connection state, so it is safe to build
98
+ * once at module scope and share it.
99
+ *
100
+ * @example
101
+ * const client = new AppSettingsClient({
102
+ * baseUrl: "https://settings.example.com",
103
+ * apiKey: process.env.SETTINGS_API_KEY!,
104
+ * environment: "production",
105
+ * });
106
+ *
107
+ * const settings = await client.resolveUser("alice");
108
+ * if (settings.boolean("dark_mode")) { ... }
109
+ */
110
+ export declare class AppSettingsClient {
111
+ #private;
112
+ constructor(options: ClientOptions);
113
+ /** The environment this client defaults to, if it has one. */
114
+ get environment(): string | undefined;
115
+ /** A copy of this client bound to a different environment. */
116
+ withEnvironment(environment: string): AppSettingsClient;
117
+ /**
118
+ * Every setting a user can see, collapsed to one effective value each.
119
+ *
120
+ * This is the call a product backend makes. Precedence, lowest to highest, is
121
+ * `default < server < advisory group < personal < enforced group`.
122
+ */
123
+ resolveUser(userId: string, options?: ResolveUserOptions): Promise<SettingsSnapshot>;
124
+ /** The server's own settings, with no user layer applied. */
125
+ resolveServer(options?: ResolveOptions): Promise<SettingsSnapshot>;
126
+ /** Describes the calling key, so a deployment can confirm what it can do. */
127
+ whoami(options?: RequestOptions): Promise<WhoAmI>;
128
+ /** Whether the process is up. Needs no API key on the server, but sends one. */
129
+ health(options?: RequestOptions): Promise<Health>;
130
+ /** Whether the server's dependencies are reachable. */
131
+ ready(options?: RequestOptions): Promise<Health>;
132
+ readonly settings: {
133
+ /** Every definition this key may see, narrowed by the given filters. */
134
+ list: (options?: ListSettingsOptions) => Promise<Setting[]>;
135
+ /** One definition by id. */
136
+ get: (id: string, options?: RequestOptions) => Promise<Setting>;
137
+ /** Defines a new setting. */
138
+ create: (input: CreateSettingInput, options?: RequestOptions) => Promise<Setting>;
139
+ /** Changes a definition. Omitted fields are left as they are. */
140
+ update: (id: string, input: UpdateSettingInput, options?: RequestOptions) => Promise<Setting>;
141
+ /**
142
+ * Removes a definition.
143
+ *
144
+ * A delete that would destroy stored values is refused with a `conflict`
145
+ * naming how many, unless `cascade` says to go ahead.
146
+ */
147
+ delete: (id: string, options?: RequestOptions & {
148
+ cascade?: boolean;
149
+ }) => Promise<void>;
150
+ };
151
+ readonly values: {
152
+ /** The server-wide layer, beneath every group and user value. */
153
+ server: {
154
+ get: (settingId: string, options?: RequestOptions) => Promise<ServerValue>;
155
+ set: (settingId: string, value: SettingValue, options?: RequestOptions) => Promise<ServerValue>;
156
+ /** Removes the value, falling back to the definition's default. */
157
+ clear: (settingId: string, options?: RequestOptions) => Promise<void>;
158
+ };
159
+ /** One user's own choice. */
160
+ personal: {
161
+ get: (settingId: string, userId: string, options?: RequestOptions) => Promise<PersonalValue>;
162
+ set: (settingId: string, userId: string, value: SettingValue, options?: RequestOptions) => Promise<PersonalValue>;
163
+ clear: (settingId: string, userId: string, options?: RequestOptions) => Promise<void>;
164
+ };
165
+ /** A group override, in either direction. */
166
+ group: {
167
+ get: (settingId: string, groupId: string, options?: RequestOptions) => Promise<IntermediateValue>;
168
+ /**
169
+ * Writes an override. `enforced` decides its direction: an enforced
170
+ * override beats the user's own value, an advisory one yields to it.
171
+ */
172
+ set: (settingId: string, groupId: string, value: SettingValue, options?: GroupValueOptions) => Promise<IntermediateValue>;
173
+ clear: (settingId: string, groupId: string, options?: RequestOptions) => Promise<void>;
174
+ };
175
+ };
176
+ readonly groups: {
177
+ list: (options?: RequestOptions & {
178
+ environment?: string;
179
+ includeEphemeral?: boolean;
180
+ }) => Promise<Group[]>;
181
+ get: (id: string, options?: RequestOptions) => Promise<Group>;
182
+ create: (input: CreateGroupInput, options?: RequestOptions) => Promise<Group>;
183
+ update: (id: string, input: {
184
+ description?: string;
185
+ priority?: number;
186
+ }, options?: RequestOptions) => Promise<Group>;
187
+ delete: (id: string, options?: RequestOptions) => Promise<void>;
188
+ /** Everyone whose membership is saved. Ad-hoc application does not appear here. */
189
+ members: (id: string, options?: RequestOptions) => Promise<GroupMember[]>;
190
+ addMembers: (id: string, userIds: string[], options?: RequestOptions) => Promise<number>;
191
+ removeMembers: (id: string, userIds: string[], options?: RequestOptions) => Promise<number>;
192
+ };
193
+ readonly taxonomy: {
194
+ roles: (options?: RequestOptions) => Promise<Role[]>;
195
+ /** Creates or updates a role. Rank orders roles and may not exceed the key's. */
196
+ upsertRole: (name: string, input: {
197
+ rank: number;
198
+ description?: string;
199
+ }, options?: RequestOptions) => Promise<Role>;
200
+ deleteRole: (name: string, options?: RequestOptions) => Promise<void>;
201
+ platforms: (options?: RequestOptions) => Promise<Platform[]>;
202
+ upsertPlatform: (name: string, description?: string, options?: RequestOptions) => Promise<Platform>;
203
+ deletePlatform: (name: string, options?: RequestOptions) => Promise<void>;
204
+ environments: (options?: RequestOptions) => Promise<Environment[]>;
205
+ upsertEnvironment: (name: string, description?: string, options?: RequestOptions) => Promise<Environment>;
206
+ deleteEnvironment: (name: string, options?: RequestOptions) => Promise<void>;
207
+ };
208
+ readonly keys: {
209
+ list: (options?: RequestOptions & {
210
+ includeRevoked?: boolean;
211
+ }) => Promise<ApiKey[]>;
212
+ /**
213
+ * Mints a key. The returned `token` is the only time it is ever available:
214
+ * only its hash is stored.
215
+ */
216
+ create: (input: CreateKeyInput, options?: RequestOptions) => Promise<CreatedApiKey>;
217
+ /** Revokes a key. This is permanent and takes effect immediately. */
218
+ revoke: (id: string, options?: RequestOptions) => Promise<void>;
219
+ };
220
+ }
221
+ export type { FetchLike, RequestOptions };
222
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Normalises a value to the canonical UTC instant the server stores.
3
+ *
4
+ * A `Date` or an epoch milliseconds number is unambiguous and converts freely.
5
+ * A string must already carry an offset: a bare `2026-01-02T15:04` means a
6
+ * different moment in every timezone, so guessing one would be a bug waiting
7
+ * to happen. Use {@link localToInstant} to state that assumption explicitly.
8
+ *
9
+ * @example
10
+ * toInstant(new Date()) // "2026-01-02T20:04:05.250Z"
11
+ * toInstant("2026-01-02T15:04:05-05:00") // "2026-01-02T20:04:05.000Z"
12
+ */
13
+ export declare function toInstant(value: Date | string | number): string;
14
+ /**
15
+ * Reads a `datetime-local` input in a named timezone and returns the instant.
16
+ *
17
+ * Passing the zone makes the assumption visible at the call site, which is the
18
+ * whole point: the same reading is a different moment in each zone.
19
+ *
20
+ * @param local A `datetime-local` value such as `2026-01-02T15:04`.
21
+ * @param timeZone An IANA zone. Defaults to the runtime's own.
22
+ *
23
+ * @example
24
+ * localToInstant("2026-01-02T15:04", "America/New_York") // "2026-01-02T20:04:00.000Z"
25
+ */
26
+ export declare function localToInstant(local: string, timeZone?: string): string;
27
+ /**
28
+ * Parses a value returned by the API into a `Date`.
29
+ *
30
+ * Returns `undefined` for null or undefined, so an unset `DATETIME` reads
31
+ * naturally without a guard at every call site.
32
+ */
33
+ export declare function parseInstant(value: unknown): Date | undefined;
34
+ /** Reports whether a string is something the server will accept as a DATETIME. */
35
+ export declare function isInstant(value: unknown): value is string;
36
+ /**
37
+ * Renders an instant for a `<input type="datetime-local">`, which is the
38
+ * inverse of {@link localToInstant} and equally zone-dependent.
39
+ */
40
+ export declare function toDateTimeLocal(value: Date | string | number, timeZone?: string): string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Normalises a value to the canonical UTC instant the server stores.
3
+ *
4
+ * A `Date` or an epoch milliseconds number is unambiguous and converts freely.
5
+ * A string must already carry an offset: a bare `2026-01-02T15:04` means a
6
+ * different moment in every timezone, so guessing one would be a bug waiting
7
+ * to happen. Use {@link localToInstant} to state that assumption explicitly.
8
+ *
9
+ * @example
10
+ * toInstant(new Date()) // "2026-01-02T20:04:05.250Z"
11
+ * toInstant("2026-01-02T15:04:05-05:00") // "2026-01-02T20:04:05.000Z"
12
+ */
13
+ export declare function toInstant(value: Date | string | number): string;
14
+ /**
15
+ * Reads a `datetime-local` input in a named timezone and returns the instant.
16
+ *
17
+ * Passing the zone makes the assumption visible at the call site, which is the
18
+ * whole point: the same reading is a different moment in each zone.
19
+ *
20
+ * @param local A `datetime-local` value such as `2026-01-02T15:04`.
21
+ * @param timeZone An IANA zone. Defaults to the runtime's own.
22
+ *
23
+ * @example
24
+ * localToInstant("2026-01-02T15:04", "America/New_York") // "2026-01-02T20:04:00.000Z"
25
+ */
26
+ export declare function localToInstant(local: string, timeZone?: string): string;
27
+ /**
28
+ * Parses a value returned by the API into a `Date`.
29
+ *
30
+ * Returns `undefined` for null or undefined, so an unset `DATETIME` reads
31
+ * naturally without a guard at every call site.
32
+ */
33
+ export declare function parseInstant(value: unknown): Date | undefined;
34
+ /** Reports whether a string is something the server will accept as a DATETIME. */
35
+ export declare function isInstant(value: unknown): value is string;
36
+ /**
37
+ * Renders an instant for a `<input type="datetime-local">`, which is the
38
+ * inverse of {@link localToInstant} and equally zone-dependent.
39
+ */
40
+ export declare function toDateTimeLocal(value: Date | string | number, timeZone?: string): string;
41
+ //# sourceMappingURL=datetime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datetime.d.ts","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAkC/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBvE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAY7D;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAMxF"}
@@ -0,0 +1,61 @@
1
+ import type { ErrorCode } from "./types.cjs";
2
+ /**
3
+ * Codes this SDK raises on top of the ones the server defines. They occupy the
4
+ * same `code` field so a single `catch` can branch on one value.
5
+ */
6
+ export type ClientErrorCode =
7
+ /** The request never got a response: DNS, TLS, CORS, offline. */
8
+ "network_error"
9
+ /** The request exceeded `timeoutMs`, or its signal was aborted. */
10
+ | "timeout"
11
+ /** The caller aborted the request through an `AbortSignal`. */
12
+ | "aborted"
13
+ /** A response arrived but was not the JSON this SDK expected. */
14
+ | "invalid_response"
15
+ /** A typed accessor was used on a setting of a different type. */
16
+ | "type_mismatch"
17
+ /** A value was rejected before it was ever sent. */
18
+ | "invalid_value";
19
+ /** Every code an {@link AppSettingsError} can carry. */
20
+ export type AnyErrorCode = ErrorCode | ClientErrorCode;
21
+ /**
22
+ * The single error type this SDK throws.
23
+ *
24
+ * Branch on {@link AppSettingsError.code}, which is stable, rather than on the
25
+ * message, which is written for a human.
26
+ */
27
+ export declare class AppSettingsError extends Error {
28
+ readonly name = "AppSettingsError";
29
+ /** A stable identifier for the failure. */
30
+ readonly code: AnyErrorCode;
31
+ /** The HTTP status, when the failure came from a response. */
32
+ readonly status?: number;
33
+ /** The server's `X-Request-ID`, worth quoting in a bug report. */
34
+ readonly requestId?: string;
35
+ /** The request that failed, as `GET /api/v1/settings`. */
36
+ readonly request?: string;
37
+ /** The parsed response body, when there was one. */
38
+ readonly body?: unknown;
39
+ constructor(message: string, options: {
40
+ code: AnyErrorCode;
41
+ status?: number;
42
+ requestId?: string;
43
+ request?: string;
44
+ body?: unknown;
45
+ cause?: unknown;
46
+ });
47
+ /** Whether the failure is worth trying again unchanged. */
48
+ get retryable(): boolean;
49
+ /** Narrows an unknown caught value to this class. */
50
+ static is(error: unknown): error is AppSettingsError;
51
+ }
52
+ /** The requested thing does not exist, or this key may not see that it does. */
53
+ export declare const isNotFound: (error: unknown) => boolean;
54
+ /** The API key was missing, malformed, expired or revoked. */
55
+ export declare const isUnauthorized: (error: unknown) => boolean;
56
+ /** The key is real but is fenced out of what it asked for. */
57
+ export declare const isForbidden: (error: unknown) => boolean;
58
+ /** The request collided with existing state, such as a duplicate name. */
59
+ export declare const isConflict: (error: unknown) => boolean;
60
+ /** The request was malformed or a value failed validation. */
61
+ export declare const isInvalidRequest: (error: unknown) => boolean;
@@ -0,0 +1,62 @@
1
+ import type { ErrorCode } from "./types.js";
2
+ /**
3
+ * Codes this SDK raises on top of the ones the server defines. They occupy the
4
+ * same `code` field so a single `catch` can branch on one value.
5
+ */
6
+ export type ClientErrorCode =
7
+ /** The request never got a response: DNS, TLS, CORS, offline. */
8
+ "network_error"
9
+ /** The request exceeded `timeoutMs`, or its signal was aborted. */
10
+ | "timeout"
11
+ /** The caller aborted the request through an `AbortSignal`. */
12
+ | "aborted"
13
+ /** A response arrived but was not the JSON this SDK expected. */
14
+ | "invalid_response"
15
+ /** A typed accessor was used on a setting of a different type. */
16
+ | "type_mismatch"
17
+ /** A value was rejected before it was ever sent. */
18
+ | "invalid_value";
19
+ /** Every code an {@link AppSettingsError} can carry. */
20
+ export type AnyErrorCode = ErrorCode | ClientErrorCode;
21
+ /**
22
+ * The single error type this SDK throws.
23
+ *
24
+ * Branch on {@link AppSettingsError.code}, which is stable, rather than on the
25
+ * message, which is written for a human.
26
+ */
27
+ export declare class AppSettingsError extends Error {
28
+ readonly name = "AppSettingsError";
29
+ /** A stable identifier for the failure. */
30
+ readonly code: AnyErrorCode;
31
+ /** The HTTP status, when the failure came from a response. */
32
+ readonly status?: number;
33
+ /** The server's `X-Request-ID`, worth quoting in a bug report. */
34
+ readonly requestId?: string;
35
+ /** The request that failed, as `GET /api/v1/settings`. */
36
+ readonly request?: string;
37
+ /** The parsed response body, when there was one. */
38
+ readonly body?: unknown;
39
+ constructor(message: string, options: {
40
+ code: AnyErrorCode;
41
+ status?: number;
42
+ requestId?: string;
43
+ request?: string;
44
+ body?: unknown;
45
+ cause?: unknown;
46
+ });
47
+ /** Whether the failure is worth trying again unchanged. */
48
+ get retryable(): boolean;
49
+ /** Narrows an unknown caught value to this class. */
50
+ static is(error: unknown): error is AppSettingsError;
51
+ }
52
+ /** The requested thing does not exist, or this key may not see that it does. */
53
+ export declare const isNotFound: (error: unknown) => boolean;
54
+ /** The API key was missing, malformed, expired or revoked. */
55
+ export declare const isUnauthorized: (error: unknown) => boolean;
56
+ /** The key is real but is fenced out of what it asked for. */
57
+ export declare const isForbidden: (error: unknown) => boolean;
58
+ /** The request collided with existing state, such as a duplicate name. */
59
+ export declare const isConflict: (error: unknown) => boolean;
60
+ /** The request was malformed or a value failed validation. */
61
+ export declare const isInvalidRequest: (error: unknown) => boolean;
62
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,eAAe;AACzB,iEAAiE;AAC/D,eAAe;AACjB,mEAAmE;GACjE,SAAS;AACX,+DAA+D;GAC7D,SAAS;AACX,iEAAiE;GAC/D,kBAAkB;AACpB,kEAAkE;GAChE,eAAe;AACjB,oDAAoD;GAClD,eAAe,CAAC;AAEpB,wDAAwD;AACxD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,eAAe,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAkB,IAAI,sBAAsB;IAE5C,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAExB,YACE,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QACP,IAAI,EAAE,YAAY,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,EAQF;IAED,2DAA2D;IAC3D,IAAI,SAAS,IAAI,OAAO,CAKvB;IAED,qDAAqD;IACrD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAEnD;CACF;AAED,gFAAgF;AAChF,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,OAAqC,CAAC;AAElF,8DAA8D;AAC9D,eAAO,MAAM,cAAc,UAAW,OAAO,KAAG,OAAwC,CAAC;AAEzF,8DAA8D;AAC9D,eAAO,MAAM,WAAW,UAAW,OAAO,KAAG,OAAqC,CAAC;AAEnF,0EAA0E;AAC1E,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,OAAoC,CAAC;AAEjF,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,UAAW,OAAO,KAAG,OACkB,CAAC"}
@@ -0,0 +1,62 @@
1
+ /** The subset of `fetch` this SDK uses, so any compatible implementation fits. */
2
+ export type FetchLike = (input: string, init: RequestInit) => Promise<Response>;
3
+ /** A value that can appear in a query string. Arrays become repeated keys. */
4
+ export type QueryValue = string | number | boolean | string[] | undefined | null;
5
+ /** Per-call options accepted by every method on the client. */
6
+ export interface RequestOptions {
7
+ /** Cancels the request, and any retry still pending. */
8
+ signal?: AbortSignal;
9
+ /** Overrides the client's `timeoutMs` for this call. */
10
+ timeoutMs?: number;
11
+ /** Extra headers, merged over the client's own. */
12
+ headers?: Record<string, string>;
13
+ }
14
+ /** Everything the transport needs to make one call. */
15
+ export interface TransportConfig {
16
+ baseUrl: string;
17
+ apiKey: string;
18
+ fetch: FetchLike;
19
+ timeoutMs: number;
20
+ retries: number;
21
+ retryDelayMs: number;
22
+ headers: Record<string, string>;
23
+ userAgent?: string;
24
+ }
25
+ /** Options for building a client's transport. */
26
+ export interface TransportOptions {
27
+ /** Where the server lives, such as `https://settings.example.com`. */
28
+ baseUrl: string;
29
+ /** The API key sent as `Authorization: Bearer`. */
30
+ apiKey: string;
31
+ /** A `fetch` implementation. Defaults to the global one. */
32
+ fetch?: FetchLike;
33
+ /** How long one attempt may take. Defaults to 10000; 0 disables the timeout. */
34
+ timeoutMs?: number;
35
+ /** How many times to retry a retryable failure. Defaults to 2. */
36
+ retries?: number;
37
+ /** Base backoff between retries, doubled each time. Defaults to 200. */
38
+ retryDelayMs?: number;
39
+ /** Headers added to every request. */
40
+ headers?: Record<string, string>;
41
+ }
42
+ /**
43
+ * Builds the transport config, resolving defaults once so each request does no
44
+ * more work than it has to.
45
+ */
46
+ export declare function createTransport(options: TransportOptions): TransportConfig;
47
+ /** One request, before defaults and retries are applied. */
48
+ export interface RequestSpec {
49
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
50
+ path: string;
51
+ query?: Record<string, QueryValue>;
52
+ body?: unknown;
53
+ options?: RequestOptions;
54
+ }
55
+ /**
56
+ * Performs a request and decodes its body.
57
+ *
58
+ * Returns `undefined` for a 204, which is what every successful DELETE returns.
59
+ */
60
+ export declare function request<T>(config: TransportConfig, spec: RequestSpec): Promise<T>;
61
+ /** Renders a query string, repeating a key for each element of an array. */
62
+ export declare function buildQuery(query: Record<string, QueryValue> | undefined): string;
package/dist/http.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ /** The subset of `fetch` this SDK uses, so any compatible implementation fits. */
2
+ export type FetchLike = (input: string, init: RequestInit) => Promise<Response>;
3
+ /** A value that can appear in a query string. Arrays become repeated keys. */
4
+ export type QueryValue = string | number | boolean | string[] | undefined | null;
5
+ /** Per-call options accepted by every method on the client. */
6
+ export interface RequestOptions {
7
+ /** Cancels the request, and any retry still pending. */
8
+ signal?: AbortSignal;
9
+ /** Overrides the client's `timeoutMs` for this call. */
10
+ timeoutMs?: number;
11
+ /** Extra headers, merged over the client's own. */
12
+ headers?: Record<string, string>;
13
+ }
14
+ /** Everything the transport needs to make one call. */
15
+ export interface TransportConfig {
16
+ baseUrl: string;
17
+ apiKey: string;
18
+ fetch: FetchLike;
19
+ timeoutMs: number;
20
+ retries: number;
21
+ retryDelayMs: number;
22
+ headers: Record<string, string>;
23
+ userAgent?: string;
24
+ }
25
+ /** Options for building a client's transport. */
26
+ export interface TransportOptions {
27
+ /** Where the server lives, such as `https://settings.example.com`. */
28
+ baseUrl: string;
29
+ /** The API key sent as `Authorization: Bearer`. */
30
+ apiKey: string;
31
+ /** A `fetch` implementation. Defaults to the global one. */
32
+ fetch?: FetchLike;
33
+ /** How long one attempt may take. Defaults to 10000; 0 disables the timeout. */
34
+ timeoutMs?: number;
35
+ /** How many times to retry a retryable failure. Defaults to 2. */
36
+ retries?: number;
37
+ /** Base backoff between retries, doubled each time. Defaults to 200. */
38
+ retryDelayMs?: number;
39
+ /** Headers added to every request. */
40
+ headers?: Record<string, string>;
41
+ }
42
+ /**
43
+ * Builds the transport config, resolving defaults once so each request does no
44
+ * more work than it has to.
45
+ */
46
+ export declare function createTransport(options: TransportOptions): TransportConfig;
47
+ /** One request, before defaults and retries are applied. */
48
+ export interface RequestSpec {
49
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
50
+ path: string;
51
+ query?: Record<string, QueryValue>;
52
+ body?: unknown;
53
+ options?: RequestOptions;
54
+ }
55
+ /**
56
+ * Performs a request and decodes its body.
57
+ *
58
+ * Returns `undefined` for a 204, which is what every successful DELETE returns.
59
+ */
60
+ export declare function request<T>(config: TransportConfig, spec: RequestSpec): Promise<T>;
61
+ /** Renders a query string, repeating a key for each element of an array. */
62
+ export declare function buildQuery(query: Record<string, QueryValue> | undefined): string;
63
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEhF,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC;AAEjF,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe,CA8B1E;AAED,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAkDvF;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,GAAG,MAAM,CAehF"}