@haex-space/vault-sdk 2.3.15 → 2.4.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.
package/dist/svelte.js CHANGED
@@ -342,6 +342,46 @@ var HAEXTENSION_EVENTS = {
342
342
  EXTERNAL_REQUEST: "haextension:external:request"
343
343
  };
344
344
 
345
+ // src/types.ts
346
+ var DEFAULT_TIMEOUT = 3e4;
347
+ var TABLE_SEPARATOR = "__";
348
+ function getTableName(publicKey, extensionName, tableName) {
349
+ return `${publicKey}${TABLE_SEPARATOR}${extensionName}${TABLE_SEPARATOR}${tableName}`;
350
+ }
351
+ var HaexVaultSdkError = class extends Error {
352
+ constructor(code, messageKey, details) {
353
+ super(messageKey);
354
+ this.code = code;
355
+ this.messageKey = messageKey;
356
+ this.details = details;
357
+ this.name = "HaexVaultSdkError";
358
+ }
359
+ /**
360
+ * Get localized error message
361
+ * @param locale - Locale code (e.g., 'en', 'de')
362
+ * @param translations - Translation object
363
+ */
364
+ getLocalizedMessage(locale = "en", translations) {
365
+ if (!translations || !translations[locale]) {
366
+ return this.messageKey;
367
+ }
368
+ let message = translations[locale][this.messageKey] || this.messageKey;
369
+ if (this.details) {
370
+ Object.entries(this.details).forEach(([key, value]) => {
371
+ message = message.replace(`{${key}}`, String(value));
372
+ });
373
+ }
374
+ return message;
375
+ }
376
+ toJSON() {
377
+ return {
378
+ code: this.code,
379
+ message: this.messageKey,
380
+ details: this.details
381
+ };
382
+ }
383
+ };
384
+
345
385
  // src/methods.ts
346
386
  var HAEXTENSION_METHODS = {
347
387
  context: {
@@ -403,46 +443,6 @@ var HAEXTENSION_METHODS = {
403
443
  }
404
444
  };
405
445
 
406
- // src/types.ts
407
- var DEFAULT_TIMEOUT = 3e4;
408
- var TABLE_SEPARATOR = "__";
409
- function getTableName(publicKey, extensionName, tableName) {
410
- return `${publicKey}${TABLE_SEPARATOR}${extensionName}${TABLE_SEPARATOR}${tableName}`;
411
- }
412
- var HaexHubError = class extends Error {
413
- constructor(code, messageKey, details) {
414
- super(messageKey);
415
- this.code = code;
416
- this.messageKey = messageKey;
417
- this.details = details;
418
- this.name = "HaexHubError";
419
- }
420
- /**
421
- * Get localized error message
422
- * @param locale - Locale code (e.g., 'en', 'de')
423
- * @param translations - Translation object
424
- */
425
- getLocalizedMessage(locale = "en", translations) {
426
- if (!translations || !translations[locale]) {
427
- return this.messageKey;
428
- }
429
- let message = translations[locale][this.messageKey] || this.messageKey;
430
- if (this.details) {
431
- Object.entries(this.details).forEach(([key, value]) => {
432
- message = message.replace(`{${key}}`, String(value));
433
- });
434
- }
435
- return message;
436
- }
437
- toJSON() {
438
- return {
439
- code: this.code,
440
- message: this.messageKey,
441
- details: this.details
442
- };
443
- }
444
- };
445
-
446
446
  // src/api/storage.ts
447
447
  var StorageAPI = class {
448
448
  constructor(client) {
@@ -985,548 +985,543 @@ var PermissionsAPI = class {
985
985
  return response.status === "granted";
986
986
  }
987
987
  };
988
- var HaexVaultClient = class {
989
- constructor(config = {}) {
990
- this.pendingRequests = /* @__PURE__ */ new Map();
991
- this.eventListeners = /* @__PURE__ */ new Map();
992
- this.externalRequestHandlers = /* @__PURE__ */ new Map();
993
- this.messageHandler = null;
994
- this.initialized = false;
995
- this.requestCounter = 0;
996
- this._extensionInfo = null;
997
- this._context = null;
998
- this.reactiveSubscribers = /* @__PURE__ */ new Set();
999
- this.isNativeWindow = false;
1000
- // Wird im Konstruktor initialisiert
1001
- this.setupPromise = null;
1002
- this.setupHook = null;
1003
- this._setupCompleted = false;
1004
- this.orm = null;
1005
- this.config = {
1006
- debug: config.debug ?? false,
1007
- timeout: config.timeout ?? DEFAULT_TIMEOUT,
1008
- manifest: config.manifest
1009
- };
1010
- this.storage = new StorageAPI(this);
1011
- this.database = new DatabaseAPI(this);
1012
- this.filesystem = new FilesystemAPI(this);
1013
- this.web = new WebAPI(this);
1014
- this.permissions = new PermissionsAPI(this);
1015
- installConsoleForwarding(this.config.debug);
1016
- this.readyPromise = new Promise((resolve) => {
1017
- this.resolveReady = resolve;
1018
- });
1019
- this.init();
988
+
989
+ // src/client/tableName.ts
990
+ function validatePublicKey(publicKey) {
991
+ if (!publicKey || typeof publicKey !== "string" || publicKey.trim() === "") {
992
+ throw new HaexVaultSdkError(
993
+ "INVALID_PUBLIC_KEY" /* INVALID_PUBLIC_KEY */,
994
+ "errors.invalid_public_key",
995
+ { publicKey }
996
+ );
1020
997
  }
1021
- /**
1022
- * Gibt ein Promise zurück, das aufgelöst wird, sobald der Client
1023
- * initialisiert ist und Extension-Infos empfangen hat.
1024
- */
1025
- async ready() {
1026
- return this.readyPromise;
998
+ }
999
+ function validateExtensionName(extensionName) {
1000
+ if (!extensionName || !/^[a-z][a-z0-9-]*$/i.test(extensionName)) {
1001
+ throw new HaexVaultSdkError(
1002
+ "INVALID_EXTENSION_NAME" /* INVALID_EXTENSION_NAME */,
1003
+ "errors.invalid_extension_name",
1004
+ { extensionName }
1005
+ );
1027
1006
  }
1028
- /**
1029
- * Gibt zurück, ob das Setup bereits abgeschlossen wurde.
1030
- */
1031
- get setupCompleted() {
1032
- return this._setupCompleted;
1007
+ if (extensionName.includes(TABLE_SEPARATOR)) {
1008
+ throw new HaexVaultSdkError(
1009
+ "INVALID_EXTENSION_NAME" /* INVALID_EXTENSION_NAME */,
1010
+ "errors.extension_name_contains_separator",
1011
+ { extensionName, separator: TABLE_SEPARATOR }
1012
+ );
1033
1013
  }
1034
- /**
1035
- * Registriert eine Setup-Funktion, die nach der Initialisierung ausgeführt wird.
1036
- * Diese Funktion sollte für Aufgaben wie Tabellenerstellung, Migrationen, etc. verwendet werden.
1037
- * @param setupFn Die Setup-Funktion, die ausgeführt werden soll
1038
- */
1039
- onSetup(setupFn) {
1040
- if (this.setupHook) {
1041
- throw new Error("Setup hook already registered");
1042
- }
1043
- this.setupHook = setupFn;
1014
+ }
1015
+ function validateTableName(tableName) {
1016
+ if (!tableName || typeof tableName !== "string") {
1017
+ throw new HaexVaultSdkError(
1018
+ "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1019
+ "errors.table_name_empty"
1020
+ );
1044
1021
  }
1045
- /**
1046
- * Gibt ein Promise zurück, das aufgelöst wird, sobald der Client vollständig eingerichtet ist.
1047
- * Dies umfasst die Initialisierung UND das Setup (z.B. Tabellenerstellung).
1048
- * Falls kein Setup-Hook registriert wurde, entspricht dies ready().
1049
- */
1050
- async setupComplete() {
1051
- await this.readyPromise;
1052
- if (!this.setupHook || this.setupCompleted) {
1053
- return;
1054
- }
1055
- if (!this.setupPromise) {
1056
- this.setupPromise = this.runSetupAsync();
1057
- }
1058
- return this.setupPromise;
1022
+ if (tableName.includes(TABLE_SEPARATOR)) {
1023
+ throw new HaexVaultSdkError(
1024
+ "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1025
+ "errors.table_name_contains_separator",
1026
+ { tableName, separator: TABLE_SEPARATOR }
1027
+ );
1059
1028
  }
1060
- async runSetupAsync() {
1061
- if (!this.setupHook) return;
1062
- try {
1063
- this.log("[HaexSpace] Running setup hook...");
1064
- await this.setupHook();
1065
- this._setupCompleted = true;
1066
- this.log("[HaexSpace] Setup completed successfully");
1067
- this.notifySubscribers();
1068
- } catch (error) {
1069
- this.log("[HaexSpace] Setup failed:", error);
1070
- throw error;
1071
- }
1029
+ if (!/^[a-z][a-z0-9-_]*$/i.test(tableName)) {
1030
+ throw new HaexVaultSdkError(
1031
+ "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1032
+ "errors.table_name_format",
1033
+ { tableName }
1034
+ );
1072
1035
  }
1073
- /**
1074
- * Initialisiert die Drizzle-Datenbankinstanz.
1075
- * Muss nach der Definition des Schemas aufgerufen werden.
1076
- * @param schema Das Drizzle-Schemaobjekt (mit bereits geprefixten Tabellennamen).
1077
- * @returns Die typsichere Drizzle-Datenbankinstanz.
1078
- */
1079
- initializeDatabase(schema) {
1080
- if (!this._extensionInfo) {
1081
- throw new HaexHubError(
1082
- "EXTENSION_INFO_UNAVAILABLE" /* EXTENSION_INFO_UNAVAILABLE */,
1083
- "errors.client_not_ready"
1084
- );
1085
- }
1086
- const dbInstance = sqliteProxy.drizzle(
1087
- async (sql, params, method) => {
1088
- try {
1089
- if (method === "run" || method === "all") {
1090
- const result2 = await this.request(
1091
- HAEXTENSION_METHODS.database.execute,
1092
- {
1093
- query: sql,
1094
- params
1095
- }
1096
- );
1097
- if (method === "all") {
1098
- return { rows: result2.rows || [] };
1099
- }
1100
- if (result2.rows && Array.isArray(result2.rows) && result2.rows.length > 0) {
1101
- return { rows: result2.rows };
1102
- }
1103
- return result2;
1104
- }
1105
- const result = await this.request(HAEXTENSION_METHODS.database.query, {
1106
- query: sql,
1107
- params
1108
- });
1109
- const rows = result.rows;
1110
- if (method === "get") {
1111
- return { rows: rows.length > 0 ? rows.at(0) : void 0 };
1112
- }
1113
- return { rows };
1114
- } catch (error) {
1115
- this.log("Database operation failed:", error);
1116
- throw error;
1117
- }
1118
- },
1119
- {
1120
- schema,
1121
- logger: false
1122
- }
1036
+ }
1037
+ function getExtensionTableName(extensionInfo2, tableName) {
1038
+ if (!extensionInfo2) {
1039
+ throw new HaexVaultSdkError(
1040
+ "EXTENSION_INFO_UNAVAILABLE" /* EXTENSION_INFO_UNAVAILABLE */,
1041
+ "errors.extension_info_unavailable"
1123
1042
  );
1124
- this.orm = dbInstance;
1125
- return dbInstance;
1126
1043
  }
1127
- get extensionInfo() {
1128
- return this._extensionInfo;
1044
+ validateTableName(tableName);
1045
+ const { publicKey, name } = extensionInfo2;
1046
+ return `"${getTableName(publicKey, name, tableName)}"`;
1047
+ }
1048
+ function getDependencyTableName(publicKey, extensionName, tableName) {
1049
+ validatePublicKey(publicKey);
1050
+ validateExtensionName(extensionName);
1051
+ validateTableName(tableName);
1052
+ return `"${getTableName(publicKey, extensionName, tableName)}"`;
1053
+ }
1054
+ function parseTableName(fullTableName) {
1055
+ let cleanTableName = fullTableName;
1056
+ if (cleanTableName.startsWith('"') && cleanTableName.endsWith('"')) {
1057
+ cleanTableName = cleanTableName.slice(1, -1);
1058
+ }
1059
+ const parts = cleanTableName.split(TABLE_SEPARATOR);
1060
+ if (parts.length !== 3) {
1061
+ return null;
1062
+ }
1063
+ const [publicKey, extensionName, tableName] = parts;
1064
+ if (!publicKey || !extensionName || !tableName) {
1065
+ return null;
1066
+ }
1067
+ return {
1068
+ publicKey,
1069
+ extensionName,
1070
+ tableName
1071
+ };
1072
+ }
1073
+
1074
+ // src/client/init.ts
1075
+ function isInIframe() {
1076
+ return window.self !== window.top;
1077
+ }
1078
+ function hasTauri() {
1079
+ return typeof window.__TAURI__ !== "undefined";
1080
+ }
1081
+ function getTauriCore() {
1082
+ return window.__TAURI__.core;
1083
+ }
1084
+ function getTauriEvent() {
1085
+ return window.__TAURI__.event;
1086
+ }
1087
+ async function initNativeMode(ctx, log, onEvent, onContextChange) {
1088
+ const { invoke } = getTauriCore();
1089
+ const extensionInfo2 = await invoke("webview_extension_get_info");
1090
+ const context2 = await invoke("webview_extension_context_get");
1091
+ ctx.state.isNativeWindow = true;
1092
+ ctx.state.initialized = true;
1093
+ ctx.state.extensionInfo = extensionInfo2;
1094
+ ctx.state.context = context2;
1095
+ log("HaexVault SDK initialized in native WebViewWindow mode");
1096
+ log("Extension info:", extensionInfo2);
1097
+ log("Application context:", context2);
1098
+ await setupTauriEventListeners(ctx, log, onEvent, onContextChange);
1099
+ return { extensionInfo: extensionInfo2, context: context2 };
1100
+ }
1101
+ async function setupTauriEventListeners(ctx, log, onEvent, onContextChange) {
1102
+ const { listen } = getTauriEvent();
1103
+ console.log("[HaexVault SDK] Setting up Tauri event listener for:", HAEXTENSION_EVENTS.CONTEXT_CHANGED);
1104
+ try {
1105
+ await listen(HAEXTENSION_EVENTS.CONTEXT_CHANGED, (event) => {
1106
+ console.log("[HaexVault SDK] Received Tauri event:", HAEXTENSION_EVENTS.CONTEXT_CHANGED, event);
1107
+ log("Received context change event:", event);
1108
+ const payload = event.payload;
1109
+ if (payload?.context) {
1110
+ ctx.state.context = payload.context;
1111
+ console.log("[HaexVault SDK] Updated context to:", ctx.state.context);
1112
+ onContextChange(payload.context);
1113
+ onEvent({
1114
+ type: HAEXTENSION_EVENTS.CONTEXT_CHANGED,
1115
+ data: { context: ctx.state.context },
1116
+ timestamp: Date.now()
1117
+ });
1118
+ } else {
1119
+ console.warn("[HaexVault SDK] Event received but no context in payload:", event);
1120
+ }
1121
+ });
1122
+ console.log("[HaexVault SDK] Context change listener registered successfully");
1123
+ } catch (error) {
1124
+ console.error("[HaexVault SDK] Failed to setup context change listener:", error);
1125
+ log("Failed to setup context change listener:", error);
1129
1126
  }
1130
- get context() {
1131
- return this._context;
1127
+ try {
1128
+ await listen(HAEXTENSION_EVENTS.EXTERNAL_REQUEST, (event) => {
1129
+ log("Received external request event:", event);
1130
+ if (event.payload) {
1131
+ onEvent({
1132
+ type: HAEXTENSION_EVENTS.EXTERNAL_REQUEST,
1133
+ data: event.payload,
1134
+ timestamp: Date.now()
1135
+ });
1136
+ }
1137
+ });
1138
+ console.log("[HaexVault SDK] External request listener registered successfully");
1139
+ } catch (error) {
1140
+ console.error("[HaexVault SDK] Failed to setup external request listener:", error);
1141
+ log("Failed to setup external request listener:", error);
1132
1142
  }
1133
- subscribe(callback) {
1134
- this.reactiveSubscribers.add(callback);
1135
- return () => {
1136
- this.reactiveSubscribers.delete(callback);
1143
+ }
1144
+ async function initIframeMode(ctx, log, messageHandler, request) {
1145
+ if (!isInIframe()) {
1146
+ throw new HaexVaultSdkError("NOT_IN_IFRAME" /* NOT_IN_IFRAME */, "errors.not_in_iframe");
1147
+ }
1148
+ ctx.handlers.messageHandler = messageHandler;
1149
+ window.addEventListener("message", messageHandler);
1150
+ ctx.state.isNativeWindow = false;
1151
+ ctx.state.initialized = true;
1152
+ log("HaexVault SDK initialized in iframe mode");
1153
+ if (ctx.config.manifest) {
1154
+ ctx.state.extensionInfo = {
1155
+ publicKey: ctx.config.manifest.publicKey,
1156
+ name: ctx.config.manifest.name,
1157
+ version: ctx.config.manifest.version,
1158
+ displayName: ctx.config.manifest.name
1137
1159
  };
1160
+ log("Extension info loaded from manifest:", ctx.state.extensionInfo);
1138
1161
  }
1139
- notifySubscribers() {
1140
- this.reactiveSubscribers.forEach((callback) => callback());
1162
+ sendDebugInfo(ctx.config);
1163
+ const context2 = await request(HAEXTENSION_METHODS.context.get);
1164
+ ctx.state.context = context2;
1165
+ log("Application context received:", context2);
1166
+ return { context: context2 };
1167
+ }
1168
+ function sendDebugInfo(config) {
1169
+ if (!config.debug) return;
1170
+ if (typeof window === "undefined" || !window.parent) return;
1171
+ const debugInfo = `SDK Debug:
1172
+ window.parent exists: ${!!window.parent}
1173
+ window.parent === window: ${window.parent === window}
1174
+ window.self === window.top: ${window.self === window.top}`;
1175
+ try {
1176
+ window.parent.postMessage({
1177
+ type: HAEXSPACE_MESSAGE_TYPES.DEBUG,
1178
+ data: debugInfo
1179
+ }, "*");
1180
+ } catch (e) {
1181
+ alert(debugInfo + `
1182
+ postMessage error: ${e}`);
1141
1183
  }
1142
- async getDependencies() {
1143
- return this.request("extensions.getDependencies");
1184
+ }
1185
+
1186
+ // src/commands.ts
1187
+ var TAURI_COMMANDS = {
1188
+ database: {
1189
+ query: "webview_extension_db_query",
1190
+ execute: "webview_extension_db_execute",
1191
+ registerMigrations: "webview_extension_db_register_migrations"
1192
+ },
1193
+ permissions: {
1194
+ checkWeb: "webview_extension_check_web_permission",
1195
+ checkDatabase: "webview_extension_check_database_permission",
1196
+ checkFilesystem: "webview_extension_check_filesystem_permission"
1197
+ },
1198
+ web: {
1199
+ open: "webview_extension_web_open",
1200
+ fetch: "webview_extension_web_request"
1201
+ },
1202
+ filesystem: {
1203
+ saveFile: "webview_extension_fs_save_file",
1204
+ openFile: "webview_extension_fs_open_file",
1205
+ showImage: "webview_extension_fs_show_image"
1206
+ },
1207
+ external: {
1208
+ respond: "webview_extension_external_respond"
1209
+ },
1210
+ filesync: {
1211
+ // Spaces
1212
+ listSpaces: "filesync_list_spaces",
1213
+ createSpace: "filesync_create_space",
1214
+ deleteSpace: "filesync_delete_space",
1215
+ // Files
1216
+ listFiles: "filesync_list_files",
1217
+ getFile: "filesync_get_file",
1218
+ uploadFile: "filesync_upload_file",
1219
+ downloadFile: "filesync_download_file",
1220
+ deleteFile: "filesync_delete_file",
1221
+ // Backends
1222
+ listBackends: "filesync_list_backends",
1223
+ addBackend: "filesync_add_backend",
1224
+ removeBackend: "filesync_remove_backend",
1225
+ testBackend: "filesync_test_backend",
1226
+ // Sync Rules
1227
+ listSyncRules: "filesync_list_sync_rules",
1228
+ addSyncRule: "filesync_add_sync_rule",
1229
+ removeSyncRule: "filesync_remove_sync_rule",
1230
+ // Sync Operations
1231
+ getSyncStatus: "filesync_get_sync_status",
1232
+ triggerSync: "filesync_trigger_sync",
1233
+ pauseSync: "filesync_pause_sync",
1234
+ resumeSync: "filesync_resume_sync",
1235
+ // Conflict Resolution
1236
+ resolveConflict: "filesync_resolve_conflict",
1237
+ // UI Helpers
1238
+ selectFolder: "filesync_select_folder"
1144
1239
  }
1145
- getTableName(tableName) {
1146
- if (!this._extensionInfo) {
1147
- throw new HaexHubError(
1148
- "EXTENSION_INFO_UNAVAILABLE" /* EXTENSION_INFO_UNAVAILABLE */,
1149
- "errors.extension_info_unavailable"
1150
- );
1151
- }
1152
- this.validateTableName(tableName);
1153
- const { publicKey, name } = this._extensionInfo;
1154
- return `"${getTableName(publicKey, name, tableName)}"`;
1240
+ };
1241
+
1242
+ // src/transport/handlers/database.ts
1243
+ var databaseHandlers = {
1244
+ [HAEXTENSION_METHODS.database.query]: {
1245
+ command: TAURI_COMMANDS.database.query,
1246
+ args: (p) => ({
1247
+ query: p.query,
1248
+ params: p.params || []
1249
+ })
1250
+ },
1251
+ [HAEXTENSION_METHODS.database.execute]: {
1252
+ command: TAURI_COMMANDS.database.execute,
1253
+ args: (p) => ({
1254
+ query: p.query,
1255
+ params: p.params || []
1256
+ })
1257
+ },
1258
+ [HAEXTENSION_METHODS.database.registerMigrations]: {
1259
+ command: TAURI_COMMANDS.database.registerMigrations,
1260
+ args: (p) => ({
1261
+ extensionVersion: p.extensionVersion,
1262
+ migrations: p.migrations
1263
+ })
1155
1264
  }
1156
- getDependencyTableName(publicKey, extensionName, tableName) {
1157
- this.validatePublicKey(publicKey);
1158
- this.validateExtensionName(extensionName);
1159
- this.validateTableName(tableName);
1160
- return `"${getTableName(publicKey, extensionName, tableName)}"`;
1265
+ };
1266
+
1267
+ // src/transport/handlers/permissions.ts
1268
+ var permissionsHandlers = {
1269
+ "permissions.web.check": {
1270
+ command: TAURI_COMMANDS.permissions.checkWeb,
1271
+ args: (p) => ({
1272
+ url: p.url
1273
+ })
1274
+ },
1275
+ "permissions.database.check": {
1276
+ command: TAURI_COMMANDS.permissions.checkDatabase,
1277
+ args: (p) => ({
1278
+ resource: p.resource,
1279
+ operation: p.operation
1280
+ })
1281
+ },
1282
+ "permissions.filesystem.check": {
1283
+ command: TAURI_COMMANDS.permissions.checkFilesystem,
1284
+ args: (p) => ({
1285
+ path: p.path,
1286
+ actionStr: p.action
1287
+ })
1161
1288
  }
1162
- parseTableName(fullTableName) {
1163
- let cleanTableName = fullTableName;
1164
- if (cleanTableName.startsWith('"') && cleanTableName.endsWith('"')) {
1165
- cleanTableName = cleanTableName.slice(1, -1);
1166
- }
1167
- const parts = cleanTableName.split(TABLE_SEPARATOR);
1168
- if (parts.length !== 3) {
1169
- return null;
1170
- }
1171
- const [publicKey, extensionName, tableName] = parts;
1172
- if (!publicKey || !extensionName || !tableName) {
1173
- return null;
1174
- }
1175
- return {
1176
- publicKey,
1177
- extensionName,
1178
- tableName
1179
- };
1289
+ };
1290
+
1291
+ // src/transport/handlers/web.ts
1292
+ var webHandlers = {
1293
+ [HAEXTENSION_METHODS.application.open]: {
1294
+ command: TAURI_COMMANDS.web.open,
1295
+ args: (p) => ({
1296
+ url: p.url
1297
+ })
1298
+ },
1299
+ [HAEXTENSION_METHODS.web.fetch]: {
1300
+ command: TAURI_COMMANDS.web.fetch,
1301
+ args: (p) => ({
1302
+ url: p.url,
1303
+ method: p.method,
1304
+ headers: p.headers,
1305
+ body: p.body
1306
+ })
1180
1307
  }
1181
- /**
1182
- * Execute a raw SQL query (SELECT)
1183
- * Returns rows as an array of objects
1184
- */
1185
- async query(sql, params = []) {
1186
- const result = await this.request(
1187
- HAEXTENSION_METHODS.database.query,
1188
- { query: sql, params }
1189
- );
1190
- if (this.config.debug) {
1191
- console.log("[SDK query()] Raw result:", JSON.stringify(result, null, 2));
1192
- }
1193
- return result.rows;
1308
+ };
1309
+
1310
+ // src/transport/handlers/filesystem.ts
1311
+ var filesystemHandlers = {
1312
+ [HAEXTENSION_METHODS.filesystem.saveFile]: {
1313
+ command: TAURI_COMMANDS.filesystem.saveFile,
1314
+ args: (p) => ({
1315
+ data: p.data,
1316
+ defaultPath: p.defaultPath,
1317
+ title: p.title,
1318
+ filters: p.filters
1319
+ })
1320
+ },
1321
+ [HAEXTENSION_METHODS.filesystem.openFile]: {
1322
+ command: TAURI_COMMANDS.filesystem.openFile,
1323
+ args: (p) => ({
1324
+ data: p.data,
1325
+ fileName: p.fileName
1326
+ })
1327
+ },
1328
+ [HAEXTENSION_METHODS.filesystem.showImage]: {
1329
+ command: TAURI_COMMANDS.filesystem.showImage,
1330
+ args: (p) => ({
1331
+ dataUrl: p.dataUrl
1332
+ })
1194
1333
  }
1195
- /**
1196
- * Alias for query() - more intuitive for SELECT statements
1197
- */
1198
- async select(sql, params = []) {
1199
- return this.query(sql, params);
1334
+ };
1335
+
1336
+ // src/transport/handlers/external.ts
1337
+ var externalHandlers = {
1338
+ "external.respond": {
1339
+ command: TAURI_COMMANDS.external.respond,
1340
+ args: (p) => ({
1341
+ requestId: p.requestId,
1342
+ success: p.success,
1343
+ data: p.data,
1344
+ error: p.error
1345
+ })
1200
1346
  }
1201
- /**
1202
- * Execute a raw SQL statement (INSERT, UPDATE, DELETE, CREATE, etc.)
1203
- * Returns rowsAffected and lastInsertId
1204
- */
1205
- async execute(sql, params = []) {
1206
- const result = await this.request(
1207
- HAEXTENSION_METHODS.database.execute,
1208
- { query: sql, params }
1209
- );
1210
- return {
1211
- rowsAffected: result.rowsAffected,
1212
- lastInsertId: result.lastInsertId
1213
- };
1214
- }
1215
- /**
1216
- * Registers and applies extension migrations with HaexVault
1217
- *
1218
- * HaexVault will:
1219
- * 1. Validate all SQL statements (ensure only extension's own tables are accessed)
1220
- * 2. Store migrations with applied_at = NULL
1221
- * 3. Query pending migrations sorted by name
1222
- * 4. Apply pending migrations and set up CRDT triggers
1223
- * 5. Mark successful migrations with applied_at timestamp
1224
- *
1225
- * @param extensionVersion - The version of the extension
1226
- * @param migrations - Array of migration objects with name and SQL content
1227
- * @returns Promise with migration result (applied count, already applied count, applied migration names)
1228
- */
1229
- async registerMigrationsAsync(extensionVersion, migrations) {
1230
- return this.database.registerMigrationsAsync(extensionVersion, migrations);
1231
- }
1232
- async requestDatabasePermission(request) {
1233
- return this.request("permissions.database.request", {
1234
- resource: request.resource,
1235
- operation: request.operation,
1236
- reason: request.reason
1237
- });
1238
- }
1239
- async checkDatabasePermission(resource, operation) {
1240
- const response = await this.request(
1241
- "permissions.database.check",
1242
- {
1243
- resource,
1244
- operation
1245
- }
1246
- );
1247
- return response.status === "granted";
1248
- }
1249
- async respondToSearch(requestId, results) {
1250
- await this.request("search.respond", {
1251
- requestId,
1252
- results
1253
- });
1254
- }
1255
- /**
1256
- * Register a handler for external requests (from browser extensions, CLI, servers, etc.)
1257
- *
1258
- * @param action - The action/method name to handle (e.g., "get-logins", "get-totp")
1259
- * @param handler - Function that processes the request and returns a response
1260
- * @returns Unsubscribe function to remove the handler
1261
- *
1262
- * @example
1263
- * ```typescript
1264
- * client.onExternalRequest("get-logins", async (request) => {
1265
- * const entries = await getMatchingEntries(request.payload.url);
1266
- * return {
1267
- * requestId: request.requestId,
1268
- * success: true,
1269
- * data: { entries }
1270
- * };
1271
- * });
1272
- * ```
1273
- */
1274
- onExternalRequest(action, handler) {
1275
- this.externalRequestHandlers.set(action, handler);
1276
- this.log(`[ExternalRequest] Registered handler for action: ${action}`);
1277
- return () => {
1278
- this.externalRequestHandlers.delete(action);
1279
- this.log(`[ExternalRequest] Unregistered handler for action: ${action}`);
1280
- };
1281
- }
1282
- /**
1283
- * Send a response to an external request back to haex-vault
1284
- * This is called internally after a handler processes a request
1285
- */
1286
- async respondToExternalRequest(response) {
1287
- await this.request("external.respond", response);
1288
- }
1289
- async request(method, params) {
1290
- const resolvedParams = params ?? {};
1291
- if (this.isNativeWindow && typeof window.__TAURI__ !== "undefined") {
1292
- return this.invoke(method, resolvedParams);
1293
- }
1294
- return this.postMessage(method, resolvedParams);
1295
- }
1296
- async postMessage(method, params) {
1297
- const requestId = this.generateRequestId();
1298
- const request = {
1299
- method,
1300
- params,
1301
- timestamp: Date.now()
1302
- };
1303
- return new Promise((resolve, reject) => {
1304
- const timeout = setTimeout(() => {
1305
- this.pendingRequests.delete(requestId);
1306
- reject(
1307
- new HaexHubError("TIMEOUT" /* TIMEOUT */, "errors.timeout", {
1308
- timeout: this.config.timeout
1309
- })
1310
- );
1311
- }, this.config.timeout);
1312
- this.pendingRequests.set(requestId, { resolve, reject, timeout });
1313
- const targetOrigin = "*";
1314
- if (this.config.debug) {
1315
- console.log("[SDK Debug] ========== Sending Request ==========");
1316
- console.log("[SDK Debug] Request ID:", requestId);
1317
- console.log("[SDK Debug] Method:", request.method);
1318
- console.log("[SDK Debug] Params:", request.params);
1319
- console.log("[SDK Debug] Target origin:", targetOrigin);
1320
- console.log("[SDK Debug] Extension info:", this._extensionInfo);
1321
- console.log("[SDK Debug] ========================================");
1322
- }
1323
- window.parent.postMessage({ id: requestId, ...request }, targetOrigin);
1324
- });
1325
- }
1326
- async invoke(method, params) {
1327
- const { invoke } = window.__TAURI__.core;
1328
- if (this.config.debug) {
1329
- console.log("[SDK Debug] ========== Invoke Request ==========");
1330
- console.log("[SDK Debug] Method:", method);
1331
- console.log("[SDK Debug] Params:", params);
1332
- console.log("[SDK Debug] =======================================");
1333
- }
1334
- switch (method) {
1335
- case HAEXTENSION_METHODS.database.query:
1336
- return invoke("webview_extension_db_query", {
1337
- query: params.query,
1338
- params: params.params || []
1339
- });
1340
- case HAEXTENSION_METHODS.database.execute:
1341
- return invoke("webview_extension_db_execute", {
1342
- query: params.query,
1343
- params: params.params || []
1344
- });
1345
- case "permissions.web.check":
1346
- return invoke("webview_extension_check_web_permission", {
1347
- url: params.url
1348
- });
1349
- case "permissions.database.check":
1350
- return invoke("webview_extension_check_database_permission", {
1351
- resource: params.resource,
1352
- operation: params.operation
1353
- });
1354
- case "permissions.filesystem.check":
1355
- return invoke("webview_extension_check_filesystem_permission", {
1356
- path: params.path,
1357
- actionStr: params.action
1358
- });
1359
- case HAEXTENSION_METHODS.application.open:
1360
- return invoke("webview_extension_web_open", {
1361
- url: params.url
1362
- });
1363
- case HAEXTENSION_METHODS.web.fetch:
1364
- return invoke("webview_extension_web_request", {
1365
- url: params.url,
1366
- method: params.method,
1367
- headers: params.headers,
1368
- body: params.body
1369
- });
1370
- case HAEXTENSION_METHODS.filesystem.saveFile:
1371
- return invoke("webview_extension_fs_save_file", {
1372
- data: params.data,
1373
- defaultPath: params.defaultPath,
1374
- title: params.title,
1375
- filters: params.filters
1376
- });
1377
- case HAEXTENSION_METHODS.filesystem.openFile:
1378
- return invoke("webview_extension_fs_open_file", {
1379
- data: params.data,
1380
- fileName: params.fileName
1381
- });
1382
- case HAEXTENSION_METHODS.database.registerMigrations:
1383
- return invoke("webview_extension_db_register_migrations", {
1384
- extensionVersion: params.extensionVersion,
1385
- migrations: params.migrations
1386
- });
1387
- case "external.respond":
1388
- return invoke("webview_extension_external_respond", {
1389
- requestId: params.requestId,
1390
- success: params.success,
1391
- data: params.data,
1392
- error: params.error
1393
- });
1394
- default:
1395
- throw new HaexHubError(
1396
- "METHOD_NOT_FOUND" /* METHOD_NOT_FOUND */,
1397
- "errors.method_not_found",
1398
- { method }
1399
- );
1400
- }
1401
- }
1402
- on(eventType, callback) {
1403
- if (!this.eventListeners.has(eventType)) {
1404
- this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
1405
- }
1406
- this.eventListeners.get(eventType).add(callback);
1407
- }
1408
- off(eventType, callback) {
1409
- const listeners = this.eventListeners.get(eventType);
1410
- if (listeners) {
1411
- listeners.delete(callback);
1412
- }
1413
- }
1414
- destroy() {
1415
- if (this.messageHandler) {
1416
- window.removeEventListener("message", this.messageHandler);
1417
- }
1418
- this.pendingRequests.forEach(({ timeout }) => clearTimeout(timeout));
1419
- this.pendingRequests.clear();
1420
- this.eventListeners.clear();
1421
- this.initialized = false;
1422
- this.log("HaexHub SDK destroyed");
1347
+ };
1348
+
1349
+ // src/transport/handlers/filesync.ts
1350
+ var filesyncHandlers = {
1351
+ // ==========================================================================
1352
+ // Spaces
1353
+ // ==========================================================================
1354
+ [HAEXTENSION_METHODS.filesystem.sync.listSpaces]: {
1355
+ command: TAURI_COMMANDS.filesync.listSpaces,
1356
+ args: () => ({})
1357
+ },
1358
+ [HAEXTENSION_METHODS.filesystem.sync.createSpace]: {
1359
+ command: TAURI_COMMANDS.filesync.createSpace,
1360
+ args: (p) => ({ request: p })
1361
+ },
1362
+ [HAEXTENSION_METHODS.filesystem.sync.deleteSpace]: {
1363
+ command: TAURI_COMMANDS.filesync.deleteSpace,
1364
+ args: (p) => ({ spaceId: p.spaceId })
1365
+ },
1366
+ // ==========================================================================
1367
+ // Files
1368
+ // ==========================================================================
1369
+ [HAEXTENSION_METHODS.filesystem.sync.listFiles]: {
1370
+ command: TAURI_COMMANDS.filesync.listFiles,
1371
+ args: (p) => ({ request: p })
1372
+ },
1373
+ [HAEXTENSION_METHODS.filesystem.sync.getFile]: {
1374
+ command: TAURI_COMMANDS.filesync.getFile,
1375
+ args: (p) => ({ fileId: p.fileId })
1376
+ },
1377
+ [HAEXTENSION_METHODS.filesystem.sync.uploadFile]: {
1378
+ command: TAURI_COMMANDS.filesync.uploadFile,
1379
+ args: (p) => ({ request: p })
1380
+ },
1381
+ [HAEXTENSION_METHODS.filesystem.sync.downloadFile]: {
1382
+ command: TAURI_COMMANDS.filesync.downloadFile,
1383
+ args: (p) => ({ request: p })
1384
+ },
1385
+ [HAEXTENSION_METHODS.filesystem.sync.deleteFile]: {
1386
+ command: TAURI_COMMANDS.filesync.deleteFile,
1387
+ args: (p) => ({ fileId: p.fileId })
1388
+ },
1389
+ // ==========================================================================
1390
+ // Backends
1391
+ // ==========================================================================
1392
+ [HAEXTENSION_METHODS.filesystem.sync.listBackends]: {
1393
+ command: TAURI_COMMANDS.filesync.listBackends,
1394
+ args: () => ({})
1395
+ },
1396
+ [HAEXTENSION_METHODS.filesystem.sync.addBackend]: {
1397
+ command: TAURI_COMMANDS.filesync.addBackend,
1398
+ args: (p) => ({ request: p })
1399
+ },
1400
+ [HAEXTENSION_METHODS.filesystem.sync.removeBackend]: {
1401
+ command: TAURI_COMMANDS.filesync.removeBackend,
1402
+ args: (p) => ({ backendId: p.backendId })
1403
+ },
1404
+ [HAEXTENSION_METHODS.filesystem.sync.testBackend]: {
1405
+ command: TAURI_COMMANDS.filesync.testBackend,
1406
+ args: (p) => ({ backendId: p.backendId })
1407
+ },
1408
+ // ==========================================================================
1409
+ // Sync Rules
1410
+ // ==========================================================================
1411
+ [HAEXTENSION_METHODS.filesystem.sync.listSyncRules]: {
1412
+ command: TAURI_COMMANDS.filesync.listSyncRules,
1413
+ args: () => ({})
1414
+ },
1415
+ [HAEXTENSION_METHODS.filesystem.sync.addSyncRule]: {
1416
+ command: TAURI_COMMANDS.filesync.addSyncRule,
1417
+ args: (p) => ({ request: p })
1418
+ },
1419
+ [HAEXTENSION_METHODS.filesystem.sync.removeSyncRule]: {
1420
+ command: TAURI_COMMANDS.filesync.removeSyncRule,
1421
+ args: (p) => ({ ruleId: p.ruleId })
1422
+ },
1423
+ // ==========================================================================
1424
+ // Sync Operations
1425
+ // ==========================================================================
1426
+ [HAEXTENSION_METHODS.filesystem.sync.getSyncStatus]: {
1427
+ command: TAURI_COMMANDS.filesync.getSyncStatus,
1428
+ args: () => ({})
1429
+ },
1430
+ [HAEXTENSION_METHODS.filesystem.sync.triggerSync]: {
1431
+ command: TAURI_COMMANDS.filesync.triggerSync,
1432
+ args: () => ({})
1433
+ },
1434
+ [HAEXTENSION_METHODS.filesystem.sync.pauseSync]: {
1435
+ command: TAURI_COMMANDS.filesync.pauseSync,
1436
+ args: () => ({})
1437
+ },
1438
+ [HAEXTENSION_METHODS.filesystem.sync.resumeSync]: {
1439
+ command: TAURI_COMMANDS.filesync.resumeSync,
1440
+ args: () => ({})
1441
+ },
1442
+ // ==========================================================================
1443
+ // Conflict Resolution
1444
+ // ==========================================================================
1445
+ [HAEXTENSION_METHODS.filesystem.sync.resolveConflict]: {
1446
+ command: TAURI_COMMANDS.filesync.resolveConflict,
1447
+ args: (p) => ({ request: p })
1448
+ },
1449
+ // ==========================================================================
1450
+ // UI Helpers
1451
+ // ==========================================================================
1452
+ [HAEXTENSION_METHODS.filesystem.sync.selectFolder]: {
1453
+ command: TAURI_COMMANDS.filesync.selectFolder,
1454
+ args: () => ({})
1423
1455
  }
1424
- async init() {
1425
- if (this.initialized) return;
1426
- const isInIframe = window.self !== window.top;
1427
- if (!isInIframe) {
1428
- try {
1429
- if (typeof window.__TAURI__ !== "undefined") {
1430
- const { invoke } = window.__TAURI__.core;
1431
- this._extensionInfo = await invoke("webview_extension_get_info");
1432
- this._context = await invoke("webview_extension_context_get");
1433
- this.isNativeWindow = true;
1434
- this.initialized = true;
1435
- this.log("HaexHub SDK initialized in native WebViewWindow mode");
1436
- this.log("Extension info:", this._extensionInfo);
1437
- this.log("Application context:", this._context);
1438
- this.notifySubscribers();
1439
- const { listen } = window.__TAURI__.event;
1440
- console.log("[HaexSpace SDK] Setting up Tauri event listener for:", HAEXTENSION_EVENTS.CONTEXT_CHANGED);
1441
- try {
1442
- await listen(HAEXTENSION_EVENTS.CONTEXT_CHANGED, (event) => {
1443
- console.log("[HaexSpace SDK] Received Tauri event:", HAEXTENSION_EVENTS.CONTEXT_CHANGED, event);
1444
- this.log("Received context change event:", event);
1445
- if (event.payload?.context) {
1446
- this._context = event.payload.context;
1447
- console.log("[HaexSpace SDK] Updated context to:", this._context);
1448
- this.handleEvent({
1449
- type: HAEXTENSION_EVENTS.CONTEXT_CHANGED,
1450
- data: { context: this._context },
1451
- timestamp: Date.now()
1452
- });
1453
- } else {
1454
- console.warn("[HaexSpace SDK] Event received but no context in payload:", event);
1455
- }
1456
- });
1457
- console.log("[HaexSpace SDK] Context change listener registered successfully");
1458
- } catch (error) {
1459
- console.error("[HaexSpace SDK] Failed to setup context change listener:", error);
1460
- this.log("Failed to setup context change listener:", error);
1461
- }
1462
- try {
1463
- await listen(HAEXTENSION_EVENTS.EXTERNAL_REQUEST, (event) => {
1464
- this.log("Received external request event:", event);
1465
- if (event.payload) {
1466
- this.handleEvent({
1467
- type: HAEXTENSION_EVENTS.EXTERNAL_REQUEST,
1468
- data: event.payload,
1469
- timestamp: Date.now()
1470
- });
1471
- }
1472
- });
1473
- console.log("[HaexSpace SDK] External request listener registered successfully");
1474
- } catch (error) {
1475
- console.error("[HaexSpace SDK] Failed to setup external request listener:", error);
1476
- this.log("Failed to setup external request listener:", error);
1477
- }
1478
- this.resolveReady();
1479
- return;
1480
- }
1481
- } catch (error) {
1482
- this.log("Tauri commands failed, falling back to iframe mode", error);
1483
- }
1484
- }
1485
- if (window.self === window.top) {
1486
- throw new HaexHubError("NOT_IN_IFRAME" /* NOT_IN_IFRAME */, "errors.not_in_iframe");
1487
- }
1488
- this.messageHandler = this.handleMessage.bind(this);
1489
- window.addEventListener("message", this.messageHandler);
1490
- this.isNativeWindow = false;
1491
- this.initialized = true;
1492
- this.log("HaexSpace SDK initialized in iframe mode");
1493
- try {
1494
- if (this.config.manifest) {
1495
- this._extensionInfo = {
1496
- publicKey: this.config.manifest.publicKey,
1497
- name: this.config.manifest.name,
1498
- version: this.config.manifest.version,
1499
- displayName: this.config.manifest.name
1500
- };
1501
- this.log("Extension info loaded from manifest:", this._extensionInfo);
1502
- this.notifySubscribers();
1503
- }
1504
- if (typeof window !== "undefined" && window.parent) {
1505
- const debugInfo = `SDK Debug:
1506
- window.parent exists: ${!!window.parent}
1507
- window.parent === window: ${window.parent === window}
1508
- window.self === window.top: ${window.self === window.top}`;
1509
- try {
1510
- window.parent.postMessage({
1511
- type: HAEXSPACE_MESSAGE_TYPES.DEBUG,
1512
- data: debugInfo
1513
- }, "*");
1514
- } catch (e) {
1515
- alert(debugInfo + `
1516
- postMessage error: ${e}`);
1517
- }
1518
- }
1519
- this._context = await this.request(HAEXTENSION_METHODS.context.get);
1520
- this.log("Application context received:", this._context);
1521
- this.notifySubscribers();
1522
- this.resolveReady();
1523
- } catch (error) {
1524
- this.log("Failed to load extension info or context:", error);
1525
- throw error;
1456
+ };
1457
+
1458
+ // src/transport/handlers/index.ts
1459
+ var allHandlers = {
1460
+ ...databaseHandlers,
1461
+ ...permissionsHandlers,
1462
+ ...webHandlers,
1463
+ ...filesystemHandlers,
1464
+ ...externalHandlers,
1465
+ ...filesyncHandlers
1466
+ };
1467
+
1468
+ // src/client/transport.ts
1469
+ function generateRequestId(counter) {
1470
+ return `req_${counter}`;
1471
+ }
1472
+ function sendPostMessage(method, params, requestId, config, extensionInfo2, pendingRequests) {
1473
+ const request = {
1474
+ method,
1475
+ params,
1476
+ timestamp: Date.now()
1477
+ };
1478
+ return new Promise((resolve, reject) => {
1479
+ const timeout = setTimeout(() => {
1480
+ pendingRequests.delete(requestId);
1481
+ reject(
1482
+ new HaexVaultSdkError("TIMEOUT" /* TIMEOUT */, "errors.timeout", {
1483
+ timeout: config.timeout
1484
+ })
1485
+ );
1486
+ }, config.timeout);
1487
+ pendingRequests.set(requestId, { resolve, reject, timeout });
1488
+ const targetOrigin = "*";
1489
+ if (config.debug) {
1490
+ console.log("[SDK Debug] ========== Sending Request ==========");
1491
+ console.log("[SDK Debug] Request ID:", requestId);
1492
+ console.log("[SDK Debug] Method:", request.method);
1493
+ console.log("[SDK Debug] Params:", request.params);
1494
+ console.log("[SDK Debug] Target origin:", targetOrigin);
1495
+ console.log("[SDK Debug] Extension info:", extensionInfo2);
1496
+ console.log("[SDK Debug] ========================================");
1526
1497
  }
1527
- }
1528
- handleMessage(event) {
1529
- if (this.config.debug) {
1498
+ window.parent.postMessage({ id: requestId, ...request }, targetOrigin);
1499
+ });
1500
+ }
1501
+ async function sendInvoke(method, params, config, log) {
1502
+ const { invoke } = window.__TAURI__.core;
1503
+ if (config.debug) {
1504
+ console.log("[SDK Debug] ========== Invoke Request ==========");
1505
+ console.log("[SDK Debug] Method:", method);
1506
+ console.log("[SDK Debug] Params:", params);
1507
+ console.log("[SDK Debug] =======================================");
1508
+ }
1509
+ const handler = allHandlers[method];
1510
+ if (handler) {
1511
+ const args = handler.args(params);
1512
+ return invoke(handler.command, args);
1513
+ }
1514
+ throw new HaexVaultSdkError(
1515
+ "METHOD_NOT_FOUND" /* METHOD_NOT_FOUND */,
1516
+ "errors.method_not_found",
1517
+ { method }
1518
+ );
1519
+ }
1520
+
1521
+ // src/client/events.ts
1522
+ function createMessageHandler(config, pendingRequests, extensionInfo2, onEvent) {
1523
+ return (event) => {
1524
+ if (config.debug) {
1530
1525
  console.log("[SDK Debug] ========== Message Received ==========");
1531
1526
  console.log("[SDK Debug] Event origin:", event.origin);
1532
1527
  console.log(
@@ -1534,160 +1529,534 @@ postMessage error: ${e}`);
1534
1529
  event.source === window.parent ? "parent window" : "unknown"
1535
1530
  );
1536
1531
  console.log("[SDK Debug] Event data:", event.data);
1537
- console.log("[SDK Debug] Extension info loaded:", !!this._extensionInfo);
1532
+ console.log("[SDK Debug] Extension info loaded:", !!extensionInfo2());
1538
1533
  console.log(
1539
1534
  "[SDK Debug] Pending requests count:",
1540
- this.pendingRequests.size
1535
+ pendingRequests.size
1541
1536
  );
1542
1537
  }
1543
1538
  if (event.source !== window.parent) {
1544
- if (this.config.debug) {
1539
+ if (config.debug) {
1545
1540
  console.error("[SDK Debug] \u274C REJECTED: Message not from parent window!");
1546
1541
  }
1547
1542
  return;
1548
1543
  }
1549
1544
  const data = event.data;
1550
- if ("id" in data && this.pendingRequests.has(data.id)) {
1551
- if (this.config.debug) {
1545
+ if ("id" in data && pendingRequests.has(data.id)) {
1546
+ if (config.debug) {
1552
1547
  console.log("[SDK Debug] \u2705 Found pending request for ID:", data.id);
1553
1548
  }
1554
- const pending = this.pendingRequests.get(data.id);
1549
+ const pending = pendingRequests.get(data.id);
1555
1550
  clearTimeout(pending.timeout);
1556
- this.pendingRequests.delete(data.id);
1551
+ pendingRequests.delete(data.id);
1557
1552
  if (data.error) {
1558
- if (this.config.debug) {
1553
+ if (config.debug) {
1559
1554
  console.error("[SDK Debug] \u274C Request failed:", data.error);
1560
1555
  }
1561
1556
  pending.reject(data.error);
1562
1557
  } else {
1563
- if (this.config.debug) {
1558
+ if (config.debug) {
1564
1559
  console.log("[SDK Debug] \u2705 Request succeeded:", data.result);
1565
1560
  }
1566
1561
  pending.resolve(data.result);
1567
1562
  }
1568
1563
  return;
1569
1564
  }
1570
- if ("id" in data && !this.pendingRequests.has(data.id)) {
1571
- if (this.config.debug) {
1565
+ if ("id" in data && !pendingRequests.has(data.id)) {
1566
+ if (config.debug) {
1572
1567
  console.warn(
1573
1568
  "[SDK Debug] \u26A0\uFE0F Received response for unknown request ID:",
1574
1569
  data.id
1575
1570
  );
1576
1571
  console.warn(
1577
1572
  "[SDK Debug] Known IDs:",
1578
- Array.from(this.pendingRequests.keys())
1573
+ Array.from(pendingRequests.keys())
1579
1574
  );
1580
1575
  }
1581
1576
  }
1582
1577
  if ("type" in data && data.type) {
1583
- if (this.config.debug) {
1578
+ if (config.debug) {
1584
1579
  console.log("[SDK Debug] Event received:", data.type);
1585
1580
  }
1586
- this.handleEvent(data);
1581
+ onEvent(data);
1587
1582
  }
1588
- if (this.config.debug) {
1583
+ if (config.debug) {
1589
1584
  console.log("[SDK Debug] ========== End Message ==========");
1590
1585
  }
1586
+ };
1587
+ }
1588
+ function processEvent(event, log, eventListeners, onContextChanged, onExternalRequest) {
1589
+ if (event.type === HAEXTENSION_EVENTS.CONTEXT_CHANGED) {
1590
+ const contextEvent = event;
1591
+ onContextChanged(contextEvent.data.context);
1592
+ log("Context updated:", contextEvent.data.context);
1593
+ }
1594
+ if (event.type === HAEXTENSION_EVENTS.EXTERNAL_REQUEST) {
1595
+ const externalEvent = event;
1596
+ onExternalRequest(externalEvent);
1597
+ return;
1591
1598
  }
1592
- handleEvent(event) {
1593
- if (event.type === HAEXTENSION_EVENTS.CONTEXT_CHANGED) {
1594
- const contextEvent = event;
1595
- this._context = contextEvent.data.context;
1596
- this.log("Context updated:", this._context);
1597
- this.notifySubscribers();
1599
+ emitEvent(event, log, eventListeners);
1600
+ }
1601
+ function emitEvent(event, log, eventListeners) {
1602
+ log("Event received:", event);
1603
+ const listeners = eventListeners.get(event.type);
1604
+ if (listeners) {
1605
+ listeners.forEach((callback) => callback(event));
1606
+ }
1607
+ }
1608
+ function addEventListener(eventType, callback, eventListeners) {
1609
+ if (!eventListeners.has(eventType)) {
1610
+ eventListeners.set(eventType, /* @__PURE__ */ new Set());
1611
+ }
1612
+ eventListeners.get(eventType).add(callback);
1613
+ }
1614
+ function removeEventListener(eventType, callback, eventListeners) {
1615
+ const listeners = eventListeners.get(eventType);
1616
+ if (listeners) {
1617
+ listeners.delete(callback);
1618
+ }
1619
+ }
1620
+ function notifySubscribers(subscribers) {
1621
+ subscribers.forEach((callback) => callback());
1622
+ }
1623
+ function createDrizzleInstance(schema, extensionInfo2, request, log) {
1624
+ if (!extensionInfo2) {
1625
+ throw new HaexVaultSdkError(
1626
+ "EXTENSION_INFO_UNAVAILABLE" /* EXTENSION_INFO_UNAVAILABLE */,
1627
+ "errors.client_not_ready"
1628
+ );
1629
+ }
1630
+ return sqliteProxy.drizzle(
1631
+ async (sql, params, method) => {
1632
+ try {
1633
+ if (method === "run" || method === "all") {
1634
+ const result2 = await request(
1635
+ HAEXTENSION_METHODS.database.execute,
1636
+ {
1637
+ query: sql,
1638
+ params
1639
+ }
1640
+ );
1641
+ if (method === "all") {
1642
+ return { rows: result2.rows || [] };
1643
+ }
1644
+ if (result2.rows && Array.isArray(result2.rows) && result2.rows.length > 0) {
1645
+ return { rows: result2.rows };
1646
+ }
1647
+ return result2;
1648
+ }
1649
+ const result = await request(HAEXTENSION_METHODS.database.query, {
1650
+ query: sql,
1651
+ params
1652
+ });
1653
+ const rows = result.rows;
1654
+ if (method === "get") {
1655
+ return { rows: rows.length > 0 ? rows.at(0) : void 0 };
1656
+ }
1657
+ return { rows };
1658
+ } catch (error) {
1659
+ log("Database operation failed:", error);
1660
+ throw error;
1661
+ }
1662
+ },
1663
+ {
1664
+ schema,
1665
+ logger: false
1598
1666
  }
1599
- if (event.type === HAEXTENSION_EVENTS.EXTERNAL_REQUEST) {
1600
- const externalEvent = event;
1601
- this.handleExternalRequest(externalEvent.data);
1602
- return;
1667
+ );
1668
+ }
1669
+ async function queryRaw(sql, params, request, debug) {
1670
+ const result = await request(
1671
+ HAEXTENSION_METHODS.database.query,
1672
+ { query: sql, params }
1673
+ );
1674
+ if (debug) {
1675
+ console.log("[SDK query()] Raw result:", JSON.stringify(result, null, 2));
1676
+ }
1677
+ return result.rows;
1678
+ }
1679
+ async function executeRaw(sql, params, request) {
1680
+ const result = await request(
1681
+ HAEXTENSION_METHODS.database.execute,
1682
+ { query: sql, params }
1683
+ );
1684
+ return {
1685
+ rowsAffected: result.rowsAffected,
1686
+ lastInsertId: result.lastInsertId
1687
+ };
1688
+ }
1689
+
1690
+ // src/client/external.ts
1691
+ function registerExternalHandler(action, handler, handlers, log) {
1692
+ handlers.set(action, handler);
1693
+ log(`[ExternalRequest] Registered handler for action: ${action}`);
1694
+ return () => {
1695
+ handlers.delete(action);
1696
+ log(`[ExternalRequest] Unregistered handler for action: ${action}`);
1697
+ };
1698
+ }
1699
+ async function handleExternalRequest(request, handlers, respond, log) {
1700
+ log(`[ExternalRequest] Received request: ${request.action} from ${request.publicKey.substring(0, 20)}...`);
1701
+ const handler = handlers.get(request.action);
1702
+ if (!handler) {
1703
+ log(`[ExternalRequest] No handler for action: ${request.action}`);
1704
+ await respond({
1705
+ requestId: request.requestId,
1706
+ success: false,
1707
+ error: `No handler registered for action: ${request.action}`
1708
+ });
1709
+ return;
1710
+ }
1711
+ try {
1712
+ const response = await handler(request);
1713
+ await respond(response);
1714
+ log(`[ExternalRequest] Response sent for: ${request.action}`);
1715
+ } catch (error) {
1716
+ log(`[ExternalRequest] Handler error:`, error);
1717
+ await respond({
1718
+ requestId: request.requestId,
1719
+ success: false,
1720
+ error: error instanceof Error ? error.message : String(error)
1721
+ });
1722
+ }
1723
+ }
1724
+ async function respondToExternalRequest(response, request) {
1725
+ await request("external.respond", response);
1726
+ }
1727
+
1728
+ // src/client.ts
1729
+ var HaexVaultClient = class {
1730
+ constructor(config = {}) {
1731
+ // State
1732
+ this.initialized = false;
1733
+ this.isNativeWindow = false;
1734
+ this.requestCounter = 0;
1735
+ this._extensionInfo = null;
1736
+ this._context = null;
1737
+ this._setupCompleted = false;
1738
+ // Collections
1739
+ this.pendingRequests = /* @__PURE__ */ new Map();
1740
+ this.eventListeners = /* @__PURE__ */ new Map();
1741
+ this.externalRequestHandlers = /* @__PURE__ */ new Map();
1742
+ this.reactiveSubscribers = /* @__PURE__ */ new Set();
1743
+ // Handlers
1744
+ this.messageHandler = null;
1745
+ this.setupPromise = null;
1746
+ this.setupHook = null;
1747
+ // Public APIs
1748
+ this.orm = null;
1749
+ this.config = {
1750
+ debug: config.debug ?? false,
1751
+ timeout: config.timeout ?? DEFAULT_TIMEOUT,
1752
+ manifest: config.manifest
1753
+ };
1754
+ this.storage = new StorageAPI(this);
1755
+ this.database = new DatabaseAPI(this);
1756
+ this.filesystem = new FilesystemAPI(this);
1757
+ this.web = new WebAPI(this);
1758
+ this.permissions = new PermissionsAPI(this);
1759
+ installConsoleForwarding(this.config.debug);
1760
+ this.readyPromise = new Promise((resolve) => {
1761
+ this.resolveReady = resolve;
1762
+ });
1763
+ this.init();
1764
+ }
1765
+ // ==========================================================================
1766
+ // Lifecycle
1767
+ // ==========================================================================
1768
+ async ready() {
1769
+ return this.readyPromise;
1770
+ }
1771
+ get setupCompleted() {
1772
+ return this._setupCompleted;
1773
+ }
1774
+ onSetup(setupFn) {
1775
+ if (this.setupHook) {
1776
+ throw new Error("Setup hook already registered");
1603
1777
  }
1604
- this.emitEvent(event);
1605
- }
1606
- async handleExternalRequest(request) {
1607
- this.log(`[ExternalRequest] Received request: ${request.action} from ${request.publicKey.substring(0, 20)}...`);
1608
- const handler = this.externalRequestHandlers.get(request.action);
1609
- if (!handler) {
1610
- this.log(`[ExternalRequest] No handler for action: ${request.action}`);
1611
- await this.respondToExternalRequest({
1612
- requestId: request.requestId,
1613
- success: false,
1614
- error: `No handler registered for action: ${request.action}`
1615
- });
1778
+ this.setupHook = setupFn;
1779
+ }
1780
+ async setupComplete() {
1781
+ await this.readyPromise;
1782
+ if (!this.setupHook || this.setupCompleted) {
1616
1783
  return;
1617
1784
  }
1618
- try {
1619
- const response = await handler(request);
1620
- await this.respondToExternalRequest(response);
1621
- this.log(`[ExternalRequest] Response sent for: ${request.action}`);
1622
- } catch (error) {
1623
- this.log(`[ExternalRequest] Handler error:`, error);
1624
- await this.respondToExternalRequest({
1625
- requestId: request.requestId,
1626
- success: false,
1627
- error: error instanceof Error ? error.message : String(error)
1628
- });
1785
+ if (!this.setupPromise) {
1786
+ this.setupPromise = this.runSetupAsync();
1629
1787
  }
1788
+ return this.setupPromise;
1630
1789
  }
1631
- emitEvent(event) {
1632
- this.log("Event received:", event);
1633
- const listeners = this.eventListeners.get(event.type);
1634
- if (listeners) {
1635
- listeners.forEach((callback) => callback(event));
1790
+ destroy() {
1791
+ if (this.messageHandler) {
1792
+ window.removeEventListener("message", this.messageHandler);
1636
1793
  }
1794
+ this.pendingRequests.forEach(({ timeout }) => clearTimeout(timeout));
1795
+ this.pendingRequests.clear();
1796
+ this.eventListeners.clear();
1797
+ this.initialized = false;
1798
+ this.log("HaexVault SDK destroyed");
1637
1799
  }
1638
- generateRequestId() {
1639
- return `req_${++this.requestCounter}`;
1800
+ // ==========================================================================
1801
+ // Properties
1802
+ // ==========================================================================
1803
+ get extensionInfo() {
1804
+ return this._extensionInfo;
1640
1805
  }
1641
- validatePublicKey(publicKey) {
1642
- if (!publicKey || typeof publicKey !== "string" || publicKey.trim() === "") {
1643
- throw new HaexHubError(
1644
- "INVALID_PUBLIC_KEY" /* INVALID_PUBLIC_KEY */,
1645
- "errors.invalid_public_key",
1646
- { publicKey }
1647
- );
1648
- }
1806
+ get context() {
1807
+ return this._context;
1649
1808
  }
1650
- validateExtensionName(extensionName) {
1651
- if (!extensionName || !/^[a-z][a-z0-9-]*$/i.test(extensionName)) {
1652
- throw new HaexHubError(
1653
- "INVALID_EXTENSION_NAME" /* INVALID_EXTENSION_NAME */,
1654
- "errors.invalid_extension_name",
1655
- { extensionName }
1656
- );
1657
- }
1658
- if (extensionName.includes(TABLE_SEPARATOR)) {
1659
- throw new HaexHubError(
1660
- "INVALID_EXTENSION_NAME" /* INVALID_EXTENSION_NAME */,
1661
- "errors.extension_name_contains_separator",
1662
- { extensionName, separator: TABLE_SEPARATOR }
1663
- );
1809
+ // ==========================================================================
1810
+ // Subscriptions
1811
+ // ==========================================================================
1812
+ subscribe(callback) {
1813
+ this.reactiveSubscribers.add(callback);
1814
+ return () => {
1815
+ this.reactiveSubscribers.delete(callback);
1816
+ };
1817
+ }
1818
+ // ==========================================================================
1819
+ // Table Name Utilities
1820
+ // ==========================================================================
1821
+ getTableName(tableName) {
1822
+ return getExtensionTableName(this._extensionInfo, tableName);
1823
+ }
1824
+ getDependencyTableName(publicKey, extensionName, tableName) {
1825
+ return getDependencyTableName(publicKey, extensionName, tableName);
1826
+ }
1827
+ parseTableName(fullTableName) {
1828
+ return parseTableName(fullTableName);
1829
+ }
1830
+ // ==========================================================================
1831
+ // Database
1832
+ // ==========================================================================
1833
+ initializeDatabase(schema) {
1834
+ const db = createDrizzleInstance(schema, this._extensionInfo, this.request.bind(this), this.log.bind(this));
1835
+ this.orm = db;
1836
+ return db;
1837
+ }
1838
+ async query(sql, params = []) {
1839
+ return queryRaw(sql, params, this.request.bind(this), this.config.debug);
1840
+ }
1841
+ async select(sql, params = []) {
1842
+ return this.query(sql, params);
1843
+ }
1844
+ async execute(sql, params = []) {
1845
+ return executeRaw(sql, params, this.request.bind(this));
1846
+ }
1847
+ async registerMigrationsAsync(extensionVersion, migrations) {
1848
+ return this.database.registerMigrationsAsync(extensionVersion, migrations);
1849
+ }
1850
+ // ==========================================================================
1851
+ // Dependencies
1852
+ // ==========================================================================
1853
+ async getDependencies() {
1854
+ return this.request("extensions.getDependencies");
1855
+ }
1856
+ // ==========================================================================
1857
+ // Permissions
1858
+ // ==========================================================================
1859
+ async requestDatabasePermission(request) {
1860
+ return this.request("permissions.database.request", {
1861
+ resource: request.resource,
1862
+ operation: request.operation,
1863
+ reason: request.reason
1864
+ });
1865
+ }
1866
+ async checkDatabasePermission(resource, operation) {
1867
+ const response = await this.request("permissions.database.check", { resource, operation });
1868
+ return response.status === "granted";
1869
+ }
1870
+ // ==========================================================================
1871
+ // Search
1872
+ // ==========================================================================
1873
+ async respondToSearch(requestId, results) {
1874
+ await this.request("search.respond", { requestId, results });
1875
+ }
1876
+ // ==========================================================================
1877
+ // External Requests
1878
+ // ==========================================================================
1879
+ onExternalRequest(action, handler) {
1880
+ return registerExternalHandler(action, handler, this.externalRequestHandlers, this.log.bind(this));
1881
+ }
1882
+ async respondToExternalRequest(response) {
1883
+ await respondToExternalRequest(response, this.request.bind(this));
1884
+ }
1885
+ // ==========================================================================
1886
+ // Events
1887
+ // ==========================================================================
1888
+ on(eventType, callback) {
1889
+ addEventListener(eventType, callback, this.eventListeners);
1890
+ }
1891
+ off(eventType, callback) {
1892
+ removeEventListener(eventType, callback, this.eventListeners);
1893
+ }
1894
+ // ==========================================================================
1895
+ // Communication
1896
+ // ==========================================================================
1897
+ async request(method, params) {
1898
+ const resolvedParams = params ?? {};
1899
+ if (this.isNativeWindow && hasTauri()) {
1900
+ return sendInvoke(method, resolvedParams, this.config, this.log.bind(this));
1664
1901
  }
1902
+ const requestId = generateRequestId(++this.requestCounter);
1903
+ return sendPostMessage(method, resolvedParams, requestId, this.config, this._extensionInfo, this.pendingRequests);
1665
1904
  }
1666
- validateTableName(tableName) {
1667
- if (!tableName || typeof tableName !== "string") {
1668
- throw new HaexHubError(
1669
- "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1670
- "errors.table_name_empty"
1671
- );
1905
+ // ==========================================================================
1906
+ // Private: Initialization
1907
+ // ==========================================================================
1908
+ async init() {
1909
+ if (this.initialized) return;
1910
+ if (!isInIframe() && hasTauri()) {
1911
+ try {
1912
+ await this.initNative();
1913
+ return;
1914
+ } catch (error) {
1915
+ this.log("Tauri commands failed, falling back to iframe mode", error);
1916
+ }
1672
1917
  }
1673
- if (tableName.includes(TABLE_SEPARATOR)) {
1674
- throw new HaexHubError(
1675
- "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1676
- "errors.table_name_contains_separator",
1677
- { tableName, separator: TABLE_SEPARATOR }
1678
- );
1918
+ await this.initIframe();
1919
+ }
1920
+ async initNative() {
1921
+ const { extensionInfo: extensionInfo2, context: context2 } = await initNativeMode(
1922
+ {
1923
+ config: this.config,
1924
+ state: {
1925
+ initialized: this.initialized,
1926
+ isNativeWindow: this.isNativeWindow,
1927
+ requestCounter: this.requestCounter,
1928
+ setupCompleted: this._setupCompleted,
1929
+ extensionInfo: this._extensionInfo,
1930
+ context: this._context,
1931
+ orm: this.orm
1932
+ },
1933
+ collections: {
1934
+ pendingRequests: this.pendingRequests,
1935
+ eventListeners: this.eventListeners,
1936
+ externalRequestHandlers: this.externalRequestHandlers,
1937
+ reactiveSubscribers: this.reactiveSubscribers
1938
+ },
1939
+ promises: {
1940
+ readyPromise: this.readyPromise,
1941
+ resolveReady: this.resolveReady,
1942
+ setupPromise: this.setupPromise,
1943
+ setupHook: this.setupHook
1944
+ },
1945
+ handlers: {
1946
+ messageHandler: this.messageHandler
1947
+ }
1948
+ },
1949
+ this.log.bind(this),
1950
+ this.handleEvent.bind(this),
1951
+ (ctx) => {
1952
+ this._context = ctx;
1953
+ this.notifySubscribersInternal();
1954
+ }
1955
+ );
1956
+ this._extensionInfo = extensionInfo2;
1957
+ this._context = context2;
1958
+ this.isNativeWindow = true;
1959
+ this.initialized = true;
1960
+ this.notifySubscribersInternal();
1961
+ this.resolveReady();
1962
+ }
1963
+ async initIframe() {
1964
+ this.messageHandler = createMessageHandler(
1965
+ this.config,
1966
+ this.pendingRequests,
1967
+ () => this._extensionInfo,
1968
+ this.handleEvent.bind(this)
1969
+ );
1970
+ const { context: context2 } = await initIframeMode(
1971
+ {
1972
+ config: this.config,
1973
+ state: {
1974
+ initialized: this.initialized,
1975
+ isNativeWindow: this.isNativeWindow,
1976
+ requestCounter: this.requestCounter,
1977
+ setupCompleted: this._setupCompleted,
1978
+ extensionInfo: this._extensionInfo,
1979
+ context: this._context,
1980
+ orm: this.orm
1981
+ },
1982
+ collections: {
1983
+ pendingRequests: this.pendingRequests,
1984
+ eventListeners: this.eventListeners,
1985
+ externalRequestHandlers: this.externalRequestHandlers,
1986
+ reactiveSubscribers: this.reactiveSubscribers
1987
+ },
1988
+ promises: {
1989
+ readyPromise: this.readyPromise,
1990
+ resolveReady: this.resolveReady,
1991
+ setupPromise: this.setupPromise,
1992
+ setupHook: this.setupHook
1993
+ },
1994
+ handlers: {
1995
+ messageHandler: this.messageHandler
1996
+ }
1997
+ },
1998
+ this.log.bind(this),
1999
+ this.messageHandler,
2000
+ this.request.bind(this)
2001
+ );
2002
+ if (this.config.manifest) {
2003
+ this._extensionInfo = {
2004
+ publicKey: this.config.manifest.publicKey,
2005
+ name: this.config.manifest.name,
2006
+ version: this.config.manifest.version,
2007
+ displayName: this.config.manifest.name
2008
+ };
2009
+ this.notifySubscribersInternal();
1679
2010
  }
1680
- if (!/^[a-z][a-z0-9-_]*$/i.test(tableName)) {
1681
- throw new HaexHubError(
1682
- "INVALID_TABLE_NAME" /* INVALID_TABLE_NAME */,
1683
- "errors.table_name_format",
1684
- { tableName }
1685
- );
2011
+ this._context = context2;
2012
+ this.isNativeWindow = false;
2013
+ this.initialized = true;
2014
+ this.notifySubscribersInternal();
2015
+ this.resolveReady();
2016
+ }
2017
+ // ==========================================================================
2018
+ // Private: Event Handling
2019
+ // ==========================================================================
2020
+ handleEvent(event) {
2021
+ processEvent(
2022
+ event,
2023
+ this.log.bind(this),
2024
+ this.eventListeners,
2025
+ (ctx) => {
2026
+ this._context = ctx;
2027
+ this.notifySubscribersInternal();
2028
+ },
2029
+ (extEvent) => this.handleExternalRequestInternal(extEvent.data)
2030
+ );
2031
+ }
2032
+ async handleExternalRequestInternal(request) {
2033
+ await handleExternalRequest(request, this.externalRequestHandlers, this.respondToExternalRequest.bind(this), this.log.bind(this));
2034
+ }
2035
+ // ==========================================================================
2036
+ // Private: Setup
2037
+ // ==========================================================================
2038
+ async runSetupAsync() {
2039
+ if (!this.setupHook) return;
2040
+ try {
2041
+ this.log("[HaexVault] Running setup hook...");
2042
+ await this.setupHook();
2043
+ this._setupCompleted = true;
2044
+ this.log("[HaexVault] Setup completed successfully");
2045
+ this.notifySubscribersInternal();
2046
+ } catch (error) {
2047
+ this.log("[HaexVault] Setup failed:", error);
2048
+ throw error;
1686
2049
  }
1687
2050
  }
2051
+ // ==========================================================================
2052
+ // Private: Utilities
2053
+ // ==========================================================================
2054
+ notifySubscribersInternal() {
2055
+ notifySubscribers(this.reactiveSubscribers);
2056
+ }
1688
2057
  log(...args) {
1689
2058
  if (this.config.debug) {
1690
- console.log("[HaexSpace SDK]", ...args);
2059
+ console.log("[HaexVault SDK]", ...args);
1691
2060
  }
1692
2061
  }
1693
2062
  };