@adobe/spacecat-shared-project-engine-client 1.12.0 → 1.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-project-engine-client",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "Shared modules of the Spacecat Services - Semrush Project Engine client and generated types",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/errors.js ADDED
@@ -0,0 +1,48 @@
1
+ /*
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ // @ts-check
14
+
15
+ /**
16
+ * The single typed error the transport facade throws from its `unwrap` seam. It carries the
17
+ * failing HTTP `method`, the response `status` (or `undefined` when there was no HTTP response —
18
+ * i.e. an exhausted-network / per-attempt-timeout failure), and the normalized parsed error
19
+ * `body`. On the network/timeout path the original thrown error is preserved as `cause`.
20
+ *
21
+ * This class does NOT translate the failure into an HTTP status for the consumer, redact the
22
+ * body, or map it onto a domain error — those are consumer-owned per ADR-0001. It exists only to
23
+ * give consumers a single `instanceof`-checkable type with the raw `{ status, method, body }`.
24
+ */
25
+ export class ProjectEngineApiError extends Error {
26
+ /**
27
+ * @param {number | undefined} status the HTTP response status, or `undefined` when there was no
28
+ * HTTP response (network / timeout failure)
29
+ * @param {string} method the HTTP method of the failing request
30
+ * @param {unknown} body the normalized parsed error body (`null` when empty/absent)
31
+ * @param {{ cause?: unknown }} [options] optional `{ cause }` forwarded to `super`, so a wrapped
32
+ * network/timeout error keeps its original as `.cause`
33
+ */
34
+ constructor(status, method, body, options) {
35
+ const message = status === undefined
36
+ ? `Project Engine ${method} request failed`
37
+ : `Project Engine ${method} request failed with status ${status}`;
38
+ super(message, options);
39
+ /** @type {string} */
40
+ this.name = 'ProjectEngineApiError';
41
+ /** @type {number | undefined} */
42
+ this.status = status;
43
+ /** @type {string} */
44
+ this.method = method;
45
+ /** @type {unknown} */
46
+ this.body = body;
47
+ }
48
+ }
package/src/index.d.ts CHANGED
@@ -10,7 +10,8 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import type { Client } from 'openapi-fetch';
13
+ import type { Client, FetchResponse, MaybeOptionalInit, MediaType } from 'openapi-fetch';
14
+ import type { RequiredKeysOf } from 'openapi-typescript-helpers';
14
15
  import type { paths, components } from './generated/types.js';
15
16
 
16
17
  /** Supplies the caller's IMS JWT — forwarded verbatim, never minted or exchanged. */
@@ -73,5 +74,309 @@ export declare function createSerenityProjectEngineApiClient(
73
74
  options: SerenityProjectEngineApiClientOptions,
74
75
  ): SerenityProjectEngineApiClient;
75
76
 
