@fileverse/api 0.0.9 → 0.0.11

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.
@@ -272,7 +272,9 @@ var init_files_model = __esm({
272
272
  linkKey: fileRaw.linkKey,
273
273
  linkKeyNonce: fileRaw.linkKeyNonce,
274
274
  commentKey: fileRaw.commentKey,
275
- link: fileRaw.link
275
+ link: fileRaw.link,
276
+ derivedKey: fileRaw.derivedKey,
277
+ secretKey: fileRaw.secretKey
276
278
  };
277
279
  }
278
280
  static async findAll(portalAddress, limit, skip) {
@@ -356,10 +358,20 @@ var init_files_model = __esm({
356
358
  const _id = uuidv7();
357
359
  const sql = `
358
360
  INSERT INTO ${this.TABLE}
359
- (_id, title, content, ddocId, portalAddress)
360
- VALUES (?, ?, ?, ?, ?)
361
+ (_id, title, content, ddocId, portalAddress, linkKey, linkKeyNonce, derivedKey, secretKey)
362
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
361
363
  `;
362
- await QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
364
+ await QueryBuilder.execute(sql, [
365
+ _id,
366
+ input.title,
367
+ input.content,
368
+ input.ddocId,
369
+ input.portalAddress,
370
+ input.linkKey ?? null,
371
+ input.linkKeyNonce ?? null,
372
+ input.derivedKey ?? null,
373
+ input.secretKey ?? null
374
+ ]);
363
375
  const created = await this.findById(_id, input.portalAddress);
364
376
  if (!created) {
365
377
  throw new Error("Failed to create file");
@@ -735,6 +747,29 @@ var init_events_model = __esm({
735
747
  `;
736
748
  await QueryBuilder.execute(sql, [Date.now(), _id]);
737
749
  }
750
+ static async markSubmitted(_id) {
751
+ const sql = `
752
+ UPDATE ${this.TABLE}
753
+ SET status = 'submitted',
754
+ lockedAt = NULL
755
+ WHERE _id = ?
756
+ `;
757
+ await QueryBuilder.execute(sql, [_id]);
758
+ }
759
+ static async findNextSubmitted(lockedFileIds) {
760
+ const exclusionClause = lockedFileIds.length > 0 ? `AND fileId NOT IN (${lockedFileIds.map(() => "?").join(", ")})` : "";
761
+ const sql = `
762
+ SELECT * FROM ${this.TABLE}
763
+ WHERE status = 'submitted'
764
+ AND userOpHash IS NOT NULL
765
+ ${exclusionClause}
766
+ ORDER BY timestamp ASC
767
+ LIMIT 1
768
+ `;
769
+ const params = [...lockedFileIds];
770
+ const row = await QueryBuilder.selectOne(sql, params);
771
+ return row ? this.parseEvent(row) : void 0;
772
+ }
738
773
  static async markProcessed(_id) {
739
774
  const sql = `
740
775
  UPDATE ${this.TABLE}
@@ -1275,7 +1310,7 @@ import { createSmartAccountClient } from "permissionless";
1275
1310
  import { toSafeSmartAccount } from "permissionless/accounts";
1276
1311
  import { entryPoint07Address } from "viem/account-abstraction";
1277
1312
  import { generatePrivateKey } from "viem/accounts";
1278
- var getPublicClient, getPimlicoClient, signerToSmartAccount, getSmartAccountClient, getNonce, waitForUserOpReceipt;
1313
+ var getPublicClient, getPimlicoClient, signerToSmartAccount, getSmartAccountClient, getNonce, getUserOpReceipt, waitForUserOpReceipt;
1279
1314
  var init_pimlico_utils = __esm({
1280
1315
  "src/sdk/pimlico-utils.ts"() {
1281
1316
  "use strict";
@@ -1339,6 +1374,14 @@ var init_pimlico_utils = __esm({
1339
1374
  size: 32
1340
1375
  })
1341
1376
  );
1377
+ getUserOpReceipt = async (hash2, authToken, portalAddress, invokerAddress) => {
1378
+ const pimlicoClient = getPimlicoClient(authToken, portalAddress, invokerAddress);
1379
+ try {
1380
+ return await pimlicoClient.getUserOperationReceipt({ hash: hash2 });
1381
+ } catch {
1382
+ return null;
1383
+ }
1384
+ };
1342
1385
  waitForUserOpReceipt = async (hash2, authToken, portalAddress, invokerAddress, timeout = 12e4) => {
1343
1386
  const pimlicoClient = getPimlicoClient(authToken, portalAddress, invokerAddress);
1344
1387
  return pimlicoClient.waitForUserOperationReceipt({
@@ -2149,7 +2192,7 @@ import { fromUint8Array, toUint8Array } from "js-base64";
2149
2192
  import { toAESKey, aesEncrypt } from "@fileverse/crypto/webcrypto";
2150
2193
  import axios from "axios";
2151
2194
  import { encodeFunctionData, parseEventLogs } from "viem";
2152
- var deriveKeyFromAg2Hash, decryptSecretKey, getExistingEncryptionMaterial, getNaclSecretKey, generateLinkKeyMaterial, jsonToFile, appendAuthTagIvToBlob, encryptFile, getNonceAppendedCipherText, jsonToBytes, buildLinklock, encryptTitleWithFileKey, uploadFileToIPFS, getEditFileTrxCalldata, getAddFileTrxCalldata, prepareCallData, prepareDeleteFileCallData, createEncryptedContentFile, buildFileMetadata, parseFileEventLog, uploadAllFilesToIPFS;
2195
+ var deriveKeyFromAg2Hash, getExistingEncryptionMaterial, getNaclSecretKey, generateLinkKeyMaterial, jsonToFile, appendAuthTagIvToBlob, encryptFile, getNonceAppendedCipherText, jsonToBytes, buildLinklock, encryptTitleWithFileKey, uploadFileToIPFS, getEditFileTrxCalldata, getAddFileTrxCalldata, prepareCallData, prepareDeleteFileCallData, createEncryptedContentFile, buildFileMetadata, parseFileEventLog, uploadAllFilesToIPFS;
2153
2196
  var init_file_utils = __esm({
2154
2197
  "src/sdk/file-utils.ts"() {
2155
2198
  "use strict";
@@ -2162,16 +2205,18 @@ var init_file_utils = __esm({
2162
2205
  info: Buffer.from("encryptionKey")
2163
2206
  });
2164
2207
  };
2165
- decryptSecretKey = async (docId, nonce, encryptedSecretKey) => {
2166
- const derivedKey = await deriveKeyFromAg2Hash(docId, toUint8Array(nonce));
2167
- return tweetnacl.secretbox.open(toUint8Array(encryptedSecretKey), toUint8Array(nonce), derivedKey);
2168
- };
2169
2208
  getExistingEncryptionMaterial = async (existingEncryptedSecretKey, existingNonce, docId) => {
2170
- const secretKey = await decryptSecretKey(docId, existingNonce, existingEncryptedSecretKey);
2209
+ const derivedKey = await deriveKeyFromAg2Hash(docId, toUint8Array(existingNonce));
2210
+ const secretKey = tweetnacl.secretbox.open(
2211
+ toUint8Array(existingEncryptedSecretKey),
2212
+ toUint8Array(existingNonce),
2213
+ derivedKey
2214
+ );
2171
2215
  return {
2172
2216
  encryptedSecretKey: existingEncryptedSecretKey,
2173
2217
  nonce: toUint8Array(existingNonce),
2174
- secretKey
2218
+ secretKey,
2219
+ derivedKey: new Uint8Array(derivedKey)
2175
2220
  };
2176
2221
  };
2177
2222
  getNaclSecretKey = async (ddocId) => {
@@ -2179,19 +2224,19 @@ var init_file_utils = __esm({
2179
2224
  const nonce = tweetnacl.randomBytes(tweetnacl.secretbox.nonceLength);
2180
2225
  const derivedKey = await deriveKeyFromAg2Hash(ddocId, nonce);
2181
2226
  const encryptedSecretKey = fromUint8Array(tweetnacl.secretbox(secretKey, nonce, derivedKey), true);
2182
- return { nonce, encryptedSecretKey, secretKey };
2227
+ return { nonce, encryptedSecretKey, secretKey, derivedKey: new Uint8Array(derivedKey) };
2183
2228
  };
2184
2229
  generateLinkKeyMaterial = async (params) => {
2185
2230
  if (params.linkKeyNonce && params.linkKey) {
2186
- const { encryptedSecretKey: encryptedSecretKey2, nonce: nonce2, secretKey: secretKey2 } = await getExistingEncryptionMaterial(
2231
+ const { encryptedSecretKey: encryptedSecretKey2, nonce: nonce2, secretKey: secretKey2, derivedKey: derivedKey2 } = await getExistingEncryptionMaterial(
2187
2232
  params.linkKey,
2188
2233
  params.linkKeyNonce,
2189
2234
  params.ddocId
2190
2235
  );
2191
- if (secretKey2) return { encryptedSecretKey: encryptedSecretKey2, nonce: nonce2, secretKey: secretKey2 };
2236
+ if (secretKey2) return { encryptedSecretKey: encryptedSecretKey2, nonce: nonce2, secretKey: secretKey2, derivedKey: derivedKey2 };
2192
2237
  }
2193
- const { secretKey, nonce, encryptedSecretKey } = await getNaclSecretKey(params.ddocId);
2194
- return { secretKey, nonce, encryptedSecretKey };
2238
+ const { secretKey, nonce, encryptedSecretKey, derivedKey } = await getNaclSecretKey(params.ddocId);
2239
+ return { secretKey, nonce, encryptedSecretKey, derivedKey };
2195
2240
  };
2196
2241
  jsonToFile = (json2, fileName) => {
2197
2242
  const blob = new Blob([JSON.stringify(json2)], {
@@ -2444,11 +2489,9 @@ var init_file_manager = __esm({
2444
2489
  }
2445
2490
  async submitAddFileTrx(file2) {
2446
2491
  logger.debug(`Preparing to add file ${file2.ddocId}`);
2447
- const { encryptedSecretKey, nonce, secretKey } = await generateLinkKeyMaterial({
2448
- ddocId: file2.ddocId,
2449
- linkKey: file2.linkKey,
2450
- linkKeyNonce: file2.linkKeyNonce
2451
- });
2492
+ const encryptedSecretKey = file2.linkKey;
2493
+ const nonce = toUint8Array2(file2.linkKeyNonce);
2494
+ const secretKey = toUint8Array2(file2.secretKey);
2452
2495
  const yJSContent = markdownToYjs(file2.content);
2453
2496
  const { encryptedFile, key } = await createEncryptedContentFile(yJSContent);
2454
2497
  logger.debug(`Generated encrypted content file for file ${file2.ddocId}`);
@@ -2492,13 +2535,59 @@ var init_file_manager = __esm({
2492
2535
  metadata
2493
2536
  };
2494
2537
  }
2495
- async updateFile(file2) {
2496
- logger.debug(`Updating file ${file2.ddocId} with onChainFileId ${file2.onChainFileId}`);
2497
- const { encryptedSecretKey, nonce, secretKey } = await generateLinkKeyMaterial({
2538
+ async submitUpdateFile(file2) {
2539
+ logger.debug(`Submitting update for file ${file2.ddocId} with onChainFileId ${file2.onChainFileId}`);
2540
+ const encryptedSecretKey = file2.linkKey;
2541
+ const nonce = toUint8Array2(file2.linkKeyNonce);
2542
+ const secretKey = toUint8Array2(file2.secretKey);
2543
+ const yjsContent = markdownToYjs(file2.content);
2544
+ const { encryptedFile, key } = await createEncryptedContentFile(yjsContent);
2545
+ const commentKey = toUint8Array2(file2.commentKey);
2546
+ const { appLock, ownerLock } = this.createLocks(key, encryptedSecretKey, commentKey);
2547
+ const linkLock = buildLinklock(secretKey, toUint8Array2(key), commentKey);
2548
+ const encryptedTitle = await encryptTitleWithFileKey({
2549
+ title: file2.title || "Untitled",
2550
+ key
2551
+ });
2552
+ const metadata = buildFileMetadata({
2553
+ encryptedTitle,
2554
+ encryptedFileSize: encryptedFile.size,
2555
+ appLock,
2556
+ ownerLock,
2498
2557
  ddocId: file2.ddocId,
2499
- linkKey: file2.linkKey,
2500
- linkKeyNonce: file2.linkKeyNonce
2558
+ nonce: fromUint8Array2(nonce),
2559
+ owner: this.agentClient.getAgentAddress()
2501
2560
  });
2561
+ const authParams = await this.getAuthParams();
2562
+ const { metadataHash, contentHash, gateHash } = await uploadAllFilesToIPFS(
2563
+ { metadata, encryptedFile, linkLock, ddocId: file2.ddocId },
2564
+ authParams
2565
+ );
2566
+ const callData = prepareCallData({
2567
+ metadataHash,
2568
+ contentHash,
2569
+ gateHash,
2570
+ appFileId: file2.ddocId,
2571
+ fileId: file2.onChainFileId
2572
+ });
2573
+ const userOpHash = await this.sendFileOperation(callData);
2574
+ logger.debug(`Submitted update user op for file ${file2.ddocId}`);
2575
+ return { userOpHash, metadata };
2576
+ }
2577
+ async submitDeleteFile(file2) {
2578
+ logger.debug(`Submitting delete for file ${file2.ddocId} with onChainFileId ${file2.onChainFileId}`);
2579
+ const callData = prepareDeleteFileCallData({
2580
+ onChainFileId: file2.onChainFileId
2581
+ });
2582
+ const userOpHash = await this.sendFileOperation(callData);
2583
+ logger.debug(`Submitted delete user op for file ${file2.ddocId}`);
2584
+ return { userOpHash };
2585
+ }
2586
+ async updateFile(file2) {
2587
+ logger.debug(`Updating file ${file2.ddocId} with onChainFileId ${file2.onChainFileId}`);
2588
+ const encryptedSecretKey = file2.linkKey;
2589
+ const nonce = toUint8Array2(file2.linkKeyNonce);
2590
+ const secretKey = toUint8Array2(file2.secretKey);
2502
2591
  logger.debug(`Generating encrypted content file for file ${file2.ddocId} with onChainFileId ${file2.onChainFileId}`);
2503
2592
  const yjsContent = markdownToYjs(file2.content);
2504
2593
  const { encryptedFile, key } = await createEncryptedContentFile(yjsContent);
@@ -2583,7 +2672,7 @@ function deriveCollaboratorKeys(apiKeySeed) {
2583
2672
  const { secretKey: ucanSecret } = generateKeyPairFromSeed(ucanDerivedSecret);
2584
2673
  return { privateAccountKey, ucanSecret };
2585
2674
  }
2586
- var createFileManager, executeOperation, handleExistingFileOp, handleNewFileOp, getProxyAuthParams;
2675
+ var createFileManager, executeOperation, handleExistingFileOp, handleNewFileOp, getProxyAuthParams, submitUpdateFileOp, submitDeleteFileOp, resolveFileOp;
2587
2676
  var init_publish = __esm({
2588
2677
  "src/domain/portal/publish.ts"() {
2589
2678
  "use strict";
@@ -2595,6 +2684,7 @@ var init_publish = __esm({
2595
2684
  init_smart_agent();
2596
2685
  init_file_manager();
2597
2686
  init_config();
2687
+ init_pimlico_utils();
2598
2688
  createFileManager = async (portalSeed, portalAddress, ucanSecret, privateAccountKey) => {
2599
2689
  const keyPair = ucans2.EdKeypair.fromSecretKey(fromUint8Array3(ucanSecret), {
2600
2690
  exportable: true
@@ -2657,6 +2747,50 @@ var init_publish = __esm({
2657
2747
  );
2658
2748
  return fileManager.getProxyAuthParams();
2659
2749
  };
2750
+ submitUpdateFileOp = async (fileId) => {
2751
+ const { file: file2, portalDetails, apiKey } = await getPortalData(fileId);
2752
+ const apiKeySeed = toUint8Array3(apiKey);
2753
+ const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
2754
+ const fileManager = await createFileManager(
2755
+ portalDetails.portalSeed,
2756
+ portalDetails.portalAddress,
2757
+ ucanSecret,
2758
+ privateAccountKey
2759
+ );
2760
+ return fileManager.submitUpdateFile(file2);
2761
+ };
2762
+ submitDeleteFileOp = async (fileId) => {
2763
+ const { file: file2, portalDetails, apiKey } = await getPortalData(fileId);
2764
+ const apiKeySeed = toUint8Array3(apiKey);
2765
+ const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
2766
+ const fileManager = await createFileManager(
2767
+ portalDetails.portalSeed,
2768
+ portalDetails.portalAddress,
2769
+ ucanSecret,
2770
+ privateAccountKey
2771
+ );
2772
+ return fileManager.submitDeleteFile(file2);
2773
+ };
2774
+ resolveFileOp = async (fileId, userOpHash, eventType) => {
2775
+ const { portalDetails, apiKey } = await getPortalData(fileId);
2776
+ const apiKeySeed = toUint8Array3(apiKey);
2777
+ const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
2778
+ const fileManager = await createFileManager(
2779
+ portalDetails.portalSeed,
2780
+ portalDetails.portalAddress,
2781
+ ucanSecret,
2782
+ privateAccountKey
2783
+ );
2784
+ const { authToken, portalAddress, invokerAddress } = await fileManager.getProxyAuthParams();
2785
+ const receipt = await getUserOpReceipt(
2786
+ userOpHash,
2787
+ authToken,
2788
+ portalAddress,
2789
+ invokerAddress
2790
+ );
2791
+ if (!receipt) return null;
2792
+ return { receipt };
2793
+ };
2660
2794
  }
2661
2795
  });
2662
2796
 
@@ -2754,7 +2888,7 @@ var init_rate_limit = __esm({
2754
2888
  });
2755
2889
 
2756
2890
  // src/infra/worker/eventProcessor.ts
2757
- var processEvent, onTransactionSuccess, processCreateEvent, processUpdateEvent, processDeleteEvent;
2891
+ var processEvent, submitEvent, resolveEvent, onTransactionSuccess, processCreateEvent, processUpdateEvent, processDeleteEvent;
2758
2892
  var init_eventProcessor = __esm({
2759
2893
  "src/infra/worker/eventProcessor.ts"() {
2760
2894
  "use strict";
@@ -2792,6 +2926,132 @@ var init_eventProcessor = __esm({
2792
2926
  return { success: false, error: errorMsg };
2793
2927
  }
2794
2928
  };
2929
+ submitEvent = async (event) => {
2930
+ const { fileId, type } = event;
2931
+ switch (type) {
2932
+ case "create": {
2933
+ const file2 = await FilesModel.findByIdIncludingDeleted(fileId);
2934
+ if (!file2) throw new Error(`File ${fileId} not found`);
2935
+ if (file2.isDeleted === 1) {
2936
+ logger.info(`File ${fileId} is deleted, skipping create submit`);
2937
+ return;
2938
+ }
2939
+ const result = await handleNewFileOp(fileId);
2940
+ await EventsModel.setEventPendingOp(event._id, result.userOpHash, {
2941
+ linkKey: result.linkKey,
2942
+ linkKeyNonce: result.linkKeyNonce,
2943
+ commentKey: result.commentKey,
2944
+ metadata: result.metadata
2945
+ });
2946
+ logger.info(`File ${file2.ddocId} create op submitted (hash: ${result.userOpHash})`);
2947
+ break;
2948
+ }
2949
+ case "update": {
2950
+ const file2 = await FilesModel.findByIdExcludingDeleted(fileId);
2951
+ if (!file2) return;
2952
+ if (file2.localVersion <= file2.onchainVersion) return;
2953
+ const result = await submitUpdateFileOp(fileId);
2954
+ await EventsModel.setEventPendingOp(event._id, result.userOpHash, {
2955
+ metadata: result.metadata,
2956
+ localVersion: file2.localVersion
2957
+ });
2958
+ logger.info(`File ${file2.ddocId} update op submitted (hash: ${result.userOpHash})`);
2959
+ break;
2960
+ }
2961
+ case "delete": {
2962
+ const file2 = await FilesModel.findByIdIncludingDeleted(fileId);
2963
+ if (!file2) return;
2964
+ if (file2.isDeleted === 1 && file2.syncStatus === "synced") {
2965
+ logger.info(`File ${fileId} deletion already synced, skipping`);
2966
+ return;
2967
+ }
2968
+ if (file2.onChainFileId === null || file2.onChainFileId === void 0) {
2969
+ await FilesModel.update(fileId, { syncStatus: "synced", isDeleted: 1 }, file2.portalAddress);
2970
+ return;
2971
+ }
2972
+ const result = await submitDeleteFileOp(fileId);
2973
+ await EventsModel.setEventPendingOp(event._id, result.userOpHash, {});
2974
+ logger.info(`File ${file2.ddocId} delete op submitted (hash: ${result.userOpHash})`);
2975
+ break;
2976
+ }
2977
+ default:
2978
+ throw new Error(`Unknown event type: ${type}`);
2979
+ }
2980
+ };
2981
+ resolveEvent = async (event) => {
2982
+ const { fileId, type, userOpHash } = event;
2983
+ if (!userOpHash) {
2984
+ logger.warn(`Event ${event._id} has no userOpHash, cannot resolve`);
2985
+ return { resolved: false };
2986
+ }
2987
+ const result = await resolveFileOp(fileId, userOpHash, type);
2988
+ if (!result) {
2989
+ return { resolved: false };
2990
+ }
2991
+ const { receipt } = result;
2992
+ if (!receipt.success) {
2993
+ await EventsModel.clearEventPendingOp(event._id);
2994
+ throw new Error(`User operation failed: ${receipt.reason}`);
2995
+ }
2996
+ switch (type) {
2997
+ case "create": {
2998
+ const file2 = await FilesModel.findByIdIncludingDeleted(fileId);
2999
+ if (!file2) throw new Error(`File ${fileId} not found during resolve`);
3000
+ const onChainFileId = parseFileEventLog(receipt.logs, "AddedFile", ADDED_FILE_EVENT);
3001
+ const pending = JSON.parse(event.pendingPayload);
3002
+ const frontendUrl = getRuntimeConfig().FRONTEND_URL;
3003
+ const payload = {
3004
+ onchainVersion: file2.localVersion,
3005
+ onChainFileId,
3006
+ linkKey: pending.linkKey,
3007
+ linkKeyNonce: pending.linkKeyNonce,
3008
+ commentKey: pending.commentKey,
3009
+ metadata: pending.metadata,
3010
+ link: `${frontendUrl}/${file2.portalAddress}/${onChainFileId}#key=${pending.linkKey}`
3011
+ };
3012
+ const updatedFile = await FilesModel.update(fileId, payload, file2.portalAddress);
3013
+ if (updatedFile.localVersion === updatedFile.onchainVersion) {
3014
+ await FilesModel.update(fileId, { syncStatus: "synced" }, file2.portalAddress);
3015
+ }
3016
+ await EventsModel.clearEventPendingOp(event._id);
3017
+ logger.info(`File ${file2.ddocId} create resolved successfully`);
3018
+ break;
3019
+ }
3020
+ case "update": {
3021
+ const file2 = await FilesModel.findByIdExcludingDeleted(fileId);
3022
+ if (!file2) throw new Error(`File ${fileId} not found during resolve`);
3023
+ parseFileEventLog(receipt.logs, "EditedFile", EDITED_FILE_EVENT);
3024
+ const pending = JSON.parse(event.pendingPayload);
3025
+ const payload = {
3026
+ onchainVersion: pending.localVersion,
3027
+ metadata: pending.metadata
3028
+ };
3029
+ const updatedFile = await FilesModel.update(fileId, payload, file2.portalAddress);
3030
+ if (updatedFile.localVersion === updatedFile.onchainVersion) {
3031
+ await FilesModel.update(fileId, { syncStatus: "synced" }, file2.portalAddress);
3032
+ }
3033
+ await EventsModel.clearEventPendingOp(event._id);
3034
+ logger.info(`File ${file2.ddocId} update resolved successfully`);
3035
+ break;
3036
+ }
3037
+ case "delete": {
3038
+ const file2 = await FilesModel.findByIdIncludingDeleted(fileId);
3039
+ if (!file2) throw new Error(`File ${fileId} not found during resolve`);
3040
+ parseFileEventLog(receipt.logs, "DeletedFile", DELETED_FILE_EVENT);
3041
+ await FilesModel.update(
3042
+ fileId,
3043
+ { syncStatus: "synced", isDeleted: 1, onchainVersion: file2.localVersion },
3044
+ file2.portalAddress
3045
+ );
3046
+ await EventsModel.clearEventPendingOp(event._id);
3047
+ logger.info(`File ${fileId} delete resolved successfully`);
3048
+ break;
3049
+ }
3050
+ default:
3051
+ throw new Error(`Unknown event type: ${type}`);
3052
+ }
3053
+ return { resolved: true };
3054
+ };
2795
3055
  onTransactionSuccess = async (fileId, file2, onChainFileId, pending) => {
2796
3056
  const frontendUrl = getRuntimeConfig().FRONTEND_URL;
2797
3057
  const payload = {
@@ -3378,7 +3638,9 @@ CREATE TABLE IF NOT EXISTS files (
3378
3638
  commentKey TEXT,
3379
3639
  linkKey TEXT,
3380
3640
  linkKeyNonce TEXT,
3381
- link TEXT
3641
+ link TEXT,
3642
+ derivedKey TEXT,
3643
+ secretKey TEXT
3382
3644
  );
3383
3645
  CREATE INDEX IF NOT EXISTS idx_files_createdAt ON files(createdAt);
3384
3646
  CREATE INDEX IF NOT EXISTS idx_files_syncStatus ON files(syncStatus);
@@ -3409,7 +3671,7 @@ CREATE TABLE IF NOT EXISTS events (
3409
3671
  type TEXT NOT NULL CHECK (type IN ('create', 'update', 'delete')),
3410
3672
  timestamp BIGINT NOT NULL,
3411
3673
  fileId TEXT NOT NULL,
3412
- status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'processed', 'failed')),
3674
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'submitted', 'processed', 'failed')),
3413
3675
  retryCount INTEGER NOT NULL DEFAULT 0,
3414
3676
  lastError TEXT,
3415
3677
  lockedAt BIGINT,
@@ -3456,7 +3718,9 @@ init_models();
3456
3718
  init_esm_shims();
3457
3719
  init_models();
3458
3720
  init_constants3();
3721
+ init_file_utils();
3459
3722
  import { generate } from "short-uuid";
3723
+ import { fromUint8Array as fromUint8Array4 } from "js-base64";
3460
3724
  async function listFiles(params) {
3461
3725
  const { limit, skip, portalAddress } = params;
3462
3726
  const effectiveLimit = limit || DEFAULT_LIST_LIMIT;
@@ -3509,11 +3773,20 @@ var createFile = async (input) => {
3509
3773
  throw new Error("title, content, and portalAddress are required");
3510
3774
  }
3511
3775
  const ddocId = generate();
3776
+ const { encryptedSecretKey, nonce, secretKey, derivedKey } = await generateLinkKeyMaterial({
3777
+ ddocId,
3778
+ linkKey: void 0,
3779
+ linkKeyNonce: void 0
3780
+ });
3512
3781
  const file2 = await FilesModel.create({
3513
3782
  title: input.title,
3514
3783
  content: input.content,
3515
3784
  ddocId,
3516
- portalAddress: input.portalAddress
3785
+ portalAddress: input.portalAddress,
3786
+ linkKey: encryptedSecretKey,
3787
+ linkKeyNonce: fromUint8Array4(nonce),
3788
+ derivedKey: fromUint8Array4(derivedKey),
3789
+ secretKey: fromUint8Array4(secretKey)
3517
3790
  });
3518
3791
  await EventsModel.create({ type: "create", fileId: file2._id, portalAddress: file2.portalAddress });
3519
3792
  return file2;
@@ -17848,10 +18121,15 @@ export {
17848
18121
  listFiles,
17849
18122
  listFolders,
17850
18123
  processEvent,
18124
+ resolveEvent,
18125
+ resolveFileOp,
17851
18126
  runMigrations,
17852
18127
  savePortal,
17853
18128
  searchNodes,
17854
18129
  setAdapter,
18130
+ submitDeleteFileOp,
18131
+ submitEvent,
18132
+ submitUpdateFileOp,
17855
18133
  updateFile
17856
18134
  };
17857
18135
  /*! Bundled license information: