@rewrlution/papyrus-cli 0.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.
Files changed (125) hide show
  1. package/README.md +699 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +16 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/auth/index.d.ts +2 -0
  6. package/dist/commands/auth/index.js +18 -0
  7. package/dist/commands/auth/index.js.map +1 -0
  8. package/dist/commands/auth/login.d.ts +1 -0
  9. package/dist/commands/auth/login.js +8 -0
  10. package/dist/commands/auth/login.js.map +1 -0
  11. package/dist/commands/auth/logout.d.ts +1 -0
  12. package/dist/commands/auth/logout.js +5 -0
  13. package/dist/commands/auth/logout.js.map +1 -0
  14. package/dist/commands/auth/register.d.ts +1 -0
  15. package/dist/commands/auth/register.js +8 -0
  16. package/dist/commands/auth/register.js.map +1 -0
  17. package/dist/commands/index.d.ts +2 -0
  18. package/dist/commands/index.js +3 -0
  19. package/dist/commands/index.js.map +1 -0
  20. package/dist/commands/journal/add.d.ts +2 -0
  21. package/dist/commands/journal/add.js +5 -0
  22. package/dist/commands/journal/add.js.map +1 -0
  23. package/dist/commands/journal/amend.d.ts +2 -0
  24. package/dist/commands/journal/amend.js +5 -0
  25. package/dist/commands/journal/amend.js.map +1 -0
  26. package/dist/commands/journal/edit.d.ts +6 -0
  27. package/dist/commands/journal/edit.js +60 -0
  28. package/dist/commands/journal/edit.js.map +1 -0
  29. package/dist/commands/journal/index.d.ts +2 -0
  30. package/dist/commands/journal/index.js +32 -0
  31. package/dist/commands/journal/index.js.map +1 -0
  32. package/dist/commands/journal/list.d.ts +1 -0
  33. package/dist/commands/journal/list.js +20 -0
  34. package/dist/commands/journal/list.js.map +1 -0
  35. package/dist/commands/journal/show.d.ts +2 -0
  36. package/dist/commands/journal/show.js +35 -0
  37. package/dist/commands/journal/show.js.map +1 -0
  38. package/dist/commands/journal/sync.d.ts +1 -0
  39. package/dist/commands/journal/sync.js +9 -0
  40. package/dist/commands/journal/sync.js.map +1 -0
  41. package/dist/commands/types.d.ts +9 -0
  42. package/dist/commands/types.js +2 -0
  43. package/dist/commands/types.js.map +1 -0
  44. package/dist/components/Browser.d.ts +29 -0
  45. package/dist/components/Browser.js +124 -0
  46. package/dist/components/Browser.js.map +1 -0
  47. package/dist/components/BrowserFooter.d.ts +6 -0
  48. package/dist/components/BrowserFooter.js +6 -0
  49. package/dist/components/BrowserFooter.js.map +1 -0
  50. package/dist/components/BrowserHeader.d.ts +6 -0
  51. package/dist/components/BrowserHeader.js +6 -0
  52. package/dist/components/BrowserHeader.js.map +1 -0
  53. package/dist/components/ColdStart.d.ts +6 -0
  54. package/dist/components/ColdStart.js +24 -0
  55. package/dist/components/ColdStart.js.map +1 -0
  56. package/dist/components/FormInput.d.ts +10 -0
  57. package/dist/components/FormInput.js +7 -0
  58. package/dist/components/FormInput.js.map +1 -0
  59. package/dist/components/JournalListView.d.ts +10 -0
  60. package/dist/components/JournalListView.js +40 -0
  61. package/dist/components/JournalListView.js.map +1 -0
  62. package/dist/components/JournalViewer.d.ts +32 -0
  63. package/dist/components/JournalViewer.js +146 -0
  64. package/dist/components/JournalViewer.js.map +1 -0
  65. package/dist/components/LoginForm.d.ts +1 -0
  66. package/dist/components/LoginForm.js +68 -0
  67. package/dist/components/LoginForm.js.map +1 -0
  68. package/dist/components/Logo.d.ts +1 -0
  69. package/dist/components/Logo.js +57 -0
  70. package/dist/components/Logo.js.map +1 -0
  71. package/dist/components/RegisterForm.d.ts +1 -0
  72. package/dist/components/RegisterForm.js +72 -0
  73. package/dist/components/RegisterForm.js.map +1 -0
  74. package/dist/components/StatusMessage.d.ts +7 -0
  75. package/dist/components/StatusMessage.js +19 -0
  76. package/dist/components/StatusMessage.js.map +1 -0
  77. package/dist/components/SyncProgress.d.ts +1 -0
  78. package/dist/components/SyncProgress.js +46 -0
  79. package/dist/components/SyncProgress.js.map +1 -0
  80. package/dist/lib/api/api-client.d.ts +23 -0
  81. package/dist/lib/api/api-client.js +111 -0
  82. package/dist/lib/api/api-client.js.map +1 -0
  83. package/dist/lib/api/index.d.ts +3 -0
  84. package/dist/lib/api/index.js +6 -0
  85. package/dist/lib/api/index.js.map +1 -0
  86. package/dist/lib/auth/index.d.ts +1 -0
  87. package/dist/lib/auth/index.js +2 -0
  88. package/dist/lib/auth/index.js.map +1 -0
  89. package/dist/lib/auth/require-auth.d.ts +67 -0
  90. package/dist/lib/auth/require-auth.js +107 -0
  91. package/dist/lib/auth/require-auth.js.map +1 -0
  92. package/dist/lib/storage/base-storage.d.ts +50 -0
  93. package/dist/lib/storage/base-storage.js +91 -0
  94. package/dist/lib/storage/base-storage.js.map +1 -0
  95. package/dist/lib/storage/config-store.d.ts +40 -0
  96. package/dist/lib/storage/config-store.js +63 -0
  97. package/dist/lib/storage/config-store.js.map +1 -0
  98. package/dist/lib/storage/index.d.ts +12 -0
  99. package/dist/lib/storage/index.js +13 -0
  100. package/dist/lib/storage/index.js.map +1 -0
  101. package/dist/lib/storage/journal-storage.d.ts +46 -0
  102. package/dist/lib/storage/journal-storage.js +78 -0
  103. package/dist/lib/storage/journal-storage.js.map +1 -0
  104. package/dist/lib/storage/sync-meta-store.d.ts +37 -0
  105. package/dist/lib/storage/sync-meta-store.js +50 -0
  106. package/dist/lib/storage/sync-meta-store.js.map +1 -0
  107. package/dist/lib/storage/token-store.d.ts +25 -0
  108. package/dist/lib/storage/token-store.js +40 -0
  109. package/dist/lib/storage/token-store.js.map +1 -0
  110. package/dist/lib/sync/sync-engine.d.ts +13 -0
  111. package/dist/lib/sync/sync-engine.js +96 -0
  112. package/dist/lib/sync/sync-engine.js.map +1 -0
  113. package/dist/utils/date.d.ts +58 -0
  114. package/dist/utils/date.js +117 -0
  115. package/dist/utils/date.js.map +1 -0
  116. package/dist/utils/editor.d.ts +2 -0
  117. package/dist/utils/editor.js +81 -0
  118. package/dist/utils/editor.js.map +1 -0
  119. package/dist/utils/template.d.ts +2 -0
  120. package/dist/utils/template.js +17 -0
  121. package/dist/utils/template.js.map +1 -0
  122. package/dist/utils/token.d.ts +20 -0
  123. package/dist/utils/token.js +64 -0
  124. package/dist/utils/token.js.map +1 -0
  125. package/package.json +53 -0
