@terminals-tech/sdk 1.0.0-rc.1 → 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 (82) hide show
  1. package/README.md +15 -19
  2. package/dist/WebContainerManager-4LIOGRVM.js +22 -0
  3. package/dist/browser-http-client-ZQLDWZMU.js +317 -0
  4. package/dist/cache-VKYSQRXX.js +45 -0
  5. package/dist/capabilities-MIPUMBLL.js +96 -0
  6. package/dist/chunk-2ESYSVXG.js +48 -0
  7. package/dist/chunk-2WTYE4SW.js +190 -0
  8. package/dist/chunk-3LFMIVJM.js +40 -0
  9. package/dist/chunk-ABCK4FWN.js +136 -0
  10. package/dist/chunk-AFDUOYHD.js +2060 -0
  11. package/dist/chunk-BCOQMFKT.js +265 -0
  12. package/dist/chunk-BYXBJQAS.js +0 -0
  13. package/dist/chunk-DKFJIILR.js +9798 -0
  14. package/dist/chunk-EXI3LJVJ.js +51 -0
  15. package/dist/chunk-FOXUEYWK.js +42 -0
  16. package/dist/chunk-GJWAJAX3.js +173 -0
  17. package/dist/chunk-H3POJCFA.js +333 -0
  18. package/dist/chunk-KASHT6C5.js +784 -0
  19. package/dist/chunk-KHR7ZYCX.js +4034 -0
  20. package/dist/chunk-L45BSQDJ.js +296 -0
  21. package/dist/chunk-LLGZTP3G.js +5521 -0
  22. package/dist/chunk-NTMBOESX.js +152 -0
  23. package/dist/chunk-OCLSAUCD.js +474 -0
  24. package/dist/chunk-OSSRZOGC.js +190 -0
  25. package/dist/chunk-PPFTKJDB.js +497 -0
  26. package/dist/chunk-PWAHFID5.js +381 -0
  27. package/dist/chunk-Q2VI6ICE.js +188 -0
  28. package/dist/chunk-QJFKEQHF.js +6460 -0
  29. package/dist/chunk-QWXPVB2L.js +320 -0
  30. package/dist/chunk-QWZRZKLZ.js +896 -0
  31. package/dist/chunk-STMI72WH.js +1005 -0
  32. package/dist/chunk-TSQ3BGLA.js +11945 -0
  33. package/dist/chunk-UJDUQNE2.js +79 -0
  34. package/dist/chunk-VZA2NUH3.js +118 -0
  35. package/dist/chunk-WGBCRNMB.js +1817 -0
  36. package/dist/chunk-WU4OTGJE.js +752 -0
  37. package/dist/chunk-XPJ63Y6T.js +70 -0
  38. package/dist/chunk-Y2EULKA2.js +172 -0
  39. package/dist/chunk-YJEZWCYV.js +94 -0
  40. package/dist/chunk-ZVO47SQV.js +150 -0
  41. package/dist/container-lite-QD3CRLS4.js +327 -0
  42. package/dist/core-H2UUDATO.js +146 -0
  43. package/dist/crypto-D4LMI2RN.js +45 -0
  44. package/dist/db-BWC2GGBN.js +50 -0
  45. package/dist/demo-T655Z5S4.js +87 -0
  46. package/dist/diagnostics-6RQTBR6I.js +113 -0
  47. package/dist/dist-OPDCWARF.js +727 -0
  48. package/dist/dist-VXJEKX3T.js +2441 -0
  49. package/dist/dist-VYGJXGUS.js +1008 -0
  50. package/dist/embeddings-7QXTXUMC.js +15 -0
  51. package/dist/embeddings-MAEWWUHW.js +9 -0
  52. package/dist/graph-RKMNE2X5.js +36 -0
  53. package/dist/hvm-CBEP3M4F.js +126 -0
  54. package/dist/index.cjs +49874 -8001
  55. package/dist/index.d.cts +1629 -1363
  56. package/dist/index.d.ts +1629 -1363
  57. package/dist/index.js +2462 -8130
  58. package/dist/mcp-NK34ZNM5.js +101 -0
  59. package/dist/mcp-client-service-browser-SGB2K3VZ.js +14 -0
  60. package/dist/neuro-state-XHRGIRVO.js +498 -0
  61. package/dist/nodes-K6GKI2FM.js +364 -0
  62. package/dist/package-EXUIU2RL.js +93 -0
  63. package/dist/package-VGL7HYTO.js +106 -0
  64. package/dist/package-XHMLOAQ4.js +98 -0
  65. package/dist/pg-events-QJAM2HIP.js +15 -0
  66. package/dist/pglite-adapter-43IOUBMV.js +50 -0
  67. package/dist/pgliteService-IUGNNOVU.js +258 -0
  68. package/dist/policy-IRJCM6FS.js +13 -0
  69. package/dist/registry-5WTDYQVQ.js +26 -0
  70. package/dist/registry-FW63E7FE.js +16 -0
  71. package/dist/registry-ZQ2IBLF6.js +9 -0
  72. package/dist/resolver-ALOJSOK5.js +24 -0
  73. package/dist/scheduler-B5CEYKWT.js +127 -0
  74. package/dist/secret-store-H7273UIT.js +18 -0
  75. package/dist/server-VW6DYDLH.js +18 -0
  76. package/dist/skills-VN7IN7SJ.js +6375 -0
  77. package/dist/stack-4KWCQQP7.js +103 -0
  78. package/dist/storage-L7MWNSPG.js +13 -0
  79. package/dist/supabaseService-6AYP2VY3.js +476 -0
  80. package/dist/topology-CIWWNVAN.js +13 -0
  81. package/dist/webcontainer-XWCE56F3.js +281 -0
  82. package/package.json +9 -3
