@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,103 @@
1
+ import {
2
+ OrchestratorV0,
3
+ loadStack
4
+ } from "./chunk-STMI72WH.js";
5
+ import {
6
+ runners
7
+ } from "./chunk-PPFTKJDB.js";
8
+ import "./chunk-KASHT6C5.js";
9
+ import "./chunk-GJWAJAX3.js";
10
+ import "./chunk-VZA2NUH3.js";
11
+ import "./chunk-H3POJCFA.js";
12
+ import "./chunk-KHR7ZYCX.js";
13
+ import "./chunk-QWXPVB2L.js";
14
+ import "./chunk-TSQ3BGLA.js";
15
+ import "./chunk-Q2VI6ICE.js";
16
+ import "./chunk-PWAHFID5.js";
17
+ import "./chunk-Y2EULKA2.js";
18
+ import "./chunk-WU4OTGJE.js";
19
+ import {
20
+ resolveNode
21
+ } from "./chunk-OCLSAUCD.js";
22
+ import "./chunk-BYXBJQAS.js";
23
+ import "./chunk-BCOQMFKT.js";
24
+ import "./chunk-2WTYE4SW.js";
25
+ import "./chunk-FOXUEYWK.js";
26
+ import "./chunk-EXI3LJVJ.js";
27
+ import "./chunk-QJFKEQHF.js";
28
+ import "./chunk-ZVO47SQV.js";
29
+ import "./chunk-XPJ63Y6T.js";
30
+ import "./chunk-WGBCRNMB.js";
31
+ import "./chunk-3LFMIVJM.js";
32
+ import "./chunk-AFDUOYHD.js";
33
+ import "./chunk-NTMBOESX.js";
34
+ import "./chunk-2ESYSVXG.js";
35
+
36
+ // ../../lib/terminals-tech/machines/core/terminal-mapper.ts
37
+ function mapTerminalInputs(terminal, externalInput) {
38
+ const overrides = {};
39
+ if (!terminal.terminalInterface?.inputMap) {
40
+ return overrides;
41
+ }
42
+ for (const [externalKey, internalTarget] of Object.entries(terminal.terminalInterface.inputMap)) {
43
+ if (externalInput[externalKey] !== void 0) {
44
+ const [nodeId, ...fieldParts] = internalTarget.split(".");
45
+ const field = fieldParts.join(".");
46
+ if (!overrides[nodeId]) {
47
+ overrides[nodeId] = {};
48
+ }
49
+ if (field) {
50
+ overrides[nodeId][field] = externalInput[externalKey];
51
+ } else {
52
+ }
53
+ }
54
+ }
55
+ return overrides;
56
+ }
57
+ function mapTerminalOutputs(terminal, nodeOutputs) {
58
+ const result = {};
59
+ if (!terminal.terminalInterface?.outputMap) {
60
+ return result;
61
+ }
62
+ for (const [internalSource, externalKey] of Object.entries(
63
+ terminal.terminalInterface.outputMap
64
+ )) {
65
+ const [nodeId, ...fieldParts] = internalSource.split(".");
66
+ const field = fieldParts.join(".");
67
+ const output = nodeOutputs.get(nodeId);
68
+ if (output && field && output[field] !== void 0) {
69
+ result[externalKey] = output[field];
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+
75
+ // ../../lib/terminals-tech/machines/core/runners/stack.ts
76
+ async function runInStack(manifest, input, ctx) {
77
+ const stackRef = manifest.id;
78
+ const targetStack = await loadStack(stackRef);
79
+ if (!targetStack) {
80
+ throw new Error(`StackRunner: target stack not found: ${stackRef}`);
81
+ }
82
+ const orchestrator = new OrchestratorV0(resolveNode);
83
+ try {
84
+ const configOverrides = mapTerminalInputs(targetStack, input);
85
+ const subRun = await orchestrator.run(targetStack, {
86
+ configOverrides,
87
+ userId: ctx.userId,
88
+ appletId: ctx.appletId,
89
+ worldId: ctx.worldId,
90
+ terminalId: ctx.terminalId
91
+ });
92
+ if (!subRun.succeeded || !subRun.outputs) {
93
+ throw subRun.error || new Error(`Recursive stack execution failed: ${subRun.runId}`);
94
+ }
95
+ return mapTerminalOutputs(targetStack, subRun.outputs);
96
+ } finally {
97
+ orchestrator.dispose();
98
+ }
99
+ }
100
+ runners.registerRunner("stack", runInStack);
101
+ export {
102
+ runInStack
103
+ };
@@ -0,0 +1,13 @@
1
+ import {
2
+ deleteDatabaseByDataDir,
3
+ getStorageEstimate,
4
+ isStoragePersisted,
5
+ requestPersistentStorage
6
+ } from "./chunk-3LFMIVJM.js";
7
+ import "./chunk-2ESYSVXG.js";
8
+ export {
9
+ deleteDatabaseByDataDir,
10
+ getStorageEstimate,
11
+ isStoragePersisted,
12
+ requestPersistentStorage
13
+ };
@@ -0,0 +1,476 @@
1
+ import {
2
+ createSupabaseClientComponentClient
3
+ } from "./chunk-QWZRZKLZ.js";
4
+ import {
5
+ ErrorCodes,
6
+ SyncError
7
+ } from "./chunk-OSSRZOGC.js";
8
+ import "./chunk-TSQ3BGLA.js";
9
+ import {
10
+ secureValidate
11
+ } from "./chunk-FOXUEYWK.js";
12
+ import "./chunk-EXI3LJVJ.js";
13
+ import "./chunk-QJFKEQHF.js";
14
+ import "./chunk-2ESYSVXG.js";
15
+
16
+ // ../../lib/utils/network.ts
17
+ function isNavigatorOnline() {
18
+ try {
19
+ if (typeof navigator === "undefined") return true;
20
+ return navigator.onLine;
21
+ } catch {
22
+ return true;
23
+ }
24
+ }
25
+ function withTimeout(p, ms) {
26
+ return new Promise((resolve, reject) => {
27
+ const t = setTimeout(() => reject(new Error("timeout")), ms);
28
+ p.then((v) => {
29
+ clearTimeout(t);
30
+ resolve(v);
31
+ }).catch((e) => {
32
+ clearTimeout(t);
33
+ reject(e);
34
+ });
35
+ });
36
+ }
37
+ var lastSupabaseCheckAt = 0;
38
+ var lastSupabaseReachable = false;
39
+ var MIN_CHECK_INTERVAL_MS = 1e4;
40
+ async function supabaseReachable(ttlMs = 3e4, timeoutMs = 2500) {
41
+ const now = Date.now();
42
+ const effectiveTtl = Math.max(ttlMs, MIN_CHECK_INTERVAL_MS);
43
+ if (now - lastSupabaseCheckAt < effectiveTtl) return lastSupabaseReachable;
44
+ const base = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
45
+ if (!base) {
46
+ lastSupabaseCheckAt = now;
47
+ lastSupabaseReachable = false;
48
+ return false;
49
+ }
50
+ if (!isNavigatorOnline()) {
51
+ lastSupabaseCheckAt = now;
52
+ lastSupabaseReachable = false;
53
+ return false;
54
+ }
55
+ try {
56
+ const url = base.replace(/\/$/, "") + "/auth/v1/health";
57
+ const headers = {};
58
+ const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
59
+ if (anonKey) {
60
+ headers["apikey"] = anonKey;
61
+ headers["Authorization"] = `Bearer ${anonKey}`;
62
+ }
63
+ const resp = await withTimeout(
64
+ fetch(url, {
65
+ method: "GET",
66
+ cache: "no-store",
67
+ mode: "cors",
68
+ headers
69
+ }),
70
+ timeoutMs
71
+ );
72
+ lastSupabaseCheckAt = now;
73
+ lastSupabaseReachable = resp.ok;
74
+ return lastSupabaseReachable;
75
+ } catch {
76
+ lastSupabaseCheckAt = now;
77
+ lastSupabaseReachable = false;
78
+ return false;
79
+ }
80
+ }
81
+
82
+ // ../../lib/supabaseService.ts
83
+ var USER_PROFILES_TABLE = "user_profiles";
84
+ var USER_PROGRESS_TABLE = "user_progress";
85
+ var MESH_EVENTS_TABLE = "mesh_events";
86
+ var SUPABASE_MESH_EVENT_SCHEMA = {
87
+ type: "object",
88
+ required: ["id", "user_id", "trace_path", "type", "payload", "created_at"],
89
+ properties: {
90
+ id: { type: "string" },
91
+ user_id: { type: "string" },
92
+ interaction_id: { type: ["string", "null"] },
93
+ run_id: { type: ["string", "null"] },
94
+ stack_id: { type: ["string", "null"] },
95
+ prev_hash: { type: ["string", "null"] },
96
+ trace_path: { type: "string" },
97
+ type: { type: "string" },
98
+ payload: { type: ["object", "array", "string", "number", "boolean", "null"] },
99
+ metadata: { type: ["object", "null"] },
100
+ embedding: { type: ["array", "null"], items: { type: "number" } },
101
+ span: { type: ["array", "null"], items: { type: "number" }, minItems: 2, maxItems: 2 },
102
+ created_at: { type: "string" }
103
+ },
104
+ additionalProperties: true
105
+ };
106
+ function getSupabaseClient() {
107
+ return createSupabaseClientComponentClient();
108
+ }
109
+ async function ensureReachableOrThrow() {
110
+ const ok = await supabaseReachable(4e3, 1500);
111
+ if (!ok) {
112
+ throw new SyncError("Supabase is unreachable", ErrorCodes.SYNC_OFFLINE, {
113
+ operation: "ensureReachable"
114
+ });
115
+ }
116
+ }
117
+ var singleflight = /* @__PURE__ */ new Map();
118
+ function sf(key, fn) {
119
+ const existing = singleflight.get(key);
120
+ if (existing) return existing;
121
+ const p = fn().finally(() => singleflight.delete(key));
122
+ singleflight.set(key, p);
123
+ return p;
124
+ }
125
+ async function getSupabaseUserProgress(userId) {
126
+ if (!userId) {
127
+ throw new SyncError(
128
+ "User ID is required to fetch Supabase user progress",
129
+ ErrorCodes.SYNC_FAILED,
130
+ { operation: "getSupabaseUserProgress" }
131
+ );
132
+ }
133
+ return sf(`progress:${userId}`, async () => {
134
+ try {
135
+ const supabase = getSupabaseClient();
136
+ let quizResults = [];
137
+ let unlockedRewards = [];
138
+ let claimedRewards = [];
139
+ await ensureReachableOrThrow();
140
+ const { data: quizData, error: quizError } = await supabase.from(USER_PROGRESS_TABLE).select("quiz_id, highest_score, completed_at").eq("user_id", userId);
141
+ if (quizError) {
142
+ throw new SyncError(
143
+ `Failed to fetch quiz progress: ${quizError.message}`,
144
+ ErrorCodes.SYNC_FAILED,
145
+ { operation: "getQuizProgress", userId, postgrestCode: quizError.code }
146
+ );
147
+ } else if (quizData) {
148
+ quizResults = quizData.map((item) => ({
149
+ quizId: item.quiz_id,
150
+ score: item.highest_score || 0,
151
+ completed: !!item.completed_at
152
+ }));
153
+ }
154
+ const { data: profileData, error: profileError } = await supabase.from(USER_PROFILES_TABLE).select("*").eq("user_id", userId).single();
155
+ if (profileError && profileError.code !== "PGRST116") {
156
+ throw new SyncError(
157
+ `Failed to fetch user profile: ${profileError.message}`,
158
+ ErrorCodes.SYNC_FAILED,
159
+ { operation: "getUserProfile", userId, postgrestCode: profileError.code }
160
+ );
161
+ } else if (profileError && profileError.code === "PGRST116") {
162
+ try {
163
+ const { error: insertErr } = await supabase.from(USER_PROFILES_TABLE).insert({ user_id: userId, preferences: {} }).single();
164
+ if (insertErr && insertErr.code !== "23505") {
165
+ throw new SyncError(
166
+ `Failed to create user profile: ${insertErr.message}`,
167
+ ErrorCodes.SYNC_FAILED,
168
+ { operation: "createUserProfile", userId, postgrestCode: insertErr.code }
169
+ );
170
+ }
171
+ } catch (error) {
172
+ if (error instanceof SyncError) throw error;
173
+ throw SyncError.fromUnknown(error, "sync");
174
+ }
175
+ } else if (profileData && profileData.preferences) {
176
+ const prefs = profileData.preferences;
177
+ unlockedRewards = prefs.unlocked_reward_ids || [];
178
+ claimedRewards = prefs.claimed_reward_ids || [];
179
+ }
180
+ return {
181
+ quizResults,
182
+ unlockedRewards,
183
+ claimedRewards,
184
+ persistenceEnabled: true,
185
+ preferences: profileData?.preferences || {}
186
+ };
187
+ } catch (error) {
188
+ if (error instanceof SyncError) throw error;
189
+ throw SyncError.fromUnknown(error, "sync");
190
+ }
191
+ });
192
+ }
193
+ async function upsertUserProgressSimple(userId, quizId, score, completedAt) {
194
+ if (!userId || !quizId) return;
195
+ try {
196
+ const supabase = getSupabaseClient();
197
+ await ensureReachableOrThrow();
198
+ const ts = completedAt || (/* @__PURE__ */ new Date()).toISOString();
199
+ const { error } = await supabase.from(USER_PROGRESS_TABLE).upsert({
200
+ user_id: userId,
201
+ quiz_id: quizId,
202
+ highest_score: score,
203
+ completed_at: ts,
204
+ last_attempted_at: ts
205
+ });
206
+ if (error) {
207
+ throw new SyncError(
208
+ `Failed to upsert user progress: ${error.message}`,
209
+ ErrorCodes.SYNC_FAILED,
210
+ { operation: "upsertUserProgressSimple", userId, quizId, postgrestCode: error.code }
211
+ );
212
+ }
213
+ } catch (error) {
214
+ if (error instanceof SyncError) throw error;
215
+ throw SyncError.fromUnknown(error, "sync");
216
+ }
217
+ }
218
+ async function saveSupabaseQuizAttempt(userId, quizId, score, allRewardsData) {
219
+ if (!userId || !quizId) {
220
+ throw new SyncError("User ID and Quiz ID are required", ErrorCodes.SYNC_FAILED, {
221
+ operation: "saveSupabaseQuizAttempt"
222
+ });
223
+ }
224
+ try {
225
+ const supabase = getSupabaseClient();
226
+ const { data: existingAttempt, error: fetchError } = await supabase.from(USER_PROGRESS_TABLE).select("highest_score").eq("user_id", userId).eq("quiz_id", quizId).single();
227
+ if (fetchError && fetchError.code !== "PGRST116") {
228
+ throw new SyncError(
229
+ `Failed to fetch existing quiz attempt: ${fetchError.message}`,
230
+ ErrorCodes.SYNC_FAILED,
231
+ { operation: "fetchExistingAttempt", userId, quizId, postgrestCode: fetchError.code }
232
+ );
233
+ }
234
+ const existingScore = existingAttempt?.highest_score || 0;
235
+ if (score > existingScore) {
236
+ const { error: upsertError } = await supabase.from(USER_PROGRESS_TABLE).upsert({
237
+ user_id: userId,
238
+ quiz_id: quizId,
239
+ highest_score: score,
240
+ completed_at: (/* @__PURE__ */ new Date()).toISOString(),
241
+ last_attempted_at: (/* @__PURE__ */ new Date()).toISOString()
242
+ // Add last_attempted_at
243
+ });
244
+ if (upsertError) {
245
+ throw new SyncError(
246
+ `Failed to upsert quiz attempt: ${upsertError.message}`,
247
+ ErrorCodes.SYNC_FAILED,
248
+ { operation: "upsertQuizAttempt", userId, quizId, postgrestCode: upsertError.code }
249
+ );
250
+ }
251
+ } else {
252
+ const { error: updateError } = await supabase.from(USER_PROGRESS_TABLE).update({ last_attempted_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("user_id", userId).eq("quiz_id", quizId);
253
+ if (updateError) {
254
+ throw new SyncError(
255
+ `Failed to update last_attempted_at: ${updateError.message}`,
256
+ ErrorCodes.SYNC_FAILED,
257
+ { operation: "updateLastAttempted", userId, quizId, postgrestCode: updateError.code }
258
+ );
259
+ }
260
+ }
261
+ const newlyUnlockedRewardIds = [];
262
+ const { data: currentProfile, error: profileError } = await supabase.from(USER_PROFILES_TABLE).select("preferences, user_id").eq("user_id", userId).single();
263
+ if (profileError && profileError.code !== "PGRST116") {
264
+ throw new SyncError(
265
+ `Failed to fetch user profile for reward processing: ${profileError.message}`,
266
+ ErrorCodes.SYNC_FAILED,
267
+ { operation: "fetchProfileForRewards", userId, postgrestCode: profileError.code }
268
+ );
269
+ }
270
+ const currentPreferences = currentProfile?.preferences || {};
271
+ const currentUnlockedIds = currentPreferences.unlocked_reward_ids || [];
272
+ const { data: allCompletedQuizData, error: allQuizError } = await supabase.from(USER_PROGRESS_TABLE).select("quiz_id").eq("user_id", userId).not("completed_at", "is", null);
273
+ if (allQuizError) {
274
+ throw new SyncError(
275
+ `Failed to fetch completed quizzes for reward processing: ${allQuizError.message}`,
276
+ ErrorCodes.SYNC_FAILED,
277
+ { operation: "fetchCompletedQuizzes", userId, postgrestCode: allQuizError.code }
278
+ );
279
+ }
280
+ const completedQuizIds = new Set((allCompletedQuizData || []).map((q) => q.quiz_id));
281
+ if (!completedQuizIds.has(quizId) && score > 0) {
282
+ completedQuizIds.add(quizId);
283
+ }
284
+ for (const reward of allRewardsData) {
285
+ if (reward.unlockRequirement?.type === "quiz" && reward.unlockRequirement.quizId) {
286
+ if (reward.unlockRequirement.quizId === quizId && !currentUnlockedIds.includes(reward.id)) {
287
+ newlyUnlockedRewardIds.push(reward.id);
288
+ }
289
+ } else if (reward.unlockRequirement?.type === "completion" && reward.unlockRequirement.required_quizzes) {
290
+ const required = reward.unlockRequirement.required_quizzes;
291
+ if (required.every((reqQuizId) => completedQuizIds.has(reqQuizId)) && !currentUnlockedIds.includes(reward.id)) {
292
+ newlyUnlockedRewardIds.push(reward.id);
293
+ }
294
+ }
295
+ }
296
+ if (newlyUnlockedRewardIds.length > 0) {
297
+ const updatedUnlockedIds = Array.from(
298
+ /* @__PURE__ */ new Set([...currentUnlockedIds, ...newlyUnlockedRewardIds])
299
+ );
300
+ const newPreferences = {
301
+ ...currentPreferences,
302
+ unlocked_reward_ids: updatedUnlockedIds
303
+ };
304
+ const { error: updatePrefsError } = await supabase.from(USER_PROFILES_TABLE).update({ preferences: newPreferences }).eq("user_id", userId);
305
+ if (updatePrefsError) {
306
+ throw new SyncError(
307
+ `Failed to update user preferences with new rewards: ${updatePrefsError.message}`,
308
+ ErrorCodes.SYNC_FAILED,
309
+ {
310
+ operation: "updateRewards",
311
+ userId,
312
+ rewardIds: newlyUnlockedRewardIds,
313
+ postgrestCode: updatePrefsError.code
314
+ }
315
+ );
316
+ }
317
+ }
318
+ return { newlyUnlockedRewardIds };
319
+ } catch (error) {
320
+ if (error instanceof SyncError) throw error;
321
+ throw SyncError.fromUnknown(error, "sync");
322
+ }
323
+ }
324
+ async function claimSupabaseReward(userId, rewardId) {
325
+ if (!userId || !rewardId) {
326
+ throw new SyncError("User ID and Reward ID are required", ErrorCodes.SYNC_FAILED, {
327
+ operation: "claimSupabaseReward"
328
+ });
329
+ }
330
+ try {
331
+ const supabase = getSupabaseClient();
332
+ const { data: profileData, error: profileError } = await supabase.from(USER_PROFILES_TABLE).select("preferences").eq("user_id", userId).single();
333
+ if (profileError) {
334
+ if (profileError.code !== "PGRST116") {
335
+ throw new SyncError(
336
+ `Failed to fetch user profile for claiming reward: ${profileError.message}`,
337
+ ErrorCodes.SYNC_FAILED,
338
+ { operation: "fetchProfileForClaim", userId, rewardId, postgrestCode: profileError.code }
339
+ );
340
+ }
341
+ try {
342
+ await supabase.from(USER_PROFILES_TABLE).insert({ user_id: userId, preferences: {} }).single();
343
+ } catch (error) {
344
+ throw SyncError.fromUnknown(error, "sync");
345
+ }
346
+ return;
347
+ }
348
+ const currentPreferences = profileData?.preferences || {};
349
+ const currentClaimedIds = currentPreferences.claimed_reward_ids || [];
350
+ const currentUnlockedIds = currentPreferences.unlocked_reward_ids || [];
351
+ if (currentUnlockedIds.includes(rewardId) && !currentClaimedIds.includes(rewardId)) {
352
+ const updatedClaimedIds = Array.from(/* @__PURE__ */ new Set([...currentClaimedIds, rewardId]));
353
+ const newPreferences = {
354
+ ...currentPreferences,
355
+ claimed_reward_ids: updatedClaimedIds
356
+ };
357
+ const { error: updatePrefsError } = await supabase.from(USER_PROFILES_TABLE).update({ preferences: newPreferences }).eq("user_id", userId);
358
+ if (updatePrefsError) {
359
+ throw new SyncError(
360
+ `Failed to update user preferences with claimed reward: ${updatePrefsError.message}`,
361
+ ErrorCodes.SYNC_FAILED,
362
+ {
363
+ operation: "updateClaimedReward",
364
+ userId,
365
+ rewardId,
366
+ postgrestCode: updatePrefsError.code
367
+ }
368
+ );
369
+ }
370
+ } else if (!currentUnlockedIds.includes(rewardId)) {
371
+ throw new SyncError(
372
+ `Attempted to claim reward that is not unlocked`,
373
+ ErrorCodes.SYNC_FAILED,
374
+ { operation: "claimLockedReward", userId, rewardId }
375
+ );
376
+ }
377
+ } catch (error) {
378
+ if (error instanceof SyncError) throw error;
379
+ throw SyncError.fromUnknown(error, "sync");
380
+ }
381
+ }
382
+ async function updateUserPreferences(userId, preferences) {
383
+ if (!userId) {
384
+ throw new SyncError("User ID is required to update preferences", ErrorCodes.SYNC_FAILED, {
385
+ operation: "updateUserPreferences"
386
+ });
387
+ }
388
+ try {
389
+ const supabase = getSupabaseClient();
390
+ const { error } = await supabase.from(USER_PROFILES_TABLE).update({ preferences }).eq("user_id", userId);
391
+ if (error) {
392
+ throw new SyncError(
393
+ `Failed to update user preferences: ${error.message}`,
394
+ ErrorCodes.SYNC_FAILED,
395
+ { operation: "updateUserPreferences", userId, postgrestCode: error.code }
396
+ );
397
+ }
398
+ } catch (error) {
399
+ if (error instanceof SyncError) throw error;
400
+ throw SyncError.fromUnknown(error, "sync");
401
+ }
402
+ }
403
+ async function appendSupabaseMeshEvent(event) {
404
+ await ensureReachableOrThrow();
405
+ const supabase = getSupabaseClient();
406
+ const { data, error } = await supabase.from(MESH_EVENTS_TABLE).insert(event).select("*").single();
407
+ if (error || !data) {
408
+ throw new SyncError(
409
+ `Failed to insert mesh event: ${error?.message || "Unknown error"}`,
410
+ ErrorCodes.SYNC_FAILED,
411
+ { operation: "appendSupabaseMeshEvent", postgrestCode: error?.code }
412
+ );
413
+ }
414
+ const validated = secureValidate(data, SUPABASE_MESH_EVENT_SCHEMA);
415
+ if (!validated) {
416
+ throw new SyncError("Supabase mesh event validation failed", ErrorCodes.SYNC_FAILED, {
417
+ operation: "appendSupabaseMeshEvent"
418
+ });
419
+ }
420
+ return validated;
421
+ }
422
+ async function querySupabaseMeshEvents(userId, interactionId, limit = 200) {
423
+ await ensureReachableOrThrow();
424
+ const supabase = getSupabaseClient();
425
+ let query = supabase.from(MESH_EVENTS_TABLE).select("*").eq("user_id", userId);
426
+ if (interactionId) {
427
+ query = query.eq("interaction_id", interactionId);
428
+ }
429
+ const { data, error } = await query.order("created_at", { ascending: false }).limit(limit);
430
+ if (error) {
431
+ throw new SyncError(`Failed to query mesh events: ${error.message}`, ErrorCodes.SYNC_FAILED, {
432
+ operation: "querySupabaseMeshEvents",
433
+ postgrestCode: error.code
434
+ });
435
+ }
436
+ const output = [];
437
+ for (const row of data || []) {
438
+ const validated = secureValidate(row, SUPABASE_MESH_EVENT_SCHEMA);
439
+ if (validated) {
440
+ output.push(validated);
441
+ }
442
+ }
443
+ return output;
444
+ }
445
+ async function updateSupabaseUserProfileAlias(userId, alias) {
446
+ if (!userId) {
447
+ throw new SyncError("User ID is required to update alias", ErrorCodes.SYNC_FAILED, {
448
+ operation: "updateSupabaseUserProfileAlias"
449
+ });
450
+ }
451
+ try {
452
+ const supabase = getSupabaseClient();
453
+ const { error } = await supabase.from(USER_PROFILES_TABLE).update({ username: alias }).eq("user_id", userId);
454
+ if (error) {
455
+ throw new SyncError(`Failed to update user alias: ${error.message}`, ErrorCodes.SYNC_FAILED, {
456
+ operation: "updateUserAlias",
457
+ userId,
458
+ alias,
459
+ postgrestCode: error.code
460
+ });
461
+ }
462
+ } catch (error) {
463
+ if (error instanceof SyncError) throw error;
464
+ throw SyncError.fromUnknown(error, "sync");
465
+ }
466
+ }
467
+ export {
468
+ appendSupabaseMeshEvent,
469
+ claimSupabaseReward,
470
+ getSupabaseUserProgress,
471
+ querySupabaseMeshEvents,
472
+ saveSupabaseQuizAttempt,
473
+ updateSupabaseUserProfileAlias,
474
+ updateUserPreferences,
475
+ upsertUserProgressSimple
476
+ };
@@ -0,0 +1,13 @@
1
+ import {
2
+ adjacencyMatrix,
3
+ canonicalizeStack,
4
+ computeShapeHash,
5
+ incidenceMatrix
6
+ } from "./chunk-GJWAJAX3.js";
7
+ import "./chunk-2ESYSVXG.js";
8
+ export {
9
+ adjacencyMatrix,
10
+ canonicalizeStack,
11
+ computeShapeHash,
12
+ incidenceMatrix
13
+ };