trilium-api 1.0.0 → 1.0.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/.github/workflows/ci.yml +37 -37
- package/.github/workflows/publish.yml +84 -86
- package/LICENSE +660 -660
- package/README.md +835 -836
- package/package.json +15 -13
- package/src/client.test.ts +477 -477
- package/src/client.ts +91 -91
- package/src/demo-mapper.ts +166 -166
- package/src/demo-search.ts +108 -108
- package/src/demo.ts +126 -126
- package/src/index.ts +34 -34
- package/src/mapper.test.ts +638 -638
- package/src/mapper.ts +534 -534
- package/tsconfig.json +42 -42
package/src/client.ts
CHANGED
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trilium API Client using openapi-fetch
|
|
3
|
-
*
|
|
4
|
-
* This provides a type-safe client for the Trilium ETAPI.
|
|
5
|
-
* Types are auto-generated from the OpenAPI specification.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import createClient from 'openapi-fetch';
|
|
9
|
-
import type { paths, components } from './generated/trilium.js';
|
|
10
|
-
|
|
11
|
-
// Re-export common types for convenience
|
|
12
|
-
export type TriliumNote = components['schemas']['Note'];
|
|
13
|
-
export type TriliumBranch = components['schemas']['Branch'];
|
|
14
|
-
export type TriliumAttribute = components['schemas']['Attribute'];
|
|
15
|
-
export type TriliumAttachment = components['schemas']['Attachment'];
|
|
16
|
-
export type TriliumAppInfo = components['schemas']['AppInfo'];
|
|
17
|
-
|
|
18
|
-
// Export the paths type for advanced usage
|
|
19
|
-
export type { paths, components };
|
|
20
|
-
|
|
21
|
-
// Re-export mapper utilities
|
|
22
|
-
export {
|
|
23
|
-
TriliumMapper,
|
|
24
|
-
buildSearchQuery,
|
|
25
|
-
transforms,
|
|
26
|
-
type MappingConfig,
|
|
27
|
-
type FieldMapping,
|
|
28
|
-
type TransformFunction,
|
|
29
|
-
type ComputedFunction,
|
|
30
|
-
type TriliumSearchHelpers,
|
|
31
|
-
type TriliumSearchConditions,
|
|
32
|
-
type TriliumSearchLogical,
|
|
33
|
-
type ComparisonOperator,
|
|
34
|
-
type ConditionValue,
|
|
35
|
-
type SearchValue,
|
|
36
|
-
} from './mapper.js';
|
|
37
|
-
|
|
38
|
-
export interface TriliumClientConfig {
|
|
39
|
-
baseUrl: string;
|
|
40
|
-
apiKey: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Create a type-safe Trilium API client
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```ts
|
|
48
|
-
* const client = createTriliumClient({
|
|
49
|
-
* baseUrl: 'http://localhost:37840',
|
|
50
|
-
* apiKey: 'your-etapi-token'
|
|
51
|
-
* });
|
|
52
|
-
*
|
|
53
|
-
* // Get app info
|
|
54
|
-
* const { data, error } = await client.GET('/app-info');
|
|
55
|
-
*
|
|
56
|
-
* // Get a note by ID
|
|
57
|
-
* const { data: note } = await client.GET('/notes/{noteId}', {
|
|
58
|
-
* params: { path: { noteId: 'root' } }
|
|
59
|
-
* });
|
|
60
|
-
*
|
|
61
|
-
* // Create a note
|
|
62
|
-
* const { data: newNote } = await client.POST('/notes', {
|
|
63
|
-
* body: {
|
|
64
|
-
* parentNoteId: 'root',
|
|
65
|
-
* title: 'My Note',
|
|
66
|
-
* type: 'text',
|
|
67
|
-
* content: '<p>Hello World</p>'
|
|
68
|
-
* }
|
|
69
|
-
* });
|
|
70
|
-
*
|
|
71
|
-
* // Search notes
|
|
72
|
-
* const { data: searchResults } = await client.GET('/notes', {
|
|
73
|
-
* params: { query: { search: '#blog' } }
|
|
74
|
-
* });
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
|
-
export function createTriliumClient(config: TriliumClientConfig) {
|
|
78
|
-
const baseUrl = config.baseUrl.endsWith('/')
|
|
79
|
-
? config.baseUrl.slice(0, -1)
|
|
80
|
-
: config.baseUrl;
|
|
81
|
-
|
|
82
|
-
return createClient<paths>({
|
|
83
|
-
baseUrl: `${baseUrl}/etapi`,
|
|
84
|
-
headers: {
|
|
85
|
-
Authorization: config.apiKey,
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Default export for convenience
|
|
91
|
-
export default createTriliumClient;
|
|
1
|
+
/**
|
|
2
|
+
* Trilium API Client using openapi-fetch
|
|
3
|
+
*
|
|
4
|
+
* This provides a type-safe client for the Trilium ETAPI.
|
|
5
|
+
* Types are auto-generated from the OpenAPI specification.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import createClient from 'openapi-fetch';
|
|
9
|
+
import type { paths, components } from './generated/trilium.js';
|
|
10
|
+
|
|
11
|
+
// Re-export common types for convenience
|
|
12
|
+
export type TriliumNote = components['schemas']['Note'];
|
|
13
|
+
export type TriliumBranch = components['schemas']['Branch'];
|
|
14
|
+
export type TriliumAttribute = components['schemas']['Attribute'];
|
|
15
|
+
export type TriliumAttachment = components['schemas']['Attachment'];
|
|
16
|
+
export type TriliumAppInfo = components['schemas']['AppInfo'];
|
|
17
|
+
|
|
18
|
+
// Export the paths type for advanced usage
|
|
19
|
+
export type { paths, components };
|
|
20
|
+
|
|
21
|
+
// Re-export mapper utilities
|
|
22
|
+
export {
|
|
23
|
+
TriliumMapper,
|
|
24
|
+
buildSearchQuery,
|
|
25
|
+
transforms,
|
|
26
|
+
type MappingConfig,
|
|
27
|
+
type FieldMapping,
|
|
28
|
+
type TransformFunction,
|
|
29
|
+
type ComputedFunction,
|
|
30
|
+
type TriliumSearchHelpers,
|
|
31
|
+
type TriliumSearchConditions,
|
|
32
|
+
type TriliumSearchLogical,
|
|
33
|
+
type ComparisonOperator,
|
|
34
|
+
type ConditionValue,
|
|
35
|
+
type SearchValue,
|
|
36
|
+
} from './mapper.js';
|
|
37
|
+
|
|
38
|
+
export interface TriliumClientConfig {
|
|
39
|
+
baseUrl: string;
|
|
40
|
+
apiKey: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create a type-safe Trilium API client
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const client = createTriliumClient({
|
|
49
|
+
* baseUrl: 'http://localhost:37840',
|
|
50
|
+
* apiKey: 'your-etapi-token'
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // Get app info
|
|
54
|
+
* const { data, error } = await client.GET('/app-info');
|
|
55
|
+
*
|
|
56
|
+
* // Get a note by ID
|
|
57
|
+
* const { data: note } = await client.GET('/notes/{noteId}', {
|
|
58
|
+
* params: { path: { noteId: 'root' } }
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* // Create a note
|
|
62
|
+
* const { data: newNote } = await client.POST('/notes', {
|
|
63
|
+
* body: {
|
|
64
|
+
* parentNoteId: 'root',
|
|
65
|
+
* title: 'My Note',
|
|
66
|
+
* type: 'text',
|
|
67
|
+
* content: '<p>Hello World</p>'
|
|
68
|
+
* }
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Search notes
|
|
72
|
+
* const { data: searchResults } = await client.GET('/notes', {
|
|
73
|
+
* params: { query: { search: '#blog' } }
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function createTriliumClient(config: TriliumClientConfig) {
|
|
78
|
+
const baseUrl = config.baseUrl.endsWith('/')
|
|
79
|
+
? config.baseUrl.slice(0, -1)
|
|
80
|
+
: config.baseUrl;
|
|
81
|
+
|
|
82
|
+
return createClient<paths>({
|
|
83
|
+
baseUrl: `${baseUrl}/etapi`,
|
|
84
|
+
headers: {
|
|
85
|
+
Authorization: config.apiKey,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Default export for convenience
|
|
91
|
+
export default createTriliumClient;
|
package/src/demo-mapper.ts
CHANGED
|
@@ -1,166 +1,166 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Demo script for the Note Mapper
|
|
3
|
-
*
|
|
4
|
-
* This demonstrates how to use TriliumMapper to map Trilium notes
|
|
5
|
-
* to strongly-typed objects.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* pnpm tsx src/demo-mapper.ts
|
|
9
|
-
*
|
|
10
|
-
* With a real Trilium instance:
|
|
11
|
-
* TRILIUM_API_KEY=your-token pnpm tsx src/demo-mapper.ts
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { TriliumMapper, transforms, type TriliumNote } from './client.js';
|
|
15
|
-
|
|
16
|
-
console.log('Note Mapper Demo');
|
|
17
|
-
console.log('='.repeat(50));
|
|
18
|
-
console.log();
|
|
19
|
-
|
|
20
|
-
// Define a sample blog post type
|
|
21
|
-
interface BlogPost {
|
|
22
|
-
id: string;
|
|
23
|
-
title: string;
|
|
24
|
-
slug: string;
|
|
25
|
-
status: 'draft' | 'published' | 'archived';
|
|
26
|
-
wordCount: number;
|
|
27
|
-
tags: string[];
|
|
28
|
-
publishedAt: Date | null;
|
|
29
|
-
readTimeMinutes: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Create a mapper configuration
|
|
33
|
-
const blogMapper = new TriliumMapper<BlogPost>({
|
|
34
|
-
// Simple property mapping
|
|
35
|
-
id: 'note.noteId',
|
|
36
|
-
title: 'note.title',
|
|
37
|
-
|
|
38
|
-
// Required label
|
|
39
|
-
slug: { from: '#slug', required: true },
|
|
40
|
-
|
|
41
|
-
// Label with default value
|
|
42
|
-
status: { from: '#status', default: 'draft' },
|
|
43
|
-
|
|
44
|
-
// Label with transform
|
|
45
|
-
wordCount: { from: '#wordCount', transform: transforms.number, default: 0 },
|
|
46
|
-
|
|
47
|
-
// Label with array transform
|
|
48
|
-
tags: { from: '#tags', transform: transforms.commaSeparated, default: [] },
|
|
49
|
-
|
|
50
|
-
// Label with date transform
|
|
51
|
-
publishedAt: { from: '#publishedAt', transform: transforms.date, default: null },
|
|
52
|
-
|
|
53
|
-
// Computed value based on other fields
|
|
54
|
-
readTimeMinutes: {
|
|
55
|
-
computed: (partial) => Math.ceil((partial.wordCount || 0) / 200),
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Create sample Trilium notes (simulating API response)
|
|
60
|
-
const sampleNotes: TriliumNote[] = [
|
|
61
|
-
{
|
|
62
|
-
noteId: 'note1',
|
|
63
|
-
title: 'Getting Started with TypeScript',
|
|
64
|
-
type: 'text',
|
|
65
|
-
mime: 'text/html',
|
|
66
|
-
isProtected: false,
|
|
67
|
-
blobId: 'blob1',
|
|
68
|
-
dateCreated: '2024-01-15T10:00:00.000Z',
|
|
69
|
-
dateModified: '2024-01-20T15:30:00.000Z',
|
|
70
|
-
utcDateCreated: '2024-01-15T10:00:00.000Z',
|
|
71
|
-
utcDateModified: '2024-01-20T15:30:00.000Z',
|
|
72
|
-
attributes: [
|
|
73
|
-
{ attributeId: 'attr1', noteId: 'note1', type: 'label', name: 'slug', value: 'getting-started-typescript', isInheritable: false },
|
|
74
|
-
{ attributeId: 'attr2', noteId: 'note1', type: 'label', name: 'status', value: 'published', isInheritable: false },
|
|
75
|
-
{ attributeId: 'attr3', noteId: 'note1', type: 'label', name: 'wordCount', value: '1500', isInheritable: false },
|
|
76
|
-
{ attributeId: 'attr4', noteId: 'note1', type: 'label', name: 'tags', value: 'typescript,programming,tutorial', isInheritable: false },
|
|
77
|
-
{ attributeId: 'attr5', noteId: 'note1', type: 'label', name: 'publishedAt', value: '2024-01-20', isInheritable: false },
|
|
78
|
-
],
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
noteId: 'note2',
|
|
82
|
-
title: 'Advanced Patterns in Node.js',
|
|
83
|
-
type: 'text',
|
|
84
|
-
mime: 'text/html',
|
|
85
|
-
isProtected: false,
|
|
86
|
-
blobId: 'blob2',
|
|
87
|
-
dateCreated: '2024-02-01T09:00:00.000Z',
|
|
88
|
-
dateModified: '2024-02-05T14:00:00.000Z',
|
|
89
|
-
utcDateCreated: '2024-02-01T09:00:00.000Z',
|
|
90
|
-
utcDateModified: '2024-02-05T14:00:00.000Z',
|
|
91
|
-
attributes: [
|
|
92
|
-
{ attributeId: 'attr6', noteId: 'note2', type: 'label', name: 'slug', value: 'advanced-nodejs-patterns', isInheritable: false },
|
|
93
|
-
{ attributeId: 'attr7', noteId: 'note2', type: 'label', name: 'status', value: 'draft', isInheritable: false },
|
|
94
|
-
{ attributeId: 'attr8', noteId: 'note2', type: 'label', name: 'wordCount', value: '2400', isInheritable: false },
|
|
95
|
-
{ attributeId: 'attr9', noteId: 'note2', type: 'label', name: 'tags', value: 'nodejs,javascript,backend', isInheritable: false },
|
|
96
|
-
],
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
noteId: 'note3',
|
|
100
|
-
title: 'Quick Tips for VS Code',
|
|
101
|
-
type: 'text',
|
|
102
|
-
mime: 'text/html',
|
|
103
|
-
isProtected: false,
|
|
104
|
-
blobId: 'blob3',
|
|
105
|
-
dateCreated: '2024-03-01T11:00:00.000Z',
|
|
106
|
-
dateModified: '2024-03-01T11:30:00.000Z',
|
|
107
|
-
utcDateCreated: '2024-03-01T11:00:00.000Z',
|
|
108
|
-
utcDateModified: '2024-03-01T11:30:00.000Z',
|
|
109
|
-
attributes: [
|
|
110
|
-
{ attributeId: 'attr10', noteId: 'note3', type: 'label', name: 'slug', value: 'vscode-tips', isInheritable: false },
|
|
111
|
-
// No status - will use default 'draft'
|
|
112
|
-
// No wordCount - will use default 0
|
|
113
|
-
// No tags - will use default []
|
|
114
|
-
],
|
|
115
|
-
},
|
|
116
|
-
];
|
|
117
|
-
|
|
118
|
-
console.log('Mapper Configuration:');
|
|
119
|
-
console.log('-'.repeat(50));
|
|
120
|
-
console.log(`
|
|
121
|
-
const blogMapper = new TriliumMapper<BlogPost>({
|
|
122
|
-
id: 'note.noteId',
|
|
123
|
-
title: 'note.title',
|
|
124
|
-
slug: { from: '#slug', required: true },
|
|
125
|
-
status: { from: '#status', default: 'draft' },
|
|
126
|
-
wordCount: { from: '#wordCount', transform: transforms.number, default: 0 },
|
|
127
|
-
tags: { from: '#tags', transform: transforms.commaSeparated, default: [] },
|
|
128
|
-
publishedAt: { from: '#publishedAt', transform: transforms.date, default: null },
|
|
129
|
-
readTimeMinutes: {
|
|
130
|
-
computed: (partial) => Math.ceil((partial.wordCount || 0) / 200),
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
`);
|
|
134
|
-
|
|
135
|
-
console.log('Mapping Results:');
|
|
136
|
-
console.log('-'.repeat(50));
|
|
137
|
-
|
|
138
|
-
// Map all notes
|
|
139
|
-
const blogPosts = blogMapper.map(sampleNotes);
|
|
140
|
-
|
|
141
|
-
for (const post of blogPosts) {
|
|
142
|
-
console.log();
|
|
143
|
-
console.log(`Title: ${post.title}`);
|
|
144
|
-
console.log(` ID: ${post.id}`);
|
|
145
|
-
console.log(` Slug: ${post.slug}`);
|
|
146
|
-
console.log(` Status: ${post.status}`);
|
|
147
|
-
console.log(` Word Count: ${post.wordCount}`);
|
|
148
|
-
console.log(` Tags: [${post.tags.join(', ')}]`);
|
|
149
|
-
console.log(` Published: ${post.publishedAt?.toISOString() || 'Not published'}`);
|
|
150
|
-
console.log(` Read Time: ${post.readTimeMinutes} min`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
console.log();
|
|
154
|
-
console.log('='.repeat(50));
|
|
155
|
-
console.log();
|
|
156
|
-
|
|
157
|
-
// Demonstrate single note mapping
|
|
158
|
-
console.log('Single Note Mapping:');
|
|
159
|
-
console.log('-'.repeat(50));
|
|
160
|
-
console.log('const singlePost = blogMapper.map(notes[0]);');
|
|
161
|
-
const singlePost = blogMapper.map(sampleNotes[0]!);
|
|
162
|
-
console.log('Result:', JSON.stringify(singlePost, null, 2));
|
|
163
|
-
console.log();
|
|
164
|
-
|
|
165
|
-
console.log('='.repeat(50));
|
|
166
|
-
console.log('Demo completed!');
|
|
1
|
+
/**
|
|
2
|
+
* Demo script for the Note Mapper
|
|
3
|
+
*
|
|
4
|
+
* This demonstrates how to use TriliumMapper to map Trilium notes
|
|
5
|
+
* to strongly-typed objects.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* pnpm tsx src/demo-mapper.ts
|
|
9
|
+
*
|
|
10
|
+
* With a real Trilium instance:
|
|
11
|
+
* TRILIUM_API_KEY=your-token pnpm tsx src/demo-mapper.ts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { TriliumMapper, transforms, type TriliumNote } from './client.js';
|
|
15
|
+
|
|
16
|
+
console.log('Note Mapper Demo');
|
|
17
|
+
console.log('='.repeat(50));
|
|
18
|
+
console.log();
|
|
19
|
+
|
|
20
|
+
// Define a sample blog post type
|
|
21
|
+
interface BlogPost {
|
|
22
|
+
id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
slug: string;
|
|
25
|
+
status: 'draft' | 'published' | 'archived';
|
|
26
|
+
wordCount: number;
|
|
27
|
+
tags: string[];
|
|
28
|
+
publishedAt: Date | null;
|
|
29
|
+
readTimeMinutes: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Create a mapper configuration
|
|
33
|
+
const blogMapper = new TriliumMapper<BlogPost>({
|
|
34
|
+
// Simple property mapping
|
|
35
|
+
id: 'note.noteId',
|
|
36
|
+
title: 'note.title',
|
|
37
|
+
|
|
38
|
+
// Required label
|
|
39
|
+
slug: { from: '#slug', required: true },
|
|
40
|
+
|
|
41
|
+
// Label with default value
|
|
42
|
+
status: { from: '#status', default: 'draft' },
|
|
43
|
+
|
|
44
|
+
// Label with transform
|
|
45
|
+
wordCount: { from: '#wordCount', transform: transforms.number, default: 0 },
|
|
46
|
+
|
|
47
|
+
// Label with array transform
|
|
48
|
+
tags: { from: '#tags', transform: transforms.commaSeparated, default: [] },
|
|
49
|
+
|
|
50
|
+
// Label with date transform
|
|
51
|
+
publishedAt: { from: '#publishedAt', transform: transforms.date, default: null },
|
|
52
|
+
|
|
53
|
+
// Computed value based on other fields
|
|
54
|
+
readTimeMinutes: {
|
|
55
|
+
computed: (partial) => Math.ceil((partial.wordCount || 0) / 200),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Create sample Trilium notes (simulating API response)
|
|
60
|
+
const sampleNotes: TriliumNote[] = [
|
|
61
|
+
{
|
|
62
|
+
noteId: 'note1',
|
|
63
|
+
title: 'Getting Started with TypeScript',
|
|
64
|
+
type: 'text',
|
|
65
|
+
mime: 'text/html',
|
|
66
|
+
isProtected: false,
|
|
67
|
+
blobId: 'blob1',
|
|
68
|
+
dateCreated: '2024-01-15T10:00:00.000Z',
|
|
69
|
+
dateModified: '2024-01-20T15:30:00.000Z',
|
|
70
|
+
utcDateCreated: '2024-01-15T10:00:00.000Z',
|
|
71
|
+
utcDateModified: '2024-01-20T15:30:00.000Z',
|
|
72
|
+
attributes: [
|
|
73
|
+
{ attributeId: 'attr1', noteId: 'note1', type: 'label', name: 'slug', value: 'getting-started-typescript', isInheritable: false },
|
|
74
|
+
{ attributeId: 'attr2', noteId: 'note1', type: 'label', name: 'status', value: 'published', isInheritable: false },
|
|
75
|
+
{ attributeId: 'attr3', noteId: 'note1', type: 'label', name: 'wordCount', value: '1500', isInheritable: false },
|
|
76
|
+
{ attributeId: 'attr4', noteId: 'note1', type: 'label', name: 'tags', value: 'typescript,programming,tutorial', isInheritable: false },
|
|
77
|
+
{ attributeId: 'attr5', noteId: 'note1', type: 'label', name: 'publishedAt', value: '2024-01-20', isInheritable: false },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
noteId: 'note2',
|
|
82
|
+
title: 'Advanced Patterns in Node.js',
|
|
83
|
+
type: 'text',
|
|
84
|
+
mime: 'text/html',
|
|
85
|
+
isProtected: false,
|
|
86
|
+
blobId: 'blob2',
|
|
87
|
+
dateCreated: '2024-02-01T09:00:00.000Z',
|
|
88
|
+
dateModified: '2024-02-05T14:00:00.000Z',
|
|
89
|
+
utcDateCreated: '2024-02-01T09:00:00.000Z',
|
|
90
|
+
utcDateModified: '2024-02-05T14:00:00.000Z',
|
|
91
|
+
attributes: [
|
|
92
|
+
{ attributeId: 'attr6', noteId: 'note2', type: 'label', name: 'slug', value: 'advanced-nodejs-patterns', isInheritable: false },
|
|
93
|
+
{ attributeId: 'attr7', noteId: 'note2', type: 'label', name: 'status', value: 'draft', isInheritable: false },
|
|
94
|
+
{ attributeId: 'attr8', noteId: 'note2', type: 'label', name: 'wordCount', value: '2400', isInheritable: false },
|
|
95
|
+
{ attributeId: 'attr9', noteId: 'note2', type: 'label', name: 'tags', value: 'nodejs,javascript,backend', isInheritable: false },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
noteId: 'note3',
|
|
100
|
+
title: 'Quick Tips for VS Code',
|
|
101
|
+
type: 'text',
|
|
102
|
+
mime: 'text/html',
|
|
103
|
+
isProtected: false,
|
|
104
|
+
blobId: 'blob3',
|
|
105
|
+
dateCreated: '2024-03-01T11:00:00.000Z',
|
|
106
|
+
dateModified: '2024-03-01T11:30:00.000Z',
|
|
107
|
+
utcDateCreated: '2024-03-01T11:00:00.000Z',
|
|
108
|
+
utcDateModified: '2024-03-01T11:30:00.000Z',
|
|
109
|
+
attributes: [
|
|
110
|
+
{ attributeId: 'attr10', noteId: 'note3', type: 'label', name: 'slug', value: 'vscode-tips', isInheritable: false },
|
|
111
|
+
// No status - will use default 'draft'
|
|
112
|
+
// No wordCount - will use default 0
|
|
113
|
+
// No tags - will use default []
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
console.log('Mapper Configuration:');
|
|
119
|
+
console.log('-'.repeat(50));
|
|
120
|
+
console.log(`
|
|
121
|
+
const blogMapper = new TriliumMapper<BlogPost>({
|
|
122
|
+
id: 'note.noteId',
|
|
123
|
+
title: 'note.title',
|
|
124
|
+
slug: { from: '#slug', required: true },
|
|
125
|
+
status: { from: '#status', default: 'draft' },
|
|
126
|
+
wordCount: { from: '#wordCount', transform: transforms.number, default: 0 },
|
|
127
|
+
tags: { from: '#tags', transform: transforms.commaSeparated, default: [] },
|
|
128
|
+
publishedAt: { from: '#publishedAt', transform: transforms.date, default: null },
|
|
129
|
+
readTimeMinutes: {
|
|
130
|
+
computed: (partial) => Math.ceil((partial.wordCount || 0) / 200),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
`);
|
|
134
|
+
|
|
135
|
+
console.log('Mapping Results:');
|
|
136
|
+
console.log('-'.repeat(50));
|
|
137
|
+
|
|
138
|
+
// Map all notes
|
|
139
|
+
const blogPosts = blogMapper.map(sampleNotes);
|
|
140
|
+
|
|
141
|
+
for (const post of blogPosts) {
|
|
142
|
+
console.log();
|
|
143
|
+
console.log(`Title: ${post.title}`);
|
|
144
|
+
console.log(` ID: ${post.id}`);
|
|
145
|
+
console.log(` Slug: ${post.slug}`);
|
|
146
|
+
console.log(` Status: ${post.status}`);
|
|
147
|
+
console.log(` Word Count: ${post.wordCount}`);
|
|
148
|
+
console.log(` Tags: [${post.tags.join(', ')}]`);
|
|
149
|
+
console.log(` Published: ${post.publishedAt?.toISOString() || 'Not published'}`);
|
|
150
|
+
console.log(` Read Time: ${post.readTimeMinutes} min`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log();
|
|
154
|
+
console.log('='.repeat(50));
|
|
155
|
+
console.log();
|
|
156
|
+
|
|
157
|
+
// Demonstrate single note mapping
|
|
158
|
+
console.log('Single Note Mapping:');
|
|
159
|
+
console.log('-'.repeat(50));
|
|
160
|
+
console.log('const singlePost = blogMapper.map(notes[0]);');
|
|
161
|
+
const singlePost = blogMapper.map(sampleNotes[0]!);
|
|
162
|
+
console.log('Result:', JSON.stringify(singlePost, null, 2));
|
|
163
|
+
console.log();
|
|
164
|
+
|
|
165
|
+
console.log('='.repeat(50));
|
|
166
|
+
console.log('Demo completed!');
|