@sdxc/atom 0.0.0-pre.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.
- package/LICENSE.md +21 -0
- package/README.md +245 -0
- package/dist/index.d.ts +231 -0
- package/dist/index.js +162 -0
- package/dist/lib/build-document.d.ts +17 -0
- package/dist/lib/build-document.js +195 -0
- package/dist/lib/clone.d.ts +42 -0
- package/dist/lib/clone.js +147 -0
- package/dist/lib/constants.d.ts +17 -0
- package/dist/lib/constants.js +17 -0
- package/dist/lib/content.d.ts +34 -0
- package/dist/lib/content.js +75 -0
- package/dist/lib/extensions.d.ts +23 -0
- package/dist/lib/extensions.js +42 -0
- package/dist/lib/namespaces.d.ts +44 -0
- package/dist/lib/namespaces.js +63 -0
- package/dist/lib/parse-feed.d.ts +20 -0
- package/dist/lib/parse-feed.js +431 -0
- package/dist/lib/text-construct.d.ts +33 -0
- package/dist/lib/text-construct.js +97 -0
- package/dist/lib/utils.d.ts +59 -0
- package/dist/lib/utils.js +99 -0
- package/dist/lib/validate-entry.d.ts +23 -0
- package/dist/lib/validate-entry.js +34 -0
- package/dist/lib/validate-feed.d.ts +15 -0
- package/dist/lib/validate-feed.js +23 -0
- package/dist/lib/xml-base.d.ts +36 -0
- package/dist/lib/xml-base.js +49 -0
- package/package.json +23 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for reading XML elements, splitting qualified names, and
|
|
3
|
+
* normalizing the package's scalar-or-array data shapes.
|
|
4
|
+
*
|
|
5
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
6
|
+
* @copyright Sergio Xalambrí 2026
|
|
7
|
+
*/
|
|
8
|
+
import type { XML } from "@sdxc/xml";
|
|
9
|
+
/**
|
|
10
|
+
* Returns the direct child elements of an XML element.
|
|
11
|
+
*
|
|
12
|
+
* @param element - The element whose child elements should be returned
|
|
13
|
+
* @returns The direct child elements in source order
|
|
14
|
+
*/
|
|
15
|
+
export declare function getChildElements(element: XML.Element): XML.Element[];
|
|
16
|
+
/**
|
|
17
|
+
* Reads the concatenated text content of one XML element.
|
|
18
|
+
*
|
|
19
|
+
* @param element - The element whose text content should be collected
|
|
20
|
+
* @returns The concatenated text content
|
|
21
|
+
*/
|
|
22
|
+
export declare function getElementText(element: XML.Element): string;
|
|
23
|
+
/**
|
|
24
|
+
* Splits the prefix off a qualified name.
|
|
25
|
+
*
|
|
26
|
+
* @param name - The qualified element or attribute name
|
|
27
|
+
* @returns The prefix, or an empty string for an unprefixed name
|
|
28
|
+
*/
|
|
29
|
+
export declare function prefixOf(name: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Strips the prefix from a qualified name, which is how this package compares
|
|
32
|
+
* element names: the XML layer resolves no namespaces, so the prefix a document
|
|
33
|
+
* binds Atom to carries no meaning on its own.
|
|
34
|
+
*
|
|
35
|
+
* @param name - The qualified element or attribute name
|
|
36
|
+
* @returns The name without its prefix
|
|
37
|
+
*/
|
|
38
|
+
export declare function localName(name: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Parses one optional numeric text value.
|
|
41
|
+
*
|
|
42
|
+
* @param value - The raw string to parse
|
|
43
|
+
* @returns The parsed number or `NaN`
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseOptionalNumber(value: string): number;
|
|
46
|
+
/**
|
|
47
|
+
* Collapses one-item arrays back to the package's scalar-friendly API shape.
|
|
48
|
+
*
|
|
49
|
+
* @param values - The values to collapse
|
|
50
|
+
* @returns One value or the full array
|
|
51
|
+
*/
|
|
52
|
+
export declare function collapseArray<T>(values: T[]): T | T[];
|
|
53
|
+
/**
|
|
54
|
+
* Normalizes undefined, scalar, and array values into a flat array.
|
|
55
|
+
*
|
|
56
|
+
* @param value - The value to normalize
|
|
57
|
+
* @returns A flat array representation
|
|
58
|
+
*/
|
|
59
|
+
export declare function normalizeArray<T>(value?: T | T[]): T[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for reading XML elements, splitting qualified names, and
|
|
3
|
+
* normalizing the package's scalar-or-array data shapes.
|
|
4
|
+
*
|
|
5
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
6
|
+
* @copyright Sergio Xalambrí 2026
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Returns the direct child elements of an XML element.
|
|
10
|
+
*
|
|
11
|
+
* @param element - The element whose child elements should be returned
|
|
12
|
+
* @returns The direct child elements in source order
|
|
13
|
+
*/
|
|
14
|
+
export function getChildElements(element) {
|
|
15
|
+
let children = [];
|
|
16
|
+
for (let child of element.children ?? []) {
|
|
17
|
+
if (typeof child === "string")
|
|
18
|
+
continue;
|
|
19
|
+
children.push(child);
|
|
20
|
+
}
|
|
21
|
+
return children;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Reads the concatenated text content of one XML element.
|
|
25
|
+
*
|
|
26
|
+
* @param element - The element whose text content should be collected
|
|
27
|
+
* @returns The concatenated text content
|
|
28
|
+
*/
|
|
29
|
+
export function getElementText(element) {
|
|
30
|
+
let content = "";
|
|
31
|
+
for (let child of element.children ?? []) {
|
|
32
|
+
if (typeof child !== "string")
|
|
33
|
+
continue;
|
|
34
|
+
content += child;
|
|
35
|
+
}
|
|
36
|
+
return content;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Splits the prefix off a qualified name.
|
|
40
|
+
*
|
|
41
|
+
* @param name - The qualified element or attribute name
|
|
42
|
+
* @returns The prefix, or an empty string for an unprefixed name
|
|
43
|
+
*/
|
|
44
|
+
export function prefixOf(name) {
|
|
45
|
+
let separator = name.indexOf(":");
|
|
46
|
+
if (separator === -1)
|
|
47
|
+
return "";
|
|
48
|
+
return name.slice(0, separator);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Strips the prefix from a qualified name, which is how this package compares
|
|
52
|
+
* element names: the XML layer resolves no namespaces, so the prefix a document
|
|
53
|
+
* binds Atom to carries no meaning on its own.
|
|
54
|
+
*
|
|
55
|
+
* @param name - The qualified element or attribute name
|
|
56
|
+
* @returns The name without its prefix
|
|
57
|
+
*/
|
|
58
|
+
export function localName(name) {
|
|
59
|
+
let separator = name.indexOf(":");
|
|
60
|
+
if (separator === -1)
|
|
61
|
+
return name;
|
|
62
|
+
return name.slice(separator + 1);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Parses one optional numeric text value.
|
|
66
|
+
*
|
|
67
|
+
* @param value - The raw string to parse
|
|
68
|
+
* @returns The parsed number or `NaN`
|
|
69
|
+
*/
|
|
70
|
+
export function parseOptionalNumber(value) {
|
|
71
|
+
let number = Number(value);
|
|
72
|
+
if (Number.isFinite(number))
|
|
73
|
+
return number;
|
|
74
|
+
return Number.NaN;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Collapses one-item arrays back to the package's scalar-friendly API shape.
|
|
78
|
+
*
|
|
79
|
+
* @param values - The values to collapse
|
|
80
|
+
* @returns One value or the full array
|
|
81
|
+
*/
|
|
82
|
+
export function collapseArray(values) {
|
|
83
|
+
if (values.length === 1)
|
|
84
|
+
return values[0];
|
|
85
|
+
return values;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Normalizes undefined, scalar, and array values into a flat array.
|
|
89
|
+
*
|
|
90
|
+
* @param value - The value to normalize
|
|
91
|
+
* @returns A flat array representation
|
|
92
|
+
*/
|
|
93
|
+
export function normalizeArray(value) {
|
|
94
|
+
if (value === undefined)
|
|
95
|
+
return [];
|
|
96
|
+
if (Array.isArray(value))
|
|
97
|
+
return value;
|
|
98
|
+
return [value];
|
|
99
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards the three elements RFC 4287 §4.1.2 requires of every entry, so an
|
|
3
|
+
* instance can never hold data that would serialize into an invalid document.
|
|
4
|
+
*
|
|
5
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
6
|
+
* @copyright Sergio Xalambrí 2026
|
|
7
|
+
*/
|
|
8
|
+
import type { Atom } from "../index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Reads the string behind either text construct form, so a caller testing for
|
|
11
|
+
* presence does not branch on which form the value took.
|
|
12
|
+
*
|
|
13
|
+
* @param text - The construct to read
|
|
14
|
+
* @returns The text, or an empty string when there is none
|
|
15
|
+
*/
|
|
16
|
+
export declare function readTextValue(text?: Atom.TextInput): string;
|
|
17
|
+
/**
|
|
18
|
+
* Checks that an entry carries an id, a title and an updated timestamp.
|
|
19
|
+
*
|
|
20
|
+
* @param entry - The entry to check
|
|
21
|
+
* @throws AtomParseError Naming the first required element that is missing
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateEntry(entry: Atom.Entry): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards the three elements RFC 4287 §4.1.2 requires of every entry, so an
|
|
3
|
+
* instance can never hold data that would serialize into an invalid document.
|
|
4
|
+
*
|
|
5
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
6
|
+
* @copyright Sergio Xalambrí 2026
|
|
7
|
+
*/
|
|
8
|
+
import { AtomParseError } from "../index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Reads the string behind either text construct form, so a caller testing for
|
|
11
|
+
* presence does not branch on which form the value took.
|
|
12
|
+
*
|
|
13
|
+
* @param text - The construct to read
|
|
14
|
+
* @returns The text, or an empty string when there is none
|
|
15
|
+
*/
|
|
16
|
+
export function readTextValue(text) {
|
|
17
|
+
if (text === undefined)
|
|
18
|
+
return "";
|
|
19
|
+
return typeof text === "string" ? text : text.value;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Checks that an entry carries an id, a title and an updated timestamp.
|
|
23
|
+
*
|
|
24
|
+
* @param entry - The entry to check
|
|
25
|
+
* @throws AtomParseError Naming the first required element that is missing
|
|
26
|
+
*/
|
|
27
|
+
export function validateEntry(entry) {
|
|
28
|
+
if (!entry.id)
|
|
29
|
+
throw new AtomParseError("Entry must include an id.");
|
|
30
|
+
if (!readTextValue(entry.title))
|
|
31
|
+
throw new AtomParseError("Entry must include a title.");
|
|
32
|
+
if (!entry.updated)
|
|
33
|
+
throw new AtomParseError("Entry must include an updated timestamp.");
|
|
34
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards the three elements RFC 4287 §4.1.1 requires of every feed, so an
|
|
3
|
+
* instance can never hold data that would serialize into an invalid document.
|
|
4
|
+
*
|
|
5
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
6
|
+
* @copyright Sergio Xalambrí 2026
|
|
7
|
+
*/
|
|
8
|
+
import type { Atom } from "../index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Checks that a feed carries an id, a title and an updated timestamp.
|
|
11
|
+
*
|
|
12
|
+
* @param feed - The feed to check
|
|
13
|
+
* @throws AtomParseError Naming the first required element that is missing
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateFeed(feed: Atom.Feed): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards the three elements RFC 4287 §4.1.1 requires of every feed, so an
|
|
3
|
+
* instance can never hold data that would serialize into an invalid document.
|
|
4
|
+
*
|
|
5
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
6
|
+
* @copyright Sergio Xalambrí 2026
|
|
7
|
+
*/
|
|
8
|
+
import { AtomParseError } from "../index.js";
|
|
9
|
+
import { readTextValue } from "./validate-entry.js";
|
|
10
|
+
/**
|
|
11
|
+
* Checks that a feed carries an id, a title and an updated timestamp.
|
|
12
|
+
*
|
|
13
|
+
* @param feed - The feed to check
|
|
14
|
+
* @throws AtomParseError Naming the first required element that is missing
|
|
15
|
+
*/
|
|
16
|
+
export function validateFeed(feed) {
|
|
17
|
+
if (!feed.id)
|
|
18
|
+
throw new AtomParseError("Feed must include an id.");
|
|
19
|
+
if (!readTextValue(feed.title))
|
|
20
|
+
throw new AtomParseError("Feed must include a title.");
|
|
21
|
+
if (!feed.updated)
|
|
22
|
+
throw new AtomParseError("Feed must include an updated timestamp.");
|
|
23
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks the `xml:base` and `xml:lang` in effect at a point in the tree, so a
|
|
3
|
+
* relative reference resolves against the bases that enclose it. RFC 4287 §4.1.1
|
|
4
|
+
* makes both inherited, and makes a relative base compose with the one above it.
|
|
5
|
+
*
|
|
6
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
7
|
+
* @copyright Sergio Xalambrí 2026
|
|
8
|
+
*/
|
|
9
|
+
import type { XML } from "@sdxc/xml";
|
|
10
|
+
/** The base URI and language inherited at one point in the document. */
|
|
11
|
+
export interface Scope {
|
|
12
|
+
base?: string;
|
|
13
|
+
lang?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Layers an element's own `xml:base` and `xml:lang` over the inherited scope. A
|
|
17
|
+
* relative `xml:base` is resolved against the enclosing one, so nested bases
|
|
18
|
+
* accumulate rather than replace.
|
|
19
|
+
*
|
|
20
|
+
* @param scope - The scope inherited from ancestors
|
|
21
|
+
* @param element - The element whose attributes should be layered on top
|
|
22
|
+
* @returns The scope in effect inside the element
|
|
23
|
+
*/
|
|
24
|
+
export declare function extendScope(scope: Scope, element: XML.Element): Scope;
|
|
25
|
+
/**
|
|
26
|
+
* Resolves a reference against the base in scope.
|
|
27
|
+
*
|
|
28
|
+
* The reference is returned unchanged when no base applies or when the pair does
|
|
29
|
+
* not form a URL, because a feed's own text is more useful to a consumer than a
|
|
30
|
+
* value this package invented or dropped.
|
|
31
|
+
*
|
|
32
|
+
* @param scope - The scope supplying the base URI
|
|
33
|
+
* @param reference - The possibly-relative reference to resolve
|
|
34
|
+
* @returns The absolute reference, or the original when it cannot be resolved
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveUri(scope: Scope, reference: string): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks the `xml:base` and `xml:lang` in effect at a point in the tree, so a
|
|
3
|
+
* relative reference resolves against the bases that enclose it. RFC 4287 §4.1.1
|
|
4
|
+
* makes both inherited, and makes a relative base compose with the one above it.
|
|
5
|
+
*
|
|
6
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
7
|
+
* @copyright Sergio Xalambrí 2026
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Layers an element's own `xml:base` and `xml:lang` over the inherited scope. A
|
|
11
|
+
* relative `xml:base` is resolved against the enclosing one, so nested bases
|
|
12
|
+
* accumulate rather than replace.
|
|
13
|
+
*
|
|
14
|
+
* @param scope - The scope inherited from ancestors
|
|
15
|
+
* @param element - The element whose attributes should be layered on top
|
|
16
|
+
* @returns The scope in effect inside the element
|
|
17
|
+
*/
|
|
18
|
+
export function extendScope(scope, element) {
|
|
19
|
+
let attributes = element.attributes ?? {};
|
|
20
|
+
let base = attributes["xml:base"];
|
|
21
|
+
let lang = attributes["xml:lang"];
|
|
22
|
+
if (base === undefined && lang === undefined)
|
|
23
|
+
return scope;
|
|
24
|
+
return {
|
|
25
|
+
base: base === undefined ? scope.base : resolveUri(scope, base),
|
|
26
|
+
lang: lang ?? scope.lang,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolves a reference against the base in scope.
|
|
31
|
+
*
|
|
32
|
+
* The reference is returned unchanged when no base applies or when the pair does
|
|
33
|
+
* not form a URL, because a feed's own text is more useful to a consumer than a
|
|
34
|
+
* value this package invented or dropped.
|
|
35
|
+
*
|
|
36
|
+
* @param scope - The scope supplying the base URI
|
|
37
|
+
* @param reference - The possibly-relative reference to resolve
|
|
38
|
+
* @returns The absolute reference, or the original when it cannot be resolved
|
|
39
|
+
*/
|
|
40
|
+
export function resolveUri(scope, reference) {
|
|
41
|
+
if (!scope.base)
|
|
42
|
+
return reference;
|
|
43
|
+
try {
|
|
44
|
+
return new URL(reference, scope.base).toString();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return reference;
|
|
48
|
+
}
|
|
49
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sdxc/atom",
|
|
3
|
+
"version": "0.0.0-pre.1",
|
|
4
|
+
"description": "Atom 1.0 feed parser and builder",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@sdxc/result": "2026.9.5",
|
|
12
|
+
"@sdxc/xml": "0.0.0-pre.1"
|
|
13
|
+
},
|
|
14
|
+
"gitHead": "6b352367c6853be4019c3a3ab761185df8ab5ab8",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/sergiodxa/monorepo.git",
|
|
21
|
+
"directory": "packages/atom"
|
|
22
|
+
}
|
|
23
|
+
}
|