@usebruno/filestore 0.5.0 → 0.7.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.
Files changed (161) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/workers/worker-script.js +1 -1
  4. package/dist/cjs/workers/worker-script.js.map +1 -1
  5. package/dist/constants.d.ts +3 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/esm/index.js +1 -1
  8. package/dist/esm/index.js.map +1 -1
  9. package/dist/esm/workers/worker-script.js +1 -1
  10. package/dist/esm/workers/worker-script.js.map +1 -1
  11. package/dist/formats/bru/index.d.ts +20 -0
  12. package/dist/formats/bru/index.d.ts.map +1 -0
  13. package/dist/formats/bru/tests/fixtures/oauth2-additional-params.d.ts +57 -0
  14. package/dist/formats/bru/tests/fixtures/oauth2-additional-params.d.ts.map +1 -0
  15. package/dist/formats/bru/tests/oauth2-additional-params.spec.d.ts +2 -0
  16. package/dist/formats/bru/tests/oauth2-additional-params.spec.d.ts.map +1 -0
  17. package/dist/formats/bru/tests/request-parse-and-redact-body-data.spec.d.ts +2 -0
  18. package/dist/formats/bru/tests/request-parse-and-redact-body-data.spec.d.ts.map +1 -0
  19. package/dist/{cjs/workers/formats → formats}/bru/utils/oauth2-additional-params.d.ts +1 -0
  20. package/dist/formats/bru/utils/oauth2-additional-params.d.ts.map +1 -0
  21. package/dist/{esm/formats → formats}/bru/utils/request-parse-and-redact-body-data.d.ts +1 -0
  22. package/dist/formats/bru/utils/request-parse-and-redact-body-data.d.ts.map +1 -0
  23. package/dist/formats/yml/common/actions.d.ts +14 -0
  24. package/dist/formats/yml/common/actions.d.ts.map +1 -0
  25. package/dist/formats/yml/common/assertions.d.ts +5 -0
  26. package/dist/formats/yml/common/assertions.d.ts.map +1 -0
  27. package/dist/formats/yml/common/auth-oauth2.d.ts +5 -0
  28. package/dist/formats/yml/common/auth-oauth2.d.ts.map +1 -0
  29. package/dist/formats/yml/common/auth.d.ts +5 -0
  30. package/dist/formats/yml/common/auth.d.ts.map +1 -0
  31. package/dist/formats/yml/common/body.d.ts +5 -0
  32. package/dist/formats/yml/common/body.d.ts.map +1 -0
  33. package/dist/formats/yml/common/headers.d.ts +7 -0
  34. package/dist/formats/yml/common/headers.d.ts.map +1 -0
  35. package/dist/formats/yml/common/params.d.ts +5 -0
  36. package/dist/formats/yml/common/params.d.ts.map +1 -0
  37. package/dist/formats/yml/common/scripts.d.ts +14 -0
  38. package/dist/formats/yml/common/scripts.d.ts.map +1 -0
  39. package/dist/formats/yml/common/variables.d.ts +17 -0
  40. package/dist/formats/yml/common/variables.d.ts.map +1 -0
  41. package/dist/formats/yml/index.d.ts +10 -0
  42. package/dist/formats/yml/index.d.ts.map +1 -0
  43. package/dist/formats/yml/items/parseGraphQLRequest.d.ts +5 -0
  44. package/dist/formats/yml/items/parseGraphQLRequest.d.ts.map +1 -0
  45. package/dist/formats/yml/items/parseGrpcRequest.d.ts +5 -0
  46. package/dist/formats/yml/items/parseGrpcRequest.d.ts.map +1 -0
  47. package/dist/formats/yml/items/parseHttpRequest.d.ts +5 -0
  48. package/dist/formats/yml/items/parseHttpRequest.d.ts.map +1 -0
  49. package/dist/formats/yml/items/parseScript.d.ts +5 -0
  50. package/dist/formats/yml/items/parseScript.d.ts.map +1 -0
  51. package/dist/formats/yml/items/parseWebsocketRequest.d.ts +5 -0
  52. package/dist/formats/yml/items/parseWebsocketRequest.d.ts.map +1 -0
  53. package/dist/formats/yml/items/stringifyGraphQLRequest.d.ts +4 -0
  54. package/dist/formats/yml/items/stringifyGraphQLRequest.d.ts.map +1 -0
  55. package/dist/formats/yml/items/stringifyGrpcRequest.d.ts +4 -0
  56. package/dist/formats/yml/items/stringifyGrpcRequest.d.ts.map +1 -0
  57. package/dist/formats/yml/items/stringifyHttpRequest.d.ts +4 -0
  58. package/dist/formats/yml/items/stringifyHttpRequest.d.ts.map +1 -0
  59. package/dist/formats/yml/items/stringifyScript.d.ts +4 -0
  60. package/dist/formats/yml/items/stringifyScript.d.ts.map +1 -0
  61. package/dist/formats/yml/items/stringifyWebsocketRequest.d.ts +4 -0
  62. package/dist/formats/yml/items/stringifyWebsocketRequest.d.ts.map +1 -0
  63. package/dist/formats/yml/parseCollection.d.ts +8 -0
  64. package/dist/formats/yml/parseCollection.d.ts.map +1 -0
  65. package/dist/formats/yml/parseEnvironment.d.ts +4 -0
  66. package/dist/formats/yml/parseEnvironment.d.ts.map +1 -0
  67. package/dist/formats/yml/parseFolder.d.ts +4 -0
  68. package/dist/formats/yml/parseFolder.d.ts.map +1 -0
  69. package/dist/formats/yml/parseItem.d.ts +4 -0
  70. package/dist/formats/yml/parseItem.d.ts.map +1 -0
  71. package/dist/formats/yml/stringifyCollection.d.ts +3 -0
  72. package/dist/formats/yml/stringifyCollection.d.ts.map +1 -0
  73. package/dist/formats/yml/stringifyEnvironment.d.ts +4 -0
  74. package/dist/formats/yml/stringifyEnvironment.d.ts.map +1 -0
  75. package/dist/formats/yml/stringifyFolder.d.ts +4 -0
  76. package/dist/formats/yml/stringifyFolder.d.ts.map +1 -0
  77. package/dist/formats/yml/stringifyItem.d.ts +4 -0
  78. package/dist/formats/yml/stringifyItem.d.ts.map +1 -0
  79. package/dist/formats/yml/utils.d.ts +3 -0
  80. package/dist/formats/yml/utils.d.ts.map +1 -0
  81. package/dist/index.d.ts +24 -0
  82. package/dist/index.d.ts.map +1 -0
  83. package/dist/types.d.ts +19 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/utils/index.d.ts +6 -0
  86. package/dist/utils/index.d.ts.map +1 -0
  87. package/dist/{esm/workers → workers}/WorkerQueue/index.d.ts +1 -0
  88. package/dist/workers/WorkerQueue/index.d.ts.map +1 -0
  89. package/dist/workers/index.d.ts +12 -0
  90. package/dist/workers/index.d.ts.map +1 -0
  91. package/dist/workers/worker-script.d.ts +2 -0
  92. package/dist/workers/worker-script.d.ts.map +1 -0
  93. package/package.json +12 -6
  94. package/src/constants.ts +3 -0
  95. package/src/formats/bru/index.ts +9 -9
  96. package/src/formats/bru/tests/oauth2-additional-params.spec.js +1 -1
  97. package/src/formats/bru/utils/oauth2-additional-params.ts +17 -18
  98. package/src/formats/bru/utils/request-parse-and-redact-body-data.ts +13 -13
  99. package/src/formats/yml/common/actions.ts +78 -0
  100. package/src/formats/yml/common/assertions.ts +146 -0
  101. package/src/formats/yml/common/auth-oauth2.ts +555 -0
  102. package/src/formats/yml/common/auth.ts +245 -0
  103. package/src/formats/yml/common/body.ts +238 -0
  104. package/src/formats/yml/common/headers.ts +58 -0
  105. package/src/formats/yml/common/params.ts +57 -0
  106. package/src/formats/yml/common/scripts.ts +64 -0
  107. package/src/formats/yml/common/variables.ts +68 -0
  108. package/src/formats/yml/index.ts +20 -0
  109. package/src/formats/yml/items/parseGraphQLRequest.ts +137 -0
  110. package/src/formats/yml/items/parseGrpcRequest.ts +116 -0
  111. package/src/formats/yml/items/parseHttpRequest.ts +195 -0
  112. package/src/formats/yml/items/parseScript.ts +25 -0
  113. package/src/formats/yml/items/parseWebsocketRequest.ts +107 -0
  114. package/src/formats/yml/items/stringifyGraphQLRequest.ts +168 -0
  115. package/src/formats/yml/items/stringifyGrpcRequest.ts +137 -0
  116. package/src/formats/yml/items/stringifyHttpRequest.ts +222 -0
  117. package/src/formats/yml/items/stringifyScript.ts +22 -0
  118. package/src/formats/yml/items/stringifyWebsocketRequest.ts +114 -0
  119. package/src/formats/yml/parseCollection.ts +220 -0
  120. package/src/formats/yml/parseEnvironment.ts +57 -0
  121. package/src/formats/yml/parseFolder.ts +93 -0
  122. package/src/formats/yml/parseItem.ts +102 -0
  123. package/src/formats/yml/stringifyCollection.ts +264 -0
  124. package/src/formats/yml/stringifyEnvironment.ts +67 -0
  125. package/src/formats/yml/stringifyFolder.ts +107 -0
  126. package/src/formats/yml/stringifyItem.ts +37 -0
  127. package/src/formats/yml/utils.ts +38 -0
  128. package/src/index.ts +64 -32
  129. package/src/types/bruno-lang.d.ts +1 -1
  130. package/src/types.ts +5 -124
  131. package/src/utils/index.ts +25 -0
  132. package/src/workers/WorkerQueue/index.ts +10 -10
  133. package/src/workers/index.ts +21 -20
  134. package/src/workers/worker-script.ts +21 -6
  135. package/dist/cjs/formats/bru/index.d.ts +0 -19
  136. package/dist/cjs/formats/bru/utils/oauth2-additional-params.d.ts +0 -38
  137. package/dist/cjs/formats/bru/utils/request-parse-and-redact-body-data.d.ts +0 -9
  138. package/dist/cjs/index.d.ts +0 -16
  139. package/dist/cjs/types.d.ts +0 -142
  140. package/dist/cjs/workers/WorkerQueue/index.d.ts +0 -26
  141. package/dist/cjs/workers/formats/bru/index.d.ts +0 -19
  142. package/dist/cjs/workers/formats/bru/utils/request-parse-and-redact-body-data.d.ts +0 -9
  143. package/dist/cjs/workers/index.d.ts +0 -16
  144. package/dist/cjs/workers/types.d.ts +0 -142
  145. package/dist/cjs/workers/worker-script.d.ts +0 -1
  146. package/dist/cjs/workers/workers/WorkerQueue/index.d.ts +0 -26
  147. package/dist/cjs/workers/workers/index.d.ts +0 -10
  148. package/dist/cjs/workers/workers/worker-script.d.ts +0 -1
  149. package/dist/esm/formats/bru/index.d.ts +0 -19
  150. package/dist/esm/formats/bru/utils/oauth2-additional-params.d.ts +0 -38
  151. package/dist/esm/index.d.ts +0 -16
  152. package/dist/esm/types.d.ts +0 -142
  153. package/dist/esm/workers/formats/bru/index.d.ts +0 -19
  154. package/dist/esm/workers/formats/bru/utils/oauth2-additional-params.d.ts +0 -38
  155. package/dist/esm/workers/formats/bru/utils/request-parse-and-redact-body-data.d.ts +0 -9
  156. package/dist/esm/workers/index.d.ts +0 -16
  157. package/dist/esm/workers/types.d.ts +0 -142
  158. package/dist/esm/workers/worker-script.d.ts +0 -1
  159. package/dist/esm/workers/workers/WorkerQueue/index.d.ts +0 -26
  160. package/dist/esm/workers/workers/index.d.ts +0 -10
  161. package/dist/esm/workers/workers/worker-script.d.ts +0 -1
