@airdraft/content 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/body.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Parse a Markdown string to sanitised HTML.
3
+ * Safe for use with `dangerouslySetInnerHTML` in React.
4
+ *
5
+ * The underlying parser is `marked`. Configure a custom renderer via
6
+ * the `render` prop on `<BodyField>` instead of tweaking this function.
7
+ */
8
+ export declare function parseMarkdown(text: string): string;
9
+ /**
10
+ * Split a plain `text` field value into paragraphs.
11
+ * Paragraphs are separated by blank lines (`\n\n`).
12
+ * Within each paragraph, single newlines are preserved (rendered as `<br>`).
13
+ */
14
+ export declare function parseText(text: string): string[];
15
+ //# sourceMappingURL=body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../src/body.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMhD"}
package/dist/body.js ADDED
@@ -0,0 +1,30 @@
1
+ import { marked } from 'marked';
2
+ import DOMPurify from 'isomorphic-dompurify';
3
+ /**
4
+ * Parse a Markdown string to sanitised HTML.
5
+ * Safe for use with `dangerouslySetInnerHTML` in React.
6
+ *
7
+ * The underlying parser is `marked`. Configure a custom renderer via
8
+ * the `render` prop on `<BodyField>` instead of tweaking this function.
9
+ */
10
+ export function parseMarkdown(text) {
11
+ if (!text)
12
+ return '';
13
+ // marked.parse is sync when not given async extensions
14
+ const raw = marked.parse(text, { async: false });
15
+ return DOMPurify.sanitize(raw);
16
+ }
17
+ /**
18
+ * Split a plain `text` field value into paragraphs.
19
+ * Paragraphs are separated by blank lines (`\n\n`).
20
+ * Within each paragraph, single newlines are preserved (rendered as `<br>`).
21
+ */
22
+ export function parseText(text) {
23
+ if (!text)
24
+ return [];
25
+ return text
26
+ .split(/\n{2,}/)
27
+ .map((p) => p.trim())
28
+ .filter(Boolean);
29
+ }
30
+ //# sourceMappingURL=body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"body.js","sourceRoot":"","sources":["../src/body.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,SAAS,MAAM,sBAAsB,CAAA;AAE5C;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IACpB,uDAAuD;IACvD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAW,CAAA;IAC1D,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;AAChC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IACpB,OAAO,IAAI;SACR,KAAK,CAAC,QAAQ,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAA;AACpB,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Resolve the URL for an image field.
3
+ *
4
+ * Resolution chain (first truthy wins):
5
+ * 1. data[`${fieldName}_media`]?.url — airdraft-managed media object URL
6
+ * 2. data[`${fieldName}_url`] — raw URL string stored alongside the key
7
+ * 3. data[fieldName] as string — raw key; prefixed with '/' if it looks relative
8
+ *
9
+ * @param fieldName - e.g. 'cover', 'thumbnail'
10
+ * @param data - entry data object
11
+ */
12
+ export declare function resolveImageUrl(fieldName: string, data: Record<string, unknown>): string | undefined;
13
+ /**
14
+ * Resolve dimensions for an image field from the airdraft media object,
15
+ * if available. Used by the Next.js `<AirdraftImage>` component to pass
16
+ * explicit width/height to `next/image`.
17
+ */
18
+ export declare function resolveImageDimensions(fieldName: string, data: Record<string, unknown>): {
19
+ width: number;
20
+ height: number;
21
+ } | undefined;
22
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,SAAS,CAwBpB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAS/C"}
package/dist/image.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Resolve the URL for an image field.
3
+ *
4
+ * Resolution chain (first truthy wins):
5
+ * 1. data[`${fieldName}_media`]?.url — airdraft-managed media object URL
6
+ * 2. data[`${fieldName}_url`] — raw URL string stored alongside the key
7
+ * 3. data[fieldName] as string — raw key; prefixed with '/' if it looks relative
8
+ *
9
+ * @param fieldName - e.g. 'cover', 'thumbnail'
10
+ * @param data - entry data object
11
+ */
12
+ export function resolveImageUrl(fieldName, data) {
13
+ // 1. Managed media object
14
+ const mediaObj = data[`${fieldName}_media`];
15
+ if (mediaObj && typeof mediaObj === 'object' && 'url' in mediaObj) {
16
+ const url = mediaObj['url'];
17
+ if (typeof url === 'string' && url)
18
+ return url;
19
+ }
20
+ // 2. Explicit URL field
21
+ const rawUrl = data[`${fieldName}_url`];
22
+ if (typeof rawUrl === 'string' && rawUrl)
23
+ return rawUrl;
24
+ // 3. Raw key / path stored in the field itself
25
+ const key = data[fieldName];
26
+ if (typeof key === 'string' && key) {
27
+ // If it's already an absolute URL or absolute path, return as-is
28
+ if (key.startsWith('http://') || key.startsWith('https://') || key.startsWith('/')) {
29
+ return key;
30
+ }
31
+ // Treat as a relative storage key — prefix with /
32
+ return `/${key}`;
33
+ }
34
+ return undefined;
35
+ }
36
+ /**
37
+ * Resolve dimensions for an image field from the airdraft media object,
38
+ * if available. Used by the Next.js `<AirdraftImage>` component to pass
39
+ * explicit width/height to `next/image`.
40
+ */
41
+ export function resolveImageDimensions(fieldName, data) {
42
+ const mediaObj = data[`${fieldName}_media`];
43
+ if (mediaObj && typeof mediaObj === 'object') {
44
+ const m = mediaObj;
45
+ if (typeof m['width'] === 'number' && typeof m['height'] === 'number') {
46
+ return { width: m['width'], height: m['height'] };
47
+ }
48
+ }
49
+ return undefined;
50
+ }
51
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAiB,EACjB,IAA6B;IAE7B,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,SAAS,QAAQ,CAAC,CAAA;IAC3C,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC;QAClE,MAAM,GAAG,GAAI,QAAoC,CAAC,KAAK,CAAC,CAAA;QACxD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG;YAAE,OAAO,GAAG,CAAA;IAChD,CAAC;IAED,wBAAwB;IACxB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,SAAS,MAAM,CAAC,CAAA;IACvC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM;QAAE,OAAO,MAAM,CAAA;IAEvD,+CAA+C;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAA;IAC3B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,EAAE,CAAC;QACnC,iEAAiE;QACjE,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACnF,OAAO,GAAG,CAAA;QACZ,CAAC;QACD,kDAAkD;QAClD,OAAO,IAAI,GAAG,EAAE,CAAA;IAClB,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAiB,EACjB,IAA6B;IAE7B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,SAAS,QAAQ,CAAC,CAAA;IAC3C,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC7C,MAAM,CAAC,GAAG,QAAmC,CAAA;QAC7C,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;YACtE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAA;QACnD,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { resolveImageUrl, resolveImageDimensions } from './image.js';
2
+ export { parseMarkdown, parseText } from './body.js';
3
+ export { resolveRelation, resolveRelations } from './relation.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AACpE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { resolveImageUrl, resolveImageDimensions } from './image.js';
2
+ export { parseMarkdown, parseText } from './body.js';
3
+ export { resolveRelation, resolveRelations } from './relation.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AACpE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolve the display label for a single `relation` field.
3
+ *
4
+ * When the relation is expanded (i.e. an object with a `data` property),
5
+ * we attempt to derive a display label from common name fields.
6
+ * Falls back to the raw slug string.
7
+ *
8
+ * @param fieldName - e.g. 'author', 'category'
9
+ * @param data - entry data object
10
+ * @returns display label, or null if the field is absent
11
+ */
12
+ export declare function resolveRelation(fieldName: string, data: Record<string, unknown>): string | null;
13
+ /**
14
+ * Resolve display labels for a `relations` (multi-relation) field.
15
+ *
16
+ * @param fieldName - e.g. 'authors', 'tags'
17
+ * @param data - entry data object
18
+ * @returns array of display labels (empty array if field is absent)
19
+ */
20
+ export declare function resolveRelations(fieldName: string, data: Record<string, unknown>): string[];
21
+ //# sourceMappingURL=relation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation.d.ts","sourceRoot":"","sources":["../src/relation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAoBf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,EAAE,CAuBV"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Resolve the display label for a single `relation` field.
3
+ *
4
+ * When the relation is expanded (i.e. an object with a `data` property),
5
+ * we attempt to derive a display label from common name fields.
6
+ * Falls back to the raw slug string.
7
+ *
8
+ * @param fieldName - e.g. 'author', 'category'
9
+ * @param data - entry data object
10
+ * @returns display label, or null if the field is absent
11
+ */
12
+ export function resolveRelation(fieldName, data) {
13
+ const value = data[fieldName];
14
+ if (value === undefined || value === null)
15
+ return null;
16
+ // Expanded entry object
17
+ if (typeof value === 'object' && value !== null && 'data' in value) {
18
+ const entryData = value['data'];
19
+ if (entryData && typeof entryData === 'object') {
20
+ const d = entryData;
21
+ const label = d['name'] ?? d['title'] ?? d['label'] ?? d['slug'];
22
+ if (typeof label === 'string')
23
+ return label;
24
+ }
25
+ const slug = value['slug'];
26
+ if (typeof slug === 'string')
27
+ return slug;
28
+ }
29
+ // Raw slug string
30
+ if (typeof value === 'string')
31
+ return value;
32
+ return null;
33
+ }
34
+ /**
35
+ * Resolve display labels for a `relations` (multi-relation) field.
36
+ *
37
+ * @param fieldName - e.g. 'authors', 'tags'
38
+ * @param data - entry data object
39
+ * @returns array of display labels (empty array if field is absent)
40
+ */
41
+ export function resolveRelations(fieldName, data) {
42
+ const value = data[fieldName];
43
+ if (!Array.isArray(value))
44
+ return [];
45
+ return value.flatMap((item) => {
46
+ if (item === null || item === undefined)
47
+ return [];
48
+ // Expanded entry object
49
+ if (typeof item === 'object' && 'data' in item) {
50
+ const entryData = item['data'];
51
+ if (entryData && typeof entryData === 'object') {
52
+ const d = entryData;
53
+ const label = d['name'] ?? d['title'] ?? d['label'] ?? d['slug'];
54
+ if (typeof label === 'string')
55
+ return [label];
56
+ }
57
+ const slug = item['slug'];
58
+ if (typeof slug === 'string')
59
+ return [slug];
60
+ return [];
61
+ }
62
+ if (typeof item === 'string')
63
+ return [item];
64
+ return [];
65
+ });
66
+ }
67
+ //# sourceMappingURL=relation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation.js","sourceRoot":"","sources":["../src/relation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAiB,EACjB,IAA6B;IAE7B,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAA;IAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtD,wBAAwB;IACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;QACnE,MAAM,SAAS,GAAI,KAAiC,CAAC,MAAM,CAAC,CAAA;QAC5D,IAAI,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,SAAoC,CAAA;YAC9C,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;YAChE,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAA;QAC7C,CAAC;QACD,MAAM,IAAI,GAAI,KAAiC,CAAC,MAAM,CAAC,CAAA;QACvD,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;IAC3C,CAAC;IAED,kBAAkB;IAClB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAE3C,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,IAA6B;IAE7B,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAA;IAC7B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAEpC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,EAAE,CAAA;QAElD,wBAAwB;QACxB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YAC/C,MAAM,SAAS,GAAI,IAAgC,CAAC,MAAM,CAAC,CAAA;YAC3D,IAAI,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAC/C,MAAM,CAAC,GAAG,SAAoC,CAAA;gBAC9C,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;gBAChE,IAAI,OAAO,KAAK,KAAK,QAAQ;oBAAE,OAAO,CAAC,KAAK,CAAC,CAAA;YAC/C,CAAC;YACD,MAAM,IAAI,GAAI,IAAgC,CAAC,MAAM,CAAC,CAAA;YACtD,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,OAAO,CAAC,IAAI,CAAC,CAAA;YAC3C,OAAO,EAAE,CAAA;QACX,CAAC;QAED,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,CAAC,IAAI,CAAC,CAAA;QAC3C,OAAO,EAAE,CAAA;IACX,CAAC,CAAC,CAAA;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@airdraft/content",
3
+ "version": "0.1.0",
4
+ "description": "Airdraft framework-agnostic content rendering utilities — URL resolution, Markdown parsing, relation helpers",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["dist", "README.md", "CHANGELOG.md"],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsc --watch",
18
+ "clean": "rm -rf dist",
19
+ "typecheck": "tsc --noEmit",
20
+ "prepublishOnly": "npm run build",
21
+ "release": "standard-version",
22
+ "release:patch": "standard-version --release-as patch",
23
+ "release:minor": "standard-version --release-as minor",
24
+ "release:major": "standard-version --release-as major"
25
+ },
26
+ "publishConfig": { "access": "public" },
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "dompurify": "^3.2.6",
30
+ "isomorphic-dompurify": "^2.26.0",
31
+ "marked": "^12.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/dompurify": "^3.0.5",
35
+ "typescript": "^5.7.2"
36
+ }
37
+ }