gudong-inbox-cli 1.0.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,352 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { createRequire } from 'node:module';
4
+ import { loadConfig, saveConfig, configExists, getConfigPath } from './config.js';
5
+ import { createCloudProvider, notesDirFor } from './cloud/factory.js';
6
+ import { NoteService } from './note/service.js';
7
+ // Read version from package.json so it stays in sync automatically.
8
+ const require = createRequire(import.meta.url);
9
+ const VERSION = require('../package.json').version;
10
+ function printHelp() {
11
+ const text = `
12
+ gudong-inbox v${VERSION} — read and write your inBox notes from the command line.
13
+
14
+ Usage:
15
+ gudong-inbox list [--limit N] [--since ISO] [--until ISO] [--field updated_at|created_at]
16
+ gudong-inbox read --id <20-hex-id>
17
+ gudong-inbox search --query "keyword" [--limit N]
18
+ gudong-inbox tags
19
+ gudong-inbox create --content "text" [--title "title"] [--tag tag1] [--tag tag2] [--box name]
20
+ gudong-inbox update --id <id> [--content "text"] [--title "title"] [--tag tag1]
21
+ gudong-inbox delete --id <id> --confirm
22
+ gudong-inbox config init Set up credentials (interactive or via flags)
23
+ gudong-inbox config show Show current config (secrets masked)
24
+ gudong-inbox config test Test connection to cloud storage
25
+ gudong-inbox --version
26
+ gudong-inbox --help
27
+
28
+ Examples:
29
+ gudong-inbox list --limit 5
30
+ gudong-inbox list --since 2026-06-25T00:00:00+08:00 --field created_at
31
+ gudong-inbox search --query "Flutter" --limit 10
32
+ gudong-inbox read --id b10a4576df11401c8673
33
+ gudong-inbox create --content "hello world" --title "test" --tag idea
34
+
35
+ Config:
36
+ ~/.gudong-inbox/config.json (primary, created by \`config init\`)
37
+ Environment variables (fallback: CLOUD_TYPE, S3_ENDPOINT, S3_BUCKET, ...)
38
+ `;
39
+ console.log(text);
40
+ }
41
+ function output(data) {
42
+ console.log(JSON.stringify(data, null, 2));
43
+ }
44
+ function getService() {
45
+ const cfg = loadConfig();
46
+ const cloud = createCloudProvider(cfg);
47
+ const dir = notesDirFor(cfg.workDir, cfg.cloudType);
48
+ return new NoteService(cloud, dir, { cacheTtlMs: cfg.cacheTtlMs });
49
+ }
50
+ // --- helpers for parseArgs ---
51
+ function getArg(args, key) {
52
+ const v = args[key];
53
+ return typeof v === 'string' ? v : undefined;
54
+ }
55
+ function getArgNumber(args, key) {
56
+ const v = args[key];
57
+ return typeof v === 'string' ? Number(v) : undefined;
58
+ }
59
+ function getArgArray(args, key) {
60
+ const v = args[key];
61
+ if (Array.isArray(v))
62
+ return v;
63
+ if (typeof v === 'string')
64
+ return [v];
65
+ return undefined;
66
+ }
67
+ function getArgBool(args, key) {
68
+ const v = args[key];
69
+ return v === true || v === 'true';
70
+ }
71
+ // --- command handlers ---
72
+ async function cmdList(args) {
73
+ const svc = getService();
74
+ const limit = getArgNumber(args, 'limit') ?? 20;
75
+ const since = getArg(args, 'since');
76
+ const until = getArg(args, 'until');
77
+ const field = (getArg(args, 'field') ?? 'updated_at');
78
+ const notes = await svc.listRecent({ limit, since, until, date_field: field });
79
+ output({ count: notes.length, notes });
80
+ }
81
+ async function cmdRead(args) {
82
+ const id = getArg(args, 'id');
83
+ if (!id)
84
+ return fail('--id is required');
85
+ const svc = getService();
86
+ const note = await svc.readById(id);
87
+ if (!note)
88
+ return fail(`note not found: ${id}`);
89
+ output(note);
90
+ }
91
+ async function cmdSearch(args) {
92
+ const query = getArg(args, 'query');
93
+ if (!query)
94
+ return fail('--query is required');
95
+ const limit = getArgNumber(args, 'limit') ?? 20;
96
+ const svc = getService();
97
+ const notes = await svc.search(query, limit);
98
+ output({ query, count: notes.length, notes });
99
+ }
100
+ async function cmdTags() {
101
+ const svc = getService();
102
+ const tags = await svc.listTags();
103
+ output({ count: tags.length, tags });
104
+ }
105
+ async function cmdCreate(args) {
106
+ const content = getArg(args, 'content');
107
+ if (!content)
108
+ return fail('--content is required');
109
+ const svc = getService();
110
+ const result = await svc.createNote({
111
+ content,
112
+ title: getArg(args, 'title'),
113
+ tags: getArgArray(args, 'tag'),
114
+ box: getArg(args, 'box'),
115
+ });
116
+ output({ success: true, ...result });
117
+ }
118
+ async function cmdUpdate(args) {
119
+ const id = getArg(args, 'id');
120
+ if (!id)
121
+ return fail('--id is required');
122
+ const svc = getService();
123
+ const result = await svc.updateNote({
124
+ id,
125
+ content: getArg(args, 'content'),
126
+ title: getArg(args, 'title'),
127
+ tags: getArgArray(args, 'tag'),
128
+ box: getArg(args, 'box'),
129
+ });
130
+ output({ success: true, ...result });
131
+ }
132
+ async function cmdDelete(args) {
133
+ const id = getArg(args, 'id');
134
+ if (!id)
135
+ return fail('--id is required');
136
+ if (!getArgBool(args, 'confirm'))
137
+ return fail('--confirm is required to delete');
138
+ const svc = getService();
139
+ const result = await svc.deleteNote(id);
140
+ output({ success: true, ...result });
141
+ }
142
+ // --- config commands ---
143
+ async function cmdConfigInit(args) {
144
+ const cloudType = getArg(args, 'cloud-type') ?? getArg(args, 'cloud_type');
145
+ if (!cloudType) {
146
+ console.log(`
147
+ gudong-inbox config init
148
+
149
+ Usage:
150
+ gudong-inbox config init --cloud-type s3 \\
151
+ --endpoint https://s3.example.com \\
152
+ --bucket my-bucket \\
153
+ --access-key AKID... \\
154
+ --secret-key SECRET... \\
155
+ [--region cn-south-1] \\
156
+ [--force-path-style] \\
157
+ [--work-dir inBox]
158
+
159
+ gudong-inbox config init --cloud-type webdav \\
160
+ --url https://dav.example.com/path \\
161
+ --username user \\
162
+ --password pass \\
163
+ [--work-dir inBox]
164
+ `);
165
+ return;
166
+ }
167
+ const workDir = getArg(args, 'work-dir') ?? getArg(args, 'work_dir') ?? 'inBox';
168
+ const cfg = { cloud_type: cloudType, work_dir: workDir };
169
+ if (cloudType === 's3') {
170
+ const endpoint = getArg(args, 'endpoint');
171
+ const bucket = getArg(args, 'bucket');
172
+ const accessKey = getArg(args, 'access-key') ?? getArg(args, 'access_key');
173
+ const secretKey = getArg(args, 'secret-key') ?? getArg(args, 'secret_key');
174
+ const region = getArg(args, 'region') ?? 'auto';
175
+ const forcePathStyle = args['force-path-style'] === true || args['force-path-style'] === 'true';
176
+ if (!endpoint)
177
+ return fail('--endpoint is required');
178
+ if (!bucket)
179
+ return fail('--bucket is required');
180
+ if (!accessKey)
181
+ return fail('--access-key is required');
182
+ if (!secretKey)
183
+ return fail('--secret-key is required');
184
+ cfg.s3 = { endpoint, bucket, accessKey, secretKey, region, forcePathStyle };
185
+ }
186
+ else if (cloudType === 'webdav') {
187
+ const url = getArg(args, 'url');
188
+ const username = getArg(args, 'username');
189
+ const password = getArg(args, 'password');
190
+ if (!url)
191
+ return fail('--url is required');
192
+ if (!username)
193
+ return fail('--username is required');
194
+ if (!password)
195
+ return fail('--password is required');
196
+ cfg.webdav = { url, username, password };
197
+ }
198
+ else {
199
+ return fail(`unknown cloud type: ${cloudType}`);
200
+ }
201
+ saveConfig(cfg);
202
+ console.log(`Config saved to ${getConfigPath()}`);
203
+ }
204
+ async function cmdConfigShow() {
205
+ if (!configExists()) {
206
+ console.log('No config found. Run `gudong-inbox config init` first.');
207
+ return;
208
+ }
209
+ const { readFileSync } = await import('node:fs');
210
+ const text = readFileSync(getConfigPath(), 'utf-8');
211
+ const cfg = JSON.parse(text);
212
+ // Mask secrets
213
+ if (cfg.s3) {
214
+ if (cfg.s3.accessKey)
215
+ cfg.s3.accessKey = mask(cfg.s3.accessKey);
216
+ if (cfg.s3.secretKey)
217
+ cfg.s3.secretKey = '***';
218
+ }
219
+ if (cfg.webdav) {
220
+ if (cfg.webdav.password)
221
+ cfg.webdav.password = '***';
222
+ }
223
+ output(cfg);
224
+ }
225
+ function mask(s) {
226
+ if (s.length <= 4)
227
+ return '***';
228
+ return s.slice(0, 2) + '***' + s.slice(-2);
229
+ }
230
+ async function cmdConfigTest() {
231
+ const cfg = loadConfig();
232
+ const cloud = createCloudProvider(cfg);
233
+ const dir = notesDirFor(cfg.workDir, cfg.cloudType);
234
+ const svc = new NoteService(cloud, dir, { cacheTtlMs: 0 });
235
+ const notes = await svc.fetchAll();
236
+ output({
237
+ status: 'ok',
238
+ cloud_type: cfg.cloudType,
239
+ work_dir: cfg.workDir,
240
+ notes_dir: dir,
241
+ total_notes: notes.length,
242
+ });
243
+ }
244
+ function fail(msg) {
245
+ console.error(`[gudong-inbox] ${msg}`);
246
+ process.exit(1);
247
+ }
248
+ // --- main ---
249
+ function main() {
250
+ const argv = process.argv.slice(2);
251
+ if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
252
+ printHelp();
253
+ return;
254
+ }
255
+ if (argv[0] === '--version' || argv[0] === '-v') {
256
+ console.log(VERSION);
257
+ return;
258
+ }
259
+ const command = argv[0];
260
+ const rest = argv.slice(1);
261
+ // For multi-value options like --tag, parseArgs needs special handling
262
+ // We use { multiple: true } for --tag only
263
+ if (command === 'config') {
264
+ const subCmd = rest[0] ?? '';
265
+ const configArgs = rest.slice(1);
266
+ if (subCmd === 'init') {
267
+ const { values } = parseArgs({
268
+ args: configArgs,
269
+ options: {
270
+ 'cloud-type': { type: 'string' },
271
+ 'cloud_type': { type: 'string' },
272
+ endpoint: { type: 'string' },
273
+ bucket: { type: 'string' },
274
+ 'access-key': { type: 'string' },
275
+ 'access_key': { type: 'string' },
276
+ 'secret-key': { type: 'string' },
277
+ 'secret_key': { type: 'string' },
278
+ region: { type: 'string' },
279
+ 'force-path-style': { type: 'boolean' },
280
+ url: { type: 'string' },
281
+ username: { type: 'string' },
282
+ password: { type: 'string' },
283
+ 'work-dir': { type: 'string' },
284
+ 'work_dir': { type: 'string' },
285
+ },
286
+ allowNegative: true,
287
+ });
288
+ cmdConfigInit(values).catch((e) => fail(e.message));
289
+ }
290
+ else if (subCmd === 'show') {
291
+ cmdConfigShow().catch((e) => fail(e.message));
292
+ }
293
+ else if (subCmd === 'test') {
294
+ cmdConfigTest().catch((e) => fail(e.message));
295
+ }
296
+ else {
297
+ console.log('Usage: gudong-inbox config <init|show|test>');
298
+ }
299
+ return;
300
+ }
301
+ const baseOptions = {
302
+ limit: { type: 'string' },
303
+ since: { type: 'string' },
304
+ until: { type: 'string' },
305
+ field: { type: 'string' },
306
+ id: { type: 'string' },
307
+ query: { type: 'string' },
308
+ content: { type: 'string' },
309
+ title: { type: 'string' },
310
+ box: { type: 'string' },
311
+ confirm: { type: 'boolean' },
312
+ };
313
+ // --tag can appear multiple times, needs { multiple: true }
314
+ const hasTag = rest.includes('--tag');
315
+ if (hasTag) {
316
+ const { values } = parseArgs({
317
+ args: rest,
318
+ options: { ...baseOptions, tag: { type: 'string', multiple: true } },
319
+ allowNegative: true,
320
+ });
321
+ const tagArr = values.tag;
322
+ delete values.tag;
323
+ runCommand(command, values, tagArr).catch((e) => fail(e.message));
324
+ }
325
+ else {
326
+ const { values } = parseArgs({
327
+ args: rest,
328
+ options: baseOptions,
329
+ allowNegative: true,
330
+ });
331
+ runCommand(command, values).catch((e) => fail(e.message));
332
+ }
333
+ }
334
+ async function runCommand(cmd, args, tags) {
335
+ if (tags)
336
+ args.tag = tags;
337
+ switch (cmd) {
338
+ case 'list': return cmdList(args);
339
+ case 'read': return cmdRead(args);
340
+ case 'search': return cmdSearch(args);
341
+ case 'tags': return cmdTags();
342
+ case 'create': return cmdCreate(args);
343
+ case 'update': return cmdUpdate(args);
344
+ case 'delete': return cmdDelete(args);
345
+ default:
346
+ console.error(`Unknown command: ${cmd}`);
347
+ printHelp();
348
+ process.exit(1);
349
+ }
350
+ }
351
+ main();
352
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { NoteJson } from './types.js';
2
+ export declare function parseNote(id: string, raw: Buffer | string): NoteJson;
@@ -0,0 +1,110 @@
1
+ const ASSET_TYPES = ['image', 'video', 'audio', 'attachment'];
2
+ const STORAGE_TYPES = [
3
+ 'local',
4
+ 'webdav',
5
+ 's3',
6
+ 'minio',
7
+ 'aliyun',
8
+ 'tencent',
9
+ 'generic',
10
+ ];
11
+ function str(x, fallback = '') {
12
+ return typeof x === 'string' ? x : fallback;
13
+ }
14
+ function strOrNull(x) {
15
+ return typeof x === 'string' ? x : null;
16
+ }
17
+ function num(x, fallback = 0) {
18
+ return typeof x === 'number' && Number.isFinite(x) ? x : fallback;
19
+ }
20
+ function numOrNull(x) {
21
+ return typeof x === 'number' && Number.isFinite(x) ? x : null;
22
+ }
23
+ function bool(x, fallback = false) {
24
+ return typeof x === 'boolean' ? x : fallback;
25
+ }
26
+ function asStringArray(x) {
27
+ return Array.isArray(x) ? x.filter((v) => typeof v === 'string') : [];
28
+ }
29
+ function parseLink(raw) {
30
+ if (typeof raw !== 'object' || raw === null)
31
+ return null;
32
+ const obj = raw;
33
+ const targetId = str(obj.targetId);
34
+ if (!/^[a-f0-9]{20}$/.test(targetId))
35
+ return null;
36
+ return {
37
+ text: typeof obj.text === 'string' || obj.text === null ? obj.text : null,
38
+ targetId,
39
+ };
40
+ }
41
+ function parseAsset(raw) {
42
+ if (typeof raw !== 'object' || raw === null)
43
+ return null;
44
+ const obj = raw;
45
+ const type = str(obj.type);
46
+ const path = str(obj.path);
47
+ if (!ASSET_TYPES.includes(type))
48
+ return null;
49
+ if (!path)
50
+ return null;
51
+ const storageRaw = str(obj.storageType, 'local');
52
+ const storageType = STORAGE_TYPES.includes(storageRaw)
53
+ ? storageRaw
54
+ : 'local';
55
+ return {
56
+ type: type,
57
+ path,
58
+ storageType,
59
+ cloudUrl: strOrNull(obj.cloudUrl),
60
+ mime_type: str(obj.mime_type),
61
+ size: num(obj.size, 0),
62
+ width: numOrNull(obj.width),
63
+ height: numOrNull(obj.height),
64
+ duration: numOrNull(obj.duration),
65
+ };
66
+ }
67
+ export function parseNote(id, raw) {
68
+ const text = typeof raw === 'string' ? raw : raw.toString('utf-8');
69
+ const d = JSON.parse(text);
70
+ const metaSrc = (d.meta ?? {});
71
+ const contentSrc = (d.content ?? {});
72
+ const flagsSrc = (d.flags ?? {});
73
+ const meta = {
74
+ created_at: str(metaSrc.created_at),
75
+ updated_at: str(metaSrc.updated_at),
76
+ device_id: str(metaSrc.device_id),
77
+ };
78
+ const links = Array.isArray(contentSrc.links)
79
+ ? contentSrc.links.map(parseLink).filter((l) => l !== null)
80
+ : [];
81
+ const assets = Array.isArray(contentSrc.assets)
82
+ ? contentSrc.assets.map(parseAsset).filter((a) => a !== null)
83
+ : [];
84
+ const content = {
85
+ content: str(contentSrc.content),
86
+ title: strOrNull(contentSrc.title),
87
+ tags: asStringArray(contentSrc.tags),
88
+ links,
89
+ assets,
90
+ box: strOrNull(contentSrc.box),
91
+ };
92
+ const flags = {
93
+ is_removed: bool(flagsSrc.is_removed, false),
94
+ is_top: bool(flagsSrc.is_top, false),
95
+ favorite: bool(flagsSrc.favorite, false),
96
+ };
97
+ const parentIdRaw = d.parent_id;
98
+ const parent_id = typeof parentIdRaw === 'string' && /^[a-f0-9]{20}$/.test(parentIdRaw)
99
+ ? parentIdRaw
100
+ : null;
101
+ return {
102
+ id,
103
+ ver: num(d.ver, 1),
104
+ meta,
105
+ content,
106
+ flags,
107
+ parent_id,
108
+ };
109
+ }
110
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/note/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,137 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseNote } from './parser.js';
3
+ const VALID_ID = 'b10a4576df11401c8673';
4
+ function makeNoteJson(overrides = {}) {
5
+ return JSON.stringify({
6
+ id: VALID_ID,
7
+ ver: 1,
8
+ meta: {
9
+ created_at: '2026-06-01T00:00:00.000Z',
10
+ updated_at: '2026-06-02T00:00:00.000Z',
11
+ device_id: 'test-device',
12
+ },
13
+ content: {
14
+ content: 'hello world',
15
+ title: 'Test Note',
16
+ tags: ['idea', 'work'],
17
+ links: [{ text: 'ref', targetId: 'a'.repeat(20) }],
18
+ assets: [
19
+ {
20
+ type: 'image',
21
+ path: '/img/photo.jpg',
22
+ storageType: 's3',
23
+ mime_type: 'image/jpeg',
24
+ size: 1024,
25
+ },
26
+ ],
27
+ },
28
+ flags: {
29
+ is_removed: false,
30
+ is_top: true,
31
+ favorite: false,
32
+ },
33
+ ...overrides,
34
+ });
35
+ }
36
+ describe('parseNote', () => {
37
+ it('parses a valid note JSON', () => {
38
+ const note = parseNote(VALID_ID, makeNoteJson());
39
+ expect(note.id).toBe(VALID_ID);
40
+ expect(note.ver).toBe(1);
41
+ expect(note.meta.created_at).toBe('2026-06-01T00:00:00.000Z');
42
+ expect(note.meta.updated_at).toBe('2026-06-02T00:00:00.000Z');
43
+ expect(note.content.content).toBe('hello world');
44
+ expect(note.content.title).toBe('Test Note');
45
+ expect(note.content.tags).toEqual(['idea', 'work']);
46
+ expect(note.flags.is_top).toBe(true);
47
+ expect(note.flags.is_removed).toBe(false);
48
+ });
49
+ it('handles content as an array (legacy format)', () => {
50
+ const json = makeNoteJson({
51
+ content: { content: ['line1', 'line2'], tags: [] },
52
+ });
53
+ const note = parseNote(VALID_ID, json);
54
+ expect(note.content.content).toBe('');
55
+ });
56
+ it('handles null title', () => {
57
+ const json = makeNoteJson({
58
+ content: { content: 'test', title: null, tags: [], links: [], assets: [] },
59
+ });
60
+ const note = parseNote(VALID_ID, json);
61
+ expect(note.content.title).toBeNull();
62
+ });
63
+ it('filters out invalid links (bad targetId)', () => {
64
+ const json = makeNoteJson({
65
+ content: {
66
+ content: 'test',
67
+ tags: [],
68
+ links: [
69
+ { text: 'good', targetId: 'a'.repeat(20) },
70
+ { text: 'bad', targetId: 'short' },
71
+ ],
72
+ assets: [],
73
+ },
74
+ });
75
+ const note = parseNote(VALID_ID, json);
76
+ expect(note.content.links).toHaveLength(1);
77
+ expect(note.content.links[0].targetId).toBe('a'.repeat(20));
78
+ });
79
+ it('filters out invalid assets (missing path)', () => {
80
+ const json = makeNoteJson({
81
+ content: {
82
+ content: 'test',
83
+ tags: [],
84
+ links: [],
85
+ assets: [
86
+ { type: 'image', path: '/ok.jpg', storageType: 'local', mime_type: '', size: 0 },
87
+ { type: 'image', path: '', storageType: 'local', mime_type: '', size: 0 },
88
+ ],
89
+ },
90
+ });
91
+ const note = parseNote(VALID_ID, json);
92
+ expect(note.content.assets).toHaveLength(1);
93
+ });
94
+ it('defaults flags to false when missing', () => {
95
+ const json = makeNoteJson({ flags: {} });
96
+ const note = parseNote(VALID_ID, json);
97
+ expect(note.flags.is_removed).toBe(false);
98
+ expect(note.flags.is_top).toBe(false);
99
+ expect(note.flags.favorite).toBe(false);
100
+ });
101
+ it('handles parent_id', () => {
102
+ const json = makeNoteJson({ parent_id: 'b'.repeat(20) });
103
+ const note = parseNote(VALID_ID, json);
104
+ expect(note.parent_id).toBe('b'.repeat(20));
105
+ });
106
+ it('rejects invalid parent_id (non-hex)', () => {
107
+ const json = makeNoteJson({ parent_id: 'invalid' });
108
+ const note = parseNote(VALID_ID, json);
109
+ expect(note.parent_id).toBeNull();
110
+ });
111
+ it('accepts Buffer input', () => {
112
+ const json = makeNoteJson();
113
+ const note = parseNote(VALID_ID, Buffer.from(json, 'utf-8'));
114
+ expect(note.id).toBe(VALID_ID);
115
+ });
116
+ it('falls back gracefully for unknown asset/storage types', () => {
117
+ const json = makeNoteJson({
118
+ content: {
119
+ content: 'test',
120
+ tags: [],
121
+ links: [],
122
+ assets: [
123
+ {
124
+ type: 'mystery',
125
+ path: '/x.dat',
126
+ storageType: 'unknown_storage',
127
+ mime_type: '',
128
+ size: 0,
129
+ },
130
+ ],
131
+ },
132
+ });
133
+ const note = parseNote(VALID_ID, json);
134
+ expect(note.content.assets).toHaveLength(0);
135
+ });
136
+ });
137
+ //# sourceMappingURL=parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../../src/note/parser.test.ts"],"names":[],"mappings":"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"}