@haex-space/vault-sdk 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 (59) hide show
  1. package/README.md +1551 -0
  2. package/dist/cli/index.d.mts +1 -0
  3. package/dist/cli/index.d.ts +1 -0
  4. package/dist/cli/index.js +455 -0
  5. package/dist/cli/index.js.map +1 -0
  6. package/dist/cli/index.mjs +430 -0
  7. package/dist/cli/index.mjs.map +1 -0
  8. package/dist/client-O_JEOzfx.d.mts +491 -0
  9. package/dist/client-O_JEOzfx.d.ts +491 -0
  10. package/dist/index.d.mts +124 -0
  11. package/dist/index.d.ts +124 -0
  12. package/dist/index.js +1429 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/index.mjs +1409 -0
  15. package/dist/index.mjs.map +1 -0
  16. package/dist/node.d.mts +25 -0
  17. package/dist/node.d.ts +25 -0
  18. package/dist/node.js +28 -0
  19. package/dist/node.js.map +1 -0
  20. package/dist/node.mjs +25 -0
  21. package/dist/node.mjs.map +1 -0
  22. package/dist/nuxt.d.mts +19 -0
  23. package/dist/nuxt.d.ts +19 -0
  24. package/dist/nuxt.js +473 -0
  25. package/dist/nuxt.js.map +1 -0
  26. package/dist/nuxt.mjs +470 -0
  27. package/dist/nuxt.mjs.map +1 -0
  28. package/dist/react.d.mts +28 -0
  29. package/dist/react.d.ts +28 -0
  30. package/dist/react.js +1389 -0
  31. package/dist/react.js.map +1 -0
  32. package/dist/react.mjs +1386 -0
  33. package/dist/react.mjs.map +1 -0
  34. package/dist/runtime/nuxt.plugin.client.d.mts +43 -0
  35. package/dist/runtime/nuxt.plugin.client.d.ts +43 -0
  36. package/dist/runtime/nuxt.plugin.client.js +1137 -0
  37. package/dist/runtime/nuxt.plugin.client.js.map +1 -0
  38. package/dist/runtime/nuxt.plugin.client.mjs +1135 -0
  39. package/dist/runtime/nuxt.plugin.client.mjs.map +1 -0
  40. package/dist/runtime/nuxt.types.d.ts +15 -0
  41. package/dist/svelte.d.mts +48 -0
  42. package/dist/svelte.d.ts +48 -0
  43. package/dist/svelte.js +1398 -0
  44. package/dist/svelte.js.map +1 -0
  45. package/dist/svelte.mjs +1391 -0
  46. package/dist/svelte.mjs.map +1 -0
  47. package/dist/vite.d.mts +37 -0
  48. package/dist/vite.d.ts +37 -0
  49. package/dist/vite.js +346 -0
  50. package/dist/vite.js.map +1 -0
  51. package/dist/vite.mjs +341 -0
  52. package/dist/vite.mjs.map +1 -0
  53. package/dist/vue.d.mts +49 -0
  54. package/dist/vue.d.ts +49 -0
  55. package/dist/vue.js +1388 -0
  56. package/dist/vue.js.map +1 -0
  57. package/dist/vue.mjs +1385 -0
  58. package/dist/vue.mjs.map +1 -0
  59. package/package.json +121 -0
