@apicircle/core 1.0.6 → 1.0.8

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 CHANGED
@@ -3973,6 +3973,699 @@ function parseAuth2(auth, warnings, name) {
3973
3973
  }
3974
3974
  }
3975
3975
 
3976
+ // src/import/apicircleFolder.ts
3977
+ import { generateId } from "@apicircle/shared";
3978
+
3979
+ // src/export/folderExportCredentials.ts
3980
+ function collectFolderExportCredentials(envelope) {
3981
+ const out = [];
3982
+ if (envelope.folder.auth) {
3983
+ out.push(
3984
+ ...authCredentialFields(envelope.folder.auth).map(
3985
+ (f) => buildCredential(
3986
+ "root-folder",
3987
+ envelope.source.folderId,
3988
+ envelope.folder.name,
3989
+ envelope.folder.auth,
3990
+ f
3991
+ )
3992
+ )
3993
+ );
3994
+ }
3995
+ for (const sub of envelope.folder.subfolders) {
3996
+ if (!sub.auth) continue;
3997
+ out.push(
3998
+ ...authCredentialFields(sub.auth).map(
3999
+ (f) => buildCredential("subfolder", sub.id, sub.name, sub.auth, f)
4000
+ )
4001
+ );
4002
+ }
4003
+ for (const req of envelope.folder.requests) {
4004
+ out.push(
4005
+ ...authCredentialFields(req.auth).map(
4006
+ (f) => buildCredential("request", req.id, req.name, req.auth, f)
4007
+ )
4008
+ );
4009
+ }
4010
+ return out.sort(credentialCompare);
4011
+ }
4012
+ function redactFolderExportCredentials(envelope, includeIds = /* @__PURE__ */ new Set()) {
4013
+ const next = {
4014
+ ...envelope,
4015
+ folder: {
4016
+ ...envelope.folder,
4017
+ auth: envelope.folder.auth ? redactAuthForScope(
4018
+ envelope.folder.auth,
4019
+ credentialIdsFor("root-folder", envelope.source.folderId, envelope.folder.auth),
4020
+ includeIds
4021
+ ) : envelope.folder.auth,
4022
+ subfolders: envelope.folder.subfolders.map((sub) => {
4023
+ if (!sub.auth) return sub;
4024
+ const ids = credentialIdsFor("subfolder", sub.id, sub.auth);
4025
+ return {
4026
+ ...sub,
4027
+ auth: redactAuthForScope(sub.auth, ids, includeIds)
4028
+ };
4029
+ }),
4030
+ requests: envelope.folder.requests.map((req) => ({
4031
+ ...req,
4032
+ auth: redactAuthForScope(
4033
+ req.auth,
4034
+ credentialIdsFor("request", req.id, req.auth),
4035
+ includeIds
4036
+ )
4037
+ }))
4038
+ }
4039
+ };
4040
+ return next;
4041
+ }
4042
+ function authCredentialFields(auth) {
4043
+ switch (auth.type) {
4044
+ case "none":
4045
+ case "inherit":
4046
+ case "custom-header":
4047
+ return [];
4048
+ case "basic":
4049
+ return [{ field: "password", label: "Basic \xB7 password" }];
4050
+ case "bearer":
4051
+ return auth.token ? [{ field: "token", label: "Bearer \xB7 token" }] : [];
4052
+ case "api-key":
4053
+ return auth.value ? [{ field: "value", label: "API key \xB7 value" }] : [];
4054
+ case "digest":
4055
+ return [{ field: "password", label: "Digest \xB7 password" }];
4056
+ case "ntlm":
4057
+ return [{ field: "password", label: "NTLM \xB7 password" }];
4058
+ case "hawk":
4059
+ return auth.hawkKey ? [{ field: "hawkKey", label: "Hawk \xB7 hawkKey" }] : [];
4060
+ case "jwt-bearer":
4061
+ return [
4062
+ ...auth.secretOrKey ? [{ field: "secretOrKey", label: "JWT \xB7 secretOrKey" }] : [],
4063
+ ...auth.token ? [{ field: "token", label: "JWT \xB7 token" }] : []
4064
+ ];
4065
+ case "aws-sigv4":
4066
+ return [
4067
+ ...auth.secretAccessKey ? [{ field: "secretAccessKey", label: "AWS SigV4 \xB7 secretAccessKey" }] : [],
4068
+ ...auth.sessionToken ? [{ field: "sessionToken", label: "AWS SigV4 \xB7 sessionToken" }] : []
4069
+ ];
4070
+ case "oauth2-client-credentials":
4071
+ case "oauth2-auth-code":
4072
+ case "oauth2-pkce":
4073
+ return [
4074
+ ...auth.clientSecret ? [{ field: "clientSecret", label: `${auth.type} \xB7 clientSecret` }] : [],
4075
+ ...auth.accessToken ? [{ field: "accessToken", label: `${auth.type} \xB7 accessToken` }] : [],
4076
+ ...auth.refreshToken ? [{ field: "refreshToken", label: `${auth.type} \xB7 refreshToken` }] : []
4077
+ ];
4078
+ case "oauth2-password":
4079
+ return [
4080
+ ...auth.clientSecret ? [{ field: "clientSecret", label: "oauth2-password \xB7 clientSecret" }] : [],
4081
+ ...auth.password ? [{ field: "password", label: "oauth2-password \xB7 password" }] : [],
4082
+ ...auth.accessToken ? [{ field: "accessToken", label: "oauth2-password \xB7 accessToken" }] : [],
4083
+ ...auth.refreshToken ? [{ field: "refreshToken", label: "oauth2-password \xB7 refreshToken" }] : []
4084
+ ];
4085
+ case "oauth2-implicit":
4086
+ return auth.accessToken ? [{ field: "accessToken", label: "oauth2-implicit \xB7 accessToken" }] : [];
4087
+ case "oauth2-device":
4088
+ return [
4089
+ ...auth.accessToken ? [{ field: "accessToken", label: "oauth2-device \xB7 accessToken" }] : [],
4090
+ ...auth.refreshToken ? [{ field: "refreshToken", label: "oauth2-device \xB7 refreshToken" }] : []
4091
+ ];
4092
+ default:
4093
+ return [];
4094
+ }
4095
+ }
4096
+ function buildCredential(scope, ownerId, ownerName, auth, desc) {
4097
+ const prefix = scope === "request" ? "request" : "folder";
4098
+ return {
4099
+ id: `${prefix}:${ownerId}.${auth.type}.${desc.field}`,
4100
+ scope,
4101
+ authType: auth.type,
4102
+ field: desc.field,
4103
+ label: desc.label,
4104
+ ownerName,
4105
+ ownerId
4106
+ };
4107
+ }
4108
+ function credentialIdsFor(scope, ownerId, auth) {
4109
+ const ids = /* @__PURE__ */ new Map();
4110
+ const prefix = scope === "request" ? "request" : "folder";
4111
+ for (const desc of authCredentialFields(auth)) {
4112
+ ids.set(desc.field, `${prefix}:${ownerId}.${auth.type}.${desc.field}`);
4113
+ }
4114
+ return ids;
4115
+ }
4116
+ function redactAuthForScope(auth, ids, includeIds) {
4117
+ const shouldBlank = (field) => {
4118
+ const id = ids.get(field);
4119
+ return !!id && !includeIds.has(id);
4120
+ };
4121
+ switch (auth.type) {
4122
+ case "none":
4123
+ case "inherit":
4124
+ case "custom-header":
4125
+ return auth;
4126
+ case "basic":
4127
+ return shouldBlank("password") ? { ...auth, password: "" } : auth;
4128
+ case "bearer":
4129
+ return shouldBlank("token") ? { ...auth, token: "" } : auth;
4130
+ case "api-key":
4131
+ return shouldBlank("value") ? { ...auth, value: "" } : auth;
4132
+ case "digest":
4133
+ return shouldBlank("password") ? { ...auth, password: "" } : auth;
4134
+ case "ntlm":
4135
+ return shouldBlank("password") ? { ...auth, password: "" } : auth;
4136
+ case "hawk":
4137
+ return shouldBlank("hawkKey") ? { ...auth, hawkKey: "" } : auth;
4138
+ case "jwt-bearer":
4139
+ return {
4140
+ ...auth,
4141
+ secretOrKey: shouldBlank("secretOrKey") ? "" : auth.secretOrKey,
4142
+ token: shouldBlank("token") ? "" : auth.token
4143
+ };
4144
+ case "aws-sigv4":
4145
+ return {
4146
+ ...auth,
4147
+ secretAccessKey: shouldBlank("secretAccessKey") ? "" : auth.secretAccessKey,
4148
+ sessionToken: shouldBlank("sessionToken") ? "" : auth.sessionToken
4149
+ };
4150
+ case "oauth2-client-credentials":
4151
+ case "oauth2-auth-code":
4152
+ case "oauth2-pkce":
4153
+ return {
4154
+ ...auth,
4155
+ clientSecret: shouldBlank("clientSecret") ? "" : auth.clientSecret,
4156
+ accessToken: shouldBlank("accessToken") ? "" : auth.accessToken,
4157
+ refreshToken: shouldBlank("refreshToken") ? "" : auth.refreshToken
4158
+ };
4159
+ case "oauth2-password":
4160
+ return {
4161
+ ...auth,
4162
+ clientSecret: shouldBlank("clientSecret") ? "" : auth.clientSecret,
4163
+ password: shouldBlank("password") ? "" : auth.password,
4164
+ accessToken: shouldBlank("accessToken") ? "" : auth.accessToken,
4165
+ refreshToken: shouldBlank("refreshToken") ? "" : auth.refreshToken
4166
+ };
4167
+ case "oauth2-implicit":
4168
+ return {
4169
+ ...auth,
4170
+ accessToken: shouldBlank("accessToken") ? "" : auth.accessToken
4171
+ };
4172
+ case "oauth2-device":
4173
+ return {
4174
+ ...auth,
4175
+ accessToken: shouldBlank("accessToken") ? "" : auth.accessToken,
4176
+ refreshToken: shouldBlank("refreshToken") ? "" : auth.refreshToken
4177
+ };
4178
+ default:
4179
+ return auth;
4180
+ }
4181
+ }
4182
+ function scopeRank(scope) {
4183
+ if (scope === "root-folder") return 0;
4184
+ if (scope === "subfolder") return 1;
4185
+ return 2;
4186
+ }
4187
+ function credentialCompare(a, b) {
4188
+ const r = scopeRank(a.scope) - scopeRank(b.scope);
4189
+ if (r !== 0) return r;
4190
+ return a.ownerName.localeCompare(b.ownerName, void 0, { sensitivity: "base" });
4191
+ }
4192
+
4193
+ // src/export/folderExport.ts
4194
+ var APICIRCLE_FOLDER_EXPORT_FORMAT = "apicircle.folder/v1";
4195
+ function collectFolderExport(args) {
4196
+ const { synced, folderId } = args;
4197
+ const root = synced.collections.folders[folderId];
4198
+ if (!root) return null;
4199
+ const now = args.now ?? (/* @__PURE__ */ new Date()).toISOString();
4200
+ const appVersion = args.appVersion ?? "apicircle-studio";
4201
+ const folderIds = /* @__PURE__ */ new Set([folderId]);
4202
+ let grew = true;
4203
+ while (grew) {
4204
+ grew = false;
4205
+ for (const f of Object.values(synced.collections.folders)) {
4206
+ if (folderIds.has(f.id)) continue;
4207
+ if (f.parentId && folderIds.has(f.parentId)) {
4208
+ folderIds.add(f.id);
4209
+ grew = true;
4210
+ }
4211
+ }
4212
+ }
4213
+ const subfolders = [];
4214
+ for (const f of Object.values(synced.collections.folders)) {
4215
+ if (f.id !== folderId && folderIds.has(f.id)) subfolders.push(cloneFolder(f));
4216
+ }
4217
+ const requests = [];
4218
+ for (const r of Object.values(synced.collections.requests)) {
4219
+ if (r.folderId && folderIds.has(r.folderId)) requests.push(cloneRequest(r));
4220
+ }
4221
+ const dependencies = collectDependencies(synced, requests);
4222
+ const envelope = {
4223
+ format: APICIRCLE_FOLDER_EXPORT_FORMAT,
4224
+ exportedAt: now,
4225
+ appVersion,
4226
+ source: {
4227
+ workspaceId: synced.workspaceId,
4228
+ folderId,
4229
+ folderName: root.name
4230
+ },
4231
+ folder: {
4232
+ name: root.name,
4233
+ auth: root.auth,
4234
+ subfolders,
4235
+ requests
4236
+ },
4237
+ dependencies
4238
+ };
4239
+ const report = buildReport(envelope);
4240
+ return { envelope, report };
4241
+ }
4242
+ function serializeFolderExport(envelope) {
4243
+ return JSON.stringify(envelope, null, 2);
4244
+ }
4245
+ function suggestFolderExportFilename(envelope) {
4246
+ const slug = envelope.folder.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4247
+ const base = slug || "folder";
4248
+ return `${base}.apicircle.json`;
4249
+ }
4250
+ function cloneFolder(f) {
4251
+ return {
4252
+ ...f,
4253
+ auth: f.auth ? { ...f.auth } : void 0
4254
+ };
4255
+ }
4256
+ function cloneRequest(r) {
4257
+ return {
4258
+ ...r,
4259
+ headers: r.headers.map((h) => ({ ...h })),
4260
+ query: r.query.map((q) => ({ ...q })),
4261
+ pathParams: r.pathParams ? { ...r.pathParams } : void 0,
4262
+ cookies: r.cookies ? r.cookies.map((c) => ({ ...c })) : void 0,
4263
+ body: cloneBody(r.body),
4264
+ auth: { ...r.auth },
4265
+ contextVars: r.contextVars.map((v) => ({ ...v })),
4266
+ extractions: r.extractions.map((e) => ({ ...e })),
4267
+ assertions: r.assertions.map((a) => ({ ...a }))
4268
+ };
4269
+ }
4270
+ function cloneBody(body) {
4271
+ if (body.type === "form-data") {
4272
+ return {
4273
+ ...body,
4274
+ formRows: body.formRows?.map((row) => ({ ...row })) ?? body.formRows
4275
+ };
4276
+ }
4277
+ if (body.type === "binary") {
4278
+ return {
4279
+ ...body,
4280
+ attachment: body.attachment ? { ...body.attachment } : void 0
4281
+ };
4282
+ }
4283
+ return { ...body };
4284
+ }
4285
+ function collectDependencies(synced, requests) {
4286
+ const schemaIds = /* @__PURE__ */ new Set();
4287
+ const graphqlIds = /* @__PURE__ */ new Set();
4288
+ const fileIds = /* @__PURE__ */ new Set();
4289
+ for (const r of requests) {
4290
+ if (r.bodySchemaId) schemaIds.add(r.bodySchemaId);
4291
+ if (r.graphqlSchemaId) graphqlIds.add(r.graphqlSchemaId);
4292
+ if (r.body.type === "binary" && r.body.attachment?.globalFileAssetId) {
4293
+ fileIds.add(r.body.attachment.globalFileAssetId);
4294
+ }
4295
+ if (r.body.type === "form-data" && r.body.formRows) {
4296
+ for (const row of r.body.formRows) {
4297
+ if (row.kind === "file" && row.globalFileAssetId) fileIds.add(row.globalFileAssetId);
4298
+ }
4299
+ }
4300
+ }
4301
+ const assets = synced.globalAssets;
4302
+ const schemas = [];
4303
+ for (const id of schemaIds) {
4304
+ const s = assets.schemas[id];
4305
+ if (s) schemas.push({ ...s });
4306
+ }
4307
+ const graphql = [];
4308
+ for (const id of graphqlIds) {
4309
+ const g = assets.graphql[id];
4310
+ if (g) graphql.push({ ...g });
4311
+ }
4312
+ const files = [];
4313
+ for (const id of fileIds) {
4314
+ const f = assets.files?.[id];
4315
+ if (f) files.push({ ...f });
4316
+ }
4317
+ schemas.sort(byNameThenId);
4318
+ graphql.sort(byNameThenId);
4319
+ files.sort(byNameThenId);
4320
+ return { schemas, graphql, files };
4321
+ }
4322
+ function byNameThenId(a, b) {
4323
+ const c = a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
4324
+ return c !== 0 ? c : a.id.localeCompare(b.id);
4325
+ }
4326
+ function buildReport(envelope) {
4327
+ const subfolderCount = envelope.folder.subfolders.length;
4328
+ const requestCount = envelope.folder.requests.length;
4329
+ const totalFolderCount = subfolderCount + 1;
4330
+ const credentials = collectFolderExportCredentials(envelope);
4331
+ return {
4332
+ folderName: envelope.folder.name,
4333
+ requestCount,
4334
+ subfolderCount,
4335
+ totalFolderCount,
4336
+ dependencies: {
4337
+ schemas: envelope.dependencies.schemas.map((s) => ({ id: s.id, name: s.name })),
4338
+ graphql: envelope.dependencies.graphql.map((g) => ({
4339
+ id: g.id,
4340
+ name: g.name,
4341
+ kind: g.kind
4342
+ })),
4343
+ files: envelope.dependencies.files.map((f) => ({
4344
+ id: f.id,
4345
+ name: f.name,
4346
+ filename: f.filename,
4347
+ size: f.size,
4348
+ mimeType: f.mimeType
4349
+ }))
4350
+ },
4351
+ hasDependencies: envelope.dependencies.schemas.length > 0 || envelope.dependencies.graphql.length > 0 || envelope.dependencies.files.length > 0,
4352
+ credentials,
4353
+ hasCredentials: credentials.length > 0
4354
+ };
4355
+ }
4356
+
4357
+ // src/import/apicircleFolder.ts
4358
+ function isApicircleFolderExport(doc) {
4359
+ if (!doc || typeof doc !== "object") return false;
4360
+ const d = doc;
4361
+ return d.format === APICIRCLE_FOLDER_EXPORT_FORMAT;
4362
+ }
4363
+ function parseApicircleFolderExport(input, options = {}) {
4364
+ let doc;
4365
+ try {
4366
+ doc = JSON.parse(input);
4367
+ } catch (err) {
4368
+ throw new Error(`Couldn't parse JSON: ${err instanceof Error ? err.message : String(err)}`);
4369
+ }
4370
+ return parseApicircleFolderExportDoc(doc, options);
4371
+ }
4372
+ function parseApicircleFolderExportDoc(doc, options = {}) {
4373
+ const id = options.idGenerator ?? generateId;
4374
+ if (!isApicircleFolderExport(doc)) {
4375
+ throw new Error(
4376
+ `Unsupported format. Expected an API Circle folder export ("format": "${APICIRCLE_FOLDER_EXPORT_FORMAT}").`
4377
+ );
4378
+ }
4379
+ const envelope = doc;
4380
+ validateEnvelopeShape(envelope);
4381
+ const warnings = [];
4382
+ const folderIdMap = /* @__PURE__ */ new Map();
4383
+ folderIdMap.set(envelope.source.folderId, id());
4384
+ for (const f of envelope.folder.subfolders) folderIdMap.set(f.id, id());
4385
+ const requestIdMap = /* @__PURE__ */ new Map();
4386
+ for (const r of envelope.folder.requests) requestIdMap.set(r.id, id());
4387
+ const schemaIdMap = /* @__PURE__ */ new Map();
4388
+ for (const s of envelope.dependencies.schemas) schemaIdMap.set(s.id, id());
4389
+ const graphqlIdMap = /* @__PURE__ */ new Map();
4390
+ for (const g of envelope.dependencies.graphql) graphqlIdMap.set(g.id, id());
4391
+ const fileIdMap = /* @__PURE__ */ new Map();
4392
+ for (const f of envelope.dependencies.files) fileIdMap.set(f.id, id());
4393
+ const rootFolderId = folderIdMap.get(envelope.source.folderId);
4394
+ const subfolders = envelope.folder.subfolders.map((f) => {
4395
+ const newId = folderIdMap.get(f.id);
4396
+ let newParentId;
4397
+ if (f.parentId === null) {
4398
+ newParentId = rootFolderId;
4399
+ } else {
4400
+ const mapped = folderIdMap.get(f.parentId);
4401
+ if (!mapped) {
4402
+ warnings.push(
4403
+ `Subfolder "${f.name}" referenced parentId "${f.parentId}" that wasn't present in the export \u2014 reattached under "${envelope.folder.name}".`
4404
+ );
4405
+ newParentId = rootFolderId;
4406
+ } else {
4407
+ newParentId = mapped;
4408
+ }
4409
+ }
4410
+ return {
4411
+ ...f,
4412
+ id: newId,
4413
+ parentId: newParentId,
4414
+ auth: f.auth ? { ...f.auth } : void 0
4415
+ };
4416
+ });
4417
+ const schemas = envelope.dependencies.schemas.map((s) => ({
4418
+ ...s,
4419
+ id: schemaIdMap.get(s.id)
4420
+ }));
4421
+ const graphql = envelope.dependencies.graphql.map((g) => ({
4422
+ ...g,
4423
+ id: graphqlIdMap.get(g.id)
4424
+ }));
4425
+ const files = envelope.dependencies.files.map((f) => ({
4426
+ ...f,
4427
+ id: fileIdMap.get(f.id)
4428
+ }));
4429
+ const requests = envelope.folder.requests.map((r) => {
4430
+ const newId = requestIdMap.get(r.id);
4431
+ let newFolderId;
4432
+ if (r.folderId === null) {
4433
+ newFolderId = rootFolderId;
4434
+ } else {
4435
+ const mapped = folderIdMap.get(r.folderId);
4436
+ if (!mapped) {
4437
+ warnings.push(
4438
+ `Request "${r.name}" referenced folderId "${r.folderId}" that wasn't present in the export \u2014 reattached under "${envelope.folder.name}".`
4439
+ );
4440
+ newFolderId = rootFolderId;
4441
+ } else {
4442
+ newFolderId = mapped;
4443
+ }
4444
+ }
4445
+ const bodySchemaId = remapDependencyRef(
4446
+ r.bodySchemaId,
4447
+ schemaIdMap,
4448
+ `Request "${r.name}".bodySchemaId`,
4449
+ warnings
4450
+ );
4451
+ const graphqlSchemaId = remapDependencyRef(
4452
+ r.graphqlSchemaId,
4453
+ graphqlIdMap,
4454
+ `Request "${r.name}".graphqlSchemaId`,
4455
+ warnings
4456
+ );
4457
+ return {
4458
+ ...r,
4459
+ id: newId,
4460
+ folderId: newFolderId,
4461
+ bodySchemaId,
4462
+ graphqlSchemaId,
4463
+ headers: r.headers.map((h) => ({ ...h })),
4464
+ query: r.query.map((q) => ({ ...q })),
4465
+ pathParams: r.pathParams ? { ...r.pathParams } : void 0,
4466
+ cookies: r.cookies ? r.cookies.map((c) => ({ ...c })) : void 0,
4467
+ body: remapBodyFileRefs(r.body, fileIdMap, r.name, warnings),
4468
+ auth: { ...r.auth },
4469
+ contextVars: r.contextVars.map((v) => ({ ...v })),
4470
+ extractions: r.extractions.map((e) => ({ ...e })),
4471
+ assertions: r.assertions.map((a) => ({ ...a }))
4472
+ };
4473
+ });
4474
+ return {
4475
+ rootFolder: {
4476
+ id: rootFolderId,
4477
+ name: envelope.folder.name,
4478
+ auth: envelope.folder.auth ? { ...envelope.folder.auth } : void 0
4479
+ },
4480
+ subfolders,
4481
+ requests,
4482
+ dependencies: { schemas, graphql, files },
4483
+ sourceFolderName: envelope.source.folderName,
4484
+ warnings
4485
+ };
4486
+ }
4487
+ function validateEnvelopeShape(envelope) {
4488
+ if (!envelope.folder || typeof envelope.folder !== "object") {
4489
+ throw new Error('API Circle folder export is missing the "folder" section.');
4490
+ }
4491
+ if (typeof envelope.folder.name !== "string" || envelope.folder.name.length === 0) {
4492
+ throw new Error('API Circle folder export must have a non-empty "folder.name".');
4493
+ }
4494
+ if (!Array.isArray(envelope.folder.subfolders)) {
4495
+ throw new Error('API Circle folder export must have a "folder.subfolders" array.');
4496
+ }
4497
+ if (!Array.isArray(envelope.folder.requests)) {
4498
+ throw new Error('API Circle folder export must have a "folder.requests" array.');
4499
+ }
4500
+ if (!envelope.dependencies || typeof envelope.dependencies !== "object") {
4501
+ throw new Error('API Circle folder export is missing the "dependencies" section.');
4502
+ }
4503
+ if (!Array.isArray(envelope.dependencies.schemas) || !Array.isArray(envelope.dependencies.graphql) || !Array.isArray(envelope.dependencies.files)) {
4504
+ throw new Error(
4505
+ 'API Circle folder export "dependencies" must have schemas / graphql / files arrays.'
4506
+ );
4507
+ }
4508
+ if (!envelope.source || typeof envelope.source !== "object") {
4509
+ throw new Error('API Circle folder export is missing the "source" section.');
4510
+ }
4511
+ if (typeof envelope.source.folderId !== "string" || typeof envelope.source.folderName !== "string") {
4512
+ throw new Error('API Circle folder export "source" must include "folderId" and "folderName".');
4513
+ }
4514
+ }
4515
+ function remapDependencyRef(value, map, label, warnings) {
4516
+ if (value === null || value === void 0) return value;
4517
+ const mapped = map.get(value);
4518
+ if (mapped) return mapped;
4519
+ warnings.push(
4520
+ `${label} referenced a dependency ("${value}") that wasn't embedded in the export \u2014 reference dropped on import.`
4521
+ );
4522
+ return null;
4523
+ }
4524
+ function remapBodyFileRefs(body, fileIdMap, requestName, warnings) {
4525
+ if (body.type === "binary") {
4526
+ if (!body.attachment) return { ...body };
4527
+ const oldId = body.attachment.globalFileAssetId;
4528
+ let nextGlobalFileAssetId = oldId;
4529
+ if (oldId) {
4530
+ const mapped = fileIdMap.get(oldId);
4531
+ if (mapped) {
4532
+ nextGlobalFileAssetId = mapped;
4533
+ } else {
4534
+ warnings.push(
4535
+ `Request "${requestName}".body.attachment referenced file asset "${oldId}" that wasn't embedded in the export \u2014 re-attach the file after import.`
4536
+ );
4537
+ nextGlobalFileAssetId = null;
4538
+ }
4539
+ }
4540
+ return {
4541
+ ...body,
4542
+ attachment: {
4543
+ ...body.attachment,
4544
+ // Reset slotId — the destination workspace owns its own slots.
4545
+ slotId: null,
4546
+ globalFileAssetId: nextGlobalFileAssetId
4547
+ }
4548
+ };
4549
+ }
4550
+ if (body.type === "form-data") {
4551
+ const formRows = body.formRows?.map((row) => {
4552
+ if (row.kind !== "file") return { ...row };
4553
+ const oldId = row.globalFileAssetId;
4554
+ let nextGlobalFileAssetId = oldId;
4555
+ if (oldId) {
4556
+ const mapped = fileIdMap.get(oldId);
4557
+ if (mapped) {
4558
+ nextGlobalFileAssetId = mapped;
4559
+ } else {
4560
+ warnings.push(
4561
+ `Request "${requestName}" form-data row "${row.key}" referenced file asset "${oldId}" that wasn't embedded in the export \u2014 re-attach the file after import.`
4562
+ );
4563
+ nextGlobalFileAssetId = null;
4564
+ }
4565
+ }
4566
+ return {
4567
+ ...row,
4568
+ slotId: null,
4569
+ globalFileAssetId: nextGlobalFileAssetId
4570
+ };
4571
+ });
4572
+ return { ...body, formRows };
4573
+ }
4574
+ return { ...body };
4575
+ }
4576
+
4577
+ // src/import/apicircleEnvironment.ts
4578
+ function isApicircleEnvironment(doc) {
4579
+ if (!doc || typeof doc !== "object") return false;
4580
+ const d = doc;
4581
+ return (d.apicircleEnvironment === 1 || d.apicircleEnvironment === 2) && typeof d.name === "string" && Array.isArray(d.variables);
4582
+ }
4583
+ function parseApicircleEnvironment(input) {
4584
+ let doc;
4585
+ try {
4586
+ doc = JSON.parse(input);
4587
+ } catch (err) {
4588
+ throw new Error(`Couldn't parse JSON: ${err instanceof Error ? err.message : String(err)}`);
4589
+ }
4590
+ return parseApicircleEnvironmentDoc(doc);
4591
+ }
4592
+ function parseApicircleEnvironmentDoc(doc) {
4593
+ if (!isApicircleEnvironment(doc)) {
4594
+ throw new Error(
4595
+ 'Unsupported format. Expected an API Circle environment export ("apicircleEnvironment": 1 or 2).'
4596
+ );
4597
+ }
4598
+ const name = doc.name.trim();
4599
+ if (!name) {
4600
+ throw new Error('API Circle environment export must have a non-empty "name".');
4601
+ }
4602
+ const payloadVersion = doc.apicircleEnvironment;
4603
+ const warnings = [];
4604
+ const variables = [];
4605
+ const encryptedBindingHints = [];
4606
+ for (let i = 0; i < doc.variables.length; i += 1) {
4607
+ const raw = doc.variables[i];
4608
+ if (!raw || typeof raw !== "object") {
4609
+ warnings.push(`Row #${i + 1} was not an object \u2014 dropped.`);
4610
+ continue;
4611
+ }
4612
+ const key = typeof raw.key === "string" ? raw.key.trim() : "";
4613
+ if (!key) {
4614
+ warnings.push(`Row #${i + 1} had no key \u2014 dropped.`);
4615
+ continue;
4616
+ }
4617
+ if (raw.encrypted === true) {
4618
+ const secretKeyId = typeof raw.secretKeyId === "string" ? raw.secretKeyId : "";
4619
+ const labelFromSecret = readLabelFromSecretField(raw.secret);
4620
+ const ciphertext = payloadVersion === 2 && typeof raw.value === "string" && raw.value.startsWith("enc:") ? raw.value : null;
4621
+ const salt = payloadVersion === 2 ? readSaltFromSecretField(raw.secret) : null;
4622
+ if (!secretKeyId && !labelFromSecret) {
4623
+ warnings.push(
4624
+ `"${key}" was marked encrypted but carried no secretKeyId and no secret label \u2014 imported as an empty plain variable. Re-bind it under Environments after import.`
4625
+ );
4626
+ variables.push({ key, value: "", encrypted: false });
4627
+ continue;
4628
+ }
4629
+ variables.push({
4630
+ key,
4631
+ value: ciphertext ?? "",
4632
+ encrypted: true,
4633
+ secretKeyId: secretKeyId || void 0
4634
+ });
4635
+ const labelFromFallback = !labelFromSecret;
4636
+ encryptedBindingHints.push({
4637
+ varKey: key,
4638
+ label: labelFromSecret ?? key,
4639
+ originSecretKeyId: secretKeyId || void 0,
4640
+ labelFromFallback,
4641
+ ciphertext,
4642
+ salt
4643
+ });
4644
+ continue;
4645
+ }
4646
+ variables.push({
4647
+ key,
4648
+ value: typeof raw.value === "string" ? raw.value : "",
4649
+ encrypted: false
4650
+ });
4651
+ }
4652
+ return { name, variables, encryptedBindingHints, payloadVersion, warnings };
4653
+ }
4654
+ function readLabelFromSecretField(field) {
4655
+ if (!field || typeof field !== "object") return null;
4656
+ const f = field;
4657
+ if (typeof f.label !== "string") return null;
4658
+ const trimmed = f.label.trim();
4659
+ return trimmed.length > 0 ? trimmed : null;
4660
+ }
4661
+ function readSaltFromSecretField(field) {
4662
+ if (!field || typeof field !== "object") return null;
4663
+ const f = field;
4664
+ if (typeof f.salt !== "string") return null;
4665
+ const trimmed = f.salt.trim();
4666
+ return trimmed.length > 0 ? trimmed : null;
4667
+ }
4668
+
3976
4669
  // src/assertions/runAssertions.ts
3977
4670
  function runAssertions(assertions, exec) {
3978
4671
  return assertions.map((a) => runOne(a, exec));
@@ -5720,7 +6413,232 @@ function structurallyEqual2(a, b) {
5720
6413
  }
5721
6414
 
5722
6415
  // src/workspace/applyMutation.ts
5723
- import { envPriorityKey, generateId } from "@apicircle/shared";
6416
+ import { envPriorityKey, generateId as generateId2 } from "@apicircle/shared";
6417
+
6418
+ // src/workspace/apicircleFolderImport.ts
6419
+ function importApicircleFolderInto(synced, parsed, parentFolderId) {
6420
+ let cur = synced;
6421
+ const schemaRemap = /* @__PURE__ */ new Map();
6422
+ const graphqlRemap = /* @__PURE__ */ new Map();
6423
+ const fileRemap = /* @__PURE__ */ new Map();
6424
+ let schemasAdded = 0;
6425
+ let schemasReused = 0;
6426
+ let graphqlAdded = 0;
6427
+ let graphqlReused = 0;
6428
+ let filesAdded = 0;
6429
+ let filesReused = 0;
6430
+ const filesRequiringReattachment = [];
6431
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6432
+ for (const incoming of parsed.dependencies.schemas) {
6433
+ const existing = findMatchingSchema(cur, incoming);
6434
+ if (existing) {
6435
+ schemaRemap.set(incoming.id, existing.id);
6436
+ schemasReused += 1;
6437
+ continue;
6438
+ }
6439
+ cur = mergeGlobalSchema(cur, incoming, now);
6440
+ schemaRemap.set(incoming.id, incoming.id);
6441
+ schemasAdded += 1;
6442
+ }
6443
+ for (const incoming of parsed.dependencies.graphql) {
6444
+ const existing = findMatchingGraphQL(cur, incoming);
6445
+ if (existing) {
6446
+ graphqlRemap.set(incoming.id, existing.id);
6447
+ graphqlReused += 1;
6448
+ continue;
6449
+ }
6450
+ cur = mergeGlobalGraphQL(cur, incoming, now);
6451
+ graphqlRemap.set(incoming.id, incoming.id);
6452
+ graphqlAdded += 1;
6453
+ }
6454
+ for (const incoming of parsed.dependencies.files) {
6455
+ const existing = findMatchingFile(cur, incoming);
6456
+ if (existing) {
6457
+ fileRemap.set(incoming.id, existing.id);
6458
+ filesReused += 1;
6459
+ continue;
6460
+ }
6461
+ cur = mergeGlobalFile(cur, incoming, now);
6462
+ fileRemap.set(incoming.id, incoming.id);
6463
+ filesAdded += 1;
6464
+ filesRequiringReattachment.push(incoming.id);
6465
+ }
6466
+ const rootName = uniquifyFolderName(cur, parentFolderId, parsed.rootFolder.name);
6467
+ const root = {
6468
+ id: parsed.rootFolder.id,
6469
+ name: rootName,
6470
+ parentId: parentFolderId,
6471
+ auth: parsed.rootFolder.auth ? { ...parsed.rootFolder.auth } : void 0
6472
+ };
6473
+ cur = insertFolder(
6474
+ cur,
6475
+ root,
6476
+ /* attachToTree */
6477
+ parentFolderId === null
6478
+ );
6479
+ for (const f of parsed.subfolders) {
6480
+ cur = insertFolder(
6481
+ cur,
6482
+ f,
6483
+ /* attachToTree */
6484
+ false
6485
+ );
6486
+ }
6487
+ for (const r of parsed.requests) {
6488
+ const rewritten = {
6489
+ ...r,
6490
+ bodySchemaId: rewriteRef(r.bodySchemaId, schemaRemap),
6491
+ graphqlSchemaId: rewriteRef(r.graphqlSchemaId, graphqlRemap),
6492
+ body: rewriteBodyFileRefs(r.body, fileRemap)
6493
+ };
6494
+ cur = insertRequest(cur, rewritten);
6495
+ }
6496
+ return {
6497
+ synced: { ...cur, meta: { ...cur.meta, updatedAt: now } },
6498
+ rootFolderId: root.id,
6499
+ rootFolderName: rootName,
6500
+ counts: {
6501
+ folders: parsed.subfolders.length + 1,
6502
+ requests: parsed.requests.length,
6503
+ schemasAdded,
6504
+ schemasReused,
6505
+ graphqlAdded,
6506
+ graphqlReused,
6507
+ filesAdded,
6508
+ filesReused
6509
+ },
6510
+ filesRequiringReattachment
6511
+ };
6512
+ }
6513
+ function isFolderNameAvailable(synced, parentFolderId, name) {
6514
+ const trimmed = name.trim().toLowerCase();
6515
+ if (!trimmed) return false;
6516
+ for (const node of Object.values(synced.collections.folders)) {
6517
+ if (node.parentId !== parentFolderId) continue;
6518
+ if (node.name.trim().toLowerCase() === trimmed) return false;
6519
+ }
6520
+ return true;
6521
+ }
6522
+ function uniquifyFolderName(synced, parentFolderId, desired) {
6523
+ if (isFolderNameAvailable(synced, parentFolderId, desired)) return desired;
6524
+ let n = 2;
6525
+ while (!isFolderNameAvailable(synced, parentFolderId, `${desired} (${n})`)) {
6526
+ n += 1;
6527
+ if (n > 999) return `${desired} (${n})`;
6528
+ }
6529
+ return `${desired} (${n})`;
6530
+ }
6531
+ function insertFolder(synced, folder, attachToTree) {
6532
+ const folders = { ...synced.collections.folders, [folder.id]: folder };
6533
+ const tree = attachToTree ? {
6534
+ ...synced.collections.tree,
6535
+ children: [...synced.collections.tree.children, { kind: "folder", id: folder.id }]
6536
+ } : synced.collections.tree;
6537
+ return {
6538
+ ...synced,
6539
+ collections: { ...synced.collections, folders, tree }
6540
+ };
6541
+ }
6542
+ function insertRequest(synced, request) {
6543
+ return {
6544
+ ...synced,
6545
+ collections: {
6546
+ ...synced.collections,
6547
+ requests: { ...synced.collections.requests, [request.id]: request }
6548
+ }
6549
+ };
6550
+ }
6551
+ function withGlobalAssets(synced) {
6552
+ return synced.globalAssets ?? { schemas: {}, graphql: {}, files: {} };
6553
+ }
6554
+ function findMatchingSchema(synced, candidate) {
6555
+ const ga = withGlobalAssets(synced);
6556
+ for (const existing of Object.values(ga.schemas)) {
6557
+ if (existing.name === candidate.name && existing.schema === candidate.schema) {
6558
+ return existing;
6559
+ }
6560
+ }
6561
+ return null;
6562
+ }
6563
+ function findMatchingGraphQL(synced, candidate) {
6564
+ const ga = withGlobalAssets(synced);
6565
+ for (const existing of Object.values(ga.graphql)) {
6566
+ if (existing.name === candidate.name && existing.kind === candidate.kind && existing.source === candidate.source) {
6567
+ return existing;
6568
+ }
6569
+ }
6570
+ return null;
6571
+ }
6572
+ function findMatchingFile(synced, candidate) {
6573
+ const ga = withGlobalAssets(synced);
6574
+ const files = ga.files ?? {};
6575
+ for (const existing of Object.values(files)) {
6576
+ if (existing.name === candidate.name && existing.filename === candidate.filename && existing.size === candidate.size) {
6577
+ return existing;
6578
+ }
6579
+ }
6580
+ return null;
6581
+ }
6582
+ function mergeGlobalSchema(synced, schema, now) {
6583
+ const ga = withGlobalAssets(synced);
6584
+ return {
6585
+ ...synced,
6586
+ globalAssets: {
6587
+ ...ga,
6588
+ schemas: { ...ga.schemas, [schema.id]: { ...schema, updatedAt: now } }
6589
+ }
6590
+ };
6591
+ }
6592
+ function mergeGlobalGraphQL(synced, graphql, now) {
6593
+ const ga = withGlobalAssets(synced);
6594
+ return {
6595
+ ...synced,
6596
+ globalAssets: {
6597
+ ...ga,
6598
+ graphql: { ...ga.graphql, [graphql.id]: { ...graphql, updatedAt: now } }
6599
+ }
6600
+ };
6601
+ }
6602
+ function mergeGlobalFile(synced, file, now) {
6603
+ const ga = withGlobalAssets(synced);
6604
+ const files = ga.files ?? {};
6605
+ return {
6606
+ ...synced,
6607
+ globalAssets: {
6608
+ ...ga,
6609
+ files: { ...files, [file.id]: { ...file, updatedAt: now } }
6610
+ }
6611
+ };
6612
+ }
6613
+ function rewriteRef(value, remap) {
6614
+ if (value === null || value === void 0) return value;
6615
+ return remap.get(value) ?? value;
6616
+ }
6617
+ function rewriteBodyFileRefs(body, remap) {
6618
+ if (body.type === "binary") {
6619
+ if (!body.attachment) return body;
6620
+ const rewritten = rewriteRef(body.attachment.globalFileAssetId, remap);
6621
+ if (rewritten === body.attachment.globalFileAssetId) return body;
6622
+ return {
6623
+ ...body,
6624
+ attachment: { ...body.attachment, globalFileAssetId: rewritten }
6625
+ };
6626
+ }
6627
+ if (body.type === "form-data" && body.formRows) {
6628
+ let mutated = false;
6629
+ const next = body.formRows.map((row) => {
6630
+ if (row.kind !== "file") return row;
6631
+ const rewritten = rewriteRef(row.globalFileAssetId, remap);
6632
+ if (rewritten === row.globalFileAssetId) return row;
6633
+ mutated = true;
6634
+ return { ...row, globalFileAssetId: rewritten };
6635
+ });
6636
+ return mutated ? { ...body, formRows: next } : body;
6637
+ }
6638
+ return body;
6639
+ }
6640
+
6641
+ // src/workspace/applyMutation.ts
5724
6642
  function applyMutation(state, patch, options = {}) {
5725
6643
  const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
5726
6644
  switch (patch.kind) {
@@ -5736,6 +6654,8 @@ function applyMutation(state, patch, options = {}) {
5736
6654
  return applyFolderDelete(state, patch.id, now);
5737
6655
  case "folder.move":
5738
6656
  return applyFolderMove(state, patch.id, patch.newParentId, now);
6657
+ case "folder.import_apicircle":
6658
+ return applyFolderImportApicircle(state, patch.parsed, patch.parentFolderId, now);
5739
6659
  case "environment.upsert":
5740
6660
  return applyEnvUpsert(state, patch.environment, now);
5741
6661
  case "environment.delete":
@@ -5744,6 +6664,8 @@ function applyMutation(state, patch, options = {}) {
5744
6664
  return applyEnvSetActive(state, patch.name, now);
5745
6665
  case "environment.setPriority":
5746
6666
  return applyEnvSetPriority(state, patch.order, now);
6667
+ case "secretKey.upsert":
6668
+ return applySecretKeyUpsert(state, patch.meta, now);
5747
6669
  case "assertion.upsert":
5748
6670
  return applyAssertionUpsert(state, patch.requestId, patch.assertion, now);
5749
6671
  case "assertion.delete":
@@ -5904,6 +6826,23 @@ function applyFolderMove(state, id, newParentId, now) {
5904
6826
  };
5905
6827
  return { next: { ...state, synced }, changedIds: [id] };
5906
6828
  }
6829
+ function applyFolderImportApicircle(state, parsed, parentFolderId, now) {
6830
+ const result = importApicircleFolderInto(
6831
+ state.synced,
6832
+ parsed,
6833
+ parentFolderId
6834
+ );
6835
+ const synced = {
6836
+ ...result.synced,
6837
+ meta: { ...result.synced.meta, updatedAt: now }
6838
+ };
6839
+ const changedIds = [
6840
+ result.rootFolderId,
6841
+ ...parsed.subfolders.map((f) => f.id),
6842
+ ...parsed.requests.map((r) => r.id)
6843
+ ];
6844
+ return { next: { ...state, synced }, changedIds };
6845
+ }
5907
6846
  function applyEnvUpsert(state, environment, now) {
5908
6847
  const trimmed = environment.name.trim();
5909
6848
  if (!trimmed) {
@@ -5979,6 +6918,20 @@ function applyEnvSetPriority(state, order, now) {
5979
6918
  };
5980
6919
  return { next: { ...state, synced }, changedIds: filtered.map(envPriorityKey) };
5981
6920
  }
6921
+ function applySecretKeyUpsert(state, meta, now) {
6922
+ if (!meta.id || !meta.label.trim() || !meta.salt) {
6923
+ return { next: state, changedIds: [] };
6924
+ }
6925
+ const synced = {
6926
+ ...state.synced,
6927
+ secretKeys: {
6928
+ ...state.synced.secretKeys ?? {},
6929
+ [meta.id]: { ...meta, label: meta.label.trim() }
6930
+ },
6931
+ meta: { ...state.synced.meta, updatedAt: now }
6932
+ };
6933
+ return { next: { ...state, synced }, changedIds: [meta.id] };
6934
+ }
5982
6935
  function applyAssertionUpsert(state, requestId, assertion, now) {
5983
6936
  const request = state.synced.collections.requests[requestId];
5984
6937
  if (!request) {
@@ -6121,7 +7074,7 @@ function evictSnapshotsToCap(entries, maxBytes) {
6121
7074
  };
6122
7075
  }
6123
7076
  function applySnapshotCapture(state, args, now) {
6124
- const id = args.id ?? generateId();
7077
+ const id = args.id ?? generateId2();
6125
7078
  const snapshot2 = {
6126
7079
  id,
6127
7080
  createdAt: now,
@@ -6210,7 +7163,7 @@ function applyHistoryPurge(state, olderThanMs) {
6210
7163
  }
6211
7164
 
6212
7165
  // src/workspace/runPlan.ts
6213
- import { envPriorityKey as envPriorityKey2, generateId as generateId2, RUN_BODY_PREVIEW_LIMIT } from "@apicircle/shared";
7166
+ import { envPriorityKey as envPriorityKey2, generateId as generateId3, RUN_BODY_PREVIEW_LIMIT } from "@apicircle/shared";
6214
7167
  var MAX_REQUEST_RUNS = 500;
6215
7168
  var MAX_PLAN_RUNS = 200;
6216
7169
  var ANONYMOUS_ACTOR = { kind: "unknown", name: "unknown" };
@@ -6357,7 +7310,7 @@ async function runPlan(state, planId, opts = {}) {
6357
7310
  const baseRefs = plan.envPriorityOrder.length > 0 ? plan.envPriorityOrder : state.synced.environments.priorityOrder;
6358
7311
  const envRefs = opts.env ? [{ kind: "local", name: opts.env }, ...baseRefs] : baseRefs;
6359
7312
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
6360
- const planRunId = generateId2();
7313
+ const planRunId = generateId3();
6361
7314
  const t0 = Date.now();
6362
7315
  const stepRecords = [];
6363
7316
  const newRequestRuns = [];
@@ -6390,7 +7343,7 @@ async function runPlan(state, planId, opts = {}) {
6390
7343
  const lookup2 = lookupPlanStepRequest(step, state.synced, state.local);
6391
7344
  const baseRequest = lookup2.request;
6392
7345
  if (!baseRequest) {
6393
- const runId = generateId2();
7346
+ const runId = generateId3();
6394
7347
  const error = lookup2.error ?? "Request no longer exists in workspace.";
6395
7348
  newRequestRuns.push(orphanRun(runId, step.requestId, error));
6396
7349
  stepRecords.push({ requestRunId: runId, passed: false });
@@ -6622,7 +7575,7 @@ function orphanRun(id, requestId, error) {
6622
7575
  function buildRequestRun(resolved, result, assertions) {
6623
7576
  const { preview, truncated } = clampPreview(result.body ?? "");
6624
7577
  return {
6625
- id: generateId2(),
7578
+ id: generateId3(),
6626
7579
  requestId: resolved.id,
6627
7580
  startedAt: result.startedAt,
6628
7581
  durationMs: result.durationMs,
@@ -6928,6 +7881,7 @@ var TRANSFORM_FORMAT_LABELS = {
6928
7881
  };
6929
7882
  export {
6930
7883
  ANONYMOUS_ACTOR,
7884
+ APICIRCLE_FOLDER_EXPORT_FORMAT,
6931
7885
  DESKTOP_APP_ORIGIN,
6932
7886
  EMPTY_UNPUSHED_SUMMARY,
6933
7887
  HTTP_HEADERS_MAP,
@@ -6952,6 +7906,8 @@ export {
6952
7906
  buildRequest,
6953
7907
  buildScope,
6954
7908
  collectAttachmentSlots,
7909
+ collectFolderExport,
7910
+ collectFolderExportCredentials,
6955
7911
  collectVariableSuggestions,
6956
7912
  compareSemver,
6957
7913
  composeBody,
@@ -6987,7 +7943,10 @@ export {
6987
7943
  getLanguageFromContentType,
6988
7944
  getVariableAutocomplete,
6989
7945
  hasUnpushedChanges,
7946
+ importApicircleFolderInto,
6990
7947
  importKey,
7948
+ isApicircleEnvironment,
7949
+ isApicircleFolderExport,
6991
7950
  isDesktop,
6992
7951
  isInsomniaExport,
6993
7952
  isPostmanEnvironment,
@@ -6996,6 +7955,10 @@ export {
6996
7955
  lookup,
6997
7956
  mergeWithAutoHeaders,
6998
7957
  normalizeContentType,
7958
+ parseApicircleEnvironment,
7959
+ parseApicircleEnvironmentDoc,
7960
+ parseApicircleFolderExport,
7961
+ parseApicircleFolderExportDoc,
6999
7962
  parseCurl,
7000
7963
  parseDigestChallenge,
7001
7964
  parseGraphqlSchema,
@@ -7011,6 +7974,7 @@ export {
7011
7974
  previewLinkedUpdate,
7012
7975
  publishRelease,
7013
7976
  readJsonPath,
7977
+ redactFolderExportCredentials,
7014
7978
  redactForGit,
7015
7979
  refreshToken,
7016
7980
  requestDeviceAuthorization,
@@ -7023,11 +7987,13 @@ export {
7023
7987
  runClientCredentials,
7024
7988
  runPlan,
7025
7989
  runRopc,
7990
+ serializeFolderExport,
7026
7991
  serializePayload,
7027
7992
  serializeWorkspaceForGit,
7028
7993
  signJwt,
7029
7994
  slugify,
7030
7995
  sortVersionsDesc,
7996
+ suggestFolderExportFilename,
7031
7997
  suggestHeaders,
7032
7998
  summarizeUnpushedChanges,
7033
7999
  supportedContentTypeLanguageMap,