@scalar/workspace-store 0.64.0 → 0.66.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 (168) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +45 -10
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +109 -10
  5. package/dist/entities/auth/schema.d.ts +351 -20
  6. package/dist/entities/auth/schema.d.ts.map +1 -1
  7. package/dist/entities/auth/schema.js +3 -1
  8. package/dist/events/definitions/auth.d.ts +1 -2
  9. package/dist/events/definitions/auth.d.ts.map +1 -1
  10. package/dist/events/definitions/operation.d.ts +3 -4
  11. package/dist/events/definitions/operation.d.ts.map +1 -1
  12. package/dist/events/definitions/server.d.ts +1 -2
  13. package/dist/events/definitions/server.d.ts.map +1 -1
  14. package/dist/events/definitions/ui.d.ts +3 -4
  15. package/dist/events/definitions/ui.d.ts.map +1 -1
  16. package/dist/events/old-definitions.d.ts +1 -2
  17. package/dist/events/old-definitions.d.ts.map +1 -1
  18. package/dist/helpers/chunk-index.d.ts +20 -2
  19. package/dist/helpers/chunk-index.d.ts.map +1 -1
  20. package/dist/helpers/chunk-index.js +14 -1
  21. package/dist/helpers/external-examples.d.ts +2 -0
  22. package/dist/helpers/external-examples.d.ts.map +1 -1
  23. package/dist/helpers/external-examples.js +1 -0
  24. package/dist/helpers/for-each-path-item-operation.d.ts +6 -5
  25. package/dist/helpers/for-each-path-item-operation.d.ts.map +1 -1
  26. package/dist/helpers/for-each-path-item-operation.js +40 -14
  27. package/dist/helpers/get-example-value.d.ts +15 -0
  28. package/dist/helpers/get-example-value.d.ts.map +1 -0
  29. package/dist/helpers/get-example-value.js +28 -0
  30. package/dist/helpers/get-parameter-example.d.ts +14 -0
  31. package/dist/helpers/get-parameter-example.d.ts.map +1 -0
  32. package/dist/helpers/get-parameter-example.js +19 -0
  33. package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
  34. package/dist/helpers/get-resolved-ref.js +12 -1
  35. package/dist/helpers/normalize-boolean-schemas.d.ts +10 -0
  36. package/dist/helpers/normalize-boolean-schemas.d.ts.map +1 -0
  37. package/dist/helpers/normalize-boolean-schemas.js +193 -0
  38. package/dist/helpers/operation-examples.d.ts.map +1 -1
  39. package/dist/helpers/operation-examples.js +5 -2
  40. package/dist/helpers/querystring-parameter.d.ts +21 -0
  41. package/dist/helpers/querystring-parameter.d.ts.map +1 -0
  42. package/dist/helpers/querystring-parameter.js +108 -0
  43. package/dist/helpers/serialize-stream-example.d.ts +8 -0
  44. package/dist/helpers/serialize-stream-example.d.ts.map +1 -0
  45. package/dist/helpers/serialize-stream-example.js +52 -0
  46. package/dist/helpers/use-external-examples.d.ts.map +1 -1
  47. package/dist/helpers/use-external-examples.js +8 -1
  48. package/dist/mutators/index.d.ts +3 -3
  49. package/dist/mutators/operation/body.d.ts.map +1 -1
  50. package/dist/mutators/operation/body.js +9 -2
  51. package/dist/mutators/operation/operation.d.ts.map +1 -1
  52. package/dist/mutators/operation/operation.js +4 -7
  53. package/dist/mutators/operation/parameters.d.ts.map +1 -1
  54. package/dist/mutators/operation/parameters.js +43 -3
  55. package/dist/navigation/get-navigation-options.d.ts.map +1 -1
  56. package/dist/navigation/get-navigation-options.js +19 -4
  57. package/dist/navigation/helpers/traverse-paths.d.ts.map +1 -1
  58. package/dist/navigation/helpers/traverse-paths.js +2 -2
  59. package/dist/navigation/helpers/traverse-webhooks.d.ts.map +1 -1
  60. package/dist/navigation/helpers/traverse-webhooks.js +2 -2
  61. package/dist/navigation/helpers/update-order-ids.d.ts +1 -2
  62. package/dist/navigation/helpers/update-order-ids.d.ts.map +1 -1
  63. package/dist/plugins/bundler/index.d.ts +1 -0
  64. package/dist/plugins/bundler/index.d.ts.map +1 -1
  65. package/dist/plugins/bundler/index.js +11 -3
  66. package/dist/plugins/bundler/openapi-document.d.ts +6 -0
  67. package/dist/plugins/bundler/openapi-document.d.ts.map +1 -0
  68. package/dist/plugins/bundler/openapi-document.js +57 -0
  69. package/dist/request-example/builder/body/build-multipart.d.ts +29 -0
  70. package/dist/request-example/builder/body/build-multipart.d.ts.map +1 -0
  71. package/dist/request-example/builder/body/build-multipart.js +191 -0
  72. package/dist/request-example/builder/body/build-request-body.d.ts +6 -1
  73. package/dist/request-example/builder/body/build-request-body.d.ts.map +1 -1
  74. package/dist/request-example/builder/body/build-request-body.js +80 -23
  75. package/dist/request-example/builder/body/encode-multipart-body.d.ts +8 -8
  76. package/dist/request-example/builder/body/encode-multipart-body.d.ts.map +1 -1
  77. package/dist/request-example/builder/body/encode-multipart-body.js +56 -18
  78. package/dist/request-example/builder/body/get-request-body-example.d.ts.map +1 -1
  79. package/dist/request-example/builder/body/get-request-body-example.js +20 -6
  80. package/dist/request-example/builder/body/multipart-limits.d.ts +3 -0
  81. package/dist/request-example/builder/body/multipart-limits.d.ts.map +1 -0
  82. package/dist/request-example/builder/body/multipart-limits.js +2 -0
  83. package/dist/request-example/builder/body/serialize-form-property.d.ts +2 -0
  84. package/dist/request-example/builder/body/serialize-form-property.d.ts.map +1 -1
  85. package/dist/request-example/builder/body/serialize-form-property.js +3 -2
  86. package/dist/request-example/builder/body/serialize-multipart-array.d.ts +1 -1
  87. package/dist/request-example/builder/build-request.d.ts.map +1 -1
  88. package/dist/request-example/builder/build-request.js +7 -1
  89. package/dist/request-example/builder/header/build-request-parameters.d.ts +2 -0
  90. package/dist/request-example/builder/header/build-request-parameters.d.ts.map +1 -1
  91. package/dist/request-example/builder/header/build-request-parameters.js +54 -11
  92. package/dist/request-example/builder/header/is-param-disabled.d.ts.map +1 -1
  93. package/dist/request-example/builder/header/is-param-disabled.js +3 -1
  94. package/dist/request-example/builder/header/serialize-parameter.d.ts +11 -0
  95. package/dist/request-example/builder/header/serialize-parameter.d.ts.map +1 -1
  96. package/dist/request-example/builder/header/serialize-parameter.js +22 -0
  97. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
  98. package/dist/request-example/builder/helpers/get-example-from-schema.js +42 -3
  99. package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
  100. package/dist/request-example/builder/helpers/get-example.js +5 -2
  101. package/dist/request-example/builder/index.d.ts +2 -2
  102. package/dist/request-example/builder/index.d.ts.map +1 -1
  103. package/dist/request-example/builder/index.js +1 -1
  104. package/dist/request-example/builder/request-factory.d.ts +7 -0
  105. package/dist/request-example/builder/request-factory.d.ts.map +1 -1
  106. package/dist/request-example/builder/request-factory.js +10 -1
  107. package/dist/request-example/builder/resolve-request-factory-url.d.ts.map +1 -1
  108. package/dist/request-example/builder/resolve-request-factory-url.js +18 -2
  109. package/dist/request-example/builder/security/secret-types.d.ts +4 -1
  110. package/dist/request-example/builder/security/secret-types.d.ts.map +1 -1
  111. package/dist/request-example/context/headers.d.ts +1 -2
  112. package/dist/request-example/context/headers.d.ts.map +1 -1
  113. package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts.map +1 -1
  114. package/dist/request-example/context/security/extract-security-scheme-secrets.js +15 -0
  115. package/dist/request-example/index.d.ts +5 -2
  116. package/dist/request-example/index.d.ts.map +1 -1
  117. package/dist/request-example/index.js +4 -1
  118. package/dist/request-example/types.d.ts +1 -2
  119. package/dist/request-example/types.d.ts.map +1 -1
  120. package/dist/request-example/xml/serialize-xml-part.d.ts +9 -0
  121. package/dist/request-example/xml/serialize-xml-part.d.ts.map +1 -0
  122. package/dist/request-example/xml/serialize-xml-part.js +12 -0
  123. package/dist/schemas/extensions/operation/x-code-samples.d.ts +22 -0
  124. package/dist/schemas/extensions/operation/x-code-samples.d.ts.map +1 -1
  125. package/dist/schemas/extensions/operation/x-code-samples.js +4 -0
  126. package/dist/schemas/extensions.d.ts +9 -0
  127. package/dist/schemas/extensions.d.ts.map +1 -1
  128. package/dist/schemas/extensions.js +9 -0
  129. package/dist/schemas/navigation.d.ts +40 -34
  130. package/dist/schemas/navigation.d.ts.map +1 -1
  131. package/dist/schemas/navigation.js +2 -3
  132. package/dist/schemas/reference-config/index.d.ts +24 -4
  133. package/dist/schemas/reference-config/index.d.ts.map +1 -1
  134. package/dist/schemas/reference-config/settings.d.ts +24 -4
  135. package/dist/schemas/reference-config/settings.d.ts.map +1 -1
  136. package/dist/schemas/v3.1/strict/oauthflows.d.ts +26 -0
  137. package/dist/schemas/v3.1/strict/oauthflows.d.ts.map +1 -1
  138. package/dist/schemas/v3.1/strict/oauthflows.js +3 -0
  139. package/dist/schemas/v3.1/strict/openapi-document.d.ts +1582 -147
  140. package/dist/schemas/v3.1/strict/openapi-document.d.ts.map +1 -1
  141. package/dist/schemas/v3.1/strict/operation.d.ts +8 -0
  142. package/dist/schemas/v3.1/strict/operation.d.ts.map +1 -1
  143. package/dist/schemas/v3.1/strict/path-item.d.ts +19 -0
  144. package/dist/schemas/v3.1/strict/path-item.d.ts.map +1 -1
  145. package/dist/schemas/v3.1/strict/path-item.js +5 -0
  146. package/dist/schemas/v3.2/openapi/index.d.ts +5 -0
  147. package/dist/schemas/v3.2/openapi/index.d.ts.map +1 -1
  148. package/dist/schemas/v3.2/openapi/index.js +48 -12
  149. package/dist/schemas/v3.2/openapi/reference.d.ts.map +1 -1
  150. package/dist/schemas/v3.2/openapi/reference.js +11 -3
  151. package/dist/schemas/v3.2/strict/openapi-document.d.ts +842 -140
  152. package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
  153. package/dist/schemas/v3.2/strict/openapi-document.js +6 -14
  154. package/dist/schemas/v3.2/strict/operation.d.ts +8 -0
  155. package/dist/schemas/v3.2/strict/operation.d.ts.map +1 -1
  156. package/dist/schemas/v3.2/strict/parameter.d.ts +14 -0
  157. package/dist/schemas/v3.2/strict/parameter.d.ts.map +1 -1
  158. package/dist/schemas/v3.2/strict/parameter.js +2 -0
  159. package/dist/server.d.ts +10 -6
  160. package/dist/server.d.ts.map +1 -1
  161. package/dist/server.js +67 -45
  162. package/package.json +19 -9
  163. package/dist/schemas/v3.1/openapi/index.d.ts +0 -149
  164. package/dist/schemas/v3.1/openapi/index.d.ts.map +0 -1
  165. package/dist/schemas/v3.1/openapi/index.js +0 -732
  166. package/dist/schemas/v3.1/openapi/reference.d.ts +0 -4
  167. package/dist/schemas/v3.1/openapi/reference.d.ts.map +0 -1
  168. package/dist/schemas/v3.1/openapi/reference.js +0 -29
