@scalar/workspace-store 0.66.0 → 0.67.1

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 (49) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/entities/auth/schema.d.ts +22 -22
  3. package/dist/entities/auth/schema.d.ts.map +1 -1
  4. package/dist/entities/auth/schema.js +6 -4
  5. package/dist/events/definitions/operation.d.ts +2 -0
  6. package/dist/events/definitions/operation.d.ts.map +1 -1
  7. package/dist/helpers/encode-chunk-name.d.ts.map +1 -1
  8. package/dist/helpers/encode-chunk-name.js +6 -1
  9. package/dist/helpers/merge-object.d.ts.map +1 -1
  10. package/dist/helpers/merge-object.js +9 -3
  11. package/dist/request-example/builder/body/build-request-body.d.ts +1 -1
  12. package/dist/request-example/builder/body/build-request-body.d.ts.map +1 -1
  13. package/dist/request-example/builder/body/build-request-body.js +2 -2
  14. package/dist/request-example/builder/body/get-request-body-example.d.ts +4 -4
  15. package/dist/request-example/builder/body/get-request-body-example.d.ts.map +1 -1
  16. package/dist/request-example/builder/body/get-request-body-example.js +29 -5
  17. package/dist/request-example/builder/helpers/example-evaluation.d.ts +19 -0
  18. package/dist/request-example/builder/helpers/example-evaluation.d.ts.map +1 -0
  19. package/dist/request-example/builder/helpers/example-evaluation.js +2 -0
  20. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts +11 -3
  21. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
  22. package/dist/request-example/builder/helpers/get-example-from-schema.js +66 -17
  23. package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
  24. package/dist/request-example/builder/helpers/get-example.js +35 -21
  25. package/dist/request-example/builder/request-factory.d.ts +3 -1
  26. package/dist/request-example/builder/request-factory.d.ts.map +1 -1
  27. package/dist/request-example/builder/request-factory.js +2 -2
  28. package/dist/request-example/index.d.ts +4 -0
  29. package/dist/request-example/index.d.ts.map +1 -1
  30. package/dist/request-example/index.js +3 -0
  31. package/dist/request-example/xml/get-xml-body-example.d.ts +8 -0
  32. package/dist/request-example/xml/get-xml-body-example.d.ts.map +1 -0
  33. package/dist/request-example/xml/get-xml-body-example.js +21 -0
  34. package/dist/request-example/xml/get-xml-example.d.ts +21 -0
  35. package/dist/request-example/xml/get-xml-example.d.ts.map +1 -0
  36. package/dist/request-example/xml/get-xml-example.js +352 -0
  37. package/dist/request-example/xml/match-xml-property-pattern.d.ts +10 -0
  38. package/dist/request-example/xml/match-xml-property-pattern.d.ts.map +1 -0
  39. package/dist/request-example/xml/match-xml-property-pattern.js +48 -0
  40. package/dist/request-example/xml/serialize-xml-part.d.ts +4 -5
  41. package/dist/request-example/xml/serialize-xml-part.d.ts.map +1 -1
  42. package/dist/request-example/xml/serialize-xml-part.js +14 -8
  43. package/dist/request-example/xml/write-xml.d.ts +9 -0
  44. package/dist/request-example/xml/write-xml.d.ts.map +1 -0
  45. package/dist/request-example/xml/write-xml.js +119 -0
  46. package/dist/request-example/xml/xml-node.d.ts +32 -0
  47. package/dist/request-example/xml/xml-node.d.ts.map +1 -0
  48. package/dist/request-example/xml/xml-node.js +1 -0
  49. package/package.json +8 -8