@@ -0,0 +1,264 @@
1
+ import type { OpenCollection } from '@opencollection/types';
2
+ import type { ProtoFileItem, ProtoFileImportPath } from '@opencollection/types/config/protobuf';
3
+ import type { HttpRequestHeader } from '@opencollection/types/requests/http';
4
+ import type { ClientCertificate, PemCertificate, Pkcs12Certificate } from '@opencollection/types/config/certificates';
5
+ import type { Variable } from '@opencollection/types/common/variables';
6
+ import type { Action } from '@opencollection/types/common/actions';
7
+ import type { Scripts } from '@opencollection/types/common/scripts';
8
+ import { stringifyYml } from './utils';
9
+ import { toOpenCollectionAuth } from './common/auth';
10
+ import { toOpenCollectionHttpHeaders } from './common/headers';
11
+ import { toOpenCollectionVariables } from './common/variables';
12
+ import { toOpenCollectionActions } from './common/actions';
13
+ import { toOpenCollectionScripts } from './common/scripts';
14
+ import type { Auth } from '@opencollection/types/common/auth';
15
+
16
+ const hasCollectionConfig = (brunoConfig: any): boolean => {
17
+ // protobuf
18
+ const hasProtobuf = (
19
+ brunoConfig.protobuf?.protofFiles?.length > 0
20
+ || brunoConfig.protobuf?.importPaths?.length > 0
21
+ );
22
+
23
+ // proxy - check if proxy is configured in newer format
24
+ // Valid newer format: has 'inherit' property and 'config' object
25
+ const isValidProxyFormat = brunoConfig.proxy
26
+ && typeof brunoConfig.proxy === 'object'
27
+ && 'inherit' in brunoConfig.proxy
28
+ && brunoConfig.proxy.config
29
+ && typeof brunoConfig.proxy.config === 'object';
30
+
31
+ const hasProxy = isValidProxyFormat;
32
+
33
+ // client certificates
34
+ const hasClientCertificates = brunoConfig.clientCertificates?.certs?.length > 0;
35
+
36
+ return hasProtobuf || hasProxy || hasClientCertificates;
37
+ };
38
+
39
+ const hasRequestDefaults = (collectionRoot: any): boolean => {
40
+ const requestRoot = collectionRoot?.request;
41
+
42
+ return Boolean((requestRoot?.headers?.length)
43
+ || (requestRoot?.vars?.req?.length)
44
+ || (requestRoot?.vars?.res?.length)
45
+ || hasRequestScripts(collectionRoot)
46
+ || hasRequestAuth(collectionRoot));
47
+ };
48
+
49
+ const hasRequestAuth = (collectionRoot: any): boolean => {
50
+ const reqAuthMode = collectionRoot?.request?.auth?.mode;
51
+ return Boolean(reqAuthMode && reqAuthMode !== 'none');
52
+ };
53
+
54
+ const hasRequestScripts = (collectionRoot: any): boolean => {
55
+ if (!collectionRoot?.request) return false;
56
+
57
+ return (collectionRoot.request.script?.req)
58
+ || (collectionRoot.request.script?.res)
59
+ || (collectionRoot.request.tests);
60
+ };
61
+
62
+ const hasPresets = (brunoConfig: any): boolean => {
63
+ return brunoConfig?.presets?.requestType?.length
64
+ || brunoConfig?.presets?.requestUrl?.length;
65
+ };
66
+
67
+ const stringifyCollection = (collectionRoot: any, brunoConfig: any): string => {
68
+ try {
69
+ const oc: OpenCollection = {};
70
+
71
+ oc.opencollection = '1.0.0';
72
+ oc.info = {
73
+ name: brunoConfig.name || 'Untitled Collection'
74
+ };
75
+
76
+ // collection config
77
+ if (hasCollectionConfig(brunoConfig)) {
78
+ oc.config = {};
79
+
80
+ if (brunoConfig.protobuf?.protofFiles?.length) {
81
+ oc.config.protobuf = {
82
+ protoFiles: brunoConfig.protobuf.protofFiles.map((protoFile: any): ProtoFileItem => ({
83
+ type: 'file' as const,
84
+ path: protoFile.path
85
+ })),
86
+ importPaths: brunoConfig.protobuf.importPaths.map((importPath: any): ProtoFileImportPath => ({
87
+ path: importPath.path,
88
+ disabled: importPath.disabled
89
+ }))
90
+ };
91
+ }
92
+
93
+ // proxy - only write newer format
94
+ // Validate that brunoConfig.proxy is in newer format before writing
95
+ const isValidProxyFormat = brunoConfig.proxy
96
+ && typeof brunoConfig.proxy === 'object'
97
+ && 'inherit' in brunoConfig.proxy
98
+ && brunoConfig.proxy.config
99
+ && typeof brunoConfig.proxy.config === 'object';
100
+
101
+ if (isValidProxyFormat) {
102
+ oc.config.proxy = {
103
+ inherit: brunoConfig.proxy.inherit,
104
+ config: {
105
+ protocol: brunoConfig.proxy.config.protocol || 'http',
106
+ hostname: brunoConfig.proxy.config.hostname || '',
107
+ port: brunoConfig.proxy.config.port || '',
108
+ auth: {
109
+ username: brunoConfig.proxy.config.auth?.username || '',
110
+ password: brunoConfig.proxy.config.auth?.password || ''
111
+ },
112
+ bypassProxy: brunoConfig.proxy.config.bypassProxy || ''
113
+ }
114
+ };
115
+
116
+ // Add optional disabled field if true
117
+ if (brunoConfig.proxy.disabled === true) {
118
+ oc.config.proxy.disabled = true;
119
+ }
120
+
121
+ // Add optional auth.disabled field if true
122
+ if (brunoConfig.proxy.config?.auth?.disabled === true) {
123
+ if (oc.config.proxy.config && oc.config.proxy.config.auth) {
124
+ oc.config.proxy.config.auth.disabled = true;
125
+ }
126
+ }
127
+ }
128
+
129
+ // client certificates
130
+ if (brunoConfig.clientCertificates?.certs?.length) {
131
+ oc.config.clientCertificates = brunoConfig.clientCertificates.certs
132
+ .map((cert: any): ClientCertificate | null => {
133
+ if (cert.type === 'cert') {
134
+ const pemCert: PemCertificate = {
135
+ domain: cert.domain,
136
+ type: 'pem',
137
+ certificateFilePath: cert.certFilePath,
138
+ privateKeyFilePath: cert.keyFilePath,
139
+ ...(cert.passphrase && { passphrase: cert.passphrase })
140
+ };
141
+ return pemCert;
142
+ } else if (cert.type === 'pfx') {
143
+ const pkcs12Cert: Pkcs12Certificate = {
144
+ domain: cert.domain,
145
+ type: 'pkcs12',
146
+ pkcs12FilePath: cert.pfxFilePath,
147
+ ...(cert.passphrase && { passphrase: cert.passphrase })
148
+ };
149
+ return pkcs12Cert;
150
+ } else {
151
+ // Unsupported certificate type - ignore silently
152
+ return null;
153
+ }
154
+ })
155
+ .filter((cert: ClientCertificate | null): cert is ClientCertificate => cert !== null);
156
+ }
157
+ }
158
+
159
+ // request defaults
160
+ if (hasRequestDefaults(collectionRoot)) {
161
+ oc.request = {};
162
+
163
+ // headers
164
+ if (collectionRoot.request?.headers?.length) {
165
+ const ocHeaders: HttpRequestHeader[] | undefined = toOpenCollectionHttpHeaders(collectionRoot.request?.headers);
166
+ if (ocHeaders) {
167
+ oc.request.headers = ocHeaders;
168
+ }
169
+ }
170
+
171
+ // auth
172
+ if (hasRequestAuth(collectionRoot)) {
173
+ const ocAuth: Auth | undefined = toOpenCollectionAuth(collectionRoot.request?.auth);
174
+ if (ocAuth) {
175
+ oc.request.auth = ocAuth;
176
+ }
177
+ }
178
+
179
+ // variables
180
+ if (collectionRoot.request?.vars?.req?.length) {
181
+ const ocVariables: Variable[] | undefined = toOpenCollectionVariables(collectionRoot.request?.vars);
182
+ if (ocVariables) {
183
+ oc.request.variables = ocVariables;
184
+ }
185
+ }
186
+
187
+ // actions (post-response variables)
188
+ if (collectionRoot.request?.vars?.res?.length) {
189
+ const ocActions: Action[] | undefined = toOpenCollectionActions(collectionRoot.request?.vars?.res);
190
+ if (ocActions) {
191
+ (oc.request as any).actions = ocActions;
192
+ }
193
+ }
194
+
195
+ // scripts
196
+ if (hasRequestScripts(collectionRoot)) {
197
+ const ocScripts: Scripts | undefined = toOpenCollectionScripts(collectionRoot.request);
198
+ if (ocScripts) {
199
+ oc.request.scripts = ocScripts;
200
+ }
201
+ }
202
+ }
203
+
204
+ // docs
205
+ if (collectionRoot?.docs?.trim().length) {
206
+ oc.docs = {
207
+ content: collectionRoot.docs,
208
+ type: 'text/markdown'
209
+ };
210
+ }
211
+
212
+ // bundled
213
+ oc.bundled = false;
214
+
215
+ // extensions
216
+ oc.extensions = {};
217
+
218
+ const hasBrunoExtensions = brunoConfig.ignore?.length || hasPresets(brunoConfig);
219
+
220
+ if (hasBrunoExtensions) {
221
+ const brunoExtension: any = {};
222
+
223
+ if (brunoConfig.ignore?.length) {
224
+ const ignoreList: string[] = [];
225
+ brunoConfig.ignore.forEach((ignore: string) => {
226
+ ignoreList.push(ignore);
227
+ });
228
+ brunoExtension.ignore = ignoreList;
229
+ }
230
+
231
+ if (hasPresets(brunoConfig)) {
232
+ const presetsRequest: any = {};
233
+ if (brunoConfig.presets.requestType?.length) {
234
+ presetsRequest.type = brunoConfig.presets.requestType;
235
+ }
236
+ if (brunoConfig.presets.requestUrl?.length) {
237
+ presetsRequest.url = brunoConfig.presets.requestUrl;
238
+ }
239
+ brunoExtension.presets = {
240
+ request: presetsRequest
241
+ };
242
+ }
243
+
244
+ oc.extensions.bruno = brunoExtension;
245
+ }
246
+
247
+ // bruno-specific script extensions
248
+ if (brunoConfig.scripts?.additionalContextRoots?.length) {
249
+ if (!oc.extensions.bruno) {
250
+ oc.extensions.bruno = {};
251
+ }
252
+ (oc.extensions.bruno as any).scripts = {
253
+ additionalContextRoots: brunoConfig.scripts.additionalContextRoots
254
+ };
255
+ }
256
+
257
+ return stringifyYml(oc);
258
+ } catch (error) {
259
+ console.error('Error stringifying opencollection.yml:', error);
260
+ throw error;
261
+ }
262
+ };
263
+
264
+ export default stringifyCollection;
@@ -0,0 +1,67 @@
1
+ import type { Environment as BrunoEnvironment, EnvironmentVariable as BrunoEnvironmentVariable } from '@usebruno/schema-types/collection/environment';
2
+ import type { Environment } from '@opencollection/types/config/environments';
3
+ import type { Variable, SecretVariable } from '@opencollection/types/common/variables';
4
+ import { stringifyYml } from './utils';
5
+
6
+ const toOpenCollectionEnvironmentVariables = (variables: BrunoEnvironmentVariable[]): (Variable | SecretVariable)[] | undefined => {
7
+ if (!variables?.length) {
8
+ return undefined;
9
+ }
10
+
11
+ const ocVariables: (Variable | SecretVariable)[] = variables
12
+ .filter((v: BrunoEnvironmentVariable) => {
13
+ // todo: currently neither bru lang nor bruno app supports non-string values
14
+ // update this when bruno app supports non-string values
15
+ return typeof v.value === 'string';
16
+ })
17
+ .map((v: BrunoEnvironmentVariable): Variable | SecretVariable => {
18
+ if (v.secret === true) {
19
+ const secretVar: SecretVariable = {
20
+ secret: true,
21
+ name: v.name || ''
22
+ };
23
+ if (v.enabled === false) {
24
+ secretVar.disabled = true;
25
+ }
26
+ return secretVar;
27
+ }
28
+
29
+ const variable: Variable = {
30
+ name: v.name || '',
31
+ value: v.value as string
32
+ };
33
+
34
+ if (v.enabled === false) {
35
+ variable.disabled = true;
36
+ }
37
+
38
+ return variable;
39
+ });
40
+
41
+ return ocVariables.length > 0 ? ocVariables : undefined;
42
+ };
43
+
44
+ const stringifyEnvironment = (environment: BrunoEnvironment): string => {
45
+ try {
46
+ const ocEnvironment: Environment = {
47
+ name: environment.name
48
+ };
49
+
50
+ if (environment.color) {
51
+ ocEnvironment.color = environment.color;
52
+ }
53
+
54
+ if (environment.variables?.length) {
55
+ const ocVariables = toOpenCollectionEnvironmentVariables(environment.variables);
56
+ if (ocVariables) {
57
+ ocEnvironment.variables = ocVariables;
58
+ }
59
+ }
60
+
61
+ return stringifyYml(ocEnvironment);
62
+ } catch (error) {
63
+ console.error('Error stringifying environment:', error);
64
+ throw error;
65
+ }
66
+ };
67
+ export default stringifyEnvironment;
@@ -0,0 +1,107 @@
1
+ import type { FolderRoot } from '@usebruno/schema-types/collection/folder';
2
+ import type { Folder, FolderInfo } from '@opencollection/types/collection/item';
3
+ import type { Variable } from '@opencollection/types/common/variables';
4
+ import type { Action } from '@opencollection/types/common/actions';
5
+ import type { Scripts } from '@opencollection/types/common/scripts';
6
+ import type { Auth } from '@opencollection/types/common/auth';
7
+ import type { HttpRequestHeader } from '@opencollection/types/requests/http';
8
+ import type { RequestDefaults } from '@opencollection/types/common/request-defaults';
9
+ import { toOpenCollectionAuth } from './common/auth';
10
+ import { toOpenCollectionHttpHeaders } from './common/headers';
11
+ import { toOpenCollectionVariables } from './common/variables';
12
+ import { toOpenCollectionActions } from './common/actions';
13
+ import { toOpenCollectionScripts } from './common/scripts';
14
+ import { stringifyYml } from './utils';
15
+
16
+ const hasRequestDefaults = (folderRoot: FolderRoot): boolean => {
17
+ const requestDefaults = folderRoot?.request;
18
+
19
+ return Boolean((requestDefaults?.headers?.length)
20
+ || (requestDefaults?.vars?.req?.length)
21
+ || (requestDefaults?.vars?.res?.length)
22
+ || hasRequestScripts(folderRoot)
23
+ || hasRequestAuth(folderRoot));
24
+ };
25
+
26
+ const hasRequestAuth = (folderRoot: FolderRoot): boolean => {
27
+ return Boolean((folderRoot.request?.auth?.mode !== 'none'));
28
+ };
29
+
30
+ const hasRequestScripts = (folderRoot: FolderRoot): boolean => {
31
+ return Boolean((folderRoot.request?.script?.req)
32
+ || (folderRoot.request?.script?.res)
33
+ || (folderRoot.request?.tests));
34
+ };
35
+
36
+ const stringifyFolder = (folderRoot: FolderRoot): string => {
37
+ try {
38
+ const ocFolder: Folder = {};
39
+
40
+ // info block
41
+ const info: FolderInfo = {
42
+ name: folderRoot.meta?.name || 'Untitled Folder',
43
+ type: 'folder',
44
+ seq: folderRoot.meta?.seq || 1
45
+ };
46
+ ocFolder.info = info;
47
+
48
+ // request defaults
49
+ if (hasRequestDefaults(folderRoot)) {
50
+ ocFolder.request = {} as RequestDefaults;
51
+
52
+ // headers
53
+ if (folderRoot.request?.headers?.length) {
54
+ const ocHeaders: HttpRequestHeader[] | undefined = toOpenCollectionHttpHeaders(folderRoot.request?.headers);
55
+ if (ocHeaders) {
56
+ ocFolder.request.headers = ocHeaders;
57
+ }
58
+ }
59
+
60
+ // auth
61
+ if (hasRequestAuth(folderRoot)) {
62
+ const ocAuth: Auth | undefined = toOpenCollectionAuth(folderRoot.request?.auth);
63
+ if (ocAuth) {
64
+ ocFolder.request.auth = ocAuth;
65
+ }
66
+ }
67
+
68
+ // variables
69
+ if (folderRoot.request?.vars?.req?.length) {
70
+ const ocVariables: Variable[] | undefined = toOpenCollectionVariables(folderRoot.request?.vars);
71
+ if (ocVariables) {
72
+ ocFolder.request.variables = ocVariables;
73
+ }
74
+ }
75
+
76
+ // actions (post-response variables)
77
+ if (folderRoot.request?.vars?.res?.length) {
78
+ const ocActions: Action[] | undefined = toOpenCollectionActions(folderRoot.request?.vars?.res);
79
+ if (ocActions) {
80
+ (ocFolder.request as any).actions = ocActions;
81
+ }
82
+ }
83
+
84
+ // scripts
85
+ if (hasRequestScripts(folderRoot)) {
86
+ const ocScripts: Scripts | undefined = toOpenCollectionScripts(folderRoot?.request);
87
+ if (ocScripts) {
88
+ ocFolder.request.scripts = ocScripts;
89
+ }
90
+ }
91
+ }
92
+
93
+ // docs
94
+ if (folderRoot.docs?.trim().length) {
95
+ ocFolder.docs = {
96
+ content: folderRoot.docs,
97
+ type: 'text/markdown'
98
+ };
99
+ }
100
+
101
+ return stringifyYml(ocFolder);
102
+ } catch (error) {
103
+ console.error('Error stringifying folder.yml:', error);
104
+ throw error;
105
+ }
106
+ };
107
+ export default stringifyFolder;
@@ -0,0 +1,37 @@
1
+ import type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
2
+ import stringifyHttpRequest from './items/stringifyHttpRequest';
3
+ import stringifyGraphqlRequest from './items/stringifyGraphQLRequest';
4
+ import stringifyGrpcRequest from './items/stringifyGrpcRequest';
5
+ import stringifyWebsocketRequest from './items/stringifyWebsocketRequest';
6
+ import stringifyScript from './items/stringifyScript';
7
+
8
+ const stringifyItem = (item: BrunoItem): string => {
9
+ try {
10
+ switch (item.type) {
11
+ case 'http-request':
12
+ return stringifyHttpRequest(item);
13
+
14
+ case 'graphql-request':
15
+ return stringifyGraphqlRequest(item);
16
+
17
+ case 'grpc-request':
18
+ return stringifyGrpcRequest(item);
19
+
20
+ case 'ws-request':
21
+ return stringifyWebsocketRequest(item);
22
+
23
+ case 'js':
24
+ return stringifyScript(item);
25
+
26
+ case 'folder':
27
+ throw new Error('Folder items should be handled separately using stringifyFolder');
28
+
29
+ default:
30
+ throw new Error(`Unsupported item type: ${item.type}`);
31
+ }
32
+ } catch (error) {
33
+ console.error('Error stringifying item:', error);
34
+ throw error;
35
+ }
36
+ };
37
+ export default stringifyItem;
@@ -0,0 +1,38 @@
1
+ import * as YAML from 'yaml';
2
+
3
+ // Top-level keys that should have a blank line before them
4
+ const BLOCK_KEYS = ['info', 'http', 'graphql', 'grpc', 'websocket', 'runtime', 'settings', 'examples', 'docs', 'items', 'request'];
5
+
6
+ export const stringifyYml = (obj: any): string => {
7
+ const yamlStr = YAML.stringify(obj, {
8
+ lineWidth: 0,
9
+ indent: 2,
10
+ minContentWidth: 0,
11
+ defaultStringType: 'PLAIN'
12
+ });
13
+
14
+ // Add blank lines before major blocks (only at the top level, not indented)
15
+ const lines = yamlStr.split('\n');
16
+ const result: string[] = [];
17
+
18
+ for (let i = 0; i < lines.length; i++) {
19
+ const line = lines[i];
20
+ // Check if this is a top-level key (no leading whitespace) that should have a blank line
21
+ if (i > 0 && !line.startsWith(' ') && !line.startsWith('\t')) {
22
+ const key = line.split(':')[0];
23
+ if (BLOCK_KEYS.includes(key)) {
24
+ // Add blank line before this block (if previous line isn't already blank)
25
+ if (result.length > 0 && result[result.length - 1].trim() !== '') {
26
+ result.push('');
27
+ }
28
+ }
29
+ }
30
+ result.push(line);
31
+ }
32
+
33
+ return result.join('\n');
34
+ };
35
+
36
+ export const parseYml = (ymlString: string): any => {
37
+ return YAML.parse(ymlString);
38
+ };
package/src/index.ts CHANGED
@@ -1,25 +1,39 @@
1
+ import type { BrunoCollection, BrunoItem, BrunoEnvironment } from '@usebruno/schema-types';
2
+
1
3
  import {
2
- bruRequestToJson,
3
- jsonRequestToBru,
4
- bruCollectionToJson,
5
- jsonCollectionToBru,
6
- bruEnvironmentToJson,
7
- jsonEnvironmentToBru
4
+ parseBruRequest,
5
+ parseBruCollection,
6
+ parseBruEnvironment,
7
+ stringifyBruRequest,
8
+ stringifyBruCollection,
9
+ stringifyBruEnvironment
8
10
  } from './formats/bru';
11
+ import {
12
+ parseYmlItem,
13
+ parseYmlCollection,
14
+ parseYmlFolder,
15
+ parseYmlEnvironment,
16
+ stringifyYmlItem,
17
+ stringifyYmlFolder,
18
+ stringifyYmlCollection,
19
+ stringifyYmlEnvironment
20
+ } from './formats/yml';
9
21
  import { dotenvToJson } from '@usebruno/lang';
10
22
  import BruParserWorker from './workers';
11
23
  import {
12
24
  ParseOptions,
13
25
  StringifyOptions,
14
- ParsedRequest,
15
- ParsedCollection,
16
- ParsedEnvironment
26
+ CollectionFormat
17
27
  } from './types';
28
+ import { DEFAULT_COLLECTION_FORMAT } from './constants';
18
29
  import { bruRequestParseAndRedactBodyData } from './formats/bru/utils/request-parse-and-redact-body-data';
19
30
 
20
- export const parseRequest = (content: string, options: ParseOptions = { format: 'bru' }): any => {
31
+ // request
32
+ export const parseRequest = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => {
21
33
  if (options.format === 'bru') {
22
- return bruRequestToJson(content);
34
+ return parseBruRequest(content);
35
+ } else if (options.format === 'yml') {
36
+ return parseYmlItem(content);
23
37
  }
24
38
  throw new Error(`Unsupported format: ${options.format}`);
25
39
  };
@@ -31,15 +45,17 @@ export const parseRequestAndRedactBody = (content: string, options: ParseOptions
31
45
  throw new Error(`Unsupported format: ${options.format}`);
32
46
  };
33
47
 
34
- export const stringifyRequest = (requestObj: ParsedRequest, options: StringifyOptions = { format: 'bru' }): string => {
48
+ export const stringifyRequest = (requestObj: BrunoItem, options: StringifyOptions = { format: DEFAULT_COLLECTION_FORMAT }): string => {
35
49
  if (options.format === 'bru') {
36
- return jsonRequestToBru(requestObj);
50
+ return stringifyBruRequest(requestObj);
51
+ } else if (options.format === 'yml') {
52
+ return stringifyYmlItem(requestObj);
37
53
  }
38
54
  throw new Error(`Unsupported format: ${options.format}`);
39
55
  };
40
56
 
57
+ // request via worker
41
58
  let globalWorkerInstance: BruParserWorker | null = null;
42
-
43
59
  const getWorkerInstance = (): BruParserWorker => {
44
60
  if (!globalWorkerInstance) {
45
61
  globalWorkerInstance = new BruParserWorker();
@@ -47,62 +63,78 @@ const getWorkerInstance = (): BruParserWorker => {
47
63
  return globalWorkerInstance;
48
64
  };
49
65
 
50
- export const parseRequestViaWorker = async (content: string): Promise<any> => {
66
+ export const parseRequestViaWorker = async (content: string, options: { format: CollectionFormat; filename?: string }): Promise<any> => {
51
67
  const fileParserWorker = getWorkerInstance();
52
- return await fileParserWorker.parseRequest(content);
68
+
69
+ return await fileParserWorker.parseRequest(content, options.format);
53
70
  };
54
71
 
55
- export const stringifyRequestViaWorker = async (requestObj: any): Promise<string> => {
72
+ export const stringifyRequestViaWorker = async (requestObj: any, options: { format: CollectionFormat }): Promise<string> => {
56
73
  const fileParserWorker = getWorkerInstance();
57
- return await fileParserWorker.stringifyRequest(requestObj);
74
+ return await fileParserWorker.stringifyRequest(requestObj, options.format);
58
75
  };
59
76
 
60
- export const parseCollection = (content: string, options: ParseOptions = { format: 'bru' }): any => {
77
+ // collection
78
+ export const parseCollection = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => {
61
79
  if (options.format === 'bru') {
62
- return bruCollectionToJson(content);
80
+ return parseBruCollection(content);
81
+ } else if (options.format === 'yml') {
82
+ return parseYmlCollection(content);
63
83
  }
64
84
  throw new Error(`Unsupported format: ${options.format}`);
65
85
  };
66
86
 
67
- export const stringifyCollection = (collectionObj: ParsedCollection, options: StringifyOptions = { format: 'bru' }): string => {
87
+ export const stringifyCollection = (collectionObj: BrunoCollection, brunoConfig: any, options: StringifyOptions = { format: DEFAULT_COLLECTION_FORMAT }): string => {
68
88
  if (options.format === 'bru') {
69
- return jsonCollectionToBru(collectionObj, false);
89
+ return stringifyBruCollection(collectionObj, false);
90
+ } else if (options.format === 'yml') {
91
+ return stringifyYmlCollection(collectionObj, brunoConfig);
70
92
  }
71
93
  throw new Error(`Unsupported format: ${options.format}`);
72
94
  };
73
95
 
74
- export const parseFolder = (content: string, options: ParseOptions = { format: 'bru' }): any => {
96
+ // folder
97
+ export const parseFolder = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => {
75
98
  if (options.format === 'bru') {
76
- return bruCollectionToJson(content);
99
+ return parseBruCollection(content);
100
+ } else if (options.format === 'yml') {
101
+ return parseYmlFolder(content);
77
102
  }
78
103
  throw new Error(`Unsupported format: ${options.format}`);
79
104
  };
80
105
 
81
- export const stringifyFolder = (folderObj: any, options: StringifyOptions = { format: 'bru' }): string => {
106
+ export const stringifyFolder = (folderObj: any, options: StringifyOptions = { format: DEFAULT_COLLECTION_FORMAT }): string => {
82
107
  if (options.format === 'bru') {
83
- return jsonCollectionToBru(folderObj, true);
108
+ return stringifyBruCollection(folderObj, true);
109
+ } else if (options.format === 'yml') {
110
+ return stringifyYmlFolder(folderObj);
84
111
  }
85
112
  throw new Error(`Unsupported format: ${options.format}`);
86
113
  };
87
114
 
88
- export const parseEnvironment = (content: string, options: ParseOptions = { format: 'bru' }): any => {
115
+ // environment
116
+ export const parseEnvironment = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => {
89
117
  if (options.format === 'bru') {
90
- return bruEnvironmentToJson(content);
118
+ return parseBruEnvironment(content);
119
+ } else if (options.format === 'yml') {
120
+ return parseYmlEnvironment(content);
91
121
  }
92
122
  throw new Error(`Unsupported format: ${options.format}`);
93
123
  };
94
124
 
95
- export const stringifyEnvironment = (envObj: ParsedEnvironment, options: StringifyOptions = { format: 'bru' }): string => {
125
+ export const stringifyEnvironment = (envObj: BrunoEnvironment, options: StringifyOptions = { format: DEFAULT_COLLECTION_FORMAT }): string => {
96
126
  if (options.format === 'bru') {
97
- return jsonEnvironmentToBru(envObj);
127
+ return stringifyBruEnvironment(envObj);
128
+ } else if (options.format === 'yml') {
129
+ return stringifyYmlEnvironment(envObj);
98
130
  }
99
131
  throw new Error(`Unsupported format: ${options.format}`);
100
132
  };
101
133
 
102
-
103
134
  export const parseDotEnv = (content: string): Record<string, string> => {
104
135
  return dotenvToJson(content);
105
136
  };
106
137
 
107
138
  export { BruParserWorker };
108
- export * from './types';
139
+ export * from './types';
140
+ export * from './constants';
@@ -6,4 +6,4 @@ declare module '@usebruno/lang' {
6
6
  export function collectionBruToJson(bruContent: string): any;
7
7
  export function jsonToCollectionBru(jsonData: any): string;
8
8
  export function dotenvToJson(envContent: string): Record<string, string>;
9
- }
9
+ }