@@ -0,0 +1,1817 @@
1
+ import {
2
+ deleteDatabaseByDataDir
3
+ } from "./chunk-3LFMIVJM.js";
4
+ import {
5
+ DORMANT_BIOSIGNAL_BRIDGES,
6
+ DORMANT_BIOSIGNAL_PROVIDERS,
7
+ PGLiteConnectionPool,
8
+ getCurrentDataDir,
9
+ getDB,
10
+ getDataDirForScope,
11
+ isBiosignalBridgeId,
12
+ isBiosignalProviderId,
13
+ isDatabaseReady,
14
+ setDbScope,
15
+ shutdownPool
16
+ } from "./chunk-AFDUOYHD.js";
17
+
18
+ // ../../lib/pglite/utils.ts
19
+ var __warnedDbNotReady = false;
20
+ async function safeDbOperation(operation, fallback) {
21
+ if (!isDatabaseReady()) {
22
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true" && !__warnedDbNotReady) {
23
+ console.warn("Database not ready, using fallback value");
24
+ __warnedDbNotReady = true;
25
+ }
26
+ return fallback;
27
+ }
28
+ try {
29
+ return await operation();
30
+ } catch (error) {
31
+ console.error("Database operation failed:", error);
32
+ return fallback;
33
+ }
34
+ }
35
+ function robustJsonParse(jsonString, fallback = null) {
36
+ if (!jsonString) return fallback;
37
+ if (typeof jsonString === "object") return jsonString;
38
+ try {
39
+ return JSON.parse(jsonString);
40
+ } catch {
41
+ return fallback;
42
+ }
43
+ }
44
+
45
+ // ../../lib/pglite/maintenance.ts
46
+ async function checkpointDatabase() {
47
+ const db = await getDB();
48
+ try {
49
+ await db.exec("CHECKPOINT");
50
+ } catch (err) {
51
+ console.warn("[PGLite] CHECKPOINT failed:", err);
52
+ }
53
+ }
54
+ async function resetDatabase(scope) {
55
+ const oldDataDir = scope ? getDataDirForScope(scope) : getCurrentDataDir();
56
+ shutdownPool();
57
+ await deleteDatabaseByDataDir(oldDataDir);
58
+ if (scope) {
59
+ setDbScope(scope);
60
+ }
61
+ }
62
+
63
+ // ../../lib/pglite/queries/progress.ts
64
+ async function getUserProgress(userId, quizId) {
65
+ console.log("PGLITE_DEBUG: getUserProgress called with userId:", userId, "quizId:", quizId);
66
+ const db = await getDB();
67
+ if (!userId || !quizId) return null;
68
+ const result = await db.query(
69
+ "SELECT * FROM user_progress WHERE user_id = $1 AND quiz_id = $2;",
70
+ [userId, quizId]
71
+ );
72
+ return result.rows[0] || null;
73
+ }
74
+ async function getAllUserProgress(userId) {
75
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
76
+ console.log("PGLITE_DEBUG: getAllUserProgress called with userId:", userId);
77
+ }
78
+ return safeDbOperation(async () => {
79
+ const db = await getDB();
80
+ if (!userId) return [];
81
+ const result = await db.query(
82
+ "SELECT * FROM user_progress WHERE user_id = $1;",
83
+ [userId]
84
+ );
85
+ return result.rows;
86
+ }, []);
87
+ }
88
+ async function upsertUserProgress(progress) {
89
+ const db = await getDB();
90
+ if (!progress.user_id || !progress.quiz_id) {
91
+ throw new Error("User ID and Quiz ID are required for upsertUserProgress");
92
+ }
93
+ const {
94
+ user_id,
95
+ quiz_id,
96
+ highest_score,
97
+ completed_at,
98
+ unlocked_rewards,
99
+ current_question_index,
100
+ answers
101
+ } = progress;
102
+ const result = await db.query(
103
+ `INSERT INTO user_progress (user_id, quiz_id, highest_score, completed_at, unlocked_rewards, current_question_index, answers, last_attempted_at)
104
+ VALUES ($1, $2, $3, $4, $5, $6, $7, CURRENT_TIMESTAMP)
105
+ ON CONFLICT (user_id, quiz_id) DO UPDATE SET
106
+ highest_score = GREATEST(EXCLUDED.highest_score, user_progress.highest_score),
107
+ completed_at = COALESCE(EXCLUDED.completed_at, user_progress.completed_at),
108
+ unlocked_rewards = COALESCE(EXCLUDED.unlocked_rewards, user_progress.unlocked_rewards),
109
+ current_question_index = EXCLUDED.current_question_index,
110
+ answers = EXCLUDED.answers,
111
+ last_attempted_at = CURRENT_TIMESTAMP
112
+ RETURNING *;`,
113
+ [
114
+ user_id,
115
+ quiz_id,
116
+ highest_score,
117
+ completed_at,
118
+ JSON.stringify(unlocked_rewards || []),
119
+ current_question_index,
120
+ JSON.stringify(answers || {})
121
+ ]
122
+ );
123
+ return result.rows[0];
124
+ }
125
+
126
+ // ../../lib/pglite/queries/user.ts
127
+ async function getUserProfileByXverseAddress(address) {
128
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
129
+ console.log("PGLITE_DEBUG: getUserProfileByXverseAddress called with address:", address);
130
+ }
131
+ return safeDbOperation(async () => {
132
+ const db = await getDB();
133
+ if (!address) return null;
134
+ const result = await db.query(
135
+ "SELECT * FROM user_profiles WHERE xverse_ord_address = $1;",
136
+ [address]
137
+ );
138
+ return result.rows[0] || null;
139
+ }, null);
140
+ }
141
+ async function getUserProfile(userId) {
142
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
143
+ console.log("PGLITE_DEBUG: getUserProfile called with userId:", userId);
144
+ }
145
+ return safeDbOperation(async () => {
146
+ const db = await getDB();
147
+ if (!userId) return null;
148
+ const result = await db.query(
149
+ "SELECT * FROM user_profiles WHERE user_id = $1;",
150
+ [userId]
151
+ );
152
+ return result.rows[0] || null;
153
+ }, null);
154
+ }
155
+ async function getEffectiveOrCreateUserProfile(walletAddress, anonymousUserIdProvider) {
156
+ const db = await getDB();
157
+ if (walletAddress) {
158
+ const existingProfile = await getUserProfileByXverseAddress(walletAddress);
159
+ if (existingProfile && existingProfile.user_id) {
160
+ console.log(
161
+ `PGLITE_DEBUG: Found existing profile for wallet ${walletAddress} by address, user_id: ${existingProfile.user_id}`
162
+ );
163
+ return existingProfile.user_id;
164
+ }
165
+ console.log(
166
+ `PGLITE_DEBUG: No existing profile found for wallet ${walletAddress}. Attempting to create.`
167
+ );
168
+ try {
169
+ const newUuid = crypto.randomUUID();
170
+ const createdProfile = await createUserProfile({
171
+ user_id: newUuid,
172
+ username: null,
173
+ avatar_url: null,
174
+ xverse_ord_address: walletAddress,
175
+ shape_hash: null,
176
+ preferences: { unlocked_reward_ids: [] },
177
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
178
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
179
+ });
180
+ console.log(
181
+ `PGLITE_DEBUG: Successfully created profile for wallet ${walletAddress}, user_id: ${createdProfile.user_id}`
182
+ );
183
+ return createdProfile.user_id;
184
+ } catch (error) {
185
+ let isUniqueViolation = false;
186
+ const err = error;
187
+ const errStr = String(err?.message || err?.toString?.() || "").toLowerCase();
188
+ if (err?.code === "23505") {
189
+ isUniqueViolation = true;
190
+ } else if (errStr.includes("unique constraint") || errStr.includes("duplicate key")) {
191
+ if (errStr.includes("user_profiles_xverse_ord_address_key") || errStr.includes("unique constraint failed")) {
192
+ isUniqueViolation = true;
193
+ }
194
+ }
195
+ if (isUniqueViolation) {
196
+ console.warn(
197
+ `PGLITE_DEBUG: Create failed for wallet ${walletAddress} due to unique constraint (Code: ${err?.code}, Message: ${errStr}). This might happen if a concurrent creation succeeded. Fetching again...`
198
+ );
199
+ const profileAfterConcurrentCreate = await getUserProfileByXverseAddress(walletAddress);
200
+ if (profileAfterConcurrentCreate && profileAfterConcurrentCreate.user_id) {
201
+ console.log(
202
+ `PGLITE_DEBUG: Fetched profile for wallet ${walletAddress} after concurrent creation, user_id: ${profileAfterConcurrentCreate.user_id}`
203
+ );
204
+ return profileAfterConcurrentCreate.user_id;
205
+ } else {
206
+ console.error(
207
+ `PGLITE_ERROR: Profile creation for ${walletAddress} failed unique constraint, and subsequent fetch also failed. This indicates a deeper issue.`
208
+ );
209
+ throw new Error(
210
+ `Failed to get or create profile for wallet ${walletAddress}: Unique constraint hit, but profile not found subsequently.`
211
+ );
212
+ }
213
+ } else {
214
+ console.error(
215
+ `PGLITE_ERROR: Error during profile creation for wallet ${walletAddress} (not a recognized unique violation):`,
216
+ error
217
+ );
218
+ throw error;
219
+ }
220
+ }
221
+ } else {
222
+ const anonymousUuid = anonymousUserIdProvider();
223
+ if (!anonymousUuid) {
224
+ const newAnonUuid = crypto.randomUUID();
225
+ console.warn(
226
+ "anonymousUserIdProvider returned null/empty, generated a new UUID:",
227
+ newAnonUuid
228
+ );
229
+ const profile2 = await getUserProfile(newAnonUuid);
230
+ if (profile2 && profile2.user_id) {
231
+ return profile2.user_id;
232
+ }
233
+ const createdProfile = await createUserProfile({
234
+ user_id: newAnonUuid,
235
+ username: "Anonymous",
236
+ avatar_url: null,
237
+ xverse_ord_address: null,
238
+ shape_hash: null,
239
+ preferences: { unlocked_reward_ids: [] },
240
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
241
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
242
+ });
243
+ return createdProfile.user_id;
244
+ }
245
+ const profile = await getUserProfile(anonymousUuid);
246
+ if (profile && profile.user_id) {
247
+ return profile.user_id;
248
+ } else {
249
+ console.log(`No profile for anonymous UUID ${anonymousUuid}, creating new profile.`);
250
+ const createdProfile = await createUserProfile({
251
+ user_id: anonymousUuid,
252
+ username: "Anonymous",
253
+ avatar_url: null,
254
+ xverse_ord_address: null,
255
+ shape_hash: null,
256
+ preferences: { unlocked_reward_ids: [] },
257
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
258
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
259
+ });
260
+ return createdProfile.user_id;
261
+ }
262
+ }
263
+ }
264
+ async function createUserProfile(profileData) {
265
+ const db = await getDB();
266
+ const preferences = profileData.preferences || { unlocked_reward_ids: [] };
267
+ if (!preferences.unlocked_reward_ids) {
268
+ preferences.unlocked_reward_ids = [];
269
+ }
270
+ const result = await db.query(
271
+ `INSERT INTO user_profiles (user_id, username, avatar_url, xverse_ord_address, shape_hash, api_keys, preferences)
272
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
273
+ ON CONFLICT (user_id)
274
+ DO UPDATE SET
275
+ username = COALESCE(EXCLUDED.username, user_profiles.username),
276
+ avatar_url = COALESCE(EXCLUDED.avatar_url, user_profiles.avatar_url),
277
+ xverse_ord_address = COALESCE(EXCLUDED.xverse_ord_address, user_profiles.xverse_ord_address),
278
+ shape_hash = COALESCE(EXCLUDED.shape_hash, user_profiles.shape_hash),
279
+ api_keys = COALESCE(EXCLUDED.api_keys, user_profiles.api_keys),
280
+ preferences = COALESCE(EXCLUDED.preferences, user_profiles.preferences),
281
+ updated_at = CURRENT_TIMESTAMP
282
+ RETURNING *;`,
283
+ [
284
+ profileData.user_id,
285
+ profileData.username,
286
+ profileData.avatar_url,
287
+ profileData.xverse_ord_address,
288
+ profileData.shape_hash,
289
+ profileData.api_keys ? JSON.stringify(profileData.api_keys) : "{}",
290
+ JSON.stringify(preferences)
291
+ ]
292
+ );
293
+ if (result.rows.length === 0) {
294
+ throw new Error("Failed to create user profile.");
295
+ }
296
+ return result.rows[0];
297
+ }
298
+ async function updateUserProfile(userId, updates) {
299
+ const db = await getDB();
300
+ if (!userId) {
301
+ console.error("updateUserProfile called with empty userId");
302
+ return null;
303
+ }
304
+ const setClauses = [];
305
+ const values = [userId];
306
+ let paramIndex = 2;
307
+ for (const [key, value] of Object.entries(updates)) {
308
+ if ((key === "preferences" || key === "api_keys") && typeof value === "object" && value !== null) {
309
+ setClauses.push(`${key} = $${paramIndex++}`);
310
+ values.push(JSON.stringify(value));
311
+ } else if (key !== "preferences" && key !== "api_keys" && key !== "user_id" && value !== void 0) {
312
+ setClauses.push(`${key} = $${paramIndex++}`);
313
+ values.push(value);
314
+ }
315
+ }
316
+ if (setClauses.length === 0) {
317
+ return getUserProfile(userId);
318
+ }
319
+ const query = `UPDATE user_profiles SET ${setClauses.join(", ")}, updated_at = CURRENT_TIMESTAMP WHERE user_id = $1 RETURNING *;`;
320
+ const result = await db.query(query, values);
321
+ return result.rows[0] || null;
322
+ }
323
+ async function saveUserArtifact(artifact) {
324
+ const db = await getDB();
325
+ const result = await db.query(
326
+ `INSERT INTO user_artifacts (user_id, source_applet, artifact_type, display_type, artifact_name, content, blob_content, metadata)
327
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
328
+ RETURNING *;`,
329
+ [
330
+ artifact.user_id,
331
+ artifact.source_applet,
332
+ artifact.artifact_type,
333
+ artifact.display_type || null,
334
+ artifact.artifact_name,
335
+ artifact.content || null,
336
+ artifact.blob_content || null,
337
+ artifact.metadata ? JSON.stringify(artifact.metadata) : null
338
+ ]
339
+ );
340
+ return result.rows[0];
341
+ }
342
+ async function getUserArtifacts(userId, sourceApplet) {
343
+ const db = await getDB();
344
+ if (sourceApplet) {
345
+ const result2 = await db.query(
346
+ "SELECT * FROM user_artifacts WHERE user_id = $1 AND source_applet = $2 ORDER BY created_at DESC;",
347
+ [userId, sourceApplet]
348
+ );
349
+ return result2.rows;
350
+ }
351
+ const result = await db.query(
352
+ "SELECT * FROM user_artifacts WHERE user_id = $1 ORDER BY created_at DESC;",
353
+ [userId]
354
+ );
355
+ return result.rows;
356
+ }
357
+ async function getUserArtifactById(artifactId) {
358
+ const db = await getDB();
359
+ const result = await db.query("SELECT * FROM user_artifacts WHERE id = $1;", [
360
+ artifactId
361
+ ]);
362
+ return result.rows[0] || null;
363
+ }
364
+ async function deleteUserArtifact(artifactId) {
365
+ const db = await getDB();
366
+ const result = await db.query("DELETE FROM user_artifacts WHERE id = $1;", [artifactId]);
367
+ return result.affectedRows !== void 0 && result.affectedRows > 0;
368
+ }
369
+
370
+ // ../../lib/pglite/queries/quiz.ts
371
+ async function getAllQuizzes() {
372
+ return safeDbOperation(async () => {
373
+ const db = await getDB();
374
+ const result = await db.query("SELECT * FROM quizzes ORDER BY title;");
375
+ return result.rows;
376
+ }, []);
377
+ }
378
+ async function getQuizById(id) {
379
+ const db = await getDB();
380
+ const result = await db.query("SELECT * FROM quizzes WHERE id = $1;", [id]);
381
+ return result.rows[0] || null;
382
+ }
383
+ var GET_ALL_QUIZZES_WITH_USER_PROGRESS_SQL = `
384
+ SELECT
385
+ q.*,
386
+ up.highest_score,
387
+ CASE WHEN up.completed_at IS NOT NULL THEN TRUE ELSE FALSE END as is_completed
388
+ FROM quizzes q
389
+ LEFT JOIN user_progress up ON q.id = up.quiz_id AND up.user_id = $1
390
+ ORDER BY q.title;
391
+ `;
392
+ async function getAllQuizzesWithUserProgress(userId) {
393
+ console.log("PGLITE_DEBUG: getAllQuizzesWithUserProgress called with userId:", userId);
394
+ const db = await getDB();
395
+ const result = await db.query(
396
+ GET_ALL_QUIZZES_WITH_USER_PROGRESS_SQL,
397
+ [userId]
398
+ );
399
+ return result.rows.map((row) => ({
400
+ ...row,
401
+ questions: row.questions ? typeof row.questions === "string" ? JSON.parse(row.questions) : row.questions : []
402
+ }));
403
+ }
404
+ var SEARCH_QUIZZES_SQL = `
405
+ SELECT q.*,
406
+ similarity(q.title, $1) AS title_sim,
407
+ similarity(COALESCE(q.description, ''), $1) AS desc_sim
408
+ FROM quizzes q
409
+ WHERE q.title % $1 OR COALESCE(q.description, '') % $1
410
+ ORDER BY GREATEST(
411
+ similarity(q.title, $1),
412
+ similarity(COALESCE(q.description, ''), $1)
413
+ ) DESC;
414
+ `;
415
+ async function searchQuizzes(searchText) {
416
+ const db = await getDB();
417
+ if (!searchText) {
418
+ return getAllQuizzes();
419
+ }
420
+ await db.exec(`SELECT set_limit(0.1);`);
421
+ const result = await db.query(SEARCH_QUIZZES_SQL, [searchText]);
422
+ return result.rows;
423
+ }
424
+ async function checkQuizCompletion(userId, quizId) {
425
+ const db = await getDB();
426
+ if (!userId || !quizId) return false;
427
+ const result = await db.query(
428
+ "SELECT completed_at FROM user_progress WHERE user_id = $1 AND quiz_id = $2;",
429
+ [userId, quizId]
430
+ );
431
+ const progress = result.rows[0];
432
+ return !!progress && progress.completed_at !== null;
433
+ }
434
+ async function getAllRewards() {
435
+ return safeDbOperation(async () => {
436
+ const db = await getDB();
437
+ const result = await db.query("SELECT * FROM rewards ORDER BY title;");
438
+ return result.rows;
439
+ }, []);
440
+ }
441
+ async function getRewardById(id) {
442
+ const db = await getDB();
443
+ const result = await db.query("SELECT * FROM rewards WHERE id = $1;", [id]);
444
+ return result.rows[0] || null;
445
+ }
446
+ async function completeQuizAndProcessRewards(userId, quizId, score) {
447
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
448
+ console.log(
449
+ "PGLITE_DEBUG: completeQuizAndProcessRewards called with userId:",
450
+ userId,
451
+ "quizId:",
452
+ quizId
453
+ );
454
+ }
455
+ const db = await getDB();
456
+ if (!userId || !quizId) {
457
+ console.error("User ID and Quiz ID are required for completeQuizAndProcessRewards");
458
+ return [];
459
+ }
460
+ const unlockedRewardsForThisQuiz = [];
461
+ await upsertUserProgress({
462
+ user_id: userId,
463
+ quiz_id: quizId,
464
+ highest_score: score,
465
+ completed_at: (/* @__PURE__ */ new Date()).toISOString(),
466
+ unlocked_rewards: [],
467
+ current_question_index: null,
468
+ answers: null
469
+ });
470
+ console.log(`Quiz ${quizId} marked as completed for user ${userId} with score ${score}`);
471
+ const allUserProgressEntries = await getAllUserProgress(userId);
472
+ const completedQuizIds = new Set(
473
+ allUserProgressEntries.filter((p) => p.completed_at).map((p) => p.quiz_id)
474
+ );
475
+ const allRewardsList = await getAllRewards();
476
+ let userProfile = await getUserProfile(userId);
477
+ if (!userProfile) {
478
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
479
+ console.error(
480
+ `Profile not found for UUID ${userId} in completeQuizAndProcessRewards. This might indicate an issue if getEffectiveOrCreateUserProfile was not called prior.`
481
+ );
482
+ }
483
+ userProfile = await createUserProfile({
484
+ user_id: userId,
485
+ username: null,
486
+ avatar_url: null,
487
+ xverse_ord_address: null,
488
+ shape_hash: null,
489
+ preferences: { unlocked_reward_ids: [] },
490
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
491
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
492
+ });
493
+ }
494
+ const preferences = userProfile.preferences || { unlocked_reward_ids: [] };
495
+ if (!preferences.unlocked_reward_ids) {
496
+ preferences.unlocked_reward_ids = [];
497
+ }
498
+ const currentUserUnlockedRewardIds = new Set(preferences.unlocked_reward_ids);
499
+ for (const reward of allRewardsList) {
500
+ if (!currentUserUnlockedRewardIds.has(reward.id)) {
501
+ const requiredQuizzes = reward.quiz_ids_required || [];
502
+ if (requiredQuizzes.length === 0 || requiredQuizzes.every((reqQuizId) => completedQuizIds.has(reqQuizId))) {
503
+ currentUserUnlockedRewardIds.add(reward.id);
504
+ unlockedRewardsForThisQuiz.push(reward.id);
505
+ }
506
+ }
507
+ }
508
+ if (unlockedRewardsForThisQuiz.length > 0) {
509
+ const updatedPreferences = {
510
+ ...preferences,
511
+ unlocked_reward_ids: Array.from(currentUserUnlockedRewardIds)
512
+ };
513
+ await updateUserProfile(userId, { preferences: updatedPreferences });
514
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
515
+ console.log(`User ${userId} unlocked new rewards: ${unlockedRewardsForThisQuiz.join(", ")}`);
516
+ }
517
+ }
518
+ return unlockedRewardsForThisQuiz;
519
+ }
520
+ async function unlockReward(userId, rewardId) {
521
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
522
+ console.log("PGLITE_DEBUG: unlockReward called with userId:", userId, "rewardId:", rewardId);
523
+ }
524
+ const db = await getDB();
525
+ if (!userId || !rewardId) {
526
+ console.error("User ID and Reward ID are required for unlockReward");
527
+ return false;
528
+ }
529
+ try {
530
+ let userProfile = await getUserProfile(userId);
531
+ if (!userProfile) {
532
+ console.error(`Profile not found for UUID ${userId} in unlockReward.`);
533
+ userProfile = await createUserProfile({
534
+ user_id: userId,
535
+ username: null,
536
+ avatar_url: null,
537
+ xverse_ord_address: null,
538
+ shape_hash: null,
539
+ preferences: { unlocked_reward_ids: [rewardId] },
540
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
541
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
542
+ });
543
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
544
+ console.log(
545
+ `Reward ${rewardId} unlocked for new user profile (UUID ${userId}) via direct call in unlockReward.`
546
+ );
547
+ }
548
+ return true;
549
+ }
550
+ const preferences = userProfile.preferences || { unlocked_reward_ids: [] };
551
+ if (!preferences.unlocked_reward_ids) {
552
+ preferences.unlocked_reward_ids = [];
553
+ }
554
+ const unlockedRewardIdsSet = new Set(preferences.unlocked_reward_ids);
555
+ if (unlockedRewardIdsSet.has(rewardId)) {
556
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
557
+ console.log(`Reward ${rewardId} already unlocked for user ${userId}.`);
558
+ }
559
+ return false;
560
+ }
561
+ unlockedRewardIdsSet.add(rewardId);
562
+ const updatedPreferences = {
563
+ ...preferences,
564
+ unlocked_reward_ids: Array.from(unlockedRewardIdsSet)
565
+ };
566
+ await updateUserProfile(userId, { preferences: updatedPreferences });
567
+ if (process.env.NEXT_PUBLIC_DEBUG_DB === "true") {
568
+ console.log(`Reward ${rewardId} unlocked for user ${userId} via direct call.`);
569
+ }
570
+ return true;
571
+ } catch (error) {
572
+ console.error("PGLiteService: Error unlocking reward:", error);
573
+ return false;
574
+ }
575
+ }
576
+
577
+ // ../../lib/pglite/queries/resources.ts
578
+ async function getAllResourceCategories() {
579
+ const db = await getDB();
580
+ const result = await db.query(
581
+ "SELECT * FROM resource_categories ORDER BY name;"
582
+ );
583
+ return result.rows;
584
+ }
585
+ async function getResourceCategoryById(id) {
586
+ const db = await getDB();
587
+ const result = await db.query(
588
+ "SELECT * FROM resource_categories WHERE id = $1;",
589
+ [id]
590
+ );
591
+ return result.rows[0] || null;
592
+ }
593
+ async function searchResourceCategories(searchText, similarityThreshold = 0.1) {
594
+ const db = await getDB();
595
+ if (!searchText) {
596
+ return getAllResourceCategories();
597
+ }
598
+ await db.exec(`SELECT set_limit(${similarityThreshold});`);
599
+ const query = `
600
+ SELECT *,
601
+ similarity(name, $1) AS name_similarity,
602
+ similarity(description, $1) AS description_similarity
603
+ FROM resource_categories
604
+ WHERE (
605
+ name % $1 OR
606
+ description % $1
607
+ )
608
+ ORDER BY GREATEST(similarity(name, $1), similarity(description, $1)) DESC;
609
+ `;
610
+ const result = await db.query(query, [searchText]);
611
+ return result.rows;
612
+ }
613
+ async function getAllResourceDocuments(categoryId) {
614
+ const db = await getDB();
615
+ if (categoryId) {
616
+ const result2 = await db.query(
617
+ "SELECT * FROM resource_documents WHERE category_id = $1 ORDER BY title;",
618
+ [categoryId]
619
+ );
620
+ return result2.rows;
621
+ }
622
+ const result = await db.query(
623
+ "SELECT * FROM resource_documents ORDER BY title;"
624
+ );
625
+ return result.rows;
626
+ }
627
+ async function getResourceDocumentById(id) {
628
+ const db = await getDB();
629
+ const result = await db.query(
630
+ "SELECT * FROM resource_documents WHERE id = $1;",
631
+ [id]
632
+ );
633
+ return result.rows[0] || null;
634
+ }
635
+ async function searchResourceDocuments(searchText, categoryId, similarityThreshold = 0.1) {
636
+ const db = await getDB();
637
+ if (!searchText) {
638
+ return getAllResourceDocuments(categoryId);
639
+ }
640
+ await db.exec(`SELECT set_limit(${similarityThreshold});`);
641
+ let query = `
642
+ SELECT *,
643
+ similarity(title, $1) AS title_similarity,
644
+ similarity(content, $1) AS content_similarity,
645
+ similarity(array_to_string(tags, ' '), $1) AS tags_similarity
646
+ FROM resource_documents
647
+ WHERE (
648
+ title % $1 OR
649
+ content % $1 OR
650
+ array_to_string(tags, ' ') % $1
651
+ )
652
+ `;
653
+ const params = [searchText];
654
+ if (categoryId) {
655
+ query += ` AND category_id = $2`;
656
+ params.push(categoryId);
657
+ }
658
+ query += `
659
+ ORDER BY GREATEST(
660
+ similarity(title, $1),
661
+ similarity(content, $1),
662
+ similarity(array_to_string(tags, ' '), $1)
663
+ ) DESC;
664
+ `;
665
+ const result = await db.query(query, params);
666
+ return result.rows;
667
+ }
668
+ async function getAllFriends() {
669
+ const db = await getDB();
670
+ const result = await db.query("SELECT * FROM friends ORDER BY name;");
671
+ return result.rows;
672
+ }
673
+ async function getAllInspirations() {
674
+ const db = await getDB();
675
+ const result = await db.query("SELECT * FROM inspirations ORDER BY name;");
676
+ return result.rows;
677
+ }
678
+
679
+ // ../../lib/pglite/queries/chat.ts
680
+ var connectionPool = PGLiteConnectionPool.getInstance();
681
+ async function createChatSession(params = {}) {
682
+ const db = await getDB();
683
+ const newSessionId = params.sessionId || crypto.randomUUID();
684
+ const now = (/* @__PURE__ */ new Date()).toISOString();
685
+ const result = await db.query(
686
+ "INSERT INTO chat_sessions (id, user_id, session_title, created_at, updated_at, metadata) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;",
687
+ [
688
+ newSessionId,
689
+ params.userId || null,
690
+ params.sessionTitle || `Session ${newSessionId.substring(0, 8)}`,
691
+ now,
692
+ now,
693
+ JSON.stringify(params.metadata || {})
694
+ ]
695
+ );
696
+ if (result.rows.length === 0) {
697
+ throw new Error("Failed to create chat session.");
698
+ }
699
+ return result.rows[0];
700
+ }
701
+ async function getAllChatSessions(userId) {
702
+ if (!connectionPool.getStats().isInitialized) {
703
+ console.warn("PGLite connection pool not ready, returning empty chat sessions array");
704
+ return [];
705
+ }
706
+ try {
707
+ const db = await getDB();
708
+ if (userId) {
709
+ const result = await db.query(
710
+ "SELECT * FROM chat_sessions WHERE user_id = $1 ORDER BY updated_at DESC;",
711
+ [userId]
712
+ );
713
+ return result.rows;
714
+ } else {
715
+ const result = await db.query(
716
+ "SELECT * FROM chat_sessions ORDER BY updated_at DESC;"
717
+ );
718
+ return result.rows;
719
+ }
720
+ } catch (error) {
721
+ console.error("Error in getAllChatSessions:", error);
722
+ return [];
723
+ }
724
+ }
725
+ async function getChatSession(sessionId, userId) {
726
+ const db = await getDB();
727
+ if (!sessionId) return null;
728
+ let query = "SELECT * FROM chat_sessions WHERE id = $1";
729
+ const queryParams = [sessionId];
730
+ if (userId) {
731
+ query += " AND user_id = $2";
732
+ queryParams.push(userId);
733
+ }
734
+ const result = await db.query(query, queryParams);
735
+ return result.rows[0] || null;
736
+ }
737
+ async function updateChatSessionTitle(sessionId, newTitle, userId) {
738
+ const db = await getDB();
739
+ if (!sessionId) return;
740
+ let query = "UPDATE chat_sessions SET session_title = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2";
741
+ const queryParams = [newTitle, sessionId];
742
+ if (userId) {
743
+ query += " AND user_id = $3";
744
+ queryParams.push(userId);
745
+ }
746
+ await db.query(query, queryParams);
747
+ }
748
+ async function deleteChatSession(sessionId, userId) {
749
+ if (!userId) {
750
+ throw new Error("deleteChatSession requires a valid userId to prevent cross-user data access.");
751
+ }
752
+ const db = await getDB();
753
+ if (!sessionId) return;
754
+ const query = "DELETE FROM chat_sessions WHERE id = $1 AND user_id = $2";
755
+ await db.query(query, [sessionId, userId]);
756
+ }
757
+ async function clearChatMessages(sessionId, userId) {
758
+ if (!userId) {
759
+ throw new Error("clearChatMessages requires a valid userId to prevent cross-user data access.");
760
+ }
761
+ const db = await getDB();
762
+ if (!sessionId) return;
763
+ const checkOwner = await db.query("SELECT 1 FROM chat_sessions WHERE id = $1 AND user_id = $2", [
764
+ sessionId,
765
+ userId
766
+ ]);
767
+ if (checkOwner.rows.length === 0) {
768
+ throw new Error("Unauthorized: session does not belong to requesting user.");
769
+ }
770
+ await db.query("DELETE FROM chat_messages WHERE session_id = $1;", [sessionId]);
771
+ }
772
+ async function addChatMessage(params) {
773
+ const db = await getDB();
774
+ const { sessionId, role, content, userId, messageId, toolCallId, toolCalls, name, metadata } = params;
775
+ const newChatMessageId = messageId || crypto.randomUUID();
776
+ const now = (/* @__PURE__ */ new Date()).toISOString();
777
+ const result = await db.query(
778
+ "INSERT INTO chat_messages (id, session_id, user_id, role, content, tool_call_id, tool_calls, name, timestamp, metadata) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *;",
779
+ [
780
+ newChatMessageId,
781
+ sessionId,
782
+ userId || null,
783
+ role,
784
+ content,
785
+ toolCallId || null,
786
+ toolCalls ? JSON.stringify(toolCalls) : null,
787
+ name || null,
788
+ now,
789
+ JSON.stringify(metadata || {})
790
+ ]
791
+ );
792
+ if (result.rows.length === 0) {
793
+ throw new Error("Failed to add chat message.");
794
+ }
795
+ return result.rows[0];
796
+ }
797
+ async function getChatMessages(sessionId, limit = 100, offset = 0) {
798
+ const db = await getDB();
799
+ try {
800
+ const result = await db.query(
801
+ `
802
+ SELECT id, session_id, role, content, user_id, tool_call_id, tool_calls, name,
803
+ to_char(timestamp, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') as timestamp, metadata
804
+ FROM chat_messages
805
+ WHERE session_id = $1
806
+ ORDER BY timestamp ASC
807
+ LIMIT $2 OFFSET $3;
808
+ `,
809
+ [sessionId, limit, offset]
810
+ );
811
+ return result.rows.map((row) => ({
812
+ ...row,
813
+ tool_calls: robustJsonParse(row.tool_calls, null),
814
+ metadata: robustJsonParse(row.metadata, null)
815
+ }));
816
+ } catch (error) {
817
+ console.error("PGLiteService: Error getting chat messages:", error);
818
+ throw error;
819
+ }
820
+ }
821
+ async function getChatMessageCount(sessionId) {
822
+ const db = await getDB();
823
+ if (!sessionId) return 0;
824
+ try {
825
+ const result = await db.query(
826
+ `
827
+ SELECT COUNT(*) FROM chat_messages WHERE session_id = $1;
828
+ `,
829
+ [sessionId]
830
+ );
831
+ return result.rows[0]?.count || 0;
832
+ } catch (error) {
833
+ console.error("PGLiteService: Error getting chat message count:", error);
834
+ return 0;
835
+ }
836
+ }
837
+
838
+ // ../../lib/pglite/queries/deploy.ts
839
+ async function createDeployment(params) {
840
+ const db = await getDB();
841
+ const id = params.id || crypto.randomUUID();
842
+ const now = (/* @__PURE__ */ new Date()).toISOString();
843
+ const result = await db.query(
844
+ `INSERT INTO deployments (id, user_id, stack_id, provider, status, deploy_url, preview_url, inspector_url, error, config, created_at, updated_at)
845
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
846
+ RETURNING *;`,
847
+ [
848
+ id,
849
+ params.user_id ?? null,
850
+ params.stack_id ?? null,
851
+ params.provider,
852
+ params.status ?? "idle",
853
+ params.deploy_url ?? null,
854
+ params.preview_url ?? null,
855
+ params.inspector_url ?? null,
856
+ params.error ?? null,
857
+ params.config ? JSON.stringify(params.config) : null,
858
+ now,
859
+ now
860
+ ]
861
+ );
862
+ if (result.rows.length === 0) {
863
+ throw new Error("Failed to create deployment.");
864
+ }
865
+ return result.rows[0];
866
+ }
867
+ async function getDeployment(id) {
868
+ const db = await getDB();
869
+ const result = await db.query(
870
+ "SELECT * FROM deployments WHERE id = $1;",
871
+ [id]
872
+ );
873
+ return result.rows[0] || null;
874
+ }
875
+ async function getDeploymentsByStack(stackId, limit = 50) {
876
+ const db = await getDB();
877
+ const result = await db.query(
878
+ "SELECT * FROM deployments WHERE stack_id = $1 ORDER BY created_at DESC LIMIT $2;",
879
+ [stackId, limit]
880
+ );
881
+ return result.rows;
882
+ }
883
+ async function getDeploymentsByUser(userId, limit = 50) {
884
+ const db = await getDB();
885
+ const result = await db.query(
886
+ "SELECT * FROM deployments WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2;",
887
+ [userId, limit]
888
+ );
889
+ return result.rows;
890
+ }
891
+ async function updateDeploymentStatus(id, status, fields) {
892
+ const db = await getDB();
893
+ const now = (/* @__PURE__ */ new Date()).toISOString();
894
+ const setClauses = ["status = $2", "updated_at = $3"];
895
+ const queryParams = [id, status, now];
896
+ let paramIdx = 4;
897
+ if (fields?.deploy_url !== void 0) {
898
+ setClauses.push(`deploy_url = $${paramIdx}`);
899
+ queryParams.push(fields.deploy_url);
900
+ paramIdx++;
901
+ }
902
+ if (fields?.preview_url !== void 0) {
903
+ setClauses.push(`preview_url = $${paramIdx}`);
904
+ queryParams.push(fields.preview_url);
905
+ paramIdx++;
906
+ }
907
+ if (fields?.inspector_url !== void 0) {
908
+ setClauses.push(`inspector_url = $${paramIdx}`);
909
+ queryParams.push(fields.inspector_url);
910
+ paramIdx++;
911
+ }
912
+ if (fields?.error !== void 0) {
913
+ setClauses.push(`error = $${paramIdx}`);
914
+ queryParams.push(fields.error);
915
+ paramIdx++;
916
+ }
917
+ const result = await db.query(
918
+ `UPDATE deployments SET ${setClauses.join(", ")} WHERE id = $1 RETURNING *;`,
919
+ queryParams
920
+ );
921
+ return result.rows[0] || null;
922
+ }
923
+ async function deleteDeployment(id) {
924
+ const db = await getDB();
925
+ const result = await db.query("DELETE FROM deployments WHERE id = $1;", [id]);
926
+ return (result.affectedRows ?? 0) > 0;
927
+ }
928
+
929
+ // ../../lib/pglite/queries/workspace.ts
930
+ function parseWorkspaceRow(row) {
931
+ return {
932
+ id: row.id,
933
+ user_id: row.user_id,
934
+ stack_id: row.stack_id || null,
935
+ open_files: robustJsonParse(row.open_files, []) ?? [],
936
+ active_file: row.active_file || null,
937
+ cursor_positions: robustJsonParse(
938
+ row.cursor_positions,
939
+ {}
940
+ ) ?? {},
941
+ scroll_positions: robustJsonParse(row.scroll_positions, {}) ?? {},
942
+ pane_layout: robustJsonParse(row.pane_layout, null),
943
+ editor_settings: robustJsonParse(row.editor_settings, null),
944
+ created_at: row.created_at,
945
+ updated_at: row.updated_at
946
+ };
947
+ }
948
+ async function getWorkspaceState(userId, stackId) {
949
+ const db = await getDB();
950
+ let query;
951
+ let params;
952
+ if (stackId) {
953
+ query = "SELECT * FROM workspace_state WHERE user_id = $1 AND stack_id = $2;";
954
+ params = [userId, stackId];
955
+ } else {
956
+ query = "SELECT * FROM workspace_state WHERE user_id = $1 AND stack_id IS NULL;";
957
+ params = [userId];
958
+ }
959
+ const result = await db.query(query, params);
960
+ if (result.rows.length === 0) return null;
961
+ return parseWorkspaceRow(result.rows[0]);
962
+ }
963
+ async function saveWorkspaceState(state) {
964
+ const db = await getDB();
965
+ const id = state.id || crypto.randomUUID();
966
+ const now = (/* @__PURE__ */ new Date()).toISOString();
967
+ const result = await db.query(
968
+ `INSERT INTO workspace_state (id, user_id, stack_id, open_files, active_file, cursor_positions, scroll_positions, pane_layout, editor_settings, created_at, updated_at)
969
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
970
+ ON CONFLICT (id) DO UPDATE SET
971
+ open_files = EXCLUDED.open_files,
972
+ active_file = EXCLUDED.active_file,
973
+ cursor_positions = EXCLUDED.cursor_positions,
974
+ scroll_positions = EXCLUDED.scroll_positions,
975
+ pane_layout = EXCLUDED.pane_layout,
976
+ editor_settings = EXCLUDED.editor_settings,
977
+ updated_at = EXCLUDED.updated_at
978
+ RETURNING *;`,
979
+ [
980
+ id,
981
+ state.user_id,
982
+ state.stack_id ?? null,
983
+ JSON.stringify(state.open_files),
984
+ state.active_file ?? null,
985
+ JSON.stringify(state.cursor_positions),
986
+ JSON.stringify(state.scroll_positions),
987
+ state.pane_layout ? JSON.stringify(state.pane_layout) : null,
988
+ state.editor_settings ? JSON.stringify(state.editor_settings) : null,
989
+ now,
990
+ now
991
+ ]
992
+ );
993
+ if (result.rows.length === 0) {
994
+ throw new Error("Failed to save workspace state.");
995
+ }
996
+ return parseWorkspaceRow(result.rows[0]);
997
+ }
998
+ async function deleteWorkspaceState(id) {
999
+ const db = await getDB();
1000
+ const result = await db.query("DELETE FROM workspace_state WHERE id = $1;", [id]);
1001
+ return (result.affectedRows ?? 0) > 0;
1002
+ }
1003
+ async function getAllWorkspaceStates(userId) {
1004
+ const db = await getDB();
1005
+ const result = await db.query(
1006
+ "SELECT * FROM workspace_state WHERE user_id = $1 ORDER BY updated_at DESC;",
1007
+ [userId]
1008
+ );
1009
+ return result.rows.map(parseWorkspaceRow);
1010
+ }
1011
+
1012
+ // ../../lib/pglite/queries/skill-runs.ts
1013
+ async function createSkillRun(params) {
1014
+ const db = await getDB();
1015
+ const id = params.id || crypto.randomUUID();
1016
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1017
+ const result = await db.query(
1018
+ `INSERT INTO skill_runs (id, user_id, skill_slug, status, input, created_at, updated_at)
1019
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
1020
+ RETURNING *;`,
1021
+ [
1022
+ id,
1023
+ params.user_id ?? null,
1024
+ params.skill_slug,
1025
+ params.status ?? "pending",
1026
+ params.input ? JSON.stringify(params.input) : null,
1027
+ now,
1028
+ now
1029
+ ]
1030
+ );
1031
+ if (result.rows.length === 0) {
1032
+ throw new Error("Failed to create skill run.");
1033
+ }
1034
+ return result.rows[0];
1035
+ }
1036
+ async function getSkillRun(id) {
1037
+ const db = await getDB();
1038
+ const result = await db.query("SELECT * FROM skill_runs WHERE id = $1;", [id]);
1039
+ return result.rows[0] || null;
1040
+ }
1041
+ async function getSkillRunsBySlug(skillSlug, limit = 50) {
1042
+ const db = await getDB();
1043
+ const result = await db.query(
1044
+ "SELECT * FROM skill_runs WHERE skill_slug = $1 ORDER BY created_at DESC LIMIT $2;",
1045
+ [skillSlug, limit]
1046
+ );
1047
+ return result.rows;
1048
+ }
1049
+ async function getSkillRunsByUser(userId, limit = 50) {
1050
+ const db = await getDB();
1051
+ const result = await db.query(
1052
+ "SELECT * FROM skill_runs WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2;",
1053
+ [userId, limit]
1054
+ );
1055
+ return result.rows;
1056
+ }
1057
+ var IMMUTABLE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
1058
+ async function updateSkillRunStatus(id, status, fields) {
1059
+ const db = await getDB();
1060
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1061
+ const setClauses = ["status = $2", "updated_at = $3"];
1062
+ const queryParams = [id, status, now];
1063
+ let paramIdx = 4;
1064
+ if (fields?.artifacts !== void 0) {
1065
+ setClauses.push(`artifacts = $${paramIdx}`);
1066
+ queryParams.push(JSON.stringify(fields.artifacts));
1067
+ paramIdx++;
1068
+ }
1069
+ if (fields?.agent_log !== void 0) {
1070
+ setClauses.push(`agent_log = $${paramIdx}`);
1071
+ queryParams.push(JSON.stringify(fields.agent_log));
1072
+ paramIdx++;
1073
+ }
1074
+ if (fields?.error !== void 0) {
1075
+ setClauses.push(`error = $${paramIdx}`);
1076
+ queryParams.push(fields.error);
1077
+ paramIdx++;
1078
+ }
1079
+ if (fields?.started_at !== void 0) {
1080
+ setClauses.push(`started_at = $${paramIdx}`);
1081
+ queryParams.push(fields.started_at);
1082
+ paramIdx++;
1083
+ }
1084
+ if (fields?.completed_at !== void 0) {
1085
+ setClauses.push(`completed_at = $${paramIdx}`);
1086
+ queryParams.push(fields.completed_at);
1087
+ paramIdx++;
1088
+ }
1089
+ const result = await db.query(
1090
+ `UPDATE skill_runs SET ${setClauses.join(", ")}
1091
+ WHERE id = $1 AND status NOT IN ('completed', 'failed', 'cancelled')
1092
+ RETURNING *;`,
1093
+ queryParams
1094
+ );
1095
+ if (result.rows.length === 0) {
1096
+ const existing = await db.query(
1097
+ "SELECT status FROM skill_runs WHERE id = $1;",
1098
+ [id]
1099
+ );
1100
+ if (existing.rows.length > 0 && IMMUTABLE_STATUSES.has(existing.rows[0].status)) {
1101
+ console.warn(
1102
+ `[SkillRuns] Rejected status update for ${id}: run is already "${existing.rows[0].status}" (immutable)`
1103
+ );
1104
+ }
1105
+ return null;
1106
+ }
1107
+ return result.rows[0];
1108
+ }
1109
+ async function cleanupOrphanedRuns() {
1110
+ if (!PGLiteConnectionPool.getInstance().getStats().isInitialized) return 0;
1111
+ const db = await getDB();
1112
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1113
+ const result = await db.query(
1114
+ `UPDATE skill_runs SET status = 'failed', error = 'Orphaned: browser closed during execution', updated_at = $1
1115
+ WHERE status = 'running';`,
1116
+ [now]
1117
+ );
1118
+ return result.affectedRows ?? 0;
1119
+ }
1120
+ async function deleteSkillRun(id) {
1121
+ const db = await getDB();
1122
+ const result = await db.query("DELETE FROM skill_runs WHERE id = $1;", [id]);
1123
+ return (result.affectedRows ?? 0) > 0;
1124
+ }
1125
+
1126
+ // ../../lib/pglite/queries/forge.ts
1127
+ function parseJsonb(val) {
1128
+ return typeof val === "string" ? JSON.parse(val) : val;
1129
+ }
1130
+ async function saveForgeScene(params) {
1131
+ const db = await getDB();
1132
+ const id = params.id || crypto.randomUUID();
1133
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1134
+ const result = await db.query(
1135
+ `INSERT INTO forge_scenes (id, user_id, name, scene_data, created_at, updated_at)
1136
+ VALUES ($1, $2, $3, $4, $5, $6)
1137
+ ON CONFLICT (id) DO UPDATE SET
1138
+ name = EXCLUDED.name,
1139
+ scene_data = EXCLUDED.scene_data,
1140
+ updated_at = EXCLUDED.updated_at
1141
+ RETURNING *;`,
1142
+ [id, params.user_id ?? null, params.name, JSON.stringify(params.scene_data), now, now]
1143
+ );
1144
+ const row = result.rows[0];
1145
+ return { ...row, scene_data: parseJsonb(row.scene_data) };
1146
+ }
1147
+ async function getForgeScene(id) {
1148
+ const db = await getDB();
1149
+ const result = await db.query(
1150
+ "SELECT * FROM forge_scenes WHERE id = $1;",
1151
+ [id]
1152
+ );
1153
+ const row = result.rows[0] || null;
1154
+ return row ? { ...row, scene_data: parseJsonb(row.scene_data) } : null;
1155
+ }
1156
+ async function getForgeScenesByUser(userId, limit = 50) {
1157
+ const db = await getDB();
1158
+ const result = await db.query(
1159
+ "SELECT * FROM forge_scenes WHERE user_id = $1 ORDER BY updated_at DESC LIMIT $2;",
1160
+ [userId, limit]
1161
+ );
1162
+ return result.rows.map((r) => ({ ...r, scene_data: parseJsonb(r.scene_data) }));
1163
+ }
1164
+ async function deleteForgeScene(id) {
1165
+ const db = await getDB();
1166
+ const result = await db.query("DELETE FROM forge_scenes WHERE id = $1;", [id]);
1167
+ return (result.affectedRows ?? 0) > 0;
1168
+ }
1169
+ async function saveForgeIteration(params) {
1170
+ const db = await getDB();
1171
+ const id = params.id || crypto.randomUUID();
1172
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1173
+ const result = await db.query(
1174
+ `INSERT INTO forge_iterations (id, scene_id, user_id, tau, sync_order, coherence, parameter_snapshot, created_at)
1175
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
1176
+ RETURNING *;`,
1177
+ [
1178
+ id,
1179
+ params.scene_id,
1180
+ params.user_id ?? null,
1181
+ params.tau,
1182
+ params.sync_order ?? null,
1183
+ params.coherence ? JSON.stringify(params.coherence) : null,
1184
+ params.parameter_snapshot ? JSON.stringify(params.parameter_snapshot) : null,
1185
+ now
1186
+ ]
1187
+ );
1188
+ const row = result.rows[0];
1189
+ return {
1190
+ ...row,
1191
+ coherence: row.coherence ? parseJsonb(row.coherence) : null,
1192
+ parameter_snapshot: row.parameter_snapshot ? parseJsonb(row.parameter_snapshot) : null
1193
+ };
1194
+ }
1195
+ async function getForgeIterationsByScene(sceneId, limit = 100) {
1196
+ const db = await getDB();
1197
+ const result = await db.query(
1198
+ "SELECT * FROM forge_iterations WHERE scene_id = $1 ORDER BY created_at DESC LIMIT $2;",
1199
+ [sceneId, limit]
1200
+ );
1201
+ return result.rows.map((r) => ({
1202
+ ...r,
1203
+ coherence: r.coherence ? parseJsonb(r.coherence) : null,
1204
+ parameter_snapshot: r.parameter_snapshot ? parseJsonb(r.parameter_snapshot) : null
1205
+ }));
1206
+ }
1207
+ async function saveForgeMaterialProfile(params) {
1208
+ const db = await getDB();
1209
+ const id = params.id || crypto.randomUUID();
1210
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1211
+ const result = await db.query(
1212
+ `INSERT INTO forge_material_profiles (id, user_id, name, properties, acoustic_signature, neural_baseline, crystallization, created_at, updated_at)
1213
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
1214
+ ON CONFLICT (id) DO UPDATE SET
1215
+ name = EXCLUDED.name,
1216
+ properties = EXCLUDED.properties,
1217
+ acoustic_signature = EXCLUDED.acoustic_signature,
1218
+ neural_baseline = EXCLUDED.neural_baseline,
1219
+ crystallization = EXCLUDED.crystallization,
1220
+ updated_at = EXCLUDED.updated_at
1221
+ RETURNING *;`,
1222
+ [
1223
+ id,
1224
+ params.user_id ?? null,
1225
+ params.name,
1226
+ JSON.stringify(params.properties),
1227
+ params.acoustic_signature ? JSON.stringify(params.acoustic_signature) : null,
1228
+ params.neural_baseline ? JSON.stringify(params.neural_baseline) : null,
1229
+ params.crystallization ?? 0,
1230
+ now,
1231
+ now
1232
+ ]
1233
+ );
1234
+ const row = result.rows[0];
1235
+ return {
1236
+ ...row,
1237
+ properties: parseJsonb(row.properties),
1238
+ acoustic_signature: row.acoustic_signature ? parseJsonb(row.acoustic_signature) : null,
1239
+ neural_baseline: row.neural_baseline ? parseJsonb(row.neural_baseline) : null
1240
+ };
1241
+ }
1242
+ async function getForgeMaterialProfiles(userId, limit = 50) {
1243
+ const db = await getDB();
1244
+ const result = await db.query(
1245
+ "SELECT * FROM forge_material_profiles WHERE user_id = $1 ORDER BY updated_at DESC LIMIT $2;",
1246
+ [userId, limit]
1247
+ );
1248
+ return result.rows.map((r) => ({
1249
+ ...r,
1250
+ properties: parseJsonb(r.properties),
1251
+ acoustic_signature: r.acoustic_signature ? parseJsonb(r.acoustic_signature) : null,
1252
+ neural_baseline: r.neural_baseline ? parseJsonb(r.neural_baseline) : null
1253
+ }));
1254
+ }
1255
+ async function deleteForgeMaterialProfile(id) {
1256
+ const db = await getDB();
1257
+ const result = await db.query("DELETE FROM forge_material_profiles WHERE id = $1;", [id]);
1258
+ return (result.affectedRows ?? 0) > 0;
1259
+ }
1260
+
1261
+ // ../../lib/pglite/queries/biosignal-bridges.ts
1262
+ function coerceBoolean(value) {
1263
+ if (typeof value === "boolean") return value;
1264
+ if (typeof value === "number") return value !== 0;
1265
+ if (typeof value === "string") {
1266
+ const normalized = value.trim().toLowerCase();
1267
+ return normalized === "true" || normalized === "t" || normalized === "1";
1268
+ }
1269
+ return false;
1270
+ }
1271
+ function normalizeBridgeRow(row) {
1272
+ if (typeof row.id !== "string" || !isBiosignalBridgeId(row.id)) {
1273
+ return null;
1274
+ }
1275
+ const domains = robustJsonParse(row.domains, []);
1276
+ const signalFamilies = robustJsonParse(row.signal_families, []);
1277
+ const config = robustJsonParse(row.config, {}) ?? {};
1278
+ return {
1279
+ id: row.id,
1280
+ label: typeof row.label === "string" ? row.label : row.id,
1281
+ transport: typeof row.transport === "string" ? row.transport : "process",
1282
+ domains: Array.isArray(domains) ? domains : [],
1283
+ signal_families: Array.isArray(signalFamilies) ? signalFamilies : [],
1284
+ status: typeof row.status === "string" ? row.status : "dormant",
1285
+ enabled: coerceBoolean(row.enabled),
1286
+ config,
1287
+ created_at: typeof row.created_at === "string" ? row.created_at : "",
1288
+ updated_at: typeof row.updated_at === "string" ? row.updated_at : ""
1289
+ };
1290
+ }
1291
+ async function listBiosignalBridgeConfigs() {
1292
+ return safeDbOperation(async () => {
1293
+ const db = await getDB();
1294
+ const result = await db.query(
1295
+ `SELECT id, label, transport, domains, signal_families, status, enabled, config, created_at, updated_at
1296
+ FROM biosignal_bridge_config
1297
+ ORDER BY id ASC`
1298
+ );
1299
+ return result.rows.map((row) => normalizeBridgeRow(row)).filter((row) => row !== null);
1300
+ }, []);
1301
+ }
1302
+ async function getBiosignalBridgeConfig(id) {
1303
+ return safeDbOperation(async () => {
1304
+ const db = await getDB();
1305
+ const result = await db.query(
1306
+ `SELECT id, label, transport, domains, signal_families, status, enabled, config, created_at, updated_at
1307
+ FROM biosignal_bridge_config
1308
+ WHERE id = $1
1309
+ LIMIT 1`,
1310
+ [id]
1311
+ );
1312
+ return normalizeBridgeRow(result.rows[0] ?? {});
1313
+ }, null);
1314
+ }
1315
+ async function listEnabledBiosignalBridges() {
1316
+ return safeDbOperation(async () => {
1317
+ const db = await getDB();
1318
+ const result = await db.query(
1319
+ `SELECT id, label, transport, domains, signal_families, status, enabled, config, created_at, updated_at
1320
+ FROM biosignal_bridge_config
1321
+ WHERE enabled = TRUE
1322
+ ORDER BY id ASC`
1323
+ );
1324
+ return result.rows.map((row) => normalizeBridgeRow(row)).filter((row) => row !== null);
1325
+ }, []);
1326
+ }
1327
+ async function setBiosignalBridgeEnabled(id, enabled) {
1328
+ return safeDbOperation(async () => {
1329
+ const db = await getDB();
1330
+ const result = await db.query(
1331
+ `UPDATE biosignal_bridge_config
1332
+ SET enabled = $2,
1333
+ updated_at = CURRENT_TIMESTAMP
1334
+ WHERE id = $1`,
1335
+ [id, enabled]
1336
+ );
1337
+ return (result.affectedRows ?? 0) > 0;
1338
+ }, false);
1339
+ }
1340
+ async function reseedDormantBiosignalBridges() {
1341
+ return safeDbOperation(async () => {
1342
+ const db = await getDB();
1343
+ for (const bridge of DORMANT_BIOSIGNAL_BRIDGES) {
1344
+ await db.query(
1345
+ `INSERT INTO biosignal_bridge_config
1346
+ (id, label, transport, domains, signal_families, status, enabled, config)
1347
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
1348
+ ON CONFLICT (id) DO NOTHING`,
1349
+ [
1350
+ bridge.id,
1351
+ bridge.label,
1352
+ bridge.transport,
1353
+ JSON.stringify(bridge.domains),
1354
+ JSON.stringify(bridge.signalFamilies),
1355
+ bridge.status,
1356
+ bridge.enabled,
1357
+ JSON.stringify(bridge.config)
1358
+ ]
1359
+ );
1360
+ }
1361
+ return true;
1362
+ }, false);
1363
+ }
1364
+
1365
+ // ../../lib/pglite/queries/biosignal-providers.ts
1366
+ function coerceBoolean2(value) {
1367
+ if (typeof value === "boolean") return value;
1368
+ if (typeof value === "number") return value !== 0;
1369
+ if (typeof value === "string") {
1370
+ const normalized = value.trim().toLowerCase();
1371
+ return normalized === "true" || normalized === "t" || normalized === "1";
1372
+ }
1373
+ return false;
1374
+ }
1375
+ function normalizeProviderRow(row) {
1376
+ if (typeof row.id !== "string" || !isBiosignalProviderId(row.id)) {
1377
+ return null;
1378
+ }
1379
+ const domains = robustJsonParse(row.domains, []);
1380
+ const signalFamilies = robustJsonParse(row.signal_families, []);
1381
+ const metrics = robustJsonParse(row.metrics, []);
1382
+ return {
1383
+ id: row.id,
1384
+ label: typeof row.label === "string" ? row.label : row.id,
1385
+ provider_type: typeof row.provider_type === "string" ? row.provider_type : "gateway",
1386
+ acquisition_mode: typeof row.acquisition_mode === "string" ? row.acquisition_mode : "local",
1387
+ domains: Array.isArray(domains) ? domains : [],
1388
+ signal_families: Array.isArray(signalFamilies) ? signalFamilies : [],
1389
+ metrics: Array.isArray(metrics) ? metrics : [],
1390
+ status: typeof row.status === "string" ? row.status : "dormant",
1391
+ enabled: coerceBoolean2(row.enabled),
1392
+ notes: typeof row.notes === "string" ? row.notes : "",
1393
+ created_at: typeof row.created_at === "string" ? row.created_at : "",
1394
+ updated_at: typeof row.updated_at === "string" ? row.updated_at : ""
1395
+ };
1396
+ }
1397
+ async function listBiosignalProviderConfigs() {
1398
+ return safeDbOperation(async () => {
1399
+ const db = await getDB();
1400
+ const result = await db.query(
1401
+ `SELECT id, label, provider_type, acquisition_mode, domains, signal_families, metrics,
1402
+ status, enabled, notes, created_at, updated_at
1403
+ FROM biosignal_provider_config
1404
+ ORDER BY id ASC`
1405
+ );
1406
+ return result.rows.map((row) => normalizeProviderRow(row)).filter((row) => row !== null);
1407
+ }, []);
1408
+ }
1409
+ async function getBiosignalProviderConfig(id) {
1410
+ return safeDbOperation(async () => {
1411
+ const db = await getDB();
1412
+ const result = await db.query(
1413
+ `SELECT id, label, provider_type, acquisition_mode, domains, signal_families, metrics,
1414
+ status, enabled, notes, created_at, updated_at
1415
+ FROM biosignal_provider_config
1416
+ WHERE id = $1
1417
+ LIMIT 1`,
1418
+ [id]
1419
+ );
1420
+ return normalizeProviderRow(result.rows[0] ?? {});
1421
+ }, null);
1422
+ }
1423
+ async function listEnabledBiosignalProviders() {
1424
+ return safeDbOperation(async () => {
1425
+ const db = await getDB();
1426
+ const result = await db.query(
1427
+ `SELECT id, label, provider_type, acquisition_mode, domains, signal_families, metrics,
1428
+ status, enabled, notes, created_at, updated_at
1429
+ FROM biosignal_provider_config
1430
+ WHERE enabled = TRUE
1431
+ ORDER BY id ASC`
1432
+ );
1433
+ return result.rows.map((row) => normalizeProviderRow(row)).filter((row) => row !== null);
1434
+ }, []);
1435
+ }
1436
+ async function setBiosignalProviderEnabled(id, enabled) {
1437
+ return safeDbOperation(async () => {
1438
+ const db = await getDB();
1439
+ const result = await db.query(
1440
+ `UPDATE biosignal_provider_config
1441
+ SET enabled = $2,
1442
+ updated_at = CURRENT_TIMESTAMP
1443
+ WHERE id = $1`,
1444
+ [id, enabled]
1445
+ );
1446
+ return (result.affectedRows ?? 0) > 0;
1447
+ }, false);
1448
+ }
1449
+ async function reseedDormantBiosignalProviders() {
1450
+ return safeDbOperation(async () => {
1451
+ const db = await getDB();
1452
+ for (const provider of DORMANT_BIOSIGNAL_PROVIDERS) {
1453
+ await db.query(
1454
+ `INSERT INTO biosignal_provider_config
1455
+ (id, label, provider_type, acquisition_mode, domains, signal_families, metrics, status, enabled, notes)
1456
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
1457
+ ON CONFLICT (id) DO NOTHING`,
1458
+ [
1459
+ provider.id,
1460
+ provider.label,
1461
+ provider.providerType,
1462
+ provider.acquisitionMode,
1463
+ JSON.stringify(provider.domains),
1464
+ JSON.stringify(provider.signalFamilies),
1465
+ JSON.stringify(provider.metrics),
1466
+ provider.status,
1467
+ provider.enabled,
1468
+ provider.notes
1469
+ ]
1470
+ );
1471
+ }
1472
+ return true;
1473
+ }, false);
1474
+ }
1475
+
1476
+ // ../../lib/pgliteService.ts
1477
+ async function initializeVectorSearch() {
1478
+ const db = await getDB();
1479
+ try {
1480
+ await db.exec(`CREATE EXTENSION IF NOT EXISTS vector;`);
1481
+ const indexExists = await db.query(
1482
+ `SELECT 1 FROM pg_indexes WHERE indexname = 'idx_resource_documents_embedding';`
1483
+ );
1484
+ if (indexExists.rows.length === 0) {
1485
+ await db.exec(
1486
+ `CREATE INDEX idx_resource_documents_embedding ON resource_documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);`
1487
+ );
1488
+ }
1489
+ } catch (error) {
1490
+ console.error("Failed to initialize vector search:", error);
1491
+ }
1492
+ }
1493
+ async function storeDocumentEmbedding(documentId, embedding) {
1494
+ const db = await getDB();
1495
+ await db.query(`UPDATE resource_documents SET embedding = $1 WHERE id = $2;`, [
1496
+ `[${embedding.join(",")}]`,
1497
+ documentId
1498
+ ]);
1499
+ }
1500
+ async function batchStoreEmbeddings(embeddings) {
1501
+ const db = await getDB();
1502
+ for (const { id, embedding } of embeddings) {
1503
+ await storeDocumentEmbedding(id, embedding);
1504
+ }
1505
+ }
1506
+ async function searchDocumentsByVector(queryEmbedding, limit = 10) {
1507
+ const db = await getDB();
1508
+ const result = await db.query(
1509
+ `SELECT resource_documents.*, embedding <=> $1 AS similarity_score
1510
+ FROM resource_documents
1511
+ WHERE embedding IS NOT NULL
1512
+ ORDER BY similarity_score
1513
+ LIMIT $2;`,
1514
+ [`[${queryEmbedding.join(",")}]`, limit]
1515
+ );
1516
+ return result.rows.map((row) => ({
1517
+ ...row,
1518
+ similarity_score: Number(row.similarity_score ?? 0),
1519
+ search_type: "semantic"
1520
+ }));
1521
+ }
1522
+ function escapeLike(input) {
1523
+ return input.replace(/[\\%_]/g, "\\$&");
1524
+ }
1525
+ async function searchDocumentsByText(query, limit) {
1526
+ const db = await getDB();
1527
+ const trimmed = query.trim();
1528
+ const likeQuery = escapeLike(trimmed);
1529
+ const result = await db.query(
1530
+ `SELECT *, similarity(title, $1) AS similarity_score
1531
+ FROM resource_documents
1532
+ WHERE similarity(title, $1) > 0.1
1533
+ OR title ILIKE '%' || $2 || '%' ESCAPE '\\'
1534
+ OR content ILIKE '%' || $2 || '%' ESCAPE '\\'
1535
+ ORDER BY similarity(title, $1) DESC
1536
+ LIMIT $3;`,
1537
+ [trimmed, likeQuery, limit]
1538
+ );
1539
+ return result.rows.map((row) => ({
1540
+ ...row,
1541
+ similarity_score: Number(row.similarity_score ?? 0),
1542
+ search_type: "text"
1543
+ }));
1544
+ }
1545
+ async function hybridSearchDocuments(options) {
1546
+ const limit = options.limit ?? 10;
1547
+ const hasVector = options.embedding && options.embedding.length > 0;
1548
+ const hasText = options.query && options.query.trim().length > 0;
1549
+ if (!hasVector && !hasText) return [];
1550
+ if (hasVector && !hasText) {
1551
+ const results = await searchDocumentsByVector(options.embedding, limit);
1552
+ return results.map((row) => ({ ...row, search_type: "semantic" }));
1553
+ }
1554
+ if (hasText && !hasVector) {
1555
+ return searchDocumentsByText(options.query, limit);
1556
+ }
1557
+ const [vectorResults, textResults] = await Promise.all([
1558
+ searchDocumentsByVector(options.embedding, limit * 2),
1559
+ searchDocumentsByText(options.query, limit * 2)
1560
+ ]);
1561
+ const k = 60;
1562
+ const scores = /* @__PURE__ */ new Map();
1563
+ vectorResults.forEach((row, i) => {
1564
+ const rrf = 1 / (k + i + 1);
1565
+ const existing = scores.get(row.id);
1566
+ scores.set(row.id, {
1567
+ score: (existing?.score ?? 0) + rrf,
1568
+ row: existing?.row ?? row
1569
+ });
1570
+ });
1571
+ textResults.forEach((row, i) => {
1572
+ const rrf = 1 / (k + i + 1);
1573
+ const existing = scores.get(row.id);
1574
+ scores.set(row.id, {
1575
+ score: (existing?.score ?? 0) + rrf,
1576
+ row: existing?.row ?? row
1577
+ });
1578
+ });
1579
+ return Array.from(scores.values()).sort((a, b) => b.score - a.score).slice(0, limit).map(({ score, row }) => ({
1580
+ ...row,
1581
+ similarity_score: score,
1582
+ search_type: "hybrid"
1583
+ }));
1584
+ }
1585
+ async function findSimilarDocuments(documentId, limit = 5) {
1586
+ const db = await getDB();
1587
+ const doc = await db.query(`SELECT embedding FROM resource_documents WHERE id = $1;`, [
1588
+ documentId
1589
+ ]);
1590
+ const row = doc.rows[0];
1591
+ if (!row?.embedding) return [];
1592
+ return searchDocumentsByVector(row.embedding, limit);
1593
+ }
1594
+ async function getDocumentsWithoutEmbeddings(limit = 100) {
1595
+ const db = await getDB();
1596
+ const result = await db.query(
1597
+ `SELECT id, title, content FROM resource_documents WHERE embedding IS NULL LIMIT $1;`,
1598
+ [limit]
1599
+ );
1600
+ return result.rows;
1601
+ }
1602
+ async function getVectorSearchMetrics() {
1603
+ const db = await getDB();
1604
+ const total = await db.query(`SELECT COUNT(*) as count FROM resource_documents;`);
1605
+ const withEmbeddings = await db.query(
1606
+ `SELECT COUNT(*) as count FROM resource_documents WHERE embedding IS NOT NULL;`
1607
+ );
1608
+ const totalRow = total.rows[0];
1609
+ const withEmbRow = withEmbeddings.rows[0];
1610
+ const totalCount = parseInt(totalRow?.count || "0", 10);
1611
+ const withEmbCount = parseInt(withEmbRow?.count || "0", 10);
1612
+ return {
1613
+ totalDocuments: totalCount,
1614
+ documentsWithEmbeddings: withEmbCount,
1615
+ documentsWithoutEmbeddings: totalCount - withEmbCount
1616
+ };
1617
+ }
1618
+ async function upsertConversationEmbedding(params) {
1619
+ const db = await getDB();
1620
+ await db.query(
1621
+ `INSERT INTO conversation_embeddings (session_id, message_id, role, content_snippet, embedding)
1622
+ VALUES ($1, $2, $3, $4, $5)
1623
+ ON CONFLICT (session_id, message_id)
1624
+ DO UPDATE SET embedding = EXCLUDED.embedding;`,
1625
+ [
1626
+ params.sessionId,
1627
+ params.messageId,
1628
+ params.role,
1629
+ params.contentSnippet,
1630
+ `[${params.embedding.join(",")}]`
1631
+ ]
1632
+ );
1633
+ }
1634
+ async function retrieveTopKForSession(sessionId, queryEmbedding, k = 5) {
1635
+ const db = await getDB();
1636
+ const result = await db.query(
1637
+ `SELECT message_id, role, content_snippet, embedding <=> $1 AS distance
1638
+ FROM conversation_embeddings
1639
+ WHERE session_id = $2
1640
+ ORDER BY distance
1641
+ LIMIT $3;`,
1642
+ [`[${queryEmbedding.join(",")}]`, sessionId, k]
1643
+ );
1644
+ return result.rows;
1645
+ }
1646
+ async function createSavepoint(name) {
1647
+ const db = await getDB();
1648
+ await db.exec(`SAVEPOINT ${name};`);
1649
+ }
1650
+ async function rollbackToSavepoint(name) {
1651
+ const db = await getDB();
1652
+ await db.exec(`ROLLBACK TO SAVEPOINT ${name};`);
1653
+ }
1654
+ async function releaseSavepoint(name) {
1655
+ const db = await getDB();
1656
+ await db.exec(`RELEASE SAVEPOINT ${name};`);
1657
+ }
1658
+ async function withSavepoint(name, operation) {
1659
+ await createSavepoint(name);
1660
+ try {
1661
+ const result = await operation();
1662
+ await releaseSavepoint(name);
1663
+ return result;
1664
+ } catch (error) {
1665
+ await rollbackToSavepoint(name);
1666
+ throw error;
1667
+ }
1668
+ }
1669
+ async function saveGenericState(key, value) {
1670
+ const db = await getDB();
1671
+ await db.query(
1672
+ `INSERT INTO key_value_store (key, value) VALUES ($1, $2)
1673
+ ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;`,
1674
+ [key, JSON.stringify(value)]
1675
+ );
1676
+ }
1677
+ async function getPgStatIoStats() {
1678
+ const db = await getDB();
1679
+ try {
1680
+ const result = await db.query(`SELECT * FROM pg_stat_io;`);
1681
+ return result.rows;
1682
+ } catch {
1683
+ return [];
1684
+ }
1685
+ }
1686
+ async function getPGLiteIndexedDbSize() {
1687
+ if (typeof indexedDB === "undefined") return 0;
1688
+ try {
1689
+ const estimate = await navigator.storage?.estimate();
1690
+ return estimate?.usage || 0;
1691
+ } catch {
1692
+ return 0;
1693
+ }
1694
+ }
1695
+ async function hybridSearch(options) {
1696
+ return hybridSearchDocuments(options);
1697
+ }
1698
+ async function getKBStats() {
1699
+ const db = await getDB();
1700
+ const docs = await db.query(`SELECT COUNT(*) as count FROM resource_documents;`);
1701
+ const cats = await db.query(`SELECT COUNT(*) as count FROM resource_categories;`);
1702
+ const withEmb = await db.query(
1703
+ `SELECT COUNT(*) as count FROM resource_documents WHERE embedding IS NOT NULL;`
1704
+ );
1705
+ const docsRow = docs.rows[0];
1706
+ const catsRow = cats.rows[0];
1707
+ const withEmbRow = withEmb.rows[0];
1708
+ return {
1709
+ totalDocuments: parseInt(docsRow?.count || "0", 10),
1710
+ totalCategories: parseInt(catsRow?.count || "0", 10),
1711
+ documentsWithEmbeddings: parseInt(withEmbRow?.count || "0", 10)
1712
+ };
1713
+ }
1714
+ if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_DEBUG_DB === "true")
1715
+ console.log("PGLite service module loaded (backward compatibility layer).");
1716
+
1717
+ export {
1718
+ safeDbOperation,
1719
+ robustJsonParse,
1720
+ checkpointDatabase,
1721
+ resetDatabase,
1722
+ getUserProgress,
1723
+ getAllUserProgress,
1724
+ upsertUserProgress,
1725
+ getUserProfileByXverseAddress,
1726
+ getUserProfile,
1727
+ getEffectiveOrCreateUserProfile,
1728
+ createUserProfile,
1729
+ updateUserProfile,
1730
+ saveUserArtifact,
1731
+ getUserArtifacts,
1732
+ getUserArtifactById,
1733
+ deleteUserArtifact,
1734
+ getAllQuizzes,
1735
+ getQuizById,
1736
+ GET_ALL_QUIZZES_WITH_USER_PROGRESS_SQL,
1737
+ getAllQuizzesWithUserProgress,
1738
+ SEARCH_QUIZZES_SQL,
1739
+ searchQuizzes,
1740
+ checkQuizCompletion,
1741
+ getAllRewards,
1742
+ getRewardById,
1743
+ completeQuizAndProcessRewards,
1744
+ unlockReward,
1745
+ getAllResourceCategories,
1746
+ getResourceCategoryById,
1747
+ searchResourceCategories,
1748
+ getAllResourceDocuments,
1749
+ getResourceDocumentById,
1750
+ searchResourceDocuments,
1751
+ getAllFriends,
1752
+ getAllInspirations,
1753
+ createChatSession,
1754
+ getAllChatSessions,
1755
+ getChatSession,
1756
+ updateChatSessionTitle,
1757
+ deleteChatSession,
1758
+ clearChatMessages,
1759
+ addChatMessage,
1760
+ getChatMessages,
1761
+ getChatMessageCount,
1762
+ createDeployment,
1763
+ getDeployment,
1764
+ getDeploymentsByStack,
1765
+ getDeploymentsByUser,
1766
+ updateDeploymentStatus,
1767
+ deleteDeployment,
1768
+ getWorkspaceState,
1769
+ saveWorkspaceState,
1770
+ deleteWorkspaceState,
1771
+ getAllWorkspaceStates,
1772
+ createSkillRun,
1773
+ getSkillRun,
1774
+ getSkillRunsBySlug,
1775
+ getSkillRunsByUser,
1776
+ updateSkillRunStatus,
1777
+ cleanupOrphanedRuns,
1778
+ deleteSkillRun,
1779
+ saveForgeScene,
1780
+ getForgeScene,
1781
+ getForgeScenesByUser,
1782
+ deleteForgeScene,
1783
+ saveForgeIteration,
1784
+ getForgeIterationsByScene,
1785
+ saveForgeMaterialProfile,
1786
+ getForgeMaterialProfiles,
1787
+ deleteForgeMaterialProfile,
1788
+ listBiosignalBridgeConfigs,
1789
+ getBiosignalBridgeConfig,
1790
+ listEnabledBiosignalBridges,
1791
+ setBiosignalBridgeEnabled,
1792
+ reseedDormantBiosignalBridges,
1793
+ listBiosignalProviderConfigs,
1794
+ getBiosignalProviderConfig,
1795
+ listEnabledBiosignalProviders,
1796
+ setBiosignalProviderEnabled,
1797
+ reseedDormantBiosignalProviders,
1798
+ initializeVectorSearch,
1799
+ storeDocumentEmbedding,
1800
+ batchStoreEmbeddings,
1801
+ searchDocumentsByVector,
1802
+ hybridSearchDocuments,
1803
+ findSimilarDocuments,
1804
+ getDocumentsWithoutEmbeddings,
1805
+ getVectorSearchMetrics,
1806
+ upsertConversationEmbedding,
1807
+ retrieveTopKForSession,
1808
+ createSavepoint,
1809
+ rollbackToSavepoint,
1810
+ releaseSavepoint,
1811
+ withSavepoint,
1812
+ saveGenericState,
1813
+ getPgStatIoStats,
1814
+ getPGLiteIndexedDbSize,
1815
+ hybridSearch,
1816
+ getKBStats
1817
+ };