orgnote-api 0.20.2 → 0.40.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.
Files changed (283) hide show
  1. package/api.d.ts +93 -98
  2. package/constants/{command-groups.contant.d.ts → command-groups.d.ts} +2 -1
  3. package/constants/{command-groups.contant.js → command-groups.js} +2 -0
  4. package/constants/extension-errors.d.ts +9 -0
  5. package/constants/extension-errors.js +18 -0
  6. package/constants/file-guard-errors.d.ts +8 -0
  7. package/constants/file-guard-errors.js +18 -0
  8. package/constants/git-errors.d.ts +9 -0
  9. package/constants/git-errors.js +18 -0
  10. package/constants/i18n-keys.d.ts +371 -0
  11. package/constants/i18n-keys.js +158 -0
  12. package/constants/index.d.ts +9 -1
  13. package/constants/index.js +9 -1
  14. package/constants/oauth-providers.d.ts +1 -0
  15. package/constants/oauth-providers.js +1 -0
  16. package/constants/route-names.d.ts +36 -0
  17. package/constants/route-names.js +37 -0
  18. package/constants/route-paths.d.ts +5 -0
  19. package/constants/route-paths.js +4 -0
  20. package/constants/style-sizes.d.ts +1 -0
  21. package/constants/style-sizes.js +1 -0
  22. package/encryption/__tests__/encryption.spec.js +4 -5
  23. package/encryption/__tests__/note-encryption.spec.js +46 -348
  24. package/encryption/encryption.d.ts +9 -4
  25. package/encryption/encryption.js +25 -5
  26. package/encryption/note-encryption.d.ts +1 -1
  27. package/encryption/note-encryption.js +6 -6
  28. package/files-api.d.ts +0 -1
  29. package/index.d.ts +4 -1
  30. package/index.js +4 -1
  31. package/mappers/orgnode-to-note.d.ts +2 -2
  32. package/mappers/orgnode-to-note.js +3 -2
  33. package/models/auth-store.d.ts +3 -3
  34. package/models/buffer-store.d.ts +14 -0
  35. package/models/buffer.d.ts +24 -0
  36. package/models/colors.d.ts +1 -0
  37. package/models/command.d.ts +13 -8
  38. package/models/commands-group-store.d.ts +11 -0
  39. package/models/commands-store.d.ts +13 -0
  40. package/models/completion-store.d.ts +14 -0
  41. package/models/completion.d.ts +16 -7
  42. package/models/config-store.d.ts +9 -0
  43. package/models/confirmation-modal.d.ts +11 -0
  44. package/models/context-menu-store.d.ts +10 -0
  45. package/models/cron-store.d.ts +21 -0
  46. package/models/cron-task.d.ts +30 -0
  47. package/models/css-utils.d.ts +17 -0
  48. package/models/default-commands.d.ts +52 -3
  49. package/models/default-commands.js +59 -2
  50. package/models/encryption-store.d.ts +10 -0
  51. package/models/encryption-store.js +1 -0
  52. package/models/encryption.d.ts +54 -11
  53. package/models/encryption.js +28 -1
  54. package/models/extension-registry-store.d.ts +9 -0
  55. package/models/extension-registry-store.js +1 -0
  56. package/models/extension-store.d.ts +18 -0
  57. package/models/extension-store.js +1 -0
  58. package/models/extension.d.ts +96 -24
  59. package/models/extension.js +88 -1
  60. package/models/file-guard-store.d.ts +14 -0
  61. package/models/file-guard-store.js +1 -0
  62. package/models/file-guard.d.ts +27 -0
  63. package/models/file-guard.js +1 -0
  64. package/models/{file-cache.d.ts → file-info.d.ts} +1 -1
  65. package/models/file-info.js +1 -0
  66. package/models/file-manager-store.d.ts +10 -12
  67. package/models/file-opener-store.d.ts +6 -4
  68. package/models/file-system-manager-store.d.ts +13 -0
  69. package/models/file-system-manager-store.js +1 -0
  70. package/models/file-system-store.d.ts +19 -0
  71. package/models/file-system-store.js +1 -0
  72. package/models/file-system.d.ts +32 -5
  73. package/models/file-system.js +2 -0
  74. package/models/file-upload.d.ts +6 -0
  75. package/models/file-upload.js +1 -0
  76. package/models/file-watcher-store.d.ts +18 -0
  77. package/models/file-watcher-store.js +1 -0
  78. package/models/files-store.d.ts +2 -2
  79. package/models/git-store.d.ts +12 -0
  80. package/models/git-store.js +1 -0
  81. package/models/git.d.ts +47 -0
  82. package/models/git.js +1 -0
  83. package/models/i18n-keys.d.ts +2 -0
  84. package/models/i18n-keys.js +1 -0
  85. package/models/index.d.ts +56 -2
  86. package/models/index.js +59 -2
  87. package/models/layout-snapshot-repository.d.ts +14 -0
  88. package/models/layout-snapshot-repository.js +1 -0
  89. package/models/layout-store.d.ts +17 -0
  90. package/models/layout-store.js +1 -0
  91. package/models/layout.d.ts +16 -0
  92. package/models/layout.js +1 -0
  93. package/models/log-repository.d.ts +17 -0
  94. package/models/log-repository.js +1 -0
  95. package/models/log-store.d.ts +15 -0
  96. package/models/log-store.js +1 -0
  97. package/models/log.d.ts +12 -0
  98. package/models/log.js +1 -0
  99. package/models/logger.d.ts +8 -0
  100. package/models/logger.js +1 -0
  101. package/models/menu-action.d.ts +18 -0
  102. package/models/menu-action.js +1 -0
  103. package/models/modal-store.d.ts +16 -0
  104. package/models/modal-store.js +1 -0
  105. package/models/modal.d.ts +10 -2
  106. package/models/note.d.ts +30 -13
  107. package/models/notification-config.d.ts +14 -0
  108. package/models/notification-config.js +1 -0
  109. package/models/notifications-store.d.ts +20 -0
  110. package/models/notifications-store.js +1 -0
  111. package/models/oauth-provider.d.ts +2 -1
  112. package/models/orgnote-config.d.ts +80 -0
  113. package/models/orgnote-config.js +42 -0
  114. package/models/orgnote-url.d.ts +6 -0
  115. package/models/orgnote-url.js +1 -0
  116. package/models/pane-snapshot-repository.d.ts +0 -0
  117. package/models/pane-snapshot-repository.js +0 -0
  118. package/models/pane.d.ts +38 -0
  119. package/models/pane.js +1 -0
  120. package/models/panes-store.d.ts +30 -0
  121. package/models/panes-store.js +1 -0
  122. package/models/platform-detection.d.ts +11 -0
  123. package/models/platform-detection.js +1 -0
  124. package/models/platform-specific.d.ts +1 -0
  125. package/models/platform-specific.js +1 -0
  126. package/models/platform.d.ts +2 -0
  127. package/models/platform.js +1 -0
  128. package/models/queue-store.d.ts +49 -0
  129. package/models/queue-store.js +1 -0
  130. package/models/queue-task.d.ts +15 -0
  131. package/models/queue-task.js +1 -0
  132. package/models/repositories.d.ts +58 -38
  133. package/models/screen-detection.d.ts +10 -0
  134. package/models/screen-detection.js +1 -0
  135. package/models/settings-store.d.ts +12 -0
  136. package/models/settings-store.js +1 -0
  137. package/models/settings-ui-store.d.ts +7 -0
  138. package/models/settings-ui-store.js +1 -0
  139. package/models/sidebar-store.d.ts +22 -0
  140. package/models/sidebar-store.js +1 -0
  141. package/models/splash-screen.d.ts +13 -0
  142. package/models/splash-screen.js +1 -0
  143. package/models/store.d.ts +1 -1
  144. package/models/style-size.d.ts +2 -0
  145. package/models/style-size.js +1 -0
  146. package/models/style-variant.d.ts +1 -0
  147. package/models/style-variant.js +1 -0
  148. package/models/sync-store.d.ts +8 -6
  149. package/models/sync.d.ts +0 -5
  150. package/models/system-info.d.ts +47 -0
  151. package/models/system-info.js +1 -0
  152. package/models/theme-store.d.ts +16 -0
  153. package/models/theme-store.js +1 -0
  154. package/models/theme-variables.d.ts +4 -189
  155. package/models/theme-variables.js +2 -191
  156. package/models/toolbar-store.d.ts +9 -0
  157. package/models/toolbar-store.js +1 -0
  158. package/models/ui-store.d.ts +6 -0
  159. package/models/ui-store.js +1 -0
  160. package/models/user.d.ts +3 -4
  161. package/models/vue-component.d.ts +4 -2
  162. package/package-lock.json +5553 -0
  163. package/package.json +37 -26
  164. package/remote-api/api.d.ts +288 -669
  165. package/remote-api/api.js +199 -485
  166. package/remote-api/base.js +1 -1
  167. package/remote-api/common.d.ts +1 -1
  168. package/sync/__tests__/memory-state.spec.d.ts +1 -0
  169. package/sync/__tests__/memory-state.spec.js +49 -0
  170. package/sync/__tests__/plan.spec.d.ts +1 -0
  171. package/sync/__tests__/plan.spec.js +116 -0
  172. package/sync/create-sync-plan.d.ts +2 -0
  173. package/sync/create-sync-plan.js +13 -0
  174. package/sync/fetch.d.ts +8 -0
  175. package/sync/fetch.js +32 -0
  176. package/sync/index.d.ts +10 -0
  177. package/sync/index.js +9 -0
  178. package/sync/memory-state.d.ts +2 -0
  179. package/sync/memory-state.js +22 -0
  180. package/sync/operations/conflict.d.ts +10 -0
  181. package/sync/operations/conflict.js +56 -0
  182. package/sync/operations/delete-local.d.ts +2 -0
  183. package/sync/operations/delete-local.js +17 -0
  184. package/sync/operations/delete-remote.d.ts +2 -0
  185. package/sync/operations/delete-remote.js +26 -0
  186. package/sync/operations/download.d.ts +2 -0
  187. package/sync/operations/download.js +20 -0
  188. package/sync/operations/index.d.ts +5 -0
  189. package/sync/operations/index.js +5 -0
  190. package/sync/operations/synced-file.d.ts +14 -0
  191. package/sync/operations/synced-file.js +5 -0
  192. package/sync/operations/upload.d.ts +2 -0
  193. package/sync/operations/upload.js +30 -0
  194. package/sync/plan.d.ts +9 -0
  195. package/sync/plan.js +57 -0
  196. package/sync/recovery.d.ts +2 -0
  197. package/sync/recovery.js +6 -0
  198. package/sync/scan.d.ts +4 -0
  199. package/sync/scan.js +40 -0
  200. package/sync/types.d.ts +74 -0
  201. package/sync/types.js +7 -0
  202. package/sync/utils/__tests__/oldest-synced-at.spec.d.ts +1 -0
  203. package/sync/utils/__tests__/oldest-synced-at.spec.js +38 -0
  204. package/sync/utils/oldest-synced-at.d.ts +2 -0
  205. package/sync/utils/oldest-synced-at.js +9 -0
  206. package/types/index.d.ts +0 -0
  207. package/types/index.js +0 -0
  208. package/utils/__tests__/find-files-diff.spec.d.ts +1 -0
  209. package/{tools → utils}/__tests__/find-files-diff.spec.js +3 -3
  210. package/utils/__tests__/find-note-files-diff.spec.d.ts +1 -0
  211. package/{tools → utils}/__tests__/find-note-files-diff.spec.js +5 -5
  212. package/utils/__tests__/get-file-name.spec.d.ts +1 -0
  213. package/utils/__tests__/get-string-path.spec.d.ts +1 -0
  214. package/utils/__tests__/is-gpg-encrypted.spec.d.ts +1 -0
  215. package/utils/__tests__/is-org-file.spec.d.ts +1 -0
  216. package/utils/__tests__/join.spec.d.ts +1 -0
  217. package/utils/__tests__/join.spec.js +32 -0
  218. package/utils/__tests__/nullable-guards.spec.d.ts +1 -0
  219. package/utils/__tests__/nullable-guards.spec.js +44 -0
  220. package/utils/__tests__/parent-folder.spec.d.ts +1 -0
  221. package/utils/__tests__/read-org-files-recursively.spec.d.ts +1 -0
  222. package/utils/__tests__/split-path.spec.d.ts +1 -0
  223. package/utils/__tests__/to-absolute-path.spec.d.ts +1 -0
  224. package/utils/__tests__/to-absolute-path.spec.js +26 -0
  225. package/utils/__tests__/to-error.spec.d.ts +1 -0
  226. package/utils/__tests__/to-error.spec.js +112 -0
  227. package/utils/__tests__/with-root.spec.d.ts +1 -0
  228. package/utils/__tests__/with-root.spec.js +20 -0
  229. package/{tools → utils}/find-notes-files-diff.js +6 -3
  230. package/{tools → utils}/index.d.ts +4 -1
  231. package/{tools → utils}/index.js +4 -1
  232. package/utils/join-path.d.ts +1 -0
  233. package/utils/join-path.js +13 -0
  234. package/utils/nullable-guards.d.ts +2 -0
  235. package/utils/nullable-guards.js +6 -0
  236. package/utils/to-absolute-path.d.ts +2 -0
  237. package/utils/to-absolute-path.js +2 -0
  238. package/utils/to-error.d.ts +6 -0
  239. package/utils/to-error.js +33 -0
  240. package/utils/toml.d.ts +3 -0
  241. package/utils/toml.js +31 -0
  242. package/utils/with-root.d.ts +1 -0
  243. package/utils/with-root.js +6 -0
  244. package/websocket/client.d.ts +24 -0
  245. package/websocket/client.js +83 -0
  246. package/models/file-tree.d.ts +0 -12
  247. package/tools/__tests__/join.spec.js +0 -24
  248. package/tools/join-path.d.ts +0 -1
  249. package/tools/join-path.js +0 -7
  250. package/tools/mock-server.d.ts +0 -1
  251. package/tools/mock-server.js +0 -12
  252. /package/models/{file-cache.js → buffer-store.js} +0 -0
  253. /package/models/{file-tree.js → buffer.js} +0 -0
  254. /package/{tools/__tests__/find-files-diff.spec.d.ts → models/colors.js} +0 -0
  255. /package/{tools/__tests__/find-note-files-diff.spec.d.ts → models/commands-group-store.js} +0 -0
  256. /package/{tools/__tests__/get-file-name.spec.d.ts → models/commands-store.js} +0 -0
  257. /package/{tools/__tests__/get-string-path.spec.d.ts → models/completion-store.js} +0 -0
  258. /package/{tools/__tests__/is-gpg-encrypted.spec.d.ts → models/config-store.js} +0 -0
  259. /package/{tools/__tests__/is-org-file.spec.d.ts → models/confirmation-modal.js} +0 -0
  260. /package/{tools/__tests__/join.spec.d.ts → models/context-menu-store.js} +0 -0
  261. /package/{tools/__tests__/parent-folder.spec.d.ts → models/cron-store.js} +0 -0
  262. /package/{tools/__tests__/read-org-files-recursively.spec.d.ts → models/cron-task.js} +0 -0
  263. /package/{tools/__tests__/split-path.spec.d.ts → models/css-utils.js} +0 -0
  264. /package/{tools → utils}/__tests__/get-file-name.spec.js +0 -0
  265. /package/{tools → utils}/__tests__/get-string-path.spec.js +0 -0
  266. /package/{tools → utils}/__tests__/is-gpg-encrypted.spec.js +0 -0
  267. /package/{tools → utils}/__tests__/is-org-file.spec.js +0 -0
  268. /package/{tools → utils}/__tests__/parent-folder.spec.js +0 -0
  269. /package/{tools → utils}/__tests__/read-org-files-recursively.spec.js +0 -0
  270. /package/{tools → utils}/__tests__/split-path.spec.js +0 -0
  271. /package/{tools → utils}/find-notes-files-diff.d.ts +0 -0
  272. /package/{tools → utils}/get-file-name.d.ts +0 -0
  273. /package/{tools → utils}/get-file-name.js +0 -0
  274. /package/{tools → utils}/get-parent-dir.d.ts +0 -0
  275. /package/{tools → utils}/get-parent-dir.js +0 -0
  276. /package/{tools → utils}/get-string-path.d.ts +0 -0
  277. /package/{tools → utils}/get-string-path.js +0 -0
  278. /package/{tools → utils}/is-gpg-encrypted.d.ts +0 -0
  279. /package/{tools → utils}/is-gpg-encrypted.js +0 -0
  280. /package/{tools → utils}/is-org-file.d.ts +0 -0
  281. /package/{tools → utils}/is-org-file.js +0 -0
  282. /package/{tools → utils}/split-path.d.ts +0 -0
  283. /package/{tools → utils}/split-path.js +0 -0