@@ -0,0 +1,352 @@
1
+ import { unescapeJsonPointer } from '@scalar/helpers/json/unescape-json-pointer';
2
+ import { isObject } from '@scalar/helpers/object/is-object';
3
+ import { isDynamicRef, pushDynamicScope, resolveDynamicRef } from '../../helpers/dynamic-ref.js';
4
+ import { getResolvedRef, mergeSiblingReferences } from '../../helpers/get-resolved-ref.js';
5
+ import { EXAMPLE_EVALUATION, } from '../builder/helpers/example-evaluation.js';
6
+ import { getExampleFromSchema, selectExampleComposition, } from '../builder/helpers/get-example-from-schema.js';
7
+ import { matchXmlPropertyPattern } from './match-xml-property-pattern.js';
8
+ import { writeXml } from './write-xml.js';
9
+ const xsi = 'http://www.w3.org/2001/XMLSchema-instance';
10
+ /** Keep failures visible even when a consumer only reads the XML string. */
11
+ const reportXmlResult = (result, options) => {
12
+ for (const diagnostic of result.diagnostics) {
13
+ if (options.onDiagnostic) {
14
+ options.onDiagnostic(diagnostic);
15
+ }
16
+ else if (diagnostic.severity === 'error') {
17
+ console.warn('Unable to generate an XML example:', diagnostic);
18
+ }
19
+ }
20
+ return result;
21
+ };
22
+ /** Generate XML using the same value evaluator and selected branches as JSON examples. */
23
+ export const getXmlExampleFromSchema = (schema, options = {}) => {
24
+ const capture = { stack: [] };
25
+ const value = getExampleFromSchema(schema, { ...options, [EXAMPLE_EVALUATION]: capture }, { schemaPath: options.schemaPath });
26
+ return reportXmlResult(buildXmlExample(value, schema, options, capture.root), options);
27
+ };
28
+ /** Serialize schema-ready example data. Serialized XML strings bypass this function at the media boundary. */
29
+ export const serializeXmlExample = (value, schema, options = {}) => reportXmlResult(buildXmlExample(value, schema, options), options);
30
+ const buildXmlExample = (value, input, options, evaluation) => {
31
+ const diagnostics = [];
32
+ const report = (severity, code, message, path) => {
33
+ diagnostics.push({ severity, code, message, path });
34
+ };
35
+ const componentNameFromRef = (reference, path) => {
36
+ const name = reference?.match(/\/schemas\/([^/]+)$/)?.[1];
37
+ if (name === undefined) {
38
+ return undefined;
39
+ }
40
+ try {
41
+ return unescapeJsonPointer(name);
42
+ }
43
+ catch {
44
+ report('error', 'invalid-reference', 'The component reference contains invalid URI escaping.', path);
45
+ return undefined;
46
+ }
47
+ };
48
+ const mergeXml = (base, next, path) => {
49
+ if (!base) {
50
+ return next;
51
+ }
52
+ if (!next) {
53
+ return base;
54
+ }
55
+ for (const key of ['name', 'namespace', 'prefix', 'nodeType', 'attribute', 'wrapped']) {
56
+ if (base[key] !== undefined && next[key] !== undefined && base[key] !== next[key]) {
57
+ report('error', 'composition-xml-conflict', `Composition members disagree on xml.${key}.`, path);
58
+ }
59
+ }
60
+ return { ...base, ...next };
61
+ };
62
+ const version32 = options.openapiVersion !== undefined && /^3\.[2-9](?:\.|$)/.test(options.openapiVersion);
63
+ const validateXmlMetadata = (xml, path) => {
64
+ if (xml.namespace !== undefined && !/^[A-Za-z][A-Za-z0-9+.-]*:/.test(xml.namespace)) {
65
+ report('error', 'relative-namespace', 'XML namespaces must be non-relative IRIs.', path);
66
+ }
67
+ if (xml.nodeType !== undefined && (xml.attribute !== undefined || xml.wrapped !== undefined)) {
68
+ report('error', 'conflicting-node-type', 'xml.nodeType cannot be combined with xml.attribute or xml.wrapped.', path);
69
+ }
70
+ if (xml.nodeType && options.openapiVersion && !/^3\.[2-9](?:\.|$)/.test(options.openapiVersion)) {
71
+ report('warning', 'xml-version', 'xml.nodeType requires OpenAPI 3.2.', path);
72
+ }
73
+ };
74
+ // Flatten composition contributions, keeping the evaluator's property provenance and branch choices.
75
+ const shape = (source, context, seen = new Set()) => {
76
+ if (!source || typeof source !== 'object' || seen.has(source) || seen.size > 50) {
77
+ return { schema: {}, scope: context.scope, evaluations: [] };
78
+ }
79
+ seen.add(source);
80
+ if (!context.evaluation && '$ref' in source && !('$ref-value' in source && source['$ref-value'] !== undefined)) {
81
+ report('error', 'unresolved-reference', 'Resolve schema references before generating XML.', context.path);
82
+ }
83
+ if (version32 && '$ref-value' in source && source['$ref-value']) {
84
+ return { schema: source, scope: context.scope, evaluations: context.evaluation?.children ?? [] };
85
+ }
86
+ let schema = getResolvedRef(source, mergeSiblingReferences);
87
+ if (isDynamicRef(schema)) {
88
+ const bound = resolveDynamicRef(schema.$dynamicRef, context.scope);
89
+ if (bound) {
90
+ return shape(bound, context, seen);
91
+ }
92
+ }
93
+ const scope = pushDynamicScope(context.scope, schema);
94
+ const evaluations = [...(context.evaluation?.children ?? [])];
95
+ const contributions = context.evaluation?.children.filter((child) => child.path.length === context.path.length ||
96
+ (child.name === undefined &&
97
+ child.path.length === context.path.length + 1 &&
98
+ /^\d+$/.test(child.path.at(-1) ?? ''))) ?? [];
99
+ const members = [];
100
+ const selectedSource = selectExampleComposition(schema, context.path, options, context.path.at(-1) === 'items');
101
+ // Captured evaluations can retain a reference name after resolution. Check the
102
+ // original selected members so unresolved references do not become null examples.
103
+ for (const member of selectedSource ? [selectedSource] : (schema.allOf ?? [])) {
104
+ if (member && '$ref' in member && !('$ref-value' in member && member['$ref-value'] !== undefined)) {
105
+ report('error', 'unresolved-reference', 'Resolve schema references before generating XML.', context.path);
106
+ return { schema, scope, evaluations };
107
+ }
108
+ }
109
+ if (version32 && selectedSource) {
110
+ members.push({ schema: selectedSource, path: context.path, evaluation: contributions[0] });
111
+ }
112
+ else if (contributions.length && !(version32 && schema.allOf)) {
113
+ members.push(...contributions.map((child) => ({ schema: child.schema, path: child.path, evaluation: child })));
114
+ }
115
+ else {
116
+ const selected = selectExampleComposition(schema, context.path, options, context.path.at(-1) === 'items');
117
+ if (selected) {
118
+ members.push({ schema: selected, path: context.path });
119
+ }
120
+ else if (schema.allOf) {
121
+ let choice = 0;
122
+ for (const member of schema.allOf) {
123
+ const resolved = getResolvedRef(member, mergeSiblingReferences);
124
+ const isChoice = resolved && (resolved.oneOf || resolved.anyOf);
125
+ members.push({
126
+ schema: member,
127
+ path: isChoice ? [...context.path, String(choice++)] : context.path,
128
+ });
129
+ }
130
+ }
131
+ }
132
+ for (const member of members) {
133
+ const contribution = shape(member.schema, { ...context, scope, path: member.path, evaluation: member.evaluation }, new Set(seen));
134
+ const properties = 'properties' in schema ? schema.properties : undefined;
135
+ const extra = 'properties' in contribution.schema ? contribution.schema.properties : undefined;
136
+ const xml = mergeXml(schema.xml, contribution.schema.xml, context.path);
137
+ const combinedProperties = { ...properties, ...extra };
138
+ for (const key of Object.keys(properties ?? {})) {
139
+ if (properties?.[key] && extra?.[key]) {
140
+ combinedProperties[key] = { allOf: [properties[key], extra[key]] };
141
+ }
142
+ }
143
+ schema = {
144
+ ...schema,
145
+ ...contribution.schema,
146
+ ...(properties || extra ? { properties: combinedProperties } : {}),
147
+ xml,
148
+ };
149
+ evaluations.push(...contribution.evaluations);
150
+ }
151
+ return { schema, scope, evaluations };
152
+ };
153
+ const active = new Set();
154
+ let count = 0;
155
+ const map = (data, source, inheritedName, context, parentAttributes) => {
156
+ // Mapping receives original property/item schemas, before evaluator resolution can erase targets.
157
+ if (source && '$ref' in source && !('$ref-value' in source && source['$ref-value'] !== undefined)) {
158
+ report('error', 'unresolved-reference', 'Resolve schema references before generating XML.', context.path);
159
+ return [];
160
+ }
161
+ if (data === undefined) {
162
+ return [];
163
+ }
164
+ if (++count > 10000 || context.depth > 50) {
165
+ report('error', 'limit-exceeded', 'The XML example exceeds the node or depth limit.', context.path);
166
+ return [];
167
+ }
168
+ // In 3.2 a reference site and its target can describe different XML nodes.
169
+ // Resolving them into one object would erase an explicit wrapper or a transparent target.
170
+ const referenceTarget = source && '$ref-value' in source ? source['$ref-value'] : undefined;
171
+ const dynamicTarget = isDynamicRef(source) ? resolveDynamicRef(source.$dynamicRef, context.scope) : undefined;
172
+ const target = referenceTarget ?? dynamicTarget;
173
+ if (version32 && target && typeof target === 'object') {
174
+ const localXml = source.xml ?? {};
175
+ validateXmlMetadata(localXml, context.path);
176
+ const localKind = localXml.nodeType ?? (localXml.attribute ? 'attribute' : 'none');
177
+ const referenceName = '$ref' in source && typeof source.$ref === 'string'
178
+ ? componentNameFromRef(source.$ref, context.path)
179
+ : undefined;
180
+ const attributes = localKind === 'none' ? parentAttributes : [];
181
+ const children = map(data, target, referenceName ?? inheritedName, {
182
+ ...context,
183
+ depth: context.depth + 1,
184
+ scope: pushDynamicScope(context.scope, source),
185
+ evaluation: dynamicTarget ? context.evaluation?.children[0] : context.evaluation,
186
+ }, attributes);
187
+ if (localKind === 'none') {
188
+ return children;
189
+ }
190
+ if (localKind !== 'element') {
191
+ report('error', 'reference-node-type', 'A reference wrapper must be an element or a transparent node.', context.path);
192
+ return [];
193
+ }
194
+ return [
195
+ {
196
+ type: 'element',
197
+ name: localXml.name ?? inheritedName ?? 'root',
198
+ namespace: localXml.namespace,
199
+ prefix: localXml.prefix,
200
+ attributes,
201
+ children,
202
+ },
203
+ ];
204
+ }
205
+ const { schema, scope, evaluations } = shape(source, context);
206
+ if (version32 && '$ref-value' in schema && schema['$ref-value']) {
207
+ return map(data, schema, inheritedName, { ...context, scope, evaluation: undefined, depth: context.depth + 1 }, parentAttributes);
208
+ }
209
+ if (schema.deprecated ||
210
+ (options.mode === 'write' && schema.readOnly) ||
211
+ (options.mode === 'read' && schema.writeOnly)) {
212
+ return [];
213
+ }
214
+ const xml = schema.xml ?? {};
215
+ validateXmlMetadata(xml, context.path);
216
+ const kind = xml.nodeType ?? (xml.attribute ? 'attribute' : Array.isArray(data) && !xml.wrapped ? 'none' : 'element');
217
+ const selectedComponentName = context.depth === 0 && '$ref' in schema && typeof schema.$ref === 'string'
218
+ ? componentNameFromRef(schema.$ref, context.path)
219
+ : undefined;
220
+ const nodeName = kind === 'none' || kind === 'text' || kind === 'cdata'
221
+ ? inheritedName
222
+ : (xml.name ?? inheritedName ?? selectedComponentName);
223
+ const name = { name: nodeName ?? 'root', namespace: xml.namespace, prefix: xml.prefix };
224
+ if (kind === 'attribute') {
225
+ if (data === null) {
226
+ report('warning', 'null-attribute', 'A null XML attribute is omitted.', context.path);
227
+ }
228
+ else if (typeof data === 'object') {
229
+ report('error', 'attribute-value', 'An XML attribute requires a primitive value.', context.path);
230
+ }
231
+ else {
232
+ parentAttributes.push({ ...name, value: String(data) });
233
+ }
234
+ return [];
235
+ }
236
+ if (kind === 'text' || kind === 'cdata') {
237
+ if (data !== null && typeof data !== 'object') {
238
+ return [{ type: kind, value: String(data) }];
239
+ }
240
+ report('error', 'text-value', 'XML text and CDATA require a non-null primitive value.', context.path);
241
+ return [];
242
+ }
243
+ if (typeof data === 'object' && data !== null) {
244
+ if (active.has(data)) {
245
+ report('error', 'circular-value', 'The supplied XML example contains a circular value.', context.path);
246
+ return [];
247
+ }
248
+ active.add(data);
249
+ }
250
+ const attributes = kind === 'none' ? parentAttributes : [];
251
+ const children = [];
252
+ if (data === null) {
253
+ if (kind === 'none') {
254
+ report('error', 'null-fragment', 'A null value cannot be represented by an XML fragment.', context.path);
255
+ }
256
+ else {
257
+ attributes.push({ name: 'nil', prefix: 'xsi', namespace: xsi, value: 'true' });
258
+ }
259
+ }
260
+ else if (Array.isArray(data)) {
261
+ const prefixes = 'prefixItems' in schema ? schema.prefixItems : undefined;
262
+ const itemSchema = 'items' in schema && typeof schema.items === 'object' ? schema.items : {};
263
+ for (let index = 0; index < data.length; index++) {
264
+ if (count > 10000) {
265
+ break;
266
+ }
267
+ const itemPath = prefixes?.[index]
268
+ ? [...context.path, 'prefixItems', String(index)]
269
+ : [...context.path, 'items'];
270
+ const itemEvaluations = evaluations.filter((child) => child.path.join('.') === itemPath.join('.'));
271
+ const itemEvaluation = itemEvaluations.length === data.length ? itemEvaluations[index] : itemEvaluations[0];
272
+ children.push(...map(data[index], (prefixes?.[index] ?? itemSchema), inheritedName ?? nodeName, {
273
+ path: itemPath,
274
+ scope: itemEvaluation?.dynamicScope ?? scope,
275
+ evaluation: itemEvaluation,
276
+ depth: context.depth + 1,
277
+ }, attributes));
278
+ }
279
+ }
280
+ else if (isObject(data)) {
281
+ const properties = 'properties' in schema ? schema.properties : undefined;
282
+ const evaluationByName = new Map(evaluations.map((child) => [child.name ?? child.path.at(-1), child]));
283
+ for (const [key, childValue] of Object.entries(data)) {
284
+ if (count > 10000) {
285
+ break;
286
+ }
287
+ const childEvaluation = evaluationByName.get(key);
288
+ const additional = 'additionalProperties' in schema && typeof schema.additionalProperties === 'object'
289
+ ? schema.additionalProperties
290
+ : undefined;
291
+ const property = properties?.[key];
292
+ const patterns = 'patternProperties' in schema ? schema.patternProperties : undefined;
293
+ const matchingPatterns = Object.entries(patterns ?? {}).flatMap(([pattern, patternSchema]) => {
294
+ const matches = matchXmlPropertyPattern(pattern, key);
295
+ if (matches === undefined) {
296
+ report('error', 'unsupported-pattern', 'Property patterns must use the bounded XML matching subset.', [
297
+ ...context.path,
298
+ key,
299
+ ]);
300
+ }
301
+ return matches ? [patternSchema] : [];
302
+ });
303
+ const propertySchemas = [...(property ? [property] : []), ...matchingPatterns];
304
+ const childSchema = (propertySchemas.length > 1
305
+ ? { allOf: propertySchemas }
306
+ : (propertySchemas[0] ?? childEvaluation?.schema ?? additional ?? {}));
307
+ const resolved = getResolvedRef(childSchema, mergeSiblingReferences);
308
+ if (resolved?.deprecated ||
309
+ (options.mode === 'write' && resolved?.readOnly) ||
310
+ (options.mode === 'read' && resolved?.writeOnly)) {
311
+ continue;
312
+ }
313
+ children.push(...map(childValue, childSchema, key, {
314
+ path: [...context.path, key],
315
+ scope: childEvaluation?.dynamicScope ?? scope,
316
+ evaluation: childEvaluation,
317
+ depth: context.depth + 1,
318
+ }, attributes));
319
+ }
320
+ }
321
+ else {
322
+ children.push({ type: 'text', value: String(data) });
323
+ }
324
+ if (typeof data === 'object' && data !== null) {
325
+ active.delete(data);
326
+ }
327
+ return kind === 'none' ? children : [{ type: 'element', ...name, attributes, children }];
328
+ };
329
+ const reference = input && '$ref' in input && typeof input.$ref === 'string' ? input.$ref : undefined;
330
+ const componentName = componentNameFromRef(reference, options.schemaPath ?? []);
331
+ const rootName = options.rootName ?? componentName;
332
+ const rootAttributes = [];
333
+ const nodes = map(value, input, rootName, { path: options.schemaPath ?? [], scope: [], evaluation, depth: 0 }, rootAttributes);
334
+ if (rootAttributes.length) {
335
+ report('error', 'root-attribute', 'An XML attribute requires a parent element.', []);
336
+ }
337
+ if (!rootName &&
338
+ nodes.length === 1 &&
339
+ nodes[0]?.type === 'element' &&
340
+ nodes[0].name === 'root' &&
341
+ !input.xml?.name) {
342
+ report('warning', 'root-name-fallback', 'No XML root name was supplied; using root.', []);
343
+ }
344
+ if (diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
345
+ return { xml: undefined, diagnostics };
346
+ }
347
+ if (value === undefined) {
348
+ return { xml: undefined, diagnostics };
349
+ }
350
+ const result = writeXml(nodes, options);
351
+ return { xml: result.xml, diagnostics: [...diagnostics, ...result.diagnostics] };
352
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Match the bounded, non-branching subset needed by common property prefixes and character classes.
3
+ * JavaScript regexes cannot be interrupted, so arbitrary schema expressions must not run on the UI
4
+ * thread or mock-server event loop. Unsupported expressions are reported rather than guessed.
5
+ * With groups, alternation, backreferences, and multiple quantifiers excluded, repetitions
6
+ * cannot interact to create exponential backtracking. The single quantified atom may
7
+ * still backtrack or restart matching, so both pattern and input lengths are bounded.
8
+ */
9
+ export declare const matchXmlPropertyPattern: (pattern: string, name: string) => boolean | undefined;
10
+ //# sourceMappingURL=match-xml-property-pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match-xml-property-pattern.d.ts","sourceRoot":"","sources":["../../../src/request-example/xml/match-xml-property-pattern.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,GAAG,SAsCjF,CAAA"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Match the bounded, non-branching subset needed by common property prefixes and character classes.
3
+ * JavaScript regexes cannot be interrupted, so arbitrary schema expressions must not run on the UI
4
+ * thread or mock-server event loop. Unsupported expressions are reported rather than guessed.
5
+ * With groups, alternation, backreferences, and multiple quantifiers excluded, repetitions
6
+ * cannot interact to create exponential backtracking. The single quantified atom may
7
+ * still backtrack or restart matching, so both pattern and input lengths are bounded.
8
+ */
9
+ export const matchXmlPropertyPattern = (pattern, name) => {
10
+ if (pattern.length > 256 || name.length > 256) {
11
+ return undefined;
12
+ }
13
+ let inClass = false;
14
+ let repetitions = 0;
15
+ for (let index = 0; index < pattern.length; index++) {
16
+ const character = pattern[index];
17
+ if (character === '\\') {
18
+ const escaped = pattern[++index];
19
+ if (!escaped || (!inClass && /[1-9k]/.test(escaped))) {
20
+ return undefined;
21
+ }
22
+ continue;
23
+ }
24
+ if (character === '[' && !inClass) {
25
+ inClass = true;
26
+ continue;
27
+ }
28
+ if (character === ']' && inClass) {
29
+ inClass = false;
30
+ continue;
31
+ }
32
+ if (inClass) {
33
+ continue;
34
+ }
35
+ if ('(){}|'.includes(character ?? '')) {
36
+ return undefined;
37
+ }
38
+ if ('*+?'.includes(character ?? '') && ++repetitions > 1) {
39
+ return undefined;
40
+ }
41
+ }
42
+ try {
43
+ return new RegExp(pattern).test(name);
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ };
@@ -1,9 +1,8 @@
1
- import type { SchemaObject } from '../../schemas/v3.2/strict/openapi-document.js';
1
+ import { type SchemaObject } from '../../schemas/v3.2/strict/openapi-document.js';
2
2
  /**
3
- * Serialize structured XML part data through a single request boundary.
4
- * TODO: Delegate to the schema-aware XML serializer when it is integrated.
5
- * Preserve the legacy root-name contract at this boundary.
6
- * Already serialized XML strings bypass this adapter in the multipart builder.
3
+ * Serialize structured XML part data through the shared schema-aware writer.
4
+ * A part must contain a complete XML document, so mapping errors reject serialization.
5
+ * Already serialized strings bypass this boundary in request builders.
7
6
  */
8
7
  export declare const serializeXmlPart: (value: Record<string, unknown>, schema?: SchemaObject) => string;
9
8
  //# sourceMappingURL=serialize-xml-part.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serialize-xml-part.d.ts","sourceRoot":"","sources":["../../../src/request-example/xml/serialize-xml-part.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,YAAY,KAAG,MAGxF,CAAA"}
1
+ {"version":3,"file":"serialize-xml-part.d.ts","sourceRoot":"","sources":["../../../src/request-example/xml/serialize-xml-part.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,wCAAwC,CAAA;AAI9F;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,YAAY,KAAG,MASxF,CAAA"}
@@ -1,12 +1,18 @@
1
- import { json2xml } from '@scalar/helpers/file/json2xml';
2
- import { unpackProxyObject } from '../../helpers/unpack-proxy.js';
1
+ import { coerceValue } from '../../schemas/typebox-coerce.js';
2
+ import { SchemaObjectSchema } from '../../schemas/v3.2/strict/openapi-document.js';
3
+ import { serializeXmlExample } from './get-xml-example.js';
3
4
  /**
4
- * Serialize structured XML part data through a single request boundary.
5
- * TODO: Delegate to the schema-aware XML serializer when it is integrated.
6
- * Preserve the legacy root-name contract at this boundary.
7
- * Already serialized XML strings bypass this adapter in the multipart builder.
5
+ * Serialize structured XML part data through the shared schema-aware writer.
6
+ * A part must contain a complete XML document, so mapping errors reject serialization.
7
+ * Already serialized strings bypass this boundary in request builders.
8
8
  */
9
9
  export const serializeXmlPart = (value, schema) => {
10
- const rootName = schema?.xml?.name ?? 'root';
11
- return json2xml({ [rootName]: unpackProxyObject(value) });
10
+ const result = serializeXmlExample(value, schema ?? coerceValue(SchemaObjectSchema, {}), {
11
+ mode: 'write',
12
+ rootName: 'root',
13
+ });
14
+ if (result.xml === undefined) {
15
+ throw new Error(`Unable to serialize XML part: ${result.diagnostics.map(({ code }) => code).join(', ')}`);
16
+ }
17
+ return result.xml;
12
18
  };
@@ -0,0 +1,9 @@
1
+ import type { XmlExampleResult, XmlNode } from './xml-node.js';
2
+ /** Formatting is restricted to element-only content, where it cannot change text values. */
3
+ export type WriteXmlOptions = {
4
+ format?: boolean;
5
+ xmlDeclaration?: boolean;
6
+ };
7
+ /** Serialize an XML document, validating names, characters, and namespace bindings. */
8
+ export declare const writeXml: (nodes: XmlNode[], options?: WriteXmlOptions) => XmlExampleResult;
9
+ //# sourceMappingURL=write-xml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-xml.d.ts","sourceRoot":"","sources":["../../../src/request-example/xml/write-xml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,gBAAgB,EAAW,OAAO,EAAE,MAAM,YAAY,CAAA;AAUnF,4FAA4F;AAC5F,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAE5E,uFAAuF;AACvF,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,EAAE,EAAE,UAAS,eAAoB,KAAG,gBA4H1E,CAAA"}
@@ -0,0 +1,119 @@
1
+ const XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
2
+ const XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/';
3
+ // XML 1.0 (fifth edition) NCName: a QName's prefix and local part cannot contain colons.
4
+ const NAME_START = 'A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u{10000}-\\u{EFFFF}';
5
+ const namePattern = new RegExp(`^[${NAME_START}][${NAME_START}\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$`, 'u');
6
+ const invalidCharacter = /[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/u;
7
+ /** Serialize an XML document, validating names, characters, and namespace bindings. */
8
+ export const writeXml = (nodes, options = {}) => {
9
+ const diagnostics = [];
10
+ const fail = (code, message, path) => {
11
+ diagnostics.push({ severity: 'error', code, message, path });
12
+ };
13
+ const escape = (value, attribute, path) => {
14
+ if (invalidCharacter.test(value)) {
15
+ fail('invalid-character', 'The value contains a character XML 1.0 cannot represent.', path);
16
+ }
17
+ const escaped = value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\r/g, '&#13;');
18
+ // XML parsers normalize literal whitespace in attributes, so encode it explicitly.
19
+ return attribute ? escaped.replace(/"/g, '&quot;').replace(/\n/g, '&#10;').replace(/\t/g, '&#9;') : escaped;
20
+ };
21
+ let generatedPrefix = 0;
22
+ let count = 0;
23
+ const render = (node, inherited, depth, path, pretty) => {
24
+ if (++count > 10000 || depth > 100) {
25
+ fail('limit-exceeded', 'The XML example exceeds the node or depth limit.', path);
26
+ return '';
27
+ }
28
+ if (node.type !== 'element') {
29
+ if (node.type === 'text') {
30
+ return escape(node.value, false, path);
31
+ }
32
+ if (invalidCharacter.test(node.value)) {
33
+ fail('invalid-character', 'The CDATA contains a character XML 1.0 cannot represent.', path);
34
+ }
35
+ // Carriage returns must be outside CDATA to survive XML line-ending normalization.
36
+ return `<![CDATA[${node.value.replace(/]]>/g, ']]]]><![CDATA[>').replace(/\r/g, ']]>&#13;<![CDATA[')}]]>`;
37
+ }
38
+ const bindings = new Map(inherited);
39
+ const declarations = new Map();
40
+ const usedPrefixes = new Map();
41
+ const explicitPrefixes = new Set([node.prefix, ...node.attributes.map((attribute) => attribute.prefix)]);
42
+ const qualify = (input, attribute) => {
43
+ if (!namePattern.test(input.name) || (input.prefix !== undefined && !namePattern.test(input.prefix))) {
44
+ fail('invalid-name', `Invalid XML name: ${input.prefix ? `${input.prefix}:` : ''}${input.name}`, path);
45
+ }
46
+ let prefix = input.prefix ?? '';
47
+ const namespace = input.namespace ?? (prefix ? bindings.get(prefix) : attribute ? '' : bindings.get('')) ?? '';
48
+ if (prefix === 'xmlns' || namespace === XMLNS_NAMESPACE || (attribute && !prefix && input.name === 'xmlns')) {
49
+ fail('reserved-namespace', 'Namespace declarations must be expressed through XML namespace metadata.', path);
50
+ }
51
+ if (namespace === XML_NAMESPACE && !prefix) {
52
+ prefix = 'xml';
53
+ }
54
+ if ((prefix === 'xml') !== (namespace === XML_NAMESPACE)) {
55
+ fail('reserved-namespace', 'The xml prefix is reserved for the XML namespace.', path);
56
+ }
57
+ if (prefix && !namespace) {
58
+ fail('unbound-prefix', `The prefix ${prefix} has no namespace binding.`, path);
59
+ }
60
+ if (attribute && namespace && !prefix) {
61
+ prefix = [...bindings].find(([candidate, uri]) => candidate !== '' && uri === namespace)?.[0] ?? '';
62
+ if (!prefix) {
63
+ do {
64
+ prefix = `ns${++generatedPrefix}`;
65
+ } while (bindings.has(prefix) || explicitPrefixes.has(prefix));
66
+ }
67
+ }
68
+ if (!attribute || prefix) {
69
+ if (usedPrefixes.has(prefix) && usedPrefixes.get(prefix) !== namespace) {
70
+ fail('namespace-conflict', `The prefix ${prefix || '(default)'} is assigned incompatible namespaces on one element.`, path);
71
+ }
72
+ usedPrefixes.set(prefix, namespace);
73
+ if ((bindings.get(prefix) ?? '') !== namespace) {
74
+ declarations.set(prefix, namespace);
75
+ bindings.set(prefix, namespace);
76
+ }
77
+ }
78
+ return { name: prefix ? `${prefix}:${input.name}` : input.name, namespace };
79
+ };
80
+ const element = qualify(node, false);
81
+ const attributeNames = new Set();
82
+ const attributes = node.attributes
83
+ .map((attribute) => {
84
+ const qualified = qualify(attribute, true);
85
+ const key = `${qualified.namespace}\0${attribute.name}`;
86
+ if (attributeNames.has(key)) {
87
+ fail('duplicate-attribute', `Duplicate XML attribute: ${qualified.name}`, path);
88
+ }
89
+ attributeNames.add(key);
90
+ return ` ${qualified.name}="${escape(attribute.value, true, path)}"`;
91
+ })
92
+ .join('');
93
+ const namespaces = [...declarations]
94
+ .map(([prefix, uri]) => ` xmlns${prefix ? `:${prefix}` : ''}="${escape(uri, true, path)}"`)
95
+ .join('');
96
+ const opening = `<${element.name}${namespaces}${attributes}`;
97
+ if (node.children.length === 0) {
98
+ return `${opening}/>`;
99
+ }
100
+ const indentChildren = pretty && node.children.every((child) => child.type === 'element');
101
+ const children = node.children.map((child, index) => render(child, bindings, depth + 1, [...path, String(index)], pretty));
102
+ if (indentChildren) {
103
+ return `${opening}>\n${children.map((child) => `${' '.repeat(depth + 1)}${child}`).join('\n')}\n${' '.repeat(depth)}</${element.name}>`;
104
+ }
105
+ return `${opening}>${children.join('')}</${element.name}>`;
106
+ };
107
+ if (nodes.length !== 1 || nodes[0]?.type !== 'element') {
108
+ fail('invalid-root', 'An XML document must contain exactly one root element.', []);
109
+ return { xml: undefined, diagnostics };
110
+ }
111
+ const body = render(nodes[0], new Map([['xml', XML_NAMESPACE]]), 0, [], options.format !== false);
112
+ const declaration = options.xmlDeclaration === false
113
+ ? ''
114
+ : `<?xml version="1.0" encoding="UTF-8"?>${options.format === false ? '' : '\n'}`;
115
+ return {
116
+ xml: diagnostics.some((diagnostic) => diagnostic.severity === 'error') ? undefined : declaration + body,
117
+ diagnostics,
118
+ };
119
+ };
@@ -0,0 +1,32 @@
1
+ /** A qualified XML name. Namespace bindings are assigned by the writer. */
2
+ export type XmlName = {
3
+ name: string;
4
+ namespace?: string;
5
+ prefix?: string;
6
+ };
7
+ /** Attributes are separate from children so element and attribute names cannot collide. */
8
+ export type XmlAttribute = XmlName & {
9
+ value: string;
10
+ };
11
+ /** An ordered XML tree, including mixed content and repeated sibling names. */
12
+ export type XmlNode = (XmlName & {
13
+ type: 'element';
14
+ attributes: XmlAttribute[];
15
+ children: XmlNode[];
16
+ }) | {
17
+ type: 'text' | 'cdata';
18
+ value: string;
19
+ };
20
+ /** Actionable mapping errors and recoverable omissions, without console side effects. */
21
+ export type XmlDiagnostic = {
22
+ severity: 'warning' | 'error';
23
+ code: string;
24
+ message: string;
25
+ path: string[];
26
+ };
27
+ /** An error never carries a partial or malformed XML document. */
28
+ export type XmlExampleResult = {
29
+ xml: string | undefined;
30
+ diagnostics: XmlDiagnostic[];
31
+ };
32
+ //# sourceMappingURL=xml-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xml-node.d.ts","sourceRoot":"","sources":["../../../src/request-example/xml/xml-node.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,2FAA2F;AAC3F,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtD,+EAA+E;AAC/E,MAAM,MAAM,OAAO,GACf,CAAC,OAAO,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,YAAY,EAAE,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,GAChF;IAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE7C,yFAAyF;AACzF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;CACf,CAAA;AAED,kEAAkE;AAClE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IACvB,WAAW,EAAE,aAAa,EAAE,CAAA;CAC7B,CAAA"}
@@ -0,0 +1 @@
1
+ export {};