@@ -0,0 +1,1135 @@
1
+ import { defineNuxtPlugin } from 'nuxt/app';
2
+ import { shallowRef } from 'vue';
3
+ import { drizzle } from 'drizzle-orm/sqlite-proxy';
4
+
5
+ // src/runtime/nuxt.plugin.client.ts
6
+
7
+ // src/events.ts
8
+ var HAEXTENSION_EVENTS = {
9
+ /** Context (theme, locale, platform) has changed */
10
+ CONTEXT_CHANGED: "haextension:context:changed",
11
+ /** Search request from HaexHub */
12
+ SEARCH_REQUEST: "haextension:search:request"
13
+ };
14
+
15
+ // src/methods.ts
16
+ var HAEXTENSION_METHODS = {
17
+ context: {
18
+ get: "haextension:context:get"
19
+ },
20
+ database: {
21
+ query: "haextension:database:query",
22
+ execute: "haextension:database:execute",
23
+ transaction: "haextension:database:transaction",
24
+ registerMigrations: "haextension:database:register-migrations"
25
+ },
26
+ filesystem: {
27
+ saveFile: "haextension:filesystem:save-file",
28
+ openFile: "haextension:filesystem:open-file",
29
+ showImage: "haextension:filesystem:show-image"
30
+ },
31
+ storage: {
32
+ getItem: "haextension:storage:get-item",
33
+ setItem: "haextension:storage:set-item",
34
+ removeItem: "haextension:storage:remove-item",
35
+ clear: "haextension:storage:clear",
36
+ keys: "haextension:storage:keys"
37
+ },
38
+ web: {
39
+ fetch: "haextension:web:fetch"
40
+ },
41
+ application: {
42
+ open: "haextension:application:open"
43
+ }
44
+ };
45
+
46
+ // src/types.ts
47
+ var DEFAULT_TIMEOUT = 3e4;
48
+ var TABLE_SEPARATOR = "__";
49
+ var HaexHubError = class extends Error {
50
+ constructor(code, messageKey, details) {
51
+ super(messageKey);
52
+ this.code = code;
53
+ this.messageKey = messageKey;
54
+ this.details = details;
55
+ this.name = "HaexHubError";
56
+ }
57
+ /**
58
+ * Get localized error message
59
+ * @param locale - Locale code (e.g., 'en', 'de')
60
+ * @param translations - Translation object
61
+ */
62
+ getLocalizedMessage(locale = "en", translations) {
63
+ if (!translations || !translations[locale]) {
64
+ return this.messageKey;
65
+ }
66
+ let message = translations[locale][this.messageKey] || this.messageKey;
67
+ if (this.details) {
68
+ Object.entries(this.details).forEach(([key, value]) => {
69
+ message = message.replace(`{${key}}`, String(value));
70
+ });
71
+ }
72
+ return message;
73
+ }
74
+ toJSON() {
75
+ return {
76
+ code: this.code,
77
+ message: this.messageKey,
78
+ details: this.details
79
+ };
80
+ }
81
+ };
82
+
83
+ // src/api/storage.ts
84
+ var StorageAPI = class {
85
+ constructor(client) {
86
+ this.client = client;
87
+ }
88
+ async getItem(key) {
89
+ return this.client.request(HAEXTENSION_METHODS.storage.getItem, { key });
90
+ }
91
+ async setItem(key, value) {
92
+ await this.client.request(HAEXTENSION_METHODS.storage.setItem, { key, value });
93
+ }
94
+ async removeItem(key) {
95
+ await this.client.request(HAEXTENSION_METHODS.storage.removeItem, { key });
96
+ }
97
+ async clear() {
98
+ await this.client.request(HAEXTENSION_METHODS.storage.clear);
99
+ }
100
+ async keys() {
101
+ return this.client.request(HAEXTENSION_METHODS.storage.keys);
102
+ }
103
+ };
104
+
105
+ // src/api/database.ts
106
+ var DatabaseAPI = class {
107
+ constructor(client) {
108
+ this.client = client;
109
+ }
110
+ async query(query, params) {
111
+ const result = await this.client.request(
112
+ HAEXTENSION_METHODS.database.query,
113
+ {
114
+ query,
115
+ params: params || []
116
+ }
117
+ );
118
+ return result.rows;
119
+ }
120
+ async queryOne(query, params) {
121
+ const rows = await this.query(query, params);
122
+ return rows.length > 0 ? rows[0] ?? null : null;
123
+ }
124
+ async execute(query, params) {
125
+ return this.client.request(HAEXTENSION_METHODS.database.execute, {
126
+ query,
127
+ params: params || []
128
+ });
129
+ }
130
+ async transaction(statements) {
131
+ await this.client.request(HAEXTENSION_METHODS.database.transaction, {
132
+ statements
133
+ });
134
+ }
135
+ async createTable(tableName, columns) {
136
+ const query = `CREATE TABLE IF NOT EXISTS ${tableName} (${columns})`;
137
+ await this.execute(query);
138
+ }
139
+ async dropTable(tableName) {
140
+ const query = `DROP TABLE IF EXISTS ${tableName}`;
141
+ await this.execute(query);
142
+ }
143
+ /**
144
+ * Registers extension migrations with HaexVault for CRDT synchronization
145
+ * HaexVault will validate and execute these migrations, ensuring only
146
+ * tables with the correct prefix are manipulated
147
+ * @param extensionVersion - The version of the extension
148
+ * @param migrations - Array of migration objects with name and SQL content
149
+ * @returns Promise that resolves when migrations are registered
150
+ */
151
+ async registerMigrationsAsync(extensionVersion, migrations) {
152
+ await this.client.request(HAEXTENSION_METHODS.database.registerMigrations, {
153
+ extensionVersion,
154
+ migrations
155
+ });
156
+ }
157
+ async insert(tableName, data) {
158
+ const keys = Object.keys(data);
159
+ const values = Object.values(data);
160
+ const placeholders = keys.map(() => "?").join(", ");
161
+ const query = `INSERT INTO ${tableName} (${keys.join(
162
+ ", "
163
+ )}) VALUES (${placeholders})`;
164
+ const result = await this.execute(query, values);
165
+ return result.lastInsertId ?? -1;
166
+ }
167
+ async update(tableName, data, where, whereParams) {
168
+ const keys = Object.keys(data);
169
+ const values = Object.values(data);
170
+ const setClause = keys.map((key) => `${key} = ?`).join(", ");
171
+ const query = `UPDATE ${tableName} SET ${setClause} WHERE ${where}`;
172
+ const result = await this.execute(query, [
173
+ ...values,
174
+ ...whereParams || []
175
+ ]);
176
+ return result.rowsAffected;
177
+ }
178
+ async delete(tableName, where, whereParams) {
179
+ const query = `DELETE FROM ${tableName} WHERE ${where}`;
180
+ const result = await this.execute(query, whereParams);
181
+ return result.rowsAffected;
182
+ }
183
+ async count(tableName, where, whereParams) {
184
+ const query = where ? `SELECT COUNT(*) as count FROM ${tableName} WHERE ${where}` : `SELECT COUNT(*) as count FROM ${tableName}`;
185
+ const result = await this.queryOne(query, whereParams);
186
+ return result?.count ?? 0;
187
+ }
188
+ };
189
+
190
+ // src/api/filesystem.ts
191
+ var FilesystemAPI = class {
192
+ constructor(client) {
193
+ this.client = client;
194
+ }
195
+ /**
196
+ * Opens a save file dialog and saves the provided data to the selected location
197
+ * @param data The file data as Uint8Array
198
+ * @param options Options for the save dialog
199
+ * @returns The path where the file was saved, or null if cancelled
200
+ */
201
+ async saveFileAsync(data, options = {}) {
202
+ const result = await this.client.request(
203
+ HAEXTENSION_METHODS.filesystem.saveFile,
204
+ {
205
+ data: Array.from(data),
206
+ // Convert Uint8Array to regular array for postMessage
207
+ defaultPath: options.defaultPath,
208
+ title: options.title,
209
+ filters: options.filters
210
+ }
211
+ );
212
+ return result;
213
+ }
214
+ /**
215
+ * Opens a file with the system's default viewer
216
+ * @param data The file data as Uint8Array
217
+ * @param options Options for opening the file
218
+ * @returns The result of the operation
219
+ */
220
+ async openFileAsync(data, options) {
221
+ const result = await this.client.request(
222
+ HAEXTENSION_METHODS.filesystem.openFile,
223
+ {
224
+ data: Array.from(data),
225
+ // Convert Uint8Array to regular array for postMessage
226
+ fileName: options.fileName,
227
+ mimeType: options.mimeType
228
+ }
229
+ );
230
+ return result;
231
+ }
232
+ /**
233
+ * Shows an image using a data URL (safe, read-only viewing)
234
+ * This is safe to use without special permissions as it only displays images
235
+ * and doesn't execute any code or open files with external applications
236
+ * @param options Options containing the data URL
237
+ * @returns The result of the operation
238
+ */
239
+ async showImageAsync(options) {
240
+ const result = await this.client.request(
241
+ HAEXTENSION_METHODS.filesystem.showImage,
242
+ {
243
+ dataUrl: options.dataUrl
244
+ }
245
+ );
246
+ return result;
247
+ }
248
+ };
249
+
250
+ // src/api/web.ts
251
+ var WebAPI = class {
252
+ constructor(client) {
253
+ this.client = client;
254
+ }
255
+ /**
256
+ * Performs a web request through the HaexHub host application
257
+ * @param url The URL to fetch
258
+ * @param options Request options (method, headers, body, timeout)
259
+ * @returns Promise resolving to the web response
260
+ */
261
+ async fetchAsync(url, options = {}) {
262
+ let bodyParam;
263
+ if (options.body) {
264
+ if (options.body instanceof ArrayBuffer) {
265
+ bodyParam = this.arrayBufferToBase64(options.body);
266
+ } else if (options.body instanceof Blob) {
267
+ bodyParam = await this.blobToBase64(options.body);
268
+ } else {
269
+ bodyParam = options.body;
270
+ }
271
+ }
272
+ const response = await this.client.request(HAEXTENSION_METHODS.web.fetch, {
273
+ url,
274
+ method: options.method || "GET",
275
+ headers: options.headers,
276
+ body: bodyParam,
277
+ timeout: options.timeout
278
+ });
279
+ const bodyBuffer = this.base64ToArrayBuffer(response.body);
280
+ return {
281
+ status: response.status,
282
+ statusText: response.statusText,
283
+ headers: response.headers,
284
+ body: bodyBuffer,
285
+ url: response.url
286
+ };
287
+ }
288
+ /**
289
+ * Convenience method for JSON requests
290
+ */
291
+ async fetchJsonAsync(url, options = {}) {
292
+ const response = await this.fetchAsync(url, options);
293
+ const text = new TextDecoder().decode(response.body);
294
+ return JSON.parse(text);
295
+ }
296
+ /**
297
+ * Convenience method for text requests
298
+ */
299
+ async fetchTextAsync(url, options = {}) {
300
+ const response = await this.fetchAsync(url, options);
301
+ return new TextDecoder().decode(response.body);
302
+ }
303
+ /**
304
+ * Convenience method for blob requests
305
+ */
306
+ async fetchBlobAsync(url, options = {}) {
307
+ const response = await this.fetchAsync(url, options);
308
+ return new Blob([response.body]);
309
+ }
310
+ /**
311
+ * Opens a URL in the user's default browser
312
+ * @param url The URL to open
313
+ */
314
+ async openAsync(url) {
315
+ await this.client.request(HAEXTENSION_METHODS.application.open, {
316
+ application: "browser",
317
+ url
318
+ });
319
+ }
320
+ arrayBufferToBase64(buffer) {
321
+ const bytes = new Uint8Array(buffer);
322
+ let binary = "";
323
+ for (let i = 0; i < bytes.byteLength; i++) {
324
+ const byte = bytes[i];
325
+ if (byte === void 0) {
326
+ throw new Error("Invalid byte at index " + i);
327
+ }
328
+ binary += String.fromCharCode(byte);
329
+ }
330
+ return btoa(binary);
331
+ }
332
+ async blobToBase64(blob) {
333
+ return new Promise((resolve, reject) => {
334
+ const reader = new FileReader();
335
+ reader.onloadend = () => {
336
+ const result = reader.result;
337
+ if (typeof result !== "string") {
338
+ reject(new Error("Failed to read blob as data URL"));
339
+ return;
340
+ }
341
+ const parts = result.split(",");
342
+ const base64 = parts[1];
343
+ if (!base64) {
344
+ reject(new Error("Failed to extract base64 from data URL"));
345
+ return;
346
+ }
347
+ resolve(base64);
348
+ };
349
+ reader.onerror = reject;
350
+ reader.readAsDataURL(blob);
351
+ });
352
+ }
353
+ base64ToArrayBuffer(base64) {
354
+ const binaryString = atob(base64);
355
+ const bytes = new Uint8Array(binaryString.length);
356
+ for (let i = 0; i < binaryString.length; i++) {
357
+ bytes[i] = binaryString.charCodeAt(i);
358
+ }
359
+ return bytes.buffer;
360
+ }
361
+ };
362
+
363
+ // src/api/permissions.ts
364
+ var PermissionsAPI = class {
365
+ constructor(client) {
366
+ this.client = client;
367
+ }
368
+ /**
369
+ * Checks if the extension has permission for a database operation
370
+ * @param resource The database resource (table name or "*" for all tables)
371
+ * @param operation The operation type ("read" or "write")
372
+ * @returns Promise<boolean> indicating if permission is granted
373
+ */
374
+ async checkDatabaseAsync(resource, operation) {
375
+ const response = await this.client.request(
376
+ "permissions.database.check",
377
+ {
378
+ resource,
379
+ operation
380
+ }
381
+ );
382
+ return response.status === "granted";
383
+ }
384
+ /**
385
+ * Checks if the extension has permission for a web request
386
+ * @param url The URL to check (e.g., "https://example.com/path")
387
+ * @returns Promise<boolean> indicating if permission is granted
388
+ * @note Method/operation is not checked - permissions apply to all HTTP methods
389
+ */
390
+ async checkWebAsync(url) {
391
+ const response = await this.client.request(
392
+ "permissions.web.check",
393
+ {
394
+ url
395
+ }
396
+ );
397
+ return response.status === "granted";
398
+ }
399
+ /**
400
+ * Checks if the extension has permission for a filesystem operation
401
+ * @param path The file or directory path
402
+ * @param operation The operation type ("read" or "write")
403
+ * @returns Promise<boolean> indicating if permission is granted
404
+ */
405
+ async checkFilesystemAsync(path, operation) {
406
+ const response = await this.client.request(
407
+ "permissions.filesystem.check",
408
+ {
409
+ path,
410
+ operation
411
+ }
412
+ );
413
+ return response.status === "granted";
414
+ }
415
+ };
416
+
417
+ // src/polyfills/consoleForwarding.ts
418
+ var originalConsole = {
419
+ log: console.log,
420
+ info: console.info,
421
+ warn: console.warn,
422
+ error: console.error,
423
+ debug: console.debug
424
+ };
425
+ function serializeArgs(args) {
426
+ return args.map((arg) => {
427
+ if (arg === null) return "null";
428
+ if (arg === void 0) return "undefined";
429
+ if (typeof arg === "object") {
430
+ try {
431
+ return JSON.stringify(arg, null, 2);
432
+ } catch {
433
+ return String(arg);
434
+ }
435
+ }
436
+ return String(arg);
437
+ }).join(" ");
438
+ }
439
+ function interceptConsole(level) {
440
+ console[level] = function(...args) {
441
+ originalConsole[level].apply(console, args);
442
+ if (window.self !== window.top && window.parent) {
443
+ try {
444
+ const message = serializeArgs(args);
445
+ const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
446
+ window.parent.postMessage(
447
+ {
448
+ type: "console.forward",
449
+ data: {
450
+ timestamp,
451
+ level,
452
+ message
453
+ },
454
+ timestamp: Date.now()
455
+ },
456
+ "*"
457
+ );
458
+ } catch (error) {
459
+ originalConsole.error("[HaexHub] Failed to forward console message:", error);
460
+ }
461
+ }
462
+ };
463
+ }
464
+ function installConsoleForwarding(debug = false) {
465
+ if (typeof window === "undefined") {
466
+ return;
467
+ }
468
+ if (window.self === window.top) {
469
+ return;
470
+ }
471
+ if (!debug) {
472
+ console.log("[HaexHub] Console forwarding disabled (not in debug mode)");
473
+ return;
474
+ }
475
+ interceptConsole("log");
476
+ interceptConsole("info");
477
+ interceptConsole("warn");
478
+ interceptConsole("error");
479
+ interceptConsole("debug");
480
+ console.log("[HaexHub] Console forwarding installed");
481
+ }
482
+ var HaexVaultClient = class {
483
+ constructor(config = {}) {
484
+ this.pendingRequests = /* @__PURE__ */ new Map();
485
+ this.eventListeners = /* @__PURE__ */ new Map();
486
+ this.messageHandler = null;
487
+ this.initialized = false;
488
+ this.requestCounter = 0;
489
+ this._extensionInfo = null;
490
+ this._context = null;
491
+ this.reactiveSubscribers = /* @__PURE__ */ new Set();
492
+ this.isNativeWindow = false;
493
+ // Wird im Konstruktor initialisiert
494
+ this.setupPromise = null;
495
+ this.setupHook = null;
496
+ this._setupCompleted = false;
497
+ this.orm = null;
498
+ this.config = {
499
+ debug: config.debug ?? false,
500
+ timeout: config.timeout ?? DEFAULT_TIMEOUT,
501
+ manifest: config.manifest
502
+ };
503
+ this.storage = new StorageAPI(this);
504
+ this.database = new DatabaseAPI(this);
505
+ this.filesystem = new FilesystemAPI(this);
506
+ this.web = new WebAPI(this);
507
+ this.permissions = new PermissionsAPI(this);
508
+ installConsoleForwarding(this.config.debug);
509
+ this.readyPromise = new Promise((resolve) => {
510
+ this.resolveReady = resolve;
511
+ });
512
+ this.init();
513
+ }
514
+ /**
515
+ * Gibt ein Promise zurück, das aufgelöst wird, sobald der Client
516
+ * initialisiert ist und Extension-Infos empfangen hat.
517
+ */
518
+ async ready() {
519
+ return this.readyPromise;
520
+ }
521
+ /**
522
+ * Gibt zurück, ob das Setup bereits abgeschlossen wurde.
523
+ */
524
+ get setupCompleted() {
525
+ return this._setupCompleted;
526
+ }
527
+ /**
528
+ * Registriert eine Setup-Funktion, die nach der Initialisierung ausgeführt wird.
529
+ * Diese Funktion sollte für Aufgaben wie Tabellenerstellung, Migrationen, etc. verwendet werden.
530
+ * @param setupFn Die Setup-Funktion, die ausgeführt werden soll
531
+ */
532
+ onSetup(setupFn) {
533
+ if (this.setupHook) {
534
+ throw new Error("Setup hook already registered");
535
+ }
536
+ this.setupHook = setupFn;
537
+ }
538
+ /**
539
+ * Gibt ein Promise zurück, das aufgelöst wird, sobald der Client vollständig eingerichtet ist.
540
+ * Dies umfasst die Initialisierung UND das Setup (z.B. Tabellenerstellung).
541
+ * Falls kein Setup-Hook registriert wurde, entspricht dies ready().
542
+ */
543
+ async setupComplete() {
544
+ await this.readyPromise;
545
+ if (!this.setupHook || this.setupCompleted) {
546
+ return;
547
+ }
548
+ if (!this.setupPromise) {
549
+ this.setupPromise = this.runSetupAsync();
550
+ }
551
+ return this.setupPromise;
552
+ }
553
+ async runSetupAsync() {
554
+ if (!this.setupHook) return;
555
+ try {
556
+ this.log("[HaexHub] Running setup hook...");
557
+ await this.setupHook();
558
+ this._setupCompleted = true;
559
+ this.log("[HaexHub] Setup completed successfully");
560
+ this.notifySubscribers();
561
+ } catch (error) {
562
+ this.log("[HaexHub] Setup failed:", error);
563
+ throw error;
564
+ }
565
+ }
566
+ /**
567
+ * Initialisiert die Drizzle-Datenbankinstanz.
568
+ * Muss nach der Definition des Schemas aufgerufen werden.
569
+ * @param schema Das Drizzle-Schemaobjekt (mit bereits geprefixten Tabellennamen).
570
+ * @returns Die typsichere Drizzle-Datenbankinstanz.
571
+ */
572
+ initializeDatabase(schema) {
573
+ if (!this._extensionInfo) {
574
+ throw new HaexHubError(
575
+ "EXTENSION_INFO_UNAVAILABLE" /* EXTENSION_INFO_UNAVAILABLE */,
576
+ "errors.client_not_ready"
577
+ );
578
+ }
579
+ const dbInstance = drizzle(
580
+ async (sql, params, method) => {
581
+ try {
582
+ if (method === "run" || method === "all") {
583
+ const result2 = await this.request(
584
+ HAEXTENSION_METHODS.database.execute,
585
+ {
586
+ query: sql,
587
+ params
588
+ }
589
+ );
590
+ if (method === "all") {
591
+ return { rows: result2.rows || [] };
592
+ }
593
+ if (result2.rows && Array.isArray(result2.rows) && result2.rows.length > 0) {
594
+ return { rows: result2.rows };
595
+ }
596
+ return result2;
597
+ }
598
+ const result = await this.request(HAEXTENSION_METHODS.database.query, {
599
+ query: sql,
600
+ params
601
+ });
602
+ const rows = result.rows;
603
+ if (method === "get") {
604
+ return { rows: rows.length > 0 ? rows.at(0) : void 0 };
605
+ }
606
+ return { rows };
607
+ } catch (error) {
608
+ this.log("Database operation failed:", error);
609
+ throw error;
610
+ }
611
+ },
612
+ {
613
+ schema,
614
+ logger: false
615
+ }
616
+ );
617
+ this.orm = dbInstance;
618
+ return dbInstance;
619
+ }
620
+ get extensionInfo() {
621
+ return this._extensionInfo;
622
+ }
623
+ get context() {
624
+ return this._context;
625
+ }
626
+ subscribe(callback) {
627
+ this.reactiveSubscribers.add(callback);
628
+ return () => {
629
+ this.reactiveSubscribers.delete(callback);
630
+ };
631
+ }
632
+ notifySubscribers() {
633
+ this.reactiveSubscribers.forEach((callback) => callback());
634
+ }
635
+ async getDependencies() {
636
+ return this.request("extensions.getDependencies");
637
+ }
638
+ getTableName(tableName) {
639
+ if (!this._extensionInfo) {
640
+ throw new HaexHubError(
641
+ "EXTENSION_INFO_UNAVAILABLE" /* EXTENSION_INFO_UNAVAILABLE */,
642
+ "errors.extension_info_unavailable"
643
+ );
644
+ }
645
+ this.validateTableName(tableName);
646
+ const { publicKey, name } = this._extensionInfo;
647
+ const extensionName = name;
648
+ return `"${publicKey}${TABLE_SEPARATOR}${extensionName}${TABLE_SEPARATOR}${tableName}"`;
649
+ }
650
+ getDependencyTableName(publicKey, extensionName, tableName) {
651
+ this.validatePublicKey(publicKey);
652
+ this.validateExtensionName(extensionName);
653
+ this.validateTableName(tableName);
654
+ return `"${publicKey}${TABLE_SEPARATOR}${extensionName}${TABLE_SEPARATOR}${tableName}"`;
655
+ }
656
+ parseTableName(fullTableName) {
657
+ let cleanTableName = fullTableName;
658
+ if (cleanTableName.startsWith('"') && cleanTableName.endsWith('"')) {
659
+ cleanTableName = cleanTableName.slice(1, -1);
660
+ }
661
+ const parts = cleanTableName.split(TABLE_SEPARATOR);
662
+ if (parts.length !== 3) {
663
+ return null;
664
+ }
665
+ const [publicKey, extensionName, tableName] = parts;
666
+ if (!publicKey || !extensionName || !tableName) {
667
+ return null;
668
+ }
669
+ return {
670
+ publicKey,
671
+ extensionName,
672
+ tableName
673
+ };
674
+ }
675
+ /**
676
+ * Execute a raw SQL query (SELECT)
677
+ * Returns rows as an array of objects
678
+ */
679
+ async query(sql, params = []) {
680
+ const result = await this.request(
681
+ HAEXTENSION_METHODS.database.query,
682
+ { query: sql, params }
683
+ );
684
+ if (this.config.debug) {
685
+ console.log("[SDK query()] Raw result:", JSON.stringify(result, null, 2));
686
+ }
687
+ return result.rows;
688
+ }
689
+ /**
690
+ * Alias for query() - more intuitive for SELECT statements
691
+ */
692
+ async select(sql, params = []) {
693
+ return this.query(sql, params);
694
+ }
695
+ /**
696
+ * Execute a raw SQL statement (INSERT, UPDATE, DELETE, CREATE, etc.)
697
+ * Returns rowsAffected and lastInsertId
698
+ */
699
+ async execute(sql, params = []) {
700
+ const result = await this.request(
701
+ HAEXTENSION_METHODS.database.execute,
702
+ { query: sql, params }
703
+ );
704
+ return {
705
+ rowsAffected: result.rowsAffected,
706
+ lastInsertId: result.lastInsertId
707
+ };
708
+ }
709
+ /**
710
+ * Registers extension migrations with HaexVault for CRDT synchronization
711
+ * HaexVault will validate and execute these migrations
712
+ * @param extensionVersion - The version of the extension
713
+ * @param migrations - Array of migration objects with name and SQL content
714
+ * @returns Promise that resolves when migrations are registered
715
+ */
716
+ async registerMigrationsAsync(extensionVersion, migrations) {
717
+ return this.database.registerMigrationsAsync(extensionVersion, migrations);
718
+ }
719
+ async requestDatabasePermission(request) {
720
+ return this.request("permissions.database.request", {
721
+ resource: request.resource,
722
+ operation: request.operation,
723
+ reason: request.reason
724
+ });
725
+ }
726
+ async checkDatabasePermission(resource, operation) {
727
+ const response = await this.request(
728
+ "permissions.database.check",
729
+ {
730
+ resource,
731
+ operation
732
+ }
733
+ );
734
+ return response.status === "granted";
735
+ }
736
+ async respondToSearch(requestId, results) {
737
+ await this.request("search.respond", {
738
+ requestId,
739
+ results
740
+ });
741
+ }
742
+ async request(method, params = {}) {
743
+ if (this.isNativeWindow && typeof window.__TAURI__ !== "undefined") {
744
+ return this.invoke(method, params);
745
+ }
746
+ return this.postMessage(method, params);
747
+ }
748
+ async postMessage(method, params) {
749
+ const requestId = this.generateRequestId();
750
+ const request = {
751
+ method,
752
+ params,
753
+ timestamp: Date.now()
754
+ };
755
+ return new Promise((resolve, reject) => {
756
+ const timeout = setTimeout(() => {
757
+ this.pendingRequests.delete(requestId);
758
+ reject(
759
+ new HaexHubError("TIMEOUT" /* TIMEOUT */, "errors.timeout", {
760
+ timeout: this.config.timeout
761
+ })
762
+ );
763
+ }, this.config.timeout);
764
+ this.pendingRequests.set(requestId, { resolve, reject, timeout });
765
+ const targetOrigin = "*";
766
+ if (this.config.debug) {
767
+ console.log("[SDK Debug] ========== Sending Request ==========");
768
+ console.log("[SDK Debug] Request ID:", requestId);
769
+ console.log("[SDK Debug] Method:", request.method);
770
+ console.log("[SDK Debug] Params:", request.params);
771
+ console.log("[SDK Debug] Target origin:", targetOrigin);
772
+ console.log("[SDK Debug] Extension info:", this._extensionInfo);
773
+ console.log("[SDK Debug] ========================================");
774
+ }
775
+ window.parent.postMessage({ id: requestId, ...request }, targetOrigin);
776
+ });
777
+ }
778
+ async invoke(method, params) {
779
+ const { invoke } = window.__TAURI__.core;
780
+ if (this.config.debug) {
781
+ console.log("[SDK Debug] ========== Invoke Request ==========");
782
+ console.log("[SDK Debug] Method:", method);
783
+ console.log("[SDK Debug] Params:", params);
784
+ console.log("[SDK Debug] =======================================");
785
+ }
786
+ switch (method) {
787
+ case HAEXTENSION_METHODS.database.query:
788
+ return invoke("webview_extension_db_query", {
789
+ query: params.query,
790
+ params: params.params || []
791
+ });
792
+ case HAEXTENSION_METHODS.database.execute:
793
+ return invoke("webview_extension_db_execute", {
794
+ query: params.query,
795
+ params: params.params || []
796
+ });
797
+ case "permissions.web.check":
798
+ return invoke("webview_extension_check_web_permission", {
799
+ url: params.url
800
+ });
801
+ case "permissions.database.check":
802
+ return invoke("webview_extension_check_database_permission", {
803
+ resource: params.resource,
804
+ operation: params.operation
805
+ });
806
+ case "permissions.filesystem.check":
807
+ return invoke("webview_extension_check_filesystem_permission", {
808
+ path: params.path,
809
+ actionStr: params.action
810
+ });
811
+ case HAEXTENSION_METHODS.application.open:
812
+ return invoke("webview_extension_web_open", {
813
+ url: params.url
814
+ });
815
+ case HAEXTENSION_METHODS.web.fetch:
816
+ return invoke("webview_extension_web_request", {
817
+ url: params.url,
818
+ method: params.method,
819
+ headers: params.headers,
820
+ body: params.body
821
+ });
822
+ case HAEXTENSION_METHODS.filesystem.saveFile:
823
+ return invoke("webview_extension_fs_save_file", {
824
+ data: params.data,
825
+ defaultPath: params.defaultPath,
826
+ title: params.title,
827
+ filters: params.filters
828
+ });
829
+ case HAEXTENSION_METHODS.filesystem.openFile:
830
+ return invoke("webview_extension_fs_open_file", {
831
+ data: params.data,
832
+ fileName: params.fileName
833
+ });
834
+ default:
835
+ throw new HaexHubError(
836
+ "METHOD_NOT_FOUND" /* METHOD_NOT_FOUND */,
837
+ "errors.method_not_found",
838
+ { method }
839
+ );
840
+ }
841
+ }
842
+ on(eventType, callback) {
843
+ if (!this.eventListeners.has(eventType)) {
844
+ this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
845
+ }
846
+ this.eventListeners.get(eventType).add(callback);
847
+ }
848
+ off(eventType, callback) {
849
+ const listeners = this.eventListeners.get(eventType);
850
+ if (listeners) {
851
+ listeners.delete(callback);
852
+ }
853
+ }
854
+ destroy() {
855
+ if (this.messageHandler) {
856
+ window.removeEventListener("message", this.messageHandler);
857
+ }
858
+ this.pendingRequests.forEach(({ timeout }) => clearTimeout(timeout));
859
+ this.pendingRequests.clear();
860
+ this.eventListeners.clear();
861
+ this.initialized = false;
862
+ this.log("HaexHub SDK destroyed");
863
+ }
864
+ async init() {
865
+ if (this.initialized) return;
866
+ const isInIframe = window.self !== window.top;
867
+ if (!isInIframe) {
868
+ try {
869
+ if (typeof window.__TAURI__ !== "undefined") {
870
+ const { invoke } = window.__TAURI__.core;
871
+ this._extensionInfo = await invoke("webview_extension_get_info");
872
+ this._context = await invoke("webview_extension_context_get");
873
+ this.isNativeWindow = true;
874
+ this.initialized = true;
875
+ this.log("HaexHub SDK initialized in native WebViewWindow mode");
876
+ this.log("Extension info:", this._extensionInfo);
877
+ this.log("Application context:", this._context);
878
+ this.notifySubscribers();
879
+ const { listen } = window.__TAURI__.event;
880
+ console.log("[HaexHub SDK] Setting up Tauri event listener for:", HAEXTENSION_EVENTS.CONTEXT_CHANGED);
881
+ try {
882
+ await listen(HAEXTENSION_EVENTS.CONTEXT_CHANGED, (event) => {
883
+ console.log("[HaexHub SDK] Received Tauri event:", HAEXTENSION_EVENTS.CONTEXT_CHANGED, event);
884
+ this.log("Received context change event:", event);
885
+ if (event.payload?.context) {
886
+ this._context = event.payload.context;
887
+ console.log("[HaexHub SDK] Updated context to:", this._context);
888
+ this.handleEvent({
889
+ type: HAEXTENSION_EVENTS.CONTEXT_CHANGED,
890
+ data: { context: this._context },
891
+ timestamp: Date.now()
892
+ });
893
+ } else {
894
+ console.warn("[HaexHub SDK] Event received but no context in payload:", event);
895
+ }
896
+ });
897
+ console.log("[HaexHub SDK] Context change listener registered successfully");
898
+ } catch (error) {
899
+ console.error("[HaexHub SDK] Failed to setup context change listener:", error);
900
+ this.log("Failed to setup context change listener:", error);
901
+ }
902
+ this.resolveReady();
903
+ return;
904
+ }
905
+ } catch (error) {
906
+ this.log("Tauri commands failed, falling back to iframe mode", error);
907
+ }
908
+ }
909
+ if (window.self === window.top) {
910
+ throw new HaexHubError("NOT_IN_IFRAME" /* NOT_IN_IFRAME */, "errors.not_in_iframe");
911
+ }
912
+ this.messageHandler = this.handleMessage.bind(this);
913
+ window.addEventListener("message", this.messageHandler);
914
+ this.isNativeWindow = false;
915
+ this.initialized = true;
916
+ this.log("HaexHub SDK initialized in iframe mode");
917
+ try {
918
+ if (this.config.manifest) {
919
+ this._extensionInfo = {
920
+ publicKey: this.config.manifest.publicKey,
921
+ name: this.config.manifest.name,
922
+ version: this.config.manifest.version,
923
+ displayName: this.config.manifest.name
924
+ };
925
+ this.log("Extension info loaded from manifest:", this._extensionInfo);
926
+ this.notifySubscribers();
927
+ }
928
+ if (typeof window !== "undefined" && window.parent) {
929
+ const debugInfo = `SDK Debug:
930
+ window.parent exists: ${!!window.parent}
931
+ window.parent === window: ${window.parent === window}
932
+ window.self === window.top: ${window.self === window.top}`;
933
+ try {
934
+ window.parent.postMessage({
935
+ type: "haexhub:debug",
936
+ data: debugInfo
937
+ }, "*");
938
+ } catch (e) {
939
+ alert(debugInfo + `
940
+ postMessage error: ${e}`);
941
+ }
942
+ }
943
+ this._context = await this.request(HAEXTENSION_METHODS.context.get);
944
+ this.log("Application context received:", this._context);
945
+ this.notifySubscribers();
946
+ this.resolveReady();
947
+ } catch (error) {
948
+ this.log("Failed to load extension info or context:", error);
949
+ throw error;
950
+ }
951
+ }
952
+ handleMessage(event) {
953
+ if (this.config.debug) {
954
+ console.log("[SDK Debug] ========== Message Received ==========");
955
+ console.log("[SDK Debug] Event origin:", event.origin);
956
+ console.log(
957
+ "[SDK Debug] Event source:",
958
+ event.source === window.parent ? "parent window" : "unknown"
959
+ );
960
+ console.log("[SDK Debug] Event data:", event.data);
961
+ console.log("[SDK Debug] Extension info loaded:", !!this._extensionInfo);
962
+ console.log(
963
+ "[SDK Debug] Pending requests count:",
964
+ this.pendingRequests.size
965
+ );
966
+ }
967
+ if (event.source !== window.parent) {
968
+ if (this.config.debug) {
969
+ console.error("[SDK Debug] \u274C REJECTED: Message not from parent window!");
970
+ }
971
+ return;
972
+ }
973
+ const data = event.data;
974
+ if ("id" in data && this.pendingRequests.has(data.id)) {
975
+ if (this.config.debug) {
976
+ console.log("[SDK Debug] \u2705 Found pending request for ID:", data.id);
977
+ }
978
+ const pending = this.pendingRequests.get(data.id);
979
+ clearTimeout(pending.timeout);
980
+ this.pendingRequests.delete(data.id);
981
+ if (data.error) {
982
+ if (this.config.debug) {
983
+ console.error("[SDK Debug] \u274C Request failed:", data.error);
984
+ }
985
+ pending.reject(data.error);
986
+ } else {
987
+ if (this.config.debug) {
988
+ console.log("[SDK Debug] \u2705 Request succeeded:", data.result);
989
+ }
990
+ pending.resolve(data.result);
991
+ }
992
+ return;
993
+ }
994
+ if ("id" in data && !this.pendingRequests.has(data.id)) {
995
+ if (this.config.debug) {
996
+ console.warn(
997
+ "[SDK Debug] \u26A0\uFE0F Received response for unknown request ID:",
998
+ data.id
999
+ );
1000
+ console.warn(
1001
+ "[SDK Debug] Known IDs:",
1002
+ Array.from(this.pendingRequests.keys())
1003
+ );
1004
+ }
1005
+ }
1006
+ if ("type" in data && data.type) {
1007
+ if (this.config.debug) {
1008
+ console.log("[SDK Debug] Event received:", data.type);
1009
+ }
1010
+ this.handleEvent(data);
1011
+ }
1012
+ if (this.config.debug) {
1013
+ console.log("[SDK Debug] ========== End Message ==========");
1014
+ }
1015
+ }
1016
+ handleEvent(event) {
1017
+ if (event.type === HAEXTENSION_EVENTS.CONTEXT_CHANGED) {
1018
+ const contextEvent = event;
1019
+ this._context = contextEvent.data.context;
1020
+ this.log("Context updated:", this._context);
1021
+ this.notifySubscribers();
1022
+ }
1023
+ this.emitEvent(event);
1024
+ }
1025
+ emitEvent(event) {
1026
+ this.log("Event received:", event);
1027
+ const listeners = this.eventListeners.get(event.type);
1028
+ if (listeners) {
1029
+ listeners.forEach((callback) => callback(event));
1030
+ }
1031
+ }
1032
+ generateRequestId() {
1033
+ return `req_${++this.requestCounter}`;
1034
+ }
1035
+ validatePublicKey(publicKey) {
1036
+ if (!publicKey || typeof publicKey !== "string" || publicKey.trim() === "") {
1037
+ throw new HaexHubError(
1038
+ "INVALID_PUBLIC_KEY" /* INVALID_PUBLIC_KEY */,
1039
+ "errors.invalid_public_key",
1040
+ { publicKey }
1041
+ );
1042
+ }
1043
+ }
1044
+ validateExtensionName(extensionName) {
1045
+ if (!extensionName || !/^[a-z][a-z0-9-]*$/i.test(extensionName)) {
1046
+ throw new HaexHubError(
1047
+ "INVALID_EXTENSION_NAME" /* INVALID_EXTENSION_NAME */,
1048
+ "errors.invalid_extension_name",
1049
+ { extensionName }
1050
+ );
1051
+ }
1052
+ if (extensionName.includes(TABLE_SEPARATOR)) {
1053
+ throw new HaexHubError(
1054
+ "INVALID_EXTENSION_NAME" /* INVALID_EXTENSION_NAME */,
1055
+ "errors.extension_name_contains_separator",
1056
+ { extensionName, separator: TABLE_SEPARATOR }
1057
+ );
1058
+ }
1059
+ }
1060
+ validateTableName(tableName) {
1061
+ if (!tableName || typeof tableName !== "string") {
1062
+ throw new HaexHubError(
1063
+ "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1064
+ "errors.table_name_empty"
1065
+ );
1066
+ }
1067
+ if (tableName.includes(TABLE_SEPARATOR)) {
1068
+ throw new HaexHubError(
1069
+ "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1070
+ "errors.table_name_contains_separator",
1071
+ { tableName, separator: TABLE_SEPARATOR }
1072
+ );
1073
+ }
1074
+ if (!/^[a-z][a-z0-9-_]*$/i.test(tableName)) {
1075
+ throw new HaexHubError(
1076
+ "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1077
+ "errors.table_name_format",
1078
+ { tableName }
1079
+ );
1080
+ }
1081
+ }
1082
+ log(...args) {
1083
+ if (this.config.debug) {
1084
+ console.log("[HaexHub SDK]", ...args);
1085
+ }
1086
+ }
1087
+ };
1088
+
1089
+ // src/runtime/nuxt.plugin.client.ts
1090
+ var nuxt_plugin_client_default = defineNuxtPlugin(async (nuxtApp) => {
1091
+ const manifest = nuxtApp.$config.public.haexhubManifest;
1092
+ const client = new HaexVaultClient({
1093
+ // @ts-ignore
1094
+ debug: nuxtApp.payload.config.public.debug ?? false,
1095
+ manifest: manifest || void 0
1096
+ });
1097
+ const state = shallowRef({
1098
+ isReady: false,
1099
+ isSetupComplete: false,
1100
+ context: client.context
1101
+ });
1102
+ await client.ready();
1103
+ console.log("[Nuxt Plugin] Client ready, context:", client.context);
1104
+ state.value = {
1105
+ isReady: true,
1106
+ isSetupComplete: false,
1107
+ context: client.context
1108
+ };
1109
+ console.log("[Nuxt Plugin] Initial state set:", state.value);
1110
+ client.subscribe(() => {
1111
+ console.log("[Nuxt Plugin] Client context updated:", client.context);
1112
+ const isSetupComplete = client.setupCompleted;
1113
+ state.value = {
1114
+ ...state.value,
1115
+ context: client.context,
1116
+ isSetupComplete
1117
+ };
1118
+ console.log("[Nuxt Plugin] State updated:", state.value);
1119
+ });
1120
+ const haexhubPlugin = {
1121
+ client,
1122
+ // Der rohe Client (für client.orm, client.database, etc.)
1123
+ state
1124
+ // Der reaktive State (für die UI)
1125
+ };
1126
+ return {
1127
+ provide: {
1128
+ haexhub: haexhubPlugin
1129
+ }
1130
+ };
1131
+ });
1132
+
1133
+ export { nuxt_plugin_client_default as default };
1134
+ //# sourceMappingURL=nuxt.plugin.client.mjs.map
1135
+ //# sourceMappingURL=nuxt.plugin.client.mjs.map