@daihum/record-formats 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { i as hasFormatErrors, n as formatFail, r as formatOk, t as createFormatIssue } from "./result-YrPe5I9D.js";
2
+
3
+ export { createFormatIssue, formatFail, formatOk, hasFormatErrors };
@@ -0,0 +1,52 @@
1
+ //#region src/result.d.ts
2
+ type FormatIssueSeverity = 'error' | 'warning';
3
+ type FormatSourceRange = Readonly<{
4
+ start?: number;
5
+ end?: number;
6
+ line?: number;
7
+ column?: number;
8
+ }>;
9
+ type FormatIssue = Readonly<{
10
+ severity: FormatIssueSeverity;
11
+ code: string;
12
+ message: string;
13
+ path?: string;
14
+ format?: string;
15
+ sourceRange?: FormatSourceRange;
16
+ cause?: unknown;
17
+ }>;
18
+ type FormatSuccess<T> = Readonly<{
19
+ ok: true;
20
+ value: T;
21
+ issues: readonly FormatIssue[];
22
+ }>;
23
+ type FormatFailure<T = never> = Readonly<{
24
+ ok: false;
25
+ issues: readonly FormatIssue[];
26
+ value?: T;
27
+ }>;
28
+ type FormatResult<T> = FormatSuccess<T> | FormatFailure<T>;
29
+ type ParseResult<T> = FormatResult<T>;
30
+ type SerializeResult<T> = FormatResult<T>;
31
+ type RecordFormatAdapter<TRecord, TInput = string, TOutput = string> = Readonly<{
32
+ format: string;
33
+ parse(input: TInput): ParseResult<TRecord>;
34
+ serialize(record: TRecord): SerializeResult<TOutput>;
35
+ validate(record: TRecord): readonly FormatIssue[];
36
+ detect?(input: TInput): boolean;
37
+ }>;
38
+ declare function createFormatIssue(params: {
39
+ severity: FormatIssueSeverity;
40
+ code: string;
41
+ message: string;
42
+ path?: string;
43
+ format?: string;
44
+ sourceRange?: FormatSourceRange;
45
+ cause?: unknown;
46
+ }): FormatIssue;
47
+ declare function formatOk<T>(value: T, issues?: readonly FormatIssue[]): FormatSuccess<T>;
48
+ declare function formatFail<T = never>(issues: readonly FormatIssue[], value?: T): FormatFailure<T>;
49
+ declare function hasFormatErrors(issues: readonly FormatIssue[]): boolean;
50
+ //#endregion
51
+ export { FormatSourceRange as a, RecordFormatAdapter as c, formatFail as d, formatOk as f, FormatResult as i, SerializeResult as l, FormatIssue as n, FormatSuccess as o, hasFormatErrors as p, FormatIssueSeverity as r, ParseResult as s, FormatFailure as t, createFormatIssue as u };
52
+ //# sourceMappingURL=result-Dd9q1uJU.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-Dd9q1uJU.d.ts","names":[],"sources":["../src/result.ts"],"sourcesContent":[],"mappings":";KAAY,mBAAA;AAAA,KAEA,iBAAA,GAAoB,QAFD,CAAA;EAEnB,KAAA,CAAA,EAAA,MAAA;EAOA,GAAA,CAAA,EAAA,MAAA;EACA,IAAA,CAAA,EAAA,MAAA;EAKI,MAAA,CAAA,EAAA,MAAA;CANU,CAAA;AAAQ,KAAtB,WAAA,GAAc,QAAQ,CAAA;EAUtB,QAAA,EATA,mBASa;EAEhB,IAAA,EAAA,MAAA;EACU,OAAA,EAAA,MAAA;EAHY,IAAA,CAAA,EAAA,MAAA;EAAQ,MAAA,CAAA,EAAA,MAAA;EAM3B,WAAA,CAAA,EAVI,iBAUS;EAEN,KAAA,CAAA,EAAA,OAAA;CACT,CAAA;AAH6B,KAN3B,aAM2B,CAAA,CAAA,CAAA,GANR,QAMQ,CAAA;EAAQ,EAAA,EAAA,IAAA;EAMnC,KAAA,EAVH,CAUG;EAAgC,MAAA,EAAA,SATzB,WASyB,EAAA;CAAd,CAAA;AAAiC,KANnD,aAMmD,CAAA,IAAA,KAAA,CAAA,GANxB,QAMwB,CAAA;EAAd,EAAA,EAAA,KAAA;EAAa,MAAA,EAAA,SAJ3C,WAI2C,EAAA;EAClD,KAAA,CAAA,EAJF,CAIE;AACZ,CAAA,CAAA;AAEY,KAJA,YAIA,CAAA,CAAmB,CAAA,GAJD,aAIC,CAJa,CAIb,CAAA,GAJkB,aAIlB,CAJgC,CAIhC,CAAA;AAEhB,KALH,WAKG,CAAA,CAAA,CAAA,GALc,YAKd,CAL2B,CAK3B,CAAA;AAAqB,KAJxB,eAIwB,CAAA,CAAA,CAAA,GAJH,YAIG,CAJU,CAIV,CAAA;AAAZ,KAFZ,mBAEY,CAAA,OAAA,EAAA,SAAA,MAAA,EAAA,UAAA,MAAA,CAAA,GAFsD,QAEtD,CAAA;EACJ,MAAA,EAAA,MAAA;EAA0B,KAAA,CAAA,KAAA,EAD/B,MAC+B,CAAA,EADtB,WACsB,CADV,OACU,CAAA;EAAhB,SAAA,CAAA,MAAA,EAAV,OAAU,CAAA,EAAA,eAAA,CAAgB,OAAhB,CAAA;EACX,QAAA,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,SAAmB,WAAnB,EAAA;EAAmB,MAAA,EAAA,KAAA,EACrB,MADqB,CAAA,EAAA,OAAA;CACrB,CAAA;AAL6D,iBAQ9D,iBAAA,CAR8D,MAAA,EAAA;EAAQ,QAAA,EAS1E,mBAT0E;EAQtE,IAAA,EAAA,MAAA;EACJ,OAAA,EAAA,MAAA;EAKI,IAAA,CAAA,EAAA,MAAA;EAEZ,MAAA,CAAA,EAAA,MAAA;EAAW,WAAA,CAAA,EAFC,iBAED;EAYC,KAAA,CAAA,EAAA,OAAQ;CAAW,CAAA,EAZ/B,WAY+B;AAAoB,iBAAvC,QAAuC,CAAA,CAAA,CAAA,CAAA,KAAA,EAApB,CAAoB,EAAA,MAAA,CAAA,EAAA,SAAA,WAAA,EAAA,CAAA,EAAqB,aAArB,CAAmC,CAAnC,CAAA;AAAmC,iBAI1E,UAJ0E,CAAA,IAAA,KAAA,CAAA,CAAA,MAAA,EAAA,SAKvE,WALuE,EAAA,EAAA,KAAA,CAAA,EAMhF,CANgF,CAAA,EAOvF,aAPuF,CAOzE,CAPyE,CAAA;AAAd,iBAe5D,eAAA,CAf4D,MAAA,EAAA,SAe3B,WAf2B,EAAA,CAAA,EAAA,OAAA"}
@@ -0,0 +1,33 @@
1
+ //#region src/result.ts
2
+ function createFormatIssue(params) {
3
+ return {
4
+ severity: params.severity,
5
+ code: params.code,
6
+ message: params.message,
7
+ ...params.path !== void 0 ? { path: params.path } : {},
8
+ ...params.format !== void 0 ? { format: params.format } : {},
9
+ ...params.sourceRange !== void 0 ? { sourceRange: params.sourceRange } : {},
10
+ ...params.cause !== void 0 ? { cause: params.cause } : {}
11
+ };
12
+ }
13
+ function formatOk(value, issues = []) {
14
+ return {
15
+ ok: true,
16
+ value,
17
+ issues
18
+ };
19
+ }
20
+ function formatFail(issues, value) {
21
+ return {
22
+ ok: false,
23
+ issues,
24
+ ...value !== void 0 ? { value } : {}
25
+ };
26
+ }
27
+ function hasFormatErrors(issues) {
28
+ return issues.some((issue) => issue.severity === "error");
29
+ }
30
+
31
+ //#endregion
32
+ export { hasFormatErrors as i, formatFail as n, formatOk as r, createFormatIssue as t };
33
+ //# sourceMappingURL=result-YrPe5I9D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-YrPe5I9D.js","names":[],"sources":["../src/result.ts"],"sourcesContent":["export type FormatIssueSeverity = 'error' | 'warning';\n\nexport type FormatSourceRange = Readonly<{\n start?: number;\n end?: number;\n line?: number;\n column?: number;\n}>;\n\nexport type FormatIssue = Readonly<{\n severity: FormatIssueSeverity;\n code: string;\n message: string;\n path?: string;\n format?: string;\n sourceRange?: FormatSourceRange;\n cause?: unknown;\n}>;\n\nexport type FormatSuccess<T> = Readonly<{\n ok: true;\n value: T;\n issues: readonly FormatIssue[];\n}>;\n\nexport type FormatFailure<T = never> = Readonly<{\n ok: false;\n issues: readonly FormatIssue[];\n value?: T;\n}>;\n\nexport type FormatResult<T> = FormatSuccess<T> | FormatFailure<T>;\nexport type ParseResult<T> = FormatResult<T>;\nexport type SerializeResult<T> = FormatResult<T>;\n\nexport type RecordFormatAdapter<TRecord, TInput = string, TOutput = string> = Readonly<{\n format: string;\n parse(input: TInput): ParseResult<TRecord>;\n serialize(record: TRecord): SerializeResult<TOutput>;\n validate(record: TRecord): readonly FormatIssue[];\n detect?(input: TInput): boolean;\n}>;\n\nexport function createFormatIssue(params: {\n severity: FormatIssueSeverity;\n code: string;\n message: string;\n path?: string;\n format?: string;\n sourceRange?: FormatSourceRange;\n cause?: unknown;\n}): FormatIssue {\n return {\n severity: params.severity,\n code: params.code,\n message: params.message,\n ...(params.path !== undefined ? { path: params.path } : {}),\n ...(params.format !== undefined ? { format: params.format } : {}),\n ...(params.sourceRange !== undefined ? { sourceRange: params.sourceRange } : {}),\n ...(params.cause !== undefined ? { cause: params.cause } : {}),\n };\n}\n\nexport function formatOk<T>(value: T, issues: readonly FormatIssue[] = []): FormatSuccess<T> {\n return { ok: true, value, issues };\n}\n\nexport function formatFail<T = never>(\n issues: readonly FormatIssue[],\n value?: T,\n): FormatFailure<T> {\n return {\n ok: false,\n issues,\n ...(value !== undefined ? { value } : {}),\n };\n}\n\nexport function hasFormatErrors(issues: readonly FormatIssue[]): boolean {\n return issues.some((issue) => issue.severity === 'error');\n}\n"],"mappings":";AA2CA,SAAgB,kBAAkB,QAQlB;AACd,QAAO;EACL,UAAU,OAAO;EACjB,MAAM,OAAO;EACb,SAAS,OAAO;EAChB,GAAI,OAAO,SAAS,SAAY,EAAE,MAAM,OAAO,MAAM,GAAG,EAAE;EAC1D,GAAI,OAAO,WAAW,SAAY,EAAE,QAAQ,OAAO,QAAQ,GAAG,EAAE;EAChE,GAAI,OAAO,gBAAgB,SAAY,EAAE,aAAa,OAAO,aAAa,GAAG,EAAE;EAC/E,GAAI,OAAO,UAAU,SAAY,EAAE,OAAO,OAAO,OAAO,GAAG,EAAE;EAC9D;;AAGH,SAAgB,SAAY,OAAU,SAAiC,EAAE,EAAoB;AAC3F,QAAO;EAAE,IAAI;EAAM;EAAO;EAAQ;;AAGpC,SAAgB,WACd,QACA,OACkB;AAClB,QAAO;EACL,IAAI;EACJ;EACA,GAAI,UAAU,SAAY,EAAE,OAAO,GAAG,EAAE;EACzC;;AAGH,SAAgB,gBAAgB,QAAyC;AACvE,QAAO,OAAO,MAAM,UAAU,MAAM,aAAa,QAAQ"}
@@ -0,0 +1,3 @@
1
+ import { _ as XmlParseOptions, a as getXmlText, c as parseXml, d as XmlAttributeMap, f as XmlBuildOptions, g as XmlObject, h as XmlNamespaceMap, i as getXmlChildren, l as splitXmlName, m as XmlName, n as buildXml, o as isXmlDocument, p as XmlDocument, r as findXmlValue, s as normalizeXmlName, t as asXmlArray, u as xmlPathToString } from "../index-90ZPmZ85.js";
2
+ import "../result-Dd9q1uJU.js";
3
+ export { XmlAttributeMap, XmlBuildOptions, XmlDocument, XmlName, XmlNamespaceMap, XmlObject, XmlParseOptions, asXmlArray, buildXml, findXmlValue, getXmlChildren, getXmlText, isXmlDocument, normalizeXmlName, parseXml, splitXmlName, xmlPathToString };
@@ -0,0 +1,4 @@
1
+ import "../result-YrPe5I9D.js";
2
+ import { a as getXmlText, c as parseXml, i as getXmlChildren, l as splitXmlName, n as buildXml, o as isXmlDocument, r as findXmlValue, s as normalizeXmlName, t as asXmlArray, u as xmlPathToString } from "../xml-6kdXnoBh.js";
3
+
4
+ export { asXmlArray, buildXml, findXmlValue, getXmlChildren, getXmlText, isXmlDocument, normalizeXmlName, parseXml, splitXmlName, xmlPathToString };
@@ -0,0 +1,149 @@
1
+ import { n as formatFail, r as formatOk, t as createFormatIssue } from "./result-YrPe5I9D.js";
2
+ import { XMLBuilder, XMLParser, XMLValidator } from "fast-xml-parser";
3
+
4
+ //#region src/xml/xml.ts
5
+ const XML_FORMAT_ID = "xml";
6
+ const ATTRIBUTE_PREFIX = "@_";
7
+ const TEXT_NODE_NAME = "#text";
8
+ function isRecord(value) {
9
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
10
+ }
11
+ function describeCause(cause) {
12
+ return cause instanceof Error ? cause.message : String(cause);
13
+ }
14
+ function createXmlIssue(params) {
15
+ const sourceRange = {
16
+ ...params.line !== void 0 ? { line: params.line } : {},
17
+ ...params.column !== void 0 ? { column: params.column } : {}
18
+ };
19
+ return createFormatIssue({
20
+ severity: "error",
21
+ code: params.code,
22
+ message: params.message,
23
+ format: XML_FORMAT_ID,
24
+ ...params.path !== void 0 ? { path: params.path } : {},
25
+ ...params.cause !== void 0 ? { cause: params.cause } : {},
26
+ ...Object.keys(sourceRange).length > 0 ? { sourceRange } : {}
27
+ });
28
+ }
29
+ function shouldArray(arrayTags, name, path) {
30
+ if (!arrayTags) return false;
31
+ if (typeof arrayTags === "function") return arrayTags(name, path);
32
+ return arrayTags.includes(name) || arrayTags.includes(normalizeXmlName(name));
33
+ }
34
+ function getRootEntry(parsed) {
35
+ for (const [name, value] of Object.entries(parsed)) {
36
+ if (name.startsWith("?")) continue;
37
+ if (isRecord(value)) return [name, value];
38
+ }
39
+ }
40
+ function parseXml(input, options = {}) {
41
+ const validation = XMLValidator.validate(input, { allowBooleanAttributes: options.preserveBooleanAttributes ?? true });
42
+ if (validation !== true) {
43
+ const err = isRecord(validation) && isRecord(validation.err) ? validation.err : {};
44
+ const line = typeof err.line === "number" ? err.line : void 0;
45
+ const column = typeof err.col === "number" ? err.col : void 0;
46
+ return formatFail([createXmlIssue({
47
+ code: "xml.parse.invalid",
48
+ message: typeof err.msg === "string" ? err.msg : "Invalid XML document.",
49
+ ...line !== void 0 ? { line } : {},
50
+ ...column !== void 0 ? { column } : {}
51
+ })]);
52
+ }
53
+ try {
54
+ const raw = new XMLParser({
55
+ ignoreAttributes: false,
56
+ attributeNamePrefix: ATTRIBUTE_PREFIX,
57
+ textNodeName: TEXT_NODE_NAME,
58
+ allowBooleanAttributes: options.preserveBooleanAttributes ?? true,
59
+ parseAttributeValue: false,
60
+ parseTagValue: false,
61
+ trimValues: options.trimValues ?? true,
62
+ ...options.arrayTags ? { isArray: (name, path) => shouldArray(options.arrayTags, name, path) } : {}
63
+ }).parse(input);
64
+ const rootEntry = getRootEntry(raw);
65
+ if (!rootEntry) return formatFail([createXmlIssue({
66
+ code: "xml.root.missing",
67
+ message: "XML document does not contain a root element."
68
+ })]);
69
+ const [rootName, root] = rootEntry;
70
+ return formatOk({
71
+ rootName,
72
+ root,
73
+ raw
74
+ });
75
+ } catch (cause) {
76
+ return formatFail([createXmlIssue({
77
+ code: "xml.parse.failed",
78
+ message: `XML parser failed: ${describeCause(cause)}`,
79
+ cause
80
+ })]);
81
+ }
82
+ }
83
+ function buildXml(document, options = {}) {
84
+ try {
85
+ const raw = isXmlDocument(document) ? document.raw : document;
86
+ return formatOk(new XMLBuilder({
87
+ ignoreAttributes: false,
88
+ attributeNamePrefix: ATTRIBUTE_PREFIX,
89
+ textNodeName: TEXT_NODE_NAME,
90
+ format: options.format ?? true,
91
+ suppressEmptyNode: options.suppressEmptyNode ?? true
92
+ }).build(raw));
93
+ } catch (cause) {
94
+ return formatFail([createXmlIssue({
95
+ code: "xml.build.failed",
96
+ message: `XML builder failed: ${describeCause(cause)}`,
97
+ cause
98
+ })]);
99
+ }
100
+ }
101
+ function isXmlDocument(value) {
102
+ return isRecord(value) && typeof value.rootName === "string" && isRecord(value.root) && isRecord(value.raw);
103
+ }
104
+ function splitXmlName(name) {
105
+ const isAttribute = name.startsWith(ATTRIBUTE_PREFIX);
106
+ const [maybePrefix, maybeLocalName] = (isAttribute ? name.slice(2) : name).split(":", 2);
107
+ const hasPrefix = maybeLocalName !== void 0;
108
+ return {
109
+ name,
110
+ localName: hasPrefix ? maybeLocalName : maybePrefix ?? "",
111
+ ...hasPrefix && maybePrefix !== void 0 ? { prefix: maybePrefix } : {},
112
+ isAttribute
113
+ };
114
+ }
115
+ function normalizeXmlName(name) {
116
+ return splitXmlName(name).localName;
117
+ }
118
+ function asXmlArray(value) {
119
+ if (value === void 0) return [];
120
+ if (Array.isArray(value)) return value;
121
+ return [value];
122
+ }
123
+ function findXmlValue(parent, localName) {
124
+ if (!isRecord(parent)) return void 0;
125
+ if (Object.prototype.hasOwnProperty.call(parent, localName)) return parent[localName];
126
+ for (const [key, value] of Object.entries(parent)) if (normalizeXmlName(key) === localName) return value;
127
+ }
128
+ function getXmlChildren(parent, localName) {
129
+ return asXmlArray(findXmlValue(parent, localName));
130
+ }
131
+ function getXmlText(value) {
132
+ if (typeof value === "string") {
133
+ const trimmed = value.trim();
134
+ return trimmed.length > 0 ? trimmed : void 0;
135
+ }
136
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
137
+ if (!isRecord(value)) return void 0;
138
+ return getXmlText(value[TEXT_NODE_NAME]);
139
+ }
140
+ function xmlPathToString(path) {
141
+ return path.reduce((current, segment) => {
142
+ if (typeof segment === "number") return `${current}[${segment}]`;
143
+ return current.length > 0 ? `${current}.${segment}` : segment;
144
+ }, "");
145
+ }
146
+
147
+ //#endregion
148
+ export { getXmlText as a, parseXml as c, getXmlChildren as i, splitXmlName as l, buildXml as n, isXmlDocument as o, findXmlValue as r, normalizeXmlName as s, asXmlArray as t, xmlPathToString as u };
149
+ //# sourceMappingURL=xml-6kdXnoBh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xml-6kdXnoBh.js","names":["err: Record<string, unknown>"],"sources":["../src/xml/xml.ts"],"sourcesContent":["import { XMLBuilder, XMLParser, XMLValidator } from 'fast-xml-parser';\nimport {\n createFormatIssue,\n formatFail,\n formatOk,\n type FormatIssue,\n type ParseResult,\n type SerializeResult,\n} from '../result.js';\nimport type {\n XmlBuildOptions,\n XmlDocument,\n XmlName,\n XmlObject,\n XmlParseOptions,\n} from './types.js';\n\nconst XML_FORMAT_ID = 'xml';\nconst ATTRIBUTE_PREFIX = '@_';\nconst TEXT_NODE_NAME = '#text';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction describeCause(cause: unknown): string {\n return cause instanceof Error ? cause.message : String(cause);\n}\n\nfunction createXmlIssue(params: {\n code: string;\n message: string;\n path?: string;\n cause?: unknown;\n line?: number;\n column?: number;\n}): FormatIssue {\n const sourceRange = {\n ...(params.line !== undefined ? { line: params.line } : {}),\n ...(params.column !== undefined ? { column: params.column } : {}),\n };\n\n return createFormatIssue({\n severity: 'error',\n code: params.code,\n message: params.message,\n format: XML_FORMAT_ID,\n ...(params.path !== undefined ? { path: params.path } : {}),\n ...(params.cause !== undefined ? { cause: params.cause } : {}),\n ...(Object.keys(sourceRange).length > 0 ? { sourceRange } : {}),\n });\n}\n\nfunction shouldArray(\n arrayTags: XmlParseOptions['arrayTags'],\n name: string,\n path: string,\n): boolean {\n if (!arrayTags) return false;\n if (typeof arrayTags === 'function') return arrayTags(name, path);\n return arrayTags.includes(name) || arrayTags.includes(normalizeXmlName(name));\n}\n\nfunction getRootEntry(parsed: Record<string, unknown>): [string, XmlObject] | undefined {\n for (const [name, value] of Object.entries(parsed)) {\n if (name.startsWith('?')) continue;\n if (isRecord(value)) return [name, value];\n }\n return undefined;\n}\n\nexport function parseXml(input: string, options: XmlParseOptions = {}): ParseResult<XmlDocument> {\n const validation = XMLValidator.validate(input, {\n allowBooleanAttributes: options.preserveBooleanAttributes ?? true,\n });\n\n if (validation !== true) {\n const err: Record<string, unknown> = isRecord(validation) && isRecord(validation.err)\n ? validation.err\n : {};\n const line = typeof err.line === 'number' ? err.line : undefined;\n const column = typeof err.col === 'number' ? err.col : undefined;\n const message = typeof err.msg === 'string' ? err.msg : 'Invalid XML document.';\n return formatFail([\n createXmlIssue({\n code: 'xml.parse.invalid',\n message,\n ...(line !== undefined ? { line } : {}),\n ...(column !== undefined ? { column } : {}),\n }),\n ]);\n }\n\n try {\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: ATTRIBUTE_PREFIX,\n textNodeName: TEXT_NODE_NAME,\n allowBooleanAttributes: options.preserveBooleanAttributes ?? true,\n parseAttributeValue: false,\n parseTagValue: false,\n trimValues: options.trimValues ?? true,\n ...(options.arrayTags\n ? {\n isArray: (name: string, path: string): boolean =>\n shouldArray(options.arrayTags, name, path),\n }\n : {}),\n });\n const raw = parser.parse(input) as Record<string, unknown>;\n const rootEntry = getRootEntry(raw);\n\n if (!rootEntry) {\n return formatFail([\n createXmlIssue({\n code: 'xml.root.missing',\n message: 'XML document does not contain a root element.',\n }),\n ]);\n }\n\n const [rootName, root] = rootEntry;\n return formatOk({ rootName, root, raw });\n } catch (cause) {\n return formatFail([\n createXmlIssue({\n code: 'xml.parse.failed',\n message: `XML parser failed: ${describeCause(cause)}`,\n cause,\n }),\n ]);\n }\n}\n\nexport function buildXml(\n document: XmlDocument | XmlObject,\n options: XmlBuildOptions = {},\n): SerializeResult<string> {\n try {\n const raw = isXmlDocument(document) ? document.raw : document;\n const builder = new XMLBuilder({\n ignoreAttributes: false,\n attributeNamePrefix: ATTRIBUTE_PREFIX,\n textNodeName: TEXT_NODE_NAME,\n format: options.format ?? true,\n suppressEmptyNode: options.suppressEmptyNode ?? true,\n });\n return formatOk(builder.build(raw));\n } catch (cause) {\n return formatFail([\n createXmlIssue({\n code: 'xml.build.failed',\n message: `XML builder failed: ${describeCause(cause)}`,\n cause,\n }),\n ]);\n }\n}\n\nexport function isXmlDocument(value: unknown): value is XmlDocument {\n return (\n isRecord(value) &&\n typeof value.rootName === 'string' &&\n isRecord(value.root) &&\n isRecord(value.raw)\n );\n}\n\nexport function splitXmlName(name: string): XmlName {\n const isAttribute = name.startsWith(ATTRIBUTE_PREFIX);\n const bareName = isAttribute ? name.slice(ATTRIBUTE_PREFIX.length) : name;\n const [maybePrefix, maybeLocalName] = bareName.split(':', 2);\n const hasPrefix = maybeLocalName !== undefined;\n const localName = hasPrefix ? maybeLocalName : maybePrefix ?? '';\n\n return {\n name,\n localName,\n ...(hasPrefix && maybePrefix !== undefined ? { prefix: maybePrefix } : {}),\n isAttribute,\n };\n}\n\nexport function normalizeXmlName(name: string): string {\n return splitXmlName(name).localName;\n}\n\nexport function asXmlArray<T>(value: T | readonly T[] | undefined): readonly T[] {\n if (value === undefined) return [];\n if (Array.isArray(value)) return value as readonly T[];\n return [value as T];\n}\n\nexport function findXmlValue(parent: unknown, localName: string): unknown {\n if (!isRecord(parent)) return undefined;\n if (Object.prototype.hasOwnProperty.call(parent, localName)) return parent[localName];\n\n for (const [key, value] of Object.entries(parent)) {\n if (normalizeXmlName(key) === localName) return value;\n }\n\n return undefined;\n}\n\nexport function getXmlChildren(parent: unknown, localName: string): readonly unknown[] {\n return asXmlArray(findXmlValue(parent, localName));\n}\n\nexport function getXmlText(value: unknown): string | undefined {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n if (!isRecord(value)) return undefined;\n return getXmlText(value[TEXT_NODE_NAME]);\n}\n\nexport function xmlPathToString(path: readonly (string | number)[]): string {\n return path.reduce<string>((current, segment) => {\n if (typeof segment === 'number') return `${current}[${segment}]`;\n return current.length > 0 ? `${current}.${segment}` : segment;\n }, '');\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@daihum/record-formats",
3
+ "version": "0.1.0",
4
+ "description": "Tree-shakeable adapters for scholarly, archival, library, and cultural record formats.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./xml": {
15
+ "types": "./dist/xml/index.d.ts",
16
+ "import": "./dist/xml/index.js"
17
+ },
18
+ "./ead3": {
19
+ "types": "./dist/ead3/index.d.ts",
20
+ "import": "./dist/ead3/index.js"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "keywords": [
30
+ "archives",
31
+ "ead3",
32
+ "library",
33
+ "metadata",
34
+ "records",
35
+ "scholarly-formats",
36
+ "xml"
37
+ ],
38
+ "author": "DAIHUM",
39
+ "license": "Apache-2.0",
40
+ "dependencies": {
41
+ "fast-xml-parser": "^4.5.0"
42
+ },
43
+ "devDependencies": {
44
+ "tsdown": "^0.16.6",
45
+ "typescript": "^5.9.3",
46
+ "vitest": "^2.1.8"
47
+ },
48
+ "engines": {
49
+ "node": ">=22.19.0"
50
+ },
51
+ "sideEffects": false,
52
+ "scripts": {
53
+ "build": "tsdown",
54
+ "clean": "rm -rf dist",
55
+ "test": "vitest run",
56
+ "test:exports": "pnpm build && node scripts/verify-exports.mjs",
57
+ "typecheck": "tsc --noEmit"
58
+ }
59
+ }