mcard-js 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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/dist/__mocks__/better-sqlite3.js +20 -0
  4. package/dist/__mocks__/better-sqlite3.js.map +1 -0
  5. package/dist/config/config_constants.js +188 -0
  6. package/dist/config/config_constants.js.map +1 -0
  7. package/dist/config/env_parameters.js +62 -0
  8. package/dist/config/env_parameters.js.map +1 -0
  9. package/dist/content/model/content_type_detector.js +89 -0
  10. package/dist/content/model/content_type_detector.js.map +1 -0
  11. package/dist/core/card-collection.js +279 -0
  12. package/dist/core/card-collection.js.map +1 -0
  13. package/dist/core/event-producer.js +132 -0
  14. package/dist/core/event-producer.js.map +1 -0
  15. package/dist/core/g_time.js +201 -0
  16. package/dist/core/g_time.js.map +1 -0
  17. package/dist/core/hash/enums.js +19 -0
  18. package/dist/core/hash/enums.js.map +1 -0
  19. package/dist/core/hash/validator.js +260 -0
  20. package/dist/core/hash/validator.js.map +1 -0
  21. package/dist/core/mcard.js +205 -0
  22. package/dist/core/mcard.js.map +1 -0
  23. package/dist/engine/sqlite_engine.js +723 -0
  24. package/dist/engine/sqlite_engine.js.map +1 -0
  25. package/dist/index.js +10 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/middleware/mcardPersistenceMiddleware.js +45 -0
  28. package/dist/middleware/mcardPersistenceMiddleware.js.map +1 -0
  29. package/dist/models/database_schemas.js +31 -0
  30. package/dist/models/database_schemas.js.map +1 -0
  31. package/dist/services/logger.js +80 -0
  32. package/dist/services/logger.js.map +1 -0
  33. package/dist/services/mcardStorageService.js +36 -0
  34. package/dist/services/mcardStorageService.js.map +1 -0
  35. package/dist/utils/actionHelpers.js +25 -0
  36. package/dist/utils/actionHelpers.js.map +1 -0
  37. package/dist/utils/bufferContentHelper.js +393 -0
  38. package/dist/utils/bufferContentHelper.js.map +1 -0
  39. package/dist/utils/bufferPolyfill.js +198 -0
  40. package/dist/utils/bufferPolyfill.js.map +1 -0
  41. package/dist/utils/content-detection.js +74 -0
  42. package/dist/utils/content-detection.js.map +1 -0
  43. package/dist/utils/content-utils.js +269 -0
  44. package/dist/utils/content-utils.js.map +1 -0
  45. package/dist/utils/content_type_detector copy.js +480 -0
  46. package/dist/utils/content_type_detector copy.js.map +1 -0
  47. package/dist/utils/content_type_detector.js +480 -0
  48. package/dist/utils/content_type_detector.js.map +1 -0
  49. package/dist/utils/cryptoPolyfill.js +166 -0
  50. package/dist/utils/cryptoPolyfill.js.map +1 -0
  51. package/dist/utils/dotenv-browser.js +35 -0
  52. package/dist/utils/dotenv-browser.js.map +1 -0
  53. package/dist/utils/environmentDetector.js +93 -0
  54. package/dist/utils/environmentDetector.js.map +1 -0
  55. package/dist/utils/logWriter.js +27 -0
  56. package/dist/utils/logWriter.js.map +1 -0
  57. package/dist/utils/serviceWorkerManager.js +118 -0
  58. package/dist/utils/serviceWorkerManager.js.map +1 -0
  59. package/dist/utils/test-content-detection.js +79 -0
  60. package/dist/utils/test-content-detection.js.map +1 -0
  61. package/dist/utils/test-detection-fix.js +121 -0
  62. package/dist/utils/test-detection-fix.js.map +1 -0
  63. package/dist/utils/test-format-conversion.js +170 -0
  64. package/dist/utils/test-format-conversion.js.map +1 -0
  65. package/dist/utils/test-mov-viewer.js +57 -0
  66. package/dist/utils/test-mov-viewer.js.map +1 -0
  67. package/dist/utils/testDetection.js +21 -0
  68. package/dist/utils/testDetection.js.map +1 -0
  69. package/dist/utils/textEncoderPolyfill.js +87 -0
  70. package/dist/utils/textEncoderPolyfill.js.map +1 -0
  71. package/package.json +74 -0
  72. package/src/__mocks__/better-sqlite3.js +14 -0
  73. package/src/config/config_constants.js +227 -0
  74. package/src/config/env_parameters.js +69 -0
  75. package/src/content/model/content_type_detector.js +87 -0
  76. package/src/core/card-collection.js +300 -0
  77. package/src/core/event-producer.js +160 -0
  78. package/src/core/g_time.js +215 -0
  79. package/src/core/hash/enums.js +13 -0
  80. package/src/core/hash/validator.js +271 -0
  81. package/src/core/mcard.js +203 -0
  82. package/src/engine/sqlite_engine.js +755 -0
  83. package/src/index.js +10 -0
  84. package/src/middleware/mcardPersistenceMiddleware.js +45 -0
  85. package/src/models/database_schemas.js +26 -0
  86. package/src/services/logger.js +74 -0
  87. package/src/services/mcardStorageService.js +34 -0
  88. package/src/utils/actionHelpers.js +13 -0
  89. package/src/utils/bufferContentHelper.js +436 -0
  90. package/src/utils/bufferPolyfill.js +202 -0
  91. package/src/utils/cn.ts +6 -0
  92. package/src/utils/content-detection.js +66 -0
  93. package/src/utils/content-utils.js +250 -0
  94. package/src/utils/content_type_detector copy.js +501 -0
  95. package/src/utils/content_type_detector.js +501 -0
  96. package/src/utils/cryptoPolyfill.js +180 -0
  97. package/src/utils/dateUtils.ts +18 -0
  98. package/src/utils/dbInitializer.ts +27 -0
  99. package/src/utils/dotenv-browser.js +29 -0
  100. package/src/utils/environmentDetector.js +92 -0
  101. package/src/utils/logWriter.js +20 -0
  102. package/src/utils/serviceWorkerManager.js +122 -0
  103. package/src/utils/stateWatcher.ts +78 -0
  104. package/src/utils/storeAdapter copy.ts +157 -0
  105. package/src/utils/storeAdapter.ts +157 -0
  106. package/src/utils/test-content-detection.js +71 -0
  107. package/src/utils/test-detection-fix.js +136 -0
  108. package/src/utils/test-format-conversion.js +165 -0
  109. package/src/utils/test-mov-viewer.js +59 -0
  110. package/src/utils/testDetection.js +16 -0
  111. package/src/utils/textEncoderPolyfill.js +88 -0