@@ -1,5 +1,7 @@
1
1
  import { getPathItemOperation } from '../../helpers/for-each-path-item-operation.js';
2
+ import { getParameterExample } from '../../helpers/get-parameter-example.js';
2
3
  import { getResolvedRef } from '../../helpers/get-resolved-ref.js';
4
+ import { getQuerystringParameter, serializeQuerystringParameter } from '../../helpers/querystring-parameter.js';
3
5
  import { unpackProxyObject } from '../../helpers/unpack-proxy.js';
4
6
  import { isOpenApiDocument } from '../../schemas/type-guards.js';
5
7
  import { isContentTypeParameterObject } from '../../schemas/v3.2/strict/type-guards.js';
@@ -41,8 +43,13 @@ const getPathItemsForParameterMutation = (pathItemRef) => {
41
43
  export const upsertOperationParameter = (document, { meta, type, payload, originalParameter }) => {
42
44
  // We are editing an existing parameter
43
45
  if (originalParameter) {
46
+ const querystring = getQuerystringParameter(originalParameter, meta.exampleKey, { includeDisabled: true });
47
+ const preserveQuerystringValue = querystring && payload.value === serializeQuerystringParameter(querystring);
44
48
  const param = originalParameter;
45
- const target = isContentTypeParameterObject(param) ? Object.values(param.content ?? {})[0] : param;
49
+ const selected = getParameterExample(param, meta.exampleKey);
50
+ const target = param.in !== 'querystring' && !selected.serialized && isContentTypeParameterObject(param)
51
+ ? Object.values(param.content ?? {})[0]
52
+ : param;
46
53
  if (!target) {
47
54
  return;
48
55
  }
@@ -77,7 +84,39 @@ export const upsertOperationParameter = (document, { meta, type, payload, origin
77
84
  return;
78
85
  }
79
86
  // Update the example value and disabled state
80
- example.value = payload.value;
87
+ if (param.in === 'querystring') {
88
+ // The whole-query editor displays the URI-ready value, including percent encoding.
89
+ delete example.value;
90
+ delete example.dataValue;
91
+ // Inline edits replace the external example source as well as its cached representation.
92
+ delete example.externalValue;
93
+ delete example.serializedValue;
94
+ // Toggling an unchanged preview must preserve data and environment placeholders
95
+ // so future environment changes still happen before serialization.
96
+ if (preserveQuerystringValue && querystring.kind !== 'uri-ready') {
97
+ if (querystring.kind === 'serialized') {
98
+ example.value = querystring.value;
99
+ }
100
+ else {
101
+ example.dataValue = querystring.value;
102
+ }
103
+ }
104
+ else {
105
+ example.serializedValue = String(payload.value);
106
+ }
107
+ }
108
+ else {
109
+ delete example.dataValue;
110
+ delete example.serializedValue;
111
+ delete example.externalValue;
112
+ if (selected.serialized || selected.mediaSerialized) {
113
+ delete example.value;
114
+ example.serializedValue = String(payload.value);
115
+ }
116
+ else {
117
+ example.value = payload.value;
118
+ }
119
+ }
81
120
  example['x-disabled'] = payload.isDisabled;
82
121
  return;
83
122
  }
@@ -212,5 +251,6 @@ export const deleteAllOperationParameters = (document, { meta, type }) => {
212
251
  return;
213
252
  }
214
253
  // Filter out parameters of the specified type
215
- operation.parameters = operation.parameters?.filter((it) => getResolvedRef(it)?.in !== type) ?? [];
254
+ operation.parameters =
255
+ operation.parameters?.filter((it) => getResolvedRef(it)?.in !== type && !(type === 'query' && getResolvedRef(it)?.in === 'querystring')) ?? [];
216
256
  };
@@ -1 +1 @@
1
- {"version":3,"file":"get-navigation-options.d.ts","sourceRoot":"","sources":["../../src/navigation/get-navigation-options.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,4BAA4B,EAAgC,MAAM,6BAA6B,CAAA;AAE7G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAG7D,MAAM,MAAM,iBAAiB,GACzB,OAAO,CACL,IAAI,CACF,4BAA4B,EAC1B,qBAAqB,GACrB,iBAAiB,GACjB,uBAAuB,GACvB,qBAAqB,GACrB,mBAAmB,GACnB,kBAAkB,GAClB,YAAY,GACZ,YAAY,GACZ,oBAAoB,GACpB,sBAAsB,CACzB,CACF,GACD,SAAS,CAAA;AAEb;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,cAAc,MAAM,EAAE,UAAU,iBAAiB,KAAG,mBAmJxF,CAAA"}
1
+ {"version":3,"file":"get-navigation-options.d.ts","sourceRoot":"","sources":["../../src/navigation/get-navigation-options.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,4BAA4B,EAAgC,MAAM,6BAA6B,CAAA;AAE7G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAe7D,MAAM,MAAM,iBAAiB,GACzB,OAAO,CACL,IAAI,CACF,4BAA4B,EAC1B,qBAAqB,GACrB,iBAAiB,GACjB,uBAAuB,GACvB,qBAAqB,GACrB,mBAAmB,GACnB,kBAAkB,GAClB,YAAY,GACZ,YAAY,GACZ,oBAAoB,GACpB,sBAAsB,CACzB,CACF,GACD,SAAS,CAAA;AAEb;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,cAAc,MAAM,EAAE,UAAU,iBAAiB,KAAG,mBAyJxF,CAAA"}
@@ -1,5 +1,16 @@
1
+ import { isHttpMethod } from '@scalar/helpers/http/is-http-method';
1
2
  import { slugify } from '@scalar/helpers/string/slugify';
2
3
  import { DEFAULT_MODELS_SECTION_LABEL } from '@scalar/types/api-reference';
4
+ /**
5
+ * Fixed fields retain their existing links; authored variants need a distinct namespace.
6
+ * Extension methods retain exact case because HTTP method tokens are case-sensitive.
7
+ * Normalizing COPY and copy would collapse distinct operations onto the same anchor.
8
+ */
9
+ const getMethodId = (method) => isHttpMethod(method)
10
+ ? method === method.toLowerCase()
11
+ ? method.toUpperCase()
12
+ : `additionalOperations/${method}`
13
+ : method;
3
14
  /**
4
15
  * Returns options for traversing an OpenAPI document, allowing customization of
5
16
  * how IDs and slugs are generated for tags, headings, models, operations, and webhooks.
@@ -65,11 +76,13 @@ export const getNavigationOptions = (documentName, options) => {
65
76
  return `${prefixTag}${options.generateOperationSlug({
66
77
  path: props.path,
67
78
  operationId: props.operation.operationId,
68
- method: props.method.toUpperCase(),
79
+ method: isHttpMethod(props.method) && props.method === props.method.toLowerCase()
80
+ ? props.method.toUpperCase()
81
+ : props.method,
69
82
  summary: props.operation.summary,
70
83
  })}`;
71
84
  }
72
- return `${prefixTag}${props.method.toUpperCase()}${props.path}`;
85
+ return `${prefixTag}${getMethodId(props.method)}${props.path}`;
73
86
  }
74
87
  // -------- Default webhook id generation logic --------
75
88
  if (props.type === 'webhook') {
@@ -83,13 +96,15 @@ export const getNavigationOptions = (documentName, options) => {
83
96
  if (options?.generateWebhookSlug) {
84
97
  return `${prefixTag}webhook/${options.generateWebhookSlug({
85
98
  name: props.name,
86
- method: props.method?.toUpperCase(),
99
+ method: isHttpMethod(props.method) && props.method === props.method.toLowerCase()
100
+ ? props.method.toUpperCase()
101
+ : props.method,
87
102
  })}`;
88
103
  }
89
104
  // Webhook events are commonly named with dots (e.g. "account_holder.created").
90
105
  // Keep the dot so the deep link stays close to the event name, instead of
91
106
  // dropping it and joining adjacent words ("account-holdercreated").
92
- return `${prefixTag}webhook/${props.method?.toUpperCase()}/${slugify(props.name, { allowedSpecialChars: '.' })}`;
107
+ return `${prefixTag}webhook/${getMethodId(props.method)}/${slugify(props.name, { allowedSpecialChars: '.' })}`;
93
108
  }
94
109
  // -------- Default model id generation logic --------
95
110
  if (props.type === 'model') {
@@ -1 +1 @@
1
- {"version":3,"file":"traverse-paths.d.ts","sourceRoot":"","sources":["../../../src/navigation/helpers/traverse-paths.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAE5F,OAAO,KAAK,EAA+B,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AAI9F,eAAO,MAAM,qBAAqB,GAAI,WAAW,eAAe,YAE/D,CAAA;AAsED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,GAAI,sEAM3B;IACD,QAAQ,EAAE,eAAe,CAAA;IACzB,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,0DAA0D;IAC1D,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAA;IAC7C,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;CAC5C,KAAG;IAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAA;CA0D7C,CAAA"}
1
+ {"version":3,"file":"traverse-paths.d.ts","sourceRoot":"","sources":["../../../src/navigation/helpers/traverse-paths.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAE5F,OAAO,KAAK,EAA+B,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AAI9F,eAAO,MAAM,qBAAqB,GAAI,WAAW,eAAe,YAE/D,CAAA;AAsED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,GAAI,sEAM3B;IACD,QAAQ,EAAE,eAAe,CAAA;IACzB,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,0DAA0D;IAC1D,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAA;IAC7C,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;CAC5C,KAAG;IAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAA;CA0D7C,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { escapeJsonPointer } from '@scalar/json-magic/helpers/escape-json-pointer';
2
- import { forEachPathItemOperation } from '../../helpers/for-each-path-item-operation.js';
2
+ import { forEachPathItemOperation, getPathItemOperationKey } from '../../helpers/for-each-path-item-operation.js';
3
3
  import { getResolvedRef, mergeSiblingReferences } from '../../helpers/get-resolved-ref.js';
4
4
  import { isHidden } from '../../helpers/is-hidden.js';
5
5
  import { traverseOperationExamples } from '../../navigation/helpers/traverse-examples.js';
@@ -83,7 +83,7 @@ export const traversePaths = ({ document, tagsMap, generateId, documentId, opera
83
83
  if (isHidden(operation)) {
84
84
  return;
85
85
  }
86
- const ref = `#/paths/${escapeJsonPointer(path)}/${method}`;
86
+ const ref = `#/paths/${escapeJsonPointer(path)}/${getPathItemOperationKey(method)}`;
87
87
  // Traverse tags
88
88
  if (operation.tags?.length) {
89
89
  operation.tags.forEach((tagName) => {
@@ -1 +1 @@
1
- {"version":3,"file":"traverse-webhooks.d.ts","sourceRoot":"","sources":["../../../src/navigation/helpers/traverse-webhooks.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvE,OAAO,KAAK,EAAE,eAAe,EAA8B,MAAM,wCAAwC,CAAA;AA2DzG;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GAAI,0EAM9B;IACD,uBAAuB;IACvB,QAAQ,EAAE,eAAe,CAAA;IACzB,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAA;IAC7C,wBAAwB,EAAE,MAAM,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;CACnB,KAAG,gBAAgB,EAuDnB,CAAA"}
1
+ {"version":3,"file":"traverse-webhooks.d.ts","sourceRoot":"","sources":["../../../src/navigation/helpers/traverse-webhooks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvE,OAAO,KAAK,EAAE,eAAe,EAA8B,MAAM,wCAAwC,CAAA;AA2DzG;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GAAI,0EAM9B;IACD,uBAAuB;IACvB,QAAQ,EAAE,eAAe,CAAA;IACzB,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAA;IAC7C,wBAAwB,EAAE,MAAM,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;CACnB,KAAG,gBAAgB,EAuDnB,CAAA"}
@@ -1,4 +1,4 @@
1
- import { forEachPathItemOperation } from '../../helpers/for-each-path-item-operation.js';
1
+ import { forEachPathItemOperation, getPathItemOperationKey } from '../../helpers/for-each-path-item-operation.js';
2
2
  import { getResolvedRef, mergeSiblingReferences } from '../../helpers/get-resolved-ref.js';
3
3
  import { isHidden } from '../../helpers/is-hidden.js';
4
4
  import { isDeprecatedOperation } from '../../navigation/helpers/traverse-paths.js';
@@ -62,7 +62,7 @@ export const traverseWebhooks = ({ document, tagsMap, generateId, untaggedWebhoo
62
62
  if (isHidden(operation)) {
63
63
  return;
64
64
  }
65
- const ref = `#/webhooks/${name}/${method}`;
65
+ const ref = `#/webhooks/${name}/${getPathItemOperationKey(method)}`;
66
66
  if (operation.tags?.length) {
67
67
  operation.tags.forEach((tagName) => {
68
68
  const { tag, id: tagId } = getTag({ tagsMap, name: tagName, documentId, generateId });
@@ -1,4 +1,3 @@
1
- import type { HttpMethod } from '@scalar/helpers/http/http-methods';
2
1
  import type { WorkspaceStore } from '../../client.js';
3
2
  import type { IdGenerator, TraversedOperation, TraversedTag, TraversedWebhook, WithParent } from '../../schemas/navigation.js';
4
3
  import type { TagObject } from '../../schemas/v3.2/strict/openapi-document.js';
@@ -10,7 +9,7 @@ type UpdateOrderIdParams = {
10
9
  /** Operation or webhook entries to update the order ID for */
11
10
  entries: (WithParent<TraversedOperation> | WithParent<TraversedWebhook>)[];
12
11
  operation: OperationObject;
13
- method: HttpMethod;
12
+ method: string;
14
13
  path: string;
15
14
  } | {
16
15
  /** Tag entries to update the order ID for */
@@ -1 +1 @@
1
- {"version":3,"file":"update-order-ids.d.ts","sourceRoot":"","sources":["../../../src/navigation/helpers/update-order-ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAA;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEvH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAA;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEtE,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,cAAc,CAAA;IACrB,UAAU,EAAE,WAAW,CAAA;CACxB,GAAG,CACA;IACE,8DAA8D;IAC9D,OAAO,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAA;IAC1E,SAAS,EAAE,eAAe,CAAA;IAC1B,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;CACb,GACD;IACE,6CAA6C;IAC7C,OAAO,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAA;IACnC,GAAG,EAAE,SAAS,CAAA;CACf,CACJ,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,gCAAgC,mBAAmB,SAiEjF,CAAA"}
1
+ {"version":3,"file":"update-order-ids.d.ts","sourceRoot":"","sources":["../../../src/navigation/helpers/update-order-ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEvH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAA;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEtE,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,cAAc,CAAA;IACrB,UAAU,EAAE,WAAW,CAAA;CACxB,GAAG,CACA;IACE,8DAA8D;IAC9D,OAAO,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAA;IAC1E,SAAS,EAAE,eAAe,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb,GACD;IACE,6CAA6C;IAC7C,OAAO,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAA;IACnC,GAAG,EAAE,SAAS,CAAA;CACf,CACJ,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,gCAAgC,mBAAmB,SAiEjF,CAAA"}
@@ -117,4 +117,5 @@ export declare const syncPathParameters: () => LifecyclePlugin;
117
117
  * These keys are used for internal purposes and are not needed in the final bundled document.
118
118
  */
119
119
  export declare const removeExtraScalarKeys: () => LifecyclePlugin;
120
+ export { openApiDocument, resolveOpenApiDocument } from './openapi-document.js';
120
121
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/bundler/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,KAAK,eAAe,EAAwB,MAAM,2BAA2B,CAAA;AAStF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,QAAO,eAahC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,KAAG,eA2CpE,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,QAAO,eAwCjC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,mBAAmB,QAAO,eAuBtC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,oBAAoB,QAAO,eAuBvC,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAAO,eAyBhC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,QAAO,eAkFrC,CAAA;AAKD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAO,eAaxC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/bundler/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,KAAK,eAAe,EAAwB,MAAM,2BAA2B,CAAA;AAStF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,QAAO,eAahC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,KAAG,eA2CpE,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,QAAO,eAwCjC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,mBAAmB,QAAO,eA6BtC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,oBAAoB,QAAO,eAuBvC,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAAO,eAyBhC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,QAAO,eAmFrC,CAAA;AAKD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAO,eAaxC,CAAA;AAED,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA"}
@@ -149,9 +149,15 @@ export const refsEverywhere = () => {
149
149
  export const restoreOriginalRefs = () => {
150
150
  return {
151
151
  type: 'lifecycle',
152
- onBeforeNodeProcess: (node, context) => {
152
+ onAfterNodeProcess: (node, context) => {
153
153
  const ref = node['$ref'];
154
154
  const root = context.rootNode;
155
+ const authoredRefs = root['x-scalar-original-refs'];
156
+ const authored = isObject(authoredRefs) ? authoredRefs[JSON.stringify(context.path)] : undefined;
157
+ if (isObject(authored) && typeof authored.original === 'string' && authored.rewritten === ref) {
158
+ node['$ref'] = authored.original;
159
+ return;
160
+ }
155
161
  const extUrls = root['x-ext-urls'];
156
162
  // Only process if $ref is a string and x-ext-urls is a valid object
157
163
  if (typeof ref !== 'string' || typeof extUrls !== 'object' || extUrls === null || !isLocalRef(ref)) {
@@ -267,8 +273,9 @@ export const syncPathParameters = () => {
267
273
  }
268
274
  const pathString = path[1];
269
275
  // Sync parameters for each operation method
270
- for (const method of HTTP_METHODS) {
271
- const operation = getResolvedRef(node[method], context);
276
+ const additionalOperations = isObject(node.additionalOperations) ? Object.values(node.additionalOperations) : [];
277
+ for (const operationNode of [...HTTP_METHODS.map((method) => node[method]), ...additionalOperations]) {
278
+ const operation = getResolvedRef(operationNode, context);
272
279
  if (!isObject(operation)) {
273
280
  continue;
274
281
  }
@@ -334,3 +341,4 @@ export const removeExtraScalarKeys = () => {
334
341
  },
335
342
  };
336
343
  };
344
+ export { openApiDocument, resolveOpenApiDocument } from './openapi-document.js';
@@ -0,0 +1,6 @@
1
+ import { type DocumentResolver, type LifecyclePlugin } from '@scalar/json-magic/bundle';
2
+ /** Resolves an OpenAPI document's declared identity against its retrieval location. */
3
+ export declare const resolveOpenApiDocument: DocumentResolver;
4
+ /** Honors `$self` for complete OpenAPI documents, including external and cached documents. */
5
+ export declare const openApiDocument: () => LifecyclePlugin;
6
+ //# sourceMappingURL=openapi-document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-document.d.ts","sourceRoot":"","sources":["../../../src/plugins/bundler/openapi-document.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAwB,MAAM,2BAA2B,CAAA;AAE7G,uFAAuF;AACvF,eAAO,MAAM,sBAAsB,EAAE,gBAcpC,CAAA;AAED,8FAA8F;AAC9F,eAAO,MAAM,eAAe,QAAO,eAsClC,CAAA"}
@@ -0,0 +1,57 @@
1
+ import { isObject } from '@scalar/helpers/object/is-object';
2
+ import { resolveReferencePath } from '@scalar/json-magic/bundle';
3
+ /** Resolves an OpenAPI document's declared identity against its retrieval location. */
4
+ export const resolveOpenApiDocument = (document, retrievalUri) => {
5
+ if (!isObject(document) ||
6
+ typeof document.openapi !== 'string' ||
7
+ !/^3\.2\.\d+$/.test(document.openapi) ||
8
+ typeof document.$self !== 'string') {
9
+ return undefined;
10
+ }
11
+ const self = resolveReferencePath(retrievalUri, document.$self);
12
+ // RFC 3986 requires a fragment-free base, while the declared identity can retain its fragment.
13
+ const fragmentIndex = self.indexOf('#');
14
+ const baseUri = fragmentIndex === -1 ? self : self.slice(0, fragmentIndex);
15
+ return { baseUri, metadata: { openapi: document.openapi, $self: self } };
16
+ };
17
+ /** Honors `$self` for complete OpenAPI documents, including external and cached documents. */
18
+ export const openApiDocument = () => {
19
+ const authoredRefs = new WeakMap();
20
+ const paths = new WeakMap();
21
+ const indexPaths = (value, path) => {
22
+ if (value === null || typeof value !== 'object' || paths.has(value))
23
+ return;
24
+ paths.set(value, path);
25
+ for (const [key, child] of Object.entries(value)) {
26
+ indexPaths(child, [...path, key]);
27
+ }
28
+ };
29
+ return {
30
+ type: 'lifecycle',
31
+ resolveDocument: resolveOpenApiDocument,
32
+ onBeforeNodeProcess: (node, context) => {
33
+ if (typeof node.$ref === 'string' && resolveOpenApiDocument(context.rootNode, context.origin)) {
34
+ // Partial bundles report paths relative to the selected subtree. Index the supplied root
35
+ // so the saved references can also be restored when exporting the entire document.
36
+ indexPaths(context.rootNode, []);
37
+ authoredRefs.set(node, node.$ref);
38
+ }
39
+ },
40
+ onAfterNodeProcess: (node, context) => {
41
+ const authored = authoredRefs.get(node);
42
+ if (authored === undefined || authored === node.$ref) {
43
+ return;
44
+ }
45
+ // A path-based map survives serialization and partial bundles without changing Reference Objects.
46
+ // Keep the first spelling, including fragments and ./, when a later pass rewrites it again.
47
+ const root = context.rootNode;
48
+ const existing = root['x-scalar-original-refs'];
49
+ const mapping = isObject(existing) ? existing : {};
50
+ const key = JSON.stringify(paths.get(node) ?? context.path);
51
+ const previous = mapping[key];
52
+ const original = isObject(previous) && previous.rewritten === authored ? previous.original : authored;
53
+ mapping[key] = { original, rewritten: node.$ref };
54
+ root['x-scalar-original-refs'] = mapping;
55
+ },
56
+ };
57
+ };
@@ -0,0 +1,29 @@
1
+ import type { MediaTypeObject, SchemaObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
+ /** Multipart parts stay structured until sending so nested text can resolve environment variables. */
3
+ export type MultipartPart = {
4
+ key?: string;
5
+ contentType?: string;
6
+ headers?: Record<string, string>;
7
+ } & ({
8
+ type: 'text';
9
+ value: string;
10
+ } | {
11
+ type: 'file';
12
+ value: File;
13
+ } | {
14
+ type: 'blob';
15
+ value: Blob;
16
+ } | {
17
+ type: 'multipart';
18
+ value: MultipartPart[];
19
+ contentType: string;
20
+ });
21
+ /** Identify ordered multipart independently of the editor's row representation. */
22
+ export declare const isPositionalMultipart: (contentType: string, media: MediaTypeObject) => boolean;
23
+ /** Whether a media type needs ordered or nested multipart serialization. */
24
+ export declare const needsMultipartEncoding: (contentType: string, media: MediaTypeObject) => boolean;
25
+ /** Resolve a positional item's schema independently of the encoding prefix length. */
26
+ export declare const getMultipartItemSchema: (schema: SchemaObject | undefined, index: number, itemSchema?: MediaTypeObject["itemSchema"]) => SchemaObject | undefined;
27
+ /** Build ordered parts, applying prefix encodings independently of schema prefix lengths. */
28
+ export declare const buildMultipart: (value: unknown, contentType: string, encoding?: Pick<MediaTypeObject, "encoding" | "prefixEncoding" | "itemEncoding" | "itemSchema">, schema?: SchemaObject, nesting?: number) => MultipartPart[];
29
+ //# sourceMappingURL=build-multipart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-multipart.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/build-multipart.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,eAAe,EACf,YAAY,EACb,MAAM,8DAA8D,CAAA;AAQrE,sGAAsG;AACtG,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC,GAAG,CACA;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CACrE,CAAA;AAED,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,GAAI,aAAa,MAAM,EAAE,OAAO,eAAe,KAAG,OASnF,CAAA;AAED,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,GAAI,aAAa,MAAM,EAAE,OAAO,eAAe,KAAG,OASpF,CAAA;AA4CD,sFAAsF;AACtF,eAAO,MAAM,sBAAsB,GACjC,QAAQ,YAAY,GAAG,SAAS,EAChC,OAAO,MAAM,EACb,aAAa,eAAe,CAAC,YAAY,CAAC,KACzC,YAAY,GAAG,SAMa,CAAA;AAc/B,6FAA6F;AAC7F,eAAO,MAAM,cAAc,GACzB,OAAO,OAAO,EACd,aAAa,MAAM,EACnB,WAAU,IAAI,CAAC,eAAe,EAAE,UAAU,GAAG,gBAAgB,GAAG,cAAc,GAAG,YAAY,CAAM,EACnG,SAAS,YAAY,EACrB,gBAAW,KACV,aAAa,EAkIf,CAAA"}
@@ -0,0 +1,191 @@
1
+ import { parseMimeType } from '@scalar/helpers/http/mime-type';
2
+ import { isObject } from '@scalar/helpers/object/is-object';
3
+ import { getResolvedRef, mergeSiblingReferences } from '@scalar/workspace-store/helpers/get-resolved-ref';
4
+ import { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy';
5
+ import { isArraySchema } from '@scalar/workspace-store/schemas/v3.2/strict/type-guards';
6
+ import { serializeXmlPart } from '../../xml/serialize-xml-part.js';
7
+ import { MAX_MULTIPART_NESTING } from './multipart-limits.js';
8
+ import { resolveLeafSchema } from './schema-value-coercion.js';
9
+ import { hasEncodingStyle, serializeFormPropertyWithEncoding } from './serialize-form-property.js';
10
+ /** Identify ordered multipart independently of the editor's row representation. */
11
+ export const isPositionalMultipart = (contentType, media) => {
12
+ const schema = getResolvedRef(media.schema);
13
+ return (parseMimeType(contentType).essence !== 'multipart/form-data' ||
14
+ media.prefixEncoding !== undefined ||
15
+ media.itemEncoding !== undefined ||
16
+ (schema !== undefined && isArraySchema(schema)) ||
17
+ media.itemSchema !== undefined);
18
+ };
19
+ /** Whether a media type needs ordered or nested multipart serialization. */
20
+ export const needsMultipartEncoding = (contentType, media) => {
21
+ const mime = parseMimeType(contentType);
22
+ return (mime.type === 'multipart' &&
23
+ (isPositionalMultipart(contentType, media) ||
24
+ Object.values(media.encoding ?? {}).some((encoding) => parseMimeType(encoding.contentType).type === 'multipart' || encoding.encoding !== undefined)));
25
+ };
26
+ const defaultContentType = (schema, value) => {
27
+ if (value instanceof Blob) {
28
+ return value.type || 'application/octet-stream';
29
+ }
30
+ // OpenAPI 3.2.1 Encoding Object defaults explicitly treat absent type as binary.
31
+ // With no schema at all, infer from the example so existing editor values remain usable.
32
+ if (schema && (!('type' in schema) || !schema.type || (schema.type === 'string' && schema.contentEncoding))) {
33
+ return 'application/octet-stream';
34
+ }
35
+ return typeof value === 'object' && value !== null ? 'application/json' : 'text/plain';
36
+ };
37
+ /** Encoding accepts ranges, but each wire header needs a single concrete media type. */
38
+ const selectContentType = (encoding, schema, value) => {
39
+ const fallback = defaultContentType(schema, value);
40
+ if (!encoding) {
41
+ return fallback;
42
+ }
43
+ if (/[\r\n\0]/.test(encoding)) {
44
+ throw new Error('Invalid multipart content type');
45
+ }
46
+ const choices = encoding.split(',').map((choice) => choice.trim());
47
+ const preferred = parseMimeType(fallback);
48
+ const match = choices.find((choice) => {
49
+ const mime = parseMimeType(choice);
50
+ return ((mime.type === '*' || mime.type === preferred.type) &&
51
+ (mime.subtype === '*' || mime.subtype === preferred.subtype));
52
+ });
53
+ if (match) {
54
+ const mime = parseMimeType(match);
55
+ if (!mime.essence.includes('*')) {
56
+ return match;
57
+ }
58
+ // Keep parameters from the range while replacing only its wildcard essence.
59
+ return match.replace(/^[^;]+/, preferred.essence);
60
+ }
61
+ // A nonmatching range cannot supply a concrete wire type; keep the inferred fallback.
62
+ return choices.find((choice) => !parseMimeType(choice).essence.includes('*')) ?? fallback;
63
+ };
64
+ /** Resolve a positional item's schema independently of the encoding prefix length. */
65
+ export const getMultipartItemSchema = (schema, index, itemSchema) => getResolvedRef((schema && 'prefixItems' in schema ? schema.prefixItems?.[index] : undefined) ??
66
+ (schema && 'items' in schema ? schema.items : undefined) ??
67
+ itemSchema, mergeSiblingReferences);
68
+ /** Serialize structured values to the selected part format; strings already containing XML stay intact. */
69
+ const serializePartValue = (value, contentType, schema) => {
70
+ const subtype = contentType ? parseMimeType(contentType).subtype : undefined;
71
+ if ((subtype === 'xml' || subtype?.endsWith('+xml')) && isObject(value)) {
72
+ return serializeXmlPart(value, schema);
73
+ }
74
+ const json = subtype === 'json' || subtype?.endsWith('+json');
75
+ return json || (value !== null && typeof value === 'object')
76
+ ? JSON.stringify(unpackProxyObject(value))
77
+ : String(value ?? '');
78
+ };
79
+ /** Build ordered parts, applying prefix encodings independently of schema prefix lengths. */
80
+ export const buildMultipart = (value, contentType, encoding = {}, schema, nesting = 0) => {
81
+ if (nesting >= MAX_MULTIPART_NESTING) {
82
+ throw new Error('Maximum multipart nesting exceeded');
83
+ }
84
+ const named = parseMimeType(contentType).essence === 'multipart/form-data';
85
+ const positional = Array.isArray(value);
86
+ const entries = positional
87
+ ? value.map((item, index) => {
88
+ const itemEncoding = index < (encoding.prefixEncoding?.length ?? 0) ? encoding.prefixEncoding?.[index] : encoding.itemEncoding;
89
+ const itemSchema = getMultipartItemSchema(schema, index, encoding.itemSchema);
90
+ if (named && item !== null && typeof item === 'object' && !Array.isArray(item)) {
91
+ // Each position describes one named part; accepting more keys would silently discard data.
92
+ const entries = Object.entries(item);
93
+ const entry = entries[0];
94
+ if (entries.length !== 1 || !entry) {
95
+ throw new Error('Named positional multipart items must contain exactly one property');
96
+ }
97
+ return [entry[0], entry[1], itemEncoding, resolveLeafSchema(itemSchema, [entry[0]])];
98
+ }
99
+ return [undefined, item, itemEncoding, itemSchema];
100
+ })
101
+ : value !== null && typeof value === 'object'
102
+ ? Object.entries(value).flatMap(([key, item]) => {
103
+ const propertySchema = resolveLeafSchema(schema, [key]);
104
+ return (Array.isArray(item) ? item : [item]).map((part) => [
105
+ key,
106
+ part,
107
+ encoding.encoding?.[key],
108
+ Array.isArray(item)
109
+ ? getResolvedRef(propertySchema && isArraySchema(propertySchema) ? propertySchema.items : undefined, mergeSiblingReferences)
110
+ : propertySchema,
111
+ ]);
112
+ })
113
+ : [];
114
+ return entries.flatMap(([key, item, partEncoding, partSchema]) => {
115
+ const style = named && hasEncodingStyle(partEncoding);
116
+ const styleParts = style ? serializeFormPropertyWithEncoding(key ?? '', item, partEncoding) : null;
117
+ const partContentType = style ? undefined : selectContentType(partEncoding?.contentType, partSchema, item);
118
+ const headers = Object.fromEntries(Object.entries(partEncoding?.headers ?? {}).flatMap(([name, ref]) => {
119
+ if (name.toLowerCase() === 'content-type') {
120
+ return [];
121
+ }
122
+ const header = getResolvedRef(ref);
123
+ if (!header || !('schema' in header)) {
124
+ return [];
125
+ }
126
+ const headerSchema = getResolvedRef(header.schema);
127
+ const example = header?.example ??
128
+ getResolvedRef(Object.values(header?.examples ?? {})[0])?.value ??
129
+ headerSchema?.const ??
130
+ headerSchema?.default;
131
+ return example === undefined ? [] : [[name, String(example)]];
132
+ }));
133
+ if (partSchema?.contentEncoding &&
134
+ !Object.keys(headers).some((name) => name.toLowerCase() === 'content-transfer-encoding')) {
135
+ headers['Content-Transfer-Encoding'] = partSchema.contentEncoding;
136
+ }
137
+ const metadata = {
138
+ ...(key === undefined ? {} : { key }),
139
+ ...(partContentType ? { contentType: partContentType } : {}),
140
+ ...(Object.keys(headers).length ? { headers } : {}),
141
+ };
142
+ if (styleParts) {
143
+ return styleParts.map((part) => ({ ...metadata, type: 'text', ...part }));
144
+ }
145
+ if (partContentType &&
146
+ parseMimeType(partContentType).type === 'multipart' &&
147
+ typeof item !== 'string' &&
148
+ !(item instanceof Blob)) {
149
+ return [
150
+ {
151
+ ...metadata,
152
+ type: 'multipart',
153
+ contentType: partContentType,
154
+ value: buildMultipart(item, partContentType, partEncoding, partSchema, nesting + 1),
155
+ },
156
+ ];
157
+ }
158
+ if (partContentType &&
159
+ parseMimeType(partContentType).essence === 'application/x-www-form-urlencoded' &&
160
+ item !== null &&
161
+ typeof item === 'object') {
162
+ const params = Object.entries(item).flatMap(([name, fieldValue]) => (Array.isArray(fieldValue) ? fieldValue : [fieldValue]).flatMap((field) => {
163
+ const fieldEncoding = partEncoding?.encoding?.[name];
164
+ const styled = serializeFormPropertyWithEncoding(name, field, fieldEncoding);
165
+ if (styled) {
166
+ return styled;
167
+ }
168
+ return [
169
+ {
170
+ key: name,
171
+ value: serializePartValue(field, hasEncodingStyle(fieldEncoding) ? undefined : fieldEncoding?.contentType),
172
+ },
173
+ ];
174
+ }));
175
+ return [
176
+ {
177
+ ...metadata,
178
+ type: 'text',
179
+ value: new URLSearchParams(params.map((part) => [part.key, part.value])).toString(),
180
+ },
181
+ ];
182
+ }
183
+ if (item instanceof File) {
184
+ return [{ ...metadata, type: 'file', value: unpackProxyObject(item) }];
185
+ }
186
+ if (item instanceof Blob) {
187
+ return [{ ...metadata, type: 'blob', value: unpackProxyObject(item) }];
188
+ }
189
+ return [{ ...metadata, type: 'text', value: serializePartValue(item, partContentType, partSchema) }];
190
+ });
191
+ };
@@ -1,4 +1,5 @@
1
1
  import type { RequestBodyObject } from '@scalar/workspace-store/schemas/v3.2/strict/request-body';
2
+ import { type MultipartPart } from './build-multipart.js';
2
3
  type FormData = {
3
4
  mode: 'formdata';
4
5
  value: ({
@@ -30,7 +31,11 @@ type Raw = {
30
31
  value: string | File | Blob;
31
32
  contentType?: string;
32
33
  };
33
- export type RequestBody = FormData | UrlEncoded | Raw;
34
+ export type RequestBody = FormData | UrlEncoded | Raw | {
35
+ mode: 'multipart';
36
+ contentType: string;
37
+ value: MultipartPart[];
38
+ };
34
39
  /**
35
40
  * Create the fetch request body
36
41
  */
@@ -1 +1 @@
1
- {"version":3,"file":"build-request-body.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/build-request-body.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0DAA0D,CAAA;AASjG,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,CACH;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,GACD;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,IAAI,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,GACD;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,IAAI,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CACJ,EAAE,CAAA;CACJ,CAAA;AAwBD,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;KACd,EAAE,CAAA;CACJ,CAAA;AAED,KAAK,GAAG,GAAG;IACT,IAAI,EAAE,KAAK,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,GAAG,CAAA;AAKrD;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,aAAa,iBAAiB,GAAG,SAAS;AAC1C,qCAAqC;AACrC,oBAAuB;AACvB,sEAAsE;AACtE,kCAAkC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvD,WAAW,GAAG,IAuUhB,CAAA"}
1
+ {"version":3,"file":"build-request-body.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/build-request-body.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0DAA0D,CAAA;AAKjG,OAAO,EACL,KAAK,aAAa,EAKnB,MAAM,mBAAmB,CAAA;AAO1B,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,CACH;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,GACD;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,IAAI,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,GACD;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,IAAI,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CACJ,EAAE,CAAA;CACJ,CAAA;AAwBD,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;KACd,EAAE,CAAA;CACJ,CAAA;AAED,KAAK,GAAG,GAAG;IACT,IAAI,EAAE,KAAK,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,UAAU,GACV,GAAG,GACH;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,CAAA;AA2CtE;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,aAAa,iBAAiB,GAAG,SAAS;AAC1C,qCAAqC;AACrC,oBAAuB;AACvB,sEAAsE;AACtE,kCAAkC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvD,WAAW,GAAG,IA8WhB,CAAA"}