@foxnose/sdk 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.
package/dist/index.js ADDED
@@ -0,0 +1,1353 @@
1
+ // src/auth/anonymous.ts
2
+ var AnonymousAuth = class {
3
+ buildHeaders(_request) {
4
+ return {};
5
+ }
6
+ };
7
+
8
+ // src/auth/jwt.ts
9
+ var StaticTokenProvider = class {
10
+ constructor(token) {
11
+ this.token = token;
12
+ }
13
+ getToken() {
14
+ return this.token;
15
+ }
16
+ };
17
+ var JWTAuth = class _JWTAuth {
18
+ provider;
19
+ scheme;
20
+ constructor(provider, options) {
21
+ this.provider = provider;
22
+ this.scheme = options?.scheme ?? "Bearer";
23
+ }
24
+ buildHeaders(_request) {
25
+ const token = this.provider.getToken();
26
+ if (!token) {
27
+ throw new Error("Token provider returned an empty token");
28
+ }
29
+ return { Authorization: `${this.scheme} ${token}` };
30
+ }
31
+ /**
32
+ * Convenience constructor for scripts with manual token management.
33
+ */
34
+ static fromStaticToken(token, options) {
35
+ return new _JWTAuth(new StaticTokenProvider(token), options);
36
+ }
37
+ };
38
+
39
+ // src/auth/secure.ts
40
+ import { createHash, createSign } from "crypto";
41
+
42
+ // src/errors.ts
43
+ var FoxnoseError = class extends Error {
44
+ constructor(message) {
45
+ super(message);
46
+ this.name = "FoxnoseError";
47
+ }
48
+ };
49
+ var FoxnoseAPIError = class extends FoxnoseError {
50
+ statusCode;
51
+ errorCode;
52
+ detail;
53
+ responseHeaders;
54
+ responseBody;
55
+ constructor(options) {
56
+ const code = options.errorCode ? `, error_code=${options.errorCode}` : "";
57
+ super(`${options.message} (status=${options.statusCode}${code})`);
58
+ this.name = "FoxnoseAPIError";
59
+ this.statusCode = options.statusCode;
60
+ this.errorCode = options.errorCode;
61
+ this.detail = options.detail;
62
+ this.responseHeaders = options.responseHeaders;
63
+ this.responseBody = options.responseBody;
64
+ }
65
+ };
66
+ var FoxnoseAuthError = class extends FoxnoseError {
67
+ constructor(message) {
68
+ super(message);
69
+ this.name = "FoxnoseAuthError";
70
+ }
71
+ };
72
+ var FoxnoseTransportError = class extends FoxnoseError {
73
+ constructor(message) {
74
+ super(message);
75
+ this.name = "FoxnoseTransportError";
76
+ }
77
+ };
78
+
79
+ // src/auth/secure.ts
80
+ var SecureKeyAuth = class {
81
+ publicKey;
82
+ privateKeyPem;
83
+ clock;
84
+ constructor(publicKey, privateKey, options) {
85
+ if (!publicKey || !privateKey) {
86
+ throw new Error("publicKey and privateKey are required");
87
+ }
88
+ this.publicKey = publicKey;
89
+ this.clock = options?.clock ?? (() => /* @__PURE__ */ new Date());
90
+ try {
91
+ const derB64 = privateKey;
92
+ this.privateKeyPem = `-----BEGIN EC PRIVATE KEY-----
93
+ ` + derB64.match(/.{1,64}/g).join("\n") + `
94
+ -----END EC PRIVATE KEY-----`;
95
+ } catch (err) {
96
+ throw new FoxnoseAuthError(`Failed to load private key: ${err}`);
97
+ }
98
+ }
99
+ buildHeaders(request) {
100
+ const body = request.body ?? new Uint8Array(0);
101
+ const now = this.clock();
102
+ const timestamp = now.toISOString().replace(/\.\d{3}Z$/, "Z");
103
+ let path;
104
+ try {
105
+ const parsed = new URL(request.url);
106
+ path = parsed.pathname || "/";
107
+ if (parsed.search) {
108
+ path = `${path}${parsed.search}`;
109
+ }
110
+ } catch {
111
+ path = request.path || "/";
112
+ }
113
+ const bodyHash = createHash("sha256").update(body).digest("hex");
114
+ const dataToSign = `${path}|${bodyHash}|${timestamp}`;
115
+ const signer = createSign("SHA256");
116
+ signer.update(dataToSign);
117
+ signer.end();
118
+ const signatureBuffer = signer.sign({
119
+ key: this.privateKeyPem,
120
+ dsaEncoding: "der"
121
+ });
122
+ const signatureB64 = signatureBuffer.toString("base64");
123
+ return {
124
+ Authorization: `Secure ${this.publicKey}:${signatureB64}`,
125
+ Date: timestamp
126
+ };
127
+ }
128
+ };
129
+
130
+ // src/auth/simple.ts
131
+ var SimpleKeyAuth = class {
132
+ publicKey;
133
+ secretKey;
134
+ constructor(publicKey, secretKey) {
135
+ if (!publicKey || !secretKey) {
136
+ throw new Error("publicKey and secretKey are required");
137
+ }
138
+ this.publicKey = publicKey;
139
+ this.secretKey = secretKey;
140
+ }
141
+ buildHeaders(_request) {
142
+ return { Authorization: `Simple ${this.publicKey}:${this.secretKey}` };
143
+ }
144
+ };
145
+
146
+ // src/config.ts
147
+ var DEFAULT_RETRY_CONFIG = {
148
+ attempts: 3,
149
+ backoffFactor: 0.5,
150
+ statusCodes: [408, 425, 429, 500, 502, 503, 504],
151
+ methods: ["GET", "HEAD", "OPTIONS", "PUT", "DELETE"]
152
+ };
153
+ var SDK_VERSION = "0.1.0";
154
+ var DEFAULT_USER_AGENT = `foxnose-sdk-js/${SDK_VERSION}`;
155
+ function createConfig(options) {
156
+ if (!options.baseUrl) {
157
+ throw new Error("baseUrl must be provided");
158
+ }
159
+ return {
160
+ baseUrl: options.baseUrl.replace(/\/+$/, ""),
161
+ timeout: options.timeout ?? 3e4,
162
+ defaultHeaders: options.defaultHeaders,
163
+ userAgent: options.userAgent ?? DEFAULT_USER_AGENT
164
+ };
165
+ }
166
+
167
+ // src/http.ts
168
+ var HttpTransport = class {
169
+ config;
170
+ auth;
171
+ retry;
172
+ constructor(options) {
173
+ this.config = options.config;
174
+ this.auth = options.auth ?? new AnonymousAuth();
175
+ this.retry = options.retryConfig ?? { ...DEFAULT_RETRY_CONFIG };
176
+ }
177
+ async request(method, path, options) {
178
+ const parseJson = options?.parseJson ?? true;
179
+ const response = await this.sendWithRetries(method, path, options);
180
+ return this.maybeDecodeResponse(response, parseJson);
181
+ }
182
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
183
+ close() {
184
+ }
185
+ buildUrl(path, params) {
186
+ const base = this.config.baseUrl + path;
187
+ if (!params || Object.keys(params).length === 0) {
188
+ return base;
189
+ }
190
+ const searchParams = new URLSearchParams();
191
+ for (const [key, value] of Object.entries(params)) {
192
+ if (value !== void 0 && value !== null) {
193
+ searchParams.append(key, String(value));
194
+ }
195
+ }
196
+ const qs = searchParams.toString();
197
+ return qs ? `${base}?${qs}` : base;
198
+ }
199
+ buildRequest(method, path, options) {
200
+ const headers = {};
201
+ if (this.config.defaultHeaders) {
202
+ Object.assign(headers, this.config.defaultHeaders);
203
+ }
204
+ headers["User-Agent"] = headers["User-Agent"] ?? this.config.userAgent;
205
+ if (options?.headers) {
206
+ Object.assign(headers, options.headers);
207
+ }
208
+ let body = new Uint8Array(0);
209
+ let bodyInit;
210
+ if (options?.jsonBody !== void 0) {
211
+ const jsonStr = JSON.stringify(options.jsonBody);
212
+ body = new TextEncoder().encode(jsonStr);
213
+ bodyInit = jsonStr;
214
+ headers["Content-Type"] = headers["Content-Type"] ?? "application/json";
215
+ } else if (options?.content) {
216
+ body = options.content;
217
+ bodyInit = options.content;
218
+ }
219
+ const url = this.buildUrl(path, options?.params);
220
+ const requestData = {
221
+ method: method.toUpperCase(),
222
+ url,
223
+ path: this.buildSigningPath(path, options?.params),
224
+ body
225
+ };
226
+ const authHeaders = this.auth.buildHeaders(requestData);
227
+ if (authHeaders) {
228
+ Object.assign(headers, authHeaders);
229
+ }
230
+ const init = {
231
+ method: method.toUpperCase(),
232
+ headers,
233
+ body: bodyInit
234
+ };
235
+ return { url, init, body };
236
+ }
237
+ buildSigningPath(path, params) {
238
+ if (!params || Object.keys(params).length === 0) {
239
+ return path;
240
+ }
241
+ const searchParams = new URLSearchParams();
242
+ for (const [key, value] of Object.entries(params)) {
243
+ if (value !== void 0 && value !== null) {
244
+ searchParams.append(key, String(value));
245
+ }
246
+ }
247
+ const qs = searchParams.toString();
248
+ return qs ? `${path}?${qs}` : path;
249
+ }
250
+ shouldRetry(method, statusCode) {
251
+ if (!this.retry.methods.includes(method.toUpperCase())) {
252
+ return false;
253
+ }
254
+ return this.retry.statusCodes.includes(statusCode);
255
+ }
256
+ computeDelay(attempt, retryAfter) {
257
+ if (retryAfter) {
258
+ const parsed = parseFloat(retryAfter);
259
+ if (!isNaN(parsed)) {
260
+ return parsed * 1e3;
261
+ }
262
+ return 0;
263
+ }
264
+ const base = this.retry.backoffFactor * Math.pow(2, Math.max(attempt - 1, 0)) * 1e3;
265
+ return base * (0.5 + Math.random() * 0.5);
266
+ }
267
+ async maybeDecodeResponse(response, parseJson) {
268
+ if (!parseJson) {
269
+ return response;
270
+ }
271
+ const text = await response.text();
272
+ if (!text) {
273
+ return null;
274
+ }
275
+ try {
276
+ return JSON.parse(text);
277
+ } catch {
278
+ return text;
279
+ }
280
+ }
281
+ async sendWithRetries(method, path, options) {
282
+ for (let attempt = 1; attempt <= this.retry.attempts; attempt++) {
283
+ const { url, init } = this.buildRequest(method, path, options);
284
+ const controller = new AbortController();
285
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
286
+ init.signal = controller.signal;
287
+ let response;
288
+ try {
289
+ response = await fetch(url, init);
290
+ } catch (err) {
291
+ clearTimeout(timeoutId);
292
+ const delay = this.handleTransportError(err, method, attempt);
293
+ if (delay > 0) {
294
+ await this.sleep(delay);
295
+ }
296
+ continue;
297
+ } finally {
298
+ clearTimeout(timeoutId);
299
+ }
300
+ if (response.status >= 400) {
301
+ if (this.shouldRetry(method, response.status) && attempt < this.retry.attempts) {
302
+ const delay = this.computeDelay(attempt, response.headers.get("Retry-After"));
303
+ if (delay > 0) {
304
+ await this.sleep(delay);
305
+ }
306
+ continue;
307
+ }
308
+ await this.raiseAPIError(response);
309
+ }
310
+ return response;
311
+ }
312
+ throw new FoxnoseTransportError("All retry attempts exhausted");
313
+ }
314
+ handleTransportError(err, method, attempt) {
315
+ const canRetry = this.retry.methods.includes(method.toUpperCase()) && attempt < this.retry.attempts;
316
+ if (!canRetry) {
317
+ const message = err instanceof Error ? err.message : String(err);
318
+ throw new FoxnoseTransportError(message);
319
+ }
320
+ return this.computeDelay(attempt);
321
+ }
322
+ async raiseAPIError(response) {
323
+ let message = "";
324
+ let errorCode;
325
+ let detail;
326
+ let body;
327
+ try {
328
+ const text = await response.text();
329
+ message = text;
330
+ if (text) {
331
+ try {
332
+ const payload = JSON.parse(text);
333
+ message = payload.message ?? message;
334
+ errorCode = payload.error_code;
335
+ detail = payload.detail;
336
+ body = payload;
337
+ } catch {
338
+ body = text;
339
+ }
340
+ }
341
+ } catch {
342
+ }
343
+ const responseHeaders = {};
344
+ response.headers.forEach((value, key) => {
345
+ responseHeaders[key] = value;
346
+ });
347
+ throw new FoxnoseAPIError({
348
+ message: message || "API request failed",
349
+ statusCode: response.status,
350
+ errorCode,
351
+ detail,
352
+ responseHeaders,
353
+ responseBody: body
354
+ });
355
+ }
356
+ sleep(ms) {
357
+ return new Promise((resolve) => setTimeout(resolve, ms));
358
+ }
359
+ };
360
+
361
+ // src/management/models.ts
362
+ function resolveKey(value) {
363
+ if (typeof value === "string") {
364
+ return value;
365
+ }
366
+ if (value && typeof value === "object" && "key" in value && typeof value.key === "string") {
367
+ return value.key;
368
+ }
369
+ throw new TypeError(
370
+ `Expected a string or an object with a 'key' attribute, got ${typeof value}`
371
+ );
372
+ }
373
+
374
+ // src/management/paths.ts
375
+ function managementPaths(environmentKey) {
376
+ return {
377
+ // Organization paths
378
+ orgRoot: (orgKey) => `/organizations/${orgKey}`,
379
+ projectsBase: (orgKey) => `/organizations/${orgKey}/projects`,
380
+ projectRoot: (orgKey, projectKey) => `/organizations/${orgKey}/projects/${projectKey}`,
381
+ environmentsBase: (orgKey, projectKey) => `/organizations/${orgKey}/projects/${projectKey}/environments`,
382
+ environmentRoot: (orgKey, projectKey, envKey) => `/organizations/${orgKey}/projects/${projectKey}/environments/${envKey}`,
383
+ // Folder paths
384
+ foldersTreeRoot: () => `/v1/${environmentKey}/folders/tree`,
385
+ foldersTreeItem: () => `/v1/${environmentKey}/folders/tree/folder`,
386
+ folderVersionsBase: (folderKey) => `/v1/${environmentKey}/folders/${folderKey}/model/versions`,
387
+ folderSchemaTree: (folderKey, versionKey) => `/v1/${environmentKey}/folders/${folderKey}/model/versions/${versionKey}/schema/tree`,
388
+ // Component paths
389
+ componentsRoot: () => `/v1/${environmentKey}/components`,
390
+ componentRoot: (componentKey) => `/v1/${environmentKey}/components/${componentKey}`,
391
+ componentVersionsBase: (componentKey) => `/v1/${environmentKey}/components/${componentKey}/model/versions`,
392
+ componentSchemaTree: (componentKey, versionKey) => `/v1/${environmentKey}/components/${componentKey}/model/versions/${versionKey}/schema/tree`,
393
+ // Resource paths
394
+ resourceBase: (folderKey) => `/v1/${environmentKey}/folders/${folderKey}/resources`,
395
+ revisionBase: (folderKey, resourceKey) => `/v1/${environmentKey}/folders/${folderKey}/resources/${resourceKey}/revisions`,
396
+ // Management API key paths
397
+ managementApiKeysRoot: () => `/v1/${environmentKey}/permissions/management-api/api-keys`,
398
+ managementApiKeyRoot: (apiKey) => `/v1/${environmentKey}/permissions/management-api/api-keys/${apiKey}`,
399
+ // Flux API key paths
400
+ fluxApiKeysRoot: () => `/v1/${environmentKey}/permissions/flux-api/api-keys`,
401
+ fluxApiKeyRoot: (apiKey) => `/v1/${environmentKey}/permissions/flux-api/api-keys/${apiKey}`,
402
+ // API management paths
403
+ apisRoot: () => `/v1/${environmentKey}/api`,
404
+ apiRoot: (apiKey) => `/v1/${environmentKey}/api/${apiKey}`,
405
+ apiFoldersRoot: (apiKey) => `/v1/${environmentKey}/api/${apiKey}/folders`,
406
+ // Management role paths
407
+ managementRolesRoot: () => `/v1/${environmentKey}/permissions/management-api/roles`,
408
+ managementRoleRoot: (roleKey) => `/v1/${environmentKey}/permissions/management-api/roles/${roleKey}`,
409
+ rolePermissionsRoot: (roleKey) => `/v1/${environmentKey}/permissions/management-api/roles/${roleKey}/permissions`,
410
+ rolePermissionsBatch: (roleKey) => `/v1/${environmentKey}/permissions/management-api/roles/${roleKey}/permissions/batch`,
411
+ rolePermissionObjectsRoot: (roleKey) => `/v1/${environmentKey}/permissions/management-api/roles/${roleKey}/permissions/objects`,
412
+ // Flux role paths
413
+ fluxRolesRoot: () => `/v1/${environmentKey}/permissions/flux-api/roles`,
414
+ fluxRoleRoot: (roleKey) => `/v1/${environmentKey}/permissions/flux-api/roles/${roleKey}`,
415
+ fluxRolePermissionsRoot: (roleKey) => `/v1/${environmentKey}/permissions/flux-api/roles/${roleKey}/permissions`,
416
+ fluxRolePermissionsBatch: (roleKey) => `/v1/${environmentKey}/permissions/flux-api/roles/${roleKey}/permissions/batch`,
417
+ fluxRolePermissionObjectsRoot: (roleKey) => `/v1/${environmentKey}/permissions/flux-api/roles/${roleKey}/permissions/objects`,
418
+ // Locale paths
419
+ localesRoot: () => `/v1/${environmentKey}/locales`,
420
+ localeRoot: (code) => `/v1/${environmentKey}/locales/${code}`
421
+ };
422
+ }
423
+
424
+ // src/management/client.ts
425
+ var ManagementClient = class {
426
+ environmentKey;
427
+ transport;
428
+ paths;
429
+ constructor(options) {
430
+ if (!options.environmentKey) {
431
+ throw new Error("environmentKey must be provided");
432
+ }
433
+ this.environmentKey = options.environmentKey;
434
+ const config = createConfig({
435
+ baseUrl: options.baseUrl ?? "https://api.foxnose.net",
436
+ timeout: options.timeout,
437
+ defaultHeaders: options.defaultHeaders
438
+ });
439
+ this.transport = new HttpTransport({
440
+ config,
441
+ auth: options.auth,
442
+ retryConfig: options.retryConfig
443
+ });
444
+ this.paths = managementPaths(options.environmentKey);
445
+ }
446
+ /**
447
+ * Low-level escape hatch for calling arbitrary endpoints.
448
+ */
449
+ async request(method, path, options) {
450
+ return this.transport.request(method, path, options);
451
+ }
452
+ close() {
453
+ this.transport.close();
454
+ }
455
+ // ------------------------------------------------------------------ //
456
+ // Organization operations
457
+ // ------------------------------------------------------------------ //
458
+ async listOrganizations() {
459
+ const payload = await this.request("GET", "/organizations/") ?? [];
460
+ return Array.isArray(payload) ? payload : [payload];
461
+ }
462
+ async getOrganization(orgKey) {
463
+ const key = resolveKey(orgKey);
464
+ return this.request("GET", `${this.paths.orgRoot(key)}/`);
465
+ }
466
+ async updateOrganization(orgKey, payload) {
467
+ const key = resolveKey(orgKey);
468
+ return this.request("PUT", `${this.paths.orgRoot(key)}/`, { jsonBody: payload });
469
+ }
470
+ async listRegions() {
471
+ const payload = await this.request("GET", "/regions/") ?? [];
472
+ return Array.isArray(payload) ? payload : [payload];
473
+ }
474
+ async getAvailablePlans() {
475
+ return this.request("GET", "/plans/");
476
+ }
477
+ async getOrganizationPlan(orgKey) {
478
+ const key = resolveKey(orgKey);
479
+ return this.request("GET", `${this.paths.orgRoot(key)}/plan/`);
480
+ }
481
+ async setOrganizationPlan(orgKey, planCode) {
482
+ const key = resolveKey(orgKey);
483
+ return this.request("POST", `${this.paths.orgRoot(key)}/plan/${planCode}/`);
484
+ }
485
+ async getOrganizationUsage(orgKey) {
486
+ const key = resolveKey(orgKey);
487
+ return this.request("GET", `${this.paths.orgRoot(key)}/usage/`);
488
+ }
489
+ // ------------------------------------------------------------------ //
490
+ // Management API key operations
491
+ // ------------------------------------------------------------------ //
492
+ async listManagementApiKeys(params) {
493
+ return this.request("GET", `${this.paths.managementApiKeysRoot()}/`, { params });
494
+ }
495
+ async createManagementApiKey(payload) {
496
+ return this.request("POST", `${this.paths.managementApiKeysRoot()}/`, {
497
+ jsonBody: payload
498
+ });
499
+ }
500
+ async getManagementApiKey(key) {
501
+ const k = resolveKey(key);
502
+ return this.request("GET", `${this.paths.managementApiKeyRoot(k)}/`);
503
+ }
504
+ async updateManagementApiKey(key, payload) {
505
+ const k = resolveKey(key);
506
+ return this.request("PUT", `${this.paths.managementApiKeyRoot(k)}/`, {
507
+ jsonBody: payload
508
+ });
509
+ }
510
+ async deleteManagementApiKey(key) {
511
+ const k = resolveKey(key);
512
+ await this.request("DELETE", `${this.paths.managementApiKeyRoot(k)}/`, {
513
+ parseJson: false
514
+ });
515
+ }
516
+ // ------------------------------------------------------------------ //
517
+ // Flux API key operations
518
+ // ------------------------------------------------------------------ //
519
+ async listFluxApiKeys(params) {
520
+ return this.request("GET", `${this.paths.fluxApiKeysRoot()}/`, { params });
521
+ }
522
+ async createFluxApiKey(payload) {
523
+ return this.request("POST", `${this.paths.fluxApiKeysRoot()}/`, { jsonBody: payload });
524
+ }
525
+ async getFluxApiKey(key) {
526
+ const k = resolveKey(key);
527
+ return this.request("GET", `${this.paths.fluxApiKeyRoot(k)}/`);
528
+ }
529
+ async updateFluxApiKey(key, payload) {
530
+ const k = resolveKey(key);
531
+ return this.request("PUT", `${this.paths.fluxApiKeyRoot(k)}/`, { jsonBody: payload });
532
+ }
533
+ async deleteFluxApiKey(key) {
534
+ const k = resolveKey(key);
535
+ await this.request("DELETE", `${this.paths.fluxApiKeyRoot(k)}/`, { parseJson: false });
536
+ }
537
+ // ------------------------------------------------------------------ //
538
+ // API management operations
539
+ // ------------------------------------------------------------------ //
540
+ async listApis(params) {
541
+ return this.request("GET", `${this.paths.apisRoot()}/`, { params });
542
+ }
543
+ async createApi(payload) {
544
+ return this.request("POST", `${this.paths.apisRoot()}/`, { jsonBody: payload });
545
+ }
546
+ async getApi(apiKey) {
547
+ const key = resolveKey(apiKey);
548
+ return this.request("GET", `${this.paths.apiRoot(key)}/`);
549
+ }
550
+ async updateApi(apiKey, payload) {
551
+ const key = resolveKey(apiKey);
552
+ return this.request("PUT", `${this.paths.apiRoot(key)}/`, { jsonBody: payload });
553
+ }
554
+ async deleteApi(apiKey) {
555
+ const key = resolveKey(apiKey);
556
+ await this.request("DELETE", `${this.paths.apiRoot(key)}/`, { parseJson: false });
557
+ }
558
+ // ------------------------------------------------------------------ //
559
+ // API Folder associations
560
+ // ------------------------------------------------------------------ //
561
+ async listApiFolders(apiKey, params) {
562
+ const key = resolveKey(apiKey);
563
+ return this.request("GET", `${this.paths.apiFoldersRoot(key)}/`, { params });
564
+ }
565
+ async addApiFolder(apiKey, folderKey, options) {
566
+ const aKey = resolveKey(apiKey);
567
+ const fKey = resolveKey(folderKey);
568
+ const body = { folder: fKey };
569
+ if (options?.allowedMethods) {
570
+ body.allowed_methods = options.allowedMethods;
571
+ }
572
+ return this.request("POST", `${this.paths.apiFoldersRoot(aKey)}/`, { jsonBody: body });
573
+ }
574
+ async getApiFolder(apiKey, folderKey) {
575
+ const aKey = resolveKey(apiKey);
576
+ const fKey = resolveKey(folderKey);
577
+ return this.request("GET", `${this.paths.apiFoldersRoot(aKey)}/${fKey}/`);
578
+ }
579
+ async updateApiFolder(apiKey, folderKey, options) {
580
+ const aKey = resolveKey(apiKey);
581
+ const fKey = resolveKey(folderKey);
582
+ const body = {};
583
+ if (options?.allowedMethods) {
584
+ body.allowed_methods = options.allowedMethods;
585
+ }
586
+ return this.request("PUT", `${this.paths.apiFoldersRoot(aKey)}/${fKey}/`, {
587
+ jsonBody: body
588
+ });
589
+ }
590
+ async removeApiFolder(apiKey, folderKey) {
591
+ const aKey = resolveKey(apiKey);
592
+ const fKey = resolveKey(folderKey);
593
+ await this.request("DELETE", `${this.paths.apiFoldersRoot(aKey)}/${fKey}/`, {
594
+ parseJson: false
595
+ });
596
+ }
597
+ // ------------------------------------------------------------------ //
598
+ // Management role operations
599
+ // ------------------------------------------------------------------ //
600
+ async listManagementRoles(params) {
601
+ return this.request("GET", `${this.paths.managementRolesRoot()}/`, { params });
602
+ }
603
+ async createManagementRole(payload) {
604
+ return this.request("POST", `${this.paths.managementRolesRoot()}/`, {
605
+ jsonBody: payload
606
+ });
607
+ }
608
+ async getManagementRole(roleKey) {
609
+ const key = resolveKey(roleKey);
610
+ return this.request("GET", `${this.paths.managementRoleRoot(key)}/`);
611
+ }
612
+ async updateManagementRole(roleKey, payload) {
613
+ const key = resolveKey(roleKey);
614
+ return this.request("PUT", `${this.paths.managementRoleRoot(key)}/`, {
615
+ jsonBody: payload
616
+ });
617
+ }
618
+ async deleteManagementRole(roleKey) {
619
+ const key = resolveKey(roleKey);
620
+ await this.request("DELETE", `${this.paths.managementRoleRoot(key)}/`, {
621
+ parseJson: false
622
+ });
623
+ }
624
+ // Management role permissions
625
+ async listManagementRolePermissions(roleKey) {
626
+ const key = resolveKey(roleKey);
627
+ const payload = await this.request("GET", `${this.paths.rolePermissionsRoot(key)}/`) ?? [];
628
+ return Array.isArray(payload) ? payload : [payload];
629
+ }
630
+ async upsertManagementRolePermission(roleKey, payload) {
631
+ const key = resolveKey(roleKey);
632
+ return this.request("POST", `${this.paths.rolePermissionsRoot(key)}/`, {
633
+ jsonBody: payload
634
+ });
635
+ }
636
+ async deleteManagementRolePermission(roleKey, contentType) {
637
+ const key = resolveKey(roleKey);
638
+ await this.request("DELETE", `${this.paths.rolePermissionsRoot(key)}/`, {
639
+ params: { content_type: contentType },
640
+ parseJson: false
641
+ });
642
+ }
643
+ async replaceManagementRolePermissions(roleKey, permissions) {
644
+ const key = resolveKey(roleKey);
645
+ const payload = await this.request("POST", `${this.paths.rolePermissionsBatch(key)}/`, {
646
+ jsonBody: permissions
647
+ });
648
+ return Array.isArray(payload) ? payload : [];
649
+ }
650
+ // Management permission objects
651
+ async listManagementPermissionObjects(roleKey, contentType) {
652
+ const key = resolveKey(roleKey);
653
+ const payload = await this.request("GET", `${this.paths.rolePermissionObjectsRoot(key)}/`, {
654
+ params: { content_type: contentType }
655
+ }) ?? [];
656
+ return Array.isArray(payload) ? payload : [payload];
657
+ }
658
+ async addManagementPermissionObject(roleKey, payload) {
659
+ const key = resolveKey(roleKey);
660
+ return this.request("POST", `${this.paths.rolePermissionObjectsRoot(key)}/`, {
661
+ jsonBody: payload
662
+ });
663
+ }
664
+ async deleteManagementPermissionObject(roleKey, payload) {
665
+ const key = resolveKey(roleKey);
666
+ await this.request("DELETE", `${this.paths.rolePermissionObjectsRoot(key)}/`, {
667
+ jsonBody: payload,
668
+ parseJson: false
669
+ });
670
+ }
671
+ // ------------------------------------------------------------------ //
672
+ // Flux role operations
673
+ // ------------------------------------------------------------------ //
674
+ async listFluxRoles(params) {
675
+ return this.request("GET", `${this.paths.fluxRolesRoot()}/`, { params });
676
+ }
677
+ async createFluxRole(payload) {
678
+ return this.request("POST", `${this.paths.fluxRolesRoot()}/`, { jsonBody: payload });
679
+ }
680
+ async getFluxRole(roleKey) {
681
+ const key = resolveKey(roleKey);
682
+ return this.request("GET", `${this.paths.fluxRoleRoot(key)}/`);
683
+ }
684
+ async updateFluxRole(roleKey, payload) {
685
+ const key = resolveKey(roleKey);
686
+ return this.request("PUT", `${this.paths.fluxRoleRoot(key)}/`, { jsonBody: payload });
687
+ }
688
+ async deleteFluxRole(roleKey) {
689
+ const key = resolveKey(roleKey);
690
+ await this.request("DELETE", `${this.paths.fluxRoleRoot(key)}/`, { parseJson: false });
691
+ }
692
+ // Flux role permissions
693
+ async listFluxRolePermissions(roleKey) {
694
+ const key = resolveKey(roleKey);
695
+ const payload = await this.request("GET", `${this.paths.fluxRolePermissionsRoot(key)}/`) ?? [];
696
+ return Array.isArray(payload) ? payload : [payload];
697
+ }
698
+ async upsertFluxRolePermission(roleKey, payload) {
699
+ const key = resolveKey(roleKey);
700
+ return this.request("POST", `${this.paths.fluxRolePermissionsRoot(key)}/`, {
701
+ jsonBody: payload
702
+ });
703
+ }
704
+ async deleteFluxRolePermission(roleKey, contentType) {
705
+ const key = resolveKey(roleKey);
706
+ await this.request(
707
+ "DELETE",
708
+ `${this.paths.fluxRolePermissionsRoot(key)}/`,
709
+ { params: { content_type: contentType }, parseJson: false }
710
+ );
711
+ }
712
+ async replaceFluxRolePermissions(roleKey, permissions) {
713
+ const key = resolveKey(roleKey);
714
+ const payload = await this.request("POST", `${this.paths.fluxRolePermissionsBatch(key)}/`, {
715
+ jsonBody: permissions
716
+ });
717
+ return Array.isArray(payload) ? payload : [];
718
+ }
719
+ // Flux permission objects
720
+ async listFluxPermissionObjects(roleKey, contentType) {
721
+ const key = resolveKey(roleKey);
722
+ const payload = await this.request("GET", `${this.paths.fluxRolePermissionObjectsRoot(key)}/`, {
723
+ params: { content_type: contentType }
724
+ }) ?? [];
725
+ return Array.isArray(payload) ? payload : [payload];
726
+ }
727
+ async addFluxPermissionObject(roleKey, payload) {
728
+ const key = resolveKey(roleKey);
729
+ return this.request("POST", `${this.paths.fluxRolePermissionObjectsRoot(key)}/`, {
730
+ jsonBody: payload
731
+ });
732
+ }
733
+ async deleteFluxPermissionObject(roleKey, payload) {
734
+ const key = resolveKey(roleKey);
735
+ await this.request("DELETE", `${this.paths.fluxRolePermissionObjectsRoot(key)}/`, {
736
+ jsonBody: payload,
737
+ parseJson: false
738
+ });
739
+ }
740
+ // ------------------------------------------------------------------ //
741
+ // Folder operations
742
+ // ------------------------------------------------------------------ //
743
+ async listFolders(params) {
744
+ return this.request("GET", `${this.paths.foldersTreeRoot()}/`, { params });
745
+ }
746
+ async getFolder(folderKey) {
747
+ const key = resolveKey(folderKey);
748
+ return this.request("GET", `${this.paths.foldersTreeItem()}/`, {
749
+ params: { key }
750
+ });
751
+ }
752
+ async getFolderByPath(path) {
753
+ return this.request("GET", `${this.paths.foldersTreeItem()}/`, {
754
+ params: { path }
755
+ });
756
+ }
757
+ async listFolderTree(options) {
758
+ const params = {};
759
+ if (options?.key) params.key = options.key;
760
+ if (options?.mode) params.mode = options.mode;
761
+ return this.request("GET", `${this.paths.foldersTreeRoot()}/`, { params });
762
+ }
763
+ async createFolder(payload) {
764
+ return this.request("POST", `${this.paths.foldersTreeRoot()}/`, { jsonBody: payload });
765
+ }
766
+ async updateFolder(folderKey, payload) {
767
+ const key = resolveKey(folderKey);
768
+ return this.request("PUT", `${this.paths.foldersTreeItem()}/`, {
769
+ params: { key },
770
+ jsonBody: payload
771
+ });
772
+ }
773
+ async deleteFolder(folderKey) {
774
+ const key = resolveKey(folderKey);
775
+ await this.request("DELETE", `${this.paths.foldersTreeItem()}/`, {
776
+ params: { key },
777
+ parseJson: false
778
+ });
779
+ }
780
+ // ------------------------------------------------------------------ //
781
+ // Folder version operations
782
+ // ------------------------------------------------------------------ //
783
+ async listFolderVersions(folderKey, params) {
784
+ const key = resolveKey(folderKey);
785
+ return this.request("GET", `${this.paths.folderVersionsBase(key)}/`, { params });
786
+ }
787
+ async createFolderVersion(folderKey, payload, options) {
788
+ const key = resolveKey(folderKey);
789
+ const params = {};
790
+ if (options?.copyFrom) {
791
+ params.copy_from = resolveKey(options.copyFrom);
792
+ }
793
+ return this.request("POST", `${this.paths.folderVersionsBase(key)}/`, {
794
+ jsonBody: payload,
795
+ params: Object.keys(params).length > 0 ? params : void 0
796
+ });
797
+ }
798
+ async getFolderVersion(folderKey, versionKey, options) {
799
+ const fKey = resolveKey(folderKey);
800
+ const vKey = resolveKey(versionKey);
801
+ const params = {};
802
+ if (options?.includeSchema !== void 0) {
803
+ params.include_schema = options.includeSchema;
804
+ }
805
+ return this.request("GET", `${this.paths.folderVersionsBase(fKey)}/${vKey}/`, {
806
+ params: Object.keys(params).length > 0 ? params : void 0
807
+ });
808
+ }
809
+ async updateFolderVersion(folderKey, versionKey, payload) {
810
+ const fKey = resolveKey(folderKey);
811
+ const vKey = resolveKey(versionKey);
812
+ return this.request("PUT", `${this.paths.folderVersionsBase(fKey)}/${vKey}/`, {
813
+ jsonBody: payload
814
+ });
815
+ }
816
+ async deleteFolderVersion(folderKey, versionKey) {
817
+ const fKey = resolveKey(folderKey);
818
+ const vKey = resolveKey(versionKey);
819
+ await this.request("DELETE", `${this.paths.folderVersionsBase(fKey)}/${vKey}/`, {
820
+ parseJson: false
821
+ });
822
+ }
823
+ async publishFolderVersion(folderKey, versionKey) {
824
+ const fKey = resolveKey(folderKey);
825
+ const vKey = resolveKey(versionKey);
826
+ return this.request(
827
+ "POST",
828
+ `${this.paths.folderVersionsBase(fKey)}/${vKey}/publish/`
829
+ );
830
+ }
831
+ // ------------------------------------------------------------------ //
832
+ // Folder field operations
833
+ // ------------------------------------------------------------------ //
834
+ async listFolderFields(folderKey, versionKey, params) {
835
+ const fKey = resolveKey(folderKey);
836
+ const vKey = resolveKey(versionKey);
837
+ return this.request("GET", `${this.paths.folderSchemaTree(fKey, vKey)}/`, { params });
838
+ }
839
+ async createFolderField(folderKey, versionKey, payload) {
840
+ const fKey = resolveKey(folderKey);
841
+ const vKey = resolveKey(versionKey);
842
+ return this.request("POST", `${this.paths.folderSchemaTree(fKey, vKey)}/`, {
843
+ jsonBody: payload
844
+ });
845
+ }
846
+ async getFolderField(folderKey, versionKey, fieldPath) {
847
+ const fKey = resolveKey(folderKey);
848
+ const vKey = resolveKey(versionKey);
849
+ return this.request(
850
+ "GET",
851
+ `${this.paths.folderSchemaTree(fKey, vKey)}/field/`,
852
+ { params: { path: fieldPath } }
853
+ );
854
+ }
855
+ async updateFolderField(folderKey, versionKey, fieldPath, payload) {
856
+ const fKey = resolveKey(folderKey);
857
+ const vKey = resolveKey(versionKey);
858
+ return this.request(
859
+ "PUT",
860
+ `${this.paths.folderSchemaTree(fKey, vKey)}/field/`,
861
+ { params: { path: fieldPath }, jsonBody: payload }
862
+ );
863
+ }
864
+ async deleteFolderField(folderKey, versionKey, fieldPath) {
865
+ const fKey = resolveKey(folderKey);
866
+ const vKey = resolveKey(versionKey);
867
+ await this.request(
868
+ "DELETE",
869
+ `${this.paths.folderSchemaTree(fKey, vKey)}/field/`,
870
+ { params: { path: fieldPath }, parseJson: false }
871
+ );
872
+ }
873
+ // ------------------------------------------------------------------ //
874
+ // Component operations
875
+ // ------------------------------------------------------------------ //
876
+ async listComponents(params) {
877
+ return this.request("GET", `${this.paths.componentsRoot()}/`, { params });
878
+ }
879
+ async getComponent(componentKey) {
880
+ const key = resolveKey(componentKey);
881
+ return this.request("GET", `${this.paths.componentRoot(key)}/`);
882
+ }
883
+ async createComponent(payload) {
884
+ return this.request("POST", `${this.paths.componentsRoot()}/`, { jsonBody: payload });
885
+ }
886
+ async updateComponent(componentKey, payload) {
887
+ const key = resolveKey(componentKey);
888
+ return this.request("PUT", `${this.paths.componentRoot(key)}/`, { jsonBody: payload });
889
+ }
890
+ async deleteComponent(componentKey) {
891
+ const key = resolveKey(componentKey);
892
+ await this.request("DELETE", `${this.paths.componentRoot(key)}/`, { parseJson: false });
893
+ }
894
+ // ------------------------------------------------------------------ //
895
+ // Component version operations
896
+ // ------------------------------------------------------------------ //
897
+ async listComponentVersions(componentKey, params) {
898
+ const key = resolveKey(componentKey);
899
+ return this.request("GET", `${this.paths.componentVersionsBase(key)}/`, { params });
900
+ }
901
+ async createComponentVersion(componentKey, payload, options) {
902
+ const key = resolveKey(componentKey);
903
+ const params = {};
904
+ if (options?.copyFrom) {
905
+ params.copy_from = resolveKey(options.copyFrom);
906
+ }
907
+ return this.request("POST", `${this.paths.componentVersionsBase(key)}/`, {
908
+ jsonBody: payload,
909
+ params: Object.keys(params).length > 0 ? params : void 0
910
+ });
911
+ }
912
+ async getComponentVersion(componentKey, versionKey, options) {
913
+ const cKey = resolveKey(componentKey);
914
+ const vKey = resolveKey(versionKey);
915
+ const params = {};
916
+ if (options?.includeSchema !== void 0) {
917
+ params.include_schema = options.includeSchema;
918
+ }
919
+ return this.request("GET", `${this.paths.componentVersionsBase(cKey)}/${vKey}/`, {
920
+ params: Object.keys(params).length > 0 ? params : void 0
921
+ });
922
+ }
923
+ async publishComponentVersion(componentKey, versionKey) {
924
+ const cKey = resolveKey(componentKey);
925
+ const vKey = resolveKey(versionKey);
926
+ return this.request(
927
+ "POST",
928
+ `${this.paths.componentVersionsBase(cKey)}/${vKey}/publish/`
929
+ );
930
+ }
931
+ async updateComponentVersion(componentKey, versionKey, payload) {
932
+ const cKey = resolveKey(componentKey);
933
+ const vKey = resolveKey(versionKey);
934
+ return this.request("PUT", `${this.paths.componentVersionsBase(cKey)}/${vKey}/`, {
935
+ jsonBody: payload
936
+ });
937
+ }
938
+ async deleteComponentVersion(componentKey, versionKey) {
939
+ const cKey = resolveKey(componentKey);
940
+ const vKey = resolveKey(versionKey);
941
+ await this.request("DELETE", `${this.paths.componentVersionsBase(cKey)}/${vKey}/`, {
942
+ parseJson: false
943
+ });
944
+ }
945
+ // ------------------------------------------------------------------ //
946
+ // Component field operations
947
+ // ------------------------------------------------------------------ //
948
+ async listComponentFields(componentKey, versionKey, params) {
949
+ const cKey = resolveKey(componentKey);
950
+ const vKey = resolveKey(versionKey);
951
+ return this.request("GET", `${this.paths.componentSchemaTree(cKey, vKey)}/`, { params });
952
+ }
953
+ async createComponentField(componentKey, versionKey, payload) {
954
+ const cKey = resolveKey(componentKey);
955
+ const vKey = resolveKey(versionKey);
956
+ return this.request("POST", `${this.paths.componentSchemaTree(cKey, vKey)}/`, {
957
+ jsonBody: payload
958
+ });
959
+ }
960
+ async getComponentField(componentKey, versionKey, fieldPath) {
961
+ const cKey = resolveKey(componentKey);
962
+ const vKey = resolveKey(versionKey);
963
+ return this.request(
964
+ "GET",
965
+ `${this.paths.componentSchemaTree(cKey, vKey)}/field/`,
966
+ { params: { path: fieldPath } }
967
+ );
968
+ }
969
+ async updateComponentField(componentKey, versionKey, fieldPath, payload) {
970
+ const cKey = resolveKey(componentKey);
971
+ const vKey = resolveKey(versionKey);
972
+ return this.request(
973
+ "PUT",
974
+ `${this.paths.componentSchemaTree(cKey, vKey)}/field/`,
975
+ { params: { path: fieldPath }, jsonBody: payload }
976
+ );
977
+ }
978
+ async deleteComponentField(componentKey, versionKey, fieldPath) {
979
+ const cKey = resolveKey(componentKey);
980
+ const vKey = resolveKey(versionKey);
981
+ await this.request(
982
+ "DELETE",
983
+ `${this.paths.componentSchemaTree(cKey, vKey)}/field/`,
984
+ { params: { path: fieldPath }, parseJson: false }
985
+ );
986
+ }
987
+ // ------------------------------------------------------------------ //
988
+ // Resource operations
989
+ // ------------------------------------------------------------------ //
990
+ async listResources(folderKey, params) {
991
+ const key = resolveKey(folderKey);
992
+ return this.request("GET", `${this.paths.resourceBase(key)}/`, { params });
993
+ }
994
+ async getResource(folderKey, resourceKey) {
995
+ const fKey = resolveKey(folderKey);
996
+ const rKey = resolveKey(resourceKey);
997
+ return this.request("GET", `${this.paths.resourceBase(fKey)}/${rKey}/`);
998
+ }
999
+ async createResource(folderKey, payload, options) {
1000
+ const key = resolveKey(folderKey);
1001
+ const params = {};
1002
+ if (options?.component) {
1003
+ params.component = resolveKey(options.component);
1004
+ }
1005
+ const body = { ...payload };
1006
+ if (options?.externalId) {
1007
+ body.external_id = options.externalId;
1008
+ }
1009
+ return this.request("POST", `${this.paths.resourceBase(key)}/`, {
1010
+ params: Object.keys(params).length ? params : void 0,
1011
+ jsonBody: body
1012
+ });
1013
+ }
1014
+ async upsertResource(folderKey, payload, options) {
1015
+ const key = resolveKey(folderKey);
1016
+ const params = { external_id: options.externalId };
1017
+ if (options.component) {
1018
+ params.component = resolveKey(options.component);
1019
+ }
1020
+ return this.request("PUT", `${this.paths.resourceBase(key)}/`, {
1021
+ params,
1022
+ jsonBody: payload
1023
+ });
1024
+ }
1025
+ async batchUpsertResources(folderKey, items, options) {
1026
+ const fKey = resolveKey(folderKey);
1027
+ const maxConcurrency = options?.maxConcurrency ?? 5;
1028
+ if (maxConcurrency < 1) {
1029
+ throw new Error("maxConcurrency must be at least 1");
1030
+ }
1031
+ if (items.length === 0) {
1032
+ return { succeeded: [], failed: [] };
1033
+ }
1034
+ const failFast = options?.failFast ?? false;
1035
+ const onProgress = options?.onProgress;
1036
+ const succeeded = [];
1037
+ const failed = [];
1038
+ let completed = 0;
1039
+ const queue = [...items.entries()];
1040
+ const processItem = async (index, item) => {
1041
+ try {
1042
+ const params = { external_id: item.external_id };
1043
+ if (item.component) {
1044
+ params.component = item.component;
1045
+ }
1046
+ const result = await this.request(
1047
+ "PUT",
1048
+ `${this.paths.resourceBase(fKey)}/`,
1049
+ { params, jsonBody: item.payload }
1050
+ );
1051
+ succeeded.push(result);
1052
+ } catch (err) {
1053
+ failed.push({
1054
+ index,
1055
+ external_id: item.external_id,
1056
+ error: err instanceof Error ? err : new Error(String(err))
1057
+ });
1058
+ if (failFast) {
1059
+ throw err;
1060
+ }
1061
+ } finally {
1062
+ completed++;
1063
+ onProgress?.(completed, items.length);
1064
+ }
1065
+ };
1066
+ let i = 0;
1067
+ while (i < queue.length) {
1068
+ const batch = queue.slice(i, i + maxConcurrency);
1069
+ try {
1070
+ await Promise.all(batch.map(([index, item]) => processItem(index, item)));
1071
+ } catch (err) {
1072
+ if (failFast) throw err;
1073
+ }
1074
+ i += maxConcurrency;
1075
+ }
1076
+ return { succeeded, failed };
1077
+ }
1078
+ async updateResource(folderKey, resourceKey, payload) {
1079
+ const fKey = resolveKey(folderKey);
1080
+ const rKey = resolveKey(resourceKey);
1081
+ await this.request("PUT", `${this.paths.resourceBase(fKey)}/${rKey}/`, {
1082
+ jsonBody: payload,
1083
+ parseJson: false
1084
+ });
1085
+ return this.getResource(fKey, rKey);
1086
+ }
1087
+ async deleteResource(folderKey, resourceKey) {
1088
+ const fKey = resolveKey(folderKey);
1089
+ const rKey = resolveKey(resourceKey);
1090
+ await this.request("DELETE", `${this.paths.resourceBase(fKey)}/${rKey}/`, {
1091
+ parseJson: false
1092
+ });
1093
+ }
1094
+ async getResourceData(folderKey, resourceKey) {
1095
+ const fKey = resolveKey(folderKey);
1096
+ const rKey = resolveKey(resourceKey);
1097
+ return this.request("GET", `${this.paths.resourceBase(fKey)}/${rKey}/data/`);
1098
+ }
1099
+ // ------------------------------------------------------------------ //
1100
+ // Revision operations
1101
+ // ------------------------------------------------------------------ //
1102
+ async listRevisions(folderKey, resourceKey, params) {
1103
+ const fKey = resolveKey(folderKey);
1104
+ const rKey = resolveKey(resourceKey);
1105
+ return this.request("GET", `${this.paths.revisionBase(fKey, rKey)}/`, { params });
1106
+ }
1107
+ async createRevision(folderKey, resourceKey, payload) {
1108
+ const fKey = resolveKey(folderKey);
1109
+ const rKey = resolveKey(resourceKey);
1110
+ return this.request("POST", `${this.paths.revisionBase(fKey, rKey)}/`, {
1111
+ jsonBody: payload
1112
+ });
1113
+ }
1114
+ async getRevision(folderKey, resourceKey, revisionKey) {
1115
+ const fKey = resolveKey(folderKey);
1116
+ const rKey = resolveKey(resourceKey);
1117
+ const rvKey = resolveKey(revisionKey);
1118
+ return this.request("GET", `${this.paths.revisionBase(fKey, rKey)}/${rvKey}/`);
1119
+ }
1120
+ async updateRevision(folderKey, resourceKey, revisionKey, payload) {
1121
+ const fKey = resolveKey(folderKey);
1122
+ const rKey = resolveKey(resourceKey);
1123
+ const rvKey = resolveKey(revisionKey);
1124
+ return this.request("PUT", `${this.paths.revisionBase(fKey, rKey)}/${rvKey}/`, {
1125
+ jsonBody: payload
1126
+ });
1127
+ }
1128
+ async deleteRevision(folderKey, resourceKey, revisionKey) {
1129
+ const fKey = resolveKey(folderKey);
1130
+ const rKey = resolveKey(resourceKey);
1131
+ const rvKey = resolveKey(revisionKey);
1132
+ await this.request("DELETE", `${this.paths.revisionBase(fKey, rKey)}/${rvKey}/`, {
1133
+ parseJson: false
1134
+ });
1135
+ }
1136
+ async publishRevision(folderKey, resourceKey, revisionKey, payload) {
1137
+ const fKey = resolveKey(folderKey);
1138
+ const rKey = resolveKey(resourceKey);
1139
+ const rvKey = resolveKey(revisionKey);
1140
+ return this.request(
1141
+ "POST",
1142
+ `${this.paths.revisionBase(fKey, rKey)}/${rvKey}/publish/`,
1143
+ payload ? { jsonBody: payload } : void 0
1144
+ );
1145
+ }
1146
+ async validateRevision(folderKey, resourceKey, revisionKey) {
1147
+ const fKey = resolveKey(folderKey);
1148
+ const rKey = resolveKey(resourceKey);
1149
+ const rvKey = resolveKey(revisionKey);
1150
+ return this.request(
1151
+ "POST",
1152
+ `${this.paths.revisionBase(fKey, rKey)}/${rvKey}/validate/`
1153
+ );
1154
+ }
1155
+ async getRevisionData(folderKey, resourceKey, revisionKey) {
1156
+ const fKey = resolveKey(folderKey);
1157
+ const rKey = resolveKey(resourceKey);
1158
+ const rvKey = resolveKey(revisionKey);
1159
+ return this.request("GET", `${this.paths.revisionBase(fKey, rKey)}/${rvKey}/data/`);
1160
+ }
1161
+ // ------------------------------------------------------------------ //
1162
+ // Locale operations
1163
+ // ------------------------------------------------------------------ //
1164
+ async listLocales() {
1165
+ const payload = await this.request("GET", `${this.paths.localesRoot()}/`) ?? [];
1166
+ return Array.isArray(payload) ? payload : [payload];
1167
+ }
1168
+ async createLocale(payload) {
1169
+ return this.request("POST", `${this.paths.localesRoot()}/`, { jsonBody: payload });
1170
+ }
1171
+ async getLocale(code) {
1172
+ return this.request("GET", `${this.paths.localeRoot(code)}/`);
1173
+ }
1174
+ async updateLocale(code, payload) {
1175
+ return this.request("PUT", `${this.paths.localeRoot(code)}/`, { jsonBody: payload });
1176
+ }
1177
+ async deleteLocale(code) {
1178
+ await this.request("DELETE", `${this.paths.localeRoot(code)}/`, { parseJson: false });
1179
+ }
1180
+ // ------------------------------------------------------------------ //
1181
+ // Project operations
1182
+ // ------------------------------------------------------------------ //
1183
+ async listProjects(orgKey, params) {
1184
+ const key = resolveKey(orgKey);
1185
+ return this.request("GET", `${this.paths.projectsBase(key)}/`, { params });
1186
+ }
1187
+ async getProject(orgKey, projectKey) {
1188
+ const oKey = resolveKey(orgKey);
1189
+ const pKey = resolveKey(projectKey);
1190
+ return this.request("GET", `${this.paths.projectRoot(oKey, pKey)}/`);
1191
+ }
1192
+ async createProject(orgKey, payload) {
1193
+ const key = resolveKey(orgKey);
1194
+ return this.request("POST", `${this.paths.projectsBase(key)}/`, { jsonBody: payload });
1195
+ }
1196
+ async updateProject(orgKey, projectKey, payload) {
1197
+ const oKey = resolveKey(orgKey);
1198
+ const pKey = resolveKey(projectKey);
1199
+ return this.request("PUT", `${this.paths.projectRoot(oKey, pKey)}/`, {
1200
+ jsonBody: payload
1201
+ });
1202
+ }
1203
+ async deleteProject(orgKey, projectKey) {
1204
+ const oKey = resolveKey(orgKey);
1205
+ const pKey = resolveKey(projectKey);
1206
+ await this.request("DELETE", `${this.paths.projectRoot(oKey, pKey)}/`, {
1207
+ parseJson: false
1208
+ });
1209
+ }
1210
+ // ------------------------------------------------------------------ //
1211
+ // Environment operations
1212
+ // ------------------------------------------------------------------ //
1213
+ async listEnvironments(orgKey, projectKey) {
1214
+ const oKey = resolveKey(orgKey);
1215
+ const pKey = resolveKey(projectKey);
1216
+ const payload = await this.request("GET", `${this.paths.environmentsBase(oKey, pKey)}/`) ?? [];
1217
+ if (Array.isArray(payload)) {
1218
+ return payload;
1219
+ }
1220
+ if (payload && typeof payload === "object" && "results" in payload) {
1221
+ return payload.results;
1222
+ }
1223
+ return [payload];
1224
+ }
1225
+ async getEnvironment(orgKey, projectKey, envKey) {
1226
+ const oKey = resolveKey(orgKey);
1227
+ const pKey = resolveKey(projectKey);
1228
+ const eKey = resolveKey(envKey);
1229
+ return this.request("GET", `${this.paths.environmentRoot(oKey, pKey, eKey)}/`);
1230
+ }
1231
+ async createEnvironment(orgKey, projectKey, payload) {
1232
+ const oKey = resolveKey(orgKey);
1233
+ const pKey = resolveKey(projectKey);
1234
+ return this.request("POST", `${this.paths.environmentsBase(oKey, pKey)}/`, {
1235
+ jsonBody: payload
1236
+ });
1237
+ }
1238
+ async updateEnvironment(orgKey, projectKey, envKey, payload) {
1239
+ const oKey = resolveKey(orgKey);
1240
+ const pKey = resolveKey(projectKey);
1241
+ const eKey = resolveKey(envKey);
1242
+ return this.request("PUT", `${this.paths.environmentRoot(oKey, pKey, eKey)}/`, {
1243
+ jsonBody: payload
1244
+ });
1245
+ }
1246
+ async deleteEnvironment(orgKey, projectKey, envKey) {
1247
+ const oKey = resolveKey(orgKey);
1248
+ const pKey = resolveKey(projectKey);
1249
+ const eKey = resolveKey(envKey);
1250
+ await this.request("DELETE", `${this.paths.environmentRoot(oKey, pKey, eKey)}/`, {
1251
+ parseJson: false
1252
+ });
1253
+ }
1254
+ async toggleEnvironment(orgKey, projectKey, envKey, isEnabled) {
1255
+ const oKey = resolveKey(orgKey);
1256
+ const pKey = resolveKey(projectKey);
1257
+ const eKey = resolveKey(envKey);
1258
+ await this.request(
1259
+ "POST",
1260
+ `${this.paths.environmentRoot(oKey, pKey, eKey)}/toggle/`,
1261
+ { jsonBody: { is_enabled: isEnabled } }
1262
+ );
1263
+ }
1264
+ async updateEnvironmentProtection(orgKey, projectKey, envKey, options) {
1265
+ const oKey = resolveKey(orgKey);
1266
+ const pKey = resolveKey(projectKey);
1267
+ const eKey = resolveKey(envKey);
1268
+ const body = { protection_level: options.protectionLevel };
1269
+ if (options.protectionReason) {
1270
+ body.protection_reason = options.protectionReason;
1271
+ }
1272
+ return this.request(
1273
+ "PATCH",
1274
+ `${this.paths.environmentRoot(oKey, pKey, eKey)}/protection/`,
1275
+ { jsonBody: body }
1276
+ );
1277
+ }
1278
+ async clearEnvironmentProtection(orgKey, projectKey, envKey) {
1279
+ return this.updateEnvironmentProtection(orgKey, projectKey, envKey, {
1280
+ protectionLevel: "none"
1281
+ });
1282
+ }
1283
+ };
1284
+
1285
+ // src/flux/client.ts
1286
+ function cleanPrefix(prefix) {
1287
+ const value = prefix.replace(/^\/+|\/+$/g, "");
1288
+ if (!value) {
1289
+ throw new Error("apiPrefix cannot be empty");
1290
+ }
1291
+ return value;
1292
+ }
1293
+ function normalizeFolderPath(folderPath) {
1294
+ return folderPath.replace(/^\/+|\/+$/g, "");
1295
+ }
1296
+ var FluxClient = class {
1297
+ apiPrefix;
1298
+ transport;
1299
+ constructor(options) {
1300
+ this.apiPrefix = cleanPrefix(options.apiPrefix);
1301
+ const config = createConfig({
1302
+ baseUrl: options.baseUrl,
1303
+ timeout: options.timeout ?? 15e3,
1304
+ defaultHeaders: options.defaultHeaders
1305
+ });
1306
+ this.transport = new HttpTransport({
1307
+ config,
1308
+ auth: options.auth,
1309
+ retryConfig: options.retryConfig
1310
+ });
1311
+ }
1312
+ buildPath(folderPath, suffix = "") {
1313
+ const folder = normalizeFolderPath(folderPath);
1314
+ const base = `/${this.apiPrefix}/${folder}`;
1315
+ return suffix ? `${base}${suffix}` : base;
1316
+ }
1317
+ async listResources(folderPath, params) {
1318
+ const path = this.buildPath(folderPath);
1319
+ return this.transport.request("GET", path, { params });
1320
+ }
1321
+ async getResource(folderPath, resourceKey, params) {
1322
+ const path = this.buildPath(folderPath, `/${resourceKey}`);
1323
+ return this.transport.request("GET", path, { params });
1324
+ }
1325
+ async search(folderPath, body) {
1326
+ const path = this.buildPath(folderPath, "/_search");
1327
+ return this.transport.request("POST", path, { jsonBody: body });
1328
+ }
1329
+ close() {
1330
+ this.transport.close();
1331
+ }
1332
+ };
1333
+ export {
1334
+ AnonymousAuth,
1335
+ DEFAULT_RETRY_CONFIG,
1336
+ DEFAULT_USER_AGENT,
1337
+ FluxClient,
1338
+ FoxnoseAPIError,
1339
+ FoxnoseAuthError,
1340
+ FoxnoseError,
1341
+ FoxnoseTransportError,
1342
+ HttpTransport,
1343
+ JWTAuth,
1344
+ ManagementClient,
1345
+ SDK_VERSION,
1346
+ SecureKeyAuth,
1347
+ SimpleKeyAuth,
1348
+ StaticTokenProvider,
1349
+ SDK_VERSION as VERSION,
1350
+ createConfig,
1351
+ resolveKey
1352
+ };
1353
+ //# sourceMappingURL=index.js.map