@@ -0,0 +1,723 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.SQLiteEngine = exports.SQLiteConnection = void 0;
8
+ var _mcard = require("../core/mcard.js");
9
+ var _cardCollection = require("../core/card-collection.js");
10
+ var _config_constants = require("../config/config_constants.js");
11
+ var _database_schemas = require("../models/database_schemas.js");
12
+ var _content_type_detector = _interopRequireDefault(require("../content/model/content_type_detector.js"));
13
+ var _bufferPolyfill = require("../utils/bufferPolyfill.js");
14
+ var _path = _interopRequireDefault(require("path"));
15
+ var _betterSqlite = _interopRequireDefault(require("better-sqlite3"));
16
+ var _fs = _interopRequireDefault(require("fs"));
17
+ class SQLiteConnection {
18
+ /**
19
+ * Singleton instance management
20
+ */
21
+ static _instance = null;
22
+
23
+ /**
24
+ * Get singleton instance of SQLiteConnection
25
+ * @param {string} [dbPath] - Optional path to the SQLite database file
26
+ * @returns {SQLiteConnection} Singleton instance
27
+ */
28
+ static getInstance(dbPath = null) {
29
+ if (!this._instance) {
30
+ this._instance = new SQLiteConnection(dbPath);
31
+ }
32
+ return this._instance;
33
+ }
34
+
35
+ /**
36
+ * Create a new SQLite database connection
37
+ * @param {string} [dbPath] - Optional path to the SQLite database file
38
+ * Prioritizes the provided path, then environment variable, then default config
39
+ */
40
+ constructor(dbPath = null) {
41
+ // Determine the database path in order of priority:
42
+ // 1. Explicitly provided path
43
+ // 2. Environment variable
44
+ // 3. Default configuration path
45
+ this.dbPath = dbPath || process.env.MCARD_DB_PATH || _config_constants.CARDS_DB_PATH;
46
+
47
+ // Ensure the path is an absolute path
48
+ this.dbPath = _path.default.resolve(this.dbPath);
49
+ this.conn = null;
50
+ }
51
+
52
+ /**
53
+ * Establish a database connection
54
+ */
55
+ connect() {
56
+ try {
57
+ // Ensure the directory exists
58
+ const dir = _path.default.dirname(this.dbPath);
59
+ if (!_fs.default.existsSync(dir)) {
60
+ _fs.default.mkdirSync(dir, {
61
+ recursive: true
62
+ });
63
+ }
64
+
65
+ // Open the database connection with appropriate flags
66
+ // Don't remove the existing database file to prevent data loss
67
+ this.conn = new _betterSqlite.default(this.dbPath, {
68
+ // Open in read-write mode, create if not exists
69
+ mode: _betterSqlite.default.OPEN_READWRITE | _betterSqlite.default.OPEN_CREATE,
70
+ // Disable verbose mode to reduce unnecessary logging
71
+ verbose: null
72
+ });
73
+ return this;
74
+ } catch (error) {
75
+ console.error(`Database connection error: ${error.message}`);
76
+ throw error;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Set up the database, creating the file and table if they don't exist
82
+ */
83
+ setup_database() {
84
+ try {
85
+ // Ensure the connection is open
86
+ if (!this.conn) {
87
+ this.connect();
88
+ }
89
+
90
+ // Check if the tables already exist
91
+ const tableExists = this.conn.prepare(`
92
+ SELECT name FROM sqlite_master
93
+ WHERE type='table' AND name='card'
94
+ `).get();
95
+
96
+ // Only create tables if they don't exist
97
+ if (!tableExists) {
98
+ console.log('Creating new database tables...');
99
+
100
+ // Create the table using the schema
101
+ this.conn.exec(_database_schemas.MCARD_TABLE_SCHEMA);
102
+
103
+ // Add triggers
104
+ _database_schemas.TRIGGERS.forEach(trigger => {
105
+ try {
106
+ this.conn.exec(trigger);
107
+ } catch (triggerError) {
108
+ console.warn(`Warning during trigger creation: ${triggerError.message}`);
109
+ }
110
+ });
111
+ console.log('Database tables created successfully');
112
+ } else {
113
+ console.log('Database tables already exist, skipping creation');
114
+ }
115
+ } catch (error) {
116
+ console.error('Database setup failed:', error);
117
+ throw error;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Close the database connection
123
+ */
124
+ disconnect() {
125
+ try {
126
+ if (this.conn) {
127
+ this.conn.close();
128
+ this.conn = null;
129
+ }
130
+ } catch (error) {
131
+ console.warn(`Error during database disconnect: ${error.message}`);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Commit the current transaction
137
+ */
138
+ commit() {
139
+ if (this.conn) {
140
+ this.conn.prepare('COMMIT').run();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Rollback the current transaction
146
+ */
147
+ rollback() {
148
+ if (this.conn) {
149
+ this.conn.prepare('ROLLBACK').run();
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Add a method to execute raw queries
155
+ * @param {string} query - Raw query to execute
156
+ * @param {array} params - Parameters for the query
157
+ * @returns {array} Results of the query
158
+ */
159
+ executeQuery(query, params = []) {
160
+ if (!this.conn) {
161
+ this.connect();
162
+ }
163
+ const stmt = this.conn.prepare(query);
164
+ return stmt.all(...params);
165
+ }
166
+ }
167
+ exports.SQLiteConnection = SQLiteConnection;
168
+ class SQLiteEngine {
169
+ /**
170
+ * Create a new SQLite storage engine
171
+ * @param {SQLiteConnection} connection - Database connection
172
+ */
173
+ constructor(connection = null) {
174
+ this.connection = connection || SQLiteConnection.getInstance();
175
+ this.connection.connect();
176
+ this.connection.setup_database();
177
+ this.clearStmt = this.connection.conn.prepare('DELETE FROM card');
178
+ }
179
+
180
+ /**
181
+ * Destructor to ensure database connection is closed
182
+ */
183
+ destructor() {
184
+ this.connection.disconnect();
185
+ }
186
+
187
+ /**
188
+ * Symbol.dispose method for resource cleanup
189
+ */
190
+ [Symbol.dispose]() {
191
+ this.destructor();
192
+ }
193
+
194
+ /**
195
+ * Add a card to the database
196
+ * @param {MCard} card - Card to add
197
+ * @returns {string} Hash of the added card
198
+ */
199
+ add(card) {
200
+ try {
201
+ console.log('SQLiteEngine.add called with card hash:', card.hash);
202
+
203
+ // Check if the card already exists
204
+ const existingCard = this.get(card.hash);
205
+ if (existingCard) {
206
+ console.log('Card already exists with hash:', card.hash);
207
+ return card.hash;
208
+ }
209
+
210
+ // Ensure content is properly serialized for SQLite storage
211
+ let finalContent;
212
+ // Always store as string (JSON) or Buffer
213
+ if (_bufferPolyfill.SafeBuffer.isBuffer(card.content)) {
214
+ // Buffers can be stored directly
215
+ finalContent = card.content;
216
+ console.log('Using Buffer content directly');
217
+ } else if (typeof card.content === 'object' && card.content !== null) {
218
+ // Explicitly serialize objects
219
+ finalContent = JSON.stringify(card.content);
220
+ console.log('Serialized object content to JSON string');
221
+ } else if (typeof card.content === 'string') {
222
+ // Strings can be stored directly
223
+ finalContent = card.content;
224
+ console.log('Using string content directly');
225
+ } else {
226
+ // Convert other types to string
227
+ finalContent = String(card.content);
228
+ console.log('Converted content to string');
229
+ }
230
+
231
+ // Insert the card into the database
232
+ try {
233
+ const stmt = this.connection.conn.prepare('INSERT INTO card (hash, content, g_time) VALUES (?, ?, ?)');
234
+ stmt.run(card.hash, finalContent, card.g_time);
235
+ console.log('Card inserted successfully with hash:', card.hash);
236
+ return card.hash;
237
+ } catch (sqlError) {
238
+ console.error('SQL error inserting card:', sqlError);
239
+ throw sqlError;
240
+ }
241
+ } catch (error) {
242
+ console.error('Error in SQLiteEngine.add:', error);
243
+ throw error;
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Retrieve a card by its hash
249
+ * @param {string} hashValue - Hash of the card to retrieve
250
+ * @returns {MCard|null} Retrieved card or null
251
+ */
252
+ get(hash) {
253
+ try {
254
+ console.log('SQLiteEngine.get called with hash:', hash);
255
+
256
+ // Query the database for the card
257
+ const stmt = this.connection.conn.prepare('SELECT hash, content, g_time, typeof(content) as content_type FROM card WHERE hash = ?');
258
+ const row = stmt.get(String(hash));
259
+ if (!row) {
260
+ console.log('No card found with hash:', hash);
261
+ return null;
262
+ }
263
+ console.log(`SQLiteEngine.get - Raw content typeof:`, row.content_type);
264
+ console.log(`SQLiteEngine.get - JS typeof:`, typeof row.content);
265
+ console.log(`SQLiteEngine.get - Is Buffer:`, _bufferPolyfill.SafeBuffer.isBuffer(row.content));
266
+ console.log(`SQLiteEngine.get - Content length:`, row.content ? row.content.length : 0);
267
+ if (_bufferPolyfill.SafeBuffer.isBuffer(row.content)) {
268
+ console.log(`SQLiteEngine.get - Buffer content first 20 bytes:`, row.content.slice(0, 20).toString('hex'));
269
+ } else if (typeof row.content === 'string') {
270
+ console.log(`SQLiteEngine.get - String content first 50 chars:`, row.content.substring(0, 50));
271
+ }
272
+
273
+ // Detect content type BEFORE any transformations
274
+ const isBlob = _bufferPolyfill.SafeBuffer.isBuffer(row.content);
275
+ const contentForDetection = row.content;
276
+
277
+ // Detect content type from the raw content
278
+ const contentType = _content_type_detector.default.detectContentType(contentForDetection);
279
+ console.log(`SQLiteEngine.get - Detected contentType:`, contentType);
280
+
281
+ // Add isBlob flag to contentType object
282
+ contentType.isBlob = isBlob;
283
+
284
+ // Parse content if it's a JSON string - but only if not detected as another type
285
+ let content = row.content;
286
+ // If stored as Buffer (Node.js), convert to Uint8Array for cross-env compatibility
287
+ if (_bufferPolyfill.SafeBuffer.isBuffer(content)) {
288
+ // Optionally decode to string if contentType is text or JSON
289
+ if (contentType.mimeType === 'application/json' || contentType.mimeType.startsWith('text/')) {
290
+ try {
291
+ const str = _bufferPolyfill.SafeBuffer.toString(content);
292
+ if (contentType.mimeType === 'application/json' && (str.startsWith('{') || str.startsWith('['))) {
293
+ content = JSON.parse(str);
294
+ console.log('Parsed JSON content successfully from Buffer');
295
+ } else {
296
+ content = str;
297
+ console.log('Converted Buffer to string');
298
+ }
299
+ } catch (e) {
300
+ console.warn('Failed to decode Buffer content:', e);
301
+ }
302
+ }
303
+ // else: leave as Uint8Array/Buffer for binary data
304
+ } else if (typeof content === 'string' && contentType.mimeType === 'application/json') {
305
+ try {
306
+ // Check if the string is a JSON object
307
+ if (content.startsWith('{') || content.startsWith('[')) {
308
+ const parsed = JSON.parse(content);
309
+ content = parsed;
310
+ console.log('Parsed JSON content successfully');
311
+ }
312
+ } catch (e) {
313
+ // If parsing fails, keep the original string
314
+ console.log('Content is not a valid JSON string, keeping as-is');
315
+ }
316
+ }
317
+ console.log('Card retrieved successfully with hash:', hash);
318
+ return {
319
+ hash: row.hash,
320
+ content: content,
321
+ g_time: row.g_time,
322
+ contentType: contentType // Add content type information to the response
323
+ };
324
+ } catch (error) {
325
+ console.error('Error retrieving card:', error);
326
+ return null;
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Delete a card by its hash
332
+ * @param {string} hashValue - Hash of the card to delete
333
+ * @returns {boolean} Whether deletion was successful
334
+ */
335
+ delete(hashValue) {
336
+ try {
337
+ const stmt = this.connection.conn.prepare('DELETE FROM card WHERE hash = ?');
338
+ const result = stmt.run(String(hashValue));
339
+ return result.changes > 0;
340
+ } catch (error) {
341
+ console.error(`Error deleting card: ${error.message}`);
342
+ throw error;
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Get a page of cards
348
+ * @param {number} page_number - Page number to retrieve
349
+ * @param {number} page_size - Number of items per page
350
+ * @returns {Page} Page of cards
351
+ */
352
+ get_page(page_number = 1, page_size = _config_constants.DEFAULT_PAGE_SIZE) {
353
+ if (page_number < 1 || page_size < 1) {
354
+ throw new Error('Page number and size must be >= 1');
355
+ }
356
+ const offset = (page_number - 1) * page_size;
357
+
358
+ // Get total count of items
359
+ const countStmt = this.connection.conn.prepare('SELECT COUNT(*) as total FROM card');
360
+ const {
361
+ total
362
+ } = countStmt.get();
363
+
364
+ // Get page of items
365
+ const stmt = this.connection.conn.prepare(`
366
+ SELECT content, g_time, hash
367
+ FROM card
368
+ ORDER BY g_time DESC
369
+ LIMIT ? OFFSET ?
370
+ `);
371
+ const rows = stmt.all(page_size, offset);
372
+
373
+ // Convert rows to cards
374
+ const items = [];
375
+ for (const row of rows) {
376
+ const [content, g_time, hash] = [row.content, row.g_time, row.hash];
377
+
378
+ // Parse content if it's a JSON string
379
+ let parsedContent = content;
380
+ if (typeof content === 'string' && (content.startsWith('{') || content.startsWith('['))) {
381
+ try {
382
+ parsedContent = JSON.parse(content);
383
+ } catch (e) {
384
+ console.warn('Failed to parse JSON content for hash:', hash);
385
+ }
386
+ }
387
+
388
+ // Create card with properly parsed content
389
+ const card = new _mcard.MCardFromData(parsedContent, hash, g_time);
390
+ items.push(card);
391
+ }
392
+
393
+ // Calculate total pages
394
+ const total_pages = Math.ceil(total / page_size);
395
+ return new _cardCollection.Page({
396
+ items,
397
+ total_items: total,
398
+ page_number,
399
+ page_size,
400
+ has_next: offset + page_size < total,
401
+ has_previous: page_number > 1,
402
+ total_pages
403
+ });
404
+ }
405
+
406
+ /**
407
+ * Search cards by string
408
+ * @param {string} search_string - String to search for
409
+ * @param {number} page_number - Page number to retrieve
410
+ * @param {number} page_size - Number of items per page
411
+ * @returns {Page} Page of matching cards
412
+ */
413
+ search_by_string(searchString, pageNumber = 1, pageSize = _config_constants.DEFAULT_PAGE_SIZE) {
414
+ try {
415
+ if (pageNumber < 1) {
416
+ throw new Error('Page number must be >= 1');
417
+ }
418
+ if (pageSize < 1) {
419
+ throw new Error('Page size must be >= 1');
420
+ }
421
+ const offset = (pageNumber - 1) * pageSize;
422
+ const cursor = this.connection.conn;
423
+
424
+ // First, get total count of matching items
425
+ const countStmt = cursor.prepare(`
426
+ SELECT COUNT(*) as total FROM card
427
+ WHERE
428
+ CAST(content AS TEXT) LIKE ? OR
429
+ hash LIKE ? OR
430
+ g_time LIKE ?
431
+ `);
432
+ const {
433
+ total
434
+ } = countStmt.get(`%${searchString}%`, `%${searchString}%`, `%${searchString}%`);
435
+
436
+ // Then, get the actual items for the current page
437
+ const stmt = cursor.prepare(`
438
+ SELECT content, g_time, hash FROM card
439
+ WHERE
440
+ CAST(content AS TEXT) LIKE ? OR
441
+ hash LIKE ? OR
442
+ g_time LIKE ?
443
+ ORDER BY g_time DESC LIMIT ? OFFSET ?
444
+ `);
445
+ const rows = stmt.all(`%${searchString}%`, `%${searchString}%`, `%${searchString}%`, pageSize, offset);
446
+
447
+ // Convert rows to cards
448
+ const items = [];
449
+ for (const row of rows) {
450
+ const {
451
+ content,
452
+ g_time,
453
+ hash
454
+ } = row;
455
+
456
+ // Parse content if it's a JSON string
457
+ let parsedContent = content;
458
+ if (typeof content === 'string' && (content.startsWith('{') || content.startsWith('['))) {
459
+ try {
460
+ parsedContent = JSON.parse(content);
461
+ } catch (e) {
462
+ console.warn('Failed to parse JSON content for hash:', hash);
463
+ }
464
+ }
465
+
466
+ // Create card with properly parsed content
467
+ const card = new _mcard.MCardFromData(parsedContent, hash, g_time);
468
+ items.push(card);
469
+ }
470
+
471
+ // Calculate pagination flags
472
+ const has_next = total > pageNumber * pageSize;
473
+ const has_previous = pageNumber > 1;
474
+ return new _cardCollection.Page({
475
+ items,
476
+ total_items: total,
477
+ page_number: pageNumber,
478
+ page_size: pageSize,
479
+ has_next,
480
+ has_previous,
481
+ total_pages: Math.ceil(total / pageSize)
482
+ });
483
+ } catch (error) {
484
+ console.error(`Error searching cards: ${error.message}`);
485
+ throw error;
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Search for cards by content, hash, or g_time
491
+ * @param {string} searchString - String to search for
492
+ * @param {number} pageNumber - Page number for pagination
493
+ * @param {number} pageSize - Number of items per page
494
+ * @returns {Page} Paginated search results
495
+ */
496
+ search_by_content(searchString, pageNumber = 1, pageSize = _config_constants.DEFAULT_PAGE_SIZE) {
497
+ try {
498
+ if (pageNumber < 1) {
499
+ throw new Error('Page number must be >= 1');
500
+ }
501
+ if (pageSize < 1) {
502
+ throw new Error('Page size must be >= 1');
503
+ }
504
+ const offset = (pageNumber - 1) * pageSize;
505
+ const cursor = this.connection.conn;
506
+
507
+ // First, get total count of matching items
508
+ const countStmt = cursor.prepare('SELECT COUNT(*) as total FROM card WHERE CAST(content AS TEXT) LIKE ?');
509
+ const {
510
+ total
511
+ } = countStmt.get(`%${searchString}%`);
512
+
513
+ // Then, get the actual items for the current page
514
+ const stmt = cursor.prepare(`
515
+ SELECT content, g_time, hash FROM card
516
+ WHERE CAST(content AS TEXT) LIKE ?
517
+ ORDER BY g_time DESC LIMIT ? OFFSET ?
518
+ `);
519
+ const rows = stmt.all(`%${searchString}%`, pageSize, offset);
520
+
521
+ // Convert rows to cards
522
+ const items = [];
523
+ for (const row of rows) {
524
+ const {
525
+ content,
526
+ g_time,
527
+ hash
528
+ } = row;
529
+
530
+ // Parse content if it's a JSON string
531
+ let parsedContent = content;
532
+ if (typeof content === 'string' && (content.startsWith('{') || content.startsWith('['))) {
533
+ try {
534
+ parsedContent = JSON.parse(content);
535
+ } catch (e) {
536
+ console.warn('Failed to parse JSON content for hash:', hash);
537
+ }
538
+ }
539
+
540
+ // Create card with properly parsed content
541
+ const card = new _mcard.MCardFromData(parsedContent, hash, g_time);
542
+ items.push(card);
543
+ }
544
+
545
+ // Calculate pagination flags
546
+ const has_next = total > pageNumber * pageSize;
547
+ const has_previous = pageNumber > 1;
548
+ return new _cardCollection.Page({
549
+ items,
550
+ total_items: total,
551
+ page_number: pageNumber,
552
+ page_size: pageSize,
553
+ has_next,
554
+ has_previous,
555
+ total_pages: Math.ceil(total / pageSize)
556
+ });
557
+ } catch (error) {
558
+ console.error(`Error searching cards: ${error.message}`);
559
+ throw error;
560
+ }
561
+ }
562
+ begin() {
563
+ if (this.connection.conn) {
564
+ this.connection.conn.prepare('BEGIN TRANSACTION').run();
565
+ }
566
+ }
567
+ commit() {
568
+ if (this.connection.conn) {
569
+ this.connection.conn.prepare('COMMIT').run();
570
+ }
571
+ }
572
+ rollback() {
573
+ if (this.connection.conn) {
574
+ this.connection.conn.prepare('ROLLBACK').run();
575
+ }
576
+ }
577
+ clear() {
578
+ try {
579
+ this.begin();
580
+ this.clearStmt.run();
581
+ this.commit();
582
+ } catch (error) {
583
+ this.rollback();
584
+ throw error;
585
+ }
586
+ }
587
+
588
+ /**
589
+ * Count the total number of cards
590
+ * @returns {number} Total number of cards
591
+ */
592
+ count() {
593
+ const stmt = this.connection.conn.prepare('SELECT COUNT(*) as total FROM card');
594
+ const {
595
+ total
596
+ } = stmt.get();
597
+ return total;
598
+ }
599
+
600
+ /**
601
+ * Update a card's content by hash
602
+ * @param {string} hash - Hash of the card to update
603
+ * @param {any} newContent - New content for the card
604
+ * @returns {boolean} Whether the update was successful
605
+ */
606
+ update(hash, newContent) {
607
+ try {
608
+ console.log('SQLiteEngine.update called with hash:', hash);
609
+
610
+ // First verify the card exists
611
+ const existingCard = this.get(hash);
612
+ if (!existingCard) {
613
+ console.log('No card found with hash:', hash);
614
+ return false;
615
+ }
616
+
617
+ // Prepare content for storage
618
+ let finalContent;
619
+ if (typeof newContent === 'object' && newContent !== null && !_bufferPolyfill.SafeBuffer.isBuffer(newContent)) {
620
+ // For objects, stringify to ensure proper SQLite storage
621
+ finalContent = JSON.stringify(newContent);
622
+ console.log('Serialized object content to JSON string for update');
623
+ } else if (typeof newContent === 'string') {
624
+ // Strings can be stored directly
625
+ finalContent = newContent;
626
+ console.log('Using string content directly for update');
627
+ } else if (_bufferPolyfill.SafeBuffer.isBuffer(newContent)) {
628
+ // Buffers can be stored directly
629
+ finalContent = newContent;
630
+ console.log('Using Buffer content directly for update');
631
+ } else {
632
+ // Convert other types to string
633
+ finalContent = String(newContent);
634
+ console.log('Converted content to string for update');
635
+ }
636
+
637
+ // Update the card in the database
638
+ const stmt = this.connection.conn.prepare('UPDATE card SET content = ? WHERE hash = ?');
639
+ const result = stmt.run(finalContent, String(hash));
640
+ if (result.changes > 0) {
641
+ console.log('Card updated successfully with hash:', hash);
642
+ return true;
643
+ } else {
644
+ console.log('Card update had no effect for hash:', hash);
645
+ return false;
646
+ }
647
+ } catch (error) {
648
+ console.error('Error updating card:', error);
649
+ return false;
650
+ }
651
+ }
652
+
653
+ /**
654
+ * Get all cards
655
+ * @param {number} page_number - Page number to retrieve
656
+ * @param {number} page_size - Number of items per page
657
+ * @returns {Page} Page of all cards
658
+ */
659
+ get_all(page_number = 1, page_size = _config_constants.DEFAULT_PAGE_SIZE) {
660
+ if (page_number < 1) {
661
+ throw new Error("Page number must be >= 1");
662
+ }
663
+ if (page_size < 1) {
664
+ throw new Error("Page size must be >= 1");
665
+ }
666
+ const offset = (page_number - 1) * page_size;
667
+
668
+ // Get total count of items
669
+ const countStmt = this.connection.conn.prepare('SELECT COUNT(*) as total FROM card');
670
+ const {
671
+ total
672
+ } = countStmt.get();
673
+ console.log('Total cards:', total);
674
+
675
+ // Get page of items
676
+ const stmt = this.connection.conn.prepare(`
677
+ SELECT content, g_time, hash
678
+ FROM card
679
+ ORDER BY g_time DESC
680
+ LIMIT ? OFFSET ?
681
+ `);
682
+ const rows = stmt.all(page_size, offset);
683
+ console.log('Rows found:', rows.length);
684
+
685
+ // Convert rows to cards
686
+ const items = [];
687
+ for (const row of rows) {
688
+ const {
689
+ content,
690
+ g_time,
691
+ hash
692
+ } = row;
693
+
694
+ // Parse content if it's a JSON string
695
+ let parsedContent = content;
696
+ if (typeof content === 'string' && (content.startsWith('{') || content.startsWith('['))) {
697
+ try {
698
+ parsedContent = JSON.parse(content);
699
+ } catch (e) {
700
+ console.warn('Failed to parse JSON content for hash:', hash);
701
+ }
702
+ }
703
+
704
+ // Create card with properly parsed content
705
+ const card = new _mcard.MCardFromData(parsedContent, hash, g_time);
706
+ items.push(card);
707
+ }
708
+
709
+ // Calculate total pages
710
+ const total_pages = Math.ceil(total / page_size);
711
+ return new _cardCollection.Page({
712
+ items,
713
+ total_items: total,
714
+ page_number,
715
+ page_size,
716
+ has_next: offset + page_size < total,
717
+ has_previous: page_number > 1,
718
+ total_pages
719
+ });
720
+ }
721
+ }
722
+ exports.SQLiteEngine = SQLiteEngine;
723
+ //# sourceMappingURL=sqlite_engine.js.map