@shipeasy/devtools-core 1.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/LICENSE +40 -0
- package/README.md +19 -0
- package/dist/api.d.mts +143 -0
- package/dist/api.d.ts +143 -0
- package/dist/api.js +10 -0
- package/dist/api.mjs +10 -0
- package/dist/auth.d.mts +57 -0
- package/dist/auth.d.ts +57 -0
- package/dist/auth.js +12 -0
- package/dist/auth.mjs +12 -0
- package/dist/chunk-2B75JLTX.js +25 -0
- package/dist/chunk-5VQLXGNR.mjs +101 -0
- package/dist/chunk-6SU5REDK.mjs +2848 -0
- package/dist/chunk-7NO55TFC.mjs +25 -0
- package/dist/chunk-GUTBZI2O.js +2848 -0
- package/dist/chunk-I2KLQ2HA.js +22 -0
- package/dist/chunk-KPO3R7ND.js +28 -0
- package/dist/chunk-LCRTLHGL.js +101 -0
- package/dist/chunk-LZDNBMH6.mjs +1394 -0
- package/dist/chunk-MK56AKZR.js +178 -0
- package/dist/chunk-RANNOC72.js +72 -0
- package/dist/chunk-RH6X24HG.mjs +28 -0
- package/dist/chunk-U5DDQYSF.mjs +22 -0
- package/dist/chunk-VHYOR3XA.mjs +178 -0
- package/dist/chunk-WP2TWHG3.mjs +72 -0
- package/dist/chunk-YQHCUZGP.js +1394 -0
- package/dist/feedback-state.d.mts +29 -0
- package/dist/feedback-state.d.ts +29 -0
- package/dist/feedback-state.js +9 -0
- package/dist/feedback-state.mjs +9 -0
- package/dist/forms.d.mts +95 -0
- package/dist/forms.d.ts +95 -0
- package/dist/forms.js +21 -0
- package/dist/forms.mjs +21 -0
- package/dist/gate-flow.d.mts +41 -0
- package/dist/gate-flow.d.ts +41 -0
- package/dist/gate-flow.js +17 -0
- package/dist/gate-flow.mjs +17 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +86 -0
- package/dist/index.mjs +86 -0
- package/dist/public-report.d.mts +56 -0
- package/dist/public-report.d.ts +56 -0
- package/dist/public-report.js +12 -0
- package/dist/public-report.mjs +12 -0
- package/dist/self-report.d.mts +25 -0
- package/dist/self-report.d.ts +25 -0
- package/dist/self-report.js +73 -0
- package/dist/self-report.mjs +73 -0
- package/dist/types-D0sroUDD.d.mts +278 -0
- package/dist/types-D0sroUDD.d.ts +278 -0
- package/dist/zod.gen-DTKpgw-T.d.mts +64 -0
- package/dist/zod.gen-DTKpgw-T.d.ts +64 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1394 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_ADMIN_BASE_URL,
|
|
3
|
+
PERMISSIVE_CONFIG_SCHEMA,
|
|
4
|
+
types_exports
|
|
5
|
+
} from "./chunk-RH6X24HG.mjs";
|
|
6
|
+
|
|
7
|
+
// src/generated/core/bodySerializer.gen.ts
|
|
8
|
+
var jsonBodySerializer = {
|
|
9
|
+
bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/generated/core/params.gen.ts
|
|
13
|
+
var extraPrefixesMap = {
|
|
14
|
+
$body_: "body",
|
|
15
|
+
$headers_: "headers",
|
|
16
|
+
$path_: "path",
|
|
17
|
+
$query_: "query"
|
|
18
|
+
};
|
|
19
|
+
var extraPrefixes = Object.entries(extraPrefixesMap);
|
|
20
|
+
|
|
21
|
+
// src/generated/core/serverSentEvents.gen.ts
|
|
22
|
+
function createSseClient({
|
|
23
|
+
onRequest,
|
|
24
|
+
onSseError,
|
|
25
|
+
onSseEvent,
|
|
26
|
+
responseTransformer,
|
|
27
|
+
responseValidator,
|
|
28
|
+
sseDefaultRetryDelay,
|
|
29
|
+
sseMaxRetryAttempts,
|
|
30
|
+
sseMaxRetryDelay,
|
|
31
|
+
sseSleepFn,
|
|
32
|
+
url,
|
|
33
|
+
...options
|
|
34
|
+
}) {
|
|
35
|
+
let lastEventId;
|
|
36
|
+
const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
37
|
+
const createStream = async function* () {
|
|
38
|
+
let retryDelay = sseDefaultRetryDelay ?? 3e3;
|
|
39
|
+
let attempt = 0;
|
|
40
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
41
|
+
while (true) {
|
|
42
|
+
if (signal.aborted) break;
|
|
43
|
+
attempt++;
|
|
44
|
+
const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
|
|
45
|
+
if (lastEventId !== void 0) {
|
|
46
|
+
headers.set("Last-Event-ID", lastEventId);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const requestInit = {
|
|
50
|
+
redirect: "follow",
|
|
51
|
+
...options,
|
|
52
|
+
body: options.serializedBody,
|
|
53
|
+
headers,
|
|
54
|
+
signal
|
|
55
|
+
};
|
|
56
|
+
let request = new Request(url, requestInit);
|
|
57
|
+
if (onRequest) {
|
|
58
|
+
request = await onRequest(url, requestInit);
|
|
59
|
+
}
|
|
60
|
+
const _fetch = options.fetch ?? globalThis.fetch;
|
|
61
|
+
const response = await _fetch(request);
|
|
62
|
+
if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
|
|
63
|
+
if (!response.body) throw new Error("No body in SSE response");
|
|
64
|
+
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
65
|
+
let buffer = "";
|
|
66
|
+
const abortHandler = () => {
|
|
67
|
+
try {
|
|
68
|
+
reader.cancel();
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
signal.addEventListener("abort", abortHandler);
|
|
73
|
+
try {
|
|
74
|
+
while (true) {
|
|
75
|
+
const { done, value } = await reader.read();
|
|
76
|
+
if (done) break;
|
|
77
|
+
buffer += value;
|
|
78
|
+
buffer = buffer.replace(/\r\n?/g, "\n");
|
|
79
|
+
const chunks = buffer.split("\n\n");
|
|
80
|
+
buffer = chunks.pop() ?? "";
|
|
81
|
+
for (const chunk of chunks) {
|
|
82
|
+
const lines = chunk.split("\n");
|
|
83
|
+
const dataLines = [];
|
|
84
|
+
let eventName;
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
if (line.startsWith("data:")) {
|
|
87
|
+
dataLines.push(line.replace(/^data:\s*/, ""));
|
|
88
|
+
} else if (line.startsWith("event:")) {
|
|
89
|
+
eventName = line.replace(/^event:\s*/, "");
|
|
90
|
+
} else if (line.startsWith("id:")) {
|
|
91
|
+
lastEventId = line.replace(/^id:\s*/, "");
|
|
92
|
+
} else if (line.startsWith("retry:")) {
|
|
93
|
+
const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
|
|
94
|
+
if (!Number.isNaN(parsed)) {
|
|
95
|
+
retryDelay = parsed;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let data;
|
|
100
|
+
let parsedJson = false;
|
|
101
|
+
if (dataLines.length) {
|
|
102
|
+
const rawData = dataLines.join("\n");
|
|
103
|
+
try {
|
|
104
|
+
data = JSON.parse(rawData);
|
|
105
|
+
parsedJson = true;
|
|
106
|
+
} catch {
|
|
107
|
+
data = rawData;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (parsedJson) {
|
|
111
|
+
if (responseValidator) {
|
|
112
|
+
await responseValidator(data);
|
|
113
|
+
}
|
|
114
|
+
if (responseTransformer) {
|
|
115
|
+
data = await responseTransformer(data);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
onSseEvent?.({
|
|
119
|
+
data,
|
|
120
|
+
event: eventName,
|
|
121
|
+
id: lastEventId,
|
|
122
|
+
retry: retryDelay
|
|
123
|
+
});
|
|
124
|
+
if (dataLines.length) {
|
|
125
|
+
yield data;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} finally {
|
|
130
|
+
signal.removeEventListener("abort", abortHandler);
|
|
131
|
+
reader.releaseLock();
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
onSseError?.(error);
|
|
136
|
+
if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4);
|
|
140
|
+
await sleep(backoff);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const stream = createStream();
|
|
145
|
+
return { stream };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/generated/core/pathSerializer.gen.ts
|
|
149
|
+
var separatorArrayExplode = (style) => {
|
|
150
|
+
switch (style) {
|
|
151
|
+
case "label":
|
|
152
|
+
return ".";
|
|
153
|
+
case "matrix":
|
|
154
|
+
return ";";
|
|
155
|
+
case "simple":
|
|
156
|
+
return ",";
|
|
157
|
+
default:
|
|
158
|
+
return "&";
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var separatorArrayNoExplode = (style) => {
|
|
162
|
+
switch (style) {
|
|
163
|
+
case "form":
|
|
164
|
+
return ",";
|
|
165
|
+
case "pipeDelimited":
|
|
166
|
+
return "|";
|
|
167
|
+
case "spaceDelimited":
|
|
168
|
+
return "%20";
|
|
169
|
+
default:
|
|
170
|
+
return ",";
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
var separatorObjectExplode = (style) => {
|
|
174
|
+
switch (style) {
|
|
175
|
+
case "label":
|
|
176
|
+
return ".";
|
|
177
|
+
case "matrix":
|
|
178
|
+
return ";";
|
|
179
|
+
case "simple":
|
|
180
|
+
return ",";
|
|
181
|
+
default:
|
|
182
|
+
return "&";
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var serializeArrayParam = ({
|
|
186
|
+
allowReserved,
|
|
187
|
+
explode,
|
|
188
|
+
name,
|
|
189
|
+
style,
|
|
190
|
+
value
|
|
191
|
+
}) => {
|
|
192
|
+
if (!explode) {
|
|
193
|
+
const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
|
|
194
|
+
switch (style) {
|
|
195
|
+
case "label":
|
|
196
|
+
return `.${joinedValues2}`;
|
|
197
|
+
case "matrix":
|
|
198
|
+
return `;${name}=${joinedValues2}`;
|
|
199
|
+
case "simple":
|
|
200
|
+
return joinedValues2;
|
|
201
|
+
default:
|
|
202
|
+
return `${name}=${joinedValues2}`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const separator = separatorArrayExplode(style);
|
|
206
|
+
const joinedValues = value.map((v) => {
|
|
207
|
+
if (style === "label" || style === "simple") {
|
|
208
|
+
return allowReserved ? v : encodeURIComponent(v);
|
|
209
|
+
}
|
|
210
|
+
return serializePrimitiveParam({
|
|
211
|
+
allowReserved,
|
|
212
|
+
name,
|
|
213
|
+
value: v
|
|
214
|
+
});
|
|
215
|
+
}).join(separator);
|
|
216
|
+
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
217
|
+
};
|
|
218
|
+
var serializePrimitiveParam = ({
|
|
219
|
+
allowReserved,
|
|
220
|
+
name,
|
|
221
|
+
value
|
|
222
|
+
}) => {
|
|
223
|
+
if (value === void 0 || value === null) {
|
|
224
|
+
return "";
|
|
225
|
+
}
|
|
226
|
+
if (typeof value === "object") {
|
|
227
|
+
throw new Error(
|
|
228
|
+
"Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
|
|
232
|
+
};
|
|
233
|
+
var serializeObjectParam = ({
|
|
234
|
+
allowReserved,
|
|
235
|
+
explode,
|
|
236
|
+
name,
|
|
237
|
+
style,
|
|
238
|
+
value,
|
|
239
|
+
valueOnly
|
|
240
|
+
}) => {
|
|
241
|
+
if (value instanceof Date) {
|
|
242
|
+
return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
|
|
243
|
+
}
|
|
244
|
+
if (style !== "deepObject" && !explode) {
|
|
245
|
+
let values = [];
|
|
246
|
+
Object.entries(value).forEach(([key, v]) => {
|
|
247
|
+
values = [...values, key, allowReserved ? v : encodeURIComponent(v)];
|
|
248
|
+
});
|
|
249
|
+
const joinedValues2 = values.join(",");
|
|
250
|
+
switch (style) {
|
|
251
|
+
case "form":
|
|
252
|
+
return `${name}=${joinedValues2}`;
|
|
253
|
+
case "label":
|
|
254
|
+
return `.${joinedValues2}`;
|
|
255
|
+
case "matrix":
|
|
256
|
+
return `;${name}=${joinedValues2}`;
|
|
257
|
+
default:
|
|
258
|
+
return joinedValues2;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const separator = separatorObjectExplode(style);
|
|
262
|
+
const joinedValues = Object.entries(value).map(
|
|
263
|
+
([key, v]) => serializePrimitiveParam({
|
|
264
|
+
allowReserved,
|
|
265
|
+
name: style === "deepObject" ? `${name}[${key}]` : key,
|
|
266
|
+
value: v
|
|
267
|
+
})
|
|
268
|
+
).join(separator);
|
|
269
|
+
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// src/generated/core/utils.gen.ts
|
|
273
|
+
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
274
|
+
var defaultPathSerializer = ({ path, url: _url }) => {
|
|
275
|
+
let url = _url;
|
|
276
|
+
const matches = _url.match(PATH_PARAM_RE);
|
|
277
|
+
if (matches) {
|
|
278
|
+
for (const match of matches) {
|
|
279
|
+
let explode = false;
|
|
280
|
+
let name = match.substring(1, match.length - 1);
|
|
281
|
+
let style = "simple";
|
|
282
|
+
if (name.endsWith("*")) {
|
|
283
|
+
explode = true;
|
|
284
|
+
name = name.substring(0, name.length - 1);
|
|
285
|
+
}
|
|
286
|
+
if (name.startsWith(".")) {
|
|
287
|
+
name = name.substring(1);
|
|
288
|
+
style = "label";
|
|
289
|
+
} else if (name.startsWith(";")) {
|
|
290
|
+
name = name.substring(1);
|
|
291
|
+
style = "matrix";
|
|
292
|
+
}
|
|
293
|
+
const value = path[name];
|
|
294
|
+
if (value === void 0 || value === null) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (Array.isArray(value)) {
|
|
298
|
+
url = url.replace(match, serializeArrayParam({ explode, name, style, value }));
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (typeof value === "object") {
|
|
302
|
+
url = url.replace(
|
|
303
|
+
match,
|
|
304
|
+
serializeObjectParam({
|
|
305
|
+
explode,
|
|
306
|
+
name,
|
|
307
|
+
style,
|
|
308
|
+
value,
|
|
309
|
+
valueOnly: true
|
|
310
|
+
})
|
|
311
|
+
);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (style === "matrix") {
|
|
315
|
+
url = url.replace(
|
|
316
|
+
match,
|
|
317
|
+
`;${serializePrimitiveParam({
|
|
318
|
+
name,
|
|
319
|
+
value
|
|
320
|
+
})}`
|
|
321
|
+
);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const replaceValue = encodeURIComponent(
|
|
325
|
+
style === "label" ? `.${value}` : value
|
|
326
|
+
);
|
|
327
|
+
url = url.replace(match, replaceValue);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return url;
|
|
331
|
+
};
|
|
332
|
+
var getUrl = ({
|
|
333
|
+
baseUrl,
|
|
334
|
+
path,
|
|
335
|
+
query,
|
|
336
|
+
querySerializer,
|
|
337
|
+
url: _url
|
|
338
|
+
}) => {
|
|
339
|
+
const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
|
|
340
|
+
let url = (baseUrl ?? "") + pathUrl;
|
|
341
|
+
if (path) {
|
|
342
|
+
url = defaultPathSerializer({ path, url });
|
|
343
|
+
}
|
|
344
|
+
let search = query ? querySerializer(query) : "";
|
|
345
|
+
if (search.startsWith("?")) {
|
|
346
|
+
search = search.substring(1);
|
|
347
|
+
}
|
|
348
|
+
if (search) {
|
|
349
|
+
url += `?${search}`;
|
|
350
|
+
}
|
|
351
|
+
return url;
|
|
352
|
+
};
|
|
353
|
+
function getValidRequestBody(options) {
|
|
354
|
+
const hasBody = options.body !== void 0;
|
|
355
|
+
const isSerializedBody = hasBody && options.bodySerializer;
|
|
356
|
+
if (isSerializedBody) {
|
|
357
|
+
if ("serializedBody" in options) {
|
|
358
|
+
const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
|
|
359
|
+
return hasSerializedBody ? options.serializedBody : null;
|
|
360
|
+
}
|
|
361
|
+
return options.body !== "" ? options.body : null;
|
|
362
|
+
}
|
|
363
|
+
if (hasBody) {
|
|
364
|
+
return options.body;
|
|
365
|
+
}
|
|
366
|
+
return void 0;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/generated/core/auth.gen.ts
|
|
370
|
+
var getAuthToken = async (auth, callback) => {
|
|
371
|
+
const token = typeof callback === "function" ? await callback(auth) : callback;
|
|
372
|
+
if (!token) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (auth.scheme === "bearer") {
|
|
376
|
+
return `Bearer ${token}`;
|
|
377
|
+
}
|
|
378
|
+
if (auth.scheme === "basic") {
|
|
379
|
+
return `Basic ${btoa(token)}`;
|
|
380
|
+
}
|
|
381
|
+
return token;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// src/generated/client/utils.gen.ts
|
|
385
|
+
var createQuerySerializer = ({
|
|
386
|
+
parameters = {},
|
|
387
|
+
...args
|
|
388
|
+
} = {}) => {
|
|
389
|
+
const querySerializer = (queryParams) => {
|
|
390
|
+
const search = [];
|
|
391
|
+
if (queryParams && typeof queryParams === "object") {
|
|
392
|
+
for (const name in queryParams) {
|
|
393
|
+
const value = queryParams[name];
|
|
394
|
+
if (value === void 0 || value === null) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
const options = parameters[name] || args;
|
|
398
|
+
if (Array.isArray(value)) {
|
|
399
|
+
const serializedArray = serializeArrayParam({
|
|
400
|
+
allowReserved: options.allowReserved,
|
|
401
|
+
explode: true,
|
|
402
|
+
name,
|
|
403
|
+
style: "form",
|
|
404
|
+
value,
|
|
405
|
+
...options.array
|
|
406
|
+
});
|
|
407
|
+
if (serializedArray) search.push(serializedArray);
|
|
408
|
+
} else if (typeof value === "object") {
|
|
409
|
+
const serializedObject = serializeObjectParam({
|
|
410
|
+
allowReserved: options.allowReserved,
|
|
411
|
+
explode: true,
|
|
412
|
+
name,
|
|
413
|
+
style: "deepObject",
|
|
414
|
+
value,
|
|
415
|
+
...options.object
|
|
416
|
+
});
|
|
417
|
+
if (serializedObject) search.push(serializedObject);
|
|
418
|
+
} else {
|
|
419
|
+
const serializedPrimitive = serializePrimitiveParam({
|
|
420
|
+
allowReserved: options.allowReserved,
|
|
421
|
+
name,
|
|
422
|
+
value
|
|
423
|
+
});
|
|
424
|
+
if (serializedPrimitive) search.push(serializedPrimitive);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return search.join("&");
|
|
429
|
+
};
|
|
430
|
+
return querySerializer;
|
|
431
|
+
};
|
|
432
|
+
var getParseAs = (contentType) => {
|
|
433
|
+
if (!contentType) {
|
|
434
|
+
return "stream";
|
|
435
|
+
}
|
|
436
|
+
const cleanContent = contentType.split(";")[0]?.trim();
|
|
437
|
+
if (!cleanContent) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
|
|
441
|
+
return "json";
|
|
442
|
+
}
|
|
443
|
+
if (cleanContent === "multipart/form-data") {
|
|
444
|
+
return "formData";
|
|
445
|
+
}
|
|
446
|
+
if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
|
|
447
|
+
return "blob";
|
|
448
|
+
}
|
|
449
|
+
if (cleanContent.startsWith("text/")) {
|
|
450
|
+
return "text";
|
|
451
|
+
}
|
|
452
|
+
return;
|
|
453
|
+
};
|
|
454
|
+
var checkForExistence = (options, name) => {
|
|
455
|
+
if (!name) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
return false;
|
|
462
|
+
};
|
|
463
|
+
async function setAuthParams(options) {
|
|
464
|
+
for (const auth of options.security ?? []) {
|
|
465
|
+
if (checkForExistence(options, auth.name)) {
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
const token = await getAuthToken(auth, options.auth);
|
|
469
|
+
if (!token) {
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const name = auth.name ?? "Authorization";
|
|
473
|
+
switch (auth.in) {
|
|
474
|
+
case "query":
|
|
475
|
+
if (!options.query) {
|
|
476
|
+
options.query = {};
|
|
477
|
+
}
|
|
478
|
+
options.query[name] = token;
|
|
479
|
+
break;
|
|
480
|
+
case "cookie":
|
|
481
|
+
options.headers.append("Cookie", `${name}=${token}`);
|
|
482
|
+
break;
|
|
483
|
+
case "header":
|
|
484
|
+
default:
|
|
485
|
+
options.headers.set(name, token);
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
var buildUrl = (options) => getUrl({
|
|
491
|
+
baseUrl: options.baseUrl,
|
|
492
|
+
path: options.path,
|
|
493
|
+
query: options.query,
|
|
494
|
+
querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
|
|
495
|
+
url: options.url
|
|
496
|
+
});
|
|
497
|
+
var mergeConfigs = (a, b) => {
|
|
498
|
+
const config = { ...a, ...b };
|
|
499
|
+
if (config.baseUrl?.endsWith("/")) {
|
|
500
|
+
config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
|
|
501
|
+
}
|
|
502
|
+
config.headers = mergeHeaders(a.headers, b.headers);
|
|
503
|
+
return config;
|
|
504
|
+
};
|
|
505
|
+
var headersEntries = (headers) => {
|
|
506
|
+
const entries = [];
|
|
507
|
+
headers.forEach((value, key) => {
|
|
508
|
+
entries.push([key, value]);
|
|
509
|
+
});
|
|
510
|
+
return entries;
|
|
511
|
+
};
|
|
512
|
+
var mergeHeaders = (...headers) => {
|
|
513
|
+
const mergedHeaders = new Headers();
|
|
514
|
+
for (const header of headers) {
|
|
515
|
+
if (!header) {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
|
|
519
|
+
for (const [key, value] of iterator) {
|
|
520
|
+
if (value === null) {
|
|
521
|
+
mergedHeaders.delete(key);
|
|
522
|
+
} else if (Array.isArray(value)) {
|
|
523
|
+
for (const v of value) {
|
|
524
|
+
mergedHeaders.append(key, v);
|
|
525
|
+
}
|
|
526
|
+
} else if (value !== void 0) {
|
|
527
|
+
mergedHeaders.set(
|
|
528
|
+
key,
|
|
529
|
+
typeof value === "object" ? JSON.stringify(value) : value
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return mergedHeaders;
|
|
535
|
+
};
|
|
536
|
+
var Interceptors = class {
|
|
537
|
+
fns = [];
|
|
538
|
+
clear() {
|
|
539
|
+
this.fns = [];
|
|
540
|
+
}
|
|
541
|
+
eject(id) {
|
|
542
|
+
const index = this.getInterceptorIndex(id);
|
|
543
|
+
if (this.fns[index]) {
|
|
544
|
+
this.fns[index] = null;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
exists(id) {
|
|
548
|
+
const index = this.getInterceptorIndex(id);
|
|
549
|
+
return Boolean(this.fns[index]);
|
|
550
|
+
}
|
|
551
|
+
getInterceptorIndex(id) {
|
|
552
|
+
if (typeof id === "number") {
|
|
553
|
+
return this.fns[id] ? id : -1;
|
|
554
|
+
}
|
|
555
|
+
return this.fns.indexOf(id);
|
|
556
|
+
}
|
|
557
|
+
update(id, fn) {
|
|
558
|
+
const index = this.getInterceptorIndex(id);
|
|
559
|
+
if (this.fns[index]) {
|
|
560
|
+
this.fns[index] = fn;
|
|
561
|
+
return id;
|
|
562
|
+
}
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
use(fn) {
|
|
566
|
+
this.fns.push(fn);
|
|
567
|
+
return this.fns.length - 1;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
var createInterceptors = () => ({
|
|
571
|
+
error: new Interceptors(),
|
|
572
|
+
request: new Interceptors(),
|
|
573
|
+
response: new Interceptors()
|
|
574
|
+
});
|
|
575
|
+
var defaultQuerySerializer = createQuerySerializer({
|
|
576
|
+
allowReserved: false,
|
|
577
|
+
array: {
|
|
578
|
+
explode: true,
|
|
579
|
+
style: "form"
|
|
580
|
+
},
|
|
581
|
+
object: {
|
|
582
|
+
explode: true,
|
|
583
|
+
style: "deepObject"
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
var defaultHeaders = {
|
|
587
|
+
"Content-Type": "application/json"
|
|
588
|
+
};
|
|
589
|
+
var createConfig = (override = {}) => ({
|
|
590
|
+
...jsonBodySerializer,
|
|
591
|
+
headers: defaultHeaders,
|
|
592
|
+
parseAs: "auto",
|
|
593
|
+
querySerializer: defaultQuerySerializer,
|
|
594
|
+
...override
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// src/generated/client/client.gen.ts
|
|
598
|
+
var createClient = (config = {}) => {
|
|
599
|
+
let _config = mergeConfigs(createConfig(), config);
|
|
600
|
+
const getConfig2 = () => ({ ..._config });
|
|
601
|
+
const setConfig = (config2) => {
|
|
602
|
+
_config = mergeConfigs(_config, config2);
|
|
603
|
+
return getConfig2();
|
|
604
|
+
};
|
|
605
|
+
const interceptors = createInterceptors();
|
|
606
|
+
const beforeRequest = async (options) => {
|
|
607
|
+
const opts = {
|
|
608
|
+
..._config,
|
|
609
|
+
...options,
|
|
610
|
+
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
|
|
611
|
+
headers: mergeHeaders(_config.headers, options.headers),
|
|
612
|
+
serializedBody: void 0
|
|
613
|
+
};
|
|
614
|
+
if (opts.security) {
|
|
615
|
+
await setAuthParams(opts);
|
|
616
|
+
}
|
|
617
|
+
if (opts.requestValidator) {
|
|
618
|
+
await opts.requestValidator(opts);
|
|
619
|
+
}
|
|
620
|
+
if (opts.body !== void 0 && opts.bodySerializer) {
|
|
621
|
+
opts.serializedBody = opts.bodySerializer(opts.body);
|
|
622
|
+
}
|
|
623
|
+
if (opts.body === void 0 || opts.serializedBody === "") {
|
|
624
|
+
opts.headers.delete("Content-Type");
|
|
625
|
+
}
|
|
626
|
+
const resolvedOpts = opts;
|
|
627
|
+
const url = buildUrl(resolvedOpts);
|
|
628
|
+
return { opts: resolvedOpts, url };
|
|
629
|
+
};
|
|
630
|
+
const request = async (options) => {
|
|
631
|
+
const throwOnError = options.throwOnError ?? _config.throwOnError;
|
|
632
|
+
const responseStyle = options.responseStyle ?? _config.responseStyle;
|
|
633
|
+
let request2;
|
|
634
|
+
let response;
|
|
635
|
+
try {
|
|
636
|
+
const { opts, url } = await beforeRequest(options);
|
|
637
|
+
const requestInit = {
|
|
638
|
+
redirect: "follow",
|
|
639
|
+
...opts,
|
|
640
|
+
body: getValidRequestBody(opts)
|
|
641
|
+
};
|
|
642
|
+
request2 = new Request(url, requestInit);
|
|
643
|
+
for (const fn of interceptors.request.fns) {
|
|
644
|
+
if (fn) {
|
|
645
|
+
request2 = await fn(request2, opts);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const _fetch = opts.fetch;
|
|
649
|
+
response = await _fetch(request2);
|
|
650
|
+
for (const fn of interceptors.response.fns) {
|
|
651
|
+
if (fn) {
|
|
652
|
+
response = await fn(response, request2, opts);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const result = {
|
|
656
|
+
request: request2,
|
|
657
|
+
response
|
|
658
|
+
};
|
|
659
|
+
if (response.ok) {
|
|
660
|
+
const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
|
|
661
|
+
if (response.status === 204 || response.headers.get("Content-Length") === "0") {
|
|
662
|
+
let emptyData;
|
|
663
|
+
switch (parseAs) {
|
|
664
|
+
case "arrayBuffer":
|
|
665
|
+
case "blob":
|
|
666
|
+
case "text":
|
|
667
|
+
emptyData = await response[parseAs]();
|
|
668
|
+
break;
|
|
669
|
+
case "formData":
|
|
670
|
+
emptyData = new FormData();
|
|
671
|
+
break;
|
|
672
|
+
case "stream":
|
|
673
|
+
emptyData = response.body;
|
|
674
|
+
break;
|
|
675
|
+
case "json":
|
|
676
|
+
default:
|
|
677
|
+
emptyData = {};
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
return opts.responseStyle === "data" ? emptyData : {
|
|
681
|
+
data: emptyData,
|
|
682
|
+
...result
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
let data;
|
|
686
|
+
switch (parseAs) {
|
|
687
|
+
case "arrayBuffer":
|
|
688
|
+
case "blob":
|
|
689
|
+
case "formData":
|
|
690
|
+
case "text":
|
|
691
|
+
data = await response[parseAs]();
|
|
692
|
+
break;
|
|
693
|
+
case "json": {
|
|
694
|
+
const text = await response.text();
|
|
695
|
+
data = text ? JSON.parse(text) : {};
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
case "stream":
|
|
699
|
+
return opts.responseStyle === "data" ? response.body : {
|
|
700
|
+
data: response.body,
|
|
701
|
+
...result
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
if (parseAs === "json") {
|
|
705
|
+
if (opts.responseValidator) {
|
|
706
|
+
await opts.responseValidator(data);
|
|
707
|
+
}
|
|
708
|
+
if (opts.responseTransformer) {
|
|
709
|
+
data = await opts.responseTransformer(data);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return opts.responseStyle === "data" ? data : {
|
|
713
|
+
data,
|
|
714
|
+
...result
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
const textError = await response.text();
|
|
718
|
+
let jsonError;
|
|
719
|
+
try {
|
|
720
|
+
jsonError = JSON.parse(textError);
|
|
721
|
+
} catch {
|
|
722
|
+
}
|
|
723
|
+
throw jsonError ?? textError;
|
|
724
|
+
} catch (error) {
|
|
725
|
+
let finalError = error;
|
|
726
|
+
for (const fn of interceptors.error.fns) {
|
|
727
|
+
if (fn) {
|
|
728
|
+
finalError = await fn(finalError, response, request2, options);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
finalError = finalError || {};
|
|
732
|
+
if (throwOnError) {
|
|
733
|
+
throw finalError;
|
|
734
|
+
}
|
|
735
|
+
return responseStyle === "data" ? void 0 : {
|
|
736
|
+
error: finalError,
|
|
737
|
+
request: request2,
|
|
738
|
+
response
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
const makeMethodFn = (method) => (options) => request({ ...options, method });
|
|
743
|
+
const makeSseFn = (method) => async (options) => {
|
|
744
|
+
const { opts, url } = await beforeRequest(options);
|
|
745
|
+
return createSseClient({
|
|
746
|
+
...opts,
|
|
747
|
+
body: opts.body,
|
|
748
|
+
method,
|
|
749
|
+
onRequest: async (url2, init) => {
|
|
750
|
+
let request2 = new Request(url2, init);
|
|
751
|
+
for (const fn of interceptors.request.fns) {
|
|
752
|
+
if (fn) {
|
|
753
|
+
request2 = await fn(request2, opts);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return request2;
|
|
757
|
+
},
|
|
758
|
+
serializedBody: getValidRequestBody(opts),
|
|
759
|
+
url
|
|
760
|
+
});
|
|
761
|
+
};
|
|
762
|
+
const _buildUrl = (options) => buildUrl({ ..._config, ...options });
|
|
763
|
+
return {
|
|
764
|
+
buildUrl: _buildUrl,
|
|
765
|
+
connect: makeMethodFn("CONNECT"),
|
|
766
|
+
delete: makeMethodFn("DELETE"),
|
|
767
|
+
get: makeMethodFn("GET"),
|
|
768
|
+
getConfig: getConfig2,
|
|
769
|
+
head: makeMethodFn("HEAD"),
|
|
770
|
+
interceptors,
|
|
771
|
+
options: makeMethodFn("OPTIONS"),
|
|
772
|
+
patch: makeMethodFn("PATCH"),
|
|
773
|
+
post: makeMethodFn("POST"),
|
|
774
|
+
put: makeMethodFn("PUT"),
|
|
775
|
+
request,
|
|
776
|
+
setConfig,
|
|
777
|
+
sse: {
|
|
778
|
+
connect: makeSseFn("CONNECT"),
|
|
779
|
+
delete: makeSseFn("DELETE"),
|
|
780
|
+
get: makeSseFn("GET"),
|
|
781
|
+
head: makeSseFn("HEAD"),
|
|
782
|
+
options: makeSseFn("OPTIONS"),
|
|
783
|
+
patch: makeSseFn("PATCH"),
|
|
784
|
+
post: makeSseFn("POST"),
|
|
785
|
+
put: makeSseFn("PUT"),
|
|
786
|
+
trace: makeSseFn("TRACE")
|
|
787
|
+
},
|
|
788
|
+
trace: makeMethodFn("TRACE")
|
|
789
|
+
};
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
// src/generated/client.gen.ts
|
|
793
|
+
var client = createClient(createConfig({ baseUrl: "https://shipeasy.ai" }));
|
|
794
|
+
|
|
795
|
+
// src/generated/sdk.gen.ts
|
|
796
|
+
var listGates = (options) => (options?.client ?? client).get({
|
|
797
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
798
|
+
url: "/api/admin/gates",
|
|
799
|
+
...options
|
|
800
|
+
});
|
|
801
|
+
var listExperiments = (options) => (options?.client ?? client).get({
|
|
802
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
803
|
+
url: "/api/admin/experiments",
|
|
804
|
+
...options
|
|
805
|
+
});
|
|
806
|
+
var listConfigs = (options) => (options?.client ?? client).get({
|
|
807
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
808
|
+
url: "/api/admin/configs",
|
|
809
|
+
...options
|
|
810
|
+
});
|
|
811
|
+
var getConfig = (options) => (options.client ?? client).get({
|
|
812
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
813
|
+
url: "/api/admin/configs/{id}",
|
|
814
|
+
...options
|
|
815
|
+
});
|
|
816
|
+
var listUniverses = (options) => (options?.client ?? client).get({
|
|
817
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
818
|
+
url: "/api/admin/universes",
|
|
819
|
+
...options
|
|
820
|
+
});
|
|
821
|
+
var listOpsItems = (options) => (options?.client ?? client).get({
|
|
822
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
823
|
+
url: "/api/admin/ops",
|
|
824
|
+
...options
|
|
825
|
+
});
|
|
826
|
+
var createOpsItem = (options) => (options.client ?? client).post({
|
|
827
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
828
|
+
url: "/api/admin/ops",
|
|
829
|
+
...options,
|
|
830
|
+
headers: {
|
|
831
|
+
"Content-Type": "application/json",
|
|
832
|
+
...options.headers
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
var getOpsItem = (options) => (options.client ?? client).get({
|
|
836
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
837
|
+
url: "/api/admin/ops/{handle}",
|
|
838
|
+
...options
|
|
839
|
+
});
|
|
840
|
+
var updateOpsItem = (options) => (options.client ?? client).patch({
|
|
841
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
842
|
+
url: "/api/admin/ops/{handle}",
|
|
843
|
+
...options,
|
|
844
|
+
headers: {
|
|
845
|
+
"Content-Type": "application/json",
|
|
846
|
+
...options.headers
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
var listI18nProfiles = (options) => (options?.client ?? client).get({
|
|
850
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
851
|
+
url: "/api/admin/i18n/profiles",
|
|
852
|
+
...options
|
|
853
|
+
});
|
|
854
|
+
var listI18nKeys = (options) => (options?.client ?? client).get({
|
|
855
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
856
|
+
url: "/api/admin/i18n/keys",
|
|
857
|
+
...options
|
|
858
|
+
});
|
|
859
|
+
var updateI18nKey = (options) => (options.client ?? client).put({
|
|
860
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
861
|
+
url: "/api/admin/i18n/keys/{id}",
|
|
862
|
+
...options,
|
|
863
|
+
headers: {
|
|
864
|
+
"Content-Type": "application/json",
|
|
865
|
+
...options.headers
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
var listI18nDrafts = (options) => (options?.client ?? client).get({
|
|
869
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
870
|
+
url: "/api/admin/i18n/drafts",
|
|
871
|
+
...options
|
|
872
|
+
});
|
|
873
|
+
var createI18nDraft = (options) => (options.client ?? client).post({
|
|
874
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
875
|
+
url: "/api/admin/i18n/drafts",
|
|
876
|
+
...options,
|
|
877
|
+
headers: {
|
|
878
|
+
"Content-Type": "application/json",
|
|
879
|
+
...options.headers
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
// src/api.ts
|
|
884
|
+
var AuthError = class extends Error {
|
|
885
|
+
status = 401;
|
|
886
|
+
constructor(message) {
|
|
887
|
+
super(message);
|
|
888
|
+
this.name = "AuthError";
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
var DevtoolsClient = class {
|
|
892
|
+
// Per-instance memo cache keyed by method/args. List endpoints rarely change
|
|
893
|
+
// between tab switches, so caching the promise lets users flip between
|
|
894
|
+
// panels without refetching. Mutations scrub their own keys.
|
|
895
|
+
cache = /* @__PURE__ */ new Map();
|
|
896
|
+
adminBaseUrl;
|
|
897
|
+
/** Raw admin bearer key for this session. Public so overlays can compare it
|
|
898
|
+
* against their stored session and rebuild the client on session swap. */
|
|
899
|
+
token;
|
|
900
|
+
projectId;
|
|
901
|
+
onUnauthed;
|
|
902
|
+
fetchImpl;
|
|
903
|
+
/** Session-configured generated client — every spec-covered call goes
|
|
904
|
+
* through it (bearer auth, base URL, injectable fetch). */
|
|
905
|
+
api;
|
|
906
|
+
constructor(opts) {
|
|
907
|
+
this.adminBaseUrl = (opts.adminBaseUrl ?? DEFAULT_ADMIN_BASE_URL).replace(/\/$/, "");
|
|
908
|
+
this.token = opts.token;
|
|
909
|
+
this.projectId = opts.projectId;
|
|
910
|
+
this.onUnauthed = opts.onUnauthed;
|
|
911
|
+
this.fetchImpl = opts.fetch ?? ((...args) => fetch(...args));
|
|
912
|
+
this.api = createClient(
|
|
913
|
+
createConfig({
|
|
914
|
+
baseUrl: this.adminBaseUrl,
|
|
915
|
+
// Generated ops declare `security: [{ scheme: "bearer" }]`; the client
|
|
916
|
+
// resolves the token through this callback per request.
|
|
917
|
+
auth: () => this.token,
|
|
918
|
+
// Never send cookies: the client authenticates with its minted admin
|
|
919
|
+
// SDK key, NOT the dashboard session. Dogfooded same-origin, a session
|
|
920
|
+
// cookie would otherwise ride along and `authenticateAdmin` prefers
|
|
921
|
+
// the session, resolving the WRONG project → 403. RN ignores this.
|
|
922
|
+
credentials: "omit",
|
|
923
|
+
fetch: opts.fetch,
|
|
924
|
+
throwOnError: false
|
|
925
|
+
})
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
/** Admin origin this client talks to. Overlays build dashboard deep links
|
|
929
|
+
* ("Open in dashboard ↗") off it. */
|
|
930
|
+
get adminUrl() {
|
|
931
|
+
return this.adminBaseUrl;
|
|
932
|
+
}
|
|
933
|
+
/** Run `fn` once per `key` and cache the resulting promise. Rejections are
|
|
934
|
+
* evicted so the next call retries instead of replaying the failure. */
|
|
935
|
+
memo(key, fn) {
|
|
936
|
+
const hit = this.cache.get(key);
|
|
937
|
+
if (hit) return hit;
|
|
938
|
+
const p = fn();
|
|
939
|
+
this.cache.set(key, p);
|
|
940
|
+
p.catch(() => {
|
|
941
|
+
if (this.cache.get(key) === p) this.cache.delete(key);
|
|
942
|
+
});
|
|
943
|
+
return p;
|
|
944
|
+
}
|
|
945
|
+
/** Drop all cached responses (pull-to-refresh). */
|
|
946
|
+
invalidate() {
|
|
947
|
+
this.cache.clear();
|
|
948
|
+
}
|
|
949
|
+
/** Build + throw the error for a failed request. 401 produces an `AuthError`
|
|
950
|
+
* and fires `onUnauthed` so the host can drop the stale session instead of
|
|
951
|
+
* leaving the user staring at a failed-load message. */
|
|
952
|
+
fail(path, error, response) {
|
|
953
|
+
const status = response?.status ?? 0;
|
|
954
|
+
let detail = "";
|
|
955
|
+
if (error && typeof error === "object") {
|
|
956
|
+
const e = error;
|
|
957
|
+
detail = e.detail ?? e.error ?? "";
|
|
958
|
+
} else if (typeof error === "string") {
|
|
959
|
+
detail = error.slice(0, 200);
|
|
960
|
+
}
|
|
961
|
+
const message = `${path} \u2192 HTTP ${status}${detail ? ` \u2014 ${detail}` : ""}`;
|
|
962
|
+
if (status === 401) {
|
|
963
|
+
try {
|
|
964
|
+
this.onUnauthed?.();
|
|
965
|
+
} catch {
|
|
966
|
+
}
|
|
967
|
+
throw new AuthError(message);
|
|
968
|
+
}
|
|
969
|
+
throw new Error(message);
|
|
970
|
+
}
|
|
971
|
+
/** Resolve a generated op result to its body, or throw the normalized
|
|
972
|
+
* error. Write ops with empty bodies pass on any 2xx. */
|
|
973
|
+
unwrap(path, r) {
|
|
974
|
+
if (r.error !== void 0 || r.data === void 0 && !(r.response?.ok ?? false)) {
|
|
975
|
+
this.fail(path, r.error, r.response);
|
|
976
|
+
}
|
|
977
|
+
return r.data;
|
|
978
|
+
}
|
|
979
|
+
/** Some list endpoints return a bare array, others a `{ data }` envelope
|
|
980
|
+
* (the spec is the contract, but be tolerant like the overlays always
|
|
981
|
+
* were). */
|
|
982
|
+
asList(body) {
|
|
983
|
+
return Array.isArray(body) ? body : body.data ?? [];
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Drain a paginated `{ data, next_cursor }` list op by walking cursors.
|
|
987
|
+
* Devtools shows the entire list at once; without this, large projects would
|
|
988
|
+
* silently truncate at the first page (limit=100 default).
|
|
989
|
+
*/
|
|
990
|
+
async drain(path, page, limit = 500) {
|
|
991
|
+
const out = [];
|
|
992
|
+
let cursor;
|
|
993
|
+
do {
|
|
994
|
+
const body = this.unwrap(path, await page({ limit, ...cursor ? { cursor } : {} }));
|
|
995
|
+
if (Array.isArray(body)) return body;
|
|
996
|
+
out.push(...body.data);
|
|
997
|
+
cursor = body.next_cursor ?? void 0;
|
|
998
|
+
} while (cursor);
|
|
999
|
+
return out;
|
|
1000
|
+
}
|
|
1001
|
+
// ── SPEC-GAP raw transport ──────────────────────────────────────────────
|
|
1002
|
+
// A few admin routes the overlays use aren't in the OpenAPI contract yet
|
|
1003
|
+
// (spec-alignment candidates). Until they land there, these two helpers
|
|
1004
|
+
// carry them with the same auth/error semantics as the generated client.
|
|
1005
|
+
rawInit(extra) {
|
|
1006
|
+
return {
|
|
1007
|
+
credentials: "omit",
|
|
1008
|
+
// see the createConfig note above
|
|
1009
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
1010
|
+
...extra
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
async rawGet(path) {
|
|
1014
|
+
const res = await this.fetchImpl(`${this.adminBaseUrl}${path}`, this.rawInit());
|
|
1015
|
+
if (!res.ok) this.fail(path, await this.readErrorDetail(res), res);
|
|
1016
|
+
const body = await res.json();
|
|
1017
|
+
return Array.isArray(body) ? body : body.data ?? body;
|
|
1018
|
+
}
|
|
1019
|
+
async rawSend(method, path, body) {
|
|
1020
|
+
const res = await this.fetchImpl(
|
|
1021
|
+
`${this.adminBaseUrl}${path}`,
|
|
1022
|
+
this.rawInit({
|
|
1023
|
+
method,
|
|
1024
|
+
headers: {
|
|
1025
|
+
Authorization: `Bearer ${this.token}`,
|
|
1026
|
+
"Content-Type": "application/json"
|
|
1027
|
+
},
|
|
1028
|
+
body: JSON.stringify(body)
|
|
1029
|
+
})
|
|
1030
|
+
);
|
|
1031
|
+
if (!res.ok) this.fail(path, await this.readErrorDetail(res), res);
|
|
1032
|
+
return await res.json();
|
|
1033
|
+
}
|
|
1034
|
+
/** Read the response error body for diagnostics. Best-effort; never throws. */
|
|
1035
|
+
async readErrorDetail(res) {
|
|
1036
|
+
try {
|
|
1037
|
+
return await res.json();
|
|
1038
|
+
} catch {
|
|
1039
|
+
try {
|
|
1040
|
+
return (await res.text()).slice(0, 200);
|
|
1041
|
+
} catch {
|
|
1042
|
+
return "";
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
// ── project ─────────────────────────────────────────────────────────────
|
|
1047
|
+
project() {
|
|
1048
|
+
return this.memo("project", async () => {
|
|
1049
|
+
const raw = await this.rawGet(`/api/admin/projects/${encodeURIComponent(this.projectId)}`);
|
|
1050
|
+
const b = (v) => v === void 0 || v === true || v === 1;
|
|
1051
|
+
return {
|
|
1052
|
+
id: raw.id,
|
|
1053
|
+
name: raw.name,
|
|
1054
|
+
domain: raw.domain,
|
|
1055
|
+
modules: {
|
|
1056
|
+
translations: b(raw.moduleTranslations),
|
|
1057
|
+
configs: b(raw.moduleConfigs),
|
|
1058
|
+
gates: b(raw.moduleGates),
|
|
1059
|
+
experiments: b(raw.moduleExperiments),
|
|
1060
|
+
feedback: b(raw.moduleFeedback),
|
|
1061
|
+
user: b(raw.moduleUser),
|
|
1062
|
+
events: b(raw.moduleEvents)
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
// ── release surface ─────────────────────────────────────────────────────
|
|
1068
|
+
gates() {
|
|
1069
|
+
return this.memo(
|
|
1070
|
+
"gates",
|
|
1071
|
+
() => (
|
|
1072
|
+
// The wire returns more fields than the generated list item declares
|
|
1073
|
+
// (killswitch, rolloutPct — spec-alignment candidates); the projection
|
|
1074
|
+
// widens to the overlay contract.
|
|
1075
|
+
this.drain(
|
|
1076
|
+
"/api/admin/gates",
|
|
1077
|
+
(query) => listGates({ client: this.api, query })
|
|
1078
|
+
)
|
|
1079
|
+
)
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
configs() {
|
|
1083
|
+
return this.memo("configs", async () => {
|
|
1084
|
+
const list = await this.drain("/api/admin/configs", (query) => listConfigs({ client: this.api, query }));
|
|
1085
|
+
const env = "prod";
|
|
1086
|
+
return Promise.all(
|
|
1087
|
+
list.map(async (c) => {
|
|
1088
|
+
try {
|
|
1089
|
+
const detail = this.unwrap(
|
|
1090
|
+
`/api/admin/configs/${c.id}`,
|
|
1091
|
+
await getConfig({ client: this.api, path: { id: c.id } })
|
|
1092
|
+
);
|
|
1093
|
+
const valueJson = detail.valueJson !== void 0 ? detail.valueJson : detail.values?.[env] ?? {};
|
|
1094
|
+
return {
|
|
1095
|
+
id: c.id,
|
|
1096
|
+
name: c.name,
|
|
1097
|
+
updatedAt: c.updatedAt,
|
|
1098
|
+
valueJson,
|
|
1099
|
+
schema: detail.schema ?? c.schema ?? PERMISSIVE_CONFIG_SCHEMA
|
|
1100
|
+
};
|
|
1101
|
+
} catch {
|
|
1102
|
+
return {
|
|
1103
|
+
id: c.id,
|
|
1104
|
+
name: c.name,
|
|
1105
|
+
updatedAt: c.updatedAt,
|
|
1106
|
+
valueJson: {},
|
|
1107
|
+
schema: c.schema ?? PERMISSIVE_CONFIG_SCHEMA
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
})
|
|
1111
|
+
);
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
/** Experiments list. The endpoint returns the non-archived set (running /
|
|
1115
|
+
* draft / stopped) by default; pass `{ archived: true }` for the archive tab
|
|
1116
|
+
* (a separate memo bucket so the overlay's Archived section loads on demand). */
|
|
1117
|
+
experiments(opts) {
|
|
1118
|
+
const archived = opts?.archived === true;
|
|
1119
|
+
return this.memo(
|
|
1120
|
+
archived ? "experiments:archived" : "experiments",
|
|
1121
|
+
() => this.drain(
|
|
1122
|
+
"/api/admin/experiments",
|
|
1123
|
+
(query) => listExperiments({
|
|
1124
|
+
client: this.api,
|
|
1125
|
+
query: archived ? { ...query, status: "archived" } : query
|
|
1126
|
+
})
|
|
1127
|
+
)
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
universes() {
|
|
1131
|
+
return this.memo(
|
|
1132
|
+
"universes",
|
|
1133
|
+
() => this.drain(
|
|
1134
|
+
"/api/admin/universes",
|
|
1135
|
+
(query) => listUniverses({ client: this.api, query })
|
|
1136
|
+
)
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
// ── i18n ────────────────────────────────────────────────────────────────
|
|
1140
|
+
profiles() {
|
|
1141
|
+
return this.memo(
|
|
1142
|
+
"profiles",
|
|
1143
|
+
async () => this.asList(
|
|
1144
|
+
this.unwrap("/api/admin/i18n/profiles", await listI18nProfiles({ client: this.api }))
|
|
1145
|
+
)
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
drafts() {
|
|
1149
|
+
return this.memo(
|
|
1150
|
+
"drafts",
|
|
1151
|
+
async () => this.asList(
|
|
1152
|
+
this.unwrap("/api/admin/i18n/drafts", await listI18nDrafts({ client: this.api }))
|
|
1153
|
+
)
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
keys(profileId) {
|
|
1157
|
+
return this.memo(`keys:${profileId ?? ""}`, async () => {
|
|
1158
|
+
const PAGE = 500;
|
|
1159
|
+
const fetchPage = async (offset) => {
|
|
1160
|
+
const body = this.unwrap(
|
|
1161
|
+
"/api/admin/i18n/keys",
|
|
1162
|
+
await listI18nKeys({
|
|
1163
|
+
client: this.api,
|
|
1164
|
+
query: {
|
|
1165
|
+
...profileId ? { profile_id: profileId } : {},
|
|
1166
|
+
limit: PAGE,
|
|
1167
|
+
offset
|
|
1168
|
+
}
|
|
1169
|
+
})
|
|
1170
|
+
);
|
|
1171
|
+
if (Array.isArray(body)) return { keys: body, total: body.length };
|
|
1172
|
+
const keys = body.keys ?? [];
|
|
1173
|
+
const total = body.total ?? keys.length;
|
|
1174
|
+
return { keys, total };
|
|
1175
|
+
};
|
|
1176
|
+
const first = await fetchPage(0);
|
|
1177
|
+
const all = first.keys.slice();
|
|
1178
|
+
while (all.length < first.total && first.keys.length > 0) {
|
|
1179
|
+
const next = await fetchPage(all.length);
|
|
1180
|
+
if (next.keys.length === 0) break;
|
|
1181
|
+
all.push(...next.keys);
|
|
1182
|
+
}
|
|
1183
|
+
return all;
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
async createDraft(input) {
|
|
1187
|
+
const r = this.unwrap(
|
|
1188
|
+
"/api/admin/i18n/drafts",
|
|
1189
|
+
await createI18nDraft({
|
|
1190
|
+
client: this.api,
|
|
1191
|
+
body: { profile_id: input.profileId, name: input.name }
|
|
1192
|
+
})
|
|
1193
|
+
);
|
|
1194
|
+
this.cache.delete("drafts");
|
|
1195
|
+
return r;
|
|
1196
|
+
}
|
|
1197
|
+
async upsertDraftKey(draftId, key, value) {
|
|
1198
|
+
await this.rawSend("POST", `/api/admin/i18n/drafts/${encodeURIComponent(draftId)}/keys`, {
|
|
1199
|
+
key,
|
|
1200
|
+
value
|
|
1201
|
+
});
|
|
1202
|
+
this.invalidateKeysCache();
|
|
1203
|
+
}
|
|
1204
|
+
async updateKeyById(id, value) {
|
|
1205
|
+
this.unwrap(
|
|
1206
|
+
`/api/admin/i18n/keys/${id}`,
|
|
1207
|
+
await updateI18nKey({ client: this.api, path: { id }, body: { value } })
|
|
1208
|
+
);
|
|
1209
|
+
this.invalidateKeysCache();
|
|
1210
|
+
}
|
|
1211
|
+
async upsertKeys(profileId, keys, chunk = "default") {
|
|
1212
|
+
await this.rawSend("PUT", `/api/admin/i18n/keys`, { profile_id: profileId, chunk, keys });
|
|
1213
|
+
this.invalidateKeysCache();
|
|
1214
|
+
}
|
|
1215
|
+
/** Drop every cached `keys(profileId?)` response. We don't track which
|
|
1216
|
+
* profile a single key write affects, so be safe and clear them all. */
|
|
1217
|
+
invalidateKeysCache() {
|
|
1218
|
+
for (const k of Array.from(this.cache.keys())) {
|
|
1219
|
+
if (k.startsWith("keys:")) this.cache.delete(k);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
// ── feedback (bugs + feature requests) ──────────────────────────────────
|
|
1223
|
+
bugs() {
|
|
1224
|
+
return this.memo(
|
|
1225
|
+
"bugs",
|
|
1226
|
+
async () => this.asList(
|
|
1227
|
+
this.unwrap(
|
|
1228
|
+
"/api/admin/ops?type=bug",
|
|
1229
|
+
await listOpsItems({ client: this.api, query: { type: "bug" } })
|
|
1230
|
+
)
|
|
1231
|
+
)
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
bug(id) {
|
|
1235
|
+
return this.memo(
|
|
1236
|
+
`bug:${id}`,
|
|
1237
|
+
async () => this.unwrap(
|
|
1238
|
+
`/api/admin/ops/${id}`,
|
|
1239
|
+
await getOpsItem({ client: this.api, path: { handle: id } })
|
|
1240
|
+
)
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
/** Auto-filed error tickets. Same list endpoint, `type=error`. */
|
|
1244
|
+
errors() {
|
|
1245
|
+
return this.memo(
|
|
1246
|
+
"errors",
|
|
1247
|
+
async () => this.asList(
|
|
1248
|
+
this.unwrap(
|
|
1249
|
+
"/api/admin/ops?type=error",
|
|
1250
|
+
await listOpsItems({ client: this.api, query: { type: "error" } })
|
|
1251
|
+
)
|
|
1252
|
+
)
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
/** Auto-filed alert tickets. Same list endpoint, `type=alert`. */
|
|
1256
|
+
alerts() {
|
|
1257
|
+
return this.memo(
|
|
1258
|
+
"alerts",
|
|
1259
|
+
async () => this.asList(
|
|
1260
|
+
this.unwrap(
|
|
1261
|
+
"/api/admin/ops?type=alert",
|
|
1262
|
+
await listOpsItems({ client: this.api, query: { type: "alert" } })
|
|
1263
|
+
)
|
|
1264
|
+
)
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
/** Generic ops-item detail — works for any type (bug/feature/error/alert). */
|
|
1268
|
+
opsItem(id) {
|
|
1269
|
+
return this.memo(
|
|
1270
|
+
`ops:${id}`,
|
|
1271
|
+
async () => this.unwrap(
|
|
1272
|
+
`/api/admin/ops/${id}`,
|
|
1273
|
+
await getOpsItem({ client: this.api, path: { handle: id } })
|
|
1274
|
+
)
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
/** Triage-only update (status / priority) that works for ANY item type —
|
|
1278
|
+
* the only editable shape for auto-filed error/alert tickets. Scrubs every
|
|
1279
|
+
* list bucket + the item's detail so the panels refetch. */
|
|
1280
|
+
async updateOps(id, patch) {
|
|
1281
|
+
this.unwrap(
|
|
1282
|
+
`/api/admin/ops/${id}`,
|
|
1283
|
+
await updateOpsItem({
|
|
1284
|
+
client: this.api,
|
|
1285
|
+
path: { handle: id },
|
|
1286
|
+
body: patch
|
|
1287
|
+
})
|
|
1288
|
+
);
|
|
1289
|
+
for (const k of ["bugs", "featureRequests", "errors", "alerts"]) this.cache.delete(k);
|
|
1290
|
+
this.cache.delete(`ops:${id}`);
|
|
1291
|
+
this.cache.delete(`bug:${id}`);
|
|
1292
|
+
this.cache.delete(`featureRequest:${id}`);
|
|
1293
|
+
}
|
|
1294
|
+
async createBug(input) {
|
|
1295
|
+
const r = this.unwrap(
|
|
1296
|
+
"/api/admin/ops",
|
|
1297
|
+
await createOpsItem({
|
|
1298
|
+
client: this.api,
|
|
1299
|
+
body: { ...input, type: "bug" }
|
|
1300
|
+
})
|
|
1301
|
+
);
|
|
1302
|
+
this.cache.delete("bugs");
|
|
1303
|
+
return r;
|
|
1304
|
+
}
|
|
1305
|
+
async updateBug(id, patch) {
|
|
1306
|
+
this.unwrap(
|
|
1307
|
+
`/api/admin/ops/${id}`,
|
|
1308
|
+
await updateOpsItem({
|
|
1309
|
+
client: this.api,
|
|
1310
|
+
path: { handle: id },
|
|
1311
|
+
body: patch
|
|
1312
|
+
})
|
|
1313
|
+
);
|
|
1314
|
+
this.cache.delete("bugs");
|
|
1315
|
+
this.cache.delete(`bug:${id}`);
|
|
1316
|
+
}
|
|
1317
|
+
featureRequests() {
|
|
1318
|
+
return this.memo(
|
|
1319
|
+
"featureRequests",
|
|
1320
|
+
async () => this.asList(
|
|
1321
|
+
this.unwrap(
|
|
1322
|
+
"/api/admin/ops?type=feature_request",
|
|
1323
|
+
await listOpsItems({ client: this.api, query: { type: "feature_request" } })
|
|
1324
|
+
)
|
|
1325
|
+
)
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
1328
|
+
featureRequest(id) {
|
|
1329
|
+
return this.memo(
|
|
1330
|
+
`featureRequest:${id}`,
|
|
1331
|
+
async () => this.unwrap(
|
|
1332
|
+
`/api/admin/ops/${id}`,
|
|
1333
|
+
await getOpsItem({ client: this.api, path: { handle: id } })
|
|
1334
|
+
)
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
async createFeatureRequest(input) {
|
|
1338
|
+
const r = this.unwrap(
|
|
1339
|
+
"/api/admin/ops",
|
|
1340
|
+
await createOpsItem({
|
|
1341
|
+
client: this.api,
|
|
1342
|
+
body: { ...input, type: "feature_request" }
|
|
1343
|
+
})
|
|
1344
|
+
);
|
|
1345
|
+
this.cache.delete("featureRequests");
|
|
1346
|
+
return r;
|
|
1347
|
+
}
|
|
1348
|
+
async updateFeatureRequest(id, patch) {
|
|
1349
|
+
this.unwrap(
|
|
1350
|
+
`/api/admin/ops/${id}`,
|
|
1351
|
+
await updateOpsItem({
|
|
1352
|
+
client: this.api,
|
|
1353
|
+
path: { handle: id },
|
|
1354
|
+
body: patch
|
|
1355
|
+
})
|
|
1356
|
+
);
|
|
1357
|
+
this.cache.delete("featureRequests");
|
|
1358
|
+
this.cache.delete(`featureRequest:${id}`);
|
|
1359
|
+
}
|
|
1360
|
+
// ── attachments ─────────────────────────────────────────────────────────
|
|
1361
|
+
// SPEC GAP: the report-attachment routes aren't in the OpenAPI contract.
|
|
1362
|
+
/** Fetch an attachment file as a Blob via the authenticated stream route
|
|
1363
|
+
* (an <Image>/<img> can't send the Authorization header itself). */
|
|
1364
|
+
async attachmentBlob(id) {
|
|
1365
|
+
const path = `/api/admin/reports/attachments/${encodeURIComponent(id)}`;
|
|
1366
|
+
const res = await this.fetchImpl(`${this.adminBaseUrl}${path}`, this.rawInit());
|
|
1367
|
+
if (!res.ok) this.fail(path, await this.readErrorDetail(res), res);
|
|
1368
|
+
return res.blob();
|
|
1369
|
+
}
|
|
1370
|
+
async uploadAttachment(args) {
|
|
1371
|
+
const fd = new FormData();
|
|
1372
|
+
fd.append("reportKind", args.reportKind);
|
|
1373
|
+
fd.append("reportId", args.reportId);
|
|
1374
|
+
fd.append("kind", args.kind);
|
|
1375
|
+
fd.append("filename", args.filename);
|
|
1376
|
+
fd.append(
|
|
1377
|
+
"file",
|
|
1378
|
+
args.blob,
|
|
1379
|
+
args.filename
|
|
1380
|
+
);
|
|
1381
|
+
const path = "/api/admin/reports/attachments";
|
|
1382
|
+
const res = await this.fetchImpl(
|
|
1383
|
+
`${this.adminBaseUrl}${path}`,
|
|
1384
|
+
this.rawInit({ method: "POST", body: fd })
|
|
1385
|
+
);
|
|
1386
|
+
if (!res.ok) this.fail(path, await this.readErrorDetail(res), res);
|
|
1387
|
+
return await res.json();
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
export {
|
|
1392
|
+
AuthError,
|
|
1393
|
+
DevtoolsClient
|
|
1394
|
+
};
|