@@ -0,0 +1,74 @@
1
+ import type { FileSystem } from '../models/file-system.js';
2
+ import type { FileChange, SyncApiFactory } from '../remote-api/index.js';
3
+ export type SyncApi = ReturnType<typeof SyncApiFactory>;
4
+ export type SyncStatus = 'synced' | 'dirty' | 'uploading' | 'downloading' | 'error';
5
+ export declare enum SyncOperationType {
6
+ Upload = "upload",
7
+ Download = "download",
8
+ DeleteLocal = "deleteLocal",
9
+ DeleteRemote = "deleteRemote"
10
+ }
11
+ export interface SyncedFile {
12
+ mtime: number;
13
+ size: number;
14
+ version?: number;
15
+ status: SyncStatus;
16
+ syncedAt?: string;
17
+ conflictPath?: string;
18
+ errorMessage?: string;
19
+ }
20
+ export interface SyncStateData {
21
+ files: Record<string, SyncedFile>;
22
+ }
23
+ export interface SyncState {
24
+ get(): Promise<SyncStateData>;
25
+ getFile(path: string): Promise<SyncedFile | null>;
26
+ setFile(path: string, file: SyncedFile): Promise<void>;
27
+ removeFile(path: string): Promise<void>;
28
+ clear(): Promise<void>;
29
+ }
30
+ export interface LocalFile {
31
+ path: string;
32
+ mtime: number;
33
+ size: number;
34
+ }
35
+ export type RemoteFile = Pick<FileChange, 'path' | 'version' | 'deleted' | 'updatedAt'>;
36
+ export type UploadResult = {
37
+ status: 'ok';
38
+ version: number;
39
+ } | {
40
+ status: 'conflict';
41
+ serverVersion: number;
42
+ };
43
+ export interface SyncExecutor {
44
+ upload: (file: LocalFile, expectedVersion?: number) => Promise<UploadResult>;
45
+ download: (file: RemoteFile) => Promise<void>;
46
+ deleteLocal: (path: string) => Promise<void>;
47
+ deleteRemote: (path: string, expectedVersion: number) => Promise<void>;
48
+ }
49
+ export interface SyncPlan {
50
+ toUpload: LocalFile[];
51
+ toDownload: RemoteFile[];
52
+ toDeleteLocal: string[];
53
+ toDeleteRemote: string[];
54
+ serverTime: string;
55
+ }
56
+ export interface SyncTask {
57
+ path: string;
58
+ operation: SyncOperationType;
59
+ file?: LocalFile | RemoteFile;
60
+ }
61
+ export interface CreateSyncPlanParams {
62
+ fs: FileSystem;
63
+ api: SyncApi;
64
+ state: SyncState;
65
+ rootPath: string;
66
+ ignorePatterns?: string[];
67
+ }
68
+ export interface SyncContext {
69
+ executor: SyncExecutor;
70
+ state: SyncState;
71
+ fs: FileSystem;
72
+ serverTime: string;
73
+ deviceName?: string;
74
+ }
package/sync/types.js ADDED
@@ -0,0 +1,7 @@
1
+ export var SyncOperationType;
2
+ (function (SyncOperationType) {
3
+ SyncOperationType["Upload"] = "upload";
4
+ SyncOperationType["Download"] = "download";
5
+ SyncOperationType["DeleteLocal"] = "deleteLocal";
6
+ SyncOperationType["DeleteRemote"] = "deleteRemote";
7
+ })(SyncOperationType || (SyncOperationType = {}));
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ import { test, expect } from 'vitest';
2
+ import { getOldestSyncedAt } from "../oldest-synced-at.js";
3
+ const createFile = (syncedAt) => ({
4
+ mtime: 1000,
5
+ size: 100,
6
+ status: 'synced',
7
+ syncedAt,
8
+ });
9
+ test('returns undefined when no files', () => {
10
+ const state = { files: {} };
11
+ expect(getOldestSyncedAt(state)).toBeUndefined();
12
+ });
13
+ test('returns undefined when no files have syncedAt', () => {
14
+ const state = {
15
+ files: { '/a.org': createFile() },
16
+ };
17
+ expect(getOldestSyncedAt(state)).toBeUndefined();
18
+ });
19
+ test('returns oldest syncedAt from files', () => {
20
+ const state = {
21
+ files: {
22
+ '/a.org': createFile('2024-01-03T00:00:00Z'),
23
+ '/b.org': createFile('2024-01-01T00:00:00Z'),
24
+ '/c.org': createFile('2024-01-02T00:00:00Z'),
25
+ },
26
+ };
27
+ expect(getOldestSyncedAt(state)).toBe('2024-01-01T00:00:00Z');
28
+ });
29
+ test('ignores files without syncedAt', () => {
30
+ const state = {
31
+ files: {
32
+ '/a.org': createFile('2024-01-02T00:00:00Z'),
33
+ '/b.org': createFile(),
34
+ '/c.org': createFile('2024-01-03T00:00:00Z'),
35
+ },
36
+ };
37
+ expect(getOldestSyncedAt(state)).toBe('2024-01-02T00:00:00Z');
38
+ });
@@ -0,0 +1,2 @@
1
+ import type { SyncStateData } from '../types.js';
2
+ export declare const getOldestSyncedAt: (stateData: SyncStateData) => string | undefined;
@@ -0,0 +1,9 @@
1
+ export const getOldestSyncedAt = (stateData) => {
2
+ const syncedTimes = Object.values(stateData.files)
3
+ .map((f) => f.syncedAt)
4
+ .filter((t) => Boolean(t));
5
+ if (syncedTimes.length === 0) {
6
+ return undefined;
7
+ }
8
+ return syncedTimes.reduce((oldest, current) => new Date(current) < new Date(oldest) ? current : oldest);
9
+ };
File without changes
package/types/index.js ADDED
File without changes
@@ -0,0 +1 @@
1
+ export {};
@@ -2,7 +2,7 @@ import { afterEach, beforeEach, expect, test } from 'vitest';
2
2
  import { mkdirSync, rmdirSync, statSync, utimesSync, writeFileSync } from 'fs';