@@ -0,0 +1,111 @@
1
+ import axios from 'axios';
2
+ import { tokenStore } from '../storage/index.js';
3
+ /**
4
+ * API Client for Papyrus server
5
+ * Uses shared types and storage layer for tokens
6
+ *
7
+ * Error handling strategy:
8
+ * - Validation errors: Caught by Zod client-side (before API call)
9
+ * - API errors: Descriptive messages from ApiErrorResponse
10
+ * - Network errors: Wrapped with descriptive message
11
+ */
12
+ export class ApiClient {
13
+ http;
14
+ constructor(baseUrl) {
15
+ this.http = axios.create({
16
+ baseURL: baseUrl,
17
+ timeout: 90000, // set api timeout to 90 sec. serverless app warm-up may take up to a minute.
18
+ headers: {
19
+ 'Content-Type': 'application/json',
20
+ },
21
+ });
22
+ // Add token to requests automatically from storage layer
23
+ this.http.interceptors.request.use((config) => {
24
+ const token = tokenStore.get();
25
+ if (token) {
26
+ config.headers.Authorization = `Bearer ${token}`;
27
+ }
28
+ return config;
29
+ });
30
+ }
31
+ async register(credentials) {
32
+ try {
33
+ const response = await this.http.post('/auth/signup', credentials);
34
+ return response.data.data;
35
+ }
36
+ catch (error) {
37
+ this.handleError(error);
38
+ }
39
+ }
40
+ async login(credentials) {
41
+ try {
42
+ const response = await this.http.post('/auth/signin', credentials);
43
+ // Save token to storage layer
44
+ if (response.data.data.token) {
45
+ tokenStore.save(response.data.data.token);
46
+ }
47
+ return response.data.data;
48
+ }
49
+ catch (error) {
50
+ this.handleError(error);
51
+ }
52
+ }
53
+ logout() {
54
+ tokenStore.clear();
55
+ }
56
+ isAuthenticated() {
57
+ return tokenStore.exists();
58
+ }
59
+ async listJournalsMetadata() {
60
+ try {
61
+ const response = await this.http.get('/journals/metadata');
62
+ return response.data.data;
63
+ }
64
+ catch (error) {
65
+ this.handleError(error);
66
+ }
67
+ }
68
+ async getJournal(date) {
69
+ try {
70
+ const response = await this.http.get(`/journals/${date}`);
71
+ return response.data.data;
72
+ }
73
+ catch (error) {
74
+ this.handleError(error);
75
+ }
76
+ }
77
+ async createJournal(date, content) {
78
+ try {
79
+ const response = await this.http.post('/journals', {
80
+ date,
81
+ content,
82
+ });
83
+ return response.data.data;
84
+ }
85
+ catch (error) {
86
+ this.handleError(error);
87
+ }
88
+ }
89
+ async updateJournal(date, content) {
90
+ try {
91
+ const response = await this.http.put(`/journals/${date}`, { content });
92
+ return response.data.data;
93
+ }
94
+ catch (error) {
95
+ this.handleError(error);
96
+ }
97
+ }
98
+ handleError(error) {
99
+ if (axios.isAxiosError(error)) {
100
+ // API returned an error response with message
101
+ if (error.response?.data.message) {
102
+ throw new Error(error.response.data.message);
103
+ }
104
+ // Network error (connection refused, timeout, DNS failure)
105
+ throw new Error(`Network error: ${error.message}`);
106
+ }
107
+ // Unknown error
108
+ throw new Error(error instanceof Error ? error.message : 'unknown error');
109
+ }
110
+ }
111
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/lib/api/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { ApiClient } from './api-client.js';
2
+ export declare const api: ApiClient;
3
+ export { ApiClient } from './api-client.js';
@@ -0,0 +1,6 @@
1
+ import { configStore } from '../storage/index.js';
2
+ import { ApiClient } from './api-client.js';
3
+ const API_BASE_URL = configStore.get('apiUrl') || 'http://localhost:3000/api';
4
+ export const api = new ApiClient(API_BASE_URL);
5
+ export { ApiClient } from './api-client.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,2BAA2B,CAAC;AAC9E,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,YAAY,CAAC,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1 @@
1
+ export { requireAuth, ensureAuthenticated, type AuthCheckResult, type RequireAuthOptions, } from './require-auth.js';
@@ -0,0 +1,2 @@
1
+ export { requireAuth, ensureAuthenticated, } from './require-auth.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,mBAAmB,GAGpB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,67 @@
1
+ export interface AuthCheckResult {
2
+ isAuthenticated: boolean;
3
+ isExpired: boolean;
4
+ isExpiringSoon: boolean;
5
+ timeUntilExpiration: string | null;
6
+ message?: string;
7
+ }
8
+ /**
9
+ * Options for authentication check
10
+ */
11
+ export interface RequireAuthOptions {
12
+ /**
13
+ * Hours before expiration to consider "expiring soon"
14
+ * @default 24
15
+ */
16
+ expirationThresholdHours?: number;
17
+ /**
18
+ * Whether to show warnings for tokens expiring soon
19
+ * @default true
20
+ */
21
+ warnOnExpiringSoon?: boolean;
22
+ }
23
+ /**
24
+ * Check if user is authenticated and token is valid
25
+ * Returns detailed auth status for flexible handling
26
+ *
27
+ * This is the core function that any command can use to check authentication.
28
+ * It handles all the edge cases: missing token, expired token, invalid token,
29
+ * and token expiring soon.
30
+ *
31
+ * @param options - Authentication check options
32
+ * @returns Authentication check result with detailed status
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * // Basic usage in a command
37
+ * const auth = requireAuth();
38
+ * if (!auth.isAuthenticated) {
39
+ * console.error(`Error: ${auth.message}`);
40
+ * process.exit(1);
41
+ * }
42
+ * ```
43
+ */
44
+ export declare function requireAuth(options?: RequireAuthOptions): AuthCheckResult;
45
+ /**
46
+ * Ensures user is authenticated, exits process if not
47
+ *
48
+ * This is a convenience function for commands that require authentication.
49
+ * It checks authentication and exits with a clear error message if not authenticated.
50
+ * If authenticated but token is expiring soon, it shows a warning but continues.
51
+ *
52
+ * Use this in commands that MUST have authentication to proceed.
53
+ *
54
+ * @param options - Authentication check options
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Simplest usage - one line in your command
59
+ * export async function sync() {
60
+ * ensureAuthenticated(); // Will exit if not authenticated
61
+ *
62
+ * // Safe to proceed - user is authenticated
63
+ * await performSync();
64
+ * }
65
+ * ```
66
+ */
67
+ export declare function ensureAuthenticated(options?: RequireAuthOptions): void;
@@ -0,0 +1,107 @@
1
+ import { isTokenExpired, isTokenExpiringSoon, getTimeUntilExpiration, } from '../../utils/token.js';
2
+ import { tokenStore } from '../storage/index.js';
3
+ /**
4
+ * Check if user is authenticated and token is valid
5
+ * Returns detailed auth status for flexible handling
6
+ *
7
+ * This is the core function that any command can use to check authentication.
8
+ * It handles all the edge cases: missing token, expired token, invalid token,
9
+ * and token expiring soon.
10
+ *
11
+ * @param options - Authentication check options
12
+ * @returns Authentication check result with detailed status
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // Basic usage in a command
17
+ * const auth = requireAuth();
18
+ * if (!auth.isAuthenticated) {
19
+ * console.error(`Error: ${auth.message}`);
20
+ * process.exit(1);
21
+ * }
22
+ * ```
23
+ */
24
+ export function requireAuth(options = {}) {
25
+ const { expirationThresholdHours = 23, warnOnExpiringSoon = true } = options;
26
+ // Check if token exists in storage
27
+ if (!tokenStore.exists()) {
28
+ return {
29
+ isAuthenticated: false,
30
+ isExpired: false,
31
+ isExpiringSoon: false,
32
+ timeUntilExpiration: null,
33
+ message: 'Not authenticated. Pleas run `papyrus login` first.',
34
+ };
35
+ }
36
+ const token = tokenStore.get();
37
+ if (!token) {
38
+ return {
39
+ isAuthenticated: false,
40
+ isExpired: false,
41
+ isExpiringSoon: false,
42
+ timeUntilExpiration: null,
43
+ message: 'Invalid token. Please run `papyrus login` again.',
44
+ };
45
+ }
46
+ // Check if token is expired
47
+ if (isTokenExpired(token)) {
48
+ // Automatically clear expired token
49
+ tokenStore.clear();
50
+ return {
51
+ isAuthenticated: false,
52
+ isExpired: true,
53
+ isExpiringSoon: false,
54
+ timeUntilExpiration: null,
55
+ message: 'Your session has expired. Please run `papyrus login` again.',
56
+ };
57
+ }
58
+ // Check if token is expiring soon
59
+ const expiringSoon = isTokenExpiringSoon(token, expirationThresholdHours);
60
+ const timeUntilExpiration = getTimeUntilExpiration(token);
61
+ return {
62
+ isAuthenticated: true,
63
+ isExpired: false,
64
+ isExpiringSoon: expiringSoon,
65
+ timeUntilExpiration,
66
+ message: expiringSoon && warnOnExpiringSoon && timeUntilExpiration
67
+ ? `Your session will expire in ${timeUntilExpiration}. Please run \`papyrus login\` to refresh.`
68
+ : undefined,
69
+ };
70
+ }
71
+ /**
72
+ * Ensures user is authenticated, exits process if not
73
+ *
74
+ * This is a convenience function for commands that require authentication.
75
+ * It checks authentication and exits with a clear error message if not authenticated.
76
+ * If authenticated but token is expiring soon, it shows a warning but continues.
77
+ *
78
+ * Use this in commands that MUST have authentication to proceed.
79
+ *
80
+ * @param options - Authentication check options
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Simplest usage - one line in your command
85
+ * export async function sync() {
86
+ * ensureAuthenticated(); // Will exit if not authenticated
87
+ *
88
+ * // Safe to proceed - user is authenticated
89
+ * await performSync();
90
+ * }
91
+ * ```
92
+ */
93
+ export function ensureAuthenticated(options = {}) {
94
+ const authResult = requireAuth(options);
95
+ if (!authResult.isAuthenticated) {
96
+ console.error(`Error: ${authResult.message}`);
97
+ process.exit(1);
98
+ }
99
+ if (authResult.isExpired) {
100
+ console.error(`Error: ${authResult.message}`);
101
+ process.exit(1);
102
+ }
103
+ if (authResult.isExpiringSoon && authResult.message) {
104
+ console.warn(`Warning: ${authResult.message}`);
105
+ }
106
+ }
107
+ //# sourceMappingURL=require-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-auth.js","sourceRoot":"","sources":["../../../src/lib/auth/require-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AA2BjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,WAAW,CAAC,UAA8B,EAAE;IAC1D,MAAM,EAAE,wBAAwB,GAAG,EAAE,EAAE,kBAAkB,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAE7E,mCAAmC;IACnC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QACzB,OAAO;YACL,eAAe,EAAE,KAAK;YACtB,SAAS,EAAE,KAAK;YAChB,cAAc,EAAE,KAAK;YACrB,mBAAmB,EAAE,IAAI;YACzB,OAAO,EAAE,qDAAqD;SAC/D,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,eAAe,EAAE,KAAK;YACtB,SAAS,EAAE,KAAK;YAChB,cAAc,EAAE,KAAK;YACrB,mBAAmB,EAAE,IAAI;YACzB,OAAO,EAAE,kDAAkD;SAC5D,CAAC;IACJ,CAAC;IAED,4BAA4B;IAC5B,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,oCAAoC;QACpC,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO;YACL,eAAe,EAAE,KAAK;YACtB,SAAS,EAAE,IAAI;YACf,cAAc,EAAE,KAAK;YACrB,mBAAmB,EAAE,IAAI;YACzB,OAAO,EAAE,6DAA6D;SACvE,CAAC;IACJ,CAAC;IAED,kCAAkC;IAClC,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;IAC1E,MAAM,mBAAmB,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAE1D,OAAO;QACL,eAAe,EAAE,IAAI;QACrB,SAAS,EAAE,KAAK;QAChB,cAAc,EAAE,YAAY;QAC5B,mBAAmB;QACnB,OAAO,EACL,YAAY,IAAI,kBAAkB,IAAI,mBAAmB;YACvD,CAAC,CAAC,+BAA+B,mBAAmB,4CAA4C;YAChG,CAAC,CAAC,SAAS;KAChB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAA8B,EAAE;IAClE,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAExC,IAAI,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,UAAU,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,UAAU,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,UAAU,CAAC,cAAc,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,YAAY,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Base storage class that follows the XDG-base directory standard
3
+ * and handles cross-platform directory management
4
+ * and common file operations
5
+ */
6
+ export declare class BaseStorage {
7
+ /**
8
+ * the env-paths will add `runtime` to the path name.
9
+ * we must set { suffix: "" }, otherwise, in Windows,
10
+ * the folder will become:
11
+ * /c/users/{username}/appdata/local/papyrus-nodejs/data
12
+ * instead of
13
+ * /c/users/{username}/appdata/local/papyrus/data
14
+ */
15
+ private paths;
16
+ /**
17
+ * Get config directory
18
+ * Linux: ~/.config/papyrus
19
+ * Windows: %APPDATA%\papyrus\Config
20
+ * macOS: ~/Library/Preferences/papyrus
21
+ */
22
+ protected getConfigDir(): string;
23
+ /**
24
+ * Get data directory
25
+ * Linux: ~/.local/share/papyrus
26
+ * Windows: %LOCALAPPDATA%\papyrus\Data
27
+ * macOS: ~/Library/Application Support/papyrus
28
+ */
29
+ protected getDataDir(): string;
30
+ /**
31
+ * Ensure directory exists, create if it doesn't
32
+ */
33
+ protected ensureDir(dir: string): void;
34
+ /**
35
+ * Read file contents, return null if doesn't exist
36
+ */
37
+ protected readFile(filePath: string): string | null;
38
+ /**
39
+ * Write file contents, create parent directories if needed
40
+ */
41
+ protected writeFile(filePath: string, content: string): void;
42
+ /**
43
+ * Delete file if it exists
44
+ */
45
+ protected deleteFile(filePath: string): void;
46
+ /**
47
+ * Check if file exists
48
+ */
49
+ protected fileExists(filePath: string): boolean;
50
+ }
@@ -0,0 +1,91 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import envPaths from 'env-paths';
4
+ /**
5
+ * Base storage class that follows the XDG-base directory standard
6
+ * and handles cross-platform directory management
7
+ * and common file operations
8
+ */
9
+ export class BaseStorage {
10
+ /**
11
+ * the env-paths will add `runtime` to the path name.
12
+ * we must set { suffix: "" }, otherwise, in Windows,
13
+ * the folder will become:
14
+ * /c/users/{username}/appdata/local/papyrus-nodejs/data
15
+ * instead of
16
+ * /c/users/{username}/appdata/local/papyrus/data
17
+ */
18
+ paths = envPaths('papyrus', { suffix: '' });
19
+ /**
20
+ * Get config directory
21
+ * Linux: ~/.config/papyrus
22
+ * Windows: %APPDATA%\papyrus\Config
23
+ * macOS: ~/Library/Preferences/papyrus
24
+ */
25
+ getConfigDir() {
26
+ return this.paths.config;
27
+ }
28
+ /**
29
+ * Get data directory
30
+ * Linux: ~/.local/share/papyrus
31
+ * Windows: %LOCALAPPDATA%\papyrus\Data
32
+ * macOS: ~/Library/Application Support/papyrus
33
+ */
34
+ getDataDir() {
35
+ return this.paths.data;
36
+ }
37
+ /**
38
+ * Ensure directory exists, create if it doesn't
39
+ */
40
+ ensureDir(dir) {
41
+ if (!fs.existsSync(dir)) {
42
+ /**
43
+ * The mode 0o700 grants the owner of the file or directory full read, write, and execute permissions,
44
+ * while denying all access to everyone else.
45
+ * - 1st digit (7): Owner (User, with `rwx` permissions)
46
+ * - 2nd digit (0): Group
47
+ * - 3rd digit (0): Others (anyone not the owner or in the group)
48
+ *
49
+ * For directories, `x` permission is required to `cd` into a dir or access files inside
50
+ */
51
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
52
+ }
53
+ }
54
+ /**
55
+ * Read file contents, return null if doesn't exist
56
+ */
57
+ readFile(filePath) {
58
+ try {
59
+ if (fs.existsSync(filePath)) {
60
+ return fs.readFileSync(filePath, 'utf-8');
61
+ }
62
+ return null;
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ /**
69
+ * Write file contents, create parent directories if needed
70
+ */
71
+ writeFile(filePath, content) {
72
+ const dir = path.dirname(filePath);
73
+ this.ensureDir(dir);
74
+ fs.writeFileSync(filePath, content, 'utf-8');
75
+ }
76
+ /**
77
+ * Delete file if it exists
78
+ */
79
+ deleteFile(filePath) {
80
+ if (fs.existsSync(filePath)) {
81
+ fs.unlinkSync(filePath);
82
+ }
83
+ }
84
+ /**
85
+ * Check if file exists
86
+ */
87
+ fileExists(filePath) {
88
+ return fs.existsSync(filePath);
89
+ }
90
+ }
91
+ //# sourceMappingURL=base-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-storage.js","sourceRoot":"","sources":["../../../src/lib/storage/base-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,QAAQ,MAAM,WAAW,CAAC;AAEjC;;;;GAIG;AACH,MAAM,OAAO,WAAW;IACtB;;;;;;;OAOG;IACK,KAAK,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;IAEpD;;;;;OAKG;IACO,YAAY;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACO,UAAU;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED;;OAEG;IACO,SAAS,CAAC,GAAW;QAC7B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB;;;;;;;;eAQG;YACH,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED;;OAEG;IACO,QAAQ,CAAC,QAAgB;QACjC,IAAI,CAAC;YACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,OAAO,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC5C,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACO,SAAS,CAAC,QAAgB,EAAE,OAAe;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACpB,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACO,UAAU,CAAC,QAAgB;QACnC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED;;OAEG;IACO,UAAU,CAAC,QAAgB;QACnC,OAAO,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;CACF"}
@@ -0,0 +1,40 @@
1
+ import { BaseStorage } from './base-storage.js';
2
+ /**
3
+ * User configuration structure
4
+ */
5
+ export interface Config {
6
+ apiUrl?: string;
7
+ editor?: string;
8
+ }
9
+ /**
10
+ * Manages user configuration stored in XDG_CONFIG_HOME
11
+ * Example: ~/.config/papyrus/config.json
12
+ */
13
+ export declare class ConfigStore extends BaseStorage {
14
+ private configPath;
15
+ constructor(configDir?: string);
16
+ /**
17
+ * Load configuration, returns empty object if not found
18
+ */
19
+ load(): Config;
20
+ /**
21
+ * Save configuration
22
+ */
23
+ save(config: Config): void;
24
+ /**
25
+ * Update specific config values
26
+ */
27
+ update(updates: Partial<Config>): void;
28
+ /**
29
+ * Get specific config value
30
+ */
31
+ get<K extends keyof Config>(key: K): Config[K] | undefined;
32
+ /**
33
+ * Set specific config value
34
+ */
35
+ set<K extends keyof Config>(key: K, value: Config[K]): void;
36
+ /**
37
+ * Clear all configuration
38
+ */
39
+ clear(): void;
40
+ }
@@ -0,0 +1,63 @@
1
+ import path from 'path';
2
+ import { BaseStorage } from './base-storage.js';
3
+ /**
4
+ * Manages user configuration stored in XDG_CONFIG_HOME
5
+ * Example: ~/.config/papyrus/config.json
6
+ */
7
+ export class ConfigStore extends BaseStorage {
8
+ configPath;
9
+ constructor(configDir) {
10
+ super();
11
+ const dir = configDir ?? this.getConfigDir();
12
+ this.configPath = path.join(dir, 'config.json');
13
+ }
14
+ /**
15
+ * Load configuration, returns empty object if not found
16
+ */
17
+ load() {
18
+ const content = this.readFile(this.configPath);
19
+ if (!content)
20
+ return {};
21
+ try {
22
+ return JSON.parse(content);
23
+ }
24
+ catch {
25
+ return {};
26
+ }
27
+ }
28
+ /**
29
+ * Save configuration
30
+ */
31
+ save(config) {
32
+ const content = JSON.stringify(config, null, 2);
33
+ this.writeFile(this.configPath, content);
34
+ }
35
+ /**
36
+ * Update specific config values
37
+ */
38
+ update(updates) {
39
+ const current = this.load();
40
+ const updated = { ...current, ...updates };
41
+ this.save(updated);
42
+ }
43
+ /**
44
+ * Get specific config value
45
+ */
46
+ get(key) {
47
+ const config = this.load();
48
+ return config[key];
49
+ }
50
+ /**
51
+ * Set specific config value
52
+ */
53
+ set(key, value) {
54
+ this.update({ [key]: value });
55
+ }
56
+ /**
57
+ * Clear all configuration
58
+ */
59
+ clear() {
60
+ this.deleteFile(this.configPath);
61
+ }
62
+ }
63
+ //# sourceMappingURL=config-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../../src/lib/storage/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUhD;;;GAGG;AACH,MAAM,OAAO,WAAY,SAAQ,WAAW;IAClC,UAAU,CAAS;IAE3B,YAAY,SAAkB;QAC5B,KAAK,EAAE,CAAC;QACR,MAAM,GAAG,GAAG,SAAS,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,IAAI;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAExB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAW,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,MAAc;QACjB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,OAAwB;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,GAAG,CAAyB,GAAM;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,GAAG,CAAyB,GAAM,EAAE,KAAgB;QAClD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAqB,CAAC,CAAC;IACnD,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ import { ConfigStore } from './config-store.js';
2
+ import { JournalStore } from './journal-storage.js';
3
+ import { SyncMetaStore } from './sync-meta-store.js';
4
+ import { TokenStore } from './token-store.js';
5
+ export { ConfigStore, type Config } from './config-store.js';
6
+ /**
7
+ * Singletons for easy access
8
+ */
9
+ export declare const configStore: ConfigStore;
10
+ export declare const tokenStore: TokenStore;
11
+ export declare const syncMetaStore: SyncMetaStore;
12
+ export declare const journalStore: JournalStore;
@@ -0,0 +1,13 @@
1
+ import { ConfigStore } from './config-store.js';
2
+ import { JournalStore } from './journal-storage.js';
3
+ import { SyncMetaStore } from './sync-meta-store.js';
4
+ import { TokenStore } from './token-store.js';
5
+ export { ConfigStore } from './config-store.js';
6
+ /**
7
+ * Singletons for easy access
8
+ */
9
+ export const configStore = new ConfigStore();
10
+ export const tokenStore = new TokenStore();
11
+ export const syncMetaStore = new SyncMetaStore();
12
+ export const journalStore = new JournalStore();
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAe,MAAM,mBAAmB,CAAC;AAC7D;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AAC7C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;AAC3C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;AACjD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { BaseStorage } from './base-storage.js';
2
+ export interface JournalFileInfo {
3
+ date: string;
4
+ path: string;
5
+ size: number;
6
+ modified: Date;
7
+ }
8
+ /**
9
+ * Manages journal entries stored as markdown files with YAML frontmatter.
10
+ *
11
+ * File format:
12
+ * ---
13
+ * date: "20251210"
14
+ * hash: "abc123..."
15
+ * createdAt: "2025-12-10T10:00:00.000Z"
16
+ * updatedAt: "2025-12-10T10:00:00.000Z"
17
+ * deletedAt: null
18
+ * ---
19
+ *
20
+ * # Journal content here
21
+ */
22
+ export declare class JournalStore extends BaseStorage {
23
+ private journalsDir;
24
+ constructor(dataDir?: string);
25
+ private getEntryPath;
26
+ /**
27
+ * Create a new journal entry
28
+ */
29
+ create(date: string, content?: string): void;
30
+ /**
31
+ * Get journal entry by date
32
+ */
33
+ load(date: string): string | null;
34
+ /**
35
+ * Delete journal entry
36
+ */
37
+ delete(date: string): void;
38
+ /**
39
+ * Check if entry exists for date
40
+ */
41
+ exists(date: string): boolean;
42
+ /**
43
+ * List all journal entries (sorted by date descending)
44
+ */
45
+ list(): JournalFileInfo[];
46
+ }