77
+ // ───────────────────────────────────────────────────────────────────────────
78
+ // Intent-named facade (transport) over the raw client.
79
+ //
80
+ // The three helpers below derive every facade method's parameter and return type
81
+ // straight from the generated `paths` contract, so the surface stays strictly
82
+ // in-spec and never degrades to `any`. They are internal to this declaration.
83
+ // ───────────────────────────────────────────────────────────────────────────
84
+
85
+ /**
86
+ * The openapi-fetch `init` argument (params.path/query, body) accepted for a given
87
+ * path `P` + HTTP method `M`, derived from the generated contract.
88
+ */
89
+ type TransportInit<P extends keyof paths, M extends keyof paths[P]> = MaybeOptionalInit<
90
+ paths[P],
91
+ M
92
+ >;
93
+
94
+ /**
95
+ * Mirror of openapi-fetch's own (unexported) `InitParam`: the `init` argument is
96
+ * OPTIONAL when nothing in it is required (no path/query params, no required body),
97
+ * and REQUIRED otherwise — so `listLanguages()` needs no argument while
98
+ * `createProject({ params, body })` enforces its params + body at the call site.
99
+ */
100
+ type TransportInitParam<Init> = RequiredKeysOf<Init> extends never
101
+ ? [init?: Init]
102
+ : [init: Init];
103
+
104
+ /**
105
+ * The value a facade method resolves with: the parsed 2xx body for path `P` +
106
+ * method `M`, or `null` for an empty body (e.g. a 204 / empty-body ack). Non-2xx
107
+ * responses never resolve — they throw at the single `unwrap` error seam.
108
+ */
109
+ type TransportData<P extends keyof paths, M extends keyof paths[P]> =
110
+ | NonNullable<FetchResponse<paths[P][M], TransportInit<P, M>, MediaType>['data']>
111
+ | null;
112
+
113
+ /**
114
+ * Intent-named facade over {@link SerenityProjectEngineApiClient}. Wraps the 28 in-spec
115
+ * Project Engine operations spacecat-api-service consumes behind verb+resource methods, so
116
+ * consumers depend on this seam rather than the raw client's literal path strings. Each method
117
+ * is THIN: it forwards the caller's openapi-fetch `init` to the underlying client and resolves
118
+ * with the unwrapped 2xx body (or throws on a non-2xx / network error at a single seam). No
119
+ * caching, redaction, error→HTTP translation, or composite methods — all consumer-owned per
120
+ * ADR-0001. The remaining generated operations stay reachable via the raw client.
121
+ */
122
+ export interface SerenityProjectEngineTransport {
123
+ /** GET /v1/languages — projects-admin-list-languages */
124
+ listLanguages(
125
+ ...init: TransportInitParam<TransportInit<'/v1/languages', 'get'>>
126
+ ): Promise<TransportData<'/v1/languages', 'get'>>;
127
+ /** GET /v1/ai_models — ai-list-global-models */
128
+ listGlobalAiModels(
129
+ ...init: TransportInitParam<TransportInit<'/v1/ai_models', 'get'>>
130
+ ): Promise<TransportData<'/v1/ai_models', 'get'>>;
131
+
132
+ /** GET /v1/workspaces/{id}/projects — projects-list-projects */
133
+ listProjects(
134
+ ...init: TransportInitParam<TransportInit<'/v1/workspaces/{id}/projects', 'get'>>
135
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects', 'get'>>;
136
+ /** POST /v1/workspaces/{id}/projects — projects-post-project */
137
+ createProject(
138
+ ...init: TransportInitParam<TransportInit<'/v1/workspaces/{id}/projects', 'post'>>
139
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects', 'post'>>;
140
+ /** GET /v1/workspaces/{id}/projects/{project_id} — projects-get-project */
141
+ getProject(
142
+ ...init: TransportInitParam<TransportInit<'/v1/workspaces/{id}/projects/{project_id}', 'get'>>
143
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects/{project_id}', 'get'>>;
144
+ /** PATCH /v1/workspaces/{id}/projects/{project_id} — projects-patch-project */
145
+ updateProject(
146
+ ...init: TransportInitParam<TransportInit<'/v1/workspaces/{id}/projects/{project_id}', 'patch'>>
147
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects/{project_id}', 'patch'>>;
148
+ /** DELETE /v1/workspaces/{id}/projects/{project_id} — projects-delete-project */
149
+ deleteProject(
150
+ ...init: TransportInitParam<
151
+ TransportInit<'/v1/workspaces/{id}/projects/{project_id}', 'delete'>
152
+ >
153
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects/{project_id}', 'delete'>>;
154
+ /** POST /v1/workspaces/{id}/projects/{project_id}/publish — projects-publish-project */
155
+ publishProject(
156
+ ...init: TransportInitParam<
157
+ TransportInit<'/v1/workspaces/{id}/projects/{project_id}/publish', 'post'>
158
+ >
159
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects/{project_id}/publish', 'post'>>;
160
+
161
+ /** GET /v1/workspaces/{id}/projects/{project_id}/ai_models — ai-list-models */
162
+ listAiModels(
163
+ ...init: TransportInitParam<
164
+ TransportInit<'/v1/workspaces/{id}/projects/{project_id}/ai_models', 'get'>
165
+ >
166
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects/{project_id}/ai_models', 'get'>>;
167
+ /** DELETE /v1/workspaces/{id}/projects/{project_id}/ai_models — ai-delete-models */
168
+ deleteAiModels(
169
+ ...init: TransportInitParam<
170
+ TransportInit<'/v1/workspaces/{id}/projects/{project_id}/ai_models', 'delete'>
171
+ >
172
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects/{project_id}/ai_models', 'delete'>>;
173
+ /** GET /v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks — ai-list-benchmarks */
174
+ listBenchmarks(
175
+ ...init: TransportInitParam<
176
+ TransportInit<'/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', 'get'>
177
+ >
178
+ ): Promise<
179
+ TransportData<'/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', 'get'>
180
+ >;
181
+ /** DELETE /v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks — ai-delete-benchmarks */
182
+ deleteBenchmarks(
183
+ ...init: TransportInitParam<
184
+ TransportInit<'/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', 'delete'>
185
+ >
186
+ ): Promise<
187
+ TransportData<'/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', 'delete'>
188
+ >;
189
+ /**
190
+ * PUT /v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks/{benchmark_id}
191
+ * — ai-update-benchmark
192
+ */
193
+ updateBenchmark(
194
+ ...init: TransportInitParam<
195
+ TransportInit<
196
+ '/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks/{benchmark_id}',
197
+ 'put'
198
+ >
199
+ >
200
+ ): Promise<
201
+ TransportData<
202
+ '/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks/{benchmark_id}',
203
+ 'put'
204
+ >
205
+ >;
206
+ /** PUT /v1/workspaces/{id}/projects/{project_id}/ci/competitors — ci-update-competitors */
207
+ updateCompetitors(
208
+ ...init: TransportInitParam<
209
+ TransportInit<'/v1/workspaces/{id}/projects/{project_id}/ci/competitors', 'put'>
210
+ >
211
+ ): Promise<TransportData<'/v1/workspaces/{id}/projects/{project_id}/ci/competitors', 'put'>>;
212
+
213
+ /** POST /v2/workspaces/{id}/projects/{project_id}/ai_models — aio-project-create-model */
214
+ createAioModel(
215
+ ...init: TransportInitParam<
216
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/ai_models', 'post'>
217
+ >
218
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/ai_models', 'post'>>;
219
+ /** POST /v2/workspaces/{id}/projects/{project_id}/ai_models/benchmarks — ai-create-benchmarks-v2 */
220
+ createBenchmarks(
221
+ ...init: TransportInitParam<
222
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', 'post'>
223
+ >
224
+ ): Promise<
225
+ TransportData<'/v2/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', 'post'>
226
+ >;
227
+
228
+ /**
229
+ * GET /v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls
230
+ * — aio-list-brand-urls
231
+ */
232
+ listBrandUrls(
233
+ ...init: TransportInitParam<
234
+ TransportInit<
235
+ '/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls',
236
+ 'get'
237
+ >
238
+ >
239
+ ): Promise<
240
+ TransportData<
241
+ '/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls',
242
+ 'get'
243
+ >
244
+ >;
245
+ /**
246
+ * POST /v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls
247
+ * — aio-create-brand-urls
248
+ */
249
+ createBrandUrls(
250
+ ...init: TransportInitParam<
251
+ TransportInit<
252
+ '/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls',
253
+ 'post'
254
+ >
255
+ >
256
+ ): Promise<
257
+ TransportData<
258
+ '/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls',
259
+ 'post'
260
+ >
261
+ >;
262
+ /**
263
+ * DELETE /v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls
264
+ * — aio-delete-brand-urls
265
+ */
266
+ deleteBrandUrls(
267
+ ...init: TransportInitParam<
268
+ TransportInit<
269
+ '/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls',
270
+ 'delete'
271
+ >
272
+ >
273
+ ): Promise<
274
+ TransportData<
275
+ '/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls',
276
+ 'delete'
277
+ >
278
+ >;
279
+
280
+ /**
281
+ * GET /v2/workspaces/{id}/projects/{project_id}/aio/init_status
282
+ * — aio-get-project-init-status-v2
283
+ */
284
+ getProjectInitStatus(
285
+ ...init: TransportInitParam<
286
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/init_status', 'get'>
287
+ >
288
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/init_status', 'get'>>;
289
+ /** POST /v2/workspaces/{id}/projects/{project_id}/aio/prompts — aio-create-prompt-v2 */
290
+ createPrompts(
291
+ ...init: TransportInitParam<
292
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts', 'post'>
293
+ >
294
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts', 'post'>>;
295
+ /** DELETE /v2/workspaces/{id}/projects/{project_id}/aio/prompts — aio-delete-prompt-by-ids-v2 */
296
+ deletePromptsByIds(
297
+ ...init: TransportInitParam<
298
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts', 'delete'>
299
+ >
300
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts', 'delete'>>;
301
+ /**
302
+ * POST /v2/workspaces/{id}/projects/{project_id}/aio/prompts/by_tags
303
+ * — aio-list-prompts-by-tag-ids
304
+ */
305
+ listPromptsByTagIds(
306
+ ...init: TransportInitParam<
307
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts/by_tags', 'post'>
308
+ >
309
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts/by_tags', 'post'>>;
310
+ /** PUT /v2/workspaces/{id}/projects/{project_id}/aio/prompts/tags — aio-update-prompts-batch */
311
+ updatePromptTags(
312
+ ...init: TransportInitParam<
313
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts/tags', 'put'>
314
+ >
315
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts/tags', 'put'>>;
316
+ /**
317
+ * POST /v2/workspaces/{id}/projects/{project_id}/aio/prompts/{prompt_id}/rename
318
+ * — aio-rename-prompt
319
+ */
320
+ renamePrompt(
321
+ ...init: TransportInitParam<
322
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts/{prompt_id}/rename', 'post'>
323
+ >
324
+ ): Promise<
325
+ TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/prompts/{prompt_id}/rename', 'post'>
326
+ >;
327
+ /** GET /v2/workspaces/{id}/projects/{project_id}/aio/tags — aio-get-project-tags */
328
+ listProjectTags(
329
+ ...init: TransportInitParam<
330
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/tags', 'get'>
331
+ >
332
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/tags', 'get'>>;
333
+ /** POST /v2/workspaces/{id}/projects/{project_id}/aio/tags — aio-create-project-tags */
334
+ createProjectTags(
335
+ ...init: TransportInitParam<
336
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/tags', 'post'>
337
+ >
338
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/tags', 'post'>>;
339
+ /** PATCH /v2/workspaces/{id}/projects/{project_id}/aio/tags/{tag_id} — aio-update-tag */
340
+ updateProjectTag(
341
+ ...init: TransportInitParam<
342
+ TransportInit<'/v2/workspaces/{id}/projects/{project_id}/aio/tags/{tag_id}', 'patch'>
343
+ >
344
+ ): Promise<TransportData<'/v2/workspaces/{id}/projects/{project_id}/aio/tags/{tag_id}', 'patch'>>;
345
+ }
346
+
347
+ /**
348
+ * Builds the {@link SerenityProjectEngineTransport} facade. Takes the SAME options as
349
+ * {@link createSerenityProjectEngineApiClient} (it builds that client internally and adds no
350
+ * options of its own).
351
+ */
352
+ export declare function createSerenityProjectEngineTransport(
353
+ options: SerenityProjectEngineApiClientOptions,
354
+ ): SerenityProjectEngineTransport;
355
+
356
+ /**
357
+ * The single typed error the transport facade throws from its `unwrap` seam. It carries the
358
+ * failing HTTP `method`, the response `status` (or `undefined` when there was no HTTP response —
359
+ * an exhausted-network / per-attempt-timeout failure), and the normalized parsed error `body`.
360
+ * On the network/timeout path the original thrown error is preserved as `cause`. No error→HTTP
361
+ * translation or redaction — consumer-owned per ADR-0001.
362
+ *
363
+ * NOTE: the `readonly` modifiers below are a type-system-only guarantee; the runtime class
364
+ * (`errors.js`) sets these fields with plain assignment in the constructor.
365
+ */
366
+ export declare class ProjectEngineApiError extends Error {
367
+ /** The HTTP response status, or `undefined` when there was no HTTP response. */
368
+ readonly status: number | undefined;
369
+ /** The HTTP method of the failing request. */
370
+ readonly method: string;
371
+ /** The normalized parsed error body (`null` when empty/absent). */
372
+ readonly body: unknown;
373
+ constructor(
374
+ status: number | undefined,
375
+ method: string,
376
+ body: unknown,
377
+ options?: { cause?: unknown },
378
+ );
379
+ }
380
+
76
381
  // Re-export the generated contract types for consumers that want them directly.
77
382
  export type { paths, components };
package/src/index.js CHANGED
@@ -13,3 +13,5 @@
13
13
  // @ts-check
14
14
 
15
15
  export { createSerenityProjectEngineApiClient } from './client.js';
16
+ export { createSerenityProjectEngineTransport } from './rest-transport.js';
17
+ export { ProjectEngineApiError } from './errors.js';
@@ -0,0 +1,238 @@
1
+ /*
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ // @ts-check
14
+
15
+ import { createSerenityProjectEngineApiClient } from './client.js';
16
+ import { ProjectEngineApiError } from './errors.js';
17
+
18
+ /**
19
+ * @typedef {import('./client.js').SerenityProjectEngineApiClientOptions}
20
+ * SerenityProjectEngineApiClientOptions
21
+ */
22
+
23
+ /**
24
+ * Intent-named facade over the raw {@link createSerenityProjectEngineApiClient} openapi-fetch
25
+ * client. It wraps ONLY the 28 in-spec Project Engine operations that spacecat-api-service
26
+ * consumes, behind verb+resource method names, so consumers depend on this seam rather than the
27
+ * raw client and its literal path strings. Each method is THIN: it forwards the caller's
28
+ * openapi-fetch `init` (params.path/query, body) to `client.<METHOD>('<literal path>', init)` and
29
+ * routes the result through the single {@link unwrap} error seam. There is deliberately NO caching,
30
+ * redaction, error→HTTP translation, or composite/convenience method here — all consumer-owned per
31
+ * ADR-0001. The remaining generated operations stay reachable via the raw client; add a facade
32
+ * method only when a real second consumer needs one.
33
+ *
34
+ * @param {SerenityProjectEngineApiClientOptions} options The SAME options as the raw client — the
35
+ * facade adds none of its own. Builds the underlying client via
36
+ * {@link createSerenityProjectEngineApiClient}.
37
+ * @returns {import('./index.js').SerenityProjectEngineTransport}
38
+ */
39
+ export function createSerenityProjectEngineTransport(options) {
40
+ const client = createSerenityProjectEngineApiClient(options);
41
+
42
+ /**
43
+ * The SINGLE seam where a failed call becomes a throw. It awaits the openapi-fetch result
44
+ * promise here (so a network/timeout rejection also flows through this one point), and turns
45
+ * both failure paths into a {@link ProjectEngineApiError}:
46
+ *
47
+ * - a non-2xx HTTP response: the typed client never throws on an HTTP error — it resolves to
48
+ * `{ data, error, response }` with the parsed error body in `error` — so the non-2xx is turned
49
+ * into a throw here carrying `response.status` + the normalized `body`;
50
+ * - an exhausted-network / per-attempt-timeout failure: `createRetryingFetch` rethrew the last
51
+ * raw error after the retry budget, so the awaited promise rejects. There is no HTTP response
52
+ * ⇒ `status` is `undefined`; the original error is preserved as `cause`.
53
+ *
54
+ * A 2xx returns the parsed body (or null for an empty body).
55
+ *
56
+ * @template T
57
+ * @param {string} method the HTTP method, for the error message
58
+ * @param {Promise<{ data?: T, error?: unknown, response: Response }>} resultPromise the pending
59
+ * openapi-fetch result
60
+ * @returns {Promise<NonNullable<T> | null>} the parsed success body, or null for an empty body
61
+ * (an empty-body operation resolves with null, never undefined)
62
+ * @throws {import('./errors.js').ProjectEngineApiError} on a non-2xx response or an
63
+ * exhausted-network / per-attempt-timeout failure
64
+ */
65
+ async function unwrap(method, resultPromise) {
66
+ let result;
67
+ try {
68
+ result = await resultPromise;
69
+ } catch (cause) {
70
+ // exhausted-network / per-attempt-timeout path: createRetryingFetch rethrew the last raw
71
+ // error after the retry budget. No HTTP response ⇒ status undefined; preserve the original
72
+ // as `cause`.
73
+ throw new ProjectEngineApiError(undefined, method, null, { cause });
74
+ }
75
+ const { data, error, response } = result;
76
+ if (!response.ok) {
77
+ // openapi-fetch puts the parsed error body in `error`; fall back to `data`, then null.
78
+ const rawBody = error ?? data ?? null;
79
+ // openapi-fetch surfaces an empty error body as '' (not undefined); normalise it to null.
80
+ const body = rawBody === '' ? null : rawBody;
81
+ throw new ProjectEngineApiError(response.status, method, body);
82
+ }
83
+ return data ?? null;
84
+ }
85
+
86
+ return {
87
+ // ─── /v1 catalog ────────────────────────────────────────────────────────
88
+ /** GET /v1/languages — projects-admin-list-languages */
89
+ listLanguages(init) {
90
+ return unwrap('GET', client.GET('/v1/languages', init));
91
+ },
92
+ /** GET /v1/ai_models — ai-list-global-models */
93
+ listGlobalAiModels(init) {
94
+ return unwrap('GET', client.GET('/v1/ai_models', init));
95
+ },
96
+
97
+ // ─── /v1 projects ───────────────────────────────────────────────────────
98
+ /** GET /v1/workspaces/{id}/projects — projects-list-projects */
99
+ listProjects(init) {
100
+ return unwrap('GET', client.GET('/v1/workspaces/{id}/projects', init));
101
+ },
102
+ /** POST /v1/workspaces/{id}/projects — projects-post-project */
103
+ createProject(init) {
104
+ return unwrap('POST', client.POST('/v1/workspaces/{id}/projects', init));
105
+ },
106
+ /** GET /v1/workspaces/{id}/projects/{project_id} — projects-get-project */
107
+ getProject(init) {
108
+ return unwrap('GET', client.GET('/v1/workspaces/{id}/projects/{project_id}', init));
109
+ },
110
+ /** PATCH /v1/workspaces/{id}/projects/{project_id} — projects-patch-project */
111
+ updateProject(init) {
112
+ return unwrap('PATCH', client.PATCH('/v1/workspaces/{id}/projects/{project_id}', init));
113
+ },
114
+ /** DELETE /v1/workspaces/{id}/projects/{project_id} — projects-delete-project */
115
+ deleteProject(init) {
116
+ return unwrap('DELETE', client.DELETE('/v1/workspaces/{id}/projects/{project_id}', init));
117
+ },
118
+ /** POST /v1/workspaces/{id}/projects/{project_id}/publish — projects-publish-project */
119
+ publishProject(init) {
120
+ return unwrap('POST', client.POST('/v1/workspaces/{id}/projects/{project_id}/publish', init));
121
+ },
122
+
123
+ // ─── /v1 AI models + benchmarks ───────────────────────────────────────────
124
+ /** GET /v1/workspaces/{id}/projects/{project_id}/ai_models — ai-list-models */
125
+ listAiModels(init) {
126
+ return unwrap('GET', client.GET('/v1/workspaces/{id}/projects/{project_id}/ai_models', init));
127
+ },
128
+ /** DELETE /v1/workspaces/{id}/projects/{project_id}/ai_models — ai-delete-models */
129
+ deleteAiModels(init) {
130
+ return unwrap('DELETE', client.DELETE('/v1/workspaces/{id}/projects/{project_id}/ai_models', init));
131
+ },
132
+ /** GET /v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks — ai-list-benchmarks */
133
+ listBenchmarks(init) {
134
+ return unwrap('GET', client.GET('/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', init));
135
+ },
136
+ /**
137
+ * DELETE /v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks — ai-delete-benchmarks
138
+ */
139
+ deleteBenchmarks(init) {
140
+ return unwrap('DELETE', client.DELETE('/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', init));
141
+ },
142
+ /**
143
+ * PUT /v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks/{benchmark_id}
144
+ * — ai-update-benchmark
145
+ */
146
+ updateBenchmark(init) {
147
+ return unwrap('PUT', client.PUT('/v1/workspaces/{id}/projects/{project_id}/ai_models/benchmarks/{benchmark_id}', init));
148
+ },
149
+ /** PUT /v1/workspaces/{id}/projects/{project_id}/ci/competitors — ci-update-competitors */
150
+ updateCompetitors(init) {
151
+ return unwrap('PUT', client.PUT('/v1/workspaces/{id}/projects/{project_id}/ci/competitors', init));
152
+ },
153
+
154
+ // ─── /v2 AI models + benchmarks ───────────────────────────────────────────
155
+ /** POST /v2/workspaces/{id}/projects/{project_id}/ai_models — aio-project-create-model */
156
+ createAioModel(init) {
157
+ return unwrap('POST', client.POST('/v2/workspaces/{id}/projects/{project_id}/ai_models', init));
158
+ },
159
+ /**
160
+ * POST /v2/workspaces/{id}/projects/{project_id}/ai_models/benchmarks — ai-create-benchmarks-v2
161
+ */
162
+ createBenchmarks(init) {
163
+ return unwrap('POST', client.POST('/v2/workspaces/{id}/projects/{project_id}/ai_models/benchmarks', init));
164
+ },
165
+
166
+ // ─── /v2 brand URLs ───────────────────────────────────────────────────────
167
+ /**
168
+ * GET /v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls
169
+ * — aio-list-brand-urls
170
+ */
171
+ listBrandUrls(init) {
172
+ return unwrap('GET', client.GET('/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls', init));
173
+ },
174
+ /**
175
+ * POST /v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls
176
+ * — aio-create-brand-urls
177
+ */
178
+ createBrandUrls(init) {
179
+ return unwrap('POST', client.POST('/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls', init));
180
+ },
181
+ /**
182
+ * DELETE /v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls
183
+ * — aio-delete-brand-urls
184
+ */
185
+ deleteBrandUrls(init) {
186
+ return unwrap('DELETE', client.DELETE('/v2/workspaces/{id}/projects/{project_id}/aio/benchmarks/{benchmark_id}/brand_urls', init));
187
+ },
188
+
189
+ // ─── /v2 AIO project state, prompts, tags ─────────────────────────────────
190
+ /**
191
+ * GET /v2/workspaces/{id}/projects/{project_id}/aio/init_status
192
+ * — aio-get-project-init-status-v2
193
+ */
194
+ getProjectInitStatus(init) {
195
+ return unwrap('GET', client.GET('/v2/workspaces/{id}/projects/{project_id}/aio/init_status', init));
196
+ },
197
+ /** POST /v2/workspaces/{id}/projects/{project_id}/aio/prompts — aio-create-prompt-v2 */
198
+ createPrompts(init) {
199
+ return unwrap('POST', client.POST('/v2/workspaces/{id}/projects/{project_id}/aio/prompts', init));
200
+ },
201
+ /**
202
+ * DELETE /v2/workspaces/{id}/projects/{project_id}/aio/prompts — aio-delete-prompt-by-ids-v2
203
+ */
204
+ deletePromptsByIds(init) {
205
+ return unwrap('DELETE', client.DELETE('/v2/workspaces/{id}/projects/{project_id}/aio/prompts', init));
206
+ },
207
+ /**
208
+ * POST /v2/workspaces/{id}/projects/{project_id}/aio/prompts/by_tags
209
+ * — aio-list-prompts-by-tag-ids
210
+ */
211
+ listPromptsByTagIds(init) {
212
+ return unwrap('POST', client.POST('/v2/workspaces/{id}/projects/{project_id}/aio/prompts/by_tags', init));
213
+ },
214
+ /** PUT /v2/workspaces/{id}/projects/{project_id}/aio/prompts/tags — aio-update-prompts-batch */
215
+ updatePromptTags(init) {
216
+ return unwrap('PUT', client.PUT('/v2/workspaces/{id}/projects/{project_id}/aio/prompts/tags', init));
217
+ },
218
+ /**
219
+ * POST /v2/workspaces/{id}/projects/{project_id}/aio/prompts/{prompt_id}/rename
220
+ * — aio-rename-prompt
221
+ */
222
+ renamePrompt(init) {
223
+ return unwrap('POST', client.POST('/v2/workspaces/{id}/projects/{project_id}/aio/prompts/{prompt_id}/rename', init));
224
+ },
225
+ /** GET /v2/workspaces/{id}/projects/{project_id}/aio/tags — aio-get-project-tags */
226
+ listProjectTags(init) {
227
+ return unwrap('GET', client.GET('/v2/workspaces/{id}/projects/{project_id}/aio/tags', init));
228
+ },
229
+ /** POST /v2/workspaces/{id}/projects/{project_id}/aio/tags — aio-create-project-tags */
230
+ createProjectTags(init) {
231
+ return unwrap('POST', client.POST('/v2/workspaces/{id}/projects/{project_id}/aio/tags', init));
232
+ },
233
+ /** PATCH /v2/workspaces/{id}/projects/{project_id}/aio/tags/{tag_id} — aio-update-tag */
234
+ updateProjectTag(init) {
235
+ return unwrap('PATCH', client.PATCH('/v2/workspaces/{id}/projects/{project_id}/aio/tags/{tag_id}', init));
236
+ },
237
+ };
238
+ }