3
3
  import { join } from 'path';
4
4
  import { findFilesDiff } from "../find-notes-files-diff.js";
5
- const testFilesFolder = 'src/tools/__tests__/miscellaneous2/';
5
+ const testFilesFolder = 'src/utils/__tests__/miscellaneous2/';
6
6
  function initFiles() {
7
7
  mkdirSync(testFilesFolder);
8
8
  mkdirSync(testFilesFolder + '/nested-folder');
@@ -83,8 +83,8 @@ test('Should find files diff when folder was renamed', () => {
83
83
  {
84
84
  "created": [],
85
85
  "deleted": [
86
- "src/tools/__tests__/miscellaneous2/nested-folder/org-file.org",
87
- "src/tools/__tests__/miscellaneous2/nested-folder/org-file2.org",
86
+ "src/utils/__tests__/miscellaneous2/nested-folder/org-file.org",
87
+ "src/utils/__tests__/miscellaneous2/nested-folder/org-file2.org",
88
88
  ],
89
89
  "updated": [],
90
90
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -5,7 +5,7 @@ import { statSync } from 'fs';
5
5
  import { rmSync } from 'fs';
6
6
  import { getFileName } from "../get-file-name.js";
7
7
  import { join } from "../join-path.js";
8
- const testFilesFolder = 'src/tools/__tests__/miscellaneous/';
8
+ const testFilesFolder = 'src/utils/__tests__/miscellaneous/';
9
9
  const nestedFolder = 'nested-folder/';
10
10
  const fn = (fileName) => `${testFilesFolder}${fileName}`;
11
11
  const fns = (fileName) => `${testFilesFolder}${nestedFolder}${fileName}`;
@@ -95,21 +95,21 @@ test('Should sync notes files', async () => {
95
95
  {
96
96
  "created": [
97
97
  {
98
- "filePath": "src/tools/__tests__/miscellaneous/file-2.org",
98
+ "filePath": "src/utils/__tests__/miscellaneous/file-2.org",
99
99
  },
100
100
  {
101
- "filePath": "src/tools/__tests__/miscellaneous/nested-folder/file-4.org",
101
+ "filePath": "src/utils/__tests__/miscellaneous/nested-folder/file-4.org",
102
102
  },
103
103
  ],
104
104
  "deleted": [
105
105
  {
106
- "filePath": "src/tools/__tests__/miscellaneous/nested-folder/file-10.org",
106
+ "filePath": "src/utils/__tests__/miscellaneous/nested-folder/file-10.org",
107
107
  "id": "10",
108
108
  },
109
109
  ],
110
110
  "updated": [
111
111
  {
112
- "filePath": "src/tools/__tests__/miscellaneous/file-9.org",
112
+ "filePath": "src/utils/__tests__/miscellaneous/file-9.org",
113
113
  },
114
114
  ],
115
115
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ import { test, expect } from 'vitest';
2
+ import { join } from "../join-path.js";
3
+ test('joins relative paths', () => {
4
+ expect(join('dir1', 'subdir2', 'file')).toBe('dir1/subdir2/file');
5
+ });
6
+ test('joins paths with trailing slashes', () => {
7
+ expect(join('dir2/', '/file')).toBe('dir2/file');
8
+ });
9
+ test('joins absolute path with subdirs', () => {
10
+ expect(join('/dir3', 'subdir2/', 'file')).toBe('/dir3/subdir2/file');
11
+ });
12
+ test('returns single path as is', () => {
13
+ expect(join('file')).toBe('file');
14
+ });
15
+ test('preserves absolute path when first arg starts with /', () => {
16
+ expect(join('/', 'this', 'is-path')).toBe('/this/is-path');
17
+ });
18
+ test('filters standalone slashes in the middle', () => {
19
+ expect(join('path', '/', 'to', '/', 'file')).toBe('path/to/file');
20
+ });
21
+ test('handles empty string without adding slash', () => {
22
+ expect(join('', 'file')).toBe('file');
23
+ });
24
+ test('returns / for root only', () => {
25
+ expect(join('/')).toBe('/');
26
+ });
27
+ test('returns / for root with empty segments', () => {
28
+ expect(join('/', '')).toBe('/');
29
+ });
30
+ test('returns empty for empty input', () => {
31
+ expect(join('')).toBe('');
32
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ import { test, expect } from 'vitest';
2
+ import { isNullable, isPresent } from "../nullable-guards.js";
3
+ test('isNullable returns true for null', () => {
4
+ expect(isNullable(null)).toBe(true);
5
+ });
6
+ test('isNullable returns true for undefined', () => {
7
+ expect(isNullable(undefined)).toBe(true);
8
+ });
9
+ test('isNullable returns false for valid values', () => {
10
+ expect(isNullable(0)).toBe(false);
11
+ expect(isNullable('')).toBe(false);
12
+ expect(isNullable(false)).toBe(false);
13
+ expect(isNullable([])).toBe(false);
14
+ });
15
+ test('isNullable returns true for NaN', () => {
16
+ expect(isNullable(NaN)).toBe(true);
17
+ });
18
+ test('isPresent returns false for null', () => {
19
+ expect(isPresent(null)).toBe(false);
20
+ });
21
+ test('isPresent returns false for undefined', () => {
22
+ expect(isPresent(undefined)).toBe(false);
23
+ });
24
+ test('isPresent returns true for valid values', () => {
25
+ expect(isPresent(0)).toBe(true);
26
+ expect(isPresent('')).toBe(true);
27
+ expect(isPresent(false)).toBe(true);
28
+ expect(isPresent([])).toBe(true);
29
+ });
30
+ test('isPresent returns false for NaN', () => {
31
+ expect(isPresent(NaN)).toBe(false);
32
+ });
33
+ test('type guards work with TypeScript narrowing', () => {
34
+ const value = 'test';
35
+ if (isPresent(value)) {
36
+ const upper = value.toUpperCase();
37
+ expect(upper).toBe('TEST');
38
+ }
39
+ });
40
+ test('isPresent filters array correctly', () => {
41
+ const arr = [1, null, 2, undefined, 3];
42
+ const filtered = arr.filter(isPresent);
43
+ expect(filtered).toEqual([1, 2, 3]);
44
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { test, expect } from 'vitest';
2
+ import { toAbsolutePath, toRelativePath } from "../to-absolute-path.js";
3
+ test('toAbsolutePath adds leading slash to relative path', () => {
4
+ expect(toAbsolutePath('folder/file.org')).toBe('/folder/file.org');
5
+ });
6
+ test('toAbsolutePath keeps absolute path unchanged', () => {
7
+ expect(toAbsolutePath('/folder/file.org')).toBe('/folder/file.org');
8
+ });
9
+ test('toAbsolutePath handles empty string', () => {
10
+ expect(toAbsolutePath('')).toBe('/');
11
+ });
12
+ test('toAbsolutePath handles root path', () => {
13
+ expect(toAbsolutePath('/')).toBe('/');
14
+ });
15
+ test('toRelativePath removes leading slash from absolute path', () => {
16
+ expect(toRelativePath('/folder/file.org')).toBe('folder/file.org');
17
+ });
18
+ test('toRelativePath keeps relative path unchanged', () => {
19
+ expect(toRelativePath('folder/file.org')).toBe('folder/file.org');
20
+ });
21
+ test('toRelativePath handles root path', () => {
22
+ expect(toRelativePath('/')).toBe('');
23
+ });
24
+ test('toRelativePath handles empty string', () => {
25
+ expect(toRelativePath('')).toBe('');
26
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,112 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { to } from "../to-error.js";
3
+ import { ok } from 'neverthrow';
4
+ describe('to-error utility', () => {
5
+ describe('synchronous functions', () => {
6
+ it('should return Ok(result) when function succeeds', () => {
7
+ const fn = () => 42;
8
+ const wrapped = to(fn);
9
+ const result = wrapped();
10
+ expect(result).toEqual(ok(42));
11
+ });
12
+ it('should return Err(Error) when function throws', () => {
13
+ const fn = () => {
14
+ throw new Error('boom');
15
+ };
16
+ const wrapped = to(fn);
17
+ const result = wrapped();
18
+ expect(result.isErr()).toBe(true);
19
+ if (result.isErr()) {
20
+ expect(result.error).toBeInstanceOf(Error);
21
+ expect(result.error.message).toBe('boom');
22
+ }
23
+ });
24
+ it('should use custom error mapper function', () => {
25
+ const fn = () => {
26
+ throw new Error('boom');
27
+ };
28
+ const mapper = (e) => new Error(`Custom: ${e instanceof Error ? e.message : e}`);
29
+ const wrapped = to(fn, mapper);
30
+ const result = wrapped();
31
+ expect(result.isErr()).toBe(true);
32
+ if (result.isErr()) {
33
+ expect(result.error.message).toBe('Custom: boom');
34
+ }
35
+ });
36
+ it('should wrap error with message when string is provided', () => {
37
+ const fn = () => {
38
+ throw new Error('original error');
39
+ };
40
+ const wrapped = to(fn, 'Context message');
41
+ const result = wrapped();
42
+ expect(result.isErr()).toBe(true);
43
+ if (result.isErr()) {
44
+ expect(result.error.message).toBe('Context message');
45
+ expect(result.error.cause).toBeInstanceOf(Error);
46
+ expect(result.error.cause.message).toBe('original error');
47
+ }
48
+ });
49
+ });
50
+ describe('asynchronous functions', () => {
51
+ it('should return Ok(result) when promise resolves', async () => {
52
+ const fn = async () => 42;
53
+ const wrapped = to(fn);
54
+ const result = await wrapped();
55
+ expect(result).toEqual(ok(42));
56
+ });
57
+ it('should return Err(Error) when promise rejects', async () => {
58
+ const fn = async () => {
59
+ throw new Error('async boom');
60
+ };
61
+ const wrapped = to(fn);
62
+ const result = await wrapped();
63
+ expect(result.isErr()).toBe(true);
64
+ if (result.isErr()) {
65
+ expect(result.error).toBeInstanceOf(Error);
66
+ expect(result.error.message).toBe('async boom');
67
+ }
68
+ });
69
+ it('should map async errors', async () => {
70
+ const fn = async () => {
71
+ throw new Error('async boom');
72
+ };
73
+ const mapper = () => new Error('mapped async error');
74
+ const wrapped = to(fn, mapper);
75
+ const result = await wrapped();
76
+ expect(result.isErr()).toBe(true);
77
+ if (result.isErr()) {
78
+ expect(result.error.message).toBe('mapped async error');
79
+ }
80
+ });
81
+ });
82
+ describe('argument passing', () => {
83
+ it('should pass arguments to the original function', () => {
84
+ const fn = (a, b) => a + b;
85
+ const wrapped = to(fn);
86
+ const result = wrapped(2, 3);
87
+ expect(result).toEqual(ok(5));
88
+ });
89
+ it('should pass arguments to async function', async () => {
90
+ const fn = async (a) => `Hello ${a}`;
91
+ const wrapped = to(fn);
92
+ const result = await wrapped('World');
93
+ expect(result).toEqual(ok('Hello World'));
94
+ });
95
+ });
96
+ describe('context binding', () => {
97
+ it('should preserve this context', () => {
98
+ class Calculator {
99
+ multiplier;
100
+ constructor(multiplier) {
101
+ this.multiplier = multiplier;
102
+ }
103
+ multiply(value) {
104
+ return value * this.multiplier;
105
+ }
106
+ }
107
+ const calc = new Calculator(2);
108
+ const wrapped = to(calc.multiply.bind(calc));
109
+ expect(wrapped(3)).toEqual(ok(6));
110
+ });
111
+ });
112
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { expect, test } from 'vitest';
2
+ import { withRoot } from "../with-root.js";
3
+ test('adds root to path without leading slash', () => {
4
+ expect(withRoot('test')).toBe('/test');
5
+ });
6
+ test('leaves path unchanged if starts with slash', () => {
7
+ expect(withRoot('/test')).toBe('/test');
8
+ });
9
+ test('handles empty string', () => {
10
+ expect(withRoot('')).toBe('/');
11
+ });
12
+ test('handles multiple leading slashes', () => {
13
+ expect(withRoot('//test')).toBe('//test');
14
+ });
15
+ test('handles path with trailing slash', () => {
16
+ expect(withRoot('test/')).toBe('/test/');
17
+ });
18
+ test('handles root path', () => {
19
+ expect(withRoot('/')).toBe('/');
20
+ });
@@ -20,10 +20,12 @@ export async function getOrgFilesFromLastSync(filePaths, fileInfo, lastSync) {
20
20
  if (!lastSync) {
21
21
  return filePaths;
22
22
  }
23
- return Promise.all(filePaths.filter(async (filePath) => {
23
+ const results = await Promise.all(filePaths.map(async (filePath) => {
24
24
  const stats = await fileInfo(filePath);
25
- return (new Date(stats.mtime) > lastSync || new Date(stats.ctime) > lastSync);
25
+ const isModified = new Date(stats.mtime) > lastSync || new Date(stats.ctime) > lastSync;
26
+ return isModified ? filePath : null;
26
27
  }));
28
+ return results.filter((p) => p !== null);
27
29
  }
28
30
  function findDeletedNotes(filePaths, storedNotesInfo) {
29
31
  const uniqueFilePaths = new Set(filePaths);
@@ -50,7 +52,8 @@ async function findUpdatedCreatedNotes(fileInfo, orgFilePaths, storedNotesInfo)
50
52
  continue;
51
53
  }
52
54
  const fileUpdatedTime = new Date((await fileInfo(f))?.mtime);
53
- if (found.updatedAt < fileUpdatedTime) {
55
+ const storedUpdatedTime = new Date(found.updatedAt);
56
+ if (storedUpdatedTime < fileUpdatedTime) {
54
57
  updated.push({ filePath: f });
55
58
  }
56
59
  }
@@ -1,4 +1,3 @@
1
- export * from './mock-server.js';
2
1
  export * from './is-gpg-encrypted.js';
3
2
  export * from './is-org-file.js';
4
3
  export * from './get-string-path.js';
@@ -7,3 +6,7 @@ export * from './find-notes-files-diff.js';
7
6
  export * from './join-path.js';
8
7
  export * from './split-path.js';
9
8
  export * from './get-parent-dir.js';
9
+ export * from './toml.js';
10
+ export * from './to-absolute-path.js';
11
+ export * from './to-error.js';
12
+ export * from './nullable-guards.js';
@@ -1,4 +1,3 @@
1
- export * from "./mock-server.js";
2
1
  export * from "./is-gpg-encrypted.js";
3
2
  export * from "./is-org-file.js";
4
3
  export * from "./get-string-path.js";
@@ -7,3 +6,7 @@ export * from "./find-notes-files-diff.js";
7
6
  export * from "./join-path.js";
8
7
  export * from "./split-path.js";
9
8
  export * from "./get-parent-dir.js";
9
+ export * from "./toml.js";
10
+ export * from "./to-absolute-path.js";
11
+ export * from "./to-error.js";
12
+ export * from "./nullable-guards.js";
@@ -0,0 +1 @@
1
+ export declare function join(...paths: string[]): string;
@@ -0,0 +1,13 @@
1
+ export function join(...paths) {
2
+ const isAbsolute = paths[0]?.startsWith('/');
3
+ const result = paths
4
+ .filter((p) => p !== '/' && p)
5
+ .join('/')
6
+ .replace(/\/+/g, '/')
7
+ .replace(/\/+$/, '');
8
+ if (!result)
9
+ return isAbsolute ? '/' : '';
10
+ if (isAbsolute && !result.startsWith('/'))
11
+ return `/${result}`;
12
+ return result;
13
+ }
@@ -0,0 +1,2 @@
1
+ export declare function isNullable<T>(value: T | null | undefined): value is null | undefined;
2
+ export declare function isPresent<T>(value: T | null | undefined): value is T;
@@ -0,0 +1,6 @@
1
+ export function isNullable(value) {
2
+ return (value === null || value === undefined || (typeof value === 'number' && Number.isNaN(value)));
3
+ }
4
+ export function isPresent(value) {
5
+ return (value !== null && value !== undefined && !(typeof value === 'number' && Number.isNaN(value)));
6
+ }
@@ -0,0 +1,2 @@
1
+ export declare const toAbsolutePath: (path: string) => string;
2
+ export declare const toRelativePath: (path: string) => string;
@@ -0,0 +1,2 @@
1
+ export const toAbsolutePath = (path) => path.startsWith('/') ? path : `/${path}`;
2
+ export const toRelativePath = (path) => path.startsWith('/') ? path.slice(1) : path;
@@ -0,0 +1,6 @@
1
+ import type { Result } from 'neverthrow';
2
+ import { ResultAsync } from 'neverthrow';
3
+ type MapperOrMsg<E> = ((e: unknown) => E) | string;
4
+ export declare function to<R, TThis = unknown, A extends any[] = any[], E = Error>(fn: (this: TThis, ...args: A) => Promise<R>, mapErrorOrMsg?: MapperOrMsg<E>): (this: TThis, ...args: A) => ResultAsync<R, E>;
5
+ export declare function to<R, TThis = unknown, A extends any[] = any[], E = Error>(fn: (this: TThis, ...args: A) => R, mapErrorOrMsg?: MapperOrMsg<E>): (this: TThis, ...args: A) => Result<R, E>;
6
+ export {};
@@ -0,0 +1,33 @@
1
+ import { ResultAsync, ok, err } from 'neverthrow';
2
+ import { isPresent } from "./nullable-guards.js";
3
+ const defaultToError = (e) => (e instanceof Error ? e : new Error(String(e)));
4
+ function isPromiseLike(x) {
5
+ return (typeof x === 'object' &&
6
+ isPresent(x) &&
7
+ 'then' in x &&
8
+ typeof x.then === 'function');
9
+ }
10
+ function resolveMapper(map) {
11
+ if (typeof map === 'function')
12
+ return map;
13
+ if (typeof map === 'string') {
14
+ const msg = map;
15
+ return (e) => new Error(msg, { cause: e instanceof Error ? e : new Error(String(e)) });
16
+ }
17
+ return defaultToError;
18
+ }
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ export function to(fn, mapErrorOrMsg) {
21
+ const mapError = resolveMapper(mapErrorOrMsg);
22
+ return function (...args) {
23
+ try {
24
+ const out = fn.apply(this, args);
25
+ if (isPromiseLike(out))
26
+ return ResultAsync.fromPromise(out, mapError);
27
+ return ok(out);
28
+ }
29
+ catch (e) {
30
+ return err(mapError(e));
31
+ }
32
+ };
33
+ }
@@ -0,0 +1,3 @@
1
+ import { BaseSchema, InferOutput } from 'valibot';
2
+ export declare function parseToml<T = unknown, S extends BaseSchema<unknown, unknown, any> | undefined = undefined>(content: string, schema?: S): S extends BaseSchema<unknown, unknown, any> ? InferOutput<S> : T;
3
+ export declare function stringifyToml(data: